FreeRDP
Loading...
Searching...
No Matches
image.c
1
22#include <stdlib.h>
23
24#include <winpr/config.h>
25
26#include <winpr/wtypes.h>
27#include <winpr/crt.h>
28#include <winpr/file.h>
29#include <winpr/cast.h>
30
31#include <winpr/image.h>
32
33#if defined(WINPR_UTILS_IMAGE_PNG)
34#include <png.h>
35#endif
36
37#if defined(WINPR_UTILS_IMAGE_JPEG)
38#define INT32 INT32_WINPR
39#include <jpeglib.h>
40#undef INT32
41#endif
42
43#if defined(WINPR_UTILS_IMAGE_WEBP)
44#include <webp/encode.h>
45#include <webp/decode.h>
46#endif
47
48#if defined(WITH_LODEPNG)
49#include <lodepng.h>
50#endif
51#include <winpr/stream.h>
52
53#include "image.h"
54#include "../log.h"
55#define TAG WINPR_TAG("utils.image")
56
57static SSIZE_T winpr_convert_from_jpeg(const BYTE* comp_data, size_t comp_data_bytes, UINT32* width,
58 UINT32* height, UINT32* bpp, BYTE** ppdecomp_data);
59static SSIZE_T winpr_convert_from_png(const BYTE* comp_data, size_t comp_data_bytes, UINT32* width,
60 UINT32* height, UINT32* bpp, BYTE** ppdecomp_data);
61static SSIZE_T winpr_convert_from_webp(const BYTE* comp_data, size_t comp_data_bytes, UINT32* width,
62 UINT32* height, UINT32* bpp, BYTE** ppdecomp_data);
63
64BOOL writeBitmapFileHeader(wStream* s, const WINPR_BITMAP_FILE_HEADER* bf)
65{
66 if (!Stream_EnsureRemainingCapacity(s, sizeof(WINPR_BITMAP_FILE_HEADER)))
67 return FALSE;
68
69 Stream_Write_UINT8(s, bf->bfType[0]);
70 Stream_Write_UINT8(s, bf->bfType[1]);
71 Stream_Write_UINT32(s, bf->bfSize);
72 Stream_Write_UINT16(s, bf->bfReserved1);
73 Stream_Write_UINT16(s, bf->bfReserved2);
74 Stream_Write_UINT32(s, bf->bfOffBits);
75 return TRUE;
76}
77
78BOOL readBitmapFileHeader(wStream* s, WINPR_BITMAP_FILE_HEADER* bf)
79{
80 static wLog* log = nullptr;
81 if (!log)
82 log = WLog_Get(TAG);
83
84 if (!s || !bf ||
85 (!Stream_CheckAndLogRequiredLengthWLog(log, s, sizeof(WINPR_BITMAP_FILE_HEADER))))
86 return FALSE;
87
88 Stream_Read_UINT8(s, bf->bfType[0]);
89 Stream_Read_UINT8(s, bf->bfType[1]);
90 Stream_Read_UINT32(s, bf->bfSize);
91 Stream_Read_UINT16(s, bf->bfReserved1);
92 Stream_Read_UINT16(s, bf->bfReserved2);
93 Stream_Read_UINT32(s, bf->bfOffBits);
94
95 if (bf->bfSize < sizeof(WINPR_BITMAP_FILE_HEADER))
96 {
97 WLog_Print(log, WLOG_ERROR, "Invalid bitmap::bfSize=%" PRIu32 ", require at least %" PRIuz,
98 bf->bfSize, sizeof(WINPR_BITMAP_FILE_HEADER));
99 return FALSE;
100 }
101
102 if ((bf->bfType[0] != 'B') || (bf->bfType[1] != 'M'))
103 {
104 WLog_Print(log, WLOG_ERROR, "Invalid bitmap header [%c%c], expected [BM]", bf->bfType[0],
105 bf->bfType[1]);
106 return FALSE;
107 }
108 return Stream_CheckAndLogRequiredLengthWLog(log, s,
109 bf->bfSize - sizeof(WINPR_BITMAP_FILE_HEADER));
110}
111
112BOOL writeBitmapInfoHeader(wStream* s, const WINPR_BITMAP_INFO_HEADER* bi)
113{
114 if (!Stream_EnsureRemainingCapacity(s, sizeof(WINPR_BITMAP_INFO_HEADER)))
115 return FALSE;
116
117 Stream_Write_UINT32(s, bi->biSize);
118 Stream_Write_INT32(s, bi->biWidth);
119 Stream_Write_INT32(s, bi->biHeight);
120 Stream_Write_UINT16(s, bi->biPlanes);
121 Stream_Write_UINT16(s, bi->biBitCount);
122 Stream_Write_UINT32(s, bi->biCompression);
123 Stream_Write_UINT32(s, bi->biSizeImage);
124 Stream_Write_INT32(s, bi->biXPelsPerMeter);
125 Stream_Write_INT32(s, bi->biYPelsPerMeter);
126 Stream_Write_UINT32(s, bi->biClrUsed);
127 Stream_Write_UINT32(s, bi->biClrImportant);
128 return TRUE;
129}
130
131BOOL readBitmapInfoHeader(wStream* s, WINPR_BITMAP_INFO_HEADER* bi, size_t* poffset)
132{
133 if (!s || !bi || (!Stream_CheckAndLogRequiredLength(TAG, s, sizeof(WINPR_BITMAP_INFO_HEADER))))
134 return FALSE;
135
136 const size_t start = Stream_GetPosition(s);
137 Stream_Read_UINT32(s, bi->biSize);
138 Stream_Read_INT32(s, bi->biWidth);
139 Stream_Read_INT32(s, bi->biHeight);
140 Stream_Read_UINT16(s, bi->biPlanes);
141 Stream_Read_UINT16(s, bi->biBitCount);
142 Stream_Read_UINT32(s, bi->biCompression);
143 Stream_Read_UINT32(s, bi->biSizeImage);
144 Stream_Read_INT32(s, bi->biXPelsPerMeter);
145 Stream_Read_INT32(s, bi->biYPelsPerMeter);
146 Stream_Read_UINT32(s, bi->biClrUsed);
147 Stream_Read_UINT32(s, bi->biClrImportant);
148
149 if ((bi->biBitCount < 1) || (bi->biBitCount > 32))
150 {
151 WLog_WARN(TAG, "invalid biBitCount=%" PRIu32, bi->biBitCount);
152 return FALSE;
153 }
154
155 if (bi->biWidth < 0)
156 {
157 WLog_WARN(TAG, "negative biWidth=%" PRId32, bi->biWidth);
158 return FALSE;
159 }
160
161 /* https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader */
162 size_t offset = 0;
163 switch (bi->biCompression)
164 {
165 case BI_RGB:
166 if (bi->biBitCount <= 8)
167 {
168 DWORD used = bi->biClrUsed;
169 if (used == 0)
170 used = 1u << bi->biBitCount;
171 offset += sizeof(RGBQUAD) * used;
172 }
173 if (bi->biSizeImage == 0)
174 {
175 const UINT64 rawustride =
176 WINPR_ASSERTING_INT_CAST(UINT64, bi->biWidth) * bi->biBitCount;
177 const UINT64 ustride = ((rawustride + 31) & ~31u) >> 3;
178 if (ustride > UINT32_MAX)
179 {
180 WLog_ERR(TAG, "bi->biWidth * bi->biBitCount > UINT32_MAX");
181 return FALSE;
182 }
183
184 const UINT32 stride = WINPR_ASSERTING_INT_CAST(uint32_t, ustride);
185 const UINT32 usize = WINPR_ASSERTING_INT_CAST(UINT32, llabs(bi->biHeight)) * stride;
186 if (usize > UINT32_MAX)
187 {
188 WLog_ERR(TAG, "abs(bi->biHeight) * stride > UINT32_MAX");
189 return FALSE;
190 }
191 bi->biSizeImage = WINPR_ASSERTING_INT_CAST(uint32_t, usize);
192 }
193 break;
194 case BI_BITFIELDS:
195 /* BITMAPV4HEADER and BITMAPV5HEADER include the color masks in biSize. */
196 if (bi->biSize == sizeof(WINPR_BITMAP_INFO_HEADER))
197 offset += sizeof(DWORD) * 3; // 3 DWORD color masks
198 break;
199 default:
200 WLog_ERR(TAG, "unsupported biCompression %" PRIu32, bi->biCompression);
201 return FALSE;
202 }
203
204 if (bi->biSizeImage == 0)
205 {
206 WLog_ERR(TAG, "invalid biSizeImage %" PRIu32, bi->biSizeImage);
207 return FALSE;
208 }
209
210 const size_t pos = Stream_GetPosition(s) - start;
211 if (bi->biSize < pos)
212 {
213 WLog_ERR(TAG, "invalid biSize %" PRIu32 " < (actual) offset %" PRIuz, bi->biSize, pos);
214 return FALSE;
215 }
216
217 *poffset = offset;
218 return Stream_SafeSeek(s, bi->biSize - pos);
219}
220
221BYTE* winpr_bitmap_construct_header(size_t width, size_t height, size_t bpp)
222{
223 BYTE* result = nullptr;
224 WINPR_BITMAP_FILE_HEADER bf = WINPR_C_ARRAY_INIT;
225 WINPR_BITMAP_INFO_HEADER bi = WINPR_C_ARRAY_INIT;
226
227 size_t stride = (width * bpp + 7) / 8;
228 if ((stride % 4) != 0)
229 stride += 4 - (stride % 4);
230
231 size_t imgSize = stride * height;
232 if ((width > INT32_MAX) || (height > INT32_MAX) || (bpp > UINT16_MAX) || (imgSize > UINT32_MAX))
233 return nullptr;
234
235 wStream* s = Stream_New(nullptr, WINPR_IMAGE_BMP_HEADER_LEN);
236 if (!s)
237 return nullptr;
238
239 bf.bfType[0] = 'B';
240 bf.bfType[1] = 'M';
241 bf.bfReserved1 = 0;
242 bf.bfReserved2 = 0;
243 bi.biSize = (UINT32)sizeof(WINPR_BITMAP_INFO_HEADER);
244 bf.bfOffBits = (UINT32)sizeof(WINPR_BITMAP_FILE_HEADER) + bi.biSize;
245 bi.biSizeImage = (UINT32)imgSize;
246 bf.bfSize = bf.bfOffBits + bi.biSizeImage;
247 bi.biWidth = (INT32)width;
248 bi.biHeight = -1 * (INT32)height;
249 bi.biPlanes = 1;
250 bi.biBitCount = (UINT16)bpp;
251 bi.biCompression = BI_RGB;
252 bi.biXPelsPerMeter = (INT32)width;
253 bi.biYPelsPerMeter = (INT32)height;
254 bi.biClrUsed = 0;
255 bi.biClrImportant = 0;
256
257 size_t offset = 0;
258 switch (bi.biCompression)
259 {
260 case BI_RGB:
261 if (bi.biBitCount <= 8)
262 {
263 DWORD used = bi.biClrUsed;
264 if (used == 0)
265 used = (1u << bi.biBitCount) / 8;
266 offset += sizeof(RGBQUAD) * used;
267 }
268 break;
269 case BI_BITFIELDS:
270 offset += sizeof(DWORD) * 3; // 3 DWORD color masks
271 break;
272 default:
273 return nullptr;
274 }
275
276 if (!writeBitmapFileHeader(s, &bf))
277 goto fail;
278
279 if (!writeBitmapInfoHeader(s, &bi))
280 goto fail;
281
282 if (!Stream_EnsureRemainingCapacity(s, offset))
283 goto fail;
284
285 Stream_Zero(s, offset);
286 result = Stream_Buffer(s);
287fail:
288 Stream_Free(s, result == nullptr);
289 return result;
290}
291
296WINPR_ATTR_MALLOC(free, 1)
297WINPR_ATTR_NODISCARD
298static void* winpr_bitmap_write_buffer(const BYTE* data, WINPR_ATTR_UNUSED size_t size,
299 UINT32 width, UINT32 height, UINT32 stride, UINT32 bpp,
300 UINT32* pSize)
301{
302 WINPR_ASSERT(data || (size == 0));
303
304 void* result = nullptr;
305 size_t bpp_stride = 1ull * width * ((bpp + 7ull) / 8ull);
306 if ((bpp_stride % 4) != 0)
307 bpp_stride += 4 - (bpp_stride % 4);
308
309 if ((bpp_stride < 4) || (bpp_stride > UINT32_MAX))
310 return nullptr;
311
312 wStream* s = Stream_New(nullptr, 1024);
313
314 if (stride == 0)
315 stride = (UINT32)bpp_stride;
316
317 BYTE* bmp_header = winpr_bitmap_construct_header(width, height, bpp);
318 if (!bmp_header)
319 goto fail;
320 if (bpp_stride < stride)
321 goto fail;
322 if (!Stream_EnsureRemainingCapacity(s, WINPR_IMAGE_BMP_HEADER_LEN))
323 goto fail;
324 Stream_Write(s, bmp_header, WINPR_IMAGE_BMP_HEADER_LEN);
325
326 if (!Stream_EnsureRemainingCapacity(s, 1ULL * bpp_stride * height))
327 goto fail;
328
329 for (size_t y = 0; y < height; y++)
330 {
331 const BYTE* line = &data[stride * y];
332
333 Stream_Write(s, line, stride);
334 Stream_Zero(s, bpp_stride - stride);
335 }
336
337 result = Stream_Buffer(s);
338 {
339 const size_t pos = Stream_GetPosition(s);
340 if (pos > UINT32_MAX)
341 goto fail;
342 *pSize = (UINT32)pos;
343 }
344fail:
345 Stream_Free(s, result == nullptr);
346 free(bmp_header);
347 return result;
348}
349
350int winpr_bitmap_write(const char* filename, const BYTE* data, size_t width, size_t height,
351 size_t bpp)
352{
353 return winpr_bitmap_write_ex(filename, data, 0, width, height, bpp);
354}
355
356int winpr_bitmap_write_ex(const char* filename, const BYTE* data, size_t stride, size_t width,
357 size_t height, size_t bpp)
358{
359 FILE* fp = nullptr;
360 int ret = -1;
361 void* bmpdata = nullptr;
362 const size_t bpp_stride = ((((width * bpp) + 31) & (size_t)~31) >> 3);
363
364 if ((stride > UINT32_MAX) || (width > UINT32_MAX) || (height > UINT32_MAX) ||
365 (bpp > UINT32_MAX))
366 goto fail;
367
368 if (stride == 0)
369 stride = bpp_stride;
370
371 {
372 UINT32 bmpsize = 0;
373 {
374 const size_t size = stride * 1ull * height;
375 bmpdata = winpr_bitmap_write_buffer(data, size, (UINT32)width, (UINT32)height,
376 (UINT32)stride, (UINT32)bpp, &bmpsize);
377 }
378 if (!bmpdata)
379 goto fail;
380
381 fp = winpr_fopen(filename, "w+b");
382 if (!fp)
383 {
384 WLog_ERR(TAG, "failed to open file %s", filename);
385 goto fail;
386 }
387
388 if (fwrite(bmpdata, bmpsize, 1, fp) != 1)
389 goto fail;
390 }
391
392 ret = 0;
393fail:
394 if (fp)
395 (void)fclose(fp);
396 free(bmpdata);
397 return ret;
398}
399
400static int write_and_free(const char* filename, void* data, size_t size)
401{
402 int status = -1;
403 if (!data)
404 goto fail;
405
406 {
407 FILE* fp = winpr_fopen(filename, "w+b");
408 if (!fp)
409 goto fail;
410
411 {
412 const size_t w = fwrite(data, 1, size, fp);
413 (void)fclose(fp);
414 status = (w == size) ? 1 : -1;
415 }
416 }
417fail:
418 free(data);
419 return status;
420}
421
422int winpr_image_write(wImage* image, const char* filename)
423{
424 WINPR_ASSERT(image);
425 return winpr_image_write_ex(image, WINPR_ASSERTING_INT_CAST(uint32_t, image->type), filename);
426}
427
428int winpr_image_write_ex(wImage* image, UINT32 format, const char* filename)
429{
430 WINPR_ASSERT(image);
431
432 size_t size = 0;
433 void* data = winpr_image_write_buffer(image, format, &size);
434 if (!data)
435 return -1;
436 return write_and_free(filename, data, size);
437}
438
439static int winpr_image_bitmap_read_buffer(wImage* image, const BYTE* buffer, size_t size)
440{
441 int rc = -1;
442 BOOL vFlip = 0;
443 WINPR_BITMAP_FILE_HEADER bf = WINPR_C_ARRAY_INIT;
444 WINPR_BITMAP_INFO_HEADER bi = WINPR_C_ARRAY_INIT;
445 wStream sbuffer = WINPR_C_ARRAY_INIT;
446 wStream* s = Stream_StaticConstInit(&sbuffer, buffer, size);
447
448 if (!s)
449 return -1;
450
451 size_t bmpoffset = 0;
452 if (!readBitmapFileHeader(s, &bf) || !readBitmapInfoHeader(s, &bi, &bmpoffset))
453 goto fail;
454
455 if ((bf.bfType[0] != 'B') || (bf.bfType[1] != 'M'))
456 {
457 WLog_WARN(TAG, "Invalid bitmap header %c%c", bf.bfType[0], bf.bfType[1]);
458 goto fail;
459 }
460
461 image->type = WINPR_IMAGE_BITMAP;
462
463 {
464 /* bfOffBits may include color masks, a color table or padding after the
465 * info header. It must leave room for the required color data. */
466 const size_t pos = Stream_GetPosition(s);
467 const size_t expect = bf.bfOffBits;
468 if ((pos > expect) || (bmpoffset > expect - pos))
469 {
470 WLog_WARN(TAG, "pos=%" PRIuz ", expected %" PRIuz ", offset=%" PRIuz, pos, expect,
471 bmpoffset);
472 goto fail;
473 }
474
475 if (!Stream_SafeSeek(s, expect - pos))
476 goto fail;
477 }
478
479 if (!Stream_CheckAndLogRequiredLength(TAG, s, bi.biSizeImage))
480 goto fail;
481
482 if (bi.biWidth <= 0)
483 {
484 WLog_WARN(TAG, "bi.biWidth=%" PRId32, bi.biWidth);
485 goto fail;
486 }
487
488 image->width = (UINT32)bi.biWidth;
489
490 if (bi.biHeight < 0)
491 {
492 vFlip = FALSE;
493 image->height = (UINT32)(-1 * bi.biHeight);
494 }
495 else
496 {
497 vFlip = TRUE;
498 image->height = (UINT32)bi.biHeight;
499 }
500
501 if (image->height <= 0)
502 {
503 WLog_WARN(TAG, "image->height=%" PRIu32, image->height);
504 goto fail;
505 }
506
507 image->bitsPerPixel = bi.biBitCount;
508 {
509 const size_t bpp = (bi.biBitCount + 7UL) / 8UL;
510 image->bytesPerPixel = WINPR_ASSERTING_INT_CAST(uint32_t, bpp);
511
512 image->scanline = WINPR_ASSERTING_INT_CAST(uint32_t, bi.biWidth) * image->bytesPerPixel;
513 if ((image->scanline % 4) != 0)
514 image->scanline += 4 - image->scanline % 4;
515
516 {
517 const size_t bmpsize = 1ULL * image->scanline * image->height;
518 if (bmpsize != bi.biSizeImage)
519 WLog_WARN(TAG, "bmpsize=%" PRIuz " != bi.biSizeImage=%" PRIu32, bmpsize,
520 bi.biSizeImage);
521
522 {
523 size_t scanline = image->scanline;
524 if (bi.biSizeImage < bmpsize)
525 {
526 /* Workaround for unaligned bitmaps */
527 const size_t uscanline = image->width * bpp;
528 const size_t unaligned = image->height * uscanline;
529 if (bi.biSizeImage != unaligned)
530 goto fail;
531 scanline = uscanline;
532 }
533
534 image->data = nullptr;
535 {
536 const size_t asize = 1ULL * image->scanline * image->height;
537 if (asize > 0)
538 image->data = (BYTE*)malloc(asize);
539 }
540
541 if (!image->data)
542 goto fail;
543
544 if (!vFlip)
545 {
546 BYTE* pDstData = image->data;
547
548 for (size_t index = 0; index < image->height; index++)
549 {
550 Stream_Read(s, pDstData, scanline);
551 pDstData += image->scanline;
552 }
553 }
554 else
555 {
556 BYTE* pDstData = &(image->data[(image->height - 1ull) * image->scanline]);
557
558 for (size_t index = 0; index < image->height; index++)
559 {
560 Stream_Read(s, pDstData, scanline);
561 pDstData -= image->scanline;
562 }
563 }
564 }
565 }
566 }
567
568 rc = 1;
569fail:
570
571 if (rc < 0)
572 {
573 free(image->data);
574 image->data = nullptr;
575 }
576
577 return rc;
578}
579
580int winpr_image_read(wImage* image, const char* filename)
581{
582 int status = -1;
583
584 FILE* fp = winpr_fopen(filename, "rb");
585 if (!fp)
586 {
587 WLog_ERR(TAG, "failed to open file %s", filename);
588 return -1;
589 }
590
591 (void)fseek(fp, 0, SEEK_END);
592 INT64 pos = _ftelli64(fp);
593 (void)fseek(fp, 0, SEEK_SET);
594
595 if (pos > 0)
596 {
597 BYTE* buffer = malloc((size_t)pos);
598 if (buffer)
599 {
600 size_t r = fread(buffer, 1, (size_t)pos, fp);
601 if (r == (size_t)pos)
602 {
603 status = winpr_image_read_buffer(image, buffer, (size_t)pos);
604 }
605 }
606 free(buffer);
607 }
608 (void)fclose(fp);
609 return status;
610}
611
612int winpr_image_read_buffer(wImage* image, const BYTE* buffer, size_t size)
613{
614 BYTE sig[12] = WINPR_C_ARRAY_INIT;
615 int status = -1;
616
617 if (size < sizeof(sig))
618 return -1;
619
620 CopyMemory(sig, buffer, sizeof(sig));
621
622 if ((sig[0] == 'B') && (sig[1] == 'M'))
623 {
624 image->type = WINPR_IMAGE_BITMAP;
625 status = winpr_image_bitmap_read_buffer(image, buffer, size);
626 }
627 else if ((sig[0] == 'R') && (sig[1] == 'I') && (sig[2] == 'F') && (sig[3] == 'F') &&
628 (sig[8] == 'W') && (sig[9] == 'E') && (sig[10] == 'B') && (sig[11] == 'P'))
629 {
630 image->type = WINPR_IMAGE_WEBP;
631 const SSIZE_T rc = winpr_convert_from_webp(buffer, size, &image->width, &image->height,
632 &image->bitsPerPixel, &image->data);
633 if (rc >= 0)
634 {
635 image->bytesPerPixel = (image->bitsPerPixel + 7) / 8;
636 image->scanline = image->width * image->bytesPerPixel;
637 status = 1;
638 }
639 }
640 else if ((sig[0] == 0xFF) && (sig[1] == 0xD8) && (sig[2] == 0xFF) && (sig[3] == 0xE0) &&
641 (sig[6] == 0x4A) && (sig[7] == 0x46) && (sig[8] == 0x49) && (sig[9] == 0x46) &&
642 (sig[10] == 0x00))
643 {
644 image->type = WINPR_IMAGE_JPEG;
645 const SSIZE_T rc = winpr_convert_from_jpeg(buffer, size, &image->width, &image->height,
646 &image->bitsPerPixel, &image->data);
647 if (rc >= 0)
648 {
649 image->bytesPerPixel = (image->bitsPerPixel + 7) / 8;
650 image->scanline = image->width * image->bytesPerPixel;
651 status = 1;
652 }
653 }
654 else if ((sig[0] == 0x89) && (sig[1] == 'P') && (sig[2] == 'N') && (sig[3] == 'G') &&
655 (sig[4] == '\r') && (sig[5] == '\n') && (sig[6] == 0x1A) && (sig[7] == '\n'))
656 {
657 image->type = WINPR_IMAGE_PNG;
658 const SSIZE_T rc = winpr_convert_from_png(buffer, size, &image->width, &image->height,
659 &image->bitsPerPixel, &image->data);
660 if (rc >= 0)
661 {
662 image->bytesPerPixel = (image->bitsPerPixel + 7) / 8;
663 image->scanline = image->width * image->bytesPerPixel;
664 status = 1;
665 }
666 }
667
668 return status;
669}
670
671wImage* winpr_image_new(void)
672{
673 wImage* image = (wImage*)calloc(1, sizeof(wImage));
674
675 if (!image)
676 return nullptr;
677
678 return image;
679}
680
681void winpr_image_free(wImage* image, BOOL bFreeBuffer)
682{
683 if (!image)
684 return;
685
686 if (bFreeBuffer)
687 free(image->data);
688
689 free(image);
690}
691
692static void* winpr_convert_to_jpeg(WINPR_ATTR_UNUSED const void* data,
693 WINPR_ATTR_UNUSED size_t size, WINPR_ATTR_UNUSED UINT32 width,
694 WINPR_ATTR_UNUSED UINT32 height, WINPR_ATTR_UNUSED UINT32 stride,
695 WINPR_ATTR_UNUSED UINT32 bpp, WINPR_ATTR_UNUSED UINT32* pSize)
696{
697 WINPR_ASSERT(data || (size == 0));
698 WINPR_ASSERT(pSize);
699
700 *pSize = 0;
701
702#if !defined(WINPR_UTILS_IMAGE_JPEG)
703 WLog_WARN(TAG, "JPEG not supported in this build");
704 return nullptr;
705#else
706 BYTE* outbuffer = nullptr;
707 unsigned long outsize = 0;
708 struct jpeg_compress_struct cinfo = WINPR_C_ARRAY_INIT;
709
710 const size_t expect1 = 1ull * stride * height;
711 const size_t bytes = (bpp + 7) / 8;
712 const size_t expect2 = 1ull * width * height * bytes;
713 if (expect1 < expect2)
714 return nullptr;
715 if (expect1 > size)
716 return nullptr;
717
718 /* Set up the error handler. */
719 struct jpeg_error_mgr jerr = WINPR_C_ARRAY_INIT;
720 cinfo.err = jpeg_std_error(&jerr);
721
722 jpeg_create_compress(&cinfo);
723 jpeg_mem_dest(&cinfo, &outbuffer, &outsize);
724
725 cinfo.image_width = width;
726 cinfo.image_height = height;
727 WINPR_ASSERT(bpp <= INT32_MAX / 8);
728 cinfo.input_components = (int)(bpp + 7) / 8;
729 cinfo.in_color_space = (bpp > 24) ? JCS_EXT_BGRA : JCS_EXT_BGR;
730 cinfo.data_precision = 8;
731
732 jpeg_set_defaults(&cinfo);
733 jpeg_set_quality(&cinfo, 100, TRUE);
734 /* Use 4:4:4 subsampling (default is 4:2:0) */
735 cinfo.comp_info[0].h_samp_factor = cinfo.comp_info[0].v_samp_factor = 1;
736
737 jpeg_start_compress(&cinfo, TRUE);
738
739 const JSAMPLE* cdata = data;
740 for (size_t x = 0; x < height; x++)
741 {
742 WINPR_ASSERT(x * stride <= UINT32_MAX);
743 const JDIMENSION offset = (JDIMENSION)x * stride;
744
745 /* libjpeg is not const correct, we must cast here to avoid issues
746 * with newer C compilers type check errors */
747 JSAMPLE* coffset = WINPR_CAST_CONST_PTR_AWAY(&cdata[offset], JSAMPLE*);
748 if (jpeg_write_scanlines(&cinfo, &coffset, 1) != 1)
749 goto fail;
750 }
751
752fail:
753 jpeg_finish_compress(&cinfo);
754 jpeg_destroy_compress(&cinfo);
755
756 WINPR_ASSERT(outsize <= UINT32_MAX);
757 *pSize = (UINT32)outsize;
758 return outbuffer;
759#endif
760}
761
762// NOLINTBEGIN(readability-non-const-parameter)
763SSIZE_T winpr_convert_from_jpeg(WINPR_ATTR_UNUSED const BYTE* comp_data,
764 WINPR_ATTR_UNUSED size_t comp_data_bytes,
765 WINPR_ATTR_UNUSED UINT32* width, WINPR_ATTR_UNUSED UINT32* height,
766 WINPR_ATTR_UNUSED UINT32* bpp,
767 WINPR_ATTR_UNUSED BYTE** ppdecomp_data)
768// NOLINTEND(readability-non-const-parameter)
769{
770 WINPR_ASSERT(comp_data || (comp_data_bytes == 0));
771 WINPR_ASSERT(width);
772 WINPR_ASSERT(height);
773 WINPR_ASSERT(bpp);
774 WINPR_ASSERT(ppdecomp_data);
775
776#if !defined(WINPR_UTILS_IMAGE_JPEG)
777 WLog_WARN(TAG, "JPEG not supported in this build");
778 return -1;
779#else
780 struct jpeg_decompress_struct cinfo = WINPR_C_ARRAY_INIT;
781 struct jpeg_error_mgr jerr;
782 SSIZE_T size = -1;
783 BYTE* decomp_data = nullptr;
784
785 cinfo.err = jpeg_std_error(&jerr);
786 jpeg_create_decompress(&cinfo);
787 jpeg_mem_src(&cinfo, comp_data, comp_data_bytes);
788
789 if (jpeg_read_header(&cinfo, 1) != JPEG_HEADER_OK)
790 goto fail;
791
792 cinfo.out_color_space = cinfo.num_components > 3 ? JCS_EXT_RGBA : JCS_EXT_BGR;
793
794 *width = WINPR_ASSERTING_INT_CAST(uint32_t, cinfo.image_width);
795 *height = WINPR_ASSERTING_INT_CAST(uint32_t, cinfo.image_height);
796 *bpp = WINPR_ASSERTING_INT_CAST(uint32_t, cinfo.num_components * 8);
797
798 if (!jpeg_start_decompress(&cinfo))
799 goto fail;
800
801 size_t stride =
802 1ULL * cinfo.image_width * WINPR_ASSERTING_INT_CAST(uint32_t, cinfo.num_components);
803
804 if ((stride == 0) || (cinfo.image_height == 0))
805 goto fail;
806
807 decomp_data = calloc(stride, cinfo.image_height);
808 if (decomp_data)
809 {
810 while (cinfo.output_scanline < cinfo.image_height)
811 {
812 JSAMPROW row = &decomp_data[cinfo.output_scanline * stride];
813 if (jpeg_read_scanlines(&cinfo, &row, 1) != 1)
814 goto fail;
815 }
816 const size_t ssize = stride * cinfo.image_height;
817 WINPR_ASSERT(ssize < SSIZE_MAX);
818 size = (SSIZE_T)ssize;
819 }
820 jpeg_finish_decompress(&cinfo);
821
822fail:
823 jpeg_destroy_decompress(&cinfo);
824 *ppdecomp_data = decomp_data;
825 return size;
826#endif
827}
828
829static void* winpr_convert_to_webp(WINPR_ATTR_UNUSED const void* data,
830 WINPR_ATTR_UNUSED size_t size, WINPR_ATTR_UNUSED UINT32 width,
831 WINPR_ATTR_UNUSED UINT32 height, WINPR_ATTR_UNUSED UINT32 stride,
832 WINPR_ATTR_UNUSED UINT32 bpp, UINT32* pSize)
833{
834 WINPR_ASSERT(data || (size == 0));
835 WINPR_ASSERT(pSize);
836
837 *pSize = 0;
838
839#if !defined(WINPR_UTILS_IMAGE_WEBP)
840 WLog_WARN(TAG, "WEBP not supported in this build");
841 return nullptr;
842#else
843 size_t dstSize = 0;
844 uint8_t* pDstData = nullptr;
845 WINPR_ASSERT(width <= INT32_MAX);
846 WINPR_ASSERT(height <= INT32_MAX);
847 WINPR_ASSERT(stride <= INT32_MAX);
848 switch (bpp)
849 {
850 case 32:
851 dstSize = WebPEncodeLosslessBGRA(data, (int)width, (int)height, (int)stride, &pDstData);
852 break;
853 case 24:
854 dstSize = WebPEncodeLosslessBGR(data, (int)width, (int)height, (int)stride, &pDstData);
855 break;
856 default:
857 return nullptr;
858 }
859
860 void* rc = malloc(dstSize);
861 if (rc)
862 {
863 memcpy(rc, pDstData, dstSize);
864
865 WINPR_ASSERT(dstSize <= UINT32_MAX);
866 *pSize = (UINT32)dstSize;
867 }
868 WebPFree(pDstData);
869 return rc;
870#endif
871}
872
873SSIZE_T winpr_convert_from_webp(WINPR_ATTR_UNUSED const BYTE* comp_data,
874 WINPR_ATTR_UNUSED size_t comp_data_bytes, UINT32* width,
875 UINT32* height, UINT32* bpp, BYTE** ppdecomp_data)
876{
877 WINPR_ASSERT(comp_data || (comp_data_bytes == 0));
878 WINPR_ASSERT(width);
879 WINPR_ASSERT(height);
880 WINPR_ASSERT(bpp);
881 WINPR_ASSERT(ppdecomp_data);
882
883 *width = 0;
884 *height = 0;
885 *bpp = 0;
886 *ppdecomp_data = nullptr;
887#if !defined(WINPR_UTILS_IMAGE_WEBP)
888 WLog_WARN(TAG, "WEBP not supported in this build");
889 return -1;
890#else
891
892 int w = 0;
893 int h = 0;
894 uint8_t* dst = WebPDecodeBGRA(comp_data, comp_data_bytes, &w, &h);
895 if (!dst || (w < 0) || (h < 0))
896 {
897 free(dst);
898 return -1;
899 }
900
901 *width = WINPR_ASSERTING_INT_CAST(uint32_t, w);
902 *height = WINPR_ASSERTING_INT_CAST(uint32_t, h);
903 *bpp = 32;
904 *ppdecomp_data = dst;
905 return 4ll * w * h;
906#endif
907}
908
909#if defined(WINPR_UTILS_IMAGE_PNG)
910struct png_mem_encode
911{
912 char* buffer;
913 size_t size;
914};
915
916static void png_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
917{
918 /* with libpng15 next line causes pointer deference error; use libpng12 */
919 struct png_mem_encode* p =
920 (struct png_mem_encode*)png_get_io_ptr(png_ptr); /* was png_ptr->io_ptr */
921 size_t nsize = p->size + length;
922
923 /* allocate or grow buffer */
924 if (p->buffer)
925 {
926 char* tmp = realloc(p->buffer, nsize);
927 if (tmp)
928 p->buffer = tmp;
929 }
930 else
931 p->buffer = malloc(nsize);
932
933 if (!p->buffer)
934 png_error(png_ptr, "Write Error");
935
936 /* copy new bytes to end of buffer */
937 memcpy(p->buffer + p->size, data, length);
938 p->size += length;
939}
940
941/* This is optional but included to show how png_set_write_fn() is called */
942static void png_flush(WINPR_ATTR_UNUSED png_structp png_ptr)
943{
944}
945
946static SSIZE_T save_png_to_buffer(UINT32 bpp, UINT32 width, uint32_t stride, UINT32 height,
947 const uint8_t* data, size_t size, void** pDstData)
948{
949 SSIZE_T rc = -1;
950 png_structp png_ptr = nullptr;
951 png_infop info_ptr = nullptr;
952 png_byte** row_pointers = nullptr;
953 struct png_mem_encode state = WINPR_C_ARRAY_INIT;
954
955 *pDstData = nullptr;
956
957 if (!data || (size == 0))
958 return 0;
959
960 WINPR_ASSERT(pDstData);
961
962 if (size < (1ULL * stride * height))
963 goto fail;
964
965 /* Initialize the write struct. */
966 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
967 if (png_ptr == nullptr)
968 goto fail;
969
970 /* Initialize the info struct. */
971 info_ptr = png_create_info_struct(png_ptr);
972 if (info_ptr == nullptr)
973 goto fail;
974
975 /* Set up error handling. */
976 if (setjmp(png_jmpbuf(png_ptr)))
977 goto fail;
978
979 /* Set image attributes. */
980 int colorType = PNG_COLOR_TYPE_PALETTE;
981 if (bpp > 8)
982 colorType = PNG_COLOR_TYPE_RGB;
983 if (bpp > 16)
984 colorType = PNG_COLOR_TYPE_RGB;
985 if (bpp > 24)
986 colorType = PNG_COLOR_TYPE_RGBA;
987
988 png_set_IHDR(png_ptr, info_ptr, width, height, 8, colorType, PNG_INTERLACE_NONE,
989 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
990
991 /* Initialize rows of PNG. */
992 row_pointers = (png_byte**)png_malloc(png_ptr, height * sizeof(png_byte*));
993 for (size_t y = 0; y < height; ++y)
994 {
995 const uint8_t* line = &data[y * stride];
996 uint8_t* row = png_malloc(png_ptr, sizeof(uint8_t) * stride);
997 row_pointers[y] = (png_byte*)row;
998 for (size_t x = 0; x < width; ++x)
999 {
1000
1001 *row++ = *line++;
1002 if (bpp > 8)
1003 *row++ = *line++;
1004 if (bpp > 16)
1005 *row++ = *line++;
1006 if (bpp > 24)
1007 *row++ = *line++;
1008 }
1009 }
1010
1011 /* Actually write the image data. */
1012 png_set_write_fn(png_ptr, &state, png_write_data, png_flush);
1013 png_set_rows(png_ptr, info_ptr, row_pointers);
1014 png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_BGR, nullptr);
1015
1016 /* Cleanup. */
1017 for (size_t y = 0; y < height; y++)
1018 png_free(png_ptr, row_pointers[y]);
1019 png_free(png_ptr, (void*)row_pointers);
1020
1021 /* Finish writing. */
1022 if (state.size > SSIZE_MAX)
1023 goto fail;
1024 rc = (SSIZE_T)state.size;
1025 *pDstData = state.buffer;
1026fail:
1027 png_destroy_write_struct(&png_ptr, &info_ptr);
1028 if (rc < 0)
1029 free(state.buffer);
1030 return rc;
1031}
1032
1033typedef struct
1034{
1035 png_bytep buffer;
1036 png_uint_32 bufsize;
1037 png_uint_32 current_pos;
1038} MEMORY_READER_STATE;
1039
1040static void read_data_memory(png_structp png_ptr, png_bytep data, size_t length)
1041{
1042 MEMORY_READER_STATE* f = png_get_io_ptr(png_ptr);
1043 if (length > (f->bufsize - f->current_pos))
1044 png_error(png_ptr, "read error in read_data_memory (loadpng)");
1045 else
1046 {
1047 memcpy(data, f->buffer + f->current_pos, length);
1048 f->current_pos += length;
1049 }
1050}
1051
1052static void* winpr_read_png_from_buffer(const void* data, size_t SrcSize, size_t* pSize,
1053 UINT32* pWidth, UINT32* pHeight, UINT32* pBpp)
1054{
1055 void* rc = nullptr;
1056 png_uint_32 width = 0;
1057 png_uint_32 height = 0;
1058 int bit_depth = 0;
1059 int color_type = 0;
1060 int interlace_type = 0;
1061 int transforms = PNG_TRANSFORM_IDENTITY;
1062 MEMORY_READER_STATE memory_reader_state = WINPR_C_ARRAY_INIT;
1063 png_bytepp row_pointers = nullptr;
1064 png_infop info_ptr = nullptr;
1065 if (SrcSize > UINT32_MAX)
1066 return nullptr;
1067
1068 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
1069 if (!png_ptr)
1070 goto fail;
1071 info_ptr = png_create_info_struct(png_ptr);
1072 if (!info_ptr)
1073 goto fail;
1074
1075 memory_reader_state.buffer = WINPR_CAST_CONST_PTR_AWAY(data, png_bytep);
1076 memory_reader_state.bufsize = (UINT32)SrcSize;
1077 memory_reader_state.current_pos = 0;
1078
1079 png_set_read_fn(png_ptr, &memory_reader_state, read_data_memory);
1080
1081 transforms |= PNG_TRANSFORM_BGR;
1082 png_read_png(png_ptr, info_ptr, transforms, nullptr);
1083
1084 if (png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type,
1085 nullptr, nullptr) != 1)
1086 goto fail;
1087
1088 WINPR_ASSERT(bit_depth >= 0);
1089 const png_byte channelcount = png_get_channels(png_ptr, info_ptr);
1090 const size_t bpp = channelcount * (size_t)bit_depth;
1091
1092 row_pointers = png_get_rows(png_ptr, info_ptr);
1093 if (row_pointers)
1094 {
1095 const size_t stride = 1ULL * width * bpp / 8ull;
1096 const size_t png_stride = png_get_rowbytes(png_ptr, info_ptr);
1097 const size_t size = 1ULL * width * height * bpp / 8ull;
1098 const size_t copybytes = stride > png_stride ? png_stride : stride;
1099
1100 rc = malloc(size);
1101 if (rc)
1102 {
1103 char* cur = rc;
1104 for (png_uint_32 i = 0; i < height; i++)
1105 {
1106 memcpy(cur, row_pointers[i], copybytes);
1107 cur += stride;
1108 }
1109 *pSize = size;
1110 *pWidth = width;
1111 *pHeight = height;
1112 WINPR_ASSERT(bpp <= UINT32_MAX);
1113 *pBpp = (UINT32)bpp;
1114 }
1115 }
1116fail:
1117
1118 png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
1119 return rc;
1120}
1121#endif
1122
1123static void* winpr_convert_to_png(WINPR_ATTR_UNUSED const void* data, WINPR_ATTR_UNUSED size_t size,
1124 WINPR_ATTR_UNUSED UINT32 width, WINPR_ATTR_UNUSED UINT32 height,
1125 WINPR_ATTR_UNUSED UINT32 stride, WINPR_ATTR_UNUSED UINT32 bpp,
1126 UINT32* pSize)
1127{
1128 WINPR_ASSERT(data || (size == 0));
1129 WINPR_ASSERT(pSize);
1130
1131 *pSize = 0;
1132
1133#if defined(WINPR_UTILS_IMAGE_PNG)
1134 void* dst = nullptr;
1135 SSIZE_T rc = save_png_to_buffer(bpp, width, stride, height, data, size, &dst);
1136 if (rc <= 0)
1137 return nullptr;
1138 *pSize = (UINT32)rc;
1139 return dst;
1140#elif defined(WITH_LODEPNG)
1141 {
1142 BYTE* dst = nullptr;
1143 size_t dstsize = 0;
1144 unsigned rc = 1;
1145
1146 switch (bpp)
1147 {
1148 case 32:
1149 rc = lodepng_encode32(&dst, &dstsize, data, width, height);
1150 break;
1151 case 24:
1152 rc = lodepng_encode24(&dst, &dstsize, data, width, height);
1153 break;
1154 default:
1155 break;
1156 }
1157 if (rc)
1158 return nullptr;
1159 *pSize = (UINT32)dstsize;
1160 return dst;
1161 }
1162#else
1163 WLog_WARN(TAG, "PNG not supported in this build");
1164 return nullptr;
1165#endif
1166}
1167
1168SSIZE_T winpr_convert_from_png(WINPR_ATTR_UNUSED const BYTE* comp_data,
1169 WINPR_ATTR_UNUSED size_t comp_data_bytes,
1170 WINPR_ATTR_UNUSED UINT32* width, WINPR_ATTR_UNUSED UINT32* height,
1171 WINPR_ATTR_UNUSED UINT32* bpp,
1172 WINPR_ATTR_UNUSED BYTE** ppdecomp_data)
1173{
1174#if defined(WINPR_UTILS_IMAGE_PNG)
1175 size_t len = 0;
1176 *ppdecomp_data =
1177 winpr_read_png_from_buffer(comp_data, comp_data_bytes, &len, width, height, bpp);
1178 if (!*ppdecomp_data)
1179 return -1;
1180 return (SSIZE_T)len;
1181#elif defined(WITH_LODEPNG)
1182 *bpp = 32;
1183 return lodepng_decode32((unsigned char**)ppdecomp_data, width, height, comp_data,
1184 comp_data_bytes);
1185#else
1186 WLog_WARN(TAG, "PNG not supported in this build");
1187 return -1;
1188#endif
1189}
1190
1191BOOL winpr_image_format_is_supported(UINT32 format)
1192{
1193 switch (format)
1194 {
1195 case WINPR_IMAGE_BITMAP:
1196#if defined(WINPR_UTILS_IMAGE_PNG) || defined(WITH_LODEPNG)
1197 case WINPR_IMAGE_PNG:
1198#endif
1199#if defined(WINPR_UTILS_IMAGE_JPEG)
1200 case WINPR_IMAGE_JPEG:
1201#endif
1202#if defined(WINPR_UTILS_IMAGE_WEBP)
1203 case WINPR_IMAGE_WEBP:
1204#endif
1205 return TRUE;
1206 default:
1207 return FALSE;
1208 }
1209}
1210
1211static BYTE* convert(const wImage* image, size_t* pstride, WINPR_ATTR_UNUSED UINT32 flags)
1212{
1213 WINPR_ASSERT(image);
1214 WINPR_ASSERT(pstride);
1215
1216 *pstride = 0;
1217 if (image->bitsPerPixel < 24)
1218 return nullptr;
1219
1220 const size_t stride = image->width * 4ull;
1221 BYTE* data = calloc(stride, image->height);
1222 if (data)
1223 {
1224 for (size_t y = 0; y < image->height; y++)
1225 {
1226 const BYTE* srcLine = &image->data[image->scanline * y];
1227 BYTE* dstLine = &data[stride * y];
1228 if (image->bitsPerPixel == 32)
1229 memcpy(dstLine, srcLine, stride);
1230 else
1231 {
1232 for (size_t x = 0; x < image->width; x++)
1233 {
1234 const BYTE* src = &srcLine[image->bytesPerPixel * x];
1235 BYTE* dst = &dstLine[4ull * x];
1236 BYTE b = *src++;
1237 BYTE g = *src++;
1238 BYTE r = *src++;
1239
1240 *dst++ = b;
1241 *dst++ = g;
1242 *dst++ = r;
1243 *dst++ = 0xff;
1244 }
1245 }
1246 }
1247 *pstride = stride;
1248 }
1249 return data;
1250}
1251
1252static BOOL compare_byte_relaxed(BYTE a, BYTE b, UINT32 flags)
1253{
1254 if (a != b)
1255 {
1256 if ((flags & WINPR_IMAGE_CMP_FUZZY) != 0)
1257 {
1258 const int diff = abs((int)a) - abs((int)b);
1259 /* filter out quantization errors */
1260 if (diff > 6)
1261 return FALSE;
1262 }
1263 else
1264 {
1265 return FALSE;
1266 }
1267 }
1268 return TRUE;
1269}
1270
1271static BOOL compare_pixel(const BYTE* pa, const BYTE* pb, UINT32 flags)
1272{
1273 WINPR_ASSERT(pa);
1274 WINPR_ASSERT(pb);
1275
1276 if (!compare_byte_relaxed(*pa++, *pb++, flags))
1277 return FALSE;
1278 if (!compare_byte_relaxed(*pa++, *pb++, flags))
1279 return FALSE;
1280 if (!compare_byte_relaxed(*pa++, *pb++, flags))
1281 return FALSE;
1282 if ((flags & WINPR_IMAGE_CMP_IGNORE_ALPHA) == 0)
1283 {
1284 if (!compare_byte_relaxed(*pa++, *pb++, flags))
1285 return FALSE;
1286 }
1287 return TRUE;
1288}
1289
1290BOOL winpr_image_equal(const wImage* imageA, const wImage* imageB, UINT32 flags)
1291{
1292 if (imageA == imageB)
1293 return TRUE;
1294 if (!imageA || !imageB)
1295 return FALSE;
1296
1297 if (imageA->height != imageB->height)
1298 return FALSE;
1299 if (imageA->width != imageB->width)
1300 return FALSE;
1301
1302 if ((flags & WINPR_IMAGE_CMP_IGNORE_DEPTH) == 0)
1303 {
1304 if (imageA->bitsPerPixel != imageB->bitsPerPixel)
1305 return FALSE;
1306 if (imageA->bytesPerPixel != imageB->bytesPerPixel)
1307 return FALSE;
1308 }
1309
1310 BOOL rc = FALSE;
1311 size_t astride = 0;
1312 size_t bstride = 0;
1313 BYTE* dataA = convert(imageA, &astride, flags);
1314 BYTE* dataB = convert(imageA, &bstride, flags);
1315 if (dataA && dataB && (astride == bstride))
1316 {
1317 rc = TRUE;
1318 for (size_t y = 0; y < imageA->height; y++)
1319 {
1320 const BYTE* lineA = &dataA[astride * y];
1321 const BYTE* lineB = &dataB[bstride * y];
1322
1323 for (size_t x = 0; x < imageA->width; x++)
1324 {
1325 const BYTE* pa = &lineA[x * 4ull];
1326 const BYTE* pb = &lineB[x * 4ull];
1327
1328 if (!compare_pixel(pa, pb, flags))
1329 rc = FALSE;
1330 }
1331 }
1332 }
1333 free(dataA);
1334 free(dataB);
1335 return rc;
1336}
1337
1338const char* winpr_image_format_mime(UINT32 format)
1339{
1340 switch (format)
1341 {
1342 case WINPR_IMAGE_BITMAP:
1343 return "image/bmp";
1344 case WINPR_IMAGE_PNG:
1345 return "image/png";
1346 case WINPR_IMAGE_WEBP:
1347 return "image/webp";
1348 case WINPR_IMAGE_JPEG:
1349 return "image/jpeg";
1350 default:
1351 return nullptr;
1352 }
1353}
1354
1355const char* winpr_image_format_extension(UINT32 format)
1356{
1357 switch (format)
1358 {
1359 case WINPR_IMAGE_BITMAP:
1360 return "bmp";
1361 case WINPR_IMAGE_PNG:
1362 return "png";
1363 case WINPR_IMAGE_WEBP:
1364 return "webp";
1365 case WINPR_IMAGE_JPEG:
1366 return "jpg";
1367 default:
1368 return nullptr;
1369 }
1370}
1371
1372void* winpr_image_write_buffer(wImage* image, UINT32 format, size_t* psize)
1373{
1374 WINPR_ASSERT(image);
1375 switch (format)
1376 {
1377 case WINPR_IMAGE_BITMAP:
1378 {
1379 UINT32 outsize = 0;
1380 size_t size = 1ull * image->height * image->scanline;
1381 void* data = winpr_bitmap_write_buffer(image->data, size, image->width, image->height,
1382 image->scanline, image->bitsPerPixel, &outsize);
1383 *psize = outsize;
1384 return data;
1385 }
1386 case WINPR_IMAGE_WEBP:
1387 {
1388 UINT32 outsize = 0;
1389 size_t size = 1ull * image->height * image->scanline;
1390 void* data = winpr_convert_to_webp(image->data, size, image->width, image->height,
1391 image->scanline, image->bitsPerPixel, &outsize);
1392 *psize = outsize;
1393 return data;
1394 }
1395 case WINPR_IMAGE_JPEG:
1396 {
1397 UINT32 outsize = 0;
1398 size_t size = 1ull * image->height * image->scanline;
1399 void* data = winpr_convert_to_jpeg(image->data, size, image->width, image->height,
1400 image->scanline, image->bitsPerPixel, &outsize);
1401 *psize = outsize;
1402 return data;
1403 }
1404 case WINPR_IMAGE_PNG:
1405 {
1406 UINT32 outsize = 0;
1407 size_t size = 1ull * image->height * image->scanline;
1408 void* data = winpr_convert_to_png(image->data, size, image->width, image->height,
1409 image->scanline, image->bitsPerPixel, &outsize);
1410 *psize = outsize;
1411 return data;
1412 }
1413 default:
1414 *psize = 0;
1415 return nullptr;
1416 }
1417}