Build a dynamically loadable module for *magick
By default, a dynamically loadable module is built for *magick (i.e. `--with-magick=module`) when: * ImageMagick or GraphicsMagick is found; * GModule is supported (`gmodule_supported` pkg-config variable). This can be overridden on the command line with: * `--without-magick` - to disable *magick usage; * `--with-magick[=yes]` - to restore the previous behavior; * `--disable-modules` - to disable the build of dynamic modules.
This commit is contained in:
parent
0c0b68b4b9
commit
34c0f31643
29
configure.ac
29
configure.ac
@ -535,7 +535,14 @@ VIPS_LIBS="$VIPS_LIBS $FFTW_LIBS"
|
||||
|
||||
# ImageMagick
|
||||
AC_ARG_WITH([magick],
|
||||
AS_HELP_STRING([--without-magick], [build without libMagic (default: test)]))
|
||||
AS_HELP_STRING([--without-magick], [build without libMagic (default: test)]),
|
||||
[with_magick=$withval],
|
||||
[with_magick=$gmodule_with_flag])
|
||||
|
||||
# libMagic as a dynamically loadable module
|
||||
AS_IF([test x"$with_magick" = x"module"],
|
||||
[with_magick_module=$gmodule_supported_flag],
|
||||
[with_magick_module=no])
|
||||
|
||||
AC_ARG_WITH([magickpackage],
|
||||
AS_HELP_STRING([--with-magickpackage],
|
||||
@ -552,6 +559,7 @@ if test x"$with_magickpackage" = x""; then
|
||||
],
|
||||
[AC_MSG_WARN([neither MagickCore nor ImageMagick found; disabling Magick support])
|
||||
with_magick=no
|
||||
with_magick_module=no
|
||||
]
|
||||
)
|
||||
]
|
||||
@ -568,23 +576,27 @@ if test x"$with_magick" != x"no"; then
|
||||
with_magick=yes
|
||||
magick7=yes
|
||||
magick_version=magick7
|
||||
PACKAGES_USED="$PACKAGES_USED $with_magickpackage"
|
||||
AS_IF([test x"$with_magick_module" = x"no"],
|
||||
[PACKAGES_USED="$PACKAGES_USED $with_magickpackage"])
|
||||
],
|
||||
[PKG_CHECK_MODULES(MAGICK, $with_magickpackage,
|
||||
[AC_DEFINE(HAVE_MAGICK6,1,[define if you have libMagick6 installed.])
|
||||
with_magick=yes
|
||||
magick6=yes
|
||||
magick_version=magick6
|
||||
PACKAGES_USED="$PACKAGES_USED $with_magickpackage"
|
||||
AS_IF([test x"$with_magick_module" = x"no"],
|
||||
[PACKAGES_USED="$PACKAGES_USED $with_magickpackage"])
|
||||
],
|
||||
[AC_MSG_WARN([$with_magickpackage not found; disabling Magick support])
|
||||
with_magick=no
|
||||
with_magick_module=no
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
else
|
||||
with_magick=no
|
||||
with_magick_module=no
|
||||
magick6=no
|
||||
magick_version=none
|
||||
with_magickpackage=none
|
||||
@ -692,8 +704,11 @@ else
|
||||
enable_magicksave=no
|
||||
fi
|
||||
|
||||
VIPS_CFLAGS="$VIPS_CFLAGS $MAGICK_CFLAGS"
|
||||
VIPS_LIBS="$VIPS_LIBS $MAGICK_LIBS"
|
||||
AS_IF([test x"$with_magick_module" = x"yes"],
|
||||
[AC_DEFINE([MAGICK_MODULE], [1], [define to build libMagic as a dynamically loadable module.])],
|
||||
[VIPS_CFLAGS="$VIPS_CFLAGS $MAGICK_CFLAGS"
|
||||
VIPS_LIBS="$VIPS_LIBS $MAGICK_LIBS"])
|
||||
AM_CONDITIONAL(MAGICK_MODULE, [test x"$with_magick_module" = x"yes"])
|
||||
|
||||
# orc
|
||||
AC_ARG_WITH([orc],
|
||||
@ -1425,7 +1440,7 @@ slide load with OpenSlide: $with_openslide (dynamic module: $with_openslide_modu
|
||||
Matlab load with matio: $with_matio, \
|
||||
NIfTI load/save with niftiio: $with_nifti, \
|
||||
FITS load/save with cfitsio: $with_cfitsio, \
|
||||
Magick package: $with_magickpackage, \
|
||||
Magick package: $with_magickpackage (dynamic module: $with_magick_module), \
|
||||
Magick API version: $magick_version, \
|
||||
load with libMagickCore: $enable_magickload, \
|
||||
save with libMagickCore: $enable_magicksave"
|
||||
@ -1541,7 +1556,7 @@ slide load with OpenSlide: $with_openslide (dynamic module: $with_o
|
||||
Matlab load with matio: $with_matio
|
||||
NIfTI load/save with niftiio: $with_nifti
|
||||
FITS load/save with cfitsio: $with_cfitsio
|
||||
Magick package: $with_magickpackage
|
||||
Magick package: $with_magickpackage (dynamic module: $with_magick_module)
|
||||
Magick API version: $magick_version
|
||||
load with libMagickCore: $enable_magickload
|
||||
save with libMagickCore: $enable_magicksave
|
||||
|
@ -82,6 +82,10 @@ module_LTLIBRARIES =
|
||||
|
||||
moduledir = @VIPS_LIBDIR@/vips-modules-@VIPS_MAJOR_VERSION@.@VIPS_MINOR_VERSION@
|
||||
|
||||
if MAGICK_MODULE
|
||||
module_LTLIBRARIES += vips-magick.la
|
||||
endif # MAGICK_MODULE
|
||||
|
||||
if HEIF_MODULE
|
||||
module_LTLIBRARIES += vips-heif.la
|
||||
endif # HEIF_MODULE
|
||||
@ -112,6 +116,18 @@ MODULE_LIBADD = \
|
||||
# dynamically loadable module. The C definitions are always
|
||||
# included in the main library.
|
||||
|
||||
vips_magick_la_SOURCES = \
|
||||
module/magick.c \
|
||||
foreign/magick.c \
|
||||
foreign/magick.h \
|
||||
foreign/magick2vips.c \
|
||||
foreign/magick6load.c \
|
||||
foreign/magick7load.c \
|
||||
foreign/vips2magick.c
|
||||
vips_magick_la_CPPFLAGS = $(MODULE_CPPFLAGS) $(MAGICK_CFLAGS)
|
||||
vips_magick_la_LDFLAGS = $(MODULE_LDFLAGS)
|
||||
vips_magick_la_LIBADD = $(MODULE_LIBADD) $(MAGICK_LIBS)
|
||||
|
||||
vips_heif_la_SOURCES = module/heif.c foreign/heif2vips.c foreign/vips2heif.c
|
||||
vips_heif_la_CPPFLAGS = $(MODULE_CPPFLAGS) $(HEIF_CFLAGS)
|
||||
vips_heif_la_LDFLAGS = $(MODULE_LDFLAGS)
|
||||
|
@ -25,10 +25,6 @@ libforeign_la_SOURCES = \
|
||||
jpeg.h \
|
||||
jpegload.c \
|
||||
jpegsave.c \
|
||||
magick2vips.c \
|
||||
magick7load.c \
|
||||
magick.c \
|
||||
magick.h \
|
||||
magickload.c \
|
||||
magicksave.c \
|
||||
matlab.c \
|
||||
@ -74,6 +70,16 @@ libforeign_la_SOURCES = \
|
||||
# We still need to include the GObject part of a loader/saver
|
||||
# if it is not built as a dynamically loadable module.
|
||||
|
||||
if !MAGICK_MODULE
|
||||
libforeign_la_SOURCES += \
|
||||
magick.c \
|
||||
magick.h \
|
||||
magick2vips.c \
|
||||
magick6load.c \
|
||||
magick7load.c \
|
||||
vips2magick.c
|
||||
endif # !MAGICK_MODULE
|
||||
|
||||
if !HEIF_MODULE
|
||||
libforeign_la_SOURCES += heif2vips.c vips2heif.c
|
||||
endif # !HEIF_MODULE
|
||||
|
@ -2327,7 +2327,7 @@ vips_foreign_operation_init( void )
|
||||
vips_foreign_load_openslide_source_get_type();
|
||||
#endif /*defined(HAVE_OPENSLIDE) && !defined(OPENSLIDE_MODULE)*/
|
||||
|
||||
#ifdef ENABLE_MAGICKLOAD
|
||||
#if defined(ENABLE_MAGICKLOAD) && !defined(MAGICK_MODULE)
|
||||
#ifdef HAVE_MAGICK6
|
||||
vips_foreign_load_magick_file_get_type();
|
||||
vips_foreign_load_magick_buffer_get_type();
|
||||
@ -2337,12 +2337,12 @@ vips_foreign_operation_init( void )
|
||||
vips_foreign_load_magick7_file_get_type();
|
||||
vips_foreign_load_magick7_buffer_get_type();
|
||||
#endif /*HAVE_MAGICK7*/
|
||||
#endif /*ENABLE_MAGICKLOAD*/
|
||||
#endif /*defined(ENABLE_MAGICKLOAD) && !defined(MAGICK_MODULE)*/
|
||||
|
||||
#ifdef ENABLE_MAGICKSAVE
|
||||
#if defined(ENABLE_MAGICKSAVE) && !defined(MAGICK_MODULE)
|
||||
vips_foreign_save_magick_file_get_type();
|
||||
vips_foreign_save_magick_buffer_get_type();
|
||||
#endif /*ENABLE_MAGICKSAVE*/
|
||||
#endif /*defined(ENABLE_MAGICKSAVE) && !defined(MAGICK_MODULE)*/
|
||||
|
||||
#ifdef HAVE_CFITSIO
|
||||
vips_foreign_load_fits_file_get_type();
|
||||
|
324
libvips/foreign/magick6load.c
Normal file
324
libvips/foreign/magick6load.c
Normal file
@ -0,0 +1,324 @@
|
||||
/* load with libMagick
|
||||
*
|
||||
* 5/12/11
|
||||
* - from openslideload.c
|
||||
* 17/1/12
|
||||
* - remove header-only loads
|
||||
* 11/6/13
|
||||
* - add @all_frames option, off by default
|
||||
* 14/2/16
|
||||
* - add @page option, 0 by default
|
||||
* 25/11/16
|
||||
* - add @n, deprecate @all_frames (just sets n = -1)
|
||||
* 8/9/17
|
||||
* - don't cache magickload
|
||||
* 21/4/21 kleisauke
|
||||
* - include GObject part from magickload.c
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
#define DEBUG
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
#include <vips/buf.h>
|
||||
#include <vips/internal.h>
|
||||
|
||||
#ifdef ENABLE_MAGICKLOAD
|
||||
|
||||
#ifdef HAVE_MAGICK6
|
||||
|
||||
#include "pforeign.h"
|
||||
#include "magick.h"
|
||||
|
||||
typedef struct _VipsForeignLoadMagick {
|
||||
VipsForeignLoad parent_object;
|
||||
|
||||
/* Deprecated. Just sets n = -1.
|
||||
*/
|
||||
gboolean all_frames;
|
||||
|
||||
char *density; /* Load at this resolution */
|
||||
int page; /* Load this page (frame) */
|
||||
int n; /* Load this many pages */
|
||||
|
||||
} VipsForeignLoadMagick;
|
||||
|
||||
typedef VipsForeignLoadClass VipsForeignLoadMagickClass;
|
||||
|
||||
G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadMagick, vips_foreign_load_magick,
|
||||
VIPS_TYPE_FOREIGN_LOAD );
|
||||
|
||||
static VipsForeignFlags
|
||||
vips_foreign_load_magick_get_flags_filename( const char *filename )
|
||||
{
|
||||
return( VIPS_FOREIGN_PARTIAL );
|
||||
}
|
||||
|
||||
static VipsForeignFlags
|
||||
vips_foreign_load_magick_get_flags( VipsForeignLoad *load )
|
||||
{
|
||||
return( VIPS_FOREIGN_PARTIAL );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_magick_class_init( VipsForeignLoadMagickClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( 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_base";
|
||||
object_class->description = _( "load with ImageMagick" );
|
||||
|
||||
/* Don't cache magickload: it can gobble up memory and disc.
|
||||
*/
|
||||
operation_class->flags = VIPS_OPERATION_NOCACHE;
|
||||
|
||||
/* We need to be well to the back of the queue since vips's
|
||||
* dedicated loaders are usually preferable.
|
||||
*/
|
||||
foreign_class->priority = -100;
|
||||
|
||||
load_class->get_flags_filename =
|
||||
vips_foreign_load_magick_get_flags_filename;
|
||||
load_class->get_flags = vips_foreign_load_magick_get_flags;
|
||||
|
||||
VIPS_ARG_BOOL( class, "all_frames", 20,
|
||||
_( "all_frames" ),
|
||||
_( "Read all frames from an image" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT | VIPS_ARGUMENT_DEPRECATED,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadMagick, all_frames ),
|
||||
FALSE );
|
||||
|
||||
VIPS_ARG_STRING( class, "density", 21,
|
||||
_( "Density" ),
|
||||
_( "Canvas resolution for rendering vector formats like SVG" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadMagick, density ),
|
||||
NULL );
|
||||
|
||||
VIPS_ARG_INT( class, "page", 22,
|
||||
_( "Page" ),
|
||||
_( "Load this page from the file" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadMagick, page ),
|
||||
0, 100000, 0 );
|
||||
|
||||
VIPS_ARG_INT( class, "n", 23,
|
||||
_( "n" ),
|
||||
_( "Load this many pages" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadMagick, n ),
|
||||
-1, 100000, 1 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_magick_init( VipsForeignLoadMagick *magick )
|
||||
{
|
||||
magick->n = 1;
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignLoadMagickFile {
|
||||
VipsForeignLoadMagick parent_object;
|
||||
|
||||
char *filename;
|
||||
|
||||
} VipsForeignLoadMagickFile;
|
||||
|
||||
typedef VipsForeignLoadMagickClass VipsForeignLoadMagickFileClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsForeignLoadMagickFile, vips_foreign_load_magick_file,
|
||||
vips_foreign_load_magick_get_type() );
|
||||
|
||||
static gboolean
|
||||
ismagick( const char *filename )
|
||||
{
|
||||
/* Fetch up to the first 100 bytes. Hopefully that'll be enough.
|
||||
*/
|
||||
unsigned char buf[100];
|
||||
int len;
|
||||
|
||||
return( (len = vips__get_bytes( filename, buf, 100 )) > 10 &&
|
||||
magick_ismagick( buf, len ) );
|
||||
}
|
||||
|
||||
/* 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_file_header( VipsForeignLoad *load )
|
||||
{
|
||||
VipsForeignLoadMagick *magick = (VipsForeignLoadMagick *) load;
|
||||
VipsForeignLoadMagickFile *magick_file =
|
||||
(VipsForeignLoadMagickFile *) load;
|
||||
|
||||
if( magick->all_frames )
|
||||
magick->n = -1;
|
||||
|
||||
if( vips__magick_read( magick_file->filename,
|
||||
load->out, magick->density,
|
||||
magick->page, magick->n ) )
|
||||
return( -1 );
|
||||
|
||||
VIPS_SETSTR( load->out->filename, magick_file->filename );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_magick_file_class_init(
|
||||
VipsForeignLoadMagickFileClass *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";
|
||||
object_class->description = _( "load file with ImageMagick" );
|
||||
|
||||
load_class->is_a = ismagick;
|
||||
load_class->header = vips_foreign_load_magick_file_header;
|
||||
load_class->load = NULL;
|
||||
|
||||
VIPS_ARG_STRING( class, "filename", 1,
|
||||
_( "Filename" ),
|
||||
_( "Filename to load from" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadMagickFile, filename ),
|
||||
NULL );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_magick_file_init( VipsForeignLoadMagickFile *magick_file )
|
||||
{
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignLoadMagickBuffer {
|
||||
VipsForeignLoadMagick parent_object;
|
||||
|
||||
VipsArea *buf;
|
||||
|
||||
} VipsForeignLoadMagickBuffer;
|
||||
|
||||
typedef VipsForeignLoadMagickClass VipsForeignLoadMagickBufferClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsForeignLoadMagickBuffer, vips_foreign_load_magick_buffer,
|
||||
vips_foreign_load_magick_get_type() );
|
||||
|
||||
static gboolean
|
||||
vips_foreign_load_magick_buffer_is_a_buffer( const void *buf, size_t len )
|
||||
{
|
||||
return( len > 10 && magick_ismagick( (const unsigned char *) buf, len ) );
|
||||
}
|
||||
|
||||
/* 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_buffer_header( VipsForeignLoad *load )
|
||||
{
|
||||
VipsForeignLoadMagick *magick = (VipsForeignLoadMagick *) load;
|
||||
VipsForeignLoadMagickBuffer *magick_buffer =
|
||||
(VipsForeignLoadMagickBuffer *) load;
|
||||
|
||||
if( magick->all_frames )
|
||||
magick->n = -1;
|
||||
|
||||
if( vips__magick_read_buffer(
|
||||
magick_buffer->buf->data, magick_buffer->buf->length,
|
||||
load->out, magick->density, magick->page,
|
||||
magick->n ) )
|
||||
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;
|
||||
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" );
|
||||
|
||||
load_class->is_a_buffer = vips_foreign_load_magick_buffer_is_a_buffer;
|
||||
load_class->header = vips_foreign_load_magick_buffer_header;
|
||||
load_class->load = NULL;
|
||||
|
||||
VIPS_ARG_BOXED( class, "buffer", 1,
|
||||
_( "Buffer" ),
|
||||
_( "Buffer to load from" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadMagickBuffer, buf ),
|
||||
VIPS_TYPE_BLOB );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_magick_buffer_init( VipsForeignLoadMagickBuffer *buffer )
|
||||
{
|
||||
}
|
||||
|
||||
#endif /*HAVE_MAGICK6*/
|
||||
|
||||
#endif /*ENABLE_MAGICKLOAD*/
|
@ -12,6 +12,8 @@
|
||||
* - add @n, deprecate @all_frames (just sets n = -1)
|
||||
* 8/9/17
|
||||
* - don't cache magickload
|
||||
* 21/4/21 kleisauke
|
||||
* - move GObject part to magick6load.c
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -58,269 +60,6 @@
|
||||
#include <vips/buf.h>
|
||||
#include <vips/internal.h>
|
||||
|
||||
#ifdef ENABLE_MAGICKLOAD
|
||||
|
||||
#ifdef HAVE_MAGICK6
|
||||
|
||||
#include "pforeign.h"
|
||||
#include "magick.h"
|
||||
|
||||
typedef struct _VipsForeignLoadMagick {
|
||||
VipsForeignLoad parent_object;
|
||||
|
||||
/* Deprecated. Just sets n = -1.
|
||||
*/
|
||||
gboolean all_frames;
|
||||
|
||||
char *density; /* Load at this resolution */
|
||||
int page; /* Load this page (frame) */
|
||||
int n; /* Load this many pages */
|
||||
|
||||
} VipsForeignLoadMagick;
|
||||
|
||||
typedef VipsForeignLoadClass VipsForeignLoadMagickClass;
|
||||
|
||||
G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadMagick, vips_foreign_load_magick,
|
||||
VIPS_TYPE_FOREIGN_LOAD );
|
||||
|
||||
static VipsForeignFlags
|
||||
vips_foreign_load_magick_get_flags_filename( const char *filename )
|
||||
{
|
||||
return( VIPS_FOREIGN_PARTIAL );
|
||||
}
|
||||
|
||||
static VipsForeignFlags
|
||||
vips_foreign_load_magick_get_flags( VipsForeignLoad *load )
|
||||
{
|
||||
return( VIPS_FOREIGN_PARTIAL );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_magick_class_init( VipsForeignLoadMagickClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( 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_base";
|
||||
object_class->description = _( "load with ImageMagick" );
|
||||
|
||||
/* Don't cache magickload: it can gobble up memory and disc.
|
||||
*/
|
||||
operation_class->flags = VIPS_OPERATION_NOCACHE;
|
||||
|
||||
/* We need to be well to the back of the queue since vips's
|
||||
* dedicated loaders are usually preferable.
|
||||
*/
|
||||
foreign_class->priority = -100;
|
||||
|
||||
load_class->get_flags_filename =
|
||||
vips_foreign_load_magick_get_flags_filename;
|
||||
load_class->get_flags = vips_foreign_load_magick_get_flags;
|
||||
|
||||
VIPS_ARG_BOOL( class, "all_frames", 20,
|
||||
_( "all_frames" ),
|
||||
_( "Read all frames from an image" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT | VIPS_ARGUMENT_DEPRECATED,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadMagick, all_frames ),
|
||||
FALSE );
|
||||
|
||||
VIPS_ARG_STRING( class, "density", 21,
|
||||
_( "Density" ),
|
||||
_( "Canvas resolution for rendering vector formats like SVG" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadMagick, density ),
|
||||
NULL );
|
||||
|
||||
VIPS_ARG_INT( class, "page", 22,
|
||||
_( "Page" ),
|
||||
_( "Load this page from the file" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadMagick, page ),
|
||||
0, 100000, 0 );
|
||||
|
||||
VIPS_ARG_INT( class, "n", 23,
|
||||
_( "n" ),
|
||||
_( "Load this many pages" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadMagick, n ),
|
||||
-1, 100000, 1 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_magick_init( VipsForeignLoadMagick *magick )
|
||||
{
|
||||
magick->n = 1;
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignLoadMagickFile {
|
||||
VipsForeignLoadMagick parent_object;
|
||||
|
||||
char *filename;
|
||||
|
||||
} VipsForeignLoadMagickFile;
|
||||
|
||||
typedef VipsForeignLoadMagickClass VipsForeignLoadMagickFileClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsForeignLoadMagickFile, vips_foreign_load_magick_file,
|
||||
vips_foreign_load_magick_get_type() );
|
||||
|
||||
static gboolean
|
||||
ismagick( const char *filename )
|
||||
{
|
||||
/* Fetch up to the first 100 bytes. Hopefully that'll be enough.
|
||||
*/
|
||||
unsigned char buf[100];
|
||||
int len;
|
||||
|
||||
return( (len = vips__get_bytes( filename, buf, 100 )) > 10 &&
|
||||
magick_ismagick( buf, len ) );
|
||||
}
|
||||
|
||||
/* 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_file_header( VipsForeignLoad *load )
|
||||
{
|
||||
VipsForeignLoadMagick *magick = (VipsForeignLoadMagick *) load;
|
||||
VipsForeignLoadMagickFile *magick_file =
|
||||
(VipsForeignLoadMagickFile *) load;
|
||||
|
||||
if( magick->all_frames )
|
||||
magick->n = -1;
|
||||
|
||||
if( vips__magick_read( magick_file->filename,
|
||||
load->out, magick->density,
|
||||
magick->page, magick->n ) )
|
||||
return( -1 );
|
||||
|
||||
VIPS_SETSTR( load->out->filename, magick_file->filename );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_magick_file_class_init(
|
||||
VipsForeignLoadMagickFileClass *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";
|
||||
object_class->description = _( "load file with ImageMagick" );
|
||||
|
||||
load_class->is_a = ismagick;
|
||||
load_class->header = vips_foreign_load_magick_file_header;
|
||||
load_class->load = NULL;
|
||||
|
||||
VIPS_ARG_STRING( class, "filename", 1,
|
||||
_( "Filename" ),
|
||||
_( "Filename to load from" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadMagickFile, filename ),
|
||||
NULL );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_magick_file_init( VipsForeignLoadMagickFile *magick_file )
|
||||
{
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignLoadMagickBuffer {
|
||||
VipsForeignLoadMagick parent_object;
|
||||
|
||||
VipsArea *buf;
|
||||
|
||||
} VipsForeignLoadMagickBuffer;
|
||||
|
||||
typedef VipsForeignLoadMagickClass VipsForeignLoadMagickBufferClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsForeignLoadMagickBuffer, vips_foreign_load_magick_buffer,
|
||||
vips_foreign_load_magick_get_type() );
|
||||
|
||||
static gboolean
|
||||
vips_foreign_load_magick_buffer_is_a_buffer( const void *buf, size_t len )
|
||||
{
|
||||
return( len > 10 && magick_ismagick( (const unsigned char *) buf, len ) );
|
||||
}
|
||||
|
||||
/* 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_buffer_header( VipsForeignLoad *load )
|
||||
{
|
||||
VipsForeignLoadMagick *magick = (VipsForeignLoadMagick *) load;
|
||||
VipsForeignLoadMagickBuffer *magick_buffer =
|
||||
(VipsForeignLoadMagickBuffer *) load;
|
||||
|
||||
if( magick->all_frames )
|
||||
magick->n = -1;
|
||||
|
||||
if( vips__magick_read_buffer(
|
||||
magick_buffer->buf->data, magick_buffer->buf->length,
|
||||
load->out, magick->density, magick->page,
|
||||
magick->n ) )
|
||||
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;
|
||||
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" );
|
||||
|
||||
load_class->is_a_buffer = vips_foreign_load_magick_buffer_is_a_buffer;
|
||||
load_class->header = vips_foreign_load_magick_buffer_header;
|
||||
load_class->load = NULL;
|
||||
|
||||
VIPS_ARG_BOXED( class, "buffer", 1,
|
||||
_( "Buffer" ),
|
||||
_( "Buffer to load from" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadMagickBuffer, buf ),
|
||||
VIPS_TYPE_BLOB );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_magick_buffer_init( VipsForeignLoadMagickBuffer *buffer )
|
||||
{
|
||||
}
|
||||
|
||||
#endif /*HAVE_MAGICK6*/
|
||||
|
||||
#endif /*ENABLE_MAGICKLOAD*/
|
||||
|
||||
/**
|
||||
* vips_magickload:
|
||||
* @filename: file to load
|
||||
|
@ -12,6 +12,8 @@
|
||||
* - support array of delays
|
||||
* 5/8/19 DarthSim
|
||||
* - support GIF optimization
|
||||
* 21/4/21 kleisauke
|
||||
* - move GObject part to vips2magick.c
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -52,584 +54,6 @@
|
||||
|
||||
#include <vips/vips.h>
|
||||
|
||||
#ifdef ENABLE_MAGICKSAVE
|
||||
|
||||
#include "pforeign.h"
|
||||
#include "magick.h"
|
||||
|
||||
typedef struct _VipsForeignSaveMagick {
|
||||
VipsForeignSave parent_object;
|
||||
|
||||
/* Parameters.
|
||||
*/
|
||||
char *filename; /* NULL during buffer output */
|
||||
char *format;
|
||||
int quality;
|
||||
gboolean optimize_gif_frames;
|
||||
gboolean optimize_gif_transparency;
|
||||
|
||||
ImageInfo *image_info;
|
||||
ExceptionInfo *exception;
|
||||
char *map;
|
||||
StorageType storage_type;
|
||||
Image *images;
|
||||
Image *current_image;
|
||||
|
||||
int page_height;
|
||||
GValue delay_gvalue;
|
||||
int *delays;
|
||||
int delays_length;
|
||||
|
||||
/* The position of current_image in the output.
|
||||
*/
|
||||
VipsRect position;
|
||||
|
||||
} VipsForeignSaveMagick;
|
||||
|
||||
typedef VipsForeignSaveClass VipsForeignSaveMagickClass;
|
||||
|
||||
G_DEFINE_ABSTRACT_TYPE( VipsForeignSaveMagick, vips_foreign_save_magick,
|
||||
VIPS_TYPE_FOREIGN_SAVE );
|
||||
|
||||
static void
|
||||
vips_foreign_save_magick_dispose( GObject *gobject )
|
||||
{
|
||||
VipsForeignSaveMagick *magick = (VipsForeignSaveMagick *) gobject;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "vips_foreign_save_magick_dispose: %p\n", gobject );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
VIPS_FREE( magick->map );
|
||||
VIPS_FREEF( DestroyImageList, magick->images );
|
||||
VIPS_FREEF( DestroyImageInfo, magick->image_info );
|
||||
VIPS_FREEF( magick_destroy_exception, magick->exception );
|
||||
g_value_unset( &magick->delay_gvalue );
|
||||
|
||||
G_OBJECT_CLASS( vips_foreign_save_magick_parent_class )->
|
||||
dispose( gobject );
|
||||
}
|
||||
|
||||
/* Move current_image on to the next image we will write.
|
||||
*/
|
||||
static int
|
||||
vips_foreign_save_magick_next_image( VipsForeignSaveMagick *magick )
|
||||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( magick );
|
||||
VipsForeignSave *save = (VipsForeignSave *) magick;
|
||||
VipsImage *im = save->ready;
|
||||
|
||||
Image *image;
|
||||
int number;
|
||||
const char *str;
|
||||
int page_index;
|
||||
|
||||
g_assert( !magick->current_image );
|
||||
|
||||
if( magick->images == NULL ) {
|
||||
if( !(image = magick_acquire_image( magick->image_info,
|
||||
magick->exception )) )
|
||||
return( -1 );
|
||||
|
||||
magick->images = image;
|
||||
magick->position.top = 0;
|
||||
magick->position.left = 0;
|
||||
magick->position.width = im->Xsize;
|
||||
magick->position.height = magick->page_height;
|
||||
}
|
||||
else {
|
||||
image = GetLastImageInList( magick->images );
|
||||
magick_acquire_next_image( magick->image_info, image,
|
||||
magick->exception );
|
||||
if( GetNextImageInList( image ) == NULL )
|
||||
return( -1 );
|
||||
|
||||
image = SyncNextImageInList( image );
|
||||
magick->position.top += magick->page_height;
|
||||
}
|
||||
|
||||
if( !magick_set_image_size( image,
|
||||
im->Xsize, magick->page_height, magick->exception ) ) {
|
||||
magick_vips_error( class->nickname, magick->exception );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* Delay must be converted from milliseconds into centiseconds
|
||||
* as GIF image requires centiseconds.
|
||||
*/
|
||||
if( magick->delays ) {
|
||||
page_index = magick->position.top / magick->page_height;
|
||||
if( page_index < magick->delays_length )
|
||||
image->delay =
|
||||
VIPS_RINT( magick->delays[page_index] / 10.0 );
|
||||
}
|
||||
|
||||
/* ImageMagick uses iterations like this (at least in gif save):
|
||||
* 0 - set 0 loops (infinite)
|
||||
* 1 - don't write the netscape extension block
|
||||
* 2 - loop once
|
||||
* 3 - loop twice etc.
|
||||
*/
|
||||
if( vips_image_get_typeof( im, "loop" ) &&
|
||||
!vips_image_get_int( im, "loop", &number ) ) {
|
||||
image->iterations = (size_t) number;
|
||||
}
|
||||
else {
|
||||
/* DEPRECATED "gif-loop"
|
||||
*
|
||||
* We have the simple gif meaning, so we must add one unless
|
||||
* it's zero.
|
||||
*/
|
||||
if( vips_image_get_typeof( im, "gif-loop" ) &&
|
||||
!vips_image_get_int( im, "gif-loop", &number ) )
|
||||
image->iterations = (size_t) (number ? number + 1 : 0);
|
||||
}
|
||||
|
||||
if( vips_image_get_typeof( im, "gif-comment" ) &&
|
||||
!vips_image_get_string( im, "gif-comment", &str ) )
|
||||
magick_set_property( image, "comment", str, magick->exception );
|
||||
|
||||
/* libvips keeps animations as a set of independent frames, so we want
|
||||
* to clear to the background between each one.
|
||||
*/
|
||||
image->dispose = BackgroundDispose;
|
||||
|
||||
if( !save->strip &&
|
||||
magick_set_magick_profile( image, im, magick->exception ) ) {
|
||||
magick_vips_error( class->nickname, magick->exception );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
magick->current_image = image;
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* We've written all the pixels to current_image ... finish it off ready to
|
||||
* move on.
|
||||
*/
|
||||
static void
|
||||
vips_foreign_save_magick_end_image( VipsForeignSaveMagick *magick )
|
||||
{
|
||||
if( magick->current_image ) {
|
||||
magick_inherit_exception( magick->exception,
|
||||
magick->current_image );
|
||||
magick->current_image = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Another block of pixels have arrived from libvips.
|
||||
*/
|
||||
static int
|
||||
vips_foreign_save_magick_write_block( VipsRegion *region, VipsRect *area,
|
||||
void *a )
|
||||
{
|
||||
VipsForeignSaveMagick *magick = (VipsForeignSaveMagick *) a;
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( magick );
|
||||
|
||||
VipsRect pixels;
|
||||
|
||||
pixels = region->valid;
|
||||
do {
|
||||
VipsRect hit;
|
||||
void *p;
|
||||
|
||||
if( !magick->current_image &&
|
||||
vips_foreign_save_magick_next_image( magick ) )
|
||||
return( -1 );
|
||||
|
||||
vips_rect_intersectrect( &pixels, &magick->position, &hit );
|
||||
p = VIPS_REGION_ADDR( region, hit.left, hit.top );
|
||||
if( !magick_import_pixels( magick->current_image,
|
||||
hit.left, hit.top - magick->position.top,
|
||||
hit.width, hit.height,
|
||||
magick->map, magick->storage_type,
|
||||
p,
|
||||
magick->exception ) ) {
|
||||
magick_vips_error( class->nickname,
|
||||
magick->exception );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* Have we filled the page.
|
||||
*/
|
||||
if( VIPS_RECT_BOTTOM( &hit ) ==
|
||||
VIPS_RECT_BOTTOM( &magick->position ) )
|
||||
vips_foreign_save_magick_end_image( magick );
|
||||
|
||||
pixels.top += hit.height;
|
||||
pixels.height -= hit.height;
|
||||
} while( pixels.height > 0 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_foreign_save_magick_build( VipsObject *object )
|
||||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
|
||||
VipsForeignSave *save = (VipsForeignSave *) object;
|
||||
VipsForeignSaveMagick *magick = (VipsForeignSaveMagick *) object;
|
||||
|
||||
VipsImage *im;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "vips_foreign_save_magick_build: %p\n", object );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_magick_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
magick_genesis();
|
||||
|
||||
/* The image to save.
|
||||
*/
|
||||
im = save->ready;
|
||||
|
||||
magick->exception = magick_acquire_exception();
|
||||
magick->image_info = CloneImageInfo( NULL );
|
||||
|
||||
switch( im->BandFmt ) {
|
||||
case VIPS_FORMAT_UCHAR:
|
||||
magick->storage_type = CharPixel;
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_USHORT:
|
||||
magick->storage_type = ShortPixel;
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_UINT:
|
||||
magick->storage_type = LongPixel;
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_FLOAT:
|
||||
magick->storage_type = FloatPixel;
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_DOUBLE:
|
||||
magick->storage_type = DoublePixel;
|
||||
break;
|
||||
|
||||
default:
|
||||
vips_error( class->nickname,
|
||||
"%s", _( "unsupported image format" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
switch( im->Bands ) {
|
||||
case 1:
|
||||
magick->map = g_strdup( "I" );
|
||||
break;
|
||||
|
||||
case 2:
|
||||
magick->map = g_strdup( "IA" );
|
||||
break;
|
||||
|
||||
case 3:
|
||||
magick->map = g_strdup( "RGB" );
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if( im->Type == VIPS_INTERPRETATION_CMYK )
|
||||
magick->map = g_strdup( "CMYK" );
|
||||
else
|
||||
magick->map = g_strdup( "RGBA" );
|
||||
break;
|
||||
|
||||
case 5:
|
||||
magick->map = g_strdup( "CMYKA" );
|
||||
break;
|
||||
|
||||
default:
|
||||
vips_error( class->nickname,
|
||||
"%s", _( "unsupported number of image bands" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( magick->format ) {
|
||||
vips_strncpy( magick->image_info->magick,
|
||||
magick->format, MaxPathExtent );
|
||||
if( magick->filename )
|
||||
(void) vips_snprintf( magick->image_info->filename,
|
||||
MaxPathExtent, "%s:%s",
|
||||
magick->format, magick->filename );
|
||||
}
|
||||
else if( magick->filename ) {
|
||||
vips_strncpy( magick->image_info->filename,
|
||||
magick->filename, MaxPathExtent );
|
||||
}
|
||||
|
||||
if( magick->quality > 0 )
|
||||
magick->image_info->quality = magick->quality;
|
||||
|
||||
magick->page_height = vips_image_get_page_height( im );
|
||||
|
||||
/* Get as a gvalue so we can keep a ref to the delay array while we
|
||||
* need it.
|
||||
*/
|
||||
if( vips_image_get_typeof( im, "delay" ) ) {
|
||||
g_value_unset( &magick->delay_gvalue );
|
||||
if( vips_image_get( im, "delay", &magick->delay_gvalue ) )
|
||||
return( -1 );
|
||||
magick->delays = vips_value_get_array_int(
|
||||
&magick->delay_gvalue, &magick->delays_length );
|
||||
}
|
||||
|
||||
if( vips_sink_disc( im,
|
||||
vips_foreign_save_magick_write_block, magick ) )
|
||||
return( -1 );
|
||||
|
||||
if( magick->optimize_gif_frames ) {
|
||||
if( !magick_optimize_image_layers( &magick->images,
|
||||
magick->exception ) ) {
|
||||
magick_inherit_exception( magick->exception,
|
||||
magick->images );
|
||||
magick_vips_error( class->nickname, magick->exception );
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
|
||||
if( magick->optimize_gif_transparency ) {
|
||||
if( !magick_optimize_image_transparency( magick->images,
|
||||
magick->exception ) ) {
|
||||
magick_inherit_exception( magick->exception,
|
||||
magick->images );
|
||||
magick_vips_error( class->nickname, magick->exception );
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* We could call into libMagick and discover what save formats it supports, but
|
||||
* that would mean starting up libMagick on libvips init, and that would add a
|
||||
* lot of time.
|
||||
*
|
||||
* Instead, just list the commonly-used formats that all libMagicks support and
|
||||
* that libvips does not.
|
||||
*/
|
||||
static const char *vips__save_magick_suffs[] = { ".gif", ".bmp", NULL };
|
||||
|
||||
/* Save a bit of typing.
|
||||
*/
|
||||
#define UC VIPS_FORMAT_UCHAR
|
||||
#define US VIPS_FORMAT_USHORT
|
||||
#define UI VIPS_FORMAT_UINT
|
||||
#define F VIPS_FORMAT_FLOAT
|
||||
#define D VIPS_FORMAT_DOUBLE
|
||||
|
||||
static int bandfmt_magick[10] = {
|
||||
/* UC C US S UI I F X D DX */
|
||||
UC, UC, US, US, UI, UI, F, F, D, D
|
||||
};
|
||||
|
||||
static void
|
||||
vips_foreign_save_magick_class_init( VipsForeignSaveMagickClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
|
||||
VipsForeignSaveClass *save_class = (VipsForeignSaveClass *) class;
|
||||
|
||||
gobject_class->dispose = vips_foreign_save_magick_dispose;
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "magicksave_base";
|
||||
object_class->description = _( "save with ImageMagick" );
|
||||
object_class->build = vips_foreign_save_magick_build;
|
||||
|
||||
/* We need to be well to the back of the queue since vips's
|
||||
* dedicated savers are usually preferable.
|
||||
*/
|
||||
foreign_class->priority = -100;
|
||||
foreign_class->suffs = vips__save_magick_suffs;
|
||||
|
||||
save_class->saveable = VIPS_SAVEABLE_ANY;
|
||||
save_class->format_table = bandfmt_magick;
|
||||
|
||||
VIPS_ARG_STRING( class, "format", 2,
|
||||
_( "Format" ),
|
||||
_( "Format to save in" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveMagick, format ),
|
||||
NULL );
|
||||
|
||||
VIPS_ARG_INT( class, "quality", 3,
|
||||
_( "Quality" ),
|
||||
_( "Quality to use" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveMagick, quality ),
|
||||
0, 100, 0 );
|
||||
|
||||
VIPS_ARG_BOOL( class, "optimize_gif_frames", 4,
|
||||
_( "Optimize_gif_frames" ),
|
||||
_( "Apply GIF frames optimization" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveMagick, optimize_gif_frames ),
|
||||
FALSE );
|
||||
|
||||
VIPS_ARG_BOOL( class, "optimize_gif_transparency", 5,
|
||||
_( "Optimize_gif_transparency" ),
|
||||
_( "Apply GIF transparency optimization" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveMagick,
|
||||
optimize_gif_transparency ),
|
||||
FALSE );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_magick_init( VipsForeignSaveMagick *magick )
|
||||
{
|
||||
/* Init to an int just to have something there. It is swapped for an
|
||||
* int array later.
|
||||
*/
|
||||
g_value_init( &magick->delay_gvalue, G_TYPE_INT );
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignSaveMagickFile {
|
||||
VipsForeignSaveMagick parent_object;
|
||||
|
||||
char *filename;
|
||||
|
||||
} VipsForeignSaveMagickFile;
|
||||
|
||||
typedef VipsForeignSaveMagickClass VipsForeignSaveMagickFileClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsForeignSaveMagickFile, vips_foreign_save_magick_file,
|
||||
vips_foreign_save_magick_get_type() );
|
||||
|
||||
static int
|
||||
vips_foreign_save_magick_file_build( VipsObject *object )
|
||||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
|
||||
VipsForeignSaveMagick *magick = (VipsForeignSaveMagick *) object;
|
||||
VipsForeignSaveMagickFile *file = (VipsForeignSaveMagickFile *) object;
|
||||
|
||||
magick->filename = file->filename;
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_magick_file_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
if( !WriteImages( magick->image_info, magick->images,
|
||||
magick->image_info->filename, magick->exception ) ) {
|
||||
magick_inherit_exception( magick->exception, magick->images );
|
||||
magick_vips_error( class->nickname, magick->exception );
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_magick_file_class_init(
|
||||
VipsForeignSaveMagickFileClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "magicksave";
|
||||
object_class->description = _( "save file with ImageMagick" );
|
||||
object_class->build = vips_foreign_save_magick_file_build;
|
||||
|
||||
VIPS_ARG_STRING( class, "filename", 1,
|
||||
_( "Filename" ),
|
||||
_( "Filename to save to" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveMagickFile, filename ),
|
||||
NULL );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_magick_file_init( VipsForeignSaveMagickFile *file )
|
||||
{
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignSaveMagickBuffer {
|
||||
VipsForeignSaveMagick parent_object;
|
||||
|
||||
/* Save to a buffer.
|
||||
*/
|
||||
VipsArea *buf;
|
||||
|
||||
} VipsForeignSaveMagickBuffer;
|
||||
|
||||
typedef VipsForeignSaveMagickClass VipsForeignSaveMagickBufferClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsForeignSaveMagickBuffer, vips_foreign_save_magick_buffer,
|
||||
vips_foreign_save_magick_get_type() );
|
||||
|
||||
static int
|
||||
vips_foreign_save_magick_buffer_build( VipsObject *object )
|
||||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
|
||||
VipsForeignSaveMagick *magick = (VipsForeignSaveMagick *) object;
|
||||
VipsForeignSaveMagickBuffer *buffer =
|
||||
(VipsForeignSaveMagickBuffer *) object;
|
||||
|
||||
void *obuf;
|
||||
size_t olen;
|
||||
VipsBlob *blob;
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_magick_buffer_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
if( !(obuf = magick_images_to_blob( magick->image_info, magick->images,
|
||||
&olen, magick->exception )) ) {
|
||||
magick_inherit_exception( magick->exception, magick->images );
|
||||
magick_vips_error( class->nickname, magick->exception );
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
blob = vips_blob_new( (VipsCallbackFn) vips_area_free_cb, obuf, olen );
|
||||
g_object_set( buffer, "buffer", blob, NULL );
|
||||
vips_area_unref( VIPS_AREA( blob ) );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_magick_buffer_class_init(
|
||||
VipsForeignSaveMagickBufferClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "magicksave_buffer";
|
||||
object_class->description = _( "save image to magick buffer" );
|
||||
object_class->build = vips_foreign_save_magick_buffer_build;
|
||||
|
||||
VIPS_ARG_BOXED( class, "buffer", 1,
|
||||
_( "Buffer" ),
|
||||
_( "Buffer to save to" ),
|
||||
VIPS_ARGUMENT_REQUIRED_OUTPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveMagickBuffer, buf ),
|
||||
VIPS_TYPE_BLOB );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_magick_buffer_init( VipsForeignSaveMagickBuffer *buffer )
|
||||
{
|
||||
}
|
||||
|
||||
#endif /*ENABLE_MAGICKSAVE*/
|
||||
|
||||
/**
|
||||
* vips_magicksave: (method)
|
||||
* @in: image to save
|
||||
|
633
libvips/foreign/vips2magick.c
Normal file
633
libvips/foreign/vips2magick.c
Normal file
@ -0,0 +1,633 @@
|
||||
/* save with libMagick
|
||||
*
|
||||
* 22/12/17 dlemstra
|
||||
* 6/2/19 DarthSim
|
||||
* - fix GraphicsMagick support
|
||||
* 17/2/19
|
||||
* - support ICC, XMP, EXIF, IPTC metadata
|
||||
* - write with a single call to vips_sink_disc()
|
||||
* 29/6/19
|
||||
* - support "strip" option
|
||||
* 6/7/19 [deftomat]
|
||||
* - support array of delays
|
||||
* 5/8/19 DarthSim
|
||||
* - support GIF optimization
|
||||
* 21/4/21 kleisauke
|
||||
* - include GObject part from magicksave.c
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
|
||||
#ifdef ENABLE_MAGICKSAVE
|
||||
|
||||
#include "pforeign.h"
|
||||
#include "magick.h"
|
||||
|
||||
typedef struct _VipsForeignSaveMagick {
|
||||
VipsForeignSave parent_object;
|
||||
|
||||
/* Parameters.
|
||||
*/
|
||||
char *filename; /* NULL during buffer output */
|
||||
char *format;
|
||||
int quality;
|
||||
gboolean optimize_gif_frames;
|
||||
gboolean optimize_gif_transparency;
|
||||
|
||||
ImageInfo *image_info;
|
||||
ExceptionInfo *exception;
|
||||
char *map;
|
||||
StorageType storage_type;
|
||||
Image *images;
|
||||
Image *current_image;
|
||||
|
||||
int page_height;
|
||||
GValue delay_gvalue;
|
||||
int *delays;
|
||||
int delays_length;
|
||||
|
||||
/* The position of current_image in the output.
|
||||
*/
|
||||
VipsRect position;
|
||||
|
||||
} VipsForeignSaveMagick;
|
||||
|
||||
typedef VipsForeignSaveClass VipsForeignSaveMagickClass;
|
||||
|
||||
G_DEFINE_ABSTRACT_TYPE( VipsForeignSaveMagick, vips_foreign_save_magick,
|
||||
VIPS_TYPE_FOREIGN_SAVE );
|
||||
|
||||
static void
|
||||
vips_foreign_save_magick_dispose( GObject *gobject )
|
||||
{
|
||||
VipsForeignSaveMagick *magick = (VipsForeignSaveMagick *) gobject;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "vips_foreign_save_magick_dispose: %p\n", gobject );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
VIPS_FREE( magick->map );
|
||||
VIPS_FREEF( DestroyImageList, magick->images );
|
||||
VIPS_FREEF( DestroyImageInfo, magick->image_info );
|
||||
VIPS_FREEF( magick_destroy_exception, magick->exception );
|
||||
g_value_unset( &magick->delay_gvalue );
|
||||
|
||||
G_OBJECT_CLASS( vips_foreign_save_magick_parent_class )->
|
||||
dispose( gobject );
|
||||
}
|
||||
|
||||
/* Move current_image on to the next image we will write.
|
||||
*/
|
||||
static int
|
||||
vips_foreign_save_magick_next_image( VipsForeignSaveMagick *magick )
|
||||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( magick );
|
||||
VipsForeignSave *save = (VipsForeignSave *) magick;
|
||||
VipsImage *im = save->ready;
|
||||
|
||||
Image *image;
|
||||
int number;
|
||||
const char *str;
|
||||
int page_index;
|
||||
|
||||
g_assert( !magick->current_image );
|
||||
|
||||
if( magick->images == NULL ) {
|
||||
if( !(image = magick_acquire_image( magick->image_info,
|
||||
magick->exception )) )
|
||||
return( -1 );
|
||||
|
||||
magick->images = image;
|
||||
magick->position.top = 0;
|
||||
magick->position.left = 0;
|
||||
magick->position.width = im->Xsize;
|
||||
magick->position.height = magick->page_height;
|
||||
}
|
||||
else {
|
||||
image = GetLastImageInList( magick->images );
|
||||
magick_acquire_next_image( magick->image_info, image,
|
||||
magick->exception );
|
||||
if( GetNextImageInList( image ) == NULL )
|
||||
return( -1 );
|
||||
|
||||
image = SyncNextImageInList( image );
|
||||
magick->position.top += magick->page_height;
|
||||
}
|
||||
|
||||
if( !magick_set_image_size( image,
|
||||
im->Xsize, magick->page_height, magick->exception ) ) {
|
||||
magick_vips_error( class->nickname, magick->exception );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* Delay must be converted from milliseconds into centiseconds
|
||||
* as GIF image requires centiseconds.
|
||||
*/
|
||||
if( magick->delays ) {
|
||||
page_index = magick->position.top / magick->page_height;
|
||||
if( page_index < magick->delays_length )
|
||||
image->delay =
|
||||
VIPS_RINT( magick->delays[page_index] / 10.0 );
|
||||
}
|
||||
|
||||
/* ImageMagick uses iterations like this (at least in gif save):
|
||||
* 0 - set 0 loops (infinite)
|
||||
* 1 - don't write the netscape extension block
|
||||
* 2 - loop once
|
||||
* 3 - loop twice etc.
|
||||
*/
|
||||
if( vips_image_get_typeof( im, "loop" ) &&
|
||||
!vips_image_get_int( im, "loop", &number ) ) {
|
||||
image->iterations = (size_t) number;
|
||||
}
|
||||
else {
|
||||
/* DEPRECATED "gif-loop"
|
||||
*
|
||||
* We have the simple gif meaning, so we must add one unless
|
||||
* it's zero.
|
||||
*/
|
||||
if( vips_image_get_typeof( im, "gif-loop" ) &&
|
||||
!vips_image_get_int( im, "gif-loop", &number ) )
|
||||
image->iterations = (size_t) (number ? number + 1 : 0);
|
||||
}
|
||||
|
||||
if( vips_image_get_typeof( im, "gif-comment" ) &&
|
||||
!vips_image_get_string( im, "gif-comment", &str ) )
|
||||
magick_set_property( image, "comment", str, magick->exception );
|
||||
|
||||
/* libvips keeps animations as a set of independent frames, so we want
|
||||
* to clear to the background between each one.
|
||||
*/
|
||||
image->dispose = BackgroundDispose;
|
||||
|
||||
if( !save->strip &&
|
||||
magick_set_magick_profile( image, im, magick->exception ) ) {
|
||||
magick_vips_error( class->nickname, magick->exception );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
magick->current_image = image;
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* We've written all the pixels to current_image ... finish it off ready to
|
||||
* move on.
|
||||
*/
|
||||
static void
|
||||
vips_foreign_save_magick_end_image( VipsForeignSaveMagick *magick )
|
||||
{
|
||||
if( magick->current_image ) {
|
||||
magick_inherit_exception( magick->exception,
|
||||
magick->current_image );
|
||||
magick->current_image = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Another block of pixels have arrived from libvips.
|
||||
*/
|
||||
static int
|
||||
vips_foreign_save_magick_write_block( VipsRegion *region, VipsRect *area,
|
||||
void *a )
|
||||
{
|
||||
VipsForeignSaveMagick *magick = (VipsForeignSaveMagick *) a;
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( magick );
|
||||
|
||||
VipsRect pixels;
|
||||
|
||||
pixels = region->valid;
|
||||
do {
|
||||
VipsRect hit;
|
||||
void *p;
|
||||
|
||||
if( !magick->current_image &&
|
||||
vips_foreign_save_magick_next_image( magick ) )
|
||||
return( -1 );
|
||||
|
||||
vips_rect_intersectrect( &pixels, &magick->position, &hit );
|
||||
p = VIPS_REGION_ADDR( region, hit.left, hit.top );
|
||||
if( !magick_import_pixels( magick->current_image,
|
||||
hit.left, hit.top - magick->position.top,
|
||||
hit.width, hit.height,
|
||||
magick->map, magick->storage_type,
|
||||
p,
|
||||
magick->exception ) ) {
|
||||
magick_vips_error( class->nickname,
|
||||
magick->exception );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* Have we filled the page.
|
||||
*/
|
||||
if( VIPS_RECT_BOTTOM( &hit ) ==
|
||||
VIPS_RECT_BOTTOM( &magick->position ) )
|
||||
vips_foreign_save_magick_end_image( magick );
|
||||
|
||||
pixels.top += hit.height;
|
||||
pixels.height -= hit.height;
|
||||
} while( pixels.height > 0 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_foreign_save_magick_build( VipsObject *object )
|
||||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
|
||||
VipsForeignSave *save = (VipsForeignSave *) object;
|
||||
VipsForeignSaveMagick *magick = (VipsForeignSaveMagick *) object;
|
||||
|
||||
VipsImage *im;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "vips_foreign_save_magick_build: %p\n", object );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_magick_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
magick_genesis();
|
||||
|
||||
/* The image to save.
|
||||
*/
|
||||
im = save->ready;
|
||||
|
||||
magick->exception = magick_acquire_exception();
|
||||
magick->image_info = CloneImageInfo( NULL );
|
||||
|
||||
switch( im->BandFmt ) {
|
||||
case VIPS_FORMAT_UCHAR:
|
||||
magick->storage_type = CharPixel;
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_USHORT:
|
||||
magick->storage_type = ShortPixel;
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_UINT:
|
||||
magick->storage_type = LongPixel;
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_FLOAT:
|
||||
magick->storage_type = FloatPixel;
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_DOUBLE:
|
||||
magick->storage_type = DoublePixel;
|
||||
break;
|
||||
|
||||
default:
|
||||
vips_error( class->nickname,
|
||||
"%s", _( "unsupported image format" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
switch( im->Bands ) {
|
||||
case 1:
|
||||
magick->map = g_strdup( "I" );
|
||||
break;
|
||||
|
||||
case 2:
|
||||
magick->map = g_strdup( "IA" );
|
||||
break;
|
||||
|
||||
case 3:
|
||||
magick->map = g_strdup( "RGB" );
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if( im->Type == VIPS_INTERPRETATION_CMYK )
|
||||
magick->map = g_strdup( "CMYK" );
|
||||
else
|
||||
magick->map = g_strdup( "RGBA" );
|
||||
break;
|
||||
|
||||
case 5:
|
||||
magick->map = g_strdup( "CMYKA" );
|
||||
break;
|
||||
|
||||
default:
|
||||
vips_error( class->nickname,
|
||||
"%s", _( "unsupported number of image bands" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( magick->format ) {
|
||||
vips_strncpy( magick->image_info->magick,
|
||||
magick->format, MaxPathExtent );
|
||||
if( magick->filename )
|
||||
(void) vips_snprintf( magick->image_info->filename,
|
||||
MaxPathExtent, "%s:%s",
|
||||
magick->format, magick->filename );
|
||||
}
|
||||
else if( magick->filename ) {
|
||||
vips_strncpy( magick->image_info->filename,
|
||||
magick->filename, MaxPathExtent );
|
||||
}
|
||||
|
||||
if( magick->quality > 0 )
|
||||
magick->image_info->quality = magick->quality;
|
||||
|
||||
magick->page_height = vips_image_get_page_height( im );
|
||||
|
||||
/* Get as a gvalue so we can keep a ref to the delay array while we
|
||||
* need it.
|
||||
*/
|
||||
if( vips_image_get_typeof( im, "delay" ) ) {
|
||||
g_value_unset( &magick->delay_gvalue );
|
||||
if( vips_image_get( im, "delay", &magick->delay_gvalue ) )
|
||||
return( -1 );
|
||||
magick->delays = vips_value_get_array_int(
|
||||
&magick->delay_gvalue, &magick->delays_length );
|
||||
}
|
||||
|
||||
if( vips_sink_disc( im,
|
||||
vips_foreign_save_magick_write_block, magick ) )
|
||||
return( -1 );
|
||||
|
||||
if( magick->optimize_gif_frames ) {
|
||||
if( !magick_optimize_image_layers( &magick->images,
|
||||
magick->exception ) ) {
|
||||
magick_inherit_exception( magick->exception,
|
||||
magick->images );
|
||||
magick_vips_error( class->nickname, magick->exception );
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
|
||||
if( magick->optimize_gif_transparency ) {
|
||||
if( !magick_optimize_image_transparency( magick->images,
|
||||
magick->exception ) ) {
|
||||
magick_inherit_exception( magick->exception,
|
||||
magick->images );
|
||||
magick_vips_error( class->nickname, magick->exception );
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* We could call into libMagick and discover what save formats it supports, but
|
||||
* that would mean starting up libMagick on libvips init, and that would add a
|
||||
* lot of time.
|
||||
*
|
||||
* Instead, just list the commonly-used formats that all libMagicks support and
|
||||
* that libvips does not.
|
||||
*/
|
||||
static const char *vips__save_magick_suffs[] = { ".gif", ".bmp", NULL };
|
||||
|
||||
/* Save a bit of typing.
|
||||
*/
|
||||
#define UC VIPS_FORMAT_UCHAR
|
||||
#define US VIPS_FORMAT_USHORT
|
||||
#define UI VIPS_FORMAT_UINT
|
||||
#define F VIPS_FORMAT_FLOAT
|
||||
#define D VIPS_FORMAT_DOUBLE
|
||||
|
||||
static int bandfmt_magick[10] = {
|
||||
/* UC C US S UI I F X D DX */
|
||||
UC, UC, US, US, UI, UI, F, F, D, D
|
||||
};
|
||||
|
||||
static void
|
||||
vips_foreign_save_magick_class_init( VipsForeignSaveMagickClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
|
||||
VipsForeignSaveClass *save_class = (VipsForeignSaveClass *) class;
|
||||
|
||||
gobject_class->dispose = vips_foreign_save_magick_dispose;
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "magicksave_base";
|
||||
object_class->description = _( "save with ImageMagick" );
|
||||
object_class->build = vips_foreign_save_magick_build;
|
||||
|
||||
/* We need to be well to the back of the queue since vips's
|
||||
* dedicated savers are usually preferable.
|
||||
*/
|
||||
foreign_class->priority = -100;
|
||||
foreign_class->suffs = vips__save_magick_suffs;
|
||||
|
||||
save_class->saveable = VIPS_SAVEABLE_ANY;
|
||||
save_class->format_table = bandfmt_magick;
|
||||
|
||||
VIPS_ARG_STRING( class, "format", 2,
|
||||
_( "Format" ),
|
||||
_( "Format to save in" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveMagick, format ),
|
||||
NULL );
|
||||
|
||||
VIPS_ARG_INT( class, "quality", 3,
|
||||
_( "Quality" ),
|
||||
_( "Quality to use" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveMagick, quality ),
|
||||
0, 100, 0 );
|
||||
|
||||
VIPS_ARG_BOOL( class, "optimize_gif_frames", 4,
|
||||
_( "Optimize_gif_frames" ),
|
||||
_( "Apply GIF frames optimization" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveMagick, optimize_gif_frames ),
|
||||
FALSE );
|
||||
|
||||
VIPS_ARG_BOOL( class, "optimize_gif_transparency", 5,
|
||||
_( "Optimize_gif_transparency" ),
|
||||
_( "Apply GIF transparency optimization" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveMagick,
|
||||
optimize_gif_transparency ),
|
||||
FALSE );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_magick_init( VipsForeignSaveMagick *magick )
|
||||
{
|
||||
/* Init to an int just to have something there. It is swapped for an
|
||||
* int array later.
|
||||
*/
|
||||
g_value_init( &magick->delay_gvalue, G_TYPE_INT );
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignSaveMagickFile {
|
||||
VipsForeignSaveMagick parent_object;
|
||||
|
||||
char *filename;
|
||||
|
||||
} VipsForeignSaveMagickFile;
|
||||
|
||||
typedef VipsForeignSaveMagickClass VipsForeignSaveMagickFileClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsForeignSaveMagickFile, vips_foreign_save_magick_file,
|
||||
vips_foreign_save_magick_get_type() );
|
||||
|
||||
static int
|
||||
vips_foreign_save_magick_file_build( VipsObject *object )
|
||||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
|
||||
VipsForeignSaveMagick *magick = (VipsForeignSaveMagick *) object;
|
||||
VipsForeignSaveMagickFile *file = (VipsForeignSaveMagickFile *) object;
|
||||
|
||||
magick->filename = file->filename;
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_magick_file_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
if( !WriteImages( magick->image_info, magick->images,
|
||||
magick->image_info->filename, magick->exception ) ) {
|
||||
magick_inherit_exception( magick->exception, magick->images );
|
||||
magick_vips_error( class->nickname, magick->exception );
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_magick_file_class_init(
|
||||
VipsForeignSaveMagickFileClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "magicksave";
|
||||
object_class->description = _( "save file with ImageMagick" );
|
||||
object_class->build = vips_foreign_save_magick_file_build;
|
||||
|
||||
VIPS_ARG_STRING( class, "filename", 1,
|
||||
_( "Filename" ),
|
||||
_( "Filename to save to" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveMagickFile, filename ),
|
||||
NULL );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_magick_file_init( VipsForeignSaveMagickFile *file )
|
||||
{
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignSaveMagickBuffer {
|
||||
VipsForeignSaveMagick parent_object;
|
||||
|
||||
/* Save to a buffer.
|
||||
*/
|
||||
VipsArea *buf;
|
||||
|
||||
} VipsForeignSaveMagickBuffer;
|
||||
|
||||
typedef VipsForeignSaveMagickClass VipsForeignSaveMagickBufferClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsForeignSaveMagickBuffer, vips_foreign_save_magick_buffer,
|
||||
vips_foreign_save_magick_get_type() );
|
||||
|
||||
static int
|
||||
vips_foreign_save_magick_buffer_build( VipsObject *object )
|
||||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
|
||||
VipsForeignSaveMagick *magick = (VipsForeignSaveMagick *) object;
|
||||
VipsForeignSaveMagickBuffer *buffer =
|
||||
(VipsForeignSaveMagickBuffer *) object;
|
||||
|
||||
void *obuf;
|
||||
size_t olen;
|
||||
VipsBlob *blob;
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_magick_buffer_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
if( !(obuf = magick_images_to_blob( magick->image_info, magick->images,
|
||||
&olen, magick->exception )) ) {
|
||||
magick_inherit_exception( magick->exception, magick->images );
|
||||
magick_vips_error( class->nickname, magick->exception );
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
blob = vips_blob_new( (VipsCallbackFn) vips_area_free_cb, obuf, olen );
|
||||
g_object_set( buffer, "buffer", blob, NULL );
|
||||
vips_area_unref( VIPS_AREA( blob ) );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_magick_buffer_class_init(
|
||||
VipsForeignSaveMagickBufferClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "magicksave_buffer";
|
||||
object_class->description = _( "save image to magick buffer" );
|
||||
object_class->build = vips_foreign_save_magick_buffer_build;
|
||||
|
||||
VIPS_ARG_BOXED( class, "buffer", 1,
|
||||
_( "Buffer" ),
|
||||
_( "Buffer to save to" ),
|
||||
VIPS_ARGUMENT_REQUIRED_OUTPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveMagickBuffer, buf ),
|
||||
VIPS_TYPE_BLOB );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_magick_buffer_init( VipsForeignSaveMagickBuffer *buffer )
|
||||
{
|
||||
}
|
||||
|
||||
#endif /*ENABLE_MAGICKSAVE*/
|
92
libvips/module/magick.c
Normal file
92
libvips/module/magick.c
Normal file
@ -0,0 +1,92 @@
|
||||
/* magick as a dynamically loadable module
|
||||
*
|
||||
* 21/4/21 kleisauke
|
||||
* - initial
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
#define DEBUG
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
#include <vips/debug.h>
|
||||
#include <vips/internal.h>
|
||||
|
||||
#if (defined(HAVE_MAGICK6) || defined (HAVE_MAGICK7)) && defined(MAGICK_MODULE)
|
||||
|
||||
/* This is called on module load.
|
||||
*/
|
||||
G_MODULE_EXPORT const gchar *
|
||||
g_module_check_init( GModule *module )
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf( "vips_magick: module init\n" );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
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_magick7_file_get_type( void );
|
||||
extern GType vips_foreign_load_magick7_buffer_get_type( void );
|
||||
extern GType vips_foreign_save_magick_file_get_type( void );
|
||||
extern GType vips_foreign_save_magick_buffer_get_type( void );
|
||||
|
||||
#ifdef ENABLE_MAGICKLOAD
|
||||
#ifdef HAVE_MAGICK6
|
||||
vips_foreign_load_magick_file_get_type();
|
||||
vips_foreign_load_magick_buffer_get_type();
|
||||
#endif /*HAVE_MAGICK6*/
|
||||
|
||||
#ifdef HAVE_MAGICK7
|
||||
vips_foreign_load_magick7_file_get_type();
|
||||
vips_foreign_load_magick7_buffer_get_type();
|
||||
#endif /*HAVE_MAGICK7*/
|
||||
#endif /*ENABLE_MAGICKLOAD*/
|
||||
|
||||
#ifdef ENABLE_MAGICKSAVE
|
||||
vips_foreign_save_magick_file_get_type();
|
||||
vips_foreign_save_magick_buffer_get_type();
|
||||
#endif /*ENABLE_MAGICKSAVE*/
|
||||
|
||||
/* We can't be unloaded, there would be chaos.
|
||||
*/
|
||||
g_module_make_resident( module );
|
||||
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
#endif /*(defined(HAVE_MAGICK6) || defined (HAVE_MAGICK7)) && defined(MAGICK_MODULE)*/
|
Loading…
Reference in New Issue
Block a user