From b1c995b2d94fabf61ab5803ecd657e1c2e1cefe4 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 1 May 2019 16:25:13 +0100 Subject: [PATCH] more animated webp load fixes webpload scale on load works for animated images --- libvips/foreign/webp2vips.c | 113 ++++++++++++++++++++++++++---------- 1 file changed, 81 insertions(+), 32 deletions(-) diff --git a/libvips/foreign/webp2vips.c b/libvips/foreign/webp2vips.c index 0faf3b57..8a2a9146 100644 --- a/libvips/foreign/webp2vips.c +++ b/libvips/foreign/webp2vips.c @@ -153,6 +153,39 @@ typedef struct { VipsRect dispose_rect; } Read; +const char * +vips__error_webp( VP8StatusCode code ) +{ + switch( code ) { + case VP8_STATUS_OK: + return( "VP8_STATUS_OK" ); + + case VP8_STATUS_OUT_OF_MEMORY: + return( "VP8_STATUS_OUT_OF_MEMORY" ); + + case VP8_STATUS_INVALID_PARAM: + return( "VP8_STATUS_INVALID_PARAM" ); + + case VP8_STATUS_BITSTREAM_ERROR: + return( "VP8_STATUS_BITSTREAM_ERROR" ); + + case VP8_STATUS_UNSUPPORTED_FEATURE: + return( "VP8_STATUS_UNSUPPORTED_FEATURE" ); + + case VP8_STATUS_SUSPENDED: + return( "VP8_STATUS_SUSPENDED" ); + + case VP8_STATUS_USER_ABORT: + return( "VP8_STATUS_USER_ABORT" ); + + case VP8_STATUS_NOT_ENOUGH_DATA: + return( "VP8_STATUS_NOT_ENOUGH_DATA" ); + + default: + return( "" ); + } +} + static void vips_image_paint_area( VipsImage *image, const VipsRect *r, const VipsPel *ink ) { @@ -233,32 +266,34 @@ blend_pixel( guint32 A, guint32 B ) return( setRGBA( rR, gR, bR, aR ) ); } +/* Blend sub into frame at left, top. + */ static void -vips_image_paint_image( VipsImage *image, - VipsImage *ink, int x, int y, gboolean blend ) +vips_image_paint_image( VipsImage *frame, + VipsImage *sub, int left, int top, gboolean blend ) { - VipsRect valid = { 0, 0, image->Xsize, image->Ysize }; - VipsRect sub = { x, y, ink->Xsize, ink->Ysize }; - int ps = VIPS_IMAGE_SIZEOF_PEL( image ); + VipsRect frame_rect = { 0, 0, frame->Xsize, frame->Ysize }; + VipsRect sub_rect = { left, top, sub->Xsize, sub->Ysize }; + int ps = VIPS_IMAGE_SIZEOF_PEL( frame ); VipsRect ovl; - g_assert( VIPS_IMAGE_SIZEOF_PEL( ink ) == ps ); + g_assert( VIPS_IMAGE_SIZEOF_PEL( sub ) == ps ); /* Disable blend if we are not RGBA. */ - if( image->Bands != 4 ) + if( frame->Bands != 4 ) blend = FALSE; - vips_rect_intersectrect( &valid, &sub, &ovl ); + vips_rect_intersectrect( &frame_rect, &sub_rect, &ovl ); if( !vips_rect_isempty( &ovl ) ) { VipsPel *p, *q; - int i; + int x, y; - p = VIPS_IMAGE_ADDR( ink, ovl.left - x, ovl.top - y ); - q = VIPS_IMAGE_ADDR( image, ovl.left, ovl.top ); + p = VIPS_IMAGE_ADDR( sub, ovl.left - left, ovl.top - top ); + q = VIPS_IMAGE_ADDR( frame, ovl.left, ovl.top ); - for( i = 0; i < ovl.height; i++ ) { + for( y = 0; y < ovl.height; y++ ) { if( blend ) { guint32 *A = (guint32 *) p; guint32 *B = (guint32 *) q; @@ -270,8 +305,8 @@ vips_image_paint_image( VipsImage *image, memcpy( (char *) q, (char *) p, ovl.width * ps ); - p += VIPS_IMAGE_SIZEOF_LINE( ink ); - q += VIPS_IMAGE_SIZEOF_LINE( image ); + p += VIPS_IMAGE_SIZEOF_LINE( sub ); + q += VIPS_IMAGE_SIZEOF_LINE( frame ); } } } @@ -402,11 +437,12 @@ read_header( Read *read, VipsImage *out ) read->frame_width = VIPS_RINT( canvas_width * read->scale ); read->frame_height = VIPS_RINT( canvas_height * read->scale ); - if( read->scale != 1.0 ) { - read->config.options.use_scaling = 1; - read->config.options.scaled_width = read->frame_width; - read->config.options.scaled_height = read->frame_height; - } +#ifdef DEBUG + printf( "webp2vips: canvas_width = %d\n", canvas_width ); + printf( "webp2vips: canvas_height = %d\n", canvas_height ); + printf( "webp2vips: frame_width = %d\n", read->frame_width ); + printf( "webp2vips: frame_height = %d\n", read->frame_height ); +#endif /*DEBUG*/ flags = WebPDemuxGetI( read->demux, WEBP_FF_FORMAT_FLAGS ); @@ -546,6 +582,9 @@ vips__webp_read_file_header( const char *filename, VipsImage *out, return( 0 ); } +/* Read a single frame -- a width * height block of pixels. This will get + * blended into the accumulator at some offset. + */ static VipsImage * read_frame( Read *read, int width, int height, const guint8 *data, size_t length ) @@ -558,7 +597,7 @@ read_frame( Read *read, frame = vips_image_new_memory(); vips_image_init_fields( frame, - width, height, + width, height, read->alpha ? 4 : 3, VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, @@ -573,6 +612,11 @@ read_frame( Read *read, read->config.output.u.RGBA.rgba = VIPS_IMAGE_ADDR( frame, 0, 0 ); read->config.output.u.RGBA.stride = VIPS_IMAGE_SIZEOF_LINE( frame ); read->config.output.u.RGBA.size = VIPS_IMAGE_SIZEOF_IMAGE( frame ); + if( read->scale != 1.0 ) { + read->config.options.use_scaling = 1; + read->config.options.scaled_width = width; + read->config.options.scaled_height = height; + } if( WebPDecode( data, length, &read->config ) != VP8_STATUS_OK ) { g_object_unref( frame ); @@ -587,16 +631,24 @@ static int read_next_frame( Read *read ) { VipsImage *frame; + VipsRect area; #ifdef DEBUG printf( "read_next_frame:\n" ); #endif /*DEBUG*/ + /* Area of this frame, in output image coordinates. + */ + area.left = read->iter.x_offset * read->scale; + area.top = read->iter.y_offset * read->scale; + area.width = read->iter.width * read->scale; + area.height = read->iter.height * read->scale; + /* Dispose from the previous frame. */ if( read->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND ) { - /* We must clear the pixels occupied by this webp frame (not - * the whole of the read frame) to 0 (transparent). + /* We must clear the pixels occupied by the previous webp + * frame (not the whole of the read frame) to 0 (transparent). * * We do not clear to WEBP_FF_BACKGROUND_COLOR. That's only * used to composite down to RGB. Perhaps we @@ -611,17 +663,14 @@ read_next_frame( Read *read ) /* Note this frame's dispose for next time. */ read->dispose_method = read->iter.dispose_method; - read->dispose_rect.left = read->iter.x_offset * read->scale; - read->dispose_rect.top = read->iter.y_offset * read->scale; - read->dispose_rect.width = read->iter.width * read->scale; - read->dispose_rect.height = read->iter.height * read->scale; + read->dispose_rect = area; #ifdef DEBUG printf( "webp2vips: frame_num = %d\n", read->iter.frame_num ); - printf( " x_offset = %d\n", read->iter.x_offset ); - printf( " y_offset = %d\n", read->iter.y_offset ); - printf( " width = %d\n", read->iter.width ); - printf( " height = %d\n", read->iter.height ); + printf( " left = %d\n", area.left ); + printf( " top = %d\n", area.top ); + printf( " width = %d\n", area.width ); + printf( " height = %d\n", area.height ); printf( " duration = %d\n", read->iter.duration ); printf( " dispose = " ); if( read->iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND ) @@ -642,14 +691,14 @@ read_next_frame( Read *read ) "not all frames have equal duration" ); if( !(frame = read_frame( read, - read->iter.width, read->iter.height, + area.width, area.height, read->iter.fragment.bytes, read->iter.fragment.size )) ) return( -1 ); /* Now blend or copy the new pixels into our accumulator. */ vips_image_paint_image( read->frame, frame, - read->iter.x_offset, read->iter.y_offset, + area.left, area.top, read->iter.blend_method == WEBP_MUX_BLEND ); g_object_unref( frame );