1 /* read with libwebp
2  *
3  * 6/8/13
4  * 	- from png2vips.c
5  * 24/2/14
6  * 	- oops, buffer path was broken, thanks Lovell
7  * 28/2/16
8  * 	- add @shrink
9  * 7/11/16
10  * 	- support XMP/ICC/EXIF metadata
11  * 18/10/17
12  * 	- sniff file type from magic number
13  * 2/11/18
14  * 	- rework for demux API
15  * 	- add animated read
16  * 19/4/19
17  * 	- could memleak on some read errors
18  * 24/4/19
19  * 	- fix bg handling in animations
20  * 30/4/19
21  * 	- deprecate shrink, use scale instead, and make it a double ... this
22  * 	  lets us do faster and more accurate thumbnailing
23  * 27/6/19
24  * 	- disable alpha output if all frame fill the canvas and are solid
25  * 6/7/19 [deftomat]
26  * 	- support array of delays
27  * 14/10/19
28  * 	- revise for source IO
29  * 27/10/21
30  * 	- disable shrink-on-load if we need subpixel accuracy in animations
31  */
32 
33 /*
34 
35     This file is part of VIPS.
36 
37     VIPS is free software; you can redistribute it and/or modify
38     it under the terms of the GNU Lesser General Public License as published by
39     the Free Software Foundation; either version 2 of the License, or
40     (at your option) any later version.
41 
42     This program is distributed in the hope that it will be useful,
43     but WITHOUT ANY WARRANTY; without even the implied warranty of
44     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
45     GNU Lesser General Public License for more details.
46 
47     You should have received a copy of the GNU Lesser General Public License
48     along with this program; if not, write to the Free Software
49     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
50     02110-1301  USA
51 
52  */
53 
54 /*
55 
56     These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
57 
58  */
59 
60 /*
61 #define DEBUG_VERBOSE
62 #define DEBUG
63  */
64 
65 #ifdef HAVE_CONFIG_H
66 #include <config.h>
67 #endif /*HAVE_CONFIG_H*/
68 #include <vips/intl.h>
69 
70 #ifdef HAVE_LIBWEBP
71 
72 #include <stdlib.h>
73 #include <string.h>
74 
75 #include <webp/decode.h>
76 #include <webp/demux.h>
77 
78 #include <vips/vips.h>
79 #include <vips/internal.h>
80 
81 #include "pforeign.h"
82 
83 /* What we track during a read.
84  */
85 typedef struct {
86 	VipsImage *out;
87 	VipsSource *source;
88 
89 	/* The data we load, as a webp object.
90 	 */
91 	WebPData data;
92 
93 	/* Load this page (frame number).
94 	 */
95 	int page;
96 
97 	/* Load this many pages.
98 	 */
99 	int n;
100 
101 	/* Scale-on-load factor. Use this to set frame_width.
102 	 */
103 	double scale;
104 
105 	/* Size of each frame in input image coordinates.
106 	 */
107 	int canvas_width;
108 	int canvas_height;
109 
110 	/* Size of each frame, in scaled output image coordinates,
111 	 */
112 	int frame_width;
113 	int frame_height;
114 
115 	/* Size of final output image.
116 	 */
117 	int width;
118 	int height;
119 
120 	/* TRUE if we will save the final image as RGBA.
121 	 */
122 	int alpha;
123 
124 	/* Number of frames in file.
125 	 */
126 	int frame_count;
127 
128 	/* Delays between frames (in miliseconds).
129 	 */
130 	int *delays;
131 
132 	/* Parse with this.
133 	 */
134 	WebPDemuxer *demux;
135 
136 	/* Decoder config.
137 	 */
138 	WebPDecoderConfig config;
139 
140 	/* The current accumulated frame as a VipsImage. These are the pixels
141 	 * we send to the output. It's a frame_width * frame_height memory
142 	 * image.
143 	 */
144 	VipsImage *frame;
145 
146 	/* The frame number currently in @frame. Numbered from 1, so 0 means
147 	 * before the first frame.
148 	 */
149 	int frame_no;
150 
151 	/* Iterate through the frames with this. iter.frame_num is the number
152 	 * of the currently loaded frame.
153 	 */
154 	WebPIterator iter;
155 
156 	/* How to junk the current frame when we move on.
157 	 */
158 	WebPMuxAnimDispose dispose_method;
159 	VipsRect dispose_rect;
160 } Read;
161 
162 const char *
vips__error_webp(VP8StatusCode code)163 vips__error_webp( VP8StatusCode code )
164 {
165 	switch( code ) {
166 	case VP8_STATUS_OK:
167 		return( "VP8_STATUS_OK" );
168 
169 	case VP8_STATUS_OUT_OF_MEMORY:
170 		return( "VP8_STATUS_OUT_OF_MEMORY" );
171 
172 	case VP8_STATUS_INVALID_PARAM:
173 		return( "VP8_STATUS_INVALID_PARAM" );
174 
175 	case VP8_STATUS_BITSTREAM_ERROR:
176 		return( "VP8_STATUS_BITSTREAM_ERROR" );
177 
178 	case VP8_STATUS_UNSUPPORTED_FEATURE:
179 		return( "VP8_STATUS_UNSUPPORTED_FEATURE" );
180 
181 	case VP8_STATUS_SUSPENDED:
182 		return( "VP8_STATUS_SUSPENDED" );
183 
184 	case VP8_STATUS_USER_ABORT:
185 		return( "VP8_STATUS_USER_ABORT" );
186 
187 	case VP8_STATUS_NOT_ENOUGH_DATA:
188 		return( "VP8_STATUS_NOT_ENOUGH_DATA" );
189 
190 	default:
191 		return( "<unkown>" );
192 	}
193 }
194 
195 static void
vips_image_paint_area(VipsImage * image,const VipsRect * r,const VipsPel * ink)196 vips_image_paint_area( VipsImage *image, const VipsRect *r, const VipsPel *ink )
197 {
198 	VipsRect valid = { 0, 0, image->Xsize, image->Ysize };
199 	VipsRect ovl;
200 
201 	vips_rect_intersectrect( r, &valid, &ovl );
202 	if( !vips_rect_isempty( &ovl ) ) {
203 		int ps = VIPS_IMAGE_SIZEOF_PEL( image );
204 		int ls = VIPS_IMAGE_SIZEOF_LINE( image );
205 		int ws = ovl.width * ps;
206 
207 		VipsPel *to, *q;
208 		int x, y, z;
209 
210 		/* We plot the first line pointwise, then memcpy() it for the
211 		 * subsequent lines. We need to work for RGB and RGBA, so we
212 		 * can't just write uint32s.
213 		 */
214 		to = VIPS_IMAGE_ADDR( image, ovl.left, ovl.top );
215 
216 		q = to;
217 		for( x = 0; x < ovl.width; x++ ) {
218 			/* Faster than memcpy() for about ps < 20.
219 			 */
220 			for( z = 0; z < ps; z++ )
221 				q[z] = ink[z];
222 
223 			q += ps;
224 		}
225 
226 		q = to + ls;
227 		for( y = 1; y < ovl.height; y++ ) {
228 			memcpy( q, to, ws );
229 			q += ls;
230 		}
231 	}
232 }
233 
234 /* Blend two guint8.
235  */
236 #define BLEND( X, aX, Y, aY, scale ) \
237 	(((X * aX + Y * aY) * scale + (1 << 12)) >> 24)
238 
239 /* Extract R, G, B, A, assuming little-endian.
240  */
241 #define getR( V ) (V & 0xff)
242 #define getG( V ) ((V >> 8) & 0xff)
243 #define getB( V ) ((V >> 16) & 0xff)
244 #define getA( V ) ((V >> 24) & 0xff)
245 
246 /* Rebuild RGBA, assuming little-endian.
247  */
248 #define setRGBA( R, G, B, A ) \
249 	(R | (G << 8) | (B << 16) | ((guint32) A << 24))
250 
251 /* OVER blend of two unpremultiplied RGBA guint32
252  *
253  * We assume little-endian (x86), add a byteswap before this if necessary.
254  */
255 static guint32
blend_pixel(guint32 A,guint32 B)256 blend_pixel( guint32 A, guint32 B )
257 {
258 	guint8 aA = getA( A );
259 
260 	if( aA == 0 )
261 		return( B );
262 
263 	guint8 aB = getA( B );
264 
265 	guint8 fac = (aB * (255 - aA) + 127) >> 8;
266 	guint8 aR =  aA + fac;
267 	int scale = aR == 0 ? 0 : (1 << 24) / aR;
268 
269 	guint8 rR = BLEND( getR( A ), aA, getR( B ), fac, scale );
270 	guint8 gR = BLEND( getG( A ), aA, getG( B ), fac, scale );
271 	guint8 bR = BLEND( getB( A ), aA, getB( B ), fac, scale );
272 
273 	return( setRGBA( rR, gR, bR, aR ) );
274 }
275 
276 /* Blend sub into frame at left, top.
277  */
278 static void
vips_image_paint_image(VipsImage * frame,VipsImage * sub,int left,int top,gboolean blend)279 vips_image_paint_image( VipsImage *frame,
280 	VipsImage *sub, int left, int top, gboolean blend )
281 {
282 	VipsRect frame_rect = { 0, 0, frame->Xsize, frame->Ysize };
283 	VipsRect sub_rect = { left, top, sub->Xsize, sub->Ysize };
284 	int ps = VIPS_IMAGE_SIZEOF_PEL( frame );
285 
286 	VipsRect ovl;
287 
288 	g_assert( VIPS_IMAGE_SIZEOF_PEL( sub ) == ps );
289 
290 	vips_rect_intersectrect( &frame_rect, &sub_rect, &ovl );
291 	if( !vips_rect_isempty( &ovl ) ) {
292 		VipsPel *p, *q;
293 		int x, y;
294 
295 		p = VIPS_IMAGE_ADDR( sub, ovl.left - left, ovl.top - top );
296 		q = VIPS_IMAGE_ADDR( frame, ovl.left, ovl.top );
297 
298 		for( y = 0; y < ovl.height; y++ ) {
299 			if( blend ) {
300 				guint32 *A = (guint32 *) p;
301 				guint32 *B = (guint32 *) q;
302 
303 				for( x = 0; x < ovl.width; x++ )
304 					B[x] = blend_pixel( A[x], B[x] );
305 			}
306 			else
307 				memcpy( (char *) q, (char *) p,
308 					ovl.width * ps );
309 
310 			p += VIPS_IMAGE_SIZEOF_LINE( sub );
311 			q += VIPS_IMAGE_SIZEOF_LINE( frame );
312 		}
313 	}
314 }
315 
316 int
vips__iswebp_source(VipsSource * source)317 vips__iswebp_source( VipsSource *source )
318 {
319 	const unsigned char *p;
320 
321 	/* WebP is "RIFF xxxx WEBP" at the start, so we need 12 bytes.
322 	 */
323 	if( (p = vips_source_sniff( source, 12 )) &&
324 		vips_isprefix( "RIFF", (char *) p ) &&
325 		vips_isprefix( "WEBP", (char *) p + 8 ) )
326 		return( 1 );
327 
328 	return( 0 );
329 }
330 
331 static int
read_free(Read * read)332 read_free( Read *read )
333 {
334 	WebPDemuxReleaseIterator( &read->iter );
335 	VIPS_UNREF( read->frame );
336 	VIPS_FREEF( WebPDemuxDelete, read->demux );
337 	WebPFreeDecBuffer( &read->config.output );
338 
339 	VIPS_UNREF( read->source );
340 	VIPS_FREE( read->delays );
341 	VIPS_FREE( read );
342 
343 	return( 0 );
344 }
345 
346 static void
read_close_cb(VipsImage * image,Read * read)347 read_close_cb( VipsImage *image, Read *read )
348 {
349 	read_free( read );
350 }
351 
352 static Read *
read_new(VipsImage * out,VipsSource * source,int page,int n,double scale)353 read_new( VipsImage *out, VipsSource *source, int page, int n, double scale )
354 {
355 	Read *read;
356 
357 	if( !(read = VIPS_NEW( NULL, Read )) )
358 		return( NULL );
359 
360 	read->out = out;
361 	read->source = source;
362 	g_object_ref( source );
363 	read->page = page;
364 	read->n = n;
365 	read->scale = scale;
366 	read->delays = NULL;
367 	read->demux = NULL;
368 	read->frame = NULL;
369 	read->dispose_method = WEBP_MUX_DISPOSE_NONE;
370 	read->frame_no = 0;
371 
372 	/* Everything has to stay open until read has finished, unfortunately,
373 	 * since webp relies on us mapping the whole file.
374 	 */
375 	g_signal_connect( out, "close",
376 		G_CALLBACK( read_close_cb ), read );
377 
378 	WebPInitDecoderConfig( &read->config );
379 	read->config.options.use_threads = 1;
380 	read->config.output.is_external_memory = 1;
381 
382 	if( !(read->data.bytes =
383 		vips_source_map( source, &read->data.size )) )
384 		return( NULL );
385 
386 	return( read );
387 }
388 
389 /* Map vips metadata names to webp names.
390  */
391 const VipsWebPNames vips__webp_names[] = {
392 	{ VIPS_META_ICC_NAME, "ICCP", ICCP_FLAG },
393 	{ VIPS_META_EXIF_NAME, "EXIF", EXIF_FLAG },
394 	{ VIPS_META_XMP_NAME, "XMP ", XMP_FLAG }
395 };
396 const int vips__n_webp_names = VIPS_NUMBER( vips__webp_names );
397 
398 static int
read_header(Read * read,VipsImage * out)399 read_header( Read *read, VipsImage *out )
400 {
401 	int flags;
402 	int i;
403 
404 	if( !(read->demux = WebPDemux( &read->data )) ) {
405 		vips_error( "webp", "%s", _( "unable to parse image" ) );
406 		return( -1 );
407 	}
408 
409 	flags = WebPDemuxGetI( read->demux, WEBP_FF_FORMAT_FLAGS );
410 
411 	read->alpha = flags & ALPHA_FLAG;
412 
413 	/* We do everything as RGBA and then, if we can, drop the alpha on
414 	 * save.
415 	 */
416 	read->config.output.colorspace = MODE_RGBA;
417 
418 	read->canvas_width =
419 		WebPDemuxGetI( read->demux, WEBP_FF_CANVAS_WIDTH );
420 	read->canvas_height =
421 		WebPDemuxGetI( read->demux, WEBP_FF_CANVAS_HEIGHT );
422 
423 	if( flags & ANIMATION_FLAG ) {
424 		int loop_count;
425 		WebPIterator iter;
426 
427 		loop_count = WebPDemuxGetI( read->demux, WEBP_FF_LOOP_COUNT );
428 		read->frame_count = WebPDemuxGetI( read->demux,
429 			WEBP_FF_FRAME_COUNT );
430 
431 #ifdef DEBUG
432 		printf( "webp2vips: animation\n" );
433 		printf( "webp2vips: loop_count = %d\n", loop_count );
434 		printf( "webp2vips: frame_count = %d\n", read->frame_count );
435 #endif /*DEBUG*/
436 
437 		vips_image_set_int( out, "loop", loop_count );
438 
439 		/* DEPRECATED "gif-loop"
440 		 *
441 		 * Not the correct behavior as loop=1 became gif-loop=0
442 		 * but we want to keep the old behavior untouched!
443 		 */
444 		vips_image_set_int( out, "gif-loop",
445 			loop_count == 0 ? 0 : loop_count - 1 );
446 
447 		if( WebPDemuxGetFrame( read->demux, 1, &iter ) ) {
448 			int i;
449 
450 			read->delays = (int *)
451 				g_malloc0( read->frame_count * sizeof( int ) );
452 			for( i = 0; i < read->frame_count; i++ )
453 				read->delays[i] = 40;
454 
455 			do {
456 				g_assert( iter.frame_num >= 1 &&
457 					iter.frame_num <= read->frame_count );
458 
459 				read->delays[iter.frame_num - 1] =
460 					iter.duration;
461 
462 				/* We need the alpha in an animation if:
463 				 *   - any frame has transparent pixels
464 				 *   - any frame doesn't fill the whole canvas.
465 				 */
466 				if( iter.has_alpha ||
467 					iter.width != read->canvas_width ||
468 					iter.height != read->canvas_height )
469 					read->alpha = TRUE;
470 
471 				/* We must disable shrink-on-load if any frame
472 				 * does not fill the whole canvas. We won't be
473 				 * able to shrink-on-load it to the exact
474 				 * position in a downsized canvas.
475 				 */
476 				if( iter.width != read->canvas_width ||
477 					iter.height != read->canvas_height )
478 					read->scale = 1.0;
479 			} while( WebPDemuxNextFrame( &iter ) );
480 
481 			vips_image_set_array_int( out,
482 				"delay", read->delays, read->frame_count );
483 
484 			/* webp uses ms for delays, gif uses centiseconds.
485 			 */
486 			vips_image_set_int( out, "gif-delay",
487 				VIPS_RINT( read->delays[0] / 10.0 ) );
488 		}
489 
490 		WebPDemuxReleaseIterator( &iter );
491 
492 		if( read->n == -1 )
493 			read->n = read->frame_count - read->page;
494 
495 		if( read->page < 0 ||
496 			read->n <= 0 ||
497 			read->page + read->n > read->frame_count ) {
498 			vips_error( "webp",
499 				"%s", _( "bad page number" ) );
500 			return( -1 );
501 		}
502 
503 		/* Note that n-pages is the number of pages in the original,
504 		 * not the number of pages in the image we are writing.
505 		 */
506 		vips_image_set_int( out, VIPS_META_N_PAGES, read->frame_count );
507 	}
508 
509 	/* We round-to-nearest cf. pdfload etc.
510 	 */
511 	read->frame_width = VIPS_RINT( read->canvas_width * read->scale );
512 	read->frame_height = VIPS_RINT( read->canvas_height * read->scale );
513 
514 #ifdef DEBUG
515 	printf( "webp2vips: canvas_width = %d\n", read->canvas_width );
516 	printf( "webp2vips: canvas_height = %d\n", read->canvas_height );
517 	printf( "webp2vips: frame_width = %d\n", read->frame_width );
518 	printf( "webp2vips: frame_height = %d\n", read->frame_height );
519 #endif /*DEBUG*/
520 
521 	if( flags & ANIMATION_FLAG ) {
522 		/* Only set page-height if we have more than one page, or
523 		 * this could accidentally turn into an animated image later.
524 		 */
525 		if( read->n > 1 )
526 			vips_image_set_int( out,
527 				VIPS_META_PAGE_HEIGHT, read->frame_height );
528 
529 		read->width = read->frame_width;
530 		read->height = read->n * read->frame_height;
531 	}
532 	else {
533 		read->width = read->frame_width;
534 		read->height = read->frame_height;
535 		read->frame_count = 1;
536 	}
537 
538 	/* height can be huge if this is an animated webp image.
539 	 */
540 	if( read->width <= 0 ||
541 		read->height <= 0 ||
542 		read->width > 0x3FFF ||
543 		read->height >= VIPS_MAX_COORD ||
544 		read->frame_width <= 0 ||
545 		read->frame_height <= 0 ||
546 		read->frame_width > 0x3FFF ||
547 		read->frame_height > 0x3FFF ) {
548 		vips_error( "webp", "%s", _( "bad image dimensions" ) );
549 		return( -1 );
550 	}
551 
552 	for( i = 0; i < vips__n_webp_names; i++ ) {
553 		const char *vips = vips__webp_names[i].vips;
554 		const char *webp = vips__webp_names[i].webp;
555 
556 		if( flags & vips__webp_names[i].flags ) {
557 			WebPChunkIterator iter;
558 
559 			WebPDemuxGetChunk( read->demux, webp, 1, &iter );
560 			vips_image_set_blob_copy( out,
561 				vips, iter.chunk.bytes, iter.chunk.size );
562 			WebPDemuxReleaseChunkIterator( &iter );
563 		}
564 	}
565 
566 	/* The canvas is always RGBA, we drop alpha to RGB on output if we
567 	 * can.
568 	 */
569 	read->frame = vips_image_new_memory();
570 	vips_image_init_fields( read->frame,
571 		read->frame_width, read->frame_height, 4,
572 		VIPS_FORMAT_UCHAR, VIPS_CODING_NONE,
573 		VIPS_INTERPRETATION_sRGB,
574 		1.0, 1.0 );
575 	if( vips_image_pipelinev( read->frame,
576 		VIPS_DEMAND_STYLE_THINSTRIP, NULL ) ||
577 		vips_image_write_prepare( read->frame ) )
578 		return( -1 );
579 
580 	vips_image_init_fields( out,
581 		read->width, read->height,
582 		read->alpha ? 4 : 3,
583 		VIPS_FORMAT_UCHAR, VIPS_CODING_NONE,
584 		VIPS_INTERPRETATION_sRGB,
585 		1.0, 1.0 );
586 	if( vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ) )
587 		return( -1 );
588 	VIPS_SETSTR( out->filename,
589 		vips_connection_filename( VIPS_CONNECTION( read->source ) ) );
590 
591 	if( !WebPDemuxGetFrame( read->demux, 1, &read->iter ) ) {
592 		vips_error( "webp",
593 			"%s", _( "unable to loop through frames" ) );
594 		return( -1 );
595 	}
596 
597 	return( 0 );
598 }
599 
600 /* Read a single frame -- a width * height block of pixels. This will get
601  * blended into the accumulator at some offset.
602  */
603 static VipsImage *
read_frame(Read * read,int width,int height,const guint8 * data,size_t length)604 read_frame( Read *read,
605 	int width, int height, const guint8 *data, size_t length )
606 {
607 	VipsImage *frame;
608 
609 #ifdef DEBUG
610 	printf( "read_frame:\n" );
611 #endif /*DEBUG*/
612 
613 	frame = vips_image_new_memory();
614 	vips_image_init_fields( frame,
615 		width, height, 4,
616 		VIPS_FORMAT_UCHAR, VIPS_CODING_NONE,
617 		VIPS_INTERPRETATION_sRGB,
618 		1.0, 1.0 );
619 	if( vips_image_pipelinev( frame, VIPS_DEMAND_STYLE_THINSTRIP, NULL ) ||
620 		vips_image_write_prepare( frame ) ) {
621 		g_object_unref( frame );
622 		return( NULL );
623 	}
624 
625 	read->config.output.u.RGBA.rgba = VIPS_IMAGE_ADDR( frame, 0, 0 );
626 	read->config.output.u.RGBA.stride = VIPS_IMAGE_SIZEOF_LINE( frame );
627 	read->config.output.u.RGBA.size = VIPS_IMAGE_SIZEOF_IMAGE( frame );
628 	if( read->scale != 1.0 ) {
629 		read->config.options.use_scaling = 1;
630 		read->config.options.scaled_width = width;
631 		read->config.options.scaled_height = height;
632 	}
633 
634 	if( WebPDecode( data, length, &read->config ) != VP8_STATUS_OK ) {
635 		g_object_unref( frame );
636 		vips_error( "webp2vips", "%s", _( "unable to read pixels" ) );
637 		return( NULL );
638 	}
639 
640 	return( frame );
641 }
642 
643 static int
read_next_frame(Read * read)644 read_next_frame( Read *read )
645 {
646 	VipsImage *frame;
647 	VipsRect area;
648 
649 #ifdef DEBUG
650 	printf( "read_next_frame:\n" );
651 #endif /*DEBUG*/
652 
653 	/* Area of this frame, in output image coordinates. We must rint(),
654 	 * since we need the same rules as the overall image scale, or we'll
655 	 * sometimes have missing pixels on edges.
656 	 */
657 	area.left = VIPS_RINT( read->iter.x_offset * read->scale );
658 	area.top = VIPS_RINT( read->iter.y_offset * read->scale );
659 	area.width = VIPS_RINT( read->iter.width * read->scale );
660 	area.height = VIPS_RINT( read->iter.height * read->scale );
661 
662 	/* Dispose from the previous frame.
663 	 */
664 	if( read->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND ) {
665 		/* We must clear the pixels occupied by the previous webp
666 		 * frame (not the whole of the read frame) to 0 (transparent).
667 		 *
668 		 * We do not clear to WEBP_FF_BACKGROUND_COLOR. That's only
669 		 * used to composite down to RGB. Perhaps we
670 		 * should attach background as metadata.
671 		 */
672 		guint32 zero = 0;
673 
674 		vips_image_paint_area( read->frame,
675 			&read->dispose_rect, (VipsPel *) &zero );
676 	}
677 
678 	/* Note this frame's dispose for next time.
679 	 */
680 	read->dispose_method = read->iter.dispose_method;
681 	read->dispose_rect = area;
682 
683 #ifdef DEBUG
684 	printf( "webp2vips: frame_num = %d\n", read->iter.frame_num );
685 	printf( "   left = %d\n", area.left );
686 	printf( "   top = %d\n", area.top );
687 	printf( "   width = %d\n", area.width );
688 	printf( "   height = %d\n", area.height );
689 	printf( "   duration = %d\n", read->iter.duration );
690 	printf( "   dispose = " );
691 	if( read->iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND )
692 		printf( "clear to background\n" );
693 	else
694 		printf( "none\n" );
695 	printf( "   has_alpha = %d\n", read->iter.has_alpha );
696 	printf( "   blend_method = " );
697 	if( read->iter.blend_method == WEBP_MUX_BLEND )
698 		printf( "blend with previous\n" );
699 	else
700 		printf( "don't blend\n" );
701 #endif /*DEBUG*/
702 
703 	if( !(frame = read_frame( read,
704 		area.width, area.height,
705 		read->iter.fragment.bytes, read->iter.fragment.size )) )
706 		return( -1 );
707 
708 	/* Now blend or copy the new pixels into our accumulator.
709 	 */
710 	vips_image_paint_image( read->frame, frame,
711 		area.left, area.top,
712 		read->iter.frame_num > 1 &&
713 			read->iter.blend_method == WEBP_MUX_BLEND );
714 
715 	g_object_unref( frame );
716 
717 	/* If there's another frame, move on.
718 	 */
719 	if( read->iter.frame_num < read->frame_count ) {
720 		if( !WebPDemuxNextFrame( &read->iter ) ) {
721 			vips_error( "webp2vips",
722 				"%s", _( "not enough frames" ) );
723 			return( -1 );
724 		}
725 	}
726 
727 	return( 0 );
728 }
729 
730 static int
read_webp_generate(VipsRegion * or,void * seq,void * a,void * b,gboolean * stop)731 read_webp_generate( VipsRegion *or,
732 	void *seq, void *a, void *b, gboolean *stop )
733 {
734         VipsRect *r = &or->valid;
735 	Read *read = (Read *) a;
736 
737 	/* iter.frame_num numbers from 1.
738 	 */
739 	int frame = 1 + r->top / read->frame_height + read->page;
740 	int line = r->top % read->frame_height;
741 
742 #ifdef DEBUG_VERBOSE
743 	printf( "read_webp_generate: line %d\n", r->top );
744 #endif /*DEBUG_VERBOSE*/
745 
746 	g_assert( r->height == 1 );
747 
748 	while( read->frame_no < frame ) {
749 		if( read_next_frame( read ) )
750 			return( -1 );
751 
752 		read->frame_no += 1;
753 	}
754 
755 	if( or->im->Bands == 4 )
756 		memcpy( VIPS_REGION_ADDR( or, 0, r->top ),
757 			VIPS_IMAGE_ADDR( read->frame, 0, line ),
758 			VIPS_IMAGE_SIZEOF_LINE( read->frame ) );
759 	else {
760 		int x;
761 		VipsPel *p;
762 		VipsPel *q;
763 
764 		/* We know that alpha is solid, so we can just drop the 4th
765 		 * band.
766 		 */
767 		p = VIPS_IMAGE_ADDR( read->frame, 0, line );
768 		q = VIPS_REGION_ADDR( or, 0, r->top );
769 		for( x = 0; x < r->width; x++ ) {
770 			q[0] = p[0];
771 			q[1] = p[1];
772 			q[2] = p[2];
773 
774 			q += 3;
775 			p += 4;
776 		}
777 	}
778 
779 	return( 0 );
780 }
781 
782 static int
read_image(Read * read,VipsImage * out)783 read_image( Read *read, VipsImage *out )
784 {
785 	VipsImage **t = (VipsImage **)
786 		vips_object_local_array( VIPS_OBJECT( out ), 3 );
787 
788 	t[0] = vips_image_new();
789 	if( read_header( read, t[0] ) )
790 		return( -1 );
791 
792 	if( vips_image_generate( t[0],
793 		NULL, read_webp_generate, NULL, read, NULL ) ||
794 		vips_sequential( t[0], &t[1], NULL ) ||
795 		vips_image_write( t[1], out ) )
796 		return( -1 );
797 
798 	return( 0 );
799 }
800 
801 int
vips__webp_read_header_source(VipsSource * source,VipsImage * out,int page,int n,double scale)802 vips__webp_read_header_source( VipsSource *source, VipsImage *out,
803 	int page, int n, double scale )
804 {
805 	Read *read;
806 
807 	if( !(read = read_new( out, source, page, n, scale )) ||
808 		read_header( read, out ) )
809 		return( -1 );
810 
811 	return( 0 );
812 }
813 
814 int
vips__webp_read_source(VipsSource * source,VipsImage * out,int page,int n,double scale)815 vips__webp_read_source( VipsSource *source, VipsImage *out,
816 	int page, int n, double scale )
817 {
818 	Read *read;
819 
820 	if( !(read = read_new( out, source, page, n, scale )) ||
821 		read_image( read, out ) )
822 		return( -1 );
823 
824 	return( 0 );
825 }
826 
827 #endif /*HAVE_LIBWEBP*/
828