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