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