Coverage Report

Created: 2026-09-14 06:55

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libvips/libvips/foreign/pngload.c
Line
Count
Source
1
/* load png from a file
2
 *
3
 * 5/12/11
4
 *  - from tiffload.c
5
 * 29/8/21 joshuamsager
6
 *  -  add "unlimited" flag to png load
7
 */
8
9
/*
10
11
  This file is part of VIPS.
12
13
  VIPS is free software; you can redistribute it and/or modify
14
  it under the terms of the GNU Lesser General Public License as published by
15
  the Free Software Foundation; either version 2 of the License, or
16
  (at your option) any later version.
17
18
  This program is distributed in the hope that it will be useful,
19
  but WITHOUT ANY WARRANTY; without even the implied warranty of
20
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
  GNU Lesser General Public License for more details.
22
23
  You should have received a copy of the GNU Lesser General Public License
24
  along with this program; if not, write to the Free Software
25
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26
  02110-1301  USA
27
28
 */
29
30
/*
31
32
  These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
33
34
 */
35
36
/*
37
#define DEBUG
38
 */
39
40
#ifdef HAVE_CONFIG_H
41
#include <config.h>
42
#endif /*HAVE_CONFIG_H*/
43
#include <glib/gi18n-lib.h>
44
45
#include <stdio.h>
46
#include <stdlib.h>
47
#include <string.h>
48
49
#include <vips/vips.h>
50
#include <vips/buf.h>
51
#include <vips/internal.h>
52
53
#include "pforeign.h"
54
55
#if defined(HAVE_PNG)
56
57
typedef struct _VipsForeignLoadPng {
58
  VipsForeignLoad parent_object;
59
60
  /* Set by subclasses.
61
   */
62
  VipsSource *source;
63
64
  /* remove all denial of service limits.
65
   */
66
  gboolean unlimited;
67
68
  /* Load this page (frame number, numbered from zero).
69
   */
70
  int page;
71
72
  /* Load this many pages (-1 for all).
73
   */
74
  int n;
75
76
} VipsForeignLoadPng;
77
78
typedef VipsForeignLoadClass VipsForeignLoadPngClass;
79
80
120
G_DEFINE_ABSTRACT_TYPE(VipsForeignLoadPng, vips_foreign_load_png,
81
120
  VIPS_TYPE_FOREIGN_LOAD);
82
120
83
120
static void
84
120
vips_foreign_load_png_dispose(GObject *gobject)
85
27.8k
{
86
27.8k
  VipsForeignLoadPng *png = (VipsForeignLoadPng *) gobject;
87
88
27.8k
  VIPS_UNREF(png->source);
89
90
27.8k
  G_OBJECT_CLASS(vips_foreign_load_png_parent_class)->dispose(gobject);
91
27.8k
}
92
93
static VipsForeignFlags
94
vips_foreign_load_png_get_flags_source(VipsSource *source)
95
27.8k
{
96
27.8k
  VipsForeignFlags flags;
97
98
27.8k
  flags = 0;
99
27.8k
  if (vips__png_isinterlaced_source(source))
100
18.7k
    flags |= VIPS_FOREIGN_PARTIAL;
101
9.08k
  else
102
9.08k
    flags |= VIPS_FOREIGN_SEQUENTIAL;
103
104
27.8k
  return flags;
105
27.8k
}
106
107
static VipsForeignFlags
108
vips_foreign_load_png_get_flags(VipsForeignLoad *load)
109
27.8k
{
110
27.8k
  VipsForeignLoadPng *png = (VipsForeignLoadPng *) load;
111
112
27.8k
  if (!png->source)
113
0
    return 0;
114
115
27.8k
  return vips_foreign_load_png_get_flags_source(png->source);
116
27.8k
}
117
118
static VipsForeignFlags
119
vips_foreign_load_png_get_flags_filename(const char *filename)
120
0
{
121
0
  VipsSource *source;
122
0
  VipsForeignFlags flags;
123
124
0
  if (!(source = vips_source_new_from_file(filename)))
125
0
    return 0;
126
0
  flags = vips_foreign_load_png_get_flags_source(source);
127
0
  VIPS_UNREF(source);
128
129
0
  return flags;
130
0
}
131
132
static int
133
vips_foreign_load_png_header(VipsForeignLoad *load)
134
27.8k
{
135
27.8k
  VipsForeignLoadPng *png = (VipsForeignLoadPng *) load;
136
137
27.8k
  if (vips__png_header_source(png->source, load->out,
138
27.8k
      png->page, png->n, png->unlimited))
139
14.9k
    return -1;
140
141
12.8k
  return 0;
142
27.8k
}
143
144
static int
145
vips_foreign_load_png_load(VipsForeignLoad *load)
146
12.2k
{
147
12.2k
  VipsForeignLoadPng *png = (VipsForeignLoadPng *) load;
148
149
12.2k
  if (vips__png_read_source(png->source, load->real,
150
12.2k
      png->page, png->n, load->fail_on, png->unlimited))
151
4.11k
    return -1;
152
153
8.15k
  return 0;
154
12.2k
}
155
156
static void
157
vips_foreign_load_png_class_init(VipsForeignLoadPngClass *class)
158
20
{
159
20
  GObjectClass *gobject_class = G_OBJECT_CLASS(class);
160
20
  VipsObjectClass *object_class = (VipsObjectClass *) class;
161
20
  VipsForeignClass *foreign_class = (VipsForeignClass *) class;
162
20
  VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
163
164
20
  gobject_class->dispose = vips_foreign_load_png_dispose;
165
20
  gobject_class->set_property = vips_object_set_property;
166
20
  gobject_class->get_property = vips_object_get_property;
167
168
20
  object_class->nickname = "pngload_base";
169
20
  object_class->description = _("load png base class");
170
171
  /* We are fast at is_a(), so high priority.
172
   */
173
20
  foreign_class->priority = 200;
174
175
20
  load_class->get_flags_filename =
176
20
    vips_foreign_load_png_get_flags_filename;
177
20
  load_class->get_flags = vips_foreign_load_png_get_flags;
178
20
  load_class->header = vips_foreign_load_png_header;
179
20
  load_class->load = vips_foreign_load_png_load;
180
181
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
182
  VIPS_ARG_BOOL(class, "unlimited", 23,
183
    _("Unlimited"),
184
    _("Remove all denial of service limits"),
185
    VIPS_ARGUMENT_OPTIONAL_INPUT,
186
    G_STRUCT_OFFSET(VipsForeignLoadPng, unlimited),
187
    FALSE);
188
#endif
189
190
20
  VIPS_ARG_INT(class, "page", 20,
191
20
    _("Page"),
192
20
    _("First page to load"),
193
20
    VIPS_ARGUMENT_OPTIONAL_INPUT,
194
20
    G_STRUCT_OFFSET(VipsForeignLoadPng, page),
195
20
    0, 100000, 0);
196
197
20
  VIPS_ARG_INT(class, "n", 21,
198
20
    _("n"),
199
20
    _("Number of pages to load, -1 for all"),
200
20
    VIPS_ARGUMENT_OPTIONAL_INPUT,
201
20
    G_STRUCT_OFFSET(VipsForeignLoadPng, n),
202
20
    -1, 100000, 1);
203
20
}
204
205
static void
206
vips_foreign_load_png_init(VipsForeignLoadPng *png)
207
27.8k
{
208
27.8k
  png->unlimited = vips_unlimited_get();
209
27.8k
  png->n = 1;
210
27.8k
}
211
212
typedef struct _VipsForeignLoadPngSource {
213
  VipsForeignLoadPng parent_object;
214
215
  /* Load from a source.
216
   */
217
  VipsSource *source;
218
219
} VipsForeignLoadPngSource;
220
221
typedef VipsForeignLoadPngClass VipsForeignLoadPngSourceClass;
222
223
40
G_DEFINE_TYPE(VipsForeignLoadPngSource, vips_foreign_load_png_source,
224
40
  vips_foreign_load_png_get_type());
225
40
226
40
static int
227
40
vips_foreign_load_png_source_build(VipsObject *object)
228
367
{
229
367
  VipsForeignLoadPng *png = (VipsForeignLoadPng *) object;
230
367
  VipsForeignLoadPngSource *source = (VipsForeignLoadPngSource *) object;
231
232
367
  if (source->source) {
233
367
    png->source = source->source;
234
367
    g_object_ref(png->source);
235
367
  }
236
237
367
  return VIPS_OBJECT_CLASS(vips_foreign_load_png_source_parent_class)
238
367
    ->build(object);
239
367
}
240
241
static gboolean
242
vips_foreign_load_png_source_is_a_source(VipsSource *source)
243
426k
{
244
426k
  return vips__png_ispng_source(source);
245
426k
}
246
247
static void
248
vips_foreign_load_png_source_class_init(VipsForeignLoadPngSourceClass *class)
249
20
{
250
20
  GObjectClass *gobject_class = G_OBJECT_CLASS(class);
251
20
  VipsObjectClass *object_class = (VipsObjectClass *) class;
252
20
  VipsOperationClass *operation_class = VIPS_OPERATION_CLASS(class);
253
20
  VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
254
255
20
  gobject_class->set_property = vips_object_set_property;
256
20
  gobject_class->get_property = vips_object_get_property;
257
258
20
  object_class->nickname = "pngload_source";
259
20
  object_class->description = _("load png from source");
260
20
  object_class->build = vips_foreign_load_png_source_build;
261
262
20
  operation_class->flags |= VIPS_OPERATION_NOCACHE;
263
264
20
  load_class->is_a_source = vips_foreign_load_png_source_is_a_source;
265
266
20
  VIPS_ARG_OBJECT(class, "source", 1,
267
20
    _("Source"),
268
20
    _("Source to load from"),
269
20
    VIPS_ARGUMENT_REQUIRED_INPUT,
270
20
    G_STRUCT_OFFSET(VipsForeignLoadPngSource, source),
271
20
    VIPS_TYPE_SOURCE);
272
20
}
273
274
static void
275
vips_foreign_load_png_source_init(VipsForeignLoadPngSource *source)
276
371
{
277
371
}
278
279
typedef struct _VipsForeignLoadPngFile {
280
  VipsForeignLoadPng parent_object;
281
282
  /* Filename for load.
283
   */
284
  char *filename;
285
286
} VipsForeignLoadPngFile;
287
288
typedef VipsForeignLoadPngClass VipsForeignLoadPngFileClass;
289
290
40
G_DEFINE_TYPE(VipsForeignLoadPngFile, vips_foreign_load_png_file,
291
40
  vips_foreign_load_png_get_type());
292
40
293
40
static int
294
40
vips_foreign_load_png_file_build(VipsObject *object)
295
2.59k
{
296
2.59k
  VipsForeignLoadPng *png = (VipsForeignLoadPng *) object;
297
2.59k
  VipsForeignLoadPngFile *file = (VipsForeignLoadPngFile *) object;
298
299
2.59k
  if (file->filename &&
300
2.59k
    !(png->source = vips_source_new_from_file(file->filename)))
301
0
    return -1;
302
303
2.59k
  return VIPS_OBJECT_CLASS(vips_foreign_load_png_file_parent_class)
304
2.59k
    ->build(object);
305
2.59k
}
306
307
static gboolean
308
vips_foreign_load_png_file_is_a(const char *filename)
309
40.0k
{
310
40.0k
  VipsSource *source;
311
40.0k
  gboolean result;
312
313
40.0k
  if (!(source = vips_source_new_from_file(filename)))
314
0
    return FALSE;
315
40.0k
  result = vips_foreign_load_png_source_is_a_source(source);
316
40.0k
  VIPS_UNREF(source);
317
318
40.0k
  return result;
319
40.0k
}
320
321
static void
322
vips_foreign_load_png_file_class_init(VipsForeignLoadPngFileClass *class)
323
20
{
324
20
  GObjectClass *gobject_class = G_OBJECT_CLASS(class);
325
20
  VipsObjectClass *object_class = (VipsObjectClass *) class;
326
20
  VipsForeignClass *foreign_class = (VipsForeignClass *) class;
327
20
  VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
328
329
20
  gobject_class->set_property = vips_object_set_property;
330
20
  gobject_class->get_property = vips_object_get_property;
331
332
20
  object_class->nickname = "pngload";
333
20
  object_class->description = _("load png from file");
334
20
  object_class->build = vips_foreign_load_png_file_build;
335
336
20
  foreign_class->suffs = vips__png_suffs;
337
338
20
  load_class->is_a = vips_foreign_load_png_file_is_a;
339
340
20
  VIPS_ARG_STRING(class, "filename", 1,
341
20
    _("Filename"),
342
20
    _("Filename to load from"),
343
20
    VIPS_ARGUMENT_REQUIRED_INPUT,
344
20
    G_STRUCT_OFFSET(VipsForeignLoadPngFile, filename),
345
20
    NULL);
346
20
}
347
348
static void
349
vips_foreign_load_png_file_init(VipsForeignLoadPngFile *file)
350
2.59k
{
351
2.59k
}
352
353
typedef struct _VipsForeignLoadPngBuffer {
354
  VipsForeignLoadPng parent_object;
355
356
  /* Load from a buffer.
357
   */
358
  VipsBlob *blob;
359
360
} VipsForeignLoadPngBuffer;
361
362
typedef VipsForeignLoadPngClass VipsForeignLoadPngBufferClass;
363
364
40
G_DEFINE_TYPE(VipsForeignLoadPngBuffer, vips_foreign_load_png_buffer,
365
40
  vips_foreign_load_png_get_type());
366
40
367
40
static int
368
40
vips_foreign_load_png_buffer_build(VipsObject *object)
369
24.8k
{
370
24.8k
  VipsForeignLoadPng *png = (VipsForeignLoadPng *) object;
371
24.8k
  VipsForeignLoadPngBuffer *buffer = (VipsForeignLoadPngBuffer *) object;
372
373
24.8k
  if (buffer->blob &&
374
24.8k
    !(png->source = vips_source_new_from_memory(
375
24.8k
        VIPS_AREA(buffer->blob)->data,
376
24.8k
        VIPS_AREA(buffer->blob)->length)))
377
0
    return -1;
378
379
24.8k
  return VIPS_OBJECT_CLASS(vips_foreign_load_png_buffer_parent_class)
380
24.8k
    ->build(object);
381
24.8k
}
382
383
static gboolean
384
vips_foreign_load_png_buffer_is_a_buffer(const void *buf, size_t len)
385
373k
{
386
373k
  VipsSource *source;
387
373k
  gboolean result;
388
389
373k
  if (!(source = vips_source_new_from_memory(buf, len)))
390
0
    return FALSE;
391
373k
  result = vips_foreign_load_png_source_is_a_source(source);
392
373k
  VIPS_UNREF(source);
393
394
373k
  return result;
395
373k
}
396
397
static void
398
vips_foreign_load_png_buffer_class_init(VipsForeignLoadPngBufferClass *class)
399
20
{
400
20
  GObjectClass *gobject_class = G_OBJECT_CLASS(class);
401
20
  VipsObjectClass *object_class = (VipsObjectClass *) class;
402
20
  VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
403
404
20
  gobject_class->set_property = vips_object_set_property;
405
20
  gobject_class->get_property = vips_object_get_property;
406
407
20
  object_class->nickname = "pngload_buffer";
408
20
  object_class->description = _("load png from buffer");
409
20
  object_class->build = vips_foreign_load_png_buffer_build;
410
411
20
  load_class->is_a_buffer = vips_foreign_load_png_buffer_is_a_buffer;
412
413
20
  VIPS_ARG_BOXED(class, "buffer", 1,
414
20
    _("Buffer"),
415
20
    _("Buffer to load from"),
416
20
    VIPS_ARGUMENT_REQUIRED_INPUT,
417
20
    G_STRUCT_OFFSET(VipsForeignLoadPngBuffer, blob),
418
20
    VIPS_TYPE_BLOB);
419
20
}
420
421
static void
422
vips_foreign_load_png_buffer_init(VipsForeignLoadPngBuffer *buffer)
423
24.8k
{
424
24.8k
}
425
426
#endif /*HAVE_PNG*/
427
428
/**
429
 * vips_pngload:
430
 * @filename: file to load
431
 * @out: (out): decompressed image
432
 * @...: `NULL`-terminated list of optional named arguments
433
 *
434
 * Read a PNG file into a VIPS image. It can read all png images, including 8-
435
 * and 16-bit images, 1 and 3 channel, with and without an alpha channel.
436
 *
437
 * Any ICC profile is read and attached to the VIPS image. It also supports
438
 * XMP metadata.
439
 *
440
 * Use @fail_on to set the type of error that will cause load to fail. By
441
 * default, loaders are permissive, that is, [enum@Vips.FailOn.NONE].
442
 *
443
 * By default, the PNG loader limits the number of text and data chunks to
444
 * block some denial of service attacks. Set @unlimited to disable these
445
 * limits.
446
 *
447
 * Use @page to set the first page (frame) to load, and @n to set the number
448
 * of pages to load. Set @n to -1 to load all pages from @page onwards. By
449
 * default, only the first page is loaded.
450
 *
451
 * For animated PNGs (APNG), pages are stacked vertically in the output image.
452
 *
453
 * ::: tip "Optional arguments"
454
 *     * @fail_on: [enum@FailOn], types of read error to fail on
455
 *     * @unlimited: `gboolean`, Remove all denial of service limits
456
 *     * @page: `gint`, first page to load
457
 *     * @n: `gint`, number of pages to load, -1 for all
458
 *
459
 * ::: seealso
460
 *     [ctor@Image.new_from_file].
461
 *
462
 * Returns: 0 on success, -1 on error.
463
 */
464
int
465
vips_pngload(const char *filename, VipsImage **out, ...)
466
0
{
467
0
  va_list ap;
468
0
  int result;
469
470
0
  va_start(ap, out);
471
0
  result = vips_call_split("pngload", ap, filename, out);
472
0
  va_end(ap);
473
474
0
  return result;
475
0
}
476
477
/**
478
 * vips_pngload_buffer:
479
 * @buf: (array length=len) (element-type guint8): memory area to load
480
 * @len: (type gsize): size of memory area
481
 * @out: (out): image to write
482
 * @...: `NULL`-terminated list of optional named arguments
483
 *
484
 * Exactly as [ctor@Image.pngload], but read from a PNG-formatted memory block.
485
 *
486
 * You must not free the buffer while @out is active. The
487
 * [signal@Object::postclose] signal on @out is a good place to free.
488
 *
489
 * ::: tip "Optional arguments"
490
 *     * @fail_on: [enum@FailOn], types of read error to fail on
491
 *     * @unlimited: `gboolean`, Remove all denial of service limits
492
 *
493
 * ::: seealso
494
 *     [ctor@Image.pngload].
495
 *
496
 * Returns: 0 on success, -1 on error.
497
 */
498
int
499
vips_pngload_buffer(void *buf, size_t len, VipsImage **out, ...)
500
0
{
501
0
  va_list ap;
502
0
  VipsBlob *blob;
503
0
  int result;
504
505
  /* We don't take a copy of the data or free it.
506
   */
507
0
  blob = vips_blob_new(NULL, buf, len);
508
509
0
  va_start(ap, out);
510
0
  result = vips_call_split("pngload_buffer", ap, blob, out);
511
0
  va_end(ap);
512
513
0
  vips_area_unref(VIPS_AREA(blob));
514
515
0
  return result;
516
0
}
517
518
/**
519
 * vips_pngload_source:
520
 * @source: source to load from
521
 * @out: (out): image to write
522
 * @...: `NULL`-terminated list of optional named arguments
523
 *
524
 * Exactly as [ctor@Image.pngload], but read from a source.
525
 *
526
 * ::: tip "Optional arguments"
527
 *     * @fail_on: [enum@FailOn], types of read error to fail on
528
 *     * @unlimited: `gboolean`, Remove all denial of service limits
529
 *
530
 * ::: seealso
531
 *     [ctor@Image.pngload].
532
 *
533
 * Returns: 0 on success, -1 on error.
534
 */
535
int
536
vips_pngload_source(VipsSource *source, VipsImage **out, ...)
537
0
{
538
0
  va_list ap;
539
0
  int result;
540
541
0
  va_start(ap, out);
542
0
  result = vips_call_split("pngload_source", ap, source, out);
543
0
  va_end(ap);
544
545
0
  return result;
546
0
}