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; VipsForeignSave *save = (VipsForeignSave *) object;
VipsForeignSaveCgif *cgif = (VipsForeignSaveCgif *) object; VipsForeignSaveCgif *cgif = (VipsForeignSaveCgif *) object;
VipsImage **t = (VipsImage **) VipsImage **t = (VipsImage **)
vips_object_local_array( VIPS_OBJECT( cgif ), 6 ); vips_object_local_array( VIPS_OBJECT( cgif ), 2 );
int rgb; int rgb;
int rgba; int rgba;
@ -117,31 +117,17 @@ vips_foreign_save_cgif_build( VipsObject *object )
if( vips_image_get_typeof( save->ready, "loop" ) ) if( vips_image_get_typeof( save->ready, "loop" ) )
vips_image_get_int( save->ready, "loop", &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__quantise_image( save->ready, &t[0], &t[1],
if( vips_extract_band( save->ready, &t[0], 0, "n", 3, NULL ) || 255, 100, cgif->dither, cgif->effort, TRUE ) )
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 ) )
return( -1 ); return( -1 );
/* Convert palette to RGB /* Convert palette to RGB
*/ */
paletteRgba = (uint8_t *) VIPS_IMAGE_ADDR( t[5], 0, 0 ); paletteRgba = (uint8_t *) VIPS_IMAGE_ADDR( t[1], 0, 0 );
paletteRgb = g_malloc0( t[5]->Xsize * 3 ); paletteRgb = g_malloc0( t[1]->Xsize * 3 );
for( rgb = 0, rgba = 0; rgb < t[5]->Xsize * 3; rgb += 3 ) { for( rgb = 0, rgba = 0; rgb < t[1]->Xsize * 3; rgb += 3 ) {
paletteRgb[rgb] = paletteRgba[rgba]; paletteRgb[rgb] = paletteRgba[rgba];
paletteRgb[rgb + 1] = paletteRgba[rgba + 1]; paletteRgb[rgb + 1] = paletteRgba[rgba + 1];
paletteRgb[rgb + 2] = paletteRgba[rgba + 2]; paletteRgb[rgb + 2] = paletteRgba[rgba + 2];
@ -156,10 +142,10 @@ vips_foreign_save_cgif_build( VipsObject *object )
/* Initiialise cgif /* Initiialise cgif
*/ */
memset( &cgif_config, 0, sizeof( CGIF_Config ) ); 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.height = page_height;
cgif_config.pGlobalPalette = paletteRgb; cgif_config.pGlobalPalette = paletteRgb;
cgif_config.numGlobalPaletteEntries = t[5]->Xsize; cgif_config.numGlobalPaletteEntries = t[1]->Xsize;
cgif_config.numLoops = loop; cgif_config.numLoops = loop;
cgif_config.attrFlags = CGIF_ATTR_IS_ANIMATED; cgif_config.attrFlags = CGIF_ATTR_IS_ANIMATED;
if( has_transparency ) if( has_transparency )
@ -171,12 +157,12 @@ vips_foreign_save_cgif_build( VipsObject *object )
/* Add each vips page as a cgif frame /* 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; int page_index = top / page_height;
memset( &cgif_frame_config, 0, sizeof( CGIF_FrameConfig ) ); memset( &cgif_frame_config, 0, sizeof( CGIF_FrameConfig ) );
cgif_frame_config.pImageData = (uint8_t *) cgif_frame_config.pImageData = (uint8_t *)
VIPS_IMAGE_ADDR( t[4], 0, top ); VIPS_IMAGE_ADDR( t[0], 0, top );
if( delay && if( delay &&
page_index < delay_length ) page_index < delay_length )
cgif_frame_config.delay = 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, int vips__quantise_image( VipsImage *in,
VipsImage **index_out, VipsImage **palette_out, 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[]; extern const char *vips_foreign_nifti_suffs[];

View File

@ -106,13 +106,15 @@ vips__quantise_new( VipsImage *in,
int int
vips__quantise_image( VipsImage *in, vips__quantise_image( VipsImage *in,
VipsImage **index_out, VipsImage **palette_out, 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; Quantise *quantise;
VipsImage *index; VipsImage *index;
VipsImage *palette; VipsImage *palette;
const liq_palette *lp; const liq_palette *lp;
int i; int i;
gboolean added_alpha = FALSE;
quantise = vips__quantise_new( in, index_out, palette_out, quantise = vips__quantise_new( in, index_out, palette_out,
colours, Q, dither, effort ); colours, Q, dither, effort );
@ -135,6 +137,7 @@ vips__quantise_image( VipsImage *in,
vips__quantise_free( quantise ); vips__quantise_free( quantise );
return( -1 ); return( -1 );
} }
added_alpha = TRUE;
in = quantise->t[1]; in = quantise->t[1];
} }
@ -144,6 +147,19 @@ vips__quantise_image( VipsImage *in,
} }
in = quantise->t[2]; 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(); quantise->attr = liq_attr_create();
liq_set_max_colors( quantise->attr, colours ); liq_set_max_colors( quantise->attr, colours );
liq_set_quality( quantise->attr, 0, Q ); liq_set_quality( quantise->attr, 0, Q );
@ -215,7 +231,8 @@ vips__quantise_image( VipsImage *in,
int int
vips__quantise_image( VipsImage *in, vips__quantise_image( VipsImage *in,
VipsImage **index_out, VipsImage **palette_out, 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", vips_error( "vips__quantise_image",
"%s", _( "libvips not built with quantisation support" ) ); "%s", _( "libvips not built with quantisation support" ) );

View File

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