1 /* load webp images
2  *
3  * 6/8/13
4  * 	- from pngload.c
5  * 28/2/16
6  * 	- add @shrink
7  * 1/11/18
8  * 	- add @page, @n
9  * 30/4/19
10  * 	- deprecate @shrink, use @scale instead
11  */
12 
13 /*
14 
15     This file is part of VIPS.
16 
17     VIPS is free software; you can redistribute it and/or modify
18     it under the terms of the GNU Lesser General Public License as published by
19     the Free Software Foundation; either version 2 of the License, or
20     (at your option) any later version.
21 
22     This program is distributed in the hope that it will be useful,
23     but WITHOUT ANY WARRANTY; without even the implied warranty of
24     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25     GNU Lesser General Public License for more details.
26 
27     You should have received a copy of the GNU Lesser General Public License
28     along with this program; if not, write to the Free Software
29     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
30     02110-1301  USA
31 
32  */
33 
34 /*
35 
36     These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
37 
38  */
39 
40 /*
41 #define DEBUG_VERBOSE
42 #define DEBUG
43  */
44 
45 #ifdef HAVE_CONFIG_H
46 #include <config.h>
47 #endif /*HAVE_CONFIG_H*/
48 #include <vips/intl.h>
49 
50 #include <string.h>
51 
52 #include <vips/vips.h>
53 
54 #include "pforeign.h"
55 
56 #ifdef HAVE_LIBWEBP
57 
58 typedef struct _VipsForeignLoadWebp {
59 	VipsForeignLoad parent_object;
60 
61 	/* Set by subclasses.
62 	 */
63 	VipsSource *source;
64 
65 	/* Load this page (frame number).
66 	 */
67 	int page;
68 
69 	/* Load this many pages.
70 	 */
71 	int n;
72 
73 	/* Scale by this much during load.
74 	 */
75 	double scale;
76 
77 	/* Old and deprecated scaling path.
78 	 */
79 	int shrink;
80 } VipsForeignLoadWebp;
81 
82 typedef VipsForeignLoadClass VipsForeignLoadWebpClass;
83 
84 G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadWebp, vips_foreign_load_webp,
85 	VIPS_TYPE_FOREIGN_LOAD );
86 
87 static void
vips_foreign_load_webp_dispose(GObject * gobject)88 vips_foreign_load_webp_dispose( GObject *gobject )
89 {
90 	VipsForeignLoadWebp *webp = (VipsForeignLoadWebp *) gobject;
91 
92 	VIPS_UNREF( webp->source );
93 
94 	G_OBJECT_CLASS( vips_foreign_load_webp_parent_class )->
95 		dispose( gobject );
96 }
97 
98 static int
vips_foreign_load_webp_build(VipsObject * object)99 vips_foreign_load_webp_build( VipsObject *object )
100 {
101 	VipsForeignLoadWebp *webp = (VipsForeignLoadWebp *) object;
102 
103 	/* BC for the old API.
104 	 */
105 	if( !vips_object_argument_isset( VIPS_OBJECT( webp ), "scale" ) &&
106 		vips_object_argument_isset( VIPS_OBJECT( webp ), "shrink" ) &&
107 		webp->shrink != 0 )
108 		webp->scale = 1.0 / webp->shrink;
109 
110 	if( VIPS_OBJECT_CLASS( vips_foreign_load_webp_parent_class )->
111 		build( object ) )
112 		return( -1 );
113 
114 	return( 0 );
115 }
116 
117 static VipsForeignFlags
vips_foreign_load_webp_get_flags(VipsForeignLoad * load)118 vips_foreign_load_webp_get_flags( VipsForeignLoad *load )
119 {
120 	return( 0 );
121 }
122 
123 static VipsForeignFlags
vips_foreign_load_webp_get_flags_filename(const char * filename)124 vips_foreign_load_webp_get_flags_filename( const char *filename )
125 {
126 	return( 0 );
127 }
128 
129 static int
vips_foreign_load_webp_header(VipsForeignLoad * load)130 vips_foreign_load_webp_header( VipsForeignLoad *load )
131 {
132 	VipsForeignLoadWebp *webp = (VipsForeignLoadWebp *) load;
133 
134 	if( vips__webp_read_header_source( webp->source, load->out,
135 		webp->page, webp->n, webp->scale ) )
136 		return( -1 );
137 
138 	return( 0 );
139 }
140 
141 static int
vips_foreign_load_webp_load(VipsForeignLoad * load)142 vips_foreign_load_webp_load( VipsForeignLoad *load )
143 {
144 	VipsForeignLoadWebp *webp = (VipsForeignLoadWebp *) load;
145 
146 	if( vips__webp_read_source( webp->source, load->real,
147 		webp->page, webp->n, webp->scale ) )
148 		return( -1 );
149 
150 	return( 0 );
151 }
152 
153 static void
vips_foreign_load_webp_class_init(VipsForeignLoadWebpClass * class)154 vips_foreign_load_webp_class_init( VipsForeignLoadWebpClass *class )
155 {
156 	GObjectClass *gobject_class = G_OBJECT_CLASS( class );
157 	VipsObjectClass *object_class = (VipsObjectClass *) class;
158 	VipsForeignClass *foreign_class = (VipsForeignClass *) class;
159 	VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
160 
161 	gobject_class->dispose = vips_foreign_load_webp_dispose;
162 	gobject_class->set_property = vips_object_set_property;
163 	gobject_class->get_property = vips_object_get_property;
164 
165 	object_class->nickname = "webpload_base";
166 	object_class->description = _( "load webp" );
167 	object_class->build = vips_foreign_load_webp_build;
168 
169 	/* We are fast at is_a(), so high priority.
170 	 */
171 	foreign_class->priority = 200;
172 
173 	load_class->get_flags_filename =
174 		vips_foreign_load_webp_get_flags_filename;
175 	load_class->get_flags = vips_foreign_load_webp_get_flags;
176 	load_class->header = vips_foreign_load_webp_header;
177 	load_class->load = vips_foreign_load_webp_load;
178 
179 	VIPS_ARG_INT( class, "page", 20,
180 		_( "Page" ),
181 		_( "Load this page from the file" ),
182 		VIPS_ARGUMENT_OPTIONAL_INPUT,
183 		G_STRUCT_OFFSET( VipsForeignLoadWebp, page ),
184 		0, 100000, 0 );
185 
186 	VIPS_ARG_INT( class, "n", 21,
187 		_( "n" ),
188 		_( "Load this many pages" ),
189 		VIPS_ARGUMENT_OPTIONAL_INPUT,
190 		G_STRUCT_OFFSET( VipsForeignLoadWebp, n ),
191 		-1, 100000, 1 );
192 
193 	VIPS_ARG_DOUBLE( class, "scale", 22,
194 		_( "Scale" ),
195 		_( "Scale factor on load" ),
196 		VIPS_ARGUMENT_OPTIONAL_INPUT,
197 		G_STRUCT_OFFSET( VipsForeignLoadWebp, scale ),
198 		0.0, 1024.0, 1.0 );
199 
200 	/* Old and deprecated scaling API. A float param lets do
201 	 * shrink-on-load for thumbnail faster and more accurately.
202 	 */
203 	VIPS_ARG_INT( class, "shrink", 23,
204 		_( "Shrink" ),
205 		_( "Shrink factor on load" ),
206 		VIPS_ARGUMENT_OPTIONAL_INPUT | VIPS_ARGUMENT_DEPRECATED,
207 		G_STRUCT_OFFSET( VipsForeignLoadWebp, shrink ),
208 		1, 1024, 1 );
209 
210 }
211 
212 static void
vips_foreign_load_webp_init(VipsForeignLoadWebp * webp)213 vips_foreign_load_webp_init( VipsForeignLoadWebp *webp )
214 {
215 	webp->n = 1;
216 	webp->shrink = 1;
217 	webp->scale = 1.0;
218 }
219 
220 typedef struct _VipsForeignLoadWebpSource {
221 	VipsForeignLoadWebp parent_object;
222 
223 	VipsSource *source;
224 
225 } VipsForeignLoadWebpSource;
226 
227 typedef VipsForeignLoadWebpClass VipsForeignLoadWebpSourceClass;
228 
229 G_DEFINE_TYPE( VipsForeignLoadWebpSource, vips_foreign_load_webp_source,
230 	vips_foreign_load_webp_get_type() );
231 
232 static int
vips_foreign_load_webp_source_build(VipsObject * object)233 vips_foreign_load_webp_source_build( VipsObject *object )
234 {
235 	VipsForeignLoadWebp *webp = (VipsForeignLoadWebp *) object;
236 	VipsForeignLoadWebpSource *source =
237 		(VipsForeignLoadWebpSource *) object;
238 
239 	if( source->source ) {
240 		webp->source = source->source;
241 		g_object_ref( webp->source );
242 	}
243 
244 	if( VIPS_OBJECT_CLASS( vips_foreign_load_webp_source_parent_class )->
245 		build( object ) )
246 		return( -1 );
247 
248 	return( 0 );
249 }
250 
251 static void
vips_foreign_load_webp_source_class_init(VipsForeignLoadWebpSourceClass * class)252 vips_foreign_load_webp_source_class_init(
253 	VipsForeignLoadWebpSourceClass *class )
254 {
255 	GObjectClass *gobject_class = G_OBJECT_CLASS( class );
256 	VipsObjectClass *object_class = (VipsObjectClass *) class;
257 	VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class );
258 	VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
259 
260 	gobject_class->set_property = vips_object_set_property;
261 	gobject_class->get_property = vips_object_get_property;
262 
263 	object_class->nickname = "webpload_source";
264 	object_class->description = _( "load webp from source" );
265 	object_class->build = vips_foreign_load_webp_source_build;
266 
267 	operation_class->flags = VIPS_OPERATION_NOCACHE;
268 
269 	load_class->is_a_source = vips__iswebp_source;
270 
271 	VIPS_ARG_OBJECT( class, "source", 1,
272 		_( "Source" ),
273 		_( "Source to load from" ),
274 		VIPS_ARGUMENT_REQUIRED_INPUT,
275 		G_STRUCT_OFFSET( VipsForeignLoadWebpSource, source ),
276 		VIPS_TYPE_SOURCE );
277 
278 }
279 
280 static void
vips_foreign_load_webp_source_init(VipsForeignLoadWebpSource * buffer)281 vips_foreign_load_webp_source_init( VipsForeignLoadWebpSource *buffer )
282 {
283 }
284 
285 typedef struct _VipsForeignLoadWebpFile {
286 	VipsForeignLoadWebp parent_object;
287 
288 	/* Filename for load.
289 	 */
290 	char *filename;
291 
292 } VipsForeignLoadWebpFile;
293 
294 typedef VipsForeignLoadWebpClass VipsForeignLoadWebpFileClass;
295 
296 G_DEFINE_TYPE( VipsForeignLoadWebpFile, vips_foreign_load_webp_file,
297 	vips_foreign_load_webp_get_type() );
298 
299 static int
vips_foreign_load_webp_file_build(VipsObject * object)300 vips_foreign_load_webp_file_build( VipsObject *object )
301 {
302 	VipsForeignLoadWebp *webp = (VipsForeignLoadWebp *) object;
303 	VipsForeignLoadWebpFile *file = (VipsForeignLoadWebpFile *) object;
304 
305 	if( file->filename &&
306 		!(webp->source =
307 			vips_source_new_from_file( file->filename )) )
308 		return( -1 );
309 
310 	if( VIPS_OBJECT_CLASS( vips_foreign_load_webp_file_parent_class )->
311 		build( object ) )
312 		return( -1 );
313 
314 	return( 0 );
315 }
316 
317 static gboolean
vips_foreign_load_webp_file_is_a(const char * filename)318 vips_foreign_load_webp_file_is_a( const char *filename )
319 {
320 	VipsSource *source;
321 	gboolean result;
322 
323 	if( !(source = vips_source_new_from_file( filename )) )
324 		return( FALSE );
325 	result = vips__iswebp_source( source );
326 	VIPS_UNREF( source );
327 
328 	return( result );
329 }
330 
331 const char *vips__webp_suffs[] = { ".webp", NULL };
332 
333 static void
vips_foreign_load_webp_file_class_init(VipsForeignLoadWebpFileClass * class)334 vips_foreign_load_webp_file_class_init( VipsForeignLoadWebpFileClass *class )
335 {
336 	GObjectClass *gobject_class = G_OBJECT_CLASS( class );
337 	VipsObjectClass *object_class = (VipsObjectClass *) class;
338 	VipsForeignClass *foreign_class = (VipsForeignClass *) class;
339 	VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
340 
341 	gobject_class->set_property = vips_object_set_property;
342 	gobject_class->get_property = vips_object_get_property;
343 
344 	object_class->nickname = "webpload";
345 	object_class->description = _( "load webp from file" );
346 	object_class->build = vips_foreign_load_webp_file_build;
347 
348 	foreign_class->suffs = vips__webp_suffs;
349 
350 	load_class->is_a = vips_foreign_load_webp_file_is_a;
351 
352 	VIPS_ARG_STRING( class, "filename", 1,
353 		_( "Filename" ),
354 		_( "Filename to load from" ),
355 		VIPS_ARGUMENT_REQUIRED_INPUT,
356 		G_STRUCT_OFFSET( VipsForeignLoadWebpFile, filename ),
357 		NULL );
358 }
359 
360 static void
vips_foreign_load_webp_file_init(VipsForeignLoadWebpFile * file)361 vips_foreign_load_webp_file_init( VipsForeignLoadWebpFile *file )
362 {
363 }
364 
365 typedef struct _VipsForeignLoadWebpBuffer {
366 	VipsForeignLoadWebp parent_object;
367 
368 	/* Load from a buffer.
369 	 */
370 	VipsBlob *blob;
371 
372 } VipsForeignLoadWebpBuffer;
373 
374 typedef VipsForeignLoadWebpClass VipsForeignLoadWebpBufferClass;
375 
376 G_DEFINE_TYPE( VipsForeignLoadWebpBuffer, vips_foreign_load_webp_buffer,
377 	vips_foreign_load_webp_get_type() );
378 
379 static int
vips_foreign_load_webp_buffer_build(VipsObject * object)380 vips_foreign_load_webp_buffer_build( VipsObject *object )
381 {
382 	VipsForeignLoadWebp *webp = (VipsForeignLoadWebp *) object;
383 	VipsForeignLoadWebpBuffer *buffer =
384 		(VipsForeignLoadWebpBuffer *) object;
385 
386 	if( buffer->blob &&
387 		!(webp->source = vips_source_new_from_memory(
388 			VIPS_AREA( buffer->blob )->data,
389 			VIPS_AREA( buffer->blob )->length )) )
390 		return( -1 );
391 
392 	if( VIPS_OBJECT_CLASS( vips_foreign_load_webp_buffer_parent_class )->
393 		build( object ) )
394 		return( -1 );
395 
396 	return( 0 );
397 }
398 
399 static gboolean
vips_foreign_load_webp_buffer_is_a_buffer(const void * buf,size_t len)400 vips_foreign_load_webp_buffer_is_a_buffer( const void *buf, size_t len )
401 {
402 	VipsSource *source;
403 	gboolean result;
404 
405 	if( !(source = vips_source_new_from_memory( buf, len )) )
406 		return( FALSE );
407 	result = vips__iswebp_source( source );
408 	VIPS_UNREF( source );
409 
410 	return( result );
411 }
412 
413 static void
vips_foreign_load_webp_buffer_class_init(VipsForeignLoadWebpBufferClass * class)414 vips_foreign_load_webp_buffer_class_init(
415 	VipsForeignLoadWebpBufferClass *class )
416 {
417 	GObjectClass *gobject_class = G_OBJECT_CLASS( class );
418 	VipsObjectClass *object_class = (VipsObjectClass *) class;
419 	VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
420 
421 	gobject_class->set_property = vips_object_set_property;
422 	gobject_class->get_property = vips_object_get_property;
423 
424 	object_class->nickname = "webpload_buffer";
425 	object_class->description = _( "load webp from buffer" );
426 	object_class->build = vips_foreign_load_webp_buffer_build;
427 
428 	load_class->is_a_buffer = vips_foreign_load_webp_buffer_is_a_buffer;
429 
430 	VIPS_ARG_BOXED( class, "buffer", 1,
431 		_( "Buffer" ),
432 		_( "Buffer to load from" ),
433 		VIPS_ARGUMENT_REQUIRED_INPUT,
434 		G_STRUCT_OFFSET( VipsForeignLoadWebpBuffer, blob ),
435 		VIPS_TYPE_BLOB );
436 }
437 
438 static void
vips_foreign_load_webp_buffer_init(VipsForeignLoadWebpBuffer * buffer)439 vips_foreign_load_webp_buffer_init( VipsForeignLoadWebpBuffer *buffer )
440 {
441 }
442 
443 #endif /*HAVE_LIBWEBP*/
444 
445 /**
446  * vips_webpload:
447  * @filename: file to load
448  * @out: (out): decompressed image
449  * @...: %NULL-terminated list of optional named arguments
450  *
451  * Optional arguments:
452  *
453  * * @page: %gint, page (frame) to read
454  * * @n: %gint, load this many pages
455  * * @scale: %gdouble, scale by this much on load
456  *
457  * Read a WebP file into a VIPS image.
458  *
459  * Use @page to select a page to render, numbering from zero.
460  *
461  * Use @n to select the number of pages to render. The default is 1. Pages are
462  * rendered in a vertical column, with each individual page aligned to the
463  * left. Set to -1 to mean "until the end of the document". Use vips_grid()
464  * to change page layout.
465  *
466  * Use @scale to specify a scale-on-load factor. For example, 2.0 to double
467  * the size on load. Animated webp images don't support shrink-on-load, so a
468  * further resize may be necessary.
469  *
470  * The loader supports ICC, EXIF and XMP metadata.
471  *
472  * See also: vips_image_new_from_file().
473  *
474  * Returns: 0 on success, -1 on error.
475  */
476 int
vips_webpload(const char * filename,VipsImage ** out,...)477 vips_webpload( const char *filename, VipsImage **out, ... )
478 {
479 	va_list ap;
480 	int result;
481 
482 	va_start( ap, out );
483 	result = vips_call_split( "webpload", ap, filename, out );
484 	va_end( ap );
485 
486 	return( result );
487 }
488 
489 /**
490  * vips_webpload_buffer:
491  * @buf: (array length=len) (element-type guint8): memory area to load
492  * @len: (type gsize): size of memory area
493  * @out: (out): image to write
494  * @...: %NULL-terminated list of optional named arguments
495  *
496  * Optional arguments:
497  *
498  * * @page: %gint, page (frame) to read
499  * * @n: %gint, load this many pages
500  * * @scale: %gdouble, scale by this much on load
501  *
502  * Read a WebP-formatted memory block into a VIPS image. Exactly as
503  * vips_webpload(), but read from a memory buffer.
504  *
505  * You must not free the buffer while @out is active. The
506  * #VipsObject::postclose signal on @out is a good place to free.
507  *
508  * See also: vips_webpload()
509  *
510  * Returns: 0 on success, -1 on error.
511  */
512 int
vips_webpload_buffer(void * buf,size_t len,VipsImage ** out,...)513 vips_webpload_buffer( void *buf, size_t len, VipsImage **out, ... )
514 {
515 	va_list ap;
516 	VipsBlob *blob;
517 	int result;
518 
519 	/* We don't take a copy of the data or free it.
520 	 */
521 	blob = vips_blob_new( NULL, buf, len );
522 
523 	va_start( ap, out );
524 	result = vips_call_split( "webpload_buffer", ap, blob, out );
525 	va_end( ap );
526 
527 	vips_area_unref( VIPS_AREA( blob ) );
528 
529 	return( result );
530 }
531 
532 /**
533  * vips_webpload_source:
534  * @source: source to load from
535  * @out: (out): image to write
536  * @...: %NULL-terminated list of optional named arguments
537  *
538  * Optional arguments:
539  *
540  * * @page: %gint, page (frame) to read
541  * * @n: %gint, load this many pages
542  * * @scale: %gdouble, scale by this much on load
543  *
544  * Exactly as vips_webpload(), but read from a source.
545  *
546  * See also: vips_webpload()
547  *
548  * Returns: 0 on success, -1 on error.
549  */
550 int
vips_webpload_source(VipsSource * source,VipsImage ** out,...)551 vips_webpload_source( VipsSource *source, VipsImage **out, ... )
552 {
553 	va_list ap;
554 	int result;
555 
556 	va_start( ap, out );
557 	result = vips_call_split( "webpload_source", ap, source, out );
558 	va_end( ap );
559 
560 	return( result );
561 }
562