summaryrefslogtreecommitdiffstats
path: root/plug-ins/file-tiff
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:30:19 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:30:19 +0000
commit5c1676dfe6d2f3c837a5e074117b45613fd29a72 (patch)
treecbffb45144febf451e54061db2b21395faf94bfe /plug-ins/file-tiff
parentInitial commit. (diff)
downloadgimp-5c1676dfe6d2f3c837a5e074117b45613fd29a72.tar.xz
gimp-5c1676dfe6d2f3c837a5e074117b45613fd29a72.zip
Adding upstream version 2.10.34.upstream/2.10.34upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'plug-ins/file-tiff')
-rw-r--r--plug-ins/file-tiff/Makefile.am58
-rw-r--r--plug-ins/file-tiff/Makefile.in1022
-rw-r--r--plug-ins/file-tiff/file-tiff-io.c585
-rw-r--r--plug-ins/file-tiff/file-tiff-io.h50
-rw-r--r--plug-ins/file-tiff/file-tiff-load.c2815
-rw-r--r--plug-ins/file-tiff/file-tiff-load.h57
-rw-r--r--plug-ins/file-tiff/file-tiff-save.c1387
-rw-r--r--plug-ins/file-tiff/file-tiff-save.h64
-rw-r--r--plug-ins/file-tiff/file-tiff.c587
9 files changed, 6625 insertions, 0 deletions
diff --git a/plug-ins/file-tiff/Makefile.am b/plug-ins/file-tiff/Makefile.am
new file mode 100644
index 0000000..8969040
--- /dev/null
+++ b/plug-ins/file-tiff/Makefile.am
@@ -0,0 +1,58 @@
+## Process this file with automake to produce Makefile.in
+
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+
+if OS_WIN32
+mwindows = -mwindows
+endif
+
+if HAVE_WINDRES
+include $(top_srcdir)/build/windows/gimprc-plug-ins.rule
+file_tiff_RC = file-tiff.rc.o
+endif
+
+AM_LDFLAGS = $(mwindows)
+
+libexecdir = $(gimpplugindir)/plug-ins/file-tiff
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(EXIF_CFLAGS) \
+ $(LCMS_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ $(GEXIV2_CFLAGS) \
+ -I$(includedir)
+
+libexec_PROGRAMS = file-tiff
+
+file_tiff_SOURCES = \
+ file-tiff.c \
+ file-tiff-io.c \
+ file-tiff-io.h \
+ file-tiff-load.c \
+ file-tiff-load.h \
+ file-tiff-save.c \
+ file-tiff-save.h
+
+file_tiff_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(TIFF_LIBS) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(GEXIV2_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_tiff_RC)
diff --git a/plug-ins/file-tiff/Makefile.in b/plug-ins/file-tiff/Makefile.in
new file mode 100644
index 0000000..5ad29fd
--- /dev/null
+++ b/plug-ins/file-tiff/Makefile.in
@@ -0,0 +1,1022 @@
+# Makefile.in generated by automake 1.16.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2020 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Version resources for Microsoft Windows
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+libexec_PROGRAMS = file-tiff$(EXEEXT)
+subdir = plug-ins/file-tiff
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/m4macros/alsa.m4 \
+ $(top_srcdir)/m4macros/ax_compare_version.m4 \
+ $(top_srcdir)/m4macros/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4macros/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/m4macros/ax_prog_cc_for_build.m4 \
+ $(top_srcdir)/m4macros/ax_prog_perl_version.m4 \
+ $(top_srcdir)/m4macros/detectcflags.m4 \
+ $(top_srcdir)/m4macros/pythondev.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(libexecdir)"
+PROGRAMS = $(libexec_PROGRAMS)
+am_file_tiff_OBJECTS = file-tiff.$(OBJEXT) file-tiff-io.$(OBJEXT) \
+ file-tiff-load.$(OBJEXT) file-tiff-save.$(OBJEXT)
+file_tiff_OBJECTS = $(am_file_tiff_OBJECTS)
+am__DEPENDENCIES_1 =
+file_tiff_DEPENDENCIES = $(libgimpui) $(libgimpwidgets) \
+ $(libgimpconfig) $(libgimp) $(libgimpcolor) $(libgimpmath) \
+ $(libgimpbase) $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(file_tiff_RC)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/file-tiff-io.Po \
+ ./$(DEPDIR)/file-tiff-load.Po ./$(DEPDIR)/file-tiff-save.Po \
+ ./$(DEPDIR)/file-tiff.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(file_tiff_SOURCES)
+DIST_SOURCES = $(file_tiff_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build/windows/gimprc-plug-ins.rule \
+ $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+AA_LIBS = @AA_LIBS@
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+ALL_LINGUAS = @ALL_LINGUAS@
+ALSA_CFLAGS = @ALSA_CFLAGS@
+ALSA_LIBS = @ALSA_LIBS@
+ALTIVEC_EXTRA_CFLAGS = @ALTIVEC_EXTRA_CFLAGS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+APPSTREAM_UTIL = @APPSTREAM_UTIL@
+AR = @AR@
+AS = @AS@
+ATK_CFLAGS = @ATK_CFLAGS@
+ATK_LIBS = @ATK_LIBS@
+ATK_REQUIRED_VERSION = @ATK_REQUIRED_VERSION@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BABL_CFLAGS = @BABL_CFLAGS@
+BABL_LIBS = @BABL_LIBS@
+BABL_REQUIRED_VERSION = @BABL_REQUIRED_VERSION@
+BUG_REPORT_URL = @BUG_REPORT_URL@
+BUILD_EXEEXT = @BUILD_EXEEXT@
+BUILD_OBJEXT = @BUILD_OBJEXT@
+BZIP2_LIBS = @BZIP2_LIBS@
+CAIRO_CFLAGS = @CAIRO_CFLAGS@
+CAIRO_LIBS = @CAIRO_LIBS@
+CAIRO_PDF_CFLAGS = @CAIRO_PDF_CFLAGS@
+CAIRO_PDF_LIBS = @CAIRO_PDF_LIBS@
+CAIRO_PDF_REQUIRED_VERSION = @CAIRO_PDF_REQUIRED_VERSION@
+CAIRO_REQUIRED_VERSION = @CAIRO_REQUIRED_VERSION@
+CATALOGS = @CATALOGS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASDEPMODE = @CCASDEPMODE@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CC_VERSION = @CC_VERSION@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CPP_FOR_BUILD = @CPP_FOR_BUILD@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DESKTOP_DATADIR = @DESKTOP_DATADIR@
+DESKTOP_FILE_VALIDATE = @DESKTOP_FILE_VALIDATE@
+DLLTOOL = @DLLTOOL@
+DOC_SHOOTER = @DOC_SHOOTER@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILE_AA = @FILE_AA@
+FILE_EXR = @FILE_EXR@
+FILE_HEIF = @FILE_HEIF@
+FILE_JP2_LOAD = @FILE_JP2_LOAD@
+FILE_JPEGXL = @FILE_JPEGXL@
+FILE_MNG = @FILE_MNG@
+FILE_PDF_SAVE = @FILE_PDF_SAVE@
+FILE_PS = @FILE_PS@
+FILE_WMF = @FILE_WMF@
+FILE_XMC = @FILE_XMC@
+FILE_XPM = @FILE_XPM@
+FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
+FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
+FONTCONFIG_REQUIRED_VERSION = @FONTCONFIG_REQUIRED_VERSION@
+FREETYPE2_REQUIRED_VERSION = @FREETYPE2_REQUIRED_VERSION@
+FREETYPE_CFLAGS = @FREETYPE_CFLAGS@
+FREETYPE_LIBS = @FREETYPE_LIBS@
+GDBUS_CODEGEN = @GDBUS_CODEGEN@
+GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@
+GDK_PIXBUF_CSOURCE = @GDK_PIXBUF_CSOURCE@
+GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@
+GDK_PIXBUF_REQUIRED_VERSION = @GDK_PIXBUF_REQUIRED_VERSION@
+GEGL = @GEGL@
+GEGL_CFLAGS = @GEGL_CFLAGS@
+GEGL_LIBS = @GEGL_LIBS@
+GEGL_MAJOR_MINOR_VERSION = @GEGL_MAJOR_MINOR_VERSION@
+GEGL_REQUIRED_VERSION = @GEGL_REQUIRED_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GEXIV2_CFLAGS = @GEXIV2_CFLAGS@
+GEXIV2_LIBS = @GEXIV2_LIBS@
+GEXIV2_REQUIRED_VERSION = @GEXIV2_REQUIRED_VERSION@
+GIMP_API_VERSION = @GIMP_API_VERSION@
+GIMP_APP_VERSION = @GIMP_APP_VERSION@
+GIMP_BINARY_AGE = @GIMP_BINARY_AGE@
+GIMP_COMMAND = @GIMP_COMMAND@
+GIMP_DATA_VERSION = @GIMP_DATA_VERSION@
+GIMP_FULL_NAME = @GIMP_FULL_NAME@
+GIMP_INTERFACE_AGE = @GIMP_INTERFACE_AGE@
+GIMP_MAJOR_VERSION = @GIMP_MAJOR_VERSION@
+GIMP_MICRO_VERSION = @GIMP_MICRO_VERSION@
+GIMP_MINOR_VERSION = @GIMP_MINOR_VERSION@
+GIMP_MKENUMS = @GIMP_MKENUMS@
+GIMP_MODULES = @GIMP_MODULES@
+GIMP_PACKAGE_REVISION = @GIMP_PACKAGE_REVISION@
+GIMP_PKGCONFIG_VERSION = @GIMP_PKGCONFIG_VERSION@
+GIMP_PLUGINS = @GIMP_PLUGINS@
+GIMP_PLUGIN_VERSION = @GIMP_PLUGIN_VERSION@
+GIMP_REAL_VERSION = @GIMP_REAL_VERSION@
+GIMP_RELEASE = @GIMP_RELEASE@
+GIMP_SYSCONF_VERSION = @GIMP_SYSCONF_VERSION@
+GIMP_TOOL_VERSION = @GIMP_TOOL_VERSION@
+GIMP_UNSTABLE = @GIMP_UNSTABLE@
+GIMP_USER_VERSION = @GIMP_USER_VERSION@
+GIMP_VERSION = @GIMP_VERSION@
+GIO_CFLAGS = @GIO_CFLAGS@
+GIO_LIBS = @GIO_LIBS@
+GIO_UNIX_CFLAGS = @GIO_UNIX_CFLAGS@
+GIO_UNIX_LIBS = @GIO_UNIX_LIBS@
+GIO_WINDOWS_CFLAGS = @GIO_WINDOWS_CFLAGS@
+GIO_WINDOWS_LIBS = @GIO_WINDOWS_LIBS@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_COMPILE_RESOURCES = @GLIB_COMPILE_RESOURCES@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GLIB_REQUIRED_VERSION = @GLIB_REQUIRED_VERSION@
+GMODULE_NO_EXPORT_CFLAGS = @GMODULE_NO_EXPORT_CFLAGS@
+GMODULE_NO_EXPORT_LIBS = @GMODULE_NO_EXPORT_LIBS@
+GMOFILES = @GMOFILES@
+GMSGFMT = @GMSGFMT@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GREP = @GREP@
+GS_LIBS = @GS_LIBS@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_CHECK_PATH = @GTKDOC_CHECK_PATH@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GTK_CFLAGS = @GTK_CFLAGS@
+GTK_LIBS = @GTK_LIBS@
+GTK_MAC_INTEGRATION_CFLAGS = @GTK_MAC_INTEGRATION_CFLAGS@
+GTK_MAC_INTEGRATION_LIBS = @GTK_MAC_INTEGRATION_LIBS@
+GTK_REQUIRED_VERSION = @GTK_REQUIRED_VERSION@
+GTK_UPDATE_ICON_CACHE = @GTK_UPDATE_ICON_CACHE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
+HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HARFBUZZ_REQUIRED_VERSION = @HARFBUZZ_REQUIRED_VERSION@
+HAVE_CXX14 = @HAVE_CXX14@
+HAVE_FINITE = @HAVE_FINITE@
+HAVE_ISFINITE = @HAVE_ISFINITE@
+HAVE_VFORK = @HAVE_VFORK@
+HOST_GLIB_COMPILE_RESOURCES = @HOST_GLIB_COMPILE_RESOURCES@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_REQUIRED_VERSION = @INTLTOOL_REQUIRED_VERSION@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISO_CODES_LOCALEDIR = @ISO_CODES_LOCALEDIR@
+ISO_CODES_LOCATION = @ISO_CODES_LOCATION@
+JPEG_LIBS = @JPEG_LIBS@
+JSON_GLIB_CFLAGS = @JSON_GLIB_CFLAGS@
+JSON_GLIB_LIBS = @JSON_GLIB_LIBS@
+JXL_CFLAGS = @JXL_CFLAGS@
+JXL_LIBS = @JXL_LIBS@
+JXL_THREADS_CFLAGS = @JXL_THREADS_CFLAGS@
+JXL_THREADS_LIBS = @JXL_THREADS_LIBS@
+LCMS_CFLAGS = @LCMS_CFLAGS@
+LCMS_LIBS = @LCMS_LIBS@
+LCMS_REQUIRED_VERSION = @LCMS_REQUIRED_VERSION@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBACKTRACE_LIBS = @LIBBACKTRACE_LIBS@
+LIBHEIF_CFLAGS = @LIBHEIF_CFLAGS@
+LIBHEIF_LIBS = @LIBHEIF_LIBS@
+LIBHEIF_REQUIRED_VERSION = @LIBHEIF_REQUIRED_VERSION@
+LIBJXL_REQUIRED_VERSION = @LIBJXL_REQUIRED_VERSION@
+LIBLZMA_REQUIRED_VERSION = @LIBLZMA_REQUIRED_VERSION@
+LIBMYPAINT_CFLAGS = @LIBMYPAINT_CFLAGS@
+LIBMYPAINT_LIBS = @LIBMYPAINT_LIBS@
+LIBMYPAINT_REQUIRED_VERSION = @LIBMYPAINT_REQUIRED_VERSION@
+LIBOBJS = @LIBOBJS@
+LIBPNG_REQUIRED_VERSION = @LIBPNG_REQUIRED_VERSION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@
+LIBUNWIND_LIBS = @LIBUNWIND_LIBS@
+LIBUNWIND_REQUIRED_VERSION = @LIBUNWIND_REQUIRED_VERSION@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_CURRENT_MINUS_AGE = @LT_CURRENT_MINUS_AGE@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+LT_VERSION_INFO = @LT_VERSION_INFO@
+LZMA_CFLAGS = @LZMA_CFLAGS@
+LZMA_LIBS = @LZMA_LIBS@
+MAIL = @MAIL@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MIME_INFO_CFLAGS = @MIME_INFO_CFLAGS@
+MIME_INFO_LIBS = @MIME_INFO_LIBS@
+MIME_TYPES = @MIME_TYPES@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MMX_EXTRA_CFLAGS = @MMX_EXTRA_CFLAGS@
+MNG_CFLAGS = @MNG_CFLAGS@
+MNG_LIBS = @MNG_LIBS@
+MSGFMT = @MSGFMT@
+MSGFMT_OPTS = @MSGFMT_OPTS@
+MSGMERGE = @MSGMERGE@
+MYPAINT_BRUSHES_CFLAGS = @MYPAINT_BRUSHES_CFLAGS@
+MYPAINT_BRUSHES_LIBS = @MYPAINT_BRUSHES_LIBS@
+NATIVE_GLIB_CFLAGS = @NATIVE_GLIB_CFLAGS@
+NATIVE_GLIB_LIBS = @NATIVE_GLIB_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENEXR_CFLAGS = @OPENEXR_CFLAGS@
+OPENEXR_LIBS = @OPENEXR_LIBS@
+OPENEXR_REQUIRED_VERSION = @OPENEXR_REQUIRED_VERSION@
+OPENJPEG_CFLAGS = @OPENJPEG_CFLAGS@
+OPENJPEG_LIBS = @OPENJPEG_LIBS@
+OPENJPEG_REQUIRED_VERSION = @OPENJPEG_REQUIRED_VERSION@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PANGOCAIRO_CFLAGS = @PANGOCAIRO_CFLAGS@
+PANGOCAIRO_LIBS = @PANGOCAIRO_LIBS@
+PANGOCAIRO_REQUIRED_VERSION = @PANGOCAIRO_REQUIRED_VERSION@
+PATHSEP = @PATHSEP@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PERL_REQUIRED_VERSION = @PERL_REQUIRED_VERSION@
+PERL_VERSION = @PERL_VERSION@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+PNG_CFLAGS = @PNG_CFLAGS@
+PNG_LIBS = @PNG_LIBS@
+POFILES = @POFILES@
+POPPLER_CFLAGS = @POPPLER_CFLAGS@
+POPPLER_DATA_CFLAGS = @POPPLER_DATA_CFLAGS@
+POPPLER_DATA_LIBS = @POPPLER_DATA_LIBS@
+POPPLER_DATA_REQUIRED_VERSION = @POPPLER_DATA_REQUIRED_VERSION@
+POPPLER_LIBS = @POPPLER_LIBS@
+POPPLER_REQUIRED_VERSION = @POPPLER_REQUIRED_VERSION@
+POSUB = @POSUB@
+PO_IN_DATADIR_FALSE = @PO_IN_DATADIR_FALSE@
+PO_IN_DATADIR_TRUE = @PO_IN_DATADIR_TRUE@
+PYBIN_PATH = @PYBIN_PATH@
+PYCAIRO_CFLAGS = @PYCAIRO_CFLAGS@
+PYCAIRO_LIBS = @PYCAIRO_LIBS@
+PYGIMP_EXTRA_CFLAGS = @PYGIMP_EXTRA_CFLAGS@
+PYGTK_CFLAGS = @PYGTK_CFLAGS@
+PYGTK_CODEGEN = @PYGTK_CODEGEN@
+PYGTK_DEFSDIR = @PYGTK_DEFSDIR@
+PYGTK_LIBS = @PYGTK_LIBS@
+PYLINK_LIBS = @PYLINK_LIBS@
+PYTHON = @PYTHON@
+PYTHON2_REQUIRED_VERSION = @PYTHON2_REQUIRED_VERSION@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_INCLUDES = @PYTHON_INCLUDES@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RSVG_REQUIRED_VERSION = @RSVG_REQUIRED_VERSION@
+RT_LIBS = @RT_LIBS@
+SCREENSHOT_LIBS = @SCREENSHOT_LIBS@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SOCKET_LIBS = @SOCKET_LIBS@
+SSE2_EXTRA_CFLAGS = @SSE2_EXTRA_CFLAGS@
+SSE4_1_EXTRA_CFLAGS = @SSE4_1_EXTRA_CFLAGS@
+SSE_EXTRA_CFLAGS = @SSE_EXTRA_CFLAGS@
+STRIP = @STRIP@
+SVG_CFLAGS = @SVG_CFLAGS@
+SVG_LIBS = @SVG_LIBS@
+SYMPREFIX = @SYMPREFIX@
+TIFF_LIBS = @TIFF_LIBS@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
+WEBKIT_LIBS = @WEBKIT_LIBS@
+WEBKIT_REQUIRED_VERSION = @WEBKIT_REQUIRED_VERSION@
+WEBPDEMUX_CFLAGS = @WEBPDEMUX_CFLAGS@
+WEBPDEMUX_LIBS = @WEBPDEMUX_LIBS@
+WEBPMUX_CFLAGS = @WEBPMUX_CFLAGS@
+WEBPMUX_LIBS = @WEBPMUX_LIBS@
+WEBP_CFLAGS = @WEBP_CFLAGS@
+WEBP_LIBS = @WEBP_LIBS@
+WEBP_REQUIRED_VERSION = @WEBP_REQUIRED_VERSION@
+WEB_PAGE = @WEB_PAGE@
+WIN32_LARGE_ADDRESS_AWARE = @WIN32_LARGE_ADDRESS_AWARE@
+WINDRES = @WINDRES@
+WMF_CFLAGS = @WMF_CFLAGS@
+WMF_CONFIG = @WMF_CONFIG@
+WMF_LIBS = @WMF_LIBS@
+WMF_REQUIRED_VERSION = @WMF_REQUIRED_VERSION@
+XDG_EMAIL = @XDG_EMAIL@
+XFIXES_CFLAGS = @XFIXES_CFLAGS@
+XFIXES_LIBS = @XFIXES_LIBS@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_REQUIRED_VERSION = @XGETTEXT_REQUIRED_VERSION@
+XMC_CFLAGS = @XMC_CFLAGS@
+XMC_LIBS = @XMC_LIBS@
+XMKMF = @XMKMF@
+XMLLINT = @XMLLINT@
+XMU_LIBS = @XMU_LIBS@
+XPM_LIBS = @XPM_LIBS@
+XSLTPROC = @XSLTPROC@
+XVFB_RUN = @XVFB_RUN@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+Z_LIBS = @Z_LIBS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CC_FOR_BUILD = @ac_ct_CC_FOR_BUILD@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+gimpdatadir = @gimpdatadir@
+gimpdir = @gimpdir@
+gimplocaledir = @gimplocaledir@
+gimpplugindir = @gimpplugindir@
+gimpsysconfdir = @gimpsysconfdir@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = $(gimpplugindir)/plug-ins/file-tiff
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+manpage_gimpdir = @manpage_gimpdir@
+mkdir_p = @mkdir_p@
+ms_librarian = @ms_librarian@
+mypaint_brushes_dir = @mypaint_brushes_dir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+libgimpui = $(top_builddir)/libgimp/libgimpui-$(GIMP_API_VERSION).la
+libgimpconfig = $(top_builddir)/libgimpconfig/libgimpconfig-$(GIMP_API_VERSION).la
+libgimpwidgets = $(top_builddir)/libgimpwidgets/libgimpwidgets-$(GIMP_API_VERSION).la
+libgimp = $(top_builddir)/libgimp/libgimp-$(GIMP_API_VERSION).la
+libgimpcolor = $(top_builddir)/libgimpcolor/libgimpcolor-$(GIMP_API_VERSION).la
+libgimpbase = $(top_builddir)/libgimpbase/libgimpbase-$(GIMP_API_VERSION).la
+libgimpmath = $(top_builddir)/libgimpmath/libgimpmath-$(GIMP_API_VERSION).la
+@OS_WIN32_TRUE@mwindows = -mwindows
+@HAVE_WINDRES_TRUE@GIMPPLUGINRC = $(top_builddir)/build/windows/gimp-plug-ins.rc
+@HAVE_WINDRES_TRUE@file_tiff_RC = file-tiff.rc.o
+AM_LDFLAGS = $(mwindows)
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(EXIF_CFLAGS) \
+ $(LCMS_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ $(GEXIV2_CFLAGS) \
+ -I$(includedir)
+
+file_tiff_SOURCES = \
+ file-tiff.c \
+ file-tiff-io.c \
+ file-tiff-io.h \
+ file-tiff-load.c \
+ file-tiff-load.h \
+ file-tiff-save.c \
+ file-tiff-save.h
+
+file_tiff_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(TIFF_LIBS) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(GEXIV2_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_tiff_RC)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu plug-ins/file-tiff/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/file-tiff/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+$(top_srcdir)/build/windows/gimprc-plug-ins.rule $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+file-tiff$(EXEEXT): $(file_tiff_OBJECTS) $(file_tiff_DEPENDENCIES) $(EXTRA_file_tiff_DEPENDENCIES)
+ @rm -f file-tiff$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_tiff_OBJECTS) $(file_tiff_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-tiff-io.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-tiff-load.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-tiff-save.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file-tiff.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(libexecdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libexecPROGRAMS clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/file-tiff-io.Po
+ -rm -f ./$(DEPDIR)/file-tiff-load.Po
+ -rm -f ./$(DEPDIR)/file-tiff-save.Po
+ -rm -f ./$(DEPDIR)/file-tiff.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-libexecPROGRAMS
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/file-tiff-io.Po
+ -rm -f ./$(DEPDIR)/file-tiff-load.Po
+ -rm -f ./$(DEPDIR)/file-tiff-save.Po
+ -rm -f ./$(DEPDIR)/file-tiff.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-libexecPROGRAMS
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libexecPROGRAMS clean-libtool \
+ cscopelist-am ctags ctags-am distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-libexecPROGRAMS \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-libexecPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# `windres` seems a very stupid tool and it breaks with double shlashes
+# in parameter paths. Strengthen the rule a little.
+@HAVE_WINDRES_TRUE@%.rc.o:
+@HAVE_WINDRES_TRUE@ $(WINDRES) --define ORIGINALFILENAME_STR="$*$(EXEEXT)" \
+@HAVE_WINDRES_TRUE@ --define INTERNALNAME_STR="$*" \
+@HAVE_WINDRES_TRUE@ --define TOP_SRCDIR="`echo $(top_srcdir) | sed 's*//*/*'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_srcdir)/app | sed 's%/\+%/%'`" \
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir)/app | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ -I"`echo $(top_builddir) | sed 's%/\+%/%'`"\
+@HAVE_WINDRES_TRUE@ $(GIMPPLUGINRC) $@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/plug-ins/file-tiff/file-tiff-io.c b/plug-ins/file-tiff/file-tiff-io.c
new file mode 100644
index 0000000..09ef81f
--- /dev/null
+++ b/plug-ins/file-tiff/file-tiff-io.c
@@ -0,0 +1,585 @@
+/* tiff loading for GIMP
+ * -Peter Mattis
+ *
+ * The TIFF loading code has been completely revamped by Nick Lamb
+ * njl195@zepler.org.uk -- 18 May 1998
+ * And it now gains support for tiles (and doubtless a zillion bugs)
+ * njl195@zepler.org.uk -- 12 June 1999
+ * LZW patent fuss continues :(
+ * njl195@zepler.org.uk -- 20 April 2000
+ * The code for this filter is based on "tifftopnm" and "pnmtotiff",
+ * 2 programs that are a part of the netpbm package.
+ * khk@khk.net -- 13 May 2000
+ * Added support for ICCPROFILE tiff tag. If this tag is present in a
+ * TIFF file, then a parasite is created and vice versa.
+ * peter@kirchgessner.net -- 29 Oct 2002
+ * Progress bar only when run interactive
+ * Added support for layer offsets - pablo.dangelo@web.de -- 7 Jan 2004
+ * Honor EXTRASAMPLES tag while loading images with alphachannel
+ * pablo.dangelo@web.de -- 16 Jan 2004
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <tiffio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "file-tiff-io.h"
+
+static gboolean tiff_file_size_error = FALSE;
+
+typedef struct
+{
+ GFile *file;
+ GObject *stream;
+ GInputStream *input;
+ GOutputStream *output;
+ gboolean can_seek;
+
+ gchar *buffer;
+ gsize allocated;
+ gsize used;
+ gsize position;
+} TiffIO;
+
+
+static TIFFExtendProc parent_extender;
+
+static void tiff_io_warning (const gchar *module,
+ const gchar *fmt,
+ va_list ap) G_GNUC_PRINTF (2, 0);
+static void tiff_io_error (const gchar *module,
+ const gchar *fmt,
+ va_list ap) G_GNUC_PRINTF (2, 0);
+static tsize_t tiff_io_read (thandle_t handle,
+ tdata_t buffer,
+ tsize_t size);
+static tsize_t tiff_io_write (thandle_t handle,
+ tdata_t buffer,
+ tsize_t size);
+static toff_t tiff_io_seek (thandle_t handle,
+ toff_t offset,
+ gint whence);
+static gint tiff_io_close (thandle_t handle);
+static toff_t tiff_io_get_file_size (thandle_t handle);
+static void register_geotags (TIFF *tif);
+
+static void
+register_geotags (TIFF *tif)
+{
+ static gboolean geotifftags_registered = FALSE;
+
+ if (geotifftags_registered)
+ return;
+
+ geotifftags_registered = TRUE;
+
+ TIFFMergeFieldInfo (tif, geotifftags_fieldinfo, (sizeof (geotifftags_fieldinfo) / sizeof (geotifftags_fieldinfo[0])));
+
+ if (parent_extender)
+ (*parent_extender) (tif);
+}
+
+static TiffIO tiff_io = { 0, };
+
+
+TIFF *
+tiff_open (GFile *file,
+ const gchar *mode,
+ GError **error)
+{
+ TIFFSetWarningHandler ((TIFFErrorHandler) tiff_io_warning);
+ TIFFSetErrorHandler ((TIFFErrorHandler) tiff_io_error);
+
+ parent_extender = TIFFSetTagExtender (register_geotags);
+
+ tiff_io.file = file;
+
+ if (! strcmp (mode, "r"))
+ {
+ tiff_io.input = G_INPUT_STREAM (g_file_read (file, NULL, error));
+ if (! tiff_io.input)
+ return NULL;
+
+ tiff_io.stream = G_OBJECT (tiff_io.input);
+ }
+#ifdef TIFF_VERSION_BIG
+ else if(! strcmp (mode, "w") || ! strcmp (mode, "w8"))
+#else
+ else if(! strcmp (mode, "w"))
+#endif
+ {
+ tiff_io.output = G_OUTPUT_STREAM (g_file_replace (file,
+ NULL, FALSE,
+ G_FILE_CREATE_NONE,
+ NULL, error));
+ if (! tiff_io.output)
+ return NULL;
+
+ tiff_io.stream = G_OBJECT (tiff_io.output);
+ }
+ else if(! strcmp (mode, "a"))
+ {
+ GIOStream *iostream = G_IO_STREAM (g_file_open_readwrite (file, NULL,
+ error));
+ if (! iostream)
+ return NULL;
+
+ tiff_io.input = g_io_stream_get_input_stream (iostream);
+ tiff_io.output = g_io_stream_get_output_stream (iostream);
+ tiff_io.stream = G_OBJECT (iostream);
+ }
+ else
+ {
+ g_assert_not_reached ();
+ }
+
+#if 0
+#warning FIXME !can_seek code is broken
+ tiff_io.can_seek = g_seekable_can_seek (G_SEEKABLE (tiff_io.stream));
+#endif
+ tiff_io.can_seek = TRUE;
+
+ return TIFFClientOpen ("file-tiff", mode,
+ (thandle_t) &tiff_io,
+ tiff_io_read,
+ tiff_io_write,
+ tiff_io_seek,
+ tiff_io_close,
+ tiff_io_get_file_size,
+ NULL, NULL);
+}
+
+gboolean
+tiff_got_file_size_error (void)
+{
+ return tiff_file_size_error;
+}
+
+void
+tiff_reset_file_size_error (void)
+{
+ tiff_file_size_error = FALSE;
+}
+
+static void
+tiff_io_warning (const gchar *module,
+ const gchar *fmt,
+ va_list ap)
+{
+ gint tag = 0;
+
+ /* Between libtiff 3.7.0beta2 and 4.0.0alpha. */
+ if (! strcmp (fmt, "%s: unknown field with tag %d (0x%x) encountered") ||
+ /* Before libtiff 3.7.0beta2. */
+ ! strcmp (fmt, "%.1000s: unknown field with tag %d (0x%x) encountered"))
+ {
+ va_list ap_test;
+
+ G_VA_COPY (ap_test, ap);
+
+ va_arg (ap_test, const char *); /* ignore first arg */
+
+ tag = va_arg (ap_test, int);
+
+ va_end (ap_test);
+ }
+ /* for older versions of libtiff? */
+ else if (! strcmp (fmt, "unknown field with tag %d (0x%x) ignored") ||
+ /* Since libtiff 4.0.0alpha. */
+ ! strcmp (fmt, "Unknown field with tag %d (0x%x) encountered") ||
+ /* Since libtiff 4.3.0rc1. */
+ ! strcmp (fmt, "Unknown field with tag %u (0x%x) encountered"))
+ {
+ va_list ap_test;
+
+ G_VA_COPY (ap_test, ap);
+
+ tag = va_arg (ap_test, int);
+
+ va_end (ap_test);
+ }
+ else if (! strcmp (fmt, "Incorrect value for \"%s\"; tag ignored"))
+ {
+ va_list ap_test;
+ const char *stag;
+
+ G_VA_COPY (ap_test, ap);
+
+ stag = va_arg (ap_test, const char *);
+
+ if (! strcmp (stag, "RichTIFFIPTC"))
+ {
+ gchar *msg = g_strdup_vprintf (fmt, ap);
+
+ /* This is an error in Adobe products. Just report to stderr. */
+ g_printerr ("[%s] %s\n", module, msg);
+ g_free (msg);
+
+ return;
+ }
+
+ va_end (ap_test);
+ }
+
+ /* Workaround for: http://bugzilla.gnome.org/show_bug.cgi?id=131975
+ * Ignore the warnings about unregistered private tags (>= 32768).
+ */
+ if (tag >= 32768)
+ return;
+
+ /* Other unknown fields are only reported to stderr. */
+ if (tag > 0)
+ {
+ gchar *msg = g_strdup_vprintf (fmt, ap);
+
+ g_printerr ("%s\n", msg);
+ g_free (msg);
+
+ return;
+ }
+ else if (! strcmp (module, "TIFFReadDirectory") &&
+ ! strcmp (fmt,
+ "Sum of Photometric type-related color channels and ExtraSamples doesn't match SamplesPerPixel."
+ " Defining non-color channels as ExtraSamples."))
+ {
+ /* We will process this issue in our code. Just report to stderr. */
+ g_printerr ("%s: [%s] %s\n", G_STRFUNC, module, fmt);
+
+ return;
+ }
+ else if (! strcmp (module, "Fax4Decode") ||
+ g_str_has_prefix (module, "Fax3Decode"))
+ {
+ /* Certain corrupt TIFF Fax images can produce a large amount of
+ * warnings which can cause GIMP to run out of GDI resources on
+ * Windows and eventually crash.
+ * The real problem seems to be that the amount of error console
+ * messages does not have a limit.
+ * See e.g. the first page of m1-8110934bb3b18d0e87ccc1ddfc5f0107.tif
+ * from imagetestsuite. LibTiff does not return -1 from
+ * ReadScanline, presumably because for fax images it's not
+ * unreasonable to expect certain lines to fail.
+ * Let's just only report to stderr in this case. */
+ gchar *msg = g_strdup_vprintf (fmt, ap);
+
+ g_printerr ("LibTiff warning: [%s] %s\n", module, msg);
+ g_free (msg);
+
+ return;
+ }
+
+ g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, ap);
+}
+
+static void
+tiff_io_error (const gchar *module,
+ const gchar *fmt,
+ va_list ap)
+{
+ gchar *msg;
+
+ /* Workaround for: http://bugzilla.gnome.org/show_bug.cgi?id=132297
+ * Ignore the errors related to random access and JPEG compression
+ */
+ if (! strcmp (fmt, "Compression algorithm does not support random access"))
+ return;
+
+ msg = g_strdup_vprintf (fmt, ap);
+
+#ifdef TIFF_VERSION_BIG
+ if (g_strcmp0 (fmt, "Maximum TIFF file size exceeded") == 0)
+ /* @module in my tests were "TIFFAppendToStrip" but I wonder if
+ * this same error could not happen with other "modules".
+ */
+ tiff_file_size_error = TRUE;
+ else
+ /* Easier for debugging to at least print messages on stderr. */
+ g_printerr ("LibTiff error: [%s] %s\n", module, msg);
+#endif
+
+ g_log (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, "%s", msg);
+ g_free (msg);
+}
+
+static tsize_t
+tiff_io_read (thandle_t handle,
+ tdata_t buffer,
+ tsize_t size)
+{
+ TiffIO *io = (TiffIO *) handle;
+ GError *error = NULL;
+ gssize read = -1;
+
+ if (io->can_seek)
+ {
+ gsize bytes_read = 0;
+
+ if (! g_input_stream_read_all (io->input,
+ (void *) buffer, (gsize) size,
+ &bytes_read,
+ NULL, &error))
+ {
+ g_printerr ("%s", error->message);
+ g_clear_error (&error);
+ }
+
+ read = bytes_read;
+ }
+ else
+ {
+ if (io->position + size > io->used)
+ {
+ gsize missing;
+ gsize bytes_read;
+
+ missing = io->position + size - io->used;
+
+ if (io->used + missing > io->allocated)
+ {
+ gchar *new_buffer;
+ gsize new_size = 1;
+ gsize needed;
+
+ needed = io->used + missing - io->allocated;
+ while (new_size < io->allocated + needed)
+ new_size *= 2;
+
+ new_buffer = g_try_realloc (io->buffer, new_size);
+ if (! new_buffer)
+ return -1;
+
+ io->buffer = new_buffer;
+ io->allocated = new_size;
+ }
+
+ if (! g_input_stream_read_all (io->input,
+ (void *) (io->buffer + io->used),
+ missing,
+ &bytes_read, NULL, &error))
+ {
+ g_printerr ("%s", error->message);
+ g_clear_error (&error);
+ }
+
+ io->used += bytes_read;
+ }
+
+ g_assert (io->position + size <= io->used);
+
+ memcpy (buffer, io->buffer + io->position, size);
+ io->position += size;
+
+ read = size;
+ }
+
+ return (tsize_t) read;
+}
+
+static tsize_t
+tiff_io_write (thandle_t handle,
+ tdata_t buffer,
+ tsize_t size)
+{
+ TiffIO *io = (TiffIO *) handle;
+ GError *error = NULL;
+ gssize written = -1;
+
+ if (io->can_seek)
+ {
+ gsize bytes_written = 0;
+
+ if (! g_output_stream_write_all (io->output,
+ (void *) buffer, (gsize) size,
+ &bytes_written,
+ NULL, &error))
+ {
+ g_printerr ("%s", error->message);
+ g_clear_error (&error);
+ }
+
+ written = bytes_written;
+ }
+ else
+ {
+ if (io->position + size > io->allocated)
+ {
+ gchar *new_buffer;
+ gsize new_size;
+
+ new_size = io->position + size;
+
+ new_buffer = g_try_realloc (io->buffer, new_size);
+ if (! new_buffer)
+ return -1;
+
+ io->buffer = new_buffer;
+ io->allocated = new_size;
+ }
+
+ g_assert (io->position + size <= io->allocated);
+
+ memcpy (io->buffer + io->position, buffer, size);
+ io->position += size;
+
+ io->used = MAX (io->used, io->position);
+
+ written = size;
+ }
+
+ return (tsize_t) written;
+}
+
+static GSeekType
+lseek_to_seek_type (gint whence)
+{
+ switch (whence)
+ {
+ default:
+ case SEEK_SET:
+ return G_SEEK_SET;
+
+ case SEEK_CUR:
+ return G_SEEK_CUR;
+
+ case SEEK_END:
+ return G_SEEK_END;
+ }
+}
+
+static toff_t
+tiff_io_seek (thandle_t handle,
+ toff_t offset,
+ gint whence)
+{
+ TiffIO *io = (TiffIO *) handle;
+ GError *error = NULL;
+ gboolean sought = FALSE;
+ goffset position = -1;
+
+ if (io->can_seek)
+ {
+ sought = g_seekable_seek (G_SEEKABLE (io->stream),
+ (goffset) offset, lseek_to_seek_type (whence),
+ NULL, &error);
+ if (sought)
+ {
+ position = g_seekable_tell (G_SEEKABLE (io->stream));
+ }
+ else
+ {
+ g_printerr ("%s", error->message);
+ g_clear_error (&error);
+ }
+ }
+ else
+ {
+ switch (whence)
+ {
+ default:
+ case SEEK_SET:
+ if (offset <= io->used)
+ position = io->position = offset;
+ break;
+
+ case SEEK_CUR:
+ if (io->position + offset <= io->used)
+ position = io->position += offset;
+ break;
+
+ case G_SEEK_END:
+ if (io->used + offset <= io->used)
+ position = io->position = io->used + offset;
+ break;
+ }
+ }
+
+ return (toff_t) position;
+}
+
+static gint
+tiff_io_close (thandle_t handle)
+{
+ TiffIO *io = (TiffIO *) handle;
+ GError *error = NULL;
+ gboolean closed = FALSE;
+
+ if (io->input && ! io->output)
+ {
+ closed = g_input_stream_close (io->input, NULL, &error);
+ }
+ else
+ {
+ if (! io->can_seek && io->buffer && io->allocated)
+ {
+ if (! g_output_stream_write_all (io->output,
+ (void *) io->buffer,
+ io->allocated,
+ NULL, NULL, &error))
+ {
+ g_printerr ("%s", error->message);
+ g_clear_error (&error);
+ }
+ }
+
+ if (io->input)
+ {
+ closed = g_io_stream_close (G_IO_STREAM (io->stream), NULL, &error);
+ }
+ else
+ {
+ closed = g_output_stream_close (io->output, NULL, &error);
+ }
+ }
+
+ if (! closed)
+ {
+ g_printerr ("%s", error->message);
+ g_clear_error (&error);
+ }
+
+ g_object_unref (io->stream);
+ io->stream = NULL;
+ io->input = NULL;
+ io->output = NULL;
+
+ g_free (io->buffer);
+ io->buffer = NULL;
+
+ io->allocated = 0;
+ io->used = 0;
+ io->position = 0;
+
+ return closed ? 0 : -1;
+}
+
+static toff_t
+tiff_io_get_file_size (thandle_t handle)
+{
+ TiffIO *io = (TiffIO *) handle;
+ GError *error = NULL;
+ GFileInfo *info;
+ goffset size = 0;
+
+ info = g_file_query_info (io->file,
+ G_FILE_ATTRIBUTE_STANDARD_SIZE,
+ G_FILE_QUERY_INFO_NONE,
+ NULL, &error);
+ if (! info)
+ {
+ g_printerr ("%s", error->message);
+ g_clear_error (&error);
+ }
+ else
+ {
+ size = g_file_info_get_size (info);
+ g_object_unref (info);
+ }
+
+ return (toff_t) size;
+}
diff --git a/plug-ins/file-tiff/file-tiff-io.h b/plug-ins/file-tiff/file-tiff-io.h
new file mode 100644
index 0000000..9f17e48
--- /dev/null
+++ b/plug-ins/file-tiff/file-tiff-io.h
@@ -0,0 +1,50 @@
+/* tiff loading for GIMP
+ * -Peter Mattis
+ *
+ * The TIFF loading code has been completely revamped by Nick Lamb
+ * njl195@zepler.org.uk -- 18 May 1998
+ * And it now gains support for tiles (and doubtless a zillion bugs)
+ * njl195@zepler.org.uk -- 12 June 1999
+ * LZW patent fuss continues :(
+ * njl195@zepler.org.uk -- 20 April 2000
+ * The code for this filter is based on "tifftopnm" and "pnmtotiff",
+ * 2 programs that are a part of the netpbm package.
+ * khk@khk.net -- 13 May 2000
+ * Added support for ICCPROFILE tiff tag. If this tag is present in a
+ * TIFF file, then a parasite is created and vice versa.
+ * peter@kirchgessner.net -- 29 Oct 2002
+ * Progress bar only when run interactive
+ * Added support for layer offsets - pablo.dangelo@web.de -- 7 Jan 2004
+ * Honor EXTRASAMPLES tag while loading images with alphachannel
+ * pablo.dangelo@web.de -- 16 Jan 2004
+ */
+
+#ifndef __FILE_TIFF_IO_H__
+#define __FILE_TIFF_IO_H__
+
+/* Adding support for GeoTIFF Tags */
+
+#define GEOTIFF_MODELPIXELSCALE 33550
+#define GEOTIFF_MODELTIEPOINT 33922
+#define GEOTIFF_MODELTRANSFORMATION 34264
+#define GEOTIFF_KEYDIRECTORY 34735
+#define GEOTIFF_DOUBLEPARAMS 34736
+#define GEOTIFF_ASCIIPARAMS 34737
+
+static const TIFFFieldInfo geotifftags_fieldinfo[] = {
+ { GEOTIFF_MODELPIXELSCALE, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, "GeoModelPixelScale" },
+ { GEOTIFF_MODELTIEPOINT, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, "GeoTiePoints" },
+ { GEOTIFF_MODELTRANSFORMATION, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, "GeoModelTransformation" },
+ { GEOTIFF_KEYDIRECTORY, -1, -1, TIFF_SHORT, FIELD_CUSTOM, TRUE, TRUE, "GeoKeyDirectory" },
+ { GEOTIFF_DOUBLEPARAMS, -1, -1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, "GeoDoubleParams" },
+ { GEOTIFF_ASCIIPARAMS, -1, -1, TIFF_ASCII, FIELD_CUSTOM, TRUE, FALSE, "GeoAsciiParams" }
+};
+
+TIFF * tiff_open (GFile *file,
+ const gchar *mode,
+ GError **error);
+
+gboolean tiff_got_file_size_error (void);
+void tiff_reset_file_size_error (void);
+
+#endif /* __FILE_TIFF_IO_H__ */
diff --git a/plug-ins/file-tiff/file-tiff-load.c b/plug-ins/file-tiff/file-tiff-load.c
new file mode 100644
index 0000000..72e9538
--- /dev/null
+++ b/plug-ins/file-tiff/file-tiff-load.c
@@ -0,0 +1,2815 @@
+/* tiff loading for GIMP
+ * -Peter Mattis
+ *
+ * The TIFF loading code has been completely revamped by Nick Lamb
+ * njl195@zepler.org.uk -- 18 May 1998
+ * And it now gains support for tiles (and doubtless a zillion bugs)
+ * njl195@zepler.org.uk -- 12 June 1999
+ * LZW patent fuss continues :(
+ * njl195@zepler.org.uk -- 20 April 2000
+ * The code for this filter is based on "tifftopnm" and "pnmtotiff",
+ * 2 programs that are a part of the netpbm package.
+ * khk@khk.net -- 13 May 2000
+ * Added support for ICCPROFILE tiff tag. If this tag is present in a
+ * TIFF file, then a parasite is created and vice versa.
+ * peter@kirchgessner.net -- 29 Oct 2002
+ * Progress bar only when run interactive
+ * Added support for layer offsets - pablo.dangelo@web.de -- 7 Jan 2004
+ * Honor EXTRASAMPLES tag while loading images with alphachannel
+ * pablo.dangelo@web.de -- 16 Jan 2004
+ */
+
+/*
+ * tifftopnm.c - converts a Tagged Image File to a portable anymap
+ *
+ * Derived by Jef Poskanzer from tif2ras.c, which is:
+ *
+ * Copyright (c) 1990 by Sun Microsystems, Inc.
+ *
+ * Author: Patrick J. Naughton
+ * naughton@wind.sun.com
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation.
+ *
+ * This file is provided AS IS with no warranties of any kind. The author
+ * shall have no liability with respect to the infringement of copyrights,
+ * trade secrets or any patents by this file or any part thereof. In no
+ * event will the author be liable for any lost revenue or profits or
+ * other special, indirect and consequential damages.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <tiffio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "file-tiff-io.h"
+#include "file-tiff-load.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_ROLE "gimp-file-tiff-load"
+
+
+typedef struct
+{
+ gint compression;
+ gint fillorder;
+ gboolean save_transp_pixels;
+} TiffSaveVals;
+
+typedef struct
+{
+ gint32 ID;
+ GeglBuffer *buffer;
+ const Babl *format;
+ guchar *pixels;
+ guchar *pixel;
+} ChannelData;
+
+typedef enum
+{
+ GIMP_TIFF_LOAD_ASSOCALPHA,
+ GIMP_TIFF_LOAD_UNASSALPHA,
+ GIMP_TIFF_LOAD_CHANNEL
+} DefaultExtra;
+
+typedef enum
+{
+ GIMP_TIFF_DEFAULT,
+ GIMP_TIFF_INDEXED,
+ GIMP_TIFF_GRAY,
+ GIMP_TIFF_GRAY_MINISWHITE,
+} TiffColorMode;
+
+/* Declare some local functions */
+
+static GimpColorProfile * load_profile (TIFF *tif);
+
+static void load_rgba (TIFF *tif,
+ ChannelData *channel);
+static void load_contiguous (TIFF *tif,
+ ChannelData *channel,
+ const Babl *type,
+ gushort bps,
+ gushort spp,
+ TiffColorMode tiff_mode,
+ gboolean is_signed,
+ gint extra);
+static void load_separate (TIFF *tif,
+ ChannelData *channel,
+ const Babl *type,
+ gushort bps,
+ gushort spp,
+ TiffColorMode tiff_mode,
+ gboolean is_signed,
+ gint extra);
+static void load_paths (TIFF *tif,
+ gint image,
+ gint width,
+ gint height,
+ gint offset_x,
+ gint offset_y);
+
+static gboolean is_non_conformant_tiff (gushort photomet,
+ gushort spp);
+static gushort get_extra_channels_count (gushort photomet,
+ gushort spp,
+ gboolean alpha);
+
+static void fill_bit2byte (TiffColorMode tiff_mode);
+static void fill_2bit2byte (TiffColorMode tiff_mode);
+static void fill_4bit2byte (TiffColorMode tiff_mode);
+static void convert_bit2byte (const guchar *src,
+ guchar *dest,
+ gint width,
+ gint height);
+static void convert_2bit2byte (const guchar *src,
+ guchar *dest,
+ gint width,
+ gint height);
+static void convert_4bit2byte (const guchar *src,
+ guchar *dest,
+ gint width,
+ gint height);
+
+static void convert_miniswhite (guchar *buffer,
+ gint width,
+ gint height);
+static void convert_int2uint (guchar *buffer,
+ gint bps,
+ gint spp,
+ gint width,
+ gint height,
+ gint stride);
+
+static gboolean load_dialog (const gchar *help_id,
+ TiffSelectedPages *pages,
+ const gchar *extra_message,
+ DefaultExtra *default_extra);
+
+static void tiff_dialog_show_reduced (GtkWidget *toggle,
+ gpointer data);
+
+
+static TiffSaveVals tsvals =
+{
+ COMPRESSION_NONE, /* compression */
+ TRUE, /* alpha handling */
+};
+
+/* Grayscale conversion mappings */
+static const guchar _1_to_8_bitmap [2] =
+{
+ 0, 255
+};
+
+static const guchar _1_to_8_bitmap_rev [2] =
+{
+ 255, 0
+};
+
+static const guchar _2_to_8_bitmap [4] =
+{
+ 0, 85, 170, 255
+};
+
+static const guchar _2_to_8_bitmap_rev [4] =
+{
+ 255, 170, 85, 0
+};
+
+static const guchar _4_to_8_bitmap [16] =
+{
+ 0, 17, 34, 51, 68, 85, 102, 119,
+ 136, 153, 170, 187, 204, 221, 238, 255
+};
+
+static const guchar _4_to_8_bitmap_rev [16] =
+{
+ 255, 238, 221, 204, 187, 170, 153, 136,
+ 119, 102, 85, 68, 51, 34, 17, 0
+};
+
+static guchar bit2byte[256 * 8];
+static guchar _2bit2byte[256 * 4];
+static guchar _4bit2byte[256 * 2];
+
+
+/* returns a pointer into the TIFF */
+static const gchar *
+tiff_get_page_name (TIFF *tif)
+{
+ static gchar *name;
+
+ if (TIFFGetField (tif, TIFFTAG_PAGENAME, &name) &&
+ g_utf8_validate (name, -1, NULL))
+ {
+ return name;
+ }
+
+ return NULL;
+}
+
+/* is_non_conformant_tiff assumes TIFFTAG_EXTRASAMPLES was not set */
+static gboolean
+is_non_conformant_tiff (gushort photomet, gushort spp)
+{
+ switch (photomet)
+ {
+ case PHOTOMETRIC_RGB:
+ case PHOTOMETRIC_YCBCR:
+ case PHOTOMETRIC_CIELAB:
+ case PHOTOMETRIC_ICCLAB:
+ case PHOTOMETRIC_ITULAB:
+ case PHOTOMETRIC_LOGLUV:
+ return (spp > 3 || (spp == 2 && photomet != PHOTOMETRIC_RGB));
+ break;
+ case PHOTOMETRIC_SEPARATED:
+ return (spp > 4);
+ break;
+ default:
+ return (spp > 1);
+ break;
+ }
+}
+
+/* get_extra_channels_count returns number of channels excluding
+ * alpha and color channels
+ */
+static gushort
+get_extra_channels_count (gushort photomet, gushort spp, gboolean alpha)
+{
+ switch (photomet)
+ {
+ case PHOTOMETRIC_RGB:
+ case PHOTOMETRIC_YCBCR:
+ case PHOTOMETRIC_CIELAB:
+ case PHOTOMETRIC_ICCLAB:
+ case PHOTOMETRIC_ITULAB:
+ case PHOTOMETRIC_LOGLUV:
+ if (spp >= 3)
+ return spp - 3 - (alpha? 1 : 0);
+ else
+ return spp - 1 - (alpha? 1 : 0);
+ break;
+ case PHOTOMETRIC_SEPARATED:
+ return spp - 4 - (alpha? 1 : 0);
+ break;
+ default:
+ return spp - 1 - (alpha? 1 : 0);
+ break;
+ }
+}
+
+GimpPDBStatusType
+load_image (GFile *file,
+ GimpRunMode run_mode,
+ gint32 *image,
+ gboolean *resolution_loaded,
+ gboolean *profile_loaded,
+ GError **error)
+{
+ TIFF *tif;
+ TiffSelectedPages pages;
+
+ GList *images_list = NULL;
+ DefaultExtra default_extra = GIMP_TIFF_LOAD_UNASSALPHA;
+ gint first_image_type = GIMP_RGB;
+ gint min_row = G_MAXINT;
+ gint min_col = G_MAXINT;
+ gint max_row = 0;
+ gint max_col = 0;
+ GimpColorProfile *first_profile = NULL;
+ const gchar *extra_message = NULL;
+ gint li;
+ gint selectable_pages;
+
+ *image = 0;
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_file_get_utf8_name (file));
+
+ tif = tiff_open (file, "r", error);
+ if (! tif)
+ {
+ if (! (error && *error))
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Not a TIFF image or image is corrupt."));
+
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ pages.target = GIMP_PAGE_SELECTOR_TARGET_LAYERS;
+ gimp_get_data (LOAD_PROC "-target", &pages.target);
+
+ pages.keep_empty_space = TRUE;
+ gimp_get_data (LOAD_PROC "-keep-empty-space",
+ &pages.keep_empty_space);
+
+ pages.n_pages = pages.o_pages = TIFFNumberOfDirectories (tif);
+ if (pages.n_pages == 0)
+ {
+ /* See #5837.
+ * It seems we might be able to rescue some data even though the
+ * TIFF is possibly syntactically wrong.
+ */
+
+ /* libtiff says max number of directory is 65535. */
+ for (li = 0; li < 65536; li++)
+ {
+ if (TIFFSetDirectory (tif, li) == 0)
+ break;
+ }
+ pages.n_pages = li;
+ if (pages.n_pages == 0)
+ {
+ TIFFClose (tif);
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("TIFF '%s' does not contain any directories"),
+ gimp_file_get_utf8_name (file));
+
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ TIFFSetDirectory (tif, 0);
+ g_message (ngettext ("TIFF '%s' directory count by header failed "
+ "though there seems to be %d page."
+ " Attempting to load the file with this assumption.",
+ "TIFF '%s' directory count by header failed "
+ "though there seem to be %d pages."
+ " Attempting to load the file with this assumption.",
+ pages.n_pages),
+ gimp_file_get_utf8_name (file), pages.n_pages);
+ }
+
+ pages.pages = NULL;
+ pages.n_filtered_pages = pages.n_pages;
+ pages.n_reducedimage_pages = pages.n_pages;
+
+ pages.filtered_pages = g_new0 (gint, pages.n_pages);
+ for (li = 0; li < pages.n_pages; li++)
+ pages.filtered_pages[li] = li;
+
+ if (pages.n_pages == 1 || run_mode != GIMP_RUN_INTERACTIVE)
+ {
+ pages.pages = g_new0 (gint, pages.n_pages);
+ for (li = 0; li < pages.n_pages; li++)
+ pages.pages[li] = li;
+ pages.target = GIMP_PAGE_SELECTOR_TARGET_LAYERS;
+ }
+
+ /* Check all pages if any has an unspecified or unset channel. */
+ for (li = 0; li < pages.n_pages; li++)
+ {
+ gushort spp;
+ gushort photomet;
+ gushort extra;
+ gushort *extra_types;
+ gushort file_type = 0;
+ gboolean first_page_old_jpeg = FALSE;
+
+ if (TIFFSetDirectory (tif, li) == 0)
+ continue;
+
+ TIFFGetFieldDefaulted (tif, TIFFTAG_SAMPLESPERPIXEL, &spp);
+ if (! TIFFGetField (tif, TIFFTAG_PHOTOMETRIC, &photomet))
+ {
+ guint16 compression;
+
+ if (TIFFGetField (tif, TIFFTAG_COMPRESSION, &compression) &&
+ (compression == COMPRESSION_CCITTFAX3 ||
+ compression == COMPRESSION_CCITTFAX4 ||
+ compression == COMPRESSION_CCITTRLE ||
+ compression == COMPRESSION_CCITTRLEW))
+ {
+ photomet = PHOTOMETRIC_MINISWHITE;
+ }
+ else
+ {
+ /* old AppleScan software misses out the photometric tag
+ * (and incidentally assumes min-is-white, but xv
+ * assumes min-is-black, so we follow xv's lead. It's
+ * not much hardship to invert the image later).
+ */
+ photomet = PHOTOMETRIC_MINISBLACK;
+ }
+ }
+ if (! TIFFGetField (tif, TIFFTAG_EXTRASAMPLES, &extra, &extra_types))
+ extra = 0;
+
+ /* Try to detect if a TIFF page is a thumbnail.
+ * Easy case: if subfiletype is set to FILETYPE_REDUCEDIMAGE.
+ * If no subfiletype is defined we try to detect it ourselves.
+ * We will consider it a thumbnail if:
+ * - It's the second page
+ * - PhotometricInterpretation is YCbCr
+ * - Compression is old style jpeg
+ * - First page uses a different compression or PhotometricInterpretation
+ *
+ * We could also add a check for the presence of TIFFTAG_EXIFIFD since
+ * this should usually be a thumbnail part of EXIF metadata. Since that
+ * probably won't make a difference, I will leave that out for now.
+ */
+ if (li == 0)
+ {
+ guint16 compression;
+
+ if (TIFFGetField (tif, TIFFTAG_COMPRESSION, &compression) &&
+ compression == COMPRESSION_OJPEG &&
+ photomet == PHOTOMETRIC_YCBCR)
+ first_page_old_jpeg = TRUE;
+ }
+
+ if (TIFFGetField (tif, TIFFTAG_SUBFILETYPE, &file_type))
+ {
+ if (file_type == FILETYPE_REDUCEDIMAGE)
+ {
+ /* file_type is a mask but we will only filter out pages
+ * that only have FILETYPE_REDUCEDIMAGE set */
+ pages.filtered_pages[li] = TIFF_REDUCEDFILE;
+ pages.n_filtered_pages--;
+ g_debug ("Page %d is a FILETYPE_REDUCEDIMAGE thumbnail.\n", li);
+ }
+ }
+ else
+ {
+ if (li == 1 && photomet == PHOTOMETRIC_YCBCR &&
+ ! first_page_old_jpeg)
+ {
+ guint16 compression;
+
+ if (TIFFGetField (tif, TIFFTAG_COMPRESSION, &compression) &&
+ compression == COMPRESSION_OJPEG)
+ {
+ pages.filtered_pages[li] = TIFF_MISC_THUMBNAIL;
+ pages.n_filtered_pages--;
+ /* This is used to conditionally show reduced images
+ * if they're not a thumbnail
+ */
+ pages.n_reducedimage_pages--;
+ g_debug ("Page %d is most likely a thumbnail.\n", li);
+ }
+ }
+ }
+
+ /* TODO: current code always assumes that the alpha channel
+ * will be the first extra channel, though the TIFF spec does
+ * not mandate such assumption. A future improvement should be
+ * to actually loop through the extra channels and save the
+ * alpha channel index.
+ * Of course, this is an edge case, as most image would likely
+ * have only a single extra channel anyway. But still we could
+ * be more accurate.
+ */
+ if (extra > 0 && (extra_types[0] == EXTRASAMPLE_UNSPECIFIED))
+ {
+ extra_message = _("Extra channels with unspecified data.");
+ break;
+ }
+ else if (extra == 0 && is_non_conformant_tiff (photomet, spp))
+ {
+ /* ExtraSamples field not set, yet we have more channels than
+ * the PhotometricInterpretation field suggests.
+ * This should not happen as the spec clearly says "This field
+ * must be present if there are extra samples". So the files
+ * can be considered non-conformant.
+ * Let's ask what to do with the channel.
+ */
+ extra_message = _("Non-conformant TIFF: extra channels without 'ExtraSamples' field.");
+ }
+ }
+ TIFFSetDirectory (tif, 0);
+
+ pages.show_reduced = FALSE;
+ if (pages.n_reducedimage_pages - pages.n_filtered_pages > 1)
+ pages.show_reduced = TRUE;
+
+ pages.tif = tif;
+
+ if (run_mode == GIMP_RUN_INTERACTIVE &&
+ (pages.n_pages > 1 || extra_message) &&
+ ! load_dialog (LOAD_PROC, &pages,
+ extra_message, &default_extra))
+ {
+ TIFFClose (tif);
+ g_clear_pointer (&pages.pages, g_free);
+
+ return GIMP_PDB_CANCEL;
+ }
+
+ selectable_pages = pages.n_filtered_pages;
+ if (pages.show_reduced)
+ selectable_pages = pages.n_reducedimage_pages;
+
+ /* Adjust pages to take filtered out pages into account. */
+ if (pages.o_pages > selectable_pages)
+ {
+ gint fi;
+ gint sel_index = 0;
+ gint sel_add = 0;
+
+ for (fi = 0; fi < pages.o_pages && sel_index < pages.n_pages; fi++)
+ {
+ if ((pages.show_reduced && pages.filtered_pages[fi] == TIFF_MISC_THUMBNAIL) ||
+ (! pages.show_reduced && pages.filtered_pages[fi] <= TIFF_MISC_THUMBNAIL))
+ {
+ sel_add++;
+ }
+ if (pages.pages[sel_index] + sel_add == fi)
+ {
+ pages.pages[sel_index] = fi;
+ sel_index++;
+ }
+ }
+ }
+
+ gimp_set_data (LOAD_PROC "-target",
+ &pages.target, sizeof (pages.target));
+ gimp_set_data (LOAD_PROC "-keep-empty-space",
+ &pages.keep_empty_space,
+ sizeof (pages.keep_empty_space));
+
+ /* We will loop through the all pages in case of multipage TIFF
+ * and load every page as a separate layer.
+ */
+ for (li = 0; li < pages.n_pages; li++)
+ {
+ gint ilayer;
+ gushort bps;
+ gushort spp;
+ gushort photomet;
+ gshort sampleformat;
+ GimpColorProfile *profile;
+ gboolean profile_linear = FALSE;
+ GimpPrecision image_precision;
+ const Babl *type;
+ const Babl *base_format = NULL;
+ const Babl *space = NULL;
+ guint16 orientation;
+ gint cols;
+ gint rows;
+ gboolean alpha;
+ gint image_type = GIMP_RGB;
+ gint layer;
+ gint layer_type = GIMP_RGB_IMAGE;
+ float layer_offset_x = 0.0;
+ float layer_offset_y = 0.0;
+ gint layer_offset_x_pixel = 0;
+ gint layer_offset_y_pixel = 0;
+ gushort extra;
+ gushort *extra_types;
+ ChannelData *channel = NULL;
+ uint16 planar = PLANARCONFIG_CONTIG;
+ TiffColorMode tiff_mode;
+ gboolean is_signed;
+ gint i;
+ gboolean worst_case = FALSE;
+ TiffSaveVals save_vals;
+ const gchar *name;
+
+ if (TIFFSetDirectory (tif, pages.pages[li]) == 0)
+ {
+ g_message (_("Couldn't read page %d of %d. Image might be corrupt.\n"),
+ li+1, pages.n_pages);
+ continue;
+ }
+ ilayer = pages.pages[li];
+
+ gimp_progress_update (0.0);
+
+ TIFFGetFieldDefaulted (tif, TIFFTAG_BITSPERSAMPLE, &bps);
+
+ TIFFGetFieldDefaulted (tif, TIFFTAG_SAMPLEFORMAT, &sampleformat);
+
+ profile = load_profile (tif);
+ if (! profile && first_profile)
+ {
+ profile = first_profile;
+ g_object_ref (profile);
+ }
+
+ if (profile)
+ {
+ profile_linear = gimp_color_profile_is_linear (profile);
+
+ if (! first_profile)
+ {
+ first_profile = profile;
+ g_object_ref (first_profile);
+
+ if (profile_linear && li > 0 && pages.target != GIMP_PAGE_SELECTOR_TARGET_IMAGES)
+ g_message (_("This image has a linear color profile but "
+ "it was not set on the first layer. "
+ "The layers below layer # %d will be "
+ "interpreted as non linear."), li+1);
+ }
+ else if (pages.target != GIMP_PAGE_SELECTOR_TARGET_IMAGES &&
+ ! gimp_color_profile_is_equal (first_profile, profile))
+ {
+ g_message (_("This image has multiple color profiles. "
+ "We will use the first one. If this leads "
+ "to incorrect results you should consider "
+ "loading each layer as a separate image."));
+ }
+
+ if (! *image)
+ *profile_loaded = TRUE;
+ }
+
+ if (bps > 64)
+ {
+ g_message (_("Suspicious bit depth: %d for page %d. Image may be corrupt."),
+ bps, li+1);
+ continue;
+ }
+
+ if (bps > 8 && bps != 8 && bps != 16 && bps != 32 && bps != 64)
+ worst_case = TRUE; /* Wrong sample width => RGBA */
+
+ switch (bps)
+ {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ if (profile_linear)
+ image_precision = GIMP_PRECISION_U8_LINEAR;
+ else
+ image_precision = GIMP_PRECISION_U8_GAMMA;
+
+ type = babl_type ("u8");
+ break;
+
+ case 16:
+ if (sampleformat == SAMPLEFORMAT_IEEEFP)
+ {
+ if (profile_linear)
+ image_precision = GIMP_PRECISION_HALF_LINEAR;
+ else
+ image_precision = GIMP_PRECISION_HALF_GAMMA;
+
+ type = babl_type ("half");
+ }
+ else
+ {
+ if (profile_linear)
+ image_precision = GIMP_PRECISION_U16_LINEAR;
+ else
+ image_precision = GIMP_PRECISION_U16_GAMMA;
+
+ type = babl_type ("u16");
+ }
+ break;
+
+ case 32:
+ if (sampleformat == SAMPLEFORMAT_IEEEFP)
+ {
+ if (profile_linear)
+ image_precision = GIMP_PRECISION_FLOAT_LINEAR;
+ else
+ image_precision = GIMP_PRECISION_FLOAT_GAMMA;
+
+ type = babl_type ("float");
+ }
+ else
+ {
+ if (profile_linear)
+ image_precision = GIMP_PRECISION_U32_LINEAR;
+ else
+ image_precision = GIMP_PRECISION_U32_GAMMA;
+
+ type = babl_type ("u32");
+ }
+ break;
+
+ case 64:
+ if (profile_linear)
+ image_precision = GIMP_PRECISION_DOUBLE_LINEAR;
+ else
+ image_precision = GIMP_PRECISION_DOUBLE_GAMMA;
+
+ type = babl_type ("double");
+ break;
+
+ default:
+ g_message (_("Unsupported bit depth: %d for page %d."),
+ bps, li+1);
+ continue;
+ }
+
+ g_printerr ("bps: %d\n", bps);
+
+ TIFFGetFieldDefaulted (tif, TIFFTAG_SAMPLESPERPIXEL, &spp);
+
+ if (! TIFFGetField (tif, TIFFTAG_EXTRASAMPLES, &extra, &extra_types))
+ extra = 0;
+
+ if (! TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &cols))
+ {
+ TIFFClose (tif);
+ g_message (_("Could not get image width from '%s'"),
+ gimp_file_get_utf8_name (file));
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ if (! TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &rows))
+ {
+ TIFFClose (tif);
+ g_message (_("Could not get image length from '%s'"),
+ gimp_file_get_utf8_name (file));
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ if (cols > GIMP_MAX_IMAGE_SIZE || cols <= 0 ||
+ rows > GIMP_MAX_IMAGE_SIZE || rows <= 0)
+ {
+ g_message (_("Invalid image dimensions (%u x %u) for page %d. "
+ "Image may be corrupt."),
+ (guint32) cols, (guint32) rows, li+1);
+ continue;
+ }
+ else
+ {
+ g_printerr ("Image dimensions: %u x %u.\n",
+ (guint32) cols, (guint32) rows);
+ }
+
+ if (! TIFFGetField (tif, TIFFTAG_PHOTOMETRIC, &photomet))
+ {
+ guint16 compression;
+
+ if (TIFFGetField (tif, TIFFTAG_COMPRESSION, &compression) &&
+ (compression == COMPRESSION_CCITTFAX3 ||
+ compression == COMPRESSION_CCITTFAX4 ||
+ compression == COMPRESSION_CCITTRLE ||
+ compression == COMPRESSION_CCITTRLEW))
+ {
+ g_message (_("Could not get photometric from '%s'. "
+ "Image is CCITT compressed, assuming min-is-white"),
+ gimp_file_get_utf8_name (file));
+ photomet = PHOTOMETRIC_MINISWHITE;
+ }
+ else
+ {
+ g_message (_("Could not get photometric from '%s'. "
+ "Assuming min-is-black"),
+ gimp_file_get_utf8_name (file));
+
+ /* old AppleScan software misses out the photometric tag
+ * (and incidentally assumes min-is-white, but xv
+ * assumes min-is-black, so we follow xv's lead. It's
+ * not much hardship to invert the image later).
+ */
+ photomet = PHOTOMETRIC_MINISBLACK;
+ }
+ }
+
+ /* test if the extrasample represents an associated alpha channel... */
+ if (extra > 0 && (extra_types[0] == EXTRASAMPLE_ASSOCALPHA))
+ {
+ alpha = TRUE;
+ tsvals.save_transp_pixels = FALSE;
+ extra--;
+ }
+ else if (extra > 0 && (extra_types[0] == EXTRASAMPLE_UNASSALPHA))
+ {
+ alpha = TRUE;
+ tsvals.save_transp_pixels = TRUE;
+ extra--;
+ }
+ else if (extra > 0 && (extra_types[0] == EXTRASAMPLE_UNSPECIFIED))
+ {
+ if (run_mode != GIMP_RUN_INTERACTIVE)
+ /* In non-interactive mode, we assume unassociated alpha if unspecified.
+ * We don't output messages in interactive mode as the user
+ * has already the ability to choose through a dialog. */
+ g_message (_("Alpha channel type not defined for %s. "
+ "Assuming alpha is not premultiplied"),
+ gimp_file_get_utf8_name (file));
+
+ switch (default_extra)
+ {
+ case GIMP_TIFF_LOAD_ASSOCALPHA:
+ alpha = TRUE;
+ tsvals.save_transp_pixels = FALSE;
+ break;
+ case GIMP_TIFF_LOAD_UNASSALPHA:
+ alpha = TRUE;
+ tsvals.save_transp_pixels = TRUE;
+ break;
+ default: /* GIMP_TIFF_LOAD_CHANNEL */
+ alpha = FALSE;
+ break;
+ }
+ extra--;
+ }
+ else /* extra == 0 */
+ {
+ if (is_non_conformant_tiff (photomet, spp))
+ {
+ if (run_mode != GIMP_RUN_INTERACTIVE)
+ g_message (_("Image '%s' does not conform to the TIFF specification: "
+ "ExtraSamples field is not set while extra channels are present. "
+ "Assuming the first extra channel is non-premultiplied alpha."),
+ gimp_file_get_utf8_name (file));
+
+ switch (default_extra)
+ {
+ case GIMP_TIFF_LOAD_ASSOCALPHA:
+ alpha = TRUE;
+ tsvals.save_transp_pixels = FALSE;
+ break;
+ case GIMP_TIFF_LOAD_UNASSALPHA:
+ alpha = TRUE;
+ tsvals.save_transp_pixels = TRUE;
+ break;
+ default: /* GIMP_TIFF_LOAD_CHANNEL */
+ alpha = FALSE;
+ break;
+ }
+ }
+ else
+ {
+ alpha = FALSE;
+ }
+ }
+
+ extra = get_extra_channels_count (photomet, spp, alpha);
+
+ tiff_mode = GIMP_TIFF_DEFAULT;
+ is_signed = sampleformat == SAMPLEFORMAT_INT;
+
+ switch (photomet)
+ {
+ case PHOTOMETRIC_PALETTE:
+ case PHOTOMETRIC_MINISBLACK:
+ case PHOTOMETRIC_MINISWHITE:
+ /* Even for bps >= we may need to use tiff_mode, so always set it.
+ * Currently we use it to detect the need to convert 8 bps miniswhite. */
+ if (photomet == PHOTOMETRIC_PALETTE)
+ tiff_mode = GIMP_TIFF_INDEXED;
+ else if (photomet == PHOTOMETRIC_MINISBLACK)
+ tiff_mode = GIMP_TIFF_GRAY;
+ else if (photomet == PHOTOMETRIC_MINISWHITE)
+ tiff_mode = GIMP_TIFF_GRAY_MINISWHITE;
+
+ if (bps < 8)
+ {
+ /* FIXME: It should be a user choice whether this should be
+ * interpreted as indexed or grayscale. For now we will
+ * use indexed (see issue #6766). */
+ image_type = GIMP_INDEXED;
+ layer_type = alpha ? GIMP_INDEXEDA_IMAGE : GIMP_INDEXED_IMAGE;
+
+ if ((bps == 1 || bps == 2 || bps == 4) && ! alpha && spp == 1)
+ {
+ if (bps == 1)
+ fill_bit2byte (tiff_mode);
+ else if (bps == 2)
+ fill_2bit2byte (tiff_mode);
+ else if (bps == 4)
+ fill_4bit2byte (tiff_mode);
+ }
+ }
+ else
+ {
+ if (photomet == PHOTOMETRIC_PALETTE)
+ {
+ image_type = GIMP_INDEXED;
+ layer_type = alpha ? GIMP_INDEXEDA_IMAGE : GIMP_INDEXED_IMAGE;
+ }
+ else
+ {
+ image_type = GIMP_GRAY;
+ layer_type = alpha ? GIMP_GRAYA_IMAGE : GIMP_GRAY_IMAGE;
+ }
+ }
+
+ if (photomet == PHOTOMETRIC_PALETTE)
+ {
+ /* Do nothing here, handled later.
+ * Didn't want more indenting in the next part. */
+ }
+ else if (alpha)
+ {
+ if (tsvals.save_transp_pixels)
+ {
+ if (profile_linear)
+ {
+ base_format = babl_format_new (babl_model ("YA"),
+ type,
+ babl_component ("Y"),
+ babl_component ("A"),
+ NULL);
+ }
+ else
+ {
+ base_format = babl_format_new (babl_model ("Y'A"),
+ type,
+ babl_component ("Y'"),
+ babl_component ("A"),
+ NULL);
+ }
+ }
+ else
+ {
+ if (profile_linear)
+ {
+ base_format = babl_format_new (babl_model ("YaA"),
+ type,
+ babl_component ("Ya"),
+ babl_component ("A"),
+ NULL);
+ }
+ else
+ {
+ base_format = babl_format_new (babl_model ("Y'aA"),
+ type,
+ babl_component ("Y'a"),
+ babl_component ("A"),
+ NULL);
+ }
+ }
+ }
+ else
+ {
+ if (profile_linear)
+ {
+ base_format = babl_format_new (babl_model ("Y"),
+ type,
+ babl_component ("Y"),
+ NULL);
+ }
+ else
+ {
+ base_format = babl_format_new (babl_model ("Y'"),
+ type,
+ babl_component ("Y'"),
+ NULL);
+ }
+ }
+ break;
+
+ case PHOTOMETRIC_RGB:
+ image_type = GIMP_RGB;
+ layer_type = alpha ? GIMP_RGBA_IMAGE : GIMP_RGB_IMAGE;
+
+ if (alpha)
+ {
+ if (tsvals.save_transp_pixels)
+ {
+ if (profile_linear)
+ {
+ base_format = babl_format_new (babl_model ("RGBA"),
+ type,
+ babl_component ("R"),
+ babl_component ("G"),
+ babl_component ("B"),
+ babl_component ("A"),
+ NULL);
+ }
+ else
+ {
+ base_format = babl_format_new (babl_model ("R'G'B'A"),
+ type,
+ babl_component ("R'"),
+ babl_component ("G'"),
+ babl_component ("B'"),
+ babl_component ("A"),
+ NULL);
+ }
+ }
+ else
+ {
+ if (profile_linear)
+ {
+ base_format = babl_format_new (babl_model ("RaGaBaA"),
+ type,
+ babl_component ("Ra"),
+ babl_component ("Ga"),
+ babl_component ("Ba"),
+ babl_component ("A"),
+ NULL);
+ }
+ else
+ {
+ base_format = babl_format_new (babl_model ("R'aG'aB'aA"),
+ type,
+ babl_component ("R'a"),
+ babl_component ("G'a"),
+ babl_component ("B'a"),
+ babl_component ("A"),
+ NULL);
+ }
+ }
+ }
+ else
+ {
+ if (profile_linear)
+ {
+ base_format = babl_format_new (babl_model ("RGB"),
+ type,
+ babl_component ("R"),
+ babl_component ("G"),
+ babl_component ("B"),
+ NULL);
+ }
+ else
+ {
+ base_format = babl_format_new (babl_model ("R'G'B'"),
+ type,
+ babl_component ("R'"),
+ babl_component ("G'"),
+ babl_component ("B'"),
+ NULL);
+ }
+ }
+ break;
+
+ case PHOTOMETRIC_SEPARATED:
+ layer_type = alpha ? GIMP_RGBA_IMAGE : GIMP_RGB_IMAGE;
+ /* It's possible that a CMYK image might not have an
+ * attached profile, so we'll check for it and set up
+ * space accordingly
+ */
+ if (profile && gimp_color_profile_is_cmyk (profile))
+ {
+ space = gimp_color_profile_get_space (profile,
+ GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC,
+ error);
+ g_clear_object (&profile);
+ }
+ else
+ {
+ space = NULL;
+ }
+
+ if (alpha)
+ base_format = babl_format_new (babl_model ("CMYKA"),
+ type,
+ babl_component ("Cyan"),
+ babl_component ("Magenta"),
+ babl_component ("Yellow"),
+ babl_component ("Key"),
+ babl_component ("A"),
+ NULL);
+ else
+ base_format = babl_format_new (babl_model ("CMYK"),
+ type,
+ babl_component ("Cyan"),
+ babl_component ("Magenta"),
+ babl_component ("Yellow"),
+ babl_component ("Key"),
+ NULL);
+
+ base_format =
+ babl_format_with_space (babl_format_get_encoding (base_format),
+ space);
+ break;
+
+ default:
+ g_printerr ("photomet: %d (%d)\n", photomet, PHOTOMETRIC_PALETTE);
+ worst_case = TRUE;
+ break;
+ }
+
+ /* attach a parasite containing the compression */
+ {
+ guint16 compression = COMPRESSION_NONE;
+
+ if (TIFFGetField (tif, TIFFTAG_COMPRESSION, &compression))
+ {
+ switch (compression)
+ {
+ case COMPRESSION_NONE:
+ case COMPRESSION_LZW:
+ case COMPRESSION_PACKBITS:
+ case COMPRESSION_DEFLATE:
+ case COMPRESSION_ADOBE_DEFLATE:
+ case COMPRESSION_JPEG:
+ case COMPRESSION_CCITTFAX3:
+ case COMPRESSION_CCITTFAX4:
+ break;
+
+ case COMPRESSION_OJPEG:
+ worst_case = TRUE;
+ compression = COMPRESSION_JPEG;
+ break;
+
+ default:
+ g_message (_("Invalid or unknown compression %u. "
+ "Setting compression to none."),
+ compression);
+ compression = COMPRESSION_NONE;
+ break;
+ }
+ }
+
+ save_vals.compression = compression;
+ }
+
+ if (worst_case)
+ {
+ image_type = GIMP_RGB;
+ layer_type = GIMP_RGBA_IMAGE;
+
+ if (profile_linear)
+ {
+ base_format = babl_format_new (babl_model ("RaGaBaA"),
+ type,
+ babl_component ("Ra"),
+ babl_component ("Ga"),
+ babl_component ("Ba"),
+ babl_component ("A"),
+ NULL);
+ }
+ else
+ {
+ base_format = babl_format_new (babl_model ("R'aG'aB'aA"),
+ type,
+ babl_component ("R'a"),
+ babl_component ("G'a"),
+ babl_component ("B'a"),
+ babl_component ("A"),
+ NULL);
+ }
+ }
+
+ if (pages.target == GIMP_PAGE_SELECTOR_TARGET_LAYERS)
+ {
+ if (li == 0)
+ {
+ first_image_type = image_type;
+ }
+ else if (image_type != first_image_type)
+ {
+ continue;
+ }
+ }
+
+ if ((pages.target == GIMP_PAGE_SELECTOR_TARGET_IMAGES) || (! *image))
+ {
+ *image = gimp_image_new_with_precision (cols, rows, image_type,
+ image_precision);
+
+ if (*image < 1)
+ {
+ TIFFClose (tif);
+ g_message (_("Could not create a new image: %s"),
+ gimp_get_pdb_error ());
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ gimp_image_undo_disable (*image);
+
+ if (pages.target == GIMP_PAGE_SELECTOR_TARGET_IMAGES)
+ {
+ gchar *fname = g_strdup_printf ("%s-%d", g_file_get_uri (file),
+ ilayer);
+
+ gimp_image_set_filename (*image, fname);
+ g_free (fname);
+
+ images_list = g_list_prepend (images_list,
+ GINT_TO_POINTER (*image));
+ }
+ else if (pages.o_pages != pages.n_pages)
+ {
+ gchar *fname = g_strdup_printf (_("%s-%d-of-%d-pages"),
+ g_file_get_uri (file),
+ pages.n_pages, pages.o_pages);
+
+ gimp_image_set_filename (*image, fname);
+ g_free (fname);
+ }
+ else
+ {
+ gimp_image_set_filename (*image, g_file_get_uri (file));
+ }
+ }
+
+ /* attach non-CMYK color profile */
+ if (profile)
+ {
+ if (pages.target == GIMP_PAGE_SELECTOR_TARGET_IMAGES || profile == first_profile)
+ gimp_image_set_color_profile (*image, profile);
+
+ g_object_unref (profile);
+ }
+
+ /* attach parasites */
+ {
+ GimpParasite *parasite;
+ const gchar *img_desc;
+
+ parasite = gimp_parasite_new ("tiff-save-options", 0,
+ sizeof (save_vals), &save_vals);
+ gimp_image_attach_parasite (*image, parasite);
+ gimp_parasite_free (parasite);
+
+ /* Attach a parasite containing the image description.
+ * Pretend to be a gimp comment so other plugins will use this
+ * description as an image comment where appropriate.
+ */
+ if (TIFFGetField (tif, TIFFTAG_IMAGEDESCRIPTION, &img_desc) &&
+ g_utf8_validate (img_desc, -1, NULL))
+ {
+ parasite = gimp_parasite_new ("gimp-comment",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (img_desc) + 1, img_desc);
+ gimp_image_attach_parasite (*image, parasite);
+ gimp_parasite_free (parasite);
+ }
+ }
+
+ /* Attach GeoTIFF Tags as Parasite, If available */
+ {
+ GimpParasite *parasite = NULL;
+ void *geotag_data = NULL;
+ uint32 count = 0;
+
+ if (TIFFGetField (tif, GEOTIFF_MODELPIXELSCALE, &count, &geotag_data))
+ {
+ parasite = gimp_parasite_new ("Gimp_GeoTIFF_ModelPixelScale",
+ GIMP_PARASITE_PERSISTENT,
+ (TIFFDataWidth (TIFF_DOUBLE) * count),
+ geotag_data);
+ gimp_image_attach_parasite (*image, parasite);
+ gimp_parasite_free (parasite);
+ }
+
+ if (TIFFGetField (tif, GEOTIFF_MODELTIEPOINT, &count, &geotag_data))
+ {
+ parasite = gimp_parasite_new ("Gimp_GeoTIFF_ModelTiePoint",
+ GIMP_PARASITE_PERSISTENT,
+ (TIFFDataWidth (TIFF_DOUBLE) * count),
+ geotag_data);
+ gimp_image_attach_parasite (*image, parasite);
+ gimp_parasite_free (parasite);
+ }
+
+ if (TIFFGetField (tif, GEOTIFF_MODELTRANSFORMATION, &count, &geotag_data))
+ {
+ parasite = gimp_parasite_new ("Gimp_GeoTIFF_ModelTransformation",
+ GIMP_PARASITE_PERSISTENT,
+ (TIFFDataWidth (TIFF_DOUBLE) * count),
+ geotag_data);
+ gimp_image_attach_parasite (*image, parasite);
+ gimp_parasite_free (parasite);
+ }
+
+ if (TIFFGetField (tif, GEOTIFF_KEYDIRECTORY, &count, &geotag_data) )
+ {
+ parasite = gimp_parasite_new ("Gimp_GeoTIFF_KeyDirectory",
+ GIMP_PARASITE_PERSISTENT,
+ (TIFFDataWidth (TIFF_SHORT) * count),
+ geotag_data);
+ gimp_image_attach_parasite (*image, parasite);
+ gimp_parasite_free (parasite);
+ }
+
+ if (TIFFGetField (tif, GEOTIFF_DOUBLEPARAMS, &count, &geotag_data))
+ {
+ parasite = gimp_parasite_new ("Gimp_GeoTIFF_DoubleParams",
+ GIMP_PARASITE_PERSISTENT,
+ (TIFFDataWidth (TIFF_DOUBLE) * count),
+ geotag_data);
+ gimp_image_attach_parasite (*image, parasite);
+ gimp_parasite_free (parasite);
+ }
+
+ if (TIFFGetField (tif, GEOTIFF_ASCIIPARAMS, &count, &geotag_data))
+ {
+ parasite = gimp_parasite_new ("Gimp_GeoTIFF_Asciiparams",
+ GIMP_PARASITE_PERSISTENT,
+ (TIFFDataWidth (TIFF_ASCII) * count),
+ geotag_data);
+ gimp_image_attach_parasite (*image, parasite);
+ gimp_parasite_free (parasite);
+ }
+ }
+
+ /* any resolution info in the file? */
+ {
+ gfloat xres = 72.0;
+ gfloat yres = 72.0;
+ gushort read_unit;
+ GimpUnit unit = GIMP_UNIT_PIXEL; /* invalid unit */
+
+ if (TIFFGetField (tif, TIFFTAG_XRESOLUTION, &xres))
+ {
+ if (TIFFGetField (tif, TIFFTAG_YRESOLUTION, &yres))
+ {
+ if (TIFFGetFieldDefaulted (tif, TIFFTAG_RESOLUTIONUNIT,
+ &read_unit))
+ {
+ switch (read_unit)
+ {
+ case RESUNIT_NONE:
+ /* ImageMagick writes files with this silly resunit */
+ break;
+
+ case RESUNIT_INCH:
+ unit = GIMP_UNIT_INCH;
+ break;
+
+ case RESUNIT_CENTIMETER:
+ xres *= 2.54;
+ yres *= 2.54;
+ unit = GIMP_UNIT_MM; /* this is our default metric unit */
+ break;
+
+ default:
+ g_message (_("Unknown resolution "
+ "unit type %d, assuming dpi"), read_unit);
+ break;
+ }
+ }
+ else
+ {
+ /* no res unit tag */
+
+ /* old AppleScan software produces these */
+ g_message (_("Warning: resolution specified without "
+ "unit type, assuming dpi"));
+ }
+ }
+ else
+ {
+ /* xres but no yres */
+
+ g_message (_("Warning: no y resolution info, assuming same as x"));
+ yres = xres;
+ }
+
+ /* now set the new image's resolution info */
+
+ /* If it is invalid, instead of forcing 72dpi, do not set
+ * the resolution at all. Gimp will then use the default
+ * set by the user
+ */
+ if (read_unit != RESUNIT_NONE)
+ {
+ if (! isfinite (xres) ||
+ xres < GIMP_MIN_RESOLUTION || xres > GIMP_MAX_RESOLUTION ||
+ ! isfinite (yres) ||
+ yres < GIMP_MIN_RESOLUTION || yres > GIMP_MAX_RESOLUTION)
+ {
+ g_message (_("Invalid image resolution info, using default"));
+ /* We need valid xres and yres for computing
+ * layer_offset_x_pixel and layer_offset_y_pixel.
+ */
+ gimp_image_get_resolution (*image, &xres, &yres);
+ }
+ else
+ {
+ gimp_image_set_resolution (*image, xres, yres);
+ if (unit != GIMP_UNIT_PIXEL)
+ gimp_image_set_unit (*image, unit);
+
+ *resolution_loaded = TRUE;
+ }
+ }
+ }
+
+ /* no x res tag => we assume we have no resolution info, so we
+ * don't care. Older versions of this plugin used to write
+ * files with no resolution tags at all.
+ */
+
+ /* TODO: haven't caught the case where yres tag is present,
+ * but not xres. This is left as an exercise for the reader -
+ * they should feel free to shoot the author of the broken
+ * program that produced the damaged TIFF file in the first
+ * place.
+ */
+
+ /* handle layer offset */
+ if (! TIFFGetField (tif, TIFFTAG_XPOSITION, &layer_offset_x))
+ layer_offset_x = 0.0;
+
+ if (! TIFFGetField (tif, TIFFTAG_YPOSITION, &layer_offset_y))
+ layer_offset_y = 0.0;
+
+ /* round floating point position to integer position required
+ * by GIMP
+ */
+ layer_offset_x_pixel = ROUND (layer_offset_x * xres);
+ layer_offset_y_pixel = ROUND (layer_offset_y * yres);
+ }
+
+ /* Install colormap for INDEXED images only */
+ if (image_type == GIMP_INDEXED)
+ {
+ guchar cmap[768];
+
+ if (photomet == PHOTOMETRIC_PALETTE)
+ {
+ gushort *redmap;
+ gushort *greenmap;
+ gushort *bluemap;
+ gint i, j;
+
+ if (! TIFFGetField (tif, TIFFTAG_COLORMAP,
+ &redmap, &greenmap, &bluemap))
+ {
+ TIFFClose (tif);
+ g_message (_("Could not get colormaps from '%s'"),
+ gimp_file_get_utf8_name (file));
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ for (i = 0, j = 0; i < (1 << bps); i++)
+ {
+ cmap[j++] = redmap[i] >> 8;
+ cmap[j++] = greenmap[i] >> 8;
+ cmap[j++] = bluemap[i] >> 8;
+ }
+
+ }
+ else if (photomet == PHOTOMETRIC_MINISBLACK)
+ {
+ gint i, j;
+
+ if (bps == 1)
+ {
+ for (i = 0, j = 0; i < (1 << bps); i++)
+ {
+ cmap[j++] = _1_to_8_bitmap[i];
+ cmap[j++] = _1_to_8_bitmap[i];
+ cmap[j++] = _1_to_8_bitmap[i];
+ }
+ }
+ else if (bps == 2)
+ {
+ for (i = 0, j = 0; i < (1 << bps); i++)
+ {
+ cmap[j++] = _2_to_8_bitmap[i];
+ cmap[j++] = _2_to_8_bitmap[i];
+ cmap[j++] = _2_to_8_bitmap[i];
+ }
+ }
+ else if (bps == 4)
+ {
+ for (i = 0, j = 0; i < (1 << bps); i++)
+ {
+ cmap[j++] = _4_to_8_bitmap[i];
+ cmap[j++] = _4_to_8_bitmap[i];
+ cmap[j++] = _4_to_8_bitmap[i];
+ }
+ }
+ }
+ else if (photomet == PHOTOMETRIC_MINISWHITE)
+ {
+ gint i, j;
+
+ if (bps == 1)
+ {
+ for (i = 0, j = 0; i < (1 << bps); i++)
+ {
+ cmap[j++] = _1_to_8_bitmap_rev[i];
+ cmap[j++] = _1_to_8_bitmap_rev[i];
+ cmap[j++] = _1_to_8_bitmap_rev[i];
+ }
+ }
+ else if (bps == 2)
+ {
+ for (i = 0, j = 0; i < (1 << bps); i++)
+ {
+ cmap[j++] = _2_to_8_bitmap_rev[i];
+ cmap[j++] = _2_to_8_bitmap_rev[i];
+ cmap[j++] = _2_to_8_bitmap_rev[i];
+ }
+ }
+ else if (bps == 4)
+ {
+ for (i = 0, j = 0; i < (1 << bps); i++)
+ {
+ cmap[j++] = _4_to_8_bitmap_rev[i];
+ cmap[j++] = _4_to_8_bitmap_rev[i];
+ cmap[j++] = _4_to_8_bitmap_rev[i];
+ }
+ }
+ }
+
+ gimp_image_set_colormap (*image, cmap, (1 << bps));
+ }
+
+ if (pages.target != GIMP_PAGE_SELECTOR_TARGET_IMAGES)
+ load_paths (tif, *image, cols, rows,
+ layer_offset_x_pixel, layer_offset_y_pixel);
+ else
+ load_paths (tif, *image, cols, rows, 0, 0);
+
+ if (extra > 99)
+ {
+ /* Validate number of channels to the same maximum as we use for
+ * Photoshop. A higher number most likely means a corrupt image
+ * and can cause GIMP to become unresponsive and/or stuck.
+ * See m2-d0f86ab189cbe900ec389ca6d7464713.tif from imagetestsuite
+ */
+ g_message (_("Suspicious number of extra channels: %d. Possibly corrupt image."), extra);
+ extra = 99;
+ }
+
+ /* Allocate ChannelData for all channels, even the background layer */
+ channel = g_new0 (ChannelData, extra + 1);
+
+ /* try and use layer name from tiff file */
+ name = tiff_get_page_name (tif);
+
+ if (name)
+ {
+ layer = gimp_layer_new (*image, name,
+ cols, rows,
+ layer_type,
+ 100,
+ gimp_image_get_default_new_layer_mode (*image));
+ }
+ else
+ {
+ gchar *name;
+
+ if (ilayer == 0)
+ name = g_strdup (_("Background"));
+ else
+ name = g_strdup_printf (_("Page %d"), ilayer);
+
+ layer = gimp_layer_new (*image, name,
+ cols, rows,
+ layer_type,
+ 100,
+ gimp_image_get_default_new_layer_mode (*image));
+ g_free (name);
+ }
+
+ if (! base_format && image_type == GIMP_INDEXED)
+ {
+ /* can't create the palette format here, need to get it from
+ * an existing layer
+ */
+ base_format = gimp_drawable_get_format (layer);
+ }
+ else if (! space)
+ {
+ base_format =
+ babl_format_with_space (babl_format_get_encoding (base_format),
+ gimp_drawable_get_format (layer));
+ }
+
+ channel[0].ID = layer;
+ channel[0].buffer = gimp_drawable_get_buffer (layer);
+ channel[0].format = base_format;
+
+ if (extra > 0 && ! worst_case)
+ {
+ /* Add extra channels as appropriate */
+ for (i = 1; i <= extra; i++)
+ {
+ GimpRGB color;
+
+ gimp_rgb_set (&color, 0.0, 0.0, 0.0);
+
+ channel[i].ID = gimp_channel_new (*image, _("TIFF Channel"),
+ cols, rows,
+ 100.0, &color);
+ gimp_image_insert_channel (*image, channel[i].ID, -1, 0);
+ channel[i].buffer = gimp_drawable_get_buffer (channel[i].ID);
+ channel[i].format = babl_format_new (babl_model ("Y'"),
+ type,
+ babl_component ("Y'"),
+ NULL);
+ }
+ }
+
+ TIFFGetField (tif, TIFFTAG_PLANARCONFIG, &planar);
+
+ if (worst_case)
+ {
+ load_rgba (tif, channel);
+ }
+ else if (planar == PLANARCONFIG_CONTIG)
+ {
+ load_contiguous (tif, channel, type, bps, spp,
+ tiff_mode, is_signed, extra);
+ }
+ else
+ {
+ load_separate (tif, channel, type, bps, spp,
+ tiff_mode, is_signed, extra);
+ }
+
+ if (TIFFGetField (tif, TIFFTAG_ORIENTATION, &orientation))
+ {
+ gboolean flip_horizontal = FALSE;
+ gboolean flip_vertical = FALSE;
+
+ switch (orientation)
+ {
+ case ORIENTATION_TOPLEFT:
+ break;
+
+ case ORIENTATION_TOPRIGHT:
+ flip_horizontal = TRUE;
+ break;
+
+ case ORIENTATION_BOTRIGHT:
+ flip_horizontal = TRUE;
+ flip_vertical = TRUE;
+ break;
+
+ case ORIENTATION_BOTLEFT:
+ flip_vertical = TRUE;
+ break;
+
+ default:
+ g_warning ("Orientation %d not handled yet!", orientation);
+ break;
+ }
+
+ if (flip_horizontal)
+ gimp_item_transform_flip_simple (layer,
+ GIMP_ORIENTATION_HORIZONTAL,
+ TRUE /* auto_center */,
+ -1.0 /* axis */);
+
+ if (flip_vertical)
+ gimp_item_transform_flip_simple (layer,
+ GIMP_ORIENTATION_VERTICAL,
+ TRUE /* auto_center */,
+ -1.0 /* axis */);
+ }
+
+ for (i = 0; i <= extra; i++)
+ {
+ if (channel[i].buffer)
+ g_object_unref (channel[i].buffer);
+ }
+
+ g_free (channel);
+ channel = NULL;
+
+ if (pages.target != GIMP_PAGE_SELECTOR_TARGET_IMAGES)
+ {
+ /* compute bounding box of all layers read so far */
+ if (min_col > layer_offset_x_pixel)
+ min_col = layer_offset_x_pixel;
+ if (min_row > layer_offset_y_pixel)
+ min_row = layer_offset_y_pixel;
+
+ if (max_col < layer_offset_x_pixel + cols)
+ max_col = layer_offset_x_pixel + cols;
+ if (max_row < layer_offset_y_pixel + rows)
+ max_row = layer_offset_y_pixel + rows;
+
+ /* position the layer */
+ if (layer_offset_x_pixel > 0 ||
+ layer_offset_y_pixel > 0)
+ {
+ gimp_layer_set_offsets (layer,
+ layer_offset_x_pixel,
+ layer_offset_y_pixel);
+ }
+ }
+
+ gimp_image_insert_layer (*image, layer, -1, -1);
+
+ if (pages.target == GIMP_PAGE_SELECTOR_TARGET_IMAGES)
+ {
+ gimp_image_undo_enable (*image);
+ gimp_image_clean_all (*image);
+ }
+
+ gimp_progress_update (1.0);
+ }
+ g_clear_object (&first_profile);
+
+ if (pages.target == GIMP_PAGE_SELECTOR_TARGET_IMAGES)
+ {
+ GList *list = images_list;
+
+ if (list)
+ {
+ *image = GPOINTER_TO_INT (list->data);
+
+ list = g_list_next (list);
+ }
+
+ for (; list; list = g_list_next (list))
+ {
+ gimp_display_new (GPOINTER_TO_INT (list->data));
+ }
+
+ g_list_free (images_list);
+ }
+ else
+ {
+ if (! (*image))
+ {
+ TIFFClose (tif);
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("No data could be read from TIFF '%s'. The file is probably corrupted."),
+ gimp_file_get_utf8_name (file));
+
+ return GIMP_PDB_EXECUTION_ERROR;
+ }
+
+ if (pages.keep_empty_space)
+ {
+ /* unfortunately we have no idea about empty space
+ at the bottom/right of layers */
+ min_col = 0;
+ min_row = 0;
+ }
+
+ /* resize image to bounding box of all layers */
+ gimp_image_resize (*image,
+ max_col - min_col, max_row - min_row,
+ -min_col, -min_row);
+
+ gimp_image_undo_enable (*image);
+ }
+
+ g_free (pages.pages);
+ TIFFClose (tif);
+
+ return GIMP_PDB_SUCCESS;
+}
+
+static GimpColorProfile *
+load_profile (TIFF *tif)
+{
+ GimpColorProfile *profile = NULL;
+
+#ifdef TIFFTAG_ICCPROFILE
+ /* If TIFFTAG_ICCPROFILE is defined we are dealing with a
+ * libtiff version that can handle ICC profiles. Otherwise just
+ * return a NULL profile.
+ */
+ uint32 profile_size;
+ guchar *icc_profile;
+
+ /* set the ICC profile - if found in the TIFF */
+ if (TIFFGetField (tif, TIFFTAG_ICCPROFILE, &profile_size, &icc_profile))
+ {
+ profile = gimp_color_profile_new_from_icc_profile (icc_profile,
+ profile_size,
+ NULL);
+ }
+#endif
+
+ return profile;
+}
+
+static void
+load_rgba (TIFF *tif,
+ ChannelData *channel)
+{
+ guint32 image_width;
+ guint32 image_height;
+ guint32 row;
+ guint32 *buffer;
+
+ g_printerr ("%s\n", __func__);
+
+ TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &image_width);
+ TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &image_height);
+
+ buffer = g_new (uint32, image_width * image_height);
+
+ if (! TIFFReadRGBAImage (tif, image_width, image_height, buffer, 0))
+ {
+ g_message (_("%s: Unsupported image format, no RGBA loader available"),
+ G_STRFUNC);
+ g_free (buffer);
+ return;
+ }
+
+ for (row = 0; row < image_height; row++)
+ {
+#if G_BYTE_ORDER != G_LITTLE_ENDIAN
+ /* Make sure our channels are in the right order */
+ guint32 row_start = row * image_width;
+ guint32 row_end = row_start + image_width;
+ guint32 i;
+
+ for (i = row_start; i < row_end; i++)
+ buffer[i] = GUINT32_TO_LE (buffer[i]);
+#endif
+
+ gegl_buffer_set (channel[0].buffer,
+ GEGL_RECTANGLE (0, image_height - row - 1,
+ image_width, 1),
+ 0, channel[0].format,
+ ((guchar *) buffer) + row * image_width * 4,
+ GEGL_AUTO_ROWSTRIDE);
+
+ if ((row % 32) == 0)
+ gimp_progress_update ((gdouble) row / (gdouble) image_height);
+ }
+
+ g_free (buffer);
+}
+
+static void
+load_paths (TIFF *tif,
+ gint image,
+ gint width,
+ gint height,
+ gint offset_x,
+ gint offset_y)
+{
+ gsize n_bytes;
+ gchar *bytes;
+ gint path_index;
+ gsize pos;
+
+ if (! TIFFGetField (tif, TIFFTAG_PHOTOSHOP, &n_bytes, &bytes))
+ return;
+
+ path_index = 0;
+ pos = 0;
+
+ while (pos < n_bytes)
+ {
+ guint16 id;
+ gsize len;
+ gchar *name;
+ guint32 *val32;
+ guint16 *val16;
+
+ if (n_bytes-pos < 7 ||
+ strncmp (bytes + pos, "8BIM", 4) != 0)
+ break;
+
+ pos += 4;
+
+ val16 = (guint16 *) (bytes + pos);
+ id = GUINT16_FROM_BE (*val16);
+ pos += 2;
+
+ /* g_printerr ("id: %x\n", id); */
+ len = (guchar) bytes[pos];
+
+ if (n_bytes - pos < len + 1)
+ break; /* block not big enough */
+
+ /* do we have the UTF-marker? is it valid UTF-8?
+ * if so, we assume an utf-8 encoded name, otherwise we
+ * assume iso8859-1
+ */
+ name = bytes + pos + 1;
+ if (len >= 3 &&
+ name[0] == '\xEF' && name[1] == '\xBB' && name[2] == '\xBF' &&
+ g_utf8_validate (name, len, NULL))
+ {
+ name = g_strndup (name + 3, len - 3);
+ }
+ else
+ {
+ name = g_convert (name, len, "utf-8", "iso8859-1", NULL, NULL, NULL);
+ }
+
+ if (! name)
+ name = g_strdup ("(imported path)");
+
+ pos += len + 1;
+
+ if (pos % 2) /* padding */
+ pos++;
+
+ if (n_bytes - pos < 4)
+ break; /* block not big enough */
+
+ val32 = (guint32 *) (bytes + pos);
+ len = GUINT32_FROM_BE (*val32);
+ pos += 4;
+
+ if (n_bytes - pos < len)
+ break; /* block not big enough */
+
+ if (id >= 2000 && id <= 2998)
+ {
+ /* path information */
+ guint16 type;
+ gint rec = pos;
+ gint32 vectors;
+ gdouble *points = NULL;
+ gint expected_points = 0;
+ gint pointcount = 0;
+ gboolean closed = FALSE;
+
+ vectors = gimp_vectors_new (image, name);
+ gimp_image_insert_vectors (image, vectors, -1, path_index);
+ path_index++;
+
+ while (rec < pos + len)
+ {
+ /* path records */
+ val16 = (guint16 *) (bytes + rec);
+ type = GUINT16_FROM_BE (*val16);
+
+ switch (type)
+ {
+ case 0: /* new closed subpath */
+ case 3: /* new open subpath */
+ val16 = (guint16 *) (bytes + rec + 2);
+ expected_points = GUINT16_FROM_BE (*val16);
+ pointcount = 0;
+ closed = (type == 0);
+
+ if (n_bytes - rec < (expected_points + 1) * 26)
+ {
+ g_printerr ("not enough point records\n");
+ rec = pos + len;
+ continue;
+ }
+
+ if (points)
+ g_free (points);
+ points = g_new (gdouble, expected_points * 6);
+ break;
+
+ case 1: /* closed subpath bezier knot, linked */
+ case 2: /* closed subpath bezier knot, unlinked */
+ case 4: /* open subpath bezier knot, linked */
+ case 5: /* open subpath bezier knot, unlinked */
+ /* since we already know if the subpath is open
+ * or closed and since we don't differentiate between
+ * linked and unlinked, just treat all the same... */
+
+ if (pointcount < expected_points)
+ {
+ gint j;
+
+ for (j = 0; j < 6; j++)
+ {
+ gdouble f;
+ guint32 coord;
+
+ const gint size = j % 2 ? width : height;
+ const gint offset = j % 2 ? offset_x : offset_y;
+
+ val32 = (guint32 *) (bytes + rec + 2 + j * 4);
+ coord = GUINT32_FROM_BE (*val32);
+
+ f = (double) ((gchar) ((coord >> 24) & 0xFF)) +
+ (double) (coord & 0x00FFFFFF) /
+ (double) 0xFFFFFF;
+
+ /* coords are stored with vertical component
+ * first, gimp expects the horizontal
+ * component first. Sigh.
+ */
+ points[pointcount * 6 + (j ^ 1)] = f * size + offset;
+ }
+
+ pointcount++;
+
+ if (pointcount == expected_points)
+ {
+ gimp_vectors_stroke_new_from_points (vectors,
+ GIMP_VECTORS_STROKE_TYPE_BEZIER,
+ pointcount * 6,
+ points,
+ closed);
+ }
+ }
+ else
+ {
+ g_printerr ("Oops - unexpected point record\n");
+ }
+
+ break;
+
+ case 6: /* path fill rule record */
+ case 7: /* clipboard record (?) */
+ case 8: /* initial fill rule record (?) */
+ /* we cannot use this information */
+
+ default:
+ break;
+ }
+
+ rec += 26;
+ }
+
+ if (points)
+ g_free (points);
+ }
+
+ pos += len;
+
+ if (pos % 2) /* padding */
+ pos++;
+
+ g_free (name);
+ }
+}
+
+
+static void
+load_contiguous (TIFF *tif,
+ ChannelData *channel,
+ const Babl *type,
+ gushort bps,
+ gushort spp,
+ TiffColorMode tiff_mode,
+ gboolean is_signed,
+ gint extra)
+{
+ guint32 image_width;
+ guint32 image_height;
+ guint32 tile_width;
+ guint32 tile_height;
+ gint bytes_per_pixel;
+ const Babl *src_format;
+ guchar *buffer;
+ guchar *bw_buffer = NULL;
+ gdouble progress = 0.0;
+ gdouble one_row;
+ guint32 y;
+ gint i;
+ gboolean needs_upscale = FALSE;
+
+ g_printerr ("%s\n", __func__);
+
+ TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &image_width);
+ TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &image_height);
+
+ tile_width = image_width;
+
+ if (TIFFIsTiled (tif))
+ {
+ TIFFGetField (tif, TIFFTAG_TILEWIDTH, &tile_width);
+ TIFFGetField (tif, TIFFTAG_TILELENGTH, &tile_height);
+
+ buffer = g_malloc (TIFFTileSize (tif));
+ }
+ else
+ {
+ tile_width = image_width;
+ tile_height = 1;
+
+ buffer = g_malloc (TIFFScanlineSize (tif));
+ }
+
+ if (tiff_mode != GIMP_TIFF_DEFAULT && bps < 8)
+ {
+ needs_upscale = TRUE;
+ bw_buffer = g_malloc (tile_width * tile_height);
+ }
+
+ one_row = (gdouble) tile_height / (gdouble) image_height;
+
+ src_format = babl_format_n (type, spp);
+
+ /* consistency check */
+ bytes_per_pixel = 0;
+ for (i = 0; i <= extra; i++)
+ bytes_per_pixel += babl_format_get_bytes_per_pixel (channel[i].format);
+
+ g_printerr ("bytes_per_pixel: %d, format: %d\n",
+ bytes_per_pixel,
+ babl_format_get_bytes_per_pixel (src_format));
+
+ for (y = 0; y < image_height; y += tile_height)
+ {
+ guint32 x;
+
+ for (x = 0; x < image_width; x += tile_width)
+ {
+ GeglBuffer *src_buf;
+ guint32 rows;
+ guint32 cols;
+ gint offset;
+
+ gimp_progress_update (progress + one_row *
+ ((gdouble) x / (gdouble) image_width));
+
+ if (TIFFIsTiled (tif))
+ {
+ if (TIFFReadTile (tif, buffer, x, y, 0, 0) == -1)
+ {
+ g_message (_("Reading tile failed. Image may be corrupt at line %d."), y);
+ g_free (buffer);
+ g_free (bw_buffer);
+ return;
+ }
+ }
+ else if (TIFFReadScanline (tif, buffer, y, 0) == -1)
+ {
+ /* Error reading scanline, stop loading */
+ g_message (_("Reading scanline failed. Image may be corrupt at line %d."), y);
+ g_free (buffer);
+ g_free (bw_buffer);
+ return;
+ }
+
+ cols = MIN (image_width - x, tile_width);
+ rows = MIN (image_height - y, tile_height);
+
+ if (needs_upscale)
+ {
+ if (bps == 1)
+ convert_bit2byte (buffer, bw_buffer, cols, rows);
+ else if (bps == 2)
+ convert_2bit2byte (buffer, bw_buffer, cols, rows);
+ else if (bps == 4)
+ convert_4bit2byte (buffer, bw_buffer, cols, rows);
+ }
+ else if (is_signed)
+ {
+ convert_int2uint (buffer, bps, spp, cols, rows,
+ tile_width * bytes_per_pixel);
+ }
+
+ if (tiff_mode == GIMP_TIFF_GRAY_MINISWHITE && bps == 8)
+ {
+ convert_miniswhite (buffer, cols, rows);
+ }
+
+ src_buf = gegl_buffer_linear_new_from_data (needs_upscale ? bw_buffer : buffer,
+ src_format,
+ GEGL_RECTANGLE (0, 0, cols, rows),
+ tile_width * bytes_per_pixel,
+ NULL, NULL);
+
+ offset = 0;
+
+ for (i = 0; i <= extra; i++)
+ {
+ GeglBufferIterator *iter;
+ gint src_bpp;
+ gint dest_bpp;
+
+ src_bpp = babl_format_get_bytes_per_pixel (src_format);
+ dest_bpp = babl_format_get_bytes_per_pixel (channel[i].format);
+
+ iter = gegl_buffer_iterator_new (src_buf,
+ GEGL_RECTANGLE (0, 0, cols, rows),
+ 0, NULL,
+ GEGL_ACCESS_READ,
+ GEGL_ABYSS_NONE, 2);
+ gegl_buffer_iterator_add (iter, channel[i].buffer,
+ GEGL_RECTANGLE (x, y, cols, rows),
+ 0, channel[i].format,
+ GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ guchar *s = iter->items[0].data;
+ guchar *d = iter->items[1].data;
+ gint length = iter->length;
+
+ s += offset;
+
+ while (length--)
+ {
+ memcpy (d, s, dest_bpp);
+ d += dest_bpp;
+ s += src_bpp;
+ }
+ }
+
+ offset += dest_bpp;
+ }
+
+ g_object_unref (src_buf);
+ }
+
+ progress += one_row;
+ }
+
+ g_free (buffer);
+ g_free (bw_buffer);
+}
+
+
+static void
+load_separate (TIFF *tif,
+ ChannelData *channel,
+ const Babl *type,
+ gushort bps,
+ gushort spp,
+ TiffColorMode tiff_mode,
+ gboolean is_signed,
+ gint extra)
+{
+ guint32 image_width;
+ guint32 image_height;
+ guint32 tile_width;
+ guint32 tile_height;
+ gint bytes_per_pixel;
+ const Babl *src_format;
+ guchar *buffer;
+ guchar *bw_buffer = NULL;
+ gdouble progress = 0.0;
+ gdouble one_row;
+ gint i, compindex;
+ gboolean needs_upscale = FALSE;
+
+ g_printerr ("%s\n", __func__);
+
+ TIFFGetField (tif, TIFFTAG_IMAGEWIDTH, &image_width);
+ TIFFGetField (tif, TIFFTAG_IMAGELENGTH, &image_height);
+
+ tile_width = image_width;
+
+ if (TIFFIsTiled (tif))
+ {
+ TIFFGetField (tif, TIFFTAG_TILEWIDTH, &tile_width);
+ TIFFGetField (tif, TIFFTAG_TILELENGTH, &tile_height);
+
+ buffer = g_malloc (TIFFTileSize (tif));
+ }
+ else
+ {
+ tile_width = image_width;
+ tile_height = 1;
+
+ buffer = g_malloc (TIFFScanlineSize (tif));
+ }
+
+ if (tiff_mode != GIMP_TIFF_DEFAULT && bps < 8)
+ {
+ needs_upscale = TRUE;
+ bw_buffer = g_malloc (tile_width * tile_height);
+ }
+
+ one_row = (gdouble) tile_height / (gdouble) image_height;
+
+ src_format = babl_format_n (type, 1);
+
+ /* consistency check */
+ bytes_per_pixel = 0;
+ for (i = 0; i <= extra; i++)
+ bytes_per_pixel += babl_format_get_bytes_per_pixel (channel[i].format);
+
+ g_printerr ("bytes_per_pixel: %d, format: %d\n",
+ bytes_per_pixel,
+ babl_format_get_bytes_per_pixel (src_format));
+
+ compindex = 0;
+
+ for (i = 0; i <= extra; i++)
+ {
+ gint n_comps;
+ gint src_bpp;
+ gint dest_bpp;
+ gint offset;
+ gint j;
+
+ n_comps = babl_format_get_n_components (channel[i].format);
+ src_bpp = babl_format_get_bytes_per_pixel (src_format);
+ dest_bpp = babl_format_get_bytes_per_pixel (channel[i].format);
+
+ offset = 0;
+
+ for (j = 0; j < n_comps; j++)
+ {
+ guint32 y;
+
+ for (y = 0; y < image_height; y += tile_height)
+ {
+ guint32 x;
+
+ for (x = 0; x < image_width; x += tile_width)
+ {
+ GeglBuffer *src_buf;
+ GeglBufferIterator *iter;
+ guint32 rows;
+ guint32 cols;
+
+ gimp_progress_update (progress + one_row *
+ ((gdouble) x / (gdouble) image_width));
+
+ if (TIFFIsTiled (tif))
+ {
+ if (TIFFReadTile (tif, buffer, x, y, 0, compindex) == -1)
+ {
+ g_message (_("Reading tile failed. Image may be corrupt at line %d."), y);
+ g_free (buffer);
+ g_free (bw_buffer);
+ return;
+ }
+ }
+ else if (TIFFReadScanline (tif, buffer, y, compindex) == -1)
+ {
+ /* Error reading scanline, stop loading */
+ g_message (_("Reading scanline failed. Image may be corrupt at line %d."), y);
+ g_free (buffer);
+ g_free (bw_buffer);
+ return;
+ }
+
+ cols = MIN (image_width - x, tile_width);
+ rows = MIN (image_height - y, tile_height);
+
+ if (needs_upscale)
+ {
+ if (bps == 1)
+ convert_bit2byte (buffer, bw_buffer, cols, rows);
+ else if (bps == 2)
+ convert_2bit2byte (buffer, bw_buffer, cols, rows);
+ else if (bps == 4)
+ convert_4bit2byte (buffer, bw_buffer, cols, rows);
+ }
+ else if (is_signed)
+ {
+ convert_int2uint (buffer, bps, 1, cols, rows,
+ tile_width * bytes_per_pixel);
+ }
+
+ if (tiff_mode == GIMP_TIFF_GRAY_MINISWHITE && bps == 8)
+ {
+ convert_miniswhite (buffer, cols, rows);
+ }
+
+ src_buf = gegl_buffer_linear_new_from_data (needs_upscale ? bw_buffer : buffer,
+ src_format,
+ GEGL_RECTANGLE (0, 0, cols, rows),
+ GEGL_AUTO_ROWSTRIDE,
+ NULL, NULL);
+
+ iter = gegl_buffer_iterator_new (src_buf,
+ GEGL_RECTANGLE (0, 0, cols, rows),
+ 0, NULL,
+ GEGL_ACCESS_READ,
+ GEGL_ABYSS_NONE, 2);
+ gegl_buffer_iterator_add (iter, channel[i].buffer,
+ GEGL_RECTANGLE (x, y, cols, rows),
+ 0, channel[i].format,
+ GEGL_ACCESS_READWRITE,
+ GEGL_ABYSS_NONE);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ guchar *s = iter->items[0].data;
+ guchar *d = iter->items[1].data;
+ gint length = iter->length;
+
+ d += offset;
+
+ while (length--)
+ {
+ memcpy (d, s, src_bpp);
+ d += dest_bpp;
+ s += src_bpp;
+ }
+ }
+
+ g_object_unref (src_buf);
+ }
+ }
+
+ offset += src_bpp;
+ compindex++;
+ }
+
+ progress += one_row;
+ }
+
+ g_free (buffer);
+ g_free (bw_buffer);
+}
+
+static void
+fill_bit2byte (TiffColorMode tiff_mode)
+{
+ static gboolean filled = FALSE;
+
+ guchar *dest;
+ gint i, j;
+
+ if (filled)
+ return;
+
+ dest = bit2byte;
+
+ if (tiff_mode == GIMP_TIFF_INDEXED)
+ {
+ for (j = 0; j < 256; j++)
+ for (i = 7; i >= 0; i--)
+ {
+ *(dest++) = ((j & (1 << i)) != 0);
+ }
+ }
+ else if (tiff_mode != GIMP_TIFF_DEFAULT)
+ {
+ guchar *_to_8_bitmap = NULL;
+
+ if (tiff_mode == GIMP_TIFF_GRAY)
+ _to_8_bitmap = (guchar *) &_1_to_8_bitmap;
+ else if (tiff_mode == GIMP_TIFF_GRAY_MINISWHITE)
+ _to_8_bitmap = (guchar *) &_1_to_8_bitmap_rev;
+
+ for (j = 0; j < 256; j++)
+ for (i = 7; i >= 0; i--)
+ {
+ gint idx;
+
+ idx = ((j & (1 << i)) != 0);
+ *(dest++) = _to_8_bitmap[idx];
+ }
+ }
+
+ filled = TRUE;
+}
+
+static void
+fill_2bit2byte (TiffColorMode tiff_mode)
+{
+ static gboolean filled2 = FALSE;
+
+ guchar *dest;
+ gint i, j;
+
+ if (filled2)
+ return;
+
+ dest = _2bit2byte;
+
+ if (tiff_mode == GIMP_TIFF_INDEXED)
+ {
+ for (j = 0; j < 256; j++)
+ {
+ for (i = 3; i >= 0; i--)
+ {
+ *(dest++) = ((j & (3 << (2*i))) >> (2*i));
+ }
+ }
+ }
+ else if (tiff_mode != GIMP_TIFF_DEFAULT)
+ {
+ guchar *_to_8_bitmap = NULL;
+
+ if (tiff_mode == GIMP_TIFF_GRAY)
+ _to_8_bitmap = (guchar *) &_2_to_8_bitmap;
+ else if (tiff_mode == GIMP_TIFF_GRAY_MINISWHITE)
+ _to_8_bitmap = (guchar *) &_2_to_8_bitmap_rev;
+
+ for (j = 0; j < 256; j++)
+ {
+ for (i = 3; i >= 0; i--)
+ {
+ gint idx;
+
+ idx = ((j & (3 << (2*i))) >> (2*i));
+ *(dest++) = _to_8_bitmap[idx];
+ }
+ }
+ }
+
+ filled2 = TRUE;
+}
+
+static void
+fill_4bit2byte (TiffColorMode tiff_mode)
+{
+ static gboolean filled4 = FALSE;
+
+ guchar *dest;
+ gint i, j;
+
+ if (filled4)
+ return;
+
+ dest = _4bit2byte;
+
+ if (tiff_mode == GIMP_TIFF_INDEXED)
+ {
+ for (j = 0; j < 256; j++)
+ {
+ for (i = 1; i >= 0; i--)
+ {
+ *(dest++) = ((j & (15 << (4*i))) >> (4*i));
+ }
+ }
+ }
+ else if (tiff_mode != GIMP_TIFF_DEFAULT)
+ {
+ guchar *_to_8_bitmap = NULL;
+
+ if (tiff_mode == GIMP_TIFF_GRAY)
+ _to_8_bitmap = (guchar *) &_4_to_8_bitmap;
+ else if (tiff_mode == GIMP_TIFF_GRAY_MINISWHITE)
+ _to_8_bitmap = (guchar *) &_4_to_8_bitmap_rev;
+
+ for (j = 0; j < 256; j++)
+ {
+ for (i = 1; i >= 0; i--)
+ {
+ gint idx;
+
+ idx = ((j & (15 << (4*i))) >> (4*i));
+ *(dest++) = _to_8_bitmap[idx];
+ }
+ }
+ }
+
+ filled4 = TRUE;
+}
+
+static void
+convert_bit2byte (const guchar *src,
+ guchar *dest,
+ gint width,
+ gint height)
+{
+ gint64 x = width * height;
+
+ while (x >= 8)
+ {
+ memcpy (dest, bit2byte + *src * 8, 8);
+ dest += 8;
+ x -= 8;
+ src++;
+ }
+
+ if (x > 0)
+ {
+ memcpy (dest, bit2byte + *src * 8, x);
+ dest += x;
+ src++;
+ }
+}
+
+static void
+convert_2bit2byte (const guchar *src,
+ guchar *dest,
+ gint width,
+ gint height)
+{
+ gint64 x = width * height;
+
+ while (x >= 4)
+ {
+ memcpy (dest, _2bit2byte + *src * 4, 4);
+ dest += 4;
+ x -= 4;
+ src++;
+ }
+
+ if (x > 0)
+ {
+ memcpy (dest, _2bit2byte + *src * 4, x);
+ dest += x;
+ src++;
+ }
+}
+
+static void
+convert_4bit2byte (const guchar *src,
+ guchar *dest,
+ gint width,
+ gint height)
+{
+ gint64 x = width * height;
+
+ while (x >= 2)
+ {
+ memcpy (dest, _4bit2byte + *src * 2, 2);
+ dest += 2;
+ x -= 2;
+ src++;
+ }
+
+ if (x > 0)
+ {
+ memcpy (dest, _4bit2byte + *src * 2, x);
+ dest += x;
+ src++;
+ }
+}
+
+static void
+convert_miniswhite (guchar *buffer,
+ gint width,
+ gint height)
+{
+ gint y;
+ guchar *buf = buffer;
+
+ for (y = 0; y < height; y++)
+ {
+ gint x;
+
+ for (x = 0; x < width; x++)
+ {
+ *buf = ~*buf;
+ buf++;
+ }
+ }
+}
+
+static void
+convert_int2uint (guchar *buffer,
+ gint bps,
+ gint spp,
+ gint width,
+ gint height,
+ gint stride)
+{
+ gint bytes_per_pixel = bps / 8;
+ gint y;
+
+ for (y = 0; y < height; y++)
+ {
+ guchar *d = buffer + stride * y;
+ gint x;
+
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+ d += bytes_per_pixel - 1;
+#endif
+
+ for (x = 0; x < width * spp; x++)
+ {
+ *d ^= 0x80;
+
+ d += bytes_per_pixel;
+ }
+ }
+}
+
+static gboolean
+load_dialog (const gchar *help_id,
+ TiffSelectedPages *pages,
+ const gchar *extra_message,
+ DefaultExtra *default_extra)
+{
+ GtkWidget *dialog;
+ GtkWidget *vbox;
+ GtkWidget *show_reduced = NULL;
+ GtkWidget *crop_option = NULL;
+ GtkWidget *extra_radio = NULL;
+ gboolean run;
+
+ pages->selector = NULL;
+
+ dialog = gimp_dialog_new (_("Import from TIFF"), PLUG_IN_ROLE,
+ NULL, 0,
+ gimp_standard_help_func, help_id,
+
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Import"), GTK_RESPONSE_OK,
+
+ NULL);
+
+ gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK,
+ GTK_RESPONSE_CANCEL,
+ -1);
+
+ gimp_window_set_transient (GTK_WINDOW (dialog));
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+ vbox, TRUE, TRUE, 0);
+
+ show_reduced = gtk_check_button_new_with_mnemonic (_("_Show reduced images"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (show_reduced),
+ pages->show_reduced);
+ gtk_box_pack_start (GTK_BOX (vbox), show_reduced, TRUE, TRUE, 0);
+
+ g_signal_connect (show_reduced, "toggled",
+ G_CALLBACK (tiff_dialog_show_reduced),
+ pages);
+
+ if (pages->n_pages > 1)
+ {
+ /* Page Selector */
+ pages->selector = gimp_page_selector_new ();
+ gtk_widget_set_size_request (pages->selector, 300, 200);
+ gtk_box_pack_start (GTK_BOX (vbox), pages->selector, TRUE, TRUE, 0);
+
+ gimp_page_selector_set_n_pages (GIMP_PAGE_SELECTOR (pages->selector),
+ pages->n_filtered_pages);
+ gimp_page_selector_set_target (GIMP_PAGE_SELECTOR (pages->selector),
+ pages->target);
+
+ /* Load a set number of pages, based on whether "Show Reduced Images"
+ * is checked
+ */
+ tiff_dialog_show_reduced (show_reduced, pages);
+
+ g_signal_connect_swapped (pages->selector, "activate",
+ G_CALLBACK (gtk_window_activate_default),
+ dialog);
+
+ /* Option to shrink the loaded image to its bounding box
+ or keep as much empty space as possible.
+ Note that there seems to be no way to keep the empty
+ space on the right and bottom. */
+ crop_option = gtk_check_button_new_with_mnemonic (_("_Keep empty space around imported layers"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (crop_option),
+ pages->keep_empty_space);
+ gtk_box_pack_start (GTK_BOX (vbox), crop_option, TRUE, TRUE, 0);
+ }
+
+ if (extra_message)
+ {
+ GtkWidget *warning;
+
+ warning = g_object_new (GIMP_TYPE_HINT_BOX,
+ "icon-name", GIMP_ICON_DIALOG_WARNING,
+ "hint", extra_message,
+ NULL);
+ gtk_box_pack_start (GTK_BOX (vbox), warning, TRUE, TRUE, 0);
+ gtk_widget_show (warning);
+
+ extra_radio = gimp_int_radio_group_new (TRUE, _("Process extra channel as:"),
+ (GCallback) gimp_radio_button_update,
+ default_extra, GIMP_TIFF_LOAD_UNASSALPHA,
+ _("_Non-premultiplied alpha"), GIMP_TIFF_LOAD_UNASSALPHA, NULL,
+ _("Pre_multiplied alpha"), GIMP_TIFF_LOAD_ASSOCALPHA, NULL,
+ _("Channe_l"), GIMP_TIFF_LOAD_CHANNEL, NULL,
+ NULL);
+ gtk_box_pack_start (GTK_BOX (vbox), extra_radio, TRUE, TRUE, 0);
+ gtk_widget_show (extra_radio);
+ }
+
+ /* Setup done; display the dialog */
+ gtk_widget_show_all (dialog);
+
+ /* run the dialog */
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ if (run)
+ {
+ if (pages->n_pages > 1)
+ {
+ pages->target =
+ gimp_page_selector_get_target (GIMP_PAGE_SELECTOR (pages->selector));
+
+ pages->pages =
+ gimp_page_selector_get_selected_pages (GIMP_PAGE_SELECTOR (pages->selector),
+ &pages->n_pages);
+
+ pages->keep_empty_space =
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (crop_option));
+
+ /* select all if none selected */
+ if (pages->n_pages == 0)
+ {
+ gimp_page_selector_select_all (GIMP_PAGE_SELECTOR (pages->selector));
+
+ pages->pages =
+ gimp_page_selector_get_selected_pages (GIMP_PAGE_SELECTOR (pages->selector),
+ &pages->n_pages);
+ }
+ }
+ }
+
+ return run;
+}
+
+static void
+tiff_dialog_show_reduced (GtkWidget *toggle,
+ gpointer data)
+{
+ gint selectable_pages;
+ gint i, j;
+ TiffSelectedPages *pages = (TiffSelectedPages *) data;
+
+ pages->show_reduced = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (toggle));
+
+ /* Clear current pages from selection */
+ gimp_page_selector_set_n_pages (GIMP_PAGE_SELECTOR (pages->selector), 0);
+ /* Jump back to start of the TIFF file */
+ TIFFSetDirectory (pages->tif, 0);
+
+ selectable_pages = pages->n_filtered_pages;
+ if (pages->show_reduced)
+ selectable_pages = pages->n_reducedimage_pages;
+
+ gimp_page_selector_set_n_pages (GIMP_PAGE_SELECTOR (pages->selector),
+ selectable_pages);
+
+ for (i = 0, j = 0; i < pages->n_pages && j < selectable_pages; i++)
+ {
+ if ((pages->show_reduced && pages->filtered_pages[i] != TIFF_MISC_THUMBNAIL) ||
+ (! pages->show_reduced && pages->filtered_pages[i] > TIFF_MISC_THUMBNAIL))
+ {
+ const gchar *name = tiff_get_page_name (pages->tif);
+
+ if (name)
+ gimp_page_selector_set_page_label (GIMP_PAGE_SELECTOR (pages->selector),
+ j, name);
+ j++;
+ }
+
+ TIFFReadDirectory (pages->tif);
+ }
+}
diff --git a/plug-ins/file-tiff/file-tiff-load.h b/plug-ins/file-tiff/file-tiff-load.h
new file mode 100644
index 0000000..cc97dda
--- /dev/null
+++ b/plug-ins/file-tiff/file-tiff-load.h
@@ -0,0 +1,57 @@
+/* tiff loading for GIMP
+ * -Peter Mattis
+ *
+ * The TIFF loading code has been completely revamped by Nick Lamb
+ * njl195@zepler.org.uk -- 18 May 1998
+ * And it now gains support for tiles (and doubtless a zillion bugs)
+ * njl195@zepler.org.uk -- 12 June 1999
+ * LZW patent fuss continues :(
+ * njl195@zepler.org.uk -- 20 April 2000
+ * The code for this filter is based on "tifftopnm" and "pnmtotiff",
+ * 2 programs that are a part of the netpbm package.
+ * khk@khk.net -- 13 May 2000
+ * Added support for ICCPROFILE tiff tag. If this tag is present in a
+ * TIFF file, then a parasite is created and vice versa.
+ * peter@kirchgessner.net -- 29 Oct 2002
+ * Progress bar only when run interactive
+ * Added support for layer offsets - pablo.dangelo@web.de -- 7 Jan 2004
+ * Honor EXTRASAMPLES tag while loading images with alphachannel
+ * pablo.dangelo@web.de -- 16 Jan 2004
+ */
+
+#ifndef __FILE_TIFF_LOAD_H__
+#define __FILE_TIFF_LOAD_H__
+
+#define LOAD_PROC "file-tiff-load"
+
+typedef enum
+{
+ TIFF_REDUCEDFILE = -2,
+ TIFF_MISC_THUMBNAIL = -1
+} TIFF_THUMBNAIL_TYPE;
+
+typedef struct
+{
+ TIFF *tif;
+ gint o_pages;
+ gint n_pages;
+ gint *pages;
+ gint *filtered_pages; /* thumbnail is marked as < 0 */
+ gint n_filtered_pages;
+ gint n_reducedimage_pages;
+ GtkWidget *selector;
+ GimpPageSelectorTarget target;
+ gboolean keep_empty_space;
+ gboolean show_reduced;
+} TiffSelectedPages;
+
+
+GimpPDBStatusType load_image (GFile *file,
+ GimpRunMode run_mode,
+ gint32 *image,
+ gboolean *resolution_loaded,
+ gboolean *profile_loaded,
+ GError **error);
+
+
+#endif /* __FILE_TIFF_LOAD_H__ */
diff --git a/plug-ins/file-tiff/file-tiff-save.c b/plug-ins/file-tiff/file-tiff-save.c
new file mode 100644
index 0000000..0def177
--- /dev/null
+++ b/plug-ins/file-tiff/file-tiff-save.c
@@ -0,0 +1,1387 @@
+/* tiff exporting for GIMP
+ * -Peter Mattis
+ *
+ * The TIFF loading code has been completely revamped by Nick Lamb
+ * njl195@zepler.org.uk -- 18 May 1998
+ * And it now gains support for tiles (and doubtless a zillion bugs)
+ * njl195@zepler.org.uk -- 12 June 1999
+ * LZW patent fuss continues :(
+ * njl195@zepler.org.uk -- 20 April 2000
+ * The code for this filter is based on "tifftopnm" and "pnmtotiff",
+ * 2 programs that are a part of the netpbm package.
+ * khk@khk.net -- 13 May 2000
+ * Added support for ICCPROFILE tiff tag. If this tag is present in a
+ * TIFF file, then a parasite is created and vice versa.
+ * peter@kirchgessner.net -- 29 Oct 2002
+ * Progress bar only when run interactive
+ * Added support for layer offsets - pablo.dangelo@web.de -- 7 Jan 2004
+ * Honor EXTRASAMPLES tag while loading images with alphachannel
+ * pablo.dangelo@web.de -- 16 Jan 2004
+ */
+
+/*
+ * tifftopnm.c - converts a Tagged Image File to a portable anymap
+ *
+ * Derived by Jef Poskanzer from tif2ras.c, which is:
+ *
+ * Copyright (c) 1990 by Sun Microsystems, Inc.
+ *
+ * Author: Patrick J. Naughton
+ * naughton@wind.sun.com
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation.
+ *
+ * This file is provided AS IS with no warranties of any kind. The author
+ * shall have no liability with respect to the infringement of copyrights,
+ * trade secrets or any patents by this file or any part thereof. In no
+ * event will the author be liable for any lost revenue or profits or
+ * other special, indirect and consequential damages.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+
+#include <tiffio.h>
+#include <gexiv2/gexiv2.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "file-tiff-io.h"
+#include "file-tiff-save.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define PLUG_IN_ROLE "gimp-file-tiff-save"
+
+
+static gboolean save_paths (TIFF *tif,
+ gint32 image,
+ gdouble width,
+ gdouble height,
+ gint offset_x,
+ gint offset_y);
+
+static void comment_entry_callback (GtkWidget *widget,
+ gchar **comment);
+
+static void byte2bit (const guchar *byteline,
+ gint width,
+ guchar *bitline,
+ gboolean invert);
+
+
+static void
+double_to_psd_fixed (gdouble value,
+ gchar *target)
+{
+ gdouble in, frac;
+ gint i, f;
+
+ frac = modf (value, &in);
+ if (frac < 0)
+ {
+ in -= 1;
+ frac += 1;
+ }
+
+ i = (gint) CLAMP (in, -16, 15);
+ f = CLAMP ((gint) (frac * 0xFFFFFF), 0, 0xFFFFFF);
+
+ target[0] = i & 0xFF;
+ target[1] = (f >> 16) & 0xFF;
+ target[2] = (f >> 8) & 0xFF;
+ target[3] = f & 0xFF;
+}
+
+static gboolean
+save_paths (TIFF *tif,
+ gint32 image,
+ gdouble width,
+ gdouble height,
+ gint offset_x,
+ gint offset_y)
+{
+ gint id = 2000; /* Photoshop paths have IDs >= 2000 */
+ gint num_vectors, *vectors, v;
+ gint num_strokes, *strokes, s;
+ GString *ps_tag;
+
+ vectors = gimp_image_get_vectors (image, &num_vectors);
+
+ if (num_vectors <= 0)
+ return FALSE;
+
+ ps_tag = g_string_new ("");
+
+ /* Only up to 1000 paths supported */
+ for (v = 0; v < MIN (num_vectors, 1000); v++)
+ {
+ GString *data;
+ gchar *name, *nameend;
+ gsize len;
+ gint lenpos;
+ gchar pointrecord[26] = { 0, };
+ gchar *tmpname;
+ GError *err = NULL;
+
+ data = g_string_new ("8BIM");
+ g_string_append_c (data, id / 256);
+ g_string_append_c (data, id % 256);
+
+ /*
+ * - use iso8859-1 if possible
+ * - otherwise use UTF-8, prepended with \xef\xbb\xbf (Byte-Order-Mark)
+ */
+ name = gimp_item_get_name (vectors[v]);
+ tmpname = g_convert (name, -1, "iso8859-1", "utf-8", NULL, &len, &err);
+
+ if (tmpname && err == NULL)
+ {
+ g_string_append_c (data, MIN (len, 255));
+ g_string_append_len (data, tmpname, MIN (len, 255));
+ g_free (tmpname);
+ }
+ else
+ {
+ /* conversion failed, we fall back to UTF-8 */
+ len = g_utf8_strlen (name, 255 - 3); /* need three marker-bytes */
+
+ nameend = g_utf8_offset_to_pointer (name, len);
+ len = nameend - name; /* in bytes */
+ g_assert (len + 3 <= 255);
+
+ g_string_append_c (data, len + 3);
+ g_string_append_len (data, "\xEF\xBB\xBF", 3); /* Unicode 0xfeff */
+ g_string_append_len (data, name, len);
+
+ if (tmpname)
+ g_free (tmpname);
+ }
+
+ if (data->len % 2) /* padding to even size */
+ g_string_append_c (data, 0);
+ g_free (name);
+
+ lenpos = data->len;
+ g_string_append_len (data, "\0\0\0\0", 4); /* will be filled in later */
+ len = data->len; /* to calculate the data size later */
+
+ pointrecord[1] = 6; /* fill rule record */
+ g_string_append_len (data, pointrecord, 26);
+
+ strokes = gimp_vectors_get_strokes (vectors[v], &num_strokes);
+
+ for (s = 0; s < num_strokes; s++)
+ {
+ GimpVectorsStrokeType type;
+ gdouble *points;
+ gint num_points;
+ gboolean closed;
+ gint p = 0;
+
+ type = gimp_vectors_stroke_get_points (vectors[v], strokes[s],
+ &num_points, &points, &closed);
+
+ if (type != GIMP_VECTORS_STROKE_TYPE_BEZIER ||
+ num_points > 65535 ||
+ num_points % 6)
+ {
+ g_printerr ("tiff-save: unsupported stroke type: "
+ "%d (%d points)\n", type, num_points);
+ continue;
+ }
+
+ memset (pointrecord, 0, 26);
+ pointrecord[1] = closed ? 0 : 3;
+ pointrecord[2] = (num_points / 6) / 256;
+ pointrecord[3] = (num_points / 6) % 256;
+ g_string_append_len (data, pointrecord, 26);
+
+ for (p = 0; p < num_points; p += 6)
+ {
+ pointrecord[1] = closed ? 2 : 5;
+
+ double_to_psd_fixed ((points[p+1] - offset_y) / height, pointrecord + 2);
+ double_to_psd_fixed ((points[p+0] - offset_x) / width, pointrecord + 6);
+ double_to_psd_fixed ((points[p+3] - offset_y) / height, pointrecord + 10);
+ double_to_psd_fixed ((points[p+2] - offset_x) / width, pointrecord + 14);
+ double_to_psd_fixed ((points[p+5] - offset_y) / height, pointrecord + 18);
+ double_to_psd_fixed ((points[p+4] - offset_x) / width, pointrecord + 22);
+
+ g_string_append_len (data, pointrecord, 26);
+ }
+ }
+
+ g_free (strokes);
+
+ /* fix up the length */
+
+ len = data->len - len;
+ data->str[lenpos + 0] = (len & 0xFF000000) >> 24;
+ data->str[lenpos + 1] = (len & 0x00FF0000) >> 16;
+ data->str[lenpos + 2] = (len & 0x0000FF00) >> 8;
+ data->str[lenpos + 3] = (len & 0x000000FF) >> 0;
+
+ g_string_append_len (ps_tag, data->str, data->len);
+ g_string_free (data, TRUE);
+ id ++;
+ }
+
+ TIFFSetField (tif, TIFFTAG_PHOTOSHOP, ps_tag->len, ps_tag->str);
+ g_string_free (ps_tag, TRUE);
+
+ g_free (vectors);
+
+ return TRUE;
+}
+
+/*
+ * pnmtotiff.c - converts a portable anymap to a Tagged Image File
+ *
+ * Derived by Jef Poskanzer from ras2tif.c, which is:
+ *
+ * Copyright (c) 1990 by Sun Microsystems, Inc.
+ *
+ * Author: Patrick J. Naughton
+ * naughton@wind.sun.com
+ *
+ * This file is provided AS IS with no warranties of any kind. The author
+ * shall have no liability with respect to the infringement of copyrights,
+ * trade secrets or any patents by this file or any part thereof. In no
+ * event will the author be liable for any lost revenue or profits or
+ * other special, indirect and consequential damages.
+ */
+
+static gboolean
+save_layer (TIFF *tif,
+ TiffSaveVals *tsvals,
+ gint32 image,
+ gint32 layer,
+ gint32 page,
+ gint32 num_pages,
+ gint32 orig_image, /* the export function might */
+ /* have created a duplicate */
+ gint origin_x,
+ gint origin_y,
+ gint *saved_bpp,
+ gboolean out_linear,
+ GError **error)
+{
+ gboolean status = FALSE;
+ gushort red[256];
+ gushort grn[256];
+ gushort blu[256];
+ gint cols, rows, row, i;
+ glong rowsperstrip;
+ gushort compression;
+ gushort extra_samples[1];
+ gboolean alpha;
+ gshort predictor;
+ gshort photometric;
+ const Babl *format;
+ const Babl *type;
+ gshort samplesperpixel;
+ gshort bitspersample;
+ gshort sampleformat;
+ gint bytesperrow;
+ guchar *src = NULL;
+ guchar *data = NULL;
+ guchar *cmap;
+ gint num_colors;
+ gint success;
+ GimpImageType drawable_type;
+ GeglBuffer *buffer = NULL;
+ gint tile_height;
+ gint y, yend;
+ gboolean is_bw = FALSE;
+ gboolean invert = TRUE;
+ const guchar bw_map[] = { 0, 0, 0, 255, 255, 255 };
+ const guchar wb_map[] = { 255, 255, 255, 0, 0, 0 };
+ gchar *layer_name = NULL;
+ const gdouble progress_base = (gdouble) page / (gdouble) num_pages;
+ const gdouble progress_fraction = 1.0 / (gdouble) num_pages;
+ gdouble xresolution;
+ gdouble yresolution;
+ gushort save_unit = RESUNIT_INCH;
+ gint offset_x, offset_y;
+
+ compression = tsvals->compression;
+
+ layer_name = gimp_item_get_name (layer);
+
+ /* Disabled because this isn't in older releases of libtiff, and it
+ * wasn't helping much anyway
+ */
+#if 0
+ if (TIFFFindCODEC((uint16) compression) == NULL)
+ compression = COMPRESSION_NONE; /* CODEC not available */
+#endif
+
+ predictor = 0;
+ tile_height = gimp_tile_height ();
+ rowsperstrip = tile_height;
+
+ drawable_type = gimp_drawable_type (layer);
+ buffer = gimp_drawable_get_buffer (layer);
+
+ format = gegl_buffer_get_format (buffer);
+ type = babl_format_get_type (format, 0);
+
+ switch (gimp_image_get_precision (image))
+ {
+ case GIMP_PRECISION_U8_LINEAR:
+ case GIMP_PRECISION_U8_GAMMA:
+ /* Promote to 16-bit if storage and export TRC don't match. */
+ if ((gimp_image_get_precision (image) == GIMP_PRECISION_U8_LINEAR && out_linear) ||
+ (gimp_image_get_precision (image) != GIMP_PRECISION_U8_LINEAR && ! out_linear))
+ {
+ bitspersample = 8;
+ sampleformat = SAMPLEFORMAT_UINT;
+ }
+ else
+ {
+ bitspersample = 16;
+ sampleformat = SAMPLEFORMAT_UINT;
+ type = babl_type ("u16");
+ }
+ break;
+
+ case GIMP_PRECISION_U16_LINEAR:
+ case GIMP_PRECISION_U16_GAMMA:
+ bitspersample = 16;
+ sampleformat = SAMPLEFORMAT_UINT;
+ break;
+
+ case GIMP_PRECISION_U32_LINEAR:
+ case GIMP_PRECISION_U32_GAMMA:
+ bitspersample = 32;
+ sampleformat = SAMPLEFORMAT_UINT;
+ break;
+
+ case GIMP_PRECISION_HALF_LINEAR:
+ case GIMP_PRECISION_HALF_GAMMA:
+ bitspersample = 16;
+ sampleformat = SAMPLEFORMAT_IEEEFP;
+ break;
+
+ default:
+ case GIMP_PRECISION_FLOAT_LINEAR:
+ case GIMP_PRECISION_FLOAT_GAMMA:
+ bitspersample = 32;
+ sampleformat = SAMPLEFORMAT_IEEEFP;
+ break;
+
+ case GIMP_PRECISION_DOUBLE_LINEAR:
+ case GIMP_PRECISION_DOUBLE_GAMMA:
+ bitspersample = 64;
+ sampleformat = SAMPLEFORMAT_IEEEFP;
+ break;
+ }
+
+ *saved_bpp = bitspersample;
+
+ cols = gegl_buffer_get_width (buffer);
+ rows = gegl_buffer_get_height (buffer);
+
+ switch (drawable_type)
+ {
+ case GIMP_RGB_IMAGE:
+ predictor = 2;
+ samplesperpixel = 3;
+ photometric = PHOTOMETRIC_RGB;
+ alpha = FALSE;
+ if (out_linear)
+ {
+ format = babl_format_new (babl_model ("RGB"),
+ type,
+ babl_component ("R"),
+ babl_component ("G"),
+ babl_component ("B"),
+ NULL);
+ }
+ else
+ {
+ format = babl_format_new (babl_model ("R'G'B'"),
+ type,
+ babl_component ("R'"),
+ babl_component ("G'"),
+ babl_component ("B'"),
+ NULL);
+ }
+ break;
+
+ case GIMP_GRAY_IMAGE:
+ samplesperpixel = 1;
+ photometric = PHOTOMETRIC_MINISBLACK;
+ alpha = FALSE;
+ if (out_linear)
+ {
+ format = babl_format_new (babl_model ("Y"),
+ type,
+ babl_component ("Y"),
+ NULL);
+ }
+ else
+ {
+ format = babl_format_new (babl_model ("Y'"),
+ type,
+ babl_component ("Y'"),
+ NULL);
+ }
+ break;
+
+ case GIMP_RGBA_IMAGE:
+ predictor = 2;
+ samplesperpixel = 4;
+ photometric = PHOTOMETRIC_RGB;
+ alpha = TRUE;
+ if (tsvals->save_transp_pixels)
+ {
+ if (out_linear)
+ {
+ format = babl_format_new (babl_model ("RGBA"),
+ type,
+ babl_component ("R"),
+ babl_component ("G"),
+ babl_component ("B"),
+ babl_component ("A"),
+ NULL);
+ }
+ else
+ {
+ format = babl_format_new (babl_model ("R'G'B'A"),
+ type,
+ babl_component ("R'"),
+ babl_component ("G'"),
+ babl_component ("B'"),
+ babl_component ("A"),
+ NULL);
+ }
+ }
+ else
+ {
+ if (out_linear)
+ {
+ format = babl_format_new (babl_model ("RaGaBaA"),
+ type,
+ babl_component ("Ra"),
+ babl_component ("Ga"),
+ babl_component ("Ba"),
+ babl_component ("A"),
+ NULL);
+ }
+ else
+ {
+ format = babl_format_new (babl_model ("R'aG'aB'aA"),
+ type,
+ babl_component ("R'a"),
+ babl_component ("G'a"),
+ babl_component ("B'a"),
+ babl_component ("A"),
+ NULL);
+ }
+ }
+ break;
+
+ case GIMP_GRAYA_IMAGE:
+ samplesperpixel = 2;
+ photometric = PHOTOMETRIC_MINISBLACK;
+ alpha = TRUE;
+ if (tsvals->save_transp_pixels)
+ {
+ if (out_linear)
+ {
+ format = babl_format_new (babl_model ("YA"),
+ type,
+ babl_component ("Y"),
+ babl_component ("A"),
+ NULL);
+ }
+ else
+ {
+ format = babl_format_new (babl_model ("Y'A"),
+ type,
+ babl_component ("Y'"),
+ babl_component ("A"),
+ NULL);
+ }
+ }
+ else
+ {
+ if (out_linear)
+ {
+ format = babl_format_new (babl_model ("YaA"),
+ type,
+ babl_component ("Ya"),
+ babl_component ("A"),
+ NULL);
+ }
+ else
+ {
+ format = babl_format_new (babl_model ("Y'aA"),
+ type,
+ babl_component ("Y'a"),
+ babl_component ("A"),
+ NULL);
+ }
+ }
+ break;
+
+ case GIMP_INDEXED_IMAGE:
+ case GIMP_INDEXEDA_IMAGE:
+ cmap = gimp_image_get_colormap (image, &num_colors);
+
+ if (num_colors == 2 || num_colors == 1)
+ {
+ is_bw = (memcmp (cmap, bw_map, 3 * num_colors) == 0);
+ photometric = PHOTOMETRIC_MINISWHITE;
+
+ if (!is_bw)
+ {
+ is_bw = (memcmp (cmap, wb_map, 3 * num_colors) == 0);
+
+ if (is_bw)
+ invert = FALSE;
+ }
+ }
+
+ if (is_bw)
+ {
+ bitspersample = 1;
+ }
+ else
+ {
+ bitspersample = 8;
+ photometric = PHOTOMETRIC_PALETTE;
+
+ for (i = 0; i < num_colors; i++)
+ {
+ red[i] = cmap[i * 3 + 0] * 65535 / 255;
+ grn[i] = cmap[i * 3 + 1] * 65535 / 255;
+ blu[i] = cmap[i * 3 + 2] * 65535 / 255;
+ }
+ }
+
+ samplesperpixel = (drawable_type == GIMP_INDEXEDA_IMAGE) ? 2 : 1;
+ bytesperrow = cols;
+ alpha = (drawable_type == GIMP_INDEXEDA_IMAGE);
+ format = gimp_drawable_get_format (layer);
+
+ g_free (cmap);
+ break;
+
+ default:
+ goto out;
+ }
+
+ bytesperrow = cols * babl_format_get_bytes_per_pixel (format);
+
+ if (compression == COMPRESSION_CCITTFAX3 ||
+ compression == COMPRESSION_CCITTFAX4)
+ {
+ if (bitspersample != 1 || samplesperpixel != 1)
+ {
+ const gchar *msg = _("Only monochrome pictures can be compressed "
+ "with \"CCITT Group 4\" or \"CCITT Group 3\".");
+
+ g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, msg);
+
+ goto out;
+ }
+ }
+
+ if (compression == COMPRESSION_JPEG)
+ {
+ if (gimp_image_base_type (image) == GIMP_INDEXED)
+ {
+ g_set_error_literal (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Indexed pictures cannot be compressed "
+ "with \"JPEG\"."));
+ goto out;
+ }
+ }
+
+
+ /* Set TIFF parameters. */
+ if (num_pages > 1)
+ {
+ TIFFSetField (tif, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE);
+ TIFFSetField (tif, TIFFTAG_PAGENUMBER, page, num_pages);
+ }
+ TIFFSetField (tif, TIFFTAG_PAGENAME, layer_name);
+ TIFFSetField (tif, TIFFTAG_IMAGEWIDTH, cols);
+ TIFFSetField (tif, TIFFTAG_IMAGELENGTH, rows);
+ TIFFSetField (tif, TIFFTAG_BITSPERSAMPLE, bitspersample);
+ TIFFSetField (tif, TIFFTAG_SAMPLEFORMAT, sampleformat);
+ TIFFSetField (tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
+ TIFFSetField (tif, TIFFTAG_COMPRESSION, compression);
+
+ if ((compression == COMPRESSION_LZW ||
+ compression == COMPRESSION_ADOBE_DEFLATE) &&
+ (predictor != 0))
+ {
+ TIFFSetField (tif, TIFFTAG_PREDICTOR, predictor);
+ }
+
+ if (alpha)
+ {
+ if (tsvals->save_transp_pixels ||
+ /* Associated alpha, hence premultiplied components is
+ * meaningless for palette images with transparency in TIFF
+ * format, since alpha is set per pixel, not per color (so a
+ * given color could be set to different alpha on different
+ * pixels, hence it cannot be premultiplied).
+ */
+ drawable_type == GIMP_INDEXEDA_IMAGE)
+ extra_samples [0] = EXTRASAMPLE_UNASSALPHA;
+ else
+ extra_samples [0] = EXTRASAMPLE_ASSOCALPHA;
+
+ TIFFSetField (tif, TIFFTAG_EXTRASAMPLES, 1, extra_samples);
+ }
+
+ TIFFSetField (tif, TIFFTAG_PHOTOMETRIC, photometric);
+ TIFFSetField (tif, TIFFTAG_SAMPLESPERPIXEL, samplesperpixel);
+ TIFFSetField (tif, TIFFTAG_ROWSPERSTRIP, rowsperstrip);
+ /* TIFFSetField( tif, TIFFTAG_STRIPBYTECOUNTS, rows / rowsperstrip ); */
+ TIFFSetField (tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
+
+ /* resolution fields */
+ gimp_image_get_resolution (orig_image, &xresolution, &yresolution);
+
+ if (gimp_unit_is_metric (gimp_image_get_unit (orig_image)))
+ {
+ save_unit = RESUNIT_CENTIMETER;
+ xresolution /= 2.54;
+ yresolution /= 2.54;
+ }
+
+ if (xresolution > 1e-5 && yresolution > 1e-5)
+ {
+ TIFFSetField (tif, TIFFTAG_XRESOLUTION, xresolution);
+ TIFFSetField (tif, TIFFTAG_YRESOLUTION, yresolution);
+ TIFFSetField (tif, TIFFTAG_RESOLUTIONUNIT, save_unit);
+ }
+
+ gimp_drawable_offsets (layer, &offset_x, &offset_y);
+
+ offset_x -= origin_x;
+ offset_y -= origin_y;
+
+ if (offset_x || offset_y)
+ {
+ TIFFSetField (tif, TIFFTAG_XPOSITION, offset_x / xresolution);
+ TIFFSetField (tif, TIFFTAG_YPOSITION, offset_y / yresolution);
+ }
+
+ if (! is_bw &&
+ (drawable_type == GIMP_INDEXED_IMAGE || drawable_type == GIMP_INDEXEDA_IMAGE))
+ TIFFSetField (tif, TIFFTAG_COLORMAP, red, grn, blu);
+
+ /* save path data. we need layer information for that,
+ * so we have to do this in here. :-( */
+ if (page == 0)
+ save_paths (tif, orig_image, cols, rows, offset_x, offset_y);
+
+ /* array to rearrange data */
+ src = g_new (guchar, bytesperrow * tile_height);
+ data = g_new (guchar, bytesperrow);
+
+ /* Now write the TIFF data. */
+ for (y = 0; y < rows; y = yend)
+ {
+ yend = y + tile_height;
+ yend = MIN (yend, rows);
+
+ gegl_buffer_get (buffer,
+ GEGL_RECTANGLE (0, y, cols, yend - y), 1.0,
+ format, src,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ for (row = y; row < yend; row++)
+ {
+ guchar *t = src + bytesperrow * (row - y);
+
+ switch (drawable_type)
+ {
+ case GIMP_INDEXED_IMAGE:
+ case GIMP_INDEXEDA_IMAGE:
+ if (is_bw)
+ {
+ byte2bit (t, bytesperrow, data, invert);
+ success = (TIFFWriteScanline (tif, data, row, 0) >= 0);
+ }
+ else
+ {
+ success = (TIFFWriteScanline (tif, t, row, 0) >= 0);
+ }
+ break;
+
+ case GIMP_GRAY_IMAGE:
+ case GIMP_GRAYA_IMAGE:
+ case GIMP_RGB_IMAGE:
+ case GIMP_RGBA_IMAGE:
+ success = (TIFFWriteScanline (tif, t, row, 0) >= 0);
+ break;
+
+ default:
+ success = FALSE;
+ break;
+ }
+
+ if (! success)
+ {
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Failed a scanline write on row %d"), row);
+ goto out;
+ }
+ }
+
+ if ((row % 32) == 0)
+ gimp_progress_update (progress_base + progress_fraction
+ * (gdouble) row / (gdouble) rows);
+ }
+
+ /* Save GeoTIFF tags to file, if available */
+ if (tsvals->save_geotiff)
+ {
+ GimpParasite *parasite = NULL;
+
+ parasite = gimp_image_get_parasite (image,"Gimp_GeoTIFF_ModelPixelScale");
+
+ if (parasite)
+ {
+ TIFFSetField (tif,
+ GEOTIFF_MODELPIXELSCALE,
+ (gimp_parasite_data_size (parasite) / TIFFDataWidth (TIFF_DOUBLE)),
+ gimp_parasite_data (parasite));
+ gimp_parasite_free (parasite);
+ }
+
+ parasite = gimp_image_get_parasite (image,"Gimp_GeoTIFF_ModelTiePoint");
+ if (parasite)
+ {
+ TIFFSetField (tif,
+ GEOTIFF_MODELTIEPOINT,
+ (gimp_parasite_data_size (parasite) / TIFFDataWidth (TIFF_DOUBLE)),
+ gimp_parasite_data (parasite));
+ gimp_parasite_free (parasite);
+ }
+
+ parasite = gimp_image_get_parasite (image,"Gimp_GeoTIFF_ModelTransformation");
+ if (parasite)
+ {
+ TIFFSetField (tif,
+ GEOTIFF_MODELTRANSFORMATION,
+ (gimp_parasite_data_size (parasite) / TIFFDataWidth (TIFF_DOUBLE)),
+ gimp_parasite_data (parasite));
+ gimp_parasite_free (parasite);
+ }
+
+ parasite = gimp_image_get_parasite (image,"Gimp_GeoTIFF_KeyDirectory");
+ if (parasite)
+ {
+ TIFFSetField (tif,
+ GEOTIFF_KEYDIRECTORY,
+ (gimp_parasite_data_size (parasite) / TIFFDataWidth (TIFF_SHORT)),
+ gimp_parasite_data (parasite));
+ gimp_parasite_free (parasite);
+ }
+
+ parasite = gimp_image_get_parasite (image,"Gimp_GeoTIFF_DoubleParams");
+ if (parasite)
+ {
+ TIFFSetField (tif,
+ GEOTIFF_DOUBLEPARAMS,
+ (gimp_parasite_data_size (parasite) / TIFFDataWidth (TIFF_DOUBLE)),
+ gimp_parasite_data (parasite));
+ gimp_parasite_free (parasite);
+ }
+
+ parasite = gimp_image_get_parasite (image,"Gimp_GeoTIFF_Asciiparams");
+ if (parasite)
+ {
+ TIFFSetField (tif,
+ GEOTIFF_ASCIIPARAMS,
+ gimp_parasite_data (parasite));
+ gimp_parasite_free (parasite);
+ }
+ }
+
+ TIFFWriteDirectory (tif);
+
+ gimp_progress_update (progress_base + progress_fraction);
+
+ status = TRUE;
+
+out:
+ if (buffer)
+ g_object_unref (buffer);
+
+ g_free (data);
+ g_free (src);
+ g_free (layer_name);
+
+ return status;
+}
+
+/* FIXME Most of the stuff in save_metadata except the
+ * thumbnail saving should probably be moved to
+ * gimpmetadata.c and gimpmetadata-save.c.
+ */
+static void
+save_metadata (GFile *file,
+ TiffSaveVals *tsvals,
+ gint32 image,
+ GimpMetadata *metadata,
+ GimpMetadataSaveFlags metadata_flags,
+ gint saved_bpp)
+{
+ gchar **exif_tags;
+
+ /* See bug 758909: clear TIFFTAG_MIN/MAXSAMPLEVALUE because
+ * exiv2 saves them with wrong type and the original values
+ * could be invalid, see also bug 761823.
+ * we also clear some other tags that were only meaningful
+ * for the original imported image.
+ */
+ static const gchar *exif_tags_to_remove[] =
+ {
+ "Exif.Image.0x0118", /* MinSampleValue */
+ "Exif.Image.0x0119", /* MaxSampleValue */
+ "Exif.Image.0x011d", /* PageName */
+ "Exif.Image.Compression",
+ "Exif.Image.FillOrder",
+ "Exif.Image.InterColorProfile",
+ "Exif.Image.NewSubfileType",
+ "Exif.Image.PageNumber",
+ "Exif.Image.PhotometricInterpretation",
+ "Exif.Image.PlanarConfiguration",
+ "Exif.Image.Predictor",
+ "Exif.Image.RowsPerStrip",
+ "Exif.Image.SampleFormat",
+ "Exif.Image.SamplesPerPixel",
+ "Exif.Image.StripByteCounts",
+ "Exif.Image.StripOffsets"
+ };
+ static const guint n_keys = G_N_ELEMENTS(exif_tags_to_remove);
+
+ for (int k = 0; k < n_keys; k++)
+ {
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (metadata),
+ exif_tags_to_remove[k]);
+ }
+
+ /* get rid of all the EXIF tags for anything but the first sub image. */
+ exif_tags = gexiv2_metadata_get_exif_tags (GEXIV2_METADATA(metadata));
+ for (char **tag = exif_tags; *tag; tag++)
+ {
+ /* Keeping Exif.Image2, 3 can cause exiv2 to save faulty extra TIFF pages
+ * that are empty except for the Exif metadata. See issue #7195. */
+ if (g_str_has_prefix (*tag, "Exif.Image")
+ && (*tag)[strlen ("Exif.Image")] >= '0'
+ && (*tag)[strlen ("Exif.Image")] <= '9')
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (metadata), *tag);
+ if (g_str_has_prefix (*tag, "Exif.SubImage")
+ && (*tag)[strlen ("Exif.SubImage")] >= '0'
+ && (*tag)[strlen ("Exif.SubImage")] <= '9')
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (metadata), *tag);
+ if (g_str_has_prefix (*tag, "Exif.Thumbnail"))
+ gexiv2_metadata_clear_tag (GEXIV2_METADATA (metadata), *tag);
+ }
+
+ gimp_metadata_set_bits_per_sample (metadata, saved_bpp);
+
+ if (tsvals->save_exif)
+ metadata_flags |= GIMP_METADATA_SAVE_EXIF;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_EXIF;
+
+ if (tsvals->save_xmp)
+ metadata_flags |= GIMP_METADATA_SAVE_XMP;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_XMP;
+
+ if (tsvals->save_iptc)
+ metadata_flags |= GIMP_METADATA_SAVE_IPTC;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_IPTC;
+
+ if (tsvals->save_thumbnail)
+ metadata_flags |= GIMP_METADATA_SAVE_THUMBNAIL;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_THUMBNAIL;
+
+ if (tsvals->save_profile)
+ metadata_flags |= GIMP_METADATA_SAVE_COLOR_PROFILE;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_COLOR_PROFILE;
+
+ gimp_image_metadata_save_finish (image,
+ "image/tiff",
+ metadata, metadata_flags,
+ file, NULL);
+}
+
+gboolean
+save_image (GFile *file,
+ TiffSaveVals *tsvals,
+ gint32 image,
+ gint32 orig_image, /* the export function */
+ /* might have created */
+ /* a duplicate */
+ const gchar *image_comment,
+ gint *saved_bpp,
+ GimpMetadata *metadata,
+ GimpMetadataSaveFlags metadata_flags,
+ GError **error)
+{
+ TIFF *tif = NULL;
+ gboolean status = FALSE;
+ gboolean out_linear = FALSE;
+ gint32 num_layers, *layers, current_layer = 0;
+ gint origin_x = 0, origin_y = 0;
+ gint32 i;
+
+ layers = gimp_image_get_layers (image, &num_layers);
+
+ gimp_progress_init_printf (_("Exporting '%s'"),
+ gimp_file_get_utf8_name (file));
+
+ /* Open file and write some global data */
+#ifdef TIFF_VERSION_BIG
+ tif = tiff_open (file, (tsvals->bigtiff ? "w8" : "w"), error);
+#else
+ tif = tiff_open (file, "w", error);
+#endif
+
+ if (! tif)
+ {
+ if (! error)
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for writing: %s"),
+ gimp_file_get_utf8_name (file), g_strerror (errno));
+ goto out;
+ }
+
+ /* The TIFF spec explicitly says ASCII for the image description. */
+ if (image_comment)
+ {
+ const gchar *c = image_comment;
+ gint len;
+
+ for (len = strlen (c); len; c++, len--)
+ {
+ if ((guchar) *c > 127)
+ {
+ g_message (_("The TIFF format only supports comments in\n"
+ "7bit ASCII encoding. No comment is saved."));
+ image_comment = NULL;
+
+ break;
+ }
+ }
+ }
+
+ /* do we have a comment? If so, create a new parasite to hold it,
+ * and attach it to the image. The attach function automatically
+ * detaches a previous incarnation of the parasite. */
+ if (image_comment && *image_comment)
+ {
+ GimpParasite *parasite;
+
+ TIFFSetField (tif, TIFFTAG_IMAGEDESCRIPTION, image_comment);
+ parasite = gimp_parasite_new ("gimp-comment",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (image_comment) + 1, image_comment);
+ gimp_image_attach_parasite (orig_image, parasite);
+ gimp_parasite_free (parasite);
+ }
+
+#ifdef TIFFTAG_ICCPROFILE
+ if (tsvals->save_profile)
+ {
+ GimpColorProfile *profile;
+ const guint8 *icc_data;
+ gsize icc_length;
+
+ profile = gimp_image_get_effective_color_profile (orig_image);
+
+ /* Curve of the exported data depends on the saved profile, i.e.
+ * any explicitly-set profile in priority, or the default one for
+ * the storage format as fallback.
+ */
+ out_linear = (gimp_color_profile_is_linear (profile));
+
+ /* Write the profile to the TIFF file. */
+ icc_data = gimp_color_profile_get_icc_profile (profile, &icc_length);
+ TIFFSetField (tif, TIFFTAG_ICCPROFILE, icc_length, icc_data);
+ g_object_unref (profile);
+ }
+#endif
+
+ /* calculate the top-left coordinates */
+ for (i = 0; i < num_layers; i++)
+ {
+ gint offset_x, offset_y;
+
+ gimp_drawable_offsets (layers[i], &offset_x, &offset_y);
+
+ origin_x = MIN (origin_x, offset_x);
+ origin_y = MIN (origin_y, offset_y);
+ }
+
+ /* write last layer as first page. */
+ if (! save_layer (tif, tsvals, image,
+ layers[num_layers - current_layer - 1],
+ current_layer, num_layers, orig_image,
+ origin_x, origin_y,
+ saved_bpp, out_linear, error))
+ {
+ goto out;
+ }
+ current_layer++;
+
+ /* close file so we can safely let exiv2 work on it to write metadata.
+ * this can be simplified once multi page TIFF is supported by exiv2
+ */
+ TIFFFlushData (tif);
+ TIFFClose (tif);
+ tif = NULL;
+ if (metadata)
+ save_metadata (file, tsvals, image, metadata, metadata_flags, *saved_bpp);
+
+ /* write the remaining layers */
+ if (num_layers > 1)
+ {
+ tif = tiff_open (file, "a", error);
+
+ if (! tif)
+ {
+ if (! error)
+ g_set_error (error, G_FILE_ERROR,
+ g_file_error_from_errno (errno),
+ _("Could not open '%s' for writing: %s"),
+ gimp_file_get_utf8_name (file),
+ g_strerror (errno));
+ goto out;
+ }
+
+ for (; current_layer < num_layers; current_layer++)
+ {
+ gint tmp_saved_bpp;
+ if (! save_layer (tif, tsvals, image,
+ layers[num_layers - current_layer - 1],
+ current_layer, num_layers, orig_image,
+ origin_x, origin_y,
+ &tmp_saved_bpp, out_linear, error))
+ {
+ goto out;
+ }
+ if (tmp_saved_bpp != *saved_bpp)
+ {
+ /* this should never happen.
+ * if it does, decide if it's really an error.
+ */
+ g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
+ _("Writing pages with different bit depth "
+ "is strange."));
+ goto out;
+ }
+ gimp_progress_update ((gdouble) (current_layer + 1) / num_layers);
+ }
+ }
+
+ status = TRUE;
+
+out:
+ /* close the file for good */
+ if (tif)
+ {
+ TIFFFlushData (tif);
+ TIFFClose (tif);
+ }
+
+ gimp_progress_update (1.0);
+
+ return status;
+}
+
+gboolean
+save_dialog (TiffSaveVals *tsvals,
+ gint32 image,
+ const gchar *help_id,
+ gboolean has_alpha,
+ gboolean is_monochrome,
+ gboolean is_indexed,
+ gchar **image_comment,
+ GError **error,
+ gboolean classic_tiff_failed)
+{
+ GtkWidget *dialog;
+ GtkWidget *vbox;
+ GtkWidget *frame;
+ GtkWidget *entry;
+ GtkWidget *toggle;
+ GtkWidget *label;
+ GtkWidget *cmp_g3;
+ GtkWidget *cmp_g4;
+ GtkWidget *cmp_jpeg;
+ GtkBuilder *builder;
+ gchar *ui_file;
+ gchar **parasites;
+ gboolean run;
+ gboolean has_geotiff = FALSE;
+ gint n_parasites;
+ gint i;
+
+ parasites = gimp_image_get_parasite_list (image, &n_parasites);
+ for (i = 0; i < n_parasites; i++)
+ {
+ if (g_str_has_prefix (parasites[i], "Gimp_GeoTIFF_"))
+ {
+ has_geotiff = TRUE;
+ break;
+ }
+ }
+ g_strfreev (parasites);
+
+ dialog = gimp_export_dialog_new (_("TIFF"), PLUG_IN_ROLE, help_id);
+
+ builder = gtk_builder_new ();
+ ui_file = g_build_filename (gimp_data_directory (),
+ "ui", "plug-ins", "plug-in-file-tiff.ui",
+ NULL);
+ if (! gtk_builder_add_from_file (builder, ui_file, error))
+ {
+ gchar *display_name = g_filename_display_name (ui_file);
+
+ g_printerr (_("Error loading UI file '%s': %s"),
+ display_name, error ? (*error)->message : _("Unknown error"));
+
+ g_free (display_name);
+ }
+
+ g_free (ui_file);
+
+ vbox = GTK_WIDGET (gtk_builder_get_object (builder, "tiff_export_vbox"));
+
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+ vbox, FALSE, FALSE, 0);
+ gtk_widget_show (vbox);
+
+ vbox = GTK_WIDGET (gtk_builder_get_object (builder, "radio_button_box"));
+
+ label = GTK_WIDGET (gtk_builder_get_object (builder, "bigtiff-warning"));
+#ifdef TIFF_VERSION_BIG
+ if (classic_tiff_failed)
+ {
+ gtk_label_set_markup (GTK_LABEL (label),
+ _("<i>Warning: maximum TIFF file size exceeded.\n Retry as BigTIFF or cancel.</i>"));
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_label_set_line_wrap_mode (GTK_LABEL (label), PANGO_WRAP_WORD);
+ gtk_label_set_max_width_chars (GTK_LABEL (label), 60);
+ }
+#endif
+ if (! classic_tiff_failed)
+ {
+ gtk_widget_hide (label);
+ }
+
+ frame = gimp_int_radio_group_new (TRUE, _("Compression"),
+ G_CALLBACK (gimp_radio_button_update),
+ &tsvals->compression, tsvals->compression,
+
+ _("_None"), COMPRESSION_NONE, NULL,
+ _("_LZW"), COMPRESSION_LZW, NULL,
+ _("_Pack Bits"), COMPRESSION_PACKBITS, NULL,
+ _("_Deflate"), COMPRESSION_ADOBE_DEFLATE, NULL,
+ _("_JPEG"), COMPRESSION_JPEG, &cmp_jpeg,
+ _("CCITT Group _3 fax"), COMPRESSION_CCITTFAX3, &cmp_g3,
+ _("CCITT Group _4 fax"), COMPRESSION_CCITTFAX4, &cmp_g4,
+
+ NULL);
+
+ gtk_widget_set_sensitive (cmp_g3, is_monochrome);
+ gtk_widget_set_sensitive (cmp_g4, is_monochrome);
+ gtk_widget_set_sensitive (cmp_jpeg, ! is_indexed);
+
+ if (! is_monochrome)
+ {
+ if (tsvals->compression == COMPRESSION_CCITTFAX3 ||
+ tsvals->compression == COMPRESSION_CCITTFAX4)
+ {
+ gimp_int_radio_group_set_active (GTK_RADIO_BUTTON (cmp_g3),
+ COMPRESSION_NONE);
+ }
+ }
+
+ if (is_indexed && tsvals->compression == COMPRESSION_JPEG)
+ {
+ gimp_int_radio_group_set_active (GTK_RADIO_BUTTON (cmp_jpeg),
+ COMPRESSION_NONE);
+ }
+
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ toggle = GTK_WIDGET (gtk_builder_get_object (builder, "save-alpha"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ has_alpha && (tsvals->save_transp_pixels || is_indexed));
+ gtk_widget_set_sensitive (toggle, has_alpha && ! is_indexed);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &tsvals->save_transp_pixels);
+
+ entry = GTK_WIDGET (gtk_builder_get_object (builder, "commentfield"));
+ gtk_entry_set_text (GTK_ENTRY (entry), *image_comment ? *image_comment : "");
+
+ g_signal_connect (entry, "changed",
+ G_CALLBACK (comment_entry_callback),
+ image_comment);
+
+ toggle = GTK_WIDGET (gtk_builder_get_object (builder, "save-exif"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ tsvals->save_exif);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &tsvals->save_exif);
+
+ toggle = GTK_WIDGET (gtk_builder_get_object (builder, "save-xmp"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ tsvals->save_xmp);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &tsvals->save_xmp);
+
+ toggle = GTK_WIDGET (gtk_builder_get_object (builder, "save-iptc"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ tsvals->save_iptc);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &tsvals->save_iptc);
+
+ toggle = GTK_WIDGET (gtk_builder_get_object (builder, "save-geotiff"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ has_geotiff && tsvals->save_geotiff);
+ gtk_widget_set_sensitive (toggle, has_geotiff);;
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &tsvals->save_geotiff);
+
+ toggle = GTK_WIDGET (gtk_builder_get_object (builder, "save-thumbnail"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ tsvals->save_thumbnail);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &tsvals->save_thumbnail);
+
+ toggle = GTK_WIDGET (gtk_builder_get_object (builder, "save-color-profile"));
+#ifdef TIFFTAG_ICCPROFILE
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ tsvals->save_profile);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &tsvals->save_profile);
+#else
+ gtk_widget_hide (toggle);
+#endif
+
+ toggle = GTK_WIDGET (gtk_builder_get_object (builder, "bigtiff"));
+#ifdef TIFF_VERSION_BIG
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ tsvals->bigtiff);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &tsvals->bigtiff);
+#else
+ gtk_widget_hide (toggle);
+#endif
+
+ toggle = GTK_WIDGET (gtk_builder_get_object (builder, "save-layers"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ tsvals->save_layers);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &tsvals->save_layers);
+
+ frame = GTK_WIDGET (gtk_builder_get_object (builder, "layers-frame"));
+ g_object_bind_property (toggle, "active",
+ frame, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ toggle = GTK_WIDGET (gtk_builder_get_object (builder, "crop-layers"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ tsvals->crop_layers);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &tsvals->crop_layers);
+
+ gtk_widget_show (dialog);
+
+ run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
+
+ gtk_widget_destroy (dialog);
+
+ return run;
+}
+
+static void
+comment_entry_callback (GtkWidget *widget,
+ gchar **comment)
+{
+ const gchar *text = gtk_entry_get_text (GTK_ENTRY (widget));
+
+ g_free (*comment);
+ *comment = g_strdup (text);
+}
+
+/* Convert n bytes of 0/1 to a line of bits */
+static void
+byte2bit (const guchar *byteline,
+ gint width,
+ guchar *bitline,
+ gboolean invert)
+{
+ guchar bitval;
+ guchar rest[8];
+
+ while (width >= 8)
+ {
+ bitval = 0;
+ if (*(byteline++)) bitval |= 0x80;
+ if (*(byteline++)) bitval |= 0x40;
+ if (*(byteline++)) bitval |= 0x20;
+ if (*(byteline++)) bitval |= 0x10;
+ if (*(byteline++)) bitval |= 0x08;
+ if (*(byteline++)) bitval |= 0x04;
+ if (*(byteline++)) bitval |= 0x02;
+ if (*(byteline++)) bitval |= 0x01;
+ *(bitline++) = invert ? ~bitval : bitval;
+ width -= 8;
+ }
+
+ if (width > 0)
+ {
+ memset (rest, 0, 8);
+ memcpy (rest, byteline, width);
+ bitval = 0;
+ byteline = rest;
+ if (*(byteline++)) bitval |= 0x80;
+ if (*(byteline++)) bitval |= 0x40;
+ if (*(byteline++)) bitval |= 0x20;
+ if (*(byteline++)) bitval |= 0x10;
+ if (*(byteline++)) bitval |= 0x08;
+ if (*(byteline++)) bitval |= 0x04;
+ if (*(byteline++)) bitval |= 0x02;
+ *bitline = invert ? ~bitval & (0xff << (8 - width)) : bitval;
+ }
+}
diff --git a/plug-ins/file-tiff/file-tiff-save.h b/plug-ins/file-tiff/file-tiff-save.h
new file mode 100644
index 0000000..f28d347
--- /dev/null
+++ b/plug-ins/file-tiff/file-tiff-save.h
@@ -0,0 +1,64 @@
+/* tiff saving for GIMP
+ * -Peter Mattis
+ *
+ * The TIFF loading code has been completely revamped by Nick Lamb
+ * njl195@zepler.org.uk -- 18 May 1998
+ * And it now gains support for tiles (and doubtless a zillion bugs)
+ * njl195@zepler.org.uk -- 12 June 1999
+ * LZW patent fuss continues :(
+ * njl195@zepler.org.uk -- 20 April 2000
+ * The code for this filter is based on "tifftopnm" and "pnmtotiff",
+ * 2 programs that are a part of the netpbm package.
+ * khk@khk.net -- 13 May 2000
+ * Added support for ICCPROFILE tiff tag. If this tag is present in a
+ * TIFF file, then a parasite is created and vice versa.
+ * peter@kirchgessner.net -- 29 Oct 2002
+ * Progress bar only when run interactive
+ * Added support for layer offsets - pablo.dangelo@web.de -- 7 Jan 2004
+ * Honor EXTRASAMPLES tag while loading images with alphachannel
+ * pablo.dangelo@web.de -- 16 Jan 2004
+ */
+
+#ifndef __FILE_TIFF_SAVE_H__
+#define __FILE_TIFF_SAVE_H__
+
+
+typedef struct
+{
+ gint compression;
+ gint fillorder;
+ gboolean save_transp_pixels;
+ gboolean save_exif;
+ gboolean save_xmp;
+ gboolean save_iptc;
+ gboolean save_geotiff;
+ gboolean save_thumbnail;
+ gboolean save_profile;
+ gboolean save_layers;
+ gboolean crop_layers;
+ gboolean bigtiff;
+} TiffSaveVals;
+
+
+gboolean save_image (GFile *file,
+ TiffSaveVals *tsvals,
+ gint32 image,
+ gint32 orig_image,
+ const gchar *image_comment,
+ gint *saved_bpp,
+ GimpMetadata *metadata,
+ GimpMetadataSaveFlags metadata_flags,
+ GError **error);
+
+gboolean save_dialog (TiffSaveVals *tsvals,
+ gint32 image,
+ const gchar *help_id,
+ gboolean has_alpha,
+ gboolean is_monochrome,
+ gboolean is_indexed,
+ gchar **image_comment,
+ GError **error,
+ gboolean classic_tiff_failed);
+
+
+#endif /* __FILE_TIFF_SAVE_H__ */
diff --git a/plug-ins/file-tiff/file-tiff.c b/plug-ins/file-tiff/file-tiff.c
new file mode 100644
index 0000000..4031d65
--- /dev/null
+++ b/plug-ins/file-tiff/file-tiff.c
@@ -0,0 +1,587 @@
+/* tiff loading for GIMP
+ * -Peter Mattis
+ *
+ * The TIFF loading code has been completely revamped by Nick Lamb
+ * njl195@zepler.org.uk -- 18 May 1998
+ * And it now gains support for tiles (and doubtless a zillion bugs)
+ * njl195@zepler.org.uk -- 12 June 1999
+ * LZW patent fuss continues :(
+ * njl195@zepler.org.uk -- 20 April 2000
+ * The code for this filter is based on "tifftopnm" and "pnmtotiff",
+ * 2 programs that are a part of the netpbm package.
+ * khk@khk.net -- 13 May 2000
+ * Added support for ICCPROFILE tiff tag. If this tag is present in a
+ * TIFF file, then a parasite is created and vice versa.
+ * peter@kirchgessner.net -- 29 Oct 2002
+ * Progress bar only when run interactive
+ * Added support for layer offsets - pablo.dangelo@web.de -- 7 Jan 2004
+ * Honor EXTRASAMPLES tag while loading images with alphachannel
+ * pablo.dangelo@web.de -- 16 Jan 2004
+ */
+
+/*
+ * tifftopnm.c - converts a Tagged Image File to a portable anymap
+ *
+ * Derived by Jef Poskanzer from tif2ras.c, which is:
+ *
+ * Copyright (c) 1990 by Sun Microsystems, Inc.
+ *
+ * Author: Patrick J. Naughton
+ * naughton@wind.sun.com
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and that
+ * both that copyright notice and this permission notice appear in
+ * supporting documentation.
+ *
+ * This file is provided AS IS with no warranties of any kind. The author
+ * shall have no liability with respect to the infringement of copyrights,
+ * trade secrets or any patents by this file or any part thereof. In no
+ * event will the author be liable for any lost revenue or profits or
+ * other special, indirect and consequential damages.
+ */
+
+#include "config.h"
+
+#include <tiffio.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "file-tiff-io.h"
+#include "file-tiff-load.h"
+#include "file-tiff-save.h"
+
+#include "libgimp/stdplugins-intl.h"
+
+
+#define SAVE_PROC "file-tiff-save"
+#define SAVE2_PROC "file-tiff-save2"
+#define SAVE_BIGTIFF_PROC "file-bigtiff-save"
+#define PLUG_IN_BINARY "file-tiff"
+
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+static GimpPDBStatusType tiff_save_rec (GimpRunMode run_mode,
+ gint32 orig_image,
+ gint32 orig_drawable,
+ GFile *file,
+ GimpMetadata *metadata,
+ GimpMetadataSaveFlags metadata_flags,
+ gboolean retried,
+ GError **error);
+
+static gboolean image_is_monochrome (gint32 image);
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+static TiffSaveVals tsvals =
+{
+ COMPRESSION_NONE, /* compression */
+ TRUE, /* alpha handling */
+ FALSE, /* save transp. pixels */
+ FALSE, /* save exif */
+ FALSE, /* save xmp */
+ FALSE, /* save iptc */
+ TRUE, /* save geotiff */
+ TRUE, /* save thumbnail */
+ TRUE, /* save profile */
+ TRUE, /* save layers */
+ TRUE, /* crop layers */
+ FALSE /* save BigTIFF */
+};
+
+static gchar *image_comment = NULL;
+
+
+MAIN ()
+
+
+static void
+query (void)
+{
+ static const GimpParamDef load_args[] =
+ {
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to load" }
+ };
+ static const GimpParamDef load_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Output image" }
+ };
+
+#define COMMON_SAVE_ARGS \
+ { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },\
+ { GIMP_PDB_IMAGE, "image", "Input image" },\
+ { GIMP_PDB_DRAWABLE, "drawable", "Drawable to save" },\
+ { GIMP_PDB_STRING, "filename", "The name of the file to save the image in" },\
+ { GIMP_PDB_STRING, "raw-filename", "The name of the file to save the image in" },\
+ { GIMP_PDB_INT32, "compression", "Compression type: { NONE (0), LZW (1), PACKBITS (2), DEFLATE (3), JPEG (4), CCITT G3 Fax (5), CCITT G4 Fax (6) }" }
+
+ static const GimpParamDef save_args_old[] =
+ {
+ COMMON_SAVE_ARGS
+ };
+
+ static const GimpParamDef save_args[] =
+ {
+ COMMON_SAVE_ARGS,
+ { GIMP_PDB_INT32, "save-transp-pixels", "Keep the color data masked by an alpha channel intact (do not store premultiplied components)" }
+ };
+
+ static const GimpParamDef save_bigtiff_args[] =
+ {
+ COMMON_SAVE_ARGS,
+ { GIMP_PDB_INT32, "save-transp-pixels", "Keep the color data masked by an alpha channel intact (do not store premultiplied components)" },
+ { GIMP_PDB_INT32, "bigtiff", "Export in BigTIFF variant file format" }
+ };
+
+ gimp_install_procedure (LOAD_PROC,
+#ifdef TIFF_VERSION_BIG
+ "Loads files of the TIFF and BigTIFF file formats",
+ "Loads files of the Tag Image File Format (TIFF) and "
+ "its 64-bits offsets variant (BigTIFF)",
+#else
+ "Loads files of the TIFF file format",
+ "Loads files of the Tag Image File Format (TIFF)",
+#endif
+ "Spencer Kimball, Peter Mattis & Nick Lamb",
+ "Nick Lamb <njl195@zepler.org.uk>",
+ "1995-1996,1998-2003",
+ N_("TIFF image"),
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (load_args),
+ G_N_ELEMENTS (load_return_vals),
+ load_args, load_return_vals);
+
+ gimp_register_file_handler_mime (LOAD_PROC, "image/tiff");
+ gimp_register_file_handler_uri (LOAD_PROC);
+ gimp_register_magic_load_handler (LOAD_PROC,
+ "tif,tiff",
+ "",
+ "0,string,II*\\0,0,string,MM\\0*");
+
+ gimp_install_procedure (SAVE_PROC,
+ "Exports files in the TIFF file format",
+ "Exports files in the Tagged Image File Format. "
+ "The value for the saved comment is taken "
+ "from the 'gimp-comment' parasite.",
+ "Spencer Kimball & Peter Mattis",
+ "Spencer Kimball & Peter Mattis",
+ "1995-1996,2000-2003",
+ N_("TIFF image"),
+ "RGB*, GRAY*, INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args_old), 0,
+ save_args_old, NULL);
+
+ gimp_register_file_handler_mime (SAVE_PROC, "image/tiff");
+ gimp_register_file_handler_uri (SAVE_PROC);
+ gimp_register_save_handler (SAVE_PROC, "tif,tiff", "");
+
+ gimp_install_procedure (SAVE2_PROC,
+ "Exports files in the TIFF file format",
+ "Exports files in the Tagged Image File Format. "
+ "The value for the saved comment is taken "
+ "from the 'gimp-comment' parasite.",
+ "Spencer Kimball & Peter Mattis",
+ "Spencer Kimball & Peter Mattis",
+ "1995-1996,2000-2003",
+ N_("TIFF image"),
+ "RGB*, GRAY*, INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+#ifdef TIFF_VERSION_BIG
+ gimp_install_procedure (SAVE_BIGTIFF_PROC,
+ "Exports files in the TIFF or BigTIFF file format",
+ "Exports files in the Tagged Image File Format or "
+ "its 64-bit offsets variant (BigTIFF) able to support "
+ "much bigger file sizes. "
+ "The value for the saved comment is taken "
+ "from the 'gimp-comment' parasite.",
+ "Spencer Kimball & Peter Mattis",
+ "Spencer Kimball & Peter Mattis",
+ "1995-1996,2000-2003",
+ N_("TIFF image"),
+ "RGB*, GRAY*, INDEXED*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_bigtiff_args), 0,
+ save_bigtiff_args, NULL);
+
+ gimp_register_file_handler_mime (SAVE_PROC, "image/tiff");
+ gimp_register_file_handler_uri (SAVE_PROC);
+ gimp_register_save_handler (SAVE_PROC, "tif,tiff", "");
+#endif
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[2];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GError *error = NULL;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ run_mode = param[0].data.d_int32;
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ GFile *file = g_file_new_for_uri (param[1].data.d_string);
+ gint32 image = 0;
+ gboolean resolution_loaded = FALSE;
+ gboolean profile_loaded = FALSE;
+
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ status = load_image (file, run_mode, &image,
+ &resolution_loaded,
+ &profile_loaded,
+ &error);
+
+ if (image > 0)
+ {
+ GimpMetadata *metadata;
+
+ metadata = gimp_image_metadata_load_prepare (image,
+ "image/tiff",
+ file, NULL);
+
+ if (metadata)
+ {
+ GimpMetadataLoadFlags flags = GIMP_METADATA_LOAD_ALL;
+
+ if (resolution_loaded)
+ flags &= ~GIMP_METADATA_LOAD_RESOLUTION;
+
+ if (profile_loaded)
+ flags &= ~GIMP_METADATA_LOAD_COLORSPACE;
+
+ gimp_image_metadata_load_finish (image, "image/tiff",
+ metadata, flags,
+ run_mode == GIMP_RUN_INTERACTIVE);
+
+ g_object_unref (metadata);
+ }
+
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image;
+ }
+
+ g_object_unref (file);
+ }
+ else if ((strcmp (name, SAVE_PROC) == 0) ||
+ (strcmp (name, SAVE2_PROC) == 0) ||
+ (strcmp (name, SAVE_BIGTIFF_PROC) == 0))
+ {
+ /* Plug-in is either file_tiff_save or file_tiff_save2 */
+ GFile *file;
+ GimpMetadata *metadata;
+ GimpMetadataSaveFlags metadata_flags;
+ GimpParasite *parasite;
+ gint32 image = param[1].data.d_int32;
+ gint32 drawable = param[2].data.d_int32;
+ gint32 orig_image = image;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+ break;
+ default:
+ break;
+ }
+
+ /* Override the defaults with preferences. */
+ metadata = gimp_image_metadata_save_prepare (orig_image,
+ "image/tiff",
+ &metadata_flags);
+ tsvals.save_exif = (metadata_flags & GIMP_METADATA_SAVE_EXIF) != 0;
+ tsvals.save_xmp = (metadata_flags & GIMP_METADATA_SAVE_XMP) != 0;
+ tsvals.save_iptc = (metadata_flags & GIMP_METADATA_SAVE_IPTC) != 0;
+ tsvals.save_thumbnail = (metadata_flags & GIMP_METADATA_SAVE_THUMBNAIL) != 0;
+ tsvals.save_profile = (metadata_flags & GIMP_METADATA_SAVE_COLOR_PROFILE) != 0;
+ tsvals.save_geotiff = TRUE;
+ tsvals.bigtiff = FALSE;
+#ifdef TIFF_VERSION_BIG
+ if (nparams >= 8)
+ tsvals.bigtiff = param[7].data.d_int32;
+#endif
+
+ parasite = gimp_image_get_parasite (orig_image, "gimp-comment");
+ if (parasite)
+ {
+ image_comment = g_strndup (gimp_parasite_data (parasite),
+ gimp_parasite_data_size (parasite));
+ gimp_parasite_free (parasite);
+ }
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ /* Possibly retrieve data */
+ gimp_get_data (name, &tsvals);
+
+ parasite = gimp_image_get_parasite (orig_image, "tiff-save-options");
+ if (parasite)
+ {
+ const TiffSaveVals *pvals = gimp_parasite_data (parasite);
+
+ if (pvals->compression == COMPRESSION_DEFLATE)
+ tsvals.compression = COMPRESSION_ADOBE_DEFLATE;
+ else
+ tsvals.compression = pvals->compression;
+
+ tsvals.save_transp_pixels = pvals->save_transp_pixels;
+ }
+ gimp_parasite_free (parasite);
+ break;
+
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ if (nparams == 6 || nparams == 7 || nparams == 8)
+ {
+ switch (param[5].data.d_int32)
+ {
+ case 0: tsvals.compression = COMPRESSION_NONE; break;
+ case 1: tsvals.compression = COMPRESSION_LZW; break;
+ case 2: tsvals.compression = COMPRESSION_PACKBITS; break;
+ case 3: tsvals.compression = COMPRESSION_ADOBE_DEFLATE; break;
+ case 4: tsvals.compression = COMPRESSION_JPEG; break;
+ case 5: tsvals.compression = COMPRESSION_CCITTFAX3; break;
+ case 6: tsvals.compression = COMPRESSION_CCITTFAX4; break;
+ default: status = GIMP_PDB_CALLING_ERROR; break;
+ }
+
+ if (nparams >= 7)
+ tsvals.save_transp_pixels = param[6].data.d_int32;
+ else
+ tsvals.save_transp_pixels = TRUE;
+
+ if (nparams == 8)
+ tsvals.bigtiff = param[7].data.d_int32;
+ else
+ tsvals.bigtiff = FALSE;
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ break;
+
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* Possibly retrieve data */
+ gimp_get_data (SAVE_PROC, &tsvals);
+
+ parasite = gimp_image_get_parasite (orig_image, "tiff-save-options");
+ if (parasite)
+ {
+ const TiffSaveVals *pvals = gimp_parasite_data (parasite);
+
+ tsvals.compression = pvals->compression;
+ tsvals.save_transp_pixels = pvals->save_transp_pixels;
+ }
+ gimp_parasite_free (parasite);
+ break;
+
+ default:
+ break;
+ }
+
+ file = g_file_new_for_uri (param[3].data.d_string);
+ status = tiff_save_rec (run_mode, orig_image, drawable,
+ file, metadata, metadata_flags, FALSE, &error);
+
+ if (metadata)
+ g_object_unref (metadata);
+ }
+ else
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+
+ if (status != GIMP_PDB_SUCCESS && error)
+ {
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_STRING;
+ values[1].data.d_string = error->message;
+ }
+
+ values[0].data.d_status = status;
+}
+
+static gboolean
+image_is_monochrome (gint32 image)
+{
+ guchar *colors;
+ gint num_colors;
+ gboolean monochrome = FALSE;
+
+ g_return_val_if_fail (image != -1, FALSE);
+
+ colors = gimp_image_get_colormap (image, &num_colors);
+
+ if (colors)
+ {
+ if (num_colors == 2 || num_colors == 1)
+ {
+ const guchar bw_map[] = { 0, 0, 0, 255, 255, 255 };
+ const guchar wb_map[] = { 255, 255, 255, 0, 0, 0 };
+
+ if (memcmp (colors, bw_map, 3 * num_colors) == 0 ||
+ memcmp (colors, wb_map, 3 * num_colors) == 0)
+ {
+ monochrome = TRUE;
+ }
+ }
+
+ g_free (colors);
+ }
+
+ return monochrome;
+}
+
+static GimpPDBStatusType
+tiff_save_rec (GimpRunMode run_mode,
+ gint32 orig_image,
+ gint32 orig_drawable,
+ GFile *file,
+ GimpMetadata *metadata,
+ GimpMetadataSaveFlags metadata_flags,
+ gboolean retried,
+ GError **error)
+{
+ gint32 image = orig_image;
+ gint32 drawable = orig_drawable;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+ gboolean bigtiff = FALSE;
+
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ {
+ if (! save_dialog (&tsvals, image,
+ SAVE_PROC,
+ gimp_drawable_has_alpha (drawable),
+ image_is_monochrome (image),
+ gimp_image_base_type (image) == GIMP_INDEXED,
+ &image_comment,
+ error,
+ retried))
+ {
+ return GIMP_PDB_CANCEL;
+ }
+ }
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ {
+ GimpExportCapabilities capabilities;
+
+ if (tsvals.compression == COMPRESSION_CCITTFAX3 ||
+ tsvals.compression == COMPRESSION_CCITTFAX4)
+ {
+ /* G3/G4 are fax compressions. They only support
+ * monochrome images without alpha support.
+ */
+ capabilities = GIMP_EXPORT_CAN_HANDLE_INDEXED;
+ }
+ else
+ {
+ capabilities = (GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_GRAY |
+ GIMP_EXPORT_CAN_HANDLE_INDEXED |
+ GIMP_EXPORT_CAN_HANDLE_ALPHA);
+ }
+
+ if (tsvals.save_layers)
+ {
+ capabilities |= GIMP_EXPORT_CAN_HANDLE_LAYERS;
+
+ if (tsvals.crop_layers)
+ capabilities |= GIMP_EXPORT_NEEDS_CROP;
+ }
+
+ export = gimp_export_image (&image, &drawable, "TIFF", capabilities);
+
+ if (export == GIMP_EXPORT_CANCEL)
+ return GIMP_PDB_CANCEL;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ gint saved_bpp;
+
+ if (save_image (file, &tsvals, image, orig_image, image_comment,
+ &saved_bpp, metadata, metadata_flags, error))
+ {
+ /* Store mvals data */
+ gimp_set_data (SAVE_PROC, &tsvals, sizeof (TiffSaveVals));
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ {
+ gimp_image_delete (image);
+ }
+
+#ifdef TIFF_VERSION_BIG
+ if (status == GIMP_PDB_EXECUTION_ERROR &&
+ run_mode == GIMP_RUN_INTERACTIVE &&
+ ! retried && ! bigtiff && tiff_got_file_size_error ())
+ {
+ /* Retrying but just once, when the save failed because we exceeded
+ * TIFF max size, to propose BigTIFF instead. */
+ tiff_reset_file_size_error ();
+ g_clear_error (error);
+
+ return tiff_save_rec (run_mode, orig_image, orig_drawable,
+ file, metadata, metadata_flags, TRUE, error);
+ }
+#endif
+
+ return status;
+}
+