Skip to content

add magickload_source #4592

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jun 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 4 additions & 0 deletions libvips/foreign/foreign.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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)*/

Expand Down
107 changes: 107 additions & 0 deletions libvips/foreign/magick6load.c
Original file line number Diff line number Diff line change
Expand Up @@ -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*/
106 changes: 106 additions & 0 deletions libvips/foreign/magick7load.c
Original file line number Diff line number Diff line change
Expand Up @@ -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*/
32 changes: 32 additions & 0 deletions libvips/foreign/magickload.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
3 changes: 3 additions & 0 deletions libvips/include/vips/foreign.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions libvips/module/magick.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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*/

Expand Down
6 changes: 5 additions & 1 deletion test/test-suite/test_foreign.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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):
Expand Down
Loading
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