FreeRDP
Loading...
Searching...
No Matches
TestImage.c
1#include <stdio.h>
2#include <winpr/string.h>
3#include <winpr/assert.h>
4#include <winpr/file.h>
5#include <winpr/path.h>
6#include <winpr/image.h>
7#include <winpr/user.h>
8
9static const char test_src_filename[] = TEST_SOURCE_PATH "/rgb";
10static const char test_bin_filename[] = TEST_BINARY_PATH "/rgb";
11
12static BOOL test_image_equal(const wImage* imageA, const wImage* imageB)
13{
14 return winpr_image_equal(imageA, imageB,
15 WINPR_IMAGE_CMP_IGNORE_DEPTH | WINPR_IMAGE_CMP_IGNORE_ALPHA |
16 WINPR_IMAGE_CMP_FUZZY);
17}
18
19static BOOL test_equal_to(const wImage* bmp, const char* name, UINT32 format)
20{
21 BOOL rc = FALSE;
22 wImage* cmp = winpr_image_new();
23 if (!cmp)
24 goto fail;
25
26 char path[MAX_PATH] = WINPR_C_ARRAY_INIT;
27 (void)_snprintf(path, sizeof(path), "%s.%s", name, winpr_image_format_extension(format));
28 const int cmpSize = winpr_image_read(cmp, path);
29 if (cmpSize <= 0)
30 {
31 (void)fprintf(stderr, "[%s] winpr_image_read failed for %s", __func__, path);
32 goto fail;
33 }
34
35 rc = test_image_equal(bmp, cmp);
36 if (!rc)
37 (void)fprintf(stderr, "[%s] winpr_image_eqal failed", __func__);
38
39fail:
40 winpr_image_free(cmp, TRUE);
41 return rc;
42}
43
44static BOOL test_equal(void)
45{
46 BOOL rc = FALSE;
47 wImage* bmp = winpr_image_new();
48
49 if (!bmp)
50 goto fail;
51
52 char path[MAX_PATH] = WINPR_C_ARRAY_INIT;
53 (void)_snprintf(path, sizeof(path), "%s.bmp", test_src_filename);
54 PathCchConvertStyleA(path, sizeof(path), PATH_STYLE_NATIVE);
55
56 const int bmpSize = winpr_image_read(bmp, path);
57 if (bmpSize <= 0)
58 {
59 (void)fprintf(stderr, "[%s] winpr_image_read failed for %s", __func__, path);
60 goto fail;
61 }
62
63 for (UINT32 x = 0; x < UINT8_MAX; x++)
64 {
65 if (!winpr_image_format_is_supported(x))
66 continue;
67 if (!test_equal_to(bmp, test_src_filename, x))
68 goto fail;
69 }
70
71 rc = TRUE;
72fail:
73 winpr_image_free(bmp, TRUE);
74
75 return rc;
76}
77
78static BOOL test_read_write_compare(const char* tname, const char* tdst, UINT32 format)
79{
80 BOOL rc = FALSE;
81 wImage* bmp1 = winpr_image_new();
82 wImage* bmp2 = winpr_image_new();
83 wImage* bmp3 = winpr_image_new();
84 if (!bmp1 || !bmp2 || !bmp3)
85 goto fail;
86
87 char spath[MAX_PATH] = WINPR_C_ARRAY_INIT;
88 char dpath[MAX_PATH] = WINPR_C_ARRAY_INIT;
89 char bpath1[MAX_PATH] = WINPR_C_ARRAY_INIT;
90 char bpath2[MAX_PATH] = WINPR_C_ARRAY_INIT;
91 (void)_snprintf(spath, sizeof(spath), "%s.%s", tname, winpr_image_format_extension(format));
92 (void)_snprintf(dpath, sizeof(dpath), "%s.%s", tdst, winpr_image_format_extension(format));
93 (void)_snprintf(bpath1, sizeof(bpath1), "%s.src.%s", dpath,
94 winpr_image_format_extension(WINPR_IMAGE_BITMAP));
95 (void)_snprintf(bpath2, sizeof(bpath2), "%s.bin.%s", dpath,
96 winpr_image_format_extension(WINPR_IMAGE_BITMAP));
97 PathCchConvertStyleA(spath, sizeof(spath), PATH_STYLE_NATIVE);
98 PathCchConvertStyleA(dpath, sizeof(dpath), PATH_STYLE_NATIVE);
99 PathCchConvertStyleA(bpath1, sizeof(bpath1), PATH_STYLE_NATIVE);
100 PathCchConvertStyleA(bpath2, sizeof(bpath2), PATH_STYLE_NATIVE);
101
102 const int bmpRSize = winpr_image_read(bmp1, spath);
103 if (bmpRSize <= 0)
104 {
105 (void)fprintf(stderr, "[%s] winpr_image_read failed for %s", __func__, spath);
106 goto fail;
107 }
108
109 const int bmpWSize = winpr_image_write(bmp1, dpath);
110 if (bmpWSize <= 0)
111 {
112 (void)fprintf(stderr, "[%s] winpr_image_write failed for %s", __func__, dpath);
113 goto fail;
114 }
115
116 const int bmp2RSize = winpr_image_read(bmp2, dpath);
117 if (bmp2RSize <= 0)
118 {
119 (void)fprintf(stderr, "[%s] winpr_image_read failed for %s", __func__, dpath);
120 goto fail;
121 }
122
123 const int bmpSrcWSize = winpr_image_write_ex(bmp1, WINPR_IMAGE_BITMAP, bpath1);
124 if (bmpSrcWSize <= 0)
125 {
126 (void)fprintf(stderr, "[%s] winpr_image_write_ex failed for %s", __func__, bpath1);
127 goto fail;
128 }
129
130 /* write a bitmap and read it back.
131 * this tests if we have the proper internal format */
132 const int bmpBinWSize = winpr_image_write_ex(bmp2, WINPR_IMAGE_BITMAP, bpath2);
133 if (bmpBinWSize <= 0)
134 {
135 (void)fprintf(stderr, "[%s] winpr_image_write_ex failed for %s", __func__, bpath2);
136 goto fail;
137 }
138
139 const int bmp3RSize = winpr_image_read(bmp3, bpath2);
140 if (bmp3RSize <= 0)
141 {
142 (void)fprintf(stderr, "[%s] winpr_image_read failed for %s", __func__, bpath2);
143 goto fail;
144 }
145
146 if (!winpr_image_equal(bmp1, bmp2,
147 WINPR_IMAGE_CMP_IGNORE_DEPTH | WINPR_IMAGE_CMP_IGNORE_ALPHA |
148 WINPR_IMAGE_CMP_FUZZY))
149 {
150 (void)fprintf(stderr, "[%s] winpr_image_eqal failed bmp1 bmp2", __func__);
151 goto fail;
152 }
153
154 rc = winpr_image_equal(bmp3, bmp2,
155 WINPR_IMAGE_CMP_IGNORE_DEPTH | WINPR_IMAGE_CMP_IGNORE_ALPHA |
156 WINPR_IMAGE_CMP_FUZZY);
157 if (!rc)
158 (void)fprintf(stderr, "[%s] winpr_image_eqal failed bmp3 bmp2", __func__);
159fail:
160 winpr_image_free(bmp1, TRUE);
161 winpr_image_free(bmp2, TRUE);
162 winpr_image_free(bmp3, TRUE);
163 return rc;
164}
165
166static BOOL test_read_write(void)
167{
168 BOOL rc = TRUE;
169 for (UINT32 x = 0; x < UINT8_MAX; x++)
170 {
171 if (!winpr_image_format_is_supported(x))
172 continue;
173 if (!test_read_write_compare(test_src_filename, test_bin_filename, x))
174 rc = FALSE;
175 }
176 return rc;
177}
178
179static BOOL test_load_file(const char* name)
180{
181 BOOL rc = FALSE;
182 wImage* image = winpr_image_new();
183 if (!image || !name)
184 goto fail;
185
186 const int res = winpr_image_read(image, name);
187 rc = (res > 0);
188
189fail:
190 winpr_image_free(image, TRUE);
191 return rc;
192}
193
194static void put_u16(BYTE* p, UINT16 v)
195{
196 p[0] = (BYTE)(v & 0xff);
197 p[1] = (BYTE)((v >> 8) & 0xff);
198}
199
200static void put_u32(BYTE* p, UINT32 v)
201{
202 p[0] = (BYTE)(v & 0xff);
203 p[1] = (BYTE)((v >> 8) & 0xff);
204 p[2] = (BYTE)((v >> 16) & 0xff);
205 p[3] = (BYTE)((v >> 24) & 0xff);
206}
207
208/* A 24bpp BMP whose biSizeImage carries the unaligned image size
209 * (width * bytesPerPixel * height) selects the unaligned read path. The decoder
210 * must not consume more than biSizeImage bytes from the input buffer. The buffer
211 * here is allocated to the exact BMP size, so any over-read is detectable. */
212static BOOL test_unaligned_no_overread(void)
213{
214 BOOL rc = FALSE;
215 const UINT32 width = 1;
216 const UINT32 height = 10;
217 const UINT32 bpp = 3;
218 const UINT32 uscanline = width * bpp; /* 3, not a multiple of 4 */
219 const UINT32 biSizeImage = uscanline * height; /* 30 */
220 const size_t offBits = 54; /* 14 + 40 */
221 const size_t size = offBits + biSizeImage; /* 84 */
222
223 wImage* image = winpr_image_new();
224 BYTE* bmp = (BYTE*)calloc(1, size);
225 if (!image || !bmp)
226 goto fail;
227
228 bmp[0] = 'B';
229 bmp[1] = 'M';
230 put_u32(&bmp[2], (UINT32)size); /* bfSize */
231 put_u32(&bmp[10], (UINT32)offBits); /* bfOffBits */
232 put_u32(&bmp[14], 40); /* biSize */
233 put_u32(&bmp[18], width); /* biWidth */
234 put_u32(&bmp[22], (UINT32)(-(INT32)height)); /* biHeight, top-down */
235 put_u16(&bmp[26], 1); /* biPlanes */
236 put_u16(&bmp[28], 24); /* biBitCount */
237 put_u32(&bmp[30], 0); /* biCompression BI_RGB */
238 put_u32(&bmp[34], biSizeImage); /* biSizeImage */
239 for (UINT32 i = 0; i < biSizeImage; i++)
240 bmp[offBits + i] = (BYTE)i;
241
242 if (winpr_image_read_buffer(image, bmp, size) <= 0)
243 goto fail;
244
245 if ((image->width != width) || (image->height != height))
246 goto fail;
247
248 for (UINT32 row = 0; row < height; row++)
249 {
250 for (UINT32 b = 0; b < uscanline; b++)
251 {
252 if (image->data[1ULL * row * image->scanline + b] != (BYTE)(row * uscanline + b))
253 goto fail;
254 }
255 }
256
257 rc = TRUE;
258fail:
259 free(bmp);
260 winpr_image_free(image, TRUE);
261 return rc;
262}
263
264static BOOL test_load(void)
265{
266 const char* names[] = {
267 "rgb.16a.bmp", "rgb.16a.nocolor.bmp", "rgb.16.bmp", "rgb.16.nocolor.bmp",
268 "rgb.16x.bmp", "rgb.16x.nocolor.bmp", "rgb.24.bmp", "rgb.24.nocolor.bmp",
269 "rgb.32.bmp", "rgb.32.nocolor.bmp", "rgb.32x.bmp", "rgb.32x.nocolor.bmp",
270 "rgb.bmp"
271 };
272
273 for (size_t x = 0; x < ARRAYSIZE(names); x++)
274 {
275 const char* name = names[x];
276 char* fname = GetCombinedPath(TEST_SOURCE_PATH, name);
277 const BOOL res = test_load_file(fname);
278 free(fname);
279 if (!res)
280 return FALSE;
281 }
282
283 return TRUE;
284}
285
286/* Pixel data may follow color masks and additional padding after BITMAPINFOHEADER. */
287static BOOL test_bmp_offbits(UINT32 compression, UINT32 padding, BOOL topDown)
288{
289 BOOL rc = FALSE;
290 const UINT32 width = 2;
291 const UINT32 height = 2;
292 const UINT32 bpp = 32;
293 const UINT32 stride = width * bpp / 8;
294 const UINT32 biSizeImage = stride * height;
295 const size_t headerSize = 54; /* 14 byte file header + 40 byte info header */
296 const size_t maskSize = (compression == BI_BITFIELDS) ? 3 * sizeof(DWORD) : 0;
297 const size_t minOffset = headerSize + maskSize;
298 const size_t offBits = minOffset + padding;
299 const size_t size = offBits + biSizeImage;
300
301 wImage* image = winpr_image_new();
302 BYTE* bmp = (BYTE*)calloc(1, size);
303 if (!image || !bmp)
304 goto fail;
305
306 bmp[0] = 'B';
307 bmp[1] = 'M';
308 put_u32(&bmp[2], (UINT32)size); /* bfSize */
309 put_u32(&bmp[10], (UINT32)offBits); /* bfOffBits */
310 put_u32(&bmp[14], 40); /* biSize */
311 put_u32(&bmp[18], width); /* biWidth */
312 put_u32(&bmp[22], topDown ? (UINT32)(-(INT32)height) : height); /* biHeight */
313 put_u16(&bmp[26], 1); /* biPlanes */
314 put_u16(&bmp[28], bpp); /* biBitCount */
315 put_u32(&bmp[30], compression); /* biCompression */
316 put_u32(&bmp[34], biSizeImage); /* biSizeImage */
317 if (compression == BI_BITFIELDS)
318 {
319 put_u32(&bmp[54], 0x00FF0000); /* red color mask */
320 put_u32(&bmp[58], 0x0000FF00); /* green color mask */
321 put_u32(&bmp[62], 0x000000FF); /* blue color mask */
322 }
323
324 {
325 const BYTE pixels[16] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
326 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18 };
327 memcpy(&bmp[offBits], pixels, sizeof(pixels));
328
329 if (winpr_image_read_buffer(image, bmp, size) <= 0)
330 {
331 (void)fprintf(stderr, "[%s] failed to read BMP at offset %" PRIuz "\n", __func__,
332 offBits);
333 goto fail;
334 }
335
336 if ((image->width != width) || (image->height != height) || (image->bitsPerPixel != bpp) ||
337 (image->bytesPerPixel != bpp / 8) || (image->scanline != stride) || !image->data)
338 {
339 (void)fprintf(stderr, "[%s] unexpected image layout\n", __func__);
340 goto fail;
341 }
342
343 for (size_t row = 0; row < height; row++)
344 {
345 const size_t srcRow = topDown ? row : height - 1 - row;
346 if (memcmp(&image->data[row * image->scanline], &pixels[srcRow * stride], stride) != 0)
347 {
348 (void)fprintf(stderr, "[%s] unexpected pixels in row %" PRIuz "\n", __func__, row);
349 goto fail;
350 }
351 }
352 }
353
354 /* Reject offsets overlapping headers or masks, beyond the buffer, or leaving
355 * too few bytes for the pixel data. */
356 {
357 const UINT32 offsets[] = { 0,
358 (UINT32)(headerSize - 1),
359 (UINT32)(minOffset - 1),
360 (UINT32)(size - 1),
361 (UINT32)size,
362 (UINT32)(size + 1),
363 UINT32_MAX };
364 for (size_t x = 0; x < ARRAYSIZE(offsets); x++)
365 {
366 winpr_image_free(image, TRUE);
367 image = winpr_image_new();
368 if (!image)
369 goto fail;
370 put_u32(&bmp[10], offsets[x]); /* bfOffBits */
371 if (winpr_image_read_buffer(image, bmp, size) > 0)
372 {
373 (void)fprintf(stderr, "[%s] accepted invalid offset %" PRIu32 "\n", __func__,
374 offsets[x]);
375 goto fail;
376 }
377 }
378 }
379
380 rc = TRUE;
381fail:
382 free(bmp);
383 winpr_image_free(image, TRUE);
384 return rc;
385}
386
387int TestImage(int argc, char* argv[])
388{
389 int rc = 0;
390
391 WINPR_UNUSED(argc);
392 WINPR_UNUSED(argv);
393
394 if (!test_equal())
395 rc -= 1;
396
397 if (!test_read_write())
398 rc -= 2;
399
400 if (!test_load())
401 rc -= 4;
402
403 if (!test_unaligned_no_overread())
404 rc -= 8;
405
406 if (!test_bmp_offbits(BI_BITFIELDS, 0, FALSE) || !test_bmp_offbits(BI_BITFIELDS, 4, TRUE) ||
407 !test_bmp_offbits(BI_RGB, 0, FALSE) || !test_bmp_offbits(BI_RGB, 12, TRUE))
408 rc -= 16;
409
410 return rc;
411}