diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index 7389fbf5..10fb231c 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -2217,6 +2217,7 @@ vips_jpegload_buffer( void *buf, size_t len, VipsImage **out, ... ) * * @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 + * * @quant_table: %gint, quantization table index * * Write a VIPS image to a file as JPEG. * @@ -2268,6 +2269,30 @@ vips_jpegload_buffer( void *buf, size_t len, VipsImage **out, ... ) * (e.g. mozjpeg >= 3.0), split the spectrum of DCT coefficients into * separate scans. Reduces file size but increases compression time. * + * If @quant_table is set and the version of libjpeg supports it + * (e.g. mozjpeg >= 3.0) it selects the quantization table to use: + * + * * 0 Tables from JPEG Annex K (vips and libjpeg default) + * * 1 Flat table + * * 2 Table tuned for MSSIM on Kodak image set + * * 3 Table from ImageMagick by N. Robidoux (current mozjpeg default) + * * 4 Table tuned for PSNR-HVS-M on Kodak image set + * * 5 Table from Relevance of Human Vision to JPEG-DCT Compression (1992) + * * 6 Table from DCTune Perceptual Optimization of Compressed Dental X-Rays (1997) + * * 7 Table from A Visual Detection Model for DCT Coefficient Quantization (1993) + * * 8 Table from An Improved Detection Model for DCT Coefficient Quantization (1993) + * + * Quantization table 0 is the default in vips and libjpeg(-turbo), but it + * tends to favor detail over color accuracy, producting colored patches and + * stripes as well as heavy banding in flat areas at high compression ratios. + * Quantization table 2 is a good candidate to try if the default quantization + * table produces banding or color shifts and is well suited for hires images. + * Quantization table 3 is the default in mozjpeg and has been tuned to produce + * good results at the default quality setting; banding at high compression. + * Quantization table 4 is the most accurate at the cost of compression ratio. + * Tables 5-7 are based on older research papers, but generally achieve worse + * compression ratios and/or quality than 2 or 4. + * * See also: vips_jpegsave_buffer(), vips_image_write_to_file(). * * Returns: 0 on success, -1 on error. @@ -2303,6 +2328,7 @@ vips_jpegsave( VipsImage *in, const char *filename, ... ) * * @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 + * * @quant_table: %gint, quantization table index * * As vips_jpegsave(), but save to a memory buffer. * @@ -2357,6 +2383,7 @@ vips_jpegsave_buffer( VipsImage *in, void **buf, size_t *len, ... ) * * @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 + * * @quant_table: %gint, quantization table index * * As vips_jpegsave(), but save as a mime jpeg on stdout. * diff --git a/libvips/foreign/jpegsave.c b/libvips/foreign/jpegsave.c index 2cc29a6a..4fac9b99 100644 --- a/libvips/foreign/jpegsave.c +++ b/libvips/foreign/jpegsave.c @@ -103,6 +103,10 @@ typedef struct _VipsForeignSaveJpeg { */ gboolean optimize_scans; + /* Use predefined quantization table with given index. + */ + int quant_table; + } VipsForeignSaveJpeg; typedef VipsForeignSaveClass VipsForeignSaveJpegClass; @@ -194,6 +198,13 @@ vips_foreign_save_jpeg_class_init( VipsForeignSaveJpegClass *class ) G_STRUCT_OFFSET( VipsForeignSaveJpeg, optimize_scans ), FALSE ); + VIPS_ARG_INT( class, "quant_table", 18, + _( "Quantization table" ), + _( "Use predefined quantization table with given index" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsForeignSaveJpeg, quant_table ), + 0, 8, 0 ); + } static void @@ -230,7 +241,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->trellis_quant, jpeg->overshoot_deringing, jpeg->optimize_scans) ) + jpeg->trellis_quant, jpeg->overshoot_deringing, + jpeg->optimize_scans, jpeg->quant_table ) ) return( -1 ); return( 0 ); @@ -294,7 +306,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->trellis_quant, jpeg->overshoot_deringing, jpeg->optimize_scans) ) + jpeg->trellis_quant, jpeg->overshoot_deringing, + jpeg->optimize_scans, jpeg->quant_table ) ) return( -1 ); blob = vips_blob_new( (VipsCallbackFn) vips_free, obuf, olen ); @@ -357,7 +370,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->trellis_quant, jpeg->overshoot_deringing, jpeg->optimize_scans) ) + jpeg->trellis_quant, jpeg->overshoot_deringing, + jpeg->optimize_scans, jpeg->quant_table ) ) return( -1 ); printf( "Content-length: %zu\r\n", olen ); diff --git a/libvips/foreign/vips2jpeg.c b/libvips/foreign/vips2jpeg.c index d714cfc9..0c7bda5b 100644 --- a/libvips/foreign/vips2jpeg.c +++ b/libvips/foreign/vips2jpeg.c @@ -966,7 +966,7 @@ static int write_vips( Write *write, int qfac, const char *profile, gboolean optimize_coding, gboolean progressive, gboolean strip, gboolean no_subsample, gboolean trellis_quant, - gboolean overshoot_deringing, gboolean optimize_scans ) + gboolean overshoot_deringing, gboolean optimize_scans, int quant_table ) { VipsImage *in; J_COLOR_SPACE space; @@ -1022,10 +1022,9 @@ write_vips( Write *write, int qfac, const char *profile, JINT_COMPRESS_PROFILE, JCP_FASTEST ); #endif - /* Rest to default. + /* Reset to default. */ jpeg_set_defaults( &write->cinfo ); - jpeg_set_quality( &write->cinfo, qfac, TRUE ); /* Compute optimal Huffman coding tables. */ @@ -1077,6 +1076,20 @@ write_vips( Write *write, int qfac, const char *profile, vips_warn( "vips2jpeg", "%s", _( "Ignoring optimize_scans for baseline" ) ); } + + /* Use predefined quantization table. + */ + if( quant_table > 0 ) { + if( jpeg_c_int_param_supported( &write->cinfo, + JINT_BASE_QUANT_TBL_IDX ) ) + jpeg_c_set_int_param( &write->cinfo, + JINT_BASE_QUANT_TBL_IDX, quant_table ); + else + vips_warn( "vips2jpeg", + "%s", _( "Setting quant_table unsupported" ) ); + } + vips_warn( "vips2jpeg", "quant_table %d", + jpeg_c_get_int_param( &write->cinfo, JINT_BASE_QUANT_TBL_IDX ) ); #else /* Using jpeglib.h without extension parameters, warn of ignored * options. @@ -1088,8 +1101,14 @@ write_vips( Write *write, int qfac, const char *profile, "%s", _( "Ignoring overshoot_deringing" ) ); if( optimize_scans ) vips_warn( "vips2jpeg", "%s", _( "Ignoring optimize_scans" ) ); + if( quant_table > 0 ) + vips_warn( "vips2jpeg", "%s", _( "Ignoring quant_table" ) ); #endif + /* Set compression quality. Must be called after setting params above. + */ + jpeg_set_quality( &write->cinfo, qfac, TRUE ); + /* Enable progressive write. */ if( progressive ) @@ -1160,7 +1179,7 @@ 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 trellis_quant, - gboolean overshoot_deringing, gboolean optimize_scans ) + gboolean overshoot_deringing, gboolean optimize_scans, int quant_table ) { Write *write; @@ -1192,7 +1211,7 @@ vips__jpeg_write_file( VipsImage *in, */ if( write_vips( write, Q, profile, optimize_coding, progressive, strip, no_subsample, - trellis_quant, overshoot_deringing, optimize_scans ) ) { + trellis_quant, overshoot_deringing, optimize_scans, quant_table ) ) { write_destroy( write ); return( -1 ); } @@ -1446,7 +1465,7 @@ 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 trellis_quant, - gboolean overshoot_deringing, gboolean optimize_scans ) + gboolean overshoot_deringing, gboolean optimize_scans, int quant_table ) { Write *write; @@ -1477,7 +1496,7 @@ vips__jpeg_write_buffer( VipsImage *in, */ if( write_vips( write, Q, profile, optimize_coding, progressive, strip, no_subsample, - trellis_quant, overshoot_deringing, optimize_scans ) ) { + trellis_quant, overshoot_deringing, optimize_scans, quant_table ) ) { write_destroy( write ); return( -1 ); diff --git a/libvips/foreign/vipsjpeg.h b/libvips/foreign/vipsjpeg.h index 9c86cb5c..a9bf86e6 100644 --- a/libvips/foreign/vipsjpeg.h +++ b/libvips/foreign/vipsjpeg.h @@ -41,12 +41,12 @@ 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 trellis_quant, - gboolean overshoot_deringing, gboolean optimize_scans ); + gboolean overshoot_deringing, gboolean optimize_scans, int quant_table ); 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 trellis_quant, - gboolean overshoot_deringing, gboolean optimize_scans ); + gboolean overshoot_deringing, gboolean optimize_scans, int quant_table ); int vips__isjpeg_buffer( const void *buf, size_t len ); int vips__isjpeg( const char *filename );