Coverage Report

Created: 2025-07-01 06:46

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