Add palette flag to trigger pngsave quantisation

and tweak docstrings.
This commit is contained in:
Felix Bünemann 2018-06-19 22:41:40 +02:00
parent 1e68b3a486
commit 1abc5f901a
4 changed files with 54 additions and 43 deletions

View File

@ -197,11 +197,12 @@ int vips__png_header_buffer( const void *buffer, size_t length, VipsImage *out )
int vips__png_write( VipsImage *in, const char *filename, int vips__png_write( VipsImage *in, const char *filename,
int compress, int interlace, const char *profile, int compress, int interlace, const char *profile,
VipsForeignPngFilter filter, gboolean strip, int colours, int Q, double dither ); VipsForeignPngFilter filter, gboolean strip,
gboolean palette, int colours, int Q, double dither );
int vips__png_write_buf( VipsImage *in, int vips__png_write_buf( VipsImage *in,
void **obuf, size_t *olen, int compression, int interlace, void **obuf, size_t *olen, int compression, int interlace,
const char *profile, VipsForeignPngFilter filter, gboolean strip, const char *profile, VipsForeignPngFilter filter, gboolean strip,
int colours, int Q, double dither ); gboolean palette, int colours, int Q, double dither );
/* Map WEBP metadata names to vips names. /* Map WEBP metadata names to vips names.
*/ */

View File

@ -60,6 +60,7 @@ typedef struct _VipsForeignSavePng {
gboolean interlace; gboolean interlace;
char *profile; char *profile;
VipsForeignPngFilter filter; VipsForeignPngFilter filter;
gboolean palette;
int colours; int colours;
int Q; int Q;
double dither; double dither;
@ -136,21 +137,28 @@ vips_foreign_save_png_class_init( VipsForeignSavePngClass *class )
VIPS_TYPE_FOREIGN_PNG_FILTER, VIPS_TYPE_FOREIGN_PNG_FILTER,
VIPS_FOREIGN_PNG_FILTER_ALL ); VIPS_FOREIGN_PNG_FILTER_ALL );
VIPS_ARG_INT( class, "colours", 13, VIPS_ARG_BOOL( class, "palette", 13,
_( "Palette" ),
_( "Quantise to 8bpp palette" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignSavePng, palette ),
FALSE );
VIPS_ARG_INT( class, "colours", 14,
_( "Colours" ), _( "Colours" ),
_( "Max number of palette colours" ), _( "Max number of palette colours" ),
VIPS_ARGUMENT_OPTIONAL_INPUT, VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignSavePng, colours ), G_STRUCT_OFFSET( VipsForeignSavePng, colours ),
2, 256, 256 ); 2, 256, 256 );
VIPS_ARG_INT( class, "Q", 14, VIPS_ARG_INT( class, "Q", 15,
_( "Quality" ), _( "Quality" ),
_( "Quantisation quality" ), _( "Quantisation quality" ),
VIPS_ARGUMENT_OPTIONAL_INPUT, VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignSavePng, Q ), G_STRUCT_OFFSET( VipsForeignSavePng, Q ),
0, 100, 100 ); 0, 100, 100 );
VIPS_ARG_DOUBLE( class, "dither", 15, VIPS_ARG_DOUBLE( class, "dither", 16,
_( "Dithering" ), _( "Dithering" ),
_( "Amount of dithering" ), _( "Amount of dithering" ),
VIPS_ARGUMENT_OPTIONAL_INPUT, VIPS_ARGUMENT_OPTIONAL_INPUT,
@ -164,7 +172,7 @@ vips_foreign_save_png_init( VipsForeignSavePng *png )
{ {
png->compression = 6; png->compression = 6;
png->filter = VIPS_FOREIGN_PNG_FILTER_ALL; png->filter = VIPS_FOREIGN_PNG_FILTER_ALL;
png->colours = 0; png->colours = 256;
png->Q = 100; png->Q = 100;
png->dither = 1.0; png->dither = 1.0;
} }
@ -193,8 +201,8 @@ vips_foreign_save_png_file_build( VipsObject *object )
if( vips__png_write( save->ready, if( vips__png_write( save->ready,
png_file->filename, png->compression, png->interlace, png_file->filename, png->compression, png->interlace,
png->profile, png->filter, save->strip, png->colours, png->Q, png->profile, png->filter, save->strip, png->palette,
png->dither ) ) png->colours, png->Q, png->dither ) )
return( -1 ); return( -1 );
return( 0 ); return( 0 );
@ -253,7 +261,7 @@ vips_foreign_save_png_buffer_build( VipsObject *object )
if( vips__png_write_buf( save->ready, &obuf, &olen, if( vips__png_write_buf( save->ready, &obuf, &olen,
png->compression, png->interlace, png->profile, png->filter, png->compression, png->interlace, png->profile, png->filter,
save->strip, png->colours, png->Q, png->dither ) ) save->strip, png->palette, png->colours, png->Q, png->dither ) )
return( -1 ); return( -1 );
/* vips__png_write_buf() makes a buffer that needs g_free(), not /* vips__png_write_buf() makes a buffer that needs g_free(), not
@ -306,7 +314,8 @@ vips_foreign_save_png_buffer_init( VipsForeignSavePngBuffer *buffer )
* * @interlace: interlace image * * @interlace: interlace image
* * @profile: ICC profile to embed * * @profile: ICC profile to embed
* * @filter: #VipsForeignPngFilter row filter flag(s) * * @filter: #VipsForeignPngFilter row filter flag(s)
* * @colours: enable 8bpp quantisation with max n colours * * @palette: enable quantisation to 8bpp palette
* * @colours: max number of palette colours for quantisation
* * @Q: quality for 8bpp quantisation (does not exceed @colours) * * @Q: quality for 8bpp quantisation (does not exceed @colours)
* * @dither: amount of dithering for 8bpp quantization * * @dither: amount of dithering for 8bpp quantization
* *
@ -335,11 +344,11 @@ vips_foreign_save_png_buffer_init( VipsForeignSavePngBuffer *buffer )
* alpha before saving. Images with more than one byte per band element are * alpha before saving. Images with more than one byte per band element are
* saved as 16-bit PNG, others are saved as 8-bit PNG. * saved as 16-bit PNG, others are saved as 8-bit PNG.
* *
* If @colours is given, it limits the maximum number of colours in the image * Set @palette to %TRUE to enable quantisation to an 8-bit per pixel palette
* and the source image will be quantised down to an 8-Bit one band indexed * image with alpha transparency support. If @colours is given, it limits the
* image with palette based alpha transparency. Similar to JPEG the quality * maximum number of palette entries. Similar to JPEG the quality can also be
* can be controlled with the @Q parameter and the amount of Floyd-Steinberg * be changed with the @Q parameter which further reduces the palette size and
* dithering is set with @dither. * @dither controls the amount of Floyd-Steinberg dithering.
* This feature requires libvips to be compiled with libimagequant. * This feature requires libvips to be compiled with libimagequant.
* *
* See also: vips_image_new_from_file(). * See also: vips_image_new_from_file().

View File

@ -896,7 +896,7 @@ quantise_image( VipsImage *in, VipsImage *out, VipsImage *palette_out,
VIPS_UNREF( srgb ); VIPS_UNREF( srgb );
} }
/* Add alpha channel if missing. */ /* Add alpha channel if missing. */
if( in->Bands == 3 ) { if( !vips_image_hasalpha(in) ) {
VipsImage *srgba; VipsImage *srgba;
if( vips_bandjoin_const1( in, &srgba, 255, NULL ) ) if( vips_bandjoin_const1( in, &srgba, 255, NULL ) )
return( -1 ); return( -1 );
@ -976,15 +976,15 @@ quantise_image( VipsImage *in, VipsImage *out, VipsImage *palette_out,
return( 0 ); return( 0 );
} }
#endif #endif /*HAVE_IMAGEQUANT*/
/* Write a VIPS image to PNG. /* Write a VIPS image to PNG.
*/ */
static int static int
write_vips( Write *write, write_vips( Write *write,
int compress, int interlace, const char *profile, int compress, int interlace, const char *profile,
VipsForeignPngFilter filter, gboolean strip, int colours, int Q, VipsForeignPngFilter filter, gboolean strip,
double dither ) gboolean palette, int colours, int Q, double dither )
{ {
VipsImage *in = write->in; VipsImage *in = write->in;
@ -1047,15 +1047,16 @@ write_vips( Write *write,
#ifdef HAVE_IMAGEQUANT #ifdef HAVE_IMAGEQUANT
/* Enable image quantisation to paletted 8bpp PNG if colours is set. /* Enable image quantisation to paletted 8bpp PNG if colours is set.
*/ */
if( colours ) { if( palette ) {
g_assert( colours >= 2 && colours <= 256 ); g_assert( colours >= 2 && colours <= 256 );
bit_depth = 8; bit_depth = 8;
color_type = PNG_COLOR_TYPE_PALETTE; color_type = PNG_COLOR_TYPE_PALETTE;
} }
#else #else
if( colours ) if( palette )
g_warning( "%s", _( "ignoring colours" ) ); g_warning( "%s",
#endif _( "ignoring palette (no quantisation support)" ) );
#endif /*HAVE_IMAGEQUANT*/
interlace_type = interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE; interlace_type = interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE;
@ -1110,19 +1111,19 @@ write_vips( Write *write,
} }
#ifdef HAVE_IMAGEQUANT #ifdef HAVE_IMAGEQUANT
if( colours ) { if( palette ) {
VipsImage *quantised = vips_image_new_memory(); VipsImage *im_quantised = vips_image_new_memory();
VipsImage *palette = vips_image_new_memory(); VipsImage *im_palette = vips_image_new_memory();
if( quantise_image( in, quantised, palette, colours, Q, if( quantise_image( in, im_quantised, im_palette, colours, Q,
dither ) ) { dither ) ) {
vips_error( "vips2png", vips_error( "vips2png",
"%s", _( "quantisation failed" ) ); "%s", _( "quantisation failed" ) );
VIPS_UNREF( quantised ); VIPS_UNREF( im_quantised );
VIPS_UNREF( palette ); VIPS_UNREF( im_palette );
return( -1 ); return( -1 );
} }
int palette_count = palette->Xsize; int palette_count = im_palette->Xsize;
g_assert( palette_count <= PNG_MAX_PALETTE_LENGTH); g_assert( palette_count <= PNG_MAX_PALETTE_LENGTH);
png_color *png_palette = (png_color *) png_malloc( write->pPng, png_color *png_palette = (png_color *) png_malloc( write->pPng,
@ -1132,8 +1133,8 @@ write_vips( Write *write,
int trans_count = 0; int trans_count = 0;
for( i = 0; i < palette_count; i++ ) { for( i = 0; i < palette_count; i++ ) {
png_byte *p = (png_byte *) VIPS_IMAGE_ADDR( palette, i, png_byte *p = (png_byte *) VIPS_IMAGE_ADDR( im_palette,
0 ); i, 0 );
png_color *col = &png_palette[i]; png_color *col = &png_palette[i];
col->red = p[0]; col->red = p[0];
col->green = p[1]; col->green = p[1];
@ -1161,13 +1162,13 @@ write_vips( Write *write,
png_set_tRNS( write->pPng, write->pInfo, png_trans, png_set_tRNS( write->pPng, write->pInfo, png_trans,
trans_count, NULL ); trans_count, NULL );
} }
VIPS_UNREF( palette ); VIPS_UNREF( im_palette );
VIPS_UNREF( write->memory ); VIPS_UNREF( write->memory );
write->memory = quantised; write->memory = im_quantised;
in = write->memory; in = write->memory;
} }
#endif #endif /*HAVE_IMAGEQUANT*/
png_write_info( write->pPng, write->pInfo ); png_write_info( write->pPng, write->pInfo );
/* If we're an intel byte order CPU and this is a 16bit image, we need /* If we're an intel byte order CPU and this is a 16bit image, we need
@ -1202,7 +1203,7 @@ int
vips__png_write( VipsImage *in, const char *filename, vips__png_write( VipsImage *in, const char *filename,
int compress, int interlace, const char *profile, int compress, int interlace, const char *profile,
VipsForeignPngFilter filter, gboolean strip, VipsForeignPngFilter filter, gboolean strip,
int colours, int Q, double dither ) gboolean palette, int colours, int Q, double dither )
{ {
Write *write; Write *write;
@ -1222,8 +1223,8 @@ vips__png_write( VipsImage *in, const char *filename,
/* Convert it! /* Convert it!
*/ */
if( write_vips( write, if( write_vips( write,
compress, interlace, profile, filter, strip, colours, Q, compress, interlace, profile, filter, strip, palette,
dither ) ) { colours, Q, dither ) ) {
vips_error( "vips2png", vips_error( "vips2png",
_( "unable to write \"%s\"" ), filename ); _( "unable to write \"%s\"" ), filename );
@ -1251,7 +1252,7 @@ int
vips__png_write_buf( VipsImage *in, vips__png_write_buf( VipsImage *in,
void **obuf, size_t *olen, int compression, int interlace, void **obuf, size_t *olen, int compression, int interlace,
const char *profile, VipsForeignPngFilter filter, gboolean strip, const char *profile, VipsForeignPngFilter filter, gboolean strip,
int colours, int Q, double dither ) gboolean palette, int colours, int Q, double dither )
{ {
Write *write; Write *write;
@ -1263,11 +1264,11 @@ vips__png_write_buf( VipsImage *in,
/* Convert it! /* Convert it!
*/ */
if( write_vips( write, if( write_vips( write,
compression, interlace, profile, filter, strip, colours, Q, compression, interlace, profile, filter, strip, palette,
dither ) ) { colours, Q, dither ) ) {
vips_error( "vips2png", vips_error( "vips2png",
"%s", _( "unable to write to buffer" ) ); "%s", _( "unable to write to buffer" ) );
return( -1 ); return( -1 );
} }

View File

@ -197,7 +197,7 @@ fi
if test_supported pngload; then if test_supported pngload; then
test_format $image png 0 test_format $image png 0
test_format $image png 0 [compression=9,interlace=1] test_format $image png 0 [compression=9,interlace=1]
test_format $image png 90 [colours=256,Q=100,dither=0,compression=9,interlace=1] test_format $image png 90 [palette,colours=256,Q=100,dither=0,interlace=1]
fi fi
if test_supported jpegload; then if test_supported jpegload; then
test_format $image jpg 90 test_format $image jpg 90