summaryrefslogtreecommitdiffstats
path: root/debian/lcms/lcms-1.19.dfsg2/src
diff options
context:
space:
mode:
Diffstat (limited to 'debian/lcms/lcms-1.19.dfsg2/src')
-rwxr-xr-xdebian/lcms/lcms-1.19.dfsg2/src/Makefile.am31
-rw-r--r--debian/lcms/lcms-1.19.dfsg2/src/Makefile.in551
-rwxr-xr-xdebian/lcms/lcms-1.19.dfsg2/src/cmscam02.c490
-rwxr-xr-xdebian/lcms/lcms-1.19.dfsg2/src/cmscam97.c721
-rwxr-xr-xdebian/lcms/lcms-1.19.dfsg2/src/cmscam97.c.rej21
-rwxr-xr-xdebian/lcms/lcms-1.19.dfsg2/src/cmscgats.c2703
-rwxr-xr-xdebian/lcms/lcms-1.19.dfsg2/src/cmscnvrt.c637
-rwxr-xr-xdebian/lcms/lcms-1.19.dfsg2/src/cmserr.c110
-rwxr-xr-xdebian/lcms/lcms-1.19.dfsg2/src/cmsgamma.c954
-rwxr-xr-xdebian/lcms/lcms-1.19.dfsg2/src/cmsgmt.c1243
-rwxr-xr-xdebian/lcms/lcms-1.19.dfsg2/src/cmsintrp.c1103
-rwxr-xr-xdebian/lcms/lcms-1.19.dfsg2/src/cmsio0.c735
-rwxr-xr-xdebian/lcms/lcms-1.19.dfsg2/src/cmsio1.c3727
-rwxr-xr-xdebian/lcms/lcms-1.19.dfsg2/src/cmslut.c843
-rwxr-xr-xdebian/lcms/lcms-1.19.dfsg2/src/cmsmatsh.c382
-rwxr-xr-xdebian/lcms/lcms-1.19.dfsg2/src/cmsmtrx.c816
-rwxr-xr-xdebian/lcms/lcms-1.19.dfsg2/src/cmsnamed.c171
-rwxr-xr-xdebian/lcms/lcms-1.19.dfsg2/src/cmspack.c2146
-rwxr-xr-xdebian/lcms/lcms-1.19.dfsg2/src/cmspcs.c601
-rwxr-xr-xdebian/lcms/lcms-1.19.dfsg2/src/cmsps2.c1717
-rwxr-xr-xdebian/lcms/lcms-1.19.dfsg2/src/cmssamp.c668
-rwxr-xr-xdebian/lcms/lcms-1.19.dfsg2/src/cmsvirt.c899
-rwxr-xr-xdebian/lcms/lcms-1.19.dfsg2/src/cmswtpnt.c695
-rwxr-xr-xdebian/lcms/lcms-1.19.dfsg2/src/cmsxform.c2018
-rwxr-xr-xdebian/lcms/lcms-1.19.dfsg2/src/lcms.def185
-rwxr-xr-xdebian/lcms/lcms-1.19.dfsg2/src/makefile.simple64
26 files changed, 24231 insertions, 0 deletions
diff --git a/debian/lcms/lcms-1.19.dfsg2/src/Makefile.am b/debian/lcms/lcms-1.19.dfsg2/src/Makefile.am
new file mode 100755
index 00000000..9f6dd579
--- /dev/null
+++ b/debian/lcms/lcms-1.19.dfsg2/src/Makefile.am
@@ -0,0 +1,31 @@
+#
+# Makefile for building LCMS library
+# Initially Written by Bob Friesenhahn, June 2003
+#
+
+# Don't require all the GNU mandated files
+AUTOMAKE_OPTIONS = 1.7 foreign
+
+includedir = ${prefix}/include
+
+# Shared libraries built in this directory
+lib_LTLIBRARIES = liblcms.la
+
+LIBRARY_CURRENT = @LIBRARY_CURRENT@
+LIBRARY_REVISION = @LIBRARY_REVISION@
+LIBRARY_AGE = @LIBRARY_AGE@
+
+INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include
+
+liblcms_la_LDFLAGS = -no-undefined \
+ -version-info $(LIBRARY_CURRENT):$(LIBRARY_REVISION):$(LIBRARY_AGE)
+
+liblcms_la_LIBADD = $(LCMS_LIB_DEPLIBS)
+
+# liblcms_la_CFLAGS = -O3
+
+liblcms_la_SOURCES = \
+ cmscnvrt.c cmserr.c cmsgamma.c cmsgmt.c cmsintrp.c cmsio0.c cmsio1.c cmslut.c \
+ cmsmatsh.c cmsmtrx.c cmspack.c cmspcs.c cmswtpnt.c cmsxform.c \
+ cmssamp.c cmscam97.c cmsnamed.c cmsps2.c cmscam02.c cmsvirt.c cmscgats.c
+
diff --git a/debian/lcms/lcms-1.19.dfsg2/src/Makefile.in b/debian/lcms/lcms-1.19.dfsg2/src/Makefile.in
new file mode 100644
index 00000000..09842bc1
--- /dev/null
+++ b/debian/lcms/lcms-1.19.dfsg2/src/Makefile.in
@@ -0,0 +1,551 @@
+# Makefile.in generated by automake 1.10 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006 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@
+
+#
+# Makefile for building LCMS library
+# Initially Written by Bob Friesenhahn, June 2003
+#
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@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@
+target_triplet = @target@
+subdir = src
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_CLEAN_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
+am__installdirs = "$(DESTDIR)$(libdir)"
+libLTLIBRARIES_INSTALL = $(INSTALL)
+LTLIBRARIES = $(lib_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+liblcms_la_DEPENDENCIES = $(am__DEPENDENCIES_1)
+am_liblcms_la_OBJECTS = cmscnvrt.lo cmserr.lo cmsgamma.lo cmsgmt.lo \
+ cmsintrp.lo cmsio0.lo cmsio1.lo cmslut.lo cmsmatsh.lo \
+ cmsmtrx.lo cmspack.lo cmspcs.lo cmswtpnt.lo cmsxform.lo \
+ cmssamp.lo cmscam97.lo cmsnamed.lo cmsps2.lo cmscam02.lo \
+ cmsvirt.lo cmscgats.lo
+liblcms_la_OBJECTS = $(am_liblcms_la_OBJECTS)
+liblcms_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(liblcms_la_LDFLAGS) $(LDFLAGS) -o $@
+DEFAULT_INCLUDES = -I.@am__isrc@
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+SOURCES = $(liblcms_la_SOURCES)
+DIST_SOURCES = $(liblcms_la_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INT16_T = @INT16_T@
+INT32_T = @INT32_T@
+INT64_T = @INT64_T@
+INT8_T = @INT8_T@
+JPEGICC_DEPLIBS = @JPEGICC_DEPLIBS@
+LCMS_LIB_DEPLIBS = @LCMS_LIB_DEPLIBS@
+LCMS_PYEXECDIR = @LCMS_PYEXECDIR@
+LCMS_PYINCLUDE = @LCMS_PYINCLUDE@
+LCMS_PYLIB = @LCMS_PYLIB@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBRARY_AGE = @LIBRARY_AGE@
+LIBRARY_CURRENT = @LIBRARY_CURRENT@
+LIBRARY_REVISION = @LIBRARY_REVISION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBTOOL_DEPS = @LIBTOOL_DEPS@
+LIB_JPEG = @LIB_JPEG@
+LIB_MATH = @LIB_MATH@
+LIB_TIFF = @LIB_TIFF@
+LIB_ZLIB = @LIB_ZLIB@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+TIFFICC_DEPLIBS = @TIFFICC_DEPLIBS@
+UINT16_T = @UINT16_T@
+UINT32_T = @UINT32_T@
+UINT64_T = @UINT64_T@
+UINT8_T = @UINT8_T@
+VERSION = @VERSION@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+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@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = ${prefix}/include
+infodir = @infodir@
+inline = @inline@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+
+# Don't require all the GNU mandated files
+AUTOMAKE_OPTIONS = 1.7 foreign
+
+# Shared libraries built in this directory
+lib_LTLIBRARIES = liblcms.la
+INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include
+liblcms_la_LDFLAGS = -no-undefined \
+ -version-info $(LIBRARY_CURRENT):$(LIBRARY_REVISION):$(LIBRARY_AGE)
+
+liblcms_la_LIBADD = $(LCMS_LIB_DEPLIBS)
+
+# liblcms_la_CFLAGS = -O3
+liblcms_la_SOURCES = \
+ cmscnvrt.c cmserr.c cmsgamma.c cmsgmt.c cmsintrp.c cmsio0.c cmsio1.c cmslut.c \
+ cmsmatsh.c cmsmtrx.c cmspack.c cmspcs.c cmswtpnt.c cmsxform.c \
+ cmssamp.c cmscam97.c cmsnamed.c cmsps2.c cmscam02.c cmsvirt.c cmscgats.c
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/Makefile
+.PRECIOUS: 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__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(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
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ if test -f $$p; then \
+ f=$(am__strip_dir) \
+ echo " $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(libdir)/$$f"; \
+ else :; fi; \
+ done
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ p=$(am__strip_dir) \
+ echo " $(LIBTOOL) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \
+ $(LIBTOOL) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+liblcms.la: $(liblcms_la_OBJECTS) $(liblcms_la_DEPENDENCIES)
+ $(liblcms_la_LINK) -rpath $(libdir) $(liblcms_la_OBJECTS) $(liblcms_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmscam02.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmscam97.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmscgats.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmscnvrt.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmserr.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmsgamma.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmsgmt.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmsintrp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmsio0.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmsio1.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmslut.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmsmatsh.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmsmtrx.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmsnamed.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmspack.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmspcs.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmsps2.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmssamp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmsvirt.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmswtpnt.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmsxform.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(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 $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$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 $(LTLIBRARIES)
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)"; 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:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_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-libLTLIBRARIES clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-exec-am: install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-info: install-info-am
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-ps: install-ps-am
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -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-libLTLIBRARIES
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libLTLIBRARIES clean-libtool ctags 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-libLTLIBRARIES 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 uninstall uninstall-am uninstall-libLTLIBRARIES
+
+# 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/debian/lcms/lcms-1.19.dfsg2/src/cmscam02.c b/debian/lcms/lcms-1.19.dfsg2/src/cmscam02.c
new file mode 100755
index 00000000..bade0e08
--- /dev/null
+++ b/debian/lcms/lcms-1.19.dfsg2/src/cmscam02.c
@@ -0,0 +1,490 @@
+//
+// Little cms
+// Copyright (C) 1998-2007 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.
+
+
+
+// CIECAM 02 appearance model. Many thanks to Jordi Vilar for the debugging.
+
+#include "lcms.h"
+
+
+LCMSAPI LCMSHANDLE LCMSEXPORT cmsCIECAM02Init(LPcmsViewingConditions pVC);
+LCMSAPI void LCMSEXPORT cmsCIECAM02Done(LCMSHANDLE hModel);
+LCMSAPI void LCMSEXPORT cmsCIECAM02Forward(LCMSHANDLE hModel, LPcmsCIEXYZ pIn, LPcmsJCh pOut);
+LCMSAPI void LCMSEXPORT cmsCIECAM02Reverse(LCMSHANDLE hModel, LPcmsJCh pIn, LPcmsCIEXYZ pOut);
+
+
+// ---------- Implementation --------------------------------------------
+
+typedef struct {
+
+ double XYZ[3];
+ double RGB[3];
+ double RGBc[3];
+ double RGBp[3];
+ double RGBpa[3];
+ double a, b, h, e, H, A, J, Q, s, t, C, M;
+ double abC[2];
+ double abs[2];
+ double abM[2];
+
+} CAM02COLOR, *LPCAM02COLOR;
+
+typedef struct {
+
+ CAM02COLOR adoptedWhite;
+ double LA, Yb;
+ double F, c, Nc;
+ int surround;
+ double n, Nbb, Ncb, z, FL, D;
+
+} cmsCIECAM02, *LPcmsCIECAM02;
+
+
+static
+double compute_n(LPcmsCIECAM02 pMod)
+{
+ return(pMod -> Yb / pMod -> adoptedWhite.XYZ[1]);
+}
+
+static
+double compute_z(LPcmsCIECAM02 pMod)
+{
+ return(1.48 + pow(pMod -> n, 0.5));
+}
+
+static
+double computeNbb(LPcmsCIECAM02 pMod)
+{
+ return(0.725 * pow((1.0 / pMod -> n), 0.2));
+}
+
+static
+double computeFL(LPcmsCIECAM02 pMod)
+{
+ double k, FL;
+
+ k = 1.0 / ((5.0 * pMod->LA) + 1.0);
+ FL = 0.2 * pow(k, 4.0) * (5.0 * pMod->LA) + 0.1 *
+ (pow((1.0 - pow(k, 4.0)), 2.0)) *
+ (pow((5.0 * pMod->LA), (1.0 / 3.0)));
+
+ return FL;
+}
+
+static
+double computeD(LPcmsCIECAM02 pMod)
+{
+ double D;
+
+ D = pMod->F - (1.0/3.6)*(exp(((-pMod ->LA-42) / 92.0)));
+
+ return D;
+}
+
+
+static
+CAM02COLOR XYZtoCAT02(CAM02COLOR clr)
+{
+ clr.RGB[0] = (clr.XYZ[0] * 0.7328) + (clr.XYZ[1] * 0.4296) + (clr.XYZ[2] * -0.1624);
+ clr.RGB[1] = (clr.XYZ[0] * -0.7036) + (clr.XYZ[1] * 1.6975) + (clr.XYZ[2] * 0.0061);
+ clr.RGB[2] = (clr.XYZ[0] * 0.0030) + (clr.XYZ[1] * 0.0136) + (clr.XYZ[2] * 0.9834);
+
+ return clr;
+}
+
+static
+CAM02COLOR ChromaticAdaptation(CAM02COLOR clr, LPcmsCIECAM02 pMod)
+{
+ int i;
+ for (i = 0; i < 3; i++) {
+ clr.RGBc[i] = ((pMod -> adoptedWhite.XYZ[1] *
+ (pMod->D / pMod -> adoptedWhite.RGB[i])) +
+ (1.0 - pMod->D)) * clr.RGB[i];
+ }
+
+ return clr;
+}
+
+
+static
+CAM02COLOR CAT02toHPE (CAM02COLOR clr)
+{
+
+ double M[9];
+
+
+ M[0] =(( 0.38971 * 1.096124) + (0.68898 * 0.454369) + (-0.07868 * -0.009628));
+ M[1] =(( 0.38971 * -0.278869) + (0.68898 * 0.473533) + (-0.07868 * -0.005698));
+ M[2] =(( 0.38971 * 0.182745) + (0.68898 * 0.072098) + (-0.07868 * 1.015326));
+ M[3] =((-0.22981 * 1.096124) + (1.18340 * 0.454369) + ( 0.04641 * -0.009628));
+ M[4] =((-0.22981 * -0.278869) + (1.18340 * 0.473533) + ( 0.04641 * -0.005698));
+ M[5] =((-0.22981 * 0.182745) + (1.18340 * 0.072098) + ( 0.04641 * 1.015326));
+ M[6] =(-0.009628);
+ M[7] =(-0.005698);
+ M[8] =( 1.015326);
+
+ clr.RGBp[0] = (clr.RGBc[0] * M[0]) + (clr.RGBc[1] * M[1]) + (clr.RGBc[2] * M[2]);
+ clr.RGBp[1] = (clr.RGBc[0] * M[3]) + (clr.RGBc[1] * M[4]) + (clr.RGBc[2] * M[5]);
+ clr.RGBp[2] = (clr.RGBc[0] * M[6]) + (clr.RGBc[1] * M[7]) + (clr.RGBc[2] * M[8]);
+
+ return clr;
+}
+
+static
+CAM02COLOR NonlinearCompression(CAM02COLOR clr, LPcmsCIECAM02 pMod)
+{
+ int i;
+ double temp;
+
+ for (i = 0; i < 3; i++) {
+ if (clr.RGBp[i] < 0) {
+
+ temp = pow((-1.0 * pMod->FL * clr.RGBp[i] / 100.0), 0.42);
+ clr.RGBpa[i] = (-1.0 * 400.0 * temp) / (temp + 27.13) + 0.1;
+ }
+ else {
+ temp = pow((pMod->FL * clr.RGBp[i] / 100.0), 0.42);
+ clr.RGBpa[i] = (400.0 * temp) / (temp + 27.13) + 0.1;
+ }
+ }
+
+ clr.A = (((2.0 * clr.RGBpa[0]) + clr.RGBpa[1] +
+ (clr.RGBpa[2] / 20.0)) - 0.305) * pMod->Nbb;
+
+ return clr;
+}
+
+static
+CAM02COLOR ComputeCorrelates(CAM02COLOR clr, LPcmsCIECAM02 pMod)
+{
+ double a, b, temp, e, t, r2d, d2r;
+
+ a = clr.RGBpa[0] - (12.0 * clr.RGBpa[1] / 11.0) + (clr.RGBpa[2] / 11.0);
+ b = (clr.RGBpa[0] + clr.RGBpa[1] - (2.0 * clr.RGBpa[2])) / 9.0;
+
+ r2d = (180.0 / 3.141592654);
+ if (a == 0) {
+ if (b == 0) clr.h = 0;
+ else if (b > 0) clr.h = 90;
+ else clr.h = 270;
+ }
+ else if (a > 0) {
+ temp = b / a;
+ if (b > 0) clr.h = (r2d * atan(temp));
+ else if (b == 0) clr.h = 0;
+ else clr.h = (r2d * atan(temp)) + 360;
+ }
+ else {
+ temp = b / a;
+ clr.h = (r2d * atan(temp)) + 180;
+ }
+
+ d2r = (3.141592654 / 180.0);
+ e = ((12500.0 / 13.0) * pMod->Nc * pMod->Ncb) *
+ (cos((clr.h * d2r + 2.0)) + 3.8);
+
+ if (clr.h < 20.14) {
+ temp = ((clr.h + 122.47)/1.2) + ((20.14 - clr.h)/0.8);
+ clr.H = 300 + (100*((clr.h + 122.47)/1.2)) / temp;
+ }
+ else if (clr.h < 90.0) {
+ temp = ((clr.h - 20.14)/0.8) + ((90.00 - clr.h)/0.7);
+ clr.H = (100*((clr.h - 20.14)/0.8)) / temp;
+ }
+ else if (clr.h < 164.25) {
+ temp = ((clr.h - 90.00)/0.7) + ((164.25 - clr.h)/1.0);
+ clr.H = 100 + ((100*((clr.h - 90.00)/0.7)) / temp);
+ }
+ else if (clr.h < 237.53) {
+ temp = ((clr.h - 164.25)/1.0) + ((237.53 - clr.h)/1.2);
+ clr.H = 200 + ((100*((clr.h - 164.25)/1.0)) / temp);
+ }
+ else {
+ temp = ((clr.h - 237.53)/1.2) + ((360 - clr.h + 20.14)/0.8);
+ clr.H = 300 + ((100*((clr.h - 237.53)/1.2)) / temp);
+ }
+
+ clr.J = 100.0 * pow((clr.A / pMod->adoptedWhite.A),
+ (pMod->c * pMod->z));
+
+ clr.Q = (4.0 / pMod->c) * pow((clr.J / 100.0), 0.5) *
+ (pMod->adoptedWhite.A + 4.0) * pow(pMod->FL, 0.25);
+
+ t = (e * pow(((a * a) + (b * b)), 0.5)) /
+ (clr.RGBpa[0] + clr.RGBpa[1] +
+ ((21.0 / 20.0) * clr.RGBpa[2]));
+
+ clr.C = pow(t, 0.9) * pow((clr.J / 100.0), 0.5) *
+ pow((1.64 - pow(0.29, pMod->n)), 0.73);
+
+ clr.M = clr.C * pow(pMod->FL, 0.25);
+ clr.s = 100.0 * pow((clr.M / clr.Q), 0.5);
+
+ return clr;
+}
+
+
+static
+CAM02COLOR InverseCorrelates(CAM02COLOR clr, LPcmsCIECAM02 pMod)
+{
+
+ double t, e, p1, p2, p3, p4, p5, hr, d2r;
+ d2r = 3.141592654 / 180.0;
+
+ t = pow( (clr.C / (pow((clr.J / 100.0), 0.5) *
+ (pow((1.64 - pow(0.29, pMod->n)), 0.73)))),
+ (1.0 / 0.9) );
+ e = ((12500.0 / 13.0) * pMod->Nc * pMod->Ncb) *
+ (cos((clr.h * d2r + 2.0)) + 3.8);
+
+ clr.A = pMod->adoptedWhite.A * pow(
+ (clr.J / 100.0),
+ (1.0 / (pMod->c * pMod->z)));
+
+ p1 = e / t;
+ p2 = (clr.A / pMod->Nbb) + 0.305;
+ p3 = 21.0 / 20.0;
+
+ hr = clr.h * d2r;
+
+ if (fabs(sin(hr)) >= fabs(cos(hr))) {
+ p4 = p1 / sin(hr);
+ clr.b = (p2 * (2.0 + p3) * (460.0 / 1403.0)) /
+ (p4 + (2.0 + p3) * (220.0 / 1403.0) *
+ (cos(hr) / sin(hr)) - (27.0 / 1403.0) +
+ p3 * (6300.0 / 1403.0));
+ clr.a = clr.b * (cos(hr) / sin(hr));
+ }
+ else {
+ p5 = p1 / cos(hr);
+ clr.a = (p2 * (2.0 + p3) * (460.0 / 1403.0)) /
+ (p5 + (2.0 + p3) * (220.0 / 1403.0) -
+ ((27.0 / 1403.0) - p3 * (6300.0 / 1403.0)) *
+ (sin(hr) / cos(hr)));
+ clr.b = clr.a * (sin(hr) / cos(hr));
+ }
+
+ clr.RGBpa[0] = ((460.0 / 1403.0) * p2) +
+ ((451.0 / 1403.0) * clr.a) +
+ ((288.0 / 1403.0) * clr.b);
+ clr.RGBpa[1] = ((460.0 / 1403.0) * p2) -
+ ((891.0 / 1403.0) * clr.a) -
+ ((261.0 / 1403.0) * clr.b);
+ clr.RGBpa[2] = ((460.0 / 1403.0) * p2) -
+ ((220.0 / 1403.0) * clr.a) -
+ ((6300.0 / 1403.0) * clr.b);
+
+ return clr;
+}
+
+static
+CAM02COLOR InverseNonlinearity(CAM02COLOR clr, LPcmsCIECAM02 pMod)
+{
+ int i;
+ double c1;
+
+ for (i = 0; i < 3; i++) {
+ if ((clr.RGBpa[i] - 0.1) < 0) c1 = -1;
+ else c1 = 1;
+ clr.RGBp[i] = c1 * (100.0 / pMod->FL) *
+ pow(((27.13 * fabs(clr.RGBpa[i] - 0.1)) /
+ (400.0 - fabs(clr.RGBpa[i] - 0.1))),
+ (1.0 / 0.42));
+ }
+
+ return clr;
+}
+
+static
+CAM02COLOR HPEtoCAT02(CAM02COLOR clr)
+{
+ double M[9];
+
+ M[0] = (( 0.7328 * 1.910197) + (0.4296 * 0.370950));
+ M[1] = (( 0.7328 * -1.112124) + (0.4296 * 0.629054));
+ M[2] = (( 0.7328 * 0.201908) + (0.4296 * 0.000008) - 0.1624);
+ M[3] = ((-0.7036 * 1.910197) + (1.6975 * 0.370950));
+ M[4] = ((-0.7036 * -1.112124) + (1.6975 * 0.629054));
+ M[5] = ((-0.7036 * 0.201908) + (1.6975 * 0.000008) + 0.0061);
+ M[6] = (( 0.0030 * 1.910197) + (0.0136 * 0.370950));
+ M[7] = (( 0.0030 * -1.112124) + (0.0136 * 0.629054));
+ M[8] = (( 0.0030 * 0.201908) + (0.0136 * 0.000008) + 0.9834);;
+
+ clr.RGBc[0] = (clr.RGBp[0] * M[0]) + (clr.RGBp[1] * M[1]) + (clr.RGBp[2] * M[2]);
+ clr.RGBc[1] = (clr.RGBp[0] * M[3]) + (clr.RGBp[1] * M[4]) + (clr.RGBp[2] * M[5]);
+ clr.RGBc[2] = (clr.RGBp[0] * M[6]) + (clr.RGBp[1] * M[7]) + (clr.RGBp[2] * M[8]);
+ return (clr);
+}
+
+
+static
+CAM02COLOR InverseChromaticAdaptation(CAM02COLOR clr, LPcmsCIECAM02 pMod)
+{
+ int i;
+ for (i = 0; i < 3; i++) {
+ clr.RGB[i] = clr.RGBc[i] /
+ ((pMod->adoptedWhite.XYZ[1] * pMod->D / pMod->adoptedWhite.RGB[i]) + 1.0 - pMod->D);
+ }
+ return(clr);
+}
+
+
+static
+CAM02COLOR CAT02toXYZ(CAM02COLOR clr)
+{
+ clr.XYZ[0] = (clr.RGB[0] * 1.096124) + (clr.RGB[1] * -0.278869) + (clr.RGB[2] * 0.182745);
+ clr.XYZ[1] = (clr.RGB[0] * 0.454369) + (clr.RGB[1] * 0.473533) + (clr.RGB[2] * 0.072098);
+ clr.XYZ[2] = (clr.RGB[0] * -0.009628) + (clr.RGB[1] * -0.005698) + (clr.RGB[2] * 1.015326);
+
+ return(clr);
+}
+
+
+
+
+LCMSHANDLE LCMSEXPORT cmsCIECAM02Init(LPcmsViewingConditions pVC)
+{
+ LPcmsCIECAM02 lpMod;
+
+
+ if((lpMod = (LPcmsCIECAM02) _cmsMalloc(sizeof(cmsCIECAM02))) == NULL) {
+ return (LCMSHANDLE) NULL;
+ }
+
+
+ ZeroMemory(lpMod, sizeof(cmsCIECAM02));
+
+ lpMod ->adoptedWhite.XYZ[0] = pVC ->whitePoint.X;
+ lpMod ->adoptedWhite.XYZ[1] = pVC ->whitePoint.Y;
+ lpMod ->adoptedWhite.XYZ[2] = pVC ->whitePoint.Z;
+
+ lpMod -> LA = pVC ->La;
+ lpMod -> Yb = pVC ->Yb;
+ lpMod -> D = pVC ->D_value;
+ lpMod -> surround = pVC ->surround;
+
+ switch (lpMod -> surround) {
+
+ case AVG_SURROUND_4:
+ lpMod->F = 1.0; // Not included in CAM02
+ lpMod->c = 0.69;
+ lpMod->Nc = 1.0;
+ break;
+
+ case CUTSHEET_SURROUND:
+ lpMod->F = 0.8;
+ lpMod->c = 0.41;
+ lpMod->Nc = 0.8;
+ break;
+
+ case DARK_SURROUND:
+ lpMod -> F = 0.8;
+ lpMod -> c = 0.525;
+ lpMod -> Nc = 0.8;
+ break;
+
+
+ case DIM_SURROUND:
+ lpMod -> F = 0.9;
+ lpMod -> c = 0.59;
+ lpMod -> Nc = 0.95;
+ break;
+
+ default:
+ // Average surround
+ lpMod -> F = 1.0;
+ lpMod -> c = 0.69;
+ lpMod -> Nc = 1.0;
+ }
+
+ lpMod -> n = compute_n(lpMod);
+ lpMod -> z = compute_z(lpMod);
+ lpMod -> Nbb = computeNbb(lpMod);
+ lpMod -> FL = computeFL(lpMod);
+
+ if (lpMod -> D == D_CALCULATE ||
+ lpMod -> D == D_CALCULATE_DISCOUNT) {
+
+ lpMod -> D = computeD(lpMod);
+ }
+
+ lpMod -> Ncb = lpMod -> Nbb;
+
+ lpMod -> adoptedWhite = XYZtoCAT02(lpMod -> adoptedWhite);
+ lpMod -> adoptedWhite = ChromaticAdaptation(lpMod -> adoptedWhite, lpMod);
+ lpMod -> adoptedWhite = CAT02toHPE(lpMod -> adoptedWhite);
+ lpMod -> adoptedWhite = NonlinearCompression(lpMod -> adoptedWhite, lpMod);
+
+ return (LCMSHANDLE) lpMod;
+
+}
+
+void LCMSEXPORT cmsCIECAM02Done(LCMSHANDLE hModel)
+{
+ LPcmsCIECAM02 lpMod = (LPcmsCIECAM02) (LPSTR) hModel;
+ if (lpMod) _cmsFree(lpMod);
+}
+
+
+void LCMSEXPORT cmsCIECAM02Forward(LCMSHANDLE hModel, LPcmsCIEXYZ pIn, LPcmsJCh pOut)
+{
+ CAM02COLOR clr;
+ LPcmsCIECAM02 lpMod = (LPcmsCIECAM02) (LPSTR) hModel;
+
+ clr.XYZ[0] = pIn ->X;
+ clr.XYZ[1] = pIn ->Y;
+ clr.XYZ[2] = pIn ->Z;
+
+ clr = XYZtoCAT02(clr);
+ clr = ChromaticAdaptation(clr, lpMod);
+ clr = CAT02toHPE(clr);
+ clr = NonlinearCompression(clr, lpMod);
+ clr = ComputeCorrelates(clr, lpMod);
+
+ pOut ->J = clr.J;
+ pOut ->C = clr.C;
+ pOut ->h = clr.h;
+}
+
+void LCMSEXPORT cmsCIECAM02Reverse(LCMSHANDLE hModel, LPcmsJCh pIn, LPcmsCIEXYZ pOut)
+{
+ CAM02COLOR clr;
+ LPcmsCIECAM02 lpMod = (LPcmsCIECAM02) (LPSTR) hModel;
+
+
+ clr.J = pIn -> J;
+ clr.C = pIn -> C;
+ clr.h = pIn -> h;
+
+ clr = InverseCorrelates(clr, lpMod);
+ clr = InverseNonlinearity(clr, lpMod);
+ clr = HPEtoCAT02(clr);
+ clr = InverseChromaticAdaptation(clr, lpMod);
+ clr = CAT02toXYZ(clr);
+
+ pOut ->X = clr.XYZ[0];
+ pOut ->Y = clr.XYZ[1];
+ pOut ->Z = clr.XYZ[2];
+
+}
+
diff --git a/debian/lcms/lcms-1.19.dfsg2/src/cmscam97.c b/debian/lcms/lcms-1.19.dfsg2/src/cmscam97.c
new file mode 100755
index 00000000..0e8a79b2
--- /dev/null
+++ b/debian/lcms/lcms-1.19.dfsg2/src/cmscam97.c
@@ -0,0 +1,721 @@
+//
+// Little cms
+// Copyright (C) 1998-2007 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 "lcms.h"
+
+
+/*
+typedef struct {
+ double J;
+ double C;
+ double h;
+
+ } cmsJCh, FAR* LPcmsJCh;
+
+
+#define AVG_SURROUND_4 0
+#define AVG_SURROUND 1
+#define DIM_SURROUND 2
+#define DARK_SURROUND 3
+#define CUTSHEET_SURROUND 4
+
+
+typedef struct {
+
+ cmsCIEXYZ whitePoint;
+ double Yb;
+ double La;
+ int surround;
+ double D_value;
+
+ } cmsViewingConditions, FAR* LPcmsViewingConditions;
+
+
+
+LCMSAPI LCMSHANDLE LCMSEXPORT cmsCIECAM97sInit(LPcmsViewingConditions pVC);
+LCMSAPI void LCMSEXPORT cmsCIECAM97sDone(LCMSHANDLE hModel);
+LCMSAPI void LCMSEXPORT cmsCIECAM97sForward(LCMSHANDLE hModel, LPcmsCIEXYZ pIn, LPcmsJCh pOut);
+LCMSAPI void LCMSEXPORT cmsCIECAM97sReverse(LCMSHANDLE hModel, LPcmsJCh pIn, LPcmsCIEXYZ pOut);
+
+*/
+
+// ---------- Implementation --------------------------------------------
+
+// #define USE_CIECAM97s2 1
+
+#ifdef USE_CIECAM97s2
+
+# define NOISE_CONSTANT 3.05
+#else
+# define NOISE_CONSTANT 2.05
+#endif
+
+
+/*
+ The model input data are the adapting field luminance in cd/m2
+ (normally taken to be 20% of the luminance of white in the adapting field),
+ LA , the relative tristimulus values of the stimulus, XYZ, the relative
+ tristimulus values of white in the same viewing conditions, Xw Yw Zw ,
+ and the relative luminance of the background, Yb . Relative tristimulus
+ values should be expressed on a scale from Y = 0 for a perfect black
+ to Y = 100 for a perfect reflecting diffuser. Additionally, the
+ parameters c, for the impact of surround, Nc , a chromatic induction factor,
+ and F, a factor for degree of adaptation, must be selected according to the
+ guidelines in table
+
+ All CIE tristimulus values are obtained using the CIE 1931
+ Standard Colorimetric Observer (2°).
+
+*/
+
+typedef struct {
+
+ cmsCIEXYZ WP;
+ int surround;
+ int calculate_D;
+
+ double Yb; // rel. luminance of background
+
+ cmsCIEXYZ RefWhite;
+
+ double La; // The adapting field luminance in cd/m2
+
+ double c; // Impact of surround
+ double Nc; // Chromatic induction factor
+ double Fll; // Lightness contrast factor (Removed on rev 2)
+ double F; // Degree of adaptation
+
+
+ double k;
+ double Fl;
+
+ double Nbb; // The background and chromatic brightness induction factors.
+ double Ncb;
+ double z; // base exponential nonlinearity
+ double n; // background induction factor
+ double D;
+
+ MAT3 MlamRigg;
+ MAT3 MlamRigg_1;
+
+ MAT3 Mhunt;
+ MAT3 Mhunt_1;
+
+ MAT3 Mhunt_x_MlamRigg_1;
+ MAT3 MlamRigg_x_Mhunt_1;
+
+
+ VEC3 RGB_subw;
+ VEC3 RGB_subw_prime;
+
+ double p;
+
+ VEC3 RGB_subwc;
+
+ VEC3 RGB_subaw_prime;
+ double A_subw;
+ double Q_subw;
+
+ } cmsCIECAM97s,FAR *LPcmsCIECAM97s;
+
+
+
+// Free model structure
+
+LCMSAPI void LCMSEXPORT cmsCIECAM97sDone(LCMSHANDLE hModel)
+{
+ LPcmsCIECAM97s lpMod = (LPcmsCIECAM97s) (LPSTR) hModel;
+ if (lpMod) _cmsFree(lpMod);
+}
+
+// Partial discounting for adaptation degree computation
+
+static
+double discount(double d, double chan)
+{
+ return (d * chan + 1 - d);
+}
+
+
+// This routine does model exponential nonlinearity on the short wavelenght
+// sensitive channel. On CIECAM97s rev 2 this has been reverted to linear.
+
+static
+void FwAdaptationDegree(LPcmsCIECAM97s lpMod, LPVEC3 RGBc, LPVEC3 RGB)
+{
+
+
+#ifdef USE_CIECAM97s2
+ RGBc->n[0] = RGB->n[0]* discount(lpMod->D, 100.0/lpMod->RGB_subw.n[0]);
+ RGBc->n[1] = RGB->n[1]* discount(lpMod->D, 100.0/lpMod->RGB_subw.n[1]);
+ RGBc->n[2] = RGB->n[2]* discount(lpMod->D, 100.0/lpMod->RGB_subw.n[2]);
+#else
+
+ RGBc->n[0] = RGB->n[0]* discount(lpMod->D, 1.0/lpMod->RGB_subw.n[0]);
+ RGBc->n[1] = RGB->n[1]* discount(lpMod->D, 1.0/lpMod->RGB_subw.n[1]);
+
+ RGBc->n[2] = pow(fabs(RGB->n[2]), lpMod ->p) * discount(lpMod->D, (1.0/pow(lpMod->RGB_subw.n[2], lpMod->p)));
+
+ // If B happens to be negative, Then Bc is also set to be negative
+
+ if (RGB->n[2] < 0)
+ RGBc->n[2] = -RGBc->n[2];
+#endif
+}
+
+
+static
+void RvAdaptationDegree(LPcmsCIECAM97s lpMod, LPVEC3 RGBc, LPVEC3 RGB)
+{
+
+
+#ifdef USE_CIECAM97s2
+ RGBc->n[0] = RGB->n[0]/discount(lpMod->D, 100.0/lpMod->RGB_subw.n[0]);
+ RGBc->n[1] = RGB->n[1]/discount(lpMod->D, 100.0/lpMod->RGB_subw.n[1]);
+ RGBc->n[2] = RGB->n[2]/discount(lpMod->D, 100.0/lpMod->RGB_subw.n[2]);
+#else
+
+ RGBc->n[0] = RGB->n[0]/discount(lpMod->D, 1.0/lpMod->RGB_subw.n[0]);
+ RGBc->n[1] = RGB->n[1]/discount(lpMod->D, 1.0/lpMod->RGB_subw.n[1]);
+ RGBc->n[2] = pow(fabs(RGB->n[2]), 1.0/lpMod->p)/pow(discount(lpMod->D, 1.0/pow(lpMod->RGB_subw.n[2], lpMod->p)), 1.0/lpMod->p);
+ if (RGB->n[2] < 0)
+ RGBc->n[2] = -RGBc->n[2];
+#endif
+}
+
+
+
+static
+void PostAdaptationConeResponses(LPcmsCIECAM97s lpMod, LPVEC3 RGBa_prime, LPVEC3 RGBprime)
+{
+ if (RGBprime->n[0]>=0.0) {
+
+ RGBa_prime->n[0]=((40.0*pow(lpMod -> Fl * RGBprime->n[0]/100.0, 0.73))/(pow(lpMod -> Fl * RGBprime->n[0]/100.0, 0.73)+2))+1;
+ }
+ else
+ {
+ RGBa_prime->n[0]=((-40.0*pow((-lpMod -> Fl * RGBprime->n[0])/100.0, 0.73))/(pow((-lpMod -> Fl * RGBprime->n[0])/100.0, 0.73)+2))+1;
+ }
+
+ if (RGBprime->n[1]>=0.0)
+ {
+ RGBa_prime->n[1]=((40.0*pow(lpMod -> Fl * RGBprime->n[1]/100.0, 0.73))/(pow(lpMod -> Fl * RGBprime->n[1]/100.0, 0.73)+2))+1;
+ }
+ else
+ {
+ RGBa_prime->n[1]=((-40.0*pow((-lpMod -> Fl * RGBprime->n[1])/100.0, 0.73))/(pow((-lpMod -> Fl * RGBprime->n[1])/100.0, 0.73)+2))+1;
+ }
+
+ if (RGBprime->n[2]>=0.0)
+ {
+ RGBa_prime->n[2]=((40.0*pow(lpMod -> Fl * RGBprime->n[2]/100.0, 0.73))/(pow(lpMod -> Fl * RGBprime->n[2]/100.0, 0.73)+2))+1;
+ }
+ else
+ {
+ RGBa_prime->n[2]=((-40.0*pow((-lpMod -> Fl * RGBprime->n[2])/100.0, 0.73))/(pow((-lpMod -> Fl * RGBprime->n[2])/100.0, 0.73)+2))+1;
+ }
+}
+
+
+// Compute hue quadrature, eccentricity factor, e
+
+static
+void ComputeHueQuadrature(double h, double* H, double* e)
+{
+
+
+#define IRED 0
+#define IYELLOW 1
+#define IGREEN 2
+#define IBLUE 3
+
+ double e_tab[] = {0.8, 0.7, 1.0, 1.2};
+ double H_tab[] = { 0, 100, 200, 300};
+ int p1, p2;
+ double e1, e2, h1, h2;
+
+
+ if (h >= 20.14 && h < 90.0) { // Red
+
+ p1 = IRED;
+ p2 = IYELLOW;
+ }
+ else
+ if (h >= 90.0 && h < 164.25) { // Yellow
+
+ p1 = IYELLOW;
+ p2 = IGREEN;
+ }
+ else
+ if (h >= 164.25 && h < 237.53) { // Green
+
+ p1 = IGREEN;
+ p2 = IBLUE; }
+ else { // Blue
+
+ p1 = IBLUE;
+ p2 = IRED;
+ }
+
+ e1 = e_tab[p1]; e2 = e_tab[p2];
+ h1 = H_tab[p1]; h2 = H_tab[p2];
+
+
+
+ *e = e1 + ((e2-e1)*(h-h1)/(h2 - h1));
+ *H = h1 + (100. * (h - h1) / e1) / ((h - h1)/e1 + (h2 - h) / e2);
+
+#undef IRED
+#undef IYELLOW
+#undef IGREEN
+#undef IBLUE
+
+}
+
+
+
+
+
+
+LCMSAPI LCMSHANDLE LCMSEXPORT cmsCIECAM97sInit(LPcmsViewingConditions pVC)
+{
+ LPcmsCIECAM97s lpMod;
+ VEC3 tmp;
+
+ if((lpMod = (LPcmsCIECAM97s) _cmsMalloc(sizeof(cmsCIECAM97s))) == NULL) {
+ return (LCMSHANDLE) NULL;
+ }
+
+
+ lpMod->WP.X = pVC->whitePoint.X;
+ lpMod->WP.Y = pVC->whitePoint.Y;
+ lpMod->WP.Z = pVC->whitePoint.Z;
+
+ lpMod->Yb = pVC->Yb;
+ lpMod->La = pVC->La;
+
+ lpMod->surround = pVC->surround;
+
+ lpMod->RefWhite.X = 100.0;
+ lpMod->RefWhite.Y = 100.0;
+ lpMod->RefWhite.Z = 100.0;
+
+#ifdef USE_CIECAM97s2
+
+ VEC3init(&lpMod->MlamRigg.v[0], 0.8562, 0.3372, -0.1934);
+ VEC3init(&lpMod->MlamRigg.v[1], -0.8360, 1.8327, 0.0033);
+ VEC3init(&lpMod->MlamRigg.v[2], 0.0357,-0.0469, 1.0112);
+
+ VEC3init(&lpMod->MlamRigg_1.v[0], 0.9874, -0.1768, 0.1894);
+ VEC3init(&lpMod->MlamRigg_1.v[1], 0.4504, 0.4649, 0.0846);
+ VEC3init(&lpMod->MlamRigg_1.v[2],-0.0139, 0.0278, 0.9861);
+
+#else
+ // Bradford transform: Lam-Rigg cone responses
+ VEC3init(&lpMod->MlamRigg.v[0], 0.8951, 0.2664, -0.1614);
+ VEC3init(&lpMod->MlamRigg.v[1], -0.7502, 1.7135, 0.0367);
+ VEC3init(&lpMod->MlamRigg.v[2], 0.0389, -0.0685, 1.0296);
+
+
+ // Inverse of Lam-Rigg
+ VEC3init(&lpMod->MlamRigg_1.v[0], 0.98699, -0.14705, 0.15996);
+ VEC3init(&lpMod->MlamRigg_1.v[1], 0.43231, 0.51836, 0.04929);
+ VEC3init(&lpMod->MlamRigg_1.v[2], -0.00853, 0.04004, 0.96849);
+
+#endif
+
+ // Hunt-Pointer-Estevez cone responses
+ VEC3init(&lpMod->Mhunt.v[0], 0.38971, 0.68898, -0.07868);
+ VEC3init(&lpMod->Mhunt.v[1], -0.22981, 1.18340, 0.04641);
+ VEC3init(&lpMod->Mhunt.v[2], 0.0, 0.0, 1.0);
+
+ // Inverse of Hunt-Pointer-Estevez
+ VEC3init(&lpMod->Mhunt_1.v[0], 1.91019, -1.11214, 0.20195);
+ VEC3init(&lpMod->Mhunt_1.v[1], 0.37095, 0.62905, 0.0);
+ VEC3init(&lpMod->Mhunt_1.v[2], 0.0, 0.0, 1.0);
+
+
+ if (pVC->D_value == -1.0)
+ lpMod->calculate_D = 1;
+ else
+ if (pVC->D_value == -2.0)
+ lpMod->calculate_D = 2;
+ else {
+ lpMod->calculate_D = 0;
+ lpMod->D = pVC->D_value;
+ }
+
+ // Table I (revised)
+
+ switch (lpMod->surround) {
+
+ case AVG_SURROUND_4:
+ lpMod->F = 1.0;
+ lpMod->c = 0.69;
+ lpMod->Fll = 0.0; // Not included on Rev 2
+ lpMod->Nc = 1.0;
+ break;
+ case AVG_SURROUND:
+ lpMod->F = 1.0;
+ lpMod->c = 0.69;
+ lpMod->Fll = 1.0;
+ lpMod->Nc = 1.0;
+ break;
+ case DIM_SURROUND:
+ lpMod->F = 0.99;
+ lpMod->c = 0.59;
+ lpMod->Fll = 1.0;
+ lpMod->Nc = 0.95;
+ break;
+ case DARK_SURROUND:
+ lpMod->F = 0.9;
+ lpMod->c = 0.525;
+ lpMod->Fll = 1.0;
+ lpMod->Nc = 0.8;
+ break;
+ case CUTSHEET_SURROUND:
+ lpMod->F = 0.9;
+ lpMod->c = 0.41;
+ lpMod->Fll = 1.0;
+ lpMod->Nc = 0.8;
+ break;
+ default:
+ lpMod->F = 1.0;
+ lpMod->c = 0.69;
+ lpMod->Fll = 1.0;
+ lpMod->Nc = 1.0;
+ break;
+ }
+
+ lpMod->k = 1 / (5 * lpMod->La + 1);
+ lpMod->Fl = lpMod->La * pow(lpMod->k, 4) + 0.1*pow(1 - pow(lpMod->k, 4), 2.0) * pow(5*lpMod->La, 1.0/3.0);
+
+ if (lpMod->calculate_D > 0) {
+
+ lpMod->D = lpMod->F * (1 - 1 / (1 + 2*pow(lpMod->La, 0.25) + pow(lpMod->La, 2)/300.0));
+ if (lpMod->calculate_D > 1)
+ lpMod->D = (lpMod->D + 1.0) / 2;
+ }
+
+
+ // RGB_subw = [MlamRigg][WP/YWp]
+#ifdef USE_CIECAM97s2
+ MAT3eval(&lpMod -> RGB_subw, &lpMod -> MlamRigg, &lpMod -> WP);
+#else
+ VEC3divK(&tmp, (LPVEC3) &lpMod -> WP, lpMod->WP.Y);
+ MAT3eval(&lpMod -> RGB_subw, &lpMod -> MlamRigg, &tmp);
+#endif
+
+
+
+ MAT3per(&lpMod -> Mhunt_x_MlamRigg_1, &lpMod -> Mhunt, &lpMod->MlamRigg_1 );
+ MAT3per(&lpMod -> MlamRigg_x_Mhunt_1, &lpMod -> MlamRigg, &lpMod -> Mhunt_1 );
+
+ // p is used on forward model
+ lpMod->p = pow(lpMod->RGB_subw.n[2], 0.0834);
+
+ FwAdaptationDegree(lpMod, &lpMod->RGB_subwc, &lpMod->RGB_subw);
+
+#if USE_CIECAM97s2
+ MAT3eval(&lpMod->RGB_subw_prime, &lpMod->Mhunt_x_MlamRigg_1, &lpMod -> RGB_subwc);
+#else
+ VEC3perK(&tmp, &lpMod -> RGB_subwc, lpMod->WP.Y);
+ MAT3eval(&lpMod->RGB_subw_prime, &lpMod->Mhunt_x_MlamRigg_1, &tmp);
+#endif
+
+ lpMod->n = lpMod-> Yb / lpMod-> WP.Y;
+
+ lpMod->z = 1 + lpMod->Fll * sqrt(lpMod->n);
+ lpMod->Nbb = lpMod->Ncb = 0.725 / pow(lpMod->n, 0.2);
+
+ PostAdaptationConeResponses(lpMod, &lpMod->RGB_subaw_prime, &lpMod->RGB_subw_prime);
+
+ lpMod->A_subw=lpMod->Nbb*(2.0*lpMod->RGB_subaw_prime.n[0]+lpMod->RGB_subaw_prime.n[1]+lpMod->RGB_subaw_prime.n[2]/20.0-NOISE_CONSTANT);
+
+ return (LCMSHANDLE) lpMod;
+}
+
+
+
+
+//
+// The forward model: XYZ -> JCh
+//
+
+LCMSAPI void LCMSEXPORT cmsCIECAM97sForward(LCMSHANDLE hModel, LPcmsCIEXYZ inPtr, LPcmsJCh outPtr)
+{
+
+ LPcmsCIECAM97s lpMod = (LPcmsCIECAM97s) (LPSTR) hModel;
+ double a, b, h, s, H1val, es, A;
+ VEC3 In, RGB, RGBc, RGBprime, RGBa_prime;
+
+ if (inPtr -> Y <= 0.0) {
+
+ outPtr -> J = outPtr -> C = outPtr -> h = 0.0;
+ return;
+ }
+
+ // An initial chromatic adaptation transform is used to go from the source
+ // viewing conditions to corresponding colours under the equal-energy-illuminant
+ // reference viewing conditions. This is handled differently on rev 2
+
+ VEC3init(&In, inPtr -> X, inPtr -> Y, inPtr -> Z); // 2.1
+
+#ifdef USE_CIECAM97s2
+ // Since the chromatic adaptation transform has been linearized, it
+ // is no longer required to divide the stimulus tristimulus values
+ // by their own Y tristimulus value prior to the chromatic adaptation.
+#else
+ VEC3divK(&In, &In, inPtr -> Y);
+#endif
+
+ MAT3eval(&RGB, &lpMod -> MlamRigg, &In); // 2.2
+
+ FwAdaptationDegree(lpMod, &RGBc, &RGB);
+
+ // The post-adaptation signals for both the sample and the white are then
+ // transformed from the sharpened cone responses to the Hunt-Pointer-Estevez
+ // cone responses.
+#ifdef USE_CIECAM97s2
+#else
+ VEC3perK(&RGBc, &RGBc, inPtr->Y);
+#endif
+
+ MAT3eval(&RGBprime, &lpMod->Mhunt_x_MlamRigg_1, &RGBc);
+
+ // The post-adaptation cone responses (for both the stimulus and the white)
+ // are then calculated.
+
+ PostAdaptationConeResponses(lpMod, &RGBa_prime, &RGBprime);
+
+ // Preliminary red-green and yellow-blue opponent dimensions are calculated
+
+ a = RGBa_prime.n[0] - (12.0 * RGBa_prime.n[1] / 11.0) + RGBa_prime.n[2]/11.0;
+ b = (RGBa_prime.n[0] + RGBa_prime.n[1] - 2.0 * RGBa_prime.n[2]) / 9.0;
+
+
+ // The CIECAM97s hue angle, h, is then calculated
+ h = (180.0/M_PI)*(atan2(b, a));
+
+
+ while (h < 0)
+ h += 360.0;
+
+ outPtr->h = h;
+
+ // hue quadrature and eccentricity factors, e, are calculated
+
+ ComputeHueQuadrature(h, &H1val, &es);
+
+ // ComputeHueQuadrature(h, &H1val, &h1, &e1, &h2, &e2, &es);
+
+
+ // The achromatic response A
+ A = lpMod->Nbb * (2.0 * RGBa_prime.n[0] + RGBa_prime.n[1] + RGBa_prime.n[2]/20.0 - NOISE_CONSTANT);
+
+ // CIECAM97s Lightness J
+ outPtr -> J = 100.0 * pow(A / lpMod->A_subw, lpMod->c * lpMod->z);
+
+ // CIECAM97s saturation s
+ s = (50 * hypot (a, b) * 100 * es * (10.0/13.0) * lpMod-> Nc * lpMod->Ncb) / (RGBa_prime.n[0] + RGBa_prime.n[1] + 1.05 * RGBa_prime.n[2]);
+
+ // CIECAM97s Chroma C
+
+#ifdef USE_CIECAM97s2
+ // Eq. 26 has been modified to allow accurate prediction of the Munsell chroma scales.
+ outPtr->C = 0.7487 * pow(s, 0.973) * pow(outPtr->J/100.0, 0.945 * lpMod->n) * (1.64 - pow(0.29, lpMod->n));
+
+#else
+ outPtr->C = 2.44 * pow(s, 0.69) * pow(outPtr->J/100.0, 0.67 * lpMod->n) * (1.64 - pow(0.29, lpMod->n));
+#endif
+}
+
+
+//
+// The reverse model JCh -> XYZ
+//
+
+
+LCMSAPI void LCMSEXPORT cmsCIECAM97sReverse(LCMSHANDLE hModel, LPcmsJCh inPtr, LPcmsCIEXYZ outPtr)
+{
+ LPcmsCIECAM97s lpMod = (LPcmsCIECAM97s) (LPSTR) hModel;
+ double J, C, h, A, H1val, es, s, a, b;
+ double tan_h, sec_h;
+ double R_suba_prime, G_suba_prime, B_suba_prime;
+ double R_prime, G_prime, B_prime;
+ double Y_subc, Y_prime, B_term;
+ VEC3 tmp;
+ VEC3 RGB_prime, RGB_subc_Y;
+ VEC3 Y_over_Y_subc_RGB;
+ VEC3 XYZ_primeprime_over_Y_subc;
+#ifdef USE_CIECAM92s2
+ VEC3 RGBY;
+ VEC3 Out;
+#endif
+
+ J = inPtr->J;
+ h = inPtr->h;
+ C = inPtr->C;
+
+ if (J <= 0) {
+
+ outPtr->X = 0.0;
+ outPtr->Y = 0.0;
+ outPtr->Z = 0.0;
+ return;
+ }
+
+
+
+ // (2) From J Obtain A
+
+ A = pow(J/100.0, 1/(lpMod->c * lpMod->z)) * lpMod->A_subw;
+
+
+ // (3), (4), (5) Using H Determine h1, h2, e1, e2
+ // e1 and h1 are the values of e and h for the unique hue having the
+ // nearest lower valur of h and e2 and h2 are the values of e and h for
+ // the unique hue having the nearest higher value of h.
+
+
+ ComputeHueQuadrature(h, &H1val, &es);
+
+ // (7) Calculate s
+
+ s = pow(C / (2.44 * pow(J/100.0, 0.67*lpMod->n) * (1.64 - pow(0.29, lpMod->n))) , (1./0.69));
+
+
+ // (8) Calculate a and b.
+ // NOTE: sqrt(1 + tan^2) == sec(h)
+
+ tan_h = tan ((M_PI/180.)*(h));
+ sec_h = sqrt(1 + tan_h * tan_h);
+
+ if ((h > 90) && (h < 270))
+ sec_h = -sec_h;
+
+ a = s * ( A/lpMod->Nbb + NOISE_CONSTANT) / ( sec_h * 50000.0 * es * lpMod->Nc * lpMod->Ncb/ 13.0 +
+ s * (11.0 / 23.0 + (108.0/23.0) * tan_h));
+
+ b = a * tan_h;
+
+ //(9) Calculate R'a G'a and B'a
+
+ R_suba_prime = (20.0/61.0) * (A/lpMod->Nbb + NOISE_CONSTANT) + (41.0/61.0) * (11.0/23.0) * a + (288.0/61.0) / 23.0 * b;
+ G_suba_prime = (20.0/61.0) * (A/lpMod->Nbb + NOISE_CONSTANT) - (81.0/61.0) * (11.0/23.0) * a - (261.0/61.0) / 23.0 * b;
+ B_suba_prime = (20.0/61.0) * (A/lpMod->Nbb + NOISE_CONSTANT) - (20.0/61.0) * (11.0/23.0) * a - (20.0/61.0) * (315.0/23.0) * b;
+
+ // (10) Calculate R', G' and B'
+
+ if ((R_suba_prime - 1) < 0) {
+
+ R_prime = -100.0 * pow((2.0 - 2.0 * R_suba_prime) /
+ (39.0 + R_suba_prime), 1.0/0.73);
+ }
+ else
+ {
+ R_prime = 100.0 * pow((2.0 * R_suba_prime - 2.0) /
+ (41.0 - R_suba_prime), 1.0/0.73);
+ }
+
+ if ((G_suba_prime - 1) < 0)
+ {
+ G_prime = -100.0 * pow((2.0 - 2.0 * G_suba_prime) /
+ (39.0 + G_suba_prime), 1.0/0.73);
+ }
+ else
+ {
+ G_prime = 100.0 * pow((2.0 * G_suba_prime - 2.0) /
+ (41.0 - G_suba_prime), 1.0/0.73);
+ }
+
+ if ((B_suba_prime - 1) < 0)
+ {
+ B_prime = -100.0 * pow((2.0 - 2.0 * B_suba_prime) /
+ (39.0 + B_suba_prime), 1.0/0.73);
+ }
+ else
+ {
+ B_prime = 100.0 * pow((2.0 * B_suba_prime - 2.0) /
+ (41.0 - B_suba_prime), 1.0/0.73);
+ }
+
+
+ // (11) Calculate RcY, GcY and BcY
+
+ VEC3init(&RGB_prime, R_prime, G_prime, B_prime);
+ VEC3divK(&tmp, &RGB_prime, lpMod -> Fl);
+
+ MAT3eval(&RGB_subc_Y, &lpMod->MlamRigg_x_Mhunt_1, &tmp);
+
+
+
+
+#ifdef USE_CIECAM97s2
+
+ // (12)
+
+
+ RvAdaptationDegree(lpMod, &RGBY, &RGB_subc_Y);
+ MAT3eval(&Out, &lpMod->MlamRigg_1, &RGBY);
+
+ outPtr -> X = Out.n[0];
+ outPtr -> Y = Out.n[1];
+ outPtr -> Z = Out.n[2];
+
+#else
+
+ // (12) Calculate Yc
+
+ Y_subc = 0.43231*RGB_subc_Y.n[0]+0.51836*RGB_subc_Y.n[1]+0.04929*RGB_subc_Y.n[2];
+
+ // (13) Calculate (Y/Yc)R, (Y/Yc)G and (Y/Yc)B
+
+ VEC3divK(&RGB_subc_Y, &RGB_subc_Y, Y_subc);
+ RvAdaptationDegree(lpMod, &Y_over_Y_subc_RGB, &RGB_subc_Y);
+
+ // (14) Calculate Y'
+ Y_prime = 0.43231*(Y_over_Y_subc_RGB.n[0]*Y_subc) + 0.51836*(Y_over_Y_subc_RGB.n[1]*Y_subc) + 0.04929 * (Y_over_Y_subc_RGB.n[2]*Y_subc);
+
+ if (Y_prime < 0 || Y_subc < 0)
+ {
+ // Discard to near black point
+
+ outPtr -> X = 0;
+ outPtr -> Y = 0;
+ outPtr -> Z = 0;
+ return;
+ }
+
+ B_term = pow(Y_prime / Y_subc, (1.0 / lpMod->p) - 1);
+
+ // (15) Calculate X'', Y'' and Z''
+ Y_over_Y_subc_RGB.n[2] /= B_term;
+ MAT3eval(&XYZ_primeprime_over_Y_subc, &lpMod->MlamRigg_1, &Y_over_Y_subc_RGB);
+
+ outPtr->X = XYZ_primeprime_over_Y_subc.n[0] * Y_subc;
+ outPtr->Y = XYZ_primeprime_over_Y_subc.n[1] * Y_subc;
+ outPtr->Z = XYZ_primeprime_over_Y_subc.n[2] * Y_subc;
+#endif
+
+}
diff --git a/debian/lcms/lcms-1.19.dfsg2/src/cmscam97.c.rej b/debian/lcms/lcms-1.19.dfsg2/src/cmscam97.c.rej
new file mode 100755
index 00000000..62117532
--- /dev/null
+++ b/debian/lcms/lcms-1.19.dfsg2/src/cmscam97.c.rej
@@ -0,0 +1,21 @@
+***************
+*** 420,428 ****
+
+ // RGB_subw = [MlamRigg][WP/YWp]
+ #ifdef USE_CIECAM97s2
+- MAT3eval(&lpMod -> RGB_subw, &lpMod -> MlamRigg, (LPVEC3) &lpMod -> WP);
+ #else
+- VEC3divK(&tmp, (LPVEC3) &lpMod -> WP, lpMod->WP.Y);
+ MAT3eval(&lpMod -> RGB_subw, &lpMod -> MlamRigg, &tmp);
+ #endif
+
+--- 420,428 ----
+
+ // RGB_subw = [MlamRigg][WP/YWp]
+ #ifdef USE_CIECAM97s2
++ MAT3eval(&lpMod -> RGB_subw, &lpMod -> MlamRigg, &lpMod -> WP);
+ #else
++ VEC3divK(&tmp, &lpMod -> WP, lpMod->WP.Y);
+ MAT3eval(&lpMod -> RGB_subw, &lpMod -> MlamRigg, &tmp);
+ #endif
+
diff --git a/debian/lcms/lcms-1.19.dfsg2/src/cmscgats.c b/debian/lcms/lcms-1.19.dfsg2/src/cmscgats.c
new file mode 100755
index 00000000..042429f8
--- /dev/null
+++ b/debian/lcms/lcms-1.19.dfsg2/src/cmscgats.c
@@ -0,0 +1,2703 @@
+//
+// Little cms
+// Copyright (C) 1998-2007 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.
+//
+// IT8.7 / CGATS.17-200x handling
+
+#include "lcms.h"
+
+
+LCMSAPI LCMSHANDLE LCMSEXPORT cmsIT8Alloc(void);
+LCMSAPI void LCMSEXPORT cmsIT8Free(LCMSHANDLE IT8);
+
+// Tables
+
+LCMSAPI int LCMSEXPORT cmsIT8TableCount(LCMSHANDLE IT8);
+LCMSAPI int LCMSEXPORT cmsIT8SetTable(LCMSHANDLE IT8, int nTable);
+
+// Persistence
+LCMSAPI LCMSHANDLE LCMSEXPORT cmsIT8LoadFromFile(const char* cFileName);
+LCMSAPI LCMSHANDLE LCMSEXPORT cmsIT8LoadFromMem(void *Ptr, size_t len);
+LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SaveToFile(LCMSHANDLE IT8, const char* cFileName);
+
+// Properties
+LCMSAPI const char* LCMSEXPORT cmsIT8GetSheetType(LCMSHANDLE hIT8);
+LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetSheetType(LCMSHANDLE hIT8, const char* Type);
+
+LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetComment(LCMSHANDLE hIT8, const char* cComment);
+
+LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetPropertyStr(LCMSHANDLE hIT8, const char* cProp, const char *Str);
+LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetPropertyDbl(LCMSHANDLE hIT8, const char* cProp, double Val);
+LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetPropertyHex(LCMSHANDLE hIT8, const char* cProp, int Val);
+LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetPropertyMulti(LCMSHANDLE hIT8, const char* cProp, const char* cSubProp, const char *Val);
+LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetPropertyUncooked(LCMSHANDLE hIT8, const char* Key, const char* Buffer);
+
+LCMSAPI const char* LCMSEXPORT cmsIT8GetProperty(LCMSHANDLE hIT8, const char* cProp);
+LCMSAPI double LCMSEXPORT cmsIT8GetPropertyDbl(LCMSHANDLE hIT8, const char* cProp);
+LCMSAPI const char* LCMSEXPORT cmsIT8GetPropertyMulti(LCMSHANDLE hIT8, const char* cProp, const char *cSubProp);
+LCMSAPI int LCMSEXPORT cmsIT8EnumProperties(LCMSHANDLE IT8, const char ***PropertyNames);
+LCMSAPI int LCMSEXPORT cmsIT8EnumPropertyMulti(LCMSHANDLE hIT8, const char* cProp, const char*** SubpropertyNames);
+
+// Datasets
+
+LCMSAPI const char* LCMSEXPORT cmsIT8GetPatchName(LCMSHANDLE hIT8, int nPatch, char* buffer);
+
+LCMSAPI const char* LCMSEXPORT cmsIT8GetDataRowCol(LCMSHANDLE IT8, int row, int col);
+LCMSAPI double LCMSEXPORT cmsIT8GetDataRowColDbl(LCMSHANDLE IT8, int col, int row);
+
+LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetDataRowCol(LCMSHANDLE hIT8, int row, int col,
+ const char* Val);
+
+LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetDataRowColDbl(LCMSHANDLE hIT8, int row, int col,
+ double Val);
+
+LCMSAPI const char* LCMSEXPORT cmsIT8GetData(LCMSHANDLE IT8, const char* cPatch, const char* cSample);
+
+
+LCMSAPI double LCMSEXPORT cmsIT8GetDataDbl(LCMSHANDLE IT8, const char* cPatch, const char* cSample);
+
+LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetData(LCMSHANDLE IT8, const char* cPatch,
+ const char* cSample,
+ const char *Val);
+
+LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetDataDbl(LCMSHANDLE hIT8, const char* cPatch,
+ const char* cSample,
+ double Val);
+
+LCMSAPI LCMSBOOL LCMSEXPORT cmsIT8SetDataFormat(LCMSHANDLE IT8, int n, const char *Sample);
+LCMSAPI int LCMSEXPORT cmsIT8EnumDataFormat(LCMSHANDLE IT8, char ***SampleNames);
+
+LCMSAPI void LCMSEXPORT cmsIT8DefineDblFormat(LCMSHANDLE IT8, const char* Formatter);
+
+LCMSAPI int LCMSEXPORT cmsIT8SetTableByLabel(LCMSHANDLE hIT8, const char* cSet,
+ const char* cField,
+ const char* ExpectedType);
+
+// ------------------------------------------------------------- Implementation
+
+
+#define SIZEOFLONGMINUS1 (sizeof(long)-1)
+#define ALIGNLONG(x) (((x)+SIZEOFLONGMINUS1) & ~(SIZEOFLONGMINUS1))
+
+// #define STRICT_CGATS 1
+
+#define MAXID 128 // Max lenght of identifier
+#define MAXSTR 1024 // Max lenght of string
+#define MAXTABLES 255 // Max Number of tables in a single stream
+#define MAXINCLUDE 20 // Max number of nested includes
+
+#define DEFAULT_DBL_FORMAT "%.10g" // Double formatting
+
+#include <ctype.h>
+#include <limits.h>
+
+#ifndef NON_WINDOWS
+#include <io.h>
+#define DIR_CHAR '\\'
+#else
+#define DIR_CHAR '/'
+#endif
+
+// Symbols
+
+typedef enum {
+
+ SNONE,
+ SINUM, // Integer
+ SDNUM, // Real
+ SIDENT, // Identifier
+ SSTRING, // string
+ SCOMMENT, // comment
+ SEOLN, // End of line
+ SEOF, // End of stream
+ SSYNERROR, // Syntax error found on stream
+
+ // Keywords
+
+ SBEGIN_DATA,
+ SBEGIN_DATA_FORMAT,
+ SEND_DATA,
+ SEND_DATA_FORMAT,
+ SKEYWORD,
+ SDATA_FORMAT_ID,
+ SINCLUDE
+
+ } SYMBOL;
+
+
+// How to write the value
+
+typedef enum {
+ WRITE_UNCOOKED,
+ WRITE_STRINGIFY,
+ WRITE_HEXADECIMAL,
+ WRITE_BINARY,
+ WRITE_PAIR
+
+ } WRITEMODE;
+
+// Linked list of variable names
+
+typedef struct _KeyVal {
+
+ struct _KeyVal* Next;
+ char* Keyword; // Name of variable
+ struct _KeyVal* NextSubkey; // If key is a dictionary, points to the next item
+ char* Subkey; // If key is a dictionary, points to the subkey name
+ char* Value; // Points to value
+ WRITEMODE WriteAs; // How to write the value
+
+ } KEYVALUE, *LPKEYVALUE;
+
+
+// Linked list of memory chunks (Memory sink)
+
+typedef struct _OwnedMem {
+
+ struct _OwnedMem* Next;
+ void * Ptr; // Point to value
+
+ } OWNEDMEM, *LPOWNEDMEM;
+
+// Suballocator
+
+typedef struct _SubAllocator {
+
+ LPBYTE Block;
+ size_t BlockSize;
+ size_t Used;
+
+ } SUBALLOCATOR, *LPSUBALLOCATOR;
+
+// Table. Each individual table can hold properties and rows & cols
+
+typedef struct _Table {
+
+ int nSamples, nPatches; // Cols, Rows
+ int SampleID; // Pos of ID
+
+ LPKEYVALUE HeaderList; // The properties
+
+ char** DataFormat; // The binary stream descriptor
+ char** Data; // The binary stream
+
+ } TABLE, *LPTABLE;
+
+// File stream being parsed
+
+typedef struct _FileContext {
+ char FileName[MAX_PATH]; // File name if being readed from file
+ FILE* Stream; // File stream or NULL if holded in memory
+ } FILECTX, *LPFILECTX;
+
+// This struct hold all information about an openened
+// IT8 handler. Only one dataset is allowed.
+
+typedef struct {
+
+ char SheetType[MAXSTR];
+
+ int TablesCount; // How many tables in this stream
+ int nTable; // The actual table
+
+ TABLE Tab[MAXTABLES];
+
+ // Memory management
+
+ LPOWNEDMEM MemorySink; // The storage backend
+ SUBALLOCATOR Allocator; // String suballocator -- just to keep it fast
+
+ // Parser state machine
+
+ SYMBOL sy; // Current symbol
+ int ch; // Current character
+
+ int inum; // integer value
+ double dnum; // real value
+ char id[MAXID]; // identifier
+ char str[MAXSTR]; // string
+
+ // Allowed keywords & datasets. They have visibility on whole stream
+
+ LPKEYVALUE ValidKeywords;
+ LPKEYVALUE ValidSampleID;
+
+ char* Source; // Points to loc. being parsed
+ int lineno; // line counter for error reporting
+
+ LPFILECTX FileStack[MAXINCLUDE]; // Stack of files being parsed
+ int IncludeSP; // Include Stack Pointer
+
+ char* MemoryBlock; // The stream if holded in memory
+
+ char DoubleFormatter[MAXID]; // Printf-like 'double' formatter
+
+ } IT8, *LPIT8;
+
+
+
+typedef struct {
+
+ FILE* stream; // For save-to-file behaviour
+
+ LPBYTE Base;
+ LPBYTE Ptr; // For save-to-mem behaviour
+ size_t Used;
+ size_t Max;
+
+ } SAVESTREAM, FAR* LPSAVESTREAM;
+
+
+// ------------------------------------------------------ IT8 parsing routines
+
+
+// A keyword
+typedef struct {
+
+ const char *id;
+ SYMBOL sy;
+
+ } KEYWORD;
+
+// The keyword->symbol translation table. Sorting is required.
+static const KEYWORD TabKeys[] = {
+
+ {"$INCLUDE", SINCLUDE},
+ {".INCLUDE", SINCLUDE},
+ {"BEGIN_DATA", SBEGIN_DATA },
+ {"BEGIN_DATA_FORMAT", SBEGIN_DATA_FORMAT },
+ {"DATA_FORMAT_IDENTIFIER", SDATA_FORMAT_ID},
+ {"END_DATA", SEND_DATA},
+ {"END_DATA_FORMAT", SEND_DATA_FORMAT},
+ {"KEYWORD", SKEYWORD}
+ };
+
+#define NUMKEYS (sizeof(TabKeys)/sizeof(KEYWORD))
+
+// Predefined properties
+
+// A property
+typedef struct {
+ const char *id;
+ WRITEMODE as;
+ } PROPERTY;
+
+static PROPERTY PredefinedProperties[] = {
+
+ {"NUMBER_OF_FIELDS", WRITE_UNCOOKED}, // Required - NUMBER OF FIELDS
+ {"NUMBER_OF_SETS", WRITE_UNCOOKED}, // Required - NUMBER OF SETS
+ {"ORIGINATOR", WRITE_STRINGIFY}, // Required - Identifies the specific system, organization or individual that created the data file.
+ {"FILE_DESCRIPTOR", WRITE_STRINGIFY}, // Required - Describes the purpose or contents of the data file.
+ {"CREATED", WRITE_STRINGIFY}, // Required - Indicates date of creation of the data file.
+ {"DESCRIPTOR", WRITE_STRINGIFY}, // Required - Describes the purpose or contents of the data file.
+ {"DIFFUSE_GEOMETRY", WRITE_STRINGIFY}, // The diffuse geometry used. Allowed values are "sphere" or "opal".
+ {"MANUFACTURER", WRITE_STRINGIFY},
+ {"MANUFACTURE", WRITE_STRINGIFY}, // Some broken Fuji targets does store this value
+ {"PROD_DATE", WRITE_STRINGIFY}, // Identifies year and month of production of the target in the form yyyy:mm.
+ {"SERIAL", WRITE_STRINGIFY}, // Uniquely identifies individual physical target.
+
+ {"MATERIAL", WRITE_STRINGIFY}, // Identifies the material on which the target was produced using a code
+ // uniquely identifying th e material. This is intend ed to be used for IT8.7
+ // physical targets only (i.e . IT8.7/1 a nd IT8.7/2).
+
+ {"INSTRUMENTATION", WRITE_STRINGIFY}, // Used to report the specific instrumentation used (manufacturer and
+ // model number) to generate the data reported. This data will often
+ // provide more information about the particular data collected than an
+ // extensive list of specific details. This is particularly important for
+ // spectral data or data derived from spectrophotometry.
+
+ {"MEASUREMENT_SOURCE", WRITE_STRINGIFY}, // Illumination used for spectral measurements. This data helps provide
+ // a guide to the potential for issues of paper fluorescence, etc.
+
+ {"PRINT_CONDITIONS", WRITE_STRINGIFY}, // Used to define the characteristics of the printed sheet being reported.
+ // Where standard conditions have been defined (e.g., SWOP at nominal)
+ // named conditions may suffice. Otherwise, detailed information is
+ // needed.
+
+ {"SAMPLE_BACKING", WRITE_STRINGIFY}, // Identifies the backing material used behind the sample during
+ // measurement. Allowed values are “black”, “white”, or {"na".
+
+ {"CHISQ_DOF", WRITE_STRINGIFY}, // Degrees of freedom associated with the Chi squared statistic
+
+// new in recent specs:
+ {"MEASUREMENT_GEOMETRY", WRITE_STRINGIFY}, // The type of measurement, either reflection or transmission, should be indicated
+ // along with details of the geometry and the aperture size and shape. For example,
+ // for transmission measurements it is important to identify 0/diffuse, diffuse/0,
+ // opal or integrating sphere, etc. For reflection it is important to identify 0/45,
+ // 45/0, sphere (specular included or excluded), etc.
+
+ {"FILTER", WRITE_STRINGIFY}, // Identifies the use of physical filter(s) during measurement. Typically used to
+ // denote the use of filters such as none, D65, Red, Green or Blue.
+
+ {"POLARIZATION", WRITE_STRINGIFY}, // Identifies the use of a physical polarization filter during measurement. Allowed
+ // values are {"yes”, “white”, “none” or “na”.
+
+ {"WEIGHTING_FUNCTION", WRITE_PAIR}, // Indicates such functions as: the CIE standard observer functions used in the
+ // calculation of various data parameters (2 degree and 10 degree), CIE standard
+ // illuminant functions used in the calculation of various data parameters (e.g., D50,
+ // D65, etc.), density status response, etc. If used there shall be at least one
+ // name-value pair following the WEIGHTING_FUNCTION tag/keyword. The first attribute
+ // in the set shall be {"name" and shall identify the particular parameter used.
+ // The second shall be {"value" and shall provide the value associated with that name.
+ // For ASCII data, a string containing the Name and Value attribute pairs shall follow
+ // the weighting function keyword. A semi-colon separates attribute pairs from each
+ // other and within the attribute the name and value are separated by a comma.
+
+ {"COMPUTATIONAL_PARAMETER", WRITE_PAIR}, // Parameter that is used in computing a value from measured data. Name is the name
+ // of the calculation, parameter is the name of the parameter used in the calculation
+ // and value is the value of the parameter.
+
+ {"TARGET_TYPE", WRITE_STRINGIFY}, // The type of target being measured, e.g. IT8.7/1, IT8.7/3, user defined, etc.
+
+ {"COLORANT", WRITE_STRINGIFY}, // Identifies the colorant(s) used in creating the target.
+
+ {"TABLE_DESCRIPTOR", WRITE_STRINGIFY}, // Describes the purpose or contents of a data table.
+
+ {"TABLE_NAME", WRITE_STRINGIFY} // Provides a short name for a data table.
+};
+
+#define NUMPREDEFINEDPROPS (sizeof(PredefinedProperties)/sizeof(PROPERTY))
+
+
+// Predefined sample types on dataset
+static const char* PredefinedSampleID[] = {
+ "SAMPLE_ID", // Identifies sample that data represents
+ "STRING", // Identifies label, or other non-machine readable value.
+ // Value must begin and end with a " symbol
+
+ "CMYK_C", // Cyan component of CMYK data expressed as a percentage
+ "CMYK_M", // Magenta component of CMYK data expressed as a percentage
+ "CMYK_Y", // Yellow component of CMYK data expressed as a percentage
+ "CMYK_K", // Black component of CMYK data expressed as a percentage
+ "D_RED", // Red filter density
+ "D_GREEN", // Green filter density
+ "D_BLUE", // Blue filter density
+ "D_VIS", // Visual filter density
+ "D_MAJOR_FILTER", // Major filter d ensity
+ "RGB_R", // Red component of RGB data
+ "RGB_G", // Green component of RGB data
+ "RGB_B", // Blue com ponent of RGB data
+ "SPECTRAL_NM", // Wavelength of measurement expressed in nanometers
+ "SPECTRAL_PCT", // Percentage reflectance/transmittance
+ "SPECTRAL_DEC", // Reflectance/transmittance
+ "XYZ_X", // X component of tristimulus data
+ "XYZ_Y", // Y component of tristimulus data
+ "XYZ_Z", // Z component of tristimulus data
+ "XYY_X" // x component of chromaticity data
+ "XYY_Y", // y component of chromaticity data
+ "XYY_CAPY", // Y component of tristimulus data
+ "LAB_L", // L* component of Lab data
+ "LAB_A", // a* component of Lab data
+ "LAB_B", // b* component of Lab data
+ "LAB_C", // C*ab component of Lab data
+ "LAB_H", // hab component of Lab data
+ "LAB_DE", // CIE dE
+ "LAB_DE_94", // CIE dE using CIE 94
+ "LAB_DE_CMC", // dE using CMC
+ "LAB_DE_2000", // CIE dE using CIE DE 2000
+ "MEAN_DE", // Mean Delta E (LAB_DE) of samples compared to batch average
+ // (Used for data files for ANSI IT8.7/1 and IT8.7/2 targets)
+ "STDEV_X", // Standard deviation of X (tristimulus data)
+ "STDEV_Y", // Standard deviation of Y (tristimulus data)
+ "STDEV_Z", // Standard deviation of Z (tristimulus data)
+ "STDEV_L", // Standard deviation of L*
+ "STDEV_A", // Standard deviation of a*
+ "STDEV_B", // Standard deviation of b*
+ "STDEV_DE", // Standard deviation of CIE dE
+ "CHI_SQD_PAR"}; // The average of the standard deviations of L*, a* and b*. It is
+ // used to derive an estimate of the chi-squared parameter which is
+ // recommended as the predictor of the variability of dE
+
+#define NUMPREDEFINEDSAMPLEID (sizeof(PredefinedSampleID)/sizeof(char *))
+
+//Forward declaration of some internal functions
+static
+void* AllocChunk(LPIT8 it8, size_t size);
+
+// Checks if c is a separator
+static
+LCMSBOOL isseparator(int c)
+{
+ return (c == ' ') || (c == '\t') || (c == '\r');
+}
+
+// Checks whatever if c is a valid identifier char
+static
+LCMSBOOL ismiddle(int c)
+{
+ return (!isseparator(c) && (c != '#') && (c !='\"') && (c != '\'') && (c > 32) && (c < 127));
+}
+
+// Checks whatsever if c is a valid identifier middle char.
+static
+LCMSBOOL isidchar(int c)
+{
+ return isalnum(c) || ismiddle(c);
+}
+
+// Checks whatsever if c is a valid identifier first char.
+static
+LCMSBOOL isfirstidchar(int c)
+{
+ return !isdigit(c) && ismiddle(c);
+}
+
+// checks whether the supplied path looks like an absolute path
+// NOTE: this function doesn't checks if the path exists or even if it's legal
+static
+LCMSBOOL isabsolutepath(const char *path)
+{
+ if(path == NULL)
+ return FALSE;
+
+ if(path[0] == DIR_CHAR)
+ return TRUE;
+
+#ifndef NON_WINDOWS
+ if(isalpha(path[0]) && path[1] == ':')
+ return TRUE;
+#endif
+ return FALSE;
+}
+
+// Makes a file path based on a given reference path
+// NOTE: buffer is assumed to point to at least MAX_PATH bytes
+// NOTE: both relPath and basePath are assumed to be no more than MAX_PATH characters long (including the null terminator!)
+// NOTE: this function doesn't check if the path exists or even if it's legal
+static
+LCMSBOOL _cmsMakePath(const char *relPath, const char *basePath, char *buffer)
+{
+ if (!isabsolutepath(relPath)) {
+
+ char *tail;
+
+ strncpy(buffer, basePath, MAX_PATH-1);
+ tail = strrchr(buffer, DIR_CHAR);
+ if (tail != NULL) {
+
+ size_t len = tail - buffer;
+ strncpy(tail + 1, relPath, MAX_PATH - len -1);
+ // TODO: if combined path is longer than MAX_PATH, this should return FALSE!
+ return TRUE;
+ }
+ }
+ strncpy(buffer, relPath, MAX_PATH - 1);
+ buffer[MAX_PATH-1] = 0;
+ return TRUE;
+}
+
+
+// Make sure no exploit is being even tried
+
+static
+const char* NoMeta(const char* str)
+{
+ if (strchr(str, '%') != NULL)
+ return "**** CORRUPTED FORMAT STRING ***";
+
+ return str;
+}
+
+
+// Syntax error
+static
+LCMSBOOL SynError(LPIT8 it8, const char *Txt, ...)
+{
+ char Buffer[256], ErrMsg[1024];
+ va_list args;
+
+ va_start(args, Txt);
+ vsnprintf(Buffer, 255, Txt, args);
+ Buffer[255] = 0;
+ va_end(args);
+
+ snprintf(ErrMsg, 1023, "%s: Line %d, %s", it8->FileStack[it8 ->IncludeSP]->FileName, it8->lineno, Buffer);
+ ErrMsg[1023] = 0;
+ it8->sy = SSYNERROR;
+ cmsSignalError(LCMS_ERRC_ABORTED, "%s", ErrMsg);
+ return FALSE;
+}
+
+// Check if current symbol is same as specified. issue an error else.
+static
+LCMSBOOL Check(LPIT8 it8, SYMBOL sy, const char* Err)
+{
+ if (it8 -> sy != sy)
+ return SynError(it8, NoMeta(Err));
+ return TRUE;
+}
+
+
+
+// Read Next character from stream
+static
+void NextCh(LPIT8 it8)
+{
+ if (it8 -> FileStack[it8 ->IncludeSP]->Stream) {
+
+ it8 ->ch = fgetc(it8 ->FileStack[it8 ->IncludeSP]->Stream);
+
+ if (feof(it8 -> FileStack[it8 ->IncludeSP]->Stream)) {
+
+ if (it8 ->IncludeSP > 0) {
+
+ fclose(it8 ->FileStack[it8->IncludeSP--]->Stream);
+ it8 -> ch = ' '; // Whitespace to be ignored
+
+ } else
+ it8 ->ch = 0; // EOF
+ }
+
+
+
+ }
+ else {
+ it8->ch = *it8->Source;
+ if (it8->ch) it8->Source++;
+ }
+}
+
+
+// Try to see if current identifier is a keyword, if so return the referred symbol
+static
+SYMBOL BinSrchKey(const char *id)
+{
+ int l = 1;
+ int r = NUMKEYS;
+ int x, res;
+
+ while (r >= l)
+ {
+ x = (l+r)/2;
+ res = stricmp(id, TabKeys[x-1].id);
+ if (res == 0) return TabKeys[x-1].sy;
+ if (res < 0) r = x - 1;
+ else l = x + 1;
+ }
+
+ return SNONE;
+}
+
+
+// 10 ^n
+static
+double xpow10(int n)
+{
+ return pow(10, (double) n);
+}
+
+
+// Reads a Real number, tries to follow from integer number
+static
+void ReadReal(LPIT8 it8, int inum)
+{
+ it8->dnum = (double) inum;
+
+ while (isdigit(it8->ch)) {
+
+ it8->dnum = it8->dnum * 10.0 + (it8->ch - '0');
+ NextCh(it8);
+ }
+
+ if (it8->ch == '.') { // Decimal point
+
+ double frac = 0.0; // fraction
+ int prec = 0; // precission
+
+ NextCh(it8); // Eats dec. point
+
+ while (isdigit(it8->ch)) {
+
+ frac = frac * 10.0 + (it8->ch - '0');
+ prec++;
+ NextCh(it8);
+ }
+
+ it8->dnum = it8->dnum + (frac / xpow10(prec));
+ }
+
+ // Exponent, example 34.00E+20
+ if (toupper(it8->ch) == 'E') {
+
+ int e;
+ int sgn;
+
+ NextCh(it8); sgn = 1;
+
+ if (it8->ch == '-') {
+
+ sgn = -1; NextCh(it8);
+ }
+ else
+ if (it8->ch == '+') {
+
+ sgn = +1;
+ NextCh(it8);
+ }
+
+
+ e = 0;
+ while (isdigit(it8->ch)) {
+
+ if ((double) e * 10L < INT_MAX)
+ e = e * 10 + (it8->ch - '0');
+
+ NextCh(it8);
+ }
+
+ e = sgn*e;
+
+ it8 -> dnum = it8 -> dnum * xpow10(e);
+ }
+}
+
+
+
+// Reads next symbol
+static
+void InSymbol(LPIT8 it8)
+{
+ register char *idptr;
+ register int k;
+ SYMBOL key;
+ int sng;
+
+ do {
+
+ while (isseparator(it8->ch))
+ NextCh(it8);
+
+ if (isfirstidchar(it8->ch)) { // Identifier
+
+
+ k = 0;
+ idptr = it8->id;
+
+ do {
+
+ if (++k < MAXID) *idptr++ = (char) it8->ch;
+
+ NextCh(it8);
+
+ } while (isidchar(it8->ch));
+
+ *idptr = '\0';
+
+
+ key = BinSrchKey(it8->id);
+ if (key == SNONE) it8->sy = SIDENT;
+ else it8->sy = key;
+
+ }
+ else // Is a number?
+ if (isdigit(it8->ch) || it8->ch == '.' || it8->ch == '-' || it8->ch == '+')
+ {
+ int sign = 1;
+
+ if (it8->ch == '-') {
+ sign = -1;
+ NextCh(it8);
+ }
+
+ it8->inum = 0;
+ it8->sy = SINUM;
+
+ if (it8->ch == '0') { // 0xnnnn (Hexa) or 0bnnnn (Binary)
+
+ NextCh(it8);
+ if (toupper(it8->ch) == 'X') {
+
+ int j;
+
+ NextCh(it8);
+ while (isxdigit(it8->ch))
+ {
+ it8->ch = toupper(it8->ch);
+ if (it8->ch >= 'A' && it8->ch <= 'F') j = it8->ch -'A'+10;
+ else j = it8->ch - '0';
+
+ if ((long) it8->inum * 16L > (long) INT_MAX)
+ {
+ SynError(it8, "Invalid hexadecimal number");
+ return;
+ }
+
+ it8->inum = it8->inum * 16 + j;
+ NextCh(it8);
+ }
+ return;
+ }
+
+ if (toupper(it8->ch) == 'B') { // Binary
+
+ int j;
+
+ NextCh(it8);
+ while (it8->ch == '0' || it8->ch == '1')
+ {
+ j = it8->ch - '0';
+
+ if ((long) it8->inum * 2L > (long) INT_MAX)
+ {
+ SynError(it8, "Invalid binary number");
+ return;
+ }
+
+ it8->inum = it8->inum * 2 + j;
+ NextCh(it8);
+ }
+ return;
+ }
+ }
+
+
+ while (isdigit(it8->ch)) {
+
+ if ((long) it8->inum * 10L > (long) INT_MAX) {
+ ReadReal(it8, it8->inum);
+ it8->sy = SDNUM;
+ it8->dnum *= sign;
+ return;
+ }
+
+ it8->inum = it8->inum * 10 + (it8->ch - '0');
+ NextCh(it8);
+ }
+
+ if (it8->ch == '.') {
+
+ ReadReal(it8, it8->inum);
+ it8->sy = SDNUM;
+ it8->dnum *= sign;
+ return;
+ }
+
+ it8 -> inum *= sign;
+
+ // Special case. Numbers followed by letters are taken as identifiers
+
+ if (isidchar(it8 ->ch)) {
+
+ if (it8 ->sy == SINUM) {
+
+ sprintf(it8->id, "%d", it8->inum);
+ }
+ else {
+
+ sprintf(it8->id, it8 ->DoubleFormatter, it8->dnum);
+ }
+
+ k = (int) strlen(it8 ->id);
+ idptr = it8 ->id + k;
+ do {
+
+ if (++k < MAXID) *idptr++ = (char) it8->ch;
+
+ NextCh(it8);
+
+ } while (isidchar(it8->ch));
+
+ *idptr = '\0';
+
+ it8->sy = SIDENT;
+ }
+ return;
+
+ }
+ else
+ switch ((int) it8->ch) {
+
+ // EOF marker -- ignore it
+ case '\x1a':
+ NextCh(it8);
+ break;
+
+ // Eof stream markers
+
+ case 0:
+ case -1:
+ it8->sy = SEOF;
+ break;
+
+
+ // Next line
+
+ case '\n':
+ NextCh(it8);
+ it8->sy = SEOLN;
+ it8->lineno++;
+ break;
+
+ // Comment
+
+ case '#':
+ NextCh(it8);
+ while (it8->ch && it8->ch != '\n')
+ NextCh(it8);
+
+ it8->sy = SCOMMENT;
+ break;
+
+ // String.
+
+ case '\'':
+ case '\"':
+ idptr = it8->str;
+ sng = it8->ch;
+ k = 0;
+ NextCh(it8);
+
+ while (k < MAXSTR && it8->ch != sng) {
+
+ if (it8->ch == '\n'|| it8->ch == '\r') k = MAXSTR+1;
+ else {
+ *idptr++ = (char) it8->ch;
+ NextCh(it8);
+ k++;
+ }
+ }
+
+ it8->sy = SSTRING;
+ *idptr = '\0';
+ NextCh(it8);
+ break;
+
+
+ default:
+ SynError(it8, "Unrecognized character: 0x%x", it8 ->ch);
+ return;
+ }
+
+ } while (it8->sy == SCOMMENT);
+
+ // Handle the include special token
+
+ if (it8 -> sy == SINCLUDE) {
+
+ LPFILECTX FileNest;
+
+ if(it8 -> IncludeSP >= (MAXINCLUDE-1))
+ {
+ SynError(it8, "Too many recursion levels");
+ return;
+ }
+
+ InSymbol(it8);
+ if (!Check(it8, SSTRING, "Filename expected")) return;
+
+ FileNest = it8 -> FileStack[it8 -> IncludeSP + 1];
+ if(FileNest == NULL)
+ {
+ FileNest = it8 ->FileStack[it8 -> IncludeSP + 1] = (LPFILECTX)AllocChunk(it8, sizeof(FILECTX));
+ //if(FileNest == NULL)
+ // TODO: how to manage out-of-memory conditions?
+ }
+
+ if(_cmsMakePath(it8->str, it8->FileStack[it8->IncludeSP]->FileName, FileNest->FileName) == FALSE)
+ {
+ SynError(it8, "File path too long");
+ return;
+ }
+
+ FileNest->Stream = fopen(FileNest->FileName, "rt");
+ if (FileNest->Stream == NULL) {
+
+ SynError(it8, "File %s not found", FileNest->FileName);
+ return;
+ }
+ it8->IncludeSP++;
+
+ it8 ->ch = ' ';
+ InSymbol(it8);
+ }
+
+}
+
+// Checks end of line separator
+static
+LCMSBOOL CheckEOLN(LPIT8 it8)
+{
+ if (!Check(it8, SEOLN, "Expected separator")) return FALSE;
+ while (it8 -> sy == SEOLN)
+ InSymbol(it8);
+ return TRUE;
+
+}
+
+// Skip a symbol
+
+static
+void Skip(LPIT8 it8, SYMBOL sy)
+{
+ if (it8->sy == sy && it8->sy != SEOF)
+ InSymbol(it8);
+}
+
+
+// Skip multiple EOLN
+static
+void SkipEOLN(LPIT8 it8)
+{
+ while (it8->sy == SEOLN) {
+ InSymbol(it8);
+ }
+}
+
+
+// Returns a string holding current value
+static
+LCMSBOOL GetVal(LPIT8 it8, char* Buffer, size_t max, const char* ErrorTitle)
+{
+ switch (it8->sy) {
+
+ case SIDENT: strncpy(Buffer, it8->id, max);
+ Buffer[max-1]=0;
+ break;
+ case SINUM: snprintf(Buffer, max, "%d", it8 -> inum); break;
+ case SDNUM: snprintf(Buffer, max, it8->DoubleFormatter, it8 -> dnum); break;
+ case SSTRING: strncpy(Buffer, it8->str, max);
+ Buffer[max-1] = 0;
+ break;
+
+
+ default:
+ return SynError(it8, "%s", ErrorTitle);
+ }
+
+ Buffer[max] = 0;
+ return TRUE;
+}
+
+// ---------------------------------------------------------- Table
+
+static
+LPTABLE GetTable(LPIT8 it8)
+{
+ if ((it8 -> nTable >= it8 ->TablesCount) || (it8 -> nTable < 0)) {
+
+ SynError(it8, "Table %d out of sequence", it8 -> nTable);
+ return it8 -> Tab;
+ }
+
+ return it8 ->Tab + it8 ->nTable;
+}
+
+// ---------------------------------------------------------- Memory management
+
+
+
+// Frees an allocator and owned memory
+void LCMSEXPORT cmsIT8Free(LCMSHANDLE hIT8)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+
+ if (it8 == NULL)
+ return;
+
+
+ if (it8->MemorySink) {
+
+ LPOWNEDMEM p;
+ LPOWNEDMEM n;
+
+ for (p = it8->MemorySink; p != NULL; p = n) {
+
+ n = p->Next;
+ if (p->Ptr) _cmsFree(p->Ptr);
+ _cmsFree(p);
+ }
+ }
+
+ if (it8->MemoryBlock)
+ _cmsFree(it8->MemoryBlock);
+
+ _cmsFree(it8);
+}
+
+
+// Allocates a chunk of data, keep linked list
+static
+void* AllocBigBlock(LPIT8 it8, size_t size)
+{
+ LPOWNEDMEM ptr1;
+ void* ptr = _cmsMalloc(size);
+
+ if (ptr) {
+
+ ZeroMemory(ptr, size);
+ ptr1 = (LPOWNEDMEM) _cmsMalloc(sizeof(OWNEDMEM));
+
+ if (ptr1 == NULL) {
+
+ _cmsFree(ptr);
+ return NULL;
+ }
+
+ ZeroMemory(ptr1, sizeof(OWNEDMEM));
+
+ ptr1-> Ptr = ptr;
+ ptr1-> Next = it8 -> MemorySink;
+ it8 -> MemorySink = ptr1;
+ }
+
+ return ptr;
+}
+
+
+// Suballocator.
+static
+void* AllocChunk(LPIT8 it8, size_t size)
+{
+ size_t free = it8 ->Allocator.BlockSize - it8 ->Allocator.Used;
+ LPBYTE ptr;
+
+ size = ALIGNLONG(size);
+
+ if (size > free) {
+
+ if (it8 -> Allocator.BlockSize == 0)
+
+ it8 -> Allocator.BlockSize = 20*1024;
+ else
+ it8 ->Allocator.BlockSize *= 2;
+
+ if (it8 ->Allocator.BlockSize < size)
+ it8 ->Allocator.BlockSize = size;
+
+ it8 ->Allocator.Used = 0;
+ it8 ->Allocator.Block = (LPBYTE) AllocBigBlock(it8, it8 ->Allocator.BlockSize);
+ }
+
+ ptr = it8 ->Allocator.Block + it8 ->Allocator.Used;
+ it8 ->Allocator.Used += size;
+
+ return (void*) ptr;
+
+}
+
+
+// Allocates a string
+static
+char *AllocString(LPIT8 it8, const char* str)
+{
+ size_t Size = strlen(str)+1;
+ char *ptr;
+
+
+ ptr = (char *) AllocChunk(it8, Size);
+ if (ptr) strncpy (ptr, str, Size-1);
+
+ return ptr;
+}
+
+// Searches through linked list
+
+static
+LCMSBOOL IsAvailableOnList(LPKEYVALUE p, const char* Key, const char* Subkey, LPKEYVALUE* LastPtr)
+{
+ if (LastPtr) *LastPtr = p;
+
+ for (; p != NULL; p = p->Next) {
+
+ if (LastPtr) *LastPtr = p;
+
+ if (*Key != '#') { // Comments are ignored
+
+ if (stricmp(Key, p->Keyword) == 0)
+ break;
+ }
+ }
+
+ if (p == NULL)
+ return FALSE;
+
+ if (Subkey == 0)
+ return TRUE;
+
+ for (; p != NULL; p = p->NextSubkey) {
+
+ if (LastPtr) *LastPtr = p;
+
+ if (stricmp(Subkey, p->Subkey) == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+
+// Add a property into a linked list
+static
+LPKEYVALUE AddToList(LPIT8 it8, LPKEYVALUE* Head, const char *Key, const char *Subkey, const char* xValue, WRITEMODE WriteAs)
+{
+ LPKEYVALUE p;
+
+ // Check if property is already in list (this is an error)
+
+ if (IsAvailableOnList(*Head, Key, Subkey, &p)) {
+
+ // This may work for editing properties
+
+ // return SynError(it8, "duplicate key <%s>", Key);
+ }
+ else {
+ LPKEYVALUE last = p;
+
+ // Allocate the container
+ p = (LPKEYVALUE) AllocChunk(it8, sizeof(KEYVALUE));
+ if (p == NULL)
+ {
+ SynError(it8, "AddToList: out of memory");
+ return NULL;
+ }
+
+ // Store name and value
+ p->Keyword = AllocString(it8, Key);
+ p->Subkey = (Subkey == NULL) ? NULL : AllocString(it8, Subkey);
+
+ // Keep the container in our list
+ if (*Head == NULL)
+ *Head = p;
+ else
+ {
+ if(Subkey != 0 && last != 0) {
+ last->NextSubkey = p;
+
+ // If Subkey is not null, then last is the last property with the same key,
+ // but not necessarily is the last property in the list, so we need to move
+ // to the actual list end
+ while(last->Next != 0)
+ last = last->Next;
+ }
+ last->Next = p;
+ }
+
+ p->Next = NULL;
+ p->NextSubkey = NULL;
+ }
+
+ p->WriteAs = WriteAs;
+ if (xValue != NULL) {
+
+ p->Value = AllocString(it8, xValue);
+ }
+ else {
+ p->Value = NULL;
+ }
+
+ return p;
+}
+
+static
+LPKEYVALUE AddAvailableProperty(LPIT8 it8, const char* Key, WRITEMODE as)
+{
+ return AddToList(it8, &it8->ValidKeywords, Key, NULL, NULL, as);
+}
+
+
+static
+LPKEYVALUE AddAvailableSampleID(LPIT8 it8, const char* Key)
+{
+ return AddToList(it8, &it8->ValidSampleID, Key, NULL, NULL, WRITE_UNCOOKED);
+}
+
+
+static
+void AllocTable(LPIT8 it8)
+{
+ LPTABLE t;
+
+ t = it8 ->Tab + it8 ->TablesCount;
+
+ t->HeaderList = NULL;
+ t->DataFormat = NULL;
+ t->Data = NULL;
+
+ it8 ->TablesCount++;
+}
+
+
+int LCMSEXPORT cmsIT8SetTable(LCMSHANDLE IT8, int nTable)
+{
+ LPIT8 it8 = (LPIT8) IT8;
+
+ if (nTable >= it8 ->TablesCount) {
+
+ if (nTable == it8 ->TablesCount) {
+
+ AllocTable(it8);
+ }
+ else {
+ SynError(it8, "Table %d is out of sequence", nTable);
+ return -1;
+ }
+ }
+
+ it8 ->nTable = nTable;
+
+ return nTable;
+}
+
+
+
+// Init an empty container
+LCMSHANDLE LCMSEXPORT cmsIT8Alloc(void)
+{
+ LPIT8 it8;
+ int i;
+
+ it8 = (LPIT8) malloc(sizeof(IT8));
+ if (it8 == NULL) return NULL;
+
+ ZeroMemory(it8, sizeof(IT8));
+
+ AllocTable(it8);
+
+ it8->MemoryBlock = NULL;
+ it8->MemorySink = NULL;
+
+ it8 ->nTable = 0;
+
+ it8->Allocator.Used = 0;
+ it8->Allocator.Block = NULL;
+ it8->Allocator.BlockSize = 0;
+
+ it8->ValidKeywords = NULL;
+ it8->ValidSampleID = NULL;
+
+ it8 -> sy = SNONE;
+ it8 -> ch = ' ';
+ it8 -> Source = NULL;
+ it8 -> inum = 0;
+ it8 -> dnum = 0.0;
+
+ it8->FileStack[0] = (LPFILECTX)AllocChunk(it8, sizeof(FILECTX));
+ it8->IncludeSP = 0;
+ it8 -> lineno = 1;
+
+ strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT);
+ strcpy(it8->SheetType, "CGATS.17");
+
+ // Initialize predefined properties & data
+
+ for (i=0; i < NUMPREDEFINEDPROPS; i++)
+ AddAvailableProperty(it8, PredefinedProperties[i].id, PredefinedProperties[i].as);
+
+ for (i=0; i < NUMPREDEFINEDSAMPLEID; i++)
+ AddAvailableSampleID(it8, PredefinedSampleID[i]);
+
+
+ return (LCMSHANDLE) it8;
+}
+
+
+const char* LCMSEXPORT cmsIT8GetSheetType(LCMSHANDLE hIT8)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+
+ return it8 ->SheetType;
+
+}
+
+LCMSBOOL LCMSEXPORT cmsIT8SetSheetType(LCMSHANDLE hIT8, const char* Type)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+
+ strncpy(it8 ->SheetType, Type, MAXSTR-1);
+ it8 ->SheetType[MAXSTR-1] = 0;
+ return TRUE;
+}
+
+LCMSBOOL LCMSEXPORT cmsIT8SetComment(LCMSHANDLE hIT8, const char* Val)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+
+ if (!Val) return FALSE;
+ if (!*Val) return FALSE;
+
+ return AddToList(it8, &GetTable(it8)->HeaderList, "# ", NULL, Val, WRITE_UNCOOKED) != NULL;
+}
+
+
+
+// Sets a property
+LCMSBOOL LCMSEXPORT cmsIT8SetPropertyStr(LCMSHANDLE hIT8, const char* Key, const char *Val)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+
+ if (!Val) return FALSE;
+ if (!*Val) return FALSE;
+
+ return AddToList(it8, &GetTable(it8)->HeaderList, Key, NULL, Val, WRITE_STRINGIFY) != NULL;
+}
+
+
+LCMSBOOL LCMSEXPORT cmsIT8SetPropertyDbl(LCMSHANDLE hIT8, const char* cProp, double Val)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+ char Buffer[1024];
+
+ sprintf(Buffer, it8->DoubleFormatter, Val);
+
+ return AddToList(it8, &GetTable(it8)->HeaderList, cProp, NULL, Buffer, WRITE_UNCOOKED) != NULL;
+}
+
+LCMSBOOL LCMSEXPORT cmsIT8SetPropertyHex(LCMSHANDLE hIT8, const char* cProp, int Val)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+ char Buffer[1024];
+
+ sprintf(Buffer, "%d", Val);
+
+ return AddToList(it8, &GetTable(it8)->HeaderList, cProp, NULL, Buffer, WRITE_HEXADECIMAL) != NULL;
+}
+
+LCMSBOOL LCMSEXPORT cmsIT8SetPropertyUncooked(LCMSHANDLE hIT8, const char* Key, const char* Buffer)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+
+ return AddToList(it8, &GetTable(it8)->HeaderList, Key, NULL, Buffer, WRITE_UNCOOKED) != NULL;
+}
+
+LCMSBOOL LCMSEXPORT cmsIT8SetPropertyMulti(LCMSHANDLE hIT8, const char* Key, const char* SubKey, const char *Buffer)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+
+ return AddToList(it8, &GetTable(it8)->HeaderList, Key, SubKey, Buffer, WRITE_PAIR) != NULL;
+}
+
+// Gets a property
+const char* LCMSEXPORT cmsIT8GetProperty(LCMSHANDLE hIT8, const char* Key)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+ LPKEYVALUE p;
+
+ if (IsAvailableOnList(GetTable(it8) -> HeaderList, Key, NULL, &p))
+ {
+ return p -> Value;
+ }
+ return NULL;
+}
+
+
+double LCMSEXPORT cmsIT8GetPropertyDbl(LCMSHANDLE hIT8, const char* cProp)
+{
+ const char *v = cmsIT8GetProperty(hIT8, cProp);
+
+ if (v) return atof(v);
+ else return 0.0;
+}
+
+const char* LCMSEXPORT cmsIT8GetPropertyMulti(LCMSHANDLE hIT8, const char* Key, const char *SubKey)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+ LPKEYVALUE p;
+
+ if (IsAvailableOnList(GetTable(it8) -> HeaderList, Key, SubKey, &p))
+ {
+ return p -> Value;
+ }
+ return NULL;
+}
+
+// ----------------------------------------------------------------- Datasets
+
+
+static
+void AllocateDataFormat(LPIT8 it8)
+{
+ LPTABLE t = GetTable(it8);
+
+ if (t -> DataFormat) return; // Already allocated
+
+ t -> nSamples = (int) cmsIT8GetPropertyDbl(it8, "NUMBER_OF_FIELDS");
+
+ if (t -> nSamples <= 0) {
+
+ SynError(it8, "AllocateDataFormat: Unknown NUMBER_OF_FIELDS");
+ t -> nSamples = 10;
+ }
+
+ t -> DataFormat = (char**) AllocChunk (it8, (t->nSamples + 1) * sizeof(char *));
+ if (t->DataFormat == NULL)
+ {
+ SynError(it8, "AllocateDataFormat: Unable to allocate dataFormat array");
+ }
+
+}
+
+static
+const char *GetDataFormat(LPIT8 it8, int n)
+{
+ LPTABLE t = GetTable(it8);
+
+ if (t->DataFormat)
+ return t->DataFormat[n];
+
+ return NULL;
+}
+
+static
+LCMSBOOL SetDataFormat(LPIT8 it8, int n, const char *label)
+{
+ LPTABLE t = GetTable(it8);
+
+#ifdef STRICT_CGATS
+ if (!IsAvailableOnList(it8-> ValidSampleID, label, NULL, NULL)) {
+ SynError(it8, "Invalid data format '%s'.", label);
+ return FALSE;
+ }
+#endif
+
+ if (!t->DataFormat)
+ AllocateDataFormat(it8);
+
+ if (n > t -> nSamples) {
+ SynError(it8, "More than NUMBER_OF_FIELDS fields.");
+ return FALSE;
+ }
+
+
+ if (t->DataFormat) {
+ t->DataFormat[n] = AllocString(it8, label);
+ }
+
+ return TRUE;
+}
+
+
+LCMSBOOL LCMSEXPORT cmsIT8SetDataFormat(LCMSHANDLE h, int n, const char *Sample)
+{
+ LPIT8 it8 = (LPIT8) h;
+ return SetDataFormat(it8, n, Sample);
+}
+
+static
+void AllocateDataSet(LPIT8 it8)
+{
+ LPTABLE t = GetTable(it8);
+
+ if (t -> Data) return; // Already allocated
+
+ t-> nSamples = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
+ t-> nPatches = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS"));
+
+ t-> Data = (char**)AllocChunk (it8, (t->nSamples + 1) * (t->nPatches + 1) *sizeof (char*));
+ if (t->Data == NULL)
+ {
+ SynError(it8, "AllocateDataSet: Unable to allocate data array");
+ }
+
+}
+
+static
+char* GetData(LPIT8 it8, int nSet, int nField)
+{
+ LPTABLE t = GetTable(it8);
+ int nSamples = t -> nSamples;
+ int nPatches = t -> nPatches;
+
+
+ if (nSet >= nPatches || nField >= nSamples)
+ return NULL;
+
+ if (!t->Data) return NULL;
+ return t->Data [nSet * nSamples + nField];
+}
+
+static
+LCMSBOOL SetData(LPIT8 it8, int nSet, int nField, const char *Val)
+{
+ LPTABLE t = GetTable(it8);
+
+ if (!t->Data)
+ AllocateDataSet(it8);
+
+ if (!t->Data) return FALSE;
+
+
+
+ if (nSet > t -> nPatches || nSet < 0) {
+
+ return SynError(it8, "Patch %d out of range, there are %d patches", nSet, t -> nPatches);
+ }
+
+ if (nField > t ->nSamples || nField < 0) {
+ return SynError(it8, "Sample %d out of range, there are %d samples", nField, t ->nSamples);
+
+ }
+
+
+ t->Data [nSet * t -> nSamples + nField] = AllocString(it8, Val);
+ return TRUE;
+}
+
+
+// --------------------------------------------------------------- File I/O
+
+
+// Writes a string to file
+static
+void WriteStr(LPSAVESTREAM f, const char *str)
+{
+
+ size_t len;
+
+ if (str == NULL)
+ str = " ";
+
+ // Lenghth to write
+ len = strlen(str);
+ f ->Used += len;
+
+
+ if (f ->stream) { // Should I write it to a file?
+
+ fwrite(str, 1, len, f->stream);
+
+ }
+ else { // Or to a memory block?
+
+
+ if (f ->Base) { // Am I just counting the bytes?
+
+ if (f ->Used > f ->Max) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "Write to memory overflows in CGATS parser");
+ return;
+ }
+
+ CopyMemory(f ->Ptr, str, len);
+ f->Ptr += len;
+
+ }
+
+ }
+}
+
+
+// Write formatted
+
+static
+void Writef(LPSAVESTREAM f, const char* frm, ...)
+{
+ char Buffer[4096];
+ va_list args;
+
+ va_start(args, frm);
+ vsnprintf(Buffer, 4095, frm, args);
+ Buffer[4095] = 0;
+ WriteStr(f, Buffer);
+ va_end(args);
+
+}
+
+// Writes full header
+static
+void WriteHeader(LPIT8 it8, LPSAVESTREAM fp)
+{
+ LPKEYVALUE p;
+ LPTABLE t = GetTable(it8);
+
+
+ for (p = t->HeaderList; (p != NULL); p = p->Next)
+ {
+ if (*p ->Keyword == '#') {
+
+ char* Pt;
+
+ WriteStr(fp, "#\n# ");
+ for (Pt = p ->Value; *Pt; Pt++) {
+
+
+ Writef(fp, "%c", *Pt);
+
+ if (*Pt == '\n') {
+ WriteStr(fp, "# ");
+ }
+ }
+
+ WriteStr(fp, "\n#\n");
+ continue;
+ }
+
+
+ if (!IsAvailableOnList(it8-> ValidKeywords, p->Keyword, NULL, NULL)) {
+
+#ifdef STRICT_CGATS
+ WriteStr(fp, "KEYWORD\t\"");
+ WriteStr(fp, p->Keyword);
+ WriteStr(fp, "\"\n");
+#endif
+
+ AddAvailableProperty(it8, p->Keyword, WRITE_UNCOOKED);
+
+ }
+
+ WriteStr(fp, p->Keyword);
+ if (p->Value) {
+
+ switch (p ->WriteAs) {
+
+ case WRITE_UNCOOKED:
+ Writef(fp, "\t%s", p ->Value);
+ break;
+
+ case WRITE_STRINGIFY:
+ Writef(fp, "\t\"%s\"", p->Value );
+ break;
+
+ case WRITE_HEXADECIMAL:
+ Writef(fp, "\t0x%X", atoi(p ->Value));
+ break;
+
+ case WRITE_BINARY:
+ Writef(fp, "\t0x%B", atoi(p ->Value));
+ break;
+
+ case WRITE_PAIR:
+ Writef(fp, "\t\"%s,%s\"", p->Subkey, p->Value);
+ break;
+
+ default: SynError(it8, "Unknown write mode %d", p ->WriteAs);
+ return;
+ }
+ }
+
+ WriteStr (fp, "\n");
+ }
+
+}
+
+
+// Writes the data format
+static
+void WriteDataFormat(LPSAVESTREAM fp, LPIT8 it8)
+{
+ int i, nSamples;
+ LPTABLE t = GetTable(it8);
+
+ if (!t -> DataFormat) return;
+
+ WriteStr(fp, "BEGIN_DATA_FORMAT\n");
+ WriteStr(fp, " ");
+ nSamples = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
+
+ for (i = 0; i < nSamples; i++) {
+
+ WriteStr(fp, t->DataFormat[i]);
+ WriteStr(fp, ((i == (nSamples-1)) ? "\n" : "\t"));
+ }
+
+ WriteStr (fp, "END_DATA_FORMAT\n");
+}
+
+
+// Writes data array
+static
+void WriteData(LPSAVESTREAM fp, LPIT8 it8)
+{
+ int i, j;
+ LPTABLE t = GetTable(it8);
+
+ if (!t->Data) return;
+
+ WriteStr (fp, "BEGIN_DATA\n");
+
+ t->nPatches = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS"));
+
+ for (i = 0; i < t-> nPatches; i++) {
+
+ WriteStr(fp, " ");
+
+ for (j = 0; j < t->nSamples; j++) {
+
+ char *ptr = t->Data[i*t->nSamples+j];
+
+ if (ptr == NULL) WriteStr(fp, "\"\"");
+ else {
+ // If value contains whitespace, enclose within quote
+
+ if (strchr(ptr, ' ') != NULL) {
+
+ WriteStr(fp, "\"");
+ WriteStr(fp, ptr);
+ WriteStr(fp, "\"");
+ }
+ else
+ WriteStr(fp, ptr);
+ }
+
+ WriteStr(fp, ((j == (t->nSamples-1)) ? "\n" : "\t"));
+ }
+ }
+ WriteStr (fp, "END_DATA\n");
+}
+
+
+
+// Saves whole file
+LCMSBOOL LCMSEXPORT cmsIT8SaveToFile(LCMSHANDLE hIT8, const char* cFileName)
+{
+ SAVESTREAM sd;
+ int i;
+ LPIT8 it8 = (LPIT8) hIT8;
+
+ ZeroMemory(&sd, sizeof(SAVESTREAM));
+
+ sd.stream = fopen(cFileName, "wt");
+ if (!sd.stream) return FALSE;
+
+ WriteStr(&sd, it8->SheetType);
+ WriteStr(&sd, "\n");
+ for (i=0; i < it8 ->TablesCount; i++) {
+
+ cmsIT8SetTable(hIT8, i);
+ WriteHeader(it8, &sd);
+ WriteDataFormat(&sd, it8);
+ WriteData(&sd, it8);
+ }
+
+ fclose(sd.stream);
+
+ return TRUE;
+}
+
+
+// Saves to memory
+LCMSBOOL LCMSEXPORT cmsIT8SaveToMem(LCMSHANDLE hIT8, void *MemPtr, size_t* BytesNeeded)
+{
+ SAVESTREAM sd;
+ int i;
+ LPIT8 it8 = (LPIT8) hIT8;
+
+ ZeroMemory(&sd, sizeof(SAVESTREAM));
+
+ sd.stream = NULL;
+ sd.Base = (LPBYTE) MemPtr;
+ sd.Ptr = sd.Base;
+
+ sd.Used = 0;
+
+ if (sd.Base)
+ sd.Max = *BytesNeeded; // Write to memory?
+ else
+ sd.Max = 0; // Just counting the needed bytes
+
+ WriteStr(&sd, it8->SheetType);
+ WriteStr(&sd, "\n");
+ for (i=0; i < it8 ->TablesCount; i++) {
+
+ cmsIT8SetTable(hIT8, i);
+ WriteHeader(it8, &sd);
+ WriteDataFormat(&sd, it8);
+ WriteData(&sd, it8);
+ }
+
+ sd.Used++; // The \0 at the very end
+
+ if (sd.Base)
+ sd.Ptr = 0;
+
+ *BytesNeeded = sd.Used;
+
+ return TRUE;
+}
+
+
+// -------------------------------------------------------------- Higer level parsing
+
+static
+LCMSBOOL DataFormatSection(LPIT8 it8)
+{
+ int iField = 0;
+ LPTABLE t = GetTable(it8);
+
+ InSymbol(it8); // Eats "BEGIN_DATA_FORMAT"
+ CheckEOLN(it8);
+
+ while (it8->sy != SEND_DATA_FORMAT &&
+ it8->sy != SEOLN &&
+ it8->sy != SEOF &&
+ it8->sy != SSYNERROR) {
+
+ if (it8->sy != SIDENT) {
+
+ return SynError(it8, "Sample type expected");
+ }
+
+ if (!SetDataFormat(it8, iField, it8->id)) return FALSE;
+ iField++;
+
+ InSymbol(it8);
+ SkipEOLN(it8);
+ }
+
+ SkipEOLN(it8);
+ Skip(it8, SEND_DATA_FORMAT);
+ SkipEOLN(it8);
+
+ if (iField != t ->nSamples) {
+ SynError(it8, "Count mismatch. NUMBER_OF_FIELDS was %d, found %d\n", t ->nSamples, iField);
+
+
+ }
+
+ return TRUE;
+}
+
+
+
+static
+LCMSBOOL DataSection (LPIT8 it8)
+{
+ int iField = 0;
+ int iSet = 0;
+ char Buffer[MAXSTR];
+ LPTABLE t = GetTable(it8);
+
+ InSymbol(it8); // Eats "BEGIN_DATA"
+ CheckEOLN(it8);
+
+ if (!t->Data)
+ AllocateDataSet(it8);
+
+ while (it8->sy != SEND_DATA && it8->sy != SEOF)
+ {
+ if (iField >= t -> nSamples) {
+ iField = 0;
+ iSet++;
+
+ }
+
+ if (it8->sy != SEND_DATA && it8->sy != SEOF) {
+
+ if (!GetVal(it8, Buffer, 255, "Sample data expected"))
+ return FALSE;
+
+ if (!SetData(it8, iSet, iField, Buffer))
+ return FALSE;
+
+ iField++;
+
+ InSymbol(it8);
+ SkipEOLN(it8);
+ }
+ }
+
+ SkipEOLN(it8);
+ Skip(it8, SEND_DATA);
+ SkipEOLN(it8);
+
+ // Check for data completion.
+
+ if ((iSet+1) != t -> nPatches)
+ return SynError(it8, "Count mismatch. NUMBER_OF_SETS was %d, found %d\n", t ->nPatches, iSet+1);
+
+ return TRUE;
+}
+
+
+
+
+static
+LCMSBOOL HeaderSection(LPIT8 it8)
+{
+ char VarName[MAXID];
+ char Buffer[MAXSTR];
+ LPKEYVALUE Key;
+
+ while (it8->sy != SEOF &&
+ it8->sy != SSYNERROR &&
+ it8->sy != SBEGIN_DATA_FORMAT &&
+ it8->sy != SBEGIN_DATA) {
+
+
+ switch (it8 -> sy) {
+
+ case SKEYWORD:
+ InSymbol(it8);
+ if (!GetVal(it8, Buffer, MAXSTR-1, "Keyword expected")) return FALSE;
+ if (!AddAvailableProperty(it8, Buffer, WRITE_UNCOOKED)) return FALSE;
+ InSymbol(it8);
+ break;
+
+
+ case SDATA_FORMAT_ID:
+ InSymbol(it8);
+ if (!GetVal(it8, Buffer, MAXSTR-1, "Keyword expected")) return FALSE;
+ if (!AddAvailableSampleID(it8, Buffer)) return FALSE;
+ InSymbol(it8);
+ break;
+
+
+ case SIDENT:
+ strncpy(VarName, it8->id, MAXID-1);
+ VarName[MAXID-1] = 0;
+
+ if (!IsAvailableOnList(it8-> ValidKeywords, VarName, NULL, &Key)) {
+
+#ifdef STRICT_CGATS
+ return SynError(it8, "Undefined keyword '%s'", VarName);
+#else
+ Key = AddAvailableProperty(it8, VarName, WRITE_UNCOOKED);
+ if (Key == NULL) return FALSE;
+#endif
+ }
+
+ InSymbol(it8);
+ if (!GetVal(it8, Buffer, MAXSTR-1, "Property data expected")) return FALSE;
+
+ if(Key->WriteAs != WRITE_PAIR) {
+ AddToList(it8, &GetTable(it8)->HeaderList, VarName, NULL, Buffer,
+ (it8->sy == SSTRING) ? WRITE_STRINGIFY : WRITE_UNCOOKED);
+ }
+ else {
+ const char *Subkey;
+ char *Nextkey;
+ if (it8->sy != SSTRING)
+ return SynError(it8, "Invalid value '%s' for property '%s'.", Buffer, VarName);
+
+ // chop the string as a list of "subkey, value" pairs, using ';' as a separator
+ for(Subkey = Buffer; Subkey != NULL; Subkey = Nextkey)
+ {
+ char *Value, *temp;
+
+ // identify token pair boundary
+ Nextkey = (char*) strchr(Subkey, ';');
+ if(Nextkey)
+ *Nextkey++ = '\0';
+
+ // for each pair, split the subkey and the value
+ Value = (char*) strrchr(Subkey, ',');
+ if(Value == NULL)
+ return SynError(it8, "Invalid value for property '%s'.", VarName);
+
+ // gobble the spaces before the coma, and the coma itself
+ temp = Value++;
+ do *temp-- = '\0'; while(temp >= Subkey && *temp == ' ');
+
+ // gobble any space at the right
+ temp = Value + strlen(Value) - 1;
+ while(*temp == ' ') *temp-- = '\0';
+
+ // trim the strings from the left
+ Subkey += strspn(Subkey, " ");
+ Value += strspn(Value, " ");
+
+ if(Subkey[0] == 0 || Value[0] == 0)
+ return SynError(it8, "Invalid value for property '%s'.", VarName);
+ AddToList(it8, &GetTable(it8)->HeaderList, VarName, Subkey, Value, WRITE_PAIR);
+ }
+ }
+
+ InSymbol(it8);
+ break;
+
+
+ case SEOLN: break;
+
+ default:
+ return SynError(it8, "expected keyword or identifier");
+ }
+
+ SkipEOLN(it8);
+ }
+
+ return TRUE;
+
+}
+
+
+static
+LCMSBOOL ParseIT8(LPIT8 it8, LCMSBOOL nosheet)
+{
+ char* SheetTypePtr = it8 ->SheetType;
+
+ if (nosheet == 0) {
+
+ // First line is a very special case.
+
+ while (isseparator(it8->ch))
+ NextCh(it8);
+
+ while (it8->ch != '\r' && it8 ->ch != '\n' && it8->ch != '\t' && it8 -> ch != -1) {
+
+ *SheetTypePtr++= (char) it8 ->ch;
+ NextCh(it8);
+ }
+ }
+
+ *SheetTypePtr = 0;
+ InSymbol(it8);
+
+ SkipEOLN(it8);
+
+ while (it8-> sy != SEOF &&
+ it8-> sy != SSYNERROR) {
+
+ switch (it8 -> sy) {
+
+ case SBEGIN_DATA_FORMAT:
+ if (!DataFormatSection(it8)) return FALSE;
+ break;
+
+ case SBEGIN_DATA:
+
+ if (!DataSection(it8)) return FALSE;
+
+ if (it8 -> sy != SEOF) {
+
+ AllocTable(it8);
+ it8 ->nTable = it8 ->TablesCount - 1;
+ }
+ break;
+
+ case SEOLN:
+ SkipEOLN(it8);
+ break;
+
+ default:
+ if (!HeaderSection(it8)) return FALSE;
+ }
+
+ }
+
+ return (it8 -> sy != SSYNERROR);
+}
+
+
+
+// Init usefull pointers
+
+static
+void CookPointers(LPIT8 it8)
+{
+ int idField, i;
+ char* Fld;
+ int j;
+ int nOldTable = it8 ->nTable;
+
+ for (j=0; j < it8 ->TablesCount; j++) {
+
+ LPTABLE t = it8 ->Tab + j;
+
+ t -> SampleID = 0;
+ it8 ->nTable = j;
+
+ for (idField = 0; idField < t -> nSamples; idField++)
+ {
+ if (t ->DataFormat == NULL) {
+ SynError(it8, "Undefined DATA_FORMAT");
+ return;
+
+ }
+
+ Fld = t->DataFormat[idField];
+ if (!Fld) continue;
+
+
+ if (stricmp(Fld, "SAMPLE_ID") == 0) {
+
+ t -> SampleID = idField;
+
+ for (i=0; i < t -> nPatches; i++) {
+
+ char *Data = GetData(it8, i, idField);
+ if (Data) {
+ char Buffer[256];
+
+ strncpy(Buffer, Data, 255);
+ Buffer[255] = 0;
+
+ if (strlen(Buffer) <= strlen(Data))
+ strcpy(Data, Buffer);
+ else
+ SetData(it8, i, idField, Buffer);
+
+ }
+ }
+
+ }
+
+ // "LABEL" is an extension. It keeps references to forward tables
+
+ if ((stricmp(Fld, "LABEL") == 0) || Fld[0] == '$' ) {
+
+ // Search for table references...
+ for (i=0; i < t -> nPatches; i++) {
+
+ char *Label = GetData(it8, i, idField);
+
+ if (Label) {
+
+ int k;
+
+ // This is the label, search for a table containing
+ // this property
+
+ for (k=0; k < it8 ->TablesCount; k++) {
+
+ LPTABLE Table = it8 ->Tab + k;
+ LPKEYVALUE p;
+
+ if (IsAvailableOnList(Table->HeaderList, Label, NULL, &p)) {
+
+ // Available, keep type and table
+ char Buffer[256];
+
+ char *Type = p ->Value;
+ int nTable = k;
+
+ snprintf(Buffer, 255, "%s %d %s", Label, nTable, Type );
+
+ SetData(it8, i, idField, Buffer);
+ }
+ }
+
+
+ }
+
+ }
+
+
+ }
+
+ }
+ }
+
+ it8 ->nTable = nOldTable;
+}
+
+// Try to infere if the file is a CGATS/IT8 file at all. Read first line
+// that should be something like some printable characters plus a \n
+
+static
+int IsMyBlock(LPBYTE Buffer, size_t n)
+{
+ int cols = 1, space = 0, quot = 0;
+ size_t i;
+
+ if (n < 10) return FALSE; // Too small
+
+ if (n > 132)
+ n = 132;
+
+ for (i = 1; i < n; i++) {
+
+ switch(Buffer[i])
+ {
+ case '\n':
+ case '\r':
+ return quot == 1 || cols > 2 ? 0 : cols;
+ case '\t':
+ case ' ':
+ if(!quot && !space)
+ space = 1;
+ break;
+ case '\"':
+ quot = !quot;
+ break;
+ default:
+ if (Buffer[i] < 32) return 0;
+ if (Buffer[i] > 127) return 0;
+ cols += space;
+ space = 0;
+ break;
+ }
+ }
+
+ return FALSE;
+
+}
+
+
+static
+int IsMyFile(const char* FileName)
+{
+ FILE *fp;
+ size_t Size;
+ BYTE Ptr[133];
+
+ fp = fopen(FileName, "rt");
+ if (!fp) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "File '%s' not found", FileName);
+ return FALSE;
+ }
+
+ Size = fread(Ptr, 1, 132, fp);
+ fclose(fp);
+
+ Ptr[Size] = '\0';
+
+ return IsMyBlock(Ptr, Size);
+}
+
+// ---------------------------------------------------------- Exported routines
+
+
+LCMSHANDLE LCMSEXPORT cmsIT8LoadFromMem(void *Ptr, size_t len)
+{
+ LCMSHANDLE hIT8;
+ LPIT8 it8;
+
+ int type = IsMyBlock((LPBYTE) Ptr, len);
+ if (type == 0) return NULL;
+
+ hIT8 = cmsIT8Alloc();
+ if (!hIT8) return NULL;
+
+ it8 = (LPIT8) hIT8;
+ it8 ->MemoryBlock = (char*) _cmsMalloc(len + 1);
+
+ strncpy(it8 ->MemoryBlock, (const char*) Ptr, len);
+ it8 ->MemoryBlock[len] = 0;
+
+ strncpy(it8->FileStack[0]->FileName, "", MAX_PATH-1);
+ it8-> Source = it8 -> MemoryBlock;
+
+ if (!ParseIT8(it8, type-1)) {
+
+ cmsIT8Free(hIT8);
+ return FALSE;
+ }
+
+ CookPointers(it8);
+ it8 ->nTable = 0;
+
+ _cmsFree(it8->MemoryBlock);
+ it8 -> MemoryBlock = NULL;
+
+ return hIT8;
+
+
+}
+
+
+LCMSHANDLE LCMSEXPORT cmsIT8LoadFromFile(const char* cFileName)
+{
+
+ LCMSHANDLE hIT8;
+ LPIT8 it8;
+
+ int type = IsMyFile(cFileName);
+ if (type == 0) return NULL;
+
+ hIT8 = cmsIT8Alloc();
+ it8 = (LPIT8) hIT8;
+ if (!hIT8) return NULL;
+
+
+ it8 ->FileStack[0]->Stream = fopen(cFileName, "rt");
+
+ if (!it8 ->FileStack[0]->Stream) {
+ cmsIT8Free(hIT8);
+ return NULL;
+ }
+
+
+ strncpy(it8->FileStack[0]->FileName, cFileName, MAX_PATH-1);
+ it8->FileStack[0]->FileName[MAX_PATH-1] = 0;
+
+ if (!ParseIT8(it8, type-1)) {
+
+ fclose(it8 ->FileStack[0]->Stream);
+ cmsIT8Free(hIT8);
+ return NULL;
+ }
+
+ CookPointers(it8);
+ it8 ->nTable = 0;
+
+ fclose(it8 ->FileStack[0]->Stream);
+ return hIT8;
+
+}
+
+int LCMSEXPORT cmsIT8EnumDataFormat(LCMSHANDLE hIT8, char ***SampleNames)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+ LPTABLE t = GetTable(it8);
+
+ *SampleNames = t -> DataFormat;
+ return t -> nSamples;
+}
+
+
+int LCMSEXPORT cmsIT8EnumProperties(LCMSHANDLE hIT8, const char ***PropertyNames)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+ LPKEYVALUE p;
+ int n;
+ const char **Props;
+ LPTABLE t = GetTable(it8);
+
+ // Pass#1 - count properties
+
+ n = 0;
+ for (p = t -> HeaderList; p != NULL; p = p->Next) {
+ n++;
+ }
+
+
+ Props = (const char **) AllocChunk(it8, sizeof(char *) * n);
+
+ // Pass#2 - Fill pointers
+ n = 0;
+ for (p = t -> HeaderList; p != NULL; p = p->Next) {
+ Props[n++] = p -> Keyword;
+ }
+
+ *PropertyNames = Props;
+ return n;
+}
+
+int LCMSEXPORT cmsIT8EnumPropertyMulti(LCMSHANDLE hIT8, const char* cProp, const char ***SubpropertyNames)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+ LPKEYVALUE p, tmp;
+ int n;
+ const char **Props;
+ LPTABLE t = GetTable(it8);
+
+ if(!IsAvailableOnList(t->HeaderList, cProp, NULL, &p)) {
+ *SubpropertyNames = 0;
+ return 0;
+ }
+
+ // Pass#1 - count properties
+
+ n = 0;
+ for (tmp = p; tmp != NULL; tmp = tmp->NextSubkey) {
+ if(tmp->Subkey != NULL)
+ n++;
+ }
+
+
+ Props = (const char **) AllocChunk(it8, sizeof(char *) * n);
+
+ // Pass#2 - Fill pointers
+ n = 0;
+ for (tmp = p; tmp != NULL; tmp = tmp->NextSubkey) {
+ if(tmp->Subkey != NULL)
+ Props[n++] = p ->Subkey;
+ }
+
+ *SubpropertyNames = Props;
+ return n;
+}
+
+static
+int LocatePatch(LPIT8 it8, const char* cPatch)
+{
+ int i;
+ const char *data;
+ LPTABLE t = GetTable(it8);
+
+ for (i=0; i < t-> nPatches; i++) {
+
+ data = GetData(it8, i, t->SampleID);
+
+ if (data != NULL) {
+
+ if (stricmp(data, cPatch) == 0)
+ return i;
+ }
+ }
+
+ // SynError(it8, "Couldn't find patch '%s'\n", cPatch);
+ return -1;
+}
+
+
+static
+int LocateEmptyPatch(LPIT8 it8)
+{
+ int i;
+ const char *data;
+ LPTABLE t = GetTable(it8);
+
+ for (i=0; i < t-> nPatches; i++) {
+
+ data = GetData(it8, i, t->SampleID);
+
+ if (data == NULL)
+ return i;
+
+ }
+
+ return -1;
+}
+
+static
+int LocateSample(LPIT8 it8, const char* cSample)
+{
+ int i;
+ const char *fld;
+ LPTABLE t = GetTable(it8);
+
+ for (i=0; i < t->nSamples; i++) {
+
+ fld = GetDataFormat(it8, i);
+ if (stricmp(fld, cSample) == 0)
+ return i;
+ }
+
+
+ // SynError(it8, "Couldn't find data field %s\n", cSample);
+ return -1;
+
+}
+
+
+int LCMSEXPORT cmsIT8GetDataFormat(LCMSHANDLE hIT8, const char* cSample)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+ return LocateSample(it8, cSample);
+}
+
+
+
+const char* LCMSEXPORT cmsIT8GetDataRowCol(LCMSHANDLE hIT8, int row, int col)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+
+ return GetData(it8, row, col);
+}
+
+
+double LCMSEXPORT cmsIT8GetDataRowColDbl(LCMSHANDLE hIT8, int row, int col)
+{
+ const char* Buffer;
+
+ Buffer = cmsIT8GetDataRowCol(hIT8, row, col);
+
+ if (Buffer) {
+
+ return atof(Buffer);
+
+ } else
+ return 0;
+
+}
+
+
+LCMSBOOL LCMSEXPORT cmsIT8SetDataRowCol(LCMSHANDLE hIT8, int row, int col, const char* Val)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+
+ return SetData(it8, row, col, Val);
+}
+
+
+LCMSBOOL LCMSEXPORT cmsIT8SetDataRowColDbl(LCMSHANDLE hIT8, int row, int col, double Val)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+ char Buff[256];
+
+ sprintf(Buff, it8->DoubleFormatter, Val);
+
+ return SetData(it8, row, col, Buff);
+}
+
+
+
+const char* LCMSEXPORT cmsIT8GetData(LCMSHANDLE hIT8, const char* cPatch, const char* cSample)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+ int iField, iSet;
+
+
+ iField = LocateSample(it8, cSample);
+ if (iField < 0) {
+ return NULL;
+ }
+
+
+ iSet = LocatePatch(it8, cPatch);
+ if (iSet < 0) {
+ return NULL;
+ }
+
+ return GetData(it8, iSet, iField);
+}
+
+
+double LCMSEXPORT cmsIT8GetDataDbl(LCMSHANDLE it8, const char* cPatch, const char* cSample)
+{
+ const char* Buffer;
+
+ Buffer = cmsIT8GetData(it8, cPatch, cSample);
+
+ if (Buffer) {
+
+ return atof(Buffer);
+
+ } else {
+
+ return 0;
+ }
+}
+
+
+
+LCMSBOOL LCMSEXPORT cmsIT8SetData(LCMSHANDLE hIT8, const char* cPatch,
+ const char* cSample,
+ const char *Val)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+ int iField, iSet;
+ LPTABLE t = GetTable(it8);
+
+
+ iField = LocateSample(it8, cSample);
+
+ if (iField < 0)
+ return FALSE;
+
+
+
+ if (t-> nPatches == 0) {
+
+ AllocateDataFormat(it8);
+ AllocateDataSet(it8);
+ CookPointers(it8);
+ }
+
+
+ if (stricmp(cSample, "SAMPLE_ID") == 0)
+ {
+
+ iSet = LocateEmptyPatch(it8);
+ if (iSet < 0) {
+ return SynError(it8, "Couldn't add more patches '%s'\n", cPatch);
+ }
+
+ iField = t -> SampleID;
+ }
+ else {
+ iSet = LocatePatch(it8, cPatch);
+ if (iSet < 0) {
+ return FALSE;
+ }
+ }
+
+ return SetData(it8, iSet, iField, Val);
+}
+
+
+LCMSBOOL LCMSEXPORT cmsIT8SetDataDbl(LCMSHANDLE hIT8, const char* cPatch,
+ const char* cSample,
+ double Val)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+ char Buff[256];
+
+ snprintf(Buff, 255, it8->DoubleFormatter, Val);
+ return cmsIT8SetData(hIT8, cPatch, cSample, Buff);
+
+}
+
+// Buffer should get MAXSTR at least
+
+const char* LCMSEXPORT cmsIT8GetPatchName(LCMSHANDLE hIT8, int nPatch, char* buffer)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+ LPTABLE t = GetTable(it8);
+ char* Data = GetData(it8, nPatch, t->SampleID);
+
+ if (!Data) return NULL;
+ if (!buffer) return Data;
+
+ strncpy(buffer, Data, MAXSTR-1);
+ buffer[MAXSTR-1] = 0;
+ return buffer;
+}
+
+int LCMSEXPORT cmsIT8GetPatchByName(LCMSHANDLE hIT8, const char *cPatch)
+{
+ return LocatePatch((LPIT8)hIT8, cPatch);
+}
+
+int LCMSEXPORT cmsIT8TableCount(LCMSHANDLE hIT8)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+
+ return it8 ->TablesCount;
+}
+
+// This handles the "LABEL" extension.
+// Label, nTable, Type
+
+int LCMSEXPORT cmsIT8SetTableByLabel(LCMSHANDLE hIT8, const char* cSet, const char* cField, const char* ExpectedType)
+{
+ const char* cLabelFld;
+ char Type[256], Label[256];
+ int nTable;
+
+ if (cField != NULL && *cField == 0)
+ cField = "LABEL";
+
+ if (cField == NULL)
+ cField = "LABEL";
+
+ cLabelFld = cmsIT8GetData(hIT8, cSet, cField);
+ if (!cLabelFld) return -1;
+
+ if (sscanf(cLabelFld, "%255s %d %255s", Label, &nTable, Type) != 3)
+ return -1;
+
+ if (ExpectedType != NULL && *ExpectedType == 0)
+ ExpectedType = NULL;
+
+ if (ExpectedType) {
+
+ if (stricmp(Type, ExpectedType) != 0) return -1;
+ }
+
+ return cmsIT8SetTable(hIT8, nTable);
+}
+
+
+LCMSBOOL LCMSEXPORT cmsIT8SetIndexColumn(LCMSHANDLE hIT8, const char* cSample)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+
+ int pos = LocateSample(it8, cSample);
+ if(pos == -1)
+ return FALSE;
+
+ it8->Tab[it8->nTable].SampleID = pos;
+ return TRUE;
+}
+
+
+void LCMSEXPORT cmsIT8DefineDblFormat(LCMSHANDLE hIT8, const char* Formatter)
+{
+ LPIT8 it8 = (LPIT8) hIT8;
+
+ if (Formatter == NULL)
+ strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT);
+ else
+ strcpy(it8->DoubleFormatter, Formatter);
+}
+
diff --git a/debian/lcms/lcms-1.19.dfsg2/src/cmscnvrt.c b/debian/lcms/lcms-1.19.dfsg2/src/cmscnvrt.c
new file mode 100755
index 00000000..7a4b23d4
--- /dev/null
+++ b/debian/lcms/lcms-1.19.dfsg2/src/cmscnvrt.c
@@ -0,0 +1,637 @@
+//
+// Little cms
+// Copyright (C) 1998-2007 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 "lcms.h"
+
+
+
+
+/*
+ This module provides conversion stages for handling intents.
+
+The chain of evaluation in a transform is:
+
+ PCS1 PCS2 PCS3 PCS4
+
+|From | |From | |Conversion | |Preview | |Gamut | |Conversion | |To | |To |
+|Input|->|Device|->|Stage 1 |->|handling|->|Checking|->|Stage 2 |->|Device|->|output |
+
+-------- ------- ------------- --------- ---------- ------------- ------- ---------
+
+ AToB0 prew0 gamut BToA0
+Formatting LUT Adjusting LUT LUT Adjusting LUT Formatting
+ Intent Intent 1 intent intent Intent 2 Intent
+
+
+Some of these LUT may be missing
+
+There are two intents involved here, the intent of the transform itself, and the
+intent the proof is being done, if is the case. Since the first intent is to be
+applied to preview, is the proofing intent. The second intent identifies the
+transform intent. Input data of any stage is taked as relative colorimetric
+always.
+
+
+NOTES: V4 states than perceptual & saturation intents between mixed v2 & v4 profiles should
+scale PCS from a black point equal to ZERO in v2 profiles to the reference media black of
+perceptual v4 PCS. Since I found many v2 profiles to be using a perceptual intent with black
+point not zero at all, I'm implementing that as a black point compensation from whatever
+black from perceptal intent to the reference media black for v4 profiles.
+
+*/
+
+
+
+
+int cdecl cmsChooseCnvrt(int Absolute,
+ int Phase1, LPcmsCIEXYZ BlackPointIn,
+ LPcmsCIEXYZ WhitePointIn,
+ LPcmsCIEXYZ IlluminantIn,
+ LPMAT3 ChromaticAdaptationMatrixIn,
+
+ int Phase2, LPcmsCIEXYZ BlackPointOut,
+ LPcmsCIEXYZ WhitePointOut,
+ LPcmsCIEXYZ IlluminantOut,
+ LPMAT3 ChromaticAdaptationMatrixOut,
+
+ int DoBlackPointCompensation,
+ double AdaptationState,
+ _cmsADJFN *fn1,
+ LPWMAT3 wm, LPWVEC3 wof);
+
+
+// -------------------------------------------------------------------------
+
+// D50 - Widely used
+
+LCMSAPI LPcmsCIEXYZ LCMSEXPORT cmsD50_XYZ(void)
+{
+ static cmsCIEXYZ D50XYZ = {D50X, D50Y, D50Z};
+
+ return &D50XYZ;
+}
+
+LCMSAPI LPcmsCIExyY LCMSEXPORT cmsD50_xyY(void)
+{
+ static cmsCIExyY D50xyY;
+ cmsXYZ2xyY(&D50xyY, cmsD50_XYZ());
+
+ return &D50xyY;
+}
+
+
+// ---------------- From LUT to LUT --------------------------
+
+
+// Calculate m, offset Relativ -> Absolute undoing any chromatic
+// adaptation done by the profile.
+
+#ifdef _MSC_VER
+#pragma warning(disable : 4100 4505)
+#endif
+
+
+
+// join scalings to obtain:
+// relative input to absolute and then to relative output
+
+static
+void Rel2RelStepAbsCoefs(double AdaptationState,
+
+ LPcmsCIEXYZ BlackPointIn,
+ LPcmsCIEXYZ WhitePointIn,
+ LPcmsCIEXYZ IlluminantIn,
+ LPMAT3 ChromaticAdaptationMatrixIn,
+
+ LPcmsCIEXYZ BlackPointOut,
+ LPcmsCIEXYZ WhitePointOut,
+ LPcmsCIEXYZ IlluminantOut,
+ LPMAT3 ChromaticAdaptationMatrixOut,
+
+ LPMAT3 m, LPVEC3 of)
+{
+
+ VEC3 WtPtIn, WtPtInAdapted;
+ VEC3 WtPtOut, WtPtOutAdapted;
+ MAT3 Scale, m1, m2, m3;
+
+ VEC3init(&WtPtIn, WhitePointIn->X, WhitePointIn->Y, WhitePointIn->Z);
+ MAT3eval(&WtPtInAdapted, ChromaticAdaptationMatrixIn, &WtPtIn);
+
+ VEC3init(&WtPtOut, WhitePointOut->X, WhitePointOut->Y, WhitePointOut->Z);
+ MAT3eval(&WtPtOutAdapted, ChromaticAdaptationMatrixOut, &WtPtOut);
+
+ VEC3init(&Scale.v[0], WtPtInAdapted.n[0] / WtPtOutAdapted.n[0], 0, 0);
+ VEC3init(&Scale.v[1], 0, WtPtInAdapted.n[1] / WtPtOutAdapted.n[1], 0);
+ VEC3init(&Scale.v[2], 0, 0, WtPtInAdapted.n[2] / WtPtOutAdapted.n[2]);
+
+
+ // Adaptation state
+
+ if (AdaptationState == 1.0) {
+
+ // Observer is fully adapted. Keep chromatic adaptation
+
+ CopyMemory(m, &Scale, sizeof(MAT3));
+
+ }
+ else {
+
+ // Observer is not adapted, undo the chromatic adaptation
+ m1 = *ChromaticAdaptationMatrixIn;
+ MAT3inverse(&m1, &m2);
+
+ MAT3per(&m3, &m2, &Scale);
+ MAT3per(m, &m3, ChromaticAdaptationMatrixOut);
+ }
+
+
+ VEC3init(of, 0.0, 0.0, 0.0);
+
+}
+
+
+// The (in)famous black point compensation. Right now implemented as
+// a linear scaling in XYZ
+
+static
+void ComputeBlackPointCompensationFactors(LPcmsCIEXYZ BlackPointIn,
+ LPcmsCIEXYZ WhitePointIn,
+ LPcmsCIEXYZ IlluminantIn,
+ LPcmsCIEXYZ BlackPointOut,
+ LPcmsCIEXYZ WhitePointOut,
+ LPcmsCIEXYZ IlluminantOut,
+ LPMAT3 m, LPVEC3 of)
+{
+
+
+ cmsCIEXYZ RelativeBlackPointIn, RelativeBlackPointOut;
+ double ax, ay, az, bx, by, bz, tx, ty, tz;
+
+ // At first, convert both black points to relative.
+
+ cmsAdaptToIlluminant(&RelativeBlackPointIn, WhitePointIn, IlluminantIn, BlackPointIn);
+ cmsAdaptToIlluminant(&RelativeBlackPointOut, WhitePointOut, IlluminantOut, BlackPointOut);
+
+ // Now we need to compute a matrix plus an offset m and of such of
+ // [m]*bpin + off = bpout
+ // [m]*D50 + off = D50
+ //
+ // This is a linear scaling in the form ax+b, where
+ // a = (bpout - D50) / (bpin - D50)
+ // b = - D50* (bpout - bpin) / (bpin - D50)
+
+
+ tx = RelativeBlackPointIn.X - IlluminantIn ->X;
+ ty = RelativeBlackPointIn.Y - IlluminantIn ->Y;
+ tz = RelativeBlackPointIn.Z - IlluminantIn ->Z;
+
+ ax = (RelativeBlackPointOut.X - IlluminantOut ->X) / tx;
+ ay = (RelativeBlackPointOut.Y - IlluminantOut ->Y) / ty;
+ az = (RelativeBlackPointOut.Z - IlluminantOut ->Z) / tz;
+
+ bx = - IlluminantOut -> X * (RelativeBlackPointOut.X - RelativeBlackPointIn.X) / tx;
+ by = - IlluminantOut -> Y * (RelativeBlackPointOut.Y - RelativeBlackPointIn.Y) / ty;
+ bz = - IlluminantOut -> Z * (RelativeBlackPointOut.Z - RelativeBlackPointIn.Z) / tz;
+
+
+ MAT3identity(m);
+
+ m->v[VX].n[0] = ax;
+ m->v[VY].n[1] = ay;
+ m->v[VZ].n[2] = az;
+
+ VEC3init(of, bx, by, bz);
+
+}
+
+// Return TRUE if both m and of are empy -- "m" being identity and "of" being 0
+
+static
+LCMSBOOL IdentityParameters(LPWMAT3 m, LPWVEC3 of)
+{
+ WVEC3 wv0;
+
+ VEC3initF(&wv0, 0, 0, 0);
+
+ if (!MAT3isIdentity(m, 0.00001)) return FALSE;
+ if (!VEC3equal(of, &wv0, 0.00001)) return FALSE;
+
+ return TRUE;
+}
+
+
+
+
+// ----------------------------------------- Inter PCS conversions
+
+// XYZ to XYZ linear scaling. Aso used on Black point compensation
+
+static
+void XYZ2XYZ(WORD In[], WORD Out[], LPWMAT3 m, LPWVEC3 of)
+{
+
+ WVEC3 a, r;
+
+ a.n[0] = In[0] << 1;
+ a.n[1] = In[1] << 1;
+ a.n[2] = In[2] << 1;
+
+ MAT3evalW(&r, m, &a);
+
+ Out[0] = _cmsClampWord((r.n[VX] + of->n[VX]) >> 1);
+ Out[1] = _cmsClampWord((r.n[VY] + of->n[VY]) >> 1);
+ Out[2] = _cmsClampWord((r.n[VZ] + of->n[VZ]) >> 1);
+}
+
+
+// XYZ to Lab, scaling first
+
+static
+void XYZ2Lab(WORD In[], WORD Out[], LPWMAT3 m, LPWVEC3 of)
+{
+ WORD XYZ[3];
+
+ XYZ2XYZ(In, XYZ, m, of);
+ cmsXYZ2LabEncoded(XYZ, Out);
+}
+
+// Lab to XYZ, then scalling
+
+static
+void Lab2XYZ(WORD In[], WORD Out[], LPWMAT3 m, LPWVEC3 of)
+{
+ WORD XYZ[3];
+
+ cmsLab2XYZEncoded(In, XYZ);
+ XYZ2XYZ(XYZ, Out, m, of);
+}
+
+// Lab to XYZ, scalling and then, back to Lab
+
+static
+void Lab2XYZ2Lab(WORD In[], WORD Out[], LPWMAT3 m, LPWVEC3 of)
+{
+ WORD XYZ[3], XYZ2[3];
+
+ cmsLab2XYZEncoded(In, XYZ);
+ XYZ2XYZ(XYZ, XYZ2, m, of);
+ cmsXYZ2LabEncoded(XYZ2, Out);
+}
+
+// ------------------------------------------------------------------
+
+// Dispatcher for XYZ Relative LUT
+
+static
+int FromXYZRelLUT(int Absolute,
+ LPcmsCIEXYZ BlackPointIn,
+ LPcmsCIEXYZ WhitePointIn,
+ LPcmsCIEXYZ IlluminantIn,
+ LPMAT3 ChromaticAdaptationMatrixIn,
+
+ int Phase2, LPcmsCIEXYZ BlackPointOut,
+ LPcmsCIEXYZ WhitePointOut,
+ LPcmsCIEXYZ IlluminantOut,
+ LPMAT3 ChromaticAdaptationMatrixOut,
+
+ int DoBlackPointCompensation,
+ double AdaptationState,
+ _cmsADJFN *fn1,
+ LPMAT3 m, LPVEC3 of)
+
+{
+ switch (Phase2) {
+
+ // From relative XYZ to Relative XYZ.
+
+ case XYZRel:
+
+ if (Absolute)
+ {
+ // From input relative to absolute, and then
+ // back to output relative
+
+ Rel2RelStepAbsCoefs(AdaptationState,
+ BlackPointIn,
+ WhitePointIn,
+ IlluminantIn,
+ ChromaticAdaptationMatrixIn,
+ BlackPointOut,
+ WhitePointOut,
+ IlluminantOut,
+ ChromaticAdaptationMatrixOut,
+ m, of);
+ *fn1 = XYZ2XYZ;
+
+ }
+ else
+ {
+ // XYZ Relative to XYZ relative, no op required
+ *fn1 = NULL;
+ if (DoBlackPointCompensation) {
+
+ *fn1 = XYZ2XYZ;
+ ComputeBlackPointCompensationFactors(BlackPointIn,
+ WhitePointIn,
+ IlluminantIn,
+ BlackPointOut,
+ WhitePointOut,
+ IlluminantOut,
+ m, of);
+
+ }
+ }
+ break;
+
+
+ // From relative XYZ to Relative Lab
+
+ case LabRel:
+
+ // First pass XYZ to absolute, then to relative and
+ // finally to Lab. I use here D50 for output in order
+ // to prepare the "to Lab" conversion.
+
+ if (Absolute)
+ {
+
+ Rel2RelStepAbsCoefs(AdaptationState,
+ BlackPointIn,
+ WhitePointIn,
+ IlluminantIn,
+ ChromaticAdaptationMatrixIn,
+ BlackPointOut,
+ WhitePointOut,
+ IlluminantOut,
+ ChromaticAdaptationMatrixOut,
+ m, of);
+
+ *fn1 = XYZ2Lab;
+
+ }
+ else
+ {
+ // Just Convert to Lab
+
+ MAT3identity(m);
+ VEC3init(of, 0, 0, 0);
+ *fn1 = XYZ2Lab;
+
+ if (DoBlackPointCompensation) {
+
+ ComputeBlackPointCompensationFactors(BlackPointIn,
+ WhitePointIn,
+ IlluminantIn,
+ BlackPointOut,
+ WhitePointOut,
+ IlluminantOut,
+ m, of);
+ }
+ }
+ break;
+
+
+ default: return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+
+
+// From Lab Relative type LUT
+
+static
+int FromLabRelLUT(int Absolute,
+ LPcmsCIEXYZ BlackPointIn,
+ LPcmsCIEXYZ WhitePointIn,
+ LPcmsCIEXYZ IlluminantIn,
+ LPMAT3 ChromaticAdaptationMatrixIn,
+
+ int Phase2, LPcmsCIEXYZ BlackPointOut,
+ LPcmsCIEXYZ WhitePointOut,
+ LPcmsCIEXYZ IlluminantOut,
+ LPMAT3 ChromaticAdaptationMatrixOut,
+
+ int DoBlackPointCompensation,
+ double AdaptationState,
+
+ _cmsADJFN *fn1,
+ LPMAT3 m, LPVEC3 of)
+{
+
+ switch (Phase2) {
+
+ // From Lab Relative to XYZ Relative, very usual case
+
+ case XYZRel:
+
+ if (Absolute) { // Absolute intent
+
+ // From lab relative, to XYZ absolute, and then,
+ // back to XYZ relative
+
+ Rel2RelStepAbsCoefs(AdaptationState,
+ BlackPointIn,
+ WhitePointIn,
+ cmsD50_XYZ(),
+ ChromaticAdaptationMatrixIn,
+ BlackPointOut,
+ WhitePointOut,
+ IlluminantOut,
+ ChromaticAdaptationMatrixOut,
+ m, of);
+
+ *fn1 = Lab2XYZ;
+
+ }
+ else
+ {
+ // From Lab relative, to XYZ relative.
+
+ *fn1 = Lab2XYZ;
+ if (DoBlackPointCompensation) {
+
+ ComputeBlackPointCompensationFactors(BlackPointIn,
+ WhitePointIn,
+ IlluminantIn,
+ BlackPointOut,
+ WhitePointOut,
+ IlluminantOut,
+ m, of);
+
+ }
+ }
+ break;
+
+
+
+ case LabRel:
+
+ if (Absolute) {
+
+ // First pass to XYZ using the input illuminant
+ // * InIlluminant / D50, then to absolute. Then
+ // to relative, but for input
+
+ Rel2RelStepAbsCoefs(AdaptationState,
+ BlackPointIn,
+ WhitePointIn, IlluminantIn,
+ ChromaticAdaptationMatrixIn,
+ BlackPointOut,
+ WhitePointOut, cmsD50_XYZ(),
+ ChromaticAdaptationMatrixOut,
+ m, of);
+ *fn1 = Lab2XYZ2Lab;
+ }
+ else
+ { // Lab -> Lab relative don't need any adjust unless
+ // black point compensation
+
+ *fn1 = NULL;
+ if (DoBlackPointCompensation) {
+
+ *fn1 = Lab2XYZ2Lab;
+ ComputeBlackPointCompensationFactors(BlackPointIn,
+ WhitePointIn,
+ IlluminantIn,
+ BlackPointOut,
+ WhitePointOut,
+ IlluminantOut,
+ m, of);
+
+
+ }
+ }
+ break;
+
+
+ default: return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+// This function does calculate the necessary conversion operations
+// needed from transpassing data from a LUT to a LUT. The conversion
+// is modeled as a pointer of function and two coefficients, a and b
+// The function is actually called only if not null pointer is provided,
+// and the two paramaters are passed in. There are several types of
+// conversions, but basically they do a linear scalling and a interchange
+
+
+
+// Main dispatcher
+
+int cmsChooseCnvrt(int Absolute,
+ int Phase1, LPcmsCIEXYZ BlackPointIn,
+ LPcmsCIEXYZ WhitePointIn,
+ LPcmsCIEXYZ IlluminantIn,
+ LPMAT3 ChromaticAdaptationMatrixIn,
+
+ int Phase2, LPcmsCIEXYZ BlackPointOut,
+ LPcmsCIEXYZ WhitePointOut,
+ LPcmsCIEXYZ IlluminantOut,
+ LPMAT3 ChromaticAdaptationMatrixOut,
+
+ int DoBlackPointCompensation,
+ double AdaptationState,
+ _cmsADJFN *fn1,
+ LPWMAT3 wm, LPWVEC3 wof)
+{
+
+ int rc;
+ MAT3 m;
+ VEC3 of;
+
+
+ MAT3identity(&m);
+ VEC3init(&of, 0, 0, 0);
+
+ switch (Phase1) {
+
+ // Input LUT is giving XYZ relative values.
+
+ case XYZRel: rc = FromXYZRelLUT(Absolute,
+ BlackPointIn,
+ WhitePointIn,
+ IlluminantIn,
+ ChromaticAdaptationMatrixIn,
+ Phase2,
+ BlackPointOut,
+ WhitePointOut,
+ IlluminantOut,
+ ChromaticAdaptationMatrixOut,
+ DoBlackPointCompensation,
+ AdaptationState,
+ fn1, &m, &of);
+ break;
+
+
+
+ // Input LUT is giving Lab relative values
+
+ case LabRel: rc = FromLabRelLUT(Absolute,
+ BlackPointIn,
+ WhitePointIn,
+ IlluminantIn,
+ ChromaticAdaptationMatrixIn,
+ Phase2,
+ BlackPointOut,
+ WhitePointOut,
+ IlluminantOut,
+ ChromaticAdaptationMatrixOut,
+ DoBlackPointCompensation,
+ AdaptationState,
+ fn1, &m, &of);
+ break;
+
+
+
+
+ // Unrecognized combination
+
+ default: cmsSignalError(LCMS_ERRC_ABORTED, "(internal) Phase error");
+ return FALSE;
+
+ }
+
+ MAT3toFix(wm, &m);
+ VEC3toFix(wof, &of);
+
+ // Do some optimization -- discard conversion if identity parameters.
+
+ if (*fn1 == XYZ2XYZ || *fn1 == Lab2XYZ2Lab) {
+
+ if (IdentityParameters(wm, wof))
+ *fn1 = NULL;
+ }
+
+
+ return rc;
+}
+
+
+
diff --git a/debian/lcms/lcms-1.19.dfsg2/src/cmserr.c b/debian/lcms/lcms-1.19.dfsg2/src/cmserr.c
new file mode 100755
index 00000000..96867297
--- /dev/null
+++ b/debian/lcms/lcms-1.19.dfsg2/src/cmserr.c
@@ -0,0 +1,110 @@
+//
+// Little cms
+// Copyright (C) 1998-2007 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 "lcms.h"
+
+
+// As a rule, only the functions visible from API can signal
+// errors.
+
+void cdecl cmsSignalError(int ErrorCode, const char *ErrorText, ...);
+
+int LCMSEXPORT cmsErrorAction(int lAbort);
+void LCMSEXPORT cmsSetErrorHandler(cmsErrorHandlerFunction Fn);
+
+
+// ******************************************************************
+
+static int nDoAbort = LCMS_ERROR_ABORT;
+static cmsErrorHandlerFunction UserErrorHandler = (cmsErrorHandlerFunction) NULL;
+
+
+int LCMSEXPORT cmsErrorAction(int nAction)
+{
+ int nOld = nDoAbort;
+ nDoAbort = nAction;
+
+ return nOld;
+}
+
+void LCMSEXPORT cmsSetErrorHandler(cmsErrorHandlerFunction Fn)
+{
+ UserErrorHandler = Fn;
+}
+
+
+// Default error handler
+
+
+void cmsSignalError(int ErrorCode, const char *ErrorText, ...)
+{
+ va_list args;
+
+ if (nDoAbort == LCMS_ERROR_IGNORE) return;
+
+ va_start(args, ErrorText);
+
+ if (UserErrorHandler != NULL) {
+
+ char Buffer[1024];
+
+ vsnprintf(Buffer, 1023, ErrorText, args);
+ va_end(args);
+
+ if (UserErrorHandler(ErrorCode, Buffer)) {
+
+ return;
+ }
+ }
+
+#if defined( __CONSOLE__ ) || defined( NON_WINDOWS ) || defined(UNICODE)
+
+ fprintf(stderr, "lcms: Error #%d; ", ErrorCode);
+ vfprintf(stderr, ErrorText, args);
+ fprintf(stderr, "\n");
+ va_end(args);
+
+ if (nDoAbort == LCMS_ERROR_ABORT) exit(1);
+#else
+ {
+ char Buffer1[1024];
+ char Buffer2[256];
+
+ snprintf(Buffer1, 767, "Error #%x; ", ErrorCode);
+ vsnprintf(Buffer2, 255, ErrorText, args);
+ strcat(Buffer1, Buffer2);
+ MessageBox(NULL, Buffer1, "Little cms",
+ MB_OK|MB_ICONSTOP|MB_TASKMODAL);
+ va_end(args);
+
+ if (nDoAbort == LCMS_ERROR_ABORT) {
+
+#ifdef __BORLANDC__
+ _cexit();
+#endif
+
+ FatalAppExit(0, "lcms is terminating application");
+ }
+ }
+#endif
+}
diff --git a/debian/lcms/lcms-1.19.dfsg2/src/cmsgamma.c b/debian/lcms/lcms-1.19.dfsg2/src/cmsgamma.c
new file mode 100755
index 00000000..857d82fc
--- /dev/null
+++ b/debian/lcms/lcms-1.19.dfsg2/src/cmsgamma.c
@@ -0,0 +1,954 @@
+//
+// Little cms
+// Copyright (C) 1998-2007 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 "lcms.h"
+
+// Gamma handling.
+
+LPGAMMATABLE LCMSEXPORT cmsAllocGamma(int nEntries);
+void LCMSEXPORT cmsFreeGamma(LPGAMMATABLE Gamma);
+void LCMSEXPORT cmsFreeGammaTriple(LPGAMMATABLE Gamma[3]);
+LPGAMMATABLE LCMSEXPORT cmsBuildGamma(int nEntries, double Gamma);
+LPGAMMATABLE LCMSEXPORT cmsDupGamma(LPGAMMATABLE Src);
+LPGAMMATABLE LCMSEXPORT cmsReverseGamma(int nResultSamples, LPGAMMATABLE InGamma);
+LPGAMMATABLE LCMSEXPORT cmsBuildParametricGamma(int nEntries, int Type, double Params[]);
+LPGAMMATABLE LCMSEXPORT cmsJoinGamma(LPGAMMATABLE InGamma, LPGAMMATABLE OutGamma);
+LPGAMMATABLE LCMSEXPORT cmsJoinGammaEx(LPGAMMATABLE InGamma, LPGAMMATABLE OutGamma, int nPoints);
+LCMSBOOL LCMSEXPORT cmsSmoothGamma(LPGAMMATABLE Tab, double lambda);
+
+LCMSBOOL cdecl _cmsSmoothEndpoints(LPWORD Table, int nPoints);
+
+
+// Sampled curves
+
+LPSAMPLEDCURVE cdecl cmsAllocSampledCurve(int nItems);
+void cdecl cmsFreeSampledCurve(LPSAMPLEDCURVE p);
+void cdecl cmsEndpointsOfSampledCurve(LPSAMPLEDCURVE p, double* Min, double* Max);
+void cdecl cmsClampSampledCurve(LPSAMPLEDCURVE p, double Min, double Max);
+LCMSBOOL cdecl cmsSmoothSampledCurve(LPSAMPLEDCURVE Tab, double SmoothingLambda);
+void cdecl cmsRescaleSampledCurve(LPSAMPLEDCURVE p, double Min, double Max, int nPoints);
+
+LPSAMPLEDCURVE cdecl cmsJoinSampledCurves(LPSAMPLEDCURVE X, LPSAMPLEDCURVE Y, int nResultingPoints);
+
+double LCMSEXPORT cmsEstimateGamma(LPGAMMATABLE t);
+double LCMSEXPORT cmsEstimateGammaEx(LPWORD GammaTable, int nEntries, double Thereshold);
+
+// ----------------------------------------------------------------------------------------
+
+
+#define MAX_KNOTS 4096
+typedef float vec[MAX_KNOTS+1];
+
+
+// Ciclic-redundant-check for assuring table is a true representation of parametric curve
+
+// The usual polynomial, which is used for AAL5, FDDI, and probably Ethernet
+#define QUOTIENT 0x04c11db7
+
+static
+unsigned int Crc32(unsigned int result, LPVOID ptr, int len)
+{
+ int i,j;
+ BYTE octet;
+ LPBYTE data = (LPBYTE) ptr;
+
+ for (i=0; i < len; i++) {
+
+ octet = *data++;
+
+ for (j=0; j < 8; j++) {
+
+ if (result & 0x80000000) {
+
+ result = (result << 1) ^ QUOTIENT ^ (octet >> 7);
+ }
+ else
+ {
+ result = (result << 1) ^ (octet >> 7);
+ }
+ octet <<= 1;
+ }
+ }
+
+ return result;
+}
+
+// Get CRC of gamma table
+
+unsigned int _cmsCrc32OfGammaTable(LPGAMMATABLE Table)
+{
+ unsigned int crc = ~0U;
+
+ crc = Crc32(crc, &Table -> Seed.Type, sizeof(int));
+ crc = Crc32(crc, Table ->Seed.Params, sizeof(double)*10);
+ crc = Crc32(crc, &Table ->nEntries, sizeof(int));
+ crc = Crc32(crc, Table ->GammaTable, sizeof(WORD) * Table -> nEntries);
+
+ return ~crc;
+
+}
+
+
+LPGAMMATABLE LCMSEXPORT cmsAllocGamma(int nEntries)
+{
+ LPGAMMATABLE p;
+ size_t size;
+
+ if (nEntries > 65530 || nEntries <= 0) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't create gammatable of more than 65530 entries");
+ return NULL;
+ }
+
+ size = sizeof(GAMMATABLE) + (sizeof(WORD) * (nEntries-1));
+
+ p = (LPGAMMATABLE) _cmsMalloc(size);
+ if (!p) return NULL;
+
+ ZeroMemory(p, size);
+
+ p -> Seed.Type = 0;
+ p -> nEntries = nEntries;
+
+ return p;
+}
+
+void LCMSEXPORT cmsFreeGamma(LPGAMMATABLE Gamma)
+{
+ if (Gamma) _cmsFree(Gamma);
+}
+
+
+
+void LCMSEXPORT cmsFreeGammaTriple(LPGAMMATABLE Gamma[3])
+{
+ cmsFreeGamma(Gamma[0]);
+ cmsFreeGamma(Gamma[1]);
+ cmsFreeGamma(Gamma[2]);
+ Gamma[0] = Gamma[1] = Gamma[2] = NULL;
+}
+
+
+
+// Duplicate a gamma table
+
+LPGAMMATABLE LCMSEXPORT cmsDupGamma(LPGAMMATABLE In)
+{
+ LPGAMMATABLE Ptr;
+ size_t size;
+
+ Ptr = cmsAllocGamma(In -> nEntries);
+ if (Ptr == NULL) return NULL;
+
+ size = sizeof(GAMMATABLE) + (sizeof(WORD) * (In -> nEntries-1));
+
+ CopyMemory(Ptr, In, size);
+ return Ptr;
+}
+
+
+// Handle gamma using interpolation tables. The resulting curves can become
+// very stange, but are pleasent to eye.
+
+LPGAMMATABLE LCMSEXPORT cmsJoinGamma(LPGAMMATABLE InGamma,
+ LPGAMMATABLE OutGamma)
+{
+ register int i;
+ L16PARAMS L16In, L16Out;
+ LPWORD InPtr, OutPtr;
+ LPGAMMATABLE p;
+
+ p = cmsAllocGamma(256);
+ if (!p) return NULL;
+
+ cmsCalcL16Params(InGamma -> nEntries, &L16In);
+ InPtr = InGamma -> GammaTable;
+
+ cmsCalcL16Params(OutGamma -> nEntries, &L16Out);
+ OutPtr = OutGamma-> GammaTable;
+
+ for (i=0; i < 256; i++)
+ {
+ WORD wValIn, wValOut;
+
+ wValIn = cmsLinearInterpLUT16(RGB_8_TO_16(i), InPtr, &L16In);
+ wValOut = cmsReverseLinearInterpLUT16(wValIn, OutPtr, &L16Out);
+
+ p -> GammaTable[i] = wValOut;
+ }
+
+ return p;
+}
+
+
+
+// New method, using smoothed parametric curves. This works FAR better.
+// We want to get
+//
+// y = f(g^-1(x)) ; f = ingamma, g = outgamma
+//
+// And this can be parametrized as
+//
+// y = f(t)
+// x = g(t)
+
+
+LPGAMMATABLE LCMSEXPORT cmsJoinGammaEx(LPGAMMATABLE InGamma,
+ LPGAMMATABLE OutGamma, int nPoints)
+{
+
+ LPSAMPLEDCURVE x, y, r;
+ LPGAMMATABLE res;
+
+ x = cmsConvertGammaToSampledCurve(InGamma, nPoints);
+ y = cmsConvertGammaToSampledCurve(OutGamma, nPoints);
+ r = cmsJoinSampledCurves(y, x, nPoints);
+
+ // Does clean "hair"
+ cmsSmoothSampledCurve(r, 0.001);
+
+ cmsClampSampledCurve(r, 0.0, 65535.0);
+
+ cmsFreeSampledCurve(x);
+ cmsFreeSampledCurve(y);
+
+ res = cmsConvertSampledCurveToGamma(r, 65535.0);
+ cmsFreeSampledCurve(r);
+
+ return res;
+}
+
+
+
+// Reverse a gamma table
+
+LPGAMMATABLE LCMSEXPORT cmsReverseGamma(int nResultSamples, LPGAMMATABLE InGamma)
+{
+ register int i;
+ L16PARAMS L16In;
+ LPWORD InPtr;
+ LPGAMMATABLE p;
+
+ // Try to reverse it analytically whatever possible
+ if (InGamma -> Seed.Type > 0 && InGamma -> Seed.Type <= 5 &&
+ _cmsCrc32OfGammaTable(InGamma) == InGamma -> Seed.Crc32) {
+
+ return cmsBuildParametricGamma(nResultSamples, -(InGamma -> Seed.Type), InGamma ->Seed.Params);
+ }
+
+
+ // Nope, reverse the table
+ p = cmsAllocGamma(nResultSamples);
+ if (!p) return NULL;
+
+ cmsCalcL16Params(InGamma -> nEntries, &L16In);
+ InPtr = InGamma -> GammaTable;
+
+ for (i=0; i < nResultSamples; i++)
+ {
+ WORD wValIn, wValOut;
+
+ wValIn = _cmsQuantizeVal(i, nResultSamples);
+ wValOut = cmsReverseLinearInterpLUT16(wValIn, InPtr, &L16In);
+ p -> GammaTable[i] = wValOut;
+ }
+
+
+ return p;
+}
+
+
+
+// Parametric curves
+//
+// Parameters goes as: Gamma, a, b, c, d, e, f
+// Type is the ICC type +1
+// if type is negative, then the curve is analyticaly inverted
+
+LPGAMMATABLE LCMSEXPORT cmsBuildParametricGamma(int nEntries, int Type, double Params[])
+{
+ LPGAMMATABLE Table;
+ double R, Val, dval, e;
+ int i;
+ int ParamsByType[] = { 0, 1, 3, 4, 5, 7 };
+
+ Table = cmsAllocGamma(nEntries);
+ if (NULL == Table) return NULL;
+
+ Table -> Seed.Type = Type;
+
+ CopyMemory(Table ->Seed.Params, Params, ParamsByType[abs(Type)] * sizeof(double));
+
+
+ for (i=0; i < nEntries; i++) {
+
+ R = (double) i / (nEntries-1);
+
+ switch (Type) {
+
+ // X = Y ^ Gamma
+ case 1:
+ Val = pow(R, Params[0]);
+ break;
+
+ // Type 1 Reversed: X = Y ^1/gamma
+ case -1:
+ Val = pow(R, 1/Params[0]);
+ break;
+
+ // CIE 122-1966
+ // Y = (aX + b)^Gamma | X >= -b/a
+ // Y = 0 | else
+ case 2:
+ if (R >= -Params[2] / Params[1]) {
+
+ e = Params[1]*R + Params[2];
+
+ if (e > 0)
+ Val = pow(e, Params[0]);
+ else
+ Val = 0;
+ }
+ else
+ Val = 0;
+ break;
+
+ // Type 2 Reversed
+ // X = (Y ^1/g - b) / a
+ case -2:
+
+ Val = (pow(R, 1.0/Params[0]) - Params[2]) / Params[1];
+ if (Val < 0)
+ Val = 0;
+ break;
+
+
+ // IEC 61966-3
+ // Y = (aX + b)^Gamma | X <= -b/a
+ // Y = c | else
+ case 3:
+ if (R >= -Params[2] / Params[1]) {
+
+ e = Params[1]*R + Params[2];
+ Val = pow(e, Params[0]) + Params[3];
+ }
+ else
+ Val = Params[3];
+ break;
+
+
+ // Type 3 reversed
+ // X=((Y-c)^1/g - b)/a | (Y>=c)
+ // X=-b/a | (Y<c)
+
+ case -3:
+ if (R >= Params[3]) {
+ e = R - Params[3];
+ Val = (pow(e, 1/Params[0]) - Params[2]) / Params[1];
+ if (Val < 0) Val = 0;
+ }
+ else {
+ Val = -Params[2] / Params[1];
+ }
+ break;
+
+
+ // IEC 61966-2.1 (sRGB)
+ // Y = (aX + b)^Gamma | X >= d
+ // Y = cX | X < d
+ case 4:
+ if (R >= Params[4]) {
+
+ e = Params[1]*R + Params[2];
+ if (e > 0)
+ Val = pow(e, Params[0]);
+ else
+ Val = 0;
+ }
+ else
+ Val = R * Params[3];
+ break;
+
+ // Type 4 reversed
+ // X=((Y^1/g-b)/a) | Y >= (ad+b)^g
+ // X=Y/c | Y< (ad+b)^g
+
+ case -4:
+ if (R >= pow(Params[1] * Params[4] + Params[2], Params[0])) {
+
+ Val = (pow(R, 1.0/Params[0]) - Params[2]) / Params[1];
+ }
+ else {
+ Val = R / Params[3];
+ }
+ break;
+
+
+
+ // Y = (aX + b)^Gamma + e | X <= d
+ // Y = cX + f | else
+ case 5:
+ if (R >= Params[4]) {
+
+ e = Params[1]*R + Params[2];
+ Val = pow(e, Params[0]) + Params[5];
+ }
+ else
+ Val = R*Params[3] + Params[6];
+ break;
+
+
+ // Reversed type 5
+ // X=((Y-e)1/g-b)/a | Y >=(ad+b)^g+e)
+ // X=(Y-f)/c | else
+ case -5:
+
+ if (R >= pow(Params[1] * Params[4], Params[0]) + Params[5]) {
+
+ Val = pow(R - Params[5], 1/Params[0]) - Params[2] / Params[1];
+ }
+ else {
+ Val = (R - Params[6]) / Params[3];
+ }
+ break;
+
+ default:
+ cmsSignalError(LCMS_ERRC_ABORTED, "Unsupported parametric curve type=%d", abs(Type)-1);
+ cmsFreeGamma(Table);
+ return NULL;
+ }
+
+
+ // Saturate
+
+ dval = Val * 65535.0 + .5;
+ if (dval > 65535.) dval = 65535.0;
+ if (dval < 0) dval = 0;
+
+ Table->GammaTable[i] = (WORD) floor(dval);
+ }
+
+ Table -> Seed.Crc32 = _cmsCrc32OfGammaTable(Table);
+
+ return Table;
+}
+
+// Build a gamma table based on gamma constant
+
+LPGAMMATABLE LCMSEXPORT cmsBuildGamma(int nEntries, double Gamma)
+{
+ return cmsBuildParametricGamma(nEntries, 1, &Gamma);
+}
+
+
+
+// From: Eilers, P.H.C. (1994) Smoothing and interpolation with finite
+// differences. in: Graphic Gems IV, Heckbert, P.S. (ed.), Academic press.
+//
+// Smoothing and interpolation with second differences.
+//
+// Input: weights (w), data (y): vector from 1 to m.
+// Input: smoothing parameter (lambda), length (m).
+// Output: smoothed vector (z): vector from 1 to m.
+
+
+static
+void smooth2(vec w, vec y, vec z, float lambda, int m)
+{
+ int i, i1, i2;
+ vec c, d, e;
+ d[1] = w[1] + lambda;
+ c[1] = -2 * lambda / d[1];
+ e[1] = lambda /d[1];
+ z[1] = w[1] * y[1];
+ d[2] = w[2] + 5 * lambda - d[1] * c[1] * c[1];
+ c[2] = (-4 * lambda - d[1] * c[1] * e[1]) / d[2];
+ e[2] = lambda / d[2];
+ z[2] = w[2] * y[2] - c[1] * z[1];
+ for (i = 3; i < m - 1; i++) {
+ i1 = i - 1; i2 = i - 2;
+ d[i]= w[i] + 6 * lambda - c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2];
+ c[i] = (-4 * lambda -d[i1] * c[i1] * e[i1])/ d[i];
+ e[i] = lambda / d[i];
+ z[i] = w[i] * y[i] - c[i1] * z[i1] - e[i2] * z[i2];
+ }
+ i1 = m - 2; i2 = m - 3;
+ d[m - 1] = w[m - 1] + 5 * lambda -c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2];
+ c[m - 1] = (-2 * lambda - d[i1] * c[i1] * e[i1]) / d[m - 1];
+ z[m - 1] = w[m - 1] * y[m - 1] - c[i1] * z[i1] - e[i2] * z[i2];
+ i1 = m - 1; i2 = m - 2;
+ d[m] = w[m] + lambda - c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2];
+ z[m] = (w[m] * y[m] - c[i1] * z[i1] - e[i2] * z[i2]) / d[m];
+ z[m - 1] = z[m - 1] / d[m - 1] - c[m - 1] * z[m];
+ for (i = m - 2; 1<= i; i--)
+ z[i] = z[i] / d[i] - c[i] * z[i + 1] - e[i] * z[i + 2];
+}
+
+
+
+// Smooths a curve sampled at regular intervals
+
+LCMSBOOL LCMSEXPORT cmsSmoothGamma(LPGAMMATABLE Tab, double lambda)
+
+{
+ vec w, y, z;
+ int i, nItems, Zeros, Poles;
+
+
+ if (cmsIsLinear(Tab->GammaTable, Tab->nEntries)) return FALSE; // Nothing to do
+
+ nItems = Tab -> nEntries;
+
+ if (nItems > MAX_KNOTS) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "cmsSmoothGamma: too many points.");
+ return FALSE;
+ }
+
+ ZeroMemory(w, nItems * sizeof(float));
+ ZeroMemory(y, nItems * sizeof(float));
+ ZeroMemory(z, nItems * sizeof(float));
+
+ for (i=0; i < nItems; i++)
+ {
+ y[i+1] = (float) Tab -> GammaTable[i];
+ w[i+1] = 1.0;
+ }
+
+ smooth2(w, y, z, (float) lambda, nItems);
+
+ // Do some reality - checking...
+ Zeros = Poles = 0;
+ for (i=nItems; i > 1; --i) {
+
+ if (z[i] == 0.) Zeros++;
+ if (z[i] >= 65535.) Poles++;
+ if (z[i] < z[i-1]) return FALSE; // Non-Monotonic
+ }
+
+ if (Zeros > (nItems / 3)) return FALSE; // Degenerated, mostly zeros
+ if (Poles > (nItems / 3)) return FALSE; // Degenerated, mostly poles
+
+ // Seems ok
+
+ for (i=0; i < nItems; i++) {
+
+ // Clamp to WORD
+
+ float v = z[i+1];
+
+ if (v < 0) v = 0;
+ if (v > 65535.) v = 65535.;
+
+ Tab -> GammaTable[i] = (WORD) floor(v + .5);
+ }
+
+ return TRUE;
+}
+
+
+// Check if curve is exponential, return gamma if so.
+
+double LCMSEXPORT cmsEstimateGammaEx(LPWORD GammaTable, int nEntries, double Thereshold)
+{
+ double gamma, sum, sum2;
+ double n, x, y, Std;
+ int i;
+
+ sum = sum2 = n = 0;
+
+ // Does exclude endpoints
+ for (i=1; i < nEntries - 1; i++) {
+
+ x = (double) i / (nEntries - 1);
+ y = (double) GammaTable[i] / 65535.;
+
+ // Avoid 7% on lower part to prevent
+ // artifacts due to linear ramps
+
+ if (y > 0. && y < 1. && x > 0.07) {
+
+ gamma = log(y) / log(x);
+ sum += gamma;
+ sum2 += gamma * gamma;
+ n++;
+ }
+
+ }
+
+ // Take a look on SD to see if gamma isn't exponential at all
+ Std = sqrt((n * sum2 - sum * sum) / (n*(n-1)));
+
+
+ if (Std > Thereshold)
+ return -1.0;
+
+ return (sum / n); // The mean
+}
+
+
+double LCMSEXPORT cmsEstimateGamma(LPGAMMATABLE t)
+{
+ return cmsEstimateGammaEx(t->GammaTable, t->nEntries, 0.7);
+}
+
+
+// -----------------------------------------------------------------Sampled curves
+
+// Allocate a empty curve
+
+LPSAMPLEDCURVE cmsAllocSampledCurve(int nItems)
+{
+ LPSAMPLEDCURVE pOut;
+
+ pOut = (LPSAMPLEDCURVE) _cmsMalloc(sizeof(SAMPLEDCURVE));
+ if (pOut == NULL)
+ return NULL;
+
+ if((pOut->Values = (double *) _cmsMalloc(nItems * sizeof(double))) == NULL)
+ {
+ _cmsFree(pOut);
+ return NULL;
+ }
+
+ pOut->nItems = nItems;
+ ZeroMemory(pOut->Values, nItems * sizeof(double));
+
+ return pOut;
+}
+
+
+void cmsFreeSampledCurve(LPSAMPLEDCURVE p)
+{
+ _cmsFree((LPVOID) p -> Values);
+ _cmsFree((LPVOID) p);
+}
+
+
+
+// Does duplicate a sampled curve
+
+LPSAMPLEDCURVE cmsDupSampledCurve(LPSAMPLEDCURVE p)
+{
+ LPSAMPLEDCURVE out;
+
+ out = cmsAllocSampledCurve(p -> nItems);
+ if (!out) return NULL;
+
+ CopyMemory(out ->Values, p ->Values, p->nItems * sizeof(double));
+
+ return out;
+}
+
+
+// Take min, max of curve
+
+void cmsEndpointsOfSampledCurve(LPSAMPLEDCURVE p, double* Min, double* Max)
+{
+ int i;
+
+ *Min = 65536.;
+ *Max = 0.;
+
+ for (i=0; i < p -> nItems; i++) {
+
+ double v = p -> Values[i];
+
+ if (v < *Min)
+ *Min = v;
+
+ if (v > *Max)
+ *Max = v;
+ }
+
+ if (*Min < 0) *Min = 0;
+ if (*Max > 65535.0) *Max = 65535.0;
+}
+
+// Clamps to Min, Max
+
+void cmsClampSampledCurve(LPSAMPLEDCURVE p, double Min, double Max)
+{
+
+ int i;
+
+ for (i=0; i < p -> nItems; i++) {
+
+ double v = p -> Values[i];
+
+ if (v < Min)
+ v = Min;
+
+ if (v > Max)
+ v = Max;
+
+ p -> Values[i] = v;
+
+ }
+
+}
+
+
+
+// Smooths a curve sampled at regular intervals
+
+LCMSBOOL cmsSmoothSampledCurve(LPSAMPLEDCURVE Tab, double lambda)
+{
+ vec w, y, z;
+ int i, nItems;
+
+ nItems = Tab -> nItems;
+
+ if (nItems > MAX_KNOTS) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "cmsSmoothSampledCurve: too many points.");
+ return FALSE;
+ }
+
+ ZeroMemory(w, nItems * sizeof(float));
+ ZeroMemory(y, nItems * sizeof(float));
+ ZeroMemory(z, nItems * sizeof(float));
+
+ for (i=0; i < nItems; i++)
+ {
+ float value = (float) Tab -> Values[i];
+
+ y[i+1] = value;
+ w[i+1] = (float) ((value < 0.0) ? 0 : 1);
+ }
+
+
+ smooth2(w, y, z, (float) lambda, nItems);
+
+ for (i=0; i < nItems; i++) {
+
+ Tab -> Values[i] = z[i+1];;
+ }
+
+ return TRUE;
+
+}
+
+
+// Scale a value v, within domain Min .. Max
+// to a domain 0..(nPoints-1)
+
+static
+double ScaleVal(double v, double Min, double Max, int nPoints)
+{
+
+ double a, b;
+
+ if (v <= Min) return 0;
+ if (v >= Max) return (nPoints-1);
+
+ a = (double) (nPoints - 1) / (Max - Min);
+ b = a * Min;
+
+ return (a * v) - b;
+
+}
+
+
+// Does rescale a sampled curve to fit in a 0..(nPoints-1) domain
+
+void cmsRescaleSampledCurve(LPSAMPLEDCURVE p, double Min, double Max, int nPoints)
+{
+
+ int i;
+
+ for (i=0; i < p -> nItems; i++) {
+
+ double v = p -> Values[i];
+
+ p -> Values[i] = ScaleVal(v, Min, Max, nPoints);
+ }
+
+}
+
+
+// Joins two sampled curves for X and Y. Curves should be sorted.
+
+LPSAMPLEDCURVE cmsJoinSampledCurves(LPSAMPLEDCURVE X, LPSAMPLEDCURVE Y, int nResultingPoints)
+{
+ int i, j;
+ LPSAMPLEDCURVE out;
+ double MinX, MinY, MaxX, MaxY;
+ double x, y, x1, y1, x2, y2, a, b;
+
+ out = cmsAllocSampledCurve(nResultingPoints);
+ if (out == NULL)
+ return NULL;
+
+ if (X -> nItems != Y -> nItems) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "cmsJoinSampledCurves: invalid curve.");
+ cmsFreeSampledCurve(out);
+ return NULL;
+ }
+
+ // Get endpoints of sampled curves
+ cmsEndpointsOfSampledCurve(X, &MinX, &MaxX);
+ cmsEndpointsOfSampledCurve(Y, &MinY, &MaxY);
+
+
+ // Set our points
+ out ->Values[0] = MinY;
+ for (i=1; i < nResultingPoints; i++) {
+
+ // Scale t to x domain
+ x = (i * (MaxX - MinX) / (nResultingPoints-1)) + MinX;
+
+ // Find interval in which j is within (always up,
+ // since fn should be monotonic at all)
+
+ j = 1;
+ while ((j < X ->nItems - 1) && X ->Values[j] < x)
+ j++;
+
+ // Now x is within X[j-1], X[j]
+ x1 = X ->Values[j-1]; x2 = X ->Values[j];
+ y1 = Y ->Values[j-1]; y2 = Y ->Values[j];
+
+ // Interpolate the value
+ a = (y1 - y2) / (x1 - x2);
+ b = y1 - a * x1;
+ y = a* x + b;
+
+ out ->Values[i] = y;
+ }
+
+
+ cmsClampSampledCurve(out, MinY, MaxY);
+ return out;
+}
+
+
+
+// Convert between curve types
+
+LPGAMMATABLE cmsConvertSampledCurveToGamma(LPSAMPLEDCURVE Sampled, double Max)
+{
+ LPGAMMATABLE Gamma;
+ int i, nPoints;
+
+
+ nPoints = Sampled ->nItems;
+
+ Gamma = cmsAllocGamma(nPoints);
+ for (i=0; i < nPoints; i++) {
+
+ Gamma->GammaTable[i] = (WORD) floor(ScaleVal(Sampled ->Values[i], 0, Max, 65536) + .5);
+ }
+
+ return Gamma;
+
+}
+
+// Inverse of anterior
+
+LPSAMPLEDCURVE cmsConvertGammaToSampledCurve(LPGAMMATABLE Gamma, int nPoints)
+{
+ LPSAMPLEDCURVE Sampled;
+ L16PARAMS L16;
+ int i;
+ WORD wQuant, wValIn;
+
+ if (nPoints > 4096) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "cmsConvertGammaToSampledCurve: too many points (max=4096)");
+ return NULL;
+ }
+
+ cmsCalcL16Params(Gamma -> nEntries, &L16);
+
+ Sampled = cmsAllocSampledCurve(nPoints);
+ for (i=0; i < nPoints; i++) {
+ wQuant = _cmsQuantizeVal(i, nPoints);
+ wValIn = cmsLinearInterpLUT16(wQuant, Gamma ->GammaTable, &L16);
+ Sampled ->Values[i] = (float) wValIn;
+ }
+
+ return Sampled;
+}
+
+
+
+
+// Smooth endpoints (used in Black/White compensation)
+
+LCMSBOOL _cmsSmoothEndpoints(LPWORD Table, int nEntries)
+{
+ vec w, y, z;
+ int i, Zeros, Poles;
+
+
+
+ if (cmsIsLinear(Table, nEntries)) return FALSE; // Nothing to do
+
+
+ if (nEntries > MAX_KNOTS) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "_cmsSmoothEndpoints: too many points.");
+ return FALSE;
+ }
+
+ ZeroMemory(w, nEntries * sizeof(float));
+ ZeroMemory(y, nEntries * sizeof(float));
+ ZeroMemory(z, nEntries * sizeof(float));
+
+ for (i=0; i < nEntries; i++)
+ {
+ y[i+1] = (float) Table[i];
+ w[i+1] = 1.0;
+ }
+
+ w[1] = 65535.0;
+ w[nEntries] = 65535.0;
+
+ smooth2(w, y, z, (float) nEntries, nEntries);
+
+ // Do some reality - checking...
+ Zeros = Poles = 0;
+ for (i=nEntries; i > 1; --i) {
+
+ if (z[i] == 0.) Zeros++;
+ if (z[i] >= 65535.) Poles++;
+ if (z[i] < z[i-1]) return FALSE; // Non-Monotonic
+ }
+
+ if (Zeros > (nEntries / 3)) return FALSE; // Degenerated, mostly zeros
+ if (Poles > (nEntries / 3)) return FALSE; // Degenerated, mostly poles
+
+ // Seems ok
+
+ for (i=0; i < nEntries; i++) {
+
+ // Clamp to WORD
+
+ float v = z[i+1];
+
+ if (v < 0) v = 0;
+ if (v > 65535.) v = 65535.;
+
+ Table[i] = (WORD) floor(v + .5);
+ }
+
+ return TRUE;
+}
diff --git a/debian/lcms/lcms-1.19.dfsg2/src/cmsgmt.c b/debian/lcms/lcms-1.19.dfsg2/src/cmsgmt.c
new file mode 100755
index 00000000..74cde8b9
--- /dev/null
+++ b/debian/lcms/lcms-1.19.dfsg2/src/cmsgmt.c
@@ -0,0 +1,1243 @@
+//
+// Little cms
+// Copyright (C) 1998-2007 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 "lcms.h"
+
+/*
+Gamut check by default is a catching of 0xFFFF/0xFFFF/0xFFFF PCS values, used
+internally by lcms to hold invalid values. Matrix LUT's, operates in a way that
+unencodeable values are marked as this combination, if PCS is XYZ, this is a very
+high value since encoding is a 1.15 fixed point, something like 1.9997, 1.9997, 1.9997
+not a very common color after all. Lab PCS is not to be a problem, since L>100 are truely
+undefined. There is a posibility than ICC comitee defines L>100 as a valid means
+to use highlights, then it will be lost.
+
+(1.10 - Actually ICC did it, so this should be checked for full ICC 4.0 support)
+
+*/
+
+
+LCMSBOOL _cmsEndPointsBySpace(icColorSpaceSignature Space, WORD **White, WORD **Black,
+ int *nOutputs)
+{
+ // Only most common spaces
+
+ static WORD RGBblack[4] = { 0, 0, 0 };
+ static WORD RGBwhite[4] = { 0xffff, 0xffff, 0xffff };
+ static WORD CMYKblack[4] = { 0xffff, 0xffff, 0xffff, 0xffff }; // 400% of ink
+ static WORD CMYKwhite[4] = { 0, 0, 0, 0 };
+ static WORD LABblack[4] = { 0, 0x8000, 0x8000 };
+ static WORD LABwhite[4] = { 0xFF00, 0x8000, 0x8000 };
+ static WORD CMYblack[4] = { 0xffff, 0xffff, 0xffff };
+ static WORD CMYwhite[4] = { 0, 0, 0 };
+ static WORD Grayblack[4] = { 0 };
+ static WORD GrayWhite[4] = { 0xffff };
+
+ switch (Space) {
+
+ case icSigGrayData: if (White) *White = GrayWhite;
+ if (Black) *Black = Grayblack;
+ if (nOutputs) *nOutputs = 1;
+ return TRUE;
+
+ case icSigRgbData: if (White) *White = RGBwhite;
+ if (Black) *Black = RGBblack;
+ if (nOutputs) *nOutputs = 3;
+ return TRUE;
+
+ case icSigLabData: if (White) *White = LABwhite;
+ if (Black) *Black = LABblack;
+ if (nOutputs) *nOutputs = 3;
+ return TRUE;
+
+ case icSigCmykData: if (White) *White = CMYKwhite;
+ if (Black) *Black = CMYKblack;
+ if (nOutputs) *nOutputs = 4;
+ return TRUE;
+
+ case icSigCmyData: if (White) *White = CMYwhite;
+ if (Black) *Black = CMYblack;
+ if (nOutputs) *nOutputs = 3;
+ return TRUE;
+
+ default:;
+ }
+
+ return FALSE;
+}
+
+
+WORD *_cmsWhiteBySpace(icColorSpaceSignature Space)
+{
+ WORD *White= NULL, *Black = NULL;
+ int Dummy;
+ static WORD Default[MAXCHANNELS];
+
+ if (_cmsEndPointsBySpace(Space, &White, &Black, &Dummy))
+ return White;
+
+ return Default;
+
+}
+
+
+
+
+WORD Clamp_L(Fixed32 in)
+{
+ if (in == 0xFFFF) return 0xFFFFU; // Marker
+
+ if (in > 0xFF00) return 0xFF00U; // L* = 100.0
+ return (WORD) in;
+}
+
+
+#define ENCODE_AB(x) (WORD) (((x) + 128.0) * 256.0 + 0.5)
+
+WORD Clamp_ab(Fixed32 in)
+{
+ if (in == 0xFFFF) return 0xFFFFU; // Marker
+
+ if (in < 0) return ENCODE_AB(-128.0); // Max negative number
+ if (in > 0xFFFF) return ENCODE_AB(+127.9961); // Max positive number
+ return (WORD) in;
+}
+
+
+
+// Returns dE on two Lab values
+
+double LCMSEXPORT cmsDeltaE(LPcmsCIELab Lab1, LPcmsCIELab Lab2)
+{
+ double dL, da, db;
+
+ if (Lab1 -> L < 0 ||
+ Lab2 -> L < 0) return 65536.;
+
+ if (Lab1 -> a < -200 || Lab1 -> a > 200) return 65536.;
+ if (Lab1 -> b < -200 || Lab1 -> b > 200) return 65536.;
+
+ if (Lab2 -> a < -200 || Lab2 -> a > 200) return 65536.;
+ if (Lab2 -> b < -200 || Lab2 -> b > 200) return 65536.;
+
+ if (Lab1 ->L == 0 && Lab2 ->L == 0) return 0;
+
+ dL = fabs(Lab1 -> L - Lab2 -> L);
+ da = fabs(Lab1 -> a - Lab2 -> a);
+ db = fabs(Lab1 -> b - Lab2 -> b);
+
+ return pow(dL*dL + da * da + db * db, 0.5);
+
+}
+
+
+// Square
+static
+double Sqr(double v)
+{
+ return v * v;
+}
+
+// Return the CIE94 Delta E
+double LCMSEXPORT cmsCIE94DeltaE(LPcmsCIELab Lab1, LPcmsCIELab Lab2)
+{
+ cmsCIELCh LCh1, LCh2;
+ double dE, dL, dC, dh, dhsq;
+ double c12, sc, sh;
+
+ if (Lab1 ->L == 0 && Lab2 ->L == 0) return 0;
+
+ dL = fabs(Lab1 ->L - Lab2 ->L);
+
+ cmsLab2LCh(&LCh1, Lab1);
+ cmsLab2LCh(&LCh2, Lab2);
+
+ dC = fabs(LCh1.C - LCh2.C);
+ dE = cmsDeltaE(Lab1, Lab2);
+
+ dhsq = Sqr(dE) - Sqr(dL) - Sqr(dC);
+ if (dhsq < 0)
+ dh = 0;
+ else
+ dh = pow(dhsq, 0.5);
+
+ c12 = sqrt(LCh1.C * LCh2.C);
+
+ sc = 1.0 + (0.048 * c12);
+ sh = 1.0 + (0.014 * c12);
+
+ return sqrt(Sqr(dL) + Sqr(dC) / Sqr(sc) + Sqr(dh) / Sqr(sh));
+}
+
+
+// Auxiliary
+
+static
+double ComputeLBFD(LPcmsCIELab Lab)
+{
+ double yt;
+
+ if (Lab->L > 7.996969)
+ yt = (Sqr((Lab->L+16)/116)*((Lab->L+16)/116))*100;
+ else
+ yt = 100 * (Lab->L / 903.3);
+
+ return (54.6 * (LOGE * (log(yt + 1.5))) - 9.6);
+}
+
+
+
+// bfd - gets BFD(1:1) difference between Lab1, Lab2
+double LCMSEXPORT cmsBFDdeltaE(LPcmsCIELab Lab1, LPcmsCIELab Lab2)
+{
+ double lbfd1,lbfd2,AveC,Aveh,dE,deltaL,
+ deltaC,deltah,dc,t,g,dh,rh,rc,rt,bfd;
+ cmsCIELCh LCh1, LCh2;
+
+
+ if (Lab1 ->L == 0 && Lab2 ->L == 0) return 0;
+
+ lbfd1 = ComputeLBFD(Lab1);
+ lbfd2 = ComputeLBFD(Lab2);
+ deltaL = lbfd2 - lbfd1;
+
+ cmsLab2LCh(&LCh1, Lab1);
+ cmsLab2LCh(&LCh2, Lab2);
+
+ deltaC = LCh2.C - LCh1.C;
+ AveC = (LCh1.C+LCh2.C)/2;
+ Aveh = (LCh1.h+LCh2.h)/2;
+
+ dE = cmsDeltaE(Lab1, Lab2);
+
+ if (Sqr(dE)>(Sqr(Lab2->L-Lab1->L)+Sqr(deltaC)))
+ deltah = sqrt(Sqr(dE)-Sqr(Lab2->L-Lab1->L)-Sqr(deltaC));
+ else
+ deltah =0;
+
+
+ dc = 0.035 * AveC / (1 + 0.00365 * AveC)+0.521;
+ g = sqrt(Sqr(Sqr(AveC))/(Sqr(Sqr(AveC))+14000));
+ t = 0.627+(0.055*cos((Aveh-254)/(180/M_PI))-
+ 0.040*cos((2*Aveh-136)/(180/M_PI))+
+ 0.070*cos((3*Aveh-31)/(180/M_PI))+
+ 0.049*cos((4*Aveh+114)/(180/M_PI))-
+ 0.015*cos((5*Aveh-103)/(180/M_PI)));
+
+ dh = dc*(g*t+1-g);
+ rh = -0.260*cos((Aveh-308)/(180/M_PI))-
+ 0.379*cos((2*Aveh-160)/(180/M_PI))-
+ 0.636*cos((3*Aveh+254)/(180/M_PI))+
+ 0.226*cos((4*Aveh+140)/(180/M_PI))-
+ 0.194*cos((5*Aveh+280)/(180/M_PI));
+
+ rc = sqrt((AveC*AveC*AveC*AveC*AveC*AveC)/((AveC*AveC*AveC*AveC*AveC*AveC)+70000000));
+ rt = rh*rc;
+
+ bfd = sqrt(Sqr(deltaL)+Sqr(deltaC/dc)+Sqr(deltah/dh)+(rt*(deltaC/dc)*(deltah/dh)));
+
+ return bfd;
+}
+
+
+// cmc - CMC(1:1) difference between Lab1, Lab2
+double LCMSEXPORT cmsCMCdeltaE(LPcmsCIELab Lab1, LPcmsCIELab Lab2)
+{
+ double dE,dL,dC,dh,sl,sc,sh,t,f,cmc;
+ cmsCIELCh LCh1, LCh2;
+
+ if (Lab1 ->L == 0 && Lab2 ->L == 0) return 0;
+
+ cmsLab2LCh(&LCh1, Lab1);
+ cmsLab2LCh(&LCh2, Lab2);
+
+
+ dL = Lab2->L-Lab1->L;
+ dC = LCh2.C-LCh1.C;
+
+ dE = cmsDeltaE(Lab1, Lab2);
+ if (Sqr(dE)>(Sqr(dL)+Sqr(dC)))
+ dh = sqrt(Sqr(dE)-Sqr(dL)-Sqr(dC));
+ else
+ dh =0;
+
+ if ((LCh1.h > 164) && (LCh1.h<345))
+ t = 0.56 + fabs(0.2 * cos(((LCh1.h + 168)/(180/M_PI))));
+ else
+ t = 0.36 + fabs(0.4 * cos(((LCh1.h + 35 )/(180/M_PI))));
+
+ sc = 0.0638 * LCh1.C / (1 + 0.0131 * LCh1.C) + 0.638;
+ sl = 0.040975 * Lab1->L /(1 + 0.01765 * Lab1->L);
+
+ if (Lab1->L<16)
+ sl = 0.511;
+
+ f = sqrt((LCh1.C * LCh1.C * LCh1.C * LCh1.C)/((LCh1.C * LCh1.C * LCh1.C * LCh1.C)+1900));
+ sh = sc*(t*f+1-f);
+ cmc = sqrt(Sqr(dL/sl)+Sqr(dC/sc)+Sqr(dh/sh));
+
+ return cmc;
+}
+
+
+
+static
+double atan2deg(double b, double a)
+{
+ double h;
+
+ if (a == 0 && b == 0)
+ h = 0;
+ else
+ h = atan2(a, b);
+
+ h *= (180. / M_PI);
+
+ while (h > 360.)
+ h -= 360.;
+
+ while ( h < 0)
+ h += 360.;
+
+ return h;
+
+}
+
+
+static
+double RADIANES(double deg)
+{
+ return (deg * M_PI) / 180.;
+}
+
+
+// dE2000 The weightings KL, KC and KH can be modified to reflect the relative
+// importance of lightness, chroma and hue in different industrial applications
+
+double LCMSEXPORT cmsCIE2000DeltaE(LPcmsCIELab Lab1, LPcmsCIELab Lab2,
+ double Kl, double Kc, double Kh)
+{
+ double L1 = Lab1->L;
+ double a1 = Lab1->a;
+ double b1 = Lab1->b;
+ double C = sqrt( Sqr(a1) + Sqr(b1) );
+
+ double Ls = Lab2 ->L;
+ double as = Lab2 ->a;
+ double bs = Lab2 ->b;
+ double Cs = sqrt( Sqr(as) + Sqr(bs) );
+
+ double G = 0.5 * ( 1 - sqrt(pow((C + Cs) / 2 , 7.0) / (pow((C + Cs) / 2, 7.0) + pow(25.0, 7.0) ) ));
+
+ double a_p = (1 + G ) * a1;
+ double b_p = b1;
+ double C_p = sqrt( Sqr(a_p) + Sqr(b_p));
+ double h_p = atan2deg(a_p, b_p);
+
+
+ double a_ps = (1 + G) * as;
+ double b_ps = bs;
+ double C_ps = sqrt(Sqr(a_ps) + Sqr(b_ps));
+ double h_ps = atan2deg(a_ps, b_ps);
+
+ double meanC_p =(C_p + C_ps) / 2;
+
+ double hps_plus_hp = h_ps + h_p;
+ double hps_minus_hp = h_ps - h_p;
+
+ double meanh_p = fabs(hps_minus_hp) <= 180.000001 ? (hps_plus_hp)/2 :
+ (hps_plus_hp) < 360 ? (hps_plus_hp + 360)/2 :
+ (hps_plus_hp - 360)/2;
+
+ double delta_h = (hps_minus_hp) <= -180.000001 ? (hps_minus_hp + 360) :
+ (hps_minus_hp) > 180 ? (hps_minus_hp - 360) :
+ (hps_minus_hp);
+ double delta_L = (Ls - L1);
+ double delta_C = (C_ps - C_p );
+
+
+ double delta_H =2 * sqrt(C_ps*C_p) * sin(RADIANES(delta_h) / 2);
+
+ double T = 1 - 0.17 * cos(RADIANES(meanh_p-30))
+ + 0.24 * cos(RADIANES(2*meanh_p))
+ + 0.32 * cos(RADIANES(3*meanh_p + 6))
+ - 0.2 * cos(RADIANES(4*meanh_p - 63));
+
+ double Sl = 1 + (0.015 * Sqr((Ls + L1) /2- 50) )/ sqrt(20 + Sqr( (Ls+L1)/2 - 50) );
+
+ double Sc = 1 + 0.045 * (C_p + C_ps)/2;
+ double Sh = 1 + 0.015 * ((C_ps + C_p)/2) * T;
+
+ double delta_ro = 30 * exp( -Sqr(((meanh_p - 275 ) / 25)));
+
+ double Rc = 2 * sqrt(( pow(meanC_p, 7.0) )/( pow(meanC_p, 7.0) + pow(25.0, 7.0)));
+
+ double Rt = -sin(2 * RADIANES(delta_ro)) * Rc;
+
+ double deltaE00 = sqrt( Sqr(delta_L /(Sl * Kl)) +
+ Sqr(delta_C/(Sc * Kc)) +
+ Sqr(delta_H/(Sh * Kh)) +
+ Rt*(delta_C/(Sc * Kc)) * (delta_H / (Sh * Kh)));
+
+ return deltaE00;
+}
+
+
+
+// Carefully, clamp on CIELab space.
+
+void LCMSEXPORT cmsClampLab(LPcmsCIELab Lab, double amax, double amin,
+ double bmax, double bmin)
+{
+
+ // Whole Luma surface to zero
+
+ if (Lab -> L < 0) {
+
+ Lab-> L = Lab->a = Lab-> b = 0.0;
+ return;
+ }
+
+ // Clamp white, DISCARD HIGHLIGHTS. This is done
+ // in such way because icc spec doesn't allow the
+ // use of L>100 as a highlight means.
+
+ if (Lab->L > 100)
+ Lab -> L = 100;
+
+ // Check out gamut prism, on a, b faces
+
+ if (Lab -> a < amin || Lab->a > amax||
+ Lab -> b < bmin || Lab->b > bmax) {
+
+ cmsCIELCh LCh;
+ double h, slope;
+
+ // Falls outside a, b limits. Transports to LCh space,
+ // and then do the clipping
+
+
+ if (Lab -> a == 0.0) { // Is hue exactly 90?
+
+ // atan will not work, so clamp here
+ Lab -> b = Lab->b < 0 ? bmin : bmax;
+ return;
+ }
+
+ cmsLab2LCh(&LCh, Lab);
+
+ slope = Lab -> b / Lab -> a;
+ h = LCh.h;
+
+ // There are 4 zones
+
+ if ((h >= 0. && h < 45.) ||
+ (h >= 315 && h <= 360.)) {
+
+ // clip by amax
+ Lab -> a = amax;
+ Lab -> b = amax * slope;
+ }
+ else
+ if (h >= 45. && h < 135)
+ {
+ // clip by bmax
+ Lab -> b = bmax;
+ Lab -> a = bmax / slope;
+ }
+ else
+ if (h >= 135 && h < 225) {
+ // clip by amin
+ Lab -> a = amin;
+ Lab -> b = amin * slope;
+
+ }
+ else
+ if (h >= 225 && h < 315) {
+ // clip by bmin
+ Lab -> b = bmin;
+ Lab -> a = bmin / slope;
+ }
+ else
+ cmsSignalError(LCMS_ERRC_ABORTED, "Invalid angle");
+
+ }
+}
+
+// Several utilities -------------------------------------------------------
+
+// Translate from our colorspace to ICC representation
+
+icColorSpaceSignature LCMSEXPORT _cmsICCcolorSpace(int OurNotation)
+{
+ switch (OurNotation) {
+
+ case 1:
+ case PT_GRAY: return icSigGrayData;
+
+ case 2:
+ case PT_RGB: return icSigRgbData;
+
+ case PT_CMY: return icSigCmyData;
+ case PT_CMYK: return icSigCmykData;
+ case PT_YCbCr:return icSigYCbCrData;
+ case PT_YUV: return icSigLuvData;
+ case PT_XYZ: return icSigXYZData;
+ case PT_Lab: return icSigLabData;
+ case PT_YUVK: return icSigLuvKData;
+ case PT_HSV: return icSigHsvData;
+ case PT_HLS: return icSigHlsData;
+ case PT_Yxy: return icSigYxyData;
+ case PT_HiFi: return icSigHexachromeData;
+ case PT_HiFi7: return icSigHeptachromeData;
+ case PT_HiFi8: return icSigOctachromeData;
+
+ case PT_HiFi9: return icSigMCH9Data;
+ case PT_HiFi10: return icSigMCHAData;
+ case PT_HiFi11: return icSigMCHBData;
+ case PT_HiFi12: return icSigMCHCData;
+ case PT_HiFi13: return icSigMCHDData;
+ case PT_HiFi14: return icSigMCHEData;
+ case PT_HiFi15: return icSigMCHFData;
+
+ default: return icMaxEnumData;
+ }
+}
+
+
+int LCMSEXPORT _cmsLCMScolorSpace(icColorSpaceSignature ProfileSpace)
+{
+ switch (ProfileSpace) {
+
+ case icSigGrayData: return PT_GRAY;
+ case icSigRgbData: return PT_RGB;
+ case icSigCmyData: return PT_CMY;
+ case icSigCmykData: return PT_CMYK;
+ case icSigYCbCrData:return PT_YCbCr;
+ case icSigLuvData: return PT_YUV;
+ case icSigXYZData: return PT_XYZ;
+ case icSigLabData: return PT_Lab;
+ case icSigLuvKData: return PT_YUVK;
+ case icSigHsvData: return PT_HSV;
+ case icSigHlsData: return PT_HLS;
+ case icSigYxyData: return PT_Yxy;
+
+ case icSig6colorData:
+ case icSigHexachromeData: return PT_HiFi;
+
+ case icSigHeptachromeData:
+ case icSig7colorData: return PT_HiFi7;
+
+ case icSigOctachromeData:
+ case icSig8colorData: return PT_HiFi8;
+
+ case icSigMCH9Data:
+ case icSig9colorData: return PT_HiFi9;
+
+ case icSigMCHAData:
+ case icSig10colorData: return PT_HiFi10;
+
+ case icSigMCHBData:
+ case icSig11colorData: return PT_HiFi11;
+
+ case icSigMCHCData:
+ case icSig12colorData: return PT_HiFi12;
+
+ case icSigMCHDData:
+ case icSig13colorData: return PT_HiFi13;
+
+ case icSigMCHEData:
+ case icSig14colorData: return PT_HiFi14;
+
+ case icSigMCHFData:
+ case icSig15colorData: return PT_HiFi15;
+
+ default: return icMaxEnumData;
+ }
+}
+
+
+int LCMSEXPORT _cmsChannelsOf(icColorSpaceSignature ColorSpace)
+{
+
+ switch (ColorSpace) {
+
+ case icSigGrayData: return 1;
+
+ case icSig2colorData: return 2;
+
+ case icSigXYZData:
+ case icSigLabData:
+ case icSigLuvData:
+ case icSigYCbCrData:
+ case icSigYxyData:
+ case icSigRgbData:
+ case icSigHsvData:
+ case icSigHlsData:
+ case icSigCmyData:
+ case icSig3colorData: return 3;
+
+ case icSigLuvKData:
+ case icSigCmykData:
+ case icSig4colorData: return 4;
+
+ case icSigMCH5Data:
+ case icSig5colorData: return 5;
+
+ case icSigHexachromeData:
+ case icSig6colorData: return 6;
+
+ case icSigHeptachromeData:
+ case icSig7colorData: return 7;
+
+ case icSigOctachromeData:
+ case icSig8colorData: return 8;
+
+ case icSigMCH9Data:
+ case icSig9colorData: return 9;
+
+ case icSigMCHAData:
+ case icSig10colorData: return 10;
+
+ case icSigMCHBData:
+ case icSig11colorData: return 11;
+
+ case icSigMCHCData:
+ case icSig12colorData: return 12;
+
+ case icSigMCHDData:
+ case icSig13colorData: return 13;
+
+ case icSigMCHEData:
+ case icSig14colorData: return 14;
+
+ case icSigMCHFData:
+ case icSig15colorData: return 15;
+
+ default: return 3;
+ }
+
+}
+
+
+// v2 L=100 is supposed to be placed on 0xFF00. There is no reasonable
+// number of gridpoints that would make exact match. However, a
+// prelinearization of 258 entries, would map 0xFF00 on entry 257.
+// This is almost what we need, unfortunately, the rest of entries
+// should be scaled by (255*257/256) and this is not exact.
+//
+// An intermediate solution would be to use 257 entries. This does not
+// map 0xFF00 exactly on a node, but so close that the dE induced is
+// negligible. AND the rest of curve is exact.
+
+static
+void CreateLabPrelinearization(LPGAMMATABLE LabTable[])
+{
+ int i;
+
+ LabTable[0] = cmsAllocGamma(257);
+ LabTable[1] = cmsBuildGamma(257, 1.0);
+ LabTable[2] = cmsBuildGamma(257, 1.0);
+
+ // L* uses 257 entries. Entry 256 holds 0xFFFF, so, the effective range
+ // is 0..0xFF00. Last entry (257) is also collapsed to 0xFFFF
+
+ // From 0 to 0xFF00
+ for (i=0; i < 256; i++)
+ LabTable[0]->GammaTable[i] = RGB_8_TO_16(i);
+
+ // Repeat last for 0xFFFF
+ LabTable[0] ->GammaTable[256] = 0xFFFF;
+}
+
+
+// Used by gamut & softproofing
+
+typedef struct {
+
+ cmsHTRANSFORM hInput; // From whatever input color space. NULL for Lab
+ cmsHTRANSFORM hForward, hReverse; // Transforms going from Lab to colorant and back
+ double Thereshold; // The thereshold after which is considered out of gamut
+
+ } GAMUTCHAIN,FAR* LPGAMUTCHAIN;
+
+// This sampler does compute gamut boundaries by comparing original
+// values with a transform going back and forth. Values above ERR_THERESHOLD
+// of maximum are considered out of gamut.
+
+
+#define ERR_THERESHOLD 5
+
+
+static
+int GamutSampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
+{
+ LPGAMUTCHAIN t = (LPGAMUTCHAIN) Cargo;
+ WORD Proof[MAXCHANNELS], Check[MAXCHANNELS];
+ WORD Proof2[MAXCHANNELS], Check2[MAXCHANNELS];
+ cmsCIELab LabIn1, LabOut1;
+ cmsCIELab LabIn2, LabOut2;
+ double dE1, dE2, ErrorRatio;
+
+ // Assume in-gamut by default.
+ dE1 = 0.;
+ dE2 = 0;
+ ErrorRatio = 1.0;
+
+
+ // Any input space? I can use In[] no matter channels
+ // because is just one pixel
+
+ if (t -> hInput != NULL) cmsDoTransform(t -> hInput, In, In, 1);
+
+ // converts from PCS to colorant. This always
+ // does return in-gamut values,
+ cmsDoTransform(t -> hForward, In, Proof, 1);
+
+ // Now, do the inverse, from colorant to PCS.
+ cmsDoTransform(t -> hReverse, Proof, Check, 1);
+
+
+ // Try again, but this time taking Check as input
+ cmsDoTransform(t -> hForward, Check, Proof2, 1);
+ cmsDoTransform(t -> hReverse, Proof2, Check2, 1);
+
+
+
+ // Does the transform returns out-of-gamut?
+ if (Check[0] == 0xFFFF &&
+ Check[1] == 0xFFFF &&
+ Check[2] == 0xFFFF)
+
+ Out[0] = 0xFF00; // Out of gamut!
+ else {
+
+ // Transport encoded values
+ cmsLabEncoded2Float(&LabIn1, In);
+ cmsLabEncoded2Float(&LabOut1, Check);
+
+ // Take difference of direct value
+ dE1 = cmsDeltaE(&LabIn1, &LabOut1);
+
+ cmsLabEncoded2Float(&LabIn2, Check);
+ cmsLabEncoded2Float(&LabOut2, Check2);
+
+ // Take difference of converted value
+ dE2 = cmsDeltaE(&LabIn2, &LabOut2);
+
+
+ // if dE1 is small and dE2 is small, value is likely to be in gamut
+ if (dE1 < t->Thereshold && dE2 < t->Thereshold)
+ Out[0] = 0;
+ else
+ // if dE1 is small and dE2 is big, undefined. Assume in gamut
+ if (dE1 < t->Thereshold && dE2 > t->Thereshold)
+ Out[0] = 0;
+ else
+ // dE1 is big and dE2 is small, clearly out of gamut
+ if (dE1 > t->Thereshold && dE2 < t->Thereshold)
+ Out[0] = (WORD) _cmsQuickFloor((dE1 - t->Thereshold) + .5);
+ else {
+
+ // dE1 is big and dE2 is also big, could be due to perceptual mapping
+ // so take error ratio
+ if (dE2 == 0.0)
+ ErrorRatio = dE1;
+ else
+ ErrorRatio = dE1 / dE2;
+
+ if (ErrorRatio > t->Thereshold)
+ Out[0] = (WORD) _cmsQuickFloor((ErrorRatio - t->Thereshold) + .5);
+ else
+ Out[0] = 0;
+ }
+
+ }
+
+ return TRUE;
+}
+
+
+// Does compute a gamut LUT going back and forth across
+// pcs -> relativ. colorimetric intent -> pcs
+// the dE obtained is then annotated on the LUT.
+// values truely out of gamut, are clipped to dE = 0xFFFE
+// and values changed are supposed to be handled by
+// any gamut remapping, so, are out of gamut as well.
+//
+// **WARNING: This algorithm does assume that gamut
+// remapping algorithms does NOT move in-gamut colors,
+// of course, many perceptual and saturation intents does
+// not work in such way, but relativ. ones should.
+
+static
+LPLUT ComputeGamutWithInput(cmsHPROFILE hInput, cmsHPROFILE hProfile, int Intent)
+{
+ cmsHPROFILE hLab;
+ LPLUT Gamut;
+ DWORD dwFormat;
+ GAMUTCHAIN Chain;
+ int nErrState, nChannels, nGridpoints;
+ LPGAMMATABLE Trans[3];
+ icColorSpaceSignature ColorSpace;
+
+
+ ZeroMemory(&Chain, sizeof(GAMUTCHAIN));
+
+ hLab = cmsCreateLabProfile(NULL);
+
+ // Safeguard against early abortion
+ nErrState = cmsErrorAction(LCMS_ERROR_IGNORE);
+
+ // The figure of merit. On matrix-shaper profiles, should be almost zero as
+ // the conversion is pretty exact. On LUT based profiles, different resolutions
+ // of input and output CLUT may result in differences.
+
+ if (!cmsIsIntentSupported(hProfile, Intent, LCMS_USED_AS_INPUT) &&
+ !cmsIsIntentSupported(hProfile, Intent, LCMS_USED_AS_OUTPUT))
+
+ Chain.Thereshold = 1.0;
+ else
+ Chain.Thereshold = ERR_THERESHOLD;
+
+ ColorSpace = cmsGetColorSpace(hProfile);
+
+ // If input profile specified, create a transform from such profile to Lab
+ if (hInput != NULL) {
+
+ nChannels = _cmsChannelsOf(ColorSpace);
+ nGridpoints = _cmsReasonableGridpointsByColorspace(ColorSpace, cmsFLAGS_HIGHRESPRECALC);
+ dwFormat = (CHANNELS_SH(nChannels)|BYTES_SH(2));
+
+ Chain.hInput = cmsCreateTransform(hInput, dwFormat,
+ hLab, TYPE_Lab_16,
+ Intent,
+ cmsFLAGS_NOTPRECALC);
+ }
+ else {
+ // Input transform=NULL (Lab) Used to compute the gamut tag
+ // This table will take 53 points to give some accurancy,
+ // 53 * 53 * 53 * 2 = 291K
+
+ nChannels = 3; // For Lab
+ nGridpoints = 53;
+ Chain.hInput = NULL;
+ dwFormat = (CHANNELS_SH(_cmsChannelsOf(ColorSpace))|BYTES_SH(2));
+ }
+
+
+ // Does create the forward step
+ Chain.hForward = cmsCreateTransform(hLab, TYPE_Lab_16,
+ hProfile, dwFormat,
+ INTENT_RELATIVE_COLORIMETRIC,
+ cmsFLAGS_NOTPRECALC);
+
+ // Does create the backwards step
+ Chain.hReverse = cmsCreateTransform(hProfile, dwFormat,
+ hLab, TYPE_Lab_16,
+ INTENT_RELATIVE_COLORIMETRIC,
+ cmsFLAGS_NOTPRECALC);
+
+ // Restores error handler previous state
+ cmsErrorAction(nErrState);
+
+
+ // All ok?
+ if (Chain.hForward && Chain.hReverse) {
+
+ // Go on, try to compute gamut LUT from PCS.
+ // This consist on a single channel containing
+ // dE when doing a transform back and forth on
+ // the colorimetric intent.
+
+ Gamut = cmsAllocLUT();
+ Gamut = cmsAlloc3DGrid(Gamut, nGridpoints, nChannels, 1);
+
+ // If no input, then this is a gamut tag operated by Lab,
+ // so include pertinent prelinearization
+ if (hInput == NULL) {
+
+ CreateLabPrelinearization(Trans);
+ cmsAllocLinearTable(Gamut, Trans, 1);
+ cmsFreeGammaTriple(Trans);
+ }
+
+
+ cmsSample3DGrid(Gamut, GamutSampler, (LPVOID) &Chain, Gamut ->wFlags);
+ }
+ else
+ Gamut = NULL; // Didn't work...
+
+ // Free all needed stuff.
+ if (Chain.hInput) cmsDeleteTransform(Chain.hInput);
+ if (Chain.hForward) cmsDeleteTransform(Chain.hForward);
+ if (Chain.hReverse) cmsDeleteTransform(Chain.hReverse);
+
+ cmsCloseProfile(hLab);
+
+ // And return computed hull
+ return Gamut;
+}
+
+
+// Wrapper
+
+LPLUT _cmsComputeGamutLUT(cmsHPROFILE hProfile, int Intent)
+{
+ return ComputeGamutWithInput(NULL, hProfile, Intent);
+}
+
+
+// This routine does compute the gamut check CLUT. This CLUT goes from whatever
+// input space to the 0 or != 0 gamut check.
+
+LPLUT _cmsPrecalculateGamutCheck(cmsHTRANSFORM h)
+{
+ _LPcmsTRANSFORM p = (_LPcmsTRANSFORM) h;
+
+ return ComputeGamutWithInput(p->InputProfile, p ->PreviewProfile, p->Intent);
+}
+
+
+// SoftProofing. Convert from Lab to device, then back to Lab,
+// any gamut remapping is applied
+
+static
+int SoftProofSampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
+{
+ LPGAMUTCHAIN t = (LPGAMUTCHAIN) Cargo;
+ WORD Colorant[MAXCHANNELS];
+
+ // From pcs to colorant
+ cmsDoTransform(t -> hForward, In, Colorant, 1);
+
+ // Now, do the inverse, from colorant to pcs.
+ cmsDoTransform(t -> hReverse, Colorant, Out, 1);
+
+ return TRUE;
+}
+
+// Does return Softproofing LUT on desired intent
+
+LPLUT _cmsComputeSoftProofLUT(cmsHPROFILE hProfile, int nIntent)
+{
+ cmsHPROFILE hLab;
+ LPLUT SoftProof;
+ DWORD dwFormat;
+ GAMUTCHAIN Chain;
+ int nErrState;
+ LPGAMMATABLE Trans[3];
+
+
+ // LUTs are never abs. colorimetric, is the transform who
+ // is responsible of generating white point displacement
+ if (nIntent == INTENT_ABSOLUTE_COLORIMETRIC)
+ nIntent = INTENT_RELATIVE_COLORIMETRIC;
+
+ ZeroMemory(&Chain, sizeof(GAMUTCHAIN));
+
+ hLab = cmsCreateLabProfile(NULL);
+
+ // ONLY 4 channels
+ dwFormat = (CHANNELS_SH(4)|BYTES_SH(2));
+
+ // Safeguard against early abortion
+ nErrState = cmsErrorAction(LCMS_ERROR_IGNORE);
+
+ // Does create the first step
+ Chain.hForward = cmsCreateTransform(hLab, TYPE_Lab_16,
+ hProfile, dwFormat,
+ nIntent,
+ cmsFLAGS_NOTPRECALC);
+
+ // Does create the last step
+ Chain.hReverse = cmsCreateTransform(hProfile, dwFormat,
+ hLab, TYPE_Lab_16,
+ INTENT_RELATIVE_COLORIMETRIC,
+ cmsFLAGS_NOTPRECALC);
+
+ // Restores error handler previous state
+ cmsErrorAction(nErrState);
+
+ // All ok?
+ if (Chain.hForward && Chain.hReverse) {
+
+ // This is Lab -> Lab, so 33 point should hold anything
+ SoftProof = cmsAllocLUT();
+ SoftProof = cmsAlloc3DGrid(SoftProof, 33, 3, 3);
+
+ CreateLabPrelinearization(Trans);
+ cmsAllocLinearTable(SoftProof, Trans, 1);
+ cmsFreeGammaTriple(Trans);
+
+ cmsSample3DGrid(SoftProof, SoftProofSampler, (LPVOID) &Chain, SoftProof->wFlags);
+ }
+ else
+ SoftProof = NULL; // Didn't work...
+
+ // Free all needed stuff.
+ if (Chain.hForward) cmsDeleteTransform(Chain.hForward);
+ if (Chain.hReverse) cmsDeleteTransform(Chain.hReverse);
+
+ cmsCloseProfile(hLab);
+
+ return SoftProof;
+}
+
+
+static
+int MostlyLinear(WORD Table[], int nEntries)
+{
+ register int i;
+ int diff;
+
+ for (i=5; i < nEntries; i++) {
+
+ diff = abs((int) Table[i] - (int) _cmsQuantizeVal(i, nEntries));
+ if (diff > 0x0300)
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static
+void SlopeLimiting(WORD Table[], int nEntries)
+{
+ int At = (int) floor((double) nEntries * 0.02 + 0.5); // Cutoff at 2%
+ double Val, Slope;
+ int i;
+
+ Val = Table[At];
+ Slope = Val / At;
+
+ for (i=0; i < At; i++)
+ Table[i] = (WORD) floor(i * Slope + 0.5);
+
+}
+
+
+// Check for monotonicity.
+
+static
+LCMSBOOL IsMonotonic(LPGAMMATABLE t)
+{
+ int n = t -> nEntries;
+ int i, last;
+
+ last = t ->GammaTable[n-1];
+
+ for (i = n-2; i >= 0; --i) {
+
+ if (t ->GammaTable[i] > last)
+
+ return FALSE;
+ else
+ last = t ->GammaTable[i];
+
+ }
+
+ return TRUE;
+}
+
+// Check for endpoints
+
+static
+LCMSBOOL HasProperEndpoints(LPGAMMATABLE t)
+{
+ if (t ->GammaTable[0] != 0) return FALSE;
+ if (t ->GammaTable[t ->nEntries-1] != 0xFFFF) return FALSE;
+
+ return TRUE;
+}
+
+
+
+#define PRELINEARIZATION_POINTS 4096
+
+// Fixes the gamma balancing of transform. Thanks to Mike Chaney
+// for pointing this subtle bug.
+
+void _cmsComputePrelinearizationTablesFromXFORM(cmsHTRANSFORM h[], int nTransforms, LPLUT Grid)
+{
+ LPGAMMATABLE Trans[MAXCHANNELS];
+ unsigned int t, i, v;
+ int j;
+ WORD In[MAXCHANNELS], Out[MAXCHANNELS];
+ LCMSBOOL lIsSuitable;
+ _LPcmsTRANSFORM InputXForm = (_LPcmsTRANSFORM) h[0];
+ _LPcmsTRANSFORM OutputXForm = (_LPcmsTRANSFORM) h[nTransforms-1];
+
+
+ // First space is *Lab, use our specialized curves for v2 Lab
+
+ if (InputXForm ->EntryColorSpace == icSigLabData &&
+ OutputXForm->ExitColorSpace != icSigLabData) {
+
+ CreateLabPrelinearization(Trans);
+ cmsAllocLinearTable(Grid, Trans, 1);
+ cmsFreeGammaTriple(Trans);
+ return;
+ }
+
+
+ // Do nothing on all but Gray/RGB to Gray/RGB transforms
+
+ if (((InputXForm ->EntryColorSpace != icSigRgbData) && (InputXForm ->EntryColorSpace != icSigGrayData)) ||
+ ((OutputXForm->ExitColorSpace != icSigRgbData) && (OutputXForm->ExitColorSpace != icSigGrayData))) return;
+
+
+ for (t = 0; t < Grid -> InputChan; t++)
+ Trans[t] = cmsAllocGamma(PRELINEARIZATION_POINTS);
+
+ for (i=0; i < PRELINEARIZATION_POINTS; i++) {
+
+ v = _cmsQuantizeVal(i, PRELINEARIZATION_POINTS);
+
+ for (t=0; t < Grid -> InputChan; t++)
+ In[t] = (WORD) v;
+
+ cmsDoTransform(h[0], In, Out, 1);
+ for (j=1; j < nTransforms; j++)
+ cmsDoTransform(h[j], Out, Out, 1);
+
+ for (t=0; t < Grid -> InputChan; t++)
+ Trans[t] ->GammaTable[i] = Out[t];
+
+ }
+
+
+ // Check transfer curves
+ lIsSuitable = TRUE;
+ for (t=0; (lIsSuitable && (t < Grid->InputChan)); t++) {
+
+
+ // Exclude if already linear
+ if (MostlyLinear(Trans[t]->GammaTable, PRELINEARIZATION_POINTS))
+ lIsSuitable = FALSE;
+
+ // Exclude if non-monotonic
+ if (!IsMonotonic(Trans[t]))
+ lIsSuitable = FALSE;
+
+ // Exclude if weird endpoints
+ if (!HasProperEndpoints(Trans[t]))
+ lIsSuitable = FALSE;
+
+ /*
+ // Exclude if transfer function is not smooth enough
+ // to be modelled as a gamma function, or the gamma is reversed
+
+ if (cmsEstimateGamma(Trans[t]) < 1.0)
+ lIsSuitable = FALSE;
+ */
+
+ }
+
+ if (lIsSuitable) {
+
+ for (t = 0; t < Grid ->InputChan; t++)
+ SlopeLimiting(Trans[t]->GammaTable, Trans[t]->nEntries);
+ }
+
+ if (lIsSuitable) cmsAllocLinearTable(Grid, Trans, 1);
+
+
+ for (t = 0; t < Grid ->InputChan; t++)
+ cmsFreeGamma(Trans[t]);
+
+
+}
+
+
+// Compute K -> L* relationship. Flags may include black point compensation. In this case,
+// the relationship is assumed from the profile with BPC to a black point zero.
+static
+LPGAMMATABLE ComputeKToLstar(cmsHPROFILE hProfile, int nPoints, int Intent, DWORD dwFlags)
+{
+ LPGAMMATABLE out;
+ int i;
+ WORD cmyk[4], wLab[3];
+ cmsHPROFILE hLab = cmsCreateLabProfile(NULL);
+ cmsHTRANSFORM xform = cmsCreateTransform(hProfile, TYPE_CMYK_16,
+ hLab, TYPE_Lab_16,
+ Intent, (dwFlags|cmsFLAGS_NOTPRECALC));
+
+
+ out = cmsAllocGamma(nPoints);
+ for (i=0; i < nPoints; i++) {
+
+ cmyk[0] = 0;
+ cmyk[1] = 0;
+ cmyk[2] = 0;
+ cmyk[3] = _cmsQuantizeVal(i, nPoints);
+
+ cmsDoTransform(xform, cmyk, wLab, 1);
+ out->GammaTable[i] = (WORD) (0xFFFF - wLab[0]);
+ }
+
+ cmsDeleteTransform(xform);
+ cmsCloseProfile(hLab);
+
+ return out;
+}
+
+
+
+// Compute Black tone curve on a CMYK -> CMYK transform. This is done by
+// using the proof direction on both profiles to find K->L* relationship
+// then joining both curves. dwFlags may include black point compensation.
+
+LPGAMMATABLE _cmsBuildKToneCurve(cmsHTRANSFORM hCMYK2CMYK, int nPoints)
+{
+ LPGAMMATABLE in, out;
+ LPGAMMATABLE KTone;
+ _LPcmsTRANSFORM p = (_LPcmsTRANSFORM) hCMYK2CMYK;
+
+
+ // Make sure CMYK -> CMYK
+ if (p -> EntryColorSpace != icSigCmykData ||
+ p -> ExitColorSpace != icSigCmykData) return NULL;
+
+ // Create individual curves. BPC works also as each K to L* is
+ // computed as a BPC to zero black point in case of L*
+ in = ComputeKToLstar(p ->InputProfile, nPoints, p->Intent, p -> dwOriginalFlags);
+ out = ComputeKToLstar(p ->OutputProfile, nPoints, p->Intent, p -> dwOriginalFlags);
+
+ // Build the relationship
+ KTone = cmsJoinGamma(in, out);
+
+ cmsFreeGamma(in); cmsFreeGamma(out);
+
+ // Make sure it is monotonic
+
+ if (!IsMonotonic(KTone)) {
+
+ cmsFreeGamma(KTone);
+ return NULL;
+ }
+
+
+ return KTone;
+}
diff --git a/debian/lcms/lcms-1.19.dfsg2/src/cmsintrp.c b/debian/lcms/lcms-1.19.dfsg2/src/cmsintrp.c
new file mode 100755
index 00000000..02ccb53b
--- /dev/null
+++ b/debian/lcms/lcms-1.19.dfsg2/src/cmsintrp.c
@@ -0,0 +1,1103 @@
+//
+// Little cms
+// Copyright (C) 1998-2007 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.
+
+// Interpolation
+
+#include "lcms.h"
+
+void cmsCalcL16Params(int nSamples, LPL16PARAMS p)
+{
+ p -> nSamples = nSamples;
+ p -> Domain = (WORD) (nSamples - 1);
+ p -> nInputs = p -> nOutputs = 1;
+
+}
+
+
+
+// Eval gray LUT having only one input channel
+
+static
+void Eval1Input(WORD StageABC[], WORD StageLMN[], WORD LutTable[], LPL16PARAMS p16)
+{
+ Fixed32 fk;
+ Fixed32 k0, k1, rk, K0, K1;
+ int OutChan;
+
+ fk = ToFixedDomain((Fixed32) StageABC[0] * p16 -> Domain);
+ k0 = FIXED_TO_INT(fk);
+ rk = (WORD) FIXED_REST_TO_INT(fk);
+
+ k1 = k0 + (StageABC[0] != 0xFFFFU ? 1 : 0);
+
+ K0 = p16 -> opta1 * k0;
+ K1 = p16 -> opta1 * k1;
+
+ for (OutChan=0; OutChan < p16->nOutputs; OutChan++) {
+
+ StageLMN[OutChan] = (WORD) FixedLERP(rk, LutTable[K0+OutChan],
+ LutTable[K1+OutChan]);
+ }
+}
+
+
+
+// For more that 3 inputs (i.e., CMYK)
+// evaluate two 3-dimensional interpolations and then linearly interpolate between them.
+static
+void Eval4Inputs(WORD StageABC[], WORD StageLMN[], WORD LutTable[], LPL16PARAMS p16)
+{
+ Fixed32 fk;
+ Fixed32 k0, rk;
+ int K0, K1;
+ LPWORD T;
+ int i;
+ WORD Tmp1[MAXCHANNELS], Tmp2[MAXCHANNELS];
+
+
+ fk = ToFixedDomain((Fixed32) StageABC[0] * p16 -> Domain);
+ k0 = FIXED_TO_INT(fk);
+ rk = FIXED_REST_TO_INT(fk);
+
+ K0 = p16 -> opta4 * k0;
+ K1 = p16 -> opta4 * (k0 + (StageABC[0] != 0xFFFFU ? 1 : 0));
+
+ p16 -> nInputs = 3;
+
+ T = LutTable + K0;
+
+ cmsTetrahedralInterp16(StageABC + 1, Tmp1, T, p16);
+
+
+ T = LutTable + K1;
+
+ cmsTetrahedralInterp16(StageABC + 1, Tmp2, T, p16);
+
+
+ p16 -> nInputs = 4;
+ for (i=0; i < p16 -> nOutputs; i++)
+ {
+ StageLMN[i] = (WORD) FixedLERP(rk, Tmp1[i], Tmp2[i]);
+
+ }
+
+}
+
+
+static
+void Eval5Inputs(WORD StageABC[], WORD StageLMN[], WORD LutTable[], LPL16PARAMS p16)
+{
+ Fixed32 fk;
+ Fixed32 k0, rk;
+ int K0, K1;
+ LPWORD T;
+ int i;
+ WORD Tmp1[MAXCHANNELS], Tmp2[MAXCHANNELS];
+
+
+ fk = ToFixedDomain((Fixed32) StageABC[0] * p16 -> Domain);
+ k0 = FIXED_TO_INT(fk);
+ rk = FIXED_REST_TO_INT(fk);
+
+ K0 = p16 -> opta5 * k0;
+ K1 = p16 -> opta5 * (k0 + (StageABC[0] != 0xFFFFU ? 1 : 0));
+
+ p16 -> nInputs = 4;
+
+ T = LutTable + K0;
+
+ Eval4Inputs(StageABC + 1, Tmp1, T, p16);
+
+ T = LutTable + K1;
+
+ Eval4Inputs(StageABC + 1, Tmp2, T, p16);
+
+ p16 -> nInputs = 5;
+ for (i=0; i < p16 -> nOutputs; i++)
+ {
+ StageLMN[i] = (WORD) FixedLERP(rk, Tmp1[i], Tmp2[i]);
+
+ }
+
+}
+
+
+static
+void Eval6Inputs(WORD StageABC[], WORD StageLMN[], WORD LutTable[], LPL16PARAMS p16)
+{
+ Fixed32 fk;
+ Fixed32 k0, rk;
+ int K0, K1;
+ LPWORD T;
+ int i;
+ WORD Tmp1[MAXCHANNELS], Tmp2[MAXCHANNELS];
+
+
+ fk = ToFixedDomain((Fixed32) StageABC[0] * p16 -> Domain);
+ k0 = FIXED_TO_INT(fk);
+ rk = FIXED_REST_TO_INT(fk);
+
+ K0 = p16 -> opta6 * k0;
+ K1 = p16 -> opta6 * (k0 + (StageABC[0] != 0xFFFFU ? 1 : 0));
+
+ p16 -> nInputs = 5;
+
+ T = LutTable + K0;
+
+ Eval5Inputs(StageABC + 1, Tmp1, T, p16);
+
+ T = LutTable + K1;
+
+ Eval5Inputs(StageABC + 1, Tmp2, T, p16);
+
+ p16 -> nInputs = 6;
+ for (i=0; i < p16 -> nOutputs; i++)
+ {
+ StageLMN[i] = (WORD) FixedLERP(rk, Tmp1[i], Tmp2[i]);
+ }
+
+}
+
+static
+void Eval7Inputs(WORD StageABC[], WORD StageLMN[], WORD LutTable[], LPL16PARAMS p16)
+{
+ Fixed32 fk;
+ Fixed32 k0, rk;
+ int K0, K1;
+ LPWORD T;
+ int i;
+ WORD Tmp1[MAXCHANNELS], Tmp2[MAXCHANNELS];
+
+
+ fk = ToFixedDomain((Fixed32) StageABC[0] * p16 -> Domain);
+ k0 = FIXED_TO_INT(fk);
+ rk = FIXED_REST_TO_INT(fk);
+
+ K0 = p16 -> opta7 * k0;
+ K1 = p16 -> opta7 * (k0 + (StageABC[0] != 0xFFFFU ? 1 : 0));
+
+ p16 -> nInputs = 6;
+
+ T = LutTable + K0;
+
+ Eval6Inputs(StageABC + 1, Tmp1, T, p16);
+
+ T = LutTable + K1;
+
+ Eval6Inputs(StageABC + 1, Tmp2, T, p16);
+
+ p16 -> nInputs = 7;
+ for (i=0; i < p16 -> nOutputs; i++)
+ {
+ StageLMN[i] = (WORD) FixedLERP(rk, Tmp1[i], Tmp2[i]);
+ }
+
+}
+
+static
+void Eval8Inputs(WORD StageABC[], WORD StageLMN[], WORD LutTable[], LPL16PARAMS p16)
+{
+ Fixed32 fk;
+ Fixed32 k0, rk;
+ int K0, K1;
+ LPWORD T;
+ int i;
+ WORD Tmp1[MAXCHANNELS], Tmp2[MAXCHANNELS];
+
+
+ fk = ToFixedDomain((Fixed32) StageABC[0] * p16 -> Domain);
+ k0 = FIXED_TO_INT(fk);
+ rk = FIXED_REST_TO_INT(fk);
+
+ K0 = p16 -> opta8 * k0;
+ K1 = p16 -> opta8 * (k0 + (StageABC[0] != 0xFFFFU ? 1 : 0));
+
+ p16 -> nInputs = 7;
+
+ T = LutTable + K0;
+
+ Eval7Inputs(StageABC + 1, Tmp1, T, p16);
+
+ T = LutTable + K1;
+
+ Eval7Inputs(StageABC + 1, Tmp2, T, p16);
+
+ p16 -> nInputs = 8;
+ for (i=0; i < p16 -> nOutputs; i++)
+ {
+ StageLMN[i] = (WORD) FixedLERP(rk, Tmp1[i], Tmp2[i]);
+ }
+
+}
+
+
+// Fills optimization parameters
+
+void cmsCalcCLUT16ParamsEx(int nSamples, int InputChan, int OutputChan,
+ LCMSBOOL lUseTetrahedral, LPL16PARAMS p)
+{
+ int clutPoints;
+
+ cmsCalcL16Params(nSamples, p);
+
+ p -> nInputs = InputChan;
+ p -> nOutputs = OutputChan;
+
+ clutPoints = p -> Domain + 1;
+
+ p -> opta1 = p -> nOutputs; // Z
+ p -> opta2 = p -> opta1 * clutPoints; // Y
+ p -> opta3 = p -> opta2 * clutPoints; // X
+ p -> opta4 = p -> opta3 * clutPoints; // Used only in 4 inputs LUT
+ p -> opta5 = p -> opta4 * clutPoints; // Used only in 5 inputs LUT
+ p -> opta6 = p -> opta5 * clutPoints; // Used only on 6 inputs LUT
+ p -> opta7 = p -> opta6 * clutPoints; // Used only on 7 inputs LUT
+ p -> opta8 = p -> opta7 * clutPoints; // Used only on 8 inputs LUT
+
+
+ switch (InputChan) {
+
+
+ case 1: // Gray LUT
+
+ p ->Interp3D = Eval1Input;
+ break;
+
+ case 3: // RGB et al
+ if (lUseTetrahedral) {
+ p ->Interp3D = cmsTetrahedralInterp16;
+ }
+ else
+ p ->Interp3D = cmsTrilinearInterp16;
+ break;
+
+ case 4: // CMYK LUT
+ p ->Interp3D = Eval4Inputs;
+ break;
+
+ case 5: // 5 Inks
+ p ->Interp3D = Eval5Inputs;
+ break;
+
+ case 6: // 6 Inks
+ p -> Interp3D = Eval6Inputs;
+ break;
+
+ case 7: // 7 inks
+ p ->Interp3D = Eval7Inputs;
+ break;
+
+ case 8: // 8 inks
+ p ->Interp3D = Eval8Inputs;
+ break;
+
+ default:
+ cmsSignalError(LCMS_ERRC_ABORTED, "Unsupported restoration (%d channels)", InputChan);
+ }
+
+}
+
+
+void cmsCalcCLUT16Params(int nSamples, int InputChan, int OutputChan, LPL16PARAMS p)
+{
+ cmsCalcCLUT16ParamsEx(nSamples, InputChan, OutputChan, FALSE, p);
+}
+
+
+
+#ifdef USE_FLOAT
+
+
+// Floating-point version
+
+WORD cmsLinearInterpLUT16(WORD Value, WORD LutTable[], LPL16PARAMS p)
+{
+ double y1, y0;
+ double y;
+ double val2, rest;
+ int cell0, cell1;
+
+ // if last value...
+
+ if (Value == 0xffff) return LutTable[p -> Domain];
+
+ val2 = p -> Domain * ((double) Value / 65535.0);
+
+ cell0 = (int) floor(val2);
+ cell1 = (int) ceil(val2);
+
+ // Rest is 16 LSB bits
+
+ rest = val2 - cell0;
+
+ y0 = LutTable[cell0] ;
+ y1 = LutTable[cell1] ;
+
+ y = y0 + (y1 - y0) * rest;
+
+
+ return (WORD) floor(y+.5);
+}
+
+#endif
+
+
+//
+// Linear interpolation (Fixed-point optimized, but C source)
+//
+
+
+#ifdef USE_C
+
+WORD cmsLinearInterpLUT16(WORD Value1, WORD LutTable[], LPL16PARAMS p)
+{
+ WORD y1, y0;
+ WORD y;
+ int dif, a1;
+ int cell0, rest;
+ int val3, Value;
+
+ // if last value...
+
+
+ Value = Value1;
+ if (Value == 0xffff) return LutTable[p -> Domain];
+
+ val3 = p -> Domain * Value;
+ val3 = ToFixedDomain(val3); // To fixed 15.16
+
+ cell0 = FIXED_TO_INT(val3); // Cell is 16 MSB bits
+ rest = FIXED_REST_TO_INT(val3); // Rest is 16 LSB bits
+
+ y0 = LutTable[cell0] ;
+ y1 = LutTable[cell0+1] ;
+
+ dif = (int) y1 - y0; // dif is in domain -ffff ... ffff
+
+ if (dif >= 0)
+ {
+ a1 = ToFixedDomain(dif * rest);
+ a1 += 0x8000;
+ }
+ else
+ {
+ a1 = ToFixedDomain((- dif) * rest);
+ a1 -= 0x8000;
+ a1 = -a1;
+ }
+
+ y = (WORD) (y0 + FIXED_TO_INT(a1));
+
+ return y;
+}
+
+#endif
+
+// Linear interpolation (asm by hand optimized)
+
+#ifdef USE_ASSEMBLER
+
+#ifdef _MSC_VER
+#pragma warning(disable : 4033)
+#pragma warning(disable : 4035)
+#endif
+
+WORD cmsLinearInterpLUT16(WORD Value, WORD LutTable[], LPL16PARAMS p)
+{
+ int xDomain = p -> Domain;
+
+
+ if (Value == 0xffff) return LutTable[p -> Domain];
+ else
+ ASM {
+ xor eax, eax
+ mov ax, word ptr ss:Value
+ mov edx, ss:xDomain
+ mul edx // val3 = p -> Domain * Value;
+ shld edx, eax, 16 // Convert it to fixed 15.16
+ shl eax, 16 // * 65536 / 65535
+ mov ebx, 0x0000ffff
+ div ebx
+ mov ecx, eax
+ sar ecx, 16 // ecx = cell0
+ mov edx, eax // rest = (val2 & 0xFFFFU)
+ and edx, 0x0000ffff // edx = rest
+ mov ebx, ss:LutTable
+ lea eax, dword ptr [ebx+2*ecx] // Ptr to LUT
+ xor ebx, ebx
+ mov bx, word ptr [eax] // EBX = y0
+ movzx eax, word ptr [eax+2] // EAX = y1
+ sub eax, ebx // EAX = y1-y0
+ js IsNegative
+ mul edx // EAX = EAX * rest
+ shld edx, eax, 16 // Pass it to fixed
+ sal eax, 16 // * 65536 / 65535
+ mov ecx, 0x0000ffff
+ div ecx
+ add eax, 0x8000 // Rounding
+ sar eax, 16
+ add eax, ebx // Done!
+ jmp end
+
+ IsNegative:
+
+ neg eax
+ mul edx // EAX = EAX * rest
+ shld edx, eax, 16 // Pass it to fixed
+ sal eax, 16 // * 65536 / 65535
+ mov ecx, 0x0000ffff
+ div ecx
+ sub eax, 0x8000
+ neg eax
+ sar eax, 16
+ add eax, ebx // Done!
+end:
+ }
+
+ RET((WORD) _EAX);
+}
+
+#ifdef _MSC_VER
+#pragma warning(default : 4033)
+#pragma warning(default : 4035)
+#endif
+
+#endif
+
+Fixed32 cmsLinearInterpFixed(WORD Value1, WORD LutTable[], LPL16PARAMS p)
+{
+ Fixed32 y1, y0;
+ int cell0;
+ int val3, Value;
+
+ // if last value...
+
+
+ Value = Value1;
+ if (Value == 0xffffU) return LutTable[p -> Domain];
+
+ val3 = p -> Domain * Value;
+ val3 = ToFixedDomain(val3); // To fixed 15.16
+
+ cell0 = FIXED_TO_INT(val3); // Cell is 16 MSB bits
+
+ y0 = LutTable[cell0] ;
+ y1 = LutTable[cell0+1] ;
+
+
+ return y0 + FixedMul((y1 - y0), (val3 & 0xFFFFL));
+}
+
+
+// Reverse Lineal interpolation (16 bits)
+// Im using a sort of binary search here, this is not a time-critical function
+
+WORD cmsReverseLinearInterpLUT16(WORD Value, WORD LutTable[], LPL16PARAMS p)
+{
+ register int l = 1;
+ register int r = 0x10000;
+ register int x = 0, res; // 'int' Give spacing for negative values
+ int NumZeroes, NumPoles;
+ int cell0, cell1;
+ double val2;
+ double y0, y1, x0, x1;
+ double a, b, f;
+
+ // July/27 2001 - Expanded to handle degenerated curves with an arbitrary
+ // number of elements containing 0 at the begining of the table (Zeroes)
+ // and another arbitrary number of poles (FFFFh) at the end.
+ // First the zero and pole extents are computed, then value is compared.
+
+ NumZeroes = 0;
+ while (LutTable[NumZeroes] == 0 && NumZeroes < p -> Domain)
+ NumZeroes++;
+
+ // There are no zeros at the beginning and we are trying to find a zero, so
+ // return anything. It seems zero would be the less destructive choice
+
+ if (NumZeroes == 0 && Value == 0)
+ return 0;
+
+ NumPoles = 0;
+ while (LutTable[p -> Domain - NumPoles] == 0xFFFF && NumPoles < p -> Domain)
+ NumPoles++;
+
+ // Does the curve belong to this case?
+ if (NumZeroes > 1 || NumPoles > 1)
+ {
+ int a, b;
+
+ // Identify if value fall downto 0 or FFFF zone
+ if (Value == 0) return 0;
+ // if (Value == 0xFFFF) return 0xFFFF;
+
+ // else restrict to valid zone
+
+ a = ((NumZeroes-1) * 0xFFFF) / p->Domain;
+ b = ((p -> Domain - NumPoles) * 0xFFFF) / p ->Domain;
+
+ l = a - 1;
+ r = b + 1;
+ }
+
+
+ // Seems not a degenerated case... apply binary search
+
+ while (r > l) {
+
+ x = (l + r) / 2;
+
+ res = (int) cmsLinearInterpLUT16((WORD) (x - 1), LutTable, p);
+
+ if (res == Value) {
+
+ // Found exact match.
+
+ return (WORD) (x - 1);
+ }
+
+ if (res > Value) r = x - 1;
+ else l = x + 1;
+ }
+
+ // Not found, should we interpolate?
+
+
+ // Get surrounding nodes
+
+ val2 = p -> Domain * ((double) (x - 1) / 65535.0);
+
+ cell0 = (int) floor(val2);
+ cell1 = (int) ceil(val2);
+
+ if (cell0 == cell1) return (WORD) x;
+
+ y0 = LutTable[cell0] ;
+ x0 = (65535.0 * cell0) / p ->Domain;
+
+ y1 = LutTable[cell1] ;
+ x1 = (65535.0 * cell1) / p ->Domain;
+
+ a = (y1 - y0) / (x1 - x0);
+ b = y0 - a * x0;
+
+ if (fabs(a) < 0.01) return (WORD) x;
+
+ f = ((Value - b) / a);
+
+ if (f < 0.0) return (WORD) 0;
+ if (f >= 65535.0) return (WORD) 0xFFFF;
+
+ return (WORD) floor(f + 0.5);
+
+}
+
+
+
+
+// Trilinear interpolation (16 bits) - float version
+
+#ifdef USE_FLOAT
+void cmsTrilinearInterp16(WORD Input[], WORD Output[],
+ WORD LutTable[], LPL16PARAMS p)
+
+{
+# define LERP(a,l,h) (double) ((l)+(((h)-(l))*(a)))
+# define DENS(X, Y, Z) (double) (LutTable[TotalOut*((Z)+clutPoints*((Y)+clutPoints*(X)))+OutChan])
+
+
+
+ double px, py, pz;
+ int x0, y0, z0,
+ x1, y1, z1;
+ int clutPoints, TotalOut, OutChan;
+ double fx, fy, fz,
+ d000, d001, d010, d011,
+ d100, d101, d110, d111,
+ dx00, dx01, dx10, dx11,
+ dxy0, dxy1, dxyz;
+
+
+ clutPoints = p -> Domain + 1;
+ TotalOut = p -> nOutputs;
+
+ px = ((double) Input[0] * (p->Domain)) / 65535.0;
+ py = ((double) Input[1] * (p->Domain)) / 65535.0;
+ pz = ((double) Input[2] * (p->Domain)) / 65535.0;
+
+ x0 = (int) _cmsQuickFloor(px); fx = px - (double) x0;
+ y0 = (int) _cmsQuickFloor(py); fy = py - (double) y0;
+ z0 = (int) _cmsQuickFloor(pz); fz = pz - (double) z0;
+
+ x1 = x0 + (Input[0] != 0xFFFFU ? 1 : 0);
+ y1 = y0 + (Input[1] != 0xFFFFU ? 1 : 0);
+ z1 = z0 + (Input[2] != 0xFFFFU ? 1 : 0);
+
+
+ for (OutChan = 0; OutChan < TotalOut; OutChan++)
+ {
+
+ d000 = DENS(x0, y0, z0);
+ d001 = DENS(x0, y0, z1);
+ d010 = DENS(x0, y1, z0);
+ d011 = DENS(x0, y1, z1);
+
+ d100 = DENS(x1, y0, z0);
+ d101 = DENS(x1, y0, z1);
+ d110 = DENS(x1, y1, z0);
+ d111 = DENS(x1, y1, z1);
+
+
+ dx00 = LERP(fx, d000, d100);
+ dx01 = LERP(fx, d001, d101);
+ dx10 = LERP(fx, d010, d110);
+ dx11 = LERP(fx, d011, d111);
+
+ dxy0 = LERP(fy, dx00, dx10);
+ dxy1 = LERP(fy, dx01, dx11);
+
+ dxyz = LERP(fz, dxy0, dxy1);
+
+ Output[OutChan] = (WORD) floor(dxyz + .5);
+ }
+
+
+# undef LERP
+# undef DENS
+}
+
+
+#endif
+
+
+#ifndef USE_FLOAT
+
+// Trilinear interpolation (16 bits) - optimized version
+
+void cmsTrilinearInterp16(WORD Input[], WORD Output[],
+ WORD LutTable[], LPL16PARAMS p)
+
+{
+#define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan])
+#define LERP(a,l,h) (WORD) (l+ ROUND_FIXED_TO_INT(((h-l)*a)))
+
+
+ int OutChan, TotalOut;
+ Fixed32 fx, fy, fz;
+ register int rx, ry, rz;
+ int x0, y0, z0;
+ register int X0, X1, Y0, Y1, Z0, Z1;
+ int d000, d001, d010, d011,
+ d100, d101, d110, d111,
+ dx00, dx01, dx10, dx11,
+ dxy0, dxy1, dxyz;
+
+
+ TotalOut = p -> nOutputs;
+
+ fx = ToFixedDomain((int) Input[0] * p -> Domain);
+ x0 = FIXED_TO_INT(fx);
+ rx = FIXED_REST_TO_INT(fx); // Rest in 0..1.0 domain
+
+
+ fy = ToFixedDomain((int) Input[1] * p -> Domain);
+ y0 = FIXED_TO_INT(fy);
+ ry = FIXED_REST_TO_INT(fy);
+
+ fz = ToFixedDomain((int) Input[2] * p -> Domain);
+ z0 = FIXED_TO_INT(fz);
+ rz = FIXED_REST_TO_INT(fz);
+
+
+
+ X0 = p -> opta3 * x0;
+ X1 = X0 + (Input[0] == 0xFFFFU ? 0 : p->opta3);
+
+ Y0 = p -> opta2 * y0;
+ Y1 = Y0 + (Input[1] == 0xFFFFU ? 0 : p->opta2);
+
+ Z0 = p -> opta1 * z0;
+ Z1 = Z0 + (Input[2] == 0xFFFFU ? 0 : p->opta1);
+
+
+
+ for (OutChan = 0; OutChan < TotalOut; OutChan++)
+ {
+
+ d000 = DENS(X0, Y0, Z0);
+ d001 = DENS(X0, Y0, Z1);
+ d010 = DENS(X0, Y1, Z0);
+ d011 = DENS(X0, Y1, Z1);
+
+ d100 = DENS(X1, Y0, Z0);
+ d101 = DENS(X1, Y0, Z1);
+ d110 = DENS(X1, Y1, Z0);
+ d111 = DENS(X1, Y1, Z1);
+
+
+ dx00 = LERP(rx, d000, d100);
+ dx01 = LERP(rx, d001, d101);
+ dx10 = LERP(rx, d010, d110);
+ dx11 = LERP(rx, d011, d111);
+
+ dxy0 = LERP(ry, dx00, dx10);
+ dxy1 = LERP(ry, dx01, dx11);
+
+ dxyz = LERP(rz, dxy0, dxy1);
+
+ Output[OutChan] = (WORD) dxyz;
+ }
+
+
+# undef LERP
+# undef DENS
+}
+
+#endif
+
+
+#ifdef USE_FLOAT
+
+#define DENS(X, Y, Z) (double) (LutTable[TotalOut*((Z)+clutPoints*((Y)+clutPoints*(X)))+OutChan])
+
+
+// Tetrahedral interpolation, using Sakamoto algorithm.
+
+void cmsTetrahedralInterp16(WORD Input[],
+ WORD Output[],
+ WORD LutTable[],
+ LPL16PARAMS p)
+{
+ double px, py, pz;
+ int x0, y0, z0,
+ x1, y1, z1;
+ double fx, fy, fz;
+ double c1=0, c2=0, c3=0;
+ int clutPoints, OutChan, TotalOut;
+
+
+ clutPoints = p -> Domain + 1;
+ TotalOut = p -> nOutputs;
+
+
+ px = ((double) Input[0] * p->Domain) / 65535.0;
+ py = ((double) Input[1] * p->Domain) / 65535.0;
+ pz = ((double) Input[2] * p->Domain) / 65535.0;
+
+ x0 = (int) _cmsQuickFloor(px); fx = (px - (double) x0);
+ y0 = (int) _cmsQuickFloor(py); fy = (py - (double) y0);
+ z0 = (int) _cmsQuickFloor(pz); fz = (pz - (double) z0);
+
+
+ x1 = x0 + (Input[0] != 0xFFFFU ? 1 : 0);
+ y1 = y0 + (Input[1] != 0xFFFFU ? 1 : 0);
+ z1 = z0 + (Input[2] != 0xFFFFU ? 1 : 0);
+
+
+ for (OutChan=0; OutChan < TotalOut; OutChan++)
+ {
+
+ // These are the 6 Tetrahedral
+
+ if (fx >= fy && fy >= fz)
+ {
+ c1 = DENS(x1, y0, z0) - DENS(x0, y0, z0);
+ c2 = DENS(x1, y1, z0) - DENS(x1, y0, z0);
+ c3 = DENS(x1, y1, z1) - DENS(x1, y1, z0);
+ }
+ else
+ if (fx >= fz && fz >= fy)
+ {
+ c1 = DENS(x1, y0, z0) - DENS(x0, y0, z0);
+ c2 = DENS(x1, y1, z1) - DENS(x1, y0, z1);
+ c3 = DENS(x1, y0, z1) - DENS(x1, y0, z0);
+ }
+ else
+ if (fz >= fx && fx >= fy)
+ {
+ c1 = DENS(x1, y0, z1) - DENS(x0, y0, z1);
+ c2 = DENS(x1, y1, z1) - DENS(x1, y0, z1);
+ c3 = DENS(x0, y0, z1) - DENS(x0, y0, z0);
+ }
+ else
+ if (fy >= fx && fx >= fz)
+ {
+ c1 = DENS(x1, y1, z0) - DENS(x0, y1, z0);
+ c2 = DENS(x0, y1, z0) - DENS(x0, y0, z0);
+ c3 = DENS(x1, y1, z1) - DENS(x1, y1, z0);
+
+ }
+ else
+ if (fy >= fz && fz >= fx)
+ {
+ c1 = DENS(x1, y1, z1) - DENS(x0, y1, z1);
+ c2 = DENS(x0, y1, z0) - DENS(x0, y0, z0);
+ c3 = DENS(x0, y1, z1) - DENS(x0, y1, z0);
+ }
+ else
+ if (fz >= fy && fy >= fx)
+ {
+ c1 = DENS(x1, y1, z1) - DENS(x0, y1, z1);
+ c2 = DENS(x0, y1, z1) - DENS(x0, y0, z1);
+ c3 = DENS(x0, y0, z1) - DENS(x0, y0, z0);
+ }
+ else
+ {
+ c1 = c2 = c3 = 0;
+ // assert(FALSE);
+ }
+
+
+ Output[OutChan] = (WORD) floor((double) DENS(x0,y0,z0) + c1 * fx + c2 * fy + c3 * fz + .5);
+ }
+
+}
+
+#undef DENS
+
+#else
+
+#define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan])
+
+
+void cmsTetrahedralInterp16(WORD Input[],
+ WORD Output[],
+ WORD LutTable1[],
+ LPL16PARAMS p)
+{
+
+ Fixed32 fx, fy, fz;
+ Fixed32 rx, ry, rz;
+ int x0, y0, z0;
+ Fixed32 c0, c1, c2, c3, Rest;
+ int OutChan;
+ Fixed32 X0, X1, Y0, Y1, Z0, Z1;
+ int TotalOut = p -> nOutputs;
+ register LPWORD LutTable = LutTable1;
+
+
+
+ fx = ToFixedDomain((int) Input[0] * p -> Domain);
+ fy = ToFixedDomain((int) Input[1] * p -> Domain);
+ fz = ToFixedDomain((int) Input[2] * p -> Domain);
+
+ x0 = FIXED_TO_INT(fx);
+ y0 = FIXED_TO_INT(fy);
+ z0 = FIXED_TO_INT(fz);
+
+ rx = FIXED_REST_TO_INT(fx);
+ ry = FIXED_REST_TO_INT(fy);
+ rz = FIXED_REST_TO_INT(fz);
+
+ X0 = p -> opta3 * x0;
+ X1 = X0 + (Input[0] == 0xFFFFU ? 0 : p->opta3);
+
+ Y0 = p -> opta2 * y0;
+ Y1 = Y0 + (Input[1] == 0xFFFFU ? 0 : p->opta2);
+
+ Z0 = p -> opta1 * z0;
+ Z1 = Z0 + (Input[2] == 0xFFFFU ? 0 : p->opta1);
+
+
+
+ // These are the 6 Tetrahedral
+ for (OutChan=0; OutChan < TotalOut; OutChan++) {
+
+ c0 = DENS(X0, Y0, Z0);
+
+ if (rx >= ry && ry >= rz) {
+
+ c1 = DENS(X1, Y0, Z0) - c0;
+ c2 = DENS(X1, Y1, Z0) - DENS(X1, Y0, Z0);
+ c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0);
+
+ }
+ else
+ if (rx >= rz && rz >= ry) {
+
+ c1 = DENS(X1, Y0, Z0) - c0;
+ c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1);
+ c3 = DENS(X1, Y0, Z1) - DENS(X1, Y0, Z0);
+
+ }
+ else
+ if (rz >= rx && rx >= ry) {
+
+ c1 = DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1);
+ c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1);
+ c3 = DENS(X0, Y0, Z1) - c0;
+
+ }
+ else
+ if (ry >= rx && rx >= rz) {
+
+ c1 = DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0);
+ c2 = DENS(X0, Y1, Z0) - c0;
+ c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0);
+
+ }
+ else
+ if (ry >= rz && rz >= rx) {
+
+ c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1);
+ c2 = DENS(X0, Y1, Z0) - c0;
+ c3 = DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0);
+
+ }
+ else
+ if (rz >= ry && ry >= rx) {
+
+ c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1);
+ c2 = DENS(X0, Y1, Z1) - DENS(X0, Y0, Z1);
+ c3 = DENS(X0, Y0, Z1) - c0;
+
+ }
+ else {
+ c1 = c2 = c3 = 0;
+ // assert(FALSE);
+ }
+
+ Rest = c1 * rx + c2 * ry + c3 * rz;
+
+ // There is a lot of math hidden in this expression. The rest is in fixed domain
+ // and the result in 0..ffff domain. So the complete expression should be
+ // ROUND_FIXED_TO_INT(ToFixedDomain(Rest)) But that can be optimized as (Rest + 0x7FFF) / 0xFFFF
+
+ Output[OutChan] = (WORD) (c0 + ((Rest + 0x7FFF) / 0xFFFF));
+
+ }
+
+}
+
+
+
+#undef DENS
+
+#endif
+
+
+// A optimized interpolation for 8-bit input.
+
+#define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan])
+
+void cmsTetrahedralInterp8(WORD Input[],
+ WORD Output[],
+ WORD LutTable[],
+ LPL16PARAMS p)
+{
+
+ int r, g, b;
+ Fixed32 rx, ry, rz;
+ Fixed32 c1, c2, c3, Rest;
+ int OutChan;
+ register Fixed32 X0, X1, Y0, Y1, Z0, Z1;
+ int TotalOut = p -> nOutputs;
+ register LPL8PARAMS p8 = p ->p8;
+
+
+
+ r = Input[0] >> 8;
+ g = Input[1] >> 8;
+ b = Input[2] >> 8;
+
+ X0 = X1 = p8->X0[r];
+ Y0 = Y1 = p8->Y0[g];
+ Z0 = Z1 = p8->Z0[b];
+
+ X1 += (r == 255) ? 0 : p ->opta3;
+ Y1 += (g == 255) ? 0 : p ->opta2;
+ Z1 += (b == 255) ? 0 : p ->opta1;
+
+ rx = p8 ->rx[r];
+ ry = p8 ->ry[g];
+ rz = p8 ->rz[b];
+
+
+ // These are the 6 Tetrahedral
+ for (OutChan=0; OutChan < TotalOut; OutChan++) {
+
+ if (rx >= ry && ry >= rz)
+ {
+
+ c1 = DENS(X1, Y0, Z0) - DENS(X0, Y0, Z0);
+ c2 = DENS(X1, Y1, Z0) - DENS(X1, Y0, Z0);
+ c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0);
+
+ }
+ else
+ if (rx >= rz && rz >= ry)
+ {
+ c1 = DENS(X1, Y0, Z0) - DENS(X0, Y0, Z0);
+ c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1);
+ c3 = DENS(X1, Y0, Z1) - DENS(X1, Y0, Z0);
+
+ }
+ else
+ if (rz >= rx && rx >= ry)
+ {
+
+ c1 = DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1);
+ c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1);
+ c3 = DENS(X0, Y0, Z1) - DENS(X0, Y0, Z0);
+
+ }
+ else
+ if (ry >= rx && rx >= rz)
+ {
+
+ c1 = DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0);
+ c2 = DENS(X0, Y1, Z0) - DENS(X0, Y0, Z0);
+ c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0);
+
+ }
+ else
+ if (ry >= rz && rz >= rx)
+ {
+
+ c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1);
+ c2 = DENS(X0, Y1, Z0) - DENS(X0, Y0, Z0);
+ c3 = DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0);
+
+ }
+ else
+ if (rz >= ry && ry >= rx)
+ {
+ c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1);
+ c2 = DENS(X0, Y1, Z1) - DENS(X0, Y0, Z1);
+ c3 = DENS(X0, Y0, Z1) - DENS(X0, Y0, Z0);
+
+ }
+ else {
+ c1 = c2 = c3 = 0;
+ // assert(FALSE);
+ }
+
+
+ Rest = c1 * rx + c2 * ry + c3 * rz;
+
+ Output[OutChan] = (WORD) (DENS(X0,Y0,Z0) + ((Rest + 0x7FFF) / 0xFFFF));
+ }
+
+}
+
+#undef DENS
+
diff --git a/debian/lcms/lcms-1.19.dfsg2/src/cmsio0.c b/debian/lcms/lcms-1.19.dfsg2/src/cmsio0.c
new file mode 100755
index 00000000..6ed9e1f5
--- /dev/null
+++ b/debian/lcms/lcms-1.19.dfsg2/src/cmsio0.c
@@ -0,0 +1,735 @@
+//
+// Little cms
+// Copyright (C) 1998-2007 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.
+//
+
+// Generic I/O, tag dictionary management, profile struct
+
+
+
+#include "lcms.h"
+
+
+// Memory-based stream ---------------------------------------------------
+
+typedef struct {
+ LPBYTE Block; // Points to allocated memory
+ size_t Size; // Size of allocated memory
+ size_t Pointer; // Points to current location
+ int FreeBlockOnClose; // As title
+
+ } FILEMEM;
+
+static
+LPVOID MemoryOpen(LPBYTE Block, size_t Size, char Mode)
+{
+ FILEMEM* fm = (FILEMEM*) _cmsMalloc(sizeof(FILEMEM));
+ if (fm == NULL) return NULL;
+
+ ZeroMemory(fm, sizeof(FILEMEM));
+
+ if (Mode == 'r') {
+
+ fm ->Block = (LPBYTE) _cmsMalloc(Size);
+ if (fm ->Block == NULL) {
+ _cmsFree(fm);
+ return NULL;
+ }
+
+ CopyMemory(fm->Block, Block, Size);
+ fm ->FreeBlockOnClose = TRUE;
+ }
+ else {
+ fm ->Block = Block;
+ fm ->FreeBlockOnClose = FALSE;
+ }
+
+ fm ->Size = Size;
+ fm ->Pointer = 0;
+
+ return (LPVOID) fm;
+}
+
+
+static
+size_t MemoryRead(LPVOID buffer, size_t size, size_t count, struct _lcms_iccprofile_struct* Icc)
+{
+ FILEMEM* ResData = (FILEMEM*) Icc ->stream;
+ LPBYTE Ptr;
+ size_t len = size * count;
+ size_t extent = ResData -> Pointer + len;
+
+ if (len == 0) {
+ return 0;
+ }
+
+ if (len / size != count) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "Read from memory error. Integer overflow with count / size.");
+ return 0;
+ }
+
+ if (extent < len || extent < ResData -> Pointer) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "Read from memory error. Integer overflow with len.");
+ return 0;
+ }
+
+ if (ResData -> Pointer + len > ResData -> Size) {
+
+ len = (ResData -> Size - ResData -> Pointer);
+ cmsSignalError(LCMS_ERRC_ABORTED, "Read from memory error. Got %d bytes, block should be of %d bytes", len * size, count * size);
+ return 0;
+ }
+
+ Ptr = ResData -> Block;
+ Ptr += ResData -> Pointer;
+ CopyMemory(buffer, Ptr, len);
+ ResData -> Pointer += (int) len;
+
+ return count;
+}
+
+// SEEK_CUR is assumed
+
+static
+LCMSBOOL MemorySeek(struct _lcms_iccprofile_struct* Icc, size_t offset)
+{
+ FILEMEM* ResData = (FILEMEM*) Icc ->stream;
+
+ if (offset > ResData ->Size) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "Pointer error; probably corrupted file");
+ return TRUE;
+ }
+
+ ResData ->Pointer = (DWORD) offset;
+ return FALSE;
+}
+
+// FTell
+
+static
+size_t MemoryTell(struct _lcms_iccprofile_struct* Icc)
+{
+ FILEMEM* ResData = (FILEMEM*) Icc ->stream;
+
+ return ResData -> Pointer;
+}
+
+
+// Writes data to memory, also keeps used space for further reference. NO CHECK IS PERFORMED
+
+static
+LCMSBOOL MemoryWrite(struct _lcms_iccprofile_struct* Icc, size_t size, void *Ptr)
+{
+ FILEMEM* ResData = (FILEMEM*) Icc ->stream;
+
+ if (size == 0) return TRUE;
+
+ if (ResData != NULL)
+ CopyMemory(ResData ->Block + Icc ->UsedSpace, Ptr, size);
+
+ Icc->UsedSpace += size;
+
+ return TRUE;
+}
+
+
+static
+LCMSBOOL MemoryClose(struct _lcms_iccprofile_struct* Icc)
+{
+ FILEMEM* ResData = (FILEMEM*) Icc ->stream;
+
+ if (ResData ->FreeBlockOnClose) {
+
+ if (ResData ->Block) _cmsFree(ResData ->Block);
+ }
+ _cmsFree(ResData);
+ return 0;
+}
+
+
+// File-based stream -------------------------------------------------------
+
+static
+LPVOID FileOpen(const char* filename)
+{
+ return (void*) fopen(filename, "rb");
+}
+
+static
+size_t FileRead(void *buffer, size_t size, size_t count, struct _lcms_iccprofile_struct* Icc)
+{
+ size_t nReaded = fread(buffer, size, count, (FILE*) Icc->stream);
+ if (nReaded != count) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "Read error. Got %d bytes, block should be of %d bytes", nReaded * size, count * size);
+ return 0;
+ }
+
+ return nReaded;
+}
+
+
+static
+LCMSBOOL FileSeek(struct _lcms_iccprofile_struct* Icc, size_t offset)
+{
+ if (fseek((FILE*) Icc ->stream, (long) offset, SEEK_SET) != 0) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "Seek error; probably corrupted file");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static
+size_t FileTell(struct _lcms_iccprofile_struct* Icc)
+{
+ return ftell((FILE*) Icc ->stream);
+}
+
+// Writes data to stream, also keeps used space for further reference
+
+
+static
+LCMSBOOL FileWrite(struct _lcms_iccprofile_struct* Icc, size_t size, LPVOID Ptr)
+{
+ if (size == 0) return TRUE;
+
+ Icc->UsedSpace += size;
+
+ if (Icc->stream == NULL) {
+
+ return TRUE;
+ }
+
+ return (fwrite(Ptr, size, 1, (FILE*) Icc->stream) == 1);
+}
+
+
+static
+LCMSBOOL FileClose(struct _lcms_iccprofile_struct* Icc)
+{
+ return fclose((FILE*) Icc ->stream);
+}
+
+// ----------------------------------------------------------------------------------------------------
+
+
+// Creates an empty structure holding all required parameters
+
+cmsHPROFILE _cmsCreateProfilePlaceholder(void)
+{
+
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) _cmsMalloc(sizeof(LCMSICCPROFILE));
+ if (Icc == NULL) return NULL;
+
+ // Empty values
+ ZeroMemory(Icc, sizeof(LCMSICCPROFILE));
+
+ // Make sure illuminant is correct
+ Icc ->Illuminant = *cmsD50_XYZ();
+
+ // Set it to empty
+ Icc -> TagCount = 0;
+
+ // Return the handle
+ return (cmsHPROFILE) Icc;
+}
+
+
+// Return the number of tags
+icInt32Number LCMSEXPORT cmsGetTagCount(cmsHPROFILE hProfile)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
+ return Icc->TagCount;
+}
+
+// Return the tag signature of a given tag number
+icTagSignature LCMSEXPORT cmsGetTagSignature(cmsHPROFILE hProfile, icInt32Number n)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
+
+ if (n < 0 || n > Icc->TagCount) return (icTagSignature) 0; // Mark as not available
+
+ return Icc ->TagNames[n];
+}
+
+
+// Search for a specific tag in tag dictionary
+// Returns position or -1 if tag not found
+
+icInt32Number _cmsSearchTag(LPLCMSICCPROFILE Profile, icTagSignature sig, LCMSBOOL lSignalError)
+{
+ icInt32Number i;
+
+ if (sig == 0) return -1; // 0 identifies a special tag holding raw memory.
+
+ for (i=0; i < Profile -> TagCount; i++) {
+
+ if (sig == Profile -> TagNames[i])
+ return i;
+ }
+
+ if (lSignalError)
+ cmsSignalError(LCMS_ERRC_ABORTED, "Tag '%lx' not found", sig);
+
+ return -1;
+}
+
+
+// Check existance
+
+LCMSBOOL LCMSEXPORT cmsIsTag(cmsHPROFILE hProfile, icTagSignature sig)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+ return _cmsSearchTag(Icc, sig, FALSE) >= 0;
+}
+
+
+
+// Search for a particular tag, replace if found or add new one else
+
+LPVOID _cmsInitTag(LPLCMSICCPROFILE Icc, icTagSignature sig, size_t size, const void* Init)
+{
+ LPVOID Ptr;
+ icInt32Number i;
+
+ i = _cmsSearchTag(Icc, sig, FALSE);
+
+ if (i >=0) {
+
+ if (Icc -> TagPtrs[i]) _cmsFree(Icc -> TagPtrs[i]);
+ }
+ else {
+
+ i = Icc -> TagCount;
+ Icc -> TagCount++;
+
+ if (Icc ->TagCount >= MAX_TABLE_TAG) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "Too many tags (%d)", MAX_TABLE_TAG);
+ Icc ->TagCount = MAX_TABLE_TAG-1;
+ return NULL;
+ }
+ }
+
+
+ Ptr = _cmsMalloc(size);
+ if (Ptr == NULL) return NULL;
+
+ CopyMemory(Ptr, Init, size);
+
+ Icc ->TagNames[i] = sig;
+ Icc ->TagSizes[i] = size;
+ Icc ->TagPtrs[i] = Ptr;
+
+ return Ptr;
+}
+
+
+
+
+
+// Creates a profile from file read placeholder
+
+LPLCMSICCPROFILE _cmsCreateProfileFromFilePlaceholder(const char* FileName)
+{
+ LPLCMSICCPROFILE NewIcc;
+ LPVOID ICCfile = FileOpen(FileName);
+
+ if (ICCfile == NULL) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "File '%s' not found", FileName);
+ return NULL;
+ }
+
+ NewIcc = (LPLCMSICCPROFILE) _cmsCreateProfilePlaceholder();
+ if (NewIcc == NULL) return NULL;
+
+ strncpy(NewIcc -> PhysicalFile, FileName, MAX_PATH-1);
+ NewIcc -> PhysicalFile[MAX_PATH-1] = 0;
+
+ NewIcc ->stream = ICCfile;
+
+ NewIcc ->Read = FileRead;
+ NewIcc ->Seek = FileSeek;
+ NewIcc ->Tell = FileTell;
+ NewIcc ->Close = FileClose;
+ NewIcc ->Write = NULL;
+
+ NewIcc ->IsWrite = FALSE;
+
+
+
+
+ return NewIcc;
+}
+
+
+// Creates a profile from memory read placeholder
+
+LPLCMSICCPROFILE _cmsCreateProfileFromMemPlaceholder(LPVOID MemPtr, DWORD dwSize)
+{
+
+ LPLCMSICCPROFILE NewIcc;
+ LPVOID ICCfile = MemoryOpen((LPBYTE) MemPtr, (size_t) dwSize, 'r');
+
+
+ if (ICCfile == NULL) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't allocate %ld bytes for profile", dwSize);
+ return NULL;
+ }
+
+
+ NewIcc = (LPLCMSICCPROFILE) _cmsCreateProfilePlaceholder();
+ if (NewIcc == NULL) return NULL;
+
+ NewIcc -> PhysicalFile[0] = 0;
+ NewIcc ->stream = ICCfile;
+
+ NewIcc ->Read = MemoryRead;
+ NewIcc ->Seek = MemorySeek;
+ NewIcc ->Tell = MemoryTell;
+ NewIcc ->Close = MemoryClose;
+ NewIcc ->Write = NULL;
+
+ NewIcc ->IsWrite = FALSE;
+
+
+ return NewIcc;
+}
+
+
+// Turn a placeholder into file writter
+
+void _cmsSetSaveToDisk(LPLCMSICCPROFILE Icc, const char* FileName)
+{
+
+ if (FileName == NULL) {
+
+ Icc ->stream = NULL;
+ }
+ else {
+
+ Icc ->stream = fopen(FileName, "wb");
+ if (Icc ->stream == NULL)
+ cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't write to file '%s'", FileName);
+ }
+
+ Icc ->Write = FileWrite; // Save to disk
+ Icc ->Close = FileClose;
+}
+
+
+
+// Turn a placeholder into memory writter
+
+void _cmsSetSaveToMemory(LPLCMSICCPROFILE Icc, LPVOID MemPtr, size_t dwSize)
+{
+
+ if (MemPtr == NULL) {
+
+ Icc ->stream = NULL;
+ }
+ else {
+
+ Icc ->stream = (FILEMEM*) MemoryOpen((LPBYTE) MemPtr, dwSize, 'w');
+ if (Icc ->stream == NULL)
+ cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't write to memory");
+ }
+
+ Icc ->Write = MemoryWrite;
+ Icc ->Close = MemoryClose;
+}
+
+
+// ----------------------------------------------------------------------- Set/Get several struct members
+
+
+
+
+LCMSBOOL LCMSEXPORT cmsTakeMediaWhitePoint(LPcmsCIEXYZ Dest, cmsHPROFILE hProfile)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
+ *Dest = Icc -> MediaWhitePoint;
+ return TRUE;
+}
+
+
+LCMSBOOL LCMSEXPORT cmsTakeMediaBlackPoint(LPcmsCIEXYZ Dest, cmsHPROFILE hProfile)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
+ *Dest = Icc -> MediaBlackPoint;
+ return TRUE;
+}
+
+LCMSBOOL LCMSEXPORT cmsTakeIluminant(LPcmsCIEXYZ Dest, cmsHPROFILE hProfile)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
+ *Dest = Icc -> Illuminant;
+ return TRUE;
+}
+
+int LCMSEXPORT cmsTakeRenderingIntent(cmsHPROFILE hProfile)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
+ return (int) Icc -> RenderingIntent;
+}
+
+void LCMSEXPORT cmsSetRenderingIntent(cmsHPROFILE hProfile, int RenderingIntent)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
+ Icc -> RenderingIntent = (icRenderingIntent) RenderingIntent;
+}
+
+
+DWORD LCMSEXPORT cmsTakeHeaderFlags(cmsHPROFILE hProfile)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
+ return (DWORD) Icc -> flags;
+}
+
+void LCMSEXPORT cmsSetHeaderFlags(cmsHPROFILE hProfile, DWORD Flags)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
+ Icc -> flags = (icUInt32Number) Flags;
+}
+
+DWORD LCMSEXPORT cmsTakeHeaderAttributes(cmsHPROFILE hProfile)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
+ return (DWORD) Icc -> attributes;
+}
+
+void LCMSEXPORT cmsSetHeaderAttributes(cmsHPROFILE hProfile, DWORD Flags)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
+ Icc -> attributes = (icUInt32Number) Flags;
+}
+
+
+const BYTE* LCMSEXPORT cmsTakeProfileID(cmsHPROFILE hProfile)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
+ return Icc ->ProfileID;
+}
+
+void LCMSEXPORT cmsSetProfileID(cmsHPROFILE hProfile, LPBYTE ProfileID)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
+ CopyMemory(Icc -> ProfileID, ProfileID, 16);
+}
+
+
+LCMSBOOL LCMSEXPORT cmsTakeCreationDateTime(struct tm *Dest, cmsHPROFILE hProfile)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+ CopyMemory(Dest, &Icc ->Created, sizeof(struct tm));
+ return TRUE;
+}
+
+
+icColorSpaceSignature LCMSEXPORT cmsGetPCS(cmsHPROFILE hProfile)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
+ return Icc -> PCS;
+}
+
+
+void LCMSEXPORT cmsSetPCS(cmsHPROFILE hProfile, icColorSpaceSignature pcs)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
+ Icc -> PCS = pcs;
+}
+
+icColorSpaceSignature LCMSEXPORT cmsGetColorSpace(cmsHPROFILE hProfile)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
+ return Icc -> ColorSpace;
+}
+
+void LCMSEXPORT cmsSetColorSpace(cmsHPROFILE hProfile, icColorSpaceSignature sig)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
+ Icc -> ColorSpace = sig;
+}
+
+icProfileClassSignature LCMSEXPORT cmsGetDeviceClass(cmsHPROFILE hProfile)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
+ return Icc -> DeviceClass;
+}
+
+DWORD LCMSEXPORT cmsGetProfileICCversion(cmsHPROFILE hProfile)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
+ return (DWORD) Icc -> Version;
+}
+
+void LCMSEXPORT cmsSetProfileICCversion(cmsHPROFILE hProfile, DWORD Version)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
+ Icc -> Version = Version;
+}
+
+
+void LCMSEXPORT cmsSetDeviceClass(cmsHPROFILE hProfile, icProfileClassSignature sig)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) hProfile;
+ Icc -> DeviceClass = sig;
+}
+
+
+// --------------------------------------------------------------------------------------------------------------
+
+
+static
+int SizeOfGammaTab(LPGAMMATABLE In)
+{
+ return sizeof(GAMMATABLE) + (In -> nEntries - 1)*sizeof(WORD);
+}
+
+
+// Creates a phantom tag holding a memory block
+
+static
+LPVOID DupBlock(LPLCMSICCPROFILE Icc, LPVOID Block, size_t size)
+{
+ if (Block != NULL && size > 0)
+ return _cmsInitTag(Icc, (icTagSignature) 0, size, Block);
+ else
+ return NULL;
+
+}
+
+// This is tricky, since LUT structs does have pointers
+
+LCMSBOOL LCMSEXPORT _cmsAddLUTTag(cmsHPROFILE hProfile, icTagSignature sig, const void* lut)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+ LPLUT Orig, Stored;
+ unsigned int i;
+
+ // The struct itself
+
+ Orig = (LPLUT) lut;
+ Stored = (LPLUT) _cmsInitTag(Icc, (icTagSignature) sig, sizeof(LUT), lut);
+
+ // dup' the memory blocks
+ for (i=0; i < Orig ->InputChan; i++)
+ Stored -> L1[i] = (LPWORD) DupBlock(Icc, (LPWORD) Orig ->L1[i],
+ sizeof(WORD) * Orig ->In16params.nSamples);
+
+ for (i=0; i < Orig ->OutputChan; i++)
+ Stored -> L2[i] = (LPWORD) DupBlock(Icc, (LPWORD) Orig ->L2[i],
+ sizeof(WORD) * Orig ->Out16params.nSamples);
+
+ Stored -> T = (LPWORD) DupBlock(Icc, (LPWORD) Orig ->T, Orig -> Tsize);
+
+ // Zero any additional pointer
+ Stored ->CLut16params.p8 = NULL;
+ return TRUE;
+}
+
+
+LCMSBOOL LCMSEXPORT _cmsAddXYZTag(cmsHPROFILE hProfile, icTagSignature sig, const cmsCIEXYZ* XYZ)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+
+ _cmsInitTag(Icc, sig, sizeof(cmsCIEXYZ), XYZ);
+ return TRUE;
+}
+
+
+LCMSBOOL LCMSEXPORT _cmsAddTextTag(cmsHPROFILE hProfile, icTagSignature sig, const char* Text)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+
+ _cmsInitTag(Icc, sig, strlen(Text)+1, (LPVOID) Text);
+ return TRUE;
+}
+
+LCMSBOOL LCMSEXPORT _cmsAddGammaTag(cmsHPROFILE hProfile, icTagSignature sig, LPGAMMATABLE TransferFunction)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+
+ _cmsInitTag(Icc, sig, SizeOfGammaTab(TransferFunction), TransferFunction);
+ return TRUE;
+}
+
+
+LCMSBOOL LCMSEXPORT _cmsAddChromaticityTag(cmsHPROFILE hProfile, icTagSignature sig, LPcmsCIExyYTRIPLE Chrm)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+
+ _cmsInitTag(Icc, sig, sizeof(cmsCIExyYTRIPLE), Chrm);
+ return TRUE;
+}
+
+
+LCMSBOOL LCMSEXPORT _cmsAddSequenceDescriptionTag(cmsHPROFILE hProfile, icTagSignature sig, LPcmsSEQ pseq)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+
+ _cmsInitTag(Icc, sig, sizeof(int) + pseq -> n * sizeof(cmsPSEQDESC), pseq);
+ return TRUE;
+
+}
+
+
+LCMSBOOL LCMSEXPORT _cmsAddNamedColorTag(cmsHPROFILE hProfile, icTagSignature sig, LPcmsNAMEDCOLORLIST nc)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+
+ _cmsInitTag(Icc, sig, sizeof(cmsNAMEDCOLORLIST) + (nc ->nColors - 1) * sizeof(cmsNAMEDCOLOR), nc);
+ return TRUE;
+}
+
+
+LCMSBOOL LCMSEXPORT _cmsAddDateTimeTag(cmsHPROFILE hProfile, icTagSignature sig, struct tm *DateTime)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+
+ _cmsInitTag(Icc, sig, sizeof(struct tm), DateTime);
+ return TRUE;
+}
+
+
+LCMSBOOL LCMSEXPORT _cmsAddColorantTableTag(cmsHPROFILE hProfile, icTagSignature sig, LPcmsNAMEDCOLORLIST nc)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+
+ _cmsInitTag(Icc, sig, sizeof(cmsNAMEDCOLORLIST) + (nc ->nColors - 1) * sizeof(cmsNAMEDCOLOR), nc);
+ return TRUE;
+}
+
+
+LCMSBOOL LCMSEXPORT _cmsAddChromaticAdaptationTag(cmsHPROFILE hProfile, icTagSignature sig, const cmsCIEXYZ* mat)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+
+ _cmsInitTag(Icc, sig, 3*sizeof(cmsCIEXYZ), mat);
+ return TRUE;
+
+}
+
+
diff --git a/debian/lcms/lcms-1.19.dfsg2/src/cmsio1.c b/debian/lcms/lcms-1.19.dfsg2/src/cmsio1.c
new file mode 100755
index 00000000..e08af468
--- /dev/null
+++ b/debian/lcms/lcms-1.19.dfsg2/src/cmsio1.c
@@ -0,0 +1,3727 @@
+//
+// Little cms
+// Copyright (C) 1998-2007 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.
+
+// ICC profile serialization
+
+
+#include "lcms.h"
+
+// ----------------------------------------------------------------- Tag Serialization
+
+// Alignment of ICC file format uses 4 bytes DWORD
+
+#define ALIGNLONG(x) (((x)+3) & ~(3)) // Aligns to DWORD boundary
+
+
+static int GlobalLanguageCode; // Language & country descriptors, for ICC 4.0 support
+static int GlobalCountryCode;
+
+
+#ifdef __BEOS__
+# define USE_CUSTOM_SWAB 1
+#endif
+
+
+#ifdef USE_CUSTOM_SWAB
+
+// Replacement to swab function, thanks to YNOP
+// for providing the BeOS port
+//
+// from: @(#)swab.c 5.10 (Berkeley) 3/6/91
+
+static
+void xswab(const void *from, void *to, size_t len)
+{
+ register unsigned long temp;
+ register int n;
+ register char *fp, *tp;
+
+ n = (len >> 1) + 1;
+ fp = (char *)from;
+ tp = (char *)to;
+#define STEP temp = *fp++,*tp++ = *fp++,*tp++ = temp
+ /* round to multiple of 8 */
+ while ((--n) & 07)
+ STEP;
+ n >>= 3;
+ while (--n >= 0) {
+
+ STEP; STEP; STEP; STEP;
+ STEP; STEP; STEP; STEP;
+ }
+#undef STEP
+}
+#else
+#define xswab swab
+#endif
+
+
+//
+// Little-Endian to Big-Endian
+//
+
+#ifdef USE_BIG_ENDIAN
+#define AdjustEndianess16(a)
+#define AdjustEndianess32(a)
+#define AdjustEndianessArray16(a, b)
+#else
+
+static
+void AdjustEndianess16(LPBYTE pByte)
+{
+ BYTE tmp;
+
+ tmp = pByte[0];
+ pByte[0] = pByte[1];
+ pByte[1] = tmp;
+}
+
+static
+void AdjustEndianess32(LPBYTE pByte)
+{
+ BYTE temp1;
+ BYTE temp2;
+
+ temp1 = *pByte++;
+ temp2 = *pByte++;
+ *(pByte-1) = *pByte;
+ *pByte++ = temp2;
+ *(pByte-3) = *pByte;
+ *pByte = temp1;
+}
+
+
+// swap bytes in a array of words
+
+static
+void AdjustEndianessArray16(LPWORD p, size_t num_words)
+{
+ xswab((char*) p, (char*)p, (int) num_words * sizeof(WORD));
+}
+
+#endif
+
+
+// Transports to properly encoded values - note that icc profiles does use
+// big endian notation.
+
+static
+icInt32Number TransportValue32(icInt32Number Value)
+{
+ icInt32Number Temp = Value;
+
+ AdjustEndianess32((LPBYTE) &Temp);
+ return Temp;
+}
+
+static
+WORD TransportValue16(WORD Value)
+{
+ WORD Temp = Value;
+
+ AdjustEndianess16((LPBYTE) &Temp);
+ return Temp;
+}
+
+
+// from Fixed point 8.8 to double
+
+static
+double Convert8Fixed8(WORD fixed8)
+{
+ BYTE msb, lsb;
+
+ lsb = (BYTE) (fixed8 & 0xff);
+ msb = (BYTE) (((WORD) fixed8 >> 8) & 0xff);
+
+ return (double) ((double) msb + ((double) lsb / 256.0));
+}
+
+
+// from Fixed point 15.16 to double
+static
+double Convert15Fixed16(icS15Fixed16Number fix32)
+{
+ double floater, sign, mid, hack;
+ int Whole, FracPart;
+
+
+ AdjustEndianess32((LPBYTE) &fix32);
+
+ sign = (fix32 < 0 ? -1 : 1);
+ fix32 = abs(fix32);
+
+ Whole = LOWORD(fix32 >> 16);
+ FracPart = LOWORD(fix32 & 0x0000ffffL);
+
+ hack = 65536.0;
+ mid = (double) FracPart / hack;
+ floater = (double) Whole + mid;
+
+ return sign * floater;
+}
+
+
+// Auxiliar-- read base and return type
+
+static
+icTagTypeSignature ReadBase(LPLCMSICCPROFILE Icc)
+{
+ icTagBase Base;
+
+ if (Icc -> Read(&Base, sizeof(icTagBase), 1, Icc) != 1)
+ return (icTagTypeSignature) 0;
+ AdjustEndianess32((LPBYTE) &Base.sig);
+
+ return Base.sig;
+}
+
+
+static
+void DecodeDateTimeNumber(const icDateTimeNumber *Source, struct tm *Dest)
+{
+ Dest->tm_sec = TransportValue16(Source->seconds);
+ Dest->tm_min = TransportValue16(Source->minutes);
+ Dest->tm_hour = TransportValue16(Source->hours);
+ Dest->tm_mday = TransportValue16(Source->day);
+ Dest->tm_mon = TransportValue16(Source->month) - 1;
+ Dest->tm_year = TransportValue16(Source->year) - 1900;
+ Dest->tm_wday = -1;
+ Dest->tm_yday = -1;
+ Dest->tm_isdst = 0;
+}
+
+static
+void EncodeDateTimeNumber(icDateTimeNumber *Dest, const struct tm *Source)
+{
+ Dest->seconds = TransportValue16((WORD) Source->tm_sec);
+ Dest->minutes = TransportValue16((WORD) Source->tm_min);
+ Dest->hours = TransportValue16((WORD) Source->tm_hour);
+ Dest->day = TransportValue16((WORD) Source->tm_mday);
+ Dest->month = TransportValue16((WORD) (Source->tm_mon + 1));
+ Dest->year = TransportValue16((WORD) (Source->tm_year + 1900));
+}
+
+
+// Jun-21-2000: Some profiles (those that comes with W2K) comes
+// with the media white (media black?) x 100. Add a sanity check
+
+static
+void NormalizeXYZ(LPcmsCIEXYZ Dest)
+{
+ while (Dest -> X > 2. &&
+ Dest -> Y > 2. &&
+ Dest -> Z > 2.) {
+
+ Dest -> X /= 10.;
+ Dest -> Y /= 10.;
+ Dest -> Z /= 10.;
+ }
+}
+
+// Evaluates a XYZ tristimulous across chromatic adaptation matrix
+
+static
+void EvalCHRM(LPcmsCIEXYZ Dest, LPMAT3 Chrm, LPcmsCIEXYZ Src)
+{
+ VEC3 d, s;
+
+ s.n[VX] = Src -> X;
+ s.n[VY] = Src -> Y;
+ s.n[VZ] = Src -> Z;
+
+ MAT3eval(&d, Chrm, &s);
+
+ Dest ->X = d.n[VX];
+ Dest ->Y = d.n[VY];
+ Dest ->Z = d.n[VZ];
+
+}
+
+
+// Read profile header and validate it
+
+static
+LPLCMSICCPROFILE ReadHeader(LPLCMSICCPROFILE Icc, LCMSBOOL lIsFromMemory)
+{
+ icTag Tag;
+ icHeader Header;
+ icInt32Number TagCount, i;
+ icUInt32Number extent;
+
+ if (Icc -> Read(&Header, sizeof(icHeader), 1, Icc) != 1)
+ goto ErrorCleanup;
+
+ // Convert endian
+
+ AdjustEndianess32((LPBYTE) &Header.size);
+ AdjustEndianess32((LPBYTE) &Header.cmmId);
+ AdjustEndianess32((LPBYTE) &Header.version);
+ AdjustEndianess32((LPBYTE) &Header.deviceClass);
+ AdjustEndianess32((LPBYTE) &Header.colorSpace);
+ AdjustEndianess32((LPBYTE) &Header.pcs);
+ AdjustEndianess32((LPBYTE) &Header.magic);
+ AdjustEndianess32((LPBYTE) &Header.flags);
+ AdjustEndianess32((LPBYTE) &Header.attributes[0]);
+ AdjustEndianess32((LPBYTE) &Header.renderingIntent);
+
+ // Validate it
+
+ if (Header.magic != icMagicNumber) goto ErrorCleanup;
+
+ if (Icc ->Read(&TagCount, sizeof(icInt32Number), 1, Icc) != 1)
+ goto ErrorCleanup;
+
+ AdjustEndianess32((LPBYTE) &TagCount);
+
+ Icc -> DeviceClass = Header.deviceClass;
+ Icc -> ColorSpace = Header.colorSpace;
+ Icc -> PCS = Header.pcs;
+ Icc -> RenderingIntent = (icRenderingIntent) Header.renderingIntent;
+ Icc -> flags = Header.flags;
+ Icc -> attributes = Header.attributes[0];
+ Icc -> Illuminant.X = Convert15Fixed16(Header.illuminant.X);
+ Icc -> Illuminant.Y = Convert15Fixed16(Header.illuminant.Y);
+ Icc -> Illuminant.Z = Convert15Fixed16(Header.illuminant.Z);
+ Icc -> Version = Header.version;
+
+ // Get creation date/time
+
+ DecodeDateTimeNumber(&Header.date, &Icc ->Created);
+
+ // Fix illuminant, some profiles are broken in this field!
+
+ Icc ->Illuminant = *cmsD50_XYZ();
+
+ // The profile ID are 16 raw bytes
+
+ CopyMemory(Icc ->ProfileID, Header.reserved, 16);
+
+ // Get rid of possible wrong profiles
+
+ NormalizeXYZ(&Icc -> Illuminant);
+
+ // Read tag directory
+
+ if (TagCount > MAX_TABLE_TAG || TagCount < 0) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "Too many tags (%d)", TagCount);
+ goto ErrorCleanup;
+ }
+
+ Icc -> TagCount = TagCount;
+ for (i=0; i < TagCount; i++) {
+
+ if (Icc ->Read(&Tag, sizeof(icTag), 1, Icc) != 1)
+ goto ErrorCleanup;
+
+ AdjustEndianess32((LPBYTE) &Tag.offset);
+ AdjustEndianess32((LPBYTE) &Tag.size);
+ AdjustEndianess32((LPBYTE) &Tag.sig); // Signature
+
+ // Perform some sanity check. Offset + size should fall inside file.
+ extent = Tag.offset + Tag.size;
+ if (extent > Header.size || extent < Tag.offset)
+ goto ErrorCleanup;
+
+ Icc -> TagNames[i] = Tag.sig;
+ Icc -> TagOffsets[i] = Tag.offset;
+ Icc -> TagSizes[i] = Tag.size;
+ }
+
+ return Icc;
+
+
+ErrorCleanup:
+
+ Icc ->Close(Icc);
+
+ if (lIsFromMemory)
+ cmsSignalError(LCMS_ERRC_ABORTED, "Corrupted memory profile");
+ else
+ cmsSignalError(LCMS_ERRC_ABORTED, "Corrupted profile: '%s'", Icc->PhysicalFile);
+
+
+ _cmsFree(Icc);
+ return NULL;
+}
+
+static
+unsigned int uipow(unsigned int a, unsigned int b) {
+ unsigned int rv = 1;
+ for (; b > 0; b--)
+ rv *= a;
+ return rv;
+}
+
+
+
+// Convert between notations.
+
+#define TO16_TAB(x) (WORD) (((x) << 8) | (x))
+
+
+// LUT8 can come only in Lab space. There is a fatal flaw in
+// converting from Lut8 to Lut16. Due to particular encoding
+// of Lab, different actions should be taken from input and
+// output Lab8 LUTS. For input, is as easy as applying a << 8,
+// since numbers comes in fixed point. However, for output LUT
+// things goes a bit more complex.... LUT 16 is supposed to
+// have a domain of 0..ff00, so we should remap the LUT in order
+// to get things working. Affected signatures are B2Axx tags,
+// preview and gamut.
+
+// I do solve it by multiplying input matrix by:
+//
+// | 0xffff/0xff00 0 0 |
+// | 0 0xffff/0xff00 0 |
+// | 0 0 0xffff/0xff00 |
+//
+// The input values got then remapped to adequate domain
+
+static
+void FixLUT8(LPLUT Lut, icTagSignature sig, size_t nTabSize)
+{
+ MAT3 Fixup, Original, Result;
+ LPWORD PtrW;
+ size_t i;
+
+ switch (sig) {
+
+
+ case icSigBToA0Tag:
+ case icSigBToA1Tag:
+ case icSigBToA2Tag:
+ case icSigGamutTag:
+ case icSigPreview0Tag:
+ case icSigPreview1Tag:
+ case icSigPreview2Tag:
+
+
+ VEC3init(&Fixup.v[0], (double) 0xFFFF/0xFF00, 0, 0);
+ VEC3init(&Fixup.v[1], 0, (double) 0xFFFF/0xFF00, 0);
+ VEC3init(&Fixup.v[2], 0, 0, (double) 0xFFFF/0xFF00);
+
+
+ MAT3fromFix(&Original, &Lut->Matrix);
+ MAT3per(&Result, &Original, &Fixup);
+ MAT3toFix(&Lut->Matrix, &Result);
+
+ Lut -> wFlags |= LUT_HASMATRIX;
+ break;
+
+ // For input, clear low part since this has to be
+ // Lab in fixed point
+
+ default:
+
+ PtrW = Lut -> T;
+ for (i = 0; i < nTabSize; i++) {
+
+ *PtrW++ &= 0xFF00;
+ }
+ }
+
+}
+
+// On Lab -> Lab abstract or Lab identities, fix both sides of LUT
+
+static
+void FixLUT8bothSides(LPLUT Lut, size_t nTabSize)
+{
+ MAT3 Fixup, Original, Result;
+ LPWORD PtrW;
+ size_t i;
+
+ VEC3init(&Fixup.v[0], (double) 0xFFFF/0xFF00, 0, 0);
+ VEC3init(&Fixup.v[1], 0, (double) 0xFFFF/0xFF00, 0);
+ VEC3init(&Fixup.v[2], 0, 0, (double) 0xFFFF/0xFF00);
+
+ MAT3fromFix(&Original, &Lut->Matrix);
+ MAT3per(&Result, &Original, &Fixup);
+ MAT3toFix(&Lut->Matrix, &Result);
+
+ Lut -> wFlags |= LUT_HASMATRIX;
+
+ PtrW = Lut -> T;
+ for (i = 0; i < nTabSize; i++) {
+
+ *PtrW++ &= 0xFF00;
+ }
+
+}
+
+
+// The infamous LUT 8
+
+static
+LCMSBOOL ReadLUT8(LPLCMSICCPROFILE Icc, LPLUT NewLUT, icTagSignature sig)
+{
+ icLut8 LUT8;
+ LPBYTE Temp;
+ size_t nTabSize;
+ unsigned int i, j;
+ unsigned int AllLinear;
+ LPWORD PtrW;
+
+ if (Icc ->Read(&LUT8, sizeof(icLut8) - SIZEOF_UINT8_ALIGNED, 1, Icc) != 1) return FALSE;
+
+ NewLUT -> wFlags = LUT_HASTL1|LUT_HASTL2|LUT_HAS3DGRID;
+ NewLUT -> cLutPoints = LUT8.clutPoints;
+ NewLUT -> InputChan = LUT8.inputChan;
+ NewLUT -> OutputChan = LUT8.outputChan;
+ NewLUT -> InputEntries = 256;
+ NewLUT -> OutputEntries = 256;
+
+ // Do some checking
+ if (!_cmsValidateLUT(NewLUT)) {
+ return FALSE;
+ }
+
+ AdjustEndianess32((LPBYTE) &LUT8.e00);
+ AdjustEndianess32((LPBYTE) &LUT8.e01);
+ AdjustEndianess32((LPBYTE) &LUT8.e02);
+ AdjustEndianess32((LPBYTE) &LUT8.e10);
+ AdjustEndianess32((LPBYTE) &LUT8.e11);
+ AdjustEndianess32((LPBYTE) &LUT8.e12);
+ AdjustEndianess32((LPBYTE) &LUT8.e20);
+ AdjustEndianess32((LPBYTE) &LUT8.e21);
+ AdjustEndianess32((LPBYTE) &LUT8.e22);
+
+
+ // Matrix handling
+
+ NewLUT -> Matrix.v[0].n[0] = (Fixed32) LUT8.e00;
+ NewLUT -> Matrix.v[0].n[1] = (Fixed32) LUT8.e01;
+ NewLUT -> Matrix.v[0].n[2] = (Fixed32) LUT8.e02;
+ NewLUT -> Matrix.v[1].n[0] = (Fixed32) LUT8.e10;
+ NewLUT -> Matrix.v[1].n[1] = (Fixed32) LUT8.e11;
+ NewLUT -> Matrix.v[1].n[2] = (Fixed32) LUT8.e12;
+ NewLUT -> Matrix.v[2].n[0] = (Fixed32) LUT8.e20;
+ NewLUT -> Matrix.v[2].n[1] = (Fixed32) LUT8.e21;
+ NewLUT -> Matrix.v[2].n[2] = (Fixed32) LUT8.e22;
+
+
+ // Only operates if not identity...
+
+ if ((NewLUT -> InputChan == 3) && !MAT3isIdentity(&NewLUT -> Matrix, 0.0001)) {
+
+ NewLUT -> wFlags |= LUT_HASMATRIX;
+ }
+
+
+ // Copy input tables
+
+ Temp = (LPBYTE) _cmsMalloc(256);
+ if (Temp == NULL) return FALSE;
+
+ AllLinear = 0;
+ for (i=0; i < NewLUT -> InputChan; i++) {
+
+ PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * 256);
+ if (PtrW == NULL) {
+ _cmsFree(Temp);
+ return FALSE;
+ }
+
+ NewLUT -> L1[i] = PtrW;
+ if (Icc ->Read(Temp, 1, 256, Icc) != 256) {
+ _cmsFree(Temp);
+ return FALSE;
+ }
+
+ for (j=0; j < 256; j++)
+ PtrW[j] = TO16_TAB(Temp[j]);
+ AllLinear += cmsIsLinear(NewLUT -> L1[i], NewLUT -> InputEntries);
+ }
+
+ // Linear input, so ignore full step
+
+ if (AllLinear == NewLUT -> InputChan) {
+
+ NewLUT -> wFlags &= ~LUT_HASTL1;
+ }
+
+ _cmsFree(Temp);
+
+ // Copy 3D CLUT
+
+ nTabSize = (NewLUT -> OutputChan * uipow(NewLUT->cLutPoints,
+ NewLUT->InputChan));
+
+ if (nTabSize > 0) {
+
+ PtrW = (LPWORD) _cmsCalloc(sizeof(WORD), nTabSize);
+ if (PtrW == NULL) return FALSE;
+
+ Temp = (LPBYTE) _cmsMalloc(nTabSize);
+ if (Temp == NULL) {
+ _cmsFree(PtrW);
+ return FALSE;
+ }
+
+ if (Icc ->Read(Temp, 1, nTabSize, Icc) != nTabSize) {
+ _cmsFree(Temp);
+ _cmsFree(PtrW);
+ return FALSE;
+ }
+
+ NewLUT -> T = PtrW;
+ NewLUT -> Tsize = (unsigned int) (nTabSize * sizeof(WORD));
+
+ for (i = 0; i < nTabSize; i++) {
+
+ *PtrW++ = TO16_TAB(Temp[i]);
+ }
+ _cmsFree(Temp);
+ }
+ else {
+ NewLUT ->T = NULL;
+ NewLUT ->Tsize = 0;
+ NewLUT ->wFlags &= ~LUT_HAS3DGRID;
+ }
+
+
+ // Copy output tables
+
+ Temp = (LPBYTE) _cmsMalloc(256);
+ if (Temp == NULL) {
+ return FALSE;
+ }
+
+ AllLinear = 0;
+ for (i=0; i < NewLUT -> OutputChan; i++) {
+
+ PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * 256);
+ if (PtrW == NULL) {
+ _cmsFree(Temp);
+ return FALSE;
+ }
+
+ NewLUT -> L2[i] = PtrW;
+ if (Icc ->Read(Temp, 1, 256, Icc) != 256) {
+ _cmsFree(Temp);
+ return FALSE;
+ }
+
+ for (j=0; j < 256; j++)
+ PtrW[j] = TO16_TAB(Temp[j]);
+ AllLinear += cmsIsLinear(NewLUT -> L2[i], 256);
+ }
+
+ // Linear input, so ignore full step
+
+ if (AllLinear == NewLUT -> OutputChan) {
+
+ NewLUT -> wFlags &= ~LUT_HASTL2;
+ }
+
+
+ _cmsFree(Temp);
+
+ cmsCalcL16Params(NewLUT -> InputEntries, &NewLUT -> In16params);
+ cmsCalcL16Params(NewLUT -> OutputEntries, &NewLUT -> Out16params);
+ cmsCalcCLUT16Params(NewLUT -> cLutPoints, NewLUT -> InputChan,
+ NewLUT -> OutputChan,
+ &NewLUT -> CLut16params);
+ // Fixup
+
+ if (Icc ->PCS == icSigLabData) {
+
+ // Abstract or Lab identity
+
+ if (Icc -> ColorSpace == icSigLabData)
+
+ FixLUT8bothSides(NewLUT, nTabSize);
+ else
+ FixLUT8(NewLUT, sig, nTabSize);
+
+
+ // Now some additional fixup. Lab encoding on 8 bit makes
+ // impossible to place gray axis on a exact node. However,
+ // some profiles does claim to do that. Poor lcms will try
+ // to detect such condition and fix up "on the fly".
+
+ switch (sig) {
+
+ case icSigBToA0Tag:
+ case icSigBToA1Tag:
+ case icSigBToA2Tag:
+ case icSigGamutTag:
+ case icSigPreview0Tag:
+ case icSigPreview1Tag:
+ case icSigPreview2Tag:
+ {
+ LPWORD WhiteLab, ExpectedWhite;
+ WORD WhiteFixed[MAXCHANNELS], WhiteUnfixed[MAXCHANNELS];
+ int j, nChannels;
+ double Dist, DistFixed, DistUnfixed;
+
+ _cmsEndPointsBySpace(icSigLabData, &WhiteLab, NULL, NULL);
+
+ if (_cmsEndPointsBySpace(Icc -> ColorSpace,
+ &ExpectedWhite, NULL, &nChannels)) {
+
+ // 1.- Find white obtained by both combinations
+
+ NewLUT -> FixGrayAxes = FALSE;
+ cmsEvalLUT(NewLUT, WhiteLab, WhiteUnfixed);
+
+ NewLUT -> FixGrayAxes = TRUE;
+ cmsEvalLUT(NewLUT, WhiteLab, WhiteFixed);
+
+ // 2.- Which method gives closer white?
+
+ DistFixed = DistUnfixed = 0;
+ for (j=0; j < nChannels; j++) {
+
+ Dist = ExpectedWhite[j] - WhiteFixed[j];
+ DistFixed += Dist*Dist;
+ Dist = ExpectedWhite[j] - WhiteUnfixed[j];
+ DistUnfixed += Dist*Dist;
+ }
+
+ // 3.- Decide method
+
+ if (sqrt(DistFixed) < sqrt(DistUnfixed))
+ NewLUT -> FixGrayAxes = TRUE;
+ else
+ NewLUT -> FixGrayAxes = FALSE;
+ }
+
+ }
+ break;
+
+ default:;
+ }
+ }
+
+ return TRUE;
+}
+
+
+
+
+// Case LUT 16
+
+static
+LCMSBOOL ReadLUT16(LPLCMSICCPROFILE Icc, LPLUT NewLUT)
+{
+ icLut16 LUT16;
+ size_t nTabSize;
+ unsigned int i;
+ unsigned int AllLinear;
+ LPWORD PtrW;
+
+
+ if (Icc ->Read(&LUT16, sizeof(icLut16)- SIZEOF_UINT16_ALIGNED, 1, Icc) != 1)
+ return FALSE;
+
+ NewLUT -> wFlags = LUT_HASTL1 | LUT_HASTL2 | LUT_HAS3DGRID;
+ NewLUT -> cLutPoints = LUT16.clutPoints;
+ NewLUT -> InputChan = LUT16.inputChan;
+ NewLUT -> OutputChan = LUT16.outputChan;
+
+ AdjustEndianess16((LPBYTE) &LUT16.inputEnt);
+ AdjustEndianess16((LPBYTE) &LUT16.outputEnt);
+
+ NewLUT -> InputEntries = LUT16.inputEnt;
+ NewLUT -> OutputEntries = LUT16.outputEnt;
+
+ if (!_cmsValidateLUT(NewLUT)) {
+ return FALSE;
+ }
+
+ // Matrix handling
+
+ AdjustEndianess32((LPBYTE) &LUT16.e00);
+ AdjustEndianess32((LPBYTE) &LUT16.e01);
+ AdjustEndianess32((LPBYTE) &LUT16.e02);
+ AdjustEndianess32((LPBYTE) &LUT16.e10);
+ AdjustEndianess32((LPBYTE) &LUT16.e11);
+ AdjustEndianess32((LPBYTE) &LUT16.e12);
+ AdjustEndianess32((LPBYTE) &LUT16.e20);
+ AdjustEndianess32((LPBYTE) &LUT16.e21);
+ AdjustEndianess32((LPBYTE) &LUT16.e22);
+
+ NewLUT -> Matrix.v[0].n[0] = (Fixed32) LUT16.e00;
+ NewLUT -> Matrix.v[0].n[1] = (Fixed32) LUT16.e01;
+ NewLUT -> Matrix.v[0].n[2] = (Fixed32) LUT16.e02;
+ NewLUT -> Matrix.v[1].n[0] = (Fixed32) LUT16.e10;
+ NewLUT -> Matrix.v[1].n[1] = (Fixed32) LUT16.e11;
+ NewLUT -> Matrix.v[1].n[2] = (Fixed32) LUT16.e12;
+ NewLUT -> Matrix.v[2].n[0] = (Fixed32) LUT16.e20;
+ NewLUT -> Matrix.v[2].n[1] = (Fixed32) LUT16.e21;
+ NewLUT -> Matrix.v[2].n[2] = (Fixed32) LUT16.e22;
+
+ // Only operates if not identity...
+
+ if ((NewLUT -> InputChan == 3) && !MAT3isIdentity(&NewLUT -> Matrix, 0.0001)) {
+
+ NewLUT -> wFlags |= LUT_HASMATRIX;
+ }
+
+
+ // Copy input tables
+
+ AllLinear = 0;
+ for (i=0; i < NewLUT -> InputChan; i++) {
+
+ PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * NewLUT -> InputEntries);
+ if (PtrW == NULL) return FALSE;
+
+ NewLUT -> L1[i] = PtrW;
+ if (Icc ->Read(PtrW, sizeof(WORD), NewLUT -> InputEntries, Icc) != NewLUT -> InputEntries) {
+ return FALSE;
+ }
+
+ AdjustEndianessArray16(PtrW, NewLUT -> InputEntries);
+ AllLinear += cmsIsLinear(NewLUT -> L1[i], NewLUT -> InputEntries);
+ }
+
+ // Linear input, so ignore full step
+
+ if (AllLinear == NewLUT -> InputChan) {
+
+ NewLUT -> wFlags &= ~LUT_HASTL1;
+ }
+
+
+ // Copy 3D CLUT
+
+ nTabSize = (NewLUT -> OutputChan * uipow(NewLUT->cLutPoints,
+ NewLUT->InputChan));
+ if (nTabSize > 0) {
+
+ PtrW = (LPWORD) _cmsCalloc(sizeof(WORD), nTabSize);
+ if (PtrW == NULL)
+ return FALSE;
+
+ NewLUT -> T = PtrW;
+ NewLUT -> Tsize = (unsigned int) (nTabSize * sizeof(WORD));
+
+ if (Icc -> Read(PtrW, sizeof(WORD), nTabSize, Icc) != nTabSize) {
+ return FALSE;
+ }
+
+ AdjustEndianessArray16(NewLUT -> T, nTabSize);
+ }
+ else {
+ NewLUT ->T = NULL;
+ NewLUT ->Tsize = 0;
+ NewLUT -> wFlags &= ~LUT_HAS3DGRID;
+ }
+
+ // Copy output tables
+
+ AllLinear = 0;
+ for (i=0; i < NewLUT -> OutputChan; i++) {
+
+ PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * NewLUT -> OutputEntries);
+ if (PtrW == NULL) {
+ return FALSE;
+ }
+
+ NewLUT -> L2[i] = PtrW;
+ if (Icc ->Read(PtrW, sizeof(WORD), NewLUT -> OutputEntries, Icc) != NewLUT -> OutputEntries) {
+ return FALSE;
+ }
+
+ AdjustEndianessArray16(PtrW, NewLUT -> OutputEntries);
+ AllLinear += cmsIsLinear(NewLUT -> L2[i], NewLUT -> OutputEntries);
+ }
+
+ // Linear output, ignore step
+
+ if (AllLinear == NewLUT -> OutputChan)
+ {
+ NewLUT -> wFlags &= ~LUT_HASTL2;
+ }
+
+
+ cmsCalcL16Params(NewLUT -> InputEntries, &NewLUT -> In16params);
+ cmsCalcL16Params(NewLUT -> OutputEntries, &NewLUT -> Out16params);
+ cmsCalcCLUT16Params(NewLUT -> cLutPoints, NewLUT -> InputChan,
+ NewLUT -> OutputChan,
+ &NewLUT -> CLut16params);
+
+ return TRUE;
+}
+
+
+// This is a shared routine for reading curves. It can handle v2 curves
+// as linear, single gamma and table-based as well as v4 parametric curves.
+
+static
+LPGAMMATABLE ReadCurve(LPLCMSICCPROFILE Icc)
+{
+ icUInt32Number Count;
+ LPGAMMATABLE NewGamma;
+ icTagTypeSignature BaseType;
+ int n;
+
+
+ BaseType = ReadBase(Icc);
+ switch (BaseType) {
+
+
+ case ((icTagTypeSignature) 0x9478ee00): // Monaco 2 profiler is BROKEN!
+ case icSigCurveType:
+
+ if (Icc ->Read(&Count, sizeof(icUInt32Number), 1, Icc) != 1) return NULL;
+ AdjustEndianess32((LPBYTE) &Count);
+
+ switch (Count) {
+
+ case 0: // Linear.
+
+ NewGamma = cmsAllocGamma(2);
+ if (!NewGamma) return NULL;
+ NewGamma -> GammaTable[0] = 0;
+ NewGamma -> GammaTable[1] = 0xFFFF;
+ return NewGamma;
+
+ case 1: // Specified as the exponent of gamma function
+ {
+ WORD SingleGammaFixed;
+
+ if (Icc ->Read(&SingleGammaFixed, sizeof(WORD), 1, Icc) != 1) return NULL;
+ AdjustEndianess16((LPBYTE) &SingleGammaFixed);
+ return cmsBuildGamma(4096, Convert8Fixed8(SingleGammaFixed));
+ }
+
+ default: { // Curve
+
+ NewGamma = cmsAllocGamma(Count);
+ if (!NewGamma) return NULL;
+
+ if (Icc ->Read(NewGamma -> GammaTable, sizeof(WORD), Count, Icc) != Count)
+ return NULL;
+ AdjustEndianessArray16(NewGamma -> GammaTable, Count);
+ return NewGamma;
+ }
+ }
+ break;
+
+
+ // Parametric curves
+ case icSigParametricCurveType: {
+
+ int ParamsByType[] = { 1, 3, 4, 5, 7 };
+ double Params[10];
+ icS15Fixed16Number Num;
+ icUInt32Number Reserved;
+ icUInt16Number Type;
+ int i;
+
+ if (Icc -> Read(&Type, sizeof(icUInt16Number), 1, Icc) != 1) return NULL;
+ if (Icc -> Read(&Reserved, sizeof(icUInt16Number), 1, Icc) != 1) return NULL;
+
+ AdjustEndianess16((LPBYTE) &Type);
+ if (Type > 4) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "Unknown parametric curve type '%d' found.", Type);
+ return NULL;
+ }
+
+ ZeroMemory(Params, 10* sizeof(double));
+ n = ParamsByType[Type];
+
+ for (i=0; i < n; i++) {
+ Num = 0;
+ if (Icc -> Read(&Num, sizeof(icS15Fixed16Number), 1, Icc) != 1) return NULL;
+ Params[i] = Convert15Fixed16(Num);
+ }
+
+
+ NewGamma = cmsBuildParametricGamma(4096, Type+1, Params);
+ return NewGamma;
+ }
+
+
+ default:
+ cmsSignalError(LCMS_ERRC_ABORTED, "Bad tag signature '%lx' found.", BaseType);
+ return NULL;
+ }
+
+}
+
+
+// Similar to anterior, but curve is reversed
+
+static
+LPGAMMATABLE ReadCurveReversed(LPLCMSICCPROFILE Icc)
+{
+
+ icTagTypeSignature BaseType;
+ LPGAMMATABLE NewGamma, ReturnGamma;
+ icUInt32Number Count;
+ int n;
+
+
+ BaseType = ReadBase(Icc);
+
+ switch (BaseType) {
+
+
+ case 0x9478ee00L: // Monaco 2 profiler is BROKEN!
+ case icSigCurveType:
+
+ if (Icc -> Read(&Count, sizeof(icUInt32Number), 1, Icc) != 1) return NULL;
+ AdjustEndianess32((LPBYTE) &Count);
+
+
+ switch (Count) {
+
+ case 0: // Linear, reverse is same.
+
+ NewGamma = cmsAllocGamma(2);
+ if (!NewGamma) return NULL;
+
+ NewGamma -> GammaTable[0] = 0;
+ NewGamma -> GammaTable[1] = 0xFFFF;
+ return NewGamma;
+
+ case 1: {
+ WORD SingleGammaFixed;
+
+ if (Icc -> Read(&SingleGammaFixed, sizeof(WORD), 1, Icc) != 1) return NULL;
+ AdjustEndianess16((LPBYTE) &SingleGammaFixed);
+ return cmsBuildGamma(4096, 1./Convert8Fixed8(SingleGammaFixed));
+ }
+
+ default: { // Curve. Do our best to trying to reverse the curve
+
+ NewGamma = cmsAllocGamma(Count);
+ if (!NewGamma) return NULL;
+
+ if (Icc -> Read(NewGamma -> GammaTable, sizeof(WORD), Count, Icc) != Count)
+ return NULL;
+
+ AdjustEndianessArray16(NewGamma -> GammaTable, Count);
+
+ if (Count < 256)
+ Count = 256; // Reverse of simple curve has not necesarely to be simple
+
+ ReturnGamma = cmsReverseGamma(Count, NewGamma);
+ cmsFreeGamma(NewGamma);
+
+ return ReturnGamma;
+ }
+ }
+ break;
+
+
+ // Parametric curves
+ case icSigParametricCurveType: {
+
+ int ParamsByType[] = { 1, 3, 4, 5, 7 };
+ double Params[10];
+ icS15Fixed16Number Num;
+ icUInt32Number Reserved;
+ icUInt16Number Type;
+ int i;
+
+
+ if (Icc -> Read(&Type, sizeof(icUInt16Number), 1, Icc) != 1) return NULL;
+ if (Icc -> Read(&Reserved, sizeof(icUInt16Number), 1, Icc) != 1) return NULL;
+
+ AdjustEndianess16((LPBYTE) &Type);
+ if (Type > 4) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "Unknown parametric curve type '%d' found.", Type);
+ return NULL;
+ }
+
+ ZeroMemory(Params, 10* sizeof(double));
+ n = ParamsByType[Type];
+
+ for (i=0; i < n; i++) {
+ if (Icc -> Read(&Num, sizeof(icS15Fixed16Number), 1, Icc) != 1) return NULL;
+ Params[i] = Convert15Fixed16(Num);
+ }
+
+
+ // Negative type as a mark of reversed curve
+ NewGamma = cmsBuildParametricGamma(4096, -(Type+1), Params);
+ return NewGamma;
+ }
+
+
+ default:
+ cmsSignalError(LCMS_ERRC_ABORTED, "Bad tag signature '%lx' found.", BaseType);
+ return NULL;
+ }
+
+}
+
+
+// V4 stuff. Read matrix for LutAtoB and LutBtoA
+
+static
+LCMSBOOL ReadMatrixOffset(LPLCMSICCPROFILE Icc, size_t Offset, LPLUT NewLUT, DWORD dwFlags)
+{
+
+ icS15Fixed16Number All[12];
+ int i;
+ MAT3 m;
+ VEC3 o;
+
+ if (Icc -> Seek(Icc, Offset)) return FALSE;
+
+ if (Icc ->Read(All, sizeof(icS15Fixed16Number), 12, Icc) != 12)
+ return FALSE;
+
+ for (i=0; i < 12; i++)
+ AdjustEndianess32((LPBYTE) &All[i]);
+
+
+ m.v[0].n[0] = FIXED_TO_DOUBLE((Fixed32) All[0]);
+ m.v[0].n[1] = FIXED_TO_DOUBLE((Fixed32) All[1]);
+ m.v[0].n[2] = FIXED_TO_DOUBLE((Fixed32) All[2]);
+ m.v[1].n[0] = FIXED_TO_DOUBLE((Fixed32) All[3]);
+ m.v[1].n[1] = FIXED_TO_DOUBLE((Fixed32) All[4]);
+ m.v[1].n[2] = FIXED_TO_DOUBLE((Fixed32) All[5]);
+ m.v[2].n[0] = FIXED_TO_DOUBLE((Fixed32) All[6]);
+ m.v[2].n[1] = FIXED_TO_DOUBLE((Fixed32) All[7]);
+ m.v[2].n[2] = FIXED_TO_DOUBLE((Fixed32) All[8]);
+
+ o.n[0] = FIXED_TO_DOUBLE((Fixed32) All[9]);
+ o.n[1] = FIXED_TO_DOUBLE((Fixed32) All[10]);
+ o.n[2] = FIXED_TO_DOUBLE((Fixed32) All[11]);
+
+ cmsSetMatrixLUT4(NewLUT, &m, &o, dwFlags);
+
+ return TRUE;
+}
+
+
+// V4 stuff. Read CLUT part for LutAtoB and LutBtoA
+
+static
+LCMSBOOL ReadCLUT(LPLCMSICCPROFILE Icc, size_t Offset, LPLUT NewLUT)
+{
+ unsigned int j;
+ icCLutStruct CLUT;
+
+ if (Icc -> Seek(Icc, Offset)) return FALSE;
+ if (Icc ->Read(&CLUT, sizeof(icCLutStruct), 1, Icc) != 1) return FALSE;
+
+
+ for (j=1; j < NewLUT ->InputChan; j++) {
+ if (CLUT.gridPoints[0] != CLUT.gridPoints[j]) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "CLUT with different granulatity is currently unsupported.");
+ return FALSE;
+ }
+
+
+ }
+
+ if (cmsAlloc3DGrid(NewLUT, CLUT.gridPoints[0], NewLUT ->InputChan,
+ NewLUT ->OutputChan) == NULL) return FALSE;
+
+ // Precission can be 1 or 2 bytes
+
+ if (CLUT.prec == 1) {
+
+ BYTE v;
+ unsigned int i;
+
+ for (i=0; i < NewLUT->Tsize / sizeof(WORD); i++) {
+ if (Icc ->Read(&v, sizeof(BYTE), 1, Icc) != 1) return FALSE;
+ NewLUT->T[i] = TO16_TAB(v);
+ }
+
+ }
+ else
+ if (CLUT.prec == 2) {
+
+ size_t n = NewLUT->Tsize / sizeof(WORD);
+
+ if (Icc ->Read(NewLUT ->T, sizeof(WORD), n, Icc) != n) return FALSE;
+ AdjustEndianessArray16(NewLUT ->T, NewLUT->Tsize / sizeof(WORD));
+ }
+ else {
+ cmsSignalError(LCMS_ERRC_ABORTED, "Unknow precission of '%d'", CLUT.prec);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static
+void ResampleCurves(LPGAMMATABLE Curves[], int nCurves)
+{
+ int i;
+ LPSAMPLEDCURVE sc;
+
+ for (i=0; i < nCurves; i++) {
+ sc = cmsConvertGammaToSampledCurve(Curves[i], 4096);
+ cmsFreeGamma(Curves[i]);
+ Curves[i] = cmsConvertSampledCurveToGamma(sc, 0xFFFF);
+ cmsFreeSampledCurve(sc);
+ }
+
+}
+
+
+static
+void SkipAlignment(LPLCMSICCPROFILE Icc)
+{
+ BYTE Buffer[4];
+ size_t At = Icc ->Tell(Icc);
+ int BytesToNextAlignedPos = (int) (At % 4);
+
+ Icc ->Read(Buffer, 1, BytesToNextAlignedPos, Icc);
+}
+
+// Read a set of curves from specific offset
+static
+LCMSBOOL ReadSetOfCurves(LPLCMSICCPROFILE Icc, size_t Offset, LPLUT NewLUT, int nLocation)
+{
+ LPGAMMATABLE Curves[MAXCHANNELS];
+ unsigned int i, nCurves;
+
+ if (Icc -> Seek(Icc, Offset)) return FALSE;
+
+ if (nLocation == 1 || nLocation == 3)
+
+ nCurves = NewLUT ->InputChan;
+ else
+ nCurves = NewLUT ->OutputChan;
+
+ ZeroMemory(Curves, sizeof(Curves));
+ for (i=0; i < nCurves; i++) {
+
+ Curves[i] = ReadCurve(Icc);
+ if (Curves[i] == NULL) goto Error;
+ SkipAlignment(Icc);
+ }
+
+ // March-26'08: some V4 profiles may have different sampling
+ // rates, in this case resample all curves to maximum
+
+ for (i=1; i < nCurves; i++) {
+ if (Curves[i]->nEntries != Curves[0]->nEntries) {
+ ResampleCurves(Curves, nCurves);
+ break;
+ }
+ }
+
+ NewLUT = cmsAllocLinearTable(NewLUT, Curves, nLocation);
+ if (NewLUT == NULL) goto Error;
+
+ for (i=0; i < nCurves; i++)
+ cmsFreeGamma(Curves[i]);
+
+ return TRUE;
+
+Error:
+
+ for (i=0; i < nCurves; i++)
+ if (Curves[i])
+ cmsFreeGamma(Curves[i]);
+
+ return FALSE;
+
+
+}
+
+// V4 stuff. LutAtoB type
+//
+// [L1] -> [CLUT] -> [L4] -> [Mat4] -> [Ofs4] -> [L2]
+//
+// Mat, Mat3, Ofs3, L3 are missing
+// L1 = A curves
+// L4 = M curves
+// L2 = B curves
+
+static
+LCMSBOOL ReadLUT_A2B(LPLCMSICCPROFILE Icc, LPLUT NewLUT, size_t BaseOffset, icTagSignature sig)
+{
+ icLutAtoB LUT16;
+
+ if (Icc ->Read(&LUT16, sizeof(icLutAtoB), 1, Icc) != 1) return FALSE;
+
+ NewLUT -> InputChan = LUT16.inputChan;
+ NewLUT -> OutputChan = LUT16.outputChan;
+
+ // Validate the NewLUT here to avoid excessive number of channels
+ // (leading to stack-based buffer overflow in ReadSetOfCurves).
+ // Needs revalidation after table size is filled in.
+ if (!_cmsValidateLUT(NewLUT)) {
+ return FALSE;
+ }
+
+ AdjustEndianess32((LPBYTE) &LUT16.offsetB);
+ AdjustEndianess32((LPBYTE) &LUT16.offsetMat);
+ AdjustEndianess32((LPBYTE) &LUT16.offsetM);
+ AdjustEndianess32((LPBYTE) &LUT16.offsetC);
+ AdjustEndianess32((LPBYTE) &LUT16.offsetA);
+
+ if (LUT16.offsetB != 0)
+ ReadSetOfCurves(Icc, BaseOffset + LUT16.offsetB, NewLUT, 2);
+
+ if (LUT16.offsetMat != 0)
+ ReadMatrixOffset(Icc, BaseOffset + LUT16.offsetMat, NewLUT, LUT_HASMATRIX4);
+
+
+ if (LUT16.offsetM != 0)
+ ReadSetOfCurves(Icc, BaseOffset + LUT16.offsetM, NewLUT, 4);
+
+ if (LUT16.offsetC != 0)
+ ReadCLUT(Icc, BaseOffset + LUT16.offsetC, NewLUT);
+
+ if (LUT16.offsetA!= 0)
+ ReadSetOfCurves(Icc, BaseOffset + LUT16.offsetA, NewLUT, 1);
+
+ // Convert to v2 PCS
+
+ if (Icc ->PCS == icSigLabData) {
+
+ switch (sig) {
+
+ case icSigAToB0Tag:
+ case icSigAToB1Tag:
+ case icSigAToB2Tag:
+ case icSigGamutTag:
+ case icSigPreview0Tag:
+ case icSigPreview1Tag:
+ case icSigPreview2Tag:
+
+ NewLUT ->wFlags |= LUT_V4_INPUT_EMULATE_V2;
+ break;
+
+ default:;
+ }
+ }
+
+
+ return TRUE;
+}
+
+// V4 stuff. LutBtoA type
+
+static
+LCMSBOOL ReadLUT_B2A(LPLCMSICCPROFILE Icc, LPLUT NewLUT, size_t BaseOffset, icTagSignature sig)
+{
+ icLutBtoA LUT16;
+
+ if (Icc ->Read(&LUT16, sizeof(icLutBtoA), 1, Icc) != 1) return FALSE;
+
+ NewLUT -> InputChan = LUT16.inputChan;
+ NewLUT -> OutputChan = LUT16.outputChan;
+
+ // Validate the NewLUT here to avoid excessive number of channels
+ // (leading to stack-based buffer overflow in ReadSetOfCurves).
+ // Needs revalidation after table size is filled in.
+ if (!_cmsValidateLUT(NewLUT)) {
+ return FALSE;
+ }
+
+ AdjustEndianess32((LPBYTE) &LUT16.offsetB);
+ AdjustEndianess32((LPBYTE) &LUT16.offsetMat);
+ AdjustEndianess32((LPBYTE) &LUT16.offsetM);
+ AdjustEndianess32((LPBYTE) &LUT16.offsetC);
+ AdjustEndianess32((LPBYTE) &LUT16.offsetA);
+
+
+ if (LUT16.offsetB != 0)
+ ReadSetOfCurves(Icc, BaseOffset + LUT16.offsetB, NewLUT, 1);
+
+ if (LUT16.offsetMat != 0)
+ ReadMatrixOffset(Icc, BaseOffset + LUT16.offsetMat, NewLUT, LUT_HASMATRIX3);
+
+ if (LUT16.offsetM != 0)
+ ReadSetOfCurves(Icc, BaseOffset + LUT16.offsetM, NewLUT, 3);
+
+ if (LUT16.offsetC != 0)
+ ReadCLUT(Icc, BaseOffset + LUT16.offsetC, NewLUT);
+
+ if (LUT16.offsetA!= 0)
+ ReadSetOfCurves(Icc, BaseOffset + LUT16.offsetA, NewLUT, 2);
+
+
+ // Convert to v2 PCS
+
+ if (Icc ->PCS == icSigLabData) {
+
+ switch (sig) {
+
+ case icSigBToA0Tag:
+ case icSigBToA1Tag:
+ case icSigBToA2Tag:
+ case icSigGamutTag:
+ case icSigPreview0Tag:
+ case icSigPreview1Tag:
+ case icSigPreview2Tag:
+
+ NewLUT ->wFlags |= LUT_V4_OUTPUT_EMULATE_V2;
+ break;
+
+ default:;
+ }
+ }
+
+ return TRUE;
+}
+
+// CLUT main reader
+
+LPLUT LCMSEXPORT cmsReadICCLut(cmsHPROFILE hProfile, icTagSignature sig)
+{
+
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+ icTagTypeSignature BaseType;
+ int n;
+ size_t offset;
+ LPLUT NewLUT;
+
+ n = _cmsSearchTag(Icc, sig, TRUE);
+ if (n < 0)
+ return NULL;
+
+
+ // If is in memory, the LUT is already there, so throw a copy
+ if (Icc -> TagPtrs[n]) {
+
+ return cmsDupLUT((LPLUT) Icc ->TagPtrs[n]);
+ }
+
+ offset = Icc -> TagOffsets[n];
+
+ if (Icc -> Seek(Icc, offset))
+ return NULL;
+
+ BaseType = ReadBase(Icc);
+
+
+ NewLUT = cmsAllocLUT();
+ if (!NewLUT) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "cmsAllocLUT() failed");
+ return NULL;
+ }
+
+
+ switch (BaseType) {
+
+ case icSigLut8Type: if (!ReadLUT8(Icc, NewLUT, sig)) {
+ cmsFreeLUT(NewLUT);
+ return NULL;
+ }
+ break;
+
+ case icSigLut16Type: if (!ReadLUT16(Icc, NewLUT)) {
+ cmsFreeLUT(NewLUT);
+ return NULL;
+ }
+ break;
+
+ case icSiglutAtoBType: if (!ReadLUT_A2B(Icc, NewLUT, offset, sig)) {
+ cmsFreeLUT(NewLUT);
+ return NULL;
+ }
+ break;
+
+ case icSiglutBtoAType: if (!ReadLUT_B2A(Icc, NewLUT, offset, sig)) {
+ cmsFreeLUT(NewLUT);
+ return NULL;
+ }
+ break;
+
+ default: cmsSignalError(LCMS_ERRC_ABORTED, "Bad tag signature %lx found.", BaseType);
+ cmsFreeLUT(NewLUT);
+ return NULL;
+ }
+
+
+ return NewLUT;
+}
+
+
+// Sets the language & country preferences. Used only in ICC 4.0 profiles
+
+void LCMSEXPORT cmsSetLanguage(const char LanguageCode[4], const char CountryCode[4])
+{
+
+ int LanguageCodeInt = *(int *) LanguageCode;
+ int CountryCodeInt = *(int *) CountryCode;
+
+ AdjustEndianess32((LPBYTE) &LanguageCodeInt);
+ AdjustEndianess32((LPBYTE) &CountryCodeInt);
+
+ GlobalLanguageCode = LanguageCodeInt;
+ GlobalCountryCode = CountryCodeInt;
+}
+
+
+
+// Some tags (e.g, 'pseq') can have text tags embedded. This function
+// handles such special case. Returns -1 on error, or the number of bytes left on success.
+
+static
+int ReadEmbeddedTextTag(LPLCMSICCPROFILE Icc, size_t size, char* Name, size_t size_max)
+{
+ icTagTypeSignature BaseType;
+
+
+ BaseType = ReadBase(Icc);
+ size -= sizeof(icTagBase);
+
+ switch (BaseType) {
+
+ case icSigTextDescriptionType: {
+
+ icUInt32Number AsciiCount;
+ icUInt32Number i, UnicodeCode, UnicodeCount;
+ icUInt16Number ScriptCodeCode, Dummy;
+ icUInt8Number ScriptCodeCount;
+
+ if (Icc ->Read(&AsciiCount, sizeof(icUInt32Number), 1, Icc) != 1) return -1;
+
+ if (size < sizeof(icUInt32Number)) return (int) size;
+ size -= sizeof(icUInt32Number);
+
+ AdjustEndianess32((LPBYTE) &AsciiCount);
+ Icc ->Read(Name, 1,
+ (AsciiCount >= size_max) ? (size_max-1) : AsciiCount, Icc);
+
+ if (size < AsciiCount) return (int) size;
+ size -= AsciiCount;
+
+ // Skip Unicode code
+
+ if (Icc ->Read(&UnicodeCode, sizeof(icUInt32Number), 1, Icc) != 1) return -1;
+ if (size < sizeof(icUInt32Number)) return (int) size;
+ size -= sizeof(icUInt32Number);
+
+ if (Icc ->Read(&UnicodeCount, sizeof(icUInt32Number), 1, Icc) != 1) return -1;
+ if (size < sizeof(icUInt32Number)) return (int) size;
+ size -= sizeof(icUInt32Number);
+
+ AdjustEndianess32((LPBYTE) &UnicodeCount);
+
+ if (UnicodeCount > size) return (int) size;
+
+ for (i=0; i < UnicodeCount; i++) {
+ size_t nread = Icc ->Read(&Dummy, sizeof(icUInt16Number), 1, Icc);
+ if (nread != 1) return (int) size;
+ size -= sizeof(icUInt16Number);
+ }
+
+ // Skip ScriptCode code
+
+ if (Icc ->Read(&ScriptCodeCode, sizeof(icUInt16Number), 1, Icc) != 1) return -1;
+ size -= sizeof(icUInt16Number);
+ if (Icc ->Read(&ScriptCodeCount, sizeof(icUInt8Number), 1, Icc) != 1) return -1;
+ size -= sizeof(icUInt8Number);
+
+ // Should remain 67 bytes as filler
+
+ if (size < 67) return (int) size;
+
+ for (i=0; i < 67; i++) {
+ size_t nread = Icc ->Read(&Dummy, sizeof(icUInt8Number), 1, Icc);
+ if (nread != 1) return (int) size;
+ size --;
+ }
+ }
+ break;
+
+
+ case icSigCopyrightTag: // Broken profiles from agfa does store copyright info in such type
+ case icSigTextType:
+ {
+ char Dummy;
+ size_t i, Missing = 0;
+
+ if (size >= size_max) {
+
+ Missing = size - size_max + 1;
+ size = size_max - 1;
+ }
+
+ if (Icc -> Read(Name, 1, size, Icc) != size) return -1;
+
+ for (i=0; i < Missing; i++)
+ Icc -> Read(&Dummy, 1, 1, Icc);
+ }
+ break;
+
+ // MultiLocalizedUnicodeType, V4 only
+
+ case icSigMultiLocalizedUnicodeType: {
+
+ icUInt32Number Count, RecLen;
+ icUInt16Number Language, Country;
+ icUInt32Number ThisLen, ThisOffset;
+ size_t Offset = 0;
+ size_t Len = 0;
+ size_t i;
+ wchar_t* wchar = L"";
+
+
+ if (Icc ->Read(&Count, sizeof(icUInt32Number), 1, Icc) != 1) return -1;
+ AdjustEndianess32((LPBYTE) &Count);
+ if (Icc ->Read(&RecLen, sizeof(icUInt32Number), 1, Icc) != 1) return -1;
+ AdjustEndianess32((LPBYTE) &RecLen);
+
+ if (RecLen != 12) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "multiLocalizedUnicodeType of len != 12 is not supported.");
+ return -1;
+ }
+
+ for (i=0; i < Count; i++) {
+
+ if (Icc ->Read(&Language, sizeof(icUInt16Number), 1, Icc) != 1) return -1;
+ AdjustEndianess16((LPBYTE) &Language);
+ if (Icc ->Read(&Country, sizeof(icUInt16Number), 1, Icc) != 1) return -1;
+ AdjustEndianess16((LPBYTE) &Country);
+
+ if (Icc ->Read(&ThisLen, sizeof(icUInt32Number), 1, Icc) != 1) return -1;
+ AdjustEndianess32((LPBYTE) &ThisLen);
+
+ if (Icc ->Read(&ThisOffset, sizeof(icUInt32Number), 1, Icc) != 1) return -1;
+ AdjustEndianess32((LPBYTE) &ThisOffset);
+
+ if (Language == GlobalLanguageCode || Offset == 0) {
+
+ Len = ThisLen; Offset = ThisOffset;
+ if (Country == GlobalCountryCode)
+ break; // Found
+ }
+
+ }
+
+
+ if (Offset == 0) {
+
+ strcpy(Name, "(no info)");
+ break;
+ }
+
+ // Compute true offset
+ Offset -= 12 * Count + 8 + sizeof(icTagBase);
+
+ // Skip unused bytes
+ for (i=0; i < Offset; i++) {
+
+ char Discard;
+ if (Icc ->Read(&Discard, 1, 1, Icc) != 1) return -1;
+ }
+
+
+ // Bound len
+ if (Len < 0) Len = 0;
+ if (Len > 20*1024) Len = 20 * 1024;
+
+ wchar = (wchar_t*) _cmsMalloc(Len*sizeof(wchar_t)+2);
+ if (!wchar) return -1;
+
+ if (Icc ->Read(wchar, 1, Len, Icc) != Len) return -1;
+ AdjustEndianessArray16((LPWORD) wchar, Len / 2);
+
+ wchar[Len / 2] = L'\0';
+ i = wcstombs(Name, wchar, size_max );
+ if (i == ((size_t) -1)) {
+
+ Name[0] = 0; // Error
+ }
+
+ _cmsFree((void*) wchar);
+ }
+ break;
+
+ default:
+ cmsSignalError(LCMS_ERRC_ABORTED, "Bad tag signature %lx found.", BaseType);
+ return -1;
+ }
+
+ return (int) size;
+}
+
+
+// Take an ASCII item. Takes at most size_max bytes
+
+int LCMSEXPORT cmsReadICCTextEx(cmsHPROFILE hProfile, icTagSignature sig, char *Name, size_t size_max)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+ size_t offset, size;
+ int n;
+
+ n = _cmsSearchTag(Icc, sig, TRUE);
+ if (n < 0)
+ return -1;
+
+ size = Icc -> TagSizes[n];
+
+ if (Icc -> TagPtrs[n]) {
+
+ if (size > size_max)
+ size = size_max;
+
+ CopyMemory(Name, Icc -> TagPtrs[n], size);
+
+ return (int) Icc -> TagSizes[n];
+ }
+
+ offset = Icc -> TagOffsets[n];
+
+
+ if (Icc -> Seek(Icc, offset))
+ return -1;
+
+ if (ReadEmbeddedTextTag(Icc, size, Name, size_max) < 0) return -1;
+
+ return (int) size;
+}
+
+// Keep compatibility with older versions
+
+int LCMSEXPORT cmsReadICCText(cmsHPROFILE hProfile, icTagSignature sig, char *Text)
+{
+ return cmsReadICCTextEx(hProfile, sig, Text, LCMS_DESC_MAX);
+}
+
+
+// Take an XYZ item
+
+static
+int ReadICCXYZ(cmsHPROFILE hProfile, icTagSignature sig, LPcmsCIEXYZ Value, LCMSBOOL lIsFatal)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+ icTagTypeSignature BaseType;
+ size_t offset;
+ int n;
+ icXYZNumber XYZ;
+
+ n = _cmsSearchTag(Icc, sig, FALSE);
+ if (n < 0)
+ return -1;
+
+ if (Icc -> TagPtrs[n]) {
+
+ CopyMemory(Value, Icc -> TagPtrs[n], Icc -> TagSizes[n]);
+ return (int) Icc -> TagSizes[n];
+ }
+
+ offset = Icc -> TagOffsets[n];
+
+ if (Icc -> Seek(Icc, offset))
+ return -1;
+
+
+ BaseType = ReadBase(Icc);
+
+ switch (BaseType) {
+
+
+ case 0x7c3b10cL: // Some apple broken embedded profiles does not have correct type
+ case icSigXYZType:
+
+ Icc ->Read(&XYZ, sizeof(icXYZNumber), 1, Icc);
+ Value -> X = Convert15Fixed16(XYZ.X);
+ Value -> Y = Convert15Fixed16(XYZ.Y);
+ Value -> Z = Convert15Fixed16(XYZ.Z);
+ break;
+
+ // Aug/21-2001 - Monaco 2 does have WRONG values.
+
+ default:
+ if (lIsFatal)
+ cmsSignalError(LCMS_ERRC_ABORTED, "Bad tag signature %lx found.", BaseType);
+ return -1;
+ }
+
+ return 1;
+}
+
+
+// Read a icSigS15Fixed16ArrayType (currently only a 3x3 matrix)
+
+static
+int ReadICCXYZArray(cmsHPROFILE hProfile, icTagSignature sig, LPMAT3 v)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+ icTagTypeSignature BaseType;
+ size_t offset, sz;
+ int i, n;
+ icXYZNumber XYZ[3];
+ cmsCIEXYZ XYZdbl[3];
+
+
+ n = _cmsSearchTag(Icc, sig, FALSE);
+ if (n < 0)
+ return -1; // Not found
+
+ if (Icc -> TagPtrs[n]) {
+
+ CopyMemory(v, Icc -> TagPtrs[n], Icc -> TagSizes[n]);
+ return (int) Icc -> TagSizes[n];
+ }
+
+ offset = Icc -> TagOffsets[n];
+
+ if (Icc -> Seek(Icc, offset))
+ return -1;
+
+ BaseType = ReadBase(Icc);
+
+ switch (BaseType) {
+
+ case icSigS15Fixed16ArrayType:
+
+ sz = Icc ->TagSizes[n] / sizeof(icXYZNumber);
+
+ if (sz != 3) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "Bad array size of %d entries.", sz);
+ return -1;
+ }
+
+ Icc ->Read(XYZ, sizeof(icXYZNumber), 3, Icc);
+
+ for (i=0; i < 3; i++) {
+
+ XYZdbl[i].X = Convert15Fixed16(XYZ[i].X);
+ XYZdbl[i].Y = Convert15Fixed16(XYZ[i].Y);
+ XYZdbl[i].Z = Convert15Fixed16(XYZ[i].Z);
+ }
+
+ CopyMemory(v, XYZdbl, 3*sizeof(cmsCIEXYZ));
+ break;
+
+ default:
+ cmsSignalError(LCMS_ERRC_ABORTED, "Bad tag signature %lx found.", BaseType);
+ return -1;
+
+ }
+
+ return sizeof(MAT3);
+}
+
+
+
+// Primaries are to be in xyY notation
+
+LCMSBOOL LCMSEXPORT cmsTakeColorants(LPcmsCIEXYZTRIPLE Dest, cmsHPROFILE hProfile)
+{
+ if (ReadICCXYZ(hProfile, icSigRedColorantTag, &Dest -> Red, TRUE) < 0) return FALSE;
+ if (ReadICCXYZ(hProfile, icSigGreenColorantTag, &Dest -> Green, TRUE) < 0) return FALSE;
+ if (ReadICCXYZ(hProfile, icSigBlueColorantTag, &Dest -> Blue, TRUE) < 0) return FALSE;
+
+ return TRUE;
+}
+
+
+LCMSBOOL cmsReadICCMatrixRGB2XYZ(LPMAT3 r, cmsHPROFILE hProfile)
+{
+ cmsCIEXYZTRIPLE Primaries;
+
+ if (!cmsTakeColorants(&Primaries, hProfile)) return FALSE;
+
+ VEC3init(&r -> v[0], Primaries.Red.X, Primaries.Green.X, Primaries.Blue.X);
+ VEC3init(&r -> v[1], Primaries.Red.Y, Primaries.Green.Y, Primaries.Blue.Y);
+ VEC3init(&r -> v[2], Primaries.Red.Z, Primaries.Green.Z, Primaries.Blue.Z);
+
+ return TRUE;
+
+}
+
+
+// Always return a suitable matrix
+
+LCMSBOOL cmsReadChromaticAdaptationMatrix(LPMAT3 r, cmsHPROFILE hProfile)
+{
+
+ if (ReadICCXYZArray(hProfile, icSigChromaticAdaptationTag, r) < 0) {
+
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+
+ // For display profiles, revert to bradford. Else take identity.
+
+ MAT3identity(r);
+
+ // Emissive devices have non-identity chad
+
+ if ((cmsGetDeviceClass(hProfile) == icSigDisplayClass) ||
+ cmsTakeHeaderFlags(hProfile) & icTransparency) {
+
+ // NULL for cone defaults to Bradford, from media to D50
+ cmsAdaptationMatrix(r, NULL, &Icc ->MediaWhitePoint, &Icc ->Illuminant);
+ }
+ }
+
+ return TRUE;
+}
+
+
+
+LPGAMMATABLE LCMSEXPORT cmsReadICCGamma(cmsHPROFILE hProfile, icTagSignature sig)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+ size_t offset;
+ int n;
+
+
+ n = _cmsSearchTag(Icc, sig, TRUE);
+ if (n < 0)
+ return NULL;
+
+ if (Icc -> TagPtrs[n]) {
+
+ return cmsDupGamma((LPGAMMATABLE) Icc -> TagPtrs[n]);
+ }
+
+ offset = Icc -> TagOffsets[n];
+
+ if (Icc -> Seek(Icc, offset))
+ return NULL;
+
+ return ReadCurve(Icc);
+
+}
+
+
+// Some ways have analytical revese. This function accounts for that
+
+LPGAMMATABLE LCMSEXPORT cmsReadICCGammaReversed(cmsHPROFILE hProfile, icTagSignature sig)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+ size_t offset;
+ int n;
+
+
+ n = _cmsSearchTag(Icc, sig, TRUE);
+ if (n < 0)
+ return NULL;
+
+ if (Icc -> TagPtrs[n]) {
+
+ return cmsReverseGamma(256, (LPGAMMATABLE) Icc -> TagPtrs[n]);
+ }
+
+ offset = Icc -> TagOffsets[n];
+
+ if (Icc -> Seek(Icc, offset))
+ return NULL;
+
+ return ReadCurveReversed(Icc);
+}
+
+// Check Named color header
+
+static
+LCMSBOOL CheckHeader(LPcmsNAMEDCOLORLIST v, icNamedColor2* nc2)
+{
+ if (v ->Prefix[0] == 0 && v ->Suffix[0] == 0 && v ->ColorantCount == 0) return TRUE;
+
+ if (stricmp(v ->Prefix, (const char*) nc2 ->prefix) != 0) return FALSE;
+ if (stricmp(v ->Suffix, (const char*) nc2 ->suffix) != 0) return FALSE;
+
+ return ((int) v ->ColorantCount == (int) nc2 ->nDeviceCoords);
+}
+
+// Read named color list
+
+int cmsReadICCnamedColorList(cmsHTRANSFORM xform, cmsHPROFILE hProfile, icTagSignature sig)
+{
+ _LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform;
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+ int n;
+ icTagTypeSignature BaseType;
+ size_t offset;
+
+ n = _cmsSearchTag(Icc, sig, TRUE);
+ if (n < 0)
+ return 0;
+
+ if (Icc -> TagPtrs[n]) {
+
+ // This replaces actual named color list.
+ size_t size = Icc -> TagSizes[n];
+
+ if (v ->NamedColorList) cmsFreeNamedColorList(v ->NamedColorList);
+ v -> NamedColorList = (LPcmsNAMEDCOLORLIST) _cmsMalloc(size);
+ CopyMemory(v -> NamedColorList, Icc ->TagPtrs[n], size);
+ return v ->NamedColorList->nColors;
+ }
+
+ offset = Icc -> TagOffsets[n];
+
+ if (Icc -> Seek(Icc, offset))
+ return 0;
+
+ BaseType = ReadBase(Icc);
+
+ switch (BaseType) {
+
+ // I never have seen one of these. Probably is not worth of implementing.
+
+ case icSigNamedColorType: {
+
+ cmsSignalError(LCMS_ERRC_WARNING, "Ancient named color profiles are not supported.");
+ return 0;
+ }
+
+ // The named color struct
+
+ case icSigNamedColor2Type: {
+
+ icNamedColor2 nc2;
+ unsigned int i, j;
+
+ if (Icc -> Read(&nc2, sizeof(icNamedColor2) - SIZEOF_UINT8_ALIGNED, 1, Icc) != 1) return 0;
+ AdjustEndianess32((LPBYTE) &nc2.vendorFlag);
+ AdjustEndianess32((LPBYTE) &nc2.count);
+ AdjustEndianess32((LPBYTE) &nc2.nDeviceCoords);
+
+ if (!CheckHeader(v->NamedColorList, &nc2)) {
+ cmsSignalError(LCMS_ERRC_WARNING, "prefix/suffix/device for named color profiles mismatch.");
+ return 0;
+ }
+
+ if (nc2.nDeviceCoords > MAXCHANNELS) {
+ cmsSignalError(LCMS_ERRC_WARNING, "Too many device coordinates.");
+ return 0;
+ }
+
+ strncpy(v ->NamedColorList->Prefix, (const char*) nc2.prefix, 32);
+ strncpy(v ->NamedColorList->Suffix, (const char*) nc2.suffix, 32);
+ v ->NamedColorList->Prefix[32] = v->NamedColorList->Suffix[32] = 0;
+
+ v ->NamedColorList ->ColorantCount = nc2.nDeviceCoords;
+
+ for (i=0; i < nc2.count; i++) {
+
+ WORD PCS[3];
+ WORD Colorant[MAXCHANNELS];
+ char Root[33];
+
+ ZeroMemory(Colorant, sizeof(WORD) * MAXCHANNELS);
+ Icc -> Read(Root, 1, 32, Icc);
+ Icc -> Read(PCS, 3, sizeof(WORD), Icc);
+
+ for (j=0; j < 3; j++)
+ AdjustEndianess16((LPBYTE) &PCS[j]);
+
+ Icc -> Read(Colorant, sizeof(WORD), nc2.nDeviceCoords, Icc);
+
+ for (j=0; j < nc2.nDeviceCoords; j++)
+ AdjustEndianess16((LPBYTE) &Colorant[j]);
+
+ cmsAppendNamedColor(v, Root, PCS, Colorant);
+ }
+
+ return v ->NamedColorList->nColors;
+ }
+ break;
+
+ default:
+ cmsSignalError(LCMS_ERRC_WARNING, "Bad tag signature '%lx' found.", BaseType);
+ return 0;
+ }
+
+ // It would never reach here
+ // return 0;
+}
+
+
+
+// Read colorant tables
+
+LPcmsNAMEDCOLORLIST LCMSEXPORT cmsReadColorantTable(cmsHPROFILE hProfile, icTagSignature sig)
+{
+ icInt32Number n;
+ icUInt32Number Count, i;
+ size_t offset;
+ icTagTypeSignature BaseType;
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+ LPcmsNAMEDCOLORLIST List;
+
+ n = _cmsSearchTag(Icc, sig, FALSE);
+ if (n < 0)
+ return NULL; // Not found
+
+ if (Icc -> TagPtrs[n]) {
+
+ size_t size = Icc -> TagSizes[n];
+ void* v = _cmsMalloc(size);
+
+ if (v == NULL) return NULL;
+ CopyMemory(v, Icc -> TagPtrs[n], size);
+ return (LPcmsNAMEDCOLORLIST) v;
+ }
+
+
+ offset = Icc -> TagOffsets[n];
+
+ if (Icc -> Seek(Icc, offset))
+ return NULL;
+
+ BaseType = ReadBase(Icc);
+
+ if (BaseType != icSigColorantTableType) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "Bad tag signature '%lx' found.", BaseType);
+ return NULL;
+ }
+
+
+ if (Icc ->Read(&Count, sizeof(icUInt32Number), 1, Icc) != 1) return NULL;
+ AdjustEndianess32((LPBYTE) &Count);
+
+ if (Count > MAXCHANNELS) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "Too many colorants '%lx'", Count);
+ return NULL;
+ }
+
+ List = cmsAllocNamedColorList(Count);
+ for (i=0; i < Count; i++) {
+
+ if (!Icc ->Read(List->List[i].Name, 1, 32 , Icc)) goto Error;
+ if (!Icc ->Read(List->List[i].PCS, sizeof(icUInt16Number), 3, Icc)) goto Error;
+ AdjustEndianessArray16(List->List[i].PCS, 3);
+ }
+
+ return List;
+
+Error:
+ cmsFreeNamedColorList(List);
+ return NULL;
+
+}
+
+
+
+// Uncooked manufacturer
+
+const char* LCMSEXPORT cmsTakeManufacturer(cmsHPROFILE hProfile)
+{
+
+ static char Manufacturer[LCMS_DESC_MAX] = "";
+
+ Manufacturer[0] = 0;
+
+ if (cmsIsTag(hProfile, icSigDeviceMfgDescTag)) {
+
+ cmsReadICCTextEx(hProfile, icSigDeviceMfgDescTag, Manufacturer, LCMS_DESC_MAX);
+ }
+
+ return Manufacturer;
+}
+
+// Uncooked model
+
+const char* LCMSEXPORT cmsTakeModel(cmsHPROFILE hProfile)
+{
+
+ static char Model[LCMS_DESC_MAX] = "";
+
+ Model[0] = 0;
+
+ if (cmsIsTag(hProfile, icSigDeviceModelDescTag)) {
+
+ cmsReadICCTextEx(hProfile, icSigDeviceModelDescTag, Model, LCMS_DESC_MAX);
+ }
+
+ return Model;
+}
+
+
+const char* LCMSEXPORT cmsTakeCopyright(cmsHPROFILE hProfile)
+{
+
+ static char Copyright[LCMS_DESC_MAX] = "";
+
+ Copyright[0] = 0;
+ if (cmsIsTag(hProfile, icSigCopyrightTag)) {
+
+ cmsReadICCTextEx(hProfile, icSigCopyrightTag, Copyright, LCMS_DESC_MAX);
+ }
+
+ return Copyright;
+}
+
+
+// We compute name with model - manufacturer
+
+const char* LCMSEXPORT cmsTakeProductName(cmsHPROFILE hProfile)
+{
+ static char Name[LCMS_DESC_MAX*2+4];
+ char Manufacturer[LCMS_DESC_MAX], Model[LCMS_DESC_MAX];
+
+ Name[0] = '\0';
+ Manufacturer[0] = Model[0] = '\0';
+
+ if (cmsIsTag(hProfile, icSigDeviceMfgDescTag)) {
+
+ cmsReadICCTextEx(hProfile, icSigDeviceMfgDescTag, Manufacturer, LCMS_DESC_MAX);
+ }
+
+ if (cmsIsTag(hProfile, icSigDeviceModelDescTag)) {
+
+ cmsReadICCTextEx(hProfile, icSigDeviceModelDescTag, Model, LCMS_DESC_MAX);
+ }
+
+ if (!Manufacturer[0] && !Model[0]) {
+
+ if (cmsIsTag(hProfile, icSigProfileDescriptionTag)) {
+
+ cmsReadICCTextEx(hProfile, icSigProfileDescriptionTag, Name, LCMS_DESC_MAX);
+ return Name;
+ }
+ else return "{no name}";
+ }
+
+
+ if (!Manufacturer[0] ||
+ strncmp(Model, Manufacturer, 8) == 0 || strlen(Model) > 30)
+ strcpy(Name, Model);
+ else
+ sprintf(Name, "%s - %s", Model, Manufacturer);
+
+ return Name;
+
+}
+
+
+// We compute desc with manufacturer - model
+
+const char* LCMSEXPORT cmsTakeProductDesc(cmsHPROFILE hProfile)
+{
+ static char Name[2048];
+
+ if (cmsIsTag(hProfile, icSigProfileDescriptionTag)) {
+
+ cmsReadICCText(hProfile, icSigProfileDescriptionTag, Name);
+ }
+ else return cmsTakeProductName(hProfile);
+
+ if (strncmp(Name, "Copyrig", 7) == 0)
+ return cmsTakeProductName(hProfile);
+
+ return Name;
+}
+
+
+const char* LCMSEXPORT cmsTakeProductInfo(cmsHPROFILE hProfile)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+
+ static char Info[4096];
+
+ Info[0] = '\0';
+
+ if (cmsIsTag(hProfile, icSigProfileDescriptionTag))
+ {
+ char Desc[1024];
+
+ cmsReadICCText(hProfile, icSigProfileDescriptionTag, Desc);
+ strcat(Info, Desc);
+ strcat(Info, "\r\n\r\n");
+ }
+
+
+ if (cmsIsTag(hProfile, icSigCopyrightTag))
+ {
+ char Copyright[LCMS_DESC_MAX];
+
+ cmsReadICCText(hProfile, icSigCopyrightTag, Copyright);
+ strcat(Info, Copyright);
+ strcat(Info, "\r\n\r\n");
+ }
+
+
+
+// KODAK private tag... But very useful
+
+#define K007 (icTagSignature)0x4B303037
+
+ // MonCal
+
+ if (cmsIsTag(hProfile, K007))
+ {
+ char MonCal[LCMS_DESC_MAX];
+
+ cmsReadICCText(hProfile, K007, MonCal);
+ strcat(Info, MonCal);
+ strcat(Info, "\r\n\r\n");
+ }
+ else
+ {
+ cmsCIEXYZ WhitePt;
+ char WhiteStr[1024];
+
+ cmsTakeMediaWhitePoint(&WhitePt, hProfile);
+ _cmsIdentifyWhitePoint(WhiteStr, &WhitePt);
+ strcat(WhiteStr, "\r\n\r\n");
+ strcat(Info, WhiteStr);
+ }
+
+
+ if (Icc -> stream) {
+ strcat(Info, Icc -> PhysicalFile);
+ }
+ return Info;
+}
+
+// Extract the target data as a big string. Does not signal if tag is not present.
+
+LCMSBOOL LCMSEXPORT cmsTakeCharTargetData(cmsHPROFILE hProfile, char** Data, size_t* len)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+ int n;
+
+ *Data = NULL;
+ *len = 0;
+
+ n = _cmsSearchTag(Icc, icSigCharTargetTag, FALSE);
+ if (n < 0) return FALSE;
+
+
+ *len = Icc -> TagSizes[n];
+
+ // Make sure that is reasonable (600K)
+ if (*len > 600*1024) *len = 600*1024;
+
+ *Data = (char*) _cmsMalloc(*len + 1); // Plus zero marker
+
+ if (!*Data) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "Out of memory allocating CharTarget space!");
+ return FALSE;
+ }
+
+ if (cmsReadICCTextEx(hProfile, icSigCharTargetTag, *Data, *len) < 0)
+ return FALSE;
+
+ (*Data)[*len] = 0; // Force a zero marker. Shouldn't be needed, but is
+ // here to simplify things.
+
+ return TRUE;
+}
+
+
+
+
+LCMSBOOL LCMSEXPORT cmsTakeCalibrationDateTime(struct tm *Dest, cmsHPROFILE hProfile)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+ int n;
+
+ n = _cmsSearchTag(Icc, icSigCalibrationDateTimeTag, FALSE);
+ if (n < 0) return FALSE;
+
+ if (Icc ->TagPtrs[n]) {
+
+ CopyMemory(Dest, Icc ->TagPtrs[n], sizeof(struct tm));
+ }
+ else
+ {
+ icDateTimeNumber timestamp;
+
+ if (Icc -> Seek(Icc, Icc -> TagOffsets[n] + sizeof(icTagBase)))
+ return FALSE;
+
+ if (Icc ->Read(&timestamp, 1, sizeof(icDateTimeNumber), Icc) != sizeof(icDateTimeNumber))
+ return FALSE;
+
+ DecodeDateTimeNumber(&timestamp, Dest);
+ }
+
+
+ return TRUE;
+}
+
+
+
+// PSEQ Tag, used in devicelink profiles
+
+LPcmsSEQ LCMSEXPORT cmsReadProfileSequenceDescription(cmsHPROFILE hProfile)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+ int n;
+ icUInt32Number i, Count;
+ icDescStruct DescStruct;
+ icTagTypeSignature BaseType;
+ size_t size, offset;
+ LPcmsSEQ OutSeq;
+
+
+ n = _cmsSearchTag(Icc, icSigProfileSequenceDescTag, FALSE);
+ if (n < 0) return NULL;
+
+ size = Icc -> TagSizes[n];
+ if (size < 12) return NULL;
+
+ if (Icc -> TagPtrs[n]) {
+
+ OutSeq = (LPcmsSEQ) _cmsMalloc(size);
+ if (OutSeq == NULL) return NULL;
+ CopyMemory(OutSeq, Icc ->TagPtrs[n], size);
+ return OutSeq;
+ }
+
+ offset = Icc -> TagOffsets[n];
+
+ if (Icc -> Seek(Icc, offset))
+ return NULL;
+
+ BaseType = ReadBase(Icc);
+
+ if (BaseType != icSigProfileSequenceDescType) return NULL;
+
+ Icc ->Read(&Count, sizeof(icUInt32Number), 1, Icc);
+ AdjustEndianess32((LPBYTE) &Count);
+
+ if (Count > 1000) {
+ return NULL;
+ }
+
+ size = sizeof(int) + Count * sizeof(cmsPSEQDESC);
+ OutSeq = (LPcmsSEQ) _cmsMalloc(size);
+ if (OutSeq == NULL) return NULL;
+
+ OutSeq ->n = Count;
+
+ // Get structures as well
+
+ for (i=0; i < Count; i++) {
+
+ LPcmsPSEQDESC sec = &OutSeq -> seq[i];
+
+ Icc -> Read(&DescStruct, sizeof(icDescStruct) - SIZEOF_UINT8_ALIGNED, 1, Icc);
+
+ AdjustEndianess32((LPBYTE) &DescStruct.deviceMfg);
+ AdjustEndianess32((LPBYTE) &DescStruct.deviceModel);
+ AdjustEndianess32((LPBYTE) &DescStruct.technology);
+ AdjustEndianess32((LPBYTE) &DescStruct.attributes[0]);
+ AdjustEndianess32((LPBYTE) &DescStruct.attributes[1]);
+
+ sec ->attributes[0] = DescStruct.attributes[0];
+ sec ->attributes[1] = DescStruct.attributes[1];
+ sec ->deviceMfg = DescStruct.deviceMfg;
+ sec ->deviceModel = DescStruct.deviceModel;
+ sec ->technology = DescStruct.technology;
+
+ if (ReadEmbeddedTextTag(Icc, size, sec ->Manufacturer, LCMS_DESC_MAX) < 0) return NULL;
+ if (ReadEmbeddedTextTag(Icc, size, sec ->Model, LCMS_DESC_MAX) < 0) return NULL;
+
+ }
+
+ return OutSeq;
+}
+
+
+void LCMSEXPORT cmsFreeProfileSequenceDescription(LPcmsSEQ pseq)
+{
+ if (pseq)
+ _cmsFree(pseq);
+}
+
+
+
+
+
+// Read a few tags that are hardly required
+
+
+static
+void ReadCriticalTags(LPLCMSICCPROFILE Icc)
+{
+ cmsHPROFILE hProfile = (cmsHPROFILE) Icc;
+
+ if (Icc ->Version >= 0x4000000) {
+
+ // v4 profiles
+
+ MAT3 ChrmCanonical;
+
+ if (ReadICCXYZ(hProfile,
+ icSigMediaWhitePointTag,
+ &Icc ->MediaWhitePoint, FALSE) < 0) {
+
+ Icc ->MediaWhitePoint = *cmsD50_XYZ();
+ }
+
+ // Read media black
+
+ if (ReadICCXYZ(hProfile,
+ icSigMediaBlackPointTag,
+ &Icc ->MediaBlackPoint, FALSE) < 0) {
+
+ Icc ->MediaBlackPoint.X = 0;
+ Icc ->MediaBlackPoint.Y = 0;
+ Icc ->MediaBlackPoint.X = 0;
+
+ }
+
+ NormalizeXYZ(&Icc ->MediaWhitePoint);
+ NormalizeXYZ(&Icc ->MediaBlackPoint);
+
+ if (ReadICCXYZArray(hProfile,
+ icSigChromaticAdaptationTag,
+ &ChrmCanonical) > 0) {
+
+ MAT3inverse(&ChrmCanonical, &Icc ->ChromaticAdaptation);
+
+ }
+ else {
+
+ MAT3identity(&Icc ->ChromaticAdaptation);
+ }
+
+
+ // Convert media white, black to absolute under original illuminant
+
+ EvalCHRM(&Icc ->MediaWhitePoint, &Icc ->ChromaticAdaptation, &Icc ->MediaWhitePoint);
+ EvalCHRM(&Icc ->MediaBlackPoint, &Icc ->ChromaticAdaptation, &Icc ->MediaBlackPoint);
+
+
+ }
+ else {
+
+ // v2 profiles
+
+ // Read media white
+
+ if (ReadICCXYZ(hProfile,
+ icSigMediaWhitePointTag,
+ &Icc ->MediaWhitePoint, FALSE) < 0) {
+
+ Icc ->MediaWhitePoint = *cmsD50_XYZ();
+ }
+
+ // Read media black
+
+ if (ReadICCXYZ(hProfile,
+ icSigMediaBlackPointTag,
+ &Icc ->MediaBlackPoint, FALSE) < 0) {
+
+ Icc ->MediaBlackPoint.X = 0;
+ Icc ->MediaBlackPoint.Y = 0;
+ Icc ->MediaBlackPoint.X = 0;
+
+ }
+
+ NormalizeXYZ(&Icc ->MediaWhitePoint);
+ NormalizeXYZ(&Icc ->MediaBlackPoint);
+
+
+ // Take Bradford as default for Display profiles only.
+
+ if (cmsGetDeviceClass(hProfile) == icSigDisplayClass) {
+
+
+ cmsAdaptationMatrix(&Icc -> ChromaticAdaptation,
+ NULL,
+ &Icc -> Illuminant,
+ &Icc -> MediaWhitePoint);
+ }
+ else
+ MAT3identity(&Icc ->ChromaticAdaptation);
+
+ }
+
+}
+
+
+// Create profile from disk file
+
+cmsHPROFILE LCMSEXPORT cmsOpenProfileFromFile(const char *lpFileName, const char *sAccess)
+{
+ LPLCMSICCPROFILE NewIcc;
+ cmsHPROFILE hEmpty;
+
+
+ // Open for write means an empty profile
+
+ if (*sAccess == 'W' || *sAccess == 'w') {
+
+ hEmpty = _cmsCreateProfilePlaceholder();
+ NewIcc = (LPLCMSICCPROFILE) (LPSTR) hEmpty;
+ NewIcc -> IsWrite = TRUE;
+ strncpy(NewIcc ->PhysicalFile, lpFileName, MAX_PATH-1);
+ NewIcc ->PhysicalFile[MAX_PATH-1] = 0;
+
+ // Save LUT as 8 bit
+
+ sAccess++;
+ if (*sAccess == '8') NewIcc ->SaveAs8Bits = TRUE;
+
+ return hEmpty;
+ }
+
+
+ // Open for read means a file placeholder
+
+ NewIcc = _cmsCreateProfileFromFilePlaceholder(lpFileName);
+ if (!NewIcc) return NULL;
+
+ if (!ReadHeader(NewIcc, FALSE)) return NULL;
+
+ ReadCriticalTags(NewIcc);
+
+ return (cmsHPROFILE) (LPSTR) NewIcc;
+}
+
+
+
+
+// Open from memory block
+
+cmsHPROFILE LCMSEXPORT cmsOpenProfileFromMem(LPVOID MemPtr, DWORD dwSize)
+{
+ LPLCMSICCPROFILE NewIcc;
+
+
+ NewIcc = _cmsCreateProfileFromMemPlaceholder(MemPtr, dwSize);
+ if (!NewIcc) return NULL;
+
+ if (!ReadHeader(NewIcc, TRUE)) return NULL;
+
+ ReadCriticalTags(NewIcc);
+
+ return (cmsHPROFILE) (LPSTR) NewIcc;
+
+}
+
+
+
+LCMSBOOL LCMSEXPORT cmsCloseProfile(cmsHPROFILE hProfile)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+ LCMSBOOL rc = TRUE;
+ icInt32Number i;
+
+ if (!Icc) return FALSE;
+
+ // Was open in write mode?
+ if (Icc ->IsWrite) {
+
+ Icc ->IsWrite = FALSE; // Assure no further writting
+ rc = _cmsSaveProfile(hProfile, Icc ->PhysicalFile);
+ }
+
+ for (i=0; i < Icc -> TagCount; i++) {
+
+ if (Icc -> TagPtrs[i])
+ free(Icc -> TagPtrs[i]);
+ }
+
+ if (Icc -> stream != NULL) { // Was a memory (i.e. not serialized) profile?
+ Icc -> Close(Icc); // No, close the stream
+ }
+
+ free(Icc); // Free placeholder memory
+
+ return rc;
+}
+
+
+
+// Write profile ------------------------------------------------------------
+
+
+
+static
+LCMSBOOL SaveWordsTable(int nEntries, LPWORD Tab, LPLCMSICCPROFILE Icc)
+{
+ size_t nTabSize = sizeof(WORD) * nEntries;
+ LPWORD PtrW = (LPWORD) _cmsMalloc(nTabSize);
+ LCMSBOOL rc;
+
+ if (!PtrW) return FALSE;
+ CopyMemory(PtrW, Tab, nTabSize);
+ AdjustEndianessArray16(PtrW, nEntries);
+ rc = Icc ->Write(Icc, nTabSize, PtrW);
+ free(PtrW);
+
+ return rc;
+}
+
+
+
+// Saves profile header
+
+static
+LCMSBOOL SaveHeader(LPLCMSICCPROFILE Icc)
+{
+ icHeader Header;
+ time_t now = time(NULL);
+
+ Header.size = TransportValue32((icInt32Number) Icc ->UsedSpace);
+ Header.cmmId = TransportValue32(lcmsSignature);
+ Header.version = TransportValue32((icInt32Number) 0x02300000);
+ Header.deviceClass = (icProfileClassSignature) TransportValue32(Icc -> DeviceClass);
+ Header.colorSpace = (icColorSpaceSignature) TransportValue32(Icc -> ColorSpace);
+ Header.pcs = (icColorSpaceSignature) TransportValue32(Icc -> PCS);
+
+ // NOTE: in v4 Timestamp must be in UTC rather than in local time
+ EncodeDateTimeNumber(&Header.date, gmtime(&now));
+
+ Header.magic = TransportValue32(icMagicNumber);
+
+#ifdef NON_WINDOWS
+ Header.platform = (icPlatformSignature)TransportValue32(icSigMacintosh);
+#else
+ Header.platform = (icPlatformSignature)TransportValue32(icSigMicrosoft);
+#endif
+
+ Header.flags = TransportValue32(Icc -> flags);
+ Header.manufacturer = TransportValue32(lcmsSignature);
+ Header.model = TransportValue32(0);
+ Header.attributes[0]= TransportValue32(Icc -> attributes);
+ Header.attributes[1]= TransportValue32(0);
+
+ Header.renderingIntent = TransportValue32(Icc -> RenderingIntent);
+
+ // Illuminant is D50
+
+ Header.illuminant.X = TransportValue32(DOUBLE_TO_FIXED(Icc -> Illuminant.X));
+ Header.illuminant.Y = TransportValue32(DOUBLE_TO_FIXED(Icc -> Illuminant.Y));
+ Header.illuminant.Z = TransportValue32(DOUBLE_TO_FIXED(Icc -> Illuminant.Z));
+
+ Header.creator = TransportValue32(lcmsSignature);
+
+ ZeroMemory(&Header.reserved, sizeof(Header.reserved));
+
+ // Set profile ID
+ CopyMemory(Header.reserved, Icc ->ProfileID, 16);
+
+
+ Icc ->UsedSpace = 0; // Mark as begin-of-file
+
+ return Icc ->Write(Icc, sizeof(icHeader), &Header);
+}
+
+
+
+// Setup base marker
+
+static
+LCMSBOOL SetupBase(icTagTypeSignature sig, LPLCMSICCPROFILE Icc)
+{
+ icTagBase Base;
+
+ Base.sig = (icTagTypeSignature) TransportValue32(sig);
+ ZeroMemory(&Base.reserved, sizeof(Base.reserved));
+ return Icc -> Write(Icc, sizeof(icTagBase), &Base);
+}
+
+
+// Store a XYZ tag
+
+static
+LCMSBOOL SaveXYZNumber(LPcmsCIEXYZ Value, LPLCMSICCPROFILE Icc)
+{
+
+ icXYZNumber XYZ;
+
+ if (!SetupBase(icSigXYZType, Icc)) return FALSE;
+
+ XYZ.X = TransportValue32(DOUBLE_TO_FIXED(Value -> X));
+ XYZ.Y = TransportValue32(DOUBLE_TO_FIXED(Value -> Y));
+ XYZ.Z = TransportValue32(DOUBLE_TO_FIXED(Value -> Z));
+
+
+ return Icc -> Write(Icc, sizeof(icXYZNumber), &XYZ);
+}
+
+
+// Store a XYZ array.
+
+static
+LCMSBOOL SaveXYZArray(int n, LPcmsCIEXYZ Value, LPLCMSICCPROFILE Icc)
+{
+ int i;
+ icXYZNumber XYZ;
+
+ if (!SetupBase(icSigS15Fixed16ArrayType, Icc)) return FALSE;
+
+ for (i=0; i < n; i++) {
+
+ XYZ.X = TransportValue32(DOUBLE_TO_FIXED(Value -> X));
+ XYZ.Y = TransportValue32(DOUBLE_TO_FIXED(Value -> Y));
+ XYZ.Z = TransportValue32(DOUBLE_TO_FIXED(Value -> Z));
+
+ if (!Icc -> Write(Icc, sizeof(icXYZNumber), &XYZ)) return FALSE;
+
+ Value++;
+ }
+
+ return TRUE;
+}
+
+
+
+// Save a gamma structure as a table
+
+static
+LCMSBOOL SaveGammaTable(LPGAMMATABLE Gamma, LPLCMSICCPROFILE Icc)
+{
+ icInt32Number Count;
+
+ if (!SetupBase(icSigCurveType, Icc)) return FALSE;
+
+ Count = TransportValue32(Gamma->nEntries);
+
+ if (!Icc ->Write(Icc, sizeof(icInt32Number), &Count)) return FALSE;
+
+ return SaveWordsTable(Gamma->nEntries, Gamma ->GammaTable, Icc);
+}
+
+
+// Save a gamma structure as a one-value
+
+static
+LCMSBOOL SaveGammaOneValue(LPGAMMATABLE Gamma, LPLCMSICCPROFILE Icc)
+{
+ icInt32Number Count;
+ Fixed32 GammaFixed32;
+ WORD GammaFixed8;
+
+ if (!SetupBase(icSigCurveType, Icc)) return FALSE;
+
+ Count = TransportValue32(1);
+ if (!Icc ->Write(Icc, sizeof(icInt32Number), &Count)) return FALSE;
+
+ GammaFixed32 = DOUBLE_TO_FIXED(Gamma ->Seed.Params[0]);
+ GammaFixed8 = (WORD) ((GammaFixed32 >> 8) & 0xFFFF);
+ GammaFixed8 = TransportValue16(GammaFixed8);
+
+ return Icc ->Write(Icc, sizeof(icInt16Number), &GammaFixed8);
+}
+
+// Save a gamma structure as a parametric gamma
+
+static
+LCMSBOOL SaveGammaParametric(LPGAMMATABLE Gamma, LPLCMSICCPROFILE Icc)
+{
+ icUInt16Number Type, Reserved;
+ int i, nParams;
+ int ParamsByType[] = { 1, 3, 4, 5, 7 };
+
+ if (!SetupBase(icSigParametricCurveType, Icc)) return FALSE;
+
+ nParams = ParamsByType[Gamma -> Seed.Type];
+
+ Type = (icUInt16Number) TransportValue16((WORD) Gamma -> Seed. Type);
+ Reserved = (icUInt16Number) TransportValue16((WORD) 0);
+
+ Icc -> Write(Icc, sizeof(icInt16Number), &Type);
+ Icc -> Write(Icc, sizeof(icUInt16Number), &Reserved);
+
+ for (i=0; i < nParams; i++) {
+
+ icInt32Number val = TransportValue32(DOUBLE_TO_FIXED(Gamma -> Seed.Params[i]));
+ Icc ->Write(Icc, sizeof(icInt32Number), &val);
+ }
+
+
+ return TRUE;
+
+}
+
+
+// Save a gamma table
+
+static
+LCMSBOOL SaveGamma(LPGAMMATABLE Gamma, LPLCMSICCPROFILE Icc)
+{
+ // Is the gamma curve type supported by ICC format?
+
+ if (Gamma -> Seed.Type < 0 || Gamma -> Seed.Type > 5 ||
+
+ // has been modified by user?
+
+ _cmsCrc32OfGammaTable(Gamma) != Gamma -> Seed.Crc32) {
+
+ return SaveGammaTable(Gamma, Icc);
+ }
+
+ if (Gamma -> Seed.Type == 1) return SaveGammaOneValue(Gamma, Icc);
+
+ // Only v4 profiles are allowed to hold parametric curves
+
+ if (cmsGetProfileICCversion((cmsHPROFILE) Icc) >= 0x4000000)
+ return SaveGammaParametric(Gamma, Icc);
+
+ // Defaults to save as table
+
+ return SaveGammaTable(Gamma, Icc);
+
+}
+
+
+
+
+// Save an DESC Tag
+
+static
+LCMSBOOL SaveDescription(const char *Text, LPLCMSICCPROFILE Icc)
+{
+
+ icUInt32Number len, Count, TotalSize, AlignedSize;
+ char Filler[256];
+
+ len = (icUInt32Number) (strlen(Text) + 1);
+
+ // * icInt8Number desc[count] * NULL terminated ascii string
+ // * icUInt32Number ucLangCode; * UniCode language code
+ // * icUInt32Number ucCount; * UniCode description length
+ // * icInt16Number ucDesc[ucCount];* The UniCode description
+ // * icUInt16Number scCode; * ScriptCode code
+ // * icUInt8Number scCount; * ScriptCode count
+ // * icInt8Number scDesc[67]; * ScriptCode Description
+
+ TotalSize = sizeof(icTagBase) + sizeof(icUInt32Number) + len +
+ sizeof(icUInt32Number) + sizeof(icUInt32Number) +
+ sizeof(icUInt16Number) + sizeof(icUInt8Number) + 67;
+
+ AlignedSize = TotalSize; // Can be unaligned!!
+
+ if (!SetupBase(icSigTextDescriptionType, Icc)) return FALSE;
+ AlignedSize -= sizeof(icTagBase);
+
+ Count = TransportValue32(len);
+ if (!Icc ->Write(Icc, sizeof(icUInt32Number), &Count)) return FALSE;
+ AlignedSize -= sizeof(icUInt32Number);
+
+ if (!Icc ->Write(Icc, len, (LPVOID)Text)) return FALSE;
+ AlignedSize -= len;
+
+ if (AlignedSize < 0)
+ AlignedSize = 0;
+ if (AlignedSize > 255)
+ AlignedSize = 255;
+
+ ZeroMemory(Filler, AlignedSize);
+ if (!Icc ->Write(Icc, AlignedSize, Filler)) return FALSE;
+
+ return TRUE;
+}
+
+// Save an ASCII Tag
+
+static
+LCMSBOOL SaveText(const char *Text, LPLCMSICCPROFILE Icc)
+{
+ size_t len = strlen(Text) + 1;
+
+ if (!SetupBase(icSigTextType, Icc)) return FALSE;
+ if (!Icc ->Write(Icc, len, (LPVOID) Text)) return FALSE;
+ return TRUE;
+}
+
+
+// Save one of these new chromaticity values
+
+static
+LCMSBOOL SaveOneChromaticity(double x, double y, LPLCMSICCPROFILE Icc)
+{
+ Fixed32 xf, yf;
+
+ xf = TransportValue32(DOUBLE_TO_FIXED(x));
+ yf = TransportValue32(DOUBLE_TO_FIXED(y));
+
+ if (!Icc ->Write(Icc, sizeof(Fixed32), &xf)) return FALSE;
+ if (!Icc ->Write(Icc, sizeof(Fixed32), &yf)) return FALSE;
+
+ return TRUE;
+}
+
+
+// New tag added in Addendum II of old spec.
+
+static
+LCMSBOOL SaveChromaticities(LPcmsCIExyYTRIPLE chrm, LPLCMSICCPROFILE Icc)
+{
+ WORD nChans, Table;
+
+ if (!SetupBase(icSigChromaticityType, Icc)) return FALSE;
+
+ nChans = TransportValue16(3);
+ if (!Icc ->Write(Icc, sizeof(WORD) , &nChans)) return FALSE;
+ Table = TransportValue16(0);
+ if (!Icc ->Write(Icc, sizeof(WORD) , &Table)) return FALSE;
+
+ if (!SaveOneChromaticity(chrm -> Red.x, chrm -> Red.y, Icc)) return FALSE;
+ if (!SaveOneChromaticity(chrm -> Green.x, chrm -> Green.y, Icc)) return FALSE;
+ if (!SaveOneChromaticity(chrm -> Blue.x, chrm -> Blue.y, Icc)) return FALSE;
+
+ return TRUE;
+}
+
+
+static
+LCMSBOOL SaveSequenceDescriptionTag(LPcmsSEQ seq, LPLCMSICCPROFILE Icc)
+{
+ icUInt32Number nSeqs;
+ icDescStruct DescStruct;
+ int i, n = seq ->n;
+ LPcmsPSEQDESC pseq = seq ->seq;
+
+ if (!SetupBase(icSigProfileSequenceDescType, Icc)) return FALSE;
+
+ nSeqs = TransportValue32(n);
+
+ if (!Icc ->Write(Icc, sizeof(icUInt32Number) , &nSeqs)) return FALSE;
+
+ for (i=0; i < n; i++) {
+
+ LPcmsPSEQDESC sec = pseq + i;
+
+
+ DescStruct.deviceMfg = (icTagTypeSignature) TransportValue32(sec ->deviceMfg);
+ DescStruct.deviceModel = (icTagTypeSignature) TransportValue32(sec ->deviceModel);
+ DescStruct.technology = (icTechnologySignature) TransportValue32(sec ->technology);
+ DescStruct.attributes[0]= TransportValue32(sec ->attributes[0]);
+ DescStruct.attributes[1]= TransportValue32(sec ->attributes[1]);
+
+ if (!Icc ->Write(Icc, sizeof(icDescStruct) - SIZEOF_UINT8_ALIGNED, &DescStruct)) return FALSE;
+
+ if (!SaveDescription(sec ->Manufacturer, Icc)) return FALSE;
+ if (!SaveDescription(sec ->Model, Icc)) return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+// Saves a timestamp tag
+
+static
+LCMSBOOL SaveDateTimeNumber(const struct tm *DateTime, LPLCMSICCPROFILE Icc)
+{
+ icDateTimeNumber Dest;
+
+ if (!SetupBase(icSigDateTimeType, Icc)) return FALSE;
+ EncodeDateTimeNumber(&Dest, DateTime);
+ if (!Icc ->Write(Icc, sizeof(icDateTimeNumber), &Dest)) return FALSE;
+
+ return TRUE;
+}
+
+
+// Saves a named color list into a named color profile
+static
+LCMSBOOL SaveNamedColorList(LPcmsNAMEDCOLORLIST NamedColorList, LPLCMSICCPROFILE Icc)
+{
+
+ icUInt32Number vendorFlag; // Bottom 16 bits for IC use
+ icUInt32Number count; // Count of named colors
+ icUInt32Number nDeviceCoords; // Num of device coordinates
+ char prefix[32]; // Prefix for each color name
+ char suffix[32]; // Suffix for each color name
+ int i;
+
+ if (!SetupBase(icSigNamedColor2Type, Icc)) return FALSE;
+
+ vendorFlag = TransportValue32(0);
+ count = TransportValue32(NamedColorList ->nColors);
+ nDeviceCoords = TransportValue32(NamedColorList ->ColorantCount);
+
+ strncpy(prefix, (const char*) NamedColorList->Prefix, 31);
+ strncpy(suffix, (const char*) NamedColorList->Suffix, 31);
+
+ suffix[31] = prefix[31] = 0;
+
+ if (!Icc ->Write(Icc, sizeof(icUInt32Number), &vendorFlag)) return FALSE;
+ if (!Icc ->Write(Icc, sizeof(icUInt32Number), &count)) return FALSE;
+ if (!Icc ->Write(Icc, sizeof(icUInt32Number), &nDeviceCoords)) return FALSE;
+ if (!Icc ->Write(Icc, 32 , prefix)) return FALSE;
+ if (!Icc ->Write(Icc, 32 , suffix)) return FALSE;
+
+ for (i=0; i < NamedColorList ->nColors; i++) {
+
+ icUInt16Number PCS[3];
+ icUInt16Number Colorant[MAXCHANNELS];
+ char root[32];
+ LPcmsNAMEDCOLOR Color;
+ int j;
+
+ Color = NamedColorList ->List + i;
+
+ strncpy(root, Color ->Name, 32);
+ Color ->Name[32] = 0;
+
+ if (!Icc ->Write(Icc, 32 , root)) return FALSE;
+
+ for (j=0; j < 3; j++)
+ PCS[j] = TransportValue16(Color ->PCS[j]);
+
+ if (!Icc ->Write(Icc, 3 * sizeof(icUInt16Number), PCS)) return FALSE;
+
+ for (j=0; j < NamedColorList ->ColorantCount; j++)
+ Colorant[j] = TransportValue16(Color ->DeviceColorant[j]);
+
+ if (!Icc ->Write(Icc,
+ NamedColorList ->ColorantCount * sizeof(icUInt16Number), Colorant)) return FALSE;
+ }
+
+
+ return TRUE;
+}
+
+
+
+// Saves a colorant table. It is using the named color structure for simplicity sake
+
+static
+LCMSBOOL SaveColorantTable(LPcmsNAMEDCOLORLIST NamedColorList, LPLCMSICCPROFILE Icc)
+{
+ icUInt32Number count; // Count of named colors
+ int i;
+
+ if (!SetupBase(icSigColorantTableType, Icc)) return FALSE;
+
+ count = TransportValue32(NamedColorList ->nColors);
+
+ if (!Icc ->Write(Icc, sizeof(icUInt32Number), &count)) return FALSE;
+
+ for (i=0; i < NamedColorList ->nColors; i++) {
+
+ icUInt16Number PCS[3];
+ icInt8Number root[33];
+ LPcmsNAMEDCOLOR Color;
+ int j;
+
+ Color = NamedColorList ->List + i;
+
+ strncpy((char*) root, Color ->Name, 32);
+ root[32] = 0;
+
+ if (!Icc ->Write(Icc, 32 , root)) return FALSE;
+
+ for (j=0; j < 3; j++)
+ PCS[j] = TransportValue16(Color ->PCS[j]);
+
+ if (!Icc ->Write(Icc, 3 * sizeof(icUInt16Number), PCS)) return FALSE;
+
+ }
+
+
+ return TRUE;
+}
+
+// Does serialization of LUT16 and writes it.
+
+static
+LCMSBOOL SaveLUT(const LUT* NewLUT, LPLCMSICCPROFILE Icc)
+{
+ icLut16 LUT16;
+ unsigned int i;
+ size_t nTabSize;
+ WORD NullTbl[2] = { 0, 0xFFFFU};
+
+
+ if (!SetupBase(icSigLut16Type, Icc)) return FALSE;
+
+ LUT16.clutPoints = (icUInt8Number) NewLUT -> cLutPoints;
+ LUT16.inputChan = (icUInt8Number) NewLUT -> InputChan;
+ LUT16.outputChan = (icUInt8Number) NewLUT -> OutputChan;
+ LUT16.pad = 0;
+
+ LUT16.inputEnt = TransportValue16((WORD) ((NewLUT -> wFlags & LUT_HASTL1) ? NewLUT -> InputEntries : 2));
+ LUT16.outputEnt = TransportValue16((WORD) ((NewLUT -> wFlags & LUT_HASTL2) ? NewLUT -> OutputEntries : 2));
+
+ if (NewLUT -> wFlags & LUT_HASMATRIX) {
+
+ LUT16.e00 = TransportValue32(NewLUT -> Matrix.v[0].n[0]);
+ LUT16.e01 = TransportValue32(NewLUT -> Matrix.v[0].n[1]);
+ LUT16.e02 = TransportValue32(NewLUT -> Matrix.v[0].n[2]);
+ LUT16.e10 = TransportValue32(NewLUT -> Matrix.v[1].n[0]);
+ LUT16.e11 = TransportValue32(NewLUT -> Matrix.v[1].n[1]);
+ LUT16.e12 = TransportValue32(NewLUT -> Matrix.v[1].n[2]);
+ LUT16.e20 = TransportValue32(NewLUT -> Matrix.v[2].n[0]);
+ LUT16.e21 = TransportValue32(NewLUT -> Matrix.v[2].n[1]);
+ LUT16.e22 = TransportValue32(NewLUT -> Matrix.v[2].n[2]);
+ }
+ else {
+
+ LUT16.e00 = TransportValue32(DOUBLE_TO_FIXED(1));
+ LUT16.e01 = TransportValue32(DOUBLE_TO_FIXED(0));
+ LUT16.e02 = TransportValue32(DOUBLE_TO_FIXED(0));
+ LUT16.e10 = TransportValue32(DOUBLE_TO_FIXED(0));
+ LUT16.e11 = TransportValue32(DOUBLE_TO_FIXED(1));
+ LUT16.e12 = TransportValue32(DOUBLE_TO_FIXED(0));
+ LUT16.e20 = TransportValue32(DOUBLE_TO_FIXED(0));
+ LUT16.e21 = TransportValue32(DOUBLE_TO_FIXED(0));
+ LUT16.e22 = TransportValue32(DOUBLE_TO_FIXED(1));
+ }
+
+
+ // Save header
+
+ Icc -> Write(Icc, sizeof(icLut16)- SIZEOF_UINT16_ALIGNED, &LUT16);
+
+ // The prelinearization table
+
+ for (i=0; i < NewLUT -> InputChan; i++) {
+
+ if (NewLUT -> wFlags & LUT_HASTL1) {
+
+ if (!SaveWordsTable(NewLUT -> InputEntries,
+ NewLUT -> L1[i], Icc)) return FALSE;
+
+ }
+ else Icc -> Write(Icc, sizeof(WORD)* 2, NullTbl);
+ }
+
+
+ nTabSize = (NewLUT -> OutputChan * uipow(NewLUT->cLutPoints,
+ NewLUT->InputChan));
+ // The 3D CLUT.
+
+ if (nTabSize > 0) {
+
+ if (!SaveWordsTable((int) nTabSize, NewLUT -> T, Icc)) return FALSE;
+ }
+ // The postlinearization table
+
+ for (i=0; i < NewLUT -> OutputChan; i++) {
+
+ if (NewLUT -> wFlags & LUT_HASTL2) {
+
+ if (!SaveWordsTable(NewLUT -> OutputEntries,
+ NewLUT -> L2[i], Icc)) return FALSE;
+ }
+ else Icc -> Write(Icc, sizeof(WORD)* 2, NullTbl);
+
+ }
+
+ return TRUE;
+}
+
+
+
+// Does serialization of LUT8 and writes it
+
+static
+LCMSBOOL SaveLUT8(const LUT* NewLUT, LPLCMSICCPROFILE Icc)
+{
+ icLut8 LUT8;
+ unsigned int i, j;
+ size_t nTabSize;
+ BYTE val;
+
+ // Sanity check
+
+ if (NewLUT -> wFlags & LUT_HASTL1) {
+
+ if (NewLUT -> InputEntries != 256) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "LUT8 needs 256 entries on prelinearization");
+ return FALSE;
+ }
+
+ }
+
+
+ if (NewLUT -> wFlags & LUT_HASTL2) {
+
+ if (NewLUT -> OutputEntries != 256) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "LUT8 needs 256 entries on postlinearization");
+ return FALSE;
+ }
+ }
+
+
+
+ if (!SetupBase(icSigLut8Type, Icc)) return FALSE;
+
+ LUT8.clutPoints = (icUInt8Number) NewLUT -> cLutPoints;
+ LUT8.inputChan = (icUInt8Number) NewLUT -> InputChan;
+ LUT8.outputChan = (icUInt8Number) NewLUT -> OutputChan;
+
+
+ if (NewLUT -> wFlags & LUT_HASMATRIX) {
+
+ LUT8.e00 = TransportValue32(NewLUT -> Matrix.v[0].n[0]);
+ LUT8.e01 = TransportValue32(NewLUT -> Matrix.v[0].n[1]);
+ LUT8.e02 = TransportValue32(NewLUT -> Matrix.v[0].n[2]);
+ LUT8.e10 = TransportValue32(NewLUT -> Matrix.v[1].n[0]);
+ LUT8.e11 = TransportValue32(NewLUT -> Matrix.v[1].n[1]);
+ LUT8.e12 = TransportValue32(NewLUT -> Matrix.v[1].n[2]);
+ LUT8.e20 = TransportValue32(NewLUT -> Matrix.v[2].n[0]);
+ LUT8.e21 = TransportValue32(NewLUT -> Matrix.v[2].n[1]);
+ LUT8.e22 = TransportValue32(NewLUT -> Matrix.v[2].n[2]);
+ }
+ else {
+
+ LUT8.e00 = TransportValue32(DOUBLE_TO_FIXED(1));
+ LUT8.e01 = TransportValue32(DOUBLE_TO_FIXED(0));
+ LUT8.e02 = TransportValue32(DOUBLE_TO_FIXED(0));
+ LUT8.e10 = TransportValue32(DOUBLE_TO_FIXED(0));
+ LUT8.e11 = TransportValue32(DOUBLE_TO_FIXED(1));
+ LUT8.e12 = TransportValue32(DOUBLE_TO_FIXED(0));
+ LUT8.e20 = TransportValue32(DOUBLE_TO_FIXED(0));
+ LUT8.e21 = TransportValue32(DOUBLE_TO_FIXED(0));
+ LUT8.e22 = TransportValue32(DOUBLE_TO_FIXED(1));
+ }
+
+
+ // Save header
+
+ Icc -> Write(Icc, sizeof(icLut8)- SIZEOF_UINT8_ALIGNED, &LUT8);
+
+ // The prelinearization table
+
+ for (i=0; i < NewLUT -> InputChan; i++) {
+
+ for (j=0; j < 256; j++) {
+
+ if (NewLUT -> wFlags & LUT_HASTL1)
+ val = (BYTE) floor(NewLUT ->L1[i][j] / 257.0 + .5);
+ else
+ val = (BYTE) j;
+
+ Icc ->Write(Icc, 1, &val);
+ }
+
+ }
+
+
+ nTabSize = (NewLUT -> OutputChan * uipow(NewLUT->cLutPoints,
+ NewLUT->InputChan));
+ // The 3D CLUT.
+
+ for (j=0; j < nTabSize; j++) {
+
+ val = (BYTE) floor(NewLUT ->T[j] / 257.0 + .5);
+ Icc ->Write(Icc, 1, &val);
+ }
+
+ // The postlinearization table
+
+ for (i=0; i < NewLUT -> OutputChan; i++) {
+
+ for (j=0; j < 256; j++) {
+
+ if (NewLUT -> wFlags & LUT_HASTL2)
+ val = (BYTE) floor(NewLUT ->L2[i][j] / 257.0 + .5);
+ else
+ val = (BYTE) j;
+
+ Icc ->Write(Icc, 1, &val);
+ }
+
+ }
+
+ return TRUE;
+}
+
+
+
+// Set the LUT bitdepth to be saved
+
+void LCMSEXPORT _cmsSetLUTdepth(cmsHPROFILE hProfile, int depth)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+
+ switch (depth) {
+
+ case 8: Icc ->SaveAs8Bits = TRUE; break;
+ case 16: Icc ->SaveAs8Bits = FALSE; break;
+
+ default:
+ cmsSignalError(LCMS_ERRC_ABORTED, "%d is an unsupported as bitdepth, use 8 or 16 only.", depth);
+ }
+}
+
+
+// Saves Tag directory
+
+static
+LCMSBOOL SaveTagDirectory(LPLCMSICCPROFILE Icc)
+{
+ icInt32Number i;
+ icTag Tag;
+ icInt32Number Count = 0;
+
+ // Get true count
+ for (i=0; i < Icc -> TagCount; i++) {
+ if (Icc ->TagNames[i] != 0)
+ Count++;
+ }
+
+ Count = TransportValue32(Count);
+ if (!Icc ->Write(Icc, sizeof(icInt32Number) , &Count)) return FALSE;
+
+ for (i=0; i < Icc -> TagCount; i++) {
+
+ if (Icc ->TagNames[i] == 0) continue;
+
+ Tag.sig = (icTagSignature)TransportValue32(Icc -> TagNames[i]);
+ Tag.offset = TransportValue32((icInt32Number) Icc -> TagOffsets[i]);
+ Tag.size = TransportValue32((icInt32Number) Icc -> TagSizes[i]);
+
+ if (!Icc ->Write(Icc, sizeof(icTag), &Tag)) return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+// Dump tag contents
+
+static
+LCMSBOOL SaveTags(LPLCMSICCPROFILE Icc, LPLCMSICCPROFILE FileOrig)
+{
+
+ LPBYTE Data;
+ icInt32Number i;
+ size_t Begin;
+ size_t AlignedSpace, FillerSize;
+
+
+ for (i=0; i < Icc -> TagCount; i++) {
+
+ if (Icc ->TagNames[i] == 0) continue;
+
+ // Align to DWORD boundary, following new spec.
+
+ AlignedSpace = ALIGNLONG(Icc ->UsedSpace);
+ FillerSize = AlignedSpace - Icc ->UsedSpace;
+ if (FillerSize > 0) {
+
+ BYTE Filler[20];
+
+ ZeroMemory(Filler, 16);
+ if (!Icc ->Write(Icc, FillerSize, Filler)) return FALSE;
+ }
+
+
+ Icc -> TagOffsets[i] = Begin = Icc ->UsedSpace;
+ Data = (LPBYTE) Icc -> TagPtrs[i];
+ if (!Data) {
+
+ // Reach here if we are copying a tag from a disk-based ICC profile which has not been modified by user.
+ // In this case a blind copy of the block data is performed
+
+ if (Icc -> TagOffsets[i]) {
+
+ size_t TagSize = FileOrig -> TagSizes[i];
+ size_t TagOffset = FileOrig -> TagOffsets[i];
+ void* Mem;
+
+ if (FileOrig ->Seek(FileOrig, TagOffset)) return FALSE;
+
+ Mem = _cmsMalloc(TagSize);
+
+ if (FileOrig ->Read(Mem, TagSize, 1, FileOrig) != 1) return FALSE;
+ if (!Icc ->Write(Icc, TagSize, Mem)) return FALSE;
+
+ Icc -> TagSizes[i] = (Icc ->UsedSpace - Begin);
+ free(Mem);
+ }
+
+ continue;
+ }
+
+
+ switch (Icc -> TagNames[i]) {
+
+ case icSigProfileDescriptionTag:
+ case icSigDeviceMfgDescTag:
+ case icSigDeviceModelDescTag:
+ if (!SaveDescription((const char *) Data, Icc)) return FALSE;
+ break;
+
+ case icSigRedColorantTag:
+ case icSigGreenColorantTag:
+ case icSigBlueColorantTag:
+ case icSigMediaWhitePointTag:
+ case icSigMediaBlackPointTag:
+ if (!SaveXYZNumber((LPcmsCIEXYZ) Data, Icc)) return FALSE;
+ break;
+
+
+ case icSigRedTRCTag:
+ case icSigGreenTRCTag:
+ case icSigBlueTRCTag:
+ case icSigGrayTRCTag:
+ if (!SaveGamma((LPGAMMATABLE) Data, Icc)) return FALSE;
+ break;
+
+ case icSigCharTargetTag:
+ case icSigCopyrightTag:
+ if (!SaveText((const char *) Data, Icc)) return FALSE;
+ break;
+
+ case icSigChromaticityTag:
+ if (!SaveChromaticities((LPcmsCIExyYTRIPLE) Data, Icc)) return FALSE;
+ break;
+
+ // Save LUT
+
+ case icSigAToB0Tag:
+ case icSigAToB1Tag:
+ case icSigAToB2Tag:
+ case icSigBToA0Tag:
+ case icSigBToA1Tag:
+ case icSigBToA2Tag:
+ case icSigGamutTag:
+ case icSigPreview0Tag:
+ case icSigPreview1Tag:
+ case icSigPreview2Tag:
+
+ if (Icc ->SaveAs8Bits) {
+
+ if (!SaveLUT8((LPLUT) Data, Icc)) return FALSE;
+ }
+ else {
+
+ if (!SaveLUT((LPLUT) Data, Icc)) return FALSE;
+ }
+ break;
+
+ case icSigProfileSequenceDescTag:
+ if (!SaveSequenceDescriptionTag((LPcmsSEQ) Data, Icc)) return FALSE;
+ break;
+
+
+ case icSigNamedColor2Tag:
+ if (!SaveNamedColorList((LPcmsNAMEDCOLORLIST) Data, Icc)) return FALSE;
+ break;
+
+
+ case icSigCalibrationDateTimeTag:
+ if (!SaveDateTimeNumber((struct tm *) Data, Icc)) return FALSE;
+ break;
+
+
+ case icSigColorantTableTag:
+ case icSigColorantTableOutTag:
+ if (!SaveColorantTable((LPcmsNAMEDCOLORLIST) Data, Icc)) return FALSE;
+ break;
+
+
+ case icSigChromaticAdaptationTag:
+ if (!SaveXYZArray(3, (LPcmsCIEXYZ) Data, Icc)) return FALSE;
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ Icc -> TagSizes[i] = (Icc ->UsedSpace - Begin);
+ }
+
+
+
+ return TRUE;
+}
+
+
+
+// Add tags to profile structure
+
+LCMSBOOL LCMSEXPORT cmsAddTag(cmsHPROFILE hProfile, icTagSignature sig, const void* Tag)
+{
+ LCMSBOOL rc;
+
+ switch (sig) {
+
+ case icSigCharTargetTag:
+ case icSigCopyrightTag:
+ case icSigProfileDescriptionTag:
+ case icSigDeviceMfgDescTag:
+ case icSigDeviceModelDescTag:
+ rc = _cmsAddTextTag(hProfile, sig, (const char*) Tag);
+ break;
+
+ case icSigRedColorantTag:
+ case icSigGreenColorantTag:
+ case icSigBlueColorantTag:
+ case icSigMediaWhitePointTag:
+ case icSigMediaBlackPointTag:
+ rc = _cmsAddXYZTag(hProfile, sig, (const cmsCIEXYZ*) Tag);
+ break;
+
+ case icSigRedTRCTag:
+ case icSigGreenTRCTag:
+ case icSigBlueTRCTag:
+ case icSigGrayTRCTag:
+ rc = _cmsAddGammaTag(hProfile, sig, (LPGAMMATABLE) Tag);
+ break;
+
+ case icSigAToB0Tag:
+ case icSigAToB1Tag:
+ case icSigAToB2Tag:
+ case icSigBToA0Tag:
+ case icSigBToA1Tag:
+ case icSigBToA2Tag:
+ case icSigGamutTag:
+ case icSigPreview0Tag:
+ case icSigPreview1Tag:
+ case icSigPreview2Tag:
+ rc = _cmsAddLUTTag(hProfile, sig, Tag);
+ break;
+
+ case icSigChromaticityTag:
+ rc = _cmsAddChromaticityTag(hProfile, sig, (LPcmsCIExyYTRIPLE) Tag);
+ break;
+
+ case icSigProfileSequenceDescTag:
+ rc = _cmsAddSequenceDescriptionTag(hProfile, sig, (LPcmsSEQ) Tag);
+ break;
+
+ case icSigNamedColor2Tag:
+ rc = _cmsAddNamedColorTag(hProfile, sig, (LPcmsNAMEDCOLORLIST) Tag);
+ break;
+
+ case icSigCalibrationDateTimeTag:
+ rc = _cmsAddDateTimeTag(hProfile, sig, (struct tm*) Tag);
+ break;
+
+ case icSigColorantTableTag:
+ case icSigColorantTableOutTag:
+ rc = _cmsAddColorantTableTag(hProfile, sig, (LPcmsNAMEDCOLORLIST) Tag);
+ break;
+
+
+ case icSigChromaticAdaptationTag:
+ rc = _cmsAddChromaticAdaptationTag(hProfile, sig, (const cmsCIEXYZ*) Tag);
+ break;
+
+ default:
+ cmsSignalError(LCMS_ERRC_ABORTED, "cmsAddTag: Tag '%x' is unsupported", sig);
+ return FALSE;
+ }
+
+ // Check for critical tags
+
+ switch (sig) {
+
+ case icSigMediaWhitePointTag:
+ case icSigMediaBlackPointTag:
+ case icSigChromaticAdaptationTag:
+
+ ReadCriticalTags((LPLCMSICCPROFILE) hProfile);
+ break;
+
+ default:;
+ }
+
+ return rc;
+
+}
+
+// Low-level save to disk. It closes the profile on exit
+
+LCMSBOOL LCMSEXPORT _cmsSaveProfile(cmsHPROFILE hProfile, const char* FileName)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+ LCMSICCPROFILE Keep;
+ LCMSBOOL rc;
+
+ CopyMemory(&Keep, Icc, sizeof(LCMSICCPROFILE));
+ _cmsSetSaveToDisk(Icc, NULL);
+
+ // Pass #1 does compute offsets
+
+ if (!SaveHeader(Icc)) return FALSE;
+ if (!SaveTagDirectory(Icc)) return FALSE;
+ if (!SaveTags(Icc, &Keep)) return FALSE;
+
+
+ _cmsSetSaveToDisk(Icc, FileName);
+
+
+ // Pass #2 does save to file
+
+ if (!SaveHeader(Icc)) goto CleanUp;
+ if (!SaveTagDirectory(Icc)) goto CleanUp;
+ if (!SaveTags(Icc, &Keep)) goto CleanUp;
+
+ rc = (Icc ->Close(Icc) == 0);
+ CopyMemory(Icc, &Keep, sizeof(LCMSICCPROFILE));
+ return rc;
+
+
+ CleanUp:
+
+ Icc ->Close(Icc);
+ unlink(FileName);
+ CopyMemory(Icc, &Keep, sizeof(LCMSICCPROFILE));
+ return FALSE;
+}
+
+
+// Low-level save from open stream
+LCMSBOOL LCMSEXPORT _cmsSaveProfileToMem(cmsHPROFILE hProfile, void *MemPtr,
+ size_t* BytesNeeded)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+ LCMSICCPROFILE Keep;
+
+
+ CopyMemory(&Keep, Icc, sizeof(LCMSICCPROFILE));
+
+ _cmsSetSaveToMemory(Icc, NULL, 0);
+
+ // Pass #1 does compute offsets
+
+ if (!SaveHeader(Icc)) return FALSE;
+ if (!SaveTagDirectory(Icc)) return FALSE;
+ if (!SaveTags(Icc, &Keep)) return FALSE;
+
+ if (!MemPtr) {
+
+ // update BytesSaved so caller knows how many bytes are needed for MemPtr
+ *BytesNeeded = Icc ->UsedSpace;
+ CopyMemory(Icc, &Keep, sizeof(LCMSICCPROFILE));
+ return TRUE;
+ }
+
+ if (*BytesNeeded < Icc ->UsedSpace) {
+
+ // need at least UsedSpace in MemPtr to continue
+ CopyMemory(Icc, &Keep, sizeof(LCMSICCPROFILE));
+ return FALSE;
+ }
+
+ _cmsSetSaveToMemory(Icc, MemPtr, *BytesNeeded);
+
+
+ // Pass #2 does save to file into supplied stream
+ if (!SaveHeader(Icc)) goto CleanUp;
+ if (!SaveTagDirectory(Icc)) goto CleanUp;
+ if (!SaveTags(Icc, &Keep)) goto CleanUp;
+
+ // update BytesSaved so caller knows how many bytes put into stream
+ *BytesNeeded = Icc ->UsedSpace;
+
+ Icc ->Close(Icc);
+ CopyMemory(Icc, &Keep, sizeof(LCMSICCPROFILE));
+ return TRUE;
+
+CleanUp:
+
+ Icc ->Close(Icc);
+ CopyMemory(Icc, &Keep, sizeof(LCMSICCPROFILE));
+ return FALSE;
+}
+
diff --git a/debian/lcms/lcms-1.19.dfsg2/src/cmslut.c b/debian/lcms/lcms-1.19.dfsg2/src/cmslut.c
new file mode 100755
index 00000000..3359f305
--- /dev/null
+++ b/debian/lcms/lcms-1.19.dfsg2/src/cmslut.c
@@ -0,0 +1,843 @@
+//
+// Little cms
+// Copyright (C) 1998-2007 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 "lcms.h"
+
+// Pipeline of LUT. Enclosed by {} are new revision 4.0 of ICC spec.
+//
+// [Mat] -> [L1] -> { [Mat3] -> [Ofs3] -> [L3] ->} [CLUT] { -> [L4] -> [Mat4] -> [Ofs4] } -> [L2]
+//
+// Some of these stages would be missing. This implements the totality of
+// combinations of old and new LUT types as follows:
+//
+// Lut8 & Lut16
+// ============
+// [Mat] -> [L1] -> [CLUT] -> [L2]
+//
+// Mat2, Ofs2, L3, L3, Mat3, Ofs3 are missing
+//
+// LutAToB
+// ========
+//
+// [L1] -> [CLUT] -> [L4] -> [Mat4] -> [Ofs4] -> [L2]
+//
+// Mat, Mat3, Ofs3, L3 are missing
+// L1 = A curves
+// L4 = M curves
+// L2 = B curves
+//
+// LutBToA
+// =======
+//
+// [L1] -> [Mat3] -> [Ofs3] -> [L3] -> [CLUT] -> [L2]
+//
+// Mat, L4, Mat4, Ofs4 are missing
+// L1 = B Curves
+// L3 = M Curves
+// L2 = A curves
+//
+//
+// V2&3 emulation
+// ===============
+//
+// For output, Mat is multiplied by
+//
+//
+// | 0xff00 / 0xffff 0 0 |
+// | 0 0xff00 / 0xffff 0 |
+// | 0 0 0xff00 / 0xffff |
+//
+//
+// For input, an additional matrix is needed at the very last end of the chain
+//
+//
+// | 0xffff / 0xff00 0 0 |
+// | 0 0xffff / 0xff00 0 |
+// | 0 0 0xffff / 0xff00 |
+//
+//
+// Which reduces to (val * 257) >> 8
+
+// A couple of macros to convert between revisions
+
+#define FROM_V2_TO_V4(x) (((((x)<<8)+(x))+0x80)>>8) // BY 65535 DIV 65280 ROUND
+#define FROM_V4_TO_V2(x) ((((x)<<8)+0x80)/257) // BY 65280 DIV 65535 ROUND
+
+
+// Lut Creation & Destruction
+
+LPLUT LCMSEXPORT cmsAllocLUT(void)
+{
+ LPLUT NewLUT;
+
+ NewLUT = (LPLUT) _cmsMalloc(sizeof(LUT));
+ if (NewLUT)
+ ZeroMemory(NewLUT, sizeof(LUT));
+
+ return NewLUT;
+}
+
+void LCMSEXPORT cmsFreeLUT(LPLUT Lut)
+{
+ unsigned int i;
+
+ if (!Lut) return;
+
+ if (Lut -> T) free(Lut -> T);
+
+ for (i=0; i < Lut -> OutputChan; i++)
+ {
+ if (Lut -> L2[i]) free(Lut -> L2[i]);
+ }
+
+ for (i=0; i < Lut -> InputChan; i++)
+ {
+
+ if (Lut -> L1[i]) free(Lut -> L1[i]);
+ }
+
+
+ if (Lut ->wFlags & LUT_HASTL3) {
+
+ for (i=0; i < Lut -> InputChan; i++) {
+
+ if (Lut -> L3[i]) free(Lut -> L3[i]);
+ }
+ }
+
+ if (Lut ->wFlags & LUT_HASTL4) {
+
+ for (i=0; i < Lut -> OutputChan; i++) {
+
+ if (Lut -> L4[i]) free(Lut -> L4[i]);
+ }
+ }
+
+ if (Lut ->CLut16params.p8)
+ free(Lut ->CLut16params.p8);
+
+ free(Lut);
+}
+
+
+static
+LPVOID DupBlockTab(LPVOID Org, size_t size)
+{
+ LPVOID mem = _cmsMalloc(size);
+ if (mem != NULL)
+ CopyMemory(mem, Org, size);
+
+ return mem;
+}
+
+
+LPLUT LCMSEXPORT cmsDupLUT(LPLUT Orig)
+{
+ LPLUT NewLUT = cmsAllocLUT();
+ unsigned int i;
+
+ CopyMemory(NewLUT, Orig, sizeof(LUT));
+
+ for (i=0; i < Orig ->InputChan; i++)
+ NewLUT -> L1[i] = (LPWORD) DupBlockTab((LPVOID) Orig ->L1[i],
+ sizeof(WORD) * Orig ->In16params.nSamples);
+
+ for (i=0; i < Orig ->OutputChan; i++)
+ NewLUT -> L2[i] = (LPWORD) DupBlockTab((LPVOID) Orig ->L2[i],
+ sizeof(WORD) * Orig ->Out16params.nSamples);
+
+ NewLUT -> T = (LPWORD) DupBlockTab((LPVOID) Orig ->T, Orig -> Tsize);
+
+ return NewLUT;
+}
+
+
+static
+unsigned int UIpow(unsigned int a, unsigned int b)
+{
+ unsigned int rv = 1;
+
+ for (; b > 0; b--)
+ rv *= a;
+
+ return rv;
+}
+
+
+LCMSBOOL _cmsValidateLUT(LPLUT NewLUT)
+{
+ unsigned int calc = 1;
+ unsigned int oldCalc;
+ unsigned int power = NewLUT -> InputChan;
+
+ if (NewLUT -> cLutPoints > 100) return FALSE;
+ if (NewLUT -> InputChan > MAXCHANNELS) return FALSE;
+ if (NewLUT -> OutputChan > MAXCHANNELS) return FALSE;
+
+ if (NewLUT -> cLutPoints == 0) return TRUE;
+
+ for (; power > 0; power--) {
+
+ oldCalc = calc;
+ calc *= NewLUT -> cLutPoints;
+
+ if (calc / NewLUT -> cLutPoints != oldCalc) {
+ return FALSE;
+ }
+ }
+
+ oldCalc = calc;
+ calc *= NewLUT -> OutputChan;
+ if (NewLUT -> OutputChan && calc / NewLUT -> OutputChan != oldCalc) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+LPLUT LCMSEXPORT cmsAlloc3DGrid(LPLUT NewLUT, int clutPoints, int inputChan, int outputChan)
+{
+ DWORD nTabSize;
+
+ NewLUT -> wFlags |= LUT_HAS3DGRID;
+ NewLUT -> cLutPoints = clutPoints;
+ NewLUT -> InputChan = inputChan;
+ NewLUT -> OutputChan = outputChan;
+
+ if (!_cmsValidateLUT(NewLUT)) {
+ return NULL;
+ }
+
+ nTabSize = NewLUT -> OutputChan * UIpow(NewLUT->cLutPoints,
+ NewLUT->InputChan);
+
+ NewLUT -> T = (LPWORD) _cmsCalloc(sizeof(WORD), nTabSize);
+ nTabSize *= sizeof(WORD);
+ if (NewLUT -> T == NULL) return NULL;
+
+ ZeroMemory(NewLUT -> T, nTabSize);
+ NewLUT ->Tsize = nTabSize;
+
+
+ cmsCalcCLUT16Params(NewLUT -> cLutPoints, NewLUT -> InputChan,
+ NewLUT -> OutputChan,
+ &NewLUT -> CLut16params);
+
+ return NewLUT;
+}
+
+
+
+
+LPLUT LCMSEXPORT cmsAllocLinearTable(LPLUT NewLUT, LPGAMMATABLE Tables[], int nTable)
+{
+ unsigned int i;
+ LPWORD PtrW;
+
+ switch (nTable) {
+
+
+ case 1: NewLUT -> wFlags |= LUT_HASTL1;
+ cmsCalcL16Params(Tables[0] -> nEntries, &NewLUT -> In16params);
+ NewLUT -> InputEntries = Tables[0] -> nEntries;
+
+ for (i=0; i < NewLUT -> InputChan; i++) {
+
+ PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * NewLUT -> InputEntries);
+ if (PtrW == NULL) return NULL;
+
+ NewLUT -> L1[i] = PtrW;
+ CopyMemory(PtrW, Tables[i]->GammaTable, sizeof(WORD) * NewLUT -> InputEntries);
+ CopyMemory(&NewLUT -> LCurvesSeed[0][i], &Tables[i] -> Seed, sizeof(LCMSGAMMAPARAMS));
+ }
+
+
+ break;
+
+ case 2: NewLUT -> wFlags |= LUT_HASTL2;
+ cmsCalcL16Params(Tables[0] -> nEntries, &NewLUT -> Out16params);
+ NewLUT -> OutputEntries = Tables[0] -> nEntries;
+ for (i=0; i < NewLUT -> OutputChan; i++) {
+
+ PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * NewLUT -> OutputEntries);
+ if (PtrW == NULL) return NULL;
+
+ NewLUT -> L2[i] = PtrW;
+ CopyMemory(PtrW, Tables[i]->GammaTable, sizeof(WORD) * NewLUT -> OutputEntries);
+ CopyMemory(&NewLUT -> LCurvesSeed[1][i], &Tables[i] -> Seed, sizeof(LCMSGAMMAPARAMS));
+ }
+ break;
+
+
+ // 3 & 4 according ICC 4.0 spec
+
+ case 3:
+ NewLUT -> wFlags |= LUT_HASTL3;
+ cmsCalcL16Params(Tables[0] -> nEntries, &NewLUT -> L3params);
+ NewLUT -> L3Entries = Tables[0] -> nEntries;
+
+ for (i=0; i < NewLUT -> InputChan; i++) {
+
+ PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * NewLUT -> L3Entries);
+ if (PtrW == NULL) return NULL;
+
+ NewLUT -> L3[i] = PtrW;
+ CopyMemory(PtrW, Tables[i]->GammaTable, sizeof(WORD) * NewLUT -> L3Entries);
+ CopyMemory(&NewLUT -> LCurvesSeed[2][i], &Tables[i] -> Seed, sizeof(LCMSGAMMAPARAMS));
+ }
+ break;
+
+ case 4:
+ NewLUT -> wFlags |= LUT_HASTL4;
+ cmsCalcL16Params(Tables[0] -> nEntries, &NewLUT -> L4params);
+ NewLUT -> L4Entries = Tables[0] -> nEntries;
+ for (i=0; i < NewLUT -> OutputChan; i++) {
+
+ PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * NewLUT -> L4Entries);
+ if (PtrW == NULL) return NULL;
+
+ NewLUT -> L4[i] = PtrW;
+ CopyMemory(PtrW, Tables[i]->GammaTable, sizeof(WORD) * NewLUT -> L4Entries);
+ CopyMemory(&NewLUT -> LCurvesSeed[3][i], &Tables[i] -> Seed, sizeof(LCMSGAMMAPARAMS));
+ }
+ break;
+
+
+ default:;
+ }
+
+ return NewLUT;
+}
+
+
+// Set the LUT matrix
+
+LPLUT LCMSEXPORT cmsSetMatrixLUT(LPLUT Lut, LPMAT3 M)
+{
+ MAT3toFix(&Lut ->Matrix, M);
+
+ if (!MAT3isIdentity(&Lut->Matrix, 0.0001))
+ Lut ->wFlags |= LUT_HASMATRIX;
+
+ return Lut;
+}
+
+
+// Set matrix & offset, v4 compatible
+
+LPLUT LCMSEXPORT cmsSetMatrixLUT4(LPLUT Lut, LPMAT3 M, LPVEC3 off, DWORD dwFlags)
+{
+ WMAT3 WMat;
+ WVEC3 Woff;
+ VEC3 Zero = {{0, 0, 0}};
+
+ MAT3toFix(&WMat, M);
+
+ if (off == NULL)
+ off = &Zero;
+
+ VEC3toFix(&Woff, off);
+
+ // Nop if identity
+ if (MAT3isIdentity(&WMat, 0.0001) &&
+ (Woff.n[VX] == 0 && Woff.n[VY] == 0 && Woff.n[VZ] == 0))
+ return Lut;
+
+ switch (dwFlags) {
+
+ case LUT_HASMATRIX:
+ Lut ->Matrix = WMat;
+ Lut ->wFlags |= LUT_HASMATRIX;
+ break;
+
+ case LUT_HASMATRIX3:
+ Lut ->Mat3 = WMat;
+ Lut ->Ofs3 = Woff;
+ Lut ->wFlags |= LUT_HASMATRIX3;
+ break;
+
+ case LUT_HASMATRIX4:
+ Lut ->Mat4 = WMat;
+ Lut ->Ofs4 = Woff;
+ Lut ->wFlags |= LUT_HASMATRIX4;
+ break;
+
+
+ default:;
+ }
+
+ return Lut;
+}
+
+
+
+// The full evaluator
+
+void LCMSEXPORT cmsEvalLUT(LPLUT Lut, WORD In[], WORD Out[])
+{
+ register unsigned int i;
+ WORD StageABC[MAXCHANNELS], StageLMN[MAXCHANNELS];
+
+
+ // Try to speedup things on plain devicelinks
+ if (Lut ->wFlags == LUT_HAS3DGRID) {
+
+ Lut ->CLut16params.Interp3D(In, Out, Lut -> T, &Lut -> CLut16params);
+ return;
+ }
+
+
+ // Nope, evaluate whole LUT
+
+ for (i=0; i < Lut -> InputChan; i++)
+ StageABC[i] = In[i];
+
+
+ if (Lut ->wFlags & LUT_V4_OUTPUT_EMULATE_V2) {
+
+ // Clamp Lab to avoid overflow
+ if (StageABC[0] > 0xFF00)
+ StageABC[0] = 0xFF00;
+
+ StageABC[0] = (WORD) FROM_V2_TO_V4(StageABC[0]);
+ StageABC[1] = (WORD) FROM_V2_TO_V4(StageABC[1]);
+ StageABC[2] = (WORD) FROM_V2_TO_V4(StageABC[2]);
+
+ }
+
+ if (Lut ->wFlags & LUT_V2_OUTPUT_EMULATE_V4) {
+
+ StageABC[0] = (WORD) FROM_V4_TO_V2(StageABC[0]);
+ StageABC[1] = (WORD) FROM_V4_TO_V2(StageABC[1]);
+ StageABC[2] = (WORD) FROM_V4_TO_V2(StageABC[2]);
+ }
+
+
+ // Matrix handling.
+
+ if (Lut -> wFlags & LUT_HASMATRIX) {
+
+ WVEC3 InVect, OutVect;
+
+ // In LUT8 here comes the special gray axis fixup
+
+ if (Lut ->FixGrayAxes) {
+
+ StageABC[1] = _cmsClampWord(StageABC[1] - 128);
+ StageABC[2] = _cmsClampWord(StageABC[2] - 128);
+ }
+
+ // Matrix
+
+ InVect.n[VX] = ToFixedDomain(StageABC[0]);
+ InVect.n[VY] = ToFixedDomain(StageABC[1]);
+ InVect.n[VZ] = ToFixedDomain(StageABC[2]);
+
+
+ MAT3evalW(&OutVect, &Lut -> Matrix, &InVect);
+
+ // PCS in 1Fixed15 format, adjusting
+
+ StageABC[0] = _cmsClampWord(FromFixedDomain(OutVect.n[VX]));
+ StageABC[1] = _cmsClampWord(FromFixedDomain(OutVect.n[VY]));
+ StageABC[2] = _cmsClampWord(FromFixedDomain(OutVect.n[VZ]));
+ }
+
+
+ // First linearization
+
+ if (Lut -> wFlags & LUT_HASTL1)
+ {
+ for (i=0; i < Lut -> InputChan; i++)
+ StageABC[i] = cmsLinearInterpLUT16(StageABC[i],
+ Lut -> L1[i],
+ &Lut -> In16params);
+ }
+
+
+ // Mat3, Ofs3, L3 processing
+
+ if (Lut ->wFlags & LUT_HASMATRIX3) {
+
+ WVEC3 InVect, OutVect;
+
+ InVect.n[VX] = ToFixedDomain(StageABC[0]);
+ InVect.n[VY] = ToFixedDomain(StageABC[1]);
+ InVect.n[VZ] = ToFixedDomain(StageABC[2]);
+
+ MAT3evalW(&OutVect, &Lut -> Mat3, &InVect);
+
+ OutVect.n[VX] += Lut ->Ofs3.n[VX];
+ OutVect.n[VY] += Lut ->Ofs3.n[VY];
+ OutVect.n[VZ] += Lut ->Ofs3.n[VZ];
+
+ StageABC[0] = _cmsClampWord(FromFixedDomain(OutVect.n[VX]));
+ StageABC[1] = _cmsClampWord(FromFixedDomain(OutVect.n[VY]));
+ StageABC[2] = _cmsClampWord(FromFixedDomain(OutVect.n[VZ]));
+
+ }
+
+ if (Lut ->wFlags & LUT_HASTL3) {
+
+ for (i=0; i < Lut -> InputChan; i++)
+ StageABC[i] = cmsLinearInterpLUT16(StageABC[i],
+ Lut -> L3[i],
+ &Lut -> L3params);
+
+ }
+
+
+
+ if (Lut -> wFlags & LUT_HAS3DGRID) {
+
+ Lut ->CLut16params.Interp3D(StageABC, StageLMN, Lut -> T, &Lut -> CLut16params);
+
+ }
+ else
+ {
+
+ for (i=0; i < Lut -> InputChan; i++)
+ StageLMN[i] = StageABC[i];
+
+ }
+
+
+ // Mat4, Ofs4, L4 processing
+
+ if (Lut ->wFlags & LUT_HASTL4) {
+
+ for (i=0; i < Lut -> OutputChan; i++)
+ StageLMN[i] = cmsLinearInterpLUT16(StageLMN[i],
+ Lut -> L4[i],
+ &Lut -> L4params);
+ }
+
+ if (Lut ->wFlags & LUT_HASMATRIX4) {
+
+ WVEC3 InVect, OutVect;
+
+ InVect.n[VX] = ToFixedDomain(StageLMN[0]);
+ InVect.n[VY] = ToFixedDomain(StageLMN[1]);
+ InVect.n[VZ] = ToFixedDomain(StageLMN[2]);
+
+ MAT3evalW(&OutVect, &Lut -> Mat4, &InVect);
+
+ OutVect.n[VX] += Lut ->Ofs4.n[VX];
+ OutVect.n[VY] += Lut ->Ofs4.n[VY];
+ OutVect.n[VZ] += Lut ->Ofs4.n[VZ];
+
+ StageLMN[0] = _cmsClampWord(FromFixedDomain(OutVect.n[VX]));
+ StageLMN[1] = _cmsClampWord(FromFixedDomain(OutVect.n[VY]));
+ StageLMN[2] = _cmsClampWord(FromFixedDomain(OutVect.n[VZ]));
+
+ }
+
+ // Last linearitzation
+
+ if (Lut -> wFlags & LUT_HASTL2)
+ {
+ for (i=0; i < Lut -> OutputChan; i++)
+ Out[i] = cmsLinearInterpLUT16(StageLMN[i],
+ Lut -> L2[i],
+ &Lut -> Out16params);
+ }
+ else
+ {
+ for (i=0; i < Lut -> OutputChan; i++)
+ Out[i] = StageLMN[i];
+ }
+
+
+
+ if (Lut ->wFlags & LUT_V4_INPUT_EMULATE_V2) {
+
+ Out[0] = (WORD) FROM_V4_TO_V2(Out[0]);
+ Out[1] = (WORD) FROM_V4_TO_V2(Out[1]);
+ Out[2] = (WORD) FROM_V4_TO_V2(Out[2]);
+
+ }
+
+ if (Lut ->wFlags & LUT_V2_INPUT_EMULATE_V4) {
+
+ Out[0] = (WORD) FROM_V2_TO_V4(Out[0]);
+ Out[1] = (WORD) FROM_V2_TO_V4(Out[1]);
+ Out[2] = (WORD) FROM_V2_TO_V4(Out[2]);
+ }
+}
+
+
+// Precomputes tables for 8-bit on input devicelink.
+//
+LPLUT _cmsBlessLUT8(LPLUT Lut)
+{
+ int i, j;
+ WORD StageABC[3];
+ Fixed32 v1, v2, v3;
+ LPL8PARAMS p8;
+ LPL16PARAMS p = &Lut ->CLut16params;
+
+
+ p8 = (LPL8PARAMS) _cmsMalloc(sizeof(L8PARAMS));
+ if (p8 == NULL) return NULL;
+
+ // values comes * 257, so we can safely take first byte (x << 8 + x)
+ // if there are prelinearization, is already smelted in tables
+
+ for (i=0; i < 256; i++) {
+
+ StageABC[0] = StageABC[1] = StageABC[2] = RGB_8_TO_16(i);
+
+ if (Lut ->wFlags & LUT_HASTL1) {
+
+ for (j=0; j < 3; j++)
+ StageABC[j] = cmsLinearInterpLUT16(StageABC[j],
+ Lut -> L1[j],
+ &Lut -> In16params);
+ Lut ->wFlags &= ~LUT_HASTL1;
+ }
+
+
+ v1 = ToFixedDomain(StageABC[0] * p -> Domain);
+ v2 = ToFixedDomain(StageABC[1] * p -> Domain);
+ v3 = ToFixedDomain(StageABC[2] * p -> Domain);
+
+ p8 ->X0[i] = p->opta3 * FIXED_TO_INT(v1);
+ p8 ->Y0[i] = p->opta2 * FIXED_TO_INT(v2);
+ p8 ->Z0[i] = p->opta1 * FIXED_TO_INT(v3);
+
+ p8 ->rx[i] = (WORD) FIXED_REST_TO_INT(v1);
+ p8 ->ry[i] = (WORD) FIXED_REST_TO_INT(v2);
+ p8 ->rz[i] = (WORD) FIXED_REST_TO_INT(v3);
+
+ }
+
+ Lut -> CLut16params.p8 = p8;
+ Lut -> CLut16params.Interp3D = cmsTetrahedralInterp8;
+
+ return Lut;
+
+}
+
+
+
+
+// ----------------------------------------------------------- Reverse interpolation
+
+
+// Here's how it goes. The derivative Df(x) of the function f is the linear
+// transformation that best approximates f near the point x. It can be represented
+// by a matrix A whose entries are the partial derivatives of the components of f
+// with respect to all the coordinates. This is know as the Jacobian
+//
+// The best linear approximation to f is given by the matrix equation:
+//
+// y-y0 = A (x-x0)
+//
+// So, if x0 is a good "guess" for the zero of f, then solving for the zero of this
+// linear approximation will give a "better guess" for the zero of f. Thus let y=0,
+// and since y0=f(x0) one can solve the above equation for x. This leads to the
+// Newton's method formula:
+//
+// xn+1 = xn - A-1 f(xn)
+//
+// where xn+1 denotes the (n+1)-st guess, obtained from the n-th guess xn in the
+// fashion described above. Iterating this will give better and better approximations
+// if you have a "good enough" initial guess.
+
+
+#define JACOBIAN_EPSILON 0.001
+#define INVERSION_MAX_ITERATIONS 30
+
+
+
+// Increment with reflexion on boundary
+
+static
+void IncDelta(double *Val)
+{
+ if (*Val < (1.0 - JACOBIAN_EPSILON))
+
+ *Val += JACOBIAN_EPSILON;
+
+ else
+ *Val -= JACOBIAN_EPSILON;
+
+}
+
+
+
+static
+void ToEncoded(WORD Encoded[3], LPVEC3 Float)
+{
+ Encoded[0] = (WORD) floor(Float->n[0] * 65535.0 + 0.5);
+ Encoded[1] = (WORD) floor(Float->n[1] * 65535.0 + 0.5);
+ Encoded[2] = (WORD) floor(Float->n[2] * 65535.0 + 0.5);
+}
+
+static
+void FromEncoded(LPVEC3 Float, WORD Encoded[3])
+{
+ Float->n[0] = Encoded[0] / 65535.0;
+ Float->n[1] = Encoded[1] / 65535.0;
+ Float->n[2] = Encoded[2] / 65535.0;
+}
+
+// Evaluates the CLUT part of a LUT (4 -> 3 only)
+static
+void EvalLUTdoubleKLab(LPLUT Lut, const VEC3* In, WORD FixedK, LPcmsCIELab Out)
+{
+ WORD wIn[4], wOut[3];
+
+ wIn[0] = (WORD) floor(In ->n[0] * 65535.0 + 0.5);
+ wIn[1] = (WORD) floor(In ->n[1] * 65535.0 + 0.5);
+ wIn[2] = (WORD) floor(In ->n[2] * 65535.0 + 0.5);
+ wIn[3] = FixedK;
+
+ cmsEvalLUT(Lut, wIn, wOut);
+ cmsLabEncoded2Float(Out, wOut);
+}
+
+// Builds a Jacobian CMY->Lab
+
+static
+void ComputeJacobianLab(LPLUT Lut, LPMAT3 Jacobian, const VEC3* Colorant, WORD K)
+{
+ VEC3 ColorantD;
+ cmsCIELab Lab, LabD;
+ int j;
+
+ EvalLUTdoubleKLab(Lut, Colorant, K, &Lab);
+
+
+ for (j = 0; j < 3; j++) {
+
+ ColorantD.n[0] = Colorant ->n[0];
+ ColorantD.n[1] = Colorant ->n[1];
+ ColorantD.n[2] = Colorant ->n[2];
+
+ IncDelta(&ColorantD.n[j]);
+
+ EvalLUTdoubleKLab(Lut, &ColorantD, K, &LabD);
+
+ Jacobian->v[0].n[j] = ((LabD.L - Lab.L) / JACOBIAN_EPSILON);
+ Jacobian->v[1].n[j] = ((LabD.a - Lab.a) / JACOBIAN_EPSILON);
+ Jacobian->v[2].n[j] = ((LabD.b - Lab.b) / JACOBIAN_EPSILON);
+
+ }
+}
+
+
+// Evaluate a LUT in reverse direction. It only searches on 3->3 LUT, but It
+// can be used on CMYK -> Lab LUT to obtain black preservation.
+// Target holds LabK in this case
+
+// x1 <- x - [J(x)]^-1 * f(x)
+
+
+LCMSAPI double LCMSEXPORT cmsEvalLUTreverse(LPLUT Lut, WORD Target[], WORD Result[], LPWORD Hint)
+{
+ int i;
+ double error, LastError = 1E20;
+ cmsCIELab fx, Goal;
+ VEC3 tmp, tmp2, x;
+ MAT3 Jacobian;
+ WORD FixedK;
+ WORD LastResult[4];
+
+
+ // This is our Lab goal
+ cmsLabEncoded2Float(&Goal, Target);
+
+ // Special case for CMYK->Lab
+
+ if (Lut ->InputChan == 4)
+ FixedK = Target[3];
+ else
+ FixedK = 0;
+
+
+ // Take the hint as starting point if specified
+
+ if (Hint == NULL) {
+
+ // Begin at any point, we choose 1/3 of neutral CMY gray
+
+ x.n[0] = x.n[1] = x.n[2] = 0.3;
+
+ }
+ else {
+
+ FromEncoded(&x, Hint);
+ }
+
+
+ // Iterate
+
+ for (i = 0; i < INVERSION_MAX_ITERATIONS; i++) {
+
+ // Get beginning fx
+ EvalLUTdoubleKLab(Lut, &x, FixedK, &fx);
+
+ // Compute error
+ error = cmsDeltaE(&fx, &Goal);
+
+ // If not convergent, return last safe value
+ if (error >= LastError)
+ break;
+
+ // Keep latest values
+ LastError = error;
+
+ ToEncoded(LastResult, &x);
+ LastResult[3] = FixedK;
+
+ // Obtain slope
+ ComputeJacobianLab(Lut, &Jacobian, &x, FixedK);
+
+ // Solve system
+ tmp2.n[0] = fx.L - Goal.L;
+ tmp2.n[1] = fx.a - Goal.a;
+ tmp2.n[2] = fx.b - Goal.b;
+
+ if (!MAT3solve(&tmp, &Jacobian, &tmp2))
+ break;
+
+ // Move our guess
+ x.n[0] -= tmp.n[0];
+ x.n[1] -= tmp.n[1];
+ x.n[2] -= tmp.n[2];
+
+ // Some clipping....
+ VEC3saturate(&x);
+ }
+
+ Result[0] = LastResult[0];
+ Result[1] = LastResult[1];
+ Result[2] = LastResult[2];
+ Result[3] = LastResult[3];
+
+ return LastError;
+
+}
+
+
+
diff --git a/debian/lcms/lcms-1.19.dfsg2/src/cmsmatsh.c b/debian/lcms/lcms-1.19.dfsg2/src/cmsmatsh.c
new file mode 100755
index 00000000..634c9f32
--- /dev/null
+++ b/debian/lcms/lcms-1.19.dfsg2/src/cmsmatsh.c
@@ -0,0 +1,382 @@
+//
+// Little cms
+// Copyright (C) 1998-2007 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 "lcms.h"
+
+
+// Shaper/Matrix handling
+// This routines handles the matrix-shaper method. A note about domain
+// is here required. If the shaper-matrix is invoked on INPUT profiles,
+// after the shaper process, we have a value between 0 and 0xFFFF. Thus,
+// for proper matrix handling, we must convert it to 15fix16, so
+// ToFixedDomain might be called. But cmsLinearInterpFixed() returns
+// data yet in fixed point, so no additional process is required.
+// Then, we obtain data on 15.16, so we need to shift >> by 1 to
+// obtain 1.15 PCS format.
+
+// On OUTPUT profiles, things are inverse, we must first expand 1 bit
+// by shifting left, and then convert result between 0 and 1.000 to
+// RGB, so FromFixedDomain() must be called before pass values to
+// shaper. Trickly, there is a situation where this shifts works
+// little different. Sometimes, lcms smelts input/output
+// matrices into a single, one shaper, process. In such cases, since
+// input is encoded from 0 to 0xffff, we must first use the shaper and
+// then the matrix, an additional FromFixedDomain() must be used to
+// accomodate output values.
+
+// For a sake of simplicity, I will handle this three behaviours
+// with different routines, so the flags MATSHAPER_INPUT and MATSHAPER_OUTPUT
+// can be conbined to signal smelted matrix-shapers
+
+
+
+static
+int ComputeTables(LPGAMMATABLE Table[3], LPWORD Out[3], LPL16PARAMS p16)
+{
+ int i, AllLinear;
+
+ cmsCalcL16Params(Table[0] -> nEntries, p16);
+
+ AllLinear = 0;
+ for (i=0; i < 3; i++)
+ {
+ LPWORD PtrW;
+
+ PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * p16 -> nSamples);
+
+ if (PtrW == NULL) return -1; // Signal error
+
+ CopyMemory(PtrW, Table[i] -> GammaTable, sizeof(WORD) * Table[i] -> nEntries);
+
+ Out[i] = PtrW; // Set table pointer
+
+ // Linear after all?
+
+ AllLinear += cmsIsLinear(PtrW, p16 -> nSamples);
+ }
+
+ // If is all linear, then supress table interpolation (this
+ // will speed greately some trivial operations.
+ // Return 1 if present, 0 if all linear
+
+
+ if (AllLinear != 3) return 1;
+
+ return 0;
+
+}
+
+
+LPMATSHAPER cmsAllocMatShaper2(LPMAT3 Matrix, LPGAMMATABLE In[], LPGAMMATABLE Out[], DWORD Behaviour)
+{
+ LPMATSHAPER NewMatShaper;
+ int rc;
+
+ NewMatShaper = (LPMATSHAPER) _cmsMalloc(sizeof(MATSHAPER));
+ if (NewMatShaper)
+ ZeroMemory(NewMatShaper, sizeof(MATSHAPER));
+
+ NewMatShaper->dwFlags = Behaviour & (MATSHAPER_ALLSMELTED);
+
+ // Fill matrix part
+
+ MAT3toFix(&NewMatShaper -> Matrix, Matrix);
+
+ // Reality check
+
+ if (!MAT3isIdentity(&NewMatShaper -> Matrix, 0.00001))
+ NewMatShaper -> dwFlags |= MATSHAPER_HASMATRIX;
+
+ // Now, on the table characteristics
+
+ if (Out) {
+
+ rc = ComputeTables(Out, NewMatShaper ->L, &NewMatShaper ->p16);
+ if (rc < 0) {
+ cmsFreeMatShaper(NewMatShaper);
+ return NULL;
+ }
+ if (rc == 1) NewMatShaper -> dwFlags |= MATSHAPER_HASSHAPER;
+ }
+
+
+ if (In) {
+
+ rc = ComputeTables(In, NewMatShaper ->L2, &NewMatShaper ->p2_16);
+ if (rc < 0) {
+ cmsFreeMatShaper(NewMatShaper);
+ return NULL;
+ }
+ if (rc == 1) NewMatShaper -> dwFlags |= MATSHAPER_HASINPSHAPER;
+ }
+
+
+ return NewMatShaper;
+
+}
+
+
+
+// Creation & Destruction
+
+LPMATSHAPER cmsAllocMatShaper(LPMAT3 Matrix, LPGAMMATABLE Tables[], DWORD Behaviour)
+{
+ LPMATSHAPER NewMatShaper;
+ int i, AllLinear;
+
+ if (Matrix == NULL) return NULL;
+ for (i=0; i < 3; i++) {
+
+ if (Tables[i] == NULL) return NULL;
+ }
+
+ NewMatShaper = (LPMATSHAPER) _cmsMalloc(sizeof(MATSHAPER));
+ if (NewMatShaper)
+ ZeroMemory(NewMatShaper, sizeof(MATSHAPER));
+
+ NewMatShaper->dwFlags = Behaviour & (MATSHAPER_ALLSMELTED);
+
+ // Fill matrix part
+
+ MAT3toFix(&NewMatShaper -> Matrix, Matrix);
+
+ // Reality check
+
+ if (!MAT3isIdentity(&NewMatShaper -> Matrix, 0.00001))
+ NewMatShaper -> dwFlags |= MATSHAPER_HASMATRIX;
+
+ // Now, on the table characteristics
+ cmsCalcL16Params(Tables[0] -> nEntries, &NewMatShaper -> p16);
+
+ // Copy tables
+
+ AllLinear = 0;
+ for (i=0; i < 3; i++) {
+
+ LPWORD PtrW;
+
+ PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * NewMatShaper -> p16.nSamples);
+
+ if (PtrW == NULL) {
+ cmsFreeMatShaper(NewMatShaper);
+ return NULL;
+ }
+
+ CopyMemory(PtrW, Tables[i] -> GammaTable,
+ sizeof(WORD) * Tables[i] -> nEntries);
+
+ NewMatShaper -> L[i] = PtrW; // Set table pointer
+
+ // Linear after all?
+
+ AllLinear += cmsIsLinear(PtrW, NewMatShaper -> p16.nSamples);
+ }
+
+ // If is all linear, then supress table interpolation (this
+ // will speed greately some trivial operations
+
+ if (AllLinear != 3)
+ NewMatShaper -> dwFlags |= MATSHAPER_HASSHAPER;
+
+ return NewMatShaper;
+}
+
+
+
+// Free associated memory
+
+void cmsFreeMatShaper(LPMATSHAPER MatShaper)
+{
+ int i;
+
+ if (!MatShaper) return;
+
+ for (i=0; i < 3; i++)
+ {
+ if (MatShaper -> L[i]) _cmsFree(MatShaper ->L[i]);
+ if (MatShaper -> L2[i]) _cmsFree(MatShaper ->L2[i]);
+ }
+
+ _cmsFree(MatShaper);
+}
+
+
+// All smelted must postpose gamma to last stage.
+
+static
+void AllSmeltedBehaviour(LPMATSHAPER MatShaper, WORD In[], WORD Out[])
+{
+
+ WORD tmp[3];
+ WVEC3 InVect, OutVect;
+
+ if (MatShaper -> dwFlags & MATSHAPER_HASINPSHAPER)
+ {
+ InVect.n[VX] = cmsLinearInterpFixed(In[0], MatShaper -> L2[0], &MatShaper -> p2_16);
+ InVect.n[VY] = cmsLinearInterpFixed(In[1], MatShaper -> L2[1], &MatShaper -> p2_16);
+ InVect.n[VZ] = cmsLinearInterpFixed(In[2], MatShaper -> L2[2], &MatShaper -> p2_16);
+ }
+ else
+ {
+ InVect.n[VX] = ToFixedDomain(In[0]);
+ InVect.n[VY] = ToFixedDomain(In[1]);
+ InVect.n[VZ] = ToFixedDomain(In[2]);
+ }
+
+
+ if (MatShaper -> dwFlags & MATSHAPER_HASMATRIX)
+ {
+
+ MAT3evalW(&OutVect, &MatShaper -> Matrix, &InVect);
+ }
+ else {
+
+ OutVect.n[VX] = InVect.n[VX];
+ OutVect.n[VY] = InVect.n[VY];
+ OutVect.n[VZ] = InVect.n[VZ];
+ }
+
+
+ tmp[0] = _cmsClampWord(FromFixedDomain(OutVect.n[VX]));
+ tmp[1] = _cmsClampWord(FromFixedDomain(OutVect.n[VY]));
+ tmp[2] = _cmsClampWord(FromFixedDomain(OutVect.n[VZ]));
+
+
+
+ if (MatShaper -> dwFlags & MATSHAPER_HASSHAPER)
+ {
+ Out[0] = cmsLinearInterpLUT16(tmp[0], MatShaper -> L[0], &MatShaper -> p16);
+ Out[1] = cmsLinearInterpLUT16(tmp[1], MatShaper -> L[1], &MatShaper -> p16);
+ Out[2] = cmsLinearInterpLUT16(tmp[2], MatShaper -> L[2], &MatShaper -> p16);
+ }
+ else
+ {
+ Out[0] = tmp[0];
+ Out[1] = tmp[1];
+ Out[2] = tmp[2];
+ }
+
+}
+
+
+static
+void InputBehaviour(LPMATSHAPER MatShaper, WORD In[], WORD Out[])
+{
+ WVEC3 InVect, OutVect;
+
+
+ if (MatShaper -> dwFlags & MATSHAPER_HASSHAPER)
+ {
+ InVect.n[VX] = cmsLinearInterpFixed(In[0], MatShaper -> L[0], &MatShaper -> p16);
+ InVect.n[VY] = cmsLinearInterpFixed(In[1], MatShaper -> L[1], &MatShaper -> p16);
+ InVect.n[VZ] = cmsLinearInterpFixed(In[2], MatShaper -> L[2], &MatShaper -> p16);
+ }
+ else
+ {
+ InVect.n[VX] = ToFixedDomain(In[0]);
+ InVect.n[VY] = ToFixedDomain(In[1]);
+ InVect.n[VZ] = ToFixedDomain(In[2]);
+ }
+
+ if (MatShaper -> dwFlags & MATSHAPER_HASMATRIX)
+ {
+ MAT3evalW(&OutVect, &MatShaper -> Matrix, &InVect);
+ }
+ else
+ {
+ OutVect = InVect;
+ }
+
+ // PCS in 1Fixed15 format, adjusting
+
+ Out[0] = _cmsClampWord((OutVect.n[VX]) >> 1);
+ Out[1] = _cmsClampWord((OutVect.n[VY]) >> 1);
+ Out[2] = _cmsClampWord((OutVect.n[VZ]) >> 1);
+
+}
+
+
+static
+void OutputBehaviour(LPMATSHAPER MatShaper, WORD In[], WORD Out[])
+{
+ WVEC3 InVect, OutVect;
+ int i;
+
+ // We need to convert from XYZ to RGB, here we must
+ // shift << 1 to pass between 1.15 to 15.16 formats
+
+ InVect.n[VX] = (Fixed32) In[0] << 1;
+ InVect.n[VY] = (Fixed32) In[1] << 1;
+ InVect.n[VZ] = (Fixed32) In[2] << 1;
+
+ if (MatShaper -> dwFlags & MATSHAPER_HASMATRIX)
+ {
+ MAT3evalW(&OutVect, &MatShaper -> Matrix, &InVect);
+ }
+ else
+ {
+ OutVect = InVect;
+ }
+
+
+ if (MatShaper -> dwFlags & MATSHAPER_HASSHAPER)
+ {
+ for (i=0; i < 3; i++)
+ {
+
+ Out[i] = cmsLinearInterpLUT16(
+ _cmsClampWord(FromFixedDomain(OutVect.n[i])),
+ MatShaper -> L[i],
+ &MatShaper ->p16);
+ }
+ }
+ else
+ {
+ // Result from fixed domain to RGB
+
+ Out[0] = _cmsClampWord(FromFixedDomain(OutVect.n[VX]));
+ Out[1] = _cmsClampWord(FromFixedDomain(OutVect.n[VY]));
+ Out[2] = _cmsClampWord(FromFixedDomain(OutVect.n[VZ]));
+ }
+
+}
+
+
+// Master on evaluating shapers, 3 different behaviours
+
+void cmsEvalMatShaper(LPMATSHAPER MatShaper, WORD In[], WORD Out[])
+{
+
+ if ((MatShaper -> dwFlags & MATSHAPER_ALLSMELTED) == MATSHAPER_ALLSMELTED)
+ {
+ AllSmeltedBehaviour(MatShaper, In, Out);
+ return;
+ }
+ if (MatShaper -> dwFlags & MATSHAPER_INPUT)
+ {
+ InputBehaviour(MatShaper, In, Out);
+ return;
+ }
+
+ OutputBehaviour(MatShaper, In, Out);
+}
diff --git a/debian/lcms/lcms-1.19.dfsg2/src/cmsmtrx.c b/debian/lcms/lcms-1.19.dfsg2/src/cmsmtrx.c
new file mode 100755
index 00000000..4b2ffc96
--- /dev/null
+++ b/debian/lcms/lcms-1.19.dfsg2/src/cmsmtrx.c
@@ -0,0 +1,816 @@
+//
+// Little cms
+// Copyright (C) 1998-2007 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.
+
+// Vector & Matrix stuff
+
+#include "lcms.h"
+
+
+void cdecl VEC3init(LPVEC3 r, double x, double y, double z);
+void cdecl VEC3initF(LPWVEC3 r, double x, double y, double z);
+void cdecl VEC3toFix(LPWVEC3 r, LPVEC3 v);
+void cdecl VEC3scaleFix(LPWORD r, LPWVEC3 Scale);
+void cdecl VEC3swap(LPVEC3 a, LPVEC3 b);
+void cdecl VEC3divK(LPVEC3 r, LPVEC3 v, double d);
+void cdecl VEC3perK(LPVEC3 r, LPVEC3 v, double d);
+void cdecl VEC3perComp(LPVEC3 r, LPVEC3 a, LPVEC3 b);
+void cdecl VEC3minus(LPVEC3 r, LPVEC3 a, LPVEC3 b);
+void cdecl VEC3scaleAndCut(LPWVEC3 r, LPVEC3 v, double d);
+void cdecl VEC3cross(LPVEC3 r, LPVEC3 u, LPVEC3 v);
+void cdecl VEC3saturate(LPVEC3 v);
+
+double cdecl VEC3length(LPVEC3 a);
+double cdecl VEC3distance(LPVEC3 a, LPVEC3 b);
+
+
+void cdecl MAT3identity(LPMAT3 a);
+void cdecl MAT3per(LPMAT3 r, LPMAT3 a, LPMAT3 b);
+int cdecl MAT3inverse(LPMAT3 a, LPMAT3 b);
+LCMSBOOL cdecl MAT3solve(LPVEC3 x, LPMAT3 a, LPVEC3 b);
+double cdecl MAT3det(LPMAT3 m);
+void cdecl MAT3eval(LPVEC3 r, LPMAT3 a, LPVEC3 v);
+void cdecl MAT3toFix(LPWMAT3 r, LPMAT3 v);
+void cdecl MAT3evalW(LPWVEC3 r, LPWMAT3 a, LPWVEC3 v);
+void cdecl MAT3perK(LPMAT3 r, LPMAT3 v, double d);
+void cdecl MAT3scaleAndCut(LPWMAT3 r, LPMAT3 v, double d);
+
+// --------------------- Implementation ----------------------------
+
+#define DSWAP(x, y) {double tmp = (x); (x)=(y); (y)=tmp;}
+
+
+
+#ifdef USE_ASSEMBLER
+
+
+#ifdef _MSC_VER
+#pragma warning(disable : 4033)
+#pragma warning(disable : 4035)
+#endif
+
+
+
+Fixed32 FixedMul(Fixed32 a, Fixed32 b)
+{
+ ASM {
+
+ mov eax, ss:a
+ mov edx, ss:b
+ imul edx
+ add eax, 0x8000
+ adc edx, 0
+ shrd eax, edx, 16
+
+ }
+
+ RET(_EAX);
+}
+
+
+
+
+Fixed32 FixedSquare(Fixed32 a)
+{
+ ASM {
+ pushf
+ push edx
+ mov eax, ss:a
+ imul eax
+ add eax, 0x8000
+ adc edx, 0
+ shrd eax, edx, 16
+ sar eax, 16
+ pop edx
+ popf
+ }
+
+ RET(_EAX);
+}
+
+
+
+
+// Linear intERPolation
+// a * (h - l) >> 16 + l
+
+Fixed32 FixedLERP(Fixed32 a, Fixed32 l, Fixed32 h)
+{
+ ASM {
+ mov eax, dword ptr ss:h
+ mov edx, dword ptr ss:l
+ push edx
+ mov ecx, dword ptr ss:a
+ sub eax, edx
+ imul ecx
+ add eax, 0x8000
+ adc edx, 0
+ shrd eax, edx, 16
+ pop edx
+ add eax, edx
+ }
+
+ RET(_EAX);
+}
+
+
+// a as word is scaled by s as float
+
+WORD FixedScale(WORD a, Fixed32 s)
+{
+ ASM {
+
+ xor eax,eax
+ mov ax, ss:a // This is faster that movzx eax, ss:a
+ sal eax, 16
+ mov edx, ss:s
+ mul edx
+ add eax, 0x8000
+ adc edx, 0
+ mov eax, edx
+ }
+
+ RET(_EAX);
+}
+
+#ifdef _MSC_VER
+#pragma warning(default : 4033)
+#pragma warning(default : 4035)
+#endif
+
+#else
+
+
+// These are floating point versions for compilers that doesn't
+// support asm at all. Use with care, since this will slow down
+// all operations
+
+
+Fixed32 FixedMul(Fixed32 a, Fixed32 b)
+{
+#ifdef USE_INT64
+ LCMSULONGLONG l = (LCMSULONGLONG) (LCMSSLONGLONG) a * (LCMSULONGLONG) (LCMSSLONGLONG) b + (LCMSULONGLONG) 0x8000;
+ l >>= 16;
+ return (Fixed32) l;
+#else
+ return DOUBLE_TO_FIXED(FIXED_TO_DOUBLE(a) * FIXED_TO_DOUBLE(b));
+#endif
+}
+
+Fixed32 FixedSquare(Fixed32 a)
+{
+ return FixedMul(a, a);
+}
+
+
+Fixed32 FixedLERP(Fixed32 a, Fixed32 l, Fixed32 h)
+{
+#ifdef USE_INT64
+
+ LCMSULONGLONG dif = (LCMSULONGLONG) (h - l) * a + 0x8000;
+ dif = (dif >> 16) + l;
+ return (Fixed32) (dif);
+#else
+ double dif = h - l;
+
+ dif *= a;
+ dif /= 65536.0;
+ dif += l;
+
+ return (Fixed32) (dif + 0.5);
+#endif
+
+}
+
+
+WORD FixedScale(WORD a, Fixed32 s)
+{
+ return (WORD) (a * FIXED_TO_DOUBLE(s));
+}
+
+#endif
+
+
+#ifndef USE_INLINE
+
+Fixed32 ToFixedDomain(int a)
+{
+ return a + ((a + 0x7fff) / 0xffff);
+}
+
+
+int FromFixedDomain(Fixed32 a)
+{
+ return a - ((a + 0x7fff) >> 16);
+}
+
+#endif
+
+
+
+// Initiate a vector (double version)
+
+
+void VEC3init(LPVEC3 r, double x, double y, double z)
+{
+ r -> n[VX] = x;
+ r -> n[VY] = y;
+ r -> n[VZ] = z;
+}
+
+// Init a vector (fixed version)
+
+void VEC3initF(LPWVEC3 r, double x, double y, double z)
+{
+ r -> n[VX] = DOUBLE_TO_FIXED(x);
+ r -> n[VY] = DOUBLE_TO_FIXED(y);
+ r -> n[VZ] = DOUBLE_TO_FIXED(z);
+}
+
+
+// Convert to fixed point encoding is 1.0 = 0xFFFF
+
+void VEC3toFix(LPWVEC3 r, LPVEC3 v)
+{
+ r -> n[VX] = DOUBLE_TO_FIXED(v -> n[VX]);
+ r -> n[VY] = DOUBLE_TO_FIXED(v -> n[VY]);
+ r -> n[VZ] = DOUBLE_TO_FIXED(v -> n[VZ]);
+}
+
+// Convert from fixed point
+
+void VEC3fromFix(LPVEC3 r, LPWVEC3 v)
+{
+ r -> n[VX] = FIXED_TO_DOUBLE(v -> n[VX]);
+ r -> n[VY] = FIXED_TO_DOUBLE(v -> n[VY]);
+ r -> n[VZ] = FIXED_TO_DOUBLE(v -> n[VZ]);
+}
+
+
+// Swap two double vectors
+
+void VEC3swap(LPVEC3 a, LPVEC3 b)
+{
+ DSWAP(a-> n[VX], b-> n[VX]);
+ DSWAP(a-> n[VY], b-> n[VY]);
+ DSWAP(a-> n[VZ], b-> n[VZ]);
+}
+
+// Divide a vector by a constant
+
+void VEC3divK(LPVEC3 r, LPVEC3 v, double d)
+{
+ double d_inv = 1./d;
+
+ r -> n[VX] = v -> n[VX] * d_inv;
+ r -> n[VY] = v -> n[VY] * d_inv;
+ r -> n[VZ] = v -> n[VZ] * d_inv;
+}
+
+// Multiply by a constant
+
+void VEC3perK(LPVEC3 r, LPVEC3 v, double d )
+{
+ r -> n[VX] = v -> n[VX] * d;
+ r -> n[VY] = v -> n[VY] * d;
+ r -> n[VZ] = v -> n[VZ] * d;
+}
+
+
+void VEC3perComp(LPVEC3 r, LPVEC3 a, LPVEC3 b)
+{
+ r -> n[VX] = a->n[VX]*b->n[VX];
+ r -> n[VY] = a->n[VY]*b->n[VY];
+ r -> n[VZ] = a->n[VZ]*b->n[VZ];
+}
+
+// Minus
+
+
+void VEC3minus(LPVEC3 r, LPVEC3 a, LPVEC3 b)
+{
+ r -> n[VX] = a -> n[VX] - b -> n[VX];
+ r -> n[VY] = a -> n[VY] - b -> n[VY];
+ r -> n[VZ] = a -> n[VZ] - b -> n[VZ];
+}
+
+
+// Check id two vectors are the same, allowing tolerance
+
+static
+LCMSBOOL RangeCheck(double l, double h, double v)
+{
+ return (v >= l && v <= h);
+}
+
+
+LCMSBOOL VEC3equal(LPWVEC3 a, LPWVEC3 b, double Tolerance)
+{
+ int i;
+ double c;
+
+ for (i=0; i < 3; i++)
+ {
+ c = FIXED_TO_DOUBLE(a -> n[i]);
+ if (!RangeCheck(c - Tolerance,
+ c + Tolerance,
+ FIXED_TO_DOUBLE(b->n[i]))) return FALSE;
+ }
+
+ return TRUE;
+}
+
+LCMSBOOL VEC3equalF(LPVEC3 a, LPVEC3 b, double Tolerance)
+{
+ int i;
+ double c;
+
+ for (i=0; i < 3; i++)
+ {
+ c = a -> n[i];
+ if (!RangeCheck(c - Tolerance,
+ c + Tolerance,
+ b->n[i])) return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+void VEC3scaleFix(LPWORD r, LPWVEC3 Scale)
+{
+ if (Scale -> n[VX] == 0x00010000L &&
+ Scale -> n[VY] == 0x00010000L &&
+ Scale -> n[VZ] == 0x00010000L) return;
+
+ r[0] = (WORD) FixedScale(r[0], Scale -> n[VX]);
+ r[1] = (WORD) FixedScale(r[1], Scale -> n[VY]);
+ r[2] = (WORD) FixedScale(r[2], Scale -> n[VZ]);
+
+}
+
+
+
+// Vector cross product
+
+void VEC3cross(LPVEC3 r, LPVEC3 u, LPVEC3 v)
+{
+
+ r ->n[VX] = u->n[VY] * v->n[VZ] - v->n[VY] * u->n[VZ];
+ r ->n[VY] = u->n[VZ] * v->n[VX] - v->n[VZ] * u->n[VX];
+ r ->n[VZ] = u->n[VX] * v->n[VY] - v->n[VX] * u->n[VY];
+}
+
+
+
+// The vector size
+
+double VEC3length(LPVEC3 a)
+{
+ return sqrt(a ->n[VX] * a ->n[VX] +
+ a ->n[VY] * a ->n[VY] +
+ a ->n[VZ] * a ->n[VZ]);
+}
+
+
+// Saturate a vector into 0..1.0 range
+
+void VEC3saturate(LPVEC3 v)
+{
+ int i;
+ for (i=0; i < 3; i++) {
+ if (v ->n[i] < 0)
+ v ->n[i] = 0;
+ else
+ if (v ->n[i] > 1.0)
+ v ->n[i] = 1.0;
+ }
+}
+
+
+// Euclidean distance
+
+double VEC3distance(LPVEC3 a, LPVEC3 b)
+{
+ double d1 = a ->n[VX] - b ->n[VX];
+ double d2 = a ->n[VY] - b ->n[VY];
+ double d3 = a ->n[VZ] - b ->n[VZ];
+
+ return sqrt(d1*d1 + d2*d2 + d3*d3);
+}
+
+
+// Identity
+
+
+void MAT3identity(LPMAT3 a)
+{
+ VEC3init(&a-> v[0], 1.0, 0.0, 0.0);
+ VEC3init(&a-> v[1], 0.0, 1.0, 0.0);
+ VEC3init(&a-> v[2], 0.0, 0.0, 1.0);
+}
+
+
+
+
+// Check if matrix is Identity. Allow a tolerance as %
+
+LCMSBOOL MAT3isIdentity(LPWMAT3 a, double Tolerance)
+{
+ int i;
+ MAT3 Idd;
+ WMAT3 Idf;
+
+ MAT3identity(&Idd);
+ MAT3toFix(&Idf, &Idd);
+
+ for (i=0; i < 3; i++)
+ if (!VEC3equal(&a -> v[i], &Idf.v[i], Tolerance)) return FALSE;
+
+ return TRUE;
+
+}
+
+// Multiply two matrices
+
+
+void MAT3per(LPMAT3 r, LPMAT3 a, LPMAT3 b)
+{
+#define ROWCOL(i, j) \
+ a->v[i].n[0]*b->v[0].n[j] + a->v[i].n[1]*b->v[1].n[j] + a->v[i].n[2]*b->v[2].n[j]
+
+ VEC3init(&r-> v[0], ROWCOL(0,0), ROWCOL(0,1), ROWCOL(0,2));
+ VEC3init(&r-> v[1], ROWCOL(1,0), ROWCOL(1,1), ROWCOL(1,2));
+ VEC3init(&r-> v[2], ROWCOL(2,0), ROWCOL(2,1), ROWCOL(2,2));
+
+#undef ROWCOL //(i, j)
+}
+
+
+
+// Inverse of a matrix b = a^(-1)
+// Gauss-Jordan elimination with partial pivoting
+
+int MAT3inverse(LPMAT3 a, LPMAT3 b)
+{
+ register int i, j, max;
+
+ MAT3identity(b);
+
+ // Loop over cols of a from left to right, eliminating above and below diag
+ for (j=0; j<3; j++) { // Find largest pivot in column j among rows j..2
+
+ max = j; // Row with largest pivot candidate
+ for (i=j+1; i<3; i++)
+ if (fabs(a -> v[i].n[j]) > fabs(a -> v[max].n[j]))
+ max = i;
+
+ // Swap rows max and j in a and b to put pivot on diagonal
+
+ VEC3swap(&a -> v[max], &a -> v[j]);
+ VEC3swap(&b -> v[max], &b -> v[j]);
+
+ // Scale row j to have a unit diagonal
+
+ if (a -> v[j].n[j]==0.)
+ return -1; // singular matrix; can't invert
+
+ VEC3divK(&b-> v[j], &b -> v[j], a->v[j].n[j]);
+ VEC3divK(&a-> v[j], &a -> v[j], a->v[j].n[j]);
+
+ // Eliminate off-diagonal elems in col j of a, doing identical ops to b
+ for (i=0; i<3; i++)
+
+ if (i !=j) {
+ VEC3 temp;
+
+ VEC3perK(&temp, &b -> v[j], a -> v[i].n[j]);
+ VEC3minus(&b -> v[i], &b -> v[i], &temp);
+
+ VEC3perK(&temp, &a -> v[j], a -> v[i].n[j]);
+ VEC3minus(&a -> v[i], &a -> v[i], &temp);
+ }
+ }
+
+ return 1;
+}
+
+
+// Solve a system in the form Ax = b
+
+LCMSBOOL MAT3solve(LPVEC3 x, LPMAT3 a, LPVEC3 b)
+{
+ MAT3 m, a_1;
+
+ CopyMemory(&m, a, sizeof(MAT3));
+
+ if (!MAT3inverse(&m, &a_1)) return FALSE; // Singular matrix
+
+ MAT3eval(x, &a_1, b);
+ return TRUE;
+}
+
+
+// The determinant
+
+double MAT3det(LPMAT3 m)
+{
+
+ double a1 = m ->v[VX].n[VX];
+ double a2 = m ->v[VX].n[VY];
+ double a3 = m ->v[VX].n[VZ];
+ double b1 = m ->v[VY].n[VX];
+ double b2 = m ->v[VY].n[VY];
+ double b3 = m ->v[VY].n[VZ];
+ double c1 = m ->v[VZ].n[VX];
+ double c2 = m ->v[VZ].n[VY];
+ double c3 = m ->v[VZ].n[VZ];
+
+
+ return a1*b2*c3 - a1*b3*c2 + a2*b3*c1 - a2*b1*c3 - a3*b1*c2 - a3*b2*c1;
+}
+
+
+// linear transform
+
+
+void MAT3eval(LPVEC3 r, LPMAT3 a, LPVEC3 v)
+{
+ r->n[VX] = a->v[0].n[VX]*v->n[VX] + a->v[0].n[VY]*v->n[VY] + a->v[0].n[VZ]*v->n[VZ];
+ r->n[VY] = a->v[1].n[VX]*v->n[VX] + a->v[1].n[VY]*v->n[VY] + a->v[1].n[VZ]*v->n[VZ];
+ r->n[VZ] = a->v[2].n[VX]*v->n[VX] + a->v[2].n[VY]*v->n[VY] + a->v[2].n[VZ]*v->n[VZ];
+}
+
+
+// Ok, this is another bottleneck of performance.
+
+
+#ifdef USE_ASSEMBLER
+
+// ecx:ebx is result in 64 bits format
+// edi points to matrix, esi points to input vector
+// since only 3 accesses are in output, this is a stack variable
+
+
+void MAT3evalW(LPWVEC3 r_, LPWMAT3 a_, LPWVEC3 v_)
+{
+
+ ASM {
+
+
+ mov esi, dword ptr ss:v_
+ mov edi, dword ptr ss:a_
+
+ // r->n[VX] = FixedMul(a->v[0].n[0], v->n[0]) +
+
+ mov eax,dword ptr [esi]
+ mov edx,dword ptr [edi]
+ imul edx
+ mov ecx, eax
+ mov ebx, edx
+
+ // FixedMul(a->v[0].n[1], v->n[1]) +
+
+ mov eax,dword ptr [esi+4]
+ mov edx,dword ptr [edi+4]
+ imul edx
+ add ecx, eax
+ adc ebx, edx
+
+ // FixedMul(a->v[0].n[2], v->n[2]);
+
+ mov eax,dword ptr [esi+8]
+ mov edx,dword ptr [edi+8]
+ imul edx
+ add ecx, eax
+ adc ebx, edx
+
+ // Back to Fixed 15.16
+
+ add ecx, 0x8000
+ adc ebx, 0
+ shrd ecx, ebx, 16
+
+ push edi
+ mov edi, dword ptr ss:r_
+ mov dword ptr [edi], ecx // r -> n[VX]
+ pop edi
+
+
+
+ // 2nd row ***************************
+
+ // FixedMul(a->v[1].n[0], v->n[0])
+
+ mov eax,dword ptr [esi]
+ mov edx,dword ptr [edi+12]
+ imul edx
+ mov ecx, eax
+ mov ebx, edx
+
+ // FixedMul(a->v[1].n[1], v->n[1]) +
+
+ mov eax,dword ptr [esi+4]
+ mov edx,dword ptr [edi+16]
+ imul edx
+ add ecx, eax
+ adc ebx, edx
+
+ // FixedMul(a->v[1].n[2], v->n[2]);
+
+ mov eax,dword ptr [esi+8]
+ mov edx,dword ptr [edi+20]
+ imul edx
+ add ecx, eax
+ adc ebx, edx
+
+ add ecx, 0x8000
+ adc ebx, 0
+ shrd ecx, ebx, 16
+
+ push edi
+ mov edi, dword ptr ss:r_
+ mov dword ptr [edi+4], ecx // r -> n[VY]
+ pop edi
+
+// 3d row **************************
+
+ // r->n[VZ] = FixedMul(a->v[2].n[0], v->n[0]) +
+
+ mov eax,dword ptr [esi]
+ mov edx,dword ptr [edi+24]
+ imul edx
+ mov ecx, eax
+ mov ebx, edx
+
+ // FixedMul(a->v[2].n[1], v->n[1]) +
+
+ mov eax,dword ptr [esi+4]
+ mov edx,dword ptr [edi+28]
+ imul edx
+ add ecx, eax
+ adc ebx, edx
+
+ // FixedMul(a->v[2].n[2], v->n[2]);
+
+ mov eax,dword ptr [esi+8]
+ mov edx,dword ptr [edi+32]
+ imul edx
+ add ecx, eax
+ adc ebx, edx
+
+ add ecx, 0x8000
+ adc ebx, 0
+ shrd ecx, ebx, 16
+
+ mov edi, dword ptr ss:r_
+ mov dword ptr [edi+8], ecx // r -> n[VZ]
+ }
+}
+
+
+#else
+
+
+#ifdef USE_FLOAT
+
+void MAT3evalW(LPWVEC3 r, LPWMAT3 a, LPWVEC3 v)
+{
+ r->n[VX] = DOUBLE_TO_FIXED(
+ FIXED_TO_DOUBLE(a->v[0].n[0]) * FIXED_TO_DOUBLE(v->n[0]) +
+ FIXED_TO_DOUBLE(a->v[0].n[1]) * FIXED_TO_DOUBLE(v->n[1]) +
+ FIXED_TO_DOUBLE(a->v[0].n[2]) * FIXED_TO_DOUBLE(v->n[2])
+ );
+
+ r->n[VY] = DOUBLE_TO_FIXED(
+ FIXED_TO_DOUBLE(a->v[1].n[0]) * FIXED_TO_DOUBLE(v->n[0]) +
+ FIXED_TO_DOUBLE(a->v[1].n[1]) * FIXED_TO_DOUBLE(v->n[1]) +
+ FIXED_TO_DOUBLE(a->v[1].n[2]) * FIXED_TO_DOUBLE(v->n[2])
+ );
+
+ r->n[VZ] = DOUBLE_TO_FIXED(
+ FIXED_TO_DOUBLE(a->v[2].n[0]) * FIXED_TO_DOUBLE(v->n[0]) +
+ FIXED_TO_DOUBLE(a->v[2].n[1]) * FIXED_TO_DOUBLE(v->n[1]) +
+ FIXED_TO_DOUBLE(a->v[2].n[2]) * FIXED_TO_DOUBLE(v->n[2])
+ );
+}
+
+
+#else
+
+void MAT3evalW(LPWVEC3 r, LPWMAT3 a, LPWVEC3 v)
+{
+
+#ifdef USE_INT64
+
+ LCMSULONGLONG l1 = (LCMSULONGLONG) (LCMSSLONGLONG) a->v[0].n[0] *
+ (LCMSULONGLONG) (LCMSSLONGLONG) v->n[0] +
+ (LCMSULONGLONG) (LCMSSLONGLONG) a->v[0].n[1] *
+ (LCMSULONGLONG) (LCMSSLONGLONG) v->n[1] +
+ (LCMSULONGLONG) (LCMSSLONGLONG) a->v[0].n[2] *
+ (LCMSULONGLONG) (LCMSSLONGLONG) v->n[2] + (LCMSULONGLONG) 0x8000;
+
+ LCMSULONGLONG l2 = (LCMSULONGLONG) (LCMSSLONGLONG) a->v[1].n[0] *
+ (LCMSULONGLONG) (LCMSSLONGLONG) v->n[0] +
+ (LCMSULONGLONG) (LCMSSLONGLONG) a->v[1].n[1] *
+ (LCMSULONGLONG) (LCMSSLONGLONG) v->n[1] +
+ (LCMSULONGLONG) (LCMSSLONGLONG) a->v[1].n[2] *
+ (LCMSULONGLONG) (LCMSSLONGLONG) v->n[2] + (LCMSULONGLONG) 0x8000;
+
+ LCMSULONGLONG l3 = (LCMSULONGLONG) (LCMSSLONGLONG) a->v[2].n[0] *
+ (LCMSULONGLONG) (LCMSSLONGLONG) v->n[0] +
+ (LCMSULONGLONG) (LCMSSLONGLONG) a->v[2].n[1] *
+ (LCMSULONGLONG) (LCMSSLONGLONG) v->n[1] +
+ (LCMSULONGLONG) (LCMSSLONGLONG) a->v[2].n[2] *
+ (LCMSULONGLONG) (LCMSSLONGLONG) v->n[2] + (LCMSULONGLONG) 0x8000;
+ l1 >>= 16;
+ l2 >>= 16;
+ l3 >>= 16;
+
+ r->n[VX] = (Fixed32) l1;
+ r->n[VY] = (Fixed32) l2;
+ r->n[VZ] = (Fixed32) l3;
+
+#else
+
+ // FIXME: Rounding should be done at very last stage. There is 1-Contone rounding error!
+
+ r->n[VX] = FixedMul(a->v[0].n[0], v->n[0]) +
+ FixedMul(a->v[0].n[1], v->n[1]) +
+ FixedMul(a->v[0].n[2], v->n[2]);
+
+ r->n[VY] = FixedMul(a->v[1].n[0], v->n[0]) +
+ FixedMul(a->v[1].n[1], v->n[1]) +
+ FixedMul(a->v[1].n[2], v->n[2]);
+
+ r->n[VZ] = FixedMul(a->v[2].n[0], v->n[0]) +
+ FixedMul(a->v[2].n[1], v->n[1]) +
+ FixedMul(a->v[2].n[2], v->n[2]);
+#endif
+}
+
+#endif
+#endif
+
+
+void MAT3perK(LPMAT3 r, LPMAT3 v, double d)
+{
+ VEC3perK(&r -> v[0], &v -> v[0], d);
+ VEC3perK(&r -> v[1], &v -> v[1], d);
+ VEC3perK(&r -> v[2], &v -> v[2], d);
+}
+
+
+void MAT3toFix(LPWMAT3 r, LPMAT3 v)
+{
+ VEC3toFix(&r -> v[0], &v -> v[0]);
+ VEC3toFix(&r -> v[1], &v -> v[1]);
+ VEC3toFix(&r -> v[2], &v -> v[2]);
+}
+
+void MAT3fromFix(LPMAT3 r, LPWMAT3 v)
+{
+ VEC3fromFix(&r -> v[0], &v -> v[0]);
+ VEC3fromFix(&r -> v[1], &v -> v[1]);
+ VEC3fromFix(&r -> v[2], &v -> v[2]);
+}
+
+
+
+// Scale v by d and store it in r giving INTEGER
+
+void VEC3scaleAndCut(LPWVEC3 r, LPVEC3 v, double d)
+{
+ r -> n[VX] = (int) floor(v -> n[VX] * d + .5);
+ r -> n[VY] = (int) floor(v -> n[VY] * d + .5);
+ r -> n[VZ] = (int) floor(v -> n[VZ] * d + .5);
+}
+
+void MAT3scaleAndCut(LPWMAT3 r, LPMAT3 v, double d)
+{
+ VEC3scaleAndCut(&r -> v[0], &v -> v[0], d);
+ VEC3scaleAndCut(&r -> v[1], &v -> v[1], d);
+ VEC3scaleAndCut(&r -> v[2], &v -> v[2], d);
+}
+
+
+
+
diff --git a/debian/lcms/lcms-1.19.dfsg2/src/cmsnamed.c b/debian/lcms/lcms-1.19.dfsg2/src/cmsnamed.c
new file mode 100755
index 00000000..9a028bb2
--- /dev/null
+++ b/debian/lcms/lcms-1.19.dfsg2/src/cmsnamed.c
@@ -0,0 +1,171 @@
+//
+// Little cms
+// Copyright (C) 1998-2007 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.
+
+
+// Named color support
+
+#include "lcms.h"
+
+
+
+static
+LPcmsNAMEDCOLORLIST GrowNamedColorList(LPcmsNAMEDCOLORLIST v, int ByElements)
+{
+ if (ByElements > v ->Allocated) {
+
+ LPcmsNAMEDCOLORLIST TheNewList;
+ int NewElements;
+ size_t size;
+
+ if (v ->Allocated == 0)
+ NewElements = 64; // Initial guess
+ else
+ NewElements = v ->Allocated;
+
+ while (ByElements > NewElements)
+ NewElements *= 2;
+
+ size = sizeof(cmsNAMEDCOLORLIST) + (sizeof(cmsNAMEDCOLOR) * NewElements);
+ TheNewList = (LPcmsNAMEDCOLORLIST) _cmsMalloc(size);
+
+
+ if (TheNewList == NULL) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "Out of memory reallocating named color list");
+ return NULL;
+ }
+ else {
+ ZeroMemory(TheNewList, size);
+ CopyMemory(TheNewList, v, sizeof(cmsNAMEDCOLORLIST) + (v ->nColors - 1) * sizeof(cmsNAMEDCOLOR));
+ TheNewList -> Allocated = NewElements;
+
+ _cmsFree(v);
+ return TheNewList;
+ }
+ }
+
+ return v;
+}
+
+
+LPcmsNAMEDCOLORLIST cmsAllocNamedColorList(int n)
+{
+ size_t size = sizeof(cmsNAMEDCOLORLIST) + (n - 1) * sizeof(cmsNAMEDCOLOR);
+
+ LPcmsNAMEDCOLORLIST v = (LPcmsNAMEDCOLORLIST) _cmsMalloc(size);
+
+
+ if (v == NULL) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "Out of memory creating named color list");
+ return NULL;
+ }
+
+ ZeroMemory(v, size);
+
+ v ->nColors = n;
+ v ->Allocated = n;
+ v ->Prefix[0] = 0;
+ v ->Suffix[0] = 0;
+
+ return v;
+}
+
+void cmsFreeNamedColorList(LPcmsNAMEDCOLORLIST v)
+{
+ if (v == NULL) {
+ cmsSignalError(LCMS_ERRC_RECOVERABLE, "Couldn't free a NULL named color list");
+ return;
+ }
+
+ _cmsFree(v);
+}
+
+LCMSBOOL cmsAppendNamedColor(cmsHTRANSFORM xform, const char* Name, WORD PCS[3], WORD Colorant[MAXCHANNELS])
+{
+ _LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform;
+ LPcmsNAMEDCOLORLIST List;
+ int i;
+
+ if (v ->NamedColorList == NULL) return FALSE;
+
+ v ->NamedColorList = GrowNamedColorList(v ->NamedColorList, v->NamedColorList ->nColors + 1);
+
+ List = v ->NamedColorList;
+
+ for (i=0; i < MAXCHANNELS; i++)
+ List ->List[List ->nColors].DeviceColorant[i] = Colorant[i];
+
+ for (i=0; i < 3; i++)
+ List ->List[List ->nColors].PCS[i] = PCS[i];
+
+ strncpy(List ->List[List ->nColors].Name, Name, MAX_PATH-1);
+ List ->List[List ->nColors].Name[MAX_PATH-1] = 0;
+
+ List ->nColors++;
+ return TRUE;
+}
+
+
+
+// Returns named color count
+
+int LCMSEXPORT cmsNamedColorCount(cmsHTRANSFORM xform)
+{
+ _LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform;
+
+ if (v ->NamedColorList == NULL) return 0;
+ return v ->NamedColorList ->nColors;
+}
+
+
+LCMSBOOL LCMSEXPORT cmsNamedColorInfo(cmsHTRANSFORM xform, int nColor, char* Name, char* Prefix, char* Suffix)
+{
+ _LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform;
+
+ if (v ->NamedColorList == NULL) return FALSE;
+
+ if (nColor < 0 || nColor >= cmsNamedColorCount(xform)) return FALSE;
+
+ if (Name) { strncpy(Name, v ->NamedColorList->List[nColor].Name, 31); Name[31] = 0; }
+ if (Prefix) { strncpy(Prefix, v ->NamedColorList->Prefix, 31); Prefix[31] = 0; }
+ if (Suffix) { strncpy(Suffix, v ->NamedColorList->Suffix, 31); Suffix[31] = 0; }
+
+ return TRUE;
+}
+
+
+int LCMSEXPORT cmsNamedColorIndex(cmsHTRANSFORM xform, const char* Name)
+{
+ _LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform;
+ int i, n;
+
+ if (v ->NamedColorList == NULL) return -1;
+
+ n = cmsNamedColorCount(xform);
+ for (i=0; i < n; i++) {
+ if (stricmp(Name, v ->NamedColorList->List[i].Name) == 0)
+ return i;
+ }
+
+ return -1;
+}
+
+
diff --git a/debian/lcms/lcms-1.19.dfsg2/src/cmspack.c b/debian/lcms/lcms-1.19.dfsg2/src/cmspack.c
new file mode 100755
index 00000000..230ea34c
--- /dev/null
+++ b/debian/lcms/lcms-1.19.dfsg2/src/cmspack.c
@@ -0,0 +1,2146 @@
+// Little cms
+// Copyright (C) 1998-2007 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 "lcms.h"
+
+// This module handles all formats supported by lcms
+
+
+// ---------------------------------------------------------------------------
+
+
+// This macro return words stored as big endian
+
+#define CHANGE_ENDIAN(w) (WORD) ((WORD) ((w)<<8)|((w)>>8))
+
+// These macros handles reversing (negative)
+
+#define REVERSE_FLAVOR_8(x) ((BYTE) (0xff-(x)))
+#define REVERSE_FLAVOR_16(x) ((WORD)(0xffff-(x)))
+
+// Supress waning about info never being used
+
+#ifdef __BORLANDC__
+#pragma warn -par
+#endif
+
+#ifdef _MSC_VER
+#pragma warning(disable : 4100)
+#endif
+
+// -------------------------------------------------------- Unpacking routines.
+
+
+static
+LPBYTE UnrollAnyBytes(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ int nChan = T_CHANNELS(info -> InputFormat);
+ register int i;
+
+ for (i=0; i < nChan; i++) {
+
+ wIn[i] = RGB_8_TO_16(*accum); accum++;
+ }
+
+ return accum + T_EXTRA(info -> InputFormat);
+}
+
+
+
+static
+LPBYTE Unroll4Bytes(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ wIn[0] = RGB_8_TO_16(*accum); accum++; // C
+ wIn[1] = RGB_8_TO_16(*accum); accum++; // M
+ wIn[2] = RGB_8_TO_16(*accum); accum++; // Y
+ wIn[3] = RGB_8_TO_16(*accum); accum++; // K
+
+ return accum;
+}
+
+static
+LPBYTE Unroll4BytesReverse(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ wIn[0] = RGB_8_TO_16(REVERSE_FLAVOR_8(*accum)); accum++; // C
+ wIn[1] = RGB_8_TO_16(REVERSE_FLAVOR_8(*accum)); accum++; // M
+ wIn[2] = RGB_8_TO_16(REVERSE_FLAVOR_8(*accum)); accum++; // Y
+ wIn[3] = RGB_8_TO_16(REVERSE_FLAVOR_8(*accum)); accum++; // K
+
+ return accum;
+}
+
+
+static
+LPBYTE Unroll4BytesSwapFirst(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+
+ wIn[3] = RGB_8_TO_16(*accum); accum++; // K
+ wIn[0] = RGB_8_TO_16(*accum); accum++; // C
+ wIn[1] = RGB_8_TO_16(*accum); accum++; // M
+ wIn[2] = RGB_8_TO_16(*accum); accum++; // Y
+
+
+ return accum;
+}
+
+
+
+// KYMC
+static
+LPBYTE Unroll4BytesSwap(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ wIn[3] = RGB_8_TO_16(*accum); accum++; // K
+ wIn[2] = RGB_8_TO_16(*accum); accum++; // Y
+ wIn[1] = RGB_8_TO_16(*accum); accum++; // M
+ wIn[0] = RGB_8_TO_16(*accum); accum++; // C
+
+ return accum;
+}
+
+
+static
+LPBYTE Unroll4BytesSwapSwapFirst(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ wIn[2] = RGB_8_TO_16(*accum); accum++; // K
+ wIn[1] = RGB_8_TO_16(*accum); accum++; // Y
+ wIn[0] = RGB_8_TO_16(*accum); accum++; // M
+ wIn[3] = RGB_8_TO_16(*accum); accum++; // C
+
+ return accum;
+}
+
+
+static
+LPBYTE UnrollAnyWords(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ int nChan = T_CHANNELS(info -> InputFormat);
+ register int i;
+
+ for (i=0; i < nChan; i++) {
+
+ wIn[i] = *(LPWORD) accum; accum += 2;
+ }
+
+ return accum + T_EXTRA(info -> InputFormat) * sizeof(WORD);
+}
+
+
+static
+LPBYTE Unroll4Words(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ wIn[0] = *(LPWORD) accum; accum+= 2; // C
+ wIn[1] = *(LPWORD) accum; accum+= 2; // M
+ wIn[2] = *(LPWORD) accum; accum+= 2; // Y
+ wIn[3] = *(LPWORD) accum; accum+= 2; // K
+
+ return accum;
+}
+
+static
+LPBYTE Unroll4WordsReverse(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ wIn[0] = REVERSE_FLAVOR_16(*(LPWORD) accum); accum+= 2; // C
+ wIn[1] = REVERSE_FLAVOR_16(*(LPWORD) accum); accum+= 2; // M
+ wIn[2] = REVERSE_FLAVOR_16(*(LPWORD) accum); accum+= 2; // Y
+ wIn[3] = REVERSE_FLAVOR_16(*(LPWORD) accum); accum+= 2; // K
+
+ return accum;
+}
+
+
+static
+LPBYTE Unroll4WordsSwapFirst(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ wIn[3] = *(LPWORD) accum; accum+= 2; // K
+ wIn[0] = *(LPWORD) accum; accum+= 2; // C
+ wIn[1] = *(LPWORD) accum; accum+= 2; // M
+ wIn[2] = *(LPWORD) accum; accum+= 2; // Y
+
+ return accum;
+}
+
+
+// KYMC
+static
+LPBYTE Unroll4WordsSwap(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ wIn[3] = *(LPWORD) accum; accum+= 2; // K
+ wIn[2] = *(LPWORD) accum; accum+= 2; // Y
+ wIn[1] = *(LPWORD) accum; accum+= 2; // M
+ wIn[0] = *(LPWORD) accum; accum+= 2; // C
+
+ return accum;
+}
+
+static
+LPBYTE Unroll4WordsSwapSwapFirst(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ wIn[2] = *(LPWORD) accum; accum+= 2; // K
+ wIn[1] = *(LPWORD) accum; accum+= 2; // Y
+ wIn[0] = *(LPWORD) accum; accum+= 2; // M
+ wIn[3] = *(LPWORD) accum; accum+= 2; // C
+
+ return accum;
+}
+
+
+static
+LPBYTE Unroll4WordsBigEndian(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ wIn[0] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2; //C
+ wIn[1] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2; //M
+ wIn[2] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2; //Y
+ wIn[3] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2; //K
+
+ return accum;
+}
+
+static
+LPBYTE Unroll4WordsBigEndianReverse(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ wIn[0] = REVERSE_FLAVOR_16(CHANGE_ENDIAN(*(LPWORD) accum)); accum+= 2; //C
+ wIn[1] = REVERSE_FLAVOR_16(CHANGE_ENDIAN(*(LPWORD) accum)); accum+= 2; //M
+ wIn[2] = REVERSE_FLAVOR_16(CHANGE_ENDIAN(*(LPWORD) accum)); accum+= 2; //Y
+ wIn[3] = REVERSE_FLAVOR_16(CHANGE_ENDIAN(*(LPWORD) accum)); accum+= 2; //K
+
+ return accum;
+}
+
+
+// KYMC
+static
+LPBYTE Unroll4WordsSwapBigEndian(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ wIn[3] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2; //K
+ wIn[2] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2; //Y
+ wIn[1] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2; //M
+ wIn[0] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2; //C
+
+ return accum;
+}
+
+static
+LPBYTE Unroll3Bytes(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+
+ wIn[0] = RGB_8_TO_16(*accum); accum++; // R
+ wIn[1] = RGB_8_TO_16(*accum); accum++; // G
+ wIn[2] = RGB_8_TO_16(*accum); accum++; // B
+
+ return accum;
+}
+
+
+// Lab8 encoding using v2 PCS
+
+static
+LPBYTE Unroll3BytesLab(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+
+ wIn[0] = (WORD) ((*accum) << 8); accum++;
+ wIn[1] = (WORD) ((*accum) << 8); accum++;
+ wIn[2] = (WORD) ((*accum) << 8); accum++;
+
+ return accum;
+}
+
+
+// BRG
+
+static
+LPBYTE Unroll3BytesSwap(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+
+ wIn[2] = RGB_8_TO_16(*accum); accum++; // B
+ wIn[1] = RGB_8_TO_16(*accum); accum++; // G
+ wIn[0] = RGB_8_TO_16(*accum); accum++; // R
+
+ return accum;
+}
+
+static
+LPBYTE Unroll3Words(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ wIn[0] = *(LPWORD) accum; accum+= 2; // C R
+ wIn[1] = *(LPWORD) accum; accum+= 2; // M G
+ wIn[2] = *(LPWORD) accum; accum+= 2; // Y B
+ return accum;
+}
+
+
+static
+LPBYTE Unroll3WordsSwap(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ wIn[2] = *(LPWORD) accum; accum+= 2; // C R
+ wIn[1] = *(LPWORD) accum; accum+= 2; // M G
+ wIn[0] = *(LPWORD) accum; accum+= 2; // Y B
+ return accum;
+}
+
+
+static
+LPBYTE Unroll3WordsBigEndian(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ wIn[0] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2;
+ wIn[1] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2;
+ wIn[2] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2;
+ return accum;
+}
+
+
+static
+LPBYTE Unroll3WordsSwapBigEndian(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ wIn[2] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2;
+ wIn[1] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2;
+ wIn[0] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2;
+ return accum;
+}
+
+
+
+// Monochrome duplicates L into RGB for null-transforms
+
+static
+LPBYTE Unroll1Byte(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ wIn[0] = wIn[1] = wIn[2] = RGB_8_TO_16(*accum); accum++; // L
+ return accum;
+}
+
+
+static
+LPBYTE Unroll1ByteSkip2(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ wIn[0] = wIn[1] = wIn[2] = RGB_8_TO_16(*accum); accum++; // L
+ accum += 2;
+ return accum;
+}
+
+static
+LPBYTE Unroll1ByteReversed(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ wIn[0] = wIn[1] = wIn[2] = REVERSE_FLAVOR_16(RGB_8_TO_16(*accum)); accum++; // L
+ return accum;
+}
+
+
+static
+LPBYTE Unroll1Word(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ wIn[0] = wIn[1] = wIn[2] = *(LPWORD) accum; accum+= 2; // L
+ return accum;
+}
+
+static
+LPBYTE Unroll1WordReversed(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ wIn[0] = wIn[1] = wIn[2] = REVERSE_FLAVOR_16(*(LPWORD) accum); accum+= 2;
+ return accum;
+}
+
+
+static
+LPBYTE Unroll1WordBigEndian(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ wIn[0] = wIn[1] = wIn[2] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2;
+ return accum;
+}
+
+static
+LPBYTE Unroll1WordSkip3(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ wIn[0] = wIn[1] = wIn[2] = *(LPWORD) accum;
+
+ accum += 8;
+ return accum;
+}
+
+
+// Monochrome + alpha. Alpha is lost
+
+static
+LPBYTE Unroll2Byte(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ wIn[0] = wIn[1] = wIn[2] = RGB_8_TO_16(*accum); accum++; // L
+ wIn[3] = RGB_8_TO_16(*accum); accum++; // alpha
+ return accum;
+}
+
+static
+LPBYTE Unroll2ByteSwapFirst(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ wIn[3] = RGB_8_TO_16(*accum); accum++; // alpha
+ wIn[0] = wIn[1] = wIn[2] = RGB_8_TO_16(*accum); accum++; // L
+ return accum;
+}
+
+
+static
+LPBYTE Unroll2Word(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ wIn[0] = wIn[1] = wIn[2] = *(LPWORD) accum; accum+= 2; // L
+ wIn[3] = *(LPWORD) accum; accum += 2; // alpha
+
+ return accum;
+}
+
+
+static
+LPBYTE Unroll2WordSwapFirst(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ wIn[3] = *(LPWORD) accum; accum += 2; // alpha
+ wIn[0] = wIn[1] = wIn[2] = *(LPWORD) accum; accum+= 2; // L
+
+ return accum;
+}
+
+static
+LPBYTE Unroll2WordBigEndian(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ wIn[0] = wIn[1] = wIn[2] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2;
+ wIn[3] = CHANGE_ENDIAN(*(LPWORD) accum); accum+= 2;
+
+ return accum;
+}
+
+
+
+
+static
+LPBYTE UnrollPlanarBytes(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ int nChan = T_CHANNELS(info -> InputFormat);
+ register int i;
+ LPBYTE Init = accum;
+
+ for (i=0; i < nChan; i++) {
+
+ wIn[i] = RGB_8_TO_16(*accum);
+ accum += info -> StrideIn;
+ }
+
+ return (Init + 1);
+}
+
+
+
+static
+LPBYTE UnrollPlanarWords(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ int nChan = T_CHANNELS(info -> InputFormat);
+ register int i;
+ LPBYTE Init = accum;
+
+ for (i=0; i < nChan; i++) {
+
+ wIn[i] = *(LPWORD) accum;
+ accum += (info -> StrideIn * sizeof(WORD));
+ }
+
+ return (Init + sizeof(WORD));
+}
+
+
+
+static
+LPBYTE UnrollPlanarWordsBigEndian(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ int nChan = T_CHANNELS(info -> InputFormat);
+ register int i;
+ LPBYTE Init = accum;
+
+ for (i=0; i < nChan; i++) {
+
+ wIn[i] = CHANGE_ENDIAN(*(LPWORD) accum);
+ accum += (info -> StrideIn * sizeof(WORD));
+ }
+
+ return (Init + sizeof(WORD));
+}
+
+
+// floating point
+static
+LPBYTE UnrollLabDouble(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+
+ if (T_PLANAR(info -> InputFormat)) {
+
+ double* Pt = (double*) accum;
+
+ cmsCIELab Lab;
+
+ Lab.L = Pt[0];
+ Lab.a = Pt[info->StrideIn];
+ Lab.b = Pt[info->StrideIn*2];
+
+ if (info ->lInputV4Lab)
+ cmsFloat2LabEncoded4(wIn, &Lab);
+ else
+ cmsFloat2LabEncoded(wIn, &Lab);
+
+ return accum + sizeof(double);
+ }
+ else {
+
+ if (info ->lInputV4Lab)
+ cmsFloat2LabEncoded4(wIn, (LPcmsCIELab) accum);
+ else
+ cmsFloat2LabEncoded(wIn, (LPcmsCIELab) accum);
+
+ accum += sizeof(cmsCIELab);
+
+ return accum;
+ }
+}
+
+static
+LPBYTE UnrollXYZDouble(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ if (T_PLANAR(info -> InputFormat)) {
+
+ double* Pt = (double*) accum;
+ cmsCIEXYZ XYZ;
+
+ XYZ.X = Pt[0];
+ XYZ.Y = Pt[info->StrideIn];
+ XYZ.Z = Pt[info->StrideIn*2];
+ cmsFloat2XYZEncoded(wIn, &XYZ);
+
+ return accum + sizeof(double);
+
+ }
+
+ else {
+
+
+ cmsFloat2XYZEncoded(wIn, (LPcmsCIEXYZ) accum);
+ accum += sizeof(cmsCIEXYZ);
+
+ return accum;
+ }
+}
+
+
+
+// Inks does come in percentage
+static
+LPBYTE UnrollInkDouble(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ double* Inks = (double*) accum;
+ int nChan = T_CHANNELS(info -> InputFormat);
+ int Planar = T_PLANAR(info -> InputFormat);
+ int i;
+ double v;
+
+ for (i=0; i < nChan; i++) {
+
+ if (Planar)
+
+ v = Inks[i * info ->StrideIn];
+ else
+ v = Inks[i];
+
+ v = floor(v * 655.35 + 0.5);
+
+ if (v > 65535.0) v = 65535.0;
+ if (v < 0) v = 0;
+
+ wIn[i] = (WORD) v;
+ }
+
+ if (T_PLANAR(info -> InputFormat))
+ return accum + sizeof(double);
+ else
+ return accum + (nChan + T_EXTRA(info ->InputFormat)) * sizeof(double);
+}
+
+
+// Remaining cases are between 0..1.0
+static
+LPBYTE UnrollDouble(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ double* Inks = (double*) accum;
+ int nChan = T_CHANNELS(info -> InputFormat);
+ int Planar = T_PLANAR(info -> InputFormat);
+ int i;
+ double v;
+
+ for (i=0; i < nChan; i++) {
+
+ if (Planar)
+
+ v = Inks[i * info ->StrideIn];
+ else
+ v = Inks[i];
+
+ v = floor(v * 65535.0 + 0.5);
+
+ if (v > 65535.0) v = 65535.0;
+ if (v < 0) v = 0;
+
+ wIn[i] = (WORD) v;
+ }
+
+ if (T_PLANAR(info -> InputFormat))
+ return accum + sizeof(double);
+ else
+ return accum + (nChan + T_EXTRA(info ->InputFormat)) * sizeof(double);
+}
+
+
+
+static
+LPBYTE UnrollDouble1Chan(register _LPcmsTRANSFORM info, register WORD wIn[], register LPBYTE accum)
+{
+ double* Inks = (double*) accum;
+ double v;
+
+
+ v = floor(Inks[0] * 65535.0 + 0.5);
+
+ if (v > 65535.0) v = 65535.0;
+ if (v < 0) v = 0;
+
+
+ wIn[0] = wIn[1] = wIn[2] = (WORD) v;
+
+ return accum + sizeof(double);
+}
+
+
+// ----------------------------------------------------------- Packing routines
+
+
+// Generic N-bytes plus dither 16-to-8 conversion. Currently is just a quick hack
+
+static int err[MAXCHANNELS];
+
+static
+LPBYTE PackNBytesDither(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ int nChan = T_CHANNELS(info -> OutputFormat);
+ register int i;
+ unsigned int n, pe, pf;
+
+ for (i=0; i < nChan; i++) {
+
+ n = wOut[i] + err[i]; // Value
+
+ pe = (n / 257); // Whole part
+ pf = (n % 257); // Fractional part
+
+ err[i] = pf; // Store it for next pixel
+
+ *output++ = (BYTE) pe;
+ }
+
+ return output + T_EXTRA(info ->OutputFormat);
+}
+
+
+
+static
+LPBYTE PackNBytesSwapDither(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ int nChan = T_CHANNELS(info -> OutputFormat);
+ register int i;
+ unsigned int n, pe, pf;
+
+ for (i=nChan-1; i >= 0; --i) {
+
+ n = wOut[i] + err[i]; // Value
+
+ pe = (n / 257); // Whole part
+ pf = (n % 257); // Fractional part
+
+ err[i] = pf; // Store it for next pixel
+
+ *output++ = (BYTE) pe;
+ }
+
+
+ return output + T_EXTRA(info ->OutputFormat);
+}
+
+
+
+// Generic chunky for byte
+
+static
+LPBYTE PackNBytes(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ int nChan = T_CHANNELS(info -> OutputFormat);
+ register int i;
+
+ for (i=0; i < nChan; i++)
+ *output++ = RGB_16_TO_8(wOut[i]);
+
+ return output + T_EXTRA(info ->OutputFormat);
+}
+
+// Chunky reversed order bytes
+
+static
+LPBYTE PackNBytesSwap(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ int nChan = T_CHANNELS(info -> OutputFormat);
+ register int i;
+
+ for (i=nChan-1; i >= 0; --i)
+ *output++ = RGB_16_TO_8(wOut[i]);
+
+ return output + T_EXTRA(info ->OutputFormat);
+
+}
+
+
+static
+LPBYTE PackNWords(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ int nChan = T_CHANNELS(info -> OutputFormat);
+ register int i;
+
+ for (i=0; i < nChan; i++) {
+ *(LPWORD) output = wOut[i];
+ output += sizeof(WORD);
+ }
+
+ return output + T_EXTRA(info ->OutputFormat) * sizeof(WORD);
+}
+
+static
+LPBYTE PackNWordsSwap(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ int nChan = T_CHANNELS(info -> OutputFormat);
+ register int i;
+
+ for (i=nChan-1; i >= 0; --i) {
+ *(LPWORD) output = wOut[i];
+ output += sizeof(WORD);
+ }
+
+ return output + T_EXTRA(info ->OutputFormat) * sizeof(WORD);
+}
+
+
+
+static
+LPBYTE PackNWordsBigEndian(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ int nChan = T_CHANNELS(info -> OutputFormat);
+ register int i;
+
+ for (i=0; i < nChan; i++) {
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[i]);
+ output += sizeof(WORD);
+ }
+
+ return output + T_EXTRA(info ->OutputFormat) * sizeof(WORD);
+}
+
+
+static
+LPBYTE PackNWordsSwapBigEndian(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ int nChan = T_CHANNELS(info -> OutputFormat);
+ register int i;
+
+ for (i=nChan-1; i >= 0; --i) {
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[i]);
+ output += sizeof(WORD);
+ }
+
+ return output + T_EXTRA(info ->OutputFormat) * sizeof(WORD);
+}
+
+
+static
+LPBYTE PackPlanarBytes(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ int nChan = T_CHANNELS(info -> OutputFormat);
+ register int i;
+ LPBYTE Init = output;
+
+ for (i=0; i < nChan; i++) {
+
+ *(LPBYTE) output = RGB_16_TO_8(wOut[i]);
+ output += info -> StrideOut;
+ }
+
+ return (Init + 1);
+}
+
+
+static
+LPBYTE PackPlanarWords(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ int nChan = T_CHANNELS(info -> OutputFormat);
+ register int i;
+ LPBYTE Init = output;
+
+ for (i=0; i < nChan; i++) {
+
+ *(LPWORD) output = wOut[i];
+ output += (info -> StrideOut * sizeof(WORD));
+ }
+
+ return (Init + 2);
+}
+
+
+// CMYKcm (unrolled for speed)
+
+static
+LPBYTE Pack6Bytes(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ *output++ = RGB_16_TO_8(wOut[0]);
+ *output++ = RGB_16_TO_8(wOut[1]);
+ *output++ = RGB_16_TO_8(wOut[2]);
+ *output++ = RGB_16_TO_8(wOut[3]);
+ *output++ = RGB_16_TO_8(wOut[4]);
+ *output++ = RGB_16_TO_8(wOut[5]);
+
+ return output;
+}
+
+// KCMYcm
+
+static
+LPBYTE Pack6BytesSwap(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ *output++ = RGB_16_TO_8(wOut[3]);
+ *output++ = RGB_16_TO_8(wOut[0]);
+ *output++ = RGB_16_TO_8(wOut[1]);
+ *output++ = RGB_16_TO_8(wOut[2]);
+ *output++ = RGB_16_TO_8(wOut[4]);
+ *output++ = RGB_16_TO_8(wOut[5]);
+
+ return output;
+}
+
+// CMYKcm
+static
+LPBYTE Pack6Words(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ *(LPWORD) output = wOut[0];
+ output+= 2;
+ *(LPWORD) output = wOut[1];
+ output+= 2;
+ *(LPWORD) output = wOut[2];
+ output+= 2;
+ *(LPWORD) output = wOut[3];
+ output+= 2;
+ *(LPWORD) output = wOut[4];
+ output+= 2;
+ *(LPWORD) output = wOut[5];
+ output+= 2;
+
+ return output;
+}
+
+// KCMYcm
+static
+LPBYTE Pack6WordsSwap(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ *(LPWORD) output = wOut[3];
+ output+= 2;
+ *(LPWORD) output = wOut[0];
+ output+= 2;
+ *(LPWORD) output = wOut[1];
+ output+= 2;
+ *(LPWORD) output = wOut[2];
+ output+= 2;
+ *(LPWORD) output = wOut[4];
+ output+= 2;
+ *(LPWORD) output = wOut[5];
+ output+= 2;
+
+ return output;
+}
+
+// CMYKcm
+static
+LPBYTE Pack6WordsBigEndian(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[0]);
+ output+= 2;
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[1]);
+ output+= 2;
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[2]);
+ output+= 2;
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[3]);
+ output+= 2;
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[4]);
+ output+= 2;
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[5]);
+ output+= 2;
+
+ return output;
+}
+
+// KCMYcm
+static
+LPBYTE Pack6WordsSwapBigEndian(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[3]);
+ output+= 2;
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[0]);
+ output+= 2;
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[1]);
+ output+= 2;
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[2]);
+ output+= 2;
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[4]);
+ output+= 2;
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[5]);
+ output+= 2;
+
+ return output;
+}
+
+
+static
+LPBYTE Pack4Bytes(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ *output++ = RGB_16_TO_8(wOut[0]);
+ *output++ = RGB_16_TO_8(wOut[1]);
+ *output++ = RGB_16_TO_8(wOut[2]);
+ *output++ = RGB_16_TO_8(wOut[3]);
+
+ return output;
+}
+
+static
+LPBYTE Pack4BytesReverse(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ *output++ = REVERSE_FLAVOR_8(RGB_16_TO_8(wOut[0]));
+ *output++ = REVERSE_FLAVOR_8(RGB_16_TO_8(wOut[1]));
+ *output++ = REVERSE_FLAVOR_8(RGB_16_TO_8(wOut[2]));
+ *output++ = REVERSE_FLAVOR_8(RGB_16_TO_8(wOut[3]));
+
+ return output;
+}
+
+
+static
+LPBYTE Pack4BytesSwapFirst(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ *output++ = RGB_16_TO_8(wOut[3]);
+ *output++ = RGB_16_TO_8(wOut[0]);
+ *output++ = RGB_16_TO_8(wOut[1]);
+ *output++ = RGB_16_TO_8(wOut[2]);
+
+ return output;
+}
+
+
+// ABGR
+
+static
+LPBYTE Pack4BytesSwap(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ *output++ = RGB_16_TO_8(wOut[3]);
+ *output++ = RGB_16_TO_8(wOut[2]);
+ *output++ = RGB_16_TO_8(wOut[1]);
+ *output++ = RGB_16_TO_8(wOut[0]);
+
+ return output;
+}
+
+
+static
+LPBYTE Pack4BytesSwapSwapFirst(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ *output++ = RGB_16_TO_8(wOut[2]);
+ *output++ = RGB_16_TO_8(wOut[1]);
+ *output++ = RGB_16_TO_8(wOut[0]);
+ *output++ = RGB_16_TO_8(wOut[3]);
+
+ return output;
+}
+
+
+static
+LPBYTE Pack4Words(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ *(LPWORD) output = wOut[0];
+ output+= 2;
+ *(LPWORD) output = wOut[1];
+ output+= 2;
+ *(LPWORD) output = wOut[2];
+ output+= 2;
+ *(LPWORD) output = wOut[3];
+ output+= 2;
+
+ return output;
+}
+
+
+static
+LPBYTE Pack4WordsReverse(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ *(LPWORD) output = REVERSE_FLAVOR_16(wOut[0]);
+ output+= 2;
+ *(LPWORD) output = REVERSE_FLAVOR_16(wOut[1]);
+ output+= 2;
+ *(LPWORD) output = REVERSE_FLAVOR_16(wOut[2]);
+ output+= 2;
+ *(LPWORD) output = REVERSE_FLAVOR_16(wOut[3]);
+ output+= 2;
+
+ return output;
+}
+
+// ABGR
+
+static
+LPBYTE Pack4WordsSwap(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ *(LPWORD) output = wOut[3];
+ output+= 2;
+ *(LPWORD) output = wOut[2];
+ output+= 2;
+ *(LPWORD) output = wOut[1];
+ output+= 2;
+ *(LPWORD) output = wOut[0];
+ output+= 2;
+
+ return output;
+}
+
+// CMYK
+static
+LPBYTE Pack4WordsBigEndian(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[0]);
+ output+= 2;
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[1]);
+ output+= 2;
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[2]);
+ output+= 2;
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[3]);
+ output+= 2;
+
+ return output;
+}
+
+
+static
+LPBYTE Pack4WordsBigEndianReverse(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ *(LPWORD) output = CHANGE_ENDIAN(REVERSE_FLAVOR_16(wOut[0]));
+ output+= 2;
+ *(LPWORD) output = CHANGE_ENDIAN(REVERSE_FLAVOR_16(wOut[1]));
+ output+= 2;
+ *(LPWORD) output = CHANGE_ENDIAN(REVERSE_FLAVOR_16(wOut[2]));
+ output+= 2;
+ *(LPWORD) output = CHANGE_ENDIAN(REVERSE_FLAVOR_16(wOut[3]));
+ output+= 2;
+
+ return output;
+}
+
+// KYMC
+
+static
+LPBYTE Pack4WordsSwapBigEndian(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[3]);
+ output+= 2;
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[2]);
+ output+= 2;
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[1]);
+ output+= 2;
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[0]);
+ output+= 2;
+
+ return output;
+}
+
+static
+LPBYTE Pack3Bytes(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ *output++ = RGB_16_TO_8(wOut[0]);
+ *output++ = RGB_16_TO_8(wOut[1]);
+ *output++ = RGB_16_TO_8(wOut[2]);
+
+ return output;
+}
+
+static
+LPBYTE Pack3BytesLab(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ *output++ = (BYTE) (wOut[0] >> 8);
+ *output++ = (BYTE) (wOut[1] >> 8);
+ *output++ = (BYTE) (wOut[2] >> 8);
+
+ return output;
+}
+
+
+static
+LPBYTE Pack3BytesSwap(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ *output++ = RGB_16_TO_8(wOut[2]);
+ *output++ = RGB_16_TO_8(wOut[1]);
+ *output++ = RGB_16_TO_8(wOut[0]);
+
+ return output;
+}
+
+
+static
+LPBYTE Pack3Words(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ *(LPWORD) output = wOut[0];
+ output+= 2;
+ *(LPWORD) output = wOut[1];
+ output+= 2;
+ *(LPWORD) output = wOut[2];
+ output+= 2;
+
+ return output;
+}
+
+static
+LPBYTE Pack3WordsSwap(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ *(LPWORD) output = wOut[2];
+ output+= 2;
+ *(LPWORD) output = wOut[1];
+ output+= 2;
+ *(LPWORD) output = wOut[0];
+ output+= 2;
+
+ return output;
+}
+
+static
+LPBYTE Pack3WordsBigEndian(register _LPcmsTRANSFORM info, register WORD wOut[], register LPBYTE output)
+{
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[0]);
+ output+= 2;
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[1]);
+ output+= 2;
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[2]);
+ output+= 2;
+
+ return output;
+}
+
+
+static
+LPBYTE Pack3WordsSwapBigEndian(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output)
+{
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[2]);
+ output+= 2;
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[1]);
+ output+= 2;
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[0]);
+ output+= 2;
+
+ return output;
+}
+
+
+static
+LPBYTE Pack3BytesAndSkip1(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output)
+{
+ *output++ = RGB_16_TO_8(wOut[0]);
+ *output++ = RGB_16_TO_8(wOut[1]);
+ *output++ = RGB_16_TO_8(wOut[2]);
+ output++;
+
+ return output;
+}
+
+
+static
+LPBYTE Pack3BytesAndSkip1SwapFirst(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output)
+{
+ output++;
+ *output++ = RGB_16_TO_8(wOut[0]);
+ *output++ = RGB_16_TO_8(wOut[1]);
+ *output++ = RGB_16_TO_8(wOut[2]);
+
+ return output;
+}
+
+static
+LPBYTE Pack3BytesAndSkip1Swap(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output)
+{
+ output++;
+ *output++ = RGB_16_TO_8(wOut[2]);
+ *output++ = RGB_16_TO_8(wOut[1]);
+ *output++ = RGB_16_TO_8(wOut[0]);
+
+ return output;
+}
+
+
+static
+LPBYTE Pack3BytesAndSkip1SwapSwapFirst(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output)
+{
+ *output++ = RGB_16_TO_8(wOut[2]);
+ *output++ = RGB_16_TO_8(wOut[1]);
+ *output++ = RGB_16_TO_8(wOut[0]);
+ output++;
+
+ return output;
+}
+
+
+static
+LPBYTE Pack3WordsAndSkip1(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output)
+{
+ *(LPWORD) output = wOut[0];
+ output+= 2;
+ *(LPWORD) output = wOut[1];
+ output+= 2;
+ *(LPWORD) output = wOut[2];
+ output+= 2;
+ output+= 2;
+
+ return output;
+}
+
+static
+LPBYTE Pack3WordsAndSkip1Swap(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output)
+{
+ output+= 2;
+ *(LPWORD) output = wOut[2];
+ output+= 2;
+ *(LPWORD) output = wOut[1];
+ output+= 2;
+ *(LPWORD) output = wOut[0];
+ output+= 2;
+
+
+ return output;
+}
+
+
+static
+LPBYTE Pack3WordsAndSkip1SwapSwapFirst(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output)
+{
+ *(LPWORD) output = wOut[2];
+ output+= 2;
+ *(LPWORD) output = wOut[1];
+ output+= 2;
+ *(LPWORD) output = wOut[0];
+ output+= 2;
+ output+= 2;
+
+
+ return output;
+}
+
+
+static
+LPBYTE Pack3WordsAndSkip1BigEndian(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output)
+{
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[0]);
+ output+= 2;
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[1]);
+ output+= 2;
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[2]);
+ output+= 2;
+ output+= 2;
+
+ return output;
+}
+
+
+static
+LPBYTE Pack3WordsAndSkip1SwapBigEndian(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output)
+{
+ output+= 2;
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[2]);
+ output+= 2;
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[1]);
+ output+= 2;
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[0]);
+ output+= 2;
+
+
+ return output;
+}
+
+
+
+static
+LPBYTE Pack1Byte(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output)
+{
+ *output++ = RGB_16_TO_8(wOut[0]);
+ return output;
+}
+
+
+static
+LPBYTE Pack1ByteAndSkip1(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output)
+{
+ *output++ = RGB_16_TO_8(wOut[0]);
+ output++;
+ return output;
+}
+
+
+static
+LPBYTE Pack1ByteAndSkip1SwapFirst(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output)
+{
+ output++;
+ *output++ = RGB_16_TO_8(wOut[0]);
+
+ return output;
+}
+
+static
+LPBYTE Pack1Word(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output)
+{
+ *(LPWORD) output = wOut[0];
+ output+= 2;
+
+ return output;
+}
+
+static
+LPBYTE Pack1WordBigEndian(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output)
+{
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[0]);
+ output+= 2;
+
+ return output;
+}
+
+
+static
+LPBYTE Pack1WordAndSkip1(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output)
+{
+ *(LPWORD) output = wOut[0];
+ output+= 4;
+
+ return output;
+}
+
+static
+LPBYTE Pack1WordAndSkip1SwapFirst(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output)
+{
+ output += 2;
+ *(LPWORD) output = wOut[0];
+ output+= 2;
+
+ return output;
+}
+
+
+static
+LPBYTE Pack1WordAndSkip1BigEndian(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output)
+{
+ *(LPWORD) output = CHANGE_ENDIAN(wOut[0]);
+ output+= 4;
+
+ return output;
+}
+
+
+// Unencoded Float values -- don't try optimize speed
+
+static
+LPBYTE PackLabDouble(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output)
+{
+
+ if (T_PLANAR(Info -> OutputFormat)) {
+
+ cmsCIELab Lab;
+ double* Out = (double*) output;
+ cmsLabEncoded2Float(&Lab, wOut);
+
+ Out[0] = Lab.L;
+ Out[Info ->StrideOut] = Lab.a;
+ Out[Info ->StrideOut*2] = Lab.b;
+
+ return output + sizeof(double);
+
+ }
+ else {
+
+ if (Info ->lOutputV4Lab)
+ cmsLabEncoded2Float4((LPcmsCIELab) output, wOut);
+ else
+ cmsLabEncoded2Float((LPcmsCIELab) output, wOut);
+
+ return output + (sizeof(cmsCIELab) + T_EXTRA(Info ->OutputFormat) * sizeof(double));
+ }
+
+}
+
+static
+LPBYTE PackXYZDouble(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output)
+{
+
+ if (T_PLANAR(Info -> OutputFormat)) {
+
+ cmsCIEXYZ XYZ;
+ double* Out = (double*) output;
+ cmsXYZEncoded2Float(&XYZ, wOut);
+
+ Out[0] = XYZ.X;
+ Out[Info ->StrideOut] = XYZ.Y;
+ Out[Info ->StrideOut*2] = XYZ.Z;
+
+ return output + sizeof(double);
+
+ }
+ else {
+
+ cmsXYZEncoded2Float((LPcmsCIEXYZ) output, wOut);
+
+ return output + (sizeof(cmsCIEXYZ) + T_EXTRA(Info ->OutputFormat) * sizeof(double));
+ }
+}
+
+
+
+static
+LPBYTE PackInkDouble(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output)
+{
+ double* Inks = (double*) output;
+ int nChan = T_CHANNELS(Info -> OutputFormat);
+ int i;
+
+ if (T_PLANAR(Info -> OutputFormat)) {
+
+ for (i=0; i < nChan; i++) {
+
+ Inks[i*Info ->StrideOut] = wOut[i] / 655.35;
+ }
+
+ return output + sizeof(double);
+ }
+ else {
+
+ for (i=0; i < nChan; i++) {
+
+ Inks[i] = wOut[i] / 655.35;
+ }
+
+
+ return output + (nChan + T_EXTRA(Info ->OutputFormat)) * sizeof(double);
+ }
+
+}
+
+
+static
+LPBYTE PackDouble(register _LPcmsTRANSFORM Info, register WORD wOut[], register LPBYTE output)
+{
+ double* Inks = (double*) output;
+ int nChan = T_CHANNELS(Info -> OutputFormat);
+ int i;
+
+
+ if (T_PLANAR(Info -> OutputFormat)) {
+
+ for (i=0; i < nChan; i++) {
+
+ Inks[i*Info ->StrideOut] = wOut[i] / 65535.0;
+ }
+
+ return output + sizeof(double);
+
+ }
+ else {
+ for (i=0; i < nChan; i++) {
+
+ Inks[i] = wOut[i] / 65535.0;
+ }
+
+ return output + (nChan + T_EXTRA(Info ->OutputFormat)) * sizeof(double);
+ }
+
+}
+
+
+// choose routine from Input identifier
+
+_cmsFIXFN _cmsIdentifyInputFormat(_LPcmsTRANSFORM xform, DWORD dwInput)
+{
+ _cmsFIXFN FromInput = NULL;
+
+
+ // Check Named Color
+
+ if (xform) {
+
+ if (xform ->InputProfile) {
+
+ if (cmsGetDeviceClass(xform ->InputProfile) == icSigNamedColorClass) {
+
+ if (dwInput != TYPE_NAMED_COLOR_INDEX) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "Named color needs TYPE_NAMED_COLOR_INDEX");
+ return NULL;
+ }
+ }
+
+ }
+ }
+
+ // Unencoded modes
+
+ if (T_BYTES(dwInput) == 0) {
+
+ switch (T_COLORSPACE(dwInput)) {
+
+ case PT_Lab:
+ FromInput = UnrollLabDouble;
+ break;
+ case PT_XYZ:
+ FromInput = UnrollXYZDouble;
+ break;
+
+ // 0.0 .. 1.0 range
+
+ case PT_GRAY:
+ case PT_RGB:
+ case PT_YCbCr:
+ case PT_YUV:
+ case PT_YUVK:
+ case PT_HSV:
+ case PT_HLS:
+ case PT_Yxy:
+ if (T_CHANNELS(dwInput) == 1)
+ FromInput = UnrollDouble1Chan;
+ else
+ FromInput = UnrollDouble;
+ break;
+
+ // Inks (%) 0.0 .. 100.0
+
+ default:
+ FromInput = UnrollInkDouble;
+ break;
+ }
+
+ }
+ else {
+
+ if (T_PLANAR(dwInput)) {
+
+ switch (T_BYTES(dwInput)) {
+
+ case 1:
+ FromInput = UnrollPlanarBytes;
+ break;
+
+ case 2:
+ if (T_ENDIAN16(dwInput))
+ FromInput = UnrollPlanarWordsBigEndian;
+ else
+ FromInput = UnrollPlanarWords;
+ break;
+
+ default:;
+ }
+ }
+ else {
+
+ switch (T_BYTES(dwInput)) {
+
+ case 1: // 1 byte per channel
+
+ switch (T_CHANNELS(dwInput) + T_EXTRA(dwInput)) {
+
+ case 1: if (T_FLAVOR(dwInput))
+ FromInput = Unroll1ByteReversed;
+ else
+ FromInput = Unroll1Byte;
+ break;
+
+ case 2: if (T_SWAPFIRST(dwInput))
+ FromInput = Unroll2ByteSwapFirst;
+ else
+ FromInput = Unroll2Byte;
+ break;
+
+ case 3: if (T_DOSWAP(dwInput))
+ FromInput = Unroll3BytesSwap;
+ else {
+ if (T_EXTRA(dwInput) == 2)
+ FromInput = Unroll1ByteSkip2;
+ else
+ if (T_COLORSPACE(dwInput) == PT_Lab)
+ FromInput = Unroll3BytesLab;
+ else
+ FromInput = Unroll3Bytes;
+ }
+ break;
+ case 4:
+ // TODO: ALab8 must be fixed to match v2 encoding
+
+ if (T_DOSWAP(dwInput)) {
+ if (T_SWAPFIRST(dwInput))
+
+ FromInput = Unroll4BytesSwapSwapFirst;
+ else
+ FromInput = Unroll4BytesSwap;
+ }
+ else {
+ if (T_SWAPFIRST(dwInput))
+ FromInput = Unroll4BytesSwapFirst;
+ else {
+ if (T_FLAVOR(dwInput))
+ FromInput = Unroll4BytesReverse;
+ else
+ FromInput = Unroll4Bytes;
+ }
+ }
+ break;
+
+
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ if (!T_DOSWAP(dwInput) && !T_SWAPFIRST(dwInput))
+ FromInput = UnrollAnyBytes;
+ break;
+
+
+ default:;
+ }
+ break;
+
+
+ case 2: // 1 word per channel
+
+ switch (T_CHANNELS(dwInput) + T_EXTRA(dwInput))
+ {
+ case 1: if (T_ENDIAN16(dwInput))
+ FromInput = Unroll1WordBigEndian;
+ else
+ if (T_FLAVOR(dwInput))
+ FromInput = Unroll1WordReversed;
+ else
+ FromInput = Unroll1Word;
+ break;
+
+ case 2: if (T_ENDIAN16(dwInput))
+ FromInput = Unroll2WordBigEndian;
+ else {
+ if (T_SWAPFIRST(dwInput))
+ FromInput = Unroll2WordSwapFirst;
+ else
+ FromInput = Unroll2Word;
+ }
+ break;
+
+ case 3: if (T_DOSWAP(dwInput)) {
+ if (T_ENDIAN16(dwInput))
+ FromInput = Unroll3WordsSwapBigEndian;
+ else
+ FromInput = Unroll3WordsSwap;
+ }
+ else {
+ if (T_ENDIAN16(dwInput))
+ FromInput = Unroll3WordsBigEndian;
+ else
+ FromInput = Unroll3Words;
+ }
+ break;
+
+ case 4: if (T_DOSWAP(dwInput)) {
+
+ if (T_ENDIAN16(dwInput))
+ FromInput = Unroll4WordsSwapBigEndian;
+ else {
+
+ if (T_SWAPFIRST(dwInput))
+ FromInput = Unroll4WordsSwapSwapFirst;
+ else
+ FromInput = Unroll4WordsSwap;
+
+ }
+
+ }
+ else {
+
+ if (T_EXTRA(dwInput) == 3)
+ FromInput = Unroll1WordSkip3;
+ else
+
+ if (T_ENDIAN16(dwInput)) {
+
+ if (T_FLAVOR(dwInput))
+ FromInput = Unroll4WordsBigEndianReverse;
+ else
+ FromInput = Unroll4WordsBigEndian;
+ }
+ else {
+ if (T_SWAPFIRST(dwInput))
+ FromInput = Unroll4WordsSwapFirst;
+ else {
+ if (T_FLAVOR(dwInput))
+ FromInput = Unroll4WordsReverse;
+ else
+ FromInput = Unroll4Words;
+ }
+ }
+ }
+ break;
+
+
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ if (!T_DOSWAP(dwInput) && !T_SWAPFIRST(dwInput))
+ FromInput = UnrollAnyWords;
+ break;
+
+ }
+ break;
+
+ default:;
+ }
+ }
+ }
+
+
+ if (!FromInput)
+ cmsSignalError(LCMS_ERRC_ABORTED, "Unknown input format");
+
+ return FromInput;
+}
+
+// choose routine from Input identifier
+
+_cmsFIXFN _cmsIdentifyOutputFormat(_LPcmsTRANSFORM xform, DWORD dwOutput)
+{
+ _cmsFIXFN ToOutput = NULL;
+
+
+ if (T_BYTES(dwOutput) == 0) {
+
+ switch (T_COLORSPACE(dwOutput)) {
+
+ case PT_Lab:
+ ToOutput = PackLabDouble;
+ break;
+ case PT_XYZ:
+ ToOutput = PackXYZDouble;
+ break;
+
+ // 0.0 .. 1.0 range
+ case PT_GRAY:
+ case PT_RGB:
+ case PT_YCbCr:
+ case PT_YUV:
+ case PT_YUVK:
+ case PT_HSV:
+ case PT_HLS:
+ case PT_Yxy:
+ ToOutput = PackDouble;
+ break;
+
+ // Inks (%) 0.0 .. 100.0
+
+ default:
+ ToOutput = PackInkDouble;
+ break;
+ }
+
+ }
+ else
+
+ if (T_PLANAR(dwOutput)) {
+
+ switch (T_BYTES(dwOutput)) {
+
+ case 1: ToOutput = PackPlanarBytes;
+ break;
+
+ case 2:if (!T_ENDIAN16(dwOutput))
+ ToOutput = PackPlanarWords;
+ break;
+
+ default:;
+ }
+ }
+ else {
+
+ switch (T_BYTES(dwOutput)) {
+
+ case 1:
+ switch (T_CHANNELS(dwOutput))
+ {
+ case 1:
+ if (T_DITHER(dwOutput))
+ ToOutput = PackNBytesDither;
+ else
+ ToOutput = Pack1Byte;
+ if (T_EXTRA(dwOutput) == 1) {
+ if (T_SWAPFIRST(dwOutput))
+ ToOutput = Pack1ByteAndSkip1SwapFirst;
+ else
+ ToOutput = Pack1ByteAndSkip1;
+ }
+ break;
+
+ case 3:
+ switch (T_EXTRA(dwOutput)) {
+
+ case 0: if (T_DOSWAP(dwOutput))
+ ToOutput = Pack3BytesSwap;
+ else
+ if (T_COLORSPACE(dwOutput) == PT_Lab)
+ ToOutput = Pack3BytesLab;
+ else {
+ if (T_DITHER(dwOutput))
+ ToOutput = PackNBytesDither;
+ else
+ ToOutput = Pack3Bytes;
+ }
+ break;
+
+ case 1: // TODO: ALab8 should be handled here
+
+ if (T_DOSWAP(dwOutput)) {
+
+ if (T_SWAPFIRST(dwOutput))
+ ToOutput = Pack3BytesAndSkip1SwapSwapFirst;
+ else
+ ToOutput = Pack3BytesAndSkip1Swap;
+ }
+ else {
+ if (T_SWAPFIRST(dwOutput))
+ ToOutput = Pack3BytesAndSkip1SwapFirst;
+ else
+ ToOutput = Pack3BytesAndSkip1;
+ }
+ break;
+
+ default:;
+ }
+ break;
+
+ case 4: if (T_EXTRA(dwOutput) == 0) {
+
+
+ if (T_DOSWAP(dwOutput)) {
+
+
+ if (T_SWAPFIRST(dwOutput)) {
+ ToOutput = Pack4BytesSwapSwapFirst;
+ }
+ else {
+
+ if (T_DITHER(dwOutput)) {
+ ToOutput = PackNBytesSwapDither;
+ }
+ else {
+ ToOutput = Pack4BytesSwap;
+ }
+ }
+ }
+ else {
+ if (T_SWAPFIRST(dwOutput))
+ ToOutput = Pack4BytesSwapFirst;
+ else {
+
+ if (T_FLAVOR(dwOutput))
+ ToOutput = Pack4BytesReverse;
+ else {
+ if (T_DITHER(dwOutput))
+ ToOutput = PackNBytesDither;
+ else
+ ToOutput = Pack4Bytes;
+ }
+ }
+ }
+ }
+ else {
+ if (!T_DOSWAP(dwOutput) && !T_SWAPFIRST(dwOutput))
+ ToOutput = PackNBytes;
+ }
+ break;
+
+ // Hexachrome separations.
+ case 6: if (T_EXTRA(dwOutput) == 0) {
+
+ if( T_DOSWAP(dwOutput))
+ ToOutput = Pack6BytesSwap;
+ else
+ ToOutput = Pack6Bytes;
+ }
+ else {
+ if (!T_DOSWAP(dwOutput) && !T_SWAPFIRST(dwOutput))
+ ToOutput = PackNBytes;
+
+ }
+ break;
+
+ case 2:
+ case 5:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+
+ if ((T_EXTRA(dwOutput) == 0) && (T_SWAPFIRST(dwOutput) == 0))
+ {
+ if (T_DOSWAP(dwOutput))
+ ToOutput = PackNBytesSwap;
+ else {
+
+ if (T_DITHER(dwOutput))
+ ToOutput = PackNBytesDither;
+ else
+ ToOutput = PackNBytes;
+ }
+ }
+ break;
+
+ default:;
+ }
+ break;
+
+
+ case 2:
+
+ switch (T_CHANNELS(dwOutput)) {
+
+ case 1:
+ if (T_ENDIAN16(dwOutput))
+
+ ToOutput = Pack1WordBigEndian;
+ else
+ ToOutput = Pack1Word;
+
+ if (T_EXTRA(dwOutput) == 1) {
+
+ if (T_ENDIAN16(dwOutput))
+
+ ToOutput = Pack1WordAndSkip1BigEndian;
+ else {
+ if (T_SWAPFIRST(dwOutput))
+ ToOutput = Pack1WordAndSkip1SwapFirst;
+ else
+ ToOutput = Pack1WordAndSkip1;
+ }
+ }
+ break;
+
+ case 3:
+
+ switch (T_EXTRA(dwOutput)) {
+
+ case 0:
+ if (T_DOSWAP(dwOutput)) {
+
+ if (T_ENDIAN16(dwOutput))
+
+ ToOutput = Pack3WordsSwapBigEndian;
+ else
+ ToOutput = Pack3WordsSwap;
+ }
+ else {
+ if (T_ENDIAN16(dwOutput))
+
+ ToOutput = Pack3WordsBigEndian;
+ else
+ ToOutput = Pack3Words;
+ }
+ break;
+
+ case 1: if (T_DOSWAP(dwOutput)) {
+
+ if (T_ENDIAN16(dwOutput))
+
+ ToOutput = Pack3WordsAndSkip1SwapBigEndian;
+ else {
+ if (T_SWAPFIRST(dwOutput))
+ ToOutput = Pack3WordsAndSkip1SwapSwapFirst;
+ else
+ ToOutput = Pack3WordsAndSkip1Swap;
+ }
+ }
+ else {
+ if (T_ENDIAN16(dwOutput))
+ ToOutput = Pack3WordsAndSkip1BigEndian;
+ else
+ ToOutput = Pack3WordsAndSkip1;
+ }
+ default:;
+ }
+ break;
+
+ case 4: if (T_EXTRA(dwOutput) == 0) {
+
+ if (T_DOSWAP(dwOutput)) {
+
+ if (T_ENDIAN16(dwOutput))
+ ToOutput = Pack4WordsSwapBigEndian;
+ else
+ ToOutput = Pack4WordsSwap;
+ }
+ else {
+
+ if (T_ENDIAN16(dwOutput)) {
+
+ if (T_FLAVOR(dwOutput))
+ ToOutput = Pack4WordsBigEndianReverse;
+ else
+ ToOutput = Pack4WordsBigEndian;
+ }
+ else {
+ if (T_FLAVOR(dwOutput))
+ ToOutput = Pack4WordsReverse;
+ else
+ ToOutput = Pack4Words;
+ }
+ }
+ }
+ else {
+ if (!T_DOSWAP(dwOutput) && !T_SWAPFIRST(dwOutput))
+ ToOutput = PackNWords;
+ }
+ break;
+
+ case 6: if (T_EXTRA(dwOutput) == 0) {
+
+ if (T_DOSWAP(dwOutput)) {
+
+ if (T_ENDIAN16(dwOutput))
+ ToOutput = Pack6WordsSwapBigEndian;
+ else
+ ToOutput = Pack6WordsSwap;
+ }
+ else {
+
+ if (T_ENDIAN16(dwOutput))
+ ToOutput = Pack6WordsBigEndian;
+ else
+ ToOutput = Pack6Words;
+ }
+ }
+ else {
+ if (!T_DOSWAP(dwOutput) && !T_SWAPFIRST(dwOutput))
+ ToOutput = PackNWords;
+ }
+ break;
+
+
+ case 2:
+ case 5:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15: if ((T_EXTRA(dwOutput) == 0) && (T_SWAPFIRST(dwOutput) == 0)) {
+
+ if (T_DOSWAP(dwOutput)) {
+
+ if (T_ENDIAN16(dwOutput))
+ ToOutput = PackNWordsSwapBigEndian;
+ else
+ ToOutput = PackNWordsSwap;
+ }
+ else {
+
+ if (T_ENDIAN16(dwOutput))
+ ToOutput = PackNWordsBigEndian;
+ else
+ ToOutput = PackNWords;
+ }
+ }
+ break;
+
+ default:;
+ }
+ break;
+
+ default:;
+ }
+ }
+
+ if (!ToOutput)
+ cmsSignalError(LCMS_ERRC_ABORTED, "Unknown output format");
+
+ return ToOutput;
+}
+
+// User formatters for (weird) cases not already included
+
+void LCMSEXPORT cmsSetUserFormatters(cmsHTRANSFORM hTransform, DWORD dwInput, cmsFORMATTER Input,
+ DWORD dwOutput, cmsFORMATTER Output)
+{
+ _LPcmsTRANSFORM xform = (_LPcmsTRANSFORM) (LPSTR) hTransform;
+
+ if (Input != NULL) {
+ xform ->FromInput = (_cmsFIXFN) Input;
+ xform ->InputFormat = dwInput;
+ }
+
+ if (Output != NULL) {
+ xform ->ToOutput = (_cmsFIXFN) Output;
+ xform ->OutputFormat = dwOutput;
+ }
+
+}
+
+void LCMSEXPORT cmsGetUserFormatters(cmsHTRANSFORM hTransform,
+ LPDWORD InputFormat, cmsFORMATTER* Input,
+ LPDWORD OutputFormat, cmsFORMATTER* Output)
+{
+ _LPcmsTRANSFORM xform = (_LPcmsTRANSFORM) (LPSTR) hTransform;
+
+ if (Input) *Input = (cmsFORMATTER) xform ->FromInput;
+ if (InputFormat) *InputFormat = xform -> InputFormat;
+ if (Output) *Output = (cmsFORMATTER) xform ->ToOutput;
+ if (OutputFormat) *OutputFormat = xform -> OutputFormat;
+}
+
+
+// Change format of yet existing transform. No colorspace checking is performed
+
+void LCMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform,
+ DWORD dwInputFormat,
+ DWORD dwOutputFormat)
+{
+
+ cmsSetUserFormatters(hTransform,
+ dwInputFormat,
+ (cmsFORMATTER) _cmsIdentifyInputFormat((_LPcmsTRANSFORM) hTransform, dwInputFormat),
+ dwOutputFormat,
+ (cmsFORMATTER) _cmsIdentifyOutputFormat((_LPcmsTRANSFORM) hTransform, dwOutputFormat));
+}
diff --git a/debian/lcms/lcms-1.19.dfsg2/src/cmspcs.c b/debian/lcms/lcms-1.19.dfsg2/src/cmspcs.c
new file mode 100755
index 00000000..28644182
--- /dev/null
+++ b/debian/lcms/lcms-1.19.dfsg2/src/cmspcs.c
@@ -0,0 +1,601 @@
+//
+// Little cms
+// Copyright (C) 1998-2007 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.
+
+// inter PCS conversions XYZ <-> CIE L* a* b*
+
+#include "lcms.h"
+
+/*
+
+
+ CIE 15:2004 CIELab is defined as:
+
+ L* = 116*f(Y/Yn) - 16 0 <= L* <= 100
+ a* = 500*[f(X/Xn) - f(Y/Yn)]
+ b* = 200*[f(Y/Yn) - f(Z/Zn)]
+
+ and
+
+ f(t) = t^(1/3) 1 >= t > (24/116)^3
+ (841/108)*t + (16/116) 0 <= t <= (24/116)^3
+
+
+ Reverse transform is:
+
+ X = Xn*[a* / 500 + (L* + 16) / 116] ^ 3 if (X/Xn) > (24/116)
+ = Xn*(a* / 500 + L* / 116) / 7.787 if (X/Xn) <= (24/116)
+
+
+
+ Following ICC. PCS in Lab is coded as:
+
+ 8 bit Lab PCS:
+
+ L* 0..100 into a 0..ff byte.
+ a* t + 128 range is -128.0 +127.0
+ b*
+
+ 16 bit Lab PCS:
+
+ L* 0..100 into a 0..ff00 word.
+ a* t + 128 range is -128.0 +127.9961
+ b*
+
+
+ We are always playing with 16 bits-data, so I will ignore the
+ 8-bits encoding scheme.
+
+
+Interchange Space Component Actual Range Encoded Range
+CIE XYZ X 0 -> 1.99997 0x0000 -> 0xffff
+CIE XYZ Y 0 -> 1.99997 0x0000 -> 0xffff
+CIE XYZ Z 0 -> 1.99997 0x0000 -> 0xffff
+
+Version 2,3
+-----------
+
+CIELAB (16 bit) L* 0 -> 100.0 0x0000 -> 0xff00
+CIELAB (16 bit) a* -128.0 -> +127.996 0x0000 -> 0x8000 -> 0xffff
+CIELAB (16 bit) b* -128.0 -> +127.996 0x0000 -> 0x8000 -> 0xffff
+
+
+Version 4
+---------
+
+CIELAB (16 bit) L* 0 -> 100.0 0x0000 -> 0xffff
+CIELAB (16 bit) a* -128.0 -> +127 0x0000 -> 0x8080 -> 0xffff
+CIELAB (16 bit) b* -128.0 -> +127 0x0000 -> 0x8080 -> 0xffff
+
+*/
+
+
+
+
+// On most modern computers, D > 4 M (i.e. a division takes more than 4
+// multiplications worth of time), so it is probably preferable to compute
+// a 24 bit result directly.
+
+// #define ITERATE 1
+
+static
+float CubeRoot(float x)
+{
+ float fr, r;
+ int ex, shx;
+
+ /* Argument reduction */
+ fr = (float) frexp(x, &ex); /* separate into mantissa and exponent */
+ shx = ex % 3;
+
+ if (shx > 0)
+ shx -= 3; /* compute shx such that (ex - shx) is divisible by 3 */
+
+ ex = (ex - shx) / 3; /* exponent of cube root */
+ fr = (float) ldexp(fr, shx);
+
+ /* 0.125 <= fr < 1.0 */
+
+#ifdef ITERATE
+ /* Compute seed with a quadratic approximation */
+
+ fr = (-0.46946116F * fr + 1.072302F) * fr + 0.3812513F;/* 0.5<=fr<1 */
+ r = ldexp(fr, ex); /* 6 bits of precision */
+
+ /* Newton-Raphson iterations */
+
+ r = (float)(2.0/3.0) * r + (float)(1.0/3.0) * x / (r * r); /* 12 bits */
+ r = (float)(2.0/3.0) * r + (float)(1.0/3.0) * x / (r * r); /* 24 bits */
+#else /* ITERATE */
+
+ /* Use quartic rational polynomial with error < 2^(-24) */
+
+ fr = (float) (((((45.2548339756803022511987494 * fr +
+ 192.2798368355061050458134625) * fr +
+ 119.1654824285581628956914143) * fr +
+ 13.43250139086239872172837314) * fr +
+ 0.1636161226585754240958355063)
+ /
+ ((((14.80884093219134573786480845 * fr +
+ 151.9714051044435648658557668) * fr +
+ 168.5254414101568283957668343) * fr +
+ 33.9905941350215598754191872) * fr +
+ 1.0));
+ r = (float) ldexp(fr, ex); /* 24 bits of precision */
+#endif
+ return r;
+}
+
+static
+double f(double t)
+{
+
+ const double Limit = (24.0/116.0) * (24.0/116.0) * (24.0/116.0);
+
+ if (t <= Limit)
+ return (841.0/108.0) * t + (16.0/116.0);
+ else
+ return CubeRoot((float) t);
+}
+
+
+static
+double f_1(double t)
+{
+ const double Limit = (24.0/116.0);
+
+ if (t <= Limit)
+ {
+ double tmp;
+
+ tmp = (108.0/841.0) * (t - (16.0/116.0));
+ if (tmp <= 0.0) return 0.0;
+ else return tmp;
+ }
+
+ return t * t * t;
+}
+
+
+
+void LCMSEXPORT cmsXYZ2Lab(LPcmsCIEXYZ WhitePoint, LPcmsCIELab Lab, const cmsCIEXYZ* xyz)
+{
+ double fx, fy, fz;
+
+ if (xyz -> X == 0 && xyz -> Y == 0 && xyz -> Z == 0)
+ {
+ Lab -> L = 0;
+ Lab -> a = 0;
+ Lab -> b = 0;
+ return;
+ }
+
+ if (WhitePoint == NULL)
+ WhitePoint = cmsD50_XYZ();
+
+ fx = f(xyz->X / WhitePoint->X);
+ fy = f(xyz->Y / WhitePoint->Y);
+ fz = f(xyz->Z / WhitePoint->Z);
+
+ Lab->L = 116.0* fy - 16.;
+
+ Lab->a = 500.0*(fx - fy);
+ Lab->b = 200.0*(fy - fz);
+}
+
+
+
+void cmsXYZ2LabEncoded(WORD XYZ[3], WORD Lab[3])
+{
+ Fixed32 X, Y, Z;
+ double x, y, z, L, a, b;
+ double fx, fy, fz;
+ Fixed32 wL, wa, wb;
+
+ X = (Fixed32) XYZ[0] << 1;
+ Y = (Fixed32) XYZ[1] << 1;
+ Z = (Fixed32) XYZ[2] << 1;
+
+
+ if (X==0 && Y==0 && Z==0) {
+
+ Lab[0] = 0;
+ Lab[1] = Lab[2] = 0x8000;
+ return;
+ }
+
+ // PCS is in D50
+
+
+ x = FIXED_TO_DOUBLE(X) / D50X;
+ y = FIXED_TO_DOUBLE(Y) / D50Y;
+ z = FIXED_TO_DOUBLE(Z) / D50Z;
+
+
+ fx = f(x);
+ fy = f(y);
+ fz = f(z);
+
+ L = 116.* fy - 16.;
+
+ a = 500.*(fx - fy);
+ b = 200.*(fy - fz);
+
+ a += 128.;
+ b += 128.;
+
+ wL = (int) (L * 652.800 + .5);
+ wa = (int) (a * 256.0 + .5);
+ wb = (int) (b * 256.0 + .5);
+
+
+ Lab[0] = Clamp_L(wL);
+ Lab[1] = Clamp_ab(wa);
+ Lab[2] = Clamp_ab(wb);
+
+
+}
+
+
+
+
+
+
+void LCMSEXPORT cmsLab2XYZ(LPcmsCIEXYZ WhitePoint, LPcmsCIEXYZ xyz, const cmsCIELab* Lab)
+{
+ double x, y, z;
+
+ if (Lab -> L <= 0) {
+ xyz -> X = 0;
+ xyz -> Y = 0;
+ xyz -> Z = 0;
+ return;
+ }
+
+
+ if (WhitePoint == NULL)
+ WhitePoint = cmsD50_XYZ();
+
+ y = (Lab-> L + 16.0) / 116.0;
+ x = y + 0.002 * Lab -> a;
+ z = y - 0.005 * Lab -> b;
+
+ xyz -> X = f_1(x) * WhitePoint -> X;
+ xyz -> Y = f_1(y) * WhitePoint -> Y;
+ xyz -> Z = f_1(z) * WhitePoint -> Z;
+
+}
+
+
+
+void cmsLab2XYZEncoded(WORD Lab[3], WORD XYZ[3])
+{
+ double L, a, b;
+ double X, Y, Z, x, y, z;
+
+
+ L = ((double) Lab[0] * 100.0) / 65280.0;
+ if (L==0.0) {
+
+ XYZ[0] = 0; XYZ[1] = 0; XYZ[2] = 0;
+ return;
+ }
+
+ a = ((double) Lab[1] / 256.0) - 128.0;
+ b = ((double) Lab[2] / 256.0) - 128.0;
+
+ y = (L + 16.) / 116.0;
+ x = y + 0.002 * a;
+ z = y - 0.005 * b;
+
+ X = f_1(x) * D50X;
+ Y = f_1(y) * D50Y;
+ Z = f_1(z) * D50Z;
+
+ // Convert to 1.15 fixed format PCS
+
+
+ XYZ[0] = _cmsClampWord((int) floor(X * 32768.0 + 0.5));
+ XYZ[1] = _cmsClampWord((int) floor(Y * 32768.0 + 0.5));
+ XYZ[2] = _cmsClampWord((int) floor(Z * 32768.0 + 0.5));
+
+
+}
+
+static
+double L2float3(WORD v)
+{
+ Fixed32 fix32;
+
+ fix32 = (Fixed32) v;
+ return (double) fix32 / 652.800;
+}
+
+
+// the a/b part
+
+static
+double ab2float3(WORD v)
+{
+ Fixed32 fix32;
+
+ fix32 = (Fixed32) v;
+ return ((double) fix32/256.0)-128.0;
+}
+
+static
+WORD L2Fix3(double L)
+{
+ return (WORD) (L * 652.800 + 0.5);
+}
+
+static
+WORD ab2Fix3(double ab)
+{
+ return (WORD) ((ab + 128.0) * 256.0 + 0.5);
+}
+
+
+// ICC 4.0 -- ICC has changed PCS Lab encoding.
+
+static
+WORD L2Fix4(double L)
+{
+ return (WORD) (L * 655.35 + 0.5);
+}
+
+static
+WORD ab2Fix4(double ab)
+{
+ return (WORD) ((ab + 128.0) * 257.0 + 0.5);
+}
+
+static
+double L2float4(WORD v)
+{
+ Fixed32 fix32;
+
+ fix32 = (Fixed32) v;
+ return (double) fix32 / 655.35;
+}
+
+
+// the a/b part
+
+static
+double ab2float4(WORD v)
+{
+ Fixed32 fix32;
+
+ fix32 = (Fixed32) v;
+ return ((double) fix32/257.0)-128.0;
+}
+
+
+void LCMSEXPORT cmsLabEncoded2Float(LPcmsCIELab Lab, const WORD wLab[3])
+{
+ Lab->L = L2float3(wLab[0]);
+ Lab->a = ab2float3(wLab[1]);
+ Lab->b = ab2float3(wLab[2]);
+}
+
+
+void LCMSEXPORT cmsLabEncoded2Float4(LPcmsCIELab Lab, const WORD wLab[3])
+{
+ Lab->L = L2float4(wLab[0]);
+ Lab->a = ab2float4(wLab[1]);
+ Lab->b = ab2float4(wLab[2]);
+}
+
+static
+double Clamp_L_double(double L)
+{
+ if (L < 0) L = 0;
+ if (L > 100) L = 100;
+
+ return L;
+}
+
+
+static
+double Clamp_ab_double(double ab)
+{
+ if (ab < -128) ab = -128.0;
+ if (ab > +127.9961) ab = +127.9961;
+
+ return ab;
+}
+
+void LCMSEXPORT cmsFloat2LabEncoded(WORD wLab[3], const cmsCIELab* fLab)
+{
+ cmsCIELab Lab;
+
+
+ Lab.L = Clamp_L_double(fLab ->L);
+ Lab.a = Clamp_ab_double(fLab ->a);
+ Lab.b = Clamp_ab_double(fLab ->b);
+
+ wLab[0] = L2Fix3(Lab.L);
+ wLab[1] = ab2Fix3(Lab.a);
+ wLab[2] = ab2Fix3(Lab.b);
+}
+
+
+void LCMSEXPORT cmsFloat2LabEncoded4(WORD wLab[3], const cmsCIELab* fLab)
+{
+ cmsCIELab Lab;
+
+
+ Lab.L = fLab ->L;
+ Lab.a = fLab ->a;
+ Lab.b = fLab ->b;
+
+
+ if (Lab.L < 0) Lab.L = 0;
+ if (Lab.L > 100.) Lab.L = 100.;
+
+ if (Lab.a < -128.) Lab.a = -128.;
+ if (Lab.a > 127.) Lab.a = 127.;
+ if (Lab.b < -128.) Lab.b = -128.;
+ if (Lab.b > 127.) Lab.b = 127.;
+
+
+ wLab[0] = L2Fix4(Lab.L);
+ wLab[1] = ab2Fix4(Lab.a);
+ wLab[2] = ab2Fix4(Lab.b);
+}
+
+
+
+
+void LCMSEXPORT cmsLab2LCh(LPcmsCIELCh LCh, const cmsCIELab* Lab)
+{
+ double a, b;
+
+ LCh -> L = Clamp_L_double(Lab -> L);
+
+ a = Clamp_ab_double(Lab -> a);
+ b = Clamp_ab_double(Lab -> b);
+
+ LCh -> C = pow(a * a + b * b, 0.5);
+
+ if (a == 0 && b == 0)
+ LCh -> h = 0;
+ else
+ LCh -> h = atan2(b, a);
+
+
+ LCh -> h *= (180. / M_PI);
+
+
+ while (LCh -> h >= 360.) // Not necessary, but included as a check.
+ LCh -> h -= 360.;
+
+ while (LCh -> h < 0)
+ LCh -> h += 360.;
+
+}
+
+
+
+
+void LCMSEXPORT cmsLCh2Lab(LPcmsCIELab Lab, const cmsCIELCh* LCh)
+{
+
+ double h = (LCh -> h * M_PI) / 180.0;
+
+ Lab -> L = Clamp_L_double(LCh -> L);
+ Lab -> a = Clamp_ab_double(LCh -> C * cos(h));
+ Lab -> b = Clamp_ab_double(LCh -> C * sin(h));
+
+}
+
+
+
+
+
+// In XYZ All 3 components are encoded using 1.15 fixed point
+
+static
+WORD XYZ2Fix(double d)
+{
+ return (WORD) floor(d * 32768.0 + 0.5);
+}
+
+
+void LCMSEXPORT cmsFloat2XYZEncoded(WORD XYZ[3], const cmsCIEXYZ* fXYZ)
+{
+ cmsCIEXYZ xyz;
+
+ xyz.X = fXYZ -> X;
+ xyz.Y = fXYZ -> Y;
+ xyz.Z = fXYZ -> Z;
+
+
+ // Clamp to encodeable values.
+ // 1.99997 is reserved as out-of-gamut marker
+
+
+ if (xyz.Y <= 0) {
+
+ xyz.X = 0;
+ xyz.Y = 0;
+ xyz.Z = 0;
+ }
+
+
+ if (xyz.X > 1.99996)
+ xyz.X = 1.99996;
+
+ if (xyz.X < 0)
+ xyz.X = 0;
+
+ if (xyz.Y > 1.99996)
+ xyz.Y = 1.99996;
+
+ if (xyz.Y < 0)
+ xyz.Y = 0;
+
+
+ if (xyz.Z > 1.99996)
+ xyz.Z = 1.99996;
+
+ if (xyz.Z < 0)
+ xyz.Z = 0;
+
+
+
+ XYZ[0] = XYZ2Fix(xyz.X);
+ XYZ[1] = XYZ2Fix(xyz.Y);
+ XYZ[2] = XYZ2Fix(xyz.Z);
+
+}
+
+
+// To convert from Fixed 1.15 point to double
+
+static
+double XYZ2float(WORD v)
+{
+ Fixed32 fix32;
+
+ // From 1.15 to 15.16
+
+ fix32 = v << 1;
+
+ // From fixed 15.16 to double
+
+ return FIXED_TO_DOUBLE(fix32);
+}
+
+
+void LCMSEXPORT cmsXYZEncoded2Float(LPcmsCIEXYZ fXYZ, const WORD XYZ[3])
+{
+
+ fXYZ -> X = XYZ2float(XYZ[0]);
+ fXYZ -> Y = XYZ2float(XYZ[1]);
+ fXYZ -> Z = XYZ2float(XYZ[2]);
+
+}
+
+
+
+
diff --git a/debian/lcms/lcms-1.19.dfsg2/src/cmsps2.c b/debian/lcms/lcms-1.19.dfsg2/src/cmsps2.c
new file mode 100755
index 00000000..6c352a70
--- /dev/null
+++ b/debian/lcms/lcms-1.19.dfsg2/src/cmsps2.c
@@ -0,0 +1,1717 @@
+//
+// Little cms
+// Copyright (C) 1998-2007 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.
+
+
+// Postscript level 2 operators
+
+
+
+#include "lcms.h"
+#include <time.h>
+#include <stdarg.h>
+
+// PostScript ColorRenderingDictionary and ColorSpaceArray
+
+LCMSAPI DWORD LCMSEXPORT cmsGetPostScriptCSA(cmsHPROFILE hProfile, int Intent, LPVOID Buffer, DWORD dwBufferLen);
+LCMSAPI DWORD LCMSEXPORT cmsGetPostScriptCRD(cmsHPROFILE hProfile, int Intent, LPVOID Buffer, DWORD dwBufferLen);
+LCMSAPI DWORD LCMSEXPORT cmsGetPostScriptCRDEx(cmsHPROFILE hProfile, int Intent, DWORD dwFlags, LPVOID Buffer, DWORD dwBufferLen);
+// -------------------------------------------------------------------- Implementation
+
+#define MAXPSCOLS 60 // Columns on tables
+
+/*
+ Implementation
+ --------------
+
+ PostScript does use XYZ as its internal PCS. But since PostScript
+ interpolation tables are limited to 8 bits, I use Lab as a way to
+ improve the accuracy, favoring perceptual results. So, for the creation
+ of each CRD, CSA the profiles are converted to Lab via a device
+ link between profile -> Lab or Lab -> profile. The PS code necessary to
+ convert Lab <-> XYZ is also included.
+
+
+
+ Color Space Arrays (CSA)
+ ==================================================================================
+
+ In order to obtain precission, code chooses between three ways to implement
+ the device -> XYZ transform. These cases identifies monochrome profiles (often
+ implemented as a set of curves), matrix-shaper and LUT-based.
+
+ Monochrome
+ -----------
+
+ This is implemented as /CIEBasedA CSA. The prelinearization curve is
+ placed into /DecodeA section, and matrix equals to D50. Since here is
+ no interpolation tables, I do the conversion directly to XYZ
+
+ NOTE: CLUT-based monochrome profiles are NOT supported. So, cmsFLAGS_MATRIXINPUT
+ flag is forced on such profiles.
+
+ [ /CIEBasedA
+ <<
+ /DecodeA { transfer function } bind
+ /MatrixA [D50]
+ /RangeLMN [ 0.0 D50X 0.0 D50Y 0.0 D50Z ]
+ /WhitePoint [D50]
+ /BlackPoint [BP]
+ /RenderingIntent (intent)
+ >>
+ ]
+
+ On simpler profiles, the PCS is already XYZ, so no conversion is required.
+
+
+ Matrix-shaper based
+ -------------------
+
+ This is implemented both with /CIEBasedABC or /CIEBasedDEF on dependig
+ of profile implementation. Since here is no interpolation tables, I do
+ the conversion directly to XYZ
+
+
+
+ [ /CIEBasedABC
+ <<
+ /DecodeABC [ {transfer1} {transfer2} {transfer3} ]
+ /MatrixABC [Matrix]
+ /RangeLMN [ 0.0 D50X 0.0 D50Y 0.0 D50Z ]
+ /DecodeLMN [ { / 2} dup dup ]
+ /WhitePoint [D50]
+ /BlackPoint [BP]
+ /RenderingIntent (intent)
+ >>
+ ]
+
+
+ CLUT based
+ ----------
+
+ Lab is used in such cases.
+
+ [ /CIEBasedDEF
+ <<
+ /DecodeDEF [ <prelinearization> ]
+ /Table [ p p p [<...>]]
+ /RangeABC [ 0 1 0 1 0 1]
+ /DecodeABC[ <postlinearization> ]
+ /RangeLMN [ -0.236 1.254 0 1 -0.635 1.640 ]
+ % -128/500 1+127/500 0 1 -127/200 1+128/200
+ /MatrixABC [ 1 1 1 1 0 0 0 0 -1]
+ /WhitePoint [D50]
+ /BlackPoint [BP]
+ /RenderingIntent (intent)
+ ]
+
+
+ Color Rendering Dictionaries (CRD)
+ ==================================
+ These are always implemented as CLUT, and always are using Lab. Since CRD are expected to
+ be used as resources, the code adds the definition as well.
+
+ <<
+ /ColorRenderingType 1
+ /WhitePoint [ D50 ]
+ /BlackPoint [BP]
+ /MatrixPQR [ Bradford ]
+ /RangePQR [-0.125 1.375 -0.125 1.375 -0.125 1.375 ]
+ /TransformPQR [
+ {4 index 3 get div 2 index 3 get mul exch pop exch pop exch pop exch pop } bind
+ {4 index 4 get div 2 index 4 get mul exch pop exch pop exch pop exch pop } bind
+ {4 index 5 get div 2 index 5 get mul exch pop exch pop exch pop exch pop } bind
+ ]
+ /MatrixABC <...>
+ /EncodeABC <...>
+ /RangeABC <.. used for XYZ -> Lab>
+ /EncodeLMN
+ /RenderTable [ p p p [<...>]]
+
+ /RenderingIntent (Perceptual)
+ >>
+ /Current exch /ColorRendering defineresource pop
+
+
+ The following stages are used to convert from XYZ to Lab
+ --------------------------------------------------------
+
+ Input is given at LMN stage on X, Y, Z
+
+ Encode LMN gives us f(X/Xn), f(Y/Yn), f(Z/Zn)
+
+ /EncodeLMN [
+
+ { 0.964200 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind
+ { 1.000000 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind
+ { 0.824900 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind
+
+ ]
+
+
+ MatrixABC is used to compute f(Y/Yn), f(X/Xn) - f(Y/Yn), f(Y/Yn) - f(Z/Zn)
+
+ | 0 1 0|
+ | 1 -1 0|
+ | 0 1 -1|
+
+ /MatrixABC [ 0 1 0 1 -1 1 0 0 -1 ]
+
+ EncodeABC finally gives Lab values.
+
+ /EncodeABC [
+ { 116 mul 16 sub 100 div } bind
+ { 500 mul 128 add 255 div } bind
+ { 200 mul 128 add 255 div } bind
+ ]
+
+ The following stages are used to convert Lab to XYZ
+ ----------------------------------------------------
+
+ /RangeABC [ 0 1 0 1 0 1]
+ /DecodeABC [ { 100 mul 16 add 116 div } bind
+ { 255 mul 128 sub 500 div } bind
+ { 255 mul 128 sub 200 div } bind
+ ]
+
+ /MatrixABC [ 1 1 1 1 0 0 0 0 -1]
+ /DecodeLMN [
+ {dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.964200 mul} bind
+ {dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse } bind
+ {dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.824900 mul} bind
+ ]
+
+
+*/
+
+/*
+
+ PostScript algorithms discussion.
+ =========================================================================================================
+
+ 1D interpolation algorithm
+
+
+ 1D interpolation (float)
+ ------------------------
+
+ val2 = Domain * Value;
+
+ cell0 = (int) floor(val2);
+ cell1 = (int) ceil(val2);
+
+ rest = val2 - cell0;
+
+ y0 = LutTable[cell0] ;
+ y1 = LutTable[cell1] ;
+
+ y = y0 + (y1 - y0) * rest;
+
+
+
+ PostScript code Stack
+ ================================================
+
+ { % v
+ <check 0..1.0>
+ [array] % v tab
+ dup % v tab tab
+ length 1 sub % v tab dom
+
+ 3 -1 roll % tab dom v
+
+ mul % tab val2
+ dup % tab val2 val2
+ dup % tab val2 val2 val2
+ floor cvi % tab val2 val2 cell0
+ exch % tab val2 cell0 val2
+ ceiling cvi % tab val2 cell0 cell1
+
+ 3 index % tab val2 cell0 cell1 tab
+ exch % tab val2 cell0 tab cell1
+ get % tab val2 cell0 y1
+
+ 4 -1 roll % val2 cell0 y1 tab
+ 3 -1 roll % val2 y1 tab cell0
+ get % val2 y1 y0
+
+ dup % val2 y1 y0 y0
+ 3 1 roll % val2 y0 y1 y0
+
+ sub % val2 y0 (y1-y0)
+ 3 -1 roll % y0 (y1-y0) val2
+ dup % y0 (y1-y0) val2 val2
+ floor cvi % y0 (y1-y0) val2 floor(val2)
+ sub % y0 (y1-y0) rest
+ mul % y0 t1
+ add % y
+ 65535 div % result
+
+ } bind
+
+
+*/
+
+static icTagSignature Device2PCSTab[] = {icSigAToB0Tag, // Perceptual
+ icSigAToB1Tag, // Relative colorimetric
+ icSigAToB2Tag, // Saturation
+ icSigAToB1Tag }; // Absolute colorimetric
+ // (Relative/WhitePoint)
+
+
+// --------------------------------------------------------------- Memory Stream
+//
+// This struct holds the memory block currently being write
+//
+
+typedef struct {
+ LPBYTE Block;
+ LPBYTE Ptr;
+ DWORD dwMax;
+ DWORD dwUsed;
+ int MaxCols;
+ int Col;
+ int HasError;
+
+ } MEMSTREAM, FAR* LPMEMSTREAM;
+
+
+typedef struct {
+ LPLUT Lut;
+ LPMEMSTREAM m;
+
+ int FirstComponent;
+ int SecondComponent;
+
+ int bps;
+ const char* PreMaj;
+ const char* PostMaj;
+ const char* PreMin;
+ const char* PostMin;
+
+ int lIsInput; // Handle L* encoding
+ int FixWhite; // Force mapping of pure white
+
+ icColorSpaceSignature ColorSpace; // ColorSpace of profile
+
+
+ } SAMPLERCARGO, FAR* LPSAMPLERCARGO;
+
+
+// Creates a ready to use memory stream
+static
+LPMEMSTREAM CreateMemStream(LPBYTE Buffer, DWORD dwMax, int MaxCols)
+{
+ LPMEMSTREAM m = (LPMEMSTREAM) _cmsMalloc(sizeof(MEMSTREAM));
+ if (m == NULL) return NULL;
+
+ ZeroMemory(m, sizeof(MEMSTREAM));
+
+ m -> Block = m -> Ptr = Buffer;
+ m -> dwMax = dwMax;
+ m -> dwUsed = 0;
+ m -> MaxCols = MaxCols;
+ m -> Col = 0;
+ m -> HasError = 0;
+
+ return m;
+}
+
+
+
+// Convert to byte
+static
+BYTE Word2Byte(WORD w)
+{
+ return (BYTE) floor((double) w / 257.0 + 0.5);
+}
+
+
+// Convert to byte (using ICC2 notation)
+
+static
+BYTE L2Byte(WORD w)
+{
+ int ww = w + 0x0080;
+
+ if (ww > 0xFFFF) return 0xFF;
+
+ return (BYTE) ((WORD) (ww >> 8) & 0xFF);
+}
+
+// Write a raw, uncooked byte. Check for space
+static
+void WriteRawByte(LPMEMSTREAM m, BYTE b)
+{
+ if (m -> dwUsed + 1 > m -> dwMax) {
+ m -> HasError = 1;
+ }
+
+ if (!m ->HasError && m ->Block) {
+ *m ->Ptr++ = b;
+ }
+
+ m -> dwUsed++;
+}
+
+// Write a cooked byte
+static
+void WriteByte(LPMEMSTREAM m, BYTE b)
+{
+ static const BYTE Hex[] = "0123456789ABCDEF";
+ BYTE c;
+
+ c = Hex[(b >> 4) & 0x0f];
+ WriteRawByte(m, c);
+
+ c = Hex[b & 0x0f];
+ WriteRawByte(m, c);
+
+ m -> Col += 2;
+
+ if (m -> Col > m -> MaxCols) {
+
+ WriteRawByte(m, '\n');
+ m -> Col = 0;
+ }
+
+}
+
+// Does write a formatted string. Guaranteed to be 2048 bytes at most.
+static
+void Writef(LPMEMSTREAM m, const char *frm, ...)
+{
+ va_list args;
+ LPBYTE pt;
+ BYTE Buffer[2048];
+
+ va_start(args, frm);
+
+ vsnprintf((char*) Buffer, 2048, frm, args);
+
+ for (pt = Buffer; *pt; pt++) {
+
+ WriteRawByte(m, *pt);
+ }
+
+ va_end(args);
+}
+
+
+
+// ----------------------------------------------------------------- PostScript generation
+
+
+// Removes offending Carriage returns
+static
+char* RemoveCR(const char* txt)
+{
+ static char Buffer[2048];
+ char* pt;
+
+ strncpy(Buffer, txt, 2047);
+ Buffer[2047] = 0;
+ for (pt = Buffer; *pt; pt++)
+ if (*pt == '\n' || *pt == '\r') *pt = ' ';
+
+ return Buffer;
+
+}
+
+static
+void EmitHeader(LPMEMSTREAM m, const char* Title, cmsHPROFILE hProfile)
+{
+
+ time_t timer;
+
+ time(&timer);
+
+ Writef(m, "%%!PS-Adobe-3.0\n");
+ Writef(m, "%%\n");
+ Writef(m, "%% %s\n", Title);
+ Writef(m, "%% Source: %s\n", RemoveCR(cmsTakeProductName(hProfile)));
+ Writef(m, "%% Description: %s\n", RemoveCR(cmsTakeProductDesc(hProfile)));
+ Writef(m, "%% Created: %s", ctime(&timer)); // ctime appends a \n!!!
+ Writef(m, "%%\n");
+ Writef(m, "%%%%BeginResource\n");
+
+}
+
+
+// Emits White & Black point. White point is always D50, Black point is the device
+// Black point adapted to D50.
+
+static
+void EmitWhiteBlackD50(LPMEMSTREAM m, LPcmsCIEXYZ BlackPoint)
+{
+
+ Writef(m, "/BlackPoint [%f %f %f]\n", BlackPoint -> X,
+ BlackPoint -> Y,
+ BlackPoint -> Z);
+
+ Writef(m, "/WhitePoint [%f %f %f]\n", cmsD50_XYZ()->X,
+ cmsD50_XYZ()->Y,
+ cmsD50_XYZ()->Z);
+}
+
+
+static
+void EmitRangeCheck(LPMEMSTREAM m)
+{
+ Writef(m, "dup 0.0 lt { pop 0.0 } if "
+ "dup 1.0 gt { pop 1.0 } if ");
+
+}
+
+// Does write the intent
+
+static
+void EmitIntent(LPMEMSTREAM m, int RenderingIntent)
+{
+ const char *intent;
+
+ switch (RenderingIntent) {
+
+ case INTENT_PERCEPTUAL: intent = "Perceptual"; break;
+ case INTENT_RELATIVE_COLORIMETRIC: intent = "RelativeColorimetric"; break;
+ case INTENT_ABSOLUTE_COLORIMETRIC: intent = "AbsoluteColorimetric"; break;
+ case INTENT_SATURATION: intent = "Saturation"; break;
+
+ default: intent = "Undefined"; break;
+ }
+
+ Writef(m, "/RenderingIntent (%s)\n", intent );
+}
+
+//
+// Convert L* to Y
+//
+// Y = Yn*[ (L* + 16) / 116] ^ 3 if (L*) >= 6 / 29
+// = Yn*( L* / 116) / 7.787 if (L*) < 6 / 29
+//
+
+/*
+static
+void EmitL2Y(LPMEMSTREAM m)
+{
+ Writef(m,
+ "{ "
+ "100 mul 16 add 116 div " // (L * 100 + 16) / 116
+ "dup 6 29 div ge " // >= 6 / 29 ?
+ "{ dup dup mul mul } " // yes, ^3 and done
+ "{ 4 29 div sub 108 841 div mul } " // no, slope limiting
+ "ifelse } bind ");
+}
+*/
+
+
+// Lab -> XYZ, see the discussion above
+
+static
+void EmitLab2XYZ(LPMEMSTREAM m)
+{
+ Writef(m, "/RangeABC [ 0 1 0 1 0 1]\n");
+ Writef(m, "/DecodeABC [\n");
+ Writef(m, "{100 mul 16 add 116 div } bind\n");
+ Writef(m, "{255 mul 128 sub 500 div } bind\n");
+ Writef(m, "{255 mul 128 sub 200 div } bind\n");
+ Writef(m, "]\n");
+ Writef(m, "/MatrixABC [ 1 1 1 1 0 0 0 0 -1]\n");
+ Writef(m, "/RangeLMN [ -0.236 1.254 0 1 -0.635 1.640 ]\n");
+ Writef(m, "/DecodeLMN [\n");
+ Writef(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.964200 mul} bind\n");
+ Writef(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse } bind\n");
+ Writef(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.824900 mul} bind\n");
+ Writef(m, "]\n");
+}
+
+
+
+// Outputs a table of words. It does use 16 bits
+
+static
+void Emit1Gamma(LPMEMSTREAM m, LPWORD Table, int nEntries)
+{
+ int i;
+ double gamma;
+
+
+ if (nEntries <= 0) return; // Empty table
+
+ // Suppress whole if identity
+ if (cmsIsLinear(Table, nEntries)) {
+ Writef(m, "{} ");
+ return;
+ }
+
+
+ // Check if is really an exponential. If so, emit "exp"
+ gamma = cmsEstimateGammaEx(Table, nEntries, 0.001);
+ if (gamma > 0) {
+ Writef(m, "{ %g exp } bind ", gamma);
+ return;
+ }
+
+ Writef(m, "{ ");
+
+ // Bounds check
+ EmitRangeCheck(m);
+
+ // Emit intepolation code
+
+ // PostScript code Stack
+ // =============== ========================
+ // v
+ Writef(m, " [");
+
+ // TODO: Check for endianess!!!
+
+ for (i=0; i < nEntries; i++) {
+ Writef(m, "%d ", Table[i]);
+ }
+
+ Writef(m, "] "); // v tab
+
+ Writef(m, "dup "); // v tab tab
+ Writef(m, "length 1 sub "); // v tab dom
+ Writef(m, "3 -1 roll "); // tab dom v
+ Writef(m, "mul "); // tab val2
+ Writef(m, "dup "); // tab val2 val2
+ Writef(m, "dup "); // tab val2 val2 val2
+ Writef(m, "floor cvi "); // tab val2 val2 cell0
+ Writef(m, "exch "); // tab val2 cell0 val2
+ Writef(m, "ceiling cvi "); // tab val2 cell0 cell1
+ Writef(m, "3 index "); // tab val2 cell0 cell1 tab
+ Writef(m, "exch "); // tab val2 cell0 tab cell1
+ Writef(m, "get "); // tab val2 cell0 y1
+ Writef(m, "4 -1 roll "); // val2 cell0 y1 tab
+ Writef(m, "3 -1 roll "); // val2 y1 tab cell0
+ Writef(m, "get "); // val2 y1 y0
+ Writef(m, "dup "); // val2 y1 y0 y0
+ Writef(m, "3 1 roll "); // val2 y0 y1 y0
+ Writef(m, "sub "); // val2 y0 (y1-y0)
+ Writef(m, "3 -1 roll "); // y0 (y1-y0) val2
+ Writef(m, "dup "); // y0 (y1-y0) val2 val2
+ Writef(m, "floor cvi "); // y0 (y1-y0) val2 floor(val2)
+ Writef(m, "sub "); // y0 (y1-y0) rest
+ Writef(m, "mul "); // y0 t1
+ Writef(m, "add "); // y
+ Writef(m, "65535 div "); // result
+
+ Writef(m, " } bind ");
+}
+
+
+// Compare gamma table
+
+static
+LCMSBOOL GammaTableEquals(LPWORD g1, LPWORD g2, int nEntries)
+{
+ return memcmp(g1, g2, nEntries* sizeof(WORD)) == 0;
+}
+
+
+// Does write a set of gamma curves
+
+static
+void EmitNGamma(LPMEMSTREAM m, int n, LPWORD g[], int nEntries)
+{
+ int i;
+
+ for( i=0; i < n; i++ )
+ {
+ if (i > 0 && GammaTableEquals(g[i-1], g[i], nEntries)) {
+
+ Writef(m, "dup ");
+ }
+ else {
+ Emit1Gamma(m, g[i], nEntries);
+ }
+ }
+
+}
+
+
+// Check whatever a profile has CLUT tables (only on input)
+
+static
+LCMSBOOL IsLUTbased(cmsHPROFILE hProfile, int Intent)
+{
+ icTagSignature Tag;
+
+ // Check if adequate tag is present
+ Tag = Device2PCSTab[Intent];
+
+ if (cmsIsTag(hProfile, Tag)) return 1;
+
+ // If not present, revert to default (perceptual)
+ Tag = icSigAToB0Tag;
+
+ // If no tag present, try matrix-shaper
+ return cmsIsTag(hProfile, Tag);
+}
+
+
+
+// Following code dumps a LUT onto memory stream
+
+
+// This is the sampler. Intended to work in SAMPLER_INSPECT mode,
+// that is, the callback will be called for each knot with
+//
+// In[] The grid location coordinates, normalized to 0..ffff
+// Out[] The LUT values, normalized to 0..ffff
+//
+// Returning a value other than 0 does terminate the sampling process
+//
+// Each row contains LUT values for all but first component. So, I
+// detect row changing by keeping a copy of last value of first
+// component. -1 is used to mark begining of whole block.
+
+static
+int OutputValueSampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
+{
+ LPSAMPLERCARGO sc = (LPSAMPLERCARGO) Cargo;
+ unsigned int i;
+
+
+ if (sc -> FixWhite) {
+
+ if (In[0] == 0xFFFF) { // Only in L* = 100, ab = [-8..8]
+
+ if ((In[1] >= 0x7800 && In[1] <= 0x8800) &&
+ (In[2] >= 0x7800 && In[2] <= 0x8800)) {
+
+ WORD* Black;
+ WORD* White;
+ int nOutputs;
+
+ if (!_cmsEndPointsBySpace(sc ->ColorSpace, &White, &Black, &nOutputs))
+ return 0;
+
+ for (i=0; i < (unsigned int) nOutputs; i++)
+ Out[i] = White[i];
+ }
+
+
+ }
+ }
+
+
+ // Hadle the parenthesis on rows
+
+ if (In[0] != sc ->FirstComponent) {
+
+ if (sc ->FirstComponent != -1) {
+
+ Writef(sc ->m, sc ->PostMin);
+ sc ->SecondComponent = -1;
+ Writef(sc ->m, sc ->PostMaj);
+ }
+
+ // Begin block
+ sc->m->Col = 0;
+
+ Writef(sc ->m, sc ->PreMaj);
+ sc ->FirstComponent = In[0];
+ }
+
+
+ if (In[1] != sc ->SecondComponent) {
+
+ if (sc ->SecondComponent != -1) {
+
+ Writef(sc ->m, sc ->PostMin);
+ }
+
+ Writef(sc ->m, sc ->PreMin);
+ sc ->SecondComponent = In[1];
+ }
+
+
+
+ // Dump table. Could be Word or byte based on
+ // depending on bps member (16 bps mode is not currently
+ // being used at all, but is here for future ampliations)
+
+ for (i=0; i < sc -> Lut ->OutputChan; i++) {
+
+ WORD wWordOut = Out[i];
+
+ if (sc ->bps == 8) {
+
+ // Value as byte
+ BYTE wByteOut;
+
+ // If is input, convert from Lab2 to Lab4 (just divide by 256)
+
+ if (sc ->lIsInput) {
+
+
+ wByteOut = L2Byte(wWordOut);
+ }
+ else
+ wByteOut = Word2Byte(wWordOut);
+
+ WriteByte(sc -> m, wByteOut);
+ }
+ else {
+
+ // Value as word
+ WriteByte(sc -> m, (BYTE) (wWordOut & 0xFF));
+ WriteByte(sc -> m, (BYTE) ((wWordOut >> 8) & 0xFF));
+ }
+ }
+
+ return 1;
+}
+
+// Writes a LUT on memstream. Could be 8 or 16 bits based
+
+static
+void WriteCLUT(LPMEMSTREAM m, LPLUT Lut, int bps, const char* PreMaj,
+ const char* PostMaj,
+ const char* PreMin,
+ const char* PostMin,
+ int lIsInput,
+ int FixWhite,
+ icColorSpaceSignature ColorSpace)
+{
+ unsigned int i;
+ SAMPLERCARGO sc;
+
+ sc.FirstComponent = -1;
+ sc.SecondComponent = -1;
+ sc.Lut = Lut;
+ sc.m = m;
+ sc.bps = bps;
+ sc.PreMaj = PreMaj;
+ sc.PostMaj= PostMaj;
+
+ sc.PreMin = PreMin;
+ sc.PostMin = PostMin;
+ sc.lIsInput = lIsInput;
+ sc.FixWhite = FixWhite;
+ sc.ColorSpace = ColorSpace;
+
+ Writef(m, "[");
+
+ for (i=0; i < Lut ->InputChan; i++)
+ Writef(m, " %d ", Lut ->cLutPoints);
+
+ Writef(m, " [\n");
+
+
+
+ cmsSample3DGrid(Lut, OutputValueSampler, (LPVOID) &sc, SAMPLER_INSPECT);
+
+
+ Writef(m, PostMin);
+ Writef(m, PostMaj);
+ Writef(m, "] ");
+
+
+
+}
+
+
+// Dumps CIEBasedA Color Space Array
+
+static
+int EmitCIEBasedA(LPMEMSTREAM m, LPWORD Tab, int nEntries, LPcmsCIEXYZ BlackPoint)
+{
+
+ Writef(m, "[ /CIEBasedA\n");
+ Writef(m, " <<\n");
+
+ Writef(m, "/DecodeA ");
+
+ Emit1Gamma(m,Tab, nEntries);
+
+ Writef(m, " \n");
+
+ Writef(m, "/MatrixA [ 0.9642 1.0000 0.8249 ]\n");
+ Writef(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n");
+
+ EmitWhiteBlackD50(m, BlackPoint);
+ EmitIntent(m, INTENT_PERCEPTUAL);
+
+ Writef(m, ">>\n");
+ Writef(m, "]\n");
+
+ return 1;
+}
+
+
+// Dumps CIEBasedABC Color Space Array
+
+static
+int EmitCIEBasedABC(LPMEMSTREAM m, LPWORD L[], int nEntries, LPWMAT3 Matrix, LPcmsCIEXYZ BlackPoint)
+{
+ int i;
+
+ Writef(m, "[ /CIEBasedABC\n");
+ Writef(m, "<<\n");
+ Writef(m, "/DecodeABC [ ");
+
+ EmitNGamma(m, 3, L, nEntries);
+
+ Writef(m, "]\n");
+
+ Writef(m, "/MatrixABC [ " );
+
+ for( i=0; i < 3; i++ ) {
+
+ Writef(m, "%.6f %.6f %.6f ",
+ FIXED_TO_DOUBLE(Matrix->v[0].n[i]),
+ FIXED_TO_DOUBLE(Matrix->v[1].n[i]),
+ FIXED_TO_DOUBLE(Matrix->v[2].n[i]));
+ }
+
+
+ Writef(m, "]\n");
+
+ Writef(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n");
+
+ EmitWhiteBlackD50(m, BlackPoint);
+ EmitIntent(m, INTENT_PERCEPTUAL);
+
+ Writef(m, ">>\n");
+ Writef(m, "]\n");
+
+
+ return 1;
+}
+
+
+static
+int EmitCIEBasedDEF(LPMEMSTREAM m, LPLUT Lut, int Intent, LPcmsCIEXYZ BlackPoint)
+{
+ const char* PreMaj;
+ const char* PostMaj;
+ const char* PreMin, *PostMin;
+
+ switch (Lut ->InputChan) {
+ case 3:
+
+ Writef(m, "[ /CIEBasedDEF\n");
+ PreMaj ="<";
+ PostMaj= ">\n";
+ PreMin = PostMin = "";
+ break;
+ case 4:
+ Writef(m, "[ /CIEBasedDEFG\n");
+ PreMaj = "[";
+ PostMaj = "]\n";
+ PreMin = "<";
+ PostMin = ">\n";
+ break;
+ default:
+ return 0;
+
+ }
+
+ Writef(m, "<<\n");
+
+ if (Lut ->wFlags & LUT_HASTL1) {
+
+ Writef(m, "/DecodeDEF [ ");
+ EmitNGamma(m, Lut ->InputChan, Lut ->L1, Lut ->CLut16params.nSamples);
+ Writef(m, "]\n");
+ }
+
+
+
+ if (Lut ->wFlags & LUT_HAS3DGRID) {
+
+ Writef(m, "/Table ");
+ WriteCLUT(m, Lut, 8, PreMaj, PostMaj, PreMin, PostMin, TRUE, FALSE, (icColorSpaceSignature) 0);
+ Writef(m, "]\n");
+ }
+
+ EmitLab2XYZ(m);
+ EmitWhiteBlackD50(m, BlackPoint);
+ EmitIntent(m, Intent);
+
+ Writef(m, " >>\n");
+ Writef(m, "]\n");
+
+
+ return 1;
+}
+
+// Generates a curve from a gray profile
+
+static
+LPGAMMATABLE ExtractGray2Y(cmsHPROFILE hProfile, int Intent)
+{
+ LPGAMMATABLE Out = cmsAllocGamma(256);
+ cmsHPROFILE hXYZ = cmsCreateXYZProfile();
+ cmsHTRANSFORM xform = cmsCreateTransform(hProfile, TYPE_GRAY_8, hXYZ, TYPE_XYZ_DBL, Intent, cmsFLAGS_NOTPRECALC);
+ int i;
+
+ for (i=0; i < 256; i++) {
+
+ BYTE Gray = (BYTE) i;
+ cmsCIEXYZ XYZ;
+
+ cmsDoTransform(xform, &Gray, &XYZ, 1);
+
+ Out ->GammaTable[i] =_cmsClampWord((int) floor(XYZ.Y * 65535.0 + 0.5));
+ }
+
+ cmsDeleteTransform(xform);
+ cmsCloseProfile(hXYZ);
+ return Out;
+}
+
+
+
+// Because PostScrip has only 8 bits in /Table, we should use
+// a more perceptually uniform space... I do choose Lab.
+
+static
+int WriteInputLUT(LPMEMSTREAM m, cmsHPROFILE hProfile, int Intent)
+{
+ cmsHPROFILE hLab;
+ cmsHTRANSFORM xform;
+ icColorSpaceSignature ColorSpace;
+ int nChannels;
+ DWORD InputFormat;
+ int rc;
+ cmsHPROFILE Profiles[2];
+ cmsCIEXYZ BlackPointAdaptedToD50;
+
+ // Does create a device-link based transform.
+ // The DeviceLink is next dumped as working CSA.
+
+ hLab = cmsCreateLabProfile(NULL);
+ ColorSpace = cmsGetColorSpace(hProfile);
+ nChannels = _cmsChannelsOf(ColorSpace);
+ InputFormat = CHANNELS_SH(nChannels) | BYTES_SH(2);
+
+ cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent,LCMS_BPFLAGS_D50_ADAPTED);
+
+ // Is a devicelink profile?
+ if (cmsGetDeviceClass(hProfile) == icSigLinkClass) {
+
+ // if devicelink output already Lab, use it directly
+
+ if (cmsGetPCS(hProfile) == icSigLabData) {
+
+ xform = cmsCreateTransform(hProfile, InputFormat, NULL,
+ TYPE_Lab_DBL, Intent, 0);
+ }
+ else {
+
+ // Nope, adjust output to Lab if possible
+
+ Profiles[0] = hProfile;
+ Profiles[1] = hLab;
+
+ xform = cmsCreateMultiprofileTransform(Profiles, 2, InputFormat,
+ TYPE_Lab_DBL, Intent, 0);
+ }
+
+
+ }
+ else {
+
+ // This is a normal profile
+ xform = cmsCreateTransform(hProfile, InputFormat, hLab,
+ TYPE_Lab_DBL, Intent, 0);
+ }
+
+
+
+ if (xform == NULL) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "Cannot create transform Profile -> Lab");
+ return 0;
+ }
+
+ // Only 1, 3 and 4 channels are allowed
+
+ switch (nChannels) {
+
+ case 1: {
+ LPGAMMATABLE Gray2Y = ExtractGray2Y(hProfile, Intent);
+ EmitCIEBasedA(m, Gray2Y->GammaTable, Gray2Y ->nEntries, &BlackPointAdaptedToD50);
+ cmsFreeGamma(Gray2Y);
+ }
+ break;
+
+ case 3:
+ case 4: {
+ LPLUT DeviceLink;
+ _LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform;
+
+ if (v ->DeviceLink)
+ rc = EmitCIEBasedDEF(m, v->DeviceLink, Intent, &BlackPointAdaptedToD50);
+ else {
+ DeviceLink = _cmsPrecalculateDeviceLink(xform, 0);
+ rc = EmitCIEBasedDEF(m, DeviceLink, Intent, &BlackPointAdaptedToD50);
+ cmsFreeLUT(DeviceLink);
+ }
+ }
+ break;
+
+ default:
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "Only 3, 4 channels supported for CSA. This profile has %d channels.", nChannels);
+ return 0;
+ }
+
+
+ cmsDeleteTransform(xform);
+ cmsCloseProfile(hLab);
+ return 1;
+}
+
+
+
+// Does create CSA based on matrix-shaper. Allowed types are gray and RGB based
+
+static
+int WriteInputMatrixShaper(LPMEMSTREAM m, cmsHPROFILE hProfile)
+{
+ icColorSpaceSignature ColorSpace;
+ LPMATSHAPER MatShaper;
+ int rc;
+ cmsCIEXYZ BlackPointAdaptedToD50;
+
+
+ ColorSpace = cmsGetColorSpace(hProfile);
+ MatShaper = cmsBuildInputMatrixShaper(hProfile);
+
+ cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, INTENT_RELATIVE_COLORIMETRIC, LCMS_BPFLAGS_D50_ADAPTED);
+
+ if (MatShaper == NULL) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "This profile is not suitable for input");
+ return 0;
+ }
+
+ if (ColorSpace == icSigGrayData) {
+
+ rc = EmitCIEBasedA(m, MatShaper ->L[0],
+ MatShaper ->p16.nSamples,
+ &BlackPointAdaptedToD50);
+
+ }
+ else
+ if (ColorSpace == icSigRgbData) {
+
+
+ rc = EmitCIEBasedABC(m, MatShaper->L,
+ MatShaper ->p16.nSamples,
+ &MatShaper ->Matrix,
+ &BlackPointAdaptedToD50);
+ }
+ else {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "Profile is not suitable for CSA. Unsupported colorspace.");
+ return 0;
+ }
+
+ cmsFreeMatShaper(MatShaper);
+ return rc;
+}
+
+
+
+// Creates a PostScript color list from a named profile data.
+// This is a HP extension, and it works in Lab instead of XYZ
+
+static
+int WriteNamedColorCSA(LPMEMSTREAM m, cmsHPROFILE hNamedColor, int Intent)
+{
+ cmsHTRANSFORM xform;
+ cmsHPROFILE hLab;
+ int i, nColors;
+ char ColorName[32];
+
+
+ hLab = cmsCreateLabProfile(NULL);
+ xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX,
+ hLab, TYPE_Lab_DBL, Intent, cmsFLAGS_NOTPRECALC);
+ if (xform == NULL) return 0;
+
+
+ Writef(m, "<<\n");
+ Writef(m, "(colorlistcomment) (%s)\n", "Named color CSA");
+ Writef(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n");
+ Writef(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n");
+
+ nColors = cmsNamedColorCount(xform);
+
+
+ for (i=0; i < nColors; i++) {
+
+ WORD In[1];
+ cmsCIELab Lab;
+
+ In[0] = (WORD) i;
+
+ if (!cmsNamedColorInfo(xform, i, ColorName, NULL, NULL))
+ continue;
+
+ cmsDoTransform(xform, In, &Lab, 1);
+ Writef(m, " (%s) [ %.3f %.3f %.3f ]\n", ColorName, Lab.L, Lab.a, Lab.b);
+ }
+
+
+
+ Writef(m, ">>\n");
+
+ cmsDeleteTransform(xform);
+ cmsCloseProfile(hLab);
+ return 1;
+}
+
+
+// Does create a Color Space Array on XYZ colorspace for PostScript usage
+
+DWORD LCMSEXPORT cmsGetPostScriptCSA(cmsHPROFILE hProfile,
+ int Intent,
+ LPVOID Buffer, DWORD dwBufferLen)
+{
+
+ LPMEMSTREAM mem;
+ DWORD dwBytesUsed;
+
+ // Set up the serialization engine
+ mem = CreateMemStream((LPBYTE) Buffer, dwBufferLen, MAXPSCOLS);
+ if (!mem) return 0;
+
+
+ // Is a named color profile?
+ if (cmsGetDeviceClass(hProfile) == icSigNamedColorClass) {
+
+ if (!WriteNamedColorCSA(mem, hProfile, Intent)) {
+
+ _cmsFree((void*) mem);
+ return 0;
+ }
+ }
+ else {
+
+
+ // Any profile class are allowed (including devicelink), but
+ // output (PCS) colorspace must be XYZ or Lab
+ icColorSpaceSignature ColorSpace = cmsGetPCS(hProfile);
+
+ if (ColorSpace != icSigXYZData &&
+ ColorSpace != icSigLabData) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "Invalid output color space");
+ _cmsFree((void*) mem);
+ return 0;
+ }
+
+ // Is there any CLUT?
+ if (IsLUTbased(hProfile, Intent)) {
+
+ // Yes, so handle as LUT-based
+ if (!WriteInputLUT(mem, hProfile, Intent)) {
+
+ _cmsFree((void*) mem);
+ return 0;
+ }
+ }
+ else {
+
+ // No, try Matrix-shaper (this only works on XYZ)
+
+ if (!WriteInputMatrixShaper(mem, hProfile)) {
+
+ _cmsFree((void*) mem); // Something went wrong
+ return 0;
+ }
+ }
+ }
+
+
+ // Done, keep memory usage
+ dwBytesUsed = mem ->dwUsed;
+
+ // Get rid of memory stream
+ _cmsFree((void*) mem);
+
+ // Finally, return used byte count
+ return dwBytesUsed;
+}
+
+// ------------------------------------------------------ Color Rendering Dictionary (CRD)
+
+
+
+/*
+
+ Black point compensation plus chromatic adaptation:
+
+ Step 1 - Chromatic adaptation
+ =============================
+
+ WPout
+ X = ------- PQR
+ Wpin
+
+ Step 2 - Black point compensation
+ =================================
+
+ (WPout - BPout)*X - WPout*(BPin - BPout)
+ out = ---------------------------------------
+ WPout - BPin
+
+
+ Algorithm discussion
+ ====================
+
+ TransformPQR(WPin, BPin, WPout, BPout, PQR)
+
+ Wpin,etc= { Xws Yws Zws Pws Qws Rws }
+
+
+ Algorithm Stack 0...n
+ ===========================================================
+ PQR BPout WPout BPin WPin
+ 4 index 3 get WPin PQR BPout WPout BPin WPin
+ div (PQR/WPin) BPout WPout BPin WPin
+ 2 index 3 get WPout (PQR/WPin) BPout WPout BPin WPin
+ mult WPout*(PQR/WPin) BPout WPout BPin WPin
+
+ 2 index 3 get WPout WPout*(PQR/WPin) BPout WPout BPin WPin
+ 2 index 3 get BPout WPout WPout*(PQR/WPin) BPout WPout BPin WPin
+ sub (WPout-BPout) WPout*(PQR/WPin) BPout WPout BPin WPin
+ mult (WPout-BPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
+
+ 2 index 3 get WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
+ 4 index 3 get BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
+ 3 index 3 get BPout BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
+
+ sub (BPin-BPout) WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
+ mult (BPin-BPout)*WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin
+ sub (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
+
+ 3 index 3 get BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
+ 3 index 3 get WPout BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
+ exch
+ sub (WPout-BPin) (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin
+ div
+
+ exch pop
+ exch pop
+ exch pop
+ exch pop
+
+*/
+
+
+static
+void EmitPQRStage(LPMEMSTREAM m, cmsHPROFILE hProfile, int DoBPC, int lIsAbsolute)
+{
+
+
+ if (lIsAbsolute) {
+
+ // For absolute colorimetric intent, encode back to relative
+ // and generate a relative LUT
+
+ // Relative encoding is obtained across XYZpcs*(D50/WhitePoint)
+
+ cmsCIEXYZ White;
+
+ cmsTakeMediaWhitePoint(&White, hProfile);
+
+ Writef(m,"/MatrixPQR [1 0 0 0 1 0 0 0 1 ]\n");
+ Writef(m,"/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n");
+
+ Writef(m, "%% Absolute colorimetric -- encode to relative to maximize LUT usage\n"
+ "/TransformPQR [\n"
+ "{0.9642 mul %g div exch pop exch pop exch pop exch pop} bind\n"
+ "{1.0000 mul %g div exch pop exch pop exch pop exch pop} bind\n"
+ "{0.8249 mul %g div exch pop exch pop exch pop exch pop} bind\n]\n",
+ White.X, White.Y, White.Z);
+ return;
+ }
+
+
+ Writef(m,"%% Bradford Cone Space\n"
+ "/MatrixPQR [0.8951 -0.7502 0.0389 0.2664 1.7135 -0.0685 -0.1614 0.0367 1.0296 ] \n");
+
+ Writef(m, "/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n");
+
+
+ // No BPC
+
+ if (!DoBPC) {
+
+ Writef(m, "%% VonKries-like transform in Bradford Cone Space\n"
+ "/TransformPQR [\n"
+ "{exch pop exch 3 get mul exch pop exch 3 get div} bind\n"
+ "{exch pop exch 4 get mul exch pop exch 4 get div} bind\n"
+ "{exch pop exch 5 get mul exch pop exch 5 get div} bind\n]\n");
+ } else {
+
+ // BPC
+
+ Writef(m, "%% VonKries-like transform in Bradford Cone Space plus BPC\n"
+ "/TransformPQR [\n");
+
+ Writef(m, "{4 index 3 get div 2 index 3 get mul "
+ "2 index 3 get 2 index 3 get sub mul "
+ "2 index 3 get 4 index 3 get 3 index 3 get sub mul sub "
+ "3 index 3 get 3 index 3 get exch sub div "
+ "exch pop exch pop exch pop exch pop } bind\n");
+
+ Writef(m, "{4 index 4 get div 2 index 4 get mul "
+ "2 index 4 get 2 index 4 get sub mul "
+ "2 index 4 get 4 index 4 get 3 index 4 get sub mul sub "
+ "3 index 4 get 3 index 4 get exch sub div "
+ "exch pop exch pop exch pop exch pop } bind\n");
+
+ Writef(m, "{4 index 5 get div 2 index 5 get mul "
+ "2 index 5 get 2 index 5 get sub mul "
+ "2 index 5 get 4 index 5 get 3 index 5 get sub mul sub "
+ "3 index 5 get 3 index 5 get exch sub div "
+ "exch pop exch pop exch pop exch pop } bind\n]\n");
+
+ }
+
+
+}
+
+
+static
+void EmitXYZ2Lab(LPMEMSTREAM m)
+{
+ Writef(m, "/RangeLMN [ -0.635 2.0 0 2 -0.635 2.0 ]\n");
+ Writef(m, "/EncodeLMN [\n");
+ Writef(m, "{ 0.964200 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
+ Writef(m, "{ 1.000000 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
+ Writef(m, "{ 0.824900 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n");
+ Writef(m, "]\n");
+ Writef(m, "/MatrixABC [ 0 1 0 1 -1 1 0 0 -1 ]\n");
+ Writef(m, "/EncodeABC [\n");
+
+
+ Writef(m, "{ 116 mul 16 sub 100 div } bind\n");
+ Writef(m, "{ 500 mul 128 add 256 div } bind\n");
+ Writef(m, "{ 200 mul 128 add 256 div } bind\n");
+
+
+ Writef(m, "]\n");
+
+
+}
+
+// Due to impedance mismatch between XYZ and almost all RGB and CMYK spaces
+// I choose to dump LUTS in Lab instead of XYZ. There is still a lot of wasted
+// space on 3D CLUT, but since space seems not to be a problem here, 33 points
+// would give a reasonable accurancy. Note also that CRD tables must operate in
+// 8 bits.
+
+static
+int WriteOutputLUT(LPMEMSTREAM m, cmsHPROFILE hProfile, int Intent, DWORD dwFlags)
+{
+ cmsHPROFILE hLab;
+ cmsHTRANSFORM xform;
+ icColorSpaceSignature ColorSpace;
+ int i, nChannels;
+ DWORD OutputFormat;
+ _LPcmsTRANSFORM v;
+ LPLUT DeviceLink;
+ cmsHPROFILE Profiles[3];
+ cmsCIEXYZ BlackPointAdaptedToD50;
+ LCMSBOOL lFreeDeviceLink = FALSE;
+ LCMSBOOL lDoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION);
+ LCMSBOOL lFixWhite = !(dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP);
+ int RelativeEncodingIntent;
+
+
+
+ hLab = cmsCreateLabProfile(NULL);
+
+ ColorSpace = cmsGetColorSpace(hProfile);
+ nChannels = _cmsChannelsOf(ColorSpace);
+ OutputFormat = CHANNELS_SH(nChannels) | BYTES_SH(2);
+
+ // For absolute colorimetric, the LUT is encoded as relative
+ // in order to preserve precission.
+
+ RelativeEncodingIntent = Intent;
+ if (RelativeEncodingIntent == INTENT_ABSOLUTE_COLORIMETRIC)
+ RelativeEncodingIntent = INTENT_RELATIVE_COLORIMETRIC;
+
+
+ // Is a devicelink profile?
+ if (cmsGetDeviceClass(hProfile) == icSigLinkClass) {
+
+ // if devicelink input already in Lab
+
+ if (ColorSpace == icSigLabData) {
+
+ // adjust input to Lab to our v4
+
+ Profiles[0] = hLab;
+ Profiles[1] = hProfile;
+
+ xform = cmsCreateMultiprofileTransform(Profiles, 2, TYPE_Lab_DBL,
+ OutputFormat, RelativeEncodingIntent,
+ dwFlags|cmsFLAGS_NOWHITEONWHITEFIXUP|cmsFLAGS_NOPRELINEARIZATION);
+
+ }
+ else {
+ cmsSignalError(LCMS_ERRC_ABORTED, "Cannot use devicelink profile for CRD creation");
+ return 0;
+ }
+
+
+ }
+ else {
+
+ // This is a normal profile
+ xform = cmsCreateTransform(hLab, TYPE_Lab_DBL, hProfile,
+ OutputFormat, RelativeEncodingIntent, dwFlags|cmsFLAGS_NOWHITEONWHITEFIXUP|cmsFLAGS_NOPRELINEARIZATION);
+ }
+
+ if (xform == NULL) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "Cannot create transform Lab -> Profile in CRD creation");
+ return 0;
+ }
+
+ // Get the internal precalculated devicelink
+
+ v = (_LPcmsTRANSFORM) xform;
+ DeviceLink = v ->DeviceLink;
+
+ if (!DeviceLink) {
+
+ DeviceLink = _cmsPrecalculateDeviceLink(xform, cmsFLAGS_NOPRELINEARIZATION);
+ lFreeDeviceLink = TRUE;
+ }
+
+ Writef(m, "<<\n");
+ Writef(m, "/ColorRenderingType 1\n");
+
+
+ cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, LCMS_BPFLAGS_D50_ADAPTED);
+
+ // Emit headers, etc.
+ EmitWhiteBlackD50(m, &BlackPointAdaptedToD50);
+ EmitPQRStage(m, hProfile, lDoBPC, Intent == INTENT_ABSOLUTE_COLORIMETRIC);
+ EmitXYZ2Lab(m);
+
+ if (DeviceLink ->wFlags & LUT_HASTL1) {
+
+ // Shouldn't happen
+ cmsSignalError(LCMS_ERRC_ABORTED, "Internal error (prelinearization on CRD)");
+ return 0;
+ }
+
+
+ // FIXUP: map Lab (100, 0, 0) to perfect white, because the particular encoding for Lab
+ // does map a=b=0 not falling into any specific node. Since range a,b goes -128..127,
+ // zero is slightly moved towards right, so assure next node (in L=100 slice) is mapped to
+ // zero. This would sacrifice a bit of highlights, but failure to do so would cause
+ // scum dot. Ouch.
+
+ if (Intent == INTENT_ABSOLUTE_COLORIMETRIC)
+ lFixWhite = FALSE;
+
+ Writef(m, "/RenderTable ");
+
+ WriteCLUT(m, DeviceLink, 8, "<", ">\n", "", "", FALSE,
+ lFixWhite, ColorSpace);
+
+ Writef(m, " %d {} bind ", nChannels);
+
+ for (i=1; i < nChannels; i++)
+ Writef(m, "dup ");
+
+ Writef(m, "]\n");
+
+
+ EmitIntent(m, Intent);
+
+ Writef(m, ">>\n");
+
+ if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
+
+ Writef(m, "/Current exch /ColorRendering defineresource pop\n");
+ }
+
+ if (lFreeDeviceLink) cmsFreeLUT(DeviceLink);
+ cmsDeleteTransform(xform);
+ cmsCloseProfile(hLab);
+
+ return 1;
+}
+
+
+// Builds a ASCII string containing colorant list in 0..1.0 range
+static
+void BuildColorantList(char *Colorant, int nColorant, WORD Out[])
+{
+ char Buff[32];
+ int j;
+
+ Colorant[0] = 0;
+ if (nColorant > MAXCHANNELS)
+ nColorant = MAXCHANNELS;
+
+ for (j=0; j < nColorant; j++) {
+
+ sprintf(Buff, "%.3f", Out[j] / 65535.0);
+ strcat(Colorant, Buff);
+ if (j < nColorant -1)
+ strcat(Colorant, " ");
+
+ }
+}
+
+
+// Creates a PostScript color list from a named profile data.
+// This is a HP extension.
+
+static
+int WriteNamedColorCRD(LPMEMSTREAM m, cmsHPROFILE hNamedColor, int Intent, DWORD dwFlags)
+{
+ cmsHTRANSFORM xform;
+ int i, nColors, nColorant;
+ DWORD OutputFormat;
+ char ColorName[32];
+ char Colorant[128];
+
+ nColorant = _cmsChannelsOf(cmsGetColorSpace(hNamedColor));
+ OutputFormat = CHANNELS_SH(nColorant) | BYTES_SH(2);
+
+ xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX,
+ NULL, OutputFormat, Intent, cmsFLAGS_NOTPRECALC);
+ if (xform == NULL) return 0;
+
+
+ Writef(m, "<<\n");
+ Writef(m, "(colorlistcomment) (%s) \n", "Named profile");
+ Writef(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n");
+ Writef(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n");
+
+ nColors = cmsNamedColorCount(xform);
+
+
+ for (i=0; i < nColors; i++) {
+
+ WORD In[1];
+ WORD Out[MAXCHANNELS];
+
+ In[0] = (WORD) i;
+
+ if (!cmsNamedColorInfo(xform, i, ColorName, NULL, NULL))
+ continue;
+
+ cmsDoTransform(xform, In, Out, 1);
+ BuildColorantList(Colorant, nColorant, Out);
+ Writef(m, " (%s) [ %s ]\n", ColorName, Colorant);
+ }
+
+ Writef(m, " >>");
+
+ if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
+
+ Writef(m, " /Current exch /HPSpotTable defineresource pop\n");
+ }
+
+ cmsDeleteTransform(xform);
+ return 1;
+}
+
+
+
+// This one does create a Color Rendering Dictionary.
+// CRD are always LUT-Based, no matter if profile is
+// implemented as matrix-shaper.
+
+DWORD LCMSEXPORT cmsGetPostScriptCRDEx(cmsHPROFILE hProfile,
+ int Intent, DWORD dwFlags,
+ LPVOID Buffer, DWORD dwBufferLen)
+{
+
+ LPMEMSTREAM mem;
+ DWORD dwBytesUsed;
+
+ // Set up the serialization artifact
+ mem = CreateMemStream((LPBYTE) Buffer, dwBufferLen, MAXPSCOLS);
+ if (!mem) return 0;
+
+
+ if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
+
+ EmitHeader(mem, "Color Rendering Dictionary (CRD)", hProfile);
+ }
+
+
+ // Is a named color profile?
+ if (cmsGetDeviceClass(hProfile) == icSigNamedColorClass) {
+
+ if (!WriteNamedColorCRD(mem, hProfile, Intent, dwFlags)) {
+
+ _cmsFree((void*) mem);
+ return 0;
+ }
+ }
+ else {
+
+ // CRD are always implemented as LUT.
+
+
+ if (!WriteOutputLUT(mem, hProfile, Intent, dwFlags)) {
+ _cmsFree((void*) mem);
+ return 0;
+ }
+ }
+
+ if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) {
+
+ Writef(mem, "%%%%EndResource\n");
+ Writef(mem, "\n%% CRD End\n");
+ }
+
+ // Done, keep memory usage
+ dwBytesUsed = mem ->dwUsed;
+
+ // Get rid of memory stream
+ _cmsFree((void*) mem);
+
+ // Finally, return used byte count
+ return dwBytesUsed;
+}
+
+
+// For compatibility with previous versions
+
+DWORD LCMSEXPORT cmsGetPostScriptCRD(cmsHPROFILE hProfile,
+ int Intent,
+ LPVOID Buffer, DWORD dwBufferLen)
+{
+ return cmsGetPostScriptCRDEx(hProfile, Intent, 0, Buffer, dwBufferLen);
+}
diff --git a/debian/lcms/lcms-1.19.dfsg2/src/cmssamp.c b/debian/lcms/lcms-1.19.dfsg2/src/cmssamp.c
new file mode 100755
index 00000000..b6bfd855
--- /dev/null
+++ b/debian/lcms/lcms-1.19.dfsg2/src/cmssamp.c
@@ -0,0 +1,668 @@
+//
+// Little cms
+// Copyright (C) 1998-2007 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 "lcms.h"
+
+
+// ---------------------------------------------------------------------------------
+
+static volatile int GlobalBlackPreservationStrategy = 0;
+
+// Quantize a value 0 <= i < MaxSamples
+
+WORD _cmsQuantizeVal(double i, int MaxSamples)
+{
+ double x;
+
+ x = ((double) i * 65535.) / (double) (MaxSamples - 1);
+
+ return (WORD) floor(x + .5);
+}
+
+
+// Is a table linear?
+
+int cmsIsLinear(WORD Table[], int nEntries)
+{
+ register int i;
+ int diff;
+
+ for (i=0; i < nEntries; i++) {
+
+ diff = abs((int) Table[i] - (int) _cmsQuantizeVal(i, nEntries));
+ if (diff > 3)
+ return 0;
+ }
+
+ return 1;
+}
+
+
+
+// pow() restricted to integer
+
+static
+int ipow(int base, int exp)
+{
+ int res = base;
+
+ while (--exp)
+ res *= base;
+
+ return res;
+}
+
+
+// Given n, 0<=n<=clut^dim, returns the colorant.
+
+static
+int ComponentOf(int n, int clut, int nColorant)
+{
+ if (nColorant <= 0)
+ return (n % clut);
+
+ n /= ipow(clut, nColorant);
+
+ return (n % clut);
+}
+
+
+
+// This routine does a sweep on whole input space, and calls its callback
+// function on knots. returns TRUE if all ok, FALSE otherwise.
+
+LCMSBOOL LCMSEXPORT cmsSample3DGrid(LPLUT Lut, _cmsSAMPLER Sampler, LPVOID Cargo, DWORD dwFlags)
+{
+ int i, t, nTotalPoints, Colorant, index;
+ WORD In[MAXCHANNELS], Out[MAXCHANNELS];
+
+ nTotalPoints = ipow(Lut->cLutPoints, Lut -> InputChan);
+
+ index = 0;
+ for (i = 0; i < nTotalPoints; i++) {
+
+ for (t=0; t < (int) Lut -> InputChan; t++) {
+
+ Colorant = ComponentOf(i, Lut -> cLutPoints, (Lut -> InputChan - t - 1 ));
+ In[t] = _cmsQuantizeVal(Colorant, Lut -> cLutPoints);
+ }
+
+
+ if (dwFlags & SAMPLER_HASTL1) {
+
+ for (t=0; t < (int) Lut -> InputChan; t++)
+ In[t] = cmsReverseLinearInterpLUT16(In[t],
+ Lut -> L1[t],
+ &Lut -> In16params);
+ }
+
+ for (t=0; t < (int) Lut -> OutputChan; t++)
+ Out[t] = Lut->T[index + t];
+
+ if (dwFlags & SAMPLER_HASTL2) {
+
+ for (t=0; t < (int) Lut -> OutputChan; t++)
+ Out[t] = cmsLinearInterpLUT16(Out[t],
+ Lut -> L2[t],
+ &Lut -> Out16params);
+ }
+
+
+ if (!Sampler(In, Out, Cargo))
+ return FALSE;
+
+ if (!(dwFlags & SAMPLER_INSPECT)) {
+
+ if (dwFlags & SAMPLER_HASTL2) {
+
+ for (t=0; t < (int) Lut -> OutputChan; t++)
+ Out[t] = cmsReverseLinearInterpLUT16(Out[t],
+ Lut -> L2[t],
+ &Lut -> Out16params);
+ }
+
+
+ for (t=0; t < (int) Lut -> OutputChan; t++)
+ Lut->T[index + t] = Out[t];
+
+ }
+
+ index += Lut -> OutputChan;
+
+ }
+
+ return TRUE;
+}
+
+
+
+
+
+
+// choose reasonable resolution
+int _cmsReasonableGridpointsByColorspace(icColorSpaceSignature Colorspace, DWORD dwFlags)
+{
+ int nChannels;
+
+ // Already specified?
+ if (dwFlags & 0x00FF0000) {
+ // Yes, grab'em
+ return (dwFlags >> 16) & 0xFF;
+ }
+
+ nChannels = _cmsChannelsOf(Colorspace);
+
+ // HighResPrecalc is maximum resolution
+
+ if (dwFlags & cmsFLAGS_HIGHRESPRECALC) {
+
+ if (nChannels > 4)
+ return 7; // 7 for Hifi
+
+ if (nChannels == 4) // 23 for CMYK
+ return 23;
+
+ return 49; // 49 for RGB and others
+ }
+
+
+ // LowResPrecal is stripped resolution
+
+ if (dwFlags & cmsFLAGS_LOWRESPRECALC) {
+
+ if (nChannels > 4)
+ return 6; // 6 for Hifi
+
+ if (nChannels == 1)
+ return 33; // For monochrome
+
+ return 17; // 17 for remaining
+ }
+
+ // Default values
+
+ if (nChannels > 4)
+ return 7; // 7 for Hifi
+
+ if (nChannels == 4)
+ return 17; // 17 for CMYK
+
+ return 33; // 33 for RGB
+
+}
+
+// Sampler implemented by another transform. This is a clean way to
+// precalculate the devicelink 3D CLUT for almost any transform
+
+static
+int XFormSampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
+{
+ cmsDoTransform((cmsHTRANSFORM) Cargo, In, Out, 1);
+ return TRUE;
+}
+
+// This routine does compute the devicelink CLUT containing whole
+// transform. Handles any channel number.
+
+LPLUT _cmsPrecalculateDeviceLink(cmsHTRANSFORM h, DWORD dwFlags)
+{
+ _LPcmsTRANSFORM p = (_LPcmsTRANSFORM) h;
+ LPLUT Grid;
+ int nGridPoints;
+ DWORD dwFormatIn, dwFormatOut;
+ DWORD SaveFormatIn, SaveFormatOut;
+ int ChannelsIn, ChannelsOut;
+ LPLUT SaveGamutLUT;
+
+
+ // Remove any gamut checking
+ SaveGamutLUT = p ->Gamut;
+ p ->Gamut = NULL;
+
+ ChannelsIn = _cmsChannelsOf(p -> EntryColorSpace);
+ ChannelsOut = _cmsChannelsOf(p -> ExitColorSpace);
+
+ nGridPoints = _cmsReasonableGridpointsByColorspace(p -> EntryColorSpace, dwFlags);
+
+ Grid = cmsAllocLUT();
+ if (!Grid) return NULL;
+
+ Grid = cmsAlloc3DGrid(Grid, nGridPoints, ChannelsIn, ChannelsOut);
+
+ // Compute device link on 16-bit basis
+ dwFormatIn = (CHANNELS_SH(ChannelsIn)|BYTES_SH(2));
+ dwFormatOut = (CHANNELS_SH(ChannelsOut)|BYTES_SH(2));
+
+ SaveFormatIn = p ->InputFormat;
+ SaveFormatOut = p ->OutputFormat;
+
+ p -> InputFormat = dwFormatIn;
+ p -> OutputFormat = dwFormatOut;
+ p -> FromInput = _cmsIdentifyInputFormat(p, dwFormatIn);
+ p -> ToOutput = _cmsIdentifyOutputFormat(p, dwFormatOut);
+
+ // Fix gamut & gamma possible mismatches.
+
+ if (!(dwFlags & cmsFLAGS_NOPRELINEARIZATION)) {
+
+ cmsHTRANSFORM hOne[1];
+ hOne[0] = h;
+
+ _cmsComputePrelinearizationTablesFromXFORM(hOne, 1, Grid);
+ }
+
+ // Attention to this typecast! we can take the luxury to
+ // do this since cmsHTRANSFORM is only an alias to a pointer
+ // to the transform struct.
+
+ if (!cmsSample3DGrid(Grid, XFormSampler, (LPVOID) p, Grid -> wFlags)) {
+
+ cmsFreeLUT(Grid);
+ Grid = NULL;
+ }
+
+ p ->Gamut = SaveGamutLUT;
+ p ->InputFormat = SaveFormatIn;
+ p ->OutputFormat = SaveFormatOut;
+
+ return Grid;
+}
+
+
+
+// Sampler for Black-preserving CMYK->CMYK transforms
+
+typedef struct {
+ cmsHTRANSFORM cmyk2cmyk;
+ cmsHTRANSFORM cmyk2Lab;
+ LPGAMMATABLE KTone;
+ L16PARAMS KToneParams;
+ LPLUT LabK2cmyk;
+ double MaxError;
+
+ cmsHTRANSFORM hRoundTrip;
+ int MaxTAC;
+
+ cmsHTRANSFORM hProofOutput;
+
+ } BPCARGO, *LPBPCARGO;
+
+
+
+// Preserve black only if that is the only ink used
+static
+int BlackPreservingGrayOnlySampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
+{
+ BPCARGO* bp = (LPBPCARGO) Cargo;
+
+ // If going across black only, keep black only
+ if (In[0] == 0 && In[1] == 0 && In[2] == 0) {
+
+ // TAC does not apply because it is black ink!
+ Out[0] = Out[1] = Out[2] = 0;
+ Out[3] = cmsLinearInterpLUT16(In[3], bp->KTone ->GammaTable, &bp->KToneParams);
+ return 1;
+ }
+
+ // Keep normal transform for other colors
+ cmsDoTransform(bp ->cmyk2cmyk, In, Out, 1);
+ return 1;
+}
+
+
+
+// Preserve all K plane.
+static
+int BlackPreservingSampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
+{
+
+ WORD LabK[4];
+ double SumCMY, SumCMYK, Error;
+ cmsCIELab ColorimetricLab, BlackPreservingLab;
+ BPCARGO* bp = (LPBPCARGO) Cargo;
+
+ // Get the K across Tone curve
+ LabK[3] = cmsLinearInterpLUT16(In[3], bp->KTone ->GammaTable, &bp->KToneParams);
+
+ // If going across black only, keep black only
+ if (In[0] == 0 && In[1] == 0 && In[2] == 0) {
+
+ Out[0] = Out[1] = Out[2] = 0;
+ Out[3] = LabK[3];
+ return 1;
+ }
+
+ // Try the original transform, maybe K is already ok (valid on K=0)
+ cmsDoTransform(bp ->cmyk2cmyk, In, Out, 1);
+ if (Out[3] == LabK[3]) return 1;
+
+
+ // No, mesure and keep Lab measurement for further usage
+ cmsDoTransform(bp->hProofOutput, Out, &ColorimetricLab, 1);
+
+ // Is not black only and the transform doesn't keep black.
+ // Obtain the Lab of CMYK. After that we have Lab + K
+ cmsDoTransform(bp ->cmyk2Lab, In, LabK, 1);
+
+ // Obtain the corresponding CMY using reverse interpolation.
+ // As a seed, we use the colorimetric CMY
+ cmsEvalLUTreverse(bp ->LabK2cmyk, LabK, Out, Out);
+
+ // Estimate the error
+ cmsDoTransform(bp->hProofOutput, Out, &BlackPreservingLab, 1);
+ Error = cmsDeltaE(&ColorimetricLab, &BlackPreservingLab);
+
+
+ // Apply TAC if needed
+
+ SumCMY = Out[0] + Out[1] + Out[2];
+ SumCMYK = SumCMY + Out[3];
+
+ if (SumCMYK > bp ->MaxTAC) {
+
+ double Ratio = 1 - ((SumCMYK - bp->MaxTAC) / SumCMY);
+ if (Ratio < 0)
+ Ratio = 0;
+
+ Out[0] = (WORD) floor(Out[0] * Ratio + 0.5); // C
+ Out[1] = (WORD) floor(Out[1] * Ratio + 0.5); // M
+ Out[2] = (WORD) floor(Out[2] * Ratio + 0.5); // Y
+ }
+
+ return 1;
+}
+
+
+// Sample whole gamut to estimate maximum TAC
+
+#ifdef _MSC_VER
+#pragma warning(disable : 4100)
+#endif
+
+static
+int EstimateTAC(register WORD In[], register WORD Out[], register LPVOID Cargo)
+{
+ BPCARGO* bp = (LPBPCARGO) Cargo;
+ WORD RoundTrip[4];
+ int Sum;
+
+ cmsDoTransform(bp->hRoundTrip, In, RoundTrip, 1);
+
+ Sum = RoundTrip[0] + RoundTrip[1] + RoundTrip[2] + RoundTrip[3];
+
+ if (Sum > bp ->MaxTAC)
+ bp ->MaxTAC = Sum;
+
+ return 1;
+}
+
+
+// Estimate the maximum error
+static
+int BlackPreservingEstimateErrorSampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
+{
+ BPCARGO* bp = (LPBPCARGO) Cargo;
+ WORD ColorimetricOut[4];
+ cmsCIELab ColorimetricLab, BlackPreservingLab;
+ double Error;
+
+ if (In[0] == 0 && In[1] == 0 && In[2] == 0) return 1;
+
+ cmsDoTransform(bp->cmyk2cmyk, In, ColorimetricOut, 1);
+
+ cmsDoTransform(bp->hProofOutput, ColorimetricOut, &ColorimetricLab, 1);
+ cmsDoTransform(bp->hProofOutput, Out, &BlackPreservingLab, 1);
+
+ Error = cmsDeltaE(&ColorimetricLab, &BlackPreservingLab);
+
+ if (Error > bp ->MaxError)
+ bp ->MaxError = Error;
+
+ return 1;
+}
+
+// Setup the K preservation strategy
+int LCMSEXPORT cmsSetCMYKPreservationStrategy(int n)
+{
+ int OldVal = GlobalBlackPreservationStrategy;
+
+ if (n >= 0)
+ GlobalBlackPreservationStrategy = n;
+
+ return OldVal;
+}
+
+#pragma warning(disable: 4550)
+
+// Get a pointer to callback on depending of strategy
+static
+_cmsSAMPLER _cmsGetBlackPreservationSampler(void)
+{
+ switch (GlobalBlackPreservationStrategy) {
+
+ case 0: return BlackPreservingGrayOnlySampler;
+ default: return BlackPreservingSampler;
+ }
+
+}
+
+// This is the black-preserving devicelink generator
+LPLUT _cmsPrecalculateBlackPreservingDeviceLink(cmsHTRANSFORM hCMYK2CMYK, DWORD dwFlags)
+{
+ _LPcmsTRANSFORM p = (_LPcmsTRANSFORM) hCMYK2CMYK;
+ BPCARGO Cargo;
+ LPLUT Grid;
+ DWORD LocalFlags;
+ cmsHPROFILE hLab = cmsCreateLabProfile(NULL);
+ int nGridPoints;
+ icTagSignature Device2PCS[] = {icSigAToB0Tag, // Perceptual
+ icSigAToB1Tag, // Relative colorimetric
+ icSigAToB2Tag, // Saturation
+ icSigAToB1Tag }; // Absolute colorimetric
+ // (Relative/WhitePoint)
+
+ nGridPoints = _cmsReasonableGridpointsByColorspace(p -> EntryColorSpace, dwFlags);
+
+ // Get a copy of inteserting flags for this kind of xform
+ LocalFlags = cmsFLAGS_NOTPRECALC;
+ if (p -> dwOriginalFlags & cmsFLAGS_BLACKPOINTCOMPENSATION)
+ LocalFlags |= cmsFLAGS_BLACKPOINTCOMPENSATION;
+
+ // Fill in cargo struct
+ Cargo.cmyk2cmyk = hCMYK2CMYK;
+
+ // Compute tone curve.
+ Cargo.KTone = _cmsBuildKToneCurve(hCMYK2CMYK, 256);
+ if (Cargo.KTone == NULL) return NULL;
+ cmsCalcL16Params(Cargo.KTone ->nEntries, &Cargo.KToneParams);
+
+
+ // Create a CMYK->Lab "normal" transform on input, without K-preservation
+ Cargo.cmyk2Lab = cmsCreateTransform(p ->InputProfile, TYPE_CMYK_16,
+ hLab, TYPE_Lab_16, p->Intent, LocalFlags);
+
+ // We are going to use the reverse of proof direction
+ Cargo.LabK2cmyk = cmsReadICCLut(p->OutputProfile, Device2PCS[p->Intent]);
+
+ // Is there any table available?
+ if (Cargo.LabK2cmyk == NULL) {
+
+ Grid = NULL;
+ goto Cleanup;
+ }
+
+ // Setup a roundtrip on output profile for TAC estimation
+ Cargo.hRoundTrip = cmsCreateTransform(p ->OutputProfile, TYPE_CMYK_16,
+ p ->OutputProfile, TYPE_CMYK_16, p->Intent, cmsFLAGS_NOTPRECALC);
+
+
+ // Setup a proof CMYK->Lab on output
+ Cargo.hProofOutput = cmsCreateTransform(p ->OutputProfile, TYPE_CMYK_16,
+ hLab, TYPE_Lab_DBL, p->Intent, LocalFlags);
+
+
+ // Create an empty LUT for holding K-preserving xform
+ Grid = cmsAllocLUT();
+ if (!Grid) goto Cleanup;
+
+ Grid = cmsAlloc3DGrid(Grid, nGridPoints, 4, 4);
+
+ // Setup formatters
+ p -> FromInput = _cmsIdentifyInputFormat(p, TYPE_CMYK_16);
+ p -> ToOutput = _cmsIdentifyOutputFormat(p, TYPE_CMYK_16);
+
+
+
+ // Step #1, estimate TAC
+ Cargo.MaxTAC = 0;
+ if (!cmsSample3DGrid(Grid, EstimateTAC, (LPVOID) &Cargo, 0)) {
+
+ cmsFreeLUT(Grid);
+ Grid = NULL;
+ goto Cleanup;
+ }
+
+
+ // Step #2, compute approximation
+ if (!cmsSample3DGrid(Grid, _cmsGetBlackPreservationSampler(), (LPVOID) &Cargo, 0)) {
+
+ cmsFreeLUT(Grid);
+ Grid = NULL;
+ goto Cleanup;
+ }
+
+ // Step #3, estimate error
+ Cargo.MaxError = 0;
+ cmsSample3DGrid(Grid, BlackPreservingEstimateErrorSampler, (LPVOID) &Cargo, SAMPLER_INSPECT);
+
+
+Cleanup:
+
+ if (Cargo.cmyk2Lab) cmsDeleteTransform(Cargo.cmyk2Lab);
+ if (Cargo.hRoundTrip) cmsDeleteTransform(Cargo.hRoundTrip);
+ if (Cargo.hProofOutput) cmsDeleteTransform(Cargo.hProofOutput);
+
+ if (hLab) cmsCloseProfile(hLab);
+ if (Cargo.KTone) cmsFreeGamma(Cargo.KTone);
+ if (Cargo.LabK2cmyk) cmsFreeLUT(Cargo.LabK2cmyk);
+
+ return Grid;
+}
+
+
+
+// Fix broken LUT. just to obtain other CMS compatibility
+
+static
+void PatchLUT(LPLUT Grid, WORD At[], WORD Value[],
+ int nChannelsOut, int nChannelsIn)
+{
+ LPL16PARAMS p16 = &Grid -> CLut16params;
+ double px, py, pz, pw;
+ int x0, y0, z0, w0;
+ int i, index;
+
+
+ if (Grid ->wFlags & LUT_HASTL1) return; // There is a prelinearization
+
+ px = ((double) At[0] * (p16->Domain)) / 65535.0;
+ py = ((double) At[1] * (p16->Domain)) / 65535.0;
+ pz = ((double) At[2] * (p16->Domain)) / 65535.0;
+ pw = ((double) At[3] * (p16->Domain)) / 65535.0;
+
+ x0 = (int) floor(px);
+ y0 = (int) floor(py);
+ z0 = (int) floor(pz);
+ w0 = (int) floor(pw);
+
+ if (nChannelsIn == 4) {
+
+ if (((px - x0) != 0) ||
+ ((py - y0) != 0) ||
+ ((pz - z0) != 0) ||
+ ((pw - w0) != 0)) return; // Not on exact node
+
+ index = p16 -> opta4 * x0 +
+ p16 -> opta3 * y0 +
+ p16 -> opta2 * z0 +
+ p16 -> opta1 * w0;
+ }
+ else
+ if (nChannelsIn == 3) {
+
+ if (((px - x0) != 0) ||
+ ((py - y0) != 0) ||
+ ((pz - z0) != 0)) return; // Not on exact node
+
+ index = p16 -> opta3 * x0 +
+ p16 -> opta2 * y0 +
+ p16 -> opta1 * z0;
+ }
+ else
+ if (nChannelsIn == 1) {
+
+ if (((px - x0) != 0)) return; // Not on exact node
+
+ index = p16 -> opta1 * x0;
+ }
+ else {
+ cmsSignalError(LCMS_ERRC_ABORTED, "(internal) %d Channels are not supported on PatchLUT", nChannelsIn);
+ return;
+ }
+
+ for (i=0; i < nChannelsOut; i++)
+ Grid -> T[index + i] = Value[i];
+
+}
+
+
+
+LCMSBOOL _cmsFixWhiteMisalignment(_LPcmsTRANSFORM p)
+{
+
+ WORD *WhitePointIn, *WhitePointOut, *BlackPointIn, *BlackPointOut;
+ int nOuts, nIns;
+
+
+ if (!p -> DeviceLink) return FALSE;
+
+ if (p ->Intent == INTENT_ABSOLUTE_COLORIMETRIC) return FALSE;
+ if ((p ->PreviewProfile != NULL) &&
+ (p ->ProofIntent == INTENT_ABSOLUTE_COLORIMETRIC)) return FALSE;
+
+
+ if (!_cmsEndPointsBySpace(p -> EntryColorSpace,
+ &WhitePointIn, &BlackPointIn, &nIns)) return FALSE;
+
+
+ if (!_cmsEndPointsBySpace(p -> ExitColorSpace,
+ &WhitePointOut, &BlackPointOut, &nOuts)) return FALSE;
+
+ // Fix white only
+
+ PatchLUT(p -> DeviceLink, WhitePointIn, WhitePointOut, nOuts, nIns);
+ // PatchLUT(p -> DeviceLink, BlackPointIn, BlackPointOut, nOuts, nIns);
+
+ return TRUE;
+}
+
diff --git a/debian/lcms/lcms-1.19.dfsg2/src/cmsvirt.c b/debian/lcms/lcms-1.19.dfsg2/src/cmsvirt.c
new file mode 100755
index 00000000..10f5bd67
--- /dev/null
+++ b/debian/lcms/lcms-1.19.dfsg2/src/cmsvirt.c
@@ -0,0 +1,899 @@
+//
+// Little cms
+// Copyright (C) 1998-2007 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 "lcms.h"
+
+
+// Virtual (built-in) profiles
+// -----------------------------------------------------------------------------------
+
+
+// This function creates a profile based on White point, primaries and
+// transfer functions.
+
+
+cmsHPROFILE LCMSEXPORT cmsCreateRGBProfile(LPcmsCIExyY WhitePoint,
+ LPcmsCIExyYTRIPLE Primaries,
+ LPGAMMATABLE TransferFunction[3])
+{
+ cmsHPROFILE hICC;
+ cmsCIEXYZ tmp;
+ MAT3 MColorants;
+ cmsCIEXYZTRIPLE Colorants;
+ cmsCIExyY MaxWhite;
+
+
+ hICC = _cmsCreateProfilePlaceholder();
+ if (!hICC) // can't allocate
+ return NULL;
+
+
+ cmsSetDeviceClass(hICC, icSigDisplayClass);
+ cmsSetColorSpace(hICC, icSigRgbData);
+ cmsSetPCS(hICC, icSigXYZData);
+ cmsSetRenderingIntent(hICC, INTENT_PERCEPTUAL);
+
+
+ // Implement profile using following tags:
+ //
+ // 1 icSigProfileDescriptionTag
+ // 2 icSigMediaWhitePointTag
+ // 3 icSigRedColorantTag
+ // 4 icSigGreenColorantTag
+ // 5 icSigBlueColorantTag
+ // 6 icSigRedTRCTag
+ // 7 icSigGreenTRCTag
+ // 8 icSigBlueTRCTag
+
+ // This conforms a standard RGB DisplayProfile as says ICC, and then I add
+
+ // 9 icSigChromaticityTag
+
+ // As addendum II
+
+
+ // Fill-in the tags
+
+ cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)");
+ cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "lcms RGB virtual profile");
+ cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "rgb built-in");
+
+
+ if (WhitePoint) {
+
+ cmsxyY2XYZ(&tmp, WhitePoint);
+ cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) &tmp);
+ }
+
+ if (WhitePoint && Primaries) {
+
+ MaxWhite.x = WhitePoint -> x;
+ MaxWhite.y = WhitePoint -> y;
+ MaxWhite.Y = 1.0;
+
+ if (!cmsBuildRGB2XYZtransferMatrix(&MColorants, &MaxWhite, Primaries))
+ {
+ cmsCloseProfile(hICC);
+ return NULL;
+ }
+
+ cmsAdaptMatrixToD50(&MColorants, &MaxWhite);
+
+ Colorants.Red.X = MColorants.v[0].n[0];
+ Colorants.Red.Y = MColorants.v[1].n[0];
+ Colorants.Red.Z = MColorants.v[2].n[0];
+
+ Colorants.Green.X = MColorants.v[0].n[1];
+ Colorants.Green.Y = MColorants.v[1].n[1];
+ Colorants.Green.Z = MColorants.v[2].n[1];
+
+ Colorants.Blue.X = MColorants.v[0].n[2];
+ Colorants.Blue.Y = MColorants.v[1].n[2];
+ Colorants.Blue.Z = MColorants.v[2].n[2];
+
+ cmsAddTag(hICC, icSigRedColorantTag, (LPVOID) &Colorants.Red);
+ cmsAddTag(hICC, icSigBlueColorantTag, (LPVOID) &Colorants.Blue);
+ cmsAddTag(hICC, icSigGreenColorantTag, (LPVOID) &Colorants.Green);
+ }
+
+
+ if (TransferFunction) {
+
+ // In case of gamma, we must dup' the table pointer
+
+ cmsAddTag(hICC, icSigRedTRCTag, (LPVOID) TransferFunction[0]);
+ cmsAddTag(hICC, icSigGreenTRCTag, (LPVOID) TransferFunction[1]);
+ cmsAddTag(hICC, icSigBlueTRCTag, (LPVOID) TransferFunction[2]);
+ }
+
+ if (Primaries) {
+ cmsAddTag(hICC, icSigChromaticityTag, (LPVOID) Primaries);
+ }
+
+ return hICC;
+}
+
+
+
+// This function creates a profile based on White point and transfer function.
+
+cmsHPROFILE LCMSEXPORT cmsCreateGrayProfile(LPcmsCIExyY WhitePoint,
+ LPGAMMATABLE TransferFunction)
+{
+ cmsHPROFILE hICC;
+ cmsCIEXYZ tmp;
+
+
+ hICC = _cmsCreateProfilePlaceholder();
+ if (!hICC) // can't allocate
+ return NULL;
+
+
+ cmsSetDeviceClass(hICC, icSigDisplayClass);
+ cmsSetColorSpace(hICC, icSigGrayData);
+ cmsSetPCS(hICC, icSigXYZData);
+ cmsSetRenderingIntent(hICC, INTENT_PERCEPTUAL);
+
+
+
+ // Implement profile using following tags:
+ //
+ // 1 icSigProfileDescriptionTag
+ // 2 icSigMediaWhitePointTag
+ // 6 icSigGrayTRCTag
+
+ // This conforms a standard Gray DisplayProfile
+
+ // Fill-in the tags
+
+
+ cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)");
+ cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "lcms gray virtual profile");
+ cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "gray built-in");
+
+
+ if (WhitePoint) {
+
+ cmsxyY2XYZ(&tmp, WhitePoint);
+ cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) &tmp);
+ }
+
+
+ if (TransferFunction) {
+
+ // In case of gamma, we must dup' the table pointer
+
+ cmsAddTag(hICC, icSigGrayTRCTag, (LPVOID) TransferFunction);
+ }
+
+ return hICC;
+
+}
+
+
+static
+int IsPCS(icColorSpaceSignature ColorSpace)
+{
+ return (ColorSpace == icSigXYZData ||
+ ColorSpace == icSigLabData);
+}
+
+static
+void FixColorSpaces(cmsHPROFILE hProfile,
+ icColorSpaceSignature ColorSpace,
+ icColorSpaceSignature PCS,
+ DWORD dwFlags)
+{
+
+ if (dwFlags & cmsFLAGS_GUESSDEVICECLASS) {
+
+ if (IsPCS(ColorSpace) && IsPCS(PCS)) {
+
+ cmsSetDeviceClass(hProfile, icSigAbstractClass);
+ cmsSetColorSpace(hProfile, ColorSpace);
+ cmsSetPCS(hProfile, PCS);
+ return;
+ }
+
+ if (IsPCS(ColorSpace) && !IsPCS(PCS)) {
+
+ cmsSetDeviceClass(hProfile, icSigOutputClass);
+ cmsSetPCS(hProfile, ColorSpace);
+ cmsSetColorSpace(hProfile, PCS);
+ return;
+ }
+
+ if (IsPCS(PCS) && !IsPCS(ColorSpace)) {
+
+ cmsSetDeviceClass(hProfile, icSigInputClass);
+ cmsSetColorSpace(hProfile, ColorSpace);
+ cmsSetPCS(hProfile, PCS);
+ return;
+ }
+ }
+
+ cmsSetDeviceClass(hProfile, icSigLinkClass);
+ cmsSetColorSpace(hProfile, ColorSpace);
+ cmsSetPCS(hProfile, PCS);
+
+}
+
+
+static
+cmsHPROFILE CreateNamedColorDevicelink(cmsHTRANSFORM xform)
+{
+ _LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform;
+ cmsHPROFILE hICC;
+ cmsCIEXYZ WhitePoint;
+ int i, nColors;
+ size_t Size;
+ LPcmsNAMEDCOLORLIST nc2;
+
+
+ hICC = _cmsCreateProfilePlaceholder();
+ if (hICC == NULL) return NULL;
+
+ cmsSetRenderingIntent(hICC, v -> Intent);
+ cmsSetDeviceClass(hICC, icSigNamedColorClass);
+ cmsSetColorSpace(hICC, v ->ExitColorSpace);
+ cmsSetPCS(hICC, cmsGetPCS(v ->InputProfile));
+ cmsTakeMediaWhitePoint(&WhitePoint, v ->InputProfile);
+
+ cmsAddTag(hICC, icSigMediaWhitePointTag, &WhitePoint);
+ cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "LittleCMS");
+ cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "Named color Device link");
+ cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "Named color Device link");
+
+
+ nColors = cmsNamedColorCount(xform);
+ nc2 = cmsAllocNamedColorList(nColors);
+
+ Size = sizeof(cmsNAMEDCOLORLIST) + (sizeof(cmsNAMEDCOLOR) * (nColors-1));
+
+ CopyMemory(nc2, v->NamedColorList, Size);
+ nc2 ->ColorantCount = _cmsChannelsOf(v ->ExitColorSpace);
+
+ for (i=0; i < nColors; i++) {
+ cmsDoTransform(xform, &i, nc2 ->List[i].DeviceColorant, 1);
+ }
+
+ cmsAddTag(hICC, icSigNamedColor2Tag, (void*) nc2);
+ cmsFreeNamedColorList(nc2);
+
+ return hICC;
+}
+
+
+// Does convert a transform into a device link profile
+
+cmsHPROFILE LCMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, DWORD dwFlags)
+{
+ cmsHPROFILE hICC;
+ _LPcmsTRANSFORM v = (_LPcmsTRANSFORM) hTransform;
+ LPLUT Lut;
+ LCMSBOOL MustFreeLUT;
+ LPcmsNAMEDCOLORLIST InputColorant = NULL;
+ LPcmsNAMEDCOLORLIST OutputColorant = NULL;
+
+
+ // Check if is a named color transform
+
+ if (cmsGetDeviceClass(v ->InputProfile) == icSigNamedColorClass) {
+
+ return CreateNamedColorDevicelink(hTransform);
+
+ }
+
+ if (v ->DeviceLink) {
+
+ Lut = v -> DeviceLink;
+ MustFreeLUT = FALSE;
+ }
+ else {
+
+ Lut = _cmsPrecalculateDeviceLink(hTransform, dwFlags);
+ if (!Lut) return NULL;
+ MustFreeLUT = TRUE;
+ }
+
+ hICC = _cmsCreateProfilePlaceholder();
+ if (!hICC) { // can't allocate
+
+ if (MustFreeLUT) cmsFreeLUT(Lut);
+ return NULL;
+ }
+
+
+ FixColorSpaces(hICC, v -> EntryColorSpace, v -> ExitColorSpace, dwFlags);
+
+ cmsSetRenderingIntent(hICC, v -> Intent);
+
+ // Implement devicelink profile using following tags:
+ //
+ // 1 icSigProfileDescriptionTag
+ // 2 icSigMediaWhitePointTag
+ // 3 icSigAToB0Tag
+
+
+ cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "LittleCMS");
+ cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "Device link");
+ cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "Device link");
+
+
+ cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) cmsD50_XYZ());
+
+ if (cmsGetDeviceClass(hICC) == icSigOutputClass) {
+
+ cmsAddTag(hICC, icSigBToA0Tag, (LPVOID) Lut);
+ }
+ else
+ cmsAddTag(hICC, icSigAToB0Tag, (LPVOID) Lut);
+
+
+
+ // Try to read input and output colorant table
+ if (cmsIsTag(v ->InputProfile, icSigColorantTableTag)) {
+
+ // Input table can only come in this way.
+ InputColorant = cmsReadColorantTable(v ->InputProfile, icSigColorantTableTag);
+ }
+
+ // Output is a little bit more complex.
+ if (cmsGetDeviceClass(v ->OutputProfile) == icSigLinkClass) {
+
+ // This tag may exist only on devicelink profiles.
+ if (cmsIsTag(v ->OutputProfile, icSigColorantTableOutTag)) {
+
+ OutputColorant = cmsReadColorantTable(v ->OutputProfile, icSigColorantTableOutTag);
+ }
+
+ } else {
+
+ if (cmsIsTag(v ->OutputProfile, icSigColorantTableTag)) {
+
+ OutputColorant = cmsReadColorantTable(v ->OutputProfile, icSigColorantTableTag);
+ }
+ }
+
+ if (InputColorant)
+ cmsAddTag(hICC, icSigColorantTableTag, InputColorant);
+
+ if (OutputColorant)
+ cmsAddTag(hICC, icSigColorantTableOutTag, OutputColorant);
+
+
+
+ if (MustFreeLUT) cmsFreeLUT(Lut);
+ if (InputColorant) cmsFreeNamedColorList(InputColorant);
+ if (OutputColorant) cmsFreeNamedColorList(OutputColorant);
+
+ return hICC;
+
+}
+
+
+// This is a devicelink operating in the target colorspace with as many transfer
+// functions as components
+
+cmsHPROFILE LCMSEXPORT cmsCreateLinearizationDeviceLink(icColorSpaceSignature ColorSpace,
+ LPGAMMATABLE TransferFunctions[])
+{
+ cmsHPROFILE hICC;
+ LPLUT Lut;
+
+
+ hICC = _cmsCreateProfilePlaceholder();
+ if (!hICC) // can't allocate
+ return NULL;
+
+
+ cmsSetDeviceClass(hICC, icSigLinkClass);
+ cmsSetColorSpace(hICC, ColorSpace);
+ cmsSetPCS(hICC, ColorSpace);
+ cmsSetRenderingIntent(hICC, INTENT_PERCEPTUAL);
+
+
+ // Creates a LUT with prelinearization step only
+ Lut = cmsAllocLUT();
+ if (Lut == NULL) return NULL;
+
+ // Set up channels
+ Lut ->InputChan = Lut ->OutputChan = _cmsChannelsOf(ColorSpace);
+
+ // Copy tables to LUT
+ cmsAllocLinearTable(Lut, TransferFunctions, 1);
+
+ // Create tags
+ cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)");
+ cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "lcms linearization device link");
+ cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "linearization built-in");
+
+ cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) cmsD50_XYZ());
+ cmsAddTag(hICC, icSigAToB0Tag, (LPVOID) Lut);
+
+ // LUT is already on virtual profile
+ cmsFreeLUT(Lut);
+
+ // Ok, done
+ return hICC;
+}
+
+
+// Ink-limiting algorithm
+//
+// Sum = C + M + Y + K
+// If Sum > InkLimit
+// Ratio= 1 - (Sum - InkLimit) / (C + M + Y)
+// if Ratio <0
+// Ratio=0
+// endif
+// Else
+// Ratio=1
+// endif
+//
+// C = Ratio * C
+// M = Ratio * M
+// Y = Ratio * Y
+// K: Does not change
+
+static
+int InkLimitingSampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
+{
+ double InkLimit = *(double *) Cargo;
+ double SumCMY, SumCMYK, Ratio;
+
+ InkLimit = (InkLimit * 655.35);
+
+ SumCMY = In[0] + In[1] + In[2];
+ SumCMYK = SumCMY + In[3];
+
+ if (SumCMYK > InkLimit) {
+
+ Ratio = 1 - ((SumCMYK - InkLimit) / SumCMY);
+ if (Ratio < 0)
+ Ratio = 0;
+ }
+ else Ratio = 1;
+
+ Out[0] = (WORD) floor(In[0] * Ratio + 0.5); // C
+ Out[1] = (WORD) floor(In[1] * Ratio + 0.5); // M
+ Out[2] = (WORD) floor(In[2] * Ratio + 0.5); // Y
+
+ Out[3] = In[3]; // K (untouched)
+
+ return TRUE;
+}
+
+// This is a devicelink operating in CMYK for ink-limiting
+
+cmsHPROFILE LCMSEXPORT cmsCreateInkLimitingDeviceLink(icColorSpaceSignature ColorSpace,
+ double Limit)
+{
+ cmsHPROFILE hICC;
+ LPLUT Lut;
+
+ if (ColorSpace != icSigCmykData) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "InkLimiting: Only CMYK currently supported");
+ return NULL;
+ }
+
+ if (Limit < 0.0 || Limit > 400) {
+
+ cmsSignalError(LCMS_ERRC_WARNING, "InkLimiting: Limit should be between 0..400");
+ if (Limit < 0) Limit = 0;
+ if (Limit > 400) Limit = 400;
+
+ }
+
+ hICC = _cmsCreateProfilePlaceholder();
+ if (!hICC) // can't allocate
+ return NULL;
+
+
+ cmsSetDeviceClass(hICC, icSigLinkClass);
+ cmsSetColorSpace(hICC, ColorSpace);
+ cmsSetPCS(hICC, ColorSpace);
+ cmsSetRenderingIntent(hICC, INTENT_PERCEPTUAL);
+
+
+ // Creates a LUT with 3D grid only
+ Lut = cmsAllocLUT();
+ if (Lut == NULL) {
+ cmsCloseProfile(hICC);
+ return NULL;
+ }
+
+
+ cmsAlloc3DGrid(Lut, 17, _cmsChannelsOf(ColorSpace),
+ _cmsChannelsOf(ColorSpace));
+
+ if (!cmsSample3DGrid(Lut, InkLimitingSampler, (LPVOID) &Limit, 0)) {
+
+ // Shouldn't reach here
+ cmsFreeLUT(Lut);
+ cmsCloseProfile(hICC);
+ return NULL;
+ }
+
+ // Create tags
+
+ cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)");
+ cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "lcms ink limiting device link");
+ cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "ink limiting built-in");
+
+ cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) cmsD50_XYZ());
+
+ cmsAddTag(hICC, icSigAToB0Tag, (LPVOID) Lut);
+
+ // LUT is already on virtual profile
+ cmsFreeLUT(Lut);
+
+ // Ok, done
+ return hICC;
+}
+
+
+
+static
+LPLUT Create3x3EmptyLUT(void)
+{
+ LPLUT AToB0 = cmsAllocLUT();
+ if (AToB0 == NULL) return NULL;
+
+ AToB0 -> InputChan = AToB0 -> OutputChan = 3;
+ return AToB0;
+}
+
+
+
+// Creates a fake Lab identity.
+cmsHPROFILE LCMSEXPORT cmsCreateLabProfile(LPcmsCIExyY WhitePoint)
+{
+ cmsHPROFILE hProfile;
+ LPLUT Lut;
+
+ hProfile = cmsCreateRGBProfile(WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL);
+ if (hProfile == NULL) return NULL;
+
+ cmsSetDeviceClass(hProfile, icSigAbstractClass);
+ cmsSetColorSpace(hProfile, icSigLabData);
+ cmsSetPCS(hProfile, icSigLabData);
+
+ cmsAddTag(hProfile, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)");
+ cmsAddTag(hProfile, icSigProfileDescriptionTag, (LPVOID) "lcms Lab identity");
+ cmsAddTag(hProfile, icSigDeviceModelDescTag, (LPVOID) "Lab built-in");
+
+
+ // An empty LUTs is all we need
+ Lut = Create3x3EmptyLUT();
+ if (Lut == NULL) {
+ cmsCloseProfile(hProfile);
+ return NULL;
+ }
+
+ cmsAddTag(hProfile, icSigAToB0Tag, (LPVOID) Lut);
+ cmsAddTag(hProfile, icSigBToA0Tag, (LPVOID) Lut);
+
+ cmsFreeLUT(Lut);
+
+ return hProfile;
+}
+
+
+// Creates a fake Lab identity.
+cmsHPROFILE LCMSEXPORT cmsCreateLab4Profile(LPcmsCIExyY WhitePoint)
+{
+ cmsHPROFILE hProfile;
+ LPLUT Lut;
+
+ hProfile = cmsCreateRGBProfile(WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL);
+ if (hProfile == NULL) return NULL;
+
+ cmsSetProfileICCversion(hProfile, 0x4000000);
+
+ cmsSetDeviceClass(hProfile, icSigAbstractClass);
+ cmsSetColorSpace(hProfile, icSigLabData);
+ cmsSetPCS(hProfile, icSigLabData);
+
+ cmsAddTag(hProfile, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)");
+ cmsAddTag(hProfile, icSigProfileDescriptionTag, (LPVOID) "lcms Lab identity v4");
+ cmsAddTag(hProfile, icSigDeviceModelDescTag, (LPVOID) "Lab v4 built-in");
+
+
+ // An empty LUTs is all we need
+ Lut = Create3x3EmptyLUT();
+ if (Lut == NULL) {
+ cmsCloseProfile(hProfile);
+ return NULL;
+ }
+
+ Lut -> wFlags |= LUT_V4_INPUT_EMULATE_V2;
+ cmsAddTag(hProfile, icSigAToB0Tag, (LPVOID) Lut);
+
+ Lut -> wFlags |= LUT_V4_OUTPUT_EMULATE_V2;
+ cmsAddTag(hProfile, icSigBToA0Tag, (LPVOID) Lut);
+
+ cmsFreeLUT(Lut);
+
+ return hProfile;
+}
+
+
+
+// Creates a fake XYZ identity
+cmsHPROFILE LCMSEXPORT cmsCreateXYZProfile(void)
+{
+ cmsHPROFILE hProfile;
+ LPLUT Lut;
+
+ hProfile = cmsCreateRGBProfile(cmsD50_xyY(), NULL, NULL);
+ if (hProfile == NULL) return NULL;
+
+ cmsSetDeviceClass(hProfile, icSigAbstractClass);
+ cmsSetColorSpace(hProfile, icSigXYZData);
+ cmsSetPCS(hProfile, icSigXYZData);
+
+ cmsAddTag(hProfile, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)");
+ cmsAddTag(hProfile, icSigProfileDescriptionTag, (LPVOID) "lcms XYZ identity");
+ cmsAddTag(hProfile, icSigDeviceModelDescTag, (LPVOID) "XYZ built-in");
+
+ // An empty LUTs is all we need
+ Lut = Create3x3EmptyLUT();
+ if (Lut == NULL) {
+ cmsCloseProfile(hProfile);
+ return NULL;
+ }
+
+ cmsAddTag(hProfile, icSigAToB0Tag, (LPVOID) Lut);
+ cmsAddTag(hProfile, icSigBToA0Tag, (LPVOID) Lut);
+ cmsAddTag(hProfile, icSigPreview0Tag, (LPVOID) Lut);
+
+ cmsFreeLUT(Lut);
+ return hProfile;
+}
+
+
+
+/*
+
+If R’sRGB,G’sRGB, B’sRGB < 0.04045
+
+ R = R’sRGB / 12.92
+ G = G’sRGB / 12.92
+ B = B’sRGB / 12.92
+
+
+
+else if R’sRGB,G’sRGB, B’sRGB >= 0.04045
+
+ R = ((R’sRGB + 0.055) / 1.055)^2.4
+ G = ((G’sRGB + 0.055) / 1.055)^2.4
+ B = ((B’sRGB + 0.055) / 1.055)^2.4
+
+ */
+
+static
+LPGAMMATABLE Build_sRGBGamma(void)
+{
+ double Parameters[5];
+
+ Parameters[0] = 2.4;
+ Parameters[1] = 1. / 1.055;
+ Parameters[2] = 0.055 / 1.055;
+ Parameters[3] = 1. / 12.92;
+ Parameters[4] = 0.04045; // d
+
+ return cmsBuildParametricGamma(1024, 4, Parameters);
+}
+
+// Create the ICC virtual profile for sRGB space
+cmsHPROFILE LCMSEXPORT cmsCreate_sRGBProfile(void)
+{
+ cmsCIExyY D65;
+ cmsCIExyYTRIPLE Rec709Primaries = {
+ {0.6400, 0.3300, 1.0},
+ {0.3000, 0.6000, 1.0},
+ {0.1500, 0.0600, 1.0}
+ };
+ LPGAMMATABLE Gamma22[3];
+ cmsHPROFILE hsRGB;
+
+ cmsWhitePointFromTemp(6504, &D65);
+ Gamma22[0] = Gamma22[1] = Gamma22[2] = Build_sRGBGamma();
+
+ hsRGB = cmsCreateRGBProfile(&D65, &Rec709Primaries, Gamma22);
+ cmsFreeGamma(Gamma22[0]);
+ if (hsRGB == NULL) return NULL;
+
+
+ cmsAddTag(hsRGB, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)");
+ cmsAddTag(hsRGB, icSigDeviceModelDescTag, (LPVOID) "sRGB built-in");
+ cmsAddTag(hsRGB, icSigProfileDescriptionTag, (LPVOID) "sRGB built-in");
+
+ return hsRGB;
+}
+
+
+
+typedef struct {
+ double Brightness;
+ double Contrast;
+ double Hue;
+ double Saturation;
+ cmsCIEXYZ WPsrc, WPdest;
+
+} BCHSWADJUSTS, *LPBCHSWADJUSTS;
+
+
+static
+int bchswSampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
+{
+ cmsCIELab LabIn, LabOut;
+ cmsCIELCh LChIn, LChOut;
+ cmsCIEXYZ XYZ;
+ LPBCHSWADJUSTS bchsw = (LPBCHSWADJUSTS) Cargo;
+
+
+ cmsLabEncoded2Float(&LabIn, In);
+
+
+ cmsLab2LCh(&LChIn, &LabIn);
+
+ // Do some adjusts on LCh
+
+ LChOut.L = LChIn.L * bchsw ->Contrast + bchsw ->Brightness;
+ LChOut.C = LChIn.C + bchsw -> Saturation;
+ LChOut.h = LChIn.h + bchsw -> Hue;
+
+
+ cmsLCh2Lab(&LabOut, &LChOut);
+
+ // Move white point in Lab
+
+ cmsLab2XYZ(&bchsw ->WPsrc, &XYZ, &LabOut);
+ cmsXYZ2Lab(&bchsw ->WPdest, &LabOut, &XYZ);
+
+ // Back to encoded
+
+ cmsFloat2LabEncoded(Out, &LabOut);
+
+ return TRUE;
+}
+
+
+// Creates an abstract profile operating in Lab space for Brightness,
+// contrast, Saturation and white point displacement
+
+cmsHPROFILE LCMSEXPORT cmsCreateBCHSWabstractProfile(int nLUTPoints,
+ double Bright,
+ double Contrast,
+ double Hue,
+ double Saturation,
+ int TempSrc,
+ int TempDest)
+{
+ cmsHPROFILE hICC;
+ LPLUT Lut;
+ BCHSWADJUSTS bchsw;
+ cmsCIExyY WhitePnt;
+
+ bchsw.Brightness = Bright;
+ bchsw.Contrast = Contrast;
+ bchsw.Hue = Hue;
+ bchsw.Saturation = Saturation;
+
+ cmsWhitePointFromTemp(TempSrc, &WhitePnt);
+ cmsxyY2XYZ(&bchsw.WPsrc, &WhitePnt);
+
+ cmsWhitePointFromTemp(TempDest, &WhitePnt);
+ cmsxyY2XYZ(&bchsw.WPdest, &WhitePnt);
+
+ hICC = _cmsCreateProfilePlaceholder();
+ if (!hICC) // can't allocate
+ return NULL;
+
+
+ cmsSetDeviceClass(hICC, icSigAbstractClass);
+ cmsSetColorSpace(hICC, icSigLabData);
+ cmsSetPCS(hICC, icSigLabData);
+
+ cmsSetRenderingIntent(hICC, INTENT_PERCEPTUAL);
+
+
+ // Creates a LUT with 3D grid only
+ Lut = cmsAllocLUT();
+ if (Lut == NULL) {
+ cmsCloseProfile(hICC);
+ return NULL;
+ }
+
+ cmsAlloc3DGrid(Lut, nLUTPoints, 3, 3);
+
+ if (!cmsSample3DGrid(Lut, bchswSampler, (LPVOID) &bchsw, 0)) {
+
+ // Shouldn't reach here
+ cmsFreeLUT(Lut);
+ cmsCloseProfile(hICC);
+ return NULL;
+ }
+
+ // Create tags
+
+ cmsAddTag(hICC, icSigDeviceMfgDescTag, (LPVOID) "(lcms internal)");
+ cmsAddTag(hICC, icSigProfileDescriptionTag, (LPVOID) "lcms BCHSW abstract profile");
+ cmsAddTag(hICC, icSigDeviceModelDescTag, (LPVOID) "BCHSW built-in");
+
+ cmsAddTag(hICC, icSigMediaWhitePointTag, (LPVOID) cmsD50_XYZ());
+
+ cmsAddTag(hICC, icSigAToB0Tag, (LPVOID) Lut);
+
+ // LUT is already on virtual profile
+ cmsFreeLUT(Lut);
+
+ // Ok, done
+ return hICC;
+
+}
+
+
+// Creates a fake NULL profile. This profile return 1 channel as always 0.
+// Is useful only for gamut checking tricks
+
+cmsHPROFILE LCMSEXPORT cmsCreateNULLProfile(void)
+{
+ cmsHPROFILE hProfile;
+ LPLUT Lut;
+ LPGAMMATABLE EmptyTab;
+
+ hProfile = _cmsCreateProfilePlaceholder();
+ if (!hProfile) // can't allocate
+ return NULL;
+
+ cmsSetDeviceClass(hProfile, icSigOutputClass);
+ cmsSetColorSpace(hProfile, icSigGrayData);
+ cmsSetPCS(hProfile, icSigLabData);
+
+
+ // An empty LUTs is all we need
+ Lut = cmsAllocLUT();
+ if (Lut == NULL) {
+ cmsCloseProfile(hProfile);
+ return NULL;
+ }
+
+ Lut -> InputChan = 3;
+ Lut -> OutputChan = 1;
+
+ EmptyTab = cmsAllocGamma(2);
+ EmptyTab ->GammaTable[0] = 0;
+ EmptyTab ->GammaTable[1] = 0;
+
+ cmsAllocLinearTable(Lut, &EmptyTab, 2);
+
+ cmsAddTag(hProfile, icSigBToA0Tag, (LPVOID) Lut);
+
+ cmsFreeLUT(Lut);
+ cmsFreeGamma(EmptyTab);
+
+ return hProfile;
+}
diff --git a/debian/lcms/lcms-1.19.dfsg2/src/cmswtpnt.c b/debian/lcms/lcms-1.19.dfsg2/src/cmswtpnt.c
new file mode 100755
index 00000000..c3ec50d9
--- /dev/null
+++ b/debian/lcms/lcms-1.19.dfsg2/src/cmswtpnt.c
@@ -0,0 +1,695 @@
+//
+// Little cms
+// Copyright (C) 1998-2007 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 "lcms.h"
+
+
+// Conversions
+
+void LCMSEXPORT cmsXYZ2xyY(LPcmsCIExyY Dest, const cmsCIEXYZ* Source)
+{
+ double ISum;
+
+ ISum = 1./(Source -> X + Source -> Y + Source -> Z);
+
+ Dest -> x = (Source -> X) * ISum;
+ Dest -> y = (Source -> Y) * ISum;
+ Dest -> Y = Source -> Y;
+}
+
+
+void LCMSEXPORT cmsxyY2XYZ(LPcmsCIEXYZ Dest, const cmsCIExyY* Source)
+{
+
+ Dest -> X = (Source -> x / Source -> y) * Source -> Y;
+ Dest -> Y = Source -> Y;
+ Dest -> Z = ((1 - Source -> x - Source -> y) / Source -> y) * Source -> Y;
+}
+
+
+// Obtains WhitePoint from Temperature
+
+LCMSBOOL LCMSEXPORT cmsWhitePointFromTemp(int TempK, LPcmsCIExyY WhitePoint)
+{
+ double x, y;
+ double T, T2, T3;
+ // double M1, M2;
+
+
+ // No optimization provided.
+
+ T = TempK;
+ T2 = T*T; // Square
+ T3 = T2*T; // Cube
+
+ // For correlated color temperature (T) between 4000K and 7000K:
+
+ if (T >= 4000. && T <= 7000.)
+ {
+ x = -4.6070*(1E9/T3) + 2.9678*(1E6/T2) + 0.09911*(1E3/T) + 0.244063;
+ }
+ else
+ // or for correlated color temperature (T) between 7000K and 25000K:
+
+ if (T > 7000.0 && T <= 25000.0)
+ {
+ x = -2.0064*(1E9/T3) + 1.9018*(1E6/T2) + 0.24748*(1E3/T) + 0.237040;
+ }
+ else {
+ cmsSignalError(LCMS_ERRC_ABORTED, "cmsWhitePointFromTemp: invalid temp");
+ return FALSE;
+ }
+
+ // Obtain y(x)
+
+ y = -3.000*(x*x) + 2.870*x - 0.275;
+
+ // wave factors (not used, but here for futures extensions)
+
+ // M1 = (-1.3515 - 1.7703*x + 5.9114 *y)/(0.0241 + 0.2562*x - 0.7341*y);
+ // M2 = (0.0300 - 31.4424*x + 30.0717*y)/(0.0241 + 0.2562*x - 0.7341*y);
+
+
+
+ // Fill WhitePoint struct
+
+ WhitePoint -> x = x;
+ WhitePoint -> y = y;
+ WhitePoint -> Y = 1.0;
+
+ return TRUE;
+}
+
+// Build a White point, primary chromas transfer matrix from RGB to CIE XYZ
+// This is just an approximation, I am not handling all the non-linear
+// aspects of the RGB to XYZ process, and assumming that the gamma correction
+// has transitive property in the tranformation chain.
+//
+// the alghoritm:
+//
+// - First I build the absolute conversion matrix using
+// primaries in XYZ. This matrix is next inverted
+// - Then I eval the source white point across this matrix
+// obtaining the coeficients of the transformation
+// - Then, I apply these coeficients to the original matrix
+
+
+LCMSBOOL LCMSEXPORT cmsBuildRGB2XYZtransferMatrix(LPMAT3 r, LPcmsCIExyY WhitePt,
+ LPcmsCIExyYTRIPLE Primrs)
+{
+ VEC3 WhitePoint, Coef;
+ MAT3 Result, Primaries;
+ double xn, yn;
+ double xr, yr;
+ double xg, yg;
+ double xb, yb;
+
+
+ xn = WhitePt -> x;
+ yn = WhitePt -> y;
+ xr = Primrs -> Red.x;
+ yr = Primrs -> Red.y;
+ xg = Primrs -> Green.x;
+ yg = Primrs -> Green.y;
+ xb = Primrs -> Blue.x;
+ yb = Primrs -> Blue.y;
+
+
+ // Build Primaries matrix
+ VEC3init(&Primaries.v[0], xr, xg, xb);
+ VEC3init(&Primaries.v[1], yr, yg, yb);
+ VEC3init(&Primaries.v[2], (1-xr-yr), (1-xg-yg), (1-xb-yb));
+
+
+ // Result = Primaries ^ (-1) inverse matrix
+ if (!MAT3inverse(&Primaries, &Result))
+ return FALSE;
+
+
+ VEC3init(&WhitePoint, xn/yn, 1.0, (1.0-xn-yn)/yn);
+
+ // Across inverse primaries ...
+ MAT3eval(&Coef, &Result, &WhitePoint);
+
+ // Give us the Coefs, then I build transformation matrix
+ VEC3init(&r -> v[0], Coef.n[VX]*xr, Coef.n[VY]*xg, Coef.n[VZ]*xb);
+ VEC3init(&r -> v[1], Coef.n[VX]*yr, Coef.n[VY]*yg, Coef.n[VZ]*yb);
+ VEC3init(&r -> v[2], Coef.n[VX]*(1.0-xr-yr), Coef.n[VY]*(1.0-xg-yg), Coef.n[VZ]*(1.0-xb-yb));
+
+
+ return TRUE;
+}
+
+
+
+// Compute chromatic adaptation matrix using Chad as cone matrix
+
+static
+void ComputeChromaticAdaptation(LPMAT3 Conversion,
+ LPcmsCIEXYZ SourceWhitePoint,
+ LPcmsCIEXYZ DestWhitePoint,
+ LPMAT3 Chad)
+
+{
+
+ MAT3 Chad_Inv;
+ VEC3 ConeSourceXYZ, ConeSourceRGB;
+ VEC3 ConeDestXYZ, ConeDestRGB;
+ MAT3 Cone, Tmp;
+
+
+ Tmp = *Chad;
+ MAT3inverse(&Tmp, &Chad_Inv);
+
+ VEC3init(&ConeSourceXYZ, SourceWhitePoint -> X,
+ SourceWhitePoint -> Y,
+ SourceWhitePoint -> Z);
+
+ VEC3init(&ConeDestXYZ, DestWhitePoint -> X,
+ DestWhitePoint -> Y,
+ DestWhitePoint -> Z);
+
+ MAT3eval(&ConeSourceRGB, Chad, &ConeSourceXYZ);
+ MAT3eval(&ConeDestRGB, Chad, &ConeDestXYZ);
+
+ // Build matrix
+
+ VEC3init(&Cone.v[0], ConeDestRGB.n[0]/ConeSourceRGB.n[0], 0.0, 0.0);
+ VEC3init(&Cone.v[1], 0.0, ConeDestRGB.n[1]/ConeSourceRGB.n[1], 0.0);
+ VEC3init(&Cone.v[2], 0.0, 0.0, ConeDestRGB.n[2]/ConeSourceRGB.n[2]);
+
+
+ // Normalize
+ MAT3per(&Tmp, &Cone, Chad);
+ MAT3per(Conversion, &Chad_Inv, &Tmp);
+
+}
+
+
+// Returns the final chrmatic adaptation from illuminant FromIll to Illuminant ToIll
+// The cone matrix can be specified in ConeMatrix. If NULL, Bradford is assumed
+
+LCMSBOOL cmsAdaptationMatrix(LPMAT3 r, LPMAT3 ConeMatrix, LPcmsCIEXYZ FromIll, LPcmsCIEXYZ ToIll)
+{
+ MAT3 LamRigg = {{ // Bradford matrix
+ {{ 0.8951, 0.2664, -0.1614 }},
+ {{ -0.7502, 1.7135, 0.0367 }},
+ {{ 0.0389, -0.0685, 1.0296 }}
+ }};
+
+
+ if (ConeMatrix == NULL)
+ ConeMatrix = &LamRigg;
+
+ ComputeChromaticAdaptation(r, FromIll, ToIll, ConeMatrix);
+ return TRUE;
+
+}
+
+// Same as anterior, but assuming D50 destination. White point is given in xyY
+
+LCMSBOOL cmsAdaptMatrixToD50(LPMAT3 r, LPcmsCIExyY SourceWhitePt)
+{
+ cmsCIEXYZ Dn;
+ MAT3 Bradford;
+ MAT3 Tmp;
+
+ cmsxyY2XYZ(&Dn, SourceWhitePt);
+
+ cmsAdaptationMatrix(&Bradford, NULL, &Dn, cmsD50_XYZ());
+
+ Tmp = *r;
+ MAT3per(r, &Bradford, &Tmp);
+
+ return TRUE;
+}
+
+
+// Same as anterior, but assuming D50 source. White point is given in xyY
+
+LCMSBOOL cmsAdaptMatrixFromD50(LPMAT3 r, LPcmsCIExyY DestWhitePt)
+{
+ cmsCIEXYZ Dn;
+ MAT3 Bradford;
+ MAT3 Tmp;
+
+ cmsxyY2XYZ(&Dn, DestWhitePt);
+
+ cmsAdaptationMatrix(&Bradford, NULL, cmsD50_XYZ(), &Dn);
+
+ Tmp = *r;
+ MAT3per(r, &Bradford, &Tmp);
+
+ return TRUE;
+}
+
+
+// Adapts a color to a given illuminant. Original color is expected to have
+// a SourceWhitePt white point.
+
+LCMSBOOL LCMSEXPORT cmsAdaptToIlluminant(LPcmsCIEXYZ Result,
+ LPcmsCIEXYZ SourceWhitePt,
+ LPcmsCIEXYZ Illuminant,
+ LPcmsCIEXYZ Value)
+{
+ MAT3 Bradford;
+ VEC3 In, Out;
+
+ // BradfordLamRiggChromaticAdaptation(&Bradford, SourceWhitePt, Illuminant);
+
+ cmsAdaptationMatrix(&Bradford, NULL, SourceWhitePt, Illuminant);
+
+ VEC3init(&In, Value -> X, Value -> Y, Value -> Z);
+ MAT3eval(&Out, &Bradford, &In);
+
+ Result -> X = Out.n[0];
+ Result -> Y = Out.n[1];
+ Result -> Z = Out.n[2];
+
+ return TRUE;
+}
+
+
+
+typedef struct {
+
+ double mirek; // temp (in microreciprocal kelvin)
+ double ut; // u coord of intersection w/ blackbody locus
+ double vt; // v coord of intersection w/ blackbody locus
+ double tt; // slope of ISOTEMPERATURE. line
+
+ } ISOTEMPERATURE,FAR* LPISOTEMPERATURE;
+
+static ISOTEMPERATURE isotempdata[] = {
+// {Mirek, Ut, Vt, Tt }
+ {0, 0.18006, 0.26352, -0.24341},
+ {10, 0.18066, 0.26589, -0.25479},
+ {20, 0.18133, 0.26846, -0.26876},
+ {30, 0.18208, 0.27119, -0.28539},
+ {40, 0.18293, 0.27407, -0.30470},
+ {50, 0.18388, 0.27709, -0.32675},
+ {60, 0.18494, 0.28021, -0.35156},
+ {70, 0.18611, 0.28342, -0.37915},
+ {80, 0.18740, 0.28668, -0.40955},
+ {90, 0.18880, 0.28997, -0.44278},
+ {100, 0.19032, 0.29326, -0.47888},
+ {125, 0.19462, 0.30141, -0.58204},
+ {150, 0.19962, 0.30921, -0.70471},
+ {175, 0.20525, 0.31647, -0.84901},
+ {200, 0.21142, 0.32312, -1.0182 },
+ {225, 0.21807, 0.32909, -1.2168 },
+ {250, 0.22511, 0.33439, -1.4512 },
+ {275, 0.23247, 0.33904, -1.7298 },
+ {300, 0.24010, 0.34308, -2.0637 },
+ {325, 0.24702, 0.34655, -2.4681 },
+ {350, 0.25591, 0.34951, -2.9641 },
+ {375, 0.26400, 0.35200, -3.5814 },
+ {400, 0.27218, 0.35407, -4.3633 },
+ {425, 0.28039, 0.35577, -5.3762 },
+ {450, 0.28863, 0.35714, -6.7262 },
+ {475, 0.29685, 0.35823, -8.5955 },
+ {500, 0.30505, 0.35907, -11.324 },
+ {525, 0.31320, 0.35968, -15.628 },
+ {550, 0.32129, 0.36011, -23.325 },
+ {575, 0.32931, 0.36038, -40.770 },
+ {600, 0.33724, 0.36051, -116.45 }
+};
+
+#define NISO sizeof(isotempdata)/sizeof(ISOTEMPERATURE)
+
+
+// Robertson's method
+
+static
+double Robertson(LPcmsCIExyY v)
+{
+ int j;
+ double us,vs;
+ double uj,vj,tj,di,dj,mi,mj;
+ double Tc = -1, xs, ys;
+
+ di = mi = 0;
+ xs = v -> x;
+ ys = v -> y;
+
+ // convert (x,y) to CIE 1960 (u,v)
+
+ us = (2*xs) / (-xs + 6*ys + 1.5);
+ vs = (3*ys) / (-xs + 6*ys + 1.5);
+
+
+ for (j=0; j < NISO; j++) {
+
+ uj = isotempdata[j].ut;
+ vj = isotempdata[j].vt;
+ tj = isotempdata[j].tt;
+ mj = isotempdata[j].mirek;
+
+ dj = ((vs - vj) - tj * (us - uj)) / sqrt(1 + tj*tj);
+
+ if ((j!=0) && (di/dj < 0.0)) {
+ Tc = 1000000.0 / (mi + (di / (di - dj)) * (mj - mi));
+ break;
+ }
+
+ di = dj;
+ mi = mj;
+ }
+
+
+ if (j == NISO) return -1;
+ return Tc;
+}
+
+
+
+static
+LCMSBOOL InRange(LPcmsCIExyY a, LPcmsCIExyY b, double tolerance)
+{
+ double dist_x, dist_y;
+
+ dist_x = fabs(a->x - b->x);
+ dist_y = fabs(a->y - b->y);
+
+ return (tolerance >= dist_x * dist_x + dist_y * dist_y);
+
+}
+
+
+typedef struct {
+ char Name[30];
+ cmsCIExyY Val;
+
+ } WHITEPOINTS,FAR *LPWHITEPOINTS;
+
+static
+int FromD40toD150(LPWHITEPOINTS pts)
+{
+ int i, n;
+
+ n = 0;
+ for (i=40; i < 150; i ++)
+ {
+ sprintf(pts[n].Name, "D%d", i);
+ cmsWhitePointFromTemp((int) (i*100.0), &pts[n].Val);
+ n++;
+ }
+
+ return n;
+}
+
+
+// To be removed in future versions
+void _cmsIdentifyWhitePoint(char *Buffer, LPcmsCIEXYZ WhitePt)
+{
+ int i, n;
+ cmsCIExyY Val;
+ double T;
+ WHITEPOINTS SomeIlluminants[140] = {
+
+ {"CIE illuminant A", {0.4476, 0.4074, 1.0}},
+ {"CIE illuminant C", {0.3101, 0.3162, 1.0}},
+ {"D65 (daylight)", {0.3127, 0.3291, 1.0}},
+ };
+
+ n = FromD40toD150(&SomeIlluminants[3]) + 3;
+
+ cmsXYZ2xyY(&Val, WhitePt);
+
+ Val.Y = 1.;
+ for (i=0; i < n; i++)
+ {
+
+ if (InRange(&Val, &SomeIlluminants[i].Val, 0.000005))
+ {
+ strcpy(Buffer, "WhitePoint : ");
+ strcat(Buffer, SomeIlluminants[i].Name);
+ return;
+ }
+ }
+
+ T = Robertson(&Val);
+
+ if (T > 0)
+ sprintf(Buffer, "White point near %dK", (int) T);
+ else
+ {
+ sprintf(Buffer, "Unknown white point (X:%1.2g, Y:%1.2g, Z:%1.2g)",
+ WhitePt -> X, WhitePt -> Y, WhitePt -> Z);
+
+ }
+
+}
+
+
+// Use darker colorant to obtain black point
+
+static
+int BlackPointAsDarkerColorant(cmsHPROFILE hInput,
+ int Intent,
+ LPcmsCIEXYZ BlackPoint,
+ DWORD dwFlags)
+{
+ WORD *Black, *White;
+ cmsHTRANSFORM xform;
+ icColorSpaceSignature Space;
+ int nChannels;
+ DWORD dwFormat;
+ cmsHPROFILE hLab;
+ cmsCIELab Lab;
+ cmsCIEXYZ BlackXYZ, MediaWhite;
+
+ // If the profile does not support input direction, assume Black point 0
+ if (!cmsIsIntentSupported(hInput, Intent, LCMS_USED_AS_INPUT)) {
+
+ BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
+ return 0;
+ }
+
+
+ // Try to get black by using black colorant
+ Space = cmsGetColorSpace(hInput);
+
+ if (!_cmsEndPointsBySpace(Space, &White, &Black, &nChannels)) {
+
+ BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
+ return 0;
+ }
+
+ dwFormat = CHANNELS_SH(nChannels)|BYTES_SH(2);
+
+ hLab = cmsCreateLabProfile(NULL);
+
+ xform = cmsCreateTransform(hInput, dwFormat,
+ hLab, TYPE_Lab_DBL, Intent, cmsFLAGS_NOTPRECALC);
+
+
+ cmsDoTransform(xform, Black, &Lab, 1);
+
+ // Force it to be neutral, clip to max. L* of 50
+
+ Lab.a = Lab.b = 0;
+ if (Lab.L > 50) Lab.L = 50;
+
+ cmsCloseProfile(hLab);
+ cmsDeleteTransform(xform);
+
+ cmsLab2XYZ(NULL, &BlackXYZ, &Lab);
+
+ if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) {
+
+ *BlackPoint = BlackXYZ;
+ }
+ else {
+
+ if (!(dwFlags & LCMS_BPFLAGS_D50_ADAPTED)) {
+
+ cmsTakeMediaWhitePoint(&MediaWhite, hInput);
+ cmsAdaptToIlluminant(BlackPoint, cmsD50_XYZ(), &MediaWhite, &BlackXYZ);
+ }
+ else
+ *BlackPoint = BlackXYZ;
+ }
+
+ return 1;
+}
+
+
+// Get a black point of output CMYK profile, discounting any ink-limiting embedded
+// in the profile. For doing that, use perceptual intent in input direction:
+// Lab (0, 0, 0) -> [Perceptual] Profile -> CMYK -> [Rel. colorimetric] Profile -> Lab
+
+static
+int BlackPointUsingPerceptualBlack(LPcmsCIEXYZ BlackPoint,
+ cmsHPROFILE hProfile,
+ DWORD dwFlags)
+{
+ cmsHTRANSFORM hPercLab2CMYK, hRelColCMYK2Lab;
+ cmsHPROFILE hLab;
+ cmsCIELab LabIn, LabOut;
+ WORD CMYK[MAXCHANNELS];
+ cmsCIEXYZ BlackXYZ, MediaWhite;
+
+
+ if (!cmsIsIntentSupported(hProfile, INTENT_PERCEPTUAL, LCMS_USED_AS_INPUT)) {
+
+ BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0;
+ return 0;
+ }
+
+ hLab = cmsCreateLabProfile(NULL);
+
+ hPercLab2CMYK = cmsCreateTransform(hLab, TYPE_Lab_DBL,
+ hProfile, TYPE_CMYK_16,
+ INTENT_PERCEPTUAL, cmsFLAGS_NOTPRECALC);
+
+ hRelColCMYK2Lab = cmsCreateTransform(hProfile, TYPE_CMYK_16,
+ hLab, TYPE_Lab_DBL,
+ INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOTPRECALC);
+
+ LabIn.L = LabIn.a = LabIn.b = 0;
+
+ cmsDoTransform(hPercLab2CMYK, &LabIn, CMYK, 1);
+ cmsDoTransform(hRelColCMYK2Lab, CMYK, &LabOut, 1);
+
+ if (LabOut.L > 50) LabOut.L = 50;
+ LabOut.a = LabOut.b = 0;
+
+ cmsDeleteTransform(hPercLab2CMYK);
+ cmsDeleteTransform(hRelColCMYK2Lab);
+ cmsCloseProfile(hLab);
+
+ cmsLab2XYZ(NULL, &BlackXYZ, &LabOut);
+
+ if (!(dwFlags & LCMS_BPFLAGS_D50_ADAPTED)){
+ cmsTakeMediaWhitePoint(&MediaWhite, hProfile);
+ cmsAdaptToIlluminant(BlackPoint, cmsD50_XYZ(), &MediaWhite, &BlackXYZ);
+ }
+ else
+ *BlackPoint = BlackXYZ;
+
+ return 1;
+
+}
+
+
+// Get Perceptual black of v4 profiles.
+static
+int GetV4PerceptualBlack(LPcmsCIEXYZ BlackPoint, cmsHPROFILE hProfile, DWORD dwFlags)
+{
+ if (dwFlags & LCMS_BPFLAGS_D50_ADAPTED) {
+
+ BlackPoint->X = PERCEPTUAL_BLACK_X;
+ BlackPoint->Y = PERCEPTUAL_BLACK_Y;
+ BlackPoint->Z = PERCEPTUAL_BLACK_Z;
+ }
+ else {
+
+ cmsCIEXYZ D50BlackPoint, MediaWhite;
+
+ cmsTakeMediaWhitePoint(&MediaWhite, hProfile);
+ D50BlackPoint.X = PERCEPTUAL_BLACK_X;
+ D50BlackPoint.Y = PERCEPTUAL_BLACK_Y;
+ D50BlackPoint.Z = PERCEPTUAL_BLACK_Z;
+
+ // Obtain the absolute XYZ. Adapt perceptual black back from D50 to whatever media white
+ cmsAdaptToIlluminant(BlackPoint, cmsD50_XYZ(), &MediaWhite, &D50BlackPoint);
+ }
+
+
+ return 1;
+}
+
+
+// This function shouldn't exist at all -- there is such quantity of broken
+// profiles on black point tag, that we must somehow fix chromaticity to
+// avoid huge tint when doing Black point compensation. This function does
+// just that. There is a special flag for using black point tag, but turned
+// off by default because it is bogus on most profiles. The detection algorithm
+// involves to turn BP to neutral and to use only L component.
+
+int cmsDetectBlackPoint(LPcmsCIEXYZ BlackPoint, cmsHPROFILE hProfile, int Intent, DWORD dwFlags)
+{
+
+ // v4 + perceptual & saturation intents does have its own black point, and it is
+ // well specified enough to use it.
+
+ if ((cmsGetProfileICCversion(hProfile) >= 0x4000000) &&
+ (Intent == INTENT_PERCEPTUAL || Intent == INTENT_SATURATION)) {
+
+ // Matrix shaper share MRC & perceptual intents
+ if (_cmsIsMatrixShaper(hProfile))
+ return BlackPointAsDarkerColorant(hProfile, INTENT_RELATIVE_COLORIMETRIC, BlackPoint, cmsFLAGS_NOTPRECALC);
+
+ // CLUT based - Get perceptual black point (fixed value)
+ return GetV4PerceptualBlack(BlackPoint, hProfile, dwFlags);
+ }
+
+
+#ifdef HONOR_BLACK_POINT_TAG
+
+ // v2, v4 rel/abs colorimetric
+ if (cmsIsTag(hProfile, icSigMediaBlackPointTag) &&
+ Intent == INTENT_RELATIVE_COLORIMETRIC) {
+
+ cmsCIEXYZ BlackXYZ, UntrustedBlackPoint, TrustedBlackPoint, MediaWhite;
+ cmsCIELab Lab;
+
+ // If black point is specified, then use it,
+
+ cmsTakeMediaBlackPoint(&BlackXYZ, hProfile);
+ cmsTakeMediaWhitePoint(&MediaWhite, hProfile);
+
+ // Black point is absolute XYZ, so adapt to D50 to get PCS value
+ cmsAdaptToIlluminant(&UntrustedBlackPoint, &MediaWhite, cmsD50_XYZ(), &BlackXYZ);
+
+ // Force a=b=0 to get rid of any chroma
+
+ cmsXYZ2Lab(NULL, &Lab, &UntrustedBlackPoint);
+ Lab.a = Lab.b = 0;
+ if (Lab.L > 50) Lab.L = 50; // Clip to L* <= 50
+
+ cmsLab2XYZ(NULL, &TrustedBlackPoint, &Lab);
+
+ // Return BP as D50 relative or absolute XYZ (depends on flags)
+ if (!(dwFlags & LCMS_BPFLAGS_D50_ADAPTED))
+ cmsAdaptToIlluminant(BlackPoint, cmsD50_XYZ(), &MediaWhite, &TrustedBlackPoint);
+ else
+ *BlackPoint = TrustedBlackPoint;
+
+ return 1;
+ }
+
+#endif
+
+ // That is about v2 profiles.
+
+ // If output profile, discount ink-limiting and that's all
+ if (Intent == INTENT_RELATIVE_COLORIMETRIC &&
+ (cmsGetDeviceClass(hProfile) == icSigOutputClass) &&
+ (cmsGetColorSpace(hProfile) == icSigCmykData))
+ return BlackPointUsingPerceptualBlack(BlackPoint, hProfile, dwFlags);
+
+ // Nope, compute BP using current intent.
+ return BlackPointAsDarkerColorant(hProfile, Intent, BlackPoint, dwFlags);
+
+}
diff --git a/debian/lcms/lcms-1.19.dfsg2/src/cmsxform.c b/debian/lcms/lcms-1.19.dfsg2/src/cmsxform.c
new file mode 100755
index 00000000..0943fda5
--- /dev/null
+++ b/debian/lcms/lcms-1.19.dfsg2/src/cmsxform.c
@@ -0,0 +1,2018 @@
+//
+// Little cms
+// Copyright (C) 1998-2007 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 "lcms.h"
+
+
+// Transformations stuff
+// -----------------------------------------------------------------------
+
+
+// Interface
+
+cmsHTRANSFORM LCMSEXPORT cmsCreateTransform(cmsHPROFILE Input,
+ DWORD InputFormat,
+ cmsHPROFILE Output,
+ DWORD OutputFormat,
+ int Intent,
+ DWORD dwFlags);
+
+cmsHTRANSFORM LCMSEXPORT cmsCreateProofingTransform(cmsHPROFILE Input,
+ DWORD InputFormat,
+ cmsHPROFILE Output,
+ DWORD OutputFormat,
+ cmsHPROFILE Proofing,
+ int Intent,
+ int ProofingIntent,
+ DWORD dwFlags);
+
+
+void LCMSEXPORT cmsDeleteTransform(cmsHTRANSFORM hTransform);
+
+void LCMSEXPORT cmsDoTransform(cmsHTRANSFORM Transform,
+ LPVOID InputBuffer,
+ LPVOID OutputBuffer, unsigned int Size);
+
+void LCMSEXPORT cmsGetAlarmCodes(int *r, int *g, int *b);
+void LCMSEXPORT cmsSetAlarmCodes(int r, int g, int b);
+LCMSBOOL LCMSEXPORT cmsIsIntentSupported(cmsHPROFILE hProfile,
+ int Intent, int UsedDirection);
+
+// -------------------------------------------------------------------------
+
+
+// Alarm RGB codes
+
+static WORD AlarmR = 0x8fff, AlarmG = 0x8fff, AlarmB = 0x8fff;
+
+// Tag tables, soted by intents
+
+static icTagSignature Device2PCS[] = {icSigAToB0Tag, // Perceptual
+ icSigAToB1Tag, // Relative colorimetric
+ icSigAToB2Tag, // Saturation
+ icSigAToB1Tag }; // Absolute colorimetric
+ // (Relative/WhitePoint)
+
+static icTagSignature PCS2Device[] = {icSigBToA0Tag, // Perceptual
+ icSigBToA1Tag, // Relative colorimetric
+ icSigBToA2Tag, // Saturation
+ icSigBToA1Tag }; // Absolute colorimetric
+ // (Relative/WhitePoint)
+
+
+static icTagSignature Preview[] = {icSigPreview0Tag,
+ icSigPreview1Tag,
+ icSigPreview2Tag,
+ icSigPreview1Tag };
+
+
+
+static volatile double GlobalAdaptationState = 0;
+
+// --------------------------------Stages--------------------------------------
+
+// Following routines does implement several kind of steps inside
+// transform. On building the transform, code chooses adequate.
+
+
+// From Shaper-Matrix to PCS
+
+static
+void ShaperMatrixToPCS(struct _cmstransform_struct *p,
+ WORD In[3], WORD Out[3])
+{
+ cmsEvalMatShaper(p -> InMatShaper, In, Out);
+}
+
+// From LUT to PCS
+
+static
+void LUTtoPCS(struct _cmstransform_struct *p,
+ WORD In[], WORD Out[3])
+{
+ cmsEvalLUT(p -> Device2PCS, In, Out);
+}
+
+// From indexed named color to PCS
+
+static
+void NC2toPCS(struct _cmstransform_struct *p,
+ WORD In[], WORD Out[3])
+{
+ int index = In[0];
+
+ if (index >= p ->NamedColorList-> nColors)
+ cmsSignalError(LCMS_ERRC_WARNING, "Color %d out of range", index);
+ else
+ CopyMemory(Out, p ->NamedColorList->List[index].PCS, 3 * sizeof(WORD));
+}
+
+// From PCS to Shaper-Matrix
+
+static
+void PCStoShaperMatrix(struct _cmstransform_struct *p,
+ WORD In[3], WORD Out[3])
+{
+ cmsEvalMatShaper(p -> OutMatShaper, In, Out);
+}
+
+// From PCS to LUT
+
+static
+void PCStoLUT(struct _cmstransform_struct *p,
+ WORD In[3], WORD Out[])
+{
+ cmsEvalLUT(p -> PCS2Device, In, Out);
+}
+
+
+
+
+// ----------------------- TRANSFORMATIONS --------------------------
+
+
+// Inlining some assignations
+
+#define COPY_3CHANS(to, from) { to[0]=from[0]; to[1]=from[1]; to[2]=from[2]; }
+
+
+// Null transformation, only hold channels
+
+static
+void NullXFORM(_LPcmsTRANSFORM p,
+ LPVOID in,
+ LPVOID out, unsigned int Size)
+{
+ register LPBYTE accum;
+ register LPBYTE output;
+ WORD wIn[MAXCHANNELS];
+ register unsigned int i, n;
+
+
+ accum = (LPBYTE) in;
+ output = (LPBYTE) out;
+ n = Size; // Buffer len
+
+ for (i=0; i < n; i++)
+ {
+ accum = p -> FromInput(p, wIn, accum);
+ output = p -> ToOutput(p, wIn, output);
+ }
+
+}
+
+
+// This is the "normal" proofing transform
+
+static
+void NormalXFORM(_LPcmsTRANSFORM p,
+ LPVOID in,
+ LPVOID out, unsigned int Size)
+{
+ register LPBYTE accum;
+ register LPBYTE output;
+ WORD wIn[MAXCHANNELS], wOut[MAXCHANNELS];
+ WORD wStageABC[3], wPCS[3], wStageLMN[MAXCHANNELS];
+ WORD wGamut[1];
+ register unsigned int i, n;
+
+
+
+ accum = (LPBYTE) in;
+ output = (LPBYTE) out;
+ n = Size; // Buffer len
+
+ for (i=0; i < n; i++)
+ {
+
+ accum = p -> FromInput(p, wIn, accum);
+
+ p -> FromDevice(p, wIn, wStageABC);
+
+ if (p -> Stage1) {
+
+ p -> Stage1(wStageABC, wPCS, &p->m1, &p->of1);
+
+ if (wPCS[0] == 0xFFFF &&
+ wPCS[1] == 0xFFFF &&
+ wPCS[2] == 0xFFFF) {
+
+ // White cutoff
+
+ output = p -> ToOutput((_LPcmsTRANSFORM) p,
+ _cmsWhiteBySpace(cmsGetColorSpace(p -> OutputProfile)),
+ output);
+ continue;
+ }
+ }
+ else
+ COPY_3CHANS(wPCS, wStageABC);
+
+
+ if (p->Gamut) {
+
+ // Gamut check, enabled across CLUT
+
+ cmsEvalLUT(p -> Gamut, wPCS, wGamut);
+
+ if (wGamut[0] >= 1) {
+
+ wOut[0] = AlarmR; // Gamut alarm
+ wOut[1] = AlarmG;
+ wOut[2] = AlarmB;
+ wOut[3] = 0;
+
+ output = p -> ToOutput((_LPcmsTRANSFORM)p, wOut, output);
+ continue;
+ }
+ }
+
+ if (p -> Preview)
+ {
+ WORD wPreview[3]; // PCS
+
+ cmsEvalLUT(p -> Preview, wPCS, wPreview);
+ COPY_3CHANS(wPCS, wPreview);
+ }
+
+ if (p -> Stage2) {
+
+ p -> Stage2(wPCS, wStageLMN, &p->m2, &p->of2);
+
+ if (wPCS[0] == 0xFFFF &&
+ wPCS[1] == 0xFFFF &&
+ wPCS[2] == 0xFFFF) {
+
+ // White cutoff
+
+ output = p -> ToOutput((_LPcmsTRANSFORM)p,
+ _cmsWhiteBySpace(cmsGetColorSpace(p -> OutputProfile)),
+ output);
+
+ continue;
+ }
+
+ }
+ else
+ COPY_3CHANS(wStageLMN, wPCS);
+
+ // Here wOut may come as MAXCHANNELS channels
+
+ p -> ToDevice(p, wStageLMN, wOut);
+
+ output = p -> ToOutput((_LPcmsTRANSFORM)p, wOut, output);
+ }
+}
+
+// Using precalculated LUT
+
+static
+void PrecalculatedXFORM(_LPcmsTRANSFORM p,
+ LPVOID in,
+ LPVOID out, unsigned int Size)
+{
+ register LPBYTE accum;
+ register LPBYTE output;
+ WORD wIn[MAXCHANNELS], wOut[MAXCHANNELS];
+ unsigned int i, n;
+
+
+ accum = (LPBYTE) in;
+ output = (LPBYTE) out;
+ n = Size; // Buffer len
+
+
+ for (i=0; i < n; i++) {
+
+ accum = p -> FromInput(p, wIn, accum);
+
+ // Try to speedup things on plain devicelinks
+
+ if (p ->DeviceLink ->wFlags == LUT_HAS3DGRID) {
+
+ p ->DeviceLink ->CLut16params.Interp3D(wIn, wOut,
+ p ->DeviceLink -> T,
+ &p ->DeviceLink -> CLut16params);
+ }
+ else
+ cmsEvalLUT(p -> DeviceLink, wIn, wOut);
+
+
+ output = p -> ToOutput(p, wOut, output);
+ }
+}
+
+// Auxiliar: Handle precalculated gamut check
+
+static
+void TransformOnePixelWithGamutCheck(_LPcmsTRANSFORM p, WORD wIn[], WORD wOut[])
+{
+ WORD wOutOfGamut;
+
+ cmsEvalLUT(p ->GamutCheck, wIn, &wOutOfGamut);
+
+ if (wOutOfGamut >= 1) {
+
+ ZeroMemory(wOut, sizeof(WORD) * MAXCHANNELS);
+
+ wOut[0] = AlarmR;
+ wOut[1] = AlarmG;
+ wOut[2] = AlarmB;
+
+ }
+ else
+ cmsEvalLUT(p -> DeviceLink, wIn, wOut);
+
+}
+
+
+static
+void PrecalculatedXFORMGamutCheck(_LPcmsTRANSFORM p,
+ LPVOID in,
+ LPVOID out, unsigned int Size)
+{
+ register LPBYTE accum;
+ register LPBYTE output;
+ WORD wIn[MAXCHANNELS], wOut[MAXCHANNELS];
+ register unsigned int i, n;
+
+
+ accum = (LPBYTE) in;
+ output = (LPBYTE) out;
+ n = Size; // Buffer len
+
+ for (i=0; i < n; i++) {
+
+ accum = p -> FromInput(p, wIn, accum);
+
+ TransformOnePixelWithGamutCheck(p, wIn, wOut);
+
+ output = p -> ToOutput(p, wOut, output);
+ }
+}
+
+
+
+// Using precalculated LUT + Cache
+
+static
+void CachedXFORM(_LPcmsTRANSFORM p,
+ LPVOID in,
+ LPVOID out, unsigned int Size)
+{
+ register LPBYTE accum;
+ register LPBYTE output;
+ WORD wIn[MAXCHANNELS], wOut[MAXCHANNELS];
+ register unsigned int i, n;
+ WORD CacheIn[MAXCHANNELS], CacheOut[MAXCHANNELS];
+
+
+ accum = (LPBYTE) in;
+ output = (LPBYTE) out;
+ n = Size; // Buffer len
+
+ // Empty buffers for quick memcmp
+
+ ZeroMemory(wIn, sizeof(WORD) * MAXCHANNELS);
+ ZeroMemory(wOut, sizeof(WORD) * MAXCHANNELS);
+
+
+ LCMS_READ_LOCK(&p ->rwlock);
+ CopyMemory(CacheIn, p ->CacheIn, sizeof(WORD) * MAXCHANNELS);
+ CopyMemory(CacheOut, p ->CacheOut, sizeof(WORD) * MAXCHANNELS);
+ LCMS_UNLOCK(&p ->rwlock);
+
+ for (i=0; i < n; i++) {
+
+ accum = p -> FromInput(p, wIn, accum);
+
+
+ if (memcmp(wIn, CacheIn, sizeof(WORD) * MAXCHANNELS) == 0) {
+
+ CopyMemory(wOut, CacheOut, sizeof(WORD) * MAXCHANNELS);
+ }
+ else {
+
+ // Try to speedup things on plain devicelinks
+
+ if (p ->DeviceLink ->wFlags == LUT_HAS3DGRID) {
+
+ p ->DeviceLink ->CLut16params.Interp3D(wIn, wOut,
+ p ->DeviceLink -> T,
+ &p ->DeviceLink -> CLut16params);
+ }
+ else
+ cmsEvalLUT(p -> DeviceLink, wIn, wOut);
+
+
+ CopyMemory(CacheIn, wIn, sizeof(WORD) * MAXCHANNELS);
+ CopyMemory(CacheOut, wOut, sizeof(WORD) * MAXCHANNELS);
+ }
+
+ output = p -> ToOutput(p, wOut, output);
+ }
+
+
+ LCMS_WRITE_LOCK(&p ->rwlock);
+ CopyMemory(p->CacheIn, CacheIn, sizeof(WORD) * MAXCHANNELS);
+ CopyMemory(p->CacheOut, CacheOut, sizeof(WORD) * MAXCHANNELS);
+ LCMS_UNLOCK(&p ->rwlock);
+
+}
+
+
+
+// Using precalculated LUT + Cache
+
+static
+void CachedXFORMGamutCheck(_LPcmsTRANSFORM p,
+ LPVOID in,
+ LPVOID out, unsigned int Size)
+{
+ register LPBYTE accum;
+ register LPBYTE output;
+ WORD wIn[MAXCHANNELS], wOut[MAXCHANNELS];
+ register unsigned int i, n;
+ WORD CacheIn[MAXCHANNELS], CacheOut[MAXCHANNELS];
+
+
+ accum = (LPBYTE) in;
+ output = (LPBYTE) out;
+ n = Size; // Buffer len
+
+ // Empty buffers for quick memcmp
+
+ ZeroMemory(wIn, sizeof(WORD) * MAXCHANNELS);
+ ZeroMemory(wOut, sizeof(WORD) * MAXCHANNELS);
+
+ LCMS_READ_LOCK(&p ->rwlock);
+ CopyMemory(CacheIn, p ->CacheIn, sizeof(WORD) * MAXCHANNELS);
+ CopyMemory(CacheOut, p ->CacheOut, sizeof(WORD) * MAXCHANNELS);
+ LCMS_UNLOCK(&p ->rwlock);
+
+
+ for (i=0; i < n; i++) {
+
+ accum = p -> FromInput(p, wIn, accum);
+
+ if (memcmp(wIn, CacheIn, sizeof(WORD) * MAXCHANNELS) == 0) {
+
+ CopyMemory(wOut, CacheOut, sizeof(WORD) * MAXCHANNELS);
+ }
+ else {
+
+ TransformOnePixelWithGamutCheck(p, wIn, wOut);
+
+ CopyMemory(CacheIn, wIn, sizeof(WORD) * MAXCHANNELS);
+ CopyMemory(CacheOut, wOut, sizeof(WORD) * MAXCHANNELS);
+ }
+
+ output = p -> ToOutput(p, wOut, output);
+ }
+
+ LCMS_WRITE_LOCK(&p ->rwlock);
+ CopyMemory(p->CacheIn, CacheIn, sizeof(WORD) * MAXCHANNELS);
+ CopyMemory(p->CacheOut, CacheOut, sizeof(WORD) * MAXCHANNELS);
+ LCMS_UNLOCK(&p ->rwlock);
+}
+
+
+// Using smelted Matrix/Shaper
+
+static
+void MatrixShaperXFORM(_LPcmsTRANSFORM p,
+ LPVOID in,
+ LPVOID out, unsigned int Size)
+{
+ register LPBYTE accum;
+ register LPBYTE output;
+ WORD wIn[MAXCHANNELS], wOut[MAXCHANNELS];
+ register unsigned int i, n;
+
+
+ accum = (LPBYTE) in;
+ output = (LPBYTE) out;
+ n = Size; // Buffer len
+
+ for (i=0; i < n; i++)
+ {
+ accum = p -> FromInput(p, wIn, accum);
+ cmsEvalMatShaper(p -> SmeltMatShaper, wIn, wOut);
+ output = p -> ToOutput(p, wOut, output);
+ }
+}
+
+
+// Using Named color input table
+
+static
+void NC2deviceXform(_LPcmsTRANSFORM p,
+ LPVOID in,
+ LPVOID out, unsigned int Size)
+{
+
+ register LPBYTE accum;
+ register LPBYTE output;
+ WORD wIn[MAXCHANNELS], wOut[MAXCHANNELS];
+ register unsigned int i;
+
+
+ accum = (LPBYTE) in;
+ output = (LPBYTE) out;
+
+ for (i=0; i < Size; i++) {
+
+ accum = p -> FromInput(p, wIn, accum);
+ CopyMemory(wOut, p ->NamedColorList->List[wIn[0]].DeviceColorant, sizeof(WORD) * MAXCHANNELS);
+ output = p -> ToOutput(p, wOut, output);
+ }
+
+}
+
+
+
+// --------------------------------------------------------------------------
+// Build a LUT based on shape-matrix method.
+
+
+// Some non-conformant gray profiles are using kTCR as L*,
+// this function converts the curve to XYZ PCS.
+
+static
+void FromLstarToXYZ(LPGAMMATABLE g, LPGAMMATABLE gxyz[3])
+{
+ int i;
+ int nPoints = 4096;
+ cmsCIELab Lab;
+ cmsCIEXYZ XYZ;
+ L16PARAMS L16;
+
+ // Setup interpolation across origin
+ cmsCalcL16Params(g ->nEntries, &L16);
+
+ // Allocate curves
+ gxyz[0] = cmsAllocGamma(nPoints);
+ gxyz[1] = cmsAllocGamma(nPoints);
+ gxyz[2] = cmsAllocGamma(nPoints);
+
+ // Transport from Lab to XYZ
+
+ for (i=0; i < nPoints; i++) {
+
+ WORD val = _cmsQuantizeVal(i, nPoints);
+ WORD w = cmsLinearInterpLUT16(val, g->GammaTable, &L16);
+
+ Lab.L = ((double) 100.0 * w ) / 65535.0;
+ Lab.a = Lab.b = 0;
+
+ cmsLab2XYZ(NULL, &XYZ, &Lab);
+
+ // Should be same curve
+ gxyz[0] ->GammaTable[i] = (WORD) floor((65535.0 * XYZ.X) / D50X + 0.5);
+ gxyz[1] ->GammaTable[i] = (WORD) floor((65535.0 * XYZ.Y) / D50Y + 0.5);
+ gxyz[2] ->GammaTable[i] = (WORD) floor((65535.0 * XYZ.Z) / D50Z + 0.5);
+ }
+}
+
+// Monochrome version
+
+static
+LPMATSHAPER cmsBuildGrayInputMatrixShaper(cmsHPROFILE hProfile)
+{
+ cmsCIEXYZ Illuminant;
+ LPGAMMATABLE GrayTRC, Shapes[3];
+ LPMATSHAPER MatShaper;
+ MAT3 Scale;
+
+ GrayTRC = cmsReadICCGamma(hProfile, icSigGrayTRCTag); // Y
+ if (GrayTRC == NULL) return NULL;
+
+ cmsTakeIluminant(&Illuminant, hProfile);
+
+ if (cmsGetPCS(hProfile) == icSigLabData) {
+
+ // Fixup for Lab monochrome
+ FromLstarToXYZ(GrayTRC, Shapes);
+ }
+ else {
+ Shapes[0] = cmsDupGamma(GrayTRC);
+ Shapes[1] = cmsDupGamma(GrayTRC);
+ Shapes[2] = cmsDupGamma(GrayTRC);
+ }
+
+ if (!Shapes[0] || !Shapes[1] || !Shapes[2])
+ return NULL;
+
+ cmsFreeGamma(GrayTRC);
+
+ // R=G=B as precondition
+
+ VEC3init(&Scale.v[0], Illuminant.X/3, Illuminant.X/3, Illuminant.X/3);
+ VEC3init(&Scale.v[1], Illuminant.Y/3, Illuminant.Y/3, Illuminant.Y/3);
+ VEC3init(&Scale.v[2], Illuminant.Z/3, Illuminant.Z/3, Illuminant.Z/3);
+
+
+ MatShaper = cmsAllocMatShaper(&Scale, Shapes, MATSHAPER_INPUT);
+ cmsFreeGammaTriple(Shapes);
+ return MatShaper;
+
+}
+
+
+// Monochrome as output
+
+static
+LPMATSHAPER cmsBuildGrayOutputMatrixShaper(cmsHPROFILE hProfile)
+{
+ cmsCIEXYZ Illuminant;
+ LPGAMMATABLE GrayTRC, Shapes[3];
+ LPMATSHAPER MatShaper;
+ MAT3 Scale;
+
+ cmsTakeIluminant(&Illuminant, hProfile);
+
+ // That is a special case for non-compliant profiles.
+
+ if (cmsGetPCS(hProfile) == icSigLabData) {
+
+ LPGAMMATABLE Shapes1[3];
+
+ GrayTRC = cmsReadICCGamma(hProfile, icSigGrayTRCTag);
+ if (GrayTRC == NULL) return NULL;
+ FromLstarToXYZ(GrayTRC, Shapes1);
+
+
+
+ // Reversing must be done after curve translation
+
+ Shapes[0] = cmsReverseGamma(Shapes1[0]->nEntries, Shapes1[0]);
+ Shapes[1] = cmsReverseGamma(Shapes1[1]->nEntries, Shapes1[1]);
+ Shapes[2] = cmsReverseGamma(Shapes1[2]->nEntries, Shapes1[2]);
+
+ cmsFreeGammaTriple(Shapes1);
+
+ }
+ else {
+
+ // Normal case
+
+ GrayTRC = cmsReadICCGammaReversed(hProfile, icSigGrayTRCTag); // Y
+ if (GrayTRC == NULL) return NULL;
+
+ Shapes[0] = cmsDupGamma(GrayTRC);
+ Shapes[1] = cmsDupGamma(GrayTRC);
+ Shapes[2] = cmsDupGamma(GrayTRC);
+ }
+
+ if (!Shapes[0] || !Shapes[1] || !Shapes[2])
+ return NULL;
+
+ cmsFreeGamma(GrayTRC);
+
+ VEC3init(&Scale.v[0], 0, 1.0/Illuminant.Y, 0);
+ VEC3init(&Scale.v[1], 0, 1.0/Illuminant.Y, 0);
+ VEC3init(&Scale.v[2], 0, 1.0/Illuminant.Y, 0);
+
+
+ MatShaper = cmsAllocMatShaper(&Scale, Shapes, MATSHAPER_OUTPUT);
+ cmsFreeGammaTriple(Shapes);
+ return MatShaper;
+
+}
+
+
+
+// Input matrix, only in XYZ
+
+LPMATSHAPER cmsBuildInputMatrixShaper(cmsHPROFILE InputProfile)
+{
+ MAT3 DoubleMat;
+ LPGAMMATABLE Shapes[3];
+ LPMATSHAPER InMatSh;
+
+ // Check if this is a grayscale profile. If so, build
+ // appropiate conversion tables. The tables are the PCS
+ // iluminant, scaled across GrayTRC
+
+ if (cmsGetColorSpace(InputProfile) == icSigGrayData)
+ {
+ return cmsBuildGrayInputMatrixShaper(InputProfile);
+ }
+
+ if (!cmsReadICCMatrixRGB2XYZ(&DoubleMat, InputProfile))
+ return NULL;
+
+ Shapes[0] = cmsReadICCGamma(InputProfile, icSigRedTRCTag);
+ Shapes[1] = cmsReadICCGamma(InputProfile, icSigGreenTRCTag);
+ Shapes[2] = cmsReadICCGamma(InputProfile, icSigBlueTRCTag);
+
+ if (!Shapes[0] || !Shapes[1] || !Shapes[2])
+ return NULL;
+
+ InMatSh = cmsAllocMatShaper(&DoubleMat, Shapes, MATSHAPER_INPUT);
+
+ cmsFreeGammaTriple(Shapes);
+
+ return InMatSh;
+}
+
+
+// Output style matrix-shaper
+
+
+LPMATSHAPER cmsBuildOutputMatrixShaper(cmsHPROFILE OutputProfile)
+{
+ MAT3 DoubleMat, DoubleInv;
+ LPGAMMATABLE InverseShapes[3];
+ LPMATSHAPER OutMatSh;
+
+
+
+ if (cmsGetColorSpace(OutputProfile) == icSigGrayData)
+ {
+ return cmsBuildGrayOutputMatrixShaper(OutputProfile);
+ }
+
+
+ if (!cmsReadICCMatrixRGB2XYZ(&DoubleMat, OutputProfile))
+ return NULL;
+
+ if (MAT3inverse(&DoubleMat, &DoubleInv) < 0)
+ return NULL;
+
+
+ InverseShapes[0] = cmsReadICCGammaReversed(OutputProfile, icSigRedTRCTag);
+ InverseShapes[1] = cmsReadICCGammaReversed(OutputProfile, icSigGreenTRCTag);
+ InverseShapes[2] = cmsReadICCGammaReversed(OutputProfile, icSigBlueTRCTag);
+
+ if (InverseShapes[0] == NULL ||
+ InverseShapes[1] == NULL ||
+ InverseShapes[2] == NULL) return NULL;
+
+ OutMatSh = cmsAllocMatShaper(&DoubleInv, InverseShapes, MATSHAPER_OUTPUT);
+
+ cmsFreeGammaTriple(InverseShapes);
+
+ return OutMatSh;
+}
+
+
+
+// This function builds a transform matrix chaining parameters
+
+static
+LCMSBOOL cmsBuildSmeltMatShaper(_LPcmsTRANSFORM p)
+{
+ MAT3 From, To, ToInv, Transfer;
+ LPGAMMATABLE In[3], InverseOut[3];
+
+
+ if (!cmsReadICCMatrixRGB2XYZ(&From, p -> InputProfile))
+ return FALSE;
+
+
+ if (!cmsReadICCMatrixRGB2XYZ(&To, p -> OutputProfile))
+ return FALSE;
+
+ // invert dest
+
+ if (MAT3inverse(&To, &ToInv) < 0)
+ return FALSE;
+
+ // Multiply
+ MAT3per(&Transfer, &ToInv, &From);
+
+
+ // Read gamma curves
+
+ In[0] = cmsReadICCGamma(p -> InputProfile, icSigRedTRCTag);
+ In[1] = cmsReadICCGamma(p -> InputProfile, icSigGreenTRCTag);
+ In[2] = cmsReadICCGamma(p -> InputProfile, icSigBlueTRCTag);
+
+ if (!In[0] || !In[1] || !In[2])
+ return FALSE;
+
+
+ InverseOut[0] = cmsReadICCGammaReversed(p -> OutputProfile, icSigRedTRCTag);
+ InverseOut[1] = cmsReadICCGammaReversed(p -> OutputProfile, icSigGreenTRCTag);
+ InverseOut[2] = cmsReadICCGammaReversed(p -> OutputProfile, icSigBlueTRCTag);
+
+ if (!InverseOut[0] || !InverseOut[1] || !InverseOut[2]) {
+ cmsFreeGammaTriple(In);
+ return FALSE;
+ }
+
+ p -> SmeltMatShaper = cmsAllocMatShaper2(&Transfer, In, InverseOut, MATSHAPER_ALLSMELTED);
+
+ cmsFreeGammaTriple(In);
+ cmsFreeGammaTriple(InverseOut);
+
+ return (p -> SmeltMatShaper != NULL);
+}
+
+
+
+
+// Conversion between PCS ------------------------------------------
+
+// Identifies intent archieved by LUT
+
+static
+int GetPhase(cmsHPROFILE hProfile)
+{
+ switch (cmsGetPCS(hProfile)) {
+
+ case icSigXYZData: return XYZRel;
+
+ case icSigLabData: return LabRel;
+
+ default: cmsSignalError(LCMS_ERRC_ABORTED, "Invalid PCS");
+ }
+
+ return XYZRel;
+}
+
+
+
+
+static
+void TakeConversionRoutines(_LPcmsTRANSFORM p, int DoBPC)
+{
+ cmsCIEXYZ BlackPointIn, WhitePointIn, IlluminantIn;
+ cmsCIEXYZ BlackPointOut, WhitePointOut, IlluminantOut;
+ cmsCIEXYZ BlackPointProof, WhitePointProof, IlluminantProof;
+ MAT3 ChromaticAdaptationMatrixIn, ChromaticAdaptationMatrixOut;
+ MAT3 ChromaticAdaptationMatrixProof;
+
+
+ cmsTakeIluminant(&IlluminantIn, p -> InputProfile);
+ cmsTakeMediaWhitePoint(&WhitePointIn, p -> InputProfile);
+ cmsTakeMediaBlackPoint(&BlackPointIn, p -> InputProfile);
+ cmsReadChromaticAdaptationMatrix(&ChromaticAdaptationMatrixIn, p -> InputProfile);
+
+ cmsTakeIluminant(&IlluminantOut, p -> OutputProfile);
+ cmsTakeMediaWhitePoint(&WhitePointOut, p -> OutputProfile);
+ cmsTakeMediaBlackPoint(&BlackPointOut, p -> OutputProfile);
+ cmsReadChromaticAdaptationMatrix(&ChromaticAdaptationMatrixOut, p -> OutputProfile);
+
+
+ if (p -> Preview == NULL && p ->Gamut == NULL) // Non-proofing
+ {
+ if (p ->Intent == INTENT_PERCEPTUAL ||
+ p ->Intent == INTENT_SATURATION) {
+
+
+ // For v4 profiles, Perceptual PCS has a reference black point
+ // which v2 profiles should scale to.
+
+ if ((cmsGetProfileICCversion(p ->InputProfile) >= 0x4000000) ||
+ (cmsGetProfileICCversion(p ->OutputProfile) >= 0x4000000)) {
+
+ DoBPC = TRUE;
+ }
+ }
+
+ // Black point compensation does not apply to absolute intent
+
+ if (p ->Intent == INTENT_ABSOLUTE_COLORIMETRIC)
+ DoBPC = FALSE;
+
+ // Black point compensation does not apply to devicelink profiles
+
+ if (cmsGetDeviceClass(p ->InputProfile) == icSigLinkClass)
+ DoBPC = FALSE;
+
+ if (cmsGetDeviceClass(p ->OutputProfile) == icSigLinkClass)
+ DoBPC = FALSE;
+
+ if (DoBPC) {
+
+ // Detect Black points
+
+ cmsDetectBlackPoint(&BlackPointIn, p->InputProfile, p->Intent, 0);
+ cmsDetectBlackPoint(&BlackPointOut, p->OutputProfile, p->Intent, 0);
+
+ // If equal black points, then do nothing. This often applies to BP=0
+
+ if (BlackPointIn.X == BlackPointOut.X &&
+ BlackPointIn.Y == BlackPointOut.Y &&
+ BlackPointIn.Z == BlackPointOut.Z)
+ DoBPC = FALSE;
+
+
+ }
+
+ cmsChooseCnvrt(p -> Intent == INTENT_ABSOLUTE_COLORIMETRIC,
+
+ p -> Phase1,
+ &BlackPointIn,
+ &WhitePointIn,
+ &IlluminantIn,
+ &ChromaticAdaptationMatrixIn,
+
+ p -> Phase3,
+ &BlackPointOut,
+ &WhitePointOut,
+ &IlluminantOut,
+ &ChromaticAdaptationMatrixOut,
+
+ DoBPC,
+ p ->AdaptationState,
+ &p->Stage1,
+ &p->m1, &p->of1);
+
+ }
+ else // Proofing
+ {
+
+
+ cmsTakeIluminant(&IlluminantProof, p -> PreviewProfile);
+ cmsTakeMediaWhitePoint(&WhitePointProof, p -> PreviewProfile);
+ cmsTakeMediaBlackPoint(&BlackPointProof, p -> PreviewProfile);
+ cmsReadChromaticAdaptationMatrix(&ChromaticAdaptationMatrixProof, p -> PreviewProfile);
+
+ if (DoBPC) {
+
+ cmsDetectBlackPoint(&BlackPointProof, p->PreviewProfile, p->Intent, 0);
+ cmsDetectBlackPoint(&BlackPointIn, p->InputProfile, p->Intent, 0);
+ cmsDetectBlackPoint(&BlackPointOut, p->OutputProfile, p->Intent, 0);
+
+ // Reality check
+
+ if (BlackPointIn.X == BlackPointProof.X &&
+ BlackPointIn.Y == BlackPointProof.Y &&
+ BlackPointIn.Z == BlackPointProof.Z)
+ DoBPC = FALSE;
+
+
+ }
+
+
+
+ cmsChooseCnvrt(p -> Intent == INTENT_ABSOLUTE_COLORIMETRIC,
+
+ p -> Phase1,
+ &BlackPointIn,
+ &WhitePointIn,
+ &IlluminantIn,
+ &ChromaticAdaptationMatrixIn,
+
+ p -> Phase2,
+ &BlackPointProof,
+ &WhitePointProof,
+ &IlluminantProof,
+ &ChromaticAdaptationMatrixProof,
+ DoBPC,
+ p ->AdaptationState,
+ &p->Stage1,
+ &p->m1, &p->of1);
+
+ cmsChooseCnvrt(p -> ProofIntent == INTENT_ABSOLUTE_COLORIMETRIC,
+
+ p -> Phase2,
+ &BlackPointProof,
+ &WhitePointProof,
+ &IlluminantProof,
+ &ChromaticAdaptationMatrixProof,
+
+ p -> Phase3,
+ &BlackPointOut,
+ &WhitePointOut,
+ &IlluminantOut,
+ &ChromaticAdaptationMatrixOut,
+ 0,
+ 0.0,
+ &p->Stage2,
+ &p->m2, &p->of2);
+ }
+
+}
+
+
+// Check colorspace
+
+static
+LCMSBOOL IsProperColorSpace(cmsHPROFILE hProfile, DWORD dwFormat, LCMSBOOL lUsePCS)
+{
+ int Space = T_COLORSPACE(dwFormat);
+
+ if (Space == PT_ANY) return TRUE;
+
+ if (lUsePCS)
+ return (Space == _cmsLCMScolorSpace(cmsGetPCS(hProfile)));
+ else
+ return (Space == _cmsLCMScolorSpace(cmsGetColorSpace(hProfile)));
+}
+
+
+// Auxiliary: allocate transform struct and set to defaults
+
+static
+_LPcmsTRANSFORM AllocEmptyTransform(void)
+{
+ // Allocate needed memory
+
+ _LPcmsTRANSFORM p = (_LPcmsTRANSFORM) _cmsMalloc(sizeof(_cmsTRANSFORM));
+ if (!p) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "cmsCreateTransform: _cmsMalloc() failed");
+ return NULL;
+ }
+
+ ZeroMemory(p, sizeof(_cmsTRANSFORM));
+
+ // Initialize default methods
+
+ p -> xform = NULL;
+ p -> Intent = INTENT_PERCEPTUAL;
+ p -> ProofIntent = INTENT_ABSOLUTE_COLORIMETRIC;
+ p -> DoGamutCheck = FALSE;
+ p -> InputProfile = NULL;
+ p -> OutputProfile = NULL;
+ p -> PreviewProfile = NULL;
+ p -> Preview = NULL;
+ p -> Gamut = NULL;
+ p -> DeviceLink = NULL;
+ p -> InMatShaper = NULL;
+ p -> OutMatShaper = NULL;
+ p -> SmeltMatShaper = NULL;
+ p -> NamedColorList = NULL;
+ p -> EntryColorSpace = (icColorSpaceSignature) 0;
+ p -> ExitColorSpace = (icColorSpaceSignature) 0;
+ p -> AdaptationState = GlobalAdaptationState;
+
+ LCMS_CREATE_LOCK(&p->rwlock);
+
+ return p;
+}
+
+
+// Identify whatever a transform is to be cached
+
+static
+void SetPrecalculatedTransform(_LPcmsTRANSFORM p)
+{
+ if ((p->dwOriginalFlags & cmsFLAGS_GAMUTCHECK) && p ->GamutCheck != NULL) {
+
+ p -> xform = PrecalculatedXFORMGamutCheck;
+
+ if (!(p->dwOriginalFlags & cmsFLAGS_NOTCACHE)) {
+
+ ZeroMemory(p ->CacheIn, sizeof(WORD) * MAXCHANNELS);
+ TransformOnePixelWithGamutCheck(p, p->CacheIn, p ->CacheOut);
+ p ->xform = CachedXFORMGamutCheck;
+ }
+
+ }
+ else {
+
+ p -> xform = PrecalculatedXFORM;
+
+ if (!(p->dwOriginalFlags & cmsFLAGS_NOTCACHE)) {
+
+ ZeroMemory(p ->CacheIn, sizeof(WORD) * MAXCHANNELS);
+ cmsEvalLUT(p ->DeviceLink, p->CacheIn, p ->CacheOut);
+ p ->xform = CachedXFORM;
+ }
+ }
+}
+
+
+// Transform is identified as device-link
+static
+cmsHPROFILE CreateDeviceLinkTransform(_LPcmsTRANSFORM p)
+{
+
+ if (!IsProperColorSpace(p->InputProfile, p->InputFormat, FALSE)) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "Device link is operating on wrong colorspace on input");
+ return NULL;
+ }
+
+ if (!IsProperColorSpace(p->InputProfile, p->OutputFormat, TRUE)) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "Device link is operating on wrong colorspace on output");
+ return NULL;
+ }
+
+ // Device link does only have AToB0Tag (ICC-Spec 1998/09)
+
+ p->DeviceLink = cmsReadICCLut(p->InputProfile, icSigAToB0Tag);
+
+ if (!p->DeviceLink) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "Noncompliant device-link profile");
+ cmsDeleteTransform((cmsHTRANSFORM) p);
+ return NULL;
+ }
+
+ if (p ->PreviewProfile != NULL) {
+ cmsSignalError(LCMS_ERRC_WARNING, "Proofing not supported on device link transforms");
+ }
+
+ if (p ->OutputProfile != NULL) {
+ cmsSignalError(LCMS_ERRC_WARNING, "Output profile should be NULL, since this is a device-link transform");
+ }
+
+ p -> Phase1 = -1;
+ p -> Phase2 = -1;
+ p -> Phase3 = -1;
+
+ SetPrecalculatedTransform(p);
+
+ p -> EntryColorSpace = cmsGetColorSpace(p -> InputProfile);
+ p -> ExitColorSpace = cmsGetPCS(p -> InputProfile);
+
+ if (p ->EntryColorSpace == icSigRgbData ||
+ p ->EntryColorSpace == icSigCmyData) {
+
+ p->DeviceLink -> CLut16params.Interp3D = cmsTetrahedralInterp16;
+ }
+
+ // Precalculated device-link profile is ready
+ return (cmsHTRANSFORM) p;
+}
+
+
+// Transform that includes proofing
+static
+void CreateProof(_LPcmsTRANSFORM p, icTagSignature *ToTagPtr)
+
+{
+ icTagSignature ProofTag;
+
+ if (p -> dwOriginalFlags & cmsFLAGS_SOFTPROOFING) {
+
+ // Apr-15, 2002 - Too much profiles does have bogus content
+ // on preview tag, so I do compute it by my own.
+
+ p -> Preview = _cmsComputeSoftProofLUT(p ->PreviewProfile, p ->Intent);
+ p -> Phase2 = LabRel;
+
+ // That's a proofing transfor, so use second intent for output.
+
+ *ToTagPtr = PCS2Device[p->ProofIntent];
+
+ if (p -> Preview == NULL) {
+
+ ProofTag = Preview[p -> Intent];
+
+ if (!cmsIsTag(p ->PreviewProfile, ProofTag)) {
+
+ ProofTag = Preview[0];
+ if (!cmsIsTag(p ->PreviewProfile, ProofTag))
+ ProofTag = (icTagSignature)0;
+ }
+
+ if (ProofTag) {
+
+ p -> Preview = cmsReadICCLut(p ->PreviewProfile, ProofTag);
+ p -> Phase2 = GetPhase(p ->PreviewProfile);
+
+ }
+ else
+ {
+ p -> Preview = NULL;
+ p ->PreviewProfile = NULL;
+ cmsSignalError(LCMS_ERRC_WARNING, "Sorry, the proof profile has not previewing capabilities");
+ }
+ }
+
+ }
+
+
+ // Aug-31, 2001 - Too much profiles does have bogus content
+ // on gamut tag, so I do compute it by my own.
+
+ if ((p -> dwOriginalFlags & cmsFLAGS_GAMUTCHECK) && (p -> dwOriginalFlags & cmsFLAGS_NOTPRECALC)) {
+
+
+ p -> Gamut = _cmsComputeGamutLUT(p->PreviewProfile, p ->Intent);
+ p -> Phase2 = LabRel;
+
+ if (p -> Gamut == NULL) {
+
+ // Profile goes only in one direction... try to see
+ // if profile has the tag, and use it, no matter it
+ // could be bogus. This is the last chance!
+
+ if (cmsIsTag(p ->PreviewProfile, icSigGamutTag)) {
+
+ p -> Gamut = cmsReadICCLut(p ->PreviewProfile, icSigGamutTag);
+
+ }
+ else {
+
+ // Nope, cannot be done.
+
+ cmsSignalError(LCMS_ERRC_WARNING, "Sorry, the proof profile has not gamut checking capabilities");
+ p -> Gamut = NULL;
+ }
+ }
+
+ }
+
+}
+
+// Choose the adequate transform routine
+
+static
+_LPcmsTRANSFORM PickTransformRoutine(_LPcmsTRANSFORM p,
+ icTagSignature *FromTagPtr,
+ icTagSignature *ToTagPtr)
+{
+
+
+
+
+ // Is a named color profile?
+ if (cmsGetDeviceClass(p->InputProfile) == icSigNamedColorClass) {
+
+ // Yes, and used as input
+ p ->FromDevice = NC2toPCS;
+ }
+ else {
+ // Can we optimize matrix-shaper only transform?
+
+ if ((*FromTagPtr == 0) &&
+ (*ToTagPtr == 0) &&
+ (!p->PreviewProfile) &&
+ (p -> Intent != INTENT_ABSOLUTE_COLORIMETRIC) &&
+ (p -> EntryColorSpace == icSigRgbData) &&
+ (p -> ExitColorSpace == icSigRgbData) &&
+ !(p -> dwOriginalFlags & cmsFLAGS_BLACKPOINTCOMPENSATION)) {
+
+ // Yes... try to smelt matrix-shapers
+ p -> xform = MatrixShaperXFORM;
+ p -> dwOriginalFlags |= cmsFLAGS_NOTPRECALC;
+
+ if (!cmsBuildSmeltMatShaper(p))
+ {
+ cmsSignalError(LCMS_ERRC_ABORTED, "unable to smelt shaper-matrix, required tags missing");
+ return NULL;
+ }
+
+ p -> Phase1 = p -> Phase3 = XYZRel;
+ return p;
+
+ }
+
+ // No, is a transform involving LUT
+
+ if (*FromTagPtr != 0) {
+
+ p -> FromDevice = LUTtoPCS;
+ p -> Device2PCS = cmsReadICCLut(p -> InputProfile, *FromTagPtr);
+ if (!p -> Device2PCS) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "profile is unsuitable for input");
+ return NULL;
+ }
+
+ }
+ else
+ {
+ p -> FromDevice = ShaperMatrixToPCS;
+ p -> InMatShaper = cmsBuildInputMatrixShaper(p -> InputProfile);
+
+ if (!p ->InMatShaper) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "profile is unsuitable for input");
+ return NULL;
+ }
+
+ p -> Phase1 = XYZRel;
+
+ }
+ }
+
+ if (*ToTagPtr != 0) {
+
+ p -> ToDevice = PCStoLUT;
+ p -> PCS2Device = cmsReadICCLut(p -> OutputProfile, *ToTagPtr);
+ if (!p -> PCS2Device) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "profile is unsuitable for output");
+ return NULL;
+ }
+
+ }
+ else
+ {
+ p -> ToDevice = PCStoShaperMatrix;
+ p -> OutMatShaper = cmsBuildOutputMatrixShaper(p->OutputProfile);
+
+ if (!p -> OutMatShaper) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "profile is unsuitable for output");
+ return NULL;
+ }
+ p -> Phase3 = XYZRel;
+
+ }
+
+
+ return p;
+}
+
+
+
+
+// Create a transform.
+
+cmsHTRANSFORM LCMSEXPORT cmsCreateProofingTransform(cmsHPROFILE InputProfile,
+ DWORD InputFormat,
+ cmsHPROFILE OutputProfile,
+ DWORD OutputFormat,
+ cmsHPROFILE ProofingProfile,
+ int nIntent,
+ int ProofingIntent,
+ DWORD dwFlags)
+
+{
+ _LPcmsTRANSFORM p;
+ icTagSignature FromTag;
+ icTagSignature ToTag;
+
+ if (nIntent < 0 || nIntent > 3 ||
+ ProofingIntent < 0 || ProofingIntent > 3) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "cmsCreateTransform: intent mismatch");
+ return NULL;
+ }
+
+ p = AllocEmptyTransform();
+ if (p == NULL) return NULL;
+
+ p -> xform = NormalXFORM;
+ p -> Intent = nIntent;
+ p -> ProofIntent = ProofingIntent;
+ p -> DoGamutCheck = FALSE;
+ p -> InputProfile = InputProfile;
+ p -> OutputProfile = OutputProfile;
+ p -> PreviewProfile = ProofingProfile;
+ p -> InputFormat = InputFormat;
+ p -> OutputFormat = OutputFormat;
+ p -> dwOriginalFlags = dwFlags;
+
+ p -> lInputV4Lab = p ->lOutputV4Lab = FALSE;
+
+
+ p -> FromInput = _cmsIdentifyInputFormat(p, InputFormat);
+ p -> ToOutput = _cmsIdentifyOutputFormat(p, OutputFormat);
+
+ // Null transform can be done without profiles
+ if ((p->dwOriginalFlags & cmsFLAGS_NULLTRANSFORM) ||
+ ((InputProfile == NULL) &&
+ (OutputProfile == NULL))) {
+
+ p -> xform = NullXFORM;
+ return (cmsHTRANSFORM) p;
+ }
+
+ // From here we need at least one input profile
+ if (InputProfile == NULL) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "Input profile cannot be NULL!");
+ cmsDeleteTransform((cmsHTRANSFORM) p);
+ return NULL;
+ }
+
+
+ // Device link are means to store precalculated transform grids.
+ if (cmsGetDeviceClass(InputProfile) == icSigLinkClass) {
+
+ return CreateDeviceLinkTransform(p);
+ }
+
+ if (!IsProperColorSpace(InputProfile, InputFormat, FALSE)) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "Input profile is operating on wrong colorspace");
+ cmsDeleteTransform((cmsHTRANSFORM) p);
+ return NULL;
+ }
+
+ p ->EntryColorSpace = cmsGetColorSpace(InputProfile);
+
+ // Oct-21-2002: Added named color transforms
+ if (cmsGetDeviceClass(InputProfile) == icSigNamedColorClass) {
+
+ if (p ->NamedColorList == NULL)
+ p ->NamedColorList = cmsAllocNamedColorList(0);
+
+ cmsReadICCnamedColorList(p, InputProfile, icSigNamedColor2Tag);
+
+ // Special case. If output profile == NULL, then the transform gives
+ // device values from named colors.
+
+ if (OutputProfile == NULL) {
+
+ p ->ExitColorSpace = p -> EntryColorSpace;
+ p ->xform = NC2deviceXform;
+ return (cmsHTRANSFORM) p;
+ }
+
+ // Named color doesn't precalc anything
+ p -> dwOriginalFlags |= cmsFLAGS_NOTPRECALC;
+ }
+
+
+ // From here we need also output profile.
+ if (OutputProfile == NULL) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "Output profile cannot be NULL!");
+ cmsDeleteTransform((cmsHTRANSFORM) p);
+ return NULL;
+ }
+
+
+ if (!IsProperColorSpace(OutputProfile, OutputFormat, FALSE)) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "Output profile is operating on wrong colorspace");
+ cmsDeleteTransform((cmsHTRANSFORM) p);
+ return NULL;
+ }
+
+ p -> ExitColorSpace = cmsGetColorSpace(OutputProfile);
+
+ // Named color only on input
+ if (cmsGetDeviceClass(OutputProfile) == icSigNamedColorClass) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "Named color profiles are not supported as output");
+ cmsDeleteTransform((cmsHTRANSFORM) p);
+ return NULL;
+ }
+
+ p -> Phase1 = GetPhase(InputProfile);
+ p -> Phase2 = -1;
+ p -> Phase3 = GetPhase(OutputProfile);
+
+ // Try to locate a LUT
+
+ FromTag = Device2PCS[nIntent];
+ ToTag = PCS2Device[nIntent];
+
+ if (!cmsIsTag(InputProfile, FromTag)) {
+
+ FromTag = Device2PCS[0];
+
+ if (!cmsIsTag(InputProfile, FromTag)) {
+ FromTag = (icTagSignature)0;
+ }
+ }
+
+ // If proofing is needed, add required tags/parameters
+ if (ProofingProfile)
+ CreateProof(p, &ToTag);
+
+
+ if (!cmsIsTag(OutputProfile, ToTag)) {
+
+ ToTag = PCS2Device[0];
+
+ // 12-Dec-2003, Abstract profiles can be placed as output and still using AToB0
+ if (cmsGetDeviceClass(OutputProfile) == icSigAbstractClass) {
+
+ if (!cmsIsTag(OutputProfile, ToTag)) {
+ ToTag = (icTagSignature) icSigAToB0Tag;
+ }
+ }
+
+ if (!cmsIsTag(OutputProfile, ToTag))
+ ToTag = (icTagSignature)0;
+ }
+
+
+ if (p-> dwOriginalFlags & cmsFLAGS_MATRIXINPUT)
+ FromTag = (icTagSignature)0;
+
+ if (p -> dwOriginalFlags & cmsFLAGS_MATRIXOUTPUT)
+ ToTag = (icTagSignature)0;
+
+
+
+ if (PickTransformRoutine(p, &FromTag, &ToTag) == NULL) {
+
+ cmsDeleteTransform((cmsHTRANSFORM) p);
+ return NULL;
+
+ }
+
+ TakeConversionRoutines(p, dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION);
+
+ if (!(p -> dwOriginalFlags & cmsFLAGS_NOTPRECALC)) {
+
+ LPLUT DeviceLink;
+ LPLUT GamutCheck = NULL;
+
+
+ if (p ->EntryColorSpace == icSigCmykData &&
+ p ->ExitColorSpace == icSigCmykData &&
+ (dwFlags & cmsFLAGS_PRESERVEBLACK)) {
+
+ DeviceLink = _cmsPrecalculateBlackPreservingDeviceLink((cmsHTRANSFORM) p, dwFlags);
+
+ // Cannot be done at all?
+ if (DeviceLink == NULL)
+ DeviceLink = _cmsPrecalculateDeviceLink((cmsHTRANSFORM) p, dwFlags);
+
+ }
+ else {
+
+ DeviceLink = _cmsPrecalculateDeviceLink((cmsHTRANSFORM) p, dwFlags);
+ }
+
+ // Allow to specify cmsFLAGS_GAMUTCHECK, even if no proofing profile is given
+ if ((p ->PreviewProfile != NULL) && (p -> dwOriginalFlags & cmsFLAGS_GAMUTCHECK)) {
+
+ GamutCheck = _cmsPrecalculateGamutCheck((cmsHTRANSFORM) p);
+ }
+
+ // If input colorspace is Rgb, Cmy, then use tetrahedral interpolation
+ // for speed reasons (it only works well on spaces on Luma is diagonal, and
+ // not if luma is in separate channel)
+ if (p ->EntryColorSpace == icSigRgbData ||
+ p ->EntryColorSpace == icSigCmyData) {
+
+
+ cmsCalcCLUT16ParamsEx(DeviceLink->CLut16params.nSamples,
+ DeviceLink->CLut16params.nInputs,
+ DeviceLink->CLut16params.nOutputs,
+ TRUE, &DeviceLink->CLut16params);
+
+ }
+
+ // If this is a 8-bit transform, optimize LUT further.
+
+ if ((T_BYTES(InputFormat) == 1) && (T_CHANNELS(InputFormat) == 3)) {
+
+ DeviceLink = _cmsBlessLUT8(DeviceLink);
+ if (DeviceLink == NULL) return NULL;
+
+ }
+
+
+ p ->GamutCheck = GamutCheck;
+
+ if (DeviceLink) {
+
+ p ->DeviceLink = DeviceLink;
+
+ if ((nIntent != INTENT_ABSOLUTE_COLORIMETRIC) &&
+ !(p -> dwOriginalFlags & cmsFLAGS_NOWHITEONWHITEFIXUP))
+
+ _cmsFixWhiteMisalignment(p);
+
+ }
+ else
+ {
+
+ cmsSignalError(LCMS_ERRC_ABORTED,
+ "Cannot precalculate %d->%d channels transform!",
+ T_CHANNELS(InputFormat), T_CHANNELS(OutputFormat));
+
+ cmsDeleteTransform(p);
+ return NULL;
+ }
+
+
+ SetPrecalculatedTransform(p);
+
+
+ }
+
+ // Re-Identify formats
+ p -> FromInput = _cmsIdentifyInputFormat(p, InputFormat);
+ p -> ToOutput = _cmsIdentifyOutputFormat(p, OutputFormat);
+
+
+ return p;
+}
+
+
+// Wrapper por simpler non-proofing transforms.
+
+cmsHTRANSFORM LCMSEXPORT cmsCreateTransform(cmsHPROFILE Input,
+ DWORD InputFormat,
+ cmsHPROFILE Output,
+ DWORD OutputFormat,
+ int Intent,
+ DWORD dwFlags)
+
+{
+ return cmsCreateProofingTransform(Input, InputFormat,
+ Output, OutputFormat,
+ NULL,
+ Intent, INTENT_ABSOLUTE_COLORIMETRIC,
+ dwFlags);
+}
+
+
+// Profiles are *NOT* closed
+
+void LCMSEXPORT cmsDeleteTransform(cmsHTRANSFORM hTransform)
+{
+ _LPcmsTRANSFORM p = (_LPcmsTRANSFORM) (LPSTR) hTransform;
+
+ if (p -> Device2PCS)
+ cmsFreeLUT(p -> Device2PCS);
+ if (p -> PCS2Device)
+ cmsFreeLUT(p -> PCS2Device);
+ if (p -> Gamut)
+ cmsFreeLUT(p -> Gamut);
+ if (p -> Preview)
+ cmsFreeLUT(p -> Preview);
+ if (p -> DeviceLink)
+ cmsFreeLUT(p -> DeviceLink);
+ if (p -> InMatShaper)
+ cmsFreeMatShaper(p -> InMatShaper);
+ if (p -> OutMatShaper)
+ cmsFreeMatShaper(p -> OutMatShaper);
+ if (p -> SmeltMatShaper)
+ cmsFreeMatShaper(p -> SmeltMatShaper);
+ if (p ->NamedColorList)
+ cmsFreeNamedColorList(p ->NamedColorList);
+ if (p -> GamutCheck)
+ cmsFreeLUT(p -> GamutCheck);
+
+ LCMS_FREE_LOCK(&p->rwlock);
+
+ _cmsFree((void *) p);
+}
+
+
+// Apply transform code
+void LCMSEXPORT cmsDoTransform(cmsHTRANSFORM Transform,
+ LPVOID InputBuffer,
+ LPVOID OutputBuffer, unsigned int Size)
+
+{
+
+ _LPcmsTRANSFORM p = (_LPcmsTRANSFORM) (LPSTR) Transform;
+
+ p -> StrideIn = p -> StrideOut = Size;
+
+ p -> xform(p, InputBuffer, OutputBuffer, Size);
+
+}
+
+
+void LCMSEXPORT cmsSetAlarmCodes(int r, int g, int b)
+{
+ AlarmR = RGB_8_TO_16(r);
+ AlarmG = RGB_8_TO_16(g);
+ AlarmB = RGB_8_TO_16(b);
+}
+
+void LCMSEXPORT cmsGetAlarmCodes(int *r, int *g, int *b)
+{
+ *r = RGB_16_TO_8(AlarmR);
+ *g = RGB_16_TO_8(AlarmG);
+ *b = RGB_16_TO_8(AlarmB);
+}
+
+// Returns TRUE if the profile is implemented as matrix-shaper
+
+LCMSBOOL LCMSEXPORT _cmsIsMatrixShaper(cmsHPROFILE hProfile)
+{
+ switch (cmsGetColorSpace(hProfile)) {
+
+ case icSigGrayData:
+
+ return cmsIsTag(hProfile, icSigGrayTRCTag);
+
+ case icSigRgbData:
+
+ return (cmsIsTag(hProfile, icSigRedColorantTag) &&
+ cmsIsTag(hProfile, icSigGreenColorantTag) &&
+ cmsIsTag(hProfile, icSigBlueColorantTag) &&
+ cmsIsTag(hProfile, icSigRedTRCTag) &&
+ cmsIsTag(hProfile, icSigGreenTRCTag) &&
+ cmsIsTag(hProfile, icSigBlueTRCTag));
+
+ default:
+
+ return FALSE;
+ }
+}
+
+
+LCMSBOOL LCMSEXPORT cmsIsIntentSupported(cmsHPROFILE hProfile,
+ int Intent, int UsedDirection)
+{
+
+ icTagSignature* TagTable;
+
+ // Device link profiles only implements the intent in header
+
+ if (cmsGetDeviceClass(hProfile) != icSigLinkClass) {
+
+ switch (UsedDirection) {
+
+ case LCMS_USED_AS_INPUT: TagTable = Device2PCS; break;
+ case LCMS_USED_AS_OUTPUT:TagTable = PCS2Device; break;
+ case LCMS_USED_AS_PROOF: TagTable = Preview; break;
+
+ default:
+ cmsSignalError(LCMS_ERRC_ABORTED, "Unexpected direction (%d)", UsedDirection);
+ return FALSE;
+ }
+
+ if (cmsIsTag(hProfile, TagTable[Intent])) return TRUE;
+ return _cmsIsMatrixShaper(hProfile);
+ }
+
+ return (cmsTakeRenderingIntent(hProfile) == Intent);
+}
+
+// Multiple profile transform.
+static
+int MultiprofileSampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
+{
+ cmsHTRANSFORM* Transforms = (cmsHTRANSFORM*) Cargo;
+ int i;
+
+ cmsDoTransform(Transforms[0], In, Out, 1);
+
+ for (i=1; Transforms[i]; i++)
+ cmsDoTransform(Transforms[i], Out, Out, 1);
+
+
+
+ return TRUE;
+}
+
+
+static
+int IsAllowedInSingleXform(icProfileClassSignature aClass)
+{
+ return (aClass == icSigInputClass) ||
+ (aClass == icSigDisplayClass) ||
+ (aClass == icSigOutputClass) ||
+ (aClass == icSigColorSpaceClass);
+}
+
+
+// A multiprofile transform does chain several profiles into a single
+// devicelink. It couls also be used to merge named color profiles into
+// a single database.
+
+
+cmsHTRANSFORM LCMSEXPORT cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles[],
+ int nProfiles,
+ DWORD dwInput,
+ DWORD dwOutput,
+ int Intent,
+ DWORD dwFlags)
+{
+ cmsHTRANSFORM Transforms[257];
+ DWORD dwPrecalcFlags = (dwFlags|cmsFLAGS_NOTPRECALC|cmsFLAGS_NOTCACHE);
+ DWORD FormatInput, FormatOutput;
+ cmsHPROFILE hLab, hXYZ, hProfile;
+ icColorSpaceSignature ColorSpace, CurrentColorSpace;
+ icColorSpaceSignature ColorSpaceIn, ColorSpaceOut;
+ LPLUT Grid;
+ int nGridPoints, ChannelsInput, ChannelsOutput = 3, i;
+ _LPcmsTRANSFORM p;
+ int nNamedColor;
+
+ if (nProfiles > 255) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "What are you trying to do with more that 255 profiles?!?, of course aborted");
+ return NULL;
+ }
+
+ // There is a simple case with just two profiles, try to catch it in order of getting
+ // black preservation to work on this function, at least with two profiles.
+
+
+ if (nProfiles == 2) {
+
+ icProfileClassSignature Class1 = cmsGetDeviceClass(hProfiles[0]);
+ icProfileClassSignature Class2 = cmsGetDeviceClass(hProfiles[1]);
+
+ // Only input, output and display are allowed
+
+ if (IsAllowedInSingleXform(Class1) &&
+ IsAllowedInSingleXform(Class2))
+ return cmsCreateTransform(hProfiles[0], dwInput, hProfiles[1], dwOutput, Intent, dwFlags);
+ }
+
+
+ // Creates a phantom transform for latter filling
+ p = (_LPcmsTRANSFORM) cmsCreateTransform(NULL, dwInput,
+ NULL, dwOutput, Intent, cmsFLAGS_NULLTRANSFORM);
+
+ // If user wants null one, give it
+ if (dwFlags & cmsFLAGS_NULLTRANSFORM) return (cmsHPROFILE) p;
+
+ // Is a bunch of named color profiles?
+ nNamedColor = 0;
+ for (i=0; i < nProfiles; i++) {
+ if (cmsGetDeviceClass(hProfiles[i]) == icSigNamedColorClass)
+ nNamedColor++;
+ }
+
+
+ if (nNamedColor == nProfiles) {
+
+ // Yes, only named color. Create a named color-device
+ // and append to named color table
+
+ cmsDeleteTransform((cmsHTRANSFORM) p);
+
+ p = (_LPcmsTRANSFORM) cmsCreateTransform(hProfiles[0], dwInput, NULL, dwOutput, Intent, dwFlags);
+ for (i=1; i < nProfiles; i++) {
+ cmsReadICCnamedColorList(p, hProfiles[i], icSigNamedColor2Tag);
+ }
+
+ return p; // Ok, done so far
+ }
+ else
+ if (nNamedColor > 0) {
+
+ cmsDeleteTransform((cmsHTRANSFORM) p);
+ cmsSignalError(LCMS_ERRC_ABORTED, "Could not mix named color profiles with other types in multiprofile transform");
+ return NULL;
+ }
+
+
+ // We will need a 3DCLUT for device link
+ Grid = cmsAllocLUT();
+ if (!Grid) return NULL;
+
+ // This one is our PCS (Always Lab)
+ hLab = cmsCreateLabProfile(NULL);
+ hXYZ = cmsCreateXYZProfile();
+
+ if (!hLab || !hXYZ) goto ErrorCleanup;
+
+ // Take some info....
+
+ p ->EntryColorSpace = CurrentColorSpace = cmsGetColorSpace(hProfiles[0]);
+
+
+ for (i=0; i < nProfiles; i++) {
+
+ int lIsDeviceLink, lIsInput;
+
+ // Check colorspace
+
+ hProfile = hProfiles[i];
+ lIsDeviceLink = (cmsGetDeviceClass(hProfile) == icSigLinkClass);
+ lIsInput = (CurrentColorSpace != icSigXYZData) &&
+ (CurrentColorSpace != icSigLabData);
+
+ if (lIsInput || lIsDeviceLink) {
+
+ ColorSpaceIn = cmsGetColorSpace(hProfile);
+ ColorSpaceOut = cmsGetPCS(hProfile);
+
+ }
+ else {
+
+ ColorSpaceIn = cmsGetPCS(hProfile);
+ ColorSpaceOut = cmsGetColorSpace(hProfile);
+ }
+
+ ChannelsInput = _cmsChannelsOf(ColorSpaceIn);
+ ChannelsOutput = _cmsChannelsOf(ColorSpaceOut);
+
+ FormatInput = BYTES_SH(2)|CHANNELS_SH(ChannelsInput);
+ FormatOutput = BYTES_SH(2)|CHANNELS_SH(ChannelsOutput);
+
+ ColorSpace = ColorSpaceIn;
+
+
+ if (ColorSpace == CurrentColorSpace) {
+
+ if (lIsDeviceLink) {
+
+ Transforms[i] = cmsCreateTransform(hProfile, FormatInput,
+ NULL, FormatOutput,
+ Intent, dwPrecalcFlags);
+ }
+
+ else {
+
+ if (lIsInput) {
+
+ Transforms[i] = cmsCreateTransform(hProfile, FormatInput,
+ (ColorSpaceOut == icSigLabData ? hLab : hXYZ), FormatOutput,
+ Intent, dwPrecalcFlags);
+ }
+ else {
+ Transforms[i] = cmsCreateTransform((ColorSpaceIn == icSigLabData ? hLab : hXYZ), FormatInput,
+ hProfile, FormatOutput,
+ Intent, dwPrecalcFlags);
+
+ }
+ }
+
+
+ }
+ else // Can come from pcs?
+ if (CurrentColorSpace == icSigXYZData) {
+
+ Transforms[i] = cmsCreateTransform(hXYZ, FormatInput,
+ hProfile, FormatOutput,
+ Intent, dwPrecalcFlags);
+
+ }
+ else
+ if (CurrentColorSpace == icSigLabData) {
+
+ Transforms[i] = cmsCreateTransform(hLab, FormatInput,
+ hProfile, FormatOutput,
+ Intent, dwPrecalcFlags);
+
+ }
+ else {
+ cmsSignalError(LCMS_ERRC_ABORTED, "cmsCreateMultiprofileTransform: ColorSpace mismatch");
+ goto ErrorCleanup;
+ }
+
+ CurrentColorSpace = ColorSpaceOut;
+
+ }
+
+ p ->ExitColorSpace = CurrentColorSpace;
+ Transforms[i] = NULL; // End marker
+
+ p ->InputProfile = hProfiles[0];
+ p ->OutputProfile = hProfiles[nProfiles - 1];
+
+ nGridPoints = _cmsReasonableGridpointsByColorspace(p ->EntryColorSpace, dwFlags);
+
+ ChannelsInput = _cmsChannelsOf(cmsGetColorSpace(p ->InputProfile));
+
+ Grid = cmsAlloc3DGrid(Grid, nGridPoints, ChannelsInput, ChannelsOutput);
+
+ if (!(dwFlags & cmsFLAGS_NOPRELINEARIZATION))
+ _cmsComputePrelinearizationTablesFromXFORM(Transforms, nProfiles, Grid);
+
+ // Compute device link on 16-bit basis
+ if (!cmsSample3DGrid(Grid, MultiprofileSampler, (LPVOID) Transforms, Grid -> wFlags)) {
+
+ cmsFreeLUT(Grid);
+ goto ErrorCleanup;
+ }
+
+ // All ok, store the newly created LUT
+ p -> DeviceLink = Grid;
+
+ SetPrecalculatedTransform(p);
+
+ for (i=nProfiles-1; i >= 0; --i)
+ cmsDeleteTransform(Transforms[i]);
+
+
+ if (hLab) cmsCloseProfile(hLab);
+ if (hXYZ) cmsCloseProfile(hXYZ);
+
+
+ if (p ->EntryColorSpace == icSigRgbData ||
+ p ->EntryColorSpace == icSigCmyData) {
+
+ p->DeviceLink -> CLut16params.Interp3D = cmsTetrahedralInterp16;
+ }
+
+
+ if ((Intent != INTENT_ABSOLUTE_COLORIMETRIC) &&
+ !(dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP))
+ _cmsFixWhiteMisalignment(p);
+
+ return (cmsHTRANSFORM) p;
+
+
+ErrorCleanup:
+
+ if (hLab) cmsCloseProfile(hLab);
+ if (hXYZ) cmsCloseProfile(hXYZ);
+ return NULL;
+}
+
+
+
+double LCMSEXPORT cmsSetAdaptationState(double d)
+{
+ double OldVal = GlobalAdaptationState;
+
+ if (d >= 0)
+ GlobalAdaptationState = d;
+
+ return OldVal;
+
+}
diff --git a/debian/lcms/lcms-1.19.dfsg2/src/lcms.def b/debian/lcms/lcms-1.19.dfsg2/src/lcms.def
new file mode 100755
index 00000000..12d92bbf
--- /dev/null
+++ b/debian/lcms/lcms-1.19.dfsg2/src/lcms.def
@@ -0,0 +1,185 @@
+LIBRARY LCMS.DLL
+
+EXPORTS
+ _cmsICCcolorSpace = _cmsICCcolorSpace
+ _cmsSaveProfile = _cmsSaveProfile
+ _cmsSaveProfileToMem = _cmsSaveProfileToMem
+ cmsAdaptToIlluminant = cmsAdaptToIlluminant
+ cmsAllocGamma = cmsAllocGamma
+ cmsBuildGamma = cmsBuildGamma
+ cmsSmoothGamma = cmsSmoothGamma
+ cmsBuildRGB2XYZtransferMatrix= cmsBuildRGB2XYZtransferMatrix
+ cmsCloseProfile = cmsCloseProfile
+ cmsCreateProofingTransform = cmsCreateProofingTransform
+ cmsCreateRGBProfile = cmsCreateRGBProfile
+ cmsCreateTransform = cmsCreateTransform
+ cmsDeleteTransform = cmsDeleteTransform
+ cmsDoTransform = cmsDoTransform
+ cmsErrorAction = cmsErrorAction
+ cmsFreeGamma = cmsFreeGamma
+ cmsGetAlarmCodes = cmsGetAlarmCodes
+ cmsGetColorSpace = cmsGetColorSpace
+ cmsGetDeviceClass = cmsGetDeviceClass
+ cmsGetPCS = cmsGetPCS
+ cmsIsIntentSupported = cmsIsIntentSupported
+ cmsIsTag = cmsIsTag
+ cmsJoinGamma = cmsJoinGamma
+ cmsJoinGammaEx = cmsJoinGammaEx
+ cmsOpenProfileFromFile = cmsOpenProfileFromFile
+ cmsOpenProfileFromMem = cmsOpenProfileFromMem
+ cmsReverseGamma = cmsReverseGamma
+ cmsSetAlarmCodes = cmsSetAlarmCodes
+ cmsTakeColorants = cmsTakeColorants
+ cmsTakeIluminant = cmsTakeIluminant
+ cmsTakeMediaBlackPoint = cmsTakeMediaBlackPoint
+ cmsTakeMediaWhitePoint = cmsTakeMediaWhitePoint
+ cmsTakeProductDesc = cmsTakeProductDesc
+ cmsTakeProductInfo = cmsTakeProductInfo
+ cmsTakeProductName = cmsTakeProductName
+ cmsTakeRenderingIntent = cmsTakeRenderingIntent
+ cmsWhitePointFromTemp = cmsWhitePointFromTemp
+ cmsXYZ2xyY = cmsXYZ2xyY
+ cmsxyY2XYZ = cmsxyY2XYZ
+ cmsLCh2Lab = cmsLCh2Lab
+ cmsLab2LCh = cmsLab2LCh
+ cmsLab2XYZ = cmsLab2XYZ
+ cmsXYZ2Lab = cmsXYZ2Lab
+ cmsClampLab = cmsClampLab
+ cmsCreateLabProfile = cmsCreateLabProfile
+ cmsCreateXYZProfile = cmsCreateXYZProfile
+ cmsCreate_sRGBProfile = cmsCreate_sRGBProfile
+ cmsD50_XYZ = cmsD50_XYZ
+ cmsD50_xyY = cmsD50_xyY
+ cmsDeltaE = cmsDeltaE
+ cmsCIE94DeltaE = cmsCIE94DeltaE
+ cmsBFDdeltaE = cmsBFDdeltaE
+ cmsCMCdeltaE = cmsCMCdeltaE
+ cmsCIE2000DeltaE = cmsCIE2000DeltaE
+ cmsFloat2LabEncoded = cmsFloat2LabEncoded
+ cmsFloat2XYZEncoded = cmsFloat2XYZEncoded
+ cmsLabEncoded2Float = cmsLabEncoded2Float
+ cmsXYZEncoded2Float = cmsXYZEncoded2Float
+ cmsBuildParametricGamma = cmsBuildParametricGamma
+ cmsCIECAM97sInit = cmsCIECAM97sInit
+ cmsCIECAM97sDone = cmsCIECAM97sDone
+ cmsCIECAM97sForward = cmsCIECAM97sForward
+ cmsCIECAM97sReverse = cmsCIECAM97sReverse
+ cmsCIECAM02Init = cmsCIECAM02Init
+ cmsCIECAM02Done = cmsCIECAM02Done
+ cmsCIECAM02Forward = cmsCIECAM02Forward
+ cmsCIECAM02Reverse = cmsCIECAM02Reverse
+ cmsCreateMultiprofileTransform = cmsCreateMultiprofileTransform
+ cmsAddTag = cmsAddTag
+ cmsAllocLUT = cmsAllocLUT
+ cmsAllocLinearTable = cmsAllocLinearTable
+ cmsAlloc3DGrid = cmsAlloc3DGrid
+ cmsFreeLUT = cmsFreeLUT
+ cmsEvalLUT = cmsEvalLUT
+ cmsReadICCLut = cmsReadICCLut
+ cmsSample3DGrid = cmsSample3DGrid
+ cmsSetMatrixLUT = cmsSetMatrixLUT
+ cmsSetMatrixLUT4 = cmsSetMatrixLUT4
+ cmsDupGamma = cmsDupGamma
+ cmsReadICCGamma = cmsReadICCGamma
+ cmsReadICCGammaReversed = cmsReadICCGammaReversed
+ cmsSetErrorHandler = cmsSetErrorHandler
+ cmsChangeBuffersFormat = cmsChangeBuffersFormat
+ cmsCreateGrayProfile = cmsCreateGrayProfile
+ cmsCreateInkLimitingDeviceLink = cmsCreateInkLimitingDeviceLink
+ cmsCreateLinearizationDeviceLink = cmsCreateLinearizationDeviceLink
+ cmsEstimateGamma = cmsEstimateGamma
+ cmsEstimateGammaEx = cmsEstimateGammaEx
+ cmsNamedColorCount = cmsNamedColorCount
+ cmsNamedColorInfo = cmsNamedColorInfo
+ cmsNamedColorIndex = cmsNamedColorIndex
+ cmsSetColorSpace = cmsSetColorSpace
+ cmsSetDeviceClass = cmsSetDeviceClass
+ cmsSetLanguage = cmsSetLanguage
+ cmsSetPCS = cmsSetPCS
+ cmsTakeCharTargetData = cmsTakeCharTargetData
+ cmsTransform2DeviceLink = cmsTransform2DeviceLink
+ _cmsChannelsOf = _cmsChannelsOf
+ cmsFreeGammaTriple = cmsFreeGammaTriple
+ cmsSetRenderingIntent = cmsSetRenderingIntent
+ cmsDupLUT = cmsDupLUT
+ cmsGetUserFormatters = cmsGetUserFormatters
+ cmsSetUserFormatters = cmsSetUserFormatters
+ cmsCreateBCHSWabstractProfile = cmsCreateBCHSWabstractProfile
+ cmsGetPostScriptCSA = cmsGetPostScriptCSA
+ cmsGetPostScriptCRD = cmsGetPostScriptCRD
+ cmsGetPostScriptCRDEx = cmsGetPostScriptCRDEx
+ cmsReadProfileSequenceDescription = cmsReadProfileSequenceDescription
+ cmsTakeManufacturer = cmsTakeManufacturer
+ cmsTakeModel = cmsTakeModel
+ cmsSetProfileID = cmsSetProfileID
+ cmsTakeProfileID = cmsTakeProfileID
+ cmsSetHeaderFlags = cmsSetHeaderFlags
+ cmsTakeHeaderFlags = cmsTakeHeaderFlags
+ cmsTakeCopyright = cmsTakeCopyright
+ _cmsSetLUTdepth = _cmsSetLUTdepth
+ _cmsAddXYZTag = _cmsAddXYZTag
+ _cmsAddLUTTag = _cmsAddLUTTag
+ _cmsAddTextTag = _cmsAddTextTag
+ _cmsAddGammaTag = _cmsAddGammaTag
+ _cmsAddChromaticityTag = _cmsAddChromaticityTag
+ _cmsAddSequenceDescriptionTag = _cmsAddSequenceDescriptionTag
+ _cmsAddNamedColorTag = _cmsAddNamedColorTag
+ _cmsLCMScolorSpace = _cmsLCMScolorSpace
+ cmsFloat2LabEncoded4 = cmsFloat2LabEncoded4
+ cmsLabEncoded2Float4 = cmsLabEncoded2Float4
+ cmsGetProfileICCversion = cmsGetProfileICCversion
+ cmsIT8Alloc = cmsIT8Alloc
+ cmsIT8Free = cmsIT8Free
+ cmsIT8TableCount = cmsIT8TableCount
+ cmsIT8SetTable = cmsIT8SetTable
+ cmsIT8LoadFromFile = cmsIT8LoadFromFile
+ cmsIT8LoadFromMem = cmsIT8LoadFromMem
+ cmsIT8SaveToFile = cmsIT8SaveToFile
+ cmsIT8GetSheetType = cmsIT8GetSheetType
+ cmsIT8SetSheetType = cmsIT8SetSheetType
+ cmsIT8SetComment = cmsIT8SetComment
+ cmsIT8SetPropertyStr = cmsIT8SetPropertyStr
+ cmsIT8SetPropertyDbl = cmsIT8SetPropertyDbl
+ cmsIT8SetPropertyHex = cmsIT8SetPropertyHex
+ cmsIT8SetPropertyUncooked = cmsIT8SetPropertyUncooked
+ cmsIT8GetProperty = cmsIT8GetProperty
+ cmsIT8GetPropertyDbl = cmsIT8GetPropertyDbl
+ cmsIT8EnumProperties = cmsIT8EnumProperties
+ cmsIT8GetDataRowCol = cmsIT8GetDataRowCol
+ cmsIT8GetDataRowColDbl = cmsIT8GetDataRowColDbl
+ cmsIT8SetDataRowCol = cmsIT8SetDataRowCol
+ cmsIT8SetDataRowColDbl = cmsIT8SetDataRowColDbl
+ cmsIT8GetData = cmsIT8GetData
+ cmsIT8GetDataDbl = cmsIT8GetDataDbl
+ cmsIT8SetData = cmsIT8SetData
+ cmsIT8SetDataDbl = cmsIT8SetDataDbl
+ cmsIT8SetDataFormat = cmsIT8SetDataFormat
+ cmsIT8EnumDataFormat = cmsIT8EnumDataFormat
+ cmsIT8GetPatchName = cmsIT8GetPatchName
+ cmsIT8SetTableByLabel = cmsIT8SetTableByLabel
+ cmsReadICCText = cmsReadICCText
+ cmsReadICCTextEx = cmsReadICCTextEx
+ cmsCreateLab4Profile = cmsCreateLab4Profile
+ cmsCreateNULLProfile = cmsCreateNULLProfile
+ cmsIT8DefineDblFormat = cmsIT8DefineDblFormat
+ cmsIT8GetDataFormat = cmsIT8GetDataFormat
+ cmsSetProfileICCversion = cmsSetProfileICCversion
+ cmsTakeCalibrationDateTime = cmsTakeCalibrationDateTime
+ cmsTakeCreationDateTime = cmsTakeCreationDateTime
+ _cmsIsMatrixShaper = _cmsIsMatrixShaper
+ _cmsAddColorantTableTag = _cmsAddColorantTableTag
+ _cmsAddDateTimeTag = _cmsAddDateTimeTag
+ cmsEvalLUTreverse = cmsEvalLUTreverse
+ cmsGetTagCount = cmsGetTagCount
+ cmsGetTagSignature = cmsGetTagSignature
+ cmsIT8SaveToMem = cmsIT8SaveToMem
+ cmsReadColorantTable = cmsReadColorantTable
+ cmsSetAdaptationState = cmsSetAdaptationState
+ cmsSetHeaderAttributes = cmsSetHeaderAttributes
+ cmsTakeHeaderAttributes = cmsTakeHeaderAttributes
+ cmsSetCMYKPreservationStrategy = cmsSetCMYKPreservationStrategy
+ cmsFreeProfileSequenceDescription = cmsFreeProfileSequenceDescription
+ _cmsAddChromaticAdaptationTag = _cmsAddChromaticAdaptationTag
+
+
+
diff --git a/debian/lcms/lcms-1.19.dfsg2/src/makefile.simple b/debian/lcms/lcms-1.19.dfsg2/src/makefile.simple
new file mode 100755
index 00000000..831cb243
--- /dev/null
+++ b/debian/lcms/lcms-1.19.dfsg2/src/makefile.simple
@@ -0,0 +1,64 @@
+SHELL = /bin/sh
+
+CFLAGS = -g -O4 -I../include -fomit-frame-pointer -Wall -Wmissing-prototypes
+LDFLAGS = -lm
+BASEDIR = $(DESTDIR)/usr
+BINDIR = $(BASEDIR)/bin
+LIBDIR = $(BASEDIR)/lib
+INCDIR = $(BASEDIR)/include
+VERSION = 1.0.15
+AR = ar -cru
+RANLIB = ranlib
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+SRCS = cmscnvrt.c cmserr.c cmsgamma.c cmsgmt.c cmsintrp.c cmsio1.c \
+ cmslut.c cmsmatsh.c cmsmtrx.c cmspack.c cmspcs.c cmswtpnt.c \
+ cmsxform.c cmssamp.c cmscam97.c cmsnamed.c cmsps2.c cmscam02.c \
+ cmsvirt.c cmscgats.c cmsio0.c
+
+OBJS = $(SRCS:.c=.o)
+
+
+all: liblcms.a liblcms.so.${VERSION}
+
+
+#
+# Library
+#
+
+liblcms.a: $(OBJS)
+ $(AR) liblcms.a $(OBJS)
+ $(RANLIB) liblcms.a
+
+liblcms.so.${VERSION}: $(OBJS)
+ -$(CC) $(CFLAGS) $(LDFLAGS) -shared -o liblcms.so.${VERSION} $(OBJS)
+ -ln -s liblcms.so.${VERSION} liblcms.so.1
+ -ln -s liblcms.so.1 liblcms.so
+
+#
+# Installation
+#
+
+install: liblcms.a liblcms.so.${VERSION}
+ mkdir -p $(BINDIR) $(LIBDIR) $(INCDIR)
+ -cp -a liblcms.so* $(LIBDIR)
+ cp -a liblcms.a $(LIBDIR)
+ cp ../include/lcms.h ../include/icc34.h $(INCDIR)
+
+#
+# Test programs
+#
+
+test:
+ # we are doing nothing for the test target
+
+
+
+#
+# Cleanup
+#
+
+clean:
+ -rm $(OBJS) liblcms.a liblcms.so.${VERSION} liblcms.so.1 liblcms.so