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 = NULL;
 
   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_CheckAndLogRequiredCapacityWLog(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  switch (bi->biCompression)
 
  160      if (bi->biBitCount <= 8)
 
  162        DWORD used = bi->biClrUsed;
 
  164          used = (1 << bi->biBitCount) / 8;
 
  165        offset += 
sizeof(
RGBQUAD) * used;
 
  167      if (bi->biSizeImage == 0)
 
  169        UINT32 stride = WINPR_ASSERTING_INT_CAST(
 
  170            uint32_t, ((((bi->biWidth * bi->biBitCount) + 31) & ~31) >> 3));
 
  171        bi->biSizeImage = WINPR_ASSERTING_INT_CAST(uint32_t, abs(bi->biHeight)) * stride;
 
  175      offset += 
sizeof(DWORD) * 3; 
 
  178      WLog_ERR(TAG, 
"unsupported biCompression %" PRIu32, bi->biCompression);
 
  182  if (bi->biSizeImage == 0)
 
  184    WLog_ERR(TAG, 
"invalid biSizeImage %" PRIuz, bi->biSizeImage);
 
  188  const size_t pos = Stream_GetPosition(s) - start;
 
  189  if (bi->biSize < pos)
 
  191    WLog_ERR(TAG, 
"invalid biSize %" PRIuz 
" < (actual) offset %" PRIuz, bi->biSize, pos);
 
  196  return Stream_SafeSeek(s, bi->biSize - pos);
 
  199BYTE* winpr_bitmap_construct_header(
size_t width, 
size_t height, 
size_t bpp)
 
  205  size_t stride = (width * bpp + 7) / 8;
 
  206  if ((stride % 4) != 0)
 
  207    stride += 4 - (stride % 4);
 
  209  size_t imgSize = stride * height;
 
  210  if ((width > INT32_MAX) || (height > INT32_MAX) || (bpp > UINT16_MAX) || (imgSize > UINT32_MAX))
 
  213  wStream* s = Stream_New(NULL, WINPR_IMAGE_BMP_HEADER_LEN);
 
  223  bi.biSizeImage = (UINT32)imgSize;
 
  224  bf.bfSize = bf.bfOffBits + bi.biSizeImage;
 
  225  bi.biWidth = (INT32)width;
 
  226  bi.biHeight = -1 * (INT32)height;
 
  228  bi.biBitCount = (UINT16)bpp;
 
  229  bi.biCompression = BI_RGB;
 
  230  bi.biXPelsPerMeter = (INT32)width;
 
  231  bi.biYPelsPerMeter = (INT32)height;
 
  233  bi.biClrImportant = 0;
 
  236  switch (bi.biCompression)
 
  239      if (bi.biBitCount <= 8)
 
  241        DWORD used = bi.biClrUsed;
 
  243          used = (1 << bi.biBitCount) / 8;
 
  244        offset += 
sizeof(
RGBQUAD) * used;
 
  248      offset += 
sizeof(DWORD) * 3; 
 
  254  if (!writeBitmapFileHeader(s, &bf))
 
  257  if (!writeBitmapInfoHeader(s, &bi))
 
  260  if (!Stream_EnsureRemainingCapacity(s, offset))
 
  263  Stream_Zero(s, offset);
 
  264  result = Stream_Buffer(s);
 
  266  Stream_Free(s, result == 0);
 
  274WINPR_ATTR_MALLOC(free, 1)
 
  275static 
void* winpr_bitmap_write_buffer(const BYTE* data, WINPR_ATTR_UNUSED 
size_t size,
 
  276                                       UINT32 width, UINT32 height, UINT32 stride, UINT32 bpp,
 
  279  WINPR_ASSERT(data || (size == 0));
 
  282  size_t bpp_stride = 1ull * width * (bpp / 8);
 
  283  if ((bpp_stride % 4) != 0)
 
  284    bpp_stride += 4 - (bpp_stride % 4);
 
  286  if (bpp_stride > UINT32_MAX)
 
  289  wStream* s = Stream_New(NULL, 1024);
 
  292    stride = (UINT32)bpp_stride;
 
  294  BYTE* bmp_header = winpr_bitmap_construct_header(width, height, bpp);
 
  297  if (!Stream_EnsureRemainingCapacity(s, WINPR_IMAGE_BMP_HEADER_LEN))
 
  299  Stream_Write(s, bmp_header, WINPR_IMAGE_BMP_HEADER_LEN);
 
  301  if (!Stream_EnsureRemainingCapacity(s, 1ULL * bpp_stride * height))
 
  304  for (
size_t y = 0; y < height; y++)
 
  306    const BYTE* line = &data[stride * y];
 
  308    Stream_Write(s, line, stride);
 
  309    Stream_Zero(s, bpp_stride - stride);
 
  312  result = Stream_Buffer(s);
 
  313  const size_t pos = Stream_GetPosition(s);
 
  314  if (pos > UINT32_MAX)
 
  316  *pSize = (UINT32)pos;
 
  318  Stream_Free(s, result == NULL);
 
  323int winpr_bitmap_write(
const char* filename, 
const BYTE* data, 
size_t width, 
size_t height,
 
  326  return winpr_bitmap_write_ex(filename, data, 0, width, height, bpp);
 
  329int winpr_bitmap_write_ex(
const char* filename, 
const BYTE* data, 
size_t stride, 
size_t width,
 
  330                          size_t height, 
size_t bpp)
 
  334  void* bmpdata = NULL;
 
  335  const size_t bpp_stride = ((((width * bpp) + 31) & (size_t)~31) >> 3);
 
  337  if ((stride > UINT32_MAX) || (width > UINT32_MAX) || (height > UINT32_MAX) ||
 
  345  const size_t size = stride * 1ull * height;
 
  346  bmpdata = winpr_bitmap_write_buffer(data, size, (UINT32)width, (UINT32)height, (UINT32)stride,
 
  347                                      (UINT32)bpp, &bmpsize);
 
  351  fp = winpr_fopen(filename, 
"w+b");
 
  354    WLog_ERR(TAG, 
"failed to open file %s", filename);
 
  358  if (fwrite(bmpdata, bmpsize, 1, fp) != 1)
 
  369static int write_and_free(
const char* filename, 
void* data, 
size_t size)
 
  375  FILE* fp = winpr_fopen(filename, 
"w+b");
 
  379  size_t w = fwrite(data, 1, size, fp);
 
  382  status = (w == size) ? 1 : -1;
 
  388int winpr_image_write(
wImage* image, 
const char* filename)
 
  391  return winpr_image_write_ex(image, WINPR_ASSERTING_INT_CAST(uint32_t, image->type), filename);
 
  394int winpr_image_write_ex(
wImage* image, UINT32 format, 
const char* filename)
 
  399  void* data = winpr_image_write_buffer(image, format, &size);
 
  402  return write_and_free(filename, data, size);
 
  405static int winpr_image_bitmap_read_buffer(
wImage* image, 
const BYTE* buffer, 
size_t size)
 
  412  wStream* s = Stream_StaticConstInit(&sbuffer, buffer, size);
 
  417  size_t bmpoffset = 0;
 
  418  if (!readBitmapFileHeader(s, &bf) || !readBitmapInfoHeader(s, &bi, &bmpoffset))
 
  421  if ((bf.bfType[0] != 
'B') || (bf.bfType[1] != 
'M'))
 
  423    WLog_WARN(TAG, 
"Invalid bitmap header %c%c", bf.bfType[0], bf.bfType[1]);
 
  427  image->type = WINPR_IMAGE_BITMAP;
 
  429  const size_t pos = Stream_GetPosition(s);
 
  430  const size_t expect = bf.bfOffBits;
 
  434    WLog_WARN(TAG, 
"pos=%" PRIuz 
", expected %" PRIuz 
", offset=" PRIuz, pos, expect,
 
  439  if (!Stream_CheckAndLogRequiredCapacity(TAG, s, bi.biSizeImage))
 
  444    WLog_WARN(TAG, 
"bi.biWidth=%" PRId32, bi.biWidth);
 
  448  image->width = (UINT32)bi.biWidth;
 
  453    image->height = (UINT32)(-1 * bi.biHeight);
 
  458    image->height = (UINT32)bi.biHeight;
 
  461  if (image->height <= 0)
 
  463    WLog_WARN(TAG, 
"image->height=%" PRIu32, image->height);
 
  467  image->bitsPerPixel = bi.biBitCount;
 
  468  const size_t bpp = (bi.biBitCount + 7UL) / 8UL;
 
  469  image->bytesPerPixel = WINPR_ASSERTING_INT_CAST(uint32_t, bpp);
 
  470  image->scanline = WINPR_ASSERTING_INT_CAST(uint32_t, bi.biWidth) * image->bytesPerPixel;
 
  471  if ((image->scanline % 4) != 0)
 
  472    image->scanline += 4 - image->scanline % 4;
 
  474  const size_t bmpsize = 1ULL * image->scanline * image->height;
 
  475  if (bmpsize != bi.biSizeImage)
 
  476    WLog_WARN(TAG, 
"bmpsize=%" PRIuz 
" != bi.biSizeImage=%" PRIu32, bmpsize, bi.biSizeImage);
 
  478  size_t scanline = image->scanline;
 
  479  if (bi.biSizeImage < bmpsize)
 
  482    const size_t uscanline = image->width * bpp;
 
  483    const size_t unaligned = image->height * uscanline;
 
  484    if (bi.biSizeImage != unaligned)
 
  486    scanline = uscanline;
 
  490  const size_t asize = 1ULL * image->scanline * image->height;
 
  492    image->data = (BYTE*)malloc(asize);
 
  499    BYTE* pDstData = image->data;
 
  501    for (
size_t index = 0; index < image->height; index++)
 
  503      Stream_Read(s, pDstData, scanline);
 
  504      Stream_Seek(s, image->scanline - scanline);
 
  505      pDstData += scanline;
 
  510    BYTE* pDstData = &(image->data[(image->height - 1ull) * image->scanline]);
 
  512    for (
size_t index = 0; index < image->height; index++)
 
  514      Stream_Read(s, pDstData, scanline);
 
  515      Stream_Seek(s, image->scanline - scanline);
 
  516      pDstData -= scanline;
 
  532int winpr_image_read(
wImage* image, 
const char* filename)
 
  536  FILE* fp = winpr_fopen(filename, 
"rb");
 
  539    WLog_ERR(TAG, 
"failed to open file %s", filename);
 
  543  (void)fseek(fp, 0, SEEK_END);
 
  544  INT64 pos = _ftelli64(fp);
 
  545  (void)fseek(fp, 0, SEEK_SET);
 
  549    BYTE* buffer = malloc((
size_t)pos);
 
  552      size_t r = fread(buffer, 1, (
size_t)pos, fp);
 
  553      if (r == (
size_t)pos)
 
  555        status = winpr_image_read_buffer(image, buffer, (
size_t)pos);
 
  564int winpr_image_read_buffer(
wImage* image, 
const BYTE* buffer, 
size_t size)
 
  566  BYTE sig[12] = { 0 };
 
  569  if (size < 
sizeof(sig))
 
  572  CopyMemory(sig, buffer, 
sizeof(sig));
 
  574  if ((sig[0] == 
'B') && (sig[1] == 
'M'))
 
  576    image->type = WINPR_IMAGE_BITMAP;
 
  577    status = winpr_image_bitmap_read_buffer(image, buffer, size);
 
  579  else if ((sig[0] == 
'R') && (sig[1] == 
'I') && (sig[2] == 
'F') && (sig[3] == 
'F') &&
 
  580           (sig[8] == 
'W') && (sig[9] == 
'E') && (sig[10] == 
'B') && (sig[11] == 
'P'))
 
  582    image->type = WINPR_IMAGE_WEBP;
 
  583    const SSIZE_T rc = winpr_convert_from_webp(buffer, size, &image->width, &image->height,
 
  584                                               &image->bitsPerPixel, &image->data);
 
  587      image->bytesPerPixel = (image->bitsPerPixel + 7) / 8;
 
  588      image->scanline = image->width * image->bytesPerPixel;
 
  592  else if ((sig[0] == 0xFF) && (sig[1] == 0xD8) && (sig[2] == 0xFF) && (sig[3] == 0xE0) &&
 
  593           (sig[6] == 0x4A) && (sig[7] == 0x46) && (sig[8] == 0x49) && (sig[9] == 0x46) &&
 
  596    image->type = WINPR_IMAGE_JPEG;
 
  597    const SSIZE_T rc = winpr_convert_from_jpeg(buffer, size, &image->width, &image->height,
 
  598                                               &image->bitsPerPixel, &image->data);
 
  601      image->bytesPerPixel = (image->bitsPerPixel + 7) / 8;
 
  602      image->scanline = image->width * image->bytesPerPixel;
 
  606  else if ((sig[0] == 0x89) && (sig[1] == 
'P') && (sig[2] == 
'N') && (sig[3] == 
'G') &&
 
  607           (sig[4] == 
'\r') && (sig[5] == 
'\n') && (sig[6] == 0x1A) && (sig[7] == 
'\n'))
 
  609    image->type = WINPR_IMAGE_PNG;
 
  610    const SSIZE_T rc = winpr_convert_from_png(buffer, size, &image->width, &image->height,
 
  611                                              &image->bitsPerPixel, &image->data);
 
  614      image->bytesPerPixel = (image->bitsPerPixel + 7) / 8;
 
  615      image->scanline = image->width * image->bytesPerPixel;
 
  623wImage* winpr_image_new(
void)
 
  633void winpr_image_free(
wImage* image, BOOL bFreeBuffer)
 
  644static void* winpr_convert_to_jpeg(WINPR_ATTR_UNUSED 
const void* data,
 
  645                                   WINPR_ATTR_UNUSED 
size_t size, WINPR_ATTR_UNUSED UINT32 width,
 
  646                                   WINPR_ATTR_UNUSED UINT32 height, WINPR_ATTR_UNUSED UINT32 stride,
 
  647                                   WINPR_ATTR_UNUSED UINT32 bpp, WINPR_ATTR_UNUSED UINT32* pSize)
 
  649  WINPR_ASSERT(data || (size == 0));
 
  654#if !defined(WINPR_UTILS_IMAGE_JPEG) 
  655  WLog_WARN(TAG, 
"JPEG not supported in this build");
 
  658  BYTE* outbuffer = NULL;
 
  659  unsigned long outsize = 0;
 
  660  struct jpeg_compress_struct cinfo = { 0 };
 
  662  const size_t expect1 = 1ull * stride * height;
 
  663  const size_t bytes = (bpp + 7) / 8;
 
  664  const size_t expect2 = 1ull * width * height * bytes;
 
  665  if (expect1 < expect2)
 
  671  struct jpeg_error_mgr jerr = { 0 };
 
  672  cinfo.err = jpeg_std_error(&jerr);
 
  674  jpeg_create_compress(&cinfo);
 
  675  jpeg_mem_dest(&cinfo, &outbuffer, &outsize);
 
  677  cinfo.image_width = width;
 
  678  cinfo.image_height = height;
 
  679  WINPR_ASSERT(bpp <= INT32_MAX / 8);
 
  680  cinfo.input_components = (int)(bpp + 7) / 8;
 
  681  cinfo.in_color_space = (bpp > 24) ? JCS_EXT_BGRA : JCS_EXT_BGR;
 
  682  cinfo.data_precision = 8;
 
  684  jpeg_set_defaults(&cinfo);
 
  685  jpeg_set_quality(&cinfo, 100, TRUE);
 
  687  cinfo.comp_info[0].h_samp_factor = cinfo.comp_info[0].v_samp_factor = 1;
 
  689  jpeg_start_compress(&cinfo, TRUE);
 
  691  const JSAMPLE* cdata = data;
 
  692  for (
size_t x = 0; x < height; x++)
 
  694    WINPR_ASSERT(x * stride <= UINT32_MAX);
 
  695    const JDIMENSION offset = (JDIMENSION)x * stride;
 
  699    JSAMPLE* coffset = WINPR_CAST_CONST_PTR_AWAY(&cdata[offset], JSAMPLE*);
 
  700    if (jpeg_write_scanlines(&cinfo, &coffset, 1) != 1)
 
  705  jpeg_finish_compress(&cinfo);
 
  706  jpeg_destroy_compress(&cinfo);
 
  708  WINPR_ASSERT(outsize <= UINT32_MAX);
 
  709  *pSize = (UINT32)outsize;
 
  715SSIZE_T winpr_convert_from_jpeg(WINPR_ATTR_UNUSED 
const BYTE* comp_data,
 
  716                                WINPR_ATTR_UNUSED 
size_t comp_data_bytes,
 
  717                                WINPR_ATTR_UNUSED UINT32* width, WINPR_ATTR_UNUSED UINT32* height,
 
  718                                WINPR_ATTR_UNUSED UINT32* bpp,
 
  719                                WINPR_ATTR_UNUSED BYTE** ppdecomp_data)
 
  722  WINPR_ASSERT(comp_data || (comp_data_bytes == 0));
 
  724  WINPR_ASSERT(height);
 
  726  WINPR_ASSERT(ppdecomp_data);
 
  728#if !defined(WINPR_UTILS_IMAGE_JPEG) 
  729  WLog_WARN(TAG, 
"JPEG not supported in this build");
 
  732  struct jpeg_decompress_struct cinfo = { 0 };
 
  733  struct jpeg_error_mgr jerr;
 
  735  BYTE* decomp_data = NULL;
 
  737  cinfo.err = jpeg_std_error(&jerr);
 
  738  jpeg_create_decompress(&cinfo);
 
  739  jpeg_mem_src(&cinfo, comp_data, comp_data_bytes);
 
  741  if (jpeg_read_header(&cinfo, 1) != JPEG_HEADER_OK)
 
  744  cinfo.out_color_space = cinfo.num_components > 3 ? JCS_EXT_RGBA : JCS_EXT_BGR;
 
  746  *width = WINPR_ASSERTING_INT_CAST(uint32_t, cinfo.image_width);
 
  747  *height = WINPR_ASSERTING_INT_CAST(uint32_t, cinfo.image_height);
 
  748  *bpp = WINPR_ASSERTING_INT_CAST(uint32_t, cinfo.num_components * 8);
 
  750  if (!jpeg_start_decompress(&cinfo))
 
  754      1ULL * cinfo.image_width * WINPR_ASSERTING_INT_CAST(uint32_t, cinfo.num_components);
 
  756  if ((stride == 0) || (cinfo.image_height == 0))
 
  759  decomp_data = calloc(stride, cinfo.image_height);
 
  762    while (cinfo.output_scanline < cinfo.image_height)
 
  764      JSAMPROW row = &decomp_data[cinfo.output_scanline * stride];
 
  765      if (jpeg_read_scanlines(&cinfo, &row, 1) != 1)
 
  768    const size_t ssize = stride * cinfo.image_height;
 
  769    WINPR_ASSERT(ssize < SSIZE_MAX);
 
  770    size = (SSIZE_T)ssize;
 
  772  jpeg_finish_decompress(&cinfo);
 
  775  jpeg_destroy_decompress(&cinfo);
 
  776  *ppdecomp_data = decomp_data;
 
  781static void* winpr_convert_to_webp(WINPR_ATTR_UNUSED 
const void* data,
 
  782                                   WINPR_ATTR_UNUSED 
size_t size, WINPR_ATTR_UNUSED UINT32 width,
 
  783                                   WINPR_ATTR_UNUSED UINT32 height, WINPR_ATTR_UNUSED UINT32 stride,
 
  784                                   WINPR_ATTR_UNUSED UINT32 bpp, UINT32* pSize)
 
  786  WINPR_ASSERT(data || (size == 0));
 
  791#if !defined(WINPR_UTILS_IMAGE_WEBP) 
  792  WLog_WARN(TAG, 
"WEBP not supported in this build");
 
  796  uint8_t* pDstData = NULL;
 
  797  WINPR_ASSERT(width <= INT32_MAX);
 
  798  WINPR_ASSERT(height <= INT32_MAX);
 
  799  WINPR_ASSERT(stride <= INT32_MAX);
 
  803      dstSize = WebPEncodeLosslessBGRA(data, (
int)width, (
int)height, (
int)stride, &pDstData);
 
  806      dstSize = WebPEncodeLosslessBGR(data, (
int)width, (
int)height, (
int)stride, &pDstData);
 
  812  void* rc = malloc(dstSize);
 
  815    memcpy(rc, pDstData, dstSize);
 
  817    WINPR_ASSERT(dstSize <= UINT32_MAX);
 
  818    *pSize = (UINT32)dstSize;
 
  825SSIZE_T winpr_convert_from_webp(WINPR_ATTR_UNUSED 
const BYTE* comp_data,
 
  826                                WINPR_ATTR_UNUSED 
size_t comp_data_bytes, UINT32* width,
 
  827                                UINT32* height, UINT32* bpp, BYTE** ppdecomp_data)
 
  829  WINPR_ASSERT(comp_data || (comp_data_bytes == 0));
 
  831  WINPR_ASSERT(height);
 
  833  WINPR_ASSERT(ppdecomp_data);
 
  838  *ppdecomp_data = NULL;
 
  839#if !defined(WINPR_UTILS_IMAGE_WEBP) 
  840  WLog_WARN(TAG, 
"WEBP not supported in this build");
 
  846  uint8_t* dst = WebPDecodeBGRA(comp_data, comp_data_bytes, &w, &h);
 
  847  if (!dst || (w < 0) || (h < 0))
 
  853  *width = WINPR_ASSERTING_INT_CAST(uint32_t, w);
 
  854  *height = WINPR_ASSERTING_INT_CAST(uint32_t, h);
 
  856  *ppdecomp_data = dst;
 
  861#if defined(WINPR_UTILS_IMAGE_PNG) 
  868static void png_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
 
  871  struct png_mem_encode* p =
 
  872      (
struct png_mem_encode*)png_get_io_ptr(png_ptr); 
 
  873  size_t nsize = p->size + length;
 
  878    char* tmp = realloc(p->buffer, nsize);
 
  883    p->buffer = malloc(nsize);
 
  886    png_error(png_ptr, 
"Write Error");
 
  889  memcpy(p->buffer + p->size, data, length);
 
  894static void png_flush(WINPR_ATTR_UNUSED png_structp png_ptr)
 
  898static SSIZE_T save_png_to_buffer(UINT32 bpp, UINT32 width, uint32_t stride, UINT32 height,
 
  899                                  const uint8_t* data, 
size_t size, 
void** pDstData)
 
  902  png_structp png_ptr = NULL;
 
  903  png_infop info_ptr = NULL;
 
  904  png_byte** row_pointers = NULL;
 
  905  struct png_mem_encode state = { 0 };
 
  909  if (!data || (size == 0))
 
  912  WINPR_ASSERT(pDstData);
 
  914  if (size < (1ULL * stride * height))
 
  918  png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
 
  923  info_ptr = png_create_info_struct(png_ptr);
 
  924  if (info_ptr == NULL)
 
  928  if (setjmp(png_jmpbuf(png_ptr)))
 
  932  int colorType = PNG_COLOR_TYPE_PALETTE;
 
  934    colorType = PNG_COLOR_TYPE_RGB;
 
  936    colorType = PNG_COLOR_TYPE_RGB;
 
  938    colorType = PNG_COLOR_TYPE_RGBA;
 
  940  png_set_IHDR(png_ptr, info_ptr, width, height, 8, colorType, PNG_INTERLACE_NONE,
 
  941               PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
 
  944  row_pointers = (png_byte**)png_malloc(png_ptr, height * 
sizeof(png_byte*));
 
  945  for (
size_t y = 0; y < height; ++y)
 
  947    const uint8_t* line = &data[y * stride];
 
  948    uint8_t* row = png_malloc(png_ptr, 
sizeof(uint8_t) * stride);
 
  949    row_pointers[y] = (png_byte*)row;
 
  950    for (
size_t x = 0; x < width; ++x)
 
  964  png_set_write_fn(png_ptr, &state, png_write_data, png_flush);
 
  965  png_set_rows(png_ptr, info_ptr, row_pointers);
 
  966  png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_BGR, NULL);
 
  969  for (
size_t y = 0; y < height; y++)
 
  970    png_free(png_ptr, row_pointers[y]);
 
  971  png_free(png_ptr, (
void*)row_pointers);
 
  974  if (state.size > SSIZE_MAX)
 
  976  rc = (SSIZE_T)state.size;
 
  977  *pDstData = state.buffer;
 
  979  png_destroy_write_struct(&png_ptr, &info_ptr);
 
  989  png_uint_32 current_pos;
 
  990} MEMORY_READER_STATE;
 
  992static void read_data_memory(png_structp png_ptr, png_bytep data, 
size_t length)
 
  994  MEMORY_READER_STATE* f = png_get_io_ptr(png_ptr);
 
  995  if (length > (f->bufsize - f->current_pos))
 
  996    png_error(png_ptr, 
"read error in read_data_memory (loadpng)");
 
  999    memcpy(data, f->buffer + f->current_pos, length);
 
 1000    f->current_pos += length;
 
 1004static void* winpr_read_png_from_buffer(
const void* data, 
size_t SrcSize, 
size_t* pSize,
 
 1005                                        UINT32* pWidth, UINT32* pHeight, UINT32* pBpp)
 
 1008  png_uint_32 width = 0;
 
 1009  png_uint_32 height = 0;
 
 1012  int interlace_type = 0;
 
 1013  int transforms = PNG_TRANSFORM_IDENTITY;
 
 1014  MEMORY_READER_STATE memory_reader_state = { 0 };
 
 1015  png_bytepp row_pointers = NULL;
 
 1016  png_infop info_ptr = NULL;
 
 1017  if (SrcSize > UINT32_MAX)
 
 1020  png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
 
 1023  info_ptr = png_create_info_struct(png_ptr);
 
 1027  memory_reader_state.buffer = WINPR_CAST_CONST_PTR_AWAY(data, png_bytep);
 
 1028  memory_reader_state.bufsize = (UINT32)SrcSize;
 
 1029  memory_reader_state.current_pos = 0;
 
 1031  png_set_read_fn(png_ptr, &memory_reader_state, read_data_memory);
 
 1033  transforms |= PNG_TRANSFORM_BGR;
 
 1034  png_read_png(png_ptr, info_ptr, transforms, NULL);
 
 1036  if (png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type,
 
 1040  WINPR_ASSERT(bit_depth >= 0);
 
 1041  const png_byte channelcount = png_get_channels(png_ptr, info_ptr);
 
 1042  const size_t bpp = channelcount * (size_t)bit_depth;
 
 1044  row_pointers = png_get_rows(png_ptr, info_ptr);
 
 1047    const size_t stride = 1ULL * width * bpp / 8ull;
 
 1048    const size_t png_stride = png_get_rowbytes(png_ptr, info_ptr);
 
 1049    const size_t size = 1ULL * width * height * bpp / 8ull;
 
 1050    const size_t copybytes = stride > png_stride ? png_stride : stride;
 
 1056      for (png_uint_32 i = 0; i < height; i++)
 
 1058        memcpy(cur, row_pointers[i], copybytes);
 
 1064      WINPR_ASSERT(bpp <= UINT32_MAX);
 
 1065      *pBpp = (UINT32)bpp;
 
 1070  png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
 
 1075static void* winpr_convert_to_png(WINPR_ATTR_UNUSED 
const void* data, WINPR_ATTR_UNUSED 
size_t size,
 
 1076                                  WINPR_ATTR_UNUSED UINT32 width, WINPR_ATTR_UNUSED UINT32 height,
 
 1077                                  WINPR_ATTR_UNUSED UINT32 stride, WINPR_ATTR_UNUSED UINT32 bpp,
 
 1080  WINPR_ASSERT(data || (size == 0));
 
 1081  WINPR_ASSERT(pSize);
 
 1085#if defined(WINPR_UTILS_IMAGE_PNG) 
 1087  SSIZE_T rc = save_png_to_buffer(bpp, width, stride, height, data, size, &dst);
 
 1090  *pSize = (UINT32)rc;
 
 1092#elif defined(WITH_LODEPNG) 
 1101        rc = lodepng_encode32(&dst, &dstsize, data, width, height);
 
 1104        rc = lodepng_encode24(&dst, &dstsize, data, width, height);
 
 1111    *pSize = (UINT32)dstsize;
 
 1115  WLog_WARN(TAG, 
"PNG not supported in this build");
 
 1120SSIZE_T winpr_convert_from_png(WINPR_ATTR_UNUSED 
const BYTE* comp_data,
 
 1121                               WINPR_ATTR_UNUSED 
size_t comp_data_bytes,
 
 1122                               WINPR_ATTR_UNUSED UINT32* width, WINPR_ATTR_UNUSED UINT32* height,
 
 1123                               WINPR_ATTR_UNUSED UINT32* bpp,
 
 1124                               WINPR_ATTR_UNUSED BYTE** ppdecomp_data)
 
 1126#if defined(WINPR_UTILS_IMAGE_PNG) 
 1129      winpr_read_png_from_buffer(comp_data, comp_data_bytes, &len, width, height, bpp);
 
 1130  if (!*ppdecomp_data)
 
 1132  return (SSIZE_T)len;
 
 1133#elif defined(WITH_LODEPNG) 
 1135  return lodepng_decode32((
unsigned char**)ppdecomp_data, width, height, comp_data,
 
 1138  WLog_WARN(TAG, 
"PNG not supported in this build");
 
 1143BOOL winpr_image_format_is_supported(UINT32 format)
 
 1147    case WINPR_IMAGE_BITMAP:
 
 1148#if defined(WINPR_UTILS_IMAGE_PNG) || defined(WITH_LODEPNG) 
 1149    case WINPR_IMAGE_PNG:
 
 1151#if defined(WINPR_UTILS_IMAGE_JPEG) 
 1152    case WINPR_IMAGE_JPEG:
 
 1154#if defined(WINPR_UTILS_IMAGE_WEBP) 
 1155    case WINPR_IMAGE_WEBP:
 
 1163static BYTE* convert(
const wImage* image, 
size_t* pstride, WINPR_ATTR_UNUSED UINT32 flags)
 
 1165  WINPR_ASSERT(image);
 
 1166  WINPR_ASSERT(pstride);
 
 1169  if (image->bitsPerPixel < 24)
 
 1172  const size_t stride = image->width * 4ull;
 
 1173  BYTE* data = calloc(stride, image->height);
 
 1176    for (
size_t y = 0; y < image->height; y++)
 
 1178      const BYTE* srcLine = &image->data[image->scanline * y];
 
 1179      BYTE* dstLine = &data[stride * y];
 
 1180      if (image->bitsPerPixel == 32)
 
 1181        memcpy(dstLine, srcLine, stride);
 
 1184        for (
size_t x = 0; x < image->width; x++)
 
 1186          const BYTE* src = &srcLine[image->bytesPerPixel * x];
 
 1187          BYTE* dst = &dstLine[4ull * x];
 
 1204static BOOL compare_byte_relaxed(BYTE a, BYTE b, UINT32 flags)
 
 1208    if ((flags & WINPR_IMAGE_CMP_FUZZY) != 0)
 
 1210      const int diff = abs((
int)a) - abs((
int)b);
 
 1223static BOOL compare_pixel(
const BYTE* pa, 
const BYTE* pb, UINT32 flags)
 
 1228  if (!compare_byte_relaxed(*pa++, *pb++, flags))
 
 1230  if (!compare_byte_relaxed(*pa++, *pb++, flags))
 
 1232  if (!compare_byte_relaxed(*pa++, *pb++, flags))
 
 1234  if ((flags & WINPR_IMAGE_CMP_IGNORE_ALPHA) == 0)
 
 1236    if (!compare_byte_relaxed(*pa++, *pb++, flags))
 
 1242BOOL winpr_image_equal(
const wImage* imageA, 
const wImage* imageB, UINT32 flags)
 
 1244  if (imageA == imageB)
 
 1246  if (!imageA || !imageB)
 
 1249  if (imageA->height != imageB->height)
 
 1251  if (imageA->width != imageB->width)
 
 1254  if ((flags & WINPR_IMAGE_CMP_IGNORE_DEPTH) == 0)
 
 1256    if (imageA->bitsPerPixel != imageB->bitsPerPixel)
 
 1258    if (imageA->bytesPerPixel != imageB->bytesPerPixel)
 
 1265  BYTE* dataA = convert(imageA, &astride, flags);
 
 1266  BYTE* dataB = convert(imageA, &bstride, flags);
 
 1267  if (dataA && dataB && (astride == bstride))
 
 1270    for (
size_t y = 0; y < imageA->height; y++)
 
 1272      const BYTE* lineA = &dataA[astride * y];
 
 1273      const BYTE* lineB = &dataB[bstride * y];
 
 1275      for (
size_t x = 0; x < imageA->width; x++)
 
 1277        const BYTE* pa = &lineA[x * 4ull];
 
 1278        const BYTE* pb = &lineB[x * 4ull];
 
 1280        if (!compare_pixel(pa, pb, flags))
 
 1290const char* winpr_image_format_mime(UINT32 format)
 
 1294    case WINPR_IMAGE_BITMAP:
 
 1296    case WINPR_IMAGE_PNG:
 
 1298    case WINPR_IMAGE_WEBP:
 
 1299      return "image/webp";
 
 1300    case WINPR_IMAGE_JPEG:
 
 1301      return "image/jpeg";
 
 1307const char* winpr_image_format_extension(UINT32 format)
 
 1311    case WINPR_IMAGE_BITMAP:
 
 1313    case WINPR_IMAGE_PNG:
 
 1315    case WINPR_IMAGE_WEBP:
 
 1317    case WINPR_IMAGE_JPEG:
 
 1324void* winpr_image_write_buffer(
wImage* image, UINT32 format, 
size_t* psize)
 
 1326  WINPR_ASSERT(image);
 
 1329    case WINPR_IMAGE_BITMAP:
 
 1332      size_t size = 1ull * image->height * image->scanline;
 
 1333      void* data = winpr_bitmap_write_buffer(image->data, size, image->width, image->height,
 
 1334                                             image->scanline, image->bitsPerPixel, &outsize);
 
 1338    case WINPR_IMAGE_WEBP:
 
 1341      size_t size = 1ull * image->height * image->scanline;
 
 1342      void* data = winpr_convert_to_webp(image->data, size, image->width, image->height,
 
 1343                                         image->scanline, image->bitsPerPixel, &outsize);
 
 1347    case WINPR_IMAGE_JPEG:
 
 1350      size_t size = 1ull * image->height * image->scanline;
 
 1351      void* data = winpr_convert_to_jpeg(image->data, size, image->width, image->height,
 
 1352                                         image->scanline, image->bitsPerPixel, &outsize);
 
 1356    case WINPR_IMAGE_PNG:
 
 1359      size_t size = 1ull * image->height * image->scanline;
 
 1360      void* data = winpr_convert_to_png(image->data, size, image->width, image->height,
 
 1361                                        image->scanline, image->bitsPerPixel, &outsize);