Coverage Report

Created: 2025-08-26 06:34

/src/libwebsockets/lib/misc/upng.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * LWS PNG -- derived from uPNG -- derived from LodePNG version 20100808
3
 * Stateful, linewise PNG decode requiring ~36KB fixed heap
4
 *
5
 * Copyright (c) 2005-2010 Lode Vandevenne (LodePNG)
6
 * Copyright (c) 2010 Sean Middleditch (uPNG)
7
 * Copyright (c) 2021 Andy Green <andy@warmcat.com> (Stateful, incremental)
8
 *
9
 * This software is provided 'as-is', without any express or implied
10
 * warranty. In no event will the authors be held liable for any damages
11
 * arising from the use of this software.
12
13
 * Permission is granted to anyone to use this software for any purpose,
14
 * including commercial applications, and to alter it and redistribute it
15
 * freely, subject to the following restrictions:
16
 *
17
 *   1. The origin of this software must not be misrepresented; you must not
18
 *      claim that you wrote the original software. If you use this software
19
 *      in a product, an acknowledgment in the product documentation would be
20
 *  appreciated but is not required.
21
 *
22
 *   2. Altered source versions must be plainly marked as such, and must not be
23
 *  misrepresented as being the original software.
24
 *
25
 *   3. This notice may not be removed or altered from any source
26
 *  distribution.
27
 *
28
 *  AG: The above notice is the ZLIB license, libpng also uses it.
29
 *
30
 * This version was rewritten from the upng project's fork of lodepng and
31
 * adapted to be a stateful stream parser.  This rewrite retains the ZLIB
32
 * license of the source material for simplicity.
33
 *
34
 * That allows it to use a fixed 32KB ringbuffer to hold decodes, and
35
 * incrementally decode chunks into it as we want output lines that are not yet
36
 * present there.  The input png nor the output bitmap need to be all in one
37
 * place at one time.
38
 */
39
40
#include <private-lib-core.h>
41
42
#include <stdio.h>
43
#include <stdlib.h>
44
#include <string.h>
45
#include <limits.h>
46
47
typedef enum upng_color {
48
  LWS_UPNG_LUM    = 0,
49
  LWS_UPNG_RGB    = 2,
50
  LWS_UPNG_LUMA   = 4,
51
  LWS_UPNG_RGBA   = 6
52
} upng_color;
53
54
struct upng_unfline {
55
  uint8_t     *recon;
56
  const uint8_t   *scanline;
57
  const uint8_t   *precon;
58
  uint8_t     filterType;
59
  unsigned int    bypp;
60
  unsigned int    bypl;
61
62
  const uint8_t   *in;
63
  uint8_t     *lines;
64
  unsigned int    bpp;
65
66
  unsigned int    y;
67
  unsigned long   diff;
68
  unsigned long   ibp;
69
  unsigned long   sp;
70
71
  char      padded;
72
  char      alt;
73
};
74
75
typedef enum {
76
  UOF_MAGIC,
77
  UOF_SKIP,
78
  UOF_TYPE4,
79
  UOF_WIDTH4,
80
  UOF_HEIGHT4,
81
  UOF_CDEPTH,
82
  UOF_CTYPE,
83
  UOF_ONLY_ZERO3,
84
  UOF_SKIP4,
85
86
  UOF_CHUNK_LEN,
87
  UOF_CHUNK_TYPE,
88
  UOF_INSIDE,
89
90
  UOF_SKIP_CHUNK_LEN,
91
} upng_outer_framing_t;
92
93
94
95
struct lws_upng_t {
96
  struct upng_unfline u;
97
  inflator_ctx_t    inf;
98
99
  unsigned int    width;
100
  unsigned int    height;
101
102
  upng_color    color_type;
103
  unsigned int    color_depth;
104
  lws_upng_format_t format;
105
106
  const uint8_t   *chunk;
107
108
  int     sctr;
109
  uint32_t    acc;
110
111
  uint32_t    chunklen;
112
  uint32_t    ctype;
113
114
  upng_outer_framing_t  of;
115
116
  uint8_t     no_more_input;
117
  char      hold_at_metadata;
118
};
119
120
static lws_stateful_ret_t
121
lws_upng_decode(lws_upng_t *upng, const uint8_t **buf, size_t *size);
122
123
static int
124
paeth(int a, int b, int c)
125
0
{
126
0
  int p = a + b - c;
127
0
  int pa = p > a ? p - a : a - p;
128
0
  int pb = p > b ? p - b : b - p;
129
0
  int pc = p > c ? p - c : c - p;
130
131
0
  if (pa <= pb && pa <= pc)
132
0
    return a;
133
134
0
  if (pb <= pc)
135
0
    return b;
136
137
0
  return c;
138
0
}
139
140
static lws_stateful_ret_t
141
unfilter_scanline(lws_upng_t *u)
142
0
{
143
0
  struct upng_unfline *uf = &u->u;
144
0
  unsigned long i;
145
146
0
  switch (uf->filterType) {
147
0
  case 0: /* None */
148
0
    for (i = 0; i < uf->bypl; i++)
149
0
      uf->recon[i] = u->inf.out[(uf->sp + i) %
150
0
                                  u->inf.info_size];
151
0
    break;
152
0
  case 1: /* Sub */
153
0
    for (i = 0; i <  uf->bypp; i++)
154
0
      uf->recon[i] = u->inf.out[(uf->sp + i) %
155
0
                                  u->inf.info_size];
156
157
0
    for (i = uf->bypp; i < uf->bypl; i++)
158
0
      uf->recon[i] = (uint8_t)(u->inf.out[(uf->sp + i) %
159
0
                                  u->inf.info_size] +
160
0
        uf->recon[i - uf->bypp]);
161
0
    break;
162
0
  case 2: /* Up */
163
0
    if (uf->y)
164
0
      for (i = 0; i < uf->bypl; i++)
165
0
        uf->recon[i] = (uint8_t)(u->inf.out[(uf->sp + i) %
166
0
                    u->inf.info_size] + uf->precon[i]);
167
0
    else
168
0
      for (i = 0; i < uf->bypl; i++)
169
0
        uf->recon[i] = (uint8_t)(u->inf.out[(uf->sp + i) %
170
0
                                  u->inf.info_size]);
171
0
    break;
172
0
  case 3: /* Average */
173
0
    if (uf->y) {
174
0
      for (i = 0; i < uf->bypp; i++)
175
0
        uf->recon[i] = (uint8_t)(u->inf.out[(uf->sp + i) %
176
0
                u->inf.info_size] + uf->precon[i] / 2);
177
0
      for (i = uf->bypp; i < uf->bypl; i++)
178
0
        uf->recon[i] = (uint8_t)
179
0
          (u->inf.out[(uf->sp + i) %
180
0
                      u->inf.info_size] +
181
0
          ((uf->recon[i - uf->bypp] +
182
0
              uf->precon[i]) / 2));
183
0
    } else {
184
0
      for (i = 0; i < uf->bypp; i++)
185
0
        uf->recon[i] = (uint8_t)(u->inf.out[(uf->sp + i) %
186
0
                                 u->inf.info_size]);
187
0
      for (i = uf->bypp; i < uf->bypl; i++)
188
0
        uf->recon[i] = (uint8_t)(u->inf.out[(uf->sp + i) %
189
0
                                 u->inf.info_size] +
190
0
          uf->recon[i - uf->bypp] / 2);
191
0
    }
192
0
    break;
193
0
  case 4: /* Paeth */
194
0
    if (uf->y) {
195
0
      for (i = 0; i < uf->bypp; i++)
196
0
        uf->recon[i] = (uint8_t)(u->inf.out[(uf->sp + i) %
197
0
                                  u->inf.info_size] +
198
0
          paeth(0, uf->precon[i], 0));
199
0
      for (i = uf->bypp; i < uf->bypl; i++)
200
0
        uf->recon[i] = (uint8_t)(u->inf.out[(uf->sp + i) %
201
0
                                  u->inf.info_size] +
202
0
          paeth(uf->recon[i - uf->bypp],
203
0
              uf->precon[i],
204
0
              uf->precon[i - uf->bypp]));
205
0
      break;
206
0
    }
207
208
0
    for (i = 0; i < uf->bypp; i++)
209
0
      uf->recon[i] = (uint8_t)(u->inf.out[(uf->sp + i) %
210
0
                                  u->inf.info_size]);
211
0
    for (i = uf->bypp; i < uf->bypl; i++)
212
0
      uf->recon[i] = (uint8_t)(u->inf.out[(uf->sp + i) %
213
0
                                      u->inf.info_size] +
214
0
        paeth(uf->recon[i - uf->bypp], 0, 0));
215
0
    break;
216
0
  default:
217
0
    lwsl_err("%s: line start is broken %d\n", __func__,
218
0
        uf->filterType);
219
0
    return LWS_SRET_FATAL + 12;
220
0
  }
221
222
0
  u->inf.consumed_linear += uf->bypl;
223
224
0
  return LWS_SRET_OK;
225
0
}
226
227
lws_stateful_ret_t
228
lws_upng_emit_next_line(lws_upng_t *u, const uint8_t **ppix,
229
        const uint8_t **pos, size_t *size, char hold_at_metadata)
230
0
{
231
0
  struct upng_unfline *uf = &u->u;
232
0
  unsigned long   obp;
233
0
  lws_stateful_ret_t  ret = LWS_SRET_OK;
234
235
0
  *ppix = NULL;
236
237
0
  u->hold_at_metadata = hold_at_metadata;
238
239
0
  if (u->height && uf->y >= u->height)
240
0
    goto out;
241
242
  /*
243
   * The decoder emits into the 32KB window ringbuffer, if we don't
244
   * already have at least one line's worth of output in there, we'll
245
   * have to do more inflation
246
   */
247
248
0
  if (u->inf.outpos_linear - u->inf.consumed_linear < uf->bypl + 1) {
249
0
    ret = lws_upng_decode(u, pos, size);
250
0
    if ((!*size && ret == LWS_SRET_WANT_INPUT) ||
251
0
        (ret & (LWS_SRET_FATAL | LWS_SRET_YIELD)) ||
252
0
        !u->inf.outpos_linear)
253
0
      return ret;
254
255
0
    assert(u->inf.info_size);
256
0
    assert(uf->bypl + 1);
257
0
  }
258
259
0
  if (u->inf.outpos_linear - u->inf.consumed_linear < uf->bypl + 1)
260
0
    return ret;
261
262
0
  obp   = uf->alt ? uf->bypl : 0;
263
0
  uf->precon  = uf->alt ? uf->lines : uf->lines + uf->bypl;
264
0
  uf->recon = &uf->lines[obp];
265
0
  *ppix   = uf->recon;
266
0
  uf->filterType  = uf->in[(u->inf.consumed_linear++) % u->inf.info_size];
267
0
  uf->sp    = u->inf.consumed_linear % u->inf.info_size;
268
269
0
  if (unfilter_scanline(u) != LWS_SRET_OK) {
270
0
    ret = LWS_SRET_FATAL + 13;
271
272
0
    goto out;
273
0
  }
274
275
0
  if (uf->padded) {
276
0
    unsigned long x;
277
278
0
    for (x = 0; x < (unsigned long)u->width * (unsigned long)uf->bpp; x++) {
279
0
      uint8_t bit = (uint8_t)((uf->in[(uf->ibp) >> 3] >>
280
0
            (7 - ((uf->ibp) & 7))) & 1);
281
0
      uf->ibp++;
282
283
0
      if (!bit)
284
0
        uf->lines[obp >> 3] &=
285
0
          (uint8_t)(~(1 << (7 - (obp & 7))));
286
0
      else
287
0
        uf->lines[obp >> 3] = (uint8_t)(uf->lines[obp >> 3] |
288
0
            (uint8_t)(1 << (7 - (obp & 7))));
289
290
0
      obp++;
291
0
    }
292
293
0
    uf->ibp += uf->diff;
294
0
  }
295
296
0
out:
297
0
  uf->alt ^= 1;
298
0
  uf->y++;
299
300
0
  return ret;
301
0
}
302
303
static lws_upng_format_t
304
0
determine_format(lws_upng_t* upng) {
305
0
  switch (upng->color_type) {
306
0
  case LWS_UPNG_LUM:
307
0
    switch (upng->color_depth) {
308
0
    case 1:
309
0
      return LWS_UPNG_LUMINANCE1;
310
0
    case 2:
311
0
      return LWS_UPNG_LUMINANCE2;
312
0
    case 4:
313
0
      return LWS_UPNG_LUMINANCE4;
314
0
    case 8:
315
0
      return LWS_UPNG_LUMINANCE8;
316
0
    default:
317
0
      return LWS_UPNG_BADFORMAT;
318
0
    }
319
0
  case LWS_UPNG_RGB:
320
0
    switch (upng->color_depth) {
321
0
    case 8:
322
0
      return LWS_UPNG_RGB8;
323
0
    case 16:
324
0
      return LWS_UPNG_RGB16;
325
0
    default:
326
0
      return LWS_UPNG_BADFORMAT;
327
0
    }
328
0
  case LWS_UPNG_LUMA:
329
0
    switch (upng->color_depth) {
330
0
    case 1:
331
0
      return LWS_UPNG_LUMINANCE_ALPHA1;
332
0
    case 2:
333
0
      return LWS_UPNG_LUMINANCE_ALPHA2;
334
0
    case 4:
335
0
      return LWS_UPNG_LUMINANCE_ALPHA4;
336
0
    case 8:
337
0
      return LWS_UPNG_LUMINANCE_ALPHA8;
338
0
    default:
339
0
      return LWS_UPNG_BADFORMAT;
340
0
    }
341
0
  case LWS_UPNG_RGBA:
342
0
    switch (upng->color_depth) {
343
0
    case 8:
344
0
      return LWS_UPNG_RGBA8;
345
0
    case 16:
346
0
      return LWS_UPNG_RGBA16;
347
0
    default:
348
0
      return LWS_UPNG_BADFORMAT;
349
0
    }
350
0
  default:
351
0
    return LWS_UPNG_BADFORMAT;
352
0
  }
353
0
}
354
355
static const uint8_t magic[] = { 137, 80, 78, 71, 13, 10, 26, 10 };
356
357
static lws_stateful_ret_t
358
lws_upng_decode(lws_upng_t* u, const uint8_t **_pos, size_t *_size)
359
0
{
360
0
  const uint8_t *pos = _pos ? *_pos : NULL, *end = pos + *_size;
361
0
  lws_stateful_ret_t r = LWS_SRET_FATAL + 60;
362
0
  size_t m;
363
364
0
  if (u->of == UOF_INSIDE && !u->inf.in) {
365
0
    u->inf.inpos = 0;
366
0
    u->inf.in = pos;
367
0
    u->inf.bp = 0;
368
0
    m = lws_ptr_diff_size_t(end, pos);
369
0
    if (m > u->chunklen)
370
0
      m = u->chunklen;
371
0
    u->inf.inlen = m;
372
0
  }
373
374
0
  while (!u->no_more_input &&
375
0
         ((u->of == UOF_INSIDE && _pos == NULL) || pos < end)) {
376
0
    switch (u->of) {
377
0
    case UOF_MAGIC:
378
0
      if (*pos++ != magic[u->sctr++])
379
0
        return LWS_SRET_FATAL + 17;
380
0
      if (u->sctr == sizeof(magic)) {
381
0
        u->of++;
382
0
        u->sctr = 0;
383
0
      }
384
0
      break;
385
386
0
    case UOF_SKIP:
387
0
      pos++;
388
0
      if (++u->sctr == 4) {
389
0
        u->of++;
390
0
        u->sctr = 0;
391
0
      }
392
0
      break;
393
394
0
    case UOF_TYPE4:
395
0
      u->acc = (u->acc << 8) | *pos++;
396
0
      if (++u->sctr == 4) {
397
0
        if (u->acc != LWS_FOURCC('I','H','D','R'))
398
0
          return LWS_SRET_FATAL + 18;
399
0
        u->of++;
400
0
        u->sctr = 0;
401
0
      }
402
0
      break;
403
404
0
    case UOF_WIDTH4:
405
0
      u->acc = (u->acc << 8) | *pos++;
406
0
      if (++u->sctr == 4) {
407
0
        u->width = u->acc;
408
0
        if (!u->acc)
409
0
          return LWS_SRET_FATAL + 18;
410
0
        u->of++;
411
0
        u->sctr = 0;
412
0
      }
413
0
      break;
414
415
0
    case UOF_HEIGHT4:
416
0
      u->acc = (u->acc << 8) | *pos++;
417
0
      if (++u->sctr == 4) {
418
0
        u->height = u->acc;
419
0
        u->of++;
420
0
        u->sctr = 0;
421
0
      }
422
0
      break;
423
424
0
    case UOF_CDEPTH:
425
0
      u->color_depth =*pos++;
426
0
      u->of++;
427
0
      break;
428
429
0
    case UOF_CTYPE:
430
0
      u->color_type = *pos++;
431
      //lwsl_notice("w %d, h %d, depth %d, type %d\n",
432
      //    u->width, u->height,
433
      //    u->color_depth, u->color_type);
434
0
      u->format = determine_format(u);
435
0
      if (u->format == LWS_UPNG_BADFORMAT)
436
0
        return LWS_SRET_FATAL + 19;
437
0
      u->of++;
438
0
      break;
439
440
0
    case UOF_ONLY_ZERO3:
441
0
      if (*pos++)
442
0
        return LWS_SRET_FATAL + 20;
443
0
      if (++u->sctr == 3) {
444
0
        u->of++;
445
0
        u->sctr = 0;
446
0
      }
447
0
      break;
448
449
0
    case UOF_SKIP4:
450
0
      pos++;
451
0
      if (++u->sctr != 4)
452
0
        break;
453
454
      /* takes us to +33 */
455
456
0
      memset(&u->inf, 0, sizeof(u->inf));
457
458
      /* 32KB gz sliding window */
459
0
      u->inf.info_size = 32768 + 512;
460
0
      u->u.bpp   = lws_upng_get_bpp(u);
461
0
      if (!u->u.bpp)
462
0
        return LWS_SRET_FATAL + 14;
463
464
0
      u->u.y    = 0;
465
0
      u->u.ibp  = 0;
466
0
      u->u.bypp = (u->u.bpp + 7) / 8;
467
0
      u->inf.bypl = u->u.bypl = u->width * u->u.bypp;
468
469
0
      u->inf.outlen = u->inf.info_size;
470
0
      u->inf.outpos = 0;
471
0
      u->inf.state  = UPNS_ID_BL_GB_DONE;
472
0
      u->inf.upng = u;
473
474
0
      u->u.alt  = 0; /* which of the two lines to write to */
475
0
      u->u.padded = u->u.bpp < 8 &&
476
0
            u->width * u->u.bpp !=
477
0
            ((u->width * u->u.bpp + 7) / 8) * 8;
478
0
      u->u.diff = (((u->width * u->u.bpp + 7) / 8) * 8) -
479
0
            (u->width * u->u.bpp);
480
481
0
      u->of++;
482
0
      u->sctr = 0;
483
0
      break;
484
485
0
    case UOF_CHUNK_LEN:
486
0
      if (!u->inf.out) {
487
0
        size_t ims = (u->u.bypl * 2) + u->inf.info_size;
488
489
0
        if (u->u.bypl > UINT_MAX / 2 || u->inf.info_size > UINT_MAX - (u->u.bypl * 2)) {
490
0
          lwsl_err("%s: integer overflow occur in ims %llu",
491
0
             __func__, (unsigned long long)ims);
492
0
          return LWS_SRET_FATAL + 27;
493
0
        }
494
495
0
        if (u->hold_at_metadata)
496
0
          return LWS_SRET_AWAIT_RETRY;
497
498
0
        u->inf.out = (uint8_t *)lws_malloc(ims, __func__);
499
0
        if (!u->inf.out) {
500
0
          lwsl_notice("%s: inf malloc %u OOM\n",
501
0
            __func__, (unsigned int)ims);
502
503
0
          return LWS_SRET_YIELD;
504
0
        }
505
0
        u->u.lines = u->inf.out + u->inf.info_size;
506
0
        u->u.in   = u->inf.out;
507
0
      }
508
0
      u->chunklen = (u->chunklen << 8) | *pos++;
509
0
      if (++u->sctr == 4) {
510
0
        u->of++;
511
0
        u->sctr = 0;
512
0
      }
513
0
      break;
514
515
0
    case UOF_CHUNK_TYPE:
516
0
      u->ctype = (u->ctype << 8) | *pos++;
517
0
      if (++u->sctr != 4)
518
0
        break;
519
0
      u->sctr = 0;
520
0
      if (u->ctype == LWS_FOURCC('I','E','N','D')) {
521
0
        u->no_more_input = 1;
522
0
        break;
523
0
      }
524
0
      if (u->ctype != LWS_FOURCC('I','D','A','T')) {
525
0
        if (!(u->ctype & (32 << 24)))
526
          /* says it is critical... */
527
0
           return LWS_SRET_FATAL + 27;
528
529
0
        u->chunklen += 4; /* chunk-end CRC */
530
531
        /* noncritical, skip */
532
0
        u->of = UOF_SKIP_CHUNK_LEN;
533
0
        break;
534
0
      }
535
536
0
      if (u->chunklen < 2)
537
0
        return LWS_SRET_FATAL + 31;
538
539
      /* it's a usable IDAT */
540
541
0
      if (!u->inf.subsequent)
542
0
        u->inf.inpos = 2;
543
0
      else
544
0
        u->inf.inpos = 0;
545
546
0
      m = lws_ptr_diff_size_t(end, pos);
547
0
      if (m > u->chunklen)
548
0
        m = u->chunklen;
549
550
0
      u->inf.in = pos;
551
0
      u->inf.inlen = m;
552
0
      u->inf.bp = 0;
553
0
      u->of++;
554
0
      break;
555
556
0
    case UOF_INSIDE:
557
0
      if (!u->inf.subsequent) {
558
559
0
        switch (u->sctr) {
560
0
        case 0:
561
0
          u->acc = (uint32_t)((*pos++) << 8);
562
0
          u->sctr++;
563
0
          continue;
564
565
0
        case 1:
566
0
          u->acc |= *pos++;
567
568
0
          if (u->acc % 31)
569
0
            return LWS_SRET_FATAL + 31;
570
571
0
          if (((u->acc >> 8) & 15) != 8 ||
572
0
              ((u->acc >> 12) & 15) > 7)
573
0
            return LWS_SRET_FATAL + 31;
574
575
0
          if ((u->acc >> 5) & 1)
576
0
            return LWS_SRET_FATAL + 31;
577
578
0
          u->inf.subsequent = 1;
579
0
          break;
580
0
        }
581
0
      }
582
583
0
      r = _lws_upng_inflate_data(&u->inf);
584
0
      switch (r) {
585
586
0
      case LWS_SRET_WANT_INPUT:
587
588
        /* indicate no existing to drain */
589
0
        u->inf.in = NULL;
590
591
0
        pos += u->inf.inlen - u->inf.inpos;
592
0
        u->chunklen = u->chunklen -
593
0
            (unsigned int)(u->inf.inlen);
594
595
0
        if (!u->chunklen) {
596
0
          u->chunklen = 4; /* skip the 32-bit CRC */
597
598
0
          u->of = UOF_SKIP_CHUNK_LEN;
599
0
          break;
600
0
        }
601
0
        if (pos != end) {
602
0
          u->inf.inpos = 0;
603
0
          u->inf.in = pos;
604
0
          m = lws_ptr_diff_size_t(end, pos);
605
0
          if (m > u->chunklen)
606
0
            m = u->chunklen;
607
0
          u->inf.inlen = m;
608
0
          continue;
609
0
        }
610
0
        goto bail;
611
0
      default:
612
0
        goto bail;
613
0
      }
614
0
      break;
615
616
0
    case UOF_SKIP_CHUNK_LEN:
617
0
      pos++;
618
0
      if (!--u->chunklen) {
619
0
        u->of = UOF_CHUNK_LEN;
620
0
        u->sctr = 0;
621
0
        break;
622
0
      }
623
0
      break;
624
0
    }
625
0
  }
626
627
0
  r = LWS_SRET_OK;
628
0
  if (!u->no_more_input)
629
0
    r = LWS_SRET_WANT_INPUT;
630
631
0
bail:
632
0
  *_pos = pos;
633
0
  *_size = lws_ptr_diff_size_t(end, pos);
634
635
0
  return r;
636
0
}
637
638
lws_upng_t *
639
lws_upng_new(void)
640
0
{
641
0
  lws_upng_t* upng;
642
643
0
  upng = (lws_upng_t*)lws_zalloc(sizeof(lws_upng_t), __func__);
644
0
  if (upng == NULL)
645
0
    return NULL;
646
647
0
  upng->color_type = LWS_UPNG_RGBA;
648
0
  upng->color_depth = 8;
649
0
  upng->format = LWS_UPNG_RGBA8;
650
651
0
  upng->of = UOF_MAGIC;
652
0
  upng->sctr = 0;
653
654
0
  upng->inf.upng = upng;
655
656
0
  return upng;
657
0
}
658
659
void
660
lws_upng_free(lws_upng_t** upng)
661
0
{
662
0
  if ((*upng)->inf.out)
663
0
    lws_free_set_NULL((*upng)->inf.out);
664
665
0
  lws_free_set_NULL(*upng);
666
0
}
667
668
669
unsigned int
670
lws_upng_get_width(const lws_upng_t* upng)
671
0
{
672
0
  return upng->width;
673
0
}
674
675
unsigned int
676
lws_upng_get_height(const lws_upng_t* upng)
677
0
{
678
0
  return upng->height;
679
0
}
680
681
unsigned int
682
lws_upng_get_bpp(const lws_upng_t* upng)
683
0
{
684
0
  return lws_upng_get_bitdepth(upng) *
685
0
      lws_upng_get_components(upng);
686
0
}
687
688
unsigned int
689
lws_upng_get_components(const lws_upng_t* upng)
690
0
{
691
0
  switch (upng->color_type) {
692
0
  case LWS_UPNG_LUM:
693
0
    return 1;
694
0
  case LWS_UPNG_RGB:
695
0
    return 3;
696
0
  case LWS_UPNG_LUMA:
697
0
    return 2;
698
0
  case LWS_UPNG_RGBA:
699
0
    return 4;
700
0
  default:
701
0
    return 0;
702
0
  }
703
0
}
704
705
unsigned int
706
lws_upng_get_bitdepth(const lws_upng_t* upng)
707
0
{
708
0
  return upng->color_depth;
709
0
}
710
711
unsigned int
712
lws_upng_get_pixelsize(const lws_upng_t* upng)
713
0
{
714
0
  unsigned bits = lws_upng_get_bitdepth(upng) *
715
0
        lws_upng_get_components(upng);
716
717
0
  bits += bits % 8;
718
719
0
  return bits;
720
0
}
721
722
lws_upng_format_t
723
lws_upng_get_format(const lws_upng_t *upng)
724
0
{
725
0
  return upng->format;
726
0
}