diff options
author | Marti Maria <info@littlecms.com> | 2010-02-08 17:20:25 +0100 |
---|---|---|
committer | Marti Maria <info@littlecms.com> | 2010-02-08 17:20:25 +0100 |
commit | 3cefc4ee12fe7144a65a205f6645c14ec5be68ce (patch) | |
tree | 37c160448e2accb5bbada6f6d4798a90741e4a12 /testbed | |
download | lcms2-3cefc4ee12fe7144a65a205f6645c14ec5be68ce.tar.gz |
initial commit
Diffstat (limited to 'testbed')
-rw-r--r-- | testbed/Makefile.am | 31 | ||||
-rw-r--r-- | testbed/Makefile.in | 487 | ||||
-rwxr-xr-x | testbed/USWebCoatedSWOP.icc | bin | 0 -> 557168 bytes | |||
-rwxr-xr-x | testbed/UncoatedFOGRA29.icc | bin | 0 -> 654140 bytes | |||
-rw-r--r-- | testbed/bad.icc | 21 | ||||
-rwxr-xr-x | testbed/sRGB Color Space Profile.icm | bin | 0 -> 3144 bytes | |||
-rwxr-xr-x | testbed/sRGBSpac.icm | bin | 0 -> 28202 bytes | |||
-rwxr-xr-x | testbed/sRGB_v4_ICC_preference.icc | bin | 0 -> 60960 bytes | |||
-rw-r--r-- | testbed/testcms2.c | 6699 | ||||
-rw-r--r-- | testbed/testthread.cpp | 52 | ||||
-rwxr-xr-x | testbed/toosmall.icc | bin | 0 -> 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 Binary files differnew file mode 100755 index 0000000..078a644 --- /dev/null +++ b/testbed/USWebCoatedSWOP.icc diff --git a/testbed/UncoatedFOGRA29.icc b/testbed/UncoatedFOGRA29.icc Binary files differnew file mode 100755 index 0000000..70e13f5 --- /dev/null +++ b/testbed/UncoatedFOGRA29.icc 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 Binary files differnew file mode 100755 index 0000000..7f9d18d --- /dev/null +++ b/testbed/sRGB Color Space Profile.icm diff --git a/testbed/sRGBSpac.icm b/testbed/sRGBSpac.icm Binary files differnew file mode 100755 index 0000000..83563ef --- /dev/null +++ b/testbed/sRGBSpac.icm diff --git a/testbed/sRGB_v4_ICC_preference.icc b/testbed/sRGB_v4_ICC_preference.icc Binary files differnew file mode 100755 index 0000000..cfbd03e --- /dev/null +++ b/testbed/sRGB_v4_ICC_preference.icc 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 Binary files differnew file mode 100755 index 0000000..15e5e49 --- /dev/null +++ b/testbed/toosmall.icc |