/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 | } |