quantise: optional thresholding of alpha channel (#2425)

Moving this logic from gifsave to quantise improves
the performance of writing RGBA GIFs by ~15%
This commit is contained in:
Lovell Fuller 2021-09-07 12:23:42 +01:00 committed by GitHub
parent f2e5b7e9da
commit e16cb5bfe5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 33 additions and 29 deletions

View File

@ -86,7 +86,7 @@ vips_foreign_save_cgif_build( VipsObject *object )
VipsForeignSave *save = (VipsForeignSave *) object;
VipsForeignSaveCgif *cgif = (VipsForeignSaveCgif *) object;
VipsImage **t = (VipsImage **)
vips_object_local_array( VIPS_OBJECT( cgif ), 6 );
vips_object_local_array( VIPS_OBJECT( cgif ), 2 );
int rgb;
int rgba;
@ -117,31 +117,17 @@ vips_foreign_save_cgif_build( VipsObject *object )
if( vips_image_get_typeof( save->ready, "loop" ) )
vips_image_get_int( save->ready, "loop", &loop );
/* Threshold alpha, if any, to fully transparent or opaque
/* Generate indexed image (t[0]) and palette (t[1])
*/
if( vips_image_hasalpha( save->ready ) ) {
if( vips_extract_band( save->ready, &t[0], 0, "n", 3, NULL ) ||
vips_extract_band( save->ready, &t[1], 3, NULL ) ||
vips_moreeq_const1( t[1], &t[2], 128, NULL ) ||
vips_bandjoin2( t[0], t[2], &t[3], NULL ) )
return( -1 );
VIPS_UNREF( save->ready );
save->ready = t[3];
g_object_ref( save->ready );
}
/* Generate indexed image (t[4]) and palette (t[5])
*/
if( vips__quantise_image( save->ready, &t[4], &t[5],
255, 100, cgif->dither, cgif->effort ) )
if( vips__quantise_image( save->ready, &t[0], &t[1],
255, 100, cgif->dither, cgif->effort, TRUE ) )
return( -1 );
/* Convert palette to RGB
*/
paletteRgba = (uint8_t *) VIPS_IMAGE_ADDR( t[5], 0, 0 );
paletteRgb = g_malloc0( t[5]->Xsize * 3 );
for( rgb = 0, rgba = 0; rgb < t[5]->Xsize * 3; rgb += 3 ) {
paletteRgba = (uint8_t *) VIPS_IMAGE_ADDR( t[1], 0, 0 );
paletteRgb = g_malloc0( t[1]->Xsize * 3 );
for( rgb = 0, rgba = 0; rgb < t[1]->Xsize * 3; rgb += 3 ) {
paletteRgb[rgb] = paletteRgba[rgba];
paletteRgb[rgb + 1] = paletteRgba[rgba + 1];
paletteRgb[rgb + 2] = paletteRgba[rgba + 2];
@ -156,10 +142,10 @@ vips_foreign_save_cgif_build( VipsObject *object )
/* Initiialise cgif
*/
memset( &cgif_config, 0, sizeof( CGIF_Config ) );
cgif_config.width = t[4]->Xsize;
cgif_config.width = t[0]->Xsize;
cgif_config.height = page_height;
cgif_config.pGlobalPalette = paletteRgb;
cgif_config.numGlobalPaletteEntries = t[5]->Xsize;
cgif_config.numGlobalPaletteEntries = t[1]->Xsize;
cgif_config.numLoops = loop;
cgif_config.attrFlags = CGIF_ATTR_IS_ANIMATED;
if( has_transparency )
@ -171,12 +157,12 @@ vips_foreign_save_cgif_build( VipsObject *object )
/* Add each vips page as a cgif frame
*/
for( top = 0; top < t[4]->Ysize; top += page_height ) {
for( top = 0; top < t[0]->Ysize; top += page_height ) {
int page_index = top / page_height;
memset( &cgif_frame_config, 0, sizeof( CGIF_FrameConfig ) );
cgif_frame_config.pImageData = (uint8_t *)
VIPS_IMAGE_ADDR( t[4], 0, top );
VIPS_IMAGE_ADDR( t[0], 0, top );
if( delay &&
page_index < delay_length )
cgif_frame_config.delay =

View File

@ -219,7 +219,8 @@ int vips__webp_write_target( VipsImage *image, VipsTarget *target,
int vips__quantise_image( VipsImage *in,
VipsImage **index_out, VipsImage **palette_out,
int colours, int Q, double dither, int effort );
int colours, int Q, double dither, int effort,
gboolean threshold_alpha );
extern const char *vips_foreign_nifti_suffs[];

View File

@ -106,13 +106,15 @@ vips__quantise_new( VipsImage *in,
int
vips__quantise_image( VipsImage *in,
VipsImage **index_out, VipsImage **palette_out,
int colours, int Q, double dither, int effort )
int colours, int Q, double dither, int effort,
gboolean threshold_alpha )
{
Quantise *quantise;
VipsImage *index;
VipsImage *palette;
const liq_palette *lp;
int i;
gboolean added_alpha = FALSE;
quantise = vips__quantise_new( in, index_out, palette_out,
colours, Q, dither, effort );
@ -135,6 +137,7 @@ vips__quantise_image( VipsImage *in,
vips__quantise_free( quantise );
return( -1 );
}
added_alpha = TRUE;
in = quantise->t[1];
}
@ -144,6 +147,19 @@ vips__quantise_image( VipsImage *in,
}
in = quantise->t[2];
/* Threshold alpha channel.
*/
if( threshold_alpha && !added_alpha ) {
VipsPel * restrict p = VIPS_IMAGE_ADDR( in, 0, 0 );
const guint64 n_pels = VIPS_IMAGE_N_PELS( in );
guint64 i;
for( i = 0; i < n_pels; i++ ) {
p[3] = p[3] > 128 ? 255 : 0;
p += 4;
}
}
quantise->attr = liq_attr_create();
liq_set_max_colors( quantise->attr, colours );
liq_set_quality( quantise->attr, 0, Q );
@ -215,7 +231,8 @@ vips__quantise_image( VipsImage *in,
int
vips__quantise_image( VipsImage *in,
VipsImage **index_out, VipsImage **palette_out,
int colours, int Q, double dither )
int colours, int Q, double dither, int effort,
gboolean threshold_alpha )
{
vips_error( "vips__quantise_image",
"%s", _( "libvips not built with quantisation support" ) );

View File

@ -1190,7 +1190,7 @@ write_vips( Write *write,
int trans_count;
if( vips__quantise_image( in, &im_index, &im_palette,
1 << bitdepth, Q, dither, effort ) )
1 << bitdepth, Q, dither, effort, FALSE ) )
return( -1 );
palette_count = im_palette->Xsize;