diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index 47f248ec..8ef04e46 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -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 diff --git a/libvips/foreign/pngload.c b/libvips/foreign/pngload.c index 226cd9c8..97763eef 100644 --- a/libvips/foreign/pngload.c +++ b/libvips/foreign/pngload.c @@ -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*/ + diff --git a/libvips/foreign/vipspng.c b/libvips/foreign/vipspng.c index 99929cda..ac39a7c4 100644 --- a/libvips/foreign/vipspng.c +++ b/libvips/foreign/vipspng.c @@ -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. diff --git a/libvips/foreign/vipspng.h b/libvips/foreign/vipspng.h index 38bdc5a9..c28951c1 100644 --- a/libvips/foreign/vipspng.h +++ b/libvips/foreign/vipspng.h @@ -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 ); diff --git a/libvips/include/vips/foreign.h b/libvips/include/vips/foreign.h index 285326e5..bb94e824 100644 --- a/libvips/include/vips/foreign.h +++ b/libvips/include/vips/foreign.h @@ -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, ... )