add new param to gifsave docs

also note in changelog and revise layout for 80 columns

see https://github.com/libvips/libvips/pull/2628
This commit is contained in:
John Cupitt 2022-02-28 16:38:02 +00:00
parent b0f993eec8
commit 3b6072437d
2 changed files with 41 additions and 21 deletions

View File

@ -13,6 +13,7 @@
- add @unlimited to heifload [lovell] - add @unlimited to heifload [lovell]
- edge antialiasing for mapim with @background, @extend and @premultiplied - edge antialiasing for mapim with @background, @extend and @premultiplied
- add support for regions in C++ API [shado23] - add support for regions in C++ API [shado23]
- add maxerror to gifsave [dloebl]
26/11/21 started 8.12.3 26/11/21 started 8.12.3
- better arg checking for hist_find_ndim [travisbell] - better arg checking for hist_find_ndim [travisbell]

View File

@ -161,21 +161,24 @@ static int vips__cgif_write( void *target, const uint8_t *buffer,
} }
/* Compare pixels in a lossy way (allow a slight colour difference). /* Compare pixels in a lossy way (allow a slight colour difference).
In combination with the GIF transparency optimization this leads to * In combination with the GIF transparency optimization this leads to
less difference between frames and therefore improves the compression ratio. * less difference between frames and therefore improves the compression ratio.
*/ */
static gboolean static gboolean
cgif_pixels_are_equal(const VipsPel *cur, const VipsPel *bef, double maxerror) vips_foreign_save_cgif_pixels_are_equal( const VipsPel *cur, const VipsPel *bef,
double maxerror )
{ {
if( bef[3] != 0xFF ) if( bef[3] != 0xFF )
/* Done. Cannot compare with alpha channel in frame before. /* Solid pixels only.
*/ */
return FALSE; return FALSE;
/* Test Euclidean distance between the two points. /* Test Euclidean distance between the two points.
*/ */
const int dR = cur[0] - bef[0]; const int dR = cur[0] - bef[0];
const int dG = cur[1] - bef[1]; const int dG = cur[1] - bef[1];
const int dB = cur[2] - bef[2]; const int dB = cur[2] - bef[2];
return( sqrt( dR * dR + dG * dG + dB * dB ) <= maxerror ); return( sqrt( dR * dR + dG * dG + dB * dB ) <= maxerror );
} }
@ -241,11 +244,13 @@ vips_foreign_save_cgif_write_frame( VipsForeignSaveCgif *cgif )
* attach it as a local cmap when we write. * attach it as a local cmap when we write.
*/ */
if( cgif->quantisation_result ) if( cgif->quantisation_result )
cgif->cgif_config.attrFlags |= CGIF_ATTR_NO_GLOBAL_TABLE; cgif->cgif_config.attrFlags |=
CGIF_ATTR_NO_GLOBAL_TABLE;
VIPS_FREEF( vips__quantise_result_destroy, cgif->quantisation_result ); VIPS_FREEF( vips__quantise_result_destroy,
if( vips__quantise_image_quantize( cgif->input_image, cgif->attr, cgif->quantisation_result );
&cgif->quantisation_result ) ) { if( vips__quantise_image_quantize( cgif->input_image,
cgif->attr, &cgif->quantisation_result ) ) {
vips_error( class->nickname, vips_error( class->nickname,
"%s", _( "quantisation failed" ) ); "%s", _( "quantisation failed" ) );
return( -1 ); return( -1 );
@ -258,15 +263,17 @@ vips_foreign_save_cgif_write_frame( VipsForeignSaveCgif *cgif )
/* Dither frame. /* Dither frame.
*/ */
vips__quantise_set_dithering_level( cgif->quantisation_result, cgif->dither ); vips__quantise_set_dithering_level( cgif->quantisation_result,
cgif->dither );
if( vips__quantise_write_remapped_image( cgif->quantisation_result, if( vips__quantise_write_remapped_image( cgif->quantisation_result,
cgif->input_image, cgif->index, n_pels ) ) { cgif->input_image, cgif->index, n_pels ) ) {
vips_error( class->nickname, "%s", _( "dither failed" ) ); vips_error( class->nickname, "%s", _( "dither failed" ) );
return( -1 ); return( -1 );
} }
/* Call vips__quantise_get_palette() after vips__quantise_write_remapped_image(), /* Call vips__quantise_get_palette() after
* as palette is improved during remapping. * vips__quantise_write_remapped_image(), as palette is improved
* during remapping.
*/ */
cgif->lp = vips__quantise_get_palette( cgif->quantisation_result ); cgif->lp = vips__quantise_get_palette( cgif->quantisation_result );
rgb = cgif->palette_rgb; rgb = cgif->palette_rgb;
@ -331,7 +338,8 @@ vips_foreign_save_cgif_write_frame( VipsForeignSaveCgif *cgif )
frame_config.transIndex = 0; frame_config.transIndex = 0;
} }
/* Do the transparency trick (only possible when no alpha channel present) /* Pixels which are equal to pixels in the previous frame can be made
* transparent.
*/ */
if( cgif->frame_bytes_head ) { if( cgif->frame_bytes_head ) {
VipsPel *cur, *bef; VipsPel *cur, *bef;
@ -340,12 +348,12 @@ vips_foreign_save_cgif_write_frame( VipsForeignSaveCgif *cgif )
bef = cgif->frame_bytes_head; bef = cgif->frame_bytes_head;
if( !cgif->has_transparency ) { if( !cgif->has_transparency ) {
const uint8_t trans_index = cgif->lp->count; const uint8_t trans_index = cgif->lp->count;
const double maxerror = cgif->maxerror;
int i; int i;
for( i = 0; i < n_pels; i++ ) { for( i = 0; i < n_pels; i++ ) {
if( cgif_pixels_are_equal( cur, bef, maxerror ) ) if( vips_foreign_save_cgif_pixels_are_equal(
cur, bef, cgif->maxerror ) )
cgif->index[i] = trans_index; cgif->index[i] = trans_index;
else { else {
bef[0] = cur[0]; bef[0] = cur[0];
@ -353,14 +361,18 @@ vips_foreign_save_cgif_write_frame( VipsForeignSaveCgif *cgif )
bef[2] = cur[2]; bef[2] = cur[2];
bef[3] = cur[3]; bef[3] = cur[3];
} }
cur += 4; cur += 4;
bef += 4; bef += 4;
} }
frame_config.attrFlags |= CGIF_FRAME_ATTR_HAS_SET_TRANS;
frame_config.attrFlags |=
CGIF_FRAME_ATTR_HAS_SET_TRANS;
frame_config.transIndex = trans_index; frame_config.transIndex = trans_index;
} else { }
/* Transparency trick not possible (alpha channel present). else {
* Update head. /* Transparency trick not possible (alpha channel
* present). Update head.
*/ */
memcpy( bef, cur, 4 * n_pels); memcpy( bef, cur, 4 * n_pels);
} }
@ -600,8 +612,8 @@ vips_foreign_save_cgif_class_init( VipsForeignSaveCgifClass *class )
1, 8, 8 ); 1, 8, 8 );
VIPS_ARG_DOUBLE( class, "maxerror", 13, VIPS_ARG_DOUBLE( class, "maxerror", 13,
_( "Max. error for lossy transparency setting"), _( "Maximum error" ),
_( "Maximum pixel error to allow when identifying areas as identical (inter frame)" ), _( "Maximum inter-frame error for transparency" ),
VIPS_ARGUMENT_OPTIONAL_INPUT, VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignSaveCgif, maxerror ), G_STRUCT_OFFSET( VipsForeignSaveCgif, maxerror ),
0, 32, 0.0 ); 0, 32, 0.0 );
@ -794,8 +806,9 @@ vips_foreign_save_cgif_buffer_init( VipsForeignSaveCgifBuffer *buffer )
* * @dither: %gdouble, quantisation dithering level * * @dither: %gdouble, quantisation dithering level
* * @effort: %gint, quantisation CPU effort * * @effort: %gint, quantisation CPU effort
* * @bitdepth: %gint, number of bits per pixel * * @bitdepth: %gint, number of bits per pixel
* * @maxerror: %gdouble, maximum inter-frame error for transparency
* *
* Write a VIPS image to a file as GIF. * Write to a file in GIF format.
* *
* Use @dither to set the degree of Floyd-Steinberg dithering * Use @dither to set the degree of Floyd-Steinberg dithering
* and @effort to control the CPU effort (1 is the fastest, * and @effort to control the CPU effort (1 is the fastest,
@ -806,6 +819,10 @@ vips_foreign_save_cgif_buffer_init( VipsForeignSaveCgifBuffer *buffer )
* always reserved for transparency. For example, a bitdepth of * always reserved for transparency. For example, a bitdepth of
* 4 will allow the output to contain up to 15 colours. * 4 will allow the output to contain up to 15 colours.
* *
* Use @maxerror to set the threshold below which pixels are considered equal.
* Pixels which don't change from frame to frame can be made transparent,
* improving the compression rate. Default 0.
*
* See also: vips_image_new_from_file(). * See also: vips_image_new_from_file().
* *
* Returns: 0 on success, -1 on error. * Returns: 0 on success, -1 on error.
@ -835,6 +852,7 @@ vips_gifsave( VipsImage *in, const char *filename, ... )
* * @dither: %gdouble, quantisation dithering level * * @dither: %gdouble, quantisation dithering level
* * @effort: %gint, quantisation CPU effort * * @effort: %gint, quantisation CPU effort
* * @bitdepth: %gint, number of bits per pixel * * @bitdepth: %gint, number of bits per pixel
* * @maxerror: %gdouble, maximum inter-frame error for transparency
* *
* As vips_gifsave(), but save to a memory buffer. * As vips_gifsave(), but save to a memory buffer.
* *
@ -885,6 +903,7 @@ vips_gifsave_buffer( VipsImage *in, void **buf, size_t *len, ... )
* * @dither: %gdouble, quantisation dithering level * * @dither: %gdouble, quantisation dithering level
* * @effort: %gint, quantisation CPU effort * * @effort: %gint, quantisation CPU effort
* * @bitdepth: %gint, number of bits per pixel * * @bitdepth: %gint, number of bits per pixel
* * @maxerror: %gdouble, maximum inter-frame error for transparency
* *
* As vips_gifsave(), but save to a target. * As vips_gifsave(), but save to a target.
* *