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 <vips/intl.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) && !defined(HAVE_SPNG)
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 } VipsForeignLoadPng;
69 
70 typedef VipsForeignLoadClass VipsForeignLoadPngClass;
71 
72 G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadPng, vips_foreign_load_png,
73 	VIPS_TYPE_FOREIGN_LOAD );
74 
75 static void
vips_foreign_load_png_dispose(GObject * gobject)76 vips_foreign_load_png_dispose( GObject *gobject )
77 {
78 	VipsForeignLoadPng *png = (VipsForeignLoadPng *) gobject;
79 
80 	VIPS_UNREF( png->source );
81 
82 	G_OBJECT_CLASS( vips_foreign_load_png_parent_class )->
83 		dispose( gobject );
84 }
85 
86 static VipsForeignFlags
vips_foreign_load_png_get_flags_source(VipsSource * source)87 vips_foreign_load_png_get_flags_source( VipsSource *source )
88 {
89 	VipsForeignFlags flags;
90 
91 	flags = 0;
92 	if( vips__png_isinterlaced_source( source ) )
93 		flags |= VIPS_FOREIGN_PARTIAL;
94 	else
95 		flags |= VIPS_FOREIGN_SEQUENTIAL;
96 
97 	return( flags );
98 }
99 
100 static VipsForeignFlags
vips_foreign_load_png_get_flags(VipsForeignLoad * load)101 vips_foreign_load_png_get_flags( VipsForeignLoad *load )
102 {
103 	VipsForeignLoadPng *png = (VipsForeignLoadPng *) load;
104 
105 	return( vips_foreign_load_png_get_flags_source( png->source ) );
106 }
107 
108 static VipsForeignFlags
vips_foreign_load_png_get_flags_filename(const char * filename)109 vips_foreign_load_png_get_flags_filename( const char *filename )
110 {
111 	VipsSource *source;
112 	VipsForeignFlags flags;
113 
114 	if( !(source = vips_source_new_from_file( filename )) )
115 		return( 0 );
116 	flags = vips_foreign_load_png_get_flags_source( source );
117 	VIPS_UNREF( source );
118 
119 	return( flags );
120 }
121 
122 static int
vips_foreign_load_png_header(VipsForeignLoad * load)123 vips_foreign_load_png_header( VipsForeignLoad *load )
124 {
125 	VipsForeignLoadPng *png = (VipsForeignLoadPng *) load;
126 
127 	if( vips__png_header_source( png->source, load->out, png->unlimited ) )
128 		return( -1 );
129 
130 	return( 0 );
131 }
132 
133 static int
vips_foreign_load_png_load(VipsForeignLoad * load)134 vips_foreign_load_png_load( VipsForeignLoad *load )
135 {
136 	VipsForeignLoadPng *png = (VipsForeignLoadPng *) load;
137 
138 	if( vips__png_read_source( png->source, load->real,
139 		load->fail_on, png->unlimited ) )
140 		return( -1 );
141 
142 	return( 0 );
143 }
144 
145 static void
vips_foreign_load_png_class_init(VipsForeignLoadPngClass * class)146 vips_foreign_load_png_class_init( VipsForeignLoadPngClass *class )
147 {
148 	GObjectClass *gobject_class = G_OBJECT_CLASS( class );
149 	VipsObjectClass *object_class = (VipsObjectClass *) class;
150 	VipsForeignClass *foreign_class = (VipsForeignClass *) class;
151 	VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
152 
153 	gobject_class->dispose = vips_foreign_load_png_dispose;
154 	gobject_class->set_property = vips_object_set_property;
155 	gobject_class->get_property = vips_object_get_property;
156 
157 	object_class->nickname = "pngload_base";
158 	object_class->description = _( "load png base class" );
159 
160 	/* We are fast at is_a(), so high priority.
161 	 */
162 	foreign_class->priority = 200;
163 
164 	load_class->get_flags_filename =
165 		vips_foreign_load_png_get_flags_filename;
166 	load_class->get_flags = vips_foreign_load_png_get_flags;
167 	load_class->header = vips_foreign_load_png_header;
168 	load_class->load = vips_foreign_load_png_load;
169 
170 	VIPS_ARG_BOOL( class, "unlimited", 23,
171 		_( "Unlimited" ),
172 		_( "Remove all denial of service limits" ),
173 		VIPS_ARGUMENT_OPTIONAL_INPUT,
174 		G_STRUCT_OFFSET( VipsForeignLoadPng, unlimited ),
175 		FALSE );
176 }
177 
178 static void
vips_foreign_load_png_init(VipsForeignLoadPng * png)179 vips_foreign_load_png_init( VipsForeignLoadPng *png )
180 {
181 }
182 
183 typedef struct _VipsForeignLoadPngSource {
184 	VipsForeignLoadPng parent_object;
185 
186 	/* Load from a source.
187 	 */
188 	VipsSource *source;
189 
190 } VipsForeignLoadPngSource;
191 
192 typedef VipsForeignLoadPngClass VipsForeignLoadPngSourceClass;
193 
194 G_DEFINE_TYPE( VipsForeignLoadPngSource, vips_foreign_load_png_source,
195 	vips_foreign_load_png_get_type() );
196 
197 static int
vips_foreign_load_png_source_build(VipsObject * object)198 vips_foreign_load_png_source_build( VipsObject *object )
199 {
200 	VipsForeignLoadPng *png = (VipsForeignLoadPng *) object;
201 	VipsForeignLoadPngSource *source = (VipsForeignLoadPngSource *) object;
202 
203 	if( source->source ) {
204 		png->source = source->source;
205 		g_object_ref( png->source );
206 	}
207 
208 	if( VIPS_OBJECT_CLASS( vips_foreign_load_png_source_parent_class )->
209 		build( object ) )
210 		return( -1 );
211 
212 	return( 0 );
213 }
214 
215 static gboolean
vips_foreign_load_png_source_is_a_source(VipsSource * source)216 vips_foreign_load_png_source_is_a_source( VipsSource *source )
217 {
218 	return( vips__png_ispng_source( source ) );
219 }
220 
221 static void
vips_foreign_load_png_source_class_init(VipsForeignLoadPngSourceClass * class)222 vips_foreign_load_png_source_class_init( VipsForeignLoadPngSourceClass *class )
223 {
224 	GObjectClass *gobject_class = G_OBJECT_CLASS( class );
225 	VipsObjectClass *object_class = (VipsObjectClass *) class;
226 	VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class );
227 	VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
228 
229 	gobject_class->set_property = vips_object_set_property;
230 	gobject_class->get_property = vips_object_get_property;
231 
232 	object_class->nickname = "pngload_source";
233 	object_class->description = _( "load png from source" );
234 	object_class->build = vips_foreign_load_png_source_build;
235 
236 	operation_class->flags = VIPS_OPERATION_NOCACHE;
237 
238 	load_class->is_a_source = vips_foreign_load_png_source_is_a_source;
239 
240 	VIPS_ARG_OBJECT( class, "source", 1,
241 		_( "Source" ),
242 		_( "Source to load from" ),
243 		VIPS_ARGUMENT_REQUIRED_INPUT,
244 		G_STRUCT_OFFSET( VipsForeignLoadPngSource, source ),
245 		VIPS_TYPE_SOURCE );
246 
247 }
248 
249 static void
vips_foreign_load_png_source_init(VipsForeignLoadPngSource * source)250 vips_foreign_load_png_source_init( VipsForeignLoadPngSource *source )
251 {
252 }
253 
254 typedef struct _VipsForeignLoadPngFile {
255 	VipsForeignLoadPng parent_object;
256 
257 	/* Filename for load.
258 	 */
259 	char *filename;
260 
261 } VipsForeignLoadPngFile;
262 
263 typedef VipsForeignLoadPngClass VipsForeignLoadPngFileClass;
264 
265 G_DEFINE_TYPE( VipsForeignLoadPngFile, vips_foreign_load_png_file,
266 	vips_foreign_load_png_get_type() );
267 
268 static int
vips_foreign_load_png_file_build(VipsObject * object)269 vips_foreign_load_png_file_build( VipsObject *object )
270 {
271 	VipsForeignLoadPng *png = (VipsForeignLoadPng *) object;
272 	VipsForeignLoadPngFile *file = (VipsForeignLoadPngFile *) object;
273 
274 	if( file->filename &&
275 		!(png->source = vips_source_new_from_file( file->filename )) )
276 		return( -1 );
277 
278 	if( VIPS_OBJECT_CLASS( vips_foreign_load_png_file_parent_class )->
279 		build( object ) )
280 		return( -1 );
281 
282 	return( 0 );
283 }
284 
285 static gboolean
vips_foreign_load_png_file_is_a(const char * filename)286 vips_foreign_load_png_file_is_a( const char *filename )
287 {
288 	VipsSource *source;
289 	gboolean result;
290 
291 	if( !(source = vips_source_new_from_file( filename )) )
292 		return( FALSE );
293 	result = vips_foreign_load_png_source_is_a_source( source );
294 	VIPS_UNREF( source );
295 
296 	return( result );
297 }
298 
299 static void
vips_foreign_load_png_file_class_init(VipsForeignLoadPngFileClass * class)300 vips_foreign_load_png_file_class_init( VipsForeignLoadPngFileClass *class )
301 {
302 	GObjectClass *gobject_class = G_OBJECT_CLASS( class );
303 	VipsObjectClass *object_class = (VipsObjectClass *) class;
304 	VipsForeignClass *foreign_class = (VipsForeignClass *) class;
305 	VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
306 
307 	gobject_class->set_property = vips_object_set_property;
308 	gobject_class->get_property = vips_object_get_property;
309 
310 	object_class->nickname = "pngload";
311 	object_class->description = _( "load png from file" );
312 	object_class->build = vips_foreign_load_png_file_build;
313 
314 	foreign_class->suffs = vips__png_suffs;
315 
316 	load_class->is_a = vips_foreign_load_png_file_is_a;
317 
318 	VIPS_ARG_STRING( class, "filename", 1,
319 		_( "Filename" ),
320 		_( "Filename to load from" ),
321 		VIPS_ARGUMENT_REQUIRED_INPUT,
322 		G_STRUCT_OFFSET( VipsForeignLoadPngFile, filename ),
323 		NULL );
324 }
325 
326 static void
vips_foreign_load_png_file_init(VipsForeignLoadPngFile * file)327 vips_foreign_load_png_file_init( VipsForeignLoadPngFile *file )
328 {
329 }
330 
331 typedef struct _VipsForeignLoadPngBuffer {
332 	VipsForeignLoadPng parent_object;
333 
334 	/* Load from a buffer.
335 	 */
336 	VipsBlob *blob;
337 
338 } VipsForeignLoadPngBuffer;
339 
340 typedef VipsForeignLoadPngClass VipsForeignLoadPngBufferClass;
341 
342 G_DEFINE_TYPE( VipsForeignLoadPngBuffer, vips_foreign_load_png_buffer,
343 	vips_foreign_load_png_get_type() );
344 
345 static int
vips_foreign_load_png_buffer_build(VipsObject * object)346 vips_foreign_load_png_buffer_build( VipsObject *object )
347 {
348 	VipsForeignLoadPng *png = (VipsForeignLoadPng *) object;
349 	VipsForeignLoadPngBuffer *buffer = (VipsForeignLoadPngBuffer *) object;
350 
351 	if( buffer->blob &&
352 		!(png->source = vips_source_new_from_memory(
353 			VIPS_AREA( buffer->blob )->data,
354 			VIPS_AREA( buffer->blob )->length )) )
355 		return( -1 );
356 
357 	if( VIPS_OBJECT_CLASS( vips_foreign_load_png_buffer_parent_class )->
358 		build( object ) )
359 		return( -1 );
360 
361 	return( 0 );
362 }
363 
364 static gboolean
vips_foreign_load_png_buffer_is_a_buffer(const void * buf,size_t len)365 vips_foreign_load_png_buffer_is_a_buffer( const void *buf, size_t len )
366 {
367 	VipsSource *source;
368 	gboolean result;
369 
370 	if( !(source = vips_source_new_from_memory( buf, len )) )
371 		return( FALSE );
372 	result = vips_foreign_load_png_source_is_a_source( source );
373 	VIPS_UNREF( source );
374 
375 	return( result );
376 }
377 
378 static void
vips_foreign_load_png_buffer_class_init(VipsForeignLoadPngBufferClass * class)379 vips_foreign_load_png_buffer_class_init( VipsForeignLoadPngBufferClass *class )
380 {
381 	GObjectClass *gobject_class = G_OBJECT_CLASS( class );
382 	VipsObjectClass *object_class = (VipsObjectClass *) class;
383 	VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
384 
385 	gobject_class->set_property = vips_object_set_property;
386 	gobject_class->get_property = vips_object_get_property;
387 
388 	object_class->nickname = "pngload_buffer";
389 	object_class->description = _( "load png from buffer" );
390 	object_class->build = vips_foreign_load_png_buffer_build;
391 
392 	load_class->is_a_buffer = vips_foreign_load_png_buffer_is_a_buffer;
393 
394 	VIPS_ARG_BOXED( class, "buffer", 1,
395 		_( "Buffer" ),
396 		_( "Buffer to load from" ),
397 		VIPS_ARGUMENT_REQUIRED_INPUT,
398 		G_STRUCT_OFFSET( VipsForeignLoadPngBuffer, blob ),
399 		VIPS_TYPE_BLOB );
400 
401 }
402 
403 static void
vips_foreign_load_png_buffer_init(VipsForeignLoadPngBuffer * buffer)404 vips_foreign_load_png_buffer_init( VipsForeignLoadPngBuffer *buffer )
405 {
406 }
407 
408 #endif /*defined(HAVE_PNG) && !defined(HAVE_SPNG)*/
409 
410 /**
411  * vips_pngload:
412  * @filename: file to load
413  * @out: (out): decompressed image
414  * @...: %NULL-terminated list of optional named arguments
415  *
416  * Optional arguments:
417  *
418  * * @fail_on: #VipsFailOn, types of read error to fail on
419  * * @unlimited: %gboolean, remove all denial of service limits
420  *
421  * Read a PNG file into a VIPS image. It can read all png images, including 8-
422  * and 16-bit images, 1 and 3 channel, with and without an alpha channel.
423  *
424  * Any ICC profile is read and attached to the VIPS image. It also supports
425  * XMP metadata.
426  *
427  * Use @fail_on to set the type of error that will cause load to fail. By
428  * default, loaders are permissive, that is, #VIPS_FAIL_ON_NONE.
429  *
430  * By default, the PNG loader limits the number of text and data chunks to
431  * block some denial of service attacks. Set @unlimited to disable these
432  * limits.
433  *
434  * See also: vips_image_new_from_file().
435  *
436  * Returns: 0 on success, -1 on error.
437  */
438 int
vips_pngload(const char * filename,VipsImage ** out,...)439 vips_pngload( const char *filename, VipsImage **out, ... )
440 {
441 	va_list ap;
442 	int result;
443 
444 	va_start( ap, out );
445 	result = vips_call_split( "pngload", ap, filename, out );
446 	va_end( ap );
447 
448 	return( result );
449 }
450 
451 /**
452  * vips_pngload_buffer:
453  * @buf: (array length=len) (element-type guint8): memory area to load
454  * @len: (type gsize): size of memory area
455  * @out: (out): image to write
456  * @...: %NULL-terminated list of optional named arguments
457  *
458  * Optional arguments:
459  *
460  * * @fail_on: #VipsFailOn, types of read error to fail on
461  * * @unlimited: %gboolean, Remove all denial of service limits
462  *
463  * Exactly as vips_pngload(), but read from a PNG-formatted memory block.
464  *
465  * You must not free the buffer while @out is active. The
466  * #VipsObject::postclose signal on @out is a good place to free.
467  *
468  * See also: vips_pngload().
469  *
470  * Returns: 0 on success, -1 on error.
471  */
472 int
vips_pngload_buffer(void * buf,size_t len,VipsImage ** out,...)473 vips_pngload_buffer( void *buf, size_t len, VipsImage **out, ... )
474 {
475 	va_list ap;
476 	VipsBlob *blob;
477 	int result;
478 
479 	/* We don't take a copy of the data or free it.
480 	 */
481 	blob = vips_blob_new( NULL, buf, len );
482 
483 	va_start( ap, out );
484 	result = vips_call_split( "pngload_buffer", ap, blob, out );
485 	va_end( ap );
486 
487 	vips_area_unref( VIPS_AREA( blob ) );
488 
489 	return( result );
490 }
491 
492 /**
493  * vips_pngload_source:
494  * @source: source to load from
495  * @out: (out): image to write
496  * @...: %NULL-terminated list of optional named arguments
497  *
498  * Optional arguments:
499  *
500  * * @fail_on: #VipsFailOn, types of read error to fail on
501  * * @unlimited: %gboolean, Remove all denial of service limits
502  *
503  * Exactly as vips_pngload(), but read from a source.
504  *
505  * See also: vips_pngload().
506  *
507  * Returns: 0 on success, -1 on error.
508  */
509 int
vips_pngload_source(VipsSource * source,VipsImage ** out,...)510 vips_pngload_source( VipsSource *source, VipsImage **out, ... )
511 {
512 	va_list ap;
513 	int result;
514 
515 	va_start( ap, out );
516 	result = vips_call_split( "pngload_source", ap, source, out );
517 	va_end( ap );
518 
519 	return( result );
520 }
521