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"]