Merge pull request #2167 from kleisauke/gmodulized

Support for building loaders/savers as dynamic loadable module
This commit is contained in:
John Cupitt 2021-04-26 09:07:09 +01:00 committed by GitHub
commit 127fab316c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 4702 additions and 3875 deletions

View File

@ -399,8 +399,8 @@ AC_CHECK_LIB(m,hypot,[AC_DEFINE(HAVE_HYPOT,1,[have hypot() in libm.])])
AC_CHECK_LIB(m,atan2,[AC_DEFINE(HAVE_ATAN2,1,[have atan2() in libm.])])
# have to have these parts of glib ... we need glib 2.15 for gio
PKG_CHECK_MODULES(REQUIRED, glib-2.0 >= 2.40 gmodule-2.0 gobject-2.0 gio-2.0)
PACKAGES_USED="$PACKAGES_USED glib-2.0 gmodule-2.0 gobject-2.0 gio-2.0"
PKG_CHECK_MODULES(REQUIRED, glib-2.0 >= 2.40 gmodule-no-export-2.0 gobject-2.0 gio-2.0)
PACKAGES_USED="$PACKAGES_USED glib-2.0 gmodule-no-export-2.0 gobject-2.0 gio-2.0"
# from 2.62 we have datetime
PKG_CHECK_MODULES(DATE_TIME_FORMAT_ISO8601, glib-2.0 >= 2.62,
@ -437,6 +437,30 @@ PKG_CHECK_MODULES(HAVE_CHECKED_MUL, glib-2.0 >= 2.48,
]
)
AC_MSG_CHECKING([whether to build dynamic modules])
AC_ARG_ENABLE([modules],
AS_HELP_STRING([--disable-modules], [disable dynamic modules (default: enabled)]))
gmodule_supported_bool=false
gmodule_supported_flag=no
gmodule_with_flag=yes
if test x"$enable_modules" = x"no"; then
AC_MSG_RESULT(no)
else
AC_MSG_RESULT(yes)
AC_MSG_CHECKING(whether dynamic modules work)
gmodule_supported_bool=`$PKG_CONFIG gmodule-no-export-2.0 --variable gmodule_supported`
if $gmodule_supported_bool; then
gmodule_supported_flag=yes
gmodule_with_flag='module'
AC_MSG_RESULT(yes)
else
AC_MSG_RESULT(no)
fi
fi
# check for gtk-doc
GTK_DOC_CHECK([1.14],[--flavour no-tmpl])
@ -511,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],
@ -528,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
]
)
]
@ -544,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
@ -668,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],
@ -824,7 +863,14 @@ VIPS_LIBS="$VIPS_LIBS $LIBOPENJP2_LIBS"
# libheif
AC_ARG_WITH([heif],
AS_HELP_STRING([--without-heif], [build without libheif (default: test)]))
AS_HELP_STRING([--without-heif], [build without libheif (default: test)]),
[with_heif=$withval],
[with_heif=$gmodule_with_flag])
# libheif as a dynamically loadable module
AS_IF([test x"$with_heif" = x"module"],
[with_heif_module=$gmodule_supported_flag],
[with_heif_module=no])
if test x"$with_heif" != x"no"; then
PKG_CHECK_MODULES(HEIF, libheif >= 1.3.0,
@ -843,11 +889,13 @@ if test x"$with_heif" != x"no"; then
AC_DEFINE(HAVE_HEIF_ENCODER,1,
[define if your libheif has encode support.])
fi
PACKAGES_USED="$PACKAGES_USED libheif"
AS_IF([test x"$with_heif_module" = x"no"],
[PACKAGES_USED="$PACKAGES_USED libheif"])
],
[AC_MSG_WARN([libheif >= 1.3.0 not found; disabling HEIF support])
pkg-config --exists --print-errors "libheif >= 1.3.0"
with_heif=no
with_heif_module=no
have_h265_decoder=
have_h265_encoder=
have_avif_decoder=
@ -856,6 +904,12 @@ if test x"$with_heif" != x"no"; then
)
fi
AS_IF([test x"$with_heif_module" = x"yes"],
[AC_DEFINE([HEIF_MODULE], [1], [define to build libheif as a dynamically loadable module.])],
[VIPS_CFLAGS="$VIPS_CFLAGS $HEIF_CFLAGS"
VIPS_LIBS="$VIPS_LIBS $HEIF_LIBS"])
AM_CONDITIONAL(HEIF_MODULE, [test x"$with_heif_module" = x"yes"])
# color profile support added in 1.3.3
if test x"$with_heif" = x"yes"; then
save_LIBS="$LIBS"
@ -889,9 +943,6 @@ if test x"$with_heif" = x"yes"; then
CFLAGS="$save_CFLAGS"
fi
VIPS_CFLAGS="$VIPS_CFLAGS $HEIF_CFLAGS"
VIPS_LIBS="$VIPS_LIBS $HEIF_LIBS"
# pdfium
AC_ARG_WITH([pdfium],
AS_HELP_STRING([--without-pdfium], [build without pdfium (default: test)]))
@ -917,21 +968,33 @@ VIPS_LIBS="$VIPS_LIBS $PDFIUM_LIBS"
# poppler
AC_ARG_WITH([poppler],
AS_HELP_STRING([--without-poppler], [build without poppler (default: test)]))
AS_HELP_STRING([--without-poppler], [build without poppler (default: test)]),
[with_poppler=$withval],
[with_poppler=$gmodule_with_flag])
# poppler as a dynamically loadable module
AS_IF([test x"$with_poppler" = x"module"],
[with_poppler_module=$gmodule_supported_flag],
[with_poppler_module=no])
if test x"$with_poppler" != x"no"; then
PKG_CHECK_MODULES(POPPLER, [poppler-glib >= 0.16.0 cairo >= 1.2], [
AC_DEFINE(HAVE_POPPLER,1,[define if you have poppler-glib >= 0.16.0 and cairo >= 1.2 installed.])
with_poppler=yes
PACKAGES_USED="$PACKAGES_USED poppler-glib cairo"
AS_IF([test x"$with_poppler_module" = x"no"],
[PACKAGES_USED="$PACKAGES_USED poppler-glib cairo"])
], [
AC_MSG_WARN([poppler-glib >= 0.16.0 or cairo >= 1.2 not found; disabling PDF load via poppler])
with_poppler=no
with_poppler_module=no
])
fi
VIPS_CFLAGS="$VIPS_CFLAGS $POPPLER_CFLAGS"
VIPS_LIBS="$VIPS_LIBS $POPPLER_LIBS"
AS_IF([test x"$with_poppler_module" = x"yes"],
[AC_DEFINE([POPPLER_MODULE], [1], [define to build poppler as a dynamically loadable module.])],
[VIPS_CFLAGS="$VIPS_CFLAGS $POPPLER_CFLAGS"
VIPS_LIBS="$VIPS_LIBS $POPPLER_LIBS"])
AM_CONDITIONAL(POPPLER_MODULE, [test x"$with_poppler_module" = x"yes"])
# librsvg
AC_ARG_WITH([rsvg],
@ -982,32 +1045,44 @@ VIPS_LIBS="$VIPS_LIBS $ZLIB_LIBS"
# OpenSlide
AC_ARG_WITH([openslide],
AS_HELP_STRING([--without-openslide],
[build without OpenSlide (default: test)])
)
[build without OpenSlide (default: test)]),
[with_openslide=$withval],
[with_openslide=$gmodule_with_flag])
# OpenSlide as a dynamically loadable module
AS_IF([test x"$with_openslide" = x"module"],
[with_openslide_module=$gmodule_supported_flag],
[with_openslide_module=no])
if test x"$with_openslide" != x"no"; then
PKG_CHECK_MODULES(OPENSLIDE, [openslide >= 3.4.0],
[AC_DEFINE(HAVE_OPENSLIDE_3_4,1,[define if you have OpenSlide >= 3.4.0 installed.])
AC_DEFINE(HAVE_OPENSLIDE,1,[define if you have OpenSlide >= 3.3.0 installed.])
with_openslide=yes
PACKAGES_USED="$PACKAGES_USED openslide"
AS_IF([test x"$with_openslide_module" = x"no"],
[PACKAGES_USED="$PACKAGES_USED openslide"])
],
[AC_MSG_NOTICE([OpenSlide >= 3.4.0 not found; checking for >= 3.3.0])
PKG_CHECK_MODULES(OPENSLIDE, [openslide >= 3.3.0],
[AC_DEFINE(HAVE_OPENSLIDE,1,[define if you have OpenSlide >= 3.3.0 installed.])
with_openslide=yes
PACKAGES_USED="$PACKAGES_USED openslide"
AS_IF([test x"$with_openslide_module" = x"no"],
[PACKAGES_USED="$PACKAGES_USED openslide"])
],
[AC_MSG_WARN([OpenSlide >= 3.3.0 not found; disabling virtual slide support])
with_openslide=no
with_openslide_module=no
]
)
]
)
fi
VIPS_CFLAGS="$VIPS_CFLAGS $OPENSLIDE_CFLAGS"
VIPS_LIBS="$VIPS_LIBS $OPENSLIDE_LIBS"
AS_IF([test x"$with_openslide_module" = x"yes"],
[AC_DEFINE([OPENSLIDE_MODULE], [1], [define to build OpenSlide as a dynamically loadable module.])],
[VIPS_CFLAGS="$VIPS_CFLAGS $OPENSLIDE_CFLAGS"
VIPS_LIBS="$VIPS_LIBS $OPENSLIDE_LIBS"])
AM_CONDITIONAL(OPENSLIDE_MODULE, [test x"$with_openslide_module" = x"yes"])
# matio
AC_ARG_WITH([matio],
@ -1356,6 +1431,7 @@ VIPS_LIBS="$VIPS_LIBS $REQUIRED_LIBS -lm"
VIPS_CONFIG="\
enable debug: $enable_debug, \
enable deprecated library components: $enable_deprecated, \
enable modules: $gmodule_supported_flag, \
enable docs with gtkdoc: $enable_gtk_doc, \
gobject introspection: $found_introspection, \
RAD load/save: $with_radiance, \
@ -1376,17 +1452,17 @@ PNG load/save with libpng: $with_png, \
8bpp PNG quantisation: $with_imagequant, \
TIFF load/save with libtiff: $with_tiff, \
image pyramid save: $with_gsf, \
HEIC/AVIF load/save with libheif: $with_heif, \
HEIC/AVIF load/save with libheif: $with_heif (dynamic module: $with_heif_module), \
WebP load/save with libwebp: $with_libwebp, \
PDF load with PDFium: $with_pdfium, \
PDF load with poppler-glib: $with_poppler, \
PDF load with poppler-glib: $with_poppler (dynamic module: $with_poppler_module), \
SVG load with librsvg-2.0: $with_rsvg, \
EXR load with OpenEXR: $with_OpenEXR, \
slide load with OpenSlide: $with_openslide, \
slide load with OpenSlide: $with_openslide (dynamic module: $with_openslide_module), \
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"
@ -1456,6 +1532,7 @@ AC_MSG_RESULT([dnl
## Build options
enable debug: $enable_debug
enable deprecated library components: $enable_deprecated
enable modules: $gmodule_supported_flag
enable docs with gtkdoc: $enable_gtk_doc
gobject introspection: $found_introspection
RAD load/save: $with_radiance
@ -1488,21 +1565,23 @@ PNG load/save with libpng: $with_png
TIFF load/save with libtiff: $with_tiff
image pyramid save: $with_gsf
(requires libgsf-1 1.14.26 or later)
HEIC/AVIF load/save with libheif: $with_heif
HEIC/AVIF load/save with libheif: $with_heif (dynamic module: $with_heif_module)
WebP load/save with libwebp: $with_libwebp
(requires libwebp, libwebpmux, libwebpdemux 0.6.0 or later)
PDF load with PDFium: $with_pdfium
PDF load with poppler-glib: $with_poppler
PDF load with poppler-glib: $with_poppler (dynamic module: $with_poppler_module)
(requires poppler-glib 0.16.0 or later)
SVG load with librsvg-2.0: $with_rsvg
(requires librsvg-2.0 2.34.0 or later)
EXR load with OpenEXR: $with_OpenEXR
slide load with OpenSlide: $with_openslide
JPEG2000 load/save with libopenjp2: $with_libopenjp2
(requires libopenjp2 2.2 or later)
slide load with OpenSlide: $with_openslide (dynamic module: $with_openslide_module)
(requires openslide-3.3.0 or later)
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

View File

@ -61,6 +61,7 @@ libvips_la_LDFLAGS = \
-version-info @LIBRARY_CURRENT@:@LIBRARY_REVISION@:@LIBRARY_AGE@
EXTRA_DIST = \
module \
$(OPTIONAL_DIST_DIR)
CLEANFILES =
@ -72,6 +73,78 @@ install-exec-hook:
cp soname.h $(DESTDIR)$(pkgincludedir) && \
rm soname.h
# Modules
module_LTLIBRARIES =
# All modules within the $VIPSHOME/lib/vips-modules-MAJOR.MINOR
# directory are automatically loaded on vips_init.
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
if POPPLER_MODULE
module_LTLIBRARIES += vips-poppler.la
endif # POPPLER_MODULE
if OPENSLIDE_MODULE
module_LTLIBRARIES += vips-openslide.la
endif # OPENSLIDE_MODULE
MODULE_CPPFLAGS = \
-I${top_srcdir}/libvips/include \
$(REQUIRED_CFLAGS)
MODULE_LDFLAGS = \
-no-undefined \
-shared \
-module \
-avoid-version
MODULE_LIBADD = \
libvips.la \
$(REQUIRED_LIBS)
# Note that only the GObject part should be included in a
# 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)
vips_heif_la_LIBADD = $(MODULE_LIBADD) $(HEIF_LIBS)
vips_poppler_la_SOURCES = module/poppler.c foreign/poppler2vips.c
vips_poppler_la_CPPFLAGS = $(MODULE_CPPFLAGS) $(POPPLER_CFLAGS)
vips_poppler_la_LDFLAGS = $(MODULE_LDFLAGS)
vips_poppler_la_LIBADD = $(MODULE_LIBADD) $(POPPLER_LIBS)
vips_openslide_la_SOURCES = module/openslide.c foreign/openslide2vips.c
vips_openslide_la_CPPFLAGS = $(MODULE_CPPFLAGS) $(OPENSLIDE_CFLAGS)
vips_openslide_la_LDFLAGS = $(MODULE_LDFLAGS)
vips_openslide_la_LIBADD = $(MODULE_LIBADD) $(OPENSLIDE_LIBS)
# Introspection
-include $(INTROSPECTION_MAKEFILE)
INTROSPECTION_GIRS =
INTROSPECTION_SCANNER_ARGS = --add-include-path=$(srcdir)

View File

@ -27,10 +27,6 @@ libforeign_la_SOURCES = \
jpeg.h \
jpegload.c \
jpegsave.c \
magick2vips.c \
magick7load.c \
magick.c \
magick.h \
magickload.c \
magicksave.c \
matlab.c \
@ -42,7 +38,6 @@ libforeign_la_SOURCES = \
nsgifload.c \
openexr2vips.c \
openexrload.c \
openslide2vips.c \
openslideload.c \
pdfiumload.c \
pdfload.c \
@ -74,6 +69,31 @@ libforeign_la_SOURCES = \
webpload.c \
webpsave.c
# 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
if !POPPLER_MODULE
libforeign_la_SOURCES += poppler2vips.c
endif # !POPPLER_MODULE
if !OPENSLIDE_MODULE
libforeign_la_SOURCES += openslide2vips.c
endif # !OPENSLIDE_MODULE
EXTRA_DIST = libnsgif
AM_CPPFLAGS = -I${top_srcdir}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@

View File

@ -2246,11 +2246,11 @@ vips_foreign_operation_init( void )
vips_foreign_save_rad_target_get_type();
#endif /*HAVE_RADIANCE*/
#ifdef HAVE_POPPLER
#if defined(HAVE_POPPLER) && !defined(POPPLER_MODULE)
vips_foreign_load_pdf_file_get_type();
vips_foreign_load_pdf_buffer_get_type();
vips_foreign_load_pdf_source_get_type();
#endif /*HAVE_POPPLER*/
#endif /*defined(HAVE_POPPLER) && !defined(POPPLER_MODULE)*/
#ifdef HAVE_PDFIUM
vips_foreign_load_pdf_file_get_type();
@ -2339,12 +2339,12 @@ vips_foreign_operation_init( void )
vips_foreign_save_tiff_buffer_get_type();
#endif /*HAVE_TIFF*/
#ifdef HAVE_OPENSLIDE
#if defined(HAVE_OPENSLIDE) && !defined(OPENSLIDE_MODULE)
vips_foreign_load_openslide_file_get_type();
vips_foreign_load_openslide_source_get_type();
#endif /*HAVE_OPENSLIDE*/
#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();
@ -2354,12 +2354,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();
@ -2377,17 +2377,17 @@ vips_foreign_operation_init( void )
vips_foreign_save_nifti_get_type();
#endif /*HAVE_NIFTI*/
#ifdef HAVE_HEIF_DECODER
#if defined(HAVE_HEIF_DECODER) && !defined(HEIF_MODULE)
vips_foreign_load_heif_file_get_type();
vips_foreign_load_heif_buffer_get_type();
vips_foreign_load_heif_source_get_type();
#endif /*HAVE_HEIF_DECODER*/
#endif /*defined(HAVE_HEIF_DECODER) && !defined(HEIF_MODULE)*/
#ifdef HAVE_HEIF_ENCODER
#if defined(HAVE_HEIF_ENCODER) && !defined(HEIF_MODULE)
vips_foreign_save_heif_file_get_type();
vips_foreign_save_heif_buffer_get_type();
vips_foreign_save_heif_target_get_type();
#endif /*HAVE_HEIF_ENCODER*/
#endif /*defined(HAVE_HEIF_ENCODER) && !defined(HEIF_MODULE)*/
vips__foreign_load_operation =
g_quark_from_static_string( "vips-foreign-load-operation" );

1267
libvips/foreign/heif2vips.c Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -8,6 +8,8 @@
* - save alpha when necessary
* 15/3/20
* - revise for new VipsTarget API
* 14/2/21 kleisauke
* - move GObject part to vips2heif.c
*/
/*
@ -54,663 +56,6 @@
#include <vips/vips.h>
#include <vips/internal.h>
#ifdef HAVE_HEIF_ENCODER
#include <libheif/heif.h>
#include "pforeign.h"
typedef struct _VipsForeignSaveHeif {
VipsForeignSave parent_object;
/* Where to write (set by subclasses).
*/
VipsTarget *target;
/* Coding quality factor (1-100).
*/
int Q;
/* Lossless compression.
*/
gboolean lossless;
/* Compression format
*/
VipsForeignHeifCompression compression;
/* CPU effort (0-8).
*/
int speed;
/* Chroma subsampling.
*/
VipsForeignSubsample subsample_mode;
/* The image we save. This is a copy of save->ready since we need to
* be able to update the metadata.
*/
VipsImage *image;
int page_width;
int page_height;
int n_pages;
struct heif_context *ctx;
struct heif_encoder *encoder;
/* The current page we are writing.
*/
struct heif_image_handle *handle;
/* The current page in memory which we build as we scan down the
* image.
*/
struct heif_image *img;
/* The libheif memory area we fill with pixels from the libvips
* pipe.
*/
uint8_t *data;
int stride;
} VipsForeignSaveHeif;
typedef VipsForeignSaveClass VipsForeignSaveHeifClass;
G_DEFINE_ABSTRACT_TYPE( VipsForeignSaveHeif, vips_foreign_save_heif,
VIPS_TYPE_FOREIGN_SAVE );
static void
vips_foreign_save_heif_dispose( GObject *gobject )
{
VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) gobject;
VIPS_UNREF( heif->target );
VIPS_UNREF( heif->image );
VIPS_FREEF( heif_image_release, heif->img );
VIPS_FREEF( heif_image_handle_release, heif->handle );
VIPS_FREEF( heif_encoder_release, heif->encoder );
VIPS_FREEF( heif_context_free, heif->ctx );
G_OBJECT_CLASS( vips_foreign_save_heif_parent_class )->
dispose( gobject );
}
typedef struct heif_error (*libheif_metadata_fn)( struct heif_context *,
const struct heif_image_handle *,
const void *, int );
struct _VipsForeignSaveHeifMetadata {
const char *name;
libheif_metadata_fn saver;
} libheif_metadata[] = {
{ VIPS_META_EXIF_NAME, heif_context_add_exif_metadata },
{ VIPS_META_XMP_NAME, heif_context_add_XMP_metadata }
};
static int
vips_foreign_save_heif_write_metadata( VipsForeignSaveHeif *heif )
{
int i;
struct heif_error error;
/* Rebuild exif from tags, if we'll be saving it.
*/
if( vips_image_get_typeof( heif->image, VIPS_META_EXIF_NAME ) )
if( vips__exif_update( heif->image ) )
return( -1 );
for( i = 0; i < VIPS_NUMBER( libheif_metadata ); i++ )
if( vips_image_get_typeof( heif->image,
libheif_metadata[i].name ) ) {
const void *data;
size_t length;
#ifdef DEBUG
printf( "attaching %s ..\n",
libheif_metadata[i].name );
#endif /*DEBUG*/
if( vips_image_get_blob( heif->image,
libheif_metadata[i].name, &data, &length ) )
return( -1 );
error = libheif_metadata[i].saver( heif->ctx,
heif->handle, data, length );
if( error.code ) {
vips__heif_error( &error );
return( -1 );
}
}
return( 0 );
}
static int
vips_foreign_save_heif_write_page( VipsForeignSaveHeif *heif, int page )
{
VipsForeignSave *save = (VipsForeignSave *) heif;
struct heif_error error;
struct heif_encoding_options *options;
#ifdef HAVE_HEIF_COLOR_PROFILE
if( !save->strip &&
vips_image_get_typeof( heif->image, VIPS_META_ICC_NAME ) ) {
const void *data;
size_t length;
#ifdef DEBUG
printf( "attaching profile ..\n" );
#endif /*DEBUG*/
if( vips_image_get_blob( heif->image,
VIPS_META_ICC_NAME, &data, &length ) )
return( -1 );
/* FIXME .. also see heif_image_set_nclx_color_profile()
*/
error = heif_image_set_raw_color_profile( heif->img,
"rICC", data, length );
if( error.code ) {
vips__heif_error( &error );
return( -1 );
}
}
#endif /*HAVE_HEIF_COLOR_PROFILE*/
options = heif_encoding_options_alloc();
if( vips_image_hasalpha( heif->image ) )
options->save_alpha_channel = 1;
#ifdef DEBUG
printf( "encoding ..\n" );
#endif /*DEBUG*/
error = heif_context_encode_image( heif->ctx,
heif->img, heif->encoder, options, &heif->handle );
heif_encoding_options_free( options );
if( error.code ) {
vips__heif_error( &error );
return( -1 );
}
if( vips_image_get_typeof( heif->image, "heif-primary" ) ) {
int primary;
if( vips_image_get_int( heif->image,
"heif-primary", &primary ) )
return( -1 );
if( page == primary ) {
error = heif_context_set_primary_image( heif->ctx,
heif->handle );
if( error.code ) {
vips__heif_error( &error );
return( -1 );
}
}
}
if( !save->strip &&
vips_foreign_save_heif_write_metadata( heif ) )
return( -1 );
VIPS_FREEF( heif_image_handle_release, heif->handle );
return( 0 );
}
static int
vips_foreign_save_heif_write_block( VipsRegion *region, VipsRect *area,
void *a )
{
VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) a;
int y;
#ifdef DEBUG
printf( "vips_foreign_save_heif_write_block: y = %d\n", area->top );
#endif /*DEBUG*/
/* Copy a line at a time into our output image, write each time the
* image fills.
*/
for( y = 0; y < area->height; y++ ) {
/* Y in page.
*/
int page = (area->top + y) / heif->page_height;
int line = (area->top + y) % heif->page_height;
VipsPel *p = VIPS_REGION_ADDR( region, 0, area->top + y );
VipsPel *q = heif->data + line * heif->stride;
memcpy( q, p, VIPS_IMAGE_SIZEOF_LINE( region->im ) );
/* Did we just write the final line? Write as a new page
* into the output.
*/
if( line == heif->page_height - 1 )
if( vips_foreign_save_heif_write_page( heif, page ) )
return( -1 );
}
return( 0 );
}
struct heif_error
vips_foreign_save_heif_write( struct heif_context *ctx,
const void *data, size_t length, void *userdata )
{
VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) userdata;
struct heif_error error;
error.code = 0;
if( vips_target_write( heif->target, data, length ) )
error.code = -1;
return( error );
}
static int
vips_foreign_save_heif_build( VipsObject *object )
{
VipsForeignSave *save = (VipsForeignSave *) object;
VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) object;
const char *filename;
struct heif_error error;
struct heif_writer writer;
char *chroma;
if( VIPS_OBJECT_CLASS( vips_foreign_save_heif_parent_class )->
build( object ) )
return( -1 );
/* Make a copy of the image in case we modify the metadata eg. for
* exif_update.
*/
if( vips_copy( save->ready, &heif->image, NULL ) )
return( -1 );
/* Compression defaults to VIPS_FOREIGN_HEIF_COMPRESSION_AV1 for .avif
* suffix.
*/
filename = vips_connection_filename( VIPS_CONNECTION( heif->target ) );
if( !vips_object_argument_isset( object, "compression" ) &&
filename &&
vips_iscasepostfix( filename, ".avif" ) )
heif->compression = VIPS_FOREIGN_HEIF_COMPRESSION_AV1;
error = heif_context_get_encoder_for_format( heif->ctx,
(enum heif_compression_format) heif->compression,
&heif->encoder );
if( error.code ) {
if( error.code == heif_error_Unsupported_filetype )
vips_error( "heifsave",
"%s", _( "Unsupported compression" ) );
else
vips__heif_error( &error );
return( -1 );
}
error = heif_encoder_set_lossy_quality( heif->encoder, heif->Q );
if( error.code ) {
vips__heif_error( &error );
return( -1 );
}
error = heif_encoder_set_lossless( heif->encoder, heif->lossless );
if( error.code ) {
vips__heif_error( &error );
return( -1 );
}
error = heif_encoder_set_parameter_integer( heif->encoder,
"speed", heif->speed );
if( error.code &&
error.subcode != heif_suberror_Unsupported_parameter ) {
vips__heif_error( &error );
return( -1 );
}
chroma = heif->subsample_mode == VIPS_FOREIGN_SUBSAMPLE_OFF ||
( heif->subsample_mode == VIPS_FOREIGN_SUBSAMPLE_AUTO &&
heif->Q >= 90 ) ? "444" : "420";
error = heif_encoder_set_parameter_string( heif->encoder,
"chroma", chroma );
if( error.code &&
error.subcode != heif_suberror_Unsupported_parameter ) {
vips__heif_error( &error );
return( -1 );
}
/* TODO .. support extra per-encoder params with
* heif_encoder_list_parameters().
*/
heif->page_width = heif->image->Xsize;
heif->page_height = vips_image_get_page_height( heif->image );
heif->n_pages = heif->image->Ysize / heif->page_height;
/* Make a heif image the size of a page. We send sink_disc() output
* here and write a frame each time it fills.
*/
#ifdef DEBUG
printf( "vips_foreign_save_heif_build:\n" );
printf( "\twidth = %d\n", heif->page_width );
printf( "\theight = %d\n", heif->page_height );
printf( "\talpha = %d\n", vips_image_hasalpha( heif->image ) );
#endif /*DEBUG*/
error = heif_image_create( heif->page_width, heif->page_height,
heif_colorspace_RGB,
vips_image_hasalpha( heif->image ) ?
heif_chroma_interleaved_RGBA :
heif_chroma_interleaved_RGB,
&heif->img );
if( error.code ) {
vips__heif_error( &error );
return( -1 );
}
error = heif_image_add_plane( heif->img, heif_channel_interleaved,
heif->page_width, heif->page_height,
vips_image_hasalpha( heif->image ) ? 32 : 24 );
if( error.code ) {
vips__heif_error( &error );
return( -1 );
}
#ifdef DEBUG
vips__heif_image_print( heif->img );
#endif /*DEBUG*/
heif->data = heif_image_get_plane( heif->img,
heif_channel_interleaved, &heif->stride );
/* Write data.
*/
if( vips_sink_disc( heif->image,
vips_foreign_save_heif_write_block, heif ) )
return( -1 );
/* This has to come right at the end :-( so there's no support for
* incremental writes.
*/
writer.writer_api_version = 1;
writer.write = vips_foreign_save_heif_write;
error = heif_context_write( heif->ctx, &writer, heif );
if( error.code ) {
vips__heif_error( &error );
return( -1 );
}
vips_target_finish( heif->target );
return( 0 );
}
/* Save a bit of typing.
*/
#define UC VIPS_FORMAT_UCHAR
static int vips_heif_bandfmt[10] = {
/* UC C US S UI I F X D DX */
UC, UC, UC, UC, UC, UC, UC, UC, UC, UC
};
static void
vips_foreign_save_heif_class_init( VipsForeignSaveHeifClass *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_heif_dispose;
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "heifsave_base";
object_class->description = _( "save image in HEIF format" );
object_class->build = vips_foreign_save_heif_build;
foreign_class->suffs = vips__heif_suffs;
save_class->saveable = VIPS_SAVEABLE_RGBA_ONLY;
save_class->format_table = vips_heif_bandfmt;
VIPS_ARG_INT( class, "Q", 10,
_( "Q" ),
_( "Q factor" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignSaveHeif, Q ),
1, 100, 50 );
VIPS_ARG_BOOL( class, "lossless", 13,
_( "Lossless" ),
_( "Enable lossless compression" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignSaveHeif, lossless ),
FALSE );
VIPS_ARG_ENUM( class, "compression", 14,
_( "compression" ),
_( "Compression format" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignSaveHeif, compression ),
VIPS_TYPE_FOREIGN_HEIF_COMPRESSION,
VIPS_FOREIGN_HEIF_COMPRESSION_HEVC );
VIPS_ARG_INT( class, "speed", 15,
_( "speed" ),
_( "CPU effort" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignSaveHeif, speed ),
0, 8, 5 );
VIPS_ARG_ENUM( class, "subsample_mode", 16,
_( "Subsample mode" ),
_( "Select chroma subsample operation mode" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignSaveHeif, subsample_mode ),
VIPS_TYPE_FOREIGN_SUBSAMPLE,
VIPS_FOREIGN_SUBSAMPLE_AUTO );
}
static void
vips_foreign_save_heif_init( VipsForeignSaveHeif *heif )
{
heif->ctx = heif_context_alloc();
heif->Q = 50;
heif->compression = VIPS_FOREIGN_HEIF_COMPRESSION_HEVC;
heif->speed = 5;
heif->subsample_mode = VIPS_FOREIGN_SUBSAMPLE_AUTO;
}
typedef struct _VipsForeignSaveHeifFile {
VipsForeignSaveHeif parent_object;
/* Filename for save.
*/
char *filename;
} VipsForeignSaveHeifFile;
typedef VipsForeignSaveHeifClass VipsForeignSaveHeifFileClass;
G_DEFINE_TYPE( VipsForeignSaveHeifFile, vips_foreign_save_heif_file,
vips_foreign_save_heif_get_type() );
static int
vips_foreign_save_heif_file_build( VipsObject *object )
{
VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) object;
VipsForeignSaveHeifFile *file = (VipsForeignSaveHeifFile *) object;
if( !(heif->target = vips_target_new_to_file( file->filename )) )
return( -1 );
if( VIPS_OBJECT_CLASS( vips_foreign_save_heif_file_parent_class )->
build( object ) )
return( -1 );
return( 0 );
}
static void
vips_foreign_save_heif_file_class_init( VipsForeignSaveHeifFileClass *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 = "heifsave";
object_class->build = vips_foreign_save_heif_file_build;
VIPS_ARG_STRING( class, "filename", 1,
_( "Filename" ),
_( "Filename to save to" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsForeignSaveHeifFile, filename ),
NULL );
}
static void
vips_foreign_save_heif_file_init( VipsForeignSaveHeifFile *file )
{
}
typedef struct _VipsForeignSaveHeifBuffer {
VipsForeignSaveHeif parent_object;
/* Save to a buffer.
*/
VipsArea *buf;
} VipsForeignSaveHeifBuffer;
typedef VipsForeignSaveHeifClass VipsForeignSaveHeifBufferClass;
G_DEFINE_TYPE( VipsForeignSaveHeifBuffer, vips_foreign_save_heif_buffer,
vips_foreign_save_heif_get_type() );
static int
vips_foreign_save_heif_buffer_build( VipsObject *object )
{
VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) object;
VipsForeignSaveHeifBuffer *buffer =
(VipsForeignSaveHeifBuffer *) object;
VipsBlob *blob;
if( !(heif->target = vips_target_new_to_memory()) )
return( -1 );
if( VIPS_OBJECT_CLASS( vips_foreign_save_heif_buffer_parent_class )->
build( object ) )
return( -1 );
g_object_get( heif->target, "blob", &blob, NULL );
g_object_set( buffer, "buffer", blob, NULL );
vips_area_unref( VIPS_AREA( blob ) );
return( 0 );
}
static void
vips_foreign_save_heif_buffer_class_init(
VipsForeignSaveHeifBufferClass *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 = "heifsave_buffer";
object_class->build = vips_foreign_save_heif_buffer_build;
VIPS_ARG_BOXED( class, "buffer", 1,
_( "Buffer" ),
_( "Buffer to save to" ),
VIPS_ARGUMENT_REQUIRED_OUTPUT,
G_STRUCT_OFFSET( VipsForeignSaveHeifBuffer, buf ),
VIPS_TYPE_BLOB );
}
static void
vips_foreign_save_heif_buffer_init( VipsForeignSaveHeifBuffer *buffer )
{
}
typedef struct _VipsForeignSaveHeifTarget {
VipsForeignSaveHeif parent_object;
VipsTarget *target;
} VipsForeignSaveHeifTarget;
typedef VipsForeignSaveHeifClass VipsForeignSaveHeifTargetClass;
G_DEFINE_TYPE( VipsForeignSaveHeifTarget, vips_foreign_save_heif_target,
vips_foreign_save_heif_get_type() );
static int
vips_foreign_save_heif_target_build( VipsObject *object )
{
VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) object;
VipsForeignSaveHeifTarget *target =
(VipsForeignSaveHeifTarget *) object;
if( target->target ) {
heif->target = target->target;
g_object_ref( heif->target );
}
if( VIPS_OBJECT_CLASS( vips_foreign_save_heif_target_parent_class )->
build( object ) )
return( -1 );
return( 0 );
}
static void
vips_foreign_save_heif_target_class_init(
VipsForeignSaveHeifTargetClass *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 = "heifsave_target";
object_class->build = vips_foreign_save_heif_target_build;
VIPS_ARG_OBJECT( class, "target", 1,
_( "Target" ),
_( "Target to save to" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsForeignSaveHeifTarget, target ),
VIPS_TYPE_TARGET );
}
static void
vips_foreign_save_heif_target_init( VipsForeignSaveHeifTarget *target )
{
}
#endif /*HAVE_HEIF_ENCODER*/
/**
* vips_heifsave: (method)
* @in: image to save

View 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*/

View File

@ -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

View File

@ -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

View File

@ -53,6 +53,8 @@
* - option to attach associated images as metadata
* 22/6/20 adamu
* - set libvips xres/yres from openslide mpp-x/mpp-y
* 13/2/21 kleisauke
* - include GObject part from openslideload.c
*/
/*
@ -126,7 +128,7 @@ typedef struct {
int tile_height;
} ReadSlide;
int
static int
vips__openslide_isslide( const char *filename )
{
#ifdef HAVE_OPENSLIDE_3_4
@ -543,7 +545,7 @@ readslide_parse( ReadSlide *rslide, VipsImage *image )
return( 0 );
}
int
static int
vips__openslide_read_header( const char *filename, VipsImage *out,
int level, gboolean autocrop,
char *associated, gboolean attach_associated )
@ -614,7 +616,7 @@ vips__openslide_generate( VipsRegion *out,
return( 0 );
}
int
static int
vips__openslide_read( const char *filename, VipsImage *out,
int level, gboolean autocrop, gboolean attach_associated )
{
@ -658,7 +660,7 @@ vips__openslide_read( const char *filename, VipsImage *out,
return( 0 );
}
int
static int
vips__openslide_read_associated( const char *filename, VipsImage *out,
const char *associated )
{
@ -699,4 +701,373 @@ vips__openslide_read_associated( const char *filename, VipsImage *out,
return( 0 );
}
typedef struct _VipsForeignLoadOpenslide {
VipsForeignLoad parent_object;
/* Source to load from (set by subclasses).
*/
VipsSource *source;
/* Filename from source.
*/
const char *filename;
/* Load this level.
*/
int level;
/* Crop to image bounds.
*/
gboolean autocrop;
/* Load just this associated image.
*/
char *associated;
/* Attach all associated images as metadata items.
*/
gboolean attach_associated;
} VipsForeignLoadOpenslide;
typedef VipsForeignLoadClass VipsForeignLoadOpenslideClass;
G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadOpenslide, vips_foreign_load_openslide,
VIPS_TYPE_FOREIGN_LOAD );
static void
vips_foreign_load_openslide_dispose( GObject *gobject )
{
VipsForeignLoadOpenslide *openslide =
(VipsForeignLoadOpenslide *) gobject;
VIPS_UNREF( openslide->source );
G_OBJECT_CLASS( vips_foreign_load_openslide_parent_class )->
dispose( gobject );
}
static int
vips_foreign_load_openslide_build( VipsObject *object )
{
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
VipsForeignLoadOpenslide *openslide =
(VipsForeignLoadOpenslide *) object;
/* We can only open source which have an associated filename, since
* the openslide library works in terms of filenames.
*/
if( openslide->source ) {
openslide->filename =
vips_connection_filename( VIPS_CONNECTION(
openslide->source ) );
if( !openslide->filename ) {
vips_error( class->nickname, "%s",
_( "no filename available" ) );
return( -1 );
}
}
if( VIPS_OBJECT_CLASS( vips_foreign_load_openslide_parent_class )->
build( object ) )
return( -1 );
return( 0 );
}
static VipsForeignFlags
vips_foreign_load_openslide_get_flags_source( VipsSource *source )
{
/* We can't tell from just the source, we need to know what part of
* the file the user wants. But it'll usually be partial.
*/
return( VIPS_FOREIGN_PARTIAL );
}
static VipsForeignFlags
vips_foreign_load_openslide_get_flags( VipsForeignLoad *load )
{
VipsForeignLoadOpenslide *openslide = (VipsForeignLoadOpenslide *) load;
VipsForeignFlags flags;
flags = 0;
if( !openslide->associated )
flags |= VIPS_FOREIGN_PARTIAL;
return( flags );
}
static VipsForeignFlags
vips_foreign_load_openslide_get_flags_filename( const char *filename )
{
VipsSource *source;
VipsForeignFlags flags;
if( !(source = vips_source_new_from_file( filename )) )
return( 0 );
flags = vips_foreign_load_openslide_get_flags_source( source );
VIPS_UNREF( source );
return( flags );
}
static int
vips_foreign_load_openslide_header( VipsForeignLoad *load )
{
VipsForeignLoadOpenslide *openslide = (VipsForeignLoadOpenslide *) load;
if( vips__openslide_read_header( openslide->filename, load->out,
openslide->level, openslide->autocrop,
openslide->associated, openslide->attach_associated ) )
return( -1 );
VIPS_SETSTR( load->out->filename, openslide->filename );
return( 0 );
}
static int
vips_foreign_load_openslide_load( VipsForeignLoad *load )
{
VipsForeignLoadOpenslide *openslide = (VipsForeignLoadOpenslide *) load;
if( !openslide->associated ) {
if( vips__openslide_read( openslide->filename, load->real,
openslide->level, openslide->autocrop,
openslide->attach_associated ) )
return( -1 );
}
else {
if( vips__openslide_read_associated( openslide->filename,
load->real, openslide->associated ) )
return( -1 );
}
return( 0 );
}
static void
vips_foreign_load_openslide_class_init( VipsForeignLoadOpenslideClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *object_class = (VipsObjectClass *) class;
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
gobject_class->dispose = vips_foreign_load_openslide_dispose;
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "openslideload_base";
object_class->description = _( "load OpenSlide base class" );
object_class->build = vips_foreign_load_openslide_build;
/* We need to be ahead of the tiff sniffer since many OpenSlide
* formats are tiff derivatives. If we see a tiff which would be
* better handled by the vips tiff loader we are careful to say no.
*
* We need to be ahead of JPEG, since MRXS images are also
* JPEGs.
*/
foreign_class->priority = 100;
load_class->get_flags_filename =
vips_foreign_load_openslide_get_flags_filename;
load_class->get_flags = vips_foreign_load_openslide_get_flags;
load_class->header = vips_foreign_load_openslide_header;
load_class->load = vips_foreign_load_openslide_load;
VIPS_ARG_INT( class, "level", 20,
_( "Level" ),
_( "Load this level from the file" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadOpenslide, level ),
0, 100000, 0 );
VIPS_ARG_BOOL( class, "autocrop", 21,
_( "Autocrop" ),
_( "Crop to image bounds" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadOpenslide, autocrop ),
FALSE );
VIPS_ARG_STRING( class, "associated", 22,
_( "Associated" ),
_( "Load this associated image" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadOpenslide, associated ),
NULL );
VIPS_ARG_BOOL( class, "attach-associated", 13,
_( "Attach associated" ),
_( "Attach all associated images" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadOpenslide, attach_associated ),
FALSE );
}
static void
vips_foreign_load_openslide_init( VipsForeignLoadOpenslide *openslide )
{
}
typedef struct _VipsForeignLoadOpenslideFile {
VipsForeignLoadOpenslide parent_object;
/* Filename for load.
*/
char *filename;
} VipsForeignLoadOpenslideFile;
typedef VipsForeignLoadOpenslideClass VipsForeignLoadOpenslideFileClass;
G_DEFINE_TYPE( VipsForeignLoadOpenslideFile, vips_foreign_load_openslide_file,
vips_foreign_load_openslide_get_type() );
static int
vips_foreign_load_openslide_file_build( VipsObject *object )
{
VipsForeignLoadOpenslide *openslide =
(VipsForeignLoadOpenslide *) object;
VipsForeignLoadOpenslideFile *file =
(VipsForeignLoadOpenslideFile *) object;
if( file->filename &&
!(openslide->source =
vips_source_new_from_file( file->filename )) )
return( -1 );
if( VIPS_OBJECT_CLASS( vips_foreign_load_openslide_file_parent_class )->
build( object ) )
return( -1 );
return( 0 );
}
static const char *vips_foreign_openslide_suffs[] = {
".svs", /* Aperio */
".vms", ".vmu", ".ndpi", /* Hamamatsu */
".scn", /* Leica */
".mrxs", /* MIRAX */
".svslide", /* Sakura */
".tif", /* Trestle */
".bif", /* Ventana */
NULL
};
static void
vips_foreign_load_openslide_file_class_init(
VipsForeignLoadOpenslideFileClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *object_class = (VipsObjectClass *) class;
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "openslideload";
object_class->description = _( "load file with OpenSlide" );
object_class->build = vips_foreign_load_openslide_file_build;
foreign_class->suffs = vips_foreign_openslide_suffs;
load_class->is_a = vips__openslide_isslide;
VIPS_ARG_STRING( class, "filename", 1,
_( "Filename" ),
_( "Filename to load from" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadOpenslideFile, filename ),
NULL );
}
static void
vips_foreign_load_openslide_file_init( VipsForeignLoadOpenslideFile *openslide )
{
}
typedef struct _VipsForeignLoadOpenslideSource {
VipsForeignLoadOpenslide parent_object;
/* Load from a source.
*/
VipsSource *source;
} VipsForeignLoadOpenslideSource;
typedef VipsForeignLoadOpenslideClass VipsForeignLoadOpenslideSourceClass;
G_DEFINE_TYPE( VipsForeignLoadOpenslideSource,
vips_foreign_load_openslide_source,
vips_foreign_load_openslide_get_type() );
static int
vips_foreign_load_openslide_source_build( VipsObject *object )
{
VipsForeignLoadOpenslide *openslide =
(VipsForeignLoadOpenslide *) object;
VipsForeignLoadOpenslideSource *source =
(VipsForeignLoadOpenslideSource *) object;
if( source->source ) {
openslide->source = source->source;
g_object_ref( openslide->source );
}
if( VIPS_OBJECT_CLASS(
vips_foreign_load_openslide_source_parent_class )->
build( object ) )
return( -1 );
return( 0 );
}
static gboolean
vips_foreign_load_openslide_source_is_a_source( VipsSource *source )
{
const char *filename;
return( (filename =
vips_connection_filename( VIPS_CONNECTION( source ) )) &&
vips__openslide_isslide( filename ) );
}
static void
vips_foreign_load_openslide_source_class_init(
VipsForeignLoadOpenslideSourceClass *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 = "openslideload_source";
object_class->description = _( "load source with OpenSlide" );
object_class->build = vips_foreign_load_openslide_source_build;
load_class->is_a_source =
vips_foreign_load_openslide_source_is_a_source;
VIPS_ARG_OBJECT( class, "source", 1,
_( "Source" ),
_( "Source to load from" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadOpenslideSource, source ),
VIPS_TYPE_SOURCE );
}
static void
vips_foreign_load_openslide_source_init(
VipsForeignLoadOpenslideSource *openslide )
{
}
#endif /*HAVE_OPENSLIDE*/

View File

@ -11,6 +11,8 @@
* - drop glib log handler (unneeded with >= 3.3.0)
* 27/1/18
* - option to attach associated images as metadata
* 13/2/21 kleisauke
* - move GObject part to openslide2vips.c
*/
/*
@ -57,381 +59,6 @@
#include <vips/buf.h>
#include <vips/internal.h>
#include "pforeign.h"
#ifdef HAVE_OPENSLIDE
typedef struct _VipsForeignLoadOpenslide {
VipsForeignLoad parent_object;
/* Source to load from (set by subclasses).
*/
VipsSource *source;
/* Filename from source.
*/
const char *filename;
/* Load this level.
*/
int level;
/* Crop to image bounds.
*/
gboolean autocrop;
/* Load just this associated image.
*/
char *associated;
/* Attach all associated images as metadata items.
*/
gboolean attach_associated;
} VipsForeignLoadOpenslide;
typedef VipsForeignLoadClass VipsForeignLoadOpenslideClass;
G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadOpenslide, vips_foreign_load_openslide,
VIPS_TYPE_FOREIGN_LOAD );
static void
vips_foreign_load_openslide_dispose( GObject *gobject )
{
VipsForeignLoadOpenslide *openslide =
(VipsForeignLoadOpenslide *) gobject;
VIPS_UNREF( openslide->source );
G_OBJECT_CLASS( vips_foreign_load_openslide_parent_class )->
dispose( gobject );
}
static int
vips_foreign_load_openslide_build( VipsObject *object )
{
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
VipsForeignLoadOpenslide *openslide =
(VipsForeignLoadOpenslide *) object;
/* We can only open source which have an associated filename, since
* the openslide library works in terms of filenames.
*/
if( openslide->source ) {
openslide->filename =
vips_connection_filename( VIPS_CONNECTION(
openslide->source ) );
if( !openslide->filename ) {
vips_error( class->nickname, "%s",
_( "no filename available" ) );
return( -1 );
}
}
if( VIPS_OBJECT_CLASS( vips_foreign_load_openslide_parent_class )->
build( object ) )
return( -1 );
return( 0 );
}
static VipsForeignFlags
vips_foreign_load_openslide_get_flags_source( VipsSource *source )
{
/* We can't tell from just the source, we need to know what part of
* the file the user wants. But it'll usually be partial.
*/
return( VIPS_FOREIGN_PARTIAL );
}
static VipsForeignFlags
vips_foreign_load_openslide_get_flags( VipsForeignLoad *load )
{
VipsForeignLoadOpenslide *openslide = (VipsForeignLoadOpenslide *) load;
VipsForeignFlags flags;
flags = 0;
if( !openslide->associated )
flags |= VIPS_FOREIGN_PARTIAL;
return( flags );
}
static VipsForeignFlags
vips_foreign_load_openslide_get_flags_filename( const char *filename )
{
VipsSource *source;
VipsForeignFlags flags;
if( !(source = vips_source_new_from_file( filename )) )
return( 0 );
flags = vips_foreign_load_openslide_get_flags_source( source );
VIPS_UNREF( source );
return( flags );
}
static int
vips_foreign_load_openslide_header( VipsForeignLoad *load )
{
VipsForeignLoadOpenslide *openslide = (VipsForeignLoadOpenslide *) load;
if( vips__openslide_read_header( openslide->filename, load->out,
openslide->level, openslide->autocrop,
openslide->associated, openslide->attach_associated ) )
return( -1 );
VIPS_SETSTR( load->out->filename, openslide->filename );
return( 0 );
}
static int
vips_foreign_load_openslide_load( VipsForeignLoad *load )
{
VipsForeignLoadOpenslide *openslide = (VipsForeignLoadOpenslide *) load;
if( !openslide->associated ) {
if( vips__openslide_read( openslide->filename, load->real,
openslide->level, openslide->autocrop,
openslide->attach_associated ) )
return( -1 );
}
else {
if( vips__openslide_read_associated( openslide->filename,
load->real, openslide->associated ) )
return( -1 );
}
return( 0 );
}
static void
vips_foreign_load_openslide_class_init( VipsForeignLoadOpenslideClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *object_class = (VipsObjectClass *) class;
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
gobject_class->dispose = vips_foreign_load_openslide_dispose;
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "openslideload_base";
object_class->description = _( "load OpenSlide base class" );
object_class->build = vips_foreign_load_openslide_build;
/* We need to be ahead of the tiff sniffer since many OpenSlide
* formats are tiff derivatives. If we see a tiff which would be
* better handled by the vips tiff loader we are careful to say no.
*
* We need to be ahead of JPEG, since MRXS images are also
* JPEGs.
*/
foreign_class->priority = 100;
load_class->get_flags_filename =
vips_foreign_load_openslide_get_flags_filename;
load_class->get_flags = vips_foreign_load_openslide_get_flags;
load_class->header = vips_foreign_load_openslide_header;
load_class->load = vips_foreign_load_openslide_load;
VIPS_ARG_INT( class, "level", 20,
_( "Level" ),
_( "Load this level from the file" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadOpenslide, level ),
0, 100000, 0 );
VIPS_ARG_BOOL( class, "autocrop", 21,
_( "Autocrop" ),
_( "Crop to image bounds" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadOpenslide, autocrop ),
FALSE );
VIPS_ARG_STRING( class, "associated", 22,
_( "Associated" ),
_( "Load this associated image" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadOpenslide, associated ),
NULL );
VIPS_ARG_BOOL( class, "attach-associated", 13,
_( "Attach associated" ),
_( "Attach all associated images" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadOpenslide, attach_associated ),
FALSE );
}
static void
vips_foreign_load_openslide_init( VipsForeignLoadOpenslide *openslide )
{
}
typedef struct _VipsForeignLoadOpenslideFile {
VipsForeignLoadOpenslide parent_object;
/* Filename for load.
*/
char *filename;
} VipsForeignLoadOpenslideFile;
typedef VipsForeignLoadOpenslideClass VipsForeignLoadOpenslideFileClass;
G_DEFINE_TYPE( VipsForeignLoadOpenslideFile, vips_foreign_load_openslide_file,
vips_foreign_load_openslide_get_type() );
static int
vips_foreign_load_openslide_file_build( VipsObject *object )
{
VipsForeignLoadOpenslide *openslide =
(VipsForeignLoadOpenslide *) object;
VipsForeignLoadOpenslideFile *file =
(VipsForeignLoadOpenslideFile *) object;
if( file->filename &&
!(openslide->source =
vips_source_new_from_file( file->filename )) )
return( -1 );
if( VIPS_OBJECT_CLASS( vips_foreign_load_openslide_file_parent_class )->
build( object ) )
return( -1 );
return( 0 );
}
static const char *vips_foreign_openslide_suffs[] = {
".svs", /* Aperio */
".vms", ".vmu", ".ndpi", /* Hamamatsu */
".scn", /* Leica */
".mrxs", /* MIRAX */
".svslide", /* Sakura */
".tif", /* Trestle */
".bif", /* Ventana */
NULL
};
static void
vips_foreign_load_openslide_file_class_init(
VipsForeignLoadOpenslideFileClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *object_class = (VipsObjectClass *) class;
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "openslideload";
object_class->description = _( "load file with OpenSlide" );
object_class->build = vips_foreign_load_openslide_file_build;
foreign_class->suffs = vips_foreign_openslide_suffs;
load_class->is_a = vips__openslide_isslide;
VIPS_ARG_STRING( class, "filename", 1,
_( "Filename" ),
_( "Filename to load from" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadOpenslideFile, filename ),
NULL );
}
static void
vips_foreign_load_openslide_file_init( VipsForeignLoadOpenslideFile *openslide )
{
}
typedef struct _VipsForeignLoadOpenslideSource {
VipsForeignLoadOpenslide parent_object;
/* Load from a source.
*/
VipsSource *source;
} VipsForeignLoadOpenslideSource;
typedef VipsForeignLoadOpenslideClass VipsForeignLoadOpenslideSourceClass;
G_DEFINE_TYPE( VipsForeignLoadOpenslideSource,
vips_foreign_load_openslide_source,
vips_foreign_load_openslide_get_type() );
static int
vips_foreign_load_openslide_source_build( VipsObject *object )
{
VipsForeignLoadOpenslide *openslide =
(VipsForeignLoadOpenslide *) object;
VipsForeignLoadOpenslideSource *source =
(VipsForeignLoadOpenslideSource *) object;
if( source->source ) {
openslide->source = source->source;
g_object_ref( openslide->source );
}
if( VIPS_OBJECT_CLASS(
vips_foreign_load_openslide_source_parent_class )->
build( object ) )
return( -1 );
return( 0 );
}
static gboolean
vips_foreign_load_openslide_source_is_a_source( VipsSource *source )
{
const char *filename;
return( (filename =
vips_connection_filename( VIPS_CONNECTION( source ) )) &&
vips__openslide_isslide( filename ) );
}
static void
vips_foreign_load_openslide_source_class_init(
VipsForeignLoadOpenslideSourceClass *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 = "openslideload_source";
object_class->description = _( "load source with OpenSlide" );
object_class->build = vips_foreign_load_openslide_source_build;
load_class->is_a_source =
vips_foreign_load_openslide_source_is_a_source;
VIPS_ARG_OBJECT( class, "source", 1,
_( "Source" ),
_( "Source to load from" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadOpenslideSource, source ),
VIPS_TYPE_SOURCE );
}
static void
vips_foreign_load_openslide_source_init(
VipsForeignLoadOpenslideSource *openslide )
{
}
#endif /*HAVE_OPENSLIDE*/
/**
* vips_openslideload:
* @filename: file to load

View File

@ -18,6 +18,8 @@
* - move on top of VipsSource
* 21/9/20
* - allow dpi and scale to both be set [le0daniel]
* 21/4/21 kleisauke
* - move GObject part to poppler2vips.c
*/
/*
@ -67,747 +69,6 @@
#include "pforeign.h"
#if defined(HAVE_POPPLER)
#include <cairo.h>
#include <poppler.h>
#define VIPS_TYPE_FOREIGN_LOAD_PDF (vips_foreign_load_pdf_get_type())
#define VIPS_FOREIGN_LOAD_PDF( obj ) \
(G_TYPE_CHECK_INSTANCE_CAST( (obj), \
VIPS_TYPE_FOREIGN_LOAD_PDF, VipsForeignLoadPdf ))
#define VIPS_FOREIGN_LOAD_PDF_CLASS( klass ) \
(G_TYPE_CHECK_CLASS_CAST( (klass), \
VIPS_TYPE_FOREIGN_LOAD_PDF, VipsForeignLoadPdfClass))
#define VIPS_IS_FOREIGN_LOAD_PDF( obj ) \
(G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_FOREIGN_LOAD_PDF ))
#define VIPS_IS_FOREIGN_LOAD_PDF_CLASS( klass ) \
(G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_FOREIGN_LOAD_PDF ))
#define VIPS_FOREIGN_LOAD_PDF_GET_CLASS( obj ) \
(G_TYPE_INSTANCE_GET_CLASS( (obj), \
VIPS_TYPE_FOREIGN_LOAD_PDF, VipsForeignLoadPdfClass ))
typedef struct _VipsForeignLoadPdf {
VipsForeignLoad parent_object;
/* The VipsSource we load from, and the GInputStream we wrap around
* it. Set from subclasses.
*/
VipsSource *source;
GInputStream *stream;
/* Load this page.
*/
int page_no;
/* Load this many pages.
*/
int n;
/* Render at this DPI.
*/
double dpi;
/* Scale by this factor.
*/
double scale;
/* The total scale factor we render with.
*/
double total_scale;
/* Background colour.
*/
VipsArrayDouble *background;
/* Poppler is not thread-safe, so we run inside a single-threaded
* cache. On the plus side, this means we only need one @page pointer,
* even though we change this during _generate().
*/
PopplerDocument *doc;
PopplerPage *page;
int current_page;
/* Doc has this many pages.
*/
int n_pages;
/* We need to read out the size of each page we will render, and lay
* them out in the final image.
*/
VipsRect image;
VipsRect *pages;
/* The [double] background converted to the image format.
*/
VipsPel *ink;
} VipsForeignLoadPdf;
typedef struct _VipsForeignLoadPdfClass {
VipsForeignLoadClass parent_class;
} VipsForeignLoadPdfClass;
G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadPdf, vips_foreign_load_pdf,
VIPS_TYPE_FOREIGN_LOAD );
static void
vips_foreign_load_pdf_dispose( GObject *gobject )
{
VipsForeignLoadPdf *pdf = VIPS_FOREIGN_LOAD_PDF( gobject );
VIPS_UNREF( pdf->page );
VIPS_UNREF( pdf->doc );
VIPS_UNREF( pdf->source );
VIPS_UNREF( pdf->stream );
G_OBJECT_CLASS( vips_foreign_load_pdf_parent_class )->
dispose( gobject );
}
static int
vips_foreign_load_pdf_build( VipsObject *object )
{
VipsForeignLoadPdf *pdf = VIPS_FOREIGN_LOAD_PDF( object );
GError *error = NULL;
if( vips_source_rewind( pdf->source ) )
return( -1 );
pdf->total_scale = pdf->scale * pdf->dpi / 72.0;
pdf->stream = vips_g_input_stream_new_from_source( pdf->source );
if( !(pdf->doc = poppler_document_new_from_stream( pdf->stream,
vips_source_length( pdf->source ), NULL, NULL, &error )) ) {
vips_g_error( &error );
return( -1 );
}
if( VIPS_OBJECT_CLASS( vips_foreign_load_pdf_parent_class )->
build( object ) )
return( -1 );
return( 0 );
}
static VipsForeignFlags
vips_foreign_load_pdf_get_flags_filename( const char *filename )
{
/* We can render any part of the page on demand.
*/
return( VIPS_FOREIGN_PARTIAL );
}
static VipsForeignFlags
vips_foreign_load_pdf_get_flags( VipsForeignLoad *load )
{
return( VIPS_FOREIGN_PARTIAL );
}
static int
vips_foreign_load_pdf_get_page( VipsForeignLoadPdf *pdf, int page_no )
{
if( pdf->current_page != page_no ||
!pdf->page ) {
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( pdf );
VIPS_UNREF( pdf->page );
pdf->current_page = -1;
#ifdef DEBUG
printf( "vips_foreign_load_pdf_get_page: %d\n", page_no );
#endif /*DEBUG*/
if( !(pdf->page = poppler_document_get_page( pdf->doc, page_no )) ) {
vips_error( class->nickname,
_( "unable to load page %d" ), page_no );
return( -1 );
}
pdf->current_page = page_no;
}
return( 0 );
}
/* String-based metadata fields we extract.
*/
typedef struct _VipsForeignLoadPdfMetadata {
char *(*pdf_fetch)( PopplerDocument *doc );
char *field;
} VipsForeignLoadPdfMetadata;
static VipsForeignLoadPdfMetadata vips_foreign_load_pdf_metadata[] = {
{ poppler_document_get_title, "pdf-title" },
{ poppler_document_get_author, "pdf-author" },
{ poppler_document_get_subject, "pdf-subject" },
{ poppler_document_get_keywords, "pdf-keywords" },
{ poppler_document_get_creator, "pdf-creator" },
{ poppler_document_get_producer, "pdf-producer" },
{ poppler_document_get_metadata, "pdf-metadata" },
};
static int n_metadata = VIPS_NUMBER( vips_foreign_load_pdf_metadata );
static int
vips_foreign_load_pdf_set_image( VipsForeignLoadPdf *pdf, VipsImage *out )
{
int i;
double res;
#ifdef DEBUG
printf( "vips_foreign_load_pdf_set_image: %p\n", pdf );
#endif /*DEBUG*/
/* We render to a linecache, so fat strips work well.
*/
vips_image_pipelinev( out, VIPS_DEMAND_STYLE_FATSTRIP, NULL );
/* Extract and attach metadata. Set the old name too for compat.
*/
vips_image_set_int( out, "pdf-n_pages", pdf->n_pages );
vips_image_set_int( out, VIPS_META_N_PAGES, pdf->n_pages );
for( i = 0; i < n_metadata; i++ ) {
VipsForeignLoadPdfMetadata *metadata =
&vips_foreign_load_pdf_metadata[i];
char *str;
if( (str = metadata->pdf_fetch( pdf->doc )) ) {
vips_image_set_string( out, metadata->field, str );
g_free( str );
}
}
/* We need pixels/mm for vips.
*/
res = pdf->dpi / 25.4;
vips_image_init_fields( out,
pdf->image.width, pdf->image.height,
4, VIPS_FORMAT_UCHAR,
VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, res, res );
return( 0 );
}
static int
vips_foreign_load_pdf_header( VipsForeignLoad *load )
{
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load );
VipsForeignLoadPdf *pdf = VIPS_FOREIGN_LOAD_PDF( load );
int top;
int i;
#ifdef DEBUG
printf( "vips_foreign_load_pdf_header: %p\n", pdf );
#endif /*DEBUG*/
pdf->n_pages = poppler_document_get_n_pages( pdf->doc );
/* @n == -1 means until the end of the doc.
*/
if( pdf->n == -1 )
pdf->n = pdf->n_pages - pdf->page_no;
if( pdf->page_no + pdf->n > pdf->n_pages ||
pdf->page_no < 0 ||
pdf->n <= 0 ) {
vips_error( class->nickname, "%s", _( "pages out of range" ) );
return( -1 );
}
/* Lay out the pages in our output image.
*/
if( !(pdf->pages = VIPS_ARRAY( pdf, pdf->n, VipsRect )) )
return( -1 );
top = 0;
pdf->image.left = 0;
pdf->image.top = 0;
pdf->image.width = 0;
pdf->image.height = 0;
for( i = 0; i < pdf->n; i++ ) {
double width;
double height;
if( vips_foreign_load_pdf_get_page( pdf, pdf->page_no + i ) )
return( -1 );
poppler_page_get_size( pdf->page, &width, &height );
pdf->pages[i].left = 0;
pdf->pages[i].top = top;
/* We do round to nearest, in the same way that vips_resize()
* does round to nearest. Without this, things like
* shrink-on-load will break.
*/
pdf->pages[i].width = VIPS_RINT( width * pdf->total_scale );
pdf->pages[i].height = VIPS_RINT( height * pdf->total_scale );
if( pdf->pages[i].width > pdf->image.width )
pdf->image.width = pdf->pages[i].width;
pdf->image.height += pdf->pages[i].height;
top += pdf->pages[i].height;
}
/* If all pages are the same height, we can tag this as a toilet roll
* image.
*/
for( i = 1; i < pdf->n; i++ )
if( pdf->pages[i].height != pdf->pages[0].height )
break;
if( vips_object_argument_isset( VIPS_OBJECT( pdf ), "n" ) )
vips_image_set_int( load->out,
VIPS_META_PAGE_HEIGHT, pdf->pages[0].height );
vips_foreign_load_pdf_set_image( pdf, load->out );
/* Convert the background to the image format.
*
* FIXME ... we probably should convert this to pre-multiplied BGRA
* to match the Cairo convention. See vips__premultiplied_bgra2rgba().
*/
if( !(pdf->ink = vips__vector_to_ink( class->nickname,
load->out,
VIPS_AREA( pdf->background )->data, NULL,
VIPS_AREA( pdf->background )->n )) )
return( -1 );
vips_source_minimise( pdf->source );
return( 0 );
}
static void
vips_foreign_load_pdf_minimise( VipsImage *image, VipsForeignLoadPdf *pdf )
{
vips_source_minimise( pdf->source );
}
static int
vips_foreign_load_pdf_generate( VipsRegion *or,
void *seq, void *a, void *b, gboolean *stop )
{
VipsForeignLoadPdf *pdf = VIPS_FOREIGN_LOAD_PDF( a );
VipsRect *r = &or->valid;
int top;
int i;
int y;
/*
printf( "vips_foreign_load_pdf_generate: "
"left = %d, top = %d, width = %d, height = %d\n",
r->left, r->top, r->width, r->height );
*/
/* Poppler won't always paint the background.
*/
vips_region_paint_pel( or, r, pdf->ink );
/* Search through the pages we are drawing for the first containing
* this rect. This could be quicker, perhaps a binary search, but who
* cares.
*/
for( i = 0; i < pdf->n; i++ )
if( VIPS_RECT_BOTTOM( &pdf->pages[i] ) > r->top )
break;
top = r->top;
while( top < VIPS_RECT_BOTTOM( r ) ) {
VipsRect rect;
cairo_surface_t *surface;
cairo_t *cr;
vips_rect_intersectrect( r, &pdf->pages[i], &rect );
surface = cairo_image_surface_create_for_data(
VIPS_REGION_ADDR( or, rect.left, rect.top ),
CAIRO_FORMAT_ARGB32,
rect.width, rect.height,
VIPS_REGION_LSKIP( or ) );
cr = cairo_create( surface );
cairo_surface_destroy( surface );
cairo_scale( cr, pdf->total_scale, pdf->total_scale );
cairo_translate( cr,
(pdf->pages[i].left - rect.left) / pdf->total_scale,
(pdf->pages[i].top - rect.top) / pdf->total_scale );
/* poppler is single-threaded, but we don't need to lock since
* we're running inside a non-threaded tilecache.
*/
if( vips_foreign_load_pdf_get_page( pdf, pdf->page_no + i ) )
return( -1 );
poppler_page_render( pdf->page, cr );
cairo_destroy( cr );
top += rect.height;
i += 1;
}
/* Cairo makes pre-multipled BRGA, we must byteswap and unpremultiply.
*/
for( y = 0; y < r->height; y++ )
vips__premultiplied_bgra2rgba(
(guint32 *) VIPS_REGION_ADDR( or, r->left, r->top + y ),
r->width );
return( 0 );
}
static int
vips_foreign_load_pdf_load( VipsForeignLoad *load )
{
VipsForeignLoadPdf *pdf = VIPS_FOREIGN_LOAD_PDF( load );
VipsImage **t = (VipsImage **)
vips_object_local_array( (VipsObject *) load, 2 );
#ifdef DEBUG
printf( "vips_foreign_load_pdf_load: %p\n", pdf );
#endif /*DEBUG*/
/* Read to this image, then cache to out, see below.
*/
t[0] = vips_image_new();
/* Close input immediately at end of read.
*/
g_signal_connect( t[0], "minimise",
G_CALLBACK( vips_foreign_load_pdf_minimise ), pdf );
/* Very large strips to limit render calls per page.
*/
vips_foreign_load_pdf_set_image( pdf, t[0] );
if( vips_image_generate( t[0],
NULL, vips_foreign_load_pdf_generate, NULL, pdf, NULL ) ||
vips_sequential( t[0], &t[1],
"tile_height", VIPS_MIN( 5000, pdf->pages[0].height ),
NULL ) ||
vips_image_write( t[1], load->real ) )
return( -1 );
return( 0 );
}
static void
vips_foreign_load_pdf_class_init( VipsForeignLoadPdfClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *object_class = (VipsObjectClass *) class;
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
gobject_class->dispose = vips_foreign_load_pdf_dispose;
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "pdfload_base";
object_class->description = _( "load PDF with libpoppler" );
object_class->build = vips_foreign_load_pdf_build;
load_class->get_flags_filename =
vips_foreign_load_pdf_get_flags_filename;
load_class->get_flags = vips_foreign_load_pdf_get_flags;
load_class->header = vips_foreign_load_pdf_header;
load_class->load = vips_foreign_load_pdf_load;
VIPS_ARG_INT( class, "page", 20,
_( "Page" ),
_( "Load this page from the file" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadPdf, page_no ),
0, 100000, 0 );
VIPS_ARG_INT( class, "n", 21,
_( "n" ),
_( "Load this many pages" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadPdf, n ),
-1, 100000, 1 );
VIPS_ARG_DOUBLE( class, "dpi", 22,
_( "DPI" ),
_( "Render at this DPI" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadPdf, dpi ),
0.001, 100000.0, 72.0 );
VIPS_ARG_DOUBLE( class, "scale", 23,
_( "Scale" ),
_( "Scale output by this factor" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadPdf, scale ),
0.001, 100000.0, 1.0 );
VIPS_ARG_BOXED( class, "background", 24,
_( "Background" ),
_( "Background value" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadPdf, background ),
VIPS_TYPE_ARRAY_DOUBLE );
}
static void
vips_foreign_load_pdf_init( VipsForeignLoadPdf *pdf )
{
pdf->dpi = 72.0;
pdf->scale = 1.0;
pdf->n = 1;
pdf->current_page = -1;
pdf->background = vips_array_double_newv( 1, 255.0 );
}
typedef struct _VipsForeignLoadPdfFile {
VipsForeignLoadPdf parent_object;
/* Filename for load.
*/
char *filename;
char *uri;
} VipsForeignLoadPdfFile;
typedef VipsForeignLoadPdfClass VipsForeignLoadPdfFileClass;
G_DEFINE_TYPE( VipsForeignLoadPdfFile, vips_foreign_load_pdf_file,
vips_foreign_load_pdf_get_type() );
static void
vips_foreign_load_pdf_file_dispose( GObject *gobject )
{
VipsForeignLoadPdfFile *file =
(VipsForeignLoadPdfFile *) gobject;
VIPS_FREE( file->uri );
G_OBJECT_CLASS( vips_foreign_load_pdf_file_parent_class )->
dispose( gobject );
}
static int
vips_foreign_load_pdf_file_header( VipsForeignLoad *load )
{
VipsForeignLoadPdfFile *file = (VipsForeignLoadPdfFile *) load;
VIPS_SETSTR( load->out->filename, file->filename );
return( VIPS_FOREIGN_LOAD_CLASS(
vips_foreign_load_pdf_file_parent_class )->header( load ) );
}
static const char *vips_foreign_pdf_suffs[] = {
".pdf",
NULL
};
static int
vips_foreign_load_pdf_file_build( VipsObject *object )
{
VipsForeignLoadPdf *pdf = VIPS_FOREIGN_LOAD_PDF( object );
VipsForeignLoadPdfFile *file = (VipsForeignLoadPdfFile *) pdf;
#ifdef DEBUG
printf( "vips_foreign_load_pdf_file_build: %s\n", file->filename );
#endif /*DEBUG*/
if( file->filename ) {
char *path;
GError *error = NULL;
/* We need an absolute path for a URI.
*/
path = vips_realpath( file->filename );
if( !(file->uri = g_filename_to_uri( path, NULL, &error )) ) {
g_free( path );
vips_g_error( &error );
return( -1 );
}
g_free( path );
if( !(pdf->source =
vips_source_new_from_file( file->filename )) )
return( -1 );
}
return( VIPS_OBJECT_CLASS( vips_foreign_load_pdf_file_parent_class )->
build( object ) );
}
static void
vips_foreign_load_pdf_file_class_init(
VipsForeignLoadPdfFileClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *object_class = (VipsObjectClass *) class;
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
gobject_class->dispose = vips_foreign_load_pdf_file_dispose;
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "pdfload";
object_class->description = _( "load PDF from file" );
object_class->build = vips_foreign_load_pdf_file_build;
foreign_class->suffs = vips_foreign_pdf_suffs;
load_class->is_a = vips_foreign_load_pdf_is_a;
load_class->header = vips_foreign_load_pdf_file_header;
VIPS_ARG_STRING( class, "filename", 1,
_( "Filename" ),
_( "Filename to load from" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadPdfFile, filename ),
NULL );
}
static void
vips_foreign_load_pdf_file_init( VipsForeignLoadPdfFile *file )
{
}
typedef struct _VipsForeignLoadPdfBuffer {
VipsForeignLoadPdf parent_object;
/* Load from a buffer.
*/
VipsArea *buf;
} VipsForeignLoadPdfBuffer;
typedef VipsForeignLoadPdfClass VipsForeignLoadPdfBufferClass;
G_DEFINE_TYPE( VipsForeignLoadPdfBuffer, vips_foreign_load_pdf_buffer,
vips_foreign_load_pdf_get_type() );
static int
vips_foreign_load_pdf_buffer_build( VipsObject *object )
{
VipsForeignLoadPdf *pdf = VIPS_FOREIGN_LOAD_PDF( object );
VipsForeignLoadPdfBuffer *buffer = (VipsForeignLoadPdfBuffer *) pdf;
if( buffer->buf &&
!(pdf->source = vips_source_new_from_memory(
VIPS_AREA( buffer->buf )->data,
VIPS_AREA( buffer->buf )->length )) )
return( -1 );
return( VIPS_OBJECT_CLASS( vips_foreign_load_pdf_buffer_parent_class )->
build( object ) );
}
static void
vips_foreign_load_pdf_buffer_class_init(
VipsForeignLoadPdfBufferClass *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 = "pdfload_buffer";
object_class->description = _( "load PDF from buffer" );
object_class->build = vips_foreign_load_pdf_buffer_build;
load_class->is_a_buffer = vips_foreign_load_pdf_is_a_buffer;
VIPS_ARG_BOXED( class, "buffer", 1,
_( "Buffer" ),
_( "Buffer to load from" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadPdfBuffer, buf ),
VIPS_TYPE_BLOB );
}
static void
vips_foreign_load_pdf_buffer_init( VipsForeignLoadPdfBuffer *buffer )
{
}
typedef struct _VipsForeignLoadPdfSource {
VipsForeignLoadPdf parent_object;
VipsSource *source;
} VipsForeignLoadPdfSource;
typedef VipsForeignLoadPdfClass VipsForeignLoadPdfSourceClass;
G_DEFINE_TYPE( VipsForeignLoadPdfSource, vips_foreign_load_pdf_source,
vips_foreign_load_pdf_get_type() );
static int
vips_foreign_load_pdf_source_build( VipsObject *object )
{
VipsForeignLoadPdf *pdf = VIPS_FOREIGN_LOAD_PDF( object );
VipsForeignLoadPdfSource *source = (VipsForeignLoadPdfSource *) pdf;
if( source->source ) {
pdf->source = source->source;
g_object_ref( pdf->source );
}
return( VIPS_OBJECT_CLASS( vips_foreign_load_pdf_source_parent_class )->
build( object ) );
}
static gboolean
vips_foreign_load_pdf_source_is_a_source( VipsSource *source )
{
const unsigned char *p;
return( (p = vips_source_sniff( source, 4 )) &&
p[0] == '%' &&
p[1] == 'P' &&
p[2] == 'D' &&
p[3] == 'F' );
}
static void
vips_foreign_load_pdf_source_class_init(
VipsForeignLoadPdfSourceClass *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 = "pdfload_source";
object_class->description = _( "load PDF from source" );
object_class->build = vips_foreign_load_pdf_source_build;
load_class->is_a_source = vips_foreign_load_pdf_source_is_a_source;
VIPS_ARG_OBJECT( class, "source", 1,
_( "Source" ),
_( "Source to load from" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadPdfSource, source ),
VIPS_TYPE_SOURCE );
}
static void
vips_foreign_load_pdf_source_init( VipsForeignLoadPdfSource *source )
{
}
#endif /*defined(HAVE_POPPLER)*/
/* Also used by the pdfium loader.
*/
gboolean

View File

@ -213,15 +213,6 @@ int vips__webp_write_target( VipsImage *image, VipsTarget *target,
gboolean min_size, int kmin, int kmax,
gboolean strip, const char *profile );
int vips__openslide_isslide( const char *filename );
int vips__openslide_read_header( const char *filename, VipsImage *out,
int level, gboolean autocrop,
char *associated, gboolean attach_associated );
int vips__openslide_read( const char *filename, VipsImage *out,
int level, gboolean autocrop, gboolean attach_associated );
int vips__openslide_read_associated( const char *filename, VipsImage *out,
const char *associated );
gboolean vips_foreign_load_pdf_is_a_buffer( const void *buf, size_t len );
gboolean vips_foreign_load_pdf_is_a( const char *filename );

View File

@ -0,0 +1,811 @@
/* load PDF with libpoppler
*
* 7/2/16
* - from openslideload.c
* 12/5/16
* - add @n ... number of pages to load
* 23/11/16
* - set page-height, if we can
* 28/6/17
* - use a much larger strip size, thanks bubba
* 8/6/18
* - add background param
* 16/8/18 [kleisauke]
* - shut down the input file as soon as we can
* 19/9/19
* - reopen the input if we minimised too early
* 11/3/20
* - move on top of VipsSource
* 21/9/20
* - allow dpi and scale to both be set [le0daniel]
* 21/4/21 kleisauke
* - include GObject part from pdfload.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 <errno.h>
#include <vips/vips.h>
#include <vips/buf.h>
#include <vips/internal.h>
#include "pforeign.h"
#ifdef HAVE_POPPLER
#include <cairo.h>
#include <poppler.h>
#define VIPS_TYPE_FOREIGN_LOAD_PDF (vips_foreign_load_pdf_get_type())
#define VIPS_FOREIGN_LOAD_PDF( obj ) \
(G_TYPE_CHECK_INSTANCE_CAST( (obj), \
VIPS_TYPE_FOREIGN_LOAD_PDF, VipsForeignLoadPdf ))
#define VIPS_FOREIGN_LOAD_PDF_CLASS( klass ) \
(G_TYPE_CHECK_CLASS_CAST( (klass), \
VIPS_TYPE_FOREIGN_LOAD_PDF, VipsForeignLoadPdfClass))
#define VIPS_IS_FOREIGN_LOAD_PDF( obj ) \
(G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_FOREIGN_LOAD_PDF ))
#define VIPS_IS_FOREIGN_LOAD_PDF_CLASS( klass ) \
(G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_FOREIGN_LOAD_PDF ))
#define VIPS_FOREIGN_LOAD_PDF_GET_CLASS( obj ) \
(G_TYPE_INSTANCE_GET_CLASS( (obj), \
VIPS_TYPE_FOREIGN_LOAD_PDF, VipsForeignLoadPdfClass ))
typedef struct _VipsForeignLoadPdf {
VipsForeignLoad parent_object;
/* The VipsSource we load from, and the GInputStream we wrap around
* it. Set from subclasses.
*/
VipsSource *source;
GInputStream *stream;
/* Load this page.
*/
int page_no;
/* Load this many pages.
*/
int n;
/* Render at this DPI.
*/
double dpi;
/* Scale by this factor.
*/
double scale;
/* The total scale factor we render with.
*/
double total_scale;
/* Background colour.
*/
VipsArrayDouble *background;
/* Poppler is not thread-safe, so we run inside a single-threaded
* cache. On the plus side, this means we only need one @page pointer,
* even though we change this during _generate().
*/
PopplerDocument *doc;
PopplerPage *page;
int current_page;
/* Doc has this many pages.
*/
int n_pages;
/* We need to read out the size of each page we will render, and lay
* them out in the final image.
*/
VipsRect image;
VipsRect *pages;
/* The [double] background converted to the image format.
*/
VipsPel *ink;
} VipsForeignLoadPdf;
typedef struct _VipsForeignLoadPdfClass {
VipsForeignLoadClass parent_class;
} VipsForeignLoadPdfClass;
G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadPdf, vips_foreign_load_pdf,
VIPS_TYPE_FOREIGN_LOAD );
static void
vips_foreign_load_pdf_dispose( GObject *gobject )
{
VipsForeignLoadPdf *pdf = VIPS_FOREIGN_LOAD_PDF( gobject );
VIPS_UNREF( pdf->page );
VIPS_UNREF( pdf->doc );
VIPS_UNREF( pdf->source );
VIPS_UNREF( pdf->stream );
G_OBJECT_CLASS( vips_foreign_load_pdf_parent_class )->
dispose( gobject );
}
static int
vips_foreign_load_pdf_build( VipsObject *object )
{
VipsForeignLoadPdf *pdf = VIPS_FOREIGN_LOAD_PDF( object );
GError *error = NULL;
if( vips_source_rewind( pdf->source ) )
return( -1 );
pdf->total_scale = pdf->scale * pdf->dpi / 72.0;
pdf->stream = vips_g_input_stream_new_from_source( pdf->source );
if( !(pdf->doc = poppler_document_new_from_stream( pdf->stream,
vips_source_length( pdf->source ), NULL, NULL, &error )) ) {
vips_g_error( &error );
return( -1 );
}
if( VIPS_OBJECT_CLASS( vips_foreign_load_pdf_parent_class )->
build( object ) )
return( -1 );
return( 0 );
}
static VipsForeignFlags
vips_foreign_load_pdf_get_flags_filename( const char *filename )
{
/* We can render any part of the page on demand.
*/
return( VIPS_FOREIGN_PARTIAL );
}
static VipsForeignFlags
vips_foreign_load_pdf_get_flags( VipsForeignLoad *load )
{
return( VIPS_FOREIGN_PARTIAL );
}
static int
vips_foreign_load_pdf_get_page( VipsForeignLoadPdf *pdf, int page_no )
{
if( pdf->current_page != page_no ||
!pdf->page ) {
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( pdf );
VIPS_UNREF( pdf->page );
pdf->current_page = -1;
#ifdef DEBUG
printf( "vips_foreign_load_pdf_get_page: %d\n", page_no );
#endif /*DEBUG*/
if( !(pdf->page = poppler_document_get_page( pdf->doc, page_no )) ) {
vips_error( class->nickname,
_( "unable to load page %d" ), page_no );
return( -1 );
}
pdf->current_page = page_no;
}
return( 0 );
}
/* String-based metadata fields we extract.
*/
typedef struct _VipsForeignLoadPdfMetadata {
char *(*pdf_fetch)( PopplerDocument *doc );
char *field;
} VipsForeignLoadPdfMetadata;
static VipsForeignLoadPdfMetadata vips_foreign_load_pdf_metadata[] = {
{ poppler_document_get_title, "pdf-title" },
{ poppler_document_get_author, "pdf-author" },
{ poppler_document_get_subject, "pdf-subject" },
{ poppler_document_get_keywords, "pdf-keywords" },
{ poppler_document_get_creator, "pdf-creator" },
{ poppler_document_get_producer, "pdf-producer" },
{ poppler_document_get_metadata, "pdf-metadata" },
};
static int n_metadata = VIPS_NUMBER( vips_foreign_load_pdf_metadata );
static int
vips_foreign_load_pdf_set_image( VipsForeignLoadPdf *pdf, VipsImage *out )
{
int i;
double res;
#ifdef DEBUG
printf( "vips_foreign_load_pdf_set_image: %p\n", pdf );
#endif /*DEBUG*/
/* We render to a linecache, so fat strips work well.
*/
vips_image_pipelinev( out, VIPS_DEMAND_STYLE_FATSTRIP, NULL );
/* Extract and attach metadata. Set the old name too for compat.
*/
vips_image_set_int( out, "pdf-n_pages", pdf->n_pages );
vips_image_set_int( out, VIPS_META_N_PAGES, pdf->n_pages );
for( i = 0; i < n_metadata; i++ ) {
VipsForeignLoadPdfMetadata *metadata =
&vips_foreign_load_pdf_metadata[i];
char *str;
if( (str = metadata->pdf_fetch( pdf->doc )) ) {
vips_image_set_string( out, metadata->field, str );
g_free( str );
}
}
/* We need pixels/mm for vips.
*/
res = pdf->dpi / 25.4;
vips_image_init_fields( out,
pdf->image.width, pdf->image.height,
4, VIPS_FORMAT_UCHAR,
VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, res, res );
return( 0 );
}
static int
vips_foreign_load_pdf_header( VipsForeignLoad *load )
{
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load );
VipsForeignLoadPdf *pdf = VIPS_FOREIGN_LOAD_PDF( load );
int top;
int i;
#ifdef DEBUG
printf( "vips_foreign_load_pdf_header: %p\n", pdf );
#endif /*DEBUG*/
pdf->n_pages = poppler_document_get_n_pages( pdf->doc );
/* @n == -1 means until the end of the doc.
*/
if( pdf->n == -1 )
pdf->n = pdf->n_pages - pdf->page_no;
if( pdf->page_no + pdf->n > pdf->n_pages ||
pdf->page_no < 0 ||
pdf->n <= 0 ) {
vips_error( class->nickname, "%s", _( "pages out of range" ) );
return( -1 );
}
/* Lay out the pages in our output image.
*/
if( !(pdf->pages = VIPS_ARRAY( pdf, pdf->n, VipsRect )) )
return( -1 );
top = 0;
pdf->image.left = 0;
pdf->image.top = 0;
pdf->image.width = 0;
pdf->image.height = 0;
for( i = 0; i < pdf->n; i++ ) {
double width;
double height;
if( vips_foreign_load_pdf_get_page( pdf, pdf->page_no + i ) )
return( -1 );
poppler_page_get_size( pdf->page, &width, &height );
pdf->pages[i].left = 0;
pdf->pages[i].top = top;
/* We do round to nearest, in the same way that vips_resize()
* does round to nearest. Without this, things like
* shrink-on-load will break.
*/
pdf->pages[i].width = VIPS_RINT( width * pdf->total_scale );
pdf->pages[i].height = VIPS_RINT( height * pdf->total_scale );
if( pdf->pages[i].width > pdf->image.width )
pdf->image.width = pdf->pages[i].width;
pdf->image.height += pdf->pages[i].height;
top += pdf->pages[i].height;
}
/* If all pages are the same height, we can tag this as a toilet roll
* image.
*/
for( i = 1; i < pdf->n; i++ )
if( pdf->pages[i].height != pdf->pages[0].height )
break;
if( vips_object_argument_isset( VIPS_OBJECT( pdf ), "n" ) )
vips_image_set_int( load->out,
VIPS_META_PAGE_HEIGHT, pdf->pages[0].height );
vips_foreign_load_pdf_set_image( pdf, load->out );
/* Convert the background to the image format.
*
* FIXME ... we probably should convert this to pre-multiplied BGRA
* to match the Cairo convention. See vips__premultiplied_bgra2rgba().
*/
if( !(pdf->ink = vips__vector_to_ink( class->nickname,
load->out,
VIPS_AREA( pdf->background )->data, NULL,
VIPS_AREA( pdf->background )->n )) )
return( -1 );
vips_source_minimise( pdf->source );
return( 0 );
}
static void
vips_foreign_load_pdf_minimise( VipsImage *image, VipsForeignLoadPdf *pdf )
{
vips_source_minimise( pdf->source );
}
static int
vips_foreign_load_pdf_generate( VipsRegion *or,
void *seq, void *a, void *b, gboolean *stop )
{
VipsForeignLoadPdf *pdf = VIPS_FOREIGN_LOAD_PDF( a );
VipsRect *r = &or->valid;
int top;
int i;
int y;
/*
printf( "vips_foreign_load_pdf_generate: "
"left = %d, top = %d, width = %d, height = %d\n",
r->left, r->top, r->width, r->height );
*/
/* Poppler won't always paint the background.
*/
vips_region_paint_pel( or, r, pdf->ink );
/* Search through the pages we are drawing for the first containing
* this rect. This could be quicker, perhaps a binary search, but who
* cares.
*/
for( i = 0; i < pdf->n; i++ )
if( VIPS_RECT_BOTTOM( &pdf->pages[i] ) > r->top )
break;
top = r->top;
while( top < VIPS_RECT_BOTTOM( r ) ) {
VipsRect rect;
cairo_surface_t *surface;
cairo_t *cr;
vips_rect_intersectrect( r, &pdf->pages[i], &rect );
surface = cairo_image_surface_create_for_data(
VIPS_REGION_ADDR( or, rect.left, rect.top ),
CAIRO_FORMAT_ARGB32,
rect.width, rect.height,
VIPS_REGION_LSKIP( or ) );
cr = cairo_create( surface );
cairo_surface_destroy( surface );
cairo_scale( cr, pdf->total_scale, pdf->total_scale );
cairo_translate( cr,
(pdf->pages[i].left - rect.left) / pdf->total_scale,
(pdf->pages[i].top - rect.top) / pdf->total_scale );
/* poppler is single-threaded, but we don't need to lock since
* we're running inside a non-threaded tilecache.
*/
if( vips_foreign_load_pdf_get_page( pdf, pdf->page_no + i ) )
return( -1 );
poppler_page_render( pdf->page, cr );
cairo_destroy( cr );
top += rect.height;
i += 1;
}
/* Cairo makes pre-multipled BRGA, we must byteswap and unpremultiply.
*/
for( y = 0; y < r->height; y++ )
vips__premultiplied_bgra2rgba(
(guint32 *) VIPS_REGION_ADDR( or, r->left, r->top + y ),
r->width );
return( 0 );
}
static int
vips_foreign_load_pdf_load( VipsForeignLoad *load )
{
VipsForeignLoadPdf *pdf = VIPS_FOREIGN_LOAD_PDF( load );
VipsImage **t = (VipsImage **)
vips_object_local_array( (VipsObject *) load, 2 );
#ifdef DEBUG
printf( "vips_foreign_load_pdf_load: %p\n", pdf );
#endif /*DEBUG*/
/* Read to this image, then cache to out, see below.
*/
t[0] = vips_image_new();
/* Close input immediately at end of read.
*/
g_signal_connect( t[0], "minimise",
G_CALLBACK( vips_foreign_load_pdf_minimise ), pdf );
/* Very large strips to limit render calls per page.
*/
vips_foreign_load_pdf_set_image( pdf, t[0] );
if( vips_image_generate( t[0],
NULL, vips_foreign_load_pdf_generate, NULL, pdf, NULL ) ||
vips_sequential( t[0], &t[1],
"tile_height", VIPS_MIN( 5000, pdf->pages[0].height ),
NULL ) ||
vips_image_write( t[1], load->real ) )
return( -1 );
return( 0 );
}
static void
vips_foreign_load_pdf_class_init( VipsForeignLoadPdfClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *object_class = (VipsObjectClass *) class;
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
gobject_class->dispose = vips_foreign_load_pdf_dispose;
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "pdfload_base";
object_class->description = _( "load PDF with libpoppler" );
object_class->build = vips_foreign_load_pdf_build;
load_class->get_flags_filename =
vips_foreign_load_pdf_get_flags_filename;
load_class->get_flags = vips_foreign_load_pdf_get_flags;
load_class->header = vips_foreign_load_pdf_header;
load_class->load = vips_foreign_load_pdf_load;
VIPS_ARG_INT( class, "page", 20,
_( "Page" ),
_( "Load this page from the file" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadPdf, page_no ),
0, 100000, 0 );
VIPS_ARG_INT( class, "n", 21,
_( "n" ),
_( "Load this many pages" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadPdf, n ),
-1, 100000, 1 );
VIPS_ARG_DOUBLE( class, "dpi", 22,
_( "DPI" ),
_( "Render at this DPI" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadPdf, dpi ),
0.001, 100000.0, 72.0 );
VIPS_ARG_DOUBLE( class, "scale", 23,
_( "Scale" ),
_( "Scale output by this factor" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadPdf, scale ),
0.001, 100000.0, 1.0 );
VIPS_ARG_BOXED( class, "background", 24,
_( "Background" ),
_( "Background value" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadPdf, background ),
VIPS_TYPE_ARRAY_DOUBLE );
}
static void
vips_foreign_load_pdf_init( VipsForeignLoadPdf *pdf )
{
pdf->dpi = 72.0;
pdf->scale = 1.0;
pdf->n = 1;
pdf->current_page = -1;
pdf->background = vips_array_double_newv( 1, 255.0 );
}
typedef struct _VipsForeignLoadPdfFile {
VipsForeignLoadPdf parent_object;
/* Filename for load.
*/
char *filename;
char *uri;
} VipsForeignLoadPdfFile;
typedef VipsForeignLoadPdfClass VipsForeignLoadPdfFileClass;
G_DEFINE_TYPE( VipsForeignLoadPdfFile, vips_foreign_load_pdf_file,
vips_foreign_load_pdf_get_type() );
static void
vips_foreign_load_pdf_file_dispose( GObject *gobject )
{
VipsForeignLoadPdfFile *file =
(VipsForeignLoadPdfFile *) gobject;
VIPS_FREE( file->uri );
G_OBJECT_CLASS( vips_foreign_load_pdf_file_parent_class )->
dispose( gobject );
}
static int
vips_foreign_load_pdf_file_header( VipsForeignLoad *load )
{
VipsForeignLoadPdfFile *file = (VipsForeignLoadPdfFile *) load;
VIPS_SETSTR( load->out->filename, file->filename );
return( VIPS_FOREIGN_LOAD_CLASS(
vips_foreign_load_pdf_file_parent_class )->header( load ) );
}
static const char *vips_foreign_pdf_suffs[] = {
".pdf",
NULL
};
static int
vips_foreign_load_pdf_file_build( VipsObject *object )
{
VipsForeignLoadPdf *pdf = VIPS_FOREIGN_LOAD_PDF( object );
VipsForeignLoadPdfFile *file = (VipsForeignLoadPdfFile *) pdf;
#ifdef DEBUG
printf( "vips_foreign_load_pdf_file_build: %s\n", file->filename );
#endif /*DEBUG*/
if( file->filename ) {
char *path;
GError *error = NULL;
/* We need an absolute path for a URI.
*/
path = vips_realpath( file->filename );
if( !(file->uri = g_filename_to_uri( path, NULL, &error )) ) {
g_free( path );
vips_g_error( &error );
return( -1 );
}
g_free( path );
if( !(pdf->source =
vips_source_new_from_file( file->filename )) )
return( -1 );
}
return( VIPS_OBJECT_CLASS( vips_foreign_load_pdf_file_parent_class )->
build( object ) );
}
static void
vips_foreign_load_pdf_file_class_init(
VipsForeignLoadPdfFileClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *object_class = (VipsObjectClass *) class;
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
gobject_class->dispose = vips_foreign_load_pdf_file_dispose;
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "pdfload";
object_class->description = _( "load PDF from file" );
object_class->build = vips_foreign_load_pdf_file_build;
foreign_class->suffs = vips_foreign_pdf_suffs;
load_class->is_a = vips_foreign_load_pdf_is_a;
load_class->header = vips_foreign_load_pdf_file_header;
VIPS_ARG_STRING( class, "filename", 1,
_( "Filename" ),
_( "Filename to load from" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadPdfFile, filename ),
NULL );
}
static void
vips_foreign_load_pdf_file_init( VipsForeignLoadPdfFile *file )
{
}
typedef struct _VipsForeignLoadPdfBuffer {
VipsForeignLoadPdf parent_object;
/* Load from a buffer.
*/
VipsArea *buf;
} VipsForeignLoadPdfBuffer;
typedef VipsForeignLoadPdfClass VipsForeignLoadPdfBufferClass;
G_DEFINE_TYPE( VipsForeignLoadPdfBuffer, vips_foreign_load_pdf_buffer,
vips_foreign_load_pdf_get_type() );
static int
vips_foreign_load_pdf_buffer_build( VipsObject *object )
{
VipsForeignLoadPdf *pdf = VIPS_FOREIGN_LOAD_PDF( object );
VipsForeignLoadPdfBuffer *buffer = (VipsForeignLoadPdfBuffer *) pdf;
if( buffer->buf &&
!(pdf->source = vips_source_new_from_memory(
VIPS_AREA( buffer->buf )->data,
VIPS_AREA( buffer->buf )->length )) )
return( -1 );
return( VIPS_OBJECT_CLASS( vips_foreign_load_pdf_buffer_parent_class )->
build( object ) );
}
static void
vips_foreign_load_pdf_buffer_class_init(
VipsForeignLoadPdfBufferClass *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 = "pdfload_buffer";
object_class->description = _( "load PDF from buffer" );
object_class->build = vips_foreign_load_pdf_buffer_build;
load_class->is_a_buffer = vips_foreign_load_pdf_is_a_buffer;
VIPS_ARG_BOXED( class, "buffer", 1,
_( "Buffer" ),
_( "Buffer to load from" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadPdfBuffer, buf ),
VIPS_TYPE_BLOB );
}
static void
vips_foreign_load_pdf_buffer_init( VipsForeignLoadPdfBuffer *buffer )
{
}
typedef struct _VipsForeignLoadPdfSource {
VipsForeignLoadPdf parent_object;
VipsSource *source;
} VipsForeignLoadPdfSource;
typedef VipsForeignLoadPdfClass VipsForeignLoadPdfSourceClass;
G_DEFINE_TYPE( VipsForeignLoadPdfSource, vips_foreign_load_pdf_source,
vips_foreign_load_pdf_get_type() );
static int
vips_foreign_load_pdf_source_build( VipsObject *object )
{
VipsForeignLoadPdf *pdf = VIPS_FOREIGN_LOAD_PDF( object );
VipsForeignLoadPdfSource *source = (VipsForeignLoadPdfSource *) pdf;
if( source->source ) {
pdf->source = source->source;
g_object_ref( pdf->source );
}
return( VIPS_OBJECT_CLASS( vips_foreign_load_pdf_source_parent_class )->
build( object ) );
}
static gboolean
vips_foreign_load_pdf_source_is_a_source( VipsSource *source )
{
const unsigned char *p;
return( (p = vips_source_sniff( source, 4 )) &&
p[0] == '%' &&
p[1] == 'P' &&
p[2] == 'D' &&
p[3] == 'F' );
}
static void
vips_foreign_load_pdf_source_class_init(
VipsForeignLoadPdfSourceClass *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 = "pdfload_source";
object_class->description = _( "load PDF from source" );
object_class->build = vips_foreign_load_pdf_source_build;
load_class->is_a_source = vips_foreign_load_pdf_source_is_a_source;
VIPS_ARG_OBJECT( class, "source", 1,
_( "Source" ),
_( "Source to load from" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadPdfSource, source ),
VIPS_TYPE_SOURCE );
}
static void
vips_foreign_load_pdf_source_init( VipsForeignLoadPdfSource *source )
{
}
#endif /*defined(HAVE_POPPLER)*/

714
libvips/foreign/vips2heif.c Normal file
View File

@ -0,0 +1,714 @@
/* save to heif
*
* 5/7/18
* - from niftisave.c
* 3/7/19 [lovell]
* - add "compression" option
* 1/9/19 [meyermarcel]
* - save alpha when necessary
* 15/3/20
* - revise for new VipsTarget API
* 14/2/21 kleisauke
* - include GObject part from heifsave.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_VERBOSE
#define DEBUG
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#ifdef HAVE_HEIF_ENCODER
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vips/vips.h>
#include <vips/internal.h>
#include "pforeign.h"
#include <libheif/heif.h>
typedef struct _VipsForeignSaveHeif {
VipsForeignSave parent_object;
/* Where to write (set by subclasses).
*/
VipsTarget *target;
/* Coding quality factor (1-100).
*/
int Q;
/* Lossless compression.
*/
gboolean lossless;
/* Compression format
*/
VipsForeignHeifCompression compression;
/* CPU effort (0-8).
*/
int speed;
/* Chroma subsampling.
*/
VipsForeignSubsample subsample_mode;
/* The image we save. This is a copy of save->ready since we need to
* be able to update the metadata.
*/
VipsImage *image;
int page_width;
int page_height;
int n_pages;
struct heif_context *ctx;
struct heif_encoder *encoder;
/* The current page we are writing.
*/
struct heif_image_handle *handle;
/* The current page in memory which we build as we scan down the
* image.
*/
struct heif_image *img;
/* The libheif memory area we fill with pixels from the libvips
* pipe.
*/
uint8_t *data;
int stride;
} VipsForeignSaveHeif;
typedef VipsForeignSaveClass VipsForeignSaveHeifClass;
G_DEFINE_ABSTRACT_TYPE( VipsForeignSaveHeif, vips_foreign_save_heif,
VIPS_TYPE_FOREIGN_SAVE );
static void
vips_foreign_save_heif_dispose( GObject *gobject )
{
VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) gobject;
VIPS_UNREF( heif->target );
VIPS_UNREF( heif->image );
VIPS_FREEF( heif_image_release, heif->img );
VIPS_FREEF( heif_image_handle_release, heif->handle );
VIPS_FREEF( heif_encoder_release, heif->encoder );
VIPS_FREEF( heif_context_free, heif->ctx );
G_OBJECT_CLASS( vips_foreign_save_heif_parent_class )->
dispose( gobject );
}
typedef struct heif_error (*libheif_metadata_fn)( struct heif_context *,
const struct heif_image_handle *,
const void *, int );
struct _VipsForeignSaveHeifMetadata {
const char *name;
libheif_metadata_fn saver;
} libheif_metadata[] = {
{ VIPS_META_EXIF_NAME, heif_context_add_exif_metadata },
{ VIPS_META_XMP_NAME, heif_context_add_XMP_metadata }
};
static int
vips_foreign_save_heif_write_metadata( VipsForeignSaveHeif *heif )
{
int i;
struct heif_error error;
/* Rebuild exif from tags, if we'll be saving it.
*/
if( vips_image_get_typeof( heif->image, VIPS_META_EXIF_NAME ) )
if( vips__exif_update( heif->image ) )
return( -1 );
for( i = 0; i < VIPS_NUMBER( libheif_metadata ); i++ )
if( vips_image_get_typeof( heif->image,
libheif_metadata[i].name ) ) {
const void *data;
size_t length;
#ifdef DEBUG
printf( "attaching %s ..\n",
libheif_metadata[i].name );
#endif /*DEBUG*/
if( vips_image_get_blob( heif->image,
libheif_metadata[i].name, &data, &length ) )
return( -1 );
error = libheif_metadata[i].saver( heif->ctx,
heif->handle, data, length );
if( error.code ) {
vips__heif_error( &error );
return( -1 );
}
}
return( 0 );
}
static int
vips_foreign_save_heif_write_page( VipsForeignSaveHeif *heif, int page )
{
VipsForeignSave *save = (VipsForeignSave *) heif;
struct heif_error error;
struct heif_encoding_options *options;
#ifdef HAVE_HEIF_COLOR_PROFILE
if( !save->strip &&
vips_image_get_typeof( heif->image, VIPS_META_ICC_NAME ) ) {
const void *data;
size_t length;
#ifdef DEBUG
printf( "attaching profile ..\n" );
#endif /*DEBUG*/
if( vips_image_get_blob( heif->image,
VIPS_META_ICC_NAME, &data, &length ) )
return( -1 );
/* FIXME .. also see heif_image_set_nclx_color_profile()
*/
error = heif_image_set_raw_color_profile( heif->img,
"rICC", data, length );
if( error.code ) {
vips__heif_error( &error );
return( -1 );
}
}
#endif /*HAVE_HEIF_COLOR_PROFILE*/
options = heif_encoding_options_alloc();
if( vips_image_hasalpha( heif->image ) )
options->save_alpha_channel = 1;
#ifdef DEBUG
printf( "encoding ..\n" );
#endif /*DEBUG*/
error = heif_context_encode_image( heif->ctx,
heif->img, heif->encoder, options, &heif->handle );
heif_encoding_options_free( options );
if( error.code ) {
vips__heif_error( &error );
return( -1 );
}
if( vips_image_get_typeof( heif->image, "heif-primary" ) ) {
int primary;
if( vips_image_get_int( heif->image,
"heif-primary", &primary ) )
return( -1 );
if( page == primary ) {
error = heif_context_set_primary_image( heif->ctx,
heif->handle );
if( error.code ) {
vips__heif_error( &error );
return( -1 );
}
}
}
if( !save->strip &&
vips_foreign_save_heif_write_metadata( heif ) )
return( -1 );
VIPS_FREEF( heif_image_handle_release, heif->handle );
return( 0 );
}
static int
vips_foreign_save_heif_write_block( VipsRegion *region, VipsRect *area,
void *a )
{
VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) a;
int y;
#ifdef DEBUG
printf( "vips_foreign_save_heif_write_block: y = %d\n", area->top );
#endif /*DEBUG*/
/* Copy a line at a time into our output image, write each time the
* image fills.
*/
for( y = 0; y < area->height; y++ ) {
/* Y in page.
*/
int page = (area->top + y) / heif->page_height;
int line = (area->top + y) % heif->page_height;
VipsPel *p = VIPS_REGION_ADDR( region, 0, area->top + y );
VipsPel *q = heif->data + line * heif->stride;
memcpy( q, p, VIPS_IMAGE_SIZEOF_LINE( region->im ) );
/* Did we just write the final line? Write as a new page
* into the output.
*/
if( line == heif->page_height - 1 )
if( vips_foreign_save_heif_write_page( heif, page ) )
return( -1 );
}
return( 0 );
}
struct heif_error
vips_foreign_save_heif_write( struct heif_context *ctx,
const void *data, size_t length, void *userdata )
{
VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) userdata;
struct heif_error error;
error.code = 0;
if( vips_target_write( heif->target, data, length ) )
error.code = -1;
return( error );
}
static int
vips_foreign_save_heif_build( VipsObject *object )
{
VipsForeignSave *save = (VipsForeignSave *) object;
VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) object;
const char *filename;
struct heif_error error;
struct heif_writer writer;
char *chroma;
if( VIPS_OBJECT_CLASS( vips_foreign_save_heif_parent_class )->
build( object ) )
return( -1 );
/* Make a copy of the image in case we modify the metadata eg. for
* exif_update.
*/
if( vips_copy( save->ready, &heif->image, NULL ) )
return( -1 );
/* Compression defaults to VIPS_FOREIGN_HEIF_COMPRESSION_AV1 for .avif
* suffix.
*/
filename = vips_connection_filename( VIPS_CONNECTION( heif->target ) );
if( !vips_object_argument_isset( object, "compression" ) &&
filename &&
vips_iscasepostfix( filename, ".avif" ) )
heif->compression = VIPS_FOREIGN_HEIF_COMPRESSION_AV1;
error = heif_context_get_encoder_for_format( heif->ctx,
(enum heif_compression_format) heif->compression,
&heif->encoder );
if( error.code ) {
if( error.code == heif_error_Unsupported_filetype )
vips_error( "heifsave",
"%s", _( "Unsupported compression" ) );
else
vips__heif_error( &error );
return( -1 );
}
error = heif_encoder_set_lossy_quality( heif->encoder, heif->Q );
if( error.code ) {
vips__heif_error( &error );
return( -1 );
}
error = heif_encoder_set_lossless( heif->encoder, heif->lossless );
if( error.code ) {
vips__heif_error( &error );
return( -1 );
}
error = heif_encoder_set_parameter_integer( heif->encoder,
"speed", heif->speed );
if( error.code &&
error.subcode != heif_suberror_Unsupported_parameter ) {
vips__heif_error( &error );
return( -1 );
}
chroma = heif->subsample_mode == VIPS_FOREIGN_SUBSAMPLE_OFF ||
( heif->subsample_mode == VIPS_FOREIGN_SUBSAMPLE_AUTO &&
heif->Q >= 90 ) ? "444" : "420";
error = heif_encoder_set_parameter_string( heif->encoder,
"chroma", chroma );
if( error.code &&
error.subcode != heif_suberror_Unsupported_parameter ) {
vips__heif_error( &error );
return( -1 );
}
/* TODO .. support extra per-encoder params with
* heif_encoder_list_parameters().
*/
heif->page_width = heif->image->Xsize;
heif->page_height = vips_image_get_page_height( heif->image );
heif->n_pages = heif->image->Ysize / heif->page_height;
/* Make a heif image the size of a page. We send sink_disc() output
* here and write a frame each time it fills.
*/
#ifdef DEBUG
printf( "vips_foreign_save_heif_build:\n" );
printf( "\twidth = %d\n", heif->page_width );
printf( "\theight = %d\n", heif->page_height );
printf( "\talpha = %d\n", vips_image_hasalpha( heif->image ) );
#endif /*DEBUG*/
error = heif_image_create( heif->page_width, heif->page_height,
heif_colorspace_RGB,
vips_image_hasalpha( heif->image ) ?
heif_chroma_interleaved_RGBA :
heif_chroma_interleaved_RGB,
&heif->img );
if( error.code ) {
vips__heif_error( &error );
return( -1 );
}
error = heif_image_add_plane( heif->img, heif_channel_interleaved,
heif->page_width, heif->page_height,
vips_image_hasalpha( heif->image ) ? 32 : 24 );
if( error.code ) {
vips__heif_error( &error );
return( -1 );
}
#ifdef DEBUG
vips__heif_image_print( heif->img );
#endif /*DEBUG*/
heif->data = heif_image_get_plane( heif->img,
heif_channel_interleaved, &heif->stride );
/* Write data.
*/
if( vips_sink_disc( heif->image,
vips_foreign_save_heif_write_block, heif ) )
return( -1 );
/* This has to come right at the end :-( so there's no support for
* incremental writes.
*/
writer.writer_api_version = 1;
writer.write = vips_foreign_save_heif_write;
error = heif_context_write( heif->ctx, &writer, heif );
if( error.code ) {
vips__heif_error( &error );
return( -1 );
}
vips_target_finish( heif->target );
return( 0 );
}
/* Save a bit of typing.
*/
#define UC VIPS_FORMAT_UCHAR
static int vips_heif_bandfmt[10] = {
/* UC C US S UI I F X D DX */
UC, UC, UC, UC, UC, UC, UC, UC, UC, UC
};
static void
vips_foreign_save_heif_class_init( VipsForeignSaveHeifClass *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_heif_dispose;
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "heifsave_base";
object_class->description = _( "save image in HEIF format" );
object_class->build = vips_foreign_save_heif_build;
foreign_class->suffs = vips__heif_suffs;
save_class->saveable = VIPS_SAVEABLE_RGBA_ONLY;
save_class->format_table = vips_heif_bandfmt;
VIPS_ARG_INT( class, "Q", 10,
_( "Q" ),
_( "Q factor" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignSaveHeif, Q ),
1, 100, 50 );
VIPS_ARG_BOOL( class, "lossless", 13,
_( "Lossless" ),
_( "Enable lossless compression" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignSaveHeif, lossless ),
FALSE );
VIPS_ARG_ENUM( class, "compression", 14,
_( "compression" ),
_( "Compression format" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignSaveHeif, compression ),
VIPS_TYPE_FOREIGN_HEIF_COMPRESSION,
VIPS_FOREIGN_HEIF_COMPRESSION_HEVC );
VIPS_ARG_INT( class, "speed", 15,
_( "speed" ),
_( "CPU effort" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignSaveHeif, speed ),
0, 8, 5 );
VIPS_ARG_ENUM( class, "subsample_mode", 16,
_( "Subsample mode" ),
_( "Select chroma subsample operation mode" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignSaveHeif, subsample_mode ),
VIPS_TYPE_FOREIGN_SUBSAMPLE,
VIPS_FOREIGN_SUBSAMPLE_AUTO );
}
static void
vips_foreign_save_heif_init( VipsForeignSaveHeif *heif )
{
heif->ctx = heif_context_alloc();
heif->Q = 50;
heif->compression = VIPS_FOREIGN_HEIF_COMPRESSION_HEVC;
heif->speed = 5;
heif->subsample_mode = VIPS_FOREIGN_SUBSAMPLE_AUTO;
}
typedef struct _VipsForeignSaveHeifFile {
VipsForeignSaveHeif parent_object;
/* Filename for save.
*/
char *filename;
} VipsForeignSaveHeifFile;
typedef VipsForeignSaveHeifClass VipsForeignSaveHeifFileClass;
G_DEFINE_TYPE( VipsForeignSaveHeifFile, vips_foreign_save_heif_file,
vips_foreign_save_heif_get_type() );
static int
vips_foreign_save_heif_file_build( VipsObject *object )
{
VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) object;
VipsForeignSaveHeifFile *file = (VipsForeignSaveHeifFile *) object;
if( !(heif->target = vips_target_new_to_file( file->filename )) )
return( -1 );
if( VIPS_OBJECT_CLASS( vips_foreign_save_heif_file_parent_class )->
build( object ) )
return( -1 );
return( 0 );
}
static void
vips_foreign_save_heif_file_class_init( VipsForeignSaveHeifFileClass *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 = "heifsave";
object_class->build = vips_foreign_save_heif_file_build;
VIPS_ARG_STRING( class, "filename", 1,
_( "Filename" ),
_( "Filename to save to" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsForeignSaveHeifFile, filename ),
NULL );
}
static void
vips_foreign_save_heif_file_init( VipsForeignSaveHeifFile *file )
{
}
typedef struct _VipsForeignSaveHeifBuffer {
VipsForeignSaveHeif parent_object;
/* Save to a buffer.
*/
VipsArea *buf;
} VipsForeignSaveHeifBuffer;
typedef VipsForeignSaveHeifClass VipsForeignSaveHeifBufferClass;
G_DEFINE_TYPE( VipsForeignSaveHeifBuffer, vips_foreign_save_heif_buffer,
vips_foreign_save_heif_get_type() );
static int
vips_foreign_save_heif_buffer_build( VipsObject *object )
{
VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) object;
VipsForeignSaveHeifBuffer *buffer =
(VipsForeignSaveHeifBuffer *) object;
VipsBlob *blob;
if( !(heif->target = vips_target_new_to_memory()) )
return( -1 );
if( VIPS_OBJECT_CLASS( vips_foreign_save_heif_buffer_parent_class )->
build( object ) )
return( -1 );
g_object_get( heif->target, "blob", &blob, NULL );
g_object_set( buffer, "buffer", blob, NULL );
vips_area_unref( VIPS_AREA( blob ) );
return( 0 );
}
static void
vips_foreign_save_heif_buffer_class_init(
VipsForeignSaveHeifBufferClass *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 = "heifsave_buffer";
object_class->build = vips_foreign_save_heif_buffer_build;
VIPS_ARG_BOXED( class, "buffer", 1,
_( "Buffer" ),
_( "Buffer to save to" ),
VIPS_ARGUMENT_REQUIRED_OUTPUT,
G_STRUCT_OFFSET( VipsForeignSaveHeifBuffer, buf ),
VIPS_TYPE_BLOB );
}
static void
vips_foreign_save_heif_buffer_init( VipsForeignSaveHeifBuffer *buffer )
{
}
typedef struct _VipsForeignSaveHeifTarget {
VipsForeignSaveHeif parent_object;
VipsTarget *target;
} VipsForeignSaveHeifTarget;
typedef VipsForeignSaveHeifClass VipsForeignSaveHeifTargetClass;
G_DEFINE_TYPE( VipsForeignSaveHeifTarget, vips_foreign_save_heif_target,
vips_foreign_save_heif_get_type() );
static int
vips_foreign_save_heif_target_build( VipsObject *object )
{
VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) object;
VipsForeignSaveHeifTarget *target =
(VipsForeignSaveHeifTarget *) object;
if( target->target ) {
heif->target = target->target;
g_object_ref( heif->target );
}
if( VIPS_OBJECT_CLASS( vips_foreign_save_heif_target_parent_class )->
build( object ) )
return( -1 );
return( 0 );
}
static void
vips_foreign_save_heif_target_class_init(
VipsForeignSaveHeifTargetClass *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 = "heifsave_target";
object_class->build = vips_foreign_save_heif_target_build;
VIPS_ARG_OBJECT( class, "target", 1,
_( "Target" ),
_( "Target to save to" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsForeignSaveHeifTarget, target ),
VIPS_TYPE_TARGET );
}
static void
vips_foreign_save_heif_target_init( VipsForeignSaveHeifTarget *target )
{
}
#endif /*HAVE_HEIF_ENCODER*/

View 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*/

View File

@ -227,8 +227,8 @@ vips_get_argv0( void )
* Returns: 0 on success, -1 otherwise
*/
/* Load all plugins in a directory ... look for '.plg' suffix. Error if we had
* any probs.
/* Load all plugins in a directory ... look for '.<G_MODULE_SUFFIX>' or
* '.plg' (deprecated) suffix. Error if we had any probs.
*/
static int
vips_load_plugins( const char *fmt, ... )
@ -259,7 +259,11 @@ vips_load_plugins( const char *fmt, ... )
result = 0;
while( (name = g_dir_read_name( dir )) )
if( vips_ispostfix( name, ".plg" ) ) {
if( vips_ispostfix( name, "." G_MODULE_SUFFIX )
#if ENABLE_DEPRECATED
|| vips_ispostfix( name, ".plg" )
#endif
) {
char path[VIPS_PATH_MAX];
GModule *module;
@ -510,13 +514,18 @@ vips_init( const char *argv0 )
vips_mosaicing_operation_init();
vips_g_input_stream_get_type();
/* Load any vips8 plugins from the vips libdir. Keep going, even if
* some plugins fail to load.
/* Load any vips8 modules from the vips libdir. Keep going, even if
* some modules fail to load.
*/
(void) vips_load_plugins( "%s/vips-modules-%d.%d",
libdir, VIPS_MAJOR_VERSION, VIPS_MINOR_VERSION );
#if ENABLE_DEPRECATED
/* Load any vips8 plugins from the vips libdir.
*/
(void) vips_load_plugins( "%s/vips-plugins-%d.%d",
libdir, VIPS_MAJOR_VERSION, VIPS_MINOR_VERSION );
#if ENABLE_DEPRECATED
/* Load up any vips7 plugins in the vips libdir. We don't error on
* failure, it's too annoying to have VIPS refuse to start because of
* a broken plugin.

87
libvips/module/heif.c Normal file
View File

@ -0,0 +1,87 @@
/* libheif as a dynamically loadable module
*
* 14/2/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_HEIF_DECODER) || defined(HAVE_HEIF_ENCODER)) && defined(HEIF_MODULE)
/* This is called on module load.
*/
G_MODULE_EXPORT const gchar *
g_module_check_init( GModule *module )
{
#ifdef DEBUG
printf( "vips_heif: module init\n" );
#endif /*DEBUG*/
extern GType vips_foreign_load_heif_file_get_type( void );
extern GType vips_foreign_load_heif_buffer_get_type( void );
extern GType vips_foreign_load_heif_source_get_type( void );
extern GType vips_foreign_save_heif_file_get_type( void );
extern GType vips_foreign_save_heif_buffer_get_type( void );
extern GType vips_foreign_save_heif_target_get_type( void );
#ifdef HAVE_HEIF_DECODER
vips_foreign_load_heif_file_get_type();
vips_foreign_load_heif_buffer_get_type();
vips_foreign_load_heif_source_get_type();
#endif /*HAVE_HEIF_DECODER*/
#ifdef HAVE_HEIF_ENCODER
vips_foreign_save_heif_file_get_type();
vips_foreign_save_heif_buffer_get_type();
vips_foreign_save_heif_target_get_type();
#endif /*HAVE_HEIF_ENCODER*/
/* We can't be unloaded, there would be chaos.
*/
g_module_make_resident( module );
return( NULL );
}
#endif /*(defined(HAVE_HEIF_DECODER) || defined(HAVE_HEIF_ENCODER)) && defined(HEIF_MODULE)*/

92
libvips/module/magick.c Normal file
View 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)*/

View File

@ -0,0 +1,74 @@
/* openslide as a dynamically loadable module
*
* 11/2/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_OPENSLIDE) && defined(OPENSLIDE_MODULE)
/* This is called on module load.
*/
G_MODULE_EXPORT const gchar *
g_module_check_init( GModule *module )
{
#ifdef DEBUG
printf( "vips_openslide: module init\n" );
#endif /*DEBUG*/
extern GType vips_foreign_load_openslide_file_get_type( void );
extern GType vips_foreign_load_openslide_source_get_type( void );
vips_foreign_load_openslide_file_get_type();
vips_foreign_load_openslide_source_get_type();
/* We can't be unloaded, there would be chaos.
*/
g_module_make_resident( module );
return( NULL );
}
#endif /*defined(HAVE_OPENSLIDE) && defined(OPENSLIDE_MODULE)*/

76
libvips/module/poppler.c Normal file
View File

@ -0,0 +1,76 @@
/* poppler 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_POPPLER) && defined(POPPLER_MODULE)
/* This is called on module load.
*/
G_MODULE_EXPORT const gchar *
g_module_check_init( GModule *module )
{
#ifdef DEBUG
printf( "vips_poppler: module init\n" );
#endif /*DEBUG*/
extern GType vips_foreign_load_pdf_file_get_type( void );
extern GType vips_foreign_load_pdf_buffer_get_type( void );
extern GType vips_foreign_load_pdf_source_get_type( void );
vips_foreign_load_pdf_file_get_type();
vips_foreign_load_pdf_buffer_get_type();
vips_foreign_load_pdf_source_get_type();
/* We can't be unloaded, there would be chaos.
*/
g_module_make_resident( module );
return( NULL );
}
#endif /*defined(HAVE_POPPLER) && defined(POPPLER_MODULE)*/