25#include <winpr/config.h>
27#include <winpr/wtypes.h>
29#include <winpr/file.h>
30#include <winpr/cast.h>
32#include <winpr/image.h>
34#if defined(WINPR_UTILS_IMAGE_PNG)
38#if defined(WINPR_UTILS_IMAGE_JPEG)
39#define INT32 INT32_WINPR
44#if defined(WINPR_UTILS_IMAGE_JPEG)
47 struct jpeg_error_mgr pub;
49} WINPR_JPEG_ERROR_MGR;
54WINPR_NORETURN(
static void winpr_jpeg_error_exit(j_common_ptr cinfo));
56void winpr_jpeg_error_exit(j_common_ptr cinfo)
58 WINPR_JPEG_ERROR_MGR* err = (WINPR_JPEG_ERROR_MGR*)cinfo->err;
59 longjmp(err->jump, 1);
63#if defined(WINPR_UTILS_IMAGE_WEBP)
64#include <webp/encode.h>
65#include <webp/decode.h>
68#if defined(WITH_LODEPNG)
71#include <winpr/stream.h>
75#define TAG WINPR_TAG("utils.image")
77#if defined(WINPR_UTILS_IMAGE_JPEG)
79#define MAX(a, b) ((a) > (b)) ? (a) : (b)
84static SSIZE_T winpr_convert_from_jpeg(
const BYTE* comp_data,
size_t comp_data_bytes, UINT32* width,
85 UINT32* height, UINT32* bpp, BYTE** ppdecomp_data,
89static SSIZE_T winpr_convert_from_png(
const BYTE* comp_data,
size_t comp_data_bytes, UINT32* width,
90 UINT32* height, UINT32* bpp, BYTE** ppdecomp_data,
94static SSIZE_T winpr_convert_from_webp(
const BYTE* comp_data,
size_t comp_data_bytes, UINT32* width,
95 UINT32* height, UINT32* bpp, BYTE** ppdecomp_data,
103 Stream_Write_UINT8(s, bf->bfType[0]);
104 Stream_Write_UINT8(s, bf->bfType[1]);
105 Stream_Write_UINT32(s, bf->bfSize);
106 Stream_Write_UINT16(s, bf->bfReserved1);
107 Stream_Write_UINT16(s, bf->bfReserved2);
108 Stream_Write_UINT32(s, bf->bfOffBits);
114 static wLog* log =
nullptr;
122 Stream_Read_UINT8(s, bf->bfType[0]);
123 Stream_Read_UINT8(s, bf->bfType[1]);
124 Stream_Read_UINT32(s, bf->bfSize);
125 Stream_Read_UINT16(s, bf->bfReserved1);
126 Stream_Read_UINT16(s, bf->bfReserved2);
127 Stream_Read_UINT32(s, bf->bfOffBits);
131 WLog_Print(log, WLOG_ERROR,
"Invalid bitmap::bfSize=%" PRIu32
", require at least %" PRIuz,
136 if ((bf->bfType[0] !=
'B') || (bf->bfType[1] !=
'M'))
138 WLog_Print(log, WLOG_ERROR,
"Invalid bitmap header [%c%c], expected [BM]", bf->bfType[0],
142 return Stream_CheckAndLogRequiredLengthWLog(log, s,
151 Stream_Write_UINT32(s, bi->biSize);
152 Stream_Write_INT32(s, bi->biWidth);
153 Stream_Write_INT32(s, bi->biHeight);
154 Stream_Write_UINT16(s, bi->biPlanes);
155 Stream_Write_UINT16(s, bi->biBitCount);
156 Stream_Write_UINT32(s, bi->biCompression);
157 Stream_Write_UINT32(s, bi->biSizeImage);
158 Stream_Write_INT32(s, bi->biXPelsPerMeter);
159 Stream_Write_INT32(s, bi->biYPelsPerMeter);
160 Stream_Write_UINT32(s, bi->biClrUsed);
161 Stream_Write_UINT32(s, bi->biClrImportant);
170 const size_t start = Stream_GetPosition(s);
171 Stream_Read_UINT32(s, bi->biSize);
172 Stream_Read_INT32(s, bi->biWidth);
173 Stream_Read_INT32(s, bi->biHeight);
174 Stream_Read_UINT16(s, bi->biPlanes);
175 Stream_Read_UINT16(s, bi->biBitCount);
176 Stream_Read_UINT32(s, bi->biCompression);
177 Stream_Read_UINT32(s, bi->biSizeImage);
178 Stream_Read_INT32(s, bi->biXPelsPerMeter);
179 Stream_Read_INT32(s, bi->biYPelsPerMeter);
180 Stream_Read_UINT32(s, bi->biClrUsed);
181 Stream_Read_UINT32(s, bi->biClrImportant);
183 switch (bi->biBitCount)
193 WLog_WARN(TAG,
"invalid biBitCount=%" PRIu32, bi->biBitCount);
199 WLog_WARN(TAG,
"negative biWidth=%" PRId32, bi->biWidth);
205 switch (bi->biCompression)
208 if (bi->biBitCount <= 8)
210 DWORD used = bi->biClrUsed;
212 used = 1u << bi->biBitCount;
213 offset +=
sizeof(
RGBQUAD) * used;
215 if (bi->biSizeImage == 0)
217 const UINT64 rawustride =
218 WINPR_ASSERTING_INT_CAST(UINT64, bi->biWidth) * bi->biBitCount;
219 const UINT64 ustride = ((rawustride + 31) & ~31u) >> 3;
220 if (ustride > UINT32_MAX)
222 WLog_ERR(TAG,
"bi->biWidth * bi->biBitCount > UINT32_MAX");
226 const UINT32 stride = WINPR_ASSERTING_INT_CAST(uint32_t, ustride);
227 const UINT64 usize = WINPR_ASSERTING_INT_CAST(UINT64, llabs(bi->biHeight)) * stride;
228 if (usize > UINT32_MAX)
230 WLog_ERR(TAG,
"abs(bi->biHeight) * stride > UINT32_MAX");
233 bi->biSizeImage = WINPR_ASSERTING_INT_CAST(uint32_t, usize);
239 offset +=
sizeof(DWORD) * 3;
242 WLog_ERR(TAG,
"unsupported biCompression %" PRIu32, bi->biCompression);
246 if (bi->biSizeImage == 0)
248 WLog_ERR(TAG,
"invalid biSizeImage %" PRIu32, bi->biSizeImage);
252 const size_t pos = Stream_GetPosition(s) - start;
253 if (bi->biSize < pos)
255 WLog_ERR(TAG,
"invalid biSize %" PRIu32
" < (actual) offset %" PRIuz, bi->biSize, pos);
260 return Stream_SafeSeek(s, bi->biSize - pos);
263WINPR_ATTR_MALLOC(Stream_Free, 1)
264static
wStream* winpr_bitmap_construct_header_stream(
size_t width,
size_t height,
size_t bpp,
270 size_t stride = (width * bpp + 7) / 8;
271 if ((stride % 4) != 0)
272 stride += 4 - (stride % 4);
274 size_t imgSize = stride * height;
275 if ((width > INT32_MAX) || (height > INT32_MAX) || (bpp > UINT16_MAX) || (imgSize > UINT32_MAX))
278 wStream* s = Stream_New(
nullptr, WINPR_IMAGE_BMP_HEADER_LEN);
288 bi.biSizeImage = (UINT32)imgSize;
289 bf.bfSize = bf.bfOffBits + bi.biSizeImage;
290 bi.biWidth = (INT32)width;
291 bi.biHeight = bottomUp ? (INT32)height : -1 * (INT32)height;
293 bi.biBitCount = (UINT16)bpp;
294 bi.biCompression = BI_RGB;
295 bi.biXPelsPerMeter = (INT32)width;
296 bi.biYPelsPerMeter = (INT32)height;
298 bi.biClrImportant = 0;
301 switch (bi.biCompression)
304 if (bi.biBitCount <= 8)
306 DWORD used = bi.biClrUsed;
308 used = 1u << bi.biBitCount;
309 offset +=
sizeof(
RGBQUAD) * used;
311 if (bi.biSizeImage == 0)
313 const UINT64 rawustride =
314 WINPR_ASSERTING_INT_CAST(UINT64, bi.biWidth) * bi.biBitCount;
315 const UINT64 ustride = ((rawustride + 31) & ~31u) >> 3;
316 if (ustride > UINT32_MAX)
318 WLog_ERR(TAG,
"bi->biWidth * bi->biBitCount > UINT32_MAX");
322 const UINT32 xstride = WINPR_ASSERTING_INT_CAST(uint32_t, ustride);
323 const UINT64 usize = WINPR_ASSERTING_INT_CAST(UINT64, llabs(bi.biHeight)) * xstride;
324 if (usize > UINT32_MAX)
326 WLog_ERR(TAG,
"abs(bi->biHeight) * stride > UINT32_MAX");
329 bi.biSizeImage = WINPR_ASSERTING_INT_CAST(uint32_t, usize);
333 offset +=
sizeof(DWORD) * 3;
339 if (offset > UINT32_MAX - bf.bfOffBits)
341 bf.bfOffBits += WINPR_ASSERTING_INT_CAST(UINT32, offset);
343 if (!writeBitmapFileHeader(s, &bf))
346 if (!writeBitmapInfoHeader(s, &bi))
349 if (!Stream_EnsureRemainingCapacity(s, offset))
352 Stream_Zero(s, offset);
353 const size_t pos = Stream_GetPosition(s);
354 if (pos != bf.bfOffBits)
358 Stream_Free(s, TRUE);
362BYTE* winpr_bitmap_construct_header(
size_t width,
size_t height,
size_t bpp)
364 wStream* s = winpr_bitmap_construct_header_stream(width, height, bpp, FALSE);
367 void* data = Stream_Buffer(s);
368 Stream_Free(s, FALSE);
376WINPR_ATTR_MALLOC(free, 1)
377static
void* winpr_bitmap_write_buffer(const BYTE* data,
size_t size, UINT32 width, UINT32 height,
378 UINT32 stride, UINT32 bpp, UINT32* pSize)
380 WINPR_ASSERT(data || (size == 0));
383 void* result =
nullptr;
384 if ((height == 0) || (width == 0) || (bpp == 0) || (width > SIZE_MAX / height) ||
385 (width > SIZE_MAX / bpp))
388 size_t bpp_stride = 1ull * width * bpp;
391 if ((bpp_stride % 8) != 0)
392 bpp_stride += 8 - (bpp_stride % 8);
396 if ((bpp_stride % 4) != 0)
397 bpp_stride += 4 - (bpp_stride % 4);
399 if ((bpp_stride < 4) || (bpp_stride > UINT32_MAX))
403 if (width > (SIZE_MAX - 7ull) / bpp)
405 const size_t row = (1ull * width * bpp + 7ull) / 8ull;
408 stride = (UINT32)bpp_stride;
410 if (stride > SIZE_MAX / height)
413 if (stride > size / height)
421 wStream* s = winpr_bitmap_construct_header_stream(width, height, bpp, TRUE);
427 for (
size_t y = height; y > 0; y--)
429 if (!Stream_EnsureRemainingCapacity(s, bpp_stride))
432 const size_t offset = (y - 1) * stride;
433 Stream_Write(s, &data[offset], row);
434 Stream_Zero(s, bpp_stride - row);
437 result = Stream_Buffer(s);
439 const size_t pos = Stream_GetPosition(s);
440 if (pos > UINT32_MAX)
442 *pSize = (UINT32)pos;
445 Stream_Free(s, result ==
nullptr);
449int winpr_bitmap_write(
const char* filename,
const BYTE* data,
size_t width,
size_t height,
452 return winpr_bitmap_write_ex(filename, data, 0, width, height, bpp);
455int winpr_bitmap_write_ex(
const char* filename,
const BYTE* data,
size_t stride,
size_t width,
456 size_t height,
size_t bpp)
460 void* bmpdata =
nullptr;
461 const size_t bpp_stride = ((((width * bpp) + 31) & (size_t)~31) >> 3);
463 if ((stride > UINT32_MAX) || (width > UINT32_MAX) || (height > UINT32_MAX) ||
473 const size_t size = stride * 1ull * height;
474 bmpdata = winpr_bitmap_write_buffer(data, size, (UINT32)width, (UINT32)height,
475 (UINT32)stride, (UINT32)bpp, &bmpsize);
480 fp = winpr_fopen(filename,
"w+b");
483 WLog_ERR(TAG,
"failed to open file %s", filename);
487 if (fwrite(bmpdata, bmpsize, 1, fp) != 1)
500static int write_and_free(
const char* filename,
void* data,
size_t size)
507 FILE* fp = winpr_fopen(filename,
"w+b");
512 const size_t w = fwrite(data, 1, size, fp);
514 status = (w == size) ? 1 : -1;
522int winpr_image_write(
wImage* image,
const char* filename)
525 return winpr_image_write_ex(image, WINPR_ASSERTING_INT_CAST(uint32_t, image->type), filename);
528int winpr_image_write_ex(
wImage* image, UINT32 format,
const char* filename)
533 void* data = winpr_image_write_buffer(image, format, &size);
536 return write_and_free(filename, data, size);
540static int winpr_image_bitmap_read_buffer(
wImage* image,
const BYTE* buffer,
size_t size)
546 wStream sbuffer = WINPR_C_ARRAY_INIT;
547 wStream* s = Stream_StaticConstInit(&sbuffer, buffer, size);
552 size_t bmpoffset = 0;
553 if (!readBitmapFileHeader(s, &bf) || !readBitmapInfoHeader(s, &bi, &bmpoffset))
556 if ((bf.bfType[0] !=
'B') || (bf.bfType[1] !=
'M'))
558 WLog_WARN(TAG,
"Invalid bitmap header %c%c", bf.bfType[0], bf.bfType[1]);
562 image->type = WINPR_IMAGE_BITMAP;
567 const size_t pos = Stream_GetPosition(s);
568 const size_t expect = bf.bfOffBits;
569 if ((pos > expect) || (bmpoffset > expect - pos))
571 WLog_WARN(TAG,
"pos=%" PRIuz
", expected %" PRIuz
", offset=%" PRIuz, pos, expect,
576 if (!Stream_SafeSeek(s, expect - pos))
580 if (!Stream_CheckAndLogRequiredLength(TAG, s, bi.biSizeImage))
585 WLog_WARN(TAG,
"bi.biWidth=%" PRId32, bi.biWidth);
589 image->width = (UINT32)bi.biWidth;
594 image->height = (UINT32)(-1ll * bi.biHeight);
599 image->height = (UINT32)bi.biHeight;
602 if (image->width <= 0)
604 WLog_WARN(TAG,
"image->width=%" PRIu32, image->width);
608 if (image->height <= 0)
610 WLog_WARN(TAG,
"image->height=%" PRIu32, image->height);
614 image->bitsPerPixel = bi.biBitCount;
616 const size_t bpp = (bi.biBitCount + 7UL) / 8UL;
617 image->bytesPerPixel = WINPR_ASSERTING_INT_CAST(uint32_t, bpp);
618 if (image->bytesPerPixel == 0)
621 if (image->width > (UINT32_MAX / image->bytesPerPixel) - 4ull)
624 image->scanline = image->width * image->bytesPerPixel;
625 if ((image->scanline % 4) != 0)
626 image->scanline += 4 - image->scanline % 4;
628 if (image->height > UINT32_MAX / image->scanline)
632 const size_t bmpsize = 1ULL * image->scanline * image->height;
633 if (bmpsize != bi.biSizeImage)
634 WLog_WARN(TAG,
"bmpsize=%" PRIuz
" != bi.biSizeImage=%" PRIu32, bmpsize,
638 size_t scanline = image->scanline;
639 if (bi.biSizeImage < bmpsize)
642 const size_t uscanline = image->width * bpp;
643 const size_t unaligned = image->height * uscanline;
644 if (bi.biSizeImage != unaligned)
646 scanline = uscanline;
649 image->data =
nullptr;
651 const size_t asize = 1ULL * image->scanline * image->height;
653 image->data = (BYTE*)malloc(asize);
661 BYTE* pDstData = image->data;
663 for (
size_t index = 0; index < image->height; index++)
665 Stream_Read(s, pDstData, scanline);
666 pDstData += image->scanline;
671 BYTE* pDstData = &(image->data[(image->height - 1ull) * image->scanline]);
673 for (
size_t index = 0; index < image->height; index++)
675 Stream_Read(s, pDstData, scanline);
676 pDstData -= image->scanline;
689 image->data =
nullptr;
695int winpr_image_read(
wImage* image,
const char* filename)
699 FILE* fp = winpr_fopen(filename,
"rb");
702 WLog_ERR(TAG,
"failed to open file %s", filename);
706 (void)fseek(fp, 0, SEEK_END);
707 INT64 pos = _ftelli64(fp);
708 (void)fseek(fp, 0, SEEK_SET);
712 BYTE* buffer = malloc((
size_t)pos);
715 size_t r = fread(buffer, 1, (
size_t)pos, fp);
716 if (r == (
size_t)pos)
718 status = winpr_image_read_buffer(image, buffer, (
size_t)pos);
727int winpr_image_read_buffer(
wImage* image,
const BYTE* buffer,
size_t size)
729 BYTE sig[12] = WINPR_C_ARRAY_INIT;
732 if (size <
sizeof(sig))
735 CopyMemory(sig, buffer,
sizeof(sig));
737 if ((sig[0] ==
'B') && (sig[1] ==
'M'))
739 image->type = WINPR_IMAGE_BITMAP;
740 status = winpr_image_bitmap_read_buffer(image, buffer, size);
742 else if ((sig[0] ==
'R') && (sig[1] ==
'I') && (sig[2] ==
'F') && (sig[3] ==
'F') &&
743 (sig[8] ==
'W') && (sig[9] ==
'E') && (sig[10] ==
'B') && (sig[11] ==
'P'))
745 image->type = WINPR_IMAGE_WEBP;
747 winpr_convert_from_webp(buffer, size, &image->width, &image->height,
748 &image->bitsPerPixel, &image->data, &image->scanline);
751 image->bytesPerPixel = (image->bitsPerPixel + 7) / 8;
755 else if ((sig[0] == 0xFF) && (sig[1] == 0xD8) && (sig[2] == 0xFF) && (sig[3] == 0xE0) &&
756 (sig[6] == 0x4A) && (sig[7] == 0x46) && (sig[8] == 0x49) && (sig[9] == 0x46) &&
759 image->type = WINPR_IMAGE_JPEG;
761 winpr_convert_from_jpeg(buffer, size, &image->width, &image->height,
762 &image->bitsPerPixel, &image->data, &image->scanline);
765 image->bytesPerPixel = (image->bitsPerPixel + 7) / 8;
769 else if ((sig[0] == 0x89) && (sig[1] ==
'P') && (sig[2] ==
'N') && (sig[3] ==
'G') &&
770 (sig[4] ==
'\r') && (sig[5] ==
'\n') && (sig[6] == 0x1A) && (sig[7] ==
'\n'))
772 image->type = WINPR_IMAGE_PNG;
774 winpr_convert_from_png(buffer, size, &image->width, &image->height,
775 &image->bitsPerPixel, &image->data, &image->scanline);
778 image->bytesPerPixel = (image->bitsPerPixel + 7) / 8;
786wImage* winpr_image_new(
void)
796void winpr_image_free(
wImage* image, BOOL bFreeBuffer)
807WINPR_ATTR_MALLOC(free, 1)
808static
void* winpr_convert_to_jpeg(WINPR_ATTR_UNUSED const
void* data,
809 WINPR_ATTR_UNUSED
size_t size, UINT32 width, UINT32 height,
810 UINT32 stride, UINT32 bpp, UINT32* pSize)
812 WINPR_ASSERT(data || (size == 0));
816 if ((width == 0) || (height == 0) || (stride > SIZE_MAX / height) || (bpp < 1) || (bpp > 32))
819#if !defined(WINPR_UTILS_IMAGE_JPEG)
820 WLog_WARN(TAG,
"JPEG not supported in this build");
823 BYTE* outbuffer =
nullptr;
824 unsigned long outsize = 0;
825 struct jpeg_compress_struct cinfo = WINPR_C_ARRAY_INIT;
827 const size_t expect1 = 1ull * stride * height;
828 const size_t bytes = (bpp + 7) / 8;
829 const size_t expect2 = 1ull * width * height * bytes;
830 if (expect1 < expect2)
836 WINPR_JPEG_ERROR_MGR jerr = WINPR_C_ARRAY_INIT;
837 cinfo.err = jpeg_std_error(&jerr.pub);
838 jerr.pub.error_exit = winpr_jpeg_error_exit;
839 jpeg_create_compress(&cinfo);
842 if (setjmp(jerr.jump) != 0)
845 jpeg_mem_dest(&cinfo, &outbuffer, &outsize);
847 cinfo.image_width = width;
848 cinfo.image_height = height;
849 WINPR_ASSERT(bpp <= INT32_MAX / 8);
850 cinfo.input_components = (int)(bpp + 7) / 8;
851 cinfo.in_color_space = (bpp > 24) ? JCS_EXT_BGRA : JCS_EXT_BGR;
852 cinfo.data_precision = 8;
854 jpeg_set_defaults(&cinfo);
855 jpeg_set_quality(&cinfo, 100, TRUE);
857 cinfo.comp_info[0].h_samp_factor = cinfo.comp_info[0].v_samp_factor = 1;
859 jpeg_start_compress(&cinfo, TRUE);
861 const JSAMPLE* cdata = data;
862 for (
size_t x = 0; x < height; x++)
864 WINPR_ASSERT(x * stride <= UINT32_MAX);
865 const JDIMENSION offset = (JDIMENSION)x * stride;
869 JSAMPLE* coffset = WINPR_CAST_CONST_PTR_AWAY(&cdata[offset], JSAMPLE*);
870 if (jpeg_write_scanlines(&cinfo, &coffset, 1) != 1)
874 WINPR_ASSERT(outsize <= UINT32_MAX);
875 *pSize = (UINT32)outsize;
876 jpeg_finish_compress(&cinfo);
877 jpeg_destroy_compress(&cinfo);
884 jpeg_destroy_compress(&cinfo);
892SSIZE_T winpr_convert_from_jpeg(WINPR_ATTR_UNUSED
const BYTE* comp_data,
893 WINPR_ATTR_UNUSED
size_t comp_data_bytes,
894 WINPR_ATTR_UNUSED UINT32* width, WINPR_ATTR_UNUSED UINT32* height,
895 WINPR_ATTR_UNUSED UINT32* bpp,
896 WINPR_ATTR_UNUSED BYTE** ppdecomp_data, UINT32* scanline)
899 WINPR_ASSERT(comp_data || (comp_data_bytes == 0));
901 WINPR_ASSERT(height);
903 WINPR_ASSERT(ppdecomp_data);
904 WINPR_ASSERT(scanline);
909 *ppdecomp_data =
nullptr;
912#if !defined(WINPR_UTILS_IMAGE_JPEG)
913 WLog_WARN(TAG,
"JPEG not supported in this build");
916 struct jpeg_decompress_struct cinfo = WINPR_C_ARRAY_INIT;
917 WINPR_JPEG_ERROR_MGR jerr = WINPR_C_ARRAY_INIT;
919 BYTE* decomp_data =
nullptr;
921 cinfo.err = jpeg_std_error(&jerr.pub);
922 jerr.pub.error_exit = winpr_jpeg_error_exit;
923 jpeg_create_decompress(&cinfo);
926 if (setjmp(jerr.jump) != 0)
929 jpeg_mem_src(&cinfo, comp_data, comp_data_bytes);
931 if (jpeg_read_header(&cinfo, 1) != JPEG_HEADER_OK)
934 cinfo.out_color_space = cinfo.num_components > 3 ? JCS_EXT_RGBA : JCS_EXT_BGR;
935 const int components = MAX(3, cinfo.num_components);
937 *width = WINPR_ASSERTING_INT_CAST(uint32_t, cinfo.image_width);
938 *height = WINPR_ASSERTING_INT_CAST(uint32_t, cinfo.image_height);
939 *bpp = WINPR_ASSERTING_INT_CAST(uint32_t, cinfo.num_components * 8);
941 if (!jpeg_start_decompress(&cinfo))
944 size_t stride = 1ULL * cinfo.image_width * WINPR_ASSERTING_INT_CAST(
size_t, components);
946 if ((stride == 0) || (cinfo.image_height == 0) || (stride > UINT32_MAX))
948 *scanline = (UINT32)stride;
950 decomp_data = calloc(stride, cinfo.image_height);
953 while (cinfo.output_scanline < cinfo.image_height)
955 JSAMPROW row = &decomp_data[cinfo.output_scanline * stride];
956 if (jpeg_read_scanlines(&cinfo, &row, 1) != 1)
959 const size_t ssize = stride * cinfo.image_height;
960 WINPR_ASSERT(ssize < SSIZE_MAX);
961 size = (SSIZE_T)ssize;
963 jpeg_finish_decompress(&cinfo);
966 jpeg_destroy_decompress(&cinfo);
967 *ppdecomp_data = decomp_data;
972WINPR_ATTR_MALLOC(free, 1)
973static
void* winpr_convert_to_webp(WINPR_ATTR_UNUSED const
void* data,
974 WINPR_ATTR_UNUSED
size_t size, WINPR_ATTR_UNUSED UINT32 width,
975 WINPR_ATTR_UNUSED UINT32 height, WINPR_ATTR_UNUSED UINT32 stride,
976 WINPR_ATTR_UNUSED UINT32 bpp, UINT32* pSize)
978 WINPR_ASSERT(data || (size == 0));
983#if !defined(WINPR_UTILS_IMAGE_WEBP)
984 WLog_WARN(TAG,
"WEBP not supported in this build");
988 uint8_t* pDstData =
nullptr;
989 WINPR_ASSERT(width <= INT32_MAX);
990 WINPR_ASSERT(height <= INT32_MAX);
991 WINPR_ASSERT(stride <= INT32_MAX);
995 dstSize = WebPEncodeLosslessBGRA(data, (
int)width, (
int)height, (
int)stride, &pDstData);
998 dstSize = WebPEncodeLosslessBGR(data, (
int)width, (
int)height, (
int)stride, &pDstData);
1006 rc = malloc(dstSize);
1009 memcpy(rc, pDstData, dstSize);
1011 WINPR_ASSERT(dstSize <= UINT32_MAX);
1012 *pSize = (UINT32)dstSize;
1020SSIZE_T winpr_convert_from_webp(WINPR_ATTR_UNUSED
const BYTE* comp_data,
1021 WINPR_ATTR_UNUSED
size_t comp_data_bytes, UINT32* width,
1022 UINT32* height, UINT32* bpp, BYTE** ppdecomp_data, UINT32* scanline)
1024 WINPR_ASSERT(comp_data || (comp_data_bytes == 0));
1025 WINPR_ASSERT(width);
1026 WINPR_ASSERT(height);
1028 WINPR_ASSERT(ppdecomp_data);
1029 WINPR_ASSERT(scanline);
1034 *ppdecomp_data =
nullptr;
1037#if !defined(WINPR_UTILS_IMAGE_WEBP)
1038 WLog_WARN(TAG,
"WEBP not supported in this build");
1044 uint8_t* dst = WebPDecodeBGRA(comp_data, comp_data_bytes, &w, &h);
1045 if (!dst || (w < 0) || (h < 0))
1051 if ((w <= 0) || ((UINT32)w > UINT32_MAX / 4ull) || (h <= 0) ||
1052 ((UINT32)h > UINT32_MAX / 4ul / (UINT32)w))
1057 *width = WINPR_ASSERTING_INT_CAST(uint32_t, w);
1058 *height = WINPR_ASSERTING_INT_CAST(uint32_t, h);
1060 *scanline = (*width) * 4ul;
1061 *ppdecomp_data = dst;
1062 return 1ll * (*scanline) * h;
1066#if defined(WINPR_UTILS_IMAGE_PNG)
1067struct png_mem_encode
1073static void png_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
1076 struct png_mem_encode* p =
1077 (
struct png_mem_encode*)png_get_io_ptr(png_ptr);
1078 size_t nsize = p->size + length;
1083 char* tmp = realloc(p->buffer, nsize);
1088 p->buffer = malloc(nsize);
1091 png_error(png_ptr,
"Write Error");
1094 memcpy(p->buffer + p->size, data, length);
1099static void png_flush(WINPR_ATTR_UNUSED png_structp png_ptr)
1104static SSIZE_T save_png_to_buffer(UINT32 bpp, UINT32 width, uint32_t stride, UINT32 height,
1105 const uint8_t* data,
size_t size,
void** pDstData)
1108 png_structp png_ptr =
nullptr;
1109 png_infop info_ptr =
nullptr;
1110 png_byte** row_pointers =
nullptr;
1111 struct png_mem_encode state = WINPR_C_ARRAY_INIT;
1113 *pDstData =
nullptr;
1115 if (!data || (size == 0))
1118 if ((width == 0) || (height == 0) || (bpp == 0))
1121 WINPR_ASSERT(pDstData);
1123 if (size / height < stride)
1127 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
nullptr,
nullptr,
nullptr);
1128 if (png_ptr ==
nullptr)
1132 info_ptr = png_create_info_struct(png_ptr);
1133 if (info_ptr ==
nullptr)
1137 if (setjmp(png_jmpbuf(png_ptr)))
1141 int colorType = PNG_COLOR_TYPE_PALETTE;
1143 colorType = PNG_COLOR_TYPE_RGB;
1145 colorType = PNG_COLOR_TYPE_RGB;
1147 colorType = PNG_COLOR_TYPE_RGBA;
1149 const UINT32 pngChannels = (bpp > 24) ? 4 : ((bpp > 8) ? 3 : 1);
1150 const UINT32 srcBytesPerPixel = (bpp + 7) / 8;
1154 if ((srcBytesPerPixel != pngChannels) || (width > stride / srcBytesPerPixel))
1157 "unsupported conversion to PNG: bpp=%" PRIu32
", stride=%" PRIu32
1158 ", min expected stride=%llu",
1159 bpp, stride, 1ull * width * srcBytesPerPixel);
1163 png_set_IHDR(png_ptr, info_ptr, width, height, 8, colorType, PNG_INTERLACE_NONE,
1164 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
1167#if SIZE_MAX <= UINT32_MAX
1168 if (height > SIZE_MAX /
sizeof(png_byte*))
1172 row_pointers = (png_byte**)png_malloc(png_ptr, height *
sizeof(png_byte*));
1173 for (
size_t y = 0; y < height; ++y)
1175 const uint8_t* line = &data[y * stride];
1177 if (width > SIZE_MAX /
sizeof(uint8_t) / pngChannels)
1180 uint8_t* row = png_malloc(png_ptr,
sizeof(uint8_t) * width * pngChannels);
1181 row_pointers[y] = (png_byte*)row;
1182 for (
size_t x = 0; x < width; ++x)
1196 png_set_write_fn(png_ptr, &state, png_write_data, png_flush);
1197 png_set_rows(png_ptr, info_ptr, row_pointers);
1198 png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_BGR,
nullptr);
1201 for (
size_t y = 0; y < height; y++)
1202 png_free(png_ptr, row_pointers[y]);
1203 png_free(png_ptr, (
void*)row_pointers);
1206 if (state.size > SSIZE_MAX)
1208 rc = (SSIZE_T)state.size;
1209 *pDstData = state.buffer;
1211 png_destroy_write_struct(&png_ptr, &info_ptr);
1220 png_uint_32 bufsize;
1221 png_uint_32 current_pos;
1222} MEMORY_READER_STATE;
1224static void read_data_memory(png_structp png_ptr, png_bytep data,
size_t length)
1226 MEMORY_READER_STATE* f = png_get_io_ptr(png_ptr);
1227 if (length > (f->bufsize - f->current_pos))
1228 png_error(png_ptr,
"read error in read_data_memory (loadpng)");
1231 memcpy(data, f->buffer + f->current_pos, length);
1232 f->current_pos += length;
1236WINPR_ATTR_MALLOC(free, 1)
1237static
void* winpr_read_png_from_buffer(const
void* data,
size_t SrcSize,
size_t* pSize,
1238 UINT32* pWidth, UINT32* pHeight, UINT32* pBpp,
1242 png_uint_32 width = 0;
1243 png_uint_32 height = 0;
1246 int interlace_type = 0;
1247 int transforms = PNG_TRANSFORM_IDENTITY;
1248 MEMORY_READER_STATE memory_reader_state = WINPR_C_ARRAY_INIT;
1249 png_bytepp row_pointers =
nullptr;
1250 png_infop info_ptr =
nullptr;
1258 if (SrcSize > UINT32_MAX)
1261 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
nullptr,
nullptr,
nullptr);
1264 info_ptr = png_create_info_struct(png_ptr);
1269 if (setjmp(png_jmpbuf(png_ptr)))
1272 memory_reader_state.buffer = WINPR_CAST_CONST_PTR_AWAY(data, png_bytep);
1273 memory_reader_state.bufsize = (UINT32)SrcSize;
1274 memory_reader_state.current_pos = 0;
1276 png_set_read_fn(png_ptr, &memory_reader_state, read_data_memory);
1278 transforms |= PNG_TRANSFORM_BGR;
1279 png_read_png(png_ptr, info_ptr, transforms,
nullptr);
1281 if (png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type,
1282 nullptr,
nullptr) != 1)
1285 WINPR_ASSERT(bit_depth >= 0);
1286 const png_byte channelcount = png_get_channels(png_ptr, info_ptr);
1287 const size_t bpp = channelcount * (size_t)bit_depth;
1289 row_pointers = png_get_rows(png_ptr, info_ptr);
1292 if ((width == 0) || (width > SIZE_MAX / bpp))
1295 const size_t png_stride = png_get_rowbytes(png_ptr, info_ptr);
1296 if ((png_stride == 0) || (height == 0) || (png_stride > SIZE_MAX / height))
1298 const size_t size = 1ULL * png_stride * height;
1300 rc = calloc(png_stride, height);
1304 for (png_uint_32 i = 0; i < height; i++)
1306 memcpy(cur, row_pointers[i], png_stride);
1312 WINPR_ASSERT(bpp <= UINT32_MAX);
1313 *pBpp = (UINT32)bpp;
1314 *pScanline = (UINT32)png_stride;
1319 png_destroy_read_struct(&png_ptr, &info_ptr,
nullptr);
1324WINPR_ATTR_MALLOC(free, 1)
1325static
void* winpr_convert_to_png(WINPR_ATTR_UNUSED const
void* data, WINPR_ATTR_UNUSED
size_t size,
1326 UINT32 width, UINT32 height, UINT32 stride, UINT32 bpp,
1329 WINPR_ASSERT(data || (size == 0));
1330 WINPR_ASSERT(pSize);
1334 if ((width == 0) || (height == 0) || (stride == 0) || (bpp == 0))
1336 if (height > UINT32_MAX / stride)
1339#if defined(WINPR_UTILS_IMAGE_PNG)
1340 void* dst =
nullptr;
1341 SSIZE_T rc = save_png_to_buffer(bpp, width, stride, height, data, size, &dst);
1344 *pSize = (UINT32)rc;
1346#elif defined(WITH_LODEPNG)
1348 BYTE* dst =
nullptr;
1355 rc = lodepng_encode32(&dst, &dstsize, data, width, height);
1358 rc = lodepng_encode24(&dst, &dstsize, data, width, height);
1365 *pSize = (UINT32)dstsize;
1369 WLog_WARN(TAG,
"PNG not supported in this build");
1374SSIZE_T winpr_convert_from_png(WINPR_ATTR_UNUSED
const BYTE* comp_data,
1375 WINPR_ATTR_UNUSED
size_t comp_data_bytes, UINT32* width,
1376 UINT32* height, UINT32* bpp, BYTE** ppdecomp_data, UINT32* scanline)
1378 WINPR_ASSERT(comp_data || (comp_data_bytes == 0));
1379 WINPR_ASSERT(width);
1380 WINPR_ASSERT(height);
1382 WINPR_ASSERT(ppdecomp_data);
1383 WINPR_ASSERT(scanline);
1388 *ppdecomp_data =
nullptr;
1391#if defined(WINPR_UTILS_IMAGE_PNG)
1394 winpr_read_png_from_buffer(comp_data, comp_data_bytes, &len, width, height, bpp, scanline);
1395 if (!*ppdecomp_data)
1397 return (SSIZE_T)len;
1398#elif defined(WITH_LODEPNG)
1400 if (*width > UINT32_MAX / 4)
1404 lodepng_decode32((
unsigned char**)ppdecomp_data, width, height, comp_data, comp_data_bytes);
1405 *scanline = *width * 4;
1408 WLog_WARN(TAG,
"PNG not supported in this build");
1413BOOL winpr_image_format_is_supported(UINT32 format)
1417 case WINPR_IMAGE_BITMAP:
1418#if defined(WINPR_UTILS_IMAGE_PNG) || defined(WITH_LODEPNG)
1419 case WINPR_IMAGE_PNG:
1421#if defined(WINPR_UTILS_IMAGE_JPEG)
1422 case WINPR_IMAGE_JPEG:
1424#if defined(WINPR_UTILS_IMAGE_WEBP)
1425 case WINPR_IMAGE_WEBP:
1433WINPR_ATTR_MALLOC(free, 1)
1434static BYTE* convert(const
wImage* image,
size_t* pstride, WINPR_ATTR_UNUSED UINT32 flags)
1436 WINPR_ASSERT(image);
1437 WINPR_ASSERT(pstride);
1440 if (image->bitsPerPixel < 24)
1443 const size_t stride = image->width * 4ull;
1444 BYTE* data = calloc(stride, image->height);
1447 for (
size_t y = 0; y < image->height; y++)
1449 const BYTE* srcLine = &image->data[image->scanline * y];
1450 BYTE* dstLine = &data[stride * y];
1451 if (image->bitsPerPixel == 32)
1452 memcpy(dstLine, srcLine, stride);
1455 for (
size_t x = 0; x < image->width; x++)
1457 const BYTE* src = &srcLine[image->bytesPerPixel * x];
1458 BYTE* dst = &dstLine[4ull * x];
1476static BOOL compare_byte_relaxed(BYTE a, BYTE b, UINT32 flags)
1480 if ((flags & WINPR_IMAGE_CMP_FUZZY) != 0)
1482 const int diff = abs((
int)a) - abs((
int)b);
1496static BOOL compare_pixel(
const BYTE* pa,
const BYTE* pb, UINT32 flags)
1501 if (!compare_byte_relaxed(*pa++, *pb++, flags))
1503 if (!compare_byte_relaxed(*pa++, *pb++, flags))
1505 if (!compare_byte_relaxed(*pa++, *pb++, flags))
1507 if ((flags & WINPR_IMAGE_CMP_IGNORE_ALPHA) == 0)
1509 if (!compare_byte_relaxed(*pa++, *pb++, flags))
1515BOOL winpr_image_equal(
const wImage* imageA,
const wImage* imageB, UINT32 flags)
1517 if (imageA == imageB)
1519 if (!imageA || !imageB)
1522 if (imageA->height != imageB->height)
1524 if (imageA->width != imageB->width)
1527 if ((flags & WINPR_IMAGE_CMP_IGNORE_DEPTH) == 0)
1529 if (imageA->bitsPerPixel != imageB->bitsPerPixel)
1531 if (imageA->bytesPerPixel != imageB->bytesPerPixel)
1538 BYTE* dataA = convert(imageA, &astride, flags);
1539 BYTE* dataB = convert(imageA, &bstride, flags);
1540 if (dataA && dataB && (astride == bstride))
1543 for (
size_t y = 0; y < imageA->height; y++)
1545 const BYTE* lineA = &dataA[astride * y];
1546 const BYTE* lineB = &dataB[bstride * y];
1548 for (
size_t x = 0; x < imageA->width; x++)
1550 const BYTE* pa = &lineA[x * 4ull];
1551 const BYTE* pb = &lineB[x * 4ull];
1553 if (!compare_pixel(pa, pb, flags))
1563const char* winpr_image_format_mime(UINT32 format)
1567 case WINPR_IMAGE_BITMAP:
1569 case WINPR_IMAGE_PNG:
1571 case WINPR_IMAGE_WEBP:
1572 return "image/webp";
1573 case WINPR_IMAGE_JPEG:
1574 return "image/jpeg";
1580const char* winpr_image_format_extension(UINT32 format)
1584 case WINPR_IMAGE_BITMAP:
1586 case WINPR_IMAGE_PNG:
1588 case WINPR_IMAGE_WEBP:
1590 case WINPR_IMAGE_JPEG:
1597void* winpr_image_write_buffer(
wImage* image, UINT32 format,
size_t* psize)
1599 WINPR_ASSERT(image);
1602 case WINPR_IMAGE_BITMAP:
1605 size_t size = 1ull * image->height * image->scanline;
1606 void* data = winpr_bitmap_write_buffer(image->data, size, image->width, image->height,
1607 image->scanline, image->bitsPerPixel, &outsize);
1611 case WINPR_IMAGE_WEBP:
1614 size_t size = 1ull * image->height * image->scanline;
1615 void* data = winpr_convert_to_webp(image->data, size, image->width, image->height,
1616 image->scanline, image->bitsPerPixel, &outsize);
1620 case WINPR_IMAGE_JPEG:
1623 size_t size = 1ull * image->height * image->scanline;
1624 void* data = winpr_convert_to_jpeg(image->data, size, image->width, image->height,
1625 image->scanline, image->bitsPerPixel, &outsize);
1629 case WINPR_IMAGE_PNG:
1632 size_t size = 1ull * image->height * image->scanline;
1633 void* data = winpr_convert_to_png(image->data, size, image->width, image->height,
1634 image->scanline, image->bitsPerPixel, &outsize);