diff --git a/ChangeLog b/ChangeLog index a7be573dcd..f69ab5cba5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,7 @@ master - add dcrawload, dcrawload_source, dcrawload_buffer: load raw camera files using libraw [lxsameer] +- add magickload_source: load from a source with imagemagick 8.17.1 diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index d9b99dec8c..75652a0dd5 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -3000,8 +3000,10 @@ vips_foreign_operation_init(void) extern GType vips_foreign_load_magick_file_get_type(void); extern GType vips_foreign_load_magick_buffer_get_type(void); + extern GType vips_foreign_load_magick_source_get_type(void); extern GType vips_foreign_load_magick7_file_get_type(void); extern GType vips_foreign_load_magick7_buffer_get_type(void); + extern GType vips_foreign_load_magick7_source_get_type(void); extern GType vips_foreign_save_magick_file_get_type(void); extern GType vips_foreign_save_magick_buffer_get_type(void); @@ -3234,11 +3236,13 @@ vips_foreign_operation_init(void) #ifdef HAVE_MAGICK6 vips_foreign_load_magick_file_get_type(); vips_foreign_load_magick_buffer_get_type(); + vips_foreign_load_magick_source_get_type(); #endif /*HAVE_MAGICK6*/ #ifdef HAVE_MAGICK7 vips_foreign_load_magick7_file_get_type(); vips_foreign_load_magick7_buffer_get_type(); + vips_foreign_load_magick7_source_get_type(); #endif /*HAVE_MAGICK7*/ #endif /*defined(ENABLE_MAGICKLOAD) && !defined(MAGICK_MODULE)*/ diff --git a/libvips/foreign/magick6load.c b/libvips/foreign/magick6load.c index 265367ed39..b809fae301 100644 --- a/libvips/foreign/magick6load.c +++ b/libvips/foreign/magick6load.c @@ -1074,6 +1074,113 @@ vips_foreign_load_magick_buffer_init(VipsForeignLoadMagickBuffer *buffer) { } +typedef struct _VipsForeignLoadMagickSource { + VipsForeignLoadMagick parent_object; + + VipsSource *source; + +} VipsForeignLoadMagickSource; + +typedef VipsForeignLoadMagickClass VipsForeignLoadMagickSourceClass; + +G_DEFINE_TYPE(VipsForeignLoadMagickSource, vips_foreign_load_magick_source, + vips_foreign_load_magick_get_type()); + +static gboolean +vips_foreign_load_magick_source_is_a_source(VipsSource *source) +{ + const unsigned char *p; + + // just use the first 100 bytes, we don't want to force too much into + // memory + return (p = vips_source_sniff(source, 100)) && + magick_ismagick(p, 100); +} + +/* Unfortunately, libMagick does not support header-only reads very well. See + * + * http://www.imagemagick.org/discourse-server/viewtopic.php?f=1&t=20017 + * + * Test especially with BMP, GIF, TGA. So we are forced to read the entire + * image in the @header() method. + */ +static int +vips_foreign_load_magick_source_header(VipsForeignLoad *load) +{ + VipsForeignLoadMagick *magick = (VipsForeignLoadMagick *) load; + VipsForeignLoadMagickSource *magick_source = + (VipsForeignLoadMagickSource *) load; + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS(magick); + +#ifdef DEBUG + printf("vips_foreign_load_magick_source_header: %p\n", load); +#endif /*DEBUG*/ + + if (vips_source_is_file(magick_source->source)) { + const char *filename = + vips_connection_filename(VIPS_CONNECTION(magick_source->source)); + + g_strlcpy(magick->image_info->filename, filename, MagickPathExtent); + magick_sniff_file(magick->image_info, filename); + magick->image = ReadImage(magick->image_info, magick->exception); + } + else { + size_t length; + const void *data; + + if (!(data = vips_source_map(magick_source->source, &length))) + return -1; + magick_sniff_bytes(magick->image_info, data, length); + magick->image = BlobToImage(magick->image_info, + data, length, magick->exception); + } + + /* It would be great if we could PingImage and just read the header, + * but sadly many IM coders do not support ping. The critical one for + * us is DICOM. TGA also has issues. + */ + if (!magick->image) { + magick_vips_error(class->nickname, magick->exception); + vips_error(class->nickname, _("unable to read source")); + return -1; + } + + if (vips_foreign_load_magick_load(magick)) + return -1; + + return 0; +} + +static void +vips_foreign_load_magick_source_class_init( + VipsForeignLoadMagickSourceClass *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 = "magickload_source"; + object_class->description = _("load source with ImageMagick"); + + load_class->is_a_source = vips_foreign_load_magick_source_is_a_source; + load_class->header = vips_foreign_load_magick_source_header; + + VIPS_ARG_OBJECT(class, "source", 1, + _("Source"), + _("Source to load from"), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET(VipsForeignLoadMagickSource, source), + VIPS_TYPE_SOURCE); +} + +static void +vips_foreign_load_magick_source_init(VipsForeignLoadMagickSource *source) +{ +} + #endif /*HAVE_MAGICK6*/ #endif /*ENABLE_MAGICKLOAD*/ diff --git a/libvips/foreign/magick7load.c b/libvips/foreign/magick7load.c index 62defc0a57..7f6db8fb15 100644 --- a/libvips/foreign/magick7load.c +++ b/libvips/foreign/magick7load.c @@ -939,6 +939,112 @@ vips_foreign_load_magick7_buffer_init(VipsForeignLoadMagick7Buffer *buffer) { } +typedef struct _VipsForeignLoadMagick7Source { + VipsForeignLoadMagick7 parent_object; + + VipsSource *source; + +} VipsForeignLoadMagick7Source; + +typedef VipsForeignLoadMagick7Class VipsForeignLoadMagick7SourceClass; + +G_DEFINE_TYPE(VipsForeignLoadMagick7Source, vips_foreign_load_magick7_source, + vips_foreign_load_magick7_get_type()); + +static gboolean +vips_foreign_load_magick7_source_is_a_source(VipsSource *source) +{ + const unsigned char *p; + + // just use the first 100 bytes, we don't want to force too much into + // memory + return (p = vips_source_sniff(source, 100)) && + magick_ismagick(p, 100); +} + +/* Unfortunately, libMagick7 does not support header-only reads very well. See + * + * http://www.imagemagick7.org/discourse-server/viewtopic.php?f=1&t=20017 + * + * Test especially with BMP, GIF, TGA. So we are forced to read the entire + * image in the @header() method. + */ +static int +vips_foreign_load_magick7_source_header(VipsForeignLoad *load) +{ + VipsForeignLoadMagick7 *magick7 = (VipsForeignLoadMagick7 *) load; + VipsForeignLoadMagick7Source *magick7_source = + (VipsForeignLoadMagick7Source *) load; + +#ifdef DEBUG + printf("vips_foreign_load_magick7_source_header: %p\n", load); +#endif /*DEBUG*/ + + if (vips_source_is_file(magick7_source->source)) { + const char *filename = + vips_connection_filename(VIPS_CONNECTION(magick7_source->source)); + + g_strlcpy(magick7->image_info->filename, filename, MaxTextExtent); + magick_sniff_file(magick7->image_info, filename); + magick7->image = ReadImage(magick7->image_info, magick7->exception); + } + else { + size_t length; + const void *data; + + if (!(data = vips_source_map(magick7_source->source, &length))) + return -1; + + magick_sniff_bytes(magick7->image_info, data, length); + magick7->image = BlobToImage(magick7->image_info, data, length, + magick7->exception); + } + + /* It would be great if we could PingImage and just read the header, + * but sadly many IM coders do not support ping. The critical one for + * us is DICOM. TGA also has issues. + */ + if (!magick7->image) { + vips_foreign_load_magick7_error(magick7); + return -1; + } + + if (vips_foreign_load_magick7_load(magick7)) + return -1; + + return 0; +} + +static void +vips_foreign_load_magick7_source_class_init( + VipsForeignLoadMagick7SourceClass *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 = "magickload_source"; + object_class->description = _("load source with ImageMagick7"); + + load_class->is_a_source = vips_foreign_load_magick7_source_is_a_source; + load_class->header = vips_foreign_load_magick7_source_header; + + VIPS_ARG_OBJECT(class, "source", 1, + _("Source"), + _("Source to load from"), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET(VipsForeignLoadMagick7Source, source), + VIPS_TYPE_SOURCE); +} + +static void +vips_foreign_load_magick7_source_init(VipsForeignLoadMagick7Source *source) +{ +} + #endif /*HAVE_MAGICK7*/ #endif /*ENABLE_MAGICKLOAD*/ diff --git a/libvips/foreign/magickload.c b/libvips/foreign/magickload.c index f0a71b56c5..a2e71a9a2d 100644 --- a/libvips/foreign/magickload.c +++ b/libvips/foreign/magickload.c @@ -156,3 +156,35 @@ vips_magickload_buffer(void *buf, size_t len, VipsImage **out, ...) return result; } + +/** + * vips_magickload_source: + * @source: source to load + * @out: (out): image to write + * @...: `NULL`-terminated list of optional named arguments + * + * Exactly as [ctor@Image.magickload], but read from a source. + * + * ::: tip "Optional arguments" + * * @page: `gint`, load from this page + * * @n: `gint`, load this many pages + * * @density: `gchararray`, canvas resolution for rendering vector formats + * like SVG + * + * ::: seealso + * [ctor@Image.magickload]. + * + * Returns: 0 on success, -1 on error. + */ +int +vips_magickload_source(VipsSource *source, VipsImage **out, ...) +{ + va_list ap; + int result; + + va_start(ap, out); + result = vips_call_split("magickload_source", ap, source, out); + va_end(ap); + + return result; +} diff --git a/libvips/include/vips/foreign.h b/libvips/include/vips/foreign.h index 8217ad456d..2a3efbaaee 100644 --- a/libvips/include/vips/foreign.h +++ b/libvips/include/vips/foreign.h @@ -716,6 +716,9 @@ VIPS_API int vips_magickload_buffer(void *buf, size_t len, VipsImage **out, ...) G_GNUC_NULL_TERMINATED; VIPS_API +int vips_magickload_source(VipsSource *source, VipsImage **out, ...) + G_GNUC_NULL_TERMINATED; +VIPS_API int vips_magicksave(VipsImage *in, const char *filename, ...) G_GNUC_NULL_TERMINATED; VIPS_API diff --git a/libvips/module/magick.c b/libvips/module/magick.c index 19f33d22a4..37d43463b1 100644 --- a/libvips/module/magick.c +++ b/libvips/module/magick.c @@ -60,8 +60,10 @@ g_module_check_init(GModule *module) extern GType vips_foreign_load_magick_file_get_type(void); extern GType vips_foreign_load_magick_buffer_get_type(void); + extern GType vips_foreign_load_magick_source_get_type(void); extern GType vips_foreign_load_magick7_file_get_type(void); extern GType vips_foreign_load_magick7_buffer_get_type(void); + extern GType vips_foreign_load_magick7_source_get_type(void); extern GType vips_foreign_save_magick_file_get_type(void); extern GType vips_foreign_save_magick_bmp_file_get_type(void); extern GType vips_foreign_save_magick_buffer_get_type(void); @@ -71,11 +73,13 @@ g_module_check_init(GModule *module) #ifdef HAVE_MAGICK6 vips_foreign_load_magick_file_get_type(); vips_foreign_load_magick_buffer_get_type(); + vips_foreign_load_magick_source_get_type(); #endif /*HAVE_MAGICK6*/ #ifdef HAVE_MAGICK7 vips_foreign_load_magick7_file_get_type(); vips_foreign_load_magick7_buffer_get_type(); + vips_foreign_load_magick7_source_get_type(); #endif /*HAVE_MAGICK7*/ #endif /*ENABLE_MAGICKLOAD*/ diff --git a/test/test-suite/test_foreign.py b/test/test-suite/test_foreign.py index d20e403947..2b134492d2 100644 --- a/test/test-suite/test_foreign.py +++ b/test/test-suite/test_foreign.py @@ -806,6 +806,9 @@ def bmp_valid(im): self.file_loader("magickload", BMP_FILE, bmp_valid) self.buffer_loader("magickload_buffer", BMP_FILE, bmp_valid) + source = pyvips.Source.new_from_file(BMP_FILE) + x = pyvips.Image.new_from_source(source, "") + bmp_valid(x) # we should have rgb or rgba for svg files ... different versions of # IM handle this differently. GM even gives 1 band. @@ -877,11 +880,12 @@ def bmp_valid(im): assert im.width == 433 assert im.height == 433 - # load should see metadata like eg. icc profiles im = pyvips.Image.magickload(JPEG_FILE) assert len(im.get("icc-profile-data")) == 564 + im = pyvips.Image.magickload(JPEG_FILE) + # added in 8.7 @skip_if_no("magicksave") def test_magicksave(self):
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: