Coverage Report

Created: 2024-09-08 06:20

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