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