libvips/libvips/foreign/vipspng.c

1345 lines
32 KiB
C
Raw Permalink Normal View History

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
* 7/2/12
* - mild refactoring
* - add support for sequential reads
* 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
* 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
* 9/8/14
* - don't check profiles, helps with libpng >=1.6.11
* 27/10/14 Lovell
* - add @filter option
* 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
* 27/2/17
* - use dbuf for buffer output
* 30/3/17
* - better behaviour for truncated png files, thanks Yury
* 26/4/17
* - better @fail handling with truncated PNGs
* 9/4/18
* - set interlaced=1 for interlaced images
* 20/6/18 [felixbuenemann]
* - support png8 palette write with palette, colours, Q, dither
2018-10-06 12:54:27 +02:00
* 25/8/18
* - support xmp read/write
* 20/4/19
* - allow huge xmp metadata
* 7/10/19
* - restart after minimise
2019-10-14 19:03:45 +02:00
* 14/10/19
* - revise for connection IO
* 11/5/20
* - only warn for saving bad profiles, don't fail
* 19/2/21 781545872
* - read out background, if we can
2021-08-29 20:08:51 +02:00
* 29/8/21 joshuamsager
* - add "unlimited" flag to png load
* 13/1/22
* - raise libpng pixel size limit to VIPS_MAX_COORD
* 17/11/22
* - add exif read/write
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
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 <glib/gi18n-lib.h>
2011-12-19 22:40:08 +01:00
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
2011-12-19 22:40:08 +01:00
#include <vips/vips.h>
#include <vips/internal.h>
#include <vips/debug.h>
#include "pforeign.h"
2021-11-21 11:11:28 +01:00
#include "quantise.h"
2011-12-19 22:40:08 +01:00
/* Shared with spng load/save.
*/
const char *vips__png_suffs[] = { ".png", NULL };
#ifdef HAVE_PNG
#include <png.h>
2011-12-19 22:40:08 +01:00
#if PNG_LIBPNG_VER < 10003
#error "PNG library too old."
#endif
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 );
/* This function must not return or the default error handler will be
* invoked.
*/
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
}
2019-10-14 19:03:45 +02:00
#define INPUT_BUFFER_SIZE (4096)
2011-12-19 22:40:08 +01:00
/* What we track during a PNG read.
*/
typedef struct {
char *name;
VipsImage *out;
VipsFailOn fail_on;
gboolean unlimited;
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;
experiment with renaming stream rename as VipsConnection, VipsSource, VipsTarget etc. see https://github.com/libvips/libvips/issues/1494#issuecomment-569498619 renamed with this script: ``` set -e edit() { sed -i -E "$1" rename } for i in $*; do cp $i rename edit s/VIPS_STREAMOU/VIPS_TARGET_CUSTOM/g edit s/VIPS_STREAMO/VIPS_TARGET/g edit s/VIPS_STREAMIU/VIPS_SOURCE_CUSTOM/g edit s/VIPS_STREAMI/VIPS_SOURCE/g edit s/VIPS_STREAM/VIPS_CONNECTION/g edit s/vips_streamou/vips_target_custom/g edit s/vips_streamo/vips_target/g edit s/vips_streamiu/vips_source_custom/g edit s/vips_streami/vips_source/g edit s/vips_stream/vips_connection/g edit s/VipsStreamou/VipsTargetCustom/g edit s/VipsStreamo/VipsTarget/g edit s/VipsStreamiu/VipsSourceCustom/g edit s/VipsStreami/VipsSource/g edit s/VipsStream/VipsConnection/g # eg. VIPS_TYPE_STREAM or VIPS_IS_STREAM edit "s/VIPS_([A-Z]+)_STREAMOU/VIPS_\1_TARGET_CUSTOM/g" edit "s/VIPS_([A-Z]+)_STREAMO/VIPS_\1_TARGET/g" edit "s/VIPS_([A-Z]+)_STREAMIU/VIPS_\1_SOURCE_CUSTOM/g" edit "s/VIPS_([A-Z]+)_STREAMI/VIPS_\1_SOURCE/g" edit "s/VIPS_([A-Z]+)_STREAM/VIPS_\1_CONNECTION/g" edit s/streamou/target_custom/g edit s/streamo/target/g edit s/streamiu/source_custom/g edit s/streami/source/g # various identifiers which also change edit s/is_a_stream/is_a_source/g edit s/find_load_stream/find_load_source/g edit s/find_save_stream/find_save_target/g edit s/new_from_stream/new_from_source/g edit s/write_to_stream/write_to_target/g edit s/vips_thumbnail_stream/vips_thumbnail_source/g # eg. vips_webpload_stream edit "s/vips_([a-z]+)load_stream/vips_\1load_source/g" # eg. vips_webpsave_stream edit "s/vips_([a-z]+)save_stream/vips_\1save_target/g" mv rename $i done ```
2019-12-29 22:40:21 +01:00
VipsSource *source;
2019-10-14 19:03:45 +02:00
/* read() to this buffer, copy to png as required. libpng does many
* very small reads and we want to avoid a syscall for each one.
*/
2019-10-14 19:03:45 +02:00
unsigned char input_buffer[INPUT_BUFFER_SIZE];
unsigned char *next_byte;
gint64 bytes_in_buffer;
2011-12-19 22:40:08 +01:00
} Read;
/* Can be called many times.
*/
2011-12-19 22:40:08 +01:00
static void
read_destroy( Read *read )
2011-12-19 22:40:08 +01:00
{
2019-10-15 09:33:48 +02:00
/* We never call png_read_end(), perhaps we should. It can fail on
* truncated files, so we'd need a setjmp().
*/
2011-12-19 22:40:08 +01:00
if( read->pPng )
png_destroy_read_struct( &read->pPng, &read->pInfo, NULL );
experiment with renaming stream rename as VipsConnection, VipsSource, VipsTarget etc. see https://github.com/libvips/libvips/issues/1494#issuecomment-569498619 renamed with this script: ``` set -e edit() { sed -i -E "$1" rename } for i in $*; do cp $i rename edit s/VIPS_STREAMOU/VIPS_TARGET_CUSTOM/g edit s/VIPS_STREAMO/VIPS_TARGET/g edit s/VIPS_STREAMIU/VIPS_SOURCE_CUSTOM/g edit s/VIPS_STREAMI/VIPS_SOURCE/g edit s/VIPS_STREAM/VIPS_CONNECTION/g edit s/vips_streamou/vips_target_custom/g edit s/vips_streamo/vips_target/g edit s/vips_streamiu/vips_source_custom/g edit s/vips_streami/vips_source/g edit s/vips_stream/vips_connection/g edit s/VipsStreamou/VipsTargetCustom/g edit s/VipsStreamo/VipsTarget/g edit s/VipsStreamiu/VipsSourceCustom/g edit s/VipsStreami/VipsSource/g edit s/VipsStream/VipsConnection/g # eg. VIPS_TYPE_STREAM or VIPS_IS_STREAM edit "s/VIPS_([A-Z]+)_STREAMOU/VIPS_\1_TARGET_CUSTOM/g" edit "s/VIPS_([A-Z]+)_STREAMO/VIPS_\1_TARGET/g" edit "s/VIPS_([A-Z]+)_STREAMIU/VIPS_\1_SOURCE_CUSTOM/g" edit "s/VIPS_([A-Z]+)_STREAMI/VIPS_\1_SOURCE/g" edit "s/VIPS_([A-Z]+)_STREAM/VIPS_\1_CONNECTION/g" edit s/streamou/target_custom/g edit s/streamo/target/g edit s/streamiu/source_custom/g edit s/streami/source/g # various identifiers which also change edit s/is_a_stream/is_a_source/g edit s/find_load_stream/find_load_source/g edit s/find_save_stream/find_save_target/g edit s/new_from_stream/new_from_source/g edit s/write_to_stream/write_to_target/g edit s/vips_thumbnail_stream/vips_thumbnail_source/g # eg. vips_webpload_stream edit "s/vips_([a-z]+)load_stream/vips_\1load_source/g" # eg. vips_webpsave_stream edit "s/vips_([a-z]+)save_stream/vips_\1save_target/g" mv rename $i done ```
2019-12-29 22:40:21 +01:00
VIPS_UNREF( read->source );
2011-12-19 22:40:08 +01:00
VIPS_FREE( read->row_pointer );
}
static void
read_close_cb( VipsImage *out, Read *read )
{
read_destroy( read );
}
static void
2019-10-14 19:03:45 +02:00
read_minimise_cb( VipsImage *image, Read *read )
{
experiment with renaming stream rename as VipsConnection, VipsSource, VipsTarget etc. see https://github.com/libvips/libvips/issues/1494#issuecomment-569498619 renamed with this script: ``` set -e edit() { sed -i -E "$1" rename } for i in $*; do cp $i rename edit s/VIPS_STREAMOU/VIPS_TARGET_CUSTOM/g edit s/VIPS_STREAMO/VIPS_TARGET/g edit s/VIPS_STREAMIU/VIPS_SOURCE_CUSTOM/g edit s/VIPS_STREAMI/VIPS_SOURCE/g edit s/VIPS_STREAM/VIPS_CONNECTION/g edit s/vips_streamou/vips_target_custom/g edit s/vips_streamo/vips_target/g edit s/vips_streamiu/vips_source_custom/g edit s/vips_streami/vips_source/g edit s/vips_stream/vips_connection/g edit s/VipsStreamou/VipsTargetCustom/g edit s/VipsStreamo/VipsTarget/g edit s/VipsStreamiu/VipsSourceCustom/g edit s/VipsStreami/VipsSource/g edit s/VipsStream/VipsConnection/g # eg. VIPS_TYPE_STREAM or VIPS_IS_STREAM edit "s/VIPS_([A-Z]+)_STREAMOU/VIPS_\1_TARGET_CUSTOM/g" edit "s/VIPS_([A-Z]+)_STREAMO/VIPS_\1_TARGET/g" edit "s/VIPS_([A-Z]+)_STREAMIU/VIPS_\1_SOURCE_CUSTOM/g" edit "s/VIPS_([A-Z]+)_STREAMI/VIPS_\1_SOURCE/g" edit "s/VIPS_([A-Z]+)_STREAM/VIPS_\1_CONNECTION/g" edit s/streamou/target_custom/g edit s/streamo/target/g edit s/streamiu/source_custom/g edit s/streami/source/g # various identifiers which also change edit s/is_a_stream/is_a_source/g edit s/find_load_stream/find_load_source/g edit s/find_save_stream/find_save_target/g edit s/new_from_stream/new_from_source/g edit s/write_to_stream/write_to_target/g edit s/vips_thumbnail_stream/vips_thumbnail_source/g # eg. vips_webpload_stream edit "s/vips_([a-z]+)load_stream/vips_\1load_source/g" # eg. vips_webpsave_stream edit "s/vips_([a-z]+)save_stream/vips_\1save_target/g" mv rename $i done ```
2019-12-29 22:40:21 +01:00
if( read->source )
vips_source_minimise( read->source );
2019-10-14 19:03:45 +02:00
}
static void
vips_png_read_source( png_structp pPng, png_bytep data, png_size_t length )
{
2019-10-14 19:03:45 +02:00
Read *read = png_get_io_ptr( pPng );
#ifdef DEBUG
printf( "vips_png_read_source: read %zd bytes\n", length );
2019-10-14 19:03:45 +02:00
#endif /*DEBUG*/
/* libpng makes many small reads, which hurts performance if you do a
* syscall for each one. Read via our own buffer.
*/
while( length > 0 ) {
gint64 bytes_available;
2019-10-14 19:03:45 +02:00
2019-10-15 09:33:48 +02:00
if( read->bytes_in_buffer <= 0 ) {
gint64 bytes_read;
2019-10-14 19:03:45 +02:00
experiment with renaming stream rename as VipsConnection, VipsSource, VipsTarget etc. see https://github.com/libvips/libvips/issues/1494#issuecomment-569498619 renamed with this script: ``` set -e edit() { sed -i -E "$1" rename } for i in $*; do cp $i rename edit s/VIPS_STREAMOU/VIPS_TARGET_CUSTOM/g edit s/VIPS_STREAMO/VIPS_TARGET/g edit s/VIPS_STREAMIU/VIPS_SOURCE_CUSTOM/g edit s/VIPS_STREAMI/VIPS_SOURCE/g edit s/VIPS_STREAM/VIPS_CONNECTION/g edit s/vips_streamou/vips_target_custom/g edit s/vips_streamo/vips_target/g edit s/vips_streamiu/vips_source_custom/g edit s/vips_streami/vips_source/g edit s/vips_stream/vips_connection/g edit s/VipsStreamou/VipsTargetCustom/g edit s/VipsStreamo/VipsTarget/g edit s/VipsStreamiu/VipsSourceCustom/g edit s/VipsStreami/VipsSource/g edit s/VipsStream/VipsConnection/g # eg. VIPS_TYPE_STREAM or VIPS_IS_STREAM edit "s/VIPS_([A-Z]+)_STREAMOU/VIPS_\1_TARGET_CUSTOM/g" edit "s/VIPS_([A-Z]+)_STREAMO/VIPS_\1_TARGET/g" edit "s/VIPS_([A-Z]+)_STREAMIU/VIPS_\1_SOURCE_CUSTOM/g" edit "s/VIPS_([A-Z]+)_STREAMI/VIPS_\1_SOURCE/g" edit "s/VIPS_([A-Z]+)_STREAM/VIPS_\1_CONNECTION/g" edit s/streamou/target_custom/g edit s/streamo/target/g edit s/streamiu/source_custom/g edit s/streami/source/g # various identifiers which also change edit s/is_a_stream/is_a_source/g edit s/find_load_stream/find_load_source/g edit s/find_save_stream/find_save_target/g edit s/new_from_stream/new_from_source/g edit s/write_to_stream/write_to_target/g edit s/vips_thumbnail_stream/vips_thumbnail_source/g # eg. vips_webpload_stream edit "s/vips_([a-z]+)load_stream/vips_\1load_source/g" # eg. vips_webpsave_stream edit "s/vips_([a-z]+)save_stream/vips_\1save_target/g" mv rename $i done ```
2019-12-29 22:40:21 +01:00
bytes_read = vips_source_read( read->source,
2019-10-14 19:03:45 +02:00
read->input_buffer, INPUT_BUFFER_SIZE );
if( bytes_read <= 0 )
png_error( pPng, "not enough data" );
read->next_byte = read->input_buffer;
read->bytes_in_buffer = bytes_read;
}
2019-10-15 09:33:48 +02:00
bytes_available = VIPS_MIN( read->bytes_in_buffer, length );
memcpy( data, read->next_byte, bytes_available );
data += bytes_available;
length -= bytes_available;
read->next_byte += bytes_available;
read->bytes_in_buffer -= bytes_available;
2019-10-14 19:03:45 +02:00
}
}
2011-12-19 22:40:08 +01:00
static Read *
2021-08-29 20:08:51 +02:00
read_new( VipsSource *source, VipsImage *out,
VipsFailOn fail_on, gboolean unlimited )
2011-12-19 22:40:08 +01:00
{
Read *read;
if( !(read = VIPS_NEW( out, Read )) )
return( NULL );
read->name = NULL;
read->fail_on = fail_on;
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;
experiment with renaming stream rename as VipsConnection, VipsSource, VipsTarget etc. see https://github.com/libvips/libvips/issues/1494#issuecomment-569498619 renamed with this script: ``` set -e edit() { sed -i -E "$1" rename } for i in $*; do cp $i rename edit s/VIPS_STREAMOU/VIPS_TARGET_CUSTOM/g edit s/VIPS_STREAMO/VIPS_TARGET/g edit s/VIPS_STREAMIU/VIPS_SOURCE_CUSTOM/g edit s/VIPS_STREAMI/VIPS_SOURCE/g edit s/VIPS_STREAM/VIPS_CONNECTION/g edit s/vips_streamou/vips_target_custom/g edit s/vips_streamo/vips_target/g edit s/vips_streamiu/vips_source_custom/g edit s/vips_streami/vips_source/g edit s/vips_stream/vips_connection/g edit s/VipsStreamou/VipsTargetCustom/g edit s/VipsStreamo/VipsTarget/g edit s/VipsStreamiu/VipsSourceCustom/g edit s/VipsStreami/VipsSource/g edit s/VipsStream/VipsConnection/g # eg. VIPS_TYPE_STREAM or VIPS_IS_STREAM edit "s/VIPS_([A-Z]+)_STREAMOU/VIPS_\1_TARGET_CUSTOM/g" edit "s/VIPS_([A-Z]+)_STREAMO/VIPS_\1_TARGET/g" edit "s/VIPS_([A-Z]+)_STREAMIU/VIPS_\1_SOURCE_CUSTOM/g" edit "s/VIPS_([A-Z]+)_STREAMI/VIPS_\1_SOURCE/g" edit "s/VIPS_([A-Z]+)_STREAM/VIPS_\1_CONNECTION/g" edit s/streamou/target_custom/g edit s/streamo/target/g edit s/streamiu/source_custom/g edit s/streami/source/g # various identifiers which also change edit s/is_a_stream/is_a_source/g edit s/find_load_stream/find_load_source/g edit s/find_save_stream/find_save_target/g edit s/new_from_stream/new_from_source/g edit s/write_to_stream/write_to_target/g edit s/vips_thumbnail_stream/vips_thumbnail_source/g # eg. vips_webpload_stream edit "s/vips_([a-z]+)load_stream/vips_\1load_source/g" # eg. vips_webpsave_stream edit "s/vips_([a-z]+)save_stream/vips_\1save_target/g" mv rename $i done ```
2019-12-29 22:40:21 +01:00
read->source = source;
read->unlimited = unlimited;
experiment with renaming stream rename as VipsConnection, VipsSource, VipsTarget etc. see https://github.com/libvips/libvips/issues/1494#issuecomment-569498619 renamed with this script: ``` set -e edit() { sed -i -E "$1" rename } for i in $*; do cp $i rename edit s/VIPS_STREAMOU/VIPS_TARGET_CUSTOM/g edit s/VIPS_STREAMO/VIPS_TARGET/g edit s/VIPS_STREAMIU/VIPS_SOURCE_CUSTOM/g edit s/VIPS_STREAMI/VIPS_SOURCE/g edit s/VIPS_STREAM/VIPS_CONNECTION/g edit s/vips_streamou/vips_target_custom/g edit s/vips_streamo/vips_target/g edit s/vips_streamiu/vips_source_custom/g edit s/vips_streami/vips_source/g edit s/vips_stream/vips_connection/g edit s/VipsStreamou/VipsTargetCustom/g edit s/VipsStreamo/VipsTarget/g edit s/VipsStreamiu/VipsSourceCustom/g edit s/VipsStreami/VipsSource/g edit s/VipsStream/VipsConnection/g # eg. VIPS_TYPE_STREAM or VIPS_IS_STREAM edit "s/VIPS_([A-Z]+)_STREAMOU/VIPS_\1_TARGET_CUSTOM/g" edit "s/VIPS_([A-Z]+)_STREAMO/VIPS_\1_TARGET/g" edit "s/VIPS_([A-Z]+)_STREAMIU/VIPS_\1_SOURCE_CUSTOM/g" edit "s/VIPS_([A-Z]+)_STREAMI/VIPS_\1_SOURCE/g" edit "s/VIPS_([A-Z]+)_STREAM/VIPS_\1_CONNECTION/g" edit s/streamou/target_custom/g edit s/streamo/target/g edit s/streamiu/source_custom/g edit s/streami/source/g # various identifiers which also change edit s/is_a_stream/is_a_source/g edit s/find_load_stream/find_load_source/g edit s/find_save_stream/find_save_target/g edit s/new_from_stream/new_from_source/g edit s/write_to_stream/write_to_target/g edit s/vips_thumbnail_stream/vips_thumbnail_source/g # eg. vips_webpload_stream edit "s/vips_([a-z]+)load_stream/vips_\1load_source/g" # eg. vips_webpsave_stream edit "s/vips_([a-z]+)save_stream/vips_\1save_target/g" mv rename $i done ```
2019-12-29 22:40:21 +01:00
g_object_ref( source );
2011-12-19 22:40:08 +01:00
g_signal_connect( out, "close",
G_CALLBACK( read_close_cb ), read );
g_signal_connect( out, "minimise",
G_CALLBACK( read_minimise_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 );
2020-02-07 18:53:42 +01:00
/* Prevent libpng (>=1.6.11) verifying sRGB profiles. Many PNGs have
* broken profiles, but we still want to be able to open them.
*/
2020-02-07 18:53:42 +01:00
#ifdef PNG_SKIP_sRGB_CHECK_PROFILE
png_set_option( read->pPng,
PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON );
#endif /*PNG_SKIP_sRGB_CHECK_PROFILE*/
/* Don't verify ADLER32 checksums (this can produce a lot of
* warnings).
*/
#ifdef PNG_IGNORE_ADLER32
png_set_option( read->pPng, PNG_IGNORE_ADLER32, PNG_OPTION_ON );
#endif /*PNG_IGNORE_ADLER32*/
2020-02-07 18:53:42 +01:00
/* Disable CRC checking in fuzzing mode. Most fuzzed images will have
* bad CRCs so this check would break fuzzing.
2019-03-14 06:42:25 +01:00
*/
2020-02-07 18:53:42 +01:00
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
2019-03-14 06:42:25 +01:00
png_set_crc_action( read->pPng,
PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE );
#endif /*FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION*/
/* libpng has a default soft limit of 1m pixels per axis.
*/
png_set_user_limits( read->pPng, VIPS_MAX_COORD, VIPS_MAX_COORD );
experiment with renaming stream rename as VipsConnection, VipsSource, VipsTarget etc. see https://github.com/libvips/libvips/issues/1494#issuecomment-569498619 renamed with this script: ``` set -e edit() { sed -i -E "$1" rename } for i in $*; do cp $i rename edit s/VIPS_STREAMOU/VIPS_TARGET_CUSTOM/g edit s/VIPS_STREAMO/VIPS_TARGET/g edit s/VIPS_STREAMIU/VIPS_SOURCE_CUSTOM/g edit s/VIPS_STREAMI/VIPS_SOURCE/g edit s/VIPS_STREAM/VIPS_CONNECTION/g edit s/vips_streamou/vips_target_custom/g edit s/vips_streamo/vips_target/g edit s/vips_streamiu/vips_source_custom/g edit s/vips_streami/vips_source/g edit s/vips_stream/vips_connection/g edit s/VipsStreamou/VipsTargetCustom/g edit s/VipsStreamo/VipsTarget/g edit s/VipsStreamiu/VipsSourceCustom/g edit s/VipsStreami/VipsSource/g edit s/VipsStream/VipsConnection/g # eg. VIPS_TYPE_STREAM or VIPS_IS_STREAM edit "s/VIPS_([A-Z]+)_STREAMOU/VIPS_\1_TARGET_CUSTOM/g" edit "s/VIPS_([A-Z]+)_STREAMO/VIPS_\1_TARGET/g" edit "s/VIPS_([A-Z]+)_STREAMIU/VIPS_\1_SOURCE_CUSTOM/g" edit "s/VIPS_([A-Z]+)_STREAMI/VIPS_\1_SOURCE/g" edit "s/VIPS_([A-Z]+)_STREAM/VIPS_\1_CONNECTION/g" edit s/streamou/target_custom/g edit s/streamo/target/g edit s/streamiu/source_custom/g edit s/streami/source/g # various identifiers which also change edit s/is_a_stream/is_a_source/g edit s/find_load_stream/find_load_source/g edit s/find_save_stream/find_save_target/g edit s/new_from_stream/new_from_source/g edit s/write_to_stream/write_to_target/g edit s/vips_thumbnail_stream/vips_thumbnail_source/g # eg. vips_webpload_stream edit "s/vips_([a-z]+)load_stream/vips_\1load_source/g" # eg. vips_webpsave_stream edit "s/vips_([a-z]+)save_stream/vips_\1save_target/g" mv rename $i done ```
2019-12-29 22:40:21 +01:00
if( vips_source_rewind( source ) )
2019-10-15 09:33:48 +02:00
return( NULL );
png_set_read_fn( read->pPng, read, vips_png_read_source );
2019-10-14 19:03:45 +02:00
/* Catch PNG errors from png_read_info() etc.
2011-12-19 22:40:08 +01:00
*/
2019-10-15 09:33:48 +02:00
if( setjmp( png_jmpbuf( read->pPng ) ) )
2011-12-19 22:40:08 +01:00
return( NULL );
if( !(read->pInfo = png_create_info_struct( read->pPng )) )
return( NULL );
#ifdef HAVE_PNG_SET_CHUNK_MALLOC_MAX
/* By default, libpng refuses to open files with a metadata chunk
* larger than 8mb. We've seen real files with 20mb, so set 50mb.
*/
png_set_chunk_malloc_max( read->pPng, 50 * 1024 * 1024 );
/* This limits the number of chunks. The limit from
* png_set_chunk_malloc_max() times this value is the maximum
* memory use.
*
* libnpng defaults to 1000, which is rather high.
*/
png_set_chunk_cache_max( read->pPng, 100 );
#endif /*HAVE_PNG_SET_CHUNK_MALLOC_MAX*/
2019-10-15 09:33:48 +02:00
png_read_info( read->pPng, read->pInfo );
2011-12-19 22:40:08 +01:00
return( read );
}
/* Set the png text data as metadata on the vips image. These are always
* null-terminated strings.
2018-10-06 12:54:27 +02:00
*/
static int
vips__set_text( VipsImage *out, int i, const char *key, const char *text )
{
char name[256];
2018-11-23 18:20:05 +01:00
if( strcmp( key, "XML:com.adobe.xmp" ) == 0 ) {
/* Save as an XMP tag. This must be a BLOB, for compatibility
* for things like the XMP blob that the tiff loader adds.
*
* Note that this will remove the null-termination from the
* string. We must carefully reattach this.
2018-10-06 12:54:27 +02:00
*/
2018-11-23 18:20:05 +01:00
vips_image_set_blob_copy( out,
VIPS_META_XMP_NAME, text, strlen( text ) );
}
else {
/* Save as a string comment. Some PNGs have EXIF data as
2018-10-06 12:54:27 +02:00
* text segments, but the correct way to support this is with
* png_get_eXIf_1().
*/
vips_snprintf( name, 256, "png-comment-%d-%s", i, key );
2018-11-23 18:20:05 +01:00
vips_image_set_string( out, name, text );
}
2018-10-06 12:54:27 +02:00
return( 0 );
}
/* Read a png header.
2011-12-19 22:40:08 +01:00
*/
static int
png2vips_header( Read *read, VipsImage *out )
2011-12-19 22:40:08 +01:00
{
png_uint_32 width, height;
int bitdepth, color_type;
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
2018-10-06 12:54:27 +02:00
png_textp text_ptr;
int num_text;
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, &bitdepth, &color_type,
&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( bitdepth > 8 ) {
2011-12-19 22:40:08 +01:00
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;
}
/* 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 13:53:38 +01:00
/* Expand transparency.
*/
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;
}
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;
}
2011-12-19 22:40:08 +01:00
/* Expand <8 bit images to full bytes.
*/
if( color_type == PNG_COLOR_TYPE_GRAY &&
bitdepth < 8 )
2011-12-19 22:40:08 +01:00
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.
*/
if( bitdepth > 8 &&
!vips_amiMSBfirst() )
2011-12-19 22:40:08 +01:00
png_set_swap( read->pPng );
/* 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;
2020-07-15 11:56:34 +02:00
res_x = 72.0 / 2.54 * 100.0;
res_y = 72.0 / 2.54 * 100.0;
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.
*/
vips_image_init_fields( out,
2011-12-19 22:40:08 +01:00
width, height, bands,
bitdepth > 8 ? VIPS_FORMAT_USHORT : VIPS_FORMAT_UCHAR,
2011-12-19 22:40:08 +01:00
VIPS_CODING_NONE, interpretation,
Xres, Yres );
2019-10-28 06:04:04 +01:00
VIPS_SETSTR( out->filename,
experiment with renaming stream rename as VipsConnection, VipsSource, VipsTarget etc. see https://github.com/libvips/libvips/issues/1494#issuecomment-569498619 renamed with this script: ``` set -e edit() { sed -i -E "$1" rename } for i in $*; do cp $i rename edit s/VIPS_STREAMOU/VIPS_TARGET_CUSTOM/g edit s/VIPS_STREAMO/VIPS_TARGET/g edit s/VIPS_STREAMIU/VIPS_SOURCE_CUSTOM/g edit s/VIPS_STREAMI/VIPS_SOURCE/g edit s/VIPS_STREAM/VIPS_CONNECTION/g edit s/vips_streamou/vips_target_custom/g edit s/vips_streamo/vips_target/g edit s/vips_streamiu/vips_source_custom/g edit s/vips_streami/vips_source/g edit s/vips_stream/vips_connection/g edit s/VipsStreamou/VipsTargetCustom/g edit s/VipsStreamo/VipsTarget/g edit s/VipsStreamiu/VipsSourceCustom/g edit s/VipsStreami/VipsSource/g edit s/VipsStream/VipsConnection/g # eg. VIPS_TYPE_STREAM or VIPS_IS_STREAM edit "s/VIPS_([A-Z]+)_STREAMOU/VIPS_\1_TARGET_CUSTOM/g" edit "s/VIPS_([A-Z]+)_STREAMO/VIPS_\1_TARGET/g" edit "s/VIPS_([A-Z]+)_STREAMIU/VIPS_\1_SOURCE_CUSTOM/g" edit "s/VIPS_([A-Z]+)_STREAMI/VIPS_\1_SOURCE/g" edit "s/VIPS_([A-Z]+)_STREAM/VIPS_\1_CONNECTION/g" edit s/streamou/target_custom/g edit s/streamo/target/g edit s/streamiu/source_custom/g edit s/streami/source/g # various identifiers which also change edit s/is_a_stream/is_a_source/g edit s/find_load_stream/find_load_source/g edit s/find_save_stream/find_save_target/g edit s/new_from_stream/new_from_source/g edit s/write_to_stream/write_to_target/g edit s/vips_thumbnail_stream/vips_thumbnail_source/g # eg. vips_webpload_stream edit "s/vips_([a-z]+)load_stream/vips_\1load_source/g" # eg. vips_webpsave_stream edit "s/vips_([a-z]+)save_stream/vips_\1save_target/g" mv rename $i done ```
2019-12-29 22:40:21 +01:00
vips_connection_filename( VIPS_CONNECTION( read->source ) ) );
2019-10-14 19:03:45 +02:00
if( vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ) )
return( -1 );
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
* "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 ) ) {
#ifdef DEBUG
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*/
vips_image_set_blob_copy( out,
VIPS_META_ICC_NAME, profile, proflen );
2012-03-13 15:22:13 +01:00
}
/* Some libpng warn you to call png_set_interlace_handling(); here, but
* that can actually break interlace on older libpngs.
*
* Only set this for libpng 1.6+.
*/
#if PNG_LIBPNG_VER > 10600
(void) png_set_interlace_handling( read->pPng );
#endif
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 );
}
/* Let our caller know. These are very expensive to decode.
*/
if( interlace_type != PNG_INTERLACE_NONE )
vips_image_set_int( out, "interlaced", 1 );
2018-10-06 12:54:27 +02:00
if( png_get_text( read->pPng, read->pInfo,
&text_ptr, &num_text ) > 0 ) {
int i;
/* Very large numbers of text chunks are used in DoS
* attacks.
*/
2021-08-29 20:08:51 +02:00
if( !read->unlimited &&
num_text > MAX_PNG_TEXT_CHUNKS ) {
g_warning(_( "%d text chunks, only %d text chunks will be loaded" ),
num_text, MAX_PNG_TEXT_CHUNKS );
num_text = MAX_PNG_TEXT_CHUNKS;
}
2018-10-06 12:54:27 +02:00
for( i = 0; i < num_text; i++ )
/* .text is always a null-terminated C string.
*/
if( vips__set_text( out, i,
text_ptr[i].key, text_ptr[i].text ) )
return( -1 );
}
/* Attach original palette bit depth, if any, as metadata.
*/
if( color_type == PNG_COLOR_TYPE_PALETTE )
vips_image_set_int( out, "palette-bit-depth", bitdepth );
/* Note the PNG background colour, if any.
*/
#ifdef PNG_bKGD_SUPPORTED
{
png_color_16 *background;
if( png_get_bKGD( read->pPng, read->pInfo, &background ) ) {
const int scale = out->BandFmt == VIPS_FORMAT_UCHAR ? 1 : 256;
double array[3];
int n;
switch( color_type ) {
case PNG_COLOR_TYPE_GRAY:
case PNG_COLOR_TYPE_GRAY_ALPHA:
array[0] = background->gray / scale;
n = 1;
break;
case PNG_COLOR_TYPE_RGB:
case PNG_COLOR_TYPE_RGB_ALPHA:
array[0] = background->red / scale;
array[1] = background->green / scale;
array[2] = background->blue / scale;
n = 3;
break;
case PNG_COLOR_TYPE_PALETTE:
default:
/* Not sure what to do here. I suppose we should read
* the palette.
*/
n = 0;
break;
}
2021-02-20 05:52:02 +01:00
if( n > 0 )
vips_image_set_array_double( out, "background",
array, n );
}
}
#endif /*PNG_bKGD_SUPPORTED*/
#ifdef PNG_eXIf_SUPPORTED
{
png_uint_32 num_exif;
png_bytep exif;
if( png_get_eXIf_1( read->pPng, read->pInfo, &num_exif, &exif ) )
vips_image_set_blob_copy( out, VIPS_META_EXIF_NAME,
exif, num_exif );
}
#endif /*PNG_eXIf_SUPPORTED*/
2011-12-19 22:40:08 +01:00
return( 0 );
}
2012-02-15 18:19:42 +01:00
/* Out is a huge "t" buffer we decompress to.
*/
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 ) )
return( -1 );
if( setjmp( png_jmpbuf( read->pPng ) ) )
return( -1 );
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 );
png_read_image( read->pPng, read->row_pointer );
read_destroy( read );
return( 0 );
}
static int
png2vips_generate( VipsRegion *or,
void *seq, void *a, void *b, gboolean *stop )
{
VipsRect *r = &or->valid;
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*/
/* 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 );
/* Tiles should always be a strip in height, unless it's the final
* strip.
*/
g_assert( r->height == VIPS_MIN( VIPS__FATSTRIP_HEIGHT,
2019-10-14 19:03:45 +02:00
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
* a vips_sequential().
2013-01-10 15:46:20 +01: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
for( y = 0; y < r->height; y++ ) {
png_bytep q = (png_bytep) VIPS_REGION_ADDR( or, 0, r->top + y );
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-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().
*/
if( read->fail_on >= VIPS_FAIL_ON_TRUNCATED ) {
2017-05-03 11:32:24 +02:00
vips_error( "vipspng",
"%s", _( "libpng read error" ) );
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;
}
return( 0 );
}
static int
png2vips_image( Read *read, VipsImage *out )
2011-12-19 22:40:08 +01: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 );
if( interlace_type != PNG_INTERLACE_NONE ) {
/* 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
*/
t[0] = vips_image_new_memory();
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 ) )
return( -1 );
}
else {
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 ) ||
vips_sequential( t[0], &t[1],
"tile_height", VIPS__FATSTRIP_HEIGHT,
NULL ) ||
2012-08-21 17:18:25 +02:00
vips_image_write( t[1], out ) )
return( -1 );
}
2011-12-19 22:40:08 +01:00
return( 0 );
}
gboolean
vips__png_ispng_source( VipsSource *source )
2014-04-22 17:27:43 +02:00
{
2019-10-14 19:03:45 +02:00
const unsigned char *p;
experiment with renaming stream rename as VipsConnection, VipsSource, VipsTarget etc. see https://github.com/libvips/libvips/issues/1494#issuecomment-569498619 renamed with this script: ``` set -e edit() { sed -i -E "$1" rename } for i in $*; do cp $i rename edit s/VIPS_STREAMOU/VIPS_TARGET_CUSTOM/g edit s/VIPS_STREAMO/VIPS_TARGET/g edit s/VIPS_STREAMIU/VIPS_SOURCE_CUSTOM/g edit s/VIPS_STREAMI/VIPS_SOURCE/g edit s/VIPS_STREAM/VIPS_CONNECTION/g edit s/vips_streamou/vips_target_custom/g edit s/vips_streamo/vips_target/g edit s/vips_streamiu/vips_source_custom/g edit s/vips_streami/vips_source/g edit s/vips_stream/vips_connection/g edit s/VipsStreamou/VipsTargetCustom/g edit s/VipsStreamo/VipsTarget/g edit s/VipsStreamiu/VipsSourceCustom/g edit s/VipsStreami/VipsSource/g edit s/VipsStream/VipsConnection/g # eg. VIPS_TYPE_STREAM or VIPS_IS_STREAM edit "s/VIPS_([A-Z]+)_STREAMOU/VIPS_\1_TARGET_CUSTOM/g" edit "s/VIPS_([A-Z]+)_STREAMO/VIPS_\1_TARGET/g" edit "s/VIPS_([A-Z]+)_STREAMIU/VIPS_\1_SOURCE_CUSTOM/g" edit "s/VIPS_([A-Z]+)_STREAMI/VIPS_\1_SOURCE/g" edit "s/VIPS_([A-Z]+)_STREAM/VIPS_\1_CONNECTION/g" edit s/streamou/target_custom/g edit s/streamo/target/g edit s/streamiu/source_custom/g edit s/streami/source/g # various identifiers which also change edit s/is_a_stream/is_a_source/g edit s/find_load_stream/find_load_source/g edit s/find_save_stream/find_save_target/g edit s/new_from_stream/new_from_source/g edit s/write_to_stream/write_to_target/g edit s/vips_thumbnail_stream/vips_thumbnail_source/g # eg. vips_webpload_stream edit "s/vips_([a-z]+)load_stream/vips_\1load_source/g" # eg. vips_webpsave_stream edit "s/vips_([a-z]+)save_stream/vips_\1save_target/g" mv rename $i done ```
2019-12-29 22:40:21 +01:00
if( (p = vips_source_sniff( source, 8 )) &&
2019-10-14 19:03:45 +02:00
!png_sig_cmp( (png_bytep) p, 0, 8 ) )
2014-04-22 17:27:43 +02:00
return( TRUE );
return( FALSE );
}
2011-12-19 22:40:08 +01:00
int
2021-08-29 20:08:51 +02:00
vips__png_header_source( VipsSource *source, VipsImage *out,
gboolean unlimited )
{
Read *read;
if( !(read = read_new( source, out, TRUE, unlimited )) ||
2022-01-09 13:20:25 +01:00
png2vips_header( read, out ) )
return( -1 );
experiment with renaming stream rename as VipsConnection, VipsSource, VipsTarget etc. see https://github.com/libvips/libvips/issues/1494#issuecomment-569498619 renamed with this script: ``` set -e edit() { sed -i -E "$1" rename } for i in $*; do cp $i rename edit s/VIPS_STREAMOU/VIPS_TARGET_CUSTOM/g edit s/VIPS_STREAMO/VIPS_TARGET/g edit s/VIPS_STREAMIU/VIPS_SOURCE_CUSTOM/g edit s/VIPS_STREAMI/VIPS_SOURCE/g edit s/VIPS_STREAM/VIPS_CONNECTION/g edit s/vips_streamou/vips_target_custom/g edit s/vips_streamo/vips_target/g edit s/vips_streamiu/vips_source_custom/g edit s/vips_streami/vips_source/g edit s/vips_stream/vips_connection/g edit s/VipsStreamou/VipsTargetCustom/g edit s/VipsStreamo/VipsTarget/g edit s/VipsStreamiu/VipsSourceCustom/g edit s/VipsStreami/VipsSource/g edit s/VipsStream/VipsConnection/g # eg. VIPS_TYPE_STREAM or VIPS_IS_STREAM edit "s/VIPS_([A-Z]+)_STREAMOU/VIPS_\1_TARGET_CUSTOM/g" edit "s/VIPS_([A-Z]+)_STREAMO/VIPS_\1_TARGET/g" edit "s/VIPS_([A-Z]+)_STREAMIU/VIPS_\1_SOURCE_CUSTOM/g" edit "s/VIPS_([A-Z]+)_STREAMI/VIPS_\1_SOURCE/g" edit "s/VIPS_([A-Z]+)_STREAM/VIPS_\1_CONNECTION/g" edit s/streamou/target_custom/g edit s/streamo/target/g edit s/streamiu/source_custom/g edit s/streami/source/g # various identifiers which also change edit s/is_a_stream/is_a_source/g edit s/find_load_stream/find_load_source/g edit s/find_save_stream/find_save_target/g edit s/new_from_stream/new_from_source/g edit s/write_to_stream/write_to_target/g edit s/vips_thumbnail_stream/vips_thumbnail_source/g # eg. vips_webpload_stream edit "s/vips_([a-z]+)load_stream/vips_\1load_source/g" # eg. vips_webpsave_stream edit "s/vips_([a-z]+)save_stream/vips_\1save_target/g" mv rename $i done ```
2019-12-29 22:40:21 +01:00
vips_source_minimise( source );
return( 0 );
}
int
2021-08-29 20:08:51 +02:00
vips__png_read_source( VipsSource *source, VipsImage *out,
VipsFailOn fail_on, gboolean unlimited )
{
Read *read;
if( !(read = read_new( source, out, fail_on, unlimited )) ||
2019-10-28 06:04:04 +01:00
png2vips_image( read, out ) ||
2022-01-09 13:20:25 +01:00
vips_source_decode( source ) )
2019-10-28 06:04:04 +01:00
return( -1 );
return( 0 );
}
/* 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_source( VipsSource *source )
{
VipsImage *image;
Read *read;
int interlace_type;
image = vips_image_new();
if( !(read = read_new( source, image, TRUE, FALSE )) ) {
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
/* What we track during a PNG write.
*/
typedef struct {
VipsImage *in;
VipsImage *memory;
2011-12-19 22:40:08 +01:00
experiment with renaming stream rename as VipsConnection, VipsSource, VipsTarget etc. see https://github.com/libvips/libvips/issues/1494#issuecomment-569498619 renamed with this script: ``` set -e edit() { sed -i -E "$1" rename } for i in $*; do cp $i rename edit s/VIPS_STREAMOU/VIPS_TARGET_CUSTOM/g edit s/VIPS_STREAMO/VIPS_TARGET/g edit s/VIPS_STREAMIU/VIPS_SOURCE_CUSTOM/g edit s/VIPS_STREAMI/VIPS_SOURCE/g edit s/VIPS_STREAM/VIPS_CONNECTION/g edit s/vips_streamou/vips_target_custom/g edit s/vips_streamo/vips_target/g edit s/vips_streamiu/vips_source_custom/g edit s/vips_streami/vips_source/g edit s/vips_stream/vips_connection/g edit s/VipsStreamou/VipsTargetCustom/g edit s/VipsStreamo/VipsTarget/g edit s/VipsStreamiu/VipsSourceCustom/g edit s/VipsStreami/VipsSource/g edit s/VipsStream/VipsConnection/g # eg. VIPS_TYPE_STREAM or VIPS_IS_STREAM edit "s/VIPS_([A-Z]+)_STREAMOU/VIPS_\1_TARGET_CUSTOM/g" edit "s/VIPS_([A-Z]+)_STREAMO/VIPS_\1_TARGET/g" edit "s/VIPS_([A-Z]+)_STREAMIU/VIPS_\1_SOURCE_CUSTOM/g" edit "s/VIPS_([A-Z]+)_STREAMI/VIPS_\1_SOURCE/g" edit "s/VIPS_([A-Z]+)_STREAM/VIPS_\1_CONNECTION/g" edit s/streamou/target_custom/g edit s/streamo/target/g edit s/streamiu/source_custom/g edit s/streami/source/g # various identifiers which also change edit s/is_a_stream/is_a_source/g edit s/find_load_stream/find_load_source/g edit s/find_save_stream/find_save_target/g edit s/new_from_stream/new_from_source/g edit s/write_to_stream/write_to_target/g edit s/vips_thumbnail_stream/vips_thumbnail_source/g # eg. vips_webpload_stream edit "s/vips_([a-z]+)load_stream/vips_\1load_source/g" # eg. vips_webpsave_stream edit "s/vips_([a-z]+)save_stream/vips_\1save_target/g" mv rename $i done ```
2019-12-29 22:40:21 +01:00
VipsTarget *target;
2011-12-19 22:40:08 +01:00
png_structp pPng;
png_infop pInfo;
png_bytep *row_pointer;
} Write;
static void
2021-02-07 16:23:46 +01:00
write_destroy( Write *write )
2011-12-19 22:40:08 +01:00
{
2021-02-07 16:23:46 +01:00
#ifdef DEBUG
printf( "write_destroy: %p\n", write );
#endif /*DEBUG*/
VIPS_UNREF( write->memory );
2011-12-19 22:40:08 +01:00
if( write->pPng )
png_destroy_write_struct( &write->pPng, &write->pInfo );
VIPS_FREE( write->row_pointer );
VIPS_FREE( write );
2012-02-14 11:53:39 +01:00
}
static void
user_write_data( png_structp pPng, png_bytep data, png_size_t length )
{
Write *write = (Write *) png_get_io_ptr( pPng );
experiment with renaming stream rename as VipsConnection, VipsSource, VipsTarget etc. see https://github.com/libvips/libvips/issues/1494#issuecomment-569498619 renamed with this script: ``` set -e edit() { sed -i -E "$1" rename } for i in $*; do cp $i rename edit s/VIPS_STREAMOU/VIPS_TARGET_CUSTOM/g edit s/VIPS_STREAMO/VIPS_TARGET/g edit s/VIPS_STREAMIU/VIPS_SOURCE_CUSTOM/g edit s/VIPS_STREAMI/VIPS_SOURCE/g edit s/VIPS_STREAM/VIPS_CONNECTION/g edit s/vips_streamou/vips_target_custom/g edit s/vips_streamo/vips_target/g edit s/vips_streamiu/vips_source_custom/g edit s/vips_streami/vips_source/g edit s/vips_stream/vips_connection/g edit s/VipsStreamou/VipsTargetCustom/g edit s/VipsStreamo/VipsTarget/g edit s/VipsStreamiu/VipsSourceCustom/g edit s/VipsStreami/VipsSource/g edit s/VipsStream/VipsConnection/g # eg. VIPS_TYPE_STREAM or VIPS_IS_STREAM edit "s/VIPS_([A-Z]+)_STREAMOU/VIPS_\1_TARGET_CUSTOM/g" edit "s/VIPS_([A-Z]+)_STREAMO/VIPS_\1_TARGET/g" edit "s/VIPS_([A-Z]+)_STREAMIU/VIPS_\1_SOURCE_CUSTOM/g" edit "s/VIPS_([A-Z]+)_STREAMI/VIPS_\1_SOURCE/g" edit "s/VIPS_([A-Z]+)_STREAM/VIPS_\1_CONNECTION/g" edit s/streamou/target_custom/g edit s/streamo/target/g edit s/streamiu/source_custom/g edit s/streami/source/g # various identifiers which also change edit s/is_a_stream/is_a_source/g edit s/find_load_stream/find_load_source/g edit s/find_save_stream/find_save_target/g edit s/new_from_stream/new_from_source/g edit s/write_to_stream/write_to_target/g edit s/vips_thumbnail_stream/vips_thumbnail_source/g # eg. vips_webpload_stream edit "s/vips_([a-z]+)load_stream/vips_\1load_source/g" # eg. vips_webpsave_stream edit "s/vips_([a-z]+)save_stream/vips_\1save_target/g" mv rename $i done ```
2019-12-29 22:40:21 +01:00
if( vips_target_write( write->target, data, length ) )
png_error( pPng, "not enough data" );
}
2011-12-19 22:40:08 +01:00
static Write *
experiment with renaming stream rename as VipsConnection, VipsSource, VipsTarget etc. see https://github.com/libvips/libvips/issues/1494#issuecomment-569498619 renamed with this script: ``` set -e edit() { sed -i -E "$1" rename } for i in $*; do cp $i rename edit s/VIPS_STREAMOU/VIPS_TARGET_CUSTOM/g edit s/VIPS_STREAMO/VIPS_TARGET/g edit s/VIPS_STREAMIU/VIPS_SOURCE_CUSTOM/g edit s/VIPS_STREAMI/VIPS_SOURCE/g edit s/VIPS_STREAM/VIPS_CONNECTION/g edit s/vips_streamou/vips_target_custom/g edit s/vips_streamo/vips_target/g edit s/vips_streamiu/vips_source_custom/g edit s/vips_streami/vips_source/g edit s/vips_stream/vips_connection/g edit s/VipsStreamou/VipsTargetCustom/g edit s/VipsStreamo/VipsTarget/g edit s/VipsStreamiu/VipsSourceCustom/g edit s/VipsStreami/VipsSource/g edit s/VipsStream/VipsConnection/g # eg. VIPS_TYPE_STREAM or VIPS_IS_STREAM edit "s/VIPS_([A-Z]+)_STREAMOU/VIPS_\1_TARGET_CUSTOM/g" edit "s/VIPS_([A-Z]+)_STREAMO/VIPS_\1_TARGET/g" edit "s/VIPS_([A-Z]+)_STREAMIU/VIPS_\1_SOURCE_CUSTOM/g" edit "s/VIPS_([A-Z]+)_STREAMI/VIPS_\1_SOURCE/g" edit "s/VIPS_([A-Z]+)_STREAM/VIPS_\1_CONNECTION/g" edit s/streamou/target_custom/g edit s/streamo/target/g edit s/streamiu/source_custom/g edit s/streami/source/g # various identifiers which also change edit s/is_a_stream/is_a_source/g edit s/find_load_stream/find_load_source/g edit s/find_save_stream/find_save_target/g edit s/new_from_stream/new_from_source/g edit s/write_to_stream/write_to_target/g edit s/vips_thumbnail_stream/vips_thumbnail_source/g # eg. vips_webpload_stream edit "s/vips_([a-z]+)load_stream/vips_\1load_source/g" # eg. vips_webpsave_stream edit "s/vips_([a-z]+)save_stream/vips_\1save_target/g" mv rename $i done ```
2019-12-29 22:40:21 +01:00
write_new( VipsImage *in, VipsTarget *target )
2011-12-19 22:40:08 +01:00
{
Write *write;
if( !(write = VIPS_NEW( NULL, Write )) )
2011-12-19 22:40:08 +01:00
return( NULL );
write->in = in;
experiment with renaming stream rename as VipsConnection, VipsSource, VipsTarget etc. see https://github.com/libvips/libvips/issues/1494#issuecomment-569498619 renamed with this script: ``` set -e edit() { sed -i -E "$1" rename } for i in $*; do cp $i rename edit s/VIPS_STREAMOU/VIPS_TARGET_CUSTOM/g edit s/VIPS_STREAMO/VIPS_TARGET/g edit s/VIPS_STREAMIU/VIPS_SOURCE_CUSTOM/g edit s/VIPS_STREAMI/VIPS_SOURCE/g edit s/VIPS_STREAM/VIPS_CONNECTION/g edit s/vips_streamou/vips_target_custom/g edit s/vips_streamo/vips_target/g edit s/vips_streamiu/vips_source_custom/g edit s/vips_streami/vips_source/g edit s/vips_stream/vips_connection/g edit s/VipsStreamou/VipsTargetCustom/g edit s/VipsStreamo/VipsTarget/g edit s/VipsStreamiu/VipsSourceCustom/g edit s/VipsStreami/VipsSource/g edit s/VipsStream/VipsConnection/g # eg. VIPS_TYPE_STREAM or VIPS_IS_STREAM edit "s/VIPS_([A-Z]+)_STREAMOU/VIPS_\1_TARGET_CUSTOM/g" edit "s/VIPS_([A-Z]+)_STREAMO/VIPS_\1_TARGET/g" edit "s/VIPS_([A-Z]+)_STREAMIU/VIPS_\1_SOURCE_CUSTOM/g" edit "s/VIPS_([A-Z]+)_STREAMI/VIPS_\1_SOURCE/g" edit "s/VIPS_([A-Z]+)_STREAM/VIPS_\1_CONNECTION/g" edit s/streamou/target_custom/g edit s/streamo/target/g edit s/streamiu/source_custom/g edit s/streami/source/g # various identifiers which also change edit s/is_a_stream/is_a_source/g edit s/find_load_stream/find_load_source/g edit s/find_save_stream/find_save_target/g edit s/new_from_stream/new_from_source/g edit s/write_to_stream/write_to_target/g edit s/vips_thumbnail_stream/vips_thumbnail_source/g # eg. vips_webpload_stream edit "s/vips_([a-z]+)load_stream/vips_\1load_source/g" # eg. vips_webpsave_stream edit "s/vips_([a-z]+)save_stream/vips_\1save_target/g" mv rename $i done ```
2019-12-29 22:40:21 +01:00
write->target = target;
2021-02-07 16:23:46 +01:00
#ifdef DEBUG
printf( "write_new: %p\n", write );
#endif /*DEBUG*/
2011-12-19 22:40:08 +01:00
if( !(write->row_pointer = VIPS_ARRAY( NULL, in->Ysize, png_bytep )) )
2011-12-19 22:40:08 +01:00
return( NULL );
if( !(write->pPng = png_create_write_struct(
PNG_LIBPNG_VER_STRING, NULL,
user_error_function, user_warning_function )) ) {
2021-02-07 16:23:46 +01:00
write_destroy( write );
2011-12-19 22:40:08 +01:00
return( NULL );
}
2011-12-19 22:40:08 +01:00
2020-02-07 18:53:42 +01:00
/* Prevent libpng (>=1.6.11) verifying sRGB profiles. We are often
* asked to copy images containing bad profiles, and this check would
* prevent that.
*/
2020-02-07 18:53:42 +01:00
#ifdef PNG_SKIP_sRGB_CHECK_PROFILE
png_set_option( write->pPng,
PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON );
#endif /*PNG_SKIP_sRGB_CHECK_PROFILE*/
png_set_write_fn( write->pPng, write, user_write_data, NULL );
2011-12-19 22:40:08 +01:00
/* Catch PNG errors from png_create_info_struct().
*/
if( setjmp( png_jmpbuf( write->pPng ) ) ) {
2021-02-07 16:23:46 +01:00
write_destroy( write );
2011-12-19 22:40:08 +01:00
return( NULL );
}
2011-12-19 22:40:08 +01:00
if( !(write->pInfo = png_create_info_struct( write->pPng )) ) {
2021-02-07 16:23:46 +01:00
write_destroy( write );
2011-12-19 22:40:08 +01:00
return( NULL );
}
2011-12-19 22:40:08 +01:00
return( write );
}
static int
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 );
2019-10-15 09:33:48 +02:00
/* Catch PNG errors.
2011-12-19 22:40:08 +01:00
*/
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-10-06 12:54:27 +02:00
static void
vips__png_set_text( png_structp pPng, png_infop pInfo,
const char *key, const char *value )
{
png_text text;
text.compression = 0;
text.key = (char *) key;
text.text = (char *) value;
text.text_length = strlen( value );
/* Before 1.4, these fields were only there if explicitly enabled.
*/
#if PNG_LIBPNG_VER > 10400
text.itxt_length = 0;
text.lang = NULL;
#endif
png_set_text( pPng, pInfo, &text, 1 );
}
static void *
write_png_comment( VipsImage *image,
const char *field, GValue *value, void *data )
{
Write *write = (Write *) data;
if( vips_isprefix( "png-comment-", field ) ) {
const char *str;
int i;
char key[256];
2018-10-06 12:54:27 +02:00
if( vips_image_get_string( write->in, field, &str ) )
return( image );
if( strlen( field ) > 256 ||
sscanf( field, "png-comment-%d-%80s", &i, key ) != 2 ) {
2018-10-06 12:54:27 +02:00
vips_error( "vips2png",
"%s", _( "bad png comment key" ) );
return( image );
}
vips__png_set_text( write->pPng, write->pInfo, key, str );
}
return( NULL );
}
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,
VipsForeignPngFilter filter, gboolean strip,
gboolean palette, int Q, double dither,
int bitdepth, int effort )
2011-12-19 22:40:08 +01:00
{
VipsImage *in = write->in;
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 );
/* 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
*/
if( interlace ) {
if( !(write->memory = vips_image_copy_memory( in )) )
return( -1 );
in = write->memory;
}
else {
if( vips_image_pio_input( in ) )
return( -1 );
}
if( compress < 0 ||
compress > 9 ) {
2011-12-19 22:40:08 +01:00
vips_error( "vips2png",
"%s", _( "compress should be in [0,9]" ) );
return( -1 );
}
/* Set compression parameters.
*/
png_set_compression_level( write->pPng, compress );
/* Set row filter.
*/
png_set_filter( write->pPng, 0, filter );
2011-12-19 22:40:08 +01:00
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
}
2021-11-21 11:11:28 +01:00
#ifdef HAVE_QUANTIZATION
/* Enable image quantisation to paletted 8bpp PNG if palette is set.
*/
if( palette )
color_type = PNG_COLOR_TYPE_PALETTE;
#else
if( palette )
g_warning( "%s",
_( "ignoring palette (no quantisation support)" ) );
2021-11-21 11:11:28 +01:00
#endif /*HAVE_QUANTIZATION*/
2011-12-19 22:40:08 +01:00
interlace_type = interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE;
/* libpng has a default soft limit of 1m pixels per axis.
*/
png_set_user_limits( write->pPng, VIPS_MAX_COORD, VIPS_MAX_COORD );
2011-12-19 22:40:08 +01:00
png_set_IHDR( write->pPng, write->pInfo,
in->Xsize, in->Ysize, bitdepth, color_type, interlace_type,
2011-12-19 22:40:08 +01:00
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT );
/* 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 );
/* Metadata
2012-03-13 15:22:13 +01:00
*/
if( !strip ) {
if( profile ) {
VipsBlob *blob;
if( vips_profile_load( profile, &blob, NULL ) )
return( -1 );
if( blob ) {
size_t length;
const void *data
= vips_blob_get( blob, &length );
#ifdef DEBUG
printf( "write_vips: attaching %zd bytes "
"of ICC profile\n", length );
#endif /*DEBUG*/
png_set_iCCP( write->pPng, write->pInfo,
"icc", PNG_COMPRESSION_TYPE_BASE,
(void *) data, length );
vips_area_unref( (VipsArea *) blob );
}
}
else if( vips_image_get_typeof( in, VIPS_META_ICC_NAME ) ) {
const void *data;
size_t length;
if( vips_image_get_blob( in, VIPS_META_ICC_NAME,
&data, &length ) )
return( -1 );
2012-03-13 15:22:13 +01:00
#ifdef DEBUG
printf( "write_vips: attaching %zd bytes "
"of ICC profile\n", length );
2012-03-13 15:22:13 +01:00
#endif /*DEBUG*/
/* We need to ignore any errors from png_set_iCCP()
* since we want to drop incompatible profiles rather
* than simply failing.
*/
if( setjmp( png_jmpbuf( write->pPng ) ) ) {
/* Silent ignore of error.
*/
g_warning( "bad ICC profile not saved" );
}
else {
/* This will jump back to the line above on
* error.
*/
png_set_iCCP( write->pPng, write->pInfo, "icc",
PNG_COMPRESSION_TYPE_BASE,
(void *) data, length );
}
2012-03-13 15:22:13 +01:00
/* And restore the setjmp.
*/
if( setjmp( png_jmpbuf( write->pPng ) ) )
return( -1 );
}
2018-10-06 12:54:27 +02:00
if( vips_image_get_typeof( in, VIPS_META_XMP_NAME ) ) {
const void *data;
size_t length;
char *str;
2018-11-23 18:20:05 +01:00
/* XMP is attached as a BLOB with no null-termination.
* We must re-add this.
*/
if( vips_image_get_blob( in,
VIPS_META_XMP_NAME, &data, &length ) )
return( -1 );
2018-10-06 12:54:27 +02:00
str = g_malloc( length + 1 );
vips_strncpy( str, data, length + 1 );
vips__png_set_text( write->pPng, write->pInfo,
"XML:com.adobe.xmp", str );
g_free( str );
}
2018-10-06 12:54:27 +02:00
#ifdef PNG_eXIf_SUPPORTED
if( vips_image_get_typeof( in, VIPS_META_EXIF_NAME ) ) {
const void *data;
size_t length;
if( vips__exif_update( in ) ||
vips_image_get_blob( in, VIPS_META_EXIF_NAME,
&data, &length ) )
return( -1 );
/* libpng does not want the JFIF "Exif\0\0" prefix.
*/
if( length >= 6 &&
vips_isprefix( "Exif", (char *) data ) ) {
data += 6;
length -= 6;
}
png_set_eXIf_1( write->pPng, write->pInfo,
length, (png_bytep) data );
}
#endif /*PNG_eXIf_SUPPORTED*/
if( vips_image_map( in, write_png_comment, write ) )
2018-10-06 12:54:27 +02:00
return( -1 );
}
2021-11-21 11:11:28 +01:00
#ifdef HAVE_QUANTIZATION
if( palette ) {
VipsImage *im_index;
VipsImage *im_palette;
int palette_count;
png_color *png_palette;
png_byte *png_trans;
int trans_count;
if( vips__quantise_image( in, &im_index, &im_palette,
1 << bitdepth, Q, dither, effort, FALSE ) )
return( -1 );
palette_count = im_palette->Xsize;
g_assert( palette_count <= PNG_MAX_PALETTE_LENGTH );
png_palette = (png_color *) png_malloc( write->pPng,
palette_count * sizeof( png_color ) );
png_trans = (png_byte *) png_malloc( write->pPng,
palette_count * sizeof( png_byte ) );
trans_count = 0;
for( i = 0; i < palette_count; i++ ) {
VipsPel *p = (VipsPel *)
VIPS_IMAGE_ADDR( im_palette, i, 0 );
png_color *col = &png_palette[i];
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 );
}
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_index;
in = write->memory;
}
2021-11-21 11:11:28 +01:00
#endif /*HAVE_QUANTIZATION*/
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.
*/
if( bitdepth > 8 &&
!vips_amiMSBfirst() )
2011-12-19 22:40:08 +01:00
png_set_swap( write->pPng );
/* If bitdepth is 1/2/4, pack pixels into bytes.
*/
png_set_packing( write->pPng );
2011-12-19 22:40:08 +01:00
if( interlace )
nb_passes = png_set_interlace_handling( write->pPng );
else
nb_passes = 1;
/* Write data.
*/
for( i = 0; i < nb_passes; i++ )
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_target( VipsImage *in, VipsTarget *target,
int compression, int interlace,
const char *profile, VipsForeignPngFilter filter, gboolean strip,
gboolean palette, int Q, double dither,
int bitdepth, int effort )
2011-12-19 22:40:08 +01:00
{
Write *write;
experiment with renaming stream rename as VipsConnection, VipsSource, VipsTarget etc. see https://github.com/libvips/libvips/issues/1494#issuecomment-569498619 renamed with this script: ``` set -e edit() { sed -i -E "$1" rename } for i in $*; do cp $i rename edit s/VIPS_STREAMOU/VIPS_TARGET_CUSTOM/g edit s/VIPS_STREAMO/VIPS_TARGET/g edit s/VIPS_STREAMIU/VIPS_SOURCE_CUSTOM/g edit s/VIPS_STREAMI/VIPS_SOURCE/g edit s/VIPS_STREAM/VIPS_CONNECTION/g edit s/vips_streamou/vips_target_custom/g edit s/vips_streamo/vips_target/g edit s/vips_streamiu/vips_source_custom/g edit s/vips_streami/vips_source/g edit s/vips_stream/vips_connection/g edit s/VipsStreamou/VipsTargetCustom/g edit s/VipsStreamo/VipsTarget/g edit s/VipsStreamiu/VipsSourceCustom/g edit s/VipsStreami/VipsSource/g edit s/VipsStream/VipsConnection/g # eg. VIPS_TYPE_STREAM or VIPS_IS_STREAM edit "s/VIPS_([A-Z]+)_STREAMOU/VIPS_\1_TARGET_CUSTOM/g" edit "s/VIPS_([A-Z]+)_STREAMO/VIPS_\1_TARGET/g" edit "s/VIPS_([A-Z]+)_STREAMIU/VIPS_\1_SOURCE_CUSTOM/g" edit "s/VIPS_([A-Z]+)_STREAMI/VIPS_\1_SOURCE/g" edit "s/VIPS_([A-Z]+)_STREAM/VIPS_\1_CONNECTION/g" edit s/streamou/target_custom/g edit s/streamo/target/g edit s/streamiu/source_custom/g edit s/streami/source/g # various identifiers which also change edit s/is_a_stream/is_a_source/g edit s/find_load_stream/find_load_source/g edit s/find_save_stream/find_save_target/g edit s/new_from_stream/new_from_source/g edit s/write_to_stream/write_to_target/g edit s/vips_thumbnail_stream/vips_thumbnail_source/g # eg. vips_webpload_stream edit "s/vips_([a-z]+)load_stream/vips_\1load_source/g" # eg. vips_webpsave_stream edit "s/vips_([a-z]+)save_stream/vips_\1save_target/g" mv rename $i done ```
2019-12-29 22:40:21 +01:00
if( !(write = write_new( in, target )) )
2011-12-20 19:14:41 +01:00
return( -1 );
2011-12-19 22:40:08 +01:00
2016-07-31 11:34:12 +02:00
if( write_vips( write,
compression, interlace, profile, filter, strip, palette,
Q, dither, bitdepth, effort ) ) {
2021-02-07 16:23:46 +01:00
write_destroy( write );
vips_error( "vips2png", _( "unable to write to target %s" ),
vips_connection_nick( VIPS_CONNECTION( target ) ) );
2011-12-19 22:40:08 +01:00
return( -1 );
}
2021-02-07 16:23:46 +01:00
write_destroy( write );
2011-12-19 22:40:08 +01:00
if( vips_target_end( target ) )
return( -1 );
2011-12-19 22:40:08 +01:00
return( 0 );
}
#endif /*HAVE_PNG*/