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