Coverage Report

Created: 2026-02-14 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mupdf/source/fitz/load-png.c
Line
Count
Source
1
// Copyright (C) 2004-2021 Artifex Software, Inc.
2
//
3
// This file is part of MuPDF.
4
//
5
// MuPDF is free software: you can redistribute it and/or modify it under the
6
// terms of the GNU Affero General Public License as published by the Free
7
// Software Foundation, either version 3 of the License, or (at your option)
8
// any later version.
9
//
10
// MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13
// details.
14
//
15
// You should have received a copy of the GNU Affero General Public License
16
// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17
//
18
// Alternative licensing terms are available from the licensor.
19
// For commercial licensing, see <https://www.artifex.com/> or contact
20
// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21
// CA 94129, USA, for further information.
22
23
#include "mupdf/fitz.h"
24
25
#include "pixmap-imp.h"
26
#include "z-imp.h"
27
28
#include <limits.h>
29
#include <string.h>
30
31
struct info
32
{
33
  unsigned int width, height, depth, n;
34
  enum fz_colorspace_type type;
35
  int interlace, indexed;
36
  size_t size;
37
  unsigned char *samples;
38
  unsigned char palette[256*4];
39
  int transparency;
40
  int trns[3];
41
  int xres, yres;
42
  fz_colorspace *cs;
43
};
44
45
static inline int getcomp(const unsigned char *line, int x, int bpc)
46
0
{
47
0
  switch (bpc)
48
0
  {
49
0
  case 1: return (line[x >> 3] >> ( 7 - (x & 7) ) ) & 1;
50
0
  case 2: return (line[x >> 2] >> ( ( 3 - (x & 3) ) << 1 ) ) & 3;
51
0
  case 4: return (line[x >> 1] >> ( ( 1 - (x & 1) ) << 2 ) ) & 15;
52
0
  case 8: return line[x];
53
0
  case 16: return line[x << 1] << 8 | line[(x << 1) + 1];
54
0
  }
55
0
  return 0;
56
0
}
57
58
static inline void putcomp(unsigned char *line, int x, int bpc, int value)
59
0
{
60
0
  int maxval = (1 << bpc) - 1;
61
62
0
  switch (bpc)
63
0
  {
64
0
  case 1: line[x >> 3] &= ~(maxval << (7 - (x & 7))); break;
65
0
  case 2: line[x >> 2] &= ~(maxval << ((3 - (x & 3)) << 1)); break;
66
0
  case 4: line[x >> 1] &= ~(maxval << ((1 - (x & 1)) << 2)); break;
67
0
  }
68
69
0
  switch (bpc)
70
0
  {
71
0
  case 1: line[x >> 3] |= value << (7 - (x & 7)); break;
72
0
  case 2: line[x >> 2] |= value << ((3 - (x & 3)) << 1); break;
73
0
  case 4: line[x >> 1] |= value << ((1 - (x & 1)) << 2); break;
74
0
  case 8: line[x] = value; break;
75
0
  case 16: line[x << 1] = value >> 8; line[(x << 1) + 1] = value & 0xFF; break;
76
0
  }
77
0
}
78
79
static const unsigned char png_signature[8] =
80
{
81
  137, 80, 78, 71, 13, 10, 26, 10
82
};
83
84
static inline int paeth(int a, int b, int c)
85
0
{
86
  /* The definitions of ac and bc are correct, not a typo. */
87
0
  int ac = b - c, bc = a - c, abcc = ac + bc;
88
0
  int pa = (ac < 0 ? -ac : ac);
89
0
  int pb = (bc < 0 ? -bc : bc);
90
0
  int pc = (abcc < 0 ? -abcc : abcc);
91
0
  return pa <= pb && pa <= pc ? a : pb <= pc ? b : c;
92
0
}
93
94
static void
95
png_predict(unsigned char *samples, unsigned int width, unsigned int height, unsigned int n, unsigned int depth)
96
0
{
97
0
  unsigned int stride = (width * n * depth + 7) / 8;
98
0
  unsigned int bpp = (n * depth + 7) / 8;
99
0
  unsigned int i, row;
100
101
0
  for (row = 0; row < height; row ++)
102
0
  {
103
0
    unsigned char *src = samples + (unsigned int)((stride + 1) * row);
104
0
    unsigned char *dst = samples + (unsigned int)(stride * row);
105
106
0
    unsigned char *a = dst;
107
0
    unsigned char *b = dst - stride;
108
0
    unsigned char *c = dst - stride;
109
110
0
    switch (*src++)
111
0
    {
112
0
    default:
113
0
    case 0: /* None */
114
0
      for (i = 0; i < stride; i++)
115
0
        *dst++ = *src++;
116
0
      break;
117
118
0
    case 1: /* Sub */
119
0
      for (i = 0; i < bpp; i++)
120
0
        *dst++ = *src++;
121
0
      for (i = bpp; i < stride; i++)
122
0
        *dst++ = *src++ + *a++;
123
0
      break;
124
125
0
    case 2: /* Up */
126
0
      if (row == 0)
127
0
        for (i = 0; i < stride; i++)
128
0
          *dst++ = *src++;
129
0
      else
130
0
        for (i = 0; i < stride; i++)
131
0
          *dst++ = *src++ + *b++;
132
0
      break;
133
134
0
    case 3: /* Average */
135
0
      if (row == 0)
136
0
      {
137
0
        for (i = 0; i < bpp; i++)
138
0
          *dst++ = *src++;
139
0
        for (i = bpp; i < stride; i++)
140
0
          *dst++ = *src++ + (*a++ >> 1);
141
0
      }
142
0
      else
143
0
      {
144
0
        for (i = 0; i < bpp; i++)
145
0
          *dst++ = *src++ + (*b++ >> 1);
146
0
        for (i = bpp; i < stride; i++)
147
0
          *dst++ = *src++ + ((*b++ + *a++) >> 1);
148
0
      }
149
0
      break;
150
151
0
    case 4: /* Paeth */
152
0
      if (row == 0)
153
0
      {
154
0
        for (i = 0; i < bpp; i++)
155
0
          *dst++ = *src++ + paeth(0, 0, 0);
156
0
        for (i = bpp; i < stride; i++)
157
0
          *dst++ = *src++ + paeth(*a++, 0, 0);
158
0
      }
159
0
      else
160
0
      {
161
0
        for (i = 0; i < bpp; i++)
162
0
          *dst++ = *src++ + paeth(0, *b++, 0);
163
0
        for (i = bpp; i < stride; i++)
164
0
          *dst++ = *src++ + paeth(*a++, *b++, *c++);
165
0
      }
166
0
      break;
167
0
    }
168
0
  }
169
0
}
170
171
static const unsigned int adam7_ix[7] = { 0, 4, 0, 2, 0, 1, 0 };
172
static const unsigned int adam7_dx[7] = { 8, 8, 4, 4, 2, 2, 1 };
173
static const unsigned int adam7_iy[7] = { 0, 0, 4, 0, 2, 0, 1 };
174
static const unsigned int adam7_dy[7] = { 8, 8, 8, 4, 4, 2, 2 };
175
176
static void
177
png_deinterlace_passes(fz_context *ctx, struct info *info, unsigned int *w, unsigned int *h, unsigned int *ofs)
178
0
{
179
0
  int p, bpp = info->depth * info->n;
180
0
  ofs[0] = 0;
181
0
  for (p = 0; p < 7; p++)
182
0
  {
183
0
    w[p] = (info->width + adam7_dx[p] - adam7_ix[p] - 1) / adam7_dx[p];
184
0
    h[p] = (info->height + adam7_dy[p] - adam7_iy[p] - 1) / adam7_dy[p];
185
0
    if (w[p] == 0) h[p] = 0;
186
0
    if (h[p] == 0) w[p] = 0;
187
0
    if (w[p] && h[p])
188
0
      ofs[p + 1] = ofs[p] + h[p] * (1 + (w[p] * bpp + 7) / 8);
189
0
    else
190
0
      ofs[p + 1] = ofs[p];
191
0
  }
192
0
}
193
194
static void
195
png_deinterlace(fz_context *ctx, struct info *info, unsigned int *passw, unsigned int *passh, unsigned int *passofs)
196
0
{
197
0
  unsigned int n = info->n;
198
0
  unsigned int depth = info->depth;
199
0
  size_t stride = ((size_t)info->width * n * depth + 7) / 8;
200
0
  unsigned char *output;
201
0
  unsigned int p, x, y, k;
202
203
0
  if (info->height > UINT_MAX / stride)
204
0
    fz_throw(ctx, FZ_ERROR_LIMIT, "image too large");
205
0
  output = Memento_label(fz_malloc(ctx, info->height * stride), "png_deinterlace");
206
207
0
  for (p = 0; p < 7; p++)
208
0
  {
209
0
    unsigned char *sp = info->samples + (passofs[p]);
210
0
    unsigned int w = passw[p];
211
0
    unsigned int h = passh[p];
212
213
0
    png_predict(sp, w, h, n, depth);
214
0
    for (y = 0; y < h; y++)
215
0
    {
216
0
      for (x = 0; x < w; x++)
217
0
      {
218
0
        int outx = x * adam7_dx[p] + adam7_ix[p];
219
0
        int outy = y * adam7_dy[p] + adam7_iy[p];
220
0
        unsigned char *dp = output + outy * stride;
221
0
        for (k = 0; k < n; k++)
222
0
        {
223
0
          int v = getcomp(sp, x * n + k, depth);
224
0
          putcomp(dp, outx * n + k, depth, v);
225
0
        }
226
0
      }
227
0
      sp += (w * depth * n + 7) / 8;
228
0
    }
229
0
  }
230
231
0
  fz_free(ctx, info->samples);
232
0
  info->samples = output;
233
0
}
234
235
static void
236
png_read_ihdr(fz_context *ctx, struct info *info, const unsigned char *p, unsigned int size)
237
0
{
238
0
  int color, compression, filter;
239
240
0
  if (size != 13)
241
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "IHDR chunk is the wrong size");
242
243
0
  info->width = fz_unpack_uint32(p + 0);
244
0
  info->height = fz_unpack_uint32(p + 4);
245
0
  info->depth = p[8];
246
247
0
  color = p[9];
248
0
  compression = p[10];
249
0
  filter = p[11];
250
0
  info->interlace = p[12];
251
252
0
  if (info->width <= 0)
253
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "image width must be > 0");
254
0
  if (info->height <= 0)
255
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "image height must be > 0");
256
257
0
  if (info->depth != 1 && info->depth != 2 && info->depth != 4 &&
258
0
      info->depth != 8 && info->depth != 16)
259
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "image bit depth must be one of 1, 2, 4, 8, 16");
260
0
  if (color == 2 && info->depth < 8)
261
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "illegal bit depth for truecolor");
262
0
  if (color == 3 && info->depth > 8)
263
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "illegal bit depth for indexed");
264
0
  if (color == 4 && info->depth < 8)
265
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "illegal bit depth for grayscale with alpha");
266
0
  if (color == 6 && info->depth < 8)
267
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "illegal bit depth for truecolor with alpha");
268
269
0
  info->indexed = 0;
270
0
  if (color == 0) /* gray */
271
0
    info->n = 1, info->type = FZ_COLORSPACE_GRAY;
272
0
  else if (color == 2) /* rgb */
273
0
    info->n = 3, info->type = FZ_COLORSPACE_RGB;
274
0
  else if (color == 4) /* gray alpha */
275
0
    info->n = 2, info->type = FZ_COLORSPACE_GRAY;
276
0
  else if (color == 6) /* rgb alpha */
277
0
    info->n = 4, info->type = FZ_COLORSPACE_RGB;
278
0
  else if (color == 3) /* indexed */
279
0
  {
280
0
    info->type = FZ_COLORSPACE_RGB; /* after colorspace expansion it will be */
281
0
    info->indexed = 1;
282
0
    info->n = 1;
283
0
  }
284
0
  else
285
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "unknown color type");
286
287
0
  if (compression != 0)
288
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "unknown compression method");
289
0
  if (filter != 0)
290
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "unknown filter method");
291
0
  if (info->interlace != 0 && info->interlace != 1)
292
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "interlace method not supported");
293
0
  if (info->height > UINT_MAX / info->width / info->n / (info->depth / 8 + 1))
294
0
    fz_throw(ctx, FZ_ERROR_LIMIT, "image dimensions might overflow");
295
0
}
296
297
static void
298
png_read_plte(fz_context *ctx, struct info *info, const unsigned char *p, unsigned int size)
299
0
{
300
0
  int n = size / 3;
301
0
  int i;
302
303
0
  if (n > 256)
304
0
  {
305
0
    fz_warn(ctx, "too many samples in palette");
306
0
    n = 256;
307
0
  }
308
309
0
  for (i = 0; i < n; i++)
310
0
  {
311
0
    info->palette[i * 4] = p[i * 3];
312
0
    info->palette[i * 4 + 1] = p[i * 3 + 1];
313
0
    info->palette[i * 4 + 2] = p[i * 3 + 2];
314
0
  }
315
316
  /* Fill in any missing palette entries */
317
0
  for (; i < 256; i++)
318
0
  {
319
0
    info->palette[i * 4] = 0;
320
0
    info->palette[i * 4 + 1] = 0;
321
0
    info->palette[i * 4 + 2] = 0;
322
0
  }
323
0
}
324
325
static void
326
png_read_trns(fz_context *ctx, struct info *info, const unsigned char *p, unsigned int size)
327
0
{
328
0
  unsigned int i;
329
330
0
  info->transparency = 1;
331
332
0
  if (info->indexed)
333
0
  {
334
0
    if (size > 256)
335
0
    {
336
0
      fz_warn(ctx, "too many samples in transparency table");
337
0
      size = 256;
338
0
    }
339
0
    for (i = 0; i < size; i++)
340
0
      info->palette[i * 4 + 3] = p[i];
341
    /* Fill in any missing entries */
342
0
    for (; i < 256; i++)
343
0
      info->palette[i * 4 + 3] = 255;
344
0
  }
345
0
  else
346
0
  {
347
0
    if (size != info->n * 2)
348
0
      fz_throw(ctx, FZ_ERROR_FORMAT, "tRNS chunk is the wrong size");
349
0
    for (i = 0; i < info->n; i++)
350
0
      info->trns[i] = (p[i * 2] << 8 | p[i * 2 + 1]) & ((1 << info->depth) - 1);
351
0
  }
352
0
}
353
354
static void
355
png_read_icc(fz_context *ctx, struct info *info, const unsigned char *p, unsigned int size)
356
0
{
357
0
#if FZ_ENABLE_ICC
358
0
  fz_stream *mstm = NULL, *zstm = NULL;
359
0
  fz_colorspace *cs = NULL;
360
0
  fz_buffer *buf = NULL;
361
0
  size_t m = fz_mini(80, size);
362
0
  size_t n = fz_strnlen((const char *)p, m);
363
0
  if (n + 2 > m)
364
0
  {
365
0
    fz_warn(ctx, "invalid ICC profile name");
366
0
    return;
367
0
  }
368
369
0
  fz_var(mstm);
370
0
  fz_var(zstm);
371
0
  fz_var(buf);
372
373
0
  fz_try(ctx)
374
0
  {
375
0
    mstm = fz_open_memory(ctx, p + n + 2, size - n - 2);
376
0
    zstm = fz_open_flated(ctx, mstm, 15);
377
0
    buf = fz_read_all(ctx, zstm, 0);
378
0
    cs = fz_new_icc_colorspace(ctx, info->type, 0, NULL, buf);
379
0
    fz_drop_colorspace(ctx, info->cs);
380
0
    info->cs = cs;
381
0
  }
382
0
  fz_always(ctx)
383
0
  {
384
0
    fz_drop_buffer(ctx, buf);
385
0
    fz_drop_stream(ctx, zstm);
386
0
    fz_drop_stream(ctx, mstm);
387
0
  }
388
0
  fz_catch(ctx)
389
0
  {
390
0
    fz_rethrow_if(ctx, FZ_ERROR_SYSTEM);
391
0
    fz_report_error(ctx);
392
0
    fz_warn(ctx, "ignoring embedded ICC profile in PNG");
393
0
  }
394
0
#endif
395
0
}
396
397
static void
398
png_read_idat(fz_context *ctx, struct info *info, const unsigned char *p, unsigned int size, z_stream *stm)
399
0
{
400
0
  int code;
401
402
0
  stm->next_in = (Bytef*)p;
403
0
  stm->avail_in = size;
404
405
0
  code = inflate(stm, Z_SYNC_FLUSH);
406
0
  if (code != Z_OK && code != Z_STREAM_END)
407
0
    fz_throw(ctx, FZ_ERROR_LIBRARY, "zlib error: %s", stm->msg);
408
0
  if (stm->avail_in != 0)
409
0
  {
410
0
    if (stm->avail_out == 0)
411
0
      fz_throw(ctx, FZ_ERROR_FORMAT, "ran out of output before input");
412
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "inflate did not consume buffer (%d remaining)", stm->avail_in);
413
0
  }
414
0
}
415
416
static void
417
png_read_phys(fz_context *ctx, struct info *info, const unsigned char *p, unsigned int size)
418
0
{
419
0
  if (size != 9)
420
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "pHYs chunk is the wrong size");
421
0
  if (p[8] == 1)
422
0
  {
423
0
    info->xres = (fz_unpack_uint32(p) * 254 + 5000) / 10000;
424
0
    info->yres = (fz_unpack_uint32(p + 4) * 254 + 5000) / 10000;
425
0
  }
426
0
}
427
428
static void
429
png_read_image(fz_context *ctx, struct info *info, const unsigned char *p, size_t total, int only_metadata)
430
0
{
431
0
  unsigned int passw[7], passh[7], passofs[8];
432
0
  unsigned int code, size;
433
0
  z_stream stm;
434
435
0
  memset(info, 0, sizeof (struct info));
436
0
  memset(info->palette, 255, sizeof(info->palette));
437
0
  info->xres = 96;
438
0
  info->yres = 96;
439
440
  /* Read signature */
441
442
0
  if (total < 8 + 12 || memcmp(p, png_signature, 8))
443
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "not a png image (wrong signature)");
444
445
0
  p += 8;
446
0
  total -= 8;
447
448
  /* Read IHDR chunk (must come first) */
449
450
0
  size = fz_unpack_uint32(p);
451
0
  if (size > total - 12)
452
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "premature end of data in png image");
453
454
0
  if (!memcmp(p + 4, "IHDR", 4))
455
0
    png_read_ihdr(ctx, info, p + 8, size);
456
0
  else
457
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "png file must start with IHDR chunk");
458
459
0
  p += size + 12;
460
0
  total -= size + 12;
461
462
  /* Prepare output buffer */
463
0
  if (!only_metadata)
464
0
  {
465
0
    if (!info->interlace)
466
0
    {
467
0
      info->size = info->height * (1 + ((size_t) info->width * info->n * info->depth + 7) / 8);
468
0
    }
469
0
    else
470
0
    {
471
0
      png_deinterlace_passes(ctx, info, passw, passh, passofs);
472
0
      info->size = passofs[7];
473
0
    }
474
475
0
    info->samples = Memento_label(fz_malloc(ctx, info->size), "png_samples");
476
477
0
    stm.zalloc = fz_zlib_alloc;
478
0
    stm.zfree = fz_zlib_free;
479
0
    stm.opaque = ctx;
480
481
0
    stm.next_out = info->samples;
482
0
    stm.avail_out = (uInt)info->size;
483
484
0
    code = inflateInit(&stm);
485
0
    if (code != Z_OK)
486
0
      fz_throw(ctx, FZ_ERROR_LIBRARY, "zlib error: %s", stm.msg);
487
0
  }
488
489
0
  fz_try(ctx)
490
0
  {
491
    /* Read remaining chunks until IEND */
492
0
    while (total > 8)
493
0
    {
494
0
      size = fz_unpack_uint32(p);
495
496
0
      if (total < 12 || size > total - 12)
497
0
        fz_throw(ctx, FZ_ERROR_FORMAT, "premature end of data in png image");
498
499
0
      if (!memcmp(p + 4, "PLTE", 4) && !only_metadata)
500
0
        png_read_plte(ctx, info, p + 8, size);
501
0
      if (!memcmp(p + 4, "tRNS", 4) && !only_metadata)
502
0
        png_read_trns(ctx, info, p + 8, size);
503
0
      if (!memcmp(p + 4, "pHYs", 4))
504
0
        png_read_phys(ctx, info, p + 8, size);
505
0
      if (!memcmp(p + 4, "IDAT", 4) && !only_metadata)
506
0
        png_read_idat(ctx, info, p + 8, size, &stm);
507
0
      if (!memcmp(p + 4, "iCCP", 4))
508
0
        png_read_icc(ctx, info, p + 8, size);
509
0
      if (!memcmp(p + 4, "IEND", 4))
510
0
        break;
511
512
0
      p += size + 12;
513
0
      total -= size + 12;
514
0
    }
515
0
    if (!only_metadata && stm.avail_out != 0)
516
0
    {
517
0
      memset(stm.next_out, 0xff, stm.avail_out);
518
0
      fz_warn(ctx, "missing pixel data in png image; possibly truncated");
519
0
    }
520
0
    else if (total <= 8)
521
0
      fz_warn(ctx, "missing IEND chunk in png image; possibly truncated");
522
0
  }
523
0
  fz_catch(ctx)
524
0
  {
525
0
    if (!only_metadata)
526
0
    {
527
0
      inflateEnd(&stm);
528
0
      fz_free(ctx, info->samples);
529
0
      info->samples = NULL;
530
0
    }
531
0
    fz_rethrow(ctx);
532
0
  }
533
534
0
  if (!only_metadata)
535
0
  {
536
0
    code = inflateEnd(&stm);
537
0
    if (code != Z_OK)
538
0
    {
539
0
      fz_free(ctx, info->samples);
540
0
      info->samples = NULL;
541
0
      fz_throw(ctx, FZ_ERROR_LIBRARY, "zlib error: %s", stm.msg);
542
0
    }
543
544
    /* Apply prediction filter and deinterlacing */
545
0
    fz_try(ctx)
546
0
    {
547
0
      if (!info->interlace)
548
0
        png_predict(info->samples, info->width, info->height, info->n, info->depth);
549
0
      else
550
0
        png_deinterlace(ctx, info, passw, passh, passofs);
551
0
    }
552
0
    fz_catch(ctx)
553
0
    {
554
0
      fz_free(ctx, info->samples);
555
0
      info->samples = NULL;
556
0
      fz_rethrow(ctx);
557
0
    }
558
0
  }
559
560
0
  if (info->cs && fz_colorspace_type(ctx, info->cs) != info->type)
561
0
  {
562
0
    fz_warn(ctx, "embedded ICC profile does not match PNG colorspace");
563
0
    fz_drop_colorspace(ctx, info->cs);
564
0
    info->cs = NULL;
565
0
  }
566
567
0
  if (info->cs == NULL)
568
0
  {
569
0
    if (info->n == 3 || info->n == 4 || info->indexed)
570
0
      info->cs = fz_keep_colorspace(ctx, fz_device_rgb(ctx));
571
0
    else
572
0
      info->cs = fz_keep_colorspace(ctx, fz_device_gray(ctx));
573
0
  }
574
0
}
575
576
static fz_pixmap *
577
png_expand_palette(fz_context *ctx, struct info *info, fz_pixmap *src)
578
0
{
579
0
  fz_pixmap *dst = fz_new_pixmap(ctx, info->cs, src->w, src->h, NULL, info->transparency);
580
0
  unsigned char *sp = src->samples;
581
0
  unsigned char *dp = dst->samples;
582
0
  unsigned int x, y;
583
0
  size_t dstride = dst->stride - dst->w * (size_t)dst->n;
584
0
  size_t sstride = src->stride - src->w * (size_t)src->n;
585
586
0
  dst->xres = src->xres;
587
0
  dst->yres = src->yres;
588
589
0
  for (y = info->height; y > 0; y--)
590
0
  {
591
0
    for (x = info->width; x > 0; x--)
592
0
    {
593
0
      int v = *sp << 2;
594
0
      *dp++ = info->palette[v];
595
0
      *dp++ = info->palette[v + 1];
596
0
      *dp++ = info->palette[v + 2];
597
0
      if (info->transparency)
598
0
        *dp++ = info->palette[v + 3];
599
0
      ++sp;
600
0
    }
601
0
    sp += sstride;
602
0
    dp += dstride;
603
0
  }
604
605
0
  fz_drop_pixmap(ctx, src);
606
0
  return dst;
607
0
}
608
609
static void
610
png_mask_transparency(struct info *info, fz_pixmap *dst)
611
0
{
612
0
  unsigned int stride = (info->width * info->n * info->depth + 7) / 8;
613
0
  unsigned int depth = info->depth;
614
0
  unsigned int n = info->n;
615
0
  unsigned int x, y, k, t;
616
617
0
  for (y = 0; y < info->height; y++)
618
0
  {
619
0
    unsigned char *sp = info->samples + (unsigned int)(y * stride);
620
0
    unsigned char *dp = dst->samples + (unsigned int)(y * dst->stride);
621
0
    for (x = 0; x < info->width; x++)
622
0
    {
623
0
      t = 1;
624
0
      for (k = 0; k < n; k++)
625
0
        if (getcomp(sp, x * n + k, depth) != info->trns[k])
626
0
          t = 0;
627
0
      if (t)
628
0
        dp[x * dst->n + dst->n - 1] = 0;
629
0
    }
630
0
  }
631
0
}
632
633
fz_pixmap *
634
fz_load_png(fz_context *ctx, const unsigned char *p, size_t total)
635
0
{
636
0
  fz_pixmap *image = NULL;
637
0
  struct info png;
638
0
  size_t stride;
639
0
  int alpha;
640
641
0
  fz_var(image);
642
643
0
  fz_try(ctx)
644
0
  {
645
0
    png_read_image(ctx, &png, p, total, 0);
646
647
0
    stride = ((size_t) png.width * png.n * png.depth + 7) / 8;
648
0
    alpha = (png.n == 2 || png.n == 4 || png.transparency);
649
650
0
    if (png.indexed)
651
0
    {
652
0
      image = fz_new_pixmap(ctx, NULL, png.width, png.height, NULL, 1);
653
0
      fz_unpack_tile(ctx, image, png.samples, png.n, png.depth, stride, 1);
654
0
      image = png_expand_palette(ctx, &png, image);
655
0
    }
656
0
    else
657
0
    {
658
0
      image = fz_new_pixmap(ctx, png.cs, png.width, png.height, NULL, alpha);
659
0
      fz_unpack_tile(ctx, image, png.samples, png.n, png.depth, stride, 0);
660
0
      if (png.transparency)
661
0
        png_mask_transparency(&png, image);
662
0
    }
663
0
    if (alpha)
664
0
      fz_premultiply_pixmap(ctx, image);
665
0
    fz_set_pixmap_resolution(ctx, image, png.xres, png.yres);
666
0
  }
667
0
  fz_always(ctx)
668
0
  {
669
0
    fz_drop_colorspace(ctx, png.cs);
670
0
    fz_free(ctx, png.samples);
671
0
  }
672
0
  fz_catch(ctx)
673
0
  {
674
0
    fz_drop_pixmap(ctx, image);
675
0
    fz_rethrow(ctx);
676
0
  }
677
678
0
  return image;
679
0
}
680
681
void
682
fz_load_png_info(fz_context *ctx, const unsigned char *p, size_t total, int *wp, int *hp, int *xresp, int *yresp, fz_colorspace **cspacep)
683
0
{
684
0
  struct info png;
685
686
0
  fz_try(ctx)
687
0
    png_read_image(ctx, &png, p, total, 1);
688
0
  fz_catch(ctx)
689
0
  {
690
0
    fz_drop_colorspace(ctx, png.cs);
691
0
    fz_rethrow(ctx);
692
0
  }
693
694
0
  *cspacep = png.cs;
695
0
  *wp = png.width;
696
0
  *hp = png.height;
697
0
  *xresp = png.xres;
698
0
  *yresp = png.xres;
699
0
}