summaryrefslogtreecommitdiff
path: root/testbed
diff options
context:
space:
mode:
authorMarti Maria <info@littlecms.com>2010-02-08 17:20:25 +0100
committerMarti Maria <info@littlecms.com>2010-02-08 17:20:25 +0100
commit3cefc4ee12fe7144a65a205f6645c14ec5be68ce (patch)
tree37c160448e2accb5bbada6f6d4798a90741e4a12 /testbed
downloadlcms2-3cefc4ee12fe7144a65a205f6645c14ec5be68ce.tar.gz
initial commit
Diffstat (limited to 'testbed')
-rw-r--r--testbed/Makefile.am31
-rw-r--r--testbed/Makefile.in487
-rwxr-xr-xtestbed/USWebCoatedSWOP.iccbin0 -> 557168 bytes
-rwxr-xr-xtestbed/UncoatedFOGRA29.iccbin0 -> 654140 bytes
-rw-r--r--testbed/bad.icc21
-rwxr-xr-xtestbed/sRGB Color Space Profile.icmbin0 -> 3144 bytes
-rwxr-xr-xtestbed/sRGBSpac.icmbin0 -> 28202 bytes
-rwxr-xr-xtestbed/sRGB_v4_ICC_preference.iccbin0 -> 60960 bytes
-rw-r--r--testbed/testcms2.c6699
-rw-r--r--testbed/testthread.cpp52
-rwxr-xr-xtestbed/toosmall.iccbin0 -> 158 bytes
11 files changed, 7290 insertions, 0 deletions
diff --git a/testbed/Makefile.am b/testbed/Makefile.am
new file mode 100644
index 0000000..37a4671
--- /dev/null
+++ b/testbed/Makefile.am
@@ -0,0 +1,31 @@
+#
+# Makefile for building testcms
+#
+
+# Don't require all the GNU mandated files
+AUTOMAKE_OPTIONS = 1.7 foreign
+
+INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include -I$(top_srcdir)/src
+
+check_PROGRAMS = testcms
+
+CFLAGS = --pedantic -Wall -std=c99 -O2
+
+testcms_LDADD = $(top_builddir)/src/liblcms2.la
+testcms_LDFLAGS = @LDFLAGS@
+testcms_SOURCES = testcms2.c
+
+EXTRA_DIST = sRGBSpac.icm bad.icc toosmall.icc UncoatedFOGRA29.icc \
+ USWebCoatedSWOP.icc sRGB_v4_ICC_preference.icc
+
+check:
+ ./testcms
+
+dist-hook:
+ cp -p $(srcdir)/"sRGB Color Space Profile.icm" $(distdir)
+ cp -p $(srcdir)/sRGBSpac.icm $(distdir)
+ cp -p $(srcdir)/bad.icc $(distdir)
+ cp -p $(srcdir)/toosmall.icc $(distdir)
+ cp -p $(srcdir)/UncoatedFOGRA29.icc $(distdir)
+ cp -p $(srcdir)/USWebCoatedSWOP.icc $(distdir)
+ cp -p $(srcdir)/sRGB_v4_ICC_preference.icc $(distdir)
diff --git a/testbed/Makefile.in b/testbed/Makefile.in
new file mode 100644
index 0000000..f790607
--- /dev/null
+++ b/testbed/Makefile.in
@@ -0,0 +1,487 @@
+# Makefile.in generated by automake 1.9.6 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005 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 testcms
+#
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+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@
+LIBOBJDIR =
+check_PROGRAMS = testcms$(EXEEXT)
+subdir = testbed
+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 = $(install_sh) -d
+CONFIG_CLEAN_FILES =
+am_testcms_OBJECTS = testcms2.$(OBJEXT)
+testcms_OBJECTS = $(am_testcms_OBJECTS)
+testcms_DEPENDENCIES = $(top_builddir)/src/liblcms2.la
+DEFAULT_INCLUDES = -I. -I$(srcdir)
+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 --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(testcms_SOURCES)
+DIST_SOURCES = $(testcms_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = --pedantic -Wall -std=c99 -O2
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GREP = @GREP@
+HasJPEG_FALSE = @HasJPEG_FALSE@
+HasJPEG_TRUE = @HasJPEG_TRUE@
+HasTIFF_FALSE = @HasTIFF_FALSE@
+HasTIFF_TRUE = @HasTIFF_TRUE@
+HasZLIB_FALSE = @HasZLIB_FALSE@
+HasZLIB_TRUE = @HasZLIB_TRUE@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+JPEGICC_DEPLIBS = @JPEGICC_DEPLIBS@
+LCMS_LIB_DEPLIBS = @LCMS_LIB_DEPLIBS@
+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@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@
+MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@
+MAKEINFO = @MAKEINFO@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+TIFFICC_DEPLIBS = @TIFFICC_DEPLIBS@
+VERSION = @VERSION@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
+am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
+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@
+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 = @includedir@
+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@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+to_host_path_cmd = @to_host_path_cmd@
+
+# Don't require all the GNU mandated files
+AUTOMAKE_OPTIONS = 1.7 foreign
+INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include -I$(top_srcdir)/src
+testcms_LDADD = $(top_builddir)/src/liblcms2.la
+testcms_LDFLAGS = @LDFLAGS@
+testcms_SOURCES = testcms2.c
+EXTRA_DIST = sRGBSpac.icm bad.icc toosmall.icc UncoatedFOGRA29.icc \
+ USWebCoatedSWOP.icc sRGB_v4_ICC_preference.icc
+
+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 testbed/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --foreign testbed/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
+
+clean-checkPROGRAMS:
+ @list='$(check_PROGRAMS)'; for p in $$list; do \
+ f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f $$p $$f"; \
+ rm -f $$p $$f ; \
+ done
+testcms$(EXEEXT): $(testcms_OBJECTS) $(testcms_DEPENDENCIES)
+ @rm -f testcms$(EXEEXT)
+ $(LINK) $(testcms_LDFLAGS) $(testcms_OBJECTS) $(testcms_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testcms2.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@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@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@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@ if $(LTCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Plo"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@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
+
+distclean-libtool:
+ -rm -f libtool config.lt
+uninstall-info-am:
+
+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)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkdir_p) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ 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
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$(top_distdir)" distdir="$(distdir)" \
+ dist-hook
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS)
+check: check-am
+all-am: Makefile
+installdirs:
+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-checkPROGRAMS clean-generic clean-libtool \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-libtool distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-exec-am:
+
+install-info: install-info-am
+
+install-man:
+
+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-info-am
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean \
+ clean-checkPROGRAMS clean-generic clean-libtool ctags \
+ dist-hook 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-exec install-exec-am install-info \
+ install-info-am install-man 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-info-am
+
+
+check:
+ ./testcms
+
+dist-hook:
+ cp -p $(srcdir)/"sRGB Color Space Profile.icm" $(distdir)
+ cp -p $(srcdir)/sRGBSpac.icm $(distdir)
+ cp -p $(srcdir)/bad.icc $(distdir)
+ cp -p $(srcdir)/toosmall.icc $(distdir)
+ cp -p $(srcdir)/UncoatedFOGRA29.icc $(distdir)
+ cp -p $(srcdir)/USWebCoatedSWOP.icc $(distdir)
+ cp -p $(srcdir)/sRGB_v4_ICC_preference.icc $(distdir)
+# 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/testbed/USWebCoatedSWOP.icc b/testbed/USWebCoatedSWOP.icc
new file mode 100755
index 0000000..078a644
--- /dev/null
+++ b/testbed/USWebCoatedSWOP.icc
Binary files differ
diff --git a/testbed/UncoatedFOGRA29.icc b/testbed/UncoatedFOGRA29.icc
new file mode 100755
index 0000000..70e13f5
--- /dev/null
+++ b/testbed/UncoatedFOGRA29.icc
Binary files differ
diff --git a/testbed/bad.icc b/testbed/bad.icc
new file mode 100644
index 0000000..ddfa622
--- /dev/null
+++ b/testbed/bad.icc
@@ -0,0 +1,21 @@
+SHELL = /bin/sh
+
+CFLAGS = -g -O4 -fomit-frame-pointer -Wall -I../include
+
+testcms.o: testcms.c
+
+testcms: testcms.o ../src/liblcms.a
+ $(CC) $(CFLAGS) testcms.o ../src/liblcms.a -o $@ -lm
+
+all: testcms test
+
+test: testcms
+ ./testcms
+
+install:
+ # Nothing to install
+
+clean:
+ -rm testcms.o testcms testcms.exe
+
+
diff --git a/testbed/sRGB Color Space Profile.icm b/testbed/sRGB Color Space Profile.icm
new file mode 100755
index 0000000..7f9d18d
--- /dev/null
+++ b/testbed/sRGB Color Space Profile.icm
Binary files differ
diff --git a/testbed/sRGBSpac.icm b/testbed/sRGBSpac.icm
new file mode 100755
index 0000000..83563ef
--- /dev/null
+++ b/testbed/sRGBSpac.icm
Binary files differ
diff --git a/testbed/sRGB_v4_ICC_preference.icc b/testbed/sRGB_v4_ICC_preference.icc
new file mode 100755
index 0000000..cfbd03e
--- /dev/null
+++ b/testbed/sRGB_v4_ICC_preference.icc
Binary files differ
diff --git a/testbed/testcms2.c b/testbed/testcms2.c
new file mode 100644
index 0000000..552965c
--- /dev/null
+++ b/testbed/testcms2.c
@@ -0,0 +1,6699 @@
+//---------------------------------------------------------------------------------
+//
+// Little Color Management System
+// Copyright (c) 1998-2010 Marti Maria Saguer
+//
+// 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.
+//
+//---------------------------------------------------------------------------------
+//
+
+#ifdef _MSC_VER
+# define _CRT_SECURE_NO_WARNINGS 1
+#endif
+
+#include "lcms2_internal.h"
+
+// On Visual Studio, use debug CRT
+#ifdef _MSC_VER
+# include "crtdbg.h"
+# include <io.h>
+#endif
+
+// A single check. Returns 1 if success, 0 if failed
+typedef int (*TestFn)(void);
+
+// A parametric Tone curve test function
+typedef cmsFloat32Number (* dblfnptr)(cmsFloat32Number x, const cmsFloat64Number Params[]);
+
+// Some globals to keep track of error
+#define TEXT_ERROR_BUFFER_SIZE 4096
+
+static char ReasonToFailBuffer[TEXT_ERROR_BUFFER_SIZE];
+static char SubTestBuffer[TEXT_ERROR_BUFFER_SIZE];
+static int TotalTests = 0, TotalFail = 0;
+static cmsBool TrappedError;
+static int SimultaneousErrors;
+
+
+#define cmsmin(a, b) (((a) < (b)) ? (a) : (b))
+
+// Die, a fatal unexpected error is detected!
+static
+void Die(const char* Reason)
+{
+ printf("\n\nArrrgggg!!: %s!\n\n", Reason);
+ fflush(stdout);
+ exit(1);
+}
+
+// Memory management replacement -----------------------------------------------------------------------------
+
+
+// This is just a simple plug-in for malloc, free and realloc to keep track of memory allocated,
+// maximum requested as a single block and maximum allocated at a given time. Results are printed at the end
+static cmsUInt32Number SingleHit, MaxAllocated=0, TotalMemory=0;
+
+// I'm hidding the size before the block. This is a well-known technique and probably the blocks coming from
+// malloc are built in a way similar to that, but I do on my own to be portable.
+typedef struct {
+ cmsUInt32Number KeepSize;
+ cmsContext WhoAllocated;
+ cmsUInt8Number mem[1];
+} MemoryBlock;
+
+#define SIZE_OF_MEM_HEADER (sizeof(cmsUInt32Number) + sizeof(cmsContext))
+
+// This is a fake thread descriptor used to check thread integrity.
+// Basically it returns a different threadID each time it is called.
+// Then the memory management replacement functions does check if each
+// free() is being called with same ContextID used on malloc()
+static
+cmsContext DbgThread(void)
+{
+ static int n = 1;
+
+ return (cmsContext) n++;
+}
+
+// The allocate routine
+static
+void* DebugMalloc(cmsContext ContextID, cmsUInt32Number size)
+{
+ MemoryBlock* blk;
+
+ if (size <= 0) {
+ Die("malloc requested with zero bytes");
+ }
+
+ TotalMemory += size;
+
+ if (TotalMemory > MaxAllocated)
+ MaxAllocated = TotalMemory;
+
+ if (size > SingleHit)
+ SingleHit = size;
+
+ blk = (MemoryBlock*) malloc(size + SIZE_OF_MEM_HEADER);
+ if (blk == NULL) return NULL;
+
+ blk ->KeepSize = size;
+ blk ->WhoAllocated = ContextID;
+
+ return blk ->mem;
+}
+
+// The free routine
+static
+void DebugFree(cmsContext ContextID, void *Ptr)
+{
+ MemoryBlock* blk;
+
+ if (Ptr == NULL) {
+ Die("NULL free (which is a no-op in C, but may be an clue of something going wrong)");
+ }
+
+ blk = (MemoryBlock*) (((cmsUInt8Number*) Ptr) - SIZE_OF_MEM_HEADER);
+ TotalMemory -= blk ->KeepSize;
+
+ if (blk ->WhoAllocated != ContextID) {
+ Die("Trying to free memory allocated by a different thread");
+ }
+
+ free(blk);
+}
+
+// Reallocate, just a malloc, a copy and a free in this case.
+static
+void * DebugRealloc(cmsContext ContextID, void* Ptr, cmsUInt32Number NewSize)
+{
+ MemoryBlock* blk;
+ void* NewPtr;
+ cmsUInt32Number max_sz;
+
+ NewPtr = DebugMalloc(ContextID, NewSize);
+ if (Ptr == NULL) return NewPtr;
+
+ blk = (MemoryBlock*) (((cmsUInt8Number*) Ptr) - SIZE_OF_MEM_HEADER);
+ max_sz = blk -> KeepSize > NewSize ? NewSize : blk ->KeepSize;
+ memmove(NewPtr, Ptr, max_sz);
+ DebugFree(ContextID, Ptr);
+
+ return NewPtr;
+}
+
+// Let's know the totals
+static
+void DebugMemPrintTotals(void)
+{
+ printf("[Memory statistics]\n");
+ printf("Allocated = %d MaxAlloc = %d Single block hit = %d\n", TotalMemory, MaxAllocated, SingleHit);
+}
+
+// Here we go with the plug-in declaration
+static cmsPluginMemHandler DebugMemHandler = {{ cmsPluginMagicNumber, 2000, cmsPluginMemHandlerSig, NULL },
+ DebugMalloc, DebugFree, DebugRealloc, NULL, NULL, NULL };
+
+// Utils -------------------------------------------------------------------------------------
+
+static
+void FatalErrorQuit(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text)
+{
+ Die(Text);
+}
+
+// Print a dot for gauging
+static
+void Dot(void)
+{
+ fprintf(stdout, "."); fflush(stdout);
+}
+
+// Keep track of the reason to fail
+static
+void Fail(const char* frm, ...)
+{
+ va_list args;
+ va_start(args, frm);
+ vsprintf(ReasonToFailBuffer, frm, args);
+ va_end(args);
+}
+
+// Keep track of subtest
+static
+void SubTest(const char* frm, ...)
+{
+ va_list args;
+
+ Dot();
+ va_start(args, frm);
+ vsprintf(SubTestBuffer, frm, args);
+ va_end(args);
+}
+
+
+// Memory string
+static
+const char* MemStr(cmsUInt32Number size)
+{
+ static char Buffer[1024];
+
+ if (size > 1024*1024) {
+ sprintf(Buffer, "%g Mb", (cmsFloat64Number) size / (1024.0*1024.0));
+ }
+ else
+ if (size > 1024) {
+ sprintf(Buffer, "%g Kb", (cmsFloat64Number) size / 1024.0);
+ }
+ else
+ sprintf(Buffer, "%g bytes", (cmsFloat64Number) size);
+
+ return Buffer;
+}
+
+
+// The check framework
+static
+void Check(const char* Title, TestFn Fn)
+{
+ printf("Checking %s ...", Title);
+ fflush(stdout);
+
+ ReasonToFailBuffer[0] = 0;
+ SubTestBuffer[0] = 0;
+ TrappedError = FALSE;
+ SimultaneousErrors = 0;
+ TotalTests++;
+
+ if (Fn() && !TrappedError) {
+
+ // It is a good place to check memory
+ if (TotalMemory > 0)
+ printf("Ok, but %s are left!\n", MemStr(TotalMemory));
+ else
+ printf("Ok.\n");
+ }
+ else {
+ printf("FAIL!\n");
+
+ if (SubTestBuffer[0])
+ printf("%s: [%s]\n\t%s\n", Title, SubTestBuffer, ReasonToFailBuffer);
+ else
+ printf("%s:\n\t%s\n", Title, ReasonToFailBuffer);
+
+ if (SimultaneousErrors > 1)
+ printf("\tMore than one (%d) errors were reported\n", SimultaneousErrors);
+
+ TotalFail++;
+ }
+ fflush(stdout);
+}
+
+// Dump a tone curve, for easy diagnostic
+void DumpToneCurve(cmsToneCurve* gamma, const char* FileName)
+{
+ cmsHANDLE hIT8;
+ cmsUInt32Number i;
+
+ hIT8 = cmsIT8Alloc(gamma ->InterpParams->ContextID);
+
+ cmsIT8SetPropertyDbl(hIT8, "NUMBER_OF_FIELDS", 2);
+ cmsIT8SetPropertyDbl(hIT8, "NUMBER_OF_SETS", gamma ->nEntries);
+
+ cmsIT8SetDataFormat(hIT8, 0, "SAMPLE_ID");
+ cmsIT8SetDataFormat(hIT8, 1, "VALUE");
+
+ for (i=0; i < gamma ->nEntries; i++) {
+ char Val[30];
+
+ sprintf(Val, "%d", i);
+ cmsIT8SetDataRowCol(hIT8, i, 0, Val);
+ sprintf(Val, "0x%x", gamma ->Table16[i]);
+ cmsIT8SetDataRowCol(hIT8, i, 1, Val);
+ }
+
+ cmsIT8SaveToFile(hIT8, FileName);
+ cmsIT8Free(hIT8);
+}
+
+// -------------------------------------------------------------------------------------------------
+
+// Used to perform several checks.
+// The space used is a clone of a well-known commercial
+// color space which I will name "Above RGB"
+static
+cmsHPROFILE Create_AboveRGB(void)
+{
+ cmsToneCurve* Curve[3];
+ cmsHPROFILE hProfile;
+ cmsCIExyY D65;
+ cmsCIExyYTRIPLE Primaries = {{0.64, 0.33, 1 },
+ {0.21, 0.71, 1 },
+ {0.15, 0.06, 1 }};
+
+ Curve[0] = Curve[1] = Curve[2] = cmsBuildGamma(DbgThread(), 2.19921875);
+
+ cmsWhitePointFromTemp(&D65, 6504);
+ hProfile = cmsCreateRGBProfileTHR(DbgThread(), &D65, &Primaries, Curve);
+ cmsFreeToneCurve(Curve[0]);
+
+ return hProfile;
+}
+
+// A gamma-2.2 gray space
+static
+cmsHPROFILE Create_Gray22(void)
+{
+ cmsHPROFILE hProfile;
+ cmsToneCurve* Curve = cmsBuildGamma(DbgThread(), 2.2);
+ if (Curve == NULL) return NULL;
+
+ hProfile = cmsCreateGrayProfileTHR(DbgThread(), cmsD50_xyY(), Curve);
+ cmsFreeToneCurve(Curve);
+
+ return hProfile;
+}
+
+// A CMYK devicelink that adds gamma 3.0 to each channel
+static
+cmsHPROFILE Create_CMYK_DeviceLink(void)
+{
+ cmsHPROFILE hProfile;
+ cmsToneCurve* Tab[4];
+ cmsToneCurve* Curve = cmsBuildGamma(DbgThread(), 3.0);
+ if (Curve == NULL) return NULL;
+
+ Tab[0] = Curve;
+ Tab[1] = Curve;
+ Tab[2] = Curve;
+ Tab[3] = Curve;
+
+ hProfile = cmsCreateLinearizationDeviceLinkTHR(DbgThread(), cmsSigCmykData, Tab);
+ if (hProfile == NULL) return NULL;
+
+ cmsFreeToneCurve(Curve);
+
+ return hProfile;
+}
+
+
+// Create a fake CMYK profile, without any other requeriment that being coarse CMYK.
+// DONT USE THIS PROFILE FOR ANYTHING, IT IS USELESS BUT FOR TESTING PURPOSES.
+typedef struct {
+
+ cmsHTRANSFORM hLab2sRGB;
+ cmsHTRANSFORM sRGB2Lab;
+ cmsHTRANSFORM hIlimit;
+
+} FakeCMYKParams;
+
+static
+cmsFloat64Number Clip(cmsFloat64Number v)
+{
+ if (v < 0) return 0;
+ if (v > 1) return 1;
+
+ return v;
+}
+
+static
+int ForwardSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
+{
+ FakeCMYKParams* p = (FakeCMYKParams*) Cargo;
+ cmsFloat64Number rgb[3], cmyk[4];
+ cmsFloat64Number c, m, y, k;
+
+ cmsDoTransform(p ->hLab2sRGB, In, rgb, 1);
+
+ c = 1 - rgb[0];
+ m = 1 - rgb[1];
+ y = 1 - rgb[2];
+
+ k = (c < m ? cmsmin(c, y) : cmsmin(m, y));
+
+ // NONSENSE WARNING!: I'm doing this just because this is a test
+ // profile that may have ink limit up to 400%. There is no UCR here
+ // so the profile is basically useless for anything but testing.
+
+ cmyk[0] = c;
+ cmyk[1] = m;
+ cmyk[2] = y;
+ cmyk[3] = k;
+
+ cmsDoTransform(p ->hIlimit, cmyk, Out, 1);
+
+ return 1;
+}
+
+
+static
+int ReverseSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
+{
+ FakeCMYKParams* p = (FakeCMYKParams*) Cargo;
+ cmsFloat64Number c, m, y, k, rgb[3];
+
+ c = In[0] / 65535.0;
+ m = In[1] / 65535.0;
+ y = In[2] / 65535.0;
+ k = In[3] / 65535.0;
+
+ if (k == 0) {
+
+ rgb[0] = Clip(1 - c);
+ rgb[1] = Clip(1 - m);
+ rgb[2] = Clip(1 - y);
+ }
+ else
+ if (k == 1) {
+
+ rgb[0] = rgb[1] = rgb[2] = 0;
+ }
+ else {
+
+ rgb[0] = Clip((1 - c) * (1 - k));
+ rgb[1] = Clip((1 - m) * (1 - k));
+ rgb[2] = Clip((1 - y) * (1 - k));
+ }
+
+ cmsDoTransform(p ->sRGB2Lab, rgb, Out, 1);
+ return 1;
+}
+
+
+
+static
+cmsHPROFILE* CreateFakeCMYK(cmsFloat64Number InkLimit, cmsBool lUseAboveRGB)
+{
+ cmsHPROFILE hICC;
+ cmsPipeline* AToB0, *BToA0;
+ cmsStage* CLUT;
+ cmsContext ContextID;
+ FakeCMYKParams p;
+ cmsHPROFILE hLab, hsRGB, hLimit;
+ cmsUInt16Number cmykfrm;
+
+
+ if (lUseAboveRGB)
+ hsRGB = Create_AboveRGB();
+ else
+ hsRGB = cmsCreate_sRGBProfile();
+
+ hLab = cmsCreateLab4Profile(NULL);
+ hLimit = cmsCreateInkLimitingDeviceLink(cmsSigCmykData, InkLimit);
+
+ cmykfrm = BYTES_SH(0)|CHANNELS_SH(4);
+ p.hLab2sRGB = cmsCreateTransform(hLab, TYPE_Lab_16, hsRGB, TYPE_RGB_DBL, INTENT_PERCEPTUAL, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE);
+ p.sRGB2Lab = cmsCreateTransform(hsRGB, TYPE_RGB_DBL, hLab, TYPE_Lab_16, INTENT_PERCEPTUAL, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE);
+ p.hIlimit = cmsCreateTransform(hLimit, cmykfrm, NULL, TYPE_CMYK_16, INTENT_PERCEPTUAL, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE);
+
+ cmsCloseProfile(hLab); cmsCloseProfile(hsRGB); cmsCloseProfile(hLimit);
+
+ ContextID = DbgThread();
+ hICC = cmsCreateProfilePlaceholder(ContextID);
+ if (!hICC) return NULL;
+
+ cmsSetProfileVersion(hICC, 4.2);
+
+ cmsSetDeviceClass(hICC, cmsSigOutputClass);
+ cmsSetColorSpace(hICC, cmsSigCmykData);
+ cmsSetPCS(hICC, cmsSigLabData);
+
+ BToA0 = cmsPipelineAlloc(ContextID, 3, 4);
+ if (BToA0 == NULL) return 0;
+ CLUT = cmsStageAllocCLut16bit(ContextID, 17, 3, 4, NULL);
+ if (CLUT == NULL) return 0;
+ if (!cmsStageSampleCLut16bit(CLUT, ForwardSampler, &p, 0)) return 0;
+
+ cmsPipelineInsertStage(BToA0, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3));
+ cmsPipelineInsertStage(BToA0, cmsAT_END, CLUT);
+ cmsPipelineInsertStage(BToA0, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, 4));
+
+ if (!cmsWriteTag(hICC, cmsSigBToA0Tag, (void*) BToA0)) return 0;
+ cmsPipelineFree(BToA0);
+
+ AToB0 = cmsPipelineAlloc(ContextID, 4, 3);
+ if (AToB0 == NULL) return 0;
+ CLUT = cmsStageAllocCLut16bit(ContextID, 17, 4, 3, NULL);
+ if (CLUT == NULL) return 0;
+ if (!cmsStageSampleCLut16bit(CLUT, ReverseSampler, &p, 0)) return 0;
+
+ cmsPipelineInsertStage(AToB0, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 4));
+ cmsPipelineInsertStage(AToB0, cmsAT_END, CLUT);
+ cmsPipelineInsertStage(AToB0, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, 3));
+
+ if (!cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) AToB0)) return 0;
+ cmsPipelineFree(AToB0);
+
+ cmsDeleteTransform(p.hLab2sRGB);
+ cmsDeleteTransform(p.sRGB2Lab);
+ cmsDeleteTransform(p.hIlimit);
+
+ cmsLinkTag(hICC, cmsSigAToB1Tag, cmsSigAToB0Tag);
+ cmsLinkTag(hICC, cmsSigAToB2Tag, cmsSigAToB0Tag);
+ cmsLinkTag(hICC, cmsSigBToA1Tag, cmsSigBToA0Tag);
+ cmsLinkTag(hICC, cmsSigBToA2Tag, cmsSigBToA0Tag);
+
+ return hICC;
+}
+
+
+// Does create several profiles for latter use------------------------------------------------------------------------------------------------
+
+static
+int OneVirtual(cmsHPROFILE h, const char* SubTestTxt, const char* FileName)
+{
+ SubTest(SubTestTxt);
+ if (h == NULL) return 0;
+
+ if (!cmsSaveProfileToFile(h, FileName)) return 0;
+ cmsCloseProfile(h);
+
+ h = cmsOpenProfileFromFile(FileName, "r");
+ if (h == NULL) return 0;
+
+ // Do some teste....
+
+ cmsCloseProfile(h);
+
+ return 1;
+}
+
+
+
+// This test checks the ability of lcms2 to save its built-ins as valid profiles.
+// It does not check the functionality of such profiles
+static
+int CreateTestProfiles(void)
+{
+ cmsHPROFILE h;
+
+ h = cmsCreate_sRGBProfileTHR(DbgThread());
+ if (!OneVirtual(h, "sRGB profile", "sRGBlcms2.icc")) return 0;
+
+ // ----
+
+ h = Create_AboveRGB();
+ if (!OneVirtual(h, "aRGB profile", "aRGBlcms2.icc")) return 0;
+
+ // ----
+
+ h = Create_Gray22();
+ if (!OneVirtual(h, "Gray profile", "graylcms2.icc")) return 0;
+
+ // ----
+
+ h = Create_CMYK_DeviceLink();
+ if (!OneVirtual(h, "Linearization profile", "linlcms2.icc")) return 0;
+
+ // -------
+ h = cmsCreateInkLimitingDeviceLinkTHR(DbgThread(), cmsSigCmykData, 150);
+ if (h == NULL) return 0;
+ if (!OneVirtual(h, "Ink-limiting profile", "limitlcms2.icc")) return 0;
+
+ // ------
+
+ h = cmsCreateLab2ProfileTHR(DbgThread(), NULL);
+ if (!OneVirtual(h, "Lab 2 identity profile", "labv2lcms2.icc")) return 0;
+
+ // ----
+
+ h = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
+ if (!OneVirtual(h, "Lab 4 identity profile", "labv4lcms2.icc")) return 0;
+
+ // ----
+
+ h = cmsCreateXYZProfileTHR(DbgThread());
+ if (!OneVirtual(h, "XYZ identity profile", "xyzlcms2.icc")) return 0;
+
+ // ----
+
+ h = cmsCreateNULLProfileTHR(DbgThread());
+ if (!OneVirtual(h, "NULL profile", "nullcms2.icc")) return 0;
+
+ // ---
+
+ h = cmsCreateBCHSWabstractProfileTHR(DbgThread(), 17, 0, 0, 0, 0, 5000, 6000);
+ if (!OneVirtual(h, "BCHS profile", "bchslcms2.icc")) return 0;
+
+ // ---
+
+ h = CreateFakeCMYK(300, FALSE);
+ if (!OneVirtual(h, "Fake CMYK profile", "lcms2cmyk.icc")) return 0;
+
+ return 1;
+}
+
+static
+void RemoveTestProfiles(void)
+{
+ remove("sRGBlcms2.icc");
+ remove("aRGBlcms2.icc");
+ remove("graylcms2.icc");
+ remove("linlcms2.icc");
+ remove("limitlcms2.icc");
+ remove("labv2lcms2.icc");
+ remove("labv4lcms2.icc");
+ remove("xyzlcms2.icc");
+ remove("nullcms2.icc");
+ remove("bchslcms2.icc");
+ remove("lcms2cmyk.icc");
+}
+
+// -------------------------------------------------------------------------------------------------
+
+// Check the size of basic types. If this test fails, nothing is going to work anyway
+static
+int CheckBaseTypes(void)
+{
+ if (sizeof(cmsUInt8Number) != 1) return 0;
+ if (sizeof(cmsInt8Number) != 1) return 0;
+ if (sizeof(cmsUInt16Number) != 2) return 0;
+ if (sizeof(cmsInt16Number) != 2) return 0;
+ if (sizeof(cmsUInt32Number) != 4) return 0;
+ if (sizeof(cmsInt32Number) != 4) return 0;
+ if (sizeof(cmsUInt64Number) != 8) return 0;
+ if (sizeof(cmsInt64Number) != 8) return 0;
+ if (sizeof(cmsFloat32Number) != 4) return 0;
+ if (sizeof(cmsFloat64Number) != 8) return 0;
+ if (sizeof(cmsSignature) != 4) return 0;
+ if (sizeof(cmsU8Fixed8Number) != 2) return 0;
+ if (sizeof(cmsS15Fixed16Number) != 4) return 0;
+ if (sizeof(cmsU16Fixed16Number) != 4) return 0;
+
+ return 1;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+
+// Are we little or big endian? From Harbison&Steele.
+static
+int CheckEndianess(void)
+{
+ int BigEndian, IsOk;
+ union {
+ long l;
+ char c[sizeof (long)];
+ } u;
+
+ u.l = 1;
+ BigEndian = (u.c[sizeof (long) - 1] == 1);
+
+#ifdef CMS_USE_BIG_ENDIAN
+ IsOk = BigEndian;
+#else
+ IsOk = !BigEndian;
+#endif
+
+ if (!IsOk) {
+ Fail("\nOOOPPSS! You have CMS_USE_BIG_ENDIAN toggle misconfigured!\n\n"
+ "Please, edit lcms2.h and %s the CMS_USE_BIG_ENDIAN toggle.\n", BigEndian? "uncomment" : "comment");
+ return 0;
+ }
+
+ return 1;
+}
+
+// Check quick floor
+static
+int CheckQuickFloor(void)
+{
+ if ((_cmsQuickFloor(1.234) != 1) ||
+ (_cmsQuickFloor(32767.234) != 32767) ||
+ (_cmsQuickFloor(-1.234) != -2) ||
+ (_cmsQuickFloor(-32767.1) != -32768)) {
+
+ Fail("\nOOOPPSS! _cmsQuickFloor() does not work as expected in your machine!\n\n"
+ "Please, edit lcms.h and uncomment the CMS_DONT_USE_FAST_FLOOR toggle.\n");
+ return 0;
+
+ }
+
+ return 1;
+}
+
+// Quick floor restricted to word
+static
+int CheckQuickFloorWord(void)
+{
+ cmsUInt32Number i;
+
+ for (i=0; i < 65535; i++) {
+
+ if (_cmsQuickFloorWord((cmsFloat64Number) i + 0.1234) != i) {
+
+ Fail("\nOOOPPSS! _cmsQuickFloorWord() does not work as expected in your machine!\n\n"
+ "Please, edit lcms.h and uncomment the CMS_DONT_USE_FAST_FLOOR toggle.\n");
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+// -------------------------------------------------------------------------------------------------
+
+// Precision stuff.
+
+// On 15.16 fixed point, this is the maximum we can obtain. Remember ICC profiles have storage limits on this number
+#define FIXED_PRECISION_15_16 (1.0 / 65535.0)
+
+// On 8.8 fixed point, that is the max we can obtain.
+#define FIXED_PRECISION_8_8 (1.0 / 255.0)
+
+// On cmsFloat32Number type, this is the precision we expect
+#define FLOAT_PRECISSION (0.00001)
+
+static cmsFloat64Number MaxErr;
+static cmsFloat64Number AllowedErr = FIXED_PRECISION_15_16;
+
+static
+cmsBool IsGoodVal(const char *title, cmsFloat64Number in, cmsFloat64Number out, cmsFloat64Number max)
+{
+ cmsFloat64Number Err = fabs(in - out);
+
+ if (Err > MaxErr) MaxErr = Err;
+
+ if ((Err > max )) {
+
+ Fail("(%s): Must be %f, But is %f ", title, in, out);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static
+cmsBool IsGoodFixed15_16(const char *title, cmsFloat64Number in, cmsFloat64Number out)
+{
+ return IsGoodVal(title, in, out, FIXED_PRECISION_15_16);
+}
+
+static
+cmsBool IsGoodFixed8_8(const char *title, cmsFloat64Number in, cmsFloat64Number out)
+{
+ return IsGoodVal(title, in, out, FIXED_PRECISION_8_8);
+}
+
+static
+cmsBool IsGoodWord(const char *title, cmsUInt16Number in, cmsUInt16Number out)
+{
+ if ((abs(in - out) > 0 )) {
+
+ Fail("(%s): Must be %x, But is %x ", title, in, out);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+// Fixed point ----------------------------------------------------------------------------------------------
+
+static
+int TestSingleFixed15_16(cmsFloat64Number d)
+{
+ cmsS15Fixed16Number f = _cmsDoubleTo15Fixed16(d);
+ cmsFloat64Number RoundTrip = _cms15Fixed16toDouble(f);
+ cmsFloat64Number Error = fabs(d - RoundTrip);
+
+ return ( Error <= FIXED_PRECISION_15_16);
+}
+
+static
+int CheckFixedPoint15_16(void)
+{
+ if (!TestSingleFixed15_16(1.0)) return 0;
+ if (!TestSingleFixed15_16(2.0)) return 0;
+ if (!TestSingleFixed15_16(1.23456)) return 0;
+ if (!TestSingleFixed15_16(0.99999)) return 0;
+ if (!TestSingleFixed15_16(0.1234567890123456789099999)) return 0;
+ if (!TestSingleFixed15_16(-1.0)) return 0;
+ if (!TestSingleFixed15_16(-2.0)) return 0;
+ if (!TestSingleFixed15_16(-1.23456)) return 0;
+ if (!TestSingleFixed15_16(-1.1234567890123456789099999)) return 0;
+ if (!TestSingleFixed15_16(+32767.1234567890123456789099999)) return 0;
+ if (!TestSingleFixed15_16(-32767.1234567890123456789099999)) return 0;
+ return 1;
+}
+
+static
+int TestSingleFixed8_8(cmsFloat64Number d)
+{
+ cmsS15Fixed16Number f = _cmsDoubleTo8Fixed8(d);
+ cmsFloat64Number RoundTrip = _cms8Fixed8toDouble((cmsUInt16Number) f);
+ cmsFloat64Number Error = fabs(d - RoundTrip);
+
+ return ( Error <= FIXED_PRECISION_8_8);
+}
+
+static
+int CheckFixedPoint8_8(void)
+{
+ if (!TestSingleFixed8_8(1.0)) return 0;
+ if (!TestSingleFixed8_8(2.0)) return 0;
+ if (!TestSingleFixed8_8(1.23456)) return 0;
+ if (!TestSingleFixed8_8(0.99999)) return 0;
+ if (!TestSingleFixed8_8(0.1234567890123456789099999)) return 0;
+ if (!TestSingleFixed8_8(+255.1234567890123456789099999)) return 0;
+
+ return 1;
+}
+
+// Linear interpolation -----------------------------------------------------------------------------------------------
+
+// Since prime factors of 65535 (FFFF) are,
+//
+// 0xFFFF = 3 * 5 * 17 * 257
+//
+// I test tables of 2, 4, 6, and 18 points, that will be exact.
+
+static
+void BuildTable(int n, cmsUInt16Number Tab[], cmsBool Descending)
+{
+ int i;
+
+ for (i=0; i < n; i++) {
+ cmsFloat64Number v = (cmsFloat64Number) ((cmsFloat64Number) 65535.0 * i ) / (n-1);
+
+ Tab[Descending ? (n - i - 1) : i ] = (cmsUInt16Number) floor(v + 0.5);
+ }
+}
+
+// A single function that does check 1D interpolation
+// nNodesToCheck = number on nodes to check
+// Down = Create decreasing tables
+// Reverse = Check reverse interpolation
+// max_err = max allowed error
+
+static
+int Check1D(int nNodesToCheck, cmsBool Down, int max_err)
+{
+ cmsUInt32Number i;
+ cmsUInt16Number in, out;
+ cmsInterpParams* p;
+ cmsUInt16Number* Tab;
+
+ Tab = (cmsUInt16Number*) malloc(sizeof(cmsUInt16Number)* nNodesToCheck);
+ if (Tab == NULL) return 0;
+
+ p = _cmsComputeInterpParams(DbgThread(), nNodesToCheck, 1, 1, Tab, CMS_LERP_FLAGS_16BITS);
+ if (p == NULL) return 0;
+
+ BuildTable(nNodesToCheck, Tab, Down);
+
+ for (i=0; i <= 0xffff; i++) {
+
+ in = (cmsUInt16Number) i;
+ out = 0;
+
+ p ->Interpolation.Lerp16(&in, &out, p);
+
+ if (Down) out = 0xffff - out;
+
+ if (abs(out - in) > max_err) {
+
+ Fail("(%dp): Must be %x, But is %x : ", nNodesToCheck, in, out);
+ _cmsFreeInterpParams(p);
+ free(Tab);
+ return 0;
+ }
+ }
+
+ _cmsFreeInterpParams(p);
+ free(Tab);
+ return 1;
+}
+
+
+static
+int Check1DLERP2(void)
+{
+ return Check1D(2, FALSE, 0);
+}
+
+
+static
+int Check1DLERP3(void)
+{
+ return Check1D(3, FALSE, 1);
+}
+
+
+static
+int Check1DLERP4(void)
+{
+ return Check1D(4, FALSE, 0);
+}
+
+static
+int Check1DLERP6(void)
+{
+ return Check1D(6, FALSE, 0);
+}
+
+static
+int Check1DLERP18(void)
+{
+ return Check1D(18, FALSE, 0);
+}
+
+
+static
+int Check1DLERP2Down(void)
+{
+ return Check1D(2, TRUE, 0);
+}
+
+
+static
+int Check1DLERP3Down(void)
+{
+ return Check1D(3, TRUE, 1);
+}
+
+static
+int Check1DLERP6Down(void)
+{
+ return Check1D(6, TRUE, 0);
+}
+
+static
+int Check1DLERP18Down(void)
+{
+ return Check1D(18, TRUE, 0);
+}
+
+static
+int ExhaustiveCheck1DLERP(void)
+{
+ cmsUInt32Number j;
+
+ printf("\n");
+ for (j=10; j <= 4096; j++) {
+
+ if ((j % 10) == 0) printf("%d \r", j);
+
+ if (!Check1D(j, FALSE, 1)) return 0;
+ }
+
+ printf("\rResult is ");
+ return 1;
+}
+
+static
+int ExhaustiveCheck1DLERPDown(void)
+{
+ cmsUInt32Number j;
+
+ printf("\n");
+ for (j=10; j <= 4096; j++) {
+
+ if ((j % 10) == 0) printf("%d \r", j);
+
+ if (!Check1D(j, TRUE, 1)) return 0;
+ }
+
+
+ printf("\rResult is ");
+ return 1;
+}
+
+
+
+// 3D interpolation -------------------------------------------------------------------------------------------------
+
+static
+int Check3DinterpolationFloatTetrahedral(void)
+{
+ cmsInterpParams* p;
+ int i;
+ cmsFloat32Number In[3], Out[3];
+ cmsFloat32Number FloatTable[] = { //R G B
+
+ 0, 0, 0, // B=0,G=0,R=0
+ 0, 0, .25, // B=1,G=0,R=0
+
+ 0, .5, 0, // B=0,G=1,R=0
+ 0, .5, .25, // B=1,G=1,R=0
+
+ 1, 0, 0, // B=0,G=0,R=1
+ 1, 0, .25, // B=1,G=0,R=1
+
+ 1, .5, 0, // B=0,G=1,R=1
+ 1, .5, .25 // B=1,G=1,R=1
+
+ };
+
+ p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, FloatTable, CMS_LERP_FLAGS_FLOAT);
+
+
+ MaxErr = 0.0;
+ for (i=0; i < 0xffff; i++) {
+
+ In[0] = In[1] = In[2] = (cmsFloat32Number) ( (cmsFloat32Number) i / 65535.0F);
+
+ p ->Interpolation.LerpFloat(In, Out, p);
+
+ if (!IsGoodFixed15_16("Channel 1", Out[0], In[0])) goto Error;
+ if (!IsGoodFixed15_16("Channel 2", Out[1], (cmsFloat32Number) In[1] / 2.F)) goto Error;
+ if (!IsGoodFixed15_16("Channel 3", Out[2], (cmsFloat32Number) In[2] / 4.F)) goto Error;
+ }
+
+ if (MaxErr > 0) printf("|Err|<%lf ", MaxErr);
+ _cmsFreeInterpParams(p);
+ return 1;
+
+Error:
+ _cmsFreeInterpParams(p);
+ return 0;
+}
+
+static
+int Check3DinterpolationFloatTrilinear(void)
+{
+ cmsInterpParams* p;
+ int i;
+ cmsFloat32Number In[3], Out[3];
+ cmsFloat32Number FloatTable[] = { //R G B
+
+ 0, 0, 0, // B=0,G=0,R=0
+ 0, 0, .25, // B=1,G=0,R=0
+
+ 0, .5, 0, // B=0,G=1,R=0
+ 0, .5, .25, // B=1,G=1,R=0
+
+ 1, 0, 0, // B=0,G=0,R=1
+ 1, 0, .25, // B=1,G=0,R=1
+
+ 1, .5, 0, // B=0,G=1,R=1
+ 1, .5, .25 // B=1,G=1,R=1
+
+ };
+
+ p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, FloatTable, CMS_LERP_FLAGS_FLOAT|CMS_LERP_FLAGS_TRILINEAR);
+
+ MaxErr = 0.0;
+ for (i=0; i < 0xffff; i++) {
+
+ In[0] = In[1] = In[2] = (cmsFloat32Number) ( (cmsFloat32Number) i / 65535.0F);
+
+ p ->Interpolation.LerpFloat(In, Out, p);
+
+ if (!IsGoodFixed15_16("Channel 1", Out[0], In[0])) goto Error;
+ if (!IsGoodFixed15_16("Channel 2", Out[1], (cmsFloat32Number) In[1] / 2.F)) goto Error;
+ if (!IsGoodFixed15_16("Channel 3", Out[2], (cmsFloat32Number) In[2] / 4.F)) goto Error;
+ }
+
+ if (MaxErr > 0) printf("|Err|<%lf ", MaxErr);
+ _cmsFreeInterpParams(p);
+ return 1;
+
+Error:
+ _cmsFreeInterpParams(p);
+ return 0;
+
+}
+
+static
+int Check3DinterpolationTetrahedral16(void)
+{
+ cmsInterpParams* p;
+ int i;
+ cmsUInt16Number In[3], Out[3];
+ cmsUInt16Number Table[] = {
+
+ 0, 0, 0,
+ 0, 0, 0xffff,
+
+ 0, 0xffff, 0,
+ 0, 0xffff, 0xffff,
+
+ 0xffff, 0, 0,
+ 0xffff, 0, 0xffff,
+
+ 0xffff, 0xffff, 0,
+ 0xffff, 0xffff, 0xffff
+ };
+
+ p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, Table, CMS_LERP_FLAGS_16BITS);
+
+ MaxErr = 0.0;
+ for (i=0; i < 0xffff; i++) {
+
+ In[0] = In[1] = In[2] = i;
+
+ p ->Interpolation.Lerp16(In, Out, p);
+
+ if (!IsGoodWord("Channel 1", Out[0], In[0])) goto Error;
+ if (!IsGoodWord("Channel 2", Out[1], In[1])) goto Error;
+ if (!IsGoodWord("Channel 3", Out[2], In[2])) goto Error;
+ }
+
+ if (MaxErr > 0) printf("|Err|<%lf ", MaxErr);
+ _cmsFreeInterpParams(p);
+ return 1;
+
+Error:
+ _cmsFreeInterpParams(p);
+ return 0;
+}
+
+static
+int Check3DinterpolationTrilinear16(void)
+{
+ cmsInterpParams* p;
+ int i;
+ cmsUInt16Number In[3], Out[3];
+ cmsUInt16Number Table[] = {
+
+ 0, 0, 0,
+ 0, 0, 0xffff,
+
+ 0, 0xffff, 0,
+ 0, 0xffff, 0xffff,
+
+ 0xffff, 0, 0,
+ 0xffff, 0, 0xffff,
+
+ 0xffff, 0xffff, 0,
+ 0xffff, 0xffff, 0xffff
+ };
+
+ p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, Table, CMS_LERP_FLAGS_TRILINEAR);
+
+ MaxErr = 0.0;
+ for (i=0; i < 0xffff; i++) {
+
+ In[0] = In[1] = In[2] = i;
+
+ p ->Interpolation.Lerp16(In, Out, p);
+
+ if (!IsGoodWord("Channel 1", Out[0], In[0])) goto Error;
+ if (!IsGoodWord("Channel 2", Out[1], In[1])) goto Error;
+ if (!IsGoodWord("Channel 3", Out[2], In[2])) goto Error;
+ }
+
+ if (MaxErr > 0) printf("|Err|<%lf ", MaxErr);
+ _cmsFreeInterpParams(p);
+ return 1;
+
+Error:
+ _cmsFreeInterpParams(p);
+ return 0;
+}
+
+
+static
+int ExaustiveCheck3DinterpolationFloatTetrahedral(void)
+{
+ cmsInterpParams* p;
+ int r, g, b;
+ cmsFloat32Number In[3], Out[3];
+ cmsFloat32Number FloatTable[] = { //R G B
+
+ 0, 0, 0, // B=0,G=0,R=0
+ 0, 0, .25, // B=1,G=0,R=0
+
+ 0, .5, 0, // B=0,G=1,R=0
+ 0, .5, .25, // B=1,G=1,R=0
+
+ 1, 0, 0, // B=0,G=0,R=1
+ 1, 0, .25, // B=1,G=0,R=1
+
+ 1, .5, 0, // B=0,G=1,R=1
+ 1, .5, .25 // B=1,G=1,R=1
+
+ };
+
+ p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, FloatTable, CMS_LERP_FLAGS_FLOAT);
+
+ MaxErr = 0.0;
+ for (r=0; r < 0xff; r++)
+ for (g=0; g < 0xff; g++)
+ for (b=0; b < 0xff; b++)
+ {
+
+ In[0] = (cmsFloat32Number) r / 255.0F;
+ In[1] = (cmsFloat32Number) g / 255.0F;
+ In[2] = (cmsFloat32Number) b / 255.0F;
+
+
+ p ->Interpolation.LerpFloat(In, Out, p);
+
+ if (!IsGoodFixed15_16("Channel 1", Out[0], In[0])) goto Error;
+ if (!IsGoodFixed15_16("Channel 2", Out[1], (cmsFloat32Number) In[1] / 2.F)) goto Error;
+ if (!IsGoodFixed15_16("Channel 3", Out[2], (cmsFloat32Number) In[2] / 4.F)) goto Error;
+ }
+
+ if (MaxErr > 0) printf("|Err|<%lf ", MaxErr);
+ _cmsFreeInterpParams(p);
+ return 1;
+
+Error:
+ _cmsFreeInterpParams(p);
+ return 0;
+}
+
+static
+int ExaustiveCheck3DinterpolationFloatTrilinear(void)
+{
+ cmsInterpParams* p;
+ int r, g, b;
+ cmsFloat32Number In[3], Out[3];
+ cmsFloat32Number FloatTable[] = { //R G B
+
+ 0, 0, 0, // B=0,G=0,R=0
+ 0, 0, .25, // B=1,G=0,R=0
+
+ 0, .5, 0, // B=0,G=1,R=0
+ 0, .5, .25, // B=1,G=1,R=0
+
+ 1, 0, 0, // B=0,G=0,R=1
+ 1, 0, .25, // B=1,G=0,R=1
+
+ 1, .5, 0, // B=0,G=1,R=1
+ 1, .5, .25 // B=1,G=1,R=1
+
+ };
+
+ p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, FloatTable, CMS_LERP_FLAGS_FLOAT|CMS_LERP_FLAGS_TRILINEAR);
+
+ MaxErr = 0.0;
+ for (r=0; r < 0xff; r++)
+ for (g=0; g < 0xff; g++)
+ for (b=0; b < 0xff; b++)
+ {
+
+ In[0] = (cmsFloat32Number) r / 255.0F;
+ In[1] = (cmsFloat32Number) g / 255.0F;
+ In[2] = (cmsFloat32Number) b / 255.0F;
+
+
+ p ->Interpolation.LerpFloat(In, Out, p);
+
+ if (!IsGoodFixed15_16("Channel 1", Out[0], In[0])) goto Error;
+ if (!IsGoodFixed15_16("Channel 2", Out[1], (cmsFloat32Number) In[1] / 2.F)) goto Error;
+ if (!IsGoodFixed15_16("Channel 3", Out[2], (cmsFloat32Number) In[2] / 4.F)) goto Error;
+ }
+
+ if (MaxErr > 0) printf("|Err|<%lf ", MaxErr);
+ _cmsFreeInterpParams(p);
+ return 1;
+
+Error:
+ _cmsFreeInterpParams(p);
+ return 0;
+
+}
+
+static
+int ExhaustiveCheck3DinterpolationTetrahedral16(void)
+{
+ cmsInterpParams* p;
+ int r, g, b;
+ cmsUInt16Number In[3], Out[3];
+ cmsUInt16Number Table[] = {
+
+ 0, 0, 0,
+ 0, 0, 0xffff,
+
+ 0, 0xffff, 0,
+ 0, 0xffff, 0xffff,
+
+ 0xffff, 0, 0,
+ 0xffff, 0, 0xffff,
+
+ 0xffff, 0xffff, 0,
+ 0xffff, 0xffff, 0xffff
+ };
+
+ p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, Table, CMS_LERP_FLAGS_16BITS);
+
+ for (r=0; r < 0xff; r++)
+ for (g=0; g < 0xff; g++)
+ for (b=0; b < 0xff; b++)
+ {
+ In[0] = r ;
+ In[1] = g ;
+ In[2] = b ;
+
+
+ p ->Interpolation.Lerp16(In, Out, p);
+
+ if (!IsGoodWord("Channel 1", Out[0], In[0])) goto Error;
+ if (!IsGoodWord("Channel 2", Out[1], In[1])) goto Error;
+ if (!IsGoodWord("Channel 3", Out[2], In[2])) goto Error;
+ }
+
+ _cmsFreeInterpParams(p);
+ return 1;
+
+Error:
+ _cmsFreeInterpParams(p);
+ return 0;
+}
+
+static
+int ExhaustiveCheck3DinterpolationTrilinear16(void)
+{
+ cmsInterpParams* p;
+ int r, g, b;
+ cmsUInt16Number In[3], Out[3];
+ cmsUInt16Number Table[] = {
+
+ 0, 0, 0,
+ 0, 0, 0xffff,
+
+ 0, 0xffff, 0,
+ 0, 0xffff, 0xffff,
+
+ 0xffff, 0, 0,
+ 0xffff, 0, 0xffff,
+
+ 0xffff, 0xffff, 0,
+ 0xffff, 0xffff, 0xffff
+ };
+
+ p = _cmsComputeInterpParams(DbgThread(), 2, 3, 3, Table, CMS_LERP_FLAGS_TRILINEAR);
+
+ for (r=0; r < 0xff; r++)
+ for (g=0; g < 0xff; g++)
+ for (b=0; b < 0xff; b++)
+ {
+ In[0] = r ;
+ In[1] = g ;
+ In[2] = b ;
+
+
+ p ->Interpolation.Lerp16(In, Out, p);
+
+ if (!IsGoodWord("Channel 1", Out[0], In[0])) goto Error;
+ if (!IsGoodWord("Channel 2", Out[1], In[1])) goto Error;
+ if (!IsGoodWord("Channel 3", Out[2], In[2])) goto Error;
+ }
+
+
+ _cmsFreeInterpParams(p);
+ return 1;
+
+Error:
+ _cmsFreeInterpParams(p);
+ return 0;
+}
+
+// Check reverse interpolation on LUTS. This is right now exclusively used by K preservation algorithm
+static
+int CheckReverseInterpolation3x3(void)
+{
+ cmsPipeline* Lut;
+ cmsStage* clut;
+ cmsFloat32Number Target[3], Result[3], Hint[3];
+ cmsFloat32Number err, max;
+ int i;
+ cmsUInt16Number Table[] = {
+
+ 0, 0, 0, // 0 0 0
+ 0, 0, 0xffff, // 0 0 1
+
+ 0, 0xffff, 0, // 0 1 0
+ 0, 0xffff, 0xffff, // 0 1 1
+
+ 0xffff, 0, 0, // 1 0 0
+ 0xffff, 0, 0xffff, // 1 0 1
+
+ 0xffff, 0xffff, 0, // 1 1 0
+ 0xffff, 0xffff, 0xffff, // 1 1 1
+ };
+
+
+
+ Lut = cmsPipelineAlloc(DbgThread(), 3, 3);
+
+ clut = cmsStageAllocCLut16bit(DbgThread(), 2, 3, 3, Table);
+ cmsPipelineInsertStage(Lut, cmsAT_BEGIN, clut);
+
+ Target[0] = 0; Target[1] = 0; Target[2] = 0;
+ Hint[0] = 0; Hint[1] = 0; Hint[2] = 0;
+ cmsPipelineEvalReverseFloat(Target, Result, NULL, Lut);
+ if (Result[0] != 0 || Result[1] != 0 || Result[2] != 0){
+
+ Fail("Reverse interpolation didn't find zero");
+ return 0;
+ }
+
+ // Transverse identity
+ max = 0;
+ for (i=0; i <= 100; i++) {
+
+ cmsFloat32Number in = i / 100.0F;
+
+ Target[0] = in; Target[1] = 0; Target[2] = 0;
+ cmsPipelineEvalReverseFloat(Target, Result, Hint, Lut);
+
+ err = fabsf(in - Result[0]);
+ if (err > max) max = err;
+
+ memcpy(Hint, Result, sizeof(Hint));
+ }
+
+ cmsPipelineFree(Lut);
+ return (max <= FLOAT_PRECISSION);
+}
+
+
+static
+int CheckReverseInterpolation4x3(void)
+{
+ cmsPipeline* Lut;
+ cmsStage* clut;
+ cmsFloat32Number Target[4], Result[4], Hint[4];
+ cmsFloat32Number err, max;
+ int i;
+
+ // 4 -> 3, output gets 3 first channels copied
+ cmsUInt16Number Table[] = {
+
+ 0, 0, 0, // 0 0 0 0 = ( 0, 0, 0)
+ 0, 0, 0, // 0 0 0 1 = ( 0, 0, 0)
+
+ 0, 0, 0xffff, // 0 0 1 0 = ( 0, 0, 1)
+ 0, 0, 0xffff, // 0 0 1 1 = ( 0, 0, 1)
+
+ 0, 0xffff, 0, // 0 1 0 0 = ( 0, 1, 0)
+ 0, 0xffff, 0, // 0 1 0 1 = ( 0, 1, 0)
+
+ 0, 0xffff, 0xffff, // 0 1 1 0 = ( 0, 1, 1)
+ 0, 0xffff, 0xffff, // 0 1 1 1 = ( 0, 1, 1)
+
+ 0xffff, 0, 0, // 1 0 0 0 = ( 1, 0, 0)
+ 0xffff, 0, 0, // 1 0 0 1 = ( 1, 0, 0)
+
+ 0xffff, 0, 0xffff, // 1 0 1 0 = ( 1, 0, 1)
+ 0xffff, 0, 0xffff, // 1 0 1 1 = ( 1, 0, 1)
+
+ 0xffff, 0xffff, 0, // 1 1 0 0 = ( 1, 1, 0)
+ 0xffff, 0xffff, 0, // 1 1 0 1 = ( 1, 1, 0)
+
+ 0xffff, 0xffff, 0xffff, // 1 1 1 0 = ( 1, 1, 1)
+ 0xffff, 0xffff, 0xffff, // 1 1 1 1 = ( 1, 1, 1)
+ };
+
+
+ Lut = cmsPipelineAlloc(DbgThread(), 4, 3);
+
+ clut = cmsStageAllocCLut16bit(DbgThread(), 2, 4, 3, Table);
+ cmsPipelineInsertStage(Lut, cmsAT_BEGIN, clut);
+
+ // Check if the LUT is behaving as expected
+ SubTest("4->3 feasibility");
+ for (i=0; i <= 100; i++) {
+
+ Target[0] = i / 100.0F;
+ Target[1] = Target[0];
+ Target[2] = 0;
+ Target[3] = 12;
+
+ cmsPipelineEvalFloat(Target, Result, Lut);
+
+ if (!IsGoodFixed15_16("0", Target[0], Result[0])) return 0;
+ if (!IsGoodFixed15_16("1", Target[1], Result[1])) return 0;
+ if (!IsGoodFixed15_16("2", Target[2], Result[2])) return 0;
+ }
+
+ SubTest("4->3 zero");
+ Target[0] = 0;
+ Target[1] = 0;
+ Target[2] = 0;
+
+ // This one holds the fixed K
+ Target[3] = 0;
+
+ // This is our hint (which is a big lie in this case)
+ Hint[0] = 0.1F; Hint[1] = 0.1F; Hint[2] = 0.1F;
+
+ cmsPipelineEvalReverseFloat(Target, Result, Hint, Lut);
+
+ if (Result[0] != 0 || Result[1] != 0 || Result[2] != 0 || Result[3] != 0){
+
+ Fail("Reverse interpolation didn't find zero");
+ return 0;
+ }
+
+ SubTest("4->3 find CMY");
+ max = 0;
+ for (i=0; i <= 100; i++) {
+
+ cmsFloat32Number in = i / 100.0F;
+
+ Target[0] = in; Target[1] = 0; Target[2] = 0;
+ cmsPipelineEvalReverseFloat(Target, Result, Hint, Lut);
+
+ err = fabsf(in - Result[0]);
+ if (err > max) max = err;
+
+ memcpy(Hint, Result, sizeof(Hint));
+ }
+
+ cmsPipelineFree(Lut);
+ return (max <= FLOAT_PRECISSION);
+}
+
+
+// Colorimetric conversions -------------------------------------------------------------------------------------------------
+
+// Lab to LCh and back should be performed at 1E-12 accuracy at least
+static
+int CheckLab2LCh(void)
+{
+ int l, a, b;
+ cmsFloat64Number dist, Max = 0;
+ cmsCIELab Lab, Lab2;
+ cmsCIELCh LCh;
+
+ for (l=0; l <= 100; l += 10) {
+
+ for (a=-128; a <= +128; a += 8) {
+
+ for (b=-128; b <= 128; b += 8) {
+
+ Lab.L = l;
+ Lab.a = a;
+ Lab.b = b;
+
+ cmsLab2LCh(&LCh, &Lab);
+ cmsLCh2Lab(&Lab2, &LCh);
+
+ dist = cmsDeltaE(&Lab, &Lab2);
+ if (dist > Max) Max = dist;
+ }
+ }
+ }
+
+ return Max < 1E-12;
+}
+
+// Lab to LCh and back should be performed at 1E-12 accuracy at least
+static
+int CheckLab2XYZ(void)
+{
+ int l, a, b;
+ cmsFloat64Number dist, Max = 0;
+ cmsCIELab Lab, Lab2;
+ cmsCIEXYZ XYZ;
+
+ for (l=0; l <= 100; l += 10) {
+
+ for (a=-128; a <= +128; a += 8) {
+
+ for (b=-128; b <= 128; b += 8) {
+
+ Lab.L = l;
+ Lab.a = a;
+ Lab.b = b;
+
+ cmsLab2XYZ(NULL, &XYZ, &Lab);
+ cmsXYZ2Lab(NULL, &Lab2, &XYZ);
+
+ dist = cmsDeltaE(&Lab, &Lab2);
+ if (dist > Max) Max = dist;
+
+ }
+ }
+ }
+
+ return Max < 1E-12;
+}
+
+// Lab to xyY and back should be performed at 1E-12 accuracy at least
+static
+int CheckLab2xyY(void)
+{
+ int l, a, b;
+ cmsFloat64Number dist, Max = 0;
+ cmsCIELab Lab, Lab2;
+ cmsCIEXYZ XYZ;
+ cmsCIExyY xyY;
+
+ for (l=0; l <= 100; l += 10) {
+
+ for (a=-128; a <= +128; a += 8) {
+
+ for (b=-128; b <= 128; b += 8) {
+
+ Lab.L = l;
+ Lab.a = a;
+ Lab.b = b;
+
+ cmsLab2XYZ(NULL, &XYZ, &Lab);
+ cmsXYZ2xyY(&xyY, &XYZ);
+ cmsxyY2XYZ(&XYZ, &xyY);
+ cmsXYZ2Lab(NULL, &Lab2, &XYZ);
+
+ dist = cmsDeltaE(&Lab, &Lab2);
+ if (dist > Max) Max = dist;
+
+ }
+ }
+ }
+
+ return Max < 1E-12;
+}
+
+
+static
+int CheckLabV2encoding(void)
+{
+ int n2, i, j;
+ cmsUInt16Number Inw[3], aw[3];
+ cmsCIELab Lab;
+
+ n2=0;
+
+ for (j=0; j < 65535; j++) {
+
+ Inw[0] = Inw[1] = Inw[2] = j;
+
+ cmsLabEncoded2FloatV2(&Lab, Inw);
+ cmsFloat2LabEncodedV2(aw, &Lab);
+
+ for (i=0; i < 3; i++) {
+
+ if (aw[i] != j) {
+ n2++;
+ }
+ }
+
+ }
+
+ return (n2 == 0);
+}
+
+static
+int CheckLabV4encoding(void)
+{
+ int n2, i, j;
+ cmsUInt16Number Inw[3], aw[3];
+ cmsCIELab Lab;
+
+ n2=0;
+
+ for (j=0; j < 65535; j++) {
+
+ Inw[0] = Inw[1] = Inw[2] = j;
+
+ cmsLabEncoded2Float(&Lab, Inw);
+ cmsFloat2LabEncoded(aw, &Lab);
+
+ for (i=0; i < 3; i++) {
+
+ if (aw[i] != j) {
+ n2++;
+ }
+ }
+
+ }
+
+ return (n2 == 0);
+}
+
+
+// BlackBody -----------------------------------------------------------------------------------------------------
+
+static
+int CheckTemp2CHRM(void)
+{
+ int j;
+ cmsFloat64Number d, v, Max = 0;
+ cmsCIExyY White;
+
+ for (j=4000; j < 25000; j++) {
+
+ cmsWhitePointFromTemp(&White, j);
+ if (!cmsTempFromWhitePoint(&v, &White)) return 0;
+
+ d = fabs(v - j);
+ if (d > Max) Max = d;
+ }
+
+ // 100 degree is the actual resolution
+ return (Max < 100);
+}
+
+
+
+// Tone curves -----------------------------------------------------------------------------------------------------
+
+static
+int CheckGammaEstimation(cmsToneCurve* c, cmsFloat64Number g)
+{
+ cmsFloat64Number est = cmsEstimateGamma(c, 0.001);
+
+ SubTest("Gamma estimation");
+ if (fabs(est - g) > 0.001) return 0;
+ return 1;
+}
+
+static
+int CheckGammaCreation16(void)
+{
+ cmsToneCurve* LinGamma = cmsBuildGamma(DbgThread(), 1.0);
+ int i;
+ cmsUInt16Number in, out;
+
+ for (i=0; i < 0xffff; i++) {
+
+ in = i;
+ out = cmsEvalToneCurve16(LinGamma, in);
+ if (in != out) {
+ Fail("(lin gamma): Must be %x, But is %x : ", in, out);
+ cmsFreeToneCurve(LinGamma);
+ return 0;
+ }
+ }
+
+ if (!CheckGammaEstimation(LinGamma, 1.0)) return 0;
+
+ cmsFreeToneCurve(LinGamma);
+ return 1;
+
+}
+
+static
+int CheckGammaCreationFlt(void)
+{
+ cmsToneCurve* LinGamma = cmsBuildGamma(DbgThread(), 1.0);
+ int i;
+ cmsFloat32Number in, out;
+
+ for (i=0; i < 0xffff; i++) {
+
+ in = (cmsFloat32Number) (i / 65535.0);
+ out = cmsEvalToneCurveFloat(LinGamma, in);
+ if (fabs(in - out) > (1/65535.0)) {
+ Fail("(lin gamma): Must be %f, But is %f : ", in, out);
+ cmsFreeToneCurve(LinGamma);
+ return 0;
+ }
+ }
+
+ if (!CheckGammaEstimation(LinGamma, 1.0)) return 0;
+ cmsFreeToneCurve(LinGamma);
+ return 1;
+}
+
+// Curve curves using a single power function
+// Error is given in 0..ffff counts
+static
+int CheckGammaFloat(int nPoints, cmsFloat64Number g)
+{
+ cmsToneCurve* Curve = cmsBuildGamma(DbgThread(), g);
+ int i;
+ cmsFloat32Number in, out;
+ cmsFloat64Number val, Err;
+
+ MaxErr = 0.0;
+ for (i=0; i < 0xffff; i++) {
+
+ in = (cmsFloat32Number) (i / 65535.0);
+ out = cmsEvalToneCurveFloat(Curve, in);
+ val = pow(in, g);
+
+ Err = fabs( val - out);
+ if (Err > MaxErr) MaxErr = Err;
+ }
+
+ if (MaxErr > 0) printf("|Err|<%lf ", MaxErr * 65535.0);
+
+ if (!CheckGammaEstimation(Curve, g)) return 0;
+
+ cmsFreeToneCurve(Curve);
+ return 1;
+}
+
+static int CheckGamma18(void)
+{
+ return CheckGammaFloat(256, 1.8);
+}
+
+static int CheckGamma22(void)
+{
+ return CheckGammaFloat(256, 2.2);
+}
+
+static int CheckGamma30(void)
+{
+ return CheckGammaFloat(256, 3.0);
+}
+
+
+// Check table-based gamma functions
+static
+int CheckGammaFloatTable(cmsFloat64Number g)
+{
+ cmsFloat32Number Values[1025];
+ cmsToneCurve* Curve;
+ int i;
+ cmsFloat32Number in, out;
+ cmsFloat64Number val, Err;
+
+ for (i=0; i <= 1024; i++) {
+
+ in = (cmsFloat32Number) (i / 1024.0);
+ Values[i] = powf(in, g);
+ }
+
+ Curve = cmsBuildTabulatedToneCurveFloat(DbgThread(), 1025, Values);
+
+ MaxErr = 0.0;
+ for (i=0; i <= 0xffff; i++) {
+
+ in = (cmsFloat32Number) (i / 65535.0);
+ out = cmsEvalToneCurveFloat(Curve, in);
+ val = pow(in, g);
+
+ Err = fabs(val - out);
+ if (Err > MaxErr) MaxErr = Err;
+ }
+
+ if (MaxErr > 0) printf("|Err|<%lf ", MaxErr * 65535.0);
+
+ if (!CheckGammaEstimation(Curve, g)) return 0;
+
+ cmsFreeToneCurve(Curve);
+ return 1;
+}
+
+
+static int CheckGamma18Table(void)
+{
+ return CheckGammaFloatTable(1.8);
+}
+
+static int CheckGamma22Table(void)
+{
+ return CheckGammaFloatTable(2.2);
+}
+
+static int CheckGamma30Table(void)
+{
+ return CheckGammaFloatTable(3.0);
+}
+
+// Create a curve from a table (which is a pure gamma function) and check it against the pow function.
+static
+int CheckGammaWordTable(cmsFloat64Number g)
+{
+ cmsUInt16Number Values[1025];
+ cmsToneCurve* Curve;
+ int i;
+ cmsFloat32Number in, out;
+ cmsFloat64Number val, Err;
+
+ for (i=0; i <= 1024; i++) {
+
+ in = (cmsFloat32Number) (i / 1024.0);
+ Values[i] = (cmsUInt16Number) floor(powf(in, g) * 65535.0 + 0.5);
+ }
+
+ Curve = cmsBuildTabulatedToneCurve16(DbgThread(), 1025, Values);
+
+ MaxErr = 0.0;
+ for (i=0; i <= 0xffff; i++) {
+
+ in = (cmsFloat32Number) (i / 65535.0);
+ out = cmsEvalToneCurveFloat(Curve, in);
+ val = pow(in, g);
+
+ Err = fabs(val - out);
+ if (Err > MaxErr) MaxErr = Err;
+ }
+
+ if (MaxErr > 0) printf("|Err|<%lf ", MaxErr * 65535.0);
+
+ if (!CheckGammaEstimation(Curve, g)) return 0;
+
+ cmsFreeToneCurve(Curve);
+ return 1;
+}
+
+static int CheckGamma18TableWord(void)
+{
+ return CheckGammaWordTable(1.8);
+}
+
+static int CheckGamma22TableWord(void)
+{
+ return CheckGammaWordTable(2.2);
+}
+
+static int CheckGamma30TableWord(void)
+{
+ return CheckGammaWordTable(3.0);
+}
+
+
+// Curve joining test. Joining two high-gamma of 3.0 curves should
+// give something like linear
+static
+int CheckJointCurves(void)
+{
+ cmsToneCurve *Forward, *Reverse, *Result;
+ cmsBool rc;
+
+ Forward = cmsBuildGamma(DbgThread(), 3.0);
+ Reverse = cmsBuildGamma(DbgThread(), 3.0);
+
+ Result = cmsJoinToneCurve(DbgThread(), Forward, Reverse, 256);
+
+ cmsFreeToneCurve(Forward); cmsFreeToneCurve(Reverse);
+
+ rc = cmsIsToneCurveLinear(Result);
+ cmsFreeToneCurve(Result);
+
+ if (!rc)
+ Fail("Joining same curve twice does not result in a linear ramp");
+
+ return rc;
+}
+
+
+// Create a gamma curve by cheating the table
+static
+cmsToneCurve* GammaTableLinear(int nEntries, cmsBool Dir)
+{
+ int i;
+ cmsToneCurve* g = cmsBuildTabulatedToneCurve16(DbgThread(), nEntries, NULL);
+
+ for (i=0; i < nEntries; i++) {
+
+ int v = _cmsQuantizeVal(i, nEntries);
+
+ if (Dir)
+ g->Table16[i] = v;
+ else
+ g->Table16[i] = 0xFFFF - v;
+ }
+
+ return g;
+}
+
+
+static
+int CheckJointCurvesDescending(void)
+{
+ cmsToneCurve *Forward, *Reverse, *Result;
+ int i, rc;
+
+ Forward = cmsBuildGamma(DbgThread(), 2.2);
+
+ // Fake the curve to be table-based
+
+ for (i=0; i < 4096; i++)
+ Forward ->Table16[i] = 0xffff - Forward->Table16[i];
+ Forward ->Segments[0].Type = 0;
+
+ Reverse = cmsReverseToneCurve(Forward);
+
+ Result = cmsJoinToneCurve(DbgThread(), Reverse, Reverse, 256);
+
+ cmsFreeToneCurve(Forward);
+ cmsFreeToneCurve(Reverse);
+
+ rc = cmsIsToneCurveLinear(Result);
+ cmsFreeToneCurve(Result);
+
+ return rc;
+}
+
+
+static
+int CheckFToneCurvePoint(cmsToneCurve* c, int Point, int Value)
+{
+ int Result;
+
+ Result = cmsEvalToneCurve16(c, Point);
+
+ return (abs(Value - Result) < 2);
+}
+
+static
+int CheckReverseDegenerated(void)
+{
+ cmsToneCurve* p, *g;
+ cmsUInt16Number Tab[16];
+
+ Tab[0] = 0;
+ Tab[1] = 0;
+ Tab[2] = 0;
+ Tab[3] = 0;
+ Tab[4] = 0;
+ Tab[5] = 0x5555;
+ Tab[6] = 0x6666;
+ Tab[7] = 0x7777;
+ Tab[8] = 0x8888;
+ Tab[9] = 0x9999;
+ Tab[10]= 0xffff;
+ Tab[11]= 0xffff;
+ Tab[12]= 0xffff;
+ Tab[13]= 0xffff;
+ Tab[14]= 0xffff;
+ Tab[15]= 0xffff;
+
+ p = cmsBuildTabulatedToneCurve16(DbgThread(), 16, Tab);
+ g = cmsReverseToneCurve(p);
+
+ // Now let's check some points
+ if (!CheckFToneCurvePoint(g, 0x5555, 0x5555)) return 0;
+ if (!CheckFToneCurvePoint(g, 0x7777, 0x7777)) return 0;
+
+ // First point for zero
+ if (!CheckFToneCurvePoint(g, 0x0000, 0x4444)) return 0;
+
+ // Last point
+ if (!CheckFToneCurvePoint(g, 0xFFFF, 0xFFFF)) return 0;
+
+ cmsFreeToneCurve(p);
+ cmsFreeToneCurve(g);
+
+ return 1;
+}
+
+
+// Build a parametric sRGB-like curve
+static
+cmsToneCurve* Build_sRGBGamma(void)
+{
+ cmsFloat64Number 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 cmsBuildParametricToneCurve(DbgThread(), 4, Parameters);
+}
+
+
+
+// Join two gamma tables in floting point format. Result should be a straight line
+static
+cmsToneCurve* CombineGammaFloat(cmsToneCurve* g1, cmsToneCurve* g2)
+{
+ cmsUInt16Number Tab[256];
+ cmsFloat32Number f;
+ int i;
+
+ for (i=0; i < 256; i++) {
+
+ f = (cmsFloat32Number) i / 255.0F;
+ f = cmsEvalToneCurveFloat(g2, cmsEvalToneCurveFloat(g1, f));
+
+ Tab[i] = (cmsUInt16Number) floor(f * 65535.0 + 0.5);
+ }
+
+ return cmsBuildTabulatedToneCurve16(DbgThread(), 256, Tab);
+}
+
+// Same of anterior, but using quantized tables
+static
+cmsToneCurve* CombineGamma16(cmsToneCurve* g1, cmsToneCurve* g2)
+{
+ cmsUInt16Number Tab[256];
+
+ int i;
+
+ for (i=0; i < 256; i++) {
+
+ cmsUInt16Number wValIn;
+
+ wValIn = _cmsQuantizeVal(i, 256);
+ Tab[i] = cmsEvalToneCurve16(g2, cmsEvalToneCurve16(g1, wValIn));
+ }
+
+ return cmsBuildTabulatedToneCurve16(DbgThread(), 256, Tab);
+}
+
+static
+int CheckJointFloatCurves_sRGB(void)
+{
+ cmsToneCurve *Forward, *Reverse, *Result;
+ cmsBool rc;
+
+ Forward = Build_sRGBGamma();
+ Reverse = cmsReverseToneCurve(Forward);
+ Result = CombineGammaFloat(Forward, Reverse);
+ cmsFreeToneCurve(Forward); cmsFreeToneCurve(Reverse);
+
+ rc = cmsIsToneCurveLinear(Result);
+ cmsFreeToneCurve(Result);
+
+ return rc;
+}
+
+static
+int CheckJoint16Curves_sRGB(void)
+{
+ cmsToneCurve *Forward, *Reverse, *Result;
+ cmsBool rc;
+
+ Forward = Build_sRGBGamma();
+ Reverse = cmsReverseToneCurve(Forward);
+ Result = CombineGamma16(Forward, Reverse);
+ cmsFreeToneCurve(Forward); cmsFreeToneCurve(Reverse);
+
+ rc = cmsIsToneCurveLinear(Result);
+ cmsFreeToneCurve(Result);
+
+ return rc;
+}
+
+// sigmoidal curve f(x) = (1-x^g) ^(1/g)
+
+static
+int CheckJointCurvesSShaped(void)
+{
+ cmsFloat64Number p = 3.2;
+ cmsToneCurve *Forward, *Reverse, *Result;
+ int rc;
+
+ Forward = cmsBuildParametricToneCurve(DbgThread(), 108, &p);
+ Reverse = cmsReverseToneCurve(Forward);
+ Result = cmsJoinToneCurve(DbgThread(), Forward, Forward, 4096);
+
+ cmsFreeToneCurve(Forward);
+ cmsFreeToneCurve(Reverse);
+
+ rc = cmsIsToneCurveLinear(Result);
+ cmsFreeToneCurve(Result);
+ return rc;
+}
+
+
+// --------------------------------------------------------------------------------------------------------
+
+// Implementation of some tone curve functions
+static
+cmsFloat32Number Gamma(cmsFloat32Number x, const cmsFloat64Number Params[])
+{
+ return (cmsFloat32Number) pow(x, Params[0]);
+}
+
+static
+cmsFloat32Number CIE122(cmsFloat32Number x, const cmsFloat64Number Params[])
+
+{
+ cmsFloat64Number e, Val;
+
+ if (x >= -Params[2] / Params[1]) {
+
+ e = Params[1]*x + Params[2];
+
+ if (e > 0)
+ Val = pow(e, Params[0]);
+ else
+ Val = 0;
+ }
+ else
+ Val = 0;
+
+ return (cmsFloat32Number) Val;
+}
+
+static
+cmsFloat32Number IEC61966_3(cmsFloat32Number x, const cmsFloat64Number Params[])
+{
+ cmsFloat64Number e, Val;
+
+ if (x >= -Params[2] / Params[1]) {
+
+ e = Params[1]*x + Params[2];
+
+ if (e > 0)
+ Val = pow(e, Params[0]) + Params[3];
+ else
+ Val = 0;
+ }
+ else
+ Val = Params[3];
+
+ return (cmsFloat32Number) Val;
+}
+
+static
+cmsFloat32Number IEC61966_21(cmsFloat32Number x, const cmsFloat64Number Params[])
+{
+ cmsFloat64Number e, Val;
+
+ if (x >= Params[4]) {
+
+ e = Params[1]*x + Params[2];
+
+ if (e > 0)
+ Val = pow(e, Params[0]);
+ else
+ Val = 0;
+ }
+ else
+ Val = x * Params[3];
+
+ return (cmsFloat32Number) Val;
+}
+
+static
+cmsFloat32Number param_5(cmsFloat32Number x, const cmsFloat64Number Params[])
+{
+ cmsFloat64Number e, Val;
+ // Y = (aX + b)^Gamma + e | X >= d
+ // Y = cX + f | else
+ if (x >= Params[4]) {
+
+ e = Params[1]*x + Params[2];
+ if (e > 0)
+ Val = pow(e, Params[0]) + Params[5];
+ else
+ Val = 0;
+ }
+ else
+ Val = x*Params[3] + Params[6];
+
+ return (cmsFloat32Number) Val;
+}
+
+static
+cmsFloat32Number param_6(cmsFloat32Number x, const cmsFloat64Number Params[])
+{
+ cmsFloat64Number e, Val;
+
+ e = Params[1]*x + Params[2];
+ if (e > 0)
+ Val = pow(e, Params[0]) + Params[3];
+ else
+ Val = 0;
+
+ return (cmsFloat32Number) Val;
+}
+
+static
+cmsFloat32Number param_7(cmsFloat32Number x, const cmsFloat64Number Params[])
+{
+ cmsFloat64Number Val;
+
+
+ Val = Params[1]*log10(Params[2] * pow(x, Params[0]) + Params[3]) + Params[4];
+
+ return (cmsFloat32Number) Val;
+}
+
+
+static
+cmsFloat32Number param_8(cmsFloat32Number x, const cmsFloat64Number Params[])
+{
+ cmsFloat64Number Val;
+
+ Val = (Params[0] * pow(Params[1], Params[2] * x + Params[3]) + Params[4]);
+
+ return (cmsFloat32Number) Val;
+}
+
+
+static
+cmsFloat32Number sigmoidal(cmsFloat32Number x, const cmsFloat64Number Params[])
+{
+ cmsFloat64Number Val;
+
+ Val = pow(1.0 - pow(1 - x, 1/Params[0]), 1/Params[0]);
+
+ return (cmsFloat32Number) Val;
+}
+
+
+static
+cmsBool CheckSingleParametric(const char* Name, dblfnptr fn, int Type, const cmsFloat64Number Params[])
+{
+ int i;
+ cmsToneCurve* tc;
+ cmsToneCurve* tc_1;
+ char InverseText[256];
+
+ tc = cmsBuildParametricToneCurve(DbgThread(), Type, Params);
+ tc_1 = cmsBuildParametricToneCurve(DbgThread(), -Type, Params);
+
+ for (i=0; i <= 1000; i++) {
+
+ cmsFloat32Number x = (cmsFloat32Number) i / 1000;
+ cmsFloat32Number y_fn, y_param, x_param, y_param2;
+
+ y_fn = fn(x, Params);
+ y_param = cmsEvalToneCurveFloat(tc, x);
+ x_param = cmsEvalToneCurveFloat(tc_1, y_param);
+
+ y_param2 = fn(x_param, Params);
+
+ if (!IsGoodVal(Name, y_fn, y_param, FIXED_PRECISION_15_16))
+ goto Error;
+
+ sprintf(InverseText, "Inverse %s", Name);
+ if (!IsGoodVal(InverseText, y_fn, y_param2, FIXED_PRECISION_15_16))
+ goto Error;
+ }
+
+ cmsFreeToneCurve(tc);
+ cmsFreeToneCurve(tc_1);
+ return TRUE;
+
+Error:
+ cmsFreeToneCurve(tc);
+ cmsFreeToneCurve(tc_1);
+ return FALSE;
+}
+
+// Check against some known values
+static
+int CheckParametricToneCurves(void)
+{
+ cmsFloat64Number Params[10];
+
+ // 1) X = Y ^ Gamma
+
+ Params[0] = 2.2;
+
+ if (!CheckSingleParametric("Gamma", Gamma, 1, Params)) return 0;
+
+ // 2) CIE 122-1966
+ // Y = (aX + b)^Gamma | X >= -b/a
+ // Y = 0 | else
+
+ Params[0] = 2.2;
+ Params[1] = 1.5;
+ Params[2] = -0.5;
+
+ if (!CheckSingleParametric("CIE122-1966", CIE122, 2, Params)) return 0;
+
+ // 3) IEC 61966-3
+ // Y = (aX + b)^Gamma | X <= -b/a
+ // Y = c | else
+
+ Params[0] = 2.2;
+ Params[1] = 1.5;
+ Params[2] = -0.5;
+ Params[3] = 0.3;
+
+
+ if (!CheckSingleParametric("IEC 61966-3", IEC61966_3, 3, Params)) return 0;
+
+ // 4) IEC 61966-2.1 (sRGB)
+ // Y = (aX + b)^Gamma | X >= d
+ // Y = cX | X < d
+
+ Params[0] = 2.4;
+ Params[1] = 1. / 1.055;
+ Params[2] = 0.055 / 1.055;
+ Params[3] = 1. / 12.92;
+ Params[4] = 0.04045;
+
+ if (!CheckSingleParametric("IEC 61966-2.1", IEC61966_21, 4, Params)) return 0;
+
+
+ // 5) Y = (aX + b)^Gamma + e | X >= d
+ // Y = cX + f | else
+
+ Params[0] = 2.2;
+ Params[1] = 0.7;
+ Params[2] = 0.2;
+ Params[3] = 0.3;
+ Params[4] = 0.1;
+ Params[5] = 0.5;
+ Params[6] = 0.2;
+
+ if (!CheckSingleParametric("param_5", param_5, 5, Params)) return 0;
+
+ // 6) Y = (aX + b) ^ Gamma + c
+
+ Params[0] = 2.2;
+ Params[1] = 0.7;
+ Params[2] = 0.2;
+ Params[3] = 0.3;
+
+ if (!CheckSingleParametric("param_6", param_6, 6, Params)) return 0;
+
+ // 7) Y = a * log (b * X^Gamma + c) + d
+
+ Params[0] = 2.2;
+ Params[1] = 0.9;
+ Params[2] = 0.9;
+ Params[3] = 0.02;
+ Params[4] = 0.1;
+
+ if (!CheckSingleParametric("param_7", param_7, 7, Params)) return 0;
+
+ // 8) Y = a * b ^ (c*X+d) + e
+
+ Params[0] = 0.9;
+ Params[1] = 0.9;
+ Params[2] = 1.02;
+ Params[3] = 0.1;
+ Params[4] = 0.2;
+
+ if (!CheckSingleParametric("param_8", param_8, 8, Params)) return 0;
+
+ // 108: S-Shaped: (1 - (1-x)^1/g)^1/g
+
+ Params[0] = 1.9;
+ if (!CheckSingleParametric("sigmoidal", sigmoidal, 108, Params)) return 0;
+
+ // All OK
+
+ return 1;
+}
+
+// LUT checks ------------------------------------------------------------------------------
+
+static
+int CheckLUTcreation(void)
+{
+ cmsPipeline* lut;
+ cmsPipeline* lut2;
+ int n1, n2;
+
+ lut = cmsPipelineAlloc(DbgThread(), 1, 1);
+ n1 = cmsPipelineStageCount(lut);
+ lut2 = cmsPipelineDup(lut);
+ n2 = cmsPipelineStageCount(lut2);
+
+ cmsPipelineFree(lut);
+ cmsPipelineFree(lut2);
+
+ return (n1 == 0) && (n2 == 0);
+}
+
+// Create a MPE for a identity matrix
+static
+void AddIdentityMatrix(cmsPipeline* lut)
+{
+ const cmsFloat64Number Identity[] = { 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1,
+ 0, 0, 0 };
+
+ cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocMatrix(DbgThread(), 3, 3, Identity, NULL));
+}
+
+// Create a MPE for identity cmsFloat32Number CLUT
+static
+void AddIdentityCLUTfloat(cmsPipeline* lut)
+{
+ const cmsFloat32Number Table[] = {
+
+ 0, 0, 0,
+ 0, 0, 1.0,
+
+ 0, 1.0, 0,
+ 0, 1.0, 1.0,
+
+ 1.0, 0, 0,
+ 1.0, 0, 1.0,
+
+ 1.0, 1.0, 0,
+ 1.0, 1.0, 1.0
+ };
+
+ cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocCLutFloat(DbgThread(), 2, 3, 3, Table));
+}
+
+// Create a MPE for identity cmsFloat32Number CLUT
+static
+void AddIdentityCLUT16(cmsPipeline* lut)
+{
+ const cmsUInt16Number Table[] = {
+
+ 0, 0, 0,
+ 0, 0, 0xffff,
+
+ 0, 0xffff, 0,
+ 0, 0xffff, 0xffff,
+
+ 0xffff, 0, 0,
+ 0xffff, 0, 0xffff,
+
+ 0xffff, 0xffff, 0,
+ 0xffff, 0xffff, 0xffff
+ };
+
+
+ cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocCLut16bit(DbgThread(), 2, 3, 3, Table));
+}
+
+
+// Create a 3 fn identity curves
+
+static
+void Add3GammaCurves(cmsPipeline* lut, cmsFloat64Number Curve)
+{
+ cmsToneCurve* id = cmsBuildGamma(DbgThread(), Curve);
+ cmsToneCurve* id3[3] = { id, id, id };
+
+ cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocToneCurves(DbgThread(), 3, id3));
+
+ cmsFreeToneCurve(id);
+}
+
+
+static
+int CheckFloatLUT(cmsPipeline* lut)
+{
+ int n1, i, j;
+ cmsFloat32Number Inf[3], Outf[3];
+
+ n1=0;
+
+ for (j=0; j < 65535; j++) {
+
+ int af[3];
+
+ Inf[0] = Inf[1] = Inf[2] = (cmsFloat32Number) j / 65535.0F;
+ cmsPipelineEvalFloat(Inf, Outf, lut);
+
+ af[0] = (int) floor(Outf[0]*65535.0 + 0.5);
+ af[1] = (int) floor(Outf[1]*65535.0 + 0.5);
+ af[2] = (int) floor(Outf[2]*65535.0 + 0.5);
+
+ for (i=0; i < 3; i++) {
+
+ if (af[i] != j) {
+ n1++;
+ }
+ }
+
+ }
+
+ return (n1 == 0);
+}
+
+
+static
+int Check16LUT(cmsPipeline* lut)
+{
+ int n2, i, j;
+ cmsUInt16Number Inw[3], Outw[3];
+
+ n2=0;
+
+ for (j=0; j < 65535; j++) {
+
+ int aw[3];
+
+ Inw[0] = Inw[1] = Inw[2] = j;
+ cmsPipelineEval16(Inw, Outw, lut);
+ aw[0] = Outw[0];
+ aw[1] = Outw[1];
+ aw[2] = Outw[2];
+
+ for (i=0; i < 3; i++) {
+
+ if (aw[i] != j) {
+ n2++;
+ }
+ }
+
+ }
+
+ return (n2 == 0);
+}
+
+
+// Check any LUT that is linear
+static
+int CheckStagesLUT(cmsPipeline* lut, int ExpectedStages)
+{
+
+ int nInpChans, nOutpChans, nStages;
+
+ nInpChans = cmsPipelineInputChannels(lut);
+ nOutpChans = cmsPipelineOutputChannels(lut);
+ nStages = cmsPipelineStageCount(lut);
+
+ return (nInpChans == 3) && (nOutpChans == 3) && (nStages == ExpectedStages);
+}
+
+
+static
+int CheckFullLUT(cmsPipeline* lut, int ExpectedStages)
+{
+ int rc = CheckStagesLUT(lut, ExpectedStages) && Check16LUT(lut) && CheckFloatLUT(lut);
+
+ cmsPipelineFree(lut);
+ return rc;
+}
+
+
+static
+int Check1StageLUT(void)
+{
+ cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
+
+ AddIdentityMatrix(lut);
+ return CheckFullLUT(lut, 1);
+}
+
+
+
+static
+int Check2StageLUT(void)
+{
+ cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
+
+ AddIdentityMatrix(lut);
+ AddIdentityCLUTfloat(lut);
+
+ return CheckFullLUT(lut, 2);
+}
+
+static
+int Check2Stage16LUT(void)
+{
+ cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
+
+ AddIdentityMatrix(lut);
+ AddIdentityCLUT16(lut);
+
+ return CheckFullLUT(lut, 2);
+}
+
+
+
+static
+int Check3StageLUT(void)
+{
+ cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
+
+ AddIdentityMatrix(lut);
+ AddIdentityCLUTfloat(lut);
+ Add3GammaCurves(lut, 1.0);
+
+ return CheckFullLUT(lut, 3);
+}
+
+static
+int Check3Stage16LUT(void)
+{
+ cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
+
+ AddIdentityMatrix(lut);
+ AddIdentityCLUT16(lut);
+ Add3GammaCurves(lut, 1.0);
+
+ return CheckFullLUT(lut, 3);
+}
+
+
+
+static
+int Check4StageLUT(void)
+{
+ cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
+
+ AddIdentityMatrix(lut);
+ AddIdentityCLUTfloat(lut);
+ Add3GammaCurves(lut, 1.0);
+ AddIdentityMatrix(lut);
+
+ return CheckFullLUT(lut, 4);
+}
+
+static
+int Check4Stage16LUT(void)
+{
+ cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
+
+ AddIdentityMatrix(lut);
+ AddIdentityCLUT16(lut);
+ Add3GammaCurves(lut, 1.0);
+ AddIdentityMatrix(lut);
+
+ return CheckFullLUT(lut, 4);
+}
+
+static
+int Check5StageLUT(void)
+{
+ cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
+
+ AddIdentityMatrix(lut);
+ AddIdentityCLUTfloat(lut);
+ Add3GammaCurves(lut, 1.0);
+ AddIdentityMatrix(lut);
+ Add3GammaCurves(lut, 1.0);
+
+ return CheckFullLUT(lut, 5);
+}
+
+
+static
+int Check5Stage16LUT(void)
+{
+ cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
+
+ AddIdentityMatrix(lut);
+ AddIdentityCLUT16(lut);
+ Add3GammaCurves(lut, 1.0);
+ AddIdentityMatrix(lut);
+ Add3GammaCurves(lut, 1.0);
+
+ return CheckFullLUT(lut, 5);
+}
+
+static
+int Check6StageLUT(void)
+{
+ cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
+
+ AddIdentityMatrix(lut);
+ Add3GammaCurves(lut, 1.0);
+ AddIdentityCLUTfloat(lut);
+ Add3GammaCurves(lut, 1.0);
+ AddIdentityMatrix(lut);
+ Add3GammaCurves(lut, 1.0);
+
+ return CheckFullLUT(lut, 6);
+}
+
+static
+int Check6Stage16LUT(void)
+{
+ cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
+
+ AddIdentityMatrix(lut);
+ Add3GammaCurves(lut, 1.0);
+ AddIdentityCLUT16(lut);
+ Add3GammaCurves(lut, 1.0);
+ AddIdentityMatrix(lut);
+ Add3GammaCurves(lut, 1.0);
+
+ return CheckFullLUT(lut, 6);
+}
+
+
+static
+int CheckLab2LabLUT(void)
+{
+ cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
+ int rc;
+
+ cmsPipelineInsertStage(lut, cmsAT_END, _cmsStageAllocLab2XYZ(DbgThread()));
+ cmsPipelineInsertStage(lut, cmsAT_END, _cmsStageAllocXYZ2Lab(DbgThread()));
+
+ rc = CheckFloatLUT(lut) && CheckStagesLUT(lut, 2);
+
+ cmsPipelineFree(lut);
+
+ return rc;
+}
+
+
+static
+int CheckXYZ2XYZLUT(void)
+{
+ cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
+ int rc;
+
+ cmsPipelineInsertStage(lut, cmsAT_END, _cmsStageAllocXYZ2Lab(DbgThread()));
+ cmsPipelineInsertStage(lut, cmsAT_END, _cmsStageAllocLab2XYZ(DbgThread()));
+
+ rc = CheckFloatLUT(lut) && CheckStagesLUT(lut, 2);
+
+ cmsPipelineFree(lut);
+
+ return rc;
+}
+
+
+
+static
+int CheckLab2LabMatLUT(void)
+{
+ cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
+ int rc;
+
+ cmsPipelineInsertStage(lut, cmsAT_END, _cmsStageAllocLab2XYZ(DbgThread()));
+ AddIdentityMatrix(lut);
+ cmsPipelineInsertStage(lut, cmsAT_END, _cmsStageAllocXYZ2Lab(DbgThread()));
+
+ rc = CheckFloatLUT(lut) && CheckStagesLUT(lut, 3);
+
+ cmsPipelineFree(lut);
+
+ return rc;
+}
+
+static
+int CheckNamedColorLUT(void)
+{
+ cmsPipeline* lut = cmsPipelineAlloc(DbgThread(), 3, 3);
+ cmsNAMEDCOLORLIST* nc;
+ int i,j, rc = 1, n2;
+ cmsUInt16Number PCS[3];
+ cmsUInt16Number Colorant[MAXCHANNELS];
+ char Name[255];
+ cmsUInt16Number Inw[3], Outw[3];
+
+
+ nc = cmsAllocNamedColorList(DbgThread(), 256, 3, "pre", "post");
+ if (nc == NULL) return 0;
+
+ for (i=0; i < 256; i++) {
+
+ PCS[0] = PCS[1] = PCS[2] = i;
+ Colorant[0] = Colorant[1] = Colorant[2] = Colorant[3] = i;
+
+ sprintf(Name, "#%d", i);
+ if (!cmsAppendNamedColor(nc, Name, PCS, Colorant)) { rc = 0; break; }
+ }
+
+ cmsPipelineInsertStage(lut, cmsAT_END, _cmsStageAllocNamedColor(nc));
+
+ cmsFreeNamedColorList(nc);
+ if (rc == 0) return 0;
+
+ n2=0;
+
+ for (j=0; j < 256; j++) {
+
+ Inw[0] = j;
+
+ cmsPipelineEval16(Inw, Outw, lut);
+ for (i=0; i < 3; i++) {
+
+ if (Outw[i] != j) {
+ n2++;
+ }
+ }
+
+ }
+
+ cmsPipelineFree(lut);
+ return (n2 == 0);
+}
+
+
+
+// --------------------------------------------------------------------------------------------
+
+// A lightweight test of multilocalized unicode structures.
+
+static
+int CheckMLU(void)
+{
+ cmsMLU* mlu, *mlu2, *mlu3;
+ char Buffer[256], Buffer2[256];
+ int rc = 1;
+ int i;
+ cmsHPROFILE h= NULL;
+
+ // Allocate a MLU structure, no preferred size
+ mlu = cmsMLUalloc(DbgThread(), 0);
+
+ // Add some localizations
+ cmsMLUsetWide(mlu, "en", "US", L"Hello, world");
+ cmsMLUsetWide(mlu, "es", "ES", L"Hola, mundo");
+ cmsMLUsetWide(mlu, "fr", "FR", L"Bonjour, le monde");
+ cmsMLUsetWide(mlu, "ca", "CA", L"Hola, mon");
+
+
+ // Check the returned string for each language
+
+ cmsMLUgetASCII(mlu, "en", "US", Buffer, 256);
+ if (strcmp(Buffer, "Hello, world") != 0) rc = 0;
+
+
+ cmsMLUgetASCII(mlu, "es", "ES", Buffer, 256);
+ if (strcmp(Buffer, "Hola, mundo") != 0) rc = 0;
+
+
+ cmsMLUgetASCII(mlu, "fr", "FR", Buffer, 256);
+ if (strcmp(Buffer, "Bonjour, le monde") != 0) rc = 0;
+
+
+ cmsMLUgetASCII(mlu, "ca", "CA", Buffer, 256);
+ if (strcmp(Buffer, "Hola, mon") != 0) rc = 0;
+
+ if (rc == 0)
+ Fail("Unexpected string '%s'", Buffer);
+
+ // So far, so good.
+ cmsMLUfree(mlu);
+
+ // Now for performance, allocate an empty struct
+ mlu = cmsMLUalloc(DbgThread(), 0);
+
+ // Fill it with several thousands of different lenguages
+ for (i=0; i < 4096; i++) {
+
+ char Lang[3];
+
+ Lang[0] = i % 255;
+ Lang[1] = i / 255;
+ Lang[2] = 0;
+
+ sprintf(Buffer, "String #%i", i);
+ cmsMLUsetASCII(mlu, Lang, Lang, Buffer);
+ }
+
+ // Duplicate it
+ mlu2 = cmsMLUdup(mlu);
+
+ // Get rid of original
+ cmsMLUfree(mlu);
+
+ // Check all is still in place
+ for (i=0; i < 4096; i++) {
+
+ char Lang[3];
+
+ Lang[0] = i % 255;
+ Lang[1] = i / 255;
+ Lang[2] = 0;
+
+ cmsMLUgetASCII(mlu2, Lang, Lang, Buffer2, 256);
+ sprintf(Buffer, "String #%i", i);
+
+ if (strcmp(Buffer, Buffer2) != 0) { rc = 0; break; }
+ }
+
+ if (rc == 0)
+ Fail("Unexpected string '%s'", Buffer2);
+
+ // Check profile IO
+
+ h = cmsOpenProfileFromFileTHR(DbgThread(), "mlucheck.icc", "w");
+
+ cmsSetProfileVersion(h, 4.2);
+
+ cmsWriteTag(h, cmsSigProfileDescriptionTag, mlu2);
+ cmsCloseProfile(h);
+ cmsMLUfree(mlu2);
+
+
+ h = cmsOpenProfileFromFileTHR(DbgThread(), "mlucheck.icc", "r");
+
+ mlu3 = cmsReadTag(h, cmsSigProfileDescriptionTag);
+ if (mlu3 == NULL) { Fail("Profile didn't get the MLU\n"); rc = 0; goto Error; }
+
+ // Check all is still in place
+ for (i=0; i < 4096; i++) {
+
+ char Lang[3];
+
+ Lang[0] = i % 255;
+ Lang[1] = i / 255;
+ Lang[2] = 0;
+
+ cmsMLUgetASCII(mlu3, Lang, Lang, Buffer2, 256);
+ sprintf(Buffer, "String #%i", i);
+
+ if (strcmp(Buffer, Buffer2) != 0) { rc = 0; break; }
+ }
+
+ if (rc == 0) Fail("Unexpected string '%s'", Buffer2);
+
+Error:
+
+ if (h != NULL) cmsCloseProfile(h);
+ remove("mlucheck.icc");
+
+ return rc;
+}
+
+
+// A lightweight test of named color structures.
+static
+int CheckNamedColorList(void)
+{
+ cmsNAMEDCOLORLIST* nc = NULL, *nc2;
+ int i, j, rc=1;
+ char Name[255];
+ cmsUInt16Number PCS[3];
+ cmsUInt16Number Colorant[MAXCHANNELS];
+ char CheckName[255];
+ cmsUInt16Number CheckPCS[3];
+ cmsUInt16Number CheckColorant[MAXCHANNELS];
+ cmsHPROFILE h;
+
+ nc = cmsAllocNamedColorList(DbgThread(), 0, 4, "prefix", "suffix");
+ if (nc == NULL) return 0;
+
+ for (i=0; i < 4096; i++) {
+
+
+ PCS[0] = PCS[1] = PCS[2] = i;
+ Colorant[0] = Colorant[1] = Colorant[2] = Colorant[3] = 4096 - i;
+
+ sprintf(Name, "#%d", i);
+ if (!cmsAppendNamedColor(nc, Name, PCS, Colorant)) { rc = 0; break; }
+ }
+
+ for (i=0; i < 4096; i++) {
+
+ CheckPCS[0] = CheckPCS[1] = CheckPCS[2] = i;
+ CheckColorant[0] = CheckColorant[1] = CheckColorant[2] = CheckColorant[3] = 4096 - i;
+
+ sprintf(CheckName, "#%d", i);
+ if (!cmsNamedColorInfo(nc, i, Name, NULL, NULL, PCS, Colorant)) { rc = 0; goto Error; }
+
+
+ for (j=0; j < 3; j++) {
+ if (CheckPCS[j] != PCS[j]) { rc = 0; Fail("Invalid PCS"); goto Error; }
+ }
+
+ for (j=0; j < 4; j++) {
+ if (CheckColorant[j] != Colorant[j]) { rc = 0; Fail("Invalid Colorant"); goto Error; };
+ }
+
+ if (strcmp(Name, CheckName) != 0) {rc = 0; Fail("Invalid Name"); goto Error; };
+ }
+
+ h = cmsOpenProfileFromFileTHR(DbgThread(), "namedcol.icc", "w");
+ if (h == NULL) return 0;
+ if (!cmsWriteTag(h, cmsSigNamedColor2Tag, nc)) return 0;
+ cmsCloseProfile(h);
+ cmsFreeNamedColorList(nc);
+ nc = NULL;
+
+ h = cmsOpenProfileFromFileTHR(DbgThread(), "namedcol.icc", "r");
+ nc2 = cmsReadTag(h, cmsSigNamedColor2Tag);
+
+ if (cmsNamedColorCount(nc2) != 4096) { rc = 0; Fail("Invalid count"); goto Error; }
+
+ i = cmsNamedColorIndex(nc2, "#123");
+ if (i != 123) { rc = 0; Fail("Invalid index"); goto Error; }
+
+
+ for (i=0; i < 4096; i++) {
+
+ CheckPCS[0] = CheckPCS[1] = CheckPCS[2] = i;
+ CheckColorant[0] = CheckColorant[1] = CheckColorant[2] = CheckColorant[3] = 4096 - i;
+
+ sprintf(CheckName, "#%d", i);
+ if (!cmsNamedColorInfo(nc2, i, Name, NULL, NULL, PCS, Colorant)) { rc = 0; goto Error; }
+
+
+ for (j=0; j < 3; j++) {
+ if (CheckPCS[j] != PCS[j]) { rc = 0; Fail("Invalid PCS"); goto Error; }
+ }
+
+ for (j=0; j < 4; j++) {
+ if (CheckColorant[j] != Colorant[j]) { rc = 0; Fail("Invalid Colorant"); goto Error; };
+ }
+
+ if (strcmp(Name, CheckName) != 0) {rc = 0; Fail("Invalid Name"); goto Error; };
+ }
+
+ cmsCloseProfile(h);
+ remove("namedcol.icc");
+
+Error:
+ if (nc != NULL) cmsFreeNamedColorList(nc);
+ return rc;
+}
+
+
+
+// ----------------------------------------------------------------------------------------------------------
+
+// Formatters
+
+static cmsBool FormatterFailed;
+
+static
+void CheckSingleFormatter16(cmsUInt32Number Type, const char* Text)
+{
+ cmsUInt16Number Values[MAXCHANNELS];
+ char Buffer[1024];
+ cmsFormatter f, b;
+ int i, j, nChannels, bytes;
+ _cmsTRANSFORM info;
+
+ // Already failed?
+ if (FormatterFailed) return;
+
+ memset(&info, 0, sizeof(info));
+ info.OutputFormat = info.InputFormat = Type;
+ info.StrideIn = info.StrideOut = 1;
+
+ // Go forth and back
+ f = _cmsGetFormatter(Type, cmsFormatterInput, 0);
+ b = _cmsGetFormatter(Type, cmsFormatterOutput, 0);
+
+ if (f.Fmt16 == NULL || b.Fmt16 == NULL) {
+ Fail("no formatter for %s", Text);
+ FormatterFailed = TRUE;
+
+ // Useful for debug
+ f = _cmsGetFormatter(Type, cmsFormatterInput, 0);
+ b = _cmsGetFormatter(Type, cmsFormatterOutput, 0);
+ return;
+ }
+
+ nChannels = T_CHANNELS(Type);
+ bytes = T_BYTES(Type);
+
+ for (j=0; j < 5; j++) {
+
+ for (i=0; i < nChannels; i++) {
+ Values[i] = (i+j);
+ // For 8-bit
+ if (bytes == 1)
+ Values[i] <<= 8;
+ }
+
+ b.Fmt16(&info, Values, Buffer);
+ memset(Values, 0, sizeof(Values));
+ f.Fmt16(&info, Values, Buffer);
+
+ for (i=0; i < nChannels; i++) {
+ if (bytes == 1)
+ Values[i] >>= 8;
+
+ if (Values[i] != i+j) {
+
+ Fail("%s failed", Text);
+ FormatterFailed = TRUE;
+
+ // Useful for debug
+ for (i=0; i < nChannels; i++) {
+ Values[i] = (i+j);
+ // For 8-bit
+ if (bytes == 1)
+ Values[i] <<= 8;
+ }
+
+ b.Fmt16(&info, Values, Buffer);
+ f.Fmt16(&info, Values, Buffer);
+ return;
+ }
+ }
+ }
+}
+
+#define C(a) CheckSingleFormatter16(a, #a)
+
+
+
+
+// Check all formatters
+static
+int CheckFormatters16(void)
+{
+ FormatterFailed = FALSE;
+
+ C( TYPE_GRAY_8 );
+ C( TYPE_GRAY_8_REV );
+ C( TYPE_GRAY_16 );
+ C( TYPE_GRAY_16_REV );
+ C( TYPE_GRAY_16_SE );
+ C( TYPE_GRAYA_8 );
+ C( TYPE_GRAYA_16 );
+ C( TYPE_GRAYA_16_SE );
+ C( TYPE_GRAYA_8_PLANAR );
+ C( TYPE_GRAYA_16_PLANAR );
+ C( TYPE_RGB_8 );
+ C( TYPE_RGB_8_PLANAR );
+ C( TYPE_BGR_8 );
+ C( TYPE_BGR_8_PLANAR );
+ C( TYPE_RGB_16 );
+ C( TYPE_RGB_16_PLANAR );
+ C( TYPE_RGB_16_SE );
+ C( TYPE_BGR_16 );
+ C( TYPE_BGR_16_PLANAR );
+ C( TYPE_BGR_16_SE );
+ C( TYPE_RGBA_8 );
+ C( TYPE_RGBA_8_PLANAR );
+ C( TYPE_RGBA_16 );
+ C( TYPE_RGBA_16_PLANAR );
+ C( TYPE_RGBA_16_SE );
+ C( TYPE_ARGB_8 );
+ C( TYPE_ARGB_16 );
+ C( TYPE_ABGR_8 );
+ C( TYPE_ABGR_16 );
+ C( TYPE_ABGR_16_PLANAR );
+ C( TYPE_ABGR_16_SE );
+ C( TYPE_BGRA_8 );
+ C( TYPE_BGRA_16 );
+ C( TYPE_BGRA_16_SE );
+ C( TYPE_CMY_8 );
+ C( TYPE_CMY_8_PLANAR );
+ C( TYPE_CMY_16 );
+ C( TYPE_CMY_16_PLANAR );
+ C( TYPE_CMY_16_SE );
+ C( TYPE_CMYK_8 );
+ C( TYPE_CMYKA_8 );
+ C( TYPE_CMYK_8_REV );
+ C( TYPE_YUVK_8 );
+ C( TYPE_CMYK_8_PLANAR );
+ C( TYPE_CMYK_16 );
+ C( TYPE_CMYK_16_REV );
+ C( TYPE_YUVK_16 );
+ C( TYPE_CMYK_16_PLANAR );
+ C( TYPE_CMYK_16_SE );
+ C( TYPE_KYMC_8 );
+ C( TYPE_KYMC_16 );
+ C( TYPE_KYMC_16_SE );
+ C( TYPE_KCMY_8 );
+ C( TYPE_KCMY_8_REV );
+ C( TYPE_KCMY_16 );
+ C( TYPE_KCMY_16_REV );
+ C( TYPE_KCMY_16_SE );
+ C( TYPE_CMYK5_8 );
+ C( TYPE_CMYK5_16 );
+ C( TYPE_CMYK5_16_SE );
+ C( TYPE_KYMC5_8 );
+ C( TYPE_KYMC5_16 );
+ C( TYPE_KYMC5_16_SE );
+ C( TYPE_CMYK6_8 );
+ C( TYPE_CMYK6_8_PLANAR );
+ C( TYPE_CMYK6_16 );
+ C( TYPE_CMYK6_16_PLANAR );
+ C( TYPE_CMYK6_16_SE );
+ C( TYPE_CMYK7_8 );
+ C( TYPE_CMYK7_16 );
+ C( TYPE_CMYK7_16_SE );
+ C( TYPE_KYMC7_8 );
+ C( TYPE_KYMC7_16 );
+ C( TYPE_KYMC7_16_SE );
+ C( TYPE_CMYK8_8 );
+ C( TYPE_CMYK8_16 );
+ C( TYPE_CMYK8_16_SE );
+ C( TYPE_KYMC8_8 );
+ C( TYPE_KYMC8_16 );
+ C( TYPE_KYMC8_16_SE );
+ C( TYPE_CMYK9_8 );
+ C( TYPE_CMYK9_16 );
+ C( TYPE_CMYK9_16_SE );
+ C( TYPE_KYMC9_8 );
+ C( TYPE_KYMC9_16 );
+ C( TYPE_KYMC9_16_SE );
+ C( TYPE_CMYK10_8 );
+ C( TYPE_CMYK10_16 );
+ C( TYPE_CMYK10_16_SE );
+ C( TYPE_KYMC10_8 );
+ C( TYPE_KYMC10_16 );
+ C( TYPE_KYMC10_16_SE );
+ C( TYPE_CMYK11_8 );
+ C( TYPE_CMYK11_16 );
+ C( TYPE_CMYK11_16_SE );
+ C( TYPE_KYMC11_8 );
+ C( TYPE_KYMC11_16 );
+ C( TYPE_KYMC11_16_SE );
+ C( TYPE_CMYK12_8 );
+ C( TYPE_CMYK12_16 );
+ C( TYPE_CMYK12_16_SE );
+ C( TYPE_KYMC12_8 );
+ C( TYPE_KYMC12_16 );
+ C( TYPE_KYMC12_16_SE );
+ C( TYPE_XYZ_16 );
+ C( TYPE_Lab_8 );
+ C( TYPE_ALab_8 );
+ C( TYPE_Lab_16 );
+ C( TYPE_Yxy_16 );
+ C( TYPE_YCbCr_8 );
+ C( TYPE_YCbCr_8_PLANAR );
+ C( TYPE_YCbCr_16 );
+ C( TYPE_YCbCr_16_PLANAR );
+ C( TYPE_YCbCr_16_SE );
+ C( TYPE_YUV_8 );
+ C( TYPE_YUV_8_PLANAR );
+ C( TYPE_YUV_16 );
+ C( TYPE_YUV_16_PLANAR );
+ C( TYPE_YUV_16_SE );
+ C( TYPE_HLS_8 );
+ C( TYPE_HLS_8_PLANAR );
+ C( TYPE_HLS_16 );
+ C( TYPE_HLS_16_PLANAR );
+ C( TYPE_HLS_16_SE );
+ C( TYPE_HSV_8 );
+ C( TYPE_HSV_8_PLANAR );
+ C( TYPE_HSV_16 );
+ C( TYPE_HSV_16_PLANAR );
+ C( TYPE_HSV_16_SE );
+
+ C( TYPE_XYZ_FLT );
+ C( TYPE_Lab_FLT );
+ C( TYPE_GRAY_FLT );
+ C( TYPE_RGB_FLT );
+ C( TYPE_CMYK_FLT );
+
+ C( TYPE_XYZ_DBL );
+ C( TYPE_Lab_DBL );
+ C( TYPE_GRAY_DBL );
+ C( TYPE_RGB_DBL );
+ C( TYPE_CMYK_DBL );
+
+ C( TYPE_LabV2_8 );
+ C( TYPE_ALabV2_8 );
+ C( TYPE_LabV2_16 );
+
+ return FormatterFailed == 0 ? 1 : 0;
+}
+#undef C
+
+static
+void CheckSingleFormatterFloat(cmsUInt32Number Type, const char* Text)
+{
+ cmsFloat32Number Values[MAXCHANNELS];
+ char Buffer[1024];
+ cmsFormatter f, b;
+ int i, j, nChannels;
+ _cmsTRANSFORM info;
+
+ // Already failed?
+ if (FormatterFailed) return;
+
+ memset(&info, 0, sizeof(info));
+ info.OutputFormat = info.InputFormat = Type;
+ info.StrideIn = info.StrideOut = 1;
+
+ // Go forth and back
+ f = _cmsGetFormatter(Type, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT);
+ b = _cmsGetFormatter(Type, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT);
+
+ if (f.FmtFloat == NULL || b.FmtFloat == NULL) {
+ Fail("no formatter for %s", Text);
+ FormatterFailed = TRUE;
+
+ // Useful for debug
+ f = _cmsGetFormatter(Type, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT);
+ b = _cmsGetFormatter(Type, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT);
+ return;
+ }
+
+ nChannels = T_CHANNELS(Type);
+
+ for (j=0; j < 5; j++) {
+
+ for (i=0; i < nChannels; i++) {
+ Values[i] = (cmsFloat32Number) (i+j);
+ }
+
+ b.FmtFloat(&info, Values, Buffer);
+ memset(Values, 0, sizeof(Values));
+ f.FmtFloat(&info, Values, Buffer);
+
+ for (i=0; i < nChannels; i++) {
+
+ cmsFloat64Number delta = fabs(Values[i] - ( i+j));
+
+ if (delta > 0.000000001) {
+
+ Fail("%s failed", Text);
+ FormatterFailed = TRUE;
+
+ // Useful for debug
+ for (i=0; i < nChannels; i++) {
+ Values[i] = (cmsFloat32Number) (i+j);
+ }
+
+ b.FmtFloat(&info, Values, Buffer);
+ f.FmtFloat(&info, Values, Buffer);
+ return;
+ }
+ }
+ }
+}
+
+#define C(a) CheckSingleFormatterFloat(a, #a)
+
+static
+int CheckFormattersFloat(void)
+{
+ FormatterFailed = FALSE;
+
+ C( TYPE_XYZ_FLT );
+ C( TYPE_Lab_FLT );
+ C( TYPE_GRAY_FLT );
+ C( TYPE_RGB_FLT );
+ C( TYPE_CMYK_FLT );
+
+ C( TYPE_XYZ_DBL );
+ C( TYPE_Lab_DBL );
+ C( TYPE_GRAY_DBL );
+ C( TYPE_RGB_DBL );
+ C( TYPE_CMYK_DBL );
+
+ return FormatterFailed == 0 ? 1 : 0;
+}
+#undef C
+
+
+// Write tag testbed ----------------------------------------------------------------------------------------
+
+static
+int CheckXYZ(int Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
+{
+ cmsCIEXYZ XYZ, *Pt;
+
+
+ switch (Pass) {
+
+ case 1:
+
+ XYZ.X = 1.0; XYZ.Y = 1.1; XYZ.Z = 1.2;
+ return cmsWriteTag(hProfile, tag, &XYZ);
+
+ case 2:
+ Pt = cmsReadTag(hProfile, tag);
+ if (Pt == NULL) return 0;
+ return IsGoodFixed15_16("X", 1.0, Pt ->X) &&
+ IsGoodFixed15_16("Y", 1.1, Pt->Y) &&
+ IsGoodFixed15_16("Z", 1.2, Pt -> Z);
+
+ default:
+ return 0;
+ }
+}
+
+
+static
+int CheckGamma(int Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
+{
+ cmsToneCurve *g, *Pt;
+ int rc;
+
+ switch (Pass) {
+
+ case 1:
+
+ g = cmsBuildGamma(DbgThread(), 1.0);
+ rc = cmsWriteTag(hProfile, tag, g);
+ cmsFreeToneCurve(g);
+ return rc;
+
+ case 2:
+ Pt = cmsReadTag(hProfile, tag);
+ if (Pt == NULL) return 0;
+ return cmsIsToneCurveLinear(Pt);
+
+ default:
+ return 0;
+ }
+}
+
+static
+int CheckText(int Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
+{
+ cmsMLU *m, *Pt;
+ int rc;
+ char Buffer[256];
+
+
+ switch (Pass) {
+
+ case 1:
+ m = cmsMLUalloc(DbgThread(), 0);
+ cmsMLUsetASCII(m, cmsNoLanguage, cmsNoCountry, "Test test");
+ rc = cmsWriteTag(hProfile, tag, m);
+ cmsMLUfree(m);
+ return rc;
+
+ case 2:
+ Pt = cmsReadTag(hProfile, tag);
+ if (Pt == NULL) return 0;
+ cmsMLUgetASCII(Pt, cmsNoLanguage, cmsNoCountry, Buffer, 256);
+ return strcmp(Buffer, "Test test") == 0;
+
+ default:
+ return 0;
+ }
+}
+
+static
+int CheckData(int Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
+{
+ cmsICCData *Pt;
+ cmsICCData d = { 1, 0, { '?' }};
+ int rc;
+
+
+ switch (Pass) {
+
+ case 1:
+ rc = cmsWriteTag(hProfile, tag, &d);
+ return rc;
+
+ case 2:
+ Pt = cmsReadTag(hProfile, tag);
+ if (Pt == NULL) return 0;
+ return (Pt ->data[0] == '?') && (Pt ->flag == 0) && (Pt ->len == 1);
+
+ default:
+ return 0;
+ }
+}
+
+
+static
+int CheckSignature(int Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
+{
+ cmsTagSignature *Pt, Holder;
+
+ switch (Pass) {
+
+ case 1:
+ Holder = cmsSigPerceptualReferenceMediumGamut;
+ return cmsWriteTag(hProfile, tag, &Holder);
+
+ case 2:
+ Pt = cmsReadTag(hProfile, tag);
+ if (Pt == NULL) return 0;
+ return *Pt == cmsSigPerceptualReferenceMediumGamut;
+
+ default:
+ return 0;
+ }
+}
+
+
+static
+int CheckDateTime(int Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
+{
+ struct tm *Pt, Holder;
+
+ switch (Pass) {
+
+ case 1:
+
+ Holder.tm_hour = 1;
+ Holder.tm_min = 2;
+ Holder.tm_sec = 3;
+ Holder.tm_mday = 4;
+ Holder.tm_mon = 5;
+ Holder.tm_year = 2009 - 1900;
+ return cmsWriteTag(hProfile, tag, &Holder);
+
+ case 2:
+ Pt = cmsReadTag(hProfile, tag);
+ if (Pt == NULL) return 0;
+
+ return (Pt ->tm_hour == 1 &&
+ Pt ->tm_min == 2 &&
+ Pt ->tm_sec == 3 &&
+ Pt ->tm_mday == 4 &&
+ Pt ->tm_mon == 5 &&
+ Pt ->tm_year == 2009 - 1900);
+
+ default:
+ return 0;
+ }
+
+}
+
+
+static
+int CheckNamedColor(int Pass, cmsHPROFILE hProfile, cmsTagSignature tag, int max_check, cmsBool colorant_check)
+{
+ cmsNAMEDCOLORLIST* nc;
+ int i, j, rc;
+ char Name[255];
+ cmsUInt16Number PCS[3];
+ cmsUInt16Number Colorant[MAXCHANNELS];
+ char CheckName[255];
+ cmsUInt16Number CheckPCS[3];
+ cmsUInt16Number CheckColorant[MAXCHANNELS];
+
+ switch (Pass) {
+
+ case 1:
+
+ nc = cmsAllocNamedColorList(DbgThread(), 0, 4, "prefix", "suffix");
+ if (nc == NULL) return 0;
+
+ for (i=0; i < max_check; i++) {
+
+ PCS[0] = PCS[1] = PCS[2] = i;
+ Colorant[0] = Colorant[1] = Colorant[2] = Colorant[3] = max_check - i;
+
+ sprintf(Name, "#%d", i);
+ if (!cmsAppendNamedColor(nc, Name, PCS, Colorant)) { Fail("Couldn't append named color"); return 0; }
+ }
+
+ rc = cmsWriteTag(hProfile, tag, nc);
+ cmsFreeNamedColorList(nc);
+ return rc;
+
+ case 2:
+
+ nc = cmsReadTag(hProfile, tag);
+ if (nc == NULL) return 0;
+
+ for (i=0; i < max_check; i++) {
+
+ CheckPCS[0] = CheckPCS[1] = CheckPCS[2] = i;
+ CheckColorant[0] = CheckColorant[1] = CheckColorant[2] = CheckColorant[3] = max_check - i;
+
+ sprintf(CheckName, "#%d", i);
+ if (!cmsNamedColorInfo(nc, i, Name, NULL, NULL, PCS, Colorant)) { Fail("Invalid string"); return 0; }
+
+
+ for (j=0; j < 3; j++) {
+ if (CheckPCS[j] != PCS[j]) { Fail("Invalid PCS"); return 0; }
+ }
+
+ // This is only used on named color list
+ if (colorant_check) {
+
+ for (j=0; j < 4; j++) {
+ if (CheckColorant[j] != Colorant[j]) { Fail("Invalid Colorant"); return 0; };
+ }
+ }
+
+ if (strcmp(Name, CheckName) != 0) { Fail("Invalid Name"); return 0; };
+ }
+ return 1;
+
+
+ default: return 0;
+ }
+}
+
+
+static
+int CheckLUT(int Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
+{
+ cmsPipeline* Lut, *Pt;
+ int rc;
+
+
+ switch (Pass) {
+
+ case 1:
+
+ Lut = cmsPipelineAlloc(DbgThread(), 3, 3);
+ if (Lut == NULL) return 0;
+
+ // Create an identity LUT
+ cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(DbgThread(), 3));
+ cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocIdentityCLut(DbgThread(), 3));
+ cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocIdentityCurves(DbgThread(), 3));
+
+ rc = cmsWriteTag(hProfile, tag, Lut);
+ cmsPipelineFree(Lut);
+ return rc;
+
+ case 2:
+ Pt = cmsReadTag(hProfile, tag);
+ if (Pt == NULL) return 0;
+
+ // Transform values, check for identity
+ return Check16LUT(Pt);
+
+ default:
+ return 0;
+ }
+}
+
+static
+int CheckCHAD(int Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
+{
+ cmsFloat64Number *Pt;
+ cmsFloat64Number CHAD[] = { 0, .1, .2, .3, .4, .5, .6, .7, .8 };
+ int i;
+
+ switch (Pass) {
+
+ case 1:
+ return cmsWriteTag(hProfile, tag, CHAD);
+
+
+ case 2:
+ Pt = cmsReadTag(hProfile, tag);
+ if (Pt == NULL) return 0;
+
+ for (i=0; i < 9; i++) {
+ if (!IsGoodFixed15_16("CHAD", Pt[i], CHAD[i])) return 0;
+ }
+
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+static
+int CheckChromaticity(int Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
+{
+ cmsCIExyYTRIPLE *Pt, c = { {0, .1, 1 }, { .3, .4, 1 }, { .6, .7, 1 }};
+
+ switch (Pass) {
+
+ case 1:
+ return cmsWriteTag(hProfile, tag, &c);
+
+
+ case 2:
+ Pt = cmsReadTag(hProfile, tag);
+ if (Pt == NULL) return 0;
+
+ if (!IsGoodFixed15_16("xyY", Pt ->Red.x, c.Red.x)) return 0;
+ if (!IsGoodFixed15_16("xyY", Pt ->Red.y, c.Red.y)) return 0;
+ if (!IsGoodFixed15_16("xyY", Pt ->Green.x, c.Green.x)) return 0;
+ if (!IsGoodFixed15_16("xyY", Pt ->Green.y, c.Green.y)) return 0;
+ if (!IsGoodFixed15_16("xyY", Pt ->Blue.x, c.Blue.x)) return 0;
+ if (!IsGoodFixed15_16("xyY", Pt ->Blue.y, c.Blue.y)) return 0;
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+
+static
+int CheckColorantOrder(int Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
+{
+ cmsUInt8Number *Pt, c[MAXCHANNELS];
+ int i;
+
+ switch (Pass) {
+
+ case 1:
+ for (i=0; i < MAXCHANNELS; i++) c[i] = MAXCHANNELS - i - 1;
+ return cmsWriteTag(hProfile, tag, c);
+
+
+ case 2:
+ Pt = cmsReadTag(hProfile, tag);
+ if (Pt == NULL) return 0;
+
+ for (i=0; i < MAXCHANNELS; i++) {
+ if (Pt[i] != ( MAXCHANNELS - i - 1 )) return 0;
+ }
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+static
+int CheckMeasurement(int Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
+{
+ cmsICCMeasurementConditions *Pt, m;
+
+ switch (Pass) {
+
+ case 1:
+ m.Backing.X = 0.1;
+ m.Backing.Y = 0.2;
+ m.Backing.Z = 0.3;
+ m.Flare = 1.0;
+ m.Geometry = 1;
+ m.IlluminantType = cmsILLUMINANT_TYPE_D50;
+ m.Observer = 1;
+ return cmsWriteTag(hProfile, tag, &m);
+
+
+ case 2:
+ Pt = cmsReadTag(hProfile, tag);
+ if (Pt == NULL) return 0;
+
+ if (!IsGoodFixed15_16("Backing", Pt ->Backing.X, 0.1)) return 0;
+ if (!IsGoodFixed15_16("Backing", Pt ->Backing.Y, 0.2)) return 0;
+ if (!IsGoodFixed15_16("Backing", Pt ->Backing.Z, 0.3)) return 0;
+ if (!IsGoodFixed15_16("Flare", Pt ->Flare, 1.0)) return 0;
+
+ if (Pt ->Geometry != 1) return 0;
+ if (Pt ->IlluminantType != cmsILLUMINANT_TYPE_D50) return 0;
+ if (Pt ->Observer != 1) return 0;
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+
+static
+int CheckUcrBg(int Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
+{
+ cmsUcrBg *Pt, m;
+ int rc;
+ char Buffer[256];
+
+ switch (Pass) {
+
+ case 1:
+ m.Ucr = cmsBuildGamma(DbgThread(), 2.4);
+ m.Bg = cmsBuildGamma(DbgThread(), -2.2);
+ m.Desc = cmsMLUalloc(DbgThread(), 1);
+ cmsMLUsetASCII(m.Desc, cmsNoLanguage, cmsNoCountry, "test UCR/BG");
+ rc = cmsWriteTag(hProfile, tag, &m);
+ cmsMLUfree(m.Desc);
+ cmsFreeToneCurve(m.Bg);
+ cmsFreeToneCurve(m.Ucr);
+ return rc;
+
+
+ case 2:
+ Pt = cmsReadTag(hProfile, tag);
+ if (Pt == NULL) return 0;
+
+ cmsMLUgetASCII(Pt ->Desc, cmsNoLanguage, cmsNoCountry, Buffer, 256);
+ if (strcmp(Buffer, "test UCR/BG") != 0) return 0;
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+
+static
+int CheckCRDinfo(int Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
+{
+ cmsMLU *mlu;
+ char Buffer[256];
+ int rc;
+
+ switch (Pass) {
+
+ case 1:
+ mlu = cmsMLUalloc(DbgThread(), 5);
+
+ cmsMLUsetWide(mlu, "PS", "nm", L"test postscript");
+ cmsMLUsetWide(mlu, "PS", "#0", L"perceptual");
+ cmsMLUsetWide(mlu, "PS", "#1", L"relative_colorimetric");
+ cmsMLUsetWide(mlu, "PS", "#2", L"saturation");
+ cmsMLUsetWide(mlu, "PS", "#3", L"absolute_colorimetric");
+ rc = cmsWriteTag(hProfile, tag, mlu);
+ cmsMLUfree(mlu);
+ return rc;
+
+
+ case 2:
+ mlu = (cmsMLU*) cmsReadTag(hProfile, tag);
+ if (mlu == NULL) return 0;
+
+
+
+ cmsMLUgetASCII(mlu, "PS", "nm", Buffer, 256);
+ if (strcmp(Buffer, "test postscript") != 0) return 0;
+
+
+ cmsMLUgetASCII(mlu, "PS", "#0", Buffer, 256);
+ if (strcmp(Buffer, "perceptual") != 0) return 0;
+
+
+ cmsMLUgetASCII(mlu, "PS", "#1", Buffer, 256);
+ if (strcmp(Buffer, "relative_colorimetric") != 0) return 0;
+
+
+ cmsMLUgetASCII(mlu, "PS", "#2", Buffer, 256);
+ if (strcmp(Buffer, "saturation") != 0) return 0;
+
+
+ cmsMLUgetASCII(mlu, "PS", "#3", Buffer, 256);
+ if (strcmp(Buffer, "absolute_colorimetric") != 0) return 0;
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+
+static
+cmsToneCurve *CreateSegmentedCurve(void)
+{
+ cmsCurveSegment Seg[3];
+ cmsFloat32Number Sampled[2] = { 0, 1};
+
+ Seg[0].Type = 6;
+ Seg[0].Params[0] = 1;
+ Seg[0].Params[1] = 0;
+ Seg[0].Params[2] = 0;
+ Seg[0].Params[3] = 0;
+ Seg[0].x0 = -1E22F;
+ Seg[0].x1 = 0;
+
+ Seg[1].Type = 0;
+ Seg[1].nGridPoints = 2;
+ Seg[1].SampledPoints = Sampled;
+ Seg[1].x0 = 0;
+ Seg[1].x1 = 1;
+
+ Seg[2].Type = 6;
+ Seg[2].Params[0] = 1;
+ Seg[2].Params[1] = 0;
+ Seg[2].Params[2] = 0;
+ Seg[2].Params[3] = 0;
+ Seg[2].x0 = 1;
+ Seg[2].x1 = 1E22F;
+
+ return cmsBuildSegmentedToneCurve(DbgThread(), 3, Seg);
+}
+
+
+static
+int CheckMPE(int Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
+{
+ cmsPipeline* Lut, *Pt;
+ cmsToneCurve* G[3];
+ int rc;
+
+ switch (Pass) {
+
+ case 1:
+
+ Lut = cmsPipelineAlloc(DbgThread(), 3, 3);
+
+ cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV2ToV4(DbgThread()));
+ cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV4ToV2(DbgThread()));
+ AddIdentityCLUTfloat(Lut);
+
+ G[0] = G[1] = G[2] = CreateSegmentedCurve();
+ cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(DbgThread(), 3, G));
+ cmsFreeToneCurve(G[0]);
+
+ rc = cmsWriteTag(hProfile, tag, Lut);
+ cmsPipelineFree(Lut);
+ return rc;
+
+ case 2:
+ Pt = cmsReadTag(hProfile, tag);
+ if (Pt == NULL) return 0;
+ return CheckFloatLUT(Pt);
+
+ default:
+ return 0;
+ }
+}
+
+
+static
+int CheckScreening(int Pass, cmsHPROFILE hProfile, cmsTagSignature tag)
+{
+ cmsScreening *Pt, sc;
+ int rc;
+
+ switch (Pass) {
+
+ case 1:
+
+ sc.Flag = 0;
+ sc.nChannels = 1;
+ sc.Channels[0].Frequency = 2.0;
+ sc.Channels[0].ScreenAngle = 3.0;
+ sc.Channels[0].SpotShape = cmsSPOT_ELLIPSE;
+
+ rc = cmsWriteTag(hProfile, tag, &sc);
+ return rc;
+
+
+ case 2:
+ Pt = cmsReadTag(hProfile, tag);
+ if (Pt == NULL) return 0;
+
+ if (Pt ->nChannels != 1) return 0;
+ if (Pt ->Flag != 0) return 0;
+ if (!IsGoodFixed15_16("Freq", Pt ->Channels[0].Frequency, 2.0)) return 0;
+ if (!IsGoodFixed15_16("Angle", Pt ->Channels[0].ScreenAngle, 3.0)) return 0;
+ if (Pt ->Channels[0].SpotShape != cmsSPOT_ELLIPSE) return 0;
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+
+static
+cmsBool CheckOneStr(cmsMLU* mlu, int n)
+{
+ char Buffer[256], Buffer2[256];
+
+
+ cmsMLUgetASCII(mlu, "en", "US", Buffer, 255);
+ sprintf(Buffer2, "Hello, world %d", n);
+ if (strcmp(Buffer, Buffer2) != 0) return FALSE;
+
+
+ cmsMLUgetASCII(mlu, "es", "ES", Buffer, 255);
+ sprintf(Buffer2, "Hola, mundo %d", n);
+ if (strcmp(Buffer, Buffer2) != 0) return FALSE;
+
+ return TRUE;
+}
+
+
+static
+void SetOneStr(cmsMLU** mlu, wchar_t* s1, wchar_t* s2)
+{
+ *mlu = cmsMLUalloc(DbgThread(), 0);
+ cmsMLUsetWide(*mlu, "en", "US", s1);
+ cmsMLUsetWide(*mlu, "es", "ES", s2);
+}
+
+
+static
+int CheckProfileSequenceTag(int Pass, cmsHPROFILE hProfile)
+{
+ cmsSEQ* s;
+ int i;
+
+ switch (Pass) {
+
+ case 1:
+
+ s = cmsAllocProfileSequenceDescription(DbgThread(), 3);
+ if (s == NULL) return 0;
+
+ SetOneStr(&s -> seq[0].Manufacturer, L"Hello, world 0", L"Hola, mundo 0");
+ SetOneStr(&s -> seq[0].Model, L"Hello, world 0", L"Hola, mundo 0");
+ SetOneStr(&s -> seq[1].Manufacturer, L"Hello, world 1", L"Hola, mundo 1");
+ SetOneStr(&s -> seq[1].Model, L"Hello, world 1", L"Hola, mundo 1");
+ SetOneStr(&s -> seq[2].Manufacturer, L"Hello, world 2", L"Hola, mundo 2");
+ SetOneStr(&s -> seq[2].Model, L"Hello, world 2", L"Hola, mundo 2");
+
+
+#ifdef CMS_DONT_USE_INT64
+ s ->seq[0].attributes[0] = cmsTransparency|cmsMatte;
+ s ->seq[0].attributes[1] = 0;
+#else
+ s ->seq[0].attributes = cmsTransparency|cmsMatte;
+#endif
+
+#ifdef CMS_DONT_USE_INT64
+ s ->seq[1].attributes[0] = cmsReflective|cmsMatte;
+ s ->seq[1].attributes[1] = 0;
+#else
+ s ->seq[1].attributes = cmsReflective|cmsMatte;
+#endif
+
+#ifdef CMS_DONT_USE_INT64
+ s ->seq[2].attributes[0] = cmsTransparency|cmsGlossy;
+ s ->seq[2].attributes[1] = 0;
+#else
+ s ->seq[2].attributes = cmsTransparency|cmsGlossy;
+#endif
+
+ if (!cmsWriteTag(hProfile, cmsSigProfileSequenceDescTag, s)) return 0;
+ cmsFreeProfileSequenceDescription(s);
+ return 1;
+
+ case 2:
+
+ s = cmsReadTag(hProfile, cmsSigProfileSequenceDescTag);
+ if (s == NULL) return 0;
+
+ if (s ->n != 3) return 0;
+
+#ifdef CMS_DONT_USE_INT64
+ if (s ->seq[0].attributes[0] != (cmsTransparency|cmsMatte)) return 0;
+ if (s ->seq[0].attributes[1] != 0) return 0;
+#else
+ if (s ->seq[0].attributes != (cmsTransparency|cmsMatte)) return 0;
+#endif
+
+#ifdef CMS_DONT_USE_INT64
+ if (s ->seq[1].attributes[0] != (cmsReflective|cmsMatte)) return 0;
+ if (s ->seq[1].attributes[1] != 0) return 0;
+#else
+ if (s ->seq[1].attributes != (cmsReflective|cmsMatte)) return 0;
+#endif
+
+#ifdef CMS_DONT_USE_INT64
+ if (s ->seq[2].attributes[0] != (cmsTransparency|cmsGlossy)) return 0;
+ if (s ->seq[2].attributes[1] != 0) return 0;
+#else
+ if (s ->seq[2].attributes != (cmsTransparency|cmsGlossy)) return 0;
+#endif
+
+ // Check MLU
+ for (i=0; i < 3; i++) {
+
+ if (!CheckOneStr(s -> seq[i].Manufacturer, i)) return 0;
+ if (!CheckOneStr(s -> seq[i].Model, i)) return 0;
+ }
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+
+static
+int CheckProfileSequenceIDTag(int Pass, cmsHPROFILE hProfile)
+{
+ cmsSEQ* s;
+ int i;
+
+ switch (Pass) {
+
+ case 1:
+
+ s = cmsAllocProfileSequenceDescription(DbgThread(), 3);
+ if (s == NULL) return 0;
+
+ memcpy(s ->seq[0].ProfileID.ID8, "0123456789ABCDEF", 16);
+ memcpy(s ->seq[1].ProfileID.ID8, "1111111111111111", 16);
+ memcpy(s ->seq[2].ProfileID.ID8, "2222222222222222", 16);
+
+
+ SetOneStr(&s -> seq[0].Description, L"Hello, world 0", L"Hola, mundo 0");
+ SetOneStr(&s -> seq[1].Description, L"Hello, world 1", L"Hola, mundo 1");
+ SetOneStr(&s -> seq[2].Description, L"Hello, world 2", L"Hola, mundo 2");
+
+ if (!cmsWriteTag(hProfile, cmsSigProfileSequenceIdTag, s)) return 0;
+ cmsFreeProfileSequenceDescription(s);
+ return 1;
+
+ case 2:
+
+ s = cmsReadTag(hProfile, cmsSigProfileSequenceIdTag);
+ if (s == NULL) return 0;
+
+ if (s ->n != 3) return 0;
+
+ if (memcmp(s ->seq[0].ProfileID.ID8, "0123456789ABCDEF", 16) != 0) return 0;
+ if (memcmp(s ->seq[1].ProfileID.ID8, "1111111111111111", 16) != 0) return 0;
+ if (memcmp(s ->seq[2].ProfileID.ID8, "2222222222222222", 16) != 0) return 0;
+
+ for (i=0; i < 3; i++) {
+
+ if (!CheckOneStr(s -> seq[i].Description, i)) return 0;
+ }
+
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+
+static
+int CheckICCViewingConditions(int Pass, cmsHPROFILE hProfile)
+{
+ cmsICCViewingConditions* v;
+ cmsICCViewingConditions s;
+
+ switch (Pass) {
+
+ case 1:
+ s.IlluminantType = 1;
+ s.IlluminantXYZ.X = 0.1;
+ s.IlluminantXYZ.Y = 0.2;
+ s.IlluminantXYZ.Z = 0.3;
+ s.SurroundXYZ.X = 0.4;
+ s.SurroundXYZ.Y = 0.5;
+ s.SurroundXYZ.Z = 0.6;
+
+ if (!cmsWriteTag(hProfile, cmsSigViewingConditionsTag, &s)) return 0;
+ return 1;
+
+ case 2:
+ v = cmsReadTag(hProfile, cmsSigViewingConditionsTag);
+ if (v == NULL) return 0;
+
+ if (v ->IlluminantType != 1) return 0;
+ if (!IsGoodVal("IlluminantXYZ.X", v ->IlluminantXYZ.X, 0.1, 0.001)) return 0;
+ if (!IsGoodVal("IlluminantXYZ.Y", v ->IlluminantXYZ.Y, 0.2, 0.001)) return 0;
+ if (!IsGoodVal("IlluminantXYZ.Z", v ->IlluminantXYZ.Z, 0.3, 0.001)) return 0;
+
+ if (!IsGoodVal("SurroundXYZ.X", v ->SurroundXYZ.X, 0.4, 0.001)) return 0;
+ if (!IsGoodVal("SurroundXYZ.Y", v ->SurroundXYZ.Y, 0.5, 0.001)) return 0;
+ if (!IsGoodVal("SurroundXYZ.Z", v ->SurroundXYZ.Z, 0.6, 0.001)) return 0;
+
+ return 1;
+
+ default:
+ return 0;
+ }
+
+}
+
+
+static
+int CheckVCGT(int Pass, cmsHPROFILE hProfile)
+{
+ cmsToneCurve* Curves[3];
+ cmsToneCurve** PtrCurve;
+
+ switch (Pass) {
+
+ case 1:
+ Curves[0] = cmsBuildGamma(DbgThread(), 1.1);
+ Curves[1] = cmsBuildGamma(DbgThread(), 2.2);
+ Curves[2] = cmsBuildGamma(DbgThread(), 3.4);
+
+ if (!cmsWriteTag(hProfile, cmsSigVcgtTag, Curves)) return 0;
+
+ cmsFreeToneCurveTriple(Curves);
+ return 1;
+
+
+ case 2:
+
+ PtrCurve = cmsReadTag(hProfile, cmsSigVcgtTag);
+ if (PtrCurve == NULL) return 0;
+ if (!IsGoodVal("VCGT R", cmsEstimateGamma(PtrCurve[0], 0.01), 1.1, 0.001)) return 0;
+ if (!IsGoodVal("VCGT G", cmsEstimateGamma(PtrCurve[1], 0.01), 2.2, 0.001)) return 0;
+ if (!IsGoodVal("VCGT B", cmsEstimateGamma(PtrCurve[2], 0.01), 3.4, 0.001)) return 0;
+ return 1;
+
+ default:;
+ }
+
+ return 0;
+}
+
+
+static
+int CheckRAWtags(int Pass, cmsHPROFILE hProfile)
+{
+ char Buffer[7];
+
+ switch (Pass) {
+
+ case 1:
+ return cmsWriteRawTag(hProfile, 0x31323334, "data123", 7);
+
+ case 2:
+ if (!cmsReadRawTag(hProfile, 0x31323334, Buffer, 7)) return 0;
+
+ if (strncmp(Buffer, "data123", 7) != 0) return 0;
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+
+// This is a very big test that checks every single tag
+static
+int CheckProfileCreation(void)
+{
+ cmsHPROFILE h;
+ int Pass;
+
+ h = cmsCreateProfilePlaceholder(DbgThread());
+ if (h == NULL) return 0;
+
+ if (cmsGetTagCount(h) != 0) { Fail("Empty profile with nonzero number of tags"); return 0; }
+ if (cmsIsTag(h, cmsSigAToB0Tag)) { Fail("Found a tag in an empty profile"); return 0; }
+
+ cmsSetColorSpace(h, cmsSigRgbData);
+ if (cmsGetColorSpace(h) != cmsSigRgbData) { Fail("Unable to set colorspace"); return 0; }
+
+ cmsSetPCS(h, cmsSigLabData);
+ if (cmsGetPCS(h) != cmsSigLabData) { Fail("Unable to set colorspace"); return 0; }
+
+ cmsSetDeviceClass(h, cmsSigDisplayClass);
+ if (cmsGetDeviceClass(h) != cmsSigDisplayClass) { Fail("Unable to set deviceclass"); return 0; }
+
+ cmsSetHeaderRenderingIntent(h, INTENT_SATURATION);
+ if (cmsGetHeaderRenderingIntent(h) != INTENT_SATURATION) { Fail("Unable to set rendering intent"); return 0; }
+
+ for (Pass = 1; Pass <= 2; Pass++) {
+
+ SubTest("Tags holding XYZ");
+
+ if (!CheckXYZ(Pass, h, cmsSigBlueColorantTag)) return 0;
+ if (!CheckXYZ(Pass, h, cmsSigGreenColorantTag)) return 0;
+ if (!CheckXYZ(Pass, h, cmsSigRedColorantTag)) return 0;
+ if (!CheckXYZ(Pass, h, cmsSigMediaBlackPointTag)) return 0;
+ if (!CheckXYZ(Pass, h, cmsSigMediaWhitePointTag)) return 0;
+ if (!CheckXYZ(Pass, h, cmsSigLuminanceTag)) return 0;
+
+ SubTest("Tags holding curves");
+
+ if (!CheckGamma(Pass, h, cmsSigBlueTRCTag)) return 0;
+ if (!CheckGamma(Pass, h, cmsSigGrayTRCTag)) return 0;
+ if (!CheckGamma(Pass, h, cmsSigGreenTRCTag)) return 0;
+ if (!CheckGamma(Pass, h, cmsSigRedTRCTag)) return 0;
+
+ SubTest("Tags holding text");
+
+ if (!CheckText(Pass, h, cmsSigCharTargetTag)) return 0;
+ if (!CheckText(Pass, h, cmsSigCopyrightTag)) return 0;
+ if (!CheckText(Pass, h, cmsSigProfileDescriptionTag)) return 0;
+ if (!CheckText(Pass, h, cmsSigDeviceMfgDescTag)) return 0;
+ if (!CheckText(Pass, h, cmsSigDeviceModelDescTag)) return 0;
+ if (!CheckText(Pass, h, cmsSigViewingCondDescTag)) return 0;
+ if (!CheckText(Pass, h, cmsSigScreeningDescTag)) return 0;
+
+ SubTest("Tags holding cmsICCData");
+
+ if (!CheckData(Pass, h, cmsSigPs2CRD0Tag)) return 0;
+ if (!CheckData(Pass, h, cmsSigPs2CRD1Tag)) return 0;
+ if (!CheckData(Pass, h, cmsSigPs2CRD2Tag)) return 0;
+ if (!CheckData(Pass, h, cmsSigPs2CRD3Tag)) return 0;
+ if (!CheckData(Pass, h, cmsSigPs2CSATag)) return 0;
+ if (!CheckData(Pass, h, cmsSigPs2RenderingIntentTag)) return 0;
+
+ SubTest("Tags holding signatures");
+
+ if (!CheckSignature(Pass, h, cmsSigColorimetricIntentImageStateTag)) return 0;
+ if (!CheckSignature(Pass, h, cmsSigPerceptualRenderingIntentGamutTag)) return 0;
+ if (!CheckSignature(Pass, h, cmsSigSaturationRenderingIntentGamutTag)) return 0;
+ if (!CheckSignature(Pass, h, cmsSigTechnologyTag)) return 0;
+
+ SubTest("Tags holding date_time");
+
+ if (!CheckDateTime(Pass, h, cmsSigCalibrationDateTimeTag)) return 0;
+ if (!CheckDateTime(Pass, h, cmsSigDateTimeTag)) return 0;
+
+ SubTest("Tags holding named color lists");
+
+ if (!CheckNamedColor(Pass, h, cmsSigColorantTableTag, 15, FALSE)) return 0;
+ if (!CheckNamedColor(Pass, h, cmsSigColorantTableOutTag, 15, FALSE)) return 0;
+ if (!CheckNamedColor(Pass, h, cmsSigNamedColor2Tag, 4096, TRUE)) return 0;
+
+ SubTest("Tags holding LUTs");
+
+ if (!CheckLUT(Pass, h, cmsSigAToB0Tag)) return 0;
+ if (!CheckLUT(Pass, h, cmsSigAToB1Tag)) return 0;
+ if (!CheckLUT(Pass, h, cmsSigAToB2Tag)) return 0;
+ if (!CheckLUT(Pass, h, cmsSigBToA0Tag)) return 0;
+ if (!CheckLUT(Pass, h, cmsSigBToA1Tag)) return 0;
+ if (!CheckLUT(Pass, h, cmsSigBToA2Tag)) return 0;
+ if (!CheckLUT(Pass, h, cmsSigPreview0Tag)) return 0;
+ if (!CheckLUT(Pass, h, cmsSigPreview1Tag)) return 0;
+ if (!CheckLUT(Pass, h, cmsSigPreview2Tag)) return 0;
+ if (!CheckLUT(Pass, h, cmsSigGamutTag)) return 0;
+
+ SubTest("Tags holding CHAD");
+ if (!CheckCHAD(Pass, h, cmsSigChromaticAdaptationTag)) return 0;
+
+ SubTest("Tags holding Chromaticity");
+ if (!CheckChromaticity(Pass, h, cmsSigChromaticityTag)) return 0;
+
+ SubTest("Tags holding colorant order");
+ if (!CheckColorantOrder(Pass, h, cmsSigColorantOrderTag)) return 0;
+
+ SubTest("Tags holding measurement");
+ if (!CheckMeasurement(Pass, h, cmsSigMeasurementTag)) return 0;
+
+ SubTest("Tags holding CRD info");
+ if (!CheckCRDinfo(Pass, h, cmsSigCrdInfoTag)) return 0;
+
+ SubTest("Tags holding UCR/BG");
+ if (!CheckUcrBg(Pass, h, cmsSigUcrBgTag)) return 0;
+
+ SubTest("Tags holding MPE");
+ if (!CheckMPE(Pass, h, cmsSigDToB0Tag)) return 0;
+ if (!CheckMPE(Pass, h, cmsSigDToB1Tag)) return 0;
+ if (!CheckMPE(Pass, h, cmsSigDToB2Tag)) return 0;
+ if (!CheckMPE(Pass, h, cmsSigDToB3Tag)) return 0;
+ if (!CheckMPE(Pass, h, cmsSigBToD0Tag)) return 0;
+ if (!CheckMPE(Pass, h, cmsSigBToD1Tag)) return 0;
+ if (!CheckMPE(Pass, h, cmsSigBToD2Tag)) return 0;
+ if (!CheckMPE(Pass, h, cmsSigBToD3Tag)) return 0;
+
+ SubTest("Tags using screening");
+ if (!CheckScreening(Pass, h, cmsSigScreeningTag)) return 0;
+
+ SubTest("Tags holding profile sequence description");
+ if (!CheckProfileSequenceTag(Pass, h)) return 0;
+ if (!CheckProfileSequenceIDTag(Pass, h)) return 0;
+
+ SubTest("Tags holding ICC viewing conditions");
+ if (!CheckICCViewingConditions(Pass, h)) return 0;
+
+
+ SubTest("VCGT tags");
+ if (!CheckVCGT(Pass, h)) return 0;
+
+ SubTest("RAW tags");
+ if (!CheckRAWtags(Pass, h)) return 0;
+
+
+ if (Pass == 1) {
+ cmsSaveProfileToFile(h, "alltags.icc");
+ cmsCloseProfile(h);
+ h = cmsOpenProfileFromFileTHR(DbgThread(), "alltags.icc", "r");
+ }
+
+ }
+
+ /*
+ Not implemented (by design):
+
+ cmsSigDataTag = 0x64617461, // 'data' -- Unused
+ cmsSigDeviceSettingsTag = 0x64657673, // 'devs' -- Unused
+ cmsSigNamedColorTag = 0x6E636f6C, // 'ncol' -- Don't use this one, deprecated by ICC
+ cmsSigOutputResponseTag = 0x72657370, // 'resp' -- Possible patent on this
+ */
+
+ cmsCloseProfile(h);
+ remove("alltags.icc");
+ return 1;
+}
+
+
+// Error reporting -------------------------------------------------------------------------------------------------------
+
+
+static
+void ErrorReportingFunction(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text)
+{
+ TrappedError = TRUE;
+ SimultaneousErrors++;
+ strncpy(ReasonToFailBuffer, Text, TEXT_ERROR_BUFFER_SIZE-1);
+}
+
+
+static
+int CheckBadProfiles(void)
+{
+ cmsHPROFILE h;
+
+ h = cmsOpenProfileFromFileTHR(DbgThread(), "IDoNotExist.icc", "r");
+ if (h != NULL) {
+ cmsCloseProfile(h);
+ return 0;
+ }
+
+ h = cmsOpenProfileFromFileTHR(DbgThread(), "IAmIllFormed*.icc", "r");
+ if (h != NULL) {
+ cmsCloseProfile(h);
+ return 0;
+ }
+
+ // No profile name given
+ h = cmsOpenProfileFromFileTHR(DbgThread(), "", "r");
+ if (h != NULL) {
+ cmsCloseProfile(h);
+ return 0;
+ }
+
+ h = cmsOpenProfileFromFileTHR(DbgThread(), "..", "r");
+ if (h != NULL) {
+ cmsCloseProfile(h);
+ return 0;
+ }
+
+ h = cmsOpenProfileFromFileTHR(DbgThread(), "IHaveBadAccessMode.icc", "@");
+ if (h != NULL) {
+ cmsCloseProfile(h);
+ return 0;
+ }
+
+ h = cmsOpenProfileFromFileTHR(DbgThread(), "bad.icc", "r");
+ if (h != NULL) {
+ cmsCloseProfile(h);
+ return 0;
+ }
+
+ h = cmsOpenProfileFromFileTHR(DbgThread(), "toosmall.icc", "r");
+ if (h != NULL) {
+ cmsCloseProfile(h);
+ return 0;
+ }
+
+ h = cmsOpenProfileFromMemTHR(DbgThread(), NULL, 3);
+ if (h != NULL) {
+ cmsCloseProfile(h);
+ return 0;
+ }
+
+ h = cmsOpenProfileFromMemTHR(DbgThread(), "123", 3);
+ if (h != NULL) {
+ cmsCloseProfile(h);
+ return 0;
+ }
+
+ if (SimultaneousErrors != 9) return 0;
+
+ return 1;
+}
+
+
+static
+int CheckErrReportingOnBadProfiles(void)
+{
+ int rc;
+
+ cmsSetLogErrorHandler(ErrorReportingFunction);
+ rc = CheckBadProfiles();
+ cmsSetLogErrorHandler(FatalErrorQuit);
+
+ // Reset the error state
+ TrappedError = FALSE;
+ return rc;
+}
+
+
+
+
+// ---------------------------------------------------------------------------------------------------------
+
+// Check a linear xform
+static
+int Check8linearXFORM(cmsHTRANSFORM xform, int nChan)
+{
+ int n2, i, j;
+ cmsUInt8Number Inw[MAXCHANNELS], Outw[MAXCHANNELS];
+
+ n2=0;
+
+ for (j=0; j < 0xFF; j++) {
+
+ memset(Inw, j, sizeof(Inw));
+ cmsDoTransform(xform, Inw, Outw, 1);
+
+ for (i=0; i < nChan; i++) {
+
+ int dif = abs(Outw[i] - j);
+ if (dif > n2) n2 = dif;
+
+ }
+ }
+
+ // We allow 2 contone of difference on 8 bits
+ if (n2 > 2) {
+
+ Fail("Differences too big (%x)", n2);
+ return 0;
+ }
+
+ return 1;
+}
+
+static
+int Compare8bitXFORM(cmsHTRANSFORM xform1, cmsHTRANSFORM xform2, int nChan)
+{
+ int n2, i, j;
+ cmsUInt8Number Inw[MAXCHANNELS], Outw1[MAXCHANNELS], Outw2[MAXCHANNELS];;
+
+ n2=0;
+
+ for (j=0; j < 0xFF; j++) {
+
+ memset(Inw, j, sizeof(Inw));
+ cmsDoTransform(xform1, Inw, Outw1, 1);
+ cmsDoTransform(xform2, Inw, Outw2, 1);
+
+ for (i=0; i < nChan; i++) {
+
+ int dif = abs(Outw2[i] - Outw1[i]);
+ if (dif > n2) n2 = dif;
+
+ }
+ }
+
+ // We allow 2 contone of difference on 8 bits
+ if (n2 > 2) {
+
+ Fail("Differences too big (%x)", n2);
+ return 0;
+ }
+
+
+ return 1;
+}
+
+
+// Check a linear xform
+static
+int Check16linearXFORM(cmsHTRANSFORM xform, int nChan)
+{
+ int n2, i, j;
+ cmsUInt16Number Inw[MAXCHANNELS], Outw[MAXCHANNELS];
+
+ n2=0;
+ for (j=0; j < 0xFFFF; j++) {
+
+ for (i=0; i < nChan; i++) Inw[i] = j;
+
+ cmsDoTransform(xform, Inw, Outw, 1);
+
+ for (i=0; i < nChan; i++) {
+
+ int dif = abs(Outw[i] - j);
+ if (dif > n2) n2 = dif;
+
+ }
+
+
+ // We allow 2 contone of difference on 16 bits
+ if (n2 > 0x200) {
+
+ Fail("Differences too big (%x)", n2);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static
+int Compare16bitXFORM(cmsHTRANSFORM xform1, cmsHTRANSFORM xform2, int nChan)
+{
+ int n2, i, j;
+ cmsUInt16Number Inw[MAXCHANNELS], Outw1[MAXCHANNELS], Outw2[MAXCHANNELS];;
+
+ n2=0;
+
+ for (j=0; j < 0xFFFF; j++) {
+
+ for (i=0; i < nChan; i++) Inw[i] = j;
+
+ cmsDoTransform(xform1, Inw, Outw1, 1);
+ cmsDoTransform(xform2, Inw, Outw2, 1);
+
+ for (i=0; i < nChan; i++) {
+
+ int dif = abs(Outw2[i] - Outw1[i]);
+ if (dif > n2) n2 = dif;
+
+ }
+ }
+
+ // We allow 2 contone of difference on 16 bits
+ if (n2 > 0x200) {
+
+ Fail("Differences too big (%x)", n2);
+ return 0;
+ }
+
+
+ return 1;
+}
+
+
+// Check a linear xform
+static
+int CheckFloatlinearXFORM(cmsHTRANSFORM xform, int nChan)
+{
+ int n2, i, j;
+ cmsFloat32Number In[MAXCHANNELS], Out[MAXCHANNELS];
+
+ n2=0;
+
+ for (j=0; j < 0xFFFF; j++) {
+
+ for (i=0; i < nChan; i++) In[i] = (cmsFloat32Number) (j / 65535.0);;
+
+ cmsDoTransform(xform, In, Out, 1);
+
+ for (i=0; i < nChan; i++) {
+
+ // We allow no difference in floating point
+ if (!IsGoodFixed15_16("linear xform cmsFloat32Number", Out[i], (cmsFloat32Number) (j / 65535.0)))
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+
+// Check a linear xform
+static
+int CompareFloatXFORM(cmsHTRANSFORM xform1, cmsHTRANSFORM xform2, int nChan)
+{
+ int n2, i, j;
+ cmsFloat32Number In[MAXCHANNELS], Out1[MAXCHANNELS], Out2[MAXCHANNELS];
+
+ n2=0;
+
+ for (j=0; j < 0xFFFF; j++) {
+
+ for (i=0; i < nChan; i++) In[i] = (cmsFloat32Number) (j / 65535.0);;
+
+ cmsDoTransform(xform1, In, Out1, 1);
+ cmsDoTransform(xform2, In, Out2, 1);
+
+ for (i=0; i < nChan; i++) {
+
+ // We allow no difference in floating point
+ if (!IsGoodFixed15_16("linear xform cmsFloat32Number", Out1[i], Out2[i]))
+ return 0;
+ }
+
+ }
+
+ return 1;
+}
+
+
+// Curves only transforms ----------------------------------------------------------------------------------------
+
+static
+int CheckCurvesOnlyTransforms(void)
+{
+
+ cmsHTRANSFORM xform1, xform2;
+ cmsHPROFILE h1, h2, h3;
+ cmsToneCurve* c1, *c2, *c3;
+ int rc = 1;
+
+
+ c1 = cmsBuildGamma(DbgThread(), 2.2);
+ c2 = cmsBuildGamma(DbgThread(), 1/2.2);
+ c3 = cmsBuildGamma(DbgThread(), 4.84);
+
+ h1 = cmsCreateLinearizationDeviceLinkTHR(DbgThread(), cmsSigGrayData, &c1);
+ h2 = cmsCreateLinearizationDeviceLinkTHR(DbgThread(), cmsSigGrayData, &c2);
+ h3 = cmsCreateLinearizationDeviceLinkTHR(DbgThread(), cmsSigGrayData, &c3);
+
+ SubTest("Gray float optimizeable transform");
+ xform1 = cmsCreateTransform(h1, TYPE_GRAY_FLT, h2, TYPE_GRAY_FLT, INTENT_PERCEPTUAL, 0);
+ rc &= CheckFloatlinearXFORM(xform1, 1);
+ cmsDeleteTransform(xform1);
+ if (rc == 0) goto Error;
+
+ SubTest("Gray 8 optimizeable transform");
+ xform1 = cmsCreateTransform(h1, TYPE_GRAY_8, h2, TYPE_GRAY_8, INTENT_PERCEPTUAL, 0);
+ rc &= Check8linearXFORM(xform1, 1);
+ cmsDeleteTransform(xform1);
+ if (rc == 0) goto Error;
+
+ SubTest("Gray 16 optimizeable transform");
+ xform1 = cmsCreateTransform(h1, TYPE_GRAY_16, h2, TYPE_GRAY_16, INTENT_PERCEPTUAL, 0);
+ rc &= Check16linearXFORM(xform1, 1);
+ cmsDeleteTransform(xform1);
+ if (rc == 0) goto Error;
+
+ SubTest("Gray float non-optimizeable transform");
+ xform1 = cmsCreateTransform(h1, TYPE_GRAY_FLT, h1, TYPE_GRAY_FLT, INTENT_PERCEPTUAL, 0);
+ xform2 = cmsCreateTransform(h3, TYPE_GRAY_FLT, NULL, TYPE_GRAY_FLT, INTENT_PERCEPTUAL, 0);
+
+ rc &= CompareFloatXFORM(xform1, xform2, 1);
+ cmsDeleteTransform(xform1);
+ cmsDeleteTransform(xform2);
+ if (rc == 0) goto Error;
+
+ SubTest("Gray 8 non-optimizeable transform");
+ xform1 = cmsCreateTransform(h1, TYPE_GRAY_8, h1, TYPE_GRAY_8, INTENT_PERCEPTUAL, 0);
+ xform2 = cmsCreateTransform(h3, TYPE_GRAY_8, NULL, TYPE_GRAY_8, INTENT_PERCEPTUAL, 0);
+
+ rc &= Compare8bitXFORM(xform1, xform2, 1);
+ cmsDeleteTransform(xform1);
+ cmsDeleteTransform(xform2);
+ if (rc == 0) goto Error;
+
+
+ SubTest("Gray 16 non-optimizeable transform");
+ xform1 = cmsCreateTransform(h1, TYPE_GRAY_16, h1, TYPE_GRAY_16, INTENT_PERCEPTUAL, 0);
+ xform2 = cmsCreateTransform(h3, TYPE_GRAY_16, NULL, TYPE_GRAY_16, INTENT_PERCEPTUAL, 0);
+
+ rc &= Compare16bitXFORM(xform1, xform2, 1);
+ cmsDeleteTransform(xform1);
+ cmsDeleteTransform(xform2);
+ if (rc == 0) goto Error;
+
+Error:
+
+ cmsCloseProfile(h1); cmsCloseProfile(h2); cmsCloseProfile(h3);
+ cmsFreeToneCurve(c1); cmsFreeToneCurve(c2); cmsFreeToneCurve(c3);
+
+ return rc;
+}
+
+
+
+// Lab to Lab trivial transforms ----------------------------------------------------------------------------------------
+
+static cmsFloat64Number MaxDE;
+
+static
+int CheckOneLab(cmsHTRANSFORM xform, cmsFloat64Number L, cmsFloat64Number a, cmsFloat64Number b)
+{
+ cmsCIELab In, Out;
+ cmsFloat64Number dE;
+
+ In.L = L; In.a = a; In.b = b;
+ cmsDoTransform(xform, &In, &Out, 1);
+
+ dE = cmsDeltaE(&In, &Out);
+
+ if (dE > MaxDE) MaxDE = dE;
+
+ if (MaxDE > 0.003) {
+ Fail("dE=%f Lab1=(%f, %f, %f)\n\tLab2=(%f %f %f)", MaxDE, In.L, In.a, In.b, Out.L, Out.a, Out.b);
+ cmsDoTransform(xform, &In, &Out, 1);
+ return 0;
+ }
+
+ return 1;
+}
+
+// Check several Lab, slicing at non-exact values. Precision should be 16 bits. 50x50x50 checks aprox.
+static
+int CheckSeveralLab(cmsHTRANSFORM xform)
+{
+ int L, a, b;
+
+ MaxDE = 0;
+ for (L=0; L < 65536; L += 1311) {
+
+ for (a = 0; a < 65536; a += 1232) {
+
+ for (b = 0; b < 65536; b += 1111) {
+
+ if (!CheckOneLab(xform, (L * 100.0) / 65535.0,
+ (a / 257.0) - 128, (b / 257.0) - 128))
+ return 0;
+ }
+
+ }
+
+ }
+ return 1;
+}
+
+
+static
+int OneTrivialLab(cmsHPROFILE hLab1, cmsHPROFILE hLab2, const char* txt)
+{
+ cmsHTRANSFORM xform;
+ int rc;
+
+ SubTest(txt);
+ xform = cmsCreateTransformTHR(DbgThread(), hLab1, TYPE_Lab_DBL, hLab2, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
+ cmsCloseProfile(hLab1); cmsCloseProfile(hLab2);
+
+ rc = CheckSeveralLab(xform);
+ cmsDeleteTransform(xform);
+ return rc;
+}
+
+
+static
+int CheckFloatLabTransforms(void)
+{
+ return OneTrivialLab(cmsCreateLab4ProfileTHR(DbgThread(), NULL), cmsCreateLab4ProfileTHR(DbgThread(), NULL), "Lab4/Lab4") &&
+ OneTrivialLab(cmsCreateLab2ProfileTHR(DbgThread(), NULL), cmsCreateLab2ProfileTHR(DbgThread(), NULL), "Lab2/Lab2") &&
+ OneTrivialLab(cmsCreateLab4ProfileTHR(DbgThread(), NULL), cmsCreateLab2ProfileTHR(DbgThread(), NULL), "Lab4/Lab2") &&
+ OneTrivialLab(cmsCreateLab2ProfileTHR(DbgThread(), NULL), cmsCreateLab4ProfileTHR(DbgThread(), NULL), "Lab2/Lab4");
+}
+
+
+static
+int CheckEncodedLabTransforms(void)
+{
+ cmsHTRANSFORM xform;
+ cmsUInt16Number In[3];
+ cmsCIELab Lab;
+ cmsCIELab White = { 100, 0, 0 };
+ cmsHPROFILE hLab1 = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
+ cmsHPROFILE hLab2 = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
+
+
+ xform = cmsCreateTransformTHR(DbgThread(), hLab1, TYPE_Lab_16, hLab2, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
+ cmsCloseProfile(hLab1); cmsCloseProfile(hLab2);
+
+ In[0] = 0xFFFF;
+ In[1] = 0x8080;
+ In[2] = 0x8080;
+
+ cmsDoTransform(xform, In, &Lab, 1);
+
+ if (cmsDeltaE(&Lab, &White) > 0.0001) return 0;
+ cmsDeleteTransform(xform);
+
+ hLab1 = cmsCreateLab2ProfileTHR(DbgThread(), NULL);
+ hLab2 = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
+
+ xform = cmsCreateTransformTHR(DbgThread(), hLab1, TYPE_LabV2_16, hLab2, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
+ cmsCloseProfile(hLab1); cmsCloseProfile(hLab2);
+
+
+ In[0] = 0xFF00;
+ In[1] = 0x8000;
+ In[2] = 0x8000;
+
+ cmsDoTransform(xform, In, &Lab, 1);
+
+ if (cmsDeltaE(&Lab, &White) > 0.0001) return 0;
+
+ cmsDeleteTransform(xform);
+
+ hLab2 = cmsCreateLab2ProfileTHR(DbgThread(), NULL);
+ hLab1 = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
+
+ xform = cmsCreateTransformTHR(DbgThread(), hLab1, TYPE_Lab_DBL, hLab2, TYPE_LabV2_16, INTENT_RELATIVE_COLORIMETRIC, 0);
+ cmsCloseProfile(hLab1); cmsCloseProfile(hLab2);
+
+ Lab.L = 100;
+ Lab.a = 0;
+ Lab.b = 0;
+
+ cmsDoTransform(xform, &Lab, In, 1);
+ if (In[0] != 0xFF00 ||
+ In[1] != 0x8000 ||
+ In[2] != 0x8000) return 0;
+
+ cmsDeleteTransform(xform);
+
+ hLab1 = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
+ hLab2 = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
+
+ xform = cmsCreateTransformTHR(DbgThread(), hLab1, TYPE_Lab_DBL, hLab2, TYPE_Lab_16, INTENT_RELATIVE_COLORIMETRIC, 0);
+ cmsCloseProfile(hLab1); cmsCloseProfile(hLab2);
+
+ Lab.L = 100;
+ Lab.a = 0;
+ Lab.b = 0;
+
+ cmsDoTransform(xform, &Lab, In, 1);
+
+ if (In[0] != 0xFFFF ||
+ In[1] != 0x8080 ||
+ In[2] != 0x8080) return 0;
+
+ cmsDeleteTransform(xform);
+
+ return 1;
+}
+
+static
+int CheckStoredIdentities(void)
+{
+ cmsHPROFILE hLab, hLink, h4, h2;
+ cmsHTRANSFORM xform;
+ int rc = 1;
+
+ hLab = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
+ xform = cmsCreateTransformTHR(DbgThread(), hLab, TYPE_Lab_8, hLab, TYPE_Lab_8, 0, 0);
+
+ hLink = cmsTransform2DeviceLink(xform, 3.4, 0);
+ cmsSaveProfileToFile(hLink, "abstractv2.icc");
+ cmsCloseProfile(hLink);
+
+ hLink = cmsTransform2DeviceLink(xform, 4.2, 0);
+ cmsSaveProfileToFile(hLink, "abstractv4.icc");
+ cmsCloseProfile(hLink);
+
+ cmsDeleteTransform(xform);
+ cmsCloseProfile(hLab);
+
+ h4 = cmsOpenProfileFromFileTHR(DbgThread(), "abstractv4.icc", "r");
+
+ xform = cmsCreateTransformTHR(DbgThread(), h4, TYPE_Lab_DBL, h4, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
+
+ SubTest("V4");
+ rc &= CheckSeveralLab(xform);
+
+ cmsDeleteTransform(xform);
+ cmsCloseProfile(h4);
+ if (!rc) goto Error;
+
+
+ SubTest("V2");
+ h2 = cmsOpenProfileFromFileTHR(DbgThread(), "abstractv2.icc", "r");
+
+ xform = cmsCreateTransformTHR(DbgThread(), h2, TYPE_Lab_DBL, h2, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
+ rc &= CheckSeveralLab(xform);
+ cmsDeleteTransform(xform);
+ cmsCloseProfile(h2);
+ if (!rc) goto Error;
+
+
+ SubTest("V2 -> V4");
+ h2 = cmsOpenProfileFromFileTHR(DbgThread(), "abstractv2.icc", "r");
+ h4 = cmsOpenProfileFromFileTHR(DbgThread(), "abstractv4.icc", "r");
+
+ xform = cmsCreateTransformTHR(DbgThread(), h4, TYPE_Lab_DBL, h2, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
+ rc &= CheckSeveralLab(xform);
+ cmsDeleteTransform(xform);
+ cmsCloseProfile(h2);
+ cmsCloseProfile(h4);
+
+ SubTest("V4 -> V2");
+ h2 = cmsOpenProfileFromFileTHR(DbgThread(), "abstractv2.icc", "r");
+ h4 = cmsOpenProfileFromFileTHR(DbgThread(), "abstractv4.icc", "r");
+
+ xform = cmsCreateTransformTHR(DbgThread(), h2, TYPE_Lab_DBL, h4, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, 0);
+ rc &= CheckSeveralLab(xform);
+ cmsDeleteTransform(xform);
+ cmsCloseProfile(h2);
+ cmsCloseProfile(h4);
+
+Error:
+ remove("abstractv2.icc");
+ remove("abstractv4.icc");
+ return rc;
+
+}
+
+
+
+// Check a simple xform from a matrix profile to itself. Test floating point accuracy.
+static
+int CheckMatrixShaperXFORMFloat(void)
+{
+ cmsHPROFILE hAbove, hSRGB;
+ cmsHTRANSFORM xform;
+ int rc1, rc2;
+
+ hAbove = Create_AboveRGB();
+ xform = cmsCreateTransformTHR(DbgThread(), hAbove, TYPE_RGB_FLT, hAbove, TYPE_RGB_FLT, INTENT_RELATIVE_COLORIMETRIC, 0);
+ cmsCloseProfile(hAbove);
+ rc1 = CheckFloatlinearXFORM(xform, 3);
+ cmsDeleteTransform(xform);
+
+ hSRGB = cmsCreate_sRGBProfileTHR(DbgThread());
+ xform = cmsCreateTransformTHR(DbgThread(), hSRGB, TYPE_RGB_FLT, hSRGB, TYPE_RGB_FLT, INTENT_RELATIVE_COLORIMETRIC, 0);
+ cmsCloseProfile(hSRGB);
+ rc2 = CheckFloatlinearXFORM(xform, 3);
+ cmsDeleteTransform(xform);
+
+
+ return rc1 && rc2;
+}
+
+// Check a simple xform from a matrix profile to itself. Test 16 bits accuracy.
+static
+int CheckMatrixShaperXFORM16(void)
+{
+ cmsHPROFILE hAbove, hSRGB;
+ cmsHTRANSFORM xform;
+ int rc1, rc2;
+
+ hAbove = Create_AboveRGB();
+ xform = cmsCreateTransformTHR(DbgThread(), hAbove, TYPE_RGB_16, hAbove, TYPE_RGB_16, INTENT_RELATIVE_COLORIMETRIC, 0);
+ cmsCloseProfile(hAbove);
+ rc1 = Check16linearXFORM(xform, 3);
+ cmsDeleteTransform(xform);
+
+ hSRGB = cmsCreate_sRGBProfileTHR(DbgThread());
+ xform = cmsCreateTransformTHR(DbgThread(), hSRGB, TYPE_RGB_16, hSRGB, TYPE_RGB_16, INTENT_RELATIVE_COLORIMETRIC, 0);
+ cmsCloseProfile(hSRGB);
+ rc2 = Check16linearXFORM(xform, 3);
+ cmsDeleteTransform(xform);
+
+ return rc1 && rc2;
+
+}
+
+
+// Check a simple xform from a matrix profile to itself. Test 8 bits accuracy.
+static
+int CheckMatrixShaperXFORM8(void)
+{
+ cmsHPROFILE hAbove, hSRGB;
+ cmsHTRANSFORM xform;
+ int rc1, rc2;
+
+ hAbove = Create_AboveRGB();
+ xform = cmsCreateTransformTHR(DbgThread(), hAbove, TYPE_RGB_8, hAbove, TYPE_RGB_8, INTENT_RELATIVE_COLORIMETRIC, 0);
+ cmsCloseProfile(hAbove);
+ rc1 = Check8linearXFORM(xform, 3);
+ cmsDeleteTransform(xform);
+
+ hSRGB = cmsCreate_sRGBProfileTHR(DbgThread());
+ xform = cmsCreateTransformTHR(DbgThread(), hSRGB, TYPE_RGB_8, hSRGB, TYPE_RGB_8, INTENT_RELATIVE_COLORIMETRIC, 0);
+ cmsCloseProfile(hSRGB);
+ rc2 = Check8linearXFORM(xform, 3);
+ cmsDeleteTransform(xform);
+
+
+ return rc1 && rc2;
+}
+
+
+// TODO: Check LUT based to LUT based transforms for CMYK
+
+
+
+
+
+
+// -----------------------------------------------------------------------------------------------------------------
+
+
+// Check known values going from sRGB to XYZ
+static
+int CheckOneRGB_f(cmsHTRANSFORM xform, int R, int G, int B, cmsFloat64Number X, cmsFloat64Number Y, cmsFloat64Number Z, cmsFloat64Number err)
+{
+ cmsFloat32Number RGB[3];
+ cmsFloat64Number Out[3];
+
+ RGB[0] = (cmsFloat32Number) (R / 255.0);
+ RGB[1] = (cmsFloat32Number) (G / 255.0);
+ RGB[2] = (cmsFloat32Number) (B / 255.0);
+
+ cmsDoTransform(xform, RGB, Out, 1);
+
+ return IsGoodVal("X", X , Out[0], err) &&
+ IsGoodVal("Y", Y , Out[1], err) &&
+ IsGoodVal("Z", Z , Out[2], err);
+}
+
+
+static
+int Chack_sRGB_Float(void)
+{
+ cmsHPROFILE hsRGB, hXYZ, hLab;
+ cmsHTRANSFORM xform1, xform2;
+ int rc;
+
+
+ hsRGB = cmsCreate_sRGBProfileTHR(DbgThread());
+ hXYZ = cmsCreateXYZProfileTHR(DbgThread());
+ hLab = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
+
+ xform1 = cmsCreateTransformTHR(DbgThread(), hsRGB, TYPE_RGB_FLT, hXYZ, TYPE_XYZ_DBL,
+ INTENT_RELATIVE_COLORIMETRIC, 0);
+
+ xform2 = cmsCreateTransformTHR(DbgThread(), hsRGB, TYPE_RGB_FLT, hLab, TYPE_Lab_DBL,
+ INTENT_RELATIVE_COLORIMETRIC, 0);
+ cmsCloseProfile(hsRGB);
+ cmsCloseProfile(hXYZ);
+ cmsCloseProfile(hLab);
+
+ MaxErr = 0;
+
+ // Xform 1 goes from 8 bits to XYZ,
+ rc = CheckOneRGB_f(xform1, 1, 1, 1, 0.0002926, 0.00030352, 0.00025037, 0.0001);
+ rc &= CheckOneRGB_f(xform1, 127, 127, 127, 0.2046329, 0.212230, 0.175069, 0.0001);
+ rc &= CheckOneRGB_f(xform1, 12, 13, 15, 0.0038364, 0.0039928, 0.00385212, 0.0001);
+ rc &= CheckOneRGB_f(xform1, 128, 0, 0, 0.0940846, 0.0480030, 0.00300543, 0.0001);
+ rc &= CheckOneRGB_f(xform1, 190, 25, 210, 0.3203491, 0.1605240, 0.46817115, 0.0001);
+
+ // Xform 2 goes from 8 bits to Lab, we allow 0.01 error max
+ rc &= CheckOneRGB_f(xform2, 1, 1, 1, 0.2741748, 0, 0, 0.01);
+ rc &= CheckOneRGB_f(xform2, 127, 127, 127, 53.192776, 0, 0, 0.01);
+ rc &= CheckOneRGB_f(xform2, 190, 25, 210, 47.043171, 74.564576, -56.89373, 0.01);
+ rc &= CheckOneRGB_f(xform2, 128, 0, 0, 26.158100, 48.474477, 39.425916, 0.01);
+
+ cmsDeleteTransform(xform1);
+ cmsDeleteTransform(xform2);
+ return rc;
+}
+
+
+// -----------------------------------------------------------------------------------------------------------------
+
+// This function will check CMYK -> CMYK transforms. It uses FOGRA29 and SWOP ICC profiles
+
+static
+int CheckCMYK(int Intent, const char *Profile1, const char* Profile2)
+{
+ cmsHPROFILE hSWOP = cmsOpenProfileFromFileTHR(DbgThread(), Profile1, "r");
+ cmsHPROFILE hFOGRA = cmsOpenProfileFromFileTHR(DbgThread(), Profile2, "r");
+ cmsHTRANSFORM xform, swop_lab, fogra_lab;
+ cmsFloat32Number CMYK1[4], CMYK2[4];
+ cmsCIELab Lab1, Lab2;
+ cmsHPROFILE hLab;
+ cmsFloat64Number DeltaL, Max;
+ int i;
+
+ hLab = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
+
+ xform = cmsCreateTransformTHR(DbgThread(), hSWOP, TYPE_CMYK_FLT, hFOGRA, TYPE_CMYK_FLT, Intent, 0);
+
+ swop_lab = cmsCreateTransformTHR(DbgThread(), hSWOP, TYPE_CMYK_FLT, hLab, TYPE_Lab_DBL, Intent, 0);
+ fogra_lab = cmsCreateTransformTHR(DbgThread(), hFOGRA, TYPE_CMYK_FLT, hLab, TYPE_Lab_DBL, Intent, 0);
+
+ Max = 0;
+ for (i=0; i <= 100; i++) {
+
+ CMYK1[0] = 10;
+ CMYK1[1] = 20;
+ CMYK1[2] = 30;
+ CMYK1[3] = (cmsFloat32Number) i;
+
+ cmsDoTransform(swop_lab, CMYK1, &Lab1, 1);
+ cmsDoTransform(xform, CMYK1, CMYK2, 1);
+ cmsDoTransform(fogra_lab, CMYK2, &Lab2, 1);
+
+ DeltaL = fabs(Lab1.L - Lab2.L);
+
+ if (DeltaL > Max) Max = DeltaL;
+ }
+
+
+ cmsDeleteTransform(xform);
+
+ if (Max > 3.0) return 0;
+
+ xform = cmsCreateTransformTHR(DbgThread(), hFOGRA, TYPE_CMYK_FLT, hSWOP, TYPE_CMYK_FLT, Intent, 0);
+
+ Max = 0;
+
+ for (i=0; i <= 100; i++) {
+ CMYK1[0] = 10;
+ CMYK1[1] = 20;
+ CMYK1[2] = 30;
+ CMYK1[3] = (cmsFloat32Number) i;
+
+ cmsDoTransform(fogra_lab, CMYK1, &Lab1, 1);
+ cmsDoTransform(xform, CMYK1, CMYK2, 1);
+ cmsDoTransform(swop_lab, CMYK2, &Lab2, 1);
+
+ DeltaL = fabs(Lab1.L - Lab2.L);
+
+ if (DeltaL > Max) Max = DeltaL;
+ }
+
+
+ cmsCloseProfile(hSWOP);
+ cmsCloseProfile(hFOGRA);
+ cmsCloseProfile(hLab);
+
+ cmsDeleteTransform(xform);
+ cmsDeleteTransform(swop_lab);
+ cmsDeleteTransform(fogra_lab);
+
+ return Max < 3.0;
+}
+
+static
+int CheckCMYKRoundtrip(void)
+{
+ return CheckCMYK(INTENT_RELATIVE_COLORIMETRIC, "USWebCoatedSWOP.icc", "USWebCoatedSWOP.icc");
+}
+
+
+static
+int CheckCMYKPerceptual(void)
+{
+ return CheckCMYK(INTENT_PERCEPTUAL, "USWebCoatedSWOP.icc", "UncoatedFOGRA29.icc");
+}
+
+
+
+static
+int CheckCMYKRelCol(void)
+{
+ return CheckCMYK(INTENT_RELATIVE_COLORIMETRIC, "USWebCoatedSWOP.icc", "UncoatedFOGRA29.icc");
+}
+
+
+
+static
+int CheckKOnlyBlackPreserving(void)
+{
+ cmsHPROFILE hSWOP = cmsOpenProfileFromFileTHR(DbgThread(), "USWebCoatedSWOP.icc", "r");
+ cmsHPROFILE hFOGRA = cmsOpenProfileFromFileTHR(DbgThread(), "UncoatedFOGRA29.icc", "r");
+ cmsHTRANSFORM xform, swop_lab, fogra_lab;
+ cmsFloat32Number CMYK1[4], CMYK2[4];
+ cmsCIELab Lab1, Lab2;
+ cmsHPROFILE hLab;
+ cmsFloat64Number DeltaL, Max;
+ int i;
+
+ hLab = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
+
+ xform = cmsCreateTransformTHR(DbgThread(), hSWOP, TYPE_CMYK_FLT, hFOGRA, TYPE_CMYK_FLT, INTENT_PRESERVE_K_ONLY_PERCEPTUAL, 0);
+
+ swop_lab = cmsCreateTransformTHR(DbgThread(), hSWOP, TYPE_CMYK_FLT, hLab, TYPE_Lab_DBL, INTENT_PERCEPTUAL, 0);
+ fogra_lab = cmsCreateTransformTHR(DbgThread(), hFOGRA, TYPE_CMYK_FLT, hLab, TYPE_Lab_DBL, INTENT_PERCEPTUAL, 0);
+
+ Max = 0;
+
+ for (i=0; i <= 100; i++) {
+ CMYK1[0] = 0;
+ CMYK1[1] = 0;
+ CMYK1[2] = 0;
+ CMYK1[3] = (cmsFloat32Number) i;
+
+ // SWOP CMYK to Lab1
+ cmsDoTransform(swop_lab, CMYK1, &Lab1, 1);
+
+ // SWOP To FOGRA using black preservation
+ cmsDoTransform(xform, CMYK1, CMYK2, 1);
+
+ // Obtained FOGRA CMYK to Lab2
+ cmsDoTransform(fogra_lab, CMYK2, &Lab2, 1);
+
+ // We care only on L*
+ DeltaL = fabs(Lab1.L - Lab2.L);
+
+ if (DeltaL > Max) Max = DeltaL;
+ }
+
+
+ cmsDeleteTransform(xform);
+
+ // dL should be below 3.0
+ if (Max > 3.0) return 0;
+
+
+ // Same, but FOGRA to SWOP
+ xform = cmsCreateTransformTHR(DbgThread(), hFOGRA, TYPE_CMYK_FLT, hSWOP, TYPE_CMYK_FLT, INTENT_PRESERVE_K_ONLY_PERCEPTUAL, 0);
+
+ Max = 0;
+
+ for (i=0; i <= 100; i++) {
+ CMYK1[0] = 0;
+ CMYK1[1] = 0;
+ CMYK1[2] = 0;
+ CMYK1[3] = (cmsFloat32Number) i;
+
+ cmsDoTransform(fogra_lab, CMYK1, &Lab1, 1);
+ cmsDoTransform(xform, CMYK1, CMYK2, 1);
+ cmsDoTransform(swop_lab, CMYK2, &Lab2, 1);
+
+ DeltaL = fabs(Lab1.L - Lab2.L);
+
+ if (DeltaL > Max) Max = DeltaL;
+ }
+
+
+ cmsCloseProfile(hSWOP);
+ cmsCloseProfile(hFOGRA);
+ cmsCloseProfile(hLab);
+
+ cmsDeleteTransform(xform);
+ cmsDeleteTransform(swop_lab);
+ cmsDeleteTransform(fogra_lab);
+
+ return Max < 3.0;
+}
+
+static
+int CheckKPlaneBlackPreserving(void)
+{
+ cmsHPROFILE hSWOP = cmsOpenProfileFromFileTHR(DbgThread(), "USWebCoatedSWOP.icc", "r");
+ cmsHPROFILE hFOGRA = cmsOpenProfileFromFileTHR(DbgThread(), "UncoatedFOGRA29.icc", "r");
+ cmsHTRANSFORM xform, swop_lab, fogra_lab;
+ cmsFloat32Number CMYK1[4], CMYK2[4];
+ cmsCIELab Lab1, Lab2;
+ cmsHPROFILE hLab;
+ cmsFloat64Number DeltaE, Max;
+ int i;
+
+ hLab = cmsCreateLab4ProfileTHR(DbgThread(), NULL);
+
+ xform = cmsCreateTransformTHR(DbgThread(), hSWOP, TYPE_CMYK_FLT, hFOGRA, TYPE_CMYK_FLT, INTENT_PERCEPTUAL, 0);
+
+ swop_lab = cmsCreateTransformTHR(DbgThread(), hSWOP, TYPE_CMYK_FLT, hLab, TYPE_Lab_DBL, INTENT_PERCEPTUAL, 0);
+ fogra_lab = cmsCreateTransformTHR(DbgThread(), hFOGRA, TYPE_CMYK_FLT, hLab, TYPE_Lab_DBL, INTENT_PERCEPTUAL, 0);
+
+ Max = 0;
+
+ for (i=0; i <= 100; i++) {
+ CMYK1[0] = 0;
+ CMYK1[1] = 0;
+ CMYK1[2] = 0;
+ CMYK1[3] = (cmsFloat32Number) i;
+
+ cmsDoTransform(swop_lab, CMYK1, &Lab1, 1);
+ cmsDoTransform(xform, CMYK1, CMYK2, 1);
+ cmsDoTransform(fogra_lab, CMYK2, &Lab2, 1);
+
+ DeltaE = cmsDeltaE(&Lab1, &Lab2);
+
+ if (DeltaE > Max) Max = DeltaE;
+ }
+
+
+ cmsDeleteTransform(xform);
+
+ xform = cmsCreateTransformTHR(DbgThread(), hFOGRA, TYPE_CMYK_FLT, hSWOP, TYPE_CMYK_FLT, INTENT_PRESERVE_K_PLANE_PERCEPTUAL, 0);
+
+ for (i=0; i <= 100; i++) {
+ CMYK1[0] = 30;
+ CMYK1[1] = 20;
+ CMYK1[2] = 10;
+ CMYK1[3] = (cmsFloat32Number) i;
+
+ cmsDoTransform(fogra_lab, CMYK1, &Lab1, 1);
+ cmsDoTransform(xform, CMYK1, CMYK2, 1);
+ cmsDoTransform(swop_lab, CMYK2, &Lab2, 1);
+
+ DeltaE = cmsDeltaE(&Lab1, &Lab2);
+
+ if (DeltaE > Max) Max = DeltaE;
+ }
+
+ cmsDeleteTransform(xform);
+
+
+
+ cmsCloseProfile(hSWOP);
+ cmsCloseProfile(hFOGRA);
+ cmsCloseProfile(hLab);
+
+
+ cmsDeleteTransform(swop_lab);
+ cmsDeleteTransform(fogra_lab);
+
+ return Max < 30.0;
+}
+
+
+// ------------------------------------------------------------------------------------------------------
+
+
+static
+int CheckProofingXFORMFloat(void)
+{
+ cmsHPROFILE hAbove;
+ cmsHTRANSFORM xform;
+ int rc;
+
+ hAbove = Create_AboveRGB();
+ xform = cmsCreateProofingTransformTHR(DbgThread(), hAbove, TYPE_RGB_FLT, hAbove, TYPE_RGB_FLT, hAbove,
+ INTENT_RELATIVE_COLORIMETRIC, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_SOFTPROOFING);
+ cmsCloseProfile(hAbove);
+ rc = CheckFloatlinearXFORM(xform, 3);
+ cmsDeleteTransform(xform);
+ return rc;
+}
+
+static
+int CheckProofingXFORM16(void)
+{
+ cmsHPROFILE hAbove;
+ cmsHTRANSFORM xform;
+ int rc;
+
+ hAbove = Create_AboveRGB();
+ xform = cmsCreateProofingTransformTHR(DbgThread(), hAbove, TYPE_RGB_16, hAbove, TYPE_RGB_16, hAbove,
+ INTENT_RELATIVE_COLORIMETRIC, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_SOFTPROOFING|cmsFLAGS_NOCACHE);
+ cmsCloseProfile(hAbove);
+ rc = Check16linearXFORM(xform, 3);
+ cmsDeleteTransform(xform);
+ return rc;
+}
+
+
+static
+int CheckGamutCheck(void)
+{
+ cmsHPROFILE hSRGB, hAbove;
+ cmsHTRANSFORM xform;
+ int rc;
+ cmsUInt16Number Alarm[3] = { 0xDEAD, 0xBABE, 0xFACE };
+
+ // Set alarm codes to fancy values so we could check the out of gamut condition
+ cmsSetAlarmCodes(Alarm);
+
+ // Create the profiles
+ hSRGB = cmsCreate_sRGBProfileTHR(DbgThread());
+ hAbove = Create_AboveRGB();
+
+ if (hSRGB == NULL || hAbove == NULL) return 0; // Failed
+
+ SubTest("Gamut check on floating point");
+
+ // Create a gamut checker in the same space. No value should be out of gamut
+ xform = cmsCreateProofingTransformTHR(DbgThread(), hAbove, TYPE_RGB_FLT, hAbove, TYPE_RGB_FLT, hAbove,
+ INTENT_RELATIVE_COLORIMETRIC, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_GAMUTCHECK);
+
+
+ if (!CheckFloatlinearXFORM(xform, 3)) {
+ cmsCloseProfile(hSRGB);
+ cmsCloseProfile(hAbove);
+ cmsDeleteTransform(xform);
+ Fail("Gamut check on same profile failed");
+ return 0;
+ }
+
+ cmsDeleteTransform(xform);
+
+ SubTest("Gamut check on 16 bits");
+
+ xform = cmsCreateProofingTransformTHR(DbgThread(), hAbove, TYPE_RGB_16, hAbove, TYPE_RGB_16, hAbove,
+ INTENT_RELATIVE_COLORIMETRIC, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_GAMUTCHECK);
+
+ cmsCloseProfile(hSRGB);
+ cmsCloseProfile(hAbove);
+
+ rc = Check16linearXFORM(xform, 3);
+
+ cmsDeleteTransform(xform);
+
+ return rc;
+}
+
+
+
+// -------------------------------------------------------------------------------------------------------------------
+
+static
+int CheckBlackPoint(void)
+{
+ cmsHPROFILE hProfile;
+ cmsCIEXYZ Black;
+ cmsCIELab Lab;
+
+ hProfile = cmsOpenProfileFromFileTHR(DbgThread(), "sRGB Color Space Profile.icm", "r");
+ cmsDetectBlackPoint(&Black, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0);
+ cmsCloseProfile(hProfile);
+
+
+ hProfile = cmsOpenProfileFromFileTHR(DbgThread(), "USWebCoatedSWOP.icc", "r");
+ cmsDetectBlackPoint(&Black, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0);
+ cmsXYZ2Lab(NULL, &Lab, &Black);
+ cmsCloseProfile(hProfile);
+
+ hProfile = cmsOpenProfileFromFileTHR(DbgThread(), "lcms2cmyk.icc", "r");
+ cmsDetectBlackPoint(&Black, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0);
+ cmsXYZ2Lab(NULL, &Lab, &Black);
+ cmsCloseProfile(hProfile);
+
+ hProfile = cmsOpenProfileFromFileTHR(DbgThread(), "UncoatedFOGRA29.icc", "r");
+ cmsDetectBlackPoint(&Black, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0);
+ cmsXYZ2Lab(NULL, &Lab, &Black);
+ cmsCloseProfile(hProfile);
+
+ hProfile = cmsOpenProfileFromFileTHR(DbgThread(), "USWebCoatedSWOP.icc", "r");
+ cmsDetectBlackPoint(&Black, hProfile, INTENT_PERCEPTUAL, 0);
+ cmsXYZ2Lab(NULL, &Lab, &Black);
+ cmsCloseProfile(hProfile);
+
+ return 1;
+}
+
+
+static
+int CheckOneTAC(cmsFloat64Number InkLimit)
+{
+ cmsHPROFILE h;
+ cmsFloat64Number d;
+
+ h =CreateFakeCMYK(InkLimit, TRUE);
+ cmsSaveProfileToFile(h, "lcmstac.icc");
+ cmsCloseProfile(h);
+
+ h = cmsOpenProfileFromFile("lcmstac.icc", "r");
+ d = cmsDetectTAC(h);
+ cmsCloseProfile(h);
+
+ remove("lcmstac.icc");
+
+ if (fabs(d - InkLimit) > 5) return 0;
+
+ return 1;
+}
+
+
+static
+int CheckTAC(void)
+{
+ if (!CheckOneTAC(180)) return 0;
+ if (!CheckOneTAC(220)) return 0;
+ if (!CheckOneTAC(286)) return 0;
+ if (!CheckOneTAC(310)) return 0;
+ if (!CheckOneTAC(330)) return 0;
+
+ return 1;
+}
+
+// -------------------------------------------------------------------------------------------------------
+
+
+#define NPOINTS_IT8 10 // (17*17*17*17)
+
+static
+int CheckCGATS(void)
+{
+ cmsHANDLE it8;
+ int i;
+
+
+ it8 = cmsIT8Alloc(DbgThread());
+ if (it8 == NULL) return 0;
+
+ cmsIT8SetSheetType(it8, "LCMS/TESTING");
+ cmsIT8SetPropertyStr(it8, "ORIGINATOR", "1 2 3 4");
+ cmsIT8SetPropertyUncooked(it8, "DESCRIPTOR", "1234");
+ cmsIT8SetPropertyStr(it8, "MANUFACTURER", "3");
+ cmsIT8SetPropertyDbl(it8, "CREATED", 4);
+ cmsIT8SetPropertyDbl(it8, "SERIAL", 5);
+ cmsIT8SetPropertyHex(it8, "MATERIAL", 0x123);
+
+ cmsIT8SetPropertyDbl(it8, "NUMBER_OF_SETS", NPOINTS_IT8);
+ cmsIT8SetPropertyDbl(it8, "NUMBER_OF_FIELDS", 4);
+
+ cmsIT8SetDataFormat(it8, 0, "SAMPLE_ID");
+ cmsIT8SetDataFormat(it8, 1, "RGB_R");
+ cmsIT8SetDataFormat(it8, 2, "RGB_G");
+ cmsIT8SetDataFormat(it8, 3, "RGB_B");
+
+ for (i=0; i < NPOINTS_IT8; i++) {
+
+ char Patch[20];
+
+ sprintf(Patch, "P%d", i);
+
+ cmsIT8SetDataRowCol(it8, i, 0, Patch);
+ cmsIT8SetDataRowColDbl(it8, i, 1, i);
+ cmsIT8SetDataRowColDbl(it8, i, 2, i);
+ cmsIT8SetDataRowColDbl(it8, i, 3, i);
+ }
+
+ cmsIT8SaveToFile(it8, "TEST.IT8");
+ cmsIT8Free(it8);
+
+
+ it8 = cmsIT8LoadFromFile(DbgThread(), "TEST.IT8");
+ cmsIT8SaveToFile(it8, "TEST.IT8");
+ cmsIT8Free(it8);
+
+
+
+ it8 = cmsIT8LoadFromFile(DbgThread(), "TEST.IT8");
+
+ if (cmsIT8GetPropertyDbl(it8, "DESCRIPTOR") != 1234) {
+
+ return 0;
+ }
+
+
+ cmsIT8SetPropertyDbl(it8, "DESCRIPTOR", 5678);
+
+ if (cmsIT8GetPropertyDbl(it8, "DESCRIPTOR") != 5678) {
+
+ return 0;
+ }
+
+ if (cmsIT8GetDataDbl(it8, "P3", "RGB_G") != 3) {
+
+ return 0;
+ }
+
+ cmsIT8Free(it8);
+
+ remove("TEST.IT8");
+ return 1;
+
+}
+
+
+// Create CSA/CRD
+
+static
+void GenerateCSA(const char* cInProf, const char* FileName)
+{
+ cmsHPROFILE hProfile;
+ cmsUInt32Number n;
+ char* Buffer;
+ cmsContext BuffThread = DbgThread();
+ FILE* o;
+
+
+ if (cInProf == NULL)
+ hProfile = cmsCreateLab4Profile(NULL);
+ else
+ hProfile = cmsOpenProfileFromFile(cInProf, "r");
+
+ n = cmsGetPostScriptCSA(DbgThread(), hProfile, 0, 0, NULL, 0);
+ if (n == 0) return;
+
+ Buffer = (char*) _cmsMalloc(BuffThread, n + 1);
+ cmsGetPostScriptCSA(DbgThread(), hProfile, 0, 0, Buffer, n);
+ Buffer[n] = 0;
+
+ if (FileName != NULL) {
+ o = fopen(FileName, "wb");
+ fwrite(Buffer, n, 1, o);
+ fclose(o);
+ }
+
+ _cmsFree(BuffThread, Buffer);
+ cmsCloseProfile(hProfile);
+ remove(FileName);
+}
+
+
+static
+void GenerateCRD(const char* cOutProf, const char* FileName)
+{
+ cmsHPROFILE hProfile;
+ cmsUInt32Number n;
+ char* Buffer;
+ cmsUInt32Number dwFlags = 0;
+ cmsContext BuffThread = DbgThread();
+
+
+ if (cOutProf == NULL)
+ hProfile = cmsCreateLab4Profile(NULL);
+ else
+ hProfile = cmsOpenProfileFromFile(cOutProf, "r");
+
+ n = cmsGetPostScriptCRD(DbgThread(), hProfile, 0, dwFlags, NULL, 0);
+ if (n == 0) return;
+
+ Buffer = (char*) _cmsMalloc(BuffThread, n + 1);
+ cmsGetPostScriptCRD(DbgThread(), hProfile, 0, dwFlags, Buffer, n);
+ Buffer[n] = 0;
+
+ if (FileName != NULL) {
+ FILE* o = fopen(FileName, "wb");
+ fwrite(Buffer, n, 1, o);
+ fclose(o);
+ }
+
+ _cmsFree(BuffThread, Buffer);
+ cmsCloseProfile(hProfile);
+ remove(FileName);
+}
+
+static
+int CheckPostScript(void)
+{
+ GenerateCSA("sRGB Color Space Profile.icm", "sRGB_CSA.ps");
+ GenerateCSA("aRGBlcms2.icc", "aRGB_CSA.ps");
+ GenerateCSA("sRGB_v4_ICC_preference.icc", "sRGBV4_CSA.ps");
+ GenerateCSA("USWebCoatedSWOP.icc", "SWOP_CSA.ps");
+ GenerateCSA(NULL, "Lab_CSA.ps");
+ GenerateCSA("graylcms2.icc", "gray_CSA.ps");
+
+ GenerateCRD("sRGB Color Space Profile.icm", "sRGB_CRD.ps");
+ GenerateCRD("aRGBlcms2.icc", "aRGB_CRD.ps");
+ GenerateCRD(NULL, "Lab_CRD.ps");
+ GenerateCRD("USWebCoatedSWOP.icc", "SWOP_CRD.ps");
+ GenerateCRD("sRGB_v4_ICC_preference.icc", "sRGBV4_CRD.ps");
+ GenerateCRD("graylcms2.icc", "gray_CRD.ps");
+
+ return 1;
+}
+
+
+
+
+
+
+
+
+static
+int CheckV4gamma(void)
+{
+ cmsHPROFILE h;
+ cmsUInt16Number Lin[] = {0, 0xffff};
+ cmsToneCurve*g = cmsBuildTabulatedToneCurve16(DbgThread(), 2, Lin);
+
+ h = cmsOpenProfileFromFileTHR(DbgThread(), "v4gamma.icc", "w");
+ if (h == NULL) return 0;
+
+
+ cmsSetProfileVersion(h, 4.2);
+
+ if (!cmsWriteTag(h, cmsSigGrayTRCTag, g)) return 0;
+ cmsCloseProfile(h);
+
+ cmsFreeToneCurve(g);
+ remove("v4gamma.icc");
+ return 1;
+}
+
+
+
+/*
+#include <windows.h>
+void WindowsPerformance(void)
+{
+ LARGE_INTEGER s1, s2, f;
+ cmsFloat64Number tmul, time;
+ cmsUInt32Number i, j;
+ cmsUInt16Number in, out;
+ cmsInterpParams* p;
+ cmsUInt16Number* Tab;
+ int nNodesToCheck = 4096;
+ int Down = 0;
+
+ Tab = (cmsUInt16Number*) malloc(sizeof(cmsUInt16Number)* nNodesToCheck);
+ if (Tab == NULL) return;
+
+ p = _cmsComputeInterpParams(DbgThread(), nNodesToCheck, 1, 1, 0);
+ if (p == NULL) return;
+
+ BuildTable(nNodesToCheck, Tab, Down);
+
+ p ->Table = Tab;
+
+ QueryPerformanceFrequency(&f);
+ QueryPerformanceCounter(&s1);
+
+
+ for (j = 0; j < 0xa00; j++) {
+
+ for (i=0; i <= 0xffff; i++) {
+
+ in = i;
+ p ->Interpolation.Lerp16(&in, &out, p);
+ }
+ }
+
+
+ QueryPerformanceCounter(&s2);
+ tmul = 1000./(cmsFloat64Number)f.QuadPart;
+ time = (s2.QuadPart - s1.QuadPart)*tmul;
+
+ printf("\t\tTime = %lg ms\n", time);
+
+ printf("xput = %f\n", (cmsFloat64Number) (65535.0 * 0xa00 * 1000.0) / (time * 1024.0 * 1024.0));
+ _cmsFreeInterpParams(p);
+ free(Tab);
+
+}
+*/
+
+
+typedef struct {cmsUInt8Number r, g, b, a;} Scanline_rgb1;
+typedef struct {cmsUInt16Number r, g, b, a;} Scanline_rgb2;
+typedef struct {cmsUInt8Number r, g, b;} Scanline_rgb8;
+typedef struct {cmsUInt16Number r, g, b;} Scanline_rgb0;
+
+
+
+static
+void SpeedTest16bits(const char * Title, cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut)
+{
+
+ int r, g, b, j;
+ clock_t atime;
+ cmsFloat64Number seconds, diff;
+ cmsHTRANSFORM hlcmsxform;
+ Scanline_rgb0 *In;
+ cmsUInt32Number Mb;
+
+ hlcmsxform = cmsCreateTransformTHR(DbgThread(), hlcmsProfileIn, TYPE_RGB_16, hlcmsProfileOut, TYPE_RGB_16, INTENT_PERCEPTUAL, cmsFLAGS_NOCACHE);
+
+ Mb = 256*256*256*sizeof(Scanline_rgb0);
+
+ In = (Scanline_rgb0*) malloc(Mb);
+
+ j = 0;
+ for (r=0; r < 256; r++)
+ for (g=0; g < 256; g++)
+ for (b=0; b < 256; b++) {
+
+ In[j].r = (cmsUInt16Number) ((r << 8) | r);
+ In[j].g = (cmsUInt16Number) ((g << 8) | g);
+ In[j].b = (cmsUInt16Number) ((b << 8) | b);
+
+ j++;
+ }
+
+
+ printf("%s...", Title);
+
+ atime = clock();
+
+ cmsDoTransform(hlcmsxform, In, In, 256*256*256);
+
+ diff = clock() - atime;
+ seconds = (cmsFloat64Number) diff / CLOCKS_PER_SEC;
+ free(In);
+
+
+ printf("done.\n[%d tics, %g sec, %g Mpixel/sec.]\n", (int) diff, seconds, Mb / (1024*1024*seconds*3*2) );
+
+ cmsDeleteTransform(hlcmsxform);
+ cmsCloseProfile(hlcmsProfileIn);
+ cmsCloseProfile(hlcmsProfileOut);
+}
+
+
+static
+void SpeedTest16bitsCMYK(const char * Title, cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut)
+{
+
+ int r, g, b, j;
+ clock_t atime;
+ cmsFloat64Number seconds, diff;
+ cmsHTRANSFORM hlcmsxform;
+ Scanline_rgb2 *In;
+ cmsUInt32Number Mb;
+
+
+ hlcmsxform = cmsCreateTransformTHR(DbgThread(), hlcmsProfileIn, TYPE_CMYK_16, hlcmsProfileOut, TYPE_CMYK_16, INTENT_PERCEPTUAL, cmsFLAGS_NOCACHE);
+
+ Mb = 256*256*256*sizeof(Scanline_rgb2);
+
+ In = (Scanline_rgb2*) malloc(Mb);
+
+ j = 0;
+ for (r=0; r < 256; r++)
+ for (g=0; g < 256; g++)
+ for (b=0; b < 256; b++) {
+
+ In[j].r = (cmsUInt16Number) ((r << 8) | r);
+ In[j].g = (cmsUInt16Number) ((g << 8) | g);
+ In[j].b = (cmsUInt16Number) ((b << 8) | b);
+ In[j].a = 0;
+
+ j++;
+ }
+
+
+ printf("%s...", Title);
+
+ atime = clock();
+
+ cmsDoTransform(hlcmsxform, In, In, 256*256*256);
+
+ diff = clock() - atime;
+ seconds = (cmsFloat64Number) diff / CLOCKS_PER_SEC;
+ free(In);
+
+
+ printf("done.\n[%d tics, %g sec, %g Mpixel/sec.]\n", (int) diff, seconds, Mb / (1024*1024*seconds*4*2) );
+
+ cmsDeleteTransform(hlcmsxform);
+ cmsCloseProfile(hlcmsProfileIn);
+ cmsCloseProfile(hlcmsProfileOut);
+}
+
+
+static
+void SpeedTest8bits(const char * Title, cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut, int Intent)
+{
+ int r, g, b, j;
+ clock_t atime;
+ cmsFloat64Number seconds, diff;
+ cmsHTRANSFORM hlcmsxform;
+ Scanline_rgb8 *In;
+ cmsUInt32Number Mb;
+
+
+ hlcmsxform = cmsCreateTransformTHR(DbgThread(), hlcmsProfileIn, TYPE_RGB_8, hlcmsProfileOut, TYPE_RGB_8, Intent, cmsFLAGS_NOCACHE);
+
+ Mb = 256*256*256*sizeof(Scanline_rgb8);
+
+ In = (Scanline_rgb8*) malloc(Mb);
+
+ j = 0;
+ for (r=0; r < 256; r++)
+ for (g=0; g < 256; g++)
+ for (b=0; b < 256; b++) {
+
+ In[j].r = (cmsUInt8Number) r;
+ In[j].g = (cmsUInt8Number) g;
+ In[j].b = (cmsUInt8Number) b;
+
+ j++;
+ }
+
+
+ printf("%s...", Title);
+
+ atime = clock();
+
+ cmsDoTransform(hlcmsxform, In, In, 256*256*256);
+
+ diff = clock() - atime;
+ seconds = (cmsFloat64Number) diff / CLOCKS_PER_SEC;
+ free(In);
+
+
+ printf("done.\n[%d tics, %g sec, %g Mpixels/sec.]\n", (int) diff, seconds, Mb / (1024*1024*seconds*3) );
+
+ cmsDeleteTransform(hlcmsxform);
+ cmsCloseProfile(hlcmsProfileIn);
+ cmsCloseProfile(hlcmsProfileOut);
+}
+
+
+static
+void SpeedTest8bitsCMYK(const char * Title, cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut)
+{
+ int r, g, b, j;
+ clock_t atime;
+ cmsFloat64Number seconds, diff;
+ cmsHTRANSFORM hlcmsxform;
+ Scanline_rgb2 *In;
+ cmsUInt32Number Mb;
+
+ hlcmsxform = cmsCreateTransformTHR(DbgThread(), hlcmsProfileIn, TYPE_CMYK_8, hlcmsProfileOut, TYPE_CMYK_8, INTENT_PERCEPTUAL, cmsFLAGS_NOCACHE);
+
+ Mb = 256*256*256*sizeof(Scanline_rgb2);
+
+ In = (Scanline_rgb2*) malloc(Mb);
+
+ j = 0;
+ for (r=0; r < 256; r++)
+ for (g=0; g < 256; g++)
+ for (b=0; b < 256; b++) {
+
+ In[j].r = (cmsUInt8Number) r;
+ In[j].g = (cmsUInt8Number) g;
+ In[j].b = (cmsUInt8Number) b;
+ In[j].a = (cmsUInt8Number) 0;
+
+ j++;
+ }
+
+
+ printf("%s...", Title);
+
+ atime = clock();
+
+ cmsDoTransform(hlcmsxform, In, In, 256*256*256);
+
+ diff = clock() - atime;
+ seconds = (cmsFloat64Number) diff / CLOCKS_PER_SEC;
+ free(In);
+
+
+ printf("done.\n[%d tics, %g sec, %g Mpixels/sec.]\n", (int) diff, seconds, Mb / (1024*1024*seconds*4) );
+
+ cmsDeleteTransform(hlcmsxform);
+ cmsCloseProfile(hlcmsProfileIn);
+ cmsCloseProfile(hlcmsProfileOut);
+}
+
+
+
+static
+cmsHPROFILE CreateCurves(void)
+{
+ cmsToneCurve* Gamma = cmsBuildGamma(DbgThread(), 1.1);
+ cmsToneCurve* Transfer[3];
+ cmsHPROFILE h;
+
+ Transfer[0] = Transfer[1] = Transfer[2] = Gamma;
+ h = cmsCreateLinearizationDeviceLink(cmsSigRgbData, Transfer);
+
+ cmsFreeToneCurve(Gamma);
+
+ return h;
+}
+
+
+static
+void SpeedTest(void)
+{
+
+ SpeedTest16bits("16 bits on CLUT profiles",
+ cmsOpenProfileFromFileTHR(DbgThread(), "sRGB Color Space Profile.icm", "r"),
+ cmsOpenProfileFromFileTHR(DbgThread(), "sRGBSpac.icm", "r"));
+
+ SpeedTest8bits("8 bits on CLUT profiles",
+ cmsOpenProfileFromFileTHR(DbgThread(), "sRGB Color Space Profile.icm", "r"),
+ cmsOpenProfileFromFileTHR(DbgThread(), "sRGBSpac.icm", "r"),
+ INTENT_PERCEPTUAL);
+
+ SpeedTest8bits("8 bits on Matrix-Shaper profiles",
+ cmsCreate_sRGBProfile(),
+ Create_AboveRGB(),
+ INTENT_PERCEPTUAL);
+
+ SpeedTest8bits("8 bits on SAME Matrix-Shaper profiles",
+ cmsCreate_sRGBProfile(),
+ cmsCreate_sRGBProfile(),
+ INTENT_PERCEPTUAL);
+
+ SpeedTest8bits("8 bits on curves",
+ CreateCurves(),
+ CreateCurves(),
+ INTENT_PERCEPTUAL);
+
+
+ SpeedTest8bits("8 bits on Matrix-Shaper profiles (AbsCol)",
+ cmsCreate_sRGBProfile(),
+ Create_AboveRGB(),
+ INTENT_ABSOLUTE_COLORIMETRIC);
+
+ SpeedTest16bits("16 bits on curves",
+ CreateCurves(),
+ CreateCurves());
+
+ SpeedTest8bitsCMYK("8 bits on CMYK profiles",
+ cmsOpenProfileFromFileTHR(DbgThread(), "USWebCoatedSWOP.icc", "r"),
+ cmsOpenProfileFromFileTHR(DbgThread(), "UncoatedFOGRA29.icc", "r"));
+
+ SpeedTest16bitsCMYK("16 bits on CMYK profiles",
+ cmsOpenProfileFromFileTHR(DbgThread(), "USWebCoatedSWOP.icc", "r"),
+ cmsOpenProfileFromFileTHR(DbgThread(), "UncoatedFOGRA29.icc", "r"));
+
+}
+
+
+
+// Print the supported intents
+static
+void PrintSupportedIntents(void)
+{
+ cmsUInt32Number n, i;
+ cmsUInt32Number Codes[200];
+ char* Descriptions[200];
+
+ n = cmsGetSupportedIntents(200, Codes, Descriptions);
+
+ printf("Supported intents:\n");
+ for (i=0; i < n; i++) {
+ printf("\t%d - %s\n", Codes[i], Descriptions[i]);
+ }
+ printf("\n");
+}
+
+// ZOO checks ------------------------------------------------------------------------------------------------------------
+
+
+#ifdef _CMS_IS_WINDOWS
+
+static char ZOOfolder[cmsMAX_PATH] = "c:\\colormaps\\bpc\\";
+static char ZOOwrite[cmsMAX_PATH] = "c:\\colormaps\\write\\";
+static char ZOORawWrite[cmsMAX_PATH] = "c:\\colormaps\\rawwrite\\";
+
+
+// Read all tags on a profile given by its handle
+static
+void ReadAllTags(cmsHPROFILE h)
+{
+ int i, n;
+ cmsTagSignature sig;
+
+ n = cmsGetTagCount(h);
+ for (i=0; i < n; i++) {
+
+ sig = cmsGetTagSignature(h, i);
+ if (cmsReadTag(h, sig) == NULL) return;
+ }
+}
+
+
+// Read all tags on a profile given by its handle
+static
+void ReadAllRAWTags(cmsHPROFILE h)
+{
+ int i, n;
+ cmsTagSignature sig;
+ int len;
+
+ n = cmsGetTagCount(h);
+ for (i=0; i < n; i++) {
+
+ sig = cmsGetTagSignature(h, i);
+ len = cmsReadRawTag(h, sig, NULL, 0);
+ }
+}
+
+
+static
+void PrintInfo(cmsHPROFILE h, cmsInfoType Info)
+{
+ wchar_t* text;
+ int len;
+ cmsContext id = DbgThread();
+
+ len = cmsGetProfileInfo(h, Info, "en", "US", NULL, 0);
+ if (len == 0) return;
+
+ text = _cmsMalloc(id, len);
+ cmsGetProfileInfo(h, Info, "en", "US", text, len);
+
+ wprintf(L"%s\n", text);
+ _cmsFree(id, text);
+}
+
+
+static
+void PrintAllInfos(cmsHPROFILE h)
+{
+ PrintInfo(h, cmsInfoDescription);
+ PrintInfo(h, cmsInfoManufacturer);
+ PrintInfo(h, cmsInfoModel);
+ PrintInfo(h, cmsInfoCopyright);
+ printf("\n\n");
+}
+
+static
+void ReadAllLUTS(cmsHPROFILE h)
+{
+ cmsPipeline* a;
+ cmsCIEXYZ Black;
+
+ a = _cmsReadInputLUT(h, INTENT_PERCEPTUAL);
+ if (a) cmsPipelineFree(a);
+
+ a = _cmsReadInputLUT(h, INTENT_RELATIVE_COLORIMETRIC);
+ if (a) cmsPipelineFree(a);
+
+ a = _cmsReadInputLUT(h, INTENT_SATURATION);
+ if (a) cmsPipelineFree(a);
+
+ a = _cmsReadInputLUT(h, INTENT_ABSOLUTE_COLORIMETRIC);
+ if (a) cmsPipelineFree(a);
+
+
+ a = _cmsReadOutputLUT(h, INTENT_PERCEPTUAL);
+ if (a) cmsPipelineFree(a);
+
+ a = _cmsReadOutputLUT(h, INTENT_RELATIVE_COLORIMETRIC);
+ if (a) cmsPipelineFree(a);
+
+ a = _cmsReadOutputLUT(h, INTENT_SATURATION);
+ if (a) cmsPipelineFree(a);
+
+ a = _cmsReadOutputLUT(h, INTENT_ABSOLUTE_COLORIMETRIC);
+ if (a) cmsPipelineFree(a);
+
+
+ a = _cmsReadDevicelinkLUT(h, INTENT_PERCEPTUAL);
+ if (a) cmsPipelineFree(a);
+
+ a = _cmsReadDevicelinkLUT(h, INTENT_RELATIVE_COLORIMETRIC);
+ if (a) cmsPipelineFree(a);
+
+ a = _cmsReadDevicelinkLUT(h, INTENT_SATURATION);
+ if (a) cmsPipelineFree(a);
+
+ a = _cmsReadDevicelinkLUT(h, INTENT_ABSOLUTE_COLORIMETRIC);
+ if (a) cmsPipelineFree(a);
+
+
+ cmsDetectBlackPoint(&Black, h, INTENT_PERCEPTUAL, 0);
+ cmsDetectBlackPoint(&Black, h, INTENT_RELATIVE_COLORIMETRIC, 0);
+ cmsDetectBlackPoint(&Black, h, INTENT_SATURATION, 0);
+ cmsDetectBlackPoint(&Black, h, INTENT_ABSOLUTE_COLORIMETRIC, 0);
+ cmsDetectTAC(h);
+}
+
+// Check one specimen in the ZOO
+
+static
+int CheckSingleSpecimen(const char* Profile)
+{
+ char BuffSrc[256];
+ char BuffDst[256];
+ cmsHPROFILE h;
+
+ sprintf(BuffSrc, "%s%s", ZOOfolder, Profile);
+ sprintf(BuffDst, "%s%s", ZOOwrite, Profile);
+
+ h = cmsOpenProfileFromFile(BuffSrc, "r");
+ if (h == NULL) return 0;
+
+ printf("%s\n", Profile);
+ PrintAllInfos(h);
+ ReadAllTags(h);
+ // ReadAllRAWTags(h);
+ ReadAllLUTS(h);
+
+ cmsSaveProfileToFile(h, BuffDst);
+ cmsCloseProfile(h);
+
+ h = cmsOpenProfileFromFile(BuffDst, "r");
+ if (h == NULL) return 0;
+ ReadAllTags(h);
+
+
+ cmsCloseProfile(h);
+
+ return 1;
+}
+
+static
+int CheckRAWSpecimen(const char* Profile)
+{
+ char BuffSrc[256];
+ char BuffDst[256];
+ cmsHPROFILE h;
+
+ sprintf(BuffSrc, "%s%s", ZOOfolder, Profile);
+ sprintf(BuffDst, "%s%s", ZOORawWrite, Profile);
+
+ h = cmsOpenProfileFromFile(BuffSrc, "r");
+ if (h == NULL) return 0;
+
+ ReadAllTags(h);
+ ReadAllRAWTags(h);
+ cmsSaveProfileToFile(h, BuffDst);
+ cmsCloseProfile(h);
+
+ h = cmsOpenProfileFromFile(BuffDst, "r");
+ if (h == NULL) return 0;
+ ReadAllTags(h);
+ cmsCloseProfile(h);
+
+ return 1;
+}
+
+
+static
+void CheckProfileZOO(void)
+{
+
+ struct _finddata_t c_file;
+ long hFile;
+
+ cmsSetLogErrorHandler(NULL);
+
+ if ( (hFile = _findfirst("c:\\colormaps\\bpc\\*.*", &c_file)) == -1L )
+ printf("No files in current directory");
+ else
+ {
+ do
+ {
+
+ printf("%s\n", c_file.name);
+ if (strcmp(c_file.name, ".") != 0 &&
+ strcmp(c_file.name, "..") != 0) {
+
+ CheckSingleSpecimen( c_file.name);
+ // CheckRAWSpecimen( c_file.name);
+
+ if (TotalMemory > 0)
+ printf("Ok, but %s are left!\n", MemStr(TotalMemory));
+ else
+ printf("Ok.\n");
+
+ }
+
+ } while ( _findnext(hFile, &c_file) == 0 );
+
+ _findclose(hFile);
+ }
+
+ cmsSetLogErrorHandler(FatalErrorQuit);
+}
+
+#endif
+
+// ---------------------------------------------------------------------------------------
+
+// cmsBool cmsGBDdumpVRML(cmsHANDLE hGBD, const char* fname);
+
+// Gamut descriptor routines
+static
+int CheckGBD(void)
+{
+ cmsCIELab Lab;
+ cmsHANDLE h;
+ int L, a, b;
+ cmsUInt32Number r1, g1, b1;
+ cmsHPROFILE hLab, hsRGB;
+ cmsHTRANSFORM xform;
+
+ h = cmsGBDAlloc(DbgThread());
+ if (h == NULL) return 0;
+
+ // Fill all Lab gamut as valid
+ SubTest("Filling RAW gamut");
+
+ for (L=0; L <= 100; L += 10)
+ for (a = -128; a <= 128; a += 5)
+ for (b = -128; b <= 128; b += 5) {
+
+ Lab.L = L;
+ Lab.a = a;
+ Lab.b = b;
+ if (!cmsGDBAddPoint(h, &Lab)) return 0;
+ }
+
+ // Complete boundaries
+ SubTest("computing Lab gamut");
+ if (!cmsGDBCompute(h, 0)) return 0;
+
+
+ // All points should be inside gamut
+ SubTest("checking Lab gamut");
+ for (L=10; L <= 90; L += 25)
+ for (a = -120; a <= 120; a += 25)
+ for (b = -120; b <= 120; b += 25) {
+
+ Lab.L = L;
+ Lab.a = a;
+ Lab.b = b;
+ if (!cmsGDBCheckPoint(h, &Lab)) {
+ return 0;
+ }
+ }
+ cmsGBDFree(h);
+
+
+ // Now for sRGB
+ SubTest("checking sRGB gamut");
+ h = cmsGBDAlloc(DbgThread());
+ hsRGB = cmsCreate_sRGBProfile();
+ hLab = cmsCreateLab4Profile(NULL);
+
+ xform = cmsCreateTransform(hsRGB, TYPE_RGB_8, hLab, TYPE_Lab_DBL, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOCACHE);
+ cmsCloseProfile(hsRGB); cmsCloseProfile(hLab);
+
+
+ for (r1=0; r1 < 256; r1 += 5) {
+ for (g1=0; g1 < 256; g1 += 5)
+ for (b1=0; b1 < 256; b1 += 5) {
+
+ cmsCIELab Lab;
+ cmsUInt8Number rgb[3];
+
+ rgb[0] = r1;
+ rgb[1] = g1;
+ rgb[2] = b1;
+
+ cmsDoTransform(xform, rgb, &Lab, 1);
+
+ // if (fabs(Lab.b) < 20 && Lab.a > 0) continue;
+
+ if (!cmsGDBAddPoint(h, &Lab)) {
+ cmsGBDFree(h);
+ return 0;
+ }
+
+
+ }
+ }
+
+
+ if (!cmsGDBCompute(h, 0)) return 0;
+ // cmsGBDdumpVRML(h, "c:\\colormaps\\lab.wrl");
+
+ for (r1=10; r1 < 200; r1 += 10) {
+ for (g1=10; g1 < 200; g1 += 10)
+ for (b1=10; b1 < 200; b1 += 10) {
+
+ cmsCIELab Lab;
+ cmsUInt8Number rgb[3];
+
+ rgb[0] = r1;
+ rgb[1] = g1;
+ rgb[2] = b1;
+
+ cmsDoTransform(xform, rgb, &Lab, 1);
+ if (!cmsGDBCheckPoint(h, &Lab)) {
+
+ cmsDeleteTransform(xform);
+ cmsGBDFree(h);
+ return 0;
+ }
+ }
+ }
+
+
+ cmsDeleteTransform(xform);
+ cmsGBDFree(h);
+
+ SubTest("checking LCh chroma ring");
+ h = cmsGBDAlloc(DbgThread());
+
+
+ for (r1=0; r1 < 360; r1++) {
+
+ cmsCIELCh LCh;
+
+ LCh.L = 70;
+ LCh.C = 60;
+ LCh.h = r1;
+
+ cmsLCh2Lab(&Lab, &LCh);
+ if (!cmsGDBAddPoint(h, &Lab)) {
+ cmsGBDFree(h);
+ return 0;
+ }
+ }
+
+
+ if (!cmsGDBCompute(h, 0)) return 0;
+
+ cmsGBDFree(h);
+
+ return 1;
+}
+
+
+static
+void CheckCode()
+{
+ cmsHTRANSFORM Proof1_CMYK_from_XYZ_TRANSFORM, Proof2_CMYK_from_XYZ_TRANSFORM;
+ cmsHPROFILE Input_XYZ_PROFILE;
+ cmsHPROFILE Output_CMYK_PROFILE;
+ int Intent = INTENT_PERCEPTUAL;
+ cmsHPROFILE PROFILE_ary[3];
+ cmsFloat64Number Limit = 250;
+
+
+ Input_XYZ_PROFILE = cmsCreateXYZProfile();
+ Output_CMYK_PROFILE = cmsOpenProfileFromFile("USWebCoatedSWOP.icc", "r");
+
+
+ Proof1_CMYK_from_XYZ_TRANSFORM = cmsCreateProofingTransform(
+ Input_XYZ_PROFILE, TYPE_XYZ_16,
+ Output_CMYK_PROFILE, TYPE_CMYK_16,
+ cmsCreateXYZProfile(),
+ Intent,
+ INTENT_ABSOLUTE_COLORIMETRIC, 0);
+
+
+
+
+
+ PROFILE_ary[0] = cmsTransform2DeviceLink ( Proof1_CMYK_from_XYZ_TRANSFORM, 3.4, 0);
+ PROFILE_ary[1] = cmsCreateInkLimitingDeviceLink( cmsSigCmykData, Limit);
+
+ // Generate proof transform with InkLimit.
+ Proof2_CMYK_from_XYZ_TRANSFORM = cmsCreateMultiprofileTransform(
+ PROFILE_ary, 2, TYPE_XYZ_16, TYPE_CMYK_16, Intent, 0);
+
+}
+
+
+int main(int argc, char* argv[])
+{
+ int Exhaustive = 0;
+ int DoSpeedTests = 1;
+
+
+#ifdef _MSC_VER
+ _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
+#endif
+
+ printf("LittleCMS %2.2f test bed %s %s\n\n", LCMS_VERSION / 1000.0, __DATE__, __TIME__);
+
+ if ((argc == 2) && strcmp(argv[1], "--exhaustive") == 0) {
+
+ Exhaustive = 1;
+ printf("Running exhaustive tests (will take a while...)\n\n");
+ }
+
+
+ printf("Installing debug memory plug-in ... ");
+ cmsPlugin(&DebugMemHandler);
+ printf("done.\n");
+
+ printf("Installing error logger ... ");
+ cmsSetLogErrorHandler(FatalErrorQuit);
+ printf("done.\n");
+
+#ifdef _CMS_IS_WINDOWS
+ // CheckProfileZOO();
+#endif
+
+ // CheckCode();
+
+ PrintSupportedIntents();
+
+ // Create utility profiles
+ Check("Creation of test profiles", CreateTestProfiles);
+
+ Check("Base types", CheckBaseTypes);
+ Check("endianess", CheckEndianess);
+ Check("quick floor", CheckQuickFloor);
+ Check("quick floor word", CheckQuickFloorWord);
+ Check("Fixed point 15.16 representation", CheckFixedPoint15_16);
+ Check("Fixed point 8.8 representation", CheckFixedPoint8_8);
+
+ // Forward 1D interpolation
+ Check("1D interpolation in 2pt tables", Check1DLERP2);
+ Check("1D interpolation in 3pt tables", Check1DLERP3);
+ Check("1D interpolation in 4pt tables", Check1DLERP4);
+ Check("1D interpolation in 6pt tables", Check1DLERP6);
+ Check("1D interpolation in 18pt tables", Check1DLERP18);
+ Check("1D interpolation in descending 2pt tables", Check1DLERP2Down);
+ Check("1D interpolation in descending 3pt tables", Check1DLERP3Down);
+ Check("1D interpolation in descending 6pt tables", Check1DLERP6Down);
+ Check("1D interpolation in descending 18pt tables", Check1DLERP18Down);
+
+ if (Exhaustive) {
+
+ Check("1D interpolation in n tables", ExhaustiveCheck1DLERP);
+ Check("1D interpolation in descending tables", ExhaustiveCheck1DLERPDown);
+ }
+
+ // Forward 3D interpolation
+ Check("3D interpolation Tetrahedral (float) ", Check3DinterpolationFloatTetrahedral);
+ Check("3D interpolation Trilinear (float) ", Check3DinterpolationFloatTrilinear);
+ Check("3D interpolation Tetrahedral (16) ", Check3DinterpolationTetrahedral16);
+ Check("3D interpolation Trilinear (16) ", Check3DinterpolationTrilinear16);
+
+ if (Exhaustive) {
+
+ Check("Exhaustive 3D interpolation Tetrahedral (float) ", ExaustiveCheck3DinterpolationFloatTetrahedral);
+ Check("Exhaustive 3D interpolation Trilinear (float) ", ExaustiveCheck3DinterpolationFloatTrilinear);
+ Check("Exhaustive 3D interpolation Tetrahedral (16) ", ExhaustiveCheck3DinterpolationTetrahedral16);
+ Check("Exhaustive 3D interpolation Trilinear (16) ", ExhaustiveCheck3DinterpolationTrilinear16);
+ }
+
+ Check("Reverse interpolation 3 -> 3", CheckReverseInterpolation3x3);
+ Check("Reverse interpolation 4 -> 3", CheckReverseInterpolation4x3);
+
+ // Encoding of colorspaces
+ Check("Lab to LCh and back (float only) ", CheckLab2LCh);
+ Check("Lab to XYZ and back (float only) ", CheckLab2XYZ);
+ Check("Lab to xyY and back (float only) ", CheckLab2xyY);
+ Check("Lab V2 encoding", CheckLabV2encoding);
+ Check("Lab V4 encoding", CheckLabV4encoding);
+
+ // BlackBody
+ Check("Blackbody radiator", CheckTemp2CHRM);
+
+ // Tone curves
+ Check("Linear gamma curves (16 bits)", CheckGammaCreation16);
+ Check("Linear gamma curves (float)", CheckGammaCreationFlt);
+
+ Check("Curve 1.8 (float)", CheckGamma18);
+ Check("Curve 2.2 (float)", CheckGamma22);
+ Check("Curve 3.0 (float)", CheckGamma30);
+
+ Check("Curve 1.8 (table)", CheckGamma18Table);
+ Check("Curve 2.2 (table)", CheckGamma22Table);
+ Check("Curve 3.0 (table)", CheckGamma30Table);
+
+ Check("Curve 1.8 (word table)", CheckGamma18TableWord);
+ Check("Curve 2.2 (word table)", CheckGamma22TableWord);
+ Check("Curve 3.0 (word table)", CheckGamma30TableWord);
+
+ Check("Parametric curves", CheckParametricToneCurves);
+
+ Check("Join curves", CheckJointCurves);
+ Check("Join curves descending", CheckJointCurvesDescending);
+ Check("Join curves degenerated", CheckReverseDegenerated);
+ Check("Join curves sRGB (Float)", CheckJointFloatCurves_sRGB);
+ Check("Join curves sRGB (16 bits)", CheckJoint16Curves_sRGB);
+ Check("Join curves sigmoidal", CheckJointCurvesSShaped);
+
+ // LUT basics
+ Check("LUT creation & dup", CheckLUTcreation);
+ Check("1 Stage LUT ", Check1StageLUT);
+ Check("2 Stage LUT ", Check2StageLUT);
+ Check("2 Stage LUT (16 bits)", Check2Stage16LUT);
+ Check("3 Stage LUT ", Check3StageLUT);
+ Check("3 Stage LUT (16 bits)", Check3Stage16LUT);
+ Check("4 Stage LUT ", Check4StageLUT);
+ Check("4 Stage LUT (16 bits)", Check4Stage16LUT);
+ Check("5 Stage LUT ", Check5StageLUT);
+ Check("5 Stage LUT (16 bits) ", Check5Stage16LUT);
+ Check("6 Stage LUT ", Check6StageLUT);
+ Check("6 Stage LUT (16 bits) ", Check6Stage16LUT);
+
+ // LUT operation
+ Check("Lab to Lab LUT (float only) ", CheckLab2LabLUT);
+ Check("XYZ to XYZ LUT (float only) ", CheckXYZ2XYZLUT);
+ Check("Lab to Lab MAT LUT (float only) ", CheckLab2LabMatLUT);
+ Check("Named Color LUT", CheckNamedColorLUT);
+ Check("Usual formatters", CheckFormatters16);
+ Check("Floating point formatters", CheckFormattersFloat);
+
+ // MLU
+ Check("Multilocalized Unicode", CheckMLU);
+
+ // Named color
+ Check("Named color lists", CheckNamedColorList);
+
+ // Profile I/O (this one is huge!)
+ Check("Profile creation", CheckProfileCreation);
+
+
+ // Error reporting
+ Check("Error reporting on bad profiles", CheckErrReportingOnBadProfiles);
+
+ // Transforms
+ Check("Curves only transforms", CheckCurvesOnlyTransforms);
+ Check("Float Lab->Lab transforms", CheckFloatLabTransforms);
+ Check("Encoded Lab->Lab transforms", CheckEncodedLabTransforms);
+ Check("Stored identities", CheckStoredIdentities);
+
+ Check("Matrix-shaper transform (float)", CheckMatrixShaperXFORMFloat);
+ Check("Matrix-shaper transform (16 bits)", CheckMatrixShaperXFORM16);
+ Check("Matrix-shaper transform (8 bits)", CheckMatrixShaperXFORM8);
+
+
+ // Known values
+ Check("Known values across matrix-shaper", Chack_sRGB_Float);
+
+ Check("Matrix-shaper proofing transform (float)", CheckProofingXFORMFloat);
+ Check("Matrix-shaper proofing transform (16 bits)", CheckProofingXFORM16);
+
+ Check("Gamut check", CheckGamutCheck);
+
+ Check("CMYK roundtrip on perceptual transform", CheckCMYKRoundtrip);
+
+ Check("CMYK perceptual transform", CheckCMYKPerceptual);
+ //Check("CMYK rel.col. transform", CheckCMYKRelCol);
+
+ Check("Black ink only preservation", CheckKOnlyBlackPreserving);
+ Check("Black plane preservation", CheckKPlaneBlackPreserving);
+
+
+ Check("Deciding curve types", CheckV4gamma);
+
+ Check("Black point detection", CheckBlackPoint);
+ Check("TAC detection", CheckTAC);
+
+ Check("CGATS parser", CheckCGATS);
+ Check("PostScript generator", CheckPostScript);
+ Check("Segment maxima GBD", CheckGBD);
+
+ if (DoSpeedTests)
+ SpeedTest();
+
+ DebugMemPrintTotals();
+
+ cmsUnregisterPlugins();
+
+ // Cleanup
+ RemoveTestProfiles();
+
+ return TotalFail;
+}
+
diff --git a/testbed/testthread.cpp b/testbed/testthread.cpp
new file mode 100644
index 0000000..435445c
--- /dev/null
+++ b/testbed/testthread.cpp
@@ -0,0 +1,52 @@
+
+#include <windows.h>
+#include "lcms.h"
+
+static cmsHPROFILE prof_xyz,prof_rgb;
+static cmsHTRANSFORM trans_xyz_to_rgb,trans_rgb_to_xyz;
+
+static DWORD WINAPI make_trans_xyz_to_rgb(LPVOID lpParameter)
+{
+ trans_xyz_to_rgb = cmsCreateTransform(
+ prof_xyz,TYPE_XYZ_DBL,
+ prof_rgb,TYPE_RGB_DBL,
+ INTENT_ABSOLUTE_COLORIMETRIC,cmsFLAGS_NOTPRECALC);
+ return 0;
+}
+
+static DWORD WINAPI make_trans_rgb_to_xyz(LPVOID lpParameter)
+{
+ trans_rgb_to_xyz = cmsCreateTransform(
+ prof_rgb,TYPE_RGB_DBL,
+ prof_xyz,TYPE_XYZ_DBL,
+ INTENT_ABSOLUTE_COLORIMETRIC,cmsFLAGS_NOTPRECALC);
+ return 0;
+}
+
+int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR
+lpCmdLine,int nCmdShow)
+{
+ prof_xyz = cmsCreateXYZProfile();
+ prof_rgb = cmsOpenProfileFromFile("AdobeRGB1998.icc","rb");
+//cmsCreate_sRGBProfile();
+ for (int i=0;i<10;++i)
+ {
+#define try_threads
+#ifdef try_threads
+ DWORD threadid;
+ HANDLE workers[2];
+ workers[0] = CreateThread(NULL,0,make_trans_xyz_to_rgb,NULL,0,&threadid);
+ workers[1] = CreateThread(NULL,0,make_trans_rgb_to_xyz,NULL,0,&threadid);
+ WaitForMultipleObjects(2,workers,TRUE,INFINITE);
+ for (unsigned i=0;i<2;++i)
+ CloseHandle(workers[i]);
+#else
+ make_trans_xyz_to_rgb(0);
+ make_trans_rgb_to_xyz(0);
+#endif
+ cmsDeleteTransform(trans_xyz_to_rgb);
+ cmsDeleteTransform(trans_rgb_to_xyz);
+ }
+ cmsCloseProfile(prof_rgb);
+ cmsCloseProfile(prof_xyz);
+}
diff --git a/testbed/toosmall.icc b/testbed/toosmall.icc
new file mode 100755
index 0000000..15e5e49
--- /dev/null
+++ b/testbed/toosmall.icc
Binary files differ