Coverage Report

Created: 2026-07-16 07:09

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