From 165e7c97176f24ee2a131d546e75aac6beea919c Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 6 Aug 2013 15:51:23 +0100 Subject: [PATCH] basic webp load done --- ChangeLog | 1 + configure.ac | 4 +- libvips/foreign/webp2vips.c | 205 ++++++++++++++++++++++++++++++++++-- libvips/iofuncs/type.c | 2 +- 4 files changed, 198 insertions(+), 14 deletions(-) diff --git a/ChangeLog b/ChangeLog index 70bf3200..b3e7d218 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,7 @@ - added vips_error_freeze() / vips_error_thaw() - used freeze() / thaw() to stop file format sniffers logging spurious errors - vipsthumbnail uses embedded jpg thumbnails if it can +- added vips_webpload(), vips_webpload_buffer() 3/7/13 started 7.34.2 - lower priority for Matlab load to reduce segvs from Mat_Open(), thanks diff --git a/configure.ac b/configure.ac index 10577455..ffedc5b5 100644 --- a/configure.ac +++ b/configure.ac @@ -689,14 +689,14 @@ fi # Gather all up for VIPS_CFLAGS, VIPS_INCLUDES, VIPS_LIBS and VIPS_CXX_LIBS # sort includes to get longer, more specific dirs first # helps, for example, selecting graphicsmagick over imagemagick -VIPS_CFLAGS=`for i in $VIPS_CFLAGS $GTHREAD_CFLAGS $REQUIRED_CFLAGS $PANGOFT2_CFLAGS $FFTW_CFLAGS $MAGICK_CFLAGS $PNG_CFLAGS $EXIF_CFLAGS $MATIO_CFLAGS $CFITSIO_CFLAGS $OPENEXR_CFLAGS $OPENSLIDE_CFLAGS $ORC_CFLAGS $TIFF_CFLAGS $LCMS_CFLAGS +VIPS_CFLAGS=`for i in $VIPS_CFLAGS $GTHREAD_CFLAGS $REQUIRED_CFLAGS $PANGOFT2_CFLAGS $FFTW_CFLAGS $MAGICK_CFLAGS $PNG_CFLAGS $EXIF_CFLAGS $MATIO_CFLAGS $CFITSIO_CFLAGS $LIBWEBP_CFLAGS $OPENEXR_CFLAGS $OPENSLIDE_CFLAGS $ORC_CFLAGS $TIFF_CFLAGS $LCMS_CFLAGS do echo $i done | sort -ru` VIPS_CFLAGS=`echo $VIPS_CFLAGS` VIPS_CFLAGS="$VIPS_DEBUG_FLAGS $VIPS_CFLAGS" VIPS_INCLUDES="$PNG_INCLUDES $TIFF_INCLUDES $ZIP_INCLUDES $JPEG_INCLUDES" -VIPS_LIBS="$MAGICK_LIBS $PNG_LIBS $TIFF_LIBS $ZIP_LIBS $JPEG_LIBS $GTHREAD_LIBS $REQUIRED_LIBS $PANGOFT2_LIBS $FFTW_LIBS $ORC_LIBS $LCMS_LIBS $OPENEXR_LIBS $OPENSLIDE_LIBS $CFITSIO_LIBS $MATIO_LIBS $EXIF_LIBS -lm" +VIPS_LIBS="$MAGICK_LIBS $PNG_LIBS $TIFF_LIBS $ZIP_LIBS $JPEG_LIBS $GTHREAD_LIBS $REQUIRED_LIBS $PANGOFT2_LIBS $FFTW_LIBS $ORC_LIBS $LCMS_LIBS $OPENEXR_LIBS $OPENSLIDE_LIBS $CFITSIO_LIBS $LIBWEBP_LIBS $MATIO_LIBS $EXIF_LIBS -lm" # we need this to generate paths in swig/python/setup.py.in AC_SUBST(top_srcdir) diff --git a/libvips/foreign/webp2vips.c b/libvips/foreign/webp2vips.c index 734a233d..1b2449ce 100644 --- a/libvips/foreign/webp2vips.c +++ b/libvips/foreign/webp2vips.c @@ -32,9 +32,9 @@ */ /* + */ #define DEBUG_VERBOSE #define DEBUG - */ #ifdef HAVE_CONFIG_H #include @@ -43,20 +43,116 @@ #ifdef HAVE_LIBWEBP +#include + #include #include #include "webp.h" +/* How many bytes do we need to read from the start of the file to be able to + * validate the header? + * + * This doesn't seem to be documented anywhere :-( guess a value. + */ +#define MINIMAL_HEADER (100) + +/* What we track during a read. + */ +typedef struct { + /* File source. + */ + char *filename; + + /* Memory source. + */ + void *buf; + size_t len; + + /* Decoder config. + */ + WebPDecoderConfig config; + + /* Incremental decoder state. + */ + WebPIDecoder *idec; +} Read; + int vips__iswebp( const char *filename ) { - unsigned char buf[2]; + unsigned char header[MINIMAL_HEADER]; - if( vips__get_bytes( filename, buf, 2 ) ) - if( (int) buf[0] == 0xff && (int) buf[1] == 0xd8 ) - return( 1 ); + if( vips__get_bytes( filename, header, MINIMAL_HEADER ) && + WebPGetInfo( header, MINIMAL_HEADER, NULL, NULL ) ) + return( 1 ); + + return( 0 ); +} + +static int +read_free( Read *read ) +{ + VIPS_FREE( read->filename ); + VIPS_FREEF( WebPIDelete, read->idec ); + WebPFreeDecBuffer( &read->config.output ); + VIPS_FREE( read ); + + return( 0 ); +} + +static Read * +read_new( const char *filename, void *buf, size_t len ) +{ + Read *read; + unsigned char header[MINIMAL_HEADER]; + + if( !(read = VIPS_NEW( NULL, Read )) ) + return( NULL ); + + read->filename = g_strdup( filename ); + read->buf = buf; + read->len = len; + read->idec = NULL; + + WebPInitDecoderConfig( &read->config ); + if( filename ) { + if( vips__get_bytes( filename, header, MINIMAL_HEADER ) && + WebPGetFeatures( header, MINIMAL_HEADER, + &read->config.input ) != VP8_STATUS_OK ) { + read_free( read ); + return( NULL ); + } + } + else { + if( WebPGetFeatures( read->buf, read->len, + &read->config.input ) != VP8_STATUS_OK ) { + read_free( read ); + return( NULL ); + } + } + + if( read->config.input.has_alpha ) + read->config.output.colorspace = MODE_RGBA; + else + read->config.output.colorspace = MODE_RGB; + read->config.options.use_threads = TRUE; + + return( read ); +} + +static int +read_header( Read *read, VipsImage *out ) +{ + vips_image_init_fields( out, + read->config.input.width, read->config.input.height, + read->config.input.has_alpha ? 4 : 3, + VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, + VIPS_INTERPRETATION_sRGB, + 1.0, 1.0 ); + + vips_demand_hint( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); return( 0 ); } @@ -64,7 +160,65 @@ vips__iswebp( const char *filename ) int vips__webp_read_file_header( const char *filename, VipsImage *out ) { - printf( "vips__webp_read_file_header\n" ); + Read *read; + + if( !(read = read_new( filename, NULL, 0 )) ) { + vips_error( "webp2vips", + _( "unable to open \"%s\"" ), filename ); + return( -1 ); + } + + if( read_header( read, out ) ) + return( -1 ); + + read_free( read ); + + return( 0 ); +} + +typedef uint8_t *(*webp_reader)( 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 ); + char *data; + unsigned int len; + webp_reader reader; + + /* libwebp makes streaming very hard. We have to read to a full memory + * buffer, then copy to out. + */ + t[0] = vips_image_new_buffer(); + if( read_header( read, t[0] ) ) + return( -1 ); + if( vips_image_write_prepare( t[0] ) ) + return( -1 ); + + if( !(data = vips__file_read_name( read->filename, NULL, &len )) ) + return( -1 ); + + if( t[0]->Bands == 3 ) + reader = WebPDecodeRGBInto; + else + reader = WebPDecodeRGBAInto; + + if( !reader( (uint8_t *) data, len, + VIPS_IMAGE_ADDR( t[0], 0, 0 ), + VIPS_IMAGE_SIZEOF_IMAGE( t[0] ), + VIPS_IMAGE_SIZEOF_LINE( t[0] ) ) ) { + g_free( data ); + vips_error( "webp2vips", "%s", _( "unable to read pixels" ) ); + return( -1 ); + } + + g_free( data ); + + if( vips_image_write( t[0], out ) ) + return( -1 ); return( 0 ); } @@ -72,7 +226,18 @@ vips__webp_read_file_header( const char *filename, VipsImage *out ) int vips__webp_read_file( const char *filename, VipsImage *out ) { - printf( "vips__webp_read_file\n" ); + Read *read; + + if( !(read = read_new( filename, NULL, 0 )) ) { + vips_error( "webp2vips", + _( "unable to open \"%s\"" ), filename ); + return( -1 ); + } + + if( read_image( read, out ) ) + return( -1 ); + + read_free( read ); return( 0 ); } @@ -80,7 +245,18 @@ vips__webp_read_file( const char *filename, VipsImage *out ) int vips__webp_read_buffer_header( void *buf, size_t len, VipsImage *out ) { - printf( "vips__webp_read_buffer_header\n" ); + Read *read; + + if( !(read = read_new( NULL, buf, len )) ) { + vips_error( "webp2vips", + "%s", _( "unable to open buffer" ) ); + return( -1 ); + } + + if( read_header( read, out ) ) + return( -1 ); + + read_free( read ); return( 0 ); } @@ -88,11 +264,18 @@ vips__webp_read_buffer_header( void *buf, size_t len, VipsImage *out ) int vips__webp_read_buffer( void *buf, size_t len, VipsImage *out ) { - printf( "vips__webp_read_buffer\n" ); + Read *read; + + if( !(read = read_new( NULL, buf, len )) ) { + vips_error( "webp2vips", + "%s", _( "unable to open buffer" ) ); + return( -1 ); + } + + if( read_image( read, out ) ) + return( -1 ); return( 0 ); } #endif /*HAVE_LIBWEBP*/ - - diff --git a/libvips/iofuncs/type.c b/libvips/iofuncs/type.c index c2303173..7295778f 100644 --- a/libvips/iofuncs/type.c +++ b/libvips/iofuncs/type.c @@ -37,9 +37,9 @@ */ /* - */ #define VIPS_DEBUG #define DEBUG + */ #ifdef HAVE_CONFIG_H #include