Add jpeg restart_interval option. (#2468)
* Add jpeg restart_interval option. This allows saving a jpeg with MCU restarts. * Fix code style. Add description of restart_interval. * Add a basic test based on output length. * Update main change log.
This commit is contained in:
parent
f6281284a1
commit
46a67cfab9
@ -11,6 +11,7 @@
|
||||
- don't use atexit for cleanup, it's too unreliable
|
||||
- tiff writer loops for the whole image rather than per page [LionelArn2]
|
||||
- fix VipsSource with named pipes [vibbix]
|
||||
- added restart_interval option to jpegsave [manthey]
|
||||
|
||||
16/8/21 started 8.11.4
|
||||
- fix off-by-one error in new rank fast path
|
||||
|
@ -103,6 +103,10 @@ typedef struct _VipsForeignSaveJpeg {
|
||||
*/
|
||||
int quant_table;
|
||||
|
||||
/* Use an MCU restart interval.
|
||||
*/
|
||||
int restart_interval;
|
||||
|
||||
} VipsForeignSaveJpeg;
|
||||
|
||||
typedef VipsForeignSaveClass VipsForeignSaveJpegClass;
|
||||
@ -231,6 +235,14 @@ vips_foreign_save_jpeg_class_init( VipsForeignSaveJpegClass *class )
|
||||
G_STRUCT_OFFSET( VipsForeignSaveJpeg, subsample_mode ),
|
||||
VIPS_TYPE_FOREIGN_SUBSAMPLE,
|
||||
VIPS_FOREIGN_SUBSAMPLE_AUTO );
|
||||
|
||||
VIPS_ARG_INT( class, "restart_interval", 20,
|
||||
_( "Restart interval" ),
|
||||
_( "Add restart markers every specified number of mcu" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveJpeg, restart_interval ),
|
||||
0, INT_MAX, 0 );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
@ -268,7 +280,8 @@ vips_foreign_save_jpeg_target_build( VipsObject *object )
|
||||
jpeg->Q, jpeg->profile, jpeg->optimize_coding,
|
||||
jpeg->interlace, save->strip, jpeg->trellis_quant,
|
||||
jpeg->overshoot_deringing, jpeg->optimize_scans,
|
||||
jpeg->quant_table, jpeg->subsample_mode ) )
|
||||
jpeg->quant_table, jpeg->subsample_mode,
|
||||
jpeg->restart_interval ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
@ -334,7 +347,8 @@ vips_foreign_save_jpeg_file_build( VipsObject *object )
|
||||
jpeg->Q, jpeg->profile, jpeg->optimize_coding,
|
||||
jpeg->interlace, save->strip, jpeg->trellis_quant,
|
||||
jpeg->overshoot_deringing, jpeg->optimize_scans,
|
||||
jpeg->quant_table, jpeg->subsample_mode ) ) {
|
||||
jpeg->quant_table, jpeg->subsample_mode,
|
||||
jpeg->restart_interval ) ) {
|
||||
VIPS_UNREF( target );
|
||||
return( -1 );
|
||||
}
|
||||
@ -404,7 +418,8 @@ vips_foreign_save_jpeg_buffer_build( VipsObject *object )
|
||||
jpeg->Q, jpeg->profile, jpeg->optimize_coding,
|
||||
jpeg->interlace, save->strip, jpeg->trellis_quant,
|
||||
jpeg->overshoot_deringing, jpeg->optimize_scans,
|
||||
jpeg->quant_table, jpeg->subsample_mode ) ) {
|
||||
jpeg->quant_table, jpeg->subsample_mode,
|
||||
jpeg->restart_interval ) ) {
|
||||
VIPS_UNREF( target );
|
||||
return( -1 );
|
||||
}
|
||||
@ -477,7 +492,8 @@ vips_foreign_save_jpeg_mime_build( VipsObject *object )
|
||||
jpeg->Q, jpeg->profile, jpeg->optimize_coding,
|
||||
jpeg->interlace, save->strip, jpeg->trellis_quant,
|
||||
jpeg->overshoot_deringing, jpeg->optimize_scans,
|
||||
jpeg->quant_table, jpeg->subsample_mode ) ) {
|
||||
jpeg->quant_table, jpeg->subsample_mode,
|
||||
jpeg->restart_interval ) ) {
|
||||
VIPS_UNREF( target );
|
||||
return( -1 );
|
||||
}
|
||||
@ -534,6 +550,7 @@ vips_foreign_save_jpeg_mime_init( VipsForeignSaveJpegMime *mime )
|
||||
* * @overshoot_deringing: %gboolean, overshoot samples with extreme values
|
||||
* * @optimize_scans: %gboolean, split DCT coefficients into separate scans
|
||||
* * @quant_table: %gint, quantization table index
|
||||
* * @restart_interval: %gint, restart interval in mcu
|
||||
*
|
||||
* Write a VIPS image to a file as JPEG.
|
||||
*
|
||||
@ -604,6 +621,12 @@ vips_foreign_save_jpeg_mime_init( VipsForeignSaveJpegMime *mime )
|
||||
* For maximum compression with mozjpeg, a useful set of options is `strip,
|
||||
* optimize-coding, interlace, optimize-scans, trellis-quant, quant_table=3`.
|
||||
*
|
||||
* By default, the output stream won't have restart markers. If a non-zero
|
||||
* restart_interval is specified, a restart marker will be added after each
|
||||
* specified number of MCU blocks. This makes the stream more recoverable
|
||||
* if there are transmission errors, but also allows for some decoders to read
|
||||
* part of the JPEG without decoding the whole stream.
|
||||
*
|
||||
* The image is automatically converted to RGB, Monochrome or CMYK before
|
||||
* saving.
|
||||
*
|
||||
@ -650,6 +673,7 @@ vips_jpegsave( VipsImage *in, const char *filename, ... )
|
||||
* * @overshoot_deringing: %gboolean, overshoot samples with extreme values
|
||||
* * @optimize_scans: %gboolean, split DCT coefficients into separate scans
|
||||
* * @quant_table: %gint, quantization table index
|
||||
* * @restart_interval: %gint, restart interval in mcu
|
||||
*
|
||||
* As vips_jpegsave(), but save to a target.
|
||||
*
|
||||
@ -689,6 +713,7 @@ vips_jpegsave_target( VipsImage *in, VipsTarget *target, ... )
|
||||
* * @overshoot_deringing: %gboolean, overshoot samples with extreme values
|
||||
* * @optimize_scans: %gboolean, split DCT coefficients into separate scans
|
||||
* * @quant_table: %gint, quantization table index
|
||||
* * @restart_interval: %gint, restart interval in mcu
|
||||
*
|
||||
* As vips_jpegsave(), but save to a memory buffer.
|
||||
*
|
||||
@ -745,6 +770,7 @@ vips_jpegsave_buffer( VipsImage *in, void **buf, size_t *len, ... )
|
||||
* * @overshoot_deringing: %gboolean, overshoot samples with extreme values
|
||||
* * @optimize_scans: %gboolean, split DCT coefficients into separate scans
|
||||
* * @quant_table: %gint, quantization table index
|
||||
* * @restart_interval: %gint, restart interval in mcu
|
||||
*
|
||||
* As vips_jpegsave(), but save as a mime jpeg on stdout.
|
||||
*
|
||||
|
@ -174,7 +174,7 @@ int vips__jpeg_write_target( VipsImage *in, VipsTarget *target,
|
||||
gboolean optimize_coding, gboolean progressive, gboolean strip,
|
||||
gboolean trellis_quant, gboolean overshoot_deringing,
|
||||
gboolean optimize_scans, int quant_table,
|
||||
VipsForeignSubsample subsample_mode );
|
||||
VipsForeignSubsample subsample_mode, int restart_interval );
|
||||
|
||||
int vips__jpeg_read_source( VipsSource *source, VipsImage *out,
|
||||
gboolean header_only, int shrink, int fail, gboolean autorotate );
|
||||
@ -252,5 +252,3 @@ extern const char *vips__jxl_suffs[];
|
||||
#endif /*__cplusplus*/
|
||||
|
||||
#endif /*VIPS_PFOREIGN_H*/
|
||||
|
||||
|
||||
|
@ -96,6 +96,8 @@
|
||||
* - add subsample_mode, deprecate no_subsample
|
||||
* 13/9/20
|
||||
* - only write JFIF resolution if we don't have EXIF
|
||||
* 7/10/21 Manthey
|
||||
* - add restart_interval
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -540,7 +542,7 @@ write_vips( Write *write, int qfac, const char *profile,
|
||||
gboolean optimize_coding, gboolean progressive, gboolean strip,
|
||||
gboolean trellis_quant, gboolean overshoot_deringing,
|
||||
gboolean optimize_scans, int quant_table,
|
||||
VipsForeignSubsample subsample_mode )
|
||||
VipsForeignSubsample subsample_mode, int restart_interval )
|
||||
{
|
||||
VipsImage *in;
|
||||
J_COLOR_SPACE space;
|
||||
@ -607,6 +609,10 @@ write_vips( Write *write, int qfac, const char *profile,
|
||||
*/
|
||||
write->cinfo.optimize_coding = optimize_coding;
|
||||
|
||||
/* Use a restart interval.
|
||||
*/
|
||||
if( restart_interval > 0 )
|
||||
write->cinfo.restart_interval = restart_interval;
|
||||
#ifdef HAVE_JPEG_EXT_PARAMS
|
||||
/* Apply trellis quantisation to each 8x8 block. Implies
|
||||
* "optimize_coding".
|
||||
@ -846,7 +852,8 @@ vips__jpeg_write_target( VipsImage *in, VipsTarget *target,
|
||||
gboolean optimize_coding, gboolean progressive,
|
||||
gboolean strip, gboolean trellis_quant,
|
||||
gboolean overshoot_deringing, gboolean optimize_scans,
|
||||
int quant_table, VipsForeignSubsample subsample_mode)
|
||||
int quant_table, VipsForeignSubsample subsample_mode,
|
||||
int restart_interval)
|
||||
{
|
||||
Write *write;
|
||||
|
||||
@ -873,7 +880,7 @@ vips__jpeg_write_target( VipsImage *in, VipsTarget *target,
|
||||
if( write_vips( write,
|
||||
Q, profile, optimize_coding, progressive, strip,
|
||||
trellis_quant, overshoot_deringing, optimize_scans,
|
||||
quant_table, subsample_mode ) ) {
|
||||
quant_table, subsample_mode, restart_interval ) ) {
|
||||
write_destroy( write );
|
||||
return( -1 );
|
||||
}
|
||||
|
@ -294,6 +294,15 @@ class TestForeign:
|
||||
assert len(q90_subsample_on) < len(q90)
|
||||
assert len(q90_subsample_off) == len(q90_subsample_auto)
|
||||
|
||||
# A non-zero restart_interval should result in a bigger file.
|
||||
# Otherwise, smaller restart intervals will have more restart markers
|
||||
# and therefore be larger
|
||||
r0 = im.jpegsave_buffer(restart_interval=0)
|
||||
r10 = im.jpegsave_buffer(restart_interval=10)
|
||||
r2 = im.jpegsave_buffer(restart_interval=2)
|
||||
assert len(r10) > len(r0)
|
||||
assert len(r2) > len(r10)
|
||||
|
||||
@skip_if_no("jpegload")
|
||||
def test_truncated(self):
|
||||
# This should open (there's enough there for the header)
|
||||
|
Loading…
Reference in New Issue
Block a user