24 #include <winpr/config.h>
26 #include <winpr/wtypes.h>
27 #include <winpr/crt.h>
28 #include <winpr/file.h>
30 #include <winpr/image.h>
32 #if defined(WINPR_UTILS_IMAGE_PNG)
36 #if defined(WINPR_UTILS_IMAGE_JPEG)
37 #define INT32 INT32_WINPR
42 #if defined(WINPR_UTILS_IMAGE_WEBP)
43 #include <webp/encode.h>
44 #include <webp/decode.h>
47 #if defined(WITH_LODEPNG)
50 #include <winpr/stream.h>
54 #define TAG WINPR_TAG("utils.image")
56 static SSIZE_T winpr_convert_from_jpeg(
const BYTE* comp_data,
size_t comp_data_bytes, UINT32* width,
57 UINT32* height, UINT32* bpp, BYTE** ppdecomp_data);
58 static SSIZE_T winpr_convert_from_png(
const BYTE* comp_data,
size_t comp_data_bytes, UINT32* width,
59 UINT32* height, UINT32* bpp, BYTE** ppdecomp_data);
60 static SSIZE_T winpr_convert_from_webp(
const BYTE* comp_data,
size_t comp_data_bytes, UINT32* width,
61 UINT32* height, UINT32* bpp, BYTE** ppdecomp_data);
68 Stream_Write_UINT8(s, bf->bfType[0]);
69 Stream_Write_UINT8(s, bf->bfType[1]);
70 Stream_Write_UINT32(s, bf->bfSize);
71 Stream_Write_UINT16(s, bf->bfReserved1);
72 Stream_Write_UINT16(s, bf->bfReserved2);
73 Stream_Write_UINT32(s, bf->bfOffBits);
79 static wLog* log = NULL;
87 Stream_Read_UINT8(s, bf->bfType[0]);
88 Stream_Read_UINT8(s, bf->bfType[1]);
89 Stream_Read_UINT32(s, bf->bfSize);
90 Stream_Read_UINT16(s, bf->bfReserved1);
91 Stream_Read_UINT16(s, bf->bfReserved2);
92 Stream_Read_UINT32(s, bf->bfOffBits);
96 WLog_Print(log, WLOG_ERROR,
"Invalid bitmap::bfSize=%" PRIu32
", require at least %" PRIuz,
101 if ((bf->bfType[0] !=
'B') || (bf->bfType[1] !=
'M'))
103 WLog_Print(log, WLOG_ERROR,
"Invalid bitmap header [%c%c], expected [BM]", bf->bfType[0],
107 return Stream_CheckAndLogRequiredCapacityWLog(log, s,
116 Stream_Write_UINT32(s, bi->biSize);
117 Stream_Write_INT32(s, bi->biWidth);
118 Stream_Write_INT32(s, bi->biHeight);
119 Stream_Write_UINT16(s, bi->biPlanes);
120 Stream_Write_UINT16(s, bi->biBitCount);
121 Stream_Write_UINT32(s, bi->biCompression);
122 Stream_Write_UINT32(s, bi->biSizeImage);
123 Stream_Write_INT32(s, bi->biXPelsPerMeter);
124 Stream_Write_INT32(s, bi->biYPelsPerMeter);
125 Stream_Write_UINT32(s, bi->biClrUsed);
126 Stream_Write_UINT32(s, bi->biClrImportant);
135 const size_t start = Stream_GetPosition(s);
136 Stream_Read_UINT32(s, bi->biSize);
137 Stream_Read_INT32(s, bi->biWidth);
138 Stream_Read_INT32(s, bi->biHeight);
139 Stream_Read_UINT16(s, bi->biPlanes);
140 Stream_Read_UINT16(s, bi->biBitCount);
141 Stream_Read_UINT32(s, bi->biCompression);
142 Stream_Read_UINT32(s, bi->biSizeImage);
143 Stream_Read_INT32(s, bi->biXPelsPerMeter);
144 Stream_Read_INT32(s, bi->biYPelsPerMeter);
145 Stream_Read_UINT32(s, bi->biClrUsed);
146 Stream_Read_UINT32(s, bi->biClrImportant);
148 if ((bi->biBitCount < 1) || (bi->biBitCount > 32))
150 WLog_WARN(TAG,
"invalid biBitCount=%" PRIu32, bi->biBitCount);
156 switch (bi->biCompression)
159 if (bi->biBitCount <= 8)
161 DWORD used = bi->biClrUsed;
163 used = (1 << bi->biBitCount) / 8;
164 offset +=
sizeof(
RGBQUAD) * used;
166 if (bi->biSizeImage == 0)
168 UINT32 stride = WINPR_ASSERTING_INT_CAST(
169 uint32_t, ((((bi->biWidth * bi->biBitCount) + 31) & ~31) >> 3));
170 bi->biSizeImage = WINPR_ASSERTING_INT_CAST(uint32_t, abs(bi->biHeight)) * stride;
174 offset +=
sizeof(DWORD) * 3;
177 WLog_ERR(TAG,
"unsupported biCompression %" PRIu32, bi->biCompression);
181 if (bi->biSizeImage == 0)
183 WLog_ERR(TAG,
"invalid biSizeImage %" PRIuz, bi->biSizeImage);
187 const size_t pos = Stream_GetPosition(s) - start;
188 if (bi->biSize < pos)
190 WLog_ERR(TAG,
"invalid biSize %" PRIuz
" < (actual) offset %" PRIuz, bi->biSize, pos);
195 return Stream_SafeSeek(s, bi->biSize - pos);
198 BYTE* winpr_bitmap_construct_header(
size_t width,
size_t height,
size_t bpp)
206 imgSize = width * height * (bpp / 8);
207 if ((width > INT32_MAX) || (height > INT32_MAX) || (bpp > UINT16_MAX) || (imgSize > UINT32_MAX))
210 s = Stream_New(NULL, WINPR_IMAGE_BMP_HEADER_LEN);
220 bi.biSizeImage = (UINT32)imgSize;
221 bf.bfSize = bf.bfOffBits + bi.biSizeImage;
222 bi.biWidth = (INT32)width;
223 bi.biHeight = -1 * (INT32)height;
225 bi.biBitCount = (UINT16)bpp;
226 bi.biCompression = BI_RGB;
227 bi.biXPelsPerMeter = (INT32)width;
228 bi.biYPelsPerMeter = (INT32)height;
230 bi.biClrImportant = 0;
233 switch (bi.biCompression)
236 if (bi.biBitCount <= 8)
238 DWORD used = bi.biClrUsed;
240 used = (1 << bi.biBitCount) / 8;
241 offset +=
sizeof(
RGBQUAD) * used;
245 offset +=
sizeof(DWORD) * 3;
251 if (!writeBitmapFileHeader(s, &bf))
254 if (!writeBitmapInfoHeader(s, &bi))
257 if (!Stream_EnsureRemainingCapacity(s, offset))
260 Stream_Zero(s, offset);
261 result = Stream_Buffer(s);
263 Stream_Free(s, result == 0);
271 WINPR_ATTR_MALLOC(free, 1)
272 static
void* winpr_bitmap_write_buffer(const BYTE* data,
size_t size, UINT32 width, UINT32 height,
273 UINT32 stride, UINT32 bpp, UINT32* pSize)
275 WINPR_ASSERT(data || (size == 0));
278 const size_t bpp_stride = 1ull * width * (bpp / 8);
279 if (bpp_stride > UINT32_MAX)
282 wStream* s = Stream_New(NULL, 1024);
285 stride = (UINT32)bpp_stride;
287 BYTE* bmp_header = winpr_bitmap_construct_header(width, height, bpp);
290 if (!Stream_EnsureRemainingCapacity(s, WINPR_IMAGE_BMP_HEADER_LEN))
292 Stream_Write(s, bmp_header, WINPR_IMAGE_BMP_HEADER_LEN);
294 if (!Stream_EnsureRemainingCapacity(s, 1ULL * stride * height))
297 for (
size_t y = 0; y < height; y++)
299 const BYTE* line = &data[stride * y];
301 Stream_Write(s, line, stride);
304 result = Stream_Buffer(s);
305 const size_t pos = Stream_GetPosition(s);
306 if (pos > UINT32_MAX)
308 *pSize = (UINT32)pos;
310 Stream_Free(s, result == NULL);
315 int winpr_bitmap_write(
const char* filename,
const BYTE* data,
size_t width,
size_t height,
318 return winpr_bitmap_write_ex(filename, data, 0, width, height, bpp);
321 int winpr_bitmap_write_ex(
const char* filename,
const BYTE* data,
size_t stride,
size_t width,
322 size_t height,
size_t bpp)
326 void* bmpdata = NULL;
327 const size_t bpp_stride = ((((width * bpp) + 31) & (size_t)~31) >> 3);
329 if ((stride > UINT32_MAX) || (width > UINT32_MAX) || (height > UINT32_MAX) ||
337 const size_t size = stride * 1ull * height;
338 bmpdata = winpr_bitmap_write_buffer(data, size, (UINT32)width, (UINT32)height, (UINT32)stride,
339 (UINT32)bpp, &bmpsize);
343 fp = winpr_fopen(filename,
"w+b");
346 WLog_ERR(TAG,
"failed to open file %s", filename);
350 if (fwrite(bmpdata, bmpsize, 1, fp) != 1)
361 static int write_and_free(
const char* filename,
void* data,
size_t size)
367 FILE* fp = winpr_fopen(filename,
"w+b");
371 size_t w = fwrite(data, 1, size, fp);
374 status = (w == size) ? 1 : -1;
380 int winpr_image_write(
wImage* image,
const char* filename)
383 return winpr_image_write_ex(image, WINPR_ASSERTING_INT_CAST(uint32_t, image->type), filename);
386 int winpr_image_write_ex(
wImage* image, UINT32 format,
const char* filename)
391 void* data = winpr_image_write_buffer(image, format, &size);
394 return write_and_free(filename, data, size);
397 static int winpr_image_bitmap_read_buffer(
wImage* image,
const BYTE* buffer,
size_t size)
404 wStream* s = Stream_StaticConstInit(&sbuffer, buffer, size);
409 size_t bmpoffset = 0;
410 if (!readBitmapFileHeader(s, &bf) || !readBitmapInfoHeader(s, &bi, &bmpoffset))
413 if ((bf.bfType[0] !=
'B') || (bf.bfType[1] !=
'M'))
415 WLog_WARN(TAG,
"Invalid bitmap header %c%c", bf.bfType[0], bf.bfType[1]);
419 image->type = WINPR_IMAGE_BITMAP;
421 const size_t pos = Stream_GetPosition(s);
422 const size_t expect = bf.bfOffBits;
426 WLog_WARN(TAG,
"pos=%" PRIuz
", expected %" PRIuz
", offset=" PRIuz, pos, expect,
431 if (!Stream_CheckAndLogRequiredCapacity(TAG, s, bi.biSizeImage))
436 WLog_WARN(TAG,
"bi.biWidth=%" PRId32, bi.biWidth);
440 image->width = (UINT32)bi.biWidth;
445 image->height = (UINT32)(-1 * bi.biHeight);
450 image->height = (UINT32)bi.biHeight;
453 if (image->height <= 0)
455 WLog_WARN(TAG,
"image->height=%" PRIu32, image->height);
459 image->bitsPerPixel = bi.biBitCount;
460 image->bytesPerPixel = (image->bitsPerPixel / 8UL);
461 const size_t bpp = (bi.biBitCount + 7UL) / 8UL;
463 WINPR_ASSERTING_INT_CAST(uint32_t, bi.biWidth) * WINPR_ASSERTING_INT_CAST(uint32_t, bpp);
464 const size_t bmpsize = 1ULL * image->scanline * image->height;
465 if (bmpsize != bi.biSizeImage)
466 WLog_WARN(TAG,
"bmpsize=%" PRIuz
" != bi.biSizeImage=%" PRIu32, bmpsize, bi.biSizeImage);
467 if (bi.biSizeImage < bmpsize)
471 if (bi.biSizeImage > 0)
472 image->data = (BYTE*)malloc(bi.biSizeImage);
478 Stream_Read(s, image->data, bi.biSizeImage);
481 BYTE* pDstData = &(image->data[(image->height - 1ull) * image->scanline]);
483 for (
size_t index = 0; index < image->height; index++)
485 Stream_Read(s, pDstData, image->scanline);
486 pDstData -= image->scanline;
502 int winpr_image_read(
wImage* image,
const char* filename)
506 FILE* fp = winpr_fopen(filename,
"rb");
509 WLog_ERR(TAG,
"failed to open file %s", filename);
513 (void)fseek(fp, 0, SEEK_END);
514 INT64 pos = _ftelli64(fp);
515 (void)fseek(fp, 0, SEEK_SET);
519 BYTE* buffer = malloc((
size_t)pos);
522 size_t r = fread(buffer, 1, (
size_t)pos, fp);
523 if (r == (
size_t)pos)
525 status = winpr_image_read_buffer(image, buffer, (
size_t)pos);
534 int winpr_image_read_buffer(
wImage* image,
const BYTE* buffer,
size_t size)
536 BYTE sig[12] = { 0 };
539 if (size <
sizeof(sig))
542 CopyMemory(sig, buffer,
sizeof(sig));
544 if ((sig[0] ==
'B') && (sig[1] ==
'M'))
546 image->type = WINPR_IMAGE_BITMAP;
547 status = winpr_image_bitmap_read_buffer(image, buffer, size);
549 else if ((sig[0] ==
'R') && (sig[1] ==
'I') && (sig[2] ==
'F') && (sig[3] ==
'F') &&
550 (sig[8] ==
'W') && (sig[9] ==
'E') && (sig[10] ==
'B') && (sig[11] ==
'P'))
552 image->type = WINPR_IMAGE_WEBP;
553 const SSIZE_T rc = winpr_convert_from_webp(buffer, size, &image->width, &image->height,
554 &image->bitsPerPixel, &image->data);
557 image->bytesPerPixel = (image->bitsPerPixel + 7) / 8;
558 image->scanline = image->width * image->bytesPerPixel;
562 else if ((sig[0] == 0xFF) && (sig[1] == 0xD8) && (sig[2] == 0xFF) && (sig[3] == 0xE0) &&
563 (sig[6] == 0x4A) && (sig[7] == 0x46) && (sig[8] == 0x49) && (sig[9] == 0x46) &&
566 image->type = WINPR_IMAGE_JPEG;
567 const SSIZE_T rc = winpr_convert_from_jpeg(buffer, size, &image->width, &image->height,
568 &image->bitsPerPixel, &image->data);
571 image->bytesPerPixel = (image->bitsPerPixel + 7) / 8;
572 image->scanline = image->width * image->bytesPerPixel;
576 else if ((sig[0] == 0x89) && (sig[1] ==
'P') && (sig[2] ==
'N') && (sig[3] ==
'G') &&
577 (sig[4] ==
'\r') && (sig[5] ==
'\n') && (sig[6] == 0x1A) && (sig[7] ==
'\n'))
579 image->type = WINPR_IMAGE_PNG;
580 const SSIZE_T rc = winpr_convert_from_png(buffer, size, &image->width, &image->height,
581 &image->bitsPerPixel, &image->data);
584 image->bytesPerPixel = (image->bitsPerPixel + 7) / 8;
585 image->scanline = image->width * image->bytesPerPixel;
593 wImage* winpr_image_new(
void)
603 void winpr_image_free(
wImage* image, BOOL bFreeBuffer)
614 static void* winpr_convert_to_jpeg(
const void* data,
size_t size, UINT32 width, UINT32 height,
615 UINT32 stride, UINT32 bpp, UINT32* pSize)
617 WINPR_ASSERT(data || (size == 0));
622 #if !defined(WINPR_UTILS_IMAGE_JPEG)
625 BYTE* outbuffer = NULL;
626 unsigned long outsize = 0;
627 struct jpeg_compress_struct cinfo = { 0 };
629 const size_t expect1 = 1ull * stride * height;
630 const size_t bytes = (bpp + 7) / 8;
631 const size_t expect2 = 1ull * width * height * bytes;
632 if (expect1 != expect2)
638 struct jpeg_error_mgr jerr = { 0 };
639 cinfo.err = jpeg_std_error(&jerr);
641 jpeg_create_compress(&cinfo);
642 jpeg_mem_dest(&cinfo, &outbuffer, &outsize);
644 cinfo.image_width = width;
645 cinfo.image_height = height;
646 WINPR_ASSERT(bpp <= INT32_MAX / 8);
647 cinfo.input_components = (int)(bpp + 7) / 8;
648 cinfo.in_color_space = (bpp > 24) ? JCS_EXT_BGRA : JCS_EXT_BGR;
649 cinfo.data_precision = 8;
651 jpeg_set_defaults(&cinfo);
652 jpeg_set_quality(&cinfo, 100, TRUE);
654 cinfo.comp_info[0].h_samp_factor = cinfo.comp_info[0].v_samp_factor = 1;
656 jpeg_start_compress(&cinfo, TRUE);
658 const JSAMPLE* cdata = data;
659 for (
size_t x = 0; x < height; x++)
661 WINPR_ASSERT(x * stride <= UINT32_MAX);
662 const JDIMENSION offset = (JDIMENSION)x * stride;
666 JSAMPLE* coffset = WINPR_CAST_CONST_PTR_AWAY(&cdata[offset], JSAMPLE*);
667 if (jpeg_write_scanlines(&cinfo, &coffset, 1) != 1)
672 jpeg_finish_compress(&cinfo);
673 jpeg_destroy_compress(&cinfo);
675 WINPR_ASSERT(outsize <= UINT32_MAX);
676 *pSize = (UINT32)outsize;
682 SSIZE_T winpr_convert_from_jpeg(
const BYTE* comp_data,
size_t comp_data_bytes, UINT32* width,
683 UINT32* height, UINT32* bpp, BYTE** ppdecomp_data)
686 WINPR_ASSERT(comp_data || (comp_data_bytes == 0));
688 WINPR_ASSERT(height);
690 WINPR_ASSERT(ppdecomp_data);
692 #if !defined(WINPR_UTILS_IMAGE_JPEG)
695 struct jpeg_decompress_struct cinfo = { 0 };
696 struct jpeg_error_mgr jerr;
698 BYTE* decomp_data = NULL;
700 cinfo.err = jpeg_std_error(&jerr);
701 jpeg_create_decompress(&cinfo);
702 jpeg_mem_src(&cinfo, comp_data, comp_data_bytes);
704 if (jpeg_read_header(&cinfo, 1) != JPEG_HEADER_OK)
707 cinfo.out_color_space = cinfo.num_components > 3 ? JCS_EXT_RGBA : JCS_EXT_BGR;
709 *width = cinfo.image_width;
710 *height = cinfo.image_height;
711 *bpp = cinfo.num_components * 8;
713 if (!jpeg_start_decompress(&cinfo))
716 size_t stride = 1ULL * cinfo.image_width * cinfo.num_components;
718 decomp_data = calloc(stride, cinfo.image_height);
721 while (cinfo.output_scanline < cinfo.image_height)
723 JSAMPROW row = &decomp_data[cinfo.output_scanline * stride];
724 if (jpeg_read_scanlines(&cinfo, &row, 1) != 1)
727 const size_t ssize = stride * cinfo.image_height;
728 WINPR_ASSERT(ssize < SSIZE_MAX);
729 size = (SSIZE_T)ssize;
731 jpeg_finish_decompress(&cinfo);
734 jpeg_destroy_decompress(&cinfo);
735 *ppdecomp_data = decomp_data;
740 static void* winpr_convert_to_webp(
const void* data,
size_t size, UINT32 width, UINT32 height,
741 UINT32 stride, UINT32 bpp, UINT32* pSize)
743 WINPR_ASSERT(data || (size == 0));
748 #if !defined(WINPR_UTILS_IMAGE_WEBP)
752 uint8_t* pDstData = NULL;
753 WINPR_ASSERT(width <= INT32_MAX);
754 WINPR_ASSERT(height <= INT32_MAX);
755 WINPR_ASSERT(stride <= INT32_MAX);
759 dstSize = WebPEncodeLosslessBGRA(data, (
int)width, (
int)height, (
int)stride, &pDstData);
762 dstSize = WebPEncodeLosslessBGR(data, (
int)width, (
int)height, (
int)stride, &pDstData);
768 void* rc = malloc(dstSize);
771 memcpy(rc, pDstData, dstSize);
773 WINPR_ASSERT(dstSize <= UINT32_MAX);
774 *pSize = (UINT32)dstSize;
781 SSIZE_T winpr_convert_from_webp(
const BYTE* comp_data,
size_t comp_data_bytes, UINT32* width,
782 UINT32* height, UINT32* bpp, BYTE** ppdecomp_data)
784 WINPR_ASSERT(comp_data || (comp_data_bytes == 0));
786 WINPR_ASSERT(height);
788 WINPR_ASSERT(ppdecomp_data);
793 *ppdecomp_data = NULL;
794 #if !defined(WINPR_UTILS_IMAGE_WEBP)
800 uint8_t* dst = WebPDecodeBGRA(comp_data, comp_data_bytes, &w, &h);
801 if (!dst || (w < 0) || (h < 0))
810 *ppdecomp_data = dst;
815 #if defined(WINPR_UTILS_IMAGE_PNG)
816 struct png_mem_encode
822 static void png_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
825 struct png_mem_encode* p =
826 (
struct png_mem_encode*)png_get_io_ptr(png_ptr);
827 size_t nsize = p->size + length;
832 char* tmp = realloc(p->buffer, nsize);
837 p->buffer = malloc(nsize);
840 png_error(png_ptr,
"Write Error");
843 memcpy(p->buffer + p->size, data, length);
848 static void png_flush(png_structp png_ptr)
852 static SSIZE_T save_png_to_buffer(UINT32 bpp, UINT32 width, UINT32 height,
const uint8_t* data,
853 size_t size,
void** pDstData)
856 png_structp png_ptr = NULL;
857 png_infop info_ptr = NULL;
858 png_byte** row_pointers = NULL;
859 struct png_mem_encode state = { 0 };
863 if (!data || (size == 0))
866 WINPR_ASSERT(pDstData);
868 const size_t bytes_per_pixel = (bpp + 7) / 8;
869 const size_t bytes_per_row = width * bytes_per_pixel;
870 if (size < bytes_per_row * height)
874 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
879 info_ptr = png_create_info_struct(png_ptr);
880 if (info_ptr == NULL)
884 if (setjmp(png_jmpbuf(png_ptr)))
888 int colorType = PNG_COLOR_TYPE_PALETTE;
890 colorType = PNG_COLOR_TYPE_RGB;
892 colorType = PNG_COLOR_TYPE_RGB;
894 colorType = PNG_COLOR_TYPE_RGBA;
896 png_set_IHDR(png_ptr, info_ptr, width, height, 8, colorType, PNG_INTERLACE_NONE,
897 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
900 row_pointers = (png_byte**)png_malloc(png_ptr, height *
sizeof(png_byte*));
901 for (
size_t y = 0; y < height; ++y)
903 uint8_t* row = png_malloc(png_ptr,
sizeof(uint8_t) * bytes_per_row);
904 row_pointers[y] = (png_byte*)row;
905 for (
size_t x = 0; x < width; ++x)
919 png_set_write_fn(png_ptr, &state, png_write_data, png_flush);
920 png_set_rows(png_ptr, info_ptr, row_pointers);
921 png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_BGR, NULL);
924 for (
size_t y = 0; y < height; y++)
925 png_free(png_ptr, row_pointers[y]);
926 png_free(png_ptr, (
void*)row_pointers);
929 if (state.size > SSIZE_MAX)
931 rc = (SSIZE_T)state.size;
932 *pDstData = state.buffer;
934 png_destroy_write_struct(&png_ptr, &info_ptr);
944 png_uint_32 current_pos;
945 } MEMORY_READER_STATE;
947 static void read_data_memory(png_structp png_ptr, png_bytep data,
size_t length)
949 MEMORY_READER_STATE* f = png_get_io_ptr(png_ptr);
950 if (length > (f->bufsize - f->current_pos))
951 png_error(png_ptr,
"read error in read_data_memory (loadpng)");
954 memcpy(data, f->buffer + f->current_pos, length);
955 f->current_pos += length;
959 static void* winpr_read_png_from_buffer(
const void* data,
size_t SrcSize,
size_t* pSize,
960 UINT32* pWidth, UINT32* pHeight, UINT32* pBpp)
963 png_uint_32 width = 0;
964 png_uint_32 height = 0;
967 int interlace_type = 0;
968 int transforms = PNG_TRANSFORM_IDENTITY;
969 MEMORY_READER_STATE memory_reader_state = { 0 };
970 png_bytepp row_pointers = NULL;
971 png_infop info_ptr = NULL;
972 if (SrcSize > UINT32_MAX)
975 png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
978 info_ptr = png_create_info_struct(png_ptr);
982 memory_reader_state.buffer = WINPR_CAST_CONST_PTR_AWAY(data, png_bytep);
983 memory_reader_state.bufsize = (UINT32)SrcSize;
984 memory_reader_state.current_pos = 0;
986 png_set_read_fn(png_ptr, &memory_reader_state, read_data_memory);
988 transforms |= PNG_TRANSFORM_BGR;
989 png_read_png(png_ptr, info_ptr, transforms, NULL);
991 if (png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type,
995 WINPR_ASSERT(bit_depth >= 0);
996 const png_byte channelcount = png_get_channels(png_ptr, info_ptr);
997 const size_t bpp = channelcount * (size_t)bit_depth;
999 row_pointers = png_get_rows(png_ptr, info_ptr);
1002 const size_t stride = 1ULL * width * bpp / 8ull;
1003 const size_t png_stride = png_get_rowbytes(png_ptr, info_ptr);
1004 const size_t size = 1ULL * width * height * bpp / 8ull;
1005 const size_t copybytes = stride > png_stride ? png_stride : stride;
1011 for (png_uint_32 i = 0; i < height; i++)
1013 memcpy(cur, row_pointers[i], copybytes);
1019 WINPR_ASSERT(bpp <= UINT32_MAX);
1020 *pBpp = (UINT32)bpp;
1025 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
1030 static void* winpr_convert_to_png(
const void* data,
size_t size, UINT32 width, UINT32 height,
1031 UINT32 stride, UINT32 bpp, UINT32* pSize)
1033 WINPR_ASSERT(data || (size == 0));
1034 WINPR_ASSERT(pSize);
1038 #if defined(WINPR_UTILS_IMAGE_PNG)
1040 SSIZE_T rc = save_png_to_buffer(bpp, width, height, data, size, &dst);
1043 *pSize = (UINT32)rc;
1045 #elif defined(WITH_LODEPNG)
1054 rc = lodepng_encode32(&dst, &dstsize, data, width, height);
1057 rc = lodepng_encode24(&dst, &dstsize, data, width, height);
1064 *pSize = (UINT32)dstsize;
1072 SSIZE_T winpr_convert_from_png(
const BYTE* comp_data,
size_t comp_data_bytes, UINT32* width,
1073 UINT32* height, UINT32* bpp, BYTE** ppdecomp_data)
1075 #if defined(WINPR_UTILS_IMAGE_PNG)
1078 winpr_read_png_from_buffer(comp_data, comp_data_bytes, &len, width, height, bpp);
1079 if (!*ppdecomp_data)
1081 return (SSIZE_T)len;
1082 #elif defined(WITH_LODEPNG)
1084 return lodepng_decode32((
unsigned char**)ppdecomp_data, width, height, comp_data,
1091 BOOL winpr_image_format_is_supported(UINT32 format)
1095 case WINPR_IMAGE_BITMAP:
1096 #if defined(WINPR_UTILS_IMAGE_PNG) || defined(WITH_LODEPNG)
1097 case WINPR_IMAGE_PNG:
1099 #if defined(WINPR_UTILS_IMAGE_JPEG)
1100 case WINPR_IMAGE_JPEG:
1102 #if defined(WINPR_UTILS_IMAGE_WEBP)
1103 case WINPR_IMAGE_WEBP:
1111 static BYTE* convert(
const wImage* image,
size_t* pstride, UINT32 flags)
1113 WINPR_ASSERT(image);
1114 WINPR_ASSERT(pstride);
1117 if (image->bitsPerPixel < 24)
1120 const size_t stride = image->width * 4ull;
1121 BYTE* data = calloc(stride, image->height);
1124 for (
size_t y = 0; y < image->height; y++)
1126 const BYTE* srcLine = &image->data[image->scanline * y];
1127 BYTE* dstLine = &data[stride * y];
1128 if (image->bitsPerPixel == 32)
1129 memcpy(dstLine, srcLine, stride);
1132 for (
size_t x = 0; x < image->width; x++)
1134 const BYTE* src = &srcLine[image->bytesPerPixel * x];
1135 BYTE* dst = &dstLine[4ull * x];
1152 static BOOL compare_byte_relaxed(BYTE a, BYTE b, UINT32 flags)
1156 if ((flags & WINPR_IMAGE_CMP_FUZZY) != 0)
1158 const int diff = abs((
int)a) - abs((
int)b);
1171 static BOOL compare_pixel(
const BYTE* pa,
const BYTE* pb, UINT32 flags)
1176 if (!compare_byte_relaxed(*pa++, *pb++, flags))
1178 if (!compare_byte_relaxed(*pa++, *pb++, flags))
1180 if (!compare_byte_relaxed(*pa++, *pb++, flags))
1182 if ((flags & WINPR_IMAGE_CMP_IGNORE_ALPHA) == 0)
1184 if (!compare_byte_relaxed(*pa++, *pb++, flags))
1190 BOOL winpr_image_equal(
const wImage* imageA,
const wImage* imageB, UINT32 flags)
1192 if (imageA == imageB)
1194 if (!imageA || !imageB)
1197 if (imageA->height != imageB->height)
1199 if (imageA->width != imageB->width)
1202 if ((flags & WINPR_IMAGE_CMP_IGNORE_DEPTH) == 0)
1204 if (imageA->bitsPerPixel != imageB->bitsPerPixel)
1206 if (imageA->bytesPerPixel != imageB->bytesPerPixel)
1213 BYTE* dataA = convert(imageA, &astride, flags);
1214 BYTE* dataB = convert(imageA, &bstride, flags);
1215 if (dataA && dataB && (astride == bstride))
1218 for (
size_t y = 0; y < imageA->height; y++)
1220 const BYTE* lineA = &dataA[astride * y];
1221 const BYTE* lineB = &dataB[bstride * y];
1223 for (
size_t x = 0; x < imageA->width; x++)
1225 const BYTE* pa = &lineA[x * 4ull];
1226 const BYTE* pb = &lineB[x * 4ull];
1228 if (!compare_pixel(pa, pb, flags))
1238 const char* winpr_image_format_mime(UINT32 format)
1242 case WINPR_IMAGE_BITMAP:
1244 case WINPR_IMAGE_PNG:
1246 case WINPR_IMAGE_WEBP:
1247 return "image/webp";
1248 case WINPR_IMAGE_JPEG:
1249 return "image/jpeg";
1255 const char* winpr_image_format_extension(UINT32 format)
1259 case WINPR_IMAGE_BITMAP:
1261 case WINPR_IMAGE_PNG:
1263 case WINPR_IMAGE_WEBP:
1265 case WINPR_IMAGE_JPEG:
1272 void* winpr_image_write_buffer(
wImage* image, UINT32 format,
size_t* psize)
1274 WINPR_ASSERT(image);
1277 case WINPR_IMAGE_BITMAP:
1280 size_t size = 1ull * image->height * image->scanline;
1281 void* data = winpr_bitmap_write_buffer(image->data, size, image->width, image->height,
1282 image->scanline, image->bitsPerPixel, &outsize);
1286 case WINPR_IMAGE_WEBP:
1289 size_t size = 1ull * image->height * image->scanline;
1290 void* data = winpr_convert_to_webp(image->data, size, image->width, image->height,
1291 image->scanline, image->bitsPerPixel, &outsize);
1295 case WINPR_IMAGE_JPEG:
1298 size_t size = 1ull * image->height * image->scanline;
1299 void* data = winpr_convert_to_jpeg(image->data, size, image->width, image->height,
1300 image->scanline, image->bitsPerPixel, &outsize);
1304 case WINPR_IMAGE_PNG:
1307 size_t size = 1ull * image->height * image->scanline;
1308 void* data = winpr_convert_to_png(image->data, size, image->width, image->height,
1309 image->scanline, image->bitsPerPixel, &outsize);