From dba1b1d81b640f06716d1ee95a3c8fc5d80cd843 Mon Sep 17 00:00:00 2001 From: Maurus Cuelenaere Date: Sat, 14 Feb 2015 18:09:31 +0100 Subject: [PATCH] Add ImageMagick buffer support This commits adds buffer support for the ImageMagick backend, using the Blob API. --- cplusplus/include/vips/vips-operators.h | 2 + cplusplus/vips-operators.cpp | 13 +++ doc/function-list.xml | 5 ++ libvips/foreign/foreign.c | 44 ++++++++++ libvips/foreign/magick.h | 5 ++ libvips/foreign/magick2vips.c | 87 ++++++++++++++++++- libvips/foreign/magickload.c | 110 ++++++++++++++++++++++++ libvips/include/vips/foreign.h | 2 + python/Vips.py | 1 + 9 files changed, 267 insertions(+), 2 deletions(-) diff --git a/cplusplus/include/vips/vips-operators.h b/cplusplus/include/vips/vips-operators.h index 301a444f..e2aa71f1 100644 --- a/cplusplus/include/vips/vips-operators.h +++ b/cplusplus/include/vips/vips-operators.h @@ -226,6 +226,8 @@ static VImage openslideload( char * filename , VOption *options = 0 ) throw( VError ); static VImage magickload( char * filename , VOption *options = 0 ) throw( VError ); +static VImage magickload_buffer( VipsBlob * buffer , VOption *options = 0 ) + throw( VError ); static VImage fitsload( char * filename , VOption *options = 0 ) throw( VError ); static VImage openexrload( char * filename , VOption *options = 0 ) diff --git a/cplusplus/vips-operators.cpp b/cplusplus/vips-operators.cpp index e8a0687b..0bc154fd 100644 --- a/cplusplus/vips-operators.cpp +++ b/cplusplus/vips-operators.cpp @@ -1567,6 +1567,19 @@ VImage VImage::magickload( char * filename , VOption *options ) return( out ); } +VImage VImage::magickload_buffer( VipsBlob * buffer , VOption *options ) + throw( VError ) +{ + VImage out; + + call( "magickload_buffer" , + (options ? options : VImage::option()) -> + set( "buffer", buffer ) -> + set( "out", &out ) ); + + return( out ); +} + VImage VImage::fitsload( char * filename , VOption *options ) throw( VError ) { diff --git a/doc/function-list.xml b/doc/function-list.xml index 6ed50238..f16e7b1e 100644 --- a/doc/function-list.xml +++ b/doc/function-list.xml @@ -641,6 +641,11 @@ load file with ImageMagick vips_magickload() + + magickload_buffer + load image from buffer with ImageMagick + vips_magickload_buffer() + fitsload load a FITS image diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index 61ccce47..eaa4e0ce 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -1546,6 +1546,7 @@ vips_foreign_operation_init( void ) extern GType vips_foreign_save_raw_get_type( void ); extern GType vips_foreign_save_raw_fd_get_type( void ); extern GType vips_foreign_load_magick_get_type( void ); + extern GType vips_foreign_load_magick_buffer_get_type( void ); extern GType vips_foreign_save_dz_get_type( void ); extern GType vips_foreign_load_webp_file_get_type( void ); extern GType vips_foreign_load_webp_buffer_get_type( void ); @@ -1610,6 +1611,7 @@ vips_foreign_operation_init( void ) #ifdef HAVE_MAGICK vips_foreign_load_magick_get_type(); + vips_foreign_load_magick_buffer_get_type(); #endif /*HAVE_MAGICK*/ #ifdef HAVE_CFITSIO @@ -1668,6 +1670,48 @@ vips_magickload( const char *filename, VipsImage **out, ... ) return( result ); } +/** + * vips_magickload_buffer: + * @buf: memory area to load + * @len: size of memory area + * @out: decompressed image + * @...: %NULL-terminated list of optional named arguments + * + * Optional arguments: + * + * @all_frames: %gboolean, load all frames in sequence + * @density: string, canvas resolution for rendering vector formats like SVG + * + * Read an image memory block using libMagick into a VIPS image. Exactly as + * vips_magickload(), but read from a memory source. + * + * You must not free the buffer while @out is active. The + * #VipsObject::postclose signal on @out is a good place to free. + * + * See also: vips_magickload(). + * + * Returns: 0 on success, -1 on error. + */ +int +vips_magickload_buffer( void *buf, size_t len, VipsImage **out, ... ) +{ + va_list ap; + VipsBlob *blob; + int result; + + /* We don't take a copy of the data or free it. + */ + blob = vips_blob_new( NULL, buf, len ); + + va_start( ap, out ); + result = vips_call_split( "magickload_buffer", ap, blob, out ); + va_end( ap ); + + vips_area_unref( VIPS_AREA( blob ) ); + + return( result ); +} + /** * vips_tiffload: * @filename: file to load diff --git a/libvips/foreign/magick.h b/libvips/foreign/magick.h index 80b98772..1d43b13c 100644 --- a/libvips/foreign/magick.h +++ b/libvips/foreign/magick.h @@ -40,6 +40,11 @@ int vips__magick_read( const char *filename, int vips__magick_read_header( const char *filename, VipsImage *out, gboolean all_frames, const char *density ); +int vips__magick_read_buffer( const void *buf, const size_t len, + VipsImage *out, gboolean all_frames, const char *density ); +int vips__magick_read_buffer_header( const void *buf, const size_t len, + VipsImage *out, gboolean all_frames, const char *density ); + #ifdef __cplusplus } #endif /*__cplusplus*/ diff --git a/libvips/foreign/magick2vips.c b/libvips/foreign/magick2vips.c index e2df66ab..ecb00d2d 100644 --- a/libvips/foreign/magick2vips.c +++ b/libvips/foreign/magick2vips.c @@ -160,7 +160,7 @@ read_new( const char *filename, VipsImage *im, gboolean all_frames, if( !(read = VIPS_NEW( im, Read )) ) return( NULL ); - read->filename = g_strdup( filename ); + read->filename = filename ? g_strdup( filename ) : NULL; read->all_frames = all_frames; read->im = im; read->image = NULL; @@ -176,7 +176,9 @@ read_new( const char *filename, VipsImage *im, gboolean all_frames, if( !read->image_info ) return( NULL ); - vips_strncpy( read->image_info->filename, filename, MaxTextExtent ); + if (filename) { + vips_strncpy( read->image_info->filename, filename, MaxTextExtent ); + } /* Canvas resolution for rendering vector formats like SVG. */ @@ -740,5 +742,86 @@ vips__magick_read_header( const char *filename, VipsImage *im, return( 0 ); } +int +vips__magick_read_buffer( const void *buf, const size_t len, VipsImage *out, + gboolean all_frames, const char *density ) +{ + Read *read; + +#ifdef DEBUG + printf( "magick2vips: vips__magick_read_buffer: %p %zu\n", buf, len ); +#endif /*DEBUG*/ + + if( !(read = read_new( NULL, out, all_frames, density )) ) + return( -1 ); + +#ifdef HAVE_SETIMAGEOPTION + /* When reading DICOM images, we want to ignore any + * window_center/_width setting, since it may put pixels outside the + * 0-65535 range and lose data. + * + * These window settings are attached as vips metadata, so our caller + * can interpret them if it wants. + */ + SetImageOption( read->image_info, "dcm:display-range", "reset" ); +#endif /*HAVE_SETIMAGEOPTION*/ + +#ifdef DEBUG + printf( "magick2vips: calling BlobToImage() ...\n" ); +#endif /*DEBUG*/ + + read->image = BlobToImage( read->image_info, buf, len, &read->exception ); + if( !read->image ) { + vips_error( "magick2vips", _( "unable to read buffer\n" + "libMagick error: %s %s" ), + read->exception.reason, read->exception.description ); + return( -1 ); + } + + if( parse_header( read ) ) + return( -1 ); + if( vips_image_generate( out, + NULL, magick_fill_region, NULL, read, NULL ) ) + return( -1 ); + + return( 0 ); +} + +int +vips__magick_read_buffer_header( const void *buf, const size_t len, + VipsImage *im, gboolean all_frames, const char *density ) +{ + Read *read; + +#ifdef DEBUG + printf( "vips__magick_read_buffer_header: %p %zu\n", buf, len ); +#endif /*DEBUG*/ + + if( !(read = read_new( NULL, im, all_frames, density )) ) + return( -1 ); + +#ifdef DEBUG + printf( "vips__magick_read_buffer_header: pinging blob ...\n" ); +#endif /*DEBUG*/ + + read->image = PingBlob( read->image_info, buf, len, &read->exception ); + if( !read->image ) { + vips_error( "magick2vips", _( "unable to ping blob\n" + "libMagick error: %s %s" ), + read->exception.reason, read->exception.description ); + return( -1 ); + } + + if( parse_header( read ) ) + return( -1 ); + + if( im->Xsize <= 0 || im->Ysize <= 0 ) { + vips_error( "magick2vips", "%s", _( "bad image size" ) ); + return( -1 ); + } + + return( 0 ); +} + #endif /*HAVE_MAGICK*/ diff --git a/libvips/foreign/magickload.c b/libvips/foreign/magickload.c index e4d2c8e2..9064e2f9 100644 --- a/libvips/foreign/magickload.c +++ b/libvips/foreign/magickload.c @@ -175,4 +175,114 @@ vips_foreign_load_magick_init( VipsForeignLoadMagick *magick ) { } +typedef struct _VipsForeignLoadMagickBuffer { + VipsForeignLoad parent_object; + + VipsArea *buf; + gboolean all_frames; + char *density; + +} VipsForeignLoadMagickBuffer; + +typedef VipsForeignLoadClass VipsForeignLoadMagickBufferClass; + +G_DEFINE_TYPE( VipsForeignLoadMagickBuffer, vips_foreign_load_magick_buffer, + VIPS_TYPE_FOREIGN_LOAD ); + +static gboolean +vips_foreign_load_magick_buffer_is_a_buffer ( void* buf, size_t len ) +{ + VipsImage *t; + int result; + + t = vips_image_new(); + vips_error_freeze(); + result = vips__magick_read_buffer_header( buf, len, t, FALSE, NULL ); + g_object_unref( t ); + vips_error_thaw(); + + return( result == 0 ); +} + +static VipsForeignFlags +vips_foreign_load_magick_buffer_get_flags( VipsForeignLoad *load ) +{ + return( VIPS_FOREIGN_PARTIAL ); +} + +static int +vips_foreign_load_magick_buffer_header( VipsForeignLoad *load ) +{ + VipsForeignLoadMagickBuffer *magick = (VipsForeignLoadMagickBuffer *) load; + + if( vips__magick_read_buffer_header( magick->buf->data, magick->buf->length, + load->out, magick->all_frames, magick->density ) ) + return( -1 ); + + return( 0 ); +} + +static int +vips_foreign_load_magick_buffer_load( VipsForeignLoad *load ) +{ + VipsForeignLoadMagickBuffer *magick = (VipsForeignLoadMagickBuffer *) load; + + if( vips__magick_read_buffer( magick->buf->data, magick->buf->length, + load->real, magick->all_frames, magick->density ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_foreign_load_magick_buffer_class_init( VipsForeignLoadMagickBufferClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsForeignClass *foreign_class = (VipsForeignClass *) 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 = "magickload_buffer"; + object_class->description = _( "load buffer with ImageMagick" ); + + /* We need to be well to the back of the queue since the vips's + * dedicated loaders are usually preferable. + */ + foreign_class->priority = -100; + + load_class->is_a_buffer = vips_foreign_load_magick_buffer_is_a_buffer; + load_class->get_flags = vips_foreign_load_magick_buffer_get_flags; + load_class->header = vips_foreign_load_magick_buffer_header; + load_class->load = vips_foreign_load_magick_buffer_load; + + VIPS_ARG_BOXED( class, "buffer", 1, + _( "Buffer" ), + _( "Buffer to load from" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsForeignLoadMagickBuffer, buf ), + VIPS_TYPE_BLOB ); + + VIPS_ARG_BOOL( class, "all_frames", 3, + _( "all_frames" ), + _( "Read all frames from an image" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsForeignLoadMagickBuffer, all_frames ), + FALSE ); + + VIPS_ARG_STRING( class, "density", 4, + _( "Density" ), + _( "Canvas resolution for rendering vector formats like SVG" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsForeignLoadMagickBuffer, density ), + NULL ); +} + +static void +vips_foreign_load_magick_buffer_init( VipsForeignLoadMagickBuffer *buffer ) +{ +} + #endif /*HAVE_MAGICK*/ diff --git a/libvips/include/vips/foreign.h b/libvips/include/vips/foreign.h index a695d4e0..072a5edd 100644 --- a/libvips/include/vips/foreign.h +++ b/libvips/include/vips/foreign.h @@ -435,6 +435,8 @@ int vips_matrixprint( VipsImage *in, ... ) int vips_magickload( const char *filename, VipsImage **out, ... ) __attribute__((sentinel)); +int vips_magickload_buffer( void *buf, size_t len, VipsImage **out, ... ) + __attribute__((sentinel)); /** * VipsForeignPngFilter: diff --git a/python/Vips.py b/python/Vips.py index 0ce06eca..c6bf8fc5 100644 --- a/python/Vips.py +++ b/python/Vips.py @@ -993,6 +993,7 @@ class_methods = [ "tiffload_buffer", "openslideload", "magickload", + "magickload_buffer", "fitsload", "openexrload"]