From cb76ffb0bda60cc94543885d6639390a101b84c5 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 20 Jun 2018 15:38:33 +0100 Subject: [PATCH] break quantise out to a separate file slightly simpler reference and memory handling too --- libvips/foreign/Makefile.am | 1 + libvips/foreign/pforeign.h | 4 + libvips/foreign/quantise.c | 224 ++++++++++++++++++++++++++++++++++++ libvips/foreign/vipspng.c | 135 ++-------------------- 4 files changed, 238 insertions(+), 126 deletions(-) create mode 100644 libvips/foreign/quantise.c diff --git a/libvips/foreign/Makefile.am b/libvips/foreign/Makefile.am index 2f17c1aa..58a33053 100644 --- a/libvips/foreign/Makefile.am +++ b/libvips/foreign/Makefile.am @@ -2,6 +2,7 @@ noinst_LTLIBRARIES = libforeign.la libforeign_la_SOURCES = \ pforeign.h \ + quantise.c \ exif.c \ gifload.c \ cairo.c \ diff --git a/libvips/foreign/pforeign.h b/libvips/foreign/pforeign.h index 50e632e7..c18c634e 100644 --- a/libvips/foreign/pforeign.h +++ b/libvips/foreign/pforeign.h @@ -252,6 +252,10 @@ int vips__openslide_read_associated( const char *filename, VipsImage *out, gboolean vips_foreign_load_pdf_is_a_buffer( const void *buf, size_t len ); gboolean vips_foreign_load_pdf_is_a( const char *filename ); +int vips__quantise_image( VipsImage *in, + VipsImage **index_out, VipsImage **palette_out, + int colours, int Q, double dither ); + #ifdef __cplusplus } #endif /*__cplusplus*/ diff --git a/libvips/foreign/quantise.c b/libvips/foreign/quantise.c new file mode 100644 index 00000000..75ddf094 --- /dev/null +++ b/libvips/foreign/quantise.c @@ -0,0 +1,224 @@ +/* quantise an image with libimagequant + * + * 20/6/18 + * - from vipspng.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 + + */ + +/* +#define DEBUG +#define VIPS_DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#ifdef HAVE_IMAGEQUANT + +#include + +#include "pforeign.h" + +#include + +/* Track during a quantisation. + */ +typedef struct _Quantise { + VipsImage *in; + VipsImage **index_out; + VipsImage **palette_out; + int colours; + int Q; + double dither; + + liq_attr *attr; + liq_image *input_image; + liq_result *quantisation_result; + VipsImage *t[5]; +} Quantise; + +static void +vips__quantise_free( Quantise *quantise ) +{ + int i; + + VIPS_FREEF( liq_result_destroy, quantise->quantisation_result ); + VIPS_FREEF( liq_image_destroy, quantise->input_image ); + VIPS_FREEF( liq_attr_destroy, quantise->attr ); + + for( i = 0; i < VIPS_NUMBER( quantise->t ); i++ ) + VIPS_UNREF( quantise->t[i] ); + + VIPS_FREE( quantise ); +} + +static Quantise * +vips__quantise_new( VipsImage *in, + VipsImage **index_out, VipsImage **palette_out, + int colours, int Q, double dither ) +{ + Quantise *quantise; + int i; + + quantise = VIPS_NEW( NULL, Quantise ); + quantise->in = in; + quantise->index_out = index_out; + quantise->palette_out = palette_out; + quantise->colours = colours; + quantise->Q = Q; + quantise->dither = dither; + for( i = 0; i < VIPS_NUMBER( quantise->t ); i++ ) + quantise->t[i] = NULL; + + return( quantise ); +} + +int +vips__quantise_image( VipsImage *in, + VipsImage **index_out, VipsImage **palette_out, + int colours, int Q, double dither ) +{ + Quantise *quantise; + VipsImage *index; + VipsImage *palette; + const liq_palette *lp; + int i; + + quantise = vips__quantise_new( in, index_out, palette_out, + colours, Q, dither ); + + /* Ensure input is sRGB. + */ + if( in->Type != VIPS_INTERPRETATION_sRGB ) { + if( vips_colourspace( in, &quantise->t[0], + VIPS_INTERPRETATION_sRGB, NULL ) ) { + vips__quantise_free( quantise ); + return( -1 ); + } + in = quantise->t[0]; + } + + /* Add alpha channel if missing. + */ + if( !vips_image_hasalpha( in ) ) { + if( vips_bandjoin_const1( in, &quantise->t[1], 255, NULL ) ) { + vips__quantise_free( quantise ); + return( -1 ); + } + in = quantise->t[1]; + } + + if( !(quantise->t[2] = vips_image_copy_memory( in )) ) { + vips__quantise_free( quantise ); + return( -1 ); + } + in = quantise->t[2]; + + quantise->attr = liq_attr_create(); + liq_set_max_colors( quantise->attr, colours ); + liq_set_quality( quantise->attr, 0, Q ); + + quantise->input_image = liq_image_create_rgba( quantise->attr, + VIPS_IMAGE_ADDR( in, 0, 0 ), in->Xsize, in->Ysize, 0 ); + + if( liq_image_quantize( quantise->input_image, quantise->attr, + &quantise->quantisation_result ) ) { + vips_error( "vips2png", "%s", _( "quantisation failed" ) ); + vips__quantise_free( quantise ); + return( -1 ); + } + + liq_set_dithering_level( quantise->quantisation_result, dither ); + + index = quantise->t[3] = vips_image_new_memory(); + vips_image_init_fields( index, + in->Xsize, in->Ysize, 1, VIPS_FORMAT_UCHAR, + VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W, 1.0, 1.0 ); + + if( vips_image_write_prepare( index ) ) { + vips__quantise_free( quantise ); + return( -1 ); + } + + if( liq_write_remapped_image( quantise->quantisation_result, + quantise->input_image, + VIPS_IMAGE_ADDR( index, 0, 0 ), VIPS_IMAGE_N_PELS( index ) ) ) { + vips_error( "vips2png", "%s", _( "quantisation failed" ) ); + vips__quantise_free( quantise ); + return( -1 ); + } + + lp = liq_get_palette( quantise->quantisation_result ); + + palette = quantise->t[4] = vips_image_new_memory(); + vips_image_init_fields( palette, lp->count, 1, 4, + VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, + 1.0, 1.0 ); + + if( vips_image_write_prepare( palette ) ) { + vips__quantise_free( quantise ); + return( -1 ); + } + + for( i = 0; i < lp->count; i++ ) { + unsigned char *p = VIPS_IMAGE_ADDR( palette, i, 0 ); + + p[0] = lp->entries[i].r; + p[1] = lp->entries[i].g; + p[2] = lp->entries[i].b; + p[3] = lp->entries[i].a; + } + + *index_out = index; + g_object_ref( index ); + *palette_out = palette; + g_object_ref( palette ); + + vips__quantise_free( quantise ); + + return( 0 ); +} + +#else /*!HAVE_IMAGEQUANT*/ + +int +vips__quantise_image( VipsImage *in, + VipsImage **index_out, VipsImage **palette_out, + int colours, int Q, double dither ) +{ + vips_error( "vips__quantise_image", + "%s", _( "libvips not built with quantisation support" ) ); + + return( -1 ); +} + +#endif /*HAVE_IMAGEQUANT*/ + diff --git a/libvips/foreign/vipspng.c b/libvips/foreign/vipspng.c index 035118a9..bf55dc15 100644 --- a/libvips/foreign/vipspng.c +++ b/libvips/foreign/vipspng.c @@ -126,10 +126,6 @@ #error "PNG library too old." #endif -#ifdef HAVE_IMAGEQUANT -#include -#endif - static void user_error_function( png_structp png_ptr, png_const_charp error_msg ) { @@ -883,113 +879,6 @@ write_png_block( VipsRegion *region, VipsRect *area, void *a ) return( 0 ); } -#ifdef HAVE_IMAGEQUANT -static int -quantise_image( VipsImage *in, VipsImage *out, VipsImage *palette_out, - int colours, int Q, double dither ) -{ - VipsImage *memory; - liq_attr *attr; - liq_image *input_image; - liq_result *quantisation_result; - int i; - - /* Ensure input is sRGB. - */ - if( in->Type != VIPS_INTERPRETATION_sRGB) { - VipsImage *srgb; - - if( vips_colourspace( in, &srgb, VIPS_INTERPRETATION_sRGB, - NULL ) ) - return( -1 ); - in = srgb; - VIPS_UNREF( srgb ); - } - - /* Add alpha channel if missing. - */ - if( !vips_image_hasalpha( in ) ) { - VipsImage *srgba; - - if( vips_bandjoin_const1( in, &srgba, 255, NULL ) ) - return( -1 ); - in = srgba; - VIPS_UNREF( srgba ); - } - - if( !(memory = vips_image_copy_memory( in )) ) - return( -1 ); - in = memory; - - attr = liq_attr_create(); - liq_set_max_colors( attr, colours ); - liq_set_quality( attr, 0, Q ); - - input_image = liq_image_create_rgba( attr, - VIPS_IMAGE_ADDR( in, 0, 0 ), in->Xsize, in->Ysize, 0 ); - - if ( liq_image_quantize( input_image, attr, &quantisation_result ) ) { - liq_result_destroy( quantisation_result ); - liq_image_destroy( input_image ); - liq_attr_destroy( attr ); - VIPS_UNREF( memory ); - return( -1 ); - } - - liq_set_dithering_level( quantisation_result, (float) dither ); - - vips_image_init_fields( 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( out ) ) { - liq_result_destroy( quantisation_result ); - liq_image_destroy( input_image ); - liq_attr_destroy( attr ); - VIPS_UNREF( memory ); - return( -1 ); - } - - if( liq_write_remapped_image( quantisation_result, input_image, - VIPS_IMAGE_ADDR( out, 0, 0 ), VIPS_IMAGE_N_PELS( out ) ) ) { - liq_result_destroy( quantisation_result ); - liq_image_destroy( input_image ); - liq_attr_destroy( attr ); - VIPS_UNREF( memory ); - return( -1 ); - } - - const liq_palette *palette = liq_get_palette( quantisation_result ); - - vips_image_init_fields( palette_out, palette->count, 1, 4, - VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, - 1.0, 1.0 ); - - if( vips_image_write_prepare( palette_out ) ) { - liq_result_destroy( quantisation_result ); - liq_image_destroy( input_image ); - liq_attr_destroy( attr ); - VIPS_UNREF( memory ); - return( -1 ); - } - - for( i = 0; i < palette->count; i++ ) { - unsigned char *p = VIPS_IMAGE_ADDR( palette_out, i, 0 ); - - p[0] = palette->entries[i].r; - p[1] = palette->entries[i].g; - p[2] = palette->entries[i].b; - p[3] = palette->entries[i].a; - } - - liq_result_destroy( quantisation_result ); - liq_image_destroy( input_image ); - liq_attr_destroy( attr ); - VIPS_UNREF( memory ); - - return( 0 ); -} -#endif /*HAVE_IMAGEQUANT*/ - /* Write a VIPS image to PNG. */ static int @@ -1125,23 +1014,16 @@ write_vips( Write *write, #ifdef HAVE_IMAGEQUANT if( palette ) { - VipsImage *im_quantised; + VipsImage *im_index; VipsImage *im_palette; int palette_count; png_color *png_palette; png_byte *png_trans; int trans_count; - im_quantised = vips_image_new_memory(); - im_palette = vips_image_new_memory(); - if( quantise_image( in, im_quantised, im_palette, colours, Q, - dither ) ) { - vips_error( "vips2png", - "%s", _( "quantisation failed" ) ); - VIPS_UNREF( im_quantised ); - VIPS_UNREF( im_palette ); + if( vips__quantise_image( in, &im_index, &im_palette, + colours, Q, dither ) ) return( -1 ); - } palette_count = im_palette->Xsize; @@ -1153,8 +1035,8 @@ write_vips( Write *write, palette_count * sizeof( png_byte ) ); trans_count = 0; for( i = 0; i < palette_count; i++ ) { - png_byte *p = (png_byte *) VIPS_IMAGE_ADDR( im_palette, - i, 0 ); + VipsPel *p = (VipsPel *) + VIPS_IMAGE_ADDR( im_palette, i, 0 ); png_color *col = &png_palette[i]; col->red = p[0]; @@ -1186,10 +1068,11 @@ write_vips( Write *write, png_free( write->pPng, (void *) png_palette ); png_free( write->pPng, (void *) png_trans ); - VIPS_UNREF( im_palette ); - VIPS_UNREF( write->memory ); - write->memory = im_quantised; + VIPS_UNREF( im_palette ); + + VIPS_UNREF( write->memory ); + write->memory = im_index; in = write->memory; } #endif /*HAVE_IMAGEQUANT*/