2#include <winpr/string.h>
3#include <winpr/assert.h>
6#include <winpr/image.h>
8#include <winpr/endian.h>
10static const char test_src_filename[] = TEST_SOURCE_PATH
"/rgb";
11static const char test_bin_filename[] = TEST_BINARY_PATH
"/rgb";
13static BOOL test_image_equal(
const wImage* imageA,
const wImage* imageB)
15 return winpr_image_equal(imageA, imageB,
16 WINPR_IMAGE_CMP_IGNORE_DEPTH | WINPR_IMAGE_CMP_IGNORE_ALPHA |
17 WINPR_IMAGE_CMP_FUZZY);
20static BOOL test_equal_to(
const wImage* bmp,
const char* name, UINT32 format)
23 wImage* cmp = winpr_image_new();
27 char path[MAX_PATH] = WINPR_C_ARRAY_INIT;
28 (void)_snprintf(path,
sizeof(path),
"%s.%s", name, winpr_image_format_extension(format));
29 const int cmpSize = winpr_image_read(cmp, path);
32 (void)fprintf(stderr,
"[%s] winpr_image_read failed for %s", __func__, path);
36 rc = test_image_equal(bmp, cmp);
38 (void)fprintf(stderr,
"[%s] winpr_image_eqal failed", __func__);
41 winpr_image_free(cmp, TRUE);
45static BOOL test_equal(
void)
48 wImage* bmp = winpr_image_new();
53 char path[MAX_PATH] = WINPR_C_ARRAY_INIT;
54 if (_snprintf(path,
sizeof(path),
"%s.bmp", test_src_filename) < 0)
56 if (FAILED(PathCchConvertStyleA(path,
sizeof(path), PATH_STYLE_NATIVE)))
59 const int bmpSize = winpr_image_read(bmp, path);
62 (void)fprintf(stderr,
"[%s] winpr_image_read failed for %s", __func__, path);
66 for (UINT32 x = 0; x < UINT8_MAX; x++)
68 if (!winpr_image_format_is_supported(x))
70 if (!test_equal_to(bmp, test_src_filename, x))
76 winpr_image_free(bmp, TRUE);
81static BOOL test_read_write_compare(
const char* tname,
const char* tdst, UINT32 format)
84 wImage* bmp1 = winpr_image_new();
85 wImage* bmp2 = winpr_image_new();
86 wImage* bmp3 = winpr_image_new();
87 if (!bmp1 || !bmp2 || !bmp3)
90 char spath[MAX_PATH] = WINPR_C_ARRAY_INIT;
91 char dpath[MAX_PATH] = WINPR_C_ARRAY_INIT;
92 char bpath1[MAX_PATH] = WINPR_C_ARRAY_INIT;
93 char bpath2[MAX_PATH] = WINPR_C_ARRAY_INIT;
94 if (_snprintf(spath,
sizeof(spath),
"%s.%s", tname, winpr_image_format_extension(format)) < 0)
96 if (_snprintf(dpath,
sizeof(dpath),
"%s.%s", tdst, winpr_image_format_extension(format)) < 0)
98 if (_snprintf(bpath1,
sizeof(bpath1),
"%s.src.%s", dpath,
99 winpr_image_format_extension(WINPR_IMAGE_BITMAP)) < 0)
101 if (_snprintf(bpath2,
sizeof(bpath2),
"%s.bin.%s", dpath,
102 winpr_image_format_extension(WINPR_IMAGE_BITMAP)) < 0)
104 if (FAILED(PathCchConvertStyleA(spath,
sizeof(spath), PATH_STYLE_NATIVE)))
106 if (FAILED(PathCchConvertStyleA(dpath,
sizeof(dpath), PATH_STYLE_NATIVE)))
108 if (FAILED(PathCchConvertStyleA(bpath1,
sizeof(bpath1), PATH_STYLE_NATIVE)))
110 if (FAILED(PathCchConvertStyleA(bpath2,
sizeof(bpath2), PATH_STYLE_NATIVE)))
113 const int bmpRSize = winpr_image_read(bmp1, spath);
116 (void)fprintf(stderr,
"[%s] winpr_image_read failed for %s", __func__, spath);
120 const int bmpWSize = winpr_image_write(bmp1, dpath);
123 (void)fprintf(stderr,
"[%s] winpr_image_write failed for %s", __func__, dpath);
127 const int bmp2RSize = winpr_image_read(bmp2, dpath);
130 (void)fprintf(stderr,
"[%s] winpr_image_read failed for %s", __func__, dpath);
134 const int bmpSrcWSize = winpr_image_write_ex(bmp1, WINPR_IMAGE_BITMAP, bpath1);
135 if (bmpSrcWSize <= 0)
137 (void)fprintf(stderr,
"[%s] winpr_image_write_ex failed for %s", __func__, bpath1);
143 const int bmpBinWSize = winpr_image_write_ex(bmp2, WINPR_IMAGE_BITMAP, bpath2);
144 if (bmpBinWSize <= 0)
146 (void)fprintf(stderr,
"[%s] winpr_image_write_ex failed for %s", __func__, bpath2);
150 const int bmp3RSize = winpr_image_read(bmp3, bpath2);
153 (void)fprintf(stderr,
"[%s] winpr_image_read failed for %s", __func__, bpath2);
157 if (!winpr_image_equal(bmp1, bmp2,
158 WINPR_IMAGE_CMP_IGNORE_DEPTH | WINPR_IMAGE_CMP_IGNORE_ALPHA |
159 WINPR_IMAGE_CMP_FUZZY))
161 (void)fprintf(stderr,
"[%s] winpr_image_eqal failed bmp1 bmp2", __func__);
165 rc = winpr_image_equal(bmp3, bmp2,
166 WINPR_IMAGE_CMP_IGNORE_DEPTH | WINPR_IMAGE_CMP_IGNORE_ALPHA |
167 WINPR_IMAGE_CMP_FUZZY);
169 (void)fprintf(stderr,
"[%s] winpr_image_eqal failed bmp3 bmp2", __func__);
171 winpr_image_free(bmp1, TRUE);
172 winpr_image_free(bmp2, TRUE);
173 winpr_image_free(bmp3, TRUE);
177static BOOL test_read_write(
void)
180 for (UINT32 x = 0; x < UINT8_MAX; x++)
182 if (!winpr_image_format_is_supported(x))
184 if (!test_read_write_compare(test_src_filename, test_bin_filename, x))
190static BOOL test_load_file(
const char* name)
193 wImage* image = winpr_image_new();
197 const int res = winpr_image_read(image, name);
201 winpr_image_free(image, TRUE);
205static void put_u16(BYTE* p, UINT16 v)
207 p[0] = (BYTE)(v & 0xff);
208 p[1] = (BYTE)((v >> 8) & 0xff);
211static void put_u32(BYTE* p, UINT32 v)
213 p[0] = (BYTE)(v & 0xff);
214 p[1] = (BYTE)((v >> 8) & 0xff);
215 p[2] = (BYTE)((v >> 16) & 0xff);
216 p[3] = (BYTE)((v >> 24) & 0xff);
223static BOOL test_unaligned_no_overread(
void)
226 const UINT32 width = 1;
227 const UINT32 height = 10;
228 const UINT32 bpp = 3;
229 const UINT32 uscanline = width * bpp;
230 const UINT32 biSizeImage = uscanline * height;
231 const size_t offBits = 54;
232 const size_t size = offBits + biSizeImage;
234 wImage* image = winpr_image_new();
235 BYTE* bmp = (BYTE*)calloc(1, size);
241 put_u32(&bmp[2], (UINT32)size);
242 put_u32(&bmp[10], (UINT32)offBits);
243 put_u32(&bmp[14], 40);
244 put_u32(&bmp[18], width);
245 put_u32(&bmp[22], (UINT32)(-(INT32)height));
246 put_u16(&bmp[26], 1);
247 put_u16(&bmp[28], 24);
248 put_u32(&bmp[30], 0);
249 put_u32(&bmp[34], biSizeImage);
250 for (UINT32 i = 0; i < biSizeImage; i++)
251 bmp[offBits + i] = (BYTE)i;
253 if (winpr_image_read_buffer(image, bmp, size) <= 0)
256 if ((image->width != width) || (image->height != height))
259 for (UINT32 row = 0; row < height; row++)
261 for (UINT32 b = 0; b < uscanline; b++)
263 if (image->data[1ULL * row * image->scanline + b] != (BYTE)(row * uscanline + b))
271 winpr_image_free(image, TRUE);
275static BOOL test_load(
void)
277 const char* names[] = {
278 "rgb.16a.bmp",
"rgb.16a.nocolor.bmp",
"rgb.16.bmp",
"rgb.16.nocolor.bmp",
279 "rgb.16x.bmp",
"rgb.16x.nocolor.bmp",
"rgb.24.bmp",
"rgb.24.nocolor.bmp",
280 "rgb.32.bmp",
"rgb.32.nocolor.bmp",
"rgb.32x.bmp",
"rgb.32x.nocolor.bmp",
284 for (
size_t x = 0; x < ARRAYSIZE(names); x++)
286 const char* name = names[x];
287 char* fname = GetCombinedPath(TEST_SOURCE_PATH, name);
288 const BOOL res = test_load_file(fname);
298static BOOL test_bmp_offbits(UINT32 compression, UINT32 padding, BOOL topDown)
301 const UINT32 width = 2;
302 const UINT32 height = 2;
303 const UINT32 bpp = 32;
304 const UINT32 stride = width * bpp / 8;
305 const UINT32 biSizeImage = stride * height;
306 const size_t headerSize = 54;
307 const size_t maskSize = (compression == BI_BITFIELDS) ? 3 *
sizeof(DWORD) : 0;
308 const size_t minOffset = headerSize + maskSize;
309 const size_t offBits = minOffset + padding;
310 const size_t size = offBits + biSizeImage;
312 wImage* image = winpr_image_new();
313 BYTE* bmp = (BYTE*)calloc(1, size);
319 put_u32(&bmp[2], (UINT32)size);
320 put_u32(&bmp[10], (UINT32)offBits);
321 put_u32(&bmp[14], 40);
322 put_u32(&bmp[18], width);
323 put_u32(&bmp[22], topDown ? (UINT32)(-(INT32)height) : height);
324 put_u16(&bmp[26], 1);
325 put_u16(&bmp[28], bpp);
326 put_u32(&bmp[30], compression);
327 put_u32(&bmp[34], biSizeImage);
328 if (compression == BI_BITFIELDS)
330 put_u32(&bmp[54], 0x00FF0000);
331 put_u32(&bmp[58], 0x0000FF00);
332 put_u32(&bmp[62], 0x000000FF);
336 const BYTE pixels[16] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
337 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18 };
338 memcpy(&bmp[offBits], pixels,
sizeof(pixels));
340 if (winpr_image_read_buffer(image, bmp, size) <= 0)
342 (void)fprintf(stderr,
"[%s] failed to read BMP at offset %" PRIuz
"\n", __func__,
347 if ((image->width != width) || (image->height != height) || (image->bitsPerPixel != bpp) ||
348 (image->bytesPerPixel != bpp / 8) || (image->scanline != stride) || !image->data)
350 (void)fprintf(stderr,
"[%s] unexpected image layout\n", __func__);
354 for (
size_t row = 0; row < height; row++)
356 const size_t srcRow = topDown ? row : height - 1 - row;
357 if (memcmp(&image->data[row * image->scanline], &pixels[srcRow * stride], stride) != 0)
359 (void)fprintf(stderr,
"[%s] unexpected pixels in row %" PRIuz
"\n", __func__, row);
368 const UINT32 offsets[] = { 0,
369 (UINT32)(headerSize - 1),
370 (UINT32)(minOffset - 1),
375 for (
size_t x = 0; x < ARRAYSIZE(offsets); x++)
377 winpr_image_free(image, TRUE);
378 image = winpr_image_new();
381 put_u32(&bmp[10], offsets[x]);
382 if (winpr_image_read_buffer(image, bmp, size) > 0)
384 (void)fprintf(stderr,
"[%s] accepted invalid offset %" PRIu32
"\n", __func__,
394 winpr_image_free(image, TRUE);
400static BOOL test_write_bottom_up(
void)
403 const UINT32 width = 2;
404 const UINT32 height = 2;
405 const UINT32 stride = width * 4;
406 const size_t offBits = 54;
407 const size_t size = offBits + 1ull * stride * height;
408 const BYTE pixels[16] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
409 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18 };
411 wImage* image = winpr_image_new();
412 BYTE* bmp = (BYTE*)calloc(1, size);
420 put_u32(&bmp[2], (UINT32)size);
421 put_u32(&bmp[10], (UINT32)offBits);
422 put_u32(&bmp[14], 40);
423 put_u32(&bmp[18], width);
424 put_u32(&bmp[22], (UINT32)(-(INT32)height));
425 put_u16(&bmp[26], 1);
426 put_u16(&bmp[28], 32);
427 put_u32(&bmp[30], BI_RGB);
428 put_u32(&bmp[34], (UINT32)(1ull * stride * height));
429 memcpy(&bmp[offBits], pixels,
sizeof(pixels));
431 if (winpr_image_read_buffer(image, bmp, size) <= 0)
434 out = winpr_image_write_buffer(image, WINPR_IMAGE_BITMAP, &outSize);
435 if (!out || (outSize < offBits +
sizeof(pixels)))
437 (void)fprintf(stderr,
"[%s] winpr_image_write_buffer failed\n", __func__);
442 const UINT32 bfOffBits = winpr_Data_Get_UINT32(&out[10]);
443 const UINT32 biSize = winpr_Data_Get_UINT32(&out[14]);
444 const INT32 biHeight = winpr_Data_Get_INT32(&out[22]);
445 const UINT32 biCompression = winpr_Data_Get_UINT32(&out[30]);
446 if ((biSize != 40) || (biHeight != (INT32)height) || (biCompression != BI_RGB) ||
447 (bfOffBits +
sizeof(pixels) > outSize))
449 (void)fprintf(stderr,
450 "[%s] unexpected header biSize=%" PRIu32
" biHeight=%" PRId32
451 " biCompression=%" PRIu32
"\n",
452 __func__, biSize, biHeight, biCompression);
457 if ((memcmp(&out[bfOffBits], &pixels[stride], stride) != 0) ||
458 (memcmp(&out[bfOffBits + stride], &pixels[0], stride) != 0))
460 (void)fprintf(stderr,
"[%s] rows are not stored bottom-up\n", __func__);
469 winpr_image_free(image, TRUE);
474static BOOL test_write_unpadded_rows(
void)
477 const UINT32 width = 3;
478 const UINT32 height = 2;
479 const size_t row = 3ull * width;
480 const size_t padded = 12;
481 const BYTE pixels[18] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
482 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19 };
486 wImage* image = winpr_image_new();
490 image->data = (BYTE*)malloc(
sizeof(pixels));
493 memcpy(image->data, pixels,
sizeof(pixels));
494 image->width = width;
495 image->height = height;
496 image->bitsPerPixel = 24;
497 image->bytesPerPixel = 3;
498 image->scanline = (UINT32)row;
500 out = winpr_image_write_buffer(image, WINPR_IMAGE_BITMAP, &outSize);
503 (void)fprintf(stderr,
"[%s] winpr_image_write_buffer failed\n", __func__);
508 const UINT32 bfOffBits = winpr_Data_Get_UINT32(&out[10]);
509 const BYTE zero[3] = WINPR_C_ARRAY_INIT;
510 if ((outSize != bfOffBits + padded * height) ||
511 (memcmp(&out[bfOffBits], &pixels[row], row) != 0) ||
512 (memcmp(&out[bfOffBits + row], zero, padded - row) != 0) ||
513 (memcmp(&out[bfOffBits + padded], &pixels[0], row) != 0) ||
514 (memcmp(&out[bfOffBits + padded + row], zero, padded - row) != 0))
516 (void)fprintf(stderr,
"[%s] unexpected pixel data\n", __func__);
524 winpr_image_free(image, TRUE);
528int TestImage(
int argc,
char* argv[])
538 if (!test_read_write())
544 if (!test_unaligned_no_overread())
547 if (!test_bmp_offbits(BI_BITFIELDS, 0, FALSE) || !test_bmp_offbits(BI_BITFIELDS, 4, TRUE) ||
548 !test_bmp_offbits(BI_RGB, 0, FALSE) || !test_bmp_offbits(BI_RGB, 12, TRUE))
551 if (!test_write_bottom_up())
554 if (!test_write_unpadded_rows())