add png load from buffer

though untested as yet
This commit is contained in:
John Cupitt 2013-06-16 22:51:28 +01:00
parent 1dbff8f5c0
commit 005984226e
5 changed files with 227 additions and 25 deletions

View File

@ -1583,6 +1583,7 @@ vips_foreign_operation_init( void )
extern GType vips_foreign_load_ppm_get_type( 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_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 );
@ -1623,6 +1624,7 @@ vips_foreign_operation_init( void )
#ifdef HAVE_PNG
vips_foreign_load_png_get_type();
vips_foreign_load_png_buffer_get_type();
vips_foreign_save_png_file_get_type();
vips_foreign_save_png_buffer_get_type();
#endif /*HAVE_PNG*/
@ -2224,6 +2226,47 @@ vips_pngload( const char *filename, VipsImage **out, ... )
return( result );
}
/**
* vips_pngload_buffer:
* @buf: memory area to load
* @len: size of memory area
* @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.
*
* Caution: on return only the header will have been read, the pixel data is
* not decompressed until the first pixel is read. Therefore you must not free
* @buf until you have read pixel data from @out.
*
* See also: vips_pngload().
*
* Returns: 0 on success, -1 on error.
*/
int
vips_pngload_buffer( void *buf, size_t len, VipsImage **out, ... )
{
va_list ap;
VipsArea *area;
int result;
/* We don't take a copy of the data or free it.
*/
area = vips_area_new_blob( NULL, buf, len );
va_start( ap, out );
result = vips_call_split( "pngload_buffer", ap, area, out );
va_end( ap );
vips_area_unref( area );
return( result );
}
/**
* vips_pngsave:
* @in: image to save

View File

@ -73,9 +73,9 @@ vips_foreign_load_png_get_flags_filename( const char *filename )
flags = 0;
if( vips__png_isinterlaced( filename ) )
flags = VIPS_FOREIGN_PARTIAL;
flags |= VIPS_FOREIGN_PARTIAL;
else
flags = VIPS_FOREIGN_SEQUENTIAL;
flags |= VIPS_FOREIGN_SEQUENTIAL;
return( flags );
}
@ -146,4 +146,72 @@ vips_foreign_load_png_init( VipsForeignLoadPng *png )
{
}
typedef struct _VipsForeignLoadPngBuffer {
VipsForeignLoad parent_object;
/* Load from a buffer.
*/
VipsArea *buf;
} VipsForeignLoadPngBuffer;
typedef VipsForeignLoadClass VipsForeignLoadPngBufferClass;
G_DEFINE_TYPE( VipsForeignLoadPngBuffer, vips_foreign_load_png_buffer,
VIPS_TYPE_FOREIGN_LOAD );
static int
vips_foreign_load_png_buffer_header( VipsForeignLoad *load )
{
VipsForeignLoadPngBuffer *png = (VipsForeignLoadPngBuffer *) load;
if( vips__png_header_buffer( load->out,
png->buf->data, png->buf->length ) )
return( -1 );
return( 0 );
}
static int
vips_foreign_load_png_buffer_load( VipsForeignLoad *load )
{
VipsForeignLoadPngBuffer *png = (VipsForeignLoadPngBuffer *) load;
if( vips__png_read_buffer( load->out,
png->buf->data, png->buf->length ) )
return( -1 );
return( 0 );
}
static void
vips_foreign_load_png_buffer_class_init( VipsForeignLoadPngBufferClass *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_buffer";
object_class->description = _( "load png from buffer" );
load_class->header = vips_foreign_load_png_buffer_header;
load_class->load = vips_foreign_load_png_buffer_load;
VIPS_ARG_BOXED( class, "buffer", 1,
_( "Buffer" ),
_( "Buffer to load from" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadPngBuffer, buf ),
VIPS_TYPE_BLOB );
}
static void
vips_foreign_load_png_buffer_init( VipsForeignLoadPngBuffer *png )
{
}
#endif /*HAVE_PNG*/

View File

@ -125,11 +125,20 @@ typedef struct {
char *name;
VipsImage *out;
FILE *fp;
int y_pos;
png_structp pPng;
png_infop pInfo;
png_bytep *row_pointer;
/* For FILE input.
*/
FILE *fp;
/* For memory input.
*/
char *buffer;
size_t length;
size_t read_pos;
} Read;
static void
@ -142,27 +151,27 @@ read_destroy( VipsImage *out, Read *read )
}
static Read *
read_new( const char *name, VipsImage *out )
read_new( VipsImage *out )
{
Read *read;
if( !(read = VIPS_NEW( out, Read )) )
return( NULL );
read->name = vips_strdup( VIPS_OBJECT( out ), name );
read->name = NULL;
read->out = out;
read->fp = NULL;
read->y_pos = 0;
read->pPng = NULL;
read->pInfo = NULL;
read->row_pointer = NULL;
read->fp = NULL;
read->buffer = NULL;
read->length = 0;
read->read_pos = 0;
g_signal_connect( out, "close",
G_CALLBACK( read_destroy ), read );
if( !(read->fp = vips__file_open_read( name, NULL, FALSE )) )
return( NULL );
if( !(read->pPng = png_create_read_struct(
PNG_LIBPNG_VER_STRING, NULL,
user_error_function, user_warning_function )) )
@ -176,6 +185,22 @@ read_new( const char *name, VipsImage *out )
if( !(read->pInfo = png_create_info_struct( read->pPng )) )
return( NULL );
return( read );
}
static Read *
read_new_filename( VipsImage *out, const char *name )
{
Read *read;
if( !(read = read_new( out )) )
return( NULL );
read->name = vips_strdup( VIPS_OBJECT( out ), name );
if( !(read->fp = vips__file_open_read( name, NULL, FALSE )) )
return( NULL );
/* Read enough of the file that png_get_interlace_type() will start
* working.
*/
@ -366,7 +391,7 @@ vips__png_header( const char *name, VipsImage *out )
{
Read *read;
if( !(read = read_new( name, out )) ||
if( !(read = read_new_filename( out, name )) ||
png2vips_header( read, out ) )
return( -1 );
@ -465,7 +490,7 @@ vips__png_isinterlaced( const char *filename )
int interlace_type;
image = vips_image_new();
if( !(read = read_new( filename, image )) ) {
if( !(read = read_new_filename( image, filename )) ) {
g_object_unref( image );
return( -1 );
}
@ -475,24 +500,13 @@ vips__png_isinterlaced( const char *filename )
return( interlace_type != PNG_INTERLACE_NONE );
}
int
vips__png_read( const char *filename, VipsImage *out )
static int
read_all( Read *read, VipsImage *out )
{
int interlace_type = png_get_interlace_type( read->pPng, read->pInfo );
VipsImage **t = (VipsImage **)
vips_object_local_array( VIPS_OBJECT( out ), 3 );
Read *read;
int interlace_type;
#ifdef DEBUG
printf( "vips__png_read: reading \"%s\"\n", filename );
#endif /*DEBUG*/
if( !(read = read_new( filename, out )) )
return( -1 );
interlace_type = png_get_interlace_type( read->pPng, read->pInfo );
if( interlace_type != PNG_INTERLACE_NONE ) {
/* Arg awful interlaced image. We have to load to a huge mem
* buffer, then copy to out.
@ -516,6 +530,22 @@ vips__png_read( const char *filename, VipsImage *out )
return( -1 );
}
return( 0 );
}
int
vips__png_read( const char *filename, VipsImage *out )
{
Read *read;
#ifdef DEBUG
printf( "vips__png_read: reading \"%s\"\n", filename );
#endif /*DEBUG*/
if( !(read = read_new_filename( out, filename )) ||
read_all( read, out ) )
return( -1 );
#ifdef DEBUG
printf( "vips__png_read: done\n" );
#endif /*DEBUG*/
@ -532,6 +562,63 @@ vips__png_ispng( const char *filename )
!png_sig_cmp( buf, 0, 8 ) );
}
static void
vips_png_read_buffer( png_structp pPng, png_bytep data, png_size_t length )
{
Read *read = png_get_io_ptr( pPng );
if( read->read_pos + length > read->length )
png_error( pPng, "not enough data in buffer" );
memcpy( data, read->buffer + read->read_pos, length );
read->read_pos += length;
}
static Read *
read_new_buffer( VipsImage *out, char *buffer, size_t length )
{
Read *read;
if( !(read = read_new( out )) )
return( NULL );
read->buffer = buffer;
read->length = length;
png_set_read_fn( read->pPng, read, vips_png_read_buffer );
/* Read enough of the file that png_get_interlace_type() will start
* working.
*/
png_read_info( read->pPng, read->pInfo );
return( read );
}
int
vips__png_header_buffer( VipsImage *out, char *buffer, size_t length )
{
Read *read;
if( !(read = read_new_buffer( out, buffer, length )) ||
png2vips_header( read, out ) )
return( -1 );
return( 0 );
}
int
vips__png_read_buffer( VipsImage *out, char *buffer, size_t length )
{
Read *read;
if( !(read = read_new_buffer( out, buffer, length )) ||
read_all( read, out ) )
return( -1 );
return( 0 );
}
const char *vips__png_suffs[] = { ".png", NULL };
/* What we track during a PNG write.

View File

@ -40,6 +40,8 @@ int vips__png_read( const char *name, VipsImage *out );
int vips__png_ispng( const char *filename );
gboolean vips__png_isinterlaced( const char *filename );
extern const char *vips__png_suffs[];
int vips__png_read_buffer( VipsImage *out, char *buffer, size_t length );
int vips__png_header_buffer( VipsImage *out, char *buffer, size_t length );
int vips__png_write( VipsImage *in, const char *filename,
int compress, int interlace );

View File

@ -407,6 +407,8 @@ int vips_magickload( const char *filename, VipsImage **out, ... )
int vips_pngload( const char *filename, VipsImage **out, ... )
__attribute__((sentinel));
int vips_pngload_buffer( void *buf, size_t len, VipsImage **out, ... )
__attribute__((sentinel));
int vips_pngsave( VipsImage *in, const char *filename, ... )
__attribute__((sentinel));
int vips_pngsave_buffer( VipsImage *in, void **buf, size_t *len, ... )