From b161f4b982b426aa980f11096eb6dad1b6a2dbcb Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 4 Nov 2018 19:58:06 +0000 Subject: [PATCH] clear to background on init --- libvips/foreign/webp2vips.c | 596 ++++++++++++++++++------------------ 1 file changed, 302 insertions(+), 294 deletions(-) diff --git a/libvips/foreign/webp2vips.c b/libvips/foreign/webp2vips.c index f181dfda..4e045791 100644 --- a/libvips/foreign/webp2vips.c +++ b/libvips/foreign/webp2vips.c @@ -140,300 +140,6 @@ typedef struct { WebPIterator iter; } Read; -int -vips__iswebp_buffer( const void *buf, size_t len ) -{ - /* WebP is "RIFF xxxx WEBP" at the start, so we need 12 bytes. - */ - if( len >= 12 && - vips_isprefix( "RIFF", (char *) buf ) && - vips_isprefix( "WEBP", (char *) buf + 8 ) ) - return( 1 ); - - return( 0 ); -} - -int -vips__iswebp( const char *filename ) -{ - /* Magic number, see above. - */ - unsigned char header[12]; - - if( vips__get_bytes( filename, header, 12 ) == 12 && - vips__iswebp_buffer( header, 12 ) ) - return( 1 ); - - return( 0 ); -} - -static int -read_free( Read *read ) -{ - WebPDemuxReleaseIterator( &read->iter ); - VIPS_UNREF( read->frame ); - VIPS_FREEF( WebPDemuxDelete, read->demux ); - WebPFreeDecBuffer( &read->config.output ); - - if( read->fd > 0 && - read->data && - read->length > 0 ) { - vips__munmap( read->data, read->length ); - read->data = NULL; - read->length = 0; - } - - VIPS_FREEF( vips_tracked_close, read->fd ); - VIPS_FREE( read->filename ); - VIPS_FREE( read ); - - return( 0 ); -} - -static Read * -read_new( const char *filename, const void *data, size_t length, - int page, int n, int shrink ) -{ - Read *read; - - if( !(read = VIPS_NEW( NULL, Read )) ) - return( NULL ); - - read->filename = g_strdup( filename ); - read->data = data; - read->length = length; - read->page = page; - read->n = n; - read->shrink = shrink; - read->delay = 100; - read->fd = 0; - read->demux = NULL; - read->frame = NULL; - - if( read->filename ) { - /* libwebp makes streaming from a file source very hard. We - * have to read to a full memory buffer, then copy to out. - * - * mmap the input file, it's slightly quicker. - */ - if( (read->fd = vips__open_image_read( read->filename )) < 0 || - (read->length = vips_file_length( read->fd )) < 0 || - !(read->data = vips__mmap( read->fd, - FALSE, read->length, 0 )) ) { - read_free( read ); - return( NULL ); - } - } - - WebPInitDecoderConfig( &read->config ); - read->config.options.use_threads = 1; - read->config.output.is_external_memory = 1; - - return( read ); -} - -/* Map vips metadata names to webp names. - */ -const VipsWebPNames vips__webp_names[] = { - { VIPS_META_ICC_NAME, "ICCP", ICCP_FLAG }, - { VIPS_META_EXIF_NAME, "EXIF", EXIF_FLAG }, - { VIPS_META_XMP_NAME, "XMP ", XMP_FLAG } -}; -const int vips__n_webp_names = VIPS_NUMBER( vips__webp_names ); - -static int -read_header( Read *read, VipsImage *out ) -{ - WebPData data; - int canvas_width; - int canvas_height; - int flags; - int i; - - data.bytes = read->data; - data.size = read->length; - if( !(read->demux = WebPDemux( &data )) ) { - vips_error( "webp", "%s", _( "unable to parse image" ) ); - return( -1 ); - } - - canvas_width = WebPDemuxGetI( read->demux, WEBP_FF_CANVAS_WIDTH ); - canvas_height = WebPDemuxGetI( read->demux, WEBP_FF_CANVAS_HEIGHT ); - read->frame_width = canvas_width / read->shrink; - read->frame_height = canvas_height / read->shrink; - - if( read->shrink > 1 ) { - read->config.options.use_scaling = 1; - read->config.options.scaled_width = read->frame_width; - read->config.options.scaled_height = read->frame_height; - } - - flags = WebPDemuxGetI( read->demux, WEBP_FF_FORMAT_FLAGS ); - - /* FIXME ... do we need to byteswap, or can we use &background as the - * ink? - */ - read->background = WebPDemuxGetI( read->demux, - WEBP_FF_BACKGROUND_COLOR ); - - read->alpha = flags & ALPHA_FLAG; - if( read->alpha ) - read->config.output.colorspace = MODE_RGBA; - else - read->config.output.colorspace = MODE_RGB; - - if( flags & ANIMATION_FLAG ) { - int loop_count; - WebPIterator iter; - - loop_count = WebPDemuxGetI( read->demux, WEBP_FF_LOOP_COUNT ); - read->frame_count = WebPDemuxGetI( read->demux, - WEBP_FF_FRAME_COUNT ); - -#ifdef DEBUG - printf( "webp2vips: animation\n" ); - printf( "webp2vips: loop_count = %d\n", loop_count ); - printf( "webp2vips: frame_count = %d\n", read->frame_count ); -#endif /*DEBUG*/ - - vips_image_set_int( out, "gif-loop", loop_count ); - vips_image_set_int( out, "page-height", read->frame_height ); - - /* We must get the first frame to get the delay. - */ - if( WebPDemuxGetFrame( read->demux, 1, &iter ) ) { - read->delay = iter.duration; - -#ifdef DEBUG - printf( "webp2vips: duration = %d\n", read->delay ); -#endif /*DEBUG*/ - - vips_image_set_int( out, "gif-delay", read->delay ); - WebPDemuxReleaseIterator( &iter ); - } - - read->width = read->frame_width; - read->height = read->frame_count * read->frame_height; - } - else { - read->width = read->frame_width; - read->height = read->frame_height; - read->frame_count = 1; - } - - if( read->width <= 0 || - read->height <= 0 ) { - vips_error( "webp", "%s", _( "bad image dimensions" ) ); - return( -1 ); - } - - for( i = 0; i < vips__n_webp_names; i++ ) { - const char *vips = vips__webp_names[i].vips; - const char *webp = vips__webp_names[i].webp; - - if( flags & vips__webp_names[i].flags ) { - WebPChunkIterator iter; - void *blob; - - WebPDemuxGetChunk( read->demux, webp, 1, &iter ); - - if( !(blob = vips_malloc( NULL, iter.chunk.size )) ) { - WebPDemuxReleaseChunkIterator( &iter ); - return( -1 ); - } - memcpy( blob, iter.chunk.bytes, iter.chunk.size ); - vips_image_set_blob( out, vips, - (VipsCallbackFn) vips_free, - blob, iter.chunk.size ); - - WebPDemuxReleaseChunkIterator( &iter ); - } - } - - read->frame = vips_image_new_memory(); - vips_image_init_fields( read->frame, - read->frame_width, read->frame_height, - read->alpha ? 4 : 3, - VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, - VIPS_INTERPRETATION_sRGB, - 1.0, 1.0 ); - vips_image_pipelinev( read->frame, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); - - if( vips_image_write_prepare( read->frame ) ) - return( -1 ); - - vips_image_init_fields( out, - read->width, read->height, - read->alpha ? 4 : 3, - VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, - VIPS_INTERPRETATION_sRGB, - 1.0, 1.0 ); - vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); - - if( !WebPDemuxGetFrame( read->demux, 1, &read->iter ) ) { - vips_error( "webp", - "%s", _( "unable to loop through frames" ) ); - return( -1 ); - } - - return( 0 ); -} - -int -vips__webp_read_file_header( const char *filename, VipsImage *out, - int page, int n, int shrink ) -{ - Read *read; - - if( !(read = read_new( filename, NULL, 0, page, n, shrink )) ) { - vips_error( "webp2vips", - _( "unable to open \"%s\"" ), filename ); - return( -1 ); - } - - if( read_header( read, out ) ) { - read_free( read ); - return( -1 ); - } - - read_free( read ); - - return( 0 ); -} - -static VipsImage * -read_frame( Read *read, - int width, int height, const uint8_t *data, size_t length ) -{ - VipsImage *frame; - - frame = vips_image_new_memory(); - vips_image_init_fields( frame, - width, height, - read->alpha ? 4 : 3, - VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, - VIPS_INTERPRETATION_sRGB, - 1.0, 1.0 ); - vips_image_pipelinev( frame, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); - - if( vips_image_write_prepare( frame ) ) { - g_object_unref( frame ); - return( NULL ); - } - - 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( WebPDecode( data, length, &read->config ) != VP8_STATUS_OK ) { - g_object_unref( frame ); - vips_error( "webp2vips", "%s", _( "unable to read pixels" ) ); - return( NULL ); - } - - return( frame ); -} - static void vips_image_paint_pel( VipsImage *image, const VipsRect *r, const VipsPel *ink ) { @@ -558,6 +264,308 @@ vips_image_paint_image( VipsImage *image, } } +int +vips__iswebp_buffer( const void *buf, size_t len ) +{ + /* WebP is "RIFF xxxx WEBP" at the start, so we need 12 bytes. + */ + if( len >= 12 && + vips_isprefix( "RIFF", (char *) buf ) && + vips_isprefix( "WEBP", (char *) buf + 8 ) ) + return( 1 ); + + return( 0 ); +} + +int +vips__iswebp( const char *filename ) +{ + /* Magic number, see above. + */ + unsigned char header[12]; + + if( vips__get_bytes( filename, header, 12 ) == 12 && + vips__iswebp_buffer( header, 12 ) ) + return( 1 ); + + return( 0 ); +} + +static int +read_free( Read *read ) +{ + WebPDemuxReleaseIterator( &read->iter ); + VIPS_UNREF( read->frame ); + VIPS_FREEF( WebPDemuxDelete, read->demux ); + WebPFreeDecBuffer( &read->config.output ); + + if( read->fd > 0 && + read->data && + read->length > 0 ) { + vips__munmap( read->data, read->length ); + read->data = NULL; + read->length = 0; + } + + VIPS_FREEF( vips_tracked_close, read->fd ); + VIPS_FREE( read->filename ); + VIPS_FREE( read ); + + return( 0 ); +} + +static Read * +read_new( const char *filename, const void *data, size_t length, + int page, int n, int shrink ) +{ + Read *read; + + if( !(read = VIPS_NEW( NULL, Read )) ) + return( NULL ); + + read->filename = g_strdup( filename ); + read->data = data; + read->length = length; + read->page = page; + read->n = n; + read->shrink = shrink; + read->delay = 100; + read->fd = 0; + read->demux = NULL; + read->frame = NULL; + + if( read->filename ) { + /* libwebp makes streaming from a file source very hard. We + * have to read to a full memory buffer, then copy to out. + * + * mmap the input file, it's slightly quicker. + */ + if( (read->fd = vips__open_image_read( read->filename )) < 0 || + (read->length = vips_file_length( read->fd )) < 0 || + !(read->data = vips__mmap( read->fd, + FALSE, read->length, 0 )) ) { + read_free( read ); + return( NULL ); + } + } + + WebPInitDecoderConfig( &read->config ); + read->config.options.use_threads = 1; + read->config.output.is_external_memory = 1; + + return( read ); +} + +/* Map vips metadata names to webp names. + */ +const VipsWebPNames vips__webp_names[] = { + { VIPS_META_ICC_NAME, "ICCP", ICCP_FLAG }, + { VIPS_META_EXIF_NAME, "EXIF", EXIF_FLAG }, + { VIPS_META_XMP_NAME, "XMP ", XMP_FLAG } +}; +const int vips__n_webp_names = VIPS_NUMBER( vips__webp_names ); + +static int +read_header( Read *read, VipsImage *out ) +{ + WebPData data; + int canvas_width; + int canvas_height; + int flags; + int i; + VipsRect area; + + data.bytes = read->data; + data.size = read->length; + if( !(read->demux = WebPDemux( &data )) ) { + vips_error( "webp", "%s", _( "unable to parse image" ) ); + return( -1 ); + } + + canvas_width = WebPDemuxGetI( read->demux, WEBP_FF_CANVAS_WIDTH ); + canvas_height = WebPDemuxGetI( read->demux, WEBP_FF_CANVAS_HEIGHT ); + read->frame_width = canvas_width / read->shrink; + read->frame_height = canvas_height / read->shrink; + + if( read->shrink > 1 ) { + read->config.options.use_scaling = 1; + read->config.options.scaled_width = read->frame_width; + read->config.options.scaled_height = read->frame_height; + } + + flags = WebPDemuxGetI( read->demux, WEBP_FF_FORMAT_FLAGS ); + + /* FIXME ... do we need to byteswap, or can we use &background as the + * ink? + */ + read->background = WebPDemuxGetI( read->demux, + WEBP_FF_BACKGROUND_COLOR ); + + read->alpha = flags & ALPHA_FLAG; + if( read->alpha ) + read->config.output.colorspace = MODE_RGBA; + else + read->config.output.colorspace = MODE_RGB; + + if( flags & ANIMATION_FLAG ) { + int loop_count; + WebPIterator iter; + + loop_count = WebPDemuxGetI( read->demux, WEBP_FF_LOOP_COUNT ); + read->frame_count = WebPDemuxGetI( read->demux, + WEBP_FF_FRAME_COUNT ); + +#ifdef DEBUG + printf( "webp2vips: animation\n" ); + printf( "webp2vips: loop_count = %d\n", loop_count ); + printf( "webp2vips: frame_count = %d\n", read->frame_count ); +#endif /*DEBUG*/ + + vips_image_set_int( out, "gif-loop", loop_count ); + vips_image_set_int( out, "page-height", read->frame_height ); + + /* We must get the first frame to get the delay. + */ + if( WebPDemuxGetFrame( read->demux, 1, &iter ) ) { + read->delay = iter.duration; + +#ifdef DEBUG + printf( "webp2vips: duration = %d\n", read->delay ); +#endif /*DEBUG*/ + + vips_image_set_int( out, "gif-delay", read->delay ); + WebPDemuxReleaseIterator( &iter ); + } + + read->width = read->frame_width; + read->height = read->frame_count * read->frame_height; + } + else { + read->width = read->frame_width; + read->height = read->frame_height; + read->frame_count = 1; + } + + if( read->width <= 0 || + read->height <= 0 ) { + vips_error( "webp", "%s", _( "bad image dimensions" ) ); + return( -1 ); + } + + for( i = 0; i < vips__n_webp_names; i++ ) { + const char *vips = vips__webp_names[i].vips; + const char *webp = vips__webp_names[i].webp; + + if( flags & vips__webp_names[i].flags ) { + WebPChunkIterator iter; + void *blob; + + WebPDemuxGetChunk( read->demux, webp, 1, &iter ); + + if( !(blob = vips_malloc( NULL, iter.chunk.size )) ) { + WebPDemuxReleaseChunkIterator( &iter ); + return( -1 ); + } + memcpy( blob, iter.chunk.bytes, iter.chunk.size ); + vips_image_set_blob( out, vips, + (VipsCallbackFn) vips_free, + blob, iter.chunk.size ); + + WebPDemuxReleaseChunkIterator( &iter ); + } + } + + read->frame = vips_image_new_memory(); + vips_image_init_fields( read->frame, + read->frame_width, read->frame_height, + read->alpha ? 4 : 3, + VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, + VIPS_INTERPRETATION_sRGB, + 1.0, 1.0 ); + vips_image_pipelinev( read->frame, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); + + if( vips_image_write_prepare( read->frame ) ) + return( -1 ); + + area.left = 0; + area.top = 0; + area.width = read->frame_width; + area.height = read->frame_height; + vips_image_paint_pel( read->frame, + &area, (VipsPel *) &read->background ); + + vips_image_init_fields( out, + read->width, read->height, + read->alpha ? 4 : 3, + VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, + VIPS_INTERPRETATION_sRGB, + 1.0, 1.0 ); + vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); + + if( !WebPDemuxGetFrame( read->demux, 1, &read->iter ) ) { + vips_error( "webp", + "%s", _( "unable to loop through frames" ) ); + return( -1 ); + } + + return( 0 ); +} + +int +vips__webp_read_file_header( const char *filename, VipsImage *out, + int page, int n, int shrink ) +{ + Read *read; + + if( !(read = read_new( filename, NULL, 0, page, n, shrink )) ) { + vips_error( "webp2vips", + _( "unable to open \"%s\"" ), filename ); + return( -1 ); + } + + if( read_header( read, out ) ) { + read_free( read ); + return( -1 ); + } + + read_free( read ); + + return( 0 ); +} + +static VipsImage * +read_frame( Read *read, + int width, int height, const uint8_t *data, size_t length ) +{ + VipsImage *frame; + + frame = vips_image_new_memory(); + vips_image_init_fields( frame, + width, height, + read->alpha ? 4 : 3, + VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, + VIPS_INTERPRETATION_sRGB, + 1.0, 1.0 ); + vips_image_pipelinev( frame, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); + + if( vips_image_write_prepare( frame ) ) { + g_object_unref( frame ); + return( NULL ); + } + + 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( WebPDecode( data, length, &read->config ) != VP8_STATUS_OK ) { + g_object_unref( frame ); + vips_error( "webp2vips", "%s", _( "unable to read pixels" ) ); + return( NULL ); + } + + return( frame ); +} + static int read_next_frame( Read *read ) {