diff options
Diffstat (limited to 'debian/lcms/lcms-1.19.dfsg2/src')
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(×tamp, 1, sizeof(icDateTimeNumber), Icc) != sizeof(icDateTimeNumber)) + return FALSE; + + DecodeDateTimeNumber(×tamp, 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
|
