disable webp shrink-on-load if it will judder

Webp decode can only shrink-on-load to int boundaries. This means that frames
in an animation which only update part of the canvas can get displaced by
up to 0.5 pixels, causing juddering.

see https://github.com/libvips/libvips/issues/2379
This commit is contained in:
John Cupitt 2021-10-27 13:25:04 +01:00
parent e815e8ad95
commit 1f321d366b
2 changed files with 36 additions and 22 deletions

View File

@ -26,6 +26,8 @@
* - support array of delays
* 14/10/19
* - revise for source IO
* 27/10/21
* - disable shrink-on-load if we need subpixel accuracy in animations
*/
/*
@ -96,7 +98,7 @@ typedef struct {
*/
int n;
/* Scale-on-load factor. Use this to set scaled_width.
/* Scale-on-load factor. Use this to set frame_width.
*/
double scale;
@ -404,23 +406,6 @@ read_header( Read *read, VipsImage *out )
return( -1 );
}
read->canvas_width =
WebPDemuxGetI( read->demux, WEBP_FF_CANVAS_WIDTH );
read->canvas_height =
WebPDemuxGetI( read->demux, WEBP_FF_CANVAS_HEIGHT );
/* We round-to-nearest cf. pdfload etc.
*/
read->frame_width = VIPS_RINT( read->canvas_width * read->scale );
read->frame_height = VIPS_RINT( read->canvas_height * read->scale );
#ifdef DEBUG
printf( "webp2vips: canvas_width = %d\n", read->canvas_width );
printf( "webp2vips: canvas_height = %d\n", read->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 );
read->alpha = flags & ALPHA_FLAG;
@ -430,6 +415,11 @@ read_header( Read *read, VipsImage *out )
*/
read->config.output.colorspace = MODE_RGBA;
read->canvas_width =
WebPDemuxGetI( read->demux, WEBP_FF_CANVAS_WIDTH );
read->canvas_height =
WebPDemuxGetI( read->demux, WEBP_FF_CANVAS_HEIGHT );
if( flags & ANIMATION_FLAG ) {
int loop_count;
WebPIterator iter;
@ -453,9 +443,6 @@ read_header( Read *read, VipsImage *out )
*/
vips_image_set_int( out, "gif-loop",
loop_count == 0 ? 0 : loop_count - 1 );
vips_image_set_int( out,
VIPS_META_PAGE_HEIGHT, read->frame_height );
if( WebPDemuxGetFrame( read->demux, 1, &iter ) ) {
int i;
@ -480,6 +467,15 @@ read_header( Read *read, VipsImage *out )
iter.width != read->canvas_width ||
iter.height != read->canvas_height )
read->alpha = TRUE;
/* We must disable shrink-on-load if any frame
* does not fill the whole canvas. We won't be
* able to shrink-on-load it to the exact
* position in a downsized canvas.
*/
if( iter.width != read->canvas_width ||
iter.height != read->canvas_height )
read->scale = 1.0;
} while( WebPDemuxNextFrame( &iter ) );
vips_image_set_array_int( out,
@ -508,6 +504,23 @@ read_header( Read *read, VipsImage *out )
* not the number of pages in the image we are writing.
*/
vips_image_set_int( out, VIPS_META_N_PAGES, read->frame_count );
}
/* We round-to-nearest cf. pdfload etc.
*/
read->frame_width = VIPS_RINT( read->canvas_width * read->scale );
read->frame_height = VIPS_RINT( read->canvas_height * read->scale );
#ifdef DEBUG
printf( "webp2vips: canvas_width = %d\n", read->canvas_width );
printf( "webp2vips: canvas_height = %d\n", read->canvas_height );
printf( "webp2vips: frame_width = %d\n", read->frame_width );
printf( "webp2vips: frame_height = %d\n", read->frame_height );
#endif /*DEBUG*/
if( flags & ANIMATION_FLAG ) {
vips_image_set_int( out,
VIPS_META_PAGE_HEIGHT, read->frame_height );
read->width = read->frame_width;
read->height = read->n * read->frame_height;

View File

@ -461,7 +461,8 @@ vips_foreign_load_webp_buffer_init( VipsForeignLoadWebpBuffer *buffer )
* to change page layout.
*
* Use @scale to specify a scale-on-load factor. For example, 2.0 to double
* the size on load.
* the size on load. Animated webp images don't support shrink-on-load, so a
* further resize may be necessary.
*
* The loader supports ICC, EXIF and XMP metadata.
*