diff --git a/TODO b/TODO index 2c235732..3acf42ab 100644 --- a/TODO +++ b/TODO @@ -1,18 +1,5 @@ - test with libwebp 0.3, the earliest that supports libwebpmux - test with ubuntu ... seems to have libwebpmux2 - - test webp metadata read and write - - webpsave should support strip - - update docs - - need to check for >4gb overflow ... use guint64 for all size calculations - - webpsave needs hooking up to exif.c - - - not sure about utf8 error messages on win diff --git a/libvips/foreign/pforeign.h b/libvips/foreign/pforeign.h index d3c87b18..78fbf480 100644 --- a/libvips/foreign/pforeign.h +++ b/libvips/foreign/pforeign.h @@ -220,11 +220,13 @@ int vips__webp_read_buffer( const void *buf, size_t len, int vips__webp_write_file( VipsImage *out, const char *filename, int Q, gboolean lossless, VipsForeignWebpPreset preset, gboolean smart_subsample, gboolean near_lossless, - int alpha_q ); + int alpha_q, + gboolean strip ); int vips__webp_write_buffer( VipsImage *out, void **buf, size_t *len, int Q, gboolean lossless, VipsForeignWebpPreset preset, gboolean smart_subsample, gboolean near_lossless, - int alpha_q ); + int alpha_q, + gboolean strip ); int vips__openslide_isslide( const char *filename ); int vips__openslide_read_header( const char *filename, VipsImage *out, diff --git a/libvips/foreign/vips2webp.c b/libvips/foreign/vips2webp.c index 04f590c2..f8d9bfdb 100644 --- a/libvips/foreign/vips2webp.c +++ b/libvips/foreign/vips2webp.c @@ -89,8 +89,12 @@ get_preset( VipsForeignWebpPreset preset ) typedef struct { uint8_t *mem; - size_t size; - size_t max_size; + + /* We want to be able to detect >4gb even on machines that have size_t + * as uint32. + */ + guint64 size; + guint64 max_size; } VipsWebPWriter; static void @@ -103,21 +107,33 @@ vips_webp_writer_init( VipsWebPWriter *writer ) static int vips_webp_writer_append( VipsWebPWriter *writer, - const uint8_t *data, size_t data_size ) + const uint8_t *data, guint64 data_size ) { - size_t next_size; + guint64 next_size; next_size = writer->size + data_size; if( next_size > writer->max_size ) { uint8_t *new_mem; - const size_t next_max_size = + const guint64 next_max_size = VIPS_MAX( 8192, VIPS_MAX( next_size, writer->max_size * 2 ) ); - if( !(new_mem = (uint8_t *) - g_try_realloc( writer->mem, next_max_size )) ) + /* We should let it creep up to 4gb rather than just + * blocking when max goes over, but no one will make a >2gb + * webp image. + */ + if( next_max_size > UINT_MAX ) { + vips_error( "webp", + "%s", _( "output webp image too large" ) ); return( 0 ); + } + + if( !(new_mem = (uint8_t *) + g_try_realloc( writer->mem, next_max_size )) ) { + vips_error( "webp", "%s", _( "out of memory" ) ); + return( 0 ); + } writer->mem = new_mem; writer->max_size = next_max_size; @@ -167,7 +183,7 @@ vips_webp_writer_appendcc( VipsWebPWriter *writer, const char buf[4] ) static gboolean vips_webp_writer_appendc( VipsWebPWriter *writer, - const char fourcc[4], const uint8_t *data, size_t data_size ) + const char fourcc[4], const uint8_t *data, guint64 data_size ) { const int zero = 0; gboolean need_padding = (data_size & 1) != 0; @@ -284,7 +300,7 @@ static int vips_webp_add_chunk( VipsWebPWriter *writer, VipsImage *image, const char *vips, const char webp[4] ) { - if( vips_image_get_typeof( image, vips) ) { + if( vips_image_get_typeof( image, vips ) ) { void *data; size_t length; @@ -345,6 +361,11 @@ vips_webp_add_metadata( VipsWebPWriter *writer, VipsImage *image ) guint64 new_size; VipsWebPWriter new; + /* Rebuild the EXIF block, if any, ready for writing. + */ + if( vips__exif_update( image ) ) + return( -1 ); + /* We have to find the size of the block we will write before we can * start to write it. */ @@ -474,7 +495,7 @@ int vips__webp_write_file( VipsImage *in, const char *filename, int Q, gboolean lossless, VipsForeignWebpPreset preset, gboolean smart_subsample, gboolean near_lossless, - int alpha_q ) + int alpha_q, gboolean strip ) { WebPPicture pic; VipsWebPWriter writer; @@ -499,7 +520,8 @@ vips__webp_write_file( VipsImage *in, const char *filename, WebPPictureFree( &pic ); - if( vips_webp_add_metadata( &writer, in ) ) { + if( !strip && + vips_webp_add_metadata( &writer, in ) ) { vips_webp_writer_unset( &writer ); return( -1 ); } @@ -525,7 +547,7 @@ int vips__webp_write_buffer( VipsImage *in, void **obuf, size_t *olen, int Q, gboolean lossless, VipsForeignWebpPreset preset, gboolean smart_subsample, gboolean near_lossless, - int alpha_q ) + int alpha_q, gboolean strip ) { WebPPicture pic; VipsWebPWriter writer; @@ -549,7 +571,8 @@ vips__webp_write_buffer( VipsImage *in, void **obuf, size_t *olen, WebPPictureFree( &pic ); - if( vips_webp_add_metadata( &writer, in ) ) { + if( !strip && + vips_webp_add_metadata( &writer, in ) ) { vips_webp_writer_unset( &writer ); return( -1 ); } diff --git a/libvips/foreign/webp2vips.c b/libvips/foreign/webp2vips.c index 1d061a7a..22e652c5 100644 --- a/libvips/foreign/webp2vips.c +++ b/libvips/foreign/webp2vips.c @@ -236,7 +236,7 @@ read_header( Read *read, VipsImage *out ) WebPMux *mux; int i; - /* We have to parse the whole file again to get the ICC profile out. + /* We have to parse the whole file again to get the metadata out. */ bitstream.bytes = read->data; bitstream.size = read->length; @@ -245,7 +245,7 @@ read_header( Read *read, VipsImage *out ) return( -1 ); } - for( i = 0; i < VIPS_NUMBER( vips__webp_names ); i++ ) { + 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; diff --git a/libvips/foreign/webpload.c b/libvips/foreign/webpload.c index a17385e9..c27a01ba 100644 --- a/libvips/foreign/webpload.c +++ b/libvips/foreign/webpload.c @@ -293,6 +293,9 @@ vips_foreign_load_webp_buffer_init( VipsForeignLoadWebpBuffer *buffer ) * * Use @shrink to specify a shrink-on-load factor. * + * If libwebpmux is available, image metadata is also read. The loader supports + * ICC, EXIF and XMP metadata. + * * See also: vips_image_new_from_file(). * * Returns: 0 on success, -1 on error. diff --git a/libvips/foreign/webpsave.c b/libvips/foreign/webpsave.c index 8ee91350..be370f49 100644 --- a/libvips/foreign/webpsave.c +++ b/libvips/foreign/webpsave.c @@ -191,7 +191,7 @@ vips_foreign_save_webp_file_build( VipsObject *object ) if( vips__webp_write_file( save->ready, file->filename, webp->Q, webp->lossless, webp->preset, webp->smart_subsample, webp->near_lossless, - webp->alpha_q ) ) + webp->alpha_q, save->strip ) ) return( -1 ); return( 0 ); @@ -255,7 +255,7 @@ vips_foreign_save_webp_buffer_build( VipsObject *object ) if( vips__webp_write_buffer( save->ready, &obuf, &olen, webp->Q, webp->lossless, webp->preset, webp->smart_subsample, webp->near_lossless, - webp->alpha_q ) ) + webp->alpha_q, save->strip ) ) return( -1 ); /* obuf is a g_free() buffer, not vips_free(). @@ -320,7 +320,7 @@ vips_foreign_save_webp_mime_build( VipsObject *object ) if( vips__webp_write_buffer( save->ready, &obuf, &olen, webp->Q, webp->lossless, webp->preset, webp->smart_subsample, webp->near_lossless, - webp->alpha_q ) ) + webp->alpha_q, save->strip ) ) return( -1 ); printf( "Content-length: %zu\r\n", olen ); @@ -363,12 +363,13 @@ vips_foreign_save_webp_mime_init( VipsForeignSaveWebpMime *mime ) * * Optional arguments: * - * * @Q: %gint quality factor - * * @lossless: %gboolean enables lossless compression - * * @preset: #VipsForeignWebpPreset choose lossy compression preset - * * @smart_subsample: %gboolean enables high quality chroma subsampling - * * @near_lossless: %gboolean preprocess in lossless mode (controlled by Q) - * * @alpha_q: %gint set alpha quality in lossless mode + * * @Q: %gint, quality factor + * * @lossless: %gboolean, enables lossless compression + * * @preset: #VipsForeignWebpPreset, choose lossy compression preset + * * @smart_subsample: %gboolean, enables high quality chroma subsampling + * * @near_lossless: %gboolean, preprocess in lossless mode (controlled by Q) + * * @alpha_q: %gint, set alpha quality in lossless mode + * * @strip: %gboolean, remove all metadata from image * * Write an image to a file in WebP format. * @@ -386,6 +387,9 @@ vips_foreign_save_webp_mime_init( VipsForeignSaveWebpMime *mime ) * with @Q 80, 60, 40 or 20 to apply increasing amounts of preprocessing * which improves the near-lossless compression ratio by up to 50%. * + * The writer will attach ICC, EXIF and XMP metadata, unless @strip is set to + * %TRUE. + * * See also: vips_webpload(), vips_image_write_to_file(). * * Returns: 0 on success, -1 on error. @@ -412,12 +416,13 @@ vips_webpsave( VipsImage *in, const char *filename, ... ) * * Optional arguments: * - * * @Q: %gint quality factor - * * @lossless: %gboolean enables lossless compression - * * @preset: #VipsForeignWebpPreset choose lossy compression preset - * * @smart_subsample: %gboolean enables high quality chroma subsampling - * * @near_lossless: %gboolean preprocess in lossless mode (controlled by Q) - * * @alpha_q: %gint set alpha quality in lossless mode + * * @Q: %gint, quality factor + * * @lossless: %gboolean, enables lossless compression + * * @preset: #VipsForeignWebpPreset, choose lossy compression preset + * * @smart_subsample: %gboolean, enables high quality chroma subsampling + * * @near_lossless: %gboolean, preprocess in lossless mode (controlled by Q) + * * @alpha_q: %gint, set alpha quality in lossless mode + * * @strip: %gboolean, remove all metadata from image * * As vips_webpsave(), but save to a memory buffer. * @@ -464,12 +469,13 @@ vips_webpsave_buffer( VipsImage *in, void **buf, size_t *len, ... ) * * Optional arguments: * - * * @Q: %gint quality factor - * * @lossless: %gboolean enables lossless compression - * * @preset: #VipsForeignWebpPreset choose lossy compression preset - * * @smart_subsample: %gboolean enables high quality chroma subsampling - * * @near_lossless: %gboolean preprocess in lossless mode (controlled by Q) - * * @alpha_q: %gint set alpha quality in lossless mode + * * @Q: %gint, quality factor + * * @lossless: %gboolean, enables lossless compression + * * @preset: #VipsForeignWebpPreset, choose lossy compression preset + * * @smart_subsample: %gboolean, enables high quality chroma subsampling + * * @near_lossless: %gboolean, preprocess in lossless mode (controlled by Q) + * * @alpha_q: %gint, set alpha quality in lossless mode + * * @strip: %gboolean, remove all metadata from image * * As vips_webpsave(), but save as a mime webp on stdout. * diff --git a/test/test_formats.sh b/test/test_formats.sh index 5cf471de..9fd711bd 100755 --- a/test/test_formats.sh +++ b/test/test_formats.sh @@ -49,7 +49,8 @@ save_load() { fi if ! $vips copy $tmp/t1.$format $tmp/back.v ; then - echo "read from $out failed" + echo "read from $tmp/t1.format failed" + echo " (was written by $vips copy $in $tmp/t1.$format$mode)" exit 1 fi }