diff options
Diffstat (limited to 'plug-ins/file-tiff')
-rw-r--r-- | plug-ins/file-tiff/Makefile.am | 58 | ||||
-rw-r--r-- | plug-ins/file-tiff/Makefile.in | 1022 | ||||
-rw-r--r-- | plug-ins/file-tiff/file-tiff-io.c | 585 | ||||
-rw-r--r-- | plug-ins/file-tiff/file-tiff-io.h | 50 | ||||
-rw-r--r-- | plug-ins/file-tiff/file-tiff-load.c | 2815 | ||||
-rw-r--r-- | plug-ins/file-tiff/file-tiff-load.h | 57 | ||||
-rw-r--r-- | plug-ins/file-tiff/file-tiff-save.c | 1387 | ||||
-rw-r--r-- | plug-ins/file-tiff/file-tiff-save.h | 64 | ||||
-rw-r--r-- | plug-ins/file-tiff/file-tiff.c | 587 |
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; +} + |