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