24#include <winpr/config.h>
26#include <winpr/wtypes.h>
28#include <winpr/file.h>
29#include <winpr/cast.h>
31#include <winpr/image.h>
33#if defined(WINPR_UTILS_IMAGE_PNG)
37#if defined(WINPR_UTILS_IMAGE_JPEG)
38#define INT32 INT32_WINPR
43#if defined(WINPR_UTILS_IMAGE_WEBP)
44#include <webp/encode.h>
45#include <webp/decode.h>
48#if defined(WITH_LODEPNG)
51#include <winpr/stream.h>
55#define TAG WINPR_TAG("utils.image")
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);
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);
80 static wLog* log =
nullptr;
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);
97 WLog_Print(log, WLOG_ERROR,
"Invalid bitmap::bfSize=%" PRIu32
", require at least %" PRIuz,
102 if ((bf->bfType[0] !=
'B') || (bf->bfType[1] !=
'M'))
104 WLog_Print(log, WLOG_ERROR,
"Invalid bitmap header [%c%c], expected [BM]", bf->bfType[0],
108 return Stream_CheckAndLogRequiredLengthWLog(log, s,
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);
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);
149 if ((bi->biBitCount < 1) || (bi->biBitCount > 32))
151 WLog_WARN(TAG,
"invalid biBitCount=%" PRIu32, bi->biBitCount);
157 WLog_WARN(TAG,
"negative biWidth=%" PRId32, bi->biWidth);
163 switch (bi->biCompression)
166 if (bi->biBitCount <= 8)
168 DWORD used = bi->biClrUsed;
170 used = 1u << bi->biBitCount;
171 offset +=
sizeof(
RGBQUAD) * used;
173 if (bi->biSizeImage == 0)
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)
180 WLog_ERR(TAG,
"bi->biWidth * bi->biBitCount > UINT32_MAX");
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)
188 WLog_ERR(TAG,
"abs(bi->biHeight) * stride > UINT32_MAX");
191 bi->biSizeImage = WINPR_ASSERTING_INT_CAST(uint32_t, usize);
197 offset +=
sizeof(DWORD) * 3;
200 WLog_ERR(TAG,
"unsupported biCompression %" PRIu32, bi->biCompression);
204 if (bi->biSizeImage == 0)
206 WLog_ERR(TAG,
"invalid biSizeImage %" PRIu32, bi->biSizeImage);
210 const size_t pos = Stream_GetPosition(s) - start;
211 if (bi->biSize < pos)
213 WLog_ERR(TAG,
"invalid biSize %" PRIu32
" < (actual) offset %" PRIuz, bi->biSize, pos);
218 return Stream_SafeSeek(s, bi->biSize - pos);
221BYTE* winpr_bitmap_construct_header(
size_t width,
size_t height,
size_t bpp)
223 BYTE* result =
nullptr;
227 size_t stride = (width * bpp + 7) / 8;
228 if ((stride % 4) != 0)
229 stride += 4 - (stride % 4);
231 size_t imgSize = stride * height;
232 if ((width > INT32_MAX) || (height > INT32_MAX) || (bpp > UINT16_MAX) || (imgSize > UINT32_MAX))
235 wStream* s = Stream_New(
nullptr, WINPR_IMAGE_BMP_HEADER_LEN);
245 bi.biSizeImage = (UINT32)imgSize;
246 bf.bfSize = bf.bfOffBits + bi.biSizeImage;
247 bi.biWidth = (INT32)width;
248 bi.biHeight = -1 * (INT32)height;
250 bi.biBitCount = (UINT16)bpp;
251 bi.biCompression = BI_RGB;
252 bi.biXPelsPerMeter = (INT32)width;
253 bi.biYPelsPerMeter = (INT32)height;
255 bi.biClrImportant = 0;
258 switch (bi.biCompression)
261 if (bi.biBitCount <= 8)
263 DWORD used = bi.biClrUsed;
265 used = (1u << bi.biBitCount) / 8;
266 offset +=
sizeof(
RGBQUAD) * used;
270 offset +=
sizeof(DWORD) * 3;
276 if (!writeBitmapFileHeader(s, &bf))
279 if (!writeBitmapInfoHeader(s, &bi))
282 if (!Stream_EnsureRemainingCapacity(s, offset))
285 Stream_Zero(s, offset);
286 result = Stream_Buffer(s);
288 Stream_Free(s, result ==
nullptr);
296WINPR_ATTR_MALLOC(free, 1)
298static
void* winpr_bitmap_write_buffer(const BYTE* data, WINPR_ATTR_UNUSED
size_t size,
299 UINT32 width, UINT32 height, UINT32 stride, UINT32 bpp,
302 WINPR_ASSERT(data || (size == 0));
304 void* result =
nullptr;
305 size_t bpp_stride = 1ull * width * (bpp / 8);
306 if ((bpp_stride % 4) != 0)
307 bpp_stride += 4 - (bpp_stride % 4);
309 if (bpp_stride > UINT32_MAX)
312 wStream* s = Stream_New(
nullptr, 1024);
315 stride = (UINT32)bpp_stride;
317 BYTE* bmp_header = winpr_bitmap_construct_header(width, height, bpp);
320 if (!Stream_EnsureRemainingCapacity(s, WINPR_IMAGE_BMP_HEADER_LEN))
322 Stream_Write(s, bmp_header, WINPR_IMAGE_BMP_HEADER_LEN);
324 if (!Stream_EnsureRemainingCapacity(s, 1ULL * bpp_stride * height))
327 for (
size_t y = 0; y < height; y++)
329 const BYTE* line = &data[stride * y];
331 Stream_Write(s, line, stride);
332 Stream_Zero(s, bpp_stride - stride);
335 result = Stream_Buffer(s);
337 const size_t pos = Stream_GetPosition(s);
338 if (pos > UINT32_MAX)
340 *pSize = (UINT32)pos;
343 Stream_Free(s, result ==
nullptr);
348int winpr_bitmap_write(
const char* filename,
const BYTE* data,
size_t width,
size_t height,
351 return winpr_bitmap_write_ex(filename, data, 0, width, height, bpp);
354int winpr_bitmap_write_ex(
const char* filename,
const BYTE* data,
size_t stride,
size_t width,
355 size_t height,
size_t bpp)
359 void* bmpdata =
nullptr;
360 const size_t bpp_stride = ((((width * bpp) + 31) & (size_t)~31) >> 3);
362 if ((stride > UINT32_MAX) || (width > UINT32_MAX) || (height > UINT32_MAX) ||
372 const size_t size = stride * 1ull * height;
373 bmpdata = winpr_bitmap_write_buffer(data, size, (UINT32)width, (UINT32)height,
374 (UINT32)stride, (UINT32)bpp, &bmpsize);
379 fp = winpr_fopen(filename,
"w+b");
382 WLog_ERR(TAG,
"failed to open file %s", filename);
386 if (fwrite(bmpdata, bmpsize, 1, fp) != 1)
398static int write_and_free(
const char* filename,
void* data,
size_t size)
405 FILE* fp = winpr_fopen(filename,
"w+b");
410 const size_t w = fwrite(data, 1, size, fp);
412 status = (w == size) ? 1 : -1;
420int winpr_image_write(
wImage* image,
const char* filename)
423 return winpr_image_write_ex(image, WINPR_ASSERTING_INT_CAST(uint32_t, image->type), filename);
426int winpr_image_write_ex(
wImage* image, UINT32 format,
const char* filename)
431 void* data = winpr_image_write_buffer(image, format, &size);
434 return write_and_free(filename, data, size);
437static int winpr_image_bitmap_read_buffer(
wImage* image,
const BYTE* buffer,
size_t size)
443 wStream sbuffer = WINPR_C_ARRAY_INIT;
444 wStream* s = Stream_StaticConstInit(&sbuffer, buffer, size);
449 size_t bmpoffset = 0;
450 if (!readBitmapFileHeader(s, &bf) || !readBitmapInfoHeader(s, &bi, &bmpoffset))
453 if ((bf.bfType[0] !=
'B') || (bf.bfType[1] !=
'M'))
455 WLog_WARN(TAG,
"Invalid bitmap header %c%c", bf.bfType[0], bf.bfType[1]);
459 image->type = WINPR_IMAGE_BITMAP;
464 const size_t pos = Stream_GetPosition(s);
465 const size_t expect = bf.bfOffBits;
466 if ((pos > expect) || (bmpoffset > expect - pos))
468 WLog_WARN(TAG,
"pos=%" PRIuz
", expected %" PRIuz
", offset=%" PRIuz, pos, expect,
473 if (!Stream_SafeSeek(s, expect - pos))
477 if (!Stream_CheckAndLogRequiredLength(TAG, s, bi.biSizeImage))
482 WLog_WARN(TAG,
"bi.biWidth=%" PRId32, bi.biWidth);
486 image->width = (UINT32)bi.biWidth;
491 image->height = (UINT32)(-1 * bi.biHeight);
496 image->height = (UINT32)bi.biHeight;
499 if (image->height <= 0)
501 WLog_WARN(TAG,
"image->height=%" PRIu32, image->height);
505 image->bitsPerPixel = bi.biBitCount;
507 const size_t bpp = (bi.biBitCount + 7UL) / 8UL;
508 image->bytesPerPixel = WINPR_ASSERTING_INT_CAST(uint32_t, bpp);
510 image->scanline = WINPR_ASSERTING_INT_CAST(uint32_t, bi.biWidth) * image->bytesPerPixel;
511 if ((image->scanline % 4) != 0)
512 image->scanline += 4 - image->scanline % 4;
515 const size_t bmpsize = 1ULL * image->scanline * image->height;
516 if (bmpsize != bi.biSizeImage)
517 WLog_WARN(TAG,
"bmpsize=%" PRIuz
" != bi.biSizeImage=%" PRIu32, bmpsize,
521 size_t scanline = image->scanline;
522 if (bi.biSizeImage < bmpsize)
525 const size_t uscanline = image->width * bpp;
526 const size_t unaligned = image->height * uscanline;
527 if (bi.biSizeImage != unaligned)
529 scanline = uscanline;
532 image->data =
nullptr;
534 const size_t asize = 1ULL * image->scanline * image->height;
536 image->data = (BYTE*)malloc(asize);
544 BYTE* pDstData = image->data;
546 for (
size_t index = 0; index < image->height; index++)
548 Stream_Read(s, pDstData, scanline);
549 pDstData += image->scanline;
554 BYTE* pDstData = &(image->data[(image->height - 1ull) * image->scanline]);
556 for (
size_t index = 0; index < image->height; index++)
558 Stream_Read(s, pDstData, scanline);
559 pDstData -= image->scanline;
572 image->data =
nullptr;
578int winpr_image_read(
wImage* image,
const char* filename)
582 FILE* fp = winpr_fopen(filename,
"rb");
585 WLog_ERR(TAG,
"failed to open file %s", filename);
589 (void)fseek(fp, 0, SEEK_END);
590 INT64 pos = _ftelli64(fp);
591 (void)fseek(fp, 0, SEEK_SET);
595 BYTE* buffer = malloc((
size_t)pos);
598 size_t r = fread(buffer, 1, (
size_t)pos, fp);
599 if (r == (
size_t)pos)
601 status = winpr_image_read_buffer(image, buffer, (
size_t)pos);
610int winpr_image_read_buffer(
wImage* image,
const BYTE* buffer,
size_t size)
612 BYTE sig[12] = WINPR_C_ARRAY_INIT;
615 if (size <
sizeof(sig))
618 CopyMemory(sig, buffer,
sizeof(sig));
620 if ((sig[0] ==
'B') && (sig[1] ==
'M'))
622 image->type = WINPR_IMAGE_BITMAP;
623 status = winpr_image_bitmap_read_buffer(image, buffer, size);
625 else if ((sig[0] ==
'R') && (sig[1] ==
'I') && (sig[2] ==
'F') && (sig[3] ==
'F') &&
626 (sig[8] ==
'W') && (sig[9] ==
'E') && (sig[10] ==
'B') && (sig[11] ==
'P'))
628 image->type = WINPR_IMAGE_WEBP;
629 const SSIZE_T rc = winpr_convert_from_webp(buffer, size, &image->width, &image->height,
630 &image->bitsPerPixel, &image->data);
633 image->bytesPerPixel = (image->bitsPerPixel + 7) / 8;
634 image->scanline = image->width * image->bytesPerPixel;
638 else if ((sig[0] == 0xFF) && (sig[1] == 0xD8) && (sig[2] == 0xFF) && (sig[3] == 0xE0) &&
639 (sig[6] == 0x4A) && (sig[7] == 0x46) && (sig[8] == 0x49) && (sig[9] == 0x46) &&
642 image->type = WINPR_IMAGE_JPEG;
643 const SSIZE_T rc = winpr_convert_from_jpeg(buffer, size, &image->width, &image->height,
644 &image->bitsPerPixel, &image->data);
647 image->bytesPerPixel = (image->bitsPerPixel + 7) / 8;
648 image->scanline = image->width * image->bytesPerPixel;
652 else if ((sig[0] == 0x89) && (sig[1] ==
'P') && (sig[2] ==
'N') && (sig[3] ==
'G') &&
653 (sig[4] ==
'\r') && (sig[5] ==
'\n') && (sig[6] == 0x1A) && (sig[7] ==
'\n'))
655 image->type = WINPR_IMAGE_PNG;
656 const SSIZE_T rc = winpr_convert_from_png(buffer, size, &image->width, &image->height,
657 &image->bitsPerPixel, &image->data);
660 image->bytesPerPixel = (image->bitsPerPixel + 7) / 8;
661 image->scanline = image->width * image->bytesPerPixel;
669wImage* winpr_image_new(
void)
679void winpr_image_free(
wImage* image, BOOL bFreeBuffer)
690static void* winpr_convert_to_jpeg(WINPR_ATTR_UNUSED
const void* data,
691 WINPR_ATTR_UNUSED
size_t size, WINPR_ATTR_UNUSED UINT32 width,
692 WINPR_ATTR_UNUSED UINT32 height, WINPR_ATTR_UNUSED UINT32 stride,
693 WINPR_ATTR_UNUSED UINT32 bpp, WINPR_ATTR_UNUSED UINT32* pSize)
695 WINPR_ASSERT(data || (size == 0));
700#if !defined(WINPR_UTILS_IMAGE_JPEG)
701 WLog_WARN(TAG,
"JPEG not supported in this build");
704 BYTE* outbuffer =
nullptr;
705 unsigned long outsize = 0;
706 struct jpeg_compress_struct cinfo = WINPR_C_ARRAY_INIT;
708 const size_t expect1 = 1ull * stride * height;
709 const size_t bytes = (bpp + 7) / 8;
710 const size_t expect2 = 1ull * width * height * bytes;
711 if (expect1 < expect2)
717 struct jpeg_error_mgr jerr = WINPR_C_ARRAY_INIT;
718 cinfo.err = jpeg_std_error(&jerr);
720 jpeg_create_compress(&cinfo);
721 jpeg_mem_dest(&cinfo, &outbuffer, &outsize);
723 cinfo.image_width = width;
724 cinfo.image_height = height;
725 WINPR_ASSERT(bpp <= INT32_MAX / 8);
726 cinfo.input_components = (int)(bpp + 7) / 8;
727 cinfo.in_color_space = (bpp > 24) ? JCS_EXT_BGRA : JCS_EXT_BGR;
728 cinfo.data_precision = 8;
730 jpeg_set_defaults(&cinfo);
731 jpeg_set_quality(&cinfo, 100, TRUE);
733 cinfo.comp_info[0].h_samp_factor = cinfo.comp_info[0].v_samp_factor = 1;
735 jpeg_start_compress(&cinfo, TRUE);
737 const JSAMPLE* cdata = data;
738 for (
size_t x = 0; x < height; x++)
740 WINPR_ASSERT(x * stride <= UINT32_MAX);
741 const JDIMENSION offset = (JDIMENSION)x * stride;
745 JSAMPLE* coffset = WINPR_CAST_CONST_PTR_AWAY(&cdata[offset], JSAMPLE*);
746 if (jpeg_write_scanlines(&cinfo, &coffset, 1) != 1)
751 jpeg_finish_compress(&cinfo);
752 jpeg_destroy_compress(&cinfo);
754 WINPR_ASSERT(outsize <= UINT32_MAX);
755 *pSize = (UINT32)outsize;
761SSIZE_T winpr_convert_from_jpeg(WINPR_ATTR_UNUSED
const BYTE* comp_data,
762 WINPR_ATTR_UNUSED
size_t comp_data_bytes,
763 WINPR_ATTR_UNUSED UINT32* width, WINPR_ATTR_UNUSED UINT32* height,
764 WINPR_ATTR_UNUSED UINT32* bpp,
765 WINPR_ATTR_UNUSED BYTE** ppdecomp_data)
768 WINPR_ASSERT(comp_data || (comp_data_bytes == 0));
770 WINPR_ASSERT(height);
772 WINPR_ASSERT(ppdecomp_data);
774#if !defined(WINPR_UTILS_IMAGE_JPEG)
775 WLog_WARN(TAG,
"JPEG not supported in this build");
778 struct jpeg_decompress_struct cinfo = WINPR_C_ARRAY_INIT;
779 struct jpeg_error_mgr jerr;
781 BYTE* decomp_data =
nullptr;
783 cinfo.err = jpeg_std_error(&jerr);
784 jpeg_create_decompress(&cinfo);
785 jpeg_mem_src(&cinfo, comp_data, comp_data_bytes);
787 if (jpeg_read_header(&cinfo, 1) != JPEG_HEADER_OK)
790 cinfo.out_color_space = cinfo.num_components > 3 ? JCS_EXT_RGBA : JCS_EXT_BGR;
792 *width = WINPR_ASSERTING_INT_CAST(uint32_t, cinfo.image_width);
793 *height = WINPR_ASSERTING_INT_CAST(uint32_t, cinfo.image_height);
794 *bpp = WINPR_ASSERTING_INT_CAST(uint32_t, cinfo.num_components * 8);
796 if (!jpeg_start_decompress(&cinfo))
800 1ULL * cinfo.image_width * WINPR_ASSERTING_INT_CAST(uint32_t, cinfo.num_components);
802 if ((stride == 0) || (cinfo.image_height == 0))
805 decomp_data = calloc(stride, cinfo.image_height);
808 while (cinfo.output_scanline < cinfo.image_height)
810 JSAMPROW row = &decomp_data[cinfo.output_scanline * stride];
811 if (jpeg_read_scanlines(&cinfo, &row, 1) != 1)
814 const size_t ssize = stride * cinfo.image_height;
815 WINPR_ASSERT(ssize < SSIZE_MAX);
816 size = (SSIZE_T)ssize;
818 jpeg_finish_decompress(&cinfo);
821 jpeg_destroy_decompress(&cinfo);
822 *ppdecomp_data = decomp_data;
827static void* winpr_convert_to_webp(WINPR_ATTR_UNUSED
const void* data,
828 WINPR_ATTR_UNUSED
size_t size, WINPR_ATTR_UNUSED UINT32 width,
829 WINPR_ATTR_UNUSED UINT32 height, WINPR_ATTR_UNUSED UINT32 stride,
830 WINPR_ATTR_UNUSED UINT32 bpp, UINT32* pSize)
832 WINPR_ASSERT(data || (size == 0));
837#if !defined(WINPR_UTILS_IMAGE_WEBP)
838 WLog_WARN(TAG,
"WEBP not supported in this build");
842 uint8_t* pDstData =
nullptr;
843 WINPR_ASSERT(width <= INT32_MAX);
844 WINPR_ASSERT(height <= INT32_MAX);
845 WINPR_ASSERT(stride <= INT32_MAX);
849 dstSize = WebPEncodeLosslessBGRA(data, (
int)width, (
int)height, (
int)stride, &pDstData);
852 dstSize = WebPEncodeLosslessBGR(data, (
int)width, (
int)height, (
int)stride, &pDstData);
858 void* rc = malloc(dstSize);
861 memcpy(rc, pDstData, dstSize);
863 WINPR_ASSERT(dstSize <= UINT32_MAX);
864 *pSize = (UINT32)dstSize;
871SSIZE_T winpr_convert_from_webp(WINPR_ATTR_UNUSED
const BYTE* comp_data,
872 WINPR_ATTR_UNUSED
size_t comp_data_bytes, UINT32* width,
873 UINT32* height, UINT32* bpp, BYTE** ppdecomp_data)
875 WINPR_ASSERT(comp_data || (comp_data_bytes == 0));
877 WINPR_ASSERT(height);
879 WINPR_ASSERT(ppdecomp_data);
884 *ppdecomp_data =
nullptr;
885#if !defined(WINPR_UTILS_IMAGE_WEBP)
886 WLog_WARN(TAG,
"WEBP not supported in this build");
892 uint8_t* dst = WebPDecodeBGRA(comp_data, comp_data_bytes, &w, &h);
893 if (!dst || (w < 0) || (h < 0))
899 *width = WINPR_ASSERTING_INT_CAST(uint32_t, w);
900 *height = WINPR_ASSERTING_INT_CAST(uint32_t, h);
902 *ppdecomp_data = dst;
907#if defined(WINPR_UTILS_IMAGE_PNG)
914static void png_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
917 struct png_mem_encode* p =
918 (
struct png_mem_encode*)png_get_io_ptr(png_ptr);
919 size_t nsize = p->size + length;
924 char* tmp = realloc(p->buffer, nsize);
929 p->buffer = malloc(nsize);
932 png_error(png_ptr,
"Write Error");
935 memcpy(p->buffer + p->size, data, length);
940static void png_flush(WINPR_ATTR_UNUSED png_structp png_ptr)
944static SSIZE_T save_png_to_buffer(UINT32 bpp, UINT32 width, uint32_t stride, UINT32 height,
945 const uint8_t* data,
size_t size,
void** pDstData)
948 png_structp png_ptr =
nullptr;
949 png_infop info_ptr =
nullptr;
950 png_byte** row_pointers =
nullptr;
951 struct png_mem_encode state = WINPR_C_ARRAY_INIT;
955 if (!data || (size == 0))
958 WINPR_ASSERT(pDstData);
960 if (size < (1ULL * stride * height))
964 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
nullptr,
nullptr,
nullptr);
965 if (png_ptr ==
nullptr)
969 info_ptr = png_create_info_struct(png_ptr);
970 if (info_ptr ==
nullptr)
974 if (setjmp(png_jmpbuf(png_ptr)))
978 int colorType = PNG_COLOR_TYPE_PALETTE;
980 colorType = PNG_COLOR_TYPE_RGB;
982 colorType = PNG_COLOR_TYPE_RGB;
984 colorType = PNG_COLOR_TYPE_RGBA;
986 png_set_IHDR(png_ptr, info_ptr, width, height, 8, colorType, PNG_INTERLACE_NONE,
987 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
990 row_pointers = (png_byte**)png_malloc(png_ptr, height *
sizeof(png_byte*));
991 for (
size_t y = 0; y < height; ++y)
993 const uint8_t* line = &data[y * stride];
994 uint8_t* row = png_malloc(png_ptr,
sizeof(uint8_t) * stride);
995 row_pointers[y] = (png_byte*)row;
996 for (
size_t x = 0; x < width; ++x)
1010 png_set_write_fn(png_ptr, &state, png_write_data, png_flush);
1011 png_set_rows(png_ptr, info_ptr, row_pointers);
1012 png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_BGR,
nullptr);
1015 for (
size_t y = 0; y < height; y++)
1016 png_free(png_ptr, row_pointers[y]);
1017 png_free(png_ptr, (
void*)row_pointers);
1020 if (state.size > SSIZE_MAX)
1022 rc = (SSIZE_T)state.size;
1023 *pDstData = state.buffer;
1025 png_destroy_write_struct(&png_ptr, &info_ptr);
1034 png_uint_32 bufsize;
1035 png_uint_32 current_pos;
1036} MEMORY_READER_STATE;
1038static void read_data_memory(png_structp png_ptr, png_bytep data,
size_t length)
1040 MEMORY_READER_STATE* f = png_get_io_ptr(png_ptr);
1041 if (length > (f->bufsize - f->current_pos))
1042 png_error(png_ptr,
"read error in read_data_memory (loadpng)");
1045 memcpy(data, f->buffer + f->current_pos, length);
1046 f->current_pos += length;
1050static void* winpr_read_png_from_buffer(
const void* data,
size_t SrcSize,
size_t* pSize,
1051 UINT32* pWidth, UINT32* pHeight, UINT32* pBpp)
1054 png_uint_32 width = 0;
1055 png_uint_32 height = 0;
1058 int interlace_type = 0;
1059 int transforms = PNG_TRANSFORM_IDENTITY;
1060 MEMORY_READER_STATE memory_reader_state = WINPR_C_ARRAY_INIT;
1061 png_bytepp row_pointers =
nullptr;
1062 png_infop info_ptr =
nullptr;
1063 if (SrcSize > UINT32_MAX)
1066 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
nullptr,
nullptr,
nullptr);
1069 info_ptr = png_create_info_struct(png_ptr);
1073 memory_reader_state.buffer = WINPR_CAST_CONST_PTR_AWAY(data, png_bytep);
1074 memory_reader_state.bufsize = (UINT32)SrcSize;
1075 memory_reader_state.current_pos = 0;
1077 png_set_read_fn(png_ptr, &memory_reader_state, read_data_memory);
1079 transforms |= PNG_TRANSFORM_BGR;
1080 png_read_png(png_ptr, info_ptr, transforms,
nullptr);
1082 if (png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type,
1083 nullptr,
nullptr) != 1)
1086 WINPR_ASSERT(bit_depth >= 0);
1087 const png_byte channelcount = png_get_channels(png_ptr, info_ptr);
1088 const size_t bpp = channelcount * (size_t)bit_depth;
1090 row_pointers = png_get_rows(png_ptr, info_ptr);
1093 const size_t stride = 1ULL * width * bpp / 8ull;
1094 const size_t png_stride = png_get_rowbytes(png_ptr, info_ptr);
1095 const size_t size = 1ULL * width * height * bpp / 8ull;
1096 const size_t copybytes = stride > png_stride ? png_stride : stride;
1102 for (png_uint_32 i = 0; i < height; i++)
1104 memcpy(cur, row_pointers[i], copybytes);
1110 WINPR_ASSERT(bpp <= UINT32_MAX);
1111 *pBpp = (UINT32)bpp;
1116 png_destroy_read_struct(&png_ptr, &info_ptr,
nullptr);
1121static void* winpr_convert_to_png(WINPR_ATTR_UNUSED
const void* data, WINPR_ATTR_UNUSED
size_t size,
1122 WINPR_ATTR_UNUSED UINT32 width, WINPR_ATTR_UNUSED UINT32 height,
1123 WINPR_ATTR_UNUSED UINT32 stride, WINPR_ATTR_UNUSED UINT32 bpp,
1126 WINPR_ASSERT(data || (size == 0));
1127 WINPR_ASSERT(pSize);
1131#if defined(WINPR_UTILS_IMAGE_PNG)
1132 void* dst =
nullptr;
1133 SSIZE_T rc = save_png_to_buffer(bpp, width, stride, height, data, size, &dst);
1136 *pSize = (UINT32)rc;
1138#elif defined(WITH_LODEPNG)
1140 BYTE* dst =
nullptr;
1147 rc = lodepng_encode32(&dst, &dstsize, data, width, height);
1150 rc = lodepng_encode24(&dst, &dstsize, data, width, height);
1157 *pSize = (UINT32)dstsize;
1161 WLog_WARN(TAG,
"PNG not supported in this build");
1166SSIZE_T winpr_convert_from_png(WINPR_ATTR_UNUSED
const BYTE* comp_data,
1167 WINPR_ATTR_UNUSED
size_t comp_data_bytes,
1168 WINPR_ATTR_UNUSED UINT32* width, WINPR_ATTR_UNUSED UINT32* height,
1169 WINPR_ATTR_UNUSED UINT32* bpp,
1170 WINPR_ATTR_UNUSED BYTE** ppdecomp_data)
1172#if defined(WINPR_UTILS_IMAGE_PNG)
1175 winpr_read_png_from_buffer(comp_data, comp_data_bytes, &len, width, height, bpp);
1176 if (!*ppdecomp_data)
1178 return (SSIZE_T)len;
1179#elif defined(WITH_LODEPNG)
1181 return lodepng_decode32((
unsigned char**)ppdecomp_data, width, height, comp_data,
1184 WLog_WARN(TAG,
"PNG not supported in this build");
1189BOOL winpr_image_format_is_supported(UINT32 format)
1193 case WINPR_IMAGE_BITMAP:
1194#if defined(WINPR_UTILS_IMAGE_PNG) || defined(WITH_LODEPNG)
1195 case WINPR_IMAGE_PNG:
1197#if defined(WINPR_UTILS_IMAGE_JPEG)
1198 case WINPR_IMAGE_JPEG:
1200#if defined(WINPR_UTILS_IMAGE_WEBP)
1201 case WINPR_IMAGE_WEBP:
1209static BYTE* convert(
const wImage* image,
size_t* pstride, WINPR_ATTR_UNUSED UINT32 flags)
1211 WINPR_ASSERT(image);
1212 WINPR_ASSERT(pstride);
1215 if (image->bitsPerPixel < 24)
1218 const size_t stride = image->width * 4ull;
1219 BYTE* data = calloc(stride, image->height);
1222 for (
size_t y = 0; y < image->height; y++)
1224 const BYTE* srcLine = &image->data[image->scanline * y];
1225 BYTE* dstLine = &data[stride * y];
1226 if (image->bitsPerPixel == 32)
1227 memcpy(dstLine, srcLine, stride);
1230 for (
size_t x = 0; x < image->width; x++)
1232 const BYTE* src = &srcLine[image->bytesPerPixel * x];
1233 BYTE* dst = &dstLine[4ull * x];
1250static BOOL compare_byte_relaxed(BYTE a, BYTE b, UINT32 flags)
1254 if ((flags & WINPR_IMAGE_CMP_FUZZY) != 0)
1256 const int diff = abs((
int)a) - abs((
int)b);
1269static BOOL compare_pixel(
const BYTE* pa,
const BYTE* pb, UINT32 flags)
1274 if (!compare_byte_relaxed(*pa++, *pb++, flags))
1276 if (!compare_byte_relaxed(*pa++, *pb++, flags))
1278 if (!compare_byte_relaxed(*pa++, *pb++, flags))
1280 if ((flags & WINPR_IMAGE_CMP_IGNORE_ALPHA) == 0)
1282 if (!compare_byte_relaxed(*pa++, *pb++, flags))
1288BOOL winpr_image_equal(
const wImage* imageA,
const wImage* imageB, UINT32 flags)
1290 if (imageA == imageB)
1292 if (!imageA || !imageB)
1295 if (imageA->height != imageB->height)
1297 if (imageA->width != imageB->width)
1300 if ((flags & WINPR_IMAGE_CMP_IGNORE_DEPTH) == 0)
1302 if (imageA->bitsPerPixel != imageB->bitsPerPixel)
1304 if (imageA->bytesPerPixel != imageB->bytesPerPixel)
1311 BYTE* dataA = convert(imageA, &astride, flags);
1312 BYTE* dataB = convert(imageA, &bstride, flags);
1313 if (dataA && dataB && (astride == bstride))
1316 for (
size_t y = 0; y < imageA->height; y++)
1318 const BYTE* lineA = &dataA[astride * y];
1319 const BYTE* lineB = &dataB[bstride * y];
1321 for (
size_t x = 0; x < imageA->width; x++)
1323 const BYTE* pa = &lineA[x * 4ull];
1324 const BYTE* pb = &lineB[x * 4ull];
1326 if (!compare_pixel(pa, pb, flags))
1336const char* winpr_image_format_mime(UINT32 format)
1340 case WINPR_IMAGE_BITMAP:
1342 case WINPR_IMAGE_PNG:
1344 case WINPR_IMAGE_WEBP:
1345 return "image/webp";
1346 case WINPR_IMAGE_JPEG:
1347 return "image/jpeg";
1353const char* winpr_image_format_extension(UINT32 format)
1357 case WINPR_IMAGE_BITMAP:
1359 case WINPR_IMAGE_PNG:
1361 case WINPR_IMAGE_WEBP:
1363 case WINPR_IMAGE_JPEG:
1370void* winpr_image_write_buffer(
wImage* image, UINT32 format,
size_t* psize)
1372 WINPR_ASSERT(image);
1375 case WINPR_IMAGE_BITMAP:
1378 size_t size = 1ull * image->height * image->scanline;
1379 void* data = winpr_bitmap_write_buffer(image->data, size, image->width, image->height,
1380 image->scanline, image->bitsPerPixel, &outsize);
1384 case WINPR_IMAGE_WEBP:
1387 size_t size = 1ull * image->height * image->scanline;
1388 void* data = winpr_convert_to_webp(image->data, size, image->width, image->height,
1389 image->scanline, image->bitsPerPixel, &outsize);
1393 case WINPR_IMAGE_JPEG:
1396 size_t size = 1ull * image->height * image->scanline;
1397 void* data = winpr_convert_to_jpeg(image->data, size, image->width, image->height,
1398 image->scanline, image->bitsPerPixel, &outsize);
1402 case WINPR_IMAGE_PNG:
1405 size_t size = 1ull * image->height * image->scanline;
1406 void* data = winpr_convert_to_png(image->data, size, image->width, image->height,
1407 image->scanline, image->bitsPerPixel, &outsize);