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:
parent
f2e5b7e9da
commit
e16cb5bfe5
@ -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 =
|
||||
|
@ -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[];
|
||||
|
||||
|
@ -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" ) );
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user