2011-12-19 22:40:08 +01:00
|
|
|
/* Load/save png image with libpng
|
|
|
|
*
|
|
|
|
* 28/11/03 JC
|
|
|
|
* - better no-overshoot on tile loop
|
|
|
|
* 22/2/05
|
|
|
|
* - read non-interlaced PNG with a line buffer (thanks Michel Brabants)
|
|
|
|
* 11/1/06
|
|
|
|
* - read RGBA palette-ized images more robustly (thanks Tom)
|
|
|
|
* 20/4/06
|
|
|
|
* - auto convert to sRGB/mono (with optional alpha) for save
|
|
|
|
* 1/5/06
|
|
|
|
* - from vips_png.c
|
|
|
|
* 8/5/06
|
|
|
|
* - set RGB16/GREY16 if appropriate
|
|
|
|
* 2/11/07
|
|
|
|
* - use im_wbuffer() API for BG writes
|
|
|
|
* 28/2/09
|
|
|
|
* - small cleanups
|
|
|
|
* 4/2/10
|
|
|
|
* - gtkdoc
|
|
|
|
* - fixed 16-bit save
|
|
|
|
* 12/5/10
|
|
|
|
* - lololo but broke 8-bit save, fixed again
|
|
|
|
* 20/7/10 Tim Elliott
|
|
|
|
* - added im_vips2bufpng()
|
|
|
|
* 8/1/11
|
|
|
|
* - get set png resolution (thanks Zhiyu Wu)
|
|
|
|
* 17/3/11
|
|
|
|
* - update for libpng-1.5 API changes
|
|
|
|
* - better handling of palette and 1-bit images
|
|
|
|
* - ... but we are now png 1.2.9 and later only :-( argh
|
|
|
|
* 28/3/11
|
|
|
|
* - argh gamma was wrong when viewed in firefox
|
|
|
|
* 19/12/11
|
|
|
|
* - rework as a set of fns ready for wrapping as a class
|
2012-02-07 15:30:54 +01:00
|
|
|
* 7/2/12
|
|
|
|
* - mild refactoring
|
|
|
|
* - add support for sequential reads
|
2012-02-23 13:42:21 +01:00
|
|
|
* 23/2/12
|
|
|
|
* - add a longjmp() to our error handler to stop the default one running
|
2012-03-13 15:22:13 +01:00
|
|
|
* 13/3/12
|
|
|
|
* - add ICC profile read/write
|
2012-03-15 13:53:38 +01:00
|
|
|
* 15/3/12
|
|
|
|
* - better alpha handling
|
|
|
|
* - sanity check pixel geometry before allowing read
|
2012-06-17 23:16:20 +02:00
|
|
|
* 17/6/12
|
|
|
|
* - more alpha fixes ... some images have no transparency chunk but
|
|
|
|
* still set color_type to alpha
|
2013-07-16 13:20:50 +02:00
|
|
|
* 16/7/13
|
|
|
|
* - more robust error handling from libpng
|
2014-08-09 18:14:49 +02:00
|
|
|
* 9/8/14
|
|
|
|
* - don't check profiles, helps with libpng >=1.6.11
|
2014-10-27 12:40:43 +01:00
|
|
|
* 27/10/14 Lovell
|
|
|
|
* - add @filter option
|
2015-02-26 15:09:01 +01:00
|
|
|
* 26/2/15
|
|
|
|
* - close the read down early for a header read ... this saves an
|
|
|
|
* fd during file read, handy for large numbers of input images
|
2016-07-31 11:34:12 +02:00
|
|
|
* 31/7/16
|
|
|
|
* - support --strip option
|
2017-01-17 15:53:40 +01:00
|
|
|
* 17/1/17
|
|
|
|
* - invalidate operation on read error
|
2017-02-27 23:06:22 +01:00
|
|
|
* 27/2/17
|
|
|
|
* - use dbuf for buffer output
|
2017-03-30 18:13:25 +02:00
|
|
|
* 30/3/17
|
|
|
|
* - better behaviour for truncated png files, thanks Yury
|
2017-04-26 11:25:52 +02:00
|
|
|
* 26/4/17
|
|
|
|
* - better @fail handling with truncated PNGs
|
2018-04-09 18:14:38 +02:00
|
|
|
* 9/4/18
|
|
|
|
* - set interlaced=1 for interlaced images
|
2018-06-20 15:20:22 +02:00
|
|
|
* 20/6/18 [felixbuenemann]
|
|
|
|
* - support png8 palette write with palette, colours, Q, dither
|
2011-12-19 22:40:08 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
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
|
2013-03-07 06:40:19 +01:00
|
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
|
|
02110-1301 USA
|
2011-12-19 22:40:08 +01:00
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2012-08-21 17:18:25 +02:00
|
|
|
#define DEBUG
|
2014-11-10 11:18:09 +01:00
|
|
|
#define VIPS_DEBUG
|
2012-08-23 14:49:21 +02:00
|
|
|
*/
|
2011-12-19 22:40:08 +01:00
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include <config.h>
|
|
|
|
#endif /*HAVE_CONFIG_H*/
|
|
|
|
#include <vips/intl.h>
|
|
|
|
|
2011-12-24 18:36:55 +01:00
|
|
|
#ifdef HAVE_PNG
|
|
|
|
|
2011-12-19 22:40:08 +01:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2013-03-01 19:09:47 +01:00
|
|
|
#include <string.h>
|
2011-12-19 22:40:08 +01:00
|
|
|
|
|
|
|
#include <vips/vips.h>
|
|
|
|
#include <vips/internal.h>
|
|
|
|
#include <vips/debug.h>
|
|
|
|
|
2016-10-15 13:29:14 +02:00
|
|
|
#include "pforeign.h"
|
2011-12-19 22:40:08 +01:00
|
|
|
|
2016-10-15 13:29:14 +02:00
|
|
|
#include <png.h>
|
2011-12-19 22:40:08 +01:00
|
|
|
|
|
|
|
#if PNG_LIBPNG_VER < 10003
|
|
|
|
#error "PNG library too old."
|
|
|
|
#endif
|
|
|
|
|
2018-06-18 02:22:46 +02:00
|
|
|
#ifdef HAVE_IMAGEQUANT
|
|
|
|
#include <libimagequant.h>
|
|
|
|
#endif
|
|
|
|
|
2011-12-19 22:40:08 +01:00
|
|
|
static void
|
|
|
|
user_error_function( png_structp png_ptr, png_const_charp error_msg )
|
|
|
|
{
|
2017-05-03 11:32:24 +02:00
|
|
|
#ifdef DEBUG
|
|
|
|
printf( "user_error_function: %s\n", error_msg );
|
|
|
|
#endif /*DEBUG*/
|
|
|
|
|
|
|
|
g_warning( "%s", error_msg );
|
2012-02-23 13:42:21 +01:00
|
|
|
|
2013-03-01 19:09:47 +01:00
|
|
|
/* This function must not return or the default error handler will be
|
2012-02-23 13:42:21 +01:00
|
|
|
* invoked.
|
2017-03-30 18:13:25 +02:00
|
|
|
*/
|
2017-04-26 11:25:52 +02:00
|
|
|
longjmp( png_jmpbuf( png_ptr ), -1 );
|
2011-12-19 22:40:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
user_warning_function( png_structp png_ptr, png_const_charp warning_msg )
|
|
|
|
{
|
2017-05-03 11:32:24 +02:00
|
|
|
#ifdef DEBUG
|
|
|
|
printf( "user_warning_function: %s\n", warning_msg );
|
|
|
|
#endif /*DEBUG*/
|
|
|
|
|
|
|
|
g_warning( "%s", warning_msg );
|
2011-12-19 22:40:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* What we track during a PNG read.
|
|
|
|
*/
|
|
|
|
typedef struct {
|
|
|
|
char *name;
|
|
|
|
VipsImage *out;
|
2017-03-30 18:13:25 +02:00
|
|
|
gboolean fail;
|
2011-12-19 22:40:08 +01:00
|
|
|
|
2013-01-10 15:46:20 +01:00
|
|
|
int y_pos;
|
2011-12-19 22:40:08 +01:00
|
|
|
png_structp pPng;
|
|
|
|
png_infop pInfo;
|
|
|
|
png_bytep *row_pointer;
|
2013-06-16 23:51:28 +02:00
|
|
|
|
|
|
|
/* For FILE input.
|
|
|
|
*/
|
|
|
|
FILE *fp;
|
|
|
|
|
|
|
|
/* For memory input.
|
|
|
|
*/
|
2015-10-23 12:45:11 +02:00
|
|
|
const void *buffer;
|
2013-06-16 23:51:28 +02:00
|
|
|
size_t length;
|
|
|
|
size_t read_pos;
|
2013-09-29 12:04:11 +02:00
|
|
|
|
2011-12-19 22:40:08 +01:00
|
|
|
} Read;
|
|
|
|
|
2015-02-26 15:09:01 +01:00
|
|
|
/* Can be called many times.
|
|
|
|
*/
|
2011-12-19 22:40:08 +01:00
|
|
|
static void
|
2014-06-23 10:49:23 +02:00
|
|
|
read_destroy( Read *read )
|
2011-12-19 22:40:08 +01:00
|
|
|
{
|
|
|
|
VIPS_FREEF( fclose, read->fp );
|
|
|
|
if( read->pPng )
|
|
|
|
png_destroy_read_struct( &read->pPng, &read->pInfo, NULL );
|
|
|
|
VIPS_FREE( read->row_pointer );
|
|
|
|
}
|
|
|
|
|
2014-06-23 10:49:23 +02:00
|
|
|
static void
|
|
|
|
read_close_cb( VipsImage *out, Read *read )
|
|
|
|
{
|
|
|
|
read_destroy( read );
|
|
|
|
}
|
|
|
|
|
2011-12-19 22:40:08 +01:00
|
|
|
static Read *
|
2017-03-30 18:13:25 +02:00
|
|
|
read_new( VipsImage *out, gboolean fail )
|
2011-12-19 22:40:08 +01:00
|
|
|
{
|
|
|
|
Read *read;
|
|
|
|
|
|
|
|
if( !(read = VIPS_NEW( out, Read )) )
|
|
|
|
return( NULL );
|
|
|
|
|
2013-06-16 23:51:28 +02:00
|
|
|
read->name = NULL;
|
2017-03-30 18:13:25 +02:00
|
|
|
read->fail = fail;
|
2011-12-19 22:40:08 +01:00
|
|
|
read->out = out;
|
2013-01-10 15:46:20 +01:00
|
|
|
read->y_pos = 0;
|
2011-12-19 22:40:08 +01:00
|
|
|
read->pPng = NULL;
|
|
|
|
read->pInfo = NULL;
|
|
|
|
read->row_pointer = NULL;
|
2013-06-16 23:51:28 +02:00
|
|
|
read->fp = NULL;
|
|
|
|
read->buffer = NULL;
|
|
|
|
read->length = 0;
|
|
|
|
read->read_pos = 0;
|
2011-12-19 22:40:08 +01:00
|
|
|
|
|
|
|
g_signal_connect( out, "close",
|
2014-06-23 10:49:23 +02:00
|
|
|
G_CALLBACK( read_close_cb ), read );
|
2011-12-19 22:40:08 +01:00
|
|
|
|
|
|
|
if( !(read->pPng = png_create_read_struct(
|
|
|
|
PNG_LIBPNG_VER_STRING, NULL,
|
|
|
|
user_error_function, user_warning_function )) )
|
|
|
|
return( NULL );
|
|
|
|
|
2014-08-07 09:18:49 +02:00
|
|
|
#ifdef PNG_SKIP_sRGB_CHECK_PROFILE
|
|
|
|
/* Prevent libpng (>=1.6.11) verifying sRGB profiles.
|
|
|
|
*/
|
2014-08-09 18:14:49 +02:00
|
|
|
png_set_option( read->pPng,
|
|
|
|
PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON );
|
2014-08-07 09:18:49 +02:00
|
|
|
#endif /*PNG_SKIP_sRGB_CHECK_PROFILE*/
|
|
|
|
|
2011-12-19 22:40:08 +01:00
|
|
|
/* Catch PNG errors from png_create_info_struct().
|
|
|
|
*/
|
|
|
|
if( setjmp( png_jmpbuf( read->pPng ) ) )
|
|
|
|
return( NULL );
|
|
|
|
|
|
|
|
if( !(read->pInfo = png_create_info_struct( read->pPng )) )
|
|
|
|
return( NULL );
|
|
|
|
|
2013-06-16 23:51:28 +02:00
|
|
|
return( read );
|
|
|
|
}
|
|
|
|
|
|
|
|
static Read *
|
2017-03-30 18:13:25 +02:00
|
|
|
read_new_filename( VipsImage *out, const char *name, gboolean fail )
|
2013-06-16 23:51:28 +02:00
|
|
|
{
|
|
|
|
Read *read;
|
|
|
|
|
2017-03-30 18:13:25 +02:00
|
|
|
if( !(read = read_new( out, fail )) )
|
2013-06-16 23:51:28 +02:00
|
|
|
return( NULL );
|
|
|
|
|
|
|
|
read->name = vips_strdup( VIPS_OBJECT( out ), name );
|
|
|
|
|
|
|
|
if( !(read->fp = vips__file_open_read( name, NULL, FALSE )) )
|
|
|
|
return( NULL );
|
|
|
|
|
2014-06-23 10:49:23 +02:00
|
|
|
/* Catch PNG errors from png_read_info().
|
|
|
|
*/
|
|
|
|
if( setjmp( png_jmpbuf( read->pPng ) ) )
|
|
|
|
return( NULL );
|
|
|
|
|
2012-02-17 18:59:09 +01:00
|
|
|
/* Read enough of the file that png_get_interlace_type() will start
|
|
|
|
* working.
|
|
|
|
*/
|
|
|
|
png_init_io( read->pPng, read->fp );
|
|
|
|
png_read_info( read->pPng, read->pInfo );
|
|
|
|
|
2011-12-19 22:40:08 +01:00
|
|
|
return( read );
|
|
|
|
}
|
|
|
|
|
2012-02-07 15:30:54 +01:00
|
|
|
/* Read a png header.
|
2011-12-19 22:40:08 +01:00
|
|
|
*/
|
|
|
|
static int
|
2012-02-07 15:30:54 +01:00
|
|
|
png2vips_header( Read *read, VipsImage *out )
|
2011-12-19 22:40:08 +01:00
|
|
|
{
|
|
|
|
png_uint_32 width, height;
|
2012-02-15 18:19:42 +01:00
|
|
|
int bit_depth, color_type;
|
2012-02-16 17:04:19 +01:00
|
|
|
int interlace_type;
|
2011-12-19 22:40:08 +01:00
|
|
|
|
|
|
|
png_uint_32 res_x, res_y;
|
|
|
|
int unit_type;
|
|
|
|
|
2012-03-13 15:22:13 +01:00
|
|
|
png_charp name;
|
|
|
|
int compression_type;
|
2012-03-14 12:56:13 +01:00
|
|
|
|
|
|
|
/* Well thank you, libpng.
|
|
|
|
*/
|
|
|
|
#if PNG_LIBPNG_VER < 10400
|
|
|
|
png_charp profile;
|
|
|
|
#else
|
2012-03-13 15:22:13 +01:00
|
|
|
png_bytep profile;
|
2012-03-14 12:56:13 +01:00
|
|
|
#endif
|
|
|
|
|
2012-03-13 15:22:13 +01:00
|
|
|
png_uint_32 proflen;
|
|
|
|
|
2011-12-19 22:40:08 +01:00
|
|
|
int bands;
|
|
|
|
VipsInterpretation interpretation;
|
|
|
|
double Xres, Yres;
|
|
|
|
|
|
|
|
if( setjmp( png_jmpbuf( read->pPng ) ) )
|
|
|
|
return( -1 );
|
|
|
|
|
|
|
|
png_get_IHDR( read->pPng, read->pInfo,
|
|
|
|
&width, &height, &bit_depth, &color_type,
|
2012-02-16 17:04:19 +01:00
|
|
|
&interlace_type, NULL, NULL );
|
2011-12-19 22:40:08 +01:00
|
|
|
|
|
|
|
/* png_get_channels() gives us 1 band for palette images ... so look
|
|
|
|
* at colour_type for output bands.
|
2012-03-15 13:53:38 +01:00
|
|
|
*
|
|
|
|
* Ignore alpha, we detect that separately below.
|
2011-12-19 22:40:08 +01:00
|
|
|
*/
|
|
|
|
switch( color_type ) {
|
|
|
|
case PNG_COLOR_TYPE_PALETTE:
|
|
|
|
bands = 3;
|
|
|
|
break;
|
|
|
|
|
2012-03-15 13:53:38 +01:00
|
|
|
case PNG_COLOR_TYPE_GRAY_ALPHA:
|
2011-12-19 22:40:08 +01:00
|
|
|
case PNG_COLOR_TYPE_GRAY:
|
|
|
|
bands = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PNG_COLOR_TYPE_RGB:
|
|
|
|
case PNG_COLOR_TYPE_RGB_ALPHA:
|
2012-03-15 13:53:38 +01:00
|
|
|
bands = 3;
|
2011-12-19 22:40:08 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
vips_error( "png2vips", "%s", _( "unsupported color type" ) );
|
|
|
|
return( -1 );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( bit_depth > 8 ) {
|
|
|
|
if( bands < 3 )
|
|
|
|
interpretation = VIPS_INTERPRETATION_GREY16;
|
|
|
|
else
|
|
|
|
interpretation = VIPS_INTERPRETATION_RGB16;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if( bands < 3 )
|
|
|
|
interpretation = VIPS_INTERPRETATION_B_W;
|
|
|
|
else
|
|
|
|
interpretation = VIPS_INTERPRETATION_sRGB;
|
|
|
|
}
|
|
|
|
|
2012-03-15 10:54:26 +01:00
|
|
|
/* Expand palette images.
|
2011-12-19 22:40:08 +01:00
|
|
|
*/
|
|
|
|
if( color_type == PNG_COLOR_TYPE_PALETTE )
|
|
|
|
png_set_palette_to_rgb( read->pPng );
|
2012-03-15 10:54:26 +01:00
|
|
|
|
2012-03-15 13:53:38 +01:00
|
|
|
/* Expand transparency.
|
2012-03-15 10:54:26 +01:00
|
|
|
*/
|
|
|
|
if( png_get_valid( read->pPng, read->pInfo, PNG_INFO_tRNS ) ) {
|
2011-12-19 22:40:08 +01:00
|
|
|
png_set_tRNS_to_alpha( read->pPng );
|
2012-03-15 13:53:38 +01:00
|
|
|
bands += 1;
|
2012-06-17 23:16:20 +02:00
|
|
|
}
|
|
|
|
else if( color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
|
|
|
|
color_type == PNG_COLOR_TYPE_RGB_ALPHA ) {
|
|
|
|
/* Some images have no transparency chunk, but still set
|
|
|
|
* color_type to alpha.
|
|
|
|
*/
|
|
|
|
bands += 1;
|
2012-03-15 10:54:26 +01:00
|
|
|
}
|
|
|
|
|
2011-12-19 22:40:08 +01:00
|
|
|
/* Expand <8 bit images to full bytes.
|
|
|
|
*/
|
|
|
|
if( color_type == PNG_COLOR_TYPE_GRAY &&
|
|
|
|
bit_depth < 8 )
|
|
|
|
png_set_expand_gray_1_2_4_to_8( read->pPng );
|
|
|
|
|
|
|
|
/* If we're an INTEL byte order machine and this is 16bits, we need
|
|
|
|
* to swap bytes.
|
|
|
|
*/
|
2014-11-04 10:38:21 +01:00
|
|
|
if( bit_depth > 8 &&
|
|
|
|
!vips_amiMSBfirst() )
|
2011-12-19 22:40:08 +01:00
|
|
|
png_set_swap( read->pPng );
|
|
|
|
|
2014-11-04 10:38:21 +01:00
|
|
|
/* Get resolution. Default to 72 pixels per inch, the usual png value.
|
2011-12-19 22:40:08 +01:00
|
|
|
*/
|
|
|
|
unit_type = PNG_RESOLUTION_METER;
|
2014-11-04 10:38:21 +01:00
|
|
|
res_x = (72 / 2.54 * 100);
|
|
|
|
res_y = (72 / 2.54 * 100);
|
2011-12-19 22:40:08 +01:00
|
|
|
png_get_pHYs( read->pPng, read->pInfo, &res_x, &res_y, &unit_type );
|
|
|
|
switch( unit_type ) {
|
|
|
|
case PNG_RESOLUTION_METER:
|
|
|
|
Xres = res_x / 1000.0;
|
|
|
|
Yres = res_y / 1000.0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
Xres = res_x;
|
|
|
|
Yres = res_y;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set VIPS header.
|
|
|
|
*/
|
2012-02-07 15:30:54 +01:00
|
|
|
vips_image_init_fields( out,
|
2011-12-19 22:40:08 +01:00
|
|
|
width, height, bands,
|
|
|
|
bit_depth > 8 ?
|
|
|
|
VIPS_FORMAT_USHORT : VIPS_FORMAT_UCHAR,
|
|
|
|
VIPS_CODING_NONE, interpretation,
|
|
|
|
Xres, Yres );
|
|
|
|
|
2017-03-05 19:04:56 +01:00
|
|
|
/* Uninterlaced images will be read in seq mode. Interlaced images are
|
|
|
|
* read via a huge memory buffer.
|
2012-02-07 15:30:54 +01:00
|
|
|
*/
|
2018-01-01 12:14:27 +01:00
|
|
|
if( interlace_type == PNG_INTERLACE_NONE )
|
2017-03-05 19:04:56 +01:00
|
|
|
/* Sequential mode needs thinstrip to work with things like
|
|
|
|
* vips_shrink().
|
|
|
|
*/
|
|
|
|
vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL );
|
|
|
|
else
|
|
|
|
vips_image_pipelinev( out, VIPS_DEMAND_STYLE_ANY, NULL );
|
2011-12-19 22:40:08 +01:00
|
|
|
|
2012-03-13 15:22:13 +01:00
|
|
|
/* Fetch the ICC profile. @name is useless, something like "icc" or
|
2018-01-01 12:14:27 +01:00
|
|
|
* "ICC Profile" etc. Ignore it.
|
2012-03-13 15:22:13 +01:00
|
|
|
*
|
|
|
|
* @profile was png_charpp in libpngs < 1.5, png_bytepp is the
|
|
|
|
* modern one. Ignore the warning, if any.
|
|
|
|
*/
|
|
|
|
if( png_get_iCCP( read->pPng, read->pInfo,
|
|
|
|
&name, &compression_type, &profile, &proflen ) ) {
|
|
|
|
void *profile_copy;
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
2017-04-26 11:25:52 +02:00
|
|
|
printf( "png2vips_header: attaching %d bytes of ICC profile\n",
|
2012-03-13 15:22:13 +01:00
|
|
|
proflen );
|
|
|
|
printf( "png2vips_header: name = \"%s\"\n", name );
|
|
|
|
#endif /*DEBUG*/
|
|
|
|
|
|
|
|
if( !(profile_copy = vips_malloc( NULL, proflen )) )
|
|
|
|
return( -1 );
|
|
|
|
memcpy( profile_copy, profile, proflen );
|
|
|
|
vips_image_set_blob( out, VIPS_META_ICC_NAME,
|
|
|
|
(VipsCallbackFn) vips_free, profile_copy, proflen );
|
|
|
|
}
|
|
|
|
|
2013-07-16 13:20:50 +02:00
|
|
|
/* Sanity-check line size.
|
2012-03-15 13:53:38 +01:00
|
|
|
*/
|
|
|
|
png_read_update_info( read->pPng, read->pInfo );
|
|
|
|
if( png_get_rowbytes( read->pPng, read->pInfo ) !=
|
|
|
|
VIPS_IMAGE_SIZEOF_LINE( out ) ) {
|
|
|
|
vips_error( "vipspng",
|
|
|
|
"%s", _( "unable to read PNG header" ) );
|
|
|
|
return( -1 );
|
|
|
|
}
|
|
|
|
|
2018-04-09 18:14:38 +02:00
|
|
|
/* Let our caller know. These are very expensive to decode.
|
|
|
|
*/
|
|
|
|
if( interlace_type != PNG_INTERLACE_NONE )
|
|
|
|
vips_image_set_int( out, "interlaced", 1 );
|
|
|
|
|
2011-12-19 22:40:08 +01:00
|
|
|
return( 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read a PNG file header into a VIPS header.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
vips__png_header( const char *name, VipsImage *out )
|
|
|
|
{
|
|
|
|
Read *read;
|
|
|
|
|
2017-03-30 18:13:25 +02:00
|
|
|
if( !(read = read_new_filename( out, name, TRUE )) ||
|
2012-02-07 15:30:54 +01:00
|
|
|
png2vips_header( read, out ) )
|
|
|
|
return( -1 );
|
|
|
|
|
2015-02-26 15:09:01 +01:00
|
|
|
/* Just a header read: we can free the read early and save an fd.
|
|
|
|
*/
|
|
|
|
read_destroy( read );
|
|
|
|
|
2012-02-07 15:30:54 +01:00
|
|
|
return( 0 );
|
|
|
|
}
|
|
|
|
|
2012-02-15 18:19:42 +01:00
|
|
|
/* Out is a huge "t" buffer we decompress to.
|
2012-02-07 15:30:54 +01:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
png2vips_interlace( Read *read, VipsImage *out )
|
|
|
|
{
|
|
|
|
int y;
|
|
|
|
|
2012-02-15 18:19:42 +01:00
|
|
|
#ifdef DEBUG
|
|
|
|
printf( "png2vips_interlace: reading whole image\n" );
|
|
|
|
#endif /*DEBUG*/
|
|
|
|
|
2012-02-17 18:12:51 +01:00
|
|
|
if( vips_image_write_prepare( out ) )
|
2012-02-07 15:30:54 +01:00
|
|
|
return( -1 );
|
|
|
|
|
|
|
|
if( setjmp( png_jmpbuf( read->pPng ) ) )
|
|
|
|
return( -1 );
|
2017-05-29 19:58:24 +02:00
|
|
|
|
2012-02-17 18:12:51 +01:00
|
|
|
if( !(read->row_pointer = VIPS_ARRAY( NULL, out->Ysize, png_bytep )) )
|
|
|
|
return( -1 );
|
|
|
|
for( y = 0; y < out->Ysize; y++ )
|
|
|
|
read->row_pointer[y] = VIPS_IMAGE_ADDR( out, 0, y );
|
|
|
|
|
2017-12-05 09:38:52 +01:00
|
|
|
/* Some libpng warn you to call png_set_interlace_handling(); here, but
|
|
|
|
* that can actually break interlace. We have to live with the warning,
|
|
|
|
* unfortunately.
|
|
|
|
*/
|
|
|
|
|
2012-02-07 15:30:54 +01:00
|
|
|
png_read_image( read->pPng, read->row_pointer );
|
|
|
|
|
2014-06-23 10:49:23 +02:00
|
|
|
png_read_end( read->pPng, NULL );
|
|
|
|
|
|
|
|
read_destroy( read );
|
|
|
|
|
2012-02-07 15:30:54 +01:00
|
|
|
return( 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2012-02-15 14:55:34 +01:00
|
|
|
png2vips_generate( VipsRegion *or,
|
2012-02-07 15:30:54 +01:00
|
|
|
void *seq, void *a, void *b, gboolean *stop )
|
|
|
|
{
|
2012-02-15 14:55:34 +01:00
|
|
|
VipsRect *r = &or->valid;
|
2012-02-07 15:30:54 +01:00
|
|
|
Read *read = (Read *) a;
|
|
|
|
|
|
|
|
int y;
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
2014-05-19 15:53:47 +02:00
|
|
|
printf( "png2vips_generate: line %d, %d rows\n", r->top, r->height );
|
|
|
|
printf( "png2vips_generate: y_top = %d\n", read->y_pos );
|
2013-01-11 15:51:12 +01:00
|
|
|
#endif /*DEBUG*/
|
2012-02-07 15:30:54 +01:00
|
|
|
|
2012-02-16 17:04:19 +01:00
|
|
|
/* We're inside a tilecache where tiles are the full image width, so
|
|
|
|
* this should always be true.
|
|
|
|
*/
|
|
|
|
g_assert( r->left == 0 );
|
|
|
|
g_assert( r->width == or->im->Xsize );
|
|
|
|
g_assert( VIPS_RECT_BOTTOM( r ) <= or->im->Ysize );
|
|
|
|
|
2012-08-24 17:06:29 +02:00
|
|
|
/* Tiles should always be a strip in height, unless it's the final
|
|
|
|
* strip.
|
|
|
|
*/
|
|
|
|
g_assert( r->height == VIPS_MIN( 8, or->im->Ysize - r->top ) );
|
|
|
|
|
2013-01-10 15:46:20 +01:00
|
|
|
/* And check that y_pos is correct. It should be, since we are inside
|
2017-02-27 11:26:59 +01:00
|
|
|
* a vips_sequential().
|
2013-01-10 15:46:20 +01:00
|
|
|
*/
|
2014-09-02 10:37:08 +02:00
|
|
|
if( r->top != read->y_pos ) {
|
|
|
|
vips_error( "vipspng",
|
|
|
|
_( "out of order read at line %d" ), read->y_pos );
|
|
|
|
return( -1 );
|
|
|
|
}
|
2013-01-10 15:46:20 +01:00
|
|
|
|
2012-02-15 14:55:34 +01:00
|
|
|
for( y = 0; y < r->height; y++ ) {
|
|
|
|
png_bytep q = (png_bytep) VIPS_REGION_ADDR( or, 0, r->top + y );
|
2012-02-07 15:30:54 +01:00
|
|
|
|
2017-05-03 11:32:24 +02:00
|
|
|
/* We need to catch errors from read_row().
|
2013-07-16 13:20:50 +02:00
|
|
|
*/
|
|
|
|
if( !setjmp( png_jmpbuf( read->pPng ) ) )
|
|
|
|
png_read_row( read->pPng, q, NULL );
|
|
|
|
else {
|
2017-01-17 15:53:40 +01:00
|
|
|
/* We've failed to read some pixels. Knock this
|
|
|
|
* operation out of cache.
|
|
|
|
*/
|
|
|
|
vips_foreign_load_invalidate( read->out );
|
|
|
|
|
2013-07-16 13:20:50 +02:00
|
|
|
#ifdef DEBUG
|
|
|
|
printf( "png2vips_generate: png_read_row() failed, "
|
|
|
|
"line %d\n", r->top + y );
|
|
|
|
printf( "png2vips_generate: file %s\n", read->name );
|
|
|
|
printf( "png2vips_generate: thread %p\n",
|
|
|
|
g_thread_self() );
|
|
|
|
#endif /*DEBUG*/
|
2017-04-26 11:25:52 +02:00
|
|
|
|
2017-05-03 11:32:24 +02:00
|
|
|
/* And bail if fail is on. We have to add an error
|
|
|
|
* message, since the handler we install just does
|
|
|
|
* g_warning().
|
2017-04-26 11:25:52 +02:00
|
|
|
*/
|
2017-05-03 11:32:24 +02:00
|
|
|
if( read->fail ) {
|
|
|
|
vips_error( "vipspng",
|
|
|
|
"%s", _( "libpng read error" ) );
|
2017-04-26 11:25:52 +02:00
|
|
|
return( -1 );
|
2017-05-03 11:32:24 +02:00
|
|
|
}
|
2013-07-16 13:20:50 +02:00
|
|
|
}
|
2013-01-10 15:46:20 +01:00
|
|
|
|
|
|
|
read->y_pos += 1;
|
2012-02-07 15:30:54 +01:00
|
|
|
}
|
|
|
|
|
2017-05-03 11:32:24 +02:00
|
|
|
/* Catch errors from png_read_end(). This can fail on a truncated
|
|
|
|
* file.
|
2014-09-10 10:05:13 +02:00
|
|
|
*/
|
2017-03-30 18:13:25 +02:00
|
|
|
if( setjmp( png_jmpbuf( read->pPng ) ) ) {
|
2017-05-03 11:32:24 +02:00
|
|
|
if( read->fail ) {
|
|
|
|
vips_error( "vipspng", "%s", _( "libpng read error" ) );
|
2017-03-30 18:13:25 +02:00
|
|
|
return( -1 );
|
2017-05-03 11:32:24 +02:00
|
|
|
}
|
2017-03-30 18:13:25 +02:00
|
|
|
|
|
|
|
return( 0 );
|
|
|
|
}
|
2014-09-10 10:05:13 +02:00
|
|
|
|
2014-06-23 10:49:23 +02:00
|
|
|
/* We need to shut down the reader immediately at the end of read or
|
|
|
|
* we won't detach ready for the next image.
|
|
|
|
*/
|
|
|
|
if( read->y_pos >= read->out->Ysize ) {
|
|
|
|
png_read_end( read->pPng, NULL );
|
|
|
|
read_destroy( read );
|
|
|
|
}
|
|
|
|
|
2012-02-07 15:30:54 +01:00
|
|
|
return( 0 );
|
|
|
|
}
|
|
|
|
|
2012-02-15 14:55:34 +01:00
|
|
|
/* Interlaced PNGs need to be entirely decompressed into memory then can be
|
|
|
|
* served partially from there. Non-interlaced PNGs may be read sequentially.
|
2012-02-07 15:30:54 +01:00
|
|
|
*/
|
2012-02-15 14:55:34 +01:00
|
|
|
gboolean
|
|
|
|
vips__png_isinterlaced( const char *filename )
|
2012-02-07 15:30:54 +01:00
|
|
|
{
|
2012-02-15 14:55:34 +01:00
|
|
|
VipsImage *image;
|
|
|
|
Read *read;
|
2012-02-15 18:19:42 +01:00
|
|
|
int interlace_type;
|
2012-02-07 15:30:54 +01:00
|
|
|
|
2012-02-15 14:55:34 +01:00
|
|
|
image = vips_image_new();
|
2017-03-30 18:13:25 +02:00
|
|
|
if( !(read = read_new_filename( image, filename, TRUE )) ) {
|
2012-02-15 14:55:34 +01:00
|
|
|
g_object_unref( image );
|
2012-02-07 15:30:54 +01:00
|
|
|
return( -1 );
|
|
|
|
}
|
2012-02-16 17:04:19 +01:00
|
|
|
interlace_type = png_get_interlace_type( read->pPng, read->pInfo );
|
2012-02-15 14:55:34 +01:00
|
|
|
g_object_unref( image );
|
2011-12-19 22:40:08 +01:00
|
|
|
|
2012-02-15 18:19:42 +01:00
|
|
|
return( interlace_type != PNG_INTERLACE_NONE );
|
2011-12-19 22:40:08 +01:00
|
|
|
}
|
|
|
|
|
2013-06-16 23:51:28 +02:00
|
|
|
static int
|
2013-06-17 10:41:22 +02:00
|
|
|
png2vips_image( Read *read, VipsImage *out )
|
2011-12-19 22:40:08 +01:00
|
|
|
{
|
2013-06-16 23:51:28 +02:00
|
|
|
int interlace_type = png_get_interlace_type( read->pPng, read->pInfo );
|
2012-02-15 18:19:42 +01:00
|
|
|
VipsImage **t = (VipsImage **)
|
|
|
|
vips_object_local_array( VIPS_OBJECT( out ), 3 );
|
|
|
|
|
2012-02-17 18:59:09 +01:00
|
|
|
if( interlace_type != PNG_INTERLACE_NONE ) {
|
2012-02-16 17:04:19 +01:00
|
|
|
/* Arg awful interlaced image. We have to load to a huge mem
|
|
|
|
* buffer, then copy to out.
|
2012-02-15 18:19:42 +01:00
|
|
|
*/
|
2014-06-08 12:16:58 +02:00
|
|
|
t[0] = vips_image_new_memory();
|
2012-02-16 17:04:19 +01:00
|
|
|
if( png2vips_header( read, t[0] ) ||
|
|
|
|
png2vips_interlace( read, t[0] ) ||
|
2012-02-15 18:19:42 +01:00
|
|
|
vips_image_write( t[0], out ) )
|
2012-02-07 15:30:54 +01:00
|
|
|
return( -1 );
|
|
|
|
}
|
|
|
|
else {
|
2012-02-16 17:04:19 +01:00
|
|
|
t[0] = vips_image_new();
|
|
|
|
if( png2vips_header( read, t[0] ) ||
|
|
|
|
vips_image_generate( t[0],
|
2012-02-15 16:47:43 +01:00
|
|
|
NULL, png2vips_generate, NULL,
|
|
|
|
read, NULL ) ||
|
2017-02-27 11:26:59 +01:00
|
|
|
vips_sequential( t[0], &t[1],
|
2012-08-24 17:06:29 +02:00
|
|
|
"tile_height", 8,
|
|
|
|
NULL ) ||
|
2012-08-21 17:18:25 +02:00
|
|
|
vips_image_write( t[1], out ) )
|
2012-02-07 15:30:54 +01:00
|
|
|
return( -1 );
|
|
|
|
}
|
|
|
|
|
2013-06-16 23:51:28 +02:00
|
|
|
return( 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2017-03-30 18:13:25 +02:00
|
|
|
vips__png_read( const char *filename, VipsImage *out, gboolean fail )
|
2013-06-16 23:51:28 +02:00
|
|
|
{
|
|
|
|
Read *read;
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
printf( "vips__png_read: reading \"%s\"\n", filename );
|
|
|
|
#endif /*DEBUG*/
|
|
|
|
|
2017-03-30 18:13:25 +02:00
|
|
|
if( !(read = read_new_filename( out, filename, fail )) ||
|
2013-06-17 10:41:22 +02:00
|
|
|
png2vips_image( read, out ) )
|
2013-06-16 23:51:28 +02:00
|
|
|
return( -1 );
|
|
|
|
|
2012-02-16 17:04:19 +01:00
|
|
|
#ifdef DEBUG
|
2013-01-10 15:46:20 +01:00
|
|
|
printf( "vips__png_read: done\n" );
|
2012-02-16 17:04:19 +01:00
|
|
|
#endif /*DEBUG*/
|
|
|
|
|
2011-12-19 22:40:08 +01:00
|
|
|
return( 0 );
|
|
|
|
}
|
|
|
|
|
2015-10-23 12:45:11 +02:00
|
|
|
gboolean
|
|
|
|
vips__png_ispng_buffer( const void *buf, size_t len )
|
2014-04-22 17:27:43 +02:00
|
|
|
{
|
|
|
|
if( len >= 8 &&
|
2014-06-23 10:49:23 +02:00
|
|
|
!png_sig_cmp( (png_bytep) buf, 0, 8 ) )
|
2014-04-22 17:27:43 +02:00
|
|
|
return( TRUE );
|
|
|
|
|
|
|
|
return( FALSE );
|
|
|
|
}
|
|
|
|
|
2011-12-19 22:40:08 +01:00
|
|
|
int
|
|
|
|
vips__png_ispng( const char *filename )
|
|
|
|
{
|
|
|
|
unsigned char buf[8];
|
|
|
|
|
2018-03-22 13:08:39 +01:00
|
|
|
return( vips__get_bytes( filename, buf, 8 ) == 8 &&
|
2014-04-22 17:27:43 +02:00
|
|
|
vips__png_ispng_buffer( buf, 8 ) );
|
2011-12-19 22:40:08 +01:00
|
|
|
}
|
|
|
|
|
2013-06-16 23:51:28 +02:00
|
|
|
static void
|
|
|
|
vips_png_read_buffer( png_structp pPng, png_bytep data, png_size_t length )
|
|
|
|
{
|
|
|
|
Read *read = png_get_io_ptr( pPng );
|
|
|
|
|
2013-06-17 10:41:22 +02:00
|
|
|
#ifdef DEBUG
|
|
|
|
printf( "vips_png_read_buffer: read %zd bytes\n", length );
|
|
|
|
#endif /*DEBUG*/
|
|
|
|
|
2013-06-16 23:51:28 +02:00
|
|
|
if( read->read_pos + length > read->length )
|
|
|
|
png_error( pPng, "not enough data in buffer" );
|
|
|
|
|
2018-06-01 20:44:43 +02:00
|
|
|
memcpy( data, (VipsPel *) read->buffer + read->read_pos, length );
|
2013-06-16 23:51:28 +02:00
|
|
|
read->read_pos += length;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Read *
|
2017-03-30 18:13:25 +02:00
|
|
|
read_new_buffer( VipsImage *out, const void *buffer, size_t length,
|
|
|
|
gboolean fail )
|
2013-06-16 23:51:28 +02:00
|
|
|
{
|
|
|
|
Read *read;
|
|
|
|
|
2017-03-30 18:13:25 +02:00
|
|
|
if( !(read = read_new( out, fail )) )
|
2013-06-16 23:51:28 +02:00
|
|
|
return( NULL );
|
|
|
|
|
|
|
|
read->length = length;
|
2013-09-29 12:04:11 +02:00
|
|
|
read->buffer = buffer;
|
2013-06-16 23:51:28 +02:00
|
|
|
|
|
|
|
png_set_read_fn( read->pPng, read, vips_png_read_buffer );
|
|
|
|
|
2014-06-23 10:49:23 +02:00
|
|
|
/* Catch PNG errors from png_read_info().
|
|
|
|
*/
|
|
|
|
if( setjmp( png_jmpbuf( read->pPng ) ) )
|
|
|
|
return( NULL );
|
|
|
|
|
2013-06-16 23:51:28 +02:00
|
|
|
/* Read enough of the file that png_get_interlace_type() will start
|
|
|
|
* working.
|
|
|
|
*/
|
|
|
|
png_read_info( read->pPng, read->pInfo );
|
|
|
|
|
|
|
|
return( read );
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2015-10-23 12:45:11 +02:00
|
|
|
vips__png_header_buffer( const void *buffer, size_t length, VipsImage *out )
|
2013-06-16 23:51:28 +02:00
|
|
|
{
|
|
|
|
Read *read;
|
|
|
|
|
2017-03-30 18:13:25 +02:00
|
|
|
if( !(read = read_new_buffer( out, buffer, length, TRUE )) ||
|
2013-06-16 23:51:28 +02:00
|
|
|
png2vips_header( read, out ) )
|
|
|
|
return( -1 );
|
|
|
|
|
|
|
|
return( 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2017-03-30 18:13:25 +02:00
|
|
|
vips__png_read_buffer( const void *buffer, size_t length, VipsImage *out,
|
|
|
|
gboolean fail )
|
2013-06-16 23:51:28 +02:00
|
|
|
{
|
|
|
|
Read *read;
|
|
|
|
|
2017-03-30 18:13:25 +02:00
|
|
|
if( !(read = read_new_buffer( out, buffer, length, fail )) ||
|
2013-06-17 10:41:22 +02:00
|
|
|
png2vips_image( read, out ) )
|
2013-06-16 23:51:28 +02:00
|
|
|
return( -1 );
|
|
|
|
|
|
|
|
return( 0 );
|
|
|
|
}
|
|
|
|
|
2017-01-27 12:15:48 +01:00
|
|
|
/* Interlaced PNGs need to be entirely decompressed into memory then can be
|
|
|
|
* served partially from there. Non-interlaced PNGs may be read sequentially.
|
|
|
|
*/
|
|
|
|
gboolean
|
|
|
|
vips__png_isinterlaced_buffer( const void *buffer, size_t length )
|
|
|
|
{
|
|
|
|
VipsImage *image;
|
|
|
|
Read *read;
|
|
|
|
int interlace_type;
|
|
|
|
|
|
|
|
image = vips_image_new();
|
|
|
|
|
2017-03-30 18:13:25 +02:00
|
|
|
if( !(read = read_new_buffer( image, buffer, length, TRUE )) ) {
|
2017-01-27 12:15:48 +01:00
|
|
|
g_object_unref( image );
|
|
|
|
return( -1 );
|
|
|
|
}
|
|
|
|
interlace_type = png_get_interlace_type( read->pPng, read->pInfo );
|
|
|
|
g_object_unref( image );
|
|
|
|
|
|
|
|
return( interlace_type != PNG_INTERLACE_NONE );
|
|
|
|
}
|
|
|
|
|
2011-12-19 22:40:08 +01:00
|
|
|
const char *vips__png_suffs[] = { ".png", NULL };
|
|
|
|
|
|
|
|
/* What we track during a PNG write.
|
|
|
|
*/
|
|
|
|
typedef struct {
|
|
|
|
VipsImage *in;
|
2015-04-19 10:45:20 +02:00
|
|
|
VipsImage *memory;
|
2011-12-19 22:40:08 +01:00
|
|
|
|
|
|
|
FILE *fp;
|
2017-02-27 23:06:22 +01:00
|
|
|
VipsDbuf dbuf;
|
2016-05-24 11:57:02 +02:00
|
|
|
|
2011-12-19 22:40:08 +01:00
|
|
|
png_structp pPng;
|
|
|
|
png_infop pInfo;
|
|
|
|
png_bytep *row_pointer;
|
|
|
|
} Write;
|
|
|
|
|
|
|
|
static void
|
2012-02-14 11:53:39 +01:00
|
|
|
write_finish( Write *write )
|
2011-12-19 22:40:08 +01:00
|
|
|
{
|
|
|
|
VIPS_FREEF( fclose, write->fp );
|
2015-04-19 10:45:20 +02:00
|
|
|
VIPS_UNREF( write->memory );
|
2017-02-27 23:06:22 +01:00
|
|
|
vips_dbuf_destroy( &write->dbuf );
|
2011-12-19 22:40:08 +01:00
|
|
|
if( write->pPng )
|
|
|
|
png_destroy_write_struct( &write->pPng, &write->pInfo );
|
|
|
|
}
|
|
|
|
|
2012-02-14 11:53:39 +01:00
|
|
|
static void
|
|
|
|
write_destroy( VipsImage *out, Write *write )
|
|
|
|
{
|
|
|
|
write_finish( write );
|
|
|
|
}
|
|
|
|
|
2011-12-19 22:40:08 +01:00
|
|
|
static Write *
|
|
|
|
write_new( VipsImage *in )
|
|
|
|
{
|
|
|
|
Write *write;
|
|
|
|
|
|
|
|
if( !(write = VIPS_NEW( in, Write )) )
|
|
|
|
return( NULL );
|
|
|
|
memset( write, 0, sizeof( Write ) );
|
|
|
|
write->in = in;
|
2015-04-19 10:45:20 +02:00
|
|
|
write->memory = NULL;
|
2016-05-24 11:57:02 +02:00
|
|
|
write->fp = NULL;
|
2017-02-27 23:06:22 +01:00
|
|
|
vips_dbuf_init( &write->dbuf );
|
2011-12-19 22:40:08 +01:00
|
|
|
g_signal_connect( in, "close",
|
|
|
|
G_CALLBACK( write_destroy ), write );
|
|
|
|
|
|
|
|
if( !(write->row_pointer = VIPS_ARRAY( in, in->Ysize, png_bytep )) )
|
|
|
|
return( NULL );
|
|
|
|
if( !(write->pPng = png_create_write_struct(
|
|
|
|
PNG_LIBPNG_VER_STRING, NULL,
|
|
|
|
user_error_function, user_warning_function )) )
|
|
|
|
return( NULL );
|
|
|
|
|
2014-08-07 09:18:49 +02:00
|
|
|
#ifdef PNG_SKIP_sRGB_CHECK_PROFILE
|
|
|
|
/* Prevent libpng (>=1.6.11) verifying sRGB profiles.
|
|
|
|
*/
|
2014-08-09 18:14:49 +02:00
|
|
|
png_set_option( write->pPng,
|
|
|
|
PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON );
|
2014-08-07 09:18:49 +02:00
|
|
|
#endif /*PNG_SKIP_sRGB_CHECK_PROFILE*/
|
|
|
|
|
2011-12-19 22:40:08 +01:00
|
|
|
/* Catch PNG errors from png_create_info_struct().
|
|
|
|
*/
|
|
|
|
if( setjmp( png_jmpbuf( write->pPng ) ) )
|
|
|
|
return( NULL );
|
|
|
|
|
|
|
|
if( !(write->pInfo = png_create_info_struct( write->pPng )) )
|
|
|
|
return( NULL );
|
|
|
|
|
|
|
|
return( write );
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2014-07-25 19:06:52 +02:00
|
|
|
write_png_block( VipsRegion *region, VipsRect *area, void *a )
|
2011-12-19 22:40:08 +01:00
|
|
|
{
|
|
|
|
Write *write = (Write *) a;
|
|
|
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* The area to write is always a set of complete scanlines.
|
|
|
|
*/
|
|
|
|
g_assert( area->left == 0 );
|
|
|
|
g_assert( area->width == region->im->Xsize );
|
|
|
|
g_assert( area->top + area->height <= region->im->Ysize );
|
|
|
|
|
|
|
|
/* Catch PNG errors. Yuk.
|
|
|
|
*/
|
|
|
|
if( setjmp( png_jmpbuf( write->pPng ) ) )
|
|
|
|
return( -1 );
|
|
|
|
|
|
|
|
for( i = 0; i < area->height; i++ )
|
|
|
|
write->row_pointer[i] = (png_bytep)
|
|
|
|
VIPS_REGION_ADDR( region, 0, area->top + i );
|
|
|
|
|
|
|
|
png_write_rows( write->pPng, write->row_pointer, area->height );
|
|
|
|
|
|
|
|
return( 0 );
|
|
|
|
}
|
|
|
|
|
2018-06-18 02:22:46 +02:00
|
|
|
#ifdef HAVE_IMAGEQUANT
|
|
|
|
static int
|
2018-06-19 21:32:44 +02:00
|
|
|
quantise_image( VipsImage *in, VipsImage *out, VipsImage *palette_out,
|
|
|
|
int colours, int Q, double dither )
|
2018-06-18 02:22:46 +02:00
|
|
|
{
|
2018-06-20 15:20:22 +02:00
|
|
|
VipsImage *memory;
|
|
|
|
liq_attr *attr;
|
|
|
|
liq_image *input_image;
|
|
|
|
liq_result *quantisation_result;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Ensure input is sRGB.
|
|
|
|
*/
|
2018-06-18 02:22:46 +02:00
|
|
|
if( in->Type != VIPS_INTERPRETATION_sRGB) {
|
|
|
|
VipsImage *srgb;
|
2018-06-20 15:20:22 +02:00
|
|
|
|
2018-06-18 02:22:46 +02:00
|
|
|
if( vips_colourspace( in, &srgb, VIPS_INTERPRETATION_sRGB,
|
|
|
|
NULL ) )
|
|
|
|
return( -1 );
|
|
|
|
in = srgb;
|
|
|
|
VIPS_UNREF( srgb );
|
|
|
|
}
|
2018-06-20 15:20:22 +02:00
|
|
|
|
|
|
|
/* Add alpha channel if missing.
|
|
|
|
*/
|
|
|
|
if( !vips_image_hasalpha( in ) ) {
|
2018-06-18 02:22:46 +02:00
|
|
|
VipsImage *srgba;
|
2018-06-20 15:20:22 +02:00
|
|
|
|
2018-06-18 02:22:46 +02:00
|
|
|
if( vips_bandjoin_const1( in, &srgba, 255, NULL ) )
|
|
|
|
return( -1 );
|
|
|
|
in = srgba;
|
|
|
|
VIPS_UNREF( srgba );
|
|
|
|
}
|
2018-06-20 15:20:22 +02:00
|
|
|
|
2018-06-18 02:22:46 +02:00
|
|
|
if( !(memory = vips_image_copy_memory( in )) )
|
|
|
|
return( -1 );
|
|
|
|
in = memory;
|
|
|
|
|
2018-06-20 15:20:22 +02:00
|
|
|
attr = liq_attr_create();
|
2018-06-19 21:32:44 +02:00
|
|
|
liq_set_max_colors( attr, colours );
|
2018-06-18 02:22:46 +02:00
|
|
|
liq_set_quality( attr, 0, Q );
|
|
|
|
|
2018-06-20 15:20:22 +02:00
|
|
|
input_image = liq_image_create_rgba( attr,
|
2018-06-18 02:22:46 +02:00
|
|
|
VIPS_IMAGE_ADDR( in, 0, 0 ), in->Xsize, in->Ysize, 0 );
|
|
|
|
|
2018-06-19 21:32:44 +02:00
|
|
|
if ( liq_image_quantize( input_image, attr, &quantisation_result ) ) {
|
|
|
|
liq_result_destroy( quantisation_result );
|
2018-06-18 02:22:46 +02:00
|
|
|
liq_image_destroy( input_image );
|
|
|
|
liq_attr_destroy( attr );
|
|
|
|
VIPS_UNREF( memory );
|
|
|
|
return( -1 );
|
|
|
|
}
|
|
|
|
|
2018-06-19 21:32:44 +02:00
|
|
|
liq_set_dithering_level( quantisation_result, (float) dither );
|
2018-06-18 02:22:46 +02:00
|
|
|
|
|
|
|
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 ) ) {
|
2018-06-19 21:32:44 +02:00
|
|
|
liq_result_destroy( quantisation_result );
|
2018-06-18 02:22:46 +02:00
|
|
|
liq_image_destroy( input_image );
|
|
|
|
liq_attr_destroy( attr );
|
|
|
|
VIPS_UNREF( memory );
|
|
|
|
return( -1 );
|
|
|
|
}
|
|
|
|
|
2018-06-19 21:32:44 +02:00
|
|
|
if( liq_write_remapped_image( quantisation_result, input_image,
|
2018-06-18 02:22:46 +02:00
|
|
|
VIPS_IMAGE_ADDR( out, 0, 0 ), VIPS_IMAGE_N_PELS( out ) ) ) {
|
2018-06-19 21:32:44 +02:00
|
|
|
liq_result_destroy( quantisation_result );
|
2018-06-18 02:22:46 +02:00
|
|
|
liq_image_destroy( input_image );
|
|
|
|
liq_attr_destroy( attr );
|
|
|
|
VIPS_UNREF( memory );
|
|
|
|
return( -1 );
|
|
|
|
}
|
|
|
|
|
2018-06-19 21:32:44 +02:00
|
|
|
const liq_palette *palette = liq_get_palette( quantisation_result );
|
2018-06-18 02:22:46 +02:00
|
|
|
|
|
|
|
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 ) ) {
|
2018-06-19 21:32:44 +02:00
|
|
|
liq_result_destroy( quantisation_result );
|
2018-06-18 02:22:46 +02:00
|
|
|
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 );
|
2018-06-20 15:20:22 +02:00
|
|
|
|
2018-06-18 02:22:46 +02:00
|
|
|
p[0] = palette->entries[i].r;
|
|
|
|
p[1] = palette->entries[i].g;
|
|
|
|
p[2] = palette->entries[i].b;
|
|
|
|
p[3] = palette->entries[i].a;
|
|
|
|
}
|
|
|
|
|
2018-06-19 21:32:44 +02:00
|
|
|
liq_result_destroy( quantisation_result );
|
2018-06-18 02:22:46 +02:00
|
|
|
liq_image_destroy( input_image );
|
|
|
|
liq_attr_destroy( attr );
|
|
|
|
VIPS_UNREF( memory );
|
|
|
|
|
|
|
|
return( 0 );
|
|
|
|
}
|
2018-06-19 22:41:40 +02:00
|
|
|
#endif /*HAVE_IMAGEQUANT*/
|
2018-06-18 02:22:46 +02:00
|
|
|
|
2011-12-19 22:40:08 +01:00
|
|
|
/* Write a VIPS image to PNG.
|
|
|
|
*/
|
|
|
|
static int
|
2016-07-31 11:34:12 +02:00
|
|
|
write_vips( Write *write,
|
|
|
|
int compress, int interlace, const char *profile,
|
2018-06-19 22:41:40 +02:00
|
|
|
VipsForeignPngFilter filter, gboolean strip,
|
|
|
|
gboolean palette, int colours, int Q, double dither )
|
2011-12-19 22:40:08 +01:00
|
|
|
{
|
|
|
|
VipsImage *in = write->in;
|
|
|
|
|
|
|
|
int bit_depth;
|
|
|
|
int color_type;
|
|
|
|
int interlace_type;
|
|
|
|
int i, nb_passes;
|
|
|
|
|
|
|
|
g_assert( in->BandFmt == VIPS_FORMAT_UCHAR ||
|
|
|
|
in->BandFmt == VIPS_FORMAT_USHORT );
|
|
|
|
g_assert( in->Coding == VIPS_CODING_NONE );
|
|
|
|
g_assert( in->Bands > 0 && in->Bands < 5 );
|
|
|
|
|
|
|
|
/* Catch PNG errors.
|
|
|
|
*/
|
|
|
|
if( setjmp( png_jmpbuf( write->pPng ) ) )
|
|
|
|
return( -1 );
|
|
|
|
|
2014-08-03 10:34:46 +02:00
|
|
|
/* Check input image. If we are writing interlaced, we need to make 7
|
|
|
|
* passes over the image. We advertise ourselves as seq, so to ensure
|
|
|
|
* we only suck once from upstream, switch to WIO.
|
2011-12-19 22:40:08 +01:00
|
|
|
*/
|
2014-08-03 10:34:46 +02:00
|
|
|
if( interlace ) {
|
2015-04-19 10:45:20 +02:00
|
|
|
if( !(write->memory = vips_image_copy_memory( in )) )
|
2014-08-03 10:34:46 +02:00
|
|
|
return( -1 );
|
2015-04-19 10:45:20 +02:00
|
|
|
in = write->memory;
|
2014-08-03 10:34:46 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
if( vips_image_pio_input( in ) )
|
|
|
|
return( -1 );
|
|
|
|
}
|
2011-12-19 22:40:08 +01:00
|
|
|
if( compress < 0 || compress > 9 ) {
|
|
|
|
vips_error( "vips2png",
|
|
|
|
"%s", _( "compress should be in [0,9]" ) );
|
|
|
|
return( -1 );
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set compression parameters.
|
|
|
|
*/
|
|
|
|
png_set_compression_level( write->pPng, compress );
|
|
|
|
|
2014-10-25 16:42:10 +02:00
|
|
|
/* Set row filter.
|
|
|
|
*/
|
|
|
|
png_set_filter( write->pPng, 0, filter );
|
|
|
|
|
2011-12-19 22:40:08 +01:00
|
|
|
bit_depth = in->BandFmt == VIPS_FORMAT_UCHAR ? 8 : 16;
|
|
|
|
|
|
|
|
switch( in->Bands ) {
|
|
|
|
case 1: color_type = PNG_COLOR_TYPE_GRAY; break;
|
|
|
|
case 2: color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break;
|
|
|
|
case 3: color_type = PNG_COLOR_TYPE_RGB; break;
|
|
|
|
case 4: color_type = PNG_COLOR_TYPE_RGB_ALPHA; break;
|
|
|
|
|
|
|
|
default:
|
2014-11-10 13:56:28 +01:00
|
|
|
vips_error( "vips2png",
|
|
|
|
_( "can't save %d band image as png" ), in->Bands );
|
|
|
|
return( -1 );
|
2011-12-19 22:40:08 +01:00
|
|
|
}
|
|
|
|
|
2018-06-18 02:22:46 +02:00
|
|
|
#ifdef HAVE_IMAGEQUANT
|
2018-06-19 21:32:44 +02:00
|
|
|
/* Enable image quantisation to paletted 8bpp PNG if colours is set.
|
2018-06-18 02:22:46 +02:00
|
|
|
*/
|
2018-06-19 22:41:40 +02:00
|
|
|
if( palette ) {
|
2018-06-20 15:20:22 +02:00
|
|
|
g_assert( colours >= 2 &&
|
|
|
|
colours <= 256 );
|
2018-06-18 02:22:46 +02:00
|
|
|
bit_depth = 8;
|
|
|
|
color_type = PNG_COLOR_TYPE_PALETTE;
|
|
|
|
}
|
|
|
|
#else
|
2018-06-19 22:41:40 +02:00
|
|
|
if( palette )
|
|
|
|
g_warning( "%s",
|
|
|
|
_( "ignoring palette (no quantisation support)" ) );
|
|
|
|
#endif /*HAVE_IMAGEQUANT*/
|
2018-06-18 02:22:46 +02:00
|
|
|
|
2011-12-19 22:40:08 +01:00
|
|
|
interlace_type = interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE;
|
|
|
|
|
|
|
|
png_set_IHDR( write->pPng, write->pInfo,
|
|
|
|
in->Xsize, in->Ysize, bit_depth, color_type, interlace_type,
|
|
|
|
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT );
|
|
|
|
|
2014-08-03 10:34:46 +02:00
|
|
|
/* Set resolution. libpng uses pixels per meter.
|
2011-12-19 22:40:08 +01:00
|
|
|
*/
|
|
|
|
png_set_pHYs( write->pPng, write->pInfo,
|
|
|
|
VIPS_RINT( in->Xres * 1000 ), VIPS_RINT( in->Yres * 1000 ),
|
|
|
|
PNG_RESOLUTION_METER );
|
|
|
|
|
2012-03-13 15:22:13 +01:00
|
|
|
/* Set ICC Profile.
|
|
|
|
*/
|
2016-07-31 11:34:12 +02:00
|
|
|
if( profile &&
|
|
|
|
!strip ) {
|
2014-09-18 10:38:12 +02:00
|
|
|
if( strcmp( profile, "none" ) != 0 ) {
|
|
|
|
void *data;
|
2014-09-18 11:19:24 +02:00
|
|
|
size_t length;
|
2014-09-18 10:38:12 +02:00
|
|
|
|
2014-09-18 11:19:24 +02:00
|
|
|
if( !(data = vips__file_read_name( profile,
|
2017-05-11 18:08:10 +02:00
|
|
|
vips__icc_dir(), &length )) )
|
2014-09-18 10:38:12 +02:00
|
|
|
return( -1 );
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
printf( "write_vips: "
|
2014-09-18 11:19:24 +02:00
|
|
|
"attaching %zd bytes of ICC profile\n",
|
2014-09-18 10:38:12 +02:00
|
|
|
length );
|
|
|
|
#endif /*DEBUG*/
|
|
|
|
|
|
|
|
png_set_iCCP( write->pPng, write->pInfo, "icc",
|
2014-09-18 11:19:24 +02:00
|
|
|
PNG_COMPRESSION_TYPE_BASE, data, length );
|
2014-09-18 10:38:12 +02:00
|
|
|
}
|
|
|
|
}
|
2016-07-31 11:34:12 +02:00
|
|
|
else if( vips_image_get_typeof( in, VIPS_META_ICC_NAME ) &&
|
|
|
|
!strip ) {
|
2014-09-18 10:38:12 +02:00
|
|
|
void *data;
|
|
|
|
size_t length;
|
2013-01-09 12:33:13 +01:00
|
|
|
|
2013-02-27 09:51:28 +01:00
|
|
|
if( vips_image_get_blob( in, VIPS_META_ICC_NAME,
|
2014-09-18 10:38:12 +02:00
|
|
|
&data, &length ) )
|
2013-01-09 12:33:13 +01:00
|
|
|
return( -1 );
|
|
|
|
|
2012-03-13 15:22:13 +01:00
|
|
|
#ifdef DEBUG
|
|
|
|
printf( "write_vips: attaching %zd bytes of ICC profile\n",
|
2014-09-18 10:38:12 +02:00
|
|
|
length );
|
2012-03-13 15:22:13 +01:00
|
|
|
#endif /*DEBUG*/
|
|
|
|
|
|
|
|
png_set_iCCP( write->pPng, write->pInfo, "icc",
|
2014-09-18 10:38:12 +02:00
|
|
|
PNG_COMPRESSION_TYPE_BASE, data, length );
|
2012-03-13 15:22:13 +01:00
|
|
|
}
|
|
|
|
|
2018-06-18 02:22:46 +02:00
|
|
|
#ifdef HAVE_IMAGEQUANT
|
2018-06-19 22:41:40 +02:00
|
|
|
if( palette ) {
|
2018-06-20 15:20:22 +02:00
|
|
|
VipsImage *im_quantised;
|
|
|
|
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();
|
2018-06-19 22:41:40 +02:00
|
|
|
if( quantise_image( in, im_quantised, im_palette, colours, Q,
|
2018-06-18 02:22:46 +02:00
|
|
|
dither ) ) {
|
|
|
|
vips_error( "vips2png",
|
2018-06-19 21:32:44 +02:00
|
|
|
"%s", _( "quantisation failed" ) );
|
2018-06-19 22:41:40 +02:00
|
|
|
VIPS_UNREF( im_quantised );
|
|
|
|
VIPS_UNREF( im_palette );
|
2018-06-18 02:22:46 +02:00
|
|
|
return( -1 );
|
|
|
|
}
|
|
|
|
|
2018-06-20 15:20:22 +02:00
|
|
|
palette_count = im_palette->Xsize;
|
2018-06-18 02:22:46 +02:00
|
|
|
|
2018-06-20 15:20:22 +02:00
|
|
|
g_assert( palette_count <= PNG_MAX_PALETTE_LENGTH );
|
|
|
|
|
|
|
|
png_palette = (png_color *) png_malloc( write->pPng,
|
2018-06-18 02:22:46 +02:00
|
|
|
palette_count * sizeof( png_color ) );
|
2018-06-20 15:20:22 +02:00
|
|
|
png_trans = (png_byte *) png_malloc( write->pPng,
|
2018-06-18 02:22:46 +02:00
|
|
|
palette_count * sizeof( png_byte ) );
|
2018-06-20 15:20:22 +02:00
|
|
|
trans_count = 0;
|
2018-06-18 02:22:46 +02:00
|
|
|
for( i = 0; i < palette_count; i++ ) {
|
2018-06-19 22:41:40 +02:00
|
|
|
png_byte *p = (png_byte *) VIPS_IMAGE_ADDR( im_palette,
|
|
|
|
i, 0 );
|
2018-06-18 02:22:46 +02:00
|
|
|
png_color *col = &png_palette[i];
|
2018-06-20 15:20:22 +02:00
|
|
|
|
2018-06-18 02:22:46 +02:00
|
|
|
col->red = p[0];
|
|
|
|
col->green = p[1];
|
|
|
|
col->blue = p[2];
|
|
|
|
png_trans[i] = p[3];
|
|
|
|
if( p[3] != 255 )
|
|
|
|
trans_count = i + 1;
|
|
|
|
#ifdef DEBUG
|
|
|
|
printf( "write_vips: palette[%d] %d %d %d %d\n",
|
|
|
|
i + 1, p[0], p[1], p[2], p[3] );
|
|
|
|
#endif /*DEBUG*/
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
printf( "write_vips: attaching %d color palette\n",
|
|
|
|
palette_count );
|
|
|
|
#endif /*DEBUG*/
|
|
|
|
png_set_PLTE( write->pPng, write->pInfo, png_palette,
|
|
|
|
palette_count );
|
|
|
|
if( trans_count ) {
|
|
|
|
#ifdef DEBUG
|
|
|
|
printf( "write_vips: attaching %d alpha values\n",
|
|
|
|
trans_count );
|
|
|
|
#endif /*DEBUG*/
|
|
|
|
png_set_tRNS( write->pPng, write->pInfo, png_trans,
|
|
|
|
trans_count, NULL );
|
|
|
|
}
|
|
|
|
|
2018-06-20 15:20:22 +02:00
|
|
|
png_free( write->pPng, (void *) png_palette );
|
|
|
|
png_free( write->pPng, (void *) png_trans );
|
|
|
|
VIPS_UNREF( im_palette );
|
2018-06-18 02:22:46 +02:00
|
|
|
VIPS_UNREF( write->memory );
|
2018-06-20 15:20:22 +02:00
|
|
|
|
2018-06-19 22:41:40 +02:00
|
|
|
write->memory = im_quantised;
|
2018-06-18 02:22:46 +02:00
|
|
|
in = write->memory;
|
|
|
|
}
|
2018-06-19 22:41:40 +02:00
|
|
|
#endif /*HAVE_IMAGEQUANT*/
|
2018-06-20 15:20:22 +02:00
|
|
|
|
2018-06-18 02:22:46 +02:00
|
|
|
png_write_info( write->pPng, write->pInfo );
|
2011-12-19 22:40:08 +01:00
|
|
|
|
|
|
|
/* If we're an intel byte order CPU and this is a 16bit image, we need
|
|
|
|
* to swap bytes.
|
|
|
|
*/
|
2018-04-01 11:32:48 +02:00
|
|
|
if( bit_depth > 8 &&
|
|
|
|
!vips_amiMSBfirst() )
|
2011-12-19 22:40:08 +01:00
|
|
|
png_set_swap( write->pPng );
|
|
|
|
|
|
|
|
if( interlace )
|
|
|
|
nb_passes = png_set_interlace_handling( write->pPng );
|
|
|
|
else
|
|
|
|
nb_passes = 1;
|
|
|
|
|
|
|
|
/* Write data.
|
|
|
|
*/
|
|
|
|
for( i = 0; i < nb_passes; i++ )
|
2014-08-03 18:59:25 +02:00
|
|
|
if( vips_sink_disc( in, write_png_block, write ) )
|
2011-12-19 22:40:08 +01:00
|
|
|
return( -1 );
|
|
|
|
|
|
|
|
/* The setjmp() was held by our background writer: reset it.
|
|
|
|
*/
|
|
|
|
if( setjmp( png_jmpbuf( write->pPng ) ) )
|
|
|
|
return( -1 );
|
|
|
|
|
|
|
|
png_write_end( write->pPng, write->pInfo );
|
|
|
|
|
|
|
|
return( 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
vips__png_write( VipsImage *in, const char *filename,
|
2014-10-25 16:42:10 +02:00
|
|
|
int compress, int interlace, const char *profile,
|
2018-06-18 02:22:46 +02:00
|
|
|
VipsForeignPngFilter filter, gboolean strip,
|
2018-06-19 22:41:40 +02:00
|
|
|
gboolean palette, int colours, int Q, double dither )
|
2011-12-19 22:40:08 +01:00
|
|
|
{
|
|
|
|
Write *write;
|
|
|
|
|
2013-01-10 15:46:20 +01:00
|
|
|
#ifdef DEBUG
|
|
|
|
printf( "vips__png_write: writing \"%s\"\n", filename );
|
|
|
|
#endif /*DEBUG*/
|
|
|
|
|
2011-12-19 22:40:08 +01:00
|
|
|
if( !(write = write_new( in )) )
|
|
|
|
return( -1 );
|
|
|
|
|
|
|
|
/* Make output.
|
|
|
|
*/
|
|
|
|
if( !(write->fp = vips__file_open_write( filename, FALSE )) )
|
|
|
|
return( -1 );
|
|
|
|
png_init_io( write->pPng, write->fp );
|
|
|
|
|
|
|
|
/* Convert it!
|
|
|
|
*/
|
2016-07-31 11:34:12 +02:00
|
|
|
if( write_vips( write,
|
2018-06-19 22:41:40 +02:00
|
|
|
compress, interlace, profile, filter, strip, palette,
|
|
|
|
colours, Q, dither ) ) {
|
2011-12-19 22:40:08 +01:00
|
|
|
vips_error( "vips2png",
|
|
|
|
_( "unable to write \"%s\"" ), filename );
|
|
|
|
|
|
|
|
return( -1 );
|
|
|
|
}
|
|
|
|
|
2012-02-14 11:53:39 +01:00
|
|
|
write_finish( write );
|
|
|
|
|
2013-01-10 15:46:20 +01:00
|
|
|
#ifdef DEBUG
|
|
|
|
printf( "vips__png_write: done\n" );
|
|
|
|
#endif /*DEBUG*/
|
|
|
|
|
2011-12-19 22:40:08 +01:00
|
|
|
return( 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
user_write_data( png_structp png_ptr, png_bytep data, png_size_t length )
|
|
|
|
{
|
2016-05-24 11:57:02 +02:00
|
|
|
Write *write = (Write *) png_get_io_ptr( png_ptr );
|
2011-12-19 22:40:08 +01:00
|
|
|
|
2017-02-28 17:44:12 +01:00
|
|
|
vips_dbuf_write( &write->dbuf, data, length );
|
2011-12-19 22:40:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
vips__png_write_buf( VipsImage *in,
|
2014-09-18 10:38:12 +02:00
|
|
|
void **obuf, size_t *olen, int compression, int interlace,
|
2018-06-18 02:22:46 +02:00
|
|
|
const char *profile, VipsForeignPngFilter filter, gboolean strip,
|
2018-06-19 22:41:40 +02:00
|
|
|
gboolean palette, int colours, int Q, double dither )
|
2011-12-19 22:40:08 +01:00
|
|
|
{
|
|
|
|
Write *write;
|
|
|
|
|
2016-05-24 11:57:02 +02:00
|
|
|
if( !(write = write_new( in )) )
|
2011-12-20 19:14:41 +01:00
|
|
|
return( -1 );
|
2011-12-19 22:40:08 +01:00
|
|
|
|
2016-05-24 11:57:02 +02:00
|
|
|
png_set_write_fn( write->pPng, write, user_write_data, NULL );
|
2011-12-19 22:40:08 +01:00
|
|
|
|
|
|
|
/* Convert it!
|
|
|
|
*/
|
2016-07-31 11:34:12 +02:00
|
|
|
if( write_vips( write,
|
2018-06-19 22:41:40 +02:00
|
|
|
compression, interlace, profile, filter, strip, palette,
|
|
|
|
colours, Q, dither ) ) {
|
2011-12-20 19:14:41 +01:00
|
|
|
vips_error( "vips2png",
|
2011-12-19 22:40:08 +01:00
|
|
|
"%s", _( "unable to write to buffer" ) );
|
2018-06-19 22:41:40 +02:00
|
|
|
|
2011-12-19 22:40:08 +01:00
|
|
|
return( -1 );
|
|
|
|
}
|
|
|
|
|
2017-02-27 23:06:22 +01:00
|
|
|
*obuf = vips_dbuf_steal( &write->dbuf, olen );
|
2011-12-19 22:40:08 +01:00
|
|
|
|
2016-05-24 11:57:02 +02:00
|
|
|
write_finish( write );
|
2011-12-19 22:40:08 +01:00
|
|
|
|
|
|
|
return( 0 );
|
|
|
|
}
|
|
|
|
|
2011-12-24 18:36:55 +01:00
|
|
|
#endif /*HAVE_PNG*/
|