From 00e27def8ea841db6b3ec9da00bbdc976241c4ae Mon Sep 17 00:00:00 2001 From: Lovell Fuller Date: Sun, 12 Apr 2015 12:00:05 +0100 Subject: [PATCH 1/2] Expose new/forthcoming features of mozjpeg/libjpeg-turbo Runtime detection via JPEG extension parameters Compile-time detection via J_BOOLEAN_PARAM of jpeglib.h --- configure.ac | 16 +++++++ libvips/foreign/foreign.c | 23 ++++++++++ libvips/foreign/jpegsave.c | 42 +++++++++++++++++-- libvips/foreign/vips2jpeg.c | 83 +++++++++++++++++++++++++++++++++---- libvips/foreign/vipsjpeg.h | 6 ++- 5 files changed, 158 insertions(+), 12 deletions(-) diff --git a/configure.ac b/configure.ac index 0b908129..031b1328 100644 --- a/configure.ac +++ b/configure.ac @@ -696,6 +696,22 @@ FIND_JPEG( with_jpeg=no ]) +# JPEG extension parameters available in libjpeg-turbo >=1.5.0, mozjpeg >=3.0 +if test x"$with_jpeg" = "xyes"; then + AC_MSG_CHECKING([for JPEG extension parameters]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #include + #include + ]], [[ + J_BOOLEAN_PARAM test; + ]])], [ + AC_MSG_RESULT([yes]) + AC_DEFINE([HAVE_JPEG_EXT_PARAMS],1,[libjpeg has extension parameters]) + ], [ + AC_MSG_RESULT([no]) + ]) +fi + # libexif AC_ARG_WITH([libexif], AS_HELP_STRING([--without-libexif], [build without libexif (default: test)])) diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index be2dff69..d050eb5b 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -2050,6 +2050,9 @@ vips_jpegload_buffer( void *buf, size_t len, VipsImage **out, ... ) * @interlace: %gboolean, write an interlaced (progressive) jpeg * @strip: %gboolean, remove all metadata from image * @no-subsample: %gboolean, disable chroma subsampling + * @trellis_quant: %gboolean, apply trellis quantisation to each 8x8 block + * @overshoot_deringing: %gboolean, overshoot samples with extreme values + * @optimize_scans: %gboolean, split DCT coefficients into separate scans * * Write a VIPS image to a file as JPEG. * @@ -2087,6 +2090,20 @@ vips_jpegload_buffer( void *buf, size_t len, VipsImage **out, ... ) * If @no-subsample is set, chrominance subsampling is disabled. This will * improve quality at the cost of larger file size. Useful for high Q factors. * + * If @trellis_quant is set and the version of libjpeg supports it + * (e.g. mozjpeg >= 3.0), apply trellis quantisation to each 8x8 block. + * Reduces file size but increases compression time. + * + * If @overshoot_deringing is set and the version of libjpeg supports it + * (e.g. mozjpeg >= 3.0), apply overshooting to samples with extreme values + * for example 0 and 255 for 8-bit. Overshooting may reduce ringing artifacts + * from compression, in particular in areas where black text appears on a + * white background. + * + * If @optimize_scans is set and the version of libjpeg supports it + * (e.g. mozjpeg >= 3.0), split the spectrum of DCT coefficients into + * separate scans. Reduces file size but increases compression time. + * * See also: vips_jpegsave_buffer(), vips_image_write_to_file(). * * Returns: 0 on success, -1 on error. @@ -2119,6 +2136,9 @@ vips_jpegsave( VipsImage *in, const char *filename, ... ) * @interlace: write an interlaced (progressive) jpeg * @strip: remove all metadata from image * @no-subsample: disable chroma subsampling + * @trellis_quant: %gboolean, apply trellis quantisation to each 8x8 block + * @overshoot_deringing: %gboolean, overshoot samples with extreme values + * @optimize_scans: %gboolean, split DCT coefficients into separate scans * * As vips_jpegsave(), but save to a memory buffer. * @@ -2170,6 +2190,9 @@ vips_jpegsave_buffer( VipsImage *in, void **buf, size_t *len, ... ) * @optimize_coding: compute optimal Huffman coding tables * @strip: remove all metadata from image * @no-subsample: disable chroma subsampling + * @trellis_quant: %gboolean, apply trellis quantisation to each 8x8 block + * @overshoot_deringing: %gboolean, overshoot samples with extreme values + * @optimize_scans: %gboolean, split DCT coefficients into separate scans * * As vips_jpegsave(), but save as a mime jpeg on stdout. * diff --git a/libvips/foreign/jpegsave.c b/libvips/foreign/jpegsave.c index 30c32caf..6329aabb 100644 --- a/libvips/foreign/jpegsave.c +++ b/libvips/foreign/jpegsave.c @@ -91,6 +91,18 @@ typedef struct _VipsForeignSaveJpeg { */ gboolean no_subsample; + /* Apply trellis quantisation to each 8x8 block. + */ + gboolean trellis_quant; + + /* Apply overshooting to samples with extreme values e.g. 0 & 255 for 8-bit. + */ + gboolean overshoot_deringing; + + /* Split the spectrum of DCT coefficients into separate scans. + */ + gboolean optimize_scans; + } VipsForeignSaveJpeg; typedef VipsForeignSaveClass VipsForeignSaveJpegClass; @@ -161,6 +173,27 @@ vips_foreign_save_jpeg_class_init( VipsForeignSaveJpegClass *class ) G_STRUCT_OFFSET( VipsForeignSaveJpeg, no_subsample ), FALSE ); + VIPS_ARG_BOOL( class, "trellis_quant", 15, + _( "Trellis quantisation" ), + _( "Apply trellis quantisation to each 8x8 block" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsForeignSaveJpeg, trellis_quant ), + FALSE ); + + VIPS_ARG_BOOL( class, "overshoot_deringing", 16, + _( "Overshoot de-ringing" ), + _( "Apply overshooting to samples with extreme values" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsForeignSaveJpeg, overshoot_deringing ), + FALSE ); + + VIPS_ARG_BOOL( class, "optimize_scans", 17, + _( "Optimize scans" ), + _( "Split the spectrum of DCT coefficients into separate scans" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsForeignSaveJpeg, optimize_scans ), + FALSE ); + } static void @@ -196,7 +229,8 @@ vips_foreign_save_jpeg_file_build( VipsObject *object ) if( vips__jpeg_write_file( save->ready, file->filename, jpeg->Q, jpeg->profile, jpeg->optimize_coding, - jpeg->interlace, save->strip, jpeg->no_subsample ) ) + jpeg->interlace, save->strip, jpeg->no_subsample, + jpeg->trellis_quant, jpeg->overshoot_deringing, jpeg->optimize_scans) ) return( -1 ); return( 0 ); @@ -259,7 +293,8 @@ vips_foreign_save_jpeg_buffer_build( VipsObject *object ) if( vips__jpeg_write_buffer( save->ready, &obuf, &olen, jpeg->Q, jpeg->profile, jpeg->optimize_coding, - jpeg->interlace, save->strip, jpeg->no_subsample ) ) + jpeg->interlace, save->strip, jpeg->no_subsample, + jpeg->trellis_quant, jpeg->overshoot_deringing, jpeg->optimize_scans) ) return( -1 ); blob = vips_blob_new( (VipsCallbackFn) vips_free, obuf, olen ); @@ -321,7 +356,8 @@ vips_foreign_save_jpeg_mime_build( VipsObject *object ) if( vips__jpeg_write_buffer( save->ready, &obuf, &olen, jpeg->Q, jpeg->profile, jpeg->optimize_coding, - jpeg->interlace, save->strip, jpeg->no_subsample ) ) + jpeg->interlace, save->strip, jpeg->no_subsample, + jpeg->trellis_quant, jpeg->overshoot_deringing, jpeg->optimize_scans) ) return( -1 ); printf( "Content-length: %zd\r\n", olen ); diff --git a/libvips/foreign/vips2jpeg.c b/libvips/foreign/vips2jpeg.c index f645fa6b..64644e94 100644 --- a/libvips/foreign/vips2jpeg.c +++ b/libvips/foreign/vips2jpeg.c @@ -860,7 +860,8 @@ write_jpeg_block( VipsRegion *region, VipsRect *area, void *a ) static int write_vips( Write *write, int qfac, const char *profile, gboolean optimize_coding, gboolean progressive, gboolean strip, - gboolean no_subsample ) + gboolean no_subsample, gboolean trellis_quant, + gboolean overshoot_deringing, gboolean optimize_scans ) { VipsImage *in; J_COLOR_SPACE space; @@ -908,6 +909,14 @@ write_vips( Write *write, int qfac, const char *profile, if( !(write->row_pointer = VIPS_ARRAY( NULL, in->Ysize, JSAMPROW )) ) return( -1 ); +#ifdef HAVE_JPEG_EXT_PARAMS + /* Reset compression profile to libjpeg defaults + */ + if( jpeg_c_int_param_supported( &write->cinfo, JINT_COMPRESS_PROFILE ) ) { + jpeg_c_set_int_param( &write->cinfo, JINT_COMPRESS_PROFILE, JCP_FASTEST ); + } +#endif + /* Rest to default. */ jpeg_set_defaults( &write->cinfo ); @@ -917,6 +926,64 @@ write_vips( Write *write, int qfac, const char *profile, */ write->cinfo.optimize_coding = optimize_coding; +#ifdef HAVE_JPEG_EXT_PARAMS + /* Apply trellis quantisation to each 8x8 block. Infers "optimize_coding". + */ + if( trellis_quant ) { + if ( jpeg_c_bool_param_supported( + &write->cinfo, JBOOLEAN_TRELLIS_QUANT ) ) { + jpeg_c_set_bool_param( &write->cinfo, + JBOOLEAN_TRELLIS_QUANT, TRUE ); + write->cinfo.optimize_coding = TRUE; + } + else { + vips_warn( "vips2jpeg", "%s", _( "trellis_quant unsupported" ) ); + } + } + /* Apply overshooting to samples with extreme values e.g. 0 & 255 for 8-bit. + */ + if( overshoot_deringing ) { + if ( jpeg_c_bool_param_supported( + &write->cinfo, JBOOLEAN_OVERSHOOT_DERINGING ) ) { + jpeg_c_set_bool_param( &write->cinfo, + JBOOLEAN_OVERSHOOT_DERINGING, TRUE ); + } + else { + vips_warn( "vips2jpeg", "%s", _( "overshoot_deringing unsupported" ) ); + } + } + /* Split the spectrum of DCT coefficients into separate scans. + * Requires progressive output. Must be set before jpeg_simple_progression. + */ + if( optimize_scans ) { + if( progressive ) { + if( jpeg_c_bool_param_supported( + &write->cinfo, JBOOLEAN_OPTIMIZE_SCANS ) ) { + jpeg_c_set_bool_param( &write->cinfo, JBOOLEAN_OPTIMIZE_SCANS, TRUE ); + } + else { + vips_warn( "vips2jpeg", "%s", _( "Ignoring optimize_scans" ) ); + } + } + else { + vips_warn( "vips2jpeg", "%s", + _( "Ignoring optimize_scans for baseline" ) ); + } + } +#else + /* Using jpeglib.h without extension parameters, warn of ignored options. + */ + if ( trellis_quant ) { + vips_warn( "vips2jpeg", "%s", _( "Ignoring trellis_quant" ) ); + } + if ( overshoot_deringing ) { + vips_warn( "vips2jpeg", "%s", _( "Ignoring overshoot_deringing" ) ); + } + if ( optimize_scans ) { + vips_warn( "vips2jpeg", "%s", _( "Ignoring optimize_scans" ) ); + } +#endif + /* Enable progressive write. */ if( progressive ) @@ -981,7 +1048,8 @@ int vips__jpeg_write_file( VipsImage *in, const char *filename, int Q, const char *profile, gboolean optimize_coding, gboolean progressive, gboolean strip, - gboolean no_subsample ) + gboolean no_subsample, gboolean trellis_quant, + gboolean overshoot_deringing, gboolean optimize_scans ) { Write *write; @@ -1012,8 +1080,8 @@ vips__jpeg_write_file( VipsImage *in, /* Convert! */ if( write_vips( write, - Q, profile, optimize_coding, progressive, strip, - no_subsample ) ) { + Q, profile, optimize_coding, progressive, strip, no_subsample, + trellis_quant, overshoot_deringing, optimize_scans ) ) { write_destroy( write ); return( -1 ); } @@ -1261,7 +1329,8 @@ int vips__jpeg_write_buffer( VipsImage *in, void **obuf, size_t *olen, int Q, const char *profile, gboolean optimize_coding, gboolean progressive, - gboolean strip, gboolean no_subsample ) + gboolean strip, gboolean no_subsample, gboolean trellis_quant, + gboolean overshoot_deringing, gboolean optimize_scans ) { Write *write; @@ -1291,8 +1360,8 @@ vips__jpeg_write_buffer( VipsImage *in, /* Convert! */ if( write_vips( write, - Q, profile, optimize_coding, progressive, strip, - no_subsample ) ) { + Q, profile, optimize_coding, progressive, strip, no_subsample, + trellis_quant, overshoot_deringing, optimize_scans ) ) { write_destroy( write ); return( -1 ); diff --git a/libvips/foreign/vipsjpeg.h b/libvips/foreign/vipsjpeg.h index 098f0dd5..305ff5b2 100644 --- a/libvips/foreign/vipsjpeg.h +++ b/libvips/foreign/vipsjpeg.h @@ -40,11 +40,13 @@ extern const char *vips__jpeg_suffs[]; int vips__jpeg_write_file( VipsImage *in, const char *filename, int Q, const char *profile, gboolean optimize_coding, gboolean progressive, gboolean strip, - gboolean no_subsample ); + gboolean no_subsample, gboolean trellis_quant, + gboolean overshoot_deringing, gboolean optimize_scans ); int vips__jpeg_write_buffer( VipsImage *in, void **obuf, size_t *olen, int Q, const char *profile, gboolean optimize_coding, gboolean progressive, gboolean strip, - gboolean no_subsample ); + gboolean no_subsample, gboolean trellis_quant, + gboolean overshoot_deringing, gboolean optimize_scans ); int vips__isjpeg_buffer( void *buf, size_t len ); int vips__isjpeg( const char *filename ); From e4326fc9adbbe5c4a8ee3ded21bbe41dd81000fe Mon Sep 17 00:00:00 2001 From: Lovell Fuller Date: Wed, 15 Apr 2015 20:05:19 +0100 Subject: [PATCH 2/2] Ensure expected signature in ExceptionInfo before destroy GraphicsMagick asserts this in DestroyExceptionInfo read_free is called multiple times since fc5a4a9 --- libvips/foreign/magick2vips.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libvips/foreign/magick2vips.c b/libvips/foreign/magick2vips.c index 962764fb..53ca0b02 100644 --- a/libvips/foreign/magick2vips.c +++ b/libvips/foreign/magick2vips.c @@ -143,7 +143,9 @@ read_free( Read *read ) VIPS_FREEF( DestroyImage, read->image ); VIPS_FREEF( DestroyImageInfo, read->image_info ); VIPS_FREE( read->frames ); - DestroyExceptionInfo( &read->exception ); + if ( (&read->exception)->signature == MagickSignature ) { + DestroyExceptionInfo( &read->exception ); + } VIPS_FREEF( vips_g_mutex_free, read->lock ); }