Merge pull request #2167 from kleisauke/gmodulized
Support for building loaders/savers as dynamic loadable module
This commit is contained in:
commit
127fab316c
139
configure.ac
139
configure.ac
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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@
|
||||
|
@ -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
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
@ -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
|
||||
|
324
libvips/foreign/magick6load.c
Normal file
324
libvips/foreign/magick6load.c
Normal file
@ -0,0 +1,324 @@
|
||||
/* load with libMagick
|
||||
*
|
||||
* 5/12/11
|
||||
* - from openslideload.c
|
||||
* 17/1/12
|
||||
* - remove header-only loads
|
||||
* 11/6/13
|
||||
* - add @all_frames option, off by default
|
||||
* 14/2/16
|
||||
* - add @page option, 0 by default
|
||||
* 25/11/16
|
||||
* - add @n, deprecate @all_frames (just sets n = -1)
|
||||
* 8/9/17
|
||||
* - don't cache magickload
|
||||
* 21/4/21 kleisauke
|
||||
* - include GObject part from magickload.c
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
#define DEBUG
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
#include <vips/buf.h>
|
||||
#include <vips/internal.h>
|
||||
|
||||
#ifdef ENABLE_MAGICKLOAD
|
||||
|
||||
#ifdef HAVE_MAGICK6
|
||||
|
||||
#include "pforeign.h"
|
||||
#include "magick.h"
|
||||
|
||||
typedef struct _VipsForeignLoadMagick {
|
||||
VipsForeignLoad parent_object;
|
||||
|
||||
/* Deprecated. Just sets n = -1.
|
||||
*/
|
||||
gboolean all_frames;
|
||||
|
||||
char *density; /* Load at this resolution */
|
||||
int page; /* Load this page (frame) */
|
||||
int n; /* Load this many pages */
|
||||
|
||||
} VipsForeignLoadMagick;
|
||||
|
||||
typedef VipsForeignLoadClass VipsForeignLoadMagickClass;
|
||||
|
||||
G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadMagick, vips_foreign_load_magick,
|
||||
VIPS_TYPE_FOREIGN_LOAD );
|
||||
|
||||
static VipsForeignFlags
|
||||
vips_foreign_load_magick_get_flags_filename( const char *filename )
|
||||
{
|
||||
return( VIPS_FOREIGN_PARTIAL );
|
||||
}
|
||||
|
||||
static VipsForeignFlags
|
||||
vips_foreign_load_magick_get_flags( VipsForeignLoad *load )
|
||||
{
|
||||
return( VIPS_FOREIGN_PARTIAL );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_magick_class_init( VipsForeignLoadMagickClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class );
|
||||
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
|
||||
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "magickload_base";
|
||||
object_class->description = _( "load with ImageMagick" );
|
||||
|
||||
/* Don't cache magickload: it can gobble up memory and disc.
|
||||
*/
|
||||
operation_class->flags = VIPS_OPERATION_NOCACHE;
|
||||
|
||||
/* We need to be well to the back of the queue since vips's
|
||||
* dedicated loaders are usually preferable.
|
||||
*/
|
||||
foreign_class->priority = -100;
|
||||
|
||||
load_class->get_flags_filename =
|
||||
vips_foreign_load_magick_get_flags_filename;
|
||||
load_class->get_flags = vips_foreign_load_magick_get_flags;
|
||||
|
||||
VIPS_ARG_BOOL( class, "all_frames", 20,
|
||||
_( "all_frames" ),
|
||||
_( "Read all frames from an image" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT | VIPS_ARGUMENT_DEPRECATED,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadMagick, all_frames ),
|
||||
FALSE );
|
||||
|
||||
VIPS_ARG_STRING( class, "density", 21,
|
||||
_( "Density" ),
|
||||
_( "Canvas resolution for rendering vector formats like SVG" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadMagick, density ),
|
||||
NULL );
|
||||
|
||||
VIPS_ARG_INT( class, "page", 22,
|
||||
_( "Page" ),
|
||||
_( "Load this page from the file" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadMagick, page ),
|
||||
0, 100000, 0 );
|
||||
|
||||
VIPS_ARG_INT( class, "n", 23,
|
||||
_( "n" ),
|
||||
_( "Load this many pages" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadMagick, n ),
|
||||
-1, 100000, 1 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_magick_init( VipsForeignLoadMagick *magick )
|
||||
{
|
||||
magick->n = 1;
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignLoadMagickFile {
|
||||
VipsForeignLoadMagick parent_object;
|
||||
|
||||
char *filename;
|
||||
|
||||
} VipsForeignLoadMagickFile;
|
||||
|
||||
typedef VipsForeignLoadMagickClass VipsForeignLoadMagickFileClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsForeignLoadMagickFile, vips_foreign_load_magick_file,
|
||||
vips_foreign_load_magick_get_type() );
|
||||
|
||||
static gboolean
|
||||
ismagick( const char *filename )
|
||||
{
|
||||
/* Fetch up to the first 100 bytes. Hopefully that'll be enough.
|
||||
*/
|
||||
unsigned char buf[100];
|
||||
int len;
|
||||
|
||||
return( (len = vips__get_bytes( filename, buf, 100 )) > 10 &&
|
||||
magick_ismagick( buf, len ) );
|
||||
}
|
||||
|
||||
/* Unfortunately, libMagick does not support header-only reads very well. See
|
||||
*
|
||||
* http://www.imagemagick.org/discourse-server/viewtopic.php?f=1&t=20017
|
||||
*
|
||||
* Test especially with BMP, GIF, TGA. So we are forced to read the entire
|
||||
* image in the @header() method.
|
||||
*/
|
||||
static int
|
||||
vips_foreign_load_magick_file_header( VipsForeignLoad *load )
|
||||
{
|
||||
VipsForeignLoadMagick *magick = (VipsForeignLoadMagick *) load;
|
||||
VipsForeignLoadMagickFile *magick_file =
|
||||
(VipsForeignLoadMagickFile *) load;
|
||||
|
||||
if( magick->all_frames )
|
||||
magick->n = -1;
|
||||
|
||||
if( vips__magick_read( magick_file->filename,
|
||||
load->out, magick->density,
|
||||
magick->page, magick->n ) )
|
||||
return( -1 );
|
||||
|
||||
VIPS_SETSTR( load->out->filename, magick_file->filename );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_magick_file_class_init(
|
||||
VipsForeignLoadMagickFileClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "magickload";
|
||||
object_class->description = _( "load file with ImageMagick" );
|
||||
|
||||
load_class->is_a = ismagick;
|
||||
load_class->header = vips_foreign_load_magick_file_header;
|
||||
load_class->load = NULL;
|
||||
|
||||
VIPS_ARG_STRING( class, "filename", 1,
|
||||
_( "Filename" ),
|
||||
_( "Filename to load from" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadMagickFile, filename ),
|
||||
NULL );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_magick_file_init( VipsForeignLoadMagickFile *magick_file )
|
||||
{
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignLoadMagickBuffer {
|
||||
VipsForeignLoadMagick parent_object;
|
||||
|
||||
VipsArea *buf;
|
||||
|
||||
} VipsForeignLoadMagickBuffer;
|
||||
|
||||
typedef VipsForeignLoadMagickClass VipsForeignLoadMagickBufferClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsForeignLoadMagickBuffer, vips_foreign_load_magick_buffer,
|
||||
vips_foreign_load_magick_get_type() );
|
||||
|
||||
static gboolean
|
||||
vips_foreign_load_magick_buffer_is_a_buffer( const void *buf, size_t len )
|
||||
{
|
||||
return( len > 10 && magick_ismagick( (const unsigned char *) buf, len ) );
|
||||
}
|
||||
|
||||
/* Unfortunately, libMagick does not support header-only reads very well. See
|
||||
*
|
||||
* http://www.imagemagick.org/discourse-server/viewtopic.php?f=1&t=20017
|
||||
*
|
||||
* Test especially with BMP, GIF, TGA. So we are forced to read the entire
|
||||
* image in the @header() method.
|
||||
*/
|
||||
static int
|
||||
vips_foreign_load_magick_buffer_header( VipsForeignLoad *load )
|
||||
{
|
||||
VipsForeignLoadMagick *magick = (VipsForeignLoadMagick *) load;
|
||||
VipsForeignLoadMagickBuffer *magick_buffer =
|
||||
(VipsForeignLoadMagickBuffer *) load;
|
||||
|
||||
if( magick->all_frames )
|
||||
magick->n = -1;
|
||||
|
||||
if( vips__magick_read_buffer(
|
||||
magick_buffer->buf->data, magick_buffer->buf->length,
|
||||
load->out, magick->density, magick->page,
|
||||
magick->n ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_magick_buffer_class_init(
|
||||
VipsForeignLoadMagickBufferClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "magickload_buffer";
|
||||
object_class->description = _( "load buffer with ImageMagick" );
|
||||
|
||||
load_class->is_a_buffer = vips_foreign_load_magick_buffer_is_a_buffer;
|
||||
load_class->header = vips_foreign_load_magick_buffer_header;
|
||||
load_class->load = NULL;
|
||||
|
||||
VIPS_ARG_BOXED( class, "buffer", 1,
|
||||
_( "Buffer" ),
|
||||
_( "Buffer to load from" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadMagickBuffer, buf ),
|
||||
VIPS_TYPE_BLOB );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_magick_buffer_init( VipsForeignLoadMagickBuffer *buffer )
|
||||
{
|
||||
}
|
||||
|
||||
#endif /*HAVE_MAGICK6*/
|
||||
|
||||
#endif /*ENABLE_MAGICKLOAD*/
|
@ -12,6 +12,8 @@
|
||||
* - add @n, deprecate @all_frames (just sets n = -1)
|
||||
* 8/9/17
|
||||
* - don't cache magickload
|
||||
* 21/4/21 kleisauke
|
||||
* - move GObject part to magick6load.c
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -58,269 +60,6 @@
|
||||
#include <vips/buf.h>
|
||||
#include <vips/internal.h>
|
||||
|
||||
#ifdef ENABLE_MAGICKLOAD
|
||||
|
||||
#ifdef HAVE_MAGICK6
|
||||
|
||||
#include "pforeign.h"
|
||||
#include "magick.h"
|
||||
|
||||
typedef struct _VipsForeignLoadMagick {
|
||||
VipsForeignLoad parent_object;
|
||||
|
||||
/* Deprecated. Just sets n = -1.
|
||||
*/
|
||||
gboolean all_frames;
|
||||
|
||||
char *density; /* Load at this resolution */
|
||||
int page; /* Load this page (frame) */
|
||||
int n; /* Load this many pages */
|
||||
|
||||
} VipsForeignLoadMagick;
|
||||
|
||||
typedef VipsForeignLoadClass VipsForeignLoadMagickClass;
|
||||
|
||||
G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadMagick, vips_foreign_load_magick,
|
||||
VIPS_TYPE_FOREIGN_LOAD );
|
||||
|
||||
static VipsForeignFlags
|
||||
vips_foreign_load_magick_get_flags_filename( const char *filename )
|
||||
{
|
||||
return( VIPS_FOREIGN_PARTIAL );
|
||||
}
|
||||
|
||||
static VipsForeignFlags
|
||||
vips_foreign_load_magick_get_flags( VipsForeignLoad *load )
|
||||
{
|
||||
return( VIPS_FOREIGN_PARTIAL );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_magick_class_init( VipsForeignLoadMagickClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class );
|
||||
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
|
||||
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "magickload_base";
|
||||
object_class->description = _( "load with ImageMagick" );
|
||||
|
||||
/* Don't cache magickload: it can gobble up memory and disc.
|
||||
*/
|
||||
operation_class->flags = VIPS_OPERATION_NOCACHE;
|
||||
|
||||
/* We need to be well to the back of the queue since vips's
|
||||
* dedicated loaders are usually preferable.
|
||||
*/
|
||||
foreign_class->priority = -100;
|
||||
|
||||
load_class->get_flags_filename =
|
||||
vips_foreign_load_magick_get_flags_filename;
|
||||
load_class->get_flags = vips_foreign_load_magick_get_flags;
|
||||
|
||||
VIPS_ARG_BOOL( class, "all_frames", 20,
|
||||
_( "all_frames" ),
|
||||
_( "Read all frames from an image" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT | VIPS_ARGUMENT_DEPRECATED,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadMagick, all_frames ),
|
||||
FALSE );
|
||||
|
||||
VIPS_ARG_STRING( class, "density", 21,
|
||||
_( "Density" ),
|
||||
_( "Canvas resolution for rendering vector formats like SVG" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadMagick, density ),
|
||||
NULL );
|
||||
|
||||
VIPS_ARG_INT( class, "page", 22,
|
||||
_( "Page" ),
|
||||
_( "Load this page from the file" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadMagick, page ),
|
||||
0, 100000, 0 );
|
||||
|
||||
VIPS_ARG_INT( class, "n", 23,
|
||||
_( "n" ),
|
||||
_( "Load this many pages" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadMagick, n ),
|
||||
-1, 100000, 1 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_magick_init( VipsForeignLoadMagick *magick )
|
||||
{
|
||||
magick->n = 1;
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignLoadMagickFile {
|
||||
VipsForeignLoadMagick parent_object;
|
||||
|
||||
char *filename;
|
||||
|
||||
} VipsForeignLoadMagickFile;
|
||||
|
||||
typedef VipsForeignLoadMagickClass VipsForeignLoadMagickFileClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsForeignLoadMagickFile, vips_foreign_load_magick_file,
|
||||
vips_foreign_load_magick_get_type() );
|
||||
|
||||
static gboolean
|
||||
ismagick( const char *filename )
|
||||
{
|
||||
/* Fetch up to the first 100 bytes. Hopefully that'll be enough.
|
||||
*/
|
||||
unsigned char buf[100];
|
||||
int len;
|
||||
|
||||
return( (len = vips__get_bytes( filename, buf, 100 )) > 10 &&
|
||||
magick_ismagick( buf, len ) );
|
||||
}
|
||||
|
||||
/* Unfortunately, libMagick does not support header-only reads very well. See
|
||||
*
|
||||
* http://www.imagemagick.org/discourse-server/viewtopic.php?f=1&t=20017
|
||||
*
|
||||
* Test especially with BMP, GIF, TGA. So we are forced to read the entire
|
||||
* image in the @header() method.
|
||||
*/
|
||||
static int
|
||||
vips_foreign_load_magick_file_header( VipsForeignLoad *load )
|
||||
{
|
||||
VipsForeignLoadMagick *magick = (VipsForeignLoadMagick *) load;
|
||||
VipsForeignLoadMagickFile *magick_file =
|
||||
(VipsForeignLoadMagickFile *) load;
|
||||
|
||||
if( magick->all_frames )
|
||||
magick->n = -1;
|
||||
|
||||
if( vips__magick_read( magick_file->filename,
|
||||
load->out, magick->density,
|
||||
magick->page, magick->n ) )
|
||||
return( -1 );
|
||||
|
||||
VIPS_SETSTR( load->out->filename, magick_file->filename );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_magick_file_class_init(
|
||||
VipsForeignLoadMagickFileClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "magickload";
|
||||
object_class->description = _( "load file with ImageMagick" );
|
||||
|
||||
load_class->is_a = ismagick;
|
||||
load_class->header = vips_foreign_load_magick_file_header;
|
||||
load_class->load = NULL;
|
||||
|
||||
VIPS_ARG_STRING( class, "filename", 1,
|
||||
_( "Filename" ),
|
||||
_( "Filename to load from" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadMagickFile, filename ),
|
||||
NULL );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_magick_file_init( VipsForeignLoadMagickFile *magick_file )
|
||||
{
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignLoadMagickBuffer {
|
||||
VipsForeignLoadMagick parent_object;
|
||||
|
||||
VipsArea *buf;
|
||||
|
||||
} VipsForeignLoadMagickBuffer;
|
||||
|
||||
typedef VipsForeignLoadMagickClass VipsForeignLoadMagickBufferClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsForeignLoadMagickBuffer, vips_foreign_load_magick_buffer,
|
||||
vips_foreign_load_magick_get_type() );
|
||||
|
||||
static gboolean
|
||||
vips_foreign_load_magick_buffer_is_a_buffer( const void *buf, size_t len )
|
||||
{
|
||||
return( len > 10 && magick_ismagick( (const unsigned char *) buf, len ) );
|
||||
}
|
||||
|
||||
/* Unfortunately, libMagick does not support header-only reads very well. See
|
||||
*
|
||||
* http://www.imagemagick.org/discourse-server/viewtopic.php?f=1&t=20017
|
||||
*
|
||||
* Test especially with BMP, GIF, TGA. So we are forced to read the entire
|
||||
* image in the @header() method.
|
||||
*/
|
||||
static int
|
||||
vips_foreign_load_magick_buffer_header( VipsForeignLoad *load )
|
||||
{
|
||||
VipsForeignLoadMagick *magick = (VipsForeignLoadMagick *) load;
|
||||
VipsForeignLoadMagickBuffer *magick_buffer =
|
||||
(VipsForeignLoadMagickBuffer *) load;
|
||||
|
||||
if( magick->all_frames )
|
||||
magick->n = -1;
|
||||
|
||||
if( vips__magick_read_buffer(
|
||||
magick_buffer->buf->data, magick_buffer->buf->length,
|
||||
load->out, magick->density, magick->page,
|
||||
magick->n ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_magick_buffer_class_init(
|
||||
VipsForeignLoadMagickBufferClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "magickload_buffer";
|
||||
object_class->description = _( "load buffer with ImageMagick" );
|
||||
|
||||
load_class->is_a_buffer = vips_foreign_load_magick_buffer_is_a_buffer;
|
||||
load_class->header = vips_foreign_load_magick_buffer_header;
|
||||
load_class->load = NULL;
|
||||
|
||||
VIPS_ARG_BOXED( class, "buffer", 1,
|
||||
_( "Buffer" ),
|
||||
_( "Buffer to load from" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadMagickBuffer, buf ),
|
||||
VIPS_TYPE_BLOB );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_magick_buffer_init( VipsForeignLoadMagickBuffer *buffer )
|
||||
{
|
||||
}
|
||||
|
||||
#endif /*HAVE_MAGICK6*/
|
||||
|
||||
#endif /*ENABLE_MAGICKLOAD*/
|
||||
|
||||
/**
|
||||
* vips_magickload:
|
||||
* @filename: file to load
|
||||
|
@ -12,6 +12,8 @@
|
||||
* - support array of delays
|
||||
* 5/8/19 DarthSim
|
||||
* - support GIF optimization
|
||||
* 21/4/21 kleisauke
|
||||
* - move GObject part to vips2magick.c
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -52,584 +54,6 @@
|
||||
|
||||
#include <vips/vips.h>
|
||||
|
||||
#ifdef ENABLE_MAGICKSAVE
|
||||
|
||||
#include "pforeign.h"
|
||||
#include "magick.h"
|
||||
|
||||
typedef struct _VipsForeignSaveMagick {
|
||||
VipsForeignSave parent_object;
|
||||
|
||||
/* Parameters.
|
||||
*/
|
||||
char *filename; /* NULL during buffer output */
|
||||
char *format;
|
||||
int quality;
|
||||
gboolean optimize_gif_frames;
|
||||
gboolean optimize_gif_transparency;
|
||||
|
||||
ImageInfo *image_info;
|
||||
ExceptionInfo *exception;
|
||||
char *map;
|
||||
StorageType storage_type;
|
||||
Image *images;
|
||||
Image *current_image;
|
||||
|
||||
int page_height;
|
||||
GValue delay_gvalue;
|
||||
int *delays;
|
||||
int delays_length;
|
||||
|
||||
/* The position of current_image in the output.
|
||||
*/
|
||||
VipsRect position;
|
||||
|
||||
} VipsForeignSaveMagick;
|
||||
|
||||
typedef VipsForeignSaveClass VipsForeignSaveMagickClass;
|
||||
|
||||
G_DEFINE_ABSTRACT_TYPE( VipsForeignSaveMagick, vips_foreign_save_magick,
|
||||
VIPS_TYPE_FOREIGN_SAVE );
|
||||
|
||||
static void
|
||||
vips_foreign_save_magick_dispose( GObject *gobject )
|
||||
{
|
||||
VipsForeignSaveMagick *magick = (VipsForeignSaveMagick *) gobject;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "vips_foreign_save_magick_dispose: %p\n", gobject );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
VIPS_FREE( magick->map );
|
||||
VIPS_FREEF( DestroyImageList, magick->images );
|
||||
VIPS_FREEF( DestroyImageInfo, magick->image_info );
|
||||
VIPS_FREEF( magick_destroy_exception, magick->exception );
|
||||
g_value_unset( &magick->delay_gvalue );
|
||||
|
||||
G_OBJECT_CLASS( vips_foreign_save_magick_parent_class )->
|
||||
dispose( gobject );
|
||||
}
|
||||
|
||||
/* Move current_image on to the next image we will write.
|
||||
*/
|
||||
static int
|
||||
vips_foreign_save_magick_next_image( VipsForeignSaveMagick *magick )
|
||||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( magick );
|
||||
VipsForeignSave *save = (VipsForeignSave *) magick;
|
||||
VipsImage *im = save->ready;
|
||||
|
||||
Image *image;
|
||||
int number;
|
||||
const char *str;
|
||||
int page_index;
|
||||
|
||||
g_assert( !magick->current_image );
|
||||
|
||||
if( magick->images == NULL ) {
|
||||
if( !(image = magick_acquire_image( magick->image_info,
|
||||
magick->exception )) )
|
||||
return( -1 );
|
||||
|
||||
magick->images = image;
|
||||
magick->position.top = 0;
|
||||
magick->position.left = 0;
|
||||
magick->position.width = im->Xsize;
|
||||
magick->position.height = magick->page_height;
|
||||
}
|
||||
else {
|
||||
image = GetLastImageInList( magick->images );
|
||||
magick_acquire_next_image( magick->image_info, image,
|
||||
magick->exception );
|
||||
if( GetNextImageInList( image ) == NULL )
|
||||
return( -1 );
|
||||
|
||||
image = SyncNextImageInList( image );
|
||||
magick->position.top += magick->page_height;
|
||||
}
|
||||
|
||||
if( !magick_set_image_size( image,
|
||||
im->Xsize, magick->page_height, magick->exception ) ) {
|
||||
magick_vips_error( class->nickname, magick->exception );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* Delay must be converted from milliseconds into centiseconds
|
||||
* as GIF image requires centiseconds.
|
||||
*/
|
||||
if( magick->delays ) {
|
||||
page_index = magick->position.top / magick->page_height;
|
||||
if( page_index < magick->delays_length )
|
||||
image->delay =
|
||||
VIPS_RINT( magick->delays[page_index] / 10.0 );
|
||||
}
|
||||
|
||||
/* ImageMagick uses iterations like this (at least in gif save):
|
||||
* 0 - set 0 loops (infinite)
|
||||
* 1 - don't write the netscape extension block
|
||||
* 2 - loop once
|
||||
* 3 - loop twice etc.
|
||||
*/
|
||||
if( vips_image_get_typeof( im, "loop" ) &&
|
||||
!vips_image_get_int( im, "loop", &number ) ) {
|
||||
image->iterations = (size_t) number;
|
||||
}
|
||||
else {
|
||||
/* DEPRECATED "gif-loop"
|
||||
*
|
||||
* We have the simple gif meaning, so we must add one unless
|
||||
* it's zero.
|
||||
*/
|
||||
if( vips_image_get_typeof( im, "gif-loop" ) &&
|
||||
!vips_image_get_int( im, "gif-loop", &number ) )
|
||||
image->iterations = (size_t) (number ? number + 1 : 0);
|
||||
}
|
||||
|
||||
if( vips_image_get_typeof( im, "gif-comment" ) &&
|
||||
!vips_image_get_string( im, "gif-comment", &str ) )
|
||||
magick_set_property( image, "comment", str, magick->exception );
|
||||
|
||||
/* libvips keeps animations as a set of independent frames, so we want
|
||||
* to clear to the background between each one.
|
||||
*/
|
||||
image->dispose = BackgroundDispose;
|
||||
|
||||
if( !save->strip &&
|
||||
magick_set_magick_profile( image, im, magick->exception ) ) {
|
||||
magick_vips_error( class->nickname, magick->exception );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
magick->current_image = image;
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* We've written all the pixels to current_image ... finish it off ready to
|
||||
* move on.
|
||||
*/
|
||||
static void
|
||||
vips_foreign_save_magick_end_image( VipsForeignSaveMagick *magick )
|
||||
{
|
||||
if( magick->current_image ) {
|
||||
magick_inherit_exception( magick->exception,
|
||||
magick->current_image );
|
||||
magick->current_image = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Another block of pixels have arrived from libvips.
|
||||
*/
|
||||
static int
|
||||
vips_foreign_save_magick_write_block( VipsRegion *region, VipsRect *area,
|
||||
void *a )
|
||||
{
|
||||
VipsForeignSaveMagick *magick = (VipsForeignSaveMagick *) a;
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( magick );
|
||||
|
||||
VipsRect pixels;
|
||||
|
||||
pixels = region->valid;
|
||||
do {
|
||||
VipsRect hit;
|
||||
void *p;
|
||||
|
||||
if( !magick->current_image &&
|
||||
vips_foreign_save_magick_next_image( magick ) )
|
||||
return( -1 );
|
||||
|
||||
vips_rect_intersectrect( &pixels, &magick->position, &hit );
|
||||
p = VIPS_REGION_ADDR( region, hit.left, hit.top );
|
||||
if( !magick_import_pixels( magick->current_image,
|
||||
hit.left, hit.top - magick->position.top,
|
||||
hit.width, hit.height,
|
||||
magick->map, magick->storage_type,
|
||||
p,
|
||||
magick->exception ) ) {
|
||||
magick_vips_error( class->nickname,
|
||||
magick->exception );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* Have we filled the page.
|
||||
*/
|
||||
if( VIPS_RECT_BOTTOM( &hit ) ==
|
||||
VIPS_RECT_BOTTOM( &magick->position ) )
|
||||
vips_foreign_save_magick_end_image( magick );
|
||||
|
||||
pixels.top += hit.height;
|
||||
pixels.height -= hit.height;
|
||||
} while( pixels.height > 0 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_foreign_save_magick_build( VipsObject *object )
|
||||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
|
||||
VipsForeignSave *save = (VipsForeignSave *) object;
|
||||
VipsForeignSaveMagick *magick = (VipsForeignSaveMagick *) object;
|
||||
|
||||
VipsImage *im;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "vips_foreign_save_magick_build: %p\n", object );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_magick_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
magick_genesis();
|
||||
|
||||
/* The image to save.
|
||||
*/
|
||||
im = save->ready;
|
||||
|
||||
magick->exception = magick_acquire_exception();
|
||||
magick->image_info = CloneImageInfo( NULL );
|
||||
|
||||
switch( im->BandFmt ) {
|
||||
case VIPS_FORMAT_UCHAR:
|
||||
magick->storage_type = CharPixel;
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_USHORT:
|
||||
magick->storage_type = ShortPixel;
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_UINT:
|
||||
magick->storage_type = LongPixel;
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_FLOAT:
|
||||
magick->storage_type = FloatPixel;
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_DOUBLE:
|
||||
magick->storage_type = DoublePixel;
|
||||
break;
|
||||
|
||||
default:
|
||||
vips_error( class->nickname,
|
||||
"%s", _( "unsupported image format" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
switch( im->Bands ) {
|
||||
case 1:
|
||||
magick->map = g_strdup( "I" );
|
||||
break;
|
||||
|
||||
case 2:
|
||||
magick->map = g_strdup( "IA" );
|
||||
break;
|
||||
|
||||
case 3:
|
||||
magick->map = g_strdup( "RGB" );
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if( im->Type == VIPS_INTERPRETATION_CMYK )
|
||||
magick->map = g_strdup( "CMYK" );
|
||||
else
|
||||
magick->map = g_strdup( "RGBA" );
|
||||
break;
|
||||
|
||||
case 5:
|
||||
magick->map = g_strdup( "CMYKA" );
|
||||
break;
|
||||
|
||||
default:
|
||||
vips_error( class->nickname,
|
||||
"%s", _( "unsupported number of image bands" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( magick->format ) {
|
||||
vips_strncpy( magick->image_info->magick,
|
||||
magick->format, MaxPathExtent );
|
||||
if( magick->filename )
|
||||
(void) vips_snprintf( magick->image_info->filename,
|
||||
MaxPathExtent, "%s:%s",
|
||||
magick->format, magick->filename );
|
||||
}
|
||||
else if( magick->filename ) {
|
||||
vips_strncpy( magick->image_info->filename,
|
||||
magick->filename, MaxPathExtent );
|
||||
}
|
||||
|
||||
if( magick->quality > 0 )
|
||||
magick->image_info->quality = magick->quality;
|
||||
|
||||
magick->page_height = vips_image_get_page_height( im );
|
||||
|
||||
/* Get as a gvalue so we can keep a ref to the delay array while we
|
||||
* need it.
|
||||
*/
|
||||
if( vips_image_get_typeof( im, "delay" ) ) {
|
||||
g_value_unset( &magick->delay_gvalue );
|
||||
if( vips_image_get( im, "delay", &magick->delay_gvalue ) )
|
||||
return( -1 );
|
||||
magick->delays = vips_value_get_array_int(
|
||||
&magick->delay_gvalue, &magick->delays_length );
|
||||
}
|
||||
|
||||
if( vips_sink_disc( im,
|
||||
vips_foreign_save_magick_write_block, magick ) )
|
||||
return( -1 );
|
||||
|
||||
if( magick->optimize_gif_frames ) {
|
||||
if( !magick_optimize_image_layers( &magick->images,
|
||||
magick->exception ) ) {
|
||||
magick_inherit_exception( magick->exception,
|
||||
magick->images );
|
||||
magick_vips_error( class->nickname, magick->exception );
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
|
||||
if( magick->optimize_gif_transparency ) {
|
||||
if( !magick_optimize_image_transparency( magick->images,
|
||||
magick->exception ) ) {
|
||||
magick_inherit_exception( magick->exception,
|
||||
magick->images );
|
||||
magick_vips_error( class->nickname, magick->exception );
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* We could call into libMagick and discover what save formats it supports, but
|
||||
* that would mean starting up libMagick on libvips init, and that would add a
|
||||
* lot of time.
|
||||
*
|
||||
* Instead, just list the commonly-used formats that all libMagicks support and
|
||||
* that libvips does not.
|
||||
*/
|
||||
static const char *vips__save_magick_suffs[] = { ".gif", ".bmp", NULL };
|
||||
|
||||
/* Save a bit of typing.
|
||||
*/
|
||||
#define UC VIPS_FORMAT_UCHAR
|
||||
#define US VIPS_FORMAT_USHORT
|
||||
#define UI VIPS_FORMAT_UINT
|
||||
#define F VIPS_FORMAT_FLOAT
|
||||
#define D VIPS_FORMAT_DOUBLE
|
||||
|
||||
static int bandfmt_magick[10] = {
|
||||
/* UC C US S UI I F X D DX */
|
||||
UC, UC, US, US, UI, UI, F, F, D, D
|
||||
};
|
||||
|
||||
static void
|
||||
vips_foreign_save_magick_class_init( VipsForeignSaveMagickClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
|
||||
VipsForeignSaveClass *save_class = (VipsForeignSaveClass *) class;
|
||||
|
||||
gobject_class->dispose = vips_foreign_save_magick_dispose;
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "magicksave_base";
|
||||
object_class->description = _( "save with ImageMagick" );
|
||||
object_class->build = vips_foreign_save_magick_build;
|
||||
|
||||
/* We need to be well to the back of the queue since vips's
|
||||
* dedicated savers are usually preferable.
|
||||
*/
|
||||
foreign_class->priority = -100;
|
||||
foreign_class->suffs = vips__save_magick_suffs;
|
||||
|
||||
save_class->saveable = VIPS_SAVEABLE_ANY;
|
||||
save_class->format_table = bandfmt_magick;
|
||||
|
||||
VIPS_ARG_STRING( class, "format", 2,
|
||||
_( "Format" ),
|
||||
_( "Format to save in" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveMagick, format ),
|
||||
NULL );
|
||||
|
||||
VIPS_ARG_INT( class, "quality", 3,
|
||||
_( "Quality" ),
|
||||
_( "Quality to use" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveMagick, quality ),
|
||||
0, 100, 0 );
|
||||
|
||||
VIPS_ARG_BOOL( class, "optimize_gif_frames", 4,
|
||||
_( "Optimize_gif_frames" ),
|
||||
_( "Apply GIF frames optimization" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveMagick, optimize_gif_frames ),
|
||||
FALSE );
|
||||
|
||||
VIPS_ARG_BOOL( class, "optimize_gif_transparency", 5,
|
||||
_( "Optimize_gif_transparency" ),
|
||||
_( "Apply GIF transparency optimization" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveMagick,
|
||||
optimize_gif_transparency ),
|
||||
FALSE );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_magick_init( VipsForeignSaveMagick *magick )
|
||||
{
|
||||
/* Init to an int just to have something there. It is swapped for an
|
||||
* int array later.
|
||||
*/
|
||||
g_value_init( &magick->delay_gvalue, G_TYPE_INT );
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignSaveMagickFile {
|
||||
VipsForeignSaveMagick parent_object;
|
||||
|
||||
char *filename;
|
||||
|
||||
} VipsForeignSaveMagickFile;
|
||||
|
||||
typedef VipsForeignSaveMagickClass VipsForeignSaveMagickFileClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsForeignSaveMagickFile, vips_foreign_save_magick_file,
|
||||
vips_foreign_save_magick_get_type() );
|
||||
|
||||
static int
|
||||
vips_foreign_save_magick_file_build( VipsObject *object )
|
||||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
|
||||
VipsForeignSaveMagick *magick = (VipsForeignSaveMagick *) object;
|
||||
VipsForeignSaveMagickFile *file = (VipsForeignSaveMagickFile *) object;
|
||||
|
||||
magick->filename = file->filename;
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_magick_file_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
if( !WriteImages( magick->image_info, magick->images,
|
||||
magick->image_info->filename, magick->exception ) ) {
|
||||
magick_inherit_exception( magick->exception, magick->images );
|
||||
magick_vips_error( class->nickname, magick->exception );
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_magick_file_class_init(
|
||||
VipsForeignSaveMagickFileClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "magicksave";
|
||||
object_class->description = _( "save file with ImageMagick" );
|
||||
object_class->build = vips_foreign_save_magick_file_build;
|
||||
|
||||
VIPS_ARG_STRING( class, "filename", 1,
|
||||
_( "Filename" ),
|
||||
_( "Filename to save to" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveMagickFile, filename ),
|
||||
NULL );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_magick_file_init( VipsForeignSaveMagickFile *file )
|
||||
{
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignSaveMagickBuffer {
|
||||
VipsForeignSaveMagick parent_object;
|
||||
|
||||
/* Save to a buffer.
|
||||
*/
|
||||
VipsArea *buf;
|
||||
|
||||
} VipsForeignSaveMagickBuffer;
|
||||
|
||||
typedef VipsForeignSaveMagickClass VipsForeignSaveMagickBufferClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsForeignSaveMagickBuffer, vips_foreign_save_magick_buffer,
|
||||
vips_foreign_save_magick_get_type() );
|
||||
|
||||
static int
|
||||
vips_foreign_save_magick_buffer_build( VipsObject *object )
|
||||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
|
||||
VipsForeignSaveMagick *magick = (VipsForeignSaveMagick *) object;
|
||||
VipsForeignSaveMagickBuffer *buffer =
|
||||
(VipsForeignSaveMagickBuffer *) object;
|
||||
|
||||
void *obuf;
|
||||
size_t olen;
|
||||
VipsBlob *blob;
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_magick_buffer_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
if( !(obuf = magick_images_to_blob( magick->image_info, magick->images,
|
||||
&olen, magick->exception )) ) {
|
||||
magick_inherit_exception( magick->exception, magick->images );
|
||||
magick_vips_error( class->nickname, magick->exception );
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
blob = vips_blob_new( (VipsCallbackFn) vips_area_free_cb, obuf, olen );
|
||||
g_object_set( buffer, "buffer", blob, NULL );
|
||||
vips_area_unref( VIPS_AREA( blob ) );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_magick_buffer_class_init(
|
||||
VipsForeignSaveMagickBufferClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "magicksave_buffer";
|
||||
object_class->description = _( "save image to magick buffer" );
|
||||
object_class->build = vips_foreign_save_magick_buffer_build;
|
||||
|
||||
VIPS_ARG_BOXED( class, "buffer", 1,
|
||||
_( "Buffer" ),
|
||||
_( "Buffer to save to" ),
|
||||
VIPS_ARGUMENT_REQUIRED_OUTPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveMagickBuffer, buf ),
|
||||
VIPS_TYPE_BLOB );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_magick_buffer_init( VipsForeignSaveMagickBuffer *buffer )
|
||||
{
|
||||
}
|
||||
|
||||
#endif /*ENABLE_MAGICKSAVE*/
|
||||
|
||||
/**
|
||||
* vips_magicksave: (method)
|
||||
* @in: image to save
|
||||
|
@ -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*/
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 );
|
||||
|
||||
|
811
libvips/foreign/poppler2vips.c
Normal file
811
libvips/foreign/poppler2vips.c
Normal 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
714
libvips/foreign/vips2heif.c
Normal 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*/
|
633
libvips/foreign/vips2magick.c
Normal file
633
libvips/foreign/vips2magick.c
Normal file
@ -0,0 +1,633 @@
|
||||
/* save with libMagick
|
||||
*
|
||||
* 22/12/17 dlemstra
|
||||
* 6/2/19 DarthSim
|
||||
* - fix GraphicsMagick support
|
||||
* 17/2/19
|
||||
* - support ICC, XMP, EXIF, IPTC metadata
|
||||
* - write with a single call to vips_sink_disc()
|
||||
* 29/6/19
|
||||
* - support "strip" option
|
||||
* 6/7/19 [deftomat]
|
||||
* - support array of delays
|
||||
* 5/8/19 DarthSim
|
||||
* - support GIF optimization
|
||||
* 21/4/21 kleisauke
|
||||
* - include GObject part from magicksave.c
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
|
||||
#ifdef ENABLE_MAGICKSAVE
|
||||
|
||||
#include "pforeign.h"
|
||||
#include "magick.h"
|
||||
|
||||
typedef struct _VipsForeignSaveMagick {
|
||||
VipsForeignSave parent_object;
|
||||
|
||||
/* Parameters.
|
||||
*/
|
||||
char *filename; /* NULL during buffer output */
|
||||
char *format;
|
||||
int quality;
|
||||
gboolean optimize_gif_frames;
|
||||
gboolean optimize_gif_transparency;
|
||||
|
||||
ImageInfo *image_info;
|
||||
ExceptionInfo *exception;
|
||||
char *map;
|
||||
StorageType storage_type;
|
||||
Image *images;
|
||||
Image *current_image;
|
||||
|
||||
int page_height;
|
||||
GValue delay_gvalue;
|
||||
int *delays;
|
||||
int delays_length;
|
||||
|
||||
/* The position of current_image in the output.
|
||||
*/
|
||||
VipsRect position;
|
||||
|
||||
} VipsForeignSaveMagick;
|
||||
|
||||
typedef VipsForeignSaveClass VipsForeignSaveMagickClass;
|
||||
|
||||
G_DEFINE_ABSTRACT_TYPE( VipsForeignSaveMagick, vips_foreign_save_magick,
|
||||
VIPS_TYPE_FOREIGN_SAVE );
|
||||
|
||||
static void
|
||||
vips_foreign_save_magick_dispose( GObject *gobject )
|
||||
{
|
||||
VipsForeignSaveMagick *magick = (VipsForeignSaveMagick *) gobject;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "vips_foreign_save_magick_dispose: %p\n", gobject );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
VIPS_FREE( magick->map );
|
||||
VIPS_FREEF( DestroyImageList, magick->images );
|
||||
VIPS_FREEF( DestroyImageInfo, magick->image_info );
|
||||
VIPS_FREEF( magick_destroy_exception, magick->exception );
|
||||
g_value_unset( &magick->delay_gvalue );
|
||||
|
||||
G_OBJECT_CLASS( vips_foreign_save_magick_parent_class )->
|
||||
dispose( gobject );
|
||||
}
|
||||
|
||||
/* Move current_image on to the next image we will write.
|
||||
*/
|
||||
static int
|
||||
vips_foreign_save_magick_next_image( VipsForeignSaveMagick *magick )
|
||||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( magick );
|
||||
VipsForeignSave *save = (VipsForeignSave *) magick;
|
||||
VipsImage *im = save->ready;
|
||||
|
||||
Image *image;
|
||||
int number;
|
||||
const char *str;
|
||||
int page_index;
|
||||
|
||||
g_assert( !magick->current_image );
|
||||
|
||||
if( magick->images == NULL ) {
|
||||
if( !(image = magick_acquire_image( magick->image_info,
|
||||
magick->exception )) )
|
||||
return( -1 );
|
||||
|
||||
magick->images = image;
|
||||
magick->position.top = 0;
|
||||
magick->position.left = 0;
|
||||
magick->position.width = im->Xsize;
|
||||
magick->position.height = magick->page_height;
|
||||
}
|
||||
else {
|
||||
image = GetLastImageInList( magick->images );
|
||||
magick_acquire_next_image( magick->image_info, image,
|
||||
magick->exception );
|
||||
if( GetNextImageInList( image ) == NULL )
|
||||
return( -1 );
|
||||
|
||||
image = SyncNextImageInList( image );
|
||||
magick->position.top += magick->page_height;
|
||||
}
|
||||
|
||||
if( !magick_set_image_size( image,
|
||||
im->Xsize, magick->page_height, magick->exception ) ) {
|
||||
magick_vips_error( class->nickname, magick->exception );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* Delay must be converted from milliseconds into centiseconds
|
||||
* as GIF image requires centiseconds.
|
||||
*/
|
||||
if( magick->delays ) {
|
||||
page_index = magick->position.top / magick->page_height;
|
||||
if( page_index < magick->delays_length )
|
||||
image->delay =
|
||||
VIPS_RINT( magick->delays[page_index] / 10.0 );
|
||||
}
|
||||
|
||||
/* ImageMagick uses iterations like this (at least in gif save):
|
||||
* 0 - set 0 loops (infinite)
|
||||
* 1 - don't write the netscape extension block
|
||||
* 2 - loop once
|
||||
* 3 - loop twice etc.
|
||||
*/
|
||||
if( vips_image_get_typeof( im, "loop" ) &&
|
||||
!vips_image_get_int( im, "loop", &number ) ) {
|
||||
image->iterations = (size_t) number;
|
||||
}
|
||||
else {
|
||||
/* DEPRECATED "gif-loop"
|
||||
*
|
||||
* We have the simple gif meaning, so we must add one unless
|
||||
* it's zero.
|
||||
*/
|
||||
if( vips_image_get_typeof( im, "gif-loop" ) &&
|
||||
!vips_image_get_int( im, "gif-loop", &number ) )
|
||||
image->iterations = (size_t) (number ? number + 1 : 0);
|
||||
}
|
||||
|
||||
if( vips_image_get_typeof( im, "gif-comment" ) &&
|
||||
!vips_image_get_string( im, "gif-comment", &str ) )
|
||||
magick_set_property( image, "comment", str, magick->exception );
|
||||
|
||||
/* libvips keeps animations as a set of independent frames, so we want
|
||||
* to clear to the background between each one.
|
||||
*/
|
||||
image->dispose = BackgroundDispose;
|
||||
|
||||
if( !save->strip &&
|
||||
magick_set_magick_profile( image, im, magick->exception ) ) {
|
||||
magick_vips_error( class->nickname, magick->exception );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
magick->current_image = image;
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* We've written all the pixels to current_image ... finish it off ready to
|
||||
* move on.
|
||||
*/
|
||||
static void
|
||||
vips_foreign_save_magick_end_image( VipsForeignSaveMagick *magick )
|
||||
{
|
||||
if( magick->current_image ) {
|
||||
magick_inherit_exception( magick->exception,
|
||||
magick->current_image );
|
||||
magick->current_image = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Another block of pixels have arrived from libvips.
|
||||
*/
|
||||
static int
|
||||
vips_foreign_save_magick_write_block( VipsRegion *region, VipsRect *area,
|
||||
void *a )
|
||||
{
|
||||
VipsForeignSaveMagick *magick = (VipsForeignSaveMagick *) a;
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( magick );
|
||||
|
||||
VipsRect pixels;
|
||||
|
||||
pixels = region->valid;
|
||||
do {
|
||||
VipsRect hit;
|
||||
void *p;
|
||||
|
||||
if( !magick->current_image &&
|
||||
vips_foreign_save_magick_next_image( magick ) )
|
||||
return( -1 );
|
||||
|
||||
vips_rect_intersectrect( &pixels, &magick->position, &hit );
|
||||
p = VIPS_REGION_ADDR( region, hit.left, hit.top );
|
||||
if( !magick_import_pixels( magick->current_image,
|
||||
hit.left, hit.top - magick->position.top,
|
||||
hit.width, hit.height,
|
||||
magick->map, magick->storage_type,
|
||||
p,
|
||||
magick->exception ) ) {
|
||||
magick_vips_error( class->nickname,
|
||||
magick->exception );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* Have we filled the page.
|
||||
*/
|
||||
if( VIPS_RECT_BOTTOM( &hit ) ==
|
||||
VIPS_RECT_BOTTOM( &magick->position ) )
|
||||
vips_foreign_save_magick_end_image( magick );
|
||||
|
||||
pixels.top += hit.height;
|
||||
pixels.height -= hit.height;
|
||||
} while( pixels.height > 0 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_foreign_save_magick_build( VipsObject *object )
|
||||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
|
||||
VipsForeignSave *save = (VipsForeignSave *) object;
|
||||
VipsForeignSaveMagick *magick = (VipsForeignSaveMagick *) object;
|
||||
|
||||
VipsImage *im;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "vips_foreign_save_magick_build: %p\n", object );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_magick_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
magick_genesis();
|
||||
|
||||
/* The image to save.
|
||||
*/
|
||||
im = save->ready;
|
||||
|
||||
magick->exception = magick_acquire_exception();
|
||||
magick->image_info = CloneImageInfo( NULL );
|
||||
|
||||
switch( im->BandFmt ) {
|
||||
case VIPS_FORMAT_UCHAR:
|
||||
magick->storage_type = CharPixel;
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_USHORT:
|
||||
magick->storage_type = ShortPixel;
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_UINT:
|
||||
magick->storage_type = LongPixel;
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_FLOAT:
|
||||
magick->storage_type = FloatPixel;
|
||||
break;
|
||||
|
||||
case VIPS_FORMAT_DOUBLE:
|
||||
magick->storage_type = DoublePixel;
|
||||
break;
|
||||
|
||||
default:
|
||||
vips_error( class->nickname,
|
||||
"%s", _( "unsupported image format" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
switch( im->Bands ) {
|
||||
case 1:
|
||||
magick->map = g_strdup( "I" );
|
||||
break;
|
||||
|
||||
case 2:
|
||||
magick->map = g_strdup( "IA" );
|
||||
break;
|
||||
|
||||
case 3:
|
||||
magick->map = g_strdup( "RGB" );
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if( im->Type == VIPS_INTERPRETATION_CMYK )
|
||||
magick->map = g_strdup( "CMYK" );
|
||||
else
|
||||
magick->map = g_strdup( "RGBA" );
|
||||
break;
|
||||
|
||||
case 5:
|
||||
magick->map = g_strdup( "CMYKA" );
|
||||
break;
|
||||
|
||||
default:
|
||||
vips_error( class->nickname,
|
||||
"%s", _( "unsupported number of image bands" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( magick->format ) {
|
||||
vips_strncpy( magick->image_info->magick,
|
||||
magick->format, MaxPathExtent );
|
||||
if( magick->filename )
|
||||
(void) vips_snprintf( magick->image_info->filename,
|
||||
MaxPathExtent, "%s:%s",
|
||||
magick->format, magick->filename );
|
||||
}
|
||||
else if( magick->filename ) {
|
||||
vips_strncpy( magick->image_info->filename,
|
||||
magick->filename, MaxPathExtent );
|
||||
}
|
||||
|
||||
if( magick->quality > 0 )
|
||||
magick->image_info->quality = magick->quality;
|
||||
|
||||
magick->page_height = vips_image_get_page_height( im );
|
||||
|
||||
/* Get as a gvalue so we can keep a ref to the delay array while we
|
||||
* need it.
|
||||
*/
|
||||
if( vips_image_get_typeof( im, "delay" ) ) {
|
||||
g_value_unset( &magick->delay_gvalue );
|
||||
if( vips_image_get( im, "delay", &magick->delay_gvalue ) )
|
||||
return( -1 );
|
||||
magick->delays = vips_value_get_array_int(
|
||||
&magick->delay_gvalue, &magick->delays_length );
|
||||
}
|
||||
|
||||
if( vips_sink_disc( im,
|
||||
vips_foreign_save_magick_write_block, magick ) )
|
||||
return( -1 );
|
||||
|
||||
if( magick->optimize_gif_frames ) {
|
||||
if( !magick_optimize_image_layers( &magick->images,
|
||||
magick->exception ) ) {
|
||||
magick_inherit_exception( magick->exception,
|
||||
magick->images );
|
||||
magick_vips_error( class->nickname, magick->exception );
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
|
||||
if( magick->optimize_gif_transparency ) {
|
||||
if( !magick_optimize_image_transparency( magick->images,
|
||||
magick->exception ) ) {
|
||||
magick_inherit_exception( magick->exception,
|
||||
magick->images );
|
||||
magick_vips_error( class->nickname, magick->exception );
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* We could call into libMagick and discover what save formats it supports, but
|
||||
* that would mean starting up libMagick on libvips init, and that would add a
|
||||
* lot of time.
|
||||
*
|
||||
* Instead, just list the commonly-used formats that all libMagicks support and
|
||||
* that libvips does not.
|
||||
*/
|
||||
static const char *vips__save_magick_suffs[] = { ".gif", ".bmp", NULL };
|
||||
|
||||
/* Save a bit of typing.
|
||||
*/
|
||||
#define UC VIPS_FORMAT_UCHAR
|
||||
#define US VIPS_FORMAT_USHORT
|
||||
#define UI VIPS_FORMAT_UINT
|
||||
#define F VIPS_FORMAT_FLOAT
|
||||
#define D VIPS_FORMAT_DOUBLE
|
||||
|
||||
static int bandfmt_magick[10] = {
|
||||
/* UC C US S UI I F X D DX */
|
||||
UC, UC, US, US, UI, UI, F, F, D, D
|
||||
};
|
||||
|
||||
static void
|
||||
vips_foreign_save_magick_class_init( VipsForeignSaveMagickClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
|
||||
VipsForeignSaveClass *save_class = (VipsForeignSaveClass *) class;
|
||||
|
||||
gobject_class->dispose = vips_foreign_save_magick_dispose;
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "magicksave_base";
|
||||
object_class->description = _( "save with ImageMagick" );
|
||||
object_class->build = vips_foreign_save_magick_build;
|
||||
|
||||
/* We need to be well to the back of the queue since vips's
|
||||
* dedicated savers are usually preferable.
|
||||
*/
|
||||
foreign_class->priority = -100;
|
||||
foreign_class->suffs = vips__save_magick_suffs;
|
||||
|
||||
save_class->saveable = VIPS_SAVEABLE_ANY;
|
||||
save_class->format_table = bandfmt_magick;
|
||||
|
||||
VIPS_ARG_STRING( class, "format", 2,
|
||||
_( "Format" ),
|
||||
_( "Format to save in" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveMagick, format ),
|
||||
NULL );
|
||||
|
||||
VIPS_ARG_INT( class, "quality", 3,
|
||||
_( "Quality" ),
|
||||
_( "Quality to use" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveMagick, quality ),
|
||||
0, 100, 0 );
|
||||
|
||||
VIPS_ARG_BOOL( class, "optimize_gif_frames", 4,
|
||||
_( "Optimize_gif_frames" ),
|
||||
_( "Apply GIF frames optimization" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveMagick, optimize_gif_frames ),
|
||||
FALSE );
|
||||
|
||||
VIPS_ARG_BOOL( class, "optimize_gif_transparency", 5,
|
||||
_( "Optimize_gif_transparency" ),
|
||||
_( "Apply GIF transparency optimization" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveMagick,
|
||||
optimize_gif_transparency ),
|
||||
FALSE );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_magick_init( VipsForeignSaveMagick *magick )
|
||||
{
|
||||
/* Init to an int just to have something there. It is swapped for an
|
||||
* int array later.
|
||||
*/
|
||||
g_value_init( &magick->delay_gvalue, G_TYPE_INT );
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignSaveMagickFile {
|
||||
VipsForeignSaveMagick parent_object;
|
||||
|
||||
char *filename;
|
||||
|
||||
} VipsForeignSaveMagickFile;
|
||||
|
||||
typedef VipsForeignSaveMagickClass VipsForeignSaveMagickFileClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsForeignSaveMagickFile, vips_foreign_save_magick_file,
|
||||
vips_foreign_save_magick_get_type() );
|
||||
|
||||
static int
|
||||
vips_foreign_save_magick_file_build( VipsObject *object )
|
||||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
|
||||
VipsForeignSaveMagick *magick = (VipsForeignSaveMagick *) object;
|
||||
VipsForeignSaveMagickFile *file = (VipsForeignSaveMagickFile *) object;
|
||||
|
||||
magick->filename = file->filename;
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_magick_file_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
if( !WriteImages( magick->image_info, magick->images,
|
||||
magick->image_info->filename, magick->exception ) ) {
|
||||
magick_inherit_exception( magick->exception, magick->images );
|
||||
magick_vips_error( class->nickname, magick->exception );
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_magick_file_class_init(
|
||||
VipsForeignSaveMagickFileClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "magicksave";
|
||||
object_class->description = _( "save file with ImageMagick" );
|
||||
object_class->build = vips_foreign_save_magick_file_build;
|
||||
|
||||
VIPS_ARG_STRING( class, "filename", 1,
|
||||
_( "Filename" ),
|
||||
_( "Filename to save to" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveMagickFile, filename ),
|
||||
NULL );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_magick_file_init( VipsForeignSaveMagickFile *file )
|
||||
{
|
||||
}
|
||||
|
||||
typedef struct _VipsForeignSaveMagickBuffer {
|
||||
VipsForeignSaveMagick parent_object;
|
||||
|
||||
/* Save to a buffer.
|
||||
*/
|
||||
VipsArea *buf;
|
||||
|
||||
} VipsForeignSaveMagickBuffer;
|
||||
|
||||
typedef VipsForeignSaveMagickClass VipsForeignSaveMagickBufferClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsForeignSaveMagickBuffer, vips_foreign_save_magick_buffer,
|
||||
vips_foreign_save_magick_get_type() );
|
||||
|
||||
static int
|
||||
vips_foreign_save_magick_buffer_build( VipsObject *object )
|
||||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
|
||||
VipsForeignSaveMagick *magick = (VipsForeignSaveMagick *) object;
|
||||
VipsForeignSaveMagickBuffer *buffer =
|
||||
(VipsForeignSaveMagickBuffer *) object;
|
||||
|
||||
void *obuf;
|
||||
size_t olen;
|
||||
VipsBlob *blob;
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_magick_buffer_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
if( !(obuf = magick_images_to_blob( magick->image_info, magick->images,
|
||||
&olen, magick->exception )) ) {
|
||||
magick_inherit_exception( magick->exception, magick->images );
|
||||
magick_vips_error( class->nickname, magick->exception );
|
||||
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
blob = vips_blob_new( (VipsCallbackFn) vips_area_free_cb, obuf, olen );
|
||||
g_object_set( buffer, "buffer", blob, NULL );
|
||||
vips_area_unref( VIPS_AREA( blob ) );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_magick_buffer_class_init(
|
||||
VipsForeignSaveMagickBufferClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "magicksave_buffer";
|
||||
object_class->description = _( "save image to magick buffer" );
|
||||
object_class->build = vips_foreign_save_magick_buffer_build;
|
||||
|
||||
VIPS_ARG_BOXED( class, "buffer", 1,
|
||||
_( "Buffer" ),
|
||||
_( "Buffer to save to" ),
|
||||
VIPS_ARGUMENT_REQUIRED_OUTPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveMagickBuffer, buf ),
|
||||
VIPS_TYPE_BLOB );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_save_magick_buffer_init( VipsForeignSaveMagickBuffer *buffer )
|
||||
{
|
||||
}
|
||||
|
||||
#endif /*ENABLE_MAGICKSAVE*/
|
@ -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
87
libvips/module/heif.c
Normal 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
92
libvips/module/magick.c
Normal file
@ -0,0 +1,92 @@
|
||||
/* magick as a dynamically loadable module
|
||||
*
|
||||
* 21/4/21 kleisauke
|
||||
* - initial
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
#define DEBUG
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
#include <vips/debug.h>
|
||||
#include <vips/internal.h>
|
||||
|
||||
#if (defined(HAVE_MAGICK6) || defined (HAVE_MAGICK7)) && defined(MAGICK_MODULE)
|
||||
|
||||
/* This is called on module load.
|
||||
*/
|
||||
G_MODULE_EXPORT const gchar *
|
||||
g_module_check_init( GModule *module )
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf( "vips_magick: module init\n" );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
extern GType vips_foreign_load_magick_file_get_type( void );
|
||||
extern GType vips_foreign_load_magick_buffer_get_type( void );
|
||||
extern GType vips_foreign_load_magick7_file_get_type( void );
|
||||
extern GType vips_foreign_load_magick7_buffer_get_type( void );
|
||||
extern GType vips_foreign_save_magick_file_get_type( void );
|
||||
extern GType vips_foreign_save_magick_buffer_get_type( void );
|
||||
|
||||
#ifdef ENABLE_MAGICKLOAD
|
||||
#ifdef HAVE_MAGICK6
|
||||
vips_foreign_load_magick_file_get_type();
|
||||
vips_foreign_load_magick_buffer_get_type();
|
||||
#endif /*HAVE_MAGICK6*/
|
||||
|
||||
#ifdef HAVE_MAGICK7
|
||||
vips_foreign_load_magick7_file_get_type();
|
||||
vips_foreign_load_magick7_buffer_get_type();
|
||||
#endif /*HAVE_MAGICK7*/
|
||||
#endif /*ENABLE_MAGICKLOAD*/
|
||||
|
||||
#ifdef ENABLE_MAGICKSAVE
|
||||
vips_foreign_save_magick_file_get_type();
|
||||
vips_foreign_save_magick_buffer_get_type();
|
||||
#endif /*ENABLE_MAGICKSAVE*/
|
||||
|
||||
/* We can't be unloaded, there would be chaos.
|
||||
*/
|
||||
g_module_make_resident( module );
|
||||
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
#endif /*(defined(HAVE_MAGICK6) || defined (HAVE_MAGICK7)) && defined(MAGICK_MODULE)*/
|
74
libvips/module/openslide.c
Normal file
74
libvips/module/openslide.c
Normal 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
76
libvips/module/poppler.c
Normal 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)*/
|
Loading…
Reference in New Issue
Block a user