From 931c604d5ea585e40b319ba0c9483ed641f58a52 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 28 Jun 2025 14:39:45 +0100 Subject: [PATCH 1/7] add magickload_source test with eg.: ``` $ cat MARBLES.BMP | vips magickload_source "[descriptor=0]" x.v $ cat MARBLES.BMP | vipsthumbnail stdin -o x.jpg ``` ImageMagick doesn't support generic IO, so it just maps the source into memory and uses the buffer interface. magick6 and magick7. --- ChangeLog | 1 + libvips/foreign/foreign.c | 4 ++ libvips/foreign/magick6load.c | 96 +++++++++++++++++++++++++++++++++++ libvips/foreign/magick7load.c | 94 ++++++++++++++++++++++++++++++++++ libvips/module/magick.c | 4 ++ 5 files changed, 199 insertions(+) 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..84a9266e2c 100644 --- a/libvips/foreign/magick6load.c +++ b/libvips/foreign/magick6load.c @@ -1074,6 +1074,102 @@ 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 byes, 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*/ + + size_t length; + const void *data; + if (!(data = vips_source_map(magick_source->source, &length))) + return -1; + + /* 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. + */ + magick_sniff_bytes(magick->image_info, data, length); + magick->image = BlobToImage(magick->image_info, + data, length, magick->exception); + 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..1eaabab60b 100644 --- a/libvips/foreign/magick7load.c +++ b/libvips/foreign/magick7load.c @@ -939,6 +939,100 @@ 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 byes, 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*/ + + size_t length; + const void *data; + if (!(data = vips_source_map(magick7_source->source, &length))) + return -1; + + /* 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. + */ + magick_sniff_bytes(magick7->image_info, data, length); + magick7->image = BlobToImage(magick7->image_info, data, length, + magick7->exception); + 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/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*/ From 8bb5506f58851307f76b396e3dc8e22bc81875a3 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 28 Jun 2025 15:18:21 +0100 Subject: [PATCH 2/7] add a test --- test/test-suite/test_foreign.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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): From 2a07e027cff8495a7c3ba8d1f6012de41f63406d Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 28 Jun 2025 15:24:01 +0100 Subject: [PATCH 3/7] Update libvips/foreign/magick6load.c Co-authored-by: Kleis Auke Wolthuizen --- libvips/foreign/magick6load.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libvips/foreign/magick6load.c b/libvips/foreign/magick6load.c index 84a9266e2c..d8f67997f7 100644 --- a/libvips/foreign/magick6load.c +++ b/libvips/foreign/magick6load.c @@ -1091,7 +1091,7 @@ vips_foreign_load_magick_source_is_a_source(VipsSource *source) { const unsigned char *p; - // just use the first 100 byes, we don't want to force too much into + // 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); From ac50cbf1b0be1606f17faaa2297d1b809ae616a3 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 28 Jun 2025 15:24:12 +0100 Subject: [PATCH 4/7] Update libvips/foreign/magick7load.c Co-authored-by: Kleis Auke Wolthuizen --- libvips/foreign/magick7load.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libvips/foreign/magick7load.c b/libvips/foreign/magick7load.c index 1eaabab60b..db78acbc39 100644 --- a/libvips/foreign/magick7load.c +++ b/libvips/foreign/magick7load.c @@ -956,7 +956,7 @@ vips_foreign_load_magick7_source_is_a_source(VipsSource *source) { const unsigned char *p; - // just use the first 100 byes, we don't want to force too much into + // 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); From 8578a75f3d6054fd6b502a983789ef98fbbfd60c Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 28 Jun 2025 15:34:59 +0100 Subject: [PATCH 5/7] use the filename interface if possible ie. if the source has an associated filename --- libvips/foreign/magick6load.c | 25 ++++++++++++++++++------- libvips/foreign/magick7load.c | 26 +++++++++++++++++++------- libvips/include/vips/foreign.h | 3 +++ 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/libvips/foreign/magick6load.c b/libvips/foreign/magick6load.c index 84a9266e2c..8ee2e7634f 100644 --- a/libvips/foreign/magick6load.c +++ b/libvips/foreign/magick6load.c @@ -1116,18 +1116,29 @@ vips_foreign_load_magick_source_header(VipsForeignLoad *load) printf("vips_foreign_load_magick_source_header: %p\n", load); #endif /*DEBUG*/ - size_t length; - const void *data; - if (!(data = vips_source_map(magick_source->source, &length))) - return -1; + if (vips_source_is_file(magick_source->source)) { + const char *filename = + vips_connection_filename(VIPS_CONNECTION(magick_source->source)); + + g_strlcpy(magick7->image_info->filename, filename, MagickPathExtent); + 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(raw->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. */ - magick_sniff_bytes(magick->image_info, data, length); - magick->image = BlobToImage(magick->image_info, - data, length, magick->exception); if (!magick->image) { magick_vips_error(class->nickname, magick->exception); vips_error(class->nickname, _("unable to read source")); diff --git a/libvips/foreign/magick7load.c b/libvips/foreign/magick7load.c index 1eaabab60b..75a8da533c 100644 --- a/libvips/foreign/magick7load.c +++ b/libvips/foreign/magick7load.c @@ -980,18 +980,30 @@ vips_foreign_load_magick7_source_header(VipsForeignLoad *load) printf("vips_foreign_load_magick7_source_header: %p\n", load); #endif /*DEBUG*/ - size_t length; - const void *data; - if (!(data = vips_source_map(magick7_source->source, &length))) - return -1; + 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. */ - magick_sniff_bytes(magick7->image_info, data, length); - magick7->image = BlobToImage(magick7->image_info, data, length, - magick7->exception); if (!magick7->image) { vips_foreign_load_magick7_error(magick7); return -1; 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 From 0a9fe3b1e43f13ded50180ef7366ba5b5c935574 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 28 Jun 2025 17:40:09 +0100 Subject: [PATCH 6/7] add C API wrapper --- libvips/foreign/magickload.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) 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; +} From 158eb0a5bb31530ddf912efe9c9a32c0d7001cc5 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 28 Jun 2025 18:27:03 +0100 Subject: [PATCH 7/7] fix imagemagick6 support --- libvips/foreign/magick6load.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libvips/foreign/magick6load.c b/libvips/foreign/magick6load.c index e7945ea1b1..b809fae301 100644 --- a/libvips/foreign/magick6load.c +++ b/libvips/foreign/magick6load.c @@ -1120,15 +1120,15 @@ vips_foreign_load_magick_source_header(VipsForeignLoad *load) const char *filename = vips_connection_filename(VIPS_CONNECTION(magick_source->source)); - g_strlcpy(magick7->image_info->filename, filename, MagickPathExtent); - magick_sniff_file(magick7->image_info, filename); - magick7->image = ReadImage(magick7->image_info, magick7->exception); + 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(raw->source, &length))) + 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, pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

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:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy