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