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