diff --git a/ChangeLog b/ChangeLog index 1a318b9d0e..621710e1bf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -18,6 +18,7 @@ master - threadpools size dynamically with load - operations can hint threadpool size - support for N-colour ICC profiles +- add vips_palette() to compute a palette for an RGBA image 11/10/22 started 8.13.3 - improve rules for 16-bit heifsave [johntrunc] diff --git a/libvips/conversion/conversion.c b/libvips/conversion/conversion.c index 5886b617d1..a6369ebca4 100644 --- a/libvips/conversion/conversion.c +++ b/libvips/conversion/conversion.c @@ -359,101 +359,105 @@ vips_conversion_init( VipsConversion *conversion ) void vips_conversion_operation_init( void ) { - extern GType vips_copy_get_type( void ); - extern GType vips_tile_cache_get_type( void ); - extern GType vips_line_cache_get_type( void ); - extern GType vips_sequential_get_type( void ); + extern GType vips_arrayjoin_get_type( void ); + extern GType vips_autorot_get_type( void ); + extern GType vips_bandbool_get_type( void ); + extern GType vips_bandfold_get_type( void ); + extern GType vips_bandjoin_const_get_type( void ); + extern GType vips_bandjoin_get_type( void ); + extern GType vips_bandmean_get_type( void ); + extern GType vips_bandrank_get_type( void ); + extern GType vips_bandunfold_get_type( void ); + extern GType vips_black_get_type( void ); + extern GType vips_byteswap_get_type( void ); extern GType vips_cache_get_type( void ); + extern GType vips_cast_get_type( void ); + extern GType vips_composite2_get_type( void ); + extern GType vips_composite_get_type( void ); + extern GType vips_copy_get_type( void ); + extern GType vips_crop_get_type( void ); + extern GType vips_dither_get_type( void ); extern GType vips_embed_get_type( void ); - extern GType vips_gravity_get_type( void ); + extern GType vips_extract_area_get_type( void ); + extern GType vips_extract_band_get_type( void ); + extern GType vips_falsecolour_get_type( void ); + extern GType vips_flatten_get_type( void ); extern GType vips_flip_get_type( void ); + extern GType vips_gamma_get_type( void ); + extern GType vips_gaussnoise_get_type( void ); + extern GType vips_gravity_get_type( void ); + extern GType vips_grid_get_type( void ); + extern GType vips_ifthenelse_get_type( void ); extern GType vips_insert_get_type( void ); extern GType vips_join_get_type( void ); - extern GType vips_arrayjoin_get_type( void ); - extern GType vips_extract_area_get_type( void ); - extern GType vips_crop_get_type( void ); - extern GType vips_smartcrop_get_type( void ); - extern GType vips_extract_band_get_type( void ); + extern GType vips_line_cache_get_type( void ); + extern GType vips_msb_get_type( void ); + extern GType vips_palette_get_type( void ); + extern GType vips_premultiply_get_type( void ); + extern GType vips_recomb_get_type( void ); extern GType vips_replicate_get_type( void ); - extern GType vips_cast_get_type( void ); - extern GType vips_bandjoin_get_type( void ); - extern GType vips_bandjoin_const_get_type( void ); - extern GType vips_bandrank_get_type( void ); - extern GType vips_black_get_type( void ); - extern GType vips_rot_get_type( void ); extern GType vips_rot45_get_type( void ); - extern GType vips_autorot_get_type( void ); - extern GType vips_ifthenelse_get_type( void ); + extern GType vips_rot_get_type( void ); + extern GType vips_scale_get_type( void ); + extern GType vips_sequential_get_type( void ); + extern GType vips_smartcrop_get_type( void ); + extern GType vips_subsample_get_type( void ); extern GType vips_switch_get_type( void ); - extern GType vips_recomb_get_type( void ); - extern GType vips_bandmean_get_type( void ); - extern GType vips_bandfold_get_type( void ); - extern GType vips_bandunfold_get_type( void ); - extern GType vips_flatten_get_type( void ); - extern GType vips_premultiply_get_type( void ); - extern GType vips_unpremultiply_get_type( void ); - extern GType vips_bandbool_get_type( void ); - extern GType vips_gaussnoise_get_type( void ); - extern GType vips_grid_get_type( void ); + extern GType vips_tile_cache_get_type( void ); extern GType vips_transpose3d_get_type( void ); - extern GType vips_scale_get_type( void ); + extern GType vips_unpremultiply_get_type( void ); extern GType vips_wrap_get_type( void ); - extern GType vips_zoom_get_type( void ); - extern GType vips_subsample_get_type( void ); - extern GType vips_msb_get_type( void ); - extern GType vips_byteswap_get_type( void ); extern GType vips_xyz_get_type( void ); - extern GType vips_falsecolour_get_type( void ); - extern GType vips_gamma_get_type( void ); - extern GType vips_composite_get_type( void ); - extern GType vips_composite2_get_type( void ); + extern GType vips_zoom_get_type( void ); - vips_copy_get_type(); - vips_tile_cache_get_type(); - vips_line_cache_get_type(); - vips_sequential_get_type(); + vips_arrayjoin_get_type(); + vips_autorot_get_type(); + vips_bandbool_get_type(); + vips_bandfold_get_type(); + vips_bandjoin_const_get_type(); + vips_bandjoin_get_type(); + vips_bandmean_get_type(); + vips_bandrank_get_type(); + vips_bandunfold_get_type(); + vips_black_get_type(); + vips_byteswap_get_type(); vips_cache_get_type(); + vips_cast_get_type(); + vips_composite2_get_type(); + vips_composite_get_type(); + vips_copy_get_type(); + vips_crop_get_type(); + vips_dither_get_type(); vips_embed_get_type(); - vips_gravity_get_type(); + vips_extract_area_get_type(); + vips_extract_band_get_type(); + vips_falsecolour_get_type(); + vips_flatten_get_type(); vips_flip_get_type(); + vips_gamma_get_type(); + vips_gaussnoise_get_type(); + vips_gravity_get_type(); + vips_grid_get_type(); + vips_ifthenelse_get_type(); vips_insert_get_type(); vips_join_get_type(); - vips_arrayjoin_get_type(); - vips_extract_area_get_type(); - vips_crop_get_type(); - vips_smartcrop_get_type(); - vips_extract_band_get_type(); + vips_line_cache_get_type(); + vips_msb_get_type(); + vips_palette_get_type(); + vips_premultiply_get_type(); + vips_recomb_get_type(); vips_replicate_get_type(); - vips_cast_get_type(); - vips_bandjoin_get_type(); - vips_bandjoin_const_get_type(); - vips_bandrank_get_type(); - vips_black_get_type(); - vips_rot_get_type(); vips_rot45_get_type(); - vips_autorot_get_type(); - vips_ifthenelse_get_type(); + vips_rot_get_type(); + vips_scale_get_type(); + vips_sequential_get_type(); + vips_smartcrop_get_type(); + vips_subsample_get_type(); vips_switch_get_type(); - vips_recomb_get_type(); - vips_bandmean_get_type(); - vips_bandfold_get_type(); - vips_bandunfold_get_type(); - vips_flatten_get_type(); - vips_premultiply_get_type(); - vips_unpremultiply_get_type(); - vips_bandbool_get_type(); - vips_gaussnoise_get_type(); - vips_grid_get_type(); + vips_tile_cache_get_type(); vips_transpose3d_get_type(); - vips_scale_get_type(); + vips_unpremultiply_get_type(); vips_wrap_get_type(); - vips_zoom_get_type(); - vips_subsample_get_type(); - vips_msb_get_type(); - vips_byteswap_get_type(); vips_xyz_get_type(); - vips_falsecolour_get_type(); - vips_gamma_get_type(); - vips_composite_get_type(); - vips_composite2_get_type(); + vips_zoom_get_type(); } diff --git a/libvips/conversion/dither.c b/libvips/conversion/dither.c new file mode 100644 index 0000000000..750575cf90 --- /dev/null +++ b/libvips/conversion/dither.c @@ -0,0 +1,288 @@ +/* find a dither + * + * 25/9/22 + * - from cgifsave.c + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include + +#include "pconversion.h" +#include "../foreign/quantise.h" + +typedef struct _VipsDither { + VipsConversion parent_instance; + + VipsImage *in; + VipsImage *palette; + double dither; + + VipsQuantiseAttr *attr; + VipsQuantiseImage *image; + VipsQuantiseResult *result; + +} VipsDither; + +typedef VipsConversionClass VipsDitherClass; + +G_DEFINE_TYPE( VipsDither, vips_dither, VIPS_TYPE_CONVERSION ); + +static void +vips_dither_dispose( GObject *gobject ) +{ + VipsDither *dither = (VipsDither *) gobject; + + VIPS_FREEF( vips__quantise_result_destroy, dither->result ); + VIPS_FREEF( vips__quantise_image_destroy, dither->image ); + VIPS_FREEF( vips__quantise_attr_destroy, dither->attr ); + + G_OBJECT_CLASS( vips_dither_parent_class )->dispose( gobject ); +} + +static int +vips_dither_to_rgba( VipsDither *dither, VipsImage *in, VipsImage **out ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( dither ); + VipsImage **t = (VipsImage **) + vips_object_local_array( VIPS_OBJECT( dither ), 1 ); + + VipsImage *rgba; + + rgba = in; + + if( vips_check_uncoded( class->nickname, rgba ) || + vips_check_format( class->nickname, rgba, VIPS_FORMAT_UCHAR ) || + vips_check_bands_atleast( class->nickname, rgba, 3 ) ) + return( -1 ); + + if( rgba->Bands == 3 ) { + if( vips_addalpha( rgba, &t[0], NULL ) ) + return( -1 ); + rgba = t[0]; + } + else if( rgba->Bands > 4 ) { + if( vips_extract_band( rgba, &t[0], 0, "n", 4, NULL ) ) + return( -1 ); + rgba = t[0]; + } + + g_object_ref( rgba ); + *out = rgba; + + return( 0 ); +} + +static int +vips_dither_build( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsConversion *conversion = VIPS_CONVERSION( object ); + VipsDither *dither = (VipsDither *) object; + VipsImage **t = (VipsImage **) vips_object_local_array( object, 5 ); + + VipsImage *in; + VipsImage *palette; + guint32 fake_image[257]; + int n_colours; + + if( VIPS_OBJECT_CLASS( vips_dither_parent_class )->build( object ) ) + return( -1 ); + + in = dither->in; + palette = dither->palette; + + /* The palette can't have more than 256 entries. + */ + if( palette->Xsize != 1 && + palette->Ysize != 1 ) { + vips_error( class->nickname, "%s", + _( "palettes must have width or height 1" ) ); + return( -1 ); + } + if( VIPS_IMAGE_N_PELS( palette ) > 256 ) { + vips_error( class->nickname, "%s", + _( "palettes must have not have more than " + "256 elements" ) ); + return( -1 ); + } + n_colours = palette->Xsize * palette->Ysize; + + /* We only work for 8-bit RGBA images. + */ + if( vips_dither_to_rgba( dither, in, &t[0] ) || + vips_dither_to_rgba( dither, palette, &t[1] ) ) + return( -1 ); + in = t[0]; + palette = t[1]; + + /* We need the whole thing in memory. + */ + if( vips_image_wio_input( in ) || + vips_image_wio_input( palette ) ) + return( -1 ); + + dither->attr = vips__quantise_attr_create(); + vips__quantise_set_max_colors( dither->attr, n_colours ); + vips__quantise_set_quality( dither->attr, 0, 100 ); + + /* Make a fake image from the input palette and quantise that to get + * the context we use for dithering. + */ + memcpy( fake_image, VIPS_IMAGE_ADDR( palette, 0, 0 ), + n_colours * sizeof( int ) ); + dither->image = vips__quantise_image_create_rgba( dither->attr, + fake_image, n_colours, 1, 0.0 ); + if( vips__quantise_image_quantize_fixed( dither->image, + dither->attr, &dither->result ) ) { + vips_error( class->nickname, + "%s", _( "quantisation failed" ) ); + return( -1 ); + } + VIPS_FREEF( vips__quantise_image_destroy, dither->image ); + +{ + const VipsQuantisePalette *lp = + vips__quantise_get_palette( dither->result ); + + for( int i = 0; i < lp->count; i++ ) + printf( "%d) r = %d, g = %d, b = %d, a = %d\n", + i, + lp->entries[i].r, + lp->entries[i].g, + lp->entries[i].b, + lp->entries[i].a ); +} + + /* The frame index buffer. + */ + vips_image_init_fields( conversion->out, + in->Xsize, in->Ysize, 1, + VIPS_FORMAT_UCHAR, + VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W, 1.0, 1.0 ); + if( vips_image_write_prepare( conversion->out ) ) + return( -1 ); + dither->image = vips__quantise_image_create_rgba( dither->attr, + VIPS_IMAGE_ADDR( in, 0, 0 ), in->Xsize, in->Ysize, 0.0 ); + + /* Now dither! + */ + vips__quantise_set_dithering_level( dither->result, dither->dither ); + if( vips__quantise_write_remapped_image( dither->result, dither->image, + VIPS_IMAGE_ADDR( conversion->out, 0, 0 ), + VIPS_IMAGE_N_PELS( conversion->out ) ) ) { + vips_error( class->nickname, "%s", _( "dither failed" ) ); + return( -1 ); + } + + return( 0 ); +} + +static void +vips_dither_class_init( VipsDitherClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class ); + + gobject_class->dispose = vips_dither_dispose; + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + vobject_class->nickname = "dither"; + vobject_class->description = _( "dither image into palette" ); + vobject_class->build = vips_dither_build; + + operation_class->flags = VIPS_OPERATION_SEQUENTIAL; + + VIPS_ARG_IMAGE( class, "in", 0, + _( "Input" ), + _( "Input image" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsDither, in ) ); + + VIPS_ARG_IMAGE( class, "palette", 3, + _( "Palette" ), + _( "Palette image" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsDither, palette ) ); + + VIPS_ARG_DOUBLE( class, "dither", 10, + _( "Dithering" ), + _( "Amount of dithering" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsDither, dither ), + 0.0, 1.0, 1.0 ); + +} + +static void +vips_dither_init( VipsDither *dither ) +{ +} + +/** + * vips_dither: (method) + * @in: input image + * @out: (out): output image + * @palette: (in): palette image + * @...: %NULL-terminated list of optional named arguments + * + * Optional arguments: + * + * * @dither: %gdouble, dithering level + * + * Dither @in using @palette. + * + * Use @dither to set the degree of Floyd-Steinberg dithering. + * + * See also: vips_palette(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_dither( VipsImage *in, VipsImage **out, VipsImage *palette, ... ) +{ + va_list ap; + int result; + + va_start( ap, palette ); + result = vips_call_split( "dither", ap, in, out, palette ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/conversion/meson.build b/libvips/conversion/meson.build index 361b0711e9..cac8cc534c 100644 --- a/libvips/conversion/meson.build +++ b/libvips/conversion/meson.build @@ -1,44 +1,46 @@ conversion_sources = files( - 'switch.c', - 'transpose3d.c', - 'composite.cpp', - 'smartcrop.c', - 'conversion.c', - 'tilecache.c', - 'gamma.c', - 'sequential.c', - 'flatten.c', - 'premultiply.c', - 'unpremultiply.c', + 'arrayjoin.c', + 'autorot.c', + 'bandary.c', + 'bandbool.c', + 'bandfold.c', + 'bandjoin.c', + 'bandmean.c', + 'bandrank.c', + 'bandunfold.c', 'byteswap.c', 'cache.c', + 'cast.c', + 'composite.cpp', + 'conversion.c', 'copy.c', + 'dither.c', 'embed.c', + 'extract.c', + 'falsecolour.c', + 'flatten.c', 'flip.c', + 'gamma.c', + 'grid.c', + 'ifthenelse.c', 'insert.c', 'join.c', - 'arrayjoin.c', - 'extract.c', - 'replicate.c', - 'cast.c', - 'bandjoin.c', - 'bandrank.c', + 'msb.c', + 'palette.c', + 'premultiply.c', 'recomb.c', - 'bandmean.c', - 'bandfold.c', - 'bandunfold.c', - 'bandbool.c', - 'bandary.c', - 'rot.c', + 'replicate.c', 'rot45.c', - 'autorot.c', - 'ifthenelse.c', - 'falsecolour.c', - 'msb.c', - 'grid.c', + 'rot.c', 'scale.c', - 'wrap.c', + 'sequential.c', + 'smartcrop.c', 'subsample.c', + 'switch.c', + 'tilecache.c', + 'transpose3d.c', + 'unpremultiply.c', + 'wrap.c', 'zoom.c', ) diff --git a/libvips/conversion/palette.c b/libvips/conversion/palette.c new file mode 100644 index 0000000000..6cd017d8a0 --- /dev/null +++ b/libvips/conversion/palette.c @@ -0,0 +1,241 @@ +/* find a palette + * + * 25/9/22 + * - from cgifsave.c + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include + +#include "pconversion.h" +#include "../foreign/quantise.h" + +typedef struct _VipsPalette { + VipsConversion parent_instance; + + VipsImage *in; + int effort; + int bitdepth; +} VipsPalette; + +typedef VipsConversionClass VipsPaletteClass; + +G_DEFINE_TYPE( VipsPalette, vips_palette, VIPS_TYPE_CONVERSION ); + +static int +vips_palette_write( VipsPalette *palette, VipsImage *out, + const VipsQuantisePalette *lp ) +{ + VipsPel line[256 * 4]; + VipsPel *rgba; + + g_assert( lp->count <= 256 ); + + rgba = line; + for( int i = 0; i < lp->count; i++ ) { + rgba[0] = lp->entries[i].r; + rgba[1] = lp->entries[i].g; + rgba[2] = lp->entries[i].b; + rgba[3] = lp->entries[i].a; + + rgba += 4; + } + + vips_image_init_fields( out, + lp->count, 1, 4, VIPS_FORMAT_UCHAR, + VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, + 1.0, 1.0 ); + + if( vips_image_write_line( out, 0, line ) ) + return( -1 ); + + return( 0 ); +} + +static int +vips_palette_build( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsConversion *conversion = VIPS_CONVERSION( object ); + VipsPalette *palette = (VipsPalette *) object; + VipsImage **t = (VipsImage **) vips_object_local_array( object, 5 ); + + VipsImage *in; + VipsQuantiseResult *result; + VipsQuantiseAttr *attr; + VipsQuantiseImage *image; + + if( VIPS_OBJECT_CLASS( vips_palette_parent_class )-> + build( object ) ) + return( -1 ); + + /* We only work for 8-bit RGBA images. + */ + in = palette->in; + if( vips_check_uncoded( class->nickname, in ) || + vips_check_format( class->nickname, in, VIPS_FORMAT_UCHAR ) || + vips_check_bands_atleast( class->nickname, in, 3 ) ) + return( -1 ); + + /* To RGBA. + */ + if( in->Bands == 3 ) { + if( vips_addalpha( in, &t[0], NULL ) ) + return( -1 ); + in = t[0]; + } + else if( in->Bands > 4 ) { + if( vips_extract_band( in, &t[0], 0, "n", 4, NULL ) ) + return( -1 ); + in = t[0]; + } + + /* We need the whole thing in memory. + */ + if( vips_image_wio_input( in ) ) + return( -1 ); + + attr = vips__quantise_attr_create(); + vips__quantise_set_max_colors( attr, + VIPS_MIN( 255, 1 << palette->bitdepth ) ); + vips__quantise_set_quality( attr, 0, 100 ); + vips__quantise_set_speed( attr, 11 - palette->effort ); + + image = vips__quantise_image_create_rgba( attr, + VIPS_IMAGE_ADDR( in, 0, 0 ), in->Xsize, in->Ysize, 0.0 ); + + result = NULL; + if( vips__quantise_image_quantize_fixed( image, attr, &result ) ) { + VIPS_FREEF( vips__quantise_image_destroy, image ); + VIPS_FREEF( vips__quantise_attr_destroy, attr ); + vips_error( class->nickname, "%s", _( "quantisation failed" ) ); + return( -1 ); + } + + if( vips_palette_write( palette, conversion->out, + vips__quantise_get_palette( result ) ) ) { + VIPS_FREEF( vips__quantise_result_destroy, result ); + return( -1 ); + } + + VIPS_FREEF( vips__quantise_image_destroy, image ); + VIPS_FREEF( vips__quantise_attr_destroy, attr ); + + VIPS_FREEF( vips__quantise_result_destroy, result ); + + return( 0 ); +} + +static void +vips_palette_class_init( VipsPaletteClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + vobject_class->nickname = "palette"; + vobject_class->description = _( "compute image palette" ); + vobject_class->build = vips_palette_build; + + operation_class->flags = VIPS_OPERATION_SEQUENTIAL; + + VIPS_ARG_IMAGE( class, "in", 0, + _( "Input" ), + _( "Input image" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsPalette, in ) ); + + VIPS_ARG_INT( class, "effort", 11, + _( "Effort" ), + _( "Quantisation effort" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsPalette, effort ), + 1, 10, 7 ); + + VIPS_ARG_INT( class, "bitdepth", 1, + _( "Bit depth" ), + _( "Compute an N bit palette" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsPalette, bitdepth ), + 0, 16, 8 ); + +} + +static void +vips_palette_init( VipsPalette *palette ) +{ +} + +/** + * vips_palette: (method) + * @in: input image + * @out: (out): output image + * @...: %NULL-terminated list of optional named arguments + * + * Optional arguments: + * + * * @effort: %gint, how much effort to spend on the search + * * @bitdepth: %gint, bitdepth for generated palette + * + * Find the most common colours in an 8-bit RGB or RGBA image. + * + * Set @bitdepth to control the size of the computed palette. By default it + * finds an 8-bit palette, or 256 colours. + * + * Set @effort to control the CPU effort (1 is the fastest, + * 10 is the slowest, 7 is the default). + * + * See also: vips_dither(), vips_maplut(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_palette( VipsImage *in, VipsImage **out, ... ) +{ + va_list ap; + int result; + + va_start( ap, out ); + result = vips_call_split( "palette", ap, in, out ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/foreign/cgifsave.c b/libvips/foreign/cgifsave.c index 1b7fbd838a..b03890d797 100644 --- a/libvips/foreign/cgifsave.c +++ b/libvips/foreign/cgifsave.c @@ -484,8 +484,10 @@ vips_foreign_save_cgif_write_frame( VipsForeignSaveCgif *cgif ) /* Local mode. Pick the global, this or previous palette. */ if( vips_foreign_save_cgif_pick_quantiser( cgif, - image, &quantisation_result, &use_local ) ) + image, &quantisation_result, &use_local ) ) { + VIPS_FREEF( vips__quantise_image_destroy, image ); return( -1 ); + } } lp = vips__quantise_get_palette( quantisation_result ); diff --git a/libvips/include/vips/conversion.h b/libvips/include/vips/conversion.h index b3dd66aae6..db67572470 100644 --- a/libvips/include/vips/conversion.h +++ b/libvips/include/vips/conversion.h @@ -336,6 +336,8 @@ VIPS_API int vips_unpremultiply( VipsImage *in, VipsImage **out, ... ) G_GNUC_NULL_TERMINATED; VIPS_API +int vips_palette( VipsImage *in, VipsImage **out, ... ); +VIPS_API int vips_composite( VipsImage **in, VipsImage **out, int n, int *mode, ... ) G_GNUC_NULL_TERMINATED; VIPS_API