From 078665ecf29b6a9ca31803e6ebd12f22f3da9961 Mon Sep 17 00:00:00 2001 From: Marti Maria Date: Wed, 12 Feb 2014 12:21:45 +0100 Subject: Merge from Artifex branch --- AUTHORS | 2 + ChangeLog | 9 +- Makefile.am | 2 +- Makefile.in | 2 +- Projects/VC2010/testbed/testbed.vcxproj | 5 + Projects/VC2010/testbed/testbed.vcxproj.filters | 11 + configure | 66 +- configure.ac | 48 +- doc/LittleCMS2.5 API.pdf | Bin 2354776 -> 0 bytes doc/LittleCMS2.5 Plugin API.pdf | Bin 1373331 -> 0 bytes doc/LittleCMS2.5 tutorial.pdf | Bin 1563508 -> 0 bytes doc/LittleCMS2.6 API.pdf | Bin 0 -> 990129 bytes doc/LittleCMS2.6 Plugin API.pdf | Bin 0 -> 506789 bytes doc/LittleCMS2.6 tutorial.pdf | Bin 0 -> 837477 bytes include/lcms2.h | 58 +- include/lcms2_plugin.h | 45 +- src/cmscam02.c | 10 +- src/cmscnvrt.c | 106 +- src/cmserr.c | 292 ++++- src/cmsgamma.c | 95 +- src/cmsgmt.c | 1180 +++++++++--------- src/cmsintrp.c | 45 +- src/cmsio0.c | 227 ++-- src/cmsio1.c | 2 +- src/cmsopt.c | 97 +- src/cmspack.c | 88 +- src/cmsplugin.c | 358 +++++- src/cmsps2.c | 4 +- src/cmstypes.c | 224 +++- src/cmsvirt.c | 6 +- src/cmsxform.c | 259 +++- src/lcms2.def | 666 +++++----- src/lcms2_internal.h | 284 ++++- testbed/Makefile.am | 4 +- testbed/Makefile.in | 9 +- testbed/ibm-t61.icc | Bin 0 -> 20480 bytes testbed/new.icc | Bin 0 -> 20480 bytes testbed/testcms2.c | 647 +++++----- testbed/testcms2.h | 84 ++ testbed/testplugin.c | 1476 +++++++++++++++++++++++ testbed/testthread.cpp | 148 ++- testbed/zoo_icc.c | 310 +++++ utils/delphi/lcms2dll.pas | 693 ++++++----- utils/jpgicc/jpgicc.c | 9 +- utils/psicc/psicc.c | 2 +- utils/tificc/tificc.c | 2 +- utils/transicc/transicc.1 | 2 +- 47 files changed, 5559 insertions(+), 2018 deletions(-) delete mode 100755 doc/LittleCMS2.5 API.pdf delete mode 100755 doc/LittleCMS2.5 Plugin API.pdf delete mode 100755 doc/LittleCMS2.5 tutorial.pdf create mode 100755 doc/LittleCMS2.6 API.pdf create mode 100755 doc/LittleCMS2.6 Plugin API.pdf create mode 100755 doc/LittleCMS2.6 tutorial.pdf create mode 100755 testbed/ibm-t61.icc create mode 100644 testbed/new.icc create mode 100755 testbed/testcms2.h create mode 100755 testbed/testplugin.c create mode 100755 testbed/zoo_icc.c diff --git a/AUTHORS b/AUTHORS index 8a6c3da..4f3820a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -32,9 +32,11 @@ XhmikosR Stanislav Brabec (SuSe) Leonhard Gruenschloss (Google) Patrick Noffke +Christopher James Halse Rogers Special Thanks -------------- +Artifex software Jan Morovic Jos Vernon (WebSupergoo) Harald Schneider (Maxon) diff --git a/ChangeLog b/ChangeLog index 1fde607..4324a6a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -131,4 +131,11 @@ Fixed some indexing out of bounds in floating point interpolation Fixed a bug in PCS/Colorspace order when reading V2 Lab devicelinks Added a way to retrieve matrix shaper always, no matter LUT is present Changed endianess detection for PowerPC -Fixed memory leaks on error handling \ No newline at end of file +Fixed memory leaks on error handling +Big revamp on Contexts, from Artifex +New locking plug-in, from Artifex +Added directories for tiff, jpeg in configure script + +----------------------- +2.6 Featured release +----------------------- diff --git a/Makefile.am b/Makefile.am index e0cba52..9b2b333 100644 --- a/Makefile.am +++ b/Makefile.am @@ -12,7 +12,7 @@ PACKAGE_VERSION = @PACKAGE_VERSION@ SUBDIRS = src include utils/tificc utils/transicc utils/linkicc utils/jpgicc utils/psicc testbed # Additional files to distribute -EXTRA_DIST = AUTHORS COPYING ChangeLog doc Projects include bin Lib INSTALL README.1ST lcms2.pc.in +EXTRA_DIST = AUTHORS COPYING ChangeLog doc Projects include bin Lib INSTALL README.1ST autogen.sh lcms2.pc.in pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = lcms2.pc diff --git a/Makefile.in b/Makefile.in index 89b9605..b97d38e 100644 --- a/Makefile.in +++ b/Makefile.in @@ -300,7 +300,7 @@ AUTOMAKE_OPTIONS = 1.7.2 dist-zip foreign SUBDIRS = src include utils/tificc utils/transicc utils/linkicc utils/jpgicc utils/psicc testbed # Additional files to distribute -EXTRA_DIST = AUTHORS COPYING ChangeLog doc Projects include bin Lib INSTALL README.1ST lcms2.pc.in +EXTRA_DIST = AUTHORS COPYING ChangeLog doc Projects include bin Lib INSTALL README.1ST autogen.sh lcms2.pc.in pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = lcms2.pc all: all-recursive diff --git a/Projects/VC2010/testbed/testbed.vcxproj b/Projects/VC2010/testbed/testbed.vcxproj index fc92b0c..c26e3cc 100644 --- a/Projects/VC2010/testbed/testbed.vcxproj +++ b/Projects/VC2010/testbed/testbed.vcxproj @@ -189,6 +189,11 @@ + + + + + diff --git a/Projects/VC2010/testbed/testbed.vcxproj.filters b/Projects/VC2010/testbed/testbed.vcxproj.filters index 21cbdd4..fecb121 100644 --- a/Projects/VC2010/testbed/testbed.vcxproj.filters +++ b/Projects/VC2010/testbed/testbed.vcxproj.filters @@ -18,5 +18,16 @@ Source Files + + Source Files + + + Source Files + + + + + Header Files + \ No newline at end of file diff --git a/configure b/configure index 425273d..e9218e9 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for lcms2 2.5. +# Generated by GNU Autoconf 2.69 for lcms2 2.6. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -587,8 +587,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='lcms2' PACKAGE_TARNAME='lcms2' -PACKAGE_VERSION='2.5' -PACKAGE_STRING='lcms2 2.5' +PACKAGE_VERSION='2.6' +PACKAGE_STRING='lcms2 2.6' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -1338,7 +1338,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures lcms2 2.5 to adapt to many kinds of systems. +\`configure' configures lcms2 2.6 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1408,7 +1408,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of lcms2 2.5:";; + short | recursive ) echo "Configuration of lcms2 2.6:";; esac cat <<\_ACEOF @@ -1429,8 +1429,8 @@ Optional Features: Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) - --without-jpeg disable JPEG support - --without-tiff disable TIFF support + --with-jpeg=DIR use jpeg installed in DIR + --with-tiff=DIR use tiff installed in DIR --without-zlib disable ZLIB support --with-pic try to use only PIC/non-PIC objects [default=use both] @@ -1517,7 +1517,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -lcms2 configure 2.5 +lcms2 configure 2.6 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2003,7 +2003,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by lcms2 $as_me 2.5, which was +It was created by lcms2 $as_me 2.6, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2374,7 +2374,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu # then set age to 0. # LIBRARY_CURRENT=2 -LIBRARY_REVISION=5 +LIBRARY_REVISION=6 LIBRARY_AGE=0 @@ -2918,7 +2918,7 @@ fi # Define the identity of the package. PACKAGE='lcms2' - VERSION='2.5' + VERSION='2.6' # Some tools Automake needs. @@ -2950,21 +2950,45 @@ am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' -# Disable JPEG. +# Point to JPEG installed in DIR or disable JPEG with --without-jpeg. # Check whether --with-jpeg was given. if test "${with_jpeg+set}" = set; then : - withval=$with_jpeg; with_jpeg=$withval + withval=$with_jpeg; + if test "x$withval" = "xno" ; then + with_jpeg='no' + else + if test "x$withval" != "xyes" ; then + with_jpeg=$withval + JPEG_DIR=$withval + CPPFLAGS="$CPPFLAGS -I$JPEG_DIR/include" + LDFLAGS="$LDFLAGS -L$JPEG_DIR/lib" + fi + with_jpeg='yes' + fi + else with_jpeg='yes' fi -# Disable TIFF. +# Point to TIFF installed in DIR or disable TIFF with --without-tiff. # Check whether --with-tiff was given. if test "${with_tiff+set}" = set; then : - withval=$with_tiff; with_tiff=$withval + withval=$with_tiff; + if test "x$withval" = "xno" ; then + with_tiff='no' + else + if test "x$withval" != "xyes" ; then + with_tiff=$withval + TIFF_DIR=$withval + CPPFLAGS="$CPPFLAGS -I$TIFF_DIR/include" + LDFLAGS="$LDFLAGS -L$TIFF_DIR/lib" + fi + with_tiff='yes' + fi + else with_tiff='yes' fi @@ -15961,8 +15985,8 @@ have_jpeg='no' LIB_JPEG='' if test ! "$with_jpeg" = 'no' then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for JPEG support " >&5 -$as_echo_n "checking for JPEG support ... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for JPEG support" >&5 +$as_echo_n "checking for JPEG support... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: " >&5 $as_echo "" >&6; } failed=0; @@ -16443,8 +16467,8 @@ have_tiff='no' LIB_TIFF='' if test ! "$with_tiff" = 'no' then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for TIFF support " >&5 -$as_echo_n "checking for TIFF support ... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for TIFF support" >&5 +$as_echo_n "checking for TIFF support... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: " >&5 $as_echo "" >&6; } failed=0; @@ -17256,7 +17280,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by lcms2 $as_me 2.5, which was +This file was extended by lcms2 $as_me 2.6, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -17313,7 +17337,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -lcms2 config.status 2.5 +lcms2 config.status 2.6 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 7041865..777a962 100644 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,7 @@ AC_PREREQ(2.60) # # Set the package name and version # -AC_INIT(lcms2,2.5) +AC_INIT(lcms2,2.6) # # Libtool library revision control info @@ -31,7 +31,7 @@ AC_INIT(lcms2,2.5) # then set age to 0. # LIBRARY_CURRENT=2 -LIBRARY_REVISION=5 +LIBRARY_REVISION=6 LIBRARY_AGE=0 AC_SUBST(LIBRARY_CURRENT)dnl @@ -43,17 +43,41 @@ AC_CANONICAL_HOST AM_INIT_AUTOMAKE([foreign 1.7.2 no-define dist-zip]) -# Disable JPEG. +# Point to JPEG installed in DIR or disable JPEG with --without-jpeg. AC_ARG_WITH(jpeg, - [ --without-jpeg disable JPEG support], - [with_jpeg=$withval], - [with_jpeg='yes']) + [ --with-jpeg=DIR use jpeg installed in DIR], + [ + if [ test "x$withval" = "xno" ]; then + [with_jpeg='no'] + else + if [ test "x$withval" != "xyes" ]; then + with_jpeg=$withval + JPEG_DIR=$withval + CPPFLAGS="$CPPFLAGS -I$JPEG_DIR/include" + LDFLAGS="$LDFLAGS -L$JPEG_DIR/lib" + fi + [with_jpeg='yes'] + fi + ], + [with_jpeg='yes']) -# Disable TIFF. +# Point to TIFF installed in DIR or disable TIFF with --without-tiff. AC_ARG_WITH(tiff, - [ --without-tiff disable TIFF support], - [with_tiff=$withval], - [with_tiff='yes']) + [ --with-tiff=DIR use tiff installed in DIR], + [ + if [ test "x$withval" = "xno" ]; then + [with_tiff='no'] + else + if [ test "x$withval" != "xyes" ]; then + with_tiff=$withval + TIFF_DIR=$withval + CPPFLAGS="$CPPFLAGS -I$TIFF_DIR/include" + LDFLAGS="$LDFLAGS -L$TIFF_DIR/lib" + fi + [with_tiff='yes'] + fi + ], + [with_tiff='yes']) # Disable ZLIB AC_ARG_WITH(zlib, @@ -116,7 +140,7 @@ have_jpeg='no' LIB_JPEG='' if test ! "$with_jpeg" = 'no' then - AC_MSG_CHECKING(for JPEG support ) + AC_MSG_CHECKING([for JPEG support]) AC_MSG_RESULT() failed=0; passed=0; @@ -221,7 +245,7 @@ have_tiff='no' LIB_TIFF='' if test ! "$with_tiff" = 'no' then - AC_MSG_CHECKING(for TIFF support ) + AC_MSG_CHECKING([for TIFF support]) AC_MSG_RESULT() failed=0; passed=0; diff --git a/doc/LittleCMS2.5 API.pdf b/doc/LittleCMS2.5 API.pdf deleted file mode 100755 index 581b244..0000000 Binary files a/doc/LittleCMS2.5 API.pdf and /dev/null differ diff --git a/doc/LittleCMS2.5 Plugin API.pdf b/doc/LittleCMS2.5 Plugin API.pdf deleted file mode 100755 index 8017972..0000000 Binary files a/doc/LittleCMS2.5 Plugin API.pdf and /dev/null differ diff --git a/doc/LittleCMS2.5 tutorial.pdf b/doc/LittleCMS2.5 tutorial.pdf deleted file mode 100755 index 13d7596..0000000 Binary files a/doc/LittleCMS2.5 tutorial.pdf and /dev/null differ diff --git a/doc/LittleCMS2.6 API.pdf b/doc/LittleCMS2.6 API.pdf new file mode 100755 index 0000000..e8e004b Binary files /dev/null and b/doc/LittleCMS2.6 API.pdf differ diff --git a/doc/LittleCMS2.6 Plugin API.pdf b/doc/LittleCMS2.6 Plugin API.pdf new file mode 100755 index 0000000..3a31286 Binary files /dev/null and b/doc/LittleCMS2.6 Plugin API.pdf differ diff --git a/doc/LittleCMS2.6 tutorial.pdf b/doc/LittleCMS2.6 tutorial.pdf new file mode 100755 index 0000000..abb2c69 Binary files /dev/null and b/doc/LittleCMS2.6 tutorial.pdf differ diff --git a/include/lcms2.h b/include/lcms2.h index c801409..c993a4b 100644 --- a/include/lcms2.h +++ b/include/lcms2.h @@ -23,7 +23,7 @@ // //--------------------------------------------------------------------------------- // -// Version 2.6b +// Version 2.6 // #ifndef _lcms2_H @@ -55,6 +55,10 @@ // Uncomment to get rid of the tables for "half" float support // #define CMS_NO_HALF_SUPPORT 1 +// Uncomment to get cmsContext legacy behavior: just a void pointer passed through. +// DON'T USE UNLESS STRICTLY NECESSARY! +// #define CMS_CONTEXT_IN_LEGACY_MODE 1 + // ********** End of configuration toggles ****************************** // Needed for streams @@ -173,10 +177,6 @@ typedef int cmsBool; // Try to detect big endian platforms. This list can be endless, so only some checks are performed over here. // you can pass this toggle to the compiler by using -DCMS_USE_BIG_ENDIAN or something similar -#if defined(_HOST_BIG_ENDIAN) || defined(__BIG_ENDIAN__) || defined(WORDS_BIGENDIAN) -# define CMS_USE_BIG_ENDIAN 1 -#endif - #if defined(__sgi__) || defined(__sgi) || defined(sparc) # define CMS_USE_BIG_ENDIAN 1 #endif @@ -193,14 +193,18 @@ typedef int cmsBool; #if defined(__powerpc__) || defined(__ppc__) || defined(TARGET_CPU_PPC) # define CMS_USE_BIG_ENDIAN 1 -# if defined (__GNUC__) -# if ( __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) -# // Don't use big endian for PowerPC little endian mode +# if defined (__GNUC__) && defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) +# if __BYTE_ORDER == __LITTLE_ENDIAN +// // Don't use big endian for PowerPC little endian mode # undef CMS_USE_BIG_ENDIAN -# endif +# endif # endif #endif +// WORDS_BIGENDIAN takes precedence +#if defined(_HOST_BIG_ENDIAN) || defined(__BIG_ENDIAN__) || defined(WORDS_BIGENDIAN) +# define CMS_USE_BIG_ENDIAN 1 +#endif #ifdef macintosh # ifdef __BIG_ENDIAN__ @@ -624,7 +628,6 @@ typedef struct { // Little CMS specific typedefs -typedef void* cmsContext; // Context identifier for multithreaded environments typedef void* cmsHANDLE ; // Generic handle typedef void* cmsHPROFILE; // Opaque typedefs to hide internals typedef void* cmsHTRANSFORM; @@ -994,11 +997,29 @@ typedef struct { CMSAPI int CMSEXPORT cmsstrcasecmp(const char* s1, const char* s2); CMSAPI long int CMSEXPORT cmsfilelength(FILE* f); -// Plug-In registering --------------------------------------------------------------------------------------------------- + +// Context handling -------------------------------------------------------------------------------------------------------- + +// Each context holds its owns globals and its own plug-ins. There is a global context with the id = 0 for lecacy compatibility +// though using the global context is not recomended. Proper context handling makes lcms more thread-safe. + +#ifdef CMS_CONTEXT_IN_LEGACY_MODE + typedef void* cmsContext; +#else + typedef struct _cmsContext_struct* cmsContext; +#endif + +CMSAPI cmsContext CMSEXPORT cmsCreateContext(void* Plugin, void* UserData); +CMSAPI void CMSEXPORT cmsDeleteContext(cmsContext ContexID); +CMSAPI cmsContext CMSEXPORT cmsDupContext(cmsContext ContextID, void* NewUserData); +CMSAPI void* CMSEXPORT cmsGetContextUserData(cmsContext ContextID); + +// Plug-In registering -------------------------------------------------------------------------------------------------- CMSAPI cmsBool CMSEXPORT cmsPlugin(void* Plugin); CMSAPI cmsBool CMSEXPORT cmsPluginTHR(cmsContext ContextID, void* Plugin); CMSAPI void CMSEXPORT cmsUnregisterPlugins(void); +CMSAPI void CMSEXPORT cmsUnregisterPluginsTHR(cmsContext ContextID); // Error logging ---------------------------------------------------------------------------------------------------------- @@ -1035,6 +1056,7 @@ typedef void (* cmsLogErrorHandlerFunction)(cmsContext ContextID, cmsUInt32Numb // Allows user to set any specific logger CMSAPI void CMSEXPORT cmsSetLogErrorHandler(cmsLogErrorHandlerFunction Fn); +CMSAPI void CMSEXPORT cmsSetLogErrorHandlerTHR(cmsContext ContextID, cmsLogErrorHandlerFunction Fn); // Conversions -------------------------------------------------------------------------------------------------------------- @@ -1587,6 +1609,7 @@ CMSAPI cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransfo // Call with NULL as parameters to get the intent count CMSAPI cmsUInt32Number CMSEXPORT cmsGetSupportedIntents(cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions); +CMSAPI cmsUInt32Number CMSEXPORT cmsGetSupportedIntentsTHR(cmsContext ContextID, cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions); // Flags @@ -1698,11 +1721,22 @@ CMSAPI void CMSEXPORT cmsDoTransformStride(cmsHTRANSFORM Transform, cmsUInt32Number Stride); -CMSAPI void CMSEXPORT cmsSetAlarmCodes(cmsUInt16Number NewAlarm[cmsMAXCHANNELS]); +CMSAPI void CMSEXPORT cmsSetAlarmCodes(const cmsUInt16Number NewAlarm[cmsMAXCHANNELS]); CMSAPI void CMSEXPORT cmsGetAlarmCodes(cmsUInt16Number NewAlarm[cmsMAXCHANNELS]); + +CMSAPI void CMSEXPORT cmsSetAlarmCodesTHR(cmsContext ContextID, + const cmsUInt16Number AlarmCodes[cmsMAXCHANNELS]); +CMSAPI void CMSEXPORT cmsGetAlarmCodesTHR(cmsContext ContextID, + cmsUInt16Number AlarmCodes[cmsMAXCHANNELS]); + + + // Adaptation state for absolute colorimetric intent CMSAPI cmsFloat64Number CMSEXPORT cmsSetAdaptationState(cmsFloat64Number d); +CMSAPI cmsFloat64Number CMSEXPORT cmsSetAdaptationStateTHR(cmsContext ContextID, cmsFloat64Number d); + + // Grab the ContextID from an open transform. Returns NULL if a NULL transform is passed CMSAPI cmsContext CMSEXPORT cmsGetTransformContextID(cmsHTRANSFORM hTransform); diff --git a/include/lcms2_plugin.h b/include/lcms2_plugin.h index 10e61cb..0c95d1f 100644 --- a/include/lcms2_plugin.h +++ b/include/lcms2_plugin.h @@ -202,6 +202,7 @@ typedef void* (* _cmsDupUserDataFn)(cmsContext ContextID, const void* Data); #define cmsPluginMultiProcessElementSig 0x6D706548 // 'mpeH' #define cmsPluginOptimizationSig 0x6F707448 // 'optH' #define cmsPluginTransformSig 0x7A666D48 // 'xfmH' +#define cmsPluginMutexSig 0x6D747A48 // 'mtxH' typedef struct _cmsPluginBaseStruct { @@ -218,19 +219,28 @@ typedef struct _cmsPluginBaseStruct { //---------------------------------------------------------------------------------------------------------- // Memory handler. Each new plug-in type replaces current behaviour + +typedef void* (* _cmsMallocFnPtrType)(cmsContext ContextID, cmsUInt32Number size); +typedef void (* _cmsFreeFnPtrType)(cmsContext ContextID, void *Ptr); +typedef void* (* _cmsReallocFnPtrType)(cmsContext ContextID, void* Ptr, cmsUInt32Number NewSize); + +typedef void* (* _cmsMalloZerocFnPtrType)(cmsContext ContextID, cmsUInt32Number size); +typedef void* (* _cmsCallocFnPtrType)(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size); +typedef void* (* _cmsDupFnPtrType)(cmsContext ContextID, const void* Org, cmsUInt32Number size); + typedef struct { cmsPluginBase base; // Required - void * (* MallocPtr)(cmsContext ContextID, cmsUInt32Number size); - void (* FreePtr)(cmsContext ContextID, void *Ptr); - void * (* ReallocPtr)(cmsContext ContextID, void* Ptr, cmsUInt32Number NewSize); + _cmsMallocFnPtrType MallocPtr; + _cmsFreeFnPtrType FreePtr; + _cmsReallocFnPtrType ReallocPtr; // Optional - void * (* MallocZeroPtr)(cmsContext ContextID, cmsUInt32Number size); - void * (* CallocPtr)(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size); - void * (* DupPtr)(cmsContext ContextID, const void* Org, cmsUInt32Number size); + _cmsMalloZerocFnPtrType MallocZeroPtr; + _cmsCallocFnPtrType CallocPtr; + _cmsDupFnPtrType DupPtr; } cmsPluginMemHandler; @@ -593,6 +603,29 @@ typedef struct { } cmsPluginTransform; +//---------------------------------------------------------------------------------------------------------- +// Mutex + +typedef void* (* _cmsCreateMutexFnPtrType)(cmsContext ContextID); +typedef void (* _cmsDestroyMutexFnPtrType)(cmsContext ContextID, void* mtx); +typedef cmsBool (* _cmsLockMutexFnPtrType)(cmsContext ContextID, void* mtx); +typedef void (* _cmsUnlockMutexFnPtrType)(cmsContext ContextID, void* mtx); + +typedef struct { + cmsPluginBase base; + + _cmsCreateMutexFnPtrType CreateMutexPtr; + _cmsDestroyMutexFnPtrType DestroyMutexPtr; + _cmsLockMutexFnPtrType LockMutexPtr; + _cmsUnlockMutexFnPtrType UnlockMutexPtr; + +} cmsPluginMutex; + +CMSAPI void* CMSEXPORT _cmsCreateMutex(cmsContext ContextID); +CMSAPI void CMSEXPORT _cmsDestroyMutex(cmsContext ContextID, void* mtx); +CMSAPI cmsBool CMSEXPORT _cmsLockMutex(cmsContext ContextID, void* mtx); +CMSAPI void CMSEXPORT _cmsUnlockMutex(cmsContext ContextID, void* mtx); + #ifndef CMS_USE_CPP_API # ifdef __cplusplus diff --git a/src/cmscam02.c b/src/cmscam02.c index 2a8cf04..9d874aa 100644 --- a/src/cmscam02.c +++ b/src/cmscam02.c @@ -437,12 +437,13 @@ void CMSEXPORT cmsCIECAM02Forward(cmsHANDLE hModel, const cmsCIEXYZ* pIn, cmsJCh { CAM02COLOR clr; cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel; - - memset(&clr, 0, sizeof(clr)); + _cmsAssert(lpMod != NULL); _cmsAssert(pIn != NULL); _cmsAssert(pOut != NULL); + memset(&clr, 0, sizeof(clr)); + clr.XYZ[0] = pIn ->X; clr.XYZ[1] = pIn ->Y; clr.XYZ[2] = pIn ->Z; @@ -462,12 +463,13 @@ void CMSEXPORT cmsCIECAM02Reverse(cmsHANDLE hModel, const cmsJCh* pIn, cmsCIEXYZ { CAM02COLOR clr; cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel; - - memset(&clr, 0, sizeof(clr)); + _cmsAssert(lpMod != NULL); _cmsAssert(pIn != NULL); _cmsAssert(pOut != NULL); + memset(&clr, 0, sizeof(clr)); + clr.J = pIn -> J; clr.C = pIn -> C; clr.h = pIn -> h; diff --git a/src/cmscnvrt.c b/src/cmscnvrt.c index 4696cc8..1a93e83 100644 --- a/src/cmscnvrt.c +++ b/src/cmscnvrt.c @@ -108,15 +108,68 @@ static cmsIntentsList DefaultIntents[] = { // A pointer to the begining of the list -static cmsIntentsList *Intents = DefaultIntents; +_cmsIntentsPluginChunkType _cmsIntentsPluginChunk = { NULL }; + +// Duplicates the zone of memory used by the plug-in in the new context +static +void DupPluginIntentsList(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + _cmsIntentsPluginChunkType newHead = { NULL }; + cmsIntentsList* entry; + cmsIntentsList* Anterior = NULL; + _cmsIntentsPluginChunkType* head = (_cmsIntentsPluginChunkType*) src->chunks[IntentPlugin]; + + // Walk the list copying all nodes + for (entry = head->Intents; + entry != NULL; + entry = entry ->Next) { + + cmsIntentsList *newEntry = ( cmsIntentsList *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(cmsIntentsList)); + + if (newEntry == NULL) + return; + + // We want to keep the linked list order, so this is a little bit tricky + newEntry -> Next = NULL; + if (Anterior) + Anterior -> Next = newEntry; + + Anterior = newEntry; + + if (newHead.Intents == NULL) + newHead.Intents = newEntry; + } + + ctx ->chunks[IntentPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsIntentsPluginChunkType)); +} + +void _cmsAllocIntentsPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + if (src != NULL) { + + // Copy all linked list + DupPluginIntentsList(ctx, src); + } + else { + static _cmsIntentsPluginChunkType IntentsPluginChunkType = { NULL }; + ctx ->chunks[IntentPlugin] = _cmsSubAllocDup(ctx ->MemPool, &IntentsPluginChunkType, sizeof(_cmsIntentsPluginChunkType)); + } +} + // Search the list for a suitable intent. Returns NULL if not found static -cmsIntentsList* SearchIntent(cmsUInt32Number Intent) +cmsIntentsList* SearchIntent(cmsContext ContextID, cmsUInt32Number Intent) { + _cmsIntentsPluginChunkType* ctx = ( _cmsIntentsPluginChunkType*) _cmsContextGetClientChunk(ContextID, IntentPlugin); cmsIntentsList* pt; - for (pt = Intents; pt != NULL; pt = pt -> Next) + for (pt = ctx -> Intents; pt != NULL; pt = pt -> Next) + if (pt ->Intent == Intent) return pt; + + for (pt = DefaultIntents; pt != NULL; pt = pt -> Next) if (pt ->Intent == Intent) return pt; return NULL; @@ -1002,7 +1055,7 @@ cmsPipeline* _cmsLinkProfiles(cmsContext ContextID, // this case would present some issues if the custom intent tries to do things like // preserve primaries. This solution is not perfect, but works well on most cases. - Intent = SearchIntent(TheIntents[0]); + Intent = SearchIntent(ContextID, TheIntents[0]); if (Intent == NULL) { cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported intent '%d'", TheIntents[0]); return NULL; @@ -1017,12 +1070,14 @@ cmsPipeline* _cmsLinkProfiles(cmsContext ContextID, // Get information about available intents. nMax is the maximum space for the supplied "Codes" // and "Descriptions" the function returns the total number of intents, which may be greater // than nMax, although the matrices are not populated beyond this level. -cmsUInt32Number CMSEXPORT cmsGetSupportedIntents(cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions) +cmsUInt32Number CMSEXPORT cmsGetSupportedIntentsTHR(cmsContext ContextID, cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions) { + _cmsIntentsPluginChunkType* ctx = ( _cmsIntentsPluginChunkType*) _cmsContextGetClientChunk(ContextID, IntentPlugin); cmsIntentsList* pt; cmsUInt32Number nIntents; - for (nIntents=0, pt = Intents; pt != NULL; pt = pt -> Next) + + for (nIntents=0, pt = ctx->Intents; pt != NULL; pt = pt -> Next) { if (nIntents < nMax) { if (Codes != NULL) @@ -1035,37 +1090,52 @@ cmsUInt32Number CMSEXPORT cmsGetSupportedIntents(cmsUInt32Number nMax, cmsUInt32 nIntents++; } + for (nIntents=0, pt = DefaultIntents; pt != NULL; pt = pt -> Next) + { + if (nIntents < nMax) { + if (Codes != NULL) + Codes[nIntents] = pt ->Intent; + + if (Descriptions != NULL) + Descriptions[nIntents] = pt ->Description; + } + + nIntents++; + } return nIntents; } +cmsUInt32Number CMSEXPORT cmsGetSupportedIntents(cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions) +{ + return cmsGetSupportedIntentsTHR(NULL, nMax, Codes, Descriptions); +} + // The plug-in registration. User can add new intents or override default routines cmsBool _cmsRegisterRenderingIntentPlugin(cmsContext id, cmsPluginBase* Data) { + _cmsIntentsPluginChunkType* ctx = ( _cmsIntentsPluginChunkType*) _cmsContextGetClientChunk(id, IntentPlugin); cmsPluginRenderingIntent* Plugin = (cmsPluginRenderingIntent*) Data; cmsIntentsList* fl; - // Do we have to reset the intents? + // Do we have to reset the custom intents? if (Data == NULL) { - Intents = DefaultIntents; - return TRUE; + ctx->Intents = NULL; + return TRUE; } - fl = SearchIntent(Plugin ->Intent); + fl = (cmsIntentsList*) _cmsPluginMalloc(id, sizeof(cmsIntentsList)); + if (fl == NULL) return FALSE; - if (fl == NULL) { - fl = (cmsIntentsList*) _cmsPluginMalloc(id, sizeof(cmsIntentsList)); - if (fl == NULL) return FALSE; - } fl ->Intent = Plugin ->Intent; - strncpy(fl ->Description, Plugin ->Description, 255); - fl ->Description[255] = 0; + strncpy(fl ->Description, Plugin ->Description, sizeof(fl ->Description)-1); + fl ->Description[sizeof(fl ->Description)-1] = 0; fl ->Link = Plugin ->Link; - fl ->Next = Intents; - Intents = fl; + fl ->Next = ctx ->Intents; + ctx ->Intents = fl; return TRUE; } diff --git a/src/cmserr.c b/src/cmserr.c index 166793f..4bfc0a7 100644 --- a/src/cmserr.c +++ b/src/cmserr.c @@ -31,13 +31,14 @@ // compare two strings ignoring case int CMSEXPORT cmsstrcasecmp(const char* s1, const char* s2) { - register const unsigned char *us1 = (const unsigned char *)s1, - *us2 = (const unsigned char *)s2; + register const unsigned char *us1 = (const unsigned char *)s1, + *us2 = (const unsigned char *)s2; - while (toupper(*us1) == toupper(*us2++)) - if (*us1++ == '\0') - return (0); - return (toupper(*us1) - toupper(*--us2)); + while (toupper(*us1) == toupper(*us2++)) + if (*us1++ == '\0') + return 0; + + return (toupper(*us1) - toupper(*--us2)); } // long int because C99 specifies ftell in such way (7.19.9.2) @@ -62,9 +63,8 @@ long int CMSEXPORT cmsfilelength(FILE* f) // // This is the interface to low-level memory management routines. By default a simple // wrapping to malloc/free/realloc is provided, although there is a limit on the max -// amount of memoy that can be reclaimed. This is mostly as a safety feature to -// prevent bogus or malintentionated code to allocate huge blocks that otherwise lcms -// would never need. +// amount of memoy that can be reclaimed. This is mostly as a safety feature to prevent +// bogus or evil code to allocate huge blocks that otherwise lcms would never need. #define MAX_MEMORY_FOR_ALLOC ((cmsUInt32Number)(1024U*1024U*512U)) @@ -74,7 +74,7 @@ long int CMSEXPORT cmsfilelength(FILE* f) // required to be implemented: malloc, realloc and free, although the user may want to // replace the optional mallocZero, calloc and dup as well. -cmsBool _cmsRegisterMemHandlerPlugin(cmsPluginBase* Plugin); +cmsBool _cmsRegisterMemHandlerPlugin(cmsContext ContextID, cmsPluginBase* Plugin); // ********************************************************************************* @@ -114,7 +114,7 @@ void _cmsFreeDefaultFn(cmsContext ContextID, void *Ptr) cmsUNUSED_PARAMETER(ContextID); } -// The default realloc function. Again it check for exploits. If Ptr is NULL, +// The default realloc function. Again it checks for exploits. If Ptr is NULL, // realloc behaves the same way as malloc and allocates a new block of size bytes. static void* _cmsReallocDefaultFn(cmsContext ContextID, void* Ptr, cmsUInt32Number size) @@ -167,28 +167,73 @@ void* _cmsDupDefaultFn(cmsContext ContextID, const void* Org, cmsUInt32Number si return mem; } -// Pointers to malloc and _cmsFree functions in current environment -static void * (* MallocPtr)(cmsContext ContextID, cmsUInt32Number size) = _cmsMallocDefaultFn; -static void * (* MallocZeroPtr)(cmsContext ContextID, cmsUInt32Number size) = _cmsMallocZeroDefaultFn; -static void (* FreePtr)(cmsContext ContextID, void *Ptr) = _cmsFreeDefaultFn; -static void * (* ReallocPtr)(cmsContext ContextID, void *Ptr, cmsUInt32Number NewSize) = _cmsReallocDefaultFn; -static void * (* CallocPtr)(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size)= _cmsCallocDefaultFn; -static void * (* DupPtr)(cmsContext ContextID, const void* Org, cmsUInt32Number size) = _cmsDupDefaultFn; + +// Pointers to memory manager functions in Context0 +_cmsMemPluginChunkType _cmsMemPluginChunk = { _cmsMallocDefaultFn, _cmsMallocZeroDefaultFn, _cmsFreeDefaultFn, + _cmsReallocDefaultFn, _cmsCallocDefaultFn, _cmsDupDefaultFn + }; + + +// Reset and duplicate memory manager +void _cmsAllocMemPluginChunk(struct _cmsContext_struct* ctx, const struct _cmsContext_struct* src) +{ + _cmsAssert(ctx != NULL); + + if (src != NULL) { + + // Duplicate + ctx ->chunks[MemPlugin] = _cmsSubAllocDup(ctx ->MemPool, src ->chunks[MemPlugin], sizeof(_cmsMemPluginChunkType)); + } + else { + + // To reset it, we use the default allocators, which cannot be overriden + ctx ->chunks[MemPlugin] = &ctx ->DefaultMemoryManager; + } +} + +// Auxiliar to fill memory management functions from plugin (or context 0 defaults) +void _cmsInstallAllocFunctions(cmsPluginMemHandler* Plugin, _cmsMemPluginChunkType* ptr) +{ + if (Plugin == NULL) { + + memcpy(ptr, &_cmsMemPluginChunk, sizeof(_cmsMemPluginChunk)); + } + else { + + ptr ->MallocPtr = Plugin -> MallocPtr; + ptr ->FreePtr = Plugin -> FreePtr; + ptr ->ReallocPtr = Plugin -> ReallocPtr; + + // Make sure we revert to defaults + ptr ->MallocZeroPtr= _cmsMallocZeroDefaultFn; + ptr ->CallocPtr = _cmsCallocDefaultFn; + ptr ->DupPtr = _cmsDupDefaultFn; + + if (Plugin ->MallocZeroPtr != NULL) ptr ->MallocZeroPtr = Plugin -> MallocZeroPtr; + if (Plugin ->CallocPtr != NULL) ptr ->CallocPtr = Plugin -> CallocPtr; + if (Plugin ->DupPtr != NULL) ptr ->DupPtr = Plugin -> DupPtr; + + } +} + // Plug-in replacement entry -cmsBool _cmsRegisterMemHandlerPlugin(cmsPluginBase *Data) +cmsBool _cmsRegisterMemHandlerPlugin(cmsContext ContextID, cmsPluginBase *Data) { - cmsPluginMemHandler* Plugin = (cmsPluginMemHandler*) Data; + cmsPluginMemHandler* Plugin = (cmsPluginMemHandler*) Data; + _cmsMemPluginChunkType* ptr; - // NULL forces to reset to defaults + // NULL forces to reset to defaults. In this special case, the defaults are stored in the context structure. + // Remaining plug-ins does NOT have any copy in the context structure, but this is somehow special as the + // context internal data should be malloce'd by using those functions. if (Data == NULL) { - MallocPtr = _cmsMallocDefaultFn; - MallocZeroPtr= _cmsMallocZeroDefaultFn; - FreePtr = _cmsFreeDefaultFn; - ReallocPtr = _cmsReallocDefaultFn; - CallocPtr = _cmsCallocDefaultFn; - DupPtr = _cmsDupDefaultFn; + struct _cmsContext_struct* ctx = ( struct _cmsContext_struct*) ContextID; + + // Return to the default allocators + if (ContextID != NULL) { + ctx->chunks[MemPlugin] = (void*) &ctx->DefaultMemoryManager; + } return TRUE; } @@ -198,51 +243,56 @@ cmsBool _cmsRegisterMemHandlerPlugin(cmsPluginBase *Data) Plugin -> ReallocPtr == NULL) return FALSE; // Set replacement functions - MallocPtr = Plugin -> MallocPtr; - FreePtr = Plugin -> FreePtr; - ReallocPtr = Plugin -> ReallocPtr; - - if (Plugin ->MallocZeroPtr != NULL) MallocZeroPtr = Plugin ->MallocZeroPtr; - if (Plugin ->CallocPtr != NULL) CallocPtr = Plugin -> CallocPtr; - if (Plugin ->DupPtr != NULL) DupPtr = Plugin -> DupPtr; + ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin); + if (ptr == NULL) + return FALSE; + _cmsInstallAllocFunctions(Plugin, ptr); return TRUE; } // Generic allocate void* CMSEXPORT _cmsMalloc(cmsContext ContextID, cmsUInt32Number size) { - return MallocPtr(ContextID, size); + _cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin); + return ptr ->MallocPtr(ContextID, size); } // Generic allocate & zero void* CMSEXPORT _cmsMallocZero(cmsContext ContextID, cmsUInt32Number size) { - return MallocZeroPtr(ContextID, size); + _cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin); + return ptr->MallocZeroPtr(ContextID, size); } // Generic calloc void* CMSEXPORT _cmsCalloc(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size) { - return CallocPtr(ContextID, num, size); + _cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin); + return ptr->CallocPtr(ContextID, num, size); } // Generic reallocate void* CMSEXPORT _cmsRealloc(cmsContext ContextID, void* Ptr, cmsUInt32Number size) { - return ReallocPtr(ContextID, Ptr, size); + _cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin); + return ptr->ReallocPtr(ContextID, Ptr, size); } // Generic free memory void CMSEXPORT _cmsFree(cmsContext ContextID, void* Ptr) { - if (Ptr != NULL) FreePtr(ContextID, Ptr); + if (Ptr != NULL) { + _cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin); + ptr ->FreePtr(ContextID, Ptr); + } } // Generic block duplication void* CMSEXPORT _cmsDupMem(cmsContext ContextID, const void* Org, cmsUInt32Number size) { - return DupPtr(ContextID, Org, size); + _cmsMemPluginChunkType* ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin); + return ptr ->DupPtr(ContextID, Org, size); } // ******************************************************************************************** @@ -351,6 +401,26 @@ void* _cmsSubAlloc(_cmsSubAllocator* sub, cmsUInt32Number size) return (void*) ptr; } +// Duplicate in pool +void* _cmsSubAllocDup(_cmsSubAllocator* s, const void *ptr, cmsUInt32Number size) +{ + void *NewPtr; + + // Dup of null pointer is also NULL + if (ptr == NULL) + return NULL; + + NewPtr = _cmsSubAlloc(s, size); + + if (ptr != NULL && NewPtr != NULL) { + memcpy(NewPtr, ptr, size); + } + + return NewPtr; +} + + + // Error logging ****************************************************************** // There is no error handling at all. When a funtion fails, it returns proper value. @@ -372,8 +442,26 @@ void* _cmsSubAlloc(_cmsSubAllocator* sub, cmsUInt32Number size) // This is our default log error static void DefaultLogErrorHandlerFunction(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text); -// The current handler in actual environment -static cmsLogErrorHandlerFunction LogErrorHandler = DefaultLogErrorHandlerFunction; +// Context0 storage, which is global +_cmsLogErrorChunkType _cmsLogErrorChunk = { DefaultLogErrorHandlerFunction }; + +// Allocates and inits error logger container for a given context. If src is NULL, only initializes the value +// to the default. Otherwise, it duplicates the value. The interface is standard across all context clients +void _cmsAllocLogErrorChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + static _cmsLogErrorChunkType LogErrorChunk = { DefaultLogErrorHandlerFunction }; + void* from; + + if (src != NULL) { + from = src ->chunks[Logger]; + } + else { + from = &LogErrorChunk; + } + + ctx ->chunks[Logger] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsLogErrorChunkType)); +} // The default error logger does nothing. static @@ -387,13 +475,24 @@ void DefaultLogErrorHandlerFunction(cmsContext ContextID, cmsUInt32Number ErrorC cmsUNUSED_PARAMETER(Text); } -// Change log error +// Change log error, context based +void CMSEXPORT cmsSetLogErrorHandlerTHR(cmsContext ContextID, cmsLogErrorHandlerFunction Fn) +{ + _cmsLogErrorChunkType* lhg = (_cmsLogErrorChunkType*) _cmsContextGetClientChunk(ContextID, Logger); + + if (lhg != NULL) { + + if (Fn == NULL) + lhg -> LogErrorHandler = DefaultLogErrorHandlerFunction; + else + lhg -> LogErrorHandler = Fn; + } +} + +// Change log error, legacy void CMSEXPORT cmsSetLogErrorHandler(cmsLogErrorHandlerFunction Fn) { - if (Fn == NULL) - LogErrorHandler = DefaultLogErrorHandlerFunction; - else - LogErrorHandler = Fn; + cmsSetLogErrorHandlerTHR(NULL, Fn); } // Log an error @@ -402,13 +501,18 @@ void CMSEXPORT cmsSignalError(cmsContext ContextID, cmsUInt32Number ErrorCode, c { va_list args; char Buffer[MAX_ERROR_MESSAGE_LEN]; + _cmsLogErrorChunkType* lhg; + va_start(args, ErrorText); vsnprintf(Buffer, MAX_ERROR_MESSAGE_LEN-1, ErrorText, args); va_end(args); - // Call handler - LogErrorHandler(ContextID, ErrorCode, Buffer); + // Check for the context, if specified go there. If not, go for the global + lhg = (_cmsLogErrorChunkType*) _cmsContextGetClientChunk(ContextID, Logger); + if (lhg ->LogErrorHandler) { + lhg ->LogErrorHandler(ContextID, ErrorCode, Buffer); + } } // Utility function to print signatures @@ -426,3 +530,93 @@ void _cmsTagSignature2String(char String[5], cmsTagSignature sig) String[4] = 0; } +//-------------------------------------------------------------------------------------------------- + +// Pointers to memory manager functions in Context0 +_cmsMutexPluginChunkType _cmsMutexPluginChunk = { NULL, NULL, NULL, NULL }; + +// Allocate and init mutex container. +void _cmsAllocMutexPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + static _cmsMutexPluginChunkType MutexChunk = { NULL, NULL, NULL, NULL }; + void* from; + + if (src != NULL) { + from = src ->chunks[MutexPlugin]; + } + else { + from = &MutexChunk; + } + + ctx ->chunks[MutexPlugin] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsMutexPluginChunkType)); +} + +// Register new ways to transform +cmsBool _cmsRegisterMutexPlugin(cmsContext ContextID, cmsPluginBase* Data) +{ + cmsPluginMutex* Plugin = (cmsPluginMutex*) Data; + _cmsMutexPluginChunkType* ctx = ( _cmsMutexPluginChunkType*) _cmsContextGetClientChunk(ContextID, MutexPlugin); + + if (Data == NULL) { + + // No lock routines + ctx->CreateMutexPtr = NULL; + ctx->DestroyMutexPtr = NULL; + ctx->LockMutexPtr = NULL; + ctx ->UnlockMutexPtr = NULL; + return TRUE; + } + + // Factory callback is required + if (Plugin ->CreateMutexPtr == NULL || Plugin ->DestroyMutexPtr == NULL || + Plugin ->LockMutexPtr == NULL || Plugin ->UnlockMutexPtr == NULL) return FALSE; + + + ctx->CreateMutexPtr = Plugin->CreateMutexPtr; + ctx->DestroyMutexPtr = Plugin ->DestroyMutexPtr; + ctx ->LockMutexPtr = Plugin ->LockMutexPtr; + ctx ->UnlockMutexPtr = Plugin ->UnlockMutexPtr; + + // All is ok + return TRUE; +} + +// Generic Mutex fns +void* CMSEXPORT _cmsCreateMutex(cmsContext ContextID) +{ + _cmsMutexPluginChunkType* ptr = (_cmsMutexPluginChunkType*) _cmsContextGetClientChunk(ContextID, MutexPlugin); + + if (ptr ->CreateMutexPtr == NULL) return NULL; + + return ptr ->CreateMutexPtr(ContextID); +} + +void CMSEXPORT _cmsDestroyMutex(cmsContext ContextID, void* mtx) +{ + _cmsMutexPluginChunkType* ptr = (_cmsMutexPluginChunkType*) _cmsContextGetClientChunk(ContextID, MutexPlugin); + + if (ptr ->DestroyMutexPtr != NULL) { + + ptr ->DestroyMutexPtr(ContextID, mtx); + } +} + +cmsBool CMSEXPORT _cmsLockMutex(cmsContext ContextID, void* mtx) +{ + _cmsMutexPluginChunkType* ptr = (_cmsMutexPluginChunkType*) _cmsContextGetClientChunk(ContextID, MutexPlugin); + + if (ptr ->LockMutexPtr == NULL) return TRUE; + + return ptr ->LockMutexPtr(ContextID, mtx); +} + +void CMSEXPORT _cmsUnlockMutex(cmsContext ContextID, void* mtx) +{ + _cmsMutexPluginChunkType* ptr = (_cmsMutexPluginChunkType*) _cmsContextGetClientChunk(ContextID, MutexPlugin); + + if (ptr ->UnlockMutexPtr != NULL) { + + ptr ->UnlockMutexPtr(ContextID, mtx); + } +} diff --git a/src/cmsgamma.c b/src/cmsgamma.c index e153103..7869166 100644 --- a/src/cmsgamma.c +++ b/src/cmsgamma.c @@ -53,7 +53,6 @@ typedef struct _cmsParametricCurvesCollection_st { } _cmsParametricCurvesCollection; - // This is the default (built-in) evaluator static cmsFloat64Number DefaultEvalParametricFn(cmsInt32Number Type, const cmsFloat64Number Params[], cmsFloat64Number R); @@ -66,22 +65,77 @@ static _cmsParametricCurvesCollection DefaultCurves = { NULL // Next in chain }; +// Duplicates the zone of memory used by the plug-in in the new context +static +void DupPluginCurvesList(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + _cmsCurvesPluginChunkType newHead = { NULL }; + _cmsParametricCurvesCollection* entry; + _cmsParametricCurvesCollection* Anterior = NULL; + _cmsCurvesPluginChunkType* head = (_cmsCurvesPluginChunkType*) src->chunks[CurvesPlugin]; + + _cmsAssert(head != NULL); + + // Walk the list copying all nodes + for (entry = head->ParametricCurves; + entry != NULL; + entry = entry ->Next) { + + _cmsParametricCurvesCollection *newEntry = ( _cmsParametricCurvesCollection *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsParametricCurvesCollection)); + + if (newEntry == NULL) + return; + + // We want to keep the linked list order, so this is a little bit tricky + newEntry -> Next = NULL; + if (Anterior) + Anterior -> Next = newEntry; + + Anterior = newEntry; + + if (newHead.ParametricCurves == NULL) + newHead.ParametricCurves = newEntry; + } + + ctx ->chunks[CurvesPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsCurvesPluginChunkType)); +} + +// The allocator have to follow the chain +void _cmsAllocCurvesPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + _cmsAssert(ctx != NULL); + + if (src != NULL) { + + // Copy all linked list + DupPluginCurvesList(ctx, src); + } + else { + static _cmsCurvesPluginChunkType CurvesPluginChunk = { NULL }; + ctx ->chunks[CurvesPlugin] = _cmsSubAllocDup(ctx ->MemPool, &CurvesPluginChunk, sizeof(_cmsCurvesPluginChunkType)); + } +} + + // The linked list head -static _cmsParametricCurvesCollection* ParametricCurves = &DefaultCurves; +_cmsCurvesPluginChunkType _cmsCurvesPluginChunk = { NULL }; // As a way to install new parametric curves -cmsBool _cmsRegisterParametricCurvesPlugin(cmsContext id, cmsPluginBase* Data) +cmsBool _cmsRegisterParametricCurvesPlugin(cmsContext ContextID, cmsPluginBase* Data) { + _cmsCurvesPluginChunkType* ctx = ( _cmsCurvesPluginChunkType*) _cmsContextGetClientChunk(ContextID, CurvesPlugin); cmsPluginParametricCurves* Plugin = (cmsPluginParametricCurves*) Data; _cmsParametricCurvesCollection* fl; if (Data == NULL) { - ParametricCurves = &DefaultCurves; + ctx -> ParametricCurves = NULL; return TRUE; } - fl = (_cmsParametricCurvesCollection*) _cmsPluginMalloc(id, sizeof(_cmsParametricCurvesCollection)); + fl = (_cmsParametricCurvesCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsParametricCurvesCollection)); if (fl == NULL) return FALSE; // Copy the parameters @@ -97,8 +151,8 @@ cmsBool _cmsRegisterParametricCurvesPlugin(cmsContext id, cmsPluginBase* Data) memmove(fl->ParameterCount, Plugin ->ParameterCount, fl->nFunctions * sizeof(cmsUInt32Number)); // Keep linked list - fl ->Next = ParametricCurves; - ParametricCurves = fl; + fl ->Next = ctx->ParametricCurves; + ctx->ParametricCurves = fl; // All is ok return TRUE; @@ -120,12 +174,24 @@ int IsInSet(int Type, _cmsParametricCurvesCollection* c) // Search for the collection which contains a specific type static -_cmsParametricCurvesCollection *GetParametricCurveByType(int Type, int* index) +_cmsParametricCurvesCollection *GetParametricCurveByType(cmsContext ContextID, int Type, int* index) { _cmsParametricCurvesCollection* c; int Position; + _cmsCurvesPluginChunkType* ctx = ( _cmsCurvesPluginChunkType*) _cmsContextGetClientChunk(ContextID, CurvesPlugin); + + for (c = ctx->ParametricCurves; c != NULL; c = c ->Next) { + + Position = IsInSet(Type, c); - for (c = ParametricCurves; c != NULL; c = c ->Next) { + if (Position != -1) { + if (index != NULL) + *index = Position; + return c; + } + } + // If none found, revert for defaults + for (c = &DefaultCurves; c != NULL; c = c ->Next) { Position = IsInSet(Type, c); @@ -222,7 +288,7 @@ cmsToneCurve* AllocateToneCurveStruct(cmsContext ContextID, cmsInt32Number nEntr p ->Segments[i].SampledPoints = NULL; - c = GetParametricCurveByType(Segments[i].Type, NULL); + c = GetParametricCurveByType(ContextID, Segments[i].Type, NULL); if (c != NULL) p ->Evals[i] = c ->Evaluator; } @@ -648,12 +714,12 @@ cmsToneCurve* CMSEXPORT cmsBuildParametricToneCurve(cmsContext ContextID, cmsInt cmsCurveSegment Seg0; int Pos = 0; cmsUInt32Number size; - _cmsParametricCurvesCollection* c = GetParametricCurveByType(Type, &Pos); + _cmsParametricCurvesCollection* c = GetParametricCurveByType(ContextID, Type, &Pos); _cmsAssert(Params != NULL); if (c == NULL) { - cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Invalid parametric curve type %d", Type); + cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Invalid parametric curve type %d", Type); return NULL; } @@ -843,7 +909,10 @@ cmsToneCurve* CMSEXPORT cmsReverseToneCurveEx(cmsInt32Number nResultSamples, con _cmsAssert(InCurve != NULL); // Try to reverse it analytically whatever possible - if (InCurve ->nSegments == 1 && InCurve ->Segments[0].Type > 0 && InCurve -> Segments[0].Type <= 5) { + + if (InCurve ->nSegments == 1 && InCurve ->Segments[0].Type > 0 && + /* InCurve -> Segments[0].Type <= 5 */ + GetParametricCurveByType(InCurve ->InterpParams->ContextID, InCurve ->Segments[0].Type, NULL) != NULL) { return cmsBuildParametricToneCurve(InCurve ->InterpParams->ContextID, -(InCurve -> Segments[0].Type), diff --git a/src/cmsgmt.c b/src/cmsgmt.c index 2966f0c..0942765 100644 --- a/src/cmsgmt.c +++ b/src/cmsgmt.c @@ -1,590 +1,590 @@ -//--------------------------------------------------------------------------------- -// -// Little Color Management System -// Copyright (c) 1998-2012 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. -// -//--------------------------------------------------------------------------------- -// - -#include "lcms2_internal.h" - - -// Auxiliar: append a Lab identity after the given sequence of profiles -// and return the transform. Lab profile is closed, rest of profiles are kept open. -cmsHTRANSFORM _cmsChain2Lab(cmsContext ContextID, - cmsUInt32Number nProfiles, - cmsUInt32Number InputFormat, - cmsUInt32Number OutputFormat, - const cmsUInt32Number Intents[], - const cmsHPROFILE hProfiles[], - const cmsBool BPC[], - const cmsFloat64Number AdaptationStates[], - cmsUInt32Number dwFlags) -{ - cmsHTRANSFORM xform; - cmsHPROFILE hLab; - cmsHPROFILE ProfileList[256]; - cmsBool BPCList[256]; - cmsFloat64Number AdaptationList[256]; - cmsUInt32Number IntentList[256]; - cmsUInt32Number i; - - // This is a rather big number and there is no need of dynamic memory - // since we are adding a profile, 254 + 1 = 255 and this is the limit - if (nProfiles > 254) return NULL; - - // The output space - hLab = cmsCreateLab4ProfileTHR(ContextID, NULL); - if (hLab == NULL) return NULL; - - // Create a copy of parameters - for (i=0; i < nProfiles; i++) { - - ProfileList[i] = hProfiles[i]; - BPCList[i] = BPC[i]; - AdaptationList[i] = AdaptationStates[i]; - IntentList[i] = Intents[i]; - } - - // Place Lab identity at chain's end. - ProfileList[nProfiles] = hLab; - BPCList[nProfiles] = 0; - AdaptationList[nProfiles] = 1.0; - IntentList[nProfiles] = INTENT_RELATIVE_COLORIMETRIC; - - // Create the transform - xform = cmsCreateExtendedTransform(ContextID, nProfiles + 1, ProfileList, - BPCList, - IntentList, - AdaptationList, - NULL, 0, - InputFormat, - OutputFormat, - dwFlags); - - cmsCloseProfile(hLab); - - return xform; -} - - -// Compute K -> L* relationship. Flags may include black point compensation. In this case, -// the relationship is assumed from the profile with BPC to a black point zero. -static -cmsToneCurve* ComputeKToLstar(cmsContext ContextID, - cmsUInt32Number nPoints, - cmsUInt32Number nProfiles, - const cmsUInt32Number Intents[], - const cmsHPROFILE hProfiles[], - const cmsBool BPC[], - const cmsFloat64Number AdaptationStates[], - cmsUInt32Number dwFlags) -{ - cmsToneCurve* out = NULL; - cmsUInt32Number i; - cmsHTRANSFORM xform; - cmsCIELab Lab; - cmsFloat32Number cmyk[4]; - cmsFloat32Number* SampledPoints; - - xform = _cmsChain2Lab(ContextID, nProfiles, TYPE_CMYK_FLT, TYPE_Lab_DBL, Intents, hProfiles, BPC, AdaptationStates, dwFlags); - if (xform == NULL) return NULL; - - SampledPoints = (cmsFloat32Number*) _cmsCalloc(ContextID, nPoints, sizeof(cmsFloat32Number)); - if (SampledPoints == NULL) goto Error; - - for (i=0; i < nPoints; i++) { - - cmyk[0] = 0; - cmyk[1] = 0; - cmyk[2] = 0; - cmyk[3] = (cmsFloat32Number) ((i * 100.0) / (nPoints-1)); - - cmsDoTransform(xform, cmyk, &Lab, 1); - SampledPoints[i]= (cmsFloat32Number) (1.0 - Lab.L / 100.0); // Negate K for easier operation - } - - out = cmsBuildTabulatedToneCurveFloat(ContextID, nPoints, SampledPoints); - -Error: - - cmsDeleteTransform(xform); - if (SampledPoints) _cmsFree(ContextID, SampledPoints); - - return out; -} - - -// Compute Black tone curve on a CMYK -> CMYK transform. This is done by -// using the proof direction on both profiles to find K->L* relationship -// then joining both curves. dwFlags may include black point compensation. -cmsToneCurve* _cmsBuildKToneCurve(cmsContext ContextID, - cmsUInt32Number nPoints, - cmsUInt32Number nProfiles, - const cmsUInt32Number Intents[], - const cmsHPROFILE hProfiles[], - const cmsBool BPC[], - const cmsFloat64Number AdaptationStates[], - cmsUInt32Number dwFlags) -{ - cmsToneCurve *in, *out, *KTone; - - // Make sure CMYK -> CMYK - if (cmsGetColorSpace(hProfiles[0]) != cmsSigCmykData || - cmsGetColorSpace(hProfiles[nProfiles-1])!= cmsSigCmykData) return NULL; - - - // Make sure last is an output profile - if (cmsGetDeviceClass(hProfiles[nProfiles - 1]) != cmsSigOutputClass) return NULL; - - // Create individual curves. BPC works also as each K to L* is - // computed as a BPC to zero black point in case of L* - in = ComputeKToLstar(ContextID, nPoints, nProfiles - 1, Intents, hProfiles, BPC, AdaptationStates, dwFlags); - if (in == NULL) return NULL; - - out = ComputeKToLstar(ContextID, nPoints, 1, - Intents + (nProfiles - 1), - hProfiles + (nProfiles - 1), - BPC + (nProfiles - 1), - AdaptationStates + (nProfiles - 1), - dwFlags); - if (out == NULL) { - cmsFreeToneCurve(in); - return NULL; - } - - // Build the relationship. This effectively limits the maximum accuracy to 16 bits, but - // since this is used on black-preserving LUTs, we are not loosing accuracy in any case - KTone = cmsJoinToneCurve(ContextID, in, out, nPoints); - - // Get rid of components - cmsFreeToneCurve(in); cmsFreeToneCurve(out); - - // Something went wrong... - if (KTone == NULL) return NULL; - - // Make sure it is monotonic - if (!cmsIsToneCurveMonotonic(KTone)) { - cmsFreeToneCurve(KTone); - return NULL; - } - - return KTone; -} - - -// Gamut LUT Creation ----------------------------------------------------------------------------------------- - -// Used by gamut & softproofing - -typedef struct { - - cmsHTRANSFORM hInput; // From whatever input color space. 16 bits to DBL - cmsHTRANSFORM hForward, hReverse; // Transforms going from Lab to colorant and back - cmsFloat64Number Thereshold; // The thereshold after which is considered out of gamut - - } GAMUTCHAIN; - -// This sampler does compute gamut boundaries by comparing original -// values with a transform going back and forth. Values above ERR_THERESHOLD -// of maximum are considered out of gamut. - -#define ERR_THERESHOLD 5 - - -static -int GamutSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo) -{ - GAMUTCHAIN* t = (GAMUTCHAIN* ) Cargo; - cmsCIELab LabIn1, LabOut1; - cmsCIELab LabIn2, LabOut2; - cmsUInt16Number Proof[cmsMAXCHANNELS], Proof2[cmsMAXCHANNELS]; - cmsFloat64Number dE1, dE2, ErrorRatio; - - // Assume in-gamut by default. - ErrorRatio = 1.0; - - // Convert input to Lab - cmsDoTransform(t -> hInput, In, &LabIn1, 1); - - // converts from PCS to colorant. This always - // does return in-gamut values, - cmsDoTransform(t -> hForward, &LabIn1, Proof, 1); - - // Now, do the inverse, from colorant to PCS. - cmsDoTransform(t -> hReverse, Proof, &LabOut1, 1); - - memmove(&LabIn2, &LabOut1, sizeof(cmsCIELab)); - - // Try again, but this time taking Check as input - cmsDoTransform(t -> hForward, &LabOut1, Proof2, 1); - cmsDoTransform(t -> hReverse, Proof2, &LabOut2, 1); - - // Take difference of direct value - dE1 = cmsDeltaE(&LabIn1, &LabOut1); - - // Take difference of converted value - dE2 = cmsDeltaE(&LabIn2, &LabOut2); - - - // if dE1 is small and dE2 is small, value is likely to be in gamut - if (dE1 < t->Thereshold && dE2 < t->Thereshold) - Out[0] = 0; - else { - - // if dE1 is small and dE2 is big, undefined. Assume in gamut - if (dE1 < t->Thereshold && dE2 > t->Thereshold) - Out[0] = 0; - else - // dE1 is big and dE2 is small, clearly out of gamut - if (dE1 > t->Thereshold && dE2 < t->Thereshold) - Out[0] = (cmsUInt16Number) _cmsQuickFloor((dE1 - t->Thereshold) + .5); - else { - - // dE1 is big and dE2 is also big, could be due to perceptual mapping - // so take error ratio - if (dE2 == 0.0) - ErrorRatio = dE1; - else - ErrorRatio = dE1 / dE2; - - if (ErrorRatio > t->Thereshold) - Out[0] = (cmsUInt16Number) _cmsQuickFloor((ErrorRatio - t->Thereshold) + .5); - else - Out[0] = 0; - } - } - - - return TRUE; -} - -// Does compute a gamut LUT going back and forth across pcs -> relativ. colorimetric intent -> pcs -// the dE obtained is then annotated on the LUT. Values truely out of gamut are clipped to dE = 0xFFFE -// and values changed are supposed to be handled by any gamut remapping, so, are out of gamut as well. -// -// **WARNING: This algorithm does assume that gamut remapping algorithms does NOT move in-gamut colors, -// of course, many perceptual and saturation intents does not work in such way, but relativ. ones should. - -cmsPipeline* _cmsCreateGamutCheckPipeline(cmsContext ContextID, - cmsHPROFILE hProfiles[], - cmsBool BPC[], - cmsUInt32Number Intents[], - cmsFloat64Number AdaptationStates[], - cmsUInt32Number nGamutPCSposition, - cmsHPROFILE hGamut) -{ - cmsHPROFILE hLab; - cmsPipeline* Gamut; - cmsStage* CLUT; - cmsUInt32Number dwFormat; - GAMUTCHAIN Chain; - int nChannels, nGridpoints; - cmsColorSpaceSignature ColorSpace; - cmsUInt32Number i; - cmsHPROFILE ProfileList[256]; - cmsBool BPCList[256]; - cmsFloat64Number AdaptationList[256]; - cmsUInt32Number IntentList[256]; - - memset(&Chain, 0, sizeof(GAMUTCHAIN)); - - - if (nGamutPCSposition <= 0 || nGamutPCSposition > 255) { - cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong position of PCS. 1..255 expected, %d found.", nGamutPCSposition); - return NULL; - } - - hLab = cmsCreateLab4ProfileTHR(ContextID, NULL); - if (hLab == NULL) return NULL; - - - // The figure of merit. On matrix-shaper profiles, should be almost zero as - // the conversion is pretty exact. On LUT based profiles, different resolutions - // of input and output CLUT may result in differences. - - if (cmsIsMatrixShaper(hGamut)) { - - Chain.Thereshold = 1.0; - } - else { - Chain.Thereshold = ERR_THERESHOLD; - } - - - // Create a copy of parameters - for (i=0; i < nGamutPCSposition; i++) { - ProfileList[i] = hProfiles[i]; - BPCList[i] = BPC[i]; - AdaptationList[i] = AdaptationStates[i]; - IntentList[i] = Intents[i]; - } - - // Fill Lab identity - ProfileList[nGamutPCSposition] = hLab; - BPCList[nGamutPCSposition] = 0; - AdaptationList[nGamutPCSposition] = 1.0; - IntentList[nGamutPCSposition] = INTENT_RELATIVE_COLORIMETRIC; - - - ColorSpace = cmsGetColorSpace(hGamut); - - nChannels = cmsChannelsOf(ColorSpace); - nGridpoints = _cmsReasonableGridpointsByColorspace(ColorSpace, cmsFLAGS_HIGHRESPRECALC); - dwFormat = (CHANNELS_SH(nChannels)|BYTES_SH(2)); - - // 16 bits to Lab double - Chain.hInput = cmsCreateExtendedTransform(ContextID, - nGamutPCSposition + 1, - ProfileList, - BPCList, - IntentList, - AdaptationList, - NULL, 0, - dwFormat, TYPE_Lab_DBL, - cmsFLAGS_NOCACHE); - - - // Does create the forward step. Lab double to device - dwFormat = (CHANNELS_SH(nChannels)|BYTES_SH(2)); - Chain.hForward = cmsCreateTransformTHR(ContextID, - hLab, TYPE_Lab_DBL, - hGamut, dwFormat, - INTENT_RELATIVE_COLORIMETRIC, - cmsFLAGS_NOCACHE); - - // Does create the backwards step - Chain.hReverse = cmsCreateTransformTHR(ContextID, hGamut, dwFormat, - hLab, TYPE_Lab_DBL, - INTENT_RELATIVE_COLORIMETRIC, - cmsFLAGS_NOCACHE); - - - // All ok? - if (Chain.hInput && Chain.hForward && Chain.hReverse) { - - // Go on, try to compute gamut LUT from PCS. This consist on a single channel containing - // dE when doing a transform back and forth on the colorimetric intent. - - Gamut = cmsPipelineAlloc(ContextID, 3, 1); - if (Gamut != NULL) { - - CLUT = cmsStageAllocCLut16bit(ContextID, nGridpoints, nChannels, 1, NULL); - if (!cmsPipelineInsertStage(Gamut, cmsAT_BEGIN, CLUT)) { - cmsPipelineFree(Gamut); - Gamut = NULL; - } - else { - cmsStageSampleCLut16bit(CLUT, GamutSampler, (void*) &Chain, 0); - } - } - } - else - Gamut = NULL; // Didn't work... - - // Free all needed stuff. - if (Chain.hInput) cmsDeleteTransform(Chain.hInput); - if (Chain.hForward) cmsDeleteTransform(Chain.hForward); - if (Chain.hReverse) cmsDeleteTransform(Chain.hReverse); - if (hLab) cmsCloseProfile(hLab); - - // And return computed hull - return Gamut; -} - -// Total Area Coverage estimation ---------------------------------------------------------------- - -typedef struct { - cmsUInt32Number nOutputChans; - cmsHTRANSFORM hRoundTrip; - cmsFloat32Number MaxTAC; - cmsFloat32Number MaxInput[cmsMAXCHANNELS]; - -} cmsTACestimator; - - -// This callback just accounts the maximum ink dropped in the given node. It does not populate any -// memory, as the destination table is NULL. Its only purpose it to know the global maximum. -static -int EstimateTAC(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void * Cargo) -{ - cmsTACestimator* bp = (cmsTACestimator*) Cargo; - cmsFloat32Number RoundTrip[cmsMAXCHANNELS]; - cmsUInt32Number i; - cmsFloat32Number Sum; - - - // Evaluate the xform - cmsDoTransform(bp->hRoundTrip, In, RoundTrip, 1); - - // All all amounts of ink - for (Sum=0, i=0; i < bp ->nOutputChans; i++) - Sum += RoundTrip[i]; - - // If above maximum, keep track of input values - if (Sum > bp ->MaxTAC) { - - bp ->MaxTAC = Sum; - - for (i=0; i < bp ->nOutputChans; i++) { - bp ->MaxInput[i] = In[i]; - } - } - - return TRUE; - - cmsUNUSED_PARAMETER(Out); -} - - -// Detect Total area coverage of the profile -cmsFloat64Number CMSEXPORT cmsDetectTAC(cmsHPROFILE hProfile) -{ - cmsTACestimator bp; - cmsUInt32Number dwFormatter; - cmsUInt32Number GridPoints[MAX_INPUT_DIMENSIONS]; - cmsHPROFILE hLab; - cmsContext ContextID = cmsGetProfileContextID(hProfile); - - // TAC only works on output profiles - if (cmsGetDeviceClass(hProfile) != cmsSigOutputClass) { - return 0; - } - - // Create a fake formatter for result - dwFormatter = cmsFormatterForColorspaceOfProfile(hProfile, 4, TRUE); - - bp.nOutputChans = T_CHANNELS(dwFormatter); - bp.MaxTAC = 0; // Initial TAC is 0 - - // for safety - if (bp.nOutputChans >= cmsMAXCHANNELS) return 0; - - hLab = cmsCreateLab4ProfileTHR(ContextID, NULL); - if (hLab == NULL) return 0; - // Setup a roundtrip on perceptual intent in output profile for TAC estimation - bp.hRoundTrip = cmsCreateTransformTHR(ContextID, hLab, TYPE_Lab_16, - hProfile, dwFormatter, INTENT_PERCEPTUAL, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE); - - cmsCloseProfile(hLab); - if (bp.hRoundTrip == NULL) return 0; - - // For L* we only need black and white. For C* we need many points - GridPoints[0] = 6; - GridPoints[1] = 74; - GridPoints[2] = 74; - - - if (!cmsSliceSpace16(3, GridPoints, EstimateTAC, &bp)) { - bp.MaxTAC = 0; - } - - cmsDeleteTransform(bp.hRoundTrip); - - // Results in % - return bp.MaxTAC; -} - - -// Carefully, clamp on CIELab space. - -cmsBool CMSEXPORT cmsDesaturateLab(cmsCIELab* Lab, - double amax, double amin, - double bmax, double bmin) -{ - - // Whole Luma surface to zero - - if (Lab -> L < 0) { - - Lab-> L = Lab->a = Lab-> b = 0.0; - return FALSE; - } - - // Clamp white, DISCARD HIGHLIGHTS. This is done - // in such way because icc spec doesn't allow the - // use of L>100 as a highlight means. - - if (Lab->L > 100) - Lab -> L = 100; - - // Check out gamut prism, on a, b faces - - if (Lab -> a < amin || Lab->a > amax|| - Lab -> b < bmin || Lab->b > bmax) { - - cmsCIELCh LCh; - double h, slope; - - // Falls outside a, b limits. Transports to LCh space, - // and then do the clipping - - - if (Lab -> a == 0.0) { // Is hue exactly 90? - - // atan will not work, so clamp here - Lab -> b = Lab->b < 0 ? bmin : bmax; - return TRUE; - } - - cmsLab2LCh(&LCh, Lab); - - slope = Lab -> b / Lab -> a; - h = LCh.h; - - // There are 4 zones - - if ((h >= 0. && h < 45.) || - (h >= 315 && h <= 360.)) { - - // clip by amax - Lab -> a = amax; - Lab -> b = amax * slope; - } - else - if (h >= 45. && h < 135.) - { - // clip by bmax - Lab -> b = bmax; - Lab -> a = bmax / slope; - } - else - if (h >= 135. && h < 225.) { - // clip by amin - Lab -> a = amin; - Lab -> b = amin * slope; - - } - else - if (h >= 225. && h < 315.) { - // clip by bmin - Lab -> b = bmin; - Lab -> a = bmin / slope; - } - else { - cmsSignalError(0, cmsERROR_RANGE, "Invalid angle"); - return FALSE; - } - - } - - return TRUE; -} +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2012 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. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + + +// Auxiliar: append a Lab identity after the given sequence of profiles +// and return the transform. Lab profile is closed, rest of profiles are kept open. +cmsHTRANSFORM _cmsChain2Lab(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number InputFormat, + cmsUInt32Number OutputFormat, + const cmsUInt32Number Intents[], + const cmsHPROFILE hProfiles[], + const cmsBool BPC[], + const cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) +{ + cmsHTRANSFORM xform; + cmsHPROFILE hLab; + cmsHPROFILE ProfileList[256]; + cmsBool BPCList[256]; + cmsFloat64Number AdaptationList[256]; + cmsUInt32Number IntentList[256]; + cmsUInt32Number i; + + // This is a rather big number and there is no need of dynamic memory + // since we are adding a profile, 254 + 1 = 255 and this is the limit + if (nProfiles > 254) return NULL; + + // The output space + hLab = cmsCreateLab4ProfileTHR(ContextID, NULL); + if (hLab == NULL) return NULL; + + // Create a copy of parameters + for (i=0; i < nProfiles; i++) { + + ProfileList[i] = hProfiles[i]; + BPCList[i] = BPC[i]; + AdaptationList[i] = AdaptationStates[i]; + IntentList[i] = Intents[i]; + } + + // Place Lab identity at chain's end. + ProfileList[nProfiles] = hLab; + BPCList[nProfiles] = 0; + AdaptationList[nProfiles] = 1.0; + IntentList[nProfiles] = INTENT_RELATIVE_COLORIMETRIC; + + // Create the transform + xform = cmsCreateExtendedTransform(ContextID, nProfiles + 1, ProfileList, + BPCList, + IntentList, + AdaptationList, + NULL, 0, + InputFormat, + OutputFormat, + dwFlags); + + cmsCloseProfile(hLab); + + return xform; +} + + +// Compute K -> L* relationship. Flags may include black point compensation. In this case, +// the relationship is assumed from the profile with BPC to a black point zero. +static +cmsToneCurve* ComputeKToLstar(cmsContext ContextID, + cmsUInt32Number nPoints, + cmsUInt32Number nProfiles, + const cmsUInt32Number Intents[], + const cmsHPROFILE hProfiles[], + const cmsBool BPC[], + const cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) +{ + cmsToneCurve* out = NULL; + cmsUInt32Number i; + cmsHTRANSFORM xform; + cmsCIELab Lab; + cmsFloat32Number cmyk[4]; + cmsFloat32Number* SampledPoints; + + xform = _cmsChain2Lab(ContextID, nProfiles, TYPE_CMYK_FLT, TYPE_Lab_DBL, Intents, hProfiles, BPC, AdaptationStates, dwFlags); + if (xform == NULL) return NULL; + + SampledPoints = (cmsFloat32Number*) _cmsCalloc(ContextID, nPoints, sizeof(cmsFloat32Number)); + if (SampledPoints == NULL) goto Error; + + for (i=0; i < nPoints; i++) { + + cmyk[0] = 0; + cmyk[1] = 0; + cmyk[2] = 0; + cmyk[3] = (cmsFloat32Number) ((i * 100.0) / (nPoints-1)); + + cmsDoTransform(xform, cmyk, &Lab, 1); + SampledPoints[i]= (cmsFloat32Number) (1.0 - Lab.L / 100.0); // Negate K for easier operation + } + + out = cmsBuildTabulatedToneCurveFloat(ContextID, nPoints, SampledPoints); + +Error: + + cmsDeleteTransform(xform); + if (SampledPoints) _cmsFree(ContextID, SampledPoints); + + return out; +} + + +// Compute Black tone curve on a CMYK -> CMYK transform. This is done by +// using the proof direction on both profiles to find K->L* relationship +// then joining both curves. dwFlags may include black point compensation. +cmsToneCurve* _cmsBuildKToneCurve(cmsContext ContextID, + cmsUInt32Number nPoints, + cmsUInt32Number nProfiles, + const cmsUInt32Number Intents[], + const cmsHPROFILE hProfiles[], + const cmsBool BPC[], + const cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) +{ + cmsToneCurve *in, *out, *KTone; + + // Make sure CMYK -> CMYK + if (cmsGetColorSpace(hProfiles[0]) != cmsSigCmykData || + cmsGetColorSpace(hProfiles[nProfiles-1])!= cmsSigCmykData) return NULL; + + + // Make sure last is an output profile + if (cmsGetDeviceClass(hProfiles[nProfiles - 1]) != cmsSigOutputClass) return NULL; + + // Create individual curves. BPC works also as each K to L* is + // computed as a BPC to zero black point in case of L* + in = ComputeKToLstar(ContextID, nPoints, nProfiles - 1, Intents, hProfiles, BPC, AdaptationStates, dwFlags); + if (in == NULL) return NULL; + + out = ComputeKToLstar(ContextID, nPoints, 1, + Intents + (nProfiles - 1), + &hProfiles [nProfiles - 1], + BPC + (nProfiles - 1), + AdaptationStates + (nProfiles - 1), + dwFlags); + if (out == NULL) { + cmsFreeToneCurve(in); + return NULL; + } + + // Build the relationship. This effectively limits the maximum accuracy to 16 bits, but + // since this is used on black-preserving LUTs, we are not loosing accuracy in any case + KTone = cmsJoinToneCurve(ContextID, in, out, nPoints); + + // Get rid of components + cmsFreeToneCurve(in); cmsFreeToneCurve(out); + + // Something went wrong... + if (KTone == NULL) return NULL; + + // Make sure it is monotonic + if (!cmsIsToneCurveMonotonic(KTone)) { + cmsFreeToneCurve(KTone); + return NULL; + } + + return KTone; +} + + +// Gamut LUT Creation ----------------------------------------------------------------------------------------- + +// Used by gamut & softproofing + +typedef struct { + + cmsHTRANSFORM hInput; // From whatever input color space. 16 bits to DBL + cmsHTRANSFORM hForward, hReverse; // Transforms going from Lab to colorant and back + cmsFloat64Number Thereshold; // The thereshold after which is considered out of gamut + + } GAMUTCHAIN; + +// This sampler does compute gamut boundaries by comparing original +// values with a transform going back and forth. Values above ERR_THERESHOLD +// of maximum are considered out of gamut. + +#define ERR_THERESHOLD 5 + + +static +int GamutSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo) +{ + GAMUTCHAIN* t = (GAMUTCHAIN* ) Cargo; + cmsCIELab LabIn1, LabOut1; + cmsCIELab LabIn2, LabOut2; + cmsUInt16Number Proof[cmsMAXCHANNELS], Proof2[cmsMAXCHANNELS]; + cmsFloat64Number dE1, dE2, ErrorRatio; + + // Assume in-gamut by default. + ErrorRatio = 1.0; + + // Convert input to Lab + cmsDoTransform(t -> hInput, In, &LabIn1, 1); + + // converts from PCS to colorant. This always + // does return in-gamut values, + cmsDoTransform(t -> hForward, &LabIn1, Proof, 1); + + // Now, do the inverse, from colorant to PCS. + cmsDoTransform(t -> hReverse, Proof, &LabOut1, 1); + + memmove(&LabIn2, &LabOut1, sizeof(cmsCIELab)); + + // Try again, but this time taking Check as input + cmsDoTransform(t -> hForward, &LabOut1, Proof2, 1); + cmsDoTransform(t -> hReverse, Proof2, &LabOut2, 1); + + // Take difference of direct value + dE1 = cmsDeltaE(&LabIn1, &LabOut1); + + // Take difference of converted value + dE2 = cmsDeltaE(&LabIn2, &LabOut2); + + + // if dE1 is small and dE2 is small, value is likely to be in gamut + if (dE1 < t->Thereshold && dE2 < t->Thereshold) + Out[0] = 0; + else { + + // if dE1 is small and dE2 is big, undefined. Assume in gamut + if (dE1 < t->Thereshold && dE2 > t->Thereshold) + Out[0] = 0; + else + // dE1 is big and dE2 is small, clearly out of gamut + if (dE1 > t->Thereshold && dE2 < t->Thereshold) + Out[0] = (cmsUInt16Number) _cmsQuickFloor((dE1 - t->Thereshold) + .5); + else { + + // dE1 is big and dE2 is also big, could be due to perceptual mapping + // so take error ratio + if (dE2 == 0.0) + ErrorRatio = dE1; + else + ErrorRatio = dE1 / dE2; + + if (ErrorRatio > t->Thereshold) + Out[0] = (cmsUInt16Number) _cmsQuickFloor((ErrorRatio - t->Thereshold) + .5); + else + Out[0] = 0; + } + } + + + return TRUE; +} + +// Does compute a gamut LUT going back and forth across pcs -> relativ. colorimetric intent -> pcs +// the dE obtained is then annotated on the LUT. Values truely out of gamut are clipped to dE = 0xFFFE +// and values changed are supposed to be handled by any gamut remapping, so, are out of gamut as well. +// +// **WARNING: This algorithm does assume that gamut remapping algorithms does NOT move in-gamut colors, +// of course, many perceptual and saturation intents does not work in such way, but relativ. ones should. + +cmsPipeline* _cmsCreateGamutCheckPipeline(cmsContext ContextID, + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsUInt32Number Intents[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number nGamutPCSposition, + cmsHPROFILE hGamut) +{ + cmsHPROFILE hLab; + cmsPipeline* Gamut; + cmsStage* CLUT; + cmsUInt32Number dwFormat; + GAMUTCHAIN Chain; + int nChannels, nGridpoints; + cmsColorSpaceSignature ColorSpace; + cmsUInt32Number i; + cmsHPROFILE ProfileList[256]; + cmsBool BPCList[256]; + cmsFloat64Number AdaptationList[256]; + cmsUInt32Number IntentList[256]; + + memset(&Chain, 0, sizeof(GAMUTCHAIN)); + + + if (nGamutPCSposition <= 0 || nGamutPCSposition > 255) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong position of PCS. 1..255 expected, %d found.", nGamutPCSposition); + return NULL; + } + + hLab = cmsCreateLab4ProfileTHR(ContextID, NULL); + if (hLab == NULL) return NULL; + + + // The figure of merit. On matrix-shaper profiles, should be almost zero as + // the conversion is pretty exact. On LUT based profiles, different resolutions + // of input and output CLUT may result in differences. + + if (cmsIsMatrixShaper(hGamut)) { + + Chain.Thereshold = 1.0; + } + else { + Chain.Thereshold = ERR_THERESHOLD; + } + + + // Create a copy of parameters + for (i=0; i < nGamutPCSposition; i++) { + ProfileList[i] = hProfiles[i]; + BPCList[i] = BPC[i]; + AdaptationList[i] = AdaptationStates[i]; + IntentList[i] = Intents[i]; + } + + // Fill Lab identity + ProfileList[nGamutPCSposition] = hLab; + BPCList[nGamutPCSposition] = 0; + AdaptationList[nGamutPCSposition] = 1.0; + IntentList[nGamutPCSposition] = INTENT_RELATIVE_COLORIMETRIC; + + + ColorSpace = cmsGetColorSpace(hGamut); + + nChannels = cmsChannelsOf(ColorSpace); + nGridpoints = _cmsReasonableGridpointsByColorspace(ColorSpace, cmsFLAGS_HIGHRESPRECALC); + dwFormat = (CHANNELS_SH(nChannels)|BYTES_SH(2)); + + // 16 bits to Lab double + Chain.hInput = cmsCreateExtendedTransform(ContextID, + nGamutPCSposition + 1, + ProfileList, + BPCList, + IntentList, + AdaptationList, + NULL, 0, + dwFormat, TYPE_Lab_DBL, + cmsFLAGS_NOCACHE); + + + // Does create the forward step. Lab double to device + dwFormat = (CHANNELS_SH(nChannels)|BYTES_SH(2)); + Chain.hForward = cmsCreateTransformTHR(ContextID, + hLab, TYPE_Lab_DBL, + hGamut, dwFormat, + INTENT_RELATIVE_COLORIMETRIC, + cmsFLAGS_NOCACHE); + + // Does create the backwards step + Chain.hReverse = cmsCreateTransformTHR(ContextID, hGamut, dwFormat, + hLab, TYPE_Lab_DBL, + INTENT_RELATIVE_COLORIMETRIC, + cmsFLAGS_NOCACHE); + + + // All ok? + if (Chain.hInput && Chain.hForward && Chain.hReverse) { + + // Go on, try to compute gamut LUT from PCS. This consist on a single channel containing + // dE when doing a transform back and forth on the colorimetric intent. + + Gamut = cmsPipelineAlloc(ContextID, 3, 1); + if (Gamut != NULL) { + + CLUT = cmsStageAllocCLut16bit(ContextID, nGridpoints, nChannels, 1, NULL); + if (!cmsPipelineInsertStage(Gamut, cmsAT_BEGIN, CLUT)) { + cmsPipelineFree(Gamut); + Gamut = NULL; + } + else { + cmsStageSampleCLut16bit(CLUT, GamutSampler, (void*) &Chain, 0); + } + } + } + else + Gamut = NULL; // Didn't work... + + // Free all needed stuff. + if (Chain.hInput) cmsDeleteTransform(Chain.hInput); + if (Chain.hForward) cmsDeleteTransform(Chain.hForward); + if (Chain.hReverse) cmsDeleteTransform(Chain.hReverse); + if (hLab) cmsCloseProfile(hLab); + + // And return computed hull + return Gamut; +} + +// Total Area Coverage estimation ---------------------------------------------------------------- + +typedef struct { + cmsUInt32Number nOutputChans; + cmsHTRANSFORM hRoundTrip; + cmsFloat32Number MaxTAC; + cmsFloat32Number MaxInput[cmsMAXCHANNELS]; + +} cmsTACestimator; + + +// This callback just accounts the maximum ink dropped in the given node. It does not populate any +// memory, as the destination table is NULL. Its only purpose it to know the global maximum. +static +int EstimateTAC(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void * Cargo) +{ + cmsTACestimator* bp = (cmsTACestimator*) Cargo; + cmsFloat32Number RoundTrip[cmsMAXCHANNELS]; + cmsUInt32Number i; + cmsFloat32Number Sum; + + + // Evaluate the xform + cmsDoTransform(bp->hRoundTrip, In, RoundTrip, 1); + + // All all amounts of ink + for (Sum=0, i=0; i < bp ->nOutputChans; i++) + Sum += RoundTrip[i]; + + // If above maximum, keep track of input values + if (Sum > bp ->MaxTAC) { + + bp ->MaxTAC = Sum; + + for (i=0; i < bp ->nOutputChans; i++) { + bp ->MaxInput[i] = In[i]; + } + } + + return TRUE; + + cmsUNUSED_PARAMETER(Out); +} + + +// Detect Total area coverage of the profile +cmsFloat64Number CMSEXPORT cmsDetectTAC(cmsHPROFILE hProfile) +{ + cmsTACestimator bp; + cmsUInt32Number dwFormatter; + cmsUInt32Number GridPoints[MAX_INPUT_DIMENSIONS]; + cmsHPROFILE hLab; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + // TAC only works on output profiles + if (cmsGetDeviceClass(hProfile) != cmsSigOutputClass) { + return 0; + } + + // Create a fake formatter for result + dwFormatter = cmsFormatterForColorspaceOfProfile(hProfile, 4, TRUE); + + bp.nOutputChans = T_CHANNELS(dwFormatter); + bp.MaxTAC = 0; // Initial TAC is 0 + + // for safety + if (bp.nOutputChans >= cmsMAXCHANNELS) return 0; + + hLab = cmsCreateLab4ProfileTHR(ContextID, NULL); + if (hLab == NULL) return 0; + // Setup a roundtrip on perceptual intent in output profile for TAC estimation + bp.hRoundTrip = cmsCreateTransformTHR(ContextID, hLab, TYPE_Lab_16, + hProfile, dwFormatter, INTENT_PERCEPTUAL, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE); + + cmsCloseProfile(hLab); + if (bp.hRoundTrip == NULL) return 0; + + // For L* we only need black and white. For C* we need many points + GridPoints[0] = 6; + GridPoints[1] = 74; + GridPoints[2] = 74; + + + if (!cmsSliceSpace16(3, GridPoints, EstimateTAC, &bp)) { + bp.MaxTAC = 0; + } + + cmsDeleteTransform(bp.hRoundTrip); + + // Results in % + return bp.MaxTAC; +} + + +// Carefully, clamp on CIELab space. + +cmsBool CMSEXPORT cmsDesaturateLab(cmsCIELab* Lab, + double amax, double amin, + double bmax, double bmin) +{ + + // Whole Luma surface to zero + + if (Lab -> L < 0) { + + Lab-> L = Lab->a = Lab-> b = 0.0; + return FALSE; + } + + // Clamp white, DISCARD HIGHLIGHTS. This is done + // in such way because icc spec doesn't allow the + // use of L>100 as a highlight means. + + if (Lab->L > 100) + Lab -> L = 100; + + // Check out gamut prism, on a, b faces + + if (Lab -> a < amin || Lab->a > amax|| + Lab -> b < bmin || Lab->b > bmax) { + + cmsCIELCh LCh; + double h, slope; + + // Falls outside a, b limits. Transports to LCh space, + // and then do the clipping + + + if (Lab -> a == 0.0) { // Is hue exactly 90? + + // atan will not work, so clamp here + Lab -> b = Lab->b < 0 ? bmin : bmax; + return TRUE; + } + + cmsLab2LCh(&LCh, Lab); + + slope = Lab -> b / Lab -> a; + h = LCh.h; + + // There are 4 zones + + if ((h >= 0. && h < 45.) || + (h >= 315 && h <= 360.)) { + + // clip by amax + Lab -> a = amax; + Lab -> b = amax * slope; + } + else + if (h >= 45. && h < 135.) + { + // clip by bmax + Lab -> b = bmax; + Lab -> a = bmax / slope; + } + else + if (h >= 135. && h < 225.) { + // clip by amin + Lab -> a = amin; + Lab -> b = amin * slope; + + } + else + if (h >= 225. && h < 315.) { + // clip by bmin + Lab -> b = bmin; + Lab -> a = bmin / slope; + } + else { + cmsSignalError(0, cmsERROR_RANGE, "Invalid angle"); + return FALSE; + } + + } + + return TRUE; +} diff --git a/src/cmsintrp.c b/src/cmsintrp.c index 01150d7..5d5f35d 100644 --- a/src/cmsintrp.c +++ b/src/cmsintrp.c @@ -33,32 +33,58 @@ static cmsInterpFunction DefaultInterpolatorsFactory(cmsUInt32Number nInputChannels, cmsUInt32Number nOutputChannels, cmsUInt32Number dwFlags); // This is the default factory -static cmsInterpFnFactory Interpolators = DefaultInterpolatorsFactory; +_cmsInterpPluginChunkType _cmsInterpPluginChunk = { NULL }; + +// The interpolation plug-in memory chunk allocator/dup +void _cmsAllocInterpPluginChunk(struct _cmsContext_struct* ctx, const struct _cmsContext_struct* src) +{ + void* from; + + _cmsAssert(ctx != NULL); + + if (src != NULL) { + from = src ->chunks[InterpPlugin]; + } + else { + static _cmsInterpPluginChunkType InterpPluginChunk = { NULL }; + + from = &InterpPluginChunk; + } + + _cmsAssert(from != NULL); + ctx ->chunks[InterpPlugin] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsInterpPluginChunkType)); +} // Main plug-in entry -cmsBool _cmsRegisterInterpPlugin(cmsPluginBase* Data) +cmsBool _cmsRegisterInterpPlugin(cmsContext ContextID, cmsPluginBase* Data) { cmsPluginInterpolation* Plugin = (cmsPluginInterpolation*) Data; + _cmsInterpPluginChunkType* ptr = (_cmsInterpPluginChunkType*) _cmsContextGetClientChunk(ContextID, InterpPlugin); if (Data == NULL) { - Interpolators = DefaultInterpolatorsFactory; + ptr ->Interpolators = NULL; return TRUE; } // Set replacement functions - Interpolators = Plugin ->InterpolatorsFactory; + ptr ->Interpolators = Plugin ->InterpolatorsFactory; return TRUE; } // Set the interpolation method -cmsBool _cmsSetInterpolationRoutine(cmsInterpParams* p) -{ - // Invoke factory, possibly in the Plug-in - p ->Interpolation = Interpolators(p -> nInputs, p ->nOutputs, p ->dwFlags); +cmsBool _cmsSetInterpolationRoutine(cmsContext ContextID, cmsInterpParams* p) +{ + _cmsInterpPluginChunkType* ptr = (_cmsInterpPluginChunkType*) _cmsContextGetClientChunk(ContextID, InterpPlugin); + + p ->Interpolation.Lerp16 = NULL; + // Invoke factory, possibly in the Plug-in + if (ptr ->Interpolators != NULL) + p ->Interpolation = ptr->Interpolators(p -> nInputs, p ->nOutputs, p ->dwFlags); + // If unsupported by the plug-in, go for the LittleCMS default. // If happens only if an extern plug-in is being used if (p ->Interpolation.Lerp16 == NULL) @@ -68,6 +94,7 @@ cmsBool _cmsSetInterpolationRoutine(cmsInterpParams* p) if (p ->Interpolation.Lerp16 == NULL) { return FALSE; } + return TRUE; } @@ -112,7 +139,7 @@ cmsInterpParams* _cmsComputeInterpParamsEx(cmsContext ContextID, p ->opta[i] = p ->opta[i-1] * nSamples[InputChan-i]; - if (!_cmsSetInterpolationRoutine(p)) { + if (!_cmsSetInterpolationRoutine(ContextID, p)) { cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported interpolation (%d->%d channels)", InputChan, OutputChan); _cmsFree(ContextID, p); return NULL; diff --git a/src/cmsio0.c b/src/cmsio0.c index 2430e98..4e8cd93 100644 --- a/src/cmsio0.c +++ b/src/cmsio0.c @@ -471,6 +471,9 @@ cmsHPROFILE CMSEXPORT cmsCreateProfilePlaceholder(cmsContext ContextID) // Set creation date/time memmove(&Icc ->Created, gmtime(&now), sizeof(Icc ->Created)); + // Create a mutex if the user provided proper plugin. NULL otherwise + Icc ->UsrMutex = _cmsCreateMutex(ContextID); + // Return the handle return (cmsHPROFILE) Icc; } @@ -549,9 +552,38 @@ int _cmsSearchTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, cmsBool lFollowLinks return n; } +// Deletes a tag entry + +static +void _cmsDeleteTagByPos(_cmsICCPROFILE* Icc, int i) +{ + _cmsAssert(Icc != NULL); + _cmsAssert(i >= 0); + + + if (Icc -> TagPtrs[i] != NULL) { + + // Free previous version + if (Icc ->TagSaveAsRaw[i]) { + _cmsFree(Icc ->ContextID, Icc ->TagPtrs[i]); + } + else { + cmsTagTypeHandler* TypeHandler = Icc ->TagTypeHandlers[i]; + + if (TypeHandler != NULL) { + + cmsTagTypeHandler LocalTypeHandler = *TypeHandler; + LocalTypeHandler.ContextID = Icc ->ContextID; // As an additional parameter + LocalTypeHandler.ICCVersion = Icc ->Version; + LocalTypeHandler.FreePtr(&LocalTypeHandler, Icc -> TagPtrs[i]); + } + } + + } +} -// Create a new tag entry +// Creates a new tag entry static cmsBool _cmsNewTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, int* NewPos) { @@ -559,15 +591,15 @@ cmsBool _cmsNewTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, int* NewPos) // Search for the tag i = _cmsSearchTag(Icc, sig, FALSE); - - // Now let's do it easy. If the tag has been already written, that's an error if (i >= 0) { - cmsSignalError(Icc ->ContextID, cmsERROR_ALREADY_DEFINED, "Tag '%x' already exists", sig); - return FALSE; + + // Already exists? delete it + _cmsDeleteTagByPos(Icc, i); + *NewPos = i; } else { - // New one + // No, make a new one if (Icc -> TagCount >= MAX_TABLE_TAG) { cmsSignalError(Icc ->ContextID, cmsERROR_RANGE, "Too many tags (%d)", MAX_TABLE_TAG); @@ -924,7 +956,7 @@ void CMSEXPORT cmsSetProfileVersion(cmsHPROFILE hProfile, cmsFloat64Number Vers // 4.2 -> 0x4200000 - Icc -> Version = BaseToBase((cmsUInt32Number) floor(Version * 100.0), 10, 16) << 16; + Icc -> Version = BaseToBase((cmsUInt32Number) floor(Version * 100.0 + 0.5), 10, 16) << 16; } cmsFloat64Number CMSEXPORT cmsGetProfileVersion(cmsHPROFILE hProfile) @@ -1147,7 +1179,7 @@ cmsBool SaveTags(_cmsICCPROFILE* Icc, _cmsICCPROFILE* FileOrig) else { // Search for support on this tag - TagDescriptor = _cmsGetTagDescriptor(Icc -> TagNames[i]); + TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, Icc -> TagNames[i]); if (TagDescriptor == NULL) continue; // Unsupported, ignore it if (TagDescriptor ->DecideType != NULL) { @@ -1159,7 +1191,7 @@ cmsBool SaveTags(_cmsICCPROFILE* Icc, _cmsICCPROFILE* FileOrig) Type = TagDescriptor ->SupportedTypes[0]; } - TypeHandler = _cmsGetTagTypeHandler(Type); + TypeHandler = _cmsGetTagTypeHandler(Icc->ContextID, Type); if (TypeHandler == NULL) { cmsSignalError(Icc ->ContextID, cmsERROR_INTERNAL, "(Internal) no handler for tag %x", Icc -> TagNames[i]); @@ -1227,7 +1259,7 @@ cmsUInt32Number CMSEXPORT cmsSaveProfileToIOhandler(cmsHPROFILE hProfile, cmsIOH { _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; _cmsICCPROFILE Keep; - cmsIOHANDLER* PrevIO; + cmsIOHANDLER* PrevIO = NULL; cmsUInt32Number UsedSpace; cmsContext ContextID; @@ -1241,8 +1273,8 @@ cmsUInt32Number CMSEXPORT cmsSaveProfileToIOhandler(cmsHPROFILE hProfile, cmsIOH // Pass #1 does compute offsets - if (!_cmsWriteHeader(Icc, 0)) return 0; - if (!SaveTags(Icc, &Keep)) return 0; + if (!_cmsWriteHeader(Icc, 0)) goto Error; + if (!SaveTags(Icc, &Keep)) goto Error; UsedSpace = PrevIO ->UsedSpace; @@ -1251,9 +1283,9 @@ cmsUInt32Number CMSEXPORT cmsSaveProfileToIOhandler(cmsHPROFILE hProfile, cmsIOH if (io != NULL) { Icc ->IOhandler = io; - if (!SetLinks(Icc)) goto CleanUp; - if (!_cmsWriteHeader(Icc, UsedSpace)) goto CleanUp; - if (!SaveTags(Icc, &Keep)) goto CleanUp; + if (!SetLinks(Icc)) goto Error; + if (!_cmsWriteHeader(Icc, UsedSpace)) goto Error; + if (!SaveTags(Icc, &Keep)) goto Error; } memmove(Icc, &Keep, sizeof(_cmsICCPROFILE)); @@ -1262,7 +1294,7 @@ cmsUInt32Number CMSEXPORT cmsSaveProfileToIOhandler(cmsHPROFILE hProfile, cmsIOH return UsedSpace; -CleanUp: +Error: cmsCloseIOhandler(PrevIO); memmove(Icc, &Keep, sizeof(_cmsICCPROFILE)); return 0; @@ -1310,11 +1342,13 @@ cmsBool CMSEXPORT cmsSaveProfileToMem(cmsHPROFILE hProfile, void *MemPtr, cmsUIn cmsIOHANDLER* io; cmsContext ContextID = cmsGetProfileContextID(hProfile); + _cmsAssert(BytesNeeded != NULL); + // Should we just calculate the needed space? if (MemPtr == NULL) { *BytesNeeded = cmsSaveProfileToIOhandler(hProfile, NULL); - return TRUE; + return (*BytesNeeded == 0) ? FALSE : TRUE; } // That is a real write operation @@ -1367,6 +1401,8 @@ cmsBool CMSEXPORT cmsCloseProfile(cmsHPROFILE hProfile) rc &= cmsCloseIOhandler(Icc->IOhandler); } + _cmsDestroyMutex(Icc->ContextID, Icc->UsrMutex); + _cmsFree(Icc ->ContextID, Icc); // Free placeholder memory return rc; @@ -1407,14 +1443,18 @@ void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig) cmsUInt32Number ElemCount; int n; + if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return NULL; + n = _cmsSearchTag(Icc, sig, TRUE); - if (n < 0) return NULL; // Not found, return NULL + if (n < 0) goto Error; // Not found, return NULL // If the element is already in memory, return the pointer if (Icc -> TagPtrs[n]) { - if (Icc ->TagSaveAsRaw[n]) return NULL; // We don't support read raw tags as cooked + if (Icc ->TagSaveAsRaw[n]) goto Error; // We don't support read raw tags as cooked + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); return Icc -> TagPtrs[n]; } @@ -1424,23 +1464,32 @@ void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig) // Seek to its location if (!io -> Seek(io, Offset)) - return NULL; + goto Error; // Search for support on this tag - TagDescriptor = _cmsGetTagDescriptor(sig); - if (TagDescriptor == NULL) return NULL; // Unsupported. + TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig); + if (TagDescriptor == NULL) { + + char String[5]; + + _cmsTagSignature2String(String, sig); + + // An unknown element was found. + cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown tag type '%s' found.", String); + goto Error; // Unsupported. + } // if supported, get type and check if in list BaseType = _cmsReadTypeBase(io); - if (BaseType == 0) return NULL; + if (BaseType == 0) goto Error; - if (!IsTypeSupported(TagDescriptor, BaseType)) return NULL; + if (!IsTypeSupported(TagDescriptor, BaseType)) goto Error; TagSize -= 8; // Alredy read by the type base logic // Get type handler - TypeHandler = _cmsGetTagTypeHandler(BaseType); - if (TypeHandler == NULL) return NULL; + TypeHandler = _cmsGetTagTypeHandler(Icc ->ContextID, BaseType); + if (TypeHandler == NULL) goto Error; LocalTypeHandler = *TypeHandler; @@ -1459,7 +1508,7 @@ void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig) _cmsTagSignature2String(String, sig); cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "Corrupted tag '%s'", String); - return NULL; + goto Error; } // This is a weird error that may be a symptom of something more serious, the number of @@ -1475,7 +1524,14 @@ void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig) // Return the data + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); return Icc -> TagPtrs[n]; + + + // Return error and unlock tha data +Error: + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return NULL; } @@ -1509,49 +1565,26 @@ cmsBool CMSEXPORT cmsWriteTag(cmsHPROFILE hProfile, cmsTagSignature sig, const v cmsFloat64Number Version; char TypeString[5], SigString[5]; + if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return FALSE; + // To delete tags. if (data == NULL) { + // Delete the tag i = _cmsSearchTag(Icc, sig, FALSE); - if (i >= 0) + if (i >= 0) { + + // Use zero as a mark of deleted + _cmsDeleteTagByPos(Icc, i); Icc ->TagNames[i] = (cmsTagSignature) 0; - // Unsupported by now, reserved for future ampliations (delete) - return FALSE; + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return TRUE; + } + // Didn't find the tag + goto Error; } - i = _cmsSearchTag(Icc, sig, FALSE); - if (i >=0) { - - if (Icc -> TagPtrs[i] != NULL) { - - // Already exists. Free previous version - if (Icc ->TagSaveAsRaw[i]) { - _cmsFree(Icc ->ContextID, Icc ->TagPtrs[i]); - } - else { - TypeHandler = Icc ->TagTypeHandlers[i]; - - if (TypeHandler != NULL) { - - LocalTypeHandler = *TypeHandler; - LocalTypeHandler.ContextID = Icc ->ContextID; // As an additional parameter - LocalTypeHandler.ICCVersion = Icc ->Version; - LocalTypeHandler.FreePtr(&LocalTypeHandler, Icc -> TagPtrs[i]); - } - } - } - } - else { - // New one - i = Icc -> TagCount; - - if (i >= MAX_TABLE_TAG) { - cmsSignalError(Icc ->ContextID, cmsERROR_RANGE, "Too many tags (%d)", MAX_TABLE_TAG); - return FALSE; - } - - Icc -> TagCount++; - } + if (!_cmsNewTag(Icc, sig, &i)) goto Error; // This is not raw Icc ->TagSaveAsRaw[i] = FALSE; @@ -1560,10 +1593,10 @@ cmsBool CMSEXPORT cmsWriteTag(cmsHPROFILE hProfile, cmsTagSignature sig, const v Icc ->TagLinked[i] = (cmsTagSignature) 0; // Get information about the TAG. - TagDescriptor = _cmsGetTagDescriptor(sig); + TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig); if (TagDescriptor == NULL){ cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported tag '%x'", sig); - return FALSE; + goto Error; } @@ -1581,7 +1614,6 @@ cmsBool CMSEXPORT cmsWriteTag(cmsHPROFILE hProfile, cmsTagSignature sig, const v } else { - Type = TagDescriptor ->SupportedTypes[0]; } @@ -1592,18 +1624,18 @@ cmsBool CMSEXPORT cmsWriteTag(cmsHPROFILE hProfile, cmsTagSignature sig, const v _cmsTagSignature2String(SigString, sig); cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported type '%s' for tag '%s'", TypeString, SigString); - return FALSE; + goto Error; } // Does we have a handler for this type? - TypeHandler = _cmsGetTagTypeHandler(Type); + TypeHandler = _cmsGetTagTypeHandler(Icc->ContextID, Type); if (TypeHandler == NULL) { _cmsTagSignature2String(TypeString, (cmsTagSignature) Type); _cmsTagSignature2String(SigString, sig); cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported type '%s' for tag '%s'", TypeString, SigString); - return FALSE; // Should never happen + goto Error; // Should never happen } @@ -1616,7 +1648,7 @@ cmsBool CMSEXPORT cmsWriteTag(cmsHPROFILE hProfile, cmsTagSignature sig, const v LocalTypeHandler = *TypeHandler; LocalTypeHandler.ContextID = Icc ->ContextID; LocalTypeHandler.ICCVersion = Icc ->Version; - Icc ->TagPtrs[i] = LocalTypeHandler.DupPtr(&LocalTypeHandler, data, TagDescriptor ->ElemCount); + Icc ->TagPtrs[i] = LocalTypeHandler.DupPtr(&LocalTypeHandler, data, TagDescriptor ->ElemCount); if (Icc ->TagPtrs[i] == NULL) { @@ -1624,10 +1656,16 @@ cmsBool CMSEXPORT cmsWriteTag(cmsHPROFILE hProfile, cmsTagSignature sig, const v _cmsTagSignature2String(SigString, sig); cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "Malformed struct in type '%s' for tag '%s'", TypeString, SigString); - return FALSE; + goto Error; } + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); return TRUE; + +Error: + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return FALSE; + } // Read and write raw data. The only way those function would work and keep consistence with normal read and write @@ -1648,9 +1686,11 @@ cmsInt32Number CMSEXPORT cmsReadRawTag(cmsHPROFILE hProfile, cmsTagSignature sig cmsUInt32Number rc; cmsUInt32Number Offset, TagSize; + if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0; + // Search for given tag in ICC profile directory i = _cmsSearchTag(Icc, sig, TRUE); - if (i < 0) return 0; // Not found, return 0 + if (i < 0) goto Error; // Not found, // It is already read? if (Icc -> TagPtrs[i] == NULL) { @@ -1665,12 +1705,13 @@ cmsInt32Number CMSEXPORT cmsReadRawTag(cmsHPROFILE hProfile, cmsTagSignature sig if (BufferSize < TagSize) TagSize = BufferSize; - if (!Icc ->IOhandler ->Seek(Icc ->IOhandler, Offset)) return 0; - if (!Icc ->IOhandler ->Read(Icc ->IOhandler, data, 1, TagSize)) return 0; + if (!Icc ->IOhandler ->Seek(Icc ->IOhandler, Offset)) goto Error; + if (!Icc ->IOhandler ->Read(Icc ->IOhandler, data, 1, TagSize)) goto Error; return TagSize; } + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); return Icc ->TagSizes[i]; } @@ -1686,16 +1727,18 @@ cmsInt32Number CMSEXPORT cmsReadRawTag(cmsHPROFILE hProfile, cmsTagSignature sig memmove(data, Icc ->TagPtrs[i], TagSize); + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); return TagSize; } + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); return Icc ->TagSizes[i]; } // Already readed, or previously set by cmsWriteTag(). We need to serialize that // data to raw in order to maintain consistency. Object = cmsReadTag(hProfile, sig); - if (Object == NULL) return 0; + if (Object == NULL) goto Error; // Now we need to serialize to a memory block: just use a memory iohandler @@ -1704,17 +1747,18 @@ cmsInt32Number CMSEXPORT cmsReadRawTag(cmsHPROFILE hProfile, cmsTagSignature sig } else{ MemIO = cmsOpenIOhandlerFromMem(cmsGetProfileContextID(hProfile), data, BufferSize, "w"); } - if (MemIO == NULL) return 0; + if (MemIO == NULL) goto Error; // Obtain type handling for the tag TypeHandler = Icc ->TagTypeHandlers[i]; - TagDescriptor = _cmsGetTagDescriptor(sig); + TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig); if (TagDescriptor == NULL) { cmsCloseIOhandler(MemIO); - return 0; + goto Error; } + + if (TypeHandler == NULL) goto Error; - // FIXME: No handling for TypeHandler == NULL here? // Serialize LocalTypeHandler = *TypeHandler; LocalTypeHandler.ContextID = Icc ->ContextID; @@ -1722,19 +1766,24 @@ cmsInt32Number CMSEXPORT cmsReadRawTag(cmsHPROFILE hProfile, cmsTagSignature sig if (!_cmsWriteTypeBase(MemIO, TypeHandler ->Signature)) { cmsCloseIOhandler(MemIO); - return 0; + goto Error; } if (!LocalTypeHandler.WritePtr(&LocalTypeHandler, MemIO, Object, TagDescriptor ->ElemCount)) { cmsCloseIOhandler(MemIO); - return 0; + goto Error; } // Get Size and close rc = MemIO ->Tell(MemIO); cmsCloseIOhandler(MemIO); // Ignore return code this time + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); return rc; + +Error: + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return 0; } // Similar to the anterior. This function allows to write directly to the ICC profile any data, without @@ -1746,7 +1795,12 @@ cmsBool CMSEXPORT cmsWriteRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, cons _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; int i; - if (!_cmsNewTag(Icc, sig, &i)) return FALSE; + if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0; + + if (!_cmsNewTag(Icc, sig, &i)) { + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return FALSE; + } // Mark the tag as being written as RAW Icc ->TagSaveAsRaw[i] = TRUE; @@ -1757,6 +1811,7 @@ cmsBool CMSEXPORT cmsWriteRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, cons Icc ->TagPtrs[i] = _cmsDupMem(Icc ->ContextID, data, Size); Icc ->TagSizes[i] = Size; + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); return TRUE; } @@ -1766,7 +1821,12 @@ cmsBool CMSEXPORT cmsLinkTag(cmsHPROFILE hProfile, cmsTagSignature sig, cmsTagSi _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; int i; - if (!_cmsNewTag(Icc, sig, &i)) return FALSE; + if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return FALSE; + + if (!_cmsNewTag(Icc, sig, &i)) { + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return FALSE; + } // Keep necessary information Icc ->TagSaveAsRaw[i] = FALSE; @@ -1777,6 +1837,7 @@ cmsBool CMSEXPORT cmsLinkTag(cmsHPROFILE hProfile, cmsTagSignature sig, cmsTagSi Icc ->TagSizes[i] = 0; Icc ->TagOffsets[i] = 0; + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); return TRUE; } diff --git a/src/cmsio1.c b/src/cmsio1.c index 3262402..89856e5 100644 --- a/src/cmsio1.c +++ b/src/cmsio1.c @@ -528,7 +528,7 @@ void ChangeInterpolationToTrilinear(cmsPipeline* Lut) _cmsStageCLutData* CLUT = (_cmsStageCLutData*) Stage ->Data; CLUT ->Params->dwFlags |= CMS_LERP_FLAGS_TRILINEAR; - _cmsSetInterpolationRoutine(CLUT ->Params); + _cmsSetInterpolationRoutine(Lut->ContextID, CLUT ->Params); } } } diff --git a/src/cmsopt.c b/src/cmsopt.c index 4bdf0a7..bf95091 100644 --- a/src/cmsopt.c +++ b/src/cmsopt.c @@ -512,8 +512,14 @@ cmsBool FixWhiteMisalignment(cmsPipeline* Lut, cmsColorSpaceSignature EntryColor for (i=0; i < nOuts; i++) { cmsToneCurve* InversePostLin = cmsReverseToneCurve(Curves[i]); - WhiteOut[i] = cmsEvalToneCurve16(InversePostLin, WhitePointOut[i]); - cmsFreeToneCurve(InversePostLin); + if (InversePostLin == NULL) { + WhiteOut[i] = WhitePointOut[i]; + + } else { + + WhiteOut[i] = cmsEvalToneCurve16(InversePostLin, WhitePointOut[i]); + cmsFreeToneCurve(InversePostLin); + } } } else { @@ -1633,44 +1639,102 @@ static _cmsOptimizationCollection DefaultOptimization[] = { }; // The linked list head -static _cmsOptimizationCollection* OptimizationCollection = DefaultOptimization; +_cmsOptimizationPluginChunkType _cmsOptimizationPluginChunk = { NULL }; + + +// Duplicates the zone of memory used by the plug-in in the new context +static +void DupPluginOptimizationList(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + _cmsOptimizationPluginChunkType newHead = { NULL }; + _cmsOptimizationCollection* entry; + _cmsOptimizationCollection* Anterior = NULL; + _cmsOptimizationPluginChunkType* head = (_cmsOptimizationPluginChunkType*) src->chunks[OptimizationPlugin]; + + _cmsAssert(ctx != NULL); + _cmsAssert(head != NULL); + + // Walk the list copying all nodes + for (entry = head->OptimizationCollection; + entry != NULL; + entry = entry ->Next) { + + _cmsOptimizationCollection *newEntry = ( _cmsOptimizationCollection *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsOptimizationCollection)); + + if (newEntry == NULL) + return; + + // We want to keep the linked list order, so this is a little bit tricky + newEntry -> Next = NULL; + if (Anterior) + Anterior -> Next = newEntry; + + Anterior = newEntry; + + if (newHead.OptimizationCollection == NULL) + newHead.OptimizationCollection = newEntry; + } + + ctx ->chunks[OptimizationPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsOptimizationPluginChunkType)); +} + +void _cmsAllocOptimizationPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + if (src != NULL) { + + // Copy all linked list + DupPluginOptimizationList(ctx, src); + } + else { + static _cmsOptimizationPluginChunkType OptimizationPluginChunkType = { NULL }; + ctx ->chunks[OptimizationPlugin] = _cmsSubAllocDup(ctx ->MemPool, &OptimizationPluginChunkType, sizeof(_cmsOptimizationPluginChunkType)); + } +} + // Register new ways to optimize -cmsBool _cmsRegisterOptimizationPlugin(cmsContext id, cmsPluginBase* Data) +cmsBool _cmsRegisterOptimizationPlugin(cmsContext ContextID, cmsPluginBase* Data) { cmsPluginOptimization* Plugin = (cmsPluginOptimization*) Data; + _cmsOptimizationPluginChunkType* ctx = ( _cmsOptimizationPluginChunkType*) _cmsContextGetClientChunk(ContextID, OptimizationPlugin); _cmsOptimizationCollection* fl; if (Data == NULL) { - OptimizationCollection = DefaultOptimization; + ctx->OptimizationCollection = NULL; return TRUE; } // Optimizer callback is required if (Plugin ->OptimizePtr == NULL) return FALSE; - fl = (_cmsOptimizationCollection*) _cmsPluginMalloc(id, sizeof(_cmsOptimizationCollection)); + fl = (_cmsOptimizationCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsOptimizationCollection)); if (fl == NULL) return FALSE; // Copy the parameters fl ->OptimizePtr = Plugin ->OptimizePtr; // Keep linked list - fl ->Next = OptimizationCollection; - OptimizationCollection = fl; + fl ->Next = ctx->OptimizationCollection; + + // Set the head + ctx ->OptimizationCollection = fl; // All is ok return TRUE; } // The entry point for LUT optimization -cmsBool _cmsOptimizePipeline(cmsPipeline** PtrLut, +cmsBool _cmsOptimizePipeline(cmsContext ContextID, + cmsPipeline** PtrLut, int Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) { + _cmsOptimizationPluginChunkType* ctx = ( _cmsOptimizationPluginChunkType*) _cmsContextGetClientChunk(ContextID, OptimizationPlugin); _cmsOptimizationCollection* Opts; cmsBool AnySuccess = FALSE; @@ -1700,8 +1764,8 @@ cmsBool _cmsOptimizePipeline(cmsPipeline** PtrLut, if (*dwFlags & cmsFLAGS_NOOPTIMIZE) return FALSE; - // Try built-in optimizations and plug-in - for (Opts = OptimizationCollection; + // Try plug-in optimizations + for (Opts = ctx->OptimizationCollection; Opts != NULL; Opts = Opts ->Next) { @@ -1712,6 +1776,17 @@ cmsBool _cmsOptimizePipeline(cmsPipeline** PtrLut, } } + // Try built-in optimizations + for (Opts = DefaultOptimization; + Opts != NULL; + Opts = Opts ->Next) { + + if (Opts ->OptimizePtr(PtrLut, Intent, InputFormat, OutputFormat, dwFlags)) { + + return TRUE; + } + } + // Only simple optimizations succeeded return AnySuccess; } diff --git a/src/cmspack.c b/src/cmspack.c index 6509e89..c84fd82 100644 --- a/src/cmspack.c +++ b/src/cmspack.c @@ -2352,9 +2352,9 @@ cmsUInt8Number* PackXYZFloatFrom16(register _cmsTRANSFORM* Info, cmsFloat32Number* Out = (cmsFloat32Number*) output; cmsXYZEncoded2Float(&XYZ, wOut); - Out[0] = XYZ.X; - Out[Stride] = XYZ.Y; - Out[Stride*2] = XYZ.Z; + Out[0] = (cmsFloat32Number) XYZ.X; + Out[Stride] = (cmsFloat32Number) XYZ.Y; + Out[Stride*2] = (cmsFloat32Number) XYZ.Z; return output + sizeof(cmsFloat32Number); @@ -2365,9 +2365,9 @@ cmsUInt8Number* PackXYZFloatFrom16(register _cmsTRANSFORM* Info, cmsFloat32Number* Out = (cmsFloat32Number*) output; cmsXYZEncoded2Float(&XYZ, wOut); - Out[0] = XYZ.X; - Out[1] = XYZ.Y; - Out[2] = XYZ.Z; + Out[0] = (cmsFloat32Number) XYZ.X; + Out[1] = (cmsFloat32Number) XYZ.Y; + Out[2] = (cmsFloat32Number) XYZ.Z; return output + (3 * sizeof(cmsFloat32Number) + T_EXTRA(Info ->OutputFormat) * sizeof(cmsFloat32Number)); } @@ -3224,40 +3224,98 @@ typedef struct _cms_formatters_factory_list { } cmsFormattersFactoryList; -static cmsFormattersFactoryList* FactoryList = NULL; +_cmsFormattersPluginChunkType _cmsFormattersPluginChunk = { NULL }; + + +// Duplicates the zone of memory used by the plug-in in the new context +static +void DupFormatterFactoryList(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + _cmsFormattersPluginChunkType newHead = { NULL }; + cmsFormattersFactoryList* entry; + cmsFormattersFactoryList* Anterior = NULL; + _cmsFormattersPluginChunkType* head = (_cmsFormattersPluginChunkType*) src->chunks[FormattersPlugin]; + + _cmsAssert(head != NULL); + + // Walk the list copying all nodes + for (entry = head->FactoryList; + entry != NULL; + entry = entry ->Next) { + + cmsFormattersFactoryList *newEntry = ( cmsFormattersFactoryList *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(cmsFormattersFactoryList)); + + if (newEntry == NULL) + return; + + // We want to keep the linked list order, so this is a little bit tricky + newEntry -> Next = NULL; + if (Anterior) + Anterior -> Next = newEntry; + + Anterior = newEntry; + + if (newHead.FactoryList == NULL) + newHead.FactoryList = newEntry; + } + + ctx ->chunks[FormattersPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsFormattersPluginChunkType)); +} + +// The interpolation plug-in memory chunk allocator/dup +void _cmsAllocFormattersPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + _cmsAssert(ctx != NULL); + + if (src != NULL) { + + // Duplicate the LIST + DupFormatterFactoryList(ctx, src); + } + else { + static _cmsFormattersPluginChunkType FormattersPluginChunk = { NULL }; + ctx ->chunks[FormattersPlugin] = _cmsSubAllocDup(ctx ->MemPool, &FormattersPluginChunk, sizeof(_cmsFormattersPluginChunkType)); + } +} + // Formatters management -cmsBool _cmsRegisterFormattersPlugin(cmsContext id, cmsPluginBase* Data) +cmsBool _cmsRegisterFormattersPlugin(cmsContext ContextID, cmsPluginBase* Data) { + _cmsFormattersPluginChunkType* ctx = ( _cmsFormattersPluginChunkType*) _cmsContextGetClientChunk(ContextID, FormattersPlugin); cmsPluginFormatters* Plugin = (cmsPluginFormatters*) Data; cmsFormattersFactoryList* fl ; - // Reset + // Reset to built-in defaults if (Data == NULL) { - FactoryList = NULL; + ctx ->FactoryList = NULL; return TRUE; } - fl = (cmsFormattersFactoryList*) _cmsPluginMalloc(id, sizeof(cmsFormattersFactoryList)); + fl = (cmsFormattersFactoryList*) _cmsPluginMalloc(ContextID, sizeof(cmsFormattersFactoryList)); if (fl == NULL) return FALSE; fl ->Factory = Plugin ->FormattersFactory; - fl ->Next = FactoryList; - FactoryList = fl; + fl ->Next = ctx -> FactoryList; + ctx ->FactoryList = fl; return TRUE; } -cmsFormatter _cmsGetFormatter(cmsUInt32Number Type, // Specific type, i.e. TYPE_RGB_8 +cmsFormatter _cmsGetFormatter(cmsContext ContextID, + cmsUInt32Number Type, // Specific type, i.e. TYPE_RGB_8 cmsFormatterDirection Dir, cmsUInt32Number dwFlags) { + _cmsFormattersPluginChunkType* ctx = ( _cmsFormattersPluginChunkType*) _cmsContextGetClientChunk(ContextID, FormattersPlugin); cmsFormattersFactoryList* f; - for (f = FactoryList; f != NULL; f = f ->Next) { + for (f =ctx->FactoryList; f != NULL; f = f ->Next) { cmsFormatter fn = f ->Factory(Type, Dir, dwFlags); if (fn.Fmt16 != NULL) return fn; diff --git a/src/cmsplugin.c b/src/cmsplugin.c index e334fac..7c58b60 100644 --- a/src/cmsplugin.c +++ b/src/cmsplugin.c @@ -515,15 +515,18 @@ cmsBool CMSEXPORT _cmsIOPrintf(cmsIOHANDLER* io, const char* frm, ...) // Plugin memory management ------------------------------------------------------------------------------------------------- -static _cmsSubAllocator* PluginPool = NULL; - // Specialized malloc for plug-ins, that is freed upon exit. -void* _cmsPluginMalloc(cmsContext id, cmsUInt32Number size) +void* _cmsPluginMalloc(cmsContext ContextID, cmsUInt32Number size) { - if (PluginPool == NULL) - PluginPool = _cmsCreateSubAlloc(id, 4*1024); + struct _cmsContext_struct* ctx = _cmsGetContext(ContextID); + + if (ctx ->MemPool == NULL) { + + cmsSignalError(ContextID, cmsERROR_CORRUPTION_DETECTED, "NULL memory pool on context"); + return NULL; + } - return _cmsSubAlloc(PluginPool, size); + return _cmsSubAlloc(ctx->MemPool, size); } @@ -542,12 +545,12 @@ cmsBool CMSEXPORT cmsPluginTHR(cmsContext id, void* Plug_in) Plugin = Plugin -> Next) { if (Plugin -> Magic != cmsPluginMagicNumber) { - cmsSignalError(0, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin"); + cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin"); return FALSE; } if (Plugin ->ExpectedVersion > LCMS_VERSION) { - cmsSignalError(0, cmsERROR_UNKNOWN_EXTENSION, "plugin needs Little CMS %d, current version is %d", + cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "plugin needs Little CMS %d, current version is %d", Plugin ->ExpectedVersion, LCMS_VERSION); return FALSE; } @@ -555,11 +558,11 @@ cmsBool CMSEXPORT cmsPluginTHR(cmsContext id, void* Plug_in) switch (Plugin -> Type) { case cmsPluginMemHandlerSig: - if (!_cmsRegisterMemHandlerPlugin(Plugin)) return FALSE; + if (!_cmsRegisterMemHandlerPlugin(id, Plugin)) return FALSE; break; case cmsPluginInterpolationSig: - if (!_cmsRegisterInterpPlugin(Plugin)) return FALSE; + if (!_cmsRegisterInterpPlugin(id, Plugin)) return FALSE; break; case cmsPluginTagTypeSig: @@ -594,8 +597,12 @@ cmsBool CMSEXPORT cmsPluginTHR(cmsContext id, void* Plug_in) if (!_cmsRegisterTransformPlugin(id, Plugin)) return FALSE; break; + case cmsPluginMutexSig: + if (!_cmsRegisterMutexPlugin(id, Plugin)) return FALSE; + break; + default: - cmsSignalError(0, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin type '%X'", Plugin -> Type); + cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin type '%X'", Plugin -> Type); return FALSE; } } @@ -608,19 +615,322 @@ cmsBool CMSEXPORT cmsPluginTHR(cmsContext id, void* Plug_in) // Revert all plug-ins to default void CMSEXPORT cmsUnregisterPlugins(void) { - _cmsRegisterMemHandlerPlugin(NULL); - _cmsRegisterInterpPlugin(NULL); - _cmsRegisterTagTypePlugin(NULL, NULL); - _cmsRegisterTagPlugin(NULL, NULL); - _cmsRegisterFormattersPlugin(NULL, NULL); - _cmsRegisterRenderingIntentPlugin(NULL, NULL); - _cmsRegisterParametricCurvesPlugin(NULL, NULL); - _cmsRegisterMultiProcessElementPlugin(NULL, NULL); - _cmsRegisterOptimizationPlugin(NULL, NULL); - _cmsRegisterTransformPlugin(NULL, NULL); + cmsUnregisterPluginsTHR(NULL); +} + + +// The Global storage for system context. This is the one and only global variable +// pointers structure. All global vars are referenced here. +static struct _cmsContext_struct globalContext = { + + cmsContextMagicNumber, + NULL, // No suballocator + { + NULL, // UserPtr, + &_cmsLogErrorChunk, // Logger, + &_cmsAlarmCodesChunk, // AlarmCodes, + &_cmsAdaptationStateChunk, // AdaptationState, + &_cmsMemPluginChunk, // MemPlugin, + &_cmsInterpPluginChunk, // InterpPlugin, + &_cmsCurvesPluginChunk, // CurvesPlugin, + &_cmsFormattersPluginChunk, // FormattersPlugin, + &_cmsTagTypePluginChunk, // TagTypePlugin, + &_cmsTagPluginChunk, // TagPlugin, + &_cmsIntentsPluginChunk, // IntentPlugin, + &_cmsMPETypePluginChunk, // MPEPlugin, + &_cmsOptimizationPluginChunk, // OptimizationPlugin, + &_cmsTransformPluginChunk, // TransformPlugin, + &_cmsMutexPluginChunk // MutexPlugin + }, + + { NULL, NULL, NULL, NULL, NULL, NULL } // The default memory allocator is not used for context 0 +}; + + +// This is a replacement for (intptr_t), which despite C99, is not supported by some compilers. +// Clever idea from Robin, to use pointer substraction. +#define PTR_TO_INT(ptr) (((cmsUInt8Number*) (ptr)) - (cmsUInt8Number*) 0) + +// Internal, get associated pointer, with guessing. Never returns NULL. +struct _cmsContext_struct* _cmsGetContext(cmsContext ContextID) +{ +#ifdef CMS_CONTEXT_IN_LEGACY_MODE + + return &globalContext; + cmsUNUSED_PARAMETER(ContextID); +#else + struct _cmsContext_struct* ctx = (struct _cmsContext_struct*) ContextID; + + // Check for low numbers. Yes, I know, it is a hack... but it works + if ((PTR_TO_INT(ContextID) < 0x1000) && (PTR_TO_INT(ContextID) > 0)) + return &globalContext; + + // On 0, use global settings + if (ctx == NULL) + return &globalContext; + + // Validate across magic number + if (ctx ->Magic != cmsContextMagicNumber) + return &globalContext; + + // New-style context, + return ctx; +#endif +} + + +// Internal: get the memory area associanted with each context client +// Returns the block assigned to the specific zone. +void* _cmsContextGetClientChunk(cmsContext ContextID, _cmsMemoryClient mc) +{ + struct _cmsContext_struct* ctx; + void *ptr; + + if (mc < 0 || mc >= MemoryClientMax) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Bad context client"); + return NULL; + } + + ctx = _cmsGetContext(ContextID); + ptr = ctx ->chunks[mc]; - if (PluginPool != NULL) - _cmsSubAllocDestroy(PluginPool); + if (ptr != NULL) + return ptr; - PluginPool = NULL; + // A null ptr means no special settings for that context, and this + // reverts to Context0 globals + return globalContext.chunks[mc]; } + + +// This function returns the given context its default pristine state, +// as no plug-ins were declared. There is no way to unregister a single +// plug-in, as a single call to cmsPluginTHR() function may register +// many different plug-ins simultaneously, then there is no way to +// identify which plug-in to unregister. +void CMSEXPORT cmsUnregisterPluginsTHR(cmsContext ContextID) +{ + _cmsRegisterMemHandlerPlugin(ContextID, NULL); + _cmsRegisterInterpPlugin(ContextID, NULL); + _cmsRegisterTagTypePlugin(ContextID, NULL); + _cmsRegisterTagPlugin(ContextID, NULL); + _cmsRegisterFormattersPlugin(ContextID, NULL); + _cmsRegisterRenderingIntentPlugin(ContextID, NULL); + _cmsRegisterParametricCurvesPlugin(ContextID, NULL); + _cmsRegisterMultiProcessElementPlugin(ContextID, NULL); + _cmsRegisterOptimizationPlugin(ContextID, NULL); + _cmsRegisterTransformPlugin(ContextID, NULL); + _cmsRegisterMutexPlugin(ContextID, NULL); +} + + +// Returns the memory manager plug-in, if any, from the Plug-in bundle +static +cmsPluginMemHandler* _cmsFindMemoryPlugin(void* PluginBundle) +{ + cmsPluginBase* Plugin; + + for (Plugin = (cmsPluginBase*) PluginBundle; + Plugin != NULL; + Plugin = Plugin -> Next) { + + if (Plugin -> Magic == cmsPluginMagicNumber && + Plugin -> ExpectedVersion <= LCMS_VERSION && + Plugin -> Type == cmsPluginMemHandlerSig) { + + // Found! + return (cmsPluginMemHandler*) Plugin; + } + } + + // Nope, revert to defaults + return NULL; +} + + +// Creates a new context with optional associated plug-ins. Caller may also specify an optional pointer to user-defined +// data that will be forwarded to plug-ins and logger. +cmsContext CMSEXPORT cmsCreateContext(void* Plugin, void* UserData) +{ +#ifdef CMS_CONTEXT_IN_LEGACY_MODE + cmsSignalError(0, cmsERROR_NOT_SUITABLE, "Lcms is compiled as legacy context mode and you called an advanced context function"); + return UserData; + + cmsUNUSED_PARAMETER(Plugin); + cmsUNUSED_PARAMETER(UserData); +#else + + + struct _cmsContext_struct* ctx; + struct _cmsContext_struct fakeContext; + + _cmsInstallAllocFunctions(_cmsFindMemoryPlugin(Plugin), &fakeContext.DefaultMemoryManager); + + fakeContext.chunks[UserPtr] = UserData; + fakeContext.chunks[MemPlugin] = &fakeContext.DefaultMemoryManager; + + // Create the context structure. + ctx = (struct _cmsContext_struct*) _cmsMalloc(&fakeContext, sizeof(struct _cmsContext_struct)); + if (ctx == NULL) + return NULL; // Something very wrong happened! + + // Init the structure and the memory manager + memset(ctx, 0, sizeof(struct _cmsContext_struct)); + + // Keep memory manager + memcpy(&ctx->DefaultMemoryManager, &fakeContext.DefaultMemoryManager, sizeof(_cmsMemPluginChunk)); + + // Identify it as a context + ctx ->Magic = cmsContextMagicNumber; + + ctx ->chunks[UserPtr] = UserData; + ctx ->chunks[MemPlugin] = &ctx->DefaultMemoryManager; + + // Now we can allocate the pool by using default memory manager + ctx ->MemPool = _cmsCreateSubAlloc(ctx, 22 * sizeof(void*)); // default size about 32 pointers + if (ctx ->MemPool == NULL) { + + cmsDeleteContext(ctx); + return NULL; + } + + _cmsAllocLogErrorChunk(ctx, NULL); + _cmsAllocAlarmCodesChunk(ctx, NULL); + _cmsAllocAdaptationStateChunk(ctx, NULL); + _cmsAllocMemPluginChunk(ctx, NULL); + _cmsAllocInterpPluginChunk(ctx, NULL); + _cmsAllocCurvesPluginChunk(ctx, NULL); + _cmsAllocFormattersPluginChunk(ctx, NULL); + _cmsAllocTagTypePluginChunk(ctx, NULL); + _cmsAllocMPETypePluginChunk(ctx, NULL); + _cmsAllocTagPluginChunk(ctx, NULL); + _cmsAllocIntentsPluginChunk(ctx, NULL); + _cmsAllocOptimizationPluginChunk(ctx, NULL); + _cmsAllocTransformPluginChunk(ctx, NULL); + _cmsAllocMutexPluginChunk(ctx, NULL); + + // Setup the plug-ins + if (!cmsPluginTHR(ctx, Plugin)) { + + cmsDeleteContext(ctx); + return NULL; + } + + return (cmsContext) ctx; +#endif +} + +// Duplicates a context with all associated plug-ins. +// Caller may specify an optional pointer to user-defined +// data that will be forwarded to plug-ins and logger. +cmsContext CMSEXPORT cmsDupContext(cmsContext ContextID, void* NewUserData) +{ +#ifdef CMS_CONTEXT_IN_LEGACY_MODE + cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Lcms is compiled as legacy context mode and you called an advanced context function"); + return ContextID; + cmsUNUSED_PARAMETER(NewUserData); + +#else + int i; + struct _cmsContext_struct* ctx; + const struct _cmsContext_struct* src = _cmsGetContext(ContextID); + + void* userData = (NewUserData != NULL) ? NewUserData : src -> chunks[UserPtr]; + + + ctx = (struct _cmsContext_struct*) _cmsMalloc(ContextID, sizeof(struct _cmsContext_struct)); + if (ctx == NULL) + return NULL; // Something very wrong happened + + // Setup default memory allocators + memcpy(&ctx->DefaultMemoryManager, &src->DefaultMemoryManager, sizeof(ctx->DefaultMemoryManager)); + + ctx ->Magic = cmsContextMagicNumber; + + ctx ->chunks[UserPtr] = userData; + ctx ->chunks[MemPlugin] = &ctx->DefaultMemoryManager; + + ctx ->MemPool = _cmsCreateSubAlloc(ctx, 22 * sizeof(void*)); + if (ctx ->MemPool == NULL) { + + cmsDeleteContext(ctx); + return NULL; + } + + // Allocate all required chunks. + _cmsAllocLogErrorChunk(ctx, src); + _cmsAllocAlarmCodesChunk(ctx, src); + _cmsAllocAdaptationStateChunk(ctx, src); + _cmsAllocMemPluginChunk(ctx, src); + _cmsAllocInterpPluginChunk(ctx, src); + _cmsAllocCurvesPluginChunk(ctx, src); + _cmsAllocFormattersPluginChunk(ctx, src); + _cmsAllocTagTypePluginChunk(ctx, src); + _cmsAllocMPETypePluginChunk(ctx, src); + _cmsAllocTagPluginChunk(ctx, src); + _cmsAllocIntentsPluginChunk(ctx, src); + _cmsAllocOptimizationPluginChunk(ctx, src); + _cmsAllocTransformPluginChunk(ctx, src); + _cmsAllocMutexPluginChunk(ctx, src); + + // Make sure no one failed + for (i=Logger; i < MemoryClientMax; i++) { + + if (src ->chunks[i] == NULL) { + cmsDeleteContext((cmsContext) ctx); + return NULL; + } + } + + return (cmsContext) ctx; +#endif + +} + +// Frees any resources associated with the given context, +// and destroys the context placeholder. +// The ContextID can no longer be used in any THR operation. +void CMSEXPORT cmsDeleteContext(cmsContext ContextID) +{ +#ifdef CMS_CONTEXT_IN_LEGACY_MODE + cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Lcms is compiled as legacy context mode and you called an advanced context function"); + cmsUNUSED_PARAMETER(ContextID); +#else + + if (ContextID != NULL) { + + struct _cmsContext_struct* ctx = (struct _cmsContext_struct*) ContextID; + struct _cmsContext_struct fakeContext; + + memcpy(&fakeContext.DefaultMemoryManager, &ctx->DefaultMemoryManager, sizeof(ctx->DefaultMemoryManager)); + + fakeContext.chunks[UserPtr] = ctx ->chunks[UserPtr]; + fakeContext.chunks[MemPlugin] = &fakeContext.DefaultMemoryManager; + + + // Get rid of plugins + cmsUnregisterPluginsTHR(ContextID); + + // Since all memory is allocated in the private pool, all what we need to do is destroy the pool + if (ctx -> MemPool != NULL) + _cmsSubAllocDestroy(ctx ->MemPool); + ctx -> MemPool = NULL; + + // free the memory block itself + _cmsFree(&fakeContext, ctx); + } +#endif +} + +// Returns the user data associated to the given ContextID, or NULL if no user data was attached on context creation +void* CMSEXPORT cmsGetContextUserData(cmsContext ContextID) +{ +#ifdef CMS_CONTEXT_IN_LEGACY_MODE + cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Lcms is compiled as legacy context mode and you called an advanced context function"); + return ContextID; +#else + return _cmsContextGetClientChunk(ContextID, UserPtr); +#endif +} + + diff --git a/src/cmsps2.c b/src/cmsps2.c index fe91500..224b44b 100644 --- a/src/cmsps2.c +++ b/src/cmsps2.c @@ -913,7 +913,7 @@ int WriteInputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, int Intent, cmsUInt32Nu if (DeviceLink == NULL) return 0; dwFlags |= cmsFLAGS_FORCE_CLUT; - _cmsOptimizePipeline(&DeviceLink, Intent, &InputFormat, &OutFrm, &dwFlags); + _cmsOptimizePipeline(m->ContextID, &DeviceLink, Intent, &InputFormat, &OutFrm, &dwFlags); rc = EmitCIEBasedDEF(m, DeviceLink, Intent, &BlackPointAdaptedToD50); cmsPipelineFree(DeviceLink); @@ -1330,7 +1330,7 @@ int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, int Intent, cmsUInt32N // We need a CLUT dwFlags |= cmsFLAGS_FORCE_CLUT; - _cmsOptimizePipeline(&DeviceLink, RelativeEncodingIntent, &InFrm, &OutputFormat, &dwFlags); + _cmsOptimizePipeline(m->ContextID, &DeviceLink, RelativeEncodingIntent, &InFrm, &OutputFormat, &dwFlags); _cmsIOPrintf(m, "<<\n"); _cmsIOPrintf(m, "/ColorRenderingType 1\n"); diff --git a/src/cmstypes.c b/src/cmstypes.c index 9114a6e..a9e3fa0 100644 --- a/src/cmstypes.c +++ b/src/cmstypes.c @@ -60,54 +60,49 @@ typedef struct _cmsTagTypeLinkedList_st { // Helper macro to define a MPE handler. Callbacks do have a fixed naming convention #define TYPE_MPE_HANDLER(t, x) { (t), READ_FN(x), WRITE_FN(x), GenericMPEdup, GenericMPEfree, NULL, 0 } -// Register a new type handler. This routine is shared between normal types and MPE +// Register a new type handler. This routine is shared between normal types and MPE. LinkedList points to the optional list head static -cmsBool RegisterTypesPlugin(cmsContext id, cmsPluginBase* Data, _cmsTagTypeLinkedList* LinkedList, cmsUInt32Number DefaultListCount) +cmsBool RegisterTypesPlugin(cmsContext id, cmsPluginBase* Data, _cmsMemoryClient pos) { cmsPluginTagType* Plugin = (cmsPluginTagType*) Data; - _cmsTagTypeLinkedList *pt, *Anterior = NULL; + _cmsTagTypePluginChunkType* ctx = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(id, pos); + _cmsTagTypeLinkedList *pt; // Calling the function with NULL as plug-in would unregister the plug in. if (Data == NULL) { - LinkedList[DefaultListCount-1].Next = NULL; + // There is no need to set free the memory, as pool is destroyed as a whole. + ctx ->TagTypes = NULL; return TRUE; } - pt = Anterior = LinkedList; - while (pt != NULL) { - - if (Plugin->Handler.Signature == pt -> Handler.Signature) { - pt ->Handler = Plugin ->Handler; // Replace old behaviour. - // Note that since no memory is allocated, unregister does not - // reset this action. - return TRUE; - } - - Anterior = pt; - pt = pt ->Next; - } - - // Registering happens in plug-in memory pool + // Registering happens in plug-in memory pool. pt = (_cmsTagTypeLinkedList*) _cmsPluginMalloc(id, sizeof(_cmsTagTypeLinkedList)); if (pt == NULL) return FALSE; pt ->Handler = Plugin ->Handler; - pt ->Next = NULL; - - if (Anterior) - Anterior -> Next = pt; + pt ->Next = ctx ->TagTypes; + ctx ->TagTypes = pt; + return TRUE; } -// Return handler for a given type or NULL if not found. Shared between normal types and MPE +// Return handler for a given type or NULL if not found. Shared between normal types and MPE. It first tries the additons +// made by plug-ins and then the built-in defaults. static -cmsTagTypeHandler* GetHandler(cmsTagTypeSignature sig, _cmsTagTypeLinkedList* LinkedList) +cmsTagTypeHandler* GetHandler(cmsTagTypeSignature sig, _cmsTagTypeLinkedList* PluginLinkedList, _cmsTagTypeLinkedList* DefaultLinkedList) { _cmsTagTypeLinkedList* pt; - for (pt = LinkedList; + for (pt = PluginLinkedList; + pt != NULL; + pt = pt ->Next) { + + if (sig == pt -> Handler.Signature) return &pt ->Handler; + } + + for (pt = DefaultLinkedList; pt != NULL; pt = pt ->Next) { @@ -134,6 +129,7 @@ cmsBool _cmsWriteWCharArray(cmsIOHANDLER* io, cmsUInt32Number n, const wchar_t* return TRUE; } +// Auxiliar to read an array of wchar_t static cmsBool _cmsReadWCharArray(cmsIOHANDLER* io, cmsUInt32Number n, wchar_t* Array) { @@ -748,6 +744,8 @@ cmsBool Type_Text_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, // Create memory Text = (char*) _cmsMalloc(self ->ContextID, size); + if (Text == NULL) return FALSE; + cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, Text, size); // Write it, including separator @@ -4355,7 +4353,7 @@ static _cmsTagTypeLinkedList SupportedMPEtypes[] = { {TYPE_MPE_HANDLER((cmsTagTypeSignature) cmsSigCLutElemType, MPEclut), NULL }, }; -#define DEFAULT_MPE_TYPE_COUNT (sizeof(SupportedMPEtypes) / sizeof(_cmsTagTypeLinkedList)) +_cmsTagTypePluginChunkType _cmsMPETypePluginChunk = { NULL }; static cmsBool ReadMPEElem(struct _cms_typehandler_struct* self, @@ -4368,6 +4366,8 @@ cmsBool ReadMPEElem(struct _cms_typehandler_struct* self, cmsTagTypeHandler* TypeHandler; cmsUInt32Number nItems; cmsPipeline *NewLUT = (cmsPipeline *) Cargo; + _cmsTagTypePluginChunkType* MPETypePluginChunk = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(self->ContextID, MPEPlugin); + // Take signature and channels for each element. if (!_cmsReadUInt32Number(io, (cmsUInt32Number*) &ElementSig)) return FALSE; @@ -4376,7 +4376,7 @@ cmsBool ReadMPEElem(struct _cms_typehandler_struct* self, if (!_cmsReadUInt32Number(io, NULL)) return FALSE; // Read diverse MPE types - TypeHandler = GetHandler((cmsTagTypeSignature) ElementSig, SupportedMPEtypes); + TypeHandler = GetHandler((cmsTagTypeSignature) ElementSig, MPETypePluginChunk ->TagTypes, SupportedMPEtypes); if (TypeHandler == NULL) { char String[5]; @@ -4453,6 +4453,7 @@ cmsBool Type_MPE_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, v cmsPipeline* Lut = (cmsPipeline*) Ptr; cmsStage* Elem = Lut ->Elements; cmsTagTypeHandler* TypeHandler; + _cmsTagTypePluginChunkType* MPETypePluginChunk = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(self->ContextID, MPEPlugin); BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); @@ -4486,7 +4487,7 @@ cmsBool Type_MPE_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, v ElementSig = Elem ->Type; - TypeHandler = GetHandler((cmsTagTypeSignature) ElementSig, SupportedMPEtypes); + TypeHandler = GetHandler((cmsTagTypeSignature) ElementSig, MPETypePluginChunk->TagTypes, SupportedMPEtypes); if (TypeHandler == NULL) { char String[5]; @@ -5263,24 +5264,95 @@ static _cmsTagTypeLinkedList SupportedTagTypes[] = { {TYPE_HANDLER(cmsSigVcgtType, vcgt), NULL } }; -#define DEFAULT_TAG_TYPE_COUNT (sizeof(SupportedTagTypes) / sizeof(_cmsTagTypeLinkedList)) + +_cmsTagTypePluginChunkType _cmsTagTypePluginChunk = { NULL }; + + + +// Duplicates the zone of memory used by the plug-in in the new context +static +void DupTagTypeList(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src, + int loc) +{ + _cmsTagTypePluginChunkType newHead = { NULL }; + _cmsTagTypeLinkedList* entry; + _cmsTagTypeLinkedList* Anterior = NULL; + _cmsTagTypePluginChunkType* head = (_cmsTagTypePluginChunkType*) src->chunks[loc]; + + // Walk the list copying all nodes + for (entry = head->TagTypes; + entry != NULL; + entry = entry ->Next) { + + _cmsTagTypeLinkedList *newEntry = ( _cmsTagTypeLinkedList *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTagTypeLinkedList)); + + if (newEntry == NULL) + return; + + // We want to keep the linked list order, so this is a little bit tricky + newEntry -> Next = NULL; + if (Anterior) + Anterior -> Next = newEntry; + + Anterior = newEntry; + + if (newHead.TagTypes == NULL) + newHead.TagTypes = newEntry; + } + + ctx ->chunks[loc] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsTagTypePluginChunkType)); +} + + +void _cmsAllocTagTypePluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + if (src != NULL) { + + // Duplicate the LIST + DupTagTypeList(ctx, src, TagTypePlugin); + } + else { + static _cmsTagTypePluginChunkType TagTypePluginChunk = { NULL }; + ctx ->chunks[TagTypePlugin] = _cmsSubAllocDup(ctx ->MemPool, &TagTypePluginChunk, sizeof(_cmsTagTypePluginChunkType)); + } +} + +void _cmsAllocMPETypePluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + if (src != NULL) { + + // Duplicate the LIST + DupTagTypeList(ctx, src, MPEPlugin); + } + else { + static _cmsTagTypePluginChunkType TagTypePluginChunk = { NULL }; + ctx ->chunks[MPEPlugin] = _cmsSubAllocDup(ctx ->MemPool, &TagTypePluginChunk, sizeof(_cmsTagTypePluginChunkType)); + } + +} + // Both kind of plug-ins share same structure cmsBool _cmsRegisterTagTypePlugin(cmsContext id, cmsPluginBase* Data) { - return RegisterTypesPlugin(id, Data, SupportedTagTypes, DEFAULT_TAG_TYPE_COUNT); + return RegisterTypesPlugin(id, Data, TagTypePlugin); } cmsBool _cmsRegisterMultiProcessElementPlugin(cmsContext id, cmsPluginBase* Data) { - return RegisterTypesPlugin(id, Data, SupportedMPEtypes, DEFAULT_MPE_TYPE_COUNT); + return RegisterTypesPlugin(id, Data,MPEPlugin); } // Wrapper for tag types -cmsTagTypeHandler* _cmsGetTagTypeHandler(cmsTagTypeSignature sig) +cmsTagTypeHandler* _cmsGetTagTypeHandler(cmsContext ContextID, cmsTagTypeSignature sig) { - return GetHandler(sig, SupportedTagTypes); + _cmsTagTypePluginChunkType* ctx = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(ContextID, TagTypePlugin); + + return GetHandler(sig, ctx->TagTypes, SupportedTagTypes); } // ******************************************************************************** @@ -5395,30 +5467,68 @@ static _cmsTagLinkedList SupportedTags[] = { cmsSigDeviceSettingsTag ==> Deprecated, useless */ -#define DEFAULT_TAG_COUNT (sizeof(SupportedTags) / sizeof(_cmsTagLinkedList)) -cmsBool _cmsRegisterTagPlugin(cmsContext id, cmsPluginBase* Data) +_cmsTagPluginChunkType _cmsTagPluginChunk = { NULL }; + + +// Duplicates the zone of memory used by the plug-in in the new context +static +void DupTagList(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) { - cmsPluginTag* Plugin = (cmsPluginTag*) Data; - _cmsTagLinkedList *pt, *Anterior; + _cmsTagPluginChunkType newHead = { NULL }; + _cmsTagLinkedList* entry; + _cmsTagLinkedList* Anterior = NULL; + _cmsTagPluginChunkType* head = (_cmsTagPluginChunkType*) src->chunks[TagPlugin]; + // Walk the list copying all nodes + for (entry = head->Tag; + entry != NULL; + entry = entry ->Next) { - if (Data == NULL) { + _cmsTagLinkedList *newEntry = ( _cmsTagLinkedList *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTagLinkedList)); - SupportedTags[DEFAULT_TAG_COUNT-1].Next = NULL; - return TRUE; + if (newEntry == NULL) + return; + + // We want to keep the linked list order, so this is a little bit tricky + newEntry -> Next = NULL; + if (Anterior) + Anterior -> Next = newEntry; + + Anterior = newEntry; + + if (newHead.Tag == NULL) + newHead.Tag = newEntry; + } + + ctx ->chunks[TagPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsTagPluginChunkType)); +} + +void _cmsAllocTagPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + if (src != NULL) { + + DupTagList(ctx, src); + } + else { + static _cmsTagPluginChunkType TagPluginChunk = { NULL }; + ctx ->chunks[TagPlugin] = _cmsSubAllocDup(ctx ->MemPool, &TagPluginChunk, sizeof(_cmsTagPluginChunkType)); } - pt = Anterior = SupportedTags; - while (pt != NULL) { +} - if (Plugin->Signature == pt -> Signature) { - pt ->Descriptor = Plugin ->Descriptor; // Replace old behaviour - return TRUE; - } +cmsBool _cmsRegisterTagPlugin(cmsContext id, cmsPluginBase* Data) +{ + cmsPluginTag* Plugin = (cmsPluginTag*) Data; + _cmsTagLinkedList *pt; + _cmsTagPluginChunkType* TagPluginChunk = ( _cmsTagPluginChunkType*) _cmsContextGetClientChunk(id, TagPlugin); + + if (Data == NULL) { - Anterior = pt; - pt = pt ->Next; + TagPluginChunk->Tag = NULL; + return TRUE; } pt = (_cmsTagLinkedList*) _cmsPluginMalloc(id, sizeof(_cmsTagLinkedList)); @@ -5426,17 +5536,25 @@ cmsBool _cmsRegisterTagPlugin(cmsContext id, cmsPluginBase* Data) pt ->Signature = Plugin ->Signature; pt ->Descriptor = Plugin ->Descriptor; - pt ->Next = NULL; - - if (Anterior != NULL) Anterior -> Next = pt; + pt ->Next = TagPluginChunk ->Tag; + TagPluginChunk ->Tag = pt; + return TRUE; } // Return a descriptor for a given tag or NULL -cmsTagDescriptor* _cmsGetTagDescriptor(cmsTagSignature sig) +cmsTagDescriptor* _cmsGetTagDescriptor(cmsContext ContextID, cmsTagSignature sig) { _cmsTagLinkedList* pt; + _cmsTagPluginChunkType* TagPluginChunk = ( _cmsTagPluginChunkType*) _cmsContextGetClientChunk(ContextID, TagPlugin); + + for (pt = TagPluginChunk->Tag; + pt != NULL; + pt = pt ->Next) { + + if (sig == pt -> Signature) return &pt ->Descriptor; + } for (pt = SupportedTags; pt != NULL; diff --git a/src/cmsvirt.c b/src/cmsvirt.c index 953377a..deeb7a6 100644 --- a/src/cmsvirt.c +++ b/src/cmsvirt.c @@ -990,7 +990,7 @@ typedef struct { static const cmsAllowedLUT AllowedLUTTypes[] = { - { FALSE, 0, cmsSigLut16Type, 4, { cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType}}, + { FALSE, 0, cmsSigLut16Type, 4, { cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType}}, { FALSE, 0, cmsSigLut16Type, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType}}, { FALSE, 0, cmsSigLut16Type, 2, { cmsSigCurveSetElemType, cmsSigCLutElemType}}, { TRUE , 0, cmsSigLutAtoBType, 1, { cmsSigCurveSetElemType }}, @@ -1121,7 +1121,7 @@ cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat if (AllowedLUT == NULL) { // Try to optimize - _cmsOptimizePipeline(&LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags); + _cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags); AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag); } @@ -1130,7 +1130,7 @@ cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat if (AllowedLUT == NULL) { dwFlags |= cmsFLAGS_FORCE_CLUT; - _cmsOptimizePipeline(&LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags); + _cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags); // Put identity curves if needed if (cmsPipelineGetPtrToFirstStage(LUT) ->Type != cmsSigCurveSetElemType) diff --git a/src/cmsxform.c b/src/cmsxform.c index b39c116..b2705cb 100644 --- a/src/cmsxform.c +++ b/src/cmsxform.c @@ -29,44 +29,120 @@ // Transformations stuff // ----------------------------------------------------------------------- -// Alarm codes for 16-bit transformations, because the fixed range of containers there are -// no values left to mark out of gamut. volatile is C99 per 6.2.5 -static volatile cmsUInt16Number Alarm[cmsMAXCHANNELS] = { 0x7F00, 0x7F00, 0x7F00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -static volatile cmsFloat64Number GlobalAdaptationState = 1; +#define DEFAULT_OBSERVER_ADAPTATION_STATE 1.0 + +// The Context0 observer adaptation state. +_cmsAdaptationStateChunkType _cmsAdaptationStateChunk = { DEFAULT_OBSERVER_ADAPTATION_STATE }; + +// Init and duplicate observer adaptation state +void _cmsAllocAdaptationStateChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + static _cmsAdaptationStateChunkType AdaptationStateChunk = { DEFAULT_OBSERVER_ADAPTATION_STATE }; + void* from; + + if (src != NULL) { + from = src ->chunks[AdaptationStateContext]; + } + else { + from = &AdaptationStateChunk; + } + + ctx ->chunks[AdaptationStateContext] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsAdaptationStateChunkType)); +} + + +// Sets adaptation state for absolute colorimetric intent in the given context. Adaptation state applies on all +// but cmsCreateExtendedTransformTHR(). Little CMS can handle incomplete adaptation states. +cmsFloat64Number CMSEXPORT cmsSetAdaptationStateTHR(cmsContext ContextID, cmsFloat64Number d) +{ + cmsFloat64Number prev; + _cmsAdaptationStateChunkType* ptr = (_cmsAdaptationStateChunkType*) _cmsContextGetClientChunk(ContextID, AdaptationStateContext); + + // Get previous value for return + prev = ptr ->AdaptationState; + + // Set the value if d is positive or zero + if (d >= 0.0) { + + ptr ->AdaptationState = d; + } + + // Always return previous value + return prev; +} + // The adaptation state may be defaulted by this function. If you don't like it, use the extended transform routine cmsFloat64Number CMSEXPORT cmsSetAdaptationState(cmsFloat64Number d) -{ - cmsFloat64Number OldVal = GlobalAdaptationState; +{ + return cmsSetAdaptationStateTHR(NULL, d); +} + +// ----------------------------------------------------------------------- + +// Alarm codes for 16-bit transformations, because the fixed range of containers there are +// no values left to mark out of gamut. - if (d >= 0) - GlobalAdaptationState = d; +#define DEFAULT_ALARM_CODES_VALUE {0x7F00, 0x7F00, 0x7F00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} - return OldVal; +_cmsAlarmCodesChunkType _cmsAlarmCodesChunk = { DEFAULT_ALARM_CODES_VALUE }; + +// Sets the codes used to mark out-out-gamut on Proofing transforms for a given context. Values are meant to be +// encoded in 16 bits. +void CMSEXPORT cmsSetAlarmCodesTHR(cmsContext ContextID, const cmsUInt16Number AlarmCodesP[cmsMAXCHANNELS]) +{ + _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(ContextID, AlarmCodesContext); + + _cmsAssert(ContextAlarmCodes != NULL); // Can't happen + + memcpy(ContextAlarmCodes->AlarmCodes, AlarmCodesP, sizeof(ContextAlarmCodes->AlarmCodes)); } -// Alarm codes are always global -void CMSEXPORT cmsSetAlarmCodes(cmsUInt16Number NewAlarm[cmsMAXCHANNELS]) +// Gets the current codes used to mark out-out-gamut on Proofing transforms for the given context. +// Values are meant to be encoded in 16 bits. +void CMSEXPORT cmsGetAlarmCodesTHR(cmsContext ContextID, cmsUInt16Number AlarmCodesP[cmsMAXCHANNELS]) { - int i; + _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(ContextID, AlarmCodesContext); - _cmsAssert(NewAlarm != NULL); + _cmsAssert(ContextAlarmCodes != NULL); // Can't happen - for (i=0; i < cmsMAXCHANNELS; i++) - Alarm[i] = NewAlarm[i]; + memcpy(AlarmCodesP, ContextAlarmCodes->AlarmCodes, sizeof(ContextAlarmCodes->AlarmCodes)); } -// You can get the codes cas well -void CMSEXPORT cmsGetAlarmCodes(cmsUInt16Number OldAlarm[cmsMAXCHANNELS]) +void CMSEXPORT cmsSetAlarmCodes(const cmsUInt16Number NewAlarm[cmsMAXCHANNELS]) { - int i; + _cmsAssert(NewAlarm != NULL); + cmsSetAlarmCodesTHR(NULL, NewAlarm); +} + +void CMSEXPORT cmsGetAlarmCodes(cmsUInt16Number OldAlarm[cmsMAXCHANNELS]) +{ _cmsAssert(OldAlarm != NULL); + cmsGetAlarmCodesTHR(NULL, OldAlarm); +} - for (i=0; i < cmsMAXCHANNELS; i++) - OldAlarm[i] = Alarm[i]; + +// Init and duplicate alarm codes +void _cmsAllocAlarmCodesChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + static _cmsAlarmCodesChunkType AlarmCodesChunk = { DEFAULT_ALARM_CODES_VALUE }; + void* from; + + if (src != NULL) { + from = src ->chunks[AlarmCodesContext]; + } + else { + from = &AlarmCodesChunk; + } + + ctx ->chunks[AlarmCodesContext] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsAlarmCodesChunkType)); } +// ----------------------------------------------------------------------- + // Get rid of transform resources void CMSEXPORT cmsDeleteTransform(cmsHTRANSFORM hTransform) { @@ -173,6 +249,30 @@ void FloatXFORM(_cmsTRANSFORM* p, } } + +static +void NullFloatXFORM(_cmsTRANSFORM* p, + const void* in, + void* out, + cmsUInt32Number Size, + cmsUInt32Number Stride) +{ + cmsUInt8Number* accum; + cmsUInt8Number* output; + cmsFloat32Number fIn[cmsMAXCHANNELS]; + cmsUInt32Number i, n; + + accum = (cmsUInt8Number*) in; + output = (cmsUInt8Number*) out; + n = Size; + + for (i=0; i < n; i++) { + + accum = p -> FromInputFloat(p, fIn, accum, Stride); + output = p -> ToOutputFloat(p, fIn, output, Stride); + } +} + // 16 bit precision ----------------------------------------------------------------------------------------------------------- // Null transformation, only applies formatters. No caché @@ -223,7 +323,7 @@ void PrecalculatedXFORM(_cmsTRANSFORM* p, } -// Auxiliar: Handle precalculated gamut check +// Auxiliar: Handle precalculated gamut check. The retrieval of context may be alittle bit slow, but this function is not critical. static void TransformOnePixelWithGamutCheck(_cmsTRANSFORM* p, const cmsUInt16Number wIn[], @@ -235,9 +335,12 @@ void TransformOnePixelWithGamutCheck(_cmsTRANSFORM* p, if (wOutOfGamut >= 1) { cmsUInt16Number i; + _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(p->ContextID, AlarmCodesContext); + + for (i=0; i < p ->Lut->OutputChannels; i++) { - for (i=0; i < p ->Lut->OutputChannels; i++) - wOut[i] = Alarm[i]; + wOut[i] = ContextAlarmCodes ->AlarmCodes[i]; + } } else p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data); @@ -364,34 +467,86 @@ typedef struct _cmsTransformCollection_st { } _cmsTransformCollection; // The linked list head -static _cmsTransformCollection* TransformCollection = NULL; +_cmsTransformPluginChunkType _cmsTransformPluginChunk = { NULL }; + + +// Duplicates the zone of memory used by the plug-in in the new context +static +void DupPluginTransformList(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + _cmsTransformPluginChunkType newHead = { NULL }; + _cmsTransformCollection* entry; + _cmsTransformCollection* Anterior = NULL; + _cmsTransformPluginChunkType* head = (_cmsTransformPluginChunkType*) src->chunks[TransformPlugin]; + + // Walk the list copying all nodes + for (entry = head->TransformCollection; + entry != NULL; + entry = entry ->Next) { + + _cmsTransformCollection *newEntry = ( _cmsTransformCollection *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTransformCollection)); + + if (newEntry == NULL) + return; + + // We want to keep the linked list order, so this is a little bit tricky + newEntry -> Next = NULL; + if (Anterior) + Anterior -> Next = newEntry; + + Anterior = newEntry; + + if (newHead.TransformCollection == NULL) + newHead.TransformCollection = newEntry; + } + + ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsTransformPluginChunkType)); +} + +void _cmsAllocTransformPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + if (src != NULL) { + + // Copy all linked list + DupPluginTransformList(ctx, src); + } + else { + static _cmsTransformPluginChunkType TransformPluginChunkType = { NULL }; + ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx ->MemPool, &TransformPluginChunkType, sizeof(_cmsTransformPluginChunkType)); + } +} + + // Register new ways to transform -cmsBool _cmsRegisterTransformPlugin(cmsContext id, cmsPluginBase* Data) +cmsBool _cmsRegisterTransformPlugin(cmsContext ContextID, cmsPluginBase* Data) { cmsPluginTransform* Plugin = (cmsPluginTransform*) Data; _cmsTransformCollection* fl; + _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID,TransformPlugin); - if (Data == NULL) { + if (Data == NULL) { // Free the chain. Memory is safely freed at exit - TransformCollection = NULL; + ctx->TransformCollection = NULL; return TRUE; } // Factory callback is required - if (Plugin ->Factory == NULL) return FALSE; + if (Plugin ->Factory == NULL) return FALSE; - fl = (_cmsTransformCollection*) _cmsPluginMalloc(id, sizeof(_cmsTransformCollection)); + fl = (_cmsTransformCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsTransformCollection)); if (fl == NULL) return FALSE; - // Copy the parameters + // Copy the parameters fl ->Factory = Plugin ->Factory; // Keep linked list - fl ->Next = TransformCollection; - TransformCollection = fl; + fl ->Next = ctx->TransformCollection; + ctx->TransformCollection = fl; // All is ok return TRUE; @@ -434,6 +589,7 @@ static _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) { + _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID, TransformPlugin); _cmsTransformCollection* Plugin; // Allocate needed memory @@ -444,7 +600,7 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut, p ->Lut = lut; // Let's see if any plug-in want to do the transform by itself - for (Plugin = TransformCollection; + for (Plugin = ctx ->TransformCollection; Plugin != NULL; Plugin = Plugin ->Next) { @@ -464,10 +620,10 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut, // Fill the formatters just in case the optimized routine is interested. // No error is thrown if the formatter doesn't exist. It is up to the optimization // factory to decide what to do in those cases. - p ->FromInput = _cmsGetFormatter(*InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16; - p ->ToOutput = _cmsGetFormatter(*OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16; - p ->FromInputFloat = _cmsGetFormatter(*InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat; - p ->ToOutputFloat = _cmsGetFormatter(*OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat; + p ->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16; + p ->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16; + p ->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat; + p ->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat; return p; } @@ -475,14 +631,14 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut, // Not suitable for the transform plug-in, let's check the pipeline plug-in if (p ->Lut != NULL) - _cmsOptimizePipeline(&p->Lut, Intent, InputFormat, OutputFormat, dwFlags); + _cmsOptimizePipeline(ContextID, &p->Lut, Intent, InputFormat, OutputFormat, dwFlags); // Check whatever this is a true floating point transform if (_cmsFormatterIsFloat(*InputFormat) && _cmsFormatterIsFloat(*OutputFormat)) { // Get formatter function always return a valid union, but the contents of this union may be NULL. - p ->FromInputFloat = _cmsGetFormatter(*InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat; - p ->ToOutputFloat = _cmsGetFormatter(*OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat; + p ->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat; + p ->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat; *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER; if (p ->FromInputFloat == NULL || p ->ToOutputFloat == NULL) { @@ -492,8 +648,15 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut, return NULL; } - // Float transforms don't use caché, always are non-NULL - p ->xform = FloatXFORM; + if (*dwFlags & cmsFLAGS_NULLTRANSFORM) { + + p ->xform = NullFloatXFORM; + } + else { + // Float transforms don't use caché, always are non-NULL + p ->xform = FloatXFORM; + } + } else { @@ -505,8 +668,8 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut, int BytesPerPixelInput; - p ->FromInput = _cmsGetFormatter(*InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16; - p ->ToOutput = _cmsGetFormatter(*OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16; + p ->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16; + p ->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16; if (p ->FromInput == NULL || p ->ToOutput == NULL) { @@ -801,7 +964,7 @@ cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransformTHR(cmsContext ContextID, for (i=0; i < nProfiles; i++) { BPC[i] = dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION ? TRUE : FALSE; Intents[i] = Intent; - AdaptationStates[i] = GlobalAdaptationState; + AdaptationStates[i] = cmsSetAdaptationStateTHR(ContextID, -1); } @@ -881,7 +1044,7 @@ cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransformTHR(cmsContext ContextID, Intents[0] = nIntent; Intents[1] = nIntent; Intents[2] = INTENT_RELATIVE_COLORIMETRIC; Intents[3] = ProofingIntent; BPC[0] = DoBPC; BPC[1] = DoBPC; BPC[2] = 0; BPC[3] = 0; - Adaptation[0] = Adaptation[1] = Adaptation[2] = Adaptation[3] = GlobalAdaptationState; + Adaptation[0] = Adaptation[1] = Adaptation[2] = Adaptation[3] = cmsSetAdaptationStateTHR(ContextID, -1); if (!(dwFlags & (cmsFLAGS_SOFTPROOFING|cmsFLAGS_GAMUTCHECK))) return cmsCreateTransformTHR(ContextID, InputProfile, InputFormat, OutputProfile, OutputFormat, nIntent, dwFlags); @@ -956,8 +1119,8 @@ cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform, return FALSE; } - FromInput = _cmsGetFormatter(InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16; - ToOutput = _cmsGetFormatter(OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16; + FromInput = _cmsGetFormatter(xform->ContextID, InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16; + ToOutput = _cmsGetFormatter(xform->ContextID, OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16; if (FromInput == NULL || ToOutput == NULL) { diff --git a/src/lcms2.def b/src/lcms2.def index def9680..a1f69c4 100644 --- a/src/lcms2.def +++ b/src/lcms2.def @@ -1,325 +1,341 @@ -LIBRARY LCMS2.DLL - -EXPORTS - -_cms15Fixed16toDouble = _cms15Fixed16toDouble -_cms8Fixed8toDouble = _cms8Fixed8toDouble -cmsAdaptToIlluminant = cmsAdaptToIlluminant -_cmsAdjustEndianess16 = _cmsAdjustEndianess16 -_cmsAdjustEndianess32 = _cmsAdjustEndianess32 -_cmsAdjustEndianess64 = _cmsAdjustEndianess64 -cmsAllocNamedColorList = cmsAllocNamedColorList -cmsAllocProfileSequenceDescription = cmsAllocProfileSequenceDescription -cmsAppendNamedColor = cmsAppendNamedColor -cmsBFDdeltaE = cmsBFDdeltaE -cmsBuildGamma = cmsBuildGamma -cmsBuildParametricToneCurve = cmsBuildParametricToneCurve -cmsBuildSegmentedToneCurve = cmsBuildSegmentedToneCurve -cmsBuildTabulatedToneCurve16 = cmsBuildTabulatedToneCurve16 -cmsBuildTabulatedToneCurveFloat = cmsBuildTabulatedToneCurveFloat -_cmsCalloc = _cmsCalloc -cmsChannelsOf = cmsChannelsOf -cmsCIE2000DeltaE = cmsCIE2000DeltaE -cmsCIE94DeltaE = cmsCIE94DeltaE -cmsCIECAM02Done = cmsCIECAM02Done -cmsCIECAM02Forward = cmsCIECAM02Forward -cmsCIECAM02Init = cmsCIECAM02Init -cmsCIECAM02Reverse = cmsCIECAM02Reverse -cmsCloseIOhandler = cmsCloseIOhandler -cmsCloseProfile = cmsCloseProfile -cmsCMCdeltaE = cmsCMCdeltaE -cmsCreate_sRGBProfile = cmsCreate_sRGBProfile -cmsCreate_sRGBProfileTHR = cmsCreate_sRGBProfileTHR -cmsCreateBCHSWabstractProfile = cmsCreateBCHSWabstractProfile -cmsCreateBCHSWabstractProfileTHR = cmsCreateBCHSWabstractProfileTHR -cmsCreateExtendedTransform = cmsCreateExtendedTransform -cmsCreateGrayProfile = cmsCreateGrayProfile -cmsCreateGrayProfileTHR = cmsCreateGrayProfileTHR -cmsCreateInkLimitingDeviceLink = cmsCreateInkLimitingDeviceLink -cmsCreateInkLimitingDeviceLinkTHR = cmsCreateInkLimitingDeviceLinkTHR -cmsCreateLab2Profile = cmsCreateLab2Profile -cmsCreateLab2ProfileTHR = cmsCreateLab2ProfileTHR -cmsCreateLab4Profile = cmsCreateLab4Profile -cmsCreateLab4ProfileTHR = cmsCreateLab4ProfileTHR -cmsCreateLinearizationDeviceLink = cmsCreateLinearizationDeviceLink -cmsCreateLinearizationDeviceLinkTHR = cmsCreateLinearizationDeviceLinkTHR -cmsCreateMultiprofileTransform = cmsCreateMultiprofileTransform -cmsCreateMultiprofileTransformTHR = cmsCreateMultiprofileTransformTHR -cmsCreateNULLProfile = cmsCreateNULLProfile -cmsCreateNULLProfileTHR = cmsCreateNULLProfileTHR -cmsCreateProfilePlaceholder = cmsCreateProfilePlaceholder -cmsCreateProofingTransform = cmsCreateProofingTransform -cmsCreateProofingTransformTHR = cmsCreateProofingTransformTHR -cmsCreateRGBProfile = cmsCreateRGBProfile -cmsCreateRGBProfileTHR = cmsCreateRGBProfileTHR -cmsCreateTransform = cmsCreateTransform -cmsCreateTransformTHR = cmsCreateTransformTHR -cmsCreateXYZProfile = cmsCreateXYZProfile -cmsCreateXYZProfileTHR = cmsCreateXYZProfileTHR -cmsD50_xyY = cmsD50_xyY -cmsD50_XYZ = cmsD50_XYZ -_cmsDecodeDateTimeNumber = _cmsDecodeDateTimeNumber -_cmsDefaultICCintents = _cmsDefaultICCintents -cmsDeleteTransform = cmsDeleteTransform -cmsDeltaE = cmsDeltaE -cmsDetectBlackPoint = cmsDetectBlackPoint -cmsDetectDestinationBlackPoint = cmsDetectDestinationBlackPoint -cmsDetectTAC = cmsDetectTAC -cmsDesaturateLab = cmsDesaturateLab -cmsDoTransform = cmsDoTransform -cmsDoTransformStride = cmsDoTransformStride -_cmsDoubleTo15Fixed16 = _cmsDoubleTo15Fixed16 -_cmsDoubleTo8Fixed8 = _cmsDoubleTo8Fixed8 -_cmsDupMem = _cmsDupMem -cmsDupNamedColorList = cmsDupNamedColorList -cmsDupProfileSequenceDescription = cmsDupProfileSequenceDescription -cmsDupToneCurve = cmsDupToneCurve -_cmsEncodeDateTimeNumber = _cmsEncodeDateTimeNumber -cmsEstimateGamma = cmsEstimateGamma -cmsGetToneCurveEstimatedTableEntries = cmsGetToneCurveEstimatedTableEntries -cmsGetToneCurveEstimatedTable = cmsGetToneCurveEstimatedTable -cmsEvalToneCurve16 = cmsEvalToneCurve16 -cmsEvalToneCurveFloat = cmsEvalToneCurveFloat -cmsfilelength = cmsfilelength -cmsFloat2LabEncoded = cmsFloat2LabEncoded -cmsFloat2LabEncodedV2 = cmsFloat2LabEncodedV2 -cmsFloat2XYZEncoded = cmsFloat2XYZEncoded -cmsFormatterForColorspaceOfProfile = cmsFormatterForColorspaceOfProfile -cmsFormatterForPCSOfProfile = cmsFormatterForPCSOfProfile -_cmsFree = _cmsFree -cmsFreeNamedColorList = cmsFreeNamedColorList -cmsFreeProfileSequenceDescription = cmsFreeProfileSequenceDescription -cmsFreeToneCurve = cmsFreeToneCurve -cmsFreeToneCurveTriple = cmsFreeToneCurveTriple -cmsGBDAlloc = cmsGBDAlloc -cmsGBDFree = cmsGBDFree -cmsGDBAddPoint = cmsGDBAddPoint -cmsGDBCheckPoint = cmsGDBCheckPoint -cmsGDBCompute = cmsGDBCompute -cmsGetAlarmCodes = cmsGetAlarmCodes -cmsGetColorSpace = cmsGetColorSpace -cmsGetDeviceClass = cmsGetDeviceClass -cmsGetEncodedICCversion = cmsGetEncodedICCversion -cmsGetHeaderAttributes = cmsGetHeaderAttributes -cmsGetHeaderCreationDateTime = cmsGetHeaderCreationDateTime -cmsGetHeaderFlags = cmsGetHeaderFlags -cmsGetHeaderManufacturer = cmsGetHeaderManufacturer -cmsGetHeaderModel = cmsGetHeaderModel -cmsGetHeaderProfileID = cmsGetHeaderProfileID -cmsGetHeaderRenderingIntent = cmsGetHeaderRenderingIntent -cmsGetNamedColorList = cmsGetNamedColorList -cmsGetPCS = cmsGetPCS -cmsGetPostScriptColorResource = cmsGetPostScriptColorResource -cmsGetPostScriptCRD = cmsGetPostScriptCRD -cmsGetPostScriptCSA = cmsGetPostScriptCSA -cmsGetProfileInfo = cmsGetProfileInfo -cmsGetProfileInfoASCII = cmsGetProfileInfoASCII -cmsGetProfileContextID = cmsGetProfileContextID -cmsGetProfileVersion = cmsGetProfileVersion -cmsGetSupportedIntents = cmsGetSupportedIntents -cmsGetTagCount = cmsGetTagCount -cmsGetTagSignature = cmsGetTagSignature -cmsGetTransformContextID = cmsGetTransformContextID -_cmsICCcolorSpace = _cmsICCcolorSpace -_cmsIOPrintf = _cmsIOPrintf -cmsIsCLUT = cmsIsCLUT -cmsIsIntentSupported = cmsIsIntentSupported -cmsIsMatrixShaper = cmsIsMatrixShaper -cmsIsTag = cmsIsTag -cmsIsToneCurveDescending = cmsIsToneCurveDescending -cmsIsToneCurveLinear = cmsIsToneCurveLinear -cmsIsToneCurveMonotonic = cmsIsToneCurveMonotonic -cmsIsToneCurveMultisegment = cmsIsToneCurveMultisegment -cmsGetToneCurveParametricType = cmsGetToneCurveParametricType -cmsIT8Alloc = cmsIT8Alloc -cmsIT8DefineDblFormat = cmsIT8DefineDblFormat -cmsIT8EnumDataFormat = cmsIT8EnumDataFormat -cmsIT8EnumProperties = cmsIT8EnumProperties -cmsIT8EnumPropertyMulti = cmsIT8EnumPropertyMulti -cmsIT8Free = cmsIT8Free -cmsIT8GetData = cmsIT8GetData -cmsIT8GetDataDbl = cmsIT8GetDataDbl -cmsIT8FindDataFormat = cmsIT8FindDataFormat -cmsIT8GetDataRowCol = cmsIT8GetDataRowCol -cmsIT8GetDataRowColDbl = cmsIT8GetDataRowColDbl -cmsIT8GetPatchName = cmsIT8GetPatchName -cmsIT8GetPatchByName = cmsIT8GetPatchByName -cmsIT8GetProperty = cmsIT8GetProperty -cmsIT8GetPropertyDbl = cmsIT8GetPropertyDbl -cmsIT8GetPropertyMulti = cmsIT8GetPropertyMulti -cmsIT8GetSheetType = cmsIT8GetSheetType -cmsIT8LoadFromFile = cmsIT8LoadFromFile -cmsIT8LoadFromMem = cmsIT8LoadFromMem -cmsIT8SaveToFile = cmsIT8SaveToFile -cmsIT8SaveToMem = cmsIT8SaveToMem -cmsIT8SetComment = cmsIT8SetComment -cmsIT8SetData = cmsIT8SetData -cmsIT8SetDataDbl = cmsIT8SetDataDbl -cmsIT8SetDataFormat = cmsIT8SetDataFormat -cmsIT8SetDataRowCol = cmsIT8SetDataRowCol -cmsIT8SetDataRowColDbl = cmsIT8SetDataRowColDbl -cmsIT8SetPropertyDbl = cmsIT8SetPropertyDbl -cmsIT8SetPropertyHex = cmsIT8SetPropertyHex -cmsIT8SetPropertyStr = cmsIT8SetPropertyStr -cmsIT8SetPropertyMulti = cmsIT8SetPropertyMulti -cmsIT8SetPropertyUncooked = cmsIT8SetPropertyUncooked -cmsIT8SetSheetType = cmsIT8SetSheetType -cmsIT8SetTable = cmsIT8SetTable -cmsIT8SetTableByLabel = cmsIT8SetTableByLabel -cmsIT8SetIndexColumn = cmsIT8SetIndexColumn -cmsIT8TableCount = cmsIT8TableCount -cmsJoinToneCurve = cmsJoinToneCurve -cmsLab2LCh = cmsLab2LCh -cmsLab2XYZ = cmsLab2XYZ -cmsLabEncoded2Float = cmsLabEncoded2Float -cmsLabEncoded2FloatV2 = cmsLabEncoded2FloatV2 -cmsLCh2Lab = cmsLCh2Lab -_cmsLCMScolorSpace = _cmsLCMScolorSpace -cmsLinkTag = cmsLinkTag -cmsTagLinkedTo = cmsTagLinkedTo -cmsPipelineAlloc = cmsPipelineAlloc -cmsPipelineCat = cmsPipelineCat -cmsPipelineCheckAndRetreiveStages = cmsPipelineCheckAndRetreiveStages -cmsPipelineDup = cmsPipelineDup -cmsPipelineStageCount = cmsPipelineStageCount -cmsPipelineEval16 = cmsPipelineEval16 -cmsPipelineEvalFloat = cmsPipelineEvalFloat -cmsPipelineEvalReverseFloat = cmsPipelineEvalReverseFloat -cmsPipelineFree = cmsPipelineFree -cmsPipelineGetPtrToFirstStage = cmsPipelineGetPtrToFirstStage -cmsPipelineGetPtrToLastStage = cmsPipelineGetPtrToLastStage -cmsPipelineInputChannels = cmsPipelineInputChannels -cmsPipelineInsertStage = cmsPipelineInsertStage -cmsPipelineOutputChannels = cmsPipelineOutputChannels -cmsPipelineSetSaveAs8bitsFlag = cmsPipelineSetSaveAs8bitsFlag -_cmsPipelineSetOptimizationParameters = _cmsPipelineSetOptimizationParameters -cmsPipelineUnlinkStage = cmsPipelineUnlinkStage -_cmsMalloc = _cmsMalloc -_cmsMallocZero = _cmsMallocZero -_cmsMAT3eval = _cmsMAT3eval -_cmsMAT3identity = _cmsMAT3identity -_cmsMAT3inverse = _cmsMAT3inverse -_cmsMAT3isIdentity = _cmsMAT3isIdentity -_cmsMAT3per = _cmsMAT3per -_cmsMAT3solve = _cmsMAT3solve -cmsMD5computeID = cmsMD5computeID -cmsMLUalloc = cmsMLUalloc -cmsMLUdup = cmsMLUdup -cmsMLUfree = cmsMLUfree -cmsMLUgetASCII = cmsMLUgetASCII -cmsMLUgetTranslation = cmsMLUgetTranslation -cmsMLUgetWide = cmsMLUgetWide -cmsMLUsetASCII = cmsMLUsetASCII -cmsMLUsetWide = cmsMLUsetWide -cmsStageAllocCLut16bit = cmsStageAllocCLut16bit -cmsStageAllocCLut16bitGranular = cmsStageAllocCLut16bitGranular -cmsStageAllocCLutFloat = cmsStageAllocCLutFloat -cmsStageAllocCLutFloatGranular = cmsStageAllocCLutFloatGranular -cmsStageAllocToneCurves = cmsStageAllocToneCurves -cmsStageAllocIdentity = cmsStageAllocIdentity -cmsStageAllocMatrix = cmsStageAllocMatrix -_cmsStageAllocPlaceholder = _cmsStageAllocPlaceholder -cmsStageDup = cmsStageDup -cmsStageFree = cmsStageFree -cmsStageNext = cmsStageNext -cmsStageInputChannels = cmsStageInputChannels -cmsStageOutputChannels = cmsStageOutputChannels -cmsStageSampleCLut16bit = cmsStageSampleCLut16bit -cmsStageSampleCLutFloat = cmsStageSampleCLutFloat -cmsStageType = cmsStageType -cmsStageData = cmsStageData -cmsNamedColorCount = cmsNamedColorCount -cmsNamedColorIndex = cmsNamedColorIndex -cmsNamedColorInfo = cmsNamedColorInfo -cmsOpenIOhandlerFromFile = cmsOpenIOhandlerFromFile -cmsOpenIOhandlerFromMem = cmsOpenIOhandlerFromMem -cmsOpenIOhandlerFromNULL = cmsOpenIOhandlerFromNULL -cmsOpenIOhandlerFromStream = cmsOpenIOhandlerFromStream -cmsOpenProfileFromFile = cmsOpenProfileFromFile -cmsOpenProfileFromFileTHR = cmsOpenProfileFromFileTHR -cmsOpenProfileFromIOhandlerTHR = cmsOpenProfileFromIOhandlerTHR -cmsOpenProfileFromMem = cmsOpenProfileFromMem -cmsOpenProfileFromMemTHR = cmsOpenProfileFromMemTHR -cmsOpenProfileFromStream = cmsOpenProfileFromStream -cmsOpenProfileFromStreamTHR = cmsOpenProfileFromStreamTHR -cmsPlugin = cmsPlugin -_cmsRead15Fixed16Number = _cmsRead15Fixed16Number -_cmsReadAlignment = _cmsReadAlignment -_cmsReadFloat32Number = _cmsReadFloat32Number -cmsReadRawTag = cmsReadRawTag -cmsReadTag = cmsReadTag -_cmsReadTypeBase = _cmsReadTypeBase -_cmsReadUInt16Array = _cmsReadUInt16Array -_cmsReadUInt16Number = _cmsReadUInt16Number -_cmsReadUInt32Number = _cmsReadUInt32Number -_cmsReadUInt64Number = _cmsReadUInt64Number -_cmsReadUInt8Number = _cmsReadUInt8Number -_cmsReadXYZNumber = _cmsReadXYZNumber -_cmsRealloc = _cmsRealloc -cmsReverseToneCurve = cmsReverseToneCurve -cmsReverseToneCurveEx = cmsReverseToneCurveEx -cmsSaveProfileToFile = cmsSaveProfileToFile -cmsSaveProfileToIOhandler = cmsSaveProfileToIOhandler -cmsSaveProfileToMem = cmsSaveProfileToMem -cmsSaveProfileToStream = cmsSaveProfileToStream -cmsSetAdaptationState = cmsSetAdaptationState -cmsSetAlarmCodes = cmsSetAlarmCodes -cmsSetColorSpace = cmsSetColorSpace -cmsSetDeviceClass = cmsSetDeviceClass -cmsSetEncodedICCversion = cmsSetEncodedICCversion -cmsSetHeaderAttributes = cmsSetHeaderAttributes -cmsSetHeaderFlags = cmsSetHeaderFlags -cmsSetHeaderManufacturer = cmsSetHeaderManufacturer -cmsSetHeaderModel = cmsSetHeaderModel -cmsSetHeaderProfileID = cmsSetHeaderProfileID -cmsSetHeaderRenderingIntent = cmsSetHeaderRenderingIntent -cmsSetLogErrorHandler = cmsSetLogErrorHandler -cmsSetPCS = cmsSetPCS -cmsSetProfileVersion = cmsSetProfileVersion -cmsSignalError = cmsSignalError -cmsSmoothToneCurve = cmsSmoothToneCurve -cmsstrcasecmp = cmsstrcasecmp -cmsTempFromWhitePoint = cmsTempFromWhitePoint -cmsTransform2DeviceLink = cmsTransform2DeviceLink -cmsUnregisterPlugins = cmsUnregisterPlugins -_cmsVEC3cross = _cmsVEC3cross -_cmsVEC3distance = _cmsVEC3distance -_cmsVEC3dot = _cmsVEC3dot -_cmsVEC3init = _cmsVEC3init -_cmsVEC3length = _cmsVEC3length -_cmsVEC3minus = _cmsVEC3minus -cmsWhitePointFromTemp = cmsWhitePointFromTemp -_cmsWrite15Fixed16Number = _cmsWrite15Fixed16Number -_cmsWriteAlignment = _cmsWriteAlignment -_cmsWriteFloat32Number = _cmsWriteFloat32Number -cmsWriteRawTag = cmsWriteRawTag -cmsWriteTag = cmsWriteTag -_cmsWriteTypeBase = _cmsWriteTypeBase -_cmsWriteUInt16Array = _cmsWriteUInt16Array -_cmsWriteUInt16Number = _cmsWriteUInt16Number -_cmsWriteUInt32Number = _cmsWriteUInt32Number -_cmsWriteUInt64Number = _cmsWriteUInt64Number -_cmsWriteUInt8Number = _cmsWriteUInt8Number -_cmsWriteXYZNumber = _cmsWriteXYZNumber -cmsxyY2XYZ = cmsxyY2XYZ -cmsXYZ2Lab = cmsXYZ2Lab -cmsXYZ2xyY = cmsXYZ2xyY -cmsXYZEncoded2Float = cmsXYZEncoded2Float -cmsSliceSpace16 = cmsSliceSpace16 -cmsSliceSpaceFloat = cmsSliceSpaceFloat -cmsChangeBuffersFormat = cmsChangeBuffersFormat -cmsDictAlloc = cmsDictAlloc -cmsDictFree = cmsDictFree -cmsDictDup = cmsDictDup -cmsDictAddEntry = cmsDictAddEntry -cmsDictGetEntryList = cmsDictGetEntryList -cmsDictNextEntry = cmsDictNextEntry -_cmsGetTransformUserData = _cmsGetTransformUserData -_cmsSetTransformUserData = _cmsSetTransformUserData -_cmsGetTransformFormatters16 = _cmsGetTransformFormatters16 -_cmsGetTransformFormattersFloat = _cmsGetTransformFormattersFloat -cmsGetHeaderCreator = cmsGetHeaderCreator -cmsPluginTHR = cmsPluginTHR -cmsGetPipelineContextID = cmsGetPipelineContextID -cmsGetTransformInputFormat = cmsGetTransformInputFormat -cmsGetTransformOutputFormat = cmsGetTransformOutputFormat +LIBRARY LCMS2.DLL + +EXPORTS + +_cms15Fixed16toDouble = _cms15Fixed16toDouble +_cms8Fixed8toDouble = _cms8Fixed8toDouble +cmsAdaptToIlluminant = cmsAdaptToIlluminant +_cmsAdjustEndianess16 = _cmsAdjustEndianess16 +_cmsAdjustEndianess32 = _cmsAdjustEndianess32 +_cmsAdjustEndianess64 = _cmsAdjustEndianess64 +cmsAllocNamedColorList = cmsAllocNamedColorList +cmsAllocProfileSequenceDescription = cmsAllocProfileSequenceDescription +cmsAppendNamedColor = cmsAppendNamedColor +cmsBFDdeltaE = cmsBFDdeltaE +cmsBuildGamma = cmsBuildGamma +cmsBuildParametricToneCurve = cmsBuildParametricToneCurve +cmsBuildSegmentedToneCurve = cmsBuildSegmentedToneCurve +cmsBuildTabulatedToneCurve16 = cmsBuildTabulatedToneCurve16 +cmsBuildTabulatedToneCurveFloat = cmsBuildTabulatedToneCurveFloat +_cmsCalloc = _cmsCalloc +cmsChannelsOf = cmsChannelsOf +cmsCIE2000DeltaE = cmsCIE2000DeltaE +cmsCIE94DeltaE = cmsCIE94DeltaE +cmsCIECAM02Done = cmsCIECAM02Done +cmsCIECAM02Forward = cmsCIECAM02Forward +cmsCIECAM02Init = cmsCIECAM02Init +cmsCIECAM02Reverse = cmsCIECAM02Reverse +cmsCloseIOhandler = cmsCloseIOhandler +cmsCloseProfile = cmsCloseProfile +cmsCMCdeltaE = cmsCMCdeltaE +cmsCreate_sRGBProfile = cmsCreate_sRGBProfile +cmsCreate_sRGBProfileTHR = cmsCreate_sRGBProfileTHR +cmsCreateBCHSWabstractProfile = cmsCreateBCHSWabstractProfile +cmsCreateBCHSWabstractProfileTHR = cmsCreateBCHSWabstractProfileTHR +cmsCreateExtendedTransform = cmsCreateExtendedTransform +cmsCreateGrayProfile = cmsCreateGrayProfile +cmsCreateGrayProfileTHR = cmsCreateGrayProfileTHR +cmsCreateInkLimitingDeviceLink = cmsCreateInkLimitingDeviceLink +cmsCreateInkLimitingDeviceLinkTHR = cmsCreateInkLimitingDeviceLinkTHR +cmsCreateLab2Profile = cmsCreateLab2Profile +cmsCreateLab2ProfileTHR = cmsCreateLab2ProfileTHR +cmsCreateLab4Profile = cmsCreateLab4Profile +cmsCreateLab4ProfileTHR = cmsCreateLab4ProfileTHR +cmsCreateLinearizationDeviceLink = cmsCreateLinearizationDeviceLink +cmsCreateLinearizationDeviceLinkTHR = cmsCreateLinearizationDeviceLinkTHR +cmsCreateMultiprofileTransform = cmsCreateMultiprofileTransform +cmsCreateMultiprofileTransformTHR = cmsCreateMultiprofileTransformTHR +cmsCreateNULLProfile = cmsCreateNULLProfile +cmsCreateNULLProfileTHR = cmsCreateNULLProfileTHR +cmsCreateProfilePlaceholder = cmsCreateProfilePlaceholder +cmsCreateProofingTransform = cmsCreateProofingTransform +cmsCreateProofingTransformTHR = cmsCreateProofingTransformTHR +cmsCreateRGBProfile = cmsCreateRGBProfile +cmsCreateRGBProfileTHR = cmsCreateRGBProfileTHR +cmsCreateTransform = cmsCreateTransform +cmsCreateTransformTHR = cmsCreateTransformTHR +cmsCreateXYZProfile = cmsCreateXYZProfile +cmsCreateXYZProfileTHR = cmsCreateXYZProfileTHR +cmsD50_xyY = cmsD50_xyY +cmsD50_XYZ = cmsD50_XYZ +_cmsDecodeDateTimeNumber = _cmsDecodeDateTimeNumber +_cmsDefaultICCintents = _cmsDefaultICCintents +cmsDeleteTransform = cmsDeleteTransform +cmsDeltaE = cmsDeltaE +cmsDetectBlackPoint = cmsDetectBlackPoint +cmsDetectDestinationBlackPoint = cmsDetectDestinationBlackPoint +cmsDetectTAC = cmsDetectTAC +cmsDesaturateLab = cmsDesaturateLab +cmsDoTransform = cmsDoTransform +cmsDoTransformStride = cmsDoTransformStride +_cmsDoubleTo15Fixed16 = _cmsDoubleTo15Fixed16 +_cmsDoubleTo8Fixed8 = _cmsDoubleTo8Fixed8 +_cmsDupMem = _cmsDupMem +cmsDupNamedColorList = cmsDupNamedColorList +cmsDupProfileSequenceDescription = cmsDupProfileSequenceDescription +cmsDupToneCurve = cmsDupToneCurve +_cmsEncodeDateTimeNumber = _cmsEncodeDateTimeNumber +cmsEstimateGamma = cmsEstimateGamma +cmsGetToneCurveEstimatedTableEntries = cmsGetToneCurveEstimatedTableEntries +cmsGetToneCurveEstimatedTable = cmsGetToneCurveEstimatedTable +cmsEvalToneCurve16 = cmsEvalToneCurve16 +cmsEvalToneCurveFloat = cmsEvalToneCurveFloat +cmsfilelength = cmsfilelength +cmsFloat2LabEncoded = cmsFloat2LabEncoded +cmsFloat2LabEncodedV2 = cmsFloat2LabEncodedV2 +cmsFloat2XYZEncoded = cmsFloat2XYZEncoded +cmsFormatterForColorspaceOfProfile = cmsFormatterForColorspaceOfProfile +cmsFormatterForPCSOfProfile = cmsFormatterForPCSOfProfile +_cmsFree = _cmsFree +cmsFreeNamedColorList = cmsFreeNamedColorList +cmsFreeProfileSequenceDescription = cmsFreeProfileSequenceDescription +cmsFreeToneCurve = cmsFreeToneCurve +cmsFreeToneCurveTriple = cmsFreeToneCurveTriple +cmsGBDAlloc = cmsGBDAlloc +cmsGBDFree = cmsGBDFree +cmsGDBAddPoint = cmsGDBAddPoint +cmsGDBCheckPoint = cmsGDBCheckPoint +cmsGDBCompute = cmsGDBCompute +cmsGetAlarmCodes = cmsGetAlarmCodes +cmsGetColorSpace = cmsGetColorSpace +cmsGetDeviceClass = cmsGetDeviceClass +cmsGetEncodedICCversion = cmsGetEncodedICCversion +cmsGetHeaderAttributes = cmsGetHeaderAttributes +cmsGetHeaderCreationDateTime = cmsGetHeaderCreationDateTime +cmsGetHeaderFlags = cmsGetHeaderFlags +cmsGetHeaderManufacturer = cmsGetHeaderManufacturer +cmsGetHeaderModel = cmsGetHeaderModel +cmsGetHeaderProfileID = cmsGetHeaderProfileID +cmsGetHeaderRenderingIntent = cmsGetHeaderRenderingIntent +cmsGetNamedColorList = cmsGetNamedColorList +cmsGetPCS = cmsGetPCS +cmsGetPostScriptColorResource = cmsGetPostScriptColorResource +cmsGetPostScriptCRD = cmsGetPostScriptCRD +cmsGetPostScriptCSA = cmsGetPostScriptCSA +cmsGetProfileInfo = cmsGetProfileInfo +cmsGetProfileInfoASCII = cmsGetProfileInfoASCII +cmsGetProfileContextID = cmsGetProfileContextID +cmsGetProfileVersion = cmsGetProfileVersion +cmsGetSupportedIntents = cmsGetSupportedIntents +cmsGetTagCount = cmsGetTagCount +cmsGetTagSignature = cmsGetTagSignature +cmsGetTransformContextID = cmsGetTransformContextID +_cmsICCcolorSpace = _cmsICCcolorSpace +_cmsIOPrintf = _cmsIOPrintf +cmsIsCLUT = cmsIsCLUT +cmsIsIntentSupported = cmsIsIntentSupported +cmsIsMatrixShaper = cmsIsMatrixShaper +cmsIsTag = cmsIsTag +cmsIsToneCurveDescending = cmsIsToneCurveDescending +cmsIsToneCurveLinear = cmsIsToneCurveLinear +cmsIsToneCurveMonotonic = cmsIsToneCurveMonotonic +cmsIsToneCurveMultisegment = cmsIsToneCurveMultisegment +cmsGetToneCurveParametricType = cmsGetToneCurveParametricType +cmsIT8Alloc = cmsIT8Alloc +cmsIT8DefineDblFormat = cmsIT8DefineDblFormat +cmsIT8EnumDataFormat = cmsIT8EnumDataFormat +cmsIT8EnumProperties = cmsIT8EnumProperties +cmsIT8EnumPropertyMulti = cmsIT8EnumPropertyMulti +cmsIT8Free = cmsIT8Free +cmsIT8GetData = cmsIT8GetData +cmsIT8GetDataDbl = cmsIT8GetDataDbl +cmsIT8FindDataFormat = cmsIT8FindDataFormat +cmsIT8GetDataRowCol = cmsIT8GetDataRowCol +cmsIT8GetDataRowColDbl = cmsIT8GetDataRowColDbl +cmsIT8GetPatchName = cmsIT8GetPatchName +cmsIT8GetPatchByName = cmsIT8GetPatchByName +cmsIT8GetProperty = cmsIT8GetProperty +cmsIT8GetPropertyDbl = cmsIT8GetPropertyDbl +cmsIT8GetPropertyMulti = cmsIT8GetPropertyMulti +cmsIT8GetSheetType = cmsIT8GetSheetType +cmsIT8LoadFromFile = cmsIT8LoadFromFile +cmsIT8LoadFromMem = cmsIT8LoadFromMem +cmsIT8SaveToFile = cmsIT8SaveToFile +cmsIT8SaveToMem = cmsIT8SaveToMem +cmsIT8SetComment = cmsIT8SetComment +cmsIT8SetData = cmsIT8SetData +cmsIT8SetDataDbl = cmsIT8SetDataDbl +cmsIT8SetDataFormat = cmsIT8SetDataFormat +cmsIT8SetDataRowCol = cmsIT8SetDataRowCol +cmsIT8SetDataRowColDbl = cmsIT8SetDataRowColDbl +cmsIT8SetPropertyDbl = cmsIT8SetPropertyDbl +cmsIT8SetPropertyHex = cmsIT8SetPropertyHex +cmsIT8SetPropertyStr = cmsIT8SetPropertyStr +cmsIT8SetPropertyMulti = cmsIT8SetPropertyMulti +cmsIT8SetPropertyUncooked = cmsIT8SetPropertyUncooked +cmsIT8SetSheetType = cmsIT8SetSheetType +cmsIT8SetTable = cmsIT8SetTable +cmsIT8SetTableByLabel = cmsIT8SetTableByLabel +cmsIT8SetIndexColumn = cmsIT8SetIndexColumn +cmsIT8TableCount = cmsIT8TableCount +cmsJoinToneCurve = cmsJoinToneCurve +cmsLab2LCh = cmsLab2LCh +cmsLab2XYZ = cmsLab2XYZ +cmsLabEncoded2Float = cmsLabEncoded2Float +cmsLabEncoded2FloatV2 = cmsLabEncoded2FloatV2 +cmsLCh2Lab = cmsLCh2Lab +_cmsLCMScolorSpace = _cmsLCMScolorSpace +cmsLinkTag = cmsLinkTag +cmsTagLinkedTo = cmsTagLinkedTo +cmsPipelineAlloc = cmsPipelineAlloc +cmsPipelineCat = cmsPipelineCat +cmsPipelineCheckAndRetreiveStages = cmsPipelineCheckAndRetreiveStages +cmsPipelineDup = cmsPipelineDup +cmsPipelineStageCount = cmsPipelineStageCount +cmsPipelineEval16 = cmsPipelineEval16 +cmsPipelineEvalFloat = cmsPipelineEvalFloat +cmsPipelineEvalReverseFloat = cmsPipelineEvalReverseFloat +cmsPipelineFree = cmsPipelineFree +cmsPipelineGetPtrToFirstStage = cmsPipelineGetPtrToFirstStage +cmsPipelineGetPtrToLastStage = cmsPipelineGetPtrToLastStage +cmsPipelineInputChannels = cmsPipelineInputChannels +cmsPipelineInsertStage = cmsPipelineInsertStage +cmsPipelineOutputChannels = cmsPipelineOutputChannels +cmsPipelineSetSaveAs8bitsFlag = cmsPipelineSetSaveAs8bitsFlag +_cmsPipelineSetOptimizationParameters = _cmsPipelineSetOptimizationParameters +cmsPipelineUnlinkStage = cmsPipelineUnlinkStage +_cmsMalloc = _cmsMalloc +_cmsMallocZero = _cmsMallocZero +_cmsMAT3eval = _cmsMAT3eval +_cmsMAT3identity = _cmsMAT3identity +_cmsMAT3inverse = _cmsMAT3inverse +_cmsMAT3isIdentity = _cmsMAT3isIdentity +_cmsMAT3per = _cmsMAT3per +_cmsMAT3solve = _cmsMAT3solve +cmsMD5computeID = cmsMD5computeID +cmsMLUalloc = cmsMLUalloc +cmsMLUdup = cmsMLUdup +cmsMLUfree = cmsMLUfree +cmsMLUgetASCII = cmsMLUgetASCII +cmsMLUgetTranslation = cmsMLUgetTranslation +cmsMLUgetWide = cmsMLUgetWide +cmsMLUsetASCII = cmsMLUsetASCII +cmsMLUsetWide = cmsMLUsetWide +cmsStageAllocCLut16bit = cmsStageAllocCLut16bit +cmsStageAllocCLut16bitGranular = cmsStageAllocCLut16bitGranular +cmsStageAllocCLutFloat = cmsStageAllocCLutFloat +cmsStageAllocCLutFloatGranular = cmsStageAllocCLutFloatGranular +cmsStageAllocToneCurves = cmsStageAllocToneCurves +cmsStageAllocIdentity = cmsStageAllocIdentity +cmsStageAllocMatrix = cmsStageAllocMatrix +_cmsStageAllocPlaceholder = _cmsStageAllocPlaceholder +cmsStageDup = cmsStageDup +cmsStageFree = cmsStageFree +cmsStageNext = cmsStageNext +cmsStageInputChannels = cmsStageInputChannels +cmsStageOutputChannels = cmsStageOutputChannels +cmsStageSampleCLut16bit = cmsStageSampleCLut16bit +cmsStageSampleCLutFloat = cmsStageSampleCLutFloat +cmsStageType = cmsStageType +cmsStageData = cmsStageData +cmsNamedColorCount = cmsNamedColorCount +cmsNamedColorIndex = cmsNamedColorIndex +cmsNamedColorInfo = cmsNamedColorInfo +cmsOpenIOhandlerFromFile = cmsOpenIOhandlerFromFile +cmsOpenIOhandlerFromMem = cmsOpenIOhandlerFromMem +cmsOpenIOhandlerFromNULL = cmsOpenIOhandlerFromNULL +cmsOpenIOhandlerFromStream = cmsOpenIOhandlerFromStream +cmsOpenProfileFromFile = cmsOpenProfileFromFile +cmsOpenProfileFromFileTHR = cmsOpenProfileFromFileTHR +cmsOpenProfileFromIOhandlerTHR = cmsOpenProfileFromIOhandlerTHR +cmsOpenProfileFromMem = cmsOpenProfileFromMem +cmsOpenProfileFromMemTHR = cmsOpenProfileFromMemTHR +cmsOpenProfileFromStream = cmsOpenProfileFromStream +cmsOpenProfileFromStreamTHR = cmsOpenProfileFromStreamTHR +cmsPlugin = cmsPlugin +_cmsRead15Fixed16Number = _cmsRead15Fixed16Number +_cmsReadAlignment = _cmsReadAlignment +_cmsReadFloat32Number = _cmsReadFloat32Number +cmsReadRawTag = cmsReadRawTag +cmsReadTag = cmsReadTag +_cmsReadTypeBase = _cmsReadTypeBase +_cmsReadUInt16Array = _cmsReadUInt16Array +_cmsReadUInt16Number = _cmsReadUInt16Number +_cmsReadUInt32Number = _cmsReadUInt32Number +_cmsReadUInt64Number = _cmsReadUInt64Number +_cmsReadUInt8Number = _cmsReadUInt8Number +_cmsReadXYZNumber = _cmsReadXYZNumber +_cmsRealloc = _cmsRealloc +cmsReverseToneCurve = cmsReverseToneCurve +cmsReverseToneCurveEx = cmsReverseToneCurveEx +cmsSaveProfileToFile = cmsSaveProfileToFile +cmsSaveProfileToIOhandler = cmsSaveProfileToIOhandler +cmsSaveProfileToMem = cmsSaveProfileToMem +cmsSaveProfileToStream = cmsSaveProfileToStream +cmsSetAdaptationState = cmsSetAdaptationState +cmsSetAlarmCodes = cmsSetAlarmCodes +cmsSetColorSpace = cmsSetColorSpace +cmsSetDeviceClass = cmsSetDeviceClass +cmsSetEncodedICCversion = cmsSetEncodedICCversion +cmsSetHeaderAttributes = cmsSetHeaderAttributes +cmsSetHeaderFlags = cmsSetHeaderFlags +cmsSetHeaderManufacturer = cmsSetHeaderManufacturer +cmsSetHeaderModel = cmsSetHeaderModel +cmsSetHeaderProfileID = cmsSetHeaderProfileID +cmsSetHeaderRenderingIntent = cmsSetHeaderRenderingIntent +cmsSetLogErrorHandler = cmsSetLogErrorHandler +cmsSetPCS = cmsSetPCS +cmsSetProfileVersion = cmsSetProfileVersion +cmsSignalError = cmsSignalError +cmsSmoothToneCurve = cmsSmoothToneCurve +cmsstrcasecmp = cmsstrcasecmp +cmsTempFromWhitePoint = cmsTempFromWhitePoint +cmsTransform2DeviceLink = cmsTransform2DeviceLink +cmsUnregisterPlugins = cmsUnregisterPlugins +_cmsVEC3cross = _cmsVEC3cross +_cmsVEC3distance = _cmsVEC3distance +_cmsVEC3dot = _cmsVEC3dot +_cmsVEC3init = _cmsVEC3init +_cmsVEC3length = _cmsVEC3length +_cmsVEC3minus = _cmsVEC3minus +cmsWhitePointFromTemp = cmsWhitePointFromTemp +_cmsWrite15Fixed16Number = _cmsWrite15Fixed16Number +_cmsWriteAlignment = _cmsWriteAlignment +_cmsWriteFloat32Number = _cmsWriteFloat32Number +cmsWriteRawTag = cmsWriteRawTag +cmsWriteTag = cmsWriteTag +_cmsWriteTypeBase = _cmsWriteTypeBase +_cmsWriteUInt16Array = _cmsWriteUInt16Array +_cmsWriteUInt16Number = _cmsWriteUInt16Number +_cmsWriteUInt32Number = _cmsWriteUInt32Number +_cmsWriteUInt64Number = _cmsWriteUInt64Number +_cmsWriteUInt8Number = _cmsWriteUInt8Number +_cmsWriteXYZNumber = _cmsWriteXYZNumber +cmsxyY2XYZ = cmsxyY2XYZ +cmsXYZ2Lab = cmsXYZ2Lab +cmsXYZ2xyY = cmsXYZ2xyY +cmsXYZEncoded2Float = cmsXYZEncoded2Float +cmsSliceSpace16 = cmsSliceSpace16 +cmsSliceSpaceFloat = cmsSliceSpaceFloat +cmsChangeBuffersFormat = cmsChangeBuffersFormat +cmsDictAlloc = cmsDictAlloc +cmsDictFree = cmsDictFree +cmsDictDup = cmsDictDup +cmsDictAddEntry = cmsDictAddEntry +cmsDictGetEntryList = cmsDictGetEntryList +cmsDictNextEntry = cmsDictNextEntry +_cmsGetTransformUserData = _cmsGetTransformUserData +_cmsSetTransformUserData = _cmsSetTransformUserData +_cmsGetTransformFormatters16 = _cmsGetTransformFormatters16 +_cmsGetTransformFormattersFloat = _cmsGetTransformFormattersFloat +cmsGetHeaderCreator = cmsGetHeaderCreator +cmsPluginTHR = cmsPluginTHR +cmsGetPipelineContextID = cmsGetPipelineContextID +cmsGetTransformInputFormat = cmsGetTransformInputFormat +cmsGetTransformOutputFormat = cmsGetTransformOutputFormat +cmsCreateContext = cmsCreateContext +cmsDupContext = cmsDupContext +cmsDeleteContext = cmsDeleteContext +cmsGetContextUserData = cmsGetContextUserData +cmsUnregisterPluginsTHR = cmsUnregisterPluginsTHR +cmsSetAlarmCodesTHR = cmsSetAlarmCodesTHR +cmsGetAlarmCodesTHR = cmsGetAlarmCodesTHR +cmsSetAdaptationStateTHR = cmsSetAdaptationStateTHR +cmsSetLogErrorHandlerTHR = cmsSetLogErrorHandlerTHR +cmsGetSupportedIntentsTHR = cmsGetSupportedIntentsTHR +cmsMLUtranslationsCount = cmsMLUtranslationsCount +cmsMLUtranslationsCodes = cmsMLUtranslationsCodes +_cmsCreateMutex = _cmsCreateMutex +_cmsDestroyMutex = _cmsDestroyMutex +_cmsLockMutex = _cmsLockMutex +_cmsUnlockMutex = _cmsUnlockMutex \ No newline at end of file diff --git a/src/lcms2_internal.h b/src/lcms2_internal.h index d6300df..61ceb00 100644 --- a/src/lcms2_internal.h +++ b/src/lcms2_internal.h @@ -164,16 +164,16 @@ cmsINLINE cmsUInt16Number _cmsQuickSaturateWord(cmsFloat64Number d) return _cmsQuickFloorWord(d); } -// Plug-In registering --------------------------------------------------------------- +// Plug-In registration --------------------------------------------------------------- // Specialized function for plug-in memory management. No pairing free() since whole pool is freed at once. void* _cmsPluginMalloc(cmsContext ContextID, cmsUInt32Number size); // Memory management -cmsBool _cmsRegisterMemHandlerPlugin(cmsPluginBase* Plugin); +cmsBool _cmsRegisterMemHandlerPlugin(cmsContext ContextID, cmsPluginBase* Plugin); // Interpolation -cmsBool _cmsRegisterInterpPlugin(cmsPluginBase* Plugin); +cmsBool _cmsRegisterInterpPlugin(cmsContext ContextID, cmsPluginBase* Plugin); // Parametric curves cmsBool _cmsRegisterParametricCurvesPlugin(cmsContext ContextID, cmsPluginBase* Plugin); @@ -199,6 +199,9 @@ cmsBool _cmsRegisterOptimizationPlugin(cmsContext ContextID, cmsPluginBase* Plu // Transform cmsBool _cmsRegisterTransformPlugin(cmsContext ContextID, cmsPluginBase* Plugin); +// Mutex +cmsBool _cmsRegisterMutexPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + // --------------------------------------------------------------------------------------------------------- // Suballocators. Those are blocks of memory that is freed at the end on whole block. @@ -224,9 +227,266 @@ typedef struct { _cmsSubAllocator* _cmsCreateSubAlloc(cmsContext ContextID, cmsUInt32Number Initial); void _cmsSubAllocDestroy(_cmsSubAllocator* s); void* _cmsSubAlloc(_cmsSubAllocator* s, cmsUInt32Number size); +void* _cmsSubAllocDup(_cmsSubAllocator* s, const void *ptr, cmsUInt32Number size); // ---------------------------------------------------------------------------------- +// The context clients. +typedef enum { + + UserPtr, // User-defined pointer + Logger, + AlarmCodesContext, + AdaptationStateContext, + MemPlugin, + InterpPlugin, + CurvesPlugin, + FormattersPlugin, + TagTypePlugin, + TagPlugin, + IntentPlugin, + MPEPlugin, + OptimizationPlugin, + TransformPlugin, + MutexPlugin, + + // Last in list + MemoryClientMax + +} _cmsMemoryClient; + +// Magic number for identifying a valid context +#define cmsContextMagicNumber 0x63747832 // 'ctx2' + +// Container for memory management plug-in. +typedef struct { + + _cmsMallocFnPtrType MallocPtr; + _cmsMalloZerocFnPtrType MallocZeroPtr; + _cmsFreeFnPtrType FreePtr; + _cmsReallocFnPtrType ReallocPtr; + _cmsCallocFnPtrType CallocPtr; + _cmsDupFnPtrType DupPtr; + +} _cmsMemPluginChunkType; + +// Copy memory management function pointers from plug-in to chunk, taking care of missing routines +void _cmsInstallAllocFunctions(cmsPluginMemHandler* Plugin, _cmsMemPluginChunkType* ptr); + +// Internal structure for context +struct _cmsContext_struct { + + cmsUInt32Number Magic; // Magic number that identifies a valid context id in the new style + _cmsSubAllocator* MemPool; // The memory pool that stores context data + + void* chunks[MemoryClientMax]; // array of pointers to client chunks. Memory itself is hold in the suballocator. + // If NULL, then it reverts to global Context0 + + _cmsMemPluginChunkType DefaultMemoryManager; // The allocators used for creating the context itself. Cannot be overriden +}; + +// Returns a pointer to a valid context structure, including the global one if id is zero. +// Verifies the magic number. +struct _cmsContext_struct* _cmsGetContext(cmsContext ContextID); + +// Returns the block assigned to the specific zone. +void* _cmsContextGetClientChunk(cmsContext id, _cmsMemoryClient mc); + + +// Chunks of context memory by plug-in client ------------------------------------------------------- + +// Those structures encapsulates all variables needed by the several context clients (mostly plug-ins) + +// Container for error logger -- not a plug-in +typedef struct { + + cmsLogErrorHandlerFunction LogErrorHandler; // Set to NULL for Context0 fallback + +} _cmsLogErrorChunkType; + +// The global Context0 storage for error logger +extern _cmsLogErrorChunkType _cmsLogErrorChunk; + +// Allocate and init error logger container. +void _cmsAllocLogErrorChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// Container for alarm codes -- not a plug-in +typedef struct { + + cmsUInt16Number AlarmCodes[cmsMAXCHANNELS]; + +} _cmsAlarmCodesChunkType; + +// The global Context0 storage for alarm codes +extern _cmsAlarmCodesChunkType _cmsAlarmCodesChunk; + +// Allocate and init alarm codes container. +void _cmsAllocAlarmCodesChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// Container for adaptation state -- not a plug-in +typedef struct { + + cmsFloat64Number AdaptationState; + +} _cmsAdaptationStateChunkType; + +// The global Context0 storage for adaptation state +extern _cmsAdaptationStateChunkType _cmsAdaptationStateChunk; + +// Allocate and init adaptation state container. +void _cmsAllocAdaptationStateChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + + +// The global Context0 storage for memory management +extern _cmsMemPluginChunkType _cmsMemPluginChunk; + +// Allocate and init memory management container. +void _cmsAllocMemPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// Container for interpolation plug-in +typedef struct { + + cmsInterpFnFactory Interpolators; + +} _cmsInterpPluginChunkType; + +// The global Context0 storage for interpolation plug-in +extern _cmsInterpPluginChunkType _cmsInterpPluginChunk; + +// Allocate and init interpolation container. +void _cmsAllocInterpPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// Container for parametric curves plug-in +typedef struct { + + struct _cmsParametricCurvesCollection_st* ParametricCurves; + +} _cmsCurvesPluginChunkType; + +// The global Context0 storage for tone curves plug-in +extern _cmsCurvesPluginChunkType _cmsCurvesPluginChunk; + +// Allocate and init parametric curves container. +void _cmsAllocCurvesPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// Container for formatters plug-in +typedef struct { + + struct _cms_formatters_factory_list* FactoryList; + +} _cmsFormattersPluginChunkType; + +// The global Context0 storage for formatters plug-in +extern _cmsFormattersPluginChunkType _cmsFormattersPluginChunk; + +// Allocate and init formatters container. +void _cmsAllocFormattersPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// This chunk type is shared by TagType plug-in and MPE Plug-in +typedef struct { + + struct _cmsTagTypeLinkedList_st* TagTypes; + +} _cmsTagTypePluginChunkType; + + +// The global Context0 storage for tag types plug-in +extern _cmsTagTypePluginChunkType _cmsTagTypePluginChunk; + + +// The global Context0 storage for mult process elements plug-in +extern _cmsTagTypePluginChunkType _cmsMPETypePluginChunk; + +// Allocate and init Tag types container. +void _cmsAllocTagTypePluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); +// Allocate and init MPE container. +void _cmsAllocMPETypePluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); +// Container for tag plug-in +typedef struct { + + struct _cmsTagLinkedList_st* Tag; + +} _cmsTagPluginChunkType; + + +// The global Context0 storage for tag plug-in +extern _cmsTagPluginChunkType _cmsTagPluginChunk; + +// Allocate and init Tag container. +void _cmsAllocTagPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// Container for intents plug-in +typedef struct { + + struct _cms_intents_list* Intents; + +} _cmsIntentsPluginChunkType; + + +// The global Context0 storage for intents plug-in +extern _cmsIntentsPluginChunkType _cmsIntentsPluginChunk; + +// Allocate and init intents container. +void _cmsAllocIntentsPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// Container for optimization plug-in +typedef struct { + + struct _cmsOptimizationCollection_st* OptimizationCollection; + +} _cmsOptimizationPluginChunkType; + + +// The global Context0 storage for optimizers plug-in +extern _cmsOptimizationPluginChunkType _cmsOptimizationPluginChunk; + +// Allocate and init optimizers container. +void _cmsAllocOptimizationPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// Container for transform plug-in +typedef struct { + + struct _cmsTransformCollection_st* TransformCollection; + +} _cmsTransformPluginChunkType; + +// The global Context0 storage for full-transform replacement plug-in +extern _cmsTransformPluginChunkType _cmsTransformPluginChunk; + +// Allocate and init transform container. +void _cmsAllocTransformPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// Container for mutex plug-in +typedef struct { + + _cmsCreateMutexFnPtrType CreateMutexPtr; + _cmsDestroyMutexFnPtrType DestroyMutexPtr; + _cmsLockMutexFnPtrType LockMutexPtr; + _cmsUnlockMutexFnPtrType UnlockMutexPtr; + +} _cmsMutexPluginChunkType; + +// The global Context0 storage for mutex plug-in +extern _cmsMutexPluginChunkType _cmsMutexPluginChunk; + +// Allocate and init mutex container. +void _cmsAllocMutexPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + +// ---------------------------------------------------------------------------------- // MLU internal representation typedef struct { @@ -318,10 +578,14 @@ typedef struct _cms_iccprofile_struct { cmsBool TagSaveAsRaw[MAX_TABLE_TAG]; // True to write uncooked void * TagPtrs[MAX_TABLE_TAG]; cmsTagTypeHandler* TagTypeHandlers[MAX_TABLE_TAG]; // Same structure may be serialized on different types - // depending on profile version, so we keep track of the // type handler for each tag in the list. + // depending on profile version, so we keep track of the + // type handler for each tag in the list. // Special cmsBool IsWrite; + // Keep a mutex for cmsReadTag -- Note that this only works if the user includes a mutex plugin + void * UsrMutex; + } _cmsICCPROFILE; // IO helpers for profiles @@ -330,9 +594,9 @@ cmsBool _cmsWriteHeader(_cmsICCPROFILE* Icc, cmsUInt32Number UsedSp int _cmsSearchTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, cmsBool lFollowLinks); // Tag types -cmsTagTypeHandler* _cmsGetTagTypeHandler(cmsTagTypeSignature sig); +cmsTagTypeHandler* _cmsGetTagTypeHandler(cmsContext ContextID, cmsTagTypeSignature sig); cmsTagTypeSignature _cmsGetTagTrueType(cmsHPROFILE hProfile, cmsTagSignature sig); -cmsTagDescriptor* _cmsGetTagDescriptor(cmsTagSignature sig); +cmsTagDescriptor* _cmsGetTagDescriptor(cmsContext ContextID, cmsTagSignature sig); // Error logging --------------------------------------------------------------------------------------------------------- @@ -343,7 +607,7 @@ void _cmsTagSignature2String(char String[5], cmsTagSignature sig cmsInterpParams* _cmsComputeInterpParams(cmsContext ContextID, int nSamples, int InputChan, int OutputChan, const void* Table, cmsUInt32Number dwFlags); cmsInterpParams* _cmsComputeInterpParamsEx(cmsContext ContextID, const cmsUInt32Number nSamples[], int InputChan, int OutputChan, const void* Table, cmsUInt32Number dwFlags); void _cmsFreeInterpParams(cmsInterpParams* p); -cmsBool _cmsSetInterpolationRoutine(cmsInterpParams* p); +cmsBool _cmsSetInterpolationRoutine(cmsContext ContextID, cmsInterpParams* p); // Curves ---------------------------------------------------------------------------------------------------------------- @@ -474,7 +738,8 @@ cmsBool _cmsEndPointsBySpace(cmsColorSpaceSignature Space, cmsUInt16Number **Black, cmsUInt32Number *nOutputs); -cmsBool _cmsOptimizePipeline(cmsPipeline** Lut, +cmsBool _cmsOptimizePipeline(cmsContext ContextID, + cmsPipeline** Lut, int Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, @@ -499,7 +764,8 @@ cmsPipeline* _cmsCreateGamutCheckPipeline(cmsContext ContextID, cmsBool _cmsFormatterIsFloat(cmsUInt32Number Type); cmsBool _cmsFormatterIs8bit(cmsUInt32Number Type); -cmsFormatter _cmsGetFormatter(cmsUInt32Number Type, // Specific type, i.e. TYPE_RGB_8 +cmsFormatter _cmsGetFormatter(cmsContext ContextID, + cmsUInt32Number Type, // Specific type, i.e. TYPE_RGB_8 cmsFormatterDirection Dir, cmsUInt32Number dwFlags); diff --git a/testbed/Makefile.am b/testbed/Makefile.am index 7961a42..371ded2 100644 --- a/testbed/Makefile.am +++ b/testbed/Makefile.am @@ -13,11 +13,11 @@ check_PROGRAMS = testcms testcms_LDADD = $(top_builddir)/src/liblcms2.la testcms_LDFLAGS = @LDFLAGS@ -testcms_SOURCES = testcms2.c +testcms_SOURCES = testcms2.c testplugin.c zoo_icc.c testcms2.h EXTRA_DIST = test1.icc bad.icc toosmall.icc test2.icc \ test3.icc test4.icc \ - test5.icc + test5.icc ibm-t61.icc new.icc check: if [ $(top_srcdir) != $(top_builddir) ]; then \ diff --git a/testbed/Makefile.in b/testbed/Makefile.in index 11723ec..8c0f64c 100644 --- a/testbed/Makefile.in +++ b/testbed/Makefile.in @@ -64,7 +64,8 @@ am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ mkinstalldirs = $(install_sh) -d CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = -am_testcms_OBJECTS = testcms2.$(OBJEXT) +am_testcms_OBJECTS = testcms2.$(OBJEXT) testplugin.$(OBJEXT) \ + zoo_icc.$(OBJEXT) testcms_OBJECTS = $(am_testcms_OBJECTS) testcms_DEPENDENCIES = $(top_builddir)/src/liblcms2.la testcms_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ @@ -233,10 +234,10 @@ INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include -I$(top_srcdir)/src # CFLAGS = --pedantic -Wall -std=c99 -O2 testcms_LDADD = $(top_builddir)/src/liblcms2.la testcms_LDFLAGS = @LDFLAGS@ -testcms_SOURCES = testcms2.c +testcms_SOURCES = testcms2.c testplugin.c zoo_icc.c testcms2.h EXTRA_DIST = test1.icc bad.icc toosmall.icc test2.icc \ test3.icc test4.icc \ - test5.icc + test5.icc ibm-t61.icc new.icc all: all-am @@ -292,6 +293,8 @@ distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testcms2.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testplugin.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/zoo_icc.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< diff --git a/testbed/ibm-t61.icc b/testbed/ibm-t61.icc new file mode 100755 index 0000000..497f2d8 Binary files /dev/null and b/testbed/ibm-t61.icc differ diff --git a/testbed/new.icc b/testbed/new.icc new file mode 100644 index 0000000..dd2f757 Binary files /dev/null and b/testbed/new.icc differ diff --git a/testbed/testcms2.c b/testbed/testcms2.c index 01deae2..f4e70bb 100644 --- a/testbed/testcms2.c +++ b/testbed/testcms2.c @@ -1,7 +1,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2010 Marti Maria Saguer +// Copyright (c) 1998-2014 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"), @@ -24,11 +24,8 @@ //--------------------------------------------------------------------------------- // -#ifdef _MSC_VER -# define _CRT_SECURE_NO_WARNINGS 1 -#endif -#include "lcms2_internal.h" +#include "testcms2.h" // On Visual Studio, use debug CRT #ifdef _MSC_VER @@ -55,7 +52,6 @@ static cmsInt32Number 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); @@ -75,6 +71,7 @@ static cmsUInt32Number SingleHit, MaxAllocated=0, TotalMemory=0; typedef struct { cmsUInt32Number KeepSize; cmsContext WhoAllocated; + cmsUInt32Number DontCheck; union { cmsUInt64Number HiSparc; @@ -100,7 +97,7 @@ cmsContext DbgThread(void) { static cmsUInt32Number n = 1; - return (cmsContext) n++; + return (cmsContext) (n++ % 0xff0); } // The allocate routine @@ -126,10 +123,12 @@ void* DebugMalloc(cmsContext ContextID, cmsUInt32Number size) blk ->KeepSize = size; blk ->WhoAllocated = ContextID; + blk ->DontCheck = 0; return (void*) ((cmsUInt8Number*) blk + SIZE_OF_MEM_HEADER); } + // The free routine static void DebugFree(cmsContext ContextID, void *Ptr) @@ -143,13 +142,14 @@ void DebugFree(cmsContext ContextID, void *Ptr) blk = (_cmsMemoryBlock*) (((cmsUInt8Number*) Ptr) - SIZE_OF_MEM_HEADER); TotalMemory -= blk ->KeepSize; - if (blk ->WhoAllocated != ContextID) { + if (blk ->WhoAllocated != ContextID && !blk->DontCheck) { 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) @@ -177,11 +177,68 @@ void DebugMemPrintTotals(void) printf("Allocated = %u MaxAlloc = %u Single block hit = %u\n", TotalMemory, MaxAllocated, SingleHit); } + +void DebugMemDontCheckThis(void *Ptr) +{ + _cmsMemoryBlock* blk = (_cmsMemoryBlock*) (((cmsUInt8Number*) Ptr) - SIZE_OF_MEM_HEADER); + + blk ->DontCheck = 1; +} + + +// 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; +} + + +void TestMemoryLeaks(cmsBool ok) +{ + if (TotalMemory > 0) + printf("Ok, but %s are left!\n", MemStr(TotalMemory)); + else { + if (ok) printf("Ok.\n"); + } +} + // Here we go with the plug-in declaration -static cmsPluginMemHandler DebugMemHandler = {{ cmsPluginMagicNumber, 2000, cmsPluginMemHandlerSig, NULL }, +static cmsPluginMemHandler DebugMemHandler = {{ cmsPluginMagicNumber, 2060, cmsPluginMemHandlerSig, NULL }, DebugMalloc, DebugFree, DebugRealloc, NULL, NULL, NULL }; -// Utils ------------------------------------------------------------------------------------- +// Returnds a pointer to the memhandler plugin +void* PluginMemHandler(void) +{ + return (void*) &DebugMemHandler; +} + +cmsContext WatchDogContext(void* usr) +{ + cmsContext ctx; + + ctx = cmsCreateContext(&DebugMemHandler, usr); + + if (ctx == NULL) + Die("Unable to create memory managed context"); + + DebugMemDontCheckThis(ctx); + return ctx; +} + + static void FatalErrorQuit(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text) @@ -190,18 +247,29 @@ void FatalErrorQuit(cmsContext ContextID, cmsUInt32Number ErrorCode, const char cmsUNUSED_PARAMETER(ContextID); cmsUNUSED_PARAMETER(ErrorCode); +} + +void ResetFatalError(void) +{ + cmsSetLogErrorHandler(FatalErrorQuit); } + // Print a dot for gauging -static void Dot(void) { fprintf(stdout, "."); fflush(stdout); } +void Say(const char* str) +{ + fprintf(stdout, "%s", str); fflush(stdout); +} + + // Keep track of the reason to fail -static + void Fail(const char* frm, ...) { va_list args; @@ -211,7 +279,7 @@ void Fail(const char* frm, ...) } // Keep track of subtest -static + void SubTest(const char* frm, ...) { va_list args; @@ -222,27 +290,6 @@ void SubTest(const char* frm, ...) 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) @@ -259,10 +306,8 @@ void Check(const char* Title, TestFn Fn) 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"); + TestMemoryLeaks(TRUE); + } else { printf("FAIL!\n"); @@ -571,11 +616,8 @@ cmsInt32Number OneVirtual(cmsHPROFILE h, const char* SubTestTxt, const char* Fil h = cmsOpenProfileFromFile(FileName, "r"); if (h == NULL) return 0; - - // Do some teste.... - + cmsCloseProfile(h); - return 1; } @@ -744,7 +786,7 @@ cmsInt32Number CheckQuickFloor(void) (_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"); + "Please, edit lcms2.h and uncomment the CMS_DONT_USE_FAST_FLOOR toggle.\n"); return 0; } @@ -763,7 +805,7 @@ cmsInt32Number CheckQuickFloorWord(void) 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"); + "Please, edit lcms2.h and uncomment the CMS_DONT_USE_FAST_FLOOR toggle.\n"); return 0; } } @@ -787,7 +829,6 @@ cmsInt32Number CheckQuickFloorWord(void) 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); @@ -803,19 +844,18 @@ cmsBool IsGoodVal(const char *title, cmsFloat64Number in, cmsFloat64Number out, 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 )) { @@ -827,7 +867,6 @@ cmsBool IsGoodWord(const char *title, cmsUInt16Number in, cmsUInt16Number out) return TRUE; } -static cmsBool IsGoodWordPrec(const char *title, cmsUInt16Number in, cmsUInt16Number out, cmsUInt16Number maxErr) { if ((abs(in - out) > maxErr )) { @@ -891,6 +930,59 @@ cmsInt32Number CheckFixedPoint8_8(void) return 1; } +// D50 constant -------------------------------------------------------------------------------------------- + +static +cmsInt32Number CheckD50Roundtrip(void) +{ + cmsFloat64Number cmsD50X_2 = 0.96420288; + cmsFloat64Number cmsD50Y_2 = 1.0; + cmsFloat64Number cmsD50Z_2 = 0.82490540; + + cmsS15Fixed16Number xe = _cmsDoubleTo15Fixed16(cmsD50X); + cmsS15Fixed16Number ye = _cmsDoubleTo15Fixed16(cmsD50Y); + cmsS15Fixed16Number ze = _cmsDoubleTo15Fixed16(cmsD50Z); + + cmsFloat64Number x = _cms15Fixed16toDouble(xe); + cmsFloat64Number y = _cms15Fixed16toDouble(ye); + cmsFloat64Number z = _cms15Fixed16toDouble(ze); + + double dx = fabs(cmsD50X - x); + double dy = fabs(cmsD50Y - y); + double dz = fabs(cmsD50Z - z); + + double euc = sqrt(dx*dx + dy*dy + dz* dz); + + if (euc > 1E-5) { + + Fail("D50 roundtrip |err| > (%f) ", euc); + return 0; + } + + xe = _cmsDoubleTo15Fixed16(cmsD50X_2); + ye = _cmsDoubleTo15Fixed16(cmsD50Y_2); + ze = _cmsDoubleTo15Fixed16(cmsD50Z_2); + + x = _cms15Fixed16toDouble(xe); + y = _cms15Fixed16toDouble(ye); + z = _cms15Fixed16toDouble(ze); + + dx = fabs(cmsD50X_2 - x); + dy = fabs(cmsD50Y_2 - y); + dz = fabs(cmsD50Z_2 - z); + + euc = sqrt(dx*dx + dy*dy + dz* dz); + + if (euc > 1E-5) { + + Fail("D50 roundtrip |err| > (%f) ", euc); + return 0; + } + + + return 1; +} + // Linear interpolation ----------------------------------------------------------------------------------------------- // Since prime factors of 65535 (FFFF) are, @@ -3664,7 +3756,7 @@ Error: static cmsBool FormatterFailed; static -void CheckSingleFormatter16(cmsUInt32Number Type, const char* Text) +void CheckSingleFormatter16(cmsContext id, cmsUInt32Number Type, const char* Text) { cmsUInt16Number Values[cmsMAXCHANNELS]; cmsUInt8Number Buffer[1024]; @@ -3679,16 +3771,16 @@ void CheckSingleFormatter16(cmsUInt32Number Type, const char* Text) info.OutputFormat = info.InputFormat = Type; // Go forth and back - f = _cmsGetFormatter(Type, cmsFormatterInput, CMS_PACK_FLAGS_16BITS); - b = _cmsGetFormatter(Type, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS); + f = _cmsGetFormatter(id, Type, cmsFormatterInput, CMS_PACK_FLAGS_16BITS); + b = _cmsGetFormatter(id, Type, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS); if (f.Fmt16 == NULL || b.Fmt16 == NULL) { Fail("no formatter for %s", Text); FormatterFailed = TRUE; // Useful for debug - f = _cmsGetFormatter(Type, cmsFormatterInput, CMS_PACK_FLAGS_16BITS); - b = _cmsGetFormatter(Type, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS); + f = _cmsGetFormatter(id, Type, cmsFormatterInput, CMS_PACK_FLAGS_16BITS); + b = _cmsGetFormatter(id, Type, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS); return; } @@ -3733,7 +3825,7 @@ void CheckSingleFormatter16(cmsUInt32Number Type, const char* Text) } } -#define C(a) CheckSingleFormatter16(a, #a) +#define C(a) CheckSingleFormatter16(0, a, #a) // Check all formatters @@ -3931,16 +4023,16 @@ void CheckSingleFormatterFloat(cmsUInt32Number Type, const char* Text) info.OutputFormat = info.InputFormat = Type; // Go forth and back - f = _cmsGetFormatter(Type, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT); - b = _cmsGetFormatter(Type, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT); + f = _cmsGetFormatter(0, Type, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT); + b = _cmsGetFormatter(0, 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); + f = _cmsGetFormatter(0, Type, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT); + b = _cmsGetFormatter(0, Type, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT); return; } @@ -4017,8 +4109,10 @@ cmsInt32Number CheckFormattersFloat(void) C( TYPE_BGRA_HALF_FLT ); C( TYPE_ABGR_HALF_FLT ); + C( TYPE_XYZ_FLT ); + - return FormatterFailed == 0 ? 1 : 0; + return FormatterFailed == 0 ? 1 : 0; } #undef C @@ -5215,7 +5309,6 @@ cmsInt32Number CheckProfileCreation(void) SubTest("Tags holding ICC viewing conditions"); if (!CheckICCViewingConditions(Pass, h)) return 0; - SubTest("VCGT tags"); if (!CheckVCGT(Pass, h)) return 0; @@ -5249,6 +5342,44 @@ cmsInt32Number CheckProfileCreation(void) } +// Thanks to Christopher James Halse Rogers for the bugfixing and providing this test +static +cmsInt32Number CheckVersionHeaderWriting(void) +{ + cmsHPROFILE h; + int index; + float test_versions[] = { + 2.3f, + 4.08f, + 4.09f, + 4.3f + }; + + for (index = 0; index < sizeof(test_versions)/sizeof(test_versions[0]); index++) { + + h = cmsCreateProfilePlaceholder(DbgThread()); + if (h == NULL) return 0; + + cmsSetProfileVersion(h, test_versions[index]); + + cmsSaveProfileToFile(h, "versions.icc"); + cmsCloseProfile(h); + + h = cmsOpenProfileFromFileTHR(DbgThread(), "versions.icc", "r"); + + // Only the first 3 digits are significant + if (fabs(cmsGetProfileVersion(h) - test_versions[index]) > 0.005) { + Fail("Version failed to round-trip: wrote %.2f, read %.2f", + test_versions[index], cmsGetProfileVersion(h)); + return 0; + } + + cmsCloseProfile(h); + remove("versions.icc"); + } + return 1; +} + // Error reporting ------------------------------------------------------------------------------------------------------- @@ -6448,7 +6579,7 @@ cmsInt32Number CheckGamutCheck(void) SubTest("Gamut check on 16 bits"); - xform = cmsCreateProofingTransformTHR(DbgThread(), hAbove, TYPE_RGB_16, hAbove, TYPE_RGB_16, hAbove, + xform = cmsCreateProofingTransformTHR(DbgThread(), hAbove, TYPE_RGB_16, hAbove, TYPE_RGB_16, hSRGB, INTENT_RELATIVE_COLORIMETRIC, INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_GAMUTCHECK); cmsCloseProfile(hSRGB); @@ -7484,6 +7615,84 @@ cmsInt32Number CheckReadRAW(void) } +static +cmsInt32Number CheckMeta(void) +{ + char *data; + cmsHANDLE dict; + cmsHPROFILE p; + cmsUInt32Number clen; + FILE *fp; + int rc; + + /* open file */ + p = cmsOpenProfileFromFile("ibm-t61.icc", "r"); + if (p == NULL) return 0; + + /* read dictionary, but don't do anything with the value */ + //COMMENT OUT THE NEXT TWO LINES AND IT WORKS FINE!!! + dict = cmsReadTag(p, cmsSigMetaTag); + if (dict == NULL) return 0; + + /* serialize profile to memory */ + rc = cmsSaveProfileToMem(p, NULL, &clen); + if (!rc) return 0; + + data = (char*) malloc(clen); + rc = cmsSaveProfileToMem(p, data, &clen); + if (!rc) return 0; + + /* write the memory blob to a file */ + //NOTE: The crash does not happen if cmsSaveProfileToFile() is used */ + fp = fopen("new.icc", "wb"); + fwrite(data, 1, clen, fp); + fclose(fp); + free(data); + + cmsCloseProfile(p); + + /* open newly created file and read metadata */ + p = cmsOpenProfileFromFile("new.icc", "r"); + //ERROR: Bad dictionary Name/Value + //ERROR: Corrupted tag 'meta' + //test: test.c:59: main: Assertion `dict' failed. + dict = cmsReadTag(p, cmsSigMetaTag); + if (dict == NULL) return 0; + + cmsCloseProfile(p); + return 1; +} + + +// Bug on applying null transforms on floating point buffers +static +cmsInt32Number CheckFloatNULLxform(void) +{ + int i; + cmsFloat32Number in[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + cmsFloat32Number out[10]; + + cmsHTRANSFORM xform = cmsCreateTransform(NULL, TYPE_GRAY_FLT, NULL, TYPE_GRAY_FLT, INTENT_PERCEPTUAL, cmsFLAGS_NULLTRANSFORM); + + if (xform == NULL) { + Fail("Unable to create float null transform"); + return 0; + } + + cmsDoTransform(xform, in, out, 10); + + cmsDeleteTransform(xform); + for (i=0; i < 10; i++) { + + if (!IsGoodVal("float nullxform", in[i], out[i], 0.001)) { + + return 0; + } + } + + return 1; +} + // -------------------------------------------------------------------------------------------------- // P E R F O R M A N C E C H E C K S @@ -7513,6 +7722,9 @@ void PrintPerformance(cmsUInt32Number Bytes, cmsUInt32Number SizeOfPixel, cmsFlo } + + + static void SpeedTest16bits(const char * Title, cmsHPROFILE hlcmsProfileIn, cmsHPROFILE hlcmsProfileOut, cmsInt32Number Intent) { @@ -7880,271 +8092,23 @@ void PrintSupportedIntents(void) printf("\n"); } -// ZOO checks ------------------------------------------------------------------------------------------------------------ - - -#ifdef CMS_IS_WINDOWS_ - -static char ZOOfolder[cmsMAX_PATH] = "c:\\colormaps\\"; -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) -{ - cmsInt32Number 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) -{ - cmsInt32Number i, n; - cmsTagSignature sig; - cmsInt32Number 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; - cmsInt32Number 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); - - - cmsDetectDestinationBlackPoint(&Black, h, INTENT_PERCEPTUAL, 0); - cmsDetectDestinationBlackPoint(&Black, h, INTENT_RELATIVE_COLORIMETRIC, 0); - cmsDetectDestinationBlackPoint(&Black, h, INTENT_SATURATION, 0); - cmsDetectDestinationBlackPoint(&Black, h, INTENT_ABSOLUTE_COLORIMETRIC, 0); - cmsDetectTAC(h); -} - -// Check one specimen in the ZOO - -static -cmsInt32Number 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 -cmsInt32Number 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; - intptr_t hFile; - - cmsSetLogErrorHandler(NULL); - - if ( (hFile = _findfirst("c:\\colormaps\\*.*", &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 - - -#if 0 -#define TYPE_709 709 -static double Rec709Math(int Type, const double Params[], double R) -{ double Fun; - -switch (Type) -{ -case 709: - -if (R <= (Params[3]*Params[4])) Fun = R / Params[3]; -else Fun = pow(((R - Params[2])/Params[1]), Params[0]); -break; -case -709: -if (R <= Params[4]) Fun = R * Params[3]; -else Fun = Params[1] * pow(R, (1/Params[0])) + Params[2]; -break; -} -return Fun; -} +// --------------------------------------------------------------------------------------- -// Add nonstandard TRC curves -> Rec709 -cmsPluginParametricCurves NewCurvePlugin = { -{ cmsPluginMagicNumber, 2000, cmsPluginParametricCurveSig, NULL }, -1, {TYPE_709}, {5}, Rec709Math}; +#ifdef LCMS_FAST_EXTENSIONS + void* cmsFast8Bitextensions(void); #endif - - - -// --------------------------------------------------------------------------------------- - int main(int argc, char* argv[]) { cmsInt32Number Exhaustive = 0; cmsInt32Number DoSpeedTests = 1; cmsInt32Number DoCheckTests = 1; + cmsInt32Number DoPluginTests = 1; + cmsInt32Number DoZooTests = 0; #ifdef _MSC_VER _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); @@ -8158,6 +8122,12 @@ int main(int argc, char* argv[]) printf("Running exhaustive tests (will take a while...)\n\n"); } +#ifdef LCMS_FAST_EXTENSIONS + printf("Installing fast 8 bit extension ..."); + cmsPlugin(cmsFast8Bitextensions()); + printf("done.\n"); +#endif + printf("Installing debug memory plug-in ... "); cmsPlugin(&DebugMemHandler); printf("done.\n"); @@ -8166,26 +8136,23 @@ int main(int argc, char* argv[]) cmsSetLogErrorHandler(FatalErrorQuit); printf("done.\n"); -#ifdef CMS_IS_WINDOWS_ - // CheckProfileZOO(); -#endif PrintSupportedIntents(); - - - // Create utility profiles - Check("Creation of test profiles", CreateTestProfiles); - - if (DoCheckTests) - { 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); + Check("D50 roundtrip", CheckD50Roundtrip); + + // Create utility profiles + if (DoCheckTests || DoSpeedTests) + Check("Creation of test profiles", CreateTestProfiles); + if (DoCheckTests) { + // Forward 1D interpolation Check("1D interpolation in 2pt tables", Check1DLERP2); Check("1D interpolation in 3pt tables", Check1DLERP3); @@ -8303,7 +8270,7 @@ int main(int argc, char* argv[]) // Profile I/O (this one is huge!) Check("Profile creation", CheckProfileCreation); - + Check("Header version", CheckVersionHeaderWriting); // Error reporting Check("Error reporting on bad profiles", CheckErrReportingOnBadProfiles); @@ -8358,18 +8325,44 @@ int main(int argc, char* argv[]) Check("Floating Point sampled curve with non-zero start", CheckFloatSamples); Check("Floating Point segmented curve with short sampled segement", CheckFloatSegments); Check("Read RAW portions", CheckReadRAW); + Check("Check MetaTag", CheckMeta); + Check("Null transform on floats", CheckFloatNULLxform); + } + + if (DoPluginTests) + { +#ifndef CMS_CONTEXT_IN_LEGACY_MODE + Check("Context memory handling", CheckAllocContext); + Check("Simple context functionality", CheckSimpleContext); + Check("Alarm codes context", CheckAlarmColorsContext); + Check("Adaptation state context", CheckAdaptationStateContext); + Check("1D interpolation plugin", CheckInterp1DPlugin); + Check("3D interpolation plugin", CheckInterp3DPlugin); + Check("Parametric curve plugin", CheckParametricCurvePlugin); + Check("Formatters plugin", CheckFormattersPlugin); + Check("Tag type plugin", CheckTagTypePlugin); + Check("MPE type plugin", CheckMPEPlugin); + Check("Optimization plugin", CheckOptimizationPlugin); + Check("Rendering intent plugin", CheckIntentPlugin); + Check("Full transform plugin", CheckTransformPlugin); + Check("Mutex plugin", CheckMutexPlugin); +#endif } if (DoSpeedTests) SpeedTest(); + if (DoZooTests) + CheckProfileZOO(); + DebugMemPrintTotals(); cmsUnregisterPlugins(); // Cleanup - RemoveTestProfiles(); + if (DoCheckTests || DoSpeedTests) + RemoveTestProfiles(); return TotalFail; } diff --git a/testbed/testcms2.h b/testbed/testcms2.h new file mode 100755 index 0000000..a1a69fe --- /dev/null +++ b/testbed/testcms2.h @@ -0,0 +1,84 @@ +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2014 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. +// +//--------------------------------------------------------------------------------- +// + +#ifndef TESTCMS2_H +#define TESTCMS2_H + +#ifdef _MSC_VER +# define _CRT_SECURE_NO_WARNINGS 1 +# include "crtdbg.h" +# include +#endif + +#include "lcms2_internal.h" + +#define cmsmin(a, b) (((a) < (b)) ? (a) : (b)) + +// Used to mark special pointers +void DebugMemDontCheckThis(void *Ptr); + + +cmsBool IsGoodVal(const char *title, cmsFloat64Number in, cmsFloat64Number out, cmsFloat64Number max); +cmsBool IsGoodFixed15_16(const char *title, cmsFloat64Number in, cmsFloat64Number out); +cmsBool IsGoodFixed8_8(const char *title, cmsFloat64Number in, cmsFloat64Number out); +cmsBool IsGoodWord(const char *title, cmsUInt16Number in, cmsUInt16Number out); +cmsBool IsGoodWordPrec(const char *title, cmsUInt16Number in, cmsUInt16Number out, cmsUInt16Number maxErr); + +void* PluginMemHandler(void); +cmsContext WatchDogContext(void* usr); + +void ResetFatalError(void); +void Die(const char* Reason); +void Dot(void); +void Fail(const char* frm, ...); +void SubTest(const char* frm, ...); +void TestMemoryLeaks(cmsBool ok); +void Say(const char* str); + +// Plug-in tests +cmsInt32Number CheckSimpleContext(void); +cmsInt32Number CheckAllocContext(void); +cmsInt32Number CheckAlarmColorsContext(void); +cmsInt32Number CheckAdaptationStateContext(void); +cmsInt32Number CheckInterp1DPlugin(void); +cmsInt32Number CheckInterp3DPlugin(void); +cmsInt32Number CheckParametricCurvePlugin(void); +cmsInt32Number CheckFormattersPlugin(void); +cmsInt32Number CheckTagTypePlugin(void); +cmsInt32Number CheckMPEPlugin(void); +cmsInt32Number CheckOptimizationPlugin(void); +cmsInt32Number CheckIntentPlugin(void); +cmsInt32Number CheckTransformPlugin(void); +cmsInt32Number CheckMutexPlugin(void); + + +cmsInt32Number CheckOptimizationPluginLeak(void); + +// Zoo +void CheckProfileZOO(void); + +#endif + diff --git a/testbed/testplugin.c b/testbed/testplugin.c new file mode 100755 index 0000000..1ba00bc --- /dev/null +++ b/testbed/testplugin.c @@ -0,0 +1,1476 @@ +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2014 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. +// +//--------------------------------------------------------------------------------- +// + +#include "testcms2.h" + +// -------------------------------------------------------------------------------------------------- +// Auxiliar, duplicate a context and mark the block as non-debug because in this case the allocator +// and deallocator have different context owners +// -------------------------------------------------------------------------------------------------- + +static +cmsContext DupContext(cmsContext src, void* Data) +{ + cmsContext cpy = cmsDupContext(src, Data); + + DebugMemDontCheckThis(cpy); + + return cpy; +} + +// -------------------------------------------------------------------------------------------------- +// Simple context functions +// -------------------------------------------------------------------------------------------------- + +// Allocation order +cmsInt32Number CheckAllocContext(void) +{ + cmsContext c1, c2, c3, c4; + + + c1 = cmsCreateContext(NULL, NULL); // This creates a context by using the normal malloc + DebugMemDontCheckThis(c1); + cmsDeleteContext(c1); + + c2 = cmsCreateContext(PluginMemHandler(), NULL); // This creates a context by using the debug malloc + DebugMemDontCheckThis(c2); + cmsDeleteContext(c2); + + c1 = cmsCreateContext(NULL, NULL); + DebugMemDontCheckThis(c1); + + c2 = cmsCreateContext(PluginMemHandler(), NULL); + DebugMemDontCheckThis(c2); + + cmsPluginTHR(c1, PluginMemHandler()); // Now the context have custom allocators + + c3 = DupContext(c1, NULL); + c4 = DupContext(c2, NULL); + + + + cmsDeleteContext(c1); // Should be deleted by using nomal malloc + cmsDeleteContext(c2); // Should be deleted by using debug malloc + cmsDeleteContext(c3); // Should be deleted by using nomal malloc + cmsDeleteContext(c4); // Should be deleted by using debug malloc + + return 1; +} + +// Test the very basic context capabilities +cmsInt32Number CheckSimpleContext(void) +{ + int a = 1; + int b = 32; + cmsInt32Number rc = 0; + + cmsContext c1, c2, c3; + + // This function creates a context with a special + // memory manager that check allocation + c1 = WatchDogContext(&a); + cmsDeleteContext(c1); + + c1 = WatchDogContext(&a); + + // Let's check duplication + c2 = DupContext(c1, NULL); + c3 = DupContext(c2, NULL); + + // User data should have been propagated + rc = (*(int*) cmsGetContextUserData(c3)) == 1 ; + + // Free resources + cmsDeleteContext(c1); + cmsDeleteContext(c2); + cmsDeleteContext(c3); + + if (!rc) { + Fail("Creation of user data failed"); + return 0; + } + + // Back to create 3 levels of inherance + c1 = cmsCreateContext(NULL, &a); + DebugMemDontCheckThis(c1); + + c2 = DupContext(c1, NULL); + c3 = DupContext(c2, &b); + + rc = (*(int*) cmsGetContextUserData(c3)) == 32 ; + + cmsDeleteContext(c1); + cmsDeleteContext(c2); + cmsDeleteContext(c3); + + if (!rc) { + Fail("Modification of user data failed"); + return 0; + } + + // All seems ok + return rc; +} + + + + +// -------------------------------------------------------------------------------------------------- +//Alarm color functions +// -------------------------------------------------------------------------------------------------- + +// This function tests the alarm codes across contexts +cmsInt32Number CheckAlarmColorsContext(void) +{ + cmsInt32Number rc = 0; + const cmsUInt16Number codes[] = {0x0000, 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888, 0x9999, 0xaaaa, 0xbbbb, 0xcccc, 0xdddd, 0xeeee, 0xffff}; + cmsUInt16Number out[16]; + cmsContext c1, c2, c3; + int i; + + c1 = WatchDogContext(NULL); + + cmsSetAlarmCodesTHR(c1, codes); + c2 = DupContext(c1, NULL); + c3 = DupContext(c2, NULL); + + cmsGetAlarmCodesTHR(c3, out); + + rc = 1; + for (i=0; i < 16; i++) { + if (out[i] != codes[i]) { + Fail("Bad alarm code %x != %x", out[i], codes[i]); + rc = 0; + break; + } + } + + cmsDeleteContext(c1); + cmsDeleteContext(c2); + cmsDeleteContext(c3); + + return rc; +} + + +// -------------------------------------------------------------------------------------------------- +//Adaptation state functions +// -------------------------------------------------------------------------------------------------- + +// Similar to the previous, but for adaptation state +cmsInt32Number CheckAdaptationStateContext(void) +{ + cmsInt32Number rc = 0; + cmsContext c1, c2, c3; + cmsFloat64Number old1, old2; + + old1 = cmsSetAdaptationStateTHR(NULL, -1); + + c1 = WatchDogContext(NULL); + + cmsSetAdaptationStateTHR(c1, 0.7); + + c2 = DupContext(c1, NULL); + c3 = DupContext(c2, NULL); + + rc = IsGoodVal("Adaptation state", cmsSetAdaptationStateTHR(c3, -1), 0.7, 0.001); + + cmsDeleteContext(c1); + cmsDeleteContext(c2); + cmsDeleteContext(c3); + + old2 = cmsSetAdaptationStateTHR(NULL, -1); + + if (old1 != old2) { + Fail("Adaptation state has changed"); + return 0; + } + + return rc; +} + +// -------------------------------------------------------------------------------------------------- +// Interpolation plugin check: A fake 1D and 3D interpolation will be used to test the functionality. +// -------------------------------------------------------------------------------------------------- + +// This fake interpolation takes always the closest lower node in the interpolation table for 1D +static +void Fake1Dfloat(const cmsFloat32Number Value[], + cmsFloat32Number Output[], + const cmsInterpParams* p) +{ + cmsFloat32Number val2; + int cell; + const cmsFloat32Number* LutTable = (const cmsFloat32Number*) p ->Table; + + // Clip upper values + if (Value[0] >= 1.0) { + Output[0] = LutTable[p -> Domain[0]]; + return; + } + + val2 = p -> Domain[0] * Value[0]; + cell = (int) floor(val2); + Output[0] = LutTable[cell] ; +} + +// This fake interpolation just uses scrambled negated indexes for output +static +void Fake3D16(register const cmsUInt16Number Input[], + register cmsUInt16Number Output[], + register const struct _cms_interp_struc* p) +{ + Output[0] = 0xFFFF - Input[2]; + Output[1] = 0xFFFF - Input[1]; + Output[2] = 0xFFFF - Input[0]; +} + +// The factory chooses interpolation routines on depending on certain conditions. +cmsInterpFunction my_Interpolators_Factory(cmsUInt32Number nInputChannels, + cmsUInt32Number nOutputChannels, + cmsUInt32Number dwFlags) +{ + cmsInterpFunction Interpolation; + cmsBool IsFloat = (dwFlags & CMS_LERP_FLAGS_FLOAT); + + // Initialize the return to zero as a non-supported mark + memset(&Interpolation, 0, sizeof(Interpolation)); + + // For 1D to 1D and floating point + if (nInputChannels == 1 && nOutputChannels == 1 && IsFloat) { + + Interpolation.LerpFloat = Fake1Dfloat; + } + else + if (nInputChannels == 3 && nOutputChannels == 3 && !IsFloat) { + + // For 3D to 3D and 16 bits + Interpolation.Lerp16 = Fake3D16; + } + + // Here is the interpolation + return Interpolation; +} + +// Interpolation plug-in +static +cmsPluginInterpolation InterpPluginSample = { + + { cmsPluginMagicNumber, 2060, cmsPluginInterpolationSig, NULL }, + my_Interpolators_Factory +}; + + +// This is the check code for 1D interpolation plug-in +cmsInt32Number CheckInterp1DPlugin(void) +{ + cmsToneCurve* Sampled1D = NULL; + cmsContext ctx = NULL; + cmsContext cpy = NULL; + const cmsFloat32Number tab[] = { 0.0f, 0.10f, 0.20f, 0.30f, 0.40f, 0.50f, 0.60f, 0.70f, 0.80f, 0.90f, 1.00f }; // A straight line + + // 1st level context + ctx = WatchDogContext(NULL); + if (ctx == NULL) { + Fail("Cannot create context"); + goto Error; + } + + cmsPluginTHR(ctx, &InterpPluginSample); + + cpy = DupContext(ctx, NULL); + if (cpy == NULL) { + Fail("Cannot create context (2)"); + goto Error; + } + + Sampled1D = cmsBuildTabulatedToneCurveFloat(cpy, 11, tab); + if (Sampled1D == NULL) { + Fail("Cannot create tone curve (1)"); + goto Error; + } + + // Do some interpolations with the plugin + if (!IsGoodVal("0.10", cmsEvalToneCurveFloat(Sampled1D, 0.10f), 0.10, 0.01)) goto Error; + if (!IsGoodVal("0.13", cmsEvalToneCurveFloat(Sampled1D, 0.13f), 0.10, 0.01)) goto Error; + if (!IsGoodVal("0.55", cmsEvalToneCurveFloat(Sampled1D, 0.55f), 0.50, 0.01)) goto Error; + if (!IsGoodVal("0.9999", cmsEvalToneCurveFloat(Sampled1D, 0.9999f), 0.90, 0.01)) goto Error; + + cmsFreeToneCurve(Sampled1D); + cmsDeleteContext(ctx); + cmsDeleteContext(cpy); + + // Now in global context + Sampled1D = cmsBuildTabulatedToneCurveFloat(NULL, 11, tab); + if (Sampled1D == NULL) { + Fail("Cannot create tone curve (2)"); + goto Error; + } + + // Now without the plug-in + if (!IsGoodVal("0.10", cmsEvalToneCurveFloat(Sampled1D, 0.10f), 0.10, 0.001)) goto Error; + if (!IsGoodVal("0.13", cmsEvalToneCurveFloat(Sampled1D, 0.13f), 0.13, 0.001)) goto Error; + if (!IsGoodVal("0.55", cmsEvalToneCurveFloat(Sampled1D, 0.55f), 0.55, 0.001)) goto Error; + if (!IsGoodVal("0.9999", cmsEvalToneCurveFloat(Sampled1D, 0.9999f), 0.9999, 0.001)) goto Error; + + cmsFreeToneCurve(Sampled1D); + return 1; + +Error: + if (ctx != NULL) cmsDeleteContext(ctx); + if (cpy != NULL) cmsDeleteContext(ctx); + if (Sampled1D != NULL) cmsFreeToneCurve(Sampled1D); + return 0; + +} + +// Checks the 3D interpolation +cmsInt32Number CheckInterp3DPlugin(void) +{ + + cmsPipeline* p; + cmsStage* clut; + cmsContext ctx; + cmsUInt16Number In[3], Out[3]; + cmsUInt16Number identity[] = { + + 0, 0, 0, + 0, 0, 0xffff, + 0, 0xffff, 0, + 0, 0xffff, 0xffff, + 0xffff, 0, 0, + 0xffff, 0, 0xffff, + 0xffff, 0xffff, 0, + 0xffff, 0xffff, 0xffff + }; + + + ctx = WatchDogContext(NULL); + if (ctx == NULL) { + Fail("Cannot create context"); + return 0; + } + + + cmsPluginTHR(ctx, &InterpPluginSample); + + + p = cmsPipelineAlloc(ctx, 3, 3); + clut = cmsStageAllocCLut16bit(ctx, 2, 3, 3, identity); + cmsPipelineInsertStage(p, cmsAT_BEGIN, clut); + + // Do some interpolations with the plugin + + In[0] = 0; In[1] = 0; In[2] = 0; + cmsPipelineEval16(In, Out, p); + + if (!IsGoodWord("0", Out[0], 0xFFFF - 0)) goto Error; + if (!IsGoodWord("1", Out[1], 0xFFFF - 0)) goto Error; + if (!IsGoodWord("2", Out[2], 0xFFFF - 0)) goto Error; + + In[0] = 0x1234; In[1] = 0x5678; In[2] = 0x9ABC; + cmsPipelineEval16(In, Out, p); + + if (!IsGoodWord("0", 0xFFFF - 0x9ABC, Out[0])) goto Error; + if (!IsGoodWord("1", 0xFFFF - 0x5678, Out[1])) goto Error; + if (!IsGoodWord("2", 0xFFFF - 0x1234, Out[2])) goto Error; + + cmsPipelineFree(p); + cmsDeleteContext(ctx); + + // Now without the plug-in + + p = cmsPipelineAlloc(NULL, 3, 3); + clut = cmsStageAllocCLut16bit(NULL, 2, 3, 3, identity); + cmsPipelineInsertStage(p, cmsAT_BEGIN, clut); + + In[0] = 0; In[1] = 0; In[2] = 0; + cmsPipelineEval16(In, Out, p); + + if (!IsGoodWord("0", 0, Out[0])) goto Error; + if (!IsGoodWord("1", 0, Out[1])) goto Error; + if (!IsGoodWord("2", 0, Out[2])) goto Error; + + In[0] = 0x1234; In[1] = 0x5678; In[2] = 0x9ABC; + cmsPipelineEval16(In, Out, p); + + if (!IsGoodWord("0", 0x1234, Out[0])) goto Error; + if (!IsGoodWord("1", 0x5678, Out[1])) goto Error; + if (!IsGoodWord("2", 0x9ABC, Out[2])) goto Error; + + cmsPipelineFree(p); + return 1; + +Error: + cmsPipelineFree(p); + return 0; + +} + +// -------------------------------------------------------------------------------------------------- +// Parametric curve plugin check: sin(x)/cos(x) function will be used to test the functionality. +// -------------------------------------------------------------------------------------------------- + +#define TYPE_SIN 1000 +#define TYPE_COS 1010 +#define TYPE_TAN 1020 +#define TYPE_709 709 + +static cmsFloat64Number my_fns(cmsInt32Number Type, + const cmsFloat64Number Params[], + cmsFloat64Number R) +{ + cmsFloat64Number Val; + switch (Type) { + + case TYPE_SIN: + Val = Params[0]* sin(R * M_PI); + break; + + case -TYPE_SIN: + Val = asin(R) / (M_PI * Params[0]); + break; + + case TYPE_COS: + Val = Params[0]* cos(R * M_PI); + break; + + case -TYPE_COS: + Val = acos(R) / (M_PI * Params[0]); + break; + + default: return -1.0; + + } + + return Val; +} + +static +cmsFloat64Number my_fns2(cmsInt32Number Type, + const cmsFloat64Number Params[], + cmsFloat64Number R) +{ + cmsFloat64Number Val; + switch (Type) { + + case TYPE_TAN: + Val = Params[0]* tan(R * M_PI); + break; + + case -TYPE_TAN: + Val = atan(R) / (M_PI * Params[0]); + break; + + default: return -1.0; + } + + return Val; +} + + +static double Rec709Math(int Type, const double Params[], double R) +{ + double Fun; + + switch (Type) + { + case 709: + + if (R <= (Params[3]*Params[4])) Fun = R / Params[3]; + else Fun = pow(((R - Params[2])/Params[1]), Params[0]); + break; + + case -709: + + if (R <= Params[4]) Fun = R * Params[3]; + else Fun = Params[1] * pow(R, (1/Params[0])) + Params[2]; + break; + } + return Fun; +} + + +// Add nonstandard TRC curves -> Rec709 + +cmsPluginParametricCurves Rec709Plugin = { + + { cmsPluginMagicNumber, 2060, cmsPluginParametricCurveSig, NULL }, + + 1, {TYPE_709}, {5}, Rec709Math + +}; + + +static +cmsPluginParametricCurves CurvePluginSample = { + { cmsPluginMagicNumber, 2060, cmsPluginParametricCurveSig, NULL }, + + 2, // nFunctions + { TYPE_SIN, TYPE_COS }, // Function Types + { 1, 1 }, // ParameterCount + my_fns // Evaluator +}; + +static +cmsPluginParametricCurves CurvePluginSample2 = { + { cmsPluginMagicNumber, 2060, cmsPluginParametricCurveSig, NULL }, + + 1, // nFunctions + { TYPE_TAN}, // Function Types + { 1 }, // ParameterCount + my_fns2 // Evaluator +}; + +// -------------------------------------------------------------------------------------------------- +// In this test, the DupContext function will be checked as well +// -------------------------------------------------------------------------------------------------- +cmsInt32Number CheckParametricCurvePlugin(void) +{ + cmsContext ctx = NULL; + cmsContext cpy = NULL; + cmsContext cpy2 = NULL; + cmsToneCurve* sinus; + cmsToneCurve* cosinus; + cmsToneCurve* tangent; + cmsToneCurve* reverse_sinus; + cmsToneCurve* reverse_cosinus; + cmsFloat64Number scale = 1.0; + + + ctx = WatchDogContext(NULL); + + cmsPluginTHR(ctx, &CurvePluginSample); + + cpy = DupContext(ctx, NULL); + + cmsPluginTHR(cpy, &CurvePluginSample2); + + cpy2 = DupContext(cpy, NULL); + + cmsPluginTHR(cpy2, &Rec709Plugin); + + + sinus = cmsBuildParametricToneCurve(cpy, TYPE_SIN, &scale); + cosinus = cmsBuildParametricToneCurve(cpy, TYPE_COS, &scale); + tangent = cmsBuildParametricToneCurve(cpy, TYPE_TAN, &scale); + reverse_sinus = cmsReverseToneCurve(sinus); + reverse_cosinus = cmsReverseToneCurve(cosinus); + + + if (!IsGoodVal("0.10", cmsEvalToneCurveFloat(sinus, 0.10f), sin(0.10 * M_PI) , 0.001)) goto Error; + if (!IsGoodVal("0.60", cmsEvalToneCurveFloat(sinus, 0.60f), sin(0.60* M_PI), 0.001)) goto Error; + if (!IsGoodVal("0.90", cmsEvalToneCurveFloat(sinus, 0.90f), sin(0.90* M_PI), 0.001)) goto Error; + + if (!IsGoodVal("0.10", cmsEvalToneCurveFloat(cosinus, 0.10f), cos(0.10* M_PI), 0.001)) goto Error; + if (!IsGoodVal("0.60", cmsEvalToneCurveFloat(cosinus, 0.60f), cos(0.60* M_PI), 0.001)) goto Error; + if (!IsGoodVal("0.90", cmsEvalToneCurveFloat(cosinus, 0.90f), cos(0.90* M_PI), 0.001)) goto Error; + + if (!IsGoodVal("0.10", cmsEvalToneCurveFloat(tangent, 0.10f), tan(0.10* M_PI), 0.001)) goto Error; + if (!IsGoodVal("0.60", cmsEvalToneCurveFloat(tangent, 0.60f), tan(0.60* M_PI), 0.001)) goto Error; + if (!IsGoodVal("0.90", cmsEvalToneCurveFloat(tangent, 0.90f), tan(0.90* M_PI), 0.001)) goto Error; + + + if (!IsGoodVal("0.10", cmsEvalToneCurveFloat(reverse_sinus, 0.10f), asin(0.10)/M_PI, 0.001)) goto Error; + if (!IsGoodVal("0.60", cmsEvalToneCurveFloat(reverse_sinus, 0.60f), asin(0.60)/M_PI, 0.001)) goto Error; + if (!IsGoodVal("0.90", cmsEvalToneCurveFloat(reverse_sinus, 0.90f), asin(0.90)/M_PI, 0.001)) goto Error; + + if (!IsGoodVal("0.10", cmsEvalToneCurveFloat(reverse_cosinus, 0.10f), acos(0.10)/M_PI, 0.001)) goto Error; + if (!IsGoodVal("0.60", cmsEvalToneCurveFloat(reverse_cosinus, 0.60f), acos(0.60)/M_PI, 0.001)) goto Error; + if (!IsGoodVal("0.90", cmsEvalToneCurveFloat(reverse_cosinus, 0.90f), acos(0.90)/M_PI, 0.001)) goto Error; + + cmsFreeToneCurve(sinus); + cmsFreeToneCurve(cosinus); + cmsFreeToneCurve(tangent); + cmsFreeToneCurve(reverse_sinus); + cmsFreeToneCurve(reverse_cosinus); + + cmsDeleteContext(ctx); + cmsDeleteContext(cpy); + cmsDeleteContext(cpy2); + + return 1; + +Error: + + cmsFreeToneCurve(sinus); + cmsFreeToneCurve(reverse_sinus); + cmsFreeToneCurve(cosinus); + cmsFreeToneCurve(reverse_cosinus); + + if (ctx != NULL) cmsDeleteContext(ctx); + if (cpy != NULL) cmsDeleteContext(cpy); + if (cpy2 != NULL) cmsDeleteContext(cpy2); + return 0; +} + +// -------------------------------------------------------------------------------------------------- +// formatters plugin check: 5-6-5 RGB format +// -------------------------------------------------------------------------------------------------- + +// We define this special type as 0 bytes not float, and set the upper bit + +#define TYPE_RGB_565 (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(0) | (1 << 23)) + +cmsUInt8Number* my_Unroll565(register struct _cmstransform_struct* nfo, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + cmsUInt16Number pixel = *(cmsUInt16Number*) accum; // Take whole pixel + + double r = floor(((double) (pixel & 31) * 65535.0) / 31.0 + 0.5); + double g = floor((((pixel >> 5) & 63) * 65535.0) / 63.0 + 0.5); + double b = floor((((pixel >> 11) & 31) * 65535.0) / 31.0 + 0.5); + + wIn[2] = (cmsUInt16Number) r; + wIn[1] = (cmsUInt16Number) g; + wIn[0] = (cmsUInt16Number) b; + + return accum + 2; +} + +cmsUInt8Number* my_Pack565(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + + register cmsUInt16Number pixel; + int r, g, b; + + r = (int) floor(( wOut[2] * 31) / 65535.0 + 0.5); + g = (int) floor(( wOut[1] * 63) / 65535.0 + 0.5); + b = (int) floor(( wOut[0] * 31) / 65535.0 + 0.5); + + + pixel = (r & 31) | (( g & 63) << 5) | ((b & 31) << 11); + + + *(cmsUInt16Number*) output = pixel; + return output + 2; +} + + +cmsFormatter my_FormatterFactory(cmsUInt32Number Type, + cmsFormatterDirection Dir, + cmsUInt32Number dwFlags) +{ + cmsFormatter Result = { NULL }; + + if ((Type == TYPE_RGB_565) && + !(dwFlags & CMS_PACK_FLAGS_FLOAT) && + (Dir == cmsFormatterInput)) { + Result.Fmt16 = my_Unroll565; + } + return Result; +} + + +cmsFormatter my_FormatterFactory2(cmsUInt32Number Type, + cmsFormatterDirection Dir, + cmsUInt32Number dwFlags) +{ + cmsFormatter Result = { NULL }; + + if ((Type == TYPE_RGB_565) && + !(dwFlags & CMS_PACK_FLAGS_FLOAT) && + (Dir == cmsFormatterOutput)) { + Result.Fmt16 = my_Pack565; + } + return Result; +} + +static +cmsPluginFormatters FormattersPluginSample = { {cmsPluginMagicNumber, + 2060, + cmsPluginFormattersSig, + NULL}, + my_FormatterFactory }; + + + +static +cmsPluginFormatters FormattersPluginSample2 = { {cmsPluginMagicNumber, + 2060, + cmsPluginFormattersSig, + NULL}, + my_FormatterFactory2 }; + + +cmsInt32Number CheckFormattersPlugin(void) +{ + cmsContext ctx = WatchDogContext(NULL); + cmsContext cpy; + cmsContext cpy2; + cmsHTRANSFORM xform; + cmsUInt16Number stream[]= { 0xffffU, 0x1234U, 0x0000U, 0x33ddU }; + cmsUInt16Number result[4]; + int i; + + + cmsPluginTHR(ctx, &FormattersPluginSample); + + cpy = DupContext(ctx, NULL); + + cmsPluginTHR(cpy, &FormattersPluginSample2); + + cpy2 = DupContext(cpy, NULL); + + xform = cmsCreateTransformTHR(cpy2, NULL, TYPE_RGB_565, NULL, TYPE_RGB_565, INTENT_PERCEPTUAL, cmsFLAGS_NULLTRANSFORM); + + cmsDoTransform(xform, stream, result, 4); + + cmsDeleteTransform(xform); + cmsDeleteContext(ctx); + cmsDeleteContext(cpy); + cmsDeleteContext(cpy2); + + for (i=0; i < 4; i++) + if (stream[i] != result[i]) return 0; + + return 1; +} + +// -------------------------------------------------------------------------------------------------- +// TagTypePlugin plugin check +// -------------------------------------------------------------------------------------------------- + +#define SigIntType ((cmsTagTypeSignature) 0x74747448) // 'tttH' +#define SigInt ((cmsTagSignature) 0x74747448) // 'tttH' + +static +void *Type_int_Read(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + cmsUInt32Number* nItems, + cmsUInt32Number SizeOfTag) +{ + cmsUInt32Number* Ptr = (cmsUInt32Number*) _cmsMalloc(self ->ContextID, sizeof(cmsUInt32Number)); + if (Ptr == NULL) return NULL; + if (!_cmsReadUInt32Number(io, Ptr)) return NULL; + *nItems = 1; + return Ptr; +} + +static +cmsBool Type_int_Write(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + void* Ptr, cmsUInt32Number nItems) +{ + return _cmsWriteUInt32Number(io, *(cmsUInt32Number*) Ptr); +} + +static +void* Type_int_Dup(struct _cms_typehandler_struct* self, + const void *Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self ->ContextID, Ptr, n * sizeof(cmsUInt32Number)); +} + +void Type_int_Free(struct _cms_typehandler_struct* self, + void* Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + + +static cmsPluginTag HiddenTagPluginSample = { + + { cmsPluginMagicNumber, 2060, cmsPluginTagSig, NULL}, + SigInt, { 1, 1, { SigIntType }, NULL } +}; + +static cmsPluginTagType TagTypePluginSample = { + + { cmsPluginMagicNumber, 2060, cmsPluginTagTypeSig, (cmsPluginBase*) &HiddenTagPluginSample}, + { SigIntType, Type_int_Read, Type_int_Write, Type_int_Dup, Type_int_Free, NULL } +}; + + +cmsInt32Number CheckTagTypePlugin(void) +{ + cmsContext ctx = NULL; + cmsContext cpy = NULL; + cmsContext cpy2 = NULL; + cmsHPROFILE h = NULL; + cmsUInt32Number myTag = 1234; + cmsUInt32Number rc = 0; + char* data = NULL; + cmsUInt32Number *ptr = NULL; + cmsUInt32Number clen = 0; + + + ctx = WatchDogContext(NULL); + cmsPluginTHR(ctx, &TagTypePluginSample); + + cpy = DupContext(ctx, NULL); + cpy2 = DupContext(cpy, NULL); + + cmsDeleteContext(ctx); + cmsDeleteContext(cpy); + + h = cmsCreateProfilePlaceholder(cpy2); + if (h == NULL) { + Fail("Create placeholder failed"); + goto Error; + } + + + if (!cmsWriteTag(h, SigInt, &myTag)) { + Fail("Plug-in failed"); + goto Error; + } + + rc = cmsSaveProfileToMem(h, NULL, &clen); + if (!rc) { + Fail("Fetch mem size failed"); + goto Error; + } + + + data = (char*) malloc(clen); + if (data == NULL) { + Fail("malloc failed ?!?"); + goto Error; + } + + + rc = cmsSaveProfileToMem(h, data, &clen); + if (!rc) { + Fail("Save to mem failed"); + goto Error; + } + + cmsCloseProfile(h); + + cmsSetLogErrorHandler(NULL); + h = cmsOpenProfileFromMem(data, clen); + if (h == NULL) { + Fail("Open profile failed"); + goto Error; + } + + ptr = (cmsUInt32Number*) cmsReadTag(h, SigInt); + if (ptr != NULL) { + + Fail("read tag/context switching failed"); + goto Error; + } + + cmsCloseProfile(h); + ResetFatalError(); + + h = cmsOpenProfileFromMemTHR(cpy2, data, clen); + if (h == NULL) { + Fail("Open profile from mem failed"); + goto Error; + } + + // Get rid of data + free(data); data = NULL; + + ptr = (cmsUInt32Number*) cmsReadTag(h, SigInt); + if (ptr == NULL) { + Fail("Read tag/conext switching failed (2)"); + return 0; + } + + rc = (*ptr == 1234); + + cmsCloseProfile(h); + + cmsDeleteContext(cpy2); + + return rc; + +Error: + + if (h != NULL) cmsCloseProfile(h); + if (ctx != NULL) cmsDeleteContext(ctx); + if (cpy != NULL) cmsDeleteContext(cpy); + if (cpy2 != NULL) cmsDeleteContext(cpy2); + if (data) free(data); + + return 0; +} + +// -------------------------------------------------------------------------------------------------- +// MPE plugin check: +// -------------------------------------------------------------------------------------------------- +#define SigNegateType ((cmsStageSignature)0x6E202020) + +static +void EvaluateNegate(const cmsFloat32Number In[], + cmsFloat32Number Out[], + const cmsStage *mpe) +{ + Out[0] = 1.0f - In[0]; + Out[1] = 1.0f - In[1]; + Out[2] = 1.0f - In[2]; +} + +static +cmsStage* StageAllocNegate(cmsContext ContextID) +{ + return _cmsStageAllocPlaceholder(ContextID, + SigNegateType, 3, 3, EvaluateNegate, + NULL, NULL, NULL); +} + +static +void *Type_negate_Read(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + cmsUInt32Number* nItems, + cmsUInt32Number SizeOfTag) +{ + cmsUInt16Number Chans; + if (!_cmsReadUInt16Number(io, &Chans)) return NULL; + if (Chans != 3) return NULL; + + *nItems = 1; + return StageAllocNegate(self -> ContextID); +} + +static +cmsBool Type_negate_Write(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + void* Ptr, cmsUInt32Number nItems) +{ + + if (!_cmsWriteUInt16Number(io, 3)) return FALSE; + return TRUE; +} + +static +cmsPluginMultiProcessElement MPEPluginSample = { + + {cmsPluginMagicNumber, 2060, cmsPluginMultiProcessElementSig, NULL}, + + { (cmsTagTypeSignature) SigNegateType, Type_negate_Read, Type_negate_Write, NULL, NULL, NULL } +}; + + +cmsInt32Number CheckMPEPlugin(void) +{ + cmsContext ctx = NULL; + cmsContext cpy = NULL; + cmsContext cpy2 = NULL; + cmsHPROFILE h = NULL; + cmsUInt32Number myTag = 1234; + cmsUInt32Number rc = 0; + char* data = NULL; + cmsUInt32Number clen = 0; + cmsFloat32Number In[3], Out[3]; + cmsPipeline* pipe; + + ctx = WatchDogContext(NULL); + cmsPluginTHR(ctx, &MPEPluginSample); + + cpy = DupContext(ctx, NULL); + cpy2 = DupContext(cpy, NULL); + + cmsDeleteContext(ctx); + cmsDeleteContext(cpy); + + h = cmsCreateProfilePlaceholder(cpy2); + if (h == NULL) { + Fail("Create placeholder failed"); + goto Error; + } + + pipe = cmsPipelineAlloc(cpy2, 3, 3); + cmsPipelineInsertStage(pipe, cmsAT_BEGIN, StageAllocNegate(cpy2)); + + + In[0] = 0.3f; In[1] = 0.2f; In[2] = 0.9f; + cmsPipelineEvalFloat(In, Out, pipe); + + rc = (IsGoodVal("0", Out[0], 1.0-In[0], 0.001) && + IsGoodVal("1", Out[1], 1.0-In[1], 0.001) && + IsGoodVal("2", Out[2], 1.0-In[2], 0.001)); + + if (!rc) { + Fail("Pipeline failed"); + goto Error; + } + + if (!cmsWriteTag(h, cmsSigDToB3Tag, pipe)) { + Fail("Plug-in failed"); + goto Error; + } + + // This cleans the stage as well + cmsPipelineFree(pipe); + + rc = cmsSaveProfileToMem(h, NULL, &clen); + if (!rc) { + Fail("Fetch mem size failed"); + goto Error; + } + + + data = (char*) malloc(clen); + if (data == NULL) { + Fail("malloc failed ?!?"); + goto Error; + } + + + rc = cmsSaveProfileToMem(h, data, &clen); + if (!rc) { + Fail("Save to mem failed"); + goto Error; + } + + cmsCloseProfile(h); + + + cmsSetLogErrorHandler(NULL); + h = cmsOpenProfileFromMem(data, clen); + if (h == NULL) { + Fail("Open profile failed"); + goto Error; + } + + pipe = (cmsPipeline*) cmsReadTag(h, cmsSigDToB3Tag); + if (pipe != NULL) { + + // Unsupported stage, should fail + Fail("read tag/context switching failed"); + goto Error; + } + + cmsCloseProfile(h); + + ResetFatalError(); + + h = cmsOpenProfileFromMemTHR(cpy2, data, clen); + if (h == NULL) { + Fail("Open profile from mem failed"); + goto Error; + } + + // Get rid of data + free(data); data = NULL; + + pipe = (cmsPipeline*) cmsReadTag(h, cmsSigDToB3Tag); + if (pipe == NULL) { + Fail("Read tag/conext switching failed (2)"); + return 0; + } + + // Evaluate for negation + In[0] = 0.3f; In[1] = 0.2f; In[2] = 0.9f; + cmsPipelineEvalFloat(In, Out, pipe); + + rc = (IsGoodVal("0", Out[0], 1.0-In[0], 0.001) && + IsGoodVal("1", Out[1], 1.0-In[1], 0.001) && + IsGoodVal("2", Out[2], 1.0-In[2], 0.001)); + + cmsCloseProfile(h); + + cmsDeleteContext(cpy2); + + return rc; + +Error: + + if (h != NULL) cmsCloseProfile(h); + if (ctx != NULL) cmsDeleteContext(ctx); + if (cpy != NULL) cmsDeleteContext(cpy); + if (cpy2 != NULL) cmsDeleteContext(cpy2); + if (data) free(data); + + return 0; +} + + +// -------------------------------------------------------------------------------------------------- +// Optimization plugin check: +// -------------------------------------------------------------------------------------------------- + +static +void FastEvaluateCurves(register const cmsUInt16Number In[], + register cmsUInt16Number Out[], + register const void* Data) +{ + Out[0] = In[0]; +} + +static +cmsBool MyOptimize(cmsPipeline** Lut, + cmsUInt32Number Intent, + cmsUInt32Number* InputFormat, + cmsUInt32Number* OutputFormat, + cmsUInt32Number* dwFlags) +{ + cmsStage* mpe; + _cmsStageToneCurvesData* Data; + + // Only curves in this LUT? All are identities? + for (mpe = cmsPipelineGetPtrToFirstStage(*Lut); + mpe != NULL; + mpe = cmsStageNext(mpe)) { + + if (cmsStageType(mpe) != cmsSigCurveSetElemType) return FALSE; + + // Check for identity + Data = (_cmsStageToneCurvesData*) cmsStageData(mpe); + if (Data ->nCurves != 1) return FALSE; + if (cmsEstimateGamma(Data->TheCurves[0], 0.1) > 1.0) return FALSE; + + } + + *dwFlags |= cmsFLAGS_NOCACHE; + _cmsPipelineSetOptimizationParameters(*Lut, FastEvaluateCurves, NULL, NULL, NULL); + + return TRUE; +} + +cmsPluginOptimization OptimizationPluginSample = { + + {cmsPluginMagicNumber, 2060, cmsPluginOptimizationSig, NULL}, + MyOptimize +}; + + +cmsInt32Number CheckOptimizationPlugin(void) +{ + cmsContext ctx = WatchDogContext(NULL); + cmsContext cpy; + cmsContext cpy2; + cmsHTRANSFORM xform; + cmsUInt8Number In[]= { 10, 20, 30, 40 }; + cmsUInt8Number Out[4]; + cmsToneCurve* Linear; + cmsHPROFILE h; + int i; + + cmsPluginTHR(ctx, &OptimizationPluginSample); + + cpy = DupContext(ctx, NULL); + cpy2 = DupContext(cpy, NULL); + + Linear = cmsBuildGamma(cpy2, 1.0); + h = cmsCreateLinearizationDeviceLinkTHR(cpy2, cmsSigGrayData, &Linear); + cmsFreeToneCurve(Linear); + + xform = cmsCreateTransformTHR(cpy2, h, TYPE_GRAY_8, h, TYPE_GRAY_8, INTENT_PERCEPTUAL, 0); + cmsCloseProfile(h); + + cmsDoTransform(xform, In, Out, 4); + + cmsDeleteTransform(xform); + cmsDeleteContext(ctx); + cmsDeleteContext(cpy); + cmsDeleteContext(cpy2); + + for (i=0; i < 4; i++) + if (In[i] != Out[i]) return 0; + + return 1; +} + + +// -------------------------------------------------------------------------------------------------- +// Check the intent plug-in +// -------------------------------------------------------------------------------------------------- + +/* + This example creates a new rendering intent, at intent number 300, that is identical to perceptual + intent for all color spaces but gray to gray transforms, in this case it bypasses the data. + Note that it has to clear all occurrences of intent 300 in the intents array to avoid + infinite recursion. +*/ + +#define INTENT_DECEPTIVE 300 + +static +cmsPipeline* MyNewIntent(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number TheIntents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) +{ + cmsPipeline* Result; + cmsUInt32Number ICCIntents[256]; + cmsUInt32Number i; + + for (i=0; i < nProfiles; i++) + ICCIntents[i] = (TheIntents[i] == INTENT_DECEPTIVE) ? INTENT_PERCEPTUAL : + TheIntents[i]; + + if (cmsGetColorSpace(hProfiles[0]) != cmsSigGrayData || + cmsGetColorSpace(hProfiles[nProfiles-1]) != cmsSigGrayData) + return _cmsDefaultICCintents(ContextID, nProfiles, + ICCIntents, hProfiles, + BPC, AdaptationStates, + dwFlags); + + Result = cmsPipelineAlloc(ContextID, 1, 1); + if (Result == NULL) return NULL; + + cmsPipelineInsertStage(Result, cmsAT_BEGIN, + cmsStageAllocIdentity(ContextID, 1)); + + return Result; +} + +static cmsPluginRenderingIntent IntentPluginSample = { + + {cmsPluginMagicNumber, 2060, cmsPluginRenderingIntentSig, NULL}, + + INTENT_DECEPTIVE, MyNewIntent, "bypass gray to gray rendering intent" +}; + +cmsInt32Number CheckIntentPlugin(void) +{ + cmsContext ctx = WatchDogContext(NULL); + cmsContext cpy; + cmsContext cpy2; + cmsHTRANSFORM xform; + cmsHPROFILE h1, h2; + cmsToneCurve* Linear1; + cmsToneCurve* Linear2; + cmsUInt8Number In[]= { 10, 20, 30, 40 }; + cmsUInt8Number Out[4]; + int i; + + cmsPluginTHR(ctx, &IntentPluginSample); + + cpy = DupContext(ctx, NULL); + cpy2 = DupContext(cpy, NULL); + + Linear1 = cmsBuildGamma(cpy2, 3.0); + Linear2 = cmsBuildGamma(cpy2, 0.1); + h1 = cmsCreateLinearizationDeviceLinkTHR(cpy2, cmsSigGrayData, &Linear1); + h2 = cmsCreateLinearizationDeviceLinkTHR(cpy2, cmsSigGrayData, &Linear2); + + cmsFreeToneCurve(Linear1); + cmsFreeToneCurve(Linear2); + + xform = cmsCreateTransformTHR(cpy2, h1, TYPE_GRAY_8, h2, TYPE_GRAY_8, INTENT_DECEPTIVE, 0); + cmsCloseProfile(h1); cmsCloseProfile(h2); + + cmsDoTransform(xform, In, Out, 4); + + cmsDeleteTransform(xform); + cmsDeleteContext(ctx); + cmsDeleteContext(cpy); + cmsDeleteContext(cpy2); + + for (i=0; i < 4; i++) + if (Out[i] != In[i]) return 0; + + return 1; +} + + +// -------------------------------------------------------------------------------------------------- +// Check the full transform plug-in +// -------------------------------------------------------------------------------------------------- + +// This is a sample intent that only works for gray8 as output, and always returns '42' +static +void TrancendentalTransform(struct _cmstransform_struct * CMM, + const void* InputBuffer, + void* OutputBuffer, + cmsUInt32Number Size, + cmsUInt32Number Stride) +{ + cmsUInt32Number i; + + for (i=0; i < Size; i++) + { + ((cmsUInt8Number*) OutputBuffer)[i] = 0x42; + } + +} + + +cmsBool TransformFactory(_cmsTransformFn* xformPtr, + void** UserData, + _cmsFreeUserDataFn* FreePrivateDataFn, + cmsPipeline** Lut, + cmsUInt32Number* InputFormat, + cmsUInt32Number* OutputFormat, + cmsUInt32Number* dwFlags) + +{ + if (*OutputFormat == TYPE_GRAY_8) + { + // *Lut holds the pipeline to be applied + *xformPtr = TrancendentalTransform; + return TRUE; + } + + return FALSE; +} + + +// The Plug-in entry point +static cmsPluginTransform FullTransformPluginSample = { + + { cmsPluginMagicNumber, 2060, cmsPluginTransformSig, NULL}, + + TransformFactory +}; + +cmsInt32Number CheckTransformPlugin(void) +{ + cmsContext ctx = WatchDogContext(NULL); + cmsContext cpy; + cmsContext cpy2; + cmsHTRANSFORM xform; + cmsUInt8Number In[]= { 10, 20, 30, 40 }; + cmsUInt8Number Out[4]; + cmsToneCurve* Linear; + cmsHPROFILE h; + int i; + + + cmsPluginTHR(ctx, &FullTransformPluginSample); + + cpy = DupContext(ctx, NULL); + cpy2 = DupContext(cpy, NULL); + + Linear = cmsBuildGamma(cpy2, 1.0); + h = cmsCreateLinearizationDeviceLinkTHR(cpy2, cmsSigGrayData, &Linear); + cmsFreeToneCurve(Linear); + + xform = cmsCreateTransformTHR(cpy2, h, TYPE_GRAY_8, h, TYPE_GRAY_8, INTENT_PERCEPTUAL, 0); + cmsCloseProfile(h); + + cmsDoTransform(xform, In, Out, 4); + + + cmsDeleteTransform(xform); + cmsDeleteContext(ctx); + cmsDeleteContext(cpy); + cmsDeleteContext(cpy2); + + for (i=0; i < 4; i++) + if (Out[i] != 0x42) return 0; + + return 1; +} + + +// -------------------------------------------------------------------------------------------------- +// Check the mutex plug-in +// -------------------------------------------------------------------------------------------------- + +typedef struct { + int nlocks; +} MyMtx; + + +static +void* MyMtxCreate(cmsContext id) +{ + MyMtx* mtx = (MyMtx*) _cmsMalloc(id, sizeof(MyMtx)); + mtx ->nlocks = 0; + return mtx; +} + +static +void MyMtxDestroy(cmsContext id, void* mtx) +{ + MyMtx* mtx_ = (MyMtx*) mtx; + + if (mtx_->nlocks != 0) + Die("Locks != 0 when setting free a mutex"); + + _cmsFree(id, mtx); + +} + +static +cmsBool MyMtxLock(cmsContext id, void* mtx) +{ + MyMtx* mtx_ = (MyMtx*) mtx; + mtx_->nlocks++; + + return TRUE; +} + +static +void MyMtxUnlock(cmsContext id, void* mtx) +{ + MyMtx* mtx_ = (MyMtx*) mtx; + mtx_->nlocks--; + +} + + +static cmsPluginMutex MutexPluginSample = { + + { cmsPluginMagicNumber, 2060, cmsPluginMutexSig, NULL}, + + MyMtxCreate, MyMtxDestroy, MyMtxLock, MyMtxUnlock +}; + + +cmsInt32Number CheckMutexPlugin(void) +{ + cmsContext ctx = WatchDogContext(NULL); + cmsContext cpy; + cmsContext cpy2; + cmsHTRANSFORM xform; + cmsUInt8Number In[]= { 10, 20, 30, 40 }; + cmsUInt8Number Out[4]; + cmsToneCurve* Linear; + cmsHPROFILE h; + int i; + + + cmsPluginTHR(ctx, &MutexPluginSample); + + cpy = DupContext(ctx, NULL); + cpy2 = DupContext(cpy, NULL); + + Linear = cmsBuildGamma(cpy2, 1.0); + h = cmsCreateLinearizationDeviceLinkTHR(cpy2, cmsSigGrayData, &Linear); + cmsFreeToneCurve(Linear); + + xform = cmsCreateTransformTHR(cpy2, h, TYPE_GRAY_8, h, TYPE_GRAY_8, INTENT_PERCEPTUAL, 0); + cmsCloseProfile(h); + + cmsDoTransform(xform, In, Out, 4); + + + cmsDeleteTransform(xform); + cmsDeleteContext(ctx); + cmsDeleteContext(cpy); + cmsDeleteContext(cpy2); + + for (i=0; i < 4; i++) + if (Out[i] != In[i]) return 0; + + return 1; +} diff --git a/testbed/testthread.cpp b/testbed/testthread.cpp index 435445c..d553567 100644 --- a/testbed/testthread.cpp +++ b/testbed/testthread.cpp @@ -1,52 +1,120 @@ #include -#include "lcms.h" +#include "lcms2_plugin.h" -static cmsHPROFILE prof_xyz,prof_rgb; -static cmsHTRANSFORM trans_xyz_to_rgb,trans_rgb_to_xyz; +static cmsContext ctx; +static cmsHPROFILE prof_cmyk, prof_rgb; +static volatile int rc = 0; -static DWORD WINAPI make_trans_xyz_to_rgb(LPVOID lpParameter) + +static +void* MyMtxCreate(cmsContext id) { - trans_xyz_to_rgb = cmsCreateTransform( - prof_xyz,TYPE_XYZ_DBL, - prof_rgb,TYPE_RGB_DBL, - INTENT_ABSOLUTE_COLORIMETRIC,cmsFLAGS_NOTPRECALC); - return 0; + return (void*) CreateMutex( NULL, FALSE, NULL); } -static DWORD WINAPI make_trans_rgb_to_xyz(LPVOID lpParameter) +static +void MyMtxDestroy(cmsContext id, void* mtx) { - trans_rgb_to_xyz = cmsCreateTransform( - prof_rgb,TYPE_RGB_DBL, - prof_xyz,TYPE_XYZ_DBL, - INTENT_ABSOLUTE_COLORIMETRIC,cmsFLAGS_NOTPRECALC); - return 0; + CloseHandle((HANDLE) mtx); +} + +static +cmsBool MyMtxLock(cmsContext id, void* mtx) +{ + WaitForSingleObject((HANDLE) mtx, INFINITE); + return TRUE; +} + +static +void MyMtxUnlock(cmsContext id, void* mtx) +{ + ReleaseMutex((HANDLE) mtx); +} + + +static cmsPluginMutex MutexPluginSample = { + + { cmsPluginMagicNumber, 2060, cmsPluginMutexSig, NULL}, + + MyMtxCreate, MyMtxDestroy, MyMtxLock, MyMtxUnlock +}; + + +static DWORD WINAPI one_thread(LPVOID lpParameter) +{ + int i, j; + cmsUInt8Number rgb[3*1000]; + cmsUInt8Number cmyk[4*1000]; + + Sleep(rand() % 500 ); + cmsHTRANSFORM xform = cmsCreateTransformTHR(ctx, prof_rgb, TYPE_RGB_8, prof_cmyk, TYPE_CMYK_8, 0, 0); + + for (i=0; i < 10000; i++) { + + for (j=0; j < 1000; j++) + { + rgb[j * 3 ] = 189; + rgb[j * 3 + 1] = 100; + rgb[j * 3 + 2] = 75; + } + cmsDoTransform(xform, rgb, cmyk, 1000); + for (j=0; j < 1000; j++) + { + if (cmyk[j * 4 ] != 37 || + cmyk[j * 4 + 1 ] != 188 || + cmyk[j * 4 + 2 ] != 195 || + cmyk[j * 4 + 3 ] != 7) + { + OutputDebugString(L"ERROR\n"); + rc = 1; + } + + } + + } + + cmsDeleteTransform(xform); + + return 0; } -int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR -lpCmdLine,int nCmdShow) +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); + int i; + cmsContext ctx; + + OutputDebugString(L"Test in progress...\n"); + + ctx = cmsCreateContext(&MutexPluginSample, 0); + + prof_cmyk = cmsOpenProfileFromFileTHR(ctx, "USWebCoatedSWOP.icc", "r"); + prof_rgb = cmsOpenProfileFromFileTHR(ctx, "AdobeRGB1998.icc","r"); + + +#define NWORKERS 10 + + HANDLE workers[NWORKERS]; + + + for (int i=0; i