add png stream load

This commit is contained in:
John Cupitt 2019-10-14 18:03:45 +01:00
parent f6d247627f
commit 1bdadeed61
11 changed files with 327 additions and 262 deletions

View File

@ -83,14 +83,22 @@ png2vips( const char *name, IMAGE *out, gboolean header_only )
}
#ifdef HAVE_PNG
if( header_only ) {
if( vips__png_header( filename, out ) )
return( -1 );
}
else {
if( vips__png_read( filename, out, TRUE ) )
return( -1 );
}
{
VipsStreamInput *input;
int result;
if( !(input = vips_stream_input_new_from_filename( filename )) )
return( -1 );
if( header_only )
result = vips__png_header_stream( input, out );
else
result = vips__png_read_stream( input, out, TRUE );
VIPS_UNREF( input );
if( result )
return( result );
}
#else
vips_error( "im_png2vips",
"%s", _( "no PNG support in your libvips" ) );

View File

@ -1716,20 +1716,26 @@ vips_foreign_save_init( VipsForeignSave *save )
save->background = vips_array_double_newv( 1, 0.0 );
}
/* Can we write this filename with this file?
/* Can we write this filename with this class?
*/
static void *
vips_foreign_find_save_sub( VipsForeignSaveClass *save_class,
const char *filename )
{
VipsObjectClass *object_class = VIPS_OBJECT_CLASS( save_class );
VipsForeignClass *class = VIPS_FOREIGN_CLASS( save_class );
/* The suffs might be defined on an abstract base class, make sure we
* don't pick that.
*
* Suffs can be defined on buffer and stream writers too. Make sure
* it's not one of those.
*/
if( !G_TYPE_IS_ABSTRACT( G_TYPE_FROM_CLASS( class ) ) &&
class->suffs &&
vips_filename_suffix_match( filename, class->suffs ) )
vips_filename_suffix_match( filename, class->suffs ) &&
!vips_ispostfix( object_class->nickname, "_buffer" ) &&
!vips_ispostfix( object_class->nickname, "_stream" ) )
return( save_class );
return( NULL );
@ -1979,6 +1985,7 @@ vips_foreign_operation_init( void )
extern GType vips_foreign_save_ppm_get_type( void );
extern GType vips_foreign_load_png_get_type( void );
extern GType vips_foreign_load_png_buffer_get_type( void );
extern GType vips_foreign_load_png_stream_get_type( void );
extern GType vips_foreign_save_png_file_get_type( void );
extern GType vips_foreign_save_png_buffer_get_type( void );
extern GType vips_foreign_load_csv_get_type( void );
@ -2093,6 +2100,7 @@ vips_foreign_operation_init( void )
#endif /*HAVE_GSF*/
#ifdef HAVE_PNG
vips_foreign_load_png_stream_get_type();
vips_foreign_load_png_get_type();
vips_foreign_load_png_buffer_get_type();
vips_foreign_save_png_file_get_type();
@ -2104,12 +2112,12 @@ vips_foreign_operation_init( void )
#endif /*HAVE_MATIO*/
#ifdef HAVE_JPEG
vips_foreign_load_jpeg_stream_get_type();
vips_foreign_load_jpeg_file_get_type();
vips_foreign_load_jpeg_buffer_get_type();
vips_foreign_load_jpeg_stream_get_type();
vips_foreign_save_jpeg_stream_get_type();
vips_foreign_save_jpeg_file_get_type();
vips_foreign_save_jpeg_buffer_get_type();
vips_foreign_save_jpeg_stream_get_type();
vips_foreign_save_jpeg_mime_get_type();
#endif /*HAVE_JPEG*/

View File

@ -104,6 +104,8 @@
* - close input on minimise rather than Y read position
* 3/10/19
* - restart after minimise
* 14/10/19
* - revise for stream IO
*/
/*
@ -336,7 +338,7 @@ readjpeg_close_cb( VipsObject *object, ReadJpeg *jpeg )
}
static void
vips_stream_input_minimise_cb( VipsImage *image, VipsStreamInput *input )
input_minimise_cb( VipsImage *image, VipsStreamInput *input )
{
vips_stream_input_minimise( input );
}
@ -377,7 +379,7 @@ readjpeg_new( VipsStreamInput *input, VipsImage *out,
g_signal_connect( out, "close",
G_CALLBACK( readjpeg_close_cb ), jpeg );
g_signal_connect( out, "minimise",
G_CALLBACK( vips_stream_input_minimise_cb ), input );
G_CALLBACK( input_minimise_cb ), input );
return( jpeg );
}
@ -556,6 +558,9 @@ read_jpeg_header( ReadJpeg *jpeg, VipsImage *out )
interpretation,
xres, yres );
VIPS_SETSTR( out->filename,
vips_stream_filename( VIPS_STREAM( jpeg->input ) ) );
vips_image_pipelinev( out, VIPS_DEMAND_STYLE_FATSTRIP, NULL );
/* cinfo->output_width and cinfo->output_height round up with
@ -741,8 +746,7 @@ read_jpeg_generate( VipsRegion *or,
/* In pixel decode mode.
*/
if( jpeg->input )
vips_stream_input_decode( jpeg->input );
vips_stream_input_decode( jpeg->input );
VIPS_GATE_START( "read_jpeg_generate: work" );

View File

@ -343,7 +343,7 @@ G_DEFINE_TYPE( VipsForeignLoadJpegBuffer, vips_foreign_load_jpeg_buffer,
vips_foreign_load_jpeg_get_type() );
static gboolean
vips_foreign_load_jpeg_buffer_is_a( const void *buf, size_t len )
vips_foreign_load_jpeg_buffer_is_a_buffer( const void *buf, size_t len )
{
VipsStreamInput *input;
gboolean result;
@ -410,7 +410,7 @@ vips_foreign_load_jpeg_buffer_class_init(
object_class->nickname = "jpegload_buffer";
object_class->description = _( "load jpeg from buffer" );
load_class->is_a_buffer = vips_foreign_load_jpeg_buffer_is_a;
load_class->is_a_buffer = vips_foreign_load_jpeg_buffer_is_a_buffer;
load_class->header = vips_foreign_load_jpeg_buffer_header;
load_class->load = vips_foreign_load_jpeg_buffer_load;

View File

@ -183,16 +183,12 @@ int vips__jpeg_read_stream( VipsStreamInput *input, VipsImage *out,
gboolean header_only, int shrink, int fail, gboolean autorotate );
int vips__isjpeg_stream( VipsStreamInput *input );
int vips__png_header( const char *name, VipsImage *out );
int vips__png_read( const char *name, VipsImage *out, gboolean fail );
gboolean vips__png_ispng_buffer( const void *buf, size_t len );
int vips__png_ispng( const char *filename );
gboolean vips__png_isinterlaced( const char *filename );
gboolean vips__png_isinterlaced_buffer( const void *buffer, size_t length );
extern const char *vips__png_suffs[];
int vips__png_read_buffer( const void *buffer, size_t length, VipsImage *out,
int vips__png_ispng_stream( VipsStreamInput *input );
int vips__png_header_stream( VipsStreamInput *input, VipsImage *out );
int vips__png_read_stream( VipsStreamInput *input, VipsImage *out,
gboolean fail );
int vips__png_header_buffer( const void *buffer, size_t length, VipsImage *out );
gboolean vips__png_isinterlaced_stream( VipsStreamInput *input );
extern const char *vips__png_suffs[];
int vips__png_write( VipsImage *in, const char *filename,
int compress, int interlace, const char *profile,

View File

@ -52,6 +52,90 @@
#ifdef HAVE_PNG
typedef struct _VipsForeignLoadPngStream {
VipsForeignLoad parent_object;
/* Load from a stream.
*/
VipsStreamInput *input;
} VipsForeignLoadPngStream;
typedef VipsForeignLoadClass VipsForeignLoadPngStreamClass;
G_DEFINE_TYPE( VipsForeignLoadPngStream, vips_foreign_load_png_stream,
VIPS_TYPE_FOREIGN_LOAD );
static VipsForeignFlags
vips_foreign_load_png_stream_get_flags( VipsForeignLoad *load )
{
VipsForeignLoadPngStream *stream = (VipsForeignLoadPngStream *) load;
VipsForeignFlags flags;
flags = 0;
if( vips__png_isinterlaced_stream( stream->input ) )
flags |= VIPS_FOREIGN_PARTIAL;
else
flags |= VIPS_FOREIGN_SEQUENTIAL;
return( flags );
}
static int
vips_foreign_load_png_stream_header( VipsForeignLoad *load )
{
VipsForeignLoadPngStream *stream = (VipsForeignLoadPngStream *) load;
if( vips__png_header_stream( stream->input, load->out ) )
return( -1 );
return( 0 );
}
static int
vips_foreign_load_png_stream_load( VipsForeignLoad *load )
{
VipsForeignLoadPngStream *stream = (VipsForeignLoadPngStream *) load;
if( vips__png_read_stream( stream->input, load->real, load->fail ) )
return( -1 );
return( 0 );
}
static void
vips_foreign_load_png_stream_class_init( VipsForeignLoadPngStreamClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *object_class = (VipsObjectClass *) class;
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "pngload_stream";
object_class->description = _( "load png from stream" );
load_class->is_a_stream = vips__png_ispng_stream;
load_class->get_flags = vips_foreign_load_png_stream_get_flags;
load_class->header = vips_foreign_load_png_stream_header;
load_class->load = vips_foreign_load_png_stream_load;
VIPS_ARG_OBJECT( class, "input", 1,
_( "Input" ),
_( "Stream to load from" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadPngStream, input ),
VIPS_TYPE_STREAM_INPUT );
}
static void
vips_foreign_load_png_stream_init( VipsForeignLoadPngStream *stream )
{
}
typedef struct _VipsForeignLoadPng {
VipsForeignLoad parent_object;
@ -66,17 +150,37 @@ typedef VipsForeignLoadClass VipsForeignLoadPngClass;
G_DEFINE_TYPE( VipsForeignLoadPng, vips_foreign_load_png,
VIPS_TYPE_FOREIGN_LOAD );
static gboolean
vips_foreign_load_png_is_a( const char *filename )
{
VipsStreamInput *input;
gboolean result;
if( !(input = vips_stream_input_new_from_filename( filename )) )
return( FALSE );
result = vips__png_ispng_stream( input );
VIPS_UNREF( input );
return( result );
}
static VipsForeignFlags
vips_foreign_load_png_get_flags_filename( const char *filename )
{
VipsStreamInput *input;
VipsForeignFlags flags;
if( !(input = vips_stream_input_new_from_filename( filename )) )
return( 0 );
flags = 0;
if( vips__png_isinterlaced( filename ) )
if( vips__png_isinterlaced_stream( input ) )
flags |= VIPS_FOREIGN_PARTIAL;
else
flags |= VIPS_FOREIGN_SEQUENTIAL;
VIPS_UNREF( input );
return( flags );
}
@ -93,10 +197,15 @@ vips_foreign_load_png_header( VipsForeignLoad *load )
{
VipsForeignLoadPng *png = (VipsForeignLoadPng *) load;
if( vips__png_header( png->filename, load->out ) )
return( -1 );
VipsStreamInput *input;
VIPS_SETSTR( load->out->filename, png->filename );
if( !(input = vips_stream_input_new_from_filename( png->filename )) )
return( -1 );
if( vips__png_header_stream( input, load->out ) ) {
VIPS_UNREF( input );
return( -1 );
}
VIPS_UNREF( input );
return( 0 );
}
@ -106,8 +215,15 @@ vips_foreign_load_png_load( VipsForeignLoad *load )
{
VipsForeignLoadPng *png = (VipsForeignLoadPng *) load;
if( vips__png_read( png->filename, load->real, load->fail ) )
VipsStreamInput *input;
if( !(input = vips_stream_input_new_from_filename( png->filename )) )
return( -1 );
if( vips__png_read_stream( input, load->real, load->fail ) ) {
VIPS_UNREF( input );
return( -1 );
}
VIPS_UNREF( input );
return( 0 );
}
@ -132,7 +248,7 @@ vips_foreign_load_png_class_init( VipsForeignLoadPngClass *class )
*/
foreign_class->priority = 200;
load_class->is_a = vips__png_ispng;
load_class->is_a = vips_foreign_load_png_is_a;
load_class->get_flags_filename =
vips_foreign_load_png_get_flags_filename;
load_class->get_flags = vips_foreign_load_png_get_flags;
@ -166,20 +282,40 @@ typedef VipsForeignLoadClass VipsForeignLoadPngBufferClass;
G_DEFINE_TYPE( VipsForeignLoadPngBuffer, vips_foreign_load_png_buffer,
VIPS_TYPE_FOREIGN_LOAD );
static gboolean
vips_foreign_load_png_buffer_is_a_buffer( const void *buf, size_t len )
{
VipsStreamInput *input;
gboolean result;
if( !(input = vips_stream_input_new_from_memory( buf, len )) )
return( FALSE );
result = vips__png_ispng_stream( input );
VIPS_UNREF( input );
return( result );
}
static VipsForeignFlags
vips_foreign_load_png_buffer_get_flags( VipsForeignLoad *load )
{
VipsForeignLoadPngBuffer *buffer = (VipsForeignLoadPngBuffer *) load;
VipsStreamInput *input;
VipsForeignFlags flags;
if( !(input = vips_stream_input_new_from_memory( buffer->buf->data,
buffer->buf->length )) )
return( 0 );
flags = 0;
if( vips__png_isinterlaced_buffer( buffer->buf->data,
buffer->buf->length ) )
if( vips__png_isinterlaced_stream( input ) )
flags |= VIPS_FOREIGN_PARTIAL;
else
flags |= VIPS_FOREIGN_SEQUENTIAL;
VIPS_UNREF( input );
return( flags );
}
@ -188,9 +324,16 @@ vips_foreign_load_png_buffer_header( VipsForeignLoad *load )
{
VipsForeignLoadPngBuffer *buffer = (VipsForeignLoadPngBuffer *) load;
if( vips__png_header_buffer( buffer->buf->data, buffer->buf->length,
load->out ) )
VipsStreamInput *input;
if( !(input = vips_stream_input_new_from_memory( buffer->buf->data,
buffer->buf->length )) )
return( -1 );
if( vips__png_header_stream( input, load->out ) ) {
VIPS_UNREF( input );
return( -1 );
}
VIPS_UNREF( input );
return( 0 );
}
@ -200,9 +343,16 @@ vips_foreign_load_png_buffer_load( VipsForeignLoad *load )
{
VipsForeignLoadPngBuffer *buffer = (VipsForeignLoadPngBuffer *) load;
if( vips__png_read_buffer( buffer->buf->data, buffer->buf->length,
load->real, load->fail ) )
VipsStreamInput *input;
if( !(input = vips_stream_input_new_from_memory( buffer->buf->data,
buffer->buf->length )) )
return( -1 );
if( vips__png_read_stream( input, load->real, load->fail ) ) {
VIPS_UNREF( input );
return( -1 );
}
VIPS_UNREF( input );
return( 0 );
}
@ -220,7 +370,7 @@ vips_foreign_load_png_buffer_class_init( VipsForeignLoadPngBufferClass *class )
object_class->nickname = "pngload_buffer";
object_class->description = _( "load png from buffer" );
load_class->is_a_buffer = vips__png_ispng_buffer;
load_class->is_a_buffer = vips_foreign_load_png_buffer_is_a_buffer;
load_class->get_flags = vips_foreign_load_png_buffer_get_flags;
load_class->header = vips_foreign_load_png_buffer_header;
load_class->load = vips_foreign_load_png_buffer_load;
@ -277,11 +427,7 @@ vips_pngload( const char *filename, VipsImage **out, ... )
* @out: (out): image to write
* @...: %NULL-terminated list of optional named arguments
*
* Read a PNG-formatted memory block into a VIPS image. It can read all png
* images, including 8- and 16-bit images, 1 and 3 channel, with and without
* an alpha channel.
*
* Any ICC profile is read and attached to the VIPS image.
* Exactly as vips_pngload(), but read from a PNG-formatted memory block.
*
* You must not free the buffer while @out is active. The
* #VipsObject::postclose signal on @out is a good place to free.
@ -310,4 +456,27 @@ vips_pngload_buffer( void *buf, size_t len, VipsImage **out, ... )
return( result );
}
/**
* vips_pngload_stream:
* @input: stream to load from
* @out: (out): image to write
* @...: %NULL-terminated list of optional named arguments
*
* Exactly as vips_pngload(), but read from a stream.
*
* See also: vips_pngload().
*
* Returns: 0 on success, -1 on error.
*/
int
vips_pngload_stream( VipsStreamInput *input, VipsImage **out, ... )
{
va_list ap;
int result;
va_start( ap, out );
result = vips_call_split( "pngload_stream", ap, input, out );
va_end( ap );
return( result );
}

View File

@ -90,6 +90,8 @@
* - fix another leak with error during buffer output
* 19/7/19
* - ignore large XMP
* 14/10/19
* - revise for stream IO
*/
/*

View File

@ -75,6 +75,8 @@
* - allow huge xmp metadata
* 7/10/19
* - restart after minimise
* 14/10/19
* - revise for stream IO
*/
/*
@ -157,6 +159,8 @@ user_warning_function( png_structp png_ptr, png_const_charp warning_msg )
g_warning( "%s", warning_msg );
}
#define INPUT_BUFFER_SIZE (4096)
/* What we track during a PNG read.
*/
typedef struct {
@ -169,62 +173,25 @@ typedef struct {
png_infop pInfo;
png_bytep *row_pointer;
/* For FILE input. If we close and reopen, save the ftell point in
* seek_position.
*/
FILE *fp;
long seek_position;
VipsStreamInput *input;
/* For memory input.
/* 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.
*/
const void *buffer;
size_t length;
size_t read_pos;
unsigned char input_buffer[INPUT_BUFFER_SIZE];
unsigned char *next_byte;
ssize_t bytes_in_buffer;
} Read;
/* This can be called many times.
*/
static void
read_close_input( Read *read )
{
#ifdef DEBUG
printf( "read_close_input:\n" );
#endif /*DEBUG*/
if( read->fp ) {
read->seek_position = ftell( read->fp );
VIPS_FREEF( fclose, read->fp );
}
}
static int
read_open_input( Read *read )
{
if( !read->fp &&
read->name ) {
if( !(read->fp =
vips__file_open_read( read->name, NULL, FALSE )) )
return( -1 );
if( read->seek_position != -1 )
fseek( read->fp, read->seek_position, SEEK_SET );
/* Just takes a copy of the fp.
*/
png_init_io( read->pPng, read->fp );
}
return( 0 );
}
/* Can be called many times.
*/
static void
read_destroy( Read *read )
{
read_close_input( read );
if( read->pPng )
png_destroy_read_struct( &read->pPng, &read->pInfo, NULL );
VIPS_UNREF( read->input );
VIPS_FREE( read->row_pointer );
}
@ -235,16 +202,59 @@ read_close_cb( VipsImage *out, Read *read )
}
static void
read_minimise_cb( VipsImage *out, Read *read )
read_minimise_cb( VipsImage *image, Read *read )
{
read_close_input( read );
if( read->input )
vips_stream_input_minimise( read->input );
}
static void
vips_png_read_stream( png_structp pPng, png_bytep data, png_size_t length )
{
Read *read = png_get_io_ptr( pPng );
#ifdef DEBUG
printf( "vips_png_read_stream: read %zd bytes\n", length );
#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 ) {
ssize_t bytes_available =
VIPS_MIN( read->bytes_in_buffer, length );
if( bytes_available > 0 ) {
memcpy( data, read->next_byte, bytes_available );
data += bytes_available;
length -= bytes_available;
read->next_byte += bytes_available;
read->bytes_in_buffer -= bytes_available;
}
else {
ssize_t bytes_read;
bytes_read = vips_stream_input_read( read->input,
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;
}
}
}
static Read *
read_new( VipsImage *out, gboolean fail )
read_new( VipsStreamInput *input, VipsImage *out, gboolean fail )
{
Read *read;
if( vips_stream_input_rewind( input ) )
return( NULL );
if( !(read = VIPS_NEW( out, Read )) )
return( NULL );
@ -255,11 +265,8 @@ read_new( VipsImage *out, gboolean fail )
read->pPng = NULL;
read->pInfo = NULL;
read->row_pointer = NULL;
read->fp = NULL;
read->seek_position = -1;
read->buffer = NULL;
read->length = 0;
read->read_pos = 0;
read->input = input;
g_object_ref( input );
g_signal_connect( out, "close",
G_CALLBACK( read_close_cb ), read );
@ -285,20 +292,18 @@ read_new( VipsImage *out, gboolean fail )
PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE );
#endif /*FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION*/
/* Catch PNG errors from png_create_info_struct().
png_set_read_fn( read->pPng, read, vips_png_read_stream );
/* Catch PNG errors from png_read_info() etc.
*/
if( setjmp( png_jmpbuf( read->pPng ) ) )
if( setjmp( png_jmpbuf( read->pPng ) ) ) {
read_destroy( read );
return( NULL );
}
if( !(read->pInfo = png_create_info_struct( read->pPng )) )
return( NULL );
return( read );
}
static void
read_info( Read *read )
{
/* By default, libpng refuses to open files with a metadata chunk
* larger than 8mb. We've seen real files with 20mb, so set 50mb.
*/
@ -306,29 +311,6 @@ read_info( Read *read )
png_set_chunk_malloc_max( read->pPng, 50 * 1024 * 1024 );
#endif /*HAVE_PNG_SET_CHUNK_MALLOC_MAX*/
png_read_info( read->pPng, read->pInfo );
}
static Read *
read_new_filename( VipsImage *out, const char *name, gboolean fail )
{
Read *read;
if( !(read = read_new( out, fail )) )
return( NULL );
read->name = vips_strdup( VIPS_OBJECT( out ), name );
if( read_open_input( read ) )
return( NULL );
/* Catch PNG errors from png_read_info().
*/
if( setjmp( png_jmpbuf( read->pPng ) ) ) {
read_destroy( read );
return( NULL );
}
read_info( read );
return( read );
}
@ -500,6 +482,9 @@ png2vips_header( Read *read, VipsImage *out )
VIPS_CODING_NONE, interpretation,
Xres, Yres );
VIPS_SETSTR( out->filename,
vips_stream_filename( VIPS_STREAM( read->input ) ) );
/* Uninterlaced images will be read in seq mode. Interlaced images are
* read via a huge memory buffer.
*/
@ -573,24 +558,6 @@ png2vips_header( Read *read, VipsImage *out )
return( 0 );
}
/* Read a PNG file header into a VIPS header.
*/
int
vips__png_header( const char *name, VipsImage *out )
{
Read *read;
if( !(read = read_new_filename( out, name, TRUE )) )
return( -1 );
if( png2vips_header( read, out ) ) {
read_close_input( read );
return( -1 );
}
read_close_input( read );
return( 0 );
}
/* Out is a huge "t" buffer we decompress to.
*/
static int
@ -647,10 +614,11 @@ png2vips_generate( VipsRegion *or,
* strip.
*/
g_assert( r->height == VIPS_MIN( VIPS__FATSTRIP_HEIGHT,
or->im->Ysize - r->top ) );
or->im->Ysize - r->top ) );
if( read_open_input( read ) )
return( -1 );
/* In pixel decode mode.
*/
vips_stream_input_decode( read->input );
/* And check that y_pos is correct. It should be, since we are inside
* a vips_sequential().
@ -708,37 +676,9 @@ png2vips_generate( VipsRegion *or,
return( 0 );
}
/* Early close to free the fd as soon as we can.
*/
if( read->y_pos >= read->out->Ysize ) {
png_read_end( read->pPng, NULL );
read_destroy( read );
}
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( const char *filename )
{
VipsImage *image;
Read *read;
int interlace_type;
image = vips_image_new();
if( !(read = read_new_filename( image, filename, TRUE )) ) {
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 );
}
static int
png2vips_image( Read *read, VipsImage *out )
{
@ -769,113 +709,41 @@ png2vips_image( Read *read, VipsImage *out )
return( -1 );
}
/* _generate will reopen.
*/
read_close_input( read );
return( 0 );
}
int
vips__png_read( const char *filename, VipsImage *out, gboolean fail )
{
Read *read;
#ifdef DEBUG
printf( "vips__png_read: reading \"%s\"\n", filename );
#endif /*DEBUG*/
if( !(read = read_new_filename( out, filename, fail )) ||
png2vips_image( read, out ) )
return( -1 );
#ifdef DEBUG
printf( "vips__png_read: done\n" );
#endif /*DEBUG*/
return( 0 );
}
gboolean
vips__png_ispng_buffer( const void *buf, size_t len )
vips__png_ispng_stream( VipsStreamInput *input )
{
if( len >= 8 &&
!png_sig_cmp( (png_bytep) buf, 0, 8 ) )
const unsigned char *p;
if( (p = vips_stream_input_sniff( input, 8 )) &&
!png_sig_cmp( (png_bytep) p, 0, 8 ) )
return( TRUE );
return( FALSE );
}
int
vips__png_ispng( const char *filename )
{
unsigned char buf[8];
return( vips__get_bytes( filename, buf, 8 ) == 8 &&
vips__png_ispng_buffer( buf, 8 ) );
}
static void
vips_png_read_buffer( png_structp pPng, png_bytep data, png_size_t length )
{
Read *read = png_get_io_ptr( pPng );
#ifdef DEBUG
printf( "vips_png_read_buffer: read %zd bytes\n", length );
#endif /*DEBUG*/
if( read->read_pos + length > read->length )
png_error( pPng, "not enough data in buffer" );
memcpy( data, (VipsPel *) read->buffer + read->read_pos, length );
read->read_pos += length;
}
static Read *
read_new_buffer( VipsImage *out, const void *buffer, size_t length,
gboolean fail )
vips__png_header_stream( VipsStreamInput *input, VipsImage *out )
{
Read *read;
if( !(read = read_new( out, fail )) )
return( NULL );
read->length = length;
read->buffer = buffer;
png_set_read_fn( read->pPng, read, vips_png_read_buffer );
/* Catch PNG errors from png_read_info().
*/
if( setjmp( png_jmpbuf( read->pPng ) ) ) {
read_destroy( read );
return( NULL );
}
read_info( read );
return( read );
}
int
vips__png_header_buffer( const void *buffer, size_t length, VipsImage *out )
{
Read *read;
if( !(read = read_new_buffer( out, buffer, length, TRUE )) ||
if( !(read = read_new( input, out, TRUE )) ||
png2vips_header( read, out ) )
return( -1 );
vips_stream_input_minimise( input );
return( 0 );
}
int
vips__png_read_buffer( const void *buffer, size_t length, VipsImage *out,
gboolean fail )
vips__png_read_stream( VipsStreamInput *input, VipsImage *out, gboolean fail )
{
Read *read;
if( !(read = read_new_buffer( out, buffer, length, fail )) ||
if( !(read = read_new( input, out, fail )) ||
png2vips_image( read, out ) )
return( -1 );
@ -886,7 +754,7 @@ vips__png_read_buffer( const void *buffer, size_t length, VipsImage *out,
* served partially from there. Non-interlaced PNGs may be read sequentially.
*/
gboolean
vips__png_isinterlaced_buffer( const void *buffer, size_t length )
vips__png_isinterlaced_stream( VipsStreamInput *input )
{
VipsImage *image;
Read *read;
@ -894,7 +762,7 @@ vips__png_isinterlaced_buffer( const void *buffer, size_t length )
image = vips_image_new();
if( !(read = read_new_buffer( image, buffer, length, TRUE )) ) {
if( !(read = read_new( input, image, TRUE )) ) {
g_object_unref( image );
return( -1 );
}

View File

@ -541,6 +541,8 @@ typedef enum /*< flags >*/ {
VIPS_FOREIGN_PNG_FILTER_ALL = 0xF8
} VipsForeignPngFilter;
int vips_pngload_stream( VipsStreamInput *input, VipsImage **out, ... )
__attribute__((sentinel));
int vips_pngload( const char *filename, VipsImage **out, ... )
__attribute__((sentinel));
int vips_pngload_buffer( void *buf, size_t len, VipsImage **out, ... )

View File

@ -86,6 +86,8 @@ typedef struct _VipsStreamClass {
GType vips_stream_get_type( void );
const char *vips_stream_filename( VipsStream *stream );
#define VIPS_TYPE_STREAM_INPUT (vips_stream_input_get_type())
#define VIPS_STREAM_INPUT( obj ) \
(G_TYPE_CHECK_INSTANCE_CAST( (obj), \

View File

@ -181,6 +181,12 @@ vips_stream_init( VipsStream *stream )
stream->close_descriptor = -1;
}
const char *
vips_stream_filename( VipsStream *stream )
{
return( stream->filename );
}
G_DEFINE_TYPE( VipsStreamInput, vips_stream_input, VIPS_TYPE_STREAM );
static void