summaryrefslogtreecommitdiffstats
path: root/plug-ins/file-jpeg
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:23:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:23:22 +0000
commite42129241681dde7adae7d20697e7b421682fbb4 (patch)
treeaf1fe815a5e639e68e59fabd8395ec69458b3e5e /plug-ins/file-jpeg
parentInitial commit. (diff)
downloadgimp-e42129241681dde7adae7d20697e7b421682fbb4.tar.xz
gimp-e42129241681dde7adae7d20697e7b421682fbb4.zip
Adding upstream version 2.10.22.upstream/2.10.22upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'plug-ins/file-jpeg')
-rw-r--r--plug-ins/file-jpeg/Makefile.am77
-rw-r--r--plug-ins/file-jpeg/Makefile.in1068
-rw-r--r--plug-ins/file-jpeg/jpeg-icc.c263
-rw-r--r--plug-ins/file-jpeg/jpeg-icc.h59
-rw-r--r--plug-ins/file-jpeg/jpeg-load.c719
-rw-r--r--plug-ins/file-jpeg/jpeg-load.h33
-rw-r--r--plug-ins/file-jpeg/jpeg-quality.c148
-rw-r--r--plug-ins/file-jpeg/jpeg-quality.h26
-rw-r--r--plug-ins/file-jpeg/jpeg-save.c1524
-rw-r--r--plug-ins/file-jpeg/jpeg-save.h57
-rw-r--r--plug-ins/file-jpeg/jpeg-settings.c397
-rw-r--r--plug-ins/file-jpeg/jpeg-settings.h37
-rw-r--r--plug-ins/file-jpeg/jpeg.c632
-rw-r--r--plug-ins/file-jpeg/jpeg.h73
-rw-r--r--plug-ins/file-jpeg/jpegqual.c1082
15 files changed, 6195 insertions, 0 deletions
diff --git a/plug-ins/file-jpeg/Makefile.am b/plug-ins/file-jpeg/Makefile.am
new file mode 100644
index 0000000..b443475
--- /dev/null
+++ b/plug-ins/file-jpeg/Makefile.am
@@ -0,0 +1,77 @@
+## 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_jpeg_RC = file-jpeg.rc.o
+endif
+
+AM_LDFLAGS = $(mwindows)
+
+libexecdir = $(gimpplugindir)/plug-ins/file-jpeg
+
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(EXIF_CFLAGS) \
+ $(LCMS_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ $(GEXIV2_CFLAGS) \
+ -I$(includedir)
+
+libexec_PROGRAMS = file-jpeg
+
+file_jpeg_SOURCES = \
+ jpeg.c \
+ jpeg.h \
+ jpeg-icc.c \
+ jpeg-icc.h \
+ jpeg-load.c \
+ jpeg-load.h \
+ jpeg-save.c \
+ jpeg-save.h \
+ jpeg-quality.c \
+ jpeg-quality.h \
+ jpeg-settings.c \
+ jpeg-settings.h
+
+file_jpeg_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(JPEG_LIBS) \
+ $(LCMS_LIBS) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(GEXIV2_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_jpeg_RC)
+
+noinst_PROGRAMS = jpegqual
+
+jpegqual_SOURCES = \
+ jpeg-quality.c \
+ jpeg-quality.h \
+ jpegqual.c
+
+jpegqual_LDADD = \
+ $(JPEG_LIBS) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS)
diff --git a/plug-ins/file-jpeg/Makefile.in b/plug-ins/file-jpeg/Makefile.in
new file mode 100644
index 0000000..10f8dea
--- /dev/null
+++ b/plug-ins/file-jpeg/Makefile.in
@@ -0,0 +1,1068 @@
+# Makefile.in generated by automake 1.16.2 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-jpeg$(EXEEXT)
+noinst_PROGRAMS = jpegqual$(EXEEXT)
+subdir = plug-ins/file-jpeg
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4macros/gtk-doc.m4 \
+ $(top_srcdir)/m4macros/intltool.m4 \
+ $(top_srcdir)/m4macros/libtool.m4 \
+ $(top_srcdir)/m4macros/ltoptions.m4 \
+ $(top_srcdir)/m4macros/ltsugar.m4 \
+ $(top_srcdir)/m4macros/ltversion.m4 \
+ $(top_srcdir)/m4macros/lt~obsolete.m4 \
+ $(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) $(noinst_PROGRAMS)
+am_file_jpeg_OBJECTS = jpeg.$(OBJEXT) jpeg-icc.$(OBJEXT) \
+ jpeg-load.$(OBJEXT) jpeg-save.$(OBJEXT) jpeg-quality.$(OBJEXT) \
+ jpeg-settings.$(OBJEXT)
+file_jpeg_OBJECTS = $(am_file_jpeg_OBJECTS)
+am__DEPENDENCIES_1 =
+file_jpeg_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) \
+ $(am__DEPENDENCIES_1) $(file_jpeg_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_jpegqual_OBJECTS = jpeg-quality.$(OBJEXT) jpegqual.$(OBJEXT)
+jpegqual_OBJECTS = $(am_jpegqual_OBJECTS)
+jpegqual_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_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)/jpeg-icc.Po ./$(DEPDIR)/jpeg-load.Po \
+ ./$(DEPDIR)/jpeg-quality.Po ./$(DEPDIR)/jpeg-save.Po \
+ ./$(DEPDIR)/jpeg-settings.Po ./$(DEPDIR)/jpeg.Po \
+ ./$(DEPDIR)/jpegqual.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_jpeg_SOURCES) $(jpegqual_SOURCES)
+DIST_SOURCES = $(file_jpeg_SOURCES) $(jpegqual_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_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_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@
+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@
+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-jpeg
+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_jpeg_RC = file-jpeg.rc.o
+AM_LDFLAGS = $(mwindows)
+AM_CPPFLAGS = \
+ -I$(top_srcdir) \
+ $(GTK_CFLAGS) \
+ $(EXIF_CFLAGS) \
+ $(LCMS_CFLAGS) \
+ $(GEGL_CFLAGS) \
+ $(GEXIV2_CFLAGS) \
+ -I$(includedir)
+
+file_jpeg_SOURCES = \
+ jpeg.c \
+ jpeg.h \
+ jpeg-icc.c \
+ jpeg-icc.h \
+ jpeg-load.c \
+ jpeg-load.h \
+ jpeg-save.c \
+ jpeg-save.h \
+ jpeg-quality.c \
+ jpeg-quality.h \
+ jpeg-settings.c \
+ jpeg-settings.h
+
+file_jpeg_LDADD = \
+ $(libgimpui) \
+ $(libgimpwidgets) \
+ $(libgimpconfig) \
+ $(libgimp) \
+ $(libgimpcolor) \
+ $(libgimpmath) \
+ $(libgimpbase) \
+ $(JPEG_LIBS) \
+ $(LCMS_LIBS) \
+ $(GTK_LIBS) \
+ $(GEGL_LIBS) \
+ $(GEXIV2_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS) \
+ $(file_jpeg_RC)
+
+jpegqual_SOURCES = \
+ jpeg-quality.c \
+ jpeg-quality.h \
+ jpegqual.c
+
+jpegqual_LDADD = \
+ $(JPEG_LIBS) \
+ $(GTK_LIBS) \
+ $(RT_LIBS) \
+ $(INTLLIBS)
+
+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-jpeg/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu plug-ins/file-jpeg/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
+
+clean-noinstPROGRAMS:
+ @list='$(noinst_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-jpeg$(EXEEXT): $(file_jpeg_OBJECTS) $(file_jpeg_DEPENDENCIES) $(EXTRA_file_jpeg_DEPENDENCIES)
+ @rm -f file-jpeg$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(file_jpeg_OBJECTS) $(file_jpeg_LDADD) $(LIBS)
+
+jpegqual$(EXEEXT): $(jpegqual_OBJECTS) $(jpegqual_DEPENDENCIES) $(EXTRA_jpegqual_DEPENDENCIES)
+ @rm -f jpegqual$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(jpegqual_OBJECTS) $(jpegqual_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jpeg-icc.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jpeg-load.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jpeg-quality.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jpeg-save.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jpeg-settings.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jpeg.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/jpegqual.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 \
+ clean-noinstPROGRAMS mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/jpeg-icc.Po
+ -rm -f ./$(DEPDIR)/jpeg-load.Po
+ -rm -f ./$(DEPDIR)/jpeg-quality.Po
+ -rm -f ./$(DEPDIR)/jpeg-save.Po
+ -rm -f ./$(DEPDIR)/jpeg-settings.Po
+ -rm -f ./$(DEPDIR)/jpeg.Po
+ -rm -f ./$(DEPDIR)/jpegqual.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)/jpeg-icc.Po
+ -rm -f ./$(DEPDIR)/jpeg-load.Po
+ -rm -f ./$(DEPDIR)/jpeg-quality.Po
+ -rm -f ./$(DEPDIR)/jpeg-save.Po
+ -rm -f ./$(DEPDIR)/jpeg-settings.Po
+ -rm -f ./$(DEPDIR)/jpeg.Po
+ -rm -f ./$(DEPDIR)/jpegqual.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 \
+ clean-noinstPROGRAMS 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-jpeg/jpeg-icc.c b/plug-ins/file-jpeg/jpeg-icc.c
new file mode 100644
index 0000000..88f75d4
--- /dev/null
+++ b/plug-ins/file-jpeg/jpeg-icc.c
@@ -0,0 +1,263 @@
+/* jpeg-icc.c
+ *
+ * This file provides code to read and write International Color
+ * Consortium (ICC) device profiles embedded in JFIF JPEG image files.
+ * The ICC has defined a standard format for including such data in
+ * JPEG "APP2" markers. The code given here does not know anything
+ * about the internal structure of the ICC profile data; it just knows
+ * how to put the profile data into a JPEG file being written, or get
+ * it back out when reading.
+ *
+ * This code depends on new features added to the IJG JPEG library as of
+ * IJG release 6b; it will not compile or work with older IJG versions.
+ */
+
+/* This code was originally copied from the jpegicc tool as found in
+ * the lcms source code. This code comes with the following copyright
+ * notice:
+ *
+ * Little cms
+ * Copyright (C) 1998-2004 Marti Maria
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ */
+
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+
+#include <jpeglib.h>
+
+#include <glib.h>
+
+#include "jpeg-icc.h"
+
+
+/*
+ * Since an ICC profile can be larger than the maximum size of a JPEG
+ * marker (64K), we need provisions to split it into multiple markers.
+ * The format defined by the ICC specifies one or more APP2 markers
+ * containing the following data:
+ *
+ * Identifying string ASCII "ICC_PROFILE\0" (12 bytes)
+ * Marker sequence number 1 for first APP2, 2 for next, etc (1 byte)
+ * Number of markers Total number of APP2's used (1 byte)
+ * Profile data (remainder of APP2 data)
+
+ * Decoders should use the marker sequence numbers to reassemble the
+ * profile, rather than assuming that the APP2 markers appear in the
+ * correct sequence.
+ */
+
+#define ICC_MARKER (JPEG_APP0 + 2) /* JPEG marker code for ICC */
+#define ICC_OVERHEAD_LEN 14 /* size of non-profile data in APP2 */
+#define MAX_BYTES_IN_MARKER 65533 /* maximum data len of a JPEG marker */
+#define MAX_DATA_BYTES_IN_MARKER (MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN)
+
+
+/*
+ * This routine writes the given ICC profile data into a JPEG file.
+ * It *must* be called AFTER calling jpeg_start_compress() and BEFORE
+ * the first call to jpeg_write_scanlines().
+ * (This ordering ensures that the APP2 marker(s) will appear after the
+ * SOI and JFIF or Adobe markers, but before all else.)
+ */
+
+void
+jpeg_icc_write_profile (j_compress_ptr cinfo,
+ const guchar *icc_data_ptr,
+ guint icc_data_len)
+{
+ unsigned int num_markers; /* total number of markers we'll write */
+ int cur_marker = 1; /* per spec, counting starts at 1 */
+ unsigned int length; /* number of bytes to write in this marker */
+
+ /* Calculate the number of markers we'll need, rounding up of course */
+ num_markers = icc_data_len / MAX_DATA_BYTES_IN_MARKER;
+ if (num_markers * MAX_DATA_BYTES_IN_MARKER != icc_data_len)
+ num_markers++;
+
+ while (icc_data_len > 0) {
+ /* length of profile to put in this marker */
+ length = icc_data_len;
+ if (length > MAX_DATA_BYTES_IN_MARKER)
+ length = MAX_DATA_BYTES_IN_MARKER;
+ icc_data_len -= length;
+
+ /* Write the JPEG marker header (APP2 code and marker length) */
+ jpeg_write_m_header(cinfo, ICC_MARKER,
+ (unsigned int) (length + ICC_OVERHEAD_LEN));
+
+ /* Write the marker identifying string "ICC_PROFILE" (null-terminated).
+ * We code it in this less-than-transparent way so that the code works
+ * even if the local character set is not ASCII.
+ */
+ jpeg_write_m_byte(cinfo, 0x49);
+ jpeg_write_m_byte(cinfo, 0x43);
+ jpeg_write_m_byte(cinfo, 0x43);
+ jpeg_write_m_byte(cinfo, 0x5F);
+ jpeg_write_m_byte(cinfo, 0x50);
+ jpeg_write_m_byte(cinfo, 0x52);
+ jpeg_write_m_byte(cinfo, 0x4F);
+ jpeg_write_m_byte(cinfo, 0x46);
+ jpeg_write_m_byte(cinfo, 0x49);
+ jpeg_write_m_byte(cinfo, 0x4C);
+ jpeg_write_m_byte(cinfo, 0x45);
+ jpeg_write_m_byte(cinfo, 0x0);
+
+ /* Add the sequencing info */
+ jpeg_write_m_byte(cinfo, cur_marker);
+ jpeg_write_m_byte(cinfo, (int) num_markers);
+
+ /* Add the profile data */
+ while (length--) {
+ jpeg_write_m_byte(cinfo, *icc_data_ptr);
+ icc_data_ptr++;
+ }
+ cur_marker++;
+ }
+}
+
+
+/*
+ * Handy subroutine to test whether a saved marker is an ICC profile marker.
+ */
+
+static boolean
+marker_is_icc (jpeg_saved_marker_ptr marker)
+{
+ return
+ marker->marker == ICC_MARKER &&
+ marker->data_length >= ICC_OVERHEAD_LEN &&
+ /* verify the identifying string */
+ GETJOCTET(marker->data[0]) == 0x49 &&
+ GETJOCTET(marker->data[1]) == 0x43 &&
+ GETJOCTET(marker->data[2]) == 0x43 &&
+ GETJOCTET(marker->data[3]) == 0x5F &&
+ GETJOCTET(marker->data[4]) == 0x50 &&
+ GETJOCTET(marker->data[5]) == 0x52 &&
+ GETJOCTET(marker->data[6]) == 0x4F &&
+ GETJOCTET(marker->data[7]) == 0x46 &&
+ GETJOCTET(marker->data[8]) == 0x49 &&
+ GETJOCTET(marker->data[9]) == 0x4C &&
+ GETJOCTET(marker->data[10]) == 0x45 &&
+ GETJOCTET(marker->data[11]) == 0x0;
+}
+
+
+/*
+ * See if there was an ICC profile in the JPEG file being read;
+ * if so, reassemble and return the profile data.
+ *
+ * TRUE is returned if an ICC profile was found, FALSE if not.
+ * If TRUE is returned, *icc_data_ptr is set to point to the
+ * returned data, and *icc_data_len is set to its length.
+ *
+ * NOTE: if the file contains invalid ICC APP2 markers, we just silently
+ * return FALSE. You might want to issue an error message instead.
+ */
+
+gboolean
+jpeg_icc_read_profile (j_decompress_ptr cinfo,
+ guchar **icc_data_ptr,
+ guint *icc_data_len)
+{
+ jpeg_saved_marker_ptr marker;
+ int num_markers = 0;
+ int seq_no;
+ JOCTET *icc_data;
+ unsigned int total_length;
+#define MAX_SEQ_NO 255 /* sufficient since marker numbers are bytes */
+ char marker_present[MAX_SEQ_NO+1]; /* 1 if marker found */
+ unsigned int data_length[MAX_SEQ_NO+1]; /* size of profile data in marker */
+ unsigned int data_offset[MAX_SEQ_NO+1]; /* offset for data in marker */
+
+ *icc_data_ptr = NULL; /* avoid confusion if FALSE return */
+ *icc_data_len = 0;
+
+ /* This first pass over the saved markers discovers whether there are
+ * any ICC markers and verifies the consistency of the marker numbering.
+ */
+
+ for (seq_no = 1; seq_no <= MAX_SEQ_NO; seq_no++)
+ marker_present[seq_no] = 0;
+
+ for (marker = cinfo->marker_list; marker != NULL; marker = marker->next) {
+ if (marker_is_icc(marker)) {
+ if (num_markers == 0)
+ num_markers = GETJOCTET(marker->data[13]);
+ else if (num_markers != GETJOCTET(marker->data[13]))
+ return FALSE; /* inconsistent num_markers fields */
+ seq_no = GETJOCTET(marker->data[12]);
+ if (seq_no <= 0 || seq_no > num_markers)
+ return FALSE; /* bogus sequence number */
+ if (marker_present[seq_no])
+ return FALSE; /* duplicate sequence numbers */
+ marker_present[seq_no] = 1;
+ data_length[seq_no] = marker->data_length - ICC_OVERHEAD_LEN;
+ }
+ }
+
+ if (num_markers == 0)
+ return FALSE;
+
+ /* Check for missing markers, count total space needed,
+ * compute offset of each marker's part of the data.
+ */
+
+ total_length = 0;
+ for (seq_no = 1; seq_no <= num_markers; seq_no++) {
+ if (marker_present[seq_no] == 0)
+ return FALSE; /* missing sequence number */
+ data_offset[seq_no] = total_length;
+ total_length += data_length[seq_no];
+ }
+
+ if (total_length <= 0)
+ return FALSE; /* found only empty markers? */
+
+ /* Allocate space for assembled data */
+ icc_data = g_try_malloc (total_length * sizeof(JOCTET));
+ if (icc_data == NULL)
+ return FALSE; /* oops, out of memory */
+
+ /* and fill it in */
+ for (marker = cinfo->marker_list; marker != NULL; marker = marker->next) {
+ if (marker_is_icc(marker)) {
+ JOCTET FAR *src_ptr;
+ JOCTET *dst_ptr;
+ unsigned int length;
+ seq_no = GETJOCTET(marker->data[12]);
+ dst_ptr = icc_data + data_offset[seq_no];
+ src_ptr = marker->data + ICC_OVERHEAD_LEN;
+ length = data_length[seq_no];
+ while (length--) {
+ *dst_ptr++ = *src_ptr++;
+ }
+ }
+ }
+
+ *icc_data_ptr = icc_data;
+ *icc_data_len = total_length;
+
+ return TRUE;
+}
diff --git a/plug-ins/file-jpeg/jpeg-icc.h b/plug-ins/file-jpeg/jpeg-icc.h
new file mode 100644
index 0000000..b5306a8
--- /dev/null
+++ b/plug-ins/file-jpeg/jpeg-icc.h
@@ -0,0 +1,59 @@
+/* jpeg-icc.h
+ *
+ * This code was originally copied from the jpegicc tool as found in
+ * the lcms source code. This code comes with the following copyright
+ * notice:
+ *
+ * Little cms
+ * Copyright (C) 1998-2004 Marti Maria
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ */
+
+#ifndef __JPEG_ICC_H__
+#define __JPEG_ICC_H__
+
+void jpeg_icc_write_profile (j_compress_ptr cinfo,
+ const guchar *icc_data_ptr,
+ guint icc_data_len);
+
+
+/*
+ * Reading a JPEG file that may contain an ICC profile requires two steps:
+ *
+ * 1. After jpeg_create_decompress() but before jpeg_read_header(),
+ * ask the IJG library to save in memory any APP2 markers it may find
+ * in the file.
+ *
+ * 2. After jpeg_read_header(), call jpeg_icc_read_profile() to find
+ * out whether there was a profile and obtain it if so.
+ *
+ * See if there was an ICC profile in the JPEG file being read;
+ * if so, reassemble and return the profile data.
+ *
+ * TRUE is returned if an ICC profile was found, FALSE if not.
+ * If TRUE is returned, *icc_data_ptr is set to point to the
+ * returned data, and *icc_data_len is set to its length.
+ *
+ * IMPORTANT: the data at **icc_data_ptr has been allocated with malloc()
+ * and must be freed by the caller with free() when the caller no longer
+ * needs it. (Alternatively, we could write this routine to use the
+ * IJG library's memory allocator, so that the data would be freed implicitly
+ * at jpeg_finish_decompress() time. But it seems likely that many apps
+ * will prefer to have the data stick around after decompression finishes.)
+ */
+
+gboolean jpeg_icc_read_profile (j_decompress_ptr cinfo,
+ guchar **icc_data_ptr,
+ guint *icc_data_len);
+
+#endif /* __JPEG_ICC_H__ */
diff --git a/plug-ins/file-jpeg/jpeg-load.c b/plug-ins/file-jpeg/jpeg-load.c
new file mode 100644
index 0000000..df53327
--- /dev/null
+++ b/plug-ins/file-jpeg/jpeg-load.c
@@ -0,0 +1,719 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <errno.h>
+#include <setjmp.h>
+
+#include <gio/gio.h>
+#include <glib/gstdio.h>
+#include <gexiv2/gexiv2.h>
+
+#include <jpeglib.h>
+#include <jerror.h>
+
+#include <lcms2.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include "jpeg.h"
+#include "jpeg-icc.h"
+#include "jpeg-settings.h"
+#include "jpeg-load.h"
+
+static gboolean jpeg_load_resolution (gint32 image_ID,
+ struct jpeg_decompress_struct
+ *cinfo);
+
+static void jpeg_load_sanitize_comment (gchar *comment);
+
+static gpointer jpeg_load_cmyk_transform (guint8 *profile_data,
+ gsize profile_len);
+static void jpeg_load_cmyk_to_rgb (guchar *buf,
+ glong pixels,
+ gpointer transform);
+
+gint32 volatile preview_image_ID;
+gint32 preview_layer_ID;
+
+gint32
+load_image (const gchar *filename,
+ GimpRunMode runmode,
+ gboolean preview,
+ gboolean *resolution_loaded,
+ GError **error)
+{
+ gint32 volatile image_ID;
+ gint32 layer_ID;
+ struct jpeg_decompress_struct cinfo;
+ struct my_error_mgr jerr;
+ jpeg_saved_marker_ptr marker;
+ FILE *infile;
+ guchar *buf;
+ guchar **rowbuf;
+ GimpImageBaseType image_type;
+ GimpImageType layer_type;
+ GeglBuffer *buffer = NULL;
+ const Babl *format;
+ gint tile_height;
+ gint i;
+ cmsHTRANSFORM cmyk_transform = NULL;
+
+ /* We set up the normal JPEG error routines. */
+ cinfo.err = jpeg_std_error (&jerr.pub);
+ jerr.pub.error_exit = my_error_exit;
+
+ if (!preview)
+ {
+ jerr.pub.output_message = my_output_message;
+
+ gimp_progress_init_printf (_("Opening '%s'"),
+ gimp_filename_to_utf8 (filename));
+ }
+
+ if ((infile = g_fopen (filename, "rb")) == NULL)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return -1;
+ }
+
+ image_ID = -1;
+
+ /* Establish the setjmp return context for my_error_exit to use. */
+ if (setjmp (jerr.setjmp_buffer))
+ {
+ /* If we get here, the JPEG code has signaled an error.
+ * We need to clean up the JPEG object, close the input file, and return.
+ */
+ jpeg_destroy_decompress (&cinfo);
+ if (infile)
+ fclose (infile);
+
+ if (image_ID != -1 && !preview)
+ gimp_image_delete (image_ID);
+
+ if (preview)
+ destroy_preview ();
+
+ if (buffer)
+ g_object_unref (buffer);
+
+ return -1;
+ }
+
+ /* Now we can initialize the JPEG decompression object. */
+ jpeg_create_decompress (&cinfo);
+
+ /* Step 2: specify data source (eg, a file) */
+
+ jpeg_stdio_src (&cinfo, infile);
+
+ if (! preview)
+ {
+ /* - step 2.1: tell the lib to save the comments */
+ jpeg_save_markers (&cinfo, JPEG_COM, 0xffff);
+
+ /* - step 2.2: tell the lib to save APP1 data (Exif or XMP) */
+ jpeg_save_markers (&cinfo, JPEG_APP0 + 1, 0xffff);
+
+ /* - step 2.3: tell the lib to save APP2 data (ICC profiles) */
+ jpeg_save_markers (&cinfo, JPEG_APP0 + 2, 0xffff);
+ }
+
+ /* Step 3: read file parameters with jpeg_read_header() */
+
+ jpeg_read_header (&cinfo, TRUE);
+
+ /* We can ignore the return value from jpeg_read_header since
+ * (a) suspension is not possible with the stdio data source, and
+ * (b) we passed TRUE to reject a tables-only JPEG file as an error.
+ * See libjpeg.doc for more info.
+ */
+
+ /* Step 4: set parameters for decompression */
+
+ /* In this example, we don't need to change any of the defaults set by
+ * jpeg_read_header(), so we do nothing here, except set the DCT
+ * method.
+ */
+
+ cinfo.dct_method = JDCT_FLOAT;
+
+ /* Step 5: Start decompressor */
+
+ jpeg_start_decompress (&cinfo);
+
+ /* We may need to do some setup of our own at this point before reading
+ * the data. After jpeg_start_decompress() we have the correct scaled
+ * output image dimensions available, as well as the output colormap
+ * if we asked for color quantization.
+ */
+
+ /* temporary buffer */
+ tile_height = gimp_tile_height ();
+ buf = g_new (guchar,
+ tile_height * cinfo.output_width * cinfo.output_components);
+
+ rowbuf = g_new (guchar *, tile_height);
+
+ for (i = 0; i < tile_height; i++)
+ rowbuf[i] = buf + cinfo.output_width * cinfo.output_components * i;
+
+ switch (cinfo.output_components)
+ {
+ case 1:
+ image_type = GIMP_GRAY;
+ layer_type = GIMP_GRAY_IMAGE;
+ break;
+
+ case 3:
+ image_type = GIMP_RGB;
+ layer_type = GIMP_RGB_IMAGE;
+ break;
+
+ case 4:
+ if (cinfo.out_color_space == JCS_CMYK)
+ {
+ image_type = GIMP_RGB;
+ layer_type = GIMP_RGB_IMAGE;
+ break;
+ }
+ /*fallthrough*/
+
+ default:
+ g_message ("Don't know how to load JPEG images "
+ "with %d color channels, using colorspace %d (%d).",
+ cinfo.output_components, cinfo.out_color_space,
+ cinfo.jpeg_color_space);
+ return -1;
+ break;
+ }
+
+ if (preview)
+ {
+ image_ID = preview_image_ID;
+ }
+ else
+ {
+ image_ID = gimp_image_new_with_precision (cinfo.output_width,
+ cinfo.output_height,
+ image_type,
+ GIMP_PRECISION_U8_GAMMA);
+
+ gimp_image_undo_disable (image_ID);
+ gimp_image_set_filename (image_ID, filename);
+ }
+
+ if (preview)
+ {
+ preview_layer_ID = gimp_layer_new (preview_image_ID, _("JPEG preview"),
+ cinfo.output_width,
+ cinfo.output_height,
+ layer_type,
+ 100,
+ gimp_image_get_default_new_layer_mode (preview_image_ID));
+ layer_ID = preview_layer_ID;
+ }
+ else
+ {
+ layer_ID = gimp_layer_new (image_ID, _("Background"),
+ cinfo.output_width,
+ cinfo.output_height,
+ layer_type,
+ 100,
+ gimp_image_get_default_new_layer_mode (image_ID));
+ }
+
+ if (! preview)
+ {
+ GString *comment_buffer = NULL;
+ guint8 *icc_data = NULL;
+ guint icc_length = 0;
+
+ /* Step 5.0: save the original JPEG settings in a parasite */
+ jpeg_detect_original_settings (&cinfo, image_ID);
+
+ /* Step 5.1: check for comments, or Exif metadata in APP1 markers */
+ for (marker = cinfo.marker_list; marker; marker = marker->next)
+ {
+ const gchar *data = (const gchar *) marker->data;
+ gsize len = marker->data_length;
+
+ if (marker->marker == JPEG_COM)
+ {
+#ifdef GIMP_UNSTABLE
+ g_print ("jpeg-load: found image comment (%d bytes)\n",
+ marker->data_length);
+#endif
+
+ if (! comment_buffer)
+ {
+ comment_buffer = g_string_new_len (data, len);
+ }
+ else
+ {
+ /* concatenate multiple comments, separate them with LF */
+ g_string_append_c (comment_buffer, '\n');
+ g_string_append_len (comment_buffer, data, len);
+ }
+ }
+ else if ((marker->marker == JPEG_APP0 + 1)
+ && (len > sizeof (JPEG_APP_HEADER_EXIF) + 8)
+ && ! strcmp (JPEG_APP_HEADER_EXIF, data))
+ {
+#ifdef GIMP_UNSTABLE
+ g_print ("jpeg-load: found Exif block (%d bytes)\n",
+ (gint) (len - sizeof (JPEG_APP_HEADER_EXIF)));
+#endif
+ }
+ }
+
+ if (jpeg_load_resolution (image_ID, &cinfo))
+ {
+ if (resolution_loaded)
+ *resolution_loaded = TRUE;
+ }
+
+ /* if we found any comments, then make a parasite for them */
+ if (comment_buffer && comment_buffer->len)
+ {
+ GimpParasite *parasite;
+
+ jpeg_load_sanitize_comment (comment_buffer->str);
+ parasite = gimp_parasite_new ("gimp-comment",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (comment_buffer->str) + 1,
+ comment_buffer->str);
+ gimp_image_attach_parasite (image_ID, parasite);
+ gimp_parasite_free (parasite);
+
+ g_string_free (comment_buffer, TRUE);
+ }
+
+ /* Step 5.3: check for an embedded ICC profile in APP2 markers */
+ jpeg_icc_read_profile (&cinfo, &icc_data, &icc_length);
+
+ if (cinfo.out_color_space == JCS_CMYK)
+ {
+ cmyk_transform = jpeg_load_cmyk_transform (icc_data, icc_length);
+ }
+ else if (icc_data) /* don't attach the profile if we are transforming */
+ {
+ GimpColorProfile *profile;
+
+ profile = gimp_color_profile_new_from_icc_profile (icc_data,
+ icc_length,
+ NULL);
+ if (profile)
+ {
+ gimp_image_set_color_profile (image_ID, profile);
+ g_object_unref (profile);
+ }
+ }
+
+ g_free (icc_data);
+
+ /* Do not attach the "jpeg-save-options" parasite to the image
+ * because this conflicts with the global defaults (bug #75398).
+ */
+ }
+
+ /* Step 6: while (scan lines remain to be read) */
+ /* jpeg_read_scanlines(...); */
+
+ /* Here we use the library's state variable cinfo.output_scanline as the
+ * loop counter, so that we don't have to keep track ourselves.
+ */
+
+ buffer = gimp_drawable_get_buffer (layer_ID);
+ format = babl_format (image_type == GIMP_RGB ? "R'G'B' u8" : "Y' u8");
+
+ while (cinfo.output_scanline < cinfo.output_height)
+ {
+ gint start, end;
+ gint scanlines;
+ gboolean image_truncated = FALSE;
+
+ start = cinfo.output_scanline;
+ end = cinfo.output_scanline + tile_height;
+ end = MIN (end, cinfo.output_height);
+
+ scanlines = end - start;
+
+ /* in case of error we now jump here, so pertially loaded imaged
+ * don't get discarded
+ */
+ if (setjmp (jerr.setjmp_buffer))
+ {
+ image_truncated = TRUE;
+
+ goto set_buffer;
+ }
+
+ for (i = 0; i < scanlines; i++)
+ jpeg_read_scanlines (&cinfo, (JSAMPARRAY) &rowbuf[i], 1);
+
+ if (cinfo.out_color_space == JCS_CMYK)
+ jpeg_load_cmyk_to_rgb (buf, cinfo.output_width * scanlines,
+ cmyk_transform);
+
+ set_buffer:
+ gegl_buffer_set (buffer,
+ GEGL_RECTANGLE (0, start, cinfo.output_width, scanlines),
+ 0,
+ format,
+ buf,
+ GEGL_AUTO_ROWSTRIDE);
+
+ if (image_truncated)
+ /* jumping to finish skips jpeg_finish_decompress(), its state
+ * might be broken by whatever caused the loading failure
+ */
+ goto finish;
+
+ if (! preview && (cinfo.output_scanline % 32) == 0)
+ gimp_progress_update ((gdouble) cinfo.output_scanline /
+ (gdouble) cinfo.output_height);
+ }
+
+ /* Step 7: Finish decompression */
+
+ jpeg_finish_decompress (&cinfo);
+ /* We can ignore the return value since suspension is not possible
+ * with the stdio data source.
+ */
+
+ finish:
+
+ if (cmyk_transform)
+ cmsDeleteTransform (cmyk_transform);
+
+ /* Step 8: Release JPEG decompression object */
+
+ /* This is an important step since it will release a good deal of memory. */
+ jpeg_destroy_decompress (&cinfo);
+
+ g_object_unref (buffer);
+
+ /* free up the temporary buffers */
+ g_free (rowbuf);
+ g_free (buf);
+
+ /* After finish_decompress, we can close the input file.
+ * Here we postpone it until after no more JPEG errors are possible,
+ * so as to simplify the setjmp error logic above. (Actually, I don't
+ * think that jpeg_destroy can do an error exit, but why assume anything...)
+ */
+ fclose (infile);
+
+ /* At this point you may want to check to see whether any corrupt-data
+ * warnings occurred (test whether jerr.num_warnings is nonzero).
+ */
+
+ /* Detach from the drawable and add it to the image.
+ */
+ if (! preview)
+ {
+ gimp_progress_update (1.0);
+ }
+
+ gimp_image_insert_layer (image_ID, layer_ID, -1, 0);
+
+ return image_ID;
+}
+
+static gboolean
+jpeg_load_resolution (gint32 image_ID,
+ struct jpeg_decompress_struct *cinfo)
+{
+ if (cinfo->saw_JFIF_marker && cinfo->X_density != 0 && cinfo->Y_density != 0)
+ {
+ gdouble xresolution = cinfo->X_density;
+ gdouble yresolution = cinfo->Y_density;
+ gdouble asymmetry = 1.0;
+
+ switch (cinfo->density_unit)
+ {
+ case 0: /* unknown -> set the aspect ratio but use the default
+ * image resolution
+ */
+ asymmetry = xresolution / yresolution;
+
+ gimp_image_get_resolution (image_ID, &xresolution, &yresolution);
+
+ xresolution *= asymmetry;
+ break;
+
+ case 1: /* dots per inch */
+ break;
+
+ case 2: /* dots per cm */
+ xresolution *= 2.54;
+ yresolution *= 2.54;
+ gimp_image_set_unit (image_ID, GIMP_UNIT_MM);
+ break;
+
+ default:
+ g_message ("Unknown density unit %d, assuming dots per inch.",
+ cinfo->density_unit);
+ break;
+ }
+
+ gimp_image_set_resolution (image_ID, xresolution, yresolution);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+ * A number of JPEG files have comments written in a local character set
+ * instead of UTF-8. Some of these files may have been saved by older
+ * versions of GIMP. It is not possible to reliably detect the character
+ * set used, but it is better to keep all characters in the ASCII range
+ * and replace the non-ASCII characters instead of discarding the whole
+ * comment. This is especially useful if the comment contains only a few
+ * non-ASCII characters such as a copyright sign, a soft hyphen, etc.
+ */
+static void
+jpeg_load_sanitize_comment (gchar *comment)
+{
+ const gchar *start_invalid;
+
+ if (! g_utf8_validate (comment, -1, &start_invalid))
+ {
+ guchar *c;
+
+ for (c = (guchar *) start_invalid; *c; c++)
+ {
+ if (*c > 126 || (*c < 32 && *c != '\t' && *c != '\n' && *c != '\r'))
+ *c = '?';
+ }
+ }
+}
+
+gint32
+load_thumbnail_image (GFile *file,
+ gint *width,
+ gint *height,
+ GimpImageType *type,
+ GError **error)
+{
+ gint32 volatile image_ID = -1;
+ struct jpeg_decompress_struct cinfo;
+ struct my_error_mgr jerr;
+ FILE *infile = NULL;
+
+ gimp_progress_init_printf (_("Opening thumbnail for '%s'"),
+ g_file_get_parse_name (file));
+
+ image_ID = gimp_image_metadata_load_thumbnail (file, error);
+ if (image_ID < 1)
+ return -1;
+
+ cinfo.err = jpeg_std_error (&jerr.pub);
+ jerr.pub.error_exit = my_error_exit;
+ jerr.pub.output_message = my_output_message;
+
+ if ((infile = g_fopen (g_file_get_path (file), "rb")) == NULL)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for reading: %s"),
+ g_file_get_parse_name (file), g_strerror (errno));
+
+ if (image_ID != -1)
+ gimp_image_delete (image_ID);
+
+ return -1;
+ }
+
+ /* Establish the setjmp return context for my_error_exit to use. */
+ if (setjmp (jerr.setjmp_buffer))
+ {
+ /* If we get here, the JPEG code has signaled an error. We
+ * need to clean up the JPEG object, close the input file,
+ * and return.
+ */
+ jpeg_destroy_decompress (&cinfo);
+
+ if (image_ID != -1)
+ gimp_image_delete (image_ID);
+
+ return -1;
+ }
+
+ /* Now we can initialize the JPEG decompression object. */
+ jpeg_create_decompress (&cinfo);
+
+ /* Step 2: specify data source (eg, a file) */
+
+ jpeg_stdio_src (&cinfo, infile);
+
+ /* Step 3: read file parameters with jpeg_read_header() */
+
+ jpeg_read_header (&cinfo, TRUE);
+
+ jpeg_start_decompress (&cinfo);
+
+ *width = cinfo.output_width;
+ *height = cinfo.output_height;
+
+ switch (cinfo.output_components)
+ {
+ case 1:
+ *type = GIMP_GRAY_IMAGE;
+ break;
+
+ case 3:
+ *type = GIMP_RGB_IMAGE;
+ break;
+
+ case 4:
+ if (cinfo.out_color_space == JCS_CMYK)
+ {
+ *type = GIMP_RGB_IMAGE;
+ break;
+ }
+ /*fallthrough*/
+
+ default:
+ g_message ("Don't know how to load JPEG images "
+ "with %d color channels, using colorspace %d (%d).",
+ cinfo.output_components, cinfo.out_color_space,
+ cinfo.jpeg_color_space);
+
+ gimp_image_delete (image_ID);
+ image_ID = -1;
+ break;
+ }
+
+ /* Step 4: Release JPEG decompression object */
+
+ /* This is an important step since it will release a good deal
+ * of memory.
+ */
+ jpeg_destroy_decompress (&cinfo);
+
+ fclose (infile);
+
+ return image_ID;
+}
+
+static gpointer
+jpeg_load_cmyk_transform (guint8 *profile_data,
+ gsize profile_len)
+{
+ GimpColorConfig *config = gimp_get_color_configuration ();
+ GimpColorProfile *cmyk_profile = NULL;
+ GimpColorProfile *rgb_profile = NULL;
+ cmsHPROFILE cmyk_lcms;
+ cmsHPROFILE rgb_lcms;
+ cmsUInt32Number flags = 0;
+ cmsHTRANSFORM transform;
+
+ /* try to load the embedded CMYK profile */
+ if (profile_data)
+ {
+ cmyk_profile = gimp_color_profile_new_from_icc_profile (profile_data,
+ profile_len,
+ NULL);
+
+ if (cmyk_profile && ! gimp_color_profile_is_cmyk (cmyk_profile))
+ {
+ g_object_unref (cmyk_profile);
+ cmyk_profile = NULL;
+ }
+ }
+
+ /* if that fails, try to load the CMYK profile configured in the prefs */
+ if (! cmyk_profile)
+ cmyk_profile = gimp_color_config_get_cmyk_color_profile (config, NULL);
+
+ /* bail out if we can't load any CMYK profile */
+ if (! cmyk_profile)
+ {
+ g_object_unref (config);
+ return NULL;
+ }
+
+ /* always convert to sRGB */
+ rgb_profile = gimp_color_profile_new_rgb_srgb ();
+
+ cmyk_lcms = gimp_color_profile_get_lcms_profile (cmyk_profile);
+ rgb_lcms = gimp_color_profile_get_lcms_profile (rgb_profile);
+
+ if (config->display_intent ==
+ GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC)
+ {
+ flags |= cmsFLAGS_BLACKPOINTCOMPENSATION;
+ }
+
+ transform = cmsCreateTransform (cmyk_lcms, TYPE_CMYK_8_REV,
+ rgb_lcms, TYPE_RGB_8,
+ config->display_intent,
+ flags);
+
+ g_object_unref (cmyk_profile);
+ g_object_unref (rgb_profile);
+
+ g_object_unref (config);
+
+ return transform;
+}
+
+
+static void
+jpeg_load_cmyk_to_rgb (guchar *buf,
+ glong pixels,
+ gpointer transform)
+{
+ const guchar *src = buf;
+ guchar *dest = buf;
+
+ if (transform)
+ {
+ cmsDoTransform (transform, buf, buf, pixels);
+ return;
+ }
+
+ /* NOTE: The following code assumes inverted CMYK values, even when an
+ APP14 marker doesn't exist. This is the behavior of recent versions
+ of PhotoShop as well. */
+
+ while (pixels--)
+ {
+ guint c = src[0];
+ guint m = src[1];
+ guint y = src[2];
+ guint k = src[3];
+
+ dest[0] = (c * k) / 255;
+ dest[1] = (m * k) / 255;
+ dest[2] = (y * k) / 255;
+
+ src += 4;
+ dest += 3;
+ }
+}
diff --git a/plug-ins/file-jpeg/jpeg-load.h b/plug-ins/file-jpeg/jpeg-load.h
new file mode 100644
index 0000000..3844097
--- /dev/null
+++ b/plug-ins/file-jpeg/jpeg-load.h
@@ -0,0 +1,33 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __JPEG_LOAD_H__
+#define __JPEG_LOAD_H__
+
+gint32 load_image (const gchar *filename,
+ GimpRunMode runmode,
+ gboolean preview,
+ gboolean *resolution_loaded,
+ GError **error);
+
+gint32 load_thumbnail_image (GFile *file,
+ gint *width,
+ gint *height,
+ GimpImageType *type,
+ GError **error);
+
+#endif /* __JPEG_LOAD_H__ */
diff --git a/plug-ins/file-jpeg/jpeg-quality.c b/plug-ins/file-jpeg/jpeg-quality.c
new file mode 100644
index 0000000..52bd2c7
--- /dev/null
+++ b/plug-ins/file-jpeg/jpeg-quality.c
@@ -0,0 +1,148 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * jpeg-quality.c
+ * Copyright (C) 2007 Raphaël Quinet <raphael@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <glib/gstdio.h>
+
+#include <jpeglib.h>
+
+#include "jpeg-quality.h"
+
+/*
+ * Note that although 0 is a valid quality for IJG's JPEG library, the
+ * baseline quantization tables for quality 0 and 1 are identical (all
+ * divisors are set to the maximum value 255). So 0 can be used here
+ * to flag unusual settings.
+ */
+
+/* sum of the luminance divisors for quality = 0..100 (IJG standard tables) */
+static gint std_luminance_sum[101] =
+{
+ G_MAXINT,
+ 16320, 16315, 15946, 15277, 14655, 14073, 13623, 13230, 12859, 12560, 12240,
+ 11861, 11456, 11081, 10714, 10360, 10027, 9679, 9368, 9056, 8680, 8331,
+ 7995, 7668, 7376, 7084, 6823, 6562, 6345, 6125, 5939, 5756, 5571,
+ 5421, 5240, 5086, 4976, 4829, 4719, 4616, 4463, 4393, 4280, 4166,
+ 4092, 3980, 3909, 3835, 3755, 3688, 3621, 3541, 3467, 3396, 3323,
+ 3247, 3170, 3096, 3021, 2952, 2874, 2804, 2727, 2657, 2583, 2509,
+ 2437, 2362, 2290, 2211, 2136, 2068, 1996, 1915, 1858, 1773, 1692,
+ 1620, 1552, 1477, 1398, 1326, 1251, 1179, 1109, 1031, 961, 884,
+ 814, 736, 667, 592, 518, 441, 369, 292, 221, 151, 86,
+ 64
+};
+
+/* sum of the chrominance divisors for quality = 0..100 (IJG standard tables) */
+static gint std_chrominance_sum[101] =
+{
+ G_MAXINT,
+ 16320, 16320, 16320, 16218, 16010, 15731, 15523, 15369, 15245, 15110, 14985,
+ 14864, 14754, 14635, 14526, 14429, 14346, 14267, 14204, 13790, 13121, 12511,
+ 11954, 11453, 11010, 10567, 10175, 9787, 9455, 9122, 8844, 8565, 8288,
+ 8114, 7841, 7616, 7447, 7227, 7060, 6897, 6672, 6562, 6396, 6226,
+ 6116, 5948, 5838, 5729, 5614, 5505, 5396, 5281, 5172, 5062, 4947,
+ 4837, 4726, 4614, 4506, 4395, 4282, 4173, 4061, 3950, 3839, 3727,
+ 3617, 3505, 3394, 3284, 3169, 3060, 2949, 2836, 2780, 2669, 2556,
+ 2445, 2336, 2221, 2111, 2000, 1888, 1778, 1666, 1555, 1444, 1332,
+ 1223, 1110, 999, 891, 779, 668, 558, 443, 333, 224, 115,
+ 64
+};
+
+/**
+ * jpeg_detect_quality:
+ * @cinfo: a pointer to a JPEG decompressor info.
+ *
+ * Returns the exact or estimated quality value that was used to save
+ * the JPEG image by analyzing the quantization table divisors.
+ *
+ * If an exact match for the IJG quantization tables is found, then a
+ * quality setting in the range 1..100 is returned. If the quality
+ * can only be estimated, then a negative number in the range -1..-100
+ * is returned; its absolute value represents the maximum IJG quality
+ * setting to use. If the quality cannot be reliably determined, then
+ * 0 is returned.
+ *
+ * This function must be called after jpeg_read_header() so that
+ * @cinfo contains the quantization tables read from the DQT markers
+ * in the file.
+ *
+ * Return Value: the JPEG quality setting in the range 1..100, -1..-100 or 0.
+ */
+gint
+jpeg_detect_quality (struct jpeg_decompress_struct *cinfo)
+{
+ gint t;
+ gint i;
+ gint sum[3];
+ gint q;
+
+ /* files using CMYK or having 4 quantization tables are unusual */
+ if (!cinfo || cinfo->output_components > 3 || cinfo->quant_tbl_ptrs[3])
+ return 0;
+
+ /* Most files use table 0 for luminance divisors (Y) and table 1 for
+ * chrominance divisors (Cb and Cr). Some files use separate tables
+ * for Cb and Cr, so table 2 may also be used.
+ */
+ for (t = 0; t < 3; t++)
+ {
+ sum[t] = 0;
+ if (cinfo->quant_tbl_ptrs[t])
+ for (i = 0; i < DCTSIZE2; i++)
+ sum[t] += cinfo->quant_tbl_ptrs[t]->quantval[i];
+ }
+
+ if (cinfo->output_components > 1)
+ {
+ gint sums;
+
+ if (sum[0] < 64 || sum[1] < 64)
+ return 0;
+
+ /* compare with the chrominance table having the lowest sum */
+ if (sum[1] < sum[2] || sum[2] <= 0)
+ sums = sum[0] + sum[1];
+ else
+ sums = sum[0] + sum[2];
+
+ q = 100;
+ while (sums > std_luminance_sum[q] + std_chrominance_sum[q])
+ q--;
+
+ if (sum[0] == std_luminance_sum[q] && sum[1] == std_chrominance_sum[q])
+ return q;
+ else
+ return -q;
+ }
+ else
+ {
+ if (sum[0] < 64)
+ return 0;
+
+ q = 100;
+ while (sum[0] > std_luminance_sum[q])
+ q--;
+
+ if (sum[0] == std_luminance_sum[q])
+ return q;
+ else
+ return -q;
+ }
+}
diff --git a/plug-ins/file-jpeg/jpeg-quality.h b/plug-ins/file-jpeg/jpeg-quality.h
new file mode 100644
index 0000000..d20600b
--- /dev/null
+++ b/plug-ins/file-jpeg/jpeg-quality.h
@@ -0,0 +1,26 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * jpeg-quality.h
+ * Copyright (C) 2007 Raphaël Quinet <raphael@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __JPEG_QUALITY_H__
+#define __JPEG_QUALITY_H__
+
+gint jpeg_detect_quality (struct jpeg_decompress_struct *cinfo);
+
+#endif /* __JPEG_QUALITY_H__ */
diff --git a/plug-ins/file-jpeg/jpeg-save.c b/plug-ins/file-jpeg/jpeg-save.c
new file mode 100644
index 0000000..4af4d1d
--- /dev/null
+++ b/plug-ins/file-jpeg/jpeg-save.c
@@ -0,0 +1,1524 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <setjmp.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <glib/gstdio.h>
+
+#include <jpeglib.h>
+#include <jerror.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include "jpeg.h"
+#include "jpeg-icc.h"
+#include "jpeg-load.h"
+#include "jpeg-save.h"
+#include "jpeg-settings.h"
+
+#ifdef C_ARITH_CODING_SUPPORTED
+static gboolean arithc_supported = TRUE;
+#else
+static gboolean arithc_supported = FALSE;
+#endif
+
+#define SCALE_WIDTH 125
+
+/* See bugs #63610 and #61088 for a discussion about the quality settings */
+#define DEFAULT_IJG_QUALITY 90.0
+#define DEFAULT_SMOOTHING 0.0
+#define DEFAULT_OPTIMIZE TRUE
+#define DEFAULT_ARITHMETIC_CODING FALSE
+#define DEFAULT_PROGRESSIVE TRUE
+#define DEFAULT_BASELINE TRUE
+#define DEFAULT_SUBSMP JPEG_SUBSAMPLING_1x1_1x1_1x1
+#define DEFAULT_RESTART 0
+#define DEFAULT_RESTART_MCU_ROWS 16
+#define DEFAULT_DCT 0
+#define DEFAULT_PREVIEW FALSE
+#define DEFAULT_EXIF FALSE
+#define DEFAULT_XMP FALSE
+#define DEFAULT_IPTC FALSE
+#define DEFAULT_THUMBNAIL FALSE
+#define DEFAULT_PROFILE TRUE
+#define DEFAULT_USE_ORIG_QUALITY FALSE
+
+#define JPEG_DEFAULTS_PARASITE "jpeg-save-defaults"
+
+
+typedef struct
+{
+ struct jpeg_compress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ gint tile_height;
+ FILE *outfile;
+ gboolean has_alpha;
+ gint rowstride;
+ guchar *data;
+ guchar *src;
+ GeglBuffer *buffer;
+ const Babl *format;
+ const gchar *file_name;
+ gboolean abort_me;
+ guint source_id;
+} PreviewPersistent;
+
+/*le added : struct containing pointers to export dialog*/
+typedef struct
+{
+ gboolean run;
+ GtkWidget *use_restart_markers; /*checkbox setting use restart markers*/
+ GtkTextBuffer *text_buffer;
+ GtkAdjustment *scale_data; /*for restart markers*/
+ gulong handler_id_restart;
+
+ GtkAdjustment *quality; /*quality slidebar*/
+ GtkAdjustment *smoothing; /*smoothing slidebar*/
+ GtkWidget *optimize; /*optimize toggle*/
+ GtkWidget *arithmetic_coding; /*arithmetic coding toggle*/
+ GtkWidget *progressive; /*progressive toggle*/
+ GtkWidget *subsmp; /*subsampling side select*/
+ GtkWidget *restart; /*spinner for setting frequency restart markers*/
+ GtkWidget *dct; /*DCT side select*/
+ GtkWidget *preview; /*show preview toggle checkbox*/
+ GtkWidget *save_exif;
+ GtkWidget *save_xmp;
+ GtkWidget *save_iptc;
+ GtkWidget *save_thumbnail;
+ GtkWidget *save_profile;
+ GtkWidget *use_orig_quality; /*quant tables toggle*/
+} JpegSaveGui;
+
+static void make_preview (void);
+
+static void save_restart_update (GtkAdjustment *adjustment,
+ GtkWidget *toggle);
+static void subsampling_changed (GtkWidget *combo,
+ GtkAdjustment *entry);
+static void quality_changed (GtkAdjustment *scale_entry,
+ GtkWidget *toggle);
+static void subsampling_changed2 (GtkWidget *combo,
+ GtkWidget *toggle);
+static void use_orig_qual_changed (GtkWidget *toggle,
+ GtkAdjustment *scale_entry);
+static void use_orig_qual_changed2 (GtkWidget *toggle,
+ GtkWidget *combo);
+
+
+static GtkWidget *restart_markers_scale = NULL;
+static GtkWidget *restart_markers_label = NULL;
+static GtkWidget *preview_size = NULL;
+static PreviewPersistent *prev_p = NULL;
+
+static void save_dialog_response (GtkWidget *widget,
+ gint response_id,
+ gpointer data);
+
+static void load_gui_defaults (JpegSaveGui *pg);
+static void save_defaults (void);
+
+
+/*
+ * sg - This is the best I can do, I'm afraid... I think it will fail
+ * if something bad really happens (but it might not). If you have a
+ * better solution, send it ;-)
+ */
+static void
+background_error_exit (j_common_ptr cinfo)
+{
+ if (prev_p)
+ prev_p->abort_me = TRUE;
+ (*cinfo->err->output_message) (cinfo);
+}
+
+static gboolean
+background_jpeg_save (PreviewPersistent *pp)
+{
+ gint yend;
+
+ if (pp->abort_me || (pp->cinfo.next_scanline >= pp->cinfo.image_height))
+ {
+ /* clean up... */
+ if (pp->abort_me)
+ {
+ jpeg_abort_compress (&(pp->cinfo));
+ }
+ else
+ {
+ jpeg_finish_compress (&(pp->cinfo));
+ }
+
+ fclose (pp->outfile);
+ jpeg_destroy_compress (&(pp->cinfo));
+
+ g_free (pp->data);
+
+ if (pp->buffer)
+ g_object_unref (pp->buffer);
+
+ /* display the preview stuff */
+ if (!pp->abort_me)
+ {
+ GFile *file = g_file_new_for_path (pp->file_name);
+ GFileInfo *info;
+ gchar *text;
+ GError *error = NULL;
+
+ info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_SIZE,
+ G_FILE_QUERY_INFO_NONE,
+ NULL, &error);
+
+ if (info)
+ {
+ goffset size = g_file_info_get_size (info);
+ gchar *size_text;
+
+ size_text = g_format_size (size);
+ text = g_strdup_printf (_("File size: %s"), size_text);
+ g_free (size_text);
+
+ g_object_unref (info);
+ }
+ else
+ {
+ text = g_strdup_printf (_("File size: %s"), error->message);
+ g_clear_error (&error);
+ }
+
+ gtk_label_set_text (GTK_LABEL (preview_size), text);
+ g_free (text);
+
+ g_object_unref (file);
+
+ /* and load the preview */
+ load_image (pp->file_name, GIMP_RUN_NONINTERACTIVE, TRUE, NULL, NULL);
+ }
+
+ /* we cleanup here (load_image doesn't run in the background) */
+ g_unlink (pp->file_name);
+
+ g_free (pp);
+ prev_p = NULL;
+
+ gimp_displays_flush ();
+ gdk_flush ();
+
+ return FALSE;
+ }
+ else
+ {
+ if ((pp->cinfo.next_scanline % pp->tile_height) == 0)
+ {
+ yend = pp->cinfo.next_scanline + pp->tile_height;
+ yend = MIN (yend, pp->cinfo.image_height);
+ gegl_buffer_get (pp->buffer,
+ GEGL_RECTANGLE (0, pp->cinfo.next_scanline,
+ pp->cinfo.image_width,
+ (yend - pp->cinfo.next_scanline)),
+ 1.0,
+ pp->format,
+ pp->data,
+ GEGL_AUTO_ROWSTRIDE,
+ GEGL_ABYSS_NONE);
+ pp->src = pp->data;
+ }
+
+ jpeg_write_scanlines (&(pp->cinfo), (JSAMPARRAY) &(pp->src), 1);
+ pp->src += pp->rowstride;
+
+ return TRUE;
+ }
+}
+
+gboolean
+save_image (const gchar *filename,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ gint32 orig_image_ID,
+ gboolean preview,
+ GError **error)
+{
+ static struct jpeg_compress_struct cinfo;
+ static struct my_error_mgr jerr;
+
+ GimpImageType drawable_type;
+ GeglBuffer *buffer;
+ const Babl *format;
+ JpegSubsampling subsampling;
+ FILE * volatile outfile;
+ guchar *data;
+ guchar *src;
+ GimpColorProfile *profile = NULL;
+
+ gboolean has_alpha;
+ gboolean out_linear = FALSE;
+ gint rowstride, yend;
+
+ drawable_type = gimp_drawable_type (drawable_ID);
+ buffer = gimp_drawable_get_buffer (drawable_ID);
+
+ if (! preview)
+ gimp_progress_init_printf (_("Exporting '%s'"),
+ gimp_filename_to_utf8 (filename));
+
+ /* Step 1: allocate and initialize JPEG compression object */
+
+ /* We have to set up the error handler first, in case the initialization
+ * step fails. (Unlikely, but it could happen if you are out of memory.)
+ * This routine fills in the contents of struct jerr, and returns jerr's
+ * address which we place into the link field in cinfo.
+ */
+ cinfo.err = jpeg_std_error (&jerr.pub);
+ jerr.pub.error_exit = my_error_exit;
+
+ outfile = NULL;
+ /* Establish the setjmp return context for my_error_exit to use. */
+ if (setjmp (jerr.setjmp_buffer))
+ {
+ /* If we get here, the JPEG code has signaled an error.
+ * We need to clean up the JPEG object, close the input file, and return.
+ */
+ jpeg_destroy_compress (&cinfo);
+ if (outfile)
+ fclose (outfile);
+ if (buffer)
+ g_object_unref (buffer);
+
+ return FALSE;
+ }
+
+ /* Now we can initialize the JPEG compression object. */
+ jpeg_create_compress (&cinfo);
+
+ /* Step 2: specify data destination (eg, a file) */
+ /* Note: steps 2 and 3 can be done in either order. */
+
+ /* Here we use the library-supplied code to send compressed data to a
+ * stdio stream. You can also write your own code to do something else.
+ * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
+ * requires it in order to write binary files.
+ */
+ if ((outfile = g_fopen (filename, "wb")) == NULL)
+ {
+ g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
+ _("Could not open '%s' for writing: %s"),
+ gimp_filename_to_utf8 (filename), g_strerror (errno));
+ return FALSE;
+ }
+
+ /* When we don't save profiles, we convert data to sRGB because
+ * that's what most/all readers expect on a no-profile JPEG.
+ * If we save an assigned profile, let's just follow its TRC.
+ * If we save the default linear profile (i.e. no assigned
+ * profile), we convert it to sRGB, except when it is 8-bit linear.
+ */
+ if (jsvals.save_profile)
+ {
+ profile = gimp_image_get_color_profile (orig_image_ID);
+
+ /* If a profile is explicitly set, follow its TRC, whatever the
+ * storage format.
+ */
+ if (profile && gimp_color_profile_is_linear (profile))
+ out_linear = TRUE;
+
+ if (! profile)
+ {
+ /* There is always an effective profile. */
+ profile = gimp_image_get_effective_color_profile (orig_image_ID);
+
+ if (gimp_color_profile_is_linear (profile))
+ {
+ if (gimp_image_get_precision (image_ID) != GIMP_PRECISION_U8_LINEAR)
+ {
+ GimpColorProfile *saved_profile;
+
+ saved_profile = gimp_color_profile_new_srgb_trc_from_color_profile (profile);
+ g_object_unref (profile);
+ profile = saved_profile;
+ }
+ else
+ {
+ /* Keep linear profile as-is for 8-bit linear image. */
+ out_linear = TRUE;
+ }
+ }
+ }
+ }
+
+ jpeg_stdio_dest (&cinfo, outfile);
+
+ /* Get the input image and a pointer to its data.
+ */
+ switch (drawable_type)
+ {
+ case GIMP_RGB_IMAGE:
+ /* # of color components per pixel */
+ cinfo.input_components = 3;
+ has_alpha = FALSE;
+
+ if (out_linear)
+ format = babl_format ("RGB u8");
+ else
+ format = babl_format ("R'G'B' u8");
+ break;
+
+ case GIMP_GRAY_IMAGE:
+ /* # of color components per pixel */
+ cinfo.input_components = 1;
+ has_alpha = FALSE;
+
+ if (out_linear)
+ format = babl_format ("Y u8");
+ else
+ format = babl_format ("Y' u8");
+ break;
+
+ case GIMP_RGBA_IMAGE:
+ /* # of color components per pixel (minus the GIMP alpha channel) */
+ cinfo.input_components = 4 - 1;
+ has_alpha = TRUE;
+
+ if (out_linear)
+ format = babl_format ("RGB u8");
+ else
+ format = babl_format ("R'G'B' u8");
+ break;
+
+ case GIMP_GRAYA_IMAGE:
+ /* # of color components per pixel (minus the GIMP alpha channel) */
+ cinfo.input_components = 2 - 1;
+ has_alpha = TRUE;
+ if (out_linear)
+ format = babl_format ("Y u8");
+ else
+ format = babl_format ("Y' u8");
+ break;
+
+ case GIMP_INDEXED_IMAGE:
+ default:
+ return FALSE;
+ }
+
+ /* Step 3: set parameters for compression */
+
+ /* First we supply a description of the input image.
+ * Four fields of the cinfo struct must be filled in:
+ */
+ /* image width and height, in pixels */
+ cinfo.image_width = gegl_buffer_get_width (buffer);
+ cinfo.image_height = gegl_buffer_get_height (buffer);
+ /* colorspace of input image */
+ cinfo.in_color_space = (drawable_type == GIMP_RGB_IMAGE ||
+ drawable_type == GIMP_RGBA_IMAGE)
+ ? JCS_RGB : JCS_GRAYSCALE;
+ /* Now use the library's routine to set default compression parameters.
+ * (You must set at least cinfo.in_color_space before calling this,
+ * since the defaults depend on the source color space.)
+ */
+ jpeg_set_defaults (&cinfo);
+
+ jpeg_set_quality (&cinfo, (gint) (jsvals.quality + 0.5), jsvals.baseline);
+
+ if (jsvals.use_orig_quality && num_quant_tables > 0)
+ {
+ guint **quant_tables;
+ gint t;
+
+ /* override tables generated by jpeg_set_quality() with custom tables */
+ quant_tables = jpeg_restore_original_tables (image_ID, num_quant_tables);
+ if (quant_tables)
+ {
+ for (t = 0; t < num_quant_tables; t++)
+ {
+ jpeg_add_quant_table (&cinfo, t, quant_tables[t],
+ 100, jsvals.baseline);
+ g_free (quant_tables[t]);
+ }
+ g_free (quant_tables);
+ }
+ }
+
+ if (arithc_supported)
+ {
+ cinfo.arith_code = jsvals.arithmetic_coding;
+ if (!jsvals.arithmetic_coding)
+ cinfo.optimize_coding = jsvals.optimize;
+ }
+ else
+ cinfo.optimize_coding = jsvals.optimize;
+
+ subsampling = (gimp_drawable_is_rgb (drawable_ID) ?
+ jsvals.subsmp : JPEG_SUBSAMPLING_1x1_1x1_1x1);
+
+ /* smoothing is not supported with nonstandard sampling ratios */
+ if (subsampling != JPEG_SUBSAMPLING_2x1_1x1_1x1 &&
+ subsampling != JPEG_SUBSAMPLING_1x2_1x1_1x1)
+ {
+ cinfo.smoothing_factor = (gint) (jsvals.smoothing * 100);
+ }
+
+ if (jsvals.progressive)
+ {
+ jpeg_simple_progression (&cinfo);
+ }
+
+ switch (subsampling)
+ {
+ case JPEG_SUBSAMPLING_2x2_1x1_1x1:
+ default:
+ cinfo.comp_info[0].h_samp_factor = 2;
+ cinfo.comp_info[0].v_samp_factor = 2;
+ cinfo.comp_info[1].h_samp_factor = 1;
+ cinfo.comp_info[1].v_samp_factor = 1;
+ cinfo.comp_info[2].h_samp_factor = 1;
+ cinfo.comp_info[2].v_samp_factor = 1;
+ break;
+
+ case JPEG_SUBSAMPLING_2x1_1x1_1x1:
+ cinfo.comp_info[0].h_samp_factor = 2;
+ cinfo.comp_info[0].v_samp_factor = 1;
+ cinfo.comp_info[1].h_samp_factor = 1;
+ cinfo.comp_info[1].v_samp_factor = 1;
+ cinfo.comp_info[2].h_samp_factor = 1;
+ cinfo.comp_info[2].v_samp_factor = 1;
+ break;
+
+ case JPEG_SUBSAMPLING_1x1_1x1_1x1:
+ cinfo.comp_info[0].h_samp_factor = 1;
+ cinfo.comp_info[0].v_samp_factor = 1;
+ cinfo.comp_info[1].h_samp_factor = 1;
+ cinfo.comp_info[1].v_samp_factor = 1;
+ cinfo.comp_info[2].h_samp_factor = 1;
+ cinfo.comp_info[2].v_samp_factor = 1;
+ break;
+
+ case JPEG_SUBSAMPLING_1x2_1x1_1x1:
+ cinfo.comp_info[0].h_samp_factor = 1;
+ cinfo.comp_info[0].v_samp_factor = 2;
+ cinfo.comp_info[1].h_samp_factor = 1;
+ cinfo.comp_info[1].v_samp_factor = 1;
+ cinfo.comp_info[2].h_samp_factor = 1;
+ cinfo.comp_info[2].v_samp_factor = 1;
+ break;
+ }
+
+ cinfo.restart_interval = 0;
+ cinfo.restart_in_rows = jsvals.restart;
+
+ switch (jsvals.dct)
+ {
+ case 0:
+ default:
+ cinfo.dct_method = JDCT_ISLOW;
+ break;
+
+ case 1:
+ cinfo.dct_method = JDCT_IFAST;
+ break;
+
+ case 2:
+ cinfo.dct_method = JDCT_FLOAT;
+ break;
+ }
+
+ {
+ gdouble xresolution;
+ gdouble yresolution;
+
+ gimp_image_get_resolution (orig_image_ID, &xresolution, &yresolution);
+
+ if (xresolution > 1e-5 && yresolution > 1e-5)
+ {
+ gdouble factor;
+
+ factor = gimp_unit_get_factor (gimp_image_get_unit (orig_image_ID));
+
+ if (factor == 2.54 /* cm */ ||
+ factor == 25.4 /* mm */)
+ {
+ cinfo.density_unit = 2; /* dots per cm */
+
+ xresolution /= 2.54;
+ yresolution /= 2.54;
+ }
+ else
+ {
+ cinfo.density_unit = 1; /* dots per inch */
+ }
+
+ cinfo.X_density = xresolution;
+ cinfo.Y_density = yresolution;
+ }
+ }
+
+ /* Step 4: Start compressor */
+
+ /* TRUE ensures that we will write a complete interchange-JPEG file.
+ * Pass TRUE unless you are very sure of what you're doing.
+ */
+ jpeg_start_compress (&cinfo, TRUE);
+
+ /* Step 4.1: Write the comment out - pw */
+ if (image_comment && *image_comment)
+ {
+#ifdef GIMP_UNSTABLE
+ g_print ("jpeg-save: saving image comment (%d bytes)\n",
+ (int) strlen (image_comment));
+#endif
+ jpeg_write_marker (&cinfo, JPEG_COM,
+ (guchar *) image_comment, strlen (image_comment));
+ }
+
+ /* Step 4.2: store the color profile */
+ if (jsvals.save_profile)
+ {
+ const guint8 *icc_data;
+ gsize icc_length;
+
+ icc_data = gimp_color_profile_get_icc_profile (profile, &icc_length);
+ jpeg_icc_write_profile (&cinfo, icc_data, icc_length);
+
+ g_object_unref (profile);
+ }
+
+ /* Step 5: while (scan lines remain to be written) */
+ /* jpeg_write_scanlines(...); */
+
+ /* Here we use the library's state variable cinfo.next_scanline as the
+ * loop counter, so that we don't have to keep track ourselves.
+ * To keep things simple, we pass one scanline per call; you can pass
+ * more if you wish, though.
+ */
+ /* JSAMPLEs per row in image_buffer */
+ rowstride = cinfo.input_components * cinfo.image_width;
+ data = g_new (guchar, rowstride * gimp_tile_height ());
+
+ /* fault if cinfo.next_scanline isn't initially a multiple of
+ * gimp_tile_height */
+ src = NULL;
+
+ /*
+ * sg - if we preview, we want this to happen in the background -- do
+ * not duplicate code in the future; for now, it's OK
+ */
+
+ if (preview)
+ {
+ PreviewPersistent *pp = g_new (PreviewPersistent, 1);
+
+ /* pass all the information we need */
+ pp->cinfo = cinfo;
+ pp->tile_height = gimp_tile_height();
+ pp->data = data;
+ pp->outfile = outfile;
+ pp->has_alpha = has_alpha;
+ pp->rowstride = rowstride;
+ pp->data = data;
+ pp->buffer = buffer;
+ pp->format = format;
+ pp->src = NULL;
+ pp->file_name = filename;
+ pp->abort_me = FALSE;
+
+ g_warn_if_fail (prev_p == NULL);
+ prev_p = pp;
+
+ pp->cinfo.err = jpeg_std_error(&(pp->jerr));
+ pp->jerr.error_exit = background_error_exit;
+
+ gtk_label_set_text (GTK_LABEL (preview_size),
+ _("Calculating file size..."));
+
+ pp->source_id = g_idle_add ((GSourceFunc) background_jpeg_save, pp);
+
+ /* background_jpeg_save() will cleanup as needed */
+ return TRUE;
+ }
+
+ while (cinfo.next_scanline < cinfo.image_height)
+ {
+ if ((cinfo.next_scanline % gimp_tile_height ()) == 0)
+ {
+ yend = cinfo.next_scanline + gimp_tile_height ();
+ yend = MIN (yend, cinfo.image_height);
+ gegl_buffer_get (buffer,
+ GEGL_RECTANGLE (0, cinfo.next_scanline,
+ cinfo.image_width,
+ (yend - cinfo.next_scanline)),
+ 1.0,
+ format,
+ data,
+ GEGL_AUTO_ROWSTRIDE,
+ GEGL_ABYSS_NONE);
+ src = data;
+ }
+
+ jpeg_write_scanlines (&cinfo, (JSAMPARRAY) &src, 1);
+ src += rowstride;
+
+ if ((cinfo.next_scanline % 32) == 0)
+ gimp_progress_update ((gdouble) cinfo.next_scanline /
+ (gdouble) cinfo.image_height);
+ }
+
+ /* Step 6: Finish compression */
+ jpeg_finish_compress (&cinfo);
+ /* After finish_compress, we can close the output file. */
+ fclose (outfile);
+
+ /* Step 7: release JPEG compression object */
+
+ /* This is an important step since it will release a good deal of memory. */
+ jpeg_destroy_compress (&cinfo);
+
+ /* free the temporary buffer */
+ g_free (data);
+
+ /* And we're done! */
+ gimp_progress_update (1.0);
+
+ g_object_unref (buffer);
+
+ return TRUE;
+}
+
+static void
+make_preview (void)
+{
+ destroy_preview ();
+
+ if (jsvals.preview)
+ {
+ gchar *tn = gimp_temp_name ("jpeg");
+
+ if (! undo_touched)
+ {
+ /* we freeze undo saving so that we can avoid sucking up
+ * tile cache with our unneeded preview steps. */
+ gimp_image_undo_freeze (preview_image_ID);
+
+ undo_touched = TRUE;
+ }
+
+ save_image (tn,
+ preview_image_ID,
+ drawable_ID_global,
+ orig_image_ID_global,
+ TRUE, NULL);
+
+ if (display_ID == -1)
+ display_ID = gimp_display_new (preview_image_ID);
+ }
+ else
+ {
+ gtk_label_set_text (GTK_LABEL (preview_size), _("File size: unknown"));
+
+ gimp_displays_flush ();
+ }
+}
+
+void
+destroy_preview (void)
+{
+ if (prev_p && !prev_p->abort_me)
+ {
+ guint id = prev_p->source_id;
+ prev_p->abort_me = TRUE; /* signal the background save to stop */
+ background_jpeg_save (prev_p);
+ g_source_remove (id);
+ }
+
+ if (gimp_image_is_valid (preview_image_ID) &&
+ gimp_item_is_valid (preview_layer_ID))
+ {
+ /* assuming that reference counting is working correctly,
+ we do not need to delete the layer, removing it from
+ the image should be sufficient */
+ gimp_image_remove_layer (preview_image_ID, preview_layer_ID);
+
+ preview_layer_ID = -1;
+ }
+}
+
+static void
+toggle_arithmetic_coding (GtkToggleButton *togglebutton,
+ gpointer user_data)
+{
+ GtkWidget *optimize = GTK_WIDGET (user_data);
+
+ gtk_widget_set_sensitive (optimize,
+ !gtk_toggle_button_get_active (togglebutton));
+}
+
+gboolean
+save_dialog (void)
+{
+ JpegSaveGui pg;
+ GtkWidget *dialog;
+ GtkWidget *vbox;
+ GtkWidget *vbox2;
+ GtkAdjustment *entry;
+ GtkWidget *table;
+ GtkWidget *table2;
+ GtkWidget *tabledefaults;
+ GtkWidget *expander;
+ GtkWidget *frame;
+ GtkWidget *toggle;
+ GtkWidget *spinbutton;
+ GtkWidget *label;
+ GtkWidget *combo;
+ GtkWidget *text_view;
+ GtkTextBuffer *text_buffer;
+ GtkWidget *scrolled_window;
+ GtkWidget *button;
+ gchar *text;
+ gint row;
+
+ dialog = gimp_export_dialog_new (_("JPEG"), PLUG_IN_BINARY, SAVE_PROC);
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (save_dialog_response),
+ &pg);
+ g_signal_connect (dialog, "destroy",
+ G_CALLBACK (gtk_main_quit),
+ NULL);
+
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+ vbox, TRUE, TRUE, 0);
+ gtk_widget_show (vbox);
+
+ vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), vbox2, FALSE, FALSE, 0);
+ gtk_widget_show (vbox2);
+
+ table = gtk_table_new (1, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (vbox2), table, FALSE, FALSE, 0);
+ gtk_widget_show (table);
+
+ pg.quality = entry = (GtkAdjustment *)
+ gimp_scale_entry_new (GTK_TABLE (table), 0, 0,
+ _("_Quality:"),
+ SCALE_WIDTH, 0, jsvals.quality,
+ 0.0, 100.0, 1.0, 10.0, 0,
+ TRUE, 0.0, 0.0,
+ _("JPEG quality parameter"),
+ "file-jpeg-save-quality");
+
+ g_signal_connect (entry, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &jsvals.quality);
+ g_signal_connect (entry, "value-changed",
+ G_CALLBACK (make_preview),
+ NULL);
+
+ /* custom quantization tables - now used also for original quality */
+ pg.use_orig_quality = toggle =
+ gtk_check_button_new_with_mnemonic (_("_Use quality settings from original "
+ "image"));
+ gtk_box_pack_start (GTK_BOX (vbox2), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ gimp_help_set_help_data (toggle,
+ _("If the original image was loaded from a JPEG "
+ "file using non-standard quality settings "
+ "(quantization tables), enable this option to "
+ "get almost the same quality and file size."),
+ NULL);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &jsvals.use_orig_quality);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ jsvals.use_orig_quality
+ && (orig_quality > 0)
+ && (orig_subsmp == jsvals.subsmp)
+ );
+ gtk_widget_set_sensitive (toggle, (orig_quality > 0));
+
+ /* changing quality disables custom quantization tables, and vice-versa */
+ g_signal_connect (pg.quality, "value-changed",
+ G_CALLBACK (quality_changed),
+ pg.use_orig_quality);
+ g_signal_connect (pg.use_orig_quality, "toggled",
+ G_CALLBACK (use_orig_qual_changed),
+ pg.quality);
+
+ /* File size */
+ vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), vbox2, FALSE, FALSE, 0);
+ gtk_widget_show (vbox2);
+
+ preview_size = gtk_label_new (_("File size: unknown"));
+ gtk_label_set_xalign (GTK_LABEL (preview_size), 0.0);
+ gtk_label_set_ellipsize (GTK_LABEL (preview_size), PANGO_ELLIPSIZE_END);
+ gimp_label_set_attributes (GTK_LABEL (preview_size),
+ PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
+ -1);
+ gtk_box_pack_start (GTK_BOX (vbox2), preview_size, FALSE, FALSE, 0);
+ gtk_widget_show (preview_size);
+
+ gimp_help_set_help_data (preview_size,
+ _("Enable preview to obtain the file size."), NULL);
+
+ pg.preview = toggle =
+ gtk_check_button_new_with_mnemonic (_("Sho_w preview in image window"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), jsvals.preview);
+ gtk_box_pack_start (GTK_BOX (vbox2), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &jsvals.preview);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (make_preview),
+ NULL);
+
+ vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+ gtk_box_pack_start (GTK_BOX (vbox), vbox2, FALSE, FALSE, 0);
+ gtk_widget_show (vbox2);
+
+ /* Save EXIF data */
+ pg.save_exif = toggle =
+ gtk_check_button_new_with_mnemonic (_("Save _Exif data"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), jsvals.save_exif);
+ gtk_box_pack_start (GTK_BOX (vbox2), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &jsvals.save_exif);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (make_preview),
+ NULL);
+
+ /* Save XMP metadata */
+ pg.save_xmp = toggle =
+ gtk_check_button_new_with_mnemonic (_("Save _XMP data"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), jsvals.save_xmp);
+ gtk_box_pack_start (GTK_BOX (vbox2), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &jsvals.save_xmp);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (make_preview),
+ NULL);
+
+ /* Save IPTC metadata */
+ pg.save_iptc = toggle =
+ gtk_check_button_new_with_mnemonic (_("Save _IPTC data"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), jsvals.save_iptc);
+ gtk_box_pack_start (GTK_BOX (vbox2), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &jsvals.save_iptc);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (make_preview),
+ NULL);
+
+ /* Save thumbnail */
+ pg.save_thumbnail = toggle =
+ gtk_check_button_new_with_mnemonic (_("Save _thumbnail"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), jsvals.save_thumbnail);
+ gtk_box_pack_start (GTK_BOX (vbox2), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &jsvals.save_thumbnail);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (make_preview),
+ NULL);
+
+ /* Save color profile */
+ pg.save_profile = toggle =
+ gtk_check_button_new_with_mnemonic (_("Save color _profile"));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), jsvals.save_profile);
+ gtk_box_pack_start (GTK_BOX (vbox2), toggle, FALSE, FALSE, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &jsvals.save_profile);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (make_preview),
+ NULL);
+
+ /* Comment */
+ frame = gimp_frame_new (_("Comment"));
+ gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0);
+ gtk_widget_show (frame);
+
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_SHADOW_IN);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_widget_set_size_request (scrolled_window, 250, 50);
+ gtk_container_add (GTK_CONTAINER (frame), scrolled_window);
+ gtk_widget_show (scrolled_window);
+
+ pg.text_buffer = text_buffer = gtk_text_buffer_new (NULL);
+ if (image_comment)
+ gtk_text_buffer_set_text (text_buffer, image_comment, -1);
+
+ text_view = gtk_text_view_new_with_buffer (text_buffer);
+ gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (text_view), GTK_WRAP_WORD);
+
+ gtk_container_add (GTK_CONTAINER (scrolled_window), text_view);
+ gtk_widget_show (text_view);
+
+ g_object_unref (text_buffer);
+
+ /* Advanced expander */
+ text = g_strdup_printf ("<b>%s</b>", _("_Advanced Options"));
+ expander = gtk_expander_new_with_mnemonic (text);
+ gtk_expander_set_use_markup (GTK_EXPANDER (expander), TRUE);
+ g_free (text);
+
+ gtk_box_pack_start (GTK_BOX (vbox), expander, TRUE, TRUE, 0);
+ gtk_widget_show (expander);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ gtk_container_add (GTK_CONTAINER (expander), vbox);
+ gtk_widget_show (vbox);
+
+ frame = gimp_frame_new ("<expander>");
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ gtk_widget_show (frame);
+
+ table = gtk_table_new (4, 8, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacing (GTK_TABLE (table), 1, 12);
+ gtk_container_add (GTK_CONTAINER (frame), table);
+ gtk_widget_show (table);
+
+ table2 = gtk_table_new (1, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (table2), 6);
+ gtk_table_attach (GTK_TABLE (table), table2,
+ 2, 6, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (table2);
+
+ pg.smoothing = entry = (GtkAdjustment *)
+ gimp_scale_entry_new (GTK_TABLE (table2), 0, 0,
+ _("S_moothing:"),
+ 100, 0, jsvals.smoothing,
+ 0.0, 1.0, 0.01, 0.1, 2,
+ TRUE, 0.0, 0.0,
+ NULL,
+ "file-jpeg-save-smoothing");
+ g_signal_connect (entry, "value-changed",
+ G_CALLBACK (gimp_double_adjustment_update),
+ &jsvals.smoothing);
+ g_signal_connect (entry, "value-changed",
+ G_CALLBACK (make_preview),
+ NULL);
+
+ restart_markers_label = gtk_label_new (_("Interval (MCU rows):"));
+ gtk_label_set_xalign (GTK_LABEL (restart_markers_label), 1.0);
+ gtk_table_attach (GTK_TABLE (table), restart_markers_label, 4, 5, 1, 2,
+ GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
+ gtk_widget_show (restart_markers_label);
+
+ pg.scale_data = (GtkAdjustment *)
+ gtk_adjustment_new (((jsvals.restart == 0) ?
+ DEFAULT_RESTART_MCU_ROWS : jsvals.restart),
+ 1.0, 64.0, 1.0, 1.0, 0);
+ pg.restart = restart_markers_scale = spinbutton =
+ gimp_spin_button_new (pg.scale_data, 1.0, 0);
+ gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
+ gtk_table_attach (GTK_TABLE (table), spinbutton, 5, 6, 1, 2,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (spinbutton);
+
+ pg.use_restart_markers = toggle =
+ gtk_check_button_new_with_mnemonic (_("Use _restart markers"));
+ gtk_table_attach (GTK_TABLE (table), toggle, 2, 4, 1, 2, GTK_FILL, 0, 0, 0);
+ gtk_widget_show (toggle);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), jsvals.restart);
+
+ gtk_widget_set_sensitive (restart_markers_label, jsvals.restart);
+ gtk_widget_set_sensitive (restart_markers_scale, jsvals.restart);
+
+ g_signal_connect (pg.scale_data, "value-changed",
+ G_CALLBACK (save_restart_update),
+ toggle);
+ pg.handler_id_restart = g_signal_connect_swapped (toggle, "toggled",
+ G_CALLBACK (save_restart_update),
+ pg.scale_data);
+
+ row = 0;
+
+ /* Optimize */
+ pg.optimize = toggle = gtk_check_button_new_with_mnemonic (_("_Optimize"));
+ gtk_table_attach (GTK_TABLE (table), toggle, 0, 1,
+ row, row + 1, GTK_FILL, 0, 0, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &jsvals.optimize);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (make_preview),
+ NULL);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), jsvals.optimize);
+
+ if (arithc_supported)
+ gtk_widget_set_sensitive (toggle, !jsvals.arithmetic_coding);
+
+ row++;
+
+ if (arithc_supported)
+ {
+ /* Arithmetic coding */
+ pg.arithmetic_coding = toggle = gtk_check_button_new_with_mnemonic
+ (_("Use arithmetic _coding"));
+ gtk_widget_set_tooltip_text
+ (toggle, _("Older software may have trouble opening "
+ "arithmetic-coded images"));
+ gtk_table_attach (GTK_TABLE (table), toggle, 0, 1,
+ row, row + 1, GTK_FILL, 0, 0, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &jsvals.arithmetic_coding);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (make_preview),
+ NULL);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (toggle_arithmetic_coding),
+ pg.optimize);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ jsvals.arithmetic_coding);
+
+ row++;
+ }
+
+ /* Progressive */
+ pg.progressive = toggle =
+ gtk_check_button_new_with_mnemonic (_("_Progressive"));
+ gtk_table_attach (GTK_TABLE (table), toggle, 0, 1,
+ row, row + 1, GTK_FILL, 0, 0, 0);
+ gtk_widget_show (toggle);
+
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (gimp_toggle_button_update),
+ &jsvals.progressive);
+ g_signal_connect (toggle, "toggled",
+ G_CALLBACK (make_preview),
+ NULL);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
+ jsvals.progressive);
+
+ row++;
+
+ /* Subsampling */
+ label = gtk_label_new_with_mnemonic (_("Su_bsampling:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label, 2, 3, 2, 3,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ pg.subsmp =
+ combo = gimp_int_combo_box_new (_("4:4:4 (best quality)"),
+ JPEG_SUBSAMPLING_1x1_1x1_1x1,
+ _("4:2:2 horizontal (chroma halved)"),
+ JPEG_SUBSAMPLING_2x1_1x1_1x1,
+ _("4:2:2 vertical (chroma halved)"),
+ JPEG_SUBSAMPLING_1x2_1x1_1x1,
+ _("4:2:0 (chroma quartered)"),
+ JPEG_SUBSAMPLING_2x2_1x1_1x1,
+ NULL);
+ gtk_table_attach (GTK_TABLE (table), combo, 3, 6, 2, 3,
+ GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
+ gtk_widget_show (combo);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo);
+
+ if (gimp_drawable_is_rgb (drawable_ID_global))
+ {
+ gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo),
+ jsvals.subsmp,
+ G_CALLBACK (subsampling_changed),
+ entry);
+ g_signal_connect (pg.subsmp, "changed",
+ G_CALLBACK (subsampling_changed2),
+ pg.use_orig_quality);
+ g_signal_connect (pg.use_orig_quality, "toggled",
+ G_CALLBACK (use_orig_qual_changed2),
+ pg.subsmp);
+ }
+ else
+ {
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo),
+ JPEG_SUBSAMPLING_1x1_1x1_1x1);
+
+ gtk_widget_set_sensitive (combo, FALSE);
+ }
+
+
+ /* DCT method */
+ label = gtk_label_new_with_mnemonic (_("_DCT method:"));
+ gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+ gtk_table_attach (GTK_TABLE (table), label, 2, 3, 3, 4,
+ GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_show (label);
+
+ pg.dct = combo = gimp_int_combo_box_new (_("Fast Integer"), 1,
+ _("Integer"), 0,
+ _("Floating-Point"), 2,
+ NULL);
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), jsvals.dct);
+ gtk_table_attach (GTK_TABLE (table), combo, 3, 6, 3, 4,
+ GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
+ gtk_widget_show (combo);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo);
+
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (gimp_int_combo_box_get_active),
+ &jsvals.dct);
+ g_signal_connect (combo, "changed",
+ G_CALLBACK (make_preview),
+ NULL);
+
+ /* Load/Save defaults */
+ tabledefaults = gtk_table_new (1, 3, FALSE);
+ gtk_table_set_col_spacings (GTK_TABLE (tabledefaults), 6);
+ gtk_container_set_border_width (GTK_CONTAINER (tabledefaults), 12);
+ gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
+ tabledefaults, FALSE, FALSE, 0);
+ gtk_widget_show (tabledefaults);
+
+ button = gtk_button_new_with_mnemonic (_("_Load Defaults"));
+ gtk_table_attach (GTK_TABLE (tabledefaults),
+ button, 0, 1, 1, 2, GTK_FILL, 0, 0, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect_swapped (button, "clicked",
+ G_CALLBACK (load_gui_defaults),
+ &pg);
+
+ button = gtk_button_new_with_mnemonic (_("Sa_ve Defaults"));
+ gtk_table_attach (GTK_TABLE (tabledefaults),
+ button, 1, 2, 1, 2, GTK_FILL, 0, 0, 0);
+ gtk_widget_show (button);
+
+ g_signal_connect_swapped (button, "clicked",
+ G_CALLBACK (save_defaults),
+ &pg);
+
+ gtk_widget_show (dialog);
+
+ make_preview ();
+
+ pg.run = FALSE;
+
+ gtk_main ();
+
+ destroy_preview ();
+
+ return pg.run;
+}
+
+static void
+save_dialog_response (GtkWidget *widget,
+ gint response_id,
+ gpointer data)
+{
+ JpegSaveGui *pg = data;
+ GtkTextIter start_iter;
+ GtkTextIter end_iter;
+
+ switch (response_id)
+ {
+ case GTK_RESPONSE_OK:
+ gtk_text_buffer_get_bounds (pg->text_buffer, &start_iter, &end_iter);
+ image_comment = gtk_text_buffer_get_text (pg->text_buffer,
+ &start_iter, &end_iter, FALSE);
+ pg->run = TRUE;
+ /* fallthrough */
+
+ default:
+ gtk_widget_destroy (widget);
+ break;
+ }
+}
+
+void
+load_defaults (void)
+{
+ jsvals.quality = DEFAULT_IJG_QUALITY;
+ jsvals.smoothing = DEFAULT_SMOOTHING;
+ jsvals.optimize = DEFAULT_OPTIMIZE;
+ jsvals.arithmetic_coding= DEFAULT_ARITHMETIC_CODING;
+ jsvals.progressive = DEFAULT_PROGRESSIVE;
+ jsvals.baseline = DEFAULT_BASELINE;
+ jsvals.subsmp = DEFAULT_SUBSMP;
+ jsvals.restart = DEFAULT_RESTART;
+ jsvals.dct = DEFAULT_DCT;
+ jsvals.preview = DEFAULT_PREVIEW;
+ jsvals.save_exif = DEFAULT_EXIF;
+ jsvals.save_xmp = DEFAULT_XMP;
+ jsvals.save_iptc = DEFAULT_IPTC;
+ jsvals.save_thumbnail = DEFAULT_THUMBNAIL;
+ jsvals.save_profile = DEFAULT_PROFILE;
+ jsvals.use_orig_quality = DEFAULT_USE_ORIG_QUALITY;
+}
+
+void
+load_parasite (void)
+{
+ GimpParasite *parasite;
+ gchar *def_str;
+ JpegSaveVals tmpvals;
+ gint num_fields;
+ gint subsampling;
+
+ parasite = gimp_get_parasite (JPEG_DEFAULTS_PARASITE);
+
+ if (! parasite)
+ return;
+
+ def_str = g_strndup (gimp_parasite_data (parasite),
+ gimp_parasite_data_size (parasite));
+
+ gimp_parasite_free (parasite);
+
+ /* Initialize tmpvals in case fewer fields exist in the parasite
+ (e.g., when importing from a previous version of GIMP). */
+ memcpy(&tmpvals, &jsvals, sizeof jsvals);
+
+ num_fields = sscanf (def_str,
+ "%lf %lf %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
+ &tmpvals.quality,
+ &tmpvals.smoothing,
+ &tmpvals.optimize,
+ &tmpvals.progressive,
+ &subsampling,
+ &tmpvals.baseline,
+ &tmpvals.restart,
+ &tmpvals.dct,
+ &tmpvals.preview,
+ &tmpvals.save_exif,
+ &tmpvals.save_thumbnail,
+ &tmpvals.save_xmp,
+ &tmpvals.use_orig_quality,
+ &tmpvals.save_iptc,
+ &tmpvals.arithmetic_coding,
+ &tmpvals.save_profile);
+
+ tmpvals.subsmp = subsampling;
+
+ if (num_fields == 13 || num_fields == 15 || num_fields == 16)
+ {
+ memcpy (&jsvals, &tmpvals, sizeof (tmpvals));
+ }
+
+ g_free (def_str);
+}
+
+static void
+save_defaults (void)
+{
+ GimpParasite *parasite;
+ gchar *def_str;
+
+ def_str = g_strdup_printf ("%lf %lf %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
+ jsvals.quality,
+ jsvals.smoothing,
+ jsvals.optimize,
+ jsvals.progressive,
+ (gint) jsvals.subsmp,
+ jsvals.baseline,
+ jsvals.restart,
+ jsvals.dct,
+ jsvals.preview,
+ jsvals.save_exif,
+ jsvals.save_thumbnail,
+ jsvals.save_xmp,
+ jsvals.use_orig_quality,
+ jsvals.save_iptc,
+ jsvals.arithmetic_coding,
+ jsvals.save_profile);
+ parasite = gimp_parasite_new (JPEG_DEFAULTS_PARASITE,
+ GIMP_PARASITE_PERSISTENT,
+ strlen (def_str), def_str);
+
+ gimp_attach_parasite (parasite);
+
+ gimp_parasite_free (parasite);
+ g_free (def_str);
+}
+
+static void
+load_gui_defaults (JpegSaveGui *pg)
+{
+ GtkAdjustment *restart_markers;
+
+ load_defaults ();
+ load_parasite ();
+
+#define SET_ACTIVE_BTTN(field) \
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (pg->field), jsvals.field)
+
+ SET_ACTIVE_BTTN (optimize);
+ SET_ACTIVE_BTTN (progressive);
+ SET_ACTIVE_BTTN (use_orig_quality);
+ SET_ACTIVE_BTTN (preview);
+ SET_ACTIVE_BTTN (save_exif);
+ SET_ACTIVE_BTTN (save_xmp);
+ SET_ACTIVE_BTTN (save_iptc);
+ SET_ACTIVE_BTTN (save_thumbnail);
+ SET_ACTIVE_BTTN (save_profile);
+
+#undef SET_ACTIVE_BTTN
+
+/*spin button stuff*/
+ g_signal_handler_block (pg->use_restart_markers, pg->handler_id_restart);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (pg->use_restart_markers),
+ jsvals.restart);
+ restart_markers = GTK_ADJUSTMENT (pg->scale_data);
+ gtk_adjustment_set_value (restart_markers, jsvals.restart);
+ g_signal_handler_unblock (pg->use_restart_markers, pg->handler_id_restart);
+
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (pg->smoothing),
+ jsvals.smoothing);
+
+ /* Don't override quality and subsampling setting if we already set it from original */
+ if (!jsvals.use_orig_quality)
+ {
+ gtk_adjustment_set_value (GTK_ADJUSTMENT (pg->quality),
+ jsvals.quality);
+
+ if (gimp_drawable_is_rgb (drawable_ID_global))
+ {
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (pg->subsmp),
+ jsvals.subsmp);
+ }
+ }
+
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (pg->dct),
+ jsvals.dct);
+}
+
+static void
+save_restart_update (GtkAdjustment *adjustment,
+ GtkWidget *toggle)
+{
+ if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (toggle)))
+ jsvals.restart = gtk_adjustment_get_value (adjustment);
+ else
+ jsvals.restart = 0;
+
+ gtk_widget_set_sensitive (restart_markers_label, jsvals.restart);
+ gtk_widget_set_sensitive (restart_markers_scale, jsvals.restart);
+
+ make_preview ();
+}
+
+static void
+subsampling_changed (GtkWidget *combo,
+ GtkAdjustment *entry)
+{
+ gint value;
+
+ gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (combo), &value);
+
+ jsvals.subsmp = value;
+
+ /* smoothing is not supported with nonstandard sampling ratios */
+ gimp_scale_entry_set_sensitive ((gpointer) entry,
+ jsvals.subsmp != JPEG_SUBSAMPLING_2x1_1x1_1x1 &&
+ jsvals.subsmp != JPEG_SUBSAMPLING_1x2_1x1_1x1);
+
+ make_preview ();
+}
+
+static void
+quality_changed (GtkAdjustment *scale_entry,
+ GtkWidget *toggle)
+{
+ if (jsvals.use_orig_quality)
+ {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), FALSE);
+ }
+}
+
+static void
+subsampling_changed2 (GtkWidget *combo,
+ GtkWidget *toggle)
+{
+ if (jsvals.use_orig_quality && orig_subsmp != jsvals.subsmp)
+ {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), FALSE);
+ }
+}
+
+static void
+use_orig_qual_changed (GtkWidget *toggle,
+ GtkAdjustment *scale_entry)
+{
+ if (jsvals.use_orig_quality && orig_quality > 0)
+ {
+ g_signal_handlers_block_by_func (scale_entry, quality_changed, toggle);
+ gtk_adjustment_set_value (scale_entry, orig_quality);
+ g_signal_handlers_unblock_by_func (scale_entry, quality_changed, toggle);
+ }
+}
+
+static void
+use_orig_qual_changed2 (GtkWidget *toggle,
+ GtkWidget *combo)
+{
+ /* the test is (orig_quality > 0), not (orig_subsmp > 0) - this is normal */
+ if (jsvals.use_orig_quality && orig_quality > 0)
+ {
+ gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), orig_subsmp);
+ }
+}
diff --git a/plug-ins/file-jpeg/jpeg-save.h b/plug-ins/file-jpeg/jpeg-save.h
new file mode 100644
index 0000000..34f0eb5
--- /dev/null
+++ b/plug-ins/file-jpeg/jpeg-save.h
@@ -0,0 +1,57 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __JPEG_SAVE_H__
+#define __JPEG_SAVE_H__
+
+typedef struct
+{
+ gdouble quality;
+ gdouble smoothing;
+ gboolean optimize;
+ gboolean arithmetic_coding;
+ gboolean progressive;
+ gboolean baseline;
+ JpegSubsampling subsmp;
+ gint restart;
+ gint dct;
+ gboolean preview;
+ gboolean save_exif;
+ gboolean save_xmp;
+ gboolean save_iptc;
+ gboolean save_thumbnail;
+ gboolean save_profile;
+ gboolean use_orig_quality;
+} JpegSaveVals;
+
+extern JpegSaveVals jsvals;
+
+extern gint32 orig_image_ID_global;
+extern gint32 drawable_ID_global;
+
+
+gboolean save_image (const gchar *filename,
+ gint32 image_ID,
+ gint32 drawable_ID,
+ gint32 orig_image_ID,
+ gboolean preview,
+ GError **error);
+gboolean save_dialog (void);
+void load_defaults (void);
+void load_parasite (void);
+
+#endif /* __JPEG_SAVE_H__ */
diff --git a/plug-ins/file-jpeg/jpeg-settings.c b/plug-ins/file-jpeg/jpeg-settings.c
new file mode 100644
index 0000000..df6ace2
--- /dev/null
+++ b/plug-ins/file-jpeg/jpeg-settings.c
@@ -0,0 +1,397 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * jpeg-settings.c
+ * Copyright (C) 2007 Raphaël Quinet <raphael@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Structure of the "jpeg-settings" parasite:
+ * 1 byte - JPEG color space (JCS_YCbCr, JCS_GRAYSCALE, JCS_CMYK, ...)
+ * 1 byte - quality (1..100 according to the IJG scale, or 0)
+ * 1 byte - number of components (0..4)
+ * 1 byte - number of quantization tables (0..4)
+ * C * 2 bytes - sampling factors for each component (1..4)
+ * T * 128 bytes - quantization tables (only if different from IJG tables)
+ *
+ * Additional data following the quantization tables is currently
+ * ignored and can be used for future extensions.
+ *
+ * In order to improve the compatibility with future versions of the
+ * plug-in that may support more subsampling types ("subsmp"), the
+ * parasite contains the original subsampling for each component
+ * instead of saving only one byte containing the subsampling type as
+ * used by the jpeg plug-in. The same applies to the other settings:
+ * for example, up to 4 quantization tables will be saved in the
+ * parasite even if the current code cannot restore more than 2 of
+ * them (4 tables may be needed by unusual JPEG color spaces such as
+ * JCS_CMYK or JCS_YCCK).
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <setjmp.h>
+
+#include <glib/gstdio.h>
+
+#include <jpeglib.h>
+
+#include <libgimp/gimp.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include "jpeg.h"
+#include "jpeg-quality.h"
+#include "jpeg-settings.h"
+
+/**
+ * jpeg_detect_original_settings:
+ * @cinfo: a pointer to a JPEG decompressor info.
+ * @image_ID: the image to which the parasite should be attached.
+ *
+ * Analyze the image being decompressed (@cinfo) and extract the
+ * sampling factors, quantization tables and overall image quality.
+ * Store this information in a parasite and attach it to @image_ID.
+ *
+ * This function must be called after jpeg_read_header() so that
+ * @cinfo contains the quantization tables and the sampling factors
+ * for each component.
+ *
+ * Return Value: TRUE if a parasite has been attached to @image_ID.
+ */
+gboolean
+jpeg_detect_original_settings (struct jpeg_decompress_struct *cinfo,
+ gint32 image_ID)
+{
+ guint parasite_size;
+ guchar *parasite_data;
+ GimpParasite *parasite;
+ guchar *dest;
+ gint quality;
+ gint num_quant_tables = 0;
+ gint t;
+ gint i;
+
+ g_return_val_if_fail (cinfo != NULL, FALSE);
+ if (cinfo->jpeg_color_space == JCS_UNKNOWN
+ || cinfo->out_color_space == JCS_UNKNOWN)
+ return FALSE;
+
+ quality = jpeg_detect_quality (cinfo);
+ /* no need to attach quantization tables if they are the ones from IJG */
+ if (quality <= 0)
+ {
+ for (t = 0; t < 4; t++)
+ if (cinfo->quant_tbl_ptrs[t])
+ num_quant_tables++;
+ }
+
+ parasite_size = 4 + cinfo->num_components * 2 + num_quant_tables * 128;
+ parasite_data = g_new (guchar, parasite_size);
+ dest = parasite_data;
+
+ *dest++ = CLAMP0255 (cinfo->jpeg_color_space);
+ *dest++ = ABS (quality);
+ *dest++ = CLAMP0255 (cinfo->num_components);
+ *dest++ = num_quant_tables;
+
+ for (i = 0; i < cinfo->num_components; i++)
+ {
+ *dest++ = CLAMP0255 (cinfo->comp_info[i].h_samp_factor);
+ *dest++ = CLAMP0255 (cinfo->comp_info[i].v_samp_factor);
+ }
+
+ if (quality <= 0)
+ {
+ for (t = 0; t < 4; t++)
+ if (cinfo->quant_tbl_ptrs[t])
+ for (i = 0; i < DCTSIZE2; i++)
+ {
+ guint16 c = cinfo->quant_tbl_ptrs[t]->quantval[i];
+ *dest++ = c / 256;
+ *dest++ = c & 255;
+ }
+ }
+
+ parasite = gimp_parasite_new ("jpeg-settings",
+ GIMP_PARASITE_PERSISTENT,
+ parasite_size,
+ parasite_data);
+ g_free (parasite_data);
+ gimp_image_attach_parasite (image_ID, parasite);
+ gimp_parasite_free (parasite);
+ return TRUE;
+}
+
+
+/*
+ * TODO: compare the JPEG color space found in the parasite with the
+ * GIMP color space of the drawable to be saved. If one of them is
+ * grayscale and the other isn't, then the quality setting may be used
+ * but the subsampling parameters and quantization tables should be
+ * ignored. The drawable_ID needs to be passed around because the
+ * color space of the drawable may be different from that of the image
+ * (e.g., when saving a mask or channel).
+ */
+
+/**
+ * jpeg_restore_original_settings:
+ * @image_ID: the image that may contain original jpeg settings in a parasite.
+ * @quality: where to store the original jpeg quality.
+ * @subsmp: where to store the original subsampling type.
+ * @num_quant_tables: where to store the number of quantization tables found.
+ *
+ * Retrieve the original JPEG settings (quality, type of subsampling
+ * and number of quantization tables) from the parasite attached to
+ * @image_ID. If the number of quantization tables is greater than
+ * zero, then these tables can be retrieved from the parasite by
+ * calling jpeg_restore_original_tables().
+ *
+ * Return Value: TRUE if a valid parasite was attached to the image
+ */
+gboolean
+jpeg_restore_original_settings (gint32 image_ID,
+ gint *quality,
+ JpegSubsampling *subsmp,
+ gint *num_quant_tables)
+{
+ GimpParasite *parasite;
+ const guchar *src;
+ glong src_size;
+ gint color_space;
+ gint q;
+ gint num_components;
+ gint num_tables;
+ guchar h[3];
+ guchar v[3];
+
+ g_return_val_if_fail (quality != NULL, FALSE);
+ g_return_val_if_fail (subsmp != NULL, FALSE);
+ g_return_val_if_fail (num_quant_tables != NULL, FALSE);
+
+ parasite = gimp_image_get_parasite (image_ID, "jpeg-settings");
+ if (parasite)
+ {
+ src = gimp_parasite_data (parasite);
+ src_size = gimp_parasite_data_size (parasite);
+ if (src_size >= 4)
+ {
+ color_space = *src++;
+ q = *src++;
+ num_components = *src++;
+ num_tables = *src++;
+
+ if (src_size >= (4 + num_components * 2 + num_tables * 128)
+ && q <= 100 && num_tables <= 4)
+ {
+ *quality = q;
+
+ /* the current plug-in can only create grayscale or YCbCr JPEGs */
+ if (color_space == JCS_GRAYSCALE || color_space == JCS_YCbCr)
+ *num_quant_tables = num_tables;
+ else
+ *num_quant_tables = -1;
+
+ /* the current plug-in can only use subsampling for YCbCr (3) */
+ *subsmp = -1;
+ if (num_components == 3)
+ {
+ h[0] = *src++;
+ v[0] = *src++;
+ h[1] = *src++;
+ v[1] = *src++;
+ h[2] = *src++;
+ v[2] = *src++;
+
+ if (h[1] == 1 && v[1] == 1 && h[2] == 1 && v[2] == 1)
+ {
+ if (h[0] == 1 && v[0] == 1)
+ *subsmp = JPEG_SUBSAMPLING_1x1_1x1_1x1;
+ else if (h[0] == 2 && v[0] == 1)
+ *subsmp = JPEG_SUBSAMPLING_2x1_1x1_1x1;
+ else if (h[0] == 1 && v[0] == 2)
+ *subsmp = JPEG_SUBSAMPLING_1x2_1x1_1x1;
+ else if (h[0] == 2 && v[0] == 2)
+ *subsmp = JPEG_SUBSAMPLING_2x2_1x1_1x1;
+ }
+ }
+
+ gimp_parasite_free (parasite);
+ return TRUE;
+ }
+ }
+
+ gimp_parasite_free (parasite);
+ }
+
+ *quality = -1;
+ *subsmp = -1;
+ *num_quant_tables = 0;
+
+ return FALSE;
+}
+
+
+/**
+ * jpeg_restore_original_tables:
+ * @image_ID: the image that may contain original jpeg settings in a parasite.
+ * @num_quant_tables: the number of quantization tables to restore.
+ *
+ * Retrieve the original quantization tables from the parasite
+ * attached to @image_ID. Each table is an array of coefficients that
+ * can be associated with a component of a JPEG image when saving it.
+ *
+ * An array of newly allocated tables is returned if @num_quant_tables
+ * matches the number of tables saved in the parasite. These tables
+ * are returned as arrays of unsigned integers even if they will never
+ * use more than 16 bits (8 bits in most cases) because the IJG JPEG
+ * library expects arrays of unsigned integers. When these tables are
+ * not needed anymore, the caller should free them using g_free(). If
+ * no parasite exists or if it cannot be used, this function returns
+ * NULL.
+ *
+ * Return Value: an array of quantization tables, or NULL.
+ */
+guint **
+jpeg_restore_original_tables (gint32 image_ID,
+ gint num_quant_tables)
+{
+ GimpParasite *parasite;
+ const guchar *src;
+ glong src_size;
+ gint num_components;
+ gint num_tables;
+ guint **quant_tables;
+ gint t;
+ gint i;
+
+ parasite = gimp_image_get_parasite (image_ID, "jpeg-settings");
+ if (parasite)
+ {
+ src_size = gimp_parasite_data_size (parasite);
+ if (src_size >= 4)
+ {
+ src = gimp_parasite_data (parasite);
+ num_components = src[2];
+ num_tables = src[3];
+
+ if (src_size >= (4 + num_components * 2 + num_tables * 128)
+ && num_tables == num_quant_tables)
+ {
+ src += 4 + num_components * 2;
+ quant_tables = g_new (guint *, num_tables);
+
+ for (t = 0; t < num_tables; t++)
+ {
+ quant_tables[t] = g_new (guint, 128);
+ for (i = 0; i < 64; i++)
+ {
+ guint c;
+
+ c = *src++ * 256;
+ c += *src++;
+ quant_tables[t][i] = c;
+ }
+ }
+ gimp_parasite_free (parasite);
+ return quant_tables;
+ }
+ }
+ gimp_parasite_free (parasite);
+ }
+ return NULL;
+}
+
+
+/**
+ * jpeg_swap_original_settings:
+ * @image_ID: the image that may contain original jpeg settings in a parasite.
+ *
+ * Swap the horizontal and vertical axis for the saved subsampling
+ * parameters and quantization tables. This should be done if the
+ * image has been rotated by +90 or -90 degrees or if it has been
+ * mirrored along its diagonal.
+ */
+void
+jpeg_swap_original_settings (gint32 image_ID)
+{
+ GimpParasite *parasite;
+ const guchar *src;
+ glong src_size;
+ gint num_components;
+ gint num_tables;
+ guchar *new_data;
+ guchar *dest;
+ gint t;
+ gint i;
+ gint j;
+
+ parasite = gimp_image_get_parasite (image_ID, "jpeg-settings");
+ if (parasite)
+ {
+ src_size = gimp_parasite_data_size (parasite);
+ if (src_size >= 4)
+ {
+ src = gimp_parasite_data (parasite);
+ num_components = src[2];
+ num_tables = src[3];
+
+ if (src_size >= (4 + num_components * 2 + num_tables * 128))
+ {
+ new_data = g_new (guchar, src_size);
+ dest = new_data;
+ *dest++ = *src++;
+ *dest++ = *src++;
+ *dest++ = *src++;
+ *dest++ = *src++;
+ for (i = 0; i < num_components; i++)
+ {
+ dest[0] = src[1];
+ dest[1] = src[0];
+ dest += 2;
+ src += 2;
+ }
+ for (t = 0; t < num_tables; t++)
+ {
+ for (i = 0; i < 8; i++)
+ {
+ for (j = 0; j < 8; j++)
+ {
+ dest[i * 16 + j * 2] = src[j * 16 + i * 2];
+ dest[i * 16 + j * 2 + 1] = src[j * 16 + i * 2 + 1];
+ }
+ }
+ dest += 128;
+ src += 128;
+ if (src_size > (4 + num_components * 2 + num_tables * 128))
+ {
+ memcpy (dest, src, src_size - (4 + num_components * 2
+ + num_tables * 128));
+ }
+ }
+ gimp_parasite_free (parasite);
+ parasite = gimp_parasite_new ("jpeg-settings",
+ GIMP_PARASITE_PERSISTENT,
+ src_size,
+ new_data);
+ g_free (new_data);
+ gimp_image_attach_parasite (image_ID, parasite);
+ }
+ }
+ gimp_parasite_free (parasite);
+ }
+}
diff --git a/plug-ins/file-jpeg/jpeg-settings.h b/plug-ins/file-jpeg/jpeg-settings.h
new file mode 100644
index 0000000..0399b84
--- /dev/null
+++ b/plug-ins/file-jpeg/jpeg-settings.h
@@ -0,0 +1,37 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * jpeg-settings.h
+ * Copyright (C) 2007 Raphaël Quinet <raphael@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __JPEG_SETTINGS_H__
+#define __JPEG_SETTINGS_H__
+
+gboolean jpeg_detect_original_settings (struct jpeg_decompress_struct *cinfo,
+ gint32 image_ID);
+
+gboolean jpeg_restore_original_settings (gint32 image_ID,
+ gint *quality,
+ JpegSubsampling *subsmp,
+ gint *num_quant_tables);
+
+guint **jpeg_restore_original_tables (gint32 image_ID,
+ gint num_quant_tables);
+
+void jpeg_swap_original_settings (gint32 image_ID);
+
+#endif /* __JPEG_SETTINGS_H__ */
diff --git a/plug-ins/file-jpeg/jpeg.c b/plug-ins/file-jpeg/jpeg.c
new file mode 100644
index 0000000..90f42fb
--- /dev/null
+++ b/plug-ins/file-jpeg/jpeg.c
@@ -0,0 +1,632 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <setjmp.h>
+#include <string.h>
+
+#include <jpeglib.h>
+#include <jerror.h>
+
+#include <libgimp/gimp.h>
+#include <libgimp/gimpui.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include "jpeg.h"
+#include "jpeg-settings.h"
+#include "jpeg-load.h"
+#include "jpeg-save.h"
+
+/* Declare local functions.
+ */
+
+static void query (void);
+static void run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals);
+
+gboolean undo_touched;
+gboolean load_interactive;
+gchar *image_comment;
+gint32 display_ID;
+JpegSaveVals jsvals;
+gint32 orig_image_ID_global;
+gint32 drawable_ID_global;
+gint orig_quality;
+JpegSubsampling orig_subsmp;
+gint num_quant_tables;
+
+
+const GimpPlugInInfo PLUG_IN_INFO =
+{
+ NULL, /* init_proc */
+ NULL, /* quit_proc */
+ query, /* query_proc */
+ run, /* run_proc */
+};
+
+
+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" }
+ };
+
+ static const GimpParamDef thumb_args[] =
+ {
+ { GIMP_PDB_STRING, "filename", "The name of the file to load" },
+ { GIMP_PDB_INT32, "thumb-size", "Preferred thumbnail size" }
+ };
+ static const GimpParamDef thumb_return_vals[] =
+ {
+ { GIMP_PDB_IMAGE, "image", "Thumbnail image" },
+ { GIMP_PDB_INT32, "image-width", "Width of full-sized image" },
+ { GIMP_PDB_INT32, "image-height", "Height of full-sized image" }
+ };
+
+ static const GimpParamDef 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_FLOAT, "quality", "Quality of saved image (0 <= quality <= 1)" },
+ { GIMP_PDB_FLOAT, "smoothing", "Smoothing factor for saved image (0 <= smoothing <= 1)" },
+ { GIMP_PDB_INT32, "optimize", "Use optimized tables during Huffman coding (0/1)" },
+ { GIMP_PDB_INT32, "progressive", "Create progressive JPEG images (0/1)" },
+ { GIMP_PDB_STRING, "comment", "Image comment" },
+ { GIMP_PDB_INT32, "subsmp", "Sub-sampling type { 0, 1, 2, 3 } 0 == 4:2:0 (chroma quartered), 1 == 4:2:2 Horizontal (chroma halved), 2 == 4:4:4 (best quality), 3 == 4:2:2 Vertical (chroma halved)" },
+ { GIMP_PDB_INT32, "baseline", "Force creation of a baseline JPEG (non-baseline JPEGs can't be read by all decoders) (0/1)" },
+ { GIMP_PDB_INT32, "restart", "Interval of restart markers (in MCU rows, 0 = no restart markers)" },
+ { GIMP_PDB_INT32, "dct", "DCT method to use { INTEGER (0), FIXED (1), FLOAT (2) }" }
+ };
+
+ gimp_install_procedure (LOAD_PROC,
+ "loads files in the JPEG file format",
+ "loads files in the JPEG file format",
+ "Spencer Kimball, Peter Mattis & others",
+ "Spencer Kimball & Peter Mattis",
+ "1995-2007",
+ N_("JPEG 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/jpeg");
+ gimp_register_magic_load_handler (LOAD_PROC,
+ "jpg,jpeg,jpe",
+ "",
+ "0,string,\xff\xd8\xff");
+
+ gimp_install_procedure (LOAD_THUMB_PROC,
+ "Loads a thumbnail from a JPEG image",
+ "Loads a thumbnail from a JPEG image (only if it exists)",
+ "Mukund Sivaraman <muks@mukund.org>, Sven Neumann <sven@gimp.org>",
+ "Mukund Sivaraman <muks@mukund.org>, Sven Neumann <sven@gimp.org>",
+ "November 15, 2004",
+ NULL,
+ NULL,
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (thumb_args),
+ G_N_ELEMENTS (thumb_return_vals),
+ thumb_args, thumb_return_vals);
+
+ gimp_register_thumbnail_loader (LOAD_PROC, LOAD_THUMB_PROC);
+
+ gimp_install_procedure (SAVE_PROC,
+ "saves files in the JPEG file format",
+ "saves files in the lossy, widely supported JPEG format",
+ "Spencer Kimball, Peter Mattis & others",
+ "Spencer Kimball & Peter Mattis",
+ "1995-2007",
+ N_("JPEG image"),
+ "RGB*, GRAY*",
+ GIMP_PLUGIN,
+ G_N_ELEMENTS (save_args), 0,
+ save_args, NULL);
+
+ gimp_register_file_handler_mime (SAVE_PROC, "image/jpeg");
+ gimp_register_save_handler (SAVE_PROC, "jpg,jpeg,jpe", "");
+}
+
+static void
+run (const gchar *name,
+ gint nparams,
+ const GimpParam *param,
+ gint *nreturn_vals,
+ GimpParam **return_vals)
+{
+ static GimpParam values[6];
+ GimpRunMode run_mode;
+ GimpPDBStatusType status = GIMP_PDB_SUCCESS;
+ gint32 image_ID;
+ gint32 drawable_ID;
+ GimpParasite *parasite;
+ GError *error = NULL;
+
+ run_mode = param[0].data.d_int32;
+
+ INIT_I18N ();
+ gegl_init (NULL, NULL);
+
+ *nreturn_vals = 1;
+ *return_vals = values;
+ values[0].type = GIMP_PDB_STATUS;
+ values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
+
+ preview_image_ID = -1;
+ preview_layer_ID = -1;
+
+ orig_quality = 0;
+ orig_subsmp = JPEG_SUBSAMPLING_2x2_1x1_1x1;
+ num_quant_tables = 0;
+
+ if (strcmp (name, LOAD_PROC) == 0)
+ {
+ gboolean resolution_loaded = FALSE;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+ load_interactive = TRUE;
+ break;
+ default:
+ load_interactive = FALSE;
+ break;
+ }
+
+ image_ID = load_image (param[1].data.d_string, run_mode, FALSE,
+ &resolution_loaded, &error);
+
+ if (image_ID != -1)
+ {
+ GFile *file = g_file_new_for_path (param[1].data.d_string);
+ GimpMetadata *metadata;
+
+ metadata = gimp_image_metadata_load_prepare (image_ID, "image/jpeg",
+ file, NULL);
+
+ if (metadata)
+ {
+ GimpMetadataLoadFlags flags = GIMP_METADATA_LOAD_ALL;
+
+ if (resolution_loaded)
+ flags &= ~GIMP_METADATA_LOAD_RESOLUTION;
+
+ gimp_image_metadata_load_finish (image_ID, "image/jpeg",
+ metadata, flags,
+ load_interactive);
+
+ g_object_unref (metadata);
+ }
+
+ g_object_unref (file);
+
+ *nreturn_vals = 2;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ else if (strcmp (name, LOAD_THUMB_PROC) == 0)
+ {
+ if (nparams < 2)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ GFile *file = g_file_new_for_path (param[0].data.d_string);
+ gint width = 0;
+ gint height = 0;
+ GimpImageType type = -1;
+
+ image_ID = load_thumbnail_image (file, &width, &height, &type,
+ &error);
+
+ g_object_unref (file);
+
+ if (image_ID != -1)
+ {
+ *nreturn_vals = 6;
+ values[1].type = GIMP_PDB_IMAGE;
+ values[1].data.d_image = image_ID;
+ values[2].type = GIMP_PDB_INT32;
+ values[2].data.d_int32 = width;
+ values[3].type = GIMP_PDB_INT32;
+ values[3].data.d_int32 = height;
+ values[4].type = GIMP_PDB_INT32;
+ values[4].data.d_int32 = type;
+ values[5].type = GIMP_PDB_INT32;
+ values[5].data.d_int32 = 1; /* num_layers */
+ }
+ else
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+ }
+ else if (strcmp (name, SAVE_PROC) == 0)
+ {
+ GimpMetadata *metadata;
+ GimpMetadataSaveFlags metadata_flags;
+ gint32 orig_image_ID;
+ GimpExportReturn export = GIMP_EXPORT_CANCEL;
+
+ image_ID = param[1].data.d_int32;
+ drawable_ID = param[2].data.d_int32;
+
+ orig_image_ID = image_ID;
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ gimp_ui_init (PLUG_IN_BINARY, FALSE);
+
+ export = gimp_export_image (&image_ID, &drawable_ID, "JPEG",
+ GIMP_EXPORT_CAN_HANDLE_RGB |
+ GIMP_EXPORT_CAN_HANDLE_GRAY);
+
+ switch (export)
+ {
+ case GIMP_EXPORT_EXPORT:
+ {
+ gchar *tmp = g_filename_from_utf8 (_("Export Preview"), -1,
+ NULL, NULL, NULL);
+ if (tmp)
+ {
+ gimp_image_set_filename (image_ID, tmp);
+ g_free (tmp);
+ }
+
+ display_ID = -1;
+ }
+ break;
+
+ case GIMP_EXPORT_IGNORE:
+ break;
+
+ case GIMP_EXPORT_CANCEL:
+ values[0].data.d_status = GIMP_PDB_CANCEL;
+ return;
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ /* Initialize with hardcoded defaults */
+ load_defaults ();
+
+ /* Override the defaults with preferences. */
+ metadata = gimp_image_metadata_save_prepare (orig_image_ID,
+ "image/jpeg",
+ &metadata_flags);
+ jsvals.save_exif = (metadata_flags & GIMP_METADATA_SAVE_EXIF) != 0;
+ jsvals.save_xmp = (metadata_flags & GIMP_METADATA_SAVE_XMP) != 0;
+ jsvals.save_iptc = (metadata_flags & GIMP_METADATA_SAVE_IPTC) != 0;
+ jsvals.save_thumbnail = (metadata_flags & GIMP_METADATA_SAVE_THUMBNAIL) != 0;
+ jsvals.save_profile = (metadata_flags & GIMP_METADATA_SAVE_COLOR_PROFILE) != 0;
+
+ parasite = gimp_image_get_parasite (orig_image_ID, "gimp-comment");
+ if (parasite)
+ {
+ image_comment = g_strndup (gimp_parasite_data (parasite),
+ gimp_parasite_data_size (parasite));
+ gimp_parasite_free (parasite);
+ }
+
+ /* Override preferences from JPG export defaults (if saved). */
+ load_parasite ();
+
+ switch (run_mode)
+ {
+ case GIMP_RUN_NONINTERACTIVE:
+ /* Make sure all the arguments are there! */
+ /* pw - added two more progressive and comment */
+ /* sg - added subsampling, preview, baseline, restarts and DCT */
+ if (nparams != 14)
+ {
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ else
+ {
+ /* Once the PDB gets default parameters, remove this hack */
+ if (param[5].data.d_float >= 0.01)
+ {
+ jsvals.quality = 100.0 * param[5].data.d_float;
+ jsvals.smoothing = param[6].data.d_float;
+ jsvals.optimize = param[7].data.d_int32;
+ jsvals.progressive = param[8].data.d_int32;
+ jsvals.baseline = param[11].data.d_int32;
+ jsvals.subsmp = param[10].data.d_int32;
+ jsvals.restart = param[12].data.d_int32;
+ jsvals.dct = param[13].data.d_int32;
+
+ /* free up the default -- wasted some effort earlier */
+ g_free (image_comment);
+ image_comment = g_strdup (param[9].data.d_string);
+ }
+
+ jsvals.preview = FALSE;
+
+ if (jsvals.quality < 0.0 || jsvals.quality > 100.0)
+ status = GIMP_PDB_CALLING_ERROR;
+ else if (jsvals.smoothing < 0.0 || jsvals.smoothing > 1.0)
+ status = GIMP_PDB_CALLING_ERROR;
+ else if (jsvals.subsmp < 0 || jsvals.subsmp > 3)
+ status = GIMP_PDB_CALLING_ERROR;
+ else if (jsvals.dct < 0 || jsvals.dct > 2)
+ status = GIMP_PDB_CALLING_ERROR;
+ }
+ break;
+
+ case GIMP_RUN_INTERACTIVE:
+ case GIMP_RUN_WITH_LAST_VALS:
+ /* restore the values found when loading the file (if available) */
+ jpeg_restore_original_settings (orig_image_ID,
+ &orig_quality,
+ &orig_subsmp,
+ &num_quant_tables);
+
+ /* load up the previously used values (if file was saved once) */
+ parasite = gimp_image_get_parasite (orig_image_ID,
+ "jpeg-save-options");
+ if (parasite)
+ {
+ const JpegSaveVals *save_vals = gimp_parasite_data (parasite);
+
+ jsvals.quality = save_vals->quality;
+ jsvals.smoothing = save_vals->smoothing;
+ jsvals.optimize = save_vals->optimize;
+ jsvals.progressive = save_vals->progressive;
+ jsvals.baseline = save_vals->baseline;
+ jsvals.subsmp = save_vals->subsmp;
+ jsvals.restart = save_vals->restart;
+ jsvals.dct = save_vals->dct;
+ jsvals.preview = save_vals->preview;
+ jsvals.save_exif = save_vals->save_exif;
+ jsvals.save_thumbnail = save_vals->save_thumbnail;
+ jsvals.save_xmp = save_vals->save_xmp;
+ jsvals.save_iptc = save_vals->save_iptc;
+ jsvals.use_orig_quality = save_vals->use_orig_quality;
+
+ gimp_parasite_free (parasite);
+ }
+ else
+ {
+ /* We are called with GIMP_RUN_WITH_LAST_VALS but this image
+ * doesn't have a "jpeg-save-options" parasite. It's better
+ * to prompt the user with a dialog now so that she has control
+ * over the JPEG encoding parameters.
+ */
+ run_mode = GIMP_RUN_INTERACTIVE;
+
+ /* If this image was loaded from a JPEG file and has not been
+ * saved yet, try to use some of the settings from the
+ * original file if they are better than the default values.
+ */
+ if (orig_quality > jsvals.quality)
+ {
+ jsvals.quality = orig_quality;
+ }
+
+ /* Skip changing subsampling to original if we already have best
+ * setting or if original have worst setting */
+ if (!(jsvals.subsmp == JPEG_SUBSAMPLING_1x1_1x1_1x1 ||
+ orig_subsmp == JPEG_SUBSAMPLING_2x2_1x1_1x1))
+ {
+ jsvals.subsmp = orig_subsmp;
+ }
+
+ if (orig_quality == jsvals.quality &&
+ orig_subsmp == jsvals.subsmp)
+ {
+ jsvals.use_orig_quality = TRUE;
+ }
+ }
+ break;
+ }
+
+ if (run_mode == GIMP_RUN_INTERACTIVE)
+ {
+ if (jsvals.preview)
+ {
+ /* we freeze undo saving so that we can avoid sucking up
+ * tile cache with our unneeded preview steps. */
+ gimp_image_undo_freeze (image_ID);
+
+ undo_touched = TRUE;
+ }
+
+ /* prepare for the preview */
+ preview_image_ID = image_ID;
+ orig_image_ID_global = orig_image_ID;
+ drawable_ID_global = drawable_ID;
+
+ /* First acquire information with a dialog */
+ status = (save_dialog () ? GIMP_PDB_SUCCESS : GIMP_PDB_CANCEL);
+
+ if (undo_touched)
+ {
+ /* thaw undo saving and flush the displays to have them
+ * reflect the current shortcuts */
+ gimp_image_undo_thaw (image_ID);
+ gimp_displays_flush ();
+ }
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ if (! save_image (param[3].data.d_string,
+ image_ID, drawable_ID, orig_image_ID, FALSE,
+ &error))
+ {
+ status = GIMP_PDB_EXECUTION_ERROR;
+ }
+ }
+
+ if (export == GIMP_EXPORT_EXPORT)
+ {
+ /* If the image was exported, delete the new display. */
+ /* This also deletes the image.
+ */
+
+ if (display_ID != -1)
+ gimp_display_delete (display_ID);
+ else
+ gimp_image_delete (image_ID);
+ }
+
+ if (status == GIMP_PDB_SUCCESS)
+ {
+ /* pw - now we need to change the defaults to be whatever
+ * was used to save this image. Dump the old parasites
+ * and add new ones.
+ */
+
+ gimp_image_detach_parasite (orig_image_ID, "gimp-comment");
+ if (image_comment && strlen (image_comment))
+ {
+ parasite = gimp_parasite_new ("gimp-comment",
+ GIMP_PARASITE_PERSISTENT,
+ strlen (image_comment) + 1,
+ image_comment);
+ gimp_image_attach_parasite (orig_image_ID, parasite);
+ gimp_parasite_free (parasite);
+ }
+
+ parasite = gimp_parasite_new ("jpeg-save-options",
+ 0, sizeof (jsvals), &jsvals);
+ gimp_image_attach_parasite (orig_image_ID, parasite);
+ gimp_parasite_free (parasite);
+
+ /* write metadata */
+
+ if (metadata)
+ {
+ GFile *file;
+
+ gimp_metadata_set_bits_per_sample (metadata, 8);
+
+ if (jsvals.save_exif)
+ metadata_flags |= GIMP_METADATA_SAVE_EXIF;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_EXIF;
+
+ if (jsvals.save_xmp)
+ metadata_flags |= GIMP_METADATA_SAVE_XMP;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_XMP;
+
+ if (jsvals.save_iptc)
+ metadata_flags |= GIMP_METADATA_SAVE_IPTC;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_IPTC;
+
+ if (jsvals.save_thumbnail)
+ metadata_flags |= GIMP_METADATA_SAVE_THUMBNAIL;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_THUMBNAIL;
+
+ if (jsvals.save_profile)
+ metadata_flags |= GIMP_METADATA_SAVE_COLOR_PROFILE;
+ else
+ metadata_flags &= ~GIMP_METADATA_SAVE_COLOR_PROFILE;
+
+ file = g_file_new_for_path (param[3].data.d_string);
+ gimp_image_metadata_save_finish (orig_image_ID,
+ "image/jpeg",
+ metadata, metadata_flags,
+ file, NULL);
+ g_object_unref (file);
+ }
+ }
+
+ 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;
+}
+
+/*
+ * Here's the routine that will replace the standard error_exit method:
+ */
+
+void
+my_error_exit (j_common_ptr cinfo)
+{
+ /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
+ my_error_ptr myerr = (my_error_ptr) cinfo->err;
+
+ /* Always display the message. */
+ /* We could postpone this until after returning, if we chose. */
+ (*cinfo->err->output_message) (cinfo);
+
+ /* Return control to the setjmp point */
+ longjmp (myerr->setjmp_buffer, 1);
+}
+
+
+void
+my_output_message (j_common_ptr cinfo)
+{
+ gchar buffer[JMSG_LENGTH_MAX + 1];
+
+ (*cinfo->err->format_message)(cinfo, buffer);
+
+ g_message ("%s", buffer);
+}
diff --git a/plug-ins/file-jpeg/jpeg.h b/plug-ins/file-jpeg/jpeg.h
new file mode 100644
index 0000000..0bf9af0
--- /dev/null
+++ b/plug-ins/file-jpeg/jpeg.h
@@ -0,0 +1,73 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef __JPEG_H__
+#define __JPEG_H__
+
+#define LOAD_PROC "file-jpeg-load"
+#define LOAD_THUMB_PROC "file-jpeg-load-thumb"
+#define SAVE_PROC "file-jpeg-save"
+#define PLUG_IN_BINARY "file-jpeg"
+#define PLUG_IN_ROLE "gimp-file-jpeg"
+
+/* headers used in some APPn markers */
+#define JPEG_APP_HEADER_EXIF "Exif\0\0"
+#define JPEG_APP_HEADER_XMP "http://ns.adobe.com/xap/1.0/"
+
+typedef struct my_error_mgr
+{
+ struct jpeg_error_mgr pub; /* "public" fields */
+
+#ifdef __ia64__
+ /* Ugh, the jmp_buf field needs to be 16-byte aligned on ia64 and some
+ * glibc/icc combinations don't guarantee this. So we pad. See bug #138357
+ * for details.
+ */
+ long double dummy;
+#endif
+
+ jmp_buf setjmp_buffer; /* for return to caller */
+} *my_error_ptr;
+
+typedef enum
+{
+ JPEG_SUBSAMPLING_2x2_1x1_1x1 = 0, /* smallest file */
+ JPEG_SUBSAMPLING_2x1_1x1_1x1 = 1, /* 4:2:2 */
+ JPEG_SUBSAMPLING_1x1_1x1_1x1 = 2,
+ JPEG_SUBSAMPLING_1x2_1x1_1x1 = 3
+} JpegSubsampling;
+
+extern gint32 volatile preview_image_ID;
+extern gint32 preview_layer_ID;
+extern gboolean undo_touched;
+extern gboolean load_interactive;
+extern gint32 display_ID;
+extern gchar *image_comment;
+extern gint orig_quality;
+extern JpegSubsampling orig_subsmp;
+extern gint num_quant_tables;
+
+
+void destroy_preview (void);
+
+void my_error_exit (j_common_ptr cinfo);
+void my_emit_message (j_common_ptr cinfo,
+ int msg_level);
+void my_output_message (j_common_ptr cinfo);
+
+
+#endif /* __JPEG_H__ */
diff --git a/plug-ins/file-jpeg/jpegqual.c b/plug-ins/file-jpeg/jpegqual.c
new file mode 100644
index 0000000..9dd5d9e
--- /dev/null
+++ b/plug-ins/file-jpeg/jpegqual.c
@@ -0,0 +1,1082 @@
+/* jpegqual.c - analyze quality settings of existing JPEG files
+ *
+ * Copyright (C) 2007 Raphaël Quinet <raphael@gimp.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * This program analyzes the quantization tables of the JPEG files
+ * given on the command line and displays their quality settings.
+ *
+ * It is useful for developers or maintainers of the JPEG plug-in
+ * because it can be used to validate the formula used in
+ * jpeg_detect_quality(), by comparing the quality reported for
+ * different JPEG files.
+ *
+ * It can also dump quantization tables so that they can be integrated
+ * into this program and recognized later. This can be used to identify
+ * which device or which program saved a JPEG file.
+ */
+
+/*
+ * TODO:
+ * - rename this program!
+ * - update quant_info[].
+ * - reorganize the options: 2 groups for print options and for selection.
+ * - re-add the code to compare different formulas for approx. IJG quality.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <setjmp.h>
+
+#include <glib.h>
+
+#include <jpeglib.h>
+#include <jerror.h>
+
+#include "jpeg-quality.h"
+
+/* command-line options */
+static gboolean option_summary = FALSE;
+static gboolean option_ctable = FALSE;
+static gboolean option_table_2cols = FALSE;
+static gboolean option_unknown = FALSE;
+static gboolean option_ignore_err = FALSE;
+static gchar **filenames = NULL;
+
+static const GOptionEntry option_entries[] =
+{
+ {
+ "ignore-errors", 'i', 0, G_OPTION_ARG_NONE, &option_ignore_err,
+ "Continue processing other files after a JPEG error", NULL
+ },
+ {
+ "summary", 's', 0, G_OPTION_ARG_NONE, &option_summary,
+ "Print summary information and closest IJG quality", NULL
+ },
+ {
+ "tables", 't', 0, G_OPTION_ARG_NONE, &option_table_2cols,
+ "Dump quantization tables", NULL
+ },
+ {
+ "c-tables", 'c', 0, G_OPTION_ARG_NONE, &option_ctable,
+ "Dump quantization tables as C code", NULL
+ },
+ {
+ "ctables", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &option_ctable,
+ NULL, NULL
+ },
+ {
+ "unknown", 'u', 0, G_OPTION_ARG_NONE, &option_unknown,
+ "Only print information about files with unknown tables", NULL
+ },
+ {
+ G_OPTION_REMAINING, 0, 0,
+ G_OPTION_ARG_FILENAME_ARRAY, &filenames,
+ NULL, NULL
+ },
+ { NULL }
+};
+
+/* information about known JPEG quantization tables */
+typedef struct
+{
+ const guint hash; /* hash of luminance/chrominance tables */
+ const gint lum_sum; /* sum of luminance table divisors */
+ const gint chrom_sum; /* sum of chrominance table divisors */
+ const gint subsmp_h; /* horizontal subsampling (1st component) */
+ const gint subsmp_v; /* vertical subsampling (1st component) */
+ const gint num_quant_tables; /* number of tables (< 0 if no grayscale) */
+ const gchar *source_name; /* name of software of device */
+ const gchar *setting_name; /* name of quality setting */
+ const gint ijg_qual; /* closest IJG quality setting */
+} QuantInfo;
+
+static const QuantInfo quant_info[] =
+{
+ { 0x0a82648b, 64, 64, 0, 0, 2, "IJG JPEG Library", "quality 100", 100 },
+ { 0x4d981764, 86, 115, 0, 0, 2, "IJG JPEG Library", "quality 99", 99 },
+ { 0x62b71702, 151, 224, 0, 0, 2, "IJG JPEG Library", "quality 98", 98 },
+ { 0x29e095c5, 221, 333, 0, 0, 2, "IJG JPEG Library", "quality 97", 97 },
+ { 0xb62c754a, 292, 443, 0, 0, 2, "IJG JPEG Library", "quality 96", 96 },
+ { 0x8e55c78a, 369, 558, 0, 0, 2, "IJG JPEG Library", "quality 95", 95 },
+ { 0x0664d770, 441, 668, 0, 0, 2, "IJG JPEG Library", "quality 94", 94 },
+ { 0x59e5c5bc, 518, 779, 0, 0, 2, "IJG JPEG Library", "quality 93", 93 },
+ { 0xd6f26606, 592, 891, 0, 0, 2, "IJG JPEG Library", "quality 92", 92 },
+ { 0x8aa986ad, 667, 999, 0, 0, 2, "IJG JPEG Library", "quality 91", 91 },
+ { 0x17816eb1, 736, 1110, 0, 0, 2, "IJG JPEG Library", "quality 90", 90 },
+ { 0x75de9350, 814, 1223, 0, 0, 2, "IJG JPEG Library", "quality 89", 89 },
+ { 0x88fdf223, 884, 1332, 0, 0, 2, "IJG JPEG Library", "quality 88", 88 },
+ { 0xf40a6a50, 961, 1444, 0, 0, 2, "IJG JPEG Library", "quality 87", 87 },
+ { 0xe9f2c235, 1031, 1555, 0, 0, 2, "IJG JPEG Library", "quality 86", 86 },
+ { 0x82683892, 1109, 1666, 0, 0, 2, "IJG JPEG Library", "quality 85", 85 },
+ { 0xb1aecce8, 1179, 1778, 0, 0, 2, "IJG JPEG Library", "quality 84", 84 },
+ { 0x83375efe, 1251, 1888, 0, 0, 2, "IJG JPEG Library", "quality 83", 83 },
+ { 0x1e99f479, 1326, 2000, 0, 0, 2, "IJG JPEG Library", "quality 82", 82 },
+ { 0x1a02d360, 1398, 2111, 0, 0, 2, "IJG JPEG Library", "quality 81", 81 },
+ { 0x96129a0d, 1477, 2221, 0, 0, 2, "IJG JPEG Library", "quality 80", 80 },
+ { 0x64d4144b, 1552, 2336, 0, 0, 2, "IJG JPEG Library", "quality 79", 79 },
+ { 0x48a344ac, 1620, 2445, 0, 0, 2, "IJG JPEG Library", "quality 78", 78 },
+ { 0x16e820e3, 1692, 2556, 0, 0, 2, "IJG JPEG Library", "quality 77", 77 },
+ { 0x246b2e95, 1773, 2669, 0, 0, 2, "IJG JPEG Library", "quality 76", 76 },
+ { 0x10b035e9, 1858, 2780, 0, 0, 2, "IJG JPEG Library", "quality 75", 75 },
+ { 0xd5c653da, 1915, 2836, 0, 0, 2, "IJG JPEG Library", "quality 74", 74 },
+ { 0xe349618c, 1996, 2949, 0, 0, 2, "IJG JPEG Library", "quality 73", 73 },
+ { 0xb18e3dc3, 2068, 3060, 0, 0, 2, "IJG JPEG Library", "quality 72", 72 },
+ { 0x955d6e24, 2136, 3169, 0, 0, 2, "IJG JPEG Library", "quality 71", 71 },
+ { 0x641ee862, 2211, 3284, 0, 0, 2, "IJG JPEG Library", "quality 70", 70 },
+ { 0xe02eaf0f, 2290, 3394, 0, 0, 2, "IJG JPEG Library", "quality 69", 69 },
+ { 0xdb978df6, 2362, 3505, 0, 0, 2, "IJG JPEG Library", "quality 68", 68 },
+ { 0x76fa2371, 2437, 3617, 0, 0, 2, "IJG JPEG Library", "quality 67", 67 },
+ { 0x4882b587, 2509, 3727, 0, 0, 2, "IJG JPEG Library", "quality 66", 66 },
+ { 0x25556ae1, 2583, 3839, 0, 0, 2, "IJG JPEG Library", "quality 65", 65 },
+ { 0x103ec03a, 2657, 3950, 0, 0, 2, "IJG JPEG Library", "quality 64", 64 },
+ { 0x0627181f, 2727, 4061, 0, 0, 2, "IJG JPEG Library", "quality 63", 63 },
+ { 0x7133904c, 2804, 4173, 0, 0, 2, "IJG JPEG Library", "quality 62", 62 },
+ { 0x8452ef1f, 2874, 4282, 0, 0, 2, "IJG JPEG Library", "quality 61", 61 },
+ { 0xe2b013be, 2952, 4395, 0, 0, 2, "IJG JPEG Library", "quality 60", 60 },
+ { 0x6f87fbc2, 3021, 4506, 0, 0, 2, "IJG JPEG Library", "quality 59", 59 },
+ { 0x233f1c69, 3096, 4614, 0, 0, 2, "IJG JPEG Library", "quality 58", 58 },
+ { 0xa04bbcb3, 3170, 4726, 0, 0, 2, "IJG JPEG Library", "quality 57", 57 },
+ { 0xf3ccaaff, 3247, 4837, 0, 0, 2, "IJG JPEG Library", "quality 56", 56 },
+ { 0x1967dbe9, 3323, 4947, 0, 0, 2, "IJG JPEG Library", "quality 55", 55 },
+ { 0x44050d25, 3396, 5062, 0, 0, 2, "IJG JPEG Library", "quality 54", 54 },
+ { 0xd050ecaa, 3467, 5172, 0, 0, 2, "IJG JPEG Library", "quality 53", 53 },
+ { 0x9e99f8f1, 3541, 5281, 0, 0, 2, "IJG JPEG Library", "quality 52", 52 },
+ { 0xdf2423f4, 3621, 5396, 0, 0, 2, "IJG JPEG Library", "quality 51", 51 },
+ { 0xe0f48a64, 3688, 5505, 0, 0, 2, "IJG JPEG Library", "quality 50", 50 },
+ { 0xe2c4f0d4, 3755, 5614, 0, 0, 2, "IJG JPEG Library", "quality 49", 49 },
+ { 0x234f1bd7, 3835, 5729, 0, 0, 2, "IJG JPEG Library", "quality 48", 48 },
+ { 0xf198281e, 3909, 5838, 0, 0, 2, "IJG JPEG Library", "quality 47", 47 },
+ { 0x7de407a3, 3980, 5948, 0, 0, 2, "IJG JPEG Library", "quality 46", 46 },
+ { 0xb3aa597b, 4092, 6116, 0, 0, 2, "IJG JPEG Library", "quality 45", 45 },
+ { 0x32b48093, 4166, 6226, 0, 0, 2, "IJG JPEG Library", "quality 44", 44 },
+ { 0x9ea9f85f, 4280, 6396, 0, 0, 2, "IJG JPEG Library", "quality 43", 43 },
+ { 0x335d6006, 4393, 6562, 0, 0, 2, "IJG JPEG Library", "quality 42", 42 },
+ { 0xa727ea4a, 4463, 6672, 0, 0, 2, "IJG JPEG Library", "quality 41", 41 },
+ { 0x1889cfc4, 4616, 6897, 0, 0, 2, "IJG JPEG Library", "quality 40", 40 },
+ { 0xb1aa548e, 4719, 7060, 0, 0, 2, "IJG JPEG Library", "quality 39", 39 },
+ { 0x99bebdd3, 4829, 7227, 0, 0, 2, "IJG JPEG Library", "quality 38", 38 },
+ { 0xf728d062, 4976, 7447, 0, 0, 2, "IJG JPEG Library", "quality 37", 37 },
+ { 0xe1ba65b9, 5086, 7616, 0, 0, 2, "IJG JPEG Library", "quality 36", 36 },
+ { 0x2c8ba6a4, 5240, 7841, 0, 0, 2, "IJG JPEG Library", "quality 35", 35 },
+ { 0x03f7963a, 5421, 8114, 0, 0, 2, "IJG JPEG Library", "quality 34", 34 },
+ { 0xa19bed1e, 5571, 8288, 0, 0, 2, "IJG JPEG Library", "quality 33", 33 },
+ { 0x7945d01c, 5756, 8565, 0, 0, 2, "IJG JPEG Library", "quality 32", 32 },
+ { 0xcc36df1a, 5939, 8844, 0, 0, 2, "IJG JPEG Library", "quality 31", 31 },
+ { 0x3eb1b5ca, 6125, 9122, 0, 0, 2, "IJG JPEG Library", "quality 30", 30 },
+ { 0xd7f65293, 6345, 9455, 0, 0, 2, "IJG JPEG Library", "quality 29", 29 },
+ { 0x4c0a8178, 6562, 9787, 0, 0, 2, "IJG JPEG Library", "quality 28", 28 },
+ { 0x8281d1a1, 6823, 10175, 0, 0, 2, "IJG JPEG Library", "quality 27", 27 },
+ { 0x0bbc9f7e, 7084, 10567, 0, 0, 2, "IJG JPEG Library", "quality 26", 26 },
+ { 0xa8ac1cbd, 7376, 11010, 0, 0, 2, "IJG JPEG Library", "quality 25", 25 },
+ { 0x459b99fc, 7668, 11453, 0, 0, 2, "IJG JPEG Library", "quality 24", 24 },
+ { 0xda09c178, 7995, 11954, 0, 0, 2, "IJG JPEG Library", "quality 23", 23 },
+ { 0x1c651f15, 8331, 12511, 0, 0, 2, "IJG JPEG Library", "quality 22", 22 },
+ { 0x59025244, 8680, 13121, 0, 0, 2, "IJG JPEG Library", "quality 21", 21 },
+ { 0xa130f919, 9056, 13790, 0, 0, 2, "IJG JPEG Library", "quality 20", 20 },
+ { 0x109756cf, 9368, 14204, 0, 0, 2, "IJG JPEG Library", "quality 19", 19 },
+ { 0xe929cab5, 9679, 14267, 0, 0, 2, "IJG JPEG Library", "quality 18", 18 },
+ { 0xcddca370, 10027, 14346, 0, 0, 2, "IJG JPEG Library", "quality 17", 17 },
+ { 0xd5fc76c0, 10360, 14429, 0, 0, 2, "IJG JPEG Library", "quality 16", 16 },
+ { 0x533a1a03, 10714, 14526, 0, 0, 2, "IJG JPEG Library", "quality 15", 15 },
+ { 0x0d8adaff, 11081, 14635, 0, 0, 2, "IJG JPEG Library", "quality 14", 14 },
+ { 0x0d2ee95d, 11456, 14754, 0, 0, 2, "IJG JPEG Library", "quality 13", 13 },
+ { 0x3a1d59a0, 11861, 14864, 0, 0, 2, "IJG JPEG Library", "quality 12", 12 },
+ { 0x66555d04, 12240, 14985, 0, 0, 2, "IJG JPEG Library", "quality 11", 11 },
+ { 0x7fa051b1, 12560, 15110, 0, 0, 2, "IJG JPEG Library", "quality 10", 10 },
+ { 0x7b668ca3, 12859, 15245, 0, 0, 2, "IJG JPEG Library", "quality 9", 9 },
+ { 0xb44d7082, 13230, 15369, 0, 0, 2, "IJG JPEG Library", "quality 8", 8 },
+ { 0xe838d325, 13623, 15523, 0, 0, 2, "IJG JPEG Library", "quality 7", 7 },
+ { 0xb6f58977, 14073, 15731, 0, 0, 2, "IJG JPEG Library", "quality 6", 6 },
+ { 0xfd3e9fc4, 14655, 16010, 0, 0, 2, "IJG JPEG Library", "quality 5", 5 },
+ { 0x7782b922, 15277, 16218, 0, 0, 2, "IJG JPEG Library", "quality 4", 4 },
+ { 0x5a03ac45, 15946, 16320, 0, 0, 2, "IJG JPEG Library", "quality 3", 3 },
+ { 0xe0afaa36, 16315, 16320, 0, 0, 2, "IJG JPEG Library", "quality 2", 2 },
+ { 0x6d640b8b, 16320, 16320, 0, 0, 2, "IJG JPEG Library", "quality 1", 1 },
+ { 0x6d640b8b, 16320, 16320, 0, 0, 2, "IJG JPEG Library", "quality 0", 1 },
+ { 0x4b1d5895, 8008, 11954, 0, 0, 2, "IJG JPEG Library", "not baseline 23", -22 },
+ { 0x36c32c2c, 8370, 12511, 0, 0, 2, "IJG JPEG Library", "not baseline 22", -21 },
+ { 0xa971f812, 8774, 13121, 0, 0, 2, "IJG JPEG Library", "not baseline 21", -20 },
+ { 0xa01f5a9b, 9234, 13790, 0, 0, 2, "IJG JPEG Library", "not baseline 20", -19 },
+ { 0x0e45ab9a, 9700, 14459, 0, 0, 2, "IJG JPEG Library", "not baseline 19", -17 },
+ { 0x5e654320, 10209, 15236, 0, 0, 2, "IJG JPEG Library", "not baseline 18", -14 },
+ { 0x5fc0115c, 10843, 16182, 0, 0, 2, "IJG JPEG Library", "not baseline 17", -11 },
+ { 0x5d8b8e7b, 11505, 17183, 0, 0, 2, "IJG JPEG Library", "not baseline 16", -7 },
+ { 0x63f8b8c1, 12279, 18351, 0, 0, 2, "IJG JPEG Library", "not baseline 15", -5 },
+ { 0x675ecd7a, 13166, 19633, 0, 0, 2, "IJG JPEG Library", "not baseline 14", 0 },
+ { 0x7a65d374, 14160, 21129, 0, 0, 2, "IJG JPEG Library", "not baseline 13", 0 },
+ { 0xf5d0af6a, 15344, 22911, 0, 0, 2, "IJG JPEG Library", "not baseline 12", 0 },
+ { 0x0227aaf0, 16748, 24969, 0, 0, 2, "IJG JPEG Library", "not baseline 11", 0 },
+ { 0xffd2d3c8, 18440, 27525, 0, 0, 2, "IJG JPEG Library", "not baseline 10", 0 },
+ { 0x27f48623, 20471, 30529, 0, 0, 2, "IJG JPEG Library", "not baseline 9", 0 },
+ { 0xff1fab81, 23056, 34422, 0, 0, 2, "IJG JPEG Library", "not baseline 8", 0 },
+ { 0xcfeac62b, 26334, 39314, 0, 0, 2, "IJG JPEG Library", "not baseline 7", 0 },
+ { 0x4a8e947e, 30719, 45876, 0, 0, 2, "IJG JPEG Library", "not baseline 6", 0 },
+ { 0xe668af85, 36880, 55050, 0, 0, 2, "IJG JPEG Library", "not baseline 5", 0 },
+ { 0x6d4b1215, 46114, 68840, 0, 0, 2, "IJG JPEG Library", "not baseline 4", 0 },
+ { 0xf2734901, 61445, 91697, 0, 0, 2, "IJG JPEG Library", "not baseline 3", 0 },
+ { 0x9a2a42bc, 92200, 137625, 0, 0, 2, "IJG JPEG Library", "not baseline 2", 0 },
+ { 0x1b178d6d, 184400, 275250, 0, 0, 2, "IJG JPEG Library", "not baseline 1", 0 },
+ { 0x1b178d6d, 184400, 275250, 0, 0, 2, "IJG JPEG Library", "not baseline 0", 0 },
+
+ /* FIXME: the following entries are incomplete and need to be verified */
+
+ { 0x31258383, 319, 665, 2, 1, -2, "ACD ?", "?", -94 },
+ { 0x91d018a3, 436, 996, 2, 1, -2, "ACD ?", "?", -92 },
+ { 0x954ee70e, 664, 1499, 2, 1, -2, "ACD ?", "?", -88 },
+ { 0xe351bb55, 1590, 3556, 2, 2, -2, "ACD ?", "?", -71 },
+ { 0x5a81e2c0, 95, 166, 1, 1, 2, "Adobe Photoshop CS2", "quality 12", -98 },
+ { 0xcd0d41ae, 232, 443, 1, 1, 2, "Adobe Photoshop CS2", "quality 11", -96 },
+ { 0x1b141cb3, 406, 722, 1, 1, 2, "Adobe Photoshop CS2", "quality 10", -93 },
+ { 0xc84c0187, 539, 801, 1, 1, 2, "Adobe Photoshop CS2", "quality 9", -92 },
+ { 0x1e822409, 649, 853, 1, 1, 2, "Adobe Photoshop CS2", "quality 8", -91 },
+ { 0x3104202b, 786, 926, 1, 1, 2, "Adobe Photoshop CS2", "quality 7", -90 },
+ { 0xcd21f666, 717, 782, 2, 2, 2, "Adobe Photoshop CS2", "quality 6", -91 },
+ { 0x1b74e018, 844, 849, 2, 2, 2, "Adobe Photoshop CS2", "quality 5", -90 },
+ { 0xde39ed89, 962, 892, 2, 2, 2, "Adobe Photoshop CS2", "quality 4", -89 },
+ { 0xbdef8414, 1068, 941, 2, 2, 2, "Adobe Photoshop CS2", "quality 3", -89 },
+ { 0xfedf6432, 1281, 998, 2, 2, 2, "Adobe Photoshop CS2", "quality 2", -87 },
+ { 0x5d6afd92, 1484, 1083, 2, 2, 2, "Adobe Photoshop CS2", "quality 1", -86 },
+ { 0x4c7d2f7d, 1582, 1108, 2, 2, 2, "Adobe Photoshop CS2", "quality 0", -85 },
+ { 0x68e798b2, 95, 168, 1, 1, 2, "Adobe Photoshop CS2", "save for web 100", -98 },
+ { 0x9f3456f2, 234, 445, 1, 1, 2, "Adobe Photoshop CS2", "save for web 90", -96 },
+ { 0xda807dd5, 406, 724, 1, 1, 2, "Adobe Photoshop CS2", "save for web 80", -93 },
+ { 0xf70a37ce, 646, 1149, 1, 1, 2, "Adobe Photoshop CS2", "save for web 70", -90 },
+ { 0xf36979d2, 974, 1769, 1, 1, 2, "Adobe Photoshop CS2", "save for web 60", -85 },
+ { 0x4966f484, 1221, 1348, 2, 2, 2, "Adobe Photoshop CS2", "save for web 50", -86 },
+ { 0xaddf6d45, 1821, 1997, 2, 2, 2, "Adobe Photoshop CS2", "save for web 40", -79 },
+ { 0xeffa362a, 2223, 2464, 2, 2, 2, "Adobe Photoshop CS2", "save for web 30", -74 },
+ { 0x7aa980c1, 2575, 2903, 2, 2, 2, "Adobe Photoshop CS2", "save for web 20", -70 },
+ { 0x489e344f, 3514, 3738, 2, 2, 2, "Adobe Photoshop CS2", "save for web 10", -60 },
+ { 0x1a2cffe0, 535, 750, 1, 1, 2, "Adobe Photoshop 7.0", "quality 10", -93 },
+ { 0x1e96d5d3, 109, 171, 1, 1, 2, "Adobe Photoshop CS", "quality 12", -98 },
+ { 0x6771042c, 303, 466, 1, 1, 2, "Adobe Photoshop CS, Camera Raw 3", "quality 11", -95 },
+ { 0xd4553f25, 668, 830, 1, 1, 2, "Adobe Photoshop 7.0, CS", "quality 9", -91 },
+ { 0xd3b24cb4, 794, 895, 1, 1, 2, "Adobe Photoshop 7.0, CS", "quality 8", -90 },
+ { 0x4ad5990c, 971, 950, 1, 1, 2, "Adobe Photoshop CS", "quality 7", -89 },
+ { 0x4293dfde, 884, 831, 2, 2, 2, "Adobe Photoshop CS", "quality 6", -90 },
+ { 0xba0212ec, 1032, 889, 2, 2, 2, "Adobe Photoshop CS", "quality 5", -89 },
+ { 0x4b50947d, 1126, 940, 2, 2, 2, "Adobe Photoshop CS", "quality 4", -88 },
+ { 0xad0f8e5c, 1216, 977, 2, 2, 2, "Adobe Photoshop CS", "quality 3", -88 },
+ { 0x560b5f0c, 339, 670, 1, 1, 2, "Adobe Photoshop ?", "save for web 85", -94 },
+ { 0x9539b14b, 427, 613, 2, 2, 2, "Adobe Photoshop ?", "?", -94 },
+ { 0x841f2655, 525, 941, 1, 1, 2, "Adobe Photoshop ?", "save for web 75", -92 },
+ { 0xaa2161e2, 803, 1428, 1, 1, 2, "Adobe Photoshop ?", "save for web 65", -87 },
+ { 0x743feb84, 1085, 1996, 1, 1, 2, "Adobe Photoshop ?", "save for web 55", -83 },
+ { 0xe9f14743, 1156, 2116, 1, 1, 2, "Adobe Photoshop ?", "save for web 52", -82 },
+ { 0x1003c8fb, 1175, 2169, 1, 1, 2, "Adobe Photoshop ?", "save for web 51", -81 },
+ { 0xd7804c45, 2272, 2522, 2, 2, 2, "Adobe Photoshop ?", "save for web 29", -73 },
+ { 0xcb5aa8ad, 2515, 2831, 2, 2, 2, "Adobe ImageReady", "save for web 22", -70 },
+ { 0x956d2a00, 3822, 3975, 2, 2, 2, "Adobe ImageReady", "save for web 6", -57 },
+ { 0xba53e0c5, 4028, 4174, 2, 2, 2, "Adobe Photoshop ?", "save for web 3", -55 },
+ { 0x13c0c8bc, 513, 0, 1, 1, 1, "Adobe Photoshop ?", "?", -93 },
+ { 0x3fad5c43, 255, 393, 2, 1, 2, "Apple Quicktime 7.1 or 7.2", "?", -96 },
+ { 0x6529bd03, 513, 775, 2, 2, 2, "Apple Quicktime 7.2", "?", -93 },
+ { 0x354e610a, 543, 784, 2, 1, -2, "Apple Quicktime 7.1", "?", -92 },
+ { 0xd596795e, 361, 506, 2, 1, 2, "Apple ?", "?", -95 },
+ { 0x74da8ba7, 1511, 2229, 2, 2, 2, "Apple ?", "?", -79 },
+ { 0x6391ca2b, 188, 276, 2, 1, -2, "Canon EOS 300D, 350D or 400D", "Fine", -97 },
+ { 0x00474eb0, 708, 1057, 2, 1, -2, "Canon EOS 10D", "Normal", -90 },
+ { 0x535174bd, 533, 1325, 2, 1, -2, "Canon Digital Ixus v2", "Fine", -92 },
+ { 0x535174bd, 533, 1325, 2, 1, -2, "Canon PowerShot A95, S1, S2, SD400 or SD630", "Fine", -89 },
+ { 0xb7be6b97, 192, 556, 2, 1, -2, "Canon PowerShot S5 IS, A300, A430, S200, SD500, SD700, Ixus 700 or 800", "Superfine", -95 },
+ { 0xb5b5c61d, 533, 1325, 1, 2, -2, "Canon Digital Ixus 400", "Fine", -89 },
+ { 0xa7a2c471, 288, 443, 2, 1, -3, "FujiFilm MX-2700", "?", -96 },
+ { 0x8db061f0, 389, 560, 2, 1, -3, "FujiFilm FinePix S700", "Fine", -94 },
+ { 0xbb7b97ba, 515, 774, 2, 1, -3, "FujiFilm FinePix 2600 Zoom", "Fine", -93 },
+ { 0x71bcdf92, 167, 240, 2, 2, -3, "HP PhotoSmart C850, C935", "?", -97 },
+ { 0x9542cc81, 1970, 1970, 2, 2, -3, "HP PhotoSmart C812", "?", -78 },
+ { 0xdb7b71d8, 369, 558, 2, 1, -3, "Kodak P880", "?", 95 },
+ { 0x82e461f8, 566, 583, 2, 2, -2, "Kodak V610", "Fine", -93 },
+ { 0x17816eb1, 736, 1110, 2, 2, -2, "Kodak DC240", "?", 90 },
+ { 0x17816eb1, 736, 1110, 1, 1, -2, "Kodak Imaging", "High (high res.)", 90 },
+ { 0x17816eb1, 736, 1110, 2, 1, -2, "Kodak Imaging", "High (medium res.)", 90 },
+ { 0x17816eb1, 736, 1110, 4, 1, -2, "Kodak Imaging", "High (low res.)", 90 },
+ { 0x3841f91b, 736, 0, 1, 1, 1, "Kodak Imaging", "High (grayscale)", 90 },
+ { 0xe0f48a64, 3688, 5505, 1, 1, -2, "Kodak Imaging", "Medium (high res.)", 50 },
+ { 0xe0f48a64, 3688, 5505, 2, 1, -2, "Kodak Imaging", "Medium (medium res.)", 50 },
+ { 0xe0f48a64, 3688, 5505, 4, 1, -2, "Kodak Imaging", "Medium (low res.)", 50 },
+ { 0x9ebccf53, 3688, 0, 1, 1, 1, "Kodak Imaging", "Medium (grayscale)", 50 },
+ { 0xa130f919, 9056, 13790, 1, 1, -2, "Kodak Imaging", "Low (high res.)", 20 },
+ { 0xa130f919, 9056, 13790, 2, 1, -2, "Kodak Imaging", "Low (medium res.)", 20 },
+ { 0xa130f919, 9056, 13790, 4, 1, -2, "Kodak Imaging", "Low (low res.)", 20 },
+ { 0x34216b8b, 9056, 0, 1, 1, 1, "Kodak Imaging", "Low (grayscale)", 20 },
+ { 0x403b528f, 161, 179, 1, 1, -2, "Lead ?", "?", -98 },
+ { 0x8550a881, 711, 1055, 1, 1, -2, "Lead ?", "?", -90 },
+ { 0x98fb09fc, 1079, 1610, 1, 1, -2, "Lead ?", "?", -85 },
+ { 0xfbb88fb8, 2031, 3054, 1, 1, -2, "Lead ?", "?", -72 },
+ { 0x5fa57f78, 4835, 7226, 1, 1, -2, "Lead ?", "?", -37 },
+ { 0x85b97881, 8199, 12287, 1, 1, -2, "Lead ?", "?", -22 },
+ { 0xd3cd4ad0, 96, 117, 2, 1, -2, "Leica Digilux 3", "?", -98 },
+ { 0x29e095c5, 221, 333, 2, 1, -2, "Leica M8", "?", 97 },
+ { 0xee344795, 582, 836, 2, 1, -2, "Medion ?", "?", -92 },
+ { 0x991408d7, 433, 667, 2, 1, -2, "Medion ?", "?", -94 },
+ { 0x10b035e9, 1858, 2780, 2, 2, 2, "Microsoft Office", "Default", 75 },
+ { 0x20fcfcb8, 116, 169, 2, 1, -2, "Nikon D50, D70, D70s, D80", "Fine", -98 },
+ { 0x2530fec2, 218, 333, 2, 1, -2, "Nikon D70 or D70s", "Normal", -97 },
+ { 0xe5dbee70, 616, 941, 2, 1, -2, "Nikon D70 or D70s", "Basic", -91 },
+ { 0x0e082d61, 671, 999, 2, 1, -2, "Nikon D70 or D70s", "Basic + raw", -90 },
+ { 0xcc6c9703, 127, 169, 2, 1, -2, "Nikon D70 v1.0", "Fine", -98 },
+ { 0x8cdfa365, 302, 444, 2, 1, -2, "Nikon D70 v1.0", "Fine", -95 },
+ { 0x23246639, 315, 499, 2, 1, -2, "Nikon D70 v1.0", "Fine", -95 },
+ { 0x978378a8, 329, 500, 2, 1, -2, "Nikon D70 v1.0", "Fine", -95 },
+ { 0x748a8379, 346, 500, 2, 1, -2, "Nikon D70 v1.0", "Fine", -95 },
+ { 0xa85255cd, 372, 558, 2, 1, -2, "Nikon D70 v1.0", "Fine", -94 },
+ { 0x016406e0, 389, 560, 2, 1, -2, "Nikon D70 v1.0", "Fine", -94 },
+ { 0xda3a50f1, 419, 611, 2, 1, -2, "Nikon D70 v1.0", "Fine", -94 },
+ { 0xd8e45108, 449, 668, 2, 1, -2, "Nikon D70 v1.0", "Fine", -93 },
+ { 0x8a62bf3c, 506, 775, 2, 1, -2, "Nikon D70 v1.0", "Fine", -93 },
+ { 0xc3108c99, 529, 781, 2, 1, -2, "Nikon D70 v1.0", "Fine", -92 },
+ { 0xeabc51a5, 261, 389, 2, 1, -2, "Nikon D50", "Normal", -96 },
+ { 0x0cddf617, 345, 499, 2, 1, -2, "Nikon D50", "Normal", -95 },
+ { 0x2b3b6401, 855, 1279, 2, 1, -2, "Nikon D40", "?", -88 },
+ { 0x5d1ca944, 667, 999, 2, 1, -3, "Nikon E4300", "Normal", 91 },
+ { 0xabcbdc47, 736, 1110, 2, 1, -3, "Nikon E4300", "Normal", 90 },
+ { 0x10b2ad77, 884, 1332, 2, 1, -3, "Nikon E4300", "Normal", 88 },
+ { 0x0a82648b, 64, 64, 1, 1, -2, "Nikon Browser 6", "High quality", 100 },
+ { 0xb091eaf2, 779, 1164, 1, 1, -2, "Nikon Browser 6 or PictureProject 1.6", "Standard quality", -89 },
+ { 0x1a856066, 1697, 2554, 2, 1, -2, "Nikon Browser 6", "Standard eq", -76 },
+ { 0xdf0774bd, 2746, 5112, 2, 2, -2, "Nikon Browser 6", "Standard compression", -57 },
+ { 0xe2fd6fb9, 8024, 12006, 2, 2, -2, "Nikon Browser 6", "Maximum compression", -22 },
+ { 0x17816eb1, 736, 1110, 2, 2, -2, "Olympus Camedia Master", "High quality?", 90 },
+ { 0x96129a0d, 1477, 2221, 2, 2, -2, "Olympus u710,S710", "Super high quality?", 80 },
+ { 0x824f84b9, 437, 617, 2, 1, -2, "Olympus u30D,S410D,u410D", "High quality", -94 },
+ { 0x1b050d58, 447, 670, 2, 1, -2, "Olympus u30D,S410D,u410D", "High quality", -93 },
+ { 0x1b050d58, 447, 670, 2, 1, -2, "Olympus u30D,S410D,u410D", "High quality", -93 },
+ { 0x68058c37, 814, 1223, 2, 1, -3, "Olympus C960Z,D460Z", "Standard quality", 89 },
+ { 0x10b2ad77, 884, 1332, 2, 1, -3, "Olympus C211Z", "Standard quality", 88 },
+ { 0x0f5fa4cb, 1552, 2336, 2, 1, -3, "Olympus C990Z,D490Z", "High quality", 79 },
+ { 0xf51554a8, 261, 392, 2, 1, -2, "Panasonic DMC-FZ5", "High", -96 },
+ { 0xf01efe6e, 251, 392, 2, 1, -2, "Panasonic DMC-FZ30", "High", -96 },
+ { 0x08064360, 280, 445, 2, 1, -2, "Panasonic DMC-FZ30", "High", -96 },
+ { 0x05831bbb, 304, 448, 2, 1, -2, "Panasonic DMC-FZ30", "High", -95 },
+ { 0xe6c08bea, 316, 499, 2, 1, -2, "Panasonic DMC-FZ30", "High", -95 },
+ { 0xcb5f5f7d, 332, 550, 2, 1, -2, "Panasonic DMC-FZ30", "High", -95 },
+ { 0xb53cf359, 355, 555, 2, 1, -2, "Panasonic DMC-FZ30", "High", -95 },
+ { 0xdbcd2690, 375, 606, 2, 1, -2, "Panasonic DMC-FZ30", "High", -94 },
+ { 0x594a3212, 400, 615, 2, 1, -2, "Panasonic DMC-FZ30", "High", -94 },
+ { 0xde23f16a, 420, 667, 2, 1, -2, "Panasonic DMC-FZ30", "High", -94 },
+ { 0xc0a43b37, 501, 775, 2, 1, -2, "Panasonic DMC-FZ30", "High", -93 },
+ { 0xc298e887, 577, 891, 2, 1, -2, "Panasonic DMC-FZ30", "High", -92 },
+ { 0x039b6bc2, 324, 499, 2, 1, 2, "Ricoh Caplio R1V", "?", -95 },
+ { 0xf60dc348, 274, 443, 2, 1, 2, "Roxio PhotoSuite", "?", -96 },
+ { 0xc6f47fa4, 634, 943, 2, 1, -2, "Samsung Digimax V3", "?", -91 },
+ { 0xb9284f39, 1313, 1997, 2, 1, -2, "Samsung Digimax V3", "?", -82 },
+ { 0x5dedca50, 218, 331, 2, 1, -2, "Samsung Digimax S600", "?", -97 },
+ { 0x095451e2, 258, 389, 2, 1, -2, "Sony Cybershot", "?", -96 },
+ { 0x4d981764, 86, 115, 2, 1, -2, "Sony DSC-W55", "Fine", 99 },
+ { 0x6d2b20ce, 122, 169, 2, 1, -2, "Sony DSC-F828, DSC-F88", "?", -98 },
+ { 0x29e095c5, 221, 333, 2, 1, -2, "Sony DSC-W30, DSC-W50, DSC-H2, DSC-H5", "?", 97 },
+ { 0x59e5c5bc, 518, 779, 2, 1, -2, "Sony DSC-W70", "?", 93 },
+ { 0x96129a0d, 1477, 2221, 2, 2, -2, "Sony DSC-W30, DSC-P43, DSC-S600", "?", 80 },
+ { 0xa4d9a6d9, 324, 682, 2, 1, -2, "Sony DSLR-A100", "?", -94 },
+ { 0x17816eb1, 736, 1110, 2, 1, -2, "SonyEricsson K750i", "Fine", 90 },
+ { 0x10b035e9, 1858, 2780, 2, 1, -2, "SonyEricsson K750i or W200i", "Normal", 75 },
+ { 0x1b0ad9d5, 836, 1094, 2, 2, -2, "SonyEricsson K750i", "Panorama fine", -89 },
+ { 0x1cd8bb9f, 1672, 2188, 2, 2, -2, "SonyEricsson K750i", "Panorama normal", -79 },
+ { 0x81d174af, 361, 555, 2, 1, -2, "SonyEricsson K750i", "?", -95 },
+ { 0x991408d7, 433, 667, 2, 1, -2, "SonyEricsson K750i", "?", -94 },
+ { 0x00034978, 954, 1443, 2, 1, -2, "SonyEricsson K750i", "?", -87 },
+ { 0xd27667ab, 1024, 1504, 2, 1, -2, "SonyEricsson K750i", "?", -86 },
+ { 0x94e96153, 1097, 1615, 2, 1, -2, "SonyEricsson K750i", "?", -85 },
+ { 0xf524688a, 1168, 1727, 2, 1, -2, "SonyEricsson K750i", "?", -84 },
+ { 0x5e5e4237, 1324, 2000, 2, 1, -2, "SonyEricsson K750i", "?", -82 },
+ { 0x2e94a836, 1473, 2170, 2, 1, -2, "SonyEricsson K750i", "?", -80 },
+ { 0xdd957ed4, 1615, 2394, 2, 1, -2, "SonyEricsson K750i", "?", -78 },
+ { 0x4147561e, 1759, 2612, 2, 1, -2, "SonyEricsson K750i", "?", -76 },
+ { 0x6f5af2b1, 1491, 1491, 2, 1, -2, "SonyEricsson Z600", "Default", -83 },
+ { 0x641ee862, 2211, 3284, 2, 1, -2, "Trust 760 Powerc@m", "?", 70 },
+ { 0x0bd95282, 2211, 3284, 1, 2, -2, "Trust 760 Powerc@m", "?", 70 },
+ { 0xe9814c86, 1830, 2725, 1, 1, 2, "Xing VT-Compress", "?", -75 },
+};
+
+typedef struct
+{
+ guint32 hashval;
+ gint subsmp_h;
+ gint subsmp_v;
+ gint num_quant_tables;
+ gint ijg_qual;
+ GSList *files;
+ guint16 luminance[DCTSIZE2];
+ guint16 chrominance[DCTSIZE2];
+} QuantTableData;
+
+static GSList *found_tables = NULL;
+
+#if 0 /* FIXME ---v-v-v---------------------------------------------v-v-v--- */
+
+static guint16 **ijg_luminance = NULL; /* luminance, baseline */
+static guint16 **ijg_chrominance = NULL; /* chrominance, baseline */
+static guint16 **ijg_luminance_nb = NULL; /* luminance, not baseline */
+static guint16 **ijg_chrominance_nb = NULL; /* chrominance, not baseline */
+
+/*
+ * Initialize the IJG quantization tables for each quality setting.
+ */
+static void
+init_ijg_tables (void)
+{
+ struct jpeg_compress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ gint q, i;
+
+ ijg_luminance = g_new (guint16 *, 101);
+ ijg_chrominance = g_new (guint16 *, 101);
+ for (q = 0; q <= 100; q++)
+ {
+ ijg_luminance[q] = g_new (guint16, DCTSIZE2);
+ ijg_chrominance[q] = g_new (guint16, DCTSIZE2);
+ }
+
+ cinfo.err = jpeg_std_error (&jerr);
+ jpeg_create_compress (&cinfo);
+
+ for (q = 0; q <= 100; q++)
+ {
+ jpeg_set_quality (&cinfo, q, TRUE);
+ for (i = 0; i < DCTSIZE2; i++)
+ ijg_luminance[q][i] = cinfo.quant_tbl_ptrs[0]->quantval[i];
+ for (i = 0; i < DCTSIZE2; i++)
+ ijg_chrominance[q][i] = cinfo.quant_tbl_ptrs[1]->quantval[i];
+ }
+ for (q = 0; q <= 100; q++)
+ {
+ jpeg_set_quality (&cinfo, q, FALSE);
+ for (i = 0; i < DCTSIZE2; i++)
+ ijg_luminance_nb[q][i] = cinfo.quant_tbl_ptrs[0]->quantval[i];
+ for (i = 0; i < DCTSIZE2; i++)
+ ijg_chrominance_nb[q][i] = cinfo.quant_tbl_ptrs[1]->quantval[i];
+ }
+ jpeg_destroy_compress (&cinfo);
+}
+
+/*
+ * Check if two quantization tables are identical.
+ */
+static gboolean
+compare_tables (const guint16 *quant_table1,
+ const guint16 *quant_table2)
+{
+ gint i;
+
+ g_return_val_if_fail (quant_table1 != NULL, FALSE);
+ g_return_val_if_fail (quant_table2 != NULL, FALSE);
+
+ for (i = 0; i < DCTSIZE2; i++)
+ if (quant_table1[i] != quant_table2[i])
+ return FALSE;
+ return TRUE;
+}
+
+#endif /* FIXME ---^-^-^--------------------------------------------^-^-^--- */
+
+/*
+ * Trivial hash function (simple, but good enough for 1 to 4 * 64 short ints).
+ */
+static guint32
+hash_quant_tables (struct jpeg_decompress_struct *cinfo)
+{
+ guint32 hashval;
+ gint t;
+ gint i;
+
+ hashval = 11;
+ for (t = 0; t < 4; t++)
+ if (cinfo->quant_tbl_ptrs[t])
+ for (i = 0; i < DCTSIZE2; i++)
+ hashval = hashval * 4177 + cinfo->quant_tbl_ptrs[t]->quantval[i];
+ return hashval;
+}
+
+static guint32
+hash_transposed_quant_tables (struct jpeg_decompress_struct *cinfo)
+{
+ guint32 hashval;
+ gint t;
+ gint i;
+ gint j;
+
+ hashval = 11;
+ for (t = 0; t < 4; t++)
+ if (cinfo->quant_tbl_ptrs[t])
+ for (i = 0; i < DCTSIZE; i++)
+ for (j = 0; j < DCTSIZE; j++)
+ hashval = hashval * 4177 + cinfo->quant_tbl_ptrs[t]->quantval[j * 8
+ + i];
+ return hashval;
+}
+
+static void
+add_unknown_table (struct jpeg_decompress_struct *cinfo,
+ gchar *filename)
+{
+ guint32 hashval;
+ GSList *list;
+ QuantTableData *table_data;
+ gint num_quant_tables;
+ gint t;
+ gint i;
+
+ hashval = hash_quant_tables (cinfo);
+
+ /* linear search - the number of unknown tables is usually very small */
+ for (list = found_tables; list; list = list->next)
+ {
+ table_data = list->data;
+ if (table_data->hashval == hashval
+ && table_data->subsmp_h == cinfo->comp_info[0].h_samp_factor
+ && table_data->subsmp_v == cinfo->comp_info[0].v_samp_factor)
+ {
+ /* this unknown table has already been seen in previous files */
+ table_data->files = g_slist_prepend (table_data->files, filename);
+ return;
+ }
+ }
+
+ /* not found => new table */
+ table_data = g_new (QuantTableData, 1);
+ table_data->hashval = hashval;
+ table_data->subsmp_h = cinfo->comp_info[0].h_samp_factor;
+ table_data->subsmp_v = cinfo->comp_info[0].v_samp_factor;
+
+ num_quant_tables = 0;
+ for (t = 0; t < 4; t++)
+ if (cinfo->quant_tbl_ptrs[t])
+ num_quant_tables++;
+
+ table_data->num_quant_tables = num_quant_tables;
+ table_data->ijg_qual = jpeg_detect_quality (cinfo);
+ table_data->files = g_slist_prepend (NULL, filename);
+
+ if (cinfo->quant_tbl_ptrs[0])
+ {
+ for (i = 0; i < DCTSIZE2; i++)
+ table_data->luminance[i] = cinfo->quant_tbl_ptrs[0]->quantval[i];
+ }
+ else
+ {
+ for (i = 0; i < DCTSIZE2; i++)
+ table_data->luminance[i] = 0;
+ }
+
+ if (cinfo->quant_tbl_ptrs[1])
+ {
+ for (i = 0; i < DCTSIZE2; i++)
+ table_data->chrominance[i] = cinfo->quant_tbl_ptrs[1]->quantval[i];
+ }
+ else
+ {
+ for (i = 0; i < DCTSIZE2; i++)
+ table_data->chrominance[i] = 0;
+ }
+
+ found_tables = g_slist_prepend (found_tables, table_data);
+}
+
+/*
+ * Analyze the JPEG quantization tables and return a list of devices or
+ * software that can generate the same tables and subsampling factors.
+ */
+static GSList *
+detect_source (struct jpeg_decompress_struct *cinfo,
+ gint num_quant_tables)
+{
+ guint lum_sum;
+ guint chrom_sum;
+ gint i;
+ GSList *source_list;
+
+ /* compute sum of luminance and chrominance quantization tables */
+ lum_sum = 0;
+ chrom_sum = 0;
+ if (cinfo->quant_tbl_ptrs[0])
+ {
+ for (i = 0; i < DCTSIZE2; i++)
+ lum_sum += cinfo->quant_tbl_ptrs[0]->quantval[i];
+ }
+ if (cinfo->quant_tbl_ptrs[1])
+ {
+ for (i = 0; i < DCTSIZE2; i++)
+ chrom_sum += cinfo->quant_tbl_ptrs[1]->quantval[i];
+ }
+
+ /* there can be more than one match (if sampling factors are compatible) */
+ source_list = NULL;
+ if (chrom_sum == 0 && num_quant_tables == 1)
+ {
+ /* grayscale */
+ for (i = 0; i < G_N_ELEMENTS (quant_info); i++)
+ {
+ if (quant_info[i].lum_sum == lum_sum
+ && (quant_info[i].subsmp_h == 0
+ || quant_info[i].subsmp_h
+ == cinfo->comp_info[0].h_samp_factor)
+ && (quant_info[i].subsmp_v == 0
+ || quant_info[i].subsmp_v
+ == cinfo->comp_info[0].v_samp_factor)
+ && quant_info[i].num_quant_tables > 0)
+ {
+ source_list = g_slist_append (source_list,
+ (gpointer) (quant_info + i));
+ }
+ }
+ }
+ else
+ {
+ /* RGB and other color spaces */
+ for (i = 0; i < G_N_ELEMENTS (quant_info); i++)
+ {
+ if (quant_info[i].lum_sum == lum_sum
+ && quant_info[i].chrom_sum == chrom_sum
+ && (quant_info[i].subsmp_h == 0
+ || quant_info[i].subsmp_h
+ == cinfo->comp_info[0].h_samp_factor)
+ && (quant_info[i].subsmp_v == 0
+ || quant_info[i].subsmp_v
+ == cinfo->comp_info[0].v_samp_factor)
+ && (quant_info[i].num_quant_tables == num_quant_tables
+ || quant_info[i].num_quant_tables == -num_quant_tables))
+ {
+ source_list = g_slist_append (source_list,
+ (gpointer) (quant_info + i));
+ }
+ }
+ }
+
+ return source_list;
+}
+
+/*
+ * ... FIXME: docs
+ */
+static void
+print_summary (struct jpeg_decompress_struct *cinfo,
+ gint num_quant_tables)
+{
+ gint quality;
+ gint i;
+ GSList *source_list;
+
+ /* detect JPEG quality - test the formula used in the jpeg plug-in */
+ quality = jpeg_detect_quality (cinfo);
+ if (quality > 0)
+ g_print ("\tQuality: %02d (exact)\n", quality);
+ else if (quality < 0)
+ g_print ("\tQuality: %02d (approx)\n", -quality);
+ else
+ g_print ("\tQuality: unknown\n");
+
+ /* JPEG sampling factors */
+ g_print ("\tSampling: %dx%d",
+ cinfo->comp_info[0].h_samp_factor,
+ cinfo->comp_info[0].v_samp_factor);
+ if ((cinfo->num_components > 1 && cinfo->num_components != 3)
+ || cinfo->comp_info[1].h_samp_factor != 1
+ || cinfo->comp_info[1].v_samp_factor != 1
+ || cinfo->comp_info[2].h_samp_factor != 1
+ || cinfo->comp_info[2].v_samp_factor != 1)
+ {
+ for (i = 1; i < cinfo->num_components; i++)
+ g_print (",%dx%d",
+ cinfo->comp_info[i].h_samp_factor,
+ cinfo->comp_info[i].v_samp_factor);
+ }
+ g_print ("\n");
+
+ /* Number of quantization tables */
+ g_print ("\tQ.tables: %d\n", num_quant_tables);
+
+ source_list = detect_source (cinfo, num_quant_tables);
+ if (source_list)
+ {
+ GSList *l;
+ guint32 hash;
+ guint32 hash_t;
+
+ hash = hash_quant_tables (cinfo);
+ hash_t = hash_transposed_quant_tables (cinfo);
+
+ for (l = source_list; l; l = l->next)
+ {
+ QuantInfo *source_info = l->data;
+ const gchar *comment = "";
+
+ if (source_info->hash == hash)
+ comment = "";
+ else if (source_info->hash == hash_t)
+ comment = " (rotated)";
+ else if (num_quant_tables == 1)
+ comment = " (grayscale)";
+ else
+ comment = " (FALSE MATCH)";
+
+ g_print ("\tSource: %s - %s%s\n",
+ source_info->source_name,
+ source_info->setting_name,
+ comment);
+ }
+ g_slist_free (source_list);
+ }
+ else
+ g_print ("\tSource: unknown\n");
+}
+
+/*
+ * Print a quantization table as a C array.
+ */
+static void
+print_ctable (gint table_id,
+ const guint16 *quant_table,
+ gboolean more)
+{
+ gint i;
+
+ g_return_if_fail (quant_table != NULL);
+ if (table_id >= 0)
+ g_print (" { /* table %d */\n ", table_id);
+ else
+ g_print (" {\n ");
+ for (i = 0; i < DCTSIZE2; i++)
+ {
+ if (i == DCTSIZE2 - 1)
+ g_print ("%3d\n", quant_table[i]);
+ else if ((i + 1) % DCTSIZE == 0)
+ g_print ("%3d,\n ", quant_table[i]);
+ else
+ g_print ("%3d, ", quant_table[i]);
+ }
+ if (more)
+ g_print (" },\n");
+ else
+ g_print (" }\n");
+}
+
+/*
+ * Print one or two quantization tables, two columns.
+ */
+static void
+print_table_2cols (gint table1_id,
+ const guint16 *quant_table1,
+ gint table2_id,
+ const guint16 *quant_table2)
+{
+ gint i;
+ gint j;
+
+ if (quant_table2)
+ g_print ("\tQuantization table %d: Quantization table %d:\n\t",
+ table1_id, table2_id);
+ else
+ g_print ("\tQuantization table %d:\n\t", table1_id);
+ for (i = 0; i < DCTSIZE; i++)
+ {
+ if (quant_table1)
+ {
+ for (j = 0; j < DCTSIZE; j++)
+ {
+ if (j != DCTSIZE - 1)
+ g_print ("%3d ", quant_table1[i * DCTSIZE + j]);
+ else
+ {
+ if (quant_table2)
+ g_print ("%3d | ", quant_table1[i * DCTSIZE + j]);
+ else if (i != DCTSIZE - 1)
+ g_print ("%3d\n\t", quant_table1[i * DCTSIZE + j]);
+ else
+ g_print ("%3d\n", quant_table1[i * DCTSIZE + j]);
+ }
+ }
+ }
+ else
+ {
+ g_print (" | ");
+ }
+ if (quant_table2)
+ {
+ for (j = 0; j < DCTSIZE; j++)
+ {
+ if (j != DCTSIZE - 1)
+ g_print ("%3d ", quant_table2[i * DCTSIZE + j]);
+ else if (i != DCTSIZE - 1)
+ g_print ("%3d\n\t", quant_table2[i * DCTSIZE + j]);
+ else
+ g_print ("%3d\n", quant_table2[i * DCTSIZE + j]);
+ }
+ }
+ }
+}
+
+/*
+ * Error handling as in the IJG libjpeg example.
+ */
+typedef struct my_error_mgr
+{
+ struct jpeg_error_mgr pub; /* "public" fields */
+#ifdef __ia64__
+ long double dummy; /* bug #138357 */
+#endif
+ jmp_buf setjmp_buffer; /* for return to caller */
+} *my_error_ptr;
+
+static void
+my_error_exit (j_common_ptr cinfo)
+{
+ my_error_ptr myerr = (my_error_ptr) cinfo->err;
+ (*cinfo->err->output_message) (cinfo);
+ longjmp (myerr->setjmp_buffer, 1);
+}
+
+/*
+ * Analyze a JPEG file according to the command-line options.
+ */
+static gboolean
+analyze_file (gchar *filename)
+{
+ struct jpeg_decompress_struct cinfo;
+ struct my_error_mgr jerr;
+ FILE *f;
+ gint i;
+ gint num_quant_tables;
+ GSList *source_list;
+
+ if ((f = fopen (filename, "rb")) == NULL)
+ {
+ g_printerr ("Cannot open '%s'\n", filename);
+ return FALSE;
+ }
+
+ if (option_summary)
+ g_print ("%s:\n", filename);
+
+ cinfo.err = jpeg_std_error (&jerr.pub);
+ jerr.pub.error_exit = my_error_exit;
+ if (setjmp (jerr.setjmp_buffer))
+ {
+ /* if we get here, the JPEG code has signaled an error. */
+ jpeg_destroy_decompress (&cinfo);
+ fclose (f);
+ return FALSE;
+ }
+ jpeg_create_decompress (&cinfo);
+
+ jpeg_stdio_src (&cinfo, f);
+
+ jpeg_read_header (&cinfo, TRUE);
+
+ num_quant_tables = 0;
+ for (i = 0; i < 4; i++)
+ if (cinfo.quant_tbl_ptrs[i])
+ num_quant_tables++;
+
+ source_list = detect_source (&cinfo, num_quant_tables);
+ if (! source_list)
+ {
+ add_unknown_table (&cinfo, filename);
+ }
+
+ if (! option_unknown)
+ {
+ if (option_summary)
+ print_summary (&cinfo, num_quant_tables);
+
+ if (option_ctable)
+ {
+ g_print (" {\n /* %s */\n \"?\", \"?\",\n %d, %d,\n %d,\n",
+ filename,
+ cinfo.comp_info[0].h_samp_factor,
+ cinfo.comp_info[0].v_samp_factor,
+ num_quant_tables);
+ for (i = 0; i < 4; i++)
+ if (cinfo.quant_tbl_ptrs[i])
+ print_ctable (i, cinfo.quant_tbl_ptrs[i]->quantval,
+ (i < 3) && cinfo.quant_tbl_ptrs[i + 1]);
+ g_print (" },\n");
+ }
+
+ if (option_table_2cols)
+ {
+ print_table_2cols (0, cinfo.quant_tbl_ptrs[0]->quantval,
+ 1, cinfo.quant_tbl_ptrs[1]->quantval);
+ if (cinfo.quant_tbl_ptrs[2] || cinfo.quant_tbl_ptrs[3])
+ print_table_2cols (2, cinfo.quant_tbl_ptrs[2]->quantval,
+ 3, cinfo.quant_tbl_ptrs[3]->quantval);
+ }
+ }
+
+ if (source_list)
+ g_slist_free (source_list);
+
+ jpeg_destroy_decompress (&cinfo);
+ fclose (f);
+
+ return TRUE;
+}
+
+/*
+ * ... FIXME: docs
+ */
+static void
+print_unknown_tables (void)
+{
+ GSList *list;
+ GSList *flist;
+ QuantTableData *table_data;
+ gint num_files;
+ gint total_files = 0;
+
+ for (list = found_tables; list; list = list->next)
+ {
+ table_data = list->data;
+
+ if (option_ctable)
+ {
+ g_print (" {\n");
+ num_files = 0;
+ for (flist = table_data->files; flist; flist = flist->next)
+ {
+ g_print(" /* %s */\n", (gchar *)(flist->data));
+ num_files++;
+ }
+
+ { /* FIXME */
+ guint lum_sum;
+ guint chrom_sum;
+ gint i;
+
+ total_files += num_files;
+ lum_sum = 0;
+ chrom_sum = 0;
+ for (i = 0; i < DCTSIZE2; i++)
+ lum_sum += table_data->luminance[i];
+ for (i = 0; i < DCTSIZE2; i++)
+ chrom_sum += table_data->chrominance[i];
+ g_print (" /* hash 0x%x, IJG %d, lum %d, chrom %d, files: %d */\n",
+ table_data->hashval,
+ table_data->ijg_qual,
+ lum_sum, chrom_sum,
+ num_files);
+
+ if (chrom_sum == 0 && table_data->num_quant_tables == 1)
+ {
+ /* grayscale */
+ for (i = 0; i < G_N_ELEMENTS (quant_info); i++)
+ {
+ if (quant_info[i].lum_sum == lum_sum
+ && (quant_info[i].subsmp_h == 0
+ || quant_info[i].subsmp_h
+ == table_data->subsmp_h)
+ && (quant_info[i].subsmp_v == 0
+ || quant_info[i].subsmp_v
+ == table_data->subsmp_v)
+ && quant_info[i].num_quant_tables > 0)
+ {
+ g_print(" XXX \"%s\", \"%s\",\n",
+ quant_info[i].source_name,
+ quant_info[i].setting_name);
+ }
+ }
+ }
+ else
+ {
+ /* RGB and other color spaces */
+ for (i = 0; i < G_N_ELEMENTS (quant_info); i++)
+ {
+ if (quant_info[i].lum_sum == lum_sum
+ && quant_info[i].chrom_sum == chrom_sum
+ && (quant_info[i].subsmp_h == 0
+ || quant_info[i].subsmp_h
+ == table_data->subsmp_h)
+ && (quant_info[i].subsmp_v == 0
+ || quant_info[i].subsmp_v
+ == table_data->subsmp_v)
+ && (quant_info[i].num_quant_tables == table_data->num_quant_tables
+ || quant_info[i].num_quant_tables == -table_data->num_quant_tables))
+ {
+ g_print(" XXX \"%s\", \"%s\",\n",
+ quant_info[i].source_name,
+ quant_info[i].setting_name);
+ }
+ }
+ }
+ } /* FIXME */
+
+ g_print (" \"?\", \"? (hash %x)\",\n"
+ " %d, %d,\n %d,\n",
+ table_data->hashval,
+ table_data->subsmp_h,
+ table_data->subsmp_v,
+ -table_data->num_quant_tables);
+ print_ctable (-1, table_data->luminance, TRUE);
+ print_ctable (-1, table_data->chrominance, FALSE);
+ g_print (" },\n");
+ }
+ }
+ g_print ("/* TOTAL FILES: %d */\n", total_files);
+}
+
+/*
+ * Some compiler told me that it needed a function called main()...
+ */
+int
+main (int argc,
+ char *argv[])
+{
+ GOptionContext *context;
+ GError *error = NULL;
+ gint i;
+
+ g_set_prgname ("jpegqual");
+
+ context =
+ g_option_context_new ("FILE [...] - analyzes JPEG quantization tables");
+ g_option_context_add_main_entries (context, option_entries,
+ NULL /* skip i18n? */);
+
+ if (! g_option_context_parse (context, &argc, &argv, &error))
+ {
+ g_printerr ("%s\n", error->message);
+ g_error_free (error);
+ return EXIT_FAILURE;
+ }
+
+ if (! filenames)
+ {
+ g_printerr ("Missing file name. Try the option --help for help\n");
+ return EXIT_FAILURE;
+ }
+
+ if (!option_summary && !option_ctable && !option_table_2cols)
+ {
+ g_printerr ("Missing output option. Assuming that you wanted --summary.\n");
+ option_summary = TRUE;
+ }
+
+ for (i = 0; filenames[i]; i++)
+ {
+ if (! analyze_file (filenames[i]) && ! option_ignore_err)
+ return EXIT_FAILURE;
+ }
+
+ if (option_unknown && found_tables)
+ {
+ print_unknown_tables ();
+ }
+
+ return EXIT_SUCCESS;
+}