add png stream load
This commit is contained in:
parent
f6d247627f
commit
1bdadeed61
@ -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" ) );
|
||||
|
@ -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*/
|
||||
|
||||
|
@ -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" );
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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, ... )
|
||||
|
@ -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), \
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user