diff --git a/ChangeLog b/ChangeLog index 5504aeca..acb81ccf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -13,6 +13,8 @@ - better handling of deprecated args in python - much better handling of arrayimage command-line args - faster hist_find (Lovell Fuller) +- webpload has a @shrink parameter for shrink-on-load +- vipsthumbnail knows about webp shrink-on-load 27/1/16 started 8.2.3 - fix a crash with SPARC byte-order labq vips images diff --git a/libvips/deprecated/im_webp2vips.c b/libvips/deprecated/im_webp2vips.c index 5bccd981..44ae9053 100644 --- a/libvips/deprecated/im_webp2vips.c +++ b/libvips/deprecated/im_webp2vips.c @@ -51,11 +51,11 @@ webp2vips( const char *name, IMAGE *out, gboolean header_only ) #ifdef HAVE_LIBWEBP if( header_only ) { - if( vips__webp_read_file_header( filename, out ) ) + if( vips__webp_read_file_header( filename, out, 1 ) ) return( -1 ); } else { - if( vips__webp_read_file( filename, out ) ) + if( vips__webp_read_file( filename, out, 1 ) ) return( -1 ); } #else diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index b51ab386..a701c9e4 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -2372,8 +2372,14 @@ vips_jpegsave_mime( VipsImage *in, ... ) * @out: decompressed image * @...: %NULL-terminated list of optional named arguments * + * Optional arguments: + * + * @shrink: %gint, shrink by this much on load + * * Read a WebP file into a VIPS image. * + * Use @shrink to specify a shrink-on-load factor. + * * See also: vips_image_new_from_file(). * * Returns: 0 on success, -1 on error. @@ -2398,6 +2404,10 @@ vips_webpload( const char *filename, VipsImage **out, ... ) * @out: image to write * @...: %NULL-terminated list of optional named arguments * + * Optional arguments: + * + * @shrink: %gint, shrink by this much on load + * * Read a WebP-formatted memory block into a VIPS image. Exactly as * vips_webpload(), but read from a memory buffer. * diff --git a/libvips/foreign/webp.h b/libvips/foreign/webp.h index 1f750ec5..83e8653b 100644 --- a/libvips/foreign/webp.h +++ b/libvips/foreign/webp.h @@ -40,13 +40,13 @@ extern const char *vips__webp_suffs[]; int vips__iswebp_buffer( const void *buf, size_t len ); int vips__iswebp( const char *filename ); -int vips__webp_read_file_header( const char *name, VipsImage *out ); -int vips__webp_read_file( const char *name, VipsImage *out ); +int vips__webp_read_file_header( const char *name, VipsImage *out, int shrink ); +int vips__webp_read_file( const char *name, VipsImage *out, int shrink ); int vips__webp_read_buffer_header( const void *buf, size_t len, - VipsImage *out ); + VipsImage *out, int shrink ); int vips__webp_read_buffer( const void *buf, size_t len, - VipsImage *out ); + VipsImage *out, int shrink ); int vips__webp_write_file( VipsImage *out, const char *filename, int Q, gboolean lossless ); diff --git a/libvips/foreign/webp2vips.c b/libvips/foreign/webp2vips.c index 722195b8..97fcba87 100644 --- a/libvips/foreign/webp2vips.c +++ b/libvips/foreign/webp2vips.c @@ -4,6 +4,8 @@ * - from png2vips.c * 24/2/14 * - oops, buffer path was broken, thanks Lovell + * 28/2/16 + * - add @shrink */ /* @@ -75,6 +77,15 @@ typedef struct { const void *data; gint64 length; + /* Shrink-on-load factor. Use this to set scaled_width. + */ + int shrink; + + /* Size we are decoding to. + */ + int width; + int height; + /* If we are opening a file object, the fd. */ int fd; @@ -132,7 +143,7 @@ read_free( Read *read ) } static Read * -read_new( const char *filename, const void *data, size_t length ) +read_new( const char *filename, const void *data, size_t length, int shrink ) { Read *read; @@ -142,6 +153,7 @@ read_new( const char *filename, const void *data, size_t length ) read->filename = g_strdup( filename ); read->data = data; read->length = length; + read->shrink = shrink; read->fd = 0; read->idec = NULL; @@ -171,7 +183,23 @@ read_new( const char *filename, const void *data, size_t length ) read->config.output.colorspace = MODE_RGBA; else read->config.output.colorspace = MODE_RGB; - read->config.options.use_threads = TRUE; + + read->config.options.use_threads = 1; + + read->width = read->config.input.width / read->shrink; + read->height = read->config.input.height / read->shrink; + + if( read->width == 0 || + read->height == 0 ) { + vips_error( "webp", "%s", _( "bad setting for shrink" ) ); + return( NULL ); + } + + if( read->shrink > 1 ) { + read->config.options.use_scaling = 1; + read->config.options.scaled_width = read->width; + read->config.options.scaled_height = read->height; + } return( read ); } @@ -180,7 +208,7 @@ static int read_header( Read *read, VipsImage *out ) { vips_image_init_fields( out, - read->config.input.width, read->config.input.height, + read->width, read->height, read->config.input.has_alpha ? 4 : 3, VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, @@ -192,11 +220,11 @@ read_header( Read *read, VipsImage *out ) } int -vips__webp_read_file_header( const char *filename, VipsImage *out ) +vips__webp_read_file_header( const char *filename, VipsImage *out, int shrink ) { Read *read; - if( !(read = read_new( filename, NULL, 0 )) ) { + if( !(read = read_new( filename, NULL, 0, shrink )) ) { vips_error( "webp2vips", _( "unable to open \"%s\"" ), filename ); return( -1 ); @@ -210,16 +238,11 @@ vips__webp_read_file_header( const char *filename, VipsImage *out ) return( 0 ); } -typedef uint8_t *(*webp_decoder)( const uint8_t* data, size_t data_size, - uint8_t* output_buffer, size_t output_buffer_size, - int output_stride ); - static int read_image( Read *read, VipsImage *out ) { VipsImage **t = (VipsImage **) vips_object_local_array( VIPS_OBJECT( out ), 3 ); - webp_decoder decoder; t[0] = vips_image_new_memory(); if( read_header( read, t[0] ) ) @@ -227,15 +250,13 @@ read_image( Read *read, VipsImage *out ) if( vips_image_write_prepare( t[0] ) ) return( -1 ); - if( t[0]->Bands == 3 ) - decoder = WebPDecodeRGBInto; - else - decoder = WebPDecodeRGBAInto; + read->config.output.u.RGBA.rgba = VIPS_IMAGE_ADDR( t[0], 0, 0 ); + read->config.output.u.RGBA.stride = VIPS_IMAGE_SIZEOF_LINE( t[0] ); + read->config.output.u.RGBA.size = VIPS_IMAGE_SIZEOF_IMAGE( t[0] ); + read->config.output.is_external_memory = 1; - if( !decoder( (uint8_t *) read->data, read->length, - VIPS_IMAGE_ADDR( t[0], 0, 0 ), - VIPS_IMAGE_SIZEOF_IMAGE( t[0] ), - VIPS_IMAGE_SIZEOF_LINE( t[0] ) ) ) { + if( WebPDecode( (uint8_t *) read->data, read->length, + &read->config) != VP8_STATUS_OK ) { vips_error( "webp2vips", "%s", _( "unable to read pixels" ) ); return( -1 ); } @@ -247,11 +268,11 @@ read_image( Read *read, VipsImage *out ) } int -vips__webp_read_file( const char *filename, VipsImage *out ) +vips__webp_read_file( const char *filename, VipsImage *out, int shrink ) { Read *read; - if( !(read = read_new( filename, NULL, 0 )) ) { + if( !(read = read_new( filename, NULL, 0, shrink )) ) { vips_error( "webp2vips", _( "unable to open \"%s\"" ), filename ); return( -1 ); @@ -266,11 +287,12 @@ vips__webp_read_file( const char *filename, VipsImage *out ) } int -vips__webp_read_buffer_header( const void *buf, size_t len, VipsImage *out ) +vips__webp_read_buffer_header( const void *buf, size_t len, VipsImage *out, + int shrink ) { Read *read; - if( !(read = read_new( NULL, buf, len )) ) { + if( !(read = read_new( NULL, buf, len, shrink )) ) { vips_error( "webp2vips", "%s", _( "unable to open buffer" ) ); return( -1 ); @@ -285,11 +307,12 @@ vips__webp_read_buffer_header( const void *buf, size_t len, VipsImage *out ) } int -vips__webp_read_buffer( const void *buf, size_t len, VipsImage *out ) +vips__webp_read_buffer( const void *buf, size_t len, VipsImage *out, + int shrink ) { Read *read; - if( !(read = read_new( NULL, buf, len )) ) { + if( !(read = read_new( NULL, buf, len, shrink )) ) { vips_error( "webp2vips", "%s", _( "unable to open buffer" ) ); return( -1 ); diff --git a/libvips/foreign/webpload.c b/libvips/foreign/webpload.c index cb7edcb8..0922c957 100644 --- a/libvips/foreign/webpload.c +++ b/libvips/foreign/webpload.c @@ -2,6 +2,8 @@ * * 6/8/13 * - from pngload.c + * 28/2/16 + * - add @shrink */ /* @@ -52,6 +54,9 @@ typedef struct _VipsForeignLoadWebp { VipsForeignLoad parent_object; + /* Shrink by this much during load. + */ + int shrink; } VipsForeignLoadWebp; typedef VipsForeignLoadClass VipsForeignLoadWebpClass; @@ -91,11 +96,19 @@ vips_foreign_load_webp_class_init( VipsForeignLoadWebpClass *class ) load_class->get_flags = vips_foreign_load_webp_get_flags; + VIPS_ARG_INT( class, "shrink", 10, + _( "Shrink" ), + _( "Shrink factor on load" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsForeignLoadWebp, shrink ), + 1, 1024, 1 ); + } static void vips_foreign_load_webp_init( VipsForeignLoadWebp *webp ) { + webp->shrink = 1; } typedef struct _VipsForeignLoadWebpFile { @@ -127,9 +140,11 @@ vips_foreign_load_webp_file_is_a( const char *filename ) static int vips_foreign_load_webp_file_header( VipsForeignLoad *load ) { + VipsForeignLoadWebp *webp = (VipsForeignLoadWebp *) load; VipsForeignLoadWebpFile *file = (VipsForeignLoadWebpFile *) load; - if( vips__webp_read_file_header( file->filename, load->out ) ) + if( vips__webp_read_file_header( file->filename, load->out, + webp->shrink ) ) return( -1 ); VIPS_SETSTR( load->out->filename, file->filename ); @@ -140,9 +155,10 @@ vips_foreign_load_webp_file_header( VipsForeignLoad *load ) static int vips_foreign_load_webp_file_load( VipsForeignLoad *load ) { + VipsForeignLoadWebp *webp = (VipsForeignLoadWebp *) load; VipsForeignLoadWebpFile *file = (VipsForeignLoadWebpFile *) load; - if( vips__webp_read_file( file->filename, load->real ) ) + if( vips__webp_read_file( file->filename, load->real, webp->shrink ) ) return( -1 ); return( 0 ); @@ -202,10 +218,11 @@ G_DEFINE_TYPE( VipsForeignLoadWebpBuffer, vips_foreign_load_webp_buffer, static int vips_foreign_load_webp_buffer_header( VipsForeignLoad *load ) { + VipsForeignLoadWebp *webp = (VipsForeignLoadWebp *) load; VipsForeignLoadWebpBuffer *buffer = (VipsForeignLoadWebpBuffer *) load; if( vips__webp_read_buffer_header( buffer->buf->data, - buffer->buf->length, load->out ) ) + buffer->buf->length, load->out, webp->shrink ) ) return( -1 ); return( 0 ); @@ -214,10 +231,11 @@ vips_foreign_load_webp_buffer_header( VipsForeignLoad *load ) static int vips_foreign_load_webp_buffer_load( VipsForeignLoad *load ) { + VipsForeignLoadWebp *webp = (VipsForeignLoadWebp *) load; VipsForeignLoadWebpBuffer *buffer = (VipsForeignLoadWebpBuffer *) load; if( vips__webp_read_buffer( buffer->buf->data, buffer->buf->length, - load->real ) ) + load->real, webp->shrink ) ) return( -1 ); return( 0 ); diff --git a/tools/vipsthumbnail.c b/tools/vipsthumbnail.c index b1d34caa..f3d0b51d 100644 --- a/tools/vipsthumbnail.c +++ b/tools/vipsthumbnail.c @@ -73,6 +73,8 @@ * 9/2/16 * - add PDF --size support * - add SVG --size support + * 28/2/16 + * - add webp --shrink support */ #ifdef HAVE_CONFIG_H @@ -327,6 +329,30 @@ thumbnail_open( VipsObject *process, const char *filename ) return( NULL ); } + else if( strcmp( loader, "VipsForeignLoadWebpFile" ) == 0 ) { + double shrink; + + /* This will just read in the header and is quick. + */ + if( !(im = vips_image_new_from_file( filename, NULL )) ) + return( NULL ); + + shrink = calculate_shrink( im ); + + g_object_unref( im ); + + vips_info( "vipsthumbnail", + "loading webp with factor %g pre-shrink", + shrink ); + + /* We can't use UNBUFERRED safely on very-many-core systems. + */ + if( !(im = vips_image_new_from_file( filename, + "access", VIPS_ACCESS_SEQUENTIAL, + "shrink", (int) shrink, + NULL )) ) + return( NULL ); + } else { /* All other formats. We can't use UNBUFERRED safely on * very-many-core systems.