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