From f7520395821090b36a5ad8c658a844c3342dbf66 Mon Sep 17 00:00:00 2001 From: Nicolas Pena Date: Thu, 10 Aug 2017 16:36:56 -0400 Subject: LCMS: rename folder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I5f240cb0779648dc5427fecb5561086e7c0fb16a Reviewed-on: https://pdfium-review.googlesource.com/10650 Reviewed-by: dsinclair Commit-Queue: Nicolás Peña --- core/fxcodec/DEPS | 2 +- core/fxcodec/codec/ccodec_iccmodule.h | 2 +- core/fxcodec/codec/fx_codec_icc.cpp | 2 +- core/fxcodec/codec/fx_codec_jpx_opj.cpp | 2 +- third_party/BUILD.gn | 54 +- third_party/lcms/0000-tag-type-confusion.patch | 14 + third_party/lcms/0001-from16-to-8-overflow.patch | 13 + .../0002-infinite-loop-GrowNamedColorList.patch | 34 + third_party/lcms/0003-uninit.patch | 30 + .../lcms/0004-memory-leak-Type_Curve_Read.patch | 28 + .../0005-memory-leak-AllocEmptyTransform.patch | 34 + .../0006-memory-leak-Type_NamedColor_Read.patch | 22 + .../0007-memory-leak-OptimizeByResampling.patch | 13 + .../0008-memory-leak-Type_MPEmatrix_Read.patch | 30 + .../lcms/0009-cmsStageAllocMatrix-param-swap.patch | 20 + third_party/lcms/0010-reject-nan.patch | 13 + .../lcms/0011-memory-leak-ReadSegmentedCurve.patch | 36 + third_party/lcms/0012-backport-c0a98d86.patch | 53 + third_party/lcms/0013-utf8.patch | 99 + third_party/lcms/0014-avoid-fixed-inf.patch | 95 + third_party/lcms/0015-sanitize-float-read.patch | 15 + third_party/lcms/0016-check-LUT-and-MPE.patch | 170 + ...-upstream-integer-overflow-MPEmatrix_Read.patch | 85 + .../lcms/0018-verify-size-before-reading.patch | 17 + .../0019-upstream-direct-leak-Type_MPE_Read.patch | 31 + third_party/lcms/README.pdfium | 35 + third_party/lcms/include/lcms2.h | 1882 +++++++ third_party/lcms/include/lcms2_plugin.h | 637 +++ third_party/lcms/src/cmscam02.c | 486 ++ third_party/lcms/src/cmscgats.c | 2776 ++++++++++ third_party/lcms/src/cmscnvrt.c | 1142 ++++ third_party/lcms/src/cmserr.c | 707 +++ third_party/lcms/src/cmsgamma.c | 1298 +++++ third_party/lcms/src/cmsgmt.c | 590 ++ third_party/lcms/src/cmshalf.c | 534 ++ third_party/lcms/src/cmsintrp.c | 1506 ++++++ third_party/lcms/src/cmsio0.c | 1895 +++++++ third_party/lcms/src/cmsio1.c | 1020 ++++ third_party/lcms/src/cmslut.c | 1820 +++++++ third_party/lcms/src/cmsmd5.c | 343 ++ third_party/lcms/src/cmsmtrx.c | 175 + third_party/lcms/src/cmsnamed.c | 937 ++++ third_party/lcms/src/cmsopt.c | 1811 +++++++ third_party/lcms/src/cmspack.c | 3369 ++++++++++++ third_party/lcms/src/cmspcs.c | 931 ++++ third_party/lcms/src/cmsplugin.c | 959 ++++ third_party/lcms/src/cmsps2.c | 1597 ++++++ third_party/lcms/src/cmssamp.c | 572 ++ third_party/lcms/src/cmssm.c | 734 +++ third_party/lcms/src/cmstypes.c | 5610 ++++++++++++++++++++ third_party/lcms/src/cmsvirt.c | 1194 +++++ third_party/lcms/src/cmswtpnt.c | 349 ++ third_party/lcms/src/cmsxform.c | 1137 ++++ third_party/lcms/src/lcms2_internal.h | 1032 ++++ .../lcms2-2.6/0000-tag-type-confusion.patch | 14 - .../lcms2-2.6/0001-from16-to-8-overflow.patch | 13 - .../0002-infinite-loop-GrowNamedColorList.patch | 34 - third_party/lcms2-2.6/0003-uninit.patch | 30 - .../0004-memory-leak-Type_Curve_Read.patch | 28 - .../0005-memory-leak-AllocEmptyTransform.patch | 34 - .../0006-memory-leak-Type_NamedColor_Read.patch | 22 - .../0007-memory-leak-OptimizeByResampling.patch | 13 - .../0008-memory-leak-Type_MPEmatrix_Read.patch | 30 - .../0009-cmsStageAllocMatrix-param-swap.patch | 20 - third_party/lcms2-2.6/0010-reject-nan.patch | 13 - .../0011-memory-leak-ReadSegmentedCurve.patch | 36 - third_party/lcms2-2.6/0012-backport-c0a98d86.patch | 53 - third_party/lcms2-2.6/0013-utf8.patch | 99 - third_party/lcms2-2.6/0014-avoid-fixed-inf.patch | 95 - .../lcms2-2.6/0015-sanitize-float-read.patch | 15 - third_party/lcms2-2.6/0016-check-LUT-and-MPE.patch | 170 - ...-upstream-integer-overflow-MPEmatrix_Read.patch | 85 - .../0018-verify-size-before-reading.patch | 17 - .../0019-upstream-direct-leak-Type_MPE_Read.patch | 31 - third_party/lcms2-2.6/README.pdfium | 35 - third_party/lcms2-2.6/include/lcms2.h | 1882 ------- third_party/lcms2-2.6/include/lcms2_plugin.h | 637 --- third_party/lcms2-2.6/src/cmscam02.c | 486 -- third_party/lcms2-2.6/src/cmscgats.c | 2776 ---------- third_party/lcms2-2.6/src/cmscnvrt.c | 1142 ---- third_party/lcms2-2.6/src/cmserr.c | 707 --- third_party/lcms2-2.6/src/cmsgamma.c | 1298 ----- third_party/lcms2-2.6/src/cmsgmt.c | 590 -- third_party/lcms2-2.6/src/cmshalf.c | 534 -- third_party/lcms2-2.6/src/cmsintrp.c | 1506 ------ third_party/lcms2-2.6/src/cmsio0.c | 1895 ------- third_party/lcms2-2.6/src/cmsio1.c | 1020 ---- third_party/lcms2-2.6/src/cmslut.c | 1820 ------- third_party/lcms2-2.6/src/cmsmd5.c | 343 -- third_party/lcms2-2.6/src/cmsmtrx.c | 175 - third_party/lcms2-2.6/src/cmsnamed.c | 937 ---- third_party/lcms2-2.6/src/cmsopt.c | 1811 ------- third_party/lcms2-2.6/src/cmspack.c | 3369 ------------ third_party/lcms2-2.6/src/cmspcs.c | 931 ---- third_party/lcms2-2.6/src/cmsplugin.c | 959 ---- third_party/lcms2-2.6/src/cmsps2.c | 1597 ------ third_party/lcms2-2.6/src/cmssamp.c | 572 -- third_party/lcms2-2.6/src/cmssm.c | 734 --- third_party/lcms2-2.6/src/cmstypes.c | 5610 -------------------- third_party/lcms2-2.6/src/cmsvirt.c | 1194 ----- third_party/lcms2-2.6/src/cmswtpnt.c | 349 -- third_party/lcms2-2.6/src/cmsxform.c | 1137 ---- third_party/lcms2-2.6/src/lcms2_internal.h | 1032 ---- 103 files changed, 37961 insertions(+), 37961 deletions(-) create mode 100644 third_party/lcms/0000-tag-type-confusion.patch create mode 100644 third_party/lcms/0001-from16-to-8-overflow.patch create mode 100644 third_party/lcms/0002-infinite-loop-GrowNamedColorList.patch create mode 100644 third_party/lcms/0003-uninit.patch create mode 100644 third_party/lcms/0004-memory-leak-Type_Curve_Read.patch create mode 100644 third_party/lcms/0005-memory-leak-AllocEmptyTransform.patch create mode 100644 third_party/lcms/0006-memory-leak-Type_NamedColor_Read.patch create mode 100644 third_party/lcms/0007-memory-leak-OptimizeByResampling.patch create mode 100644 third_party/lcms/0008-memory-leak-Type_MPEmatrix_Read.patch create mode 100644 third_party/lcms/0009-cmsStageAllocMatrix-param-swap.patch create mode 100644 third_party/lcms/0010-reject-nan.patch create mode 100644 third_party/lcms/0011-memory-leak-ReadSegmentedCurve.patch create mode 100644 third_party/lcms/0012-backport-c0a98d86.patch create mode 100644 third_party/lcms/0013-utf8.patch create mode 100644 third_party/lcms/0014-avoid-fixed-inf.patch create mode 100644 third_party/lcms/0015-sanitize-float-read.patch create mode 100644 third_party/lcms/0016-check-LUT-and-MPE.patch create mode 100644 third_party/lcms/0017-upstream-integer-overflow-MPEmatrix_Read.patch create mode 100644 third_party/lcms/0018-verify-size-before-reading.patch create mode 100644 third_party/lcms/0019-upstream-direct-leak-Type_MPE_Read.patch create mode 100644 third_party/lcms/README.pdfium create mode 100644 third_party/lcms/include/lcms2.h create mode 100644 third_party/lcms/include/lcms2_plugin.h create mode 100644 third_party/lcms/src/cmscam02.c create mode 100644 third_party/lcms/src/cmscgats.c create mode 100644 third_party/lcms/src/cmscnvrt.c create mode 100644 third_party/lcms/src/cmserr.c create mode 100644 third_party/lcms/src/cmsgamma.c create mode 100644 third_party/lcms/src/cmsgmt.c create mode 100644 third_party/lcms/src/cmshalf.c create mode 100644 third_party/lcms/src/cmsintrp.c create mode 100644 third_party/lcms/src/cmsio0.c create mode 100644 third_party/lcms/src/cmsio1.c create mode 100644 third_party/lcms/src/cmslut.c create mode 100644 third_party/lcms/src/cmsmd5.c create mode 100644 third_party/lcms/src/cmsmtrx.c create mode 100644 third_party/lcms/src/cmsnamed.c create mode 100644 third_party/lcms/src/cmsopt.c create mode 100644 third_party/lcms/src/cmspack.c create mode 100644 third_party/lcms/src/cmspcs.c create mode 100644 third_party/lcms/src/cmsplugin.c create mode 100644 third_party/lcms/src/cmsps2.c create mode 100644 third_party/lcms/src/cmssamp.c create mode 100644 third_party/lcms/src/cmssm.c create mode 100644 third_party/lcms/src/cmstypes.c create mode 100644 third_party/lcms/src/cmsvirt.c create mode 100644 third_party/lcms/src/cmswtpnt.c create mode 100644 third_party/lcms/src/cmsxform.c create mode 100644 third_party/lcms/src/lcms2_internal.h delete mode 100644 third_party/lcms2-2.6/0000-tag-type-confusion.patch delete mode 100644 third_party/lcms2-2.6/0001-from16-to-8-overflow.patch delete mode 100644 third_party/lcms2-2.6/0002-infinite-loop-GrowNamedColorList.patch delete mode 100644 third_party/lcms2-2.6/0003-uninit.patch delete mode 100644 third_party/lcms2-2.6/0004-memory-leak-Type_Curve_Read.patch delete mode 100644 third_party/lcms2-2.6/0005-memory-leak-AllocEmptyTransform.patch delete mode 100644 third_party/lcms2-2.6/0006-memory-leak-Type_NamedColor_Read.patch delete mode 100644 third_party/lcms2-2.6/0007-memory-leak-OptimizeByResampling.patch delete mode 100644 third_party/lcms2-2.6/0008-memory-leak-Type_MPEmatrix_Read.patch delete mode 100644 third_party/lcms2-2.6/0009-cmsStageAllocMatrix-param-swap.patch delete mode 100644 third_party/lcms2-2.6/0010-reject-nan.patch delete mode 100644 third_party/lcms2-2.6/0011-memory-leak-ReadSegmentedCurve.patch delete mode 100644 third_party/lcms2-2.6/0012-backport-c0a98d86.patch delete mode 100644 third_party/lcms2-2.6/0013-utf8.patch delete mode 100644 third_party/lcms2-2.6/0014-avoid-fixed-inf.patch delete mode 100644 third_party/lcms2-2.6/0015-sanitize-float-read.patch delete mode 100644 third_party/lcms2-2.6/0016-check-LUT-and-MPE.patch delete mode 100644 third_party/lcms2-2.6/0017-upstream-integer-overflow-MPEmatrix_Read.patch delete mode 100644 third_party/lcms2-2.6/0018-verify-size-before-reading.patch delete mode 100644 third_party/lcms2-2.6/0019-upstream-direct-leak-Type_MPE_Read.patch delete mode 100644 third_party/lcms2-2.6/README.pdfium delete mode 100644 third_party/lcms2-2.6/include/lcms2.h delete mode 100644 third_party/lcms2-2.6/include/lcms2_plugin.h delete mode 100644 third_party/lcms2-2.6/src/cmscam02.c delete mode 100644 third_party/lcms2-2.6/src/cmscgats.c delete mode 100644 third_party/lcms2-2.6/src/cmscnvrt.c delete mode 100644 third_party/lcms2-2.6/src/cmserr.c delete mode 100644 third_party/lcms2-2.6/src/cmsgamma.c delete mode 100644 third_party/lcms2-2.6/src/cmsgmt.c delete mode 100644 third_party/lcms2-2.6/src/cmshalf.c delete mode 100644 third_party/lcms2-2.6/src/cmsintrp.c delete mode 100644 third_party/lcms2-2.6/src/cmsio0.c delete mode 100644 third_party/lcms2-2.6/src/cmsio1.c delete mode 100644 third_party/lcms2-2.6/src/cmslut.c delete mode 100644 third_party/lcms2-2.6/src/cmsmd5.c delete mode 100644 third_party/lcms2-2.6/src/cmsmtrx.c delete mode 100644 third_party/lcms2-2.6/src/cmsnamed.c delete mode 100644 third_party/lcms2-2.6/src/cmsopt.c delete mode 100644 third_party/lcms2-2.6/src/cmspack.c delete mode 100644 third_party/lcms2-2.6/src/cmspcs.c delete mode 100644 third_party/lcms2-2.6/src/cmsplugin.c delete mode 100644 third_party/lcms2-2.6/src/cmsps2.c delete mode 100644 third_party/lcms2-2.6/src/cmssamp.c delete mode 100644 third_party/lcms2-2.6/src/cmssm.c delete mode 100644 third_party/lcms2-2.6/src/cmstypes.c delete mode 100644 third_party/lcms2-2.6/src/cmsvirt.c delete mode 100644 third_party/lcms2-2.6/src/cmswtpnt.c delete mode 100644 third_party/lcms2-2.6/src/cmsxform.c delete mode 100644 third_party/lcms2-2.6/src/lcms2_internal.h diff --git a/core/fxcodec/DEPS b/core/fxcodec/DEPS index f82f477616..9919478889 100644 --- a/core/fxcodec/DEPS +++ b/core/fxcodec/DEPS @@ -1,5 +1,5 @@ include_rules = [ - '+third_party/lcms2-2.6', + '+third_party/lcms', '+third_party/libjpeg/jpeglib.h', # For non-standalone builds that may use libjpeg_turbo. '+third_party/libjpeg_turbo/jpeglib.h', diff --git a/core/fxcodec/codec/ccodec_iccmodule.h b/core/fxcodec/codec/ccodec_iccmodule.h index e775475249..c304b4bb8e 100644 --- a/core/fxcodec/codec/ccodec_iccmodule.h +++ b/core/fxcodec/codec/ccodec_iccmodule.h @@ -17,7 +17,7 @@ #if defined(USE_SYSTEM_LCMS2) #include #else -#include "third_party/lcms2-2.6/include/lcms2.h" +#include "third_party/lcms/include/lcms2.h" #endif class CLcmsCmm { diff --git a/core/fxcodec/codec/fx_codec_icc.cpp b/core/fxcodec/codec/fx_codec_icc.cpp index 00ddf65115..b6d17d9d96 100644 --- a/core/fxcodec/codec/fx_codec_icc.cpp +++ b/core/fxcodec/codec/fx_codec_icc.cpp @@ -12,7 +12,7 @@ #if defined(USE_SYSTEM_LCMS2) #include #else -#include "third_party/lcms2-2.6/include/lcms2.h" +#include "third_party/lcms/include/lcms2.h" #endif namespace { diff --git a/core/fxcodec/codec/fx_codec_jpx_opj.cpp b/core/fxcodec/codec/fx_codec_jpx_opj.cpp index 27c2dd440b..00e93fb3d5 100644 --- a/core/fxcodec/codec/fx_codec_jpx_opj.cpp +++ b/core/fxcodec/codec/fx_codec_jpx_opj.cpp @@ -23,7 +23,7 @@ #if defined(USE_SYSTEM_LCMS2) #include #else -#include "third_party/lcms2-2.6/include/lcms2.h" +#include "third_party/lcms/include/lcms2.h" #endif namespace { diff --git a/third_party/BUILD.gn b/third_party/BUILD.gn index 6d093f2249..743bbb0064 100644 --- a/third_party/BUILD.gn +++ b/third_party/BUILD.gn @@ -253,33 +253,33 @@ static_library("fx_lcms2") { ":fx_lcms2_warnings", ] sources = [ - "lcms2-2.6/include/lcms2.h", - "lcms2-2.6/include/lcms2_plugin.h", - "lcms2-2.6/src/cmscam02.c", - "lcms2-2.6/src/cmscgats.c", - "lcms2-2.6/src/cmscnvrt.c", - "lcms2-2.6/src/cmserr.c", - "lcms2-2.6/src/cmsgamma.c", - "lcms2-2.6/src/cmsgmt.c", - "lcms2-2.6/src/cmshalf.c", - "lcms2-2.6/src/cmsintrp.c", - "lcms2-2.6/src/cmsio0.c", - "lcms2-2.6/src/cmsio1.c", - "lcms2-2.6/src/cmslut.c", - "lcms2-2.6/src/cmsmd5.c", - "lcms2-2.6/src/cmsmtrx.c", - "lcms2-2.6/src/cmsnamed.c", - "lcms2-2.6/src/cmsopt.c", - "lcms2-2.6/src/cmspack.c", - "lcms2-2.6/src/cmspcs.c", - "lcms2-2.6/src/cmsplugin.c", - "lcms2-2.6/src/cmsps2.c", - "lcms2-2.6/src/cmssamp.c", - "lcms2-2.6/src/cmssm.c", - "lcms2-2.6/src/cmstypes.c", - "lcms2-2.6/src/cmsvirt.c", - "lcms2-2.6/src/cmswtpnt.c", - "lcms2-2.6/src/cmsxform.c", + "lcms/include/lcms2.h", + "lcms/include/lcms2_plugin.h", + "lcms/src/cmscam02.c", + "lcms/src/cmscgats.c", + "lcms/src/cmscnvrt.c", + "lcms/src/cmserr.c", + "lcms/src/cmsgamma.c", + "lcms/src/cmsgmt.c", + "lcms/src/cmshalf.c", + "lcms/src/cmsintrp.c", + "lcms/src/cmsio0.c", + "lcms/src/cmsio1.c", + "lcms/src/cmslut.c", + "lcms/src/cmsmd5.c", + "lcms/src/cmsmtrx.c", + "lcms/src/cmsnamed.c", + "lcms/src/cmsopt.c", + "lcms/src/cmspack.c", + "lcms/src/cmspcs.c", + "lcms/src/cmsplugin.c", + "lcms/src/cmsps2.c", + "lcms/src/cmssamp.c", + "lcms/src/cmssm.c", + "lcms/src/cmstypes.c", + "lcms/src/cmsvirt.c", + "lcms/src/cmswtpnt.c", + "lcms/src/cmsxform.c", ] } diff --git a/third_party/lcms/0000-tag-type-confusion.patch b/third_party/lcms/0000-tag-type-confusion.patch new file mode 100644 index 0000000000..97d3c7f4d9 --- /dev/null +++ b/third_party/lcms/0000-tag-type-confusion.patch @@ -0,0 +1,14 @@ +diff --git a/third_party/lcms/src/cmsio0.c b/third_party/lcms/src/cmsio0.c +index 6549d15..5f9f08a 100644 +--- a/third_party/lcms/src/cmsio0.c ++++ b/third_party/lcms/src/cmsio0.c +@@ -719,7 +719,8 @@ cmsBool _cmsReadHeader(_cmsICCPROFILE* Icc) + for (j=0; j < Icc ->TagCount; j++) { + + if ((Icc ->TagOffsets[j] == Tag.offset) && +- (Icc ->TagSizes[j] == Tag.size)) { ++ (Icc ->TagSizes[j] == Tag.size) && ++ (Icc ->TagNames[j] == Tag.sig)) { + + Icc ->TagLinked[Icc ->TagCount] = Icc ->TagNames[j]; + } diff --git a/third_party/lcms/0001-from16-to-8-overflow.patch b/third_party/lcms/0001-from16-to-8-overflow.patch new file mode 100644 index 0000000000..9d31c84b36 --- /dev/null +++ b/third_party/lcms/0001-from16-to-8-overflow.patch @@ -0,0 +1,13 @@ +diff --git a/third_party/lcms/src/lcms2_internal.h b/third_party/lcms/src/lcms2_internal.h +index 8617e92..cc76d48 100644 +--- a/third_party/lcms/src/lcms2_internal.h ++++ b/third_party/lcms/src/lcms2_internal.h +@@ -94,7 +94,7 @@ + + // A fast way to convert from/to 16 <-> 8 bits + #define FROM_8_TO_16(rgb) (cmsUInt16Number) ((((cmsUInt16Number) (rgb)) << 8)|(rgb)) +-#define FROM_16_TO_8(rgb) (cmsUInt8Number) ((((rgb) * 65281 + 8388608) >> 24) & 0xFF) ++#define FROM_16_TO_8(rgb) (cmsUInt8Number) ((((cmsUInt32Number)(rgb) * 65281U + 8388608U) >> 24) & 0xFFU) + + // Code analysis is broken on asserts + #ifdef _MSC_VER diff --git a/third_party/lcms/0002-infinite-loop-GrowNamedColorList.patch b/third_party/lcms/0002-infinite-loop-GrowNamedColorList.patch new file mode 100644 index 0000000000..2a6f3df6c7 --- /dev/null +++ b/third_party/lcms/0002-infinite-loop-GrowNamedColorList.patch @@ -0,0 +1,34 @@ +diff --git a/third_party/lcms/src/cmsnamed.c b/third_party/lcms/src/cmsnamed.c +index acfd1c8..ef1eb30 100644 +--- a/third_party/lcms/src/cmsnamed.c ++++ b/third_party/lcms/src/cmsnamed.c +@@ -514,8 +514,12 @@ cmsNAMEDCOLORLIST* CMSEXPORT cmsAllocNamedColorList(cmsContext ContextID, cmsUIn + v ->nColors = 0; + v ->ContextID = ContextID; + +- while (v -> Allocated < n) +- GrowNamedColorList(v); ++ while (v -> Allocated < n) { ++ if (!GrowNamedColorList(v)) { ++ cmsFreeNamedColorList(v); ++ return NULL; ++ } ++ } + + strncpy(v ->Prefix, Prefix, sizeof(v ->Prefix)-1); + strncpy(v ->Suffix, Suffix, sizeof(v ->Suffix)-1); +@@ -544,8 +548,12 @@ cmsNAMEDCOLORLIST* CMSEXPORT cmsDupNamedColorList(const cmsNAMEDCOLORLIST* v) + if (NewNC == NULL) return NULL; + + // For really large tables we need this +- while (NewNC ->Allocated < v ->Allocated) +- GrowNamedColorList(NewNC); ++ while (NewNC ->Allocated < v ->Allocated) { ++ if (!GrowNamedColorList(NewNC)) { ++ cmsFreeNamedColorList(NewNC); ++ return NULL; ++ } ++ } + + memmove(NewNC ->Prefix, v ->Prefix, sizeof(v ->Prefix)); + memmove(NewNC ->Suffix, v ->Suffix, sizeof(v ->Suffix)); diff --git a/third_party/lcms/0003-uninit.patch b/third_party/lcms/0003-uninit.patch new file mode 100644 index 0000000000..4017c0150b --- /dev/null +++ b/third_party/lcms/0003-uninit.patch @@ -0,0 +1,30 @@ +diff --git a/third_party/lcms/src/cmstypes.c b/third_party/lcms/src/cmstypes.c +index 06742b5..44c5b87 100644 +--- a/third_party/lcms/src/cmstypes.c ++++ b/third_party/lcms/src/cmstypes.c +@@ -2964,7 +2964,7 @@ void *Type_ColorantTable_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER + { + cmsUInt32Number i, Count; + cmsNAMEDCOLORLIST* List; +- char Name[34]; ++ char Name[33]; + cmsUInt16Number PCS[3]; + + +@@ -2979,7 +2979,7 @@ void *Type_ColorantTable_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER + for (i=0; i < Count; i++) { + + if (io ->Read(io, Name, 32, 1) != 1) goto Error; +- Name[33] = 0; ++ Name[32] = 0; + + if (!_cmsReadUInt16Array(io, 3, PCS)) goto Error; + +@@ -3106,6 +3106,7 @@ void *Type_NamedColor_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* i + + memset(Colorant, 0, sizeof(Colorant)); + if (io -> Read(io, Root, 32, 1) != 1) return NULL; ++ Root[32] = 0; + if (!_cmsReadUInt16Array(io, 3, PCS)) goto Error; + if (!_cmsReadUInt16Array(io, nDeviceCoords, Colorant)) goto Error; + diff --git a/third_party/lcms/0004-memory-leak-Type_Curve_Read.patch b/third_party/lcms/0004-memory-leak-Type_Curve_Read.patch new file mode 100644 index 0000000000..7edc7f9e0f --- /dev/null +++ b/third_party/lcms/0004-memory-leak-Type_Curve_Read.patch @@ -0,0 +1,28 @@ +diff --git a/third_party/lcms/src/cmstypes.c b/third_party/lcms/src/cmstypes.c +index 06742b5..9fe5e2a 100644 +--- a/third_party/lcms/src/cmstypes.c ++++ b/third_party/lcms/src/cmstypes.c +@@ -1112,7 +1112,10 @@ void *Type_Curve_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cm + NewGamma = cmsBuildTabulatedToneCurve16(self ->ContextID, Count, NULL); + if (!NewGamma) return NULL; + +- if (!_cmsReadUInt16Array(io, Count, NewGamma -> Table16)) return NULL; ++ if (!_cmsReadUInt16Array(io, Count, NewGamma -> Table16)) { ++ cmsFreeToneCurve(NewGamma); ++ return NULL; ++ } + + *nItems = 1; + return NewGamma; +@@ -2350,7 +2353,10 @@ cmsStage* ReadCLUT(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUI + + for (i=0; i < Data ->nEntries; i++) { + +- if (io ->Read(io, &v, sizeof(cmsUInt8Number), 1) != 1) return NULL; ++ if (io ->Read(io, &v, sizeof(cmsUInt8Number), 1) != 1) { ++ cmsStageFree(CLUT); ++ return NULL; ++ } + Data ->Tab.T[i] = FROM_8_TO_16(v); + } + diff --git a/third_party/lcms/0005-memory-leak-AllocEmptyTransform.patch b/third_party/lcms/0005-memory-leak-AllocEmptyTransform.patch new file mode 100644 index 0000000000..481d421292 --- /dev/null +++ b/third_party/lcms/0005-memory-leak-AllocEmptyTransform.patch @@ -0,0 +1,34 @@ +diff --git a/third_party/lcms/src/cmsxform.c b/third_party/lcms/src/cmsxform.c +index eddb9bd..6466d27 100644 +--- a/third_party/lcms/src/cmsxform.c ++++ b/third_party/lcms/src/cmsxform.c +@@ -593,7 +593,10 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut, + + // Allocate needed memory + _cmsTRANSFORM* p = (_cmsTRANSFORM*) _cmsMallocZero(ContextID, sizeof(_cmsTRANSFORM)); +- if (!p) return NULL; ++ if (!p) { ++ cmsPipelineFree(lut); ++ return NULL; ++ } + + // Store the proposed pipeline + p ->Lut = lut; +@@ -643,7 +646,7 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut, + if (p ->FromInputFloat == NULL || p ->ToOutputFloat == NULL) { + + cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format"); +- _cmsFree(ContextID, p); ++ cmsDeleteTransform(p); + return NULL; + } + +@@ -673,7 +676,7 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut, + if (p ->FromInput == NULL || p ->ToOutput == NULL) { + + cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format"); +- _cmsFree(ContextID, p); ++ cmsDeleteTransform(p); + return NULL; + } + diff --git a/third_party/lcms/0006-memory-leak-Type_NamedColor_Read.patch b/third_party/lcms/0006-memory-leak-Type_NamedColor_Read.patch new file mode 100644 index 0000000000..f6fa010357 --- /dev/null +++ b/third_party/lcms/0006-memory-leak-Type_NamedColor_Read.patch @@ -0,0 +1,22 @@ +diff --git a/third_party/lcms/src/cmstypes.c b/third_party/lcms/src/cmstypes.c +index feba387..4d24fc2 100644 +--- a/third_party/lcms/src/cmstypes.c ++++ b/third_party/lcms/src/cmstypes.c +@@ -3102,7 +3102,7 @@ void *Type_NamedColor_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* i + + if (nDeviceCoords > cmsMAXCHANNELS) { + cmsSignalError(self->ContextID, cmsERROR_RANGE, "Too many device coordinates '%d'", nDeviceCoords); +- return 0; ++ goto Error; + } + for (i=0; i < count; i++) { + +@@ -3111,7 +3111,7 @@ void *Type_NamedColor_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* i + char Root[33]; + + memset(Colorant, 0, sizeof(Colorant)); +- if (io -> Read(io, Root, 32, 1) != 1) return NULL; ++ if (io -> Read(io, Root, 32, 1) != 1) goto Error; + Root[32] = 0; + if (!_cmsReadUInt16Array(io, 3, PCS)) goto Error; + if (!_cmsReadUInt16Array(io, nDeviceCoords, Colorant)) goto Error; diff --git a/third_party/lcms/0007-memory-leak-OptimizeByResampling.patch b/third_party/lcms/0007-memory-leak-OptimizeByResampling.patch new file mode 100644 index 0000000000..48645d1f01 --- /dev/null +++ b/third_party/lcms/0007-memory-leak-OptimizeByResampling.patch @@ -0,0 +1,13 @@ +diff --git a/third_party/lcms/src/cmsopt.c b/third_party/lcms/src/cmsopt.c +index f885ef3..684910d 100644 +--- a/third_party/lcms/src/cmsopt.c ++++ b/third_party/lcms/src/cmsopt.c +@@ -612,7 +612,7 @@ cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3 + + // Allocate the CLUT + CLUT = cmsStageAllocCLut16bit(Src ->ContextID, nGridPoints, Src ->InputChannels, Src->OutputChannels, NULL); +- if (CLUT == NULL) return FALSE; ++ if (CLUT == NULL) goto Error; + + // Add the CLUT to the destination LUT + if (!cmsPipelineInsertStage(Dest, cmsAT_END, CLUT)) { diff --git a/third_party/lcms/0008-memory-leak-Type_MPEmatrix_Read.patch b/third_party/lcms/0008-memory-leak-Type_MPEmatrix_Read.patch new file mode 100644 index 0000000000..8cc477fb8e --- /dev/null +++ b/third_party/lcms/0008-memory-leak-Type_MPEmatrix_Read.patch @@ -0,0 +1,30 @@ +diff --git a/third_party/lcms/src/cmstypes.c b/third_party/lcms/src/cmstypes.c +index 441d6bb..15199c7 100644 +--- a/third_party/lcms/src/cmstypes.c ++++ b/third_party/lcms/src/cmstypes.c +@@ -4203,7 +4203,11 @@ void *Type_MPEmatrix_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io + + cmsFloat32Number v; + +- if (!_cmsReadFloat32Number(io, &v)) return NULL; ++ if (!_cmsReadFloat32Number(io, &v)) { ++ _cmsFree(self ->ContextID, Matrix); ++ _cmsFree(self ->ContextID, Offsets); ++ return NULL; ++ } + Matrix[i] = v; + } + +@@ -4212,7 +4216,11 @@ void *Type_MPEmatrix_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io + + cmsFloat32Number v; + +- if (!_cmsReadFloat32Number(io, &v)) return NULL; ++ if (!_cmsReadFloat32Number(io, &v)) { ++ _cmsFree(self ->ContextID, Matrix); ++ _cmsFree(self ->ContextID, Offsets); ++ return NULL; ++ } + Offsets[i] = v; + } + diff --git a/third_party/lcms/0009-cmsStageAllocMatrix-param-swap.patch b/third_party/lcms/0009-cmsStageAllocMatrix-param-swap.patch new file mode 100644 index 0000000000..04f854398c --- /dev/null +++ b/third_party/lcms/0009-cmsStageAllocMatrix-param-swap.patch @@ -0,0 +1,20 @@ +diff --git a/third_party/lcms/src/cmslut.c b/third_party/lcms/src/cmslut.c +index 73e6726..9b0eb4b 100644 +--- a/third_party/lcms/src/cmslut.c ++++ b/third_party/lcms/src/cmslut.c +@@ -414,13 +414,13 @@ cmsStage* CMSEXPORT cmsStageAllocMatrix(cmsContext ContextID, cmsUInt32Number R + + if (Offset != NULL) { + +- NewElem ->Offset = (cmsFloat64Number*) _cmsCalloc(ContextID, Cols, sizeof(cmsFloat64Number)); ++ NewElem ->Offset = (cmsFloat64Number*) _cmsCalloc(ContextID, Rows, sizeof(cmsFloat64Number)); + if (NewElem->Offset == NULL) { + MatrixElemTypeFree(NewMPE); + return NULL; + } + +- for (i=0; i < Cols; i++) { ++ for (i=0; i < Rows; i++) { + NewElem ->Offset[i] = Offset[i]; + } + diff --git a/third_party/lcms/0010-reject-nan.patch b/third_party/lcms/0010-reject-nan.patch new file mode 100644 index 0000000000..8aa325a749 --- /dev/null +++ b/third_party/lcms/0010-reject-nan.patch @@ -0,0 +1,13 @@ +diff --git a/third_party/lcms/src/cmsplugin.c b/third_party/lcms/src/cmsplugin.c +index 8903d2b..b95befb 100644 +--- a/third_party/lcms/src/cmsplugin.c ++++ b/third_party/lcms/src/cmsplugin.c +@@ -179,6 +179,8 @@ cmsBool CMSEXPORT _cmsReadFloat32Number(cmsIOHANDLER* io, cmsFloat32Number* n) + + tmp = _cmsAdjustEndianess32(tmp); + *n = *(cmsFloat32Number*) &tmp; ++ if (isnan(*n)) ++ return FALSE; + } + return TRUE; + } diff --git a/third_party/lcms/0011-memory-leak-ReadSegmentedCurve.patch b/third_party/lcms/0011-memory-leak-ReadSegmentedCurve.patch new file mode 100644 index 0000000000..472bcadf05 --- /dev/null +++ b/third_party/lcms/0011-memory-leak-ReadSegmentedCurve.patch @@ -0,0 +1,36 @@ +diff --git a/third_party/lcms/src/cmstypes.c b/third_party/lcms/src/cmstypes.c +index 15199c7..04dd0c4 100644 +--- a/third_party/lcms/src/cmstypes.c ++++ b/third_party/lcms/src/cmstypes.c +@@ -3968,7 +3968,7 @@ cmsToneCurve* ReadSegmentedCurve(struct _cms_typehandler_struct* self, cmsIOHAND + case cmsSigSampledCurveSeg: { + cmsUInt32Number Count; + +- if (!_cmsReadUInt32Number(io, &Count)) return NULL; ++ if (!_cmsReadUInt32Number(io, &Count)) goto Error; + + Segments[i].nGridPoints = Count; + Segments[i].SampledPoints = (cmsFloat32Number*) _cmsCalloc(self ->ContextID, Count, sizeof(cmsFloat32Number)); +@@ -3987,7 +3987,7 @@ cmsToneCurve* ReadSegmentedCurve(struct _cms_typehandler_struct* self, cmsIOHAND + _cmsTagSignature2String(String, (cmsTagSignature) ElementSig); + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown curve element type '%s' found.", String); + } +- return NULL; ++ goto Error; + + } + } +@@ -4001,7 +4001,12 @@ cmsToneCurve* ReadSegmentedCurve(struct _cms_typehandler_struct* self, cmsIOHAND + return Curve; + + Error: +- if (Segments) _cmsFree(self ->ContextID, Segments); ++ if (Segments) { ++ for (i=0; i < nSegments; i++) { ++ if (Segments[i].SampledPoints) _cmsFree(self ->ContextID, Segments[i].SampledPoints); ++ } ++ _cmsFree(self ->ContextID, Segments); ++ } + return NULL; + } + diff --git a/third_party/lcms/0012-backport-c0a98d86.patch b/third_party/lcms/0012-backport-c0a98d86.patch new file mode 100644 index 0000000000..3835b0d6cc --- /dev/null +++ b/third_party/lcms/0012-backport-c0a98d86.patch @@ -0,0 +1,53 @@ +diff --git a/third_party/lcms/src/cmsintrp.c b/third_party/lcms/src/cmsintrp.c +index 5d5f35d..14c6856 100644 +--- a/third_party/lcms/src/cmsintrp.c ++++ b/third_party/lcms/src/cmsintrp.c +@@ -215,7 +215,7 @@ void LinLerp1D(register const cmsUInt16Number Value[], + // To prevent out of bounds indexing + cmsINLINE cmsFloat32Number fclamp(cmsFloat32Number v) + { +- return v < 0.0f ? 0.0f : (v > 1.0f ? 1.0f : v); ++ return ((v < 0.0f) || isnan(v)) ? 0.0f : (v > 1.0f ? 1.0f : v); + } + + // Floating-point version of 1D interpolation +diff --git a/third_party/lcms/src/cmsio0.c b/third_party/lcms/src/cmsio0.c +index 5f9f08a..3ed730a 100644 +--- a/third_party/lcms/src/cmsio0.c ++++ b/third_party/lcms/src/cmsio0.c +@@ -1475,6 +1475,17 @@ void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig) + // If the element is already in memory, return the pointer + if (Icc -> TagPtrs[n]) { + ++ if (Icc->TagTypeHandlers[n] == NULL) goto Error; ++ ++ // Sanity check ++ BaseType = Icc->TagTypeHandlers[n]->Signature; ++ if (BaseType == 0) goto Error; ++ ++ TagDescriptor = _cmsGetTagDescriptor(Icc->ContextID, sig); ++ if (TagDescriptor == NULL) goto Error; ++ ++ if (!IsTypeSupported(TagDescriptor, BaseType)) goto Error; ++ + if (Icc ->TagSaveAsRaw[n]) goto Error; // We don't support read raw tags as cooked + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); +diff --git a/third_party/lcms/src/cmstypes.c b/third_party/lcms/src/cmstypes.c +index 04dd0c4..386439b 100644 +--- a/third_party/lcms/src/cmstypes.c ++++ b/third_party/lcms/src/cmstypes.c +@@ -4297,8 +4297,12 @@ void *Type_MPEclut_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, + + // Copy MAX_INPUT_DIMENSIONS at most. Expand to cmsUInt32Number + nMaxGrids = InputChans > MAX_INPUT_DIMENSIONS ? MAX_INPUT_DIMENSIONS : InputChans; +- for (i=0; i < nMaxGrids; i++) GridPoints[i] = (cmsUInt32Number) Dimensions8[i]; + ++ for (i = 0; i < nMaxGrids; i++) { ++ if (Dimensions8[i] == 1) goto Error; // Impossible value, 0 for no CLUT and then 2 at least ++ GridPoints[i] = (cmsUInt32Number)Dimensions8[i]; ++ } ++ + // Allocate the true CLUT + mpe = cmsStageAllocCLutFloatGranular(self ->ContextID, GridPoints, InputChans, OutputChans, NULL); + if (mpe == NULL) goto Error; diff --git a/third_party/lcms/0013-utf8.patch b/third_party/lcms/0013-utf8.patch new file mode 100644 index 0000000000..c143aa8793 --- /dev/null +++ b/third_party/lcms/0013-utf8.patch @@ -0,0 +1,99 @@ +diff --git a/third_party/lcms/src/cmscgats.c b/third_party/lcms/src/cmscgats.c +index 9017435..5720c66 100644 +--- a/third_party/lcms/src/cmscgats.c ++++ b/third_party/lcms/src/cmscgats.c +@@ -258,7 +258,7 @@ static PROPERTY PredefinedProperties[] = { + // needed. + + {"SAMPLE_BACKING", WRITE_STRINGIFY}, // Identifies the backing material used behind the sample during +- // measurement. Allowed values are “black? “white? or {"na". ++ // measurement. Allowed values are "black" "white" or "na". + + {"CHISQ_DOF", WRITE_STRINGIFY}, // Degrees of freedom associated with the Chi squared statistic + +@@ -274,7 +274,7 @@ static PROPERTY PredefinedProperties[] = { + // denote the use of filters such as none, D65, Red, Green or Blue. + + {"POLARIZATION", WRITE_STRINGIFY}, // Identifies the use of a physical polarization filter during measurement. Allowed +- // values are {"yes? “white? “none?or “na? ++ // values are "yes" "white" "none" or "na" + + {"WEIGHTING_FUNCTION", WRITE_PAIR}, // Indicates such functions as: the CIE standard observer functions used in the + // calculation of various data parameters (2 degree and 10 degree), CIE standard +diff --git a/third_party/lcms/src/cmstypes.c b/third_party/lcms/src/cmstypes.c +index 386439b..e5ed06c 100644 +--- a/third_party/lcms/src/cmstypes.c ++++ b/third_party/lcms/src/cmstypes.c +@@ -962,7 +962,7 @@ cmsBool Type_Text_Description_Write(struct _cms_typehandler_struct* self, cmsIO + len = cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, NULL, 0); + + // From ICC3.4: It has been found that textDescriptionType can contain misaligned data +- //(see clause 4.1 for the definition of “aligned?. Because the Unicode language ++ //(see clause 4.1 for the definition of 'aligned'. Because the Unicode language + // code and Unicode count immediately follow the ASCII description, their + // alignment is not correct if the ASCII count is not a multiple of four. The + // ScriptCode code is misaligned when the ASCII count is odd. Profile reading and +@@ -3064,9 +3064,10 @@ void Type_ColorantTable_Free(struct _cms_typehandler_struct* self, void* Ptr) + //The namedColor2Type is a count value and array of structures that provide color + //coordinates for 7-bit ASCII color names. For each named color, a PCS and optional + //device representation of the color are given. Both representations are 16-bit values. +-//The device representation corresponds to the header’s “color space of data?field. +-//This representation should be consistent with the “number of device components?//field in the namedColor2Type. If this field is 0, device coordinates are not provided. +-//The PCS representation corresponds to the header’s PCS field. The PCS representation ++//The device representation corresponds to the header's 'color space of data' field. ++//This representation should be consistent with the 'number of device components' ++//field in the namedColor2Type. If this field is 0, device coordinates are not provided. ++//The PCS representation corresponds to the header's PCS field. The PCS representation + //is always provided. Color names are fixed-length, 32-byte fields including null + //termination. In order to maintain maximum portability, it is strongly recommended + //that special characters of the 7-bit ASCII set not be used. +@@ -3809,7 +3810,8 @@ void Type_Screening_Free(struct _cms_typehandler_struct* self, void* Ptr) + // ******************************************************************************** + // + //This type represents a set of viewing condition parameters including: +-//CIE ’absolute?illuminant white point tristimulus values and CIE ’absolute?//surround tristimulus values. ++//CIE 'absolute'illuminant white point tristimulus values and CIE 'absolute' ++//surround tristimulus values. + + static + void *Type_ViewingConditions_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +@@ -3895,7 +3897,7 @@ void GenericMPEfree(struct _cms_typehandler_struct* self, void *Ptr) + } + + // Each curve is stored in one or more curve segments, with break-points specified between curve segments. +-// The first curve segment always starts at –Infinity, and the last curve segment always ends at +Infinity. The ++// The first curve segment always starts at -Infinity, and the last curve segment always ends at +Infinity. The + // first and last curve segments shall be specified in terms of a formula, whereas the other segments shall be + // specified either in terms of a formula, or by a sampled curve. + +diff --git a/third_party/lcms/src/cmsvirt.c b/third_party/lcms/src/cmsvirt.c +index b324c99..d19ace1 100644 +--- a/third_party/lcms/src/cmsvirt.c ++++ b/third_party/lcms/src/cmsvirt.c +@@ -612,18 +612,18 @@ cmsHPROFILE CMSEXPORT cmsCreateXYZProfile(void) + + //sRGB Curves are defined by: + // +-//If R’sRGB,G’sRGB, B’sRGB < 0.04045 ++//If R'sRGB,G'sRGB, B'sRGB < 0.04045 + // +-// R = R’sRGB / 12.92 +-// G = G’sRGB / 12.92 +-// B = B’sRGB / 12.92 ++// R = R'sRGB / 12.92 ++// G = G'sRGB / 12.92 ++// B = B'sRGB / 12.92 + // + // +-//else if R’sRGB,G’sRGB, B’sRGB >= 0.04045 ++//else if R'sRGB,G'sRGB, B'sRGB >= 0.04045 + // +-// R = ((R’sRGB + 0.055) / 1.055)^2.4 +-// G = ((G’sRGB + 0.055) / 1.055)^2.4 +-// B = ((B’sRGB + 0.055) / 1.055)^2.4 ++// R = ((R'sRGB + 0.055) / 1.055)^2.4 ++// G = ((G'sRGB + 0.055) / 1.055)^2.4 ++// B = ((B'sRGB + 0.055) / 1.055)^2.4 + + static + cmsToneCurve* Build_sRGBGamma(cmsContext ContextID) diff --git a/third_party/lcms/0014-avoid-fixed-inf.patch b/third_party/lcms/0014-avoid-fixed-inf.patch new file mode 100644 index 0000000000..b7960e6885 --- /dev/null +++ b/third_party/lcms/0014-avoid-fixed-inf.patch @@ -0,0 +1,95 @@ +diff --git a/third_party/lcms/src/cmsopt.c b/third_party/lcms/src/cmsopt.c +index 684910d..76de015 100644 +--- a/third_party/lcms/src/cmsopt.c ++++ b/third_party/lcms/src/cmsopt.c +@@ -1443,7 +1443,7 @@ void MatShaperEval16(register const cmsUInt16Number In[], + + // This table converts from 8 bits to 1.14 after applying the curve + static +-void FillFirstShaper(cmsS1Fixed14Number* Table, cmsToneCurve* Curve) ++cmsBool FillFirstShaper(cmsS1Fixed14Number* Table, cmsToneCurve* Curve) + { + int i; + cmsFloat32Number R, y; +@@ -1452,14 +1452,17 @@ void FillFirstShaper(cmsS1Fixed14Number* Table, cmsToneCurve* Curve) + + R = (cmsFloat32Number) (i / 255.0); + y = cmsEvalToneCurveFloat(Curve, R); ++ if (isinf(y)) ++ return FALSE; + + Table[i] = DOUBLE_TO_1FIXED14(y); + } ++ return TRUE; + } + + // This table converts form 1.14 (being 0x4000 the last entry) to 8 bits after applying the curve + static +-void FillSecondShaper(cmsUInt16Number* Table, cmsToneCurve* Curve, cmsBool Is8BitsOutput) ++cmsBool FillSecondShaper(cmsUInt16Number* Table, cmsToneCurve* Curve, cmsBool Is8BitsOutput) + { + int i; + cmsFloat32Number R, Val; +@@ -1468,6 +1471,8 @@ void FillSecondShaper(cmsUInt16Number* Table, cmsToneCurve* Curve, cmsBool Is8Bi + + R = (cmsFloat32Number) (i / 16384.0); + Val = cmsEvalToneCurveFloat(Curve, R); // Val comes 0..1.0 ++ if (isinf(Val)) ++ return FALSE; + + if (Is8BitsOutput) { + +@@ -1482,6 +1487,7 @@ void FillSecondShaper(cmsUInt16Number* Table, cmsToneCurve* Curve, cmsBool Is8Bi + } + else Table[i] = _cmsQuickSaturateWord(Val * 65535.0); + } ++ return TRUE; + } + + // Compute the matrix-shaper structure +@@ -1499,13 +1505,19 @@ cmsBool SetMatShaper(cmsPipeline* Dest, cmsToneCurve* Curve1[3], cmsMAT3* Mat, c + p -> ContextID = Dest -> ContextID; + + // Precompute tables +- FillFirstShaper(p ->Shaper1R, Curve1[0]); +- FillFirstShaper(p ->Shaper1G, Curve1[1]); +- FillFirstShaper(p ->Shaper1B, Curve1[2]); ++ if (!FillFirstShaper(p ->Shaper1R, Curve1[0])) ++ goto Error; ++ if (!FillFirstShaper(p ->Shaper1G, Curve1[1])) ++ goto Error; ++ if (!FillFirstShaper(p ->Shaper1B, Curve1[2])) ++ goto Error; + +- FillSecondShaper(p ->Shaper2R, Curve2[0], Is8Bits); +- FillSecondShaper(p ->Shaper2G, Curve2[1], Is8Bits); +- FillSecondShaper(p ->Shaper2B, Curve2[2], Is8Bits); ++ if (!FillSecondShaper(p ->Shaper2R, Curve2[0], Is8Bits)) ++ goto Error; ++ if (!FillSecondShaper(p ->Shaper2G, Curve2[1], Is8Bits)) ++ goto Error; ++ if (!FillSecondShaper(p ->Shaper2B, Curve2[2], Is8Bits)) ++ goto Error; + + // Convert matrix to nFixed14. Note that those values may take more than 16 bits as + for (i=0; i < 3; i++) { +@@ -1531,6 +1543,9 @@ cmsBool SetMatShaper(cmsPipeline* Dest, cmsToneCurve* Curve1[3], cmsMAT3* Mat, c + // Fill function pointers + _cmsPipelineSetOptimizationParameters(Dest, MatShaperEval16, (void*) p, FreeMatShaper, DupMatShaper); + return TRUE; ++Error: ++ _cmsFree(Dest->ContextID, p); ++ return FALSE; + } + + // 8 bits on input allows matrix-shaper boot up to 25 Mpixels per second on RGB. That's fast! +@@ -1606,7 +1621,8 @@ cmsBool OptimizeMatrixShaper(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3 + *dwFlags |= cmsFLAGS_NOCACHE; + + // Setup the optimizarion routines +- SetMatShaper(Dest, mpeC1 ->TheCurves, &res, (cmsVEC3*) Data2 ->Offset, mpeC2->TheCurves, OutputFormat); ++ if (!SetMatShaper(Dest, mpeC1 ->TheCurves, &res, (cmsVEC3*) Data2 ->Offset, mpeC2->TheCurves, OutputFormat)) ++ goto Error; + } + + cmsPipelineFree(Src); diff --git a/third_party/lcms/0015-sanitize-float-read.patch b/third_party/lcms/0015-sanitize-float-read.patch new file mode 100644 index 0000000000..568fd0e112 --- /dev/null +++ b/third_party/lcms/0015-sanitize-float-read.patch @@ -0,0 +1,15 @@ +diff --git a/third_party/lcms/src/cmsplugin.c b/third_party/lcms/src/cmsplugin.c +index b95befb..4ba998b 100644 +--- a/third_party/lcms/src/cmsplugin.c ++++ b/third_party/lcms/src/cmsplugin.c +@@ -182,7 +182,9 @@ cmsBool CMSEXPORT _cmsReadFloat32Number(cmsIOHANDLER* io, cmsFloat32Number* n) + if (isnan(*n)) + return FALSE; + } +- return TRUE; ++ ++ // fpclassify() required by C99 ++ return (fpclassify(*n) == FP_ZERO) || (fpclassify(*n) == FP_NORMAL); + } + + diff --git a/third_party/lcms/0016-check-LUT-and-MPE.patch b/third_party/lcms/0016-check-LUT-and-MPE.patch new file mode 100644 index 0000000000..e2efe5a3a7 --- /dev/null +++ b/third_party/lcms/0016-check-LUT-and-MPE.patch @@ -0,0 +1,170 @@ +diff --git a/third_party/lcms/src/cmslut.c b/third_party/lcms/src/cmslut.c +index 9b0eb4b54..19d43361f 100644 +--- a/third_party/lcms/src/cmslut.c ++++ b/third_party/lcms/src/cmslut.c +@@ -1255,21 +1255,39 @@ cmsStage* CMSEXPORT cmsStageDup(cmsStage* mpe) + // *********************************************************************************************************** + + // This function sets up the channel count +- + static +-void BlessLUT(cmsPipeline* lut) ++cmsBool BlessLUT(cmsPipeline* lut) + { + // We can set the input/ouput channels only if we have elements. + if (lut ->Elements != NULL) { + +- cmsStage *First, *Last; ++ cmsStage* prev; ++ cmsStage* next; ++ cmsStage* First; ++ cmsStage* Last; + + First = cmsPipelineGetPtrToFirstStage(lut); + Last = cmsPipelineGetPtrToLastStage(lut); + +- if (First != NULL)lut ->InputChannels = First ->InputChannels; +- if (Last != NULL) lut ->OutputChannels = Last ->OutputChannels; ++ if (First == NULL || Last == NULL) return FALSE; ++ ++ lut->InputChannels = First->InputChannels; ++ lut->OutputChannels = Last->OutputChannels; ++ ++ // Check chain consistency ++ prev = First; ++ next = prev->Next; ++ ++ while (next != NULL) ++ { ++ if (next->InputChannels != prev->OutputChannels) ++ return FALSE; ++ ++ next = next->Next; ++ prev = prev->Next; ++ } + } ++ return TRUE; + } + + +@@ -1331,6 +1349,7 @@ cmsPipeline* CMSEXPORT cmsPipelineAlloc(cmsContext ContextID, cmsUInt32Number In + { + cmsPipeline* NewLUT; + ++ // A value of zero in channels is allowed as placeholder + if (InputChannels >= cmsMAXCHANNELS || + OutputChannels >= cmsMAXCHANNELS) return NULL; + +@@ -1348,7 +1367,11 @@ cmsPipeline* CMSEXPORT cmsPipelineAlloc(cmsContext ContextID, cmsUInt32Number In + NewLUT ->Data = NewLUT; + NewLUT ->ContextID = ContextID; + +- BlessLUT(NewLUT); ++ if (!BlessLUT(NewLUT)) ++ { ++ _cmsFree(ContextID, NewLUT); ++ return NULL; ++ } + + return NewLUT; + } +@@ -1454,7 +1477,12 @@ cmsPipeline* CMSEXPORT cmsPipelineDup(const cmsPipeline* lut) + + NewLUT ->SaveAs8Bits = lut ->SaveAs8Bits; + +- BlessLUT(NewLUT); ++ if (!BlessLUT(NewLUT)) ++ { ++ _cmsFree(lut->ContextID, NewLUT); ++ return NULL; ++ } ++ + return NewLUT; + } + +@@ -1491,8 +1519,7 @@ int CMSEXPORT cmsPipelineInsertStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage + return FALSE; + } + +- BlessLUT(lut); +- return TRUE; ++ return BlessLUT(lut); + } + + // Unlink an element and return the pointer to it +@@ -1547,6 +1574,7 @@ void CMSEXPORT cmsPipelineUnlinkStage(cmsPipeline* lut, cmsStageLoc loc, cmsStag + else + cmsStageFree(Unlinked); + ++ // May fail, but we ignore it + BlessLUT(lut); + } + +@@ -1573,8 +1601,7 @@ cmsBool CMSEXPORT cmsPipelineCat(cmsPipeline* l1, const cmsPipeline* l2) + return FALSE; + } + +- BlessLUT(l1); +- return TRUE; ++ return BlessLUT(l1); + } + + +diff --git a/third_party/lcms/src/cmstypes.c b/third_party/lcms/src/cmstypes.c +index e5ed06c33..0256e247b 100644 +--- a/third_party/lcms/src/cmstypes.c ++++ b/third_party/lcms/src/cmstypes.c +@@ -1755,8 +1755,8 @@ void *Type_LUT8_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cms + if (!_cmsReadUInt8Number(io, NULL)) goto Error; + + // Do some checking +- if (InputChannels > cmsMAXCHANNELS) goto Error; +- if (OutputChannels > cmsMAXCHANNELS) goto Error; ++ if (InputChannels == 0 || InputChannels > cmsMAXCHANNELS) goto Error; ++ if (OutputChannels == 0 || OutputChannels > cmsMAXCHANNELS) goto Error; + + // Allocates an empty Pipeline + NewLUT = cmsPipelineAlloc(self ->ContextID, InputChannels, OutputChannels); +@@ -2048,8 +2048,8 @@ void *Type_LUT16_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cm + if (!_cmsReadUInt8Number(io, NULL)) return NULL; + + // Do some checking +- if (InputChannels > cmsMAXCHANNELS) goto Error; +- if (OutputChannels > cmsMAXCHANNELS) goto Error; ++ if (InputChannels == 0 || InputChannels > cmsMAXCHANNELS) goto Error; ++ if (OutputChannels == 0 || OutputChannels > cmsMAXCHANNELS) goto Error; + + // Allocates an empty LUT + NewLUT = cmsPipelineAlloc(self ->ContextID, InputChannels, OutputChannels); +@@ -2486,7 +2486,10 @@ void* Type_LUTA2B_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, c + if (!_cmsReadUInt32Number(io, &offsetC)) return NULL; + if (!_cmsReadUInt32Number(io, &offsetA)) return NULL; + +- // Allocates an empty LUT ++ if (inputChan == 0 || inputChan >= cmsMAXCHANNELS) return NULL; ++ if (outputChan == 0 || outputChan >= cmsMAXCHANNELS) return NULL; ++ ++ // Allocates an empty LUT + NewLUT = cmsPipelineAlloc(self ->ContextID, inputChan, outputChan); + if (NewLUT == NULL) return NULL; + +@@ -2794,6 +2797,9 @@ void* Type_LUTB2A_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, c + if (!_cmsReadUInt8Number(io, &inputChan)) return NULL; + if (!_cmsReadUInt8Number(io, &outputChan)) return NULL; + ++ if (inputChan == 0 || inputChan >= cmsMAXCHANNELS) return NULL; ++ if (outputChan == 0 || outputChan >= cmsMAXCHANNELS) return NULL; ++ + // Padding + if (!_cmsReadUInt16Number(io, NULL)) return NULL; + +@@ -4443,6 +4449,9 @@ void *Type_MPE_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsU + if (!_cmsReadUInt16Number(io, &InputChans)) return NULL; + if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL; + ++ if (InputChans == 0 || InputChans >= cmsMAXCHANNELS) return NULL; ++ if (OutputChans == 0 || OutputChans >= cmsMAXCHANNELS) return NULL; ++ + // Allocates an empty LUT + NewLUT = cmsPipelineAlloc(self ->ContextID, InputChans, OutputChans); + if (NewLUT == NULL) return NULL; diff --git a/third_party/lcms/0017-upstream-integer-overflow-MPEmatrix_Read.patch b/third_party/lcms/0017-upstream-integer-overflow-MPEmatrix_Read.patch new file mode 100644 index 0000000000..70a6bb9b20 --- /dev/null +++ b/third_party/lcms/0017-upstream-integer-overflow-MPEmatrix_Read.patch @@ -0,0 +1,85 @@ +diff --git a/third_party/lcms/src/cmscgats.c b/third_party/lcms/src/cmscgats.c +index 5720c66a7..cce4cedba 100644 +--- a/third_party/lcms/src/cmscgats.c ++++ b/third_party/lcms/src/cmscgats.c +@@ -150,23 +150,24 @@ typedef struct { + SUBALLOCATOR Allocator; // String suballocator -- just to keep it fast + + // Parser state machine +- SYMBOL sy; // Current symbol +- int ch; // Current character ++ SYMBOL sy; // Current symbol ++ int ch; // Current character ++ ++ cmsInt32Number inum; // integer value ++ cmsFloat64Number dnum; // real value + +- int inum; // integer value +- cmsFloat64Number dnum; // real value + char id[MAXID]; // identifier + char str[MAXSTR]; // string + + // Allowed keywords & datasets. They have visibility on whole stream +- KEYVALUE* ValidKeywords; +- KEYVALUE* ValidSampleID; ++ KEYVALUE* ValidKeywords; ++ KEYVALUE* ValidSampleID; + + char* Source; // Points to loc. being parsed +- int lineno; // line counter for error reporting ++ cmsInt32Number lineno; // line counter for error reporting + + FILECTX* FileStack[MAXINCLUDE]; // Stack of files being parsed +- int IncludeSP; // Include Stack Pointer ++ cmsInt32Number IncludeSP; // Include Stack Pointer + + char* MemoryBlock; // The stream if holded in memory + +@@ -568,8 +569,8 @@ void ReadReal(cmsIT8* it8, int inum) + // Exponent, example 34.00E+20 + if (toupper(it8->ch) == 'E') { + +- int e; +- int sgn; ++ cmsInt32Number e; ++ cmsInt32Number sgn; + + NextCh(it8); sgn = 1; + +@@ -587,7 +588,7 @@ void ReadReal(cmsIT8* it8, int inum) + e = 0; + while (isdigit(it8->ch)) { + +- if ((cmsFloat64Number) e * 10L < INT_MAX) ++ if ((cmsFloat64Number) e * 10L < (cmsFloat64Number) +2147483647.0) + e = e * 10 + (it8->ch - '0'); + + NextCh(it8); +@@ -777,7 +778,7 @@ void InSymbol(cmsIT8* it8) + + while (isdigit(it8->ch)) { + +- if ((long) it8->inum * 10L > (long) INT_MAX) { ++ if ((cmsFloat64Number) it8->inum * 10L > (cmsFloat64Number) +2147483647.0) { + ReadReal(it8, it8->inum); + it8->sy = SDNUM; + it8->dnum *= sign; +diff --git a/third_party/lcms/src/cmstypes.c b/third_party/lcms/src/cmstypes.c +index 0256e247b..75f1fae32 100644 +--- a/third_party/lcms/src/cmstypes.c ++++ b/third_party/lcms/src/cmstypes.c +@@ -4199,9 +4199,13 @@ void *Type_MPEmatrix_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io + if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL; + + ++ // Input and output chans may be ANY (up to 0xffff), ++ // but we choose to limit to 16 channels for now ++ if (InputChans >= cmsMAXCHANNELS) return NULL; ++ if (OutputChans >= cmsMAXCHANNELS) return NULL; ++ + nElems = InputChans * OutputChans; + +- // Input and output chans may be ANY (up to 0xffff) + Matrix = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, nElems, sizeof(cmsFloat64Number)); + if (Matrix == NULL) return NULL; + diff --git a/third_party/lcms/0018-verify-size-before-reading.patch b/third_party/lcms/0018-verify-size-before-reading.patch new file mode 100644 index 0000000000..e72e310a1b --- /dev/null +++ b/third_party/lcms/0018-verify-size-before-reading.patch @@ -0,0 +1,17 @@ +diff --git a/third_party/lcms/src/cmstypes.c b/third_party/lcms/src/cmstypes.c +index 75f1fae32..4d96a1ed6 100644 +--- a/third_party/lcms/src/cmstypes.c ++++ b/third_party/lcms/src/cmstypes.c +@@ -173,6 +173,12 @@ cmsBool ReadPositionTable(struct _cms_typehandler_struct* self, + { + cmsUInt32Number i; + cmsUInt32Number *ElementOffsets = NULL, *ElementSizes = NULL; ++ cmsUInt32Number currentPosition; ++ ++ currentPosition = io->Tell(io); ++ // Verify there is enough space left to read two cmsUInt32Number items for Count items. ++ if (((io->ReportedSize - currentPosition) / (2 * sizeof(cmsUInt32Number))) < Count) ++ return FALSE; + + // Let's take the offsets to each element + ElementOffsets = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number)); diff --git a/third_party/lcms/0019-upstream-direct-leak-Type_MPE_Read.patch b/third_party/lcms/0019-upstream-direct-leak-Type_MPE_Read.patch new file mode 100644 index 0000000000..339333188a --- /dev/null +++ b/third_party/lcms/0019-upstream-direct-leak-Type_MPE_Read.patch @@ -0,0 +1,31 @@ +diff --git a/third_party/lcms/src/cmstypes.c b/third_party/lcms/src/cmstypes.c +index 75f1fae32..f92a92822 100644 +--- a/third_party/lcms/src/cmstypes.c ++++ b/third_party/lcms/src/cmstypes.c +@@ -4460,18 +4460,19 @@ void *Type_MPE_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsU + NewLUT = cmsPipelineAlloc(self ->ContextID, InputChans, OutputChans); + if (NewLUT == NULL) return NULL; + +- if (!_cmsReadUInt32Number(io, &ElementCount)) return NULL; +- +- if (!ReadPositionTable(self, io, ElementCount, BaseOffset, NewLUT, ReadMPEElem)) { +- if (NewLUT != NULL) cmsPipelineFree(NewLUT); +- *nItems = 0; +- return NULL; +- } ++ if (!_cmsReadUInt32Number(io, &ElementCount)) goto Error; ++ if (!ReadPositionTable(self, io, ElementCount, BaseOffset, NewLUT, ReadMPEElem)) goto Error; + + // Success + *nItems = 1; + return NewLUT; + ++ // Error ++Error: ++ if (NewLUT != NULL) cmsPipelineFree(NewLUT); ++ *nItems = 0; ++ return NULL; ++ + cmsUNUSED_PARAMETER(SizeOfTag); + } + diff --git a/third_party/lcms/README.pdfium b/third_party/lcms/README.pdfium new file mode 100644 index 0000000000..94dc67a7e3 --- /dev/null +++ b/third_party/lcms/README.pdfium @@ -0,0 +1,35 @@ +Name: Little CMS +URL: http://www.littlecms.com/ +Version: 2.6 +Security Critical: yes +License: MIT License + +Description: +Color Management Engine. + +Local Modifications: + +0000-tag-type-confusion.patch: Fix a type confusion. +0001-from16-to-8-overflow.patch: Prevent a UBSan warning. +0002-infinite-loop-GrowNamedColorList.patch: Fix infinite loop when calling GrowNamedColorList. +0003-uninit.patch: Fix use uninitialized value and stack buffer overflow read. +0004-memory-leak-Type_Curve_Read.patch: Fix memory leak in Type_Curve_Read. +0005-memory-leak-AllocEmptyTransform.patch: Fix memory leak in AllocEmptyTransform. +0006-memory-leak-Type_NamedColor_Read.patch: Fix memory leak in Type_NamedColor_Read. +0007-memory-leak-OptimizeByResampling.patch: Fix memory leak in OptimizeByResampling. +0008-memory-leak-Type_MPEmatrix_Read.patch: Fix memory leak in MPEmatrix_Read. +0009-cmsStageAllocMatrix-param-swap.patch: Fix rows/cols swap in cmsStageAllocMatrix. +0010-reject-nan.patch: Reject NaN when reading float numbers. +0011-memory-leak-ReadSegmentedCurve.patch: Fix memory leak in ReadSegmentedCurve. +0012-backport-c0a98d86.patch: Fix several issues. Backport from upstream + https://github.com/mm2/Little-CMS/commit/c0a98d86 +0013-utf8.patch: Encode source files as utf-8. +0014-avoid-fixed-inf.patch: Avoid fixed number LUT optimization on inf values. +0015-sanitize-float-read.patch: Sanitize floating point read. Partially backport + from upstream https://github.com/mm2/Little-CMS/commit/4011a6e3 +0016-check-LUT-and-MPE.patch: check LUT consistency and sanitize MPE profiles. +0017-upstream-integer-overflow-MPEmatrix_Read.patch: fix some integer overflows. +0018-verify-size-before-reading.patch: fix OOM issue when there won't be enough + data to read anyway. +0019-upstream-direct-leak-Type_MPE_Read.patch: fix leak in cmstypes.c. +TODO(ochang): List other patches. diff --git a/third_party/lcms/include/lcms2.h b/third_party/lcms/include/lcms2.h new file mode 100644 index 0000000000..8595f70203 --- /dev/null +++ b/third_party/lcms/include/lcms2.h @@ -0,0 +1,1882 @@ +//--------------------------------------------------------------------------------- +// +// 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. +// +//--------------------------------------------------------------------------------- +// +// Version 2.6 +// + +#ifndef _lcms2_H + +// ********** Configuration toggles **************************************** + +// Uncomment this one if you are using big endian machines +// #define CMS_USE_BIG_ENDIAN 1 + +// Uncomment this one if your compiler/machine does NOT support the +// "long long" type. +// #define CMS_DONT_USE_INT64 1 + +// Uncomment this if your compiler doesn't work with fast floor function +// #define CMS_DONT_USE_FAST_FLOOR 1 + +// Uncomment this line if you want lcms to use the black point tag in profile, +// if commented, lcms will compute the black point by its own. +// It is safer to leave it commented out +// #define CMS_USE_PROFILE_BLACK_POINT_TAG 1 + +// Uncomment this line if you are compiling as C++ and want a C++ API +// #define CMS_USE_CPP_API + +// Uncomment this line if you need strict CGATS syntax. Makes CGATS files to +// require "KEYWORD" on undefined identifiers, keep it comented out unless needed +// #define CMS_STRICT_CGATS 1 + +// Uncomment to get rid of the tables for "half" float support +// #define CMS_NO_HALF_SUPPORT 1 + +// Uncomment to get rid of pthreads/windows dependency +// #define CMS_NO_PTHREADS 1 + +// ********** End of configuration toggles ****************************** + +// Needed for streams +#include + +// Needed for portability (C99 per 7.1.2) +#include +#include +#include + +#ifndef CMS_USE_CPP_API +# ifdef __cplusplus +extern "C" { +# endif +#endif + +// Version/release +#define LCMS_VERSION 2060 + +// I will give the chance of redefining basic types for compilers that are not fully C99 compliant +#ifndef CMS_BASIC_TYPES_ALREADY_DEFINED + +// Base types +typedef unsigned char cmsUInt8Number; // That is guaranteed by the C99 spec +typedef signed char cmsInt8Number; // That is guaranteed by the C99 spec + +#if CHAR_BIT != 8 +# error "Unable to find 8 bit type, unsupported compiler" +#endif + +// IEEE float storage numbers +typedef float cmsFloat32Number; +typedef double cmsFloat64Number; + +// 16-bit base types +#if (USHRT_MAX == 65535U) + typedef unsigned short cmsUInt16Number; +#elif (UINT_MAX == 65535U) + typedef unsigned int cmsUInt16Number; +#else +# error "Unable to find 16 bits unsigned type, unsupported compiler" +#endif + +#if (SHRT_MAX == 32767) + typedef short cmsInt16Number; +#elif (INT_MAX == 32767) + typedef int cmsInt16Number; +#else +# error "Unable to find 16 bits signed type, unsupported compiler" +#endif + +// 32-bit base type +#if (UINT_MAX == 4294967295U) + typedef unsigned int cmsUInt32Number; +#elif (ULONG_MAX == 4294967295U) + typedef unsigned long cmsUInt32Number; +#else +# error "Unable to find 32 bit unsigned type, unsupported compiler" +#endif + +#if (INT_MAX == +2147483647) + typedef int cmsInt32Number; +#elif (LONG_MAX == +2147483647) + typedef long cmsInt32Number; +#else +# error "Unable to find 32 bit signed type, unsupported compiler" +#endif + +// 64-bit base types +#ifndef CMS_DONT_USE_INT64 +# if (ULONG_MAX == 18446744073709551615U) + typedef unsigned long cmsUInt64Number; +# elif (ULLONG_MAX == 18446744073709551615U) + typedef unsigned long long cmsUInt64Number; +# else +# define CMS_DONT_USE_INT64 1 +# endif +# if (LONG_MAX == +9223372036854775807) + typedef long cmsInt64Number; +# elif (LLONG_MAX == +9223372036854775807) + typedef long long cmsInt64Number; +# else +# define CMS_DONT_USE_INT64 1 +# endif +#endif +#endif + +// In the case 64 bit numbers are not supported by the compiler +#ifdef CMS_DONT_USE_INT64 + typedef cmsUInt32Number cmsUInt64Number[2]; + typedef cmsInt32Number cmsInt64Number[2]; +#endif + +// Derivative types +typedef cmsUInt32Number cmsSignature; +typedef cmsUInt16Number cmsU8Fixed8Number; +typedef cmsInt32Number cmsS15Fixed16Number; +typedef cmsUInt32Number cmsU16Fixed16Number; + +// Boolean type, which will be using the native integer +typedef int cmsBool; + +// Try to detect windows +#if defined (_WIN32) || defined(_WIN64) || defined(WIN32) || defined(_WIN32_) +# define CMS_IS_WINDOWS_ 1 +#endif + +#ifdef _MSC_VER +# define CMS_IS_WINDOWS_ 1 +#endif + +#ifdef __BORLANDC__ +# define CMS_IS_WINDOWS_ 1 +#endif + +// 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(__sgi__) || defined(__sgi) || defined(sparc) +# define CMS_USE_BIG_ENDIAN 1 +#endif + +#if defined(__s390__) || defined(__s390x__) +# define CMS_USE_BIG_ENDIAN 1 +#endif + +# ifdef TARGET_CPU_PPC +# if TARGET_CPU_PPC +# define CMS_USE_BIG_ENDIAN 1 +# endif +# endif + +#if defined(__powerpc__) || defined(__ppc__) || defined(TARGET_CPU_PPC) +# define CMS_USE_BIG_ENDIAN 1 +# 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 + +// 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__ +# define CMS_USE_BIG_ENDIAN 1 +# endif +# ifdef __LITTLE_ENDIAN__ +# undef CMS_USE_BIG_ENDIAN +# endif +#endif + +// Calling convention -- this is hardly platform and compiler dependent +#ifdef CMS_IS_WINDOWS_ +# if defined(CMS_DLL) || defined(CMS_DLL_BUILD) +# ifdef __BORLANDC__ +# define CMSEXPORT __stdcall _export +# define CMSAPI +# else +# define CMSEXPORT _stdcall +# ifdef CMS_DLL_BUILD +# define CMSAPI __declspec(dllexport) +# else +# define CMSAPI __declspec(dllimport) +# endif +# endif +# else +# define CMSEXPORT +# define CMSAPI +# endif +#else +# define CMSEXPORT +# define CMSAPI +#endif + +#ifdef HasTHREADS +# if HasTHREADS == 1 +# undef CMS_NO_PTHREADS +# else +# define CMS_NO_PTHREADS 1 +# endif +#endif + +// Some common definitions +#define cmsMAX_PATH 256 + +#ifndef FALSE +# define FALSE 0 +#endif +#ifndef TRUE +# define TRUE 1 +#endif + +// D50 XYZ normalized to Y=1.0 +#define cmsD50X 0.9642 +#define cmsD50Y 1.0 +#define cmsD50Z 0.8249 + +// V4 perceptual black +#define cmsPERCEPTUAL_BLACK_X 0.00336 +#define cmsPERCEPTUAL_BLACK_Y 0.0034731 +#define cmsPERCEPTUAL_BLACK_Z 0.00287 + +// Definitions in ICC spec +#define cmsMagicNumber 0x61637370 // 'acsp' +#define lcmsSignature 0x6c636d73 // 'lcms' + + +// Base ICC type definitions +typedef enum { + cmsSigChromaticityType = 0x6368726D, // 'chrm' + cmsSigColorantOrderType = 0x636C726F, // 'clro' + cmsSigColorantTableType = 0x636C7274, // 'clrt' + cmsSigCrdInfoType = 0x63726469, // 'crdi' + cmsSigCurveType = 0x63757276, // 'curv' + cmsSigDataType = 0x64617461, // 'data' + cmsSigDictType = 0x64696374, // 'dict' + cmsSigDateTimeType = 0x6474696D, // 'dtim' + cmsSigDeviceSettingsType = 0x64657673, // 'devs' + cmsSigLut16Type = 0x6d667432, // 'mft2' + cmsSigLut8Type = 0x6d667431, // 'mft1' + cmsSigLutAtoBType = 0x6d414220, // 'mAB ' + cmsSigLutBtoAType = 0x6d424120, // 'mBA ' + cmsSigMeasurementType = 0x6D656173, // 'meas' + cmsSigMultiLocalizedUnicodeType = 0x6D6C7563, // 'mluc' + cmsSigMultiProcessElementType = 0x6D706574, // 'mpet' + cmsSigNamedColorType = 0x6E636f6C, // 'ncol' -- DEPRECATED! + cmsSigNamedColor2Type = 0x6E636C32, // 'ncl2' + cmsSigParametricCurveType = 0x70617261, // 'para' + cmsSigProfileSequenceDescType = 0x70736571, // 'pseq' + cmsSigProfileSequenceIdType = 0x70736964, // 'psid' + cmsSigResponseCurveSet16Type = 0x72637332, // 'rcs2' + cmsSigS15Fixed16ArrayType = 0x73663332, // 'sf32' + cmsSigScreeningType = 0x7363726E, // 'scrn' + cmsSigSignatureType = 0x73696720, // 'sig ' + cmsSigTextType = 0x74657874, // 'text' + cmsSigTextDescriptionType = 0x64657363, // 'desc' + cmsSigU16Fixed16ArrayType = 0x75663332, // 'uf32' + cmsSigUcrBgType = 0x62666420, // 'bfd ' + cmsSigUInt16ArrayType = 0x75693136, // 'ui16' + cmsSigUInt32ArrayType = 0x75693332, // 'ui32' + cmsSigUInt64ArrayType = 0x75693634, // 'ui64' + cmsSigUInt8ArrayType = 0x75693038, // 'ui08' + cmsSigVcgtType = 0x76636774, // 'vcgt' + cmsSigViewingConditionsType = 0x76696577, // 'view' + cmsSigXYZType = 0x58595A20 // 'XYZ ' + + +} cmsTagTypeSignature; + +// Base ICC tag definitions +typedef enum { + cmsSigAToB0Tag = 0x41324230, // 'A2B0' + cmsSigAToB1Tag = 0x41324231, // 'A2B1' + cmsSigAToB2Tag = 0x41324232, // 'A2B2' + cmsSigBlueColorantTag = 0x6258595A, // 'bXYZ' + cmsSigBlueMatrixColumnTag = 0x6258595A, // 'bXYZ' + cmsSigBlueTRCTag = 0x62545243, // 'bTRC' + cmsSigBToA0Tag = 0x42324130, // 'B2A0' + cmsSigBToA1Tag = 0x42324131, // 'B2A1' + cmsSigBToA2Tag = 0x42324132, // 'B2A2' + cmsSigCalibrationDateTimeTag = 0x63616C74, // 'calt' + cmsSigCharTargetTag = 0x74617267, // 'targ' + cmsSigChromaticAdaptationTag = 0x63686164, // 'chad' + cmsSigChromaticityTag = 0x6368726D, // 'chrm' + cmsSigColorantOrderTag = 0x636C726F, // 'clro' + cmsSigColorantTableTag = 0x636C7274, // 'clrt' + cmsSigColorantTableOutTag = 0x636C6F74, // 'clot' + cmsSigColorimetricIntentImageStateTag = 0x63696973, // 'ciis' + cmsSigCopyrightTag = 0x63707274, // 'cprt' + cmsSigCrdInfoTag = 0x63726469, // 'crdi' + cmsSigDataTag = 0x64617461, // 'data' + cmsSigDateTimeTag = 0x6474696D, // 'dtim' + cmsSigDeviceMfgDescTag = 0x646D6E64, // 'dmnd' + cmsSigDeviceModelDescTag = 0x646D6464, // 'dmdd' + cmsSigDeviceSettingsTag = 0x64657673, // 'devs' + cmsSigDToB0Tag = 0x44324230, // 'D2B0' + cmsSigDToB1Tag = 0x44324231, // 'D2B1' + cmsSigDToB2Tag = 0x44324232, // 'D2B2' + cmsSigDToB3Tag = 0x44324233, // 'D2B3' + cmsSigBToD0Tag = 0x42324430, // 'B2D0' + cmsSigBToD1Tag = 0x42324431, // 'B2D1' + cmsSigBToD2Tag = 0x42324432, // 'B2D2' + cmsSigBToD3Tag = 0x42324433, // 'B2D3' + cmsSigGamutTag = 0x67616D74, // 'gamt' + cmsSigGrayTRCTag = 0x6b545243, // 'kTRC' + cmsSigGreenColorantTag = 0x6758595A, // 'gXYZ' + cmsSigGreenMatrixColumnTag = 0x6758595A, // 'gXYZ' + cmsSigGreenTRCTag = 0x67545243, // 'gTRC' + cmsSigLuminanceTag = 0x6C756d69, // 'lumi' + cmsSigMeasurementTag = 0x6D656173, // 'meas' + cmsSigMediaBlackPointTag = 0x626B7074, // 'bkpt' + cmsSigMediaWhitePointTag = 0x77747074, // 'wtpt' + cmsSigNamedColorTag = 0x6E636f6C, // 'ncol' // Deprecated by the ICC + cmsSigNamedColor2Tag = 0x6E636C32, // 'ncl2' + cmsSigOutputResponseTag = 0x72657370, // 'resp' + cmsSigPerceptualRenderingIntentGamutTag = 0x72696730, // 'rig0' + cmsSigPreview0Tag = 0x70726530, // 'pre0' + cmsSigPreview1Tag = 0x70726531, // 'pre1' + cmsSigPreview2Tag = 0x70726532, // 'pre2' + cmsSigProfileDescriptionTag = 0x64657363, // 'desc' + cmsSigProfileDescriptionMLTag = 0x6473636d, // 'dscm' + cmsSigProfileSequenceDescTag = 0x70736571, // 'pseq' + cmsSigProfileSequenceIdTag = 0x70736964, // 'psid' + cmsSigPs2CRD0Tag = 0x70736430, // 'psd0' + cmsSigPs2CRD1Tag = 0x70736431, // 'psd1' + cmsSigPs2CRD2Tag = 0x70736432, // 'psd2' + cmsSigPs2CRD3Tag = 0x70736433, // 'psd3' + cmsSigPs2CSATag = 0x70733273, // 'ps2s' + cmsSigPs2RenderingIntentTag = 0x70733269, // 'ps2i' + cmsSigRedColorantTag = 0x7258595A, // 'rXYZ' + cmsSigRedMatrixColumnTag = 0x7258595A, // 'rXYZ' + cmsSigRedTRCTag = 0x72545243, // 'rTRC' + cmsSigSaturationRenderingIntentGamutTag = 0x72696732, // 'rig2' + cmsSigScreeningDescTag = 0x73637264, // 'scrd' + cmsSigScreeningTag = 0x7363726E, // 'scrn' + cmsSigTechnologyTag = 0x74656368, // 'tech' + cmsSigUcrBgTag = 0x62666420, // 'bfd ' + cmsSigViewingCondDescTag = 0x76756564, // 'vued' + cmsSigViewingConditionsTag = 0x76696577, // 'view' + cmsSigVcgtTag = 0x76636774, // 'vcgt' + cmsSigMetaTag = 0x6D657461 // 'meta' + +} cmsTagSignature; + + +// ICC Technology tag +typedef enum { + cmsSigDigitalCamera = 0x6463616D, // 'dcam' + cmsSigFilmScanner = 0x6673636E, // 'fscn' + cmsSigReflectiveScanner = 0x7273636E, // 'rscn' + cmsSigInkJetPrinter = 0x696A6574, // 'ijet' + cmsSigThermalWaxPrinter = 0x74776178, // 'twax' + cmsSigElectrophotographicPrinter = 0x6570686F, // 'epho' + cmsSigElectrostaticPrinter = 0x65737461, // 'esta' + cmsSigDyeSublimationPrinter = 0x64737562, // 'dsub' + cmsSigPhotographicPaperPrinter = 0x7270686F, // 'rpho' + cmsSigFilmWriter = 0x6670726E, // 'fprn' + cmsSigVideoMonitor = 0x7669646D, // 'vidm' + cmsSigVideoCamera = 0x76696463, // 'vidc' + cmsSigProjectionTelevision = 0x706A7476, // 'pjtv' + cmsSigCRTDisplay = 0x43525420, // 'CRT ' + cmsSigPMDisplay = 0x504D4420, // 'PMD ' + cmsSigAMDisplay = 0x414D4420, // 'AMD ' + cmsSigPhotoCD = 0x4B504344, // 'KPCD' + cmsSigPhotoImageSetter = 0x696D6773, // 'imgs' + cmsSigGravure = 0x67726176, // 'grav' + cmsSigOffsetLithography = 0x6F666673, // 'offs' + cmsSigSilkscreen = 0x73696C6B, // 'silk' + cmsSigFlexography = 0x666C6578, // 'flex' + cmsSigMotionPictureFilmScanner = 0x6D706673, // 'mpfs' + cmsSigMotionPictureFilmRecorder = 0x6D706672, // 'mpfr' + cmsSigDigitalMotionPictureCamera = 0x646D7063, // 'dmpc' + cmsSigDigitalCinemaProjector = 0x64636A70 // 'dcpj' + +} cmsTechnologySignature; + + +// ICC Color spaces +typedef enum { + cmsSigXYZData = 0x58595A20, // 'XYZ ' + cmsSigLabData = 0x4C616220, // 'Lab ' + cmsSigLuvData = 0x4C757620, // 'Luv ' + cmsSigYCbCrData = 0x59436272, // 'YCbr' + cmsSigYxyData = 0x59787920, // 'Yxy ' + cmsSigRgbData = 0x52474220, // 'RGB ' + cmsSigGrayData = 0x47524159, // 'GRAY' + cmsSigHsvData = 0x48535620, // 'HSV ' + cmsSigHlsData = 0x484C5320, // 'HLS ' + cmsSigCmykData = 0x434D594B, // 'CMYK' + cmsSigCmyData = 0x434D5920, // 'CMY ' + cmsSigMCH1Data = 0x4D434831, // 'MCH1' + cmsSigMCH2Data = 0x4D434832, // 'MCH2' + cmsSigMCH3Data = 0x4D434833, // 'MCH3' + cmsSigMCH4Data = 0x4D434834, // 'MCH4' + cmsSigMCH5Data = 0x4D434835, // 'MCH5' + cmsSigMCH6Data = 0x4D434836, // 'MCH6' + cmsSigMCH7Data = 0x4D434837, // 'MCH7' + cmsSigMCH8Data = 0x4D434838, // 'MCH8' + cmsSigMCH9Data = 0x4D434839, // 'MCH9' + cmsSigMCHAData = 0x4D434841, // 'MCHA' + cmsSigMCHBData = 0x4D434842, // 'MCHB' + cmsSigMCHCData = 0x4D434843, // 'MCHC' + cmsSigMCHDData = 0x4D434844, // 'MCHD' + cmsSigMCHEData = 0x4D434845, // 'MCHE' + cmsSigMCHFData = 0x4D434846, // 'MCHF' + cmsSigNamedData = 0x6e6d636c, // 'nmcl' + cmsSig1colorData = 0x31434C52, // '1CLR' + cmsSig2colorData = 0x32434C52, // '2CLR' + cmsSig3colorData = 0x33434C52, // '3CLR' + cmsSig4colorData = 0x34434C52, // '4CLR' + cmsSig5colorData = 0x35434C52, // '5CLR' + cmsSig6colorData = 0x36434C52, // '6CLR' + cmsSig7colorData = 0x37434C52, // '7CLR' + cmsSig8colorData = 0x38434C52, // '8CLR' + cmsSig9colorData = 0x39434C52, // '9CLR' + cmsSig10colorData = 0x41434C52, // 'ACLR' + cmsSig11colorData = 0x42434C52, // 'BCLR' + cmsSig12colorData = 0x43434C52, // 'CCLR' + cmsSig13colorData = 0x44434C52, // 'DCLR' + cmsSig14colorData = 0x45434C52, // 'ECLR' + cmsSig15colorData = 0x46434C52, // 'FCLR' + cmsSigLuvKData = 0x4C75764B // 'LuvK' + +} cmsColorSpaceSignature; + +// ICC Profile Class +typedef enum { + cmsSigInputClass = 0x73636E72, // 'scnr' + cmsSigDisplayClass = 0x6D6E7472, // 'mntr' + cmsSigOutputClass = 0x70727472, // 'prtr' + cmsSigLinkClass = 0x6C696E6B, // 'link' + cmsSigAbstractClass = 0x61627374, // 'abst' + cmsSigColorSpaceClass = 0x73706163, // 'spac' + cmsSigNamedColorClass = 0x6e6d636c // 'nmcl' + +} cmsProfileClassSignature; + +// ICC Platforms +typedef enum { + cmsSigMacintosh = 0x4150504C, // 'APPL' + cmsSigMicrosoft = 0x4D534654, // 'MSFT' + cmsSigSolaris = 0x53554E57, // 'SUNW' + cmsSigSGI = 0x53474920, // 'SGI ' + cmsSigTaligent = 0x54474E54, // 'TGNT' + cmsSigUnices = 0x2A6E6978 // '*nix' // From argyll -- Not official + +} cmsPlatformSignature; + +// Reference gamut +#define cmsSigPerceptualReferenceMediumGamut 0x70726d67 //'prmg' + +// For cmsSigColorimetricIntentImageStateTag +#define cmsSigSceneColorimetryEstimates 0x73636F65 //'scoe' +#define cmsSigSceneAppearanceEstimates 0x73617065 //'sape' +#define cmsSigFocalPlaneColorimetryEstimates 0x66706365 //'fpce' +#define cmsSigReflectionHardcopyOriginalColorimetry 0x72686F63 //'rhoc' +#define cmsSigReflectionPrintOutputColorimetry 0x72706F63 //'rpoc' + +// Multi process elements types +typedef enum { + cmsSigCurveSetElemType = 0x63767374, //'cvst' + cmsSigMatrixElemType = 0x6D617466, //'matf' + cmsSigCLutElemType = 0x636C7574, //'clut' + + cmsSigBAcsElemType = 0x62414353, // 'bACS' + cmsSigEAcsElemType = 0x65414353, // 'eACS' + + // Custom from here, not in the ICC Spec + cmsSigXYZ2LabElemType = 0x6C327820, // 'l2x ' + cmsSigLab2XYZElemType = 0x78326C20, // 'x2l ' + cmsSigNamedColorElemType = 0x6E636C20, // 'ncl ' + cmsSigLabV2toV4 = 0x32203420, // '2 4 ' + cmsSigLabV4toV2 = 0x34203220, // '4 2 ' + + // Identities + cmsSigIdentityElemType = 0x69646E20, // 'idn ' + + // Float to floatPCS + cmsSigLab2FloatPCS = 0x64326C20, // 'd2l ' + cmsSigFloatPCS2Lab = 0x6C326420, // 'l2d ' + cmsSigXYZ2FloatPCS = 0x64327820, // 'd2x ' + cmsSigFloatPCS2XYZ = 0x78326420 // 'x2d ' + +} cmsStageSignature; + +// Types of CurveElements +typedef enum { + + cmsSigFormulaCurveSeg = 0x70617266, // 'parf' + cmsSigSampledCurveSeg = 0x73616D66, // 'samf' + cmsSigSegmentedCurve = 0x63757266 // 'curf' + +} cmsCurveSegSignature; + +// Used in ResponseCurveType +#define cmsSigStatusA 0x53746141 //'StaA' +#define cmsSigStatusE 0x53746145 //'StaE' +#define cmsSigStatusI 0x53746149 //'StaI' +#define cmsSigStatusT 0x53746154 //'StaT' +#define cmsSigStatusM 0x5374614D //'StaM' +#define cmsSigDN 0x444E2020 //'DN ' +#define cmsSigDNP 0x444E2050 //'DN P' +#define cmsSigDNN 0x444E4E20 //'DNN ' +#define cmsSigDNNP 0x444E4E50 //'DNNP' + +// Device attributes, currently defined values correspond to the low 4 bytes +// of the 8 byte attribute quantity +#define cmsReflective 0 +#define cmsTransparency 1 +#define cmsGlossy 0 +#define cmsMatte 2 + +// Common structures in ICC tags +typedef struct { + cmsUInt32Number len; + cmsUInt32Number flag; + cmsUInt8Number data[1]; + +} cmsICCData; + +// ICC date time +typedef struct { + cmsUInt16Number year; + cmsUInt16Number month; + cmsUInt16Number day; + cmsUInt16Number hours; + cmsUInt16Number minutes; + cmsUInt16Number seconds; + +} cmsDateTimeNumber; + +// ICC XYZ +typedef struct { + cmsS15Fixed16Number X; + cmsS15Fixed16Number Y; + cmsS15Fixed16Number Z; + +} cmsEncodedXYZNumber; + + +// Profile ID as computed by MD5 algorithm +typedef union { + cmsUInt8Number ID8[16]; + cmsUInt16Number ID16[8]; + cmsUInt32Number ID32[4]; + +} cmsProfileID; + + +// ---------------------------------------------------------------------------------------------- +// ICC profile internal base types. Strictly, shouldn't be declared in this header, but maybe +// somebody want to use this info for accessing profile header directly, so here it is. + +// Profile header -- it is 32-bit aligned, so no issues are expected on alignment +typedef struct { + cmsUInt32Number size; // Profile size in bytes + cmsSignature cmmId; // CMM for this profile + cmsUInt32Number version; // Format version number + cmsProfileClassSignature deviceClass; // Type of profile + cmsColorSpaceSignature colorSpace; // Color space of data + cmsColorSpaceSignature pcs; // PCS, XYZ or Lab only + cmsDateTimeNumber date; // Date profile was created + cmsSignature magic; // Magic Number to identify an ICC profile + cmsPlatformSignature platform; // Primary Platform + cmsUInt32Number flags; // Various bit settings + cmsSignature manufacturer; // Device manufacturer + cmsUInt32Number model; // Device model number + cmsUInt64Number attributes; // Device attributes + cmsUInt32Number renderingIntent;// Rendering intent + cmsEncodedXYZNumber illuminant; // Profile illuminant + cmsSignature creator; // Profile creator + cmsProfileID profileID; // Profile ID using MD5 + cmsInt8Number reserved[28]; // Reserved for future use + +} cmsICCHeader; + +// ICC base tag +typedef struct { + cmsTagTypeSignature sig; + cmsInt8Number reserved[4]; + +} cmsTagBase; + +// A tag entry in directory +typedef struct { + cmsTagSignature sig; // The tag signature + cmsUInt32Number offset; // Start of tag + cmsUInt32Number size; // Size in bytes + +} cmsTagEntry; + +// ---------------------------------------------------------------------------------------------- + +// Little CMS specific typedefs + +typedef void* cmsHANDLE ; // Generic handle +typedef void* cmsHPROFILE; // Opaque typedefs to hide internals +typedef void* cmsHTRANSFORM; + +#define cmsMAXCHANNELS 16 // Maximum number of channels in ICC profiles + +// Format of pixel is defined by one cmsUInt32Number, using bit fields as follows +// +// 2 1 0 +// 3 2 10987 6 5 4 3 2 1 098 7654 321 +// A O TTTTT U Y F P X S EEE CCCC BBB +// +// A: Floating point -- With this flag we can differentiate 16 bits as float and as int +// O: Optimized -- previous optimization already returns the final 8-bit value +// T: Pixeltype +// F: Flavor 0=MinIsBlack(Chocolate) 1=MinIsWhite(Vanilla) +// P: Planar? 0=Chunky, 1=Planar +// X: swap 16 bps endianess? +// S: Do swap? ie, BGR, KYMC +// E: Extra samples +// C: Channels (Samples per pixel) +// B: bytes per sample +// Y: Swap first - changes ABGR to BGRA and KCMY to CMYK + +#define FLOAT_SH(a) ((a) << 22) +#define OPTIMIZED_SH(s) ((s) << 21) +#define COLORSPACE_SH(s) ((s) << 16) +#define SWAPFIRST_SH(s) ((s) << 14) +#define FLAVOR_SH(s) ((s) << 13) +#define PLANAR_SH(p) ((p) << 12) +#define ENDIAN16_SH(e) ((e) << 11) +#define DOSWAP_SH(e) ((e) << 10) +#define EXTRA_SH(e) ((e) << 7) +#define CHANNELS_SH(c) ((c) << 3) +#define BYTES_SH(b) (b) + +// These macros unpack format specifiers into integers +#define T_FLOAT(a) (((a)>>22)&1) +#define T_OPTIMIZED(o) (((o)>>21)&1) +#define T_COLORSPACE(s) (((s)>>16)&31) +#define T_SWAPFIRST(s) (((s)>>14)&1) +#define T_FLAVOR(s) (((s)>>13)&1) +#define T_PLANAR(p) (((p)>>12)&1) +#define T_ENDIAN16(e) (((e)>>11)&1) +#define T_DOSWAP(e) (((e)>>10)&1) +#define T_EXTRA(e) (((e)>>7)&7) +#define T_CHANNELS(c) (((c)>>3)&15) +#define T_BYTES(b) ((b)&7) + + +// Pixel types +#define PT_ANY 0 // Don't check colorspace + // 1 & 2 are reserved +#define PT_GRAY 3 +#define PT_RGB 4 +#define PT_CMY 5 +#define PT_CMYK 6 +#define PT_YCbCr 7 +#define PT_YUV 8 // Lu'v' +#define PT_XYZ 9 +#define PT_Lab 10 +#define PT_YUVK 11 // Lu'v'K +#define PT_HSV 12 +#define PT_HLS 13 +#define PT_Yxy 14 + +#define PT_MCH1 15 +#define PT_MCH2 16 +#define PT_MCH3 17 +#define PT_MCH4 18 +#define PT_MCH5 19 +#define PT_MCH6 20 +#define PT_MCH7 21 +#define PT_MCH8 22 +#define PT_MCH9 23 +#define PT_MCH10 24 +#define PT_MCH11 25 +#define PT_MCH12 26 +#define PT_MCH13 27 +#define PT_MCH14 28 +#define PT_MCH15 29 + +#define PT_LabV2 30 // Identical to PT_Lab, but using the V2 old encoding + +// Some (not all!) representations + +#ifndef TYPE_RGB_8 // TYPE_RGB_8 is a very common identifier, so don't include ours + // if user has it already defined. + +#define TYPE_GRAY_8 (COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(1)) +#define TYPE_GRAY_8_REV (COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(1)|FLAVOR_SH(1)) +#define TYPE_GRAY_16 (COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(2)) +#define TYPE_GRAY_16_REV (COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(2)|FLAVOR_SH(1)) +#define TYPE_GRAY_16_SE (COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_GRAYA_8 (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(1)) +#define TYPE_GRAYA_16 (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(2)) +#define TYPE_GRAYA_16_SE (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_GRAYA_8_PLANAR (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(1)|PLANAR_SH(1)) +#define TYPE_GRAYA_16_PLANAR (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(2)|PLANAR_SH(1)) + +#define TYPE_RGB_8 (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(1)) +#define TYPE_RGB_8_PLANAR (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) +#define TYPE_BGR_8 (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)) +#define TYPE_BGR_8_PLANAR (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|PLANAR_SH(1)) +#define TYPE_RGB_16 (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_RGB_16_PLANAR (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) +#define TYPE_RGB_16_SE (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_BGR_16 (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_BGR_16_PLANAR (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|PLANAR_SH(1)) +#define TYPE_BGR_16_SE (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) + +#define TYPE_RGBA_8 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)) +#define TYPE_RGBA_8_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) +#define TYPE_RGBA_16 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_RGBA_16_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) +#define TYPE_RGBA_16_SE (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) + +#define TYPE_ARGB_8 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_ARGB_8_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|SWAPFIRST_SH(1)|PLANAR_SH(1)) +#define TYPE_ARGB_16 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|SWAPFIRST_SH(1)) + +#define TYPE_ABGR_8 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)) +#define TYPE_ABGR_8_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|PLANAR_SH(1)) +#define TYPE_ABGR_16 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_ABGR_16_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|PLANAR_SH(1)) +#define TYPE_ABGR_16_SE (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) + +#define TYPE_BGRA_8 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_BGRA_8_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1)|PLANAR_SH(1)) +#define TYPE_BGRA_16 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_BGRA_16_SE (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1)) + +#define TYPE_CMY_8 (COLORSPACE_SH(PT_CMY)|CHANNELS_SH(3)|BYTES_SH(1)) +#define TYPE_CMY_8_PLANAR (COLORSPACE_SH(PT_CMY)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) +#define TYPE_CMY_16 (COLORSPACE_SH(PT_CMY)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_CMY_16_PLANAR (COLORSPACE_SH(PT_CMY)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) +#define TYPE_CMY_16_SE (COLORSPACE_SH(PT_CMY)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) + +#define TYPE_CMYK_8 (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(1)) +#define TYPE_CMYKA_8 (COLORSPACE_SH(PT_CMYK)|EXTRA_SH(1)|CHANNELS_SH(4)|BYTES_SH(1)) +#define TYPE_CMYK_8_REV (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(1)|FLAVOR_SH(1)) +#define TYPE_YUVK_8 TYPE_CMYK_8_REV +#define TYPE_CMYK_8_PLANAR (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(1)|PLANAR_SH(1)) +#define TYPE_CMYK_16 (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)) +#define TYPE_CMYK_16_REV (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|FLAVOR_SH(1)) +#define TYPE_YUVK_16 TYPE_CMYK_16_REV +#define TYPE_CMYK_16_PLANAR (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|PLANAR_SH(1)) +#define TYPE_CMYK_16_SE (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|ENDIAN16_SH(1)) + +#define TYPE_KYMC_8 (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1)) +#define TYPE_KYMC_16 (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_KYMC_16_SE (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) + +#define TYPE_KCMY_8 (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_KCMY_8_REV (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(1)|FLAVOR_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_KCMY_16 (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|SWAPFIRST_SH(1)) +#define TYPE_KCMY_16_REV (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|FLAVOR_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_KCMY_16_SE (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|ENDIAN16_SH(1)|SWAPFIRST_SH(1)) + +#define TYPE_CMYK5_8 (COLORSPACE_SH(PT_MCH5)|CHANNELS_SH(5)|BYTES_SH(1)) +#define TYPE_CMYK5_16 (COLORSPACE_SH(PT_MCH5)|CHANNELS_SH(5)|BYTES_SH(2)) +#define TYPE_CMYK5_16_SE (COLORSPACE_SH(PT_MCH5)|CHANNELS_SH(5)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_KYMC5_8 (COLORSPACE_SH(PT_MCH5)|CHANNELS_SH(5)|BYTES_SH(1)|DOSWAP_SH(1)) +#define TYPE_KYMC5_16 (COLORSPACE_SH(PT_MCH5)|CHANNELS_SH(5)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_KYMC5_16_SE (COLORSPACE_SH(PT_MCH5)|CHANNELS_SH(5)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) +#define TYPE_CMYK6_8 (COLORSPACE_SH(PT_MCH6)|CHANNELS_SH(6)|BYTES_SH(1)) +#define TYPE_CMYK6_8_PLANAR (COLORSPACE_SH(PT_MCH6)|CHANNELS_SH(6)|BYTES_SH(1)|PLANAR_SH(1)) +#define TYPE_CMYK6_16 (COLORSPACE_SH(PT_MCH6)|CHANNELS_SH(6)|BYTES_SH(2)) +#define TYPE_CMYK6_16_PLANAR (COLORSPACE_SH(PT_MCH6)|CHANNELS_SH(6)|BYTES_SH(2)|PLANAR_SH(1)) +#define TYPE_CMYK6_16_SE (COLORSPACE_SH(PT_MCH6)|CHANNELS_SH(6)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_CMYK7_8 (COLORSPACE_SH(PT_MCH7)|CHANNELS_SH(7)|BYTES_SH(1)) +#define TYPE_CMYK7_16 (COLORSPACE_SH(PT_MCH7)|CHANNELS_SH(7)|BYTES_SH(2)) +#define TYPE_CMYK7_16_SE (COLORSPACE_SH(PT_MCH7)|CHANNELS_SH(7)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_KYMC7_8 (COLORSPACE_SH(PT_MCH7)|CHANNELS_SH(7)|BYTES_SH(1)|DOSWAP_SH(1)) +#define TYPE_KYMC7_16 (COLORSPACE_SH(PT_MCH7)|CHANNELS_SH(7)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_KYMC7_16_SE (COLORSPACE_SH(PT_MCH7)|CHANNELS_SH(7)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) +#define TYPE_CMYK8_8 (COLORSPACE_SH(PT_MCH8)|CHANNELS_SH(8)|BYTES_SH(1)) +#define TYPE_CMYK8_16 (COLORSPACE_SH(PT_MCH8)|CHANNELS_SH(8)|BYTES_SH(2)) +#define TYPE_CMYK8_16_SE (COLORSPACE_SH(PT_MCH8)|CHANNELS_SH(8)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_KYMC8_8 (COLORSPACE_SH(PT_MCH8)|CHANNELS_SH(8)|BYTES_SH(1)|DOSWAP_SH(1)) +#define TYPE_KYMC8_16 (COLORSPACE_SH(PT_MCH8)|CHANNELS_SH(8)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_KYMC8_16_SE (COLORSPACE_SH(PT_MCH8)|CHANNELS_SH(8)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) +#define TYPE_CMYK9_8 (COLORSPACE_SH(PT_MCH9)|CHANNELS_SH(9)|BYTES_SH(1)) +#define TYPE_CMYK9_16 (COLORSPACE_SH(PT_MCH9)|CHANNELS_SH(9)|BYTES_SH(2)) +#define TYPE_CMYK9_16_SE (COLORSPACE_SH(PT_MCH9)|CHANNELS_SH(9)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_KYMC9_8 (COLORSPACE_SH(PT_MCH9)|CHANNELS_SH(9)|BYTES_SH(1)|DOSWAP_SH(1)) +#define TYPE_KYMC9_16 (COLORSPACE_SH(PT_MCH9)|CHANNELS_SH(9)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_KYMC9_16_SE (COLORSPACE_SH(PT_MCH9)|CHANNELS_SH(9)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) +#define TYPE_CMYK10_8 (COLORSPACE_SH(PT_MCH10)|CHANNELS_SH(10)|BYTES_SH(1)) +#define TYPE_CMYK10_16 (COLORSPACE_SH(PT_MCH10)|CHANNELS_SH(10)|BYTES_SH(2)) +#define TYPE_CMYK10_16_SE (COLORSPACE_SH(PT_MCH10)|CHANNELS_SH(10)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_KYMC10_8 (COLORSPACE_SH(PT_MCH10)|CHANNELS_SH(10)|BYTES_SH(1)|DOSWAP_SH(1)) +#define TYPE_KYMC10_16 (COLORSPACE_SH(PT_MCH10)|CHANNELS_SH(10)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_KYMC10_16_SE (COLORSPACE_SH(PT_MCH10)|CHANNELS_SH(10)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) +#define TYPE_CMYK11_8 (COLORSPACE_SH(PT_MCH11)|CHANNELS_SH(11)|BYTES_SH(1)) +#define TYPE_CMYK11_16 (COLORSPACE_SH(PT_MCH11)|CHANNELS_SH(11)|BYTES_SH(2)) +#define TYPE_CMYK11_16_SE (COLORSPACE_SH(PT_MCH11)|CHANNELS_SH(11)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_KYMC11_8 (COLORSPACE_SH(PT_MCH11)|CHANNELS_SH(11)|BYTES_SH(1)|DOSWAP_SH(1)) +#define TYPE_KYMC11_16 (COLORSPACE_SH(PT_MCH11)|CHANNELS_SH(11)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_KYMC11_16_SE (COLORSPACE_SH(PT_MCH11)|CHANNELS_SH(11)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) +#define TYPE_CMYK12_8 (COLORSPACE_SH(PT_MCH12)|CHANNELS_SH(12)|BYTES_SH(1)) +#define TYPE_CMYK12_16 (COLORSPACE_SH(PT_MCH12)|CHANNELS_SH(12)|BYTES_SH(2)) +#define TYPE_CMYK12_16_SE (COLORSPACE_SH(PT_MCH12)|CHANNELS_SH(12)|BYTES_SH(2)|ENDIAN16_SH(1)) +#define TYPE_KYMC12_8 (COLORSPACE_SH(PT_MCH12)|CHANNELS_SH(12)|BYTES_SH(1)|DOSWAP_SH(1)) +#define TYPE_KYMC12_16 (COLORSPACE_SH(PT_MCH12)|CHANNELS_SH(12)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_KYMC12_16_SE (COLORSPACE_SH(PT_MCH12)|CHANNELS_SH(12)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) + +// Colorimetric +#define TYPE_XYZ_16 (COLORSPACE_SH(PT_XYZ)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_Lab_8 (COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(1)) +#define TYPE_LabV2_8 (COLORSPACE_SH(PT_LabV2)|CHANNELS_SH(3)|BYTES_SH(1)) + +#define TYPE_ALab_8 (COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_ALabV2_8 (COLORSPACE_SH(PT_LabV2)|CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_Lab_16 (COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_LabV2_16 (COLORSPACE_SH(PT_LabV2)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_Yxy_16 (COLORSPACE_SH(PT_Yxy)|CHANNELS_SH(3)|BYTES_SH(2)) + +// YCbCr +#define TYPE_YCbCr_8 (COLORSPACE_SH(PT_YCbCr)|CHANNELS_SH(3)|BYTES_SH(1)) +#define TYPE_YCbCr_8_PLANAR (COLORSPACE_SH(PT_YCbCr)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) +#define TYPE_YCbCr_16 (COLORSPACE_SH(PT_YCbCr)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_YCbCr_16_PLANAR (COLORSPACE_SH(PT_YCbCr)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) +#define TYPE_YCbCr_16_SE (COLORSPACE_SH(PT_YCbCr)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) + +// YUV +#define TYPE_YUV_8 (COLORSPACE_SH(PT_YUV)|CHANNELS_SH(3)|BYTES_SH(1)) +#define TYPE_YUV_8_PLANAR (COLORSPACE_SH(PT_YUV)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) +#define TYPE_YUV_16 (COLORSPACE_SH(PT_YUV)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_YUV_16_PLANAR (COLORSPACE_SH(PT_YUV)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) +#define TYPE_YUV_16_SE (COLORSPACE_SH(PT_YUV)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) + +// HLS +#define TYPE_HLS_8 (COLORSPACE_SH(PT_HLS)|CHANNELS_SH(3)|BYTES_SH(1)) +#define TYPE_HLS_8_PLANAR (COLORSPACE_SH(PT_HLS)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) +#define TYPE_HLS_16 (COLORSPACE_SH(PT_HLS)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_HLS_16_PLANAR (COLORSPACE_SH(PT_HLS)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) +#define TYPE_HLS_16_SE (COLORSPACE_SH(PT_HLS)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) + +// HSV +#define TYPE_HSV_8 (COLORSPACE_SH(PT_HSV)|CHANNELS_SH(3)|BYTES_SH(1)) +#define TYPE_HSV_8_PLANAR (COLORSPACE_SH(PT_HSV)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) +#define TYPE_HSV_16 (COLORSPACE_SH(PT_HSV)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_HSV_16_PLANAR (COLORSPACE_SH(PT_HSV)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) +#define TYPE_HSV_16_SE (COLORSPACE_SH(PT_HSV)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) + +// Named color index. Only 16 bits allowed (don't check colorspace) +#define TYPE_NAMED_COLOR_INDEX (CHANNELS_SH(1)|BYTES_SH(2)) + +// Float formatters. +#define TYPE_XYZ_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_XYZ)|CHANNELS_SH(3)|BYTES_SH(4)) +#define TYPE_Lab_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(4)) +#define TYPE_LabA_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_Lab)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)) +#define TYPE_GRAY_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(4)) +#define TYPE_RGB_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(4)) + +#define TYPE_RGBA_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)) +#define TYPE_ARGB_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|SWAPFIRST_SH(1)) +#define TYPE_BGR_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(4)|DOSWAP_SH(1)) +#define TYPE_BGRA_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|DOSWAP_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_ABGR_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(4)|DOSWAP_SH(1)) + +#define TYPE_CMYK_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(4)) + +// Floating point formatters. +// NOTE THAT 'BYTES' FIELD IS SET TO ZERO ON DLB because 8 bytes overflows the bitfield +#define TYPE_XYZ_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_XYZ)|CHANNELS_SH(3)|BYTES_SH(0)) +#define TYPE_Lab_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(0)) +#define TYPE_GRAY_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(0)) +#define TYPE_RGB_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(0)) +#define TYPE_BGR_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(0)|DOSWAP_SH(1)) +#define TYPE_CMYK_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(0)) + +// IEEE 754-2008 "half" +#define TYPE_GRAY_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(2)) +#define TYPE_RGB_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_RGBA_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_CMYK_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)) + +#define TYPE_RGBA_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_ARGB_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|SWAPFIRST_SH(1)) +#define TYPE_BGR_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_BGRA_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_ABGR_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)) + +#endif + +// Colorspaces +typedef struct { + cmsFloat64Number X; + cmsFloat64Number Y; + cmsFloat64Number Z; + + } cmsCIEXYZ; + +typedef struct { + cmsFloat64Number x; + cmsFloat64Number y; + cmsFloat64Number Y; + + } cmsCIExyY; + +typedef struct { + cmsFloat64Number L; + cmsFloat64Number a; + cmsFloat64Number b; + + } cmsCIELab; + +typedef struct { + cmsFloat64Number L; + cmsFloat64Number C; + cmsFloat64Number h; + + } cmsCIELCh; + +typedef struct { + cmsFloat64Number J; + cmsFloat64Number C; + cmsFloat64Number h; + + } cmsJCh; + +typedef struct { + cmsCIEXYZ Red; + cmsCIEXYZ Green; + cmsCIEXYZ Blue; + + } cmsCIEXYZTRIPLE; + +typedef struct { + cmsCIExyY Red; + cmsCIExyY Green; + cmsCIExyY Blue; + + } cmsCIExyYTRIPLE; + +// Illuminant types for structs below +#define cmsILLUMINANT_TYPE_UNKNOWN 0x0000000 +#define cmsILLUMINANT_TYPE_D50 0x0000001 +#define cmsILLUMINANT_TYPE_D65 0x0000002 +#define cmsILLUMINANT_TYPE_D93 0x0000003 +#define cmsILLUMINANT_TYPE_F2 0x0000004 +#define cmsILLUMINANT_TYPE_D55 0x0000005 +#define cmsILLUMINANT_TYPE_A 0x0000006 +#define cmsILLUMINANT_TYPE_E 0x0000007 +#define cmsILLUMINANT_TYPE_F8 0x0000008 + +typedef struct { + cmsUInt32Number Observer; // 0 = unknown, 1=CIE 1931, 2=CIE 1964 + cmsCIEXYZ Backing; // Value of backing + cmsUInt32Number Geometry; // 0=unknown, 1=45/0, 0/45 2=0d, d/0 + cmsFloat64Number Flare; // 0..1.0 + cmsUInt32Number IlluminantType; + + } cmsICCMeasurementConditions; + +typedef struct { + cmsCIEXYZ IlluminantXYZ; // Not the same struct as CAM02, + cmsCIEXYZ SurroundXYZ; // This is for storing the tag + cmsUInt32Number IlluminantType; // viewing condition + + } cmsICCViewingConditions; + +// Support of non-standard functions -------------------------------------------------------------------------------------- + +CMSAPI int CMSEXPORT cmsstrcasecmp(const char* s1, const char* s2); +CMSAPI long int CMSEXPORT cmsfilelength(FILE* f); + + +// 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. + +typedef struct _cmsContext_struct* cmsContext; + +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 ---------------------------------------------------------------------------------------------------------- + +// There is no error handling at all. When a function fails, it returns proper value. +// For example, all create functions does return NULL on failure. Other may return FALSE. +// It may be interesting, for the developer, to know why the function is failing. +// for that reason, lcms2 does offer a logging function. This function will get +// an ENGLISH string with some clues on what is going wrong. You can show this +// info to the end user if you wish, or just create some sort of log on disk. +// The logging function should NOT terminate the program, as this obviously can leave +// unfreed resources. It is the programmer's responsibility to check each function +// return code to make sure it didn't fail. + +#define cmsERROR_UNDEFINED 0 +#define cmsERROR_FILE 1 +#define cmsERROR_RANGE 2 +#define cmsERROR_INTERNAL 3 +#define cmsERROR_NULL 4 +#define cmsERROR_READ 5 +#define cmsERROR_SEEK 6 +#define cmsERROR_WRITE 7 +#define cmsERROR_UNKNOWN_EXTENSION 8 +#define cmsERROR_COLORSPACE_CHECK 9 +#define cmsERROR_ALREADY_DEFINED 10 +#define cmsERROR_BAD_SIGNATURE 11 +#define cmsERROR_CORRUPTION_DETECTED 12 +#define cmsERROR_NOT_SUITABLE 13 + +// Error logger is called with the ContextID when a message is raised. This gives the +// chance to know which thread is responsible of the warning and any environment associated +// with it. Non-multithreading applications may safely ignore this parameter. +// Note that under certain special circumstances, ContextID may be NULL. +typedef void (* cmsLogErrorHandlerFunction)(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text); + +// Allows user to set any specific logger +CMSAPI void CMSEXPORT cmsSetLogErrorHandler(cmsLogErrorHandlerFunction Fn); +CMSAPI void CMSEXPORT cmsSetLogErrorHandlerTHR(cmsContext ContextID, cmsLogErrorHandlerFunction Fn); + +// Conversions -------------------------------------------------------------------------------------------------------------- + +// Returns pointers to constant structs +CMSAPI const cmsCIEXYZ* CMSEXPORT cmsD50_XYZ(void); +CMSAPI const cmsCIExyY* CMSEXPORT cmsD50_xyY(void); + +// Colorimetric space conversions +CMSAPI void CMSEXPORT cmsXYZ2xyY(cmsCIExyY* Dest, const cmsCIEXYZ* Source); +CMSAPI void CMSEXPORT cmsxyY2XYZ(cmsCIEXYZ* Dest, const cmsCIExyY* Source); +CMSAPI void CMSEXPORT cmsXYZ2Lab(const cmsCIEXYZ* WhitePoint, cmsCIELab* Lab, const cmsCIEXYZ* xyz); +CMSAPI void CMSEXPORT cmsLab2XYZ(const cmsCIEXYZ* WhitePoint, cmsCIEXYZ* xyz, const cmsCIELab* Lab); +CMSAPI void CMSEXPORT cmsLab2LCh(cmsCIELCh*LCh, const cmsCIELab* Lab); +CMSAPI void CMSEXPORT cmsLCh2Lab(cmsCIELab* Lab, const cmsCIELCh* LCh); + +// Encoding /Decoding on PCS +CMSAPI void CMSEXPORT cmsLabEncoded2Float(cmsCIELab* Lab, const cmsUInt16Number wLab[3]); +CMSAPI void CMSEXPORT cmsLabEncoded2FloatV2(cmsCIELab* Lab, const cmsUInt16Number wLab[3]); +CMSAPI void CMSEXPORT cmsFloat2LabEncoded(cmsUInt16Number wLab[3], const cmsCIELab* Lab); +CMSAPI void CMSEXPORT cmsFloat2LabEncodedV2(cmsUInt16Number wLab[3], const cmsCIELab* Lab); +CMSAPI void CMSEXPORT cmsXYZEncoded2Float(cmsCIEXYZ* fxyz, const cmsUInt16Number XYZ[3]); +CMSAPI void CMSEXPORT cmsFloat2XYZEncoded(cmsUInt16Number XYZ[3], const cmsCIEXYZ* fXYZ); + +// DeltaE metrics +CMSAPI cmsFloat64Number CMSEXPORT cmsDeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2); +CMSAPI cmsFloat64Number CMSEXPORT cmsCIE94DeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2); +CMSAPI cmsFloat64Number CMSEXPORT cmsBFDdeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2); +CMSAPI cmsFloat64Number CMSEXPORT cmsCMCdeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2, cmsFloat64Number l, cmsFloat64Number c); +CMSAPI cmsFloat64Number CMSEXPORT cmsCIE2000DeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2, cmsFloat64Number Kl, cmsFloat64Number Kc, cmsFloat64Number Kh); + +// Temperature <-> Chromaticity (Black body) +CMSAPI cmsBool CMSEXPORT cmsWhitePointFromTemp(cmsCIExyY* WhitePoint, cmsFloat64Number TempK); +CMSAPI cmsBool CMSEXPORT cmsTempFromWhitePoint(cmsFloat64Number* TempK, const cmsCIExyY* WhitePoint); + +// Chromatic adaptation +CMSAPI cmsBool CMSEXPORT cmsAdaptToIlluminant(cmsCIEXYZ* Result, const cmsCIEXYZ* SourceWhitePt, + const cmsCIEXYZ* Illuminant, + const cmsCIEXYZ* Value); + +// CIECAM02 --------------------------------------------------------------------------------------------------- + +// Viewing conditions. Please note those are CAM model viewing conditions, and not the ICC tag viewing +// conditions, which I'm naming cmsICCViewingConditions to make differences evident. Unfortunately, the tag +// cannot deal with surround La, Yb and D value so is basically useless to store CAM02 viewing conditions. + + +#define AVG_SURROUND 1 +#define DIM_SURROUND 2 +#define DARK_SURROUND 3 +#define CUTSHEET_SURROUND 4 + +#define D_CALCULATE (-1) + +typedef struct { + cmsCIEXYZ whitePoint; + cmsFloat64Number Yb; + cmsFloat64Number La; + int surround; + cmsFloat64Number D_value; + + } cmsViewingConditions; + +CMSAPI cmsHANDLE CMSEXPORT cmsCIECAM02Init(cmsContext ContextID, const cmsViewingConditions* pVC); +CMSAPI void CMSEXPORT cmsCIECAM02Done(cmsHANDLE hModel); +CMSAPI void CMSEXPORT cmsCIECAM02Forward(cmsHANDLE hModel, const cmsCIEXYZ* pIn, cmsJCh* pOut); +CMSAPI void CMSEXPORT cmsCIECAM02Reverse(cmsHANDLE hModel, const cmsJCh* pIn, cmsCIEXYZ* pOut); + + +// Tone curves ----------------------------------------------------------------------------------------- + +// This describes a curve segment. For a table of supported types, see the manual. User can increase the number of +// available types by using a proper plug-in. Parametric segments allow 10 parameters at most + +typedef struct { + cmsFloat32Number x0, x1; // Domain; for x0 < x <= x1 + cmsInt32Number Type; // Parametric type, Type == 0 means sampled segment. Negative values are reserved + cmsFloat64Number Params[10]; // Parameters if Type != 0 + cmsUInt32Number nGridPoints; // Number of grid points if Type == 0 + cmsFloat32Number* SampledPoints; // Points to an array of floats if Type == 0 + +} cmsCurveSegment; + +// The internal representation is none of your business. +typedef struct _cms_curve_struct cmsToneCurve; + +CMSAPI cmsToneCurve* CMSEXPORT cmsBuildSegmentedToneCurve(cmsContext ContextID, cmsInt32Number nSegments, const cmsCurveSegment Segments[]); +CMSAPI cmsToneCurve* CMSEXPORT cmsBuildParametricToneCurve(cmsContext ContextID, cmsInt32Number Type, const cmsFloat64Number Params[]); +CMSAPI cmsToneCurve* CMSEXPORT cmsBuildGamma(cmsContext ContextID, cmsFloat64Number Gamma); +CMSAPI cmsToneCurve* CMSEXPORT cmsBuildTabulatedToneCurve16(cmsContext ContextID, cmsInt32Number nEntries, const cmsUInt16Number values[]); +CMSAPI cmsToneCurve* CMSEXPORT cmsBuildTabulatedToneCurveFloat(cmsContext ContextID, cmsUInt32Number nEntries, const cmsFloat32Number values[]); +CMSAPI void CMSEXPORT cmsFreeToneCurve(cmsToneCurve* Curve); +CMSAPI void CMSEXPORT cmsFreeToneCurveTriple(cmsToneCurve* Curve[3]); +CMSAPI cmsToneCurve* CMSEXPORT cmsDupToneCurve(const cmsToneCurve* Src); +CMSAPI cmsToneCurve* CMSEXPORT cmsReverseToneCurve(const cmsToneCurve* InGamma); +CMSAPI cmsToneCurve* CMSEXPORT cmsReverseToneCurveEx(cmsInt32Number nResultSamples, const cmsToneCurve* InGamma); +CMSAPI cmsToneCurve* CMSEXPORT cmsJoinToneCurve(cmsContext ContextID, const cmsToneCurve* X, const cmsToneCurve* Y, cmsUInt32Number nPoints); +CMSAPI cmsBool CMSEXPORT cmsSmoothToneCurve(cmsToneCurve* Tab, cmsFloat64Number lambda); +CMSAPI cmsFloat32Number CMSEXPORT cmsEvalToneCurveFloat(const cmsToneCurve* Curve, cmsFloat32Number v); +CMSAPI cmsUInt16Number CMSEXPORT cmsEvalToneCurve16(const cmsToneCurve* Curve, cmsUInt16Number v); +CMSAPI cmsBool CMSEXPORT cmsIsToneCurveMultisegment(const cmsToneCurve* InGamma); +CMSAPI cmsBool CMSEXPORT cmsIsToneCurveLinear(const cmsToneCurve* Curve); +CMSAPI cmsBool CMSEXPORT cmsIsToneCurveMonotonic(const cmsToneCurve* t); +CMSAPI cmsBool CMSEXPORT cmsIsToneCurveDescending(const cmsToneCurve* t); +CMSAPI cmsInt32Number CMSEXPORT cmsGetToneCurveParametricType(const cmsToneCurve* t); +CMSAPI cmsFloat64Number CMSEXPORT cmsEstimateGamma(const cmsToneCurve* t, cmsFloat64Number Precision); + +// Tone curve tabular estimation +CMSAPI cmsUInt32Number CMSEXPORT cmsGetToneCurveEstimatedTableEntries(const cmsToneCurve* t); +CMSAPI const cmsUInt16Number* CMSEXPORT cmsGetToneCurveEstimatedTable(const cmsToneCurve* t); + + +// Implements pipelines of multi-processing elements ------------------------------------------------------------- + +// Nothing to see here, move along +typedef struct _cmsPipeline_struct cmsPipeline; +typedef struct _cmsStage_struct cmsStage; + +// Those are hi-level pipelines +CMSAPI cmsPipeline* CMSEXPORT cmsPipelineAlloc(cmsContext ContextID, cmsUInt32Number InputChannels, cmsUInt32Number OutputChannels); +CMSAPI void CMSEXPORT cmsPipelineFree(cmsPipeline* lut); +CMSAPI cmsPipeline* CMSEXPORT cmsPipelineDup(const cmsPipeline* Orig); + +CMSAPI cmsContext CMSEXPORT cmsGetPipelineContextID(const cmsPipeline* lut); +CMSAPI cmsUInt32Number CMSEXPORT cmsPipelineInputChannels(const cmsPipeline* lut); +CMSAPI cmsUInt32Number CMSEXPORT cmsPipelineOutputChannels(const cmsPipeline* lut); + +CMSAPI cmsUInt32Number CMSEXPORT cmsPipelineStageCount(const cmsPipeline* lut); +CMSAPI cmsStage* CMSEXPORT cmsPipelineGetPtrToFirstStage(const cmsPipeline* lut); +CMSAPI cmsStage* CMSEXPORT cmsPipelineGetPtrToLastStage(const cmsPipeline* lut); + +CMSAPI void CMSEXPORT cmsPipelineEval16(const cmsUInt16Number In[], cmsUInt16Number Out[], const cmsPipeline* lut); +CMSAPI void CMSEXPORT cmsPipelineEvalFloat(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsPipeline* lut); +CMSAPI cmsBool CMSEXPORT cmsPipelineEvalReverseFloat(cmsFloat32Number Target[], cmsFloat32Number Result[], cmsFloat32Number Hint[], const cmsPipeline* lut); +CMSAPI cmsBool CMSEXPORT cmsPipelineCat(cmsPipeline* l1, const cmsPipeline* l2); +CMSAPI cmsBool CMSEXPORT cmsPipelineSetSaveAs8bitsFlag(cmsPipeline* lut, cmsBool On); + +// Where to place/locate the stages in the pipeline chain +typedef enum { cmsAT_BEGIN, cmsAT_END } cmsStageLoc; + +CMSAPI int CMSEXPORT cmsPipelineInsertStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage* mpe); +CMSAPI void CMSEXPORT cmsPipelineUnlinkStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage** mpe); + +// This function is quite useful to analyze the structure of a Pipeline and retrieve the Stage elements +// that conform the Pipeline. It should be called with the Pipeline, the number of expected elements and +// then a list of expected types followed with a list of double pointers to Stage elements. If +// the function founds a match with current pipeline, it fills the pointers and returns TRUE +// if not, returns FALSE without touching anything. +CMSAPI cmsBool CMSEXPORT cmsPipelineCheckAndRetreiveStages(const cmsPipeline* Lut, cmsUInt32Number n, ...); + +// Matrix has double precision and CLUT has only float precision. That is because an ICC profile can encode +// matrices with far more precision that CLUTS +CMSAPI cmsStage* CMSEXPORT cmsStageAllocIdentity(cmsContext ContextID, cmsUInt32Number nChannels); +CMSAPI cmsStage* CMSEXPORT cmsStageAllocToneCurves(cmsContext ContextID, cmsUInt32Number nChannels, cmsToneCurve* const Curves[]); +CMSAPI cmsStage* CMSEXPORT cmsStageAllocMatrix(cmsContext ContextID, cmsUInt32Number Rows, cmsUInt32Number Cols, const cmsFloat64Number* Matrix, const cmsFloat64Number* Offset); + +CMSAPI cmsStage* CMSEXPORT cmsStageAllocCLut16bit(cmsContext ContextID, cmsUInt32Number nGridPoints, cmsUInt32Number inputChan, cmsUInt32Number outputChan, const cmsUInt16Number* Table); +CMSAPI cmsStage* CMSEXPORT cmsStageAllocCLutFloat(cmsContext ContextID, cmsUInt32Number nGridPoints, cmsUInt32Number inputChan, cmsUInt32Number outputChan, const cmsFloat32Number* Table); + +CMSAPI cmsStage* CMSEXPORT cmsStageAllocCLut16bitGranular(cmsContext ContextID, const cmsUInt32Number clutPoints[], cmsUInt32Number inputChan, cmsUInt32Number outputChan, const cmsUInt16Number* Table); +CMSAPI cmsStage* CMSEXPORT cmsStageAllocCLutFloatGranular(cmsContext ContextID, const cmsUInt32Number clutPoints[], cmsUInt32Number inputChan, cmsUInt32Number outputChan, const cmsFloat32Number* Table); + +CMSAPI cmsStage* CMSEXPORT cmsStageDup(cmsStage* mpe); +CMSAPI void CMSEXPORT cmsStageFree(cmsStage* mpe); +CMSAPI cmsStage* CMSEXPORT cmsStageNext(const cmsStage* mpe); + +CMSAPI cmsUInt32Number CMSEXPORT cmsStageInputChannels(const cmsStage* mpe); +CMSAPI cmsUInt32Number CMSEXPORT cmsStageOutputChannels(const cmsStage* mpe); +CMSAPI cmsStageSignature CMSEXPORT cmsStageType(const cmsStage* mpe); +CMSAPI void* CMSEXPORT cmsStageData(const cmsStage* mpe); + +// Sampling +typedef cmsInt32Number (* cmsSAMPLER16) (register const cmsUInt16Number In[], + register cmsUInt16Number Out[], + register void * Cargo); + +typedef cmsInt32Number (* cmsSAMPLERFLOAT)(register const cmsFloat32Number In[], + register cmsFloat32Number Out[], + register void * Cargo); + +// Use this flag to prevent changes being written to destination +#define SAMPLER_INSPECT 0x01000000 + +// For CLUT only +CMSAPI cmsBool CMSEXPORT cmsStageSampleCLut16bit(cmsStage* mpe, cmsSAMPLER16 Sampler, void* Cargo, cmsUInt32Number dwFlags); +CMSAPI cmsBool CMSEXPORT cmsStageSampleCLutFloat(cmsStage* mpe, cmsSAMPLERFLOAT Sampler, void* Cargo, cmsUInt32Number dwFlags); + +// Slicers +CMSAPI cmsBool CMSEXPORT cmsSliceSpace16(cmsUInt32Number nInputs, const cmsUInt32Number clutPoints[], + cmsSAMPLER16 Sampler, void * Cargo); + +CMSAPI cmsBool CMSEXPORT cmsSliceSpaceFloat(cmsUInt32Number nInputs, const cmsUInt32Number clutPoints[], + cmsSAMPLERFLOAT Sampler, void * Cargo); + +// Multilocalized Unicode management --------------------------------------------------------------------------------------- + +typedef struct _cms_MLU_struct cmsMLU; + +#define cmsNoLanguage "\0\0" +#define cmsNoCountry "\0\0" + +CMSAPI cmsMLU* CMSEXPORT cmsMLUalloc(cmsContext ContextID, cmsUInt32Number nItems); +CMSAPI void CMSEXPORT cmsMLUfree(cmsMLU* mlu); +CMSAPI cmsMLU* CMSEXPORT cmsMLUdup(const cmsMLU* mlu); + +CMSAPI cmsBool CMSEXPORT cmsMLUsetASCII(cmsMLU* mlu, + const char LanguageCode[3], const char CountryCode[3], + const char* ASCIIString); +CMSAPI cmsBool CMSEXPORT cmsMLUsetWide(cmsMLU* mlu, + const char LanguageCode[3], const char CountryCode[3], + const wchar_t* WideString); + +CMSAPI cmsUInt32Number CMSEXPORT cmsMLUgetASCII(const cmsMLU* mlu, + const char LanguageCode[3], const char CountryCode[3], + char* Buffer, cmsUInt32Number BufferSize); + +CMSAPI cmsUInt32Number CMSEXPORT cmsMLUgetWide(const cmsMLU* mlu, + const char LanguageCode[3], const char CountryCode[3], + wchar_t* Buffer, cmsUInt32Number BufferSize); + +CMSAPI cmsBool CMSEXPORT cmsMLUgetTranslation(const cmsMLU* mlu, + const char LanguageCode[3], const char CountryCode[3], + char ObtainedLanguage[3], char ObtainedCountry[3]); + +CMSAPI cmsUInt32Number CMSEXPORT cmsMLUtranslationsCount(const cmsMLU* mlu); + +CMSAPI cmsBool CMSEXPORT cmsMLUtranslationsCodes(const cmsMLU* mlu, + cmsUInt32Number idx, + char LanguageCode[3], + char CountryCode[3]); + +// Undercolorremoval & black generation ------------------------------------------------------------------------------------- + +typedef struct { + cmsToneCurve* Ucr; + cmsToneCurve* Bg; + cmsMLU* Desc; + +} cmsUcrBg; + +// Screening ---------------------------------------------------------------------------------------------------------------- + +#define cmsPRINTER_DEFAULT_SCREENS 0x0001 +#define cmsFREQUENCE_UNITS_LINES_CM 0x0000 +#define cmsFREQUENCE_UNITS_LINES_INCH 0x0002 + +#define cmsSPOT_UNKNOWN 0 +#define cmsSPOT_PRINTER_DEFAULT 1 +#define cmsSPOT_ROUND 2 +#define cmsSPOT_DIAMOND 3 +#define cmsSPOT_ELLIPSE 4 +#define cmsSPOT_LINE 5 +#define cmsSPOT_SQUARE 6 +#define cmsSPOT_CROSS 7 + +typedef struct { + cmsFloat64Number Frequency; + cmsFloat64Number ScreenAngle; + cmsUInt32Number SpotShape; + +} cmsScreeningChannel; + +typedef struct { + cmsUInt32Number Flag; + cmsUInt32Number nChannels; + cmsScreeningChannel Channels[cmsMAXCHANNELS]; + +} cmsScreening; + + +// Named color ----------------------------------------------------------------------------------------------------------------- + +typedef struct _cms_NAMEDCOLORLIST_struct cmsNAMEDCOLORLIST; + +CMSAPI cmsNAMEDCOLORLIST* CMSEXPORT cmsAllocNamedColorList(cmsContext ContextID, + cmsUInt32Number n, + cmsUInt32Number ColorantCount, + const char* Prefix, const char* Suffix); + +CMSAPI void CMSEXPORT cmsFreeNamedColorList(cmsNAMEDCOLORLIST* v); +CMSAPI cmsNAMEDCOLORLIST* CMSEXPORT cmsDupNamedColorList(const cmsNAMEDCOLORLIST* v); +CMSAPI cmsBool CMSEXPORT cmsAppendNamedColor(cmsNAMEDCOLORLIST* v, const char* Name, + cmsUInt16Number PCS[3], + cmsUInt16Number Colorant[cmsMAXCHANNELS]); + +CMSAPI cmsUInt32Number CMSEXPORT cmsNamedColorCount(const cmsNAMEDCOLORLIST* v); +CMSAPI cmsInt32Number CMSEXPORT cmsNamedColorIndex(const cmsNAMEDCOLORLIST* v, const char* Name); + +CMSAPI cmsBool CMSEXPORT cmsNamedColorInfo(const cmsNAMEDCOLORLIST* NamedColorList, cmsUInt32Number nColor, + char* Name, + char* Prefix, + char* Suffix, + cmsUInt16Number* PCS, + cmsUInt16Number* Colorant); + +// Retrieve named color list from transform +CMSAPI cmsNAMEDCOLORLIST* CMSEXPORT cmsGetNamedColorList(cmsHTRANSFORM xform); + +// Profile sequence ----------------------------------------------------------------------------------------------------- + +// Profile sequence descriptor. Some fields come from profile sequence descriptor tag, others +// come from Profile Sequence Identifier Tag +typedef struct { + + cmsSignature deviceMfg; + cmsSignature deviceModel; + cmsUInt64Number attributes; + cmsTechnologySignature technology; + cmsProfileID ProfileID; + cmsMLU* Manufacturer; + cmsMLU* Model; + cmsMLU* Description; + +} cmsPSEQDESC; + +typedef struct { + + cmsUInt32Number n; + cmsContext ContextID; + cmsPSEQDESC* seq; + +} cmsSEQ; + +CMSAPI cmsSEQ* CMSEXPORT cmsAllocProfileSequenceDescription(cmsContext ContextID, cmsUInt32Number n); +CMSAPI cmsSEQ* CMSEXPORT cmsDupProfileSequenceDescription(const cmsSEQ* pseq); +CMSAPI void CMSEXPORT cmsFreeProfileSequenceDescription(cmsSEQ* pseq); + +// Dictionaries -------------------------------------------------------------------------------------------------------- + +typedef struct _cmsDICTentry_struct { + + struct _cmsDICTentry_struct* Next; + + cmsMLU *DisplayName; + cmsMLU *DisplayValue; + wchar_t* Name; + wchar_t* Value; + +} cmsDICTentry; + +CMSAPI cmsHANDLE CMSEXPORT cmsDictAlloc(cmsContext ContextID); +CMSAPI void CMSEXPORT cmsDictFree(cmsHANDLE hDict); +CMSAPI cmsHANDLE CMSEXPORT cmsDictDup(cmsHANDLE hDict); + +CMSAPI cmsBool CMSEXPORT cmsDictAddEntry(cmsHANDLE hDict, const wchar_t* Name, const wchar_t* Value, const cmsMLU *DisplayName, const cmsMLU *DisplayValue); +CMSAPI const cmsDICTentry* CMSEXPORT cmsDictGetEntryList(cmsHANDLE hDict); +CMSAPI const cmsDICTentry* CMSEXPORT cmsDictNextEntry(const cmsDICTentry* e); + +// Access to Profile data ---------------------------------------------------------------------------------------------- +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateProfilePlaceholder(cmsContext ContextID); + +CMSAPI cmsContext CMSEXPORT cmsGetProfileContextID(cmsHPROFILE hProfile); +CMSAPI cmsInt32Number CMSEXPORT cmsGetTagCount(cmsHPROFILE hProfile); +CMSAPI cmsTagSignature CMSEXPORT cmsGetTagSignature(cmsHPROFILE hProfile, cmsUInt32Number n); +CMSAPI cmsBool CMSEXPORT cmsIsTag(cmsHPROFILE hProfile, cmsTagSignature sig); + +// Read and write pre-formatted data +CMSAPI void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig); +CMSAPI cmsBool CMSEXPORT cmsWriteTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data); +CMSAPI cmsBool CMSEXPORT cmsLinkTag(cmsHPROFILE hProfile, cmsTagSignature sig, cmsTagSignature dest); +CMSAPI cmsTagSignature CMSEXPORT cmsTagLinkedTo(cmsHPROFILE hProfile, cmsTagSignature sig); + +// Read and write raw data +CMSAPI cmsInt32Number CMSEXPORT cmsReadRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, void* Buffer, cmsUInt32Number BufferSize); +CMSAPI cmsBool CMSEXPORT cmsWriteRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data, cmsUInt32Number Size); + +// Access header data +#define cmsEmbeddedProfileFalse 0x00000000 +#define cmsEmbeddedProfileTrue 0x00000001 +#define cmsUseAnywhere 0x00000000 +#define cmsUseWithEmbeddedDataOnly 0x00000002 + +CMSAPI cmsUInt32Number CMSEXPORT cmsGetHeaderFlags(cmsHPROFILE hProfile); +CMSAPI void CMSEXPORT cmsGetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number* Flags); +CMSAPI void CMSEXPORT cmsGetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID); +CMSAPI cmsBool CMSEXPORT cmsGetHeaderCreationDateTime(cmsHPROFILE hProfile, struct tm *Dest); +CMSAPI cmsUInt32Number CMSEXPORT cmsGetHeaderRenderingIntent(cmsHPROFILE hProfile); + +CMSAPI void CMSEXPORT cmsSetHeaderFlags(cmsHPROFILE hProfile, cmsUInt32Number Flags); +CMSAPI cmsUInt32Number CMSEXPORT cmsGetHeaderManufacturer(cmsHPROFILE hProfile); +CMSAPI void CMSEXPORT cmsSetHeaderManufacturer(cmsHPROFILE hProfile, cmsUInt32Number manufacturer); +CMSAPI cmsUInt32Number CMSEXPORT cmsGetHeaderCreator(cmsHPROFILE hProfile); +CMSAPI cmsUInt32Number CMSEXPORT cmsGetHeaderModel(cmsHPROFILE hProfile); +CMSAPI void CMSEXPORT cmsSetHeaderModel(cmsHPROFILE hProfile, cmsUInt32Number model); +CMSAPI void CMSEXPORT cmsSetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number Flags); +CMSAPI void CMSEXPORT cmsSetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID); +CMSAPI void CMSEXPORT cmsSetHeaderRenderingIntent(cmsHPROFILE hProfile, cmsUInt32Number RenderingIntent); + +CMSAPI cmsColorSpaceSignature + CMSEXPORT cmsGetPCS(cmsHPROFILE hProfile); +CMSAPI void CMSEXPORT cmsSetPCS(cmsHPROFILE hProfile, cmsColorSpaceSignature pcs); +CMSAPI cmsColorSpaceSignature + CMSEXPORT cmsGetColorSpace(cmsHPROFILE hProfile); +CMSAPI void CMSEXPORT cmsSetColorSpace(cmsHPROFILE hProfile, cmsColorSpaceSignature sig); +CMSAPI cmsProfileClassSignature + CMSEXPORT cmsGetDeviceClass(cmsHPROFILE hProfile); +CMSAPI void CMSEXPORT cmsSetDeviceClass(cmsHPROFILE hProfile, cmsProfileClassSignature sig); +CMSAPI void CMSEXPORT cmsSetProfileVersion(cmsHPROFILE hProfile, cmsFloat64Number Version); +CMSAPI cmsFloat64Number CMSEXPORT cmsGetProfileVersion(cmsHPROFILE hProfile); + +CMSAPI cmsUInt32Number CMSEXPORT cmsGetEncodedICCversion(cmsHPROFILE hProfile); +CMSAPI void CMSEXPORT cmsSetEncodedICCversion(cmsHPROFILE hProfile, cmsUInt32Number Version); + +// How profiles may be used +#define LCMS_USED_AS_INPUT 0 +#define LCMS_USED_AS_OUTPUT 1 +#define LCMS_USED_AS_PROOF 2 + +CMSAPI cmsBool CMSEXPORT cmsIsIntentSupported(cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number UsedDirection); +CMSAPI cmsBool CMSEXPORT cmsIsMatrixShaper(cmsHPROFILE hProfile); +CMSAPI cmsBool CMSEXPORT cmsIsCLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number UsedDirection); + +// Translate form/to our notation to ICC +CMSAPI cmsColorSpaceSignature CMSEXPORT _cmsICCcolorSpace(int OurNotation); +CMSAPI int CMSEXPORT _cmsLCMScolorSpace(cmsColorSpaceSignature ProfileSpace); + +CMSAPI cmsUInt32Number CMSEXPORT cmsChannelsOf(cmsColorSpaceSignature ColorSpace); + +// Build a suitable formatter for the colorspace of this profile +CMSAPI cmsUInt32Number CMSEXPORT cmsFormatterForColorspaceOfProfile(cmsHPROFILE hProfile, cmsUInt32Number nBytes, cmsBool lIsFloat); +CMSAPI cmsUInt32Number CMSEXPORT cmsFormatterForPCSOfProfile(cmsHPROFILE hProfile, cmsUInt32Number nBytes, cmsBool lIsFloat); + + +// Localized info +typedef enum { + cmsInfoDescription = 0, + cmsInfoManufacturer = 1, + cmsInfoModel = 2, + cmsInfoCopyright = 3 +} cmsInfoType; + +CMSAPI cmsUInt32Number CMSEXPORT cmsGetProfileInfo(cmsHPROFILE hProfile, cmsInfoType Info, + const char LanguageCode[3], const char CountryCode[3], + wchar_t* Buffer, cmsUInt32Number BufferSize); + +CMSAPI cmsUInt32Number CMSEXPORT cmsGetProfileInfoASCII(cmsHPROFILE hProfile, cmsInfoType Info, + const char LanguageCode[3], const char CountryCode[3], + char* Buffer, cmsUInt32Number BufferSize); + +// IO handlers ---------------------------------------------------------------------------------------------------------- + +typedef struct _cms_io_handler cmsIOHANDLER; + +CMSAPI cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromFile(cmsContext ContextID, const char* FileName, const char* AccessMode); +CMSAPI cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromStream(cmsContext ContextID, FILE* Stream); +CMSAPI cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromMem(cmsContext ContextID, void *Buffer, cmsUInt32Number size, const char* AccessMode); +CMSAPI cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromNULL(cmsContext ContextID); +CMSAPI cmsBool CMSEXPORT cmsCloseIOhandler(cmsIOHANDLER* io); + +// MD5 message digest -------------------------------------------------------------------------------------------------- + +CMSAPI cmsBool CMSEXPORT cmsMD5computeID(cmsHPROFILE hProfile); +CMSAPI cmsBool CMSEXPORT cmsMD5computeIDExt(const void* buf, unsigned long size, unsigned char ProfileID[16]); + +// Profile high level funtions ------------------------------------------------------------------------------------------ + +CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromFile(const char *ICCProfile, const char *sAccess); +CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromFileTHR(cmsContext ContextID, const char *ICCProfile, const char *sAccess); +CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromStream(FILE* ICCProfile, const char* sAccess); +CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromStreamTHR(cmsContext ContextID, FILE* ICCProfile, const char* sAccess); +CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromMem(const void * MemPtr, cmsUInt32Number dwSize); +CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromMemTHR(cmsContext ContextID, const void * MemPtr, cmsUInt32Number dwSize); +CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromIOhandlerTHR(cmsContext ContextID, cmsIOHANDLER* io); +CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromIOhandler2THR(cmsContext ContextID, cmsIOHANDLER* io, cmsBool write); +CMSAPI cmsBool CMSEXPORT cmsCloseProfile(cmsHPROFILE hProfile); + +CMSAPI cmsBool CMSEXPORT cmsSaveProfileToFile(cmsHPROFILE hProfile, const char* FileName); +CMSAPI cmsBool CMSEXPORT cmsSaveProfileToStream(cmsHPROFILE hProfile, FILE* Stream); +CMSAPI cmsBool CMSEXPORT cmsSaveProfileToMem(cmsHPROFILE hProfile, void *MemPtr, cmsUInt32Number* BytesNeeded); +CMSAPI cmsUInt32Number CMSEXPORT cmsSaveProfileToIOhandler(cmsHPROFILE hProfile, cmsIOHANDLER* io); + +// Predefined virtual profiles ------------------------------------------------------------------------------------------ + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateRGBProfileTHR(cmsContext ContextID, + const cmsCIExyY* WhitePoint, + const cmsCIExyYTRIPLE* Primaries, + cmsToneCurve* const TransferFunction[3]); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateRGBProfile(const cmsCIExyY* WhitePoint, + const cmsCIExyYTRIPLE* Primaries, + cmsToneCurve* const TransferFunction[3]); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateGrayProfileTHR(cmsContext ContextID, + const cmsCIExyY* WhitePoint, + const cmsToneCurve* TransferFunction); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateGrayProfile(const cmsCIExyY* WhitePoint, + const cmsToneCurve* TransferFunction); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLinkTHR(cmsContext ContextID, + cmsColorSpaceSignature ColorSpace, + cmsToneCurve* const TransferFunctions[]); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLink(cmsColorSpaceSignature ColorSpace, + cmsToneCurve* const TransferFunctions[]); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLinkTHR(cmsContext ContextID, + cmsColorSpaceSignature ColorSpace, cmsFloat64Number Limit); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLink(cmsColorSpaceSignature ColorSpace, cmsFloat64Number Limit); + + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateLab2ProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint); +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateLab2Profile(const cmsCIExyY* WhitePoint); +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateLab4ProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint); +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateLab4Profile(const cmsCIExyY* WhitePoint); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateXYZProfileTHR(cmsContext ContextID); +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateXYZProfile(void); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfileTHR(cmsContext ContextID); +CMSAPI cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfile(void); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfileTHR(cmsContext ContextID, + int nLUTPoints, + cmsFloat64Number Bright, + cmsFloat64Number Contrast, + cmsFloat64Number Hue, + cmsFloat64Number Saturation, + int TempSrc, + int TempDest); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfile(int nLUTPoints, + cmsFloat64Number Bright, + cmsFloat64Number Contrast, + cmsFloat64Number Hue, + cmsFloat64Number Saturation, + int TempSrc, + int TempDest); + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateNULLProfileTHR(cmsContext ContextID); +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateNULLProfile(void); + +// Converts a transform to a devicelink profile +CMSAPI cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat64Number Version, cmsUInt32Number dwFlags); + +// Intents ---------------------------------------------------------------------------------------------- + +// ICC Intents +#define INTENT_PERCEPTUAL 0 +#define INTENT_RELATIVE_COLORIMETRIC 1 +#define INTENT_SATURATION 2 +#define INTENT_ABSOLUTE_COLORIMETRIC 3 + +// Non-ICC intents +#define INTENT_PRESERVE_K_ONLY_PERCEPTUAL 10 +#define INTENT_PRESERVE_K_ONLY_RELATIVE_COLORIMETRIC 11 +#define INTENT_PRESERVE_K_ONLY_SATURATION 12 +#define INTENT_PRESERVE_K_PLANE_PERCEPTUAL 13 +#define INTENT_PRESERVE_K_PLANE_RELATIVE_COLORIMETRIC 14 +#define INTENT_PRESERVE_K_PLANE_SATURATION 15 + +// 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 + +#define cmsFLAGS_NOCACHE 0x0040 // Inhibit 1-pixel cache +#define cmsFLAGS_NOOPTIMIZE 0x0100 // Inhibit optimizations +#define cmsFLAGS_NULLTRANSFORM 0x0200 // Don't transform anyway + +// Proofing flags +#define cmsFLAGS_GAMUTCHECK 0x1000 // Out of Gamut alarm +#define cmsFLAGS_SOFTPROOFING 0x4000 // Do softproofing + +// Misc +#define cmsFLAGS_BLACKPOINTCOMPENSATION 0x2000 +#define cmsFLAGS_NOWHITEONWHITEFIXUP 0x0004 // Don't fix scum dot +#define cmsFLAGS_HIGHRESPRECALC 0x0400 // Use more memory to give better accurancy +#define cmsFLAGS_LOWRESPRECALC 0x0800 // Use less memory to minimize resouces + +// For devicelink creation +#define cmsFLAGS_8BITS_DEVICELINK 0x0008 // Create 8 bits devicelinks +#define cmsFLAGS_GUESSDEVICECLASS 0x0020 // Guess device class (for transform2devicelink) +#define cmsFLAGS_KEEP_SEQUENCE 0x0080 // Keep profile sequence for devicelink creation + +// Specific to a particular optimizations +#define cmsFLAGS_FORCE_CLUT 0x0002 // Force CLUT optimization +#define cmsFLAGS_CLUT_POST_LINEARIZATION 0x0001 // create postlinearization tables if possible +#define cmsFLAGS_CLUT_PRE_LINEARIZATION 0x0010 // create prelinearization tables if possible + +// Fine-tune control over number of gridpoints +#define cmsFLAGS_GRIDPOINTS(n) (((n) & 0xFF) << 16) + +// CRD special +#define cmsFLAGS_NODEFAULTRESOURCEDEF 0x01000000 + +// Transforms --------------------------------------------------------------------------------------------------- + +CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateTransformTHR(cmsContext ContextID, + cmsHPROFILE Input, + cmsUInt32Number InputFormat, + cmsHPROFILE Output, + cmsUInt32Number OutputFormat, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags); + +CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateTransform(cmsHPROFILE Input, + cmsUInt32Number InputFormat, + cmsHPROFILE Output, + cmsUInt32Number OutputFormat, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags); + +CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransformTHR(cmsContext ContextID, + cmsHPROFILE Input, + cmsUInt32Number InputFormat, + cmsHPROFILE Output, + cmsUInt32Number OutputFormat, + cmsHPROFILE Proofing, + cmsUInt32Number Intent, + cmsUInt32Number ProofingIntent, + cmsUInt32Number dwFlags); + +CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransform(cmsHPROFILE Input, + cmsUInt32Number InputFormat, + cmsHPROFILE Output, + cmsUInt32Number OutputFormat, + cmsHPROFILE Proofing, + cmsUInt32Number Intent, + cmsUInt32Number ProofingIntent, + cmsUInt32Number dwFlags); + +CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransformTHR(cmsContext ContextID, + cmsHPROFILE hProfiles[], + cmsUInt32Number nProfiles, + cmsUInt32Number InputFormat, + cmsUInt32Number OutputFormat, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags); + + +CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles[], + cmsUInt32Number nProfiles, + cmsUInt32Number InputFormat, + cmsUInt32Number OutputFormat, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags); + + +CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID, + cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsUInt32Number Intents[], + cmsFloat64Number AdaptationStates[], + cmsHPROFILE hGamutProfile, + cmsUInt32Number nGamutPCSposition, + cmsUInt32Number InputFormat, + cmsUInt32Number OutputFormat, + cmsUInt32Number dwFlags); + +CMSAPI void CMSEXPORT cmsDeleteTransform(cmsHTRANSFORM hTransform); + +CMSAPI void CMSEXPORT cmsDoTransform(cmsHTRANSFORM Transform, + const void * InputBuffer, + void * OutputBuffer, + cmsUInt32Number Size); + +CMSAPI void CMSEXPORT cmsDoTransformStride(cmsHTRANSFORM Transform, + const void * InputBuffer, + void * OutputBuffer, + cmsUInt32Number Size, + cmsUInt32Number Stride); + + +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); + +// Grab the input/output formats +CMSAPI cmsUInt32Number CMSEXPORT cmsGetTransformInputFormat(cmsHTRANSFORM hTransform); +CMSAPI cmsUInt32Number CMSEXPORT cmsGetTransformOutputFormat(cmsHTRANSFORM hTransform); + +// For backwards compatibility +CMSAPI cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform, + cmsUInt32Number InputFormat, + cmsUInt32Number OutputFormat); + + + +// PostScript ColorRenderingDictionary and ColorSpaceArray ---------------------------------------------------- + +typedef enum { cmsPS_RESOURCE_CSA, cmsPS_RESOURCE_CRD } cmsPSResourceType; + +// lcms2 unified method to access postscript color resources +CMSAPI cmsUInt32Number CMSEXPORT cmsGetPostScriptColorResource(cmsContext ContextID, + cmsPSResourceType Type, + cmsHPROFILE hProfile, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags, + cmsIOHANDLER* io); + +CMSAPI cmsUInt32Number CMSEXPORT cmsGetPostScriptCSA(cmsContext ContextID, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags, void* Buffer, cmsUInt32Number dwBufferLen); +CMSAPI cmsUInt32Number CMSEXPORT cmsGetPostScriptCRD(cmsContext ContextID, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags, void* Buffer, cmsUInt32Number dwBufferLen); + + +// IT8.7 / CGATS.17-200x handling ----------------------------------------------------------------------------- + +CMSAPI cmsHANDLE CMSEXPORT cmsIT8Alloc(cmsContext ContextID); +CMSAPI void CMSEXPORT cmsIT8Free(cmsHANDLE hIT8); + +// Tables +CMSAPI cmsUInt32Number CMSEXPORT cmsIT8TableCount(cmsHANDLE hIT8); +CMSAPI cmsInt32Number CMSEXPORT cmsIT8SetTable(cmsHANDLE hIT8, cmsUInt32Number nTable); + +// Persistence +CMSAPI cmsHANDLE CMSEXPORT cmsIT8LoadFromFile(cmsContext ContextID, const char* cFileName); +CMSAPI cmsHANDLE CMSEXPORT cmsIT8LoadFromMem(cmsContext ContextID, void *Ptr, cmsUInt32Number len); +// CMSAPI cmsHANDLE CMSEXPORT cmsIT8LoadFromIOhandler(cmsContext ContextID, cmsIOHANDLER* io); + +CMSAPI cmsBool CMSEXPORT cmsIT8SaveToFile(cmsHANDLE hIT8, const char* cFileName); +CMSAPI cmsBool CMSEXPORT cmsIT8SaveToMem(cmsHANDLE hIT8, void *MemPtr, cmsUInt32Number* BytesNeeded); + +// Properties +CMSAPI const char* CMSEXPORT cmsIT8GetSheetType(cmsHANDLE hIT8); +CMSAPI cmsBool CMSEXPORT cmsIT8SetSheetType(cmsHANDLE hIT8, const char* Type); + +CMSAPI cmsBool CMSEXPORT cmsIT8SetComment(cmsHANDLE hIT8, const char* cComment); + +CMSAPI cmsBool CMSEXPORT cmsIT8SetPropertyStr(cmsHANDLE hIT8, const char* cProp, const char *Str); +CMSAPI cmsBool CMSEXPORT cmsIT8SetPropertyDbl(cmsHANDLE hIT8, const char* cProp, cmsFloat64Number Val); +CMSAPI cmsBool CMSEXPORT cmsIT8SetPropertyHex(cmsHANDLE hIT8, const char* cProp, cmsUInt32Number Val); +CMSAPI cmsBool CMSEXPORT cmsIT8SetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char* SubKey, const char *Buffer); +CMSAPI cmsBool CMSEXPORT cmsIT8SetPropertyUncooked(cmsHANDLE hIT8, const char* Key, const char* Buffer); + + +CMSAPI const char* CMSEXPORT cmsIT8GetProperty(cmsHANDLE hIT8, const char* cProp); +CMSAPI cmsFloat64Number CMSEXPORT cmsIT8GetPropertyDbl(cmsHANDLE hIT8, const char* cProp); +CMSAPI const char* CMSEXPORT cmsIT8GetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char *SubKey); +CMSAPI cmsUInt32Number CMSEXPORT cmsIT8EnumProperties(cmsHANDLE hIT8, char ***PropertyNames); +CMSAPI cmsUInt32Number CMSEXPORT cmsIT8EnumPropertyMulti(cmsHANDLE hIT8, const char* cProp, const char ***SubpropertyNames); + +// Datasets +CMSAPI const char* CMSEXPORT cmsIT8GetDataRowCol(cmsHANDLE hIT8, int row, int col); +CMSAPI cmsFloat64Number CMSEXPORT cmsIT8GetDataRowColDbl(cmsHANDLE hIT8, int row, int col); + +CMSAPI cmsBool CMSEXPORT cmsIT8SetDataRowCol(cmsHANDLE hIT8, int row, int col, + const char* Val); + +CMSAPI cmsBool CMSEXPORT cmsIT8SetDataRowColDbl(cmsHANDLE hIT8, int row, int col, + cmsFloat64Number Val); + +CMSAPI const char* CMSEXPORT cmsIT8GetData(cmsHANDLE hIT8, const char* cPatch, const char* cSample); + + +CMSAPI cmsFloat64Number CMSEXPORT cmsIT8GetDataDbl(cmsHANDLE hIT8, const char* cPatch, const char* cSample); + +CMSAPI cmsBool CMSEXPORT cmsIT8SetData(cmsHANDLE hIT8, const char* cPatch, + const char* cSample, + const char *Val); + +CMSAPI cmsBool CMSEXPORT cmsIT8SetDataDbl(cmsHANDLE hIT8, const char* cPatch, + const char* cSample, + cmsFloat64Number Val); + +CMSAPI int CMSEXPORT cmsIT8FindDataFormat(cmsHANDLE hIT8, const char* cSample); +CMSAPI cmsBool CMSEXPORT cmsIT8SetDataFormat(cmsHANDLE hIT8, int n, const char *Sample); +CMSAPI int CMSEXPORT cmsIT8EnumDataFormat(cmsHANDLE hIT8, char ***SampleNames); + +CMSAPI const char* CMSEXPORT cmsIT8GetPatchName(cmsHANDLE hIT8, int nPatch, char* buffer); +CMSAPI int CMSEXPORT cmsIT8GetPatchByName(cmsHANDLE hIT8, const char *cPatch); + +// The LABEL extension +CMSAPI int CMSEXPORT cmsIT8SetTableByLabel(cmsHANDLE hIT8, const char* cSet, const char* cField, const char* ExpectedType); + +CMSAPI cmsBool CMSEXPORT cmsIT8SetIndexColumn(cmsHANDLE hIT8, const char* cSample); + +// Formatter for double +CMSAPI void CMSEXPORT cmsIT8DefineDblFormat(cmsHANDLE hIT8, const char* Formatter); + +// Gamut boundary description routines ------------------------------------------------------------------------------ + +CMSAPI cmsHANDLE CMSEXPORT cmsGBDAlloc(cmsContext ContextID); +CMSAPI void CMSEXPORT cmsGBDFree(cmsHANDLE hGBD); +CMSAPI cmsBool CMSEXPORT cmsGDBAddPoint(cmsHANDLE hGBD, const cmsCIELab* Lab); +CMSAPI cmsBool CMSEXPORT cmsGDBCompute(cmsHANDLE hGDB, cmsUInt32Number dwFlags); +CMSAPI cmsBool CMSEXPORT cmsGDBCheckPoint(cmsHANDLE hGBD, const cmsCIELab* Lab); + +// Feature detection ---------------------------------------------------------------------------------------------- + +// Estimate the black point +CMSAPI cmsBool CMSEXPORT cmsDetectBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags); +CMSAPI cmsBool CMSEXPORT cmsDetectDestinationBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags); + +// Estimate total area coverage +CMSAPI cmsFloat64Number CMSEXPORT cmsDetectTAC(cmsHPROFILE hProfile); + + +// Poor man's gamut mapping +CMSAPI cmsBool CMSEXPORT cmsDesaturateLab(cmsCIELab* Lab, + double amax, double amin, + double bmax, double bmin); + +#ifndef CMS_USE_CPP_API +# ifdef __cplusplus + } +# endif +#endif + +#define _lcms2_H +#endif diff --git a/third_party/lcms/include/lcms2_plugin.h b/third_party/lcms/include/lcms2_plugin.h new file mode 100644 index 0000000000..0c95d1f73c --- /dev/null +++ b/third_party/lcms/include/lcms2_plugin.h @@ -0,0 +1,637 @@ +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2011 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. +// +//--------------------------------------------------------------------------------- +// +// This is the plug-in header file. Normal LittleCMS clients should not use it. +// It is provided for plug-in writters that may want to access the support +// functions to do low level operations. All plug-in related structures +// are defined here. Including this file forces to include the standard API too. + +#ifndef _lcms_plugin_H + +// Deal with Microsoft's attempt at deprecating C standard runtime functions +#ifdef _MSC_VER +# if (_MSC_VER >= 1400) +# ifndef _CRT_SECURE_NO_DEPRECATE +# define _CRT_SECURE_NO_DEPRECATE +# endif +# ifndef _CRT_SECURE_NO_WARNINGS +# define _CRT_SECURE_NO_WARNINGS +# endif +# endif +#endif + +#ifndef _lcms2_H +#include "lcms2.h" +#endif + +// We need some standard C functions. +#include +#include +#include +#include +#include + + +#ifndef CMS_USE_CPP_API +# ifdef __cplusplus +extern "C" { +# endif +#endif + +// Vector & Matrix operations ----------------------------------------------------------------------- + +// Axis of the matrix/array. No specific meaning at all. +#define VX 0 +#define VY 1 +#define VZ 2 + +// Vectors +typedef struct { + cmsFloat64Number n[3]; + + } cmsVEC3; + +// 3x3 Matrix +typedef struct { + cmsVEC3 v[3]; + + } cmsMAT3; + +CMSAPI void CMSEXPORT _cmsVEC3init(cmsVEC3* r, cmsFloat64Number x, cmsFloat64Number y, cmsFloat64Number z); +CMSAPI void CMSEXPORT _cmsVEC3minus(cmsVEC3* r, const cmsVEC3* a, const cmsVEC3* b); +CMSAPI void CMSEXPORT _cmsVEC3cross(cmsVEC3* r, const cmsVEC3* u, const cmsVEC3* v); +CMSAPI cmsFloat64Number CMSEXPORT _cmsVEC3dot(const cmsVEC3* u, const cmsVEC3* v); +CMSAPI cmsFloat64Number CMSEXPORT _cmsVEC3length(const cmsVEC3* a); +CMSAPI cmsFloat64Number CMSEXPORT _cmsVEC3distance(const cmsVEC3* a, const cmsVEC3* b); + +CMSAPI void CMSEXPORT _cmsMAT3identity(cmsMAT3* a); +CMSAPI cmsBool CMSEXPORT _cmsMAT3isIdentity(const cmsMAT3* a); +CMSAPI void CMSEXPORT _cmsMAT3per(cmsMAT3* r, const cmsMAT3* a, const cmsMAT3* b); +CMSAPI cmsBool CMSEXPORT _cmsMAT3inverse(const cmsMAT3* a, cmsMAT3* b); +CMSAPI cmsBool CMSEXPORT _cmsMAT3solve(cmsVEC3* x, cmsMAT3* a, cmsVEC3* b); +CMSAPI void CMSEXPORT _cmsMAT3eval(cmsVEC3* r, const cmsMAT3* a, const cmsVEC3* v); + + +// Error logging ------------------------------------------------------------------------------------- + +CMSAPI void CMSEXPORT cmsSignalError(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *ErrorText, ...); + +// Memory management ---------------------------------------------------------------------------------- + +CMSAPI void* CMSEXPORT _cmsMalloc(cmsContext ContextID, cmsUInt32Number size); +CMSAPI void* CMSEXPORT _cmsMallocZero(cmsContext ContextID, cmsUInt32Number size); +CMSAPI void* CMSEXPORT _cmsCalloc(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size); +CMSAPI void* CMSEXPORT _cmsRealloc(cmsContext ContextID, void* Ptr, cmsUInt32Number NewSize); +CMSAPI void CMSEXPORT _cmsFree(cmsContext ContextID, void* Ptr); +CMSAPI void* CMSEXPORT _cmsDupMem(cmsContext ContextID, const void* Org, cmsUInt32Number size); + +// I/O handler ---------------------------------------------------------------------------------- + +struct _cms_io_handler { + + void* stream; // Associated stream, which is implemented differently depending on media. + + cmsContext ContextID; + cmsUInt32Number UsedSpace; + cmsUInt32Number ReportedSize; + char PhysicalFile[cmsMAX_PATH]; + + cmsUInt32Number (* Read)(struct _cms_io_handler* iohandler, void *Buffer, + cmsUInt32Number size, + cmsUInt32Number count); + cmsBool (* Seek)(struct _cms_io_handler* iohandler, cmsUInt32Number offset); + cmsBool (* Close)(struct _cms_io_handler* iohandler); + cmsUInt32Number (* Tell)(struct _cms_io_handler* iohandler); + cmsBool (* Write)(struct _cms_io_handler* iohandler, cmsUInt32Number size, + const void* Buffer); +}; + +// Endianess adjust functions +CMSAPI cmsUInt16Number CMSEXPORT _cmsAdjustEndianess16(cmsUInt16Number Word); +CMSAPI cmsUInt32Number CMSEXPORT _cmsAdjustEndianess32(cmsUInt32Number Value); +CMSAPI void CMSEXPORT _cmsAdjustEndianess64(cmsUInt64Number* Result, cmsUInt64Number* QWord); + +// Helper IO functions +CMSAPI cmsBool CMSEXPORT _cmsReadUInt8Number(cmsIOHANDLER* io, cmsUInt8Number* n); +CMSAPI cmsBool CMSEXPORT _cmsReadUInt16Number(cmsIOHANDLER* io, cmsUInt16Number* n); +CMSAPI cmsBool CMSEXPORT _cmsReadUInt32Number(cmsIOHANDLER* io, cmsUInt32Number* n); +CMSAPI cmsBool CMSEXPORT _cmsReadFloat32Number(cmsIOHANDLER* io, cmsFloat32Number* n); +CMSAPI cmsBool CMSEXPORT _cmsReadUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n); +CMSAPI cmsBool CMSEXPORT _cmsRead15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number* n); +CMSAPI cmsBool CMSEXPORT _cmsReadXYZNumber(cmsIOHANDLER* io, cmsCIEXYZ* XYZ); +CMSAPI cmsBool CMSEXPORT _cmsReadUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, cmsUInt16Number* Array); + +CMSAPI cmsBool CMSEXPORT _cmsWriteUInt8Number(cmsIOHANDLER* io, cmsUInt8Number n); +CMSAPI cmsBool CMSEXPORT _cmsWriteUInt16Number(cmsIOHANDLER* io, cmsUInt16Number n); +CMSAPI cmsBool CMSEXPORT _cmsWriteUInt32Number(cmsIOHANDLER* io, cmsUInt32Number n); +CMSAPI cmsBool CMSEXPORT _cmsWriteFloat32Number(cmsIOHANDLER* io, cmsFloat32Number n); +CMSAPI cmsBool CMSEXPORT _cmsWriteUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n); +CMSAPI cmsBool CMSEXPORT _cmsWrite15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number n); +CMSAPI cmsBool CMSEXPORT _cmsWriteXYZNumber(cmsIOHANDLER* io, const cmsCIEXYZ* XYZ); +CMSAPI cmsBool CMSEXPORT _cmsWriteUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, const cmsUInt16Number* Array); + +// ICC base tag +typedef struct { + cmsTagTypeSignature sig; + cmsInt8Number reserved[4]; + +} _cmsTagBase; + +// Type base helper functions +CMSAPI cmsTagTypeSignature CMSEXPORT _cmsReadTypeBase(cmsIOHANDLER* io); +CMSAPI cmsBool CMSEXPORT _cmsWriteTypeBase(cmsIOHANDLER* io, cmsTagTypeSignature sig); + +// Alignment functions +CMSAPI cmsBool CMSEXPORT _cmsReadAlignment(cmsIOHANDLER* io); +CMSAPI cmsBool CMSEXPORT _cmsWriteAlignment(cmsIOHANDLER* io); + +// To deal with text streams. 2K at most +CMSAPI cmsBool CMSEXPORT _cmsIOPrintf(cmsIOHANDLER* io, const char* frm, ...); + +// Fixed point helper functions +CMSAPI cmsFloat64Number CMSEXPORT _cms8Fixed8toDouble(cmsUInt16Number fixed8); +CMSAPI cmsUInt16Number CMSEXPORT _cmsDoubleTo8Fixed8(cmsFloat64Number val); + +CMSAPI cmsFloat64Number CMSEXPORT _cms15Fixed16toDouble(cmsS15Fixed16Number fix32); +CMSAPI cmsS15Fixed16Number CMSEXPORT _cmsDoubleTo15Fixed16(cmsFloat64Number v); + +// Date/time helper functions +CMSAPI void CMSEXPORT _cmsEncodeDateTimeNumber(cmsDateTimeNumber *Dest, const struct tm *Source); +CMSAPI void CMSEXPORT _cmsDecodeDateTimeNumber(const cmsDateTimeNumber *Source, struct tm *Dest); + +//---------------------------------------------------------------------------------------------------------- + +// Shared callbacks for user data +typedef void (* _cmsFreeUserDataFn)(cmsContext ContextID, void* Data); +typedef void* (* _cmsDupUserDataFn)(cmsContext ContextID, const void* Data); + +//---------------------------------------------------------------------------------------------------------- + +// Plug-in foundation +#define cmsPluginMagicNumber 0x61637070 // 'acpp' + +#define cmsPluginMemHandlerSig 0x6D656D48 // 'memH' +#define cmsPluginInterpolationSig 0x696E7048 // 'inpH' +#define cmsPluginParametricCurveSig 0x70617248 // 'parH' +#define cmsPluginFormattersSig 0x66726D48 // 'frmH +#define cmsPluginTagTypeSig 0x74797048 // 'typH' +#define cmsPluginTagSig 0x74616748 // 'tagH' +#define cmsPluginRenderingIntentSig 0x696E7448 // 'intH' +#define cmsPluginMultiProcessElementSig 0x6D706548 // 'mpeH' +#define cmsPluginOptimizationSig 0x6F707448 // 'optH' +#define cmsPluginTransformSig 0x7A666D48 // 'xfmH' +#define cmsPluginMutexSig 0x6D747A48 // 'mtxH' + +typedef struct _cmsPluginBaseStruct { + + cmsUInt32Number Magic; // 'acpp' signature + cmsUInt32Number ExpectedVersion; // Expected version of LittleCMS + cmsUInt32Number Type; // Type of plug-in + struct _cmsPluginBaseStruct* Next; // For multiple plugin definition. NULL for end of list. + +} cmsPluginBase; + +// Maximum number of types in a plugin array +#define MAX_TYPES_IN_LCMS_PLUGIN 20 + +//---------------------------------------------------------------------------------------------------------- + +// 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 + _cmsMallocFnPtrType MallocPtr; + _cmsFreeFnPtrType FreePtr; + _cmsReallocFnPtrType ReallocPtr; + + // Optional + _cmsMalloZerocFnPtrType MallocZeroPtr; + _cmsCallocFnPtrType CallocPtr; + _cmsDupFnPtrType DupPtr; + +} cmsPluginMemHandler; + + +// ------------------------------------------------------------------------------------------------------------------ + +// Interpolation. 16 bits and floating point versions. +struct _cms_interp_struc; + +// Interpolation callbacks + +// 16 bits forward interpolation. This function performs precision-limited linear interpolation +// and is supposed to be quite fast. Implementation may be tetrahedral or trilinear, and plug-ins may +// choose to implement any other interpolation algorithm. +typedef void (* _cmsInterpFn16)(register const cmsUInt16Number Input[], + register cmsUInt16Number Output[], + register const struct _cms_interp_struc* p); + +// Floating point forward interpolation. Full precision interpolation using floats. This is not a +// time critical function. Implementation may be tetrahedral or trilinear, and plug-ins may +// choose to implement any other interpolation algorithm. +typedef void (* _cmsInterpFnFloat)(cmsFloat32Number const Input[], + cmsFloat32Number Output[], + const struct _cms_interp_struc* p); + + + +// This type holds a pointer to an interpolator that can be either 16 bits or float +typedef union { + _cmsInterpFn16 Lerp16; // Forward interpolation in 16 bits + _cmsInterpFnFloat LerpFloat; // Forward interpolation in floating point +} cmsInterpFunction; + +// Flags for interpolator selection +#define CMS_LERP_FLAGS_16BITS 0x0000 // The default +#define CMS_LERP_FLAGS_FLOAT 0x0001 // Requires different implementation +#define CMS_LERP_FLAGS_TRILINEAR 0x0100 // Hint only + + +#define MAX_INPUT_DIMENSIONS 8 + +typedef struct _cms_interp_struc { // Used on all interpolations. Supplied by lcms2 when calling the interpolation function + + cmsContext ContextID; // The calling thread + + cmsUInt32Number dwFlags; // Keep original flags + cmsUInt32Number nInputs; // != 1 only in 3D interpolation + cmsUInt32Number nOutputs; // != 1 only in 3D interpolation + + cmsUInt32Number nSamples[MAX_INPUT_DIMENSIONS]; // Valid on all kinds of tables + cmsUInt32Number Domain[MAX_INPUT_DIMENSIONS]; // Domain = nSamples - 1 + + cmsUInt32Number opta[MAX_INPUT_DIMENSIONS]; // Optimization for 3D CLUT. This is the number of nodes premultiplied for each + // dimension. For example, in 7 nodes, 7, 7^2 , 7^3, 7^4, etc. On non-regular + // Samplings may vary according of the number of nodes for each dimension. + + const void *Table; // Points to the actual interpolation table + cmsInterpFunction Interpolation; // Points to the function to do the interpolation + + } cmsInterpParams; + +// Interpolators factory +typedef cmsInterpFunction (* cmsInterpFnFactory)(cmsUInt32Number nInputChannels, cmsUInt32Number nOutputChannels, cmsUInt32Number dwFlags); + +// The plug-in +typedef struct { + cmsPluginBase base; + + // Points to a user-supplied function which implements the factory + cmsInterpFnFactory InterpolatorsFactory; + +} cmsPluginInterpolation; + +//---------------------------------------------------------------------------------------------------------- + +// Parametric curves. A negative type means same function but analytically inverted. Max. number of params is 10 + +// Evaluator callback for user-suplied parametric curves. May implement more than one type +typedef cmsFloat64Number (* cmsParametricCurveEvaluator)(cmsInt32Number Type, const cmsFloat64Number Params[10], cmsFloat64Number R); + +// Plug-in may implement an arbitrary number of parametric curves +typedef struct { + cmsPluginBase base; + + cmsUInt32Number nFunctions; // Number of supported functions + cmsUInt32Number FunctionTypes[MAX_TYPES_IN_LCMS_PLUGIN]; // The identification types + cmsUInt32Number ParameterCount[MAX_TYPES_IN_LCMS_PLUGIN]; // Number of parameters for each function + + cmsParametricCurveEvaluator Evaluator; // The evaluator + +} cmsPluginParametricCurves; +//---------------------------------------------------------------------------------------------------------- + +// Formatters. This plug-in adds new handlers, replacing them if they already exist. Formatters dealing with +// cmsFloat32Number (bps = 4) or double (bps = 0) types are requested via FormatterFloat callback. Others come across +// Formatter16 callback + +struct _cmstransform_struct; + +typedef cmsUInt8Number* (* cmsFormatter16)(register struct _cmstransform_struct* CMMcargo, + register cmsUInt16Number Values[], + register cmsUInt8Number* Buffer, + register cmsUInt32Number Stride); + +typedef cmsUInt8Number* (* cmsFormatterFloat)(struct _cmstransform_struct* CMMcargo, + cmsFloat32Number Values[], + cmsUInt8Number* Buffer, + cmsUInt32Number Stride); + +// This type holds a pointer to a formatter that can be either 16 bits or cmsFloat32Number +typedef union { + cmsFormatter16 Fmt16; + cmsFormatterFloat FmtFloat; + +} cmsFormatter; + +#define CMS_PACK_FLAGS_16BITS 0x0000 +#define CMS_PACK_FLAGS_FLOAT 0x0001 + +typedef enum { cmsFormatterInput=0, cmsFormatterOutput=1 } cmsFormatterDirection; + +typedef cmsFormatter (* cmsFormatterFactory)(cmsUInt32Number Type, // Specific type, i.e. TYPE_RGB_8 + cmsFormatterDirection Dir, + cmsUInt32Number dwFlags); // precision + +// Plug-in may implement an arbitrary number of formatters +typedef struct { + cmsPluginBase base; + cmsFormatterFactory FormattersFactory; + +} cmsPluginFormatters; + +//---------------------------------------------------------------------------------------------------------- + +// Tag type handler. Each type is free to return anything it wants, and it is up to the caller to +// know in advance what is the type contained in the tag. +typedef struct _cms_typehandler_struct { + + cmsTagTypeSignature Signature; // The signature of the type + + // Allocates and reads items + void * (* ReadPtr)(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + cmsUInt32Number* nItems, + cmsUInt32Number SizeOfTag); + + // Writes n Items + cmsBool (* WritePtr)(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + void* Ptr, + cmsUInt32Number nItems); + + // Duplicate an item or array of items + void* (* DupPtr)(struct _cms_typehandler_struct* self, + const void *Ptr, + cmsUInt32Number n); + + // Free all resources + void (* FreePtr)(struct _cms_typehandler_struct* self, + void *Ptr); + + // Additional parameters used by the calling thread + cmsContext ContextID; + cmsUInt32Number ICCVersion; + +} cmsTagTypeHandler; + +// Each plug-in implements a single type +typedef struct { + cmsPluginBase base; + cmsTagTypeHandler Handler; + +} cmsPluginTagType; + +//---------------------------------------------------------------------------------------------------------- + +// This is the tag plugin, which identifies tags. For writing, a pointer to function is provided. +// This function should return the desired type for this tag, given the version of profile +// and the data being serialized. +typedef struct { + + cmsUInt32Number ElemCount; // If this tag needs an array, how many elements should keep + + // For reading. + cmsUInt32Number nSupportedTypes; // In how many types this tag can come (MAX_TYPES_IN_LCMS_PLUGIN maximum) + cmsTagTypeSignature SupportedTypes[MAX_TYPES_IN_LCMS_PLUGIN]; + + // For writting + cmsTagTypeSignature (* DecideType)(cmsFloat64Number ICCVersion, const void *Data); + +} cmsTagDescriptor; + +// Plug-in implements a single tag +typedef struct { + cmsPluginBase base; + + cmsTagSignature Signature; + cmsTagDescriptor Descriptor; + +} cmsPluginTag; + +//---------------------------------------------------------------------------------------------------------- + +// Custom intents. This function should join all profiles specified in the array in +// a single LUT. Any custom intent in the chain redirects to custom function. If more than +// one custom intent is found, the one located first is invoked. Usually users should use only one +// custom intent, so mixing custom intents in same multiprofile transform is not supported. + +typedef cmsPipeline* (* cmsIntentFn)( cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number Intents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags); + + +// Each plug-in defines a single intent number. +typedef struct { + cmsPluginBase base; + cmsUInt32Number Intent; + cmsIntentFn Link; + char Description[256]; + +} cmsPluginRenderingIntent; + + +// The default ICC intents (perceptual, saturation, rel.col and abs.col) +CMSAPI cmsPipeline* CMSEXPORT _cmsDefaultICCintents(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number Intents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags); + + +//---------------------------------------------------------------------------------------------------------- + +// Pipelines, Multi Process Elements. + +typedef void (* _cmsStageEvalFn) (const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage* mpe); +typedef void*(* _cmsStageDupElemFn) (cmsStage* mpe); +typedef void (* _cmsStageFreeElemFn) (cmsStage* mpe); + + +// This function allocates a generic MPE +CMSAPI cmsStage* CMSEXPORT _cmsStageAllocPlaceholder(cmsContext ContextID, + cmsStageSignature Type, + cmsUInt32Number InputChannels, + cmsUInt32Number OutputChannels, + _cmsStageEvalFn EvalPtr, // Points to fn that evaluates the element (always in floating point) + _cmsStageDupElemFn DupElemPtr, // Points to a fn that duplicates the stage + _cmsStageFreeElemFn FreePtr, // Points to a fn that sets the element free + void* Data); // A generic pointer to whatever memory needed by the element +typedef struct { + cmsPluginBase base; + cmsTagTypeHandler Handler; + +} cmsPluginMultiProcessElement; + + +// Data kept in "Element" member of cmsStage + +// Curves +typedef struct { + cmsUInt32Number nCurves; + cmsToneCurve** TheCurves; + +} _cmsStageToneCurvesData; + +// Matrix +typedef struct { + cmsFloat64Number* Double; // floating point for the matrix + cmsFloat64Number* Offset; // The offset + +} _cmsStageMatrixData; + +// CLUT +typedef struct { + + union { // Can have only one of both representations at same time + cmsUInt16Number* T; // Points to the table 16 bits table + cmsFloat32Number* TFloat; // Points to the cmsFloat32Number table + + } Tab; + + cmsInterpParams* Params; + cmsUInt32Number nEntries; + cmsBool HasFloatValues; + +} _cmsStageCLutData; + + +//---------------------------------------------------------------------------------------------------------- +// Optimization. Using this plug-in, additional optimization strategies may be implemented. +// The function should return TRUE if any optimization is done on the LUT, this terminates +// the optimization search. Or FALSE if it is unable to optimize and want to give a chance +// to the rest of optimizers. + +typedef void (* _cmsOPTeval16Fn)(register const cmsUInt16Number In[], + register cmsUInt16Number Out[], + register const void* Data); + + +typedef cmsBool (* _cmsOPToptimizeFn)(cmsPipeline** Lut, + cmsUInt32Number Intent, + cmsUInt32Number* InputFormat, + cmsUInt32Number* OutputFormat, + cmsUInt32Number* dwFlags); + +// This function may be used to set the optional evaluator and a block of private data. If private data is being used, an optional +// duplicator and free functions should also be specified in order to duplicate the LUT construct. Use NULL to inhibit such functionality. + +CMSAPI void CMSEXPORT _cmsPipelineSetOptimizationParameters(cmsPipeline* Lut, + _cmsOPTeval16Fn Eval16, + void* PrivateData, + _cmsFreeUserDataFn FreePrivateDataFn, + _cmsDupUserDataFn DupPrivateDataFn); + +typedef struct { + cmsPluginBase base; + + // Optimize entry point + _cmsOPToptimizeFn OptimizePtr; + +} cmsPluginOptimization; + +//---------------------------------------------------------------------------------------------------------- +// Full xform +typedef void (* _cmsTransformFn)(struct _cmstransform_struct *CMMcargo, + const void* InputBuffer, + void* OutputBuffer, + cmsUInt32Number Size, + cmsUInt32Number Stride); + +typedef cmsBool (* _cmsTransformFactory)(_cmsTransformFn* xform, + void** UserData, + _cmsFreeUserDataFn* FreePrivateDataFn, + cmsPipeline** Lut, + cmsUInt32Number* InputFormat, + cmsUInt32Number* OutputFormat, + cmsUInt32Number* dwFlags); + + +// Retrieve user data as specified by the factory +CMSAPI void CMSEXPORT _cmsSetTransformUserData(struct _cmstransform_struct *CMMcargo, void* ptr, _cmsFreeUserDataFn FreePrivateDataFn); +CMSAPI void * CMSEXPORT _cmsGetTransformUserData(struct _cmstransform_struct *CMMcargo); + + +// Retrieve formatters +CMSAPI void CMSEXPORT _cmsGetTransformFormatters16 (struct _cmstransform_struct *CMMcargo, cmsFormatter16* FromInput, cmsFormatter16* ToOutput); +CMSAPI void CMSEXPORT _cmsGetTransformFormattersFloat(struct _cmstransform_struct *CMMcargo, cmsFormatterFloat* FromInput, cmsFormatterFloat* ToOutput); + +typedef struct { + cmsPluginBase base; + + // Transform entry point + _cmsTransformFactory Factory; + +} 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 + } +# endif +#endif + +#define _lcms_plugin_H +#endif diff --git a/third_party/lcms/src/cmscam02.c b/third_party/lcms/src/cmscam02.c new file mode 100644 index 0000000000..9d874aa205 --- /dev/null +++ b/third_party/lcms/src/cmscam02.c @@ -0,0 +1,486 @@ +//--------------------------------------------------------------------------------- +// +// 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" + +// CIECAM 02 appearance model. Many thanks to Jordi Vilar for the debugging. + +// ---------- Implementation -------------------------------------------- + +typedef struct { + + cmsFloat64Number XYZ[3]; + cmsFloat64Number RGB[3]; + cmsFloat64Number RGBc[3]; + cmsFloat64Number RGBp[3]; + cmsFloat64Number RGBpa[3]; + cmsFloat64Number a, b, h, e, H, A, J, Q, s, t, C, M; + cmsFloat64Number abC[2]; + cmsFloat64Number abs[2]; + cmsFloat64Number abM[2]; + +} CAM02COLOR; + +typedef struct { + + CAM02COLOR adoptedWhite; + cmsFloat64Number LA, Yb; + cmsFloat64Number F, c, Nc; + cmsUInt32Number surround; + cmsFloat64Number n, Nbb, Ncb, z, FL, D; + + cmsContext ContextID; + +} cmsCIECAM02; + + +static +cmsFloat64Number compute_n(cmsCIECAM02* pMod) +{ + return (pMod -> Yb / pMod -> adoptedWhite.XYZ[1]); +} + +static +cmsFloat64Number compute_z(cmsCIECAM02* pMod) +{ + return (1.48 + pow(pMod -> n, 0.5)); +} + +static +cmsFloat64Number computeNbb(cmsCIECAM02* pMod) +{ + return (0.725 * pow((1.0 / pMod -> n), 0.2)); +} + +static +cmsFloat64Number computeFL(cmsCIECAM02* pMod) +{ + cmsFloat64Number k, FL; + + k = 1.0 / ((5.0 * pMod->LA) + 1.0); + FL = 0.2 * pow(k, 4.0) * (5.0 * pMod->LA) + 0.1 * + (pow((1.0 - pow(k, 4.0)), 2.0)) * + (pow((5.0 * pMod->LA), (1.0 / 3.0))); + + return FL; +} + +static +cmsFloat64Number computeD(cmsCIECAM02* pMod) +{ + cmsFloat64Number D; + + D = pMod->F - (1.0/3.6)*(exp(((-pMod ->LA-42) / 92.0))); + + return D; +} + + +static +CAM02COLOR XYZtoCAT02(CAM02COLOR clr) +{ + clr.RGB[0] = (clr.XYZ[0] * 0.7328) + (clr.XYZ[1] * 0.4296) + (clr.XYZ[2] * -0.1624); + clr.RGB[1] = (clr.XYZ[0] * -0.7036) + (clr.XYZ[1] * 1.6975) + (clr.XYZ[2] * 0.0061); + clr.RGB[2] = (clr.XYZ[0] * 0.0030) + (clr.XYZ[1] * 0.0136) + (clr.XYZ[2] * 0.9834); + + return clr; +} + +static +CAM02COLOR ChromaticAdaptation(CAM02COLOR clr, cmsCIECAM02* pMod) +{ + cmsUInt32Number i; + + for (i = 0; i < 3; i++) { + clr.RGBc[i] = ((pMod -> adoptedWhite.XYZ[1] * + (pMod->D / pMod -> adoptedWhite.RGB[i])) + + (1.0 - pMod->D)) * clr.RGB[i]; + } + + return clr; +} + + +static +CAM02COLOR CAT02toHPE(CAM02COLOR clr) +{ + cmsFloat64Number M[9]; + + M[0] =(( 0.38971 * 1.096124) + (0.68898 * 0.454369) + (-0.07868 * -0.009628)); + M[1] =(( 0.38971 * -0.278869) + (0.68898 * 0.473533) + (-0.07868 * -0.005698)); + M[2] =(( 0.38971 * 0.182745) + (0.68898 * 0.072098) + (-0.07868 * 1.015326)); + M[3] =((-0.22981 * 1.096124) + (1.18340 * 0.454369) + ( 0.04641 * -0.009628)); + M[4] =((-0.22981 * -0.278869) + (1.18340 * 0.473533) + ( 0.04641 * -0.005698)); + M[5] =((-0.22981 * 0.182745) + (1.18340 * 0.072098) + ( 0.04641 * 1.015326)); + M[6] =(-0.009628); + M[7] =(-0.005698); + M[8] =( 1.015326); + + clr.RGBp[0] = (clr.RGBc[0] * M[0]) + (clr.RGBc[1] * M[1]) + (clr.RGBc[2] * M[2]); + clr.RGBp[1] = (clr.RGBc[0] * M[3]) + (clr.RGBc[1] * M[4]) + (clr.RGBc[2] * M[5]); + clr.RGBp[2] = (clr.RGBc[0] * M[6]) + (clr.RGBc[1] * M[7]) + (clr.RGBc[2] * M[8]); + + return clr; +} + +static +CAM02COLOR NonlinearCompression(CAM02COLOR clr, cmsCIECAM02* pMod) +{ + cmsUInt32Number i; + cmsFloat64Number temp; + + for (i = 0; i < 3; i++) { + if (clr.RGBp[i] < 0) { + + temp = pow((-1.0 * pMod->FL * clr.RGBp[i] / 100.0), 0.42); + clr.RGBpa[i] = (-1.0 * 400.0 * temp) / (temp + 27.13) + 0.1; + } + else { + temp = pow((pMod->FL * clr.RGBp[i] / 100.0), 0.42); + clr.RGBpa[i] = (400.0 * temp) / (temp + 27.13) + 0.1; + } + } + + clr.A = (((2.0 * clr.RGBpa[0]) + clr.RGBpa[1] + + (clr.RGBpa[2] / 20.0)) - 0.305) * pMod->Nbb; + + return clr; +} + +static +CAM02COLOR ComputeCorrelates(CAM02COLOR clr, cmsCIECAM02* pMod) +{ + cmsFloat64Number a, b, temp, e, t, r2d, d2r; + + a = clr.RGBpa[0] - (12.0 * clr.RGBpa[1] / 11.0) + (clr.RGBpa[2] / 11.0); + b = (clr.RGBpa[0] + clr.RGBpa[1] - (2.0 * clr.RGBpa[2])) / 9.0; + + r2d = (180.0 / 3.141592654); + if (a == 0) { + if (b == 0) clr.h = 0; + else if (b > 0) clr.h = 90; + else clr.h = 270; + } + else if (a > 0) { + temp = b / a; + if (b > 0) clr.h = (r2d * atan(temp)); + else if (b == 0) clr.h = 0; + else clr.h = (r2d * atan(temp)) + 360; + } + else { + temp = b / a; + clr.h = (r2d * atan(temp)) + 180; + } + + d2r = (3.141592654 / 180.0); + e = ((12500.0 / 13.0) * pMod->Nc * pMod->Ncb) * + (cos((clr.h * d2r + 2.0)) + 3.8); + + if (clr.h < 20.14) { + temp = ((clr.h + 122.47)/1.2) + ((20.14 - clr.h)/0.8); + clr.H = 300 + (100*((clr.h + 122.47)/1.2)) / temp; + } + else if (clr.h < 90.0) { + temp = ((clr.h - 20.14)/0.8) + ((90.00 - clr.h)/0.7); + clr.H = (100*((clr.h - 20.14)/0.8)) / temp; + } + else if (clr.h < 164.25) { + temp = ((clr.h - 90.00)/0.7) + ((164.25 - clr.h)/1.0); + clr.H = 100 + ((100*((clr.h - 90.00)/0.7)) / temp); + } + else if (clr.h < 237.53) { + temp = ((clr.h - 164.25)/1.0) + ((237.53 - clr.h)/1.2); + clr.H = 200 + ((100*((clr.h - 164.25)/1.0)) / temp); + } + else { + temp = ((clr.h - 237.53)/1.2) + ((360 - clr.h + 20.14)/0.8); + clr.H = 300 + ((100*((clr.h - 237.53)/1.2)) / temp); + } + + clr.J = 100.0 * pow((clr.A / pMod->adoptedWhite.A), + (pMod->c * pMod->z)); + + clr.Q = (4.0 / pMod->c) * pow((clr.J / 100.0), 0.5) * + (pMod->adoptedWhite.A + 4.0) * pow(pMod->FL, 0.25); + + t = (e * pow(((a * a) + (b * b)), 0.5)) / + (clr.RGBpa[0] + clr.RGBpa[1] + + ((21.0 / 20.0) * clr.RGBpa[2])); + + clr.C = pow(t, 0.9) * pow((clr.J / 100.0), 0.5) * + pow((1.64 - pow(0.29, pMod->n)), 0.73); + + clr.M = clr.C * pow(pMod->FL, 0.25); + clr.s = 100.0 * pow((clr.M / clr.Q), 0.5); + + return clr; +} + + +static +CAM02COLOR InverseCorrelates(CAM02COLOR clr, cmsCIECAM02* pMod) +{ + + cmsFloat64Number t, e, p1, p2, p3, p4, p5, hr, d2r; + d2r = 3.141592654 / 180.0; + + t = pow( (clr.C / (pow((clr.J / 100.0), 0.5) * + (pow((1.64 - pow(0.29, pMod->n)), 0.73)))), + (1.0 / 0.9) ); + e = ((12500.0 / 13.0) * pMod->Nc * pMod->Ncb) * + (cos((clr.h * d2r + 2.0)) + 3.8); + + clr.A = pMod->adoptedWhite.A * pow( + (clr.J / 100.0), + (1.0 / (pMod->c * pMod->z))); + + p1 = e / t; + p2 = (clr.A / pMod->Nbb) + 0.305; + p3 = 21.0 / 20.0; + + hr = clr.h * d2r; + + if (fabs(sin(hr)) >= fabs(cos(hr))) { + p4 = p1 / sin(hr); + clr.b = (p2 * (2.0 + p3) * (460.0 / 1403.0)) / + (p4 + (2.0 + p3) * (220.0 / 1403.0) * + (cos(hr) / sin(hr)) - (27.0 / 1403.0) + + p3 * (6300.0 / 1403.0)); + clr.a = clr.b * (cos(hr) / sin(hr)); + } + else { + p5 = p1 / cos(hr); + clr.a = (p2 * (2.0 + p3) * (460.0 / 1403.0)) / + (p5 + (2.0 + p3) * (220.0 / 1403.0) - + ((27.0 / 1403.0) - p3 * (6300.0 / 1403.0)) * + (sin(hr) / cos(hr))); + clr.b = clr.a * (sin(hr) / cos(hr)); + } + + clr.RGBpa[0] = ((460.0 / 1403.0) * p2) + + ((451.0 / 1403.0) * clr.a) + + ((288.0 / 1403.0) * clr.b); + clr.RGBpa[1] = ((460.0 / 1403.0) * p2) - + ((891.0 / 1403.0) * clr.a) - + ((261.0 / 1403.0) * clr.b); + clr.RGBpa[2] = ((460.0 / 1403.0) * p2) - + ((220.0 / 1403.0) * clr.a) - + ((6300.0 / 1403.0) * clr.b); + + return clr; +} + +static +CAM02COLOR InverseNonlinearity(CAM02COLOR clr, cmsCIECAM02* pMod) +{ + cmsUInt32Number i; + cmsFloat64Number c1; + + for (i = 0; i < 3; i++) { + if ((clr.RGBpa[i] - 0.1) < 0) c1 = -1; + else c1 = 1; + clr.RGBp[i] = c1 * (100.0 / pMod->FL) * + pow(((27.13 * fabs(clr.RGBpa[i] - 0.1)) / + (400.0 - fabs(clr.RGBpa[i] - 0.1))), + (1.0 / 0.42)); + } + + return clr; +} + +static +CAM02COLOR HPEtoCAT02(CAM02COLOR clr) +{ + cmsFloat64Number M[9]; + + M[0] = (( 0.7328 * 1.910197) + (0.4296 * 0.370950)); + M[1] = (( 0.7328 * -1.112124) + (0.4296 * 0.629054)); + M[2] = (( 0.7328 * 0.201908) + (0.4296 * 0.000008) - 0.1624); + M[3] = ((-0.7036 * 1.910197) + (1.6975 * 0.370950)); + M[4] = ((-0.7036 * -1.112124) + (1.6975 * 0.629054)); + M[5] = ((-0.7036 * 0.201908) + (1.6975 * 0.000008) + 0.0061); + M[6] = (( 0.0030 * 1.910197) + (0.0136 * 0.370950)); + M[7] = (( 0.0030 * -1.112124) + (0.0136 * 0.629054)); + M[8] = (( 0.0030 * 0.201908) + (0.0136 * 0.000008) + 0.9834);; + + clr.RGBc[0] = (clr.RGBp[0] * M[0]) + (clr.RGBp[1] * M[1]) + (clr.RGBp[2] * M[2]); + clr.RGBc[1] = (clr.RGBp[0] * M[3]) + (clr.RGBp[1] * M[4]) + (clr.RGBp[2] * M[5]); + clr.RGBc[2] = (clr.RGBp[0] * M[6]) + (clr.RGBp[1] * M[7]) + (clr.RGBp[2] * M[8]); + return clr; +} + + +static +CAM02COLOR InverseChromaticAdaptation(CAM02COLOR clr, cmsCIECAM02* pMod) +{ + cmsUInt32Number i; + for (i = 0; i < 3; i++) { + clr.RGB[i] = clr.RGBc[i] / + ((pMod->adoptedWhite.XYZ[1] * pMod->D / pMod->adoptedWhite.RGB[i]) + 1.0 - pMod->D); + } + return clr; +} + + +static +CAM02COLOR CAT02toXYZ(CAM02COLOR clr) +{ + clr.XYZ[0] = (clr.RGB[0] * 1.096124) + (clr.RGB[1] * -0.278869) + (clr.RGB[2] * 0.182745); + clr.XYZ[1] = (clr.RGB[0] * 0.454369) + (clr.RGB[1] * 0.473533) + (clr.RGB[2] * 0.072098); + clr.XYZ[2] = (clr.RGB[0] * -0.009628) + (clr.RGB[1] * -0.005698) + (clr.RGB[2] * 1.015326); + + return clr; +} + + +cmsHANDLE CMSEXPORT cmsCIECAM02Init(cmsContext ContextID, const cmsViewingConditions* pVC) +{ + cmsCIECAM02* lpMod; + + _cmsAssert(pVC != NULL); + + if((lpMod = (cmsCIECAM02*) _cmsMallocZero(ContextID, sizeof(cmsCIECAM02))) == NULL) { + return NULL; + } + + lpMod ->ContextID = ContextID; + + lpMod ->adoptedWhite.XYZ[0] = pVC ->whitePoint.X; + lpMod ->adoptedWhite.XYZ[1] = pVC ->whitePoint.Y; + lpMod ->adoptedWhite.XYZ[2] = pVC ->whitePoint.Z; + + lpMod -> LA = pVC ->La; + lpMod -> Yb = pVC ->Yb; + lpMod -> D = pVC ->D_value; + lpMod -> surround = pVC ->surround; + + switch (lpMod -> surround) { + + + case CUTSHEET_SURROUND: + lpMod->F = 0.8; + lpMod->c = 0.41; + lpMod->Nc = 0.8; + break; + + case DARK_SURROUND: + lpMod -> F = 0.8; + lpMod -> c = 0.525; + lpMod -> Nc = 0.8; + break; + + case DIM_SURROUND: + lpMod -> F = 0.9; + lpMod -> c = 0.59; + lpMod -> Nc = 0.95; + break; + + default: + // Average surround + lpMod -> F = 1.0; + lpMod -> c = 0.69; + lpMod -> Nc = 1.0; + } + + lpMod -> n = compute_n(lpMod); + lpMod -> z = compute_z(lpMod); + lpMod -> Nbb = computeNbb(lpMod); + lpMod -> FL = computeFL(lpMod); + + if (lpMod -> D == D_CALCULATE) { + lpMod -> D = computeD(lpMod); + } + + lpMod -> Ncb = lpMod -> Nbb; + + lpMod -> adoptedWhite = XYZtoCAT02(lpMod -> adoptedWhite); + lpMod -> adoptedWhite = ChromaticAdaptation(lpMod -> adoptedWhite, lpMod); + lpMod -> adoptedWhite = CAT02toHPE(lpMod -> adoptedWhite); + lpMod -> adoptedWhite = NonlinearCompression(lpMod -> adoptedWhite, lpMod); + + return (cmsHANDLE) lpMod; + +} + +void CMSEXPORT cmsCIECAM02Done(cmsHANDLE hModel) +{ + cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel; + + if (lpMod) _cmsFree(lpMod ->ContextID, lpMod); +} + + +void CMSEXPORT cmsCIECAM02Forward(cmsHANDLE hModel, const cmsCIEXYZ* pIn, cmsJCh* pOut) +{ + CAM02COLOR clr; + cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel; + + _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; + + clr = XYZtoCAT02(clr); + clr = ChromaticAdaptation(clr, lpMod); + clr = CAT02toHPE(clr); + clr = NonlinearCompression(clr, lpMod); + clr = ComputeCorrelates(clr, lpMod); + + pOut ->J = clr.J; + pOut ->C = clr.C; + pOut ->h = clr.h; +} + +void CMSEXPORT cmsCIECAM02Reverse(cmsHANDLE hModel, const cmsJCh* pIn, cmsCIEXYZ* pOut) +{ + CAM02COLOR clr; + cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel; + + _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; + + clr = InverseCorrelates(clr, lpMod); + clr = InverseNonlinearity(clr, lpMod); + clr = HPEtoCAT02(clr); + clr = InverseChromaticAdaptation(clr, lpMod); + clr = CAT02toXYZ(clr); + + pOut ->X = clr.XYZ[0]; + pOut ->Y = clr.XYZ[1]; + pOut ->Z = clr.XYZ[2]; +} diff --git a/third_party/lcms/src/cmscgats.c b/third_party/lcms/src/cmscgats.c new file mode 100644 index 0000000000..cce4cedbad --- /dev/null +++ b/third_party/lcms/src/cmscgats.c @@ -0,0 +1,2776 @@ +//--------------------------------------------------------------------------------- +// +// 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" + + +// IT8.7 / CGATS.17-200x handling ----------------------------------------------------------------------------- + + +#define MAXID 128 // Max length of identifier +#define MAXSTR 1024 // Max length of string +#define MAXTABLES 255 // Max Number of tables in a single stream +#define MAXINCLUDE 20 // Max number of nested includes + +#define DEFAULT_DBL_FORMAT "%.10g" // Double formatting + +#ifdef CMS_IS_WINDOWS_ +//sunliang.liu modified 2010426 for wince error +# ifndef _WIN32_WCE +# include +# endif +# define DIR_CHAR '\\' +#else +# define DIR_CHAR '/' +#endif + + +// Symbols +typedef enum { + + SNONE, + SINUM, // Integer + SDNUM, // Real + SIDENT, // Identifier + SSTRING, // string + SCOMMENT, // comment + SEOLN, // End of line + SEOF, // End of stream + SSYNERROR, // Syntax error found on stream + + // Keywords + + SBEGIN_DATA, + SBEGIN_DATA_FORMAT, + SEND_DATA, + SEND_DATA_FORMAT, + SKEYWORD, + SDATA_FORMAT_ID, + SINCLUDE + + } SYMBOL; + + +// How to write the value +typedef enum { + + WRITE_UNCOOKED, + WRITE_STRINGIFY, + WRITE_HEXADECIMAL, + WRITE_BINARY, + WRITE_PAIR + + } WRITEMODE; + +// Linked list of variable names +typedef struct _KeyVal { + + struct _KeyVal* Next; + char* Keyword; // Name of variable + struct _KeyVal* NextSubkey; // If key is a dictionary, points to the next item + char* Subkey; // If key is a dictionary, points to the subkey name + char* Value; // Points to value + WRITEMODE WriteAs; // How to write the value + + } KEYVALUE; + + +// Linked list of memory chunks (Memory sink) +typedef struct _OwnedMem { + + struct _OwnedMem* Next; + void * Ptr; // Point to value + + } OWNEDMEM; + +// Suballocator +typedef struct _SubAllocator { + + cmsUInt8Number* Block; + cmsUInt32Number BlockSize; + cmsUInt32Number Used; + + } SUBALLOCATOR; + +// Table. Each individual table can hold properties and rows & cols +typedef struct _Table { + + char SheetType[MAXSTR]; // The first row of the IT8 (the type) + + int nSamples, nPatches; // Cols, Rows + int SampleID; // Pos of ID + + KEYVALUE* HeaderList; // The properties + + char** DataFormat; // The binary stream descriptor + char** Data; // The binary stream + + } TABLE; + +// File stream being parsed +typedef struct _FileContext { + char FileName[cmsMAX_PATH]; // File name if being readed from file + FILE* Stream; // File stream or NULL if holded in memory + } FILECTX; + +// This struct hold all information about an open IT8 handler. +typedef struct { + + + cmsUInt32Number TablesCount; // How many tables in this stream + cmsUInt32Number nTable; // The actual table + + TABLE Tab[MAXTABLES]; + + // Memory management + OWNEDMEM* MemorySink; // The storage backend + SUBALLOCATOR Allocator; // String suballocator -- just to keep it fast + + // Parser state machine + SYMBOL sy; // Current symbol + int ch; // Current character + + cmsInt32Number inum; // integer value + cmsFloat64Number dnum; // real value + + char id[MAXID]; // identifier + char str[MAXSTR]; // string + + // Allowed keywords & datasets. They have visibility on whole stream + KEYVALUE* ValidKeywords; + KEYVALUE* ValidSampleID; + + char* Source; // Points to loc. being parsed + cmsInt32Number lineno; // line counter for error reporting + + FILECTX* FileStack[MAXINCLUDE]; // Stack of files being parsed + cmsInt32Number IncludeSP; // Include Stack Pointer + + char* MemoryBlock; // The stream if holded in memory + + char DoubleFormatter[MAXID];// Printf-like 'cmsFloat64Number' formatter + + cmsContext ContextID; // The threading context + + } cmsIT8; + + +// The stream for save operations +typedef struct { + + FILE* stream; // For save-to-file behaviour + + cmsUInt8Number* Base; + cmsUInt8Number* Ptr; // For save-to-mem behaviour + cmsUInt32Number Used; + cmsUInt32Number Max; + + } SAVESTREAM; + + +// ------------------------------------------------------ cmsIT8 parsing routines + + +// A keyword +typedef struct { + + const char *id; + SYMBOL sy; + + } KEYWORD; + +// The keyword->symbol translation table. Sorting is required. +static const KEYWORD TabKeys[] = { + + {"$INCLUDE", SINCLUDE}, // This is an extension! + {".INCLUDE", SINCLUDE}, // This is an extension! + + {"BEGIN_DATA", SBEGIN_DATA }, + {"BEGIN_DATA_FORMAT", SBEGIN_DATA_FORMAT }, + {"DATA_FORMAT_IDENTIFIER", SDATA_FORMAT_ID}, + {"END_DATA", SEND_DATA}, + {"END_DATA_FORMAT", SEND_DATA_FORMAT}, + {"KEYWORD", SKEYWORD} + }; + +#define NUMKEYS (sizeof(TabKeys)/sizeof(KEYWORD)) + +// Predefined properties + +// A property +typedef struct { + const char *id; // The identifier + WRITEMODE as; // How is supposed to be written + } PROPERTY; + +static PROPERTY PredefinedProperties[] = { + + {"NUMBER_OF_FIELDS", WRITE_UNCOOKED}, // Required - NUMBER OF FIELDS + {"NUMBER_OF_SETS", WRITE_UNCOOKED}, // Required - NUMBER OF SETS + {"ORIGINATOR", WRITE_STRINGIFY}, // Required - Identifies the specific system, organization or individual that created the data file. + {"FILE_DESCRIPTOR", WRITE_STRINGIFY}, // Required - Describes the purpose or contents of the data file. + {"CREATED", WRITE_STRINGIFY}, // Required - Indicates date of creation of the data file. + {"DESCRIPTOR", WRITE_STRINGIFY}, // Required - Describes the purpose or contents of the data file. + {"DIFFUSE_GEOMETRY", WRITE_STRINGIFY}, // The diffuse geometry used. Allowed values are "sphere" or "opal". + {"MANUFACTURER", WRITE_STRINGIFY}, + {"MANUFACTURE", WRITE_STRINGIFY}, // Some broken Fuji targets does store this value + {"PROD_DATE", WRITE_STRINGIFY}, // Identifies year and month of production of the target in the form yyyy:mm. + {"SERIAL", WRITE_STRINGIFY}, // Uniquely identifies individual physical target. + + {"MATERIAL", WRITE_STRINGIFY}, // Identifies the material on which the target was produced using a code + // uniquely identifying th e material. This is intend ed to be used for IT8.7 + // physical targets only (i.e . IT8.7/1 a nd IT8.7/2). + + {"INSTRUMENTATION", WRITE_STRINGIFY}, // Used to report the specific instrumentation used (manufacturer and + // model number) to generate the data reported. This data will often + // provide more information about the particular data collected than an + // extensive list of specific details. This is particularly important for + // spectral data or data derived from spectrophotometry. + + {"MEASUREMENT_SOURCE", WRITE_STRINGIFY}, // Illumination used for spectral measurements. This data helps provide + // a guide to the potential for issues of paper fluorescence, etc. + + {"PRINT_CONDITIONS", WRITE_STRINGIFY}, // Used to define the characteristics of the printed sheet being reported. + // Where standard conditions have been defined (e.g., SWOP at nominal) + // named conditions may suffice. Otherwise, detailed information is + // needed. + + {"SAMPLE_BACKING", WRITE_STRINGIFY}, // Identifies the backing material used behind the sample during + // measurement. Allowed values are "black" "white" or "na". + + {"CHISQ_DOF", WRITE_STRINGIFY}, // Degrees of freedom associated with the Chi squared statistic + + // below properties are new in recent specs: + + {"MEASUREMENT_GEOMETRY", WRITE_STRINGIFY}, // The type of measurement, either reflection or transmission, should be indicated + // along with details of the geometry and the aperture size and shape. For example, + // for transmission measurements it is important to identify 0/diffuse, diffuse/0, + // opal or integrating sphere, etc. For reflection it is important to identify 0/45, + // 45/0, sphere (specular included or excluded), etc. + + {"FILTER", WRITE_STRINGIFY}, // Identifies the use of physical filter(s) during measurement. Typically used to + // denote the use of filters such as none, D65, Red, Green or Blue. + + {"POLARIZATION", WRITE_STRINGIFY}, // Identifies the use of a physical polarization filter during measurement. Allowed + // values are "yes" "white" "none" or "na" + + {"WEIGHTING_FUNCTION", WRITE_PAIR}, // Indicates such functions as: the CIE standard observer functions used in the + // calculation of various data parameters (2 degree and 10 degree), CIE standard + // illuminant functions used in the calculation of various data parameters (e.g., D50, + // D65, etc.), density status response, etc. If used there shall be at least one + // name-value pair following the WEIGHTING_FUNCTION tag/keyword. The first attribute + // in the set shall be {"name" and shall identify the particular parameter used. + // The second shall be {"value" and shall provide the value associated with that name. + // For ASCII data, a string containing the Name and Value attribute pairs shall follow + // the weighting function keyword. A semi-colon separates attribute pairs from each + // other and within the attribute the name and value are separated by a comma. + + {"COMPUTATIONAL_PARAMETER", WRITE_PAIR}, // Parameter that is used in computing a value from measured data. Name is the name + // of the calculation, parameter is the name of the parameter used in the calculation + // and value is the value of the parameter. + + {"TARGET_TYPE", WRITE_STRINGIFY}, // The type of target being measured, e.g. IT8.7/1, IT8.7/3, user defined, etc. + + {"COLORANT", WRITE_STRINGIFY}, // Identifies the colorant(s) used in creating the target. + + {"TABLE_DESCRIPTOR", WRITE_STRINGIFY}, // Describes the purpose or contents of a data table. + + {"TABLE_NAME", WRITE_STRINGIFY} // Provides a short name for a data table. +}; + +#define NUMPREDEFINEDPROPS (sizeof(PredefinedProperties)/sizeof(PROPERTY)) + + +// Predefined sample types on dataset +static const char* PredefinedSampleID[] = { + "SAMPLE_ID", // Identifies sample that data represents + "STRING", // Identifies label, or other non-machine readable value. + // Value must begin and end with a " symbol + + "CMYK_C", // Cyan component of CMYK data expressed as a percentage + "CMYK_M", // Magenta component of CMYK data expressed as a percentage + "CMYK_Y", // Yellow component of CMYK data expressed as a percentage + "CMYK_K", // Black component of CMYK data expressed as a percentage + "D_RED", // Red filter density + "D_GREEN", // Green filter density + "D_BLUE", // Blue filter density + "D_VIS", // Visual filter density + "D_MAJOR_FILTER", // Major filter d ensity + "RGB_R", // Red component of RGB data + "RGB_G", // Green component of RGB data + "RGB_B", // Blue com ponent of RGB data + "SPECTRAL_NM", // Wavelength of measurement expressed in nanometers + "SPECTRAL_PCT", // Percentage reflectance/transmittance + "SPECTRAL_DEC", // Reflectance/transmittance + "XYZ_X", // X component of tristimulus data + "XYZ_Y", // Y component of tristimulus data + "XYZ_Z", // Z component of tristimulus data + "XYY_X" // x component of chromaticity data + "XYY_Y", // y component of chromaticity data + "XYY_CAPY", // Y component of tristimulus data + "LAB_L", // L* component of Lab data + "LAB_A", // a* component of Lab data + "LAB_B", // b* component of Lab data + "LAB_C", // C*ab component of Lab data + "LAB_H", // hab component of Lab data + "LAB_DE", // CIE dE + "LAB_DE_94", // CIE dE using CIE 94 + "LAB_DE_CMC", // dE using CMC + "LAB_DE_2000", // CIE dE using CIE DE 2000 + "MEAN_DE", // Mean Delta E (LAB_DE) of samples compared to batch average + // (Used for data files for ANSI IT8.7/1 and IT8.7/2 targets) + "STDEV_X", // Standard deviation of X (tristimulus data) + "STDEV_Y", // Standard deviation of Y (tristimulus data) + "STDEV_Z", // Standard deviation of Z (tristimulus data) + "STDEV_L", // Standard deviation of L* + "STDEV_A", // Standard deviation of a* + "STDEV_B", // Standard deviation of b* + "STDEV_DE", // Standard deviation of CIE dE + "CHI_SQD_PAR"}; // The average of the standard deviations of L*, a* and b*. It is + // used to derive an estimate of the chi-squared parameter which is + // recommended as the predictor of the variability of dE + +#define NUMPREDEFINEDSAMPLEID (sizeof(PredefinedSampleID)/sizeof(char *)) + +//Forward declaration of some internal functions +static void* AllocChunk(cmsIT8* it8, cmsUInt32Number size); + +// Checks whatever c is a separator +static +cmsBool isseparator(int c) +{ + return (c == ' ') || (c == '\t') ; +} + +// Checks whatever c is a valid identifier char +static +cmsBool ismiddle(int c) +{ + return (!isseparator(c) && (c != '#') && (c !='\"') && (c != '\'') && (c > 32) && (c < 127)); +} + +// Checks whatsever c is a valid identifier middle char. +static +cmsBool isidchar(int c) +{ + return isalnum(c) || ismiddle(c); +} + +// Checks whatsever c is a valid identifier first char. +static +cmsBool isfirstidchar(int c) +{ + return !isdigit(c) && ismiddle(c); +} + +// Guess whether the supplied path looks like an absolute path +static +cmsBool isabsolutepath(const char *path) +{ + char ThreeChars[4]; + + if(path == NULL) + return FALSE; + if (path[0] == 0) + return FALSE; + + strncpy(ThreeChars, path, 3); + ThreeChars[3] = 0; + + if(ThreeChars[0] == DIR_CHAR) + return TRUE; + +#ifdef CMS_IS_WINDOWS_ + if (isalpha((int) ThreeChars[0]) && ThreeChars[1] == ':') + return TRUE; +#endif + return FALSE; +} + + +// Makes a file path based on a given reference path +// NOTE: this function doesn't check if the path exists or even if it's legal +static +cmsBool BuildAbsolutePath(const char *relPath, const char *basePath, char *buffer, cmsUInt32Number MaxLen) +{ + char *tail; + cmsUInt32Number len; + + // Already absolute? + if (isabsolutepath(relPath)) { + + strncpy(buffer, relPath, MaxLen); + buffer[MaxLen-1] = 0; + return TRUE; + } + + // No, search for last + strncpy(buffer, basePath, MaxLen); + buffer[MaxLen-1] = 0; + + tail = strrchr(buffer, DIR_CHAR); + if (tail == NULL) return FALSE; // Is not absolute and has no separators?? + + len = (cmsUInt32Number) (tail - buffer); + if (len >= MaxLen) return FALSE; + + // No need to assure zero terminator over here + strncpy(tail + 1, relPath, MaxLen - len); + + return TRUE; +} + + +// Make sure no exploit is being even tried +static +const char* NoMeta(const char* str) +{ + if (strchr(str, '%') != NULL) + return "**** CORRUPTED FORMAT STRING ***"; + + return str; +} + +// Syntax error +static +cmsBool SynError(cmsIT8* it8, const char *Txt, ...) +{ + char Buffer[256], ErrMsg[1024]; + va_list args; + + va_start(args, Txt); + vsnprintf(Buffer, 255, Txt, args); + Buffer[255] = 0; + va_end(args); + + snprintf(ErrMsg, 1023, "%s: Line %d, %s", it8->FileStack[it8 ->IncludeSP]->FileName, it8->lineno, Buffer); + ErrMsg[1023] = 0; + it8->sy = SSYNERROR; + cmsSignalError(it8 ->ContextID, cmsERROR_CORRUPTION_DETECTED, "%s", ErrMsg); + return FALSE; +} + +// Check if current symbol is same as specified. issue an error else. +static +cmsBool Check(cmsIT8* it8, SYMBOL sy, const char* Err) +{ + if (it8 -> sy != sy) + return SynError(it8, NoMeta(Err)); + return TRUE; +} + +// Read Next character from stream +static +void NextCh(cmsIT8* it8) +{ + if (it8 -> FileStack[it8 ->IncludeSP]->Stream) { + + it8 ->ch = fgetc(it8 ->FileStack[it8 ->IncludeSP]->Stream); + + if (feof(it8 -> FileStack[it8 ->IncludeSP]->Stream)) { + + if (it8 ->IncludeSP > 0) { + + fclose(it8 ->FileStack[it8->IncludeSP--]->Stream); + it8 -> ch = ' '; // Whitespace to be ignored + + } else + it8 ->ch = 0; // EOF + } + } + else { + it8->ch = *it8->Source; + if (it8->ch) it8->Source++; + } +} + + +// Try to see if current identifier is a keyword, if so return the referred symbol +static +SYMBOL BinSrchKey(const char *id) +{ + int l = 1; + int r = NUMKEYS; + int x, res; + + while (r >= l) + { + x = (l+r)/2; + res = cmsstrcasecmp(id, TabKeys[x-1].id); + if (res == 0) return TabKeys[x-1].sy; + if (res < 0) r = x - 1; + else l = x + 1; + } + + return SNONE; +} + + +// 10 ^n +static +cmsFloat64Number xpow10(int n) +{ + return pow(10, (cmsFloat64Number) n); +} + + +// Reads a Real number, tries to follow from integer number +static +void ReadReal(cmsIT8* it8, int inum) +{ + it8->dnum = (cmsFloat64Number) inum; + + while (isdigit(it8->ch)) { + + it8->dnum = it8->dnum * 10.0 + (it8->ch - '0'); + NextCh(it8); + } + + if (it8->ch == '.') { // Decimal point + + cmsFloat64Number frac = 0.0; // fraction + int prec = 0; // precision + + NextCh(it8); // Eats dec. point + + while (isdigit(it8->ch)) { + + frac = frac * 10.0 + (it8->ch - '0'); + prec++; + NextCh(it8); + } + + it8->dnum = it8->dnum + (frac / xpow10(prec)); + } + + // Exponent, example 34.00E+20 + if (toupper(it8->ch) == 'E') { + + cmsInt32Number e; + cmsInt32Number sgn; + + NextCh(it8); sgn = 1; + + if (it8->ch == '-') { + + sgn = -1; NextCh(it8); + } + else + if (it8->ch == '+') { + + sgn = +1; + NextCh(it8); + } + + e = 0; + while (isdigit(it8->ch)) { + + if ((cmsFloat64Number) e * 10L < (cmsFloat64Number) +2147483647.0) + e = e * 10 + (it8->ch - '0'); + + NextCh(it8); + } + + e = sgn*e; + it8 -> dnum = it8 -> dnum * xpow10(e); + } +} + +// Parses a float number +// This can not call directly atof because it uses locale dependant +// parsing, while CCMX files always use . as decimal separator +static +cmsFloat64Number ParseFloatNumber(const char *Buffer) +{ + cmsFloat64Number dnum = 0.0; + int sign = 1; + + // keep safe + if (Buffer == NULL) return 0.0; + + if (*Buffer == '-' || *Buffer == '+') { + + sign = (*Buffer == '-') ? -1 : 1; + Buffer++; + } + + + while (*Buffer && isdigit((int) *Buffer)) { + + dnum = dnum * 10.0 + (*Buffer - '0'); + if (*Buffer) Buffer++; + } + + if (*Buffer == '.') { + + cmsFloat64Number frac = 0.0; // fraction + int prec = 0; // precission + + if (*Buffer) Buffer++; + + while (*Buffer && isdigit((int) *Buffer)) { + + frac = frac * 10.0 + (*Buffer - '0'); + prec++; + if (*Buffer) Buffer++; + } + + dnum = dnum + (frac / xpow10(prec)); + } + + // Exponent, example 34.00E+20 + if (*Buffer && toupper(*Buffer) == 'E') { + + int e; + int sgn; + + if (*Buffer) Buffer++; + sgn = 1; + + if (*Buffer == '-') { + + sgn = -1; + if (*Buffer) Buffer++; + } + else + if (*Buffer == '+') { + + sgn = +1; + if (*Buffer) Buffer++; + } + + e = 0; + while (*Buffer && isdigit((int) *Buffer)) { + + if ((cmsFloat64Number) e * 10L < INT_MAX) + e = e * 10 + (*Buffer - '0'); + + if (*Buffer) Buffer++; + } + + e = sgn*e; + dnum = dnum * xpow10(e); + } + + return sign * dnum; +} + + +// Reads next symbol +static +void InSymbol(cmsIT8* it8) +{ + register char *idptr; + register int k; + SYMBOL key; + int sng; + + do { + + while (isseparator(it8->ch)) + NextCh(it8); + + if (isfirstidchar(it8->ch)) { // Identifier + + k = 0; + idptr = it8->id; + + do { + + if (++k < MAXID) *idptr++ = (char) it8->ch; + + NextCh(it8); + + } while (isidchar(it8->ch)); + + *idptr = '\0'; + + + key = BinSrchKey(it8->id); + if (key == SNONE) it8->sy = SIDENT; + else it8->sy = key; + + } + else // Is a number? + if (isdigit(it8->ch) || it8->ch == '.' || it8->ch == '-' || it8->ch == '+') + { + int sign = 1; + + if (it8->ch == '-') { + sign = -1; + NextCh(it8); + } + + it8->inum = 0; + it8->sy = SINUM; + + if (it8->ch == '0') { // 0xnnnn (Hexa) or 0bnnnn (Binary) + + NextCh(it8); + if (toupper(it8->ch) == 'X') { + + int j; + + NextCh(it8); + while (isxdigit(it8->ch)) + { + it8->ch = toupper(it8->ch); + if (it8->ch >= 'A' && it8->ch <= 'F') j = it8->ch -'A'+10; + else j = it8->ch - '0'; + + if ((long) it8->inum * 16L > (long) INT_MAX) + { + SynError(it8, "Invalid hexadecimal number"); + return; + } + + it8->inum = it8->inum * 16 + j; + NextCh(it8); + } + return; + } + + if (toupper(it8->ch) == 'B') { // Binary + + int j; + + NextCh(it8); + while (it8->ch == '0' || it8->ch == '1') + { + j = it8->ch - '0'; + + if ((long) it8->inum * 2L > (long) INT_MAX) + { + SynError(it8, "Invalid binary number"); + return; + } + + it8->inum = it8->inum * 2 + j; + NextCh(it8); + } + return; + } + } + + + while (isdigit(it8->ch)) { + + if ((cmsFloat64Number) it8->inum * 10L > (cmsFloat64Number) +2147483647.0) { + ReadReal(it8, it8->inum); + it8->sy = SDNUM; + it8->dnum *= sign; + return; + } + + it8->inum = it8->inum * 10 + (it8->ch - '0'); + NextCh(it8); + } + + if (it8->ch == '.') { + + ReadReal(it8, it8->inum); + it8->sy = SDNUM; + it8->dnum *= sign; + return; + } + + it8 -> inum *= sign; + + // Special case. Numbers followed by letters are taken as identifiers + + if (isidchar(it8 ->ch)) { + + if (it8 ->sy == SINUM) { + + sprintf(it8->id, "%d", it8->inum); + } + else { + + sprintf(it8->id, it8 ->DoubleFormatter, it8->dnum); + } + + k = (int) strlen(it8 ->id); + idptr = it8 ->id + k; + do { + + if (++k < MAXID) *idptr++ = (char) it8->ch; + + NextCh(it8); + + } while (isidchar(it8->ch)); + + *idptr = '\0'; + it8->sy = SIDENT; + } + return; + + } + else + switch ((int) it8->ch) { + + // EOF marker -- ignore it + case '\x1a': + NextCh(it8); + break; + + // Eof stream markers + case 0: + case -1: + it8->sy = SEOF; + break; + + + // Next line + case '\r': + NextCh(it8); + if (it8 ->ch == '\n') + NextCh(it8); + it8->sy = SEOLN; + it8->lineno++; + break; + + case '\n': + NextCh(it8); + it8->sy = SEOLN; + it8->lineno++; + break; + + // Comment + case '#': + NextCh(it8); + while (it8->ch && it8->ch != '\n' && it8->ch != '\r') + NextCh(it8); + + it8->sy = SCOMMENT; + break; + + // String. + case '\'': + case '\"': + idptr = it8->str; + sng = it8->ch; + k = 0; + NextCh(it8); + + while (k < MAXSTR && it8->ch != sng) { + + if (it8->ch == '\n'|| it8->ch == '\r') k = MAXSTR+1; + else { + *idptr++ = (char) it8->ch; + NextCh(it8); + k++; + } + } + + it8->sy = SSTRING; + *idptr = '\0'; + NextCh(it8); + break; + + + default: + SynError(it8, "Unrecognized character: 0x%x", it8 ->ch); + return; + } + + } while (it8->sy == SCOMMENT); + + // Handle the include special token + + if (it8 -> sy == SINCLUDE) { + + FILECTX* FileNest; + + if(it8 -> IncludeSP >= (MAXINCLUDE-1)) { + + SynError(it8, "Too many recursion levels"); + return; + } + + InSymbol(it8); + if (!Check(it8, SSTRING, "Filename expected")) return; + + FileNest = it8 -> FileStack[it8 -> IncludeSP + 1]; + if(FileNest == NULL) { + + FileNest = it8 ->FileStack[it8 -> IncludeSP + 1] = (FILECTX*)AllocChunk(it8, sizeof(FILECTX)); + //if(FileNest == NULL) + // TODO: how to manage out-of-memory conditions? + } + + if (BuildAbsolutePath(it8->str, + it8->FileStack[it8->IncludeSP]->FileName, + FileNest->FileName, cmsMAX_PATH-1) == FALSE) { + SynError(it8, "File path too long"); + return; + } + + FileNest->Stream = fopen(FileNest->FileName, "rt"); + if (FileNest->Stream == NULL) { + + SynError(it8, "File %s not found", FileNest->FileName); + return; + } + it8->IncludeSP++; + + it8 ->ch = ' '; + InSymbol(it8); + } + +} + +// Checks end of line separator +static +cmsBool CheckEOLN(cmsIT8* it8) +{ + if (!Check(it8, SEOLN, "Expected separator")) return FALSE; + while (it8 -> sy == SEOLN) + InSymbol(it8); + return TRUE; + +} + +// Skip a symbol + +static +void Skip(cmsIT8* it8, SYMBOL sy) +{ + if (it8->sy == sy && it8->sy != SEOF) + InSymbol(it8); +} + + +// Skip multiple EOLN +static +void SkipEOLN(cmsIT8* it8) +{ + while (it8->sy == SEOLN) { + InSymbol(it8); + } +} + + +// Returns a string holding current value +static +cmsBool GetVal(cmsIT8* it8, char* Buffer, cmsUInt32Number max, const char* ErrorTitle) +{ + switch (it8->sy) { + + case SEOLN: // Empty value + Buffer[0]=0; + break; + case SIDENT: strncpy(Buffer, it8->id, max); + Buffer[max-1]=0; + break; + case SINUM: snprintf(Buffer, max, "%d", it8 -> inum); break; + case SDNUM: snprintf(Buffer, max, it8->DoubleFormatter, it8 -> dnum); break; + case SSTRING: strncpy(Buffer, it8->str, max); + Buffer[max-1] = 0; + break; + + + default: + return SynError(it8, "%s", ErrorTitle); + } + + Buffer[max] = 0; + return TRUE; +} + +// ---------------------------------------------------------- Table + +static +TABLE* GetTable(cmsIT8* it8) +{ + if ((it8 -> nTable >= it8 ->TablesCount)) { + + SynError(it8, "Table %d out of sequence", it8 -> nTable); + return it8 -> Tab; + } + + return it8 ->Tab + it8 ->nTable; +} + +// ---------------------------------------------------------- Memory management + + +// Frees an allocator and owned memory +void CMSEXPORT cmsIT8Free(cmsHANDLE hIT8) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + + if (it8 == NULL) + return; + + if (it8->MemorySink) { + + OWNEDMEM* p; + OWNEDMEM* n; + + for (p = it8->MemorySink; p != NULL; p = n) { + + n = p->Next; + if (p->Ptr) _cmsFree(it8 ->ContextID, p->Ptr); + _cmsFree(it8 ->ContextID, p); + } + } + + if (it8->MemoryBlock) + _cmsFree(it8 ->ContextID, it8->MemoryBlock); + + _cmsFree(it8 ->ContextID, it8); +} + + +// Allocates a chunk of data, keep linked list +static +void* AllocBigBlock(cmsIT8* it8, cmsUInt32Number size) +{ + OWNEDMEM* ptr1; + void* ptr = _cmsMallocZero(it8->ContextID, size); + + if (ptr != NULL) { + + ptr1 = (OWNEDMEM*) _cmsMallocZero(it8 ->ContextID, sizeof(OWNEDMEM)); + + if (ptr1 == NULL) { + + _cmsFree(it8 ->ContextID, ptr); + return NULL; + } + + ptr1-> Ptr = ptr; + ptr1-> Next = it8 -> MemorySink; + it8 -> MemorySink = ptr1; + } + + return ptr; +} + + +// Suballocator. +static +void* AllocChunk(cmsIT8* it8, cmsUInt32Number size) +{ + cmsUInt32Number Free = it8 ->Allocator.BlockSize - it8 ->Allocator.Used; + cmsUInt8Number* ptr; + + size = _cmsALIGNMEM(size); + + if (size > Free) { + + if (it8 -> Allocator.BlockSize == 0) + + it8 -> Allocator.BlockSize = 20*1024; + else + it8 ->Allocator.BlockSize *= 2; + + if (it8 ->Allocator.BlockSize < size) + it8 ->Allocator.BlockSize = size; + + it8 ->Allocator.Used = 0; + it8 ->Allocator.Block = (cmsUInt8Number*) AllocBigBlock(it8, it8 ->Allocator.BlockSize); + } + + ptr = it8 ->Allocator.Block + it8 ->Allocator.Used; + it8 ->Allocator.Used += size; + + return (void*) ptr; + +} + + +// Allocates a string +static +char *AllocString(cmsIT8* it8, const char* str) +{ + cmsUInt32Number Size = (cmsUInt32Number) strlen(str)+1; + char *ptr; + + + ptr = (char *) AllocChunk(it8, Size); + if (ptr) strncpy (ptr, str, Size-1); + + return ptr; +} + +// Searches through linked list + +static +cmsBool IsAvailableOnList(KEYVALUE* p, const char* Key, const char* Subkey, KEYVALUE** LastPtr) +{ + if (LastPtr) *LastPtr = p; + + for (; p != NULL; p = p->Next) { + + if (LastPtr) *LastPtr = p; + + if (*Key != '#') { // Comments are ignored + + if (cmsstrcasecmp(Key, p->Keyword) == 0) + break; + } + } + + if (p == NULL) + return FALSE; + + if (Subkey == 0) + return TRUE; + + for (; p != NULL; p = p->NextSubkey) { + + if (p ->Subkey == NULL) continue; + + if (LastPtr) *LastPtr = p; + + if (cmsstrcasecmp(Subkey, p->Subkey) == 0) + return TRUE; + } + + return FALSE; +} + + + +// Add a property into a linked list +static +KEYVALUE* AddToList(cmsIT8* it8, KEYVALUE** Head, const char *Key, const char *Subkey, const char* xValue, WRITEMODE WriteAs) +{ + KEYVALUE* p; + KEYVALUE* last; + + + // Check if property is already in list + + if (IsAvailableOnList(*Head, Key, Subkey, &p)) { + + // This may work for editing properties + + // return SynError(it8, "duplicate key <%s>", Key); + } + else { + + last = p; + + // Allocate the container + p = (KEYVALUE*) AllocChunk(it8, sizeof(KEYVALUE)); + if (p == NULL) + { + SynError(it8, "AddToList: out of memory"); + return NULL; + } + + // Store name and value + p->Keyword = AllocString(it8, Key); + p->Subkey = (Subkey == NULL) ? NULL : AllocString(it8, Subkey); + + // Keep the container in our list + if (*Head == NULL) { + *Head = p; + } + else + { + if (Subkey != NULL && last != NULL) { + + last->NextSubkey = p; + + // If Subkey is not null, then last is the last property with the same key, + // but not necessarily is the last property in the list, so we need to move + // to the actual list end + while (last->Next != NULL) + last = last->Next; + } + + if (last != NULL) last->Next = p; + } + + p->Next = NULL; + p->NextSubkey = NULL; + } + + p->WriteAs = WriteAs; + + if (xValue != NULL) { + + p->Value = AllocString(it8, xValue); + } + else { + p->Value = NULL; + } + + return p; +} + +static +KEYVALUE* AddAvailableProperty(cmsIT8* it8, const char* Key, WRITEMODE as) +{ + return AddToList(it8, &it8->ValidKeywords, Key, NULL, NULL, as); +} + + +static +KEYVALUE* AddAvailableSampleID(cmsIT8* it8, const char* Key) +{ + return AddToList(it8, &it8->ValidSampleID, Key, NULL, NULL, WRITE_UNCOOKED); +} + + +static +void AllocTable(cmsIT8* it8) +{ + TABLE* t; + + t = it8 ->Tab + it8 ->TablesCount; + + t->HeaderList = NULL; + t->DataFormat = NULL; + t->Data = NULL; + + it8 ->TablesCount++; +} + + +cmsInt32Number CMSEXPORT cmsIT8SetTable(cmsHANDLE IT8, cmsUInt32Number nTable) +{ + cmsIT8* it8 = (cmsIT8*) IT8; + + if (nTable >= it8 ->TablesCount) { + + if (nTable == it8 ->TablesCount) { + + AllocTable(it8); + } + else { + SynError(it8, "Table %d is out of sequence", nTable); + return -1; + } + } + + it8 ->nTable = nTable; + + return (cmsInt32Number) nTable; +} + + + +// Init an empty container +cmsHANDLE CMSEXPORT cmsIT8Alloc(cmsContext ContextID) +{ + cmsIT8* it8; + cmsUInt32Number i; + + it8 = (cmsIT8*) _cmsMallocZero(ContextID, sizeof(cmsIT8)); + if (it8 == NULL) return NULL; + + AllocTable(it8); + + it8->MemoryBlock = NULL; + it8->MemorySink = NULL; + + it8 ->nTable = 0; + + it8->ContextID = ContextID; + it8->Allocator.Used = 0; + it8->Allocator.Block = NULL; + it8->Allocator.BlockSize = 0; + + it8->ValidKeywords = NULL; + it8->ValidSampleID = NULL; + + it8 -> sy = SNONE; + it8 -> ch = ' '; + it8 -> Source = NULL; + it8 -> inum = 0; + it8 -> dnum = 0.0; + + it8->FileStack[0] = (FILECTX*)AllocChunk(it8, sizeof(FILECTX)); + it8->IncludeSP = 0; + it8 -> lineno = 1; + + strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT); + cmsIT8SetSheetType((cmsHANDLE) it8, "CGATS.17"); + + // Initialize predefined properties & data + + for (i=0; i < NUMPREDEFINEDPROPS; i++) + AddAvailableProperty(it8, PredefinedProperties[i].id, PredefinedProperties[i].as); + + for (i=0; i < NUMPREDEFINEDSAMPLEID; i++) + AddAvailableSampleID(it8, PredefinedSampleID[i]); + + + return (cmsHANDLE) it8; +} + + +const char* CMSEXPORT cmsIT8GetSheetType(cmsHANDLE hIT8) +{ + return GetTable((cmsIT8*) hIT8)->SheetType; +} + +cmsBool CMSEXPORT cmsIT8SetSheetType(cmsHANDLE hIT8, const char* Type) +{ + TABLE* t = GetTable((cmsIT8*) hIT8); + + strncpy(t ->SheetType, Type, MAXSTR-1); + t ->SheetType[MAXSTR-1] = 0; + return TRUE; +} + +cmsBool CMSEXPORT cmsIT8SetComment(cmsHANDLE hIT8, const char* Val) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + + if (!Val) return FALSE; + if (!*Val) return FALSE; + + return AddToList(it8, &GetTable(it8)->HeaderList, "# ", NULL, Val, WRITE_UNCOOKED) != NULL; +} + +// Sets a property +cmsBool CMSEXPORT cmsIT8SetPropertyStr(cmsHANDLE hIT8, const char* Key, const char *Val) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + + if (!Val) return FALSE; + if (!*Val) return FALSE; + + return AddToList(it8, &GetTable(it8)->HeaderList, Key, NULL, Val, WRITE_STRINGIFY) != NULL; +} + +cmsBool CMSEXPORT cmsIT8SetPropertyDbl(cmsHANDLE hIT8, const char* cProp, cmsFloat64Number Val) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + char Buffer[1024]; + + sprintf(Buffer, it8->DoubleFormatter, Val); + + return AddToList(it8, &GetTable(it8)->HeaderList, cProp, NULL, Buffer, WRITE_UNCOOKED) != NULL; +} + +cmsBool CMSEXPORT cmsIT8SetPropertyHex(cmsHANDLE hIT8, const char* cProp, cmsUInt32Number Val) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + char Buffer[1024]; + + sprintf(Buffer, "%u", Val); + + return AddToList(it8, &GetTable(it8)->HeaderList, cProp, NULL, Buffer, WRITE_HEXADECIMAL) != NULL; +} + +cmsBool CMSEXPORT cmsIT8SetPropertyUncooked(cmsHANDLE hIT8, const char* Key, const char* Buffer) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + + return AddToList(it8, &GetTable(it8)->HeaderList, Key, NULL, Buffer, WRITE_UNCOOKED) != NULL; +} + +cmsBool CMSEXPORT cmsIT8SetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char* SubKey, const char *Buffer) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + + return AddToList(it8, &GetTable(it8)->HeaderList, Key, SubKey, Buffer, WRITE_PAIR) != NULL; +} + +// Gets a property +const char* CMSEXPORT cmsIT8GetProperty(cmsHANDLE hIT8, const char* Key) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + KEYVALUE* p; + + if (IsAvailableOnList(GetTable(it8) -> HeaderList, Key, NULL, &p)) + { + return p -> Value; + } + return NULL; +} + + +cmsFloat64Number CMSEXPORT cmsIT8GetPropertyDbl(cmsHANDLE hIT8, const char* cProp) +{ + const char *v = cmsIT8GetProperty(hIT8, cProp); + + if (v == NULL) return 0.0; + + return ParseFloatNumber(v); +} + +const char* CMSEXPORT cmsIT8GetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char *SubKey) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + KEYVALUE* p; + + if (IsAvailableOnList(GetTable(it8) -> HeaderList, Key, SubKey, &p)) { + return p -> Value; + } + return NULL; +} + +// ----------------------------------------------------------------- Datasets + + +static +void AllocateDataFormat(cmsIT8* it8) +{ + TABLE* t = GetTable(it8); + + if (t -> DataFormat) return; // Already allocated + + t -> nSamples = (int) cmsIT8GetPropertyDbl(it8, "NUMBER_OF_FIELDS"); + + if (t -> nSamples <= 0) { + + SynError(it8, "AllocateDataFormat: Unknown NUMBER_OF_FIELDS"); + t -> nSamples = 10; + } + + t -> DataFormat = (char**) AllocChunk (it8, ((cmsUInt32Number) t->nSamples + 1) * sizeof(char *)); + if (t->DataFormat == NULL) { + + SynError(it8, "AllocateDataFormat: Unable to allocate dataFormat array"); + } + +} + +static +const char *GetDataFormat(cmsIT8* it8, int n) +{ + TABLE* t = GetTable(it8); + + if (t->DataFormat) + return t->DataFormat[n]; + + return NULL; +} + +static +cmsBool SetDataFormat(cmsIT8* it8, int n, const char *label) +{ + TABLE* t = GetTable(it8); + + if (!t->DataFormat) + AllocateDataFormat(it8); + + if (n > t -> nSamples) { + SynError(it8, "More than NUMBER_OF_FIELDS fields."); + return FALSE; + } + + if (t->DataFormat) { + t->DataFormat[n] = AllocString(it8, label); + } + + return TRUE; +} + + +cmsBool CMSEXPORT cmsIT8SetDataFormat(cmsHANDLE h, int n, const char *Sample) +{ + cmsIT8* it8 = (cmsIT8*) h; + return SetDataFormat(it8, n, Sample); +} + +static +void AllocateDataSet(cmsIT8* it8) +{ + TABLE* t = GetTable(it8); + + if (t -> Data) return; // Already allocated + + t-> nSamples = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS")); + t-> nPatches = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS")); + + t-> Data = (char**)AllocChunk (it8, ((cmsUInt32Number) t->nSamples + 1) * ((cmsUInt32Number) t->nPatches + 1) *sizeof (char*)); + if (t->Data == NULL) { + + SynError(it8, "AllocateDataSet: Unable to allocate data array"); + } + +} + +static +char* GetData(cmsIT8* it8, int nSet, int nField) +{ + TABLE* t = GetTable(it8); + int nSamples = t -> nSamples; + int nPatches = t -> nPatches; + + if (nSet >= nPatches || nField >= nSamples) + return NULL; + + if (!t->Data) return NULL; + return t->Data [nSet * nSamples + nField]; +} + +static +cmsBool SetData(cmsIT8* it8, int nSet, int nField, const char *Val) +{ + TABLE* t = GetTable(it8); + + if (!t->Data) + AllocateDataSet(it8); + + if (!t->Data) return FALSE; + + if (nSet > t -> nPatches || nSet < 0) { + + return SynError(it8, "Patch %d out of range, there are %d patches", nSet, t -> nPatches); + } + + if (nField > t ->nSamples || nField < 0) { + return SynError(it8, "Sample %d out of range, there are %d samples", nField, t ->nSamples); + + } + + t->Data [nSet * t -> nSamples + nField] = AllocString(it8, Val); + return TRUE; +} + + +// --------------------------------------------------------------- File I/O + + +// Writes a string to file +static +void WriteStr(SAVESTREAM* f, const char *str) +{ + cmsUInt32Number len; + + if (str == NULL) + str = " "; + + // Length to write + len = (cmsUInt32Number) strlen(str); + f ->Used += len; + + + if (f ->stream) { // Should I write it to a file? + + if (fwrite(str, 1, len, f->stream) != len) { + cmsSignalError(0, cmsERROR_WRITE, "Write to file error in CGATS parser"); + return; + } + + } + else { // Or to a memory block? + + if (f ->Base) { // Am I just counting the bytes? + + if (f ->Used > f ->Max) { + + cmsSignalError(0, cmsERROR_WRITE, "Write to memory overflows in CGATS parser"); + return; + } + + memmove(f ->Ptr, str, len); + f->Ptr += len; + } + + } +} + + +// Write formatted + +static +void Writef(SAVESTREAM* f, const char* frm, ...) +{ + char Buffer[4096]; + va_list args; + + va_start(args, frm); + vsnprintf(Buffer, 4095, frm, args); + Buffer[4095] = 0; + WriteStr(f, Buffer); + va_end(args); + +} + +// Writes full header +static +void WriteHeader(cmsIT8* it8, SAVESTREAM* fp) +{ + KEYVALUE* p; + TABLE* t = GetTable(it8); + + // Writes the type + WriteStr(fp, t->SheetType); + WriteStr(fp, "\n"); + + for (p = t->HeaderList; (p != NULL); p = p->Next) + { + if (*p ->Keyword == '#') { + + char* Pt; + + WriteStr(fp, "#\n# "); + for (Pt = p ->Value; *Pt; Pt++) { + + + Writef(fp, "%c", *Pt); + + if (*Pt == '\n') { + WriteStr(fp, "# "); + } + } + + WriteStr(fp, "\n#\n"); + continue; + } + + + if (!IsAvailableOnList(it8-> ValidKeywords, p->Keyword, NULL, NULL)) { + +#ifdef CMS_STRICT_CGATS + WriteStr(fp, "KEYWORD\t\""); + WriteStr(fp, p->Keyword); + WriteStr(fp, "\"\n"); +#endif + + AddAvailableProperty(it8, p->Keyword, WRITE_UNCOOKED); + } + + WriteStr(fp, p->Keyword); + if (p->Value) { + + switch (p ->WriteAs) { + + case WRITE_UNCOOKED: + Writef(fp, "\t%s", p ->Value); + break; + + case WRITE_STRINGIFY: + Writef(fp, "\t\"%s\"", p->Value ); + break; + + case WRITE_HEXADECIMAL: + Writef(fp, "\t0x%X", atoi(p ->Value)); + break; + + case WRITE_BINARY: + Writef(fp, "\t0x%B", atoi(p ->Value)); + break; + + case WRITE_PAIR: + Writef(fp, "\t\"%s,%s\"", p->Subkey, p->Value); + break; + + default: SynError(it8, "Unknown write mode %d", p ->WriteAs); + return; + } + } + + WriteStr (fp, "\n"); + } + +} + + +// Writes the data format +static +void WriteDataFormat(SAVESTREAM* fp, cmsIT8* it8) +{ + int i, nSamples; + TABLE* t = GetTable(it8); + + if (!t -> DataFormat) return; + + WriteStr(fp, "BEGIN_DATA_FORMAT\n"); + WriteStr(fp, " "); + nSamples = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS")); + + for (i = 0; i < nSamples; i++) { + + WriteStr(fp, t->DataFormat[i]); + WriteStr(fp, ((i == (nSamples-1)) ? "\n" : "\t")); + } + + WriteStr (fp, "END_DATA_FORMAT\n"); +} + + +// Writes data array +static +void WriteData(SAVESTREAM* fp, cmsIT8* it8) +{ + int i, j; + TABLE* t = GetTable(it8); + + if (!t->Data) return; + + WriteStr (fp, "BEGIN_DATA\n"); + + t->nPatches = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS")); + + for (i = 0; i < t-> nPatches; i++) { + + WriteStr(fp, " "); + + for (j = 0; j < t->nSamples; j++) { + + char *ptr = t->Data[i*t->nSamples+j]; + + if (ptr == NULL) WriteStr(fp, "\"\""); + else { + // If value contains whitespace, enclose within quote + + if (strchr(ptr, ' ') != NULL) { + + WriteStr(fp, "\""); + WriteStr(fp, ptr); + WriteStr(fp, "\""); + } + else + WriteStr(fp, ptr); + } + + WriteStr(fp, ((j == (t->nSamples-1)) ? "\n" : "\t")); + } + } + WriteStr (fp, "END_DATA\n"); +} + + + +// Saves whole file +cmsBool CMSEXPORT cmsIT8SaveToFile(cmsHANDLE hIT8, const char* cFileName) +{ + SAVESTREAM sd; + cmsUInt32Number i; + cmsIT8* it8 = (cmsIT8*) hIT8; + + memset(&sd, 0, sizeof(sd)); + + sd.stream = fopen(cFileName, "wt"); + if (!sd.stream) return FALSE; + + for (i=0; i < it8 ->TablesCount; i++) { + + cmsIT8SetTable(hIT8, i); + WriteHeader(it8, &sd); + WriteDataFormat(&sd, it8); + WriteData(&sd, it8); + } + + if (fclose(sd.stream) != 0) return FALSE; + + return TRUE; +} + + +// Saves to memory +cmsBool CMSEXPORT cmsIT8SaveToMem(cmsHANDLE hIT8, void *MemPtr, cmsUInt32Number* BytesNeeded) +{ + SAVESTREAM sd; + cmsUInt32Number i; + cmsIT8* it8 = (cmsIT8*) hIT8; + + memset(&sd, 0, sizeof(sd)); + + sd.stream = NULL; + sd.Base = (cmsUInt8Number*) MemPtr; + sd.Ptr = sd.Base; + + sd.Used = 0; + + if (sd.Base) + sd.Max = *BytesNeeded; // Write to memory? + else + sd.Max = 0; // Just counting the needed bytes + + for (i=0; i < it8 ->TablesCount; i++) { + + cmsIT8SetTable(hIT8, i); + WriteHeader(it8, &sd); + WriteDataFormat(&sd, it8); + WriteData(&sd, it8); + } + + sd.Used++; // The \0 at the very end + + if (sd.Base) + *sd.Ptr = 0; + + *BytesNeeded = sd.Used; + + return TRUE; +} + + +// -------------------------------------------------------------- Higer level parsing + +static +cmsBool DataFormatSection(cmsIT8* it8) +{ + int iField = 0; + TABLE* t = GetTable(it8); + + InSymbol(it8); // Eats "BEGIN_DATA_FORMAT" + CheckEOLN(it8); + + while (it8->sy != SEND_DATA_FORMAT && + it8->sy != SEOLN && + it8->sy != SEOF && + it8->sy != SSYNERROR) { + + if (it8->sy != SIDENT) { + + return SynError(it8, "Sample type expected"); + } + + if (!SetDataFormat(it8, iField, it8->id)) return FALSE; + iField++; + + InSymbol(it8); + SkipEOLN(it8); + } + + SkipEOLN(it8); + Skip(it8, SEND_DATA_FORMAT); + SkipEOLN(it8); + + if (iField != t ->nSamples) { + SynError(it8, "Count mismatch. NUMBER_OF_FIELDS was %d, found %d\n", t ->nSamples, iField); + + + } + + return TRUE; +} + + + +static +cmsBool DataSection (cmsIT8* it8) +{ + int iField = 0; + int iSet = 0; + char Buffer[256]; + TABLE* t = GetTable(it8); + + InSymbol(it8); // Eats "BEGIN_DATA" + CheckEOLN(it8); + + if (!t->Data) + AllocateDataSet(it8); + + while (it8->sy != SEND_DATA && it8->sy != SEOF) + { + if (iField >= t -> nSamples) { + iField = 0; + iSet++; + + } + + if (it8->sy != SEND_DATA && it8->sy != SEOF) { + + if (!GetVal(it8, Buffer, 255, "Sample data expected")) + return FALSE; + + if (!SetData(it8, iSet, iField, Buffer)) + return FALSE; + + iField++; + + InSymbol(it8); + SkipEOLN(it8); + } + } + + SkipEOLN(it8); + Skip(it8, SEND_DATA); + SkipEOLN(it8); + + // Check for data completion. + + if ((iSet+1) != t -> nPatches) + return SynError(it8, "Count mismatch. NUMBER_OF_SETS was %d, found %d\n", t ->nPatches, iSet+1); + + return TRUE; +} + + + + +static +cmsBool HeaderSection(cmsIT8* it8) +{ + char VarName[MAXID]; + char Buffer[MAXSTR]; + KEYVALUE* Key; + + while (it8->sy != SEOF && + it8->sy != SSYNERROR && + it8->sy != SBEGIN_DATA_FORMAT && + it8->sy != SBEGIN_DATA) { + + + switch (it8 -> sy) { + + case SKEYWORD: + InSymbol(it8); + if (!GetVal(it8, Buffer, MAXSTR-1, "Keyword expected")) return FALSE; + if (!AddAvailableProperty(it8, Buffer, WRITE_UNCOOKED)) return FALSE; + InSymbol(it8); + break; + + + case SDATA_FORMAT_ID: + InSymbol(it8); + if (!GetVal(it8, Buffer, MAXSTR-1, "Keyword expected")) return FALSE; + if (!AddAvailableSampleID(it8, Buffer)) return FALSE; + InSymbol(it8); + break; + + + case SIDENT: + strncpy(VarName, it8->id, MAXID-1); + VarName[MAXID-1] = 0; + + if (!IsAvailableOnList(it8-> ValidKeywords, VarName, NULL, &Key)) { + +#ifdef CMS_STRICT_CGATS + return SynError(it8, "Undefined keyword '%s'", VarName); +#else + Key = AddAvailableProperty(it8, VarName, WRITE_UNCOOKED); + if (Key == NULL) return FALSE; +#endif + } + + InSymbol(it8); + if (!GetVal(it8, Buffer, MAXSTR-1, "Property data expected")) return FALSE; + + if(Key->WriteAs != WRITE_PAIR) { + AddToList(it8, &GetTable(it8)->HeaderList, VarName, NULL, Buffer, + (it8->sy == SSTRING) ? WRITE_STRINGIFY : WRITE_UNCOOKED); + } + else { + const char *Subkey; + char *Nextkey; + if (it8->sy != SSTRING) + return SynError(it8, "Invalid value '%s' for property '%s'.", Buffer, VarName); + + // chop the string as a list of "subkey, value" pairs, using ';' as a separator + for (Subkey = Buffer; Subkey != NULL; Subkey = Nextkey) + { + char *Value, *temp; + + // identify token pair boundary + Nextkey = (char*) strchr(Subkey, ';'); + if(Nextkey) + *Nextkey++ = '\0'; + + // for each pair, split the subkey and the value + Value = (char*) strrchr(Subkey, ','); + if(Value == NULL) + return SynError(it8, "Invalid value for property '%s'.", VarName); + + // gobble the spaces before the coma, and the coma itself + temp = Value++; + do *temp-- = '\0'; while(temp >= Subkey && *temp == ' '); + + // gobble any space at the right + temp = Value + strlen(Value) - 1; + while(*temp == ' ') *temp-- = '\0'; + + // trim the strings from the left + Subkey += strspn(Subkey, " "); + Value += strspn(Value, " "); + + if(Subkey[0] == 0 || Value[0] == 0) + return SynError(it8, "Invalid value for property '%s'.", VarName); + AddToList(it8, &GetTable(it8)->HeaderList, VarName, Subkey, Value, WRITE_PAIR); + } + } + + InSymbol(it8); + break; + + + case SEOLN: break; + + default: + return SynError(it8, "expected keyword or identifier"); + } + + SkipEOLN(it8); + } + + return TRUE; + +} + + +static +void ReadType(cmsIT8* it8, char* SheetTypePtr) +{ + // First line is a very special case. + + while (isseparator(it8->ch)) + NextCh(it8); + + while (it8->ch != '\r' && it8 ->ch != '\n' && it8->ch != '\t' && it8 -> ch != -1) { + + *SheetTypePtr++= (char) it8 ->ch; + NextCh(it8); + } + + *SheetTypePtr = 0; +} + + +static +cmsBool ParseIT8(cmsIT8* it8, cmsBool nosheet) +{ + char* SheetTypePtr = it8 ->Tab[0].SheetType; + + if (nosheet == 0) { + ReadType(it8, SheetTypePtr); + } + + InSymbol(it8); + + SkipEOLN(it8); + + while (it8-> sy != SEOF && + it8-> sy != SSYNERROR) { + + switch (it8 -> sy) { + + case SBEGIN_DATA_FORMAT: + if (!DataFormatSection(it8)) return FALSE; + break; + + case SBEGIN_DATA: + + if (!DataSection(it8)) return FALSE; + + if (it8 -> sy != SEOF) { + + AllocTable(it8); + it8 ->nTable = it8 ->TablesCount - 1; + + // Read sheet type if present. We only support identifier and string. + // is a type string + // anything else, is not a type string + if (nosheet == 0) { + + if (it8 ->sy == SIDENT) { + + // May be a type sheet or may be a prop value statement. We cannot use insymbol in + // this special case... + while (isseparator(it8->ch)) + NextCh(it8); + + // If a newline is found, then this is a type string + if (it8 ->ch == '\n' || it8->ch == '\r') { + + cmsIT8SetSheetType(it8, it8 ->id); + InSymbol(it8); + } + else + { + // It is not. Just continue + cmsIT8SetSheetType(it8, ""); + } + } + else + // Validate quoted strings + if (it8 ->sy == SSTRING) { + cmsIT8SetSheetType(it8, it8 ->str); + InSymbol(it8); + } + } + + } + break; + + case SEOLN: + SkipEOLN(it8); + break; + + default: + if (!HeaderSection(it8)) return FALSE; + } + + } + + return (it8 -> sy != SSYNERROR); +} + + + +// Init usefull pointers + +static +void CookPointers(cmsIT8* it8) +{ + int idField, i; + char* Fld; + cmsUInt32Number j; + cmsUInt32Number nOldTable = it8 ->nTable; + + for (j=0; j < it8 ->TablesCount; j++) { + + TABLE* t = it8 ->Tab + j; + + t -> SampleID = 0; + it8 ->nTable = j; + + for (idField = 0; idField < t -> nSamples; idField++) + { + if (t ->DataFormat == NULL){ + SynError(it8, "Undefined DATA_FORMAT"); + return; + } + + Fld = t->DataFormat[idField]; + if (!Fld) continue; + + + if (cmsstrcasecmp(Fld, "SAMPLE_ID") == 0) { + + t -> SampleID = idField; + + for (i=0; i < t -> nPatches; i++) { + + char *Data = GetData(it8, i, idField); + if (Data) { + char Buffer[256]; + + strncpy(Buffer, Data, 255); + Buffer[255] = 0; + + if (strlen(Buffer) <= strlen(Data)) + strcpy(Data, Buffer); + else + SetData(it8, i, idField, Buffer); + + } + } + + } + + // "LABEL" is an extension. It keeps references to forward tables + + if ((cmsstrcasecmp(Fld, "LABEL") == 0) || Fld[0] == '$' ) { + + // Search for table references... + for (i=0; i < t -> nPatches; i++) { + + char *Label = GetData(it8, i, idField); + + if (Label) { + + cmsUInt32Number k; + + // This is the label, search for a table containing + // this property + + for (k=0; k < it8 ->TablesCount; k++) { + + TABLE* Table = it8 ->Tab + k; + KEYVALUE* p; + + if (IsAvailableOnList(Table->HeaderList, Label, NULL, &p)) { + + // Available, keep type and table + char Buffer[256]; + + char *Type = p ->Value; + int nTable = (int) k; + + snprintf(Buffer, 255, "%s %d %s", Label, nTable, Type ); + + SetData(it8, i, idField, Buffer); + } + } + + + } + + } + + + } + + } + } + + it8 ->nTable = nOldTable; +} + +// Try to infere if the file is a CGATS/IT8 file at all. Read first line +// that should be something like some printable characters plus a \n +// returns 0 if this is not like a CGATS, or an integer otherwise. This integer is the number of words in first line? +static +int IsMyBlock(cmsUInt8Number* Buffer, int n) +{ + int words = 1, space = 0, quot = 0; + int i; + + if (n < 10) return 0; // Too small + + if (n > 132) + n = 132; + + for (i = 1; i < n; i++) { + + switch(Buffer[i]) + { + case '\n': + case '\r': + return ((quot == 1) || (words > 2)) ? 0 : words; + case '\t': + case ' ': + if(!quot && !space) + space = 1; + break; + case '\"': + quot = !quot; + break; + default: + if (Buffer[i] < 32) return 0; + if (Buffer[i] > 127) return 0; + words += space; + space = 0; + break; + } + } + + return 0; +} + + +static +cmsBool IsMyFile(const char* FileName) +{ + FILE *fp; + cmsUInt32Number Size; + cmsUInt8Number Ptr[133]; + + fp = fopen(FileName, "rt"); + if (!fp) { + cmsSignalError(0, cmsERROR_FILE, "File '%s' not found", FileName); + return FALSE; + } + + Size = (cmsUInt32Number) fread(Ptr, 1, 132, fp); + + if (fclose(fp) != 0) + return FALSE; + + Ptr[Size] = '\0'; + + return IsMyBlock(Ptr, Size); +} + +// ---------------------------------------------------------- Exported routines + + +cmsHANDLE CMSEXPORT cmsIT8LoadFromMem(cmsContext ContextID, void *Ptr, cmsUInt32Number len) +{ + cmsHANDLE hIT8; + cmsIT8* it8; + int type; + + _cmsAssert(Ptr != NULL); + _cmsAssert(len != 0); + + type = IsMyBlock((cmsUInt8Number*)Ptr, len); + if (type == 0) return NULL; + + hIT8 = cmsIT8Alloc(ContextID); + if (!hIT8) return NULL; + + it8 = (cmsIT8*) hIT8; + it8 ->MemoryBlock = (char*) _cmsMalloc(ContextID, len + 1); + + strncpy(it8 ->MemoryBlock, (const char*) Ptr, len); + it8 ->MemoryBlock[len] = 0; + + strncpy(it8->FileStack[0]->FileName, "", cmsMAX_PATH-1); + it8-> Source = it8 -> MemoryBlock; + + if (!ParseIT8(it8, type-1)) { + + cmsIT8Free(hIT8); + return FALSE; + } + + CookPointers(it8); + it8 ->nTable = 0; + + _cmsFree(ContextID, it8->MemoryBlock); + it8 -> MemoryBlock = NULL; + + return hIT8; + + +} + + +cmsHANDLE CMSEXPORT cmsIT8LoadFromFile(cmsContext ContextID, const char* cFileName) +{ + + cmsHANDLE hIT8; + cmsIT8* it8; + int type; + + _cmsAssert(cFileName != NULL); + + type = IsMyFile(cFileName); + if (type == 0) return NULL; + + hIT8 = cmsIT8Alloc(ContextID); + it8 = (cmsIT8*) hIT8; + if (!hIT8) return NULL; + + + it8 ->FileStack[0]->Stream = fopen(cFileName, "rt"); + + if (!it8 ->FileStack[0]->Stream) { + cmsIT8Free(hIT8); + return NULL; + } + + + strncpy(it8->FileStack[0]->FileName, cFileName, cmsMAX_PATH-1); + it8->FileStack[0]->FileName[cmsMAX_PATH-1] = 0; + + if (!ParseIT8(it8, type-1)) { + + fclose(it8 ->FileStack[0]->Stream); + cmsIT8Free(hIT8); + return NULL; + } + + CookPointers(it8); + it8 ->nTable = 0; + + if (fclose(it8 ->FileStack[0]->Stream)!= 0) { + cmsIT8Free(hIT8); + return NULL; + } + + return hIT8; + +} + +int CMSEXPORT cmsIT8EnumDataFormat(cmsHANDLE hIT8, char ***SampleNames) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + TABLE* t; + + _cmsAssert(hIT8 != NULL); + + t = GetTable(it8); + + if (SampleNames) + *SampleNames = t -> DataFormat; + return t -> nSamples; +} + + +cmsUInt32Number CMSEXPORT cmsIT8EnumProperties(cmsHANDLE hIT8, char ***PropertyNames) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + KEYVALUE* p; + cmsUInt32Number n; + char **Props; + TABLE* t; + + _cmsAssert(hIT8 != NULL); + + t = GetTable(it8); + + // Pass#1 - count properties + + n = 0; + for (p = t -> HeaderList; p != NULL; p = p->Next) { + n++; + } + + + Props = (char **) AllocChunk(it8, sizeof(char *) * n); + + // Pass#2 - Fill pointers + n = 0; + for (p = t -> HeaderList; p != NULL; p = p->Next) { + Props[n++] = p -> Keyword; + } + + *PropertyNames = Props; + return n; +} + +cmsUInt32Number CMSEXPORT cmsIT8EnumPropertyMulti(cmsHANDLE hIT8, const char* cProp, const char ***SubpropertyNames) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + KEYVALUE *p, *tmp; + cmsUInt32Number n; + const char **Props; + TABLE* t; + + _cmsAssert(hIT8 != NULL); + + + t = GetTable(it8); + + if(!IsAvailableOnList(t->HeaderList, cProp, NULL, &p)) { + *SubpropertyNames = 0; + return 0; + } + + // Pass#1 - count properties + + n = 0; + for (tmp = p; tmp != NULL; tmp = tmp->NextSubkey) { + if(tmp->Subkey != NULL) + n++; + } + + + Props = (const char **) AllocChunk(it8, sizeof(char *) * n); + + // Pass#2 - Fill pointers + n = 0; + for (tmp = p; tmp != NULL; tmp = tmp->NextSubkey) { + if(tmp->Subkey != NULL) + Props[n++] = p ->Subkey; + } + + *SubpropertyNames = Props; + return n; +} + +static +int LocatePatch(cmsIT8* it8, const char* cPatch) +{ + int i; + const char *data; + TABLE* t = GetTable(it8); + + for (i=0; i < t-> nPatches; i++) { + + data = GetData(it8, i, t->SampleID); + + if (data != NULL) { + + if (cmsstrcasecmp(data, cPatch) == 0) + return i; + } + } + + // SynError(it8, "Couldn't find patch '%s'\n", cPatch); + return -1; +} + + +static +int LocateEmptyPatch(cmsIT8* it8) +{ + int i; + const char *data; + TABLE* t = GetTable(it8); + + for (i=0; i < t-> nPatches; i++) { + + data = GetData(it8, i, t->SampleID); + + if (data == NULL) + return i; + + } + + return -1; +} + +static +int LocateSample(cmsIT8* it8, const char* cSample) +{ + int i; + const char *fld; + TABLE* t = GetTable(it8); + + for (i=0; i < t->nSamples; i++) { + + fld = GetDataFormat(it8, i); + if (cmsstrcasecmp(fld, cSample) == 0) + return i; + } + + return -1; + +} + + +int CMSEXPORT cmsIT8FindDataFormat(cmsHANDLE hIT8, const char* cSample) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + + _cmsAssert(hIT8 != NULL); + + return LocateSample(it8, cSample); +} + + + +const char* CMSEXPORT cmsIT8GetDataRowCol(cmsHANDLE hIT8, int row, int col) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + + _cmsAssert(hIT8 != NULL); + + return GetData(it8, row, col); +} + + +cmsFloat64Number CMSEXPORT cmsIT8GetDataRowColDbl(cmsHANDLE hIT8, int row, int col) +{ + const char* Buffer; + + Buffer = cmsIT8GetDataRowCol(hIT8, row, col); + + if (Buffer == NULL) return 0.0; + + return ParseFloatNumber(Buffer); +} + + +cmsBool CMSEXPORT cmsIT8SetDataRowCol(cmsHANDLE hIT8, int row, int col, const char* Val) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + + _cmsAssert(hIT8 != NULL); + + return SetData(it8, row, col, Val); +} + + +cmsBool CMSEXPORT cmsIT8SetDataRowColDbl(cmsHANDLE hIT8, int row, int col, cmsFloat64Number Val) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + char Buff[256]; + + _cmsAssert(hIT8 != NULL); + + sprintf(Buff, it8->DoubleFormatter, Val); + + return SetData(it8, row, col, Buff); +} + + + +const char* CMSEXPORT cmsIT8GetData(cmsHANDLE hIT8, const char* cPatch, const char* cSample) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + int iField, iSet; + + _cmsAssert(hIT8 != NULL); + + iField = LocateSample(it8, cSample); + if (iField < 0) { + return NULL; + } + + iSet = LocatePatch(it8, cPatch); + if (iSet < 0) { + return NULL; + } + + return GetData(it8, iSet, iField); +} + + +cmsFloat64Number CMSEXPORT cmsIT8GetDataDbl(cmsHANDLE it8, const char* cPatch, const char* cSample) +{ + const char* Buffer; + + Buffer = cmsIT8GetData(it8, cPatch, cSample); + + return ParseFloatNumber(Buffer); +} + + + +cmsBool CMSEXPORT cmsIT8SetData(cmsHANDLE hIT8, const char* cPatch, const char* cSample, const char *Val) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + int iField, iSet; + TABLE* t; + + _cmsAssert(hIT8 != NULL); + + t = GetTable(it8); + + iField = LocateSample(it8, cSample); + + if (iField < 0) + return FALSE; + + if (t-> nPatches == 0) { + + AllocateDataFormat(it8); + AllocateDataSet(it8); + CookPointers(it8); + } + + if (cmsstrcasecmp(cSample, "SAMPLE_ID") == 0) { + + iSet = LocateEmptyPatch(it8); + if (iSet < 0) { + return SynError(it8, "Couldn't add more patches '%s'\n", cPatch); + } + + iField = t -> SampleID; + } + else { + iSet = LocatePatch(it8, cPatch); + if (iSet < 0) { + return FALSE; + } + } + + return SetData(it8, iSet, iField, Val); +} + + +cmsBool CMSEXPORT cmsIT8SetDataDbl(cmsHANDLE hIT8, const char* cPatch, + const char* cSample, + cmsFloat64Number Val) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + char Buff[256]; + + _cmsAssert(hIT8 != NULL); + + snprintf(Buff, 255, it8->DoubleFormatter, Val); + return cmsIT8SetData(hIT8, cPatch, cSample, Buff); +} + +// Buffer should get MAXSTR at least + +const char* CMSEXPORT cmsIT8GetPatchName(cmsHANDLE hIT8, int nPatch, char* buffer) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + TABLE* t; + char* Data; + + _cmsAssert(hIT8 != NULL); + + t = GetTable(it8); + Data = GetData(it8, nPatch, t->SampleID); + + if (!Data) return NULL; + if (!buffer) return Data; + + strncpy(buffer, Data, MAXSTR-1); + buffer[MAXSTR-1] = 0; + return buffer; +} + +int CMSEXPORT cmsIT8GetPatchByName(cmsHANDLE hIT8, const char *cPatch) +{ + _cmsAssert(hIT8 != NULL); + + return LocatePatch((cmsIT8*)hIT8, cPatch); +} + +cmsUInt32Number CMSEXPORT cmsIT8TableCount(cmsHANDLE hIT8) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + + _cmsAssert(hIT8 != NULL); + + return it8 ->TablesCount; +} + +// This handles the "LABEL" extension. +// Label, nTable, Type + +int CMSEXPORT cmsIT8SetTableByLabel(cmsHANDLE hIT8, const char* cSet, const char* cField, const char* ExpectedType) +{ + const char* cLabelFld; + char Type[256], Label[256]; + int nTable; + + _cmsAssert(hIT8 != NULL); + + if (cField != NULL && *cField == 0) + cField = "LABEL"; + + if (cField == NULL) + cField = "LABEL"; + + cLabelFld = cmsIT8GetData(hIT8, cSet, cField); + if (!cLabelFld) return -1; + + if (sscanf(cLabelFld, "%255s %d %255s", Label, &nTable, Type) != 3) + return -1; + + if (ExpectedType != NULL && *ExpectedType == 0) + ExpectedType = NULL; + + if (ExpectedType) { + + if (cmsstrcasecmp(Type, ExpectedType) != 0) return -1; + } + + return cmsIT8SetTable(hIT8, nTable); +} + + +cmsBool CMSEXPORT cmsIT8SetIndexColumn(cmsHANDLE hIT8, const char* cSample) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + int pos; + + _cmsAssert(hIT8 != NULL); + + pos = LocateSample(it8, cSample); + if(pos == -1) + return FALSE; + + it8->Tab[it8->nTable].SampleID = pos; + return TRUE; +} + + +void CMSEXPORT cmsIT8DefineDblFormat(cmsHANDLE hIT8, const char* Formatter) +{ + cmsIT8* it8 = (cmsIT8*) hIT8; + + _cmsAssert(hIT8 != NULL); + + if (Formatter == NULL) + strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT); + else + strncpy(it8->DoubleFormatter, Formatter, sizeof(it8->DoubleFormatter)); + + it8 ->DoubleFormatter[sizeof(it8 ->DoubleFormatter)-1] = 0; +} diff --git a/third_party/lcms/src/cmscnvrt.c b/third_party/lcms/src/cmscnvrt.c new file mode 100644 index 0000000000..1a93e83f90 --- /dev/null +++ b/third_party/lcms/src/cmscnvrt.c @@ -0,0 +1,1142 @@ +//--------------------------------------------------------------------------------- +// +// 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" + + +// Link several profiles to obtain a single LUT modelling the whole color transform. Intents, Black point +// compensation and Adaptation parameters may vary across profiles. BPC and Adaptation refers to the PCS +// after the profile. I.e, BPC[0] refers to connexion between profile(0) and profile(1) +cmsPipeline* _cmsLinkProfiles(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number Intents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags); + +//--------------------------------------------------------------------------------- + +// This is the default routine for ICC-style intents. A user may decide to override it by using a plugin. +// Supported intents are perceptual, relative colorimetric, saturation and ICC-absolute colorimetric +static +cmsPipeline* DefaultICCintents(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number Intents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags); + +//--------------------------------------------------------------------------------- + +// This is the entry for black-preserving K-only intents, which are non-ICC. Last profile have to be a output profile +// to do the trick (no devicelinks allowed at that position) +static +cmsPipeline* BlackPreservingKOnlyIntents(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number Intents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags); + +//--------------------------------------------------------------------------------- + +// This is the entry for black-plane preserving, which are non-ICC. Again, Last profile have to be a output profile +// to do the trick (no devicelinks allowed at that position) +static +cmsPipeline* BlackPreservingKPlaneIntents(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number Intents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags); + +//--------------------------------------------------------------------------------- + + +// This is a structure holding implementations for all supported intents. +typedef struct _cms_intents_list { + + cmsUInt32Number Intent; + char Description[256]; + cmsIntentFn Link; + struct _cms_intents_list* Next; + +} cmsIntentsList; + + +// Built-in intents +static cmsIntentsList DefaultIntents[] = { + + { INTENT_PERCEPTUAL, "Perceptual", DefaultICCintents, &DefaultIntents[1] }, + { INTENT_RELATIVE_COLORIMETRIC, "Relative colorimetric", DefaultICCintents, &DefaultIntents[2] }, + { INTENT_SATURATION, "Saturation", DefaultICCintents, &DefaultIntents[3] }, + { INTENT_ABSOLUTE_COLORIMETRIC, "Absolute colorimetric", DefaultICCintents, &DefaultIntents[4] }, + { INTENT_PRESERVE_K_ONLY_PERCEPTUAL, "Perceptual preserving black ink", BlackPreservingKOnlyIntents, &DefaultIntents[5] }, + { INTENT_PRESERVE_K_ONLY_RELATIVE_COLORIMETRIC, "Relative colorimetric preserving black ink", BlackPreservingKOnlyIntents, &DefaultIntents[6] }, + { INTENT_PRESERVE_K_ONLY_SATURATION, "Saturation preserving black ink", BlackPreservingKOnlyIntents, &DefaultIntents[7] }, + { INTENT_PRESERVE_K_PLANE_PERCEPTUAL, "Perceptual preserving black plane", BlackPreservingKPlaneIntents, &DefaultIntents[8] }, + { INTENT_PRESERVE_K_PLANE_RELATIVE_COLORIMETRIC,"Relative colorimetric preserving black plane", BlackPreservingKPlaneIntents, &DefaultIntents[9] }, + { INTENT_PRESERVE_K_PLANE_SATURATION, "Saturation preserving black plane", BlackPreservingKPlaneIntents, NULL } +}; + + +// A pointer to the begining of the list +_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(cmsContext ContextID, cmsUInt32Number Intent) +{ + _cmsIntentsPluginChunkType* ctx = ( _cmsIntentsPluginChunkType*) _cmsContextGetClientChunk(ContextID, IntentPlugin); + cmsIntentsList* pt; + + 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; +} + +// Black point compensation. Implemented as a linear scaling in XYZ. Black points +// should come relative to the white point. Fills an matrix/offset element m +// which is organized as a 4x4 matrix. +static +void ComputeBlackPointCompensation(const cmsCIEXYZ* BlackPointIn, + const cmsCIEXYZ* BlackPointOut, + cmsMAT3* m, cmsVEC3* off) +{ + cmsFloat64Number ax, ay, az, bx, by, bz, tx, ty, tz; + + // Now we need to compute a matrix plus an offset m and of such of + // [m]*bpin + off = bpout + // [m]*D50 + off = D50 + // + // This is a linear scaling in the form ax+b, where + // a = (bpout - D50) / (bpin - D50) + // b = - D50* (bpout - bpin) / (bpin - D50) + + tx = BlackPointIn->X - cmsD50_XYZ()->X; + ty = BlackPointIn->Y - cmsD50_XYZ()->Y; + tz = BlackPointIn->Z - cmsD50_XYZ()->Z; + + ax = (BlackPointOut->X - cmsD50_XYZ()->X) / tx; + ay = (BlackPointOut->Y - cmsD50_XYZ()->Y) / ty; + az = (BlackPointOut->Z - cmsD50_XYZ()->Z) / tz; + + bx = - cmsD50_XYZ()-> X * (BlackPointOut->X - BlackPointIn->X) / tx; + by = - cmsD50_XYZ()-> Y * (BlackPointOut->Y - BlackPointIn->Y) / ty; + bz = - cmsD50_XYZ()-> Z * (BlackPointOut->Z - BlackPointIn->Z) / tz; + + _cmsVEC3init(&m ->v[0], ax, 0, 0); + _cmsVEC3init(&m ->v[1], 0, ay, 0); + _cmsVEC3init(&m ->v[2], 0, 0, az); + _cmsVEC3init(off, bx, by, bz); + +} + + +// Approximate a blackbody illuminant based on CHAD information +static +cmsFloat64Number CHAD2Temp(const cmsMAT3* Chad) +{ + // Convert D50 across inverse CHAD to get the absolute white point + cmsVEC3 d, s; + cmsCIEXYZ Dest; + cmsCIExyY DestChromaticity; + cmsFloat64Number TempK; + cmsMAT3 m1, m2; + + m1 = *Chad; + if (!_cmsMAT3inverse(&m1, &m2)) return FALSE; + + s.n[VX] = cmsD50_XYZ() -> X; + s.n[VY] = cmsD50_XYZ() -> Y; + s.n[VZ] = cmsD50_XYZ() -> Z; + + _cmsMAT3eval(&d, &m2, &s); + + Dest.X = d.n[VX]; + Dest.Y = d.n[VY]; + Dest.Z = d.n[VZ]; + + cmsXYZ2xyY(&DestChromaticity, &Dest); + + if (!cmsTempFromWhitePoint(&TempK, &DestChromaticity)) + return -1.0; + + return TempK; +} + +// Compute a CHAD based on a given temperature +static + void Temp2CHAD(cmsMAT3* Chad, cmsFloat64Number Temp) +{ + cmsCIEXYZ White; + cmsCIExyY ChromaticityOfWhite; + + cmsWhitePointFromTemp(&ChromaticityOfWhite, Temp); + cmsxyY2XYZ(&White, &ChromaticityOfWhite); + _cmsAdaptationMatrix(Chad, NULL, &White, cmsD50_XYZ()); +} + +// Join scalings to obtain relative input to absolute and then to relative output. +// Result is stored in a 3x3 matrix +static +cmsBool ComputeAbsoluteIntent(cmsFloat64Number AdaptationState, + const cmsCIEXYZ* WhitePointIn, + const cmsMAT3* ChromaticAdaptationMatrixIn, + const cmsCIEXYZ* WhitePointOut, + const cmsMAT3* ChromaticAdaptationMatrixOut, + cmsMAT3* m) +{ + cmsMAT3 Scale, m1, m2, m3, m4; + + // Adaptation state + if (AdaptationState == 1.0) { + + // Observer is fully adapted. Keep chromatic adaptation. + // That is the standard V4 behaviour + _cmsVEC3init(&m->v[0], WhitePointIn->X / WhitePointOut->X, 0, 0); + _cmsVEC3init(&m->v[1], 0, WhitePointIn->Y / WhitePointOut->Y, 0); + _cmsVEC3init(&m->v[2], 0, 0, WhitePointIn->Z / WhitePointOut->Z); + + } + else { + + // Incomplete adaptation. This is an advanced feature. + _cmsVEC3init(&Scale.v[0], WhitePointIn->X / WhitePointOut->X, 0, 0); + _cmsVEC3init(&Scale.v[1], 0, WhitePointIn->Y / WhitePointOut->Y, 0); + _cmsVEC3init(&Scale.v[2], 0, 0, WhitePointIn->Z / WhitePointOut->Z); + + + if (AdaptationState == 0.0) { + + m1 = *ChromaticAdaptationMatrixOut; + _cmsMAT3per(&m2, &m1, &Scale); + // m2 holds CHAD from output white to D50 times abs. col. scaling + + // Observer is not adapted, undo the chromatic adaptation + _cmsMAT3per(m, &m2, ChromaticAdaptationMatrixOut); + + m3 = *ChromaticAdaptationMatrixIn; + if (!_cmsMAT3inverse(&m3, &m4)) return FALSE; + _cmsMAT3per(m, &m2, &m4); + + } else { + + cmsMAT3 MixedCHAD; + cmsFloat64Number TempSrc, TempDest, Temp; + + m1 = *ChromaticAdaptationMatrixIn; + if (!_cmsMAT3inverse(&m1, &m2)) return FALSE; + _cmsMAT3per(&m3, &m2, &Scale); + // m3 holds CHAD from input white to D50 times abs. col. scaling + + TempSrc = CHAD2Temp(ChromaticAdaptationMatrixIn); + TempDest = CHAD2Temp(ChromaticAdaptationMatrixOut); + + if (TempSrc < 0.0 || TempDest < 0.0) return FALSE; // Something went wrong + + if (_cmsMAT3isIdentity(&Scale) && fabs(TempSrc - TempDest) < 0.01) { + + _cmsMAT3identity(m); + return TRUE; + } + + Temp = (1.0 - AdaptationState) * TempDest + AdaptationState * TempSrc; + + // Get a CHAD from whatever output temperature to D50. This replaces output CHAD + Temp2CHAD(&MixedCHAD, Temp); + + _cmsMAT3per(m, &m3, &MixedCHAD); + } + + } + return TRUE; + +} + +// Just to see if m matrix should be applied +static +cmsBool IsEmptyLayer(cmsMAT3* m, cmsVEC3* off) +{ + cmsFloat64Number diff = 0; + cmsMAT3 Ident; + int i; + + if (m == NULL && off == NULL) return TRUE; // NULL is allowed as an empty layer + if (m == NULL && off != NULL) return FALSE; // This is an internal error + + _cmsMAT3identity(&Ident); + + for (i=0; i < 3*3; i++) + diff += fabs(((cmsFloat64Number*)m)[i] - ((cmsFloat64Number*)&Ident)[i]); + + for (i=0; i < 3; i++) + diff += fabs(((cmsFloat64Number*)off)[i]); + + + return (diff < 0.002); +} + + +// Compute the conversion layer +static +cmsBool ComputeConversion(int i, cmsHPROFILE hProfiles[], + cmsUInt32Number Intent, + cmsBool BPC, + cmsFloat64Number AdaptationState, + cmsMAT3* m, cmsVEC3* off) +{ + + int k; + + // m and off are set to identity and this is detected latter on + _cmsMAT3identity(m); + _cmsVEC3init(off, 0, 0, 0); + + // If intent is abs. colorimetric, + if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) { + + cmsCIEXYZ WhitePointIn, WhitePointOut; + cmsMAT3 ChromaticAdaptationMatrixIn, ChromaticAdaptationMatrixOut; + + _cmsReadMediaWhitePoint(&WhitePointIn, hProfiles[i-1]); + _cmsReadCHAD(&ChromaticAdaptationMatrixIn, hProfiles[i-1]); + + _cmsReadMediaWhitePoint(&WhitePointOut, hProfiles[i]); + _cmsReadCHAD(&ChromaticAdaptationMatrixOut, hProfiles[i]); + + if (!ComputeAbsoluteIntent(AdaptationState, + &WhitePointIn, &ChromaticAdaptationMatrixIn, + &WhitePointOut, &ChromaticAdaptationMatrixOut, m)) return FALSE; + + } + else { + // Rest of intents may apply BPC. + + if (BPC) { + + cmsCIEXYZ BlackPointIn, BlackPointOut; + + cmsDetectBlackPoint(&BlackPointIn, hProfiles[i-1], Intent, 0); + cmsDetectDestinationBlackPoint(&BlackPointOut, hProfiles[i], Intent, 0); + + // If black points are equal, then do nothing + if (BlackPointIn.X != BlackPointOut.X || + BlackPointIn.Y != BlackPointOut.Y || + BlackPointIn.Z != BlackPointOut.Z) + ComputeBlackPointCompensation(&BlackPointIn, &BlackPointOut, m, off); + } + } + + // Offset should be adjusted because the encoding. We encode XYZ normalized to 0..1.0, + // to do that, we divide by MAX_ENCODEABLE_XZY. The conversion stage goes XYZ -> XYZ so + // we have first to convert from encoded to XYZ and then convert back to encoded. + // y = Mx + Off + // x = x'c + // y = M x'c + Off + // y = y'c; y' = y / c + // y' = (Mx'c + Off) /c = Mx' + (Off / c) + + for (k=0; k < 3; k++) { + off ->n[k] /= MAX_ENCODEABLE_XYZ; + } + + return TRUE; +} + + +// Add a conversion stage if needed. If a matrix/offset m is given, it applies to XYZ space +static +cmsBool AddConversion(cmsPipeline* Result, cmsColorSpaceSignature InPCS, cmsColorSpaceSignature OutPCS, cmsMAT3* m, cmsVEC3* off) +{ + cmsFloat64Number* m_as_dbl = (cmsFloat64Number*) m; + cmsFloat64Number* off_as_dbl = (cmsFloat64Number*) off; + + // Handle PCS mismatches. A specialized stage is added to the LUT in such case + switch (InPCS) { + + case cmsSigXYZData: // Input profile operates in XYZ + + switch (OutPCS) { + + case cmsSigXYZData: // XYZ -> XYZ + if (!IsEmptyLayer(m, off) && + !cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl))) + return FALSE; + break; + + case cmsSigLabData: // XYZ -> Lab + if (!IsEmptyLayer(m, off) && + !cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl))) + return FALSE; + if (!cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocXYZ2Lab(Result ->ContextID))) + return FALSE; + break; + + default: + return FALSE; // Colorspace mismatch + } + break; + + case cmsSigLabData: // Input profile operates in Lab + + switch (OutPCS) { + + case cmsSigXYZData: // Lab -> XYZ + + if (!cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocLab2XYZ(Result ->ContextID))) + return FALSE; + if (!IsEmptyLayer(m, off) && + !cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl))) + return FALSE; + break; + + case cmsSigLabData: // Lab -> Lab + + if (!IsEmptyLayer(m, off)) { + if (!cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocLab2XYZ(Result ->ContextID)) || + !cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl)) || + !cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocXYZ2Lab(Result ->ContextID))) + return FALSE; + } + break; + + default: + return FALSE; // Mismatch + } + break; + + // On colorspaces other than PCS, check for same space + default: + if (InPCS != OutPCS) return FALSE; + break; + } + + return TRUE; +} + + +// Is a given space compatible with another? +static +cmsBool ColorSpaceIsCompatible(cmsColorSpaceSignature a, cmsColorSpaceSignature b) +{ + // If they are same, they are compatible. + if (a == b) return TRUE; + + // Check for MCH4 substitution of CMYK + if ((a == cmsSig4colorData) && (b == cmsSigCmykData)) return TRUE; + if ((a == cmsSigCmykData) && (b == cmsSig4colorData)) return TRUE; + + // Check for XYZ/Lab. Those spaces are interchangeable as they can be computed one from other. + if ((a == cmsSigXYZData) && (b == cmsSigLabData)) return TRUE; + if ((a == cmsSigLabData) && (b == cmsSigXYZData)) return TRUE; + + return FALSE; +} + + +// Default handler for ICC-style intents +static +cmsPipeline* DefaultICCintents(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number TheIntents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) +{ + cmsPipeline* Lut = NULL; + cmsPipeline* Result; + cmsHPROFILE hProfile; + cmsMAT3 m; + cmsVEC3 off; + cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut, CurrentColorSpace; + cmsProfileClassSignature ClassSig; + cmsUInt32Number i, Intent; + + // For safety + if (nProfiles == 0) return NULL; + + // Allocate an empty LUT for holding the result. 0 as channel count means 'undefined' + Result = cmsPipelineAlloc(ContextID, 0, 0); + if (Result == NULL) return NULL; + + CurrentColorSpace = cmsGetColorSpace(hProfiles[0]); + + for (i=0; i < nProfiles; i++) { + + cmsBool lIsDeviceLink, lIsInput; + + hProfile = hProfiles[i]; + ClassSig = cmsGetDeviceClass(hProfile); + lIsDeviceLink = (ClassSig == cmsSigLinkClass || ClassSig == cmsSigAbstractClass ); + + // First profile is used as input unless devicelink or abstract + if ((i == 0) && !lIsDeviceLink) { + lIsInput = TRUE; + } + else { + // Else use profile in the input direction if current space is not PCS + lIsInput = (CurrentColorSpace != cmsSigXYZData) && + (CurrentColorSpace != cmsSigLabData); + } + + Intent = TheIntents[i]; + + if (lIsInput || lIsDeviceLink) { + + ColorSpaceIn = cmsGetColorSpace(hProfile); + ColorSpaceOut = cmsGetPCS(hProfile); + } + else { + + ColorSpaceIn = cmsGetPCS(hProfile); + ColorSpaceOut = cmsGetColorSpace(hProfile); + } + + if (!ColorSpaceIsCompatible(ColorSpaceIn, CurrentColorSpace)) { + + cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "ColorSpace mismatch"); + goto Error; + } + + // If devicelink is found, then no custom intent is allowed and we can + // read the LUT to be applied. Settings don't apply here. + if (lIsDeviceLink || ((ClassSig == cmsSigNamedColorClass) && (nProfiles == 1))) { + + // Get the involved LUT from the profile + Lut = _cmsReadDevicelinkLUT(hProfile, Intent); + if (Lut == NULL) goto Error; + + // What about abstract profiles? + if (ClassSig == cmsSigAbstractClass && i > 0) { + if (!ComputeConversion(i, hProfiles, Intent, BPC[i], AdaptationStates[i], &m, &off)) goto Error; + } + else { + _cmsMAT3identity(&m); + _cmsVEC3init(&off, 0, 0, 0); + } + + + if (!AddConversion(Result, CurrentColorSpace, ColorSpaceIn, &m, &off)) goto Error; + + } + else { + + if (lIsInput) { + // Input direction means non-pcs connection, so proceed like devicelinks + Lut = _cmsReadInputLUT(hProfile, Intent); + if (Lut == NULL) goto Error; + } + else { + + // Output direction means PCS connection. Intent may apply here + Lut = _cmsReadOutputLUT(hProfile, Intent); + if (Lut == NULL) goto Error; + + + if (!ComputeConversion(i, hProfiles, Intent, BPC[i], AdaptationStates[i], &m, &off)) goto Error; + if (!AddConversion(Result, CurrentColorSpace, ColorSpaceIn, &m, &off)) goto Error; + + } + } + + // Concatenate to the output LUT + if (!cmsPipelineCat(Result, Lut)) + goto Error; + + cmsPipelineFree(Lut); + Lut = NULL; + + // Update current space + CurrentColorSpace = ColorSpaceOut; + } + + return Result; + +Error: + + if (Lut != NULL) cmsPipelineFree(Lut); + if (Result != NULL) cmsPipelineFree(Result); + return NULL; + + cmsUNUSED_PARAMETER(dwFlags); +} + + +// Wrapper for DLL calling convention +cmsPipeline* CMSEXPORT _cmsDefaultICCintents(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number TheIntents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) +{ + return DefaultICCintents(ContextID, nProfiles, TheIntents, hProfiles, BPC, AdaptationStates, dwFlags); +} + +// Black preserving intents --------------------------------------------------------------------------------------------- + +// Translate black-preserving intents to ICC ones +static +int TranslateNonICCIntents(int Intent) +{ + switch (Intent) { + case INTENT_PRESERVE_K_ONLY_PERCEPTUAL: + case INTENT_PRESERVE_K_PLANE_PERCEPTUAL: + return INTENT_PERCEPTUAL; + + case INTENT_PRESERVE_K_ONLY_RELATIVE_COLORIMETRIC: + case INTENT_PRESERVE_K_PLANE_RELATIVE_COLORIMETRIC: + return INTENT_RELATIVE_COLORIMETRIC; + + case INTENT_PRESERVE_K_ONLY_SATURATION: + case INTENT_PRESERVE_K_PLANE_SATURATION: + return INTENT_SATURATION; + + default: return Intent; + } +} + +// Sampler for Black-only preserving CMYK->CMYK transforms + +typedef struct { + cmsPipeline* cmyk2cmyk; // The original transform + cmsToneCurve* KTone; // Black-to-black tone curve + +} GrayOnlyParams; + + +// Preserve black only if that is the only ink used +static +int BlackPreservingGrayOnlySampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo) +{ + GrayOnlyParams* bp = (GrayOnlyParams*) Cargo; + + // If going across black only, keep black only + if (In[0] == 0 && In[1] == 0 && In[2] == 0) { + + // TAC does not apply because it is black ink! + Out[0] = Out[1] = Out[2] = 0; + Out[3] = cmsEvalToneCurve16(bp->KTone, In[3]); + return TRUE; + } + + // Keep normal transform for other colors + bp ->cmyk2cmyk ->Eval16Fn(In, Out, bp ->cmyk2cmyk->Data); + return TRUE; +} + +// This is the entry for black-preserving K-only intents, which are non-ICC +static +cmsPipeline* BlackPreservingKOnlyIntents(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number TheIntents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) +{ + GrayOnlyParams bp; + cmsPipeline* Result; + cmsUInt32Number ICCIntents[256]; + cmsStage* CLUT; + cmsUInt32Number i, nGridPoints; + + + // Sanity check + if (nProfiles < 1 || nProfiles > 255) return NULL; + + // Translate black-preserving intents to ICC ones + for (i=0; i < nProfiles; i++) + ICCIntents[i] = TranslateNonICCIntents(TheIntents[i]); + + // Check for non-cmyk profiles + if (cmsGetColorSpace(hProfiles[0]) != cmsSigCmykData || + cmsGetColorSpace(hProfiles[nProfiles-1]) != cmsSigCmykData) + return DefaultICCintents(ContextID, nProfiles, ICCIntents, hProfiles, BPC, AdaptationStates, dwFlags); + + memset(&bp, 0, sizeof(bp)); + + // Allocate an empty LUT for holding the result + Result = cmsPipelineAlloc(ContextID, 4, 4); + if (Result == NULL) return NULL; + + // Create a LUT holding normal ICC transform + bp.cmyk2cmyk = DefaultICCintents(ContextID, + nProfiles, + ICCIntents, + hProfiles, + BPC, + AdaptationStates, + dwFlags); + + if (bp.cmyk2cmyk == NULL) goto Error; + + // Now, compute the tone curve + bp.KTone = _cmsBuildKToneCurve(ContextID, + 4096, + nProfiles, + ICCIntents, + hProfiles, + BPC, + AdaptationStates, + dwFlags); + + if (bp.KTone == NULL) goto Error; + + + // How many gridpoints are we going to use? + nGridPoints = _cmsReasonableGridpointsByColorspace(cmsSigCmykData, dwFlags); + + // Create the CLUT. 16 bits + CLUT = cmsStageAllocCLut16bit(ContextID, nGridPoints, 4, 4, NULL); + if (CLUT == NULL) goto Error; + + // This is the one and only MPE in this LUT + if (!cmsPipelineInsertStage(Result, cmsAT_BEGIN, CLUT)) + goto Error; + + // Sample it. We cannot afford pre/post linearization this time. + if (!cmsStageSampleCLut16bit(CLUT, BlackPreservingGrayOnlySampler, (void*) &bp, 0)) + goto Error; + + // Get rid of xform and tone curve + cmsPipelineFree(bp.cmyk2cmyk); + cmsFreeToneCurve(bp.KTone); + + return Result; + +Error: + + if (bp.cmyk2cmyk != NULL) cmsPipelineFree(bp.cmyk2cmyk); + if (bp.KTone != NULL) cmsFreeToneCurve(bp.KTone); + if (Result != NULL) cmsPipelineFree(Result); + return NULL; + +} + +// K Plane-preserving CMYK to CMYK ------------------------------------------------------------------------------------ + +typedef struct { + + cmsPipeline* cmyk2cmyk; // The original transform + cmsHTRANSFORM hProofOutput; // Output CMYK to Lab (last profile) + cmsHTRANSFORM cmyk2Lab; // The input chain + cmsToneCurve* KTone; // Black-to-black tone curve + cmsPipeline* LabK2cmyk; // The output profile + cmsFloat64Number MaxError; + + cmsHTRANSFORM hRoundTrip; + cmsFloat64Number MaxTAC; + + +} PreserveKPlaneParams; + + +// The CLUT will be stored at 16 bits, but calculations are performed at cmsFloat32Number precision +static +int BlackPreservingSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo) +{ + int i; + cmsFloat32Number Inf[4], Outf[4]; + cmsFloat32Number LabK[4]; + cmsFloat64Number SumCMY, SumCMYK, Error, Ratio; + cmsCIELab ColorimetricLab, BlackPreservingLab; + PreserveKPlaneParams* bp = (PreserveKPlaneParams*) Cargo; + + // Convert from 16 bits to floating point + for (i=0; i < 4; i++) + Inf[i] = (cmsFloat32Number) (In[i] / 65535.0); + + // Get the K across Tone curve + LabK[3] = cmsEvalToneCurveFloat(bp ->KTone, Inf[3]); + + // If going across black only, keep black only + if (In[0] == 0 && In[1] == 0 && In[2] == 0) { + + Out[0] = Out[1] = Out[2] = 0; + Out[3] = _cmsQuickSaturateWord(LabK[3] * 65535.0); + return TRUE; + } + + // Try the original transform, + cmsPipelineEvalFloat( Inf, Outf, bp ->cmyk2cmyk); + + // Store a copy of the floating point result into 16-bit + for (i=0; i < 4; i++) + Out[i] = _cmsQuickSaturateWord(Outf[i] * 65535.0); + + // Maybe K is already ok (mostly on K=0) + if ( fabs(Outf[3] - LabK[3]) < (3.0 / 65535.0) ) { + return TRUE; + } + + // K differ, mesure and keep Lab measurement for further usage + // this is done in relative colorimetric intent + cmsDoTransform(bp->hProofOutput, Out, &ColorimetricLab, 1); + + // Is not black only and the transform doesn't keep black. + // Obtain the Lab of output CMYK. After that we have Lab + K + cmsDoTransform(bp ->cmyk2Lab, Outf, LabK, 1); + + // Obtain the corresponding CMY using reverse interpolation + // (K is fixed in LabK[3]) + if (!cmsPipelineEvalReverseFloat(LabK, Outf, Outf, bp ->LabK2cmyk)) { + + // Cannot find a suitable value, so use colorimetric xform + // which is already stored in Out[] + return TRUE; + } + + // Make sure to pass thru K (which now is fixed) + Outf[3] = LabK[3]; + + // Apply TAC if needed + SumCMY = Outf[0] + Outf[1] + Outf[2]; + SumCMYK = SumCMY + Outf[3]; + + if (SumCMYK > bp ->MaxTAC) { + + Ratio = 1 - ((SumCMYK - bp->MaxTAC) / SumCMY); + if (Ratio < 0) + Ratio = 0; + } + else + Ratio = 1.0; + + Out[0] = _cmsQuickSaturateWord(Outf[0] * Ratio * 65535.0); // C + Out[1] = _cmsQuickSaturateWord(Outf[1] * Ratio * 65535.0); // M + Out[2] = _cmsQuickSaturateWord(Outf[2] * Ratio * 65535.0); // Y + Out[3] = _cmsQuickSaturateWord(Outf[3] * 65535.0); + + // Estimate the error (this goes 16 bits to Lab DBL) + cmsDoTransform(bp->hProofOutput, Out, &BlackPreservingLab, 1); + Error = cmsDeltaE(&ColorimetricLab, &BlackPreservingLab); + if (Error > bp -> MaxError) + bp->MaxError = Error; + + return TRUE; +} + +// This is the entry for black-plane preserving, which are non-ICC +static +cmsPipeline* BlackPreservingKPlaneIntents(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number TheIntents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) +{ + PreserveKPlaneParams bp; + cmsPipeline* Result = NULL; + cmsUInt32Number ICCIntents[256]; + cmsStage* CLUT; + cmsUInt32Number i, nGridPoints; + cmsHPROFILE hLab; + + // Sanity check + if (nProfiles < 1 || nProfiles > 255) return NULL; + + // Translate black-preserving intents to ICC ones + for (i=0; i < nProfiles; i++) + ICCIntents[i] = TranslateNonICCIntents(TheIntents[i]); + + // Check for non-cmyk profiles + if (cmsGetColorSpace(hProfiles[0]) != cmsSigCmykData || + !(cmsGetColorSpace(hProfiles[nProfiles-1]) == cmsSigCmykData || + cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigOutputClass)) + return DefaultICCintents(ContextID, nProfiles, ICCIntents, hProfiles, BPC, AdaptationStates, dwFlags); + + // Allocate an empty LUT for holding the result + Result = cmsPipelineAlloc(ContextID, 4, 4); + if (Result == NULL) return NULL; + + + memset(&bp, 0, sizeof(bp)); + + // We need the input LUT of the last profile, assuming this one is responsible of + // black generation. This LUT will be seached in inverse order. + bp.LabK2cmyk = _cmsReadInputLUT(hProfiles[nProfiles-1], INTENT_RELATIVE_COLORIMETRIC); + if (bp.LabK2cmyk == NULL) goto Cleanup; + + // Get total area coverage (in 0..1 domain) + bp.MaxTAC = cmsDetectTAC(hProfiles[nProfiles-1]) / 100.0; + if (bp.MaxTAC <= 0) goto Cleanup; + + + // Create a LUT holding normal ICC transform + bp.cmyk2cmyk = DefaultICCintents(ContextID, + nProfiles, + ICCIntents, + hProfiles, + BPC, + AdaptationStates, + dwFlags); + if (bp.cmyk2cmyk == NULL) goto Cleanup; + + // Now the tone curve + bp.KTone = _cmsBuildKToneCurve(ContextID, 4096, nProfiles, + ICCIntents, + hProfiles, + BPC, + AdaptationStates, + dwFlags); + if (bp.KTone == NULL) goto Cleanup; + + // To measure the output, Last profile to Lab + hLab = cmsCreateLab4ProfileTHR(ContextID, NULL); + bp.hProofOutput = cmsCreateTransformTHR(ContextID, hProfiles[nProfiles-1], + CHANNELS_SH(4)|BYTES_SH(2), hLab, TYPE_Lab_DBL, + INTENT_RELATIVE_COLORIMETRIC, + cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE); + if ( bp.hProofOutput == NULL) goto Cleanup; + + // Same as anterior, but lab in the 0..1 range + bp.cmyk2Lab = cmsCreateTransformTHR(ContextID, hProfiles[nProfiles-1], + FLOAT_SH(1)|CHANNELS_SH(4)|BYTES_SH(4), hLab, + FLOAT_SH(1)|CHANNELS_SH(3)|BYTES_SH(4), + INTENT_RELATIVE_COLORIMETRIC, + cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE); + if (bp.cmyk2Lab == NULL) goto Cleanup; + cmsCloseProfile(hLab); + + // Error estimation (for debug only) + bp.MaxError = 0; + + // How many gridpoints are we going to use? + nGridPoints = _cmsReasonableGridpointsByColorspace(cmsSigCmykData, dwFlags); + + + CLUT = cmsStageAllocCLut16bit(ContextID, nGridPoints, 4, 4, NULL); + if (CLUT == NULL) goto Cleanup; + + if (!cmsPipelineInsertStage(Result, cmsAT_BEGIN, CLUT)) + goto Cleanup; + + cmsStageSampleCLut16bit(CLUT, BlackPreservingSampler, (void*) &bp, 0); + +Cleanup: + + if (bp.cmyk2cmyk) cmsPipelineFree(bp.cmyk2cmyk); + if (bp.cmyk2Lab) cmsDeleteTransform(bp.cmyk2Lab); + if (bp.hProofOutput) cmsDeleteTransform(bp.hProofOutput); + + if (bp.KTone) cmsFreeToneCurve(bp.KTone); + if (bp.LabK2cmyk) cmsPipelineFree(bp.LabK2cmyk); + + return Result; +} + +// Link routines ------------------------------------------------------------------------------------------------------ + +// Chain several profiles into a single LUT. It just checks the parameters and then calls the handler +// for the first intent in chain. The handler may be user-defined. Is up to the handler to deal with the +// rest of intents in chain. A maximum of 255 profiles at time are supported, which is pretty reasonable. +cmsPipeline* _cmsLinkProfiles(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number TheIntents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags) +{ + cmsUInt32Number i; + cmsIntentsList* Intent; + + // Make sure a reasonable number of profiles is provided + if (nProfiles <= 0 || nProfiles > 255) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Couldn't link '%d' profiles", nProfiles); + return NULL; + } + + for (i=0; i < nProfiles; i++) { + + // Check if black point is really needed or allowed. Note that + // following Adobe's document: + // BPC does not apply to devicelink profiles, nor to abs colorimetric, + // and applies always on V4 perceptual and saturation. + + if (TheIntents[i] == INTENT_ABSOLUTE_COLORIMETRIC) + BPC[i] = FALSE; + + if (TheIntents[i] == INTENT_PERCEPTUAL || TheIntents[i] == INTENT_SATURATION) { + + // Force BPC for V4 profiles in perceptual and saturation + if (cmsGetProfileVersion(hProfiles[i]) >= 4.0) + BPC[i] = TRUE; + } + } + + // Search for a handler. The first intent in the chain defines the handler. That would + // prevent using multiple custom intents in a multiintent chain, but the behaviour of + // 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(ContextID, TheIntents[0]); + if (Intent == NULL) { + cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported intent '%d'", TheIntents[0]); + return NULL; + } + + // Call the handler + return Intent ->Link(ContextID, nProfiles, TheIntents, hProfiles, BPC, AdaptationStates, dwFlags); +} + +// ------------------------------------------------------------------------------------------------- + +// 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 cmsGetSupportedIntentsTHR(cmsContext ContextID, cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions) +{ + _cmsIntentsPluginChunkType* ctx = ( _cmsIntentsPluginChunkType*) _cmsContextGetClientChunk(ContextID, IntentPlugin); + cmsIntentsList* pt; + cmsUInt32Number nIntents; + + + for (nIntents=0, pt = ctx->Intents; pt != NULL; pt = pt -> Next) + { + if (nIntents < nMax) { + if (Codes != NULL) + Codes[nIntents] = pt ->Intent; + + if (Descriptions != NULL) + Descriptions[nIntents] = pt ->Description; + } + + 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 custom intents? + if (Data == NULL) { + + ctx->Intents = NULL; + return TRUE; + } + + fl = (cmsIntentsList*) _cmsPluginMalloc(id, sizeof(cmsIntentsList)); + if (fl == NULL) return FALSE; + + + fl ->Intent = Plugin ->Intent; + strncpy(fl ->Description, Plugin ->Description, sizeof(fl ->Description)-1); + fl ->Description[sizeof(fl ->Description)-1] = 0; + + fl ->Link = Plugin ->Link; + + fl ->Next = ctx ->Intents; + ctx ->Intents = fl; + + return TRUE; +} + diff --git a/third_party/lcms/src/cmserr.c b/third_party/lcms/src/cmserr.c new file mode 100644 index 0000000000..f9adc3824a --- /dev/null +++ b/third_party/lcms/src/cmserr.c @@ -0,0 +1,707 @@ +//--------------------------------------------------------------------------------- +// +// 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" + +// I am so tired about incompatibilities on those functions that here are some replacements +// that hopefully would be fully portable. + +// 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; + + 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) +long int CMSEXPORT cmsfilelength(FILE* f) +{ + long int p , n; + + p = ftell(f); // register current file position + + if (fseek(f, 0, SEEK_END) != 0) { + return -1; + } + + n = ftell(f); + fseek(f, p, SEEK_SET); // file position restored + + return n; +} + +#if 0 +// Memory handling ------------------------------------------------------------------ +// +// 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 evil code to allocate huge blocks that otherwise lcms would never need. + +#define MAX_MEMORY_FOR_ALLOC ((cmsUInt32Number)(1024U*1024U*512U)) + +// User may override this behaviour by using a memory plug-in, which basically replaces +// the default memory management functions. In this case, no check is performed and it +// is up to the plug-in writter to keep in the safe side. There are only three functions +// 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(cmsContext ContextID, cmsPluginBase* Plugin); + +// ********************************************************************************* + +// This is the default memory allocation function. It does a very coarse +// check of amout of memory, just to prevent exploits +static +void* _cmsMallocDefaultFn(cmsContext ContextID, cmsUInt32Number size) +{ + if (size > MAX_MEMORY_FOR_ALLOC) return NULL; // Never allow over maximum + + return (void*) malloc(size); + + cmsUNUSED_PARAMETER(ContextID); +} + +// Generic allocate & zero +static +void* _cmsMallocZeroDefaultFn(cmsContext ContextID, cmsUInt32Number size) +{ + void *pt = _cmsMalloc(ContextID, size); + if (pt == NULL) return NULL; + + memset(pt, 0, size); + return pt; +} + + +// The default free function. The only check proformed is against NULL pointers +static +void _cmsFreeDefaultFn(cmsContext ContextID, void *Ptr) +{ + // free(NULL) is defined a no-op by C99, therefore it is safe to + // avoid the check, but it is here just in case... + + if (Ptr) free(Ptr); + + cmsUNUSED_PARAMETER(ContextID); +} + +// 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) +{ + + if (size > MAX_MEMORY_FOR_ALLOC) return NULL; // Never realloc over 512Mb + + return realloc(Ptr, size); + + cmsUNUSED_PARAMETER(ContextID); +} + + +// The default calloc function. Allocates an array of num elements, each one of size bytes +// all memory is initialized to zero. +static +void* _cmsCallocDefaultFn(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size) +{ + cmsUInt32Number Total = num * size; + + // Preserve calloc behaviour + if (Total == 0) return NULL; + + // Safe check for overflow. + if (num >= UINT_MAX / size) return NULL; + + // Check for overflow + if (Total < num || Total < size) { + return NULL; + } + + if (Total > MAX_MEMORY_FOR_ALLOC) return NULL; // Never alloc over 512Mb + + return _cmsMallocZero(ContextID, Total); +} + +// Generic block duplication +static +void* _cmsDupDefaultFn(cmsContext ContextID, const void* Org, cmsUInt32Number size) +{ + void* mem; + + if (size > MAX_MEMORY_FOR_ALLOC) return NULL; // Never dup over 512Mb + + mem = _cmsMalloc(ContextID, size); + + if (mem != NULL && Org != NULL) + memmove(mem, Org, size); + + return mem; +} + + +// 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(cmsContext ContextID, cmsPluginBase *Data) +{ + cmsPluginMemHandler* Plugin = (cmsPluginMemHandler*) Data; + _cmsMemPluginChunkType* ptr; + + // 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) { + + struct _cmsContext_struct* ctx = ( struct _cmsContext_struct*) ContextID; + + // Return to the default allocators + if (ContextID != NULL) { + ctx->chunks[MemPlugin] = (void*) &ctx->DefaultMemoryManager; + } + return TRUE; + } + + // Check for required callbacks + if (Plugin -> MallocPtr == NULL || + Plugin -> FreePtr == NULL || + Plugin -> ReallocPtr == NULL) return FALSE; + + // Set replacement functions + ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin); + if (ptr == NULL) + return FALSE; + + _cmsInstallAllocFunctions(Plugin, ptr); + return TRUE; +} +#else +#include "core/fxcrt/fx_memory.h" +#include "core/fxcrt/fx_system.h" + +cmsBool _cmsRegisterMemHandlerPlugin(cmsContext ContextID, cmsPluginBase* Plugin) +{ + return TRUE; +} + +// Generic allocate +void* CMSEXPORT _cmsMalloc(cmsContext ContextID, cmsUInt32Number size) +{ + return FXMEM_DefaultAlloc(size, 1); +} + +// Generic allocate & zero +void* CMSEXPORT _cmsMallocZero(cmsContext ContextID, cmsUInt32Number size) +{ + void* p = FXMEM_DefaultAlloc(size, 1); + if (p) memset(p, 0, size); + return p; +} + +// Generic calloc +void* CMSEXPORT _cmsCalloc(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size) +{ + cmsUInt32Number total = num * size; + if (total == 0 || total / size != num || total >= 512 * 1024 * 1024) + return NULL; + + return _cmsMallocZero(ContextID, num * size); +} + +// Generic reallocate +void* CMSEXPORT _cmsRealloc(cmsContext ContextID, void* Ptr, cmsUInt32Number size) +{ + return FXMEM_DefaultRealloc(Ptr, size, 1); +} + +// Generic free memory +void CMSEXPORT _cmsFree(cmsContext ContextID, void* Ptr) +{ + if (Ptr != NULL) FXMEM_DefaultFree(Ptr, 0); +} + +// Generic block duplication +void* CMSEXPORT _cmsDupMem(cmsContext ContextID, const void* Org, cmsUInt32Number size) +{ + void* p = FXMEM_DefaultAlloc(size, 1); + memmove(p, Org, size); + return p; +} + +_cmsMemPluginChunkType _cmsMemPluginChunk = {_cmsMalloc, _cmsMallocZero, _cmsFree, + _cmsRealloc, _cmsCalloc, _cmsDupMem + }; + +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; + } +} + +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= _cmsMallocZero; + ptr ->CallocPtr = _cmsCalloc; + ptr ->DupPtr = _cmsDupMem; + + if (Plugin ->MallocZeroPtr != NULL) ptr ->MallocZeroPtr = Plugin -> MallocZeroPtr; + if (Plugin ->CallocPtr != NULL) ptr ->CallocPtr = Plugin -> CallocPtr; + if (Plugin ->DupPtr != NULL) ptr ->DupPtr = Plugin -> DupPtr; + + } +} +#endif + +// ******************************************************************************************** + +// Sub allocation takes care of many pointers of small size. The memory allocated in +// this way have be freed at once. Next function allocates a single chunk for linked list +// I prefer this method over realloc due to the big inpact on xput realloc may have if +// memory is being swapped to disk. This approach is safer (although that may not be true on all platforms) +static +_cmsSubAllocator_chunk* _cmsCreateSubAllocChunk(cmsContext ContextID, cmsUInt32Number Initial) +{ + _cmsSubAllocator_chunk* chunk; + + // 20K by default + if (Initial == 0) + Initial = 20*1024; + + // Create the container + chunk = (_cmsSubAllocator_chunk*) _cmsMallocZero(ContextID, sizeof(_cmsSubAllocator_chunk)); + if (chunk == NULL) return NULL; + + // Initialize values + chunk ->Block = (cmsUInt8Number*) _cmsMalloc(ContextID, Initial); + if (chunk ->Block == NULL) { + + // Something went wrong + _cmsFree(ContextID, chunk); + return NULL; + } + + chunk ->BlockSize = Initial; + chunk ->Used = 0; + chunk ->next = NULL; + + return chunk; +} + +// The suballocated is nothing but a pointer to the first element in the list. We also keep +// the thread ID in this structure. +_cmsSubAllocator* _cmsCreateSubAlloc(cmsContext ContextID, cmsUInt32Number Initial) +{ + _cmsSubAllocator* sub; + + // Create the container + sub = (_cmsSubAllocator*) _cmsMallocZero(ContextID, sizeof(_cmsSubAllocator)); + if (sub == NULL) return NULL; + + sub ->ContextID = ContextID; + + sub ->h = _cmsCreateSubAllocChunk(ContextID, Initial); + if (sub ->h == NULL) { + _cmsFree(ContextID, sub); + return NULL; + } + + return sub; +} + + +// Get rid of whole linked list +void _cmsSubAllocDestroy(_cmsSubAllocator* sub) +{ + _cmsSubAllocator_chunk *chunk, *n; + + for (chunk = sub ->h; chunk != NULL; chunk = n) { + + n = chunk->next; + if (chunk->Block != NULL) _cmsFree(sub ->ContextID, chunk->Block); + _cmsFree(sub ->ContextID, chunk); + } + + // Free the header + _cmsFree(sub ->ContextID, sub); +} + + +// Get a pointer to small memory block. +void* _cmsSubAlloc(_cmsSubAllocator* sub, cmsUInt32Number size) +{ + cmsUInt32Number Free = sub -> h ->BlockSize - sub -> h -> Used; + cmsUInt8Number* ptr; + + size = _cmsALIGNMEM(size); + + // Check for memory. If there is no room, allocate a new chunk of double memory size. + if (size > Free) { + + _cmsSubAllocator_chunk* chunk; + cmsUInt32Number newSize; + + newSize = sub -> h ->BlockSize * 2; + if (newSize < size) newSize = size; + + chunk = _cmsCreateSubAllocChunk(sub -> ContextID, newSize); + if (chunk == NULL) return NULL; + + // Link list + chunk ->next = sub ->h; + sub ->h = chunk; + + } + + ptr = sub -> h ->Block + sub -> h ->Used; + sub -> h -> Used += 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. +// For example, all create functions does return NULL on failure. Other return FALSE +// It may be interesting, for the developer, to know why the function is failing. +// for that reason, lcms2 does offer a logging function. This function does recive +// a ENGLISH string with some clues on what is going wrong. You can show this +// info to the end user, or just create some sort of log. +// The logging function should NOT terminate the program, as this obviously can leave +// resources. It is the programmer's responsability to check each function return code +// to make sure it didn't fail. + +// Error messages are limited to MAX_ERROR_MESSAGE_LEN + +#define MAX_ERROR_MESSAGE_LEN 1024 + +// --------------------------------------------------------------------------------------------------------- + +// This is our default log error +static void DefaultLogErrorHandlerFunction(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text); + +// 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 +void DefaultLogErrorHandlerFunction(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text) +{ + // fprintf(stderr, "[lcms]: %s\n", Text); + // fflush(stderr); + + cmsUNUSED_PARAMETER(ContextID); + cmsUNUSED_PARAMETER(ErrorCode); + cmsUNUSED_PARAMETER(Text); +} + +// 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) +{ + cmsSetLogErrorHandlerTHR(NULL, Fn); +} + +// Log an error +// ErrorText is a text holding an english description of error. +void CMSEXPORT cmsSignalError(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *ErrorText, ...) +{ + 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); + + // 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 +void _cmsTagSignature2String(char String[5], cmsTagSignature sig) +{ + cmsUInt32Number be; + + // Convert to big endian + be = _cmsAdjustEndianess32((cmsUInt32Number) sig); + + // Move chars + memmove(String, &be, 4); + + // Make sure of terminator + String[4] = 0; +} + +//-------------------------------------------------------------------------------------------------- + + +static +void* defMtxCreate(cmsContext id) +{ + _cmsMutex* ptr_mutex = (_cmsMutex*) _cmsMalloc(id, sizeof(_cmsMutex)); + _cmsInitMutexPrimitive(ptr_mutex); + return (void*) ptr_mutex; +} + +static +void defMtxDestroy(cmsContext id, void* mtx) +{ + _cmsDestroyMutexPrimitive((_cmsMutex *) mtx); + _cmsFree(id, mtx); +} + +static +cmsBool defMtxLock(cmsContext id, void* mtx) +{ + cmsUNUSED_PARAMETER(id); + return _cmsLockPrimitive((_cmsMutex *) mtx) == 0; +} + +static +void defMtxUnlock(cmsContext id, void* mtx) +{ + cmsUNUSED_PARAMETER(id); + _cmsUnlockPrimitive((_cmsMutex *) mtx); +} + + + +// Pointers to memory manager functions in Context0 +_cmsMutexPluginChunkType _cmsMutexPluginChunk = { defMtxCreate, defMtxDestroy, defMtxLock, defMtxUnlock }; + +// Allocate and init mutex container. +void _cmsAllocMutexPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + static _cmsMutexPluginChunkType MutexChunk = {defMtxCreate, defMtxDestroy, defMtxLock, defMtxUnlock }; + 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/third_party/lcms/src/cmsgamma.c b/third_party/lcms/src/cmsgamma.c new file mode 100644 index 0000000000..97aeb7cc16 --- /dev/null +++ b/third_party/lcms/src/cmsgamma.c @@ -0,0 +1,1298 @@ +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2013 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" + +// Tone curves are powerful constructs that can contain curves specified in diverse ways. +// The curve is stored in segments, where each segment can be sampled or specified by parameters. +// a 16.bit simplification of the *whole* curve is kept for optimization purposes. For float operation, +// each segment is evaluated separately. Plug-ins may be used to define new parametric schemes, +// each plug-in may define up to MAX_TYPES_IN_LCMS_PLUGIN functions types. For defining a function, +// the plug-in should provide the type id, how many parameters each type has, and a pointer to +// a procedure that evaluates the function. In the case of reverse evaluation, the evaluator will +// be called with the type id as a negative value, and a sampled version of the reversed curve +// will be built. + +// ----------------------------------------------------------------- Implementation +// Maxim number of nodes +#define MAX_NODES_IN_CURVE 4097 +#define MINUS_INF (-1E22F) +#define PLUS_INF (+1E22F) + +// The list of supported parametric curves +typedef struct _cmsParametricCurvesCollection_st { + + int nFunctions; // Number of supported functions in this chunk + int FunctionTypes[MAX_TYPES_IN_LCMS_PLUGIN]; // The identification types + int ParameterCount[MAX_TYPES_IN_LCMS_PLUGIN]; // Number of parameters for each function + cmsParametricCurveEvaluator Evaluator; // The evaluator + + struct _cmsParametricCurvesCollection_st* Next; // Next in list + +} _cmsParametricCurvesCollection; + +// This is the default (built-in) evaluator +static cmsFloat64Number DefaultEvalParametricFn(cmsInt32Number Type, const cmsFloat64Number Params[], cmsFloat64Number R); + +// The built-in list +static _cmsParametricCurvesCollection DefaultCurves = { + 9, // # of curve types + { 1, 2, 3, 4, 5, 6, 7, 8, 108 }, // Parametric curve ID + { 1, 3, 4, 5, 7, 4, 5, 5, 1 }, // Parameters by type + DefaultEvalParametricFn, // Evaluator + 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 +_cmsCurvesPluginChunkType _cmsCurvesPluginChunk = { NULL }; + +// As a way to install new parametric curves +cmsBool _cmsRegisterParametricCurvesPlugin(cmsContext ContextID, cmsPluginBase* Data) +{ + _cmsCurvesPluginChunkType* ctx = ( _cmsCurvesPluginChunkType*) _cmsContextGetClientChunk(ContextID, CurvesPlugin); + cmsPluginParametricCurves* Plugin = (cmsPluginParametricCurves*) Data; + _cmsParametricCurvesCollection* fl; + + if (Data == NULL) { + + ctx -> ParametricCurves = NULL; + return TRUE; + } + + fl = (_cmsParametricCurvesCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsParametricCurvesCollection)); + if (fl == NULL) return FALSE; + + // Copy the parameters + fl ->Evaluator = Plugin ->Evaluator; + fl ->nFunctions = Plugin ->nFunctions; + + // Make sure no mem overwrites + if (fl ->nFunctions > MAX_TYPES_IN_LCMS_PLUGIN) + fl ->nFunctions = MAX_TYPES_IN_LCMS_PLUGIN; + + // Copy the data + memmove(fl->FunctionTypes, Plugin ->FunctionTypes, fl->nFunctions * sizeof(cmsUInt32Number)); + memmove(fl->ParameterCount, Plugin ->ParameterCount, fl->nFunctions * sizeof(cmsUInt32Number)); + + // Keep linked list + fl ->Next = ctx->ParametricCurves; + ctx->ParametricCurves = fl; + + // All is ok + return TRUE; +} + + +// Search in type list, return position or -1 if not found +static +int IsInSet(int Type, _cmsParametricCurvesCollection* c) +{ + int i; + + for (i=0; i < c ->nFunctions; i++) + if (abs(Type) == c ->FunctionTypes[i]) return i; + + return -1; +} + + +// Search for the collection which contains a specific type +static +_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); + + 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); + + if (Position != -1) { + if (index != NULL) + *index = Position; + return c; + } + } + + return NULL; +} + +// Low level allocate, which takes care of memory details. nEntries may be zero, and in this case +// no optimation curve is computed. nSegments may also be zero in the inverse case, where only the +// optimization curve is given. Both features simultaneously is an error +static +cmsToneCurve* AllocateToneCurveStruct(cmsContext ContextID, cmsInt32Number nEntries, + cmsInt32Number nSegments, const cmsCurveSegment* Segments, + const cmsUInt16Number* Values) +{ + cmsToneCurve* p; + int i; + + // We allow huge tables, which are then restricted for smoothing operations + if (nEntries > 65530 || nEntries < 0) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Couldn't create tone curve of more than 65530 entries"); + return NULL; + } + + if (nEntries <= 0 && nSegments <= 0) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Couldn't create tone curve with zero segments and no table"); + return NULL; + } + + // Allocate all required pointers, etc. + p = (cmsToneCurve*) _cmsMallocZero(ContextID, sizeof(cmsToneCurve)); + if (!p) return NULL; + + // In this case, there are no segments + if (nSegments <= 0) { + p ->Segments = NULL; + p ->Evals = NULL; + } + else { + p ->Segments = (cmsCurveSegment*) _cmsCalloc(ContextID, nSegments, sizeof(cmsCurveSegment)); + if (p ->Segments == NULL) goto Error; + + p ->Evals = (cmsParametricCurveEvaluator*) _cmsCalloc(ContextID, nSegments, sizeof(cmsParametricCurveEvaluator)); + if (p ->Evals == NULL) goto Error; + } + + p -> nSegments = nSegments; + + // This 16-bit table contains a limited precision representation of the whole curve and is kept for + // increasing xput on certain operations. + if (nEntries <= 0) { + p ->Table16 = NULL; + } + else { + p ->Table16 = (cmsUInt16Number*) _cmsCalloc(ContextID, nEntries, sizeof(cmsUInt16Number)); + if (p ->Table16 == NULL) goto Error; + } + + p -> nEntries = nEntries; + + // Initialize members if requested + if (Values != NULL && (nEntries > 0)) { + + for (i=0; i < nEntries; i++) + p ->Table16[i] = Values[i]; + } + + // Initialize the segments stuff. The evaluator for each segment is located and a pointer to it + // is placed in advance to maximize performance. + if (Segments != NULL && (nSegments > 0)) { + + _cmsParametricCurvesCollection *c; + + p ->SegInterp = (cmsInterpParams**) _cmsCalloc(ContextID, nSegments, sizeof(cmsInterpParams*)); + if (p ->SegInterp == NULL) goto Error; + + for (i=0; i< nSegments; i++) { + + // Type 0 is a special marker for table-based curves + if (Segments[i].Type == 0) + p ->SegInterp[i] = _cmsComputeInterpParams(ContextID, Segments[i].nGridPoints, 1, 1, NULL, CMS_LERP_FLAGS_FLOAT); + + memmove(&p ->Segments[i], &Segments[i], sizeof(cmsCurveSegment)); + + if (Segments[i].Type == 0 && Segments[i].SampledPoints != NULL) + p ->Segments[i].SampledPoints = (cmsFloat32Number*) _cmsDupMem(ContextID, Segments[i].SampledPoints, sizeof(cmsFloat32Number) * Segments[i].nGridPoints); + else + p ->Segments[i].SampledPoints = NULL; + + + c = GetParametricCurveByType(ContextID, Segments[i].Type, NULL); + if (c != NULL) + p ->Evals[i] = c ->Evaluator; + } + } + + p ->InterpParams = _cmsComputeInterpParams(ContextID, p ->nEntries, 1, 1, p->Table16, CMS_LERP_FLAGS_16BITS); + if (p->InterpParams != NULL) + return p; + +Error: + if (p -> Segments) _cmsFree(ContextID, p ->Segments); + if (p -> Evals) _cmsFree(ContextID, p -> Evals); + if (p ->Table16) _cmsFree(ContextID, p ->Table16); + _cmsFree(ContextID, p); + return NULL; +} + + +// Parametric Fn using floating point +static +cmsFloat64Number DefaultEvalParametricFn(cmsInt32Number Type, const cmsFloat64Number Params[], cmsFloat64Number R) +{ + cmsFloat64Number e, Val, disc; + + switch (Type) { + + // X = Y ^ Gamma + case 1: + if (R < 0) { + + if (fabs(Params[0] - 1.0) < MATRIX_DET_TOLERANCE) + Val = R; + else + Val = 0; + } + else + Val = pow(R, Params[0]); + break; + + // Type 1 Reversed: X = Y ^1/gamma + case -1: + if (R < 0) { + + if (fabs(Params[0] - 1.0) < MATRIX_DET_TOLERANCE) + Val = R; + else + Val = 0; + } + else + Val = pow(R, 1/Params[0]); + break; + + // CIE 122-1966 + // Y = (aX + b)^Gamma | X >= -b/a + // Y = 0 | else + case 2: + disc = -Params[2] / Params[1]; + + if (R >= disc ) { + + e = Params[1]*R + Params[2]; + + if (e > 0) + Val = pow(e, Params[0]); + else + Val = 0; + } + else + Val = 0; + break; + + // Type 2 Reversed + // X = (Y ^1/g - b) / a + case -2: + if (R < 0) + Val = 0; + else + Val = (pow(R, 1.0/Params[0]) - Params[2]) / Params[1]; + + if (Val < 0) + Val = 0; + break; + + + // IEC 61966-3 + // Y = (aX + b)^Gamma | X <= -b/a + // Y = c | else + case 3: + disc = -Params[2] / Params[1]; + if (disc < 0) + disc = 0; + + if (R >= disc) { + + e = Params[1]*R + Params[2]; + + if (e > 0) + Val = pow(e, Params[0]) + Params[3]; + else + Val = 0; + } + else + Val = Params[3]; + break; + + + // Type 3 reversed + // X=((Y-c)^1/g - b)/a | (Y>=c) + // X=-b/a | (Y= Params[3]) { + + e = R - Params[3]; + + if (e > 0) + Val = (pow(e, 1/Params[0]) - Params[2]) / Params[1]; + else + Val = 0; + } + else { + Val = -Params[2] / Params[1]; + } + break; + + + // IEC 61966-2.1 (sRGB) + // Y = (aX + b)^Gamma | X >= d + // Y = cX | X < d + case 4: + if (R >= Params[4]) { + + e = Params[1]*R + Params[2]; + + if (e > 0) + Val = pow(e, Params[0]); + else + Val = 0; + } + else + Val = R * Params[3]; + break; + + // Type 4 reversed + // X=((Y^1/g-b)/a) | Y >= (ad+b)^g + // X=Y/c | Y< (ad+b)^g + case -4: + e = Params[1] * Params[4] + Params[2]; + if (e < 0) + disc = 0; + else + disc = pow(e, Params[0]); + + if (R >= disc) { + + Val = (pow(R, 1.0/Params[0]) - Params[2]) / Params[1]; + } + else { + Val = R / Params[3]; + } + break; + + + // Y = (aX + b)^Gamma + e | X >= d + // Y = cX + f | X < d + case 5: + if (R >= Params[4]) { + + e = Params[1]*R + Params[2]; + + if (e > 0) + Val = pow(e, Params[0]) + Params[5]; + else + Val = Params[5]; + } + else + Val = R*Params[3] + Params[6]; + break; + + + // Reversed type 5 + // X=((Y-e)1/g-b)/a | Y >=(ad+b)^g+e), cd+f + // X=(Y-f)/c | else + case -5: + + disc = Params[3] * Params[4] + Params[6]; + if (R >= disc) { + + e = R - Params[5]; + if (e < 0) + Val = 0; + else + Val = (pow(e, 1.0/Params[0]) - Params[2]) / Params[1]; + } + else { + Val = (R - Params[6]) / Params[3]; + } + break; + + + // Types 6,7,8 comes from segmented curves as described in ICCSpecRevision_02_11_06_Float.pdf + // Type 6 is basically identical to type 5 without d + + // Y = (a * X + b) ^ Gamma + c + case 6: + e = Params[1]*R + Params[2]; + + if (e < 0) + Val = Params[3]; + else + Val = pow(e, Params[0]) + Params[3]; + break; + + // ((Y - c) ^1/Gamma - b) / a + case -6: + e = R - Params[3]; + if (e < 0) + Val = 0; + else + Val = (pow(e, 1.0/Params[0]) - Params[2]) / Params[1]; + break; + + + // Y = a * log (b * X^Gamma + c) + d + case 7: + + e = Params[2] * pow(R, Params[0]) + Params[3]; + if (e <= 0) + Val = Params[4]; + else + Val = Params[1]*log10(e) + Params[4]; + break; + + // (Y - d) / a = log(b * X ^Gamma + c) + // pow(10, (Y-d) / a) = b * X ^Gamma + c + // pow((pow(10, (Y-d) / a) - c) / b, 1/g) = X + case -7: + Val = pow((pow(10.0, (R-Params[4]) / Params[1]) - Params[3]) / Params[2], 1.0 / Params[0]); + break; + + + //Y = a * b^(c*X+d) + e + case 8: + Val = (Params[0] * pow(Params[1], Params[2] * R + Params[3]) + Params[4]); + break; + + + // Y = (log((y-e) / a) / log(b) - d ) / c + // a=0, b=1, c=2, d=3, e=4, + case -8: + + disc = R - Params[4]; + if (disc < 0) Val = 0; + else + Val = (log(disc / Params[0]) / log(Params[1]) - Params[3]) / Params[2]; + break; + + // S-Shaped: (1 - (1-x)^1/g)^1/g + case 108: + Val = pow(1.0 - pow(1 - R, 1/Params[0]), 1/Params[0]); + break; + + // y = (1 - (1-x)^1/g)^1/g + // y^g = (1 - (1-x)^1/g) + // 1 - y^g = (1-x)^1/g + // (1 - y^g)^g = 1 - x + // 1 - (1 - y^g)^g + case -108: + Val = 1 - pow(1 - pow(R, Params[0]), Params[0]); + break; + + default: + // Unsupported parametric curve. Should never reach here + return 0; + } + + return Val; +} + +// Evaluate a segmented funtion for a single value. Return -1 if no valid segment found . +// If fn type is 0, perform an interpolation on the table +static +cmsFloat64Number EvalSegmentedFn(const cmsToneCurve *g, cmsFloat64Number R) +{ + int i; + + for (i = g ->nSegments-1; i >= 0 ; --i) { + + // Check for domain + if ((R > g ->Segments[i].x0) && (R <= g ->Segments[i].x1)) { + + // Type == 0 means segment is sampled + if (g ->Segments[i].Type == 0) { + + cmsFloat32Number R1 = (cmsFloat32Number) (R - g ->Segments[i].x0) / (g ->Segments[i].x1 - g ->Segments[i].x0); + cmsFloat32Number Out; + + // Setup the table (TODO: clean that) + g ->SegInterp[i]-> Table = g ->Segments[i].SampledPoints; + + g ->SegInterp[i] -> Interpolation.LerpFloat(&R1, &Out, g ->SegInterp[i]); + + return Out; + } + else + return g ->Evals[i](g->Segments[i].Type, g ->Segments[i].Params, R); + } + } + + return MINUS_INF; +} + +// Access to estimated low-res table +cmsUInt32Number CMSEXPORT cmsGetToneCurveEstimatedTableEntries(const cmsToneCurve* t) +{ + _cmsAssert(t != NULL); + return t ->nEntries; +} + +const cmsUInt16Number* CMSEXPORT cmsGetToneCurveEstimatedTable(const cmsToneCurve* t) +{ + _cmsAssert(t != NULL); + return t ->Table16; +} + + +// Create an empty gamma curve, by using tables. This specifies only the limited-precision part, and leaves the +// floating point description empty. +cmsToneCurve* CMSEXPORT cmsBuildTabulatedToneCurve16(cmsContext ContextID, cmsInt32Number nEntries, const cmsUInt16Number Values[]) +{ + return AllocateToneCurveStruct(ContextID, nEntries, 0, NULL, Values); +} + +static +int EntriesByGamma(cmsFloat64Number Gamma) +{ + if (fabs(Gamma - 1.0) < 0.001) return 2; + return 4096; +} + + +// Create a segmented gamma, fill the table +cmsToneCurve* CMSEXPORT cmsBuildSegmentedToneCurve(cmsContext ContextID, + cmsInt32Number nSegments, const cmsCurveSegment Segments[]) +{ + int i; + cmsFloat64Number R, Val; + cmsToneCurve* g; + int nGridPoints = 4096; + + _cmsAssert(Segments != NULL); + + // Optimizatin for identity curves. + if (nSegments == 1 && Segments[0].Type == 1) { + + nGridPoints = EntriesByGamma(Segments[0].Params[0]); + } + + g = AllocateToneCurveStruct(ContextID, nGridPoints, nSegments, Segments, NULL); + if (g == NULL) return NULL; + + // Once we have the floating point version, we can approximate a 16 bit table of 4096 entries + // for performance reasons. This table would normally not be used except on 8/16 bits transforms. + for (i=0; i < nGridPoints; i++) { + + R = (cmsFloat64Number) i / (nGridPoints-1); + + Val = EvalSegmentedFn(g, R); + + // Round and saturate + g ->Table16[i] = _cmsQuickSaturateWord(Val * 65535.0); + } + + return g; +} + +// Use a segmented curve to store the floating point table +cmsToneCurve* CMSEXPORT cmsBuildTabulatedToneCurveFloat(cmsContext ContextID, cmsUInt32Number nEntries, const cmsFloat32Number values[]) +{ + cmsCurveSegment Seg[3]; + + // A segmented tone curve should have function segments in the first and last positions + // Initialize segmented curve part up to 0 to constant value = samples[0] + Seg[0].x0 = MINUS_INF; + Seg[0].x1 = 0; + Seg[0].Type = 6; + + Seg[0].Params[0] = 1; + Seg[0].Params[1] = 0; + Seg[0].Params[2] = 0; + Seg[0].Params[3] = values[0]; + Seg[0].Params[4] = 0; + + // From zero to 1 + Seg[1].x0 = 0; + Seg[1].x1 = 1.0; + Seg[1].Type = 0; + + Seg[1].nGridPoints = nEntries; + Seg[1].SampledPoints = (cmsFloat32Number*) values; + + // Final segment is constant = lastsample + Seg[2].x0 = 1.0; + Seg[2].x1 = PLUS_INF; + Seg[2].Type = 6; + + Seg[2].Params[0] = 1; + Seg[2].Params[1] = 0; + Seg[2].Params[2] = 0; + Seg[2].Params[3] = values[nEntries-1]; + Seg[2].Params[4] = 0; + + + return cmsBuildSegmentedToneCurve(ContextID, 3, Seg); +} + +// Parametric curves +// +// Parameters goes as: Curve, a, b, c, d, e, f +// Type is the ICC type +1 +// if type is negative, then the curve is analyticaly inverted +cmsToneCurve* CMSEXPORT cmsBuildParametricToneCurve(cmsContext ContextID, cmsInt32Number Type, const cmsFloat64Number Params[]) +{ + cmsCurveSegment Seg0; + int Pos = 0; + cmsUInt32Number size; + _cmsParametricCurvesCollection* c = GetParametricCurveByType(ContextID, Type, &Pos); + + _cmsAssert(Params != NULL); + + if (c == NULL) { + cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Invalid parametric curve type %d", Type); + return NULL; + } + + memset(&Seg0, 0, sizeof(Seg0)); + + Seg0.x0 = MINUS_INF; + Seg0.x1 = PLUS_INF; + Seg0.Type = Type; + + size = c->ParameterCount[Pos] * sizeof(cmsFloat64Number); + memmove(Seg0.Params, Params, size); + + return cmsBuildSegmentedToneCurve(ContextID, 1, &Seg0); +} + + + +// Build a gamma table based on gamma constant +cmsToneCurve* CMSEXPORT cmsBuildGamma(cmsContext ContextID, cmsFloat64Number Gamma) +{ + return cmsBuildParametricToneCurve(ContextID, 1, &Gamma); +} + + +// Free all memory taken by the gamma curve +void CMSEXPORT cmsFreeToneCurve(cmsToneCurve* Curve) +{ + cmsContext ContextID; + + // added by Xiaochuan Liu + // Curve->InterpParams may be null + if (Curve == NULL || Curve->InterpParams == NULL) return; + + ContextID = Curve ->InterpParams->ContextID; + + _cmsFreeInterpParams(Curve ->InterpParams); + Curve ->InterpParams = NULL; + + if (Curve -> Table16) + { + _cmsFree(ContextID, Curve ->Table16); + Curve ->Table16 = NULL; + } + + if (Curve ->Segments) { + + cmsUInt32Number i; + + for (i=0; i < Curve ->nSegments; i++) { + + if (Curve ->Segments[i].SampledPoints) { + _cmsFree(ContextID, Curve ->Segments[i].SampledPoints); + Curve ->Segments[i].SampledPoints = NULL; + } + + if (Curve ->SegInterp[i] != 0) + { + _cmsFreeInterpParams(Curve->SegInterp[i]); + Curve->SegInterp[i] = NULL; + } + } + + _cmsFree(ContextID, Curve ->Segments); + Curve ->Segments = NULL; + _cmsFree(ContextID, Curve ->SegInterp); + Curve ->SegInterp = NULL; + } + + if (Curve -> Evals) + { + _cmsFree(ContextID, Curve -> Evals); + Curve -> Evals = NULL; + } + + if (Curve) + { + _cmsFree(ContextID, Curve); + Curve = NULL; + } +} + +// Utility function, free 3 gamma tables +void CMSEXPORT cmsFreeToneCurveTriple(cmsToneCurve* Curve[3]) +{ + + _cmsAssert(Curve != NULL); + + if (Curve[0] != NULL) cmsFreeToneCurve(Curve[0]); + if (Curve[1] != NULL) cmsFreeToneCurve(Curve[1]); + if (Curve[2] != NULL) cmsFreeToneCurve(Curve[2]); + + Curve[0] = Curve[1] = Curve[2] = NULL; +} + + +// Duplicate a gamma table +cmsToneCurve* CMSEXPORT cmsDupToneCurve(const cmsToneCurve* In) +{ + // Xiaochuan Liu + // fix openpdf bug(mantis id:0055683, google id:360198) + // the function CurveSetElemTypeFree in cmslut.c also needs to check pointer + if (In == NULL || In ->InterpParams == NULL || In ->Segments == NULL || In ->Table16 == NULL) return NULL; + + return AllocateToneCurveStruct(In ->InterpParams ->ContextID, In ->nEntries, In ->nSegments, In ->Segments, In ->Table16); +} + +// Joins two curves for X and Y. Curves should be monotonic. +// We want to get +// +// y = Y^-1(X(t)) +// +cmsToneCurve* CMSEXPORT cmsJoinToneCurve(cmsContext ContextID, + const cmsToneCurve* X, + const cmsToneCurve* Y, cmsUInt32Number nResultingPoints) +{ + cmsToneCurve* out = NULL; + cmsToneCurve* Yreversed = NULL; + cmsFloat32Number t, x; + cmsFloat32Number* Res = NULL; + cmsUInt32Number i; + + + _cmsAssert(X != NULL); + _cmsAssert(Y != NULL); + + Yreversed = cmsReverseToneCurveEx(nResultingPoints, Y); + if (Yreversed == NULL) goto Error; + + Res = (cmsFloat32Number*) _cmsCalloc(ContextID, nResultingPoints, sizeof(cmsFloat32Number)); + if (Res == NULL) goto Error; + + //Iterate + for (i=0; i < nResultingPoints; i++) { + + t = (cmsFloat32Number) i / (nResultingPoints-1); + x = cmsEvalToneCurveFloat(X, t); + Res[i] = cmsEvalToneCurveFloat(Yreversed, x); + } + + // Allocate space for output + out = cmsBuildTabulatedToneCurveFloat(ContextID, nResultingPoints, Res); + +Error: + + if (Res != NULL) _cmsFree(ContextID, Res); + if (Yreversed != NULL) cmsFreeToneCurve(Yreversed); + + return out; +} + + + +// Get the surrounding nodes. This is tricky on non-monotonic tables +static +int GetInterval(cmsFloat64Number In, const cmsUInt16Number LutTable[], const struct _cms_interp_struc* p) +{ + int i; + int y0, y1; + + // A 1 point table is not allowed + if (p -> Domain[0] < 1) return -1; + + // Let's see if ascending or descending. + if (LutTable[0] < LutTable[p ->Domain[0]]) { + + // Table is overall ascending + for (i=p->Domain[0]-1; i >=0; --i) { + + y0 = LutTable[i]; + y1 = LutTable[i+1]; + + if (y0 <= y1) { // Increasing + if (In >= y0 && In <= y1) return i; + } + else + if (y1 < y0) { // Decreasing + if (In >= y1 && In <= y0) return i; + } + } + } + else { + // Table is overall descending + for (i=0; i < (int) p -> Domain[0]; i++) { + + y0 = LutTable[i]; + y1 = LutTable[i+1]; + + if (y0 <= y1) { // Increasing + if (In >= y0 && In <= y1) return i; + } + else + if (y1 < y0) { // Decreasing + if (In >= y1 && In <= y0) return i; + } + } + } + + return -1; +} + +// Reverse a gamma table +cmsToneCurve* CMSEXPORT cmsReverseToneCurveEx(cmsInt32Number nResultSamples, const cmsToneCurve* InCurve) +{ + cmsToneCurve *out; + cmsFloat64Number a = 0, b = 0, y, x1, y1, x2, y2; + int i, j; + int Ascending; + + _cmsAssert(InCurve != NULL); + + // Try to reverse it analytically whatever possible + + 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), + InCurve -> Segments[0].Params); + } + + // Nope, reverse the table. + out = cmsBuildTabulatedToneCurve16(InCurve ->InterpParams->ContextID, nResultSamples, NULL); + if (out == NULL) + return NULL; + + // We want to know if this is an ascending or descending table + Ascending = !cmsIsToneCurveDescending(InCurve); + + // Iterate across Y axis + for (i=0; i < nResultSamples; i++) { + + y = (cmsFloat64Number) i * 65535.0 / (nResultSamples - 1); + + // Find interval in which y is within. + j = GetInterval(y, InCurve->Table16, InCurve->InterpParams); + if (j >= 0) { + + + // Get limits of interval + x1 = InCurve ->Table16[j]; + x2 = InCurve ->Table16[j+1]; + + y1 = (cmsFloat64Number) (j * 65535.0) / (InCurve ->nEntries - 1); + y2 = (cmsFloat64Number) ((j+1) * 65535.0 ) / (InCurve ->nEntries - 1); + + // If collapsed, then use any + if (x1 == x2) { + + out ->Table16[i] = _cmsQuickSaturateWord(Ascending ? y2 : y1); + continue; + + } else { + + // Interpolate + a = (y2 - y1) / (x2 - x1); + b = y2 - a * x2; + } + } + + out ->Table16[i] = _cmsQuickSaturateWord(a* y + b); + } + + + return out; +} + +// Reverse a gamma table +cmsToneCurve* CMSEXPORT cmsReverseToneCurve(const cmsToneCurve* InGamma) +{ + _cmsAssert(InGamma != NULL); + + return cmsReverseToneCurveEx(4096, InGamma); +} + +// From: Eilers, P.H.C. (1994) Smoothing and interpolation with finite +// differences. in: Graphic Gems IV, Heckbert, P.S. (ed.), Academic press. +// +// Smoothing and interpolation with second differences. +// +// Input: weights (w), data (y): vector from 1 to m. +// Input: smoothing parameter (lambda), length (m). +// Output: smoothed vector (z): vector from 1 to m. + +static +cmsBool smooth2(cmsContext ContextID, cmsFloat32Number w[], cmsFloat32Number y[], cmsFloat32Number z[], cmsFloat32Number lambda, int m) +{ + int i, i1, i2; + cmsFloat32Number *c, *d, *e; + cmsBool st; + + + c = (cmsFloat32Number*) _cmsCalloc(ContextID, MAX_NODES_IN_CURVE, sizeof(cmsFloat32Number)); + d = (cmsFloat32Number*) _cmsCalloc(ContextID, MAX_NODES_IN_CURVE, sizeof(cmsFloat32Number)); + e = (cmsFloat32Number*) _cmsCalloc(ContextID, MAX_NODES_IN_CURVE, sizeof(cmsFloat32Number)); + + if (c != NULL && d != NULL && e != NULL) { + + + d[1] = w[1] + lambda; + c[1] = -2 * lambda / d[1]; + e[1] = lambda /d[1]; + z[1] = w[1] * y[1]; + d[2] = w[2] + 5 * lambda - d[1] * c[1] * c[1]; + c[2] = (-4 * lambda - d[1] * c[1] * e[1]) / d[2]; + e[2] = lambda / d[2]; + z[2] = w[2] * y[2] - c[1] * z[1]; + + for (i = 3; i < m - 1; i++) { + i1 = i - 1; i2 = i - 2; + d[i]= w[i] + 6 * lambda - c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2]; + c[i] = (-4 * lambda -d[i1] * c[i1] * e[i1])/ d[i]; + e[i] = lambda / d[i]; + z[i] = w[i] * y[i] - c[i1] * z[i1] - e[i2] * z[i2]; + } + + i1 = m - 2; i2 = m - 3; + + d[m - 1] = w[m - 1] + 5 * lambda -c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2]; + c[m - 1] = (-2 * lambda - d[i1] * c[i1] * e[i1]) / d[m - 1]; + z[m - 1] = w[m - 1] * y[m - 1] - c[i1] * z[i1] - e[i2] * z[i2]; + i1 = m - 1; i2 = m - 2; + + d[m] = w[m] + lambda - c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2]; + z[m] = (w[m] * y[m] - c[i1] * z[i1] - e[i2] * z[i2]) / d[m]; + z[m - 1] = z[m - 1] / d[m - 1] - c[m - 1] * z[m]; + + for (i = m - 2; 1<= i; i--) + z[i] = z[i] / d[i] - c[i] * z[i + 1] - e[i] * z[i + 2]; + + st = TRUE; + } + else st = FALSE; + + if (c != NULL) _cmsFree(ContextID, c); + if (d != NULL) _cmsFree(ContextID, d); + if (e != NULL) _cmsFree(ContextID, e); + + return st; +} + +// Smooths a curve sampled at regular intervals. +cmsBool CMSEXPORT cmsSmoothToneCurve(cmsToneCurve* Tab, cmsFloat64Number lambda) +{ + cmsFloat32Number w[MAX_NODES_IN_CURVE], y[MAX_NODES_IN_CURVE], z[MAX_NODES_IN_CURVE]; + int i, nItems, Zeros, Poles; + + if (Tab == NULL) return FALSE; + + if (cmsIsToneCurveLinear(Tab)) return TRUE; // Nothing to do + + nItems = Tab -> nEntries; + + if (nItems >= MAX_NODES_IN_CURVE) { + cmsSignalError(Tab ->InterpParams->ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: too many points."); + return FALSE; + } + + memset(w, 0, nItems * sizeof(cmsFloat32Number)); + memset(y, 0, nItems * sizeof(cmsFloat32Number)); + memset(z, 0, nItems * sizeof(cmsFloat32Number)); + + for (i=0; i < nItems; i++) + { + y[i+1] = (cmsFloat32Number) Tab -> Table16[i]; + w[i+1] = 1.0; + } + + if (!smooth2(Tab ->InterpParams->ContextID, w, y, z, (cmsFloat32Number) lambda, nItems)) return FALSE; + + // Do some reality - checking... + Zeros = Poles = 0; + for (i=nItems; i > 1; --i) { + + if (z[i] == 0.) Zeros++; + if (z[i] >= 65535.) Poles++; + if (z[i] < z[i-1]) { + cmsSignalError(Tab ->InterpParams->ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Non-Monotonic."); + return FALSE; + } + } + + if (Zeros > (nItems / 3)) { + cmsSignalError(Tab ->InterpParams->ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Degenerated, mostly zeros."); + return FALSE; + } + if (Poles > (nItems / 3)) { + cmsSignalError(Tab ->InterpParams->ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Degenerated, mostly poles."); + return FALSE; + } + + // Seems ok + for (i=0; i < nItems; i++) { + + // Clamp to cmsUInt16Number + Tab -> Table16[i] = _cmsQuickSaturateWord(z[i+1]); + } + + return TRUE; +} + +// Is a table linear? Do not use parametric since we cannot guarantee some weird parameters resulting +// in a linear table. This way assures it is linear in 12 bits, which should be enought in most cases. +cmsBool CMSEXPORT cmsIsToneCurveLinear(const cmsToneCurve* Curve) +{ + cmsUInt32Number i; + int diff; + + _cmsAssert(Curve != NULL); + + for (i=0; i < Curve ->nEntries; i++) { + + diff = abs((int) Curve->Table16[i] - (int) _cmsQuantizeVal(i, Curve ->nEntries)); + if (diff > 0x0f) + return FALSE; + } + + return TRUE; +} + +// Same, but for monotonicity +cmsBool CMSEXPORT cmsIsToneCurveMonotonic(const cmsToneCurve* t) +{ + int n; + int i, last; + cmsBool lDescending; + + _cmsAssert(t != NULL); + + // Degenerated curves are monotonic? Ok, let's pass them + n = t ->nEntries; + if (n < 2) return TRUE; + + // Curve direction + lDescending = cmsIsToneCurveDescending(t); + + if (lDescending) { + + last = t ->Table16[0]; + + for (i = 1; i < n; i++) { + + if (t ->Table16[i] - last > 2) // We allow some ripple + return FALSE; + else + last = t ->Table16[i]; + + } + } + else { + + last = t ->Table16[n-1]; + + for (i = n-2; i >= 0; --i) { + + if (t ->Table16[i] - last > 2) + return FALSE; + else + last = t ->Table16[i]; + + } + } + + return TRUE; +} + +// Same, but for descending tables +cmsBool CMSEXPORT cmsIsToneCurveDescending(const cmsToneCurve* t) +{ + _cmsAssert(t != NULL); + + return t ->Table16[0] > t ->Table16[t ->nEntries-1]; +} + + +// Another info fn: is out gamma table multisegment? +cmsBool CMSEXPORT cmsIsToneCurveMultisegment(const cmsToneCurve* t) +{ + _cmsAssert(t != NULL); + + return t -> nSegments > 1; +} + +cmsInt32Number CMSEXPORT cmsGetToneCurveParametricType(const cmsToneCurve* t) +{ + _cmsAssert(t != NULL); + + if (t -> nSegments != 1) return 0; + return t ->Segments[0].Type; +} + +// We need accuracy this time +cmsFloat32Number CMSEXPORT cmsEvalToneCurveFloat(const cmsToneCurve* Curve, cmsFloat32Number v) +{ + _cmsAssert(Curve != NULL); + + // Check for 16 bits table. If so, this is a limited-precision tone curve + if (Curve ->nSegments == 0) { + + cmsUInt16Number In, Out; + + In = (cmsUInt16Number) _cmsQuickSaturateWord(v * 65535.0); + Out = cmsEvalToneCurve16(Curve, In); + + return (cmsFloat32Number) (Out / 65535.0); + } + + return (cmsFloat32Number) EvalSegmentedFn(Curve, v); +} + +// We need xput over here +cmsUInt16Number CMSEXPORT cmsEvalToneCurve16(const cmsToneCurve* Curve, cmsUInt16Number v) +{ + cmsUInt16Number out; + + _cmsAssert(Curve != NULL); + + Curve ->InterpParams ->Interpolation.Lerp16(&v, &out, Curve ->InterpParams); + return out; +} + + +// Least squares fitting. +// A mathematical procedure for finding the best-fitting curve to a given set of points by +// minimizing the sum of the squares of the offsets ("the residuals") of the points from the curve. +// The sum of the squares of the offsets is used instead of the offset absolute values because +// this allows the residuals to be treated as a continuous differentiable quantity. +// +// y = f(x) = x ^ g +// +// R = (yi - (xi^g)) +// R2 = (yi - (xi^g))2 +// SUM R2 = SUM (yi - (xi^g))2 +// +// dR2/dg = -2 SUM x^g log(x)(y - x^g) +// solving for dR2/dg = 0 +// +// g = 1/n * SUM(log(y) / log(x)) + +cmsFloat64Number CMSEXPORT cmsEstimateGamma(const cmsToneCurve* t, cmsFloat64Number Precision) +{ + cmsFloat64Number gamma, sum, sum2; + cmsFloat64Number n, x, y, Std; + cmsUInt32Number i; + + _cmsAssert(t != NULL); + + sum = sum2 = n = 0; + + // Excluding endpoints + for (i=1; i < (MAX_NODES_IN_CURVE-1); i++) { + + x = (cmsFloat64Number) i / (MAX_NODES_IN_CURVE-1); + y = (cmsFloat64Number) cmsEvalToneCurveFloat(t, (cmsFloat32Number) x); + + // Avoid 7% on lower part to prevent + // artifacts due to linear ramps + + if (y > 0. && y < 1. && x > 0.07) { + + gamma = log(y) / log(x); + sum += gamma; + sum2 += gamma * gamma; + n++; + } + } + + // Take a look on SD to see if gamma isn't exponential at all + Std = sqrt((n * sum2 - sum * sum) / (n*(n-1))); + + if (Std > Precision) + return -1.0; + + return (sum / n); // The mean +} diff --git a/third_party/lcms/src/cmsgmt.c b/third_party/lcms/src/cmsgmt.c new file mode 100644 index 0000000000..1103363a78 --- /dev/null +++ b/third_party/lcms/src/cmsgmt.c @@ -0,0 +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; +} diff --git a/third_party/lcms/src/cmshalf.c b/third_party/lcms/src/cmshalf.c new file mode 100644 index 0000000000..f038b57b4c --- /dev/null +++ b/third_party/lcms/src/cmshalf.c @@ -0,0 +1,534 @@ +//--------------------------------------------------------------------------------- +// +// 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" + +#ifndef CMS_NO_HALF_SUPPORT + +// This code is inspired in the paper "Fast Half Float Conversions" +// by Jeroen van der Zijp + +static cmsUInt32Number Mantissa[2048] = { + +0x00000000, 0x33800000, 0x34000000, 0x34400000, 0x34800000, 0x34a00000, +0x34c00000, 0x34e00000, 0x35000000, 0x35100000, 0x35200000, 0x35300000, +0x35400000, 0x35500000, 0x35600000, 0x35700000, 0x35800000, 0x35880000, +0x35900000, 0x35980000, 0x35a00000, 0x35a80000, 0x35b00000, 0x35b80000, +0x35c00000, 0x35c80000, 0x35d00000, 0x35d80000, 0x35e00000, 0x35e80000, +0x35f00000, 0x35f80000, 0x36000000, 0x36040000, 0x36080000, 0x360c0000, +0x36100000, 0x36140000, 0x36180000, 0x361c0000, 0x36200000, 0x36240000, +0x36280000, 0x362c0000, 0x36300000, 0x36340000, 0x36380000, 0x363c0000, +0x36400000, 0x36440000, 0x36480000, 0x364c0000, 0x36500000, 0x36540000, +0x36580000, 0x365c0000, 0x36600000, 0x36640000, 0x36680000, 0x366c0000, +0x36700000, 0x36740000, 0x36780000, 0x367c0000, 0x36800000, 0x36820000, +0x36840000, 0x36860000, 0x36880000, 0x368a0000, 0x368c0000, 0x368e0000, +0x36900000, 0x36920000, 0x36940000, 0x36960000, 0x36980000, 0x369a0000, +0x369c0000, 0x369e0000, 0x36a00000, 0x36a20000, 0x36a40000, 0x36a60000, +0x36a80000, 0x36aa0000, 0x36ac0000, 0x36ae0000, 0x36b00000, 0x36b20000, +0x36b40000, 0x36b60000, 0x36b80000, 0x36ba0000, 0x36bc0000, 0x36be0000, +0x36c00000, 0x36c20000, 0x36c40000, 0x36c60000, 0x36c80000, 0x36ca0000, +0x36cc0000, 0x36ce0000, 0x36d00000, 0x36d20000, 0x36d40000, 0x36d60000, +0x36d80000, 0x36da0000, 0x36dc0000, 0x36de0000, 0x36e00000, 0x36e20000, +0x36e40000, 0x36e60000, 0x36e80000, 0x36ea0000, 0x36ec0000, 0x36ee0000, +0x36f00000, 0x36f20000, 0x36f40000, 0x36f60000, 0x36f80000, 0x36fa0000, +0x36fc0000, 0x36fe0000, 0x37000000, 0x37010000, 0x37020000, 0x37030000, +0x37040000, 0x37050000, 0x37060000, 0x37070000, 0x37080000, 0x37090000, +0x370a0000, 0x370b0000, 0x370c0000, 0x370d0000, 0x370e0000, 0x370f0000, +0x37100000, 0x37110000, 0x37120000, 0x37130000, 0x37140000, 0x37150000, +0x37160000, 0x37170000, 0x37180000, 0x37190000, 0x371a0000, 0x371b0000, +0x371c0000, 0x371d0000, 0x371e0000, 0x371f0000, 0x37200000, 0x37210000, +0x37220000, 0x37230000, 0x37240000, 0x37250000, 0x37260000, 0x37270000, +0x37280000, 0x37290000, 0x372a0000, 0x372b0000, 0x372c0000, 0x372d0000, +0x372e0000, 0x372f0000, 0x37300000, 0x37310000, 0x37320000, 0x37330000, +0x37340000, 0x37350000, 0x37360000, 0x37370000, 0x37380000, 0x37390000, +0x373a0000, 0x373b0000, 0x373c0000, 0x373d0000, 0x373e0000, 0x373f0000, +0x37400000, 0x37410000, 0x37420000, 0x37430000, 0x37440000, 0x37450000, +0x37460000, 0x37470000, 0x37480000, 0x37490000, 0x374a0000, 0x374b0000, +0x374c0000, 0x374d0000, 0x374e0000, 0x374f0000, 0x37500000, 0x37510000, +0x37520000, 0x37530000, 0x37540000, 0x37550000, 0x37560000, 0x37570000, +0x37580000, 0x37590000, 0x375a0000, 0x375b0000, 0x375c0000, 0x375d0000, +0x375e0000, 0x375f0000, 0x37600000, 0x37610000, 0x37620000, 0x37630000, +0x37640000, 0x37650000, 0x37660000, 0x37670000, 0x37680000, 0x37690000, +0x376a0000, 0x376b0000, 0x376c0000, 0x376d0000, 0x376e0000, 0x376f0000, +0x37700000, 0x37710000, 0x37720000, 0x37730000, 0x37740000, 0x37750000, +0x37760000, 0x37770000, 0x37780000, 0x37790000, 0x377a0000, 0x377b0000, +0x377c0000, 0x377d0000, 0x377e0000, 0x377f0000, 0x37800000, 0x37808000, +0x37810000, 0x37818000, 0x37820000, 0x37828000, 0x37830000, 0x37838000, +0x37840000, 0x37848000, 0x37850000, 0x37858000, 0x37860000, 0x37868000, +0x37870000, 0x37878000, 0x37880000, 0x37888000, 0x37890000, 0x37898000, +0x378a0000, 0x378a8000, 0x378b0000, 0x378b8000, 0x378c0000, 0x378c8000, +0x378d0000, 0x378d8000, 0x378e0000, 0x378e8000, 0x378f0000, 0x378f8000, +0x37900000, 0x37908000, 0x37910000, 0x37918000, 0x37920000, 0x37928000, +0x37930000, 0x37938000, 0x37940000, 0x37948000, 0x37950000, 0x37958000, +0x37960000, 0x37968000, 0x37970000, 0x37978000, 0x37980000, 0x37988000, +0x37990000, 0x37998000, 0x379a0000, 0x379a8000, 0x379b0000, 0x379b8000, +0x379c0000, 0x379c8000, 0x379d0000, 0x379d8000, 0x379e0000, 0x379e8000, +0x379f0000, 0x379f8000, 0x37a00000, 0x37a08000, 0x37a10000, 0x37a18000, +0x37a20000, 0x37a28000, 0x37a30000, 0x37a38000, 0x37a40000, 0x37a48000, +0x37a50000, 0x37a58000, 0x37a60000, 0x37a68000, 0x37a70000, 0x37a78000, +0x37a80000, 0x37a88000, 0x37a90000, 0x37a98000, 0x37aa0000, 0x37aa8000, +0x37ab0000, 0x37ab8000, 0x37ac0000, 0x37ac8000, 0x37ad0000, 0x37ad8000, +0x37ae0000, 0x37ae8000, 0x37af0000, 0x37af8000, 0x37b00000, 0x37b08000, +0x37b10000, 0x37b18000, 0x37b20000, 0x37b28000, 0x37b30000, 0x37b38000, +0x37b40000, 0x37b48000, 0x37b50000, 0x37b58000, 0x37b60000, 0x37b68000, +0x37b70000, 0x37b78000, 0x37b80000, 0x37b88000, 0x37b90000, 0x37b98000, +0x37ba0000, 0x37ba8000, 0x37bb0000, 0x37bb8000, 0x37bc0000, 0x37bc8000, +0x37bd0000, 0x37bd8000, 0x37be0000, 0x37be8000, 0x37bf0000, 0x37bf8000, +0x37c00000, 0x37c08000, 0x37c10000, 0x37c18000, 0x37c20000, 0x37c28000, +0x37c30000, 0x37c38000, 0x37c40000, 0x37c48000, 0x37c50000, 0x37c58000, +0x37c60000, 0x37c68000, 0x37c70000, 0x37c78000, 0x37c80000, 0x37c88000, +0x37c90000, 0x37c98000, 0x37ca0000, 0x37ca8000, 0x37cb0000, 0x37cb8000, +0x37cc0000, 0x37cc8000, 0x37cd0000, 0x37cd8000, 0x37ce0000, 0x37ce8000, +0x37cf0000, 0x37cf8000, 0x37d00000, 0x37d08000, 0x37d10000, 0x37d18000, +0x37d20000, 0x37d28000, 0x37d30000, 0x37d38000, 0x37d40000, 0x37d48000, +0x37d50000, 0x37d58000, 0x37d60000, 0x37d68000, 0x37d70000, 0x37d78000, +0x37d80000, 0x37d88000, 0x37d90000, 0x37d98000, 0x37da0000, 0x37da8000, +0x37db0000, 0x37db8000, 0x37dc0000, 0x37dc8000, 0x37dd0000, 0x37dd8000, +0x37de0000, 0x37de8000, 0x37df0000, 0x37df8000, 0x37e00000, 0x37e08000, +0x37e10000, 0x37e18000, 0x37e20000, 0x37e28000, 0x37e30000, 0x37e38000, +0x37e40000, 0x37e48000, 0x37e50000, 0x37e58000, 0x37e60000, 0x37e68000, +0x37e70000, 0x37e78000, 0x37e80000, 0x37e88000, 0x37e90000, 0x37e98000, +0x37ea0000, 0x37ea8000, 0x37eb0000, 0x37eb8000, 0x37ec0000, 0x37ec8000, +0x37ed0000, 0x37ed8000, 0x37ee0000, 0x37ee8000, 0x37ef0000, 0x37ef8000, +0x37f00000, 0x37f08000, 0x37f10000, 0x37f18000, 0x37f20000, 0x37f28000, +0x37f30000, 0x37f38000, 0x37f40000, 0x37f48000, 0x37f50000, 0x37f58000, +0x37f60000, 0x37f68000, 0x37f70000, 0x37f78000, 0x37f80000, 0x37f88000, +0x37f90000, 0x37f98000, 0x37fa0000, 0x37fa8000, 0x37fb0000, 0x37fb8000, +0x37fc0000, 0x37fc8000, 0x37fd0000, 0x37fd8000, 0x37fe0000, 0x37fe8000, +0x37ff0000, 0x37ff8000, 0x38000000, 0x38004000, 0x38008000, 0x3800c000, +0x38010000, 0x38014000, 0x38018000, 0x3801c000, 0x38020000, 0x38024000, +0x38028000, 0x3802c000, 0x38030000, 0x38034000, 0x38038000, 0x3803c000, +0x38040000, 0x38044000, 0x38048000, 0x3804c000, 0x38050000, 0x38054000, +0x38058000, 0x3805c000, 0x38060000, 0x38064000, 0x38068000, 0x3806c000, +0x38070000, 0x38074000, 0x38078000, 0x3807c000, 0x38080000, 0x38084000, +0x38088000, 0x3808c000, 0x38090000, 0x38094000, 0x38098000, 0x3809c000, +0x380a0000, 0x380a4000, 0x380a8000, 0x380ac000, 0x380b0000, 0x380b4000, +0x380b8000, 0x380bc000, 0x380c0000, 0x380c4000, 0x380c8000, 0x380cc000, +0x380d0000, 0x380d4000, 0x380d8000, 0x380dc000, 0x380e0000, 0x380e4000, +0x380e8000, 0x380ec000, 0x380f0000, 0x380f4000, 0x380f8000, 0x380fc000, +0x38100000, 0x38104000, 0x38108000, 0x3810c000, 0x38110000, 0x38114000, +0x38118000, 0x3811c000, 0x38120000, 0x38124000, 0x38128000, 0x3812c000, +0x38130000, 0x38134000, 0x38138000, 0x3813c000, 0x38140000, 0x38144000, +0x38148000, 0x3814c000, 0x38150000, 0x38154000, 0x38158000, 0x3815c000, +0x38160000, 0x38164000, 0x38168000, 0x3816c000, 0x38170000, 0x38174000, +0x38178000, 0x3817c000, 0x38180000, 0x38184000, 0x38188000, 0x3818c000, +0x38190000, 0x38194000, 0x38198000, 0x3819c000, 0x381a0000, 0x381a4000, +0x381a8000, 0x381ac000, 0x381b0000, 0x381b4000, 0x381b8000, 0x381bc000, +0x381c0000, 0x381c4000, 0x381c8000, 0x381cc000, 0x381d0000, 0x381d4000, +0x381d8000, 0x381dc000, 0x381e0000, 0x381e4000, 0x381e8000, 0x381ec000, +0x381f0000, 0x381f4000, 0x381f8000, 0x381fc000, 0x38200000, 0x38204000, +0x38208000, 0x3820c000, 0x38210000, 0x38214000, 0x38218000, 0x3821c000, +0x38220000, 0x38224000, 0x38228000, 0x3822c000, 0x38230000, 0x38234000, +0x38238000, 0x3823c000, 0x38240000, 0x38244000, 0x38248000, 0x3824c000, +0x38250000, 0x38254000, 0x38258000, 0x3825c000, 0x38260000, 0x38264000, +0x38268000, 0x3826c000, 0x38270000, 0x38274000, 0x38278000, 0x3827c000, +0x38280000, 0x38284000, 0x38288000, 0x3828c000, 0x38290000, 0x38294000, +0x38298000, 0x3829c000, 0x382a0000, 0x382a4000, 0x382a8000, 0x382ac000, +0x382b0000, 0x382b4000, 0x382b8000, 0x382bc000, 0x382c0000, 0x382c4000, +0x382c8000, 0x382cc000, 0x382d0000, 0x382d4000, 0x382d8000, 0x382dc000, +0x382e0000, 0x382e4000, 0x382e8000, 0x382ec000, 0x382f0000, 0x382f4000, +0x382f8000, 0x382fc000, 0x38300000, 0x38304000, 0x38308000, 0x3830c000, +0x38310000, 0x38314000, 0x38318000, 0x3831c000, 0x38320000, 0x38324000, +0x38328000, 0x3832c000, 0x38330000, 0x38334000, 0x38338000, 0x3833c000, +0x38340000, 0x38344000, 0x38348000, 0x3834c000, 0x38350000, 0x38354000, +0x38358000, 0x3835c000, 0x38360000, 0x38364000, 0x38368000, 0x3836c000, +0x38370000, 0x38374000, 0x38378000, 0x3837c000, 0x38380000, 0x38384000, +0x38388000, 0x3838c000, 0x38390000, 0x38394000, 0x38398000, 0x3839c000, +0x383a0000, 0x383a4000, 0x383a8000, 0x383ac000, 0x383b0000, 0x383b4000, +0x383b8000, 0x383bc000, 0x383c0000, 0x383c4000, 0x383c8000, 0x383cc000, +0x383d0000, 0x383d4000, 0x383d8000, 0x383dc000, 0x383e0000, 0x383e4000, +0x383e8000, 0x383ec000, 0x383f0000, 0x383f4000, 0x383f8000, 0x383fc000, +0x38400000, 0x38404000, 0x38408000, 0x3840c000, 0x38410000, 0x38414000, +0x38418000, 0x3841c000, 0x38420000, 0x38424000, 0x38428000, 0x3842c000, +0x38430000, 0x38434000, 0x38438000, 0x3843c000, 0x38440000, 0x38444000, +0x38448000, 0x3844c000, 0x38450000, 0x38454000, 0x38458000, 0x3845c000, +0x38460000, 0x38464000, 0x38468000, 0x3846c000, 0x38470000, 0x38474000, +0x38478000, 0x3847c000, 0x38480000, 0x38484000, 0x38488000, 0x3848c000, +0x38490000, 0x38494000, 0x38498000, 0x3849c000, 0x384a0000, 0x384a4000, +0x384a8000, 0x384ac000, 0x384b0000, 0x384b4000, 0x384b8000, 0x384bc000, +0x384c0000, 0x384c4000, 0x384c8000, 0x384cc000, 0x384d0000, 0x384d4000, +0x384d8000, 0x384dc000, 0x384e0000, 0x384e4000, 0x384e8000, 0x384ec000, +0x384f0000, 0x384f4000, 0x384f8000, 0x384fc000, 0x38500000, 0x38504000, +0x38508000, 0x3850c000, 0x38510000, 0x38514000, 0x38518000, 0x3851c000, +0x38520000, 0x38524000, 0x38528000, 0x3852c000, 0x38530000, 0x38534000, +0x38538000, 0x3853c000, 0x38540000, 0x38544000, 0x38548000, 0x3854c000, +0x38550000, 0x38554000, 0x38558000, 0x3855c000, 0x38560000, 0x38564000, +0x38568000, 0x3856c000, 0x38570000, 0x38574000, 0x38578000, 0x3857c000, +0x38580000, 0x38584000, 0x38588000, 0x3858c000, 0x38590000, 0x38594000, +0x38598000, 0x3859c000, 0x385a0000, 0x385a4000, 0x385a8000, 0x385ac000, +0x385b0000, 0x385b4000, 0x385b8000, 0x385bc000, 0x385c0000, 0x385c4000, +0x385c8000, 0x385cc000, 0x385d0000, 0x385d4000, 0x385d8000, 0x385dc000, +0x385e0000, 0x385e4000, 0x385e8000, 0x385ec000, 0x385f0000, 0x385f4000, +0x385f8000, 0x385fc000, 0x38600000, 0x38604000, 0x38608000, 0x3860c000, +0x38610000, 0x38614000, 0x38618000, 0x3861c000, 0x38620000, 0x38624000, +0x38628000, 0x3862c000, 0x38630000, 0x38634000, 0x38638000, 0x3863c000, +0x38640000, 0x38644000, 0x38648000, 0x3864c000, 0x38650000, 0x38654000, +0x38658000, 0x3865c000, 0x38660000, 0x38664000, 0x38668000, 0x3866c000, +0x38670000, 0x38674000, 0x38678000, 0x3867c000, 0x38680000, 0x38684000, +0x38688000, 0x3868c000, 0x38690000, 0x38694000, 0x38698000, 0x3869c000, +0x386a0000, 0x386a4000, 0x386a8000, 0x386ac000, 0x386b0000, 0x386b4000, +0x386b8000, 0x386bc000, 0x386c0000, 0x386c4000, 0x386c8000, 0x386cc000, +0x386d0000, 0x386d4000, 0x386d8000, 0x386dc000, 0x386e0000, 0x386e4000, +0x386e8000, 0x386ec000, 0x386f0000, 0x386f4000, 0x386f8000, 0x386fc000, +0x38700000, 0x38704000, 0x38708000, 0x3870c000, 0x38710000, 0x38714000, +0x38718000, 0x3871c000, 0x38720000, 0x38724000, 0x38728000, 0x3872c000, +0x38730000, 0x38734000, 0x38738000, 0x3873c000, 0x38740000, 0x38744000, +0x38748000, 0x3874c000, 0x38750000, 0x38754000, 0x38758000, 0x3875c000, +0x38760000, 0x38764000, 0x38768000, 0x3876c000, 0x38770000, 0x38774000, +0x38778000, 0x3877c000, 0x38780000, 0x38784000, 0x38788000, 0x3878c000, +0x38790000, 0x38794000, 0x38798000, 0x3879c000, 0x387a0000, 0x387a4000, +0x387a8000, 0x387ac000, 0x387b0000, 0x387b4000, 0x387b8000, 0x387bc000, +0x387c0000, 0x387c4000, 0x387c8000, 0x387cc000, 0x387d0000, 0x387d4000, +0x387d8000, 0x387dc000, 0x387e0000, 0x387e4000, 0x387e8000, 0x387ec000, +0x387f0000, 0x387f4000, 0x387f8000, 0x387fc000, 0x38000000, 0x38002000, +0x38004000, 0x38006000, 0x38008000, 0x3800a000, 0x3800c000, 0x3800e000, +0x38010000, 0x38012000, 0x38014000, 0x38016000, 0x38018000, 0x3801a000, +0x3801c000, 0x3801e000, 0x38020000, 0x38022000, 0x38024000, 0x38026000, +0x38028000, 0x3802a000, 0x3802c000, 0x3802e000, 0x38030000, 0x38032000, +0x38034000, 0x38036000, 0x38038000, 0x3803a000, 0x3803c000, 0x3803e000, +0x38040000, 0x38042000, 0x38044000, 0x38046000, 0x38048000, 0x3804a000, +0x3804c000, 0x3804e000, 0x38050000, 0x38052000, 0x38054000, 0x38056000, +0x38058000, 0x3805a000, 0x3805c000, 0x3805e000, 0x38060000, 0x38062000, +0x38064000, 0x38066000, 0x38068000, 0x3806a000, 0x3806c000, 0x3806e000, +0x38070000, 0x38072000, 0x38074000, 0x38076000, 0x38078000, 0x3807a000, +0x3807c000, 0x3807e000, 0x38080000, 0x38082000, 0x38084000, 0x38086000, +0x38088000, 0x3808a000, 0x3808c000, 0x3808e000, 0x38090000, 0x38092000, +0x38094000, 0x38096000, 0x38098000, 0x3809a000, 0x3809c000, 0x3809e000, +0x380a0000, 0x380a2000, 0x380a4000, 0x380a6000, 0x380a8000, 0x380aa000, +0x380ac000, 0x380ae000, 0x380b0000, 0x380b2000, 0x380b4000, 0x380b6000, +0x380b8000, 0x380ba000, 0x380bc000, 0x380be000, 0x380c0000, 0x380c2000, +0x380c4000, 0x380c6000, 0x380c8000, 0x380ca000, 0x380cc000, 0x380ce000, +0x380d0000, 0x380d2000, 0x380d4000, 0x380d6000, 0x380d8000, 0x380da000, +0x380dc000, 0x380de000, 0x380e0000, 0x380e2000, 0x380e4000, 0x380e6000, +0x380e8000, 0x380ea000, 0x380ec000, 0x380ee000, 0x380f0000, 0x380f2000, +0x380f4000, 0x380f6000, 0x380f8000, 0x380fa000, 0x380fc000, 0x380fe000, +0x38100000, 0x38102000, 0x38104000, 0x38106000, 0x38108000, 0x3810a000, +0x3810c000, 0x3810e000, 0x38110000, 0x38112000, 0x38114000, 0x38116000, +0x38118000, 0x3811a000, 0x3811c000, 0x3811e000, 0x38120000, 0x38122000, +0x38124000, 0x38126000, 0x38128000, 0x3812a000, 0x3812c000, 0x3812e000, +0x38130000, 0x38132000, 0x38134000, 0x38136000, 0x38138000, 0x3813a000, +0x3813c000, 0x3813e000, 0x38140000, 0x38142000, 0x38144000, 0x38146000, +0x38148000, 0x3814a000, 0x3814c000, 0x3814e000, 0x38150000, 0x38152000, +0x38154000, 0x38156000, 0x38158000, 0x3815a000, 0x3815c000, 0x3815e000, +0x38160000, 0x38162000, 0x38164000, 0x38166000, 0x38168000, 0x3816a000, +0x3816c000, 0x3816e000, 0x38170000, 0x38172000, 0x38174000, 0x38176000, +0x38178000, 0x3817a000, 0x3817c000, 0x3817e000, 0x38180000, 0x38182000, +0x38184000, 0x38186000, 0x38188000, 0x3818a000, 0x3818c000, 0x3818e000, +0x38190000, 0x38192000, 0x38194000, 0x38196000, 0x38198000, 0x3819a000, +0x3819c000, 0x3819e000, 0x381a0000, 0x381a2000, 0x381a4000, 0x381a6000, +0x381a8000, 0x381aa000, 0x381ac000, 0x381ae000, 0x381b0000, 0x381b2000, +0x381b4000, 0x381b6000, 0x381b8000, 0x381ba000, 0x381bc000, 0x381be000, +0x381c0000, 0x381c2000, 0x381c4000, 0x381c6000, 0x381c8000, 0x381ca000, +0x381cc000, 0x381ce000, 0x381d0000, 0x381d2000, 0x381d4000, 0x381d6000, +0x381d8000, 0x381da000, 0x381dc000, 0x381de000, 0x381e0000, 0x381e2000, +0x381e4000, 0x381e6000, 0x381e8000, 0x381ea000, 0x381ec000, 0x381ee000, +0x381f0000, 0x381f2000, 0x381f4000, 0x381f6000, 0x381f8000, 0x381fa000, +0x381fc000, 0x381fe000, 0x38200000, 0x38202000, 0x38204000, 0x38206000, +0x38208000, 0x3820a000, 0x3820c000, 0x3820e000, 0x38210000, 0x38212000, +0x38214000, 0x38216000, 0x38218000, 0x3821a000, 0x3821c000, 0x3821e000, +0x38220000, 0x38222000, 0x38224000, 0x38226000, 0x38228000, 0x3822a000, +0x3822c000, 0x3822e000, 0x38230000, 0x38232000, 0x38234000, 0x38236000, +0x38238000, 0x3823a000, 0x3823c000, 0x3823e000, 0x38240000, 0x38242000, +0x38244000, 0x38246000, 0x38248000, 0x3824a000, 0x3824c000, 0x3824e000, +0x38250000, 0x38252000, 0x38254000, 0x38256000, 0x38258000, 0x3825a000, +0x3825c000, 0x3825e000, 0x38260000, 0x38262000, 0x38264000, 0x38266000, +0x38268000, 0x3826a000, 0x3826c000, 0x3826e000, 0x38270000, 0x38272000, +0x38274000, 0x38276000, 0x38278000, 0x3827a000, 0x3827c000, 0x3827e000, +0x38280000, 0x38282000, 0x38284000, 0x38286000, 0x38288000, 0x3828a000, +0x3828c000, 0x3828e000, 0x38290000, 0x38292000, 0x38294000, 0x38296000, +0x38298000, 0x3829a000, 0x3829c000, 0x3829e000, 0x382a0000, 0x382a2000, +0x382a4000, 0x382a6000, 0x382a8000, 0x382aa000, 0x382ac000, 0x382ae000, +0x382b0000, 0x382b2000, 0x382b4000, 0x382b6000, 0x382b8000, 0x382ba000, +0x382bc000, 0x382be000, 0x382c0000, 0x382c2000, 0x382c4000, 0x382c6000, +0x382c8000, 0x382ca000, 0x382cc000, 0x382ce000, 0x382d0000, 0x382d2000, +0x382d4000, 0x382d6000, 0x382d8000, 0x382da000, 0x382dc000, 0x382de000, +0x382e0000, 0x382e2000, 0x382e4000, 0x382e6000, 0x382e8000, 0x382ea000, +0x382ec000, 0x382ee000, 0x382f0000, 0x382f2000, 0x382f4000, 0x382f6000, +0x382f8000, 0x382fa000, 0x382fc000, 0x382fe000, 0x38300000, 0x38302000, +0x38304000, 0x38306000, 0x38308000, 0x3830a000, 0x3830c000, 0x3830e000, +0x38310000, 0x38312000, 0x38314000, 0x38316000, 0x38318000, 0x3831a000, +0x3831c000, 0x3831e000, 0x38320000, 0x38322000, 0x38324000, 0x38326000, +0x38328000, 0x3832a000, 0x3832c000, 0x3832e000, 0x38330000, 0x38332000, +0x38334000, 0x38336000, 0x38338000, 0x3833a000, 0x3833c000, 0x3833e000, +0x38340000, 0x38342000, 0x38344000, 0x38346000, 0x38348000, 0x3834a000, +0x3834c000, 0x3834e000, 0x38350000, 0x38352000, 0x38354000, 0x38356000, +0x38358000, 0x3835a000, 0x3835c000, 0x3835e000, 0x38360000, 0x38362000, +0x38364000, 0x38366000, 0x38368000, 0x3836a000, 0x3836c000, 0x3836e000, +0x38370000, 0x38372000, 0x38374000, 0x38376000, 0x38378000, 0x3837a000, +0x3837c000, 0x3837e000, 0x38380000, 0x38382000, 0x38384000, 0x38386000, +0x38388000, 0x3838a000, 0x3838c000, 0x3838e000, 0x38390000, 0x38392000, +0x38394000, 0x38396000, 0x38398000, 0x3839a000, 0x3839c000, 0x3839e000, +0x383a0000, 0x383a2000, 0x383a4000, 0x383a6000, 0x383a8000, 0x383aa000, +0x383ac000, 0x383ae000, 0x383b0000, 0x383b2000, 0x383b4000, 0x383b6000, +0x383b8000, 0x383ba000, 0x383bc000, 0x383be000, 0x383c0000, 0x383c2000, +0x383c4000, 0x383c6000, 0x383c8000, 0x383ca000, 0x383cc000, 0x383ce000, +0x383d0000, 0x383d2000, 0x383d4000, 0x383d6000, 0x383d8000, 0x383da000, +0x383dc000, 0x383de000, 0x383e0000, 0x383e2000, 0x383e4000, 0x383e6000, +0x383e8000, 0x383ea000, 0x383ec000, 0x383ee000, 0x383f0000, 0x383f2000, +0x383f4000, 0x383f6000, 0x383f8000, 0x383fa000, 0x383fc000, 0x383fe000, +0x38400000, 0x38402000, 0x38404000, 0x38406000, 0x38408000, 0x3840a000, +0x3840c000, 0x3840e000, 0x38410000, 0x38412000, 0x38414000, 0x38416000, +0x38418000, 0x3841a000, 0x3841c000, 0x3841e000, 0x38420000, 0x38422000, +0x38424000, 0x38426000, 0x38428000, 0x3842a000, 0x3842c000, 0x3842e000, +0x38430000, 0x38432000, 0x38434000, 0x38436000, 0x38438000, 0x3843a000, +0x3843c000, 0x3843e000, 0x38440000, 0x38442000, 0x38444000, 0x38446000, +0x38448000, 0x3844a000, 0x3844c000, 0x3844e000, 0x38450000, 0x38452000, +0x38454000, 0x38456000, 0x38458000, 0x3845a000, 0x3845c000, 0x3845e000, +0x38460000, 0x38462000, 0x38464000, 0x38466000, 0x38468000, 0x3846a000, +0x3846c000, 0x3846e000, 0x38470000, 0x38472000, 0x38474000, 0x38476000, +0x38478000, 0x3847a000, 0x3847c000, 0x3847e000, 0x38480000, 0x38482000, +0x38484000, 0x38486000, 0x38488000, 0x3848a000, 0x3848c000, 0x3848e000, +0x38490000, 0x38492000, 0x38494000, 0x38496000, 0x38498000, 0x3849a000, +0x3849c000, 0x3849e000, 0x384a0000, 0x384a2000, 0x384a4000, 0x384a6000, +0x384a8000, 0x384aa000, 0x384ac000, 0x384ae000, 0x384b0000, 0x384b2000, +0x384b4000, 0x384b6000, 0x384b8000, 0x384ba000, 0x384bc000, 0x384be000, +0x384c0000, 0x384c2000, 0x384c4000, 0x384c6000, 0x384c8000, 0x384ca000, +0x384cc000, 0x384ce000, 0x384d0000, 0x384d2000, 0x384d4000, 0x384d6000, +0x384d8000, 0x384da000, 0x384dc000, 0x384de000, 0x384e0000, 0x384e2000, +0x384e4000, 0x384e6000, 0x384e8000, 0x384ea000, 0x384ec000, 0x384ee000, +0x384f0000, 0x384f2000, 0x384f4000, 0x384f6000, 0x384f8000, 0x384fa000, +0x384fc000, 0x384fe000, 0x38500000, 0x38502000, 0x38504000, 0x38506000, +0x38508000, 0x3850a000, 0x3850c000, 0x3850e000, 0x38510000, 0x38512000, +0x38514000, 0x38516000, 0x38518000, 0x3851a000, 0x3851c000, 0x3851e000, +0x38520000, 0x38522000, 0x38524000, 0x38526000, 0x38528000, 0x3852a000, +0x3852c000, 0x3852e000, 0x38530000, 0x38532000, 0x38534000, 0x38536000, +0x38538000, 0x3853a000, 0x3853c000, 0x3853e000, 0x38540000, 0x38542000, +0x38544000, 0x38546000, 0x38548000, 0x3854a000, 0x3854c000, 0x3854e000, +0x38550000, 0x38552000, 0x38554000, 0x38556000, 0x38558000, 0x3855a000, +0x3855c000, 0x3855e000, 0x38560000, 0x38562000, 0x38564000, 0x38566000, +0x38568000, 0x3856a000, 0x3856c000, 0x3856e000, 0x38570000, 0x38572000, +0x38574000, 0x38576000, 0x38578000, 0x3857a000, 0x3857c000, 0x3857e000, +0x38580000, 0x38582000, 0x38584000, 0x38586000, 0x38588000, 0x3858a000, +0x3858c000, 0x3858e000, 0x38590000, 0x38592000, 0x38594000, 0x38596000, +0x38598000, 0x3859a000, 0x3859c000, 0x3859e000, 0x385a0000, 0x385a2000, +0x385a4000, 0x385a6000, 0x385a8000, 0x385aa000, 0x385ac000, 0x385ae000, +0x385b0000, 0x385b2000, 0x385b4000, 0x385b6000, 0x385b8000, 0x385ba000, +0x385bc000, 0x385be000, 0x385c0000, 0x385c2000, 0x385c4000, 0x385c6000, +0x385c8000, 0x385ca000, 0x385cc000, 0x385ce000, 0x385d0000, 0x385d2000, +0x385d4000, 0x385d6000, 0x385d8000, 0x385da000, 0x385dc000, 0x385de000, +0x385e0000, 0x385e2000, 0x385e4000, 0x385e6000, 0x385e8000, 0x385ea000, +0x385ec000, 0x385ee000, 0x385f0000, 0x385f2000, 0x385f4000, 0x385f6000, +0x385f8000, 0x385fa000, 0x385fc000, 0x385fe000, 0x38600000, 0x38602000, +0x38604000, 0x38606000, 0x38608000, 0x3860a000, 0x3860c000, 0x3860e000, +0x38610000, 0x38612000, 0x38614000, 0x38616000, 0x38618000, 0x3861a000, +0x3861c000, 0x3861e000, 0x38620000, 0x38622000, 0x38624000, 0x38626000, +0x38628000, 0x3862a000, 0x3862c000, 0x3862e000, 0x38630000, 0x38632000, +0x38634000, 0x38636000, 0x38638000, 0x3863a000, 0x3863c000, 0x3863e000, +0x38640000, 0x38642000, 0x38644000, 0x38646000, 0x38648000, 0x3864a000, +0x3864c000, 0x3864e000, 0x38650000, 0x38652000, 0x38654000, 0x38656000, +0x38658000, 0x3865a000, 0x3865c000, 0x3865e000, 0x38660000, 0x38662000, +0x38664000, 0x38666000, 0x38668000, 0x3866a000, 0x3866c000, 0x3866e000, +0x38670000, 0x38672000, 0x38674000, 0x38676000, 0x38678000, 0x3867a000, +0x3867c000, 0x3867e000, 0x38680000, 0x38682000, 0x38684000, 0x38686000, +0x38688000, 0x3868a000, 0x3868c000, 0x3868e000, 0x38690000, 0x38692000, +0x38694000, 0x38696000, 0x38698000, 0x3869a000, 0x3869c000, 0x3869e000, +0x386a0000, 0x386a2000, 0x386a4000, 0x386a6000, 0x386a8000, 0x386aa000, +0x386ac000, 0x386ae000, 0x386b0000, 0x386b2000, 0x386b4000, 0x386b6000, +0x386b8000, 0x386ba000, 0x386bc000, 0x386be000, 0x386c0000, 0x386c2000, +0x386c4000, 0x386c6000, 0x386c8000, 0x386ca000, 0x386cc000, 0x386ce000, +0x386d0000, 0x386d2000, 0x386d4000, 0x386d6000, 0x386d8000, 0x386da000, +0x386dc000, 0x386de000, 0x386e0000, 0x386e2000, 0x386e4000, 0x386e6000, +0x386e8000, 0x386ea000, 0x386ec000, 0x386ee000, 0x386f0000, 0x386f2000, +0x386f4000, 0x386f6000, 0x386f8000, 0x386fa000, 0x386fc000, 0x386fe000, +0x38700000, 0x38702000, 0x38704000, 0x38706000, 0x38708000, 0x3870a000, +0x3870c000, 0x3870e000, 0x38710000, 0x38712000, 0x38714000, 0x38716000, +0x38718000, 0x3871a000, 0x3871c000, 0x3871e000, 0x38720000, 0x38722000, +0x38724000, 0x38726000, 0x38728000, 0x3872a000, 0x3872c000, 0x3872e000, +0x38730000, 0x38732000, 0x38734000, 0x38736000, 0x38738000, 0x3873a000, +0x3873c000, 0x3873e000, 0x38740000, 0x38742000, 0x38744000, 0x38746000, +0x38748000, 0x3874a000, 0x3874c000, 0x3874e000, 0x38750000, 0x38752000, +0x38754000, 0x38756000, 0x38758000, 0x3875a000, 0x3875c000, 0x3875e000, +0x38760000, 0x38762000, 0x38764000, 0x38766000, 0x38768000, 0x3876a000, +0x3876c000, 0x3876e000, 0x38770000, 0x38772000, 0x38774000, 0x38776000, +0x38778000, 0x3877a000, 0x3877c000, 0x3877e000, 0x38780000, 0x38782000, +0x38784000, 0x38786000, 0x38788000, 0x3878a000, 0x3878c000, 0x3878e000, +0x38790000, 0x38792000, 0x38794000, 0x38796000, 0x38798000, 0x3879a000, +0x3879c000, 0x3879e000, 0x387a0000, 0x387a2000, 0x387a4000, 0x387a6000, +0x387a8000, 0x387aa000, 0x387ac000, 0x387ae000, 0x387b0000, 0x387b2000, +0x387b4000, 0x387b6000, 0x387b8000, 0x387ba000, 0x387bc000, 0x387be000, +0x387c0000, 0x387c2000, 0x387c4000, 0x387c6000, 0x387c8000, 0x387ca000, +0x387cc000, 0x387ce000, 0x387d0000, 0x387d2000, 0x387d4000, 0x387d6000, +0x387d8000, 0x387da000, 0x387dc000, 0x387de000, 0x387e0000, 0x387e2000, +0x387e4000, 0x387e6000, 0x387e8000, 0x387ea000, 0x387ec000, 0x387ee000, +0x387f0000, 0x387f2000, 0x387f4000, 0x387f6000, 0x387f8000, 0x387fa000, +0x387fc000, 0x387fe000 +}; + +static cmsUInt16Number Offset[64] = { +0x0000, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0000, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, +0x0400, 0x0400, 0x0400, 0x0400 +}; + +static cmsUInt32Number Exponent[64] = { +0x00000000, 0x00800000, 0x01000000, 0x01800000, 0x02000000, 0x02800000, +0x03000000, 0x03800000, 0x04000000, 0x04800000, 0x05000000, 0x05800000, +0x06000000, 0x06800000, 0x07000000, 0x07800000, 0x08000000, 0x08800000, +0x09000000, 0x09800000, 0x0a000000, 0x0a800000, 0x0b000000, 0x0b800000, +0x0c000000, 0x0c800000, 0x0d000000, 0x0d800000, 0x0e000000, 0x0e800000, +0x0f000000, 0x47800000, 0x80000000, 0x80800000, 0x81000000, 0x81800000, +0x82000000, 0x82800000, 0x83000000, 0x83800000, 0x84000000, 0x84800000, +0x85000000, 0x85800000, 0x86000000, 0x86800000, 0x87000000, 0x87800000, +0x88000000, 0x88800000, 0x89000000, 0x89800000, 0x8a000000, 0x8a800000, +0x8b000000, 0x8b800000, 0x8c000000, 0x8c800000, 0x8d000000, 0x8d800000, +0x8e000000, 0x8e800000, 0x8f000000, 0xc7800000 +}; + +static cmsUInt16Number Base[512] = { +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, +0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, +0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x0c00, 0x1000, 0x1400, 0x1800, 0x1c00, +0x2000, 0x2400, 0x2800, 0x2c00, 0x3000, 0x3400, 0x3800, 0x3c00, 0x4000, 0x4400, +0x4800, 0x4c00, 0x5000, 0x5400, 0x5800, 0x5c00, 0x6000, 0x6400, 0x6800, 0x6c00, +0x7000, 0x7400, 0x7800, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, +0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, +0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8001, +0x8002, 0x8004, 0x8008, 0x8010, 0x8020, 0x8040, 0x8080, 0x8100, 0x8200, 0x8400, +0x8800, 0x8c00, 0x9000, 0x9400, 0x9800, 0x9c00, 0xa000, 0xa400, 0xa800, 0xac00, +0xb000, 0xb400, 0xb800, 0xbc00, 0xc000, 0xc400, 0xc800, 0xcc00, 0xd000, 0xd400, +0xd800, 0xdc00, 0xe000, 0xe400, 0xe800, 0xec00, 0xf000, 0xf400, 0xf800, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, +0xfc00, 0xfc00 +}; + +static cmsUInt8Number Shift[512] = { +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, +0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, +0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, +0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0d, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, +0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, +0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, +0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, +0x18, 0x18, 0x18, 0x18, 0x0d +}; + +cmsFloat32Number _cmsHalf2Float(cmsUInt16Number h) +{ + union { + cmsFloat32Number flt; + cmsUInt32Number num; + } out; + + int n = h >> 10; + + out.num = Mantissa[ (h & 0x3ff) + Offset[ n ] ] + Exponent[ n ]; + return out.flt; +} + +cmsUInt16Number _cmsFloat2Half(cmsFloat32Number flt) +{ + union { + cmsFloat32Number flt; + cmsUInt32Number num; + } in; + + cmsUInt32Number n, j; + + in.flt = flt; + n = in.num; + j = (n >> 23) & 0x1ff; + + return (cmsUInt16Number) ((cmsUInt32Number) Base[ j ] + (( n & 0x007fffff) >> Shift[ j ])); +} + +#endif diff --git a/third_party/lcms/src/cmsintrp.c b/third_party/lcms/src/cmsintrp.c new file mode 100644 index 0000000000..14c68563ca --- /dev/null +++ b/third_party/lcms/src/cmsintrp.c @@ -0,0 +1,1506 @@ +//--------------------------------------------------------------------------------- +// +// 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" + +// This module incorporates several interpolation routines, for 1 to 8 channels on input and +// up to 65535 channels on output. The user may change those by using the interpolation plug-in + +// Interpolation routines by default +static cmsInterpFunction DefaultInterpolatorsFactory(cmsUInt32Number nInputChannels, cmsUInt32Number nOutputChannels, cmsUInt32Number dwFlags); + +// This is the default factory +_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(cmsContext ContextID, cmsPluginBase* Data) +{ + cmsPluginInterpolation* Plugin = (cmsPluginInterpolation*) Data; + _cmsInterpPluginChunkType* ptr = (_cmsInterpPluginChunkType*) _cmsContextGetClientChunk(ContextID, InterpPlugin); + + if (Data == NULL) { + + ptr ->Interpolators = NULL; + return TRUE; + } + + // Set replacement functions + ptr ->Interpolators = Plugin ->InterpolatorsFactory; + return TRUE; +} + + +// Set the interpolation method +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) + p ->Interpolation = DefaultInterpolatorsFactory(p ->nInputs, p ->nOutputs, p ->dwFlags); + + // Check for valid interpolator (we just check one member of the union) + if (p ->Interpolation.Lerp16 == NULL) { + return FALSE; + } + + return TRUE; +} + + +// This function precalculates as many parameters as possible to speed up the interpolation. +cmsInterpParams* _cmsComputeInterpParamsEx(cmsContext ContextID, + const cmsUInt32Number nSamples[], + int InputChan, int OutputChan, + const void *Table, + cmsUInt32Number dwFlags) +{ + cmsInterpParams* p; + int i; + + // Check for maximum inputs + if (InputChan > MAX_INPUT_DIMENSIONS) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Too many input channels (%d channels, max=%d)", InputChan, MAX_INPUT_DIMENSIONS); + return NULL; + } + + // Creates an empty object + p = (cmsInterpParams*) _cmsMallocZero(ContextID, sizeof(cmsInterpParams)); + if (p == NULL) return NULL; + + // Keep original parameters + p -> dwFlags = dwFlags; + p -> nInputs = InputChan; + p -> nOutputs = OutputChan; + p ->Table = Table; + p ->ContextID = ContextID; + + // Fill samples per input direction and domain (which is number of nodes minus one) + for (i=0; i < InputChan; i++) { + + p -> nSamples[i] = nSamples[i]; + p -> Domain[i] = nSamples[i] - 1; + } + + // Compute factors to apply to each component to index the grid array + p -> opta[0] = p -> nOutputs; + for (i=1; i < InputChan; i++) + p ->opta[i] = p ->opta[i-1] * nSamples[InputChan-i]; + + + if (!_cmsSetInterpolationRoutine(ContextID, p)) { + cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported interpolation (%d->%d channels)", InputChan, OutputChan); + _cmsFree(ContextID, p); + return NULL; + } + + // All seems ok + return p; +} + + +// This one is a wrapper on the anterior, but assuming all directions have same number of nodes +cmsInterpParams* _cmsComputeInterpParams(cmsContext ContextID, int nSamples, int InputChan, int OutputChan, const void* Table, cmsUInt32Number dwFlags) +{ + int i; + cmsUInt32Number Samples[MAX_INPUT_DIMENSIONS]; + + // Fill the auxiliar array + for (i=0; i < MAX_INPUT_DIMENSIONS; i++) + Samples[i] = nSamples; + + // Call the extended function + return _cmsComputeInterpParamsEx(ContextID, Samples, InputChan, OutputChan, Table, dwFlags); +} + + +// Free all associated memory +void _cmsFreeInterpParams(cmsInterpParams* p) +{ + if (p != NULL) _cmsFree(p ->ContextID, p); +} + + +// Inline fixed point interpolation +cmsINLINE cmsUInt16Number LinearInterp(cmsS15Fixed16Number a, cmsS15Fixed16Number l, cmsS15Fixed16Number h) +{ + cmsUInt32Number dif = (cmsUInt32Number) (h - l) * a + 0x8000; + dif = (dif >> 16) + l; + return (cmsUInt16Number) (dif); +} + + +// Linear interpolation (Fixed-point optimized) +static +void LinLerp1D(register const cmsUInt16Number Value[], + register cmsUInt16Number Output[], + register const cmsInterpParams* p) +{ + cmsUInt16Number y1, y0; + int cell0, rest; + int val3; + const cmsUInt16Number* LutTable = (cmsUInt16Number*) p ->Table; + + // if last value... + if (Value[0] == 0xffff) { + + Output[0] = LutTable[p -> Domain[0]]; + return; + } + + val3 = p -> Domain[0] * Value[0]; + val3 = _cmsToFixedDomain(val3); // To fixed 15.16 + + cell0 = FIXED_TO_INT(val3); // Cell is 16 MSB bits + rest = FIXED_REST_TO_INT(val3); // Rest is 16 LSB bits + + y0 = LutTable[cell0]; + y1 = LutTable[cell0+1]; + + + Output[0] = LinearInterp(rest, y0, y1); +} + +// To prevent out of bounds indexing +cmsINLINE cmsFloat32Number fclamp(cmsFloat32Number v) +{ + return ((v < 0.0f) || isnan(v)) ? 0.0f : (v > 1.0f ? 1.0f : v); +} + +// Floating-point version of 1D interpolation +static +void LinLerp1Dfloat(const cmsFloat32Number Value[], + cmsFloat32Number Output[], + const cmsInterpParams* p) +{ + cmsFloat32Number y1, y0; + cmsFloat32Number val2, rest; + int cell0, cell1; + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p ->Table; + + val2 = fclamp(Value[0]); + + // if last value... + if (val2 == 1.0) { + Output[0] = LutTable[p -> Domain[0]]; + return; + } + + val2 *= p -> Domain[0]; + + cell0 = (int) floor(val2); + cell1 = (int) ceil(val2); + + // Rest is 16 LSB bits + rest = val2 - cell0; + + y0 = LutTable[cell0] ; + y1 = LutTable[cell1] ; + + Output[0] = y0 + (y1 - y0) * rest; +} + + + +// Eval gray LUT having only one input channel +static +void Eval1Input(register const cmsUInt16Number Input[], + register cmsUInt16Number Output[], + register const cmsInterpParams* p16) +{ + cmsS15Fixed16Number fk; + cmsS15Fixed16Number k0, k1, rk, K0, K1; + int v; + cmsUInt32Number OutChan; + const cmsUInt16Number* LutTable = (cmsUInt16Number*) p16 -> Table; + + v = Input[0] * p16 -> Domain[0]; + fk = _cmsToFixedDomain(v); + + k0 = FIXED_TO_INT(fk); + rk = (cmsUInt16Number) FIXED_REST_TO_INT(fk); + + k1 = k0 + (Input[0] != 0xFFFFU ? 1 : 0); + + K0 = p16 -> opta[0] * k0; + K1 = p16 -> opta[0] * k1; + + for (OutChan=0; OutChan < p16->nOutputs; OutChan++) { + + Output[OutChan] = LinearInterp(rk, LutTable[K0+OutChan], LutTable[K1+OutChan]); + } +} + + + +// Eval gray LUT having only one input channel +static +void Eval1InputFloat(const cmsFloat32Number Value[], + cmsFloat32Number Output[], + const cmsInterpParams* p) +{ + cmsFloat32Number y1, y0; + cmsFloat32Number val2, rest; + int cell0, cell1; + cmsUInt32Number OutChan; + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p ->Table; + + val2 = fclamp(Value[0]); + + // if last value... + if (val2 == 1.0) { + Output[0] = LutTable[p -> Domain[0]]; + return; + } + + val2 *= p -> Domain[0]; + + cell0 = (int) floor(val2); + cell1 = (int) ceil(val2); + + // Rest is 16 LSB bits + rest = val2 - cell0; + + cell0 *= p -> opta[0]; + cell1 *= p -> opta[0]; + + for (OutChan=0; OutChan < p->nOutputs; OutChan++) { + + y0 = LutTable[cell0 + OutChan] ; + y1 = LutTable[cell1 + OutChan] ; + + Output[OutChan] = y0 + (y1 - y0) * rest; + } +} + +// Bilinear interpolation (16 bits) - cmsFloat32Number version +static +void BilinearInterpFloat(const cmsFloat32Number Input[], + cmsFloat32Number Output[], + const cmsInterpParams* p) + +{ +# define LERP(a,l,h) (cmsFloat32Number) ((l)+(((h)-(l))*(a))) +# define DENS(i,j) (LutTable[(i)+(j)+OutChan]) + + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p ->Table; + cmsFloat32Number px, py; + int x0, y0, + X0, Y0, X1, Y1; + int TotalOut, OutChan; + cmsFloat32Number fx, fy, + d00, d01, d10, d11, + dx0, dx1, + dxy; + + TotalOut = p -> nOutputs; + px = fclamp(Input[0]) * p->Domain[0]; + py = fclamp(Input[1]) * p->Domain[1]; + + x0 = (int) _cmsQuickFloor(px); fx = px - (cmsFloat32Number) x0; + y0 = (int) _cmsQuickFloor(py); fy = py - (cmsFloat32Number) y0; + + X0 = p -> opta[1] * x0; + X1 = X0 + (Input[0] >= 1.0 ? 0 : p->opta[1]); + + Y0 = p -> opta[0] * y0; + Y1 = Y0 + (Input[1] >= 1.0 ? 0 : p->opta[0]); + + for (OutChan = 0; OutChan < TotalOut; OutChan++) { + + d00 = DENS(X0, Y0); + d01 = DENS(X0, Y1); + d10 = DENS(X1, Y0); + d11 = DENS(X1, Y1); + + dx0 = LERP(fx, d00, d10); + dx1 = LERP(fx, d01, d11); + + dxy = LERP(fy, dx0, dx1); + + Output[OutChan] = dxy; + } + + +# undef LERP +# undef DENS +} + +// Bilinear interpolation (16 bits) - optimized version +static +void BilinearInterp16(register const cmsUInt16Number Input[], + register cmsUInt16Number Output[], + register const cmsInterpParams* p) + +{ +#define DENS(i,j) (LutTable[(i)+(j)+OutChan]) +#define LERP(a,l,h) (cmsUInt16Number) (l + ROUND_FIXED_TO_INT(((h-l)*a))) + + const cmsUInt16Number* LutTable = (cmsUInt16Number*) p ->Table; + int OutChan, TotalOut; + cmsS15Fixed16Number fx, fy; + register int rx, ry; + int x0, y0; + register int X0, X1, Y0, Y1; + int d00, d01, d10, d11, + dx0, dx1, + dxy; + + TotalOut = p -> nOutputs; + + fx = _cmsToFixedDomain((int) Input[0] * p -> Domain[0]); + x0 = FIXED_TO_INT(fx); + rx = FIXED_REST_TO_INT(fx); // Rest in 0..1.0 domain + + + fy = _cmsToFixedDomain((int) Input[1] * p -> Domain[1]); + y0 = FIXED_TO_INT(fy); + ry = FIXED_REST_TO_INT(fy); + + + X0 = p -> opta[1] * x0; + X1 = X0 + (Input[0] == 0xFFFFU ? 0 : p->opta[1]); + + Y0 = p -> opta[0] * y0; + Y1 = Y0 + (Input[1] == 0xFFFFU ? 0 : p->opta[0]); + + for (OutChan = 0; OutChan < TotalOut; OutChan++) { + + d00 = DENS(X0, Y0); + d01 = DENS(X0, Y1); + d10 = DENS(X1, Y0); + d11 = DENS(X1, Y1); + + dx0 = LERP(rx, d00, d10); + dx1 = LERP(rx, d01, d11); + + dxy = LERP(ry, dx0, dx1); + + Output[OutChan] = (cmsUInt16Number) dxy; + } + + +# undef LERP +# undef DENS +} + + +// Trilinear interpolation (16 bits) - cmsFloat32Number version +static +void TrilinearInterpFloat(const cmsFloat32Number Input[], + cmsFloat32Number Output[], + const cmsInterpParams* p) + +{ +# define LERP(a,l,h) (cmsFloat32Number) ((l)+(((h)-(l))*(a))) +# define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan]) + + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p ->Table; + cmsFloat32Number px, py, pz; + int x0, y0, z0, + X0, Y0, Z0, X1, Y1, Z1; + int TotalOut, OutChan; + cmsFloat32Number fx, fy, fz, + d000, d001, d010, d011, + d100, d101, d110, d111, + dx00, dx01, dx10, dx11, + dxy0, dxy1, dxyz; + + TotalOut = p -> nOutputs; + + // We need some clipping here + px = fclamp(Input[0]) * p->Domain[0]; + py = fclamp(Input[1]) * p->Domain[1]; + pz = fclamp(Input[2]) * p->Domain[2]; + + x0 = (int) _cmsQuickFloor(px); fx = px - (cmsFloat32Number) x0; + y0 = (int) _cmsQuickFloor(py); fy = py - (cmsFloat32Number) y0; + z0 = (int) _cmsQuickFloor(pz); fz = pz - (cmsFloat32Number) z0; + + X0 = p -> opta[2] * x0; + X1 = X0 + (Input[0] >= 1.0 ? 0 : p->opta[2]); + + Y0 = p -> opta[1] * y0; + Y1 = Y0 + (Input[1] >= 1.0 ? 0 : p->opta[1]); + + Z0 = p -> opta[0] * z0; + Z1 = Z0 + (Input[2] >= 1.0 ? 0 : p->opta[0]); + + for (OutChan = 0; OutChan < TotalOut; OutChan++) { + + d000 = DENS(X0, Y0, Z0); + d001 = DENS(X0, Y0, Z1); + d010 = DENS(X0, Y1, Z0); + d011 = DENS(X0, Y1, Z1); + + d100 = DENS(X1, Y0, Z0); + d101 = DENS(X1, Y0, Z1); + d110 = DENS(X1, Y1, Z0); + d111 = DENS(X1, Y1, Z1); + + + dx00 = LERP(fx, d000, d100); + dx01 = LERP(fx, d001, d101); + dx10 = LERP(fx, d010, d110); + dx11 = LERP(fx, d011, d111); + + dxy0 = LERP(fy, dx00, dx10); + dxy1 = LERP(fy, dx01, dx11); + + dxyz = LERP(fz, dxy0, dxy1); + + Output[OutChan] = dxyz; + } + + +# undef LERP +# undef DENS +} + +// Trilinear interpolation (16 bits) - optimized version +static +void TrilinearInterp16(register const cmsUInt16Number Input[], + register cmsUInt16Number Output[], + register const cmsInterpParams* p) + +{ +#define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan]) +#define LERP(a,l,h) (cmsUInt16Number) (l + ROUND_FIXED_TO_INT(((h-l)*a))) + + const cmsUInt16Number* LutTable = (cmsUInt16Number*) p ->Table; + int OutChan, TotalOut; + cmsS15Fixed16Number fx, fy, fz; + register int rx, ry, rz; + int x0, y0, z0; + register int X0, X1, Y0, Y1, Z0, Z1; + int d000, d001, d010, d011, + d100, d101, d110, d111, + dx00, dx01, dx10, dx11, + dxy0, dxy1, dxyz; + + TotalOut = p -> nOutputs; + + fx = _cmsToFixedDomain((int) Input[0] * p -> Domain[0]); + x0 = FIXED_TO_INT(fx); + rx = FIXED_REST_TO_INT(fx); // Rest in 0..1.0 domain + + + fy = _cmsToFixedDomain((int) Input[1] * p -> Domain[1]); + y0 = FIXED_TO_INT(fy); + ry = FIXED_REST_TO_INT(fy); + + fz = _cmsToFixedDomain((int) Input[2] * p -> Domain[2]); + z0 = FIXED_TO_INT(fz); + rz = FIXED_REST_TO_INT(fz); + + + X0 = p -> opta[2] * x0; + X1 = X0 + (Input[0] == 0xFFFFU ? 0 : p->opta[2]); + + Y0 = p -> opta[1] * y0; + Y1 = Y0 + (Input[1] == 0xFFFFU ? 0 : p->opta[1]); + + Z0 = p -> opta[0] * z0; + Z1 = Z0 + (Input[2] == 0xFFFFU ? 0 : p->opta[0]); + + for (OutChan = 0; OutChan < TotalOut; OutChan++) { + + d000 = DENS(X0, Y0, Z0); + d001 = DENS(X0, Y0, Z1); + d010 = DENS(X0, Y1, Z0); + d011 = DENS(X0, Y1, Z1); + + d100 = DENS(X1, Y0, Z0); + d101 = DENS(X1, Y0, Z1); + d110 = DENS(X1, Y1, Z0); + d111 = DENS(X1, Y1, Z1); + + + dx00 = LERP(rx, d000, d100); + dx01 = LERP(rx, d001, d101); + dx10 = LERP(rx, d010, d110); + dx11 = LERP(rx, d011, d111); + + dxy0 = LERP(ry, dx00, dx10); + dxy1 = LERP(ry, dx01, dx11); + + dxyz = LERP(rz, dxy0, dxy1); + + Output[OutChan] = (cmsUInt16Number) dxyz; + } + + +# undef LERP +# undef DENS +} + + +// Tetrahedral interpolation, using Sakamoto algorithm. +#define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan]) +static +void TetrahedralInterpFloat(const cmsFloat32Number Input[], + cmsFloat32Number Output[], + const cmsInterpParams* p) +{ + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table; + cmsFloat32Number px, py, pz; + int x0, y0, z0, + X0, Y0, Z0, X1, Y1, Z1; + cmsFloat32Number rx, ry, rz; + cmsFloat32Number c0, c1=0, c2=0, c3=0; + int OutChan, TotalOut; + + TotalOut = p -> nOutputs; + + // We need some clipping here + px = fclamp(Input[0]) * p->Domain[0]; + py = fclamp(Input[1]) * p->Domain[1]; + pz = fclamp(Input[2]) * p->Domain[2]; + + x0 = (int) _cmsQuickFloor(px); rx = (px - (cmsFloat32Number) x0); + y0 = (int) _cmsQuickFloor(py); ry = (py - (cmsFloat32Number) y0); + z0 = (int) _cmsQuickFloor(pz); rz = (pz - (cmsFloat32Number) z0); + + + X0 = p -> opta[2] * x0; + X1 = X0 + (Input[0] >= 1.0 ? 0 : p->opta[2]); + + Y0 = p -> opta[1] * y0; + Y1 = Y0 + (Input[1] >= 1.0 ? 0 : p->opta[1]); + + Z0 = p -> opta[0] * z0; + Z1 = Z0 + (Input[2] >= 1.0 ? 0 : p->opta[0]); + + for (OutChan=0; OutChan < TotalOut; OutChan++) { + + // These are the 6 Tetrahedral + + c0 = DENS(X0, Y0, Z0); + + if (rx >= ry && ry >= rz) { + + c1 = DENS(X1, Y0, Z0) - c0; + c2 = DENS(X1, Y1, Z0) - DENS(X1, Y0, Z0); + c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + + } + else + if (rx >= rz && rz >= ry) { + + c1 = DENS(X1, Y0, Z0) - c0; + c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); + c3 = DENS(X1, Y0, Z1) - DENS(X1, Y0, Z0); + + } + else + if (rz >= rx && rx >= ry) { + + c1 = DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1); + c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); + c3 = DENS(X0, Y0, Z1) - c0; + + } + else + if (ry >= rx && rx >= rz) { + + c1 = DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0); + c2 = DENS(X0, Y1, Z0) - c0; + c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + + } + else + if (ry >= rz && rz >= rx) { + + c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); + c2 = DENS(X0, Y1, Z0) - c0; + c3 = DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0); + + } + else + if (rz >= ry && ry >= rx) { + + c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); + c2 = DENS(X0, Y1, Z1) - DENS(X0, Y0, Z1); + c3 = DENS(X0, Y0, Z1) - c0; + + } + else { + c1 = c2 = c3 = 0; + } + + Output[OutChan] = c0 + c1 * rx + c2 * ry + c3 * rz; + } + +} + +#undef DENS + + + + +static +void TetrahedralInterp16(register const cmsUInt16Number Input[], + register cmsUInt16Number Output[], + register const cmsInterpParams* p) +{ + const cmsUInt16Number* LutTable = (cmsUInt16Number*) p -> Table; + cmsS15Fixed16Number fx, fy, fz; + cmsS15Fixed16Number rx, ry, rz; + int x0, y0, z0; + cmsS15Fixed16Number c0, c1, c2, c3, Rest; + cmsS15Fixed16Number X0, X1, Y0, Y1, Z0, Z1; + cmsUInt32Number TotalOut = p -> nOutputs; + + fx = _cmsToFixedDomain((int) Input[0] * p -> Domain[0]); + fy = _cmsToFixedDomain((int) Input[1] * p -> Domain[1]); + fz = _cmsToFixedDomain((int) Input[2] * p -> Domain[2]); + + x0 = FIXED_TO_INT(fx); + y0 = FIXED_TO_INT(fy); + z0 = FIXED_TO_INT(fz); + + rx = FIXED_REST_TO_INT(fx); + ry = FIXED_REST_TO_INT(fy); + rz = FIXED_REST_TO_INT(fz); + + X0 = p -> opta[2] * x0; + X1 = (Input[0] == 0xFFFFU ? 0 : p->opta[2]); + + Y0 = p -> opta[1] * y0; + Y1 = (Input[1] == 0xFFFFU ? 0 : p->opta[1]); + + Z0 = p -> opta[0] * z0; + Z1 = (Input[2] == 0xFFFFU ? 0 : p->opta[0]); + + LutTable = &LutTable[X0+Y0+Z0]; + + // Output should be computed as x = ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest)) + // which expands as: x = (Rest + ((Rest+0x7fff)/0xFFFF) + 0x8000)>>16 + // This can be replaced by: t = Rest+0x8001, x = (t + (t>>16))>>16 + // at the cost of being off by one at 7fff and 17ffe. + + if (rx >= ry) { + if (ry >= rz) { + Y1 += X1; + Z1 += Y1; + for (; TotalOut; TotalOut--) { + c1 = LutTable[X1]; + c2 = LutTable[Y1]; + c3 = LutTable[Z1]; + c0 = *LutTable++; + c3 -= c2; + c2 -= c1; + c1 -= c0; + Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; + *Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16); + } + } else if (rz >= rx) { + X1 += Z1; + Y1 += X1; + for (; TotalOut; TotalOut--) { + c1 = LutTable[X1]; + c2 = LutTable[Y1]; + c3 = LutTable[Z1]; + c0 = *LutTable++; + c2 -= c1; + c1 -= c3; + c3 -= c0; + Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; + *Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16); + } + } else { + Z1 += X1; + Y1 += Z1; + for (; TotalOut; TotalOut--) { + c1 = LutTable[X1]; + c2 = LutTable[Y1]; + c3 = LutTable[Z1]; + c0 = *LutTable++; + c2 -= c3; + c3 -= c1; + c1 -= c0; + Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; + *Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16); + } + } + } else { + if (rx >= rz) { + X1 += Y1; + Z1 += X1; + for (; TotalOut; TotalOut--) { + c1 = LutTable[X1]; + c2 = LutTable[Y1]; + c3 = LutTable[Z1]; + c0 = *LutTable++; + c3 -= c1; + c1 -= c2; + c2 -= c0; + Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; + *Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16); + } + } else if (ry >= rz) { + Z1 += Y1; + X1 += Z1; + for (; TotalOut; TotalOut--) { + c1 = LutTable[X1]; + c2 = LutTable[Y1]; + c3 = LutTable[Z1]; + c0 = *LutTable++; + c1 -= c3; + c3 -= c2; + c2 -= c0; + Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; + *Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16); + } + } else { + Y1 += Z1; + X1 += Y1; + for (; TotalOut; TotalOut--) { + c1 = LutTable[X1]; + c2 = LutTable[Y1]; + c3 = LutTable[Z1]; + c0 = *LutTable++; + c1 -= c2; + c2 -= c3; + c3 -= c0; + Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; + *Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16); + } + } + } +} + + +#define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan]) +static +void Eval4Inputs(register const cmsUInt16Number Input[], + register cmsUInt16Number Output[], + register const cmsInterpParams* p16) +{ + const cmsUInt16Number* LutTable; + cmsS15Fixed16Number fk; + cmsS15Fixed16Number k0, rk; + int K0, K1; + cmsS15Fixed16Number fx, fy, fz; + cmsS15Fixed16Number rx, ry, rz; + int x0, y0, z0; + cmsS15Fixed16Number X0, X1, Y0, Y1, Z0, Z1; + cmsUInt32Number i; + cmsS15Fixed16Number c0, c1, c2, c3, Rest; + cmsUInt32Number OutChan; + cmsUInt16Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; + + + fk = _cmsToFixedDomain((int) Input[0] * p16 -> Domain[0]); + fx = _cmsToFixedDomain((int) Input[1] * p16 -> Domain[1]); + fy = _cmsToFixedDomain((int) Input[2] * p16 -> Domain[2]); + fz = _cmsToFixedDomain((int) Input[3] * p16 -> Domain[3]); + + k0 = FIXED_TO_INT(fk); + x0 = FIXED_TO_INT(fx); + y0 = FIXED_TO_INT(fy); + z0 = FIXED_TO_INT(fz); + + rk = FIXED_REST_TO_INT(fk); + rx = FIXED_REST_TO_INT(fx); + ry = FIXED_REST_TO_INT(fy); + rz = FIXED_REST_TO_INT(fz); + + K0 = p16 -> opta[3] * k0; + K1 = K0 + (Input[0] == 0xFFFFU ? 0 : p16->opta[3]); + + X0 = p16 -> opta[2] * x0; + X1 = X0 + (Input[1] == 0xFFFFU ? 0 : p16->opta[2]); + + Y0 = p16 -> opta[1] * y0; + Y1 = Y0 + (Input[2] == 0xFFFFU ? 0 : p16->opta[1]); + + Z0 = p16 -> opta[0] * z0; + Z1 = Z0 + (Input[3] == 0xFFFFU ? 0 : p16->opta[0]); + + LutTable = (cmsUInt16Number*) p16 -> Table; + LutTable += K0; + + for (OutChan=0; OutChan < p16 -> nOutputs; OutChan++) { + + c0 = DENS(X0, Y0, Z0); + + if (rx >= ry && ry >= rz) { + + c1 = DENS(X1, Y0, Z0) - c0; + c2 = DENS(X1, Y1, Z0) - DENS(X1, Y0, Z0); + c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + + } + else + if (rx >= rz && rz >= ry) { + + c1 = DENS(X1, Y0, Z0) - c0; + c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); + c3 = DENS(X1, Y0, Z1) - DENS(X1, Y0, Z0); + + } + else + if (rz >= rx && rx >= ry) { + + c1 = DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1); + c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); + c3 = DENS(X0, Y0, Z1) - c0; + + } + else + if (ry >= rx && rx >= rz) { + + c1 = DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0); + c2 = DENS(X0, Y1, Z0) - c0; + c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + + } + else + if (ry >= rz && rz >= rx) { + + c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); + c2 = DENS(X0, Y1, Z0) - c0; + c3 = DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0); + + } + else + if (rz >= ry && ry >= rx) { + + c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); + c2 = DENS(X0, Y1, Z1) - DENS(X0, Y0, Z1); + c3 = DENS(X0, Y0, Z1) - c0; + + } + else { + c1 = c2 = c3 = 0; + } + + Rest = c1 * rx + c2 * ry + c3 * rz; + + Tmp1[OutChan] = (cmsUInt16Number) c0 + ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest)); + } + + + LutTable = (cmsUInt16Number*) p16 -> Table; + LutTable += K1; + + for (OutChan=0; OutChan < p16 -> nOutputs; OutChan++) { + + c0 = DENS(X0, Y0, Z0); + + if (rx >= ry && ry >= rz) { + + c1 = DENS(X1, Y0, Z0) - c0; + c2 = DENS(X1, Y1, Z0) - DENS(X1, Y0, Z0); + c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + + } + else + if (rx >= rz && rz >= ry) { + + c1 = DENS(X1, Y0, Z0) - c0; + c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); + c3 = DENS(X1, Y0, Z1) - DENS(X1, Y0, Z0); + + } + else + if (rz >= rx && rx >= ry) { + + c1 = DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1); + c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); + c3 = DENS(X0, Y0, Z1) - c0; + + } + else + if (ry >= rx && rx >= rz) { + + c1 = DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0); + c2 = DENS(X0, Y1, Z0) - c0; + c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + + } + else + if (ry >= rz && rz >= rx) { + + c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); + c2 = DENS(X0, Y1, Z0) - c0; + c3 = DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0); + + } + else + if (rz >= ry && ry >= rx) { + + c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); + c2 = DENS(X0, Y1, Z1) - DENS(X0, Y0, Z1); + c3 = DENS(X0, Y0, Z1) - c0; + + } + else { + c1 = c2 = c3 = 0; + } + + Rest = c1 * rx + c2 * ry + c3 * rz; + + Tmp2[OutChan] = (cmsUInt16Number) c0 + ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest)); + } + + + + for (i=0; i < p16 -> nOutputs; i++) { + Output[i] = LinearInterp(rk, Tmp1[i], Tmp2[i]); + } +} +#undef DENS + + +// For more that 3 inputs (i.e., CMYK) +// evaluate two 3-dimensional interpolations and then linearly interpolate between them. + + +static +void Eval4InputsFloat(const cmsFloat32Number Input[], + cmsFloat32Number Output[], + const cmsInterpParams* p) +{ + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table; + cmsFloat32Number rest; + cmsFloat32Number pk; + int k0, K0, K1; + const cmsFloat32Number* T; + cmsUInt32Number i; + cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; + cmsInterpParams p1; + + pk = fclamp(Input[0]) * p->Domain[0]; + k0 = _cmsQuickFloor(pk); + rest = pk - (cmsFloat32Number) k0; + + K0 = p -> opta[3] * k0; + K1 = K0 + (Input[0] >= 1.0 ? 0 : p->opta[3]); + + p1 = *p; + memmove(&p1.Domain[0], &p ->Domain[1], 3*sizeof(cmsUInt32Number)); + + T = LutTable + K0; + p1.Table = T; + + TetrahedralInterpFloat(Input + 1, Tmp1, &p1); + + T = LutTable + K1; + p1.Table = T; + TetrahedralInterpFloat(Input + 1, Tmp2, &p1); + + for (i=0; i < p -> nOutputs; i++) + { + cmsFloat32Number y0 = Tmp1[i]; + cmsFloat32Number y1 = Tmp2[i]; + + Output[i] = y0 + (y1 - y0) * rest; + } +} + + +static +void Eval5Inputs(register const cmsUInt16Number Input[], + register cmsUInt16Number Output[], + + register const cmsInterpParams* p16) +{ + const cmsUInt16Number* LutTable = (cmsUInt16Number*) p16 -> Table; + cmsS15Fixed16Number fk; + cmsS15Fixed16Number k0, rk; + int K0, K1; + const cmsUInt16Number* T; + cmsUInt32Number i; + cmsUInt16Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; + cmsInterpParams p1; + + + fk = _cmsToFixedDomain((cmsS15Fixed16Number) Input[0] * p16 -> Domain[0]); + k0 = FIXED_TO_INT(fk); + rk = FIXED_REST_TO_INT(fk); + + K0 = p16 -> opta[4] * k0; + K1 = p16 -> opta[4] * (k0 + (Input[0] != 0xFFFFU ? 1 : 0)); + + p1 = *p16; + memmove(&p1.Domain[0], &p16 ->Domain[1], 4*sizeof(cmsUInt32Number)); + + T = LutTable + K0; + p1.Table = T; + + Eval4Inputs(Input + 1, Tmp1, &p1); + + T = LutTable + K1; + p1.Table = T; + + Eval4Inputs(Input + 1, Tmp2, &p1); + + for (i=0; i < p16 -> nOutputs; i++) { + + Output[i] = LinearInterp(rk, Tmp1[i], Tmp2[i]); + } + +} + + +static +void Eval5InputsFloat(const cmsFloat32Number Input[], + cmsFloat32Number Output[], + const cmsInterpParams* p) +{ + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table; + cmsFloat32Number rest; + cmsFloat32Number pk; + int k0, K0, K1; + const cmsFloat32Number* T; + cmsUInt32Number i; + cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; + cmsInterpParams p1; + + pk = fclamp(Input[0]) * p->Domain[0]; + k0 = _cmsQuickFloor(pk); + rest = pk - (cmsFloat32Number) k0; + + K0 = p -> opta[4] * k0; + K1 = K0 + (Input[0] >= 1.0 ? 0 : p->opta[4]); + + p1 = *p; + memmove(&p1.Domain[0], &p ->Domain[1], 4*sizeof(cmsUInt32Number)); + + T = LutTable + K0; + p1.Table = T; + + Eval4InputsFloat(Input + 1, Tmp1, &p1); + + T = LutTable + K1; + p1.Table = T; + + Eval4InputsFloat(Input + 1, Tmp2, &p1); + + for (i=0; i < p -> nOutputs; i++) { + + cmsFloat32Number y0 = Tmp1[i]; + cmsFloat32Number y1 = Tmp2[i]; + + Output[i] = y0 + (y1 - y0) * rest; + } +} + + + +static +void Eval6Inputs(register const cmsUInt16Number Input[], + register cmsUInt16Number Output[], + register const cmsInterpParams* p16) +{ + const cmsUInt16Number* LutTable = (cmsUInt16Number*) p16 -> Table; + cmsS15Fixed16Number fk; + cmsS15Fixed16Number k0, rk; + int K0, K1; + const cmsUInt16Number* T; + cmsUInt32Number i; + cmsUInt16Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; + cmsInterpParams p1; + + fk = _cmsToFixedDomain((cmsS15Fixed16Number) Input[0] * p16 -> Domain[0]); + k0 = FIXED_TO_INT(fk); + rk = FIXED_REST_TO_INT(fk); + + K0 = p16 -> opta[5] * k0; + K1 = p16 -> opta[5] * (k0 + (Input[0] != 0xFFFFU ? 1 : 0)); + + p1 = *p16; + memmove(&p1.Domain[0], &p16 ->Domain[1], 5*sizeof(cmsUInt32Number)); + + T = LutTable + K0; + p1.Table = T; + + Eval5Inputs(Input + 1, Tmp1, &p1); + + T = LutTable + K1; + p1.Table = T; + + Eval5Inputs(Input + 1, Tmp2, &p1); + + for (i=0; i < p16 -> nOutputs; i++) { + + Output[i] = LinearInterp(rk, Tmp1[i], Tmp2[i]); + } + +} + + +static +void Eval6InputsFloat(const cmsFloat32Number Input[], + cmsFloat32Number Output[], + const cmsInterpParams* p) +{ + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table; + cmsFloat32Number rest; + cmsFloat32Number pk; + int k0, K0, K1; + const cmsFloat32Number* T; + cmsUInt32Number i; + cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; + cmsInterpParams p1; + + pk = fclamp(Input[0]) * p->Domain[0]; + k0 = _cmsQuickFloor(pk); + rest = pk - (cmsFloat32Number) k0; + + K0 = p -> opta[5] * k0; + K1 = K0 + (Input[0] >= 1.0 ? 0 : p->opta[5]); + + p1 = *p; + memmove(&p1.Domain[0], &p ->Domain[1], 5*sizeof(cmsUInt32Number)); + + T = LutTable + K0; + p1.Table = T; + + Eval5InputsFloat(Input + 1, Tmp1, &p1); + + T = LutTable + K1; + p1.Table = T; + + Eval5InputsFloat(Input + 1, Tmp2, &p1); + + for (i=0; i < p -> nOutputs; i++) { + + cmsFloat32Number y0 = Tmp1[i]; + cmsFloat32Number y1 = Tmp2[i]; + + Output[i] = y0 + (y1 - y0) * rest; + } +} + + +static +void Eval7Inputs(register const cmsUInt16Number Input[], + register cmsUInt16Number Output[], + register const cmsInterpParams* p16) +{ + const cmsUInt16Number* LutTable = (cmsUInt16Number*) p16 -> Table; + cmsS15Fixed16Number fk; + cmsS15Fixed16Number k0, rk; + int K0, K1; + const cmsUInt16Number* T; + cmsUInt32Number i; + cmsUInt16Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; + cmsInterpParams p1; + + + fk = _cmsToFixedDomain((cmsS15Fixed16Number) Input[0] * p16 -> Domain[0]); + k0 = FIXED_TO_INT(fk); + rk = FIXED_REST_TO_INT(fk); + + K0 = p16 -> opta[6] * k0; + K1 = p16 -> opta[6] * (k0 + (Input[0] != 0xFFFFU ? 1 : 0)); + + p1 = *p16; + memmove(&p1.Domain[0], &p16 ->Domain[1], 6*sizeof(cmsUInt32Number)); + + T = LutTable + K0; + p1.Table = T; + + Eval6Inputs(Input + 1, Tmp1, &p1); + + T = LutTable + K1; + p1.Table = T; + + Eval6Inputs(Input + 1, Tmp2, &p1); + + for (i=0; i < p16 -> nOutputs; i++) { + Output[i] = LinearInterp(rk, Tmp1[i], Tmp2[i]); + } +} + + +static +void Eval7InputsFloat(const cmsFloat32Number Input[], + cmsFloat32Number Output[], + const cmsInterpParams* p) +{ + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table; + cmsFloat32Number rest; + cmsFloat32Number pk; + int k0, K0, K1; + const cmsFloat32Number* T; + cmsUInt32Number i; + cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; + cmsInterpParams p1; + + pk = fclamp(Input[0]) * p->Domain[0]; + k0 = _cmsQuickFloor(pk); + rest = pk - (cmsFloat32Number) k0; + + K0 = p -> opta[6] * k0; + K1 = K0 + (Input[0] >= 1.0 ? 0 : p->opta[6]); + + p1 = *p; + memmove(&p1.Domain[0], &p ->Domain[1], 6*sizeof(cmsUInt32Number)); + + T = LutTable + K0; + p1.Table = T; + + Eval6InputsFloat(Input + 1, Tmp1, &p1); + + T = LutTable + K1; + p1.Table = T; + + Eval6InputsFloat(Input + 1, Tmp2, &p1); + + + for (i=0; i < p -> nOutputs; i++) { + + cmsFloat32Number y0 = Tmp1[i]; + cmsFloat32Number y1 = Tmp2[i]; + + Output[i] = y0 + (y1 - y0) * rest; + + } +} + +static +void Eval8Inputs(register const cmsUInt16Number Input[], + register cmsUInt16Number Output[], + register const cmsInterpParams* p16) +{ + const cmsUInt16Number* LutTable = (cmsUInt16Number*) p16 -> Table; + cmsS15Fixed16Number fk; + cmsS15Fixed16Number k0, rk; + int K0, K1; + const cmsUInt16Number* T; + cmsUInt32Number i; + cmsUInt16Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; + cmsInterpParams p1; + + fk = _cmsToFixedDomain((cmsS15Fixed16Number) Input[0] * p16 -> Domain[0]); + k0 = FIXED_TO_INT(fk); + rk = FIXED_REST_TO_INT(fk); + + K0 = p16 -> opta[7] * k0; + K1 = p16 -> opta[7] * (k0 + (Input[0] != 0xFFFFU ? 1 : 0)); + + p1 = *p16; + memmove(&p1.Domain[0], &p16 ->Domain[1], 7*sizeof(cmsUInt32Number)); + + T = LutTable + K0; + p1.Table = T; + + Eval7Inputs(Input + 1, Tmp1, &p1); + + T = LutTable + K1; + p1.Table = T; + Eval7Inputs(Input + 1, Tmp2, &p1); + + for (i=0; i < p16 -> nOutputs; i++) { + Output[i] = LinearInterp(rk, Tmp1[i], Tmp2[i]); + } +} + + + +static +void Eval8InputsFloat(const cmsFloat32Number Input[], + cmsFloat32Number Output[], + const cmsInterpParams* p) +{ + const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table; + cmsFloat32Number rest; + cmsFloat32Number pk; + int k0, K0, K1; + const cmsFloat32Number* T; + cmsUInt32Number i; + cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; + cmsInterpParams p1; + + pk = fclamp(Input[0]) * p->Domain[0]; + k0 = _cmsQuickFloor(pk); + rest = pk - (cmsFloat32Number) k0; + + K0 = p -> opta[7] * k0; + K1 = K0 + (Input[0] >= 1.0 ? 0 : p->opta[7]); + + p1 = *p; + memmove(&p1.Domain[0], &p ->Domain[1], 7*sizeof(cmsUInt32Number)); + + T = LutTable + K0; + p1.Table = T; + + Eval7InputsFloat(Input + 1, Tmp1, &p1); + + T = LutTable + K1; + p1.Table = T; + + Eval7InputsFloat(Input + 1, Tmp2, &p1); + + + for (i=0; i < p -> nOutputs; i++) { + + cmsFloat32Number y0 = Tmp1[i]; + cmsFloat32Number y1 = Tmp2[i]; + + Output[i] = y0 + (y1 - y0) * rest; + } +} + +// The default factory +static +cmsInterpFunction DefaultInterpolatorsFactory(cmsUInt32Number nInputChannels, cmsUInt32Number nOutputChannels, cmsUInt32Number dwFlags) +{ + + cmsInterpFunction Interpolation; + cmsBool IsFloat = (dwFlags & CMS_LERP_FLAGS_FLOAT); + cmsBool IsTrilinear = (dwFlags & CMS_LERP_FLAGS_TRILINEAR); + + memset(&Interpolation, 0, sizeof(Interpolation)); + + // Safety check + if (nInputChannels >= 4 && nOutputChannels >= MAX_STAGE_CHANNELS) + return Interpolation; + + switch (nInputChannels) { + + case 1: // Gray LUT / linear + + if (nOutputChannels == 1) { + + if (IsFloat) + Interpolation.LerpFloat = LinLerp1Dfloat; + else + Interpolation.Lerp16 = LinLerp1D; + + } + else { + + if (IsFloat) + Interpolation.LerpFloat = Eval1InputFloat; + else + Interpolation.Lerp16 = Eval1Input; + } + break; + + case 2: // Duotone + if (IsFloat) + Interpolation.LerpFloat = BilinearInterpFloat; + else + Interpolation.Lerp16 = BilinearInterp16; + break; + + case 3: // RGB et al + + if (IsTrilinear) { + + if (IsFloat) + Interpolation.LerpFloat = TrilinearInterpFloat; + else + Interpolation.Lerp16 = TrilinearInterp16; + } + else { + + if (IsFloat) + Interpolation.LerpFloat = TetrahedralInterpFloat; + else { + + Interpolation.Lerp16 = TetrahedralInterp16; + } + } + break; + + case 4: // CMYK lut + + if (IsFloat) + Interpolation.LerpFloat = Eval4InputsFloat; + else + Interpolation.Lerp16 = Eval4Inputs; + break; + + case 5: // 5 Inks + if (IsFloat) + Interpolation.LerpFloat = Eval5InputsFloat; + else + Interpolation.Lerp16 = Eval5Inputs; + break; + + case 6: // 6 Inks + if (IsFloat) + Interpolation.LerpFloat = Eval6InputsFloat; + else + Interpolation.Lerp16 = Eval6Inputs; + break; + + case 7: // 7 inks + if (IsFloat) + Interpolation.LerpFloat = Eval7InputsFloat; + else + Interpolation.Lerp16 = Eval7Inputs; + break; + + case 8: // 8 inks + if (IsFloat) + Interpolation.LerpFloat = Eval8InputsFloat; + else + Interpolation.Lerp16 = Eval8Inputs; + break; + + break; + + default: + Interpolation.Lerp16 = NULL; + } + + return Interpolation; +} diff --git a/third_party/lcms/src/cmsio0.c b/third_party/lcms/src/cmsio0.c new file mode 100644 index 0000000000..3ed730a92a --- /dev/null +++ b/third_party/lcms/src/cmsio0.c @@ -0,0 +1,1895 @@ +//--------------------------------------------------------------------------------- +// +// 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" + +// Generic I/O, tag dictionary management, profile struct + +// IOhandlers are abstractions used by littleCMS to read from whatever file, stream, +// memory block or any storage. Each IOhandler provides implementations for read, +// write, seek and tell functions. LittleCMS code deals with IO across those objects. +// In this way, is easier to add support for new storage media. + +// NULL stream, for taking care of used space ------------------------------------- + +// NULL IOhandler basically does nothing but keep track on how many bytes have been +// written. This is handy when creating profiles, where the file size is needed in the +// header. Then, whole profile is serialized across NULL IOhandler and a second pass +// writes the bytes to the pertinent IOhandler. + +typedef struct { + cmsUInt32Number Pointer; // Points to current location +} FILENULL; + +static +cmsUInt32Number NULLRead(cmsIOHANDLER* iohandler, void *Buffer, cmsUInt32Number size, cmsUInt32Number count) +{ + FILENULL* ResData = (FILENULL*) iohandler ->stream; + + cmsUInt32Number len = size * count; + ResData -> Pointer += len; + return count; + + cmsUNUSED_PARAMETER(Buffer); +} + +static +cmsBool NULLSeek(cmsIOHANDLER* iohandler, cmsUInt32Number offset) +{ + FILENULL* ResData = (FILENULL*) iohandler ->stream; + + ResData ->Pointer = offset; + return TRUE; +} + +static +cmsUInt32Number NULLTell(cmsIOHANDLER* iohandler) +{ + FILENULL* ResData = (FILENULL*) iohandler ->stream; + return ResData -> Pointer; +} + +static +cmsBool NULLWrite(cmsIOHANDLER* iohandler, cmsUInt32Number size, const void *Ptr) +{ + FILENULL* ResData = (FILENULL*) iohandler ->stream; + + ResData ->Pointer += size; + if (ResData ->Pointer > iohandler->UsedSpace) + iohandler->UsedSpace = ResData ->Pointer; + + return TRUE; + + cmsUNUSED_PARAMETER(Ptr); +} + +static +cmsBool NULLClose(cmsIOHANDLER* iohandler) +{ + FILENULL* ResData = (FILENULL*) iohandler ->stream; + + _cmsFree(iohandler ->ContextID, ResData); + _cmsFree(iohandler ->ContextID, iohandler); + return TRUE; +} + +// The NULL IOhandler creator +cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromNULL(cmsContext ContextID) +{ + struct _cms_io_handler* iohandler = NULL; + FILENULL* fm = NULL; + + iohandler = (struct _cms_io_handler*) _cmsMallocZero(ContextID, sizeof(struct _cms_io_handler)); + if (iohandler == NULL) return NULL; + + fm = (FILENULL*) _cmsMallocZero(ContextID, sizeof(FILENULL)); + if (fm == NULL) goto Error; + + fm ->Pointer = 0; + + iohandler ->ContextID = ContextID; + iohandler ->stream = (void*) fm; + iohandler ->UsedSpace = 0; + iohandler ->ReportedSize = 0; + iohandler ->PhysicalFile[0] = 0; + + iohandler ->Read = NULLRead; + iohandler ->Seek = NULLSeek; + iohandler ->Close = NULLClose; + iohandler ->Tell = NULLTell; + iohandler ->Write = NULLWrite; + + return iohandler; + +Error: + if (iohandler) _cmsFree(ContextID, iohandler); + return NULL; + +} + + +// Memory-based stream -------------------------------------------------------------- + +// Those functions implements an iohandler which takes a block of memory as storage medium. + +typedef struct { + cmsUInt8Number* Block; // Points to allocated memory + cmsUInt32Number Size; // Size of allocated memory + cmsUInt32Number Pointer; // Points to current location + int FreeBlockOnClose; // As title + +} FILEMEM; + +static +cmsUInt32Number MemoryRead(struct _cms_io_handler* iohandler, void *Buffer, cmsUInt32Number size, cmsUInt32Number count) +{ + FILEMEM* ResData = (FILEMEM*) iohandler ->stream; + cmsUInt8Number* Ptr; + cmsUInt32Number len = size * count; + + if (ResData -> Pointer + len > ResData -> Size){ + + len = (ResData -> Size - ResData -> Pointer); + cmsSignalError(iohandler ->ContextID, cmsERROR_READ, "Read from memory error. Got %d bytes, block should be of %d bytes", len, count * size); + return 0; + } + + Ptr = ResData -> Block; + Ptr += ResData -> Pointer; + memmove(Buffer, Ptr, len); + ResData -> Pointer += len; + + return count; +} + +// SEEK_CUR is assumed +static +cmsBool MemorySeek(struct _cms_io_handler* iohandler, cmsUInt32Number offset) +{ + FILEMEM* ResData = (FILEMEM*) iohandler ->stream; + + if (offset > ResData ->Size) { + cmsSignalError(iohandler ->ContextID, cmsERROR_SEEK, "Too few data; probably corrupted profile"); + return FALSE; + } + + ResData ->Pointer = offset; + return TRUE; +} + +// Tell for memory +static +cmsUInt32Number MemoryTell(struct _cms_io_handler* iohandler) +{ + FILEMEM* ResData = (FILEMEM*) iohandler ->stream; + + if (ResData == NULL) return 0; + return ResData -> Pointer; +} + + +// Writes data to memory, also keeps used space for further reference. +static +cmsBool MemoryWrite(struct _cms_io_handler* iohandler, cmsUInt32Number size, const void *Ptr) +{ + FILEMEM* ResData = (FILEMEM*) iohandler ->stream; + + if (ResData == NULL) return FALSE; // Housekeeping + + // Check for available space. Clip. + if (ResData->Pointer + size > ResData->Size) { + size = ResData ->Size - ResData->Pointer; + } + + if (size == 0) return TRUE; // Write zero bytes is ok, but does nothing + + memmove(ResData ->Block + ResData ->Pointer, Ptr, size); + ResData ->Pointer += size; + + if (ResData ->Pointer > iohandler->UsedSpace) + iohandler->UsedSpace = ResData ->Pointer; + + return TRUE; +} + + +static +cmsBool MemoryClose(struct _cms_io_handler* iohandler) +{ + FILEMEM* ResData = (FILEMEM*) iohandler ->stream; + + if (ResData ->FreeBlockOnClose) { + + if (ResData ->Block) _cmsFree(iohandler ->ContextID, ResData ->Block); + } + + _cmsFree(iohandler ->ContextID, ResData); + _cmsFree(iohandler ->ContextID, iohandler); + + return TRUE; +} + +// Create a iohandler for memory block. AccessMode=='r' assumes the iohandler is going to read, and makes +// a copy of the memory block for letting user to free the memory after invoking open profile. In write +// mode ("w"), Buffere points to the begin of memory block to be written. +cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromMem(cmsContext ContextID, void *Buffer, cmsUInt32Number size, const char* AccessMode) +{ + cmsIOHANDLER* iohandler = NULL; + FILEMEM* fm = NULL; + + _cmsAssert(AccessMode != NULL); + + iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER)); + if (iohandler == NULL) return NULL; + + switch (*AccessMode) { + + case 'r': + fm = (FILEMEM*) _cmsMallocZero(ContextID, sizeof(FILEMEM)); + if (fm == NULL) goto Error; + + if (Buffer == NULL) { + cmsSignalError(ContextID, cmsERROR_READ, "Couldn't read profile from NULL pointer"); + goto Error; + } + + fm ->Block = (cmsUInt8Number*) _cmsMalloc(ContextID, size); + if (fm ->Block == NULL) { + + _cmsFree(ContextID, fm); + _cmsFree(ContextID, iohandler); + cmsSignalError(ContextID, cmsERROR_READ, "Couldn't allocate %ld bytes for profile", size); + return NULL; + } + + + memmove(fm->Block, Buffer, size); + fm ->FreeBlockOnClose = TRUE; + fm ->Size = size; + fm ->Pointer = 0; + iohandler -> ReportedSize = size; + break; + + case 'w': + fm = (FILEMEM*) _cmsMallocZero(ContextID, sizeof(FILEMEM)); + if (fm == NULL) goto Error; + + fm ->Block = (cmsUInt8Number*) Buffer; + fm ->FreeBlockOnClose = FALSE; + fm ->Size = size; + fm ->Pointer = 0; + iohandler -> ReportedSize = 0; + break; + + default: + cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown access mode '%c'", *AccessMode); + return NULL; + } + + iohandler ->ContextID = ContextID; + iohandler ->stream = (void*) fm; + iohandler ->UsedSpace = 0; + iohandler ->PhysicalFile[0] = 0; + + iohandler ->Read = MemoryRead; + iohandler ->Seek = MemorySeek; + iohandler ->Close = MemoryClose; + iohandler ->Tell = MemoryTell; + iohandler ->Write = MemoryWrite; + + return iohandler; + +Error: + if (fm) _cmsFree(ContextID, fm); + if (iohandler) _cmsFree(ContextID, iohandler); + return NULL; +} + +// File-based stream ------------------------------------------------------- + +// Read count elements of size bytes each. Return number of elements read +static +cmsUInt32Number FileRead(cmsIOHANDLER* iohandler, void *Buffer, cmsUInt32Number size, cmsUInt32Number count) +{ + cmsUInt32Number nReaded = (cmsUInt32Number) fread(Buffer, size, count, (FILE*) iohandler->stream); + + if (nReaded != count) { + cmsSignalError(iohandler ->ContextID, cmsERROR_FILE, "Read error. Got %d bytes, block should be of %d bytes", nReaded * size, count * size); + return 0; + } + + return nReaded; +} + +// Postion file pointer in the file +static +cmsBool FileSeek(cmsIOHANDLER* iohandler, cmsUInt32Number offset) +{ + if (fseek((FILE*) iohandler ->stream, (long) offset, SEEK_SET) != 0) { + + cmsSignalError(iohandler ->ContextID, cmsERROR_FILE, "Seek error; probably corrupted file"); + return FALSE; + } + + return TRUE; +} + +// Returns file pointer position +static +cmsUInt32Number FileTell(cmsIOHANDLER* iohandler) +{ + return (cmsUInt32Number) ftell((FILE*)iohandler ->stream); +} + +// Writes data to stream, also keeps used space for further reference. Returns TRUE on success, FALSE on error +static +cmsBool FileWrite(cmsIOHANDLER* iohandler, cmsUInt32Number size, const void* Buffer) +{ + if (size == 0) return TRUE; // We allow to write 0 bytes, but nothing is written + + iohandler->UsedSpace += size; + return (fwrite(Buffer, size, 1, (FILE*) iohandler->stream) == 1); +} + +// Closes the file +static +cmsBool FileClose(cmsIOHANDLER* iohandler) +{ + if (fclose((FILE*) iohandler ->stream) != 0) return FALSE; + _cmsFree(iohandler ->ContextID, iohandler); + return TRUE; +} + +// Create a iohandler for disk based files. +cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromFile(cmsContext ContextID, const char* FileName, const char* AccessMode) +{ + cmsIOHANDLER* iohandler = NULL; + FILE* fm = NULL; + + _cmsAssert(FileName != NULL); + _cmsAssert(AccessMode != NULL); + + iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER)); + if (iohandler == NULL) return NULL; + + switch (*AccessMode) { + + case 'r': + fm = fopen(FileName, "rb"); + if (fm == NULL) { + _cmsFree(ContextID, iohandler); + cmsSignalError(ContextID, cmsERROR_FILE, "File '%s' not found", FileName); + return NULL; + } + iohandler -> ReportedSize = (cmsUInt32Number) cmsfilelength(fm); + break; + + case 'w': + fm = fopen(FileName, "wb"); + if (fm == NULL) { + _cmsFree(ContextID, iohandler); + cmsSignalError(ContextID, cmsERROR_FILE, "Couldn't create '%s'", FileName); + return NULL; + } + iohandler -> ReportedSize = 0; + break; + + default: + _cmsFree(ContextID, iohandler); + cmsSignalError(ContextID, cmsERROR_FILE, "Unknown access mode '%c'", *AccessMode); + return NULL; + } + + iohandler ->ContextID = ContextID; + iohandler ->stream = (void*) fm; + iohandler ->UsedSpace = 0; + + // Keep track of the original file + strncpy(iohandler -> PhysicalFile, FileName, sizeof(iohandler -> PhysicalFile)-1); + iohandler -> PhysicalFile[sizeof(iohandler -> PhysicalFile)-1] = 0; + + iohandler ->Read = FileRead; + iohandler ->Seek = FileSeek; + iohandler ->Close = FileClose; + iohandler ->Tell = FileTell; + iohandler ->Write = FileWrite; + + return iohandler; +} + +// Create a iohandler for stream based files +cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromStream(cmsContext ContextID, FILE* Stream) +{ + cmsIOHANDLER* iohandler = NULL; + + iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER)); + if (iohandler == NULL) return NULL; + + iohandler -> ContextID = ContextID; + iohandler -> stream = (void*) Stream; + iohandler -> UsedSpace = 0; + iohandler -> ReportedSize = (cmsUInt32Number) cmsfilelength(Stream); + iohandler -> PhysicalFile[0] = 0; + + iohandler ->Read = FileRead; + iohandler ->Seek = FileSeek; + iohandler ->Close = FileClose; + iohandler ->Tell = FileTell; + iohandler ->Write = FileWrite; + + return iohandler; +} + + + +// Close an open IO handler +cmsBool CMSEXPORT cmsCloseIOhandler(cmsIOHANDLER* io) +{ + return io -> Close(io); +} + +// ------------------------------------------------------------------------------------------------------- + +#ifdef _WIN32_WCE +time_t wceex_time(time_t *timer); +struct tm * wceex_gmtime(const time_t *timer); + +#define time wceex_time +#define gmtime wceex_gmtime +#endif + +// Creates an empty structure holding all required parameters +cmsHPROFILE CMSEXPORT cmsCreateProfilePlaceholder(cmsContext ContextID) +{ + time_t now = time(NULL); + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) _cmsMallocZero(ContextID, sizeof(_cmsICCPROFILE)); + if (Icc == NULL) return NULL; + + Icc ->ContextID = ContextID; + + // Set it to empty + Icc -> TagCount = 0; + + // Set default version + Icc ->Version = 0x02100000; + + // 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; +} + +cmsContext CMSEXPORT cmsGetProfileContextID(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + + if (Icc == NULL) return NULL; + return Icc -> ContextID; +} + + +// Return the number of tags +cmsInt32Number CMSEXPORT cmsGetTagCount(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + if (Icc == NULL) return -1; + + return Icc->TagCount; +} + +// Return the tag signature of a given tag number +cmsTagSignature CMSEXPORT cmsGetTagSignature(cmsHPROFILE hProfile, cmsUInt32Number n) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + + if (n > Icc->TagCount) return (cmsTagSignature) 0; // Mark as not available + if (n >= MAX_TABLE_TAG) return (cmsTagSignature) 0; // As double check + + return Icc ->TagNames[n]; +} + + +static +int SearchOneTag(_cmsICCPROFILE* Profile, cmsTagSignature sig) +{ + cmsUInt32Number i; + + for (i=0; i < Profile -> TagCount; i++) { + + if (sig == Profile -> TagNames[i]) + return i; + } + + return -1; +} + +// Search for a specific tag in tag dictionary. Returns position or -1 if tag not found. +// If followlinks is turned on, then the position of the linked tag is returned +int _cmsSearchTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, cmsBool lFollowLinks) +{ + int n; + cmsTagSignature LinkedSig; + + do { + + // Search for given tag in ICC profile directory + n = SearchOneTag(Icc, sig); + if (n < 0) + return -1; // Not found + + if (!lFollowLinks) + return n; // Found, don't follow links + + // Is this a linked tag? + LinkedSig = Icc ->TagLinked[n]; + + // Yes, follow link + if (LinkedSig != (cmsTagSignature) 0) { + // fix bug mantis id#0055942 + // assume that TRCTag and ColorantTag can't be linked. + // Xiaochuan Liu 2014-04-23 + if ((sig == cmsSigRedTRCTag || sig == cmsSigGreenTRCTag || sig == cmsSigBlueTRCTag) && + (LinkedSig == cmsSigRedColorantTag || LinkedSig == cmsSigGreenColorantTag || LinkedSig == cmsSigBlueColorantTag)) + { + return n; + } + sig = LinkedSig; + } + + } while (LinkedSig != (cmsTagSignature) 0); + + 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]); + Icc ->TagPtrs[i] = NULL; + } + } + + } +} + + +// Creates a new tag entry +static +cmsBool _cmsNewTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, int* NewPos) +{ + int i; + + // Search for the tag + i = _cmsSearchTag(Icc, sig, FALSE); + if (i >= 0) { + + // Already exists? delete it + _cmsDeleteTagByPos(Icc, i); + *NewPos = i; + } + else { + + // No, make a new one + + if (Icc -> TagCount >= MAX_TABLE_TAG) { + cmsSignalError(Icc ->ContextID, cmsERROR_RANGE, "Too many tags (%d)", MAX_TABLE_TAG); + return FALSE; + } + + *NewPos = Icc ->TagCount; + Icc -> TagCount++; + } + + return TRUE; +} + + +// Check existance +cmsBool CMSEXPORT cmsIsTag(cmsHPROFILE hProfile, cmsTagSignature sig) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) (void*) hProfile; + return _cmsSearchTag(Icc, sig, FALSE) >= 0; +} + + +// Read profile header and validate it +cmsBool _cmsReadHeader(_cmsICCPROFILE* Icc) +{ + cmsTagEntry Tag; + cmsICCHeader Header; + cmsUInt32Number i, j; + cmsUInt32Number HeaderSize; + cmsIOHANDLER* io = Icc ->IOhandler; + cmsUInt32Number TagCount; + + + // Read the header + if (io -> Read(io, &Header, sizeof(cmsICCHeader), 1) != 1) { + return FALSE; + } + + // Validate file as an ICC profile + if (_cmsAdjustEndianess32(Header.magic) != cmsMagicNumber) { + cmsSignalError(Icc ->ContextID, cmsERROR_BAD_SIGNATURE, "not an ICC profile, invalid signature"); + return FALSE; + } + + // Adjust endianess of the used parameters + Icc -> DeviceClass = (cmsProfileClassSignature) _cmsAdjustEndianess32(Header.deviceClass); + Icc -> ColorSpace = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Header.colorSpace); + Icc -> PCS = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Header.pcs); + + Icc -> RenderingIntent = _cmsAdjustEndianess32(Header.renderingIntent); + Icc -> flags = _cmsAdjustEndianess32(Header.flags); + Icc -> manufacturer = _cmsAdjustEndianess32(Header.manufacturer); + Icc -> model = _cmsAdjustEndianess32(Header.model); + Icc -> creator = _cmsAdjustEndianess32(Header.creator); + + _cmsAdjustEndianess64(&Icc -> attributes, &Header.attributes); + Icc -> Version = _cmsAdjustEndianess32(Header.version); + + // Get size as reported in header + HeaderSize = _cmsAdjustEndianess32(Header.size); + + // Make sure HeaderSize is lower than profile size + if (HeaderSize >= Icc ->IOhandler ->ReportedSize) + HeaderSize = Icc ->IOhandler ->ReportedSize; + + + // Get creation date/time + _cmsDecodeDateTimeNumber(&Header.date, &Icc ->Created); + + // The profile ID are 32 raw bytes + memmove(Icc ->ProfileID.ID32, Header.profileID.ID32, 16); + + + // Read tag directory + if (!_cmsReadUInt32Number(io, &TagCount)) return FALSE; + if (TagCount > MAX_TABLE_TAG) { + + cmsSignalError(Icc ->ContextID, cmsERROR_RANGE, "Too many tags (%d)", TagCount); + return FALSE; + } + + + // Read tag directory + Icc -> TagCount = 0; + for (i=0; i < TagCount; i++) { + + if (!_cmsReadUInt32Number(io, (cmsUInt32Number *) &Tag.sig)) return FALSE; + if (!_cmsReadUInt32Number(io, &Tag.offset)) return FALSE; + if (!_cmsReadUInt32Number(io, &Tag.size)) return FALSE; + + // Perform some sanity check. Offset + size should fall inside file. + if (Tag.offset + Tag.size > HeaderSize || + Tag.offset + Tag.size < Tag.offset) + continue; + + Icc -> TagNames[Icc ->TagCount] = Tag.sig; + Icc -> TagOffsets[Icc ->TagCount] = Tag.offset; + Icc -> TagSizes[Icc ->TagCount] = Tag.size; + + // Search for links + for (j=0; j < Icc ->TagCount; j++) { + + if ((Icc ->TagOffsets[j] == Tag.offset) && + (Icc ->TagSizes[j] == Tag.size) && + (Icc ->TagNames[j] == Tag.sig)) { + + Icc ->TagLinked[Icc ->TagCount] = Icc ->TagNames[j]; + } + + } + + Icc ->TagCount++; + } + + return TRUE; +} + +// Saves profile header +cmsBool _cmsWriteHeader(_cmsICCPROFILE* Icc, cmsUInt32Number UsedSpace) +{ + cmsICCHeader Header; + cmsUInt32Number i; + cmsTagEntry Tag; + cmsInt32Number Count = 0; + + Header.size = _cmsAdjustEndianess32(UsedSpace); + Header.cmmId = _cmsAdjustEndianess32(lcmsSignature); + Header.version = _cmsAdjustEndianess32(Icc ->Version); + + Header.deviceClass = (cmsProfileClassSignature) _cmsAdjustEndianess32(Icc -> DeviceClass); + Header.colorSpace = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Icc -> ColorSpace); + Header.pcs = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Icc -> PCS); + + // NOTE: in v4 Timestamp must be in UTC rather than in local time + _cmsEncodeDateTimeNumber(&Header.date, &Icc ->Created); + + Header.magic = _cmsAdjustEndianess32(cmsMagicNumber); + +#ifdef CMS_IS_WINDOWS_ + Header.platform = (cmsPlatformSignature) _cmsAdjustEndianess32(cmsSigMicrosoft); +#else + Header.platform = (cmsPlatformSignature) _cmsAdjustEndianess32(cmsSigMacintosh); +#endif + + Header.flags = _cmsAdjustEndianess32(Icc -> flags); + Header.manufacturer = _cmsAdjustEndianess32(Icc -> manufacturer); + Header.model = _cmsAdjustEndianess32(Icc -> model); + + _cmsAdjustEndianess64(&Header.attributes, &Icc -> attributes); + + // Rendering intent in the header (for embedded profiles) + Header.renderingIntent = _cmsAdjustEndianess32(Icc -> RenderingIntent); + + // Illuminant is always D50 + Header.illuminant.X = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(cmsD50_XYZ()->X)); + Header.illuminant.Y = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(cmsD50_XYZ()->Y)); + Header.illuminant.Z = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(cmsD50_XYZ()->Z)); + + // Created by LittleCMS (that's me!) + Header.creator = _cmsAdjustEndianess32(lcmsSignature); + + memset(&Header.reserved, 0, sizeof(Header.reserved)); + + // Set profile ID. Endianess is always big endian + memmove(&Header.profileID, &Icc ->ProfileID, 16); + + // Dump the header + if (!Icc -> IOhandler->Write(Icc->IOhandler, sizeof(cmsICCHeader), &Header)) return FALSE; + + // Saves Tag directory + + // Get true count + for (i=0; i < Icc -> TagCount; i++) { + if (Icc ->TagNames[i] != 0) + Count++; + } + + // Store number of tags + if (!_cmsWriteUInt32Number(Icc ->IOhandler, Count)) return FALSE; + + for (i=0; i < Icc -> TagCount; i++) { + + if (Icc ->TagNames[i] == 0) continue; // It is just a placeholder + + Tag.sig = (cmsTagSignature) _cmsAdjustEndianess32((cmsInt32Number) Icc -> TagNames[i]); + Tag.offset = _cmsAdjustEndianess32((cmsInt32Number) Icc -> TagOffsets[i]); + Tag.size = _cmsAdjustEndianess32((cmsInt32Number) Icc -> TagSizes[i]); + + if (!Icc ->IOhandler -> Write(Icc-> IOhandler, sizeof(cmsTagEntry), &Tag)) return FALSE; + } + + return TRUE; +} + +// ----------------------------------------------------------------------- Set/Get several struct members + + +cmsUInt32Number CMSEXPORT cmsGetHeaderRenderingIntent(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return Icc -> RenderingIntent; +} + +void CMSEXPORT cmsSetHeaderRenderingIntent(cmsHPROFILE hProfile, cmsUInt32Number RenderingIntent) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + Icc -> RenderingIntent = RenderingIntent; +} + +cmsUInt32Number CMSEXPORT cmsGetHeaderFlags(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return (cmsUInt32Number) Icc -> flags; +} + +void CMSEXPORT cmsSetHeaderFlags(cmsHPROFILE hProfile, cmsUInt32Number Flags) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + Icc -> flags = (cmsUInt32Number) Flags; +} + +cmsUInt32Number CMSEXPORT cmsGetHeaderManufacturer(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return Icc ->manufacturer; +} + +void CMSEXPORT cmsSetHeaderManufacturer(cmsHPROFILE hProfile, cmsUInt32Number manufacturer) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + Icc -> manufacturer = manufacturer; +} + +cmsUInt32Number CMSEXPORT cmsGetHeaderCreator(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return Icc ->creator; +} + +cmsUInt32Number CMSEXPORT cmsGetHeaderModel(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return Icc ->model; +} + +void CMSEXPORT cmsSetHeaderModel(cmsHPROFILE hProfile, cmsUInt32Number model) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + Icc -> model = model; +} + +void CMSEXPORT cmsGetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number* Flags) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + memmove(Flags, &Icc -> attributes, sizeof(cmsUInt64Number)); +} + +void CMSEXPORT cmsSetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number Flags) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + memmove(&Icc -> attributes, &Flags, sizeof(cmsUInt64Number)); +} + +void CMSEXPORT cmsGetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + memmove(ProfileID, Icc ->ProfileID.ID8, 16); +} + +void CMSEXPORT cmsSetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + memmove(&Icc -> ProfileID, ProfileID, 16); +} + +cmsBool CMSEXPORT cmsGetHeaderCreationDateTime(cmsHPROFILE hProfile, struct tm *Dest) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + memmove(Dest, &Icc ->Created, sizeof(struct tm)); + return TRUE; +} + +cmsColorSpaceSignature CMSEXPORT cmsGetPCS(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return Icc -> PCS; +} + +void CMSEXPORT cmsSetPCS(cmsHPROFILE hProfile, cmsColorSpaceSignature pcs) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + Icc -> PCS = pcs; +} + +cmsColorSpaceSignature CMSEXPORT cmsGetColorSpace(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return Icc -> ColorSpace; +} + +void CMSEXPORT cmsSetColorSpace(cmsHPROFILE hProfile, cmsColorSpaceSignature sig) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + Icc -> ColorSpace = sig; +} + +cmsProfileClassSignature CMSEXPORT cmsGetDeviceClass(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return Icc -> DeviceClass; +} + +void CMSEXPORT cmsSetDeviceClass(cmsHPROFILE hProfile, cmsProfileClassSignature sig) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + Icc -> DeviceClass = sig; +} + +cmsUInt32Number CMSEXPORT cmsGetEncodedICCversion(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + return Icc -> Version; +} + +void CMSEXPORT cmsSetEncodedICCversion(cmsHPROFILE hProfile, cmsUInt32Number Version) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + Icc -> Version = Version; +} + +// Get an hexadecimal number with same digits as v +static +cmsUInt32Number BaseToBase(cmsUInt32Number in, int BaseIn, int BaseOut) +{ + char Buff[100]; + int i, len; + cmsUInt32Number out; + + for (len=0; in > 0 && len < 100; len++) { + + Buff[len] = (char) (in % BaseIn); + in /= BaseIn; + } + + for (i=len-1, out=0; i >= 0; --i) { + out = out * BaseOut + Buff[i]; + } + + return out; +} + +void CMSEXPORT cmsSetProfileVersion(cmsHPROFILE hProfile, cmsFloat64Number Version) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + + // 4.2 -> 0x4200000 + + Icc -> Version = BaseToBase((cmsUInt32Number) floor(Version * 100.0 + 0.5), 10, 16) << 16; +} + +cmsFloat64Number CMSEXPORT cmsGetProfileVersion(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + cmsUInt32Number n = Icc -> Version >> 16; + + return BaseToBase(n, 16, 10) / 100.0; +} +// -------------------------------------------------------------------------------------------------------------- + + +// Create profile from IOhandler +cmsHPROFILE CMSEXPORT cmsOpenProfileFromIOhandlerTHR(cmsContext ContextID, cmsIOHANDLER* io) +{ + _cmsICCPROFILE* NewIcc; + cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID); + + if (hEmpty == NULL) return NULL; + + NewIcc = (_cmsICCPROFILE*) hEmpty; + + NewIcc ->IOhandler = io; + if (!_cmsReadHeader(NewIcc)) goto Error; + return hEmpty; + +Error: + cmsCloseProfile(hEmpty); + return NULL; +} + +// Create profile from IOhandler +cmsHPROFILE CMSEXPORT cmsOpenProfileFromIOhandler2THR(cmsContext ContextID, cmsIOHANDLER* io, cmsBool write) +{ + _cmsICCPROFILE* NewIcc; + cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID); + + if (hEmpty == NULL) return NULL; + + NewIcc = (_cmsICCPROFILE*) hEmpty; + + NewIcc ->IOhandler = io; + if (write) { + + NewIcc -> IsWrite = TRUE; + return hEmpty; + } + + if (!_cmsReadHeader(NewIcc)) goto Error; + return hEmpty; + +Error: + cmsCloseProfile(hEmpty); + return NULL; +} + + +// Create profile from disk file +cmsHPROFILE CMSEXPORT cmsOpenProfileFromFileTHR(cmsContext ContextID, const char *lpFileName, const char *sAccess) +{ + _cmsICCPROFILE* NewIcc; + cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID); + + if (hEmpty == NULL) return NULL; + + NewIcc = (_cmsICCPROFILE*) hEmpty; + + NewIcc ->IOhandler = cmsOpenIOhandlerFromFile(ContextID, lpFileName, sAccess); + if (NewIcc ->IOhandler == NULL) goto Error; + + if (*sAccess == 'W' || *sAccess == 'w') { + + NewIcc -> IsWrite = TRUE; + + return hEmpty; + } + + if (!_cmsReadHeader(NewIcc)) goto Error; + return hEmpty; + +Error: + cmsCloseProfile(hEmpty); + return NULL; +} + + +cmsHPROFILE CMSEXPORT cmsOpenProfileFromFile(const char *ICCProfile, const char *sAccess) +{ + return cmsOpenProfileFromFileTHR(NULL, ICCProfile, sAccess); +} + + +cmsHPROFILE CMSEXPORT cmsOpenProfileFromStreamTHR(cmsContext ContextID, FILE* ICCProfile, const char *sAccess) +{ + _cmsICCPROFILE* NewIcc; + cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID); + + if (hEmpty == NULL) return NULL; + + NewIcc = (_cmsICCPROFILE*) hEmpty; + + NewIcc ->IOhandler = cmsOpenIOhandlerFromStream(ContextID, ICCProfile); + if (NewIcc ->IOhandler == NULL) goto Error; + + if (*sAccess == 'w') { + + NewIcc -> IsWrite = TRUE; + return hEmpty; + } + + if (!_cmsReadHeader(NewIcc)) goto Error; + return hEmpty; + +Error: + cmsCloseProfile(hEmpty); + return NULL; + +} + +cmsHPROFILE CMSEXPORT cmsOpenProfileFromStream(FILE* ICCProfile, const char *sAccess) +{ + return cmsOpenProfileFromStreamTHR(NULL, ICCProfile, sAccess); +} + + +// Open from memory block +cmsHPROFILE CMSEXPORT cmsOpenProfileFromMemTHR(cmsContext ContextID, const void* MemPtr, cmsUInt32Number dwSize) +{ + _cmsICCPROFILE* NewIcc; + cmsHPROFILE hEmpty; + + hEmpty = cmsCreateProfilePlaceholder(ContextID); + if (hEmpty == NULL) return NULL; + + NewIcc = (_cmsICCPROFILE*) hEmpty; + + // Ok, in this case const void* is casted to void* just because open IO handler + // shares read and writting modes. Don't abuse this feature! + NewIcc ->IOhandler = cmsOpenIOhandlerFromMem(ContextID, (void*) MemPtr, dwSize, "r"); + if (NewIcc ->IOhandler == NULL) goto Error; + + if (!_cmsReadHeader(NewIcc)) goto Error; + + return hEmpty; + +Error: + cmsCloseProfile(hEmpty); + return NULL; +} + +cmsHPROFILE CMSEXPORT cmsOpenProfileFromMem(const void* MemPtr, cmsUInt32Number dwSize) +{ + return cmsOpenProfileFromMemTHR(NULL, MemPtr, dwSize); +} + + + +// Dump tag contents. If the profile is being modified, untouched tags are copied from FileOrig +static +cmsBool SaveTags(_cmsICCPROFILE* Icc, _cmsICCPROFILE* FileOrig) +{ + cmsUInt8Number* Data; + cmsUInt32Number i; + cmsUInt32Number Begin; + cmsIOHANDLER* io = Icc ->IOhandler; + cmsTagDescriptor* TagDescriptor; + cmsTagTypeSignature TypeBase; + cmsTagTypeSignature Type; + cmsTagTypeHandler* TypeHandler; + cmsFloat64Number Version = cmsGetProfileVersion((cmsHPROFILE) Icc); + cmsTagTypeHandler LocalTypeHandler; + + for (i=0; i < Icc -> TagCount; i++) { + + if (Icc ->TagNames[i] == 0) continue; + + // Linked tags are not written + if (Icc ->TagLinked[i] != (cmsTagSignature) 0) continue; + + Icc -> TagOffsets[i] = Begin = io ->UsedSpace; + + Data = (cmsUInt8Number*) Icc -> TagPtrs[i]; + + if (!Data) { + + // Reach here if we are copying a tag from a disk-based ICC profile which has not been modified by user. + // In this case a blind copy of the block data is performed + if (FileOrig != NULL && Icc -> TagOffsets[i]) { + + cmsUInt32Number TagSize = FileOrig -> TagSizes[i]; + cmsUInt32Number TagOffset = FileOrig -> TagOffsets[i]; + void* Mem; + + if (!FileOrig ->IOhandler->Seek(FileOrig ->IOhandler, TagOffset)) return FALSE; + + Mem = _cmsMalloc(Icc ->ContextID, TagSize); + if (Mem == NULL) return FALSE; + + if (FileOrig ->IOhandler->Read(FileOrig->IOhandler, Mem, TagSize, 1) != 1) return FALSE; + if (!io ->Write(io, TagSize, Mem)) return FALSE; + _cmsFree(Icc ->ContextID, Mem); + + Icc -> TagSizes[i] = (io ->UsedSpace - Begin); + + + // Align to 32 bit boundary. + if (! _cmsWriteAlignment(io)) + return FALSE; + } + + continue; + } + + + // Should this tag be saved as RAW? If so, tagsizes should be specified in advance (no further cooking is done) + if (Icc ->TagSaveAsRaw[i]) { + + if (io -> Write(io, Icc ->TagSizes[i], Data) != 1) return FALSE; + } + else { + + // Search for support on this tag + TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, Icc -> TagNames[i]); + if (TagDescriptor == NULL) continue; // Unsupported, ignore it + + if (TagDescriptor ->DecideType != NULL) { + + Type = TagDescriptor ->DecideType(Version, Data); + } + else { + + Type = TagDescriptor ->SupportedTypes[0]; + } + + TypeHandler = _cmsGetTagTypeHandler(Icc->ContextID, Type); + + if (TypeHandler == NULL) { + cmsSignalError(Icc ->ContextID, cmsERROR_INTERNAL, "(Internal) no handler for tag %x", Icc -> TagNames[i]); + continue; + } + + TypeBase = TypeHandler ->Signature; + if (!_cmsWriteTypeBase(io, TypeBase)) + return FALSE; + + LocalTypeHandler = *TypeHandler; + LocalTypeHandler.ContextID = Icc ->ContextID; + LocalTypeHandler.ICCVersion = Icc ->Version; + if (!LocalTypeHandler.WritePtr(&LocalTypeHandler, io, Data, TagDescriptor ->ElemCount)) { + + char String[5]; + + _cmsTagSignature2String(String, (cmsTagSignature) TypeBase); + cmsSignalError(Icc ->ContextID, cmsERROR_WRITE, "Couldn't write type '%s'", String); + return FALSE; + } + } + + + Icc -> TagSizes[i] = (io ->UsedSpace - Begin); + + // Align to 32 bit boundary. + if (! _cmsWriteAlignment(io)) + return FALSE; + } + + + return TRUE; +} + + +// Fill the offset and size fields for all linked tags +static +cmsBool SetLinks( _cmsICCPROFILE* Icc) +{ + cmsUInt32Number i; + + for (i=0; i < Icc -> TagCount; i++) { + + cmsTagSignature lnk = Icc ->TagLinked[i]; + if (lnk != (cmsTagSignature) 0) { + + int j = _cmsSearchTag(Icc, lnk, FALSE); + if (j >= 0) { + + Icc ->TagOffsets[i] = Icc ->TagOffsets[j]; + Icc ->TagSizes[i] = Icc ->TagSizes[j]; + } + + } + } + + return TRUE; +} + +// Low-level save to IOHANDLER. It returns the number of bytes used to +// store the profile, or zero on error. io may be NULL and in this case +// no data is written--only sizes are calculated +cmsUInt32Number CMSEXPORT cmsSaveProfileToIOhandler(cmsHPROFILE hProfile, cmsIOHANDLER* io) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + _cmsICCPROFILE Keep; + cmsIOHANDLER* PrevIO = NULL; + cmsUInt32Number UsedSpace; + cmsContext ContextID; + + _cmsAssert(hProfile != NULL); + + memmove(&Keep, Icc, sizeof(_cmsICCPROFILE)); + + ContextID = cmsGetProfileContextID(hProfile); + PrevIO = Icc ->IOhandler = cmsOpenIOhandlerFromNULL(ContextID); + if (PrevIO == NULL) return 0; + + // Pass #1 does compute offsets + + if (!_cmsWriteHeader(Icc, 0)) goto Error; + if (!SaveTags(Icc, &Keep)) goto Error; + + UsedSpace = PrevIO ->UsedSpace; + + // Pass #2 does save to iohandler + + if (io != NULL) { + + Icc ->IOhandler = io; + if (!SetLinks(Icc)) goto Error; + if (!_cmsWriteHeader(Icc, UsedSpace)) goto Error; + if (!SaveTags(Icc, &Keep)) goto Error; + } + + memmove(Icc, &Keep, sizeof(_cmsICCPROFILE)); + if (!cmsCloseIOhandler(PrevIO)) return 0; + + return UsedSpace; + + +Error: + cmsCloseIOhandler(PrevIO); + memmove(Icc, &Keep, sizeof(_cmsICCPROFILE)); + return 0; +} + +#ifdef _WIN32_WCE +int wceex_unlink(const char *filename); +#ifndef remove +# define remove wceex_unlink +#endif +#endif + +// Low-level save to disk. +cmsBool CMSEXPORT cmsSaveProfileToFile(cmsHPROFILE hProfile, const char* FileName) +{ + cmsContext ContextID = cmsGetProfileContextID(hProfile); + cmsIOHANDLER* io = cmsOpenIOhandlerFromFile(ContextID, FileName, "w"); + cmsBool rc; + + if (io == NULL) return FALSE; + + rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0); + rc &= cmsCloseIOhandler(io); + + if (rc == FALSE) { // remove() is C99 per 7.19.4.1 + remove(FileName); // We have to IGNORE return value in this case + } + return rc; +} + +// Same as anterior, but for streams +cmsBool CMSEXPORT cmsSaveProfileToStream(cmsHPROFILE hProfile, FILE* Stream) +{ + cmsBool rc; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + cmsIOHANDLER* io = cmsOpenIOhandlerFromStream(ContextID, Stream); + + if (io == NULL) return FALSE; + + rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0); + rc &= cmsCloseIOhandler(io); + + return rc; +} + + +// Same as anterior, but for memory blocks. In this case, a NULL as MemPtr means calculate needed space only +cmsBool CMSEXPORT cmsSaveProfileToMem(cmsHPROFILE hProfile, void *MemPtr, cmsUInt32Number* BytesNeeded) +{ + cmsBool rc; + cmsIOHANDLER* io; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + _cmsAssert(BytesNeeded != NULL); + + // Should we just calculate the needed space? + if (MemPtr == NULL) { + + *BytesNeeded = cmsSaveProfileToIOhandler(hProfile, NULL); + return (*BytesNeeded == 0) ? FALSE : TRUE; + } + + // That is a real write operation + io = cmsOpenIOhandlerFromMem(ContextID, MemPtr, *BytesNeeded, "w"); + if (io == NULL) return FALSE; + + rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0); + rc &= cmsCloseIOhandler(io); + + return rc; +} + + + +// Closes a profile freeing any involved resources +cmsBool CMSEXPORT cmsCloseProfile(cmsHPROFILE hProfile) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + cmsBool rc = TRUE; + cmsUInt32Number i; + + if (!Icc) return FALSE; + + // Was open in write mode? + if (Icc ->IsWrite) { + + Icc ->IsWrite = FALSE; // Assure no further writting + rc &= cmsSaveProfileToFile(hProfile, Icc ->IOhandler->PhysicalFile); + } + + for (i=0; i < Icc -> TagCount; i++) { + + if (Icc -> TagPtrs[i]) { + + cmsTagTypeHandler* TypeHandler = Icc ->TagTypeHandlers[i]; + + if (TypeHandler != NULL) { + cmsTagTypeHandler LocalTypeHandler = *TypeHandler; + + LocalTypeHandler.ContextID = Icc ->ContextID; // As an additional parameters + LocalTypeHandler.ICCVersion = Icc ->Version; + LocalTypeHandler.FreePtr(&LocalTypeHandler, Icc -> TagPtrs[i]); + } + else + _cmsFree(Icc ->ContextID, Icc ->TagPtrs[i]); + } + } + + if (Icc ->IOhandler != NULL) { + rc &= cmsCloseIOhandler(Icc->IOhandler); + } + + _cmsDestroyMutex(Icc->ContextID, Icc->UsrMutex); + + _cmsFree(Icc ->ContextID, Icc); // Free placeholder memory + + return rc; +} + + +// ------------------------------------------------------------------------------------------------------------------- + + +// Returns TRUE if a given tag is supported by a plug-in +static +cmsBool IsTypeSupported(cmsTagDescriptor* TagDescriptor, cmsTagTypeSignature Type) +{ + cmsUInt32Number i, nMaxTypes; + + nMaxTypes = TagDescriptor->nSupportedTypes; + if (nMaxTypes >= MAX_TYPES_IN_LCMS_PLUGIN) + nMaxTypes = MAX_TYPES_IN_LCMS_PLUGIN; + + for (i=0; i < nMaxTypes; i++) { + if (Type == TagDescriptor ->SupportedTypes[i]) return TRUE; + } + + return FALSE; +} + + +// That's the main read function +void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + cmsIOHANDLER* io = Icc ->IOhandler; + cmsTagTypeHandler* TypeHandler; + cmsTagTypeHandler LocalTypeHandler; + cmsTagDescriptor* TagDescriptor; + cmsTagTypeSignature BaseType; + cmsUInt32Number Offset, TagSize; + cmsUInt32Number ElemCount; + int n; + + if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return NULL; + + n = _cmsSearchTag(Icc, sig, TRUE); + 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->TagTypeHandlers[n] == NULL) goto Error; + + // Sanity check + BaseType = Icc->TagTypeHandlers[n]->Signature; + if (BaseType == 0) goto Error; + + TagDescriptor = _cmsGetTagDescriptor(Icc->ContextID, sig); + if (TagDescriptor == NULL) goto Error; + + if (!IsTypeSupported(TagDescriptor, BaseType)) goto Error; + + if (Icc ->TagSaveAsRaw[n]) goto Error; // We don't support read raw tags as cooked + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return Icc -> TagPtrs[n]; + } + + // We need to read it. Get the offset and size to the file + Offset = Icc -> TagOffsets[n]; + TagSize = Icc -> TagSizes[n]; + + // Seek to its location + if (!io -> Seek(io, Offset)) + goto Error; + + // Search for support on this tag + 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) goto Error; + + if (!IsTypeSupported(TagDescriptor, BaseType)) goto Error; + + TagSize -= 8; // Alredy read by the type base logic + + // Get type handler + TypeHandler = _cmsGetTagTypeHandler(Icc ->ContextID, BaseType); + if (TypeHandler == NULL) goto Error; + LocalTypeHandler = *TypeHandler; + + + // Read the tag + Icc -> TagTypeHandlers[n] = TypeHandler; + + LocalTypeHandler.ContextID = Icc ->ContextID; + LocalTypeHandler.ICCVersion = Icc ->Version; + Icc -> TagPtrs[n] = LocalTypeHandler.ReadPtr(&LocalTypeHandler, io, &ElemCount, TagSize); + + // The tag type is supported, but something wrong happend and we cannot read the tag. + // let know the user about this (although it is just a warning) + if (Icc -> TagPtrs[n] == NULL) { + + char String[5]; + + _cmsTagSignature2String(String, sig); + cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "Corrupted tag '%s'", String); + goto Error; + } + + // This is a weird error that may be a symptom of something more serious, the number of + // stored item is actually less than the number of required elements. + if (ElemCount < TagDescriptor ->ElemCount) { + + char String[5]; + + _cmsTagSignature2String(String, sig); + cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "'%s' Inconsistent number of items: expected %d, got %d", + String, TagDescriptor ->ElemCount, ElemCount); + } + + + // 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; +} + + +// Get true type of data +cmsTagTypeSignature _cmsGetTagTrueType(cmsHPROFILE hProfile, cmsTagSignature sig) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + cmsTagTypeHandler* TypeHandler; + int n; + + // Search for given tag in ICC profile directory + n = _cmsSearchTag(Icc, sig, TRUE); + if (n < 0) return (cmsTagTypeSignature) 0; // Not found, return NULL + + // Get the handler. The true type is there + TypeHandler = Icc -> TagTypeHandlers[n]; + return TypeHandler ->Signature; +} + + +// Write a single tag. This just keeps track of the tak into a list of "to be written". If the tag is already +// in that list, the previous version is deleted. +cmsBool CMSEXPORT cmsWriteTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + cmsTagTypeHandler* TypeHandler = NULL; + cmsTagTypeHandler LocalTypeHandler; + cmsTagDescriptor* TagDescriptor = NULL; + cmsTagTypeSignature Type; + int i; + 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) { + + // Use zero as a mark of deleted + _cmsDeleteTagByPos(Icc, i); + Icc ->TagNames[i] = (cmsTagSignature) 0; + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return TRUE; + } + // Didn't find the tag + goto Error; + } + + if (!_cmsNewTag(Icc, sig, &i)) goto Error; + + // This is not raw + Icc ->TagSaveAsRaw[i] = FALSE; + + // This is not a link + Icc ->TagLinked[i] = (cmsTagSignature) 0; + + // Get information about the TAG. + TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig); + if (TagDescriptor == NULL){ + cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported tag '%x'", sig); + goto Error; + } + + + // Now we need to know which type to use. It depends on the version. + Version = cmsGetProfileVersion(hProfile); + + if (TagDescriptor ->DecideType != NULL) { + + // Let the tag descriptor to decide the type base on depending on + // the data. This is useful for example on parametric curves, where + // curves specified by a table cannot be saved as parametric and needs + // to be casted to single v2-curves, even on v4 profiles. + + Type = TagDescriptor ->DecideType(Version, data); + } + else { + + Type = TagDescriptor ->SupportedTypes[0]; + } + + // Does the tag support this type? + if (!IsTypeSupported(TagDescriptor, Type)) { + + _cmsTagSignature2String(TypeString, (cmsTagSignature) Type); + _cmsTagSignature2String(SigString, sig); + + cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported type '%s' for tag '%s'", TypeString, SigString); + goto Error; + } + + // Does we have a handler for this 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); + goto Error; // Should never happen + } + + + // Fill fields on icc structure + Icc ->TagTypeHandlers[i] = TypeHandler; + Icc ->TagNames[i] = sig; + Icc ->TagSizes[i] = 0; + Icc ->TagOffsets[i] = 0; + + LocalTypeHandler = *TypeHandler; + LocalTypeHandler.ContextID = Icc ->ContextID; + LocalTypeHandler.ICCVersion = Icc ->Version; + Icc ->TagPtrs[i] = LocalTypeHandler.DupPtr(&LocalTypeHandler, data, TagDescriptor ->ElemCount); + + if (Icc ->TagPtrs[i] == NULL) { + + _cmsTagSignature2String(TypeString, (cmsTagSignature) Type); + _cmsTagSignature2String(SigString, sig); + cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "Malformed struct in type '%s' for tag '%s'", TypeString, SigString); + + 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 +// is to do an additional step of serialization. That means, readRaw would issue a normal read and then convert the obtained +// data to raw bytes by using the "write" serialization logic. And vice-versa. I know this may end in situations where +// raw data written does not exactly correspond with the raw data proposed to cmsWriteRaw data, but this approach allows +// to write a tag as raw data and the read it as handled. + +cmsInt32Number CMSEXPORT cmsReadRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, void* data, cmsUInt32Number BufferSize) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + void *Object; + int i; + cmsIOHANDLER* MemIO; + cmsTagTypeHandler* TypeHandler = NULL; + cmsTagTypeHandler LocalTypeHandler; + cmsTagDescriptor* TagDescriptor = NULL; + 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) goto Error; // Not found, + + // It is already read? + if (Icc -> TagPtrs[i] == NULL) { + + // No yet, get original position + Offset = Icc ->TagOffsets[i]; + TagSize = Icc ->TagSizes[i]; + + // read the data directly, don't keep copy + if (data != NULL) { + + if (BufferSize < TagSize) + TagSize = BufferSize; + + if (!Icc ->IOhandler ->Seek(Icc ->IOhandler, Offset)) goto Error; + if (!Icc ->IOhandler ->Read(Icc ->IOhandler, data, 1, TagSize)) goto Error; + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return TagSize; + } + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return Icc ->TagSizes[i]; + } + + // The data has been already read, or written. But wait!, maybe the user choosed to save as + // raw data. In this case, return the raw data directly + if (Icc ->TagSaveAsRaw[i]) { + + if (data != NULL) { + + TagSize = Icc ->TagSizes[i]; + if (BufferSize < TagSize) + TagSize = BufferSize; + + 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. + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + Object = cmsReadTag(hProfile, sig); + if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0; + + if (Object == NULL) goto Error; + + // Now we need to serialize to a memory block: just use a memory iohandler + + if (data == NULL) { + MemIO = cmsOpenIOhandlerFromNULL(cmsGetProfileContextID(hProfile)); + } else{ + MemIO = cmsOpenIOhandlerFromMem(cmsGetProfileContextID(hProfile), data, BufferSize, "w"); + } + if (MemIO == NULL) goto Error; + + // Obtain type handling for the tag + TypeHandler = Icc ->TagTypeHandlers[i]; + TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig); + if (TagDescriptor == NULL) { + cmsCloseIOhandler(MemIO); + goto Error; + } + + if (TypeHandler == NULL) goto Error; + + // Serialize + LocalTypeHandler = *TypeHandler; + LocalTypeHandler.ContextID = Icc ->ContextID; + LocalTypeHandler.ICCVersion = Icc ->Version; + + if (!_cmsWriteTypeBase(MemIO, TypeHandler ->Signature)) { + cmsCloseIOhandler(MemIO); + goto Error; + } + + if (!LocalTypeHandler.WritePtr(&LocalTypeHandler, MemIO, Object, TagDescriptor ->ElemCount)) { + cmsCloseIOhandler(MemIO); + 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 +// checking anything. As a rule, mixing Raw with cooked doesn't work, so writting a tag as raw and then reading +// it as cooked without serializing does result into an error. If that is wha you want, you will need to dump +// the profile to memry or disk and then reopen it. +cmsBool CMSEXPORT cmsWriteRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data, cmsUInt32Number Size) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + int i; + + 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; + Icc ->TagNames[i] = sig; + Icc ->TagLinked[i] = (cmsTagSignature) 0; + + // Keep a copy of the block + Icc ->TagPtrs[i] = _cmsDupMem(Icc ->ContextID, data, Size); + Icc ->TagSizes[i] = Size; + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return TRUE; +} + +// Using this function you can collapse several tag entries to the same block in the profile +cmsBool CMSEXPORT cmsLinkTag(cmsHPROFILE hProfile, cmsTagSignature sig, cmsTagSignature dest) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + int i; + + 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; + Icc ->TagNames[i] = sig; + Icc ->TagLinked[i] = dest; + + Icc ->TagPtrs[i] = NULL; + Icc ->TagSizes[i] = 0; + Icc ->TagOffsets[i] = 0; + + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); + return TRUE; +} + + +// Returns the tag linked to sig, in the case two tags are sharing same resource +cmsTagSignature CMSEXPORT cmsTagLinkedTo(cmsHPROFILE hProfile, cmsTagSignature sig) +{ + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + int i; + + // Search for given tag in ICC profile directory + i = _cmsSearchTag(Icc, sig, FALSE); + if (i < 0) return (cmsTagSignature) 0; // Not found, return 0 + + return Icc -> TagLinked[i]; +} diff --git a/third_party/lcms/src/cmsio1.c b/third_party/lcms/src/cmsio1.c new file mode 100644 index 0000000000..778aa2b4fc --- /dev/null +++ b/third_party/lcms/src/cmsio1.c @@ -0,0 +1,1020 @@ +//--------------------------------------------------------------------------------- +// +// 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" + +// Read tags using low-level functions, provides necessary glue code to adapt versions, etc. + +// LUT tags +static const cmsTagSignature Device2PCS16[] = {cmsSigAToB0Tag, // Perceptual + cmsSigAToB1Tag, // Relative colorimetric + cmsSigAToB2Tag, // Saturation + cmsSigAToB1Tag }; // Absolute colorimetric + +static const cmsTagSignature Device2PCSFloat[] = {cmsSigDToB0Tag, // Perceptual + cmsSigDToB1Tag, // Relative colorimetric + cmsSigDToB2Tag, // Saturation + cmsSigDToB3Tag }; // Absolute colorimetric + +static const cmsTagSignature PCS2Device16[] = {cmsSigBToA0Tag, // Perceptual + cmsSigBToA1Tag, // Relative colorimetric + cmsSigBToA2Tag, // Saturation + cmsSigBToA1Tag }; // Absolute colorimetric + +static const cmsTagSignature PCS2DeviceFloat[] = {cmsSigBToD0Tag, // Perceptual + cmsSigBToD1Tag, // Relative colorimetric + cmsSigBToD2Tag, // Saturation + cmsSigBToD3Tag }; // Absolute colorimetric + + +// Factors to convert from 1.15 fixed point to 0..1.0 range and vice-versa +#define InpAdj (1.0/MAX_ENCODEABLE_XYZ) // (65536.0/(65535.0*2.0)) +#define OutpAdj (MAX_ENCODEABLE_XYZ) // ((2.0*65535.0)/65536.0) + +// Several resources for gray conversions. +static const cmsFloat64Number GrayInputMatrix[] = { (InpAdj*cmsD50X), (InpAdj*cmsD50Y), (InpAdj*cmsD50Z) }; +static const cmsFloat64Number OneToThreeInputMatrix[] = { 1, 1, 1 }; +static const cmsFloat64Number PickYMatrix[] = { 0, (OutpAdj*cmsD50Y), 0 }; +static const cmsFloat64Number PickLstarMatrix[] = { 1, 0, 0 }; + +// Get a media white point fixing some issues found in certain old profiles +cmsBool _cmsReadMediaWhitePoint(cmsCIEXYZ* Dest, cmsHPROFILE hProfile) +{ + cmsCIEXYZ* Tag; + + _cmsAssert(Dest != NULL); + + Tag = (cmsCIEXYZ*) cmsReadTag(hProfile, cmsSigMediaWhitePointTag); + + // If no wp, take D50 + if (Tag == NULL) { + *Dest = *cmsD50_XYZ(); + return TRUE; + } + + // V2 display profiles should give D50 + if (cmsGetEncodedICCversion(hProfile) < 0x4000000) { + + if (cmsGetDeviceClass(hProfile) == cmsSigDisplayClass) { + *Dest = *cmsD50_XYZ(); + return TRUE; + } + } + + // All seems ok + *Dest = *Tag; + return TRUE; +} + + +// Chromatic adaptation matrix. Fix some issues as well +cmsBool _cmsReadCHAD(cmsMAT3* Dest, cmsHPROFILE hProfile) +{ + cmsMAT3* Tag; + + _cmsAssert(Dest != NULL); + + Tag = (cmsMAT3*) cmsReadTag(hProfile, cmsSigChromaticAdaptationTag); + + if (Tag != NULL) { + *Dest = *Tag; + return TRUE; + } + + // No CHAD available, default it to identity + _cmsMAT3identity(Dest); + + // V2 display profiles should give D50 + if (cmsGetEncodedICCversion(hProfile) < 0x4000000) { + + if (cmsGetDeviceClass(hProfile) == cmsSigDisplayClass) { + + cmsCIEXYZ* White = (cmsCIEXYZ*) cmsReadTag(hProfile, cmsSigMediaWhitePointTag); + + if (White == NULL) { + + _cmsMAT3identity(Dest); + return TRUE; + } + + return _cmsAdaptationMatrix(Dest, NULL, White, cmsD50_XYZ()); + } + } + + return TRUE; +} + + +// Auxiliar, read colorants as a MAT3 structure. Used by any function that needs a matrix-shaper +static +cmsBool ReadICCMatrixRGB2XYZ(cmsMAT3* r, cmsHPROFILE hProfile) +{ + cmsCIEXYZ *PtrRed, *PtrGreen, *PtrBlue; + + _cmsAssert(r != NULL); + + PtrRed = (cmsCIEXYZ *) cmsReadTag(hProfile, cmsSigRedColorantTag); + PtrGreen = (cmsCIEXYZ *) cmsReadTag(hProfile, cmsSigGreenColorantTag); + PtrBlue = (cmsCIEXYZ *) cmsReadTag(hProfile, cmsSigBlueColorantTag); + + if (PtrRed == NULL || PtrGreen == NULL || PtrBlue == NULL) + return FALSE; + + _cmsVEC3init(&r -> v[0], PtrRed -> X, PtrGreen -> X, PtrBlue -> X); + _cmsVEC3init(&r -> v[1], PtrRed -> Y, PtrGreen -> Y, PtrBlue -> Y); + _cmsVEC3init(&r -> v[2], PtrRed -> Z, PtrGreen -> Z, PtrBlue -> Z); + + return TRUE; +} + + +// Gray input pipeline +static +cmsPipeline* BuildGrayInputMatrixPipeline(cmsHPROFILE hProfile) +{ + cmsToneCurve *GrayTRC; + cmsPipeline* Lut; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + GrayTRC = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGrayTRCTag); + if (GrayTRC == NULL) return NULL; + + Lut = cmsPipelineAlloc(ContextID, 1, 3); + if (Lut == NULL) + goto Error; + + if (cmsGetPCS(hProfile) == cmsSigLabData) { + + // In this case we implement the profile as an identity matrix plus 3 tone curves + cmsUInt16Number Zero[2] = { 0x8080, 0x8080 }; + cmsToneCurve* EmptyTab; + cmsToneCurve* LabCurves[3]; + + EmptyTab = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero); + + if (EmptyTab == NULL) + goto Error; + + LabCurves[0] = GrayTRC; + LabCurves[1] = EmptyTab; + LabCurves[2] = EmptyTab; + + if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 1, OneToThreeInputMatrix, NULL)) || + !cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, LabCurves))) { + cmsFreeToneCurve(EmptyTab); + goto Error; + } + + cmsFreeToneCurve(EmptyTab); + + } + else { + + if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 1, &GrayTRC)) || + !cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 1, GrayInputMatrix, NULL))) + goto Error; + } + + return Lut; + +Error: + // memory pointed by GrayTRC is not a new malloc memory, so don't free it here, + // memory pointed by GrayTRC will be freed when hProfile is closed. + // test file :0047776_Pocket Medicine_ The Massachusetts General Hospital Handbook of Internal Medicine-2.pdf + // Xiaochuan Liu, 20140421 + //cmsFreeToneCurve(GrayTRC); + cmsPipelineFree(Lut); + return NULL; +} + +// RGB Matrix shaper +static +cmsPipeline* BuildRGBInputMatrixShaper(cmsHPROFILE hProfile) +{ + cmsPipeline* Lut; + cmsMAT3 Mat; + cmsToneCurve *Shapes[3]; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + int i, j; + + if (!ReadICCMatrixRGB2XYZ(&Mat, hProfile)) return NULL; + + // XYZ PCS in encoded in 1.15 format, and the matrix output comes in 0..0xffff range, so + // we need to adjust the output by a factor of (0x10000/0xffff) to put data in + // a 1.16 range, and then a >> 1 to obtain 1.15. The total factor is (65536.0)/(65535.0*2) + + for (i=0; i < 3; i++) + for (j=0; j < 3; j++) + Mat.v[i].n[j] *= InpAdj; + + + Shapes[0] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigRedTRCTag); + Shapes[1] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGreenTRCTag); + Shapes[2] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigBlueTRCTag); + + if (!Shapes[0] || !Shapes[1] || !Shapes[2]) + return NULL; + + Lut = cmsPipelineAlloc(ContextID, 3, 3); + if (Lut != NULL) { + + if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, Shapes)) || + !cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 3, (cmsFloat64Number*) &Mat, NULL))) + goto Error; + + // Note that it is certainly possible a single profile would have a LUT based + // tag for output working in lab and a matrix-shaper for the fallback cases. + // This is not allowed by the spec, but this code is tolerant to those cases + if (cmsGetPCS(hProfile) == cmsSigLabData) { + + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocXYZ2Lab(ContextID))) + goto Error; + } + + } + + return Lut; + +Error: + cmsPipelineFree(Lut); + return NULL; +} + + + +// Read the DToAX tag, adjusting the encoding of Lab or XYZ if neded +static +cmsPipeline* _cmsReadFloatInputTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat) +{ + cmsContext ContextID = cmsGetProfileContextID(hProfile); + cmsPipeline* Lut = cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat)); + cmsColorSpaceSignature spc = cmsGetColorSpace(hProfile); + cmsColorSpaceSignature PCS = cmsGetPCS(hProfile); + + if (Lut == NULL) return NULL; + + // input and output of transform are in lcms 0..1 encoding. If XYZ or Lab spaces are used, + // these need to be normalized into the appropriate ranges (Lab = 100,0,0, XYZ=1.0,1.0,1.0) + if ( spc == cmsSigLabData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToLabFloat(ContextID))) + goto Error; + } + else if (spc == cmsSigXYZData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToXyzFloat(ContextID))) + goto Error; + } + + if ( PCS == cmsSigLabData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID))) + goto Error; + } + else if( PCS == cmsSigXYZData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID))) + goto Error; + } + + return Lut; + +Error: + cmsPipelineFree(Lut); + return NULL; +} + + +// Read and create a BRAND NEW MPE LUT from a given profile. All stuff dependent of version, etc +// is adjusted here in order to create a LUT that takes care of all those details. +// We add intent = -1 as a way to read matrix shaper always, no matter of other LUT +cmsPipeline* _cmsReadInputLUT(cmsHPROFILE hProfile, int Intent) +{ + cmsTagTypeSignature OriginalType; + cmsTagSignature tag16 = Device2PCS16[Intent]; + cmsTagSignature tagFloat = Device2PCSFloat[Intent]; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + // On named color, take the appropiate tag + if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) { + + cmsPipeline* Lut; + cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) cmsReadTag(hProfile, cmsSigNamedColor2Tag); + + if (nc == NULL) return NULL; + + Lut = cmsPipelineAlloc(ContextID, 0, 0); + if (Lut == NULL) { + cmsFreeNamedColorList(nc); + return NULL; + } + + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocNamedColor(nc, TRUE)) || + !cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) { + cmsPipelineFree(Lut); + return NULL; + } + return Lut; + } + + // This is an attempt to reuse this funtion to retrieve the matrix-shaper as pipeline no + // matter other LUT are present and have precedence. Intent = -1 means just this. + if (Intent != -1) { + + if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence + + // Floating point LUT are always V4, but the encoding range is no + // longer 0..1.0, so we need to add an stage depending on the color space + return _cmsReadFloatInputTag(hProfile, tagFloat); + } + + // Revert to perceptual if no tag is found + if (!cmsIsTag(hProfile, tag16)) { + tag16 = Device2PCS16[0]; + } + + if (cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table? + + // Check profile version and LUT type. Do the necessary adjustments if needed + + // First read the tag + cmsPipeline* Lut = (cmsPipeline*) cmsReadTag(hProfile, tag16); + if (Lut == NULL) return NULL; + + // After reading it, we have now info about the original type + OriginalType = _cmsGetTagTrueType(hProfile, tag16); + + // The profile owns the Lut, so we need to copy it + Lut = cmsPipelineDup(Lut); + + // We need to adjust data only for Lab16 on output + if (OriginalType != cmsSigLut16Type || cmsGetPCS(hProfile) != cmsSigLabData) + return Lut; + + // If the input is Lab, add also a conversion at the begin + if (cmsGetColorSpace(hProfile) == cmsSigLabData && + !cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID))) + goto Error; + + // Add a matrix for conversion V2 to V4 Lab PCS + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) + goto Error; + + return Lut; +Error: + cmsPipelineFree(Lut); + return NULL; + } + } + + // Lut was not found, try to create a matrix-shaper + + // Check if this is a grayscale profile. + if (cmsGetColorSpace(hProfile) == cmsSigGrayData) { + + // if so, build appropiate conversion tables. + // The tables are the PCS iluminant, scaled across GrayTRC + return BuildGrayInputMatrixPipeline(hProfile); + } + + // Not gray, create a normal matrix-shaper + return BuildRGBInputMatrixShaper(hProfile); +} + +// --------------------------------------------------------------------------------------------------------------- + +// Gray output pipeline. +// XYZ -> Gray or Lab -> Gray. Since we only know the GrayTRC, we need to do some assumptions. Gray component will be +// given by Y on XYZ PCS and by L* on Lab PCS, Both across inverse TRC curve. +// The complete pipeline on XYZ is Matrix[3:1] -> Tone curve and in Lab Matrix[3:1] -> Tone Curve as well. + +static +cmsPipeline* BuildGrayOutputPipeline(cmsHPROFILE hProfile) +{ + cmsToneCurve *GrayTRC, *RevGrayTRC; + cmsPipeline* Lut; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + GrayTRC = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGrayTRCTag); + if (GrayTRC == NULL) return NULL; + + RevGrayTRC = cmsReverseToneCurve(GrayTRC); + if (RevGrayTRC == NULL) return NULL; + + Lut = cmsPipelineAlloc(ContextID, 3, 1); + if (Lut == NULL) { + cmsFreeToneCurve(RevGrayTRC); + return NULL; + } + + if (cmsGetPCS(hProfile) == cmsSigLabData) { + + if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickLstarMatrix, NULL))) + goto Error; + } + else { + if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickYMatrix, NULL))) + goto Error; + } + + if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 1, &RevGrayTRC))) + goto Error; + + cmsFreeToneCurve(RevGrayTRC); + return Lut; + +Error: + cmsFreeToneCurve(RevGrayTRC); + cmsPipelineFree(Lut); + return NULL; +} + + +static +cmsPipeline* BuildRGBOutputMatrixShaper(cmsHPROFILE hProfile) +{ + cmsPipeline* Lut; + cmsToneCurve *Shapes[3], *InvShapes[3]; + cmsMAT3 Mat, Inv; + int i, j; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + if (!ReadICCMatrixRGB2XYZ(&Mat, hProfile)) + return NULL; + + if (!_cmsMAT3inverse(&Mat, &Inv)) + return NULL; + + // XYZ PCS in encoded in 1.15 format, and the matrix input should come in 0..0xffff range, so + // we need to adjust the input by a << 1 to obtain a 1.16 fixed and then by a factor of + // (0xffff/0x10000) to put data in 0..0xffff range. Total factor is (2.0*65535.0)/65536.0; + + for (i=0; i < 3; i++) + for (j=0; j < 3; j++) + Inv.v[i].n[j] *= OutpAdj; + + Shapes[0] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigRedTRCTag); + Shapes[1] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGreenTRCTag); + Shapes[2] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigBlueTRCTag); + + if (!Shapes[0] || !Shapes[1] || !Shapes[2]) + return NULL; + + InvShapes[0] = cmsReverseToneCurve(Shapes[0]); + InvShapes[1] = cmsReverseToneCurve(Shapes[1]); + InvShapes[2] = cmsReverseToneCurve(Shapes[2]); + + if (!InvShapes[0] || !InvShapes[1] || !InvShapes[2]) { + return NULL; + } + + Lut = cmsPipelineAlloc(ContextID, 3, 3); + if (Lut != NULL) { + + // Note that it is certainly possible a single profile would have a LUT based + // tag for output working in lab and a matrix-shaper for the fallback cases. + // This is not allowed by the spec, but this code is tolerant to those cases + if (cmsGetPCS(hProfile) == cmsSigLabData) { + + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLab2XYZ(ContextID))) + goto Error; + } + + if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 3, (cmsFloat64Number*) &Inv, NULL)) || + !cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, InvShapes))) + goto Error; + } + + cmsFreeToneCurveTriple(InvShapes); + return Lut; +Error: + cmsFreeToneCurveTriple(InvShapes); + cmsPipelineFree(Lut); + return NULL; +} + + +// Change CLUT interpolation to trilinear +static +void ChangeInterpolationToTrilinear(cmsPipeline* Lut) +{ + cmsStage* Stage; + + for (Stage = cmsPipelineGetPtrToFirstStage(Lut); + Stage != NULL; + Stage = cmsStageNext(Stage)) { + + if (cmsStageType(Stage) == cmsSigCLutElemType) { + + _cmsStageCLutData* CLUT = (_cmsStageCLutData*) Stage ->Data; + + CLUT ->Params->dwFlags |= CMS_LERP_FLAGS_TRILINEAR; + _cmsSetInterpolationRoutine(Lut->ContextID, CLUT ->Params); + } + } +} + + +// Read the DToAX tag, adjusting the encoding of Lab or XYZ if neded +static +cmsPipeline* _cmsReadFloatOutputTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat) +{ + cmsContext ContextID = cmsGetProfileContextID(hProfile); + cmsPipeline* Lut = cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat)); + cmsColorSpaceSignature PCS = cmsGetPCS(hProfile); + cmsColorSpaceSignature dataSpace = cmsGetColorSpace(hProfile); + + if (Lut == NULL) return NULL; + + // If PCS is Lab or XYZ, the floating point tag is accepting data in the space encoding, + // and since the formatter has already accomodated to 0..1.0, we should undo this change + if ( PCS == cmsSigLabData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToLabFloat(ContextID))) + goto Error; + } + else + if (PCS == cmsSigXYZData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToXyzFloat(ContextID))) + goto Error; + } + + // the output can be Lab or XYZ, in which case normalisation is needed on the end of the pipeline + if ( dataSpace == cmsSigLabData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID))) + goto Error; + } + else if (dataSpace == cmsSigXYZData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID))) + goto Error; + } + + return Lut; + +Error: + cmsPipelineFree(Lut); + return NULL; +} + +// Create an output MPE LUT from agiven profile. Version mismatches are handled here +cmsPipeline* _cmsReadOutputLUT(cmsHPROFILE hProfile, int Intent) +{ + cmsTagTypeSignature OriginalType; + cmsTagSignature tag16 = PCS2Device16[Intent]; + cmsTagSignature tagFloat = PCS2DeviceFloat[Intent]; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + + if (Intent != -1) { + + if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence + + // Floating point LUT are always V4 + return _cmsReadFloatOutputTag(hProfile, tagFloat); + } + + // Revert to perceptual if no tag is found + if (!cmsIsTag(hProfile, tag16)) { + tag16 = PCS2Device16[0]; + } + + if (cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table? + + // Check profile version and LUT type. Do the necessary adjustments if needed + + // First read the tag + cmsPipeline* Lut = (cmsPipeline*) cmsReadTag(hProfile, tag16); + if (Lut == NULL) return NULL; + + // After reading it, we have info about the original type + OriginalType = _cmsGetTagTrueType(hProfile, tag16); + + // The profile owns the Lut, so we need to copy it + Lut = cmsPipelineDup(Lut); + if (Lut == NULL) return NULL; + + // Now it is time for a controversial stuff. I found that for 3D LUTS using + // Lab used as indexer space, trilinear interpolation should be used + if (cmsGetPCS(hProfile) == cmsSigLabData) + ChangeInterpolationToTrilinear(Lut); + + // We need to adjust data only for Lab and Lut16 type + if (OriginalType != cmsSigLut16Type || cmsGetPCS(hProfile) != cmsSigLabData) + return Lut; + + // Add a matrix for conversion V4 to V2 Lab PCS + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID))) + goto Error; + + // If the output is Lab, add also a conversion at the end + if (cmsGetColorSpace(hProfile) == cmsSigLabData) + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) + goto Error; + + return Lut; +Error: + cmsPipelineFree(Lut); + return NULL; + } + } + + // Lut not found, try to create a matrix-shaper + + // Check if this is a grayscale profile. + if (cmsGetColorSpace(hProfile) == cmsSigGrayData) { + + // if so, build appropiate conversion tables. + // The tables are the PCS iluminant, scaled across GrayTRC + return BuildGrayOutputPipeline(hProfile); + } + + // Not gray, create a normal matrix-shaper, which only operates in XYZ space + return BuildRGBOutputMatrixShaper(hProfile); +} + +// --------------------------------------------------------------------------------------------------------------- + +// Read the AToD0 tag, adjusting the encoding of Lab or XYZ if neded +static +cmsPipeline* _cmsReadFloatDevicelinkTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat) +{ + cmsContext ContextID = cmsGetProfileContextID(hProfile); + cmsPipeline* Lut = cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat)); + cmsColorSpaceSignature PCS = cmsGetPCS(hProfile); + cmsColorSpaceSignature spc = cmsGetColorSpace(hProfile); + + if (Lut == NULL) return NULL; + + if (spc == cmsSigLabData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToLabFloat(ContextID))) + goto Error; + } + else + if (spc == cmsSigXYZData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToXyzFloat(ContextID))) + goto Error; + } + + if (PCS == cmsSigLabData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID))) + goto Error; + } + else + if (PCS == cmsSigXYZData) + { + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID))) + goto Error; + } + + return Lut; +Error: + cmsPipelineFree(Lut); + return NULL; +} + +// This one includes abstract profiles as well. Matrix-shaper cannot be obtained on that device class. The +// tag name here may default to AToB0 +cmsPipeline* _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, int Intent) +{ + cmsPipeline* Lut; + cmsTagTypeSignature OriginalType; + cmsTagSignature tag16 = Device2PCS16[Intent]; + cmsTagSignature tagFloat = Device2PCSFloat[Intent]; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + + // On named color, take the appropiate tag + if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) { + + cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) cmsReadTag(hProfile, cmsSigNamedColor2Tag); + + if (nc == NULL) return NULL; + + Lut = cmsPipelineAlloc(ContextID, 0, 0); + if (Lut == NULL) + goto Error; + + if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocNamedColor(nc, FALSE))) + goto Error; + + if (cmsGetColorSpace(hProfile) == cmsSigLabData) + if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) + goto Error; + + return Lut; +Error: + cmsPipelineFree(Lut); + cmsFreeNamedColorList(nc); + return NULL; + } + + if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence + + // Floating point LUT are always V + return _cmsReadFloatDevicelinkTag(hProfile, tagFloat); + } + + tagFloat = Device2PCSFloat[0]; + if (cmsIsTag(hProfile, tagFloat)) { + + return cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat)); + } + + if (!cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table? + + tag16 = Device2PCS16[0]; + if (!cmsIsTag(hProfile, tag16)) return NULL; + } + + // Check profile version and LUT type. Do the necessary adjustments if needed + + // Read the tag + Lut = (cmsPipeline*) cmsReadTag(hProfile, tag16); + if (Lut == NULL) return NULL; + + // The profile owns the Lut, so we need to copy it + Lut = cmsPipelineDup(Lut); + if (Lut == NULL) return NULL; + + // Now it is time for a controversial stuff. I found that for 3D LUTS using + // Lab used as indexer space, trilinear interpolation should be used + if (cmsGetPCS(hProfile) == cmsSigLabData) + ChangeInterpolationToTrilinear(Lut); + + // After reading it, we have info about the original type + OriginalType = _cmsGetTagTrueType(hProfile, tag16); + + // We need to adjust data for Lab16 on output + if (OriginalType != cmsSigLut16Type) return Lut; + + // Here it is possible to get Lab on both sides + + if (cmsGetColorSpace(hProfile) == cmsSigLabData) { + if(!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID))) + goto Error2; + } + + if (cmsGetPCS(hProfile) == cmsSigLabData) { + if(!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) + goto Error2; + } + + return Lut; + +Error2: + cmsPipelineFree(Lut); + return NULL; +} + +// --------------------------------------------------------------------------------------------------------------- + +// Returns TRUE if the profile is implemented as matrix-shaper +cmsBool CMSEXPORT cmsIsMatrixShaper(cmsHPROFILE hProfile) +{ + switch (cmsGetColorSpace(hProfile)) { + + case cmsSigGrayData: + + return cmsIsTag(hProfile, cmsSigGrayTRCTag); + + case cmsSigRgbData: + + return (cmsIsTag(hProfile, cmsSigRedColorantTag) && + cmsIsTag(hProfile, cmsSigGreenColorantTag) && + cmsIsTag(hProfile, cmsSigBlueColorantTag) && + cmsIsTag(hProfile, cmsSigRedTRCTag) && + cmsIsTag(hProfile, cmsSigGreenTRCTag) && + cmsIsTag(hProfile, cmsSigBlueTRCTag)); + + default: + + return FALSE; + } +} + +// Returns TRUE if the intent is implemented as CLUT +cmsBool CMSEXPORT cmsIsCLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number UsedDirection) +{ + const cmsTagSignature* TagTable; + + // For devicelinks, the supported intent is that one stated in the header + if (cmsGetDeviceClass(hProfile) == cmsSigLinkClass) { + return (cmsGetHeaderRenderingIntent(hProfile) == Intent); + } + + switch (UsedDirection) { + + case LCMS_USED_AS_INPUT: TagTable = Device2PCS16; break; + case LCMS_USED_AS_OUTPUT:TagTable = PCS2Device16; break; + + // For proofing, we need rel. colorimetric in output. Let's do some recursion + case LCMS_USED_AS_PROOF: + return cmsIsIntentSupported(hProfile, Intent, LCMS_USED_AS_INPUT) && + cmsIsIntentSupported(hProfile, INTENT_RELATIVE_COLORIMETRIC, LCMS_USED_AS_OUTPUT); + + default: + cmsSignalError(cmsGetProfileContextID(hProfile), cmsERROR_RANGE, "Unexpected direction (%d)", UsedDirection); + return FALSE; + } + + return cmsIsTag(hProfile, TagTable[Intent]); + +} + + +// Return info about supported intents +cmsBool CMSEXPORT cmsIsIntentSupported(cmsHPROFILE hProfile, + cmsUInt32Number Intent, cmsUInt32Number UsedDirection) +{ + + if (cmsIsCLUT(hProfile, Intent, UsedDirection)) return TRUE; + + // Is there any matrix-shaper? If so, the intent is supported. This is a bit odd, since V2 matrix shaper + // does not fully support relative colorimetric because they cannot deal with non-zero black points, but + // many profiles claims that, and this is certainly not true for V4 profiles. Lets answer "yes" no matter + // the accuracy would be less than optimal in rel.col and v2 case. + + return cmsIsMatrixShaper(hProfile); +} + + +// --------------------------------------------------------------------------------------------------------------- + +// Read both, profile sequence description and profile sequence id if present. Then combine both to +// create qa unique structure holding both. Shame on ICC to store things in such complicated way. +cmsSEQ* _cmsReadProfileSequence(cmsHPROFILE hProfile) +{ + cmsSEQ* ProfileSeq; + cmsSEQ* ProfileId; + cmsSEQ* NewSeq; + cmsUInt32Number i; + + // Take profile sequence description first + ProfileSeq = (cmsSEQ*) cmsReadTag(hProfile, cmsSigProfileSequenceDescTag); + + // Take profile sequence ID + ProfileId = (cmsSEQ*) cmsReadTag(hProfile, cmsSigProfileSequenceIdTag); + + if (ProfileSeq == NULL && ProfileId == NULL) return NULL; + + if (ProfileSeq == NULL) return cmsDupProfileSequenceDescription(ProfileId); + if (ProfileId == NULL) return cmsDupProfileSequenceDescription(ProfileSeq); + + // We have to mix both together. For that they must agree + if (ProfileSeq ->n != ProfileId ->n) return cmsDupProfileSequenceDescription(ProfileSeq); + + NewSeq = cmsDupProfileSequenceDescription(ProfileSeq); + + // Ok, proceed to the mixing + if (NewSeq != NULL) { + for (i=0; i < ProfileSeq ->n; i++) { + + memmove(&NewSeq ->seq[i].ProfileID, &ProfileId ->seq[i].ProfileID, sizeof(cmsProfileID)); + NewSeq ->seq[i].Description = cmsMLUdup(ProfileId ->seq[i].Description); + } + } + return NewSeq; +} + +// Dump the contents of profile sequence in both tags (if v4 available) +cmsBool _cmsWriteProfileSequence(cmsHPROFILE hProfile, const cmsSEQ* seq) +{ + if (!cmsWriteTag(hProfile, cmsSigProfileSequenceDescTag, seq)) return FALSE; + + if (cmsGetProfileVersion(hProfile) >= 4.0) { + + if (!cmsWriteTag(hProfile, cmsSigProfileSequenceIdTag, seq)) return FALSE; + } + + return TRUE; +} + + +// Auxiliar, read and duplicate a MLU if found. +static +cmsMLU* GetMLUFromProfile(cmsHPROFILE h, cmsTagSignature sig) +{ + cmsMLU* mlu = (cmsMLU*) cmsReadTag(h, sig); + if (mlu == NULL) return NULL; + + return cmsMLUdup(mlu); +} + +// Create a sequence description out of an array of profiles +cmsSEQ* _cmsCompileProfileSequence(cmsContext ContextID, cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[]) +{ + cmsUInt32Number i; + cmsSEQ* seq = cmsAllocProfileSequenceDescription(ContextID, nProfiles); + + if (seq == NULL) return NULL; + + for (i=0; i < nProfiles; i++) { + + cmsPSEQDESC* ps = &seq ->seq[i]; + cmsHPROFILE h = hProfiles[i]; + cmsTechnologySignature* techpt; + + cmsGetHeaderAttributes(h, &ps ->attributes); + cmsGetHeaderProfileID(h, ps ->ProfileID.ID8); + ps ->deviceMfg = cmsGetHeaderManufacturer(h); + ps ->deviceModel = cmsGetHeaderModel(h); + + techpt = (cmsTechnologySignature*) cmsReadTag(h, cmsSigTechnologyTag); + if (techpt == NULL) + ps ->technology = (cmsTechnologySignature) 0; + else + ps ->technology = *techpt; + + ps ->Manufacturer = GetMLUFromProfile(h, cmsSigDeviceMfgDescTag); + ps ->Model = GetMLUFromProfile(h, cmsSigDeviceModelDescTag); + ps ->Description = GetMLUFromProfile(h, cmsSigProfileDescriptionTag); + + } + + return seq; +} + +// ------------------------------------------------------------------------------------------------------------------- + + +static +const cmsMLU* GetInfo(cmsHPROFILE hProfile, cmsInfoType Info) +{ + cmsTagSignature sig; + + switch (Info) { + + case cmsInfoDescription: + sig = cmsSigProfileDescriptionTag; + break; + + case cmsInfoManufacturer: + sig = cmsSigDeviceMfgDescTag; + break; + + case cmsInfoModel: + sig = cmsSigDeviceModelDescTag; + break; + + case cmsInfoCopyright: + sig = cmsSigCopyrightTag; + break; + + default: return NULL; + } + + + return (cmsMLU*) cmsReadTag(hProfile, sig); +} + + + +cmsUInt32Number CMSEXPORT cmsGetProfileInfo(cmsHPROFILE hProfile, cmsInfoType Info, + const char LanguageCode[3], const char CountryCode[3], + wchar_t* Buffer, cmsUInt32Number BufferSize) +{ + const cmsMLU* mlu = GetInfo(hProfile, Info); + if (mlu == NULL) return 0; + + return cmsMLUgetWide(mlu, LanguageCode, CountryCode, Buffer, BufferSize); +} + + +cmsUInt32Number CMSEXPORT cmsGetProfileInfoASCII(cmsHPROFILE hProfile, cmsInfoType Info, + const char LanguageCode[3], const char CountryCode[3], + char* Buffer, cmsUInt32Number BufferSize) +{ + const cmsMLU* mlu = GetInfo(hProfile, Info); + if (mlu == NULL) return 0; + + return cmsMLUgetASCII(mlu, LanguageCode, CountryCode, Buffer, BufferSize); +} diff --git a/third_party/lcms/src/cmslut.c b/third_party/lcms/src/cmslut.c new file mode 100644 index 0000000000..19d43361f0 --- /dev/null +++ b/third_party/lcms/src/cmslut.c @@ -0,0 +1,1820 @@ +//--------------------------------------------------------------------------------- +// +// 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" + + +// Allocates an empty multi profile element +cmsStage* CMSEXPORT _cmsStageAllocPlaceholder(cmsContext ContextID, + cmsStageSignature Type, + cmsUInt32Number InputChannels, + cmsUInt32Number OutputChannels, + _cmsStageEvalFn EvalPtr, + _cmsStageDupElemFn DupElemPtr, + _cmsStageFreeElemFn FreePtr, + void* Data) +{ + cmsStage* ph = (cmsStage*) _cmsMallocZero(ContextID, sizeof(cmsStage)); + + if (ph == NULL) return NULL; + + + ph ->ContextID = ContextID; + + ph ->Type = Type; + ph ->Implements = Type; // By default, no clue on what is implementing + + ph ->InputChannels = InputChannels; + ph ->OutputChannels = OutputChannels; + ph ->EvalPtr = EvalPtr; + ph ->DupElemPtr = DupElemPtr; + ph ->FreePtr = FreePtr; + ph ->Data = Data; + + return ph; +} + + +static +void EvaluateIdentity(const cmsFloat32Number In[], + cmsFloat32Number Out[], + const cmsStage *mpe) +{ + memmove(Out, In, mpe ->InputChannels * sizeof(cmsFloat32Number)); +} + + +cmsStage* CMSEXPORT cmsStageAllocIdentity(cmsContext ContextID, cmsUInt32Number nChannels) +{ + return _cmsStageAllocPlaceholder(ContextID, + cmsSigIdentityElemType, + nChannels, nChannels, + EvaluateIdentity, + NULL, + NULL, + NULL); + } + +// Conversion functions. From floating point to 16 bits +static +void FromFloatTo16(const cmsFloat32Number In[], cmsUInt16Number Out[], cmsUInt32Number n) +{ + cmsUInt32Number i; + + for (i=0; i < n; i++) { + Out[i] = _cmsQuickSaturateWord(In[i] * 65535.0); + } +} + +// From 16 bits to floating point +static +void From16ToFloat(const cmsUInt16Number In[], cmsFloat32Number Out[], cmsUInt32Number n) +{ + cmsUInt32Number i; + + for (i=0; i < n; i++) { + Out[i] = (cmsFloat32Number) In[i] / 65535.0F; + } +} + + +// This function is quite useful to analyze the structure of a LUT and retrieve the MPE elements +// that conform the LUT. It should be called with the LUT, the number of expected elements and +// then a list of expected types followed with a list of cmsFloat64Number pointers to MPE elements. If +// the function founds a match with current pipeline, it fills the pointers and returns TRUE +// if not, returns FALSE without touching anything. Setting pointers to NULL does bypass +// the storage process. +cmsBool CMSEXPORT cmsPipelineCheckAndRetreiveStages(const cmsPipeline* Lut, cmsUInt32Number n, ...) +{ + va_list args; + cmsUInt32Number i; + cmsStage* mpe; + cmsStageSignature Type; + void** ElemPtr; + + // Make sure same number of elements + if (cmsPipelineStageCount(Lut) != n) return FALSE; + + va_start(args, n); + + // Iterate across asked types + mpe = Lut ->Elements; + for (i=0; i < n; i++) { + + // Get asked type + Type = (cmsStageSignature)va_arg(args, cmsStageSignature); + if (mpe ->Type != Type) { + + va_end(args); // Mismatch. We are done. + return FALSE; + } + mpe = mpe ->Next; + } + + // Found a combination, fill pointers if not NULL + mpe = Lut ->Elements; + for (i=0; i < n; i++) { + + ElemPtr = va_arg(args, void**); + if (ElemPtr != NULL) + *ElemPtr = mpe; + + mpe = mpe ->Next; + } + + va_end(args); + return TRUE; +} + +// Below there are implementations for several types of elements. Each type may be implemented by a +// evaluation function, a duplication function, a function to free resources and a constructor. + +// ************************************************************************************************* +// Type cmsSigCurveSetElemType (curves) +// ************************************************************************************************* + +cmsToneCurve** _cmsStageGetPtrToCurveSet(const cmsStage* mpe) +{ + _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) mpe ->Data; + + return Data ->TheCurves; +} + +static +void EvaluateCurves(const cmsFloat32Number In[], + cmsFloat32Number Out[], + const cmsStage *mpe) +{ + _cmsStageToneCurvesData* Data; + cmsUInt32Number i; + + _cmsAssert(mpe != NULL); + + Data = (_cmsStageToneCurvesData*) mpe ->Data; + if (Data == NULL) return; + + if (Data ->TheCurves == NULL) return; + + for (i=0; i < Data ->nCurves; i++) { + Out[i] = cmsEvalToneCurveFloat(Data ->TheCurves[i], In[i]); + } +} + +static +void CurveSetElemTypeFree(cmsStage* mpe) +{ + _cmsStageToneCurvesData* Data; + cmsUInt32Number i; + + _cmsAssert(mpe != NULL); + + Data = (_cmsStageToneCurvesData*) mpe ->Data; + if (Data == NULL) return; + + if (Data ->TheCurves != NULL) { + for (i=0; i < Data ->nCurves; i++) { + if (Data ->TheCurves[i] != NULL) + cmsFreeToneCurve(Data ->TheCurves[i]); + } + } + _cmsFree(mpe ->ContextID, Data ->TheCurves); + _cmsFree(mpe ->ContextID, Data); +} + + +static +void* CurveSetDup(cmsStage* mpe) +{ + _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) mpe ->Data; + _cmsStageToneCurvesData* NewElem; + cmsUInt32Number i; + + NewElem = (_cmsStageToneCurvesData*) _cmsMallocZero(mpe ->ContextID, sizeof(_cmsStageToneCurvesData)); + if (NewElem == NULL) return NULL; + + NewElem ->nCurves = Data ->nCurves; + NewElem ->TheCurves = (cmsToneCurve**) _cmsCalloc(mpe ->ContextID, NewElem ->nCurves, sizeof(cmsToneCurve*)); + + if (NewElem ->TheCurves == NULL) goto Error; + + for (i=0; i < NewElem ->nCurves; i++) { + + // Duplicate each curve. It may fail. + NewElem ->TheCurves[i] = cmsDupToneCurve(Data ->TheCurves[i]); + if (NewElem ->TheCurves[i] == NULL) goto Error; + + + } + return (void*) NewElem; + +Error: + + if (NewElem ->TheCurves != NULL) { + for (i=0; i < NewElem ->nCurves; i++) { + if (NewElem ->TheCurves[i]) + cmsFreeToneCurve(NewElem ->TheCurves[i]); + } + } + _cmsFree(mpe ->ContextID, NewElem ->TheCurves); + _cmsFree(mpe ->ContextID, NewElem); + return NULL; +} + + +// Curves == NULL forces identity curves +cmsStage* CMSEXPORT cmsStageAllocToneCurves(cmsContext ContextID, cmsUInt32Number nChannels, cmsToneCurve* const Curves[]) +{ + cmsUInt32Number i; + _cmsStageToneCurvesData* NewElem; + cmsStage* NewMPE; + + + NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigCurveSetElemType, nChannels, nChannels, + EvaluateCurves, CurveSetDup, CurveSetElemTypeFree, NULL ); + if (NewMPE == NULL) return NULL; + + NewElem = (_cmsStageToneCurvesData*) _cmsMallocZero(ContextID, sizeof(_cmsStageToneCurvesData)); + if (NewElem == NULL) { + cmsStageFree(NewMPE); + return NULL; + } + + NewMPE ->Data = (void*) NewElem; + + NewElem ->nCurves = nChannels; + NewElem ->TheCurves = (cmsToneCurve**) _cmsCalloc(ContextID, nChannels, sizeof(cmsToneCurve*)); + if (NewElem ->TheCurves == NULL) { + cmsStageFree(NewMPE); + return NULL; + } + + for (i=0; i < nChannels; i++) { + + if (Curves == NULL) { + NewElem ->TheCurves[i] = cmsBuildGamma(ContextID, 1.0); + } + else { + NewElem ->TheCurves[i] = cmsDupToneCurve(Curves[i]); + } + + if (NewElem ->TheCurves[i] == NULL) { + cmsStageFree(NewMPE); + return NULL; + } + + } + + return NewMPE; +} + + +// Create a bunch of identity curves +cmsStage* _cmsStageAllocIdentityCurves(cmsContext ContextID, int nChannels) +{ + cmsStage* mpe = cmsStageAllocToneCurves(ContextID, nChannels, NULL); + + if (mpe == NULL) return NULL; + mpe ->Implements = cmsSigIdentityElemType; + return mpe; +} + + +// ************************************************************************************************* +// Type cmsSigMatrixElemType (Matrices) +// ************************************************************************************************* + + +// Special care should be taken here because precision loss. A temporary cmsFloat64Number buffer is being used +static +void EvaluateMatrix(const cmsFloat32Number In[], + cmsFloat32Number Out[], + const cmsStage *mpe) +{ + cmsUInt32Number i, j; + _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data; + cmsFloat64Number Tmp; + + // Input is already in 0..1.0 notation + for (i=0; i < mpe ->OutputChannels; i++) { + + Tmp = 0; + for (j=0; j < mpe->InputChannels; j++) { + Tmp += In[j] * Data->Double[i*mpe->InputChannels + j]; + } + + if (Data ->Offset != NULL) + Tmp += Data->Offset[i]; + + Out[i] = (cmsFloat32Number) Tmp; + } + + + // Output in 0..1.0 domain +} + + +// Duplicate a yet-existing matrix element +static +void* MatrixElemDup(cmsStage* mpe) +{ + _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data; + _cmsStageMatrixData* NewElem; + cmsUInt32Number sz; + + NewElem = (_cmsStageMatrixData*) _cmsMallocZero(mpe ->ContextID, sizeof(_cmsStageMatrixData)); + if (NewElem == NULL) return NULL; + + sz = mpe ->InputChannels * mpe ->OutputChannels; + + NewElem ->Double = (cmsFloat64Number*) _cmsDupMem(mpe ->ContextID, Data ->Double, sz * sizeof(cmsFloat64Number)) ; + + if (Data ->Offset) + NewElem ->Offset = (cmsFloat64Number*) _cmsDupMem(mpe ->ContextID, + Data ->Offset, mpe -> OutputChannels * sizeof(cmsFloat64Number)) ; + + return (void*) NewElem; +} + + +static +void MatrixElemTypeFree(cmsStage* mpe) +{ + _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data; + if (Data == NULL) + return; + if (Data ->Double) + _cmsFree(mpe ->ContextID, Data ->Double); + + if (Data ->Offset) + _cmsFree(mpe ->ContextID, Data ->Offset); + + _cmsFree(mpe ->ContextID, mpe ->Data); +} + + + +cmsStage* CMSEXPORT cmsStageAllocMatrix(cmsContext ContextID, cmsUInt32Number Rows, cmsUInt32Number Cols, + const cmsFloat64Number* Matrix, const cmsFloat64Number* Offset) +{ + cmsUInt32Number i, n; + _cmsStageMatrixData* NewElem; + cmsStage* NewMPE; + + n = Rows * Cols; + + // Check for overflow + if (n == 0) return NULL; + if (n >= UINT_MAX / Cols) return NULL; + if (n >= UINT_MAX / Rows) return NULL; + if (n < Rows || n < Cols) return NULL; + + NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigMatrixElemType, Cols, Rows, + EvaluateMatrix, MatrixElemDup, MatrixElemTypeFree, NULL ); + if (NewMPE == NULL) return NULL; + + + NewElem = (_cmsStageMatrixData*) _cmsMallocZero(ContextID, sizeof(_cmsStageMatrixData)); + if (NewElem == NULL) return NULL; + + + NewElem ->Double = (cmsFloat64Number*) _cmsCalloc(ContextID, n, sizeof(cmsFloat64Number)); + + if (NewElem->Double == NULL) { + MatrixElemTypeFree(NewMPE); + return NULL; + } + + for (i=0; i < n; i++) { + NewElem ->Double[i] = Matrix[i]; + } + + + if (Offset != NULL) { + + NewElem ->Offset = (cmsFloat64Number*) _cmsCalloc(ContextID, Rows, sizeof(cmsFloat64Number)); + if (NewElem->Offset == NULL) { + MatrixElemTypeFree(NewMPE); + return NULL; + } + + for (i=0; i < Rows; i++) { + NewElem ->Offset[i] = Offset[i]; + } + + } + + NewMPE ->Data = (void*) NewElem; + return NewMPE; +} + + +// ************************************************************************************************* +// Type cmsSigCLutElemType +// ************************************************************************************************* + + +// Evaluate in true floating point +static +void EvaluateCLUTfloat(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) +{ + _cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data; + + Data -> Params ->Interpolation.LerpFloat(In, Out, Data->Params); +} + + +// Convert to 16 bits, evaluate, and back to floating point +static +void EvaluateCLUTfloatIn16(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) +{ + _cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data; + cmsUInt16Number In16[MAX_STAGE_CHANNELS], Out16[MAX_STAGE_CHANNELS]; + + _cmsAssert(mpe ->InputChannels <= MAX_STAGE_CHANNELS); + _cmsAssert(mpe ->OutputChannels <= MAX_STAGE_CHANNELS); + + FromFloatTo16(In, In16, mpe ->InputChannels); + Data -> Params ->Interpolation.Lerp16(In16, Out16, Data->Params); + From16ToFloat(Out16, Out, mpe ->OutputChannels); +} + + +// Given an hypercube of b dimensions, with Dims[] number of nodes by dimension, calculate the total amount of nodes +static +cmsUInt32Number CubeSize(const cmsUInt32Number Dims[], cmsUInt32Number b) +{ + cmsUInt32Number rv, dim; + + _cmsAssert(Dims != NULL); + + for (rv = 1; b > 0; b--) { + + dim = Dims[b-1]; + if (dim == 0) return 0; // Error + + rv *= dim; + + // Check for overflow + if (rv > UINT_MAX / dim) return 0; + } + + return rv; +} + +static +void* CLUTElemDup(cmsStage* mpe) +{ + _cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data; + _cmsStageCLutData* NewElem; + + + NewElem = (_cmsStageCLutData*) _cmsMallocZero(mpe ->ContextID, sizeof(_cmsStageCLutData)); + if (NewElem == NULL) return NULL; + + NewElem ->nEntries = Data ->nEntries; + NewElem ->HasFloatValues = Data ->HasFloatValues; + + if (Data ->Tab.T) { + + if (Data ->HasFloatValues) { + NewElem ->Tab.TFloat = (cmsFloat32Number*) _cmsDupMem(mpe ->ContextID, Data ->Tab.TFloat, Data ->nEntries * sizeof (cmsFloat32Number)); + if (NewElem ->Tab.TFloat == NULL) + goto Error; + } else { + NewElem ->Tab.T = (cmsUInt16Number*) _cmsDupMem(mpe ->ContextID, Data ->Tab.T, Data ->nEntries * sizeof (cmsUInt16Number)); + if (NewElem ->Tab.TFloat == NULL) + goto Error; + } + } + + NewElem ->Params = _cmsComputeInterpParamsEx(mpe ->ContextID, + Data ->Params ->nSamples, + Data ->Params ->nInputs, + Data ->Params ->nOutputs, + NewElem ->Tab.T, + Data ->Params ->dwFlags); + if (NewElem->Params != NULL) + return (void*) NewElem; + Error: + if (NewElem->Tab.T) + // This works for both types + _cmsFree(mpe ->ContextID, NewElem -> Tab.T); + _cmsFree(mpe ->ContextID, NewElem); + return NULL; +} + + +static +void CLutElemTypeFree(cmsStage* mpe) +{ + + _cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data; + + // Already empty + if (Data == NULL) return; + + // This works for both types + if (Data -> Tab.T) + _cmsFree(mpe ->ContextID, Data -> Tab.T); + + _cmsFreeInterpParams(Data ->Params); + _cmsFree(mpe ->ContextID, mpe ->Data); +} + + +// Allocates a 16-bit multidimensional CLUT. This is evaluated at 16-bit precision. Table may have different +// granularity on each dimension. +cmsStage* CMSEXPORT cmsStageAllocCLut16bitGranular(cmsContext ContextID, + const cmsUInt32Number clutPoints[], + cmsUInt32Number inputChan, + cmsUInt32Number outputChan, + const cmsUInt16Number* Table) +{ + cmsUInt32Number i, n; + _cmsStageCLutData* NewElem; + cmsStage* NewMPE; + + _cmsAssert(clutPoints != NULL); + + if (inputChan > MAX_INPUT_DIMENSIONS) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Too many input channels (%d channels, max=%d)", inputChan, MAX_INPUT_DIMENSIONS); + return NULL; + } + + NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigCLutElemType, inputChan, outputChan, + EvaluateCLUTfloatIn16, CLUTElemDup, CLutElemTypeFree, NULL ); + + if (NewMPE == NULL) return NULL; + + NewElem = (_cmsStageCLutData*) _cmsMallocZero(ContextID, sizeof(_cmsStageCLutData)); + if (NewElem == NULL) { + cmsStageFree(NewMPE); + return NULL; + } + + NewMPE ->Data = (void*) NewElem; + + NewElem -> nEntries = n = outputChan * CubeSize(clutPoints, inputChan); + NewElem -> HasFloatValues = FALSE; + + if (n == 0) { + cmsStageFree(NewMPE); + return NULL; + } + + + NewElem ->Tab.T = (cmsUInt16Number*) _cmsCalloc(ContextID, n, sizeof(cmsUInt16Number)); + if (NewElem ->Tab.T == NULL) { + cmsStageFree(NewMPE); + return NULL; + } + + if (Table != NULL) { + for (i=0; i < n; i++) { + NewElem ->Tab.T[i] = Table[i]; + } + } + + NewElem ->Params = _cmsComputeInterpParamsEx(ContextID, clutPoints, inputChan, outputChan, NewElem ->Tab.T, CMS_LERP_FLAGS_16BITS); + if (NewElem ->Params == NULL) { + cmsStageFree(NewMPE); + return NULL; + } + + return NewMPE; +} + +cmsStage* CMSEXPORT cmsStageAllocCLut16bit(cmsContext ContextID, + cmsUInt32Number nGridPoints, + cmsUInt32Number inputChan, + cmsUInt32Number outputChan, + const cmsUInt16Number* Table) +{ + cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS]; + int i; + + // Our resulting LUT would be same gridpoints on all dimensions + for (i=0; i < MAX_INPUT_DIMENSIONS; i++) + Dimensions[i] = nGridPoints; + + return cmsStageAllocCLut16bitGranular(ContextID, Dimensions, inputChan, outputChan, Table); +} + + +cmsStage* CMSEXPORT cmsStageAllocCLutFloat(cmsContext ContextID, + cmsUInt32Number nGridPoints, + cmsUInt32Number inputChan, + cmsUInt32Number outputChan, + const cmsFloat32Number* Table) +{ + cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS]; + int i; + + // Our resulting LUT would be same gridpoints on all dimensions + for (i=0; i < MAX_INPUT_DIMENSIONS; i++) + Dimensions[i] = nGridPoints; + + return cmsStageAllocCLutFloatGranular(ContextID, Dimensions, inputChan, outputChan, Table); +} + + + +cmsStage* CMSEXPORT cmsStageAllocCLutFloatGranular(cmsContext ContextID, const cmsUInt32Number clutPoints[], cmsUInt32Number inputChan, cmsUInt32Number outputChan, const cmsFloat32Number* Table) +{ + cmsUInt32Number i, n; + _cmsStageCLutData* NewElem; + cmsStage* NewMPE; + + _cmsAssert(clutPoints != NULL); + + if (inputChan > MAX_INPUT_DIMENSIONS) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Too many input channels (%d channels, max=%d)", inputChan, MAX_INPUT_DIMENSIONS); + return NULL; + } + + NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigCLutElemType, inputChan, outputChan, + EvaluateCLUTfloat, CLUTElemDup, CLutElemTypeFree, NULL); + if (NewMPE == NULL) return NULL; + + + NewElem = (_cmsStageCLutData*) _cmsMallocZero(ContextID, sizeof(_cmsStageCLutData)); + if (NewElem == NULL) { + cmsStageFree(NewMPE); + return NULL; + } + + NewMPE ->Data = (void*) NewElem; + + // There is a potential integer overflow on conputing n and nEntries. + NewElem -> nEntries = n = outputChan * CubeSize(clutPoints, inputChan); + NewElem -> HasFloatValues = TRUE; + + if (n == 0) { + cmsStageFree(NewMPE); + return NULL; + } + + NewElem ->Tab.TFloat = (cmsFloat32Number*) _cmsCalloc(ContextID, n, sizeof(cmsFloat32Number)); + if (NewElem ->Tab.TFloat == NULL) { + cmsStageFree(NewMPE); + return NULL; + } + + if (Table != NULL) { + for (i=0; i < n; i++) { + NewElem ->Tab.TFloat[i] = Table[i]; + } + } + + NewElem ->Params = _cmsComputeInterpParamsEx(ContextID, clutPoints, inputChan, outputChan, NewElem ->Tab.TFloat, CMS_LERP_FLAGS_FLOAT); + if (NewElem ->Params == NULL) { + cmsStageFree(NewMPE); + return NULL; + } + + return NewMPE; +} + + +static +int IdentitySampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void * Cargo) +{ + int nChan = *(int*) Cargo; + int i; + + for (i=0; i < nChan; i++) + Out[i] = In[i]; + + return 1; +} + +// Creates an MPE that just copies input to output +cmsStage* _cmsStageAllocIdentityCLut(cmsContext ContextID, int nChan) +{ + cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS]; + cmsStage* mpe ; + int i; + + for (i=0; i < MAX_INPUT_DIMENSIONS; i++) + Dimensions[i] = 2; + + mpe = cmsStageAllocCLut16bitGranular(ContextID, Dimensions, nChan, nChan, NULL); + if (mpe == NULL) return NULL; + + if (!cmsStageSampleCLut16bit(mpe, IdentitySampler, &nChan, 0)) { + cmsStageFree(mpe); + return NULL; + } + + mpe ->Implements = cmsSigIdentityElemType; + return mpe; +} + + + +// Quantize a value 0 <= i < MaxSamples to 0..0xffff +cmsUInt16Number _cmsQuantizeVal(cmsFloat64Number i, int MaxSamples) +{ + cmsFloat64Number x; + + x = ((cmsFloat64Number) i * 65535.) / (cmsFloat64Number) (MaxSamples - 1); + return _cmsQuickSaturateWord(x); +} + + +// This routine does a sweep on whole input space, and calls its callback +// function on knots. returns TRUE if all ok, FALSE otherwise. +cmsBool CMSEXPORT cmsStageSampleCLut16bit(cmsStage* mpe, cmsSAMPLER16 Sampler, void * Cargo, cmsUInt32Number dwFlags) +{ + int i, t, nTotalPoints, index, rest; + int nInputs, nOutputs; + cmsUInt32Number* nSamples; + cmsUInt16Number In[MAX_INPUT_DIMENSIONS+1], Out[MAX_STAGE_CHANNELS]; + _cmsStageCLutData* clut; + + if (mpe == NULL) return FALSE; + + clut = (_cmsStageCLutData*) mpe->Data; + + if (clut == NULL) return FALSE; + + nSamples = clut->Params ->nSamples; + nInputs = clut->Params ->nInputs; + nOutputs = clut->Params ->nOutputs; + + if (nInputs <= 0) return FALSE; + if (nOutputs <= 0) return FALSE; + if (nInputs > MAX_INPUT_DIMENSIONS) return FALSE; + if (nOutputs >= MAX_STAGE_CHANNELS) return FALSE; + + nTotalPoints = CubeSize(nSamples, nInputs); + if (nTotalPoints == 0) return FALSE; + + index = 0; + for (i = 0; i < nTotalPoints; i++) { + + rest = i; + for (t = nInputs-1; t >=0; --t) { + + cmsUInt32Number Colorant = rest % nSamples[t]; + + rest /= nSamples[t]; + + In[t] = _cmsQuantizeVal(Colorant, nSamples[t]); + } + + if (clut ->Tab.T != NULL) { + for (t=0; t < nOutputs; t++) + Out[t] = clut->Tab.T[index + t]; + } + + if (!Sampler(In, Out, Cargo)) + return FALSE; + + if (!(dwFlags & SAMPLER_INSPECT)) { + + if (clut ->Tab.T != NULL) { + for (t=0; t < nOutputs; t++) + clut->Tab.T[index + t] = Out[t]; + } + } + + index += nOutputs; + } + + return TRUE; +} + +// Same as anterior, but for floting point +cmsBool CMSEXPORT cmsStageSampleCLutFloat(cmsStage* mpe, cmsSAMPLERFLOAT Sampler, void * Cargo, cmsUInt32Number dwFlags) +{ + int i, t, nTotalPoints, index, rest; + int nInputs, nOutputs; + cmsUInt32Number* nSamples; + cmsFloat32Number In[MAX_INPUT_DIMENSIONS+1], Out[MAX_STAGE_CHANNELS]; + _cmsStageCLutData* clut = (_cmsStageCLutData*) mpe->Data; + + nSamples = clut->Params ->nSamples; + nInputs = clut->Params ->nInputs; + nOutputs = clut->Params ->nOutputs; + + if (nInputs <= 0) return FALSE; + if (nOutputs <= 0) return FALSE; + if (nInputs > MAX_INPUT_DIMENSIONS) return FALSE; + if (nOutputs >= MAX_STAGE_CHANNELS) return FALSE; + + nTotalPoints = CubeSize(nSamples, nInputs); + if (nTotalPoints == 0) return FALSE; + + index = 0; + for (i = 0; i < nTotalPoints; i++) { + + rest = i; + for (t = nInputs-1; t >=0; --t) { + + cmsUInt32Number Colorant = rest % nSamples[t]; + + rest /= nSamples[t]; + + In[t] = (cmsFloat32Number) (_cmsQuantizeVal(Colorant, nSamples[t]) / 65535.0); + } + + if (clut ->Tab.TFloat != NULL) { + for (t=0; t < nOutputs; t++) + Out[t] = clut->Tab.TFloat[index + t]; + } + + if (!Sampler(In, Out, Cargo)) + return FALSE; + + if (!(dwFlags & SAMPLER_INSPECT)) { + + if (clut ->Tab.TFloat != NULL) { + for (t=0; t < nOutputs; t++) + clut->Tab.TFloat[index + t] = Out[t]; + } + } + + index += nOutputs; + } + + return TRUE; +} + + + +// This routine does a sweep on whole input space, and calls its callback +// function on knots. returns TRUE if all ok, FALSE otherwise. +cmsBool CMSEXPORT cmsSliceSpace16(cmsUInt32Number nInputs, const cmsUInt32Number clutPoints[], + cmsSAMPLER16 Sampler, void * Cargo) +{ + int i, t, nTotalPoints, rest; + cmsUInt16Number In[cmsMAXCHANNELS]; + + if (nInputs >= cmsMAXCHANNELS) return FALSE; + + nTotalPoints = CubeSize(clutPoints, nInputs); + if (nTotalPoints == 0) return FALSE; + + for (i = 0; i < nTotalPoints; i++) { + + rest = i; + for (t = nInputs-1; t >=0; --t) { + + cmsUInt32Number Colorant = rest % clutPoints[t]; + + rest /= clutPoints[t]; + In[t] = _cmsQuantizeVal(Colorant, clutPoints[t]); + + } + + if (!Sampler(In, NULL, Cargo)) + return FALSE; + } + + return TRUE; +} + +cmsInt32Number CMSEXPORT cmsSliceSpaceFloat(cmsUInt32Number nInputs, const cmsUInt32Number clutPoints[], + cmsSAMPLERFLOAT Sampler, void * Cargo) +{ + int i, t, nTotalPoints, rest; + cmsFloat32Number In[cmsMAXCHANNELS]; + + if (nInputs >= cmsMAXCHANNELS) return FALSE; + + nTotalPoints = CubeSize(clutPoints, nInputs); + if (nTotalPoints == 0) return FALSE; + + for (i = 0; i < nTotalPoints; i++) { + + rest = i; + for (t = nInputs-1; t >=0; --t) { + + cmsUInt32Number Colorant = rest % clutPoints[t]; + + rest /= clutPoints[t]; + In[t] = (cmsFloat32Number) (_cmsQuantizeVal(Colorant, clutPoints[t]) / 65535.0); + + } + + if (!Sampler(In, NULL, Cargo)) + return FALSE; + } + + return TRUE; +} + +// ******************************************************************************** +// Type cmsSigLab2XYZElemType +// ******************************************************************************** + + +static +void EvaluateLab2XYZ(const cmsFloat32Number In[], + cmsFloat32Number Out[], + const cmsStage *mpe) +{ + cmsCIELab Lab; + cmsCIEXYZ XYZ; + const cmsFloat64Number XYZadj = MAX_ENCODEABLE_XYZ; + + // V4 rules + Lab.L = In[0] * 100.0; + Lab.a = In[1] * 255.0 - 128.0; + Lab.b = In[2] * 255.0 - 128.0; + + cmsLab2XYZ(NULL, &XYZ, &Lab); + + // From XYZ, range 0..19997 to 0..1.0, note that 1.99997 comes from 0xffff + // encoded as 1.15 fixed point, so 1 + (32767.0 / 32768.0) + + Out[0] = (cmsFloat32Number) ((cmsFloat64Number) XYZ.X / XYZadj); + Out[1] = (cmsFloat32Number) ((cmsFloat64Number) XYZ.Y / XYZadj); + Out[2] = (cmsFloat32Number) ((cmsFloat64Number) XYZ.Z / XYZadj); + return; + + cmsUNUSED_PARAMETER(mpe); +} + + +// No dup or free routines needed, as the structure has no pointers in it. +cmsStage* _cmsStageAllocLab2XYZ(cmsContext ContextID) +{ + return _cmsStageAllocPlaceholder(ContextID, cmsSigLab2XYZElemType, 3, 3, EvaluateLab2XYZ, NULL, NULL, NULL); +} + +// ******************************************************************************** + +// v2 L=100 is supposed to be placed on 0xFF00. There is no reasonable +// number of gridpoints that would make exact match. However, a prelinearization +// of 258 entries, would map 0xFF00 exactly on entry 257, and this is good to avoid scum dot. +// Almost all what we need but unfortunately, the rest of entries should be scaled by +// (255*257/256) and this is not exact. + +cmsStage* _cmsStageAllocLabV2ToV4curves(cmsContext ContextID) +{ + cmsStage* mpe; + cmsToneCurve* LabTable[3]; + int i, j; + + LabTable[0] = cmsBuildTabulatedToneCurve16(ContextID, 258, NULL); + LabTable[1] = cmsBuildTabulatedToneCurve16(ContextID, 258, NULL); + LabTable[2] = cmsBuildTabulatedToneCurve16(ContextID, 258, NULL); + + for (j=0; j < 3; j++) { + + if (LabTable[j] == NULL) { + cmsFreeToneCurveTriple(LabTable); + return NULL; + } + + // We need to map * (0xffff / 0xff00), thats same as (257 / 256) + // So we can use 258-entry tables to do the trick (i / 257) * (255 * 257) * (257 / 256); + for (i=0; i < 257; i++) { + + LabTable[j]->Table16[i] = (cmsUInt16Number) ((i * 0xffff + 0x80) >> 8); + } + + LabTable[j] ->Table16[257] = 0xffff; + } + + mpe = cmsStageAllocToneCurves(ContextID, 3, LabTable); + cmsFreeToneCurveTriple(LabTable); + + if (mpe == NULL) return NULL; + mpe ->Implements = cmsSigLabV2toV4; + return mpe; +} + +// ******************************************************************************** + +// Matrix-based conversion, which is more accurate, but slower and cannot properly be saved in devicelink profiles +cmsStage* _cmsStageAllocLabV2ToV4(cmsContext ContextID) +{ + static const cmsFloat64Number V2ToV4[] = { 65535.0/65280.0, 0, 0, + 0, 65535.0/65280.0, 0, + 0, 0, 65535.0/65280.0 + }; + + cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, V2ToV4, NULL); + + if (mpe == NULL) return mpe; + mpe ->Implements = cmsSigLabV2toV4; + return mpe; +} + + +// Reverse direction +cmsStage* _cmsStageAllocLabV4ToV2(cmsContext ContextID) +{ + static const cmsFloat64Number V4ToV2[] = { 65280.0/65535.0, 0, 0, + 0, 65280.0/65535.0, 0, + 0, 0, 65280.0/65535.0 + }; + + cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, V4ToV2, NULL); + + if (mpe == NULL) return mpe; + mpe ->Implements = cmsSigLabV4toV2; + return mpe; +} + + +// To Lab to float. Note that the MPE gives numbers in normal Lab range +// and we need 0..1.0 range for the formatters +// L* : 0...100 => 0...1.0 (L* / 100) +// ab* : -128..+127 to 0..1 ((ab* + 128) / 255) + +cmsStage* _cmsStageNormalizeFromLabFloat(cmsContext ContextID) +{ + static const cmsFloat64Number a1[] = { + 1.0/100.0, 0, 0, + 0, 1.0/255.0, 0, + 0, 0, 1.0/255.0 + }; + + static const cmsFloat64Number o1[] = { + 0, + 128.0/255.0, + 128.0/255.0 + }; + + cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, o1); + + if (mpe == NULL) return mpe; + mpe ->Implements = cmsSigLab2FloatPCS; + return mpe; +} + +// Fom XYZ to floating point PCS +cmsStage* _cmsStageNormalizeFromXyzFloat(cmsContext ContextID) +{ +#define n (32768.0/65535.0) + static const cmsFloat64Number a1[] = { + n, 0, 0, + 0, n, 0, + 0, 0, n + }; +#undef n + + cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, NULL); + + if (mpe == NULL) return mpe; + mpe ->Implements = cmsSigXYZ2FloatPCS; + return mpe; +} + +cmsStage* _cmsStageNormalizeToLabFloat(cmsContext ContextID) +{ + static const cmsFloat64Number a1[] = { + 100.0, 0, 0, + 0, 255.0, 0, + 0, 0, 255.0 + }; + + static const cmsFloat64Number o1[] = { + 0, + -128.0, + -128.0 + }; + + cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, o1); + if (mpe == NULL) return mpe; + mpe ->Implements = cmsSigFloatPCS2Lab; + return mpe; +} + +cmsStage* _cmsStageNormalizeToXyzFloat(cmsContext ContextID) +{ +#define n (65535.0/32768.0) + + static const cmsFloat64Number a1[] = { + n, 0, 0, + 0, n, 0, + 0, 0, n + }; +#undef n + + cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, NULL); + if (mpe == NULL) return mpe; + mpe ->Implements = cmsSigFloatPCS2XYZ; + return mpe; +} + + + +// ******************************************************************************** +// Type cmsSigXYZ2LabElemType +// ******************************************************************************** + +static +void EvaluateXYZ2Lab(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) +{ + cmsCIELab Lab; + cmsCIEXYZ XYZ; + const cmsFloat64Number XYZadj = MAX_ENCODEABLE_XYZ; + + // From 0..1.0 to XYZ + + XYZ.X = In[0] * XYZadj; + XYZ.Y = In[1] * XYZadj; + XYZ.Z = In[2] * XYZadj; + + cmsXYZ2Lab(NULL, &Lab, &XYZ); + + // From V4 Lab to 0..1.0 + + Out[0] = (cmsFloat32Number) (Lab.L / 100.0); + Out[1] = (cmsFloat32Number) ((Lab.a + 128.0) / 255.0); + Out[2] = (cmsFloat32Number) ((Lab.b + 128.0) / 255.0); + return; + + cmsUNUSED_PARAMETER(mpe); +} + +cmsStage* _cmsStageAllocXYZ2Lab(cmsContext ContextID) +{ + return _cmsStageAllocPlaceholder(ContextID, cmsSigXYZ2LabElemType, 3, 3, EvaluateXYZ2Lab, NULL, NULL, NULL); + +} + +// ******************************************************************************** + +// For v4, S-Shaped curves are placed in a/b axis to increase resolution near gray + +cmsStage* _cmsStageAllocLabPrelin(cmsContext ContextID) +{ + cmsToneCurve* LabTable[3]; + cmsFloat64Number Params[1] = {2.4} ; + + LabTable[0] = cmsBuildGamma(ContextID, 1.0); + LabTable[1] = cmsBuildParametricToneCurve(ContextID, 108, Params); + LabTable[2] = cmsBuildParametricToneCurve(ContextID, 108, Params); + + return cmsStageAllocToneCurves(ContextID, 3, LabTable); +} + + +// Free a single MPE +void CMSEXPORT cmsStageFree(cmsStage* mpe) +{ + if (mpe ->FreePtr) + mpe ->FreePtr(mpe); + + _cmsFree(mpe ->ContextID, mpe); +} + + +cmsUInt32Number CMSEXPORT cmsStageInputChannels(const cmsStage* mpe) +{ + return mpe ->InputChannels; +} + +cmsUInt32Number CMSEXPORT cmsStageOutputChannels(const cmsStage* mpe) +{ + return mpe ->OutputChannels; +} + +cmsStageSignature CMSEXPORT cmsStageType(const cmsStage* mpe) +{ + return mpe -> Type; +} + +void* CMSEXPORT cmsStageData(const cmsStage* mpe) +{ + return mpe -> Data; +} + +cmsStage* CMSEXPORT cmsStageNext(const cmsStage* mpe) +{ + return mpe -> Next; +} + + +// Duplicates an MPE +cmsStage* CMSEXPORT cmsStageDup(cmsStage* mpe) +{ + cmsStage* NewMPE; + + if (mpe == NULL) return NULL; + NewMPE = _cmsStageAllocPlaceholder(mpe ->ContextID, + mpe ->Type, + mpe ->InputChannels, + mpe ->OutputChannels, + mpe ->EvalPtr, + mpe ->DupElemPtr, + mpe ->FreePtr, + NULL); + if (NewMPE == NULL) return NULL; + + NewMPE ->Implements = mpe ->Implements; + + if (mpe ->DupElemPtr) { + + NewMPE ->Data = mpe ->DupElemPtr(mpe); + + if (NewMPE->Data == NULL) { + + cmsStageFree(NewMPE); + return NULL; + } + + } else { + + NewMPE ->Data = NULL; + } + + return NewMPE; +} + + +// *********************************************************************************************************** + +// This function sets up the channel count +static +cmsBool BlessLUT(cmsPipeline* lut) +{ + // We can set the input/ouput channels only if we have elements. + if (lut ->Elements != NULL) { + + cmsStage* prev; + cmsStage* next; + cmsStage* First; + cmsStage* Last; + + First = cmsPipelineGetPtrToFirstStage(lut); + Last = cmsPipelineGetPtrToLastStage(lut); + + if (First == NULL || Last == NULL) return FALSE; + + lut->InputChannels = First->InputChannels; + lut->OutputChannels = Last->OutputChannels; + + // Check chain consistency + prev = First; + next = prev->Next; + + while (next != NULL) + { + if (next->InputChannels != prev->OutputChannels) + return FALSE; + + next = next->Next; + prev = prev->Next; + } + } + return TRUE; +} + + +// Default to evaluate the LUT on 16 bit-basis. Precision is retained. +static +void _LUTeval16(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register const void* D) +{ + cmsPipeline* lut = (cmsPipeline*) D; + cmsStage *mpe; + cmsFloat32Number Storage[2][MAX_STAGE_CHANNELS] = {0.0f}; + int Phase = 0, NextPhase; + + From16ToFloat(In, &Storage[Phase][0], lut ->InputChannels); + + for (mpe = lut ->Elements; + mpe != NULL; + mpe = mpe ->Next) { + + NextPhase = Phase ^ 1; + mpe ->EvalPtr(&Storage[Phase][0], &Storage[NextPhase][0], mpe); + Phase = NextPhase; + } + + + FromFloatTo16(&Storage[Phase][0], Out, lut ->OutputChannels); +} + + + +// Does evaluate the LUT on cmsFloat32Number-basis. +static +void _LUTevalFloat(register const cmsFloat32Number In[], register cmsFloat32Number Out[], const void* D) +{ + cmsPipeline* lut = (cmsPipeline*) D; + cmsStage *mpe; + cmsFloat32Number Storage[2][MAX_STAGE_CHANNELS] = {0.0f}; + int Phase = 0, NextPhase; + + memmove(&Storage[Phase][0], In, lut ->InputChannels * sizeof(cmsFloat32Number)); + + for (mpe = lut ->Elements; + mpe != NULL; + mpe = mpe ->Next) { + + NextPhase = Phase ^ 1; + mpe ->EvalPtr(&Storage[Phase][0], &Storage[NextPhase][0], mpe); + Phase = NextPhase; + } + + memmove(Out, &Storage[Phase][0], lut ->OutputChannels * sizeof(cmsFloat32Number)); +} + + + + +// LUT Creation & Destruction + +cmsPipeline* CMSEXPORT cmsPipelineAlloc(cmsContext ContextID, cmsUInt32Number InputChannels, cmsUInt32Number OutputChannels) +{ + cmsPipeline* NewLUT; + + // A value of zero in channels is allowed as placeholder + if (InputChannels >= cmsMAXCHANNELS || + OutputChannels >= cmsMAXCHANNELS) return NULL; + + NewLUT = (cmsPipeline*) _cmsMallocZero(ContextID, sizeof(cmsPipeline)); + if (NewLUT == NULL) return NULL; + + + NewLUT -> InputChannels = InputChannels; + NewLUT -> OutputChannels = OutputChannels; + + NewLUT ->Eval16Fn = _LUTeval16; + NewLUT ->EvalFloatFn = _LUTevalFloat; + NewLUT ->DupDataFn = NULL; + NewLUT ->FreeDataFn = NULL; + NewLUT ->Data = NewLUT; + NewLUT ->ContextID = ContextID; + + if (!BlessLUT(NewLUT)) + { + _cmsFree(ContextID, NewLUT); + return NULL; + } + + return NewLUT; +} + +cmsContext CMSEXPORT cmsGetPipelineContextID(const cmsPipeline* lut) +{ + _cmsAssert(lut != NULL); + return lut ->ContextID; +} + +cmsUInt32Number CMSEXPORT cmsPipelineInputChannels(const cmsPipeline* lut) +{ + _cmsAssert(lut != NULL); + return lut ->InputChannels; +} + +cmsUInt32Number CMSEXPORT cmsPipelineOutputChannels(const cmsPipeline* lut) +{ + _cmsAssert(lut != NULL); + return lut ->OutputChannels; +} + +// Free a profile elements LUT +void CMSEXPORT cmsPipelineFree(cmsPipeline* lut) +{ + cmsStage *mpe, *Next; + + if (lut == NULL) return; + + for (mpe = lut ->Elements; + mpe != NULL; + mpe = Next) { + + Next = mpe ->Next; + cmsStageFree(mpe); + } + + if (lut ->FreeDataFn) lut ->FreeDataFn(lut ->ContextID, lut ->Data); + + _cmsFree(lut ->ContextID, lut); +} + + +// Default to evaluate the LUT on 16 bit-basis. +void CMSEXPORT cmsPipelineEval16(const cmsUInt16Number In[], cmsUInt16Number Out[], const cmsPipeline* lut) +{ + _cmsAssert(lut != NULL); + lut ->Eval16Fn(In, Out, lut->Data); +} + + +// Does evaluate the LUT on cmsFloat32Number-basis. +void CMSEXPORT cmsPipelineEvalFloat(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsPipeline* lut) +{ + _cmsAssert(lut != NULL); + lut ->EvalFloatFn(In, Out, lut); +} + + + +// Duplicates a LUT +cmsPipeline* CMSEXPORT cmsPipelineDup(const cmsPipeline* lut) +{ + cmsPipeline* NewLUT; + cmsStage *NewMPE, *Anterior = NULL, *mpe; + cmsBool First = TRUE; + + if (lut == NULL) return NULL; + + NewLUT = cmsPipelineAlloc(lut ->ContextID, lut ->InputChannels, lut ->OutputChannels); + if (NewLUT == NULL) return NULL; + + for (mpe = lut ->Elements; + mpe != NULL; + mpe = mpe ->Next) { + + NewMPE = cmsStageDup(mpe); + + if (NewMPE == NULL) { + cmsPipelineFree(NewLUT); + return NULL; + } + + if (First) { + NewLUT ->Elements = NewMPE; + First = FALSE; + } + else { + Anterior ->Next = NewMPE; + } + + Anterior = NewMPE; + } + + NewLUT ->Eval16Fn = lut ->Eval16Fn; + NewLUT ->EvalFloatFn = lut ->EvalFloatFn; + NewLUT ->DupDataFn = lut ->DupDataFn; + NewLUT ->FreeDataFn = lut ->FreeDataFn; + + if (NewLUT ->DupDataFn != NULL) + NewLUT ->Data = NewLUT ->DupDataFn(lut ->ContextID, lut->Data); + + + NewLUT ->SaveAs8Bits = lut ->SaveAs8Bits; + + if (!BlessLUT(NewLUT)) + { + _cmsFree(lut->ContextID, NewLUT); + return NULL; + } + + return NewLUT; +} + + +int CMSEXPORT cmsPipelineInsertStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage* mpe) +{ + cmsStage* Anterior = NULL, *pt; + + if (lut == NULL || mpe == NULL) + return FALSE; + + switch (loc) { + + case cmsAT_BEGIN: + mpe ->Next = lut ->Elements; + lut ->Elements = mpe; + break; + + case cmsAT_END: + + if (lut ->Elements == NULL) + lut ->Elements = mpe; + else { + + for (pt = lut ->Elements; + pt != NULL; + pt = pt -> Next) Anterior = pt; + + Anterior ->Next = mpe; + mpe ->Next = NULL; + } + break; + default:; + return FALSE; + } + + return BlessLUT(lut); +} + +// Unlink an element and return the pointer to it +void CMSEXPORT cmsPipelineUnlinkStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage** mpe) +{ + cmsStage *Anterior, *pt, *Last; + cmsStage *Unlinked = NULL; + + + // If empty LUT, there is nothing to remove + if (lut ->Elements == NULL) { + if (mpe) *mpe = NULL; + return; + } + + // On depending on the strategy... + switch (loc) { + + case cmsAT_BEGIN: + { + cmsStage* elem = lut ->Elements; + + lut ->Elements = elem -> Next; + elem ->Next = NULL; + Unlinked = elem; + + } + break; + + case cmsAT_END: + Anterior = Last = NULL; + for (pt = lut ->Elements; + pt != NULL; + pt = pt -> Next) { + Anterior = Last; + Last = pt; + } + + Unlinked = Last; // Next already points to NULL + + // Truncate the chain + if (Anterior) + Anterior ->Next = NULL; + else + lut ->Elements = NULL; + break; + default:; + } + + if (mpe) + *mpe = Unlinked; + else + cmsStageFree(Unlinked); + + // May fail, but we ignore it + BlessLUT(lut); +} + + +// Concatenate two LUT into a new single one +cmsBool CMSEXPORT cmsPipelineCat(cmsPipeline* l1, const cmsPipeline* l2) +{ + cmsStage* mpe; + + // If both LUTS does not have elements, we need to inherit + // the number of channels + if (l1 ->Elements == NULL && l2 ->Elements == NULL) { + l1 ->InputChannels = l2 ->InputChannels; + l1 ->OutputChannels = l2 ->OutputChannels; + } + + // Cat second + for (mpe = l2 ->Elements; + mpe != NULL; + mpe = mpe ->Next) { + + // We have to dup each element + if (!cmsPipelineInsertStage(l1, cmsAT_END, cmsStageDup(mpe))) + return FALSE; + } + + return BlessLUT(l1); +} + + +cmsBool CMSEXPORT cmsPipelineSetSaveAs8bitsFlag(cmsPipeline* lut, cmsBool On) +{ + cmsBool Anterior = lut ->SaveAs8Bits; + + lut ->SaveAs8Bits = On; + return Anterior; +} + + +cmsStage* CMSEXPORT cmsPipelineGetPtrToFirstStage(const cmsPipeline* lut) +{ + return lut ->Elements; +} + +cmsStage* CMSEXPORT cmsPipelineGetPtrToLastStage(const cmsPipeline* lut) +{ + cmsStage *mpe, *Anterior = NULL; + + for (mpe = lut ->Elements; mpe != NULL; mpe = mpe ->Next) + Anterior = mpe; + + return Anterior; +} + +cmsUInt32Number CMSEXPORT cmsPipelineStageCount(const cmsPipeline* lut) +{ + cmsStage *mpe; + cmsUInt32Number n; + + for (n=0, mpe = lut ->Elements; mpe != NULL; mpe = mpe ->Next) + n++; + + return n; +} + +// This function may be used to set the optional evaluator and a block of private data. If private data is being used, an optional +// duplicator and free functions should also be specified in order to duplicate the LUT construct. Use NULL to inhibit such functionality. +void CMSEXPORT _cmsPipelineSetOptimizationParameters(cmsPipeline* Lut, + _cmsOPTeval16Fn Eval16, + void* PrivateData, + _cmsFreeUserDataFn FreePrivateDataFn, + _cmsDupUserDataFn DupPrivateDataFn) +{ + + Lut ->Eval16Fn = Eval16; + Lut ->DupDataFn = DupPrivateDataFn; + Lut ->FreeDataFn = FreePrivateDataFn; + Lut ->Data = PrivateData; +} + + +// ----------------------------------------------------------- Reverse interpolation +// Here's how it goes. The derivative Df(x) of the function f is the linear +// transformation that best approximates f near the point x. It can be represented +// by a matrix A whose entries are the partial derivatives of the components of f +// with respect to all the coordinates. This is know as the Jacobian +// +// The best linear approximation to f is given by the matrix equation: +// +// y-y0 = A (x-x0) +// +// So, if x0 is a good "guess" for the zero of f, then solving for the zero of this +// linear approximation will give a "better guess" for the zero of f. Thus let y=0, +// and since y0=f(x0) one can solve the above equation for x. This leads to the +// Newton's method formula: +// +// xn+1 = xn - A-1 f(xn) +// +// where xn+1 denotes the (n+1)-st guess, obtained from the n-th guess xn in the +// fashion described above. Iterating this will give better and better approximations +// if you have a "good enough" initial guess. + + +#define JACOBIAN_EPSILON 0.001f +#define INVERSION_MAX_ITERATIONS 30 + +// Increment with reflexion on boundary +static +void IncDelta(cmsFloat32Number *Val) +{ + if (*Val < (1.0 - JACOBIAN_EPSILON)) + + *Val += JACOBIAN_EPSILON; + + else + *Val -= JACOBIAN_EPSILON; + +} + + + +// Euclidean distance between two vectors of n elements each one +static +cmsFloat32Number EuclideanDistance(cmsFloat32Number a[], cmsFloat32Number b[], int n) +{ + cmsFloat32Number sum = 0; + int i; + + for (i=0; i < n; i++) { + cmsFloat32Number dif = b[i] - a[i]; + sum += dif * dif; + } + + return sqrtf(sum); +} + + +// Evaluate a LUT in reverse direction. It only searches on 3->3 LUT. Uses Newton method +// +// x1 <- x - [J(x)]^-1 * f(x) +// +// lut: The LUT on where to do the search +// Target: LabK, 3 values of Lab plus destination K which is fixed +// Result: The obtained CMYK +// Hint: Location where begin the search + +cmsBool CMSEXPORT cmsPipelineEvalReverseFloat(cmsFloat32Number Target[], + cmsFloat32Number Result[], + cmsFloat32Number Hint[], + const cmsPipeline* lut) +{ + cmsUInt32Number i, j; + cmsFloat64Number error, LastError = 1E20; + cmsFloat32Number fx[4], x[4], xd[4], fxd[4]; + cmsVEC3 tmp, tmp2; + cmsMAT3 Jacobian; + + // Only 3->3 and 4->3 are supported + if (lut ->InputChannels != 3 && lut ->InputChannels != 4) return FALSE; + if (lut ->OutputChannels != 3) return FALSE; + + // Take the hint as starting point if specified + if (Hint == NULL) { + + // Begin at any point, we choose 1/3 of CMY axis + x[0] = x[1] = x[2] = 0.3f; + } + else { + + // Only copy 3 channels from hint... + for (j=0; j < 3; j++) + x[j] = Hint[j]; + } + + // If Lut is 4-dimensions, then grab target[3], which is fixed + if (lut ->InputChannels == 4) { + x[3] = Target[3]; + } + else x[3] = 0; // To keep lint happy + + + // Iterate + for (i = 0; i < INVERSION_MAX_ITERATIONS; i++) { + + // Get beginning fx + cmsPipelineEvalFloat(x, fx, lut); + + // Compute error + error = EuclideanDistance(fx, Target, 3); + + // If not convergent, return last safe value + if (error >= LastError) + break; + + // Keep latest values + LastError = error; + for (j=0; j < lut ->InputChannels; j++) + Result[j] = x[j]; + + // Found an exact match? + if (error <= 0) + break; + + // Obtain slope (the Jacobian) + for (j = 0; j < 3; j++) { + + xd[0] = x[0]; + xd[1] = x[1]; + xd[2] = x[2]; + xd[3] = x[3]; // Keep fixed channel + + IncDelta(&xd[j]); + + cmsPipelineEvalFloat(xd, fxd, lut); + + Jacobian.v[0].n[j] = ((fxd[0] - fx[0]) / JACOBIAN_EPSILON); + Jacobian.v[1].n[j] = ((fxd[1] - fx[1]) / JACOBIAN_EPSILON); + Jacobian.v[2].n[j] = ((fxd[2] - fx[2]) / JACOBIAN_EPSILON); + } + + // Solve system + tmp2.n[0] = fx[0] - Target[0]; + tmp2.n[1] = fx[1] - Target[1]; + tmp2.n[2] = fx[2] - Target[2]; + + if (!_cmsMAT3solve(&tmp, &Jacobian, &tmp2)) + return FALSE; + + // Move our guess + x[0] -= (cmsFloat32Number) tmp.n[0]; + x[1] -= (cmsFloat32Number) tmp.n[1]; + x[2] -= (cmsFloat32Number) tmp.n[2]; + + // Some clipping.... + for (j=0; j < 3; j++) { + if (x[j] < 0) x[j] = 0; + else + if (x[j] > 1.0) x[j] = 1.0; + } + } + + return TRUE; +} diff --git a/third_party/lcms/src/cmsmd5.c b/third_party/lcms/src/cmsmd5.c new file mode 100644 index 0000000000..a4758ff662 --- /dev/null +++ b/third_party/lcms/src/cmsmd5.c @@ -0,0 +1,343 @@ +//--------------------------------------------------------------------------------- +// +// 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" + +#ifdef CMS_USE_BIG_ENDIAN + +static +void byteReverse(cmsUInt8Number * buf, cmsUInt32Number longs) +{ + do { + + cmsUInt32Number t = _cmsAdjustEndianess32(*(cmsUInt32Number *) buf); + *(cmsUInt32Number *) buf = t; + buf += sizeof(cmsUInt32Number); + + } while (--longs); + +} + +#else +#define byteReverse(buf, len) +#endif + + +typedef struct { + + cmsUInt32Number buf[4]; + cmsUInt32Number bits[2]; + cmsUInt8Number in[64]; + cmsContext ContextID; + +} _cmsMD5; + +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +#define STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + + +static +void MD5_Transform(cmsUInt32Number buf[4], cmsUInt32Number in[16]) + +{ + register cmsUInt32Number a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + + +// Create a MD5 object +static +cmsHANDLE MD5alloc(cmsContext ContextID) +{ + _cmsMD5* ctx = (_cmsMD5*) _cmsMallocZero(ContextID, sizeof(_cmsMD5)); + if (ctx == NULL) return NULL; + + ctx ->ContextID = ContextID; + + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; + + return (cmsHANDLE) ctx; +} + + +static +void MD5add(cmsHANDLE Handle, cmsUInt8Number* buf, cmsUInt32Number len) +{ + _cmsMD5* ctx = (_cmsMD5*) Handle; + cmsUInt32Number t; + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + (len << 3)) < t) + ctx->bits[1]++; + + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; + + if (t) { + + cmsUInt8Number *p = (cmsUInt8Number *) ctx->in + t; + + t = 64 - t; + if (len < t) { + memmove(p, buf, len); + return; + } + + memmove(p, buf, t); + byteReverse(ctx->in, 16); + + MD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in); + buf += t; + len -= t; + } + + while (len >= 64) { + memmove(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in); + buf += 64; + len -= 64; + } + + memmove(ctx->in, buf, len); +} + +// Destroy the object and return the checksum +static +void MD5finish(cmsProfileID* ProfileID, cmsHANDLE Handle) +{ + _cmsMD5* ctx = (_cmsMD5*) Handle; + cmsUInt32Number count; + cmsUInt8Number *p; + + count = (ctx->bits[0] >> 3) & 0x3F; + + p = ctx->in + count; + *p++ = 0x80; + + count = 64 - 1 - count; + + if (count < 8) { + + memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in); + + memset(ctx->in, 0, 56); + } else { + memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + ((cmsUInt32Number *) ctx->in)[14] = ctx->bits[0]; + ((cmsUInt32Number *) ctx->in)[15] = ctx->bits[1]; + + MD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in); + + byteReverse((cmsUInt8Number *) ctx->buf, 4); + memmove(ProfileID ->ID8, ctx->buf, 16); + + _cmsFree(ctx ->ContextID, ctx); +} + + + +// Assuming io points to an ICC profile, compute and store MD5 checksum +// In the header, rendering intentent, attributes and ID should be set to zero +// before computing MD5 checksum (per 6.1.13 in ICC spec) + +cmsBool CMSEXPORT cmsMD5computeID(cmsHPROFILE hProfile) +{ + cmsContext ContextID; + cmsUInt32Number BytesNeeded; + cmsUInt8Number* Mem = NULL; + cmsHANDLE MD5 = NULL; + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; + _cmsICCPROFILE Keep; + + _cmsAssert(hProfile != NULL); + + ContextID = cmsGetProfileContextID(hProfile); + + // Save a copy of the profile header + memmove(&Keep, Icc, sizeof(_cmsICCPROFILE)); + + // Set RI, attributes and ID + memset(&Icc ->attributes, 0, sizeof(Icc ->attributes)); + Icc ->RenderingIntent = 0; + memset(&Icc ->ProfileID, 0, sizeof(Icc ->ProfileID)); + + // Compute needed storage + if (!cmsSaveProfileToMem(hProfile, NULL, &BytesNeeded)) goto Error; + + // Allocate memory + Mem = (cmsUInt8Number*) _cmsMalloc(ContextID, BytesNeeded); + if (Mem == NULL) goto Error; + + // Save to temporary storage + if (!cmsSaveProfileToMem(hProfile, Mem, &BytesNeeded)) goto Error; + + // Create MD5 object + MD5 = MD5alloc(ContextID); + if (MD5 == NULL) goto Error; + + // Add all bytes + MD5add(MD5, Mem, BytesNeeded); + + // Temp storage is no longer needed + _cmsFree(ContextID, Mem); + + // Restore header + memmove(Icc, &Keep, sizeof(_cmsICCPROFILE)); + + // And store the ID + MD5finish(&Icc ->ProfileID, MD5); + return TRUE; + +Error: + + // Free resources as something went wrong + // "MD5" cannot be other than NULL here, so no need to free it + if (Mem != NULL) _cmsFree(ContextID, Mem); + memmove(Icc, &Keep, sizeof(_cmsICCPROFILE)); + return FALSE; +} + +cmsBool CMSEXPORT cmsMD5computeIDExt(const void* buf, unsigned long size, unsigned char ProfileID[16]) +{ + cmsHANDLE MD5; + cmsUInt8Number* Mem; + + if (buf == NULL) + return FALSE; + MD5 = NULL; + Mem = (cmsUInt8Number*)_cmsMalloc(NULL,size); + memmove(Mem,buf,size); + // Create MD5 object + MD5 = MD5alloc(NULL); + if (MD5 == NULL) goto Error; + + // Add all bytes + MD5add(MD5, Mem, size); + + // Temp storage is no longer needed + _cmsFree(NULL, Mem); + + // And store the ID + MD5finish((cmsProfileID*)ProfileID, MD5); + return TRUE; +Error: + if (MD5 != NULL) _cmsFree(NULL, MD5); + return FALSE; +} diff --git a/third_party/lcms/src/cmsmtrx.c b/third_party/lcms/src/cmsmtrx.c new file mode 100644 index 0000000000..fb7b91caf1 --- /dev/null +++ b/third_party/lcms/src/cmsmtrx.c @@ -0,0 +1,175 @@ +//--------------------------------------------------------------------------------- +// +// 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" + + +#define DSWAP(x, y) {cmsFloat64Number tmp = (x); (x)=(y); (y)=tmp;} + + +// Initiate a vector +void CMSEXPORT _cmsVEC3init(cmsVEC3* r, cmsFloat64Number x, cmsFloat64Number y, cmsFloat64Number z) +{ + r -> n[VX] = x; + r -> n[VY] = y; + r -> n[VZ] = z; +} + +// Vector substraction +void CMSEXPORT _cmsVEC3minus(cmsVEC3* r, const cmsVEC3* a, const cmsVEC3* b) +{ + r -> n[VX] = a -> n[VX] - b -> n[VX]; + r -> n[VY] = a -> n[VY] - b -> n[VY]; + r -> n[VZ] = a -> n[VZ] - b -> n[VZ]; +} + +// Vector cross product +void CMSEXPORT _cmsVEC3cross(cmsVEC3* r, const cmsVEC3* u, const cmsVEC3* v) +{ + r ->n[VX] = u->n[VY] * v->n[VZ] - v->n[VY] * u->n[VZ]; + r ->n[VY] = u->n[VZ] * v->n[VX] - v->n[VZ] * u->n[VX]; + r ->n[VZ] = u->n[VX] * v->n[VY] - v->n[VX] * u->n[VY]; +} + +// Vector dot product +cmsFloat64Number CMSEXPORT _cmsVEC3dot(const cmsVEC3* u, const cmsVEC3* v) +{ + return u->n[VX] * v->n[VX] + u->n[VY] * v->n[VY] + u->n[VZ] * v->n[VZ]; +} + +// Euclidean length +cmsFloat64Number CMSEXPORT _cmsVEC3length(const cmsVEC3* a) +{ + return sqrt(a ->n[VX] * a ->n[VX] + + a ->n[VY] * a ->n[VY] + + a ->n[VZ] * a ->n[VZ]); +} + +// Euclidean distance +cmsFloat64Number CMSEXPORT _cmsVEC3distance(const cmsVEC3* a, const cmsVEC3* b) +{ + cmsFloat64Number d1 = a ->n[VX] - b ->n[VX]; + cmsFloat64Number d2 = a ->n[VY] - b ->n[VY]; + cmsFloat64Number d3 = a ->n[VZ] - b ->n[VZ]; + + return sqrt(d1*d1 + d2*d2 + d3*d3); +} + + + +// 3x3 Identity +void CMSEXPORT _cmsMAT3identity(cmsMAT3* a) +{ + _cmsVEC3init(&a-> v[0], 1.0, 0.0, 0.0); + _cmsVEC3init(&a-> v[1], 0.0, 1.0, 0.0); + _cmsVEC3init(&a-> v[2], 0.0, 0.0, 1.0); +} + +static +cmsBool CloseEnough(cmsFloat64Number a, cmsFloat64Number b) +{ + return fabs(b - a) < (1.0 / 65535.0); +} + + +cmsBool CMSEXPORT _cmsMAT3isIdentity(const cmsMAT3* a) +{ + cmsMAT3 Identity; + int i, j; + + _cmsMAT3identity(&Identity); + + for (i=0; i < 3; i++) + for (j=0; j < 3; j++) + if (!CloseEnough(a ->v[i].n[j], Identity.v[i].n[j])) return FALSE; + + return TRUE; +} + + +// Multiply two matrices +void CMSEXPORT _cmsMAT3per(cmsMAT3* r, const cmsMAT3* a, const cmsMAT3* b) +{ +#define ROWCOL(i, j) \ + a->v[i].n[0]*b->v[0].n[j] + a->v[i].n[1]*b->v[1].n[j] + a->v[i].n[2]*b->v[2].n[j] + + _cmsVEC3init(&r-> v[0], ROWCOL(0,0), ROWCOL(0,1), ROWCOL(0,2)); + _cmsVEC3init(&r-> v[1], ROWCOL(1,0), ROWCOL(1,1), ROWCOL(1,2)); + _cmsVEC3init(&r-> v[2], ROWCOL(2,0), ROWCOL(2,1), ROWCOL(2,2)); + +#undef ROWCOL //(i, j) +} + + + +// Inverse of a matrix b = a^(-1) +cmsBool CMSEXPORT _cmsMAT3inverse(const cmsMAT3* a, cmsMAT3* b) +{ + cmsFloat64Number det, c0, c1, c2; + + c0 = a -> v[1].n[1]*a -> v[2].n[2] - a -> v[1].n[2]*a -> v[2].n[1]; + c1 = -a -> v[1].n[0]*a -> v[2].n[2] + a -> v[1].n[2]*a -> v[2].n[0]; + c2 = a -> v[1].n[0]*a -> v[2].n[1] - a -> v[1].n[1]*a -> v[2].n[0]; + + det = a -> v[0].n[0]*c0 + a -> v[0].n[1]*c1 + a -> v[0].n[2]*c2; + + if (fabs(det) < MATRIX_DET_TOLERANCE) return FALSE; // singular matrix; can't invert + + b -> v[0].n[0] = c0/det; + b -> v[0].n[1] = (a -> v[0].n[2]*a -> v[2].n[1] - a -> v[0].n[1]*a -> v[2].n[2])/det; + b -> v[0].n[2] = (a -> v[0].n[1]*a -> v[1].n[2] - a -> v[0].n[2]*a -> v[1].n[1])/det; + b -> v[1].n[0] = c1/det; + b -> v[1].n[1] = (a -> v[0].n[0]*a -> v[2].n[2] - a -> v[0].n[2]*a -> v[2].n[0])/det; + b -> v[1].n[2] = (a -> v[0].n[2]*a -> v[1].n[0] - a -> v[0].n[0]*a -> v[1].n[2])/det; + b -> v[2].n[0] = c2/det; + b -> v[2].n[1] = (a -> v[0].n[1]*a -> v[2].n[0] - a -> v[0].n[0]*a -> v[2].n[1])/det; + b -> v[2].n[2] = (a -> v[0].n[0]*a -> v[1].n[1] - a -> v[0].n[1]*a -> v[1].n[0])/det; + + return TRUE; +} + + +// Solve a system in the form Ax = b +cmsBool CMSEXPORT _cmsMAT3solve(cmsVEC3* x, cmsMAT3* a, cmsVEC3* b) +{ + cmsMAT3 m, a_1; + + memmove(&m, a, sizeof(cmsMAT3)); + + if (!_cmsMAT3inverse(&m, &a_1)) return FALSE; // Singular matrix + + _cmsMAT3eval(x, &a_1, b); + return TRUE; +} + +// Evaluate a vector across a matrix +void CMSEXPORT _cmsMAT3eval(cmsVEC3* r, const cmsMAT3* a, const cmsVEC3* v) +{ + r->n[VX] = a->v[0].n[VX]*v->n[VX] + a->v[0].n[VY]*v->n[VY] + a->v[0].n[VZ]*v->n[VZ]; + r->n[VY] = a->v[1].n[VX]*v->n[VX] + a->v[1].n[VY]*v->n[VY] + a->v[1].n[VZ]*v->n[VZ]; + r->n[VZ] = a->v[2].n[VX]*v->n[VX] + a->v[2].n[VY]*v->n[VY] + a->v[2].n[VZ]*v->n[VZ]; +} + diff --git a/third_party/lcms/src/cmsnamed.c b/third_party/lcms/src/cmsnamed.c new file mode 100644 index 0000000000..ef1eb3089e --- /dev/null +++ b/third_party/lcms/src/cmsnamed.c @@ -0,0 +1,937 @@ +//--------------------------------------------------------------------------------- +// +// 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" + +// Multilocalized unicode objects. That is an attempt to encapsulate i18n. + + +// Allocates an empty multi localizad unicode object +cmsMLU* CMSEXPORT cmsMLUalloc(cmsContext ContextID, cmsUInt32Number nItems) +{ + cmsMLU* mlu; + + // nItems should be positive if given + if (nItems <= 0) nItems = 2; + + // Create the container + mlu = (cmsMLU*) _cmsMallocZero(ContextID, sizeof(cmsMLU)); + if (mlu == NULL) return NULL; + + mlu ->ContextID = ContextID; + + // Create entry array + mlu ->Entries = (_cmsMLUentry*) _cmsCalloc(ContextID, nItems, sizeof(_cmsMLUentry)); + if (mlu ->Entries == NULL) { + _cmsFree(ContextID, mlu); + return NULL; + } + + // Ok, keep indexes up to date + mlu ->AllocatedEntries = nItems; + mlu ->UsedEntries = 0; + + return mlu; +} + + +// Grows a mempool table for a MLU. Each time this function is called, mempool size is multiplied times two. +static +cmsBool GrowMLUpool(cmsMLU* mlu) +{ + cmsUInt32Number size; + void *NewPtr; + + // Sanity check + if (mlu == NULL) return FALSE; + + if (mlu ->PoolSize == 0) + size = 256; + else + size = mlu ->PoolSize * 2; + + // Check for overflow + if (size < mlu ->PoolSize) return FALSE; + + // Reallocate the pool + NewPtr = _cmsRealloc(mlu ->ContextID, mlu ->MemPool, size); + if (NewPtr == NULL) return FALSE; + + + mlu ->MemPool = NewPtr; + mlu ->PoolSize = size; + + return TRUE; +} + + +// Grows a entry table for a MLU. Each time this function is called, table size is multiplied times two. +static +cmsBool GrowMLUtable(cmsMLU* mlu) +{ + int AllocatedEntries; + _cmsMLUentry *NewPtr; + + // Sanity check + if (mlu == NULL) return FALSE; + + AllocatedEntries = mlu ->AllocatedEntries * 2; + + // Check for overflow + if (AllocatedEntries / 2 != mlu ->AllocatedEntries) return FALSE; + + // Reallocate the memory + NewPtr = (_cmsMLUentry*)_cmsRealloc(mlu ->ContextID, mlu ->Entries, AllocatedEntries*sizeof(_cmsMLUentry)); + if (NewPtr == NULL) return FALSE; + + mlu ->Entries = NewPtr; + mlu ->AllocatedEntries = AllocatedEntries; + + return TRUE; +} + + +// Search for a specific entry in the structure. Language and Country are used. +static +int SearchMLUEntry(cmsMLU* mlu, cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode) +{ + int i; + + // Sanity check + if (mlu == NULL) return -1; + + // Iterate whole table + for (i=0; i < mlu ->UsedEntries; i++) { + + if (mlu ->Entries[i].Country == CountryCode && + mlu ->Entries[i].Language == LanguageCode) return i; + } + + // Not found + return -1; +} + +// Add a block of characters to the intended MLU. Language and country are specified. +// Only one entry for Language/country pair is allowed. +static +cmsBool AddMLUBlock(cmsMLU* mlu, cmsUInt32Number size, const wchar_t *Block, + cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode) +{ + cmsUInt32Number Offset; + cmsUInt8Number* Ptr; + + // Sanity check + if (mlu == NULL) return FALSE; + + // Is there any room available? + if (mlu ->UsedEntries >= mlu ->AllocatedEntries) { + if (!GrowMLUtable(mlu)) return FALSE; + } + + // Only one ASCII string + if (SearchMLUEntry(mlu, LanguageCode, CountryCode) >= 0) return FALSE; // Only one is allowed! + + // Check for size + while ((mlu ->PoolSize - mlu ->PoolUsed) < size) { + + if (!GrowMLUpool(mlu)) return FALSE; + } + + Offset = mlu ->PoolUsed; + + Ptr = (cmsUInt8Number*) mlu ->MemPool; + if (Ptr == NULL) return FALSE; + + // Set the entry + memmove(Ptr + Offset, Block, size); + mlu ->PoolUsed += size; + + mlu ->Entries[mlu ->UsedEntries].StrW = Offset; + mlu ->Entries[mlu ->UsedEntries].Len = size; + mlu ->Entries[mlu ->UsedEntries].Country = CountryCode; + mlu ->Entries[mlu ->UsedEntries].Language = LanguageCode; + mlu ->UsedEntries++; + + return TRUE; +} + + +// Add an ASCII entry. +cmsBool CMSEXPORT cmsMLUsetASCII(cmsMLU* mlu, const char LanguageCode[3], const char CountryCode[3], const char* ASCIIString) +{ + cmsUInt32Number i, len = (cmsUInt32Number) strlen(ASCIIString)+1; + wchar_t* WStr; + cmsBool rc; + cmsUInt16Number Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) LanguageCode); + cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) CountryCode); + + if (mlu == NULL) return FALSE; + + WStr = (wchar_t*) _cmsCalloc(mlu ->ContextID, len, sizeof(wchar_t)); + if (WStr == NULL) return FALSE; + + for (i=0; i < len; i++) + WStr[i] = (wchar_t) ASCIIString[i]; + + rc = AddMLUBlock(mlu, len * sizeof(wchar_t), WStr, Lang, Cntry); + + _cmsFree(mlu ->ContextID, WStr); + return rc; + +} + +// We don't need any wcs support library +static +cmsUInt32Number mywcslen(const wchar_t *s) +{ + const wchar_t *p; + + p = s; + while (*p) + p++; + + return (cmsUInt32Number)(p - s); +} + + +// Add a wide entry +cmsBool CMSEXPORT cmsMLUsetWide(cmsMLU* mlu, const char Language[3], const char Country[3], const wchar_t* WideString) +{ + cmsUInt16Number Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) Language); + cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) Country); + cmsUInt32Number len; + + if (mlu == NULL) return FALSE; + if (WideString == NULL) return FALSE; + + len = (cmsUInt32Number) (mywcslen(WideString) + 1) * sizeof(wchar_t); + return AddMLUBlock(mlu, len, WideString, Lang, Cntry); +} + +// Duplicating a MLU is as easy as copying all members +cmsMLU* CMSEXPORT cmsMLUdup(const cmsMLU* mlu) +{ + cmsMLU* NewMlu = NULL; + + // Duplicating a NULL obtains a NULL + if (mlu == NULL) return NULL; + + NewMlu = cmsMLUalloc(mlu ->ContextID, mlu ->UsedEntries); + if (NewMlu == NULL) return NULL; + + // Should never happen + if (NewMlu ->AllocatedEntries < mlu ->UsedEntries) + goto Error; + + // Sanitize... + if (NewMlu ->Entries == NULL || mlu ->Entries == NULL) goto Error; + + memmove(NewMlu ->Entries, mlu ->Entries, mlu ->UsedEntries * sizeof(_cmsMLUentry)); + NewMlu ->UsedEntries = mlu ->UsedEntries; + + // The MLU may be empty + if (mlu ->PoolUsed == 0) { + NewMlu ->MemPool = NULL; + } + else { + // It is not empty + NewMlu ->MemPool = _cmsMalloc(mlu ->ContextID, mlu ->PoolUsed); + if (NewMlu ->MemPool == NULL) goto Error; + } + + NewMlu ->PoolSize = mlu ->PoolUsed; + + if (NewMlu ->MemPool == NULL || mlu ->MemPool == NULL) goto Error; + + memmove(NewMlu ->MemPool, mlu->MemPool, mlu ->PoolUsed); + NewMlu ->PoolUsed = mlu ->PoolUsed; + + return NewMlu; + +Error: + + if (NewMlu != NULL) cmsMLUfree(NewMlu); + return NULL; +} + +// Free any used memory +void CMSEXPORT cmsMLUfree(cmsMLU* mlu) +{ + if (mlu) { + + if (mlu -> Entries) _cmsFree(mlu ->ContextID, mlu->Entries); + if (mlu -> MemPool) _cmsFree(mlu ->ContextID, mlu->MemPool); + + _cmsFree(mlu ->ContextID, mlu); + } +} + + +// The algorithm first searches for an exact match of country and language, if not found it uses +// the Language. If none is found, first entry is used instead. +static +const wchar_t* _cmsMLUgetWide(const cmsMLU* mlu, + cmsUInt32Number *len, + cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode, + cmsUInt16Number* UsedLanguageCode, cmsUInt16Number* UsedCountryCode) +{ + int i; + int Best = -1; + _cmsMLUentry* v; + + if (mlu == NULL) return NULL; + + if (mlu -> AllocatedEntries <= 0) return NULL; + + for (i=0; i < mlu ->UsedEntries; i++) { + + v = mlu ->Entries + i; + + if (v -> Language == LanguageCode) { + + if (Best == -1) Best = i; + + if (v -> Country == CountryCode) { + + if (UsedLanguageCode != NULL) *UsedLanguageCode = v ->Language; + if (UsedCountryCode != NULL) *UsedCountryCode = v ->Country; + + if (len != NULL) *len = v ->Len; + + return (wchar_t*) ((cmsUInt8Number*) mlu ->MemPool + v -> StrW); // Found exact match + } + } + } + + // No string found. Return First one + if (Best == -1) + Best = 0; + + v = mlu ->Entries + Best; + + if (UsedLanguageCode != NULL) *UsedLanguageCode = v ->Language; + if (UsedCountryCode != NULL) *UsedCountryCode = v ->Country; + + if (len != NULL) *len = v ->Len; + + return(wchar_t*) ((cmsUInt8Number*) mlu ->MemPool + v ->StrW); +} + + +// Obtain an ASCII representation of the wide string. Setting buffer to NULL returns the len +cmsUInt32Number CMSEXPORT cmsMLUgetASCII(const cmsMLU* mlu, + const char LanguageCode[3], const char CountryCode[3], + char* Buffer, cmsUInt32Number BufferSize) +{ + const wchar_t *Wide; + cmsUInt32Number StrLen = 0; + cmsUInt32Number ASCIIlen, i; + + cmsUInt16Number Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) LanguageCode); + cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) CountryCode); + + // Sanitize + if (mlu == NULL) return 0; + + // Get WideChar + Wide = _cmsMLUgetWide(mlu, &StrLen, Lang, Cntry, NULL, NULL); + if (Wide == NULL) return 0; + + ASCIIlen = StrLen / sizeof(wchar_t); + + // Maybe we want only to know the len? + if (Buffer == NULL) return ASCIIlen + 1; // Note the zero at the end + + // No buffer size means no data + if (BufferSize <= 0) return 0; + + // Some clipping may be required + if (BufferSize < ASCIIlen + 1) + ASCIIlen = BufferSize - 1; + + // Precess each character + for (i=0; i < ASCIIlen; i++) { + + if (Wide[i] == 0) + Buffer[i] = 0; + else + Buffer[i] = (char) Wide[i]; + } + + // We put a termination "\0" + Buffer[ASCIIlen] = 0; + return ASCIIlen + 1; +} + +// Obtain a wide representation of the MLU, on depending on current locale settings +cmsUInt32Number CMSEXPORT cmsMLUgetWide(const cmsMLU* mlu, + const char LanguageCode[3], const char CountryCode[3], + wchar_t* Buffer, cmsUInt32Number BufferSize) +{ + const wchar_t *Wide; + cmsUInt32Number StrLen = 0; + + cmsUInt16Number Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) LanguageCode); + cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) CountryCode); + + // Sanitize + if (mlu == NULL) return 0; + + Wide = _cmsMLUgetWide(mlu, &StrLen, Lang, Cntry, NULL, NULL); + if (Wide == NULL) return 0; + + // Maybe we want only to know the len? + if (Buffer == NULL) return StrLen + sizeof(wchar_t); + + // No buffer size means no data + if (BufferSize <= 0) return 0; + + // Some clipping may be required + if (BufferSize < StrLen + sizeof(wchar_t)) + StrLen = BufferSize - + sizeof(wchar_t); + + memmove(Buffer, Wide, StrLen); + Buffer[StrLen / sizeof(wchar_t)] = 0; + + return StrLen + sizeof(wchar_t); +} + + +// Get also the language and country +CMSAPI cmsBool CMSEXPORT cmsMLUgetTranslation(const cmsMLU* mlu, + const char LanguageCode[3], const char CountryCode[3], + char ObtainedLanguage[3], char ObtainedCountry[3]) +{ + const wchar_t *Wide; + + cmsUInt16Number Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) LanguageCode); + cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) CountryCode); + cmsUInt16Number ObtLang, ObtCode; + + // Sanitize + if (mlu == NULL) return FALSE; + + Wide = _cmsMLUgetWide(mlu, NULL, Lang, Cntry, &ObtLang, &ObtCode); + if (Wide == NULL) return FALSE; + + // Get used language and code + *(cmsUInt16Number *)ObtainedLanguage = _cmsAdjustEndianess16(ObtLang); + *(cmsUInt16Number *)ObtainedCountry = _cmsAdjustEndianess16(ObtCode); + + ObtainedLanguage[2] = ObtainedCountry[2] = 0; + return TRUE; +} + + + +// Get the number of translations in the MLU object +cmsUInt32Number CMSEXPORT cmsMLUtranslationsCount(const cmsMLU* mlu) +{ + if (mlu == NULL) return 0; + return mlu->UsedEntries; +} + +// Get the language and country codes for a specific MLU index +cmsBool CMSEXPORT cmsMLUtranslationsCodes(const cmsMLU* mlu, + cmsUInt32Number idx, + char LanguageCode[3], + char CountryCode[3]) +{ + _cmsMLUentry *entry; + + if (mlu == NULL) return FALSE; + + if (idx >= (cmsUInt32Number) mlu->UsedEntries) return FALSE; + + entry = &mlu->Entries[idx]; + + *(cmsUInt16Number *)LanguageCode = _cmsAdjustEndianess16(entry->Language); + *(cmsUInt16Number *)CountryCode = _cmsAdjustEndianess16(entry->Country); + + return TRUE; +} + + +// Named color lists -------------------------------------------------------------------------------------------- + +// Grow the list to keep at least NumElements +static +cmsBool GrowNamedColorList(cmsNAMEDCOLORLIST* v) +{ + cmsUInt32Number size; + _cmsNAMEDCOLOR * NewPtr; + + if (v == NULL) return FALSE; + + if (v ->Allocated == 0) + size = 64; // Initial guess + else + size = v ->Allocated * 2; + + // Keep a maximum color lists can grow, 100K entries seems reasonable + if (size > 1024*100) return FALSE; + + NewPtr = (_cmsNAMEDCOLOR*) _cmsRealloc(v ->ContextID, v ->List, size * sizeof(_cmsNAMEDCOLOR)); + if (NewPtr == NULL) + return FALSE; + + v ->List = NewPtr; + v ->Allocated = size; + return TRUE; +} + +// Allocate a list for n elements +cmsNAMEDCOLORLIST* CMSEXPORT cmsAllocNamedColorList(cmsContext ContextID, cmsUInt32Number n, cmsUInt32Number ColorantCount, const char* Prefix, const char* Suffix) +{ + cmsNAMEDCOLORLIST* v = (cmsNAMEDCOLORLIST*) _cmsMallocZero(ContextID, sizeof(cmsNAMEDCOLORLIST)); + + if (v == NULL) return NULL; + + v ->List = NULL; + v ->nColors = 0; + v ->ContextID = ContextID; + + while (v -> Allocated < n) { + if (!GrowNamedColorList(v)) { + cmsFreeNamedColorList(v); + return NULL; + } + } + + strncpy(v ->Prefix, Prefix, sizeof(v ->Prefix)-1); + strncpy(v ->Suffix, Suffix, sizeof(v ->Suffix)-1); + v->Prefix[32] = v->Suffix[32] = 0; + + v -> ColorantCount = ColorantCount; + + return v; +} + +// Free a list +void CMSEXPORT cmsFreeNamedColorList(cmsNAMEDCOLORLIST* v) +{ + if (v == NULL) return; + if (v ->List) _cmsFree(v ->ContextID, v ->List); + _cmsFree(v ->ContextID, v); +} + +cmsNAMEDCOLORLIST* CMSEXPORT cmsDupNamedColorList(const cmsNAMEDCOLORLIST* v) +{ + cmsNAMEDCOLORLIST* NewNC; + + if (v == NULL) return NULL; + + NewNC= cmsAllocNamedColorList(v ->ContextID, v -> nColors, v ->ColorantCount, v ->Prefix, v ->Suffix); + if (NewNC == NULL) return NULL; + + // For really large tables we need this + while (NewNC ->Allocated < v ->Allocated) { + if (!GrowNamedColorList(NewNC)) { + cmsFreeNamedColorList(NewNC); + return NULL; + } + } + + memmove(NewNC ->Prefix, v ->Prefix, sizeof(v ->Prefix)); + memmove(NewNC ->Suffix, v ->Suffix, sizeof(v ->Suffix)); + NewNC ->ColorantCount = v ->ColorantCount; + memmove(NewNC->List, v ->List, v->nColors * sizeof(_cmsNAMEDCOLOR)); + NewNC ->nColors = v ->nColors; + return NewNC; +} + + +// Append a color to a list. List pointer may change if reallocated +cmsBool CMSEXPORT cmsAppendNamedColor(cmsNAMEDCOLORLIST* NamedColorList, + const char* Name, + cmsUInt16Number PCS[3], cmsUInt16Number Colorant[cmsMAXCHANNELS]) +{ + cmsUInt32Number i; + + if (NamedColorList == NULL) return FALSE; + + if (NamedColorList ->nColors + 1 > NamedColorList ->Allocated) { + if (!GrowNamedColorList(NamedColorList)) return FALSE; + } + + for (i=0; i < NamedColorList ->ColorantCount; i++) + NamedColorList ->List[NamedColorList ->nColors].DeviceColorant[i] = Colorant == NULL? 0 : Colorant[i]; + + for (i=0; i < 3; i++) + NamedColorList ->List[NamedColorList ->nColors].PCS[i] = PCS == NULL ? 0 : PCS[i]; + + if (Name != NULL) { + + strncpy(NamedColorList ->List[NamedColorList ->nColors].Name, Name, cmsMAX_PATH-1); + NamedColorList ->List[NamedColorList ->nColors].Name[cmsMAX_PATH-1] = 0; + + } + else + NamedColorList ->List[NamedColorList ->nColors].Name[0] = 0; + + + NamedColorList ->nColors++; + return TRUE; +} + +// Returns number of elements +cmsUInt32Number CMSEXPORT cmsNamedColorCount(const cmsNAMEDCOLORLIST* NamedColorList) +{ + if (NamedColorList == NULL) return 0; + return NamedColorList ->nColors; +} + +// Info aboout a given color +cmsBool CMSEXPORT cmsNamedColorInfo(const cmsNAMEDCOLORLIST* NamedColorList, cmsUInt32Number nColor, + char* Name, + char* Prefix, + char* Suffix, + cmsUInt16Number* PCS, + cmsUInt16Number* Colorant) +{ + if (NamedColorList == NULL) return FALSE; + + if (nColor >= cmsNamedColorCount(NamedColorList)) return FALSE; + + if (Name) strcpy(Name, NamedColorList->List[nColor].Name); + if (Prefix) strcpy(Prefix, NamedColorList->Prefix); + if (Suffix) strcpy(Suffix, NamedColorList->Suffix); + if (PCS) + memmove(PCS, NamedColorList ->List[nColor].PCS, 3*sizeof(cmsUInt16Number)); + + if (Colorant) + memmove(Colorant, NamedColorList ->List[nColor].DeviceColorant, + sizeof(cmsUInt16Number) * NamedColorList ->ColorantCount); + + + return TRUE; +} + +// Search for a given color name (no prefix or suffix) +cmsInt32Number CMSEXPORT cmsNamedColorIndex(const cmsNAMEDCOLORLIST* NamedColorList, const char* Name) +{ + int i, n; + + if (NamedColorList == NULL) return -1; + n = cmsNamedColorCount(NamedColorList); + for (i=0; i < n; i++) { + if (cmsstrcasecmp(Name, NamedColorList->List[i].Name) == 0) + return i; + } + + return -1; +} + +// MPE support ----------------------------------------------------------------------------------------------------------------- + +static +void FreeNamedColorList(cmsStage* mpe) +{ + cmsNAMEDCOLORLIST* List = (cmsNAMEDCOLORLIST*) mpe ->Data; + cmsFreeNamedColorList(List); +} + +static +void* DupNamedColorList(cmsStage* mpe) +{ + cmsNAMEDCOLORLIST* List = (cmsNAMEDCOLORLIST*) mpe ->Data; + return cmsDupNamedColorList(List); +} + +static +void EvalNamedColorPCS(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) +{ + cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) mpe ->Data; + cmsUInt16Number index = (cmsUInt16Number) _cmsQuickSaturateWord(In[0] * 65535.0); + + if (index >= NamedColorList-> nColors) { + cmsSignalError(NamedColorList ->ContextID, cmsERROR_RANGE, "Color %d out of range; ignored", index); + } + else { + + // Named color always uses Lab + Out[0] = (cmsFloat32Number) (NamedColorList->List[index].PCS[0] / 65535.0); + Out[1] = (cmsFloat32Number) (NamedColorList->List[index].PCS[1] / 65535.0); + Out[2] = (cmsFloat32Number) (NamedColorList->List[index].PCS[2] / 65535.0); + } +} + +static +void EvalNamedColor(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) +{ + cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) mpe ->Data; + cmsUInt16Number index = (cmsUInt16Number) _cmsQuickSaturateWord(In[0] * 65535.0); + cmsUInt32Number j; + + if (index >= NamedColorList-> nColors) { + cmsSignalError(NamedColorList ->ContextID, cmsERROR_RANGE, "Color %d out of range; ignored", index); + } + else { + for (j=0; j < NamedColorList ->ColorantCount; j++) + Out[j] = (cmsFloat32Number) (NamedColorList->List[index].DeviceColorant[j] / 65535.0); + } +} + + +// Named color lookup element +cmsStage* _cmsStageAllocNamedColor(cmsNAMEDCOLORLIST* NamedColorList, cmsBool UsePCS) +{ + return _cmsStageAllocPlaceholder(NamedColorList ->ContextID, + cmsSigNamedColorElemType, + 1, UsePCS ? 3 : NamedColorList ->ColorantCount, + UsePCS ? EvalNamedColorPCS : EvalNamedColor, + DupNamedColorList, + FreeNamedColorList, + cmsDupNamedColorList(NamedColorList)); + +} + + +// Retrieve the named color list from a transform. Should be first element in the LUT +cmsNAMEDCOLORLIST* CMSEXPORT cmsGetNamedColorList(cmsHTRANSFORM xform) +{ + _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform; + cmsStage* mpe = v ->Lut->Elements; + + if (mpe ->Type != cmsSigNamedColorElemType) return NULL; + return (cmsNAMEDCOLORLIST*) mpe ->Data; +} + + +// Profile sequence description routines ------------------------------------------------------------------------------------- + +cmsSEQ* CMSEXPORT cmsAllocProfileSequenceDescription(cmsContext ContextID, cmsUInt32Number n) +{ + cmsSEQ* Seq; + cmsUInt32Number i; + + if (n == 0) return NULL; + + // In a absolutely arbitrary way, I hereby decide to allow a maxim of 255 profiles linked + // in a devicelink. It makes not sense anyway and may be used for exploits, so let's close the door! + if (n > 255) return NULL; + + Seq = (cmsSEQ*) _cmsMallocZero(ContextID, sizeof(cmsSEQ)); + if (Seq == NULL) return NULL; + + Seq -> ContextID = ContextID; + Seq -> seq = (cmsPSEQDESC*) _cmsCalloc(ContextID, n, sizeof(cmsPSEQDESC)); + Seq -> n = n; + + if (Seq -> seq == NULL) { + _cmsFree(ContextID, Seq); + return NULL; + } + + for (i=0; i < n; i++) { + Seq -> seq[i].Manufacturer = NULL; + Seq -> seq[i].Model = NULL; + Seq -> seq[i].Description = NULL; + } + + return Seq; +} + +void CMSEXPORT cmsFreeProfileSequenceDescription(cmsSEQ* pseq) +{ + cmsUInt32Number i; + + for (i=0; i < pseq ->n; i++) { + if (pseq ->seq[i].Manufacturer != NULL) cmsMLUfree(pseq ->seq[i].Manufacturer); + if (pseq ->seq[i].Model != NULL) cmsMLUfree(pseq ->seq[i].Model); + if (pseq ->seq[i].Description != NULL) cmsMLUfree(pseq ->seq[i].Description); + } + + if (pseq ->seq != NULL) _cmsFree(pseq ->ContextID, pseq ->seq); + _cmsFree(pseq -> ContextID, pseq); +} + +cmsSEQ* CMSEXPORT cmsDupProfileSequenceDescription(const cmsSEQ* pseq) +{ + cmsSEQ *NewSeq; + cmsUInt32Number i; + + if (pseq == NULL) + return NULL; + + NewSeq = (cmsSEQ*) _cmsMalloc(pseq -> ContextID, sizeof(cmsSEQ)); + if (NewSeq == NULL) return NULL; + + + NewSeq -> seq = (cmsPSEQDESC*) _cmsCalloc(pseq ->ContextID, pseq ->n, sizeof(cmsPSEQDESC)); + if (NewSeq ->seq == NULL) goto Error; + + NewSeq -> ContextID = pseq ->ContextID; + NewSeq -> n = pseq ->n; + + for (i=0; i < pseq->n; i++) { + + memmove(&NewSeq ->seq[i].attributes, &pseq ->seq[i].attributes, sizeof(cmsUInt64Number)); + + NewSeq ->seq[i].deviceMfg = pseq ->seq[i].deviceMfg; + NewSeq ->seq[i].deviceModel = pseq ->seq[i].deviceModel; + memmove(&NewSeq ->seq[i].ProfileID, &pseq ->seq[i].ProfileID, sizeof(cmsProfileID)); + NewSeq ->seq[i].technology = pseq ->seq[i].technology; + + NewSeq ->seq[i].Manufacturer = cmsMLUdup(pseq ->seq[i].Manufacturer); + NewSeq ->seq[i].Model = cmsMLUdup(pseq ->seq[i].Model); + NewSeq ->seq[i].Description = cmsMLUdup(pseq ->seq[i].Description); + + } + + return NewSeq; + +Error: + + cmsFreeProfileSequenceDescription(NewSeq); + return NULL; +} + +// Dictionaries -------------------------------------------------------------------------------------------------------- + +// Dictionaries are just very simple linked lists + + +typedef struct _cmsDICT_struct { + cmsDICTentry* head; + cmsContext ContextID; +} _cmsDICT; + + +// Allocate an empty dictionary +cmsHANDLE CMSEXPORT cmsDictAlloc(cmsContext ContextID) +{ + _cmsDICT* dict = (_cmsDICT*) _cmsMallocZero(ContextID, sizeof(_cmsDICT)); + if (dict == NULL) return NULL; + + dict ->ContextID = ContextID; + return (cmsHANDLE) dict; + +} + +// Dispose resources +void CMSEXPORT cmsDictFree(cmsHANDLE hDict) +{ + _cmsDICT* dict = (_cmsDICT*) hDict; + cmsDICTentry *entry, *next; + + _cmsAssert(dict != NULL); + + // Walk the list freeing all nodes + entry = dict ->head; + while (entry != NULL) { + + if (entry ->DisplayName != NULL) cmsMLUfree(entry ->DisplayName); + if (entry ->DisplayValue != NULL) cmsMLUfree(entry ->DisplayValue); + if (entry ->Name != NULL) _cmsFree(dict ->ContextID, entry -> Name); + if (entry ->Value != NULL) _cmsFree(dict ->ContextID, entry -> Value); + + // Don't fall in the habitual trap... + next = entry ->Next; + _cmsFree(dict ->ContextID, entry); + + entry = next; + } + + _cmsFree(dict ->ContextID, dict); +} + + +// Duplicate a wide char string +static +wchar_t* DupWcs(cmsContext ContextID, const wchar_t* ptr) +{ + if (ptr == NULL) return NULL; + return (wchar_t*) _cmsDupMem(ContextID, ptr, (mywcslen(ptr) + 1) * sizeof(wchar_t)); +} + +// Add a new entry to the linked list +cmsBool CMSEXPORT cmsDictAddEntry(cmsHANDLE hDict, const wchar_t* Name, const wchar_t* Value, const cmsMLU *DisplayName, const cmsMLU *DisplayValue) +{ + _cmsDICT* dict = (_cmsDICT*) hDict; + cmsDICTentry *entry; + + _cmsAssert(dict != NULL); + _cmsAssert(Name != NULL); + + entry = (cmsDICTentry*) _cmsMallocZero(dict ->ContextID, sizeof(cmsDICTentry)); + if (entry == NULL) return FALSE; + + entry ->DisplayName = cmsMLUdup(DisplayName); + entry ->DisplayValue = cmsMLUdup(DisplayValue); + entry ->Name = DupWcs(dict ->ContextID, Name); + entry ->Value = DupWcs(dict ->ContextID, Value); + + entry ->Next = dict ->head; + dict ->head = entry; + + return TRUE; +} + + +// Duplicates an existing dictionary +cmsHANDLE CMSEXPORT cmsDictDup(cmsHANDLE hDict) +{ + _cmsDICT* old_dict = (_cmsDICT*) hDict; + cmsHANDLE hNew; + cmsDICTentry *entry; + + _cmsAssert(old_dict != NULL); + + hNew = cmsDictAlloc(old_dict ->ContextID); + if (hNew == NULL) return NULL; + + // Walk the list freeing all nodes + entry = old_dict ->head; + while (entry != NULL) { + + if (!cmsDictAddEntry(hNew, entry ->Name, entry ->Value, entry ->DisplayName, entry ->DisplayValue)) { + + cmsDictFree(hNew); + return NULL; + } + + entry = entry -> Next; + } + + return hNew; +} + +// Get a pointer to the linked list +const cmsDICTentry* CMSEXPORT cmsDictGetEntryList(cmsHANDLE hDict) +{ + _cmsDICT* dict = (_cmsDICT*) hDict; + + if (dict == NULL) return NULL; + return dict ->head; +} + +// Helper For external languages +const cmsDICTentry* CMSEXPORT cmsDictNextEntry(const cmsDICTentry* e) +{ + if (e == NULL) return NULL; + return e ->Next; +} diff --git a/third_party/lcms/src/cmsopt.c b/third_party/lcms/src/cmsopt.c new file mode 100644 index 0000000000..76de01554c --- /dev/null +++ b/third_party/lcms/src/cmsopt.c @@ -0,0 +1,1811 @@ +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2011 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" + + +//---------------------------------------------------------------------------------- + +// Optimization for 8 bits, Shaper-CLUT (3 inputs only) +typedef struct { + + cmsContext ContextID; + + const cmsInterpParams* p; // Tetrahedrical interpolation parameters. This is a not-owned pointer. + + cmsUInt16Number rx[256], ry[256], rz[256]; + cmsUInt32Number X0[256], Y0[256], Z0[256]; // Precomputed nodes and offsets for 8-bit input data + + +} Prelin8Data; + + +// Generic optimization for 16 bits Shaper-CLUT-Shaper (any inputs) +typedef struct { + + cmsContext ContextID; + + // Number of channels + int nInputs; + int nOutputs; + + _cmsInterpFn16 EvalCurveIn16[MAX_INPUT_DIMENSIONS]; // The maximum number of input channels is known in advance + cmsInterpParams* ParamsCurveIn16[MAX_INPUT_DIMENSIONS]; + + _cmsInterpFn16 EvalCLUT; // The evaluator for 3D grid + const cmsInterpParams* CLUTparams; // (not-owned pointer) + + + _cmsInterpFn16* EvalCurveOut16; // Points to an array of curve evaluators in 16 bits (not-owned pointer) + cmsInterpParams** ParamsCurveOut16; // Points to an array of references to interpolation params (not-owned pointer) + + +} Prelin16Data; + + +// Optimization for matrix-shaper in 8 bits. Numbers are operated in n.14 signed, tables are stored in 1.14 fixed + +typedef cmsInt32Number cmsS1Fixed14Number; // Note that this may hold more than 16 bits! + +#define DOUBLE_TO_1FIXED14(x) ((cmsS1Fixed14Number) floor((x) * 16384.0 + 0.5)) + +typedef struct { + + cmsContext ContextID; + + cmsS1Fixed14Number Shaper1R[256]; // from 0..255 to 1.14 (0.0...1.0) + cmsS1Fixed14Number Shaper1G[256]; + cmsS1Fixed14Number Shaper1B[256]; + + cmsS1Fixed14Number Mat[3][3]; // n.14 to n.14 (needs a saturation after that) + cmsS1Fixed14Number Off[3]; + + cmsUInt16Number Shaper2R[16385]; // 1.14 to 0..255 + cmsUInt16Number Shaper2G[16385]; + cmsUInt16Number Shaper2B[16385]; + +} MatShaper8Data; + +// Curves, optimization is shared between 8 and 16 bits +typedef struct { + + cmsContext ContextID; + + int nCurves; // Number of curves + int nElements; // Elements in curves + cmsUInt16Number** Curves; // Points to a dynamically allocated array + +} Curves16Data; + + +// Simple optimizations ---------------------------------------------------------------------------------------------------------- + + +// Remove an element in linked chain +static +void _RemoveElement(cmsStage** head) +{ + cmsStage* mpe = *head; + cmsStage* next = mpe ->Next; + *head = next; + cmsStageFree(mpe); +} + +// Remove all identities in chain. Note that pt actually is a double pointer to the element that holds the pointer. +static +cmsBool _Remove1Op(cmsPipeline* Lut, cmsStageSignature UnaryOp) +{ + cmsStage** pt = &Lut ->Elements; + cmsBool AnyOpt = FALSE; + + while (*pt != NULL) { + + if ((*pt) ->Implements == UnaryOp) { + _RemoveElement(pt); + AnyOpt = TRUE; + } + else + pt = &((*pt) -> Next); + } + + return AnyOpt; +} + +// Same, but only if two adjacent elements are found +static +cmsBool _Remove2Op(cmsPipeline* Lut, cmsStageSignature Op1, cmsStageSignature Op2) +{ + cmsStage** pt1; + cmsStage** pt2; + cmsBool AnyOpt = FALSE; + + pt1 = &Lut ->Elements; + if (*pt1 == NULL) return AnyOpt; + + while (*pt1 != NULL) { + + pt2 = &((*pt1) -> Next); + if (*pt2 == NULL) return AnyOpt; + + if ((*pt1) ->Implements == Op1 && (*pt2) ->Implements == Op2) { + _RemoveElement(pt2); + _RemoveElement(pt1); + AnyOpt = TRUE; + } + else + pt1 = &((*pt1) -> Next); + } + + return AnyOpt; +} + +// Preoptimize just gets rif of no-ops coming paired. Conversion from v2 to v4 followed +// by a v4 to v2 and vice-versa. The elements are then discarded. +static +cmsBool PreOptimize(cmsPipeline* Lut) +{ + cmsBool AnyOpt = FALSE, Opt; + + do { + + Opt = FALSE; + + // Remove all identities + Opt |= _Remove1Op(Lut, cmsSigIdentityElemType); + + // Remove XYZ2Lab followed by Lab2XYZ + Opt |= _Remove2Op(Lut, cmsSigXYZ2LabElemType, cmsSigLab2XYZElemType); + + // Remove Lab2XYZ followed by XYZ2Lab + Opt |= _Remove2Op(Lut, cmsSigLab2XYZElemType, cmsSigXYZ2LabElemType); + + // Remove V4 to V2 followed by V2 to V4 + Opt |= _Remove2Op(Lut, cmsSigLabV4toV2, cmsSigLabV2toV4); + + // Remove V2 to V4 followed by V4 to V2 + Opt |= _Remove2Op(Lut, cmsSigLabV2toV4, cmsSigLabV4toV2); + + // Remove float pcs Lab conversions + Opt |= _Remove2Op(Lut, cmsSigLab2FloatPCS, cmsSigFloatPCS2Lab); + + // Remove float pcs Lab conversions + Opt |= _Remove2Op(Lut, cmsSigXYZ2FloatPCS, cmsSigFloatPCS2XYZ); + + if (Opt) AnyOpt = TRUE; + + } while (Opt); + + return AnyOpt; +} + +static +void Eval16nop1D(register const cmsUInt16Number Input[], + register cmsUInt16Number Output[], + register const struct _cms_interp_struc* p) +{ + Output[0] = Input[0]; + + cmsUNUSED_PARAMETER(p); +} + +static +void PrelinEval16(register const cmsUInt16Number Input[], + register cmsUInt16Number Output[], + register const void* D) +{ + Prelin16Data* p16 = (Prelin16Data*) D; + cmsUInt16Number StageABC[MAX_INPUT_DIMENSIONS]; + cmsUInt16Number StageDEF[cmsMAXCHANNELS]; + int i; + + for (i=0; i < p16 ->nInputs; i++) { + + p16 ->EvalCurveIn16[i](&Input[i], &StageABC[i], p16 ->ParamsCurveIn16[i]); + } + + p16 ->EvalCLUT(StageABC, StageDEF, p16 ->CLUTparams); + + for (i=0; i < p16 ->nOutputs; i++) { + + p16 ->EvalCurveOut16[i](&StageDEF[i], &Output[i], p16 ->ParamsCurveOut16[i]); + } +} + + +static +void PrelinOpt16free(cmsContext ContextID, void* ptr) +{ + Prelin16Data* p16 = (Prelin16Data*) ptr; + + _cmsFree(ContextID, p16 ->EvalCurveOut16); + _cmsFree(ContextID, p16 ->ParamsCurveOut16); + + _cmsFree(ContextID, p16); +} + +static +void* Prelin16dup(cmsContext ContextID, const void* ptr) +{ + Prelin16Data* p16 = (Prelin16Data*) ptr; + Prelin16Data* Duped = _cmsDupMem(ContextID, p16, sizeof(Prelin16Data)); + + if (Duped == NULL) return NULL; + + Duped ->EvalCurveOut16 = (_cmsInterpFn16*)_cmsDupMem(ContextID, p16 ->EvalCurveOut16, p16 ->nOutputs * sizeof(_cmsInterpFn16)); + Duped ->ParamsCurveOut16 = (cmsInterpParams**)_cmsDupMem(ContextID, p16 ->ParamsCurveOut16, p16 ->nOutputs * sizeof(cmsInterpParams* )); + + return Duped; +} + + +static +Prelin16Data* PrelinOpt16alloc(cmsContext ContextID, + const cmsInterpParams* ColorMap, + int nInputs, cmsToneCurve** In, + int nOutputs, cmsToneCurve** Out ) +{ + int i; + Prelin16Data* p16 = _cmsMallocZero(ContextID, sizeof(Prelin16Data)); + if (p16 == NULL) return NULL; + + p16 ->nInputs = nInputs; + p16 -> nOutputs = nOutputs; + + + for (i=0; i < nInputs; i++) { + + if (In == NULL) { + p16 -> ParamsCurveIn16[i] = NULL; + p16 -> EvalCurveIn16[i] = Eval16nop1D; + + } + else { + p16 -> ParamsCurveIn16[i] = In[i] ->InterpParams; + p16 -> EvalCurveIn16[i] = p16 ->ParamsCurveIn16[i]->Interpolation.Lerp16; + } + } + + p16 ->CLUTparams = ColorMap; + p16 ->EvalCLUT = ColorMap ->Interpolation.Lerp16; + + + p16 -> EvalCurveOut16 = (_cmsInterpFn16*) _cmsCalloc(ContextID, nOutputs, sizeof(_cmsInterpFn16)); + p16 -> ParamsCurveOut16 = (cmsInterpParams**) _cmsCalloc(ContextID, nOutputs, sizeof(cmsInterpParams* )); + + for (i=0; i < nOutputs; i++) { + + if (Out == NULL) { + p16 ->ParamsCurveOut16[i] = NULL; + p16 -> EvalCurveOut16[i] = Eval16nop1D; + } + else { + + p16 ->ParamsCurveOut16[i] = Out[i] ->InterpParams; + p16 -> EvalCurveOut16[i] = p16 ->ParamsCurveOut16[i]->Interpolation.Lerp16; + } + } + + return p16; +} + + + +// Resampling --------------------------------------------------------------------------------- + +#define PRELINEARIZATION_POINTS 4096 + +// Sampler implemented by another LUT. This is a clean way to precalculate the devicelink 3D CLUT for +// almost any transform. We use floating point precision and then convert from floating point to 16 bits. +static +int XFormSampler16(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo) +{ + cmsPipeline* Lut = (cmsPipeline*) Cargo; + cmsFloat32Number InFloat[cmsMAXCHANNELS], OutFloat[cmsMAXCHANNELS]; + cmsUInt32Number i; + + _cmsAssert(Lut -> InputChannels < cmsMAXCHANNELS); + _cmsAssert(Lut -> OutputChannels < cmsMAXCHANNELS); + + // From 16 bit to floating point + for (i=0; i < Lut ->InputChannels; i++) + InFloat[i] = (cmsFloat32Number) (In[i] / 65535.0); + + // Evaluate in floating point + cmsPipelineEvalFloat(InFloat, OutFloat, Lut); + + // Back to 16 bits representation + for (i=0; i < Lut ->OutputChannels; i++) + Out[i] = _cmsQuickSaturateWord(OutFloat[i] * 65535.0); + + // Always succeed + return TRUE; +} + +// Try to see if the curves of a given MPE are linear +static +cmsBool AllCurvesAreLinear(cmsStage* mpe) +{ + cmsToneCurve** Curves; + cmsUInt32Number i, n; + + Curves = _cmsStageGetPtrToCurveSet(mpe); + if (Curves == NULL) return FALSE; + + n = cmsStageOutputChannels(mpe); + + for (i=0; i < n; i++) { + if (!cmsIsToneCurveLinear(Curves[i])) return FALSE; + } + + return TRUE; +} + +// This function replaces a specific node placed in "At" by the "Value" numbers. Its purpose +// is to fix scum dot on broken profiles/transforms. Works on 1, 3 and 4 channels +static +cmsBool PatchLUT(cmsStage* CLUT, cmsUInt16Number At[], cmsUInt16Number Value[], + int nChannelsOut, int nChannelsIn) +{ + _cmsStageCLutData* Grid = (_cmsStageCLutData*) CLUT ->Data; + cmsInterpParams* p16 = Grid ->Params; + cmsFloat64Number px, py, pz, pw; + int x0, y0, z0, w0; + int i, index; + + if (CLUT -> Type != cmsSigCLutElemType) { + cmsSignalError(CLUT->ContextID, cmsERROR_INTERNAL, "(internal) Attempt to PatchLUT on non-lut stage"); + return FALSE; + } + + if (nChannelsIn != 1 && nChannelsIn != 3 && nChannelsIn != 4) { + cmsSignalError(CLUT->ContextID, cmsERROR_INTERNAL, "(internal) %d Channels are not supported on PatchLUT", nChannelsIn); + return FALSE; + } + if (nChannelsIn == 4) { + + px = ((cmsFloat64Number) At[0] * (p16->Domain[0])) / 65535.0; + py = ((cmsFloat64Number) At[1] * (p16->Domain[1])) / 65535.0; + pz = ((cmsFloat64Number) At[2] * (p16->Domain[2])) / 65535.0; + pw = ((cmsFloat64Number) At[3] * (p16->Domain[3])) / 65535.0; + + x0 = (int) floor(px); + y0 = (int) floor(py); + z0 = (int) floor(pz); + w0 = (int) floor(pw); + + if (((px - x0) != 0) || + ((py - y0) != 0) || + ((pz - z0) != 0) || + ((pw - w0) != 0)) return FALSE; // Not on exact node + + index = p16 -> opta[3] * x0 + + p16 -> opta[2] * y0 + + p16 -> opta[1] * z0 + + p16 -> opta[0] * w0; + } + else + if (nChannelsIn == 3) { + + px = ((cmsFloat64Number) At[0] * (p16->Domain[0])) / 65535.0; + py = ((cmsFloat64Number) At[1] * (p16->Domain[1])) / 65535.0; + pz = ((cmsFloat64Number) At[2] * (p16->Domain[2])) / 65535.0; + + x0 = (int) floor(px); + y0 = (int) floor(py); + z0 = (int) floor(pz); + + if (((px - x0) != 0) || + ((py - y0) != 0) || + ((pz - z0) != 0)) return FALSE; // Not on exact node + + index = p16 -> opta[2] * x0 + + p16 -> opta[1] * y0 + + p16 -> opta[0] * z0; + } + else + if (nChannelsIn == 1) { + + px = ((cmsFloat64Number) At[0] * (p16->Domain[0])) / 65535.0; + + x0 = (int) floor(px); + + if (((px - x0) != 0)) return FALSE; // Not on exact node + + index = p16 -> opta[0] * x0; + } + else { + cmsSignalError(CLUT->ContextID, cmsERROR_INTERNAL, "(internal) %d Channels are not supported on PatchLUT", nChannelsIn); + return FALSE; + } + + for (i=0; i < nChannelsOut; i++) + Grid -> Tab.T[index + i] = Value[i]; + + return TRUE; +} + +// Auxiliar, to see if two values are equal or very different +static +cmsBool WhitesAreEqual(int n, cmsUInt16Number White1[], cmsUInt16Number White2[] ) +{ + int i; + + for (i=0; i < n; i++) { + + if (abs(White1[i] - White2[i]) > 0xf000) return TRUE; // Values are so extremly different that the fixup should be avoided + if (White1[i] != White2[i]) return FALSE; + } + return TRUE; +} + + +// Locate the node for the white point and fix it to pure white in order to avoid scum dot. +static +cmsBool FixWhiteMisalignment(cmsPipeline* Lut, cmsColorSpaceSignature EntryColorSpace, cmsColorSpaceSignature ExitColorSpace) +{ + cmsUInt16Number *WhitePointIn, *WhitePointOut; + cmsUInt16Number WhiteIn[cmsMAXCHANNELS], WhiteOut[cmsMAXCHANNELS], ObtainedOut[cmsMAXCHANNELS]; + cmsUInt32Number i, nOuts, nIns; + cmsStage *PreLin = NULL, *CLUT = NULL, *PostLin = NULL; + + if (!_cmsEndPointsBySpace(EntryColorSpace, + &WhitePointIn, NULL, &nIns)) return FALSE; + + if (!_cmsEndPointsBySpace(ExitColorSpace, + &WhitePointOut, NULL, &nOuts)) return FALSE; + + // It needs to be fixed? + if (Lut ->InputChannels != nIns) return FALSE; + if (Lut ->OutputChannels != nOuts) return FALSE; + + cmsPipelineEval16(WhitePointIn, ObtainedOut, Lut); + + if (WhitesAreEqual(nOuts, WhitePointOut, ObtainedOut)) return TRUE; // whites already match + + // Check if the LUT comes as Prelin, CLUT or Postlin. We allow all combinations + if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, &PreLin, &CLUT, &PostLin)) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 2, cmsSigCurveSetElemType, cmsSigCLutElemType, &PreLin, &CLUT)) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 2, cmsSigCLutElemType, cmsSigCurveSetElemType, &CLUT, &PostLin)) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 1, cmsSigCLutElemType, &CLUT)) + return FALSE; + + // We need to interpolate white points of both, pre and post curves + if (PreLin) { + + cmsToneCurve** Curves = _cmsStageGetPtrToCurveSet(PreLin); + + for (i=0; i < nIns; i++) { + WhiteIn[i] = cmsEvalToneCurve16(Curves[i], WhitePointIn[i]); + } + } + else { + for (i=0; i < nIns; i++) + WhiteIn[i] = WhitePointIn[i]; + } + + // If any post-linearization, we need to find how is represented white before the curve, do + // a reverse interpolation in this case. + if (PostLin) { + + cmsToneCurve** Curves = _cmsStageGetPtrToCurveSet(PostLin); + + for (i=0; i < nOuts; i++) { + + cmsToneCurve* InversePostLin = cmsReverseToneCurve(Curves[i]); + if (InversePostLin == NULL) { + WhiteOut[i] = WhitePointOut[i]; + + } else { + + WhiteOut[i] = cmsEvalToneCurve16(InversePostLin, WhitePointOut[i]); + cmsFreeToneCurve(InversePostLin); + } + } + } + else { + for (i=0; i < nOuts; i++) + WhiteOut[i] = WhitePointOut[i]; + } + + // Ok, proceed with patching. May fail and we don't care if it fails + PatchLUT(CLUT, WhiteIn, WhiteOut, nOuts, nIns); + + return TRUE; +} + +// ----------------------------------------------------------------------------------------------------------------------------------------------- +// This function creates simple LUT from complex ones. The generated LUT has an optional set of +// prelinearization curves, a CLUT of nGridPoints and optional postlinearization tables. +// These curves have to exist in the original LUT in order to be used in the simplified output. +// Caller may also use the flags to allow this feature. +// LUTS with all curves will be simplified to a single curve. Parametric curves are lost. +// This function should be used on 16-bits LUTS only, as floating point losses precision when simplified +// ----------------------------------------------------------------------------------------------------------------------------------------------- + +static +cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) +{ + cmsPipeline* Src = NULL; + cmsPipeline* Dest = NULL; + cmsStage* mpe; + cmsStage* CLUT; + cmsStage *KeepPreLin = NULL, *KeepPostLin = NULL; + int nGridPoints; + cmsColorSpaceSignature ColorSpace, OutputColorSpace; + cmsStage *NewPreLin = NULL; + cmsStage *NewPostLin = NULL; + _cmsStageCLutData* DataCLUT; + cmsToneCurve** DataSetIn; + cmsToneCurve** DataSetOut; + Prelin16Data* p16; + + // This is a loosy optimization! does not apply in floating-point cases + if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) return FALSE; + + ColorSpace = _cmsICCcolorSpace(T_COLORSPACE(*InputFormat)); + OutputColorSpace = _cmsICCcolorSpace(T_COLORSPACE(*OutputFormat)); + nGridPoints = _cmsReasonableGridpointsByColorspace(ColorSpace, *dwFlags); + + // For empty LUTs, 2 points are enough + if (cmsPipelineStageCount(*Lut) == 0) + nGridPoints = 2; + + Src = *Lut; + + // Named color pipelines cannot be optimized either + for (mpe = cmsPipelineGetPtrToFirstStage(Src); + mpe != NULL; + mpe = cmsStageNext(mpe)) { + if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE; + } + + // Allocate an empty LUT + Dest = cmsPipelineAlloc(Src ->ContextID, Src ->InputChannels, Src ->OutputChannels); + if (!Dest) return FALSE; + + // Prelinearization tables are kept unless indicated by flags + if (*dwFlags & cmsFLAGS_CLUT_PRE_LINEARIZATION) { + + // Get a pointer to the prelinearization element + cmsStage* PreLin = cmsPipelineGetPtrToFirstStage(Src); + + // Check if suitable + if (PreLin ->Type == cmsSigCurveSetElemType) { + + // Maybe this is a linear tram, so we can avoid the whole stuff + if (!AllCurvesAreLinear(PreLin)) { + + // All seems ok, proceed. + NewPreLin = cmsStageDup(PreLin); + if(!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, NewPreLin)) + goto Error; + + // Remove prelinearization. Since we have duplicated the curve + // in destination LUT, the sampling shoud be applied after this stage. + cmsPipelineUnlinkStage(Src, cmsAT_BEGIN, &KeepPreLin); + } + } + } + + // Allocate the CLUT + CLUT = cmsStageAllocCLut16bit(Src ->ContextID, nGridPoints, Src ->InputChannels, Src->OutputChannels, NULL); + if (CLUT == NULL) goto Error; + + // Add the CLUT to the destination LUT + if (!cmsPipelineInsertStage(Dest, cmsAT_END, CLUT)) { + goto Error; + } + + // Postlinearization tables are kept unless indicated by flags + if (*dwFlags & cmsFLAGS_CLUT_POST_LINEARIZATION) { + + // Get a pointer to the postlinearization if present + cmsStage* PostLin = cmsPipelineGetPtrToLastStage(Src); + + // Check if suitable + if (cmsStageType(PostLin) == cmsSigCurveSetElemType) { + + // Maybe this is a linear tram, so we can avoid the whole stuff + if (!AllCurvesAreLinear(PostLin)) { + + // All seems ok, proceed. + NewPostLin = cmsStageDup(PostLin); + if (!cmsPipelineInsertStage(Dest, cmsAT_END, NewPostLin)) + goto Error; + + // In destination LUT, the sampling shoud be applied after this stage. + cmsPipelineUnlinkStage(Src, cmsAT_END, &KeepPostLin); + } + } + } + + // Now its time to do the sampling. We have to ignore pre/post linearization + // The source LUT whithout pre/post curves is passed as parameter. + if (!cmsStageSampleCLut16bit(CLUT, XFormSampler16, (void*) Src, 0)) { +Error: + // Ops, something went wrong, Restore stages + if (KeepPreLin != NULL) { + if (!cmsPipelineInsertStage(Src, cmsAT_BEGIN, KeepPreLin)) { + _cmsAssert(0); // This never happens + } + } + if (KeepPostLin != NULL) { + if (!cmsPipelineInsertStage(Src, cmsAT_END, KeepPostLin)) { + _cmsAssert(0); // This never happens + } + } + cmsPipelineFree(Dest); + return FALSE; + } + + // Done. + + if (KeepPreLin != NULL) cmsStageFree(KeepPreLin); + if (KeepPostLin != NULL) cmsStageFree(KeepPostLin); + cmsPipelineFree(Src); + + DataCLUT = (_cmsStageCLutData*) CLUT ->Data; + + if (NewPreLin == NULL) DataSetIn = NULL; + else DataSetIn = ((_cmsStageToneCurvesData*) NewPreLin ->Data) ->TheCurves; + + if (NewPostLin == NULL) DataSetOut = NULL; + else DataSetOut = ((_cmsStageToneCurvesData*) NewPostLin ->Data) ->TheCurves; + + + if (DataSetIn == NULL && DataSetOut == NULL) { + + _cmsPipelineSetOptimizationParameters(Dest, (_cmsOPTeval16Fn) DataCLUT->Params->Interpolation.Lerp16, DataCLUT->Params, NULL, NULL); + } + else { + + p16 = PrelinOpt16alloc(Dest ->ContextID, + DataCLUT ->Params, + Dest ->InputChannels, + DataSetIn, + Dest ->OutputChannels, + DataSetOut); + + _cmsPipelineSetOptimizationParameters(Dest, PrelinEval16, (void*) p16, PrelinOpt16free, Prelin16dup); + } + + + // Don't fix white on absolute colorimetric + if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) + *dwFlags |= cmsFLAGS_NOWHITEONWHITEFIXUP; + + if (!(*dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP)) { + + FixWhiteMisalignment(Dest, ColorSpace, OutputColorSpace); + } + + *Lut = Dest; + return TRUE; + + cmsUNUSED_PARAMETER(Intent); +} + + +// ----------------------------------------------------------------------------------------------------------------------------------------------- +// Fixes the gamma balancing of transform. This is described in my paper "Prelinearization Stages on +// Color-Management Application-Specific Integrated Circuits (ASICs)" presented at NIP24. It only works +// for RGB transforms. See the paper for more details +// ----------------------------------------------------------------------------------------------------------------------------------------------- + + +// Normalize endpoints by slope limiting max and min. This assures endpoints as well. +// Descending curves are handled as well. +static +void SlopeLimiting(cmsToneCurve* g) +{ + int BeginVal, EndVal; + int AtBegin = (int) floor((cmsFloat64Number) g ->nEntries * 0.02 + 0.5); // Cutoff at 2% + int AtEnd = g ->nEntries - AtBegin - 1; // And 98% + cmsFloat64Number Val, Slope, beta; + int i; + + if (cmsIsToneCurveDescending(g)) { + BeginVal = 0xffff; EndVal = 0; + } + else { + BeginVal = 0; EndVal = 0xffff; + } + + // Compute slope and offset for begin of curve + Val = g ->Table16[AtBegin]; + Slope = (Val - BeginVal) / AtBegin; + beta = Val - Slope * AtBegin; + + for (i=0; i < AtBegin; i++) + g ->Table16[i] = _cmsQuickSaturateWord(i * Slope + beta); + + // Compute slope and offset for the end + Val = g ->Table16[AtEnd]; + Slope = (EndVal - Val) / AtBegin; // AtBegin holds the X interval, which is same in both cases + beta = Val - Slope * AtEnd; + + for (i = AtEnd; i < (int) g ->nEntries; i++) + g ->Table16[i] = _cmsQuickSaturateWord(i * Slope + beta); +} + + +// Precomputes tables for 8-bit on input devicelink. +static +Prelin8Data* PrelinOpt8alloc(cmsContext ContextID, const cmsInterpParams* p, cmsToneCurve* G[3]) +{ + int i; + cmsUInt16Number Input[3]; + cmsS15Fixed16Number v1, v2, v3; + Prelin8Data* p8; + + p8 = (Prelin8Data*)_cmsMallocZero(ContextID, sizeof(Prelin8Data)); + if (p8 == NULL) return NULL; + + // Since this only works for 8 bit input, values comes always as x * 257, + // we can safely take msb byte (x << 8 + x) + + for (i=0; i < 256; i++) { + + if (G != NULL) { + + // Get 16-bit representation + Input[0] = cmsEvalToneCurve16(G[0], FROM_8_TO_16(i)); + Input[1] = cmsEvalToneCurve16(G[1], FROM_8_TO_16(i)); + Input[2] = cmsEvalToneCurve16(G[2], FROM_8_TO_16(i)); + } + else { + Input[0] = FROM_8_TO_16(i); + Input[1] = FROM_8_TO_16(i); + Input[2] = FROM_8_TO_16(i); + } + + + // Move to 0..1.0 in fixed domain + v1 = _cmsToFixedDomain(Input[0] * p -> Domain[0]); + v2 = _cmsToFixedDomain(Input[1] * p -> Domain[1]); + v3 = _cmsToFixedDomain(Input[2] * p -> Domain[2]); + + // Store the precalculated table of nodes + p8 ->X0[i] = (p->opta[2] * FIXED_TO_INT(v1)); + p8 ->Y0[i] = (p->opta[1] * FIXED_TO_INT(v2)); + p8 ->Z0[i] = (p->opta[0] * FIXED_TO_INT(v3)); + + // Store the precalculated table of offsets + p8 ->rx[i] = (cmsUInt16Number) FIXED_REST_TO_INT(v1); + p8 ->ry[i] = (cmsUInt16Number) FIXED_REST_TO_INT(v2); + p8 ->rz[i] = (cmsUInt16Number) FIXED_REST_TO_INT(v3); + } + + p8 ->ContextID = ContextID; + p8 ->p = p; + + return p8; +} + +static +void Prelin8free(cmsContext ContextID, void* ptr) +{ + _cmsFree(ContextID, ptr); +} + +static +void* Prelin8dup(cmsContext ContextID, const void* ptr) +{ + return _cmsDupMem(ContextID, ptr, sizeof(Prelin8Data)); +} + + + +// A optimized interpolation for 8-bit input. +#define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan]) +static +void PrelinEval8(register const cmsUInt16Number Input[], + register cmsUInt16Number Output[], + register const void* D) +{ + + cmsUInt8Number r, g, b; + cmsS15Fixed16Number rx, ry, rz; + cmsS15Fixed16Number c0, c1, c2, c3, Rest; + int OutChan; + register cmsS15Fixed16Number X0, X1, Y0, Y1, Z0, Z1; + Prelin8Data* p8 = (Prelin8Data*) D; + register const cmsInterpParams* p = p8 ->p; + int TotalOut = p -> nOutputs; + const cmsUInt16Number* LutTable = (const cmsUInt16Number*)p -> Table; + + r = Input[0] >> 8; + g = Input[1] >> 8; + b = Input[2] >> 8; + + X0 = X1 = p8->X0[r]; + Y0 = Y1 = p8->Y0[g]; + Z0 = Z1 = p8->Z0[b]; + + rx = p8 ->rx[r]; + ry = p8 ->ry[g]; + rz = p8 ->rz[b]; + + X1 = X0 + ((rx == 0) ? 0 : p ->opta[2]); + Y1 = Y0 + ((ry == 0) ? 0 : p ->opta[1]); + Z1 = Z0 + ((rz == 0) ? 0 : p ->opta[0]); + + + // These are the 6 Tetrahedral + for (OutChan=0; OutChan < TotalOut; OutChan++) { + + c0 = DENS(X0, Y0, Z0); + + if (rx >= ry && ry >= rz) + { + c1 = DENS(X1, Y0, Z0) - c0; + c2 = DENS(X1, Y1, Z0) - DENS(X1, Y0, Z0); + c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + } + else + if (rx >= rz && rz >= ry) + { + c1 = DENS(X1, Y0, Z0) - c0; + c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); + c3 = DENS(X1, Y0, Z1) - DENS(X1, Y0, Z0); + } + else + if (rz >= rx && rx >= ry) + { + c1 = DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1); + c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); + c3 = DENS(X0, Y0, Z1) - c0; + } + else + if (ry >= rx && rx >= rz) + { + c1 = DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0); + c2 = DENS(X0, Y1, Z0) - c0; + c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); + } + else + if (ry >= rz && rz >= rx) + { + c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); + c2 = DENS(X0, Y1, Z0) - c0; + c3 = DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0); + } + else + if (rz >= ry && ry >= rx) + { + c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); + c2 = DENS(X0, Y1, Z1) - DENS(X0, Y0, Z1); + c3 = DENS(X0, Y0, Z1) - c0; + } + else { + c1 = c2 = c3 = 0; + } + + + Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; + Output[OutChan] = (cmsUInt16Number)c0 + ((Rest + (Rest>>16))>>16); + + } +} + +#undef DENS + + +// Curves that contain wide empty areas are not optimizeable +static +cmsBool IsDegenerated(const cmsToneCurve* g) +{ + int i, Zeros = 0, Poles = 0; + int nEntries = g ->nEntries; + + for (i=0; i < nEntries; i++) { + + if (g ->Table16[i] == 0x0000) Zeros++; + if (g ->Table16[i] == 0xffff) Poles++; + } + + if (Zeros == 1 && Poles == 1) return FALSE; // For linear tables + if (Zeros > (nEntries / 4)) return TRUE; // Degenerated, mostly zeros + if (Poles > (nEntries / 4)) return TRUE; // Degenerated, mostly poles + + return FALSE; +} + +// -------------------------------------------------------------------------------------------------------------- +// We need xput over here + +static +cmsBool OptimizeByComputingLinearization(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) +{ + cmsPipeline* OriginalLut; + int nGridPoints; + cmsToneCurve *Trans[cmsMAXCHANNELS], *TransReverse[cmsMAXCHANNELS]; + cmsUInt32Number t, i; + cmsFloat32Number v, In[cmsMAXCHANNELS], Out[cmsMAXCHANNELS]; + cmsBool lIsSuitable, lIsLinear; + cmsPipeline* OptimizedLUT = NULL, *LutPlusCurves = NULL; + cmsStage* OptimizedCLUTmpe; + cmsColorSpaceSignature ColorSpace, OutputColorSpace; + cmsStage* OptimizedPrelinMpe; + cmsStage* mpe; + cmsToneCurve** OptimizedPrelinCurves; + _cmsStageCLutData* OptimizedPrelinCLUT; + + + // This is a loosy optimization! does not apply in floating-point cases + if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) return FALSE; + + // Only on RGB + if (T_COLORSPACE(*InputFormat) != PT_RGB) return FALSE; + if (T_COLORSPACE(*OutputFormat) != PT_RGB) return FALSE; + + + // On 16 bits, user has to specify the feature + if (!_cmsFormatterIs8bit(*InputFormat)) { + if (!(*dwFlags & cmsFLAGS_CLUT_PRE_LINEARIZATION)) return FALSE; + } + + OriginalLut = *Lut; + + // Named color pipelines cannot be optimized either + for (mpe = cmsPipelineGetPtrToFirstStage(OriginalLut); + mpe != NULL; + mpe = cmsStageNext(mpe)) { + if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE; + } + + ColorSpace = _cmsICCcolorSpace(T_COLORSPACE(*InputFormat)); + OutputColorSpace = _cmsICCcolorSpace(T_COLORSPACE(*OutputFormat)); + nGridPoints = _cmsReasonableGridpointsByColorspace(ColorSpace, *dwFlags); + + // Empty gamma containers + memset(Trans, 0, sizeof(Trans)); + memset(TransReverse, 0, sizeof(TransReverse)); + + for (t = 0; t < OriginalLut ->InputChannels; t++) { + Trans[t] = cmsBuildTabulatedToneCurve16(OriginalLut ->ContextID, PRELINEARIZATION_POINTS, NULL); + if (Trans[t] == NULL) goto Error; + } + + // Populate the curves + for (i=0; i < PRELINEARIZATION_POINTS; i++) { + + v = (cmsFloat32Number) ((cmsFloat64Number) i / (PRELINEARIZATION_POINTS - 1)); + + // Feed input with a gray ramp + for (t=0; t < OriginalLut ->InputChannels; t++) + In[t] = v; + + // Evaluate the gray value + cmsPipelineEvalFloat(In, Out, OriginalLut); + + // Store result in curve + for (t=0; t < OriginalLut ->InputChannels; t++) + Trans[t] ->Table16[i] = _cmsQuickSaturateWord(Out[t] * 65535.0); + } + + // Slope-limit the obtained curves + for (t = 0; t < OriginalLut ->InputChannels; t++) + SlopeLimiting(Trans[t]); + + // Check for validity + lIsSuitable = TRUE; + lIsLinear = TRUE; + for (t=0; (lIsSuitable && (t < OriginalLut ->InputChannels)); t++) { + + // Exclude if already linear + if (!cmsIsToneCurveLinear(Trans[t])) + lIsLinear = FALSE; + + // Exclude if non-monotonic + if (!cmsIsToneCurveMonotonic(Trans[t])) + lIsSuitable = FALSE; + + if (IsDegenerated(Trans[t])) + lIsSuitable = FALSE; + } + + // If it is not suitable, just quit + if (!lIsSuitable) goto Error; + + // Invert curves if possible + for (t = 0; t < OriginalLut ->InputChannels; t++) { + TransReverse[t] = cmsReverseToneCurveEx(PRELINEARIZATION_POINTS, Trans[t]); + if (TransReverse[t] == NULL) goto Error; + } + + // Now inset the reversed curves at the begin of transform + LutPlusCurves = cmsPipelineDup(OriginalLut); + if (LutPlusCurves == NULL) goto Error; + + if (!cmsPipelineInsertStage(LutPlusCurves, cmsAT_BEGIN, cmsStageAllocToneCurves(OriginalLut ->ContextID, OriginalLut ->InputChannels, TransReverse))) + goto Error; + + // Create the result LUT + OptimizedLUT = cmsPipelineAlloc(OriginalLut ->ContextID, OriginalLut ->InputChannels, OriginalLut ->OutputChannels); + if (OptimizedLUT == NULL) goto Error; + + OptimizedPrelinMpe = cmsStageAllocToneCurves(OriginalLut ->ContextID, OriginalLut ->InputChannels, Trans); + + // Create and insert the curves at the beginning + if (!cmsPipelineInsertStage(OptimizedLUT, cmsAT_BEGIN, OptimizedPrelinMpe)) + goto Error; + + // Allocate the CLUT for result + OptimizedCLUTmpe = cmsStageAllocCLut16bit(OriginalLut ->ContextID, nGridPoints, OriginalLut ->InputChannels, OriginalLut ->OutputChannels, NULL); + + // Add the CLUT to the destination LUT + if (!cmsPipelineInsertStage(OptimizedLUT, cmsAT_END, OptimizedCLUTmpe)) + goto Error; + + // Resample the LUT + if (!cmsStageSampleCLut16bit(OptimizedCLUTmpe, XFormSampler16, (void*) LutPlusCurves, 0)) goto Error; + + // Free resources + for (t = 0; t < OriginalLut ->InputChannels; t++) { + + if (Trans[t]) cmsFreeToneCurve(Trans[t]); + if (TransReverse[t]) cmsFreeToneCurve(TransReverse[t]); + } + + cmsPipelineFree(LutPlusCurves); + + + OptimizedPrelinCurves = _cmsStageGetPtrToCurveSet(OptimizedPrelinMpe); + OptimizedPrelinCLUT = (_cmsStageCLutData*) OptimizedCLUTmpe ->Data; + + // Set the evaluator if 8-bit + if (_cmsFormatterIs8bit(*InputFormat)) { + + Prelin8Data* p8 = PrelinOpt8alloc(OptimizedLUT ->ContextID, + OptimizedPrelinCLUT ->Params, + OptimizedPrelinCurves); + if (p8 == NULL) return FALSE; + + _cmsPipelineSetOptimizationParameters(OptimizedLUT, PrelinEval8, (void*) p8, Prelin8free, Prelin8dup); + + } + else + { + Prelin16Data* p16 = PrelinOpt16alloc(OptimizedLUT ->ContextID, + OptimizedPrelinCLUT ->Params, + 3, OptimizedPrelinCurves, 3, NULL); + if (p16 == NULL) return FALSE; + + _cmsPipelineSetOptimizationParameters(OptimizedLUT, PrelinEval16, (void*) p16, PrelinOpt16free, Prelin16dup); + + } + + // Don't fix white on absolute colorimetric + if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) + *dwFlags |= cmsFLAGS_NOWHITEONWHITEFIXUP; + + if (!(*dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP)) { + + if (!FixWhiteMisalignment(OptimizedLUT, ColorSpace, OutputColorSpace)) { + + return FALSE; + } + } + + // And return the obtained LUT + + cmsPipelineFree(OriginalLut); + *Lut = OptimizedLUT; + return TRUE; + +Error: + + for (t = 0; t < OriginalLut ->InputChannels; t++) { + + if (Trans[t]) cmsFreeToneCurve(Trans[t]); + if (TransReverse[t]) cmsFreeToneCurve(TransReverse[t]); + } + + if (LutPlusCurves != NULL) cmsPipelineFree(LutPlusCurves); + if (OptimizedLUT != NULL) cmsPipelineFree(OptimizedLUT); + + return FALSE; + + cmsUNUSED_PARAMETER(Intent); +} + + +// Curves optimizer ------------------------------------------------------------------------------------------------------------------ + +static +void CurvesFree(cmsContext ContextID, void* ptr) +{ + Curves16Data* Data = (Curves16Data*) ptr; + int i; + + for (i=0; i < Data -> nCurves; i++) { + + _cmsFree(ContextID, Data ->Curves[i]); + } + + _cmsFree(ContextID, Data ->Curves); + _cmsFree(ContextID, ptr); +} + +static +void* CurvesDup(cmsContext ContextID, const void* ptr) +{ + Curves16Data* Data = (Curves16Data*)_cmsDupMem(ContextID, ptr, sizeof(Curves16Data)); + int i; + + if (Data == NULL) return NULL; + + Data ->Curves = (cmsUInt16Number**)_cmsDupMem(ContextID, Data ->Curves, Data ->nCurves * sizeof(cmsUInt16Number*)); + + for (i=0; i < Data -> nCurves; i++) { + Data ->Curves[i] = (cmsUInt16Number*)_cmsDupMem(ContextID, Data ->Curves[i], Data -> nElements * sizeof(cmsUInt16Number)); + } + + return (void*) Data; +} + +// Precomputes tables for 8-bit on input devicelink. +static +Curves16Data* CurvesAlloc(cmsContext ContextID, int nCurves, int nElements, cmsToneCurve** G) +{ + int i, j; + Curves16Data* c16; + + c16 = (Curves16Data*)_cmsMallocZero(ContextID, sizeof(Curves16Data)); + if (c16 == NULL) return NULL; + + c16 ->nCurves = nCurves; + c16 ->nElements = nElements; + + c16 ->Curves = (cmsUInt16Number**)_cmsCalloc(ContextID, nCurves, sizeof(cmsUInt16Number*)); + if (c16 ->Curves == NULL) return NULL; + + for (i=0; i < nCurves; i++) { + + c16->Curves[i] = (cmsUInt16Number*)_cmsCalloc(ContextID, nElements, sizeof(cmsUInt16Number)); + + if (c16->Curves[i] == NULL) { + + for (j=0; j < i; j++) { + _cmsFree(ContextID, c16->Curves[j]); + } + _cmsFree(ContextID, c16->Curves); + _cmsFree(ContextID, c16); + return NULL; + } + + if (nElements == 256) { + + for (j=0; j < nElements; j++) { + + c16 ->Curves[i][j] = cmsEvalToneCurve16(G[i], FROM_8_TO_16(j)); + } + } + else { + + for (j=0; j < nElements; j++) { + c16 ->Curves[i][j] = cmsEvalToneCurve16(G[i], (cmsUInt16Number) j); + } + } + } + + return c16; +} + +static +void FastEvaluateCurves8(register const cmsUInt16Number In[], + register cmsUInt16Number Out[], + register const void* D) +{ + Curves16Data* Data = (Curves16Data*) D; + cmsUInt8Number x; + int i; + + for (i=0; i < Data ->nCurves; i++) { + + x = (In[i] >> 8); + Out[i] = Data -> Curves[i][x]; + } +} + + +static +void FastEvaluateCurves16(register const cmsUInt16Number In[], + register cmsUInt16Number Out[], + register const void* D) +{ + Curves16Data* Data = (Curves16Data*) D; + int i; + + for (i=0; i < Data ->nCurves; i++) { + Out[i] = Data -> Curves[i][In[i]]; + } +} + + +static +void FastIdentity16(register const cmsUInt16Number In[], + register cmsUInt16Number Out[], + register const void* D) +{ + cmsPipeline* Lut = (cmsPipeline*) D; + cmsUInt32Number i; + + for (i=0; i < Lut ->InputChannels; i++) { + Out[i] = In[i]; + } +} + + +// If the target LUT holds only curves, the optimization procedure is to join all those +// curves together. That only works on curves and does not work on matrices. +static +cmsBool OptimizeByJoiningCurves(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) +{ + cmsToneCurve** GammaTables = NULL; + cmsFloat32Number InFloat[cmsMAXCHANNELS], OutFloat[cmsMAXCHANNELS]; + cmsUInt32Number i, j; + cmsPipeline* Src = *Lut; + cmsPipeline* Dest = NULL; + cmsStage* mpe; + cmsStage* ObtainedCurves = NULL; + + + // This is a loosy optimization! does not apply in floating-point cases + if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) return FALSE; + + // Only curves in this LUT? + for (mpe = cmsPipelineGetPtrToFirstStage(Src); + mpe != NULL; + mpe = cmsStageNext(mpe)) { + if (cmsStageType(mpe) != cmsSigCurveSetElemType) return FALSE; + } + + // Allocate an empty LUT + Dest = cmsPipelineAlloc(Src ->ContextID, Src ->InputChannels, Src ->OutputChannels); + if (Dest == NULL) return FALSE; + + // Create target curves + GammaTables = (cmsToneCurve**) _cmsCalloc(Src ->ContextID, Src ->InputChannels, sizeof(cmsToneCurve*)); + if (GammaTables == NULL) goto Error; + + for (i=0; i < Src ->InputChannels; i++) { + GammaTables[i] = cmsBuildTabulatedToneCurve16(Src ->ContextID, PRELINEARIZATION_POINTS, NULL); + if (GammaTables[i] == NULL) goto Error; + } + + // Compute 16 bit result by using floating point + for (i=0; i < PRELINEARIZATION_POINTS; i++) { + + for (j=0; j < Src ->InputChannels; j++) + InFloat[j] = (cmsFloat32Number) ((cmsFloat64Number) i / (PRELINEARIZATION_POINTS - 1)); + + cmsPipelineEvalFloat(InFloat, OutFloat, Src); + + for (j=0; j < Src ->InputChannels; j++) + GammaTables[j] -> Table16[i] = _cmsQuickSaturateWord(OutFloat[j] * 65535.0); + } + + ObtainedCurves = cmsStageAllocToneCurves(Src ->ContextID, Src ->InputChannels, GammaTables); + if (ObtainedCurves == NULL) goto Error; + + for (i=0; i < Src ->InputChannels; i++) { + cmsFreeToneCurve(GammaTables[i]); + GammaTables[i] = NULL; + } + + if (GammaTables != NULL) _cmsFree(Src ->ContextID, GammaTables); + + // Maybe the curves are linear at the end + if (!AllCurvesAreLinear(ObtainedCurves)) { + + if (!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, ObtainedCurves)) + goto Error; + + // If the curves are to be applied in 8 bits, we can save memory + if (_cmsFormatterIs8bit(*InputFormat)) { + + _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) ObtainedCurves ->Data; + Curves16Data* c16 = CurvesAlloc(Dest ->ContextID, Data ->nCurves, 256, Data ->TheCurves); + + if (c16 == NULL) goto Error; + *dwFlags |= cmsFLAGS_NOCACHE; + _cmsPipelineSetOptimizationParameters(Dest, FastEvaluateCurves8, c16, CurvesFree, CurvesDup); + + } + else { + + _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) cmsStageData(ObtainedCurves); + Curves16Data* c16 = CurvesAlloc(Dest ->ContextID, Data ->nCurves, 65536, Data ->TheCurves); + + if (c16 == NULL) goto Error; + *dwFlags |= cmsFLAGS_NOCACHE; + _cmsPipelineSetOptimizationParameters(Dest, FastEvaluateCurves16, c16, CurvesFree, CurvesDup); + } + } + else { + + // LUT optimizes to nothing. Set the identity LUT + cmsStageFree(ObtainedCurves); + + if (!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, cmsStageAllocIdentity(Dest ->ContextID, Src ->InputChannels))) + goto Error; + + *dwFlags |= cmsFLAGS_NOCACHE; + _cmsPipelineSetOptimizationParameters(Dest, FastIdentity16, (void*) Dest, NULL, NULL); + } + + // We are done. + cmsPipelineFree(Src); + *Lut = Dest; + return TRUE; + +Error: + + if (ObtainedCurves != NULL) cmsStageFree(ObtainedCurves); + if (GammaTables != NULL) { + for (i=0; i < Src ->InputChannels; i++) { + if (GammaTables[i] != NULL) cmsFreeToneCurve(GammaTables[i]); + } + + _cmsFree(Src ->ContextID, GammaTables); + } + + if (Dest != NULL) cmsPipelineFree(Dest); + return FALSE; + + cmsUNUSED_PARAMETER(Intent); + cmsUNUSED_PARAMETER(InputFormat); + cmsUNUSED_PARAMETER(OutputFormat); + cmsUNUSED_PARAMETER(dwFlags); +} + +// ------------------------------------------------------------------------------------------------------------------------------------- +// LUT is Shaper - Matrix - Matrix - Shaper, which is very frequent when combining two matrix-shaper profiles + + +static +void FreeMatShaper(cmsContext ContextID, void* Data) +{ + if (Data != NULL) _cmsFree(ContextID, Data); +} + +static +void* DupMatShaper(cmsContext ContextID, const void* Data) +{ + return _cmsDupMem(ContextID, Data, sizeof(MatShaper8Data)); +} + + +// A fast matrix-shaper evaluator for 8 bits. This is a bit ticky since I'm using 1.14 signed fixed point +// to accomplish some performance. Actually it takes 256x3 16 bits tables and 16385 x 3 tables of 8 bits, +// in total about 50K, and the performance boost is huge! +static +void MatShaperEval16(register const cmsUInt16Number In[], + register cmsUInt16Number Out[], + register const void* D) +{ + MatShaper8Data* p = (MatShaper8Data*) D; + cmsS1Fixed14Number l1, l2, l3, r, g, b; + cmsUInt32Number ri, gi, bi; + + // In this case (and only in this case!) we can use this simplification since + // In[] is assured to come from a 8 bit number. (a << 8 | a) + ri = In[0] & 0xFF; + gi = In[1] & 0xFF; + bi = In[2] & 0xFF; + + // Across first shaper, which also converts to 1.14 fixed point + r = p->Shaper1R[ri]; + g = p->Shaper1G[gi]; + b = p->Shaper1B[bi]; + + // Evaluate the matrix in 1.14 fixed point + l1 = (p->Mat[0][0] * r + p->Mat[0][1] * g + p->Mat[0][2] * b + p->Off[0] + 0x2000) >> 14; + l2 = (p->Mat[1][0] * r + p->Mat[1][1] * g + p->Mat[1][2] * b + p->Off[1] + 0x2000) >> 14; + l3 = (p->Mat[2][0] * r + p->Mat[2][1] * g + p->Mat[2][2] * b + p->Off[2] + 0x2000) >> 14; + + // Now we have to clip to 0..1.0 range + ri = (l1 < 0) ? 0 : ((l1 > 16384) ? 16384 : l1); + gi = (l2 < 0) ? 0 : ((l2 > 16384) ? 16384 : l2); + bi = (l3 < 0) ? 0 : ((l3 > 16384) ? 16384 : l3); + + // And across second shaper, + Out[0] = p->Shaper2R[ri]; + Out[1] = p->Shaper2G[gi]; + Out[2] = p->Shaper2B[bi]; + +} + +// This table converts from 8 bits to 1.14 after applying the curve +static +cmsBool FillFirstShaper(cmsS1Fixed14Number* Table, cmsToneCurve* Curve) +{ + int i; + cmsFloat32Number R, y; + + for (i=0; i < 256; i++) { + + R = (cmsFloat32Number) (i / 255.0); + y = cmsEvalToneCurveFloat(Curve, R); + if (isinf(y)) + return FALSE; + + Table[i] = DOUBLE_TO_1FIXED14(y); + } + return TRUE; +} + +// This table converts form 1.14 (being 0x4000 the last entry) to 8 bits after applying the curve +static +cmsBool FillSecondShaper(cmsUInt16Number* Table, cmsToneCurve* Curve, cmsBool Is8BitsOutput) +{ + int i; + cmsFloat32Number R, Val; + + for (i=0; i < 16385; i++) { + + R = (cmsFloat32Number) (i / 16384.0); + Val = cmsEvalToneCurveFloat(Curve, R); // Val comes 0..1.0 + if (isinf(Val)) + return FALSE; + + if (Is8BitsOutput) { + + // If 8 bits output, we can optimize further by computing the / 257 part. + // first we compute the resulting byte and then we store the byte times + // 257. This quantization allows to round very quick by doing a >> 8, but + // since the low byte is always equal to msb, we can do a & 0xff and this works! + cmsUInt16Number w = _cmsQuickSaturateWord(Val * 65535.0); + cmsUInt8Number b = FROM_16_TO_8(w); + + Table[i] = FROM_8_TO_16(b); + } + else Table[i] = _cmsQuickSaturateWord(Val * 65535.0); + } + return TRUE; +} + +// Compute the matrix-shaper structure +static +cmsBool SetMatShaper(cmsPipeline* Dest, cmsToneCurve* Curve1[3], cmsMAT3* Mat, cmsVEC3* Off, cmsToneCurve* Curve2[3], cmsUInt32Number* OutputFormat) +{ + MatShaper8Data* p; + int i, j; + cmsBool Is8Bits = _cmsFormatterIs8bit(*OutputFormat); + + // Allocate a big chuck of memory to store precomputed tables + p = (MatShaper8Data*) _cmsMalloc(Dest ->ContextID, sizeof(MatShaper8Data)); + if (p == NULL) return FALSE; + + p -> ContextID = Dest -> ContextID; + + // Precompute tables + if (!FillFirstShaper(p ->Shaper1R, Curve1[0])) + goto Error; + if (!FillFirstShaper(p ->Shaper1G, Curve1[1])) + goto Error; + if (!FillFirstShaper(p ->Shaper1B, Curve1[2])) + goto Error; + + if (!FillSecondShaper(p ->Shaper2R, Curve2[0], Is8Bits)) + goto Error; + if (!FillSecondShaper(p ->Shaper2G, Curve2[1], Is8Bits)) + goto Error; + if (!FillSecondShaper(p ->Shaper2B, Curve2[2], Is8Bits)) + goto Error; + + // Convert matrix to nFixed14. Note that those values may take more than 16 bits as + for (i=0; i < 3; i++) { + for (j=0; j < 3; j++) { + p ->Mat[i][j] = DOUBLE_TO_1FIXED14(Mat->v[i].n[j]); + } + } + + for (i=0; i < 3; i++) { + + if (Off == NULL) { + p ->Off[i] = 0; + } + else { + p ->Off[i] = DOUBLE_TO_1FIXED14(Off->n[i]); + } + } + + // Mark as optimized for faster formatter + if (Is8Bits) + *OutputFormat |= OPTIMIZED_SH(1); + + // Fill function pointers + _cmsPipelineSetOptimizationParameters(Dest, MatShaperEval16, (void*) p, FreeMatShaper, DupMatShaper); + return TRUE; +Error: + _cmsFree(Dest->ContextID, p); + return FALSE; +} + +// 8 bits on input allows matrix-shaper boot up to 25 Mpixels per second on RGB. That's fast! +// TODO: Allow a third matrix for abs. colorimetric +static +cmsBool OptimizeMatrixShaper(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) +{ + cmsStage* Curve1, *Curve2; + cmsStage* Matrix1, *Matrix2; + _cmsStageMatrixData* Data1; + _cmsStageMatrixData* Data2; + cmsMAT3 res; + cmsBool IdentityMat; + cmsPipeline* Dest, *Src; + + // Only works on RGB to RGB + if (T_CHANNELS(*InputFormat) != 3 || T_CHANNELS(*OutputFormat) != 3) return FALSE; + + // Only works on 8 bit input + if (!_cmsFormatterIs8bit(*InputFormat)) return FALSE; + + // Seems suitable, proceed + Src = *Lut; + + // Check for shaper-matrix-matrix-shaper structure, that is what this optimizer stands for + if (!cmsPipelineCheckAndRetreiveStages(Src, 4, + cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, + &Curve1, &Matrix1, &Matrix2, &Curve2)) return FALSE; + + // Get both matrices + Data1 = (_cmsStageMatrixData*) cmsStageData(Matrix1); + Data2 = (_cmsStageMatrixData*) cmsStageData(Matrix2); + + // Input offset should be zero + if (Data1 ->Offset != NULL) return FALSE; + + // Multiply both matrices to get the result + _cmsMAT3per(&res, (cmsMAT3*) Data2 ->Double, (cmsMAT3*) Data1 ->Double); + + // Now the result is in res + Data2 -> Offset. Maybe is a plain identity? + IdentityMat = FALSE; + if (_cmsMAT3isIdentity(&res) && Data2 ->Offset == NULL) { + + // We can get rid of full matrix + IdentityMat = TRUE; + } + + // Allocate an empty LUT + Dest = cmsPipelineAlloc(Src ->ContextID, Src ->InputChannels, Src ->OutputChannels); + if (!Dest) return FALSE; + + // Assamble the new LUT + if (!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, cmsStageDup(Curve1))) + goto Error; + + if (!IdentityMat) + if (!cmsPipelineInsertStage(Dest, cmsAT_END, cmsStageAllocMatrix(Dest ->ContextID, 3, 3, (const cmsFloat64Number*) &res, Data2 ->Offset))) + goto Error; + if (!cmsPipelineInsertStage(Dest, cmsAT_END, cmsStageDup(Curve2))) + goto Error; + + // If identity on matrix, we can further optimize the curves, so call the join curves routine + if (IdentityMat) { + + OptimizeByJoiningCurves(&Dest, Intent, InputFormat, OutputFormat, dwFlags); + } + else { + _cmsStageToneCurvesData* mpeC1 = (_cmsStageToneCurvesData*) cmsStageData(Curve1); + _cmsStageToneCurvesData* mpeC2 = (_cmsStageToneCurvesData*) cmsStageData(Curve2); + + // In this particular optimization, cach?does not help as it takes more time to deal with + // the cach?that with the pixel handling + *dwFlags |= cmsFLAGS_NOCACHE; + + // Setup the optimizarion routines + if (!SetMatShaper(Dest, mpeC1 ->TheCurves, &res, (cmsVEC3*) Data2 ->Offset, mpeC2->TheCurves, OutputFormat)) + goto Error; + } + + cmsPipelineFree(Src); + *Lut = Dest; + return TRUE; +Error: + // Leave Src unchanged + cmsPipelineFree(Dest); + return FALSE; +} + + +// ------------------------------------------------------------------------------------------------------------------------------------- +// Optimization plug-ins + +// List of optimizations +typedef struct _cmsOptimizationCollection_st { + + _cmsOPToptimizeFn OptimizePtr; + + struct _cmsOptimizationCollection_st *Next; + +} _cmsOptimizationCollection; + + +// The built-in list. We currently implement 4 types of optimizations. Joining of curves, matrix-shaper, linearization and resampling +static _cmsOptimizationCollection DefaultOptimization[] = { + + { OptimizeByJoiningCurves, &DefaultOptimization[1] }, + { OptimizeMatrixShaper, &DefaultOptimization[2] }, + { OptimizeByComputingLinearization, &DefaultOptimization[3] }, + { OptimizeByResampling, NULL } +}; + +// The linked list head +_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 ContextID, cmsPluginBase* Data) +{ + cmsPluginOptimization* Plugin = (cmsPluginOptimization*) Data; + _cmsOptimizationPluginChunkType* ctx = ( _cmsOptimizationPluginChunkType*) _cmsContextGetClientChunk(ContextID, OptimizationPlugin); + _cmsOptimizationCollection* fl; + + if (Data == NULL) { + + ctx->OptimizationCollection = NULL; + return TRUE; + } + + // Optimizer callback is required + if (Plugin ->OptimizePtr == NULL) return FALSE; + + fl = (_cmsOptimizationCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsOptimizationCollection)); + if (fl == NULL) return FALSE; + + // Copy the parameters + fl ->OptimizePtr = Plugin ->OptimizePtr; + + // Keep linked list + fl ->Next = ctx->OptimizationCollection; + + // Set the head + ctx ->OptimizationCollection = fl; + + // All is ok + return TRUE; +} + +// The entry point for LUT optimization +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; + + // A CLUT is being asked, so force this specific optimization + if (*dwFlags & cmsFLAGS_FORCE_CLUT) { + + PreOptimize(*PtrLut); + return OptimizeByResampling(PtrLut, Intent, InputFormat, OutputFormat, dwFlags); + } + + // Anything to optimize? + if ((*PtrLut) ->Elements == NULL) { + _cmsPipelineSetOptimizationParameters(*PtrLut, FastIdentity16, (void*) *PtrLut, NULL, NULL); + return TRUE; + } + + // Try to get rid of identities and trivial conversions. + AnySuccess = PreOptimize(*PtrLut); + + // After removal do we end with an identity? + if ((*PtrLut) ->Elements == NULL) { + _cmsPipelineSetOptimizationParameters(*PtrLut, FastIdentity16, (void*) *PtrLut, NULL, NULL); + return TRUE; + } + + // Do not optimize, keep all precision + if (*dwFlags & cmsFLAGS_NOOPTIMIZE) + return FALSE; + + // Try plug-in optimizations + for (Opts = ctx->OptimizationCollection; + Opts != NULL; + Opts = Opts ->Next) { + + // If one schema succeeded, we are done + if (Opts ->OptimizePtr(PtrLut, Intent, InputFormat, OutputFormat, dwFlags)) { + + return TRUE; // Optimized! + } + } + + // 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/third_party/lcms/src/cmspack.c b/third_party/lcms/src/cmspack.c new file mode 100644 index 0000000000..9323b53ec5 --- /dev/null +++ b/third_party/lcms/src/cmspack.c @@ -0,0 +1,3369 @@ +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2010 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// +#include "lcms2_internal.h" + +// This module handles all formats supported by lcms. There are two flavors, 16 bits and +// floating point. Floating point is supported only in a subset, those formats holding +// cmsFloat32Number (4 bytes per component) and double (marked as 0 bytes per component +// as special case) + +// --------------------------------------------------------------------------- + + +// This macro return words stored as big endian +#define CHANGE_ENDIAN(w) (cmsUInt16Number) ((cmsUInt16Number) ((w)<<8)|((w)>>8)) + +// These macros handles reversing (negative) +#define REVERSE_FLAVOR_8(x) ((cmsUInt8Number) (0xff-(x))) +#define REVERSE_FLAVOR_16(x) ((cmsUInt16Number)(0xffff-(x))) + +// * 0xffff / 0xff00 = (255 * 257) / (255 * 256) = 257 / 256 +cmsINLINE cmsUInt16Number FomLabV2ToLabV4(cmsUInt16Number x) +{ + int a = (x << 8 | x) >> 8; // * 257 / 256 + if ( a > 0xffff) return 0xffff; + return (cmsUInt16Number) a; +} + +// * 0xf00 / 0xffff = * 256 / 257 +cmsINLINE cmsUInt16Number FomLabV4ToLabV2(cmsUInt16Number x) +{ + return (cmsUInt16Number) (((x << 8) + 0x80) / 257); +} + + +typedef struct { + cmsUInt32Number Type; + cmsUInt32Number Mask; + cmsFormatter16 Frm; + +} cmsFormatters16; + +typedef struct { + cmsUInt32Number Type; + cmsUInt32Number Mask; + cmsFormatterFloat Frm; + +} cmsFormattersFloat; + + +#define ANYSPACE COLORSPACE_SH(31) +#define ANYCHANNELS CHANNELS_SH(15) +#define ANYEXTRA EXTRA_SH(7) +#define ANYPLANAR PLANAR_SH(1) +#define ANYENDIAN ENDIAN16_SH(1) +#define ANYSWAP DOSWAP_SH(1) +#define ANYSWAPFIRST SWAPFIRST_SH(1) +#define ANYFLAVOR FLAVOR_SH(1) + + +// Supress waning about info never being used + +#ifdef _MSC_VER +#pragma warning(disable : 4100) +#endif + +// Unpacking routines (16 bits) ---------------------------------------------------------------------------------------- + + +// Does almost everything but is slow +static +cmsUInt8Number* UnrollChunkyBytes(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + int nChan = T_CHANNELS(info -> InputFormat); + int DoSwap = T_DOSWAP(info ->InputFormat); + int Reverse = T_FLAVOR(info ->InputFormat); + int SwapFirst = T_SWAPFIRST(info -> InputFormat); + int Extra = T_EXTRA(info -> InputFormat); + int ExtraFirst = DoSwap ^ SwapFirst; + cmsUInt16Number v; + int i; + + if (ExtraFirst) { + accum += Extra; + } + + for (i=0; i < nChan; i++) { + int index = DoSwap ? (nChan - i - 1) : i; + + v = FROM_8_TO_16(*accum); + v = Reverse ? REVERSE_FLAVOR_16(v) : v; + wIn[index] = v; + accum++; + } + + if (!ExtraFirst) { + accum += Extra; + } + + if (Extra == 0 && SwapFirst) { + cmsUInt16Number tmp = wIn[0]; + + memmove(&wIn[0], &wIn[1], (nChan-1) * sizeof(cmsUInt16Number)); + wIn[nChan-1] = tmp; + } + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); + +} + +// Extra channels are just ignored because come in the next planes +static +cmsUInt8Number* UnrollPlanarBytes(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + int nChan = T_CHANNELS(info -> InputFormat); + int DoSwap = T_DOSWAP(info ->InputFormat); + int SwapFirst = T_SWAPFIRST(info ->InputFormat); + int Reverse = T_FLAVOR(info ->InputFormat); + int i; + cmsUInt8Number* Init = accum; + + if (DoSwap ^ SwapFirst) { + accum += T_EXTRA(info -> InputFormat) * Stride; + } + + for (i=0; i < nChan; i++) { + + int index = DoSwap ? (nChan - i - 1) : i; + cmsUInt16Number v = FROM_8_TO_16(*accum); + + wIn[index] = Reverse ? REVERSE_FLAVOR_16(v) : v; + accum += Stride; + } + + return (Init + 1); +} + +// Special cases, provided for performance +static +cmsUInt8Number* Unroll4Bytes(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = FROM_8_TO_16(*accum); accum++; // C + wIn[1] = FROM_8_TO_16(*accum); accum++; // M + wIn[2] = FROM_8_TO_16(*accum); accum++; // Y + wIn[3] = FROM_8_TO_16(*accum); accum++; // K + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll4BytesReverse(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = FROM_8_TO_16(REVERSE_FLAVOR_8(*accum)); accum++; // C + wIn[1] = FROM_8_TO_16(REVERSE_FLAVOR_8(*accum)); accum++; // M + wIn[2] = FROM_8_TO_16(REVERSE_FLAVOR_8(*accum)); accum++; // Y + wIn[3] = FROM_8_TO_16(REVERSE_FLAVOR_8(*accum)); accum++; // K + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll4BytesSwapFirst(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[3] = FROM_8_TO_16(*accum); accum++; // K + wIn[0] = FROM_8_TO_16(*accum); accum++; // C + wIn[1] = FROM_8_TO_16(*accum); accum++; // M + wIn[2] = FROM_8_TO_16(*accum); accum++; // Y + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +// KYMC +static +cmsUInt8Number* Unroll4BytesSwap(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[3] = FROM_8_TO_16(*accum); accum++; // K + wIn[2] = FROM_8_TO_16(*accum); accum++; // Y + wIn[1] = FROM_8_TO_16(*accum); accum++; // M + wIn[0] = FROM_8_TO_16(*accum); accum++; // C + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll4BytesSwapSwapFirst(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[2] = FROM_8_TO_16(*accum); accum++; // K + wIn[1] = FROM_8_TO_16(*accum); accum++; // Y + wIn[0] = FROM_8_TO_16(*accum); accum++; // M + wIn[3] = FROM_8_TO_16(*accum); accum++; // C + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll3Bytes(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = FROM_8_TO_16(*accum); accum++; // R + wIn[1] = FROM_8_TO_16(*accum); accum++; // G + wIn[2] = FROM_8_TO_16(*accum); accum++; // B + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll3BytesSkip1Swap(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + accum++; // A + wIn[2] = FROM_8_TO_16(*accum); accum++; // B + wIn[1] = FROM_8_TO_16(*accum); accum++; // G + wIn[0] = FROM_8_TO_16(*accum); accum++; // R + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll3BytesSkip1SwapSwapFirst(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[2] = FROM_8_TO_16(*accum); accum++; // B + wIn[1] = FROM_8_TO_16(*accum); accum++; // G + wIn[0] = FROM_8_TO_16(*accum); accum++; // R + accum++; // A + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll3BytesSkip1SwapFirst(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + accum++; // A + wIn[0] = FROM_8_TO_16(*accum); accum++; // R + wIn[1] = FROM_8_TO_16(*accum); accum++; // G + wIn[2] = FROM_8_TO_16(*accum); accum++; // B + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +// BRG +static +cmsUInt8Number* Unroll3BytesSwap(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[2] = FROM_8_TO_16(*accum); accum++; // B + wIn[1] = FROM_8_TO_16(*accum); accum++; // G + wIn[0] = FROM_8_TO_16(*accum); accum++; // R + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* UnrollLabV2_8(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++; // L + wIn[1] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++; // a + wIn[2] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++; // b + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* UnrollALabV2_8(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + accum++; // A + wIn[0] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++; // L + wIn[1] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++; // a + wIn[2] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++; // b + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* UnrollLabV2_16(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = FomLabV2ToLabV4(*(cmsUInt16Number*) accum); accum += 2; // L + wIn[1] = FomLabV2ToLabV4(*(cmsUInt16Number*) accum); accum += 2; // a + wIn[2] = FomLabV2ToLabV4(*(cmsUInt16Number*) accum); accum += 2; // b + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +// for duplex +static +cmsUInt8Number* Unroll2Bytes(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = FROM_8_TO_16(*accum); accum++; // ch1 + wIn[1] = FROM_8_TO_16(*accum); accum++; // ch2 + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + + + +// Monochrome duplicates L into RGB for null-transforms +static +cmsUInt8Number* Unroll1Byte(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = wIn[1] = wIn[2] = FROM_8_TO_16(*accum); accum++; // L + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Unroll1ByteSkip1(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = wIn[1] = wIn[2] = FROM_8_TO_16(*accum); accum++; // L + accum += 1; + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll1ByteSkip2(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = wIn[1] = wIn[2] = FROM_8_TO_16(*accum); accum++; // L + accum += 2; + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll1ByteReversed(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = wIn[1] = wIn[2] = REVERSE_FLAVOR_16(FROM_8_TO_16(*accum)); accum++; // L + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* UnrollAnyWords(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + int nChan = T_CHANNELS(info -> InputFormat); + int SwapEndian = T_ENDIAN16(info -> InputFormat); + int DoSwap = T_DOSWAP(info ->InputFormat); + int Reverse = T_FLAVOR(info ->InputFormat); + int SwapFirst = T_SWAPFIRST(info -> InputFormat); + int Extra = T_EXTRA(info -> InputFormat); + int ExtraFirst = DoSwap ^ SwapFirst; + int i; + + if (ExtraFirst) { + accum += Extra * sizeof(cmsUInt16Number); + } + + for (i=0; i < nChan; i++) { + + int index = DoSwap ? (nChan - i - 1) : i; + cmsUInt16Number v = *(cmsUInt16Number*) accum; + + if (SwapEndian) + v = CHANGE_ENDIAN(v); + + wIn[index] = Reverse ? REVERSE_FLAVOR_16(v) : v; + + accum += sizeof(cmsUInt16Number); + } + + if (!ExtraFirst) { + accum += Extra * sizeof(cmsUInt16Number); + } + + if (Extra == 0 && SwapFirst) { + + cmsUInt16Number tmp = wIn[0]; + + memmove(&wIn[0], &wIn[1], (nChan-1) * sizeof(cmsUInt16Number)); + wIn[nChan-1] = tmp; + } + + return accum; + + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* UnrollPlanarWords(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + int nChan = T_CHANNELS(info -> InputFormat); + int DoSwap= T_DOSWAP(info ->InputFormat); + int Reverse= T_FLAVOR(info ->InputFormat); + int SwapEndian = T_ENDIAN16(info -> InputFormat); + int i; + cmsUInt8Number* Init = accum; + + if (DoSwap) { + accum += T_EXTRA(info -> InputFormat) * Stride * sizeof(cmsUInt16Number); + } + + for (i=0; i < nChan; i++) { + + int index = DoSwap ? (nChan - i - 1) : i; + cmsUInt16Number v = *(cmsUInt16Number*) accum; + + if (SwapEndian) + v = CHANGE_ENDIAN(v); + + wIn[index] = Reverse ? REVERSE_FLAVOR_16(v) : v; + + accum += Stride * sizeof(cmsUInt16Number); + } + + return (Init + sizeof(cmsUInt16Number)); +} + + +static +cmsUInt8Number* Unroll4Words(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = *(cmsUInt16Number*) accum; accum+= 2; // C + wIn[1] = *(cmsUInt16Number*) accum; accum+= 2; // M + wIn[2] = *(cmsUInt16Number*) accum; accum+= 2; // Y + wIn[3] = *(cmsUInt16Number*) accum; accum+= 2; // K + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll4WordsReverse(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = REVERSE_FLAVOR_16(*(cmsUInt16Number*) accum); accum+= 2; // C + wIn[1] = REVERSE_FLAVOR_16(*(cmsUInt16Number*) accum); accum+= 2; // M + wIn[2] = REVERSE_FLAVOR_16(*(cmsUInt16Number*) accum); accum+= 2; // Y + wIn[3] = REVERSE_FLAVOR_16(*(cmsUInt16Number*) accum); accum+= 2; // K + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll4WordsSwapFirst(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[3] = *(cmsUInt16Number*) accum; accum+= 2; // K + wIn[0] = *(cmsUInt16Number*) accum; accum+= 2; // C + wIn[1] = *(cmsUInt16Number*) accum; accum+= 2; // M + wIn[2] = *(cmsUInt16Number*) accum; accum+= 2; // Y + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +// KYMC +static +cmsUInt8Number* Unroll4WordsSwap(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[3] = *(cmsUInt16Number*) accum; accum+= 2; // K + wIn[2] = *(cmsUInt16Number*) accum; accum+= 2; // Y + wIn[1] = *(cmsUInt16Number*) accum; accum+= 2; // M + wIn[0] = *(cmsUInt16Number*) accum; accum+= 2; // C + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll4WordsSwapSwapFirst(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[2] = *(cmsUInt16Number*) accum; accum+= 2; // K + wIn[1] = *(cmsUInt16Number*) accum; accum+= 2; // Y + wIn[0] = *(cmsUInt16Number*) accum; accum+= 2; // M + wIn[3] = *(cmsUInt16Number*) accum; accum+= 2; // C + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll3Words(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = *(cmsUInt16Number*) accum; accum+= 2; // C R + wIn[1] = *(cmsUInt16Number*) accum; accum+= 2; // M G + wIn[2] = *(cmsUInt16Number*) accum; accum+= 2; // Y B + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll3WordsSwap(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[2] = *(cmsUInt16Number*) accum; accum+= 2; // C R + wIn[1] = *(cmsUInt16Number*) accum; accum+= 2; // M G + wIn[0] = *(cmsUInt16Number*) accum; accum+= 2; // Y B + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll3WordsSkip1Swap(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + accum += 2; // A + wIn[2] = *(cmsUInt16Number*) accum; accum += 2; // R + wIn[1] = *(cmsUInt16Number*) accum; accum += 2; // G + wIn[0] = *(cmsUInt16Number*) accum; accum += 2; // B + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll3WordsSkip1SwapFirst(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + accum += 2; // A + wIn[0] = *(cmsUInt16Number*) accum; accum += 2; // R + wIn[1] = *(cmsUInt16Number*) accum; accum += 2; // G + wIn[2] = *(cmsUInt16Number*) accum; accum += 2; // B + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll1Word(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = wIn[1] = wIn[2] = *(cmsUInt16Number*) accum; accum+= 2; // L + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll1WordReversed(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = wIn[1] = wIn[2] = REVERSE_FLAVOR_16(*(cmsUInt16Number*) accum); accum+= 2; + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll1WordSkip3(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = wIn[1] = wIn[2] = *(cmsUInt16Number*) accum; + + accum += 8; + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Unroll2Words(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + wIn[0] = *(cmsUInt16Number*) accum; accum += 2; // ch1 + wIn[1] = *(cmsUInt16Number*) accum; accum += 2; // ch2 + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +// This is a conversion of Lab double to 16 bits +static +cmsUInt8Number* UnrollLabDoubleTo16(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + if (T_PLANAR(info -> InputFormat)) { + + cmsFloat64Number* Pt = (cmsFloat64Number*) accum; + + cmsCIELab Lab; + + Lab.L = Pt[0]; + Lab.a = Pt[Stride]; + Lab.b = Pt[Stride*2]; + + cmsFloat2LabEncoded(wIn, &Lab); + return accum + sizeof(cmsFloat64Number); + } + else { + + cmsFloat2LabEncoded(wIn, (cmsCIELab*) accum); + accum += sizeof(cmsCIELab) + T_EXTRA(info ->InputFormat) * sizeof(cmsFloat64Number); + return accum; + } +} + + +// This is a conversion of Lab float to 16 bits +static +cmsUInt8Number* UnrollLabFloatTo16(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + cmsCIELab Lab; + + if (T_PLANAR(info -> InputFormat)) { + + cmsFloat32Number* Pt = (cmsFloat32Number*) accum; + + + Lab.L = Pt[0]; + Lab.a = Pt[Stride]; + Lab.b = Pt[Stride*2]; + + cmsFloat2LabEncoded(wIn, &Lab); + return accum + sizeof(cmsFloat32Number); + } + else { + + Lab.L = ((cmsFloat32Number*) accum)[0]; + Lab.a = ((cmsFloat32Number*) accum)[1]; + Lab.b = ((cmsFloat32Number*) accum)[2]; + + cmsFloat2LabEncoded(wIn, &Lab); + accum += (3 + T_EXTRA(info ->InputFormat)) * sizeof(cmsFloat32Number); + return accum; + } +} + +// This is a conversion of XYZ double to 16 bits +static +cmsUInt8Number* UnrollXYZDoubleTo16(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + if (T_PLANAR(info -> InputFormat)) { + + cmsFloat64Number* Pt = (cmsFloat64Number*) accum; + cmsCIEXYZ XYZ; + + XYZ.X = Pt[0]; + XYZ.Y = Pt[Stride]; + XYZ.Z = Pt[Stride*2]; + cmsFloat2XYZEncoded(wIn, &XYZ); + + return accum + sizeof(cmsFloat64Number); + + } + + else { + cmsFloat2XYZEncoded(wIn, (cmsCIEXYZ*) accum); + accum += sizeof(cmsCIEXYZ) + T_EXTRA(info ->InputFormat) * sizeof(cmsFloat64Number); + + return accum; + } +} + +// This is a conversion of XYZ float to 16 bits +static +cmsUInt8Number* UnrollXYZFloatTo16(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + if (T_PLANAR(info -> InputFormat)) { + + cmsFloat32Number* Pt = (cmsFloat32Number*) accum; + cmsCIEXYZ XYZ; + + XYZ.X = Pt[0]; + XYZ.Y = Pt[Stride]; + XYZ.Z = Pt[Stride*2]; + cmsFloat2XYZEncoded(wIn, &XYZ); + + return accum + sizeof(cmsFloat32Number); + + } + + else { + cmsFloat32Number* Pt = (cmsFloat32Number*) accum; + cmsCIEXYZ XYZ; + + XYZ.X = Pt[0]; + XYZ.Y = Pt[1]; + XYZ.Z = Pt[2]; + cmsFloat2XYZEncoded(wIn, &XYZ); + + accum += 3 * sizeof(cmsFloat32Number) + T_EXTRA(info ->InputFormat) * sizeof(cmsFloat32Number); + + return accum; + } +} + +// Check if space is marked as ink +cmsINLINE cmsBool IsInkSpace(cmsUInt32Number Type) +{ + switch (T_COLORSPACE(Type)) { + + case PT_CMY: + case PT_CMYK: + case PT_MCH5: + case PT_MCH6: + case PT_MCH7: + case PT_MCH8: + case PT_MCH9: + case PT_MCH10: + case PT_MCH11: + case PT_MCH12: + case PT_MCH13: + case PT_MCH14: + case PT_MCH15: return TRUE; + + default: return FALSE; + } +} + +// Inks does come in percentage, remaining cases are between 0..1.0, again to 16 bits +static +cmsUInt8Number* UnrollDoubleTo16(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + + int nChan = T_CHANNELS(info -> InputFormat); + int DoSwap = T_DOSWAP(info ->InputFormat); + int Reverse = T_FLAVOR(info ->InputFormat); + int SwapFirst = T_SWAPFIRST(info -> InputFormat); + int Extra = T_EXTRA(info -> InputFormat); + int ExtraFirst = DoSwap ^ SwapFirst; + int Planar = T_PLANAR(info -> InputFormat); + cmsFloat64Number v; + cmsUInt16Number vi; + int i, start = 0; + cmsFloat64Number maximum = IsInkSpace(info ->InputFormat) ? 655.35 : 65535.0; + + + if (ExtraFirst) + start = Extra; + + for (i=0; i < nChan; i++) { + + int index = DoSwap ? (nChan - i - 1) : i; + + if (Planar) + v = (cmsFloat32Number) ((cmsFloat64Number*) accum)[(i + start) * Stride]; + else + v = (cmsFloat32Number) ((cmsFloat64Number*) accum)[i + start]; + + vi = _cmsQuickSaturateWord(v * maximum); + + if (Reverse) + vi = REVERSE_FLAVOR_16(vi); + + wIn[index] = vi; + } + + + if (Extra == 0 && SwapFirst) { + cmsUInt16Number tmp = wIn[0]; + + memmove(&wIn[0], &wIn[1], (nChan-1) * sizeof(cmsUInt16Number)); + wIn[nChan-1] = tmp; + } + + if (T_PLANAR(info -> InputFormat)) + return accum + sizeof(cmsFloat64Number); + else + return accum + (nChan + Extra) * sizeof(cmsFloat64Number); +} + + + +static +cmsUInt8Number* UnrollFloatTo16(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + + int nChan = T_CHANNELS(info -> InputFormat); + int DoSwap = T_DOSWAP(info ->InputFormat); + int Reverse = T_FLAVOR(info ->InputFormat); + int SwapFirst = T_SWAPFIRST(info -> InputFormat); + int Extra = T_EXTRA(info -> InputFormat); + int ExtraFirst = DoSwap ^ SwapFirst; + int Planar = T_PLANAR(info -> InputFormat); + cmsFloat32Number v; + cmsUInt16Number vi; + int i, start = 0; + cmsFloat64Number maximum = IsInkSpace(info ->InputFormat) ? 655.35 : 65535.0; + + + if (ExtraFirst) + start = Extra; + + for (i=0; i < nChan; i++) { + + int index = DoSwap ? (nChan - i - 1) : i; + + if (Planar) + v = (cmsFloat32Number) ((cmsFloat32Number*) accum)[(i + start) * Stride]; + else + v = (cmsFloat32Number) ((cmsFloat32Number*) accum)[i + start]; + + vi = _cmsQuickSaturateWord(v * maximum); + + if (Reverse) + vi = REVERSE_FLAVOR_16(vi); + + wIn[index] = vi; + } + + + if (Extra == 0 && SwapFirst) { + cmsUInt16Number tmp = wIn[0]; + + memmove(&wIn[0], &wIn[1], (nChan-1) * sizeof(cmsUInt16Number)); + wIn[nChan-1] = tmp; + } + + if (T_PLANAR(info -> InputFormat)) + return accum + sizeof(cmsFloat32Number); + else + return accum + (nChan + Extra) * sizeof(cmsFloat32Number); +} + + + + +// For 1 channel, we need to duplicate data (it comes in 0..1.0 range) +static +cmsUInt8Number* UnrollDouble1Chan(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + cmsFloat64Number* Inks = (cmsFloat64Number*) accum; + + wIn[0] = wIn[1] = wIn[2] = _cmsQuickSaturateWord(Inks[0] * 65535.0); + + return accum + sizeof(cmsFloat64Number); + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +//------------------------------------------------------------------------------------------------------------------- + +// For anything going from cmsFloat32Number +static +cmsUInt8Number* UnrollFloatsToFloat(_cmsTRANSFORM* info, + cmsFloat32Number wIn[], + cmsUInt8Number* accum, + cmsUInt32Number Stride) +{ + + int nChan = T_CHANNELS(info -> InputFormat); + int DoSwap = T_DOSWAP(info ->InputFormat); + int Reverse = T_FLAVOR(info ->InputFormat); + int SwapFirst = T_SWAPFIRST(info -> InputFormat); + int Extra = T_EXTRA(info -> InputFormat); + int ExtraFirst = DoSwap ^ SwapFirst; + int Planar = T_PLANAR(info -> InputFormat); + cmsFloat32Number v; + int i, start = 0; + cmsFloat32Number maximum = IsInkSpace(info ->InputFormat) ? 100.0F : 1.0F; + + + if (ExtraFirst) + start = Extra; + + for (i=0; i < nChan; i++) { + + int index = DoSwap ? (nChan - i - 1) : i; + + if (Planar) + v = (cmsFloat32Number) ((cmsFloat32Number*) accum)[(i + start) * Stride]; + else + v = (cmsFloat32Number) ((cmsFloat32Number*) accum)[i + start]; + + v /= maximum; + + wIn[index] = Reverse ? 1 - v : v; + } + + + if (Extra == 0 && SwapFirst) { + cmsFloat32Number tmp = wIn[0]; + + memmove(&wIn[0], &wIn[1], (nChan-1) * sizeof(cmsFloat32Number)); + wIn[nChan-1] = tmp; + } + + if (T_PLANAR(info -> InputFormat)) + return accum + sizeof(cmsFloat32Number); + else + return accum + (nChan + Extra) * sizeof(cmsFloat32Number); +} + +// For anything going from double + +static +cmsUInt8Number* UnrollDoublesToFloat(_cmsTRANSFORM* info, + cmsFloat32Number wIn[], + cmsUInt8Number* accum, + cmsUInt32Number Stride) +{ + + int nChan = T_CHANNELS(info -> InputFormat); + int DoSwap = T_DOSWAP(info ->InputFormat); + int Reverse = T_FLAVOR(info ->InputFormat); + int SwapFirst = T_SWAPFIRST(info -> InputFormat); + int Extra = T_EXTRA(info -> InputFormat); + int ExtraFirst = DoSwap ^ SwapFirst; + int Planar = T_PLANAR(info -> InputFormat); + cmsFloat64Number v; + int i, start = 0; + cmsFloat64Number maximum = IsInkSpace(info ->InputFormat) ? 100.0 : 1.0; + + + if (ExtraFirst) + start = Extra; + + for (i=0; i < nChan; i++) { + + int index = DoSwap ? (nChan - i - 1) : i; + + if (Planar) + v = (cmsFloat64Number) ((cmsFloat64Number*) accum)[(i + start) * Stride]; + else + v = (cmsFloat64Number) ((cmsFloat64Number*) accum)[i + start]; + + v /= maximum; + + wIn[index] = (cmsFloat32Number) (Reverse ? 1.0 - v : v); + } + + + if (Extra == 0 && SwapFirst) { + cmsFloat32Number tmp = wIn[0]; + + memmove(&wIn[0], &wIn[1], (nChan-1) * sizeof(cmsFloat32Number)); + wIn[nChan-1] = tmp; + } + + if (T_PLANAR(info -> InputFormat)) + return accum + sizeof(cmsFloat64Number); + else + return accum + (nChan + Extra) * sizeof(cmsFloat64Number); +} + + + +// From Lab double to cmsFloat32Number +static +cmsUInt8Number* UnrollLabDoubleToFloat(_cmsTRANSFORM* info, + cmsFloat32Number wIn[], + cmsUInt8Number* accum, + cmsUInt32Number Stride) +{ + cmsFloat64Number* Pt = (cmsFloat64Number*) accum; + + if (T_PLANAR(info -> InputFormat)) { + + wIn[0] = (cmsFloat32Number) (Pt[0] / 100.0); // from 0..100 to 0..1 + wIn[1] = (cmsFloat32Number) ((Pt[Stride] + 128) / 255.0); // form -128..+127 to 0..1 + wIn[2] = (cmsFloat32Number) ((Pt[Stride*2] + 128) / 255.0); + + return accum + sizeof(cmsFloat64Number); + } + else { + + wIn[0] = (cmsFloat32Number) (Pt[0] / 100.0); // from 0..100 to 0..1 + wIn[1] = (cmsFloat32Number) ((Pt[1] + 128) / 255.0); // form -128..+127 to 0..1 + wIn[2] = (cmsFloat32Number) ((Pt[2] + 128) / 255.0); + + accum += sizeof(cmsFloat64Number)*(3 + T_EXTRA(info ->InputFormat)); + return accum; + } +} + +// From Lab double to cmsFloat32Number +static +cmsUInt8Number* UnrollLabFloatToFloat(_cmsTRANSFORM* info, + cmsFloat32Number wIn[], + cmsUInt8Number* accum, + cmsUInt32Number Stride) +{ + cmsFloat32Number* Pt = (cmsFloat32Number*) accum; + + if (T_PLANAR(info -> InputFormat)) { + + wIn[0] = (cmsFloat32Number) (Pt[0] / 100.0); // from 0..100 to 0..1 + wIn[1] = (cmsFloat32Number) ((Pt[Stride] + 128) / 255.0); // form -128..+127 to 0..1 + wIn[2] = (cmsFloat32Number) ((Pt[Stride*2] + 128) / 255.0); + + return accum + sizeof(cmsFloat32Number); + } + else { + + wIn[0] = (cmsFloat32Number) (Pt[0] / 100.0); // from 0..100 to 0..1 + wIn[1] = (cmsFloat32Number) ((Pt[1] + 128) / 255.0); // form -128..+127 to 0..1 + wIn[2] = (cmsFloat32Number) ((Pt[2] + 128) / 255.0); + + accum += sizeof(cmsFloat32Number)*(3 + T_EXTRA(info ->InputFormat)); + return accum; + } +} + + + +// 1.15 fixed point, that means maximum value is MAX_ENCODEABLE_XYZ (0xFFFF) +static +cmsUInt8Number* UnrollXYZDoubleToFloat(_cmsTRANSFORM* info, + cmsFloat32Number wIn[], + cmsUInt8Number* accum, + cmsUInt32Number Stride) +{ + cmsFloat64Number* Pt = (cmsFloat64Number*) accum; + + if (T_PLANAR(info -> InputFormat)) { + + wIn[0] = (cmsFloat32Number) (Pt[0] / MAX_ENCODEABLE_XYZ); + wIn[1] = (cmsFloat32Number) (Pt[Stride] / MAX_ENCODEABLE_XYZ); + wIn[2] = (cmsFloat32Number) (Pt[Stride*2] / MAX_ENCODEABLE_XYZ); + + return accum + sizeof(cmsFloat64Number); + } + else { + + wIn[0] = (cmsFloat32Number) (Pt[0] / MAX_ENCODEABLE_XYZ); + wIn[1] = (cmsFloat32Number) (Pt[1] / MAX_ENCODEABLE_XYZ); + wIn[2] = (cmsFloat32Number) (Pt[2] / MAX_ENCODEABLE_XYZ); + + accum += sizeof(cmsFloat64Number)*(3 + T_EXTRA(info ->InputFormat)); + return accum; + } +} + +static +cmsUInt8Number* UnrollXYZFloatToFloat(_cmsTRANSFORM* info, + cmsFloat32Number wIn[], + cmsUInt8Number* accum, + cmsUInt32Number Stride) +{ + cmsFloat32Number* Pt = (cmsFloat32Number*) accum; + + if (T_PLANAR(info -> InputFormat)) { + + wIn[0] = (cmsFloat32Number) (Pt[0] / MAX_ENCODEABLE_XYZ); + wIn[1] = (cmsFloat32Number) (Pt[Stride] / MAX_ENCODEABLE_XYZ); + wIn[2] = (cmsFloat32Number) (Pt[Stride*2] / MAX_ENCODEABLE_XYZ); + + return accum + sizeof(cmsFloat32Number); + } + else { + + wIn[0] = (cmsFloat32Number) (Pt[0] / MAX_ENCODEABLE_XYZ); + wIn[1] = (cmsFloat32Number) (Pt[1] / MAX_ENCODEABLE_XYZ); + wIn[2] = (cmsFloat32Number) (Pt[2] / MAX_ENCODEABLE_XYZ); + + accum += sizeof(cmsFloat32Number)*(3 + T_EXTRA(info ->InputFormat)); + return accum; + } +} + + + +// Packing routines ----------------------------------------------------------------------------------------------------------- + + +// Generic chunky for byte + +static +cmsUInt8Number* PackAnyBytes(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + int nChan = T_CHANNELS(info -> OutputFormat); + int DoSwap = T_DOSWAP(info ->OutputFormat); + int Reverse = T_FLAVOR(info ->OutputFormat); + int Extra = T_EXTRA(info -> OutputFormat); + int SwapFirst = T_SWAPFIRST(info -> OutputFormat); + int ExtraFirst = DoSwap ^ SwapFirst; + cmsUInt8Number* swap1; + cmsUInt8Number v = 0; + int i; + + swap1 = output; + + if (ExtraFirst) { + output += Extra; + } + + for (i=0; i < nChan; i++) { + + int index = DoSwap ? (nChan - i - 1) : i; + + v = FROM_16_TO_8(wOut[index]); + + if (Reverse) + v = REVERSE_FLAVOR_8(v); + + *output++ = v; + } + + if (!ExtraFirst) { + output += Extra; + } + + if (Extra == 0 && SwapFirst) { + + memmove(swap1 + 1, swap1, nChan-1); + *swap1 = v; + } + + + return output; + + cmsUNUSED_PARAMETER(Stride); +} + + + +static +cmsUInt8Number* PackAnyWords(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + int nChan = T_CHANNELS(info -> OutputFormat); + int SwapEndian = T_ENDIAN16(info -> InputFormat); + int DoSwap = T_DOSWAP(info ->OutputFormat); + int Reverse = T_FLAVOR(info ->OutputFormat); + int Extra = T_EXTRA(info -> OutputFormat); + int SwapFirst = T_SWAPFIRST(info -> OutputFormat); + int ExtraFirst = DoSwap ^ SwapFirst; + cmsUInt16Number* swap1; + cmsUInt16Number v = 0; + int i; + + swap1 = (cmsUInt16Number*) output; + + if (ExtraFirst) { + output += Extra * sizeof(cmsUInt16Number); + } + + for (i=0; i < nChan; i++) { + + int index = DoSwap ? (nChan - i - 1) : i; + + v = wOut[index]; + + if (SwapEndian) + v = CHANGE_ENDIAN(v); + + if (Reverse) + v = REVERSE_FLAVOR_16(v); + + *(cmsUInt16Number*) output = v; + + output += sizeof(cmsUInt16Number); + } + + if (!ExtraFirst) { + output += Extra * sizeof(cmsUInt16Number); + } + + if (Extra == 0 && SwapFirst) { + + memmove(swap1 + 1, swap1, (nChan-1)* sizeof(cmsUInt16Number)); + *swap1 = v; + } + + + return output; + + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* PackPlanarBytes(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + int nChan = T_CHANNELS(info -> OutputFormat); + int DoSwap = T_DOSWAP(info ->OutputFormat); + int SwapFirst = T_SWAPFIRST(info ->OutputFormat); + int Reverse = T_FLAVOR(info ->OutputFormat); + int i; + cmsUInt8Number* Init = output; + + + if (DoSwap ^ SwapFirst) { + output += T_EXTRA(info -> OutputFormat) * Stride; + } + + + for (i=0; i < nChan; i++) { + + int index = DoSwap ? (nChan - i - 1) : i; + cmsUInt8Number v = FROM_16_TO_8(wOut[index]); + + *(cmsUInt8Number*) output = (cmsUInt8Number) (Reverse ? REVERSE_FLAVOR_8(v) : v); + output += Stride; + } + + return (Init + 1); + + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* PackPlanarWords(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + int nChan = T_CHANNELS(info -> OutputFormat); + int DoSwap = T_DOSWAP(info ->OutputFormat); + int Reverse= T_FLAVOR(info ->OutputFormat); + int SwapEndian = T_ENDIAN16(info -> OutputFormat); + int i; + cmsUInt8Number* Init = output; + cmsUInt16Number v; + + if (DoSwap) { + output += T_EXTRA(info -> OutputFormat) * Stride * sizeof(cmsUInt16Number); + } + + for (i=0; i < nChan; i++) { + + int index = DoSwap ? (nChan - i - 1) : i; + + v = wOut[index]; + + if (SwapEndian) + v = CHANGE_ENDIAN(v); + + if (Reverse) + v = REVERSE_FLAVOR_16(v); + + *(cmsUInt16Number*) output = v; + output += (Stride * sizeof(cmsUInt16Number)); + } + + return (Init + sizeof(cmsUInt16Number)); +} + +// CMYKcm (unrolled for speed) + +static +cmsUInt8Number* Pack6Bytes(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[0]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[2]); + *output++ = FROM_16_TO_8(wOut[3]); + *output++ = FROM_16_TO_8(wOut[4]); + *output++ = FROM_16_TO_8(wOut[5]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +// KCMYcm + +static +cmsUInt8Number* Pack6BytesSwap(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[5]); + *output++ = FROM_16_TO_8(wOut[4]); + *output++ = FROM_16_TO_8(wOut[3]); + *output++ = FROM_16_TO_8(wOut[2]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[0]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +// CMYKcm +static +cmsUInt8Number* Pack6Words(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + *(cmsUInt16Number*) output = wOut[1]; + output+= 2; + *(cmsUInt16Number*) output = wOut[2]; + output+= 2; + *(cmsUInt16Number*) output = wOut[3]; + output+= 2; + *(cmsUInt16Number*) output = wOut[4]; + output+= 2; + *(cmsUInt16Number*) output = wOut[5]; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +// KCMYcm +static +cmsUInt8Number* Pack6WordsSwap(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = wOut[5]; + output+= 2; + *(cmsUInt16Number*) output = wOut[4]; + output+= 2; + *(cmsUInt16Number*) output = wOut[3]; + output+= 2; + *(cmsUInt16Number*) output = wOut[2]; + output+= 2; + *(cmsUInt16Number*) output = wOut[1]; + output+= 2; + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack4Bytes(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[0]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[2]); + *output++ = FROM_16_TO_8(wOut[3]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack4BytesReverse(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = REVERSE_FLAVOR_8(FROM_16_TO_8(wOut[0])); + *output++ = REVERSE_FLAVOR_8(FROM_16_TO_8(wOut[1])); + *output++ = REVERSE_FLAVOR_8(FROM_16_TO_8(wOut[2])); + *output++ = REVERSE_FLAVOR_8(FROM_16_TO_8(wOut[3])); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack4BytesSwapFirst(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[3]); + *output++ = FROM_16_TO_8(wOut[0]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[2]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +// ABGR +static +cmsUInt8Number* Pack4BytesSwap(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[3]); + *output++ = FROM_16_TO_8(wOut[2]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[0]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack4BytesSwapSwapFirst(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[2]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[0]); + *output++ = FROM_16_TO_8(wOut[3]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack4Words(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + *(cmsUInt16Number*) output = wOut[1]; + output+= 2; + *(cmsUInt16Number*) output = wOut[2]; + output+= 2; + *(cmsUInt16Number*) output = wOut[3]; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack4WordsReverse(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = REVERSE_FLAVOR_16(wOut[0]); + output+= 2; + *(cmsUInt16Number*) output = REVERSE_FLAVOR_16(wOut[1]); + output+= 2; + *(cmsUInt16Number*) output = REVERSE_FLAVOR_16(wOut[2]); + output+= 2; + *(cmsUInt16Number*) output = REVERSE_FLAVOR_16(wOut[3]); + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +// ABGR +static +cmsUInt8Number* Pack4WordsSwap(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = wOut[3]; + output+= 2; + *(cmsUInt16Number*) output = wOut[2]; + output+= 2; + *(cmsUInt16Number*) output = wOut[1]; + output+= 2; + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +// CMYK +static +cmsUInt8Number* Pack4WordsBigEndian(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[0]); + output+= 2; + *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[1]); + output+= 2; + *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[2]); + output+= 2; + *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[3]); + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* PackLabV2_8(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(FomLabV4ToLabV2(wOut[0])); + *output++ = FROM_16_TO_8(FomLabV4ToLabV2(wOut[1])); + *output++ = FROM_16_TO_8(FomLabV4ToLabV2(wOut[2])); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* PackALabV2_8(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + output++; + *output++ = FROM_16_TO_8(FomLabV4ToLabV2(wOut[0])); + *output++ = FROM_16_TO_8(FomLabV4ToLabV2(wOut[1])); + *output++ = FROM_16_TO_8(FomLabV4ToLabV2(wOut[2])); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* PackLabV2_16(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = FomLabV4ToLabV2(wOut[0]); + output += 2; + *(cmsUInt16Number*) output = FomLabV4ToLabV2(wOut[1]); + output += 2; + *(cmsUInt16Number*) output = FomLabV4ToLabV2(wOut[2]); + output += 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3Bytes(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[0]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[2]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3BytesOptimized(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = (wOut[0] & 0xFF); + *output++ = (wOut[1] & 0xFF); + *output++ = (wOut[2] & 0xFF); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3BytesSwap(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[2]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[0]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3BytesSwapOptimized(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = (wOut[2] & 0xFF); + *output++ = (wOut[1] & 0xFF); + *output++ = (wOut[0] & 0xFF); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack3Words(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + *(cmsUInt16Number*) output = wOut[1]; + output+= 2; + *(cmsUInt16Number*) output = wOut[2]; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3WordsSwap(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = wOut[2]; + output+= 2; + *(cmsUInt16Number*) output = wOut[1]; + output+= 2; + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3WordsBigEndian(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[0]); + output+= 2; + *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[1]); + output+= 2; + *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[2]); + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3BytesAndSkip1(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[0]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[2]); + output++; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3BytesAndSkip1Optimized(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = (wOut[0] & 0xFF); + *output++ = (wOut[1] & 0xFF); + *output++ = (wOut[2] & 0xFF); + output++; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack3BytesAndSkip1SwapFirst(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + output++; + *output++ = FROM_16_TO_8(wOut[0]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[2]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3BytesAndSkip1SwapFirstOptimized(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + output++; + *output++ = (wOut[0] & 0xFF); + *output++ = (wOut[1] & 0xFF); + *output++ = (wOut[2] & 0xFF); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3BytesAndSkip1Swap(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + output++; + *output++ = FROM_16_TO_8(wOut[2]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[0]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3BytesAndSkip1SwapOptimized(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + output++; + *output++ = (wOut[2] & 0xFF); + *output++ = (wOut[1] & 0xFF); + *output++ = (wOut[0] & 0xFF); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack3BytesAndSkip1SwapSwapFirst(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[2]); + *output++ = FROM_16_TO_8(wOut[1]); + *output++ = FROM_16_TO_8(wOut[0]); + output++; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3BytesAndSkip1SwapSwapFirstOptimized(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = (wOut[2] & 0xFF); + *output++ = (wOut[1] & 0xFF); + *output++ = (wOut[0] & 0xFF); + output++; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3WordsAndSkip1(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + *(cmsUInt16Number*) output = wOut[1]; + output+= 2; + *(cmsUInt16Number*) output = wOut[2]; + output+= 2; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack3WordsAndSkip1Swap(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + output+= 2; + *(cmsUInt16Number*) output = wOut[2]; + output+= 2; + *(cmsUInt16Number*) output = wOut[1]; + output+= 2; + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack3WordsAndSkip1SwapFirst(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + output+= 2; + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + *(cmsUInt16Number*) output = wOut[1]; + output+= 2; + *(cmsUInt16Number*) output = wOut[2]; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack3WordsAndSkip1SwapSwapFirst(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = wOut[2]; + output+= 2; + *(cmsUInt16Number*) output = wOut[1]; + output+= 2; + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + + +static +cmsUInt8Number* Pack1Byte(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[0]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack1ByteReversed(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(REVERSE_FLAVOR_16(wOut[0])); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack1ByteSkip1(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *output++ = FROM_16_TO_8(wOut[0]); + output++; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack1ByteSkip1SwapFirst(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + output++; + *output++ = FROM_16_TO_8(wOut[0]); + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack1Word(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack1WordReversed(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = REVERSE_FLAVOR_16(wOut[0]); + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack1WordBigEndian(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[0]); + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +static +cmsUInt8Number* Pack1WordSkip1(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + *(cmsUInt16Number*) output = wOut[0]; + output+= 4; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* Pack1WordSkip1SwapFirst(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + output += 2; + *(cmsUInt16Number*) output = wOut[0]; + output+= 2; + + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + + +// Unencoded Float values -- don't try optimize speed +static +cmsUInt8Number* PackLabDoubleFrom16(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + + if (T_PLANAR(info -> OutputFormat)) { + + cmsCIELab Lab; + cmsFloat64Number* Out = (cmsFloat64Number*) output; + cmsLabEncoded2Float(&Lab, wOut); + + Out[0] = Lab.L; + Out[Stride] = Lab.a; + Out[Stride*2] = Lab.b; + + return output + sizeof(cmsFloat64Number); + } + else { + + cmsLabEncoded2Float((cmsCIELab*) output, wOut); + return output + (sizeof(cmsCIELab) + T_EXTRA(info ->OutputFormat) * sizeof(cmsFloat64Number)); + } +} + + +static +cmsUInt8Number* PackLabFloatFrom16(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + cmsCIELab Lab; + cmsLabEncoded2Float(&Lab, wOut); + + if (T_PLANAR(info -> OutputFormat)) { + + cmsFloat32Number* Out = (cmsFloat32Number*) output; + + Out[0] = (cmsFloat32Number)Lab.L; + Out[Stride] = (cmsFloat32Number)Lab.a; + Out[Stride*2] = (cmsFloat32Number)Lab.b; + + return output + sizeof(cmsFloat32Number); + } + else { + + ((cmsFloat32Number*) output)[0] = (cmsFloat32Number) Lab.L; + ((cmsFloat32Number*) output)[1] = (cmsFloat32Number) Lab.a; + ((cmsFloat32Number*) output)[2] = (cmsFloat32Number) Lab.b; + + return output + (3 + T_EXTRA(info ->OutputFormat)) * sizeof(cmsFloat32Number); + } +} + +static +cmsUInt8Number* PackXYZDoubleFrom16(register _cmsTRANSFORM* Info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + if (T_PLANAR(Info -> OutputFormat)) { + + cmsCIEXYZ XYZ; + cmsFloat64Number* Out = (cmsFloat64Number*) output; + cmsXYZEncoded2Float(&XYZ, wOut); + + Out[0] = XYZ.X; + Out[Stride] = XYZ.Y; + Out[Stride*2] = XYZ.Z; + + return output + sizeof(cmsFloat64Number); + + } + else { + + cmsXYZEncoded2Float((cmsCIEXYZ*) output, wOut); + + return output + (sizeof(cmsCIEXYZ) + T_EXTRA(Info ->OutputFormat) * sizeof(cmsFloat64Number)); + } +} + +static +cmsUInt8Number* PackXYZFloatFrom16(register _cmsTRANSFORM* Info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + if (T_PLANAR(Info -> OutputFormat)) { + + cmsCIEXYZ XYZ; + cmsFloat32Number* Out = (cmsFloat32Number*) output; + cmsXYZEncoded2Float(&XYZ, wOut); + + Out[0] = (cmsFloat32Number) XYZ.X; + Out[Stride] = (cmsFloat32Number) XYZ.Y; + Out[Stride*2] = (cmsFloat32Number) XYZ.Z; + + return output + sizeof(cmsFloat32Number); + + } + else { + + cmsCIEXYZ XYZ; + cmsFloat32Number* Out = (cmsFloat32Number*) output; + cmsXYZEncoded2Float(&XYZ, wOut); + + 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)); + } +} + +static +cmsUInt8Number* PackDoubleFrom16(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + int nChan = T_CHANNELS(info -> OutputFormat); + int DoSwap = T_DOSWAP(info ->OutputFormat); + int Reverse = T_FLAVOR(info ->OutputFormat); + int Extra = T_EXTRA(info -> OutputFormat); + int SwapFirst = T_SWAPFIRST(info -> OutputFormat); + int Planar = T_PLANAR(info -> OutputFormat); + int ExtraFirst = DoSwap ^ SwapFirst; + cmsFloat64Number maximum = IsInkSpace(info ->OutputFormat) ? 655.35 : 65535.0; + cmsFloat64Number v = 0; + cmsFloat64Number* swap1 = (cmsFloat64Number*) output; + int i, start = 0; + + if (ExtraFirst) + start = Extra; + + for (i=0; i < nChan; i++) { + + int index = DoSwap ? (nChan - i - 1) : i; + + v = (cmsFloat64Number) wOut[index] / maximum; + + if (Reverse) + v = maximum - v; + + if (Planar) + ((cmsFloat64Number*) output)[(i + start) * Stride]= v; + else + ((cmsFloat64Number*) output)[i + start] = v; + } + + if (!ExtraFirst) { + output += Extra * sizeof(cmsFloat64Number); + } + + if (Extra == 0 && SwapFirst) { + + memmove(swap1 + 1, swap1, (nChan-1)* sizeof(cmsFloat64Number)); + *swap1 = v; + } + + if (T_PLANAR(info -> OutputFormat)) + return output + sizeof(cmsFloat64Number); + else + return output + nChan * sizeof(cmsFloat64Number); + +} + + +static +cmsUInt8Number* PackFloatFrom16(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + int nChan = T_CHANNELS(info -> OutputFormat); + int DoSwap = T_DOSWAP(info ->OutputFormat); + int Reverse = T_FLAVOR(info ->OutputFormat); + int Extra = T_EXTRA(info -> OutputFormat); + int SwapFirst = T_SWAPFIRST(info -> OutputFormat); + int Planar = T_PLANAR(info -> OutputFormat); + int ExtraFirst = DoSwap ^ SwapFirst; + cmsFloat64Number maximum = IsInkSpace(info ->OutputFormat) ? 655.35 : 65535.0; + cmsFloat64Number v = 0; + cmsFloat32Number* swap1 = (cmsFloat32Number*) output; + int i, start = 0; + + if (ExtraFirst) + start = Extra; + + for (i=0; i < nChan; i++) { + + int index = DoSwap ? (nChan - i - 1) : i; + + v = (cmsFloat64Number) wOut[index] / maximum; + + if (Reverse) + v = maximum - v; + + if (Planar) + ((cmsFloat32Number*) output)[(i + start ) * Stride]= (cmsFloat32Number) v; + else + ((cmsFloat32Number*) output)[i + start] = (cmsFloat32Number) v; + } + + if (!ExtraFirst) { + output += Extra * sizeof(cmsFloat32Number); + } + + if (Extra == 0 && SwapFirst) { + + memmove(swap1 + 1, swap1, (nChan-1)* sizeof(cmsFloat32Number)); + *swap1 = (cmsFloat32Number) v; + } + + if (T_PLANAR(info -> OutputFormat)) + return output + sizeof(cmsFloat32Number); + else + return output + nChan * sizeof(cmsFloat32Number); +} + + + +// -------------------------------------------------------------------------------------------------------- + +static +cmsUInt8Number* PackFloatsFromFloat(_cmsTRANSFORM* info, + cmsFloat32Number wOut[], + cmsUInt8Number* output, + cmsUInt32Number Stride) +{ + int nChan = T_CHANNELS(info -> OutputFormat); + int DoSwap = T_DOSWAP(info ->OutputFormat); + int Reverse = T_FLAVOR(info ->OutputFormat); + int Extra = T_EXTRA(info -> OutputFormat); + int SwapFirst = T_SWAPFIRST(info -> OutputFormat); + int Planar = T_PLANAR(info -> OutputFormat); + int ExtraFirst = DoSwap ^ SwapFirst; + cmsFloat64Number maximum = IsInkSpace(info ->OutputFormat) ? 100.0 : 1.0; + cmsFloat32Number* swap1 = (cmsFloat32Number*) output; + cmsFloat64Number v = 0; + int i, start = 0; + + if (ExtraFirst) + start = Extra; + + for (i=0; i < nChan; i++) { + + int index = DoSwap ? (nChan - i - 1) : i; + + v = wOut[index] * maximum; + + if (Reverse) + v = maximum - v; + + if (Planar) + ((cmsFloat32Number*) output)[(i + start)* Stride]= (cmsFloat32Number) v; + else + ((cmsFloat32Number*) output)[i + start] = (cmsFloat32Number) v; + } + + if (!ExtraFirst) { + output += Extra * sizeof(cmsFloat32Number); + } + + if (Extra == 0 && SwapFirst) { + + memmove(swap1 + 1, swap1, (nChan-1)* sizeof(cmsFloat32Number)); + *swap1 = (cmsFloat32Number) v; + } + + if (T_PLANAR(info -> OutputFormat)) + return output + sizeof(cmsFloat32Number); + else + return output + nChan * sizeof(cmsFloat32Number); +} + +static +cmsUInt8Number* PackDoublesFromFloat(_cmsTRANSFORM* info, + cmsFloat32Number wOut[], + cmsUInt8Number* output, + cmsUInt32Number Stride) +{ + int nChan = T_CHANNELS(info -> OutputFormat); + int DoSwap = T_DOSWAP(info ->OutputFormat); + int Reverse = T_FLAVOR(info ->OutputFormat); + int Extra = T_EXTRA(info -> OutputFormat); + int SwapFirst = T_SWAPFIRST(info -> OutputFormat); + int Planar = T_PLANAR(info -> OutputFormat); + int ExtraFirst = DoSwap ^ SwapFirst; + cmsFloat64Number maximum = IsInkSpace(info ->OutputFormat) ? 100.0 : 1.0; + cmsFloat64Number v = 0; + cmsFloat64Number* swap1 = (cmsFloat64Number*) output; + int i, start = 0; + + if (ExtraFirst) + start = Extra; + + for (i=0; i < nChan; i++) { + + int index = DoSwap ? (nChan - i - 1) : i; + + v = wOut[index] * maximum; + + if (Reverse) + v = maximum - v; + + if (Planar) + ((cmsFloat64Number*) output)[(i + start) * Stride] = v; + else + ((cmsFloat64Number*) output)[i + start] = v; + } + + if (!ExtraFirst) { + output += Extra * sizeof(cmsFloat64Number); + } + + if (Extra == 0 && SwapFirst) { + + memmove(swap1 + 1, swap1, (nChan-1)* sizeof(cmsFloat64Number)); + *swap1 = v; + } + + + if (T_PLANAR(info -> OutputFormat)) + return output + sizeof(cmsFloat64Number); + else + return output + nChan * sizeof(cmsFloat64Number); + +} + + + + + +static +cmsUInt8Number* PackLabFloatFromFloat(_cmsTRANSFORM* Info, + cmsFloat32Number wOut[], + cmsUInt8Number* output, + cmsUInt32Number Stride) +{ + cmsFloat32Number* Out = (cmsFloat32Number*) output; + + if (T_PLANAR(Info -> OutputFormat)) { + + Out[0] = (cmsFloat32Number) (wOut[0] * 100.0); + Out[Stride] = (cmsFloat32Number) (wOut[1] * 255.0 - 128.0); + Out[Stride*2] = (cmsFloat32Number) (wOut[2] * 255.0 - 128.0); + + return output + sizeof(cmsFloat32Number); + } + else { + + Out[0] = (cmsFloat32Number) (wOut[0] * 100.0); + Out[1] = (cmsFloat32Number) (wOut[1] * 255.0 - 128.0); + Out[2] = (cmsFloat32Number) (wOut[2] * 255.0 - 128.0); + + return output + (sizeof(cmsFloat32Number)*3 + T_EXTRA(Info ->OutputFormat) * sizeof(cmsFloat32Number)); + } + +} + + +static +cmsUInt8Number* PackLabDoubleFromFloat(_cmsTRANSFORM* Info, + cmsFloat32Number wOut[], + cmsUInt8Number* output, + cmsUInt32Number Stride) +{ + cmsFloat64Number* Out = (cmsFloat64Number*) output; + + if (T_PLANAR(Info -> OutputFormat)) { + + Out[0] = (cmsFloat64Number) (wOut[0] * 100.0); + Out[Stride] = (cmsFloat64Number) (wOut[1] * 255.0 - 128.0); + Out[Stride*2] = (cmsFloat64Number) (wOut[2] * 255.0 - 128.0); + + return output + sizeof(cmsFloat64Number); + } + else { + + Out[0] = (cmsFloat64Number) (wOut[0] * 100.0); + Out[1] = (cmsFloat64Number) (wOut[1] * 255.0 - 128.0); + Out[2] = (cmsFloat64Number) (wOut[2] * 255.0 - 128.0); + + return output + (sizeof(cmsFloat64Number)*3 + T_EXTRA(Info ->OutputFormat) * sizeof(cmsFloat64Number)); + } + +} + + +// From 0..1 range to 0..MAX_ENCODEABLE_XYZ +static +cmsUInt8Number* PackXYZFloatFromFloat(_cmsTRANSFORM* Info, + cmsFloat32Number wOut[], + cmsUInt8Number* output, + cmsUInt32Number Stride) +{ + cmsFloat32Number* Out = (cmsFloat32Number*) output; + + if (T_PLANAR(Info -> OutputFormat)) { + + Out[0] = (cmsFloat32Number) (wOut[0] * MAX_ENCODEABLE_XYZ); + Out[Stride] = (cmsFloat32Number) (wOut[1] * MAX_ENCODEABLE_XYZ); + Out[Stride*2] = (cmsFloat32Number) (wOut[2] * MAX_ENCODEABLE_XYZ); + + return output + sizeof(cmsFloat32Number); + } + else { + + Out[0] = (cmsFloat32Number) (wOut[0] * MAX_ENCODEABLE_XYZ); + Out[1] = (cmsFloat32Number) (wOut[1] * MAX_ENCODEABLE_XYZ); + Out[2] = (cmsFloat32Number) (wOut[2] * MAX_ENCODEABLE_XYZ); + + return output + (sizeof(cmsFloat32Number)*3 + T_EXTRA(Info ->OutputFormat) * sizeof(cmsFloat32Number)); + } + +} + +// Same, but convert to double +static +cmsUInt8Number* PackXYZDoubleFromFloat(_cmsTRANSFORM* Info, + cmsFloat32Number wOut[], + cmsUInt8Number* output, + cmsUInt32Number Stride) +{ + cmsFloat64Number* Out = (cmsFloat64Number*) output; + + if (T_PLANAR(Info -> OutputFormat)) { + + Out[0] = (cmsFloat64Number) (wOut[0] * MAX_ENCODEABLE_XYZ); + Out[Stride] = (cmsFloat64Number) (wOut[1] * MAX_ENCODEABLE_XYZ); + Out[Stride*2] = (cmsFloat64Number) (wOut[2] * MAX_ENCODEABLE_XYZ); + + return output + sizeof(cmsFloat64Number); + } + else { + + Out[0] = (cmsFloat64Number) (wOut[0] * MAX_ENCODEABLE_XYZ); + Out[1] = (cmsFloat64Number) (wOut[1] * MAX_ENCODEABLE_XYZ); + Out[2] = (cmsFloat64Number) (wOut[2] * MAX_ENCODEABLE_XYZ); + + return output + (sizeof(cmsFloat64Number)*3 + T_EXTRA(Info ->OutputFormat) * sizeof(cmsFloat64Number)); + } + +} + + +// ---------------------------------------------------------------------------------------------------------------- + +#ifndef CMS_NO_HALF_SUPPORT + +// Decodes an stream of half floats to wIn[] described by input format + +static +cmsUInt8Number* UnrollHalfTo16(register _cmsTRANSFORM* info, + register cmsUInt16Number wIn[], + register cmsUInt8Number* accum, + register cmsUInt32Number Stride) +{ + + int nChan = T_CHANNELS(info -> InputFormat); + int DoSwap = T_DOSWAP(info ->InputFormat); + int Reverse = T_FLAVOR(info ->InputFormat); + int SwapFirst = T_SWAPFIRST(info -> InputFormat); + int Extra = T_EXTRA(info -> InputFormat); + int ExtraFirst = DoSwap ^ SwapFirst; + int Planar = T_PLANAR(info -> InputFormat); + cmsFloat32Number v; + int i, start = 0; + cmsFloat32Number maximum = IsInkSpace(info ->InputFormat) ? 655.35F : 65535.0F; + + + if (ExtraFirst) + start = Extra; + + for (i=0; i < nChan; i++) { + + int index = DoSwap ? (nChan - i - 1) : i; + + if (Planar) + v = _cmsHalf2Float ( ((cmsUInt16Number*) accum)[(i + start) * Stride] ); + else + v = _cmsHalf2Float ( ((cmsUInt16Number*) accum)[i + start] ) ; + + if (Reverse) v = maximum - v; + + wIn[index] = _cmsQuickSaturateWord(v * maximum); + } + + + if (Extra == 0 && SwapFirst) { + cmsUInt16Number tmp = wIn[0]; + + memmove(&wIn[0], &wIn[1], (nChan-1) * sizeof(cmsUInt16Number)); + wIn[nChan-1] = tmp; + } + + if (T_PLANAR(info -> InputFormat)) + return accum + sizeof(cmsUInt16Number); + else + return accum + (nChan + Extra) * sizeof(cmsUInt16Number); +} + +// Decodes an stream of half floats to wIn[] described by input format + +static +cmsUInt8Number* UnrollHalfToFloat(_cmsTRANSFORM* info, + cmsFloat32Number wIn[], + cmsUInt8Number* accum, + cmsUInt32Number Stride) +{ + + int nChan = T_CHANNELS(info -> InputFormat); + int DoSwap = T_DOSWAP(info ->InputFormat); + int Reverse = T_FLAVOR(info ->InputFormat); + int SwapFirst = T_SWAPFIRST(info -> InputFormat); + int Extra = T_EXTRA(info -> InputFormat); + int ExtraFirst = DoSwap ^ SwapFirst; + int Planar = T_PLANAR(info -> InputFormat); + cmsFloat32Number v; + int i, start = 0; + cmsFloat32Number maximum = IsInkSpace(info ->InputFormat) ? 100.0F : 1.0F; + + + if (ExtraFirst) + start = Extra; + + for (i=0; i < nChan; i++) { + + int index = DoSwap ? (nChan - i - 1) : i; + + if (Planar) + v = _cmsHalf2Float ( ((cmsUInt16Number*) accum)[(i + start) * Stride] ); + else + v = _cmsHalf2Float ( ((cmsUInt16Number*) accum)[i + start] ) ; + + v /= maximum; + + wIn[index] = Reverse ? 1 - v : v; + } + + + if (Extra == 0 && SwapFirst) { + cmsFloat32Number tmp = wIn[0]; + + memmove(&wIn[0], &wIn[1], (nChan-1) * sizeof(cmsFloat32Number)); + wIn[nChan-1] = tmp; + } + + if (T_PLANAR(info -> InputFormat)) + return accum + sizeof(cmsUInt16Number); + else + return accum + (nChan + Extra) * sizeof(cmsUInt16Number); +} + + +static +cmsUInt8Number* PackHalfFrom16(register _cmsTRANSFORM* info, + register cmsUInt16Number wOut[], + register cmsUInt8Number* output, + register cmsUInt32Number Stride) +{ + int nChan = T_CHANNELS(info -> OutputFormat); + int DoSwap = T_DOSWAP(info ->OutputFormat); + int Reverse = T_FLAVOR(info ->OutputFormat); + int Extra = T_EXTRA(info -> OutputFormat); + int SwapFirst = T_SWAPFIRST(info -> OutputFormat); + int Planar = T_PLANAR(info -> OutputFormat); + int ExtraFirst = DoSwap ^ SwapFirst; + cmsFloat32Number maximum = IsInkSpace(info ->OutputFormat) ? 655.35F : 65535.0F; + cmsFloat32Number v = 0; + cmsUInt16Number* swap1 = (cmsUInt16Number*) output; + int i, start = 0; + + if (ExtraFirst) + start = Extra; + + for (i=0; i < nChan; i++) { + + int index = DoSwap ? (nChan - i - 1) : i; + + v = (cmsFloat32Number) wOut[index] / maximum; + + if (Reverse) + v = maximum - v; + + if (Planar) + ((cmsUInt16Number*) output)[(i + start ) * Stride]= _cmsFloat2Half(v); + else + ((cmsUInt16Number*) output)[i + start] = _cmsFloat2Half(v); + } + + if (!ExtraFirst) { + output += Extra * sizeof(cmsUInt16Number); + } + + if (Extra == 0 && SwapFirst) { + + memmove(swap1 + 1, swap1, (nChan-1)* sizeof(cmsUInt16Number)); + *swap1 = _cmsFloat2Half(v); + } + + if (T_PLANAR(info -> OutputFormat)) + return output + sizeof(cmsUInt16Number); + else + return output + nChan * sizeof(cmsUInt16Number); +} + + + +static +cmsUInt8Number* PackHalfFromFloat(_cmsTRANSFORM* info, + cmsFloat32Number wOut[], + cmsUInt8Number* output, + cmsUInt32Number Stride) +{ + int nChan = T_CHANNELS(info -> OutputFormat); + int DoSwap = T_DOSWAP(info ->OutputFormat); + int Reverse = T_FLAVOR(info ->OutputFormat); + int Extra = T_EXTRA(info -> OutputFormat); + int SwapFirst = T_SWAPFIRST(info -> OutputFormat); + int Planar = T_PLANAR(info -> OutputFormat); + int ExtraFirst = DoSwap ^ SwapFirst; + cmsFloat32Number maximum = IsInkSpace(info ->OutputFormat) ? 100.0F : 1.0F; + cmsUInt16Number* swap1 = (cmsUInt16Number*) output; + cmsFloat32Number v = 0; + int i, start = 0; + + if (ExtraFirst) + start = Extra; + + for (i=0; i < nChan; i++) { + + int index = DoSwap ? (nChan - i - 1) : i; + + v = wOut[index] * maximum; + + if (Reverse) + v = maximum - v; + + if (Planar) + ((cmsUInt16Number*) output)[(i + start)* Stride]= _cmsFloat2Half( v ); + else + ((cmsUInt16Number*) output)[i + start] = _cmsFloat2Half( v ); + } + + if (!ExtraFirst) { + output += Extra * sizeof(cmsUInt16Number); + } + + if (Extra == 0 && SwapFirst) { + + memmove(swap1 + 1, swap1, (nChan-1)* sizeof(cmsUInt16Number)); + *swap1 = (cmsUInt16Number) _cmsFloat2Half( v ); + } + + if (T_PLANAR(info -> OutputFormat)) + return output + sizeof(cmsUInt16Number); + else + return output + nChan * sizeof(cmsUInt16Number); +} + +#endif + +// ---------------------------------------------------------------------------------------------------------------- + + +static cmsFormatters16 InputFormatters16[] = { + + // Type Mask Function + // ---------------------------- ------------------------------------ ---------------------------- + { TYPE_Lab_DBL, ANYPLANAR|ANYEXTRA, UnrollLabDoubleTo16}, + { TYPE_XYZ_DBL, ANYPLANAR|ANYEXTRA, UnrollXYZDoubleTo16}, + { TYPE_Lab_FLT, ANYPLANAR|ANYEXTRA, UnrollLabFloatTo16}, + { TYPE_XYZ_FLT, ANYPLANAR|ANYEXTRA, UnrollXYZFloatTo16}, + { TYPE_GRAY_DBL, 0, UnrollDouble1Chan}, + { FLOAT_SH(1)|BYTES_SH(0), ANYCHANNELS|ANYPLANAR|ANYSWAPFIRST|ANYFLAVOR| + ANYSWAP|ANYEXTRA|ANYSPACE, UnrollDoubleTo16}, + { FLOAT_SH(1)|BYTES_SH(4), ANYCHANNELS|ANYPLANAR|ANYSWAPFIRST|ANYFLAVOR| + ANYSWAP|ANYEXTRA|ANYSPACE, UnrollFloatTo16}, +#ifndef CMS_NO_HALF_SUPPORT + { FLOAT_SH(1)|BYTES_SH(2), ANYCHANNELS|ANYPLANAR|ANYSWAPFIRST|ANYFLAVOR| + ANYEXTRA|ANYSWAP|ANYSPACE, UnrollHalfTo16}, +#endif + + { CHANNELS_SH(1)|BYTES_SH(1), ANYSPACE, Unroll1Byte}, + { CHANNELS_SH(1)|BYTES_SH(1)|EXTRA_SH(1), ANYSPACE, Unroll1ByteSkip1}, + { CHANNELS_SH(1)|BYTES_SH(1)|EXTRA_SH(2), ANYSPACE, Unroll1ByteSkip2}, + { CHANNELS_SH(1)|BYTES_SH(1)|FLAVOR_SH(1), ANYSPACE, Unroll1ByteReversed}, + { COLORSPACE_SH(PT_MCH2)|CHANNELS_SH(2)|BYTES_SH(1), 0, Unroll2Bytes}, + + { TYPE_LabV2_8, 0, UnrollLabV2_8 }, + { TYPE_ALabV2_8, 0, UnrollALabV2_8 }, + { TYPE_LabV2_16, 0, UnrollLabV2_16 }, + + { CHANNELS_SH(3)|BYTES_SH(1), ANYSPACE, Unroll3Bytes}, + { CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1), ANYSPACE, Unroll3BytesSwap}, + { CHANNELS_SH(3)|EXTRA_SH(1)|BYTES_SH(1)|DOSWAP_SH(1), ANYSPACE, Unroll3BytesSkip1Swap}, + { CHANNELS_SH(3)|EXTRA_SH(1)|BYTES_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Unroll3BytesSkip1SwapFirst}, + + { CHANNELS_SH(3)|EXTRA_SH(1)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1), + ANYSPACE, Unroll3BytesSkip1SwapSwapFirst}, + + { CHANNELS_SH(4)|BYTES_SH(1), ANYSPACE, Unroll4Bytes}, + { CHANNELS_SH(4)|BYTES_SH(1)|FLAVOR_SH(1), ANYSPACE, Unroll4BytesReverse}, + { CHANNELS_SH(4)|BYTES_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Unroll4BytesSwapFirst}, + { CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1), ANYSPACE, Unroll4BytesSwap}, + { CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Unroll4BytesSwapSwapFirst}, + + { BYTES_SH(1)|PLANAR_SH(1), ANYFLAVOR|ANYSWAPFIRST| + ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, UnrollPlanarBytes}, + + { BYTES_SH(1), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP| + ANYEXTRA|ANYCHANNELS|ANYSPACE, UnrollChunkyBytes}, + + { CHANNELS_SH(1)|BYTES_SH(2), ANYSPACE, Unroll1Word}, + { CHANNELS_SH(1)|BYTES_SH(2)|FLAVOR_SH(1), ANYSPACE, Unroll1WordReversed}, + { CHANNELS_SH(1)|BYTES_SH(2)|EXTRA_SH(3), ANYSPACE, Unroll1WordSkip3}, + + { CHANNELS_SH(2)|BYTES_SH(2), ANYSPACE, Unroll2Words}, + { CHANNELS_SH(3)|BYTES_SH(2), ANYSPACE, Unroll3Words}, + { CHANNELS_SH(4)|BYTES_SH(2), ANYSPACE, Unroll4Words}, + + { CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1), ANYSPACE, Unroll3WordsSwap}, + { CHANNELS_SH(3)|BYTES_SH(2)|EXTRA_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Unroll3WordsSkip1SwapFirst}, + { CHANNELS_SH(3)|BYTES_SH(2)|EXTRA_SH(1)|DOSWAP_SH(1), ANYSPACE, Unroll3WordsSkip1Swap}, + { CHANNELS_SH(4)|BYTES_SH(2)|FLAVOR_SH(1), ANYSPACE, Unroll4WordsReverse}, + { CHANNELS_SH(4)|BYTES_SH(2)|SWAPFIRST_SH(1), ANYSPACE, Unroll4WordsSwapFirst}, + { CHANNELS_SH(4)|BYTES_SH(2)|DOSWAP_SH(1), ANYSPACE, Unroll4WordsSwap}, + { CHANNELS_SH(4)|BYTES_SH(2)|DOSWAP_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Unroll4WordsSwapSwapFirst}, + + + { BYTES_SH(2)|PLANAR_SH(1), ANYFLAVOR|ANYSWAP|ANYENDIAN|ANYEXTRA|ANYCHANNELS|ANYSPACE, UnrollPlanarWords}, + { BYTES_SH(2), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYENDIAN|ANYEXTRA|ANYCHANNELS|ANYSPACE, UnrollAnyWords}, +}; + + + +static cmsFormattersFloat InputFormattersFloat[] = { + + // Type Mask Function + // ---------------------------- ------------------------------------ ---------------------------- + { TYPE_Lab_DBL, ANYPLANAR|ANYEXTRA, UnrollLabDoubleToFloat}, + { TYPE_Lab_FLT, ANYPLANAR|ANYEXTRA, UnrollLabFloatToFloat}, + + { TYPE_XYZ_DBL, ANYPLANAR|ANYEXTRA, UnrollXYZDoubleToFloat}, + { TYPE_XYZ_FLT, ANYPLANAR|ANYEXTRA, UnrollXYZFloatToFloat}, + + { FLOAT_SH(1)|BYTES_SH(4), ANYPLANAR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA| + ANYCHANNELS|ANYSPACE, UnrollFloatsToFloat}, + + { FLOAT_SH(1)|BYTES_SH(0), ANYPLANAR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA| + ANYCHANNELS|ANYSPACE, UnrollDoublesToFloat}, +#ifndef CMS_NO_HALF_SUPPORT + { FLOAT_SH(1)|BYTES_SH(2), ANYPLANAR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA| + ANYCHANNELS|ANYSPACE, UnrollHalfToFloat}, +#endif +}; + + +// Bit fields set to one in the mask are not compared +static +cmsFormatter _cmsGetStockInputFormatter(cmsUInt32Number dwInput, cmsUInt32Number dwFlags) +{ + cmsUInt32Number i; + cmsFormatter fr; + + switch (dwFlags) { + + case CMS_PACK_FLAGS_16BITS: { + for (i=0; i < sizeof(InputFormatters16) / sizeof(cmsFormatters16); i++) { + cmsFormatters16* f = InputFormatters16 + i; + + if ((dwInput & ~f ->Mask) == f ->Type) { + fr.Fmt16 = f ->Frm; + return fr; + } + } + } + break; + + case CMS_PACK_FLAGS_FLOAT: { + for (i=0; i < sizeof(InputFormattersFloat) / sizeof(cmsFormattersFloat); i++) { + cmsFormattersFloat* f = InputFormattersFloat + i; + + if ((dwInput & ~f ->Mask) == f ->Type) { + fr.FmtFloat = f ->Frm; + return fr; + } + } + } + break; + + default:; + + } + + fr.Fmt16 = NULL; + return fr; +} + +static cmsFormatters16 OutputFormatters16[] = { + // Type Mask Function + // ---------------------------- ------------------------------------ ---------------------------- + + { TYPE_Lab_DBL, ANYPLANAR|ANYEXTRA, PackLabDoubleFrom16}, + { TYPE_XYZ_DBL, ANYPLANAR|ANYEXTRA, PackXYZDoubleFrom16}, + + { TYPE_Lab_FLT, ANYPLANAR|ANYEXTRA, PackLabFloatFrom16}, + { TYPE_XYZ_FLT, ANYPLANAR|ANYEXTRA, PackXYZFloatFrom16}, + + { FLOAT_SH(1)|BYTES_SH(0), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP| + ANYCHANNELS|ANYPLANAR|ANYEXTRA|ANYSPACE, PackDoubleFrom16}, + { FLOAT_SH(1)|BYTES_SH(4), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP| + ANYCHANNELS|ANYPLANAR|ANYEXTRA|ANYSPACE, PackFloatFrom16}, +#ifndef CMS_NO_HALF_SUPPORT + { FLOAT_SH(1)|BYTES_SH(2), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP| + ANYCHANNELS|ANYPLANAR|ANYEXTRA|ANYSPACE, PackHalfFrom16}, +#endif + + { CHANNELS_SH(1)|BYTES_SH(1), ANYSPACE, Pack1Byte}, + { CHANNELS_SH(1)|BYTES_SH(1)|EXTRA_SH(1), ANYSPACE, Pack1ByteSkip1}, + { CHANNELS_SH(1)|BYTES_SH(1)|EXTRA_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Pack1ByteSkip1SwapFirst}, + + { CHANNELS_SH(1)|BYTES_SH(1)|FLAVOR_SH(1), ANYSPACE, Pack1ByteReversed}, + + { TYPE_LabV2_8, 0, PackLabV2_8 }, + { TYPE_ALabV2_8, 0, PackALabV2_8 }, + { TYPE_LabV2_16, 0, PackLabV2_16 }, + + { CHANNELS_SH(3)|BYTES_SH(1)|OPTIMIZED_SH(1), ANYSPACE, Pack3BytesOptimized}, + { CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|OPTIMIZED_SH(1), ANYSPACE, Pack3BytesAndSkip1Optimized}, + { CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|SWAPFIRST_SH(1)|OPTIMIZED_SH(1), + ANYSPACE, Pack3BytesAndSkip1SwapFirstOptimized}, + { CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1)|OPTIMIZED_SH(1), + ANYSPACE, Pack3BytesAndSkip1SwapSwapFirstOptimized}, + { CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|EXTRA_SH(1)|OPTIMIZED_SH(1), + ANYSPACE, Pack3BytesAndSkip1SwapOptimized}, + { CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|OPTIMIZED_SH(1), ANYSPACE, Pack3BytesSwapOptimized}, + + + + { CHANNELS_SH(3)|BYTES_SH(1), ANYSPACE, Pack3Bytes}, + { CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1), ANYSPACE, Pack3BytesAndSkip1}, + { CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Pack3BytesAndSkip1SwapFirst}, + { CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1), + ANYSPACE, Pack3BytesAndSkip1SwapSwapFirst}, + { CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|EXTRA_SH(1), ANYSPACE, Pack3BytesAndSkip1Swap}, + { CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1), ANYSPACE, Pack3BytesSwap}, + { CHANNELS_SH(6)|BYTES_SH(1), ANYSPACE, Pack6Bytes}, + { CHANNELS_SH(6)|BYTES_SH(1)|DOSWAP_SH(1), ANYSPACE, Pack6BytesSwap}, + { CHANNELS_SH(4)|BYTES_SH(1), ANYSPACE, Pack4Bytes}, + { CHANNELS_SH(4)|BYTES_SH(1)|FLAVOR_SH(1), ANYSPACE, Pack4BytesReverse}, + { CHANNELS_SH(4)|BYTES_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Pack4BytesSwapFirst}, + { CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1), ANYSPACE, Pack4BytesSwap}, + { CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Pack4BytesSwapSwapFirst}, + + { BYTES_SH(1), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackAnyBytes}, + { BYTES_SH(1)|PLANAR_SH(1), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackPlanarBytes}, + + { CHANNELS_SH(1)|BYTES_SH(2), ANYSPACE, Pack1Word}, + { CHANNELS_SH(1)|BYTES_SH(2)|EXTRA_SH(1), ANYSPACE, Pack1WordSkip1}, + { CHANNELS_SH(1)|BYTES_SH(2)|EXTRA_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Pack1WordSkip1SwapFirst}, + { CHANNELS_SH(1)|BYTES_SH(2)|FLAVOR_SH(1), ANYSPACE, Pack1WordReversed}, + { CHANNELS_SH(1)|BYTES_SH(2)|ENDIAN16_SH(1), ANYSPACE, Pack1WordBigEndian}, + { CHANNELS_SH(3)|BYTES_SH(2), ANYSPACE, Pack3Words}, + { CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1), ANYSPACE, Pack3WordsSwap}, + { CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1), ANYSPACE, Pack3WordsBigEndian}, + { CHANNELS_SH(3)|BYTES_SH(2)|EXTRA_SH(1), ANYSPACE, Pack3WordsAndSkip1}, + { CHANNELS_SH(3)|BYTES_SH(2)|EXTRA_SH(1)|DOSWAP_SH(1), ANYSPACE, Pack3WordsAndSkip1Swap}, + { CHANNELS_SH(3)|BYTES_SH(2)|EXTRA_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Pack3WordsAndSkip1SwapFirst}, + + { CHANNELS_SH(3)|BYTES_SH(2)|EXTRA_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1), + ANYSPACE, Pack3WordsAndSkip1SwapSwapFirst}, + + { CHANNELS_SH(4)|BYTES_SH(2), ANYSPACE, Pack4Words}, + { CHANNELS_SH(4)|BYTES_SH(2)|FLAVOR_SH(1), ANYSPACE, Pack4WordsReverse}, + { CHANNELS_SH(4)|BYTES_SH(2)|DOSWAP_SH(1), ANYSPACE, Pack4WordsSwap}, + { CHANNELS_SH(4)|BYTES_SH(2)|ENDIAN16_SH(1), ANYSPACE, Pack4WordsBigEndian}, + + { CHANNELS_SH(6)|BYTES_SH(2), ANYSPACE, Pack6Words}, + { CHANNELS_SH(6)|BYTES_SH(2)|DOSWAP_SH(1), ANYSPACE, Pack6WordsSwap}, + + { BYTES_SH(2)|PLANAR_SH(1), ANYFLAVOR|ANYENDIAN|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackPlanarWords}, + { BYTES_SH(2), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYENDIAN|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackAnyWords} + +}; + + +static cmsFormattersFloat OutputFormattersFloat[] = { + // Type Mask Function + // ---------------------------- --------------------------------------------------- ---------------------------- + { TYPE_Lab_FLT, ANYPLANAR|ANYEXTRA, PackLabFloatFromFloat}, + { TYPE_XYZ_FLT, ANYPLANAR|ANYEXTRA, PackXYZFloatFromFloat}, + + { TYPE_Lab_DBL, ANYPLANAR|ANYEXTRA, PackLabDoubleFromFloat}, + { TYPE_XYZ_DBL, ANYPLANAR|ANYEXTRA, PackXYZDoubleFromFloat}, + + { FLOAT_SH(1)|BYTES_SH(4), ANYPLANAR| + ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackFloatsFromFloat }, + { FLOAT_SH(1)|BYTES_SH(0), ANYPLANAR| + ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackDoublesFromFloat }, +#ifndef CMS_NO_HALF_SUPPORT + { FLOAT_SH(1)|BYTES_SH(2), + ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackHalfFromFloat }, +#endif + + + +}; + + +// Bit fields set to one in the mask are not compared +static +cmsFormatter _cmsGetStockOutputFormatter(cmsUInt32Number dwInput, cmsUInt32Number dwFlags) +{ + cmsUInt32Number i; + cmsFormatter fr; + + + switch (dwFlags) + { + + case CMS_PACK_FLAGS_16BITS: { + + for (i=0; i < sizeof(OutputFormatters16) / sizeof(cmsFormatters16); i++) { + cmsFormatters16* f = OutputFormatters16 + i; + + if ((dwInput & ~f ->Mask) == f ->Type) { + fr.Fmt16 = f ->Frm; + return fr; + } + } + } + break; + + case CMS_PACK_FLAGS_FLOAT: { + + for (i=0; i < sizeof(OutputFormattersFloat) / sizeof(cmsFormattersFloat); i++) { + cmsFormattersFloat* f = OutputFormattersFloat + i; + + if ((dwInput & ~f ->Mask) == f ->Type) { + fr.FmtFloat = f ->Frm; + return fr; + } + } + } + break; + + default:; + + } + + fr.Fmt16 = NULL; + return fr; +} + + +typedef struct _cms_formatters_factory_list { + + cmsFormatterFactory Factory; + struct _cms_formatters_factory_list *Next; + +} cmsFormattersFactoryList; + +_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 ContextID, cmsPluginBase* Data) +{ + _cmsFormattersPluginChunkType* ctx = ( _cmsFormattersPluginChunkType*) _cmsContextGetClientChunk(ContextID, FormattersPlugin); + cmsPluginFormatters* Plugin = (cmsPluginFormatters*) Data; + cmsFormattersFactoryList* fl ; + + // Reset to built-in defaults + if (Data == NULL) { + + ctx ->FactoryList = NULL; + return TRUE; + } + + fl = (cmsFormattersFactoryList*) _cmsPluginMalloc(ContextID, sizeof(cmsFormattersFactoryList)); + if (fl == NULL) return FALSE; + + fl ->Factory = Plugin ->FormattersFactory; + + fl ->Next = ctx -> FactoryList; + ctx ->FactoryList = fl; + + return TRUE; +} + +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 =ctx->FactoryList; f != NULL; f = f ->Next) { + + cmsFormatter fn = f ->Factory(Type, Dir, dwFlags); + if (fn.Fmt16 != NULL) return fn; + } + + // Revert to default + if (Dir == cmsFormatterInput) + return _cmsGetStockInputFormatter(Type, dwFlags); + else + return _cmsGetStockOutputFormatter(Type, dwFlags); +} + + +// Return whatever given formatter refers to float values +cmsBool _cmsFormatterIsFloat(cmsUInt32Number Type) +{ + return T_FLOAT(Type) ? TRUE : FALSE; +} + +// Return whatever given formatter refers to 8 bits +cmsBool _cmsFormatterIs8bit(cmsUInt32Number Type) +{ + int Bytes = T_BYTES(Type); + + return (Bytes == 1); +} + +// Build a suitable formatter for the colorspace of this profile +cmsUInt32Number CMSEXPORT cmsFormatterForColorspaceOfProfile(cmsHPROFILE hProfile, cmsUInt32Number nBytes, cmsBool lIsFloat) +{ + + cmsColorSpaceSignature ColorSpace = cmsGetColorSpace(hProfile); + cmsUInt32Number ColorSpaceBits = _cmsLCMScolorSpace(ColorSpace); + cmsUInt32Number nOutputChans = cmsChannelsOf(ColorSpace); + cmsUInt32Number Float = lIsFloat ? 1 : 0; + + // Create a fake formatter for result + return FLOAT_SH(Float) | COLORSPACE_SH(ColorSpaceBits) | BYTES_SH(nBytes) | CHANNELS_SH(nOutputChans); +} + +// Build a suitable formatter for the colorspace of this profile +cmsUInt32Number CMSEXPORT cmsFormatterForPCSOfProfile(cmsHPROFILE hProfile, cmsUInt32Number nBytes, cmsBool lIsFloat) +{ + + cmsColorSpaceSignature ColorSpace = cmsGetPCS(hProfile); + int ColorSpaceBits = _cmsLCMScolorSpace(ColorSpace); + cmsUInt32Number nOutputChans = cmsChannelsOf(ColorSpace); + cmsUInt32Number Float = lIsFloat ? 1 : 0; + + // Create a fake formatter for result + return FLOAT_SH(Float) | COLORSPACE_SH(ColorSpaceBits) | BYTES_SH(nBytes) | CHANNELS_SH(nOutputChans); +} diff --git a/third_party/lcms/src/cmspcs.c b/third_party/lcms/src/cmspcs.c new file mode 100644 index 0000000000..102cd7d21e --- /dev/null +++ b/third_party/lcms/src/cmspcs.c @@ -0,0 +1,931 @@ +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2010 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + +// inter PCS conversions XYZ <-> CIE L* a* b* +/* + + + CIE 15:2004 CIELab is defined as: + + L* = 116*f(Y/Yn) - 16 0 <= L* <= 100 + a* = 500*[f(X/Xn) - f(Y/Yn)] + b* = 200*[f(Y/Yn) - f(Z/Zn)] + + and + + f(t) = t^(1/3) 1 >= t > (24/116)^3 + (841/108)*t + (16/116) 0 <= t <= (24/116)^3 + + + Reverse transform is: + + X = Xn*[a* / 500 + (L* + 16) / 116] ^ 3 if (X/Xn) > (24/116) + = Xn*(a* / 500 + L* / 116) / 7.787 if (X/Xn) <= (24/116) + + + + PCS in Lab2 is encoded as: + + 8 bit Lab PCS: + + L* 0..100 into a 0..ff byte. + a* t + 128 range is -128.0 +127.0 + b* + + 16 bit Lab PCS: + + L* 0..100 into a 0..ff00 word. + a* t + 128 range is -128.0 +127.9961 + b* + + + +Interchange Space Component Actual Range Encoded Range +CIE XYZ X 0 -> 1.99997 0x0000 -> 0xffff +CIE XYZ Y 0 -> 1.99997 0x0000 -> 0xffff +CIE XYZ Z 0 -> 1.99997 0x0000 -> 0xffff + +Version 2,3 +----------- + +CIELAB (16 bit) L* 0 -> 100.0 0x0000 -> 0xff00 +CIELAB (16 bit) a* -128.0 -> +127.996 0x0000 -> 0x8000 -> 0xffff +CIELAB (16 bit) b* -128.0 -> +127.996 0x0000 -> 0x8000 -> 0xffff + + +Version 4 +--------- + +CIELAB (16 bit) L* 0 -> 100.0 0x0000 -> 0xffff +CIELAB (16 bit) a* -128.0 -> +127 0x0000 -> 0x8080 -> 0xffff +CIELAB (16 bit) b* -128.0 -> +127 0x0000 -> 0x8080 -> 0xffff + +*/ + +// Conversions +void CMSEXPORT cmsXYZ2xyY(cmsCIExyY* Dest, const cmsCIEXYZ* Source) +{ + cmsFloat64Number ISum; + + ISum = 1./(Source -> X + Source -> Y + Source -> Z); + + Dest -> x = (Source -> X) * ISum; + Dest -> y = (Source -> Y) * ISum; + Dest -> Y = Source -> Y; +} + +void CMSEXPORT cmsxyY2XYZ(cmsCIEXYZ* Dest, const cmsCIExyY* Source) +{ + Dest -> X = (Source -> x / Source -> y) * Source -> Y; + Dest -> Y = Source -> Y; + Dest -> Z = ((1 - Source -> x - Source -> y) / Source -> y) * Source -> Y; +} + +static +cmsFloat64Number f(cmsFloat64Number t) +{ + const cmsFloat64Number Limit = (24.0/116.0) * (24.0/116.0) * (24.0/116.0); + + if (t <= Limit) + return (841.0/108.0) * t + (16.0/116.0); + else + return pow(t, 1.0/3.0); +} + +static +cmsFloat64Number f_1(cmsFloat64Number t) +{ + const cmsFloat64Number Limit = (24.0/116.0); + + if (t <= Limit) { + return (108.0/841.0) * (t - (16.0/116.0)); + } + + return t * t * t; +} + + +// Standard XYZ to Lab. it can handle negative XZY numbers in some cases +void CMSEXPORT cmsXYZ2Lab(const cmsCIEXYZ* WhitePoint, cmsCIELab* Lab, const cmsCIEXYZ* xyz) +{ + cmsFloat64Number fx, fy, fz; + + if (WhitePoint == NULL) + WhitePoint = cmsD50_XYZ(); + + fx = f(xyz->X / WhitePoint->X); + fy = f(xyz->Y / WhitePoint->Y); + fz = f(xyz->Z / WhitePoint->Z); + + Lab->L = 116.0*fy - 16.0; + Lab->a = 500.0*(fx - fy); + Lab->b = 200.0*(fy - fz); +} + + +// Standard XYZ to Lab. It can return negative XYZ in some cases +void CMSEXPORT cmsLab2XYZ(const cmsCIEXYZ* WhitePoint, cmsCIEXYZ* xyz, const cmsCIELab* Lab) +{ + cmsFloat64Number x, y, z; + + if (WhitePoint == NULL) + WhitePoint = cmsD50_XYZ(); + + y = (Lab-> L + 16.0) / 116.0; + x = y + 0.002 * Lab -> a; + z = y - 0.005 * Lab -> b; + + xyz -> X = f_1(x) * WhitePoint -> X; + xyz -> Y = f_1(y) * WhitePoint -> Y; + xyz -> Z = f_1(z) * WhitePoint -> Z; + +} + +static +cmsFloat64Number L2float2(cmsUInt16Number v) +{ + return (cmsFloat64Number) v / 652.800; +} + +// the a/b part +static +cmsFloat64Number ab2float2(cmsUInt16Number v) +{ + return ((cmsFloat64Number) v / 256.0) - 128.0; +} + +static +cmsUInt16Number L2Fix2(cmsFloat64Number L) +{ + return _cmsQuickSaturateWord(L * 652.8); +} + +static +cmsUInt16Number ab2Fix2(cmsFloat64Number ab) +{ + return _cmsQuickSaturateWord((ab + 128.0) * 256.0); +} + + +static +cmsFloat64Number L2float4(cmsUInt16Number v) +{ + return (cmsFloat64Number) v / 655.35; +} + +// the a/b part +static +cmsFloat64Number ab2float4(cmsUInt16Number v) +{ + return ((cmsFloat64Number) v / 257.0) - 128.0; +} + + +void CMSEXPORT cmsLabEncoded2FloatV2(cmsCIELab* Lab, const cmsUInt16Number wLab[3]) +{ + Lab->L = L2float2(wLab[0]); + Lab->a = ab2float2(wLab[1]); + Lab->b = ab2float2(wLab[2]); +} + + +void CMSEXPORT cmsLabEncoded2Float(cmsCIELab* Lab, const cmsUInt16Number wLab[3]) +{ + Lab->L = L2float4(wLab[0]); + Lab->a = ab2float4(wLab[1]); + Lab->b = ab2float4(wLab[2]); +} + +static +cmsFloat64Number Clamp_L_doubleV2(cmsFloat64Number L) +{ + const cmsFloat64Number L_max = (cmsFloat64Number) (0xFFFF * 100.0) / 0xFF00; + + if (L < 0) L = 0; + if (L > L_max) L = L_max; + + return L; +} + + +static +cmsFloat64Number Clamp_ab_doubleV2(cmsFloat64Number ab) +{ + if (ab < MIN_ENCODEABLE_ab2) ab = MIN_ENCODEABLE_ab2; + if (ab > MAX_ENCODEABLE_ab2) ab = MAX_ENCODEABLE_ab2; + + return ab; +} + +void CMSEXPORT cmsFloat2LabEncodedV2(cmsUInt16Number wLab[3], const cmsCIELab* fLab) +{ + cmsCIELab Lab; + + Lab.L = Clamp_L_doubleV2(fLab ->L); + Lab.a = Clamp_ab_doubleV2(fLab ->a); + Lab.b = Clamp_ab_doubleV2(fLab ->b); + + wLab[0] = L2Fix2(Lab.L); + wLab[1] = ab2Fix2(Lab.a); + wLab[2] = ab2Fix2(Lab.b); +} + + +static +cmsFloat64Number Clamp_L_doubleV4(cmsFloat64Number L) +{ + if (L < 0) L = 0; + if (L > 100.0) L = 100.0; + + return L; +} + +static +cmsFloat64Number Clamp_ab_doubleV4(cmsFloat64Number ab) +{ + if (ab < MIN_ENCODEABLE_ab4) ab = MIN_ENCODEABLE_ab4; + if (ab > MAX_ENCODEABLE_ab4) ab = MAX_ENCODEABLE_ab4; + + return ab; +} + +static +cmsUInt16Number L2Fix4(cmsFloat64Number L) +{ + return _cmsQuickSaturateWord(L * 655.35); +} + +static +cmsUInt16Number ab2Fix4(cmsFloat64Number ab) +{ + return _cmsQuickSaturateWord((ab + 128.0) * 257.0); +} + +void CMSEXPORT cmsFloat2LabEncoded(cmsUInt16Number wLab[3], const cmsCIELab* fLab) +{ + cmsCIELab Lab; + + Lab.L = Clamp_L_doubleV4(fLab ->L); + Lab.a = Clamp_ab_doubleV4(fLab ->a); + Lab.b = Clamp_ab_doubleV4(fLab ->b); + + wLab[0] = L2Fix4(Lab.L); + wLab[1] = ab2Fix4(Lab.a); + wLab[2] = ab2Fix4(Lab.b); +} + +// Auxiliar: convert to Radians +static +cmsFloat64Number RADIANS(cmsFloat64Number deg) +{ + return (deg * M_PI) / 180.; +} + + +// Auxiliar: atan2 but operating in degrees and returning 0 if a==b==0 +static +cmsFloat64Number atan2deg(cmsFloat64Number a, cmsFloat64Number b) +{ + cmsFloat64Number h; + + if (a == 0 && b == 0) + h = 0; + else + h = atan2(a, b); + + h *= (180. / M_PI); + + while (h > 360.) + h -= 360.; + + while ( h < 0) + h += 360.; + + return h; +} + + +// Auxiliar: Square +static +cmsFloat64Number Sqr(cmsFloat64Number v) +{ + return v * v; +} +// From cylindrical coordinates. No check is performed, then negative values are allowed +void CMSEXPORT cmsLab2LCh(cmsCIELCh* LCh, const cmsCIELab* Lab) +{ + LCh -> L = Lab -> L; + LCh -> C = pow(Sqr(Lab ->a) + Sqr(Lab ->b), 0.5); + LCh -> h = atan2deg(Lab ->b, Lab ->a); +} + + +// To cylindrical coordinates. No check is performed, then negative values are allowed +void CMSEXPORT cmsLCh2Lab(cmsCIELab* Lab, const cmsCIELCh* LCh) +{ + cmsFloat64Number h = (LCh -> h * M_PI) / 180.0; + + Lab -> L = LCh -> L; + Lab -> a = LCh -> C * cos(h); + Lab -> b = LCh -> C * sin(h); +} + +// In XYZ All 3 components are encoded using 1.15 fixed point +static +cmsUInt16Number XYZ2Fix(cmsFloat64Number d) +{ + return _cmsQuickSaturateWord(d * 32768.0); +} + +void CMSEXPORT cmsFloat2XYZEncoded(cmsUInt16Number XYZ[3], const cmsCIEXYZ* fXYZ) +{ + cmsCIEXYZ xyz; + + xyz.X = fXYZ -> X; + xyz.Y = fXYZ -> Y; + xyz.Z = fXYZ -> Z; + + // Clamp to encodeable values. + if (xyz.Y <= 0) { + + xyz.X = 0; + xyz.Y = 0; + xyz.Z = 0; + } + + if (xyz.X > MAX_ENCODEABLE_XYZ) + xyz.X = MAX_ENCODEABLE_XYZ; + + if (xyz.X < 0) + xyz.X = 0; + + if (xyz.Y > MAX_ENCODEABLE_XYZ) + xyz.Y = MAX_ENCODEABLE_XYZ; + + if (xyz.Y < 0) + xyz.Y = 0; + + if (xyz.Z > MAX_ENCODEABLE_XYZ) + xyz.Z = MAX_ENCODEABLE_XYZ; + + if (xyz.Z < 0) + xyz.Z = 0; + + + XYZ[0] = XYZ2Fix(xyz.X); + XYZ[1] = XYZ2Fix(xyz.Y); + XYZ[2] = XYZ2Fix(xyz.Z); +} + + +// To convert from Fixed 1.15 point to cmsFloat64Number +static +cmsFloat64Number XYZ2float(cmsUInt16Number v) +{ + cmsS15Fixed16Number fix32; + + // From 1.15 to 15.16 + fix32 = v << 1; + + // From fixed 15.16 to cmsFloat64Number + return _cms15Fixed16toDouble(fix32); +} + + +void CMSEXPORT cmsXYZEncoded2Float(cmsCIEXYZ* fXYZ, const cmsUInt16Number XYZ[3]) +{ + fXYZ -> X = XYZ2float(XYZ[0]); + fXYZ -> Y = XYZ2float(XYZ[1]); + fXYZ -> Z = XYZ2float(XYZ[2]); +} + + +// Returns dE on two Lab values +cmsFloat64Number CMSEXPORT cmsDeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2) +{ + cmsFloat64Number dL, da, db; + + dL = fabs(Lab1 -> L - Lab2 -> L); + da = fabs(Lab1 -> a - Lab2 -> a); + db = fabs(Lab1 -> b - Lab2 -> b); + + return pow(Sqr(dL) + Sqr(da) + Sqr(db), 0.5); +} + + +// Return the CIE94 Delta E +cmsFloat64Number CMSEXPORT cmsCIE94DeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2) +{ + cmsCIELCh LCh1, LCh2; + cmsFloat64Number dE, dL, dC, dh, dhsq; + cmsFloat64Number c12, sc, sh; + + dL = fabs(Lab1 ->L - Lab2 ->L); + + cmsLab2LCh(&LCh1, Lab1); + cmsLab2LCh(&LCh2, Lab2); + + dC = fabs(LCh1.C - LCh2.C); + dE = cmsDeltaE(Lab1, Lab2); + + dhsq = Sqr(dE) - Sqr(dL) - Sqr(dC); + if (dhsq < 0) + dh = 0; + else + dh = pow(dhsq, 0.5); + + c12 = sqrt(LCh1.C * LCh2.C); + + sc = 1.0 + (0.048 * c12); + sh = 1.0 + (0.014 * c12); + + return sqrt(Sqr(dL) + Sqr(dC) / Sqr(sc) + Sqr(dh) / Sqr(sh)); +} + + +// Auxiliary +static +cmsFloat64Number ComputeLBFD(const cmsCIELab* Lab) +{ + cmsFloat64Number yt; + + if (Lab->L > 7.996969) + yt = (Sqr((Lab->L+16)/116)*((Lab->L+16)/116))*100; + else + yt = 100 * (Lab->L / 903.3); + + return (54.6 * (M_LOG10E * (log(yt + 1.5))) - 9.6); +} + + + +// bfd - gets BFD(1:1) difference between Lab1, Lab2 +cmsFloat64Number CMSEXPORT cmsBFDdeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2) +{ + cmsFloat64Number lbfd1,lbfd2,AveC,Aveh,dE,deltaL, + deltaC,deltah,dc,t,g,dh,rh,rc,rt,bfd; + cmsCIELCh LCh1, LCh2; + + + lbfd1 = ComputeLBFD(Lab1); + lbfd2 = ComputeLBFD(Lab2); + deltaL = lbfd2 - lbfd1; + + cmsLab2LCh(&LCh1, Lab1); + cmsLab2LCh(&LCh2, Lab2); + + deltaC = LCh2.C - LCh1.C; + AveC = (LCh1.C+LCh2.C)/2; + Aveh = (LCh1.h+LCh2.h)/2; + + dE = cmsDeltaE(Lab1, Lab2); + + if (Sqr(dE)>(Sqr(Lab2->L-Lab1->L)+Sqr(deltaC))) + deltah = sqrt(Sqr(dE)-Sqr(Lab2->L-Lab1->L)-Sqr(deltaC)); + else + deltah =0; + + + dc = 0.035 * AveC / (1 + 0.00365 * AveC)+0.521; + g = sqrt(Sqr(Sqr(AveC))/(Sqr(Sqr(AveC))+14000)); + t = 0.627+(0.055*cos((Aveh-254)/(180/M_PI))- + 0.040*cos((2*Aveh-136)/(180/M_PI))+ + 0.070*cos((3*Aveh-31)/(180/M_PI))+ + 0.049*cos((4*Aveh+114)/(180/M_PI))- + 0.015*cos((5*Aveh-103)/(180/M_PI))); + + dh = dc*(g*t+1-g); + rh = -0.260*cos((Aveh-308)/(180/M_PI))- + 0.379*cos((2*Aveh-160)/(180/M_PI))- + 0.636*cos((3*Aveh+254)/(180/M_PI))+ + 0.226*cos((4*Aveh+140)/(180/M_PI))- + 0.194*cos((5*Aveh+280)/(180/M_PI)); + + rc = sqrt((AveC*AveC*AveC*AveC*AveC*AveC)/((AveC*AveC*AveC*AveC*AveC*AveC)+70000000)); + rt = rh*rc; + + bfd = sqrt(Sqr(deltaL)+Sqr(deltaC/dc)+Sqr(deltah/dh)+(rt*(deltaC/dc)*(deltah/dh))); + + return bfd; +} + + +// cmc - CMC(l:c) difference between Lab1, Lab2 +cmsFloat64Number CMSEXPORT cmsCMCdeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2, cmsFloat64Number l, cmsFloat64Number c) +{ + cmsFloat64Number dE,dL,dC,dh,sl,sc,sh,t,f,cmc; + cmsCIELCh LCh1, LCh2; + + if (Lab1 ->L == 0 && Lab2 ->L == 0) return 0; + + cmsLab2LCh(&LCh1, Lab1); + cmsLab2LCh(&LCh2, Lab2); + + + dL = Lab2->L-Lab1->L; + dC = LCh2.C-LCh1.C; + + dE = cmsDeltaE(Lab1, Lab2); + + if (Sqr(dE)>(Sqr(dL)+Sqr(dC))) + dh = sqrt(Sqr(dE)-Sqr(dL)-Sqr(dC)); + else + dh =0; + + if ((LCh1.h > 164) && (LCh1.h < 345)) + t = 0.56 + fabs(0.2 * cos(((LCh1.h + 168)/(180/M_PI)))); + else + t = 0.36 + fabs(0.4 * cos(((LCh1.h + 35 )/(180/M_PI)))); + + sc = 0.0638 * LCh1.C / (1 + 0.0131 * LCh1.C) + 0.638; + sl = 0.040975 * Lab1->L /(1 + 0.01765 * Lab1->L); + + if (Lab1->L<16) + sl = 0.511; + + f = sqrt((LCh1.C * LCh1.C * LCh1.C * LCh1.C)/((LCh1.C * LCh1.C * LCh1.C * LCh1.C)+1900)); + sh = sc*(t*f+1-f); + cmc = sqrt(Sqr(dL/(l*sl))+Sqr(dC/(c*sc))+Sqr(dh/sh)); + + return cmc; +} + +// dE2000 The weightings KL, KC and KH can be modified to reflect the relative +// importance of lightness, chroma and hue in different industrial applications +cmsFloat64Number CMSEXPORT cmsCIE2000DeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2, + cmsFloat64Number Kl, cmsFloat64Number Kc, cmsFloat64Number Kh) +{ + cmsFloat64Number L1 = Lab1->L; + cmsFloat64Number a1 = Lab1->a; + cmsFloat64Number b1 = Lab1->b; + cmsFloat64Number C = sqrt( Sqr(a1) + Sqr(b1) ); + + cmsFloat64Number Ls = Lab2 ->L; + cmsFloat64Number as = Lab2 ->a; + cmsFloat64Number bs = Lab2 ->b; + cmsFloat64Number Cs = sqrt( Sqr(as) + Sqr(bs) ); + + cmsFloat64Number G = 0.5 * ( 1 - sqrt(pow((C + Cs) / 2 , 7.0) / (pow((C + Cs) / 2, 7.0) + pow(25.0, 7.0) ) )); + + cmsFloat64Number a_p = (1 + G ) * a1; + cmsFloat64Number b_p = b1; + cmsFloat64Number C_p = sqrt( Sqr(a_p) + Sqr(b_p)); + cmsFloat64Number h_p = atan2deg(b_p, a_p); + + + cmsFloat64Number a_ps = (1 + G) * as; + cmsFloat64Number b_ps = bs; + cmsFloat64Number C_ps = sqrt(Sqr(a_ps) + Sqr(b_ps)); + cmsFloat64Number h_ps = atan2deg(b_ps, a_ps); + + cmsFloat64Number meanC_p =(C_p + C_ps) / 2; + + cmsFloat64Number hps_plus_hp = h_ps + h_p; + cmsFloat64Number hps_minus_hp = h_ps - h_p; + + cmsFloat64Number meanh_p = fabs(hps_minus_hp) <= 180.000001 ? (hps_plus_hp)/2 : + (hps_plus_hp) < 360 ? (hps_plus_hp + 360)/2 : + (hps_plus_hp - 360)/2; + + cmsFloat64Number delta_h = (hps_minus_hp) <= -180.000001 ? (hps_minus_hp + 360) : + (hps_minus_hp) > 180 ? (hps_minus_hp - 360) : + (hps_minus_hp); + cmsFloat64Number delta_L = (Ls - L1); + cmsFloat64Number delta_C = (C_ps - C_p ); + + + cmsFloat64Number delta_H =2 * sqrt(C_ps*C_p) * sin(RADIANS(delta_h) / 2); + + cmsFloat64Number T = 1 - 0.17 * cos(RADIANS(meanh_p-30)) + + 0.24 * cos(RADIANS(2*meanh_p)) + + 0.32 * cos(RADIANS(3*meanh_p + 6)) + - 0.2 * cos(RADIANS(4*meanh_p - 63)); + + cmsFloat64Number Sl = 1 + (0.015 * Sqr((Ls + L1) /2- 50) )/ sqrt(20 + Sqr( (Ls+L1)/2 - 50) ); + + cmsFloat64Number Sc = 1 + 0.045 * (C_p + C_ps)/2; + cmsFloat64Number Sh = 1 + 0.015 * ((C_ps + C_p)/2) * T; + + cmsFloat64Number delta_ro = 30 * exp( -Sqr(((meanh_p - 275 ) / 25))); + + cmsFloat64Number Rc = 2 * sqrt(( pow(meanC_p, 7.0) )/( pow(meanC_p, 7.0) + pow(25.0, 7.0))); + + cmsFloat64Number Rt = -sin(2 * RADIANS(delta_ro)) * Rc; + + cmsFloat64Number deltaE00 = sqrt( Sqr(delta_L /(Sl * Kl)) + + Sqr(delta_C/(Sc * Kc)) + + Sqr(delta_H/(Sh * Kh)) + + Rt*(delta_C/(Sc * Kc)) * (delta_H / (Sh * Kh))); + + return deltaE00; +} + +// This function returns a number of gridpoints to be used as LUT table. It assumes same number +// of gripdpoints in all dimensions. Flags may override the choice. +int _cmsReasonableGridpointsByColorspace(cmsColorSpaceSignature Colorspace, cmsUInt32Number dwFlags) +{ + int nChannels; + + // Already specified? + if (dwFlags & 0x00FF0000) { + // Yes, grab'em + return (dwFlags >> 16) & 0xFF; + } + + nChannels = cmsChannelsOf(Colorspace); + + // HighResPrecalc is maximum resolution + if (dwFlags & cmsFLAGS_HIGHRESPRECALC) { + + if (nChannels > 4) + return 7; // 7 for Hifi + + if (nChannels == 4) // 23 for CMYK + return 23; + + return 49; // 49 for RGB and others + } + + + // LowResPrecal is lower resolution + if (dwFlags & cmsFLAGS_LOWRESPRECALC) { + + if (nChannels > 4) + return 6; // 6 for more than 4 channels + + if (nChannels == 1) + return 33; // For monochrome + + return 17; // 17 for remaining + } + + // Default values + if (nChannels > 4) + return 7; // 7 for Hifi + + if (nChannels == 4) + return 17; // 17 for CMYK + + return 33; // 33 for RGB +} + + +cmsBool _cmsEndPointsBySpace(cmsColorSpaceSignature Space, + cmsUInt16Number **White, + cmsUInt16Number **Black, + cmsUInt32Number *nOutputs) +{ + // Only most common spaces + + static cmsUInt16Number RGBblack[4] = { 0, 0, 0 }; + static cmsUInt16Number RGBwhite[4] = { 0xffff, 0xffff, 0xffff }; + static cmsUInt16Number CMYKblack[4] = { 0xffff, 0xffff, 0xffff, 0xffff }; // 400% of ink + static cmsUInt16Number CMYKwhite[4] = { 0, 0, 0, 0 }; + static cmsUInt16Number LABblack[4] = { 0, 0x8080, 0x8080 }; // V4 Lab encoding + static cmsUInt16Number LABwhite[4] = { 0xFFFF, 0x8080, 0x8080 }; + static cmsUInt16Number CMYblack[4] = { 0xffff, 0xffff, 0xffff }; + static cmsUInt16Number CMYwhite[4] = { 0, 0, 0 }; + static cmsUInt16Number Grayblack[4] = { 0 }; + static cmsUInt16Number GrayWhite[4] = { 0xffff }; + + switch (Space) { + + case cmsSigGrayData: if (White) *White = GrayWhite; + if (Black) *Black = Grayblack; + if (nOutputs) *nOutputs = 1; + return TRUE; + + case cmsSigRgbData: if (White) *White = RGBwhite; + if (Black) *Black = RGBblack; + if (nOutputs) *nOutputs = 3; + return TRUE; + + case cmsSigLabData: if (White) *White = LABwhite; + if (Black) *Black = LABblack; + if (nOutputs) *nOutputs = 3; + return TRUE; + + case cmsSigCmykData: if (White) *White = CMYKwhite; + if (Black) *Black = CMYKblack; + if (nOutputs) *nOutputs = 4; + return TRUE; + + case cmsSigCmyData: if (White) *White = CMYwhite; + if (Black) *Black = CMYblack; + if (nOutputs) *nOutputs = 3; + return TRUE; + + default:; + } + + return FALSE; +} + + + +// Several utilities ------------------------------------------------------- + +// Translate from our colorspace to ICC representation + +cmsColorSpaceSignature CMSEXPORT _cmsICCcolorSpace(int OurNotation) +{ + switch (OurNotation) { + + case 1: + case PT_GRAY: return cmsSigGrayData; + + case 2: + case PT_RGB: return cmsSigRgbData; + + case PT_CMY: return cmsSigCmyData; + case PT_CMYK: return cmsSigCmykData; + case PT_YCbCr:return cmsSigYCbCrData; + case PT_YUV: return cmsSigLuvData; + case PT_XYZ: return cmsSigXYZData; + + case PT_LabV2: + case PT_Lab: return cmsSigLabData; + + case PT_YUVK: return cmsSigLuvKData; + case PT_HSV: return cmsSigHsvData; + case PT_HLS: return cmsSigHlsData; + case PT_Yxy: return cmsSigYxyData; + + case PT_MCH1: return cmsSigMCH1Data; + case PT_MCH2: return cmsSigMCH2Data; + case PT_MCH3: return cmsSigMCH3Data; + case PT_MCH4: return cmsSigMCH4Data; + case PT_MCH5: return cmsSigMCH5Data; + case PT_MCH6: return cmsSigMCH6Data; + case PT_MCH7: return cmsSigMCH7Data; + case PT_MCH8: return cmsSigMCH8Data; + + case PT_MCH9: return cmsSigMCH9Data; + case PT_MCH10: return cmsSigMCHAData; + case PT_MCH11: return cmsSigMCHBData; + case PT_MCH12: return cmsSigMCHCData; + case PT_MCH13: return cmsSigMCHDData; + case PT_MCH14: return cmsSigMCHEData; + case PT_MCH15: return cmsSigMCHFData; + + default: return (cmsColorSpaceSignature) (-1); + } +} + + +int CMSEXPORT _cmsLCMScolorSpace(cmsColorSpaceSignature ProfileSpace) +{ + switch (ProfileSpace) { + + case cmsSigGrayData: return PT_GRAY; + case cmsSigRgbData: return PT_RGB; + case cmsSigCmyData: return PT_CMY; + case cmsSigCmykData: return PT_CMYK; + case cmsSigYCbCrData:return PT_YCbCr; + case cmsSigLuvData: return PT_YUV; + case cmsSigXYZData: return PT_XYZ; + case cmsSigLabData: return PT_Lab; + case cmsSigLuvKData: return PT_YUVK; + case cmsSigHsvData: return PT_HSV; + case cmsSigHlsData: return PT_HLS; + case cmsSigYxyData: return PT_Yxy; + + case cmsSig1colorData: + case cmsSigMCH1Data: return PT_MCH1; + + case cmsSig2colorData: + case cmsSigMCH2Data: return PT_MCH2; + + case cmsSig3colorData: + case cmsSigMCH3Data: return PT_MCH3; + + case cmsSig4colorData: + case cmsSigMCH4Data: return PT_MCH4; + + case cmsSig5colorData: + case cmsSigMCH5Data: return PT_MCH5; + + case cmsSig6colorData: + case cmsSigMCH6Data: return PT_MCH6; + + case cmsSigMCH7Data: + case cmsSig7colorData:return PT_MCH7; + + case cmsSigMCH8Data: + case cmsSig8colorData:return PT_MCH8; + + case cmsSigMCH9Data: + case cmsSig9colorData:return PT_MCH9; + + case cmsSigMCHAData: + case cmsSig10colorData:return PT_MCH10; + + case cmsSigMCHBData: + case cmsSig11colorData:return PT_MCH11; + + case cmsSigMCHCData: + case cmsSig12colorData:return PT_MCH12; + + case cmsSigMCHDData: + case cmsSig13colorData:return PT_MCH13; + + case cmsSigMCHEData: + case cmsSig14colorData:return PT_MCH14; + + case cmsSigMCHFData: + case cmsSig15colorData:return PT_MCH15; + + default: return (cmsColorSpaceSignature) (-1); + } +} + + +cmsUInt32Number CMSEXPORT cmsChannelsOf(cmsColorSpaceSignature ColorSpace) +{ + switch (ColorSpace) { + + case cmsSigMCH1Data: + case cmsSig1colorData: + case cmsSigGrayData: return 1; + + case cmsSigMCH2Data: + case cmsSig2colorData: return 2; + + case cmsSigXYZData: + case cmsSigLabData: + case cmsSigLuvData: + case cmsSigYCbCrData: + case cmsSigYxyData: + case cmsSigRgbData: + case cmsSigHsvData: + case cmsSigHlsData: + case cmsSigCmyData: + case cmsSigMCH3Data: + case cmsSig3colorData: return 3; + + case cmsSigLuvKData: + case cmsSigCmykData: + case cmsSigMCH4Data: + case cmsSig4colorData: return 4; + + case cmsSigMCH5Data: + case cmsSig5colorData: return 5; + + case cmsSigMCH6Data: + case cmsSig6colorData: return 6; + + case cmsSigMCH7Data: + case cmsSig7colorData: return 7; + + case cmsSigMCH8Data: + case cmsSig8colorData: return 8; + + case cmsSigMCH9Data: + case cmsSig9colorData: return 9; + + case cmsSigMCHAData: + case cmsSig10colorData: return 10; + + case cmsSigMCHBData: + case cmsSig11colorData: return 11; + + case cmsSigMCHCData: + case cmsSig12colorData: return 12; + + case cmsSigMCHDData: + case cmsSig13colorData: return 13; + + case cmsSigMCHEData: + case cmsSig14colorData: return 14; + + case cmsSigMCHFData: + case cmsSig15colorData: return 15; + + default: return 3; + } +} diff --git a/third_party/lcms/src/cmsplugin.c b/third_party/lcms/src/cmsplugin.c new file mode 100644 index 0000000000..42c4002b55 --- /dev/null +++ b/third_party/lcms/src/cmsplugin.c @@ -0,0 +1,959 @@ +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2010 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + + +// ---------------------------------------------------------------------------------- +// Encoding & Decoding support functions +// ---------------------------------------------------------------------------------- + +// Little-Endian to Big-Endian + +// Adjust a word value after being readed/ before being written from/to an ICC profile +cmsUInt16Number CMSEXPORT _cmsAdjustEndianess16(cmsUInt16Number Word) +{ +#ifndef CMS_USE_BIG_ENDIAN + + cmsUInt8Number* pByte = (cmsUInt8Number*) &Word; + cmsUInt8Number tmp; + + tmp = pByte[0]; + pByte[0] = pByte[1]; + pByte[1] = tmp; +#endif + + return Word; +} + + +// Transports to properly encoded values - note that icc profiles does use big endian notation. + +// 1 2 3 4 +// 4 3 2 1 + +cmsUInt32Number CMSEXPORT _cmsAdjustEndianess32(cmsUInt32Number DWord) +{ +#ifndef CMS_USE_BIG_ENDIAN + + cmsUInt8Number* pByte = (cmsUInt8Number*) &DWord; + cmsUInt8Number temp1; + cmsUInt8Number temp2; + + temp1 = *pByte++; + temp2 = *pByte++; + *(pByte-1) = *pByte; + *pByte++ = temp2; + *(pByte-3) = *pByte; + *pByte = temp1; +#endif + return DWord; +} + +// 1 2 3 4 5 6 7 8 +// 8 7 6 5 4 3 2 1 + +void CMSEXPORT _cmsAdjustEndianess64(cmsUInt64Number* Result, cmsUInt64Number* QWord) +{ + +#ifndef CMS_USE_BIG_ENDIAN + + cmsUInt8Number* pIn = (cmsUInt8Number*) QWord; + cmsUInt8Number* pOut = (cmsUInt8Number*) Result; + + _cmsAssert(Result != NULL); + + pOut[7] = pIn[0]; + pOut[6] = pIn[1]; + pOut[5] = pIn[2]; + pOut[4] = pIn[3]; + pOut[3] = pIn[4]; + pOut[2] = pIn[5]; + pOut[1] = pIn[6]; + pOut[0] = pIn[7]; + +#else + _cmsAssert(Result != NULL); + +# ifdef CMS_DONT_USE_INT64 + (*Result)[0] = QWord[0]; + (*Result)[1] = QWord[1]; +# else + *Result = *QWord; +# endif +#endif +} + +// Auxiliar -- read 8, 16 and 32-bit numbers +cmsBool CMSEXPORT _cmsReadUInt8Number(cmsIOHANDLER* io, cmsUInt8Number* n) +{ + cmsUInt8Number tmp; + + _cmsAssert(io != NULL); + + if (io -> Read(io, &tmp, sizeof(cmsUInt8Number), 1) != 1) + return FALSE; + + if (n != NULL) *n = tmp; + return TRUE; +} + +cmsBool CMSEXPORT _cmsReadUInt16Number(cmsIOHANDLER* io, cmsUInt16Number* n) +{ + cmsUInt16Number tmp; + + _cmsAssert(io != NULL); + + if (io -> Read(io, &tmp, sizeof(cmsUInt16Number), 1) != 1) + return FALSE; + + if (n != NULL) *n = _cmsAdjustEndianess16(tmp); + return TRUE; +} + +cmsBool CMSEXPORT _cmsReadUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, cmsUInt16Number* Array) +{ + cmsUInt32Number i; + + _cmsAssert(io != NULL); + + for (i=0; i < n; i++) { + + if (Array != NULL) { + if (!_cmsReadUInt16Number(io, Array + i)) return FALSE; + } + else { + if (!_cmsReadUInt16Number(io, NULL)) return FALSE; + } + + } + return TRUE; +} + +cmsBool CMSEXPORT _cmsReadUInt32Number(cmsIOHANDLER* io, cmsUInt32Number* n) +{ + cmsUInt32Number tmp; + + _cmsAssert(io != NULL); + + if (io -> Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1) + return FALSE; + + if (n != NULL) *n = _cmsAdjustEndianess32(tmp); + return TRUE; +} + +cmsBool CMSEXPORT _cmsReadFloat32Number(cmsIOHANDLER* io, cmsFloat32Number* n) +{ + cmsUInt32Number tmp; + + _cmsAssert(io != NULL); + + if (io -> Read(io, &tmp, sizeof(cmsFloat32Number), 1) != 1) + return FALSE; + + if (n != NULL) { + + tmp = _cmsAdjustEndianess32(tmp); + *n = *(cmsFloat32Number*) &tmp; + if (isnan(*n)) + return FALSE; + } + + // fpclassify() required by C99 + return (fpclassify(*n) == FP_ZERO) || (fpclassify(*n) == FP_NORMAL); +} + + +cmsBool CMSEXPORT _cmsReadUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n) +{ + cmsUInt64Number tmp; + + _cmsAssert(io != NULL); + + if (io -> Read(io, &tmp, sizeof(cmsUInt64Number), 1) != 1) + return FALSE; + + if (n != NULL) _cmsAdjustEndianess64(n, &tmp); + return TRUE; +} + + +cmsBool CMSEXPORT _cmsRead15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number* n) +{ + cmsUInt32Number tmp; + + _cmsAssert(io != NULL); + + if (io -> Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1) + return FALSE; + + if (n != NULL) { + *n = _cms15Fixed16toDouble(_cmsAdjustEndianess32(tmp)); + } + + return TRUE; +} + + +// Jun-21-2000: Some profiles (those that comes with W2K) comes +// with the media white (media black?) x 100. Add a sanity check + +static +void NormalizeXYZ(cmsCIEXYZ* Dest) +{ + while (Dest -> X > 2. && + Dest -> Y > 2. && + Dest -> Z > 2.) { + + Dest -> X /= 10.; + Dest -> Y /= 10.; + Dest -> Z /= 10.; + } +} + +cmsBool CMSEXPORT _cmsReadXYZNumber(cmsIOHANDLER* io, cmsCIEXYZ* XYZ) +{ + cmsEncodedXYZNumber xyz; + + _cmsAssert(io != NULL); + + if (io ->Read(io, &xyz, sizeof(cmsEncodedXYZNumber), 1) != 1) return FALSE; + + if (XYZ != NULL) { + + XYZ->X = _cms15Fixed16toDouble(_cmsAdjustEndianess32(xyz.X)); + XYZ->Y = _cms15Fixed16toDouble(_cmsAdjustEndianess32(xyz.Y)); + XYZ->Z = _cms15Fixed16toDouble(_cmsAdjustEndianess32(xyz.Z)); + + NormalizeXYZ(XYZ); + } + return TRUE; +} + +cmsBool CMSEXPORT _cmsWriteUInt8Number(cmsIOHANDLER* io, cmsUInt8Number n) +{ + _cmsAssert(io != NULL); + + if (io -> Write(io, sizeof(cmsUInt8Number), &n) != 1) + return FALSE; + + return TRUE; +} + +cmsBool CMSEXPORT _cmsWriteUInt16Number(cmsIOHANDLER* io, cmsUInt16Number n) +{ + cmsUInt16Number tmp; + + _cmsAssert(io != NULL); + + tmp = _cmsAdjustEndianess16(n); + if (io -> Write(io, sizeof(cmsUInt16Number), &tmp) != 1) + return FALSE; + + return TRUE; +} + +cmsBool CMSEXPORT _cmsWriteUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, const cmsUInt16Number* Array) +{ + cmsUInt32Number i; + + _cmsAssert(io != NULL); + _cmsAssert(Array != NULL); + + for (i=0; i < n; i++) { + if (!_cmsWriteUInt16Number(io, Array[i])) return FALSE; + } + + return TRUE; +} + +cmsBool CMSEXPORT _cmsWriteUInt32Number(cmsIOHANDLER* io, cmsUInt32Number n) +{ + cmsUInt32Number tmp; + + _cmsAssert(io != NULL); + + tmp = _cmsAdjustEndianess32(n); + if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1) + return FALSE; + + return TRUE; +} + + +cmsBool CMSEXPORT _cmsWriteFloat32Number(cmsIOHANDLER* io, cmsFloat32Number n) +{ + cmsUInt32Number tmp; + + _cmsAssert(io != NULL); + + tmp = *(cmsUInt32Number*) &n; + tmp = _cmsAdjustEndianess32(tmp); + if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1) + return FALSE; + + return TRUE; +} + +cmsBool CMSEXPORT _cmsWriteUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n) +{ + cmsUInt64Number tmp; + + _cmsAssert(io != NULL); + + _cmsAdjustEndianess64(&tmp, n); + if (io -> Write(io, sizeof(cmsUInt64Number), &tmp) != 1) + return FALSE; + + return TRUE; +} + +cmsBool CMSEXPORT _cmsWrite15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number n) +{ + cmsUInt32Number tmp; + + _cmsAssert(io != NULL); + + tmp = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(n)); + if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1) + return FALSE; + + return TRUE; +} + +cmsBool CMSEXPORT _cmsWriteXYZNumber(cmsIOHANDLER* io, const cmsCIEXYZ* XYZ) +{ + cmsEncodedXYZNumber xyz; + + _cmsAssert(io != NULL); + _cmsAssert(XYZ != NULL); + + xyz.X = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(XYZ->X)); + xyz.Y = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(XYZ->Y)); + xyz.Z = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(XYZ->Z)); + + return io -> Write(io, sizeof(cmsEncodedXYZNumber), &xyz); +} + +// from Fixed point 8.8 to double +cmsFloat64Number CMSEXPORT _cms8Fixed8toDouble(cmsUInt16Number fixed8) +{ + cmsUInt8Number msb, lsb; + + lsb = (cmsUInt8Number) (fixed8 & 0xff); + msb = (cmsUInt8Number) (((cmsUInt16Number) fixed8 >> 8) & 0xff); + + return (cmsFloat64Number) ((cmsFloat64Number) msb + ((cmsFloat64Number) lsb / 256.0)); +} + +cmsUInt16Number CMSEXPORT _cmsDoubleTo8Fixed8(cmsFloat64Number val) +{ + cmsS15Fixed16Number GammaFixed32 = _cmsDoubleTo15Fixed16(val); + return (cmsUInt16Number) ((GammaFixed32 >> 8) & 0xFFFF); +} + +// from Fixed point 15.16 to double +cmsFloat64Number CMSEXPORT _cms15Fixed16toDouble(cmsS15Fixed16Number fix32) +{ + cmsFloat64Number floater, sign, mid; + int Whole, FracPart; + + sign = (fix32 < 0 ? -1 : 1); + fix32 = abs(fix32); + + Whole = (cmsUInt16Number)(fix32 >> 16) & 0xffff; + FracPart = (cmsUInt16Number)(fix32 & 0xffff); + + mid = (cmsFloat64Number) FracPart / 65536.0; + floater = (cmsFloat64Number) Whole + mid; + + return sign * floater; +} + +// from double to Fixed point 15.16 +cmsS15Fixed16Number CMSEXPORT _cmsDoubleTo15Fixed16(cmsFloat64Number v) +{ + return ((cmsS15Fixed16Number) floor((v)*65536.0 + 0.5)); +} + +// Date/Time functions + +void CMSEXPORT _cmsDecodeDateTimeNumber(const cmsDateTimeNumber *Source, struct tm *Dest) +{ + + _cmsAssert(Dest != NULL); + _cmsAssert(Source != NULL); + + Dest->tm_sec = _cmsAdjustEndianess16(Source->seconds); + Dest->tm_min = _cmsAdjustEndianess16(Source->minutes); + Dest->tm_hour = _cmsAdjustEndianess16(Source->hours); + Dest->tm_mday = _cmsAdjustEndianess16(Source->day); + Dest->tm_mon = _cmsAdjustEndianess16(Source->month) - 1; + Dest->tm_year = _cmsAdjustEndianess16(Source->year) - 1900; + Dest->tm_wday = -1; + Dest->tm_yday = -1; + Dest->tm_isdst = 0; +} + +void CMSEXPORT _cmsEncodeDateTimeNumber(cmsDateTimeNumber *Dest, const struct tm *Source) +{ + _cmsAssert(Dest != NULL); + _cmsAssert(Source != NULL); + + Dest->seconds = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_sec); + Dest->minutes = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_min); + Dest->hours = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_hour); + Dest->day = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_mday); + Dest->month = _cmsAdjustEndianess16((cmsUInt16Number) (Source->tm_mon + 1)); + Dest->year = _cmsAdjustEndianess16((cmsUInt16Number) (Source->tm_year + 1900)); +} + +// Read base and return type base +cmsTagTypeSignature CMSEXPORT _cmsReadTypeBase(cmsIOHANDLER* io) +{ + _cmsTagBase Base; + + _cmsAssert(io != NULL); + + if (io -> Read(io, &Base, sizeof(_cmsTagBase), 1) != 1) + return (cmsTagTypeSignature) 0; + + return (cmsTagTypeSignature) _cmsAdjustEndianess32(Base.sig); +} + +// Setup base marker +cmsBool CMSEXPORT _cmsWriteTypeBase(cmsIOHANDLER* io, cmsTagTypeSignature sig) +{ + _cmsTagBase Base; + + _cmsAssert(io != NULL); + + Base.sig = (cmsTagTypeSignature) _cmsAdjustEndianess32(sig); + memset(&Base.reserved, 0, sizeof(Base.reserved)); + return io -> Write(io, sizeof(_cmsTagBase), &Base); +} + +cmsBool CMSEXPORT _cmsReadAlignment(cmsIOHANDLER* io) +{ + cmsUInt8Number Buffer[4]; + cmsUInt32Number NextAligned, At; + cmsUInt32Number BytesToNextAlignedPos; + + _cmsAssert(io != NULL); + + At = io -> Tell(io); + NextAligned = _cmsALIGNLONG(At); + BytesToNextAlignedPos = NextAligned - At; + if (BytesToNextAlignedPos == 0) return TRUE; + if (BytesToNextAlignedPos > 4) return FALSE; + + return (io ->Read(io, Buffer, BytesToNextAlignedPos, 1) == 1); +} + +cmsBool CMSEXPORT _cmsWriteAlignment(cmsIOHANDLER* io) +{ + cmsUInt8Number Buffer[4]; + cmsUInt32Number NextAligned, At; + cmsUInt32Number BytesToNextAlignedPos; + + _cmsAssert(io != NULL); + + At = io -> Tell(io); + NextAligned = _cmsALIGNLONG(At); + BytesToNextAlignedPos = NextAligned - At; + if (BytesToNextAlignedPos == 0) return TRUE; + if (BytesToNextAlignedPos > 4) return FALSE; + + memset(Buffer, 0, BytesToNextAlignedPos); + return io -> Write(io, BytesToNextAlignedPos, Buffer); +} + + +// To deal with text streams. 2K at most +cmsBool CMSEXPORT _cmsIOPrintf(cmsIOHANDLER* io, const char* frm, ...) +{ + va_list args; + int len; + cmsUInt8Number Buffer[2048]; + cmsBool rc; + + _cmsAssert(io != NULL); + _cmsAssert(frm != NULL); + + va_start(args, frm); + + len = vsnprintf((char*) Buffer, 2047, frm, args); + if (len < 0) return FALSE; // Truncated, which is a fatal error for us + + rc = io ->Write(io, len, Buffer); + + va_end(args); + + return rc; +} + + +// Plugin memory management ------------------------------------------------------------------------------------------------- + +// Specialized malloc for plug-ins, that is freed upon exit. +void* _cmsPluginMalloc(cmsContext ContextID, cmsUInt32Number size) +{ + struct _cmsContext_struct* ctx = _cmsGetContext(ContextID); + + if (ctx ->MemPool == NULL) { + + if (ContextID == NULL) { + + ctx->MemPool = _cmsCreateSubAlloc(0, 2*1024); + } + else { + cmsSignalError(ContextID, cmsERROR_CORRUPTION_DETECTED, "NULL memory pool on context"); + return NULL; + } + } + + return _cmsSubAlloc(ctx->MemPool, size); +} + + +// Main plug-in dispatcher +cmsBool CMSEXPORT cmsPlugin(void* Plug_in) +{ + return cmsPluginTHR(NULL, Plug_in); +} + +cmsBool CMSEXPORT cmsPluginTHR(cmsContext id, void* Plug_in) +{ + cmsPluginBase* Plugin; + + for (Plugin = (cmsPluginBase*) Plug_in; + Plugin != NULL; + Plugin = Plugin -> Next) { + + if (Plugin -> Magic != cmsPluginMagicNumber) { + cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin"); + return FALSE; + } + + if (Plugin ->ExpectedVersion > LCMS_VERSION) { + cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "plugin needs Little CMS %d, current version is %d", + Plugin ->ExpectedVersion, LCMS_VERSION); + return FALSE; + } + + switch (Plugin -> Type) { + + case cmsPluginMemHandlerSig: + if (!_cmsRegisterMemHandlerPlugin(id, Plugin)) return FALSE; + break; + + case cmsPluginInterpolationSig: + if (!_cmsRegisterInterpPlugin(id, Plugin)) return FALSE; + break; + + case cmsPluginTagTypeSig: + if (!_cmsRegisterTagTypePlugin(id, Plugin)) return FALSE; + break; + + case cmsPluginTagSig: + if (!_cmsRegisterTagPlugin(id, Plugin)) return FALSE; + break; + + case cmsPluginFormattersSig: + if (!_cmsRegisterFormattersPlugin(id, Plugin)) return FALSE; + break; + + case cmsPluginRenderingIntentSig: + if (!_cmsRegisterRenderingIntentPlugin(id, Plugin)) return FALSE; + break; + + case cmsPluginParametricCurveSig: + if (!_cmsRegisterParametricCurvesPlugin(id, Plugin)) return FALSE; + break; + + case cmsPluginMultiProcessElementSig: + if (!_cmsRegisterMultiProcessElementPlugin(id, Plugin)) return FALSE; + break; + + case cmsPluginOptimizationSig: + if (!_cmsRegisterOptimizationPlugin(id, Plugin)) return FALSE; + break; + + case cmsPluginTransformSig: + if (!_cmsRegisterTransformPlugin(id, Plugin)) return FALSE; + break; + + case cmsPluginMutexSig: + if (!_cmsRegisterMutexPlugin(id, Plugin)) return FALSE; + break; + + default: + cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin type '%X'", Plugin -> Type); + return FALSE; + } + } + + // Keep a reference to the plug-in + return TRUE; +} + + +// Revert all plug-ins to default +void CMSEXPORT cmsUnregisterPlugins(void) +{ + 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 = { + + NULL, // Not in the linked list + 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 +}; + + +// The context pool (linked list head) +static _cmsMutex _cmsContextPoolHeadMutex = CMS_MUTEX_INITIALIZER; +static struct _cmsContext_struct* _cmsContextPoolHead = NULL; + +// Internal, get associated pointer, with guessing. Never returns NULL. +struct _cmsContext_struct* _cmsGetContext(cmsContext ContextID) +{ + struct _cmsContext_struct* id = (struct _cmsContext_struct*) ContextID; + struct _cmsContext_struct* ctx; + + + // On 0, use global settings + if (id == NULL) + return &globalContext; + + // Search + for (ctx = _cmsContextPoolHead; + ctx != NULL; + ctx = ctx ->Next) { + + // Found it? + if (id == ctx) + return ctx; // New-style context, + } + + return &globalContext; +} + + +// 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 >= MemoryClientMax) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Bad context client"); + return NULL; + } + + ctx = _cmsGetContext(ContextID); + ptr = ctx ->chunks[mc]; + + if (ptr != NULL) + return ptr; + + // 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) +{ + 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)); + + // Maintain the linked list (with proper locking) + _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex); + ctx ->Next = _cmsContextPoolHead; + _cmsContextPoolHead = ctx; + _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex); + + 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; +} + +// 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) +{ + 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)); + + // Maintain the linked list + _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex); + ctx ->Next = _cmsContextPoolHead; + _cmsContextPoolHead = ctx; + _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex); + + 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; +} + + + +static +struct _cmsContext_struct* FindPrev(struct _cmsContext_struct* id) +{ + struct _cmsContext_struct* prev; + + // Search for previous + for (prev = _cmsContextPoolHead; + prev != NULL; + prev = prev ->Next) + { + if (prev ->Next == id) + return prev; + } + + return NULL; // List is empty or only one element! +} + +// 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) +{ + if (ContextID != NULL) { + + struct _cmsContext_struct* ctx = (struct _cmsContext_struct*) ContextID; + struct _cmsContext_struct fakeContext; + struct _cmsContext_struct* prev; + + 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; + + // Maintain list + _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex); + if (_cmsContextPoolHead == ctx) { + + _cmsContextPoolHead = ctx->Next; + } + else { + + // Search for previous + for (prev = _cmsContextPoolHead; + prev != NULL; + prev = prev ->Next) + { + if (prev -> Next == ctx) { + prev -> Next = ctx ->Next; + break; + } + } + } + _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex); + + // free the memory block itself + _cmsFree(&fakeContext, ctx); + } +} + +// 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) +{ + return _cmsContextGetClientChunk(ContextID, UserPtr); +} diff --git a/third_party/lcms/src/cmsps2.c b/third_party/lcms/src/cmsps2.c new file mode 100644 index 0000000000..224b44b542 --- /dev/null +++ b/third_party/lcms/src/cmsps2.c @@ -0,0 +1,1597 @@ +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2011 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" + +// PostScript ColorRenderingDictionary and ColorSpaceArray + + +#define MAXPSCOLS 60 // Columns on tables + +/* + Implementation + -------------- + + PostScript does use XYZ as its internal PCS. But since PostScript + interpolation tables are limited to 8 bits, I use Lab as a way to + improve the accuracy, favoring perceptual results. So, for the creation + of each CRD, CSA the profiles are converted to Lab via a device + link between profile -> Lab or Lab -> profile. The PS code necessary to + convert Lab <-> XYZ is also included. + + + + Color Space Arrays (CSA) + ================================================================================== + + In order to obtain precision, code chooses between three ways to implement + the device -> XYZ transform. These cases identifies monochrome profiles (often + implemented as a set of curves), matrix-shaper and Pipeline-based. + + Monochrome + ----------- + + This is implemented as /CIEBasedA CSA. The prelinearization curve is + placed into /DecodeA section, and matrix equals to D50. Since here is + no interpolation tables, I do the conversion directly to XYZ + + NOTE: CLUT-based monochrome profiles are NOT supported. So, cmsFLAGS_MATRIXINPUT + flag is forced on such profiles. + + [ /CIEBasedA + << + /DecodeA { transfer function } bind + /MatrixA [D50] + /RangeLMN [ 0.0 cmsD50X 0.0 cmsD50Y 0.0 cmsD50Z ] + /WhitePoint [D50] + /BlackPoint [BP] + /RenderingIntent (intent) + >> + ] + + On simpler profiles, the PCS is already XYZ, so no conversion is required. + + + Matrix-shaper based + ------------------- + + This is implemented both with /CIEBasedABC or /CIEBasedDEF on dependig + of profile implementation. Since here there are no interpolation tables, I do + the conversion directly to XYZ + + + + [ /CIEBasedABC + << + /DecodeABC [ {transfer1} {transfer2} {transfer3} ] + /MatrixABC [Matrix] + /RangeLMN [ 0.0 cmsD50X 0.0 cmsD50Y 0.0 cmsD50Z ] + /DecodeLMN [ { / 2} dup dup ] + /WhitePoint [D50] + /BlackPoint [BP] + /RenderingIntent (intent) + >> + ] + + + CLUT based + ---------- + + Lab is used in such cases. + + [ /CIEBasedDEF + << + /DecodeDEF [ ] + /Table [ p p p [<...>]] + /RangeABC [ 0 1 0 1 0 1] + /DecodeABC[ ] + /RangeLMN [ -0.236 1.254 0 1 -0.635 1.640 ] + % -128/500 1+127/500 0 1 -127/200 1+128/200 + /MatrixABC [ 1 1 1 1 0 0 0 0 -1] + /WhitePoint [D50] + /BlackPoint [BP] + /RenderingIntent (intent) + ] + + + Color Rendering Dictionaries (CRD) + ================================== + These are always implemented as CLUT, and always are using Lab. Since CRD are expected to + be used as resources, the code adds the definition as well. + + << + /ColorRenderingType 1 + /WhitePoint [ D50 ] + /BlackPoint [BP] + /MatrixPQR [ Bradford ] + /RangePQR [-0.125 1.375 -0.125 1.375 -0.125 1.375 ] + /TransformPQR [ + {4 index 3 get div 2 index 3 get mul exch pop exch pop exch pop exch pop } bind + {4 index 4 get div 2 index 4 get mul exch pop exch pop exch pop exch pop } bind + {4 index 5 get div 2 index 5 get mul exch pop exch pop exch pop exch pop } bind + ] + /MatrixABC <...> + /EncodeABC <...> + /RangeABC <.. used for XYZ -> Lab> + /EncodeLMN + /RenderTable [ p p p [<...>]] + + /RenderingIntent (Perceptual) + >> + /Current exch /ColorRendering defineresource pop + + + The following stages are used to convert from XYZ to Lab + -------------------------------------------------------- + + Input is given at LMN stage on X, Y, Z + + Encode LMN gives us f(X/Xn), f(Y/Yn), f(Z/Zn) + + /EncodeLMN [ + + { 0.964200 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind + { 1.000000 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind + { 0.824900 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind + + ] + + + MatrixABC is used to compute f(Y/Yn), f(X/Xn) - f(Y/Yn), f(Y/Yn) - f(Z/Zn) + + | 0 1 0| + | 1 -1 0| + | 0 1 -1| + + /MatrixABC [ 0 1 0 1 -1 1 0 0 -1 ] + + EncodeABC finally gives Lab values. + + /EncodeABC [ + { 116 mul 16 sub 100 div } bind + { 500 mul 128 add 255 div } bind + { 200 mul 128 add 255 div } bind + ] + + The following stages are used to convert Lab to XYZ + ---------------------------------------------------- + + /RangeABC [ 0 1 0 1 0 1] + /DecodeABC [ { 100 mul 16 add 116 div } bind + { 255 mul 128 sub 500 div } bind + { 255 mul 128 sub 200 div } bind + ] + + /MatrixABC [ 1 1 1 1 0 0 0 0 -1] + /DecodeLMN [ + {dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.964200 mul} bind + {dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse } bind + {dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.824900 mul} bind + ] + + +*/ + +/* + + PostScript algorithms discussion. + ========================================================================================================= + + 1D interpolation algorithm + + + 1D interpolation (float) + ------------------------ + + val2 = Domain * Value; + + cell0 = (int) floor(val2); + cell1 = (int) ceil(val2); + + rest = val2 - cell0; + + y0 = LutTable[cell0] ; + y1 = LutTable[cell1] ; + + y = y0 + (y1 - y0) * rest; + + + + PostScript code Stack + ================================================ + + { % v + + [array] % v tab + dup % v tab tab + length 1 sub % v tab dom + + 3 -1 roll % tab dom v + + mul % tab val2 + dup % tab val2 val2 + dup % tab val2 val2 val2 + floor cvi % tab val2 val2 cell0 + exch % tab val2 cell0 val2 + ceiling cvi % tab val2 cell0 cell1 + + 3 index % tab val2 cell0 cell1 tab + exch % tab val2 cell0 tab cell1 + get % tab val2 cell0 y1 + + 4 -1 roll % val2 cell0 y1 tab + 3 -1 roll % val2 y1 tab cell0 + get % val2 y1 y0 + + dup % val2 y1 y0 y0 + 3 1 roll % val2 y0 y1 y0 + + sub % val2 y0 (y1-y0) + 3 -1 roll % y0 (y1-y0) val2 + dup % y0 (y1-y0) val2 val2 + floor cvi % y0 (y1-y0) val2 floor(val2) + sub % y0 (y1-y0) rest + mul % y0 t1 + add % y + 65535 div % result + + } bind + + +*/ + + +// This struct holds the memory block currently being write +typedef struct { + _cmsStageCLutData* Pipeline; + cmsIOHANDLER* m; + + int FirstComponent; + int SecondComponent; + + const char* PreMaj; + const char* PostMaj; + const char* PreMin; + const char* PostMin; + + int FixWhite; // Force mapping of pure white + + cmsColorSpaceSignature ColorSpace; // ColorSpace of profile + + +} cmsPsSamplerCargo; + +static int _cmsPSActualColumn = 0; + + +// Convert to byte +static +cmsUInt8Number Word2Byte(cmsUInt16Number w) +{ + return (cmsUInt8Number) floor((cmsFloat64Number) w / 257.0 + 0.5); +} + + +// Convert to byte (using ICC2 notation) +/* +static +cmsUInt8Number L2Byte(cmsUInt16Number w) +{ + int ww = w + 0x0080; + + if (ww > 0xFFFF) return 0xFF; + + return (cmsUInt8Number) ((cmsUInt16Number) (ww >> 8) & 0xFF); +} +*/ + +// Write a cooked byte + +static +void WriteByte(cmsIOHANDLER* m, cmsUInt8Number b) +{ + _cmsIOPrintf(m, "%02x", b); + _cmsPSActualColumn += 2; + + if (_cmsPSActualColumn > MAXPSCOLS) { + + _cmsIOPrintf(m, "\n"); + _cmsPSActualColumn = 0; + } +} + +// ----------------------------------------------------------------- PostScript generation + + +// Removes offending Carriage returns +static +char* RemoveCR(const char* txt) +{ + static char Buffer[2048]; + char* pt; + + strncpy(Buffer, txt, 2047); + Buffer[2047] = 0; + for (pt = Buffer; *pt; pt++) + if (*pt == '\n' || *pt == '\r') *pt = ' '; + + return Buffer; + +} + +static +void EmitHeader(cmsIOHANDLER* m, const char* Title, cmsHPROFILE hProfile) +{ + time_t timer; + cmsMLU *Description, *Copyright; + char DescASCII[256], CopyrightASCII[256]; + + time(&timer); + + Description = (cmsMLU*) cmsReadTag(hProfile, cmsSigProfileDescriptionTag); + Copyright = (cmsMLU*) cmsReadTag(hProfile, cmsSigCopyrightTag); + + DescASCII[0] = DescASCII[255] = 0; + CopyrightASCII[0] = CopyrightASCII[255] = 0; + + if (Description != NULL) cmsMLUgetASCII(Description, cmsNoLanguage, cmsNoCountry, DescASCII, 255); + if (Copyright != NULL) cmsMLUgetASCII(Copyright, cmsNoLanguage, cmsNoCountry, CopyrightASCII, 255); + + _cmsIOPrintf(m, "%%!PS-Adobe-3.0\n"); + _cmsIOPrintf(m, "%%\n"); + _cmsIOPrintf(m, "%% %s\n", Title); + _cmsIOPrintf(m, "%% Source: %s\n", RemoveCR(DescASCII)); + _cmsIOPrintf(m, "%% %s\n", RemoveCR(CopyrightASCII)); + _cmsIOPrintf(m, "%% Created: %s", ctime(&timer)); // ctime appends a \n!!! + _cmsIOPrintf(m, "%%\n"); + _cmsIOPrintf(m, "%%%%BeginResource\n"); + +} + + +// Emits White & Black point. White point is always D50, Black point is the device +// Black point adapted to D50. + +static +void EmitWhiteBlackD50(cmsIOHANDLER* m, cmsCIEXYZ* BlackPoint) +{ + + _cmsIOPrintf(m, "/BlackPoint [%f %f %f]\n", BlackPoint -> X, + BlackPoint -> Y, + BlackPoint -> Z); + + _cmsIOPrintf(m, "/WhitePoint [%f %f %f]\n", cmsD50_XYZ()->X, + cmsD50_XYZ()->Y, + cmsD50_XYZ()->Z); +} + + +static +void EmitRangeCheck(cmsIOHANDLER* m) +{ + _cmsIOPrintf(m, "dup 0.0 lt { pop 0.0 } if " + "dup 1.0 gt { pop 1.0 } if "); + +} + +// Does write the intent + +static +void EmitIntent(cmsIOHANDLER* m, int RenderingIntent) +{ + const char *intent; + + switch (RenderingIntent) { + + case INTENT_PERCEPTUAL: intent = "Perceptual"; break; + case INTENT_RELATIVE_COLORIMETRIC: intent = "RelativeColorimetric"; break; + case INTENT_ABSOLUTE_COLORIMETRIC: intent = "AbsoluteColorimetric"; break; + case INTENT_SATURATION: intent = "Saturation"; break; + + default: intent = "Undefined"; break; + } + + _cmsIOPrintf(m, "/RenderingIntent (%s)\n", intent ); +} + +// +// Convert L* to Y +// +// Y = Yn*[ (L* + 16) / 116] ^ 3 if (L*) >= 6 / 29 +// = Yn*( L* / 116) / 7.787 if (L*) < 6 / 29 +// + +/* +static +void EmitL2Y(cmsIOHANDLER* m) +{ + _cmsIOPrintf(m, + "{ " + "100 mul 16 add 116 div " // (L * 100 + 16) / 116 + "dup 6 29 div ge " // >= 6 / 29 ? + "{ dup dup mul mul } " // yes, ^3 and done + "{ 4 29 div sub 108 841 div mul } " // no, slope limiting + "ifelse } bind "); +} +*/ + + +// Lab -> XYZ, see the discussion above + +static +void EmitLab2XYZ(cmsIOHANDLER* m) +{ + _cmsIOPrintf(m, "/RangeABC [ 0 1 0 1 0 1]\n"); + _cmsIOPrintf(m, "/DecodeABC [\n"); + _cmsIOPrintf(m, "{100 mul 16 add 116 div } bind\n"); + _cmsIOPrintf(m, "{255 mul 128 sub 500 div } bind\n"); + _cmsIOPrintf(m, "{255 mul 128 sub 200 div } bind\n"); + _cmsIOPrintf(m, "]\n"); + _cmsIOPrintf(m, "/MatrixABC [ 1 1 1 1 0 0 0 0 -1]\n"); + _cmsIOPrintf(m, "/RangeLMN [ -0.236 1.254 0 1 -0.635 1.640 ]\n"); + _cmsIOPrintf(m, "/DecodeLMN [\n"); + _cmsIOPrintf(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.964200 mul} bind\n"); + _cmsIOPrintf(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse } bind\n"); + _cmsIOPrintf(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.824900 mul} bind\n"); + _cmsIOPrintf(m, "]\n"); +} + + + +// Outputs a table of words. It does use 16 bits + +static +void Emit1Gamma(cmsIOHANDLER* m, cmsToneCurve* Table) +{ + cmsUInt32Number i; + cmsFloat64Number gamma; + + if (Table == NULL) return; // Error + + if (Table ->nEntries <= 0) return; // Empty table + + // Suppress whole if identity + if (cmsIsToneCurveLinear(Table)) return; + + // Check if is really an exponential. If so, emit "exp" + gamma = cmsEstimateGamma(Table, 0.001); + if (gamma > 0) { + _cmsIOPrintf(m, "{ %g exp } bind ", gamma); + return; + } + + _cmsIOPrintf(m, "{ "); + + // Bounds check + EmitRangeCheck(m); + + // Emit intepolation code + + // PostScript code Stack + // =============== ======================== + // v + _cmsIOPrintf(m, " ["); + + for (i=0; i < Table->nEntries; i++) { + _cmsIOPrintf(m, "%d ", Table->Table16[i]); + } + + _cmsIOPrintf(m, "] "); // v tab + + _cmsIOPrintf(m, "dup "); // v tab tab + _cmsIOPrintf(m, "length 1 sub "); // v tab dom + _cmsIOPrintf(m, "3 -1 roll "); // tab dom v + _cmsIOPrintf(m, "mul "); // tab val2 + _cmsIOPrintf(m, "dup "); // tab val2 val2 + _cmsIOPrintf(m, "dup "); // tab val2 val2 val2 + _cmsIOPrintf(m, "floor cvi "); // tab val2 val2 cell0 + _cmsIOPrintf(m, "exch "); // tab val2 cell0 val2 + _cmsIOPrintf(m, "ceiling cvi "); // tab val2 cell0 cell1 + _cmsIOPrintf(m, "3 index "); // tab val2 cell0 cell1 tab + _cmsIOPrintf(m, "exch "); // tab val2 cell0 tab cell1 + _cmsIOPrintf(m, "get "); // tab val2 cell0 y1 + _cmsIOPrintf(m, "4 -1 roll "); // val2 cell0 y1 tab + _cmsIOPrintf(m, "3 -1 roll "); // val2 y1 tab cell0 + _cmsIOPrintf(m, "get "); // val2 y1 y0 + _cmsIOPrintf(m, "dup "); // val2 y1 y0 y0 + _cmsIOPrintf(m, "3 1 roll "); // val2 y0 y1 y0 + _cmsIOPrintf(m, "sub "); // val2 y0 (y1-y0) + _cmsIOPrintf(m, "3 -1 roll "); // y0 (y1-y0) val2 + _cmsIOPrintf(m, "dup "); // y0 (y1-y0) val2 val2 + _cmsIOPrintf(m, "floor cvi "); // y0 (y1-y0) val2 floor(val2) + _cmsIOPrintf(m, "sub "); // y0 (y1-y0) rest + _cmsIOPrintf(m, "mul "); // y0 t1 + _cmsIOPrintf(m, "add "); // y + _cmsIOPrintf(m, "65535 div "); // result + + _cmsIOPrintf(m, " } bind "); +} + + +// Compare gamma table + +static +cmsBool GammaTableEquals(cmsUInt16Number* g1, cmsUInt16Number* g2, int nEntries) +{ + return memcmp(g1, g2, nEntries* sizeof(cmsUInt16Number)) == 0; +} + + +// Does write a set of gamma curves + +static +void EmitNGamma(cmsIOHANDLER* m, int n, cmsToneCurve* g[]) +{ + int i; + + for( i=0; i < n; i++ ) + { + if (g[i] == NULL) return; // Error + + if (i > 0 && GammaTableEquals(g[i-1]->Table16, g[i]->Table16, g[i]->nEntries)) { + + _cmsIOPrintf(m, "dup "); + } + else { + Emit1Gamma(m, g[i]); + } + } + +} + + + + + +// Following code dumps a LUT onto memory stream + + +// This is the sampler. Intended to work in SAMPLER_INSPECT mode, +// that is, the callback will be called for each knot with +// +// In[] The grid location coordinates, normalized to 0..ffff +// Out[] The Pipeline values, normalized to 0..ffff +// +// Returning a value other than 0 does terminate the sampling process +// +// Each row contains Pipeline values for all but first component. So, I +// detect row changing by keeping a copy of last value of first +// component. -1 is used to mark begining of whole block. + +static +int OutputValueSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo) +{ + cmsPsSamplerCargo* sc = (cmsPsSamplerCargo*) Cargo; + cmsUInt32Number i; + + + if (sc -> FixWhite) { + + if (In[0] == 0xFFFF) { // Only in L* = 100, ab = [-8..8] + + if ((In[1] >= 0x7800 && In[1] <= 0x8800) && + (In[2] >= 0x7800 && In[2] <= 0x8800)) { + + cmsUInt16Number* Black; + cmsUInt16Number* White; + cmsUInt32Number nOutputs; + + if (!_cmsEndPointsBySpace(sc ->ColorSpace, &White, &Black, &nOutputs)) + return 0; + + for (i=0; i < nOutputs; i++) + Out[i] = White[i]; + } + + + } + } + + + // Hadle the parenthesis on rows + + if (In[0] != sc ->FirstComponent) { + + if (sc ->FirstComponent != -1) { + + _cmsIOPrintf(sc ->m, sc ->PostMin); + sc ->SecondComponent = -1; + _cmsIOPrintf(sc ->m, sc ->PostMaj); + } + + // Begin block + _cmsPSActualColumn = 0; + + _cmsIOPrintf(sc ->m, sc ->PreMaj); + sc ->FirstComponent = In[0]; + } + + + if (In[1] != sc ->SecondComponent) { + + if (sc ->SecondComponent != -1) { + + _cmsIOPrintf(sc ->m, sc ->PostMin); + } + + _cmsIOPrintf(sc ->m, sc ->PreMin); + sc ->SecondComponent = In[1]; + } + + // Dump table. + + for (i=0; i < sc -> Pipeline ->Params->nOutputs; i++) { + + cmsUInt16Number wWordOut = Out[i]; + cmsUInt8Number wByteOut; // Value as byte + + + // We always deal with Lab4 + + wByteOut = Word2Byte(wWordOut); + WriteByte(sc -> m, wByteOut); + } + + return 1; +} + +// Writes a Pipeline on memstream. Could be 8 or 16 bits based + +static +void WriteCLUT(cmsIOHANDLER* m, cmsStage* mpe, const char* PreMaj, + const char* PostMaj, + const char* PreMin, + const char* PostMin, + int FixWhite, + cmsColorSpaceSignature ColorSpace) +{ + cmsUInt32Number i; + cmsPsSamplerCargo sc; + + sc.FirstComponent = -1; + sc.SecondComponent = -1; + sc.Pipeline = (_cmsStageCLutData *) mpe ->Data; + sc.m = m; + sc.PreMaj = PreMaj; + sc.PostMaj= PostMaj; + + sc.PreMin = PreMin; + sc.PostMin = PostMin; + sc.FixWhite = FixWhite; + sc.ColorSpace = ColorSpace; + + _cmsIOPrintf(m, "["); + + for (i=0; i < sc.Pipeline->Params->nInputs; i++) + _cmsIOPrintf(m, " %d ", sc.Pipeline->Params->nSamples[i]); + + _cmsIOPrintf(m, " [\n"); + + cmsStageSampleCLut16bit(mpe, OutputValueSampler, (void*) &sc, SAMPLER_INSPECT); + + _cmsIOPrintf(m, PostMin); + _cmsIOPrintf(m, PostMaj); + _cmsIOPrintf(m, "] "); + +} + + +// Dumps CIEBasedA Color Space Array + +static +int EmitCIEBasedA(cmsIOHANDLER* m, cmsToneCurve* Curve, cmsCIEXYZ* BlackPoint) +{ + + _cmsIOPrintf(m, "[ /CIEBasedA\n"); + _cmsIOPrintf(m, " <<\n"); + + _cmsIOPrintf(m, "/DecodeA "); + + Emit1Gamma(m, Curve); + + _cmsIOPrintf(m, " \n"); + + _cmsIOPrintf(m, "/MatrixA [ 0.9642 1.0000 0.8249 ]\n"); + _cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n"); + + EmitWhiteBlackD50(m, BlackPoint); + EmitIntent(m, INTENT_PERCEPTUAL); + + _cmsIOPrintf(m, ">>\n"); + _cmsIOPrintf(m, "]\n"); + + return 1; +} + + +// Dumps CIEBasedABC Color Space Array + +static +int EmitCIEBasedABC(cmsIOHANDLER* m, cmsFloat64Number* Matrix, cmsToneCurve** CurveSet, cmsCIEXYZ* BlackPoint) +{ + int i; + + _cmsIOPrintf(m, "[ /CIEBasedABC\n"); + _cmsIOPrintf(m, "<<\n"); + _cmsIOPrintf(m, "/DecodeABC [ "); + + EmitNGamma(m, 3, CurveSet); + + _cmsIOPrintf(m, "]\n"); + + _cmsIOPrintf(m, "/MatrixABC [ " ); + + for( i=0; i < 3; i++ ) { + + _cmsIOPrintf(m, "%.6f %.6f %.6f ", Matrix[i + 3*0], + Matrix[i + 3*1], + Matrix[i + 3*2]); + } + + + _cmsIOPrintf(m, "]\n"); + + _cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n"); + + EmitWhiteBlackD50(m, BlackPoint); + EmitIntent(m, INTENT_PERCEPTUAL); + + _cmsIOPrintf(m, ">>\n"); + _cmsIOPrintf(m, "]\n"); + + + return 1; +} + + +static +int EmitCIEBasedDEF(cmsIOHANDLER* m, cmsPipeline* Pipeline, int Intent, cmsCIEXYZ* BlackPoint) +{ + const char* PreMaj; + const char* PostMaj; + const char* PreMin, *PostMin; + cmsStage* mpe; + + mpe = Pipeline ->Elements; + + switch (cmsStageInputChannels(mpe)) { + case 3: + + _cmsIOPrintf(m, "[ /CIEBasedDEF\n"); + PreMaj ="<"; + PostMaj= ">\n"; + PreMin = PostMin = ""; + break; + case 4: + _cmsIOPrintf(m, "[ /CIEBasedDEFG\n"); + PreMaj = "["; + PostMaj = "]\n"; + PreMin = "<"; + PostMin = ">\n"; + break; + default: + return 0; + + } + + _cmsIOPrintf(m, "<<\n"); + + if (cmsStageType(mpe) == cmsSigCurveSetElemType) { + + _cmsIOPrintf(m, "/DecodeDEF [ "); + EmitNGamma(m, cmsStageOutputChannels(mpe), _cmsStageGetPtrToCurveSet(mpe)); + _cmsIOPrintf(m, "]\n"); + + mpe = mpe ->Next; + } + + if (cmsStageType(mpe) == cmsSigCLutElemType) { + + _cmsIOPrintf(m, "/Table "); + WriteCLUT(m, mpe, PreMaj, PostMaj, PreMin, PostMin, FALSE, (cmsColorSpaceSignature) 0); + _cmsIOPrintf(m, "]\n"); + } + + EmitLab2XYZ(m); + EmitWhiteBlackD50(m, BlackPoint); + EmitIntent(m, Intent); + + _cmsIOPrintf(m, " >>\n"); + _cmsIOPrintf(m, "]\n"); + + return 1; +} + +// Generates a curve from a gray profile + +static + cmsToneCurve* ExtractGray2Y(cmsContext ContextID, cmsHPROFILE hProfile, int Intent) +{ + cmsToneCurve* Out = cmsBuildTabulatedToneCurve16(ContextID, 256, NULL); + cmsHPROFILE hXYZ = cmsCreateXYZProfile(); + cmsHTRANSFORM xform = cmsCreateTransformTHR(ContextID, hProfile, TYPE_GRAY_8, hXYZ, TYPE_XYZ_DBL, Intent, cmsFLAGS_NOOPTIMIZE); + int i; + + if (Out != NULL) { + for (i=0; i < 256; i++) { + + cmsUInt8Number Gray = (cmsUInt8Number) i; + cmsCIEXYZ XYZ; + + cmsDoTransform(xform, &Gray, &XYZ, 1); + + Out ->Table16[i] =_cmsQuickSaturateWord(XYZ.Y * 65535.0); + } + } + + cmsDeleteTransform(xform); + cmsCloseProfile(hXYZ); + return Out; +} + + + +// Because PostScript has only 8 bits in /Table, we should use +// a more perceptually uniform space... I do choose Lab. + +static +int WriteInputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, int Intent, cmsUInt32Number dwFlags) +{ + cmsHPROFILE hLab; + cmsHTRANSFORM xform; + cmsUInt32Number nChannels; + cmsUInt32Number InputFormat; + int rc; + cmsHPROFILE Profiles[2]; + cmsCIEXYZ BlackPointAdaptedToD50; + + // Does create a device-link based transform. + // The DeviceLink is next dumped as working CSA. + + InputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE); + nChannels = T_CHANNELS(InputFormat); + + + cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0); + + // Adjust output to Lab4 + hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL); + + Profiles[0] = hProfile; + Profiles[1] = hLab; + + xform = cmsCreateMultiprofileTransform(Profiles, 2, InputFormat, TYPE_Lab_DBL, Intent, 0); + cmsCloseProfile(hLab); + + if (xform == NULL) { + + cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Profile -> Lab"); + return 0; + } + + // Only 1, 3 and 4 channels are allowed + + switch (nChannels) { + + case 1: { + cmsToneCurve* Gray2Y = ExtractGray2Y(m ->ContextID, hProfile, Intent); + EmitCIEBasedA(m, Gray2Y, &BlackPointAdaptedToD50); + cmsFreeToneCurve(Gray2Y); + } + break; + + case 3: + case 4: { + cmsUInt32Number OutFrm = TYPE_Lab_16; + cmsPipeline* DeviceLink; + _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform; + + DeviceLink = cmsPipelineDup(v ->Lut); + if (DeviceLink == NULL) return 0; + + dwFlags |= cmsFLAGS_FORCE_CLUT; + _cmsOptimizePipeline(m->ContextID, &DeviceLink, Intent, &InputFormat, &OutFrm, &dwFlags); + + rc = EmitCIEBasedDEF(m, DeviceLink, Intent, &BlackPointAdaptedToD50); + cmsPipelineFree(DeviceLink); + if (rc == 0) return 0; + } + break; + + default: + + cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Only 3, 4 channels supported for CSA. This profile has %d channels.", nChannels); + return 0; + } + + + cmsDeleteTransform(xform); + + return 1; +} + +static +cmsFloat64Number* GetPtrToMatrix(const cmsStage* mpe) +{ + _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data; + + return Data -> Double; +} + + +// Does create CSA based on matrix-shaper. Allowed types are gray and RGB based + +static +int WriteInputMatrixShaper(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsStage* Matrix, cmsStage* Shaper) +{ + cmsColorSpaceSignature ColorSpace; + int rc; + cmsCIEXYZ BlackPointAdaptedToD50; + + ColorSpace = cmsGetColorSpace(hProfile); + + cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0); + + if (ColorSpace == cmsSigGrayData) { + + cmsToneCurve** ShaperCurve = _cmsStageGetPtrToCurveSet(Shaper); + rc = EmitCIEBasedA(m, ShaperCurve[0], &BlackPointAdaptedToD50); + + } + else + if (ColorSpace == cmsSigRgbData) { + + cmsMAT3 Mat; + int i, j; + + memmove(&Mat, GetPtrToMatrix(Matrix), sizeof(Mat)); + + for (i=0; i < 3; i++) + for (j=0; j < 3; j++) + Mat.v[i].n[j] *= MAX_ENCODEABLE_XYZ; + + rc = EmitCIEBasedABC(m, (cmsFloat64Number *) &Mat, + _cmsStageGetPtrToCurveSet(Shaper), + &BlackPointAdaptedToD50); + } + else { + + cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Profile is not suitable for CSA. Unsupported colorspace."); + return 0; + } + + return rc; +} + + + +// Creates a PostScript color list from a named profile data. +// This is a HP extension, and it works in Lab instead of XYZ + +static +int WriteNamedColorCSA(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, int Intent) +{ + cmsHTRANSFORM xform; + cmsHPROFILE hLab; + int i, nColors; + char ColorName[32]; + cmsNAMEDCOLORLIST* NamedColorList; + + hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL); + xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, hLab, TYPE_Lab_DBL, Intent, 0); + if (xform == NULL) return 0; + + NamedColorList = cmsGetNamedColorList(xform); + if (NamedColorList == NULL) return 0; + + _cmsIOPrintf(m, "<<\n"); + _cmsIOPrintf(m, "(colorlistcomment) (%s)\n", "Named color CSA"); + _cmsIOPrintf(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n"); + _cmsIOPrintf(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n"); + + nColors = cmsNamedColorCount(NamedColorList); + + + for (i=0; i < nColors; i++) { + + cmsUInt16Number In[1]; + cmsCIELab Lab; + + In[0] = (cmsUInt16Number) i; + + if (!cmsNamedColorInfo(NamedColorList, i, ColorName, NULL, NULL, NULL, NULL)) + continue; + + cmsDoTransform(xform, In, &Lab, 1); + _cmsIOPrintf(m, " (%s) [ %.3f %.3f %.3f ]\n", ColorName, Lab.L, Lab.a, Lab.b); + } + + + + _cmsIOPrintf(m, ">>\n"); + + cmsDeleteTransform(xform); + cmsCloseProfile(hLab); + return 1; +} + + +// Does create a Color Space Array on XYZ colorspace for PostScript usage +static +cmsUInt32Number GenerateCSA(cmsContext ContextID, + cmsHPROFILE hProfile, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags, + cmsIOHANDLER* mem) +{ + cmsUInt32Number dwBytesUsed; + cmsPipeline* lut = NULL; + cmsStage* Matrix, *Shaper; + + + // Is a named color profile? + if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) { + + if (!WriteNamedColorCSA(mem, hProfile, Intent)) goto Error; + } + else { + + + // Any profile class are allowed (including devicelink), but + // output (PCS) colorspace must be XYZ or Lab + cmsColorSpaceSignature ColorSpace = cmsGetPCS(hProfile); + + if (ColorSpace != cmsSigXYZData && + ColorSpace != cmsSigLabData) { + + cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Invalid output color space"); + goto Error; + } + + + // Read the lut with all necessary conversion stages + lut = _cmsReadInputLUT(hProfile, Intent); + if (lut == NULL) goto Error; + + + // Tone curves + matrix can be implemented without any LUT + if (cmsPipelineCheckAndRetreiveStages(lut, 2, cmsSigCurveSetElemType, cmsSigMatrixElemType, &Shaper, &Matrix)) { + + if (!WriteInputMatrixShaper(mem, hProfile, Matrix, Shaper)) goto Error; + + } + else { + // We need a LUT for the rest + if (!WriteInputLUT(mem, hProfile, Intent, dwFlags)) goto Error; + } + } + + + // Done, keep memory usage + dwBytesUsed = mem ->UsedSpace; + + // Get rid of LUT + if (lut != NULL) cmsPipelineFree(lut); + + // Finally, return used byte count + return dwBytesUsed; + +Error: + if (lut != NULL) cmsPipelineFree(lut); + return 0; +} + +// ------------------------------------------------------ Color Rendering Dictionary (CRD) + + + +/* + + Black point compensation plus chromatic adaptation: + + Step 1 - Chromatic adaptation + ============================= + + WPout + X = ------- PQR + Wpin + + Step 2 - Black point compensation + ================================= + + (WPout - BPout)*X - WPout*(BPin - BPout) + out = --------------------------------------- + WPout - BPin + + + Algorithm discussion + ==================== + + TransformPQR(WPin, BPin, WPout, BPout, PQR) + + Wpin,etc= { Xws Yws Zws Pws Qws Rws } + + + Algorithm Stack 0...n + =========================================================== + PQR BPout WPout BPin WPin + 4 index 3 get WPin PQR BPout WPout BPin WPin + div (PQR/WPin) BPout WPout BPin WPin + 2 index 3 get WPout (PQR/WPin) BPout WPout BPin WPin + mult WPout*(PQR/WPin) BPout WPout BPin WPin + + 2 index 3 get WPout WPout*(PQR/WPin) BPout WPout BPin WPin + 2 index 3 get BPout WPout WPout*(PQR/WPin) BPout WPout BPin WPin + sub (WPout-BPout) WPout*(PQR/WPin) BPout WPout BPin WPin + mult (WPout-BPout)* WPout*(PQR/WPin) BPout WPout BPin WPin + + 2 index 3 get WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin + 4 index 3 get BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin + 3 index 3 get BPout BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin + + sub (BPin-BPout) WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin + mult (BPin-BPout)*WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin + sub (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin + + 3 index 3 get BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin + 3 index 3 get WPout BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin + exch + sub (WPout-BPin) (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin + div + + exch pop + exch pop + exch pop + exch pop + +*/ + + +static +void EmitPQRStage(cmsIOHANDLER* m, cmsHPROFILE hProfile, int DoBPC, int lIsAbsolute) +{ + + + if (lIsAbsolute) { + + // For absolute colorimetric intent, encode back to relative + // and generate a relative Pipeline + + // Relative encoding is obtained across XYZpcs*(D50/WhitePoint) + + cmsCIEXYZ White; + + _cmsReadMediaWhitePoint(&White, hProfile); + + _cmsIOPrintf(m,"/MatrixPQR [1 0 0 0 1 0 0 0 1 ]\n"); + _cmsIOPrintf(m,"/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n"); + + _cmsIOPrintf(m, "%% Absolute colorimetric -- encode to relative to maximize LUT usage\n" + "/TransformPQR [\n" + "{0.9642 mul %g div exch pop exch pop exch pop exch pop} bind\n" + "{1.0000 mul %g div exch pop exch pop exch pop exch pop} bind\n" + "{0.8249 mul %g div exch pop exch pop exch pop exch pop} bind\n]\n", + White.X, White.Y, White.Z); + return; + } + + + _cmsIOPrintf(m,"%% Bradford Cone Space\n" + "/MatrixPQR [0.8951 -0.7502 0.0389 0.2664 1.7135 -0.0685 -0.1614 0.0367 1.0296 ] \n"); + + _cmsIOPrintf(m, "/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n"); + + + // No BPC + + if (!DoBPC) { + + _cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space\n" + "/TransformPQR [\n" + "{exch pop exch 3 get mul exch pop exch 3 get div} bind\n" + "{exch pop exch 4 get mul exch pop exch 4 get div} bind\n" + "{exch pop exch 5 get mul exch pop exch 5 get div} bind\n]\n"); + } else { + + // BPC + + _cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space plus BPC\n" + "/TransformPQR [\n"); + + _cmsIOPrintf(m, "{4 index 3 get div 2 index 3 get mul " + "2 index 3 get 2 index 3 get sub mul " + "2 index 3 get 4 index 3 get 3 index 3 get sub mul sub " + "3 index 3 get 3 index 3 get exch sub div " + "exch pop exch pop exch pop exch pop } bind\n"); + + _cmsIOPrintf(m, "{4 index 4 get div 2 index 4 get mul " + "2 index 4 get 2 index 4 get sub mul " + "2 index 4 get 4 index 4 get 3 index 4 get sub mul sub " + "3 index 4 get 3 index 4 get exch sub div " + "exch pop exch pop exch pop exch pop } bind\n"); + + _cmsIOPrintf(m, "{4 index 5 get div 2 index 5 get mul " + "2 index 5 get 2 index 5 get sub mul " + "2 index 5 get 4 index 5 get 3 index 5 get sub mul sub " + "3 index 5 get 3 index 5 get exch sub div " + "exch pop exch pop exch pop exch pop } bind\n]\n"); + + } + + +} + + +static +void EmitXYZ2Lab(cmsIOHANDLER* m) +{ + _cmsIOPrintf(m, "/RangeLMN [ -0.635 2.0 0 2 -0.635 2.0 ]\n"); + _cmsIOPrintf(m, "/EncodeLMN [\n"); + _cmsIOPrintf(m, "{ 0.964200 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n"); + _cmsIOPrintf(m, "{ 1.000000 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n"); + _cmsIOPrintf(m, "{ 0.824900 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n"); + _cmsIOPrintf(m, "]\n"); + _cmsIOPrintf(m, "/MatrixABC [ 0 1 0 1 -1 1 0 0 -1 ]\n"); + _cmsIOPrintf(m, "/EncodeABC [\n"); + + + _cmsIOPrintf(m, "{ 116 mul 16 sub 100 div } bind\n"); + _cmsIOPrintf(m, "{ 500 mul 128 add 256 div } bind\n"); + _cmsIOPrintf(m, "{ 200 mul 128 add 256 div } bind\n"); + + + _cmsIOPrintf(m, "]\n"); + + +} + +// Due to impedance mismatch between XYZ and almost all RGB and CMYK spaces +// I choose to dump LUTS in Lab instead of XYZ. There is still a lot of wasted +// space on 3D CLUT, but since space seems not to be a problem here, 33 points +// would give a reasonable accurancy. Note also that CRD tables must operate in +// 8 bits. + +static +int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, int Intent, cmsUInt32Number dwFlags) +{ + cmsHPROFILE hLab; + cmsHTRANSFORM xform; + int i, nChannels; + cmsUInt32Number OutputFormat; + _cmsTRANSFORM* v; + cmsPipeline* DeviceLink; + cmsHPROFILE Profiles[3]; + cmsCIEXYZ BlackPointAdaptedToD50; + cmsBool lDoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION); + cmsBool lFixWhite = !(dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP); + cmsUInt32Number InFrm = TYPE_Lab_16; + int RelativeEncodingIntent; + cmsColorSpaceSignature ColorSpace; + + + hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL); + if (hLab == NULL) return 0; + + OutputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE); + nChannels = T_CHANNELS(OutputFormat); + + ColorSpace = cmsGetColorSpace(hProfile); + + // For absolute colorimetric, the LUT is encoded as relative in order to preserve precision. + + RelativeEncodingIntent = Intent; + if (RelativeEncodingIntent == INTENT_ABSOLUTE_COLORIMETRIC) + RelativeEncodingIntent = INTENT_RELATIVE_COLORIMETRIC; + + + // Use V4 Lab always + Profiles[0] = hLab; + Profiles[1] = hProfile; + + xform = cmsCreateMultiprofileTransformTHR(m ->ContextID, + Profiles, 2, TYPE_Lab_DBL, + OutputFormat, RelativeEncodingIntent, 0); + cmsCloseProfile(hLab); + + if (xform == NULL) { + + cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Lab -> Profile in CRD creation"); + return 0; + } + + // Get a copy of the internal devicelink + v = (_cmsTRANSFORM*) xform; + DeviceLink = cmsPipelineDup(v ->Lut); + if (DeviceLink == NULL) return 0; + + + // We need a CLUT + dwFlags |= cmsFLAGS_FORCE_CLUT; + _cmsOptimizePipeline(m->ContextID, &DeviceLink, RelativeEncodingIntent, &InFrm, &OutputFormat, &dwFlags); + + _cmsIOPrintf(m, "<<\n"); + _cmsIOPrintf(m, "/ColorRenderingType 1\n"); + + + cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0); + + // Emit headers, etc. + EmitWhiteBlackD50(m, &BlackPointAdaptedToD50); + EmitPQRStage(m, hProfile, lDoBPC, Intent == INTENT_ABSOLUTE_COLORIMETRIC); + EmitXYZ2Lab(m); + + + // FIXUP: map Lab (100, 0, 0) to perfect white, because the particular encoding for Lab + // does map a=b=0 not falling into any specific node. Since range a,b goes -128..127, + // zero is slightly moved towards right, so assure next node (in L=100 slice) is mapped to + // zero. This would sacrifice a bit of highlights, but failure to do so would cause + // scum dot. Ouch. + + if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) + lFixWhite = FALSE; + + _cmsIOPrintf(m, "/RenderTable "); + + + WriteCLUT(m, cmsPipelineGetPtrToFirstStage(DeviceLink), "<", ">\n", "", "", lFixWhite, ColorSpace); + + _cmsIOPrintf(m, " %d {} bind ", nChannels); + + for (i=1; i < nChannels; i++) + _cmsIOPrintf(m, "dup "); + + _cmsIOPrintf(m, "]\n"); + + + EmitIntent(m, Intent); + + _cmsIOPrintf(m, ">>\n"); + + if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) { + + _cmsIOPrintf(m, "/Current exch /ColorRendering defineresource pop\n"); + } + + cmsPipelineFree(DeviceLink); + cmsDeleteTransform(xform); + + return 1; +} + + +// Builds a ASCII string containing colorant list in 0..1.0 range +static +void BuildColorantList(char *Colorant, int nColorant, cmsUInt16Number Out[]) +{ + char Buff[32]; + int j; + + Colorant[0] = 0; + if (nColorant > cmsMAXCHANNELS) + nColorant = cmsMAXCHANNELS; + + for (j=0; j < nColorant; j++) { + + sprintf(Buff, "%.3f", Out[j] / 65535.0); + strcat(Colorant, Buff); + if (j < nColorant -1) + strcat(Colorant, " "); + + } +} + + +// Creates a PostScript color list from a named profile data. +// This is a HP extension. + +static +int WriteNamedColorCRD(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, int Intent, cmsUInt32Number dwFlags) +{ + cmsHTRANSFORM xform; + int i, nColors, nColorant; + cmsUInt32Number OutputFormat; + char ColorName[32]; + char Colorant[128]; + cmsNAMEDCOLORLIST* NamedColorList; + + + OutputFormat = cmsFormatterForColorspaceOfProfile(hNamedColor, 2, FALSE); + nColorant = T_CHANNELS(OutputFormat); + + + xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, NULL, OutputFormat, Intent, dwFlags); + if (xform == NULL) return 0; + + + NamedColorList = cmsGetNamedColorList(xform); + if (NamedColorList == NULL) return 0; + + _cmsIOPrintf(m, "<<\n"); + _cmsIOPrintf(m, "(colorlistcomment) (%s) \n", "Named profile"); + _cmsIOPrintf(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n"); + _cmsIOPrintf(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n"); + + nColors = cmsNamedColorCount(NamedColorList); + + for (i=0; i < nColors; i++) { + + cmsUInt16Number In[1]; + cmsUInt16Number Out[cmsMAXCHANNELS]; + + In[0] = (cmsUInt16Number) i; + + if (!cmsNamedColorInfo(NamedColorList, i, ColorName, NULL, NULL, NULL, NULL)) + continue; + + cmsDoTransform(xform, In, Out, 1); + BuildColorantList(Colorant, nColorant, Out); + _cmsIOPrintf(m, " (%s) [ %s ]\n", ColorName, Colorant); + } + + _cmsIOPrintf(m, " >>"); + + if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) { + + _cmsIOPrintf(m, " /Current exch /HPSpotTable defineresource pop\n"); + } + + cmsDeleteTransform(xform); + return 1; +} + + + +// This one does create a Color Rendering Dictionary. +// CRD are always LUT-Based, no matter if profile is +// implemented as matrix-shaper. + +static +cmsUInt32Number GenerateCRD(cmsContext ContextID, + cmsHPROFILE hProfile, + cmsUInt32Number Intent, cmsUInt32Number dwFlags, + cmsIOHANDLER* mem) +{ + cmsUInt32Number dwBytesUsed; + + if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) { + + EmitHeader(mem, "Color Rendering Dictionary (CRD)", hProfile); + } + + + // Is a named color profile? + if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) { + + if (!WriteNamedColorCRD(mem, hProfile, Intent, dwFlags)) { + return 0; + } + } + else { + + // CRD are always implemented as LUT + + if (!WriteOutputLUT(mem, hProfile, Intent, dwFlags)) { + return 0; + } + } + + if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) { + + _cmsIOPrintf(mem, "%%%%EndResource\n"); + _cmsIOPrintf(mem, "\n%% CRD End\n"); + } + + // Done, keep memory usage + dwBytesUsed = mem ->UsedSpace; + + // Finally, return used byte count + return dwBytesUsed; + + cmsUNUSED_PARAMETER(ContextID); +} + + + + +cmsUInt32Number CMSEXPORT cmsGetPostScriptColorResource(cmsContext ContextID, + cmsPSResourceType Type, + cmsHPROFILE hProfile, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags, + cmsIOHANDLER* io) +{ + cmsUInt32Number rc; + + + switch (Type) { + + case cmsPS_RESOURCE_CSA: + rc = GenerateCSA(ContextID, hProfile, Intent, dwFlags, io); + break; + + default: + case cmsPS_RESOURCE_CRD: + rc = GenerateCRD(ContextID, hProfile, Intent, dwFlags, io); + break; + } + + return rc; +} + + + +cmsUInt32Number CMSEXPORT cmsGetPostScriptCRD(cmsContext ContextID, + cmsHPROFILE hProfile, + cmsUInt32Number Intent, cmsUInt32Number dwFlags, + void* Buffer, cmsUInt32Number dwBufferLen) +{ + cmsIOHANDLER* mem; + cmsUInt32Number dwBytesUsed; + + // Set up the serialization engine + if (Buffer == NULL) + mem = cmsOpenIOhandlerFromNULL(ContextID); + else + mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w"); + + if (!mem) return 0; + + dwBytesUsed = cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CRD, hProfile, Intent, dwFlags, mem); + + // Get rid of memory stream + cmsCloseIOhandler(mem); + + return dwBytesUsed; +} + + + +// Does create a Color Space Array on XYZ colorspace for PostScript usage +cmsUInt32Number CMSEXPORT cmsGetPostScriptCSA(cmsContext ContextID, + cmsHPROFILE hProfile, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags, + void* Buffer, + cmsUInt32Number dwBufferLen) +{ + cmsIOHANDLER* mem; + cmsUInt32Number dwBytesUsed; + + if (Buffer == NULL) + mem = cmsOpenIOhandlerFromNULL(ContextID); + else + mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w"); + + if (!mem) return 0; + + dwBytesUsed = cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CSA, hProfile, Intent, dwFlags, mem); + + // Get rid of memory stream + cmsCloseIOhandler(mem); + + return dwBytesUsed; + +} diff --git a/third_party/lcms/src/cmssamp.c b/third_party/lcms/src/cmssamp.c new file mode 100644 index 0000000000..70e469161f --- /dev/null +++ b/third_party/lcms/src/cmssamp.c @@ -0,0 +1,572 @@ +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2010 Marti Maria Saguer +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the Software +// is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +//--------------------------------------------------------------------------------- +// + +#include "lcms2_internal.h" + + +#define cmsmin(a, b) (((a) < (b)) ? (a) : (b)) +#define cmsmax(a, b) (((a) > (b)) ? (a) : (b)) + +// This file contains routines for resampling and LUT optimization, black point detection +// and black preservation. + +// Black point detection ------------------------------------------------------------------------- + + +// PCS -> PCS round trip transform, always uses relative intent on the device -> pcs +static +cmsHTRANSFORM CreateRoundtripXForm(cmsHPROFILE hProfile, cmsUInt32Number nIntent) +{ + cmsContext ContextID = cmsGetProfileContextID(hProfile); + cmsHPROFILE hLab = cmsCreateLab4ProfileTHR(ContextID, NULL); + cmsHTRANSFORM xform; + cmsBool BPC[4] = { FALSE, FALSE, FALSE, FALSE }; + cmsFloat64Number States[4] = { 1.0, 1.0, 1.0, 1.0 }; + cmsHPROFILE hProfiles[4]; + cmsUInt32Number Intents[4]; + + hProfiles[0] = hLab; hProfiles[1] = hProfile; hProfiles[2] = hProfile; hProfiles[3] = hLab; + Intents[0] = INTENT_RELATIVE_COLORIMETRIC; Intents[1] = nIntent; Intents[2] = INTENT_RELATIVE_COLORIMETRIC; Intents[3] = INTENT_RELATIVE_COLORIMETRIC; + + xform = cmsCreateExtendedTransform(ContextID, 4, hProfiles, BPC, Intents, + States, NULL, 0, TYPE_Lab_DBL, TYPE_Lab_DBL, cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE); + + cmsCloseProfile(hLab); + return xform; +} + +// Use darker colorants to obtain black point. This works in the relative colorimetric intent and +// assumes more ink results in darker colors. No ink limit is assumed. +static +cmsBool BlackPointAsDarkerColorant(cmsHPROFILE hInput, + cmsUInt32Number Intent, + cmsCIEXYZ* BlackPoint, + cmsUInt32Number dwFlags) +{ + cmsUInt16Number *Black; + cmsHTRANSFORM xform; + cmsColorSpaceSignature Space; + cmsUInt32Number nChannels; + cmsUInt32Number dwFormat; + cmsHPROFILE hLab; + cmsCIELab Lab; + cmsCIEXYZ BlackXYZ; + cmsContext ContextID = cmsGetProfileContextID(hInput); + + // If the profile does not support input direction, assume Black point 0 + if (!cmsIsIntentSupported(hInput, Intent, LCMS_USED_AS_INPUT)) { + + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } + + // Create a formatter which has n channels and floating point + dwFormat = cmsFormatterForColorspaceOfProfile(hInput, 2, FALSE); + + // Try to get black by using black colorant + Space = cmsGetColorSpace(hInput); + + // This function returns darker colorant in 16 bits for several spaces + if (!_cmsEndPointsBySpace(Space, NULL, &Black, &nChannels)) { + + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } + + if (nChannels != T_CHANNELS(dwFormat)) { + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } + + // Lab will be used as the output space, but lab2 will avoid recursion + hLab = cmsCreateLab2ProfileTHR(ContextID, NULL); + if (hLab == NULL) { + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } + + // Create the transform + xform = cmsCreateTransformTHR(ContextID, hInput, dwFormat, + hLab, TYPE_Lab_DBL, Intent, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE); + cmsCloseProfile(hLab); + + if (xform == NULL) { + + // Something went wrong. Get rid of open resources and return zero as black + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } + + // Convert black to Lab + cmsDoTransform(xform, Black, &Lab, 1); + + // Force it to be neutral, clip to max. L* of 50 + Lab.a = Lab.b = 0; + if (Lab.L > 50) Lab.L = 50; + + // Free the resources + cmsDeleteTransform(xform); + + // Convert from Lab (which is now clipped) to XYZ. + cmsLab2XYZ(NULL, &BlackXYZ, &Lab); + + if (BlackPoint != NULL) + *BlackPoint = BlackXYZ; + + return TRUE; + + cmsUNUSED_PARAMETER(dwFlags); +} + +// Get a black point of output CMYK profile, discounting any ink-limiting embedded +// in the profile. For doing that, we use perceptual intent in input direction: +// Lab (0, 0, 0) -> [Perceptual] Profile -> CMYK -> [Rel. colorimetric] Profile -> Lab +static +cmsBool BlackPointUsingPerceptualBlack(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile) +{ + cmsHTRANSFORM hRoundTrip; + cmsCIELab LabIn, LabOut; + cmsCIEXYZ BlackXYZ; + + // Is the intent supported by the profile? + if (!cmsIsIntentSupported(hProfile, INTENT_PERCEPTUAL, LCMS_USED_AS_INPUT)) { + + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return TRUE; + } + + hRoundTrip = CreateRoundtripXForm(hProfile, INTENT_PERCEPTUAL); + if (hRoundTrip == NULL) { + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } + + LabIn.L = LabIn.a = LabIn.b = 0; + cmsDoTransform(hRoundTrip, &LabIn, &LabOut, 1); + + // Clip Lab to reasonable limits + if (LabOut.L > 50) LabOut.L = 50; + LabOut.a = LabOut.b = 0; + + cmsDeleteTransform(hRoundTrip); + + // Convert it to XYZ + cmsLab2XYZ(NULL, &BlackXYZ, &LabOut); + + if (BlackPoint != NULL) + *BlackPoint = BlackXYZ; + + return TRUE; +} + +// This function shouldn't exist at all -- there is such quantity of broken +// profiles on black point tag, that we must somehow fix chromaticity to +// avoid huge tint when doing Black point compensation. This function does +// just that. There is a special flag for using black point tag, but turned +// off by default because it is bogus on most profiles. The detection algorithm +// involves to turn BP to neutral and to use only L component. +cmsBool CMSEXPORT cmsDetectBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags) +{ + cmsProfileClassSignature devClass; + + // Make sure the device class is adequate + devClass = cmsGetDeviceClass(hProfile); + if (devClass == cmsSigLinkClass || + devClass == cmsSigAbstractClass || + devClass == cmsSigNamedColorClass) { + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } + + // Make sure intent is adequate + if (Intent != INTENT_PERCEPTUAL && + Intent != INTENT_RELATIVE_COLORIMETRIC && + Intent != INTENT_SATURATION) { + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } + + // v4 + perceptual & saturation intents does have its own black point, and it is + // well specified enough to use it. Black point tag is deprecated in V4. + if ((cmsGetEncodedICCversion(hProfile) >= 0x4000000) && + (Intent == INTENT_PERCEPTUAL || Intent == INTENT_SATURATION)) { + + // Matrix shaper share MRC & perceptual intents + if (cmsIsMatrixShaper(hProfile)) + return BlackPointAsDarkerColorant(hProfile, INTENT_RELATIVE_COLORIMETRIC, BlackPoint, 0); + + // Get Perceptual black out of v4 profiles. That is fixed for perceptual & saturation intents + BlackPoint -> X = cmsPERCEPTUAL_BLACK_X; + BlackPoint -> Y = cmsPERCEPTUAL_BLACK_Y; + BlackPoint -> Z = cmsPERCEPTUAL_BLACK_Z; + + return TRUE; + } + + +#ifdef CMS_USE_PROFILE_BLACK_POINT_TAG + + // v2, v4 rel/abs colorimetric + if (cmsIsTag(hProfile, cmsSigMediaBlackPointTag) && + Intent == INTENT_RELATIVE_COLORIMETRIC) { + + cmsCIEXYZ *BlackPtr, BlackXYZ, UntrustedBlackPoint, TrustedBlackPoint, MediaWhite; + cmsCIELab Lab; + + // If black point is specified, then use it, + + BlackPtr = cmsReadTag(hProfile, cmsSigMediaBlackPointTag); + if (BlackPtr != NULL) { + + BlackXYZ = *BlackPtr; + _cmsReadMediaWhitePoint(&MediaWhite, hProfile); + + // Black point is absolute XYZ, so adapt to D50 to get PCS value + cmsAdaptToIlluminant(&UntrustedBlackPoint, &MediaWhite, cmsD50_XYZ(), &BlackXYZ); + + // Force a=b=0 to get rid of any chroma + cmsXYZ2Lab(NULL, &Lab, &UntrustedBlackPoint); + Lab.a = Lab.b = 0; + if (Lab.L > 50) Lab.L = 50; // Clip to L* <= 50 + cmsLab2XYZ(NULL, &TrustedBlackPoint, &Lab); + + if (BlackPoint != NULL) + *BlackPoint = TrustedBlackPoint; + + return TRUE; + } + } +#endif + + // That is about v2 profiles. + + // If output profile, discount ink-limiting and that's all + if (Intent == INTENT_RELATIVE_COLORIMETRIC && + (cmsGetDeviceClass(hProfile) == cmsSigOutputClass) && + (cmsGetColorSpace(hProfile) == cmsSigCmykData)) + return BlackPointUsingPerceptualBlack(BlackPoint, hProfile); + + // Nope, compute BP using current intent. + return BlackPointAsDarkerColorant(hProfile, Intent, BlackPoint, dwFlags); +} + + + +// --------------------------------------------------------------------------------------------------------- + +// Least Squares Fit of a Quadratic Curve to Data +// http://www.personal.psu.edu/jhm/f90/lectures/lsq2.html + +static +cmsFloat64Number RootOfLeastSquaresFitQuadraticCurve(int n, cmsFloat64Number x[], cmsFloat64Number y[]) +{ + double sum_x = 0, sum_x2 = 0, sum_x3 = 0, sum_x4 = 0; + double sum_y = 0, sum_yx = 0, sum_yx2 = 0; + double d, a, b, c; + int i; + cmsMAT3 m; + cmsVEC3 v, res; + + if (n < 4) return 0; + + for (i=0; i < n; i++) { + + double xn = x[i]; + double yn = y[i]; + + sum_x += xn; + sum_x2 += xn*xn; + sum_x3 += xn*xn*xn; + sum_x4 += xn*xn*xn*xn; + + sum_y += yn; + sum_yx += yn*xn; + sum_yx2 += yn*xn*xn; + } + + _cmsVEC3init(&m.v[0], n, sum_x, sum_x2); + _cmsVEC3init(&m.v[1], sum_x, sum_x2, sum_x3); + _cmsVEC3init(&m.v[2], sum_x2, sum_x3, sum_x4); + + _cmsVEC3init(&v, sum_y, sum_yx, sum_yx2); + + if (!_cmsMAT3solve(&res, &m, &v)) return 0; + + + a = res.n[2]; + b = res.n[1]; + c = res.n[0]; + + if (fabs(a) < 1.0E-10) { + + return cmsmin(0, cmsmax(50, -c/b )); + } + else { + + d = b*b - 4.0 * a * c; + if (d <= 0) { + return 0; + } + else { + + double rt = (-b + sqrt(d)) / (2.0 * a); + + return cmsmax(0, cmsmin(50, rt)); + } + } + +} + +/* +static +cmsBool IsMonotonic(int n, const cmsFloat64Number Table[]) +{ + int i; + cmsFloat64Number last; + + last = Table[n-1]; + + for (i = n-2; i >= 0; --i) { + + if (Table[i] > last) + + return FALSE; + else + last = Table[i]; + + } + + return TRUE; +} +*/ + +// Calculates the black point of a destination profile. +// This algorithm comes from the Adobe paper disclosing its black point compensation method. +cmsBool CMSEXPORT cmsDetectDestinationBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags) +{ + cmsColorSpaceSignature ColorSpace; + cmsHTRANSFORM hRoundTrip = NULL; + cmsCIELab InitialLab, destLab, Lab; + cmsFloat64Number inRamp[256], outRamp[256]; + cmsFloat64Number MinL, MaxL; + cmsBool NearlyStraightMidrange = TRUE; + cmsFloat64Number yRamp[256]; + cmsFloat64Number x[256], y[256]; + cmsFloat64Number lo, hi; + int n, l; + cmsProfileClassSignature devClass; + + // Make sure the device class is adequate + devClass = cmsGetDeviceClass(hProfile); + if (devClass == cmsSigLinkClass || + devClass == cmsSigAbstractClass || + devClass == cmsSigNamedColorClass) { + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } + + // Make sure intent is adequate + if (Intent != INTENT_PERCEPTUAL && + Intent != INTENT_RELATIVE_COLORIMETRIC && + Intent != INTENT_SATURATION) { + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } + + + // v4 + perceptual & saturation intents does have its own black point, and it is + // well specified enough to use it. Black point tag is deprecated in V4. + if ((cmsGetEncodedICCversion(hProfile) >= 0x4000000) && + (Intent == INTENT_PERCEPTUAL || Intent == INTENT_SATURATION)) { + + // Matrix shaper share MRC & perceptual intents + if (cmsIsMatrixShaper(hProfile)) + return BlackPointAsDarkerColorant(hProfile, INTENT_RELATIVE_COLORIMETRIC, BlackPoint, 0); + + // Get Perceptual black out of v4 profiles. That is fixed for perceptual & saturation intents + BlackPoint -> X = cmsPERCEPTUAL_BLACK_X; + BlackPoint -> Y = cmsPERCEPTUAL_BLACK_Y; + BlackPoint -> Z = cmsPERCEPTUAL_BLACK_Z; + return TRUE; + } + + + // Check if the profile is lut based and gray, rgb or cmyk (7.2 in Adobe's document) + ColorSpace = cmsGetColorSpace(hProfile); + if (!cmsIsCLUT(hProfile, Intent, LCMS_USED_AS_OUTPUT ) || + (ColorSpace != cmsSigGrayData && + ColorSpace != cmsSigRgbData && + ColorSpace != cmsSigCmykData)) { + + // In this case, handle as input case + return cmsDetectBlackPoint(BlackPoint, hProfile, Intent, dwFlags); + } + + // It is one of the valid cases!, use Adobe algorithm + + + // Set a first guess, that should work on good profiles. + if (Intent == INTENT_RELATIVE_COLORIMETRIC) { + + cmsCIEXYZ IniXYZ; + + // calculate initial Lab as source black point + if (!cmsDetectBlackPoint(&IniXYZ, hProfile, Intent, dwFlags)) { + return FALSE; + } + + // convert the XYZ to lab + cmsXYZ2Lab(NULL, &InitialLab, &IniXYZ); + + } else { + + // set the initial Lab to zero, that should be the black point for perceptual and saturation + InitialLab.L = 0; + InitialLab.a = 0; + InitialLab.b = 0; + } + + + // Step 2 + // ====== + + // Create a roundtrip. Define a Transform BT for all x in L*a*b* + hRoundTrip = CreateRoundtripXForm(hProfile, Intent); + if (hRoundTrip == NULL) return FALSE; + + // Compute ramps + + for (l=0; l < 256; l++) { + + Lab.L = (cmsFloat64Number) (l * 100.0) / 255.0; + Lab.a = cmsmin(50, cmsmax(-50, InitialLab.a)); + Lab.b = cmsmin(50, cmsmax(-50, InitialLab.b)); + + cmsDoTransform(hRoundTrip, &Lab, &destLab, 1); + + inRamp[l] = Lab.L; + outRamp[l] = destLab.L; + } + + // Make monotonic + for (l = 254; l > 0; --l) { + outRamp[l] = cmsmin(outRamp[l], outRamp[l+1]); + } + + // Check + if (! (outRamp[0] < outRamp[255])) { + + cmsDeleteTransform(hRoundTrip); + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } + + + // Test for mid range straight (only on relative colorimetric) + + NearlyStraightMidrange = TRUE; + MinL = outRamp[0]; MaxL = outRamp[255]; + if (Intent == INTENT_RELATIVE_COLORIMETRIC) { + + for (l=0; l < 256; l++) { + + if (! ((inRamp[l] <= MinL + 0.2 * (MaxL - MinL) ) || + (fabs(inRamp[l] - outRamp[l]) < 4.0 ))) + NearlyStraightMidrange = FALSE; + } + + // If the mid range is straight (as determined above) then the + // DestinationBlackPoint shall be the same as initialLab. + // Otherwise, the DestinationBlackPoint shall be determined + // using curve fitting. + + if (NearlyStraightMidrange) { + + cmsLab2XYZ(NULL, BlackPoint, &InitialLab); + cmsDeleteTransform(hRoundTrip); + return TRUE; + } + } + + + // curve fitting: The round-trip curve normally looks like a nearly constant section at the black point, + // with a corner and a nearly straight line to the white point. + + for (l=0; l < 256; l++) { + + yRamp[l] = (outRamp[l] - MinL) / (MaxL - MinL); + } + + // find the black point using the least squares error quadratic curve fitting + + if (Intent == INTENT_RELATIVE_COLORIMETRIC) { + lo = 0.1; + hi = 0.5; + } + else { + + // Perceptual and saturation + lo = 0.03; + hi = 0.25; + } + + // Capture shadow points for the fitting. + n = 0; + for (l=0; l < 256; l++) { + + cmsFloat64Number ff = yRamp[l]; + + if (ff >= lo && ff < hi) { + x[n] = inRamp[l]; + y[n] = yRamp[l]; + n++; + } + } + + + // No suitable points + if (n < 3 ) { + cmsDeleteTransform(hRoundTrip); + BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; + return FALSE; + } + + + // fit and get the vertex of quadratic curve + Lab.L = RootOfLeastSquaresFitQuadraticCurve(n, x, y); + + if (Lab.L < 0.0) { // clip to zero L* if the vertex is negative + Lab.L = 0; + } + + Lab.a = InitialLab.a; + Lab.b = InitialLab.b; + + cmsLab2XYZ(NULL, BlackPoint, &Lab); + + cmsDeleteTransform(hRoundTrip); + return TRUE; +} diff --git a/third_party/lcms/src/cmssm.c b/third_party/lcms/src/cmssm.c new file mode 100644 index 0000000000..5836e15506 --- /dev/null +++ b/third_party/lcms/src/cmssm.c @@ -0,0 +1,734 @@ +//--------------------------------------------------------------------------------- +// +// Little Color Management System +// Copyright (c) 1998-2011 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" + + +// ------------------------------------------------------------------------ + +// Gamut boundary description by using Jan Morovic's Segment maxima method +// Many thanks to Jan for allowing me to use his algorithm. + +// r = C* +// alpha = Hab +// theta = L* + +#define SECTORS 16 // number of divisions in alpha and theta + +// Spherical coordinates +typedef struct { + + cmsFloat64Number r; + cmsFloat64Number alpha; + cmsFloat64Number theta; + +} cmsSpherical; + +typedef enum { + GP_EMPTY, + GP_SPECIFIED, + GP_MODELED + + } GDBPointType; + + +typedef struct { + + GDBPointType Type; + cmsSpherical p; // Keep also alpha & theta of maximum + +} cmsGDBPoint; + + +typedef struct { + + cmsContext ContextID; + cmsGDBPoint Gamut[SECTORS][SECTORS]; + +} cmsGDB; + + +// A line using the parametric form +// P = a + t*u +typedef struct { + + cmsVEC3 a; + cmsVEC3 u; + +} cmsLine; + + +// A plane using the parametric form +// Q = b + r*v + s*w +typedef struct { + + cmsVEC3 b; + cmsVEC3 v; + cmsVEC3 w; + +} cmsPlane; + + + +// -------------------------------------------------------------------------------------------- + +// ATAN2() which always returns degree positive numbers + +static +cmsFloat64Number _cmsAtan2(cmsFloat64Number y, cmsFloat64Number x) +{ + cmsFloat64Number a; + + // Deal with undefined case + if (x == 0.0 && y == 0.0) return 0; + + a = (atan2(y, x) * 180.0) / M_PI; + + while (a < 0) { + a += 360; + } + + return a; +} + +// Convert to spherical coordinates +static +void ToSpherical(cmsSpherical* sp, const cmsVEC3* v) +{ + + cmsFloat64Number L, a, b; + + L = v ->n[VX]; + a = v ->n[VY]; + b = v ->n[VZ]; + + sp ->r = sqrt( L*L + a*a + b*b ); + + if (sp ->r == 0) { + sp ->alpha = sp ->theta = 0; + return; + } + + sp ->alpha = _cmsAtan2(a, b); + sp ->theta = _cmsAtan2(sqrt(a*a + b*b), L); +} + + +// Convert to cartesian from spherical +static +void ToCartesian(cmsVEC3* v, const cmsSpherical* sp) +{ + cmsFloat64Number sin_alpha; + cmsFloat64Number cos_alpha; + cmsFloat64Number sin_theta; + cmsFloat64Number cos_theta; + cmsFloat64Number L, a, b; + + sin_alpha = sin((M_PI * sp ->alpha) / 180.0); + cos_alpha = cos((M_PI * sp ->alpha) / 180.0); + sin_theta = sin((M_PI * sp ->theta) / 180.0); + cos_theta = cos((M_PI * sp ->theta) / 180.0); + + a = sp ->r * sin_theta * sin_alpha; + b = sp ->r * sin_theta * cos_alpha; + L = sp ->r * cos_theta; + + v ->n[VX] = L; + v ->n[VY] = a; + v ->n[VZ] = b; +} + + +// Quantize sector of a spherical coordinate. Saturate 360, 180 to last sector +// The limits are the centers of each sector, so +static +void QuantizeToSector(const cmsSpherical* sp, int* alpha, int* theta) +{ + *alpha = (int) floor(((sp->alpha * (SECTORS)) / 360.0) ); + *theta = (int) floor(((sp->theta * (SECTORS)) / 180.0) ); + + if (*alpha >= SECTORS) + *alpha = SECTORS-1; + if (*theta >= SECTORS) + *theta = SECTORS-1; +} + + +// Line determined by 2 points +static +void LineOf2Points(cmsLine* line, cmsVEC3* a, cmsVEC3* b) +{ + + _cmsVEC3init(&line ->a, a ->n[VX], a ->n[VY], a ->n[VZ]); + _cmsVEC3init(&line ->u, b ->n[VX] - a ->n[VX], + b ->n[VY] - a ->n[VY], + b ->n[VZ] - a ->n[VZ]); +} + + +// Evaluate parametric line +static +void GetPointOfLine(cmsVEC3* p, const cmsLine* line, cmsFloat64Number t) +{ + p ->n[VX] = line ->a.n[VX] + t * line->u.n[VX]; + p ->n[VY] = line ->a.n[VY] + t * line->u.n[VY]; + p ->n[VZ] = line ->a.n[VZ] + t * line->u.n[VZ]; +} + + + +/* + Closest point in sector line1 to sector line2 (both are defined as 0 <=t <= 1) + http://softsurfer.com/Archive/algorithm_0106/algorithm_0106.htm + + Copyright 2001, softSurfer (www.softsurfer.com) + This code may be freely used and modified for any purpose + providing that this copyright notice is included with it. + SoftSurfer makes no warranty for this code, and cannot be held + liable for any real or imagined damage resulting from its use. + Users of this code must verify correctness for their application. + +*/ + +static +cmsBool ClosestLineToLine(cmsVEC3* r, const cmsLine* line1, const cmsLine* line2) +{ + cmsFloat64Number a, b, c, d, e, D; + cmsFloat64Number sc, sN, sD; + cmsFloat64Number tc, tN, tD; + cmsVEC3 w0; + + _cmsVEC3minus(&w0, &line1 ->a, &line2 ->a); + + a = _cmsVEC3dot(&line1 ->u, &line1 ->u); + b = _cmsVEC3dot(&line1 ->u, &line2 ->u); + c = _cmsVEC3dot(&line2 ->u, &line2 ->u); + d = _cmsVEC3dot(&line1 ->u, &w0); + e = _cmsVEC3dot(&line2 ->u, &w0); + + D = a*c - b * b; // Denominator + sD = tD = D; // default sD = D >= 0 + + if (D < MATRIX_DET_TOLERANCE) { // the lines are almost parallel + + sN = 0.0; // force using point P0 on segment S1 + sD = 1.0; // to prevent possible division by 0.0 later + tN = e; + tD = c; + } + else { // get the closest points on the infinite lines + + sN = (b*e - c*d); + tN = (a*e - b*d); + + if (sN < 0.0) { // sc < 0 => the s=0 edge is visible + + sN = 0.0; + tN = e; + tD = c; + } + else if (sN > sD) { // sc > 1 => the s=1 edge is visible + sN = sD; + tN = e + b; + tD = c; + } + } + + if (tN < 0.0) { // tc < 0 => the t=0 edge is visible + + tN = 0.0; + // recompute sc for this edge + if (-d < 0.0) + sN = 0.0; + else if (-d > a) + sN = sD; + else { + sN = -d; + sD = a; + } + } + else if (tN > tD) { // tc > 1 => the t=1 edge is visible + + tN = tD; + + // recompute sc for this edge + if ((-d + b) < 0.0) + sN = 0; + else if ((-d + b) > a) + sN = sD; + else { + sN = (-d + b); + sD = a; + } + } + // finally do the division to get sc and tc + sc = (fabs(sN) < MATRIX_DET_TOLERANCE ? 0.0 : sN / sD); + tc = (fabs(tN) < MATRIX_DET_TOLERANCE ? 0.0 : tN / tD); + + GetPointOfLine(r, line1, sc); + return TRUE; +} + + + +// ------------------------------------------------------------------ Wrapper + + +// Allocate & free structure +cmsHANDLE CMSEXPORT cmsGBDAlloc(cmsContext ContextID) +{ + cmsGDB* gbd = (cmsGDB*) _cmsMallocZero(ContextID, sizeof(cmsGDB)); + if (gbd == NULL) return NULL; + + gbd -> ContextID = ContextID; + + return (cmsHANDLE) gbd; +} + + +void CMSEXPORT cmsGBDFree(cmsHANDLE hGBD) +{ + cmsGDB* gbd = (cmsGDB*) hGBD; + if (hGBD != NULL) + _cmsFree(gbd->ContextID, (void*) gbd); +} + + +// Auxiliar to retrieve a pointer to the segmentr containing the Lab value +static +cmsGDBPoint* GetPoint(cmsGDB* gbd, const cmsCIELab* Lab, cmsSpherical* sp) +{ + cmsVEC3 v; + int alpha, theta; + + // Housekeeping + _cmsAssert(gbd != NULL); + _cmsAssert(Lab != NULL); + _cmsAssert(sp != NULL); + + // Center L* by substracting half of its domain, that's 50 + _cmsVEC3init(&v, Lab ->L - 50.0, Lab ->a, Lab ->b); + + // Convert to spherical coordinates + ToSpherical(sp, &v); + + if (sp ->r < 0 || sp ->alpha < 0 || sp->theta < 0) { + cmsSignalError(gbd ->ContextID, cmsERROR_RANGE, "spherical value out of range"); + return NULL; + } + + // On which sector it falls? + QuantizeToSector(sp, &alpha, &theta); + + if (alpha < 0 || theta < 0 || alpha >= SECTORS || theta >= SECTORS) { + cmsSignalError(gbd ->ContextID, cmsERROR_RANGE, " quadrant out of range"); + return NULL; + } + + // Get pointer to the sector + return &gbd ->Gamut[theta][alpha]; +} + +// Add a point to gamut descriptor. Point to add is in Lab color space. +// GBD is centered on a=b=0 and L*=50 +cmsBool CMSEXPORT cmsGDBAddPoint(cmsHANDLE hGBD, const cmsCIELab* Lab) +{ + cmsGDB* gbd = (cmsGDB*) hGBD; + cmsGDBPoint* ptr; + cmsSpherical sp; + + + // Get pointer to the sector + ptr = GetPoint(gbd, Lab, &sp); + if (ptr == NULL) return FALSE; + + // If no samples at this sector, add it + if (ptr ->Type == GP_EMPTY) { + + ptr -> Type = GP_SPECIFIED; + ptr -> p = sp; + } + else { + + + // Substitute only if radius is greater + if (sp.r > ptr -> p.r) { + + ptr -> Type = GP_SPECIFIED; + ptr -> p = sp; + } + } + + return TRUE; +} + +// Check if a given point falls inside gamut +cmsBool CMSEXPORT cmsGDBCheckPoint(cmsHANDLE hGBD, const cmsCIELab* Lab) +{ + cmsGDB* gbd = (cmsGDB*) hGBD; + cmsGDBPoint* ptr; + cmsSpherical sp; + + // Get pointer to the sector + ptr = GetPoint(gbd, Lab, &sp); + if (ptr == NULL) return FALSE; + + // If no samples at this sector, return no data + if (ptr ->Type == GP_EMPTY) return FALSE; + + // In gamut only if radius is greater + + return (sp.r <= ptr -> p.r); +} + +// ----------------------------------------------------------------------------------------------------------------------- + +// Find near sectors. The list of sectors found is returned on Close[]. +// The function returns the number of sectors as well. + +// 24 9 10 11 12 +// 23 8 1 2 13 +// 22 7 * 3 14 +// 21 6 5 4 15 +// 20 19 18 17 16 +// +// Those are the relative movements +// {-2,-2}, {-1, -2}, {0, -2}, {+1, -2}, {+2, -2}, +// {-2,-1}, {-1, -1}, {0, -1}, {+1, -1}, {+2, -1}, +// {-2, 0}, {-1, 0}, {0, 0}, {+1, 0}, {+2, 0}, +// {-2,+1}, {-1, +1}, {0, +1}, {+1, +1}, {+2, +1}, +// {-2,+2}, {-1, +2}, {0, +2}, {+1, +2}, {+2, +2}}; + + +static +const struct _spiral { + + int AdvX, AdvY; + + } Spiral[] = { {0, -1}, {+1, -1}, {+1, 0}, {+1, +1}, {0, +1}, {-1, +1}, + {-1, 0}, {-1, -1}, {-1, -2}, {0, -2}, {+1, -2}, {+2, -2}, + {+2, -1}, {+2, 0}, {+2, +1}, {+2, +2}, {+1, +2}, {0, +2}, + {-1, +2}, {-2, +2}, {-2, +1}, {-2, 0}, {-2, -1}, {-2, -2} }; + +#define NSTEPS (sizeof(Spiral) / sizeof(struct _spiral)) + +static +int FindNearSectors(cmsGDB* gbd, int alpha, int theta, cmsGDBPoint* Close[]) +{ + int nSectors = 0; + int a, t; + cmsUInt32Number i; + cmsGDBPoint* pt; + + for (i=0; i < NSTEPS; i++) { + + a = alpha + Spiral[i].AdvX; + t = theta + Spiral[i].AdvY; + + // Cycle at the end + a %= SECTORS; + t %= SECTORS; + + // Cycle at the begin + if (a < 0) a = SECTORS + a; + if (t < 0) t = SECTORS + t; + + pt = &gbd ->Gamut[t][a]; + + if (pt -> Type != GP_EMPTY) { + + Close[nSectors++] = pt; + } + } + + return nSectors; +} + + +// Interpolate a missing sector. Method identifies whatever this is top, bottom or mid +static +cmsBool InterpolateMissingSector(cmsGDB* gbd, int alpha, int theta) +{ + cmsSpherical sp; + cmsVEC3 Lab; + cmsVEC3 Centre; + cmsLine ray; + int nCloseSectors; + cmsGDBPoint* Close[NSTEPS + 1]; + cmsSpherical closel, templ; + cmsLine edge; + int k, m; + + // Is that point already specified? + if (gbd ->Gamut[theta][alpha].Type != GP_EMPTY) return TRUE; + + // Fill close points + nCloseSectors = FindNearSectors(gbd, alpha, theta, Close); + + + // Find a central point on the sector + sp.alpha = (cmsFloat64Number) ((alpha + 0.5) * 360.0) / (SECTORS); + sp.theta = (cmsFloat64Number) ((theta + 0.5) * 180.0) / (SECTORS); + sp.r = 50.0; + + // Convert to Cartesian + ToCartesian(&Lab, &sp); + + // Create a ray line from centre to this point + _cmsVEC3init(&Centre, 50.0, 0, 0); + LineOf2Points(&ray, &Lab, &Centre); + + // For all close sectors + closel.r = 0.0; + closel.alpha = 0; + closel.theta = 0; + + for (k=0; k < nCloseSectors; k++) { + + for(m = k+1; m < nCloseSectors; m++) { + + cmsVEC3 temp, a1, a2; + + // A line from sector to sector + ToCartesian(&a1, &Close[k]->p); + ToCartesian(&a2, &Close[m]->p); + + LineOf2Points(&edge, &a1, &a2); + + // Find a line + ClosestLineToLine(&temp, &ray, &edge); + + // Convert to spherical + ToSpherical(&templ, &temp); + + + if ( templ.r > closel.r && + templ.theta >= (theta*180.0/SECTORS) && + templ.theta <= ((theta+1)*180.0/SECTORS) && + templ.alpha >= (alpha*360.0/SECTORS) && + templ.alpha <= ((alpha+1)*360.0/SECTORS)) { + + closel = templ; + } + } + } + + gbd ->Gamut[theta][alpha].p = closel; + gbd ->Gamut[theta][alpha].Type = GP_MODELED; + + return TRUE; + +} + + +// Interpolate missing parts. The algorithm fist computes slices at +// theta=0 and theta=Max. +cmsBool CMSEXPORT cmsGDBCompute(cmsHANDLE hGBD, cmsUInt32Number dwFlags) +{ + int alpha, theta; + cmsGDB* gbd = (cmsGDB*) hGBD; + + _cmsAssert(hGBD != NULL); + + // Interpolate black + for (alpha = 0; alpha < SECTORS; alpha++) { + + if (!InterpolateMissingSector(gbd, alpha, 0)) return FALSE; + } + + // Interpolate white + for (alpha = 0; alpha < SECTORS; alpha++) { + + if (!InterpolateMissingSector(gbd, alpha, SECTORS-1)) return FALSE; + } + + + // Interpolate Mid + for (theta = 1; theta < SECTORS; theta++) { + for (alpha = 0; alpha < SECTORS; alpha++) { + + if (!InterpolateMissingSector(gbd, alpha, theta)) return FALSE; + } + } + + // Done + return TRUE; + + cmsUNUSED_PARAMETER(dwFlags); +} + + + + +// -------------------------------------------------------------------------------------------------------- + +// Great for debug, but not suitable for real use + +#if 0 +cmsBool cmsGBDdumpVRML(cmsHANDLE hGBD, const char* fname) +{ + FILE* fp; + int i, j; + cmsGDB* gbd = (cmsGDB*) hGBD; + cmsGDBPoint* pt; + + fp = fopen (fname, "wt"); + if (fp == NULL) + return FALSE; + + fprintf (fp, "#VRML V2.0 utf8\n"); + + // set the viewing orientation and distance + fprintf (fp, "DEF CamTest Group {\n"); + fprintf (fp, "\tchildren [\n"); + fprintf (fp, "\t\tDEF Cameras Group {\n"); + fprintf (fp, "\t\t\tchildren [\n"); + fprintf (fp, "\t\t\t\tDEF DefaultView Viewpoint {\n"); + fprintf (fp, "\t\t\t\t\tposition 0 0 340\n"); + fprintf (fp, "\t\t\t\t\torientation 0 0 1 0\n"); + fprintf (fp, "\t\t\t\t\tdescription \"default view\"\n"); + fprintf (fp, "\t\t\t\t}\n"); + fprintf (fp, "\t\t\t]\n"); + fprintf (fp, "\t\t},\n"); + fprintf (fp, "\t]\n"); + fprintf (fp, "}\n"); + + // Output the background stuff + fprintf (fp, "Background {\n"); + fprintf (fp, "\tskyColor [\n"); + fprintf (fp, "\t\t.5 .5 .5\n"); + fprintf (fp, "\t]\n"); + fprintf (fp, "}\n"); + + // Output the shape stuff + fprintf (fp, "Transform {\n"); + fprintf (fp, "\tscale .3 .3 .3\n"); + fprintf (fp, "\tchildren [\n"); + + // Draw the axes as a shape: + fprintf (fp, "\t\tShape {\n"); + fprintf (fp, "\t\t\tappearance Appearance {\n"); + fprintf (fp, "\t\t\t\tmaterial Material {\n"); + fprintf (fp, "\t\t\t\t\tdiffuseColor 0 0.8 0\n"); + fprintf (fp, "\t\t\t\t\temissiveColor 1.0 1.0 1.0\n"); + fprintf (fp, "\t\t\t\t\tshininess 0.8\n"); + fprintf (fp, "\t\t\t\t}\n"); + fprintf (fp, "\t\t\t}\n"); + fprintf (fp, "\t\t\tgeometry IndexedLineSet {\n"); + fprintf (fp, "\t\t\t\tcoord Coordinate {\n"); + fprintf (fp, "\t\t\t\t\tpoint [\n"); + fprintf (fp, "\t\t\t\t\t0.0 0.0 0.0,\n"); + fprintf (fp, "\t\t\t\t\t%f 0.0 0.0,\n", 255.0); + fprintf (fp, "\t\t\t\t\t0.0 %f 0.0,\n", 255.0); + fprintf (fp, "\t\t\t\t\t0.0 0.0 %f]\n", 255.0); + fprintf (fp, "\t\t\t\t}\n"); + fprintf (fp, "\t\t\t\tcoordIndex [\n"); + fprintf (fp, "\t\t\t\t\t0, 1, -1\n"); + fprintf (fp, "\t\t\t\t\t0, 2, -1\n"); + fprintf (fp, "\t\t\t\t\t0, 3, -1]\n"); + fprintf (fp, "\t\t\t}\n"); + fprintf (fp, "\t\t}\n"); + + + fprintf (fp, "\t\tShape {\n"); + fprintf (fp, "\t\t\tappearance Appearance {\n"); + fprintf (fp, "\t\t\t\tmaterial Material {\n"); + fprintf (fp, "\t\t\t\t\tdiffuseColor 0 0.8 0\n"); + fprintf (fp, "\t\t\t\t\temissiveColor 1 1 1\n"); + fprintf (fp, "\t\t\t\t\tshininess 0.8\n"); + fprintf (fp, "\t\t\t\t}\n"); + fprintf (fp, "\t\t\t}\n"); + fprintf (fp, "\t\t\tgeometry PointSet {\n"); + + // fill in the points here + fprintf (fp, "\t\t\t\tcoord Coordinate {\n"); + fprintf (fp, "\t\t\t\t\tpoint [\n"); + + // We need to transverse all gamut hull. + for (i=0; i < SECTORS; i++) + for (j=0; j < SECTORS; j++) { + + cmsVEC3 v; + + pt = &gbd ->Gamut[i][j]; + ToCartesian(&v, &pt ->p); + + fprintf (fp, "\t\t\t\t\t%g %g %g", v.n[0]+50, v.n[1], v.n[2]); + + if ((j == SECTORS - 1) && (i == SECTORS - 1)) + fprintf (fp, "]\n"); + else + fprintf (fp, ",\n"); + + } + + fprintf (fp, "\t\t\t\t}\n"); + + + + // fill in the face colors + fprintf (fp, "\t\t\t\tcolor Color {\n"); + fprintf (fp, "\t\t\t\t\tcolor [\n"); + + for (i=0; i < SECTORS; i++) + for (j=0; j < SECTORS; j++) { + + cmsVEC3 v; + + pt = &gbd ->Gamut[i][j]; + + + ToCartesian(&v, &pt ->p); + + + if (pt ->Type == GP_EMPTY) + fprintf (fp, "\t\t\t\t\t%g %g %g", 0.0, 0.0, 0.0); + else + if (pt ->Type == GP_MODELED) + fprintf (fp, "\t\t\t\t\t%g %g %g", 1.0, .5, .5); + else { + fprintf (fp, "\t\t\t\t\t%g %g %g", 1.0, 1.0, 1.0); + + } + + if ((j == SECTORS - 1) && (i == SECTORS - 1)) + fprintf (fp, "]\n"); + else + fprintf (fp, ",\n"); + } + fprintf (fp, "\t\t\t}\n"); + + + fprintf (fp, "\t\t\t}\n"); + fprintf (fp, "\t\t}\n"); + fprintf (fp, "\t]\n"); + fprintf (fp, "}\n"); + + fclose (fp); + + return TRUE; +} +#endif diff --git a/third_party/lcms/src/cmstypes.c b/third_party/lcms/src/cmstypes.c new file mode 100644 index 0000000000..29806fb194 --- /dev/null +++ b/third_party/lcms/src/cmstypes.c @@ -0,0 +1,5610 @@ +//--------------------------------------------------------------------------------- +// +// 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 "lcms2_internal.h" + +// Tag Serialization ----------------------------------------------------------------------------- +// This file implements every single tag and tag type as described in the ICC spec. Some types +// have been deprecated, like ncl and Data. There is no implementation for those types as there +// are no profiles holding them. The programmer can also extend this list by defining his own types +// by using the appropiate plug-in. There are three types of plug ins regarding that. First type +// allows to define new tags using any existing type. Next plug-in type allows to define new types +// and the third one is very specific: allows to extend the number of elements in the multiprocessing +// elements special type. +//-------------------------------------------------------------------------------------------------- + +// Some broken types +#define cmsCorbisBrokenXYZtype ((cmsTagTypeSignature) 0x17A505B8) +#define cmsMonacoBrokenCurveType ((cmsTagTypeSignature) 0x9478ee00) + +// This is the linked list that keeps track of the defined types +typedef struct _cmsTagTypeLinkedList_st { + + cmsTagTypeHandler Handler; + struct _cmsTagTypeLinkedList_st* Next; + +} _cmsTagTypeLinkedList; + +// Some macros to define callbacks. +#define READ_FN(x) Type_##x##_Read +#define WRITE_FN(x) Type_##x##_Write +#define FREE_FN(x) Type_##x##_Free +#define DUP_FN(x) Type_##x##_Dup + +// Helper macro to define a handler. Callbacks do have a fixed naming convention. +#define TYPE_HANDLER(t, x) { (t), READ_FN(x), WRITE_FN(x), DUP_FN(x), FREE_FN(x), NULL, 0 } + +// 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. LinkedList points to the optional list head +static +cmsBool RegisterTypesPlugin(cmsContext id, cmsPluginBase* Data, _cmsMemoryClient pos) +{ + cmsPluginTagType* Plugin = (cmsPluginTagType*) Data; + _cmsTagTypePluginChunkType* ctx = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(id, pos); + _cmsTagTypeLinkedList *pt; + + // Calling the function with NULL as plug-in would unregister the plug in. + if (Data == NULL) { + + // There is no need to set free the memory, as pool is destroyed as a whole. + ctx ->TagTypes = NULL; + return TRUE; + } + + // Registering happens in plug-in memory pool. + pt = (_cmsTagTypeLinkedList*) _cmsPluginMalloc(id, sizeof(_cmsTagTypeLinkedList)); + if (pt == NULL) return FALSE; + + pt ->Handler = Plugin ->Handler; + 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. It first tries the additons +// made by plug-ins and then the built-in defaults. +static +cmsTagTypeHandler* GetHandler(cmsTagTypeSignature sig, _cmsTagTypeLinkedList* PluginLinkedList, _cmsTagTypeLinkedList* DefaultLinkedList) +{ + _cmsTagTypeLinkedList* pt; + + for (pt = PluginLinkedList; + pt != NULL; + pt = pt ->Next) { + + if (sig == pt -> Handler.Signature) return &pt ->Handler; + } + + for (pt = DefaultLinkedList; + pt != NULL; + pt = pt ->Next) { + + if (sig == pt -> Handler.Signature) return &pt ->Handler; + } + + return NULL; +} + + +// Auxiliar to convert UTF-32 to UTF-16 in some cases +static +cmsBool _cmsWriteWCharArray(cmsIOHANDLER* io, cmsUInt32Number n, const wchar_t* Array) +{ + cmsUInt32Number i; + + _cmsAssert(io != NULL); + _cmsAssert(!(Array == NULL && n > 0)); + + for (i=0; i < n; i++) { + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) Array[i])) return FALSE; + } + + return TRUE; +} + +// Auxiliar to read an array of wchar_t +static +cmsBool _cmsReadWCharArray(cmsIOHANDLER* io, cmsUInt32Number n, wchar_t* Array) +{ + cmsUInt32Number i; + cmsUInt16Number tmp; + + _cmsAssert(io != NULL); + + for (i=0; i < n; i++) { + + if (Array != NULL) { + + if (!_cmsReadUInt16Number(io, &tmp)) return FALSE; + Array[i] = (wchar_t) tmp; + } + else { + if (!_cmsReadUInt16Number(io, NULL)) return FALSE; + } + + } + return TRUE; +} + +// To deal with position tables +typedef cmsBool (* PositionTableEntryFn)(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + void* Cargo, + cmsUInt32Number n, + cmsUInt32Number SizeOfTag); + +// Helper function to deal with position tables as decribed in ICC spec 4.3 +// A table of n elements is readed, where first comes n records containing offsets and sizes and +// then a block containing the data itself. This allows to reuse same data in more than one entry +static +cmsBool ReadPositionTable(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + cmsUInt32Number Count, + cmsUInt32Number BaseOffset, + void *Cargo, + PositionTableEntryFn ElementFn) +{ + cmsUInt32Number i; + cmsUInt32Number *ElementOffsets = NULL, *ElementSizes = NULL; + cmsUInt32Number currentPosition; + + currentPosition = io->Tell(io); + // Verify there is enough space left to read two cmsUInt32Number items for Count items. + if (((io->ReportedSize - currentPosition) / (2 * sizeof(cmsUInt32Number))) < Count) + return FALSE; + + // Let's take the offsets to each element + ElementOffsets = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number)); + if (ElementOffsets == NULL) goto Error; + + ElementSizes = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number)); + if (ElementSizes == NULL) goto Error; + + for (i=0; i < Count; i++) { + + if (!_cmsReadUInt32Number(io, &ElementOffsets[i])) goto Error; + if (!_cmsReadUInt32Number(io, &ElementSizes[i])) goto Error; + + ElementOffsets[i] += BaseOffset; + } + + // Seek to each element and read it + for (i=0; i < Count; i++) { + + if (!io -> Seek(io, ElementOffsets[i])) goto Error; + + // This is the reader callback + if (!ElementFn(self, io, Cargo, i, ElementSizes[i])) goto Error; + } + + // Success + if (ElementOffsets != NULL) _cmsFree(io ->ContextID, ElementOffsets); + if (ElementSizes != NULL) _cmsFree(io ->ContextID, ElementSizes); + return TRUE; + +Error: + if (ElementOffsets != NULL) _cmsFree(io ->ContextID, ElementOffsets); + if (ElementSizes != NULL) _cmsFree(io ->ContextID, ElementSizes); + return FALSE; +} + +// Same as anterior, but for write position tables +static +cmsBool WritePositionTable(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + cmsUInt32Number SizeOfTag, + cmsUInt32Number Count, + cmsUInt32Number BaseOffset, + void *Cargo, + PositionTableEntryFn ElementFn) +{ + cmsUInt32Number i; + cmsUInt32Number DirectoryPos, CurrentPos, Before; + cmsUInt32Number *ElementOffsets = NULL, *ElementSizes = NULL; + + // Create table + ElementOffsets = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number)); + if (ElementOffsets == NULL) goto Error; + + ElementSizes = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number)); + if (ElementSizes == NULL) goto Error; + + // Keep starting position of curve offsets + DirectoryPos = io ->Tell(io); + + // Write a fake directory to be filled latter on + for (i=0; i < Count; i++) { + + if (!_cmsWriteUInt32Number(io, 0)) goto Error; // Offset + if (!_cmsWriteUInt32Number(io, 0)) goto Error; // size + } + + // Write each element. Keep track of the size as well. + for (i=0; i < Count; i++) { + + Before = io ->Tell(io); + ElementOffsets[i] = Before - BaseOffset; + + // Callback to write... + if (!ElementFn(self, io, Cargo, i, SizeOfTag)) goto Error; + + // Now the size + ElementSizes[i] = io ->Tell(io) - Before; + } + + // Write the directory + CurrentPos = io ->Tell(io); + if (!io ->Seek(io, DirectoryPos)) goto Error; + + for (i=0; i < Count; i++) { + if (!_cmsWriteUInt32Number(io, ElementOffsets[i])) goto Error; + if (!_cmsWriteUInt32Number(io, ElementSizes[i])) goto Error; + } + + if (!io ->Seek(io, CurrentPos)) goto Error; + + if (ElementOffsets != NULL) _cmsFree(io ->ContextID, ElementOffsets); + if (ElementSizes != NULL) _cmsFree(io ->ContextID, ElementSizes); + return TRUE; + +Error: + if (ElementOffsets != NULL) _cmsFree(io ->ContextID, ElementOffsets); + if (ElementSizes != NULL) _cmsFree(io ->ContextID, ElementSizes); + return FALSE; +} + + +// ******************************************************************************** +// Type XYZ. Only one value is allowed +// ******************************************************************************** + +//The XYZType contains an array of three encoded values for the XYZ tristimulus +//values. Tristimulus values must be non-negative. The signed encoding allows for +//implementation optimizations by minimizing the number of fixed formats. + + +static +void *Type_XYZ_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsCIEXYZ* xyz; + + *nItems = 0; + xyz = (cmsCIEXYZ*) _cmsMallocZero(self ->ContextID, sizeof(cmsCIEXYZ)); + if (xyz == NULL) return NULL; + + if (!_cmsReadXYZNumber(io, xyz)) { + _cmsFree(self ->ContextID, xyz); + return NULL; + } + + *nItems = 1; + return (void*) xyz; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +static +cmsBool Type_XYZ_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + return _cmsWriteXYZNumber(io, (cmsCIEXYZ*) Ptr); + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + +static +void* Type_XYZ_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsCIEXYZ)); + + cmsUNUSED_PARAMETER(n); +} + +static +void Type_XYZ_Free(struct _cms_typehandler_struct* self, void *Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + + +static +cmsTagTypeSignature DecideXYZtype(cmsFloat64Number ICCVersion, const void *Data) +{ + return cmsSigXYZType; + + cmsUNUSED_PARAMETER(ICCVersion); + cmsUNUSED_PARAMETER(Data); +} + + +// ******************************************************************************** +// Type chromaticity. Only one value is allowed +// ******************************************************************************** +// The chromaticity tag type provides basic chromaticity data and type of +// phosphors or colorants of a monitor to applications and utilities. + +static +void *Type_Chromaticity_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsCIExyYTRIPLE* chrm; + cmsUInt16Number nChans, Table; + + *nItems = 0; + chrm = (cmsCIExyYTRIPLE*) _cmsMallocZero(self ->ContextID, sizeof(cmsCIExyYTRIPLE)); + if (chrm == NULL) return NULL; + + if (!_cmsReadUInt16Number(io, &nChans)) goto Error; + + // Let's recover from a bug introduced in early versions of lcms1 + if (nChans == 0 && SizeOfTag == 32) { + + if (!_cmsReadUInt16Number(io, NULL)) goto Error; + if (!_cmsReadUInt16Number(io, &nChans)) goto Error; + } + + if (nChans != 3) goto Error; + + if (!_cmsReadUInt16Number(io, &Table)) goto Error; + + if (!_cmsRead15Fixed16Number(io, &chrm ->Red.x)) goto Error; + if (!_cmsRead15Fixed16Number(io, &chrm ->Red.y)) goto Error; + + chrm ->Red.Y = 1.0; + + if (!_cmsRead15Fixed16Number(io, &chrm ->Green.x)) goto Error; + if (!_cmsRead15Fixed16Number(io, &chrm ->Green.y)) goto Error; + + chrm ->Green.Y = 1.0; + + if (!_cmsRead15Fixed16Number(io, &chrm ->Blue.x)) goto Error; + if (!_cmsRead15Fixed16Number(io, &chrm ->Blue.y)) goto Error; + + chrm ->Blue.Y = 1.0; + + *nItems = 1; + return (void*) chrm; + +Error: + _cmsFree(self ->ContextID, (void*) chrm); + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +static +cmsBool SaveOneChromaticity(cmsFloat64Number x, cmsFloat64Number y, cmsIOHANDLER* io) +{ + if (!_cmsWriteUInt32Number(io, _cmsDoubleTo15Fixed16(x))) return FALSE; + if (!_cmsWriteUInt32Number(io, _cmsDoubleTo15Fixed16(y))) return FALSE; + + return TRUE; +} + +static +cmsBool Type_Chromaticity_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsCIExyYTRIPLE* chrm = (cmsCIExyYTRIPLE*) Ptr; + + if (!_cmsWriteUInt16Number(io, 3)) return FALSE; // nChannels + if (!_cmsWriteUInt16Number(io, 0)) return FALSE; // Table + + if (!SaveOneChromaticity(chrm -> Red.x, chrm -> Red.y, io)) return FALSE; + if (!SaveOneChromaticity(chrm -> Green.x, chrm -> Green.y, io)) return FALSE; + if (!SaveOneChromaticity(chrm -> Blue.x, chrm -> Blue.y, io)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + +static +void* Type_Chromaticity_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsCIExyYTRIPLE)); + + cmsUNUSED_PARAMETER(n); +} + +static +void Type_Chromaticity_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + + +// ******************************************************************************** +// Type cmsSigColorantOrderType +// ******************************************************************************** + +// This is an optional tag which specifies the laydown order in which colorants will +// be printed on an n-colorant device. The laydown order may be the same as the +// channel generation order listed in the colorantTableTag or the channel order of a +// colour space such as CMYK, in which case this tag is not needed. When this is not +// the case (for example, ink-towers sometimes use the order KCMY), this tag may be +// used to specify the laydown order of the colorants. + + +static +void *Type_ColorantOrderType_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsUInt8Number* ColorantOrder; + cmsUInt32Number Count; + + *nItems = 0; + if (!_cmsReadUInt32Number(io, &Count)) return NULL; + if (Count > cmsMAXCHANNELS) return NULL; + + ColorantOrder = (cmsUInt8Number*) _cmsCalloc(self ->ContextID, cmsMAXCHANNELS, sizeof(cmsUInt8Number)); + if (ColorantOrder == NULL) return NULL; + + // We use FF as end marker + memset(ColorantOrder, 0xFF, cmsMAXCHANNELS * sizeof(cmsUInt8Number)); + + if (io ->Read(io, ColorantOrder, sizeof(cmsUInt8Number), Count) != Count) { + + _cmsFree(self ->ContextID, (void*) ColorantOrder); + return NULL; + } + + *nItems = 1; + return (void*) ColorantOrder; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +static +cmsBool Type_ColorantOrderType_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsUInt8Number* ColorantOrder = (cmsUInt8Number*) Ptr; + cmsUInt32Number i, sz, Count; + + // Get the length + for (Count=i=0; i < cmsMAXCHANNELS; i++) { + if (ColorantOrder[i] != 0xFF) Count++; + } + + if (!_cmsWriteUInt32Number(io, Count)) return FALSE; + + sz = Count * sizeof(cmsUInt8Number); + if (!io -> Write(io, sz, ColorantOrder)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + +static +void* Type_ColorantOrderType_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self ->ContextID, Ptr, cmsMAXCHANNELS * sizeof(cmsUInt8Number)); + + cmsUNUSED_PARAMETER(n); +} + + +static +void Type_ColorantOrderType_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + +// ******************************************************************************** +// Type cmsSigS15Fixed16ArrayType +// ******************************************************************************** +// This type represents an array of generic 4-byte/32-bit fixed point quantity. +// The number of values is determined from the size of the tag. + +static +void *Type_S15Fixed16_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsFloat64Number* array_double; + cmsUInt32Number i, n; + + *nItems = 0; + n = SizeOfTag / sizeof(cmsUInt32Number); + array_double = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, n, sizeof(cmsFloat64Number)); + if (array_double == NULL) return NULL; + + for (i=0; i < n; i++) { + + if (!_cmsRead15Fixed16Number(io, &array_double[i])) { + + _cmsFree(self ->ContextID, array_double); + return NULL; + } + } + + *nItems = n; + return (void*) array_double; +} + +static +cmsBool Type_S15Fixed16_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsFloat64Number* Value = (cmsFloat64Number*) Ptr; + cmsUInt32Number i; + + for (i=0; i < nItems; i++) { + + if (!_cmsWrite15Fixed16Number(io, Value[i])) return FALSE; + } + + return TRUE; + + cmsUNUSED_PARAMETER(self); +} + +static +void* Type_S15Fixed16_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self ->ContextID, Ptr, n * sizeof(cmsFloat64Number)); +} + + +static +void Type_S15Fixed16_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + +// ******************************************************************************** +// Type cmsSigU16Fixed16ArrayType +// ******************************************************************************** +// This type represents an array of generic 4-byte/32-bit quantity. +// The number of values is determined from the size of the tag. + + +static +void *Type_U16Fixed16_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsFloat64Number* array_double; + cmsUInt32Number v; + cmsUInt32Number i, n; + + *nItems = 0; + n = SizeOfTag / sizeof(cmsUInt32Number); + array_double = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, n, sizeof(cmsFloat64Number)); + if (array_double == NULL) return NULL; + + for (i=0; i < n; i++) { + + if (!_cmsReadUInt32Number(io, &v)) { + _cmsFree(self ->ContextID, (void*) array_double); + return NULL; + } + + // Convert to cmsFloat64Number + array_double[i] = (cmsFloat64Number) (v / 65536.0); + } + + *nItems = n; + return (void*) array_double; +} + +static +cmsBool Type_U16Fixed16_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsFloat64Number* Value = (cmsFloat64Number*) Ptr; + cmsUInt32Number i; + + for (i=0; i < nItems; i++) { + + cmsUInt32Number v = (cmsUInt32Number) floor(Value[i]*65536.0 + 0.5); + + if (!_cmsWriteUInt32Number(io, v)) return FALSE; + } + + return TRUE; + + cmsUNUSED_PARAMETER(self); +} + + +static +void* Type_U16Fixed16_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self ->ContextID, Ptr, n * sizeof(cmsFloat64Number)); +} + +static +void Type_U16Fixed16_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + +// ******************************************************************************** +// Type cmsSigSignatureType +// ******************************************************************************** +// +// The signatureType contains a four-byte sequence, Sequences of less than four +// characters are padded at the end with spaces, 20h. +// Typically this type is used for registered tags that can be displayed on many +// development systems as a sequence of four characters. + +static +void *Type_Signature_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsSignature* SigPtr = (cmsSignature*) _cmsMalloc(self ->ContextID, sizeof(cmsSignature)); + if (SigPtr == NULL) return NULL; + + if (!_cmsReadUInt32Number(io, SigPtr)) return NULL; + *nItems = 1; + + return SigPtr; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +static +cmsBool Type_Signature_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsSignature* SigPtr = (cmsSignature*) Ptr; + + return _cmsWriteUInt32Number(io, *SigPtr); + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + +static +void* Type_Signature_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self ->ContextID, Ptr, n * sizeof(cmsSignature)); +} + +static +void Type_Signature_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + + +// ******************************************************************************** +// Type cmsSigTextType +// ******************************************************************************** +// +// The textType is a simple text structure that contains a 7-bit ASCII text string. +// The length of the string is obtained by subtracting 8 from the element size portion +// of the tag itself. This string must be terminated with a 00h byte. + +static +void *Type_Text_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + char* Text = NULL; + cmsMLU* mlu = NULL; + + // Create a container + mlu = cmsMLUalloc(self ->ContextID, 1); + if (mlu == NULL) return NULL; + + *nItems = 0; + + // We need to store the "\0" at the end, so +1 + if (SizeOfTag == UINT_MAX) goto Error; + + Text = (char*) _cmsMalloc(self ->ContextID, SizeOfTag + 1); + if (Text == NULL) goto Error; + + if (io -> Read(io, Text, sizeof(char), SizeOfTag) != SizeOfTag) goto Error; + + // Make sure text is properly ended + Text[SizeOfTag] = 0; + *nItems = 1; + + // Keep the result + if (!cmsMLUsetASCII(mlu, cmsNoLanguage, cmsNoCountry, Text)) goto Error; + + _cmsFree(self ->ContextID, Text); + return (void*) mlu; + +Error: + if (mlu != NULL) + cmsMLUfree(mlu); + if (Text != NULL) + _cmsFree(self ->ContextID, Text); + + return NULL; +} + +// The conversion implies to choose a language. So, we choose the actual language. +static +cmsBool Type_Text_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsMLU* mlu = (cmsMLU*) Ptr; + cmsUInt32Number size; + cmsBool rc; + char* Text; + + // Get the size of the string. Note there is an extra "\0" at the end + size = cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, NULL, 0); + if (size == 0) return FALSE; // Cannot be zero! + + // Create memory + Text = (char*) _cmsMalloc(self ->ContextID, size); + if (Text == NULL) return FALSE; + + cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, Text, size); + + // Write it, including separator + rc = io ->Write(io, size, Text); + + _cmsFree(self ->ContextID, Text); + return rc; + + cmsUNUSED_PARAMETER(nItems); +} + +static +void* Type_Text_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsMLUdup((cmsMLU*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + + +static +void Type_Text_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsMLU* mlu = (cmsMLU*) Ptr; + cmsMLUfree(mlu); + return; + + cmsUNUSED_PARAMETER(self); +} + +static +cmsTagTypeSignature DecideTextType(cmsFloat64Number ICCVersion, const void *Data) +{ + if (ICCVersion >= 4.0) + return cmsSigMultiLocalizedUnicodeType; + + return cmsSigTextType; + + cmsUNUSED_PARAMETER(Data); +} + + +// ******************************************************************************** +// Type cmsSigDataType +// ******************************************************************************** + +// General purpose data type +static +void *Type_Data_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsICCData* BinData; + cmsUInt32Number LenOfData; + + *nItems = 0; + + if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL; + + LenOfData = SizeOfTag - sizeof(cmsUInt32Number); + if (LenOfData > INT_MAX) return NULL; + + BinData = (cmsICCData*) _cmsMalloc(self ->ContextID, sizeof(cmsICCData) + LenOfData - 1); + if (BinData == NULL) return NULL; + + BinData ->len = LenOfData; + if (!_cmsReadUInt32Number(io, &BinData->flag)) { + _cmsFree(self ->ContextID, BinData); + return NULL; + } + + if (io -> Read(io, BinData ->data, sizeof(cmsUInt8Number), LenOfData) != LenOfData) { + + _cmsFree(self ->ContextID, BinData); + return NULL; + } + + *nItems = 1; + + return (void*) BinData; +} + + +static +cmsBool Type_Data_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsICCData* BinData = (cmsICCData*) Ptr; + + if (!_cmsWriteUInt32Number(io, BinData ->flag)) return FALSE; + + return io ->Write(io, BinData ->len, BinData ->data); + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + + +static +void* Type_Data_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + cmsICCData* BinData = (cmsICCData*) Ptr; + + return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsICCData) + BinData ->len - 1); + + cmsUNUSED_PARAMETER(n); +} + +static +void Type_Data_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + +// ******************************************************************************** +// Type cmsSigTextDescriptionType +// ******************************************************************************** + +static +void *Type_Text_Description_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + char* Text = NULL; + cmsMLU* mlu = NULL; + cmsUInt32Number AsciiCount; + cmsUInt32Number i, UnicodeCode, UnicodeCount; + cmsUInt16Number ScriptCodeCode, Dummy; + cmsUInt8Number ScriptCodeCount; + + *nItems = 0; + + // One dword should be there + if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL; + + // Read len of ASCII + if (!_cmsReadUInt32Number(io, &AsciiCount)) return NULL; + SizeOfTag -= sizeof(cmsUInt32Number); + + // Check for size + if (SizeOfTag < AsciiCount) return NULL; + + // All seems Ok, allocate the container + mlu = cmsMLUalloc(self ->ContextID, 1); + if (mlu == NULL) return NULL; + + // As many memory as size of tag + Text = (char*) _cmsMalloc(self ->ContextID, AsciiCount + 1); + if (Text == NULL) goto Error; + + // Read it + if (io ->Read(io, Text, sizeof(char), AsciiCount) != AsciiCount) goto Error; + SizeOfTag -= AsciiCount; + + // Make sure there is a terminator + Text[AsciiCount] = 0; + + // Set the MLU entry. From here we can be tolerant to wrong types + if (!cmsMLUsetASCII(mlu, cmsNoLanguage, cmsNoCountry, Text)) goto Error; + _cmsFree(self ->ContextID, (void*) Text); + Text = NULL; + + // Skip Unicode code + if (SizeOfTag < 2* sizeof(cmsUInt32Number)) goto Done; + if (!_cmsReadUInt32Number(io, &UnicodeCode)) goto Done; + if (!_cmsReadUInt32Number(io, &UnicodeCount)) goto Done; + SizeOfTag -= 2* sizeof(cmsUInt32Number); + + if (SizeOfTag < UnicodeCount*sizeof(cmsUInt16Number)) goto Done; + + for (i=0; i < UnicodeCount; i++) { + if (!io ->Read(io, &Dummy, sizeof(cmsUInt16Number), 1)) goto Done; + } + SizeOfTag -= UnicodeCount*sizeof(cmsUInt16Number); + + // Skip ScriptCode code if present. Some buggy profiles does have less + // data that stricttly required. We need to skip it as this type may come + // embedded in other types. + + if (SizeOfTag >= sizeof(cmsUInt16Number) + sizeof(cmsUInt8Number) + 67) { + + if (!_cmsReadUInt16Number(io, &ScriptCodeCode)) goto Done; + if (!_cmsReadUInt8Number(io, &ScriptCodeCount)) goto Done; + + // Skip rest of tag + for (i=0; i < 67; i++) { + if (!io ->Read(io, &Dummy, sizeof(cmsUInt8Number), 1)) goto Error; + } + } + +Done: + + *nItems = 1; + return mlu; + +Error: + if (Text) _cmsFree(self ->ContextID, (void*) Text); + if (mlu) cmsMLUfree(mlu); + return NULL; +} + + +// This tag can come IN UNALIGNED SIZE. In order to prevent issues, we force zeros on description to align it +static +cmsBool Type_Text_Description_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsMLU* mlu = (cmsMLU*) Ptr; + char *Text = NULL; + wchar_t *Wide = NULL; + cmsUInt32Number len, len_aligned, len_filler_alignment; + cmsBool rc = FALSE; + char Filler[68]; + + // Used below for writting zeroes + memset(Filler, 0, sizeof(Filler)); + + // Get the len of string + len = cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, NULL, 0); + + // From ICC3.4: It has been found that textDescriptionType can contain misaligned data + //(see clause 4.1 for the definition of 'aligned'. Because the Unicode language + // code and Unicode count immediately follow the ASCII description, their + // alignment is not correct if the ASCII count is not a multiple of four. The + // ScriptCode code is misaligned when the ASCII count is odd. Profile reading and + // writing software must be written carefully in order to handle these alignment + // problems. + + // Compute an aligned size + len_aligned = _cmsALIGNLONG(len); + len_filler_alignment = len_aligned - len; + + // Null strings + if (len <= 0) { + + Text = (char*) _cmsDupMem(self ->ContextID, "", sizeof(char)); + Wide = (wchar_t*) _cmsDupMem(self ->ContextID, L"", sizeof(wchar_t)); + } + else { + // Create independent buffers + Text = (char*) _cmsCalloc(self ->ContextID, len, sizeof(char)); + if (Text == NULL) goto Error; + + Wide = (wchar_t*) _cmsCalloc(self ->ContextID, len, sizeof(wchar_t)); + if (Wide == NULL) goto Error; + + // Get both representations. + cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, Text, len * sizeof(char)); + cmsMLUgetWide(mlu, cmsNoLanguage, cmsNoCountry, Wide, len * sizeof(wchar_t)); + } + + // * cmsUInt32Number count; * Description length + // * cmsInt8Number desc[count] * NULL terminated ascii string + // * cmsUInt32Number ucLangCode; * UniCode language code + // * cmsUInt32Number ucCount; * UniCode description length + // * cmsInt16Number ucDesc[ucCount];* The UniCode description + // * cmsUInt16Number scCode; * ScriptCode code + // * cmsUInt8Number scCount; * ScriptCode count + // * cmsInt8Number scDesc[67]; * ScriptCode Description + + if (!_cmsWriteUInt32Number(io, len_aligned)) goto Error; + if (!io ->Write(io, len, Text)) goto Error; + if (!io ->Write(io, len_filler_alignment, Filler)) goto Error; + + if (!_cmsWriteUInt32Number(io, 0)) goto Error; // ucLanguageCode + + // This part is tricky: we need an aligned tag size, and the ScriptCode part + // takes 70 bytes, so we need 2 extra bytes to do the alignment + + if (!_cmsWriteUInt32Number(io, len_aligned+1)) goto Error; + + // Note that in some compilers sizeof(cmsUInt16Number) != sizeof(wchar_t) + if (!_cmsWriteWCharArray(io, len, Wide)) goto Error; + if (!_cmsWriteUInt16Array(io, len_filler_alignment+1, (cmsUInt16Number*) Filler)) goto Error; + + // ScriptCode Code & count (unused) + if (!_cmsWriteUInt16Number(io, 0)) goto Error; + if (!_cmsWriteUInt8Number(io, 0)) goto Error; + + if (!io ->Write(io, 67, Filler)) goto Error; + + rc = TRUE; + +Error: + if (Text) _cmsFree(self ->ContextID, Text); + if (Wide) _cmsFree(self ->ContextID, Wide); + + return rc; + + cmsUNUSED_PARAMETER(nItems); +} + + +static +void* Type_Text_Description_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsMLUdup((cmsMLU*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_Text_Description_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsMLU* mlu = (cmsMLU*) Ptr; + + cmsMLUfree(mlu); + return; + + cmsUNUSED_PARAMETER(self); +} + + +static +cmsTagTypeSignature DecideTextDescType(cmsFloat64Number ICCVersion, const void *Data) +{ + if (ICCVersion >= 4.0) + return cmsSigMultiLocalizedUnicodeType; + + return cmsSigTextDescriptionType; + + cmsUNUSED_PARAMETER(Data); +} + + +// ******************************************************************************** +// Type cmsSigCurveType +// ******************************************************************************** + +static +void *Type_Curve_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsUInt32Number Count; + cmsToneCurve* NewGamma; + + *nItems = 0; + if (!_cmsReadUInt32Number(io, &Count)) return NULL; + + switch (Count) { + + case 0: // Linear. + { + cmsFloat64Number SingleGamma = 1.0; + + NewGamma = cmsBuildParametricToneCurve(self ->ContextID, 1, &SingleGamma); + if (!NewGamma) return NULL; + *nItems = 1; + return NewGamma; + } + + case 1: // Specified as the exponent of gamma function + { + cmsUInt16Number SingleGammaFixed; + cmsFloat64Number SingleGamma; + + if (!_cmsReadUInt16Number(io, &SingleGammaFixed)) return NULL; + SingleGamma = _cms8Fixed8toDouble(SingleGammaFixed); + + *nItems = 1; + return cmsBuildParametricToneCurve(self ->ContextID, 1, &SingleGamma); + } + + default: // Curve + + if (Count > 0x7FFF) + return NULL; // This is to prevent bad guys for doing bad things + + NewGamma = cmsBuildTabulatedToneCurve16(self ->ContextID, Count, NULL); + if (!NewGamma) return NULL; + + if (!_cmsReadUInt16Array(io, Count, NewGamma -> Table16)) { + cmsFreeToneCurve(NewGamma); + return NULL; + } + + *nItems = 1; + return NewGamma; + } + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + +static +cmsBool Type_Curve_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsToneCurve* Curve = (cmsToneCurve*) Ptr; + + if (Curve ->nSegments == 1 && Curve ->Segments[0].Type == 1) { + + // Single gamma, preserve number + cmsUInt16Number SingleGammaFixed = _cmsDoubleTo8Fixed8(Curve ->Segments[0].Params[0]); + + if (!_cmsWriteUInt32Number(io, 1)) return FALSE; + if (!_cmsWriteUInt16Number(io, SingleGammaFixed)) return FALSE; + return TRUE; + + } + + if (!_cmsWriteUInt32Number(io, Curve ->nEntries)) return FALSE; + return _cmsWriteUInt16Array(io, Curve ->nEntries, Curve ->Table16); + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + + +static +void* Type_Curve_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsDupToneCurve((cmsToneCurve*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_Curve_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsToneCurve* gamma = (cmsToneCurve*) Ptr; + + cmsFreeToneCurve(gamma); + return; + + cmsUNUSED_PARAMETER(self); +} + + +// ******************************************************************************** +// Type cmsSigParametricCurveType +// ******************************************************************************** + + +// Decide which curve type to use on writting +static +cmsTagTypeSignature DecideCurveType(cmsFloat64Number ICCVersion, const void *Data) +{ + cmsToneCurve* Curve = (cmsToneCurve*) Data; + + if (ICCVersion < 4.0) return cmsSigCurveType; + if (Curve ->nSegments != 1) return cmsSigCurveType; // Only 1-segment curves can be saved as parametric + if (Curve ->Segments[0].Type < 0) return cmsSigCurveType; // Only non-inverted curves + if (Curve ->Segments[0].Type > 5) return cmsSigCurveType; // Only ICC parametric curves + + return cmsSigParametricCurveType; +} + +static +void *Type_ParametricCurve_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + static const int ParamsByType[] = { 1, 3, 4, 5, 7 }; + cmsFloat64Number Params[10]; + cmsUInt16Number Type; + int i, n; + cmsToneCurve* NewGamma; + + if (!_cmsReadUInt16Number(io, &Type)) return NULL; + if (!_cmsReadUInt16Number(io, NULL)) return NULL; // Reserved + + if (Type > 4) { + + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown parametric curve type '%d'", Type); + return NULL; + } + + memset(Params, 0, sizeof(Params)); + n = ParamsByType[Type]; + + for (i=0; i < n; i++) { + + if (!_cmsRead15Fixed16Number(io, &Params[i])) return NULL; + } + + NewGamma = cmsBuildParametricToneCurve(self ->ContextID, Type+1, Params); + + *nItems = 1; + return NewGamma; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + +static +cmsBool Type_ParametricCurve_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsToneCurve* Curve = (cmsToneCurve*) Ptr; + int i, nParams, typen; + static const int ParamsByType[] = { 0, 1, 3, 4, 5, 7 }; + + typen = Curve -> Segments[0].Type; + + if (Curve ->nSegments > 1 || typen < 1) { + + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Multisegment or Inverted parametric curves cannot be written"); + return FALSE; + } + + if (typen > 5) { + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported parametric curve"); + return FALSE; + } + + nParams = ParamsByType[typen]; + + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) (Curve ->Segments[0].Type - 1))) return FALSE; + if (!_cmsWriteUInt16Number(io, 0)) return FALSE; // Reserved + + for (i=0; i < nParams; i++) { + + if (!_cmsWrite15Fixed16Number(io, Curve -> Segments[0].Params[i])) return FALSE; + } + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); +} + +static +void* Type_ParametricCurve_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsDupToneCurve((cmsToneCurve*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_ParametricCurve_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsToneCurve* gamma = (cmsToneCurve*) Ptr; + + cmsFreeToneCurve(gamma); + return; + + cmsUNUSED_PARAMETER(self); +} + + +// ******************************************************************************** +// Type cmsSigDateTimeType +// ******************************************************************************** + +// A 12-byte value representation of the time and date, where the byte usage is assigned +// as specified in table 1. The actual values are encoded as 16-bit unsigned integers +// (uInt16Number - see 5.1.6). +// +// All the dateTimeNumber values in a profile shall be in Coordinated Universal Time +// (UTC, also known as GMT or ZULU Time). Profile writers are required to convert local +// time to UTC when setting these values. Programmes that display these values may show +// the dateTimeNumber as UTC, show the equivalent local time (at current locale), or +// display both UTC and local versions of the dateTimeNumber. + +static +void *Type_DateTime_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsDateTimeNumber timestamp; + struct tm * NewDateTime; + + *nItems = 0; + NewDateTime = (struct tm*) _cmsMalloc(self ->ContextID, sizeof(struct tm)); + if (NewDateTime == NULL) return NULL; + + if (io->Read(io, ×tamp, sizeof(cmsDateTimeNumber), 1) != 1) return NULL; + + _cmsDecodeDateTimeNumber(×tamp, NewDateTime); + + *nItems = 1; + return NewDateTime; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + +static +cmsBool Type_DateTime_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + struct tm * DateTime = (struct tm*) Ptr; + cmsDateTimeNumber timestamp; + + _cmsEncodeDateTimeNumber(×tamp, DateTime); + if (!io ->Write(io, sizeof(cmsDateTimeNumber), ×tamp)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + +static +void* Type_DateTime_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self ->ContextID, Ptr, sizeof(struct tm)); + + cmsUNUSED_PARAMETER(n); +} + +static +void Type_DateTime_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + + + +// ******************************************************************************** +// Type icMeasurementType +// ******************************************************************************** + +/* +The measurementType information refers only to the internal profile data and is +meant to provide profile makers an alternative to the default measurement +specifications. +*/ + +static +void *Type_Measurement_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsICCMeasurementConditions mc; + + + memset(&mc, 0, sizeof(mc)); + + if (!_cmsReadUInt32Number(io, &mc.Observer)) return NULL; + if (!_cmsReadXYZNumber(io, &mc.Backing)) return NULL; + if (!_cmsReadUInt32Number(io, &mc.Geometry)) return NULL; + if (!_cmsRead15Fixed16Number(io, &mc.Flare)) return NULL; + if (!_cmsReadUInt32Number(io, &mc.IlluminantType)) return NULL; + + *nItems = 1; + return _cmsDupMem(self ->ContextID, &mc, sizeof(cmsICCMeasurementConditions)); + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + +static +cmsBool Type_Measurement_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsICCMeasurementConditions* mc =(cmsICCMeasurementConditions*) Ptr; + + if (!_cmsWriteUInt32Number(io, mc->Observer)) return FALSE; + if (!_cmsWriteXYZNumber(io, &mc->Backing)) return FALSE; + if (!_cmsWriteUInt32Number(io, mc->Geometry)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, mc->Flare)) return FALSE; + if (!_cmsWriteUInt32Number(io, mc->IlluminantType)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + +static +void* Type_Measurement_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsICCMeasurementConditions)); + + cmsUNUSED_PARAMETER(n); +} + +static +void Type_Measurement_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + + +// ******************************************************************************** +// Type cmsSigMultiLocalizedUnicodeType +// ******************************************************************************** +// +// Do NOT trust SizeOfTag as there is an issue on the definition of profileSequenceDescTag. See the TechNote from +// Max Derhak and Rohit Patil about this: basically the size of the string table should be guessed and cannot be +// taken from the size of tag if this tag is embedded as part of bigger structures (profileSequenceDescTag, for instance) +// + +static +void *Type_MLU_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsMLU* mlu; + cmsUInt32Number Count, RecLen, NumOfWchar; + cmsUInt32Number SizeOfHeader; + cmsUInt32Number Len, Offset; + cmsUInt32Number i; + wchar_t* Block; + cmsUInt32Number BeginOfThisString, EndOfThisString, LargestPosition; + + *nItems = 0; + if (!_cmsReadUInt32Number(io, &Count)) return NULL; + if (!_cmsReadUInt32Number(io, &RecLen)) return NULL; + + if (RecLen != 12) { + + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "multiLocalizedUnicodeType of len != 12 is not supported."); + return NULL; + } + + mlu = cmsMLUalloc(self ->ContextID, Count); + if (mlu == NULL) return NULL; + + mlu ->UsedEntries = Count; + + SizeOfHeader = 12 * Count + sizeof(_cmsTagBase); + LargestPosition = 0; + + for (i=0; i < Count; i++) { + + if (!_cmsReadUInt16Number(io, &mlu ->Entries[i].Language)) goto Error; + if (!_cmsReadUInt16Number(io, &mlu ->Entries[i].Country)) goto Error; + + // Now deal with Len and offset. + if (!_cmsReadUInt32Number(io, &Len)) goto Error; + if (!_cmsReadUInt32Number(io, &Offset)) goto Error; + + // Check for overflow + if (Offset < (SizeOfHeader + 8)) goto Error; + + // True begin of the string + BeginOfThisString = Offset - SizeOfHeader - 8; + + // Ajust to wchar_t elements + mlu ->Entries[i].Len = (Len * sizeof(wchar_t)) / sizeof(cmsUInt16Number); + mlu ->Entries[i].StrW = (BeginOfThisString * sizeof(wchar_t)) / sizeof(cmsUInt16Number); + + // To guess maximum size, add offset + len + EndOfThisString = BeginOfThisString + Len; + if (EndOfThisString > LargestPosition) + LargestPosition = EndOfThisString; + } + + // Now read the remaining of tag and fill all strings. Substract the directory + SizeOfTag = (LargestPosition * sizeof(wchar_t)) / sizeof(cmsUInt16Number); + if (SizeOfTag == 0) + { + Block = NULL; + NumOfWchar = 0; + + } + else + { + Block = (wchar_t*) _cmsMalloc(self ->ContextID, SizeOfTag); + if (Block == NULL) goto Error; + NumOfWchar = SizeOfTag / sizeof(wchar_t); + if (!_cmsReadWCharArray(io, NumOfWchar, Block)) goto Error; + } + + mlu ->MemPool = Block; + mlu ->PoolSize = SizeOfTag; + mlu ->PoolUsed = SizeOfTag; + + *nItems = 1; + return (void*) mlu; + +Error: + if (mlu) cmsMLUfree(mlu); + return NULL; +} + +static +cmsBool Type_MLU_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsMLU* mlu =(cmsMLU*) Ptr; + cmsUInt32Number HeaderSize; + cmsUInt32Number Len, Offset; + int i; + + if (Ptr == NULL) { + + // Empty placeholder + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + if (!_cmsWriteUInt32Number(io, 12)) return FALSE; + return TRUE; + } + + if (!_cmsWriteUInt32Number(io, mlu ->UsedEntries)) return FALSE; + if (!_cmsWriteUInt32Number(io, 12)) return FALSE; + + HeaderSize = 12 * mlu ->UsedEntries + sizeof(_cmsTagBase); + + for (i=0; i < mlu ->UsedEntries; i++) { + + Len = mlu ->Entries[i].Len; + Offset = mlu ->Entries[i].StrW; + + Len = (Len * sizeof(cmsUInt16Number)) / sizeof(wchar_t); + Offset = (Offset * sizeof(cmsUInt16Number)) / sizeof(wchar_t) + HeaderSize + 8; + + if (!_cmsWriteUInt16Number(io, mlu ->Entries[i].Language)) return FALSE; + if (!_cmsWriteUInt16Number(io, mlu ->Entries[i].Country)) return FALSE; + if (!_cmsWriteUInt32Number(io, Len)) return FALSE; + if (!_cmsWriteUInt32Number(io, Offset)) return FALSE; + } + + if (!_cmsWriteWCharArray(io, mlu ->PoolUsed / sizeof(wchar_t), (wchar_t*) mlu ->MemPool)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + + +static +void* Type_MLU_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsMLUdup((cmsMLU*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_MLU_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsMLUfree((cmsMLU*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + + +// ******************************************************************************** +// Type cmsSigLut8Type +// ******************************************************************************** + +// Decide which LUT type to use on writting +static +cmsTagTypeSignature DecideLUTtypeA2B(cmsFloat64Number ICCVersion, const void *Data) +{ + cmsPipeline* Lut = (cmsPipeline*) Data; + + if (ICCVersion < 4.0) { + if (Lut ->SaveAs8Bits) return cmsSigLut8Type; + return cmsSigLut16Type; + } + else { + return cmsSigLutAtoBType; + } +} + +static +cmsTagTypeSignature DecideLUTtypeB2A(cmsFloat64Number ICCVersion, const void *Data) +{ + cmsPipeline* Lut = (cmsPipeline*) Data; + + if (ICCVersion < 4.0) { + if (Lut ->SaveAs8Bits) return cmsSigLut8Type; + return cmsSigLut16Type; + } + else { + return cmsSigLutBtoAType; + } +} + +/* +This structure represents a colour transform using tables of 8-bit precision. +This type contains four processing elements: a 3 by 3 matrix (which shall be +the identity matrix unless the input colour space is XYZ), a set of one dimensional +input tables, a multidimensional lookup table, and a set of one dimensional output +tables. Data is processed using these elements via the following sequence: +(matrix) -> (1d input tables) -> (multidimensional lookup table - CLUT) -> (1d output tables) + +Byte Position Field Length (bytes) Content Encoded as... +8 1 Number of Input Channels (i) uInt8Number +9 1 Number of Output Channels (o) uInt8Number +10 1 Number of CLUT grid points (identical for each side) (g) uInt8Number +11 1 Reserved for padding (fill with 00h) + +12..15 4 Encoded e00 parameter s15Fixed16Number +*/ + + +// Read 8 bit tables as gamma functions +static +cmsBool Read8bitTables(cmsContext ContextID, cmsIOHANDLER* io, cmsPipeline* lut, int nChannels) +{ + cmsUInt8Number* Temp = NULL; + int i, j; + cmsToneCurve* Tables[cmsMAXCHANNELS]; + + if (nChannels > cmsMAXCHANNELS) return FALSE; + if (nChannels <= 0) return FALSE; + + memset(Tables, 0, sizeof(Tables)); + + Temp = (cmsUInt8Number*) _cmsMalloc(ContextID, 256); + if (Temp == NULL) return FALSE; + + for (i=0; i < nChannels; i++) { + Tables[i] = cmsBuildTabulatedToneCurve16(ContextID, 256, NULL); + if (Tables[i] == NULL) goto Error; + } + + for (i=0; i < nChannels; i++) { + + if (io ->Read(io, Temp, 256, 1) != 1) goto Error; + + for (j=0; j < 256; j++) + Tables[i]->Table16[j] = (cmsUInt16Number) FROM_8_TO_16(Temp[j]); + } + + _cmsFree(ContextID, Temp); + Temp = NULL; + + if (!cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, nChannels, Tables))) + goto Error; + + for (i=0; i < nChannels; i++) + cmsFreeToneCurve(Tables[i]); + + return TRUE; + +Error: + for (i=0; i < nChannels; i++) { + if (Tables[i]) cmsFreeToneCurve(Tables[i]); + } + + if (Temp) _cmsFree(ContextID, Temp); + return FALSE; +} + + +static +cmsBool Write8bitTables(cmsContext ContextID, cmsIOHANDLER* io, cmsUInt32Number n, _cmsStageToneCurvesData* Tables) +{ + int j; + cmsUInt32Number i; + cmsUInt8Number val; + + for (i=0; i < n; i++) { + + if (Tables) { + + // Usual case of identity curves + if ((Tables ->TheCurves[i]->nEntries == 2) && + (Tables->TheCurves[i]->Table16[0] == 0) && + (Tables->TheCurves[i]->Table16[1] == 65535)) { + + for (j=0; j < 256; j++) { + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) j)) return FALSE; + } + } + else + if (Tables ->TheCurves[i]->nEntries != 256) { + cmsSignalError(ContextID, cmsERROR_RANGE, "LUT8 needs 256 entries on prelinearization"); + return FALSE; + } + else + for (j=0; j < 256; j++) { + + if (Tables != NULL) + val = (cmsUInt8Number) FROM_16_TO_8(Tables->TheCurves[i]->Table16[j]); + else + val = (cmsUInt8Number) j; + + if (!_cmsWriteUInt8Number(io, val)) return FALSE; + } + } + } + return TRUE; +} + + +// Check overflow +static +cmsUInt32Number uipow(cmsUInt32Number n, cmsUInt32Number a, cmsUInt32Number b) +{ + cmsUInt32Number rv = 1, rc; + + if (a == 0) return 0; + if (n == 0) return 0; + + for (; b > 0; b--) { + + rv *= a; + + // Check for overflow + if (rv > UINT_MAX / a) return (cmsUInt32Number) -1; + + } + + rc = rv * n; + + if (rv != rc / n) return (cmsUInt32Number) -1; + return rc; +} + + +// That will create a MPE LUT with Matrix, pre tables, CLUT and post tables. +// 8 bit lut may be scaled easely to v4 PCS, but we need also to properly adjust +// PCS on BToAxx tags and AtoB if abstract. We need to fix input direction. + +static +void *Type_LUT8_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsUInt8Number InputChannels, OutputChannels, CLUTpoints; + cmsUInt8Number* Temp = NULL; + cmsPipeline* NewLUT = NULL; + cmsUInt32Number nTabSize, i; + cmsFloat64Number Matrix[3*3]; + + *nItems = 0; + + if (!_cmsReadUInt8Number(io, &InputChannels)) goto Error; + if (!_cmsReadUInt8Number(io, &OutputChannels)) goto Error; + if (!_cmsReadUInt8Number(io, &CLUTpoints)) goto Error; + + if (CLUTpoints == 1) goto Error; // Impossible value, 0 for no CLUT and then 2 at least + + // Padding + if (!_cmsReadUInt8Number(io, NULL)) goto Error; + + // Do some checking + if (InputChannels == 0 || InputChannels > cmsMAXCHANNELS) goto Error; + if (OutputChannels == 0 || OutputChannels > cmsMAXCHANNELS) goto Error; + + // Allocates an empty Pipeline + NewLUT = cmsPipelineAlloc(self ->ContextID, InputChannels, OutputChannels); + if (NewLUT == NULL) goto Error; + + // Read the Matrix + if (!_cmsRead15Fixed16Number(io, &Matrix[0])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[1])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[2])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[3])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[4])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[5])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[6])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[7])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[8])) goto Error; + + + // Only operates if not identity... + if ((InputChannels == 3) && !_cmsMAT3isIdentity((cmsMAT3*) Matrix)) { + + if (!cmsPipelineInsertStage(NewLUT, cmsAT_BEGIN, cmsStageAllocMatrix(self ->ContextID, 3, 3, Matrix, NULL))) + goto Error; + } + + // Get input tables + if (!Read8bitTables(self ->ContextID, io, NewLUT, InputChannels)) goto Error; + + // Get 3D CLUT. Check the overflow.... + nTabSize = uipow(OutputChannels, CLUTpoints, InputChannels); + if (nTabSize == (cmsUInt32Number) -1) goto Error; + if (nTabSize > 0) { + + cmsUInt16Number *PtrW, *T; + + PtrW = T = (cmsUInt16Number*) _cmsCalloc(self ->ContextID, nTabSize, sizeof(cmsUInt16Number)); + if (T == NULL) goto Error; + + Temp = (cmsUInt8Number*) _cmsMalloc(self ->ContextID, nTabSize); + if (Temp == NULL) { + _cmsFree(self ->ContextID, T); + goto Error; + } + + if (io ->Read(io, Temp, nTabSize, 1) != 1) { + _cmsFree(self ->ContextID, T); + _cmsFree(self ->ContextID, Temp); + goto Error; + } + + for (i = 0; i < nTabSize; i++) { + + *PtrW++ = FROM_8_TO_16(Temp[i]); + } + _cmsFree(self ->ContextID, Temp); + Temp = NULL; + + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, cmsStageAllocCLut16bit(self ->ContextID, CLUTpoints, InputChannels, OutputChannels, T))) + goto Error; + _cmsFree(self ->ContextID, T); + } + + + // Get output tables + if (!Read8bitTables(self ->ContextID, io, NewLUT, OutputChannels)) goto Error; + + *nItems = 1; + return NewLUT; + +Error: + if (NewLUT != NULL) cmsPipelineFree(NewLUT); + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +// We only allow a specific MPE structure: Matrix plus prelin, plus clut, plus post-lin. +static +cmsBool Type_LUT8_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsUInt32Number j, nTabSize; + cmsUInt8Number val; + cmsPipeline* NewLUT = (cmsPipeline*) Ptr; + cmsStage* mpe; + _cmsStageToneCurvesData* PreMPE = NULL, *PostMPE = NULL; + _cmsStageMatrixData* MatMPE = NULL; + _cmsStageCLutData* clut = NULL; + int clutPoints; + + // Disassemble the LUT into components. + mpe = NewLUT -> Elements; + if (mpe ->Type == cmsSigMatrixElemType) { + + MatMPE = (_cmsStageMatrixData*) mpe ->Data; + mpe = mpe -> Next; + } + + if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) { + PreMPE = (_cmsStageToneCurvesData*) mpe ->Data; + mpe = mpe -> Next; + } + + if (mpe != NULL && mpe ->Type == cmsSigCLutElemType) { + clut = (_cmsStageCLutData*) mpe -> Data; + mpe = mpe ->Next; + } + + if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) { + PostMPE = (_cmsStageToneCurvesData*) mpe ->Data; + mpe = mpe -> Next; + } + + // That should be all + if (mpe != NULL) { + cmsSignalError(mpe->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT is not suitable to be saved as LUT8"); + return FALSE; + } + + + if (clut == NULL) + clutPoints = 0; + else + clutPoints = clut->Params->nSamples[0]; + + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) NewLUT ->InputChannels)) return FALSE; + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) NewLUT ->OutputChannels)) return FALSE; + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) clutPoints)) return FALSE; + if (!_cmsWriteUInt8Number(io, 0)) return FALSE; // Padding + + + if (MatMPE != NULL) { + + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[0])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[1])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[2])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[3])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[4])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[5])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[6])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[7])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[8])) return FALSE; + + } + else { + + if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE; + } + + // The prelinearization table + if (!Write8bitTables(self ->ContextID, io, NewLUT ->InputChannels, PreMPE)) return FALSE; + + nTabSize = uipow(NewLUT->OutputChannels, clutPoints, NewLUT ->InputChannels); + if (nTabSize == (cmsUInt32Number) -1) return FALSE; + if (nTabSize > 0) { + + // The 3D CLUT. + if (clut != NULL) { + + for (j=0; j < nTabSize; j++) { + + val = (cmsUInt8Number) FROM_16_TO_8(clut ->Tab.T[j]); + if (!_cmsWriteUInt8Number(io, val)) return FALSE; + } + } + } + + // The postlinearization table + if (!Write8bitTables(self ->ContextID, io, NewLUT ->OutputChannels, PostMPE)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); +} + + +static +void* Type_LUT8_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsPipelineDup((cmsPipeline*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_LUT8_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsPipelineFree((cmsPipeline*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + +// ******************************************************************************** +// Type cmsSigLut16Type +// ******************************************************************************** + +// Read 16 bit tables as gamma functions +static +cmsBool Read16bitTables(cmsContext ContextID, cmsIOHANDLER* io, cmsPipeline* lut, int nChannels, int nEntries) +{ + int i; + cmsToneCurve* Tables[cmsMAXCHANNELS]; + + // Maybe an empty table? (this is a lcms extension) + if (nEntries <= 0) return TRUE; + + // Check for malicious profiles + if (nEntries < 2) return FALSE; + if (nChannels > cmsMAXCHANNELS) return FALSE; + + // Init table to zero + memset(Tables, 0, sizeof(Tables)); + + for (i=0; i < nChannels; i++) { + + Tables[i] = cmsBuildTabulatedToneCurve16(ContextID, nEntries, NULL); + if (Tables[i] == NULL) goto Error; + + if (!_cmsReadUInt16Array(io, nEntries, Tables[i]->Table16)) goto Error; + } + + + // Add the table (which may certainly be an identity, but this is up to the optimizer, not the reading code) + if (!cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, nChannels, Tables))) + goto Error; + + for (i=0; i < nChannels; i++) + cmsFreeToneCurve(Tables[i]); + + return TRUE; + +Error: + for (i=0; i < nChannels; i++) { + if (Tables[i]) cmsFreeToneCurve(Tables[i]); + } + + return FALSE; +} + +static +cmsBool Write16bitTables(cmsContext ContextID, cmsIOHANDLER* io, _cmsStageToneCurvesData* Tables) +{ + int j; + cmsUInt32Number i; + cmsUInt16Number val; + int nEntries; + + _cmsAssert(Tables != NULL); + + nEntries = Tables->TheCurves[0]->nEntries; + + for (i=0; i < Tables ->nCurves; i++) { + + for (j=0; j < nEntries; j++) { + + val = Tables->TheCurves[i]->Table16[j]; + if (!_cmsWriteUInt16Number(io, val)) return FALSE; + } + } + return TRUE; + + cmsUNUSED_PARAMETER(ContextID); +} + +static +void *Type_LUT16_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsUInt8Number InputChannels, OutputChannels, CLUTpoints; + cmsPipeline* NewLUT = NULL; + cmsUInt32Number nTabSize; + cmsFloat64Number Matrix[3*3]; + cmsUInt16Number InputEntries, OutputEntries; + + *nItems = 0; + + if (!_cmsReadUInt8Number(io, &InputChannels)) return NULL; + if (!_cmsReadUInt8Number(io, &OutputChannels)) return NULL; + if (!_cmsReadUInt8Number(io, &CLUTpoints)) return NULL; // 255 maximum + + // Padding + if (!_cmsReadUInt8Number(io, NULL)) return NULL; + + // Do some checking + if (InputChannels == 0 || InputChannels > cmsMAXCHANNELS) goto Error; + if (OutputChannels == 0 || OutputChannels > cmsMAXCHANNELS) goto Error; + + // Allocates an empty LUT + NewLUT = cmsPipelineAlloc(self ->ContextID, InputChannels, OutputChannels); + if (NewLUT == NULL) goto Error; + + // Read the Matrix + if (!_cmsRead15Fixed16Number(io, &Matrix[0])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[1])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[2])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[3])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[4])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[5])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[6])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[7])) goto Error; + if (!_cmsRead15Fixed16Number(io, &Matrix[8])) goto Error; + + + // Only operates on 3 channels + if ((InputChannels == 3) && !_cmsMAT3isIdentity((cmsMAT3*) Matrix)) { + + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, cmsStageAllocMatrix(self ->ContextID, 3, 3, Matrix, NULL))) + goto Error; + } + + if (!_cmsReadUInt16Number(io, &InputEntries)) goto Error; + if (!_cmsReadUInt16Number(io, &OutputEntries)) goto Error; + + if (InputEntries > 0x7FFF || OutputEntries > 0x7FFF) goto Error; + if (CLUTpoints == 1) goto Error; // Impossible value, 0 for no CLUT and then 2 at least + + // Get input tables + if (!Read16bitTables(self ->ContextID, io, NewLUT, InputChannels, InputEntries)) goto Error; + + // Get 3D CLUT + nTabSize = uipow(OutputChannels, CLUTpoints, InputChannels); + if (nTabSize == (cmsUInt32Number) -1) goto Error; + if (nTabSize > 0) { + + cmsUInt16Number *T; + + T = (cmsUInt16Number*) _cmsCalloc(self ->ContextID, nTabSize, sizeof(cmsUInt16Number)); + if (T == NULL) goto Error; + + if (!_cmsReadUInt16Array(io, nTabSize, T)) { + _cmsFree(self ->ContextID, T); + goto Error; + } + + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, cmsStageAllocCLut16bit(self ->ContextID, CLUTpoints, InputChannels, OutputChannels, T))) { + _cmsFree(self ->ContextID, T); + goto Error; + } + _cmsFree(self ->ContextID, T); + } + + + // Get output tables + if (!Read16bitTables(self ->ContextID, io, NewLUT, OutputChannels, OutputEntries)) goto Error; + + *nItems = 1; + return NewLUT; + +Error: + if (NewLUT != NULL) cmsPipelineFree(NewLUT); + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +// We only allow some specific MPE structures: Matrix plus prelin, plus clut, plus post-lin. +// Some empty defaults are created for missing parts + +static +cmsBool Type_LUT16_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsUInt32Number nTabSize; + cmsPipeline* NewLUT = (cmsPipeline*) Ptr; + cmsStage* mpe; + _cmsStageToneCurvesData* PreMPE = NULL, *PostMPE = NULL; + _cmsStageMatrixData* MatMPE = NULL; + _cmsStageCLutData* clut = NULL; + int i, InputChannels, OutputChannels, clutPoints; + + // Disassemble the LUT into components. + mpe = NewLUT -> Elements; + if (mpe != NULL && mpe ->Type == cmsSigMatrixElemType) { + + MatMPE = (_cmsStageMatrixData*) mpe ->Data; + mpe = mpe -> Next; + } + + + if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) { + PreMPE = (_cmsStageToneCurvesData*) mpe ->Data; + mpe = mpe -> Next; + } + + if (mpe != NULL && mpe ->Type == cmsSigCLutElemType) { + clut = (_cmsStageCLutData*) mpe -> Data; + mpe = mpe ->Next; + } + + if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) { + PostMPE = (_cmsStageToneCurvesData*) mpe ->Data; + mpe = mpe -> Next; + } + + // That should be all + if (mpe != NULL) { + cmsSignalError(mpe->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT is not suitable to be saved as LUT16"); + return FALSE; + } + + InputChannels = cmsPipelineInputChannels(NewLUT); + OutputChannels = cmsPipelineOutputChannels(NewLUT); + + if (clut == NULL) + clutPoints = 0; + else + clutPoints = clut->Params->nSamples[0]; + + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) InputChannels)) return FALSE; + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) OutputChannels)) return FALSE; + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) clutPoints)) return FALSE; + if (!_cmsWriteUInt8Number(io, 0)) return FALSE; // Padding + + + if (MatMPE != NULL) { + + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[0])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[1])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[2])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[3])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[4])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[5])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[6])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[7])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[8])) return FALSE; + } + else { + + if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE; + } + + + if (PreMPE != NULL) { + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) PreMPE ->TheCurves[0]->nEntries)) return FALSE; + } else { + if (!_cmsWriteUInt16Number(io, 2)) return FALSE; + } + + if (PostMPE != NULL) { + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) PostMPE ->TheCurves[0]->nEntries)) return FALSE; + } else { + if (!_cmsWriteUInt16Number(io, 2)) return FALSE; + + } + + // The prelinearization table + + if (PreMPE != NULL) { + if (!Write16bitTables(self ->ContextID, io, PreMPE)) return FALSE; + } + else { + for (i=0; i < InputChannels; i++) { + + if (!_cmsWriteUInt16Number(io, 0)) return FALSE; + if (!_cmsWriteUInt16Number(io, 0xffff)) return FALSE; + } + } + + nTabSize = uipow(OutputChannels, clutPoints, InputChannels); + if (nTabSize == (cmsUInt32Number) -1) return FALSE; + if (nTabSize > 0) { + // The 3D CLUT. + if (clut != NULL) { + if (!_cmsWriteUInt16Array(io, nTabSize, clut->Tab.T)) return FALSE; + } + } + + // The postlinearization table + if (PostMPE != NULL) { + if (!Write16bitTables(self ->ContextID, io, PostMPE)) return FALSE; + } + else { + for (i=0; i < OutputChannels; i++) { + + if (!_cmsWriteUInt16Number(io, 0)) return FALSE; + if (!_cmsWriteUInt16Number(io, 0xffff)) return FALSE; + } + } + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); +} + +static +void* Type_LUT16_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsPipelineDup((cmsPipeline*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_LUT16_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsPipelineFree((cmsPipeline*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + + +// ******************************************************************************** +// Type cmsSigLutAToBType +// ******************************************************************************** + + +// V4 stuff. Read matrix for LutAtoB and LutBtoA + +static +cmsStage* ReadMatrix(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number Offset) +{ + cmsFloat64Number dMat[3*3]; + cmsFloat64Number dOff[3]; + cmsStage* Mat; + + // Go to address + if (!io -> Seek(io, Offset)) return NULL; + + // Read the Matrix + if (!_cmsRead15Fixed16Number(io, &dMat[0])) return NULL; + if (!_cmsRead15Fixed16Number(io, &dMat[1])) return NULL; + if (!_cmsRead15Fixed16Number(io, &dMat[2])) return NULL; + if (!_cmsRead15Fixed16Number(io, &dMat[3])) return NULL; + if (!_cmsRead15Fixed16Number(io, &dMat[4])) return NULL; + if (!_cmsRead15Fixed16Number(io, &dMat[5])) return NULL; + if (!_cmsRead15Fixed16Number(io, &dMat[6])) return NULL; + if (!_cmsRead15Fixed16Number(io, &dMat[7])) return NULL; + if (!_cmsRead15Fixed16Number(io, &dMat[8])) return NULL; + + if (!_cmsRead15Fixed16Number(io, &dOff[0])) return NULL; + if (!_cmsRead15Fixed16Number(io, &dOff[1])) return NULL; + if (!_cmsRead15Fixed16Number(io, &dOff[2])) return NULL; + + Mat = cmsStageAllocMatrix(self ->ContextID, 3, 3, dMat, dOff); + + return Mat; +} + + + + +// V4 stuff. Read CLUT part for LutAtoB and LutBtoA + +static +cmsStage* ReadCLUT(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number Offset, int InputChannels, int OutputChannels) +{ + cmsUInt8Number gridPoints8[cmsMAXCHANNELS]; // Number of grid points in each dimension. + cmsUInt32Number GridPoints[cmsMAXCHANNELS], i; + cmsUInt8Number Precision; + cmsStage* CLUT; + _cmsStageCLutData* Data; + + if (!io -> Seek(io, Offset)) return NULL; + if (io -> Read(io, gridPoints8, cmsMAXCHANNELS, 1) != 1) return NULL; + + + for (i=0; i < cmsMAXCHANNELS; i++) { + + if (gridPoints8[i] == 1) return NULL; // Impossible value, 0 for no CLUT and then 2 at least + GridPoints[i] = gridPoints8[i]; + } + + if (!_cmsReadUInt8Number(io, &Precision)) return NULL; + + if (!_cmsReadUInt8Number(io, NULL)) return NULL; + if (!_cmsReadUInt8Number(io, NULL)) return NULL; + if (!_cmsReadUInt8Number(io, NULL)) return NULL; + + CLUT = cmsStageAllocCLut16bitGranular(self ->ContextID, GridPoints, InputChannels, OutputChannels, NULL); + if (CLUT == NULL) return NULL; + + Data = (_cmsStageCLutData*) CLUT ->Data; + + // Precision can be 1 or 2 bytes + if (Precision == 1) { + + cmsUInt8Number v; + + for (i=0; i < Data ->nEntries; i++) { + + if (io ->Read(io, &v, sizeof(cmsUInt8Number), 1) != 1) { + cmsStageFree(CLUT); + return NULL; + } + Data ->Tab.T[i] = FROM_8_TO_16(v); + } + + } + else + if (Precision == 2) { + + if (!_cmsReadUInt16Array(io, Data->nEntries, Data ->Tab.T)) { + cmsStageFree(CLUT); + return NULL; + } + } + else { + cmsStageFree(CLUT); + cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown precision of '%d'", Precision); + return NULL; + } + + return CLUT; +} + +static +cmsToneCurve* ReadEmbeddedCurve(struct _cms_typehandler_struct* self, cmsIOHANDLER* io) +{ + cmsTagTypeSignature BaseType; + cmsUInt32Number nItems; + + BaseType = _cmsReadTypeBase(io); + switch (BaseType) { + + case cmsSigCurveType: + return (cmsToneCurve*) Type_Curve_Read(self, io, &nItems, 0); + + case cmsSigParametricCurveType: + return (cmsToneCurve*) Type_ParametricCurve_Read(self, io, &nItems, 0); + + default: + { + char String[5]; + + _cmsTagSignature2String(String, (cmsTagSignature) BaseType); + cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown curve type '%s'", String); + } + return NULL; + } +} + + +// Read a set of curves from specific offset +static +cmsStage* ReadSetOfCurves(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number Offset, cmsUInt32Number nCurves) +{ + cmsToneCurve* Curves[cmsMAXCHANNELS]; + cmsUInt32Number i; + cmsStage* Lin = NULL; + + if (nCurves > cmsMAXCHANNELS) return FALSE; + + if (!io -> Seek(io, Offset)) return FALSE; + + for (i=0; i < nCurves; i++) + Curves[i] = NULL; + + for (i=0; i < nCurves; i++) { + + Curves[i] = ReadEmbeddedCurve(self, io); + if (Curves[i] == NULL) goto Error; + if (!_cmsReadAlignment(io)) goto Error; + + } + + Lin = cmsStageAllocToneCurves(self ->ContextID, nCurves, Curves); + +Error: + for (i=0; i < nCurves; i++) + cmsFreeToneCurve(Curves[i]); + + return Lin; +} + + +// LutAtoB type + +// This structure represents a colour transform. The type contains up to five processing +// elements which are stored in the AtoBTag tag in the following order: a set of one +// dimensional curves, a 3 by 3 matrix with offset terms, a set of one dimensional curves, +// a multidimensional lookup table, and a set of one dimensional output curves. +// Data are processed using these elements via the following sequence: +// +//("A" curves) -> (multidimensional lookup table - CLUT) -> ("M" curves) -> (matrix) -> ("B" curves). +// +/* +It is possible to use any or all of these processing elements. At least one processing element +must be included.Only the following combinations are allowed: + +B +M - Matrix - B +A - CLUT - B +A - CLUT - M - Matrix - B + +*/ + +static +void* Type_LUTA2B_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsUInt32Number BaseOffset; + cmsUInt8Number inputChan; // Number of input channels + cmsUInt8Number outputChan; // Number of output channels + cmsUInt32Number offsetB; // Offset to first "B" curve + cmsUInt32Number offsetMat; // Offset to matrix + cmsUInt32Number offsetM; // Offset to first "M" curve + cmsUInt32Number offsetC; // Offset to CLUT + cmsUInt32Number offsetA; // Offset to first "A" curve + cmsPipeline* NewLUT = NULL; + + + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + if (!_cmsReadUInt8Number(io, &inputChan)) return NULL; + if (!_cmsReadUInt8Number(io, &outputChan)) return NULL; + + if (!_cmsReadUInt16Number(io, NULL)) return NULL; + + if (!_cmsReadUInt32Number(io, &offsetB)) return NULL; + if (!_cmsReadUInt32Number(io, &offsetMat)) return NULL; + if (!_cmsReadUInt32Number(io, &offsetM)) return NULL; + if (!_cmsReadUInt32Number(io, &offsetC)) return NULL; + if (!_cmsReadUInt32Number(io, &offsetA)) return NULL; + + if (inputChan == 0 || inputChan >= cmsMAXCHANNELS) return NULL; + if (outputChan == 0 || outputChan >= cmsMAXCHANNELS) return NULL; + + // Allocates an empty LUT + NewLUT = cmsPipelineAlloc(self ->ContextID, inputChan, outputChan); + if (NewLUT == NULL) return NULL; + + if (offsetA!= 0) { + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetA, inputChan))) + goto Error; + } + + if (offsetC != 0) { + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadCLUT(self, io, BaseOffset + offsetC, inputChan, outputChan))) + goto Error; + } + + if (offsetM != 0) { + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetM, outputChan))) + goto Error; + } + + if (offsetMat != 0) { + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadMatrix(self, io, BaseOffset + offsetMat))) + goto Error; + } + + if (offsetB != 0) { + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetB, outputChan))) + goto Error; + } + + *nItems = 1; + return NewLUT; +Error: + cmsPipelineFree(NewLUT); + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +// Write a set of curves +static +cmsBool WriteMatrix(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsStage* mpe) +{ + _cmsStageMatrixData* m = (_cmsStageMatrixData*) mpe -> Data; + + // Write the Matrix + if (!_cmsWrite15Fixed16Number(io, m -> Double[0])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, m -> Double[1])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, m -> Double[2])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, m -> Double[3])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, m -> Double[4])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, m -> Double[5])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, m -> Double[6])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, m -> Double[7])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, m -> Double[8])) return FALSE; + + if (m ->Offset != NULL) { + + if (!_cmsWrite15Fixed16Number(io, m -> Offset[0])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, m -> Offset[1])) return FALSE; + if (!_cmsWrite15Fixed16Number(io, m -> Offset[2])) return FALSE; + } + else { + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; + + } + + + return TRUE; + + cmsUNUSED_PARAMETER(self); +} + + +// Write a set of curves +static +cmsBool WriteSetOfCurves(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsTagTypeSignature Type, cmsStage* mpe) +{ + cmsUInt32Number i, n; + cmsTagTypeSignature CurrentType; + cmsToneCurve** Curves; + + + n = cmsStageOutputChannels(mpe); + Curves = _cmsStageGetPtrToCurveSet(mpe); + + for (i=0; i < n; i++) { + + // If this is a table-based curve, use curve type even on V4 + CurrentType = Type; + + if ((Curves[i] ->nSegments == 0)|| + ((Curves[i]->nSegments == 2) && (Curves[i] ->Segments[1].Type == 0)) ) + CurrentType = cmsSigCurveType; + else + if (Curves[i] ->Segments[0].Type < 0) + CurrentType = cmsSigCurveType; + + if (!_cmsWriteTypeBase(io, CurrentType)) return FALSE; + + switch (CurrentType) { + + case cmsSigCurveType: + if (!Type_Curve_Write(self, io, Curves[i], 1)) return FALSE; + break; + + case cmsSigParametricCurveType: + if (!Type_ParametricCurve_Write(self, io, Curves[i], 1)) return FALSE; + break; + + default: + { + char String[5]; + + _cmsTagSignature2String(String, (cmsTagSignature) Type); + cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown curve type '%s'", String); + } + return FALSE; + } + + if (!_cmsWriteAlignment(io)) return FALSE; + } + + + return TRUE; +} + + +static +cmsBool WriteCLUT(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt8Number Precision, cmsStage* mpe) +{ + cmsUInt8Number gridPoints[cmsMAXCHANNELS]; // Number of grid points in each dimension. + cmsUInt32Number i; + _cmsStageCLutData* CLUT = ( _cmsStageCLutData*) mpe -> Data; + + if (CLUT ->HasFloatValues) { + cmsSignalError(self ->ContextID, cmsERROR_NOT_SUITABLE, "Cannot save floating point data, CLUT are 8 or 16 bit only"); + return FALSE; + } + + memset(gridPoints, 0, sizeof(gridPoints)); + for (i=0; i < (cmsUInt32Number) CLUT ->Params ->nInputs; i++) + gridPoints[i] = (cmsUInt8Number) CLUT ->Params ->nSamples[i]; + + if (!io -> Write(io, cmsMAXCHANNELS*sizeof(cmsUInt8Number), gridPoints)) return FALSE; + + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) Precision)) return FALSE; + if (!_cmsWriteUInt8Number(io, 0)) return FALSE; + if (!_cmsWriteUInt8Number(io, 0)) return FALSE; + if (!_cmsWriteUInt8Number(io, 0)) return FALSE; + + // Precision can be 1 or 2 bytes + if (Precision == 1) { + + for (i=0; i < CLUT->nEntries; i++) { + + if (!_cmsWriteUInt8Number(io, FROM_16_TO_8(CLUT->Tab.T[i]))) return FALSE; + } + } + else + if (Precision == 2) { + + if (!_cmsWriteUInt16Array(io, CLUT->nEntries, CLUT ->Tab.T)) return FALSE; + } + else { + cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown precision of '%d'", Precision); + return FALSE; + } + + if (!_cmsWriteAlignment(io)) return FALSE; + + return TRUE; +} + + + + +static +cmsBool Type_LUTA2B_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsPipeline* Lut = (cmsPipeline*) Ptr; + int inputChan, outputChan; + cmsStage *A = NULL, *B = NULL, *M = NULL; + cmsStage * Matrix = NULL; + cmsStage * CLUT = NULL; + cmsUInt32Number offsetB = 0, offsetMat = 0, offsetM = 0, offsetC = 0, offsetA = 0; + cmsUInt32Number BaseOffset, DirectoryPos, CurrentPos; + + // Get the base for all offsets + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + if (Lut ->Elements != NULL) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 1, cmsSigCurveSetElemType, &B)) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, &M, &Matrix, &B)) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, &A, &CLUT, &B)) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 5, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, + cmsSigMatrixElemType, cmsSigCurveSetElemType, &A, &CLUT, &M, &Matrix, &B)) { + + cmsSignalError(self->ContextID, cmsERROR_NOT_SUITABLE, "LUT is not suitable to be saved as LutAToB"); + return FALSE; + } + + // Get input, output channels + inputChan = cmsPipelineInputChannels(Lut); + outputChan = cmsPipelineOutputChannels(Lut); + + // Write channel count + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) inputChan)) return FALSE; + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) outputChan)) return FALSE; + if (!_cmsWriteUInt16Number(io, 0)) return FALSE; + + // Keep directory to be filled latter + DirectoryPos = io ->Tell(io); + + // Write the directory + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + + if (A != NULL) { + + offsetA = io ->Tell(io) - BaseOffset; + if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, A)) return FALSE; + } + + if (CLUT != NULL) { + offsetC = io ->Tell(io) - BaseOffset; + if (!WriteCLUT(self, io, Lut ->SaveAs8Bits ? 1 : 2, CLUT)) return FALSE; + + } + if (M != NULL) { + + offsetM = io ->Tell(io) - BaseOffset; + if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, M)) return FALSE; + } + + if (Matrix != NULL) { + offsetMat = io ->Tell(io) - BaseOffset; + if (!WriteMatrix(self, io, Matrix)) return FALSE; + } + + if (B != NULL) { + + offsetB = io ->Tell(io) - BaseOffset; + if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, B)) return FALSE; + } + + CurrentPos = io ->Tell(io); + + if (!io ->Seek(io, DirectoryPos)) return FALSE; + + if (!_cmsWriteUInt32Number(io, offsetB)) return FALSE; + if (!_cmsWriteUInt32Number(io, offsetMat)) return FALSE; + if (!_cmsWriteUInt32Number(io, offsetM)) return FALSE; + if (!_cmsWriteUInt32Number(io, offsetC)) return FALSE; + if (!_cmsWriteUInt32Number(io, offsetA)) return FALSE; + + if (!io ->Seek(io, CurrentPos)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); +} + + +static +void* Type_LUTA2B_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsPipelineDup((cmsPipeline*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_LUTA2B_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsPipelineFree((cmsPipeline*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + + +// LutBToA type + +static +void* Type_LUTB2A_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsUInt8Number inputChan; // Number of input channels + cmsUInt8Number outputChan; // Number of output channels + cmsUInt32Number BaseOffset; // Actual position in file + cmsUInt32Number offsetB; // Offset to first "B" curve + cmsUInt32Number offsetMat; // Offset to matrix + cmsUInt32Number offsetM; // Offset to first "M" curve + cmsUInt32Number offsetC; // Offset to CLUT + cmsUInt32Number offsetA; // Offset to first "A" curve + cmsPipeline* NewLUT = NULL; + + + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + if (!_cmsReadUInt8Number(io, &inputChan)) return NULL; + if (!_cmsReadUInt8Number(io, &outputChan)) return NULL; + + if (inputChan == 0 || inputChan >= cmsMAXCHANNELS) return NULL; + if (outputChan == 0 || outputChan >= cmsMAXCHANNELS) return NULL; + + // Padding + if (!_cmsReadUInt16Number(io, NULL)) return NULL; + + if (!_cmsReadUInt32Number(io, &offsetB)) return NULL; + if (!_cmsReadUInt32Number(io, &offsetMat)) return NULL; + if (!_cmsReadUInt32Number(io, &offsetM)) return NULL; + if (!_cmsReadUInt32Number(io, &offsetC)) return NULL; + if (!_cmsReadUInt32Number(io, &offsetA)) return NULL; + + // Allocates an empty LUT + NewLUT = cmsPipelineAlloc(self ->ContextID, inputChan, outputChan); + if (NewLUT == NULL) return NULL; + + if (offsetB != 0) { + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetB, inputChan))) + goto Error; + } + + if (offsetMat != 0) { + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadMatrix(self, io, BaseOffset + offsetMat))) + goto Error; + } + + if (offsetM != 0) { + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetM, inputChan))) + goto Error; + } + + if (offsetC != 0) { + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadCLUT(self, io, BaseOffset + offsetC, inputChan, outputChan))) + goto Error; + } + + if (offsetA!= 0) { + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetA, outputChan))) + goto Error; + } + + *nItems = 1; + return NewLUT; +Error: + cmsPipelineFree(NewLUT); + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + +/* +B +B - Matrix - M +B - CLUT - A +B - Matrix - M - CLUT - A +*/ + +static +cmsBool Type_LUTB2A_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsPipeline* Lut = (cmsPipeline*) Ptr; + int inputChan, outputChan; + cmsStage *A = NULL, *B = NULL, *M = NULL; + cmsStage *Matrix = NULL; + cmsStage *CLUT = NULL; + cmsUInt32Number offsetB = 0, offsetMat = 0, offsetM = 0, offsetC = 0, offsetA = 0; + cmsUInt32Number BaseOffset, DirectoryPos, CurrentPos; + + + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + if (!cmsPipelineCheckAndRetreiveStages(Lut, 1, cmsSigCurveSetElemType, &B)) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, &B, &Matrix, &M)) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, &B, &CLUT, &A)) + if (!cmsPipelineCheckAndRetreiveStages(Lut, 5, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, + cmsSigCLutElemType, cmsSigCurveSetElemType, &B, &Matrix, &M, &CLUT, &A)) { + cmsSignalError(self->ContextID, cmsERROR_NOT_SUITABLE, "LUT is not suitable to be saved as LutBToA"); + return FALSE; + } + + inputChan = cmsPipelineInputChannels(Lut); + outputChan = cmsPipelineOutputChannels(Lut); + + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) inputChan)) return FALSE; + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) outputChan)) return FALSE; + if (!_cmsWriteUInt16Number(io, 0)) return FALSE; + + DirectoryPos = io ->Tell(io); + + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + + if (A != NULL) { + + offsetA = io ->Tell(io) - BaseOffset; + if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, A)) return FALSE; + } + + if (CLUT != NULL) { + offsetC = io ->Tell(io) - BaseOffset; + if (!WriteCLUT(self, io, Lut ->SaveAs8Bits ? 1 : 2, CLUT)) return FALSE; + + } + if (M != NULL) { + + offsetM = io ->Tell(io) - BaseOffset; + if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, M)) return FALSE; + } + + if (Matrix != NULL) { + offsetMat = io ->Tell(io) - BaseOffset; + if (!WriteMatrix(self, io, Matrix)) return FALSE; + } + + if (B != NULL) { + + offsetB = io ->Tell(io) - BaseOffset; + if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, B)) return FALSE; + } + + CurrentPos = io ->Tell(io); + + if (!io ->Seek(io, DirectoryPos)) return FALSE; + + if (!_cmsWriteUInt32Number(io, offsetB)) return FALSE; + if (!_cmsWriteUInt32Number(io, offsetMat)) return FALSE; + if (!_cmsWriteUInt32Number(io, offsetM)) return FALSE; + if (!_cmsWriteUInt32Number(io, offsetC)) return FALSE; + if (!_cmsWriteUInt32Number(io, offsetA)) return FALSE; + + if (!io ->Seek(io, CurrentPos)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); +} + + + +static +void* Type_LUTB2A_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsPipelineDup((cmsPipeline*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_LUTB2A_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsPipelineFree((cmsPipeline*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + + + +// ******************************************************************************** +// Type cmsSigColorantTableType +// ******************************************************************************** +/* +The purpose of this tag is to identify the colorants used in the profile by a +unique name and set of XYZ or L*a*b* values to give the colorant an unambiguous +value. The first colorant listed is the colorant of the first device channel of +a lut tag. The second colorant listed is the colorant of the second device channel +of a lut tag, and so on. +*/ + +static +void *Type_ColorantTable_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsUInt32Number i, Count; + cmsNAMEDCOLORLIST* List; + char Name[33]; + cmsUInt16Number PCS[3]; + + + if (!_cmsReadUInt32Number(io, &Count)) return NULL; + + if (Count > cmsMAXCHANNELS) { + cmsSignalError(self->ContextID, cmsERROR_RANGE, "Too many colorants '%d'", Count); + return NULL; + } + + List = cmsAllocNamedColorList(self ->ContextID, Count, 0, "", ""); + for (i=0; i < Count; i++) { + + if (io ->Read(io, Name, 32, 1) != 1) goto Error; + Name[32] = 0; + + if (!_cmsReadUInt16Array(io, 3, PCS)) goto Error; + + if (!cmsAppendNamedColor(List, Name, PCS, NULL)) goto Error; + + } + + *nItems = 1; + return List; + +Error: + *nItems = 0; + cmsFreeNamedColorList(List); + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + + +// Saves a colorant table. It is using the named color structure for simplicity sake +static +cmsBool Type_ColorantTable_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) Ptr; + int i, nColors; + + nColors = cmsNamedColorCount(NamedColorList); + + if (!_cmsWriteUInt32Number(io, nColors)) return FALSE; + + for (i=0; i < nColors; i++) { + + char root[33]; + cmsUInt16Number PCS[3]; + + if (!cmsNamedColorInfo(NamedColorList, i, root, NULL, NULL, PCS, NULL)) return 0; + root[32] = 0; + + if (!io ->Write(io, 32, root)) return FALSE; + if (!_cmsWriteUInt16Array(io, 3, PCS)) return FALSE; + } + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + + +static +void* Type_ColorantTable_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n) +{ + cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) Ptr; + return (void*) cmsDupNamedColorList(nc); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + + +static +void Type_ColorantTable_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsFreeNamedColorList((cmsNAMEDCOLORLIST*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + + +// ******************************************************************************** +// Type cmsSigNamedColor2Type +// ******************************************************************************** +// +//The namedColor2Type is a count value and array of structures that provide color +//coordinates for 7-bit ASCII color names. For each named color, a PCS and optional +//device representation of the color are given. Both representations are 16-bit values. +//The device representation corresponds to the header's 'color space of data' field. +//This representation should be consistent with the 'number of device components' +//field in the namedColor2Type. If this field is 0, device coordinates are not provided. +//The PCS representation corresponds to the header's PCS field. The PCS representation +//is always provided. Color names are fixed-length, 32-byte fields including null +//termination. In order to maintain maximum portability, it is strongly recommended +//that special characters of the 7-bit ASCII set not be used. + +static +void *Type_NamedColor_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + + cmsUInt32Number vendorFlag; // Bottom 16 bits for ICC use + cmsUInt32Number count; // Count of named colors + cmsUInt32Number nDeviceCoords; // Num of device coordinates + char prefix[32]; // Prefix for each color name + char suffix[32]; // Suffix for each color name + cmsNAMEDCOLORLIST* v; + cmsUInt32Number i; + + + *nItems = 0; + if (!_cmsReadUInt32Number(io, &vendorFlag)) return NULL; + if (!_cmsReadUInt32Number(io, &count)) return NULL; + if (!_cmsReadUInt32Number(io, &nDeviceCoords)) return NULL; + + if (io -> Read(io, prefix, 32, 1) != 1) return NULL; + if (io -> Read(io, suffix, 32, 1) != 1) return NULL; + + prefix[31] = suffix[31] = 0; + + v = cmsAllocNamedColorList(self ->ContextID, count, nDeviceCoords, prefix, suffix); + if (v == NULL) { + cmsSignalError(self->ContextID, cmsERROR_RANGE, "Too many named colors '%d'", count); + return NULL; + } + + if (nDeviceCoords > cmsMAXCHANNELS) { + cmsSignalError(self->ContextID, cmsERROR_RANGE, "Too many device coordinates '%d'", nDeviceCoords); + goto Error; + } + for (i=0; i < count; i++) { + + cmsUInt16Number PCS[3]; + cmsUInt16Number Colorant[cmsMAXCHANNELS]; + char Root[33]; + + memset(Colorant, 0, sizeof(Colorant)); + if (io -> Read(io, Root, 32, 1) != 1) goto Error; + Root[32] = 0; + if (!_cmsReadUInt16Array(io, 3, PCS)) goto Error; + if (!_cmsReadUInt16Array(io, nDeviceCoords, Colorant)) goto Error; + + if (!cmsAppendNamedColor(v, Root, PCS, Colorant)) goto Error; + } + + *nItems = 1; + return (void*) v ; + +Error: + cmsFreeNamedColorList(v); + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + +// Saves a named color list into a named color profile +static +cmsBool Type_NamedColor_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) Ptr; + char prefix[32]; // Prefix for each color name + char suffix[32]; // Suffix for each color name + int i, nColors; + + nColors = cmsNamedColorCount(NamedColorList); + + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + if (!_cmsWriteUInt32Number(io, nColors)) return FALSE; + if (!_cmsWriteUInt32Number(io, NamedColorList ->ColorantCount)) return FALSE; + + strncpy(prefix, (const char*) NamedColorList->Prefix, 32); + strncpy(suffix, (const char*) NamedColorList->Suffix, 32); + + suffix[31] = prefix[31] = 0; + + if (!io ->Write(io, 32, prefix)) return FALSE; + if (!io ->Write(io, 32, suffix)) return FALSE; + + for (i=0; i < nColors; i++) { + + cmsUInt16Number PCS[3]; + cmsUInt16Number Colorant[cmsMAXCHANNELS]; + char Root[33]; + + if (!cmsNamedColorInfo(NamedColorList, i, Root, NULL, NULL, PCS, Colorant)) return 0; + if (!io ->Write(io, 32 , Root)) return FALSE; + if (!_cmsWriteUInt16Array(io, 3, PCS)) return FALSE; + if (!_cmsWriteUInt16Array(io, NamedColorList ->ColorantCount, Colorant)) return FALSE; + } + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + +static +void* Type_NamedColor_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n) +{ + cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) Ptr; + + return (void*) cmsDupNamedColorList(nc); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + + +static +void Type_NamedColor_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsFreeNamedColorList((cmsNAMEDCOLORLIST*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + + +// ******************************************************************************** +// Type cmsSigProfileSequenceDescType +// ******************************************************************************** + +// This type is an array of structures, each of which contains information from the +// header fields and tags from the original profiles which were combined to create +// the final profile. The order of the structures is the order in which the profiles +// were combined and includes a structure for the final profile. This provides a +// description of the profile sequence from source to destination, +// typically used with the DeviceLink profile. + +static +cmsBool ReadEmbeddedText(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU** mlu, cmsUInt32Number SizeOfTag) +{ + cmsTagTypeSignature BaseType; + cmsUInt32Number nItems; + + BaseType = _cmsReadTypeBase(io); + + switch (BaseType) { + + case cmsSigTextType: + if (*mlu) cmsMLUfree(*mlu); + *mlu = (cmsMLU*)Type_Text_Read(self, io, &nItems, SizeOfTag); + return (*mlu != NULL); + + case cmsSigTextDescriptionType: + if (*mlu) cmsMLUfree(*mlu); + *mlu = (cmsMLU*) Type_Text_Description_Read(self, io, &nItems, SizeOfTag); + return (*mlu != NULL); + + /* + TBD: Size is needed for MLU, and we have no idea on which is the available size + */ + + case cmsSigMultiLocalizedUnicodeType: + if (*mlu) cmsMLUfree(*mlu); + *mlu = (cmsMLU*) Type_MLU_Read(self, io, &nItems, SizeOfTag); + return (*mlu != NULL); + + default: return FALSE; + } +} + + +static +void *Type_ProfileSequenceDesc_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsSEQ* OutSeq; + cmsUInt32Number i, Count; + + *nItems = 0; + + if (!_cmsReadUInt32Number(io, &Count)) return NULL; + + if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL; + SizeOfTag -= sizeof(cmsUInt32Number); + + + OutSeq = cmsAllocProfileSequenceDescription(self ->ContextID, Count); + if (OutSeq == NULL) return NULL; + + OutSeq ->n = Count; + + // Get structures as well + + for (i=0; i < Count; i++) { + + cmsPSEQDESC* sec = &OutSeq -> seq[i]; + + if (!_cmsReadUInt32Number(io, &sec ->deviceMfg)) goto Error; + if (SizeOfTag < sizeof(cmsUInt32Number)) goto Error; + SizeOfTag -= sizeof(cmsUInt32Number); + + if (!_cmsReadUInt32Number(io, &sec ->deviceModel)) goto Error; + if (SizeOfTag < sizeof(cmsUInt32Number)) goto Error; + SizeOfTag -= sizeof(cmsUInt32Number); + + if (!_cmsReadUInt64Number(io, &sec ->attributes)) goto Error; + if (SizeOfTag < sizeof(cmsUInt64Number)) goto Error; + SizeOfTag -= sizeof(cmsUInt64Number); + + if (!_cmsReadUInt32Number(io, (cmsUInt32Number *)&sec ->technology)) goto Error; + if (SizeOfTag < sizeof(cmsUInt32Number)) goto Error; + SizeOfTag -= sizeof(cmsUInt32Number); + + if (!ReadEmbeddedText(self, io, &sec ->Manufacturer, SizeOfTag)) goto Error; + if (!ReadEmbeddedText(self, io, &sec ->Model, SizeOfTag)) goto Error; + } + + *nItems = 1; + return OutSeq; + +Error: + cmsFreeProfileSequenceDescription(OutSeq); + return NULL; +} + + +// Aux--Embed a text description type. It can be of type text description or multilocalized unicode +// and it depends of the version number passed on cmsTagDescriptor structure instead of stack +static +cmsBool SaveDescription(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* Text) +{ + if (self ->ICCVersion < 0x4000000) { + + if (!_cmsWriteTypeBase(io, cmsSigTextDescriptionType)) return FALSE; + return Type_Text_Description_Write(self, io, Text, 1); + } + else { + if (!_cmsWriteTypeBase(io, cmsSigMultiLocalizedUnicodeType)) return FALSE; + return Type_MLU_Write(self, io, Text, 1); + } +} + + +static +cmsBool Type_ProfileSequenceDesc_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsSEQ* Seq = (cmsSEQ*) Ptr; + cmsUInt32Number i; + + if (!_cmsWriteUInt32Number(io, Seq->n)) return FALSE; + + for (i=0; i < Seq ->n; i++) { + + cmsPSEQDESC* sec = &Seq -> seq[i]; + + if (!_cmsWriteUInt32Number(io, sec ->deviceMfg)) return FALSE; + if (!_cmsWriteUInt32Number(io, sec ->deviceModel)) return FALSE; + if (!_cmsWriteUInt64Number(io, &sec ->attributes)) return FALSE; + if (!_cmsWriteUInt32Number(io, sec ->technology)) return FALSE; + + if (!SaveDescription(self, io, sec ->Manufacturer)) return FALSE; + if (!SaveDescription(self, io, sec ->Model)) return FALSE; + } + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); +} + + +static +void* Type_ProfileSequenceDesc_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n) +{ + return (void*) cmsDupProfileSequenceDescription((cmsSEQ*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_ProfileSequenceDesc_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsFreeProfileSequenceDescription((cmsSEQ*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + + +// ******************************************************************************** +// Type cmsSigProfileSequenceIdType +// ******************************************************************************** +/* +In certain workflows using ICC Device Link Profiles, it is necessary to identify the +original profiles that were combined to create the Device Link Profile. +This type is an array of structures, each of which contains information for +identification of a profile used in a sequence +*/ + + +static +cmsBool ReadSeqID(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + void* Cargo, + cmsUInt32Number n, + cmsUInt32Number SizeOfTag) +{ + cmsSEQ* OutSeq = (cmsSEQ*) Cargo; + cmsPSEQDESC* seq = &OutSeq ->seq[n]; + + if (io -> Read(io, seq ->ProfileID.ID8, 16, 1) != 1) return FALSE; + if (!ReadEmbeddedText(self, io, &seq ->Description, SizeOfTag)) return FALSE; + + return TRUE; +} + + + +static +void *Type_ProfileSequenceId_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsSEQ* OutSeq; + cmsUInt32Number Count; + cmsUInt32Number BaseOffset; + + *nItems = 0; + + // Get actual position as a basis for element offsets + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + // Get table count + if (!_cmsReadUInt32Number(io, &Count)) return NULL; + SizeOfTag -= sizeof(cmsUInt32Number); + + // Allocate an empty structure + OutSeq = cmsAllocProfileSequenceDescription(self ->ContextID, Count); + if (OutSeq == NULL) return NULL; + + + // Read the position table + if (!ReadPositionTable(self, io, Count, BaseOffset, OutSeq, ReadSeqID)) { + + cmsFreeProfileSequenceDescription(OutSeq); + return NULL; + } + + // Success + *nItems = 1; + return OutSeq; + +} + + +static +cmsBool WriteSeqID(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + void* Cargo, + cmsUInt32Number n, + cmsUInt32Number SizeOfTag) +{ + cmsSEQ* Seq = (cmsSEQ*) Cargo; + + if (!io ->Write(io, 16, Seq ->seq[n].ProfileID.ID8)) return FALSE; + + // Store here the MLU + if (!SaveDescription(self, io, Seq ->seq[n].Description)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +static +cmsBool Type_ProfileSequenceId_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsSEQ* Seq = (cmsSEQ*) Ptr; + cmsUInt32Number BaseOffset; + + // Keep the base offset + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + // This is the table count + if (!_cmsWriteUInt32Number(io, Seq ->n)) return FALSE; + + // This is the position table and content + if (!WritePositionTable(self, io, 0, Seq ->n, BaseOffset, Seq, WriteSeqID)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); +} + +static +void* Type_ProfileSequenceId_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n) +{ + return (void*) cmsDupProfileSequenceDescription((cmsSEQ*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_ProfileSequenceId_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsFreeProfileSequenceDescription((cmsSEQ*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + + +// ******************************************************************************** +// Type cmsSigUcrBgType +// ******************************************************************************** +/* +This type contains curves representing the under color removal and black +generation and a text string which is a general description of the method used +for the ucr/bg. +*/ + +static +void *Type_UcrBg_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsUcrBg* n = (cmsUcrBg*) _cmsMallocZero(self ->ContextID, sizeof(cmsUcrBg)); + cmsUInt32Number CountUcr, CountBg; + char* ASCIIString; + + *nItems = 0; + if (n == NULL) return NULL; + + // First curve is Under color removal + if (!_cmsReadUInt32Number(io, &CountUcr)) return NULL; + if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL; + SizeOfTag -= sizeof(cmsUInt32Number); + + n ->Ucr = cmsBuildTabulatedToneCurve16(self ->ContextID, CountUcr, NULL); + if (n ->Ucr == NULL) return NULL; + + if (!_cmsReadUInt16Array(io, CountUcr, n ->Ucr->Table16)) return NULL; + if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL; + SizeOfTag -= CountUcr * sizeof(cmsUInt16Number); + + // Second curve is Black generation + if (!_cmsReadUInt32Number(io, &CountBg)) return NULL; + if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL; + SizeOfTag -= sizeof(cmsUInt32Number); + + n ->Bg = cmsBuildTabulatedToneCurve16(self ->ContextID, CountBg, NULL); + if (n ->Bg == NULL) return NULL; + if (!_cmsReadUInt16Array(io, CountBg, n ->Bg->Table16)) return NULL; + if (SizeOfTag < CountBg * sizeof(cmsUInt16Number)) return NULL; + SizeOfTag -= CountBg * sizeof(cmsUInt16Number); + if (SizeOfTag == UINT_MAX) return NULL; + + // Now comes the text. The length is specified by the tag size + n ->Desc = cmsMLUalloc(self ->ContextID, 1); + if (n ->Desc == NULL) return NULL; + + ASCIIString = (char*) _cmsMalloc(self ->ContextID, SizeOfTag + 1); + if (io ->Read(io, ASCIIString, sizeof(char), SizeOfTag) != SizeOfTag) return NULL; + ASCIIString[SizeOfTag] = 0; + cmsMLUsetASCII(n ->Desc, cmsNoLanguage, cmsNoCountry, ASCIIString); + _cmsFree(self ->ContextID, ASCIIString); + + *nItems = 1; + return (void*) n; +} + +static +cmsBool Type_UcrBg_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsUcrBg* Value = (cmsUcrBg*) Ptr; + cmsUInt32Number TextSize; + char* Text; + + // First curve is Under color removal + if (!_cmsWriteUInt32Number(io, Value ->Ucr ->nEntries)) return FALSE; + if (!_cmsWriteUInt16Array(io, Value ->Ucr ->nEntries, Value ->Ucr ->Table16)) return FALSE; + + // Then black generation + if (!_cmsWriteUInt32Number(io, Value ->Bg ->nEntries)) return FALSE; + if (!_cmsWriteUInt16Array(io, Value ->Bg ->nEntries, Value ->Bg ->Table16)) return FALSE; + + // Now comes the text. The length is specified by the tag size + TextSize = cmsMLUgetASCII(Value ->Desc, cmsNoLanguage, cmsNoCountry, NULL, 0); + Text = (char*) _cmsMalloc(self ->ContextID, TextSize); + if (cmsMLUgetASCII(Value ->Desc, cmsNoLanguage, cmsNoCountry, Text, TextSize) != TextSize) return FALSE; + + if (!io ->Write(io, TextSize, Text)) return FALSE; + _cmsFree(self ->ContextID, Text); + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); +} + +static +void* Type_UcrBg_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + cmsUcrBg* Src = (cmsUcrBg*) Ptr; + cmsUcrBg* NewUcrBg = (cmsUcrBg*) _cmsMallocZero(self ->ContextID, sizeof(cmsUcrBg)); + + if (NewUcrBg == NULL) return NULL; + + NewUcrBg ->Bg = cmsDupToneCurve(Src ->Bg); + NewUcrBg ->Ucr = cmsDupToneCurve(Src ->Ucr); + NewUcrBg ->Desc = cmsMLUdup(Src ->Desc); + + return (void*) NewUcrBg; + + cmsUNUSED_PARAMETER(n); +} + +static +void Type_UcrBg_Free(struct _cms_typehandler_struct* self, void *Ptr) +{ + cmsUcrBg* Src = (cmsUcrBg*) Ptr; + + if (Src ->Ucr) cmsFreeToneCurve(Src ->Ucr); + if (Src ->Bg) cmsFreeToneCurve(Src ->Bg); + if (Src ->Desc) cmsMLUfree(Src ->Desc); + + _cmsFree(self ->ContextID, Ptr); +} + +// ******************************************************************************** +// Type cmsSigCrdInfoType +// ******************************************************************************** + +/* +This type contains the PostScript product name to which this profile corresponds +and the names of the companion CRDs. Recall that a single profile can generate +multiple CRDs. It is implemented as a MLU being the language code "PS" and then +country varies for each element: + + nm: PostScript product name + #0: Rendering intent 0 CRD name + #1: Rendering intent 1 CRD name + #2: Rendering intent 2 CRD name + #3: Rendering intent 3 CRD name +*/ + + + +// Auxiliar, read an string specified as count + string +static +cmsBool ReadCountAndSting(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* mlu, cmsUInt32Number* SizeOfTag, const char* Section) +{ + cmsUInt32Number Count; + char* Text; + + if (*SizeOfTag < sizeof(cmsUInt32Number)) return FALSE; + + if (!_cmsReadUInt32Number(io, &Count)) return FALSE; + + if (Count > UINT_MAX - sizeof(cmsUInt32Number)) return FALSE; + if (*SizeOfTag < Count + sizeof(cmsUInt32Number)) return FALSE; + + Text = (char*) _cmsMalloc(self ->ContextID, Count+1); + if (Text == NULL) return FALSE; + + if (io ->Read(io, Text, sizeof(cmsUInt8Number), Count) != Count) { + _cmsFree(self ->ContextID, Text); + return FALSE; + } + + Text[Count] = 0; + + cmsMLUsetASCII(mlu, "PS", Section, Text); + _cmsFree(self ->ContextID, Text); + + *SizeOfTag -= (Count + sizeof(cmsUInt32Number)); + return TRUE; +} + +static +cmsBool WriteCountAndSting(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* mlu, const char* Section) +{ + cmsUInt32Number TextSize; + char* Text; + + TextSize = cmsMLUgetASCII(mlu, "PS", Section, NULL, 0); + Text = (char*) _cmsMalloc(self ->ContextID, TextSize); + + if (!_cmsWriteUInt32Number(io, TextSize)) return FALSE; + + if (cmsMLUgetASCII(mlu, "PS", Section, Text, TextSize) == 0) return FALSE; + + if (!io ->Write(io, TextSize, Text)) return FALSE; + _cmsFree(self ->ContextID, Text); + + return TRUE; +} + +static +void *Type_CrdInfo_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsMLU* mlu = cmsMLUalloc(self ->ContextID, 5); + + *nItems = 0; + if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "nm")) goto Error; + if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#0")) goto Error; + if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#1")) goto Error; + if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#2")) goto Error; + if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#3")) goto Error; + + *nItems = 1; + return (void*) mlu; + +Error: + cmsMLUfree(mlu); + return NULL; + +} + +static +cmsBool Type_CrdInfo_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + + cmsMLU* mlu = (cmsMLU*) Ptr; + + if (!WriteCountAndSting(self, io, mlu, "nm")) goto Error; + if (!WriteCountAndSting(self, io, mlu, "#0")) goto Error; + if (!WriteCountAndSting(self, io, mlu, "#1")) goto Error; + if (!WriteCountAndSting(self, io, mlu, "#2")) goto Error; + if (!WriteCountAndSting(self, io, mlu, "#3")) goto Error; + + return TRUE; + +Error: + return FALSE; + + cmsUNUSED_PARAMETER(nItems); +} + + +static +void* Type_CrdInfo_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsMLUdup((cmsMLU*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_CrdInfo_Free(struct _cms_typehandler_struct* self, void *Ptr) +{ + cmsMLUfree((cmsMLU*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + +// ******************************************************************************** +// Type cmsSigScreeningType +// ******************************************************************************** +// +//The screeningType describes various screening parameters including screen +//frequency, screening angle, and spot shape. + +static +void *Type_Screening_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsScreening* sc = NULL; + cmsUInt32Number i; + + sc = (cmsScreening*) _cmsMallocZero(self ->ContextID, sizeof(cmsScreening)); + if (sc == NULL) return NULL; + + *nItems = 0; + + if (!_cmsReadUInt32Number(io, &sc ->Flag)) goto Error; + if (!_cmsReadUInt32Number(io, &sc ->nChannels)) goto Error; + + if (sc ->nChannels > cmsMAXCHANNELS - 1) + sc ->nChannels = cmsMAXCHANNELS - 1; + + for (i=0; i < sc ->nChannels; i++) { + + if (!_cmsRead15Fixed16Number(io, &sc ->Channels[i].Frequency)) goto Error; + if (!_cmsRead15Fixed16Number(io, &sc ->Channels[i].ScreenAngle)) goto Error; + if (!_cmsReadUInt32Number(io, &sc ->Channels[i].SpotShape)) goto Error; + } + + + *nItems = 1; + + return (void*) sc; + +Error: + if (sc != NULL) + _cmsFree(self ->ContextID, sc); + + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + +static +cmsBool Type_Screening_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsScreening* sc = (cmsScreening* ) Ptr; + cmsUInt32Number i; + + if (!_cmsWriteUInt32Number(io, sc ->Flag)) return FALSE; + if (!_cmsWriteUInt32Number(io, sc ->nChannels)) return FALSE; + + for (i=0; i < sc ->nChannels; i++) { + + if (!_cmsWrite15Fixed16Number(io, sc ->Channels[i].Frequency)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, sc ->Channels[i].ScreenAngle)) return FALSE; + if (!_cmsWriteUInt32Number(io, sc ->Channels[i].SpotShape)) return FALSE; + } + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + + +static +void* Type_Screening_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsScreening)); + + cmsUNUSED_PARAMETER(n); +} + + +static +void Type_Screening_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + +// ******************************************************************************** +// Type cmsSigViewingConditionsType +// ******************************************************************************** +// +//This type represents a set of viewing condition parameters including: +//CIE 'absolute'illuminant white point tristimulus values and CIE 'absolute' +//surround tristimulus values. + +static +void *Type_ViewingConditions_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsICCViewingConditions* vc = NULL; + + vc = (cmsICCViewingConditions*) _cmsMallocZero(self ->ContextID, sizeof(cmsICCViewingConditions)); + if (vc == NULL) return NULL; + + *nItems = 0; + + if (!_cmsReadXYZNumber(io, &vc ->IlluminantXYZ)) goto Error; + if (!_cmsReadXYZNumber(io, &vc ->SurroundXYZ)) goto Error; + if (!_cmsReadUInt32Number(io, &vc ->IlluminantType)) goto Error; + + *nItems = 1; + + return (void*) vc; + +Error: + if (vc != NULL) + _cmsFree(self ->ContextID, vc); + + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + +static +cmsBool Type_ViewingConditions_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsICCViewingConditions* sc = (cmsICCViewingConditions* ) Ptr; + + if (!_cmsWriteXYZNumber(io, &sc ->IlluminantXYZ)) return FALSE; + if (!_cmsWriteXYZNumber(io, &sc ->SurroundXYZ)) return FALSE; + if (!_cmsWriteUInt32Number(io, sc ->IlluminantType)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + + +static +void* Type_ViewingConditions_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsScreening)); + + cmsUNUSED_PARAMETER(n); +} + + +static +void Type_ViewingConditions_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + _cmsFree(self ->ContextID, Ptr); +} + + +// ******************************************************************************** +// Type cmsSigMultiProcessElementType +// ******************************************************************************** + + +static +void* GenericMPEdup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsStageDup((cmsStage*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void GenericMPEfree(struct _cms_typehandler_struct* self, void *Ptr) +{ + cmsStageFree((cmsStage*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + +// Each curve is stored in one or more curve segments, with break-points specified between curve segments. +// The first curve segment always starts at -Infinity, and the last curve segment always ends at +Infinity. The +// first and last curve segments shall be specified in terms of a formula, whereas the other segments shall be +// specified either in terms of a formula, or by a sampled curve. + + +// Read an embedded segmented curve +static +cmsToneCurve* ReadSegmentedCurve(struct _cms_typehandler_struct* self, cmsIOHANDLER* io) +{ + cmsCurveSegSignature ElementSig; + cmsUInt32Number i, j; + cmsUInt16Number nSegments; + cmsCurveSegment* Segments; + cmsToneCurve* Curve; + cmsFloat32Number PrevBreak = -1E22F; // - infinite + + // Take signature and channels for each element. + if (!_cmsReadUInt32Number(io, (cmsUInt32Number*) &ElementSig)) return NULL; + + // That should be a segmented curve + if (ElementSig != cmsSigSegmentedCurve) return NULL; + + if (!_cmsReadUInt32Number(io, NULL)) return NULL; + if (!_cmsReadUInt16Number(io, &nSegments)) return NULL; + if (!_cmsReadUInt16Number(io, NULL)) return NULL; + + if (nSegments < 1) return NULL; + Segments = (cmsCurveSegment*) _cmsCalloc(self ->ContextID, nSegments, sizeof(cmsCurveSegment)); + if (Segments == NULL) return NULL; + + // Read breakpoints + for (i=0; i < (cmsUInt32Number) nSegments - 1; i++) { + + Segments[i].x0 = PrevBreak; + if (!_cmsReadFloat32Number(io, &Segments[i].x1)) goto Error; + PrevBreak = Segments[i].x1; + } + + Segments[nSegments-1].x0 = PrevBreak; + Segments[nSegments-1].x1 = 1E22F; // A big cmsFloat32Number number + + // Read segments + for (i=0; i < nSegments; i++) { + + if (!_cmsReadUInt32Number(io, (cmsUInt32Number*) &ElementSig)) goto Error; + if (!_cmsReadUInt32Number(io, NULL)) goto Error; + + switch (ElementSig) { + + case cmsSigFormulaCurveSeg: { + + cmsUInt16Number Type; + cmsUInt32Number ParamsByType[] = {4, 5, 5 }; + + if (!_cmsReadUInt16Number(io, &Type)) goto Error; + if (!_cmsReadUInt16Number(io, NULL)) goto Error; + + Segments[i].Type = Type + 6; + if (Type > 2) goto Error; + + for (j=0; j < ParamsByType[Type]; j++) { + + cmsFloat32Number f; + if (!_cmsReadFloat32Number(io, &f)) goto Error; + Segments[i].Params[j] = f; + } + } + break; + + + case cmsSigSampledCurveSeg: { + cmsUInt32Number Count; + + if (!_cmsReadUInt32Number(io, &Count)) goto Error; + + Segments[i].nGridPoints = Count; + Segments[i].SampledPoints = (cmsFloat32Number*) _cmsCalloc(self ->ContextID, Count, sizeof(cmsFloat32Number)); + if (Segments[i].SampledPoints == NULL) goto Error; + + for (j=0; j < Count; j++) { + if (!_cmsReadFloat32Number(io, &Segments[i].SampledPoints[j])) goto Error; + } + } + break; + + default: + { + char String[5]; + + _cmsTagSignature2String(String, (cmsTagSignature) ElementSig); + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown curve element type '%s' found.", String); + } + goto Error; + + } + } + + Curve = cmsBuildSegmentedToneCurve(self ->ContextID, nSegments, Segments); + + for (i=0; i < nSegments; i++) { + if (Segments[i].SampledPoints) _cmsFree(self ->ContextID, Segments[i].SampledPoints); + } + _cmsFree(self ->ContextID, Segments); + return Curve; + +Error: + if (Segments) { + for (i=0; i < nSegments; i++) { + if (Segments[i].SampledPoints) _cmsFree(self ->ContextID, Segments[i].SampledPoints); + } + _cmsFree(self ->ContextID, Segments); + } + return NULL; +} + + +static +cmsBool ReadMPECurve(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + void* Cargo, + cmsUInt32Number n, + cmsUInt32Number SizeOfTag) +{ + cmsToneCurve** GammaTables = ( cmsToneCurve**) Cargo; + + GammaTables[n] = ReadSegmentedCurve(self, io); + return (GammaTables[n] != NULL); + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +static +void *Type_MPEcurve_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsStage* mpe = NULL; + cmsUInt16Number InputChans, OutputChans; + cmsUInt32Number i, BaseOffset; + cmsToneCurve** GammaTables; + + *nItems = 0; + + // Get actual position as a basis for element offsets + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + if (!_cmsReadUInt16Number(io, &InputChans)) return NULL; + if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL; + + if (InputChans != OutputChans) return NULL; + + GammaTables = (cmsToneCurve**) _cmsCalloc(self ->ContextID, InputChans, sizeof(cmsToneCurve*)); + if (GammaTables == NULL) return NULL; + + if (ReadPositionTable(self, io, InputChans, BaseOffset, GammaTables, ReadMPECurve)) { + + mpe = cmsStageAllocToneCurves(self ->ContextID, InputChans, GammaTables); + } + else { + mpe = NULL; + } + + for (i=0; i < InputChans; i++) { + if (GammaTables[i]) cmsFreeToneCurve(GammaTables[i]); + } + + _cmsFree(self ->ContextID, GammaTables); + *nItems = (mpe != NULL) ? 1 : 0; + return mpe; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + +// Write a single segmented curve. NO CHECK IS PERFORMED ON VALIDITY +static +cmsBool WriteSegmentedCurve(cmsIOHANDLER* io, cmsToneCurve* g) +{ + cmsUInt32Number i, j; + cmsCurveSegment* Segments = g ->Segments; + cmsUInt32Number nSegments = g ->nSegments; + + if (!_cmsWriteUInt32Number(io, cmsSigSegmentedCurve)) goto Error; + if (!_cmsWriteUInt32Number(io, 0)) goto Error; + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) nSegments)) goto Error; + if (!_cmsWriteUInt16Number(io, 0)) goto Error; + + // Write the break-points + for (i=0; i < nSegments - 1; i++) { + if (!_cmsWriteFloat32Number(io, Segments[i].x1)) goto Error; + } + + // Write the segments + for (i=0; i < g ->nSegments; i++) { + + cmsCurveSegment* ActualSeg = Segments + i; + + if (ActualSeg -> Type == 0) { + + // This is a sampled curve + if (!_cmsWriteUInt32Number(io, (cmsUInt32Number) cmsSigSampledCurveSeg)) goto Error; + if (!_cmsWriteUInt32Number(io, 0)) goto Error; + if (!_cmsWriteUInt32Number(io, ActualSeg -> nGridPoints)) goto Error; + + for (j=0; j < g ->Segments[i].nGridPoints; j++) { + if (!_cmsWriteFloat32Number(io, ActualSeg -> SampledPoints[j])) goto Error; + } + + } + else { + int Type; + cmsUInt32Number ParamsByType[] = { 4, 5, 5 }; + + // This is a formula-based + if (!_cmsWriteUInt32Number(io, (cmsUInt32Number) cmsSigFormulaCurveSeg)) goto Error; + if (!_cmsWriteUInt32Number(io, 0)) goto Error; + + // We only allow 1, 2 and 3 as types + Type = ActualSeg ->Type - 6; + if (Type > 2 || Type < 0) goto Error; + + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) Type)) goto Error; + if (!_cmsWriteUInt16Number(io, 0)) goto Error; + + for (j=0; j < ParamsByType[Type]; j++) { + if (!_cmsWriteFloat32Number(io, (cmsFloat32Number) ActualSeg ->Params[j])) goto Error; + } + } + + // It seems there is no need to align. Code is here, and for safety commented out + // if (!_cmsWriteAlignment(io)) goto Error; + } + + return TRUE; + +Error: + return FALSE; +} + + +static +cmsBool WriteMPECurve(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + void* Cargo, + cmsUInt32Number n, + cmsUInt32Number SizeOfTag) +{ + _cmsStageToneCurvesData* Curves = (_cmsStageToneCurvesData*) Cargo; + + return WriteSegmentedCurve(io, Curves ->TheCurves[n]); + + cmsUNUSED_PARAMETER(SizeOfTag); + cmsUNUSED_PARAMETER(self); +} + +// Write a curve, checking first for validity +static +cmsBool Type_MPEcurve_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsUInt32Number BaseOffset; + cmsStage* mpe = (cmsStage*) Ptr; + _cmsStageToneCurvesData* Curves = (_cmsStageToneCurvesData*) mpe ->Data; + + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + // Write the header. Since those are curves, input and output channels are same + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->InputChannels)) return FALSE; + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->InputChannels)) return FALSE; + + if (!WritePositionTable(self, io, 0, + mpe ->InputChannels, BaseOffset, Curves, WriteMPECurve)) return FALSE; + + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); +} + + + +// The matrix is organized as an array of PxQ+Q elements, where P is the number of input channels to the +// matrix, and Q is the number of output channels. The matrix elements are each float32Numbers. The array +// is organized as follows: +// array = [e11, e12, ? e1P, e21, e22, ? e2P, ? eQ1, eQ2, ? eQP, e1, e2, ? eQ] + +static +void *Type_MPEmatrix_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsStage* mpe; + cmsUInt16Number InputChans, OutputChans; + cmsUInt32Number nElems, i; + cmsFloat64Number* Matrix; + cmsFloat64Number* Offsets; + + if (!_cmsReadUInt16Number(io, &InputChans)) return NULL; + if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL; + + + // Input and output chans may be ANY (up to 0xffff), + // but we choose to limit to 16 channels for now + if (InputChans >= cmsMAXCHANNELS) return NULL; + if (OutputChans >= cmsMAXCHANNELS) return NULL; + + nElems = InputChans * OutputChans; + + Matrix = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, nElems, sizeof(cmsFloat64Number)); + if (Matrix == NULL) return NULL; + + Offsets = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, OutputChans, sizeof(cmsFloat64Number)); + if (Offsets == NULL) { + + _cmsFree(self ->ContextID, Matrix); + return NULL; + } + + for (i=0; i < nElems; i++) { + + cmsFloat32Number v; + + if (!_cmsReadFloat32Number(io, &v)) { + _cmsFree(self ->ContextID, Matrix); + _cmsFree(self ->ContextID, Offsets); + return NULL; + } + Matrix[i] = v; + } + + + for (i=0; i < OutputChans; i++) { + + cmsFloat32Number v; + + if (!_cmsReadFloat32Number(io, &v)) { + _cmsFree(self ->ContextID, Matrix); + _cmsFree(self ->ContextID, Offsets); + return NULL; + } + Offsets[i] = v; + } + + + mpe = cmsStageAllocMatrix(self ->ContextID, OutputChans, InputChans, Matrix, Offsets); + _cmsFree(self ->ContextID, Matrix); + _cmsFree(self ->ContextID, Offsets); + + *nItems = 1; + + return mpe; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +static +cmsBool Type_MPEmatrix_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsUInt32Number i, nElems; + cmsStage* mpe = (cmsStage*) Ptr; + _cmsStageMatrixData* Matrix = (_cmsStageMatrixData*) mpe ->Data; + + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->InputChannels)) return FALSE; + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->OutputChannels)) return FALSE; + + nElems = mpe ->InputChannels * mpe ->OutputChannels; + + for (i=0; i < nElems; i++) { + if (!_cmsWriteFloat32Number(io, (cmsFloat32Number) Matrix->Double[i])) return FALSE; + } + + + for (i=0; i < mpe ->OutputChannels; i++) { + + if (Matrix ->Offset == NULL) { + + if (!_cmsWriteFloat32Number(io, 0)) return FALSE; + } + else { + if (!_cmsWriteFloat32Number(io, (cmsFloat32Number) Matrix->Offset[i])) return FALSE; + } + } + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + + + +static +void *Type_MPEclut_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsStage* mpe = NULL; + cmsUInt16Number InputChans, OutputChans; + cmsUInt8Number Dimensions8[16]; + cmsUInt32Number i, nMaxGrids, GridPoints[MAX_INPUT_DIMENSIONS]; + _cmsStageCLutData* clut; + + if (!_cmsReadUInt16Number(io, &InputChans)) return NULL; + if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL; + + if (InputChans == 0) goto Error; + if (OutputChans == 0) goto Error; + + if (io ->Read(io, Dimensions8, sizeof(cmsUInt8Number), 16) != 16) + goto Error; + + // Copy MAX_INPUT_DIMENSIONS at most. Expand to cmsUInt32Number + nMaxGrids = InputChans > MAX_INPUT_DIMENSIONS ? MAX_INPUT_DIMENSIONS : InputChans; + + for (i = 0; i < nMaxGrids; i++) { + if (Dimensions8[i] == 1) goto Error; // Impossible value, 0 for no CLUT and then 2 at least + GridPoints[i] = (cmsUInt32Number)Dimensions8[i]; + } + + // Allocate the true CLUT + mpe = cmsStageAllocCLutFloatGranular(self ->ContextID, GridPoints, InputChans, OutputChans, NULL); + if (mpe == NULL) goto Error; + + // Read the data + clut = (_cmsStageCLutData*) mpe ->Data; + for (i=0; i < clut ->nEntries; i++) { + + if (!_cmsReadFloat32Number(io, &clut ->Tab.TFloat[i])) goto Error; + } + + *nItems = 1; + return mpe; + +Error: + *nItems = 0; + if (mpe != NULL) cmsStageFree(mpe); + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + +// Write a CLUT in floating point +static +cmsBool Type_MPEclut_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsUInt8Number Dimensions8[16]; + cmsUInt32Number i; + cmsStage* mpe = (cmsStage*) Ptr; + _cmsStageCLutData* clut = (_cmsStageCLutData*) mpe ->Data; + + // Check for maximum number of channels + if (mpe -> InputChannels > 15) return FALSE; + + // Only floats are supported in MPE + if (clut ->HasFloatValues == FALSE) return FALSE; + + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->InputChannels)) return FALSE; + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->OutputChannels)) return FALSE; + + memset(Dimensions8, 0, sizeof(Dimensions8)); + + for (i=0; i < mpe ->InputChannels; i++) + Dimensions8[i] = (cmsUInt8Number) clut ->Params ->nSamples[i]; + + if (!io ->Write(io, 16, Dimensions8)) return FALSE; + + for (i=0; i < clut ->nEntries; i++) { + + if (!_cmsWriteFloat32Number(io, clut ->Tab.TFloat[i])) return FALSE; + } + + return TRUE; + + cmsUNUSED_PARAMETER(nItems); + cmsUNUSED_PARAMETER(self); +} + + + +// This is the list of built-in MPE types +static _cmsTagTypeLinkedList SupportedMPEtypes[] = { + +{{ (cmsTagTypeSignature) cmsSigBAcsElemType, NULL, NULL, NULL, NULL, NULL, 0 }, &SupportedMPEtypes[1] }, // Ignore those elements for now +{{ (cmsTagTypeSignature) cmsSigEAcsElemType, NULL, NULL, NULL, NULL, NULL, 0 }, &SupportedMPEtypes[2] }, // (That's what the spec says) + +{TYPE_MPE_HANDLER((cmsTagTypeSignature) cmsSigCurveSetElemType, MPEcurve), &SupportedMPEtypes[3] }, +{TYPE_MPE_HANDLER((cmsTagTypeSignature) cmsSigMatrixElemType, MPEmatrix), &SupportedMPEtypes[4] }, +{TYPE_MPE_HANDLER((cmsTagTypeSignature) cmsSigCLutElemType, MPEclut), NULL }, +}; + +_cmsTagTypePluginChunkType _cmsMPETypePluginChunk = { NULL }; + +static +cmsBool ReadMPEElem(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + void* Cargo, + cmsUInt32Number n, + cmsUInt32Number SizeOfTag) +{ + cmsStageSignature ElementSig; + 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; + + // The reserved placeholder + if (!_cmsReadUInt32Number(io, NULL)) return FALSE; + + // Read diverse MPE types + TypeHandler = GetHandler((cmsTagTypeSignature) ElementSig, MPETypePluginChunk ->TagTypes, SupportedMPEtypes); + if (TypeHandler == NULL) { + + char String[5]; + + _cmsTagSignature2String(String, (cmsTagSignature) ElementSig); + + // An unknown element was found. + cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown MPE type '%s' found.", String); + return FALSE; + } + + // If no read method, just ignore the element (valid for cmsSigBAcsElemType and cmsSigEAcsElemType) + // Read the MPE. No size is given + if (TypeHandler ->ReadPtr != NULL) { + + // This is a real element which should be read and processed + if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, (cmsStage*) TypeHandler ->ReadPtr(self, io, &nItems, SizeOfTag))) + return FALSE; + } + + return TRUE; + + cmsUNUSED_PARAMETER(SizeOfTag); + cmsUNUSED_PARAMETER(n); +} + + +// This is the main dispatcher for MPE +static +void *Type_MPE_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsUInt16Number InputChans, OutputChans; + cmsUInt32Number ElementCount; + cmsPipeline *NewLUT = NULL; + cmsUInt32Number BaseOffset; + + // Get actual position as a basis for element offsets + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + // Read channels and element count + if (!_cmsReadUInt16Number(io, &InputChans)) return NULL; + if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL; + + if (InputChans == 0 || InputChans >= cmsMAXCHANNELS) return NULL; + if (OutputChans == 0 || OutputChans >= cmsMAXCHANNELS) return NULL; + + // Allocates an empty LUT + NewLUT = cmsPipelineAlloc(self ->ContextID, InputChans, OutputChans); + if (NewLUT == NULL) return NULL; + + if (!_cmsReadUInt32Number(io, &ElementCount)) goto Error; + if (!ReadPositionTable(self, io, ElementCount, BaseOffset, NewLUT, ReadMPEElem)) goto Error; + + // Success + *nItems = 1; + return NewLUT; + + // Error +Error: + if (NewLUT != NULL) cmsPipelineFree(NewLUT); + *nItems = 0; + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + + +// This one is a liitle bit more complex, so we don't use position tables this time. +static +cmsBool Type_MPE_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsUInt32Number i, BaseOffset, DirectoryPos, CurrentPos; + int inputChan, outputChan; + cmsUInt32Number ElemCount; + cmsUInt32Number *ElementOffsets = NULL, *ElementSizes = NULL, Before; + cmsStageSignature ElementSig; + cmsPipeline* Lut = (cmsPipeline*) Ptr; + cmsStage* Elem = Lut ->Elements; + cmsTagTypeHandler* TypeHandler; + _cmsTagTypePluginChunkType* MPETypePluginChunk = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(self->ContextID, MPEPlugin); + + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + inputChan = cmsPipelineInputChannels(Lut); + outputChan = cmsPipelineOutputChannels(Lut); + ElemCount = cmsPipelineStageCount(Lut); + + ElementOffsets = (cmsUInt32Number *) _cmsCalloc(self ->ContextID, ElemCount, sizeof(cmsUInt32Number)); + if (ElementOffsets == NULL) goto Error; + + ElementSizes = (cmsUInt32Number *) _cmsCalloc(self ->ContextID, ElemCount, sizeof(cmsUInt32Number)); + if (ElementSizes == NULL) goto Error; + + // Write the head + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) inputChan)) goto Error; + if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) outputChan)) goto Error; + if (!_cmsWriteUInt32Number(io, (cmsUInt16Number) ElemCount)) goto Error; + + DirectoryPos = io ->Tell(io); + + // Write a fake directory to be filled latter on + for (i=0; i < ElemCount; i++) { + if (!_cmsWriteUInt32Number(io, 0)) goto Error; // Offset + if (!_cmsWriteUInt32Number(io, 0)) goto Error; // size + } + + // Write each single tag. Keep track of the size as well. + for (i=0; i < ElemCount; i++) { + + ElementOffsets[i] = io ->Tell(io) - BaseOffset; + + ElementSig = Elem ->Type; + + TypeHandler = GetHandler((cmsTagTypeSignature) ElementSig, MPETypePluginChunk->TagTypes, SupportedMPEtypes); + if (TypeHandler == NULL) { + + char String[5]; + + _cmsTagSignature2String(String, (cmsTagSignature) ElementSig); + + // An unknow element was found. + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Found unknown MPE type '%s'", String); + goto Error; + } + + if (!_cmsWriteUInt32Number(io, ElementSig)) goto Error; + if (!_cmsWriteUInt32Number(io, 0)) goto Error; + Before = io ->Tell(io); + if (!TypeHandler ->WritePtr(self, io, Elem, 1)) goto Error; + if (!_cmsWriteAlignment(io)) goto Error; + + ElementSizes[i] = io ->Tell(io) - Before; + + Elem = Elem ->Next; + } + + // Write the directory + CurrentPos = io ->Tell(io); + + if (!io ->Seek(io, DirectoryPos)) goto Error; + + for (i=0; i < ElemCount; i++) { + if (!_cmsWriteUInt32Number(io, ElementOffsets[i])) goto Error; + if (!_cmsWriteUInt32Number(io, ElementSizes[i])) goto Error; + } + + if (!io ->Seek(io, CurrentPos)) goto Error; + + if (ElementOffsets != NULL) _cmsFree(self ->ContextID, ElementOffsets); + if (ElementSizes != NULL) _cmsFree(self ->ContextID, ElementSizes); + return TRUE; + +Error: + if (ElementOffsets != NULL) _cmsFree(self ->ContextID, ElementOffsets); + if (ElementSizes != NULL) _cmsFree(self ->ContextID, ElementSizes); + return FALSE; + + cmsUNUSED_PARAMETER(nItems); +} + + +static +void* Type_MPE_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsPipelineDup((cmsPipeline*) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + +static +void Type_MPE_Free(struct _cms_typehandler_struct* self, void *Ptr) +{ + cmsPipelineFree((cmsPipeline*) Ptr); + return; + + cmsUNUSED_PARAMETER(self); +} + + +// ******************************************************************************** +// Type cmsSigVcgtType +// ******************************************************************************** + + +#define cmsVideoCardGammaTableType 0 +#define cmsVideoCardGammaFormulaType 1 + +// Used internally +typedef struct { + double Gamma; + double Min; + double Max; +} _cmsVCGTGAMMA; + + +static +void *Type_vcgt_Read(struct _cms_typehandler_struct* self, + cmsIOHANDLER* io, + cmsUInt32Number* nItems, + cmsUInt32Number SizeOfTag) +{ + cmsUInt32Number TagType, n, i; + cmsToneCurve** Curves; + + *nItems = 0; + + // Read tag type + if (!_cmsReadUInt32Number(io, &TagType)) return NULL; + + // Allocate space for the array + Curves = ( cmsToneCurve**) _cmsCalloc(self ->ContextID, 3, sizeof(cmsToneCurve*)); + if (Curves == NULL) return NULL; + + // There are two possible flavors + switch (TagType) { + + // Gamma is stored as a table + case cmsVideoCardGammaTableType: + { + cmsUInt16Number nChannels, nElems, nBytes; + + // Check channel count, which should be 3 (we don't support monochrome this time) + if (!_cmsReadUInt16Number(io, &nChannels)) goto Error; + + if (nChannels != 3) { + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported number of channels for VCGT '%d'", nChannels); + goto Error; + } + + // Get Table element count and bytes per element + if (!_cmsReadUInt16Number(io, &nElems)) goto Error; + if (!_cmsReadUInt16Number(io, &nBytes)) goto Error; + + // Adobe's quirk fixup. Fixing broken profiles... + if (nElems == 256 && nBytes == 1 && SizeOfTag == 1576) + nBytes = 2; + + + // Populate tone curves + for (n=0; n < 3; n++) { + + Curves[n] = cmsBuildTabulatedToneCurve16(self ->ContextID, nElems, NULL); + if (Curves[n] == NULL) goto Error; + + // On depending on byte depth + switch (nBytes) { + + // One byte, 0..255 + case 1: + for (i=0; i < nElems; i++) { + + cmsUInt8Number v; + + if (!_cmsReadUInt8Number(io, &v)) goto Error; + Curves[n] ->Table16[i] = FROM_8_TO_16(v); + } + break; + + // One word 0..65535 + case 2: + if (!_cmsReadUInt16Array(io, nElems, Curves[n]->Table16)) goto Error; + break; + + // Unsupported + default: + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported bit depth for VCGT '%d'", nBytes * 8); + goto Error; + } + } // For all 3 channels + } + break; + + // In this case, gamma is stored as a formula + case cmsVideoCardGammaFormulaType: + { + _cmsVCGTGAMMA Colorant[3]; + + // Populate tone curves + for (n=0; n < 3; n++) { + + double Params[10]; + + if (!_cmsRead15Fixed16Number(io, &Colorant[n].Gamma)) goto Error; + if (!_cmsRead15Fixed16Number(io, &Colorant[n].Min)) goto Error; + if (!_cmsRead15Fixed16Number(io, &Colorant[n].Max)) goto Error; + + // Parametric curve type 5 is: + // Y = (aX + b)^Gamma + e | X >= d + // Y = cX + f | X < d + + // vcgt formula is: + // Y = (Max ?Min) * (X ^ Gamma) + Min + + // So, the translation is + // a = (Max ?Min) ^ ( 1 / Gamma) + // e = Min + // b=c=d=f=0 + + Params[0] = Colorant[n].Gamma; + Params[1] = pow((Colorant[n].Max - Colorant[n].Min), (1.0 / Colorant[n].Gamma)); + Params[2] = 0; + Params[3] = 0; + Params[4] = 0; + Params[5] = Colorant[n].Min; + Params[6] = 0; + + Curves[n] = cmsBuildParametricToneCurve(self ->ContextID, 5, Params); + if (Curves[n] == NULL) goto Error; + } + } + break; + + // Unsupported + default: + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported tag type for VCGT '%d'", TagType); + goto Error; + } + + *nItems = 1; + return (void*) Curves; + +// Regret, free all resources +Error: + + cmsFreeToneCurveTriple(Curves); + _cmsFree(self ->ContextID, Curves); + return NULL; + + cmsUNUSED_PARAMETER(SizeOfTag); +} + + +// We don't support all flavors, only 16bits tables and formula +static +cmsBool Type_vcgt_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsToneCurve** Curves = (cmsToneCurve**) Ptr; + cmsUInt32Number i, j; + + if (cmsGetToneCurveParametricType(Curves[0]) == 5 && + cmsGetToneCurveParametricType(Curves[1]) == 5 && + cmsGetToneCurveParametricType(Curves[2]) == 5) { + + if (!_cmsWriteUInt32Number(io, cmsVideoCardGammaFormulaType)) return FALSE; + + // Save parameters + for (i=0; i < 3; i++) { + + _cmsVCGTGAMMA v; + + v.Gamma = Curves[i] ->Segments[0].Params[0]; + v.Min = Curves[i] ->Segments[0].Params[5]; + v.Max = pow(Curves[i] ->Segments[0].Params[1], v.Gamma) + v.Min; + + if (!_cmsWrite15Fixed16Number(io, v.Gamma)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, v.Min)) return FALSE; + if (!_cmsWrite15Fixed16Number(io, v.Max)) return FALSE; + } + } + + else { + + // Always store as a table of 256 words + if (!_cmsWriteUInt32Number(io, cmsVideoCardGammaTableType)) return FALSE; + if (!_cmsWriteUInt16Number(io, 3)) return FALSE; + if (!_cmsWriteUInt16Number(io, 256)) return FALSE; + if (!_cmsWriteUInt16Number(io, 2)) return FALSE; + + for (i=0; i < 3; i++) { + for (j=0; j < 256; j++) { + + cmsFloat32Number v = cmsEvalToneCurveFloat(Curves[i], (cmsFloat32Number) (j / 255.0)); + cmsUInt16Number n = _cmsQuickSaturateWord(v * 65535.0); + + if (!_cmsWriteUInt16Number(io, n)) return FALSE; + } + } + } + + return TRUE; + + cmsUNUSED_PARAMETER(self); + cmsUNUSED_PARAMETER(nItems); +} + +static +void* Type_vcgt_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + cmsToneCurve** OldCurves = (cmsToneCurve**) Ptr; + cmsToneCurve** NewCurves; + + NewCurves = ( cmsToneCurve**) _cmsCalloc(self ->ContextID, 3, sizeof(cmsToneCurve*)); + if (NewCurves == NULL) return NULL; + + NewCurves[0] = cmsDupToneCurve(OldCurves[0]); + NewCurves[1] = cmsDupToneCurve(OldCurves[1]); + NewCurves[2] = cmsDupToneCurve(OldCurves[2]); + + return (void*) NewCurves; + + cmsUNUSED_PARAMETER(n); +} + + +static +void Type_vcgt_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsFreeToneCurveTriple((cmsToneCurve**) Ptr); + _cmsFree(self ->ContextID, Ptr); +} + + +// ******************************************************************************** +// Type cmsSigDictType +// ******************************************************************************** + +// Single column of the table can point to wchar or MLUC elements. Holds arrays of data +typedef struct { + cmsContext ContextID; + cmsUInt32Number *Offsets; + cmsUInt32Number *Sizes; +} _cmsDICelem; + +typedef struct { + _cmsDICelem Name, Value, DisplayName, DisplayValue; + +} _cmsDICarray; + +// Allocate an empty array element +static +cmsBool AllocElem(cmsContext ContextID, _cmsDICelem* e, cmsUInt32Number Count) +{ + e->Offsets = (cmsUInt32Number *) _cmsCalloc(ContextID, Count, sizeof(cmsUInt32Number)); + if (e->Offsets == NULL) return FALSE; + + e->Sizes = (cmsUInt32Number *) _cmsCalloc(ContextID, Count, sizeof(cmsUInt32Number)); + if (e->Sizes == NULL) { + + _cmsFree(ContextID, e -> Offsets); + return FALSE; + } + + e ->ContextID = ContextID; + return TRUE; +} + +// Free an array element +static +void FreeElem(_cmsDICelem* e) +{ + if (e ->Offsets != NULL) _cmsFree(e -> ContextID, e -> Offsets); + if (e ->Sizes != NULL) _cmsFree(e -> ContextID, e -> Sizes); + e->Offsets = e ->Sizes = NULL; +} + +// Get rid of whole array +static +void FreeArray( _cmsDICarray* a) +{ + if (a ->Name.Offsets != NULL) FreeElem(&a->Name); + if (a ->Value.Offsets != NULL) FreeElem(&a ->Value); + if (a ->DisplayName.Offsets != NULL) FreeElem(&a->DisplayName); + if (a ->DisplayValue.Offsets != NULL) FreeElem(&a ->DisplayValue); +} + + +// Allocate whole array +static +cmsBool AllocArray(cmsContext ContextID, _cmsDICarray* a, cmsUInt32Number Count, cmsUInt32Number Length) +{ + // Empty values + memset(a, 0, sizeof(_cmsDICarray)); + + // On depending on record size, create column arrays + if (!AllocElem(ContextID, &a ->Name, Count)) goto Error; + if (!AllocElem(ContextID, &a ->Value, Count)) goto Error; + + if (Length > 16) { + if (!AllocElem(ContextID, &a -> DisplayName, Count)) goto Error; + + } + if (Length > 24) { + if (!AllocElem(ContextID, &a ->DisplayValue, Count)) goto Error; + } + return TRUE; + +Error: + FreeArray(a); + return FALSE; +} + +// Read one element +static +cmsBool ReadOneElem(cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i, cmsUInt32Number BaseOffset) +{ + if (!_cmsReadUInt32Number(io, &e->Offsets[i])) return FALSE; + if (!_cmsReadUInt32Number(io, &e ->Sizes[i])) return FALSE; + + // An offset of zero has special meaning and shal be preserved + if (e ->Offsets[i] > 0) + e ->Offsets[i] += BaseOffset; + return TRUE; +} + + +static +cmsBool ReadOffsetArray(cmsIOHANDLER* io, _cmsDICarray* a, cmsUInt32Number Count, cmsUInt32Number Length, cmsUInt32Number BaseOffset) +{ + cmsUInt32Number i; + + // Read column arrays + for (i=0; i < Count; i++) { + + if (!ReadOneElem(io, &a -> Name, i, BaseOffset)) return FALSE; + if (!ReadOneElem(io, &a -> Value, i, BaseOffset)) return FALSE; + + if (Length > 16) { + + if (!ReadOneElem(io, &a ->DisplayName, i, BaseOffset)) return FALSE; + + } + + if (Length > 24) { + + if (!ReadOneElem(io, & a -> DisplayValue, i, BaseOffset)) return FALSE; + } + } + return TRUE; +} + + +// Write one element +static +cmsBool WriteOneElem(cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i) +{ + if (!_cmsWriteUInt32Number(io, e->Offsets[i])) return FALSE; + if (!_cmsWriteUInt32Number(io, e ->Sizes[i])) return FALSE; + + return TRUE; +} + +static +cmsBool WriteOffsetArray(cmsIOHANDLER* io, _cmsDICarray* a, cmsUInt32Number Count, cmsUInt32Number Length) +{ + cmsUInt32Number i; + + for (i=0; i < Count; i++) { + + if (!WriteOneElem(io, &a -> Name, i)) return FALSE; + if (!WriteOneElem(io, &a -> Value, i)) return FALSE; + + if (Length > 16) { + + if (!WriteOneElem(io, &a -> DisplayName, i)) return FALSE; + } + + if (Length > 24) { + + if (!WriteOneElem(io, &a -> DisplayValue, i)) return FALSE; + } + } + + return TRUE; +} + +static +cmsBool ReadOneWChar(cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i, wchar_t ** wcstr) +{ + + cmsUInt32Number nChars; + + // Special case for undefined strings (see ICC Votable + // Proposal Submission, Dictionary Type and Metadata TAG Definition) + if (e -> Offsets[i] == 0) { + + *wcstr = NULL; + return TRUE; + } + + if (!io -> Seek(io, e -> Offsets[i])) return FALSE; + + nChars = e ->Sizes[i] / sizeof(cmsUInt16Number); + + + *wcstr = (wchar_t*) _cmsMallocZero(e ->ContextID, (nChars + 1) * sizeof(wchar_t)); + if (*wcstr == NULL) return FALSE; + + if (!_cmsReadWCharArray(io, nChars, *wcstr)) { + _cmsFree(e ->ContextID, *wcstr); + return FALSE; + } + + // End of string marker + (*wcstr)[nChars] = 0; + return TRUE; +} + +static +cmsUInt32Number mywcslen(const wchar_t *s) +{ + const wchar_t *p; + + p = s; + while (*p) + p++; + + return (cmsUInt32Number)(p - s); +} + +static +cmsBool WriteOneWChar(cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i, const wchar_t * wcstr, cmsUInt32Number BaseOffset) +{ + cmsUInt32Number Before = io ->Tell(io); + cmsUInt32Number n; + + e ->Offsets[i] = Before - BaseOffset; + + if (wcstr == NULL) { + e ->Sizes[i] = 0; + e ->Offsets[i] = 0; + return TRUE; + } + + n = mywcslen(wcstr); + if (!_cmsWriteWCharArray(io, n, wcstr)) return FALSE; + + e ->Sizes[i] = io ->Tell(io) - Before; + return TRUE; +} + +static +cmsBool ReadOneMLUC(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i, cmsMLU** mlu) +{ + cmsUInt32Number nItems = 0; + + // A way to get null MLUCs + if (e -> Offsets[i] == 0 || e ->Sizes[i] == 0) { + + *mlu = NULL; + return TRUE; + } + + if (!io -> Seek(io, e -> Offsets[i])) return FALSE; + + *mlu = (cmsMLU*) Type_MLU_Read(self, io, &nItems, e ->Sizes[i]); + return *mlu != NULL; +} + +static +cmsBool WriteOneMLUC(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i, const cmsMLU* mlu, cmsUInt32Number BaseOffset) +{ + cmsUInt32Number Before; + + // Special case for undefined strings (see ICC Votable + // Proposal Submission, Dictionary Type and Metadata TAG Definition) + if (mlu == NULL) { + e ->Sizes[i] = 0; + e ->Offsets[i] = 0; + return TRUE; + } + + Before = io ->Tell(io); + e ->Offsets[i] = Before - BaseOffset; + + if (!Type_MLU_Write(self, io, (void*) mlu, 1)) return FALSE; + + e ->Sizes[i] = io ->Tell(io) - Before; + return TRUE; +} + + +static +void *Type_Dictionary_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsHANDLE hDict; + cmsUInt32Number i, Count, Length; + cmsUInt32Number BaseOffset; + _cmsDICarray a; + wchar_t *NameWCS = NULL, *ValueWCS = NULL; + cmsMLU *DisplayNameMLU = NULL, *DisplayValueMLU=NULL; + cmsBool rc; + + *nItems = 0; + + // Get actual position as a basis for element offsets + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + // Get name-value record count + if (!_cmsReadUInt32Number(io, &Count)) return NULL; + SizeOfTag -= sizeof(cmsUInt32Number); + + // Get rec length + if (!_cmsReadUInt32Number(io, &Length)) return NULL; + SizeOfTag -= sizeof(cmsUInt32Number); + + // Check for valid lengths + if (Length != 16 && Length != 24 && Length != 32) { + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown record length in dictionary '%d'", Length); + return NULL; + } + + // Creates an empty dictionary + hDict = cmsDictAlloc(self -> ContextID); + if (hDict == NULL) return NULL; + + // On depending on record size, create column arrays + if (!AllocArray(self -> ContextID, &a, Count, Length)) goto Error; + + // Read column arrays + if (!ReadOffsetArray(io, &a, Count, Length, BaseOffset)) goto Error; + + // Seek to each element and read it + for (i=0; i < Count; i++) { + + if (!ReadOneWChar(io, &a.Name, i, &NameWCS)) goto Error; + if (!ReadOneWChar(io, &a.Value, i, &ValueWCS)) goto Error; + + if (Length > 16) { + if (!ReadOneMLUC(self, io, &a.DisplayName, i, &DisplayNameMLU)) goto Error; + } + + if (Length > 24) { + if (!ReadOneMLUC(self, io, &a.DisplayValue, i, &DisplayValueMLU)) goto Error; + } + + if (NameWCS == NULL || ValueWCS == NULL) { + + cmsSignalError(self->ContextID, cmsERROR_CORRUPTION_DETECTED, "Bad dictionary Name/Value"); + rc = FALSE; + } + else { + + rc = cmsDictAddEntry(hDict, NameWCS, ValueWCS, DisplayNameMLU, DisplayValueMLU); + } + + if (NameWCS != NULL) _cmsFree(self ->ContextID, NameWCS); + if (ValueWCS != NULL) _cmsFree(self ->ContextID, ValueWCS); + if (DisplayNameMLU != NULL) cmsMLUfree(DisplayNameMLU); + if (DisplayValueMLU != NULL) cmsMLUfree(DisplayValueMLU); + + if (!rc) goto Error; + } + + FreeArray(&a); + *nItems = 1; + return (void*) hDict; + +Error: + FreeArray(&a); + cmsDictFree(hDict); + return NULL; +} + + +static +cmsBool Type_Dictionary_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsHANDLE hDict = (cmsHANDLE) Ptr; + const cmsDICTentry* p; + cmsBool AnyName, AnyValue; + cmsUInt32Number i, Count, Length; + cmsUInt32Number DirectoryPos, CurrentPos, BaseOffset; + _cmsDICarray a; + + if (hDict == NULL) return FALSE; + + BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); + + // Let's inspect the dictionary + Count = 0; AnyName = FALSE; AnyValue = FALSE; + for (p = cmsDictGetEntryList(hDict); p != NULL; p = cmsDictNextEntry(p)) { + + if (p ->DisplayName != NULL) AnyName = TRUE; + if (p ->DisplayValue != NULL) AnyValue = TRUE; + Count++; + } + + Length = 16; + if (AnyName) Length += 8; + if (AnyValue) Length += 8; + + if (!_cmsWriteUInt32Number(io, Count)) return FALSE; + if (!_cmsWriteUInt32Number(io, Length)) return FALSE; + + // Keep starting position of offsets table + DirectoryPos = io ->Tell(io); + + // Allocate offsets array + if (!AllocArray(self ->ContextID, &a, Count, Length)) goto Error; + + // Write a fake directory to be filled latter on + if (!WriteOffsetArray(io, &a, Count, Length)) goto Error; + + // Write each element. Keep track of the size as well. + p = cmsDictGetEntryList(hDict); + for (i=0; i < Count; i++) { + + if (!WriteOneWChar(io, &a.Name, i, p ->Name, BaseOffset)) goto Error; + if (!WriteOneWChar(io, &a.Value, i, p ->Value, BaseOffset)) goto Error; + + if (p ->DisplayName != NULL) { + if (!WriteOneMLUC(self, io, &a.DisplayName, i, p ->DisplayName, BaseOffset)) goto Error; + } + + if (p ->DisplayValue != NULL) { + if (!WriteOneMLUC(self, io, &a.DisplayValue, i, p ->DisplayValue, BaseOffset)) goto Error; + } + + p = cmsDictNextEntry(p); + } + + // Write the directory + CurrentPos = io ->Tell(io); + if (!io ->Seek(io, DirectoryPos)) goto Error; + + if (!WriteOffsetArray(io, &a, Count, Length)) goto Error; + + if (!io ->Seek(io, CurrentPos)) goto Error; + + FreeArray(&a); + return TRUE; + +Error: + FreeArray(&a); + return FALSE; + + cmsUNUSED_PARAMETER(nItems); +} + + +static +void* Type_Dictionary_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) +{ + return (void*) cmsDictDup((cmsHANDLE) Ptr); + + cmsUNUSED_PARAMETER(n); + cmsUNUSED_PARAMETER(self); +} + + +static +void Type_Dictionary_Free(struct _cms_typehandler_struct* self, void* Ptr) +{ + cmsDictFree((cmsHANDLE) Ptr); + cmsUNUSED_PARAMETER(self); +} + + +// ******************************************************************************** +// Type support main routines +// ******************************************************************************** + + +// This is the list of built-in types +static _cmsTagTypeLinkedList SupportedTagTypes[] = { + +{TYPE_HANDLER(cmsSigChromaticityType, Chromaticity), &SupportedTagTypes[1] }, +{TYPE_HANDLER(cmsSigColorantOrderType, ColorantOrderType), &SupportedTagTypes[2] }, +{TYPE_HANDLER(cmsSigS15Fixed16ArrayType, S15Fixed16), &SupportedTagTypes[3] }, +{TYPE_HANDLER(cmsSigU16Fixed16ArrayType, U16Fixed16), &SupportedTagTypes[4] }, +{TYPE_HANDLER(cmsSigTextType, Text), &SupportedTagTypes[5] }, +{TYPE_HANDLER(cmsSigTextDescriptionType, Text_Description), &SupportedTagTypes[6] }, +{TYPE_HANDLER(cmsSigCurveType, Curve), &SupportedTagTypes[7] }, +{TYPE_HANDLER(cmsSigParametricCurveType, ParametricCurve), &SupportedTagTypes[8] }, +{TYPE_HANDLER(cmsSigDateTimeType, DateTime), &SupportedTagTypes[9] }, +{TYPE_HANDLER(cmsSigLut8Type, LUT8), &SupportedTagTypes[10] }, +{TYPE_HANDLER(cmsSigLut16Type, LUT16), &SupportedTagTypes[11] }, +{TYPE_HANDLER(cmsSigColorantTableType, ColorantTable), &SupportedTagTypes[12] }, +{TYPE_HANDLER(cmsSigNamedColor2Type, NamedColor), &SupportedTagTypes[13] }, +{TYPE_HANDLER(cmsSigMultiLocalizedUnicodeType, MLU), &SupportedTagTypes[14] }, +{TYPE_HANDLER(cmsSigProfileSequenceDescType, ProfileSequenceDesc), &SupportedTagTypes[15] }, +{TYPE_HANDLER(cmsSigSignatureType, Signature), &SupportedTagTypes[16] }, +{TYPE_HANDLER(cmsSigMeasurementType, Measurement), &SupportedTagTypes[17] }, +{TYPE_HANDLER(cmsSigDataType, Data), &SupportedTagTypes[18] }, +{TYPE_HANDLER(cmsSigLutAtoBType, LUTA2B), &SupportedTagTypes[19] }, +{TYPE_HANDLER(cmsSigLutBtoAType, LUTB2A), &SupportedTagTypes[20] }, +{TYPE_HANDLER(cmsSigUcrBgType, UcrBg), &SupportedTagTypes[21] }, +{TYPE_HANDLER(cmsSigCrdInfoType, CrdInfo), &SupportedTagTypes[22] }, +{TYPE_HANDLER(cmsSigMultiProcessElementType, MPE), &SupportedTagTypes[23] }, +{TYPE_HANDLER(cmsSigScreeningType, Screening), &SupportedTagTypes[24] }, +{TYPE_HANDLER(cmsSigViewingConditionsType, ViewingConditions), &SupportedTagTypes[25] }, +{TYPE_HANDLER(cmsSigXYZType, XYZ), &SupportedTagTypes[26] }, +{TYPE_HANDLER(cmsCorbisBrokenXYZtype, XYZ), &SupportedTagTypes[27] }, +{TYPE_HANDLER(cmsMonacoBrokenCurveType, Curve), &SupportedTagTypes[28] }, +{TYPE_HANDLER(cmsSigProfileSequenceIdType, ProfileSequenceId), &SupportedTagTypes[29] }, +{TYPE_HANDLER(cmsSigDictType, Dictionary), &SupportedTagTypes[30] }, +{TYPE_HANDLER(cmsSigVcgtType, vcgt), NULL } +}; + + +_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, TagTypePlugin); +} + +cmsBool _cmsRegisterMultiProcessElementPlugin(cmsContext id, cmsPluginBase* Data) +{ + return RegisterTypesPlugin(id, Data,MPEPlugin); +} + + +// Wrapper for tag types +cmsTagTypeHandler* _cmsGetTagTypeHandler(cmsContext ContextID, cmsTagTypeSignature sig) +{ + _cmsTagTypePluginChunkType* ctx = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(ContextID, TagTypePlugin); + + return GetHandler(sig, ctx->TagTypes, SupportedTagTypes); +} + +// ******************************************************************************** +// Tag support main routines +// ******************************************************************************** + +typedef struct _cmsTagLinkedList_st { + + cmsTagSignature Signature; + cmsTagDescriptor Descriptor; + struct _cmsTagLinkedList_st* Next; + +} _cmsTagLinkedList; + +// This is the list of built-in tags +static _cmsTagLinkedList SupportedTags[] = { + + { cmsSigAToB0Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutAtoBType, cmsSigLut8Type}, DecideLUTtypeA2B}, &SupportedTags[1]}, + { cmsSigAToB1Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutAtoBType, cmsSigLut8Type}, DecideLUTtypeA2B}, &SupportedTags[2]}, + { cmsSigAToB2Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutAtoBType, cmsSigLut8Type}, DecideLUTtypeA2B}, &SupportedTags[3]}, + { cmsSigBToA0Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type}, DecideLUTtypeB2A}, &SupportedTags[4]}, + { cmsSigBToA1Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type}, DecideLUTtypeB2A}, &SupportedTags[5]}, + { cmsSigBToA2Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type}, DecideLUTtypeB2A}, &SupportedTags[6]}, + + // Allow corbis and its broken XYZ type + { cmsSigRedColorantTag, { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, DecideXYZtype}, &SupportedTags[7]}, + { cmsSigGreenColorantTag, { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, DecideXYZtype}, &SupportedTags[8]}, + { cmsSigBlueColorantTag, { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, DecideXYZtype}, &SupportedTags[9]}, + + { cmsSigRedTRCTag, { 1, 3, { cmsSigCurveType, cmsSigParametricCurveType, cmsMonacoBrokenCurveType }, DecideCurveType}, &SupportedTags[10]}, + { cmsSigGreenTRCTag, { 1, 3, { cmsSigCurveType, cmsSigParametricCurveType, cmsMonacoBrokenCurveType }, DecideCurveType}, &SupportedTags[11]}, + { cmsSigBlueTRCTag, { 1, 3, { cmsSigCurveType, cmsSigParametricCurveType, cmsMonacoBrokenCurveType }, DecideCurveType}, &SupportedTags[12]}, + + { cmsSigCalibrationDateTimeTag, { 1, 1, { cmsSigDateTimeType }, NULL}, &SupportedTags[13]}, + { cmsSigCharTargetTag, { 1, 1, { cmsSigTextType }, NULL}, &SupportedTags[14]}, + + { cmsSigChromaticAdaptationTag, { 9, 1, { cmsSigS15Fixed16ArrayType }, NULL}, &SupportedTags[15]}, + { cmsSigChromaticityTag, { 1, 1, { cmsSigChromaticityType }, NULL}, &SupportedTags[16]}, + { cmsSigColorantOrderTag, { 1, 1, { cmsSigColorantOrderType }, NULL}, &SupportedTags[17]}, + { cmsSigColorantTableTag, { 1, 1, { cmsSigColorantTableType }, NULL}, &SupportedTags[18]}, + { cmsSigColorantTableOutTag, { 1, 1, { cmsSigColorantTableType }, NULL}, &SupportedTags[19]}, + + { cmsSigCopyrightTag, { 1, 3, { cmsSigTextType, cmsSigMultiLocalizedUnicodeType, cmsSigTextDescriptionType}, DecideTextType}, &SupportedTags[20]}, + { cmsSigDateTimeTag, { 1, 1, { cmsSigDateTimeType }, NULL}, &SupportedTags[21]}, + + { cmsSigDeviceMfgDescTag, { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[22]}, + { cmsSigDeviceModelDescTag, { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[23]}, + + { cmsSigGamutTag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[24]}, + + { cmsSigGrayTRCTag, { 1, 2, { cmsSigCurveType, cmsSigParametricCurveType }, DecideCurveType}, &SupportedTags[25]}, + { cmsSigLuminanceTag, { 1, 1, { cmsSigXYZType }, NULL}, &SupportedTags[26]}, + + { cmsSigMediaBlackPointTag, { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, NULL}, &SupportedTags[27]}, + { cmsSigMediaWhitePointTag, { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, NULL}, &SupportedTags[28]}, + + { cmsSigNamedColor2Tag, { 1, 1, { cmsSigNamedColor2Type }, NULL}, &SupportedTags[29]}, + + { cmsSigPreview0Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[30]}, + { cmsSigPreview1Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[31]}, + { cmsSigPreview2Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[32]}, + + { cmsSigProfileDescriptionTag, { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[33]}, + { cmsSigProfileSequenceDescTag, { 1, 1, { cmsSigProfileSequenceDescType }, NULL}, &SupportedTags[34]}, + { cmsSigTechnologyTag, { 1, 1, { cmsSigSignatureType }, NULL}, &SupportedTags[35]}, + + { cmsSigColorimetricIntentImageStateTag, { 1, 1, { cmsSigSignatureType }, NULL}, &SupportedTags[36]}, + { cmsSigPerceptualRenderingIntentGamutTag, { 1, 1, { cmsSigSignatureType }, NULL}, &SupportedTags[37]}, + { cmsSigSaturationRenderingIntentGamutTag, { 1, 1, { cmsSigSignatureType }, NULL}, &SupportedTags[38]}, + + { cmsSigMeasurementTag, { 1, 1, { cmsSigMeasurementType }, NULL}, &SupportedTags[39]}, + + { cmsSigPs2CRD0Tag, { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[40]}, + { cmsSigPs2CRD1Tag, { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[41]}, + { cmsSigPs2CRD2Tag, { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[42]}, + { cmsSigPs2CRD3Tag, { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[43]}, + { cmsSigPs2CSATag, { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[44]}, + { cmsSigPs2RenderingIntentTag, { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[45]}, + + { cmsSigViewingCondDescTag, { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[46]}, + + { cmsSigUcrBgTag, { 1, 1, { cmsSigUcrBgType}, NULL}, &SupportedTags[47]}, + { cmsSigCrdInfoTag, { 1, 1, { cmsSigCrdInfoType}, NULL}, &SupportedTags[48]}, + + { cmsSigDToB0Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[49]}, + { cmsSigDToB1Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[50]}, + { cmsSigDToB2Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[51]}, + { cmsSigDToB3Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[52]}, + { cmsSigBToD0Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[53]}, + { cmsSigBToD1Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[54]}, + { cmsSigBToD2Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[55]}, + { cmsSigBToD3Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[56]}, + + { cmsSigScreeningDescTag, { 1, 1, { cmsSigTextDescriptionType }, NULL}, &SupportedTags[57]}, + { cmsSigViewingConditionsTag, { 1, 1, { cmsSigViewingConditionsType }, NULL}, &SupportedTags[58]}, + + { cmsSigScreeningTag, { 1, 1, { cmsSigScreeningType}, NULL }, &SupportedTags[59]}, + { cmsSigVcgtTag, { 1, 1, { cmsSigVcgtType}, NULL }, &SupportedTags[60]}, + { cmsSigMetaTag, { 1, 1, { cmsSigDictType}, NULL }, &SupportedTags[61]}, + { cmsSigProfileSequenceIdTag, { 1, 1, { cmsSigProfileSequenceIdType}, NULL }, &SupportedTags[62]}, + { cmsSigProfileDescriptionMLTag,{ 1, 1, { cmsSigMultiLocalizedUnicodeType}, NULL}, NULL} + + +}; + +/* + Not supported Why + ======================= ========================================= + cmsSigOutputResponseTag ==> WARNING, POSSIBLE PATENT ON THIS SUBJECT! + cmsSigNamedColorTag ==> Deprecated + cmsSigDataTag ==> Ancient, unused + cmsSigDeviceSettingsTag ==> Deprecated, useless +*/ + + +_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) +{ + _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) { + + _cmsTagLinkedList *newEntry = ( _cmsTagLinkedList *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTagLinkedList)); + + 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)); + } + +} + +cmsBool _cmsRegisterTagPlugin(cmsContext id, cmsPluginBase* Data) +{ + cmsPluginTag* Plugin = (cmsPluginTag*) Data; + _cmsTagLinkedList *pt; + _cmsTagPluginChunkType* TagPluginChunk = ( _cmsTagPluginChunkType*) _cmsContextGetClientChunk(id, TagPlugin); + + if (Data == NULL) { + + TagPluginChunk->Tag = NULL; + return TRUE; + } + + pt = (_cmsTagLinkedList*) _cmsPluginMalloc(id, sizeof(_cmsTagLinkedList)); + if (pt == NULL) return FALSE; + + pt ->Signature = Plugin ->Signature; + pt ->Descriptor = Plugin ->Descriptor; + pt ->Next = TagPluginChunk ->Tag; + + TagPluginChunk ->Tag = pt; + + return TRUE; +} + +// Return a descriptor for a given tag or NULL +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; + pt = pt ->Next) { + + if (sig == pt -> Signature) return &pt ->Descriptor; + } + + return NULL; +} diff --git a/third_party/lcms/src/cmsvirt.c b/third_party/lcms/src/cmsvirt.c new file mode 100644 index 0000000000..d19ace1651 --- /dev/null +++ b/third_party/lcms/src/cmsvirt.c @@ -0,0 +1,1194 @@ +//--------------------------------------------------------------------------------- +// +// 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 "lcms2_internal.h" + +// Virtual (built-in) profiles +// ----------------------------------------------------------------------------------- + +static +cmsBool SetTextTags(cmsHPROFILE hProfile, const wchar_t* Description) +{ + cmsMLU *DescriptionMLU, *CopyrightMLU; + cmsBool rc = FALSE; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + + DescriptionMLU = cmsMLUalloc(ContextID, 1); + CopyrightMLU = cmsMLUalloc(ContextID, 1); + + if (DescriptionMLU == NULL || CopyrightMLU == NULL) goto Error; + + if (!cmsMLUsetWide(DescriptionMLU, "en", "US", Description)) goto Error; + if (!cmsMLUsetWide(CopyrightMLU, "en", "US", L"No copyright, use freely")) goto Error; + + if (!cmsWriteTag(hProfile, cmsSigProfileDescriptionTag, DescriptionMLU)) goto Error; + if (!cmsWriteTag(hProfile, cmsSigCopyrightTag, CopyrightMLU)) goto Error; + + rc = TRUE; + +Error: + + if (DescriptionMLU) + cmsMLUfree(DescriptionMLU); + if (CopyrightMLU) + cmsMLUfree(CopyrightMLU); + return rc; +} + + +static +cmsBool SetSeqDescTag(cmsHPROFILE hProfile, const char* Model) +{ + cmsBool rc = FALSE; + cmsContext ContextID = cmsGetProfileContextID(hProfile); + cmsSEQ* Seq = cmsAllocProfileSequenceDescription(ContextID, 1); + + if (Seq == NULL) return FALSE; + + Seq->seq[0].deviceMfg = (cmsSignature) 0; + Seq->seq[0].deviceModel = (cmsSignature) 0; + +#ifdef CMS_DONT_USE_INT64 + Seq->seq[0].attributes[0] = 0; + Seq->seq[0].attributes[1] = 0; +#else + Seq->seq[0].attributes = 0; +#endif + + Seq->seq[0].technology = (cmsTechnologySignature) 0; + + cmsMLUsetASCII( Seq->seq[0].Manufacturer, cmsNoLanguage, cmsNoCountry, "Little CMS"); + cmsMLUsetASCII( Seq->seq[0].Model, cmsNoLanguage, cmsNoCountry, Model); + + if (!_cmsWriteProfileSequence(hProfile, Seq)) goto Error; + + rc = TRUE; + +Error: + if (Seq) + cmsFreeProfileSequenceDescription(Seq); + + return rc; +} + + + +// This function creates a profile based on White point, primaries and +// transfer functions. +cmsHPROFILE CMSEXPORT cmsCreateRGBProfileTHR(cmsContext ContextID, + const cmsCIExyY* WhitePoint, + const cmsCIExyYTRIPLE* Primaries, + cmsToneCurve* const TransferFunction[3]) +{ + cmsHPROFILE hICC; + cmsMAT3 MColorants; + cmsCIEXYZTRIPLE Colorants; + cmsCIExyY MaxWhite; + cmsMAT3 CHAD; + cmsCIEXYZ WhitePointXYZ; + + hICC = cmsCreateProfilePlaceholder(ContextID); + if (!hICC) // can't allocate + return NULL; + + cmsSetProfileVersion(hICC, 4.3); + + cmsSetDeviceClass(hICC, cmsSigDisplayClass); + cmsSetColorSpace(hICC, cmsSigRgbData); + cmsSetPCS(hICC, cmsSigXYZData); + + cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL); + + + // Implement profile using following tags: + // + // 1 cmsSigProfileDescriptionTag + // 2 cmsSigMediaWhitePointTag + // 3 cmsSigRedColorantTag + // 4 cmsSigGreenColorantTag + // 5 cmsSigBlueColorantTag + // 6 cmsSigRedTRCTag + // 7 cmsSigGreenTRCTag + // 8 cmsSigBlueTRCTag + // 9 Chromatic adaptation Tag + // This conforms a standard RGB DisplayProfile as says ICC, and then I add (As per addendum II) + // 10 cmsSigChromaticityTag + + + if (!SetTextTags(hICC, L"RGB built-in")) goto Error; + + if (WhitePoint) { + + if (!cmsWriteTag(hICC, cmsSigMediaWhitePointTag, cmsD50_XYZ())) goto Error; + + cmsxyY2XYZ(&WhitePointXYZ, WhitePoint); + _cmsAdaptationMatrix(&CHAD, NULL, &WhitePointXYZ, cmsD50_XYZ()); + + // This is a V4 tag, but many CMM does read and understand it no matter which version + if (!cmsWriteTag(hICC, cmsSigChromaticAdaptationTag, (void*) &CHAD)) goto Error; + } + + if (WhitePoint && Primaries) { + + MaxWhite.x = WhitePoint -> x; + MaxWhite.y = WhitePoint -> y; + MaxWhite.Y = 1.0; + + if (!_cmsBuildRGB2XYZtransferMatrix(&MColorants, &MaxWhite, Primaries)) goto Error; + + Colorants.Red.X = MColorants.v[0].n[0]; + Colorants.Red.Y = MColorants.v[1].n[0]; + Colorants.Red.Z = MColorants.v[2].n[0]; + + Colorants.Green.X = MColorants.v[0].n[1]; + Colorants.Green.Y = MColorants.v[1].n[1]; + Colorants.Green.Z = MColorants.v[2].n[1]; + + Colorants.Blue.X = MColorants.v[0].n[2]; + Colorants.Blue.Y = MColorants.v[1].n[2]; + Colorants.Blue.Z = MColorants.v[2].n[2]; + + if (!cmsWriteTag(hICC, cmsSigRedColorantTag, (void*) &Colorants.Red)) goto Error; + if (!cmsWriteTag(hICC, cmsSigBlueColorantTag, (void*) &Colorants.Blue)) goto Error; + if (!cmsWriteTag(hICC, cmsSigGreenColorantTag, (void*) &Colorants.Green)) goto Error; + } + + + if (TransferFunction) { + + // Tries to minimize space. Thanks to Richard Hughes for this nice idea + if (!cmsWriteTag(hICC, cmsSigRedTRCTag, (void*) TransferFunction[0])) goto Error; + + if (TransferFunction[1] == TransferFunction[0]) { + + if (!cmsLinkTag (hICC, cmsSigGreenTRCTag, cmsSigRedTRCTag)) goto Error; + + } else { + + if (!cmsWriteTag(hICC, cmsSigGreenTRCTag, (void*) TransferFunction[1])) goto Error; + } + + if (TransferFunction[2] == TransferFunction[0]) { + + if (!cmsLinkTag (hICC, cmsSigBlueTRCTag, cmsSigRedTRCTag)) goto Error; + + } else { + + if (!cmsWriteTag(hICC, cmsSigBlueTRCTag, (void*) TransferFunction[2])) goto Error; + } + } + + if (Primaries) { + if (!cmsWriteTag(hICC, cmsSigChromaticityTag, (void*) Primaries)) goto Error; + } + + + return hICC; + +Error: + if (hICC) + cmsCloseProfile(hICC); + return NULL; +} + +cmsHPROFILE CMSEXPORT cmsCreateRGBProfile(const cmsCIExyY* WhitePoint, + const cmsCIExyYTRIPLE* Primaries, + cmsToneCurve* const TransferFunction[3]) +{ + return cmsCreateRGBProfileTHR(NULL, WhitePoint, Primaries, TransferFunction); +} + + + +// This function creates a profile based on White point and transfer function. +cmsHPROFILE CMSEXPORT cmsCreateGrayProfileTHR(cmsContext ContextID, + const cmsCIExyY* WhitePoint, + const cmsToneCurve* TransferFunction) +{ + cmsHPROFILE hICC; + cmsCIEXYZ tmp; + + hICC = cmsCreateProfilePlaceholder(ContextID); + if (!hICC) // can't allocate + return NULL; + + cmsSetProfileVersion(hICC, 4.3); + + cmsSetDeviceClass(hICC, cmsSigDisplayClass); + cmsSetColorSpace(hICC, cmsSigGrayData); + cmsSetPCS(hICC, cmsSigXYZData); + cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL); + + + // Implement profile using following tags: + // + // 1 cmsSigProfileDescriptionTag + // 2 cmsSigMediaWhitePointTag + // 3 cmsSigGrayTRCTag + + // This conforms a standard Gray DisplayProfile + + // Fill-in the tags + + if (!SetTextTags(hICC, L"gray built-in")) goto Error; + + + if (WhitePoint) { + + cmsxyY2XYZ(&tmp, WhitePoint); + if (!cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*) &tmp)) goto Error; + } + + if (TransferFunction) { + + if (!cmsWriteTag(hICC, cmsSigGrayTRCTag, (void*) TransferFunction)) goto Error; + } + + return hICC; + +Error: + if (hICC) + cmsCloseProfile(hICC); + return NULL; +} + + + +cmsHPROFILE CMSEXPORT cmsCreateGrayProfile(const cmsCIExyY* WhitePoint, + const cmsToneCurve* TransferFunction) +{ + return cmsCreateGrayProfileTHR(NULL, WhitePoint, TransferFunction); +} + +// This is a devicelink operating in the target colorspace with as many transfer functions as components + +cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLinkTHR(cmsContext ContextID, + cmsColorSpaceSignature ColorSpace, + cmsToneCurve* const TransferFunctions[]) +{ + cmsHPROFILE hICC; + cmsPipeline* Pipeline; + int nChannels; + + hICC = cmsCreateProfilePlaceholder(ContextID); + if (!hICC) + return NULL; + + cmsSetProfileVersion(hICC, 4.3); + + cmsSetDeviceClass(hICC, cmsSigLinkClass); + cmsSetColorSpace(hICC, ColorSpace); + cmsSetPCS(hICC, ColorSpace); + + cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL); + + // Set up channels + nChannels = cmsChannelsOf(ColorSpace); + + // Creates a Pipeline with prelinearization step only + Pipeline = cmsPipelineAlloc(ContextID, nChannels, nChannels); + if (Pipeline == NULL) goto Error; + + + // Copy tables to Pipeline + if (!cmsPipelineInsertStage(Pipeline, cmsAT_BEGIN, cmsStageAllocToneCurves(ContextID, nChannels, TransferFunctions))) + goto Error; + + // Create tags + if (!SetTextTags(hICC, L"Linearization built-in")) goto Error; + if (!cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) Pipeline)) goto Error; + if (!SetSeqDescTag(hICC, "Linearization built-in")) goto Error; + + // Pipeline is already on virtual profile + cmsPipelineFree(Pipeline); + + // Ok, done + return hICC; + +Error: + cmsPipelineFree(Pipeline); + if (hICC) + cmsCloseProfile(hICC); + + + return NULL; +} + +cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLink(cmsColorSpaceSignature ColorSpace, + cmsToneCurve* const TransferFunctions[]) +{ + return cmsCreateLinearizationDeviceLinkTHR(NULL, ColorSpace, TransferFunctions); +} + +// Ink-limiting algorithm +// +// Sum = C + M + Y + K +// If Sum > InkLimit +// Ratio= 1 - (Sum - InkLimit) / (C + M + Y) +// if Ratio <0 +// Ratio=0 +// endif +// Else +// Ratio=1 +// endif +// +// C = Ratio * C +// M = Ratio * M +// Y = Ratio * Y +// K: Does not change + +static +int InkLimitingSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo) +{ + cmsFloat64Number InkLimit = *(cmsFloat64Number *) Cargo; + cmsFloat64Number SumCMY, SumCMYK, Ratio; + + InkLimit = (InkLimit * 655.35); + + SumCMY = In[0] + In[1] + In[2]; + SumCMYK = SumCMY + In[3]; + + if (SumCMYK > InkLimit) { + + Ratio = 1 - ((SumCMYK - InkLimit) / SumCMY); + if (Ratio < 0) + Ratio = 0; + } + else Ratio = 1; + + Out[0] = _cmsQuickSaturateWord(In[0] * Ratio); // C + Out[1] = _cmsQuickSaturateWord(In[1] * Ratio); // M + Out[2] = _cmsQuickSaturateWord(In[2] * Ratio); // Y + + Out[3] = In[3]; // K (untouched) + + return TRUE; +} + +// This is a devicelink operating in CMYK for ink-limiting + +cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLinkTHR(cmsContext ContextID, + cmsColorSpaceSignature ColorSpace, + cmsFloat64Number Limit) +{ + cmsHPROFILE hICC; + cmsPipeline* LUT; + cmsStage* CLUT; + int nChannels; + + if (ColorSpace != cmsSigCmykData) { + cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "InkLimiting: Only CMYK currently supported"); + return NULL; + } + + if (Limit < 0.0 || Limit > 400) { + + cmsSignalError(ContextID, cmsERROR_RANGE, "InkLimiting: Limit should be between 0..400"); + if (Limit < 0) Limit = 0; + if (Limit > 400) Limit = 400; + + } + + hICC = cmsCreateProfilePlaceholder(ContextID); + if (!hICC) // can't allocate + return NULL; + + cmsSetProfileVersion(hICC, 4.3); + + cmsSetDeviceClass(hICC, cmsSigLinkClass); + cmsSetColorSpace(hICC, ColorSpace); + cmsSetPCS(hICC, ColorSpace); + + cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL); + + + // Creates a Pipeline with 3D grid only + LUT = cmsPipelineAlloc(ContextID, 4, 4); + if (LUT == NULL) goto Error; + + + nChannels = cmsChannelsOf(ColorSpace); + + CLUT = cmsStageAllocCLut16bit(ContextID, 17, nChannels, nChannels, NULL); + if (CLUT == NULL) goto Error; + + if (!cmsStageSampleCLut16bit(CLUT, InkLimitingSampler, (void*) &Limit, 0)) goto Error; + + if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, nChannels)) || + !cmsPipelineInsertStage(LUT, cmsAT_END, CLUT) || + !cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, nChannels))) + goto Error; + + // Create tags + if (!SetTextTags(hICC, L"ink-limiting built-in")) goto Error; + + if (!cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) LUT)) goto Error; + if (!SetSeqDescTag(hICC, "ink-limiting built-in")) goto Error; + + // cmsPipeline is already on virtual profile + cmsPipelineFree(LUT); + + // Ok, done + return hICC; + +Error: + if (LUT != NULL) + cmsPipelineFree(LUT); + + if (hICC != NULL) + cmsCloseProfile(hICC); + + return NULL; +} + +cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLink(cmsColorSpaceSignature ColorSpace, cmsFloat64Number Limit) +{ + return cmsCreateInkLimitingDeviceLinkTHR(NULL, ColorSpace, Limit); +} + + +// Creates a fake Lab identity. +cmsHPROFILE CMSEXPORT cmsCreateLab2ProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint) +{ + cmsHPROFILE hProfile; + cmsPipeline* LUT = NULL; + + hProfile = cmsCreateRGBProfileTHR(ContextID, WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL); + if (hProfile == NULL) return NULL; + + cmsSetProfileVersion(hProfile, 2.1); + + cmsSetDeviceClass(hProfile, cmsSigAbstractClass); + cmsSetColorSpace(hProfile, cmsSigLabData); + cmsSetPCS(hProfile, cmsSigLabData); + + if (!SetTextTags(hProfile, L"Lab identity built-in")) return NULL; + + // An identity LUT is all we need + LUT = cmsPipelineAlloc(ContextID, 3, 3); + if (LUT == NULL) goto Error; + + if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCLut(ContextID, 3))) + goto Error; + + if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error; + cmsPipelineFree(LUT); + + return hProfile; + +Error: + + if (LUT != NULL) + cmsPipelineFree(LUT); + + if (hProfile != NULL) + cmsCloseProfile(hProfile); + + return NULL; +} + + +cmsHPROFILE CMSEXPORT cmsCreateLab2Profile(const cmsCIExyY* WhitePoint) +{ + return cmsCreateLab2ProfileTHR(NULL, WhitePoint); +} + + +// Creates a fake Lab V4 identity. +cmsHPROFILE CMSEXPORT cmsCreateLab4ProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint) +{ + cmsHPROFILE hProfile; + cmsPipeline* LUT = NULL; + + hProfile = cmsCreateRGBProfileTHR(ContextID, WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL); + if (hProfile == NULL) return NULL; + + cmsSetProfileVersion(hProfile, 4.3); + + cmsSetDeviceClass(hProfile, cmsSigAbstractClass); + cmsSetColorSpace(hProfile, cmsSigLabData); + cmsSetPCS(hProfile, cmsSigLabData); + + if (!SetTextTags(hProfile, L"Lab identity built-in")) goto Error; + + // An empty LUTs is all we need + LUT = cmsPipelineAlloc(ContextID, 3, 3); + if (LUT == NULL) goto Error; + + if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3))) + goto Error; + + if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error; + cmsPipelineFree(LUT); + + return hProfile; + +Error: + + if (LUT != NULL) + cmsPipelineFree(LUT); + + if (hProfile != NULL) + cmsCloseProfile(hProfile); + + return NULL; +} + +cmsHPROFILE CMSEXPORT cmsCreateLab4Profile(const cmsCIExyY* WhitePoint) +{ + return cmsCreateLab4ProfileTHR(NULL, WhitePoint); +} + + +// Creates a fake XYZ identity +cmsHPROFILE CMSEXPORT cmsCreateXYZProfileTHR(cmsContext ContextID) +{ + cmsHPROFILE hProfile; + cmsPipeline* LUT = NULL; + + hProfile = cmsCreateRGBProfileTHR(ContextID, cmsD50_xyY(), NULL, NULL); + if (hProfile == NULL) return NULL; + + cmsSetProfileVersion(hProfile, 4.3); + + cmsSetDeviceClass(hProfile, cmsSigAbstractClass); + cmsSetColorSpace(hProfile, cmsSigXYZData); + cmsSetPCS(hProfile, cmsSigXYZData); + + if (!SetTextTags(hProfile, L"XYZ identity built-in")) goto Error; + + // An identity LUT is all we need + LUT = cmsPipelineAlloc(ContextID, 3, 3); + if (LUT == NULL) goto Error; + + if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3))) + goto Error; + + if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error; + cmsPipelineFree(LUT); + + return hProfile; + +Error: + + if (LUT != NULL) + cmsPipelineFree(LUT); + + if (hProfile != NULL) + cmsCloseProfile(hProfile); + + return NULL; +} + + +cmsHPROFILE CMSEXPORT cmsCreateXYZProfile(void) +{ + return cmsCreateXYZProfileTHR(NULL); +} + + +//sRGB Curves are defined by: +// +//If R'sRGB,G'sRGB, B'sRGB < 0.04045 +// +// R = R'sRGB / 12.92 +// G = G'sRGB / 12.92 +// B = B'sRGB / 12.92 +// +// +//else if R'sRGB,G'sRGB, B'sRGB >= 0.04045 +// +// R = ((R'sRGB + 0.055) / 1.055)^2.4 +// G = ((G'sRGB + 0.055) / 1.055)^2.4 +// B = ((B'sRGB + 0.055) / 1.055)^2.4 + +static +cmsToneCurve* Build_sRGBGamma(cmsContext ContextID) +{ + cmsFloat64Number Parameters[5]; + + Parameters[0] = 2.4; + Parameters[1] = 1. / 1.055; + Parameters[2] = 0.055 / 1.055; + Parameters[3] = 1. / 12.92; + Parameters[4] = 0.04045; + + return cmsBuildParametricToneCurve(ContextID, 4, Parameters); +} + +// Create the ICC virtual profile for sRGB space +cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfileTHR(cmsContext ContextID) +{ + cmsCIExyY D65; + cmsCIExyYTRIPLE Rec709Primaries = { + {0.6400, 0.3300, 1.0}, + {0.3000, 0.6000, 1.0}, + {0.1500, 0.0600, 1.0} + }; + cmsToneCurve* Gamma22[3]; + cmsHPROFILE hsRGB; + + cmsWhitePointFromTemp(&D65, 6504); + Gamma22[0] = Gamma22[1] = Gamma22[2] = Build_sRGBGamma(ContextID); + if (Gamma22[0] == NULL) return NULL; + + hsRGB = cmsCreateRGBProfileTHR(ContextID, &D65, &Rec709Primaries, Gamma22); + cmsFreeToneCurve(Gamma22[0]); + if (hsRGB == NULL) return NULL; + + if (!SetTextTags(hsRGB, L"sRGB built-in")) { + cmsCloseProfile(hsRGB); + return NULL; + } + + return hsRGB; +} + +cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfile(void) +{ + return cmsCreate_sRGBProfileTHR(NULL); +} + + + +typedef struct { + cmsFloat64Number Brightness; + cmsFloat64Number Contrast; + cmsFloat64Number Hue; + cmsFloat64Number Saturation; + cmsCIEXYZ WPsrc, WPdest; + +} BCHSWADJUSTS, *LPBCHSWADJUSTS; + + +static +int bchswSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo) +{ + cmsCIELab LabIn, LabOut; + cmsCIELCh LChIn, LChOut; + cmsCIEXYZ XYZ; + LPBCHSWADJUSTS bchsw = (LPBCHSWADJUSTS) Cargo; + + + cmsLabEncoded2Float(&LabIn, In); + + + cmsLab2LCh(&LChIn, &LabIn); + + // Do some adjusts on LCh + + LChOut.L = LChIn.L * bchsw ->Contrast + bchsw ->Brightness; + LChOut.C = LChIn.C + bchsw -> Saturation; + LChOut.h = LChIn.h + bchsw -> Hue; + + + cmsLCh2Lab(&LabOut, &LChOut); + + // Move white point in Lab + + cmsLab2XYZ(&bchsw ->WPsrc, &XYZ, &LabOut); + cmsXYZ2Lab(&bchsw ->WPdest, &LabOut, &XYZ); + + // Back to encoded + + cmsFloat2LabEncoded(Out, &LabOut); + + return TRUE; +} + + +// Creates an abstract profile operating in Lab space for Brightness, +// contrast, Saturation and white point displacement + +cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfileTHR(cmsContext ContextID, + int nLUTPoints, + cmsFloat64Number Bright, + cmsFloat64Number Contrast, + cmsFloat64Number Hue, + cmsFloat64Number Saturation, + int TempSrc, + int TempDest) +{ + cmsHPROFILE hICC; + cmsPipeline* Pipeline; + BCHSWADJUSTS bchsw; + cmsCIExyY WhitePnt; + cmsStage* CLUT; + cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS]; + int i; + + bchsw.Brightness = Bright; + bchsw.Contrast = Contrast; + bchsw.Hue = Hue; + bchsw.Saturation = Saturation; + + cmsWhitePointFromTemp(&WhitePnt, TempSrc ); + cmsxyY2XYZ(&bchsw.WPsrc, &WhitePnt); + + cmsWhitePointFromTemp(&WhitePnt, TempDest); + cmsxyY2XYZ(&bchsw.WPdest, &WhitePnt); + + hICC = cmsCreateProfilePlaceholder(ContextID); + if (!hICC) // can't allocate + return NULL; + + + cmsSetDeviceClass(hICC, cmsSigAbstractClass); + cmsSetColorSpace(hICC, cmsSigLabData); + cmsSetPCS(hICC, cmsSigLabData); + + cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL); + + // Creates a Pipeline with 3D grid only + Pipeline = cmsPipelineAlloc(ContextID, 3, 3); + if (Pipeline == NULL) { + cmsCloseProfile(hICC); + return NULL; + } + + for (i=0; i < MAX_INPUT_DIMENSIONS; i++) Dimensions[i] = nLUTPoints; + CLUT = cmsStageAllocCLut16bitGranular(ContextID, Dimensions, 3, 3, NULL); + if (CLUT == NULL) return NULL; + + + if (!cmsStageSampleCLut16bit(CLUT, bchswSampler, (void*) &bchsw, 0)) { + + // Shouldn't reach here + goto Error; + } + + if (!cmsPipelineInsertStage(Pipeline, cmsAT_END, CLUT)) { + goto Error; + } + + // Create tags + if (!SetTextTags(hICC, L"BCHS built-in")) return NULL; + + cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*) cmsD50_XYZ()); + + cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) Pipeline); + + // Pipeline is already on virtual profile + cmsPipelineFree(Pipeline); + + // Ok, done + return hICC; + +Error: + cmsPipelineFree(Pipeline); + cmsCloseProfile(hICC); + return NULL; +} + + +CMSAPI cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfile(int nLUTPoints, + cmsFloat64Number Bright, + cmsFloat64Number Contrast, + cmsFloat64Number Hue, + cmsFloat64Number Saturation, + int TempSrc, + int TempDest) +{ + return cmsCreateBCHSWabstractProfileTHR(NULL, nLUTPoints, Bright, Contrast, Hue, Saturation, TempSrc, TempDest); +} + + +// Creates a fake NULL profile. This profile return 1 channel as always 0. +// Is useful only for gamut checking tricks +cmsHPROFILE CMSEXPORT cmsCreateNULLProfileTHR(cmsContext ContextID) +{ + cmsHPROFILE hProfile; + cmsPipeline* LUT = NULL; + cmsStage* PostLin; + cmsToneCurve* EmptyTab; + cmsUInt16Number Zero[2] = { 0, 0 }; + + hProfile = cmsCreateProfilePlaceholder(ContextID); + if (!hProfile) // can't allocate + return NULL; + + cmsSetProfileVersion(hProfile, 4.3); + + if (!SetTextTags(hProfile, L"NULL profile built-in")) goto Error; + + + + cmsSetDeviceClass(hProfile, cmsSigOutputClass); + cmsSetColorSpace(hProfile, cmsSigGrayData); + cmsSetPCS(hProfile, cmsSigLabData); + + // An empty LUTs is all we need + LUT = cmsPipelineAlloc(ContextID, 1, 1); + if (LUT == NULL) goto Error; + + EmptyTab = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero); + PostLin = cmsStageAllocToneCurves(ContextID, 1, &EmptyTab); + cmsFreeToneCurve(EmptyTab); + + if (!cmsPipelineInsertStage(LUT, cmsAT_END, PostLin)) + goto Error; + + if (!cmsWriteTag(hProfile, cmsSigBToA0Tag, (void*) LUT)) goto Error; + if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, cmsD50_XYZ())) goto Error; + + cmsPipelineFree(LUT); + return hProfile; + +Error: + + if (LUT != NULL) + cmsPipelineFree(LUT); + + if (hProfile != NULL) + cmsCloseProfile(hProfile); + + return NULL; +} + +cmsHPROFILE CMSEXPORT cmsCreateNULLProfile(void) +{ + return cmsCreateNULLProfileTHR(NULL); +} + + +static +int IsPCS(cmsColorSpaceSignature ColorSpace) +{ + return (ColorSpace == cmsSigXYZData || + ColorSpace == cmsSigLabData); +} + + +static +void FixColorSpaces(cmsHPROFILE hProfile, + cmsColorSpaceSignature ColorSpace, + cmsColorSpaceSignature PCS, + cmsUInt32Number dwFlags) +{ + if (dwFlags & cmsFLAGS_GUESSDEVICECLASS) { + + if (IsPCS(ColorSpace) && IsPCS(PCS)) { + + cmsSetDeviceClass(hProfile, cmsSigAbstractClass); + cmsSetColorSpace(hProfile, ColorSpace); + cmsSetPCS(hProfile, PCS); + return; + } + + if (IsPCS(ColorSpace) && !IsPCS(PCS)) { + + cmsSetDeviceClass(hProfile, cmsSigOutputClass); + cmsSetPCS(hProfile, ColorSpace); + cmsSetColorSpace(hProfile, PCS); + return; + } + + if (IsPCS(PCS) && !IsPCS(ColorSpace)) { + + cmsSetDeviceClass(hProfile, cmsSigInputClass); + cmsSetColorSpace(hProfile, ColorSpace); + cmsSetPCS(hProfile, PCS); + return; + } + } + + cmsSetDeviceClass(hProfile, cmsSigLinkClass); + cmsSetColorSpace(hProfile, ColorSpace); + cmsSetPCS(hProfile, PCS); +} + + + +// This function creates a named color profile dumping all the contents of transform to a single profile +// In this way, LittleCMS may be used to "group" several named color databases into a single profile. +// It has, however, several minor limitations. PCS is always Lab, which is not very critic since this +// is the normal PCS for named color profiles. +static +cmsHPROFILE CreateNamedColorDevicelink(cmsHTRANSFORM xform) +{ + _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform; + cmsHPROFILE hICC = NULL; + int i, nColors; + cmsNAMEDCOLORLIST *nc2 = NULL, *Original = NULL; + + // Create an empty placeholder + hICC = cmsCreateProfilePlaceholder(v->ContextID); + if (hICC == NULL) return NULL; + + // Critical information + cmsSetDeviceClass(hICC, cmsSigNamedColorClass); + cmsSetColorSpace(hICC, v ->ExitColorSpace); + cmsSetPCS(hICC, cmsSigLabData); + + // Tag profile with information + if (!SetTextTags(hICC, L"Named color devicelink")) goto Error; + + Original = cmsGetNamedColorList(xform); + if (Original == NULL) goto Error; + + nColors = cmsNamedColorCount(Original); + nc2 = cmsDupNamedColorList(Original); + if (nc2 == NULL) goto Error; + + // Colorant count now depends on the output space + nc2 ->ColorantCount = cmsPipelineOutputChannels(v ->Lut); + + // Make sure we have proper formatters + cmsChangeBuffersFormat(xform, TYPE_NAMED_COLOR_INDEX, + FLOAT_SH(0) | COLORSPACE_SH(_cmsLCMScolorSpace(v ->ExitColorSpace)) + | BYTES_SH(2) | CHANNELS_SH(cmsChannelsOf(v ->ExitColorSpace))); + + // Apply the transfor to colorants. + for (i=0; i < nColors; i++) { + cmsDoTransform(xform, &i, nc2 ->List[i].DeviceColorant, 1); + } + + if (!cmsWriteTag(hICC, cmsSigNamedColor2Tag, (void*) nc2)) goto Error; + cmsFreeNamedColorList(nc2); + + return hICC; + +Error: + if (hICC != NULL) cmsCloseProfile(hICC); + return NULL; +} + + +// This structure holds information about which MPU can be stored on a profile based on the version + +typedef struct { + cmsBool IsV4; // Is a V4 tag? + cmsTagSignature RequiredTag; // Set to 0 for both types + cmsTagTypeSignature LutType; // The LUT type + int nTypes; // Number of types (up to 5) + cmsStageSignature MpeTypes[5]; // 5 is the maximum number + +} cmsAllowedLUT; + +static const cmsAllowedLUT AllowedLUTTypes[] = { + + { 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 }}, + { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 3, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType } }, + { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } }, + { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 5, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }}, + { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 1, { cmsSigCurveSetElemType }}, + { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 3, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }}, + { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }}, + { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 5, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }} +}; + +#define SIZE_OF_ALLOWED_LUT (sizeof(AllowedLUTTypes)/sizeof(cmsAllowedLUT)) + +// Check a single entry +static +cmsBool CheckOne(const cmsAllowedLUT* Tab, const cmsPipeline* Lut) +{ + cmsStage* mpe; + int n; + + for (n=0, mpe = Lut ->Elements; mpe != NULL; mpe = mpe ->Next, n++) { + + if (n > Tab ->nTypes) return FALSE; + if (cmsStageType(mpe) != Tab ->MpeTypes[n]) return FALSE; + } + + return (n == Tab ->nTypes); +} + + +static +const cmsAllowedLUT* FindCombination(const cmsPipeline* Lut, cmsBool IsV4, cmsTagSignature DestinationTag) +{ + cmsUInt32Number n; + + for (n=0; n < SIZE_OF_ALLOWED_LUT; n++) { + + const cmsAllowedLUT* Tab = AllowedLUTTypes + n; + + if (IsV4 ^ Tab -> IsV4) continue; + if ((Tab ->RequiredTag != 0) && (Tab ->RequiredTag != DestinationTag)) continue; + + if (CheckOne(Tab, Lut)) return Tab; + } + + return NULL; +} + + +// Does convert a transform into a device link profile +cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat64Number Version, cmsUInt32Number dwFlags) +{ + cmsHPROFILE hProfile = NULL; + cmsUInt32Number FrmIn, FrmOut, ChansIn, ChansOut; + cmsUInt32Number ColorSpaceBitsIn, ColorSpaceBitsOut; + _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; + cmsPipeline* LUT = NULL; + cmsStage* mpe; + cmsContext ContextID = cmsGetTransformContextID(hTransform); + const cmsAllowedLUT* AllowedLUT; + cmsTagSignature DestinationTag; + cmsProfileClassSignature deviceClass; + + _cmsAssert(hTransform != NULL); + + // Get the first mpe to check for named color + mpe = cmsPipelineGetPtrToFirstStage(xform ->Lut); + + // Check if is a named color transform + if (mpe != NULL) { + + if (cmsStageType(mpe) == cmsSigNamedColorElemType) { + return CreateNamedColorDevicelink(hTransform); + } + } + + // First thing to do is to get a copy of the transformation + LUT = cmsPipelineDup(xform ->Lut); + if (LUT == NULL) return NULL; + + // Time to fix the Lab2/Lab4 issue. + if ((xform ->EntryColorSpace == cmsSigLabData) && (Version < 4.0)) { + + if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocLabV2ToV4curves(ContextID))) + goto Error; + } + + // On the output side too + if ((xform ->ExitColorSpace) == cmsSigLabData && (Version < 4.0)) { + + if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocLabV4ToV2(ContextID))) + goto Error; + } + + + hProfile = cmsCreateProfilePlaceholder(ContextID); + if (!hProfile) goto Error; // can't allocate + + cmsSetProfileVersion(hProfile, Version); + + FixColorSpaces(hProfile, xform -> EntryColorSpace, xform -> ExitColorSpace, dwFlags); + + // Optimize the LUT and precalculate a devicelink + + ChansIn = cmsChannelsOf(xform -> EntryColorSpace); + ChansOut = cmsChannelsOf(xform -> ExitColorSpace); + + ColorSpaceBitsIn = _cmsLCMScolorSpace(xform -> EntryColorSpace); + ColorSpaceBitsOut = _cmsLCMScolorSpace(xform -> ExitColorSpace); + + FrmIn = COLORSPACE_SH(ColorSpaceBitsIn) | CHANNELS_SH(ChansIn)|BYTES_SH(2); + FrmOut = COLORSPACE_SH(ColorSpaceBitsOut) | CHANNELS_SH(ChansOut)|BYTES_SH(2); + + deviceClass = cmsGetDeviceClass(hProfile); + + if (deviceClass == cmsSigOutputClass) + DestinationTag = cmsSigBToA0Tag; + else + DestinationTag = cmsSigAToB0Tag; + + // Check if the profile/version can store the result + if (dwFlags & cmsFLAGS_FORCE_CLUT) + AllowedLUT = NULL; + else + AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag); + + if (AllowedLUT == NULL) { + + // Try to optimize + _cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags); + AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag); + + } + + // If no way, then force CLUT that for sure can be written + if (AllowedLUT == NULL) { + + dwFlags |= cmsFLAGS_FORCE_CLUT; + _cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags); + + // Put identity curves if needed + if (cmsPipelineGetPtrToFirstStage(LUT) ->Type != cmsSigCurveSetElemType) + if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, ChansIn))) + goto Error; + + if (cmsPipelineGetPtrToLastStage(LUT) ->Type != cmsSigCurveSetElemType) + if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, ChansOut))) + goto Error; + + AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag); + } + + // Somethings is wrong... + if (AllowedLUT == NULL) { + goto Error; + } + + + if (dwFlags & cmsFLAGS_8BITS_DEVICELINK) + cmsPipelineSetSaveAs8bitsFlag(LUT, TRUE); + + // Tag profile with information + if (!SetTextTags(hProfile, L"devicelink")) goto Error; + + // Store result + if (!cmsWriteTag(hProfile, DestinationTag, LUT)) goto Error; + + + if (xform -> InputColorant != NULL) { + if (!cmsWriteTag(hProfile, cmsSigColorantTableTag, xform->InputColorant)) goto Error; + } + + if (xform -> OutputColorant != NULL) { + if (!cmsWriteTag(hProfile, cmsSigColorantTableOutTag, xform->OutputColorant)) goto Error; + } + + if ((deviceClass == cmsSigLinkClass) && (xform ->Sequence != NULL)) { + if (!_cmsWriteProfileSequence(hProfile, xform ->Sequence)) goto Error; + } + + // Set the white point + if (deviceClass == cmsSigInputClass) { + if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->EntryWhitePoint)) goto Error; + } + else { + if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->ExitWhitePoint)) goto Error; + } + + + // Per 7.2.15 in spec 4.3 + cmsSetHeaderRenderingIntent(hProfile, xform ->RenderingIntent); + + cmsPipelineFree(LUT); + return hProfile; + +Error: + if (LUT != NULL) cmsPipelineFree(LUT); + cmsCloseProfile(hProfile); + return NULL; +} diff --git a/third_party/lcms/src/cmswtpnt.c b/third_party/lcms/src/cmswtpnt.c new file mode 100644 index 0000000000..903fdd7497 --- /dev/null +++ b/third_party/lcms/src/cmswtpnt.c @@ -0,0 +1,349 @@ +//--------------------------------------------------------------------------------- +// +// 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 "lcms2_internal.h" + + +// D50 - Widely used +const cmsCIEXYZ* CMSEXPORT cmsD50_XYZ(void) +{ + static cmsCIEXYZ D50XYZ = {cmsD50X, cmsD50Y, cmsD50Z}; + + return &D50XYZ; +} + +const cmsCIExyY* CMSEXPORT cmsD50_xyY(void) +{ + static cmsCIExyY D50xyY; + + cmsXYZ2xyY(&D50xyY, cmsD50_XYZ()); + + return &D50xyY; +} + +// Obtains WhitePoint from Temperature +cmsBool CMSEXPORT cmsWhitePointFromTemp(cmsCIExyY* WhitePoint, cmsFloat64Number TempK) +{ + cmsFloat64Number x, y; + cmsFloat64Number T, T2, T3; + // cmsFloat64Number M1, M2; + + _cmsAssert(WhitePoint != NULL); + + T = TempK; + T2 = T*T; // Square + T3 = T2*T; // Cube + + // For correlated color temperature (T) between 4000K and 7000K: + + if (T >= 4000. && T <= 7000.) + { + x = -4.6070*(1E9/T3) + 2.9678*(1E6/T2) + 0.09911*(1E3/T) + 0.244063; + } + else + // or for correlated color temperature (T) between 7000K and 25000K: + + if (T > 7000.0 && T <= 25000.0) + { + x = -2.0064*(1E9/T3) + 1.9018*(1E6/T2) + 0.24748*(1E3/T) + 0.237040; + } + else { + cmsSignalError(0, cmsERROR_RANGE, "cmsWhitePointFromTemp: invalid temp"); + return FALSE; + } + + // Obtain y(x) + + y = -3.000*(x*x) + 2.870*x - 0.275; + + // wave factors (not used, but here for futures extensions) + + // M1 = (-1.3515 - 1.7703*x + 5.9114 *y)/(0.0241 + 0.2562*x - 0.7341*y); + // M2 = (0.0300 - 31.4424*x + 30.0717*y)/(0.0241 + 0.2562*x - 0.7341*y); + + WhitePoint -> x = x; + WhitePoint -> y = y; + WhitePoint -> Y = 1.0; + + return TRUE; +} + + + +typedef struct { + + cmsFloat64Number mirek; // temp (in microreciprocal kelvin) + cmsFloat64Number ut; // u coord of intersection w/ blackbody locus + cmsFloat64Number vt; // v coord of intersection w/ blackbody locus + cmsFloat64Number tt; // slope of ISOTEMPERATURE. line + + } ISOTEMPERATURE; + +static ISOTEMPERATURE isotempdata[] = { +// {Mirek, Ut, Vt, Tt } + {0, 0.18006, 0.26352, -0.24341}, + {10, 0.18066, 0.26589, -0.25479}, + {20, 0.18133, 0.26846, -0.26876}, + {30, 0.18208, 0.27119, -0.28539}, + {40, 0.18293, 0.27407, -0.30470}, + {50, 0.18388, 0.27709, -0.32675}, + {60, 0.18494, 0.28021, -0.35156}, + {70, 0.18611, 0.28342, -0.37915}, + {80, 0.18740, 0.28668, -0.40955}, + {90, 0.18880, 0.28997, -0.44278}, + {100, 0.19032, 0.29326, -0.47888}, + {125, 0.19462, 0.30141, -0.58204}, + {150, 0.19962, 0.30921, -0.70471}, + {175, 0.20525, 0.31647, -0.84901}, + {200, 0.21142, 0.32312, -1.0182 }, + {225, 0.21807, 0.32909, -1.2168 }, + {250, 0.22511, 0.33439, -1.4512 }, + {275, 0.23247, 0.33904, -1.7298 }, + {300, 0.24010, 0.34308, -2.0637 }, + {325, 0.24702, 0.34655, -2.4681 }, + {350, 0.25591, 0.34951, -2.9641 }, + {375, 0.26400, 0.35200, -3.5814 }, + {400, 0.27218, 0.35407, -4.3633 }, + {425, 0.28039, 0.35577, -5.3762 }, + {450, 0.28863, 0.35714, -6.7262 }, + {475, 0.29685, 0.35823, -8.5955 }, + {500, 0.30505, 0.35907, -11.324 }, + {525, 0.31320, 0.35968, -15.628 }, + {550, 0.32129, 0.36011, -23.325 }, + {575, 0.32931, 0.36038, -40.770 }, + {600, 0.33724, 0.36051, -116.45 } +}; + +#define NISO sizeof(isotempdata)/sizeof(ISOTEMPERATURE) + + +// Robertson's method +cmsBool CMSEXPORT cmsTempFromWhitePoint(cmsFloat64Number* TempK, const cmsCIExyY* WhitePoint) +{ + cmsUInt32Number j; + cmsFloat64Number us,vs; + cmsFloat64Number uj,vj,tj,di,dj,mi,mj; + cmsFloat64Number xs, ys; + + _cmsAssert(WhitePoint != NULL); + _cmsAssert(TempK != NULL); + + di = mi = 0; + xs = WhitePoint -> x; + ys = WhitePoint -> y; + + // convert (x,y) to CIE 1960 (u,WhitePoint) + + us = (2*xs) / (-xs + 6*ys + 1.5); + vs = (3*ys) / (-xs + 6*ys + 1.5); + + + for (j=0; j < NISO; j++) { + + uj = isotempdata[j].ut; + vj = isotempdata[j].vt; + tj = isotempdata[j].tt; + mj = isotempdata[j].mirek; + + dj = ((vs - vj) - tj * (us - uj)) / sqrt(1.0 + tj * tj); + + if ((j != 0) && (di/dj < 0.0)) { + + // Found a match + *TempK = 1000000.0 / (mi + (di / (di - dj)) * (mj - mi)); + return TRUE; + } + + di = dj; + mi = mj; + } + + // Not found + return FALSE; +} + + +// Compute chromatic adaptation matrix using Chad as cone matrix + +static +cmsBool ComputeChromaticAdaptation(cmsMAT3* Conversion, + const cmsCIEXYZ* SourceWhitePoint, + const cmsCIEXYZ* DestWhitePoint, + const cmsMAT3* Chad) + +{ + + cmsMAT3 Chad_Inv; + cmsVEC3 ConeSourceXYZ, ConeSourceRGB; + cmsVEC3 ConeDestXYZ, ConeDestRGB; + cmsMAT3 Cone, Tmp; + + + Tmp = *Chad; + if (!_cmsMAT3inverse(&Tmp, &Chad_Inv)) return FALSE; + + _cmsVEC3init(&ConeSourceXYZ, SourceWhitePoint -> X, + SourceWhitePoint -> Y, + SourceWhitePoint -> Z); + + _cmsVEC3init(&ConeDestXYZ, DestWhitePoint -> X, + DestWhitePoint -> Y, + DestWhitePoint -> Z); + + _cmsMAT3eval(&ConeSourceRGB, Chad, &ConeSourceXYZ); + _cmsMAT3eval(&ConeDestRGB, Chad, &ConeDestXYZ); + + // Build matrix + _cmsVEC3init(&Cone.v[0], ConeDestRGB.n[0]/ConeSourceRGB.n[0], 0.0, 0.0); + _cmsVEC3init(&Cone.v[1], 0.0, ConeDestRGB.n[1]/ConeSourceRGB.n[1], 0.0); + _cmsVEC3init(&Cone.v[2], 0.0, 0.0, ConeDestRGB.n[2]/ConeSourceRGB.n[2]); + + + // Normalize + _cmsMAT3per(&Tmp, &Cone, Chad); + _cmsMAT3per(Conversion, &Chad_Inv, &Tmp); + + return TRUE; +} + +// Returns the final chrmatic adaptation from illuminant FromIll to Illuminant ToIll +// The cone matrix can be specified in ConeMatrix. If NULL, Bradford is assumed +cmsBool _cmsAdaptationMatrix(cmsMAT3* r, const cmsMAT3* ConeMatrix, const cmsCIEXYZ* FromIll, const cmsCIEXYZ* ToIll) +{ + cmsMAT3 LamRigg = {{ // Bradford matrix + {{ 0.8951, 0.2664, -0.1614 }}, + {{ -0.7502, 1.7135, 0.0367 }}, + {{ 0.0389, -0.0685, 1.0296 }} + }}; + + if (ConeMatrix == NULL) + ConeMatrix = &LamRigg; + + return ComputeChromaticAdaptation(r, FromIll, ToIll, ConeMatrix); +} + +// Same as anterior, but assuming D50 destination. White point is given in xyY +static +cmsBool _cmsAdaptMatrixToD50(cmsMAT3* r, const cmsCIExyY* SourceWhitePt) +{ + cmsCIEXYZ Dn; + cmsMAT3 Bradford; + cmsMAT3 Tmp; + + cmsxyY2XYZ(&Dn, SourceWhitePt); + + if (!_cmsAdaptationMatrix(&Bradford, NULL, &Dn, cmsD50_XYZ())) return FALSE; + + Tmp = *r; + _cmsMAT3per(r, &Bradford, &Tmp); + + return TRUE; +} + +// Build a White point, primary chromas transfer matrix from RGB to CIE XYZ +// This is just an approximation, I am not handling all the non-linear +// aspects of the RGB to XYZ process, and assumming that the gamma correction +// has transitive property in the tranformation chain. +// +// the alghoritm: +// +// - First I build the absolute conversion matrix using +// primaries in XYZ. This matrix is next inverted +// - Then I eval the source white point across this matrix +// obtaining the coeficients of the transformation +// - Then, I apply these coeficients to the original matrix +// +cmsBool _cmsBuildRGB2XYZtransferMatrix(cmsMAT3* r, const cmsCIExyY* WhitePt, const cmsCIExyYTRIPLE* Primrs) +{ + cmsVEC3 WhitePoint, Coef; + cmsMAT3 Result, Primaries; + cmsFloat64Number xn, yn; + cmsFloat64Number xr, yr; + cmsFloat64Number xg, yg; + cmsFloat64Number xb, yb; + + xn = WhitePt -> x; + yn = WhitePt -> y; + xr = Primrs -> Red.x; + yr = Primrs -> Red.y; + xg = Primrs -> Green.x; + yg = Primrs -> Green.y; + xb = Primrs -> Blue.x; + yb = Primrs -> Blue.y; + + // Build Primaries matrix + _cmsVEC3init(&Primaries.v[0], xr, xg, xb); + _cmsVEC3init(&Primaries.v[1], yr, yg, yb); + _cmsVEC3init(&Primaries.v[2], (1-xr-yr), (1-xg-yg), (1-xb-yb)); + + + // Result = Primaries ^ (-1) inverse matrix + if (!_cmsMAT3inverse(&Primaries, &Result)) + return FALSE; + + + _cmsVEC3init(&WhitePoint, xn/yn, 1.0, (1.0-xn-yn)/yn); + + // Across inverse primaries ... + _cmsMAT3eval(&Coef, &Result, &WhitePoint); + + // Give us the Coefs, then I build transformation matrix + _cmsVEC3init(&r -> v[0], Coef.n[VX]*xr, Coef.n[VY]*xg, Coef.n[VZ]*xb); + _cmsVEC3init(&r -> v[1], Coef.n[VX]*yr, Coef.n[VY]*yg, Coef.n[VZ]*yb); + _cmsVEC3init(&r -> v[2], Coef.n[VX]*(1.0-xr-yr), Coef.n[VY]*(1.0-xg-yg), Coef.n[VZ]*(1.0-xb-yb)); + + + return _cmsAdaptMatrixToD50(r, WhitePt); + +} + + +// Adapts a color to a given illuminant. Original color is expected to have +// a SourceWhitePt white point. +cmsBool CMSEXPORT cmsAdaptToIlluminant(cmsCIEXYZ* Result, + const cmsCIEXYZ* SourceWhitePt, + const cmsCIEXYZ* Illuminant, + const cmsCIEXYZ* Value) +{ + cmsMAT3 Bradford; + cmsVEC3 In, Out; + + _cmsAssert(Result != NULL); + _cmsAssert(SourceWhitePt != NULL); + _cmsAssert(Illuminant != NULL); + _cmsAssert(Value != NULL); + + if (!_cmsAdaptationMatrix(&Bradford, NULL, SourceWhitePt, Illuminant)) return FALSE; + + _cmsVEC3init(&In, Value -> X, Value -> Y, Value -> Z); + _cmsMAT3eval(&Out, &Bradford, &In); + + Result -> X = Out.n[0]; + Result -> Y = Out.n[1]; + Result -> Z = Out.n[2]; + + return TRUE; +} diff --git a/third_party/lcms/src/cmsxform.c b/third_party/lcms/src/cmsxform.c new file mode 100644 index 0000000000..dffe6b2fb7 --- /dev/null +++ b/third_party/lcms/src/cmsxform.c @@ -0,0 +1,1137 @@ +//--------------------------------------------------------------------------------- +// +// 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 "lcms2_internal.h" + +// Transformations stuff +// ----------------------------------------------------------------------- + +#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) +{ + 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. + +#define DEFAULT_ALARM_CODES_VALUE {0x7F00, 0x7F00, 0x7F00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + +_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)); +} + +// 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]) +{ + _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(ContextID, AlarmCodesContext); + + _cmsAssert(ContextAlarmCodes != NULL); // Can't happen + + memcpy(AlarmCodesP, ContextAlarmCodes->AlarmCodes, sizeof(ContextAlarmCodes->AlarmCodes)); +} + +void CMSEXPORT cmsSetAlarmCodes(const cmsUInt16Number NewAlarm[cmsMAXCHANNELS]) +{ + _cmsAssert(NewAlarm != NULL); + + cmsSetAlarmCodesTHR(NULL, NewAlarm); +} + +void CMSEXPORT cmsGetAlarmCodes(cmsUInt16Number OldAlarm[cmsMAXCHANNELS]) +{ + _cmsAssert(OldAlarm != NULL); + cmsGetAlarmCodesTHR(NULL, OldAlarm); +} + + +// 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) +{ + _cmsTRANSFORM* p = (_cmsTRANSFORM*) hTransform; + + _cmsAssert(p != NULL); + + if (p -> GamutCheck) + cmsPipelineFree(p -> GamutCheck); + + if (p -> Lut) + cmsPipelineFree(p -> Lut); + + if (p ->InputColorant) + cmsFreeNamedColorList(p ->InputColorant); + + if (p -> OutputColorant) + cmsFreeNamedColorList(p ->OutputColorant); + + if (p ->Sequence) + cmsFreeProfileSequenceDescription(p ->Sequence); + + if (p ->UserData) + p ->FreeUserData(p ->ContextID, p ->UserData); + + _cmsFree(p ->ContextID, (void *) p); +} + +// Apply transform. +void CMSEXPORT cmsDoTransform(cmsHTRANSFORM Transform, + const void* InputBuffer, + void* OutputBuffer, + cmsUInt32Number Size) + +{ + _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform; + + p -> xform(p, InputBuffer, OutputBuffer, Size, Size); +} + + +// Apply transform. +void CMSEXPORT cmsDoTransformStride(cmsHTRANSFORM Transform, + const void* InputBuffer, + void* OutputBuffer, + cmsUInt32Number Size, cmsUInt32Number Stride) + +{ + _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform; + + p -> xform(p, InputBuffer, OutputBuffer, Size, Stride); +} + + +// Transform routines ---------------------------------------------------------------------------------------------------------- + +// Float xform converts floats. Since there are no performance issues, one routine does all job, including gamut check. +// Note that because extended range, we can use a -1.0 value for out of gamut in this case. +static +void FloatXFORM(_cmsTRANSFORM* p, + const void* in, + void* out, cmsUInt32Number Size, cmsUInt32Number Stride) +{ + cmsUInt8Number* accum; + cmsUInt8Number* output; + cmsFloat32Number fIn[cmsMAXCHANNELS], fOut[cmsMAXCHANNELS]; + cmsFloat32Number OutOfGamut; + cmsUInt32Number i, j; + + accum = (cmsUInt8Number*) in; + output = (cmsUInt8Number*) out; + + for (i=0; i < Size; i++) { + + accum = p -> FromInputFloat(p, fIn, accum, Stride); + + // Any gamut chack to do? + if (p ->GamutCheck != NULL) { + + // Evaluate gamut marker. + cmsPipelineEvalFloat( fIn, &OutOfGamut, p ->GamutCheck); + + // Is current color out of gamut? + if (OutOfGamut > 0.0) { + + // Certainly, out of gamut + for (j=0; j < cmsMAXCHANNELS; j++) + fOut[j] = -1.0; + + } + else { + // No, proceed normally + cmsPipelineEvalFloat(fIn, fOut, p -> Lut); + } + } + else { + + // No gamut check at all + cmsPipelineEvalFloat(fIn, fOut, p -> Lut); + } + + // Back to asked representation + output = p -> ToOutputFloat(p, fOut, output, Stride); + } +} + + +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?static +void NullXFORM(_cmsTRANSFORM* p, + const void* in, + void* out, cmsUInt32Number Size, + cmsUInt32Number Stride) +{ + cmsUInt8Number* accum; + cmsUInt8Number* output; + cmsUInt16Number wIn[cmsMAXCHANNELS]; + cmsUInt32Number i, n; + + accum = (cmsUInt8Number*) in; + output = (cmsUInt8Number*) out; + n = Size; // Buffer len + + for (i=0; i < n; i++) { + + accum = p -> FromInput(p, wIn, accum, Stride); + output = p -> ToOutput(p, wIn, output, Stride); + } +} + + +// No gamut check, no cache, 16 bits +static +void PrecalculatedXFORM(_cmsTRANSFORM* p, + const void* in, + void* out, cmsUInt32Number Size, cmsUInt32Number Stride) +{ + register cmsUInt8Number* accum; + register cmsUInt8Number* output; + cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS]; + cmsUInt32Number i, n; + + accum = (cmsUInt8Number*) in; + output = (cmsUInt8Number*) out; + n = Size; + + for (i=0; i < n; i++) { + + accum = p -> FromInput(p, wIn, accum, Stride); + p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data); + output = p -> ToOutput(p, wOut, output, Stride); + } +} + + +// 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[], + cmsUInt16Number wOut[]) +{ + cmsUInt16Number wOutOfGamut; + + p ->GamutCheck ->Eval16Fn(wIn, &wOutOfGamut, p ->GamutCheck ->Data); + if (wOutOfGamut >= 1) { + + cmsUInt16Number i; + _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(p->ContextID, AlarmCodesContext); + + for (i=0; i < p ->Lut->OutputChannels; i++) { + + wOut[i] = ContextAlarmCodes ->AlarmCodes[i]; + } + } + else + p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data); +} + +// Gamut check, No cach? 16 bits. +static +void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p, + const void* in, + void* out, cmsUInt32Number Size, cmsUInt32Number Stride) +{ + cmsUInt8Number* accum; + cmsUInt8Number* output; + cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS]; + cmsUInt32Number i, n; + + accum = (cmsUInt8Number*) in; + output = (cmsUInt8Number*) out; + n = Size; // Buffer len + + for (i=0; i < n; i++) { + + accum = p -> FromInput(p, wIn, accum, Stride); + TransformOnePixelWithGamutCheck(p, wIn, wOut); + output = p -> ToOutput(p, wOut, output, Stride); + } +} + + +// No gamut check, Cach? 16 bits, +static +void CachedXFORM(_cmsTRANSFORM* p, + const void* in, + void* out, cmsUInt32Number Size, cmsUInt32Number Stride) +{ + cmsUInt8Number* accum; + cmsUInt8Number* output; + cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS]; + cmsUInt32Number i, n; + _cmsCACHE Cache; + + accum = (cmsUInt8Number*) in; + output = (cmsUInt8Number*) out; + n = Size; // Buffer len + + // Empty buffers for quick memcmp + memset(wIn, 0, sizeof(wIn)); + memset(wOut, 0, sizeof(wOut)); + + // Get copy of zero cache + memcpy(&Cache, &p ->Cache, sizeof(Cache)); + + for (i=0; i < n; i++) { + + accum = p -> FromInput(p, wIn, accum, Stride); + + if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) { + + memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut)); + } + else { + + p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data); + + memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn)); + memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut)); + } + + output = p -> ToOutput(p, wOut, output, Stride); + } + +} + + +// All those nice features together +static +void CachedXFORMGamutCheck(_cmsTRANSFORM* p, + const void* in, + void* out, cmsUInt32Number Size, cmsUInt32Number Stride) +{ + cmsUInt8Number* accum; + cmsUInt8Number* output; + cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS]; + cmsUInt32Number i, n; + _cmsCACHE Cache; + + accum = (cmsUInt8Number*) in; + output = (cmsUInt8Number*) out; + n = Size; // Buffer len + + // Empty buffers for quick memcmp + memset(wIn, 0, sizeof(cmsUInt16Number) * cmsMAXCHANNELS); + memset(wOut, 0, sizeof(cmsUInt16Number) * cmsMAXCHANNELS); + + // Get copy of zero cache + memcpy(&Cache, &p ->Cache, sizeof(Cache)); + + for (i=0; i < n; i++) { + + accum = p -> FromInput(p, wIn, accum, Stride); + + if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) { + memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut)); + } + else { + TransformOnePixelWithGamutCheck(p, wIn, wOut); + memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn)); + memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut)); + } + + output = p -> ToOutput(p, wOut, output, Stride); + } + +} + +// ------------------------------------------------------------------------------------------------------------- + +// List of used-defined transform factories +typedef struct _cmsTransformCollection_st { + + _cmsTransformFactory Factory; + struct _cmsTransformCollection_st *Next; + +} _cmsTransformCollection; + +// The linked list head +_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 ContextID, cmsPluginBase* Data) +{ + cmsPluginTransform* Plugin = (cmsPluginTransform*) Data; + _cmsTransformCollection* fl; + _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID,TransformPlugin); + + if (Data == NULL) { + + // Free the chain. Memory is safely freed at exit + ctx->TransformCollection = NULL; + return TRUE; + } + + // Factory callback is required + if (Plugin ->Factory == NULL) return FALSE; + + + fl = (_cmsTransformCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsTransformCollection)); + if (fl == NULL) return FALSE; + + // Copy the parameters + fl ->Factory = Plugin ->Factory; + + // Keep linked list + fl ->Next = ctx->TransformCollection; + ctx->TransformCollection = fl; + + // All is ok + return TRUE; +} + + +void CMSEXPORT _cmsSetTransformUserData(struct _cmstransform_struct *CMMcargo, void* ptr, _cmsFreeUserDataFn FreePrivateDataFn) +{ + _cmsAssert(CMMcargo != NULL); + CMMcargo ->UserData = ptr; + CMMcargo ->FreeUserData = FreePrivateDataFn; +} + +// returns the pointer defined by the plug-in to store private data +void * CMSEXPORT _cmsGetTransformUserData(struct _cmstransform_struct *CMMcargo) +{ + _cmsAssert(CMMcargo != NULL); + return CMMcargo ->UserData; +} + +// returns the current formatters +void CMSEXPORT _cmsGetTransformFormatters16(struct _cmstransform_struct *CMMcargo, cmsFormatter16* FromInput, cmsFormatter16* ToOutput) +{ + _cmsAssert(CMMcargo != NULL); + if (FromInput) *FromInput = CMMcargo ->FromInput; + if (ToOutput) *ToOutput = CMMcargo ->ToOutput; +} + +void CMSEXPORT _cmsGetTransformFormattersFloat(struct _cmstransform_struct *CMMcargo, cmsFormatterFloat* FromInput, cmsFormatterFloat* ToOutput) +{ + _cmsAssert(CMMcargo != NULL); + if (FromInput) *FromInput = CMMcargo ->FromInputFloat; + if (ToOutput) *ToOutput = CMMcargo ->ToOutputFloat; +} + + +// Allocate transform struct and set it to defaults. Ask the optimization plug-in about if those formats are proper +// for separated transforms. If this is the case, +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 + _cmsTRANSFORM* p = (_cmsTRANSFORM*) _cmsMallocZero(ContextID, sizeof(_cmsTRANSFORM)); + if (!p) { + cmsPipelineFree(lut); + return NULL; + } + + // Store the proposed pipeline + p ->Lut = lut; + + // Let's see if any plug-in want to do the transform by itself + for (Plugin = ctx ->TransformCollection; + Plugin != NULL; + Plugin = Plugin ->Next) { + + if (Plugin ->Factory(&p->xform, &p->UserData, &p ->FreeUserData, &p ->Lut, InputFormat, OutputFormat, dwFlags)) { + + // Last plugin in the declaration order takes control. We just keep + // the original parameters as a logging. + // Note that cmsFLAGS_CAN_CHANGE_FORMATTER is not set, so by default + // an optimized transform is not reusable. The plug-in can, however, change + // the flags and make it suitable. + + p ->ContextID = ContextID; + p ->InputFormat = *InputFormat; + p ->OutputFormat = *OutputFormat; + p ->dwOriginalFlags = *dwFlags; + + // 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(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; + } + } + + // Not suitable for the transform plug-in, let's check the pipeline plug-in + if (p ->Lut != NULL) + _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(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) { + + cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format"); + cmsDeleteTransform(p); + return NULL; + } + + if (*dwFlags & cmsFLAGS_NULLTRANSFORM) { + + p ->xform = NullFloatXFORM; + } + else { + // Float transforms don't use cach? always are non-NULL + p ->xform = FloatXFORM; + } + + } + else { + + if (*InputFormat == 0 && *OutputFormat == 0) { + p ->FromInput = p ->ToOutput = NULL; + *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER; + } + else { + + int BytesPerPixelInput; + + 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) { + + cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format"); + cmsDeleteTransform(p); + return NULL; + } + + BytesPerPixelInput = T_BYTES(p ->InputFormat); + if (BytesPerPixelInput == 0 || BytesPerPixelInput >= 2) + *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER; + + } + + if (*dwFlags & cmsFLAGS_NULLTRANSFORM) { + + p ->xform = NullXFORM; + } + else { + if (*dwFlags & cmsFLAGS_NOCACHE) { + + if (*dwFlags & cmsFLAGS_GAMUTCHECK) + p ->xform = PrecalculatedXFORMGamutCheck; // Gamut check, no cach? + else + p ->xform = PrecalculatedXFORM; // No cach? no gamut check + } + else { + + if (*dwFlags & cmsFLAGS_GAMUTCHECK) + p ->xform = CachedXFORMGamutCheck; // Gamut check, cach? + else + p ->xform = CachedXFORM; // No gamut check, cach? + } + } + } + + p ->InputFormat = *InputFormat; + p ->OutputFormat = *OutputFormat; + p ->dwOriginalFlags = *dwFlags; + p ->ContextID = ContextID; + p ->UserData = NULL; + return p; +} + +static +cmsBool GetXFormColorSpaces(int nProfiles, cmsHPROFILE hProfiles[], cmsColorSpaceSignature* Input, cmsColorSpaceSignature* Output) +{ + cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut; + cmsColorSpaceSignature PostColorSpace; + int i; + + if (nProfiles <= 0) return FALSE; + if (hProfiles[0] == NULL) return FALSE; + + *Input = PostColorSpace = cmsGetColorSpace(hProfiles[0]); + + for (i=0; i < nProfiles; i++) { + + cmsProfileClassSignature cls; + cmsHPROFILE hProfile = hProfiles[i]; + + int lIsInput = (PostColorSpace != cmsSigXYZData) && + (PostColorSpace != cmsSigLabData); + + if (hProfile == NULL) return FALSE; + + cls = cmsGetDeviceClass(hProfile); + + if (cls == cmsSigNamedColorClass) { + + ColorSpaceIn = cmsSig1colorData; + ColorSpaceOut = (nProfiles > 1) ? cmsGetPCS(hProfile) : cmsGetColorSpace(hProfile); + } + else + if (lIsInput || (cls == cmsSigLinkClass)) { + + ColorSpaceIn = cmsGetColorSpace(hProfile); + ColorSpaceOut = cmsGetPCS(hProfile); + } + else + { + ColorSpaceIn = cmsGetPCS(hProfile); + ColorSpaceOut = cmsGetColorSpace(hProfile); + } + + if (i==0) + *Input = ColorSpaceIn; + + PostColorSpace = ColorSpaceOut; + } + + *Output = PostColorSpace; + + return TRUE; +} + +// Check colorspace +static +cmsBool IsProperColorSpace(cmsColorSpaceSignature Check, cmsUInt32Number dwFormat) +{ + int Space1 = T_COLORSPACE(dwFormat); + int Space2 = _cmsLCMScolorSpace(Check); + + if (Space1 == PT_ANY) return TRUE; + if (Space1 == Space2) return TRUE; + + if (Space1 == PT_LabV2 && Space2 == PT_Lab) return TRUE; + if (Space1 == PT_Lab && Space2 == PT_LabV2) return TRUE; + + return FALSE; +} + +// ---------------------------------------------------------------------------------------------------------------- + +static +void SetWhitePoint(cmsCIEXYZ* wtPt, const cmsCIEXYZ* src) +{ + if (src == NULL) { + wtPt ->X = cmsD50X; + wtPt ->Y = cmsD50Y; + wtPt ->Z = cmsD50Z; + } + else { + wtPt ->X = src->X; + wtPt ->Y = src->Y; + wtPt ->Z = src->Z; + } + +} + +// New to lcms 2.0 -- have all parameters available. +cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID, + cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsUInt32Number Intents[], + cmsFloat64Number AdaptationStates[], + cmsHPROFILE hGamutProfile, + cmsUInt32Number nGamutPCSposition, + cmsUInt32Number InputFormat, + cmsUInt32Number OutputFormat, + cmsUInt32Number dwFlags) +{ + _cmsTRANSFORM* xform; + cmsColorSpaceSignature EntryColorSpace; + cmsColorSpaceSignature ExitColorSpace; + cmsPipeline* Lut; + cmsUInt32Number LastIntent = Intents[nProfiles-1]; + + // If it is a fake transform + if (dwFlags & cmsFLAGS_NULLTRANSFORM) + { + return AllocEmptyTransform(ContextID, NULL, INTENT_PERCEPTUAL, &InputFormat, &OutputFormat, &dwFlags); + } + + // If gamut check is requested, make sure we have a gamut profile + if (dwFlags & cmsFLAGS_GAMUTCHECK) { + if (hGamutProfile == NULL) dwFlags &= ~cmsFLAGS_GAMUTCHECK; + } + + // On floating point transforms, inhibit cache + if (_cmsFormatterIsFloat(InputFormat) || _cmsFormatterIsFloat(OutputFormat)) + dwFlags |= cmsFLAGS_NOCACHE; + + // Mark entry/exit spaces + if (!GetXFormColorSpaces(nProfiles, hProfiles, &EntryColorSpace, &ExitColorSpace)) { + cmsSignalError(ContextID, cmsERROR_NULL, "NULL input profiles on transform"); + return NULL; + } + + // Check if proper colorspaces + if (!IsProperColorSpace(EntryColorSpace, InputFormat)) { + cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong input color space on transform"); + return NULL; + } + + if (!IsProperColorSpace(ExitColorSpace, OutputFormat)) { + cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong output color space on transform"); + return NULL; + } + + // Create a pipeline with all transformations + Lut = _cmsLinkProfiles(ContextID, nProfiles, Intents, hProfiles, BPC, AdaptationStates, dwFlags); + if (Lut == NULL) { + cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Couldn't link the profiles"); + return NULL; + } + + // Check channel count + if ((cmsChannelsOf(EntryColorSpace) != cmsPipelineInputChannels(Lut)) || + (cmsChannelsOf(ExitColorSpace) != cmsPipelineOutputChannels(Lut))) { + cmsPipelineFree(Lut); + cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Channel count doesn't match. Profile is corrupted"); + return NULL; + } + + + // All seems ok + xform = AllocEmptyTransform(ContextID, Lut, LastIntent, &InputFormat, &OutputFormat, &dwFlags); + if (xform == NULL) { + return NULL; + } + + // Keep values + xform ->EntryColorSpace = EntryColorSpace; + xform ->ExitColorSpace = ExitColorSpace; + xform ->RenderingIntent = Intents[nProfiles-1]; + + // Take white points + SetWhitePoint(&xform->EntryWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[0], cmsSigMediaWhitePointTag)); + SetWhitePoint(&xform->ExitWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[nProfiles-1], cmsSigMediaWhitePointTag)); + + + // Create a gamut check LUT if requested + if (hGamutProfile != NULL && (dwFlags & cmsFLAGS_GAMUTCHECK)) + xform ->GamutCheck = _cmsCreateGamutCheckPipeline(ContextID, hProfiles, + BPC, Intents, + AdaptationStates, + nGamutPCSposition, + hGamutProfile); + + + // Try to read input and output colorant table + if (cmsIsTag(hProfiles[0], cmsSigColorantTableTag)) { + + // Input table can only come in this way. + xform ->InputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[0], cmsSigColorantTableTag)); + } + + // Output is a little bit more complex. + if (cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigLinkClass) { + + // This tag may exist only on devicelink profiles. + if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag)) { + + // It may be NULL if error + xform ->OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag)); + } + + } else { + + if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableTag)) { + + xform -> OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableTag)); + } + } + + // Store the sequence of profiles + if (dwFlags & cmsFLAGS_KEEP_SEQUENCE) { + xform ->Sequence = _cmsCompileProfileSequence(ContextID, nProfiles, hProfiles); + } + else + xform ->Sequence = NULL; + + // If this is a cached transform, init first value, which is zero (16 bits only) + if (!(dwFlags & cmsFLAGS_NOCACHE)) { + + memset(&xform ->Cache.CacheIn, 0, sizeof(xform ->Cache.CacheIn)); + + if (xform ->GamutCheck != NULL) { + TransformOnePixelWithGamutCheck(xform, xform ->Cache.CacheIn, xform->Cache.CacheOut); + } + else { + + xform ->Lut ->Eval16Fn(xform ->Cache.CacheIn, xform->Cache.CacheOut, xform -> Lut->Data); + } + + } + + return (cmsHTRANSFORM) xform; +} + +// Multiprofile transforms: Gamut check is not available here, as it is unclear from which profile the gamut comes. +cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransformTHR(cmsContext ContextID, + cmsHPROFILE hProfiles[], + cmsUInt32Number nProfiles, + cmsUInt32Number InputFormat, + cmsUInt32Number OutputFormat, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags) +{ + cmsUInt32Number i; + cmsBool BPC[256]; + cmsUInt32Number Intents[256]; + cmsFloat64Number AdaptationStates[256]; + + if (nProfiles <= 0 || nProfiles > 255) { + cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles); + return NULL; + } + + for (i=0; i < nProfiles; i++) { + BPC[i] = dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION ? TRUE : FALSE; + Intents[i] = Intent; + AdaptationStates[i] = cmsSetAdaptationStateTHR(ContextID, -1); + } + + + return cmsCreateExtendedTransform(ContextID, nProfiles, hProfiles, BPC, Intents, AdaptationStates, NULL, 0, InputFormat, OutputFormat, dwFlags); +} + + + +cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles[], + cmsUInt32Number nProfiles, + cmsUInt32Number InputFormat, + cmsUInt32Number OutputFormat, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags) +{ + + if (nProfiles <= 0 || nProfiles > 255) { + cmsSignalError(NULL, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles); + return NULL; + } + + return cmsCreateMultiprofileTransformTHR(cmsGetProfileContextID(hProfiles[0]), + hProfiles, + nProfiles, + InputFormat, + OutputFormat, + Intent, + dwFlags); +} + +cmsHTRANSFORM CMSEXPORT cmsCreateTransformTHR(cmsContext ContextID, + cmsHPROFILE Input, + cmsUInt32Number InputFormat, + cmsHPROFILE Output, + cmsUInt32Number OutputFormat, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags) +{ + + cmsHPROFILE hArray[2]; + + hArray[0] = Input; + hArray[1] = Output; + + return cmsCreateMultiprofileTransformTHR(ContextID, hArray, Output == NULL ? 1 : 2, InputFormat, OutputFormat, Intent, dwFlags); +} + +CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateTransform(cmsHPROFILE Input, + cmsUInt32Number InputFormat, + cmsHPROFILE Output, + cmsUInt32Number OutputFormat, + cmsUInt32Number Intent, + cmsUInt32Number dwFlags) +{ + return cmsCreateTransformTHR(cmsGetProfileContextID(Input), Input, InputFormat, Output, OutputFormat, Intent, dwFlags); +} + + +cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransformTHR(cmsContext ContextID, + cmsHPROFILE InputProfile, + cmsUInt32Number InputFormat, + cmsHPROFILE OutputProfile, + cmsUInt32Number OutputFormat, + cmsHPROFILE ProofingProfile, + cmsUInt32Number nIntent, + cmsUInt32Number ProofingIntent, + cmsUInt32Number dwFlags) +{ + cmsHPROFILE hArray[4]; + cmsUInt32Number Intents[4]; + cmsBool BPC[4]; + cmsFloat64Number Adaptation[4]; + cmsBool DoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION) ? TRUE : FALSE; + + + hArray[0] = InputProfile; hArray[1] = ProofingProfile; hArray[2] = ProofingProfile; hArray[3] = OutputProfile; + 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] = cmsSetAdaptationStateTHR(ContextID, -1); + + if (!(dwFlags & (cmsFLAGS_SOFTPROOFING|cmsFLAGS_GAMUTCHECK))) + return cmsCreateTransformTHR(ContextID, InputProfile, InputFormat, OutputProfile, OutputFormat, nIntent, dwFlags); + + return cmsCreateExtendedTransform(ContextID, 4, hArray, BPC, Intents, Adaptation, + ProofingProfile, 1, InputFormat, OutputFormat, dwFlags); + +} + + +cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransform(cmsHPROFILE InputProfile, + cmsUInt32Number InputFormat, + cmsHPROFILE OutputProfile, + cmsUInt32Number OutputFormat, + cmsHPROFILE ProofingProfile, + cmsUInt32Number nIntent, + cmsUInt32Number ProofingIntent, + cmsUInt32Number dwFlags) +{ + return cmsCreateProofingTransformTHR(cmsGetProfileContextID(InputProfile), + InputProfile, + InputFormat, + OutputProfile, + OutputFormat, + ProofingProfile, + nIntent, + ProofingIntent, + dwFlags); +} + + +// Grab the ContextID from an open transform. Returns NULL if a NULL transform is passed +cmsContext CMSEXPORT cmsGetTransformContextID(cmsHTRANSFORM hTransform) +{ + _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; + + if (xform == NULL) return NULL; + return xform -> ContextID; +} + +// Grab the input/output formats +cmsUInt32Number CMSEXPORT cmsGetTransformInputFormat(cmsHTRANSFORM hTransform) +{ + _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; + + if (xform == NULL) return 0; + return xform->InputFormat; +} + +cmsUInt32Number CMSEXPORT cmsGetTransformOutputFormat(cmsHTRANSFORM hTransform) +{ + _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; + + if (xform == NULL) return 0; + return xform->OutputFormat; +} + +// For backwards compatibility +cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform, + cmsUInt32Number InputFormat, + cmsUInt32Number OutputFormat) +{ + + _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; + cmsFormatter16 FromInput, ToOutput; + + + // We only can afford to change formatters if previous transform is at least 16 bits + if (!(xform ->dwOriginalFlags & cmsFLAGS_CAN_CHANGE_FORMATTER)) { + + cmsSignalError(xform ->ContextID, cmsERROR_NOT_SUITABLE, "cmsChangeBuffersFormat works only on transforms created originally with at least 16 bits of precision"); + return FALSE; + } + + 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) { + + cmsSignalError(xform -> ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format"); + return FALSE; + } + + xform ->InputFormat = InputFormat; + xform ->OutputFormat = OutputFormat; + xform ->FromInput = FromInput; + xform ->ToOutput = ToOutput; + return TRUE; +} diff --git a/third_party/lcms/src/lcms2_internal.h b/third_party/lcms/src/lcms2_internal.h new file mode 100644 index 0000000000..1dedf16b8d --- /dev/null +++ b/third_party/lcms/src/lcms2_internal.h @@ -0,0 +1,1032 @@ +//<<<+++OPENSOURCE +//<<<+++OPENSOURCE_MUST_BEGIN COMMENT==TRUE +// +// 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 _lcms_internal_H + +// Include plug-in foundation +#ifndef _lcms_plugin_H +#include "third_party/lcms/include/lcms2_plugin.h" +#endif + +// ctype is part of C99 as per 7.1.2 +#include + +// assert macro is part of C99 as per 7.2 +#include + +// Some needed constants +#ifndef M_PI +# define M_PI 3.14159265358979323846 +#endif + +#ifndef M_LOG10E +# define M_LOG10E 0.434294481903251827651 +#endif + +// BorlandC 5.5, VC2003 are broken on that +#if defined(__BORLANDC__) || (_MSC_VER < 1400) // 1400 == VC++ 8.0 +#define sinf(x) (float)sin((float)x) +#define sqrtf(x) (float)sqrt((float)x) +#endif + + +// Alignment of ICC file format uses 4 bytes (cmsUInt32Number) +#define _cmsALIGNLONG(x) (((x)+(sizeof(cmsUInt32Number)-1)) & ~(sizeof(cmsUInt32Number)-1)) + +// Alignment to memory pointer +#define _cmsALIGNMEM(x) (((x)+(sizeof(void *) - 1)) & ~(sizeof(void *) - 1)) + +// Maximum encodeable values in floating point +#define MAX_ENCODEABLE_XYZ (1.0 + 32767.0/32768.0) +#define MIN_ENCODEABLE_ab2 (-128.0) +#define MAX_ENCODEABLE_ab2 ((65535.0/256.0) - 128.0) +#define MIN_ENCODEABLE_ab4 (-128.0) +#define MAX_ENCODEABLE_ab4 (127.0) + +// Maximum of channels for internal pipeline evaluation +#define MAX_STAGE_CHANNELS 128 + +// Unused parameter warning supression +#define cmsUNUSED_PARAMETER(x) ((void)x) + +// The specification for "inline" is section 6.7.4 of the C99 standard (ISO/IEC 9899:1999). +// unfortunately VisualC++ does not conform that +#if defined(_MSC_VER) || defined(__BORLANDC__) +# define cmsINLINE __inline +#else +# define cmsINLINE static inline +#endif + +// Other replacement functions +#ifdef _MSC_VER +# ifndef snprintf +# define snprintf _snprintf +# endif +# ifndef vsnprintf +# define vsnprintf _vsnprintf +# endif +#endif + + +// A fast way to convert from/to 16 <-> 8 bits +#define FROM_8_TO_16(rgb) (cmsUInt16Number) ((((cmsUInt16Number) (rgb)) << 8)|(rgb)) +#define FROM_16_TO_8(rgb) (cmsUInt8Number) ((((cmsUInt32Number)(rgb) * 65281U + 8388608U) >> 24) & 0xFFU) + +// Code analysis is broken on asserts +#ifdef _MSC_VER +# if (_MSC_VER >= 1500) +# define _cmsAssert(a) { assert((a)); __analysis_assume((a)); } +# else +# define _cmsAssert(a) assert((a)) +# endif +#else +# define _cmsAssert(a) assert((a)) +#endif + +//--------------------------------------------------------------------------------- + +// Determinant lower than that are assumed zero (used on matrix invert) +#define MATRIX_DET_TOLERANCE 0.0001 + +//--------------------------------------------------------------------------------- + +// Fixed point +#define FIXED_TO_INT(x) ((x)>>16) +#define FIXED_REST_TO_INT(x) ((x)&0xFFFFU) +#define ROUND_FIXED_TO_INT(x) (((x)+0x8000)>>16) + +cmsINLINE cmsS15Fixed16Number _cmsToFixedDomain(int a) { return a + ((a + 0x7fff) / 0xffff); } +cmsINLINE int _cmsFromFixedDomain(cmsS15Fixed16Number a) { return a - ((a + 0x7fff) >> 16); } + +// ----------------------------------------------------------------------------------------------------------- + +// Fast floor conversion logic. Thanks to Sree Kotay and Stuart Nixon +// note than this only works in the range ..-32767...+32767 because +// mantissa is interpreted as 15.16 fixed point. +// The union is to avoid pointer aliasing overoptimization. +cmsINLINE int _cmsQuickFloor(cmsFloat64Number val) +{ +#ifdef CMS_DONT_USE_FAST_FLOOR + return (int) floor(val); +#else + const cmsFloat64Number _lcms_double2fixmagic = 68719476736.0 * 1.5; // 2^36 * 1.5, (52-16=36) uses limited precision to floor + union { + cmsFloat64Number val; + int halves[2]; + } temp; + + temp.val = val + _lcms_double2fixmagic; + +#ifdef CMS_USE_BIG_ENDIAN + return temp.halves[1] >> 16; +#else + return temp.halves[0] >> 16; +#endif +#endif +} + +// Fast floor restricted to 0..65535.0 +cmsINLINE cmsUInt16Number _cmsQuickFloorWord(cmsFloat64Number d) +{ + return (cmsUInt16Number) _cmsQuickFloor(d - 32767.0) + 32767U; +} + +// Floor to word, taking care of saturation +cmsINLINE cmsUInt16Number _cmsQuickSaturateWord(cmsFloat64Number d) +{ + d += 0.5; + if (d <= 0) return 0; + if (d >= 65535.0) return 0xffff; + + return _cmsQuickFloorWord(d); +} + + +// Pthread support -------------------------------------------------------------------- +#ifndef CMS_NO_PTHREADS + +// This is the threading support. Unfortunately, it has to be platform-dependent because +// windows does not support pthreads. + +#ifdef CMS_IS_WINDOWS_ + +#define WIN32_LEAN_AND_MEAN 1 +#include + + +// From: http://locklessinc.com/articles/pthreads_on_windows/ +// The pthreads API has an initialization macro that has no correspondence to anything in +// the windows API. By investigating the internal definition of the critical section type, +// one may work out how to initialize one without calling InitializeCriticalSection(). +// The trick here is that InitializeCriticalSection() is not allowed to fail. It tries +// to allocate a critical section debug object, but if no memory is available, it sets +// the pointer to a specific value. (One would expect that value to be NULL, but it is +// actually (void *)-1 for some reason.) Thus we can use this special value for that +// pointer, and the critical section code will work. + +// The other important part of the critical section type to initialize is the number +// of waiters. This controls whether or not the mutex is locked. Fortunately, this +// part of the critical section is unlikely to change. Apparently, many programs +// already test critical sections to see if they are locked using this value, so +// Microsoft felt that it was necessary to keep it set at -1 for an unlocked critical +// section, even when they changed the underlying algorithm to be more scalable. +// The final parts of the critical section object are unimportant, and can be set +// to zero for their defaults. This yields an initialization macro: + +typedef CRITICAL_SECTION _cmsMutex; + +#define CMS_MUTEX_INITIALIZER {(void*) -1,-1,0,0,0,0} + +cmsINLINE int _cmsLockPrimitive(_cmsMutex *m) +{ + EnterCriticalSection(m); + return 0; +} + +cmsINLINE int _cmsUnlockPrimitive(_cmsMutex *m) +{ + LeaveCriticalSection(m); + return 0; +} + +cmsINLINE int _cmsInitMutexPrimitive(_cmsMutex *m) +{ + InitializeCriticalSection(m); + return 0; +} + +cmsINLINE int _cmsDestroyMutexPrimitive(_cmsMutex *m) +{ + DeleteCriticalSection(m); + return 0; +} + +cmsINLINE int _cmsEnterCriticalSectionPrimitive(_cmsMutex *m) +{ + EnterCriticalSection(m); + return 0; +} + +cmsINLINE int _cmsLeaveCriticalSectionPrimitive(_cmsMutex *m) +{ + LeaveCriticalSection(m); + return 0; +} + +#else + +// Rest of the wide world +#include + +#define CMS_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER +typedef pthread_mutex_t _cmsMutex; + + +cmsINLINE int _cmsLockPrimitive(_cmsMutex *m) +{ + return pthread_mutex_lock(m); +} + +cmsINLINE int _cmsUnlockPrimitive(_cmsMutex *m) +{ + return pthread_mutex_unlock(m); +} + +cmsINLINE int _cmsInitMutexPrimitive(_cmsMutex *m) +{ + return pthread_mutex_init(m, NULL); +} + +cmsINLINE int _cmsDestroyMutexPrimitive(_cmsMutex *m) +{ + return pthread_mutex_destroy(m); +} + +cmsINLINE int _cmsEnterCriticalSectionPrimitive(_cmsMutex *m) +{ + return pthread_mutex_lock(m); +} + +cmsINLINE int _cmsLeaveCriticalSectionPrimitive(_cmsMutex *m) +{ + return pthread_mutex_unlock(m); +} + +#endif +#else + +#define CMS_MUTEX_INITIALIZER 0 +typedef int _cmsMutex; + + +cmsINLINE int _cmsLockPrimitive(_cmsMutex *m) +{ + return 0; + cmsUNUSED_PARAMETER(m); +} + +cmsINLINE int _cmsUnlockPrimitive(_cmsMutex *m) +{ + return 0; + cmsUNUSED_PARAMETER(m); +} + +cmsINLINE int _cmsInitMutexPrimitive(_cmsMutex *m) +{ + return 0; + cmsUNUSED_PARAMETER(m); +} + +cmsINLINE int _cmsDestroyMutexPrimitive(_cmsMutex *m) +{ + return 0; + cmsUNUSED_PARAMETER(m); +} + +cmsINLINE int _cmsEnterCriticalSectionPrimitive(_cmsMutex *m) +{ + return 0; + cmsUNUSED_PARAMETER(m); +} + +cmsINLINE int _cmsLeaveCriticalSectionPrimitive(_cmsMutex *m) +{ + return 0; + cmsUNUSED_PARAMETER(m); +} +#endif + +// 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(cmsContext ContextID, cmsPluginBase* Plugin); + +// Interpolation +cmsBool _cmsRegisterInterpPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// Parametric curves +cmsBool _cmsRegisterParametricCurvesPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// Formatters management +cmsBool _cmsRegisterFormattersPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// Tag type management +cmsBool _cmsRegisterTagTypePlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// Tag management +cmsBool _cmsRegisterTagPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// Intent management +cmsBool _cmsRegisterRenderingIntentPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// Multi Process elements +cmsBool _cmsRegisterMultiProcessElementPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// Optimization +cmsBool _cmsRegisterOptimizationPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// Transform +cmsBool _cmsRegisterTransformPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// Mutex +cmsBool _cmsRegisterMutexPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + +// --------------------------------------------------------------------------------------------------------- + +// Suballocators. +typedef struct _cmsSubAllocator_chunk_st { + + cmsUInt8Number* Block; + cmsUInt32Number BlockSize; + cmsUInt32Number Used; + + struct _cmsSubAllocator_chunk_st* next; + +} _cmsSubAllocator_chunk; + + +typedef struct { + + cmsContext ContextID; + _cmsSubAllocator_chunk* h; + +} _cmsSubAllocator; + + +_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; + + +// 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 { + + struct _cmsContext_struct* Next; // Points to next context 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 { + + cmsUInt16Number Language; + cmsUInt16Number Country; + + cmsUInt32Number StrW; // Offset to current unicode string + cmsUInt32Number Len; // Length in bytes + +} _cmsMLUentry; + +struct _cms_MLU_struct { + + cmsContext ContextID; + + // The directory + int AllocatedEntries; + int UsedEntries; + _cmsMLUentry* Entries; // Array of pointers to strings allocated in MemPool + + // The Pool + cmsUInt32Number PoolSize; // The maximum allocated size + cmsUInt32Number PoolUsed; // The used size + void* MemPool; // Pointer to begin of memory pool +}; + +// Named color list internal representation +typedef struct { + + char Name[cmsMAX_PATH]; + cmsUInt16Number PCS[3]; + cmsUInt16Number DeviceColorant[cmsMAXCHANNELS]; + +} _cmsNAMEDCOLOR; + +struct _cms_NAMEDCOLORLIST_struct { + + cmsUInt32Number nColors; + cmsUInt32Number Allocated; + cmsUInt32Number ColorantCount; + + char Prefix[33]; // Prefix and suffix are defined to be 32 characters at most + char Suffix[33]; + + _cmsNAMEDCOLOR* List; + + cmsContext ContextID; +}; + + +// ---------------------------------------------------------------------------------- + +// This is the internal struct holding profile details. + +// Maximum supported tags in a profile +#define MAX_TABLE_TAG 100 + +typedef struct _cms_iccprofile_struct { + + // I/O handler + cmsIOHANDLER* IOhandler; + + // The thread ID + cmsContext ContextID; + + // Creation time + struct tm Created; + + // Only most important items found in ICC profiles + cmsUInt32Number Version; + cmsProfileClassSignature DeviceClass; + cmsColorSpaceSignature ColorSpace; + cmsColorSpaceSignature PCS; + cmsUInt32Number RenderingIntent; + + cmsUInt32Number flags; + cmsUInt32Number manufacturer, model; + cmsUInt64Number attributes; + cmsUInt32Number creator; + + cmsProfileID ProfileID; + + // Dictionary + cmsUInt32Number TagCount; + cmsTagSignature TagNames[MAX_TABLE_TAG]; + cmsTagSignature TagLinked[MAX_TABLE_TAG]; // The tag to wich is linked (0=none) + cmsUInt32Number TagSizes[MAX_TABLE_TAG]; // Size on disk + cmsUInt32Number TagOffsets[MAX_TABLE_TAG]; + 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. + // 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 +cmsBool _cmsReadHeader(_cmsICCPROFILE* Icc); +cmsBool _cmsWriteHeader(_cmsICCPROFILE* Icc, cmsUInt32Number UsedSpace); +int _cmsSearchTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, cmsBool lFollowLinks); + +// Tag types +cmsTagTypeHandler* _cmsGetTagTypeHandler(cmsContext ContextID, cmsTagTypeSignature sig); +cmsTagTypeSignature _cmsGetTagTrueType(cmsHPROFILE hProfile, cmsTagSignature sig); +cmsTagDescriptor* _cmsGetTagDescriptor(cmsContext ContextID, cmsTagSignature sig); + +// Error logging --------------------------------------------------------------------------------------------------------- + +void _cmsTagSignature2String(char String[5], cmsTagSignature sig); + +// Interpolation --------------------------------------------------------------------------------------------------------- + +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(cmsContext ContextID, cmsInterpParams* p); + +// Curves ---------------------------------------------------------------------------------------------------------------- + +// This struct holds information about a segment, plus a pointer to the function that implements the evaluation. +// In the case of table-based, Eval pointer is set to NULL + +// The gamma function main structure +struct _cms_curve_struct { + + cmsInterpParams* InterpParams; // Private optimizations for interpolation + + cmsUInt32Number nSegments; // Number of segments in the curve. Zero for a 16-bit based tables + cmsCurveSegment* Segments; // The segments + cmsInterpParams** SegInterp; // Array of private optimizations for interpolation in table-based segments + + cmsParametricCurveEvaluator* Evals; // Evaluators (one per segment) + + // 16 bit Table-based representation follows + cmsUInt32Number nEntries; // Number of table elements + cmsUInt16Number* Table16; // The table itself. +}; + + +// Pipelines & Stages --------------------------------------------------------------------------------------------- + +// A single stage +struct _cmsStage_struct { + + cmsContext ContextID; + + cmsStageSignature Type; // Identifies the stage + cmsStageSignature Implements; // Identifies the *function* of the stage (for optimizations) + + cmsUInt32Number InputChannels; // Input channels -- for optimization purposes + cmsUInt32Number OutputChannels; // Output channels -- for optimization purposes + + _cmsStageEvalFn EvalPtr; // Points to fn that evaluates the stage (always in floating point) + _cmsStageDupElemFn DupElemPtr; // Points to a fn that duplicates the *data* of the stage + _cmsStageFreeElemFn FreePtr; // Points to a fn that sets the *data* of the stage free + + // A generic pointer to whatever memory needed by the stage + void* Data; + + // Maintains linked list (used internally) + struct _cmsStage_struct* Next; +}; + + +// Special Stages (cannot be saved) +cmsStage* _cmsStageAllocLab2XYZ(cmsContext ContextID); +cmsStage* _cmsStageAllocXYZ2Lab(cmsContext ContextID); +cmsStage* _cmsStageAllocLabPrelin(cmsContext ContextID); +cmsStage* _cmsStageAllocLabV2ToV4(cmsContext ContextID); +cmsStage* _cmsStageAllocLabV2ToV4curves(cmsContext ContextID); +cmsStage* _cmsStageAllocLabV4ToV2(cmsContext ContextID); +cmsStage* _cmsStageAllocNamedColor(cmsNAMEDCOLORLIST* NamedColorList, cmsBool UsePCS); +cmsStage* _cmsStageAllocIdentityCurves(cmsContext ContextID, int nChannels); +cmsStage* _cmsStageAllocIdentityCLut(cmsContext ContextID, int nChan); +cmsStage* _cmsStageNormalizeFromLabFloat(cmsContext ContextID); +cmsStage* _cmsStageNormalizeFromXyzFloat(cmsContext ContextID); +cmsStage* _cmsStageNormalizeToLabFloat(cmsContext ContextID); +cmsStage* _cmsStageNormalizeToXyzFloat(cmsContext ContextID); + +// For curve set only +cmsToneCurve** _cmsStageGetPtrToCurveSet(const cmsStage* mpe); + + +// Pipeline Evaluator (in floating point) +typedef void (* _cmsPipelineEvalFloatFn)(const cmsFloat32Number In[], + cmsFloat32Number Out[], + const void* Data); + +struct _cmsPipeline_struct { + + cmsStage* Elements; // Points to elements chain + cmsUInt32Number InputChannels, OutputChannels; + + // Data & evaluators + void *Data; + + _cmsOPTeval16Fn Eval16Fn; + _cmsPipelineEvalFloatFn EvalFloatFn; + _cmsFreeUserDataFn FreeDataFn; + _cmsDupUserDataFn DupDataFn; + + cmsContext ContextID; // Environment + + cmsBool SaveAs8Bits; // Implementation-specific: save as 8 bits if possible +}; + +// LUT reading & creation ------------------------------------------------------------------------------------------- + +// Read tags using low-level function, provide necessary glue code to adapt versions, etc. All those return a brand new copy +// of the LUTS, since ownership of original is up to the profile. The user should free allocated resources. + +cmsPipeline* _cmsReadInputLUT(cmsHPROFILE hProfile, int Intent); +cmsPipeline* _cmsReadOutputLUT(cmsHPROFILE hProfile, int Intent); +cmsPipeline* _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, int Intent); + +// Special values +cmsBool _cmsReadMediaWhitePoint(cmsCIEXYZ* Dest, cmsHPROFILE hProfile); +cmsBool _cmsReadCHAD(cmsMAT3* Dest, cmsHPROFILE hProfile); + +// Profile linker -------------------------------------------------------------------------------------------------- + +cmsPipeline* _cmsLinkProfiles(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number TheIntents[], + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags); + +// Sequence -------------------------------------------------------------------------------------------------------- + +cmsSEQ* _cmsReadProfileSequence(cmsHPROFILE hProfile); +cmsBool _cmsWriteProfileSequence(cmsHPROFILE hProfile, const cmsSEQ* seq); +cmsSEQ* _cmsCompileProfileSequence(cmsContext ContextID, cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[]); + + +// LUT optimization ------------------------------------------------------------------------------------------------ + +cmsUInt16Number _cmsQuantizeVal(cmsFloat64Number i, int MaxSamples); +int _cmsReasonableGridpointsByColorspace(cmsColorSpaceSignature Colorspace, cmsUInt32Number dwFlags); + +cmsBool _cmsEndPointsBySpace(cmsColorSpaceSignature Space, + cmsUInt16Number **White, + cmsUInt16Number **Black, + cmsUInt32Number *nOutputs); + +cmsBool _cmsOptimizePipeline(cmsContext ContextID, + cmsPipeline** Lut, + int Intent, + cmsUInt32Number* InputFormat, + cmsUInt32Number* OutputFormat, + cmsUInt32Number* dwFlags ); + + +// Hi level LUT building ---------------------------------------------------------------------------------------------- + +cmsPipeline* _cmsCreateGamutCheckPipeline(cmsContext ContextID, + cmsHPROFILE hProfiles[], + cmsBool BPC[], + cmsUInt32Number Intents[], + cmsFloat64Number AdaptationStates[], + cmsUInt32Number nGamutPCSposition, + cmsHPROFILE hGamut); + + +// Formatters ------------------------------------------------------------------------------------------------------------ + +#define cmsFLAGS_CAN_CHANGE_FORMATTER 0x02000000 // Allow change buffer format + +cmsBool _cmsFormatterIsFloat(cmsUInt32Number Type); +cmsBool _cmsFormatterIs8bit(cmsUInt32Number Type); + +cmsFormatter _cmsGetFormatter(cmsContext ContextID, + cmsUInt32Number Type, // Specific type, i.e. TYPE_RGB_8 + cmsFormatterDirection Dir, + cmsUInt32Number dwFlags); + + +#ifndef CMS_NO_HALF_SUPPORT + +// Half float +cmsFloat32Number _cmsHalf2Float(cmsUInt16Number h); +cmsUInt16Number _cmsFloat2Half(cmsFloat32Number flt); + +#endif + +// Transform logic ------------------------------------------------------------------------------------------------------ + +struct _cmstransform_struct; + +typedef struct { + + // 1-pixel cache (16 bits only) + cmsUInt16Number CacheIn[cmsMAXCHANNELS]; + cmsUInt16Number CacheOut[cmsMAXCHANNELS]; + +} _cmsCACHE; + + + +// Transformation +typedef struct _cmstransform_struct { + + cmsUInt32Number InputFormat, OutputFormat; // Keep formats for further reference + + // Points to transform code + _cmsTransformFn xform; + + // Formatters, cannot be embedded into LUT because cache + cmsFormatter16 FromInput; + cmsFormatter16 ToOutput; + + cmsFormatterFloat FromInputFloat; + cmsFormatterFloat ToOutputFloat; + + // 1-pixel cache seed for zero as input (16 bits, read only) + _cmsCACHE Cache; + + // A Pipeline holding the full (optimized) transform + cmsPipeline* Lut; + + // A Pipeline holding the gamut check. It goes from the input space to bilevel + cmsPipeline* GamutCheck; + + // Colorant tables + cmsNAMEDCOLORLIST* InputColorant; // Input Colorant table + cmsNAMEDCOLORLIST* OutputColorant; // Colorant table (for n chans > CMYK) + + // Informational only + cmsColorSpaceSignature EntryColorSpace; + cmsColorSpaceSignature ExitColorSpace; + + // White points (informative only) + cmsCIEXYZ EntryWhitePoint; + cmsCIEXYZ ExitWhitePoint; + + // Profiles used to create the transform + cmsSEQ* Sequence; + + cmsUInt32Number dwOriginalFlags; + cmsFloat64Number AdaptationState; + + // The intent of this transform. That is usually the last intent in the profilechain, but may differ + cmsUInt32Number RenderingIntent; + + // An id that uniquely identifies the running context. May be null. + cmsContext ContextID; + + // A user-defined pointer that can be used to store data for transform plug-ins + void* UserData; + _cmsFreeUserDataFn FreeUserData; + +} _cmsTRANSFORM; + +// -------------------------------------------------------------------------------------------------- + +cmsHTRANSFORM _cmsChain2Lab(cmsContext ContextID, + cmsUInt32Number nProfiles, + cmsUInt32Number InputFormat, + cmsUInt32Number OutputFormat, + const cmsUInt32Number Intents[], + const cmsHPROFILE hProfiles[], + const cmsBool BPC[], + const cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags); + + +cmsToneCurve* _cmsBuildKToneCurve(cmsContext ContextID, + cmsUInt32Number nPoints, + cmsUInt32Number nProfiles, + const cmsUInt32Number Intents[], + const cmsHPROFILE hProfiles[], + const cmsBool BPC[], + const cmsFloat64Number AdaptationStates[], + cmsUInt32Number dwFlags); + +cmsBool _cmsAdaptationMatrix(cmsMAT3* r, const cmsMAT3* ConeMatrix, const cmsCIEXYZ* FromIll, const cmsCIEXYZ* ToIll); + +cmsBool _cmsBuildRGB2XYZtransferMatrix(cmsMAT3* r, const cmsCIExyY* WhitePoint, const cmsCIExyYTRIPLE* Primaries); + + +#define _lcms_internal_H +#endif +//<<<+++OPENSOURCE_MUST_END diff --git a/third_party/lcms2-2.6/0000-tag-type-confusion.patch b/third_party/lcms2-2.6/0000-tag-type-confusion.patch deleted file mode 100644 index df94cb7bfe..0000000000 --- a/third_party/lcms2-2.6/0000-tag-type-confusion.patch +++ /dev/null @@ -1,14 +0,0 @@ -diff --git a/third_party/lcms2-2.6/src/cmsio0.c b/third_party/lcms2-2.6/src/cmsio0.c -index 6549d15..5f9f08a 100644 ---- a/third_party/lcms2-2.6/src/cmsio0.c -+++ b/third_party/lcms2-2.6/src/cmsio0.c -@@ -719,7 +719,8 @@ cmsBool _cmsReadHeader(_cmsICCPROFILE* Icc) - for (j=0; j < Icc ->TagCount; j++) { - - if ((Icc ->TagOffsets[j] == Tag.offset) && -- (Icc ->TagSizes[j] == Tag.size)) { -+ (Icc ->TagSizes[j] == Tag.size) && -+ (Icc ->TagNames[j] == Tag.sig)) { - - Icc ->TagLinked[Icc ->TagCount] = Icc ->TagNames[j]; - } diff --git a/third_party/lcms2-2.6/0001-from16-to-8-overflow.patch b/third_party/lcms2-2.6/0001-from16-to-8-overflow.patch deleted file mode 100644 index 6e7b16d061..0000000000 --- a/third_party/lcms2-2.6/0001-from16-to-8-overflow.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/third_party/lcms2-2.6/src/lcms2_internal.h b/third_party/lcms2-2.6/src/lcms2_internal.h -index 8617e92..cc76d48 100644 ---- a/third_party/lcms2-2.6/src/lcms2_internal.h -+++ b/third_party/lcms2-2.6/src/lcms2_internal.h -@@ -94,7 +94,7 @@ - - // A fast way to convert from/to 16 <-> 8 bits - #define FROM_8_TO_16(rgb) (cmsUInt16Number) ((((cmsUInt16Number) (rgb)) << 8)|(rgb)) --#define FROM_16_TO_8(rgb) (cmsUInt8Number) ((((rgb) * 65281 + 8388608) >> 24) & 0xFF) -+#define FROM_16_TO_8(rgb) (cmsUInt8Number) ((((cmsUInt32Number)(rgb) * 65281U + 8388608U) >> 24) & 0xFFU) - - // Code analysis is broken on asserts - #ifdef _MSC_VER diff --git a/third_party/lcms2-2.6/0002-infinite-loop-GrowNamedColorList.patch b/third_party/lcms2-2.6/0002-infinite-loop-GrowNamedColorList.patch deleted file mode 100644 index 9c01a51579..0000000000 --- a/third_party/lcms2-2.6/0002-infinite-loop-GrowNamedColorList.patch +++ /dev/null @@ -1,34 +0,0 @@ -diff --git a/third_party/lcms2-2.6/src/cmsnamed.c b/third_party/lcms2-2.6/src/cmsnamed.c -index acfd1c8..ef1eb30 100644 ---- a/third_party/lcms2-2.6/src/cmsnamed.c -+++ b/third_party/lcms2-2.6/src/cmsnamed.c -@@ -514,8 +514,12 @@ cmsNAMEDCOLORLIST* CMSEXPORT cmsAllocNamedColorList(cmsContext ContextID, cmsUIn - v ->nColors = 0; - v ->ContextID = ContextID; - -- while (v -> Allocated < n) -- GrowNamedColorList(v); -+ while (v -> Allocated < n) { -+ if (!GrowNamedColorList(v)) { -+ cmsFreeNamedColorList(v); -+ return NULL; -+ } -+ } - - strncpy(v ->Prefix, Prefix, sizeof(v ->Prefix)-1); - strncpy(v ->Suffix, Suffix, sizeof(v ->Suffix)-1); -@@ -544,8 +548,12 @@ cmsNAMEDCOLORLIST* CMSEXPORT cmsDupNamedColorList(const cmsNAMEDCOLORLIST* v) - if (NewNC == NULL) return NULL; - - // For really large tables we need this -- while (NewNC ->Allocated < v ->Allocated) -- GrowNamedColorList(NewNC); -+ while (NewNC ->Allocated < v ->Allocated) { -+ if (!GrowNamedColorList(NewNC)) { -+ cmsFreeNamedColorList(NewNC); -+ return NULL; -+ } -+ } - - memmove(NewNC ->Prefix, v ->Prefix, sizeof(v ->Prefix)); - memmove(NewNC ->Suffix, v ->Suffix, sizeof(v ->Suffix)); diff --git a/third_party/lcms2-2.6/0003-uninit.patch b/third_party/lcms2-2.6/0003-uninit.patch deleted file mode 100644 index 476007e776..0000000000 --- a/third_party/lcms2-2.6/0003-uninit.patch +++ /dev/null @@ -1,30 +0,0 @@ -diff --git a/third_party/lcms2-2.6/src/cmstypes.c b/third_party/lcms2-2.6/src/cmstypes.c -index 06742b5..44c5b87 100644 ---- a/third_party/lcms2-2.6/src/cmstypes.c -+++ b/third_party/lcms2-2.6/src/cmstypes.c -@@ -2964,7 +2964,7 @@ void *Type_ColorantTable_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER - { - cmsUInt32Number i, Count; - cmsNAMEDCOLORLIST* List; -- char Name[34]; -+ char Name[33]; - cmsUInt16Number PCS[3]; - - -@@ -2979,7 +2979,7 @@ void *Type_ColorantTable_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER - for (i=0; i < Count; i++) { - - if (io ->Read(io, Name, 32, 1) != 1) goto Error; -- Name[33] = 0; -+ Name[32] = 0; - - if (!_cmsReadUInt16Array(io, 3, PCS)) goto Error; - -@@ -3106,6 +3106,7 @@ void *Type_NamedColor_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* i - - memset(Colorant, 0, sizeof(Colorant)); - if (io -> Read(io, Root, 32, 1) != 1) return NULL; -+ Root[32] = 0; - if (!_cmsReadUInt16Array(io, 3, PCS)) goto Error; - if (!_cmsReadUInt16Array(io, nDeviceCoords, Colorant)) goto Error; - diff --git a/third_party/lcms2-2.6/0004-memory-leak-Type_Curve_Read.patch b/third_party/lcms2-2.6/0004-memory-leak-Type_Curve_Read.patch deleted file mode 100644 index 341a1c6b47..0000000000 --- a/third_party/lcms2-2.6/0004-memory-leak-Type_Curve_Read.patch +++ /dev/null @@ -1,28 +0,0 @@ -diff --git a/third_party/lcms2-2.6/src/cmstypes.c b/third_party/lcms2-2.6/src/cmstypes.c -index 06742b5..9fe5e2a 100644 ---- a/third_party/lcms2-2.6/src/cmstypes.c -+++ b/third_party/lcms2-2.6/src/cmstypes.c -@@ -1112,7 +1112,10 @@ void *Type_Curve_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cm - NewGamma = cmsBuildTabulatedToneCurve16(self ->ContextID, Count, NULL); - if (!NewGamma) return NULL; - -- if (!_cmsReadUInt16Array(io, Count, NewGamma -> Table16)) return NULL; -+ if (!_cmsReadUInt16Array(io, Count, NewGamma -> Table16)) { -+ cmsFreeToneCurve(NewGamma); -+ return NULL; -+ } - - *nItems = 1; - return NewGamma; -@@ -2350,7 +2353,10 @@ cmsStage* ReadCLUT(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUI - - for (i=0; i < Data ->nEntries; i++) { - -- if (io ->Read(io, &v, sizeof(cmsUInt8Number), 1) != 1) return NULL; -+ if (io ->Read(io, &v, sizeof(cmsUInt8Number), 1) != 1) { -+ cmsStageFree(CLUT); -+ return NULL; -+ } - Data ->Tab.T[i] = FROM_8_TO_16(v); - } - diff --git a/third_party/lcms2-2.6/0005-memory-leak-AllocEmptyTransform.patch b/third_party/lcms2-2.6/0005-memory-leak-AllocEmptyTransform.patch deleted file mode 100644 index a85a9682bd..0000000000 --- a/third_party/lcms2-2.6/0005-memory-leak-AllocEmptyTransform.patch +++ /dev/null @@ -1,34 +0,0 @@ -diff --git a/third_party/lcms2-2.6/src/cmsxform.c b/third_party/lcms2-2.6/src/cmsxform.c -index eddb9bd..6466d27 100644 ---- a/third_party/lcms2-2.6/src/cmsxform.c -+++ b/third_party/lcms2-2.6/src/cmsxform.c -@@ -593,7 +593,10 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut, - - // Allocate needed memory - _cmsTRANSFORM* p = (_cmsTRANSFORM*) _cmsMallocZero(ContextID, sizeof(_cmsTRANSFORM)); -- if (!p) return NULL; -+ if (!p) { -+ cmsPipelineFree(lut); -+ return NULL; -+ } - - // Store the proposed pipeline - p ->Lut = lut; -@@ -643,7 +646,7 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut, - if (p ->FromInputFloat == NULL || p ->ToOutputFloat == NULL) { - - cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format"); -- _cmsFree(ContextID, p); -+ cmsDeleteTransform(p); - return NULL; - } - -@@ -673,7 +676,7 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut, - if (p ->FromInput == NULL || p ->ToOutput == NULL) { - - cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format"); -- _cmsFree(ContextID, p); -+ cmsDeleteTransform(p); - return NULL; - } - diff --git a/third_party/lcms2-2.6/0006-memory-leak-Type_NamedColor_Read.patch b/third_party/lcms2-2.6/0006-memory-leak-Type_NamedColor_Read.patch deleted file mode 100644 index 5d25d5a737..0000000000 --- a/third_party/lcms2-2.6/0006-memory-leak-Type_NamedColor_Read.patch +++ /dev/null @@ -1,22 +0,0 @@ -diff --git a/third_party/lcms2-2.6/src/cmstypes.c b/third_party/lcms2-2.6/src/cmstypes.c -index feba387..4d24fc2 100644 ---- a/third_party/lcms2-2.6/src/cmstypes.c -+++ b/third_party/lcms2-2.6/src/cmstypes.c -@@ -3102,7 +3102,7 @@ void *Type_NamedColor_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* i - - if (nDeviceCoords > cmsMAXCHANNELS) { - cmsSignalError(self->ContextID, cmsERROR_RANGE, "Too many device coordinates '%d'", nDeviceCoords); -- return 0; -+ goto Error; - } - for (i=0; i < count; i++) { - -@@ -3111,7 +3111,7 @@ void *Type_NamedColor_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* i - char Root[33]; - - memset(Colorant, 0, sizeof(Colorant)); -- if (io -> Read(io, Root, 32, 1) != 1) return NULL; -+ if (io -> Read(io, Root, 32, 1) != 1) goto Error; - Root[32] = 0; - if (!_cmsReadUInt16Array(io, 3, PCS)) goto Error; - if (!_cmsReadUInt16Array(io, nDeviceCoords, Colorant)) goto Error; diff --git a/third_party/lcms2-2.6/0007-memory-leak-OptimizeByResampling.patch b/third_party/lcms2-2.6/0007-memory-leak-OptimizeByResampling.patch deleted file mode 100644 index 2c55b94fa1..0000000000 --- a/third_party/lcms2-2.6/0007-memory-leak-OptimizeByResampling.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/third_party/lcms2-2.6/src/cmsopt.c b/third_party/lcms2-2.6/src/cmsopt.c -index f885ef3..684910d 100644 ---- a/third_party/lcms2-2.6/src/cmsopt.c -+++ b/third_party/lcms2-2.6/src/cmsopt.c -@@ -612,7 +612,7 @@ cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3 - - // Allocate the CLUT - CLUT = cmsStageAllocCLut16bit(Src ->ContextID, nGridPoints, Src ->InputChannels, Src->OutputChannels, NULL); -- if (CLUT == NULL) return FALSE; -+ if (CLUT == NULL) goto Error; - - // Add the CLUT to the destination LUT - if (!cmsPipelineInsertStage(Dest, cmsAT_END, CLUT)) { diff --git a/third_party/lcms2-2.6/0008-memory-leak-Type_MPEmatrix_Read.patch b/third_party/lcms2-2.6/0008-memory-leak-Type_MPEmatrix_Read.patch deleted file mode 100644 index 93ee3d3fde..0000000000 --- a/third_party/lcms2-2.6/0008-memory-leak-Type_MPEmatrix_Read.patch +++ /dev/null @@ -1,30 +0,0 @@ -diff --git a/third_party/lcms2-2.6/src/cmstypes.c b/third_party/lcms2-2.6/src/cmstypes.c -index 441d6bb..15199c7 100644 ---- a/third_party/lcms2-2.6/src/cmstypes.c -+++ b/third_party/lcms2-2.6/src/cmstypes.c -@@ -4203,7 +4203,11 @@ void *Type_MPEmatrix_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io - - cmsFloat32Number v; - -- if (!_cmsReadFloat32Number(io, &v)) return NULL; -+ if (!_cmsReadFloat32Number(io, &v)) { -+ _cmsFree(self ->ContextID, Matrix); -+ _cmsFree(self ->ContextID, Offsets); -+ return NULL; -+ } - Matrix[i] = v; - } - -@@ -4212,7 +4216,11 @@ void *Type_MPEmatrix_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io - - cmsFloat32Number v; - -- if (!_cmsReadFloat32Number(io, &v)) return NULL; -+ if (!_cmsReadFloat32Number(io, &v)) { -+ _cmsFree(self ->ContextID, Matrix); -+ _cmsFree(self ->ContextID, Offsets); -+ return NULL; -+ } - Offsets[i] = v; - } - diff --git a/third_party/lcms2-2.6/0009-cmsStageAllocMatrix-param-swap.patch b/third_party/lcms2-2.6/0009-cmsStageAllocMatrix-param-swap.patch deleted file mode 100644 index 26db3dd223..0000000000 --- a/third_party/lcms2-2.6/0009-cmsStageAllocMatrix-param-swap.patch +++ /dev/null @@ -1,20 +0,0 @@ -diff --git a/third_party/lcms2-2.6/src/cmslut.c b/third_party/lcms2-2.6/src/cmslut.c -index 73e6726..9b0eb4b 100644 ---- a/third_party/lcms2-2.6/src/cmslut.c -+++ b/third_party/lcms2-2.6/src/cmslut.c -@@ -414,13 +414,13 @@ cmsStage* CMSEXPORT cmsStageAllocMatrix(cmsContext ContextID, cmsUInt32Number R - - if (Offset != NULL) { - -- NewElem ->Offset = (cmsFloat64Number*) _cmsCalloc(ContextID, Cols, sizeof(cmsFloat64Number)); -+ NewElem ->Offset = (cmsFloat64Number*) _cmsCalloc(ContextID, Rows, sizeof(cmsFloat64Number)); - if (NewElem->Offset == NULL) { - MatrixElemTypeFree(NewMPE); - return NULL; - } - -- for (i=0; i < Cols; i++) { -+ for (i=0; i < Rows; i++) { - NewElem ->Offset[i] = Offset[i]; - } - diff --git a/third_party/lcms2-2.6/0010-reject-nan.patch b/third_party/lcms2-2.6/0010-reject-nan.patch deleted file mode 100644 index 2cf49ca0f7..0000000000 --- a/third_party/lcms2-2.6/0010-reject-nan.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/third_party/lcms2-2.6/src/cmsplugin.c b/third_party/lcms2-2.6/src/cmsplugin.c -index 8903d2b..b95befb 100644 ---- a/third_party/lcms2-2.6/src/cmsplugin.c -+++ b/third_party/lcms2-2.6/src/cmsplugin.c -@@ -179,6 +179,8 @@ cmsBool CMSEXPORT _cmsReadFloat32Number(cmsIOHANDLER* io, cmsFloat32Number* n) - - tmp = _cmsAdjustEndianess32(tmp); - *n = *(cmsFloat32Number*) &tmp; -+ if (isnan(*n)) -+ return FALSE; - } - return TRUE; - } diff --git a/third_party/lcms2-2.6/0011-memory-leak-ReadSegmentedCurve.patch b/third_party/lcms2-2.6/0011-memory-leak-ReadSegmentedCurve.patch deleted file mode 100644 index a6cfe02b8c..0000000000 --- a/third_party/lcms2-2.6/0011-memory-leak-ReadSegmentedCurve.patch +++ /dev/null @@ -1,36 +0,0 @@ -diff --git a/third_party/lcms2-2.6/src/cmstypes.c b/third_party/lcms2-2.6/src/cmstypes.c -index 15199c7..04dd0c4 100644 ---- a/third_party/lcms2-2.6/src/cmstypes.c -+++ b/third_party/lcms2-2.6/src/cmstypes.c -@@ -3968,7 +3968,7 @@ cmsToneCurve* ReadSegmentedCurve(struct _cms_typehandler_struct* self, cmsIOHAND - case cmsSigSampledCurveSeg: { - cmsUInt32Number Count; - -- if (!_cmsReadUInt32Number(io, &Count)) return NULL; -+ if (!_cmsReadUInt32Number(io, &Count)) goto Error; - - Segments[i].nGridPoints = Count; - Segments[i].SampledPoints = (cmsFloat32Number*) _cmsCalloc(self ->ContextID, Count, sizeof(cmsFloat32Number)); -@@ -3987,7 +3987,7 @@ cmsToneCurve* ReadSegmentedCurve(struct _cms_typehandler_struct* self, cmsIOHAND - _cmsTagSignature2String(String, (cmsTagSignature) ElementSig); - cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown curve element type '%s' found.", String); - } -- return NULL; -+ goto Error; - - } - } -@@ -4001,7 +4001,12 @@ cmsToneCurve* ReadSegmentedCurve(struct _cms_typehandler_struct* self, cmsIOHAND - return Curve; - - Error: -- if (Segments) _cmsFree(self ->ContextID, Segments); -+ if (Segments) { -+ for (i=0; i < nSegments; i++) { -+ if (Segments[i].SampledPoints) _cmsFree(self ->ContextID, Segments[i].SampledPoints); -+ } -+ _cmsFree(self ->ContextID, Segments); -+ } - return NULL; - } - diff --git a/third_party/lcms2-2.6/0012-backport-c0a98d86.patch b/third_party/lcms2-2.6/0012-backport-c0a98d86.patch deleted file mode 100644 index 3041e31d76..0000000000 --- a/third_party/lcms2-2.6/0012-backport-c0a98d86.patch +++ /dev/null @@ -1,53 +0,0 @@ -diff --git a/third_party/lcms2-2.6/src/cmsintrp.c b/third_party/lcms2-2.6/src/cmsintrp.c -index 5d5f35d..14c6856 100644 ---- a/third_party/lcms2-2.6/src/cmsintrp.c -+++ b/third_party/lcms2-2.6/src/cmsintrp.c -@@ -215,7 +215,7 @@ void LinLerp1D(register const cmsUInt16Number Value[], - // To prevent out of bounds indexing - cmsINLINE cmsFloat32Number fclamp(cmsFloat32Number v) - { -- return v < 0.0f ? 0.0f : (v > 1.0f ? 1.0f : v); -+ return ((v < 0.0f) || isnan(v)) ? 0.0f : (v > 1.0f ? 1.0f : v); - } - - // Floating-point version of 1D interpolation -diff --git a/third_party/lcms2-2.6/src/cmsio0.c b/third_party/lcms2-2.6/src/cmsio0.c -index 5f9f08a..3ed730a 100644 ---- a/third_party/lcms2-2.6/src/cmsio0.c -+++ b/third_party/lcms2-2.6/src/cmsio0.c -@@ -1475,6 +1475,17 @@ void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig) - // If the element is already in memory, return the pointer - if (Icc -> TagPtrs[n]) { - -+ if (Icc->TagTypeHandlers[n] == NULL) goto Error; -+ -+ // Sanity check -+ BaseType = Icc->TagTypeHandlers[n]->Signature; -+ if (BaseType == 0) goto Error; -+ -+ TagDescriptor = _cmsGetTagDescriptor(Icc->ContextID, sig); -+ if (TagDescriptor == NULL) goto Error; -+ -+ if (!IsTypeSupported(TagDescriptor, BaseType)) goto Error; -+ - if (Icc ->TagSaveAsRaw[n]) goto Error; // We don't support read raw tags as cooked - - _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); -diff --git a/third_party/lcms2-2.6/src/cmstypes.c b/third_party/lcms2-2.6/src/cmstypes.c -index 04dd0c4..386439b 100644 ---- a/third_party/lcms2-2.6/src/cmstypes.c -+++ b/third_party/lcms2-2.6/src/cmstypes.c -@@ -4297,8 +4297,12 @@ void *Type_MPEclut_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, - - // Copy MAX_INPUT_DIMENSIONS at most. Expand to cmsUInt32Number - nMaxGrids = InputChans > MAX_INPUT_DIMENSIONS ? MAX_INPUT_DIMENSIONS : InputChans; -- for (i=0; i < nMaxGrids; i++) GridPoints[i] = (cmsUInt32Number) Dimensions8[i]; - -+ for (i = 0; i < nMaxGrids; i++) { -+ if (Dimensions8[i] == 1) goto Error; // Impossible value, 0 for no CLUT and then 2 at least -+ GridPoints[i] = (cmsUInt32Number)Dimensions8[i]; -+ } -+ - // Allocate the true CLUT - mpe = cmsStageAllocCLutFloatGranular(self ->ContextID, GridPoints, InputChans, OutputChans, NULL); - if (mpe == NULL) goto Error; diff --git a/third_party/lcms2-2.6/0013-utf8.patch b/third_party/lcms2-2.6/0013-utf8.patch deleted file mode 100644 index c28d2c281b..0000000000 --- a/third_party/lcms2-2.6/0013-utf8.patch +++ /dev/null @@ -1,99 +0,0 @@ -diff --git a/third_party/lcms2-2.6/src/cmscgats.c b/third_party/lcms2-2.6/src/cmscgats.c -index 9017435..5720c66 100644 ---- a/third_party/lcms2-2.6/src/cmscgats.c -+++ b/third_party/lcms2-2.6/src/cmscgats.c -@@ -258,7 +258,7 @@ static PROPERTY PredefinedProperties[] = { - // needed. - - {"SAMPLE_BACKING", WRITE_STRINGIFY}, // Identifies the backing material used behind the sample during -- // measurement. Allowed values are “black? “white? or {"na". -+ // measurement. Allowed values are "black" "white" or "na". - - {"CHISQ_DOF", WRITE_STRINGIFY}, // Degrees of freedom associated with the Chi squared statistic - -@@ -274,7 +274,7 @@ static PROPERTY PredefinedProperties[] = { - // denote the use of filters such as none, D65, Red, Green or Blue. - - {"POLARIZATION", WRITE_STRINGIFY}, // Identifies the use of a physical polarization filter during measurement. Allowed -- // values are {"yes? “white? “none?or “na? -+ // values are "yes" "white" "none" or "na" - - {"WEIGHTING_FUNCTION", WRITE_PAIR}, // Indicates such functions as: the CIE standard observer functions used in the - // calculation of various data parameters (2 degree and 10 degree), CIE standard -diff --git a/third_party/lcms2-2.6/src/cmstypes.c b/third_party/lcms2-2.6/src/cmstypes.c -index 386439b..e5ed06c 100644 ---- a/third_party/lcms2-2.6/src/cmstypes.c -+++ b/third_party/lcms2-2.6/src/cmstypes.c -@@ -962,7 +962,7 @@ cmsBool Type_Text_Description_Write(struct _cms_typehandler_struct* self, cmsIO - len = cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, NULL, 0); - - // From ICC3.4: It has been found that textDescriptionType can contain misaligned data -- //(see clause 4.1 for the definition of “aligned?. Because the Unicode language -+ //(see clause 4.1 for the definition of 'aligned'. Because the Unicode language - // code and Unicode count immediately follow the ASCII description, their - // alignment is not correct if the ASCII count is not a multiple of four. The - // ScriptCode code is misaligned when the ASCII count is odd. Profile reading and -@@ -3064,9 +3064,10 @@ void Type_ColorantTable_Free(struct _cms_typehandler_struct* self, void* Ptr) - //The namedColor2Type is a count value and array of structures that provide color - //coordinates for 7-bit ASCII color names. For each named color, a PCS and optional - //device representation of the color are given. Both representations are 16-bit values. --//The device representation corresponds to the header’s “color space of data?field. --//This representation should be consistent with the “number of device components?//field in the namedColor2Type. If this field is 0, device coordinates are not provided. --//The PCS representation corresponds to the header’s PCS field. The PCS representation -+//The device representation corresponds to the header's 'color space of data' field. -+//This representation should be consistent with the 'number of device components' -+//field in the namedColor2Type. If this field is 0, device coordinates are not provided. -+//The PCS representation corresponds to the header's PCS field. The PCS representation - //is always provided. Color names are fixed-length, 32-byte fields including null - //termination. In order to maintain maximum portability, it is strongly recommended - //that special characters of the 7-bit ASCII set not be used. -@@ -3809,7 +3810,8 @@ void Type_Screening_Free(struct _cms_typehandler_struct* self, void* Ptr) - // ******************************************************************************** - // - //This type represents a set of viewing condition parameters including: --//CIE ’absolute?illuminant white point tristimulus values and CIE ’absolute?//surround tristimulus values. -+//CIE 'absolute'illuminant white point tristimulus values and CIE 'absolute' -+//surround tristimulus values. - - static - void *Type_ViewingConditions_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) -@@ -3895,7 +3897,7 @@ void GenericMPEfree(struct _cms_typehandler_struct* self, void *Ptr) - } - - // Each curve is stored in one or more curve segments, with break-points specified between curve segments. --// The first curve segment always starts at –Infinity, and the last curve segment always ends at +Infinity. The -+// The first curve segment always starts at -Infinity, and the last curve segment always ends at +Infinity. The - // first and last curve segments shall be specified in terms of a formula, whereas the other segments shall be - // specified either in terms of a formula, or by a sampled curve. - -diff --git a/third_party/lcms2-2.6/src/cmsvirt.c b/third_party/lcms2-2.6/src/cmsvirt.c -index b324c99..d19ace1 100644 ---- a/third_party/lcms2-2.6/src/cmsvirt.c -+++ b/third_party/lcms2-2.6/src/cmsvirt.c -@@ -612,18 +612,18 @@ cmsHPROFILE CMSEXPORT cmsCreateXYZProfile(void) - - //sRGB Curves are defined by: - // --//If R’sRGB,G’sRGB, B’sRGB < 0.04045 -+//If R'sRGB,G'sRGB, B'sRGB < 0.04045 - // --// R = R’sRGB / 12.92 --// G = G’sRGB / 12.92 --// B = B’sRGB / 12.92 -+// R = R'sRGB / 12.92 -+// G = G'sRGB / 12.92 -+// B = B'sRGB / 12.92 - // - // --//else if R’sRGB,G’sRGB, B’sRGB >= 0.04045 -+//else if R'sRGB,G'sRGB, B'sRGB >= 0.04045 - // --// R = ((R’sRGB + 0.055) / 1.055)^2.4 --// G = ((G’sRGB + 0.055) / 1.055)^2.4 --// B = ((B’sRGB + 0.055) / 1.055)^2.4 -+// R = ((R'sRGB + 0.055) / 1.055)^2.4 -+// G = ((G'sRGB + 0.055) / 1.055)^2.4 -+// B = ((B'sRGB + 0.055) / 1.055)^2.4 - - static - cmsToneCurve* Build_sRGBGamma(cmsContext ContextID) diff --git a/third_party/lcms2-2.6/0014-avoid-fixed-inf.patch b/third_party/lcms2-2.6/0014-avoid-fixed-inf.patch deleted file mode 100644 index 563787a941..0000000000 --- a/third_party/lcms2-2.6/0014-avoid-fixed-inf.patch +++ /dev/null @@ -1,95 +0,0 @@ -diff --git a/third_party/lcms2-2.6/src/cmsopt.c b/third_party/lcms2-2.6/src/cmsopt.c -index 684910d..76de015 100644 ---- a/third_party/lcms2-2.6/src/cmsopt.c -+++ b/third_party/lcms2-2.6/src/cmsopt.c -@@ -1443,7 +1443,7 @@ void MatShaperEval16(register const cmsUInt16Number In[], - - // This table converts from 8 bits to 1.14 after applying the curve - static --void FillFirstShaper(cmsS1Fixed14Number* Table, cmsToneCurve* Curve) -+cmsBool FillFirstShaper(cmsS1Fixed14Number* Table, cmsToneCurve* Curve) - { - int i; - cmsFloat32Number R, y; -@@ -1452,14 +1452,17 @@ void FillFirstShaper(cmsS1Fixed14Number* Table, cmsToneCurve* Curve) - - R = (cmsFloat32Number) (i / 255.0); - y = cmsEvalToneCurveFloat(Curve, R); -+ if (isinf(y)) -+ return FALSE; - - Table[i] = DOUBLE_TO_1FIXED14(y); - } -+ return TRUE; - } - - // This table converts form 1.14 (being 0x4000 the last entry) to 8 bits after applying the curve - static --void FillSecondShaper(cmsUInt16Number* Table, cmsToneCurve* Curve, cmsBool Is8BitsOutput) -+cmsBool FillSecondShaper(cmsUInt16Number* Table, cmsToneCurve* Curve, cmsBool Is8BitsOutput) - { - int i; - cmsFloat32Number R, Val; -@@ -1468,6 +1471,8 @@ void FillSecondShaper(cmsUInt16Number* Table, cmsToneCurve* Curve, cmsBool Is8Bi - - R = (cmsFloat32Number) (i / 16384.0); - Val = cmsEvalToneCurveFloat(Curve, R); // Val comes 0..1.0 -+ if (isinf(Val)) -+ return FALSE; - - if (Is8BitsOutput) { - -@@ -1482,6 +1487,7 @@ void FillSecondShaper(cmsUInt16Number* Table, cmsToneCurve* Curve, cmsBool Is8Bi - } - else Table[i] = _cmsQuickSaturateWord(Val * 65535.0); - } -+ return TRUE; - } - - // Compute the matrix-shaper structure -@@ -1499,13 +1505,19 @@ cmsBool SetMatShaper(cmsPipeline* Dest, cmsToneCurve* Curve1[3], cmsMAT3* Mat, c - p -> ContextID = Dest -> ContextID; - - // Precompute tables -- FillFirstShaper(p ->Shaper1R, Curve1[0]); -- FillFirstShaper(p ->Shaper1G, Curve1[1]); -- FillFirstShaper(p ->Shaper1B, Curve1[2]); -+ if (!FillFirstShaper(p ->Shaper1R, Curve1[0])) -+ goto Error; -+ if (!FillFirstShaper(p ->Shaper1G, Curve1[1])) -+ goto Error; -+ if (!FillFirstShaper(p ->Shaper1B, Curve1[2])) -+ goto Error; - -- FillSecondShaper(p ->Shaper2R, Curve2[0], Is8Bits); -- FillSecondShaper(p ->Shaper2G, Curve2[1], Is8Bits); -- FillSecondShaper(p ->Shaper2B, Curve2[2], Is8Bits); -+ if (!FillSecondShaper(p ->Shaper2R, Curve2[0], Is8Bits)) -+ goto Error; -+ if (!FillSecondShaper(p ->Shaper2G, Curve2[1], Is8Bits)) -+ goto Error; -+ if (!FillSecondShaper(p ->Shaper2B, Curve2[2], Is8Bits)) -+ goto Error; - - // Convert matrix to nFixed14. Note that those values may take more than 16 bits as - for (i=0; i < 3; i++) { -@@ -1531,6 +1543,9 @@ cmsBool SetMatShaper(cmsPipeline* Dest, cmsToneCurve* Curve1[3], cmsMAT3* Mat, c - // Fill function pointers - _cmsPipelineSetOptimizationParameters(Dest, MatShaperEval16, (void*) p, FreeMatShaper, DupMatShaper); - return TRUE; -+Error: -+ _cmsFree(Dest->ContextID, p); -+ return FALSE; - } - - // 8 bits on input allows matrix-shaper boot up to 25 Mpixels per second on RGB. That's fast! -@@ -1606,7 +1621,8 @@ cmsBool OptimizeMatrixShaper(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3 - *dwFlags |= cmsFLAGS_NOCACHE; - - // Setup the optimizarion routines -- SetMatShaper(Dest, mpeC1 ->TheCurves, &res, (cmsVEC3*) Data2 ->Offset, mpeC2->TheCurves, OutputFormat); -+ if (!SetMatShaper(Dest, mpeC1 ->TheCurves, &res, (cmsVEC3*) Data2 ->Offset, mpeC2->TheCurves, OutputFormat)) -+ goto Error; - } - - cmsPipelineFree(Src); diff --git a/third_party/lcms2-2.6/0015-sanitize-float-read.patch b/third_party/lcms2-2.6/0015-sanitize-float-read.patch deleted file mode 100644 index 70dc7b35cf..0000000000 --- a/third_party/lcms2-2.6/0015-sanitize-float-read.patch +++ /dev/null @@ -1,15 +0,0 @@ -diff --git a/third_party/lcms2-2.6/src/cmsplugin.c b/third_party/lcms2-2.6/src/cmsplugin.c -index b95befb..4ba998b 100644 ---- a/third_party/lcms2-2.6/src/cmsplugin.c -+++ b/third_party/lcms2-2.6/src/cmsplugin.c -@@ -182,7 +182,9 @@ cmsBool CMSEXPORT _cmsReadFloat32Number(cmsIOHANDLER* io, cmsFloat32Number* n) - if (isnan(*n)) - return FALSE; - } -- return TRUE; -+ -+ // fpclassify() required by C99 -+ return (fpclassify(*n) == FP_ZERO) || (fpclassify(*n) == FP_NORMAL); - } - - diff --git a/third_party/lcms2-2.6/0016-check-LUT-and-MPE.patch b/third_party/lcms2-2.6/0016-check-LUT-and-MPE.patch deleted file mode 100644 index bfa84e2eed..0000000000 --- a/third_party/lcms2-2.6/0016-check-LUT-and-MPE.patch +++ /dev/null @@ -1,170 +0,0 @@ -diff --git a/third_party/lcms2-2.6/src/cmslut.c b/third_party/lcms2-2.6/src/cmslut.c -index 9b0eb4b54..19d43361f 100644 ---- a/third_party/lcms2-2.6/src/cmslut.c -+++ b/third_party/lcms2-2.6/src/cmslut.c -@@ -1255,21 +1255,39 @@ cmsStage* CMSEXPORT cmsStageDup(cmsStage* mpe) - // *********************************************************************************************************** - - // This function sets up the channel count -- - static --void BlessLUT(cmsPipeline* lut) -+cmsBool BlessLUT(cmsPipeline* lut) - { - // We can set the input/ouput channels only if we have elements. - if (lut ->Elements != NULL) { - -- cmsStage *First, *Last; -+ cmsStage* prev; -+ cmsStage* next; -+ cmsStage* First; -+ cmsStage* Last; - - First = cmsPipelineGetPtrToFirstStage(lut); - Last = cmsPipelineGetPtrToLastStage(lut); - -- if (First != NULL)lut ->InputChannels = First ->InputChannels; -- if (Last != NULL) lut ->OutputChannels = Last ->OutputChannels; -+ if (First == NULL || Last == NULL) return FALSE; -+ -+ lut->InputChannels = First->InputChannels; -+ lut->OutputChannels = Last->OutputChannels; -+ -+ // Check chain consistency -+ prev = First; -+ next = prev->Next; -+ -+ while (next != NULL) -+ { -+ if (next->InputChannels != prev->OutputChannels) -+ return FALSE; -+ -+ next = next->Next; -+ prev = prev->Next; -+ } - } -+ return TRUE; - } - - -@@ -1331,6 +1349,7 @@ cmsPipeline* CMSEXPORT cmsPipelineAlloc(cmsContext ContextID, cmsUInt32Number In - { - cmsPipeline* NewLUT; - -+ // A value of zero in channels is allowed as placeholder - if (InputChannels >= cmsMAXCHANNELS || - OutputChannels >= cmsMAXCHANNELS) return NULL; - -@@ -1348,7 +1367,11 @@ cmsPipeline* CMSEXPORT cmsPipelineAlloc(cmsContext ContextID, cmsUInt32Number In - NewLUT ->Data = NewLUT; - NewLUT ->ContextID = ContextID; - -- BlessLUT(NewLUT); -+ if (!BlessLUT(NewLUT)) -+ { -+ _cmsFree(ContextID, NewLUT); -+ return NULL; -+ } - - return NewLUT; - } -@@ -1454,7 +1477,12 @@ cmsPipeline* CMSEXPORT cmsPipelineDup(const cmsPipeline* lut) - - NewLUT ->SaveAs8Bits = lut ->SaveAs8Bits; - -- BlessLUT(NewLUT); -+ if (!BlessLUT(NewLUT)) -+ { -+ _cmsFree(lut->ContextID, NewLUT); -+ return NULL; -+ } -+ - return NewLUT; - } - -@@ -1491,8 +1519,7 @@ int CMSEXPORT cmsPipelineInsertStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage - return FALSE; - } - -- BlessLUT(lut); -- return TRUE; -+ return BlessLUT(lut); - } - - // Unlink an element and return the pointer to it -@@ -1547,6 +1574,7 @@ void CMSEXPORT cmsPipelineUnlinkStage(cmsPipeline* lut, cmsStageLoc loc, cmsStag - else - cmsStageFree(Unlinked); - -+ // May fail, but we ignore it - BlessLUT(lut); - } - -@@ -1573,8 +1601,7 @@ cmsBool CMSEXPORT cmsPipelineCat(cmsPipeline* l1, const cmsPipeline* l2) - return FALSE; - } - -- BlessLUT(l1); -- return TRUE; -+ return BlessLUT(l1); - } - - -diff --git a/third_party/lcms2-2.6/src/cmstypes.c b/third_party/lcms2-2.6/src/cmstypes.c -index e5ed06c33..0256e247b 100644 ---- a/third_party/lcms2-2.6/src/cmstypes.c -+++ b/third_party/lcms2-2.6/src/cmstypes.c -@@ -1755,8 +1755,8 @@ void *Type_LUT8_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cms - if (!_cmsReadUInt8Number(io, NULL)) goto Error; - - // Do some checking -- if (InputChannels > cmsMAXCHANNELS) goto Error; -- if (OutputChannels > cmsMAXCHANNELS) goto Error; -+ if (InputChannels == 0 || InputChannels > cmsMAXCHANNELS) goto Error; -+ if (OutputChannels == 0 || OutputChannels > cmsMAXCHANNELS) goto Error; - - // Allocates an empty Pipeline - NewLUT = cmsPipelineAlloc(self ->ContextID, InputChannels, OutputChannels); -@@ -2048,8 +2048,8 @@ void *Type_LUT16_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cm - if (!_cmsReadUInt8Number(io, NULL)) return NULL; - - // Do some checking -- if (InputChannels > cmsMAXCHANNELS) goto Error; -- if (OutputChannels > cmsMAXCHANNELS) goto Error; -+ if (InputChannels == 0 || InputChannels > cmsMAXCHANNELS) goto Error; -+ if (OutputChannels == 0 || OutputChannels > cmsMAXCHANNELS) goto Error; - - // Allocates an empty LUT - NewLUT = cmsPipelineAlloc(self ->ContextID, InputChannels, OutputChannels); -@@ -2486,7 +2486,10 @@ void* Type_LUTA2B_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, c - if (!_cmsReadUInt32Number(io, &offsetC)) return NULL; - if (!_cmsReadUInt32Number(io, &offsetA)) return NULL; - -- // Allocates an empty LUT -+ if (inputChan == 0 || inputChan >= cmsMAXCHANNELS) return NULL; -+ if (outputChan == 0 || outputChan >= cmsMAXCHANNELS) return NULL; -+ -+ // Allocates an empty LUT - NewLUT = cmsPipelineAlloc(self ->ContextID, inputChan, outputChan); - if (NewLUT == NULL) return NULL; - -@@ -2794,6 +2797,9 @@ void* Type_LUTB2A_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, c - if (!_cmsReadUInt8Number(io, &inputChan)) return NULL; - if (!_cmsReadUInt8Number(io, &outputChan)) return NULL; - -+ if (inputChan == 0 || inputChan >= cmsMAXCHANNELS) return NULL; -+ if (outputChan == 0 || outputChan >= cmsMAXCHANNELS) return NULL; -+ - // Padding - if (!_cmsReadUInt16Number(io, NULL)) return NULL; - -@@ -4443,6 +4449,9 @@ void *Type_MPE_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsU - if (!_cmsReadUInt16Number(io, &InputChans)) return NULL; - if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL; - -+ if (InputChans == 0 || InputChans >= cmsMAXCHANNELS) return NULL; -+ if (OutputChans == 0 || OutputChans >= cmsMAXCHANNELS) return NULL; -+ - // Allocates an empty LUT - NewLUT = cmsPipelineAlloc(self ->ContextID, InputChans, OutputChans); - if (NewLUT == NULL) return NULL; diff --git a/third_party/lcms2-2.6/0017-upstream-integer-overflow-MPEmatrix_Read.patch b/third_party/lcms2-2.6/0017-upstream-integer-overflow-MPEmatrix_Read.patch deleted file mode 100644 index 47df7a887d..0000000000 --- a/third_party/lcms2-2.6/0017-upstream-integer-overflow-MPEmatrix_Read.patch +++ /dev/null @@ -1,85 +0,0 @@ -diff --git a/third_party/lcms2-2.6/src/cmscgats.c b/third_party/lcms2-2.6/src/cmscgats.c -index 5720c66a7..cce4cedba 100644 ---- a/third_party/lcms2-2.6/src/cmscgats.c -+++ b/third_party/lcms2-2.6/src/cmscgats.c -@@ -150,23 +150,24 @@ typedef struct { - SUBALLOCATOR Allocator; // String suballocator -- just to keep it fast - - // Parser state machine -- SYMBOL sy; // Current symbol -- int ch; // Current character -+ SYMBOL sy; // Current symbol -+ int ch; // Current character -+ -+ cmsInt32Number inum; // integer value -+ cmsFloat64Number dnum; // real value - -- int inum; // integer value -- cmsFloat64Number dnum; // real value - char id[MAXID]; // identifier - char str[MAXSTR]; // string - - // Allowed keywords & datasets. They have visibility on whole stream -- KEYVALUE* ValidKeywords; -- KEYVALUE* ValidSampleID; -+ KEYVALUE* ValidKeywords; -+ KEYVALUE* ValidSampleID; - - char* Source; // Points to loc. being parsed -- int lineno; // line counter for error reporting -+ cmsInt32Number lineno; // line counter for error reporting - - FILECTX* FileStack[MAXINCLUDE]; // Stack of files being parsed -- int IncludeSP; // Include Stack Pointer -+ cmsInt32Number IncludeSP; // Include Stack Pointer - - char* MemoryBlock; // The stream if holded in memory - -@@ -568,8 +569,8 @@ void ReadReal(cmsIT8* it8, int inum) - // Exponent, example 34.00E+20 - if (toupper(it8->ch) == 'E') { - -- int e; -- int sgn; -+ cmsInt32Number e; -+ cmsInt32Number sgn; - - NextCh(it8); sgn = 1; - -@@ -587,7 +588,7 @@ void ReadReal(cmsIT8* it8, int inum) - e = 0; - while (isdigit(it8->ch)) { - -- if ((cmsFloat64Number) e * 10L < INT_MAX) -+ if ((cmsFloat64Number) e * 10L < (cmsFloat64Number) +2147483647.0) - e = e * 10 + (it8->ch - '0'); - - NextCh(it8); -@@ -777,7 +778,7 @@ void InSymbol(cmsIT8* it8) - - while (isdigit(it8->ch)) { - -- if ((long) it8->inum * 10L > (long) INT_MAX) { -+ if ((cmsFloat64Number) it8->inum * 10L > (cmsFloat64Number) +2147483647.0) { - ReadReal(it8, it8->inum); - it8->sy = SDNUM; - it8->dnum *= sign; -diff --git a/third_party/lcms2-2.6/src/cmstypes.c b/third_party/lcms2-2.6/src/cmstypes.c -index 0256e247b..75f1fae32 100644 ---- a/third_party/lcms2-2.6/src/cmstypes.c -+++ b/third_party/lcms2-2.6/src/cmstypes.c -@@ -4199,9 +4199,13 @@ void *Type_MPEmatrix_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io - if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL; - - -+ // Input and output chans may be ANY (up to 0xffff), -+ // but we choose to limit to 16 channels for now -+ if (InputChans >= cmsMAXCHANNELS) return NULL; -+ if (OutputChans >= cmsMAXCHANNELS) return NULL; -+ - nElems = InputChans * OutputChans; - -- // Input and output chans may be ANY (up to 0xffff) - Matrix = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, nElems, sizeof(cmsFloat64Number)); - if (Matrix == NULL) return NULL; - diff --git a/third_party/lcms2-2.6/0018-verify-size-before-reading.patch b/third_party/lcms2-2.6/0018-verify-size-before-reading.patch deleted file mode 100644 index fa666e7f0c..0000000000 --- a/third_party/lcms2-2.6/0018-verify-size-before-reading.patch +++ /dev/null @@ -1,17 +0,0 @@ -diff --git a/third_party/lcms2-2.6/src/cmstypes.c b/third_party/lcms2-2.6/src/cmstypes.c -index 75f1fae32..4d96a1ed6 100644 ---- a/third_party/lcms2-2.6/src/cmstypes.c -+++ b/third_party/lcms2-2.6/src/cmstypes.c -@@ -173,6 +173,12 @@ cmsBool ReadPositionTable(struct _cms_typehandler_struct* self, - { - cmsUInt32Number i; - cmsUInt32Number *ElementOffsets = NULL, *ElementSizes = NULL; -+ cmsUInt32Number currentPosition; -+ -+ currentPosition = io->Tell(io); -+ // Verify there is enough space left to read two cmsUInt32Number items for Count items. -+ if (((io->ReportedSize - currentPosition) / (2 * sizeof(cmsUInt32Number))) < Count) -+ return FALSE; - - // Let's take the offsets to each element - ElementOffsets = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number)); diff --git a/third_party/lcms2-2.6/0019-upstream-direct-leak-Type_MPE_Read.patch b/third_party/lcms2-2.6/0019-upstream-direct-leak-Type_MPE_Read.patch deleted file mode 100644 index 7a2f2788b0..0000000000 --- a/third_party/lcms2-2.6/0019-upstream-direct-leak-Type_MPE_Read.patch +++ /dev/null @@ -1,31 +0,0 @@ -diff --git a/third_party/lcms2-2.6/src/cmstypes.c b/third_party/lcms2-2.6/src/cmstypes.c -index 75f1fae32..f92a92822 100644 ---- a/third_party/lcms2-2.6/src/cmstypes.c -+++ b/third_party/lcms2-2.6/src/cmstypes.c -@@ -4460,18 +4460,19 @@ void *Type_MPE_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsU - NewLUT = cmsPipelineAlloc(self ->ContextID, InputChans, OutputChans); - if (NewLUT == NULL) return NULL; - -- if (!_cmsReadUInt32Number(io, &ElementCount)) return NULL; -- -- if (!ReadPositionTable(self, io, ElementCount, BaseOffset, NewLUT, ReadMPEElem)) { -- if (NewLUT != NULL) cmsPipelineFree(NewLUT); -- *nItems = 0; -- return NULL; -- } -+ if (!_cmsReadUInt32Number(io, &ElementCount)) goto Error; -+ if (!ReadPositionTable(self, io, ElementCount, BaseOffset, NewLUT, ReadMPEElem)) goto Error; - - // Success - *nItems = 1; - return NewLUT; - -+ // Error -+Error: -+ if (NewLUT != NULL) cmsPipelineFree(NewLUT); -+ *nItems = 0; -+ return NULL; -+ - cmsUNUSED_PARAMETER(SizeOfTag); - } - diff --git a/third_party/lcms2-2.6/README.pdfium b/third_party/lcms2-2.6/README.pdfium deleted file mode 100644 index 94dc67a7e3..0000000000 --- a/third_party/lcms2-2.6/README.pdfium +++ /dev/null @@ -1,35 +0,0 @@ -Name: Little CMS -URL: http://www.littlecms.com/ -Version: 2.6 -Security Critical: yes -License: MIT License - -Description: -Color Management Engine. - -Local Modifications: - -0000-tag-type-confusion.patch: Fix a type confusion. -0001-from16-to-8-overflow.patch: Prevent a UBSan warning. -0002-infinite-loop-GrowNamedColorList.patch: Fix infinite loop when calling GrowNamedColorList. -0003-uninit.patch: Fix use uninitialized value and stack buffer overflow read. -0004-memory-leak-Type_Curve_Read.patch: Fix memory leak in Type_Curve_Read. -0005-memory-leak-AllocEmptyTransform.patch: Fix memory leak in AllocEmptyTransform. -0006-memory-leak-Type_NamedColor_Read.patch: Fix memory leak in Type_NamedColor_Read. -0007-memory-leak-OptimizeByResampling.patch: Fix memory leak in OptimizeByResampling. -0008-memory-leak-Type_MPEmatrix_Read.patch: Fix memory leak in MPEmatrix_Read. -0009-cmsStageAllocMatrix-param-swap.patch: Fix rows/cols swap in cmsStageAllocMatrix. -0010-reject-nan.patch: Reject NaN when reading float numbers. -0011-memory-leak-ReadSegmentedCurve.patch: Fix memory leak in ReadSegmentedCurve. -0012-backport-c0a98d86.patch: Fix several issues. Backport from upstream - https://github.com/mm2/Little-CMS/commit/c0a98d86 -0013-utf8.patch: Encode source files as utf-8. -0014-avoid-fixed-inf.patch: Avoid fixed number LUT optimization on inf values. -0015-sanitize-float-read.patch: Sanitize floating point read. Partially backport - from upstream https://github.com/mm2/Little-CMS/commit/4011a6e3 -0016-check-LUT-and-MPE.patch: check LUT consistency and sanitize MPE profiles. -0017-upstream-integer-overflow-MPEmatrix_Read.patch: fix some integer overflows. -0018-verify-size-before-reading.patch: fix OOM issue when there won't be enough - data to read anyway. -0019-upstream-direct-leak-Type_MPE_Read.patch: fix leak in cmstypes.c. -TODO(ochang): List other patches. diff --git a/third_party/lcms2-2.6/include/lcms2.h b/third_party/lcms2-2.6/include/lcms2.h deleted file mode 100644 index 8595f70203..0000000000 --- a/third_party/lcms2-2.6/include/lcms2.h +++ /dev/null @@ -1,1882 +0,0 @@ -//--------------------------------------------------------------------------------- -// -// 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. -// -//--------------------------------------------------------------------------------- -// -// Version 2.6 -// - -#ifndef _lcms2_H - -// ********** Configuration toggles **************************************** - -// Uncomment this one if you are using big endian machines -// #define CMS_USE_BIG_ENDIAN 1 - -// Uncomment this one if your compiler/machine does NOT support the -// "long long" type. -// #define CMS_DONT_USE_INT64 1 - -// Uncomment this if your compiler doesn't work with fast floor function -// #define CMS_DONT_USE_FAST_FLOOR 1 - -// Uncomment this line if you want lcms to use the black point tag in profile, -// if commented, lcms will compute the black point by its own. -// It is safer to leave it commented out -// #define CMS_USE_PROFILE_BLACK_POINT_TAG 1 - -// Uncomment this line if you are compiling as C++ and want a C++ API -// #define CMS_USE_CPP_API - -// Uncomment this line if you need strict CGATS syntax. Makes CGATS files to -// require "KEYWORD" on undefined identifiers, keep it comented out unless needed -// #define CMS_STRICT_CGATS 1 - -// Uncomment to get rid of the tables for "half" float support -// #define CMS_NO_HALF_SUPPORT 1 - -// Uncomment to get rid of pthreads/windows dependency -// #define CMS_NO_PTHREADS 1 - -// ********** End of configuration toggles ****************************** - -// Needed for streams -#include - -// Needed for portability (C99 per 7.1.2) -#include -#include -#include - -#ifndef CMS_USE_CPP_API -# ifdef __cplusplus -extern "C" { -# endif -#endif - -// Version/release -#define LCMS_VERSION 2060 - -// I will give the chance of redefining basic types for compilers that are not fully C99 compliant -#ifndef CMS_BASIC_TYPES_ALREADY_DEFINED - -// Base types -typedef unsigned char cmsUInt8Number; // That is guaranteed by the C99 spec -typedef signed char cmsInt8Number; // That is guaranteed by the C99 spec - -#if CHAR_BIT != 8 -# error "Unable to find 8 bit type, unsupported compiler" -#endif - -// IEEE float storage numbers -typedef float cmsFloat32Number; -typedef double cmsFloat64Number; - -// 16-bit base types -#if (USHRT_MAX == 65535U) - typedef unsigned short cmsUInt16Number; -#elif (UINT_MAX == 65535U) - typedef unsigned int cmsUInt16Number; -#else -# error "Unable to find 16 bits unsigned type, unsupported compiler" -#endif - -#if (SHRT_MAX == 32767) - typedef short cmsInt16Number; -#elif (INT_MAX == 32767) - typedef int cmsInt16Number; -#else -# error "Unable to find 16 bits signed type, unsupported compiler" -#endif - -// 32-bit base type -#if (UINT_MAX == 4294967295U) - typedef unsigned int cmsUInt32Number; -#elif (ULONG_MAX == 4294967295U) - typedef unsigned long cmsUInt32Number; -#else -# error "Unable to find 32 bit unsigned type, unsupported compiler" -#endif - -#if (INT_MAX == +2147483647) - typedef int cmsInt32Number; -#elif (LONG_MAX == +2147483647) - typedef long cmsInt32Number; -#else -# error "Unable to find 32 bit signed type, unsupported compiler" -#endif - -// 64-bit base types -#ifndef CMS_DONT_USE_INT64 -# if (ULONG_MAX == 18446744073709551615U) - typedef unsigned long cmsUInt64Number; -# elif (ULLONG_MAX == 18446744073709551615U) - typedef unsigned long long cmsUInt64Number; -# else -# define CMS_DONT_USE_INT64 1 -# endif -# if (LONG_MAX == +9223372036854775807) - typedef long cmsInt64Number; -# elif (LLONG_MAX == +9223372036854775807) - typedef long long cmsInt64Number; -# else -# define CMS_DONT_USE_INT64 1 -# endif -#endif -#endif - -// In the case 64 bit numbers are not supported by the compiler -#ifdef CMS_DONT_USE_INT64 - typedef cmsUInt32Number cmsUInt64Number[2]; - typedef cmsInt32Number cmsInt64Number[2]; -#endif - -// Derivative types -typedef cmsUInt32Number cmsSignature; -typedef cmsUInt16Number cmsU8Fixed8Number; -typedef cmsInt32Number cmsS15Fixed16Number; -typedef cmsUInt32Number cmsU16Fixed16Number; - -// Boolean type, which will be using the native integer -typedef int cmsBool; - -// Try to detect windows -#if defined (_WIN32) || defined(_WIN64) || defined(WIN32) || defined(_WIN32_) -# define CMS_IS_WINDOWS_ 1 -#endif - -#ifdef _MSC_VER -# define CMS_IS_WINDOWS_ 1 -#endif - -#ifdef __BORLANDC__ -# define CMS_IS_WINDOWS_ 1 -#endif - -// 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(__sgi__) || defined(__sgi) || defined(sparc) -# define CMS_USE_BIG_ENDIAN 1 -#endif - -#if defined(__s390__) || defined(__s390x__) -# define CMS_USE_BIG_ENDIAN 1 -#endif - -# ifdef TARGET_CPU_PPC -# if TARGET_CPU_PPC -# define CMS_USE_BIG_ENDIAN 1 -# endif -# endif - -#if defined(__powerpc__) || defined(__ppc__) || defined(TARGET_CPU_PPC) -# define CMS_USE_BIG_ENDIAN 1 -# 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 - -// 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__ -# define CMS_USE_BIG_ENDIAN 1 -# endif -# ifdef __LITTLE_ENDIAN__ -# undef CMS_USE_BIG_ENDIAN -# endif -#endif - -// Calling convention -- this is hardly platform and compiler dependent -#ifdef CMS_IS_WINDOWS_ -# if defined(CMS_DLL) || defined(CMS_DLL_BUILD) -# ifdef __BORLANDC__ -# define CMSEXPORT __stdcall _export -# define CMSAPI -# else -# define CMSEXPORT _stdcall -# ifdef CMS_DLL_BUILD -# define CMSAPI __declspec(dllexport) -# else -# define CMSAPI __declspec(dllimport) -# endif -# endif -# else -# define CMSEXPORT -# define CMSAPI -# endif -#else -# define CMSEXPORT -# define CMSAPI -#endif - -#ifdef HasTHREADS -# if HasTHREADS == 1 -# undef CMS_NO_PTHREADS -# else -# define CMS_NO_PTHREADS 1 -# endif -#endif - -// Some common definitions -#define cmsMAX_PATH 256 - -#ifndef FALSE -# define FALSE 0 -#endif -#ifndef TRUE -# define TRUE 1 -#endif - -// D50 XYZ normalized to Y=1.0 -#define cmsD50X 0.9642 -#define cmsD50Y 1.0 -#define cmsD50Z 0.8249 - -// V4 perceptual black -#define cmsPERCEPTUAL_BLACK_X 0.00336 -#define cmsPERCEPTUAL_BLACK_Y 0.0034731 -#define cmsPERCEPTUAL_BLACK_Z 0.00287 - -// Definitions in ICC spec -#define cmsMagicNumber 0x61637370 // 'acsp' -#define lcmsSignature 0x6c636d73 // 'lcms' - - -// Base ICC type definitions -typedef enum { - cmsSigChromaticityType = 0x6368726D, // 'chrm' - cmsSigColorantOrderType = 0x636C726F, // 'clro' - cmsSigColorantTableType = 0x636C7274, // 'clrt' - cmsSigCrdInfoType = 0x63726469, // 'crdi' - cmsSigCurveType = 0x63757276, // 'curv' - cmsSigDataType = 0x64617461, // 'data' - cmsSigDictType = 0x64696374, // 'dict' - cmsSigDateTimeType = 0x6474696D, // 'dtim' - cmsSigDeviceSettingsType = 0x64657673, // 'devs' - cmsSigLut16Type = 0x6d667432, // 'mft2' - cmsSigLut8Type = 0x6d667431, // 'mft1' - cmsSigLutAtoBType = 0x6d414220, // 'mAB ' - cmsSigLutBtoAType = 0x6d424120, // 'mBA ' - cmsSigMeasurementType = 0x6D656173, // 'meas' - cmsSigMultiLocalizedUnicodeType = 0x6D6C7563, // 'mluc' - cmsSigMultiProcessElementType = 0x6D706574, // 'mpet' - cmsSigNamedColorType = 0x6E636f6C, // 'ncol' -- DEPRECATED! - cmsSigNamedColor2Type = 0x6E636C32, // 'ncl2' - cmsSigParametricCurveType = 0x70617261, // 'para' - cmsSigProfileSequenceDescType = 0x70736571, // 'pseq' - cmsSigProfileSequenceIdType = 0x70736964, // 'psid' - cmsSigResponseCurveSet16Type = 0x72637332, // 'rcs2' - cmsSigS15Fixed16ArrayType = 0x73663332, // 'sf32' - cmsSigScreeningType = 0x7363726E, // 'scrn' - cmsSigSignatureType = 0x73696720, // 'sig ' - cmsSigTextType = 0x74657874, // 'text' - cmsSigTextDescriptionType = 0x64657363, // 'desc' - cmsSigU16Fixed16ArrayType = 0x75663332, // 'uf32' - cmsSigUcrBgType = 0x62666420, // 'bfd ' - cmsSigUInt16ArrayType = 0x75693136, // 'ui16' - cmsSigUInt32ArrayType = 0x75693332, // 'ui32' - cmsSigUInt64ArrayType = 0x75693634, // 'ui64' - cmsSigUInt8ArrayType = 0x75693038, // 'ui08' - cmsSigVcgtType = 0x76636774, // 'vcgt' - cmsSigViewingConditionsType = 0x76696577, // 'view' - cmsSigXYZType = 0x58595A20 // 'XYZ ' - - -} cmsTagTypeSignature; - -// Base ICC tag definitions -typedef enum { - cmsSigAToB0Tag = 0x41324230, // 'A2B0' - cmsSigAToB1Tag = 0x41324231, // 'A2B1' - cmsSigAToB2Tag = 0x41324232, // 'A2B2' - cmsSigBlueColorantTag = 0x6258595A, // 'bXYZ' - cmsSigBlueMatrixColumnTag = 0x6258595A, // 'bXYZ' - cmsSigBlueTRCTag = 0x62545243, // 'bTRC' - cmsSigBToA0Tag = 0x42324130, // 'B2A0' - cmsSigBToA1Tag = 0x42324131, // 'B2A1' - cmsSigBToA2Tag = 0x42324132, // 'B2A2' - cmsSigCalibrationDateTimeTag = 0x63616C74, // 'calt' - cmsSigCharTargetTag = 0x74617267, // 'targ' - cmsSigChromaticAdaptationTag = 0x63686164, // 'chad' - cmsSigChromaticityTag = 0x6368726D, // 'chrm' - cmsSigColorantOrderTag = 0x636C726F, // 'clro' - cmsSigColorantTableTag = 0x636C7274, // 'clrt' - cmsSigColorantTableOutTag = 0x636C6F74, // 'clot' - cmsSigColorimetricIntentImageStateTag = 0x63696973, // 'ciis' - cmsSigCopyrightTag = 0x63707274, // 'cprt' - cmsSigCrdInfoTag = 0x63726469, // 'crdi' - cmsSigDataTag = 0x64617461, // 'data' - cmsSigDateTimeTag = 0x6474696D, // 'dtim' - cmsSigDeviceMfgDescTag = 0x646D6E64, // 'dmnd' - cmsSigDeviceModelDescTag = 0x646D6464, // 'dmdd' - cmsSigDeviceSettingsTag = 0x64657673, // 'devs' - cmsSigDToB0Tag = 0x44324230, // 'D2B0' - cmsSigDToB1Tag = 0x44324231, // 'D2B1' - cmsSigDToB2Tag = 0x44324232, // 'D2B2' - cmsSigDToB3Tag = 0x44324233, // 'D2B3' - cmsSigBToD0Tag = 0x42324430, // 'B2D0' - cmsSigBToD1Tag = 0x42324431, // 'B2D1' - cmsSigBToD2Tag = 0x42324432, // 'B2D2' - cmsSigBToD3Tag = 0x42324433, // 'B2D3' - cmsSigGamutTag = 0x67616D74, // 'gamt' - cmsSigGrayTRCTag = 0x6b545243, // 'kTRC' - cmsSigGreenColorantTag = 0x6758595A, // 'gXYZ' - cmsSigGreenMatrixColumnTag = 0x6758595A, // 'gXYZ' - cmsSigGreenTRCTag = 0x67545243, // 'gTRC' - cmsSigLuminanceTag = 0x6C756d69, // 'lumi' - cmsSigMeasurementTag = 0x6D656173, // 'meas' - cmsSigMediaBlackPointTag = 0x626B7074, // 'bkpt' - cmsSigMediaWhitePointTag = 0x77747074, // 'wtpt' - cmsSigNamedColorTag = 0x6E636f6C, // 'ncol' // Deprecated by the ICC - cmsSigNamedColor2Tag = 0x6E636C32, // 'ncl2' - cmsSigOutputResponseTag = 0x72657370, // 'resp' - cmsSigPerceptualRenderingIntentGamutTag = 0x72696730, // 'rig0' - cmsSigPreview0Tag = 0x70726530, // 'pre0' - cmsSigPreview1Tag = 0x70726531, // 'pre1' - cmsSigPreview2Tag = 0x70726532, // 'pre2' - cmsSigProfileDescriptionTag = 0x64657363, // 'desc' - cmsSigProfileDescriptionMLTag = 0x6473636d, // 'dscm' - cmsSigProfileSequenceDescTag = 0x70736571, // 'pseq' - cmsSigProfileSequenceIdTag = 0x70736964, // 'psid' - cmsSigPs2CRD0Tag = 0x70736430, // 'psd0' - cmsSigPs2CRD1Tag = 0x70736431, // 'psd1' - cmsSigPs2CRD2Tag = 0x70736432, // 'psd2' - cmsSigPs2CRD3Tag = 0x70736433, // 'psd3' - cmsSigPs2CSATag = 0x70733273, // 'ps2s' - cmsSigPs2RenderingIntentTag = 0x70733269, // 'ps2i' - cmsSigRedColorantTag = 0x7258595A, // 'rXYZ' - cmsSigRedMatrixColumnTag = 0x7258595A, // 'rXYZ' - cmsSigRedTRCTag = 0x72545243, // 'rTRC' - cmsSigSaturationRenderingIntentGamutTag = 0x72696732, // 'rig2' - cmsSigScreeningDescTag = 0x73637264, // 'scrd' - cmsSigScreeningTag = 0x7363726E, // 'scrn' - cmsSigTechnologyTag = 0x74656368, // 'tech' - cmsSigUcrBgTag = 0x62666420, // 'bfd ' - cmsSigViewingCondDescTag = 0x76756564, // 'vued' - cmsSigViewingConditionsTag = 0x76696577, // 'view' - cmsSigVcgtTag = 0x76636774, // 'vcgt' - cmsSigMetaTag = 0x6D657461 // 'meta' - -} cmsTagSignature; - - -// ICC Technology tag -typedef enum { - cmsSigDigitalCamera = 0x6463616D, // 'dcam' - cmsSigFilmScanner = 0x6673636E, // 'fscn' - cmsSigReflectiveScanner = 0x7273636E, // 'rscn' - cmsSigInkJetPrinter = 0x696A6574, // 'ijet' - cmsSigThermalWaxPrinter = 0x74776178, // 'twax' - cmsSigElectrophotographicPrinter = 0x6570686F, // 'epho' - cmsSigElectrostaticPrinter = 0x65737461, // 'esta' - cmsSigDyeSublimationPrinter = 0x64737562, // 'dsub' - cmsSigPhotographicPaperPrinter = 0x7270686F, // 'rpho' - cmsSigFilmWriter = 0x6670726E, // 'fprn' - cmsSigVideoMonitor = 0x7669646D, // 'vidm' - cmsSigVideoCamera = 0x76696463, // 'vidc' - cmsSigProjectionTelevision = 0x706A7476, // 'pjtv' - cmsSigCRTDisplay = 0x43525420, // 'CRT ' - cmsSigPMDisplay = 0x504D4420, // 'PMD ' - cmsSigAMDisplay = 0x414D4420, // 'AMD ' - cmsSigPhotoCD = 0x4B504344, // 'KPCD' - cmsSigPhotoImageSetter = 0x696D6773, // 'imgs' - cmsSigGravure = 0x67726176, // 'grav' - cmsSigOffsetLithography = 0x6F666673, // 'offs' - cmsSigSilkscreen = 0x73696C6B, // 'silk' - cmsSigFlexography = 0x666C6578, // 'flex' - cmsSigMotionPictureFilmScanner = 0x6D706673, // 'mpfs' - cmsSigMotionPictureFilmRecorder = 0x6D706672, // 'mpfr' - cmsSigDigitalMotionPictureCamera = 0x646D7063, // 'dmpc' - cmsSigDigitalCinemaProjector = 0x64636A70 // 'dcpj' - -} cmsTechnologySignature; - - -// ICC Color spaces -typedef enum { - cmsSigXYZData = 0x58595A20, // 'XYZ ' - cmsSigLabData = 0x4C616220, // 'Lab ' - cmsSigLuvData = 0x4C757620, // 'Luv ' - cmsSigYCbCrData = 0x59436272, // 'YCbr' - cmsSigYxyData = 0x59787920, // 'Yxy ' - cmsSigRgbData = 0x52474220, // 'RGB ' - cmsSigGrayData = 0x47524159, // 'GRAY' - cmsSigHsvData = 0x48535620, // 'HSV ' - cmsSigHlsData = 0x484C5320, // 'HLS ' - cmsSigCmykData = 0x434D594B, // 'CMYK' - cmsSigCmyData = 0x434D5920, // 'CMY ' - cmsSigMCH1Data = 0x4D434831, // 'MCH1' - cmsSigMCH2Data = 0x4D434832, // 'MCH2' - cmsSigMCH3Data = 0x4D434833, // 'MCH3' - cmsSigMCH4Data = 0x4D434834, // 'MCH4' - cmsSigMCH5Data = 0x4D434835, // 'MCH5' - cmsSigMCH6Data = 0x4D434836, // 'MCH6' - cmsSigMCH7Data = 0x4D434837, // 'MCH7' - cmsSigMCH8Data = 0x4D434838, // 'MCH8' - cmsSigMCH9Data = 0x4D434839, // 'MCH9' - cmsSigMCHAData = 0x4D434841, // 'MCHA' - cmsSigMCHBData = 0x4D434842, // 'MCHB' - cmsSigMCHCData = 0x4D434843, // 'MCHC' - cmsSigMCHDData = 0x4D434844, // 'MCHD' - cmsSigMCHEData = 0x4D434845, // 'MCHE' - cmsSigMCHFData = 0x4D434846, // 'MCHF' - cmsSigNamedData = 0x6e6d636c, // 'nmcl' - cmsSig1colorData = 0x31434C52, // '1CLR' - cmsSig2colorData = 0x32434C52, // '2CLR' - cmsSig3colorData = 0x33434C52, // '3CLR' - cmsSig4colorData = 0x34434C52, // '4CLR' - cmsSig5colorData = 0x35434C52, // '5CLR' - cmsSig6colorData = 0x36434C52, // '6CLR' - cmsSig7colorData = 0x37434C52, // '7CLR' - cmsSig8colorData = 0x38434C52, // '8CLR' - cmsSig9colorData = 0x39434C52, // '9CLR' - cmsSig10colorData = 0x41434C52, // 'ACLR' - cmsSig11colorData = 0x42434C52, // 'BCLR' - cmsSig12colorData = 0x43434C52, // 'CCLR' - cmsSig13colorData = 0x44434C52, // 'DCLR' - cmsSig14colorData = 0x45434C52, // 'ECLR' - cmsSig15colorData = 0x46434C52, // 'FCLR' - cmsSigLuvKData = 0x4C75764B // 'LuvK' - -} cmsColorSpaceSignature; - -// ICC Profile Class -typedef enum { - cmsSigInputClass = 0x73636E72, // 'scnr' - cmsSigDisplayClass = 0x6D6E7472, // 'mntr' - cmsSigOutputClass = 0x70727472, // 'prtr' - cmsSigLinkClass = 0x6C696E6B, // 'link' - cmsSigAbstractClass = 0x61627374, // 'abst' - cmsSigColorSpaceClass = 0x73706163, // 'spac' - cmsSigNamedColorClass = 0x6e6d636c // 'nmcl' - -} cmsProfileClassSignature; - -// ICC Platforms -typedef enum { - cmsSigMacintosh = 0x4150504C, // 'APPL' - cmsSigMicrosoft = 0x4D534654, // 'MSFT' - cmsSigSolaris = 0x53554E57, // 'SUNW' - cmsSigSGI = 0x53474920, // 'SGI ' - cmsSigTaligent = 0x54474E54, // 'TGNT' - cmsSigUnices = 0x2A6E6978 // '*nix' // From argyll -- Not official - -} cmsPlatformSignature; - -// Reference gamut -#define cmsSigPerceptualReferenceMediumGamut 0x70726d67 //'prmg' - -// For cmsSigColorimetricIntentImageStateTag -#define cmsSigSceneColorimetryEstimates 0x73636F65 //'scoe' -#define cmsSigSceneAppearanceEstimates 0x73617065 //'sape' -#define cmsSigFocalPlaneColorimetryEstimates 0x66706365 //'fpce' -#define cmsSigReflectionHardcopyOriginalColorimetry 0x72686F63 //'rhoc' -#define cmsSigReflectionPrintOutputColorimetry 0x72706F63 //'rpoc' - -// Multi process elements types -typedef enum { - cmsSigCurveSetElemType = 0x63767374, //'cvst' - cmsSigMatrixElemType = 0x6D617466, //'matf' - cmsSigCLutElemType = 0x636C7574, //'clut' - - cmsSigBAcsElemType = 0x62414353, // 'bACS' - cmsSigEAcsElemType = 0x65414353, // 'eACS' - - // Custom from here, not in the ICC Spec - cmsSigXYZ2LabElemType = 0x6C327820, // 'l2x ' - cmsSigLab2XYZElemType = 0x78326C20, // 'x2l ' - cmsSigNamedColorElemType = 0x6E636C20, // 'ncl ' - cmsSigLabV2toV4 = 0x32203420, // '2 4 ' - cmsSigLabV4toV2 = 0x34203220, // '4 2 ' - - // Identities - cmsSigIdentityElemType = 0x69646E20, // 'idn ' - - // Float to floatPCS - cmsSigLab2FloatPCS = 0x64326C20, // 'd2l ' - cmsSigFloatPCS2Lab = 0x6C326420, // 'l2d ' - cmsSigXYZ2FloatPCS = 0x64327820, // 'd2x ' - cmsSigFloatPCS2XYZ = 0x78326420 // 'x2d ' - -} cmsStageSignature; - -// Types of CurveElements -typedef enum { - - cmsSigFormulaCurveSeg = 0x70617266, // 'parf' - cmsSigSampledCurveSeg = 0x73616D66, // 'samf' - cmsSigSegmentedCurve = 0x63757266 // 'curf' - -} cmsCurveSegSignature; - -// Used in ResponseCurveType -#define cmsSigStatusA 0x53746141 //'StaA' -#define cmsSigStatusE 0x53746145 //'StaE' -#define cmsSigStatusI 0x53746149 //'StaI' -#define cmsSigStatusT 0x53746154 //'StaT' -#define cmsSigStatusM 0x5374614D //'StaM' -#define cmsSigDN 0x444E2020 //'DN ' -#define cmsSigDNP 0x444E2050 //'DN P' -#define cmsSigDNN 0x444E4E20 //'DNN ' -#define cmsSigDNNP 0x444E4E50 //'DNNP' - -// Device attributes, currently defined values correspond to the low 4 bytes -// of the 8 byte attribute quantity -#define cmsReflective 0 -#define cmsTransparency 1 -#define cmsGlossy 0 -#define cmsMatte 2 - -// Common structures in ICC tags -typedef struct { - cmsUInt32Number len; - cmsUInt32Number flag; - cmsUInt8Number data[1]; - -} cmsICCData; - -// ICC date time -typedef struct { - cmsUInt16Number year; - cmsUInt16Number month; - cmsUInt16Number day; - cmsUInt16Number hours; - cmsUInt16Number minutes; - cmsUInt16Number seconds; - -} cmsDateTimeNumber; - -// ICC XYZ -typedef struct { - cmsS15Fixed16Number X; - cmsS15Fixed16Number Y; - cmsS15Fixed16Number Z; - -} cmsEncodedXYZNumber; - - -// Profile ID as computed by MD5 algorithm -typedef union { - cmsUInt8Number ID8[16]; - cmsUInt16Number ID16[8]; - cmsUInt32Number ID32[4]; - -} cmsProfileID; - - -// ---------------------------------------------------------------------------------------------- -// ICC profile internal base types. Strictly, shouldn't be declared in this header, but maybe -// somebody want to use this info for accessing profile header directly, so here it is. - -// Profile header -- it is 32-bit aligned, so no issues are expected on alignment -typedef struct { - cmsUInt32Number size; // Profile size in bytes - cmsSignature cmmId; // CMM for this profile - cmsUInt32Number version; // Format version number - cmsProfileClassSignature deviceClass; // Type of profile - cmsColorSpaceSignature colorSpace; // Color space of data - cmsColorSpaceSignature pcs; // PCS, XYZ or Lab only - cmsDateTimeNumber date; // Date profile was created - cmsSignature magic; // Magic Number to identify an ICC profile - cmsPlatformSignature platform; // Primary Platform - cmsUInt32Number flags; // Various bit settings - cmsSignature manufacturer; // Device manufacturer - cmsUInt32Number model; // Device model number - cmsUInt64Number attributes; // Device attributes - cmsUInt32Number renderingIntent;// Rendering intent - cmsEncodedXYZNumber illuminant; // Profile illuminant - cmsSignature creator; // Profile creator - cmsProfileID profileID; // Profile ID using MD5 - cmsInt8Number reserved[28]; // Reserved for future use - -} cmsICCHeader; - -// ICC base tag -typedef struct { - cmsTagTypeSignature sig; - cmsInt8Number reserved[4]; - -} cmsTagBase; - -// A tag entry in directory -typedef struct { - cmsTagSignature sig; // The tag signature - cmsUInt32Number offset; // Start of tag - cmsUInt32Number size; // Size in bytes - -} cmsTagEntry; - -// ---------------------------------------------------------------------------------------------- - -// Little CMS specific typedefs - -typedef void* cmsHANDLE ; // Generic handle -typedef void* cmsHPROFILE; // Opaque typedefs to hide internals -typedef void* cmsHTRANSFORM; - -#define cmsMAXCHANNELS 16 // Maximum number of channels in ICC profiles - -// Format of pixel is defined by one cmsUInt32Number, using bit fields as follows -// -// 2 1 0 -// 3 2 10987 6 5 4 3 2 1 098 7654 321 -// A O TTTTT U Y F P X S EEE CCCC BBB -// -// A: Floating point -- With this flag we can differentiate 16 bits as float and as int -// O: Optimized -- previous optimization already returns the final 8-bit value -// T: Pixeltype -// F: Flavor 0=MinIsBlack(Chocolate) 1=MinIsWhite(Vanilla) -// P: Planar? 0=Chunky, 1=Planar -// X: swap 16 bps endianess? -// S: Do swap? ie, BGR, KYMC -// E: Extra samples -// C: Channels (Samples per pixel) -// B: bytes per sample -// Y: Swap first - changes ABGR to BGRA and KCMY to CMYK - -#define FLOAT_SH(a) ((a) << 22) -#define OPTIMIZED_SH(s) ((s) << 21) -#define COLORSPACE_SH(s) ((s) << 16) -#define SWAPFIRST_SH(s) ((s) << 14) -#define FLAVOR_SH(s) ((s) << 13) -#define PLANAR_SH(p) ((p) << 12) -#define ENDIAN16_SH(e) ((e) << 11) -#define DOSWAP_SH(e) ((e) << 10) -#define EXTRA_SH(e) ((e) << 7) -#define CHANNELS_SH(c) ((c) << 3) -#define BYTES_SH(b) (b) - -// These macros unpack format specifiers into integers -#define T_FLOAT(a) (((a)>>22)&1) -#define T_OPTIMIZED(o) (((o)>>21)&1) -#define T_COLORSPACE(s) (((s)>>16)&31) -#define T_SWAPFIRST(s) (((s)>>14)&1) -#define T_FLAVOR(s) (((s)>>13)&1) -#define T_PLANAR(p) (((p)>>12)&1) -#define T_ENDIAN16(e) (((e)>>11)&1) -#define T_DOSWAP(e) (((e)>>10)&1) -#define T_EXTRA(e) (((e)>>7)&7) -#define T_CHANNELS(c) (((c)>>3)&15) -#define T_BYTES(b) ((b)&7) - - -// Pixel types -#define PT_ANY 0 // Don't check colorspace - // 1 & 2 are reserved -#define PT_GRAY 3 -#define PT_RGB 4 -#define PT_CMY 5 -#define PT_CMYK 6 -#define PT_YCbCr 7 -#define PT_YUV 8 // Lu'v' -#define PT_XYZ 9 -#define PT_Lab 10 -#define PT_YUVK 11 // Lu'v'K -#define PT_HSV 12 -#define PT_HLS 13 -#define PT_Yxy 14 - -#define PT_MCH1 15 -#define PT_MCH2 16 -#define PT_MCH3 17 -#define PT_MCH4 18 -#define PT_MCH5 19 -#define PT_MCH6 20 -#define PT_MCH7 21 -#define PT_MCH8 22 -#define PT_MCH9 23 -#define PT_MCH10 24 -#define PT_MCH11 25 -#define PT_MCH12 26 -#define PT_MCH13 27 -#define PT_MCH14 28 -#define PT_MCH15 29 - -#define PT_LabV2 30 // Identical to PT_Lab, but using the V2 old encoding - -// Some (not all!) representations - -#ifndef TYPE_RGB_8 // TYPE_RGB_8 is a very common identifier, so don't include ours - // if user has it already defined. - -#define TYPE_GRAY_8 (COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(1)) -#define TYPE_GRAY_8_REV (COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(1)|FLAVOR_SH(1)) -#define TYPE_GRAY_16 (COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(2)) -#define TYPE_GRAY_16_REV (COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(2)|FLAVOR_SH(1)) -#define TYPE_GRAY_16_SE (COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(2)|ENDIAN16_SH(1)) -#define TYPE_GRAYA_8 (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(1)) -#define TYPE_GRAYA_16 (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(2)) -#define TYPE_GRAYA_16_SE (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(2)|ENDIAN16_SH(1)) -#define TYPE_GRAYA_8_PLANAR (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(1)|PLANAR_SH(1)) -#define TYPE_GRAYA_16_PLANAR (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(2)|PLANAR_SH(1)) - -#define TYPE_RGB_8 (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(1)) -#define TYPE_RGB_8_PLANAR (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) -#define TYPE_BGR_8 (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)) -#define TYPE_BGR_8_PLANAR (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|PLANAR_SH(1)) -#define TYPE_RGB_16 (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)) -#define TYPE_RGB_16_PLANAR (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) -#define TYPE_RGB_16_SE (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) -#define TYPE_BGR_16 (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)) -#define TYPE_BGR_16_PLANAR (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|PLANAR_SH(1)) -#define TYPE_BGR_16_SE (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) - -#define TYPE_RGBA_8 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)) -#define TYPE_RGBA_8_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) -#define TYPE_RGBA_16 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)) -#define TYPE_RGBA_16_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) -#define TYPE_RGBA_16_SE (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) - -#define TYPE_ARGB_8 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|SWAPFIRST_SH(1)) -#define TYPE_ARGB_8_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|SWAPFIRST_SH(1)|PLANAR_SH(1)) -#define TYPE_ARGB_16 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|SWAPFIRST_SH(1)) - -#define TYPE_ABGR_8 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)) -#define TYPE_ABGR_8_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|PLANAR_SH(1)) -#define TYPE_ABGR_16 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)) -#define TYPE_ABGR_16_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|PLANAR_SH(1)) -#define TYPE_ABGR_16_SE (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) - -#define TYPE_BGRA_8 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1)) -#define TYPE_BGRA_8_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1)|PLANAR_SH(1)) -#define TYPE_BGRA_16 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|SWAPFIRST_SH(1)) -#define TYPE_BGRA_16_SE (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1)) - -#define TYPE_CMY_8 (COLORSPACE_SH(PT_CMY)|CHANNELS_SH(3)|BYTES_SH(1)) -#define TYPE_CMY_8_PLANAR (COLORSPACE_SH(PT_CMY)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) -#define TYPE_CMY_16 (COLORSPACE_SH(PT_CMY)|CHANNELS_SH(3)|BYTES_SH(2)) -#define TYPE_CMY_16_PLANAR (COLORSPACE_SH(PT_CMY)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) -#define TYPE_CMY_16_SE (COLORSPACE_SH(PT_CMY)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) - -#define TYPE_CMYK_8 (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(1)) -#define TYPE_CMYKA_8 (COLORSPACE_SH(PT_CMYK)|EXTRA_SH(1)|CHANNELS_SH(4)|BYTES_SH(1)) -#define TYPE_CMYK_8_REV (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(1)|FLAVOR_SH(1)) -#define TYPE_YUVK_8 TYPE_CMYK_8_REV -#define TYPE_CMYK_8_PLANAR (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(1)|PLANAR_SH(1)) -#define TYPE_CMYK_16 (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)) -#define TYPE_CMYK_16_REV (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|FLAVOR_SH(1)) -#define TYPE_YUVK_16 TYPE_CMYK_16_REV -#define TYPE_CMYK_16_PLANAR (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|PLANAR_SH(1)) -#define TYPE_CMYK_16_SE (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|ENDIAN16_SH(1)) - -#define TYPE_KYMC_8 (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1)) -#define TYPE_KYMC_16 (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|DOSWAP_SH(1)) -#define TYPE_KYMC_16_SE (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) - -#define TYPE_KCMY_8 (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(1)|SWAPFIRST_SH(1)) -#define TYPE_KCMY_8_REV (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(1)|FLAVOR_SH(1)|SWAPFIRST_SH(1)) -#define TYPE_KCMY_16 (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|SWAPFIRST_SH(1)) -#define TYPE_KCMY_16_REV (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|FLAVOR_SH(1)|SWAPFIRST_SH(1)) -#define TYPE_KCMY_16_SE (COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)|ENDIAN16_SH(1)|SWAPFIRST_SH(1)) - -#define TYPE_CMYK5_8 (COLORSPACE_SH(PT_MCH5)|CHANNELS_SH(5)|BYTES_SH(1)) -#define TYPE_CMYK5_16 (COLORSPACE_SH(PT_MCH5)|CHANNELS_SH(5)|BYTES_SH(2)) -#define TYPE_CMYK5_16_SE (COLORSPACE_SH(PT_MCH5)|CHANNELS_SH(5)|BYTES_SH(2)|ENDIAN16_SH(1)) -#define TYPE_KYMC5_8 (COLORSPACE_SH(PT_MCH5)|CHANNELS_SH(5)|BYTES_SH(1)|DOSWAP_SH(1)) -#define TYPE_KYMC5_16 (COLORSPACE_SH(PT_MCH5)|CHANNELS_SH(5)|BYTES_SH(2)|DOSWAP_SH(1)) -#define TYPE_KYMC5_16_SE (COLORSPACE_SH(PT_MCH5)|CHANNELS_SH(5)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) -#define TYPE_CMYK6_8 (COLORSPACE_SH(PT_MCH6)|CHANNELS_SH(6)|BYTES_SH(1)) -#define TYPE_CMYK6_8_PLANAR (COLORSPACE_SH(PT_MCH6)|CHANNELS_SH(6)|BYTES_SH(1)|PLANAR_SH(1)) -#define TYPE_CMYK6_16 (COLORSPACE_SH(PT_MCH6)|CHANNELS_SH(6)|BYTES_SH(2)) -#define TYPE_CMYK6_16_PLANAR (COLORSPACE_SH(PT_MCH6)|CHANNELS_SH(6)|BYTES_SH(2)|PLANAR_SH(1)) -#define TYPE_CMYK6_16_SE (COLORSPACE_SH(PT_MCH6)|CHANNELS_SH(6)|BYTES_SH(2)|ENDIAN16_SH(1)) -#define TYPE_CMYK7_8 (COLORSPACE_SH(PT_MCH7)|CHANNELS_SH(7)|BYTES_SH(1)) -#define TYPE_CMYK7_16 (COLORSPACE_SH(PT_MCH7)|CHANNELS_SH(7)|BYTES_SH(2)) -#define TYPE_CMYK7_16_SE (COLORSPACE_SH(PT_MCH7)|CHANNELS_SH(7)|BYTES_SH(2)|ENDIAN16_SH(1)) -#define TYPE_KYMC7_8 (COLORSPACE_SH(PT_MCH7)|CHANNELS_SH(7)|BYTES_SH(1)|DOSWAP_SH(1)) -#define TYPE_KYMC7_16 (COLORSPACE_SH(PT_MCH7)|CHANNELS_SH(7)|BYTES_SH(2)|DOSWAP_SH(1)) -#define TYPE_KYMC7_16_SE (COLORSPACE_SH(PT_MCH7)|CHANNELS_SH(7)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) -#define TYPE_CMYK8_8 (COLORSPACE_SH(PT_MCH8)|CHANNELS_SH(8)|BYTES_SH(1)) -#define TYPE_CMYK8_16 (COLORSPACE_SH(PT_MCH8)|CHANNELS_SH(8)|BYTES_SH(2)) -#define TYPE_CMYK8_16_SE (COLORSPACE_SH(PT_MCH8)|CHANNELS_SH(8)|BYTES_SH(2)|ENDIAN16_SH(1)) -#define TYPE_KYMC8_8 (COLORSPACE_SH(PT_MCH8)|CHANNELS_SH(8)|BYTES_SH(1)|DOSWAP_SH(1)) -#define TYPE_KYMC8_16 (COLORSPACE_SH(PT_MCH8)|CHANNELS_SH(8)|BYTES_SH(2)|DOSWAP_SH(1)) -#define TYPE_KYMC8_16_SE (COLORSPACE_SH(PT_MCH8)|CHANNELS_SH(8)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) -#define TYPE_CMYK9_8 (COLORSPACE_SH(PT_MCH9)|CHANNELS_SH(9)|BYTES_SH(1)) -#define TYPE_CMYK9_16 (COLORSPACE_SH(PT_MCH9)|CHANNELS_SH(9)|BYTES_SH(2)) -#define TYPE_CMYK9_16_SE (COLORSPACE_SH(PT_MCH9)|CHANNELS_SH(9)|BYTES_SH(2)|ENDIAN16_SH(1)) -#define TYPE_KYMC9_8 (COLORSPACE_SH(PT_MCH9)|CHANNELS_SH(9)|BYTES_SH(1)|DOSWAP_SH(1)) -#define TYPE_KYMC9_16 (COLORSPACE_SH(PT_MCH9)|CHANNELS_SH(9)|BYTES_SH(2)|DOSWAP_SH(1)) -#define TYPE_KYMC9_16_SE (COLORSPACE_SH(PT_MCH9)|CHANNELS_SH(9)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) -#define TYPE_CMYK10_8 (COLORSPACE_SH(PT_MCH10)|CHANNELS_SH(10)|BYTES_SH(1)) -#define TYPE_CMYK10_16 (COLORSPACE_SH(PT_MCH10)|CHANNELS_SH(10)|BYTES_SH(2)) -#define TYPE_CMYK10_16_SE (COLORSPACE_SH(PT_MCH10)|CHANNELS_SH(10)|BYTES_SH(2)|ENDIAN16_SH(1)) -#define TYPE_KYMC10_8 (COLORSPACE_SH(PT_MCH10)|CHANNELS_SH(10)|BYTES_SH(1)|DOSWAP_SH(1)) -#define TYPE_KYMC10_16 (COLORSPACE_SH(PT_MCH10)|CHANNELS_SH(10)|BYTES_SH(2)|DOSWAP_SH(1)) -#define TYPE_KYMC10_16_SE (COLORSPACE_SH(PT_MCH10)|CHANNELS_SH(10)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) -#define TYPE_CMYK11_8 (COLORSPACE_SH(PT_MCH11)|CHANNELS_SH(11)|BYTES_SH(1)) -#define TYPE_CMYK11_16 (COLORSPACE_SH(PT_MCH11)|CHANNELS_SH(11)|BYTES_SH(2)) -#define TYPE_CMYK11_16_SE (COLORSPACE_SH(PT_MCH11)|CHANNELS_SH(11)|BYTES_SH(2)|ENDIAN16_SH(1)) -#define TYPE_KYMC11_8 (COLORSPACE_SH(PT_MCH11)|CHANNELS_SH(11)|BYTES_SH(1)|DOSWAP_SH(1)) -#define TYPE_KYMC11_16 (COLORSPACE_SH(PT_MCH11)|CHANNELS_SH(11)|BYTES_SH(2)|DOSWAP_SH(1)) -#define TYPE_KYMC11_16_SE (COLORSPACE_SH(PT_MCH11)|CHANNELS_SH(11)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) -#define TYPE_CMYK12_8 (COLORSPACE_SH(PT_MCH12)|CHANNELS_SH(12)|BYTES_SH(1)) -#define TYPE_CMYK12_16 (COLORSPACE_SH(PT_MCH12)|CHANNELS_SH(12)|BYTES_SH(2)) -#define TYPE_CMYK12_16_SE (COLORSPACE_SH(PT_MCH12)|CHANNELS_SH(12)|BYTES_SH(2)|ENDIAN16_SH(1)) -#define TYPE_KYMC12_8 (COLORSPACE_SH(PT_MCH12)|CHANNELS_SH(12)|BYTES_SH(1)|DOSWAP_SH(1)) -#define TYPE_KYMC12_16 (COLORSPACE_SH(PT_MCH12)|CHANNELS_SH(12)|BYTES_SH(2)|DOSWAP_SH(1)) -#define TYPE_KYMC12_16_SE (COLORSPACE_SH(PT_MCH12)|CHANNELS_SH(12)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) - -// Colorimetric -#define TYPE_XYZ_16 (COLORSPACE_SH(PT_XYZ)|CHANNELS_SH(3)|BYTES_SH(2)) -#define TYPE_Lab_8 (COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(1)) -#define TYPE_LabV2_8 (COLORSPACE_SH(PT_LabV2)|CHANNELS_SH(3)|BYTES_SH(1)) - -#define TYPE_ALab_8 (COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|SWAPFIRST_SH(1)) -#define TYPE_ALabV2_8 (COLORSPACE_SH(PT_LabV2)|CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|SWAPFIRST_SH(1)) -#define TYPE_Lab_16 (COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(2)) -#define TYPE_LabV2_16 (COLORSPACE_SH(PT_LabV2)|CHANNELS_SH(3)|BYTES_SH(2)) -#define TYPE_Yxy_16 (COLORSPACE_SH(PT_Yxy)|CHANNELS_SH(3)|BYTES_SH(2)) - -// YCbCr -#define TYPE_YCbCr_8 (COLORSPACE_SH(PT_YCbCr)|CHANNELS_SH(3)|BYTES_SH(1)) -#define TYPE_YCbCr_8_PLANAR (COLORSPACE_SH(PT_YCbCr)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) -#define TYPE_YCbCr_16 (COLORSPACE_SH(PT_YCbCr)|CHANNELS_SH(3)|BYTES_SH(2)) -#define TYPE_YCbCr_16_PLANAR (COLORSPACE_SH(PT_YCbCr)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) -#define TYPE_YCbCr_16_SE (COLORSPACE_SH(PT_YCbCr)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) - -// YUV -#define TYPE_YUV_8 (COLORSPACE_SH(PT_YUV)|CHANNELS_SH(3)|BYTES_SH(1)) -#define TYPE_YUV_8_PLANAR (COLORSPACE_SH(PT_YUV)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) -#define TYPE_YUV_16 (COLORSPACE_SH(PT_YUV)|CHANNELS_SH(3)|BYTES_SH(2)) -#define TYPE_YUV_16_PLANAR (COLORSPACE_SH(PT_YUV)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) -#define TYPE_YUV_16_SE (COLORSPACE_SH(PT_YUV)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) - -// HLS -#define TYPE_HLS_8 (COLORSPACE_SH(PT_HLS)|CHANNELS_SH(3)|BYTES_SH(1)) -#define TYPE_HLS_8_PLANAR (COLORSPACE_SH(PT_HLS)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) -#define TYPE_HLS_16 (COLORSPACE_SH(PT_HLS)|CHANNELS_SH(3)|BYTES_SH(2)) -#define TYPE_HLS_16_PLANAR (COLORSPACE_SH(PT_HLS)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) -#define TYPE_HLS_16_SE (COLORSPACE_SH(PT_HLS)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) - -// HSV -#define TYPE_HSV_8 (COLORSPACE_SH(PT_HSV)|CHANNELS_SH(3)|BYTES_SH(1)) -#define TYPE_HSV_8_PLANAR (COLORSPACE_SH(PT_HSV)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) -#define TYPE_HSV_16 (COLORSPACE_SH(PT_HSV)|CHANNELS_SH(3)|BYTES_SH(2)) -#define TYPE_HSV_16_PLANAR (COLORSPACE_SH(PT_HSV)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) -#define TYPE_HSV_16_SE (COLORSPACE_SH(PT_HSV)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) - -// Named color index. Only 16 bits allowed (don't check colorspace) -#define TYPE_NAMED_COLOR_INDEX (CHANNELS_SH(1)|BYTES_SH(2)) - -// Float formatters. -#define TYPE_XYZ_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_XYZ)|CHANNELS_SH(3)|BYTES_SH(4)) -#define TYPE_Lab_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(4)) -#define TYPE_LabA_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_Lab)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)) -#define TYPE_GRAY_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(4)) -#define TYPE_RGB_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(4)) - -#define TYPE_RGBA_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)) -#define TYPE_ARGB_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|SWAPFIRST_SH(1)) -#define TYPE_BGR_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(4)|DOSWAP_SH(1)) -#define TYPE_BGRA_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|DOSWAP_SH(1)|SWAPFIRST_SH(1)) -#define TYPE_ABGR_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(4)|DOSWAP_SH(1)) - -#define TYPE_CMYK_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(4)) - -// Floating point formatters. -// NOTE THAT 'BYTES' FIELD IS SET TO ZERO ON DLB because 8 bytes overflows the bitfield -#define TYPE_XYZ_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_XYZ)|CHANNELS_SH(3)|BYTES_SH(0)) -#define TYPE_Lab_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(0)) -#define TYPE_GRAY_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(0)) -#define TYPE_RGB_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(0)) -#define TYPE_BGR_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(0)|DOSWAP_SH(1)) -#define TYPE_CMYK_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(0)) - -// IEEE 754-2008 "half" -#define TYPE_GRAY_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(2)) -#define TYPE_RGB_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)) -#define TYPE_RGBA_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)) -#define TYPE_CMYK_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(2)) - -#define TYPE_RGBA_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)) -#define TYPE_ARGB_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|SWAPFIRST_SH(1)) -#define TYPE_BGR_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)) -#define TYPE_BGRA_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|SWAPFIRST_SH(1)) -#define TYPE_ABGR_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)) - -#endif - -// Colorspaces -typedef struct { - cmsFloat64Number X; - cmsFloat64Number Y; - cmsFloat64Number Z; - - } cmsCIEXYZ; - -typedef struct { - cmsFloat64Number x; - cmsFloat64Number y; - cmsFloat64Number Y; - - } cmsCIExyY; - -typedef struct { - cmsFloat64Number L; - cmsFloat64Number a; - cmsFloat64Number b; - - } cmsCIELab; - -typedef struct { - cmsFloat64Number L; - cmsFloat64Number C; - cmsFloat64Number h; - - } cmsCIELCh; - -typedef struct { - cmsFloat64Number J; - cmsFloat64Number C; - cmsFloat64Number h; - - } cmsJCh; - -typedef struct { - cmsCIEXYZ Red; - cmsCIEXYZ Green; - cmsCIEXYZ Blue; - - } cmsCIEXYZTRIPLE; - -typedef struct { - cmsCIExyY Red; - cmsCIExyY Green; - cmsCIExyY Blue; - - } cmsCIExyYTRIPLE; - -// Illuminant types for structs below -#define cmsILLUMINANT_TYPE_UNKNOWN 0x0000000 -#define cmsILLUMINANT_TYPE_D50 0x0000001 -#define cmsILLUMINANT_TYPE_D65 0x0000002 -#define cmsILLUMINANT_TYPE_D93 0x0000003 -#define cmsILLUMINANT_TYPE_F2 0x0000004 -#define cmsILLUMINANT_TYPE_D55 0x0000005 -#define cmsILLUMINANT_TYPE_A 0x0000006 -#define cmsILLUMINANT_TYPE_E 0x0000007 -#define cmsILLUMINANT_TYPE_F8 0x0000008 - -typedef struct { - cmsUInt32Number Observer; // 0 = unknown, 1=CIE 1931, 2=CIE 1964 - cmsCIEXYZ Backing; // Value of backing - cmsUInt32Number Geometry; // 0=unknown, 1=45/0, 0/45 2=0d, d/0 - cmsFloat64Number Flare; // 0..1.0 - cmsUInt32Number IlluminantType; - - } cmsICCMeasurementConditions; - -typedef struct { - cmsCIEXYZ IlluminantXYZ; // Not the same struct as CAM02, - cmsCIEXYZ SurroundXYZ; // This is for storing the tag - cmsUInt32Number IlluminantType; // viewing condition - - } cmsICCViewingConditions; - -// Support of non-standard functions -------------------------------------------------------------------------------------- - -CMSAPI int CMSEXPORT cmsstrcasecmp(const char* s1, const char* s2); -CMSAPI long int CMSEXPORT cmsfilelength(FILE* f); - - -// 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. - -typedef struct _cmsContext_struct* cmsContext; - -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 ---------------------------------------------------------------------------------------------------------- - -// There is no error handling at all. When a function fails, it returns proper value. -// For example, all create functions does return NULL on failure. Other may return FALSE. -// It may be interesting, for the developer, to know why the function is failing. -// for that reason, lcms2 does offer a logging function. This function will get -// an ENGLISH string with some clues on what is going wrong. You can show this -// info to the end user if you wish, or just create some sort of log on disk. -// The logging function should NOT terminate the program, as this obviously can leave -// unfreed resources. It is the programmer's responsibility to check each function -// return code to make sure it didn't fail. - -#define cmsERROR_UNDEFINED 0 -#define cmsERROR_FILE 1 -#define cmsERROR_RANGE 2 -#define cmsERROR_INTERNAL 3 -#define cmsERROR_NULL 4 -#define cmsERROR_READ 5 -#define cmsERROR_SEEK 6 -#define cmsERROR_WRITE 7 -#define cmsERROR_UNKNOWN_EXTENSION 8 -#define cmsERROR_COLORSPACE_CHECK 9 -#define cmsERROR_ALREADY_DEFINED 10 -#define cmsERROR_BAD_SIGNATURE 11 -#define cmsERROR_CORRUPTION_DETECTED 12 -#define cmsERROR_NOT_SUITABLE 13 - -// Error logger is called with the ContextID when a message is raised. This gives the -// chance to know which thread is responsible of the warning and any environment associated -// with it. Non-multithreading applications may safely ignore this parameter. -// Note that under certain special circumstances, ContextID may be NULL. -typedef void (* cmsLogErrorHandlerFunction)(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text); - -// Allows user to set any specific logger -CMSAPI void CMSEXPORT cmsSetLogErrorHandler(cmsLogErrorHandlerFunction Fn); -CMSAPI void CMSEXPORT cmsSetLogErrorHandlerTHR(cmsContext ContextID, cmsLogErrorHandlerFunction Fn); - -// Conversions -------------------------------------------------------------------------------------------------------------- - -// Returns pointers to constant structs -CMSAPI const cmsCIEXYZ* CMSEXPORT cmsD50_XYZ(void); -CMSAPI const cmsCIExyY* CMSEXPORT cmsD50_xyY(void); - -// Colorimetric space conversions -CMSAPI void CMSEXPORT cmsXYZ2xyY(cmsCIExyY* Dest, const cmsCIEXYZ* Source); -CMSAPI void CMSEXPORT cmsxyY2XYZ(cmsCIEXYZ* Dest, const cmsCIExyY* Source); -CMSAPI void CMSEXPORT cmsXYZ2Lab(const cmsCIEXYZ* WhitePoint, cmsCIELab* Lab, const cmsCIEXYZ* xyz); -CMSAPI void CMSEXPORT cmsLab2XYZ(const cmsCIEXYZ* WhitePoint, cmsCIEXYZ* xyz, const cmsCIELab* Lab); -CMSAPI void CMSEXPORT cmsLab2LCh(cmsCIELCh*LCh, const cmsCIELab* Lab); -CMSAPI void CMSEXPORT cmsLCh2Lab(cmsCIELab* Lab, const cmsCIELCh* LCh); - -// Encoding /Decoding on PCS -CMSAPI void CMSEXPORT cmsLabEncoded2Float(cmsCIELab* Lab, const cmsUInt16Number wLab[3]); -CMSAPI void CMSEXPORT cmsLabEncoded2FloatV2(cmsCIELab* Lab, const cmsUInt16Number wLab[3]); -CMSAPI void CMSEXPORT cmsFloat2LabEncoded(cmsUInt16Number wLab[3], const cmsCIELab* Lab); -CMSAPI void CMSEXPORT cmsFloat2LabEncodedV2(cmsUInt16Number wLab[3], const cmsCIELab* Lab); -CMSAPI void CMSEXPORT cmsXYZEncoded2Float(cmsCIEXYZ* fxyz, const cmsUInt16Number XYZ[3]); -CMSAPI void CMSEXPORT cmsFloat2XYZEncoded(cmsUInt16Number XYZ[3], const cmsCIEXYZ* fXYZ); - -// DeltaE metrics -CMSAPI cmsFloat64Number CMSEXPORT cmsDeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2); -CMSAPI cmsFloat64Number CMSEXPORT cmsCIE94DeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2); -CMSAPI cmsFloat64Number CMSEXPORT cmsBFDdeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2); -CMSAPI cmsFloat64Number CMSEXPORT cmsCMCdeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2, cmsFloat64Number l, cmsFloat64Number c); -CMSAPI cmsFloat64Number CMSEXPORT cmsCIE2000DeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2, cmsFloat64Number Kl, cmsFloat64Number Kc, cmsFloat64Number Kh); - -// Temperature <-> Chromaticity (Black body) -CMSAPI cmsBool CMSEXPORT cmsWhitePointFromTemp(cmsCIExyY* WhitePoint, cmsFloat64Number TempK); -CMSAPI cmsBool CMSEXPORT cmsTempFromWhitePoint(cmsFloat64Number* TempK, const cmsCIExyY* WhitePoint); - -// Chromatic adaptation -CMSAPI cmsBool CMSEXPORT cmsAdaptToIlluminant(cmsCIEXYZ* Result, const cmsCIEXYZ* SourceWhitePt, - const cmsCIEXYZ* Illuminant, - const cmsCIEXYZ* Value); - -// CIECAM02 --------------------------------------------------------------------------------------------------- - -// Viewing conditions. Please note those are CAM model viewing conditions, and not the ICC tag viewing -// conditions, which I'm naming cmsICCViewingConditions to make differences evident. Unfortunately, the tag -// cannot deal with surround La, Yb and D value so is basically useless to store CAM02 viewing conditions. - - -#define AVG_SURROUND 1 -#define DIM_SURROUND 2 -#define DARK_SURROUND 3 -#define CUTSHEET_SURROUND 4 - -#define D_CALCULATE (-1) - -typedef struct { - cmsCIEXYZ whitePoint; - cmsFloat64Number Yb; - cmsFloat64Number La; - int surround; - cmsFloat64Number D_value; - - } cmsViewingConditions; - -CMSAPI cmsHANDLE CMSEXPORT cmsCIECAM02Init(cmsContext ContextID, const cmsViewingConditions* pVC); -CMSAPI void CMSEXPORT cmsCIECAM02Done(cmsHANDLE hModel); -CMSAPI void CMSEXPORT cmsCIECAM02Forward(cmsHANDLE hModel, const cmsCIEXYZ* pIn, cmsJCh* pOut); -CMSAPI void CMSEXPORT cmsCIECAM02Reverse(cmsHANDLE hModel, const cmsJCh* pIn, cmsCIEXYZ* pOut); - - -// Tone curves ----------------------------------------------------------------------------------------- - -// This describes a curve segment. For a table of supported types, see the manual. User can increase the number of -// available types by using a proper plug-in. Parametric segments allow 10 parameters at most - -typedef struct { - cmsFloat32Number x0, x1; // Domain; for x0 < x <= x1 - cmsInt32Number Type; // Parametric type, Type == 0 means sampled segment. Negative values are reserved - cmsFloat64Number Params[10]; // Parameters if Type != 0 - cmsUInt32Number nGridPoints; // Number of grid points if Type == 0 - cmsFloat32Number* SampledPoints; // Points to an array of floats if Type == 0 - -} cmsCurveSegment; - -// The internal representation is none of your business. -typedef struct _cms_curve_struct cmsToneCurve; - -CMSAPI cmsToneCurve* CMSEXPORT cmsBuildSegmentedToneCurve(cmsContext ContextID, cmsInt32Number nSegments, const cmsCurveSegment Segments[]); -CMSAPI cmsToneCurve* CMSEXPORT cmsBuildParametricToneCurve(cmsContext ContextID, cmsInt32Number Type, const cmsFloat64Number Params[]); -CMSAPI cmsToneCurve* CMSEXPORT cmsBuildGamma(cmsContext ContextID, cmsFloat64Number Gamma); -CMSAPI cmsToneCurve* CMSEXPORT cmsBuildTabulatedToneCurve16(cmsContext ContextID, cmsInt32Number nEntries, const cmsUInt16Number values[]); -CMSAPI cmsToneCurve* CMSEXPORT cmsBuildTabulatedToneCurveFloat(cmsContext ContextID, cmsUInt32Number nEntries, const cmsFloat32Number values[]); -CMSAPI void CMSEXPORT cmsFreeToneCurve(cmsToneCurve* Curve); -CMSAPI void CMSEXPORT cmsFreeToneCurveTriple(cmsToneCurve* Curve[3]); -CMSAPI cmsToneCurve* CMSEXPORT cmsDupToneCurve(const cmsToneCurve* Src); -CMSAPI cmsToneCurve* CMSEXPORT cmsReverseToneCurve(const cmsToneCurve* InGamma); -CMSAPI cmsToneCurve* CMSEXPORT cmsReverseToneCurveEx(cmsInt32Number nResultSamples, const cmsToneCurve* InGamma); -CMSAPI cmsToneCurve* CMSEXPORT cmsJoinToneCurve(cmsContext ContextID, const cmsToneCurve* X, const cmsToneCurve* Y, cmsUInt32Number nPoints); -CMSAPI cmsBool CMSEXPORT cmsSmoothToneCurve(cmsToneCurve* Tab, cmsFloat64Number lambda); -CMSAPI cmsFloat32Number CMSEXPORT cmsEvalToneCurveFloat(const cmsToneCurve* Curve, cmsFloat32Number v); -CMSAPI cmsUInt16Number CMSEXPORT cmsEvalToneCurve16(const cmsToneCurve* Curve, cmsUInt16Number v); -CMSAPI cmsBool CMSEXPORT cmsIsToneCurveMultisegment(const cmsToneCurve* InGamma); -CMSAPI cmsBool CMSEXPORT cmsIsToneCurveLinear(const cmsToneCurve* Curve); -CMSAPI cmsBool CMSEXPORT cmsIsToneCurveMonotonic(const cmsToneCurve* t); -CMSAPI cmsBool CMSEXPORT cmsIsToneCurveDescending(const cmsToneCurve* t); -CMSAPI cmsInt32Number CMSEXPORT cmsGetToneCurveParametricType(const cmsToneCurve* t); -CMSAPI cmsFloat64Number CMSEXPORT cmsEstimateGamma(const cmsToneCurve* t, cmsFloat64Number Precision); - -// Tone curve tabular estimation -CMSAPI cmsUInt32Number CMSEXPORT cmsGetToneCurveEstimatedTableEntries(const cmsToneCurve* t); -CMSAPI const cmsUInt16Number* CMSEXPORT cmsGetToneCurveEstimatedTable(const cmsToneCurve* t); - - -// Implements pipelines of multi-processing elements ------------------------------------------------------------- - -// Nothing to see here, move along -typedef struct _cmsPipeline_struct cmsPipeline; -typedef struct _cmsStage_struct cmsStage; - -// Those are hi-level pipelines -CMSAPI cmsPipeline* CMSEXPORT cmsPipelineAlloc(cmsContext ContextID, cmsUInt32Number InputChannels, cmsUInt32Number OutputChannels); -CMSAPI void CMSEXPORT cmsPipelineFree(cmsPipeline* lut); -CMSAPI cmsPipeline* CMSEXPORT cmsPipelineDup(const cmsPipeline* Orig); - -CMSAPI cmsContext CMSEXPORT cmsGetPipelineContextID(const cmsPipeline* lut); -CMSAPI cmsUInt32Number CMSEXPORT cmsPipelineInputChannels(const cmsPipeline* lut); -CMSAPI cmsUInt32Number CMSEXPORT cmsPipelineOutputChannels(const cmsPipeline* lut); - -CMSAPI cmsUInt32Number CMSEXPORT cmsPipelineStageCount(const cmsPipeline* lut); -CMSAPI cmsStage* CMSEXPORT cmsPipelineGetPtrToFirstStage(const cmsPipeline* lut); -CMSAPI cmsStage* CMSEXPORT cmsPipelineGetPtrToLastStage(const cmsPipeline* lut); - -CMSAPI void CMSEXPORT cmsPipelineEval16(const cmsUInt16Number In[], cmsUInt16Number Out[], const cmsPipeline* lut); -CMSAPI void CMSEXPORT cmsPipelineEvalFloat(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsPipeline* lut); -CMSAPI cmsBool CMSEXPORT cmsPipelineEvalReverseFloat(cmsFloat32Number Target[], cmsFloat32Number Result[], cmsFloat32Number Hint[], const cmsPipeline* lut); -CMSAPI cmsBool CMSEXPORT cmsPipelineCat(cmsPipeline* l1, const cmsPipeline* l2); -CMSAPI cmsBool CMSEXPORT cmsPipelineSetSaveAs8bitsFlag(cmsPipeline* lut, cmsBool On); - -// Where to place/locate the stages in the pipeline chain -typedef enum { cmsAT_BEGIN, cmsAT_END } cmsStageLoc; - -CMSAPI int CMSEXPORT cmsPipelineInsertStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage* mpe); -CMSAPI void CMSEXPORT cmsPipelineUnlinkStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage** mpe); - -// This function is quite useful to analyze the structure of a Pipeline and retrieve the Stage elements -// that conform the Pipeline. It should be called with the Pipeline, the number of expected elements and -// then a list of expected types followed with a list of double pointers to Stage elements. If -// the function founds a match with current pipeline, it fills the pointers and returns TRUE -// if not, returns FALSE without touching anything. -CMSAPI cmsBool CMSEXPORT cmsPipelineCheckAndRetreiveStages(const cmsPipeline* Lut, cmsUInt32Number n, ...); - -// Matrix has double precision and CLUT has only float precision. That is because an ICC profile can encode -// matrices with far more precision that CLUTS -CMSAPI cmsStage* CMSEXPORT cmsStageAllocIdentity(cmsContext ContextID, cmsUInt32Number nChannels); -CMSAPI cmsStage* CMSEXPORT cmsStageAllocToneCurves(cmsContext ContextID, cmsUInt32Number nChannels, cmsToneCurve* const Curves[]); -CMSAPI cmsStage* CMSEXPORT cmsStageAllocMatrix(cmsContext ContextID, cmsUInt32Number Rows, cmsUInt32Number Cols, const cmsFloat64Number* Matrix, const cmsFloat64Number* Offset); - -CMSAPI cmsStage* CMSEXPORT cmsStageAllocCLut16bit(cmsContext ContextID, cmsUInt32Number nGridPoints, cmsUInt32Number inputChan, cmsUInt32Number outputChan, const cmsUInt16Number* Table); -CMSAPI cmsStage* CMSEXPORT cmsStageAllocCLutFloat(cmsContext ContextID, cmsUInt32Number nGridPoints, cmsUInt32Number inputChan, cmsUInt32Number outputChan, const cmsFloat32Number* Table); - -CMSAPI cmsStage* CMSEXPORT cmsStageAllocCLut16bitGranular(cmsContext ContextID, const cmsUInt32Number clutPoints[], cmsUInt32Number inputChan, cmsUInt32Number outputChan, const cmsUInt16Number* Table); -CMSAPI cmsStage* CMSEXPORT cmsStageAllocCLutFloatGranular(cmsContext ContextID, const cmsUInt32Number clutPoints[], cmsUInt32Number inputChan, cmsUInt32Number outputChan, const cmsFloat32Number* Table); - -CMSAPI cmsStage* CMSEXPORT cmsStageDup(cmsStage* mpe); -CMSAPI void CMSEXPORT cmsStageFree(cmsStage* mpe); -CMSAPI cmsStage* CMSEXPORT cmsStageNext(const cmsStage* mpe); - -CMSAPI cmsUInt32Number CMSEXPORT cmsStageInputChannels(const cmsStage* mpe); -CMSAPI cmsUInt32Number CMSEXPORT cmsStageOutputChannels(const cmsStage* mpe); -CMSAPI cmsStageSignature CMSEXPORT cmsStageType(const cmsStage* mpe); -CMSAPI void* CMSEXPORT cmsStageData(const cmsStage* mpe); - -// Sampling -typedef cmsInt32Number (* cmsSAMPLER16) (register const cmsUInt16Number In[], - register cmsUInt16Number Out[], - register void * Cargo); - -typedef cmsInt32Number (* cmsSAMPLERFLOAT)(register const cmsFloat32Number In[], - register cmsFloat32Number Out[], - register void * Cargo); - -// Use this flag to prevent changes being written to destination -#define SAMPLER_INSPECT 0x01000000 - -// For CLUT only -CMSAPI cmsBool CMSEXPORT cmsStageSampleCLut16bit(cmsStage* mpe, cmsSAMPLER16 Sampler, void* Cargo, cmsUInt32Number dwFlags); -CMSAPI cmsBool CMSEXPORT cmsStageSampleCLutFloat(cmsStage* mpe, cmsSAMPLERFLOAT Sampler, void* Cargo, cmsUInt32Number dwFlags); - -// Slicers -CMSAPI cmsBool CMSEXPORT cmsSliceSpace16(cmsUInt32Number nInputs, const cmsUInt32Number clutPoints[], - cmsSAMPLER16 Sampler, void * Cargo); - -CMSAPI cmsBool CMSEXPORT cmsSliceSpaceFloat(cmsUInt32Number nInputs, const cmsUInt32Number clutPoints[], - cmsSAMPLERFLOAT Sampler, void * Cargo); - -// Multilocalized Unicode management --------------------------------------------------------------------------------------- - -typedef struct _cms_MLU_struct cmsMLU; - -#define cmsNoLanguage "\0\0" -#define cmsNoCountry "\0\0" - -CMSAPI cmsMLU* CMSEXPORT cmsMLUalloc(cmsContext ContextID, cmsUInt32Number nItems); -CMSAPI void CMSEXPORT cmsMLUfree(cmsMLU* mlu); -CMSAPI cmsMLU* CMSEXPORT cmsMLUdup(const cmsMLU* mlu); - -CMSAPI cmsBool CMSEXPORT cmsMLUsetASCII(cmsMLU* mlu, - const char LanguageCode[3], const char CountryCode[3], - const char* ASCIIString); -CMSAPI cmsBool CMSEXPORT cmsMLUsetWide(cmsMLU* mlu, - const char LanguageCode[3], const char CountryCode[3], - const wchar_t* WideString); - -CMSAPI cmsUInt32Number CMSEXPORT cmsMLUgetASCII(const cmsMLU* mlu, - const char LanguageCode[3], const char CountryCode[3], - char* Buffer, cmsUInt32Number BufferSize); - -CMSAPI cmsUInt32Number CMSEXPORT cmsMLUgetWide(const cmsMLU* mlu, - const char LanguageCode[3], const char CountryCode[3], - wchar_t* Buffer, cmsUInt32Number BufferSize); - -CMSAPI cmsBool CMSEXPORT cmsMLUgetTranslation(const cmsMLU* mlu, - const char LanguageCode[3], const char CountryCode[3], - char ObtainedLanguage[3], char ObtainedCountry[3]); - -CMSAPI cmsUInt32Number CMSEXPORT cmsMLUtranslationsCount(const cmsMLU* mlu); - -CMSAPI cmsBool CMSEXPORT cmsMLUtranslationsCodes(const cmsMLU* mlu, - cmsUInt32Number idx, - char LanguageCode[3], - char CountryCode[3]); - -// Undercolorremoval & black generation ------------------------------------------------------------------------------------- - -typedef struct { - cmsToneCurve* Ucr; - cmsToneCurve* Bg; - cmsMLU* Desc; - -} cmsUcrBg; - -// Screening ---------------------------------------------------------------------------------------------------------------- - -#define cmsPRINTER_DEFAULT_SCREENS 0x0001 -#define cmsFREQUENCE_UNITS_LINES_CM 0x0000 -#define cmsFREQUENCE_UNITS_LINES_INCH 0x0002 - -#define cmsSPOT_UNKNOWN 0 -#define cmsSPOT_PRINTER_DEFAULT 1 -#define cmsSPOT_ROUND 2 -#define cmsSPOT_DIAMOND 3 -#define cmsSPOT_ELLIPSE 4 -#define cmsSPOT_LINE 5 -#define cmsSPOT_SQUARE 6 -#define cmsSPOT_CROSS 7 - -typedef struct { - cmsFloat64Number Frequency; - cmsFloat64Number ScreenAngle; - cmsUInt32Number SpotShape; - -} cmsScreeningChannel; - -typedef struct { - cmsUInt32Number Flag; - cmsUInt32Number nChannels; - cmsScreeningChannel Channels[cmsMAXCHANNELS]; - -} cmsScreening; - - -// Named color ----------------------------------------------------------------------------------------------------------------- - -typedef struct _cms_NAMEDCOLORLIST_struct cmsNAMEDCOLORLIST; - -CMSAPI cmsNAMEDCOLORLIST* CMSEXPORT cmsAllocNamedColorList(cmsContext ContextID, - cmsUInt32Number n, - cmsUInt32Number ColorantCount, - const char* Prefix, const char* Suffix); - -CMSAPI void CMSEXPORT cmsFreeNamedColorList(cmsNAMEDCOLORLIST* v); -CMSAPI cmsNAMEDCOLORLIST* CMSEXPORT cmsDupNamedColorList(const cmsNAMEDCOLORLIST* v); -CMSAPI cmsBool CMSEXPORT cmsAppendNamedColor(cmsNAMEDCOLORLIST* v, const char* Name, - cmsUInt16Number PCS[3], - cmsUInt16Number Colorant[cmsMAXCHANNELS]); - -CMSAPI cmsUInt32Number CMSEXPORT cmsNamedColorCount(const cmsNAMEDCOLORLIST* v); -CMSAPI cmsInt32Number CMSEXPORT cmsNamedColorIndex(const cmsNAMEDCOLORLIST* v, const char* Name); - -CMSAPI cmsBool CMSEXPORT cmsNamedColorInfo(const cmsNAMEDCOLORLIST* NamedColorList, cmsUInt32Number nColor, - char* Name, - char* Prefix, - char* Suffix, - cmsUInt16Number* PCS, - cmsUInt16Number* Colorant); - -// Retrieve named color list from transform -CMSAPI cmsNAMEDCOLORLIST* CMSEXPORT cmsGetNamedColorList(cmsHTRANSFORM xform); - -// Profile sequence ----------------------------------------------------------------------------------------------------- - -// Profile sequence descriptor. Some fields come from profile sequence descriptor tag, others -// come from Profile Sequence Identifier Tag -typedef struct { - - cmsSignature deviceMfg; - cmsSignature deviceModel; - cmsUInt64Number attributes; - cmsTechnologySignature technology; - cmsProfileID ProfileID; - cmsMLU* Manufacturer; - cmsMLU* Model; - cmsMLU* Description; - -} cmsPSEQDESC; - -typedef struct { - - cmsUInt32Number n; - cmsContext ContextID; - cmsPSEQDESC* seq; - -} cmsSEQ; - -CMSAPI cmsSEQ* CMSEXPORT cmsAllocProfileSequenceDescription(cmsContext ContextID, cmsUInt32Number n); -CMSAPI cmsSEQ* CMSEXPORT cmsDupProfileSequenceDescription(const cmsSEQ* pseq); -CMSAPI void CMSEXPORT cmsFreeProfileSequenceDescription(cmsSEQ* pseq); - -// Dictionaries -------------------------------------------------------------------------------------------------------- - -typedef struct _cmsDICTentry_struct { - - struct _cmsDICTentry_struct* Next; - - cmsMLU *DisplayName; - cmsMLU *DisplayValue; - wchar_t* Name; - wchar_t* Value; - -} cmsDICTentry; - -CMSAPI cmsHANDLE CMSEXPORT cmsDictAlloc(cmsContext ContextID); -CMSAPI void CMSEXPORT cmsDictFree(cmsHANDLE hDict); -CMSAPI cmsHANDLE CMSEXPORT cmsDictDup(cmsHANDLE hDict); - -CMSAPI cmsBool CMSEXPORT cmsDictAddEntry(cmsHANDLE hDict, const wchar_t* Name, const wchar_t* Value, const cmsMLU *DisplayName, const cmsMLU *DisplayValue); -CMSAPI const cmsDICTentry* CMSEXPORT cmsDictGetEntryList(cmsHANDLE hDict); -CMSAPI const cmsDICTentry* CMSEXPORT cmsDictNextEntry(const cmsDICTentry* e); - -// Access to Profile data ---------------------------------------------------------------------------------------------- -CMSAPI cmsHPROFILE CMSEXPORT cmsCreateProfilePlaceholder(cmsContext ContextID); - -CMSAPI cmsContext CMSEXPORT cmsGetProfileContextID(cmsHPROFILE hProfile); -CMSAPI cmsInt32Number CMSEXPORT cmsGetTagCount(cmsHPROFILE hProfile); -CMSAPI cmsTagSignature CMSEXPORT cmsGetTagSignature(cmsHPROFILE hProfile, cmsUInt32Number n); -CMSAPI cmsBool CMSEXPORT cmsIsTag(cmsHPROFILE hProfile, cmsTagSignature sig); - -// Read and write pre-formatted data -CMSAPI void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig); -CMSAPI cmsBool CMSEXPORT cmsWriteTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data); -CMSAPI cmsBool CMSEXPORT cmsLinkTag(cmsHPROFILE hProfile, cmsTagSignature sig, cmsTagSignature dest); -CMSAPI cmsTagSignature CMSEXPORT cmsTagLinkedTo(cmsHPROFILE hProfile, cmsTagSignature sig); - -// Read and write raw data -CMSAPI cmsInt32Number CMSEXPORT cmsReadRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, void* Buffer, cmsUInt32Number BufferSize); -CMSAPI cmsBool CMSEXPORT cmsWriteRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data, cmsUInt32Number Size); - -// Access header data -#define cmsEmbeddedProfileFalse 0x00000000 -#define cmsEmbeddedProfileTrue 0x00000001 -#define cmsUseAnywhere 0x00000000 -#define cmsUseWithEmbeddedDataOnly 0x00000002 - -CMSAPI cmsUInt32Number CMSEXPORT cmsGetHeaderFlags(cmsHPROFILE hProfile); -CMSAPI void CMSEXPORT cmsGetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number* Flags); -CMSAPI void CMSEXPORT cmsGetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID); -CMSAPI cmsBool CMSEXPORT cmsGetHeaderCreationDateTime(cmsHPROFILE hProfile, struct tm *Dest); -CMSAPI cmsUInt32Number CMSEXPORT cmsGetHeaderRenderingIntent(cmsHPROFILE hProfile); - -CMSAPI void CMSEXPORT cmsSetHeaderFlags(cmsHPROFILE hProfile, cmsUInt32Number Flags); -CMSAPI cmsUInt32Number CMSEXPORT cmsGetHeaderManufacturer(cmsHPROFILE hProfile); -CMSAPI void CMSEXPORT cmsSetHeaderManufacturer(cmsHPROFILE hProfile, cmsUInt32Number manufacturer); -CMSAPI cmsUInt32Number CMSEXPORT cmsGetHeaderCreator(cmsHPROFILE hProfile); -CMSAPI cmsUInt32Number CMSEXPORT cmsGetHeaderModel(cmsHPROFILE hProfile); -CMSAPI void CMSEXPORT cmsSetHeaderModel(cmsHPROFILE hProfile, cmsUInt32Number model); -CMSAPI void CMSEXPORT cmsSetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number Flags); -CMSAPI void CMSEXPORT cmsSetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID); -CMSAPI void CMSEXPORT cmsSetHeaderRenderingIntent(cmsHPROFILE hProfile, cmsUInt32Number RenderingIntent); - -CMSAPI cmsColorSpaceSignature - CMSEXPORT cmsGetPCS(cmsHPROFILE hProfile); -CMSAPI void CMSEXPORT cmsSetPCS(cmsHPROFILE hProfile, cmsColorSpaceSignature pcs); -CMSAPI cmsColorSpaceSignature - CMSEXPORT cmsGetColorSpace(cmsHPROFILE hProfile); -CMSAPI void CMSEXPORT cmsSetColorSpace(cmsHPROFILE hProfile, cmsColorSpaceSignature sig); -CMSAPI cmsProfileClassSignature - CMSEXPORT cmsGetDeviceClass(cmsHPROFILE hProfile); -CMSAPI void CMSEXPORT cmsSetDeviceClass(cmsHPROFILE hProfile, cmsProfileClassSignature sig); -CMSAPI void CMSEXPORT cmsSetProfileVersion(cmsHPROFILE hProfile, cmsFloat64Number Version); -CMSAPI cmsFloat64Number CMSEXPORT cmsGetProfileVersion(cmsHPROFILE hProfile); - -CMSAPI cmsUInt32Number CMSEXPORT cmsGetEncodedICCversion(cmsHPROFILE hProfile); -CMSAPI void CMSEXPORT cmsSetEncodedICCversion(cmsHPROFILE hProfile, cmsUInt32Number Version); - -// How profiles may be used -#define LCMS_USED_AS_INPUT 0 -#define LCMS_USED_AS_OUTPUT 1 -#define LCMS_USED_AS_PROOF 2 - -CMSAPI cmsBool CMSEXPORT cmsIsIntentSupported(cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number UsedDirection); -CMSAPI cmsBool CMSEXPORT cmsIsMatrixShaper(cmsHPROFILE hProfile); -CMSAPI cmsBool CMSEXPORT cmsIsCLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number UsedDirection); - -// Translate form/to our notation to ICC -CMSAPI cmsColorSpaceSignature CMSEXPORT _cmsICCcolorSpace(int OurNotation); -CMSAPI int CMSEXPORT _cmsLCMScolorSpace(cmsColorSpaceSignature ProfileSpace); - -CMSAPI cmsUInt32Number CMSEXPORT cmsChannelsOf(cmsColorSpaceSignature ColorSpace); - -// Build a suitable formatter for the colorspace of this profile -CMSAPI cmsUInt32Number CMSEXPORT cmsFormatterForColorspaceOfProfile(cmsHPROFILE hProfile, cmsUInt32Number nBytes, cmsBool lIsFloat); -CMSAPI cmsUInt32Number CMSEXPORT cmsFormatterForPCSOfProfile(cmsHPROFILE hProfile, cmsUInt32Number nBytes, cmsBool lIsFloat); - - -// Localized info -typedef enum { - cmsInfoDescription = 0, - cmsInfoManufacturer = 1, - cmsInfoModel = 2, - cmsInfoCopyright = 3 -} cmsInfoType; - -CMSAPI cmsUInt32Number CMSEXPORT cmsGetProfileInfo(cmsHPROFILE hProfile, cmsInfoType Info, - const char LanguageCode[3], const char CountryCode[3], - wchar_t* Buffer, cmsUInt32Number BufferSize); - -CMSAPI cmsUInt32Number CMSEXPORT cmsGetProfileInfoASCII(cmsHPROFILE hProfile, cmsInfoType Info, - const char LanguageCode[3], const char CountryCode[3], - char* Buffer, cmsUInt32Number BufferSize); - -// IO handlers ---------------------------------------------------------------------------------------------------------- - -typedef struct _cms_io_handler cmsIOHANDLER; - -CMSAPI cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromFile(cmsContext ContextID, const char* FileName, const char* AccessMode); -CMSAPI cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromStream(cmsContext ContextID, FILE* Stream); -CMSAPI cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromMem(cmsContext ContextID, void *Buffer, cmsUInt32Number size, const char* AccessMode); -CMSAPI cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromNULL(cmsContext ContextID); -CMSAPI cmsBool CMSEXPORT cmsCloseIOhandler(cmsIOHANDLER* io); - -// MD5 message digest -------------------------------------------------------------------------------------------------- - -CMSAPI cmsBool CMSEXPORT cmsMD5computeID(cmsHPROFILE hProfile); -CMSAPI cmsBool CMSEXPORT cmsMD5computeIDExt(const void* buf, unsigned long size, unsigned char ProfileID[16]); - -// Profile high level funtions ------------------------------------------------------------------------------------------ - -CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromFile(const char *ICCProfile, const char *sAccess); -CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromFileTHR(cmsContext ContextID, const char *ICCProfile, const char *sAccess); -CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromStream(FILE* ICCProfile, const char* sAccess); -CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromStreamTHR(cmsContext ContextID, FILE* ICCProfile, const char* sAccess); -CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromMem(const void * MemPtr, cmsUInt32Number dwSize); -CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromMemTHR(cmsContext ContextID, const void * MemPtr, cmsUInt32Number dwSize); -CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromIOhandlerTHR(cmsContext ContextID, cmsIOHANDLER* io); -CMSAPI cmsHPROFILE CMSEXPORT cmsOpenProfileFromIOhandler2THR(cmsContext ContextID, cmsIOHANDLER* io, cmsBool write); -CMSAPI cmsBool CMSEXPORT cmsCloseProfile(cmsHPROFILE hProfile); - -CMSAPI cmsBool CMSEXPORT cmsSaveProfileToFile(cmsHPROFILE hProfile, const char* FileName); -CMSAPI cmsBool CMSEXPORT cmsSaveProfileToStream(cmsHPROFILE hProfile, FILE* Stream); -CMSAPI cmsBool CMSEXPORT cmsSaveProfileToMem(cmsHPROFILE hProfile, void *MemPtr, cmsUInt32Number* BytesNeeded); -CMSAPI cmsUInt32Number CMSEXPORT cmsSaveProfileToIOhandler(cmsHPROFILE hProfile, cmsIOHANDLER* io); - -// Predefined virtual profiles ------------------------------------------------------------------------------------------ - -CMSAPI cmsHPROFILE CMSEXPORT cmsCreateRGBProfileTHR(cmsContext ContextID, - const cmsCIExyY* WhitePoint, - const cmsCIExyYTRIPLE* Primaries, - cmsToneCurve* const TransferFunction[3]); - -CMSAPI cmsHPROFILE CMSEXPORT cmsCreateRGBProfile(const cmsCIExyY* WhitePoint, - const cmsCIExyYTRIPLE* Primaries, - cmsToneCurve* const TransferFunction[3]); - -CMSAPI cmsHPROFILE CMSEXPORT cmsCreateGrayProfileTHR(cmsContext ContextID, - const cmsCIExyY* WhitePoint, - const cmsToneCurve* TransferFunction); - -CMSAPI cmsHPROFILE CMSEXPORT cmsCreateGrayProfile(const cmsCIExyY* WhitePoint, - const cmsToneCurve* TransferFunction); - -CMSAPI cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLinkTHR(cmsContext ContextID, - cmsColorSpaceSignature ColorSpace, - cmsToneCurve* const TransferFunctions[]); - -CMSAPI cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLink(cmsColorSpaceSignature ColorSpace, - cmsToneCurve* const TransferFunctions[]); - -CMSAPI cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLinkTHR(cmsContext ContextID, - cmsColorSpaceSignature ColorSpace, cmsFloat64Number Limit); - -CMSAPI cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLink(cmsColorSpaceSignature ColorSpace, cmsFloat64Number Limit); - - -CMSAPI cmsHPROFILE CMSEXPORT cmsCreateLab2ProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint); -CMSAPI cmsHPROFILE CMSEXPORT cmsCreateLab2Profile(const cmsCIExyY* WhitePoint); -CMSAPI cmsHPROFILE CMSEXPORT cmsCreateLab4ProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint); -CMSAPI cmsHPROFILE CMSEXPORT cmsCreateLab4Profile(const cmsCIExyY* WhitePoint); - -CMSAPI cmsHPROFILE CMSEXPORT cmsCreateXYZProfileTHR(cmsContext ContextID); -CMSAPI cmsHPROFILE CMSEXPORT cmsCreateXYZProfile(void); - -CMSAPI cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfileTHR(cmsContext ContextID); -CMSAPI cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfile(void); - -CMSAPI cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfileTHR(cmsContext ContextID, - int nLUTPoints, - cmsFloat64Number Bright, - cmsFloat64Number Contrast, - cmsFloat64Number Hue, - cmsFloat64Number Saturation, - int TempSrc, - int TempDest); - -CMSAPI cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfile(int nLUTPoints, - cmsFloat64Number Bright, - cmsFloat64Number Contrast, - cmsFloat64Number Hue, - cmsFloat64Number Saturation, - int TempSrc, - int TempDest); - -CMSAPI cmsHPROFILE CMSEXPORT cmsCreateNULLProfileTHR(cmsContext ContextID); -CMSAPI cmsHPROFILE CMSEXPORT cmsCreateNULLProfile(void); - -// Converts a transform to a devicelink profile -CMSAPI cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat64Number Version, cmsUInt32Number dwFlags); - -// Intents ---------------------------------------------------------------------------------------------- - -// ICC Intents -#define INTENT_PERCEPTUAL 0 -#define INTENT_RELATIVE_COLORIMETRIC 1 -#define INTENT_SATURATION 2 -#define INTENT_ABSOLUTE_COLORIMETRIC 3 - -// Non-ICC intents -#define INTENT_PRESERVE_K_ONLY_PERCEPTUAL 10 -#define INTENT_PRESERVE_K_ONLY_RELATIVE_COLORIMETRIC 11 -#define INTENT_PRESERVE_K_ONLY_SATURATION 12 -#define INTENT_PRESERVE_K_PLANE_PERCEPTUAL 13 -#define INTENT_PRESERVE_K_PLANE_RELATIVE_COLORIMETRIC 14 -#define INTENT_PRESERVE_K_PLANE_SATURATION 15 - -// 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 - -#define cmsFLAGS_NOCACHE 0x0040 // Inhibit 1-pixel cache -#define cmsFLAGS_NOOPTIMIZE 0x0100 // Inhibit optimizations -#define cmsFLAGS_NULLTRANSFORM 0x0200 // Don't transform anyway - -// Proofing flags -#define cmsFLAGS_GAMUTCHECK 0x1000 // Out of Gamut alarm -#define cmsFLAGS_SOFTPROOFING 0x4000 // Do softproofing - -// Misc -#define cmsFLAGS_BLACKPOINTCOMPENSATION 0x2000 -#define cmsFLAGS_NOWHITEONWHITEFIXUP 0x0004 // Don't fix scum dot -#define cmsFLAGS_HIGHRESPRECALC 0x0400 // Use more memory to give better accurancy -#define cmsFLAGS_LOWRESPRECALC 0x0800 // Use less memory to minimize resouces - -// For devicelink creation -#define cmsFLAGS_8BITS_DEVICELINK 0x0008 // Create 8 bits devicelinks -#define cmsFLAGS_GUESSDEVICECLASS 0x0020 // Guess device class (for transform2devicelink) -#define cmsFLAGS_KEEP_SEQUENCE 0x0080 // Keep profile sequence for devicelink creation - -// Specific to a particular optimizations -#define cmsFLAGS_FORCE_CLUT 0x0002 // Force CLUT optimization -#define cmsFLAGS_CLUT_POST_LINEARIZATION 0x0001 // create postlinearization tables if possible -#define cmsFLAGS_CLUT_PRE_LINEARIZATION 0x0010 // create prelinearization tables if possible - -// Fine-tune control over number of gridpoints -#define cmsFLAGS_GRIDPOINTS(n) (((n) & 0xFF) << 16) - -// CRD special -#define cmsFLAGS_NODEFAULTRESOURCEDEF 0x01000000 - -// Transforms --------------------------------------------------------------------------------------------------- - -CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateTransformTHR(cmsContext ContextID, - cmsHPROFILE Input, - cmsUInt32Number InputFormat, - cmsHPROFILE Output, - cmsUInt32Number OutputFormat, - cmsUInt32Number Intent, - cmsUInt32Number dwFlags); - -CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateTransform(cmsHPROFILE Input, - cmsUInt32Number InputFormat, - cmsHPROFILE Output, - cmsUInt32Number OutputFormat, - cmsUInt32Number Intent, - cmsUInt32Number dwFlags); - -CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransformTHR(cmsContext ContextID, - cmsHPROFILE Input, - cmsUInt32Number InputFormat, - cmsHPROFILE Output, - cmsUInt32Number OutputFormat, - cmsHPROFILE Proofing, - cmsUInt32Number Intent, - cmsUInt32Number ProofingIntent, - cmsUInt32Number dwFlags); - -CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransform(cmsHPROFILE Input, - cmsUInt32Number InputFormat, - cmsHPROFILE Output, - cmsUInt32Number OutputFormat, - cmsHPROFILE Proofing, - cmsUInt32Number Intent, - cmsUInt32Number ProofingIntent, - cmsUInt32Number dwFlags); - -CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransformTHR(cmsContext ContextID, - cmsHPROFILE hProfiles[], - cmsUInt32Number nProfiles, - cmsUInt32Number InputFormat, - cmsUInt32Number OutputFormat, - cmsUInt32Number Intent, - cmsUInt32Number dwFlags); - - -CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles[], - cmsUInt32Number nProfiles, - cmsUInt32Number InputFormat, - cmsUInt32Number OutputFormat, - cmsUInt32Number Intent, - cmsUInt32Number dwFlags); - - -CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID, - cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[], - cmsBool BPC[], - cmsUInt32Number Intents[], - cmsFloat64Number AdaptationStates[], - cmsHPROFILE hGamutProfile, - cmsUInt32Number nGamutPCSposition, - cmsUInt32Number InputFormat, - cmsUInt32Number OutputFormat, - cmsUInt32Number dwFlags); - -CMSAPI void CMSEXPORT cmsDeleteTransform(cmsHTRANSFORM hTransform); - -CMSAPI void CMSEXPORT cmsDoTransform(cmsHTRANSFORM Transform, - const void * InputBuffer, - void * OutputBuffer, - cmsUInt32Number Size); - -CMSAPI void CMSEXPORT cmsDoTransformStride(cmsHTRANSFORM Transform, - const void * InputBuffer, - void * OutputBuffer, - cmsUInt32Number Size, - cmsUInt32Number Stride); - - -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); - -// Grab the input/output formats -CMSAPI cmsUInt32Number CMSEXPORT cmsGetTransformInputFormat(cmsHTRANSFORM hTransform); -CMSAPI cmsUInt32Number CMSEXPORT cmsGetTransformOutputFormat(cmsHTRANSFORM hTransform); - -// For backwards compatibility -CMSAPI cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform, - cmsUInt32Number InputFormat, - cmsUInt32Number OutputFormat); - - - -// PostScript ColorRenderingDictionary and ColorSpaceArray ---------------------------------------------------- - -typedef enum { cmsPS_RESOURCE_CSA, cmsPS_RESOURCE_CRD } cmsPSResourceType; - -// lcms2 unified method to access postscript color resources -CMSAPI cmsUInt32Number CMSEXPORT cmsGetPostScriptColorResource(cmsContext ContextID, - cmsPSResourceType Type, - cmsHPROFILE hProfile, - cmsUInt32Number Intent, - cmsUInt32Number dwFlags, - cmsIOHANDLER* io); - -CMSAPI cmsUInt32Number CMSEXPORT cmsGetPostScriptCSA(cmsContext ContextID, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags, void* Buffer, cmsUInt32Number dwBufferLen); -CMSAPI cmsUInt32Number CMSEXPORT cmsGetPostScriptCRD(cmsContext ContextID, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags, void* Buffer, cmsUInt32Number dwBufferLen); - - -// IT8.7 / CGATS.17-200x handling ----------------------------------------------------------------------------- - -CMSAPI cmsHANDLE CMSEXPORT cmsIT8Alloc(cmsContext ContextID); -CMSAPI void CMSEXPORT cmsIT8Free(cmsHANDLE hIT8); - -// Tables -CMSAPI cmsUInt32Number CMSEXPORT cmsIT8TableCount(cmsHANDLE hIT8); -CMSAPI cmsInt32Number CMSEXPORT cmsIT8SetTable(cmsHANDLE hIT8, cmsUInt32Number nTable); - -// Persistence -CMSAPI cmsHANDLE CMSEXPORT cmsIT8LoadFromFile(cmsContext ContextID, const char* cFileName); -CMSAPI cmsHANDLE CMSEXPORT cmsIT8LoadFromMem(cmsContext ContextID, void *Ptr, cmsUInt32Number len); -// CMSAPI cmsHANDLE CMSEXPORT cmsIT8LoadFromIOhandler(cmsContext ContextID, cmsIOHANDLER* io); - -CMSAPI cmsBool CMSEXPORT cmsIT8SaveToFile(cmsHANDLE hIT8, const char* cFileName); -CMSAPI cmsBool CMSEXPORT cmsIT8SaveToMem(cmsHANDLE hIT8, void *MemPtr, cmsUInt32Number* BytesNeeded); - -// Properties -CMSAPI const char* CMSEXPORT cmsIT8GetSheetType(cmsHANDLE hIT8); -CMSAPI cmsBool CMSEXPORT cmsIT8SetSheetType(cmsHANDLE hIT8, const char* Type); - -CMSAPI cmsBool CMSEXPORT cmsIT8SetComment(cmsHANDLE hIT8, const char* cComment); - -CMSAPI cmsBool CMSEXPORT cmsIT8SetPropertyStr(cmsHANDLE hIT8, const char* cProp, const char *Str); -CMSAPI cmsBool CMSEXPORT cmsIT8SetPropertyDbl(cmsHANDLE hIT8, const char* cProp, cmsFloat64Number Val); -CMSAPI cmsBool CMSEXPORT cmsIT8SetPropertyHex(cmsHANDLE hIT8, const char* cProp, cmsUInt32Number Val); -CMSAPI cmsBool CMSEXPORT cmsIT8SetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char* SubKey, const char *Buffer); -CMSAPI cmsBool CMSEXPORT cmsIT8SetPropertyUncooked(cmsHANDLE hIT8, const char* Key, const char* Buffer); - - -CMSAPI const char* CMSEXPORT cmsIT8GetProperty(cmsHANDLE hIT8, const char* cProp); -CMSAPI cmsFloat64Number CMSEXPORT cmsIT8GetPropertyDbl(cmsHANDLE hIT8, const char* cProp); -CMSAPI const char* CMSEXPORT cmsIT8GetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char *SubKey); -CMSAPI cmsUInt32Number CMSEXPORT cmsIT8EnumProperties(cmsHANDLE hIT8, char ***PropertyNames); -CMSAPI cmsUInt32Number CMSEXPORT cmsIT8EnumPropertyMulti(cmsHANDLE hIT8, const char* cProp, const char ***SubpropertyNames); - -// Datasets -CMSAPI const char* CMSEXPORT cmsIT8GetDataRowCol(cmsHANDLE hIT8, int row, int col); -CMSAPI cmsFloat64Number CMSEXPORT cmsIT8GetDataRowColDbl(cmsHANDLE hIT8, int row, int col); - -CMSAPI cmsBool CMSEXPORT cmsIT8SetDataRowCol(cmsHANDLE hIT8, int row, int col, - const char* Val); - -CMSAPI cmsBool CMSEXPORT cmsIT8SetDataRowColDbl(cmsHANDLE hIT8, int row, int col, - cmsFloat64Number Val); - -CMSAPI const char* CMSEXPORT cmsIT8GetData(cmsHANDLE hIT8, const char* cPatch, const char* cSample); - - -CMSAPI cmsFloat64Number CMSEXPORT cmsIT8GetDataDbl(cmsHANDLE hIT8, const char* cPatch, const char* cSample); - -CMSAPI cmsBool CMSEXPORT cmsIT8SetData(cmsHANDLE hIT8, const char* cPatch, - const char* cSample, - const char *Val); - -CMSAPI cmsBool CMSEXPORT cmsIT8SetDataDbl(cmsHANDLE hIT8, const char* cPatch, - const char* cSample, - cmsFloat64Number Val); - -CMSAPI int CMSEXPORT cmsIT8FindDataFormat(cmsHANDLE hIT8, const char* cSample); -CMSAPI cmsBool CMSEXPORT cmsIT8SetDataFormat(cmsHANDLE hIT8, int n, const char *Sample); -CMSAPI int CMSEXPORT cmsIT8EnumDataFormat(cmsHANDLE hIT8, char ***SampleNames); - -CMSAPI const char* CMSEXPORT cmsIT8GetPatchName(cmsHANDLE hIT8, int nPatch, char* buffer); -CMSAPI int CMSEXPORT cmsIT8GetPatchByName(cmsHANDLE hIT8, const char *cPatch); - -// The LABEL extension -CMSAPI int CMSEXPORT cmsIT8SetTableByLabel(cmsHANDLE hIT8, const char* cSet, const char* cField, const char* ExpectedType); - -CMSAPI cmsBool CMSEXPORT cmsIT8SetIndexColumn(cmsHANDLE hIT8, const char* cSample); - -// Formatter for double -CMSAPI void CMSEXPORT cmsIT8DefineDblFormat(cmsHANDLE hIT8, const char* Formatter); - -// Gamut boundary description routines ------------------------------------------------------------------------------ - -CMSAPI cmsHANDLE CMSEXPORT cmsGBDAlloc(cmsContext ContextID); -CMSAPI void CMSEXPORT cmsGBDFree(cmsHANDLE hGBD); -CMSAPI cmsBool CMSEXPORT cmsGDBAddPoint(cmsHANDLE hGBD, const cmsCIELab* Lab); -CMSAPI cmsBool CMSEXPORT cmsGDBCompute(cmsHANDLE hGDB, cmsUInt32Number dwFlags); -CMSAPI cmsBool CMSEXPORT cmsGDBCheckPoint(cmsHANDLE hGBD, const cmsCIELab* Lab); - -// Feature detection ---------------------------------------------------------------------------------------------- - -// Estimate the black point -CMSAPI cmsBool CMSEXPORT cmsDetectBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags); -CMSAPI cmsBool CMSEXPORT cmsDetectDestinationBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags); - -// Estimate total area coverage -CMSAPI cmsFloat64Number CMSEXPORT cmsDetectTAC(cmsHPROFILE hProfile); - - -// Poor man's gamut mapping -CMSAPI cmsBool CMSEXPORT cmsDesaturateLab(cmsCIELab* Lab, - double amax, double amin, - double bmax, double bmin); - -#ifndef CMS_USE_CPP_API -# ifdef __cplusplus - } -# endif -#endif - -#define _lcms2_H -#endif diff --git a/third_party/lcms2-2.6/include/lcms2_plugin.h b/third_party/lcms2-2.6/include/lcms2_plugin.h deleted file mode 100644 index 0c95d1f73c..0000000000 --- a/third_party/lcms2-2.6/include/lcms2_plugin.h +++ /dev/null @@ -1,637 +0,0 @@ -//--------------------------------------------------------------------------------- -// -// Little Color Management System -// Copyright (c) 1998-2011 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. -// -//--------------------------------------------------------------------------------- -// -// This is the plug-in header file. Normal LittleCMS clients should not use it. -// It is provided for plug-in writters that may want to access the support -// functions to do low level operations. All plug-in related structures -// are defined here. Including this file forces to include the standard API too. - -#ifndef _lcms_plugin_H - -// Deal with Microsoft's attempt at deprecating C standard runtime functions -#ifdef _MSC_VER -# if (_MSC_VER >= 1400) -# ifndef _CRT_SECURE_NO_DEPRECATE -# define _CRT_SECURE_NO_DEPRECATE -# endif -# ifndef _CRT_SECURE_NO_WARNINGS -# define _CRT_SECURE_NO_WARNINGS -# endif -# endif -#endif - -#ifndef _lcms2_H -#include "lcms2.h" -#endif - -// We need some standard C functions. -#include -#include -#include -#include -#include - - -#ifndef CMS_USE_CPP_API -# ifdef __cplusplus -extern "C" { -# endif -#endif - -// Vector & Matrix operations ----------------------------------------------------------------------- - -// Axis of the matrix/array. No specific meaning at all. -#define VX 0 -#define VY 1 -#define VZ 2 - -// Vectors -typedef struct { - cmsFloat64Number n[3]; - - } cmsVEC3; - -// 3x3 Matrix -typedef struct { - cmsVEC3 v[3]; - - } cmsMAT3; - -CMSAPI void CMSEXPORT _cmsVEC3init(cmsVEC3* r, cmsFloat64Number x, cmsFloat64Number y, cmsFloat64Number z); -CMSAPI void CMSEXPORT _cmsVEC3minus(cmsVEC3* r, const cmsVEC3* a, const cmsVEC3* b); -CMSAPI void CMSEXPORT _cmsVEC3cross(cmsVEC3* r, const cmsVEC3* u, const cmsVEC3* v); -CMSAPI cmsFloat64Number CMSEXPORT _cmsVEC3dot(const cmsVEC3* u, const cmsVEC3* v); -CMSAPI cmsFloat64Number CMSEXPORT _cmsVEC3length(const cmsVEC3* a); -CMSAPI cmsFloat64Number CMSEXPORT _cmsVEC3distance(const cmsVEC3* a, const cmsVEC3* b); - -CMSAPI void CMSEXPORT _cmsMAT3identity(cmsMAT3* a); -CMSAPI cmsBool CMSEXPORT _cmsMAT3isIdentity(const cmsMAT3* a); -CMSAPI void CMSEXPORT _cmsMAT3per(cmsMAT3* r, const cmsMAT3* a, const cmsMAT3* b); -CMSAPI cmsBool CMSEXPORT _cmsMAT3inverse(const cmsMAT3* a, cmsMAT3* b); -CMSAPI cmsBool CMSEXPORT _cmsMAT3solve(cmsVEC3* x, cmsMAT3* a, cmsVEC3* b); -CMSAPI void CMSEXPORT _cmsMAT3eval(cmsVEC3* r, const cmsMAT3* a, const cmsVEC3* v); - - -// Error logging ------------------------------------------------------------------------------------- - -CMSAPI void CMSEXPORT cmsSignalError(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *ErrorText, ...); - -// Memory management ---------------------------------------------------------------------------------- - -CMSAPI void* CMSEXPORT _cmsMalloc(cmsContext ContextID, cmsUInt32Number size); -CMSAPI void* CMSEXPORT _cmsMallocZero(cmsContext ContextID, cmsUInt32Number size); -CMSAPI void* CMSEXPORT _cmsCalloc(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size); -CMSAPI void* CMSEXPORT _cmsRealloc(cmsContext ContextID, void* Ptr, cmsUInt32Number NewSize); -CMSAPI void CMSEXPORT _cmsFree(cmsContext ContextID, void* Ptr); -CMSAPI void* CMSEXPORT _cmsDupMem(cmsContext ContextID, const void* Org, cmsUInt32Number size); - -// I/O handler ---------------------------------------------------------------------------------- - -struct _cms_io_handler { - - void* stream; // Associated stream, which is implemented differently depending on media. - - cmsContext ContextID; - cmsUInt32Number UsedSpace; - cmsUInt32Number ReportedSize; - char PhysicalFile[cmsMAX_PATH]; - - cmsUInt32Number (* Read)(struct _cms_io_handler* iohandler, void *Buffer, - cmsUInt32Number size, - cmsUInt32Number count); - cmsBool (* Seek)(struct _cms_io_handler* iohandler, cmsUInt32Number offset); - cmsBool (* Close)(struct _cms_io_handler* iohandler); - cmsUInt32Number (* Tell)(struct _cms_io_handler* iohandler); - cmsBool (* Write)(struct _cms_io_handler* iohandler, cmsUInt32Number size, - const void* Buffer); -}; - -// Endianess adjust functions -CMSAPI cmsUInt16Number CMSEXPORT _cmsAdjustEndianess16(cmsUInt16Number Word); -CMSAPI cmsUInt32Number CMSEXPORT _cmsAdjustEndianess32(cmsUInt32Number Value); -CMSAPI void CMSEXPORT _cmsAdjustEndianess64(cmsUInt64Number* Result, cmsUInt64Number* QWord); - -// Helper IO functions -CMSAPI cmsBool CMSEXPORT _cmsReadUInt8Number(cmsIOHANDLER* io, cmsUInt8Number* n); -CMSAPI cmsBool CMSEXPORT _cmsReadUInt16Number(cmsIOHANDLER* io, cmsUInt16Number* n); -CMSAPI cmsBool CMSEXPORT _cmsReadUInt32Number(cmsIOHANDLER* io, cmsUInt32Number* n); -CMSAPI cmsBool CMSEXPORT _cmsReadFloat32Number(cmsIOHANDLER* io, cmsFloat32Number* n); -CMSAPI cmsBool CMSEXPORT _cmsReadUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n); -CMSAPI cmsBool CMSEXPORT _cmsRead15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number* n); -CMSAPI cmsBool CMSEXPORT _cmsReadXYZNumber(cmsIOHANDLER* io, cmsCIEXYZ* XYZ); -CMSAPI cmsBool CMSEXPORT _cmsReadUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, cmsUInt16Number* Array); - -CMSAPI cmsBool CMSEXPORT _cmsWriteUInt8Number(cmsIOHANDLER* io, cmsUInt8Number n); -CMSAPI cmsBool CMSEXPORT _cmsWriteUInt16Number(cmsIOHANDLER* io, cmsUInt16Number n); -CMSAPI cmsBool CMSEXPORT _cmsWriteUInt32Number(cmsIOHANDLER* io, cmsUInt32Number n); -CMSAPI cmsBool CMSEXPORT _cmsWriteFloat32Number(cmsIOHANDLER* io, cmsFloat32Number n); -CMSAPI cmsBool CMSEXPORT _cmsWriteUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n); -CMSAPI cmsBool CMSEXPORT _cmsWrite15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number n); -CMSAPI cmsBool CMSEXPORT _cmsWriteXYZNumber(cmsIOHANDLER* io, const cmsCIEXYZ* XYZ); -CMSAPI cmsBool CMSEXPORT _cmsWriteUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, const cmsUInt16Number* Array); - -// ICC base tag -typedef struct { - cmsTagTypeSignature sig; - cmsInt8Number reserved[4]; - -} _cmsTagBase; - -// Type base helper functions -CMSAPI cmsTagTypeSignature CMSEXPORT _cmsReadTypeBase(cmsIOHANDLER* io); -CMSAPI cmsBool CMSEXPORT _cmsWriteTypeBase(cmsIOHANDLER* io, cmsTagTypeSignature sig); - -// Alignment functions -CMSAPI cmsBool CMSEXPORT _cmsReadAlignment(cmsIOHANDLER* io); -CMSAPI cmsBool CMSEXPORT _cmsWriteAlignment(cmsIOHANDLER* io); - -// To deal with text streams. 2K at most -CMSAPI cmsBool CMSEXPORT _cmsIOPrintf(cmsIOHANDLER* io, const char* frm, ...); - -// Fixed point helper functions -CMSAPI cmsFloat64Number CMSEXPORT _cms8Fixed8toDouble(cmsUInt16Number fixed8); -CMSAPI cmsUInt16Number CMSEXPORT _cmsDoubleTo8Fixed8(cmsFloat64Number val); - -CMSAPI cmsFloat64Number CMSEXPORT _cms15Fixed16toDouble(cmsS15Fixed16Number fix32); -CMSAPI cmsS15Fixed16Number CMSEXPORT _cmsDoubleTo15Fixed16(cmsFloat64Number v); - -// Date/time helper functions -CMSAPI void CMSEXPORT _cmsEncodeDateTimeNumber(cmsDateTimeNumber *Dest, const struct tm *Source); -CMSAPI void CMSEXPORT _cmsDecodeDateTimeNumber(const cmsDateTimeNumber *Source, struct tm *Dest); - -//---------------------------------------------------------------------------------------------------------- - -// Shared callbacks for user data -typedef void (* _cmsFreeUserDataFn)(cmsContext ContextID, void* Data); -typedef void* (* _cmsDupUserDataFn)(cmsContext ContextID, const void* Data); - -//---------------------------------------------------------------------------------------------------------- - -// Plug-in foundation -#define cmsPluginMagicNumber 0x61637070 // 'acpp' - -#define cmsPluginMemHandlerSig 0x6D656D48 // 'memH' -#define cmsPluginInterpolationSig 0x696E7048 // 'inpH' -#define cmsPluginParametricCurveSig 0x70617248 // 'parH' -#define cmsPluginFormattersSig 0x66726D48 // 'frmH -#define cmsPluginTagTypeSig 0x74797048 // 'typH' -#define cmsPluginTagSig 0x74616748 // 'tagH' -#define cmsPluginRenderingIntentSig 0x696E7448 // 'intH' -#define cmsPluginMultiProcessElementSig 0x6D706548 // 'mpeH' -#define cmsPluginOptimizationSig 0x6F707448 // 'optH' -#define cmsPluginTransformSig 0x7A666D48 // 'xfmH' -#define cmsPluginMutexSig 0x6D747A48 // 'mtxH' - -typedef struct _cmsPluginBaseStruct { - - cmsUInt32Number Magic; // 'acpp' signature - cmsUInt32Number ExpectedVersion; // Expected version of LittleCMS - cmsUInt32Number Type; // Type of plug-in - struct _cmsPluginBaseStruct* Next; // For multiple plugin definition. NULL for end of list. - -} cmsPluginBase; - -// Maximum number of types in a plugin array -#define MAX_TYPES_IN_LCMS_PLUGIN 20 - -//---------------------------------------------------------------------------------------------------------- - -// 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 - _cmsMallocFnPtrType MallocPtr; - _cmsFreeFnPtrType FreePtr; - _cmsReallocFnPtrType ReallocPtr; - - // Optional - _cmsMalloZerocFnPtrType MallocZeroPtr; - _cmsCallocFnPtrType CallocPtr; - _cmsDupFnPtrType DupPtr; - -} cmsPluginMemHandler; - - -// ------------------------------------------------------------------------------------------------------------------ - -// Interpolation. 16 bits and floating point versions. -struct _cms_interp_struc; - -// Interpolation callbacks - -// 16 bits forward interpolation. This function performs precision-limited linear interpolation -// and is supposed to be quite fast. Implementation may be tetrahedral or trilinear, and plug-ins may -// choose to implement any other interpolation algorithm. -typedef void (* _cmsInterpFn16)(register const cmsUInt16Number Input[], - register cmsUInt16Number Output[], - register const struct _cms_interp_struc* p); - -// Floating point forward interpolation. Full precision interpolation using floats. This is not a -// time critical function. Implementation may be tetrahedral or trilinear, and plug-ins may -// choose to implement any other interpolation algorithm. -typedef void (* _cmsInterpFnFloat)(cmsFloat32Number const Input[], - cmsFloat32Number Output[], - const struct _cms_interp_struc* p); - - - -// This type holds a pointer to an interpolator that can be either 16 bits or float -typedef union { - _cmsInterpFn16 Lerp16; // Forward interpolation in 16 bits - _cmsInterpFnFloat LerpFloat; // Forward interpolation in floating point -} cmsInterpFunction; - -// Flags for interpolator selection -#define CMS_LERP_FLAGS_16BITS 0x0000 // The default -#define CMS_LERP_FLAGS_FLOAT 0x0001 // Requires different implementation -#define CMS_LERP_FLAGS_TRILINEAR 0x0100 // Hint only - - -#define MAX_INPUT_DIMENSIONS 8 - -typedef struct _cms_interp_struc { // Used on all interpolations. Supplied by lcms2 when calling the interpolation function - - cmsContext ContextID; // The calling thread - - cmsUInt32Number dwFlags; // Keep original flags - cmsUInt32Number nInputs; // != 1 only in 3D interpolation - cmsUInt32Number nOutputs; // != 1 only in 3D interpolation - - cmsUInt32Number nSamples[MAX_INPUT_DIMENSIONS]; // Valid on all kinds of tables - cmsUInt32Number Domain[MAX_INPUT_DIMENSIONS]; // Domain = nSamples - 1 - - cmsUInt32Number opta[MAX_INPUT_DIMENSIONS]; // Optimization for 3D CLUT. This is the number of nodes premultiplied for each - // dimension. For example, in 7 nodes, 7, 7^2 , 7^3, 7^4, etc. On non-regular - // Samplings may vary according of the number of nodes for each dimension. - - const void *Table; // Points to the actual interpolation table - cmsInterpFunction Interpolation; // Points to the function to do the interpolation - - } cmsInterpParams; - -// Interpolators factory -typedef cmsInterpFunction (* cmsInterpFnFactory)(cmsUInt32Number nInputChannels, cmsUInt32Number nOutputChannels, cmsUInt32Number dwFlags); - -// The plug-in -typedef struct { - cmsPluginBase base; - - // Points to a user-supplied function which implements the factory - cmsInterpFnFactory InterpolatorsFactory; - -} cmsPluginInterpolation; - -//---------------------------------------------------------------------------------------------------------- - -// Parametric curves. A negative type means same function but analytically inverted. Max. number of params is 10 - -// Evaluator callback for user-suplied parametric curves. May implement more than one type -typedef cmsFloat64Number (* cmsParametricCurveEvaluator)(cmsInt32Number Type, const cmsFloat64Number Params[10], cmsFloat64Number R); - -// Plug-in may implement an arbitrary number of parametric curves -typedef struct { - cmsPluginBase base; - - cmsUInt32Number nFunctions; // Number of supported functions - cmsUInt32Number FunctionTypes[MAX_TYPES_IN_LCMS_PLUGIN]; // The identification types - cmsUInt32Number ParameterCount[MAX_TYPES_IN_LCMS_PLUGIN]; // Number of parameters for each function - - cmsParametricCurveEvaluator Evaluator; // The evaluator - -} cmsPluginParametricCurves; -//---------------------------------------------------------------------------------------------------------- - -// Formatters. This plug-in adds new handlers, replacing them if they already exist. Formatters dealing with -// cmsFloat32Number (bps = 4) or double (bps = 0) types are requested via FormatterFloat callback. Others come across -// Formatter16 callback - -struct _cmstransform_struct; - -typedef cmsUInt8Number* (* cmsFormatter16)(register struct _cmstransform_struct* CMMcargo, - register cmsUInt16Number Values[], - register cmsUInt8Number* Buffer, - register cmsUInt32Number Stride); - -typedef cmsUInt8Number* (* cmsFormatterFloat)(struct _cmstransform_struct* CMMcargo, - cmsFloat32Number Values[], - cmsUInt8Number* Buffer, - cmsUInt32Number Stride); - -// This type holds a pointer to a formatter that can be either 16 bits or cmsFloat32Number -typedef union { - cmsFormatter16 Fmt16; - cmsFormatterFloat FmtFloat; - -} cmsFormatter; - -#define CMS_PACK_FLAGS_16BITS 0x0000 -#define CMS_PACK_FLAGS_FLOAT 0x0001 - -typedef enum { cmsFormatterInput=0, cmsFormatterOutput=1 } cmsFormatterDirection; - -typedef cmsFormatter (* cmsFormatterFactory)(cmsUInt32Number Type, // Specific type, i.e. TYPE_RGB_8 - cmsFormatterDirection Dir, - cmsUInt32Number dwFlags); // precision - -// Plug-in may implement an arbitrary number of formatters -typedef struct { - cmsPluginBase base; - cmsFormatterFactory FormattersFactory; - -} cmsPluginFormatters; - -//---------------------------------------------------------------------------------------------------------- - -// Tag type handler. Each type is free to return anything it wants, and it is up to the caller to -// know in advance what is the type contained in the tag. -typedef struct _cms_typehandler_struct { - - cmsTagTypeSignature Signature; // The signature of the type - - // Allocates and reads items - void * (* ReadPtr)(struct _cms_typehandler_struct* self, - cmsIOHANDLER* io, - cmsUInt32Number* nItems, - cmsUInt32Number SizeOfTag); - - // Writes n Items - cmsBool (* WritePtr)(struct _cms_typehandler_struct* self, - cmsIOHANDLER* io, - void* Ptr, - cmsUInt32Number nItems); - - // Duplicate an item or array of items - void* (* DupPtr)(struct _cms_typehandler_struct* self, - const void *Ptr, - cmsUInt32Number n); - - // Free all resources - void (* FreePtr)(struct _cms_typehandler_struct* self, - void *Ptr); - - // Additional parameters used by the calling thread - cmsContext ContextID; - cmsUInt32Number ICCVersion; - -} cmsTagTypeHandler; - -// Each plug-in implements a single type -typedef struct { - cmsPluginBase base; - cmsTagTypeHandler Handler; - -} cmsPluginTagType; - -//---------------------------------------------------------------------------------------------------------- - -// This is the tag plugin, which identifies tags. For writing, a pointer to function is provided. -// This function should return the desired type for this tag, given the version of profile -// and the data being serialized. -typedef struct { - - cmsUInt32Number ElemCount; // If this tag needs an array, how many elements should keep - - // For reading. - cmsUInt32Number nSupportedTypes; // In how many types this tag can come (MAX_TYPES_IN_LCMS_PLUGIN maximum) - cmsTagTypeSignature SupportedTypes[MAX_TYPES_IN_LCMS_PLUGIN]; - - // For writting - cmsTagTypeSignature (* DecideType)(cmsFloat64Number ICCVersion, const void *Data); - -} cmsTagDescriptor; - -// Plug-in implements a single tag -typedef struct { - cmsPluginBase base; - - cmsTagSignature Signature; - cmsTagDescriptor Descriptor; - -} cmsPluginTag; - -//---------------------------------------------------------------------------------------------------------- - -// Custom intents. This function should join all profiles specified in the array in -// a single LUT. Any custom intent in the chain redirects to custom function. If more than -// one custom intent is found, the one located first is invoked. Usually users should use only one -// custom intent, so mixing custom intents in same multiprofile transform is not supported. - -typedef cmsPipeline* (* cmsIntentFn)( cmsContext ContextID, - cmsUInt32Number nProfiles, - cmsUInt32Number Intents[], - cmsHPROFILE hProfiles[], - cmsBool BPC[], - cmsFloat64Number AdaptationStates[], - cmsUInt32Number dwFlags); - - -// Each plug-in defines a single intent number. -typedef struct { - cmsPluginBase base; - cmsUInt32Number Intent; - cmsIntentFn Link; - char Description[256]; - -} cmsPluginRenderingIntent; - - -// The default ICC intents (perceptual, saturation, rel.col and abs.col) -CMSAPI cmsPipeline* CMSEXPORT _cmsDefaultICCintents(cmsContext ContextID, - cmsUInt32Number nProfiles, - cmsUInt32Number Intents[], - cmsHPROFILE hProfiles[], - cmsBool BPC[], - cmsFloat64Number AdaptationStates[], - cmsUInt32Number dwFlags); - - -//---------------------------------------------------------------------------------------------------------- - -// Pipelines, Multi Process Elements. - -typedef void (* _cmsStageEvalFn) (const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage* mpe); -typedef void*(* _cmsStageDupElemFn) (cmsStage* mpe); -typedef void (* _cmsStageFreeElemFn) (cmsStage* mpe); - - -// This function allocates a generic MPE -CMSAPI cmsStage* CMSEXPORT _cmsStageAllocPlaceholder(cmsContext ContextID, - cmsStageSignature Type, - cmsUInt32Number InputChannels, - cmsUInt32Number OutputChannels, - _cmsStageEvalFn EvalPtr, // Points to fn that evaluates the element (always in floating point) - _cmsStageDupElemFn DupElemPtr, // Points to a fn that duplicates the stage - _cmsStageFreeElemFn FreePtr, // Points to a fn that sets the element free - void* Data); // A generic pointer to whatever memory needed by the element -typedef struct { - cmsPluginBase base; - cmsTagTypeHandler Handler; - -} cmsPluginMultiProcessElement; - - -// Data kept in "Element" member of cmsStage - -// Curves -typedef struct { - cmsUInt32Number nCurves; - cmsToneCurve** TheCurves; - -} _cmsStageToneCurvesData; - -// Matrix -typedef struct { - cmsFloat64Number* Double; // floating point for the matrix - cmsFloat64Number* Offset; // The offset - -} _cmsStageMatrixData; - -// CLUT -typedef struct { - - union { // Can have only one of both representations at same time - cmsUInt16Number* T; // Points to the table 16 bits table - cmsFloat32Number* TFloat; // Points to the cmsFloat32Number table - - } Tab; - - cmsInterpParams* Params; - cmsUInt32Number nEntries; - cmsBool HasFloatValues; - -} _cmsStageCLutData; - - -//---------------------------------------------------------------------------------------------------------- -// Optimization. Using this plug-in, additional optimization strategies may be implemented. -// The function should return TRUE if any optimization is done on the LUT, this terminates -// the optimization search. Or FALSE if it is unable to optimize and want to give a chance -// to the rest of optimizers. - -typedef void (* _cmsOPTeval16Fn)(register const cmsUInt16Number In[], - register cmsUInt16Number Out[], - register const void* Data); - - -typedef cmsBool (* _cmsOPToptimizeFn)(cmsPipeline** Lut, - cmsUInt32Number Intent, - cmsUInt32Number* InputFormat, - cmsUInt32Number* OutputFormat, - cmsUInt32Number* dwFlags); - -// This function may be used to set the optional evaluator and a block of private data. If private data is being used, an optional -// duplicator and free functions should also be specified in order to duplicate the LUT construct. Use NULL to inhibit such functionality. - -CMSAPI void CMSEXPORT _cmsPipelineSetOptimizationParameters(cmsPipeline* Lut, - _cmsOPTeval16Fn Eval16, - void* PrivateData, - _cmsFreeUserDataFn FreePrivateDataFn, - _cmsDupUserDataFn DupPrivateDataFn); - -typedef struct { - cmsPluginBase base; - - // Optimize entry point - _cmsOPToptimizeFn OptimizePtr; - -} cmsPluginOptimization; - -//---------------------------------------------------------------------------------------------------------- -// Full xform -typedef void (* _cmsTransformFn)(struct _cmstransform_struct *CMMcargo, - const void* InputBuffer, - void* OutputBuffer, - cmsUInt32Number Size, - cmsUInt32Number Stride); - -typedef cmsBool (* _cmsTransformFactory)(_cmsTransformFn* xform, - void** UserData, - _cmsFreeUserDataFn* FreePrivateDataFn, - cmsPipeline** Lut, - cmsUInt32Number* InputFormat, - cmsUInt32Number* OutputFormat, - cmsUInt32Number* dwFlags); - - -// Retrieve user data as specified by the factory -CMSAPI void CMSEXPORT _cmsSetTransformUserData(struct _cmstransform_struct *CMMcargo, void* ptr, _cmsFreeUserDataFn FreePrivateDataFn); -CMSAPI void * CMSEXPORT _cmsGetTransformUserData(struct _cmstransform_struct *CMMcargo); - - -// Retrieve formatters -CMSAPI void CMSEXPORT _cmsGetTransformFormatters16 (struct _cmstransform_struct *CMMcargo, cmsFormatter16* FromInput, cmsFormatter16* ToOutput); -CMSAPI void CMSEXPORT _cmsGetTransformFormattersFloat(struct _cmstransform_struct *CMMcargo, cmsFormatterFloat* FromInput, cmsFormatterFloat* ToOutput); - -typedef struct { - cmsPluginBase base; - - // Transform entry point - _cmsTransformFactory Factory; - -} 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 - } -# endif -#endif - -#define _lcms_plugin_H -#endif diff --git a/third_party/lcms2-2.6/src/cmscam02.c b/third_party/lcms2-2.6/src/cmscam02.c deleted file mode 100644 index 9d874aa205..0000000000 --- a/third_party/lcms2-2.6/src/cmscam02.c +++ /dev/null @@ -1,486 +0,0 @@ -//--------------------------------------------------------------------------------- -// -// 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" - -// CIECAM 02 appearance model. Many thanks to Jordi Vilar for the debugging. - -// ---------- Implementation -------------------------------------------- - -typedef struct { - - cmsFloat64Number XYZ[3]; - cmsFloat64Number RGB[3]; - cmsFloat64Number RGBc[3]; - cmsFloat64Number RGBp[3]; - cmsFloat64Number RGBpa[3]; - cmsFloat64Number a, b, h, e, H, A, J, Q, s, t, C, M; - cmsFloat64Number abC[2]; - cmsFloat64Number abs[2]; - cmsFloat64Number abM[2]; - -} CAM02COLOR; - -typedef struct { - - CAM02COLOR adoptedWhite; - cmsFloat64Number LA, Yb; - cmsFloat64Number F, c, Nc; - cmsUInt32Number surround; - cmsFloat64Number n, Nbb, Ncb, z, FL, D; - - cmsContext ContextID; - -} cmsCIECAM02; - - -static -cmsFloat64Number compute_n(cmsCIECAM02* pMod) -{ - return (pMod -> Yb / pMod -> adoptedWhite.XYZ[1]); -} - -static -cmsFloat64Number compute_z(cmsCIECAM02* pMod) -{ - return (1.48 + pow(pMod -> n, 0.5)); -} - -static -cmsFloat64Number computeNbb(cmsCIECAM02* pMod) -{ - return (0.725 * pow((1.0 / pMod -> n), 0.2)); -} - -static -cmsFloat64Number computeFL(cmsCIECAM02* pMod) -{ - cmsFloat64Number k, FL; - - k = 1.0 / ((5.0 * pMod->LA) + 1.0); - FL = 0.2 * pow(k, 4.0) * (5.0 * pMod->LA) + 0.1 * - (pow((1.0 - pow(k, 4.0)), 2.0)) * - (pow((5.0 * pMod->LA), (1.0 / 3.0))); - - return FL; -} - -static -cmsFloat64Number computeD(cmsCIECAM02* pMod) -{ - cmsFloat64Number D; - - D = pMod->F - (1.0/3.6)*(exp(((-pMod ->LA-42) / 92.0))); - - return D; -} - - -static -CAM02COLOR XYZtoCAT02(CAM02COLOR clr) -{ - clr.RGB[0] = (clr.XYZ[0] * 0.7328) + (clr.XYZ[1] * 0.4296) + (clr.XYZ[2] * -0.1624); - clr.RGB[1] = (clr.XYZ[0] * -0.7036) + (clr.XYZ[1] * 1.6975) + (clr.XYZ[2] * 0.0061); - clr.RGB[2] = (clr.XYZ[0] * 0.0030) + (clr.XYZ[1] * 0.0136) + (clr.XYZ[2] * 0.9834); - - return clr; -} - -static -CAM02COLOR ChromaticAdaptation(CAM02COLOR clr, cmsCIECAM02* pMod) -{ - cmsUInt32Number i; - - for (i = 0; i < 3; i++) { - clr.RGBc[i] = ((pMod -> adoptedWhite.XYZ[1] * - (pMod->D / pMod -> adoptedWhite.RGB[i])) + - (1.0 - pMod->D)) * clr.RGB[i]; - } - - return clr; -} - - -static -CAM02COLOR CAT02toHPE(CAM02COLOR clr) -{ - cmsFloat64Number M[9]; - - M[0] =(( 0.38971 * 1.096124) + (0.68898 * 0.454369) + (-0.07868 * -0.009628)); - M[1] =(( 0.38971 * -0.278869) + (0.68898 * 0.473533) + (-0.07868 * -0.005698)); - M[2] =(( 0.38971 * 0.182745) + (0.68898 * 0.072098) + (-0.07868 * 1.015326)); - M[3] =((-0.22981 * 1.096124) + (1.18340 * 0.454369) + ( 0.04641 * -0.009628)); - M[4] =((-0.22981 * -0.278869) + (1.18340 * 0.473533) + ( 0.04641 * -0.005698)); - M[5] =((-0.22981 * 0.182745) + (1.18340 * 0.072098) + ( 0.04641 * 1.015326)); - M[6] =(-0.009628); - M[7] =(-0.005698); - M[8] =( 1.015326); - - clr.RGBp[0] = (clr.RGBc[0] * M[0]) + (clr.RGBc[1] * M[1]) + (clr.RGBc[2] * M[2]); - clr.RGBp[1] = (clr.RGBc[0] * M[3]) + (clr.RGBc[1] * M[4]) + (clr.RGBc[2] * M[5]); - clr.RGBp[2] = (clr.RGBc[0] * M[6]) + (clr.RGBc[1] * M[7]) + (clr.RGBc[2] * M[8]); - - return clr; -} - -static -CAM02COLOR NonlinearCompression(CAM02COLOR clr, cmsCIECAM02* pMod) -{ - cmsUInt32Number i; - cmsFloat64Number temp; - - for (i = 0; i < 3; i++) { - if (clr.RGBp[i] < 0) { - - temp = pow((-1.0 * pMod->FL * clr.RGBp[i] / 100.0), 0.42); - clr.RGBpa[i] = (-1.0 * 400.0 * temp) / (temp + 27.13) + 0.1; - } - else { - temp = pow((pMod->FL * clr.RGBp[i] / 100.0), 0.42); - clr.RGBpa[i] = (400.0 * temp) / (temp + 27.13) + 0.1; - } - } - - clr.A = (((2.0 * clr.RGBpa[0]) + clr.RGBpa[1] + - (clr.RGBpa[2] / 20.0)) - 0.305) * pMod->Nbb; - - return clr; -} - -static -CAM02COLOR ComputeCorrelates(CAM02COLOR clr, cmsCIECAM02* pMod) -{ - cmsFloat64Number a, b, temp, e, t, r2d, d2r; - - a = clr.RGBpa[0] - (12.0 * clr.RGBpa[1] / 11.0) + (clr.RGBpa[2] / 11.0); - b = (clr.RGBpa[0] + clr.RGBpa[1] - (2.0 * clr.RGBpa[2])) / 9.0; - - r2d = (180.0 / 3.141592654); - if (a == 0) { - if (b == 0) clr.h = 0; - else if (b > 0) clr.h = 90; - else clr.h = 270; - } - else if (a > 0) { - temp = b / a; - if (b > 0) clr.h = (r2d * atan(temp)); - else if (b == 0) clr.h = 0; - else clr.h = (r2d * atan(temp)) + 360; - } - else { - temp = b / a; - clr.h = (r2d * atan(temp)) + 180; - } - - d2r = (3.141592654 / 180.0); - e = ((12500.0 / 13.0) * pMod->Nc * pMod->Ncb) * - (cos((clr.h * d2r + 2.0)) + 3.8); - - if (clr.h < 20.14) { - temp = ((clr.h + 122.47)/1.2) + ((20.14 - clr.h)/0.8); - clr.H = 300 + (100*((clr.h + 122.47)/1.2)) / temp; - } - else if (clr.h < 90.0) { - temp = ((clr.h - 20.14)/0.8) + ((90.00 - clr.h)/0.7); - clr.H = (100*((clr.h - 20.14)/0.8)) / temp; - } - else if (clr.h < 164.25) { - temp = ((clr.h - 90.00)/0.7) + ((164.25 - clr.h)/1.0); - clr.H = 100 + ((100*((clr.h - 90.00)/0.7)) / temp); - } - else if (clr.h < 237.53) { - temp = ((clr.h - 164.25)/1.0) + ((237.53 - clr.h)/1.2); - clr.H = 200 + ((100*((clr.h - 164.25)/1.0)) / temp); - } - else { - temp = ((clr.h - 237.53)/1.2) + ((360 - clr.h + 20.14)/0.8); - clr.H = 300 + ((100*((clr.h - 237.53)/1.2)) / temp); - } - - clr.J = 100.0 * pow((clr.A / pMod->adoptedWhite.A), - (pMod->c * pMod->z)); - - clr.Q = (4.0 / pMod->c) * pow((clr.J / 100.0), 0.5) * - (pMod->adoptedWhite.A + 4.0) * pow(pMod->FL, 0.25); - - t = (e * pow(((a * a) + (b * b)), 0.5)) / - (clr.RGBpa[0] + clr.RGBpa[1] + - ((21.0 / 20.0) * clr.RGBpa[2])); - - clr.C = pow(t, 0.9) * pow((clr.J / 100.0), 0.5) * - pow((1.64 - pow(0.29, pMod->n)), 0.73); - - clr.M = clr.C * pow(pMod->FL, 0.25); - clr.s = 100.0 * pow((clr.M / clr.Q), 0.5); - - return clr; -} - - -static -CAM02COLOR InverseCorrelates(CAM02COLOR clr, cmsCIECAM02* pMod) -{ - - cmsFloat64Number t, e, p1, p2, p3, p4, p5, hr, d2r; - d2r = 3.141592654 / 180.0; - - t = pow( (clr.C / (pow((clr.J / 100.0), 0.5) * - (pow((1.64 - pow(0.29, pMod->n)), 0.73)))), - (1.0 / 0.9) ); - e = ((12500.0 / 13.0) * pMod->Nc * pMod->Ncb) * - (cos((clr.h * d2r + 2.0)) + 3.8); - - clr.A = pMod->adoptedWhite.A * pow( - (clr.J / 100.0), - (1.0 / (pMod->c * pMod->z))); - - p1 = e / t; - p2 = (clr.A / pMod->Nbb) + 0.305; - p3 = 21.0 / 20.0; - - hr = clr.h * d2r; - - if (fabs(sin(hr)) >= fabs(cos(hr))) { - p4 = p1 / sin(hr); - clr.b = (p2 * (2.0 + p3) * (460.0 / 1403.0)) / - (p4 + (2.0 + p3) * (220.0 / 1403.0) * - (cos(hr) / sin(hr)) - (27.0 / 1403.0) + - p3 * (6300.0 / 1403.0)); - clr.a = clr.b * (cos(hr) / sin(hr)); - } - else { - p5 = p1 / cos(hr); - clr.a = (p2 * (2.0 + p3) * (460.0 / 1403.0)) / - (p5 + (2.0 + p3) * (220.0 / 1403.0) - - ((27.0 / 1403.0) - p3 * (6300.0 / 1403.0)) * - (sin(hr) / cos(hr))); - clr.b = clr.a * (sin(hr) / cos(hr)); - } - - clr.RGBpa[0] = ((460.0 / 1403.0) * p2) + - ((451.0 / 1403.0) * clr.a) + - ((288.0 / 1403.0) * clr.b); - clr.RGBpa[1] = ((460.0 / 1403.0) * p2) - - ((891.0 / 1403.0) * clr.a) - - ((261.0 / 1403.0) * clr.b); - clr.RGBpa[2] = ((460.0 / 1403.0) * p2) - - ((220.0 / 1403.0) * clr.a) - - ((6300.0 / 1403.0) * clr.b); - - return clr; -} - -static -CAM02COLOR InverseNonlinearity(CAM02COLOR clr, cmsCIECAM02* pMod) -{ - cmsUInt32Number i; - cmsFloat64Number c1; - - for (i = 0; i < 3; i++) { - if ((clr.RGBpa[i] - 0.1) < 0) c1 = -1; - else c1 = 1; - clr.RGBp[i] = c1 * (100.0 / pMod->FL) * - pow(((27.13 * fabs(clr.RGBpa[i] - 0.1)) / - (400.0 - fabs(clr.RGBpa[i] - 0.1))), - (1.0 / 0.42)); - } - - return clr; -} - -static -CAM02COLOR HPEtoCAT02(CAM02COLOR clr) -{ - cmsFloat64Number M[9]; - - M[0] = (( 0.7328 * 1.910197) + (0.4296 * 0.370950)); - M[1] = (( 0.7328 * -1.112124) + (0.4296 * 0.629054)); - M[2] = (( 0.7328 * 0.201908) + (0.4296 * 0.000008) - 0.1624); - M[3] = ((-0.7036 * 1.910197) + (1.6975 * 0.370950)); - M[4] = ((-0.7036 * -1.112124) + (1.6975 * 0.629054)); - M[5] = ((-0.7036 * 0.201908) + (1.6975 * 0.000008) + 0.0061); - M[6] = (( 0.0030 * 1.910197) + (0.0136 * 0.370950)); - M[7] = (( 0.0030 * -1.112124) + (0.0136 * 0.629054)); - M[8] = (( 0.0030 * 0.201908) + (0.0136 * 0.000008) + 0.9834);; - - clr.RGBc[0] = (clr.RGBp[0] * M[0]) + (clr.RGBp[1] * M[1]) + (clr.RGBp[2] * M[2]); - clr.RGBc[1] = (clr.RGBp[0] * M[3]) + (clr.RGBp[1] * M[4]) + (clr.RGBp[2] * M[5]); - clr.RGBc[2] = (clr.RGBp[0] * M[6]) + (clr.RGBp[1] * M[7]) + (clr.RGBp[2] * M[8]); - return clr; -} - - -static -CAM02COLOR InverseChromaticAdaptation(CAM02COLOR clr, cmsCIECAM02* pMod) -{ - cmsUInt32Number i; - for (i = 0; i < 3; i++) { - clr.RGB[i] = clr.RGBc[i] / - ((pMod->adoptedWhite.XYZ[1] * pMod->D / pMod->adoptedWhite.RGB[i]) + 1.0 - pMod->D); - } - return clr; -} - - -static -CAM02COLOR CAT02toXYZ(CAM02COLOR clr) -{ - clr.XYZ[0] = (clr.RGB[0] * 1.096124) + (clr.RGB[1] * -0.278869) + (clr.RGB[2] * 0.182745); - clr.XYZ[1] = (clr.RGB[0] * 0.454369) + (clr.RGB[1] * 0.473533) + (clr.RGB[2] * 0.072098); - clr.XYZ[2] = (clr.RGB[0] * -0.009628) + (clr.RGB[1] * -0.005698) + (clr.RGB[2] * 1.015326); - - return clr; -} - - -cmsHANDLE CMSEXPORT cmsCIECAM02Init(cmsContext ContextID, const cmsViewingConditions* pVC) -{ - cmsCIECAM02* lpMod; - - _cmsAssert(pVC != NULL); - - if((lpMod = (cmsCIECAM02*) _cmsMallocZero(ContextID, sizeof(cmsCIECAM02))) == NULL) { - return NULL; - } - - lpMod ->ContextID = ContextID; - - lpMod ->adoptedWhite.XYZ[0] = pVC ->whitePoint.X; - lpMod ->adoptedWhite.XYZ[1] = pVC ->whitePoint.Y; - lpMod ->adoptedWhite.XYZ[2] = pVC ->whitePoint.Z; - - lpMod -> LA = pVC ->La; - lpMod -> Yb = pVC ->Yb; - lpMod -> D = pVC ->D_value; - lpMod -> surround = pVC ->surround; - - switch (lpMod -> surround) { - - - case CUTSHEET_SURROUND: - lpMod->F = 0.8; - lpMod->c = 0.41; - lpMod->Nc = 0.8; - break; - - case DARK_SURROUND: - lpMod -> F = 0.8; - lpMod -> c = 0.525; - lpMod -> Nc = 0.8; - break; - - case DIM_SURROUND: - lpMod -> F = 0.9; - lpMod -> c = 0.59; - lpMod -> Nc = 0.95; - break; - - default: - // Average surround - lpMod -> F = 1.0; - lpMod -> c = 0.69; - lpMod -> Nc = 1.0; - } - - lpMod -> n = compute_n(lpMod); - lpMod -> z = compute_z(lpMod); - lpMod -> Nbb = computeNbb(lpMod); - lpMod -> FL = computeFL(lpMod); - - if (lpMod -> D == D_CALCULATE) { - lpMod -> D = computeD(lpMod); - } - - lpMod -> Ncb = lpMod -> Nbb; - - lpMod -> adoptedWhite = XYZtoCAT02(lpMod -> adoptedWhite); - lpMod -> adoptedWhite = ChromaticAdaptation(lpMod -> adoptedWhite, lpMod); - lpMod -> adoptedWhite = CAT02toHPE(lpMod -> adoptedWhite); - lpMod -> adoptedWhite = NonlinearCompression(lpMod -> adoptedWhite, lpMod); - - return (cmsHANDLE) lpMod; - -} - -void CMSEXPORT cmsCIECAM02Done(cmsHANDLE hModel) -{ - cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel; - - if (lpMod) _cmsFree(lpMod ->ContextID, lpMod); -} - - -void CMSEXPORT cmsCIECAM02Forward(cmsHANDLE hModel, const cmsCIEXYZ* pIn, cmsJCh* pOut) -{ - CAM02COLOR clr; - cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel; - - _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; - - clr = XYZtoCAT02(clr); - clr = ChromaticAdaptation(clr, lpMod); - clr = CAT02toHPE(clr); - clr = NonlinearCompression(clr, lpMod); - clr = ComputeCorrelates(clr, lpMod); - - pOut ->J = clr.J; - pOut ->C = clr.C; - pOut ->h = clr.h; -} - -void CMSEXPORT cmsCIECAM02Reverse(cmsHANDLE hModel, const cmsJCh* pIn, cmsCIEXYZ* pOut) -{ - CAM02COLOR clr; - cmsCIECAM02* lpMod = (cmsCIECAM02*) hModel; - - _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; - - clr = InverseCorrelates(clr, lpMod); - clr = InverseNonlinearity(clr, lpMod); - clr = HPEtoCAT02(clr); - clr = InverseChromaticAdaptation(clr, lpMod); - clr = CAT02toXYZ(clr); - - pOut ->X = clr.XYZ[0]; - pOut ->Y = clr.XYZ[1]; - pOut ->Z = clr.XYZ[2]; -} diff --git a/third_party/lcms2-2.6/src/cmscgats.c b/third_party/lcms2-2.6/src/cmscgats.c deleted file mode 100644 index cce4cedbad..0000000000 --- a/third_party/lcms2-2.6/src/cmscgats.c +++ /dev/null @@ -1,2776 +0,0 @@ -//--------------------------------------------------------------------------------- -// -// 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" - - -// IT8.7 / CGATS.17-200x handling ----------------------------------------------------------------------------- - - -#define MAXID 128 // Max length of identifier -#define MAXSTR 1024 // Max length of string -#define MAXTABLES 255 // Max Number of tables in a single stream -#define MAXINCLUDE 20 // Max number of nested includes - -#define DEFAULT_DBL_FORMAT "%.10g" // Double formatting - -#ifdef CMS_IS_WINDOWS_ -//sunliang.liu modified 2010426 for wince error -# ifndef _WIN32_WCE -# include -# endif -# define DIR_CHAR '\\' -#else -# define DIR_CHAR '/' -#endif - - -// Symbols -typedef enum { - - SNONE, - SINUM, // Integer - SDNUM, // Real - SIDENT, // Identifier - SSTRING, // string - SCOMMENT, // comment - SEOLN, // End of line - SEOF, // End of stream - SSYNERROR, // Syntax error found on stream - - // Keywords - - SBEGIN_DATA, - SBEGIN_DATA_FORMAT, - SEND_DATA, - SEND_DATA_FORMAT, - SKEYWORD, - SDATA_FORMAT_ID, - SINCLUDE - - } SYMBOL; - - -// How to write the value -typedef enum { - - WRITE_UNCOOKED, - WRITE_STRINGIFY, - WRITE_HEXADECIMAL, - WRITE_BINARY, - WRITE_PAIR - - } WRITEMODE; - -// Linked list of variable names -typedef struct _KeyVal { - - struct _KeyVal* Next; - char* Keyword; // Name of variable - struct _KeyVal* NextSubkey; // If key is a dictionary, points to the next item - char* Subkey; // If key is a dictionary, points to the subkey name - char* Value; // Points to value - WRITEMODE WriteAs; // How to write the value - - } KEYVALUE; - - -// Linked list of memory chunks (Memory sink) -typedef struct _OwnedMem { - - struct _OwnedMem* Next; - void * Ptr; // Point to value - - } OWNEDMEM; - -// Suballocator -typedef struct _SubAllocator { - - cmsUInt8Number* Block; - cmsUInt32Number BlockSize; - cmsUInt32Number Used; - - } SUBALLOCATOR; - -// Table. Each individual table can hold properties and rows & cols -typedef struct _Table { - - char SheetType[MAXSTR]; // The first row of the IT8 (the type) - - int nSamples, nPatches; // Cols, Rows - int SampleID; // Pos of ID - - KEYVALUE* HeaderList; // The properties - - char** DataFormat; // The binary stream descriptor - char** Data; // The binary stream - - } TABLE; - -// File stream being parsed -typedef struct _FileContext { - char FileName[cmsMAX_PATH]; // File name if being readed from file - FILE* Stream; // File stream or NULL if holded in memory - } FILECTX; - -// This struct hold all information about an open IT8 handler. -typedef struct { - - - cmsUInt32Number TablesCount; // How many tables in this stream - cmsUInt32Number nTable; // The actual table - - TABLE Tab[MAXTABLES]; - - // Memory management - OWNEDMEM* MemorySink; // The storage backend - SUBALLOCATOR Allocator; // String suballocator -- just to keep it fast - - // Parser state machine - SYMBOL sy; // Current symbol - int ch; // Current character - - cmsInt32Number inum; // integer value - cmsFloat64Number dnum; // real value - - char id[MAXID]; // identifier - char str[MAXSTR]; // string - - // Allowed keywords & datasets. They have visibility on whole stream - KEYVALUE* ValidKeywords; - KEYVALUE* ValidSampleID; - - char* Source; // Points to loc. being parsed - cmsInt32Number lineno; // line counter for error reporting - - FILECTX* FileStack[MAXINCLUDE]; // Stack of files being parsed - cmsInt32Number IncludeSP; // Include Stack Pointer - - char* MemoryBlock; // The stream if holded in memory - - char DoubleFormatter[MAXID];// Printf-like 'cmsFloat64Number' formatter - - cmsContext ContextID; // The threading context - - } cmsIT8; - - -// The stream for save operations -typedef struct { - - FILE* stream; // For save-to-file behaviour - - cmsUInt8Number* Base; - cmsUInt8Number* Ptr; // For save-to-mem behaviour - cmsUInt32Number Used; - cmsUInt32Number Max; - - } SAVESTREAM; - - -// ------------------------------------------------------ cmsIT8 parsing routines - - -// A keyword -typedef struct { - - const char *id; - SYMBOL sy; - - } KEYWORD; - -// The keyword->symbol translation table. Sorting is required. -static const KEYWORD TabKeys[] = { - - {"$INCLUDE", SINCLUDE}, // This is an extension! - {".INCLUDE", SINCLUDE}, // This is an extension! - - {"BEGIN_DATA", SBEGIN_DATA }, - {"BEGIN_DATA_FORMAT", SBEGIN_DATA_FORMAT }, - {"DATA_FORMAT_IDENTIFIER", SDATA_FORMAT_ID}, - {"END_DATA", SEND_DATA}, - {"END_DATA_FORMAT", SEND_DATA_FORMAT}, - {"KEYWORD", SKEYWORD} - }; - -#define NUMKEYS (sizeof(TabKeys)/sizeof(KEYWORD)) - -// Predefined properties - -// A property -typedef struct { - const char *id; // The identifier - WRITEMODE as; // How is supposed to be written - } PROPERTY; - -static PROPERTY PredefinedProperties[] = { - - {"NUMBER_OF_FIELDS", WRITE_UNCOOKED}, // Required - NUMBER OF FIELDS - {"NUMBER_OF_SETS", WRITE_UNCOOKED}, // Required - NUMBER OF SETS - {"ORIGINATOR", WRITE_STRINGIFY}, // Required - Identifies the specific system, organization or individual that created the data file. - {"FILE_DESCRIPTOR", WRITE_STRINGIFY}, // Required - Describes the purpose or contents of the data file. - {"CREATED", WRITE_STRINGIFY}, // Required - Indicates date of creation of the data file. - {"DESCRIPTOR", WRITE_STRINGIFY}, // Required - Describes the purpose or contents of the data file. - {"DIFFUSE_GEOMETRY", WRITE_STRINGIFY}, // The diffuse geometry used. Allowed values are "sphere" or "opal". - {"MANUFACTURER", WRITE_STRINGIFY}, - {"MANUFACTURE", WRITE_STRINGIFY}, // Some broken Fuji targets does store this value - {"PROD_DATE", WRITE_STRINGIFY}, // Identifies year and month of production of the target in the form yyyy:mm. - {"SERIAL", WRITE_STRINGIFY}, // Uniquely identifies individual physical target. - - {"MATERIAL", WRITE_STRINGIFY}, // Identifies the material on which the target was produced using a code - // uniquely identifying th e material. This is intend ed to be used for IT8.7 - // physical targets only (i.e . IT8.7/1 a nd IT8.7/2). - - {"INSTRUMENTATION", WRITE_STRINGIFY}, // Used to report the specific instrumentation used (manufacturer and - // model number) to generate the data reported. This data will often - // provide more information about the particular data collected than an - // extensive list of specific details. This is particularly important for - // spectral data or data derived from spectrophotometry. - - {"MEASUREMENT_SOURCE", WRITE_STRINGIFY}, // Illumination used for spectral measurements. This data helps provide - // a guide to the potential for issues of paper fluorescence, etc. - - {"PRINT_CONDITIONS", WRITE_STRINGIFY}, // Used to define the characteristics of the printed sheet being reported. - // Where standard conditions have been defined (e.g., SWOP at nominal) - // named conditions may suffice. Otherwise, detailed information is - // needed. - - {"SAMPLE_BACKING", WRITE_STRINGIFY}, // Identifies the backing material used behind the sample during - // measurement. Allowed values are "black" "white" or "na". - - {"CHISQ_DOF", WRITE_STRINGIFY}, // Degrees of freedom associated with the Chi squared statistic - - // below properties are new in recent specs: - - {"MEASUREMENT_GEOMETRY", WRITE_STRINGIFY}, // The type of measurement, either reflection or transmission, should be indicated - // along with details of the geometry and the aperture size and shape. For example, - // for transmission measurements it is important to identify 0/diffuse, diffuse/0, - // opal or integrating sphere, etc. For reflection it is important to identify 0/45, - // 45/0, sphere (specular included or excluded), etc. - - {"FILTER", WRITE_STRINGIFY}, // Identifies the use of physical filter(s) during measurement. Typically used to - // denote the use of filters such as none, D65, Red, Green or Blue. - - {"POLARIZATION", WRITE_STRINGIFY}, // Identifies the use of a physical polarization filter during measurement. Allowed - // values are "yes" "white" "none" or "na" - - {"WEIGHTING_FUNCTION", WRITE_PAIR}, // Indicates such functions as: the CIE standard observer functions used in the - // calculation of various data parameters (2 degree and 10 degree), CIE standard - // illuminant functions used in the calculation of various data parameters (e.g., D50, - // D65, etc.), density status response, etc. If used there shall be at least one - // name-value pair following the WEIGHTING_FUNCTION tag/keyword. The first attribute - // in the set shall be {"name" and shall identify the particular parameter used. - // The second shall be {"value" and shall provide the value associated with that name. - // For ASCII data, a string containing the Name and Value attribute pairs shall follow - // the weighting function keyword. A semi-colon separates attribute pairs from each - // other and within the attribute the name and value are separated by a comma. - - {"COMPUTATIONAL_PARAMETER", WRITE_PAIR}, // Parameter that is used in computing a value from measured data. Name is the name - // of the calculation, parameter is the name of the parameter used in the calculation - // and value is the value of the parameter. - - {"TARGET_TYPE", WRITE_STRINGIFY}, // The type of target being measured, e.g. IT8.7/1, IT8.7/3, user defined, etc. - - {"COLORANT", WRITE_STRINGIFY}, // Identifies the colorant(s) used in creating the target. - - {"TABLE_DESCRIPTOR", WRITE_STRINGIFY}, // Describes the purpose or contents of a data table. - - {"TABLE_NAME", WRITE_STRINGIFY} // Provides a short name for a data table. -}; - -#define NUMPREDEFINEDPROPS (sizeof(PredefinedProperties)/sizeof(PROPERTY)) - - -// Predefined sample types on dataset -static const char* PredefinedSampleID[] = { - "SAMPLE_ID", // Identifies sample that data represents - "STRING", // Identifies label, or other non-machine readable value. - // Value must begin and end with a " symbol - - "CMYK_C", // Cyan component of CMYK data expressed as a percentage - "CMYK_M", // Magenta component of CMYK data expressed as a percentage - "CMYK_Y", // Yellow component of CMYK data expressed as a percentage - "CMYK_K", // Black component of CMYK data expressed as a percentage - "D_RED", // Red filter density - "D_GREEN", // Green filter density - "D_BLUE", // Blue filter density - "D_VIS", // Visual filter density - "D_MAJOR_FILTER", // Major filter d ensity - "RGB_R", // Red component of RGB data - "RGB_G", // Green component of RGB data - "RGB_B", // Blue com ponent of RGB data - "SPECTRAL_NM", // Wavelength of measurement expressed in nanometers - "SPECTRAL_PCT", // Percentage reflectance/transmittance - "SPECTRAL_DEC", // Reflectance/transmittance - "XYZ_X", // X component of tristimulus data - "XYZ_Y", // Y component of tristimulus data - "XYZ_Z", // Z component of tristimulus data - "XYY_X" // x component of chromaticity data - "XYY_Y", // y component of chromaticity data - "XYY_CAPY", // Y component of tristimulus data - "LAB_L", // L* component of Lab data - "LAB_A", // a* component of Lab data - "LAB_B", // b* component of Lab data - "LAB_C", // C*ab component of Lab data - "LAB_H", // hab component of Lab data - "LAB_DE", // CIE dE - "LAB_DE_94", // CIE dE using CIE 94 - "LAB_DE_CMC", // dE using CMC - "LAB_DE_2000", // CIE dE using CIE DE 2000 - "MEAN_DE", // Mean Delta E (LAB_DE) of samples compared to batch average - // (Used for data files for ANSI IT8.7/1 and IT8.7/2 targets) - "STDEV_X", // Standard deviation of X (tristimulus data) - "STDEV_Y", // Standard deviation of Y (tristimulus data) - "STDEV_Z", // Standard deviation of Z (tristimulus data) - "STDEV_L", // Standard deviation of L* - "STDEV_A", // Standard deviation of a* - "STDEV_B", // Standard deviation of b* - "STDEV_DE", // Standard deviation of CIE dE - "CHI_SQD_PAR"}; // The average of the standard deviations of L*, a* and b*. It is - // used to derive an estimate of the chi-squared parameter which is - // recommended as the predictor of the variability of dE - -#define NUMPREDEFINEDSAMPLEID (sizeof(PredefinedSampleID)/sizeof(char *)) - -//Forward declaration of some internal functions -static void* AllocChunk(cmsIT8* it8, cmsUInt32Number size); - -// Checks whatever c is a separator -static -cmsBool isseparator(int c) -{ - return (c == ' ') || (c == '\t') ; -} - -// Checks whatever c is a valid identifier char -static -cmsBool ismiddle(int c) -{ - return (!isseparator(c) && (c != '#') && (c !='\"') && (c != '\'') && (c > 32) && (c < 127)); -} - -// Checks whatsever c is a valid identifier middle char. -static -cmsBool isidchar(int c) -{ - return isalnum(c) || ismiddle(c); -} - -// Checks whatsever c is a valid identifier first char. -static -cmsBool isfirstidchar(int c) -{ - return !isdigit(c) && ismiddle(c); -} - -// Guess whether the supplied path looks like an absolute path -static -cmsBool isabsolutepath(const char *path) -{ - char ThreeChars[4]; - - if(path == NULL) - return FALSE; - if (path[0] == 0) - return FALSE; - - strncpy(ThreeChars, path, 3); - ThreeChars[3] = 0; - - if(ThreeChars[0] == DIR_CHAR) - return TRUE; - -#ifdef CMS_IS_WINDOWS_ - if (isalpha((int) ThreeChars[0]) && ThreeChars[1] == ':') - return TRUE; -#endif - return FALSE; -} - - -// Makes a file path based on a given reference path -// NOTE: this function doesn't check if the path exists or even if it's legal -static -cmsBool BuildAbsolutePath(const char *relPath, const char *basePath, char *buffer, cmsUInt32Number MaxLen) -{ - char *tail; - cmsUInt32Number len; - - // Already absolute? - if (isabsolutepath(relPath)) { - - strncpy(buffer, relPath, MaxLen); - buffer[MaxLen-1] = 0; - return TRUE; - } - - // No, search for last - strncpy(buffer, basePath, MaxLen); - buffer[MaxLen-1] = 0; - - tail = strrchr(buffer, DIR_CHAR); - if (tail == NULL) return FALSE; // Is not absolute and has no separators?? - - len = (cmsUInt32Number) (tail - buffer); - if (len >= MaxLen) return FALSE; - - // No need to assure zero terminator over here - strncpy(tail + 1, relPath, MaxLen - len); - - return TRUE; -} - - -// Make sure no exploit is being even tried -static -const char* NoMeta(const char* str) -{ - if (strchr(str, '%') != NULL) - return "**** CORRUPTED FORMAT STRING ***"; - - return str; -} - -// Syntax error -static -cmsBool SynError(cmsIT8* it8, const char *Txt, ...) -{ - char Buffer[256], ErrMsg[1024]; - va_list args; - - va_start(args, Txt); - vsnprintf(Buffer, 255, Txt, args); - Buffer[255] = 0; - va_end(args); - - snprintf(ErrMsg, 1023, "%s: Line %d, %s", it8->FileStack[it8 ->IncludeSP]->FileName, it8->lineno, Buffer); - ErrMsg[1023] = 0; - it8->sy = SSYNERROR; - cmsSignalError(it8 ->ContextID, cmsERROR_CORRUPTION_DETECTED, "%s", ErrMsg); - return FALSE; -} - -// Check if current symbol is same as specified. issue an error else. -static -cmsBool Check(cmsIT8* it8, SYMBOL sy, const char* Err) -{ - if (it8 -> sy != sy) - return SynError(it8, NoMeta(Err)); - return TRUE; -} - -// Read Next character from stream -static -void NextCh(cmsIT8* it8) -{ - if (it8 -> FileStack[it8 ->IncludeSP]->Stream) { - - it8 ->ch = fgetc(it8 ->FileStack[it8 ->IncludeSP]->Stream); - - if (feof(it8 -> FileStack[it8 ->IncludeSP]->Stream)) { - - if (it8 ->IncludeSP > 0) { - - fclose(it8 ->FileStack[it8->IncludeSP--]->Stream); - it8 -> ch = ' '; // Whitespace to be ignored - - } else - it8 ->ch = 0; // EOF - } - } - else { - it8->ch = *it8->Source; - if (it8->ch) it8->Source++; - } -} - - -// Try to see if current identifier is a keyword, if so return the referred symbol -static -SYMBOL BinSrchKey(const char *id) -{ - int l = 1; - int r = NUMKEYS; - int x, res; - - while (r >= l) - { - x = (l+r)/2; - res = cmsstrcasecmp(id, TabKeys[x-1].id); - if (res == 0) return TabKeys[x-1].sy; - if (res < 0) r = x - 1; - else l = x + 1; - } - - return SNONE; -} - - -// 10 ^n -static -cmsFloat64Number xpow10(int n) -{ - return pow(10, (cmsFloat64Number) n); -} - - -// Reads a Real number, tries to follow from integer number -static -void ReadReal(cmsIT8* it8, int inum) -{ - it8->dnum = (cmsFloat64Number) inum; - - while (isdigit(it8->ch)) { - - it8->dnum = it8->dnum * 10.0 + (it8->ch - '0'); - NextCh(it8); - } - - if (it8->ch == '.') { // Decimal point - - cmsFloat64Number frac = 0.0; // fraction - int prec = 0; // precision - - NextCh(it8); // Eats dec. point - - while (isdigit(it8->ch)) { - - frac = frac * 10.0 + (it8->ch - '0'); - prec++; - NextCh(it8); - } - - it8->dnum = it8->dnum + (frac / xpow10(prec)); - } - - // Exponent, example 34.00E+20 - if (toupper(it8->ch) == 'E') { - - cmsInt32Number e; - cmsInt32Number sgn; - - NextCh(it8); sgn = 1; - - if (it8->ch == '-') { - - sgn = -1; NextCh(it8); - } - else - if (it8->ch == '+') { - - sgn = +1; - NextCh(it8); - } - - e = 0; - while (isdigit(it8->ch)) { - - if ((cmsFloat64Number) e * 10L < (cmsFloat64Number) +2147483647.0) - e = e * 10 + (it8->ch - '0'); - - NextCh(it8); - } - - e = sgn*e; - it8 -> dnum = it8 -> dnum * xpow10(e); - } -} - -// Parses a float number -// This can not call directly atof because it uses locale dependant -// parsing, while CCMX files always use . as decimal separator -static -cmsFloat64Number ParseFloatNumber(const char *Buffer) -{ - cmsFloat64Number dnum = 0.0; - int sign = 1; - - // keep safe - if (Buffer == NULL) return 0.0; - - if (*Buffer == '-' || *Buffer == '+') { - - sign = (*Buffer == '-') ? -1 : 1; - Buffer++; - } - - - while (*Buffer && isdigit((int) *Buffer)) { - - dnum = dnum * 10.0 + (*Buffer - '0'); - if (*Buffer) Buffer++; - } - - if (*Buffer == '.') { - - cmsFloat64Number frac = 0.0; // fraction - int prec = 0; // precission - - if (*Buffer) Buffer++; - - while (*Buffer && isdigit((int) *Buffer)) { - - frac = frac * 10.0 + (*Buffer - '0'); - prec++; - if (*Buffer) Buffer++; - } - - dnum = dnum + (frac / xpow10(prec)); - } - - // Exponent, example 34.00E+20 - if (*Buffer && toupper(*Buffer) == 'E') { - - int e; - int sgn; - - if (*Buffer) Buffer++; - sgn = 1; - - if (*Buffer == '-') { - - sgn = -1; - if (*Buffer) Buffer++; - } - else - if (*Buffer == '+') { - - sgn = +1; - if (*Buffer) Buffer++; - } - - e = 0; - while (*Buffer && isdigit((int) *Buffer)) { - - if ((cmsFloat64Number) e * 10L < INT_MAX) - e = e * 10 + (*Buffer - '0'); - - if (*Buffer) Buffer++; - } - - e = sgn*e; - dnum = dnum * xpow10(e); - } - - return sign * dnum; -} - - -// Reads next symbol -static -void InSymbol(cmsIT8* it8) -{ - register char *idptr; - register int k; - SYMBOL key; - int sng; - - do { - - while (isseparator(it8->ch)) - NextCh(it8); - - if (isfirstidchar(it8->ch)) { // Identifier - - k = 0; - idptr = it8->id; - - do { - - if (++k < MAXID) *idptr++ = (char) it8->ch; - - NextCh(it8); - - } while (isidchar(it8->ch)); - - *idptr = '\0'; - - - key = BinSrchKey(it8->id); - if (key == SNONE) it8->sy = SIDENT; - else it8->sy = key; - - } - else // Is a number? - if (isdigit(it8->ch) || it8->ch == '.' || it8->ch == '-' || it8->ch == '+') - { - int sign = 1; - - if (it8->ch == '-') { - sign = -1; - NextCh(it8); - } - - it8->inum = 0; - it8->sy = SINUM; - - if (it8->ch == '0') { // 0xnnnn (Hexa) or 0bnnnn (Binary) - - NextCh(it8); - if (toupper(it8->ch) == 'X') { - - int j; - - NextCh(it8); - while (isxdigit(it8->ch)) - { - it8->ch = toupper(it8->ch); - if (it8->ch >= 'A' && it8->ch <= 'F') j = it8->ch -'A'+10; - else j = it8->ch - '0'; - - if ((long) it8->inum * 16L > (long) INT_MAX) - { - SynError(it8, "Invalid hexadecimal number"); - return; - } - - it8->inum = it8->inum * 16 + j; - NextCh(it8); - } - return; - } - - if (toupper(it8->ch) == 'B') { // Binary - - int j; - - NextCh(it8); - while (it8->ch == '0' || it8->ch == '1') - { - j = it8->ch - '0'; - - if ((long) it8->inum * 2L > (long) INT_MAX) - { - SynError(it8, "Invalid binary number"); - return; - } - - it8->inum = it8->inum * 2 + j; - NextCh(it8); - } - return; - } - } - - - while (isdigit(it8->ch)) { - - if ((cmsFloat64Number) it8->inum * 10L > (cmsFloat64Number) +2147483647.0) { - ReadReal(it8, it8->inum); - it8->sy = SDNUM; - it8->dnum *= sign; - return; - } - - it8->inum = it8->inum * 10 + (it8->ch - '0'); - NextCh(it8); - } - - if (it8->ch == '.') { - - ReadReal(it8, it8->inum); - it8->sy = SDNUM; - it8->dnum *= sign; - return; - } - - it8 -> inum *= sign; - - // Special case. Numbers followed by letters are taken as identifiers - - if (isidchar(it8 ->ch)) { - - if (it8 ->sy == SINUM) { - - sprintf(it8->id, "%d", it8->inum); - } - else { - - sprintf(it8->id, it8 ->DoubleFormatter, it8->dnum); - } - - k = (int) strlen(it8 ->id); - idptr = it8 ->id + k; - do { - - if (++k < MAXID) *idptr++ = (char) it8->ch; - - NextCh(it8); - - } while (isidchar(it8->ch)); - - *idptr = '\0'; - it8->sy = SIDENT; - } - return; - - } - else - switch ((int) it8->ch) { - - // EOF marker -- ignore it - case '\x1a': - NextCh(it8); - break; - - // Eof stream markers - case 0: - case -1: - it8->sy = SEOF; - break; - - - // Next line - case '\r': - NextCh(it8); - if (it8 ->ch == '\n') - NextCh(it8); - it8->sy = SEOLN; - it8->lineno++; - break; - - case '\n': - NextCh(it8); - it8->sy = SEOLN; - it8->lineno++; - break; - - // Comment - case '#': - NextCh(it8); - while (it8->ch && it8->ch != '\n' && it8->ch != '\r') - NextCh(it8); - - it8->sy = SCOMMENT; - break; - - // String. - case '\'': - case '\"': - idptr = it8->str; - sng = it8->ch; - k = 0; - NextCh(it8); - - while (k < MAXSTR && it8->ch != sng) { - - if (it8->ch == '\n'|| it8->ch == '\r') k = MAXSTR+1; - else { - *idptr++ = (char) it8->ch; - NextCh(it8); - k++; - } - } - - it8->sy = SSTRING; - *idptr = '\0'; - NextCh(it8); - break; - - - default: - SynError(it8, "Unrecognized character: 0x%x", it8 ->ch); - return; - } - - } while (it8->sy == SCOMMENT); - - // Handle the include special token - - if (it8 -> sy == SINCLUDE) { - - FILECTX* FileNest; - - if(it8 -> IncludeSP >= (MAXINCLUDE-1)) { - - SynError(it8, "Too many recursion levels"); - return; - } - - InSymbol(it8); - if (!Check(it8, SSTRING, "Filename expected")) return; - - FileNest = it8 -> FileStack[it8 -> IncludeSP + 1]; - if(FileNest == NULL) { - - FileNest = it8 ->FileStack[it8 -> IncludeSP + 1] = (FILECTX*)AllocChunk(it8, sizeof(FILECTX)); - //if(FileNest == NULL) - // TODO: how to manage out-of-memory conditions? - } - - if (BuildAbsolutePath(it8->str, - it8->FileStack[it8->IncludeSP]->FileName, - FileNest->FileName, cmsMAX_PATH-1) == FALSE) { - SynError(it8, "File path too long"); - return; - } - - FileNest->Stream = fopen(FileNest->FileName, "rt"); - if (FileNest->Stream == NULL) { - - SynError(it8, "File %s not found", FileNest->FileName); - return; - } - it8->IncludeSP++; - - it8 ->ch = ' '; - InSymbol(it8); - } - -} - -// Checks end of line separator -static -cmsBool CheckEOLN(cmsIT8* it8) -{ - if (!Check(it8, SEOLN, "Expected separator")) return FALSE; - while (it8 -> sy == SEOLN) - InSymbol(it8); - return TRUE; - -} - -// Skip a symbol - -static -void Skip(cmsIT8* it8, SYMBOL sy) -{ - if (it8->sy == sy && it8->sy != SEOF) - InSymbol(it8); -} - - -// Skip multiple EOLN -static -void SkipEOLN(cmsIT8* it8) -{ - while (it8->sy == SEOLN) { - InSymbol(it8); - } -} - - -// Returns a string holding current value -static -cmsBool GetVal(cmsIT8* it8, char* Buffer, cmsUInt32Number max, const char* ErrorTitle) -{ - switch (it8->sy) { - - case SEOLN: // Empty value - Buffer[0]=0; - break; - case SIDENT: strncpy(Buffer, it8->id, max); - Buffer[max-1]=0; - break; - case SINUM: snprintf(Buffer, max, "%d", it8 -> inum); break; - case SDNUM: snprintf(Buffer, max, it8->DoubleFormatter, it8 -> dnum); break; - case SSTRING: strncpy(Buffer, it8->str, max); - Buffer[max-1] = 0; - break; - - - default: - return SynError(it8, "%s", ErrorTitle); - } - - Buffer[max] = 0; - return TRUE; -} - -// ---------------------------------------------------------- Table - -static -TABLE* GetTable(cmsIT8* it8) -{ - if ((it8 -> nTable >= it8 ->TablesCount)) { - - SynError(it8, "Table %d out of sequence", it8 -> nTable); - return it8 -> Tab; - } - - return it8 ->Tab + it8 ->nTable; -} - -// ---------------------------------------------------------- Memory management - - -// Frees an allocator and owned memory -void CMSEXPORT cmsIT8Free(cmsHANDLE hIT8) -{ - cmsIT8* it8 = (cmsIT8*) hIT8; - - if (it8 == NULL) - return; - - if (it8->MemorySink) { - - OWNEDMEM* p; - OWNEDMEM* n; - - for (p = it8->MemorySink; p != NULL; p = n) { - - n = p->Next; - if (p->Ptr) _cmsFree(it8 ->ContextID, p->Ptr); - _cmsFree(it8 ->ContextID, p); - } - } - - if (it8->MemoryBlock) - _cmsFree(it8 ->ContextID, it8->MemoryBlock); - - _cmsFree(it8 ->ContextID, it8); -} - - -// Allocates a chunk of data, keep linked list -static -void* AllocBigBlock(cmsIT8* it8, cmsUInt32Number size) -{ - OWNEDMEM* ptr1; - void* ptr = _cmsMallocZero(it8->ContextID, size); - - if (ptr != NULL) { - - ptr1 = (OWNEDMEM*) _cmsMallocZero(it8 ->ContextID, sizeof(OWNEDMEM)); - - if (ptr1 == NULL) { - - _cmsFree(it8 ->ContextID, ptr); - return NULL; - } - - ptr1-> Ptr = ptr; - ptr1-> Next = it8 -> MemorySink; - it8 -> MemorySink = ptr1; - } - - return ptr; -} - - -// Suballocator. -static -void* AllocChunk(cmsIT8* it8, cmsUInt32Number size) -{ - cmsUInt32Number Free = it8 ->Allocator.BlockSize - it8 ->Allocator.Used; - cmsUInt8Number* ptr; - - size = _cmsALIGNMEM(size); - - if (size > Free) { - - if (it8 -> Allocator.BlockSize == 0) - - it8 -> Allocator.BlockSize = 20*1024; - else - it8 ->Allocator.BlockSize *= 2; - - if (it8 ->Allocator.BlockSize < size) - it8 ->Allocator.BlockSize = size; - - it8 ->Allocator.Used = 0; - it8 ->Allocator.Block = (cmsUInt8Number*) AllocBigBlock(it8, it8 ->Allocator.BlockSize); - } - - ptr = it8 ->Allocator.Block + it8 ->Allocator.Used; - it8 ->Allocator.Used += size; - - return (void*) ptr; - -} - - -// Allocates a string -static -char *AllocString(cmsIT8* it8, const char* str) -{ - cmsUInt32Number Size = (cmsUInt32Number) strlen(str)+1; - char *ptr; - - - ptr = (char *) AllocChunk(it8, Size); - if (ptr) strncpy (ptr, str, Size-1); - - return ptr; -} - -// Searches through linked list - -static -cmsBool IsAvailableOnList(KEYVALUE* p, const char* Key, const char* Subkey, KEYVALUE** LastPtr) -{ - if (LastPtr) *LastPtr = p; - - for (; p != NULL; p = p->Next) { - - if (LastPtr) *LastPtr = p; - - if (*Key != '#') { // Comments are ignored - - if (cmsstrcasecmp(Key, p->Keyword) == 0) - break; - } - } - - if (p == NULL) - return FALSE; - - if (Subkey == 0) - return TRUE; - - for (; p != NULL; p = p->NextSubkey) { - - if (p ->Subkey == NULL) continue; - - if (LastPtr) *LastPtr = p; - - if (cmsstrcasecmp(Subkey, p->Subkey) == 0) - return TRUE; - } - - return FALSE; -} - - - -// Add a property into a linked list -static -KEYVALUE* AddToList(cmsIT8* it8, KEYVALUE** Head, const char *Key, const char *Subkey, const char* xValue, WRITEMODE WriteAs) -{ - KEYVALUE* p; - KEYVALUE* last; - - - // Check if property is already in list - - if (IsAvailableOnList(*Head, Key, Subkey, &p)) { - - // This may work for editing properties - - // return SynError(it8, "duplicate key <%s>", Key); - } - else { - - last = p; - - // Allocate the container - p = (KEYVALUE*) AllocChunk(it8, sizeof(KEYVALUE)); - if (p == NULL) - { - SynError(it8, "AddToList: out of memory"); - return NULL; - } - - // Store name and value - p->Keyword = AllocString(it8, Key); - p->Subkey = (Subkey == NULL) ? NULL : AllocString(it8, Subkey); - - // Keep the container in our list - if (*Head == NULL) { - *Head = p; - } - else - { - if (Subkey != NULL && last != NULL) { - - last->NextSubkey = p; - - // If Subkey is not null, then last is the last property with the same key, - // but not necessarily is the last property in the list, so we need to move - // to the actual list end - while (last->Next != NULL) - last = last->Next; - } - - if (last != NULL) last->Next = p; - } - - p->Next = NULL; - p->NextSubkey = NULL; - } - - p->WriteAs = WriteAs; - - if (xValue != NULL) { - - p->Value = AllocString(it8, xValue); - } - else { - p->Value = NULL; - } - - return p; -} - -static -KEYVALUE* AddAvailableProperty(cmsIT8* it8, const char* Key, WRITEMODE as) -{ - return AddToList(it8, &it8->ValidKeywords, Key, NULL, NULL, as); -} - - -static -KEYVALUE* AddAvailableSampleID(cmsIT8* it8, const char* Key) -{ - return AddToList(it8, &it8->ValidSampleID, Key, NULL, NULL, WRITE_UNCOOKED); -} - - -static -void AllocTable(cmsIT8* it8) -{ - TABLE* t; - - t = it8 ->Tab + it8 ->TablesCount; - - t->HeaderList = NULL; - t->DataFormat = NULL; - t->Data = NULL; - - it8 ->TablesCount++; -} - - -cmsInt32Number CMSEXPORT cmsIT8SetTable(cmsHANDLE IT8, cmsUInt32Number nTable) -{ - cmsIT8* it8 = (cmsIT8*) IT8; - - if (nTable >= it8 ->TablesCount) { - - if (nTable == it8 ->TablesCount) { - - AllocTable(it8); - } - else { - SynError(it8, "Table %d is out of sequence", nTable); - return -1; - } - } - - it8 ->nTable = nTable; - - return (cmsInt32Number) nTable; -} - - - -// Init an empty container -cmsHANDLE CMSEXPORT cmsIT8Alloc(cmsContext ContextID) -{ - cmsIT8* it8; - cmsUInt32Number i; - - it8 = (cmsIT8*) _cmsMallocZero(ContextID, sizeof(cmsIT8)); - if (it8 == NULL) return NULL; - - AllocTable(it8); - - it8->MemoryBlock = NULL; - it8->MemorySink = NULL; - - it8 ->nTable = 0; - - it8->ContextID = ContextID; - it8->Allocator.Used = 0; - it8->Allocator.Block = NULL; - it8->Allocator.BlockSize = 0; - - it8->ValidKeywords = NULL; - it8->ValidSampleID = NULL; - - it8 -> sy = SNONE; - it8 -> ch = ' '; - it8 -> Source = NULL; - it8 -> inum = 0; - it8 -> dnum = 0.0; - - it8->FileStack[0] = (FILECTX*)AllocChunk(it8, sizeof(FILECTX)); - it8->IncludeSP = 0; - it8 -> lineno = 1; - - strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT); - cmsIT8SetSheetType((cmsHANDLE) it8, "CGATS.17"); - - // Initialize predefined properties & data - - for (i=0; i < NUMPREDEFINEDPROPS; i++) - AddAvailableProperty(it8, PredefinedProperties[i].id, PredefinedProperties[i].as); - - for (i=0; i < NUMPREDEFINEDSAMPLEID; i++) - AddAvailableSampleID(it8, PredefinedSampleID[i]); - - - return (cmsHANDLE) it8; -} - - -const char* CMSEXPORT cmsIT8GetSheetType(cmsHANDLE hIT8) -{ - return GetTable((cmsIT8*) hIT8)->SheetType; -} - -cmsBool CMSEXPORT cmsIT8SetSheetType(cmsHANDLE hIT8, const char* Type) -{ - TABLE* t = GetTable((cmsIT8*) hIT8); - - strncpy(t ->SheetType, Type, MAXSTR-1); - t ->SheetType[MAXSTR-1] = 0; - return TRUE; -} - -cmsBool CMSEXPORT cmsIT8SetComment(cmsHANDLE hIT8, const char* Val) -{ - cmsIT8* it8 = (cmsIT8*) hIT8; - - if (!Val) return FALSE; - if (!*Val) return FALSE; - - return AddToList(it8, &GetTable(it8)->HeaderList, "# ", NULL, Val, WRITE_UNCOOKED) != NULL; -} - -// Sets a property -cmsBool CMSEXPORT cmsIT8SetPropertyStr(cmsHANDLE hIT8, const char* Key, const char *Val) -{ - cmsIT8* it8 = (cmsIT8*) hIT8; - - if (!Val) return FALSE; - if (!*Val) return FALSE; - - return AddToList(it8, &GetTable(it8)->HeaderList, Key, NULL, Val, WRITE_STRINGIFY) != NULL; -} - -cmsBool CMSEXPORT cmsIT8SetPropertyDbl(cmsHANDLE hIT8, const char* cProp, cmsFloat64Number Val) -{ - cmsIT8* it8 = (cmsIT8*) hIT8; - char Buffer[1024]; - - sprintf(Buffer, it8->DoubleFormatter, Val); - - return AddToList(it8, &GetTable(it8)->HeaderList, cProp, NULL, Buffer, WRITE_UNCOOKED) != NULL; -} - -cmsBool CMSEXPORT cmsIT8SetPropertyHex(cmsHANDLE hIT8, const char* cProp, cmsUInt32Number Val) -{ - cmsIT8* it8 = (cmsIT8*) hIT8; - char Buffer[1024]; - - sprintf(Buffer, "%u", Val); - - return AddToList(it8, &GetTable(it8)->HeaderList, cProp, NULL, Buffer, WRITE_HEXADECIMAL) != NULL; -} - -cmsBool CMSEXPORT cmsIT8SetPropertyUncooked(cmsHANDLE hIT8, const char* Key, const char* Buffer) -{ - cmsIT8* it8 = (cmsIT8*) hIT8; - - return AddToList(it8, &GetTable(it8)->HeaderList, Key, NULL, Buffer, WRITE_UNCOOKED) != NULL; -} - -cmsBool CMSEXPORT cmsIT8SetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char* SubKey, const char *Buffer) -{ - cmsIT8* it8 = (cmsIT8*) hIT8; - - return AddToList(it8, &GetTable(it8)->HeaderList, Key, SubKey, Buffer, WRITE_PAIR) != NULL; -} - -// Gets a property -const char* CMSEXPORT cmsIT8GetProperty(cmsHANDLE hIT8, const char* Key) -{ - cmsIT8* it8 = (cmsIT8*) hIT8; - KEYVALUE* p; - - if (IsAvailableOnList(GetTable(it8) -> HeaderList, Key, NULL, &p)) - { - return p -> Value; - } - return NULL; -} - - -cmsFloat64Number CMSEXPORT cmsIT8GetPropertyDbl(cmsHANDLE hIT8, const char* cProp) -{ - const char *v = cmsIT8GetProperty(hIT8, cProp); - - if (v == NULL) return 0.0; - - return ParseFloatNumber(v); -} - -const char* CMSEXPORT cmsIT8GetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char *SubKey) -{ - cmsIT8* it8 = (cmsIT8*) hIT8; - KEYVALUE* p; - - if (IsAvailableOnList(GetTable(it8) -> HeaderList, Key, SubKey, &p)) { - return p -> Value; - } - return NULL; -} - -// ----------------------------------------------------------------- Datasets - - -static -void AllocateDataFormat(cmsIT8* it8) -{ - TABLE* t = GetTable(it8); - - if (t -> DataFormat) return; // Already allocated - - t -> nSamples = (int) cmsIT8GetPropertyDbl(it8, "NUMBER_OF_FIELDS"); - - if (t -> nSamples <= 0) { - - SynError(it8, "AllocateDataFormat: Unknown NUMBER_OF_FIELDS"); - t -> nSamples = 10; - } - - t -> DataFormat = (char**) AllocChunk (it8, ((cmsUInt32Number) t->nSamples + 1) * sizeof(char *)); - if (t->DataFormat == NULL) { - - SynError(it8, "AllocateDataFormat: Unable to allocate dataFormat array"); - } - -} - -static -const char *GetDataFormat(cmsIT8* it8, int n) -{ - TABLE* t = GetTable(it8); - - if (t->DataFormat) - return t->DataFormat[n]; - - return NULL; -} - -static -cmsBool SetDataFormat(cmsIT8* it8, int n, const char *label) -{ - TABLE* t = GetTable(it8); - - if (!t->DataFormat) - AllocateDataFormat(it8); - - if (n > t -> nSamples) { - SynError(it8, "More than NUMBER_OF_FIELDS fields."); - return FALSE; - } - - if (t->DataFormat) { - t->DataFormat[n] = AllocString(it8, label); - } - - return TRUE; -} - - -cmsBool CMSEXPORT cmsIT8SetDataFormat(cmsHANDLE h, int n, const char *Sample) -{ - cmsIT8* it8 = (cmsIT8*) h; - return SetDataFormat(it8, n, Sample); -} - -static -void AllocateDataSet(cmsIT8* it8) -{ - TABLE* t = GetTable(it8); - - if (t -> Data) return; // Already allocated - - t-> nSamples = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS")); - t-> nPatches = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS")); - - t-> Data = (char**)AllocChunk (it8, ((cmsUInt32Number) t->nSamples + 1) * ((cmsUInt32Number) t->nPatches + 1) *sizeof (char*)); - if (t->Data == NULL) { - - SynError(it8, "AllocateDataSet: Unable to allocate data array"); - } - -} - -static -char* GetData(cmsIT8* it8, int nSet, int nField) -{ - TABLE* t = GetTable(it8); - int nSamples = t -> nSamples; - int nPatches = t -> nPatches; - - if (nSet >= nPatches || nField >= nSamples) - return NULL; - - if (!t->Data) return NULL; - return t->Data [nSet * nSamples + nField]; -} - -static -cmsBool SetData(cmsIT8* it8, int nSet, int nField, const char *Val) -{ - TABLE* t = GetTable(it8); - - if (!t->Data) - AllocateDataSet(it8); - - if (!t->Data) return FALSE; - - if (nSet > t -> nPatches || nSet < 0) { - - return SynError(it8, "Patch %d out of range, there are %d patches", nSet, t -> nPatches); - } - - if (nField > t ->nSamples || nField < 0) { - return SynError(it8, "Sample %d out of range, there are %d samples", nField, t ->nSamples); - - } - - t->Data [nSet * t -> nSamples + nField] = AllocString(it8, Val); - return TRUE; -} - - -// --------------------------------------------------------------- File I/O - - -// Writes a string to file -static -void WriteStr(SAVESTREAM* f, const char *str) -{ - cmsUInt32Number len; - - if (str == NULL) - str = " "; - - // Length to write - len = (cmsUInt32Number) strlen(str); - f ->Used += len; - - - if (f ->stream) { // Should I write it to a file? - - if (fwrite(str, 1, len, f->stream) != len) { - cmsSignalError(0, cmsERROR_WRITE, "Write to file error in CGATS parser"); - return; - } - - } - else { // Or to a memory block? - - if (f ->Base) { // Am I just counting the bytes? - - if (f ->Used > f ->Max) { - - cmsSignalError(0, cmsERROR_WRITE, "Write to memory overflows in CGATS parser"); - return; - } - - memmove(f ->Ptr, str, len); - f->Ptr += len; - } - - } -} - - -// Write formatted - -static -void Writef(SAVESTREAM* f, const char* frm, ...) -{ - char Buffer[4096]; - va_list args; - - va_start(args, frm); - vsnprintf(Buffer, 4095, frm, args); - Buffer[4095] = 0; - WriteStr(f, Buffer); - va_end(args); - -} - -// Writes full header -static -void WriteHeader(cmsIT8* it8, SAVESTREAM* fp) -{ - KEYVALUE* p; - TABLE* t = GetTable(it8); - - // Writes the type - WriteStr(fp, t->SheetType); - WriteStr(fp, "\n"); - - for (p = t->HeaderList; (p != NULL); p = p->Next) - { - if (*p ->Keyword == '#') { - - char* Pt; - - WriteStr(fp, "#\n# "); - for (Pt = p ->Value; *Pt; Pt++) { - - - Writef(fp, "%c", *Pt); - - if (*Pt == '\n') { - WriteStr(fp, "# "); - } - } - - WriteStr(fp, "\n#\n"); - continue; - } - - - if (!IsAvailableOnList(it8-> ValidKeywords, p->Keyword, NULL, NULL)) { - -#ifdef CMS_STRICT_CGATS - WriteStr(fp, "KEYWORD\t\""); - WriteStr(fp, p->Keyword); - WriteStr(fp, "\"\n"); -#endif - - AddAvailableProperty(it8, p->Keyword, WRITE_UNCOOKED); - } - - WriteStr(fp, p->Keyword); - if (p->Value) { - - switch (p ->WriteAs) { - - case WRITE_UNCOOKED: - Writef(fp, "\t%s", p ->Value); - break; - - case WRITE_STRINGIFY: - Writef(fp, "\t\"%s\"", p->Value ); - break; - - case WRITE_HEXADECIMAL: - Writef(fp, "\t0x%X", atoi(p ->Value)); - break; - - case WRITE_BINARY: - Writef(fp, "\t0x%B", atoi(p ->Value)); - break; - - case WRITE_PAIR: - Writef(fp, "\t\"%s,%s\"", p->Subkey, p->Value); - break; - - default: SynError(it8, "Unknown write mode %d", p ->WriteAs); - return; - } - } - - WriteStr (fp, "\n"); - } - -} - - -// Writes the data format -static -void WriteDataFormat(SAVESTREAM* fp, cmsIT8* it8) -{ - int i, nSamples; - TABLE* t = GetTable(it8); - - if (!t -> DataFormat) return; - - WriteStr(fp, "BEGIN_DATA_FORMAT\n"); - WriteStr(fp, " "); - nSamples = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS")); - - for (i = 0; i < nSamples; i++) { - - WriteStr(fp, t->DataFormat[i]); - WriteStr(fp, ((i == (nSamples-1)) ? "\n" : "\t")); - } - - WriteStr (fp, "END_DATA_FORMAT\n"); -} - - -// Writes data array -static -void WriteData(SAVESTREAM* fp, cmsIT8* it8) -{ - int i, j; - TABLE* t = GetTable(it8); - - if (!t->Data) return; - - WriteStr (fp, "BEGIN_DATA\n"); - - t->nPatches = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS")); - - for (i = 0; i < t-> nPatches; i++) { - - WriteStr(fp, " "); - - for (j = 0; j < t->nSamples; j++) { - - char *ptr = t->Data[i*t->nSamples+j]; - - if (ptr == NULL) WriteStr(fp, "\"\""); - else { - // If value contains whitespace, enclose within quote - - if (strchr(ptr, ' ') != NULL) { - - WriteStr(fp, "\""); - WriteStr(fp, ptr); - WriteStr(fp, "\""); - } - else - WriteStr(fp, ptr); - } - - WriteStr(fp, ((j == (t->nSamples-1)) ? "\n" : "\t")); - } - } - WriteStr (fp, "END_DATA\n"); -} - - - -// Saves whole file -cmsBool CMSEXPORT cmsIT8SaveToFile(cmsHANDLE hIT8, const char* cFileName) -{ - SAVESTREAM sd; - cmsUInt32Number i; - cmsIT8* it8 = (cmsIT8*) hIT8; - - memset(&sd, 0, sizeof(sd)); - - sd.stream = fopen(cFileName, "wt"); - if (!sd.stream) return FALSE; - - for (i=0; i < it8 ->TablesCount; i++) { - - cmsIT8SetTable(hIT8, i); - WriteHeader(it8, &sd); - WriteDataFormat(&sd, it8); - WriteData(&sd, it8); - } - - if (fclose(sd.stream) != 0) return FALSE; - - return TRUE; -} - - -// Saves to memory -cmsBool CMSEXPORT cmsIT8SaveToMem(cmsHANDLE hIT8, void *MemPtr, cmsUInt32Number* BytesNeeded) -{ - SAVESTREAM sd; - cmsUInt32Number i; - cmsIT8* it8 = (cmsIT8*) hIT8; - - memset(&sd, 0, sizeof(sd)); - - sd.stream = NULL; - sd.Base = (cmsUInt8Number*) MemPtr; - sd.Ptr = sd.Base; - - sd.Used = 0; - - if (sd.Base) - sd.Max = *BytesNeeded; // Write to memory? - else - sd.Max = 0; // Just counting the needed bytes - - for (i=0; i < it8 ->TablesCount; i++) { - - cmsIT8SetTable(hIT8, i); - WriteHeader(it8, &sd); - WriteDataFormat(&sd, it8); - WriteData(&sd, it8); - } - - sd.Used++; // The \0 at the very end - - if (sd.Base) - *sd.Ptr = 0; - - *BytesNeeded = sd.Used; - - return TRUE; -} - - -// -------------------------------------------------------------- Higer level parsing - -static -cmsBool DataFormatSection(cmsIT8* it8) -{ - int iField = 0; - TABLE* t = GetTable(it8); - - InSymbol(it8); // Eats "BEGIN_DATA_FORMAT" - CheckEOLN(it8); - - while (it8->sy != SEND_DATA_FORMAT && - it8->sy != SEOLN && - it8->sy != SEOF && - it8->sy != SSYNERROR) { - - if (it8->sy != SIDENT) { - - return SynError(it8, "Sample type expected"); - } - - if (!SetDataFormat(it8, iField, it8->id)) return FALSE; - iField++; - - InSymbol(it8); - SkipEOLN(it8); - } - - SkipEOLN(it8); - Skip(it8, SEND_DATA_FORMAT); - SkipEOLN(it8); - - if (iField != t ->nSamples) { - SynError(it8, "Count mismatch. NUMBER_OF_FIELDS was %d, found %d\n", t ->nSamples, iField); - - - } - - return TRUE; -} - - - -static -cmsBool DataSection (cmsIT8* it8) -{ - int iField = 0; - int iSet = 0; - char Buffer[256]; - TABLE* t = GetTable(it8); - - InSymbol(it8); // Eats "BEGIN_DATA" - CheckEOLN(it8); - - if (!t->Data) - AllocateDataSet(it8); - - while (it8->sy != SEND_DATA && it8->sy != SEOF) - { - if (iField >= t -> nSamples) { - iField = 0; - iSet++; - - } - - if (it8->sy != SEND_DATA && it8->sy != SEOF) { - - if (!GetVal(it8, Buffer, 255, "Sample data expected")) - return FALSE; - - if (!SetData(it8, iSet, iField, Buffer)) - return FALSE; - - iField++; - - InSymbol(it8); - SkipEOLN(it8); - } - } - - SkipEOLN(it8); - Skip(it8, SEND_DATA); - SkipEOLN(it8); - - // Check for data completion. - - if ((iSet+1) != t -> nPatches) - return SynError(it8, "Count mismatch. NUMBER_OF_SETS was %d, found %d\n", t ->nPatches, iSet+1); - - return TRUE; -} - - - - -static -cmsBool HeaderSection(cmsIT8* it8) -{ - char VarName[MAXID]; - char Buffer[MAXSTR]; - KEYVALUE* Key; - - while (it8->sy != SEOF && - it8->sy != SSYNERROR && - it8->sy != SBEGIN_DATA_FORMAT && - it8->sy != SBEGIN_DATA) { - - - switch (it8 -> sy) { - - case SKEYWORD: - InSymbol(it8); - if (!GetVal(it8, Buffer, MAXSTR-1, "Keyword expected")) return FALSE; - if (!AddAvailableProperty(it8, Buffer, WRITE_UNCOOKED)) return FALSE; - InSymbol(it8); - break; - - - case SDATA_FORMAT_ID: - InSymbol(it8); - if (!GetVal(it8, Buffer, MAXSTR-1, "Keyword expected")) return FALSE; - if (!AddAvailableSampleID(it8, Buffer)) return FALSE; - InSymbol(it8); - break; - - - case SIDENT: - strncpy(VarName, it8->id, MAXID-1); - VarName[MAXID-1] = 0; - - if (!IsAvailableOnList(it8-> ValidKeywords, VarName, NULL, &Key)) { - -#ifdef CMS_STRICT_CGATS - return SynError(it8, "Undefined keyword '%s'", VarName); -#else - Key = AddAvailableProperty(it8, VarName, WRITE_UNCOOKED); - if (Key == NULL) return FALSE; -#endif - } - - InSymbol(it8); - if (!GetVal(it8, Buffer, MAXSTR-1, "Property data expected")) return FALSE; - - if(Key->WriteAs != WRITE_PAIR) { - AddToList(it8, &GetTable(it8)->HeaderList, VarName, NULL, Buffer, - (it8->sy == SSTRING) ? WRITE_STRINGIFY : WRITE_UNCOOKED); - } - else { - const char *Subkey; - char *Nextkey; - if (it8->sy != SSTRING) - return SynError(it8, "Invalid value '%s' for property '%s'.", Buffer, VarName); - - // chop the string as a list of "subkey, value" pairs, using ';' as a separator - for (Subkey = Buffer; Subkey != NULL; Subkey = Nextkey) - { - char *Value, *temp; - - // identify token pair boundary - Nextkey = (char*) strchr(Subkey, ';'); - if(Nextkey) - *Nextkey++ = '\0'; - - // for each pair, split the subkey and the value - Value = (char*) strrchr(Subkey, ','); - if(Value == NULL) - return SynError(it8, "Invalid value for property '%s'.", VarName); - - // gobble the spaces before the coma, and the coma itself - temp = Value++; - do *temp-- = '\0'; while(temp >= Subkey && *temp == ' '); - - // gobble any space at the right - temp = Value + strlen(Value) - 1; - while(*temp == ' ') *temp-- = '\0'; - - // trim the strings from the left - Subkey += strspn(Subkey, " "); - Value += strspn(Value, " "); - - if(Subkey[0] == 0 || Value[0] == 0) - return SynError(it8, "Invalid value for property '%s'.", VarName); - AddToList(it8, &GetTable(it8)->HeaderList, VarName, Subkey, Value, WRITE_PAIR); - } - } - - InSymbol(it8); - break; - - - case SEOLN: break; - - default: - return SynError(it8, "expected keyword or identifier"); - } - - SkipEOLN(it8); - } - - return TRUE; - -} - - -static -void ReadType(cmsIT8* it8, char* SheetTypePtr) -{ - // First line is a very special case. - - while (isseparator(it8->ch)) - NextCh(it8); - - while (it8->ch != '\r' && it8 ->ch != '\n' && it8->ch != '\t' && it8 -> ch != -1) { - - *SheetTypePtr++= (char) it8 ->ch; - NextCh(it8); - } - - *SheetTypePtr = 0; -} - - -static -cmsBool ParseIT8(cmsIT8* it8, cmsBool nosheet) -{ - char* SheetTypePtr = it8 ->Tab[0].SheetType; - - if (nosheet == 0) { - ReadType(it8, SheetTypePtr); - } - - InSymbol(it8); - - SkipEOLN(it8); - - while (it8-> sy != SEOF && - it8-> sy != SSYNERROR) { - - switch (it8 -> sy) { - - case SBEGIN_DATA_FORMAT: - if (!DataFormatSection(it8)) return FALSE; - break; - - case SBEGIN_DATA: - - if (!DataSection(it8)) return FALSE; - - if (it8 -> sy != SEOF) { - - AllocTable(it8); - it8 ->nTable = it8 ->TablesCount - 1; - - // Read sheet type if present. We only support identifier and string. - // is a type string - // anything else, is not a type string - if (nosheet == 0) { - - if (it8 ->sy == SIDENT) { - - // May be a type sheet or may be a prop value statement. We cannot use insymbol in - // this special case... - while (isseparator(it8->ch)) - NextCh(it8); - - // If a newline is found, then this is a type string - if (it8 ->ch == '\n' || it8->ch == '\r') { - - cmsIT8SetSheetType(it8, it8 ->id); - InSymbol(it8); - } - else - { - // It is not. Just continue - cmsIT8SetSheetType(it8, ""); - } - } - else - // Validate quoted strings - if (it8 ->sy == SSTRING) { - cmsIT8SetSheetType(it8, it8 ->str); - InSymbol(it8); - } - } - - } - break; - - case SEOLN: - SkipEOLN(it8); - break; - - default: - if (!HeaderSection(it8)) return FALSE; - } - - } - - return (it8 -> sy != SSYNERROR); -} - - - -// Init usefull pointers - -static -void CookPointers(cmsIT8* it8) -{ - int idField, i; - char* Fld; - cmsUInt32Number j; - cmsUInt32Number nOldTable = it8 ->nTable; - - for (j=0; j < it8 ->TablesCount; j++) { - - TABLE* t = it8 ->Tab + j; - - t -> SampleID = 0; - it8 ->nTable = j; - - for (idField = 0; idField < t -> nSamples; idField++) - { - if (t ->DataFormat == NULL){ - SynError(it8, "Undefined DATA_FORMAT"); - return; - } - - Fld = t->DataFormat[idField]; - if (!Fld) continue; - - - if (cmsstrcasecmp(Fld, "SAMPLE_ID") == 0) { - - t -> SampleID = idField; - - for (i=0; i < t -> nPatches; i++) { - - char *Data = GetData(it8, i, idField); - if (Data) { - char Buffer[256]; - - strncpy(Buffer, Data, 255); - Buffer[255] = 0; - - if (strlen(Buffer) <= strlen(Data)) - strcpy(Data, Buffer); - else - SetData(it8, i, idField, Buffer); - - } - } - - } - - // "LABEL" is an extension. It keeps references to forward tables - - if ((cmsstrcasecmp(Fld, "LABEL") == 0) || Fld[0] == '$' ) { - - // Search for table references... - for (i=0; i < t -> nPatches; i++) { - - char *Label = GetData(it8, i, idField); - - if (Label) { - - cmsUInt32Number k; - - // This is the label, search for a table containing - // this property - - for (k=0; k < it8 ->TablesCount; k++) { - - TABLE* Table = it8 ->Tab + k; - KEYVALUE* p; - - if (IsAvailableOnList(Table->HeaderList, Label, NULL, &p)) { - - // Available, keep type and table - char Buffer[256]; - - char *Type = p ->Value; - int nTable = (int) k; - - snprintf(Buffer, 255, "%s %d %s", Label, nTable, Type ); - - SetData(it8, i, idField, Buffer); - } - } - - - } - - } - - - } - - } - } - - it8 ->nTable = nOldTable; -} - -// Try to infere if the file is a CGATS/IT8 file at all. Read first line -// that should be something like some printable characters plus a \n -// returns 0 if this is not like a CGATS, or an integer otherwise. This integer is the number of words in first line? -static -int IsMyBlock(cmsUInt8Number* Buffer, int n) -{ - int words = 1, space = 0, quot = 0; - int i; - - if (n < 10) return 0; // Too small - - if (n > 132) - n = 132; - - for (i = 1; i < n; i++) { - - switch(Buffer[i]) - { - case '\n': - case '\r': - return ((quot == 1) || (words > 2)) ? 0 : words; - case '\t': - case ' ': - if(!quot && !space) - space = 1; - break; - case '\"': - quot = !quot; - break; - default: - if (Buffer[i] < 32) return 0; - if (Buffer[i] > 127) return 0; - words += space; - space = 0; - break; - } - } - - return 0; -} - - -static -cmsBool IsMyFile(const char* FileName) -{ - FILE *fp; - cmsUInt32Number Size; - cmsUInt8Number Ptr[133]; - - fp = fopen(FileName, "rt"); - if (!fp) { - cmsSignalError(0, cmsERROR_FILE, "File '%s' not found", FileName); - return FALSE; - } - - Size = (cmsUInt32Number) fread(Ptr, 1, 132, fp); - - if (fclose(fp) != 0) - return FALSE; - - Ptr[Size] = '\0'; - - return IsMyBlock(Ptr, Size); -} - -// ---------------------------------------------------------- Exported routines - - -cmsHANDLE CMSEXPORT cmsIT8LoadFromMem(cmsContext ContextID, void *Ptr, cmsUInt32Number len) -{ - cmsHANDLE hIT8; - cmsIT8* it8; - int type; - - _cmsAssert(Ptr != NULL); - _cmsAssert(len != 0); - - type = IsMyBlock((cmsUInt8Number*)Ptr, len); - if (type == 0) return NULL; - - hIT8 = cmsIT8Alloc(ContextID); - if (!hIT8) return NULL; - - it8 = (cmsIT8*) hIT8; - it8 ->MemoryBlock = (char*) _cmsMalloc(ContextID, len + 1); - - strncpy(it8 ->MemoryBlock, (const char*) Ptr, len); - it8 ->MemoryBlock[len] = 0; - - strncpy(it8->FileStack[0]->FileName, "", cmsMAX_PATH-1); - it8-> Source = it8 -> MemoryBlock; - - if (!ParseIT8(it8, type-1)) { - - cmsIT8Free(hIT8); - return FALSE; - } - - CookPointers(it8); - it8 ->nTable = 0; - - _cmsFree(ContextID, it8->MemoryBlock); - it8 -> MemoryBlock = NULL; - - return hIT8; - - -} - - -cmsHANDLE CMSEXPORT cmsIT8LoadFromFile(cmsContext ContextID, const char* cFileName) -{ - - cmsHANDLE hIT8; - cmsIT8* it8; - int type; - - _cmsAssert(cFileName != NULL); - - type = IsMyFile(cFileName); - if (type == 0) return NULL; - - hIT8 = cmsIT8Alloc(ContextID); - it8 = (cmsIT8*) hIT8; - if (!hIT8) return NULL; - - - it8 ->FileStack[0]->Stream = fopen(cFileName, "rt"); - - if (!it8 ->FileStack[0]->Stream) { - cmsIT8Free(hIT8); - return NULL; - } - - - strncpy(it8->FileStack[0]->FileName, cFileName, cmsMAX_PATH-1); - it8->FileStack[0]->FileName[cmsMAX_PATH-1] = 0; - - if (!ParseIT8(it8, type-1)) { - - fclose(it8 ->FileStack[0]->Stream); - cmsIT8Free(hIT8); - return NULL; - } - - CookPointers(it8); - it8 ->nTable = 0; - - if (fclose(it8 ->FileStack[0]->Stream)!= 0) { - cmsIT8Free(hIT8); - return NULL; - } - - return hIT8; - -} - -int CMSEXPORT cmsIT8EnumDataFormat(cmsHANDLE hIT8, char ***SampleNames) -{ - cmsIT8* it8 = (cmsIT8*) hIT8; - TABLE* t; - - _cmsAssert(hIT8 != NULL); - - t = GetTable(it8); - - if (SampleNames) - *SampleNames = t -> DataFormat; - return t -> nSamples; -} - - -cmsUInt32Number CMSEXPORT cmsIT8EnumProperties(cmsHANDLE hIT8, char ***PropertyNames) -{ - cmsIT8* it8 = (cmsIT8*) hIT8; - KEYVALUE* p; - cmsUInt32Number n; - char **Props; - TABLE* t; - - _cmsAssert(hIT8 != NULL); - - t = GetTable(it8); - - // Pass#1 - count properties - - n = 0; - for (p = t -> HeaderList; p != NULL; p = p->Next) { - n++; - } - - - Props = (char **) AllocChunk(it8, sizeof(char *) * n); - - // Pass#2 - Fill pointers - n = 0; - for (p = t -> HeaderList; p != NULL; p = p->Next) { - Props[n++] = p -> Keyword; - } - - *PropertyNames = Props; - return n; -} - -cmsUInt32Number CMSEXPORT cmsIT8EnumPropertyMulti(cmsHANDLE hIT8, const char* cProp, const char ***SubpropertyNames) -{ - cmsIT8* it8 = (cmsIT8*) hIT8; - KEYVALUE *p, *tmp; - cmsUInt32Number n; - const char **Props; - TABLE* t; - - _cmsAssert(hIT8 != NULL); - - - t = GetTable(it8); - - if(!IsAvailableOnList(t->HeaderList, cProp, NULL, &p)) { - *SubpropertyNames = 0; - return 0; - } - - // Pass#1 - count properties - - n = 0; - for (tmp = p; tmp != NULL; tmp = tmp->NextSubkey) { - if(tmp->Subkey != NULL) - n++; - } - - - Props = (const char **) AllocChunk(it8, sizeof(char *) * n); - - // Pass#2 - Fill pointers - n = 0; - for (tmp = p; tmp != NULL; tmp = tmp->NextSubkey) { - if(tmp->Subkey != NULL) - Props[n++] = p ->Subkey; - } - - *SubpropertyNames = Props; - return n; -} - -static -int LocatePatch(cmsIT8* it8, const char* cPatch) -{ - int i; - const char *data; - TABLE* t = GetTable(it8); - - for (i=0; i < t-> nPatches; i++) { - - data = GetData(it8, i, t->SampleID); - - if (data != NULL) { - - if (cmsstrcasecmp(data, cPatch) == 0) - return i; - } - } - - // SynError(it8, "Couldn't find patch '%s'\n", cPatch); - return -1; -} - - -static -int LocateEmptyPatch(cmsIT8* it8) -{ - int i; - const char *data; - TABLE* t = GetTable(it8); - - for (i=0; i < t-> nPatches; i++) { - - data = GetData(it8, i, t->SampleID); - - if (data == NULL) - return i; - - } - - return -1; -} - -static -int LocateSample(cmsIT8* it8, const char* cSample) -{ - int i; - const char *fld; - TABLE* t = GetTable(it8); - - for (i=0; i < t->nSamples; i++) { - - fld = GetDataFormat(it8, i); - if (cmsstrcasecmp(fld, cSample) == 0) - return i; - } - - return -1; - -} - - -int CMSEXPORT cmsIT8FindDataFormat(cmsHANDLE hIT8, const char* cSample) -{ - cmsIT8* it8 = (cmsIT8*) hIT8; - - _cmsAssert(hIT8 != NULL); - - return LocateSample(it8, cSample); -} - - - -const char* CMSEXPORT cmsIT8GetDataRowCol(cmsHANDLE hIT8, int row, int col) -{ - cmsIT8* it8 = (cmsIT8*) hIT8; - - _cmsAssert(hIT8 != NULL); - - return GetData(it8, row, col); -} - - -cmsFloat64Number CMSEXPORT cmsIT8GetDataRowColDbl(cmsHANDLE hIT8, int row, int col) -{ - const char* Buffer; - - Buffer = cmsIT8GetDataRowCol(hIT8, row, col); - - if (Buffer == NULL) return 0.0; - - return ParseFloatNumber(Buffer); -} - - -cmsBool CMSEXPORT cmsIT8SetDataRowCol(cmsHANDLE hIT8, int row, int col, const char* Val) -{ - cmsIT8* it8 = (cmsIT8*) hIT8; - - _cmsAssert(hIT8 != NULL); - - return SetData(it8, row, col, Val); -} - - -cmsBool CMSEXPORT cmsIT8SetDataRowColDbl(cmsHANDLE hIT8, int row, int col, cmsFloat64Number Val) -{ - cmsIT8* it8 = (cmsIT8*) hIT8; - char Buff[256]; - - _cmsAssert(hIT8 != NULL); - - sprintf(Buff, it8->DoubleFormatter, Val); - - return SetData(it8, row, col, Buff); -} - - - -const char* CMSEXPORT cmsIT8GetData(cmsHANDLE hIT8, const char* cPatch, const char* cSample) -{ - cmsIT8* it8 = (cmsIT8*) hIT8; - int iField, iSet; - - _cmsAssert(hIT8 != NULL); - - iField = LocateSample(it8, cSample); - if (iField < 0) { - return NULL; - } - - iSet = LocatePatch(it8, cPatch); - if (iSet < 0) { - return NULL; - } - - return GetData(it8, iSet, iField); -} - - -cmsFloat64Number CMSEXPORT cmsIT8GetDataDbl(cmsHANDLE it8, const char* cPatch, const char* cSample) -{ - const char* Buffer; - - Buffer = cmsIT8GetData(it8, cPatch, cSample); - - return ParseFloatNumber(Buffer); -} - - - -cmsBool CMSEXPORT cmsIT8SetData(cmsHANDLE hIT8, const char* cPatch, const char* cSample, const char *Val) -{ - cmsIT8* it8 = (cmsIT8*) hIT8; - int iField, iSet; - TABLE* t; - - _cmsAssert(hIT8 != NULL); - - t = GetTable(it8); - - iField = LocateSample(it8, cSample); - - if (iField < 0) - return FALSE; - - if (t-> nPatches == 0) { - - AllocateDataFormat(it8); - AllocateDataSet(it8); - CookPointers(it8); - } - - if (cmsstrcasecmp(cSample, "SAMPLE_ID") == 0) { - - iSet = LocateEmptyPatch(it8); - if (iSet < 0) { - return SynError(it8, "Couldn't add more patches '%s'\n", cPatch); - } - - iField = t -> SampleID; - } - else { - iSet = LocatePatch(it8, cPatch); - if (iSet < 0) { - return FALSE; - } - } - - return SetData(it8, iSet, iField, Val); -} - - -cmsBool CMSEXPORT cmsIT8SetDataDbl(cmsHANDLE hIT8, const char* cPatch, - const char* cSample, - cmsFloat64Number Val) -{ - cmsIT8* it8 = (cmsIT8*) hIT8; - char Buff[256]; - - _cmsAssert(hIT8 != NULL); - - snprintf(Buff, 255, it8->DoubleFormatter, Val); - return cmsIT8SetData(hIT8, cPatch, cSample, Buff); -} - -// Buffer should get MAXSTR at least - -const char* CMSEXPORT cmsIT8GetPatchName(cmsHANDLE hIT8, int nPatch, char* buffer) -{ - cmsIT8* it8 = (cmsIT8*) hIT8; - TABLE* t; - char* Data; - - _cmsAssert(hIT8 != NULL); - - t = GetTable(it8); - Data = GetData(it8, nPatch, t->SampleID); - - if (!Data) return NULL; - if (!buffer) return Data; - - strncpy(buffer, Data, MAXSTR-1); - buffer[MAXSTR-1] = 0; - return buffer; -} - -int CMSEXPORT cmsIT8GetPatchByName(cmsHANDLE hIT8, const char *cPatch) -{ - _cmsAssert(hIT8 != NULL); - - return LocatePatch((cmsIT8*)hIT8, cPatch); -} - -cmsUInt32Number CMSEXPORT cmsIT8TableCount(cmsHANDLE hIT8) -{ - cmsIT8* it8 = (cmsIT8*) hIT8; - - _cmsAssert(hIT8 != NULL); - - return it8 ->TablesCount; -} - -// This handles the "LABEL" extension. -// Label, nTable, Type - -int CMSEXPORT cmsIT8SetTableByLabel(cmsHANDLE hIT8, const char* cSet, const char* cField, const char* ExpectedType) -{ - const char* cLabelFld; - char Type[256], Label[256]; - int nTable; - - _cmsAssert(hIT8 != NULL); - - if (cField != NULL && *cField == 0) - cField = "LABEL"; - - if (cField == NULL) - cField = "LABEL"; - - cLabelFld = cmsIT8GetData(hIT8, cSet, cField); - if (!cLabelFld) return -1; - - if (sscanf(cLabelFld, "%255s %d %255s", Label, &nTable, Type) != 3) - return -1; - - if (ExpectedType != NULL && *ExpectedType == 0) - ExpectedType = NULL; - - if (ExpectedType) { - - if (cmsstrcasecmp(Type, ExpectedType) != 0) return -1; - } - - return cmsIT8SetTable(hIT8, nTable); -} - - -cmsBool CMSEXPORT cmsIT8SetIndexColumn(cmsHANDLE hIT8, const char* cSample) -{ - cmsIT8* it8 = (cmsIT8*) hIT8; - int pos; - - _cmsAssert(hIT8 != NULL); - - pos = LocateSample(it8, cSample); - if(pos == -1) - return FALSE; - - it8->Tab[it8->nTable].SampleID = pos; - return TRUE; -} - - -void CMSEXPORT cmsIT8DefineDblFormat(cmsHANDLE hIT8, const char* Formatter) -{ - cmsIT8* it8 = (cmsIT8*) hIT8; - - _cmsAssert(hIT8 != NULL); - - if (Formatter == NULL) - strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT); - else - strncpy(it8->DoubleFormatter, Formatter, sizeof(it8->DoubleFormatter)); - - it8 ->DoubleFormatter[sizeof(it8 ->DoubleFormatter)-1] = 0; -} diff --git a/third_party/lcms2-2.6/src/cmscnvrt.c b/third_party/lcms2-2.6/src/cmscnvrt.c deleted file mode 100644 index 1a93e83f90..0000000000 --- a/third_party/lcms2-2.6/src/cmscnvrt.c +++ /dev/null @@ -1,1142 +0,0 @@ -//--------------------------------------------------------------------------------- -// -// 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" - - -// Link several profiles to obtain a single LUT modelling the whole color transform. Intents, Black point -// compensation and Adaptation parameters may vary across profiles. BPC and Adaptation refers to the PCS -// after the profile. I.e, BPC[0] refers to connexion between profile(0) and profile(1) -cmsPipeline* _cmsLinkProfiles(cmsContext ContextID, - cmsUInt32Number nProfiles, - cmsUInt32Number Intents[], - cmsHPROFILE hProfiles[], - cmsBool BPC[], - cmsFloat64Number AdaptationStates[], - cmsUInt32Number dwFlags); - -//--------------------------------------------------------------------------------- - -// This is the default routine for ICC-style intents. A user may decide to override it by using a plugin. -// Supported intents are perceptual, relative colorimetric, saturation and ICC-absolute colorimetric -static -cmsPipeline* DefaultICCintents(cmsContext ContextID, - cmsUInt32Number nProfiles, - cmsUInt32Number Intents[], - cmsHPROFILE hProfiles[], - cmsBool BPC[], - cmsFloat64Number AdaptationStates[], - cmsUInt32Number dwFlags); - -//--------------------------------------------------------------------------------- - -// This is the entry for black-preserving K-only intents, which are non-ICC. Last profile have to be a output profile -// to do the trick (no devicelinks allowed at that position) -static -cmsPipeline* BlackPreservingKOnlyIntents(cmsContext ContextID, - cmsUInt32Number nProfiles, - cmsUInt32Number Intents[], - cmsHPROFILE hProfiles[], - cmsBool BPC[], - cmsFloat64Number AdaptationStates[], - cmsUInt32Number dwFlags); - -//--------------------------------------------------------------------------------- - -// This is the entry for black-plane preserving, which are non-ICC. Again, Last profile have to be a output profile -// to do the trick (no devicelinks allowed at that position) -static -cmsPipeline* BlackPreservingKPlaneIntents(cmsContext ContextID, - cmsUInt32Number nProfiles, - cmsUInt32Number Intents[], - cmsHPROFILE hProfiles[], - cmsBool BPC[], - cmsFloat64Number AdaptationStates[], - cmsUInt32Number dwFlags); - -//--------------------------------------------------------------------------------- - - -// This is a structure holding implementations for all supported intents. -typedef struct _cms_intents_list { - - cmsUInt32Number Intent; - char Description[256]; - cmsIntentFn Link; - struct _cms_intents_list* Next; - -} cmsIntentsList; - - -// Built-in intents -static cmsIntentsList DefaultIntents[] = { - - { INTENT_PERCEPTUAL, "Perceptual", DefaultICCintents, &DefaultIntents[1] }, - { INTENT_RELATIVE_COLORIMETRIC, "Relative colorimetric", DefaultICCintents, &DefaultIntents[2] }, - { INTENT_SATURATION, "Saturation", DefaultICCintents, &DefaultIntents[3] }, - { INTENT_ABSOLUTE_COLORIMETRIC, "Absolute colorimetric", DefaultICCintents, &DefaultIntents[4] }, - { INTENT_PRESERVE_K_ONLY_PERCEPTUAL, "Perceptual preserving black ink", BlackPreservingKOnlyIntents, &DefaultIntents[5] }, - { INTENT_PRESERVE_K_ONLY_RELATIVE_COLORIMETRIC, "Relative colorimetric preserving black ink", BlackPreservingKOnlyIntents, &DefaultIntents[6] }, - { INTENT_PRESERVE_K_ONLY_SATURATION, "Saturation preserving black ink", BlackPreservingKOnlyIntents, &DefaultIntents[7] }, - { INTENT_PRESERVE_K_PLANE_PERCEPTUAL, "Perceptual preserving black plane", BlackPreservingKPlaneIntents, &DefaultIntents[8] }, - { INTENT_PRESERVE_K_PLANE_RELATIVE_COLORIMETRIC,"Relative colorimetric preserving black plane", BlackPreservingKPlaneIntents, &DefaultIntents[9] }, - { INTENT_PRESERVE_K_PLANE_SATURATION, "Saturation preserving black plane", BlackPreservingKPlaneIntents, NULL } -}; - - -// A pointer to the begining of the list -_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(cmsContext ContextID, cmsUInt32Number Intent) -{ - _cmsIntentsPluginChunkType* ctx = ( _cmsIntentsPluginChunkType*) _cmsContextGetClientChunk(ContextID, IntentPlugin); - cmsIntentsList* pt; - - 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; -} - -// Black point compensation. Implemented as a linear scaling in XYZ. Black points -// should come relative to the white point. Fills an matrix/offset element m -// which is organized as a 4x4 matrix. -static -void ComputeBlackPointCompensation(const cmsCIEXYZ* BlackPointIn, - const cmsCIEXYZ* BlackPointOut, - cmsMAT3* m, cmsVEC3* off) -{ - cmsFloat64Number ax, ay, az, bx, by, bz, tx, ty, tz; - - // Now we need to compute a matrix plus an offset m and of such of - // [m]*bpin + off = bpout - // [m]*D50 + off = D50 - // - // This is a linear scaling in the form ax+b, where - // a = (bpout - D50) / (bpin - D50) - // b = - D50* (bpout - bpin) / (bpin - D50) - - tx = BlackPointIn->X - cmsD50_XYZ()->X; - ty = BlackPointIn->Y - cmsD50_XYZ()->Y; - tz = BlackPointIn->Z - cmsD50_XYZ()->Z; - - ax = (BlackPointOut->X - cmsD50_XYZ()->X) / tx; - ay = (BlackPointOut->Y - cmsD50_XYZ()->Y) / ty; - az = (BlackPointOut->Z - cmsD50_XYZ()->Z) / tz; - - bx = - cmsD50_XYZ()-> X * (BlackPointOut->X - BlackPointIn->X) / tx; - by = - cmsD50_XYZ()-> Y * (BlackPointOut->Y - BlackPointIn->Y) / ty; - bz = - cmsD50_XYZ()-> Z * (BlackPointOut->Z - BlackPointIn->Z) / tz; - - _cmsVEC3init(&m ->v[0], ax, 0, 0); - _cmsVEC3init(&m ->v[1], 0, ay, 0); - _cmsVEC3init(&m ->v[2], 0, 0, az); - _cmsVEC3init(off, bx, by, bz); - -} - - -// Approximate a blackbody illuminant based on CHAD information -static -cmsFloat64Number CHAD2Temp(const cmsMAT3* Chad) -{ - // Convert D50 across inverse CHAD to get the absolute white point - cmsVEC3 d, s; - cmsCIEXYZ Dest; - cmsCIExyY DestChromaticity; - cmsFloat64Number TempK; - cmsMAT3 m1, m2; - - m1 = *Chad; - if (!_cmsMAT3inverse(&m1, &m2)) return FALSE; - - s.n[VX] = cmsD50_XYZ() -> X; - s.n[VY] = cmsD50_XYZ() -> Y; - s.n[VZ] = cmsD50_XYZ() -> Z; - - _cmsMAT3eval(&d, &m2, &s); - - Dest.X = d.n[VX]; - Dest.Y = d.n[VY]; - Dest.Z = d.n[VZ]; - - cmsXYZ2xyY(&DestChromaticity, &Dest); - - if (!cmsTempFromWhitePoint(&TempK, &DestChromaticity)) - return -1.0; - - return TempK; -} - -// Compute a CHAD based on a given temperature -static - void Temp2CHAD(cmsMAT3* Chad, cmsFloat64Number Temp) -{ - cmsCIEXYZ White; - cmsCIExyY ChromaticityOfWhite; - - cmsWhitePointFromTemp(&ChromaticityOfWhite, Temp); - cmsxyY2XYZ(&White, &ChromaticityOfWhite); - _cmsAdaptationMatrix(Chad, NULL, &White, cmsD50_XYZ()); -} - -// Join scalings to obtain relative input to absolute and then to relative output. -// Result is stored in a 3x3 matrix -static -cmsBool ComputeAbsoluteIntent(cmsFloat64Number AdaptationState, - const cmsCIEXYZ* WhitePointIn, - const cmsMAT3* ChromaticAdaptationMatrixIn, - const cmsCIEXYZ* WhitePointOut, - const cmsMAT3* ChromaticAdaptationMatrixOut, - cmsMAT3* m) -{ - cmsMAT3 Scale, m1, m2, m3, m4; - - // Adaptation state - if (AdaptationState == 1.0) { - - // Observer is fully adapted. Keep chromatic adaptation. - // That is the standard V4 behaviour - _cmsVEC3init(&m->v[0], WhitePointIn->X / WhitePointOut->X, 0, 0); - _cmsVEC3init(&m->v[1], 0, WhitePointIn->Y / WhitePointOut->Y, 0); - _cmsVEC3init(&m->v[2], 0, 0, WhitePointIn->Z / WhitePointOut->Z); - - } - else { - - // Incomplete adaptation. This is an advanced feature. - _cmsVEC3init(&Scale.v[0], WhitePointIn->X / WhitePointOut->X, 0, 0); - _cmsVEC3init(&Scale.v[1], 0, WhitePointIn->Y / WhitePointOut->Y, 0); - _cmsVEC3init(&Scale.v[2], 0, 0, WhitePointIn->Z / WhitePointOut->Z); - - - if (AdaptationState == 0.0) { - - m1 = *ChromaticAdaptationMatrixOut; - _cmsMAT3per(&m2, &m1, &Scale); - // m2 holds CHAD from output white to D50 times abs. col. scaling - - // Observer is not adapted, undo the chromatic adaptation - _cmsMAT3per(m, &m2, ChromaticAdaptationMatrixOut); - - m3 = *ChromaticAdaptationMatrixIn; - if (!_cmsMAT3inverse(&m3, &m4)) return FALSE; - _cmsMAT3per(m, &m2, &m4); - - } else { - - cmsMAT3 MixedCHAD; - cmsFloat64Number TempSrc, TempDest, Temp; - - m1 = *ChromaticAdaptationMatrixIn; - if (!_cmsMAT3inverse(&m1, &m2)) return FALSE; - _cmsMAT3per(&m3, &m2, &Scale); - // m3 holds CHAD from input white to D50 times abs. col. scaling - - TempSrc = CHAD2Temp(ChromaticAdaptationMatrixIn); - TempDest = CHAD2Temp(ChromaticAdaptationMatrixOut); - - if (TempSrc < 0.0 || TempDest < 0.0) return FALSE; // Something went wrong - - if (_cmsMAT3isIdentity(&Scale) && fabs(TempSrc - TempDest) < 0.01) { - - _cmsMAT3identity(m); - return TRUE; - } - - Temp = (1.0 - AdaptationState) * TempDest + AdaptationState * TempSrc; - - // Get a CHAD from whatever output temperature to D50. This replaces output CHAD - Temp2CHAD(&MixedCHAD, Temp); - - _cmsMAT3per(m, &m3, &MixedCHAD); - } - - } - return TRUE; - -} - -// Just to see if m matrix should be applied -static -cmsBool IsEmptyLayer(cmsMAT3* m, cmsVEC3* off) -{ - cmsFloat64Number diff = 0; - cmsMAT3 Ident; - int i; - - if (m == NULL && off == NULL) return TRUE; // NULL is allowed as an empty layer - if (m == NULL && off != NULL) return FALSE; // This is an internal error - - _cmsMAT3identity(&Ident); - - for (i=0; i < 3*3; i++) - diff += fabs(((cmsFloat64Number*)m)[i] - ((cmsFloat64Number*)&Ident)[i]); - - for (i=0; i < 3; i++) - diff += fabs(((cmsFloat64Number*)off)[i]); - - - return (diff < 0.002); -} - - -// Compute the conversion layer -static -cmsBool ComputeConversion(int i, cmsHPROFILE hProfiles[], - cmsUInt32Number Intent, - cmsBool BPC, - cmsFloat64Number AdaptationState, - cmsMAT3* m, cmsVEC3* off) -{ - - int k; - - // m and off are set to identity and this is detected latter on - _cmsMAT3identity(m); - _cmsVEC3init(off, 0, 0, 0); - - // If intent is abs. colorimetric, - if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) { - - cmsCIEXYZ WhitePointIn, WhitePointOut; - cmsMAT3 ChromaticAdaptationMatrixIn, ChromaticAdaptationMatrixOut; - - _cmsReadMediaWhitePoint(&WhitePointIn, hProfiles[i-1]); - _cmsReadCHAD(&ChromaticAdaptationMatrixIn, hProfiles[i-1]); - - _cmsReadMediaWhitePoint(&WhitePointOut, hProfiles[i]); - _cmsReadCHAD(&ChromaticAdaptationMatrixOut, hProfiles[i]); - - if (!ComputeAbsoluteIntent(AdaptationState, - &WhitePointIn, &ChromaticAdaptationMatrixIn, - &WhitePointOut, &ChromaticAdaptationMatrixOut, m)) return FALSE; - - } - else { - // Rest of intents may apply BPC. - - if (BPC) { - - cmsCIEXYZ BlackPointIn, BlackPointOut; - - cmsDetectBlackPoint(&BlackPointIn, hProfiles[i-1], Intent, 0); - cmsDetectDestinationBlackPoint(&BlackPointOut, hProfiles[i], Intent, 0); - - // If black points are equal, then do nothing - if (BlackPointIn.X != BlackPointOut.X || - BlackPointIn.Y != BlackPointOut.Y || - BlackPointIn.Z != BlackPointOut.Z) - ComputeBlackPointCompensation(&BlackPointIn, &BlackPointOut, m, off); - } - } - - // Offset should be adjusted because the encoding. We encode XYZ normalized to 0..1.0, - // to do that, we divide by MAX_ENCODEABLE_XZY. The conversion stage goes XYZ -> XYZ so - // we have first to convert from encoded to XYZ and then convert back to encoded. - // y = Mx + Off - // x = x'c - // y = M x'c + Off - // y = y'c; y' = y / c - // y' = (Mx'c + Off) /c = Mx' + (Off / c) - - for (k=0; k < 3; k++) { - off ->n[k] /= MAX_ENCODEABLE_XYZ; - } - - return TRUE; -} - - -// Add a conversion stage if needed. If a matrix/offset m is given, it applies to XYZ space -static -cmsBool AddConversion(cmsPipeline* Result, cmsColorSpaceSignature InPCS, cmsColorSpaceSignature OutPCS, cmsMAT3* m, cmsVEC3* off) -{ - cmsFloat64Number* m_as_dbl = (cmsFloat64Number*) m; - cmsFloat64Number* off_as_dbl = (cmsFloat64Number*) off; - - // Handle PCS mismatches. A specialized stage is added to the LUT in such case - switch (InPCS) { - - case cmsSigXYZData: // Input profile operates in XYZ - - switch (OutPCS) { - - case cmsSigXYZData: // XYZ -> XYZ - if (!IsEmptyLayer(m, off) && - !cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl))) - return FALSE; - break; - - case cmsSigLabData: // XYZ -> Lab - if (!IsEmptyLayer(m, off) && - !cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl))) - return FALSE; - if (!cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocXYZ2Lab(Result ->ContextID))) - return FALSE; - break; - - default: - return FALSE; // Colorspace mismatch - } - break; - - case cmsSigLabData: // Input profile operates in Lab - - switch (OutPCS) { - - case cmsSigXYZData: // Lab -> XYZ - - if (!cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocLab2XYZ(Result ->ContextID))) - return FALSE; - if (!IsEmptyLayer(m, off) && - !cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl))) - return FALSE; - break; - - case cmsSigLabData: // Lab -> Lab - - if (!IsEmptyLayer(m, off)) { - if (!cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocLab2XYZ(Result ->ContextID)) || - !cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl)) || - !cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocXYZ2Lab(Result ->ContextID))) - return FALSE; - } - break; - - default: - return FALSE; // Mismatch - } - break; - - // On colorspaces other than PCS, check for same space - default: - if (InPCS != OutPCS) return FALSE; - break; - } - - return TRUE; -} - - -// Is a given space compatible with another? -static -cmsBool ColorSpaceIsCompatible(cmsColorSpaceSignature a, cmsColorSpaceSignature b) -{ - // If they are same, they are compatible. - if (a == b) return TRUE; - - // Check for MCH4 substitution of CMYK - if ((a == cmsSig4colorData) && (b == cmsSigCmykData)) return TRUE; - if ((a == cmsSigCmykData) && (b == cmsSig4colorData)) return TRUE; - - // Check for XYZ/Lab. Those spaces are interchangeable as they can be computed one from other. - if ((a == cmsSigXYZData) && (b == cmsSigLabData)) return TRUE; - if ((a == cmsSigLabData) && (b == cmsSigXYZData)) return TRUE; - - return FALSE; -} - - -// Default handler for ICC-style intents -static -cmsPipeline* DefaultICCintents(cmsContext ContextID, - cmsUInt32Number nProfiles, - cmsUInt32Number TheIntents[], - cmsHPROFILE hProfiles[], - cmsBool BPC[], - cmsFloat64Number AdaptationStates[], - cmsUInt32Number dwFlags) -{ - cmsPipeline* Lut = NULL; - cmsPipeline* Result; - cmsHPROFILE hProfile; - cmsMAT3 m; - cmsVEC3 off; - cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut, CurrentColorSpace; - cmsProfileClassSignature ClassSig; - cmsUInt32Number i, Intent; - - // For safety - if (nProfiles == 0) return NULL; - - // Allocate an empty LUT for holding the result. 0 as channel count means 'undefined' - Result = cmsPipelineAlloc(ContextID, 0, 0); - if (Result == NULL) return NULL; - - CurrentColorSpace = cmsGetColorSpace(hProfiles[0]); - - for (i=0; i < nProfiles; i++) { - - cmsBool lIsDeviceLink, lIsInput; - - hProfile = hProfiles[i]; - ClassSig = cmsGetDeviceClass(hProfile); - lIsDeviceLink = (ClassSig == cmsSigLinkClass || ClassSig == cmsSigAbstractClass ); - - // First profile is used as input unless devicelink or abstract - if ((i == 0) && !lIsDeviceLink) { - lIsInput = TRUE; - } - else { - // Else use profile in the input direction if current space is not PCS - lIsInput = (CurrentColorSpace != cmsSigXYZData) && - (CurrentColorSpace != cmsSigLabData); - } - - Intent = TheIntents[i]; - - if (lIsInput || lIsDeviceLink) { - - ColorSpaceIn = cmsGetColorSpace(hProfile); - ColorSpaceOut = cmsGetPCS(hProfile); - } - else { - - ColorSpaceIn = cmsGetPCS(hProfile); - ColorSpaceOut = cmsGetColorSpace(hProfile); - } - - if (!ColorSpaceIsCompatible(ColorSpaceIn, CurrentColorSpace)) { - - cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "ColorSpace mismatch"); - goto Error; - } - - // If devicelink is found, then no custom intent is allowed and we can - // read the LUT to be applied. Settings don't apply here. - if (lIsDeviceLink || ((ClassSig == cmsSigNamedColorClass) && (nProfiles == 1))) { - - // Get the involved LUT from the profile - Lut = _cmsReadDevicelinkLUT(hProfile, Intent); - if (Lut == NULL) goto Error; - - // What about abstract profiles? - if (ClassSig == cmsSigAbstractClass && i > 0) { - if (!ComputeConversion(i, hProfiles, Intent, BPC[i], AdaptationStates[i], &m, &off)) goto Error; - } - else { - _cmsMAT3identity(&m); - _cmsVEC3init(&off, 0, 0, 0); - } - - - if (!AddConversion(Result, CurrentColorSpace, ColorSpaceIn, &m, &off)) goto Error; - - } - else { - - if (lIsInput) { - // Input direction means non-pcs connection, so proceed like devicelinks - Lut = _cmsReadInputLUT(hProfile, Intent); - if (Lut == NULL) goto Error; - } - else { - - // Output direction means PCS connection. Intent may apply here - Lut = _cmsReadOutputLUT(hProfile, Intent); - if (Lut == NULL) goto Error; - - - if (!ComputeConversion(i, hProfiles, Intent, BPC[i], AdaptationStates[i], &m, &off)) goto Error; - if (!AddConversion(Result, CurrentColorSpace, ColorSpaceIn, &m, &off)) goto Error; - - } - } - - // Concatenate to the output LUT - if (!cmsPipelineCat(Result, Lut)) - goto Error; - - cmsPipelineFree(Lut); - Lut = NULL; - - // Update current space - CurrentColorSpace = ColorSpaceOut; - } - - return Result; - -Error: - - if (Lut != NULL) cmsPipelineFree(Lut); - if (Result != NULL) cmsPipelineFree(Result); - return NULL; - - cmsUNUSED_PARAMETER(dwFlags); -} - - -// Wrapper for DLL calling convention -cmsPipeline* CMSEXPORT _cmsDefaultICCintents(cmsContext ContextID, - cmsUInt32Number nProfiles, - cmsUInt32Number TheIntents[], - cmsHPROFILE hProfiles[], - cmsBool BPC[], - cmsFloat64Number AdaptationStates[], - cmsUInt32Number dwFlags) -{ - return DefaultICCintents(ContextID, nProfiles, TheIntents, hProfiles, BPC, AdaptationStates, dwFlags); -} - -// Black preserving intents --------------------------------------------------------------------------------------------- - -// Translate black-preserving intents to ICC ones -static -int TranslateNonICCIntents(int Intent) -{ - switch (Intent) { - case INTENT_PRESERVE_K_ONLY_PERCEPTUAL: - case INTENT_PRESERVE_K_PLANE_PERCEPTUAL: - return INTENT_PERCEPTUAL; - - case INTENT_PRESERVE_K_ONLY_RELATIVE_COLORIMETRIC: - case INTENT_PRESERVE_K_PLANE_RELATIVE_COLORIMETRIC: - return INTENT_RELATIVE_COLORIMETRIC; - - case INTENT_PRESERVE_K_ONLY_SATURATION: - case INTENT_PRESERVE_K_PLANE_SATURATION: - return INTENT_SATURATION; - - default: return Intent; - } -} - -// Sampler for Black-only preserving CMYK->CMYK transforms - -typedef struct { - cmsPipeline* cmyk2cmyk; // The original transform - cmsToneCurve* KTone; // Black-to-black tone curve - -} GrayOnlyParams; - - -// Preserve black only if that is the only ink used -static -int BlackPreservingGrayOnlySampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo) -{ - GrayOnlyParams* bp = (GrayOnlyParams*) Cargo; - - // If going across black only, keep black only - if (In[0] == 0 && In[1] == 0 && In[2] == 0) { - - // TAC does not apply because it is black ink! - Out[0] = Out[1] = Out[2] = 0; - Out[3] = cmsEvalToneCurve16(bp->KTone, In[3]); - return TRUE; - } - - // Keep normal transform for other colors - bp ->cmyk2cmyk ->Eval16Fn(In, Out, bp ->cmyk2cmyk->Data); - return TRUE; -} - -// This is the entry for black-preserving K-only intents, which are non-ICC -static -cmsPipeline* BlackPreservingKOnlyIntents(cmsContext ContextID, - cmsUInt32Number nProfiles, - cmsUInt32Number TheIntents[], - cmsHPROFILE hProfiles[], - cmsBool BPC[], - cmsFloat64Number AdaptationStates[], - cmsUInt32Number dwFlags) -{ - GrayOnlyParams bp; - cmsPipeline* Result; - cmsUInt32Number ICCIntents[256]; - cmsStage* CLUT; - cmsUInt32Number i, nGridPoints; - - - // Sanity check - if (nProfiles < 1 || nProfiles > 255) return NULL; - - // Translate black-preserving intents to ICC ones - for (i=0; i < nProfiles; i++) - ICCIntents[i] = TranslateNonICCIntents(TheIntents[i]); - - // Check for non-cmyk profiles - if (cmsGetColorSpace(hProfiles[0]) != cmsSigCmykData || - cmsGetColorSpace(hProfiles[nProfiles-1]) != cmsSigCmykData) - return DefaultICCintents(ContextID, nProfiles, ICCIntents, hProfiles, BPC, AdaptationStates, dwFlags); - - memset(&bp, 0, sizeof(bp)); - - // Allocate an empty LUT for holding the result - Result = cmsPipelineAlloc(ContextID, 4, 4); - if (Result == NULL) return NULL; - - // Create a LUT holding normal ICC transform - bp.cmyk2cmyk = DefaultICCintents(ContextID, - nProfiles, - ICCIntents, - hProfiles, - BPC, - AdaptationStates, - dwFlags); - - if (bp.cmyk2cmyk == NULL) goto Error; - - // Now, compute the tone curve - bp.KTone = _cmsBuildKToneCurve(ContextID, - 4096, - nProfiles, - ICCIntents, - hProfiles, - BPC, - AdaptationStates, - dwFlags); - - if (bp.KTone == NULL) goto Error; - - - // How many gridpoints are we going to use? - nGridPoints = _cmsReasonableGridpointsByColorspace(cmsSigCmykData, dwFlags); - - // Create the CLUT. 16 bits - CLUT = cmsStageAllocCLut16bit(ContextID, nGridPoints, 4, 4, NULL); - if (CLUT == NULL) goto Error; - - // This is the one and only MPE in this LUT - if (!cmsPipelineInsertStage(Result, cmsAT_BEGIN, CLUT)) - goto Error; - - // Sample it. We cannot afford pre/post linearization this time. - if (!cmsStageSampleCLut16bit(CLUT, BlackPreservingGrayOnlySampler, (void*) &bp, 0)) - goto Error; - - // Get rid of xform and tone curve - cmsPipelineFree(bp.cmyk2cmyk); - cmsFreeToneCurve(bp.KTone); - - return Result; - -Error: - - if (bp.cmyk2cmyk != NULL) cmsPipelineFree(bp.cmyk2cmyk); - if (bp.KTone != NULL) cmsFreeToneCurve(bp.KTone); - if (Result != NULL) cmsPipelineFree(Result); - return NULL; - -} - -// K Plane-preserving CMYK to CMYK ------------------------------------------------------------------------------------ - -typedef struct { - - cmsPipeline* cmyk2cmyk; // The original transform - cmsHTRANSFORM hProofOutput; // Output CMYK to Lab (last profile) - cmsHTRANSFORM cmyk2Lab; // The input chain - cmsToneCurve* KTone; // Black-to-black tone curve - cmsPipeline* LabK2cmyk; // The output profile - cmsFloat64Number MaxError; - - cmsHTRANSFORM hRoundTrip; - cmsFloat64Number MaxTAC; - - -} PreserveKPlaneParams; - - -// The CLUT will be stored at 16 bits, but calculations are performed at cmsFloat32Number precision -static -int BlackPreservingSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo) -{ - int i; - cmsFloat32Number Inf[4], Outf[4]; - cmsFloat32Number LabK[4]; - cmsFloat64Number SumCMY, SumCMYK, Error, Ratio; - cmsCIELab ColorimetricLab, BlackPreservingLab; - PreserveKPlaneParams* bp = (PreserveKPlaneParams*) Cargo; - - // Convert from 16 bits to floating point - for (i=0; i < 4; i++) - Inf[i] = (cmsFloat32Number) (In[i] / 65535.0); - - // Get the K across Tone curve - LabK[3] = cmsEvalToneCurveFloat(bp ->KTone, Inf[3]); - - // If going across black only, keep black only - if (In[0] == 0 && In[1] == 0 && In[2] == 0) { - - Out[0] = Out[1] = Out[2] = 0; - Out[3] = _cmsQuickSaturateWord(LabK[3] * 65535.0); - return TRUE; - } - - // Try the original transform, - cmsPipelineEvalFloat( Inf, Outf, bp ->cmyk2cmyk); - - // Store a copy of the floating point result into 16-bit - for (i=0; i < 4; i++) - Out[i] = _cmsQuickSaturateWord(Outf[i] * 65535.0); - - // Maybe K is already ok (mostly on K=0) - if ( fabs(Outf[3] - LabK[3]) < (3.0 / 65535.0) ) { - return TRUE; - } - - // K differ, mesure and keep Lab measurement for further usage - // this is done in relative colorimetric intent - cmsDoTransform(bp->hProofOutput, Out, &ColorimetricLab, 1); - - // Is not black only and the transform doesn't keep black. - // Obtain the Lab of output CMYK. After that we have Lab + K - cmsDoTransform(bp ->cmyk2Lab, Outf, LabK, 1); - - // Obtain the corresponding CMY using reverse interpolation - // (K is fixed in LabK[3]) - if (!cmsPipelineEvalReverseFloat(LabK, Outf, Outf, bp ->LabK2cmyk)) { - - // Cannot find a suitable value, so use colorimetric xform - // which is already stored in Out[] - return TRUE; - } - - // Make sure to pass thru K (which now is fixed) - Outf[3] = LabK[3]; - - // Apply TAC if needed - SumCMY = Outf[0] + Outf[1] + Outf[2]; - SumCMYK = SumCMY + Outf[3]; - - if (SumCMYK > bp ->MaxTAC) { - - Ratio = 1 - ((SumCMYK - bp->MaxTAC) / SumCMY); - if (Ratio < 0) - Ratio = 0; - } - else - Ratio = 1.0; - - Out[0] = _cmsQuickSaturateWord(Outf[0] * Ratio * 65535.0); // C - Out[1] = _cmsQuickSaturateWord(Outf[1] * Ratio * 65535.0); // M - Out[2] = _cmsQuickSaturateWord(Outf[2] * Ratio * 65535.0); // Y - Out[3] = _cmsQuickSaturateWord(Outf[3] * 65535.0); - - // Estimate the error (this goes 16 bits to Lab DBL) - cmsDoTransform(bp->hProofOutput, Out, &BlackPreservingLab, 1); - Error = cmsDeltaE(&ColorimetricLab, &BlackPreservingLab); - if (Error > bp -> MaxError) - bp->MaxError = Error; - - return TRUE; -} - -// This is the entry for black-plane preserving, which are non-ICC -static -cmsPipeline* BlackPreservingKPlaneIntents(cmsContext ContextID, - cmsUInt32Number nProfiles, - cmsUInt32Number TheIntents[], - cmsHPROFILE hProfiles[], - cmsBool BPC[], - cmsFloat64Number AdaptationStates[], - cmsUInt32Number dwFlags) -{ - PreserveKPlaneParams bp; - cmsPipeline* Result = NULL; - cmsUInt32Number ICCIntents[256]; - cmsStage* CLUT; - cmsUInt32Number i, nGridPoints; - cmsHPROFILE hLab; - - // Sanity check - if (nProfiles < 1 || nProfiles > 255) return NULL; - - // Translate black-preserving intents to ICC ones - for (i=0; i < nProfiles; i++) - ICCIntents[i] = TranslateNonICCIntents(TheIntents[i]); - - // Check for non-cmyk profiles - if (cmsGetColorSpace(hProfiles[0]) != cmsSigCmykData || - !(cmsGetColorSpace(hProfiles[nProfiles-1]) == cmsSigCmykData || - cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigOutputClass)) - return DefaultICCintents(ContextID, nProfiles, ICCIntents, hProfiles, BPC, AdaptationStates, dwFlags); - - // Allocate an empty LUT for holding the result - Result = cmsPipelineAlloc(ContextID, 4, 4); - if (Result == NULL) return NULL; - - - memset(&bp, 0, sizeof(bp)); - - // We need the input LUT of the last profile, assuming this one is responsible of - // black generation. This LUT will be seached in inverse order. - bp.LabK2cmyk = _cmsReadInputLUT(hProfiles[nProfiles-1], INTENT_RELATIVE_COLORIMETRIC); - if (bp.LabK2cmyk == NULL) goto Cleanup; - - // Get total area coverage (in 0..1 domain) - bp.MaxTAC = cmsDetectTAC(hProfiles[nProfiles-1]) / 100.0; - if (bp.MaxTAC <= 0) goto Cleanup; - - - // Create a LUT holding normal ICC transform - bp.cmyk2cmyk = DefaultICCintents(ContextID, - nProfiles, - ICCIntents, - hProfiles, - BPC, - AdaptationStates, - dwFlags); - if (bp.cmyk2cmyk == NULL) goto Cleanup; - - // Now the tone curve - bp.KTone = _cmsBuildKToneCurve(ContextID, 4096, nProfiles, - ICCIntents, - hProfiles, - BPC, - AdaptationStates, - dwFlags); - if (bp.KTone == NULL) goto Cleanup; - - // To measure the output, Last profile to Lab - hLab = cmsCreateLab4ProfileTHR(ContextID, NULL); - bp.hProofOutput = cmsCreateTransformTHR(ContextID, hProfiles[nProfiles-1], - CHANNELS_SH(4)|BYTES_SH(2), hLab, TYPE_Lab_DBL, - INTENT_RELATIVE_COLORIMETRIC, - cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE); - if ( bp.hProofOutput == NULL) goto Cleanup; - - // Same as anterior, but lab in the 0..1 range - bp.cmyk2Lab = cmsCreateTransformTHR(ContextID, hProfiles[nProfiles-1], - FLOAT_SH(1)|CHANNELS_SH(4)|BYTES_SH(4), hLab, - FLOAT_SH(1)|CHANNELS_SH(3)|BYTES_SH(4), - INTENT_RELATIVE_COLORIMETRIC, - cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE); - if (bp.cmyk2Lab == NULL) goto Cleanup; - cmsCloseProfile(hLab); - - // Error estimation (for debug only) - bp.MaxError = 0; - - // How many gridpoints are we going to use? - nGridPoints = _cmsReasonableGridpointsByColorspace(cmsSigCmykData, dwFlags); - - - CLUT = cmsStageAllocCLut16bit(ContextID, nGridPoints, 4, 4, NULL); - if (CLUT == NULL) goto Cleanup; - - if (!cmsPipelineInsertStage(Result, cmsAT_BEGIN, CLUT)) - goto Cleanup; - - cmsStageSampleCLut16bit(CLUT, BlackPreservingSampler, (void*) &bp, 0); - -Cleanup: - - if (bp.cmyk2cmyk) cmsPipelineFree(bp.cmyk2cmyk); - if (bp.cmyk2Lab) cmsDeleteTransform(bp.cmyk2Lab); - if (bp.hProofOutput) cmsDeleteTransform(bp.hProofOutput); - - if (bp.KTone) cmsFreeToneCurve(bp.KTone); - if (bp.LabK2cmyk) cmsPipelineFree(bp.LabK2cmyk); - - return Result; -} - -// Link routines ------------------------------------------------------------------------------------------------------ - -// Chain several profiles into a single LUT. It just checks the parameters and then calls the handler -// for the first intent in chain. The handler may be user-defined. Is up to the handler to deal with the -// rest of intents in chain. A maximum of 255 profiles at time are supported, which is pretty reasonable. -cmsPipeline* _cmsLinkProfiles(cmsContext ContextID, - cmsUInt32Number nProfiles, - cmsUInt32Number TheIntents[], - cmsHPROFILE hProfiles[], - cmsBool BPC[], - cmsFloat64Number AdaptationStates[], - cmsUInt32Number dwFlags) -{ - cmsUInt32Number i; - cmsIntentsList* Intent; - - // Make sure a reasonable number of profiles is provided - if (nProfiles <= 0 || nProfiles > 255) { - cmsSignalError(ContextID, cmsERROR_RANGE, "Couldn't link '%d' profiles", nProfiles); - return NULL; - } - - for (i=0; i < nProfiles; i++) { - - // Check if black point is really needed or allowed. Note that - // following Adobe's document: - // BPC does not apply to devicelink profiles, nor to abs colorimetric, - // and applies always on V4 perceptual and saturation. - - if (TheIntents[i] == INTENT_ABSOLUTE_COLORIMETRIC) - BPC[i] = FALSE; - - if (TheIntents[i] == INTENT_PERCEPTUAL || TheIntents[i] == INTENT_SATURATION) { - - // Force BPC for V4 profiles in perceptual and saturation - if (cmsGetProfileVersion(hProfiles[i]) >= 4.0) - BPC[i] = TRUE; - } - } - - // Search for a handler. The first intent in the chain defines the handler. That would - // prevent using multiple custom intents in a multiintent chain, but the behaviour of - // 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(ContextID, TheIntents[0]); - if (Intent == NULL) { - cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported intent '%d'", TheIntents[0]); - return NULL; - } - - // Call the handler - return Intent ->Link(ContextID, nProfiles, TheIntents, hProfiles, BPC, AdaptationStates, dwFlags); -} - -// ------------------------------------------------------------------------------------------------- - -// 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 cmsGetSupportedIntentsTHR(cmsContext ContextID, cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions) -{ - _cmsIntentsPluginChunkType* ctx = ( _cmsIntentsPluginChunkType*) _cmsContextGetClientChunk(ContextID, IntentPlugin); - cmsIntentsList* pt; - cmsUInt32Number nIntents; - - - for (nIntents=0, pt = ctx->Intents; pt != NULL; pt = pt -> Next) - { - if (nIntents < nMax) { - if (Codes != NULL) - Codes[nIntents] = pt ->Intent; - - if (Descriptions != NULL) - Descriptions[nIntents] = pt ->Description; - } - - 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 custom intents? - if (Data == NULL) { - - ctx->Intents = NULL; - return TRUE; - } - - fl = (cmsIntentsList*) _cmsPluginMalloc(id, sizeof(cmsIntentsList)); - if (fl == NULL) return FALSE; - - - fl ->Intent = Plugin ->Intent; - strncpy(fl ->Description, Plugin ->Description, sizeof(fl ->Description)-1); - fl ->Description[sizeof(fl ->Description)-1] = 0; - - fl ->Link = Plugin ->Link; - - fl ->Next = ctx ->Intents; - ctx ->Intents = fl; - - return TRUE; -} - diff --git a/third_party/lcms2-2.6/src/cmserr.c b/third_party/lcms2-2.6/src/cmserr.c deleted file mode 100644 index f9adc3824a..0000000000 --- a/third_party/lcms2-2.6/src/cmserr.c +++ /dev/null @@ -1,707 +0,0 @@ -//--------------------------------------------------------------------------------- -// -// 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" - -// I am so tired about incompatibilities on those functions that here are some replacements -// that hopefully would be fully portable. - -// 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; - - 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) -long int CMSEXPORT cmsfilelength(FILE* f) -{ - long int p , n; - - p = ftell(f); // register current file position - - if (fseek(f, 0, SEEK_END) != 0) { - return -1; - } - - n = ftell(f); - fseek(f, p, SEEK_SET); // file position restored - - return n; -} - -#if 0 -// Memory handling ------------------------------------------------------------------ -// -// 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 evil code to allocate huge blocks that otherwise lcms would never need. - -#define MAX_MEMORY_FOR_ALLOC ((cmsUInt32Number)(1024U*1024U*512U)) - -// User may override this behaviour by using a memory plug-in, which basically replaces -// the default memory management functions. In this case, no check is performed and it -// is up to the plug-in writter to keep in the safe side. There are only three functions -// 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(cmsContext ContextID, cmsPluginBase* Plugin); - -// ********************************************************************************* - -// This is the default memory allocation function. It does a very coarse -// check of amout of memory, just to prevent exploits -static -void* _cmsMallocDefaultFn(cmsContext ContextID, cmsUInt32Number size) -{ - if (size > MAX_MEMORY_FOR_ALLOC) return NULL; // Never allow over maximum - - return (void*) malloc(size); - - cmsUNUSED_PARAMETER(ContextID); -} - -// Generic allocate & zero -static -void* _cmsMallocZeroDefaultFn(cmsContext ContextID, cmsUInt32Number size) -{ - void *pt = _cmsMalloc(ContextID, size); - if (pt == NULL) return NULL; - - memset(pt, 0, size); - return pt; -} - - -// The default free function. The only check proformed is against NULL pointers -static -void _cmsFreeDefaultFn(cmsContext ContextID, void *Ptr) -{ - // free(NULL) is defined a no-op by C99, therefore it is safe to - // avoid the check, but it is here just in case... - - if (Ptr) free(Ptr); - - cmsUNUSED_PARAMETER(ContextID); -} - -// 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) -{ - - if (size > MAX_MEMORY_FOR_ALLOC) return NULL; // Never realloc over 512Mb - - return realloc(Ptr, size); - - cmsUNUSED_PARAMETER(ContextID); -} - - -// The default calloc function. Allocates an array of num elements, each one of size bytes -// all memory is initialized to zero. -static -void* _cmsCallocDefaultFn(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size) -{ - cmsUInt32Number Total = num * size; - - // Preserve calloc behaviour - if (Total == 0) return NULL; - - // Safe check for overflow. - if (num >= UINT_MAX / size) return NULL; - - // Check for overflow - if (Total < num || Total < size) { - return NULL; - } - - if (Total > MAX_MEMORY_FOR_ALLOC) return NULL; // Never alloc over 512Mb - - return _cmsMallocZero(ContextID, Total); -} - -// Generic block duplication -static -void* _cmsDupDefaultFn(cmsContext ContextID, const void* Org, cmsUInt32Number size) -{ - void* mem; - - if (size > MAX_MEMORY_FOR_ALLOC) return NULL; // Never dup over 512Mb - - mem = _cmsMalloc(ContextID, size); - - if (mem != NULL && Org != NULL) - memmove(mem, Org, size); - - return mem; -} - - -// 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(cmsContext ContextID, cmsPluginBase *Data) -{ - cmsPluginMemHandler* Plugin = (cmsPluginMemHandler*) Data; - _cmsMemPluginChunkType* ptr; - - // 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) { - - struct _cmsContext_struct* ctx = ( struct _cmsContext_struct*) ContextID; - - // Return to the default allocators - if (ContextID != NULL) { - ctx->chunks[MemPlugin] = (void*) &ctx->DefaultMemoryManager; - } - return TRUE; - } - - // Check for required callbacks - if (Plugin -> MallocPtr == NULL || - Plugin -> FreePtr == NULL || - Plugin -> ReallocPtr == NULL) return FALSE; - - // Set replacement functions - ptr = (_cmsMemPluginChunkType*) _cmsContextGetClientChunk(ContextID, MemPlugin); - if (ptr == NULL) - return FALSE; - - _cmsInstallAllocFunctions(Plugin, ptr); - return TRUE; -} -#else -#include "core/fxcrt/fx_memory.h" -#include "core/fxcrt/fx_system.h" - -cmsBool _cmsRegisterMemHandlerPlugin(cmsContext ContextID, cmsPluginBase* Plugin) -{ - return TRUE; -} - -// Generic allocate -void* CMSEXPORT _cmsMalloc(cmsContext ContextID, cmsUInt32Number size) -{ - return FXMEM_DefaultAlloc(size, 1); -} - -// Generic allocate & zero -void* CMSEXPORT _cmsMallocZero(cmsContext ContextID, cmsUInt32Number size) -{ - void* p = FXMEM_DefaultAlloc(size, 1); - if (p) memset(p, 0, size); - return p; -} - -// Generic calloc -void* CMSEXPORT _cmsCalloc(cmsContext ContextID, cmsUInt32Number num, cmsUInt32Number size) -{ - cmsUInt32Number total = num * size; - if (total == 0 || total / size != num || total >= 512 * 1024 * 1024) - return NULL; - - return _cmsMallocZero(ContextID, num * size); -} - -// Generic reallocate -void* CMSEXPORT _cmsRealloc(cmsContext ContextID, void* Ptr, cmsUInt32Number size) -{ - return FXMEM_DefaultRealloc(Ptr, size, 1); -} - -// Generic free memory -void CMSEXPORT _cmsFree(cmsContext ContextID, void* Ptr) -{ - if (Ptr != NULL) FXMEM_DefaultFree(Ptr, 0); -} - -// Generic block duplication -void* CMSEXPORT _cmsDupMem(cmsContext ContextID, const void* Org, cmsUInt32Number size) -{ - void* p = FXMEM_DefaultAlloc(size, 1); - memmove(p, Org, size); - return p; -} - -_cmsMemPluginChunkType _cmsMemPluginChunk = {_cmsMalloc, _cmsMallocZero, _cmsFree, - _cmsRealloc, _cmsCalloc, _cmsDupMem - }; - -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; - } -} - -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= _cmsMallocZero; - ptr ->CallocPtr = _cmsCalloc; - ptr ->DupPtr = _cmsDupMem; - - if (Plugin ->MallocZeroPtr != NULL) ptr ->MallocZeroPtr = Plugin -> MallocZeroPtr; - if (Plugin ->CallocPtr != NULL) ptr ->CallocPtr = Plugin -> CallocPtr; - if (Plugin ->DupPtr != NULL) ptr ->DupPtr = Plugin -> DupPtr; - - } -} -#endif - -// ******************************************************************************************** - -// Sub allocation takes care of many pointers of small size. The memory allocated in -// this way have be freed at once. Next function allocates a single chunk for linked list -// I prefer this method over realloc due to the big inpact on xput realloc may have if -// memory is being swapped to disk. This approach is safer (although that may not be true on all platforms) -static -_cmsSubAllocator_chunk* _cmsCreateSubAllocChunk(cmsContext ContextID, cmsUInt32Number Initial) -{ - _cmsSubAllocator_chunk* chunk; - - // 20K by default - if (Initial == 0) - Initial = 20*1024; - - // Create the container - chunk = (_cmsSubAllocator_chunk*) _cmsMallocZero(ContextID, sizeof(_cmsSubAllocator_chunk)); - if (chunk == NULL) return NULL; - - // Initialize values - chunk ->Block = (cmsUInt8Number*) _cmsMalloc(ContextID, Initial); - if (chunk ->Block == NULL) { - - // Something went wrong - _cmsFree(ContextID, chunk); - return NULL; - } - - chunk ->BlockSize = Initial; - chunk ->Used = 0; - chunk ->next = NULL; - - return chunk; -} - -// The suballocated is nothing but a pointer to the first element in the list. We also keep -// the thread ID in this structure. -_cmsSubAllocator* _cmsCreateSubAlloc(cmsContext ContextID, cmsUInt32Number Initial) -{ - _cmsSubAllocator* sub; - - // Create the container - sub = (_cmsSubAllocator*) _cmsMallocZero(ContextID, sizeof(_cmsSubAllocator)); - if (sub == NULL) return NULL; - - sub ->ContextID = ContextID; - - sub ->h = _cmsCreateSubAllocChunk(ContextID, Initial); - if (sub ->h == NULL) { - _cmsFree(ContextID, sub); - return NULL; - } - - return sub; -} - - -// Get rid of whole linked list -void _cmsSubAllocDestroy(_cmsSubAllocator* sub) -{ - _cmsSubAllocator_chunk *chunk, *n; - - for (chunk = sub ->h; chunk != NULL; chunk = n) { - - n = chunk->next; - if (chunk->Block != NULL) _cmsFree(sub ->ContextID, chunk->Block); - _cmsFree(sub ->ContextID, chunk); - } - - // Free the header - _cmsFree(sub ->ContextID, sub); -} - - -// Get a pointer to small memory block. -void* _cmsSubAlloc(_cmsSubAllocator* sub, cmsUInt32Number size) -{ - cmsUInt32Number Free = sub -> h ->BlockSize - sub -> h -> Used; - cmsUInt8Number* ptr; - - size = _cmsALIGNMEM(size); - - // Check for memory. If there is no room, allocate a new chunk of double memory size. - if (size > Free) { - - _cmsSubAllocator_chunk* chunk; - cmsUInt32Number newSize; - - newSize = sub -> h ->BlockSize * 2; - if (newSize < size) newSize = size; - - chunk = _cmsCreateSubAllocChunk(sub -> ContextID, newSize); - if (chunk == NULL) return NULL; - - // Link list - chunk ->next = sub ->h; - sub ->h = chunk; - - } - - ptr = sub -> h ->Block + sub -> h ->Used; - sub -> h -> Used += 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. -// For example, all create functions does return NULL on failure. Other return FALSE -// It may be interesting, for the developer, to know why the function is failing. -// for that reason, lcms2 does offer a logging function. This function does recive -// a ENGLISH string with some clues on what is going wrong. You can show this -// info to the end user, or just create some sort of log. -// The logging function should NOT terminate the program, as this obviously can leave -// resources. It is the programmer's responsability to check each function return code -// to make sure it didn't fail. - -// Error messages are limited to MAX_ERROR_MESSAGE_LEN - -#define MAX_ERROR_MESSAGE_LEN 1024 - -// --------------------------------------------------------------------------------------------------------- - -// This is our default log error -static void DefaultLogErrorHandlerFunction(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text); - -// 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 -void DefaultLogErrorHandlerFunction(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *Text) -{ - // fprintf(stderr, "[lcms]: %s\n", Text); - // fflush(stderr); - - cmsUNUSED_PARAMETER(ContextID); - cmsUNUSED_PARAMETER(ErrorCode); - cmsUNUSED_PARAMETER(Text); -} - -// 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) -{ - cmsSetLogErrorHandlerTHR(NULL, Fn); -} - -// Log an error -// ErrorText is a text holding an english description of error. -void CMSEXPORT cmsSignalError(cmsContext ContextID, cmsUInt32Number ErrorCode, const char *ErrorText, ...) -{ - 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); - - // 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 -void _cmsTagSignature2String(char String[5], cmsTagSignature sig) -{ - cmsUInt32Number be; - - // Convert to big endian - be = _cmsAdjustEndianess32((cmsUInt32Number) sig); - - // Move chars - memmove(String, &be, 4); - - // Make sure of terminator - String[4] = 0; -} - -//-------------------------------------------------------------------------------------------------- - - -static -void* defMtxCreate(cmsContext id) -{ - _cmsMutex* ptr_mutex = (_cmsMutex*) _cmsMalloc(id, sizeof(_cmsMutex)); - _cmsInitMutexPrimitive(ptr_mutex); - return (void*) ptr_mutex; -} - -static -void defMtxDestroy(cmsContext id, void* mtx) -{ - _cmsDestroyMutexPrimitive((_cmsMutex *) mtx); - _cmsFree(id, mtx); -} - -static -cmsBool defMtxLock(cmsContext id, void* mtx) -{ - cmsUNUSED_PARAMETER(id); - return _cmsLockPrimitive((_cmsMutex *) mtx) == 0; -} - -static -void defMtxUnlock(cmsContext id, void* mtx) -{ - cmsUNUSED_PARAMETER(id); - _cmsUnlockPrimitive((_cmsMutex *) mtx); -} - - - -// Pointers to memory manager functions in Context0 -_cmsMutexPluginChunkType _cmsMutexPluginChunk = { defMtxCreate, defMtxDestroy, defMtxLock, defMtxUnlock }; - -// Allocate and init mutex container. -void _cmsAllocMutexPluginChunk(struct _cmsContext_struct* ctx, - const struct _cmsContext_struct* src) -{ - static _cmsMutexPluginChunkType MutexChunk = {defMtxCreate, defMtxDestroy, defMtxLock, defMtxUnlock }; - 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/third_party/lcms2-2.6/src/cmsgamma.c b/third_party/lcms2-2.6/src/cmsgamma.c deleted file mode 100644 index 97aeb7cc16..0000000000 --- a/third_party/lcms2-2.6/src/cmsgamma.c +++ /dev/null @@ -1,1298 +0,0 @@ -//--------------------------------------------------------------------------------- -// -// Little Color Management System -// Copyright (c) 1998-2013 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" - -// Tone curves are powerful constructs that can contain curves specified in diverse ways. -// The curve is stored in segments, where each segment can be sampled or specified by parameters. -// a 16.bit simplification of the *whole* curve is kept for optimization purposes. For float operation, -// each segment is evaluated separately. Plug-ins may be used to define new parametric schemes, -// each plug-in may define up to MAX_TYPES_IN_LCMS_PLUGIN functions types. For defining a function, -// the plug-in should provide the type id, how many parameters each type has, and a pointer to -// a procedure that evaluates the function. In the case of reverse evaluation, the evaluator will -// be called with the type id as a negative value, and a sampled version of the reversed curve -// will be built. - -// ----------------------------------------------------------------- Implementation -// Maxim number of nodes -#define MAX_NODES_IN_CURVE 4097 -#define MINUS_INF (-1E22F) -#define PLUS_INF (+1E22F) - -// The list of supported parametric curves -typedef struct _cmsParametricCurvesCollection_st { - - int nFunctions; // Number of supported functions in this chunk - int FunctionTypes[MAX_TYPES_IN_LCMS_PLUGIN]; // The identification types - int ParameterCount[MAX_TYPES_IN_LCMS_PLUGIN]; // Number of parameters for each function - cmsParametricCurveEvaluator Evaluator; // The evaluator - - struct _cmsParametricCurvesCollection_st* Next; // Next in list - -} _cmsParametricCurvesCollection; - -// This is the default (built-in) evaluator -static cmsFloat64Number DefaultEvalParametricFn(cmsInt32Number Type, const cmsFloat64Number Params[], cmsFloat64Number R); - -// The built-in list -static _cmsParametricCurvesCollection DefaultCurves = { - 9, // # of curve types - { 1, 2, 3, 4, 5, 6, 7, 8, 108 }, // Parametric curve ID - { 1, 3, 4, 5, 7, 4, 5, 5, 1 }, // Parameters by type - DefaultEvalParametricFn, // Evaluator - 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 -_cmsCurvesPluginChunkType _cmsCurvesPluginChunk = { NULL }; - -// As a way to install new parametric curves -cmsBool _cmsRegisterParametricCurvesPlugin(cmsContext ContextID, cmsPluginBase* Data) -{ - _cmsCurvesPluginChunkType* ctx = ( _cmsCurvesPluginChunkType*) _cmsContextGetClientChunk(ContextID, CurvesPlugin); - cmsPluginParametricCurves* Plugin = (cmsPluginParametricCurves*) Data; - _cmsParametricCurvesCollection* fl; - - if (Data == NULL) { - - ctx -> ParametricCurves = NULL; - return TRUE; - } - - fl = (_cmsParametricCurvesCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsParametricCurvesCollection)); - if (fl == NULL) return FALSE; - - // Copy the parameters - fl ->Evaluator = Plugin ->Evaluator; - fl ->nFunctions = Plugin ->nFunctions; - - // Make sure no mem overwrites - if (fl ->nFunctions > MAX_TYPES_IN_LCMS_PLUGIN) - fl ->nFunctions = MAX_TYPES_IN_LCMS_PLUGIN; - - // Copy the data - memmove(fl->FunctionTypes, Plugin ->FunctionTypes, fl->nFunctions * sizeof(cmsUInt32Number)); - memmove(fl->ParameterCount, Plugin ->ParameterCount, fl->nFunctions * sizeof(cmsUInt32Number)); - - // Keep linked list - fl ->Next = ctx->ParametricCurves; - ctx->ParametricCurves = fl; - - // All is ok - return TRUE; -} - - -// Search in type list, return position or -1 if not found -static -int IsInSet(int Type, _cmsParametricCurvesCollection* c) -{ - int i; - - for (i=0; i < c ->nFunctions; i++) - if (abs(Type) == c ->FunctionTypes[i]) return i; - - return -1; -} - - -// Search for the collection which contains a specific type -static -_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); - - 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); - - if (Position != -1) { - if (index != NULL) - *index = Position; - return c; - } - } - - return NULL; -} - -// Low level allocate, which takes care of memory details. nEntries may be zero, and in this case -// no optimation curve is computed. nSegments may also be zero in the inverse case, where only the -// optimization curve is given. Both features simultaneously is an error -static -cmsToneCurve* AllocateToneCurveStruct(cmsContext ContextID, cmsInt32Number nEntries, - cmsInt32Number nSegments, const cmsCurveSegment* Segments, - const cmsUInt16Number* Values) -{ - cmsToneCurve* p; - int i; - - // We allow huge tables, which are then restricted for smoothing operations - if (nEntries > 65530 || nEntries < 0) { - cmsSignalError(ContextID, cmsERROR_RANGE, "Couldn't create tone curve of more than 65530 entries"); - return NULL; - } - - if (nEntries <= 0 && nSegments <= 0) { - cmsSignalError(ContextID, cmsERROR_RANGE, "Couldn't create tone curve with zero segments and no table"); - return NULL; - } - - // Allocate all required pointers, etc. - p = (cmsToneCurve*) _cmsMallocZero(ContextID, sizeof(cmsToneCurve)); - if (!p) return NULL; - - // In this case, there are no segments - if (nSegments <= 0) { - p ->Segments = NULL; - p ->Evals = NULL; - } - else { - p ->Segments = (cmsCurveSegment*) _cmsCalloc(ContextID, nSegments, sizeof(cmsCurveSegment)); - if (p ->Segments == NULL) goto Error; - - p ->Evals = (cmsParametricCurveEvaluator*) _cmsCalloc(ContextID, nSegments, sizeof(cmsParametricCurveEvaluator)); - if (p ->Evals == NULL) goto Error; - } - - p -> nSegments = nSegments; - - // This 16-bit table contains a limited precision representation of the whole curve and is kept for - // increasing xput on certain operations. - if (nEntries <= 0) { - p ->Table16 = NULL; - } - else { - p ->Table16 = (cmsUInt16Number*) _cmsCalloc(ContextID, nEntries, sizeof(cmsUInt16Number)); - if (p ->Table16 == NULL) goto Error; - } - - p -> nEntries = nEntries; - - // Initialize members if requested - if (Values != NULL && (nEntries > 0)) { - - for (i=0; i < nEntries; i++) - p ->Table16[i] = Values[i]; - } - - // Initialize the segments stuff. The evaluator for each segment is located and a pointer to it - // is placed in advance to maximize performance. - if (Segments != NULL && (nSegments > 0)) { - - _cmsParametricCurvesCollection *c; - - p ->SegInterp = (cmsInterpParams**) _cmsCalloc(ContextID, nSegments, sizeof(cmsInterpParams*)); - if (p ->SegInterp == NULL) goto Error; - - for (i=0; i< nSegments; i++) { - - // Type 0 is a special marker for table-based curves - if (Segments[i].Type == 0) - p ->SegInterp[i] = _cmsComputeInterpParams(ContextID, Segments[i].nGridPoints, 1, 1, NULL, CMS_LERP_FLAGS_FLOAT); - - memmove(&p ->Segments[i], &Segments[i], sizeof(cmsCurveSegment)); - - if (Segments[i].Type == 0 && Segments[i].SampledPoints != NULL) - p ->Segments[i].SampledPoints = (cmsFloat32Number*) _cmsDupMem(ContextID, Segments[i].SampledPoints, sizeof(cmsFloat32Number) * Segments[i].nGridPoints); - else - p ->Segments[i].SampledPoints = NULL; - - - c = GetParametricCurveByType(ContextID, Segments[i].Type, NULL); - if (c != NULL) - p ->Evals[i] = c ->Evaluator; - } - } - - p ->InterpParams = _cmsComputeInterpParams(ContextID, p ->nEntries, 1, 1, p->Table16, CMS_LERP_FLAGS_16BITS); - if (p->InterpParams != NULL) - return p; - -Error: - if (p -> Segments) _cmsFree(ContextID, p ->Segments); - if (p -> Evals) _cmsFree(ContextID, p -> Evals); - if (p ->Table16) _cmsFree(ContextID, p ->Table16); - _cmsFree(ContextID, p); - return NULL; -} - - -// Parametric Fn using floating point -static -cmsFloat64Number DefaultEvalParametricFn(cmsInt32Number Type, const cmsFloat64Number Params[], cmsFloat64Number R) -{ - cmsFloat64Number e, Val, disc; - - switch (Type) { - - // X = Y ^ Gamma - case 1: - if (R < 0) { - - if (fabs(Params[0] - 1.0) < MATRIX_DET_TOLERANCE) - Val = R; - else - Val = 0; - } - else - Val = pow(R, Params[0]); - break; - - // Type 1 Reversed: X = Y ^1/gamma - case -1: - if (R < 0) { - - if (fabs(Params[0] - 1.0) < MATRIX_DET_TOLERANCE) - Val = R; - else - Val = 0; - } - else - Val = pow(R, 1/Params[0]); - break; - - // CIE 122-1966 - // Y = (aX + b)^Gamma | X >= -b/a - // Y = 0 | else - case 2: - disc = -Params[2] / Params[1]; - - if (R >= disc ) { - - e = Params[1]*R + Params[2]; - - if (e > 0) - Val = pow(e, Params[0]); - else - Val = 0; - } - else - Val = 0; - break; - - // Type 2 Reversed - // X = (Y ^1/g - b) / a - case -2: - if (R < 0) - Val = 0; - else - Val = (pow(R, 1.0/Params[0]) - Params[2]) / Params[1]; - - if (Val < 0) - Val = 0; - break; - - - // IEC 61966-3 - // Y = (aX + b)^Gamma | X <= -b/a - // Y = c | else - case 3: - disc = -Params[2] / Params[1]; - if (disc < 0) - disc = 0; - - if (R >= disc) { - - e = Params[1]*R + Params[2]; - - if (e > 0) - Val = pow(e, Params[0]) + Params[3]; - else - Val = 0; - } - else - Val = Params[3]; - break; - - - // Type 3 reversed - // X=((Y-c)^1/g - b)/a | (Y>=c) - // X=-b/a | (Y= Params[3]) { - - e = R - Params[3]; - - if (e > 0) - Val = (pow(e, 1/Params[0]) - Params[2]) / Params[1]; - else - Val = 0; - } - else { - Val = -Params[2] / Params[1]; - } - break; - - - // IEC 61966-2.1 (sRGB) - // Y = (aX + b)^Gamma | X >= d - // Y = cX | X < d - case 4: - if (R >= Params[4]) { - - e = Params[1]*R + Params[2]; - - if (e > 0) - Val = pow(e, Params[0]); - else - Val = 0; - } - else - Val = R * Params[3]; - break; - - // Type 4 reversed - // X=((Y^1/g-b)/a) | Y >= (ad+b)^g - // X=Y/c | Y< (ad+b)^g - case -4: - e = Params[1] * Params[4] + Params[2]; - if (e < 0) - disc = 0; - else - disc = pow(e, Params[0]); - - if (R >= disc) { - - Val = (pow(R, 1.0/Params[0]) - Params[2]) / Params[1]; - } - else { - Val = R / Params[3]; - } - break; - - - // Y = (aX + b)^Gamma + e | X >= d - // Y = cX + f | X < d - case 5: - if (R >= Params[4]) { - - e = Params[1]*R + Params[2]; - - if (e > 0) - Val = pow(e, Params[0]) + Params[5]; - else - Val = Params[5]; - } - else - Val = R*Params[3] + Params[6]; - break; - - - // Reversed type 5 - // X=((Y-e)1/g-b)/a | Y >=(ad+b)^g+e), cd+f - // X=(Y-f)/c | else - case -5: - - disc = Params[3] * Params[4] + Params[6]; - if (R >= disc) { - - e = R - Params[5]; - if (e < 0) - Val = 0; - else - Val = (pow(e, 1.0/Params[0]) - Params[2]) / Params[1]; - } - else { - Val = (R - Params[6]) / Params[3]; - } - break; - - - // Types 6,7,8 comes from segmented curves as described in ICCSpecRevision_02_11_06_Float.pdf - // Type 6 is basically identical to type 5 without d - - // Y = (a * X + b) ^ Gamma + c - case 6: - e = Params[1]*R + Params[2]; - - if (e < 0) - Val = Params[3]; - else - Val = pow(e, Params[0]) + Params[3]; - break; - - // ((Y - c) ^1/Gamma - b) / a - case -6: - e = R - Params[3]; - if (e < 0) - Val = 0; - else - Val = (pow(e, 1.0/Params[0]) - Params[2]) / Params[1]; - break; - - - // Y = a * log (b * X^Gamma + c) + d - case 7: - - e = Params[2] * pow(R, Params[0]) + Params[3]; - if (e <= 0) - Val = Params[4]; - else - Val = Params[1]*log10(e) + Params[4]; - break; - - // (Y - d) / a = log(b * X ^Gamma + c) - // pow(10, (Y-d) / a) = b * X ^Gamma + c - // pow((pow(10, (Y-d) / a) - c) / b, 1/g) = X - case -7: - Val = pow((pow(10.0, (R-Params[4]) / Params[1]) - Params[3]) / Params[2], 1.0 / Params[0]); - break; - - - //Y = a * b^(c*X+d) + e - case 8: - Val = (Params[0] * pow(Params[1], Params[2] * R + Params[3]) + Params[4]); - break; - - - // Y = (log((y-e) / a) / log(b) - d ) / c - // a=0, b=1, c=2, d=3, e=4, - case -8: - - disc = R - Params[4]; - if (disc < 0) Val = 0; - else - Val = (log(disc / Params[0]) / log(Params[1]) - Params[3]) / Params[2]; - break; - - // S-Shaped: (1 - (1-x)^1/g)^1/g - case 108: - Val = pow(1.0 - pow(1 - R, 1/Params[0]), 1/Params[0]); - break; - - // y = (1 - (1-x)^1/g)^1/g - // y^g = (1 - (1-x)^1/g) - // 1 - y^g = (1-x)^1/g - // (1 - y^g)^g = 1 - x - // 1 - (1 - y^g)^g - case -108: - Val = 1 - pow(1 - pow(R, Params[0]), Params[0]); - break; - - default: - // Unsupported parametric curve. Should never reach here - return 0; - } - - return Val; -} - -// Evaluate a segmented funtion for a single value. Return -1 if no valid segment found . -// If fn type is 0, perform an interpolation on the table -static -cmsFloat64Number EvalSegmentedFn(const cmsToneCurve *g, cmsFloat64Number R) -{ - int i; - - for (i = g ->nSegments-1; i >= 0 ; --i) { - - // Check for domain - if ((R > g ->Segments[i].x0) && (R <= g ->Segments[i].x1)) { - - // Type == 0 means segment is sampled - if (g ->Segments[i].Type == 0) { - - cmsFloat32Number R1 = (cmsFloat32Number) (R - g ->Segments[i].x0) / (g ->Segments[i].x1 - g ->Segments[i].x0); - cmsFloat32Number Out; - - // Setup the table (TODO: clean that) - g ->SegInterp[i]-> Table = g ->Segments[i].SampledPoints; - - g ->SegInterp[i] -> Interpolation.LerpFloat(&R1, &Out, g ->SegInterp[i]); - - return Out; - } - else - return g ->Evals[i](g->Segments[i].Type, g ->Segments[i].Params, R); - } - } - - return MINUS_INF; -} - -// Access to estimated low-res table -cmsUInt32Number CMSEXPORT cmsGetToneCurveEstimatedTableEntries(const cmsToneCurve* t) -{ - _cmsAssert(t != NULL); - return t ->nEntries; -} - -const cmsUInt16Number* CMSEXPORT cmsGetToneCurveEstimatedTable(const cmsToneCurve* t) -{ - _cmsAssert(t != NULL); - return t ->Table16; -} - - -// Create an empty gamma curve, by using tables. This specifies only the limited-precision part, and leaves the -// floating point description empty. -cmsToneCurve* CMSEXPORT cmsBuildTabulatedToneCurve16(cmsContext ContextID, cmsInt32Number nEntries, const cmsUInt16Number Values[]) -{ - return AllocateToneCurveStruct(ContextID, nEntries, 0, NULL, Values); -} - -static -int EntriesByGamma(cmsFloat64Number Gamma) -{ - if (fabs(Gamma - 1.0) < 0.001) return 2; - return 4096; -} - - -// Create a segmented gamma, fill the table -cmsToneCurve* CMSEXPORT cmsBuildSegmentedToneCurve(cmsContext ContextID, - cmsInt32Number nSegments, const cmsCurveSegment Segments[]) -{ - int i; - cmsFloat64Number R, Val; - cmsToneCurve* g; - int nGridPoints = 4096; - - _cmsAssert(Segments != NULL); - - // Optimizatin for identity curves. - if (nSegments == 1 && Segments[0].Type == 1) { - - nGridPoints = EntriesByGamma(Segments[0].Params[0]); - } - - g = AllocateToneCurveStruct(ContextID, nGridPoints, nSegments, Segments, NULL); - if (g == NULL) return NULL; - - // Once we have the floating point version, we can approximate a 16 bit table of 4096 entries - // for performance reasons. This table would normally not be used except on 8/16 bits transforms. - for (i=0; i < nGridPoints; i++) { - - R = (cmsFloat64Number) i / (nGridPoints-1); - - Val = EvalSegmentedFn(g, R); - - // Round and saturate - g ->Table16[i] = _cmsQuickSaturateWord(Val * 65535.0); - } - - return g; -} - -// Use a segmented curve to store the floating point table -cmsToneCurve* CMSEXPORT cmsBuildTabulatedToneCurveFloat(cmsContext ContextID, cmsUInt32Number nEntries, const cmsFloat32Number values[]) -{ - cmsCurveSegment Seg[3]; - - // A segmented tone curve should have function segments in the first and last positions - // Initialize segmented curve part up to 0 to constant value = samples[0] - Seg[0].x0 = MINUS_INF; - Seg[0].x1 = 0; - Seg[0].Type = 6; - - Seg[0].Params[0] = 1; - Seg[0].Params[1] = 0; - Seg[0].Params[2] = 0; - Seg[0].Params[3] = values[0]; - Seg[0].Params[4] = 0; - - // From zero to 1 - Seg[1].x0 = 0; - Seg[1].x1 = 1.0; - Seg[1].Type = 0; - - Seg[1].nGridPoints = nEntries; - Seg[1].SampledPoints = (cmsFloat32Number*) values; - - // Final segment is constant = lastsample - Seg[2].x0 = 1.0; - Seg[2].x1 = PLUS_INF; - Seg[2].Type = 6; - - Seg[2].Params[0] = 1; - Seg[2].Params[1] = 0; - Seg[2].Params[2] = 0; - Seg[2].Params[3] = values[nEntries-1]; - Seg[2].Params[4] = 0; - - - return cmsBuildSegmentedToneCurve(ContextID, 3, Seg); -} - -// Parametric curves -// -// Parameters goes as: Curve, a, b, c, d, e, f -// Type is the ICC type +1 -// if type is negative, then the curve is analyticaly inverted -cmsToneCurve* CMSEXPORT cmsBuildParametricToneCurve(cmsContext ContextID, cmsInt32Number Type, const cmsFloat64Number Params[]) -{ - cmsCurveSegment Seg0; - int Pos = 0; - cmsUInt32Number size; - _cmsParametricCurvesCollection* c = GetParametricCurveByType(ContextID, Type, &Pos); - - _cmsAssert(Params != NULL); - - if (c == NULL) { - cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Invalid parametric curve type %d", Type); - return NULL; - } - - memset(&Seg0, 0, sizeof(Seg0)); - - Seg0.x0 = MINUS_INF; - Seg0.x1 = PLUS_INF; - Seg0.Type = Type; - - size = c->ParameterCount[Pos] * sizeof(cmsFloat64Number); - memmove(Seg0.Params, Params, size); - - return cmsBuildSegmentedToneCurve(ContextID, 1, &Seg0); -} - - - -// Build a gamma table based on gamma constant -cmsToneCurve* CMSEXPORT cmsBuildGamma(cmsContext ContextID, cmsFloat64Number Gamma) -{ - return cmsBuildParametricToneCurve(ContextID, 1, &Gamma); -} - - -// Free all memory taken by the gamma curve -void CMSEXPORT cmsFreeToneCurve(cmsToneCurve* Curve) -{ - cmsContext ContextID; - - // added by Xiaochuan Liu - // Curve->InterpParams may be null - if (Curve == NULL || Curve->InterpParams == NULL) return; - - ContextID = Curve ->InterpParams->ContextID; - - _cmsFreeInterpParams(Curve ->InterpParams); - Curve ->InterpParams = NULL; - - if (Curve -> Table16) - { - _cmsFree(ContextID, Curve ->Table16); - Curve ->Table16 = NULL; - } - - if (Curve ->Segments) { - - cmsUInt32Number i; - - for (i=0; i < Curve ->nSegments; i++) { - - if (Curve ->Segments[i].SampledPoints) { - _cmsFree(ContextID, Curve ->Segments[i].SampledPoints); - Curve ->Segments[i].SampledPoints = NULL; - } - - if (Curve ->SegInterp[i] != 0) - { - _cmsFreeInterpParams(Curve->SegInterp[i]); - Curve->SegInterp[i] = NULL; - } - } - - _cmsFree(ContextID, Curve ->Segments); - Curve ->Segments = NULL; - _cmsFree(ContextID, Curve ->SegInterp); - Curve ->SegInterp = NULL; - } - - if (Curve -> Evals) - { - _cmsFree(ContextID, Curve -> Evals); - Curve -> Evals = NULL; - } - - if (Curve) - { - _cmsFree(ContextID, Curve); - Curve = NULL; - } -} - -// Utility function, free 3 gamma tables -void CMSEXPORT cmsFreeToneCurveTriple(cmsToneCurve* Curve[3]) -{ - - _cmsAssert(Curve != NULL); - - if (Curve[0] != NULL) cmsFreeToneCurve(Curve[0]); - if (Curve[1] != NULL) cmsFreeToneCurve(Curve[1]); - if (Curve[2] != NULL) cmsFreeToneCurve(Curve[2]); - - Curve[0] = Curve[1] = Curve[2] = NULL; -} - - -// Duplicate a gamma table -cmsToneCurve* CMSEXPORT cmsDupToneCurve(const cmsToneCurve* In) -{ - // Xiaochuan Liu - // fix openpdf bug(mantis id:0055683, google id:360198) - // the function CurveSetElemTypeFree in cmslut.c also needs to check pointer - if (In == NULL || In ->InterpParams == NULL || In ->Segments == NULL || In ->Table16 == NULL) return NULL; - - return AllocateToneCurveStruct(In ->InterpParams ->ContextID, In ->nEntries, In ->nSegments, In ->Segments, In ->Table16); -} - -// Joins two curves for X and Y. Curves should be monotonic. -// We want to get -// -// y = Y^-1(X(t)) -// -cmsToneCurve* CMSEXPORT cmsJoinToneCurve(cmsContext ContextID, - const cmsToneCurve* X, - const cmsToneCurve* Y, cmsUInt32Number nResultingPoints) -{ - cmsToneCurve* out = NULL; - cmsToneCurve* Yreversed = NULL; - cmsFloat32Number t, x; - cmsFloat32Number* Res = NULL; - cmsUInt32Number i; - - - _cmsAssert(X != NULL); - _cmsAssert(Y != NULL); - - Yreversed = cmsReverseToneCurveEx(nResultingPoints, Y); - if (Yreversed == NULL) goto Error; - - Res = (cmsFloat32Number*) _cmsCalloc(ContextID, nResultingPoints, sizeof(cmsFloat32Number)); - if (Res == NULL) goto Error; - - //Iterate - for (i=0; i < nResultingPoints; i++) { - - t = (cmsFloat32Number) i / (nResultingPoints-1); - x = cmsEvalToneCurveFloat(X, t); - Res[i] = cmsEvalToneCurveFloat(Yreversed, x); - } - - // Allocate space for output - out = cmsBuildTabulatedToneCurveFloat(ContextID, nResultingPoints, Res); - -Error: - - if (Res != NULL) _cmsFree(ContextID, Res); - if (Yreversed != NULL) cmsFreeToneCurve(Yreversed); - - return out; -} - - - -// Get the surrounding nodes. This is tricky on non-monotonic tables -static -int GetInterval(cmsFloat64Number In, const cmsUInt16Number LutTable[], const struct _cms_interp_struc* p) -{ - int i; - int y0, y1; - - // A 1 point table is not allowed - if (p -> Domain[0] < 1) return -1; - - // Let's see if ascending or descending. - if (LutTable[0] < LutTable[p ->Domain[0]]) { - - // Table is overall ascending - for (i=p->Domain[0]-1; i >=0; --i) { - - y0 = LutTable[i]; - y1 = LutTable[i+1]; - - if (y0 <= y1) { // Increasing - if (In >= y0 && In <= y1) return i; - } - else - if (y1 < y0) { // Decreasing - if (In >= y1 && In <= y0) return i; - } - } - } - else { - // Table is overall descending - for (i=0; i < (int) p -> Domain[0]; i++) { - - y0 = LutTable[i]; - y1 = LutTable[i+1]; - - if (y0 <= y1) { // Increasing - if (In >= y0 && In <= y1) return i; - } - else - if (y1 < y0) { // Decreasing - if (In >= y1 && In <= y0) return i; - } - } - } - - return -1; -} - -// Reverse a gamma table -cmsToneCurve* CMSEXPORT cmsReverseToneCurveEx(cmsInt32Number nResultSamples, const cmsToneCurve* InCurve) -{ - cmsToneCurve *out; - cmsFloat64Number a = 0, b = 0, y, x1, y1, x2, y2; - int i, j; - int Ascending; - - _cmsAssert(InCurve != NULL); - - // Try to reverse it analytically whatever possible - - 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), - InCurve -> Segments[0].Params); - } - - // Nope, reverse the table. - out = cmsBuildTabulatedToneCurve16(InCurve ->InterpParams->ContextID, nResultSamples, NULL); - if (out == NULL) - return NULL; - - // We want to know if this is an ascending or descending table - Ascending = !cmsIsToneCurveDescending(InCurve); - - // Iterate across Y axis - for (i=0; i < nResultSamples; i++) { - - y = (cmsFloat64Number) i * 65535.0 / (nResultSamples - 1); - - // Find interval in which y is within. - j = GetInterval(y, InCurve->Table16, InCurve->InterpParams); - if (j >= 0) { - - - // Get limits of interval - x1 = InCurve ->Table16[j]; - x2 = InCurve ->Table16[j+1]; - - y1 = (cmsFloat64Number) (j * 65535.0) / (InCurve ->nEntries - 1); - y2 = (cmsFloat64Number) ((j+1) * 65535.0 ) / (InCurve ->nEntries - 1); - - // If collapsed, then use any - if (x1 == x2) { - - out ->Table16[i] = _cmsQuickSaturateWord(Ascending ? y2 : y1); - continue; - - } else { - - // Interpolate - a = (y2 - y1) / (x2 - x1); - b = y2 - a * x2; - } - } - - out ->Table16[i] = _cmsQuickSaturateWord(a* y + b); - } - - - return out; -} - -// Reverse a gamma table -cmsToneCurve* CMSEXPORT cmsReverseToneCurve(const cmsToneCurve* InGamma) -{ - _cmsAssert(InGamma != NULL); - - return cmsReverseToneCurveEx(4096, InGamma); -} - -// From: Eilers, P.H.C. (1994) Smoothing and interpolation with finite -// differences. in: Graphic Gems IV, Heckbert, P.S. (ed.), Academic press. -// -// Smoothing and interpolation with second differences. -// -// Input: weights (w), data (y): vector from 1 to m. -// Input: smoothing parameter (lambda), length (m). -// Output: smoothed vector (z): vector from 1 to m. - -static -cmsBool smooth2(cmsContext ContextID, cmsFloat32Number w[], cmsFloat32Number y[], cmsFloat32Number z[], cmsFloat32Number lambda, int m) -{ - int i, i1, i2; - cmsFloat32Number *c, *d, *e; - cmsBool st; - - - c = (cmsFloat32Number*) _cmsCalloc(ContextID, MAX_NODES_IN_CURVE, sizeof(cmsFloat32Number)); - d = (cmsFloat32Number*) _cmsCalloc(ContextID, MAX_NODES_IN_CURVE, sizeof(cmsFloat32Number)); - e = (cmsFloat32Number*) _cmsCalloc(ContextID, MAX_NODES_IN_CURVE, sizeof(cmsFloat32Number)); - - if (c != NULL && d != NULL && e != NULL) { - - - d[1] = w[1] + lambda; - c[1] = -2 * lambda / d[1]; - e[1] = lambda /d[1]; - z[1] = w[1] * y[1]; - d[2] = w[2] + 5 * lambda - d[1] * c[1] * c[1]; - c[2] = (-4 * lambda - d[1] * c[1] * e[1]) / d[2]; - e[2] = lambda / d[2]; - z[2] = w[2] * y[2] - c[1] * z[1]; - - for (i = 3; i < m - 1; i++) { - i1 = i - 1; i2 = i - 2; - d[i]= w[i] + 6 * lambda - c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2]; - c[i] = (-4 * lambda -d[i1] * c[i1] * e[i1])/ d[i]; - e[i] = lambda / d[i]; - z[i] = w[i] * y[i] - c[i1] * z[i1] - e[i2] * z[i2]; - } - - i1 = m - 2; i2 = m - 3; - - d[m - 1] = w[m - 1] + 5 * lambda -c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2]; - c[m - 1] = (-2 * lambda - d[i1] * c[i1] * e[i1]) / d[m - 1]; - z[m - 1] = w[m - 1] * y[m - 1] - c[i1] * z[i1] - e[i2] * z[i2]; - i1 = m - 1; i2 = m - 2; - - d[m] = w[m] + lambda - c[i1] * c[i1] * d[i1] - e[i2] * e[i2] * d[i2]; - z[m] = (w[m] * y[m] - c[i1] * z[i1] - e[i2] * z[i2]) / d[m]; - z[m - 1] = z[m - 1] / d[m - 1] - c[m - 1] * z[m]; - - for (i = m - 2; 1<= i; i--) - z[i] = z[i] / d[i] - c[i] * z[i + 1] - e[i] * z[i + 2]; - - st = TRUE; - } - else st = FALSE; - - if (c != NULL) _cmsFree(ContextID, c); - if (d != NULL) _cmsFree(ContextID, d); - if (e != NULL) _cmsFree(ContextID, e); - - return st; -} - -// Smooths a curve sampled at regular intervals. -cmsBool CMSEXPORT cmsSmoothToneCurve(cmsToneCurve* Tab, cmsFloat64Number lambda) -{ - cmsFloat32Number w[MAX_NODES_IN_CURVE], y[MAX_NODES_IN_CURVE], z[MAX_NODES_IN_CURVE]; - int i, nItems, Zeros, Poles; - - if (Tab == NULL) return FALSE; - - if (cmsIsToneCurveLinear(Tab)) return TRUE; // Nothing to do - - nItems = Tab -> nEntries; - - if (nItems >= MAX_NODES_IN_CURVE) { - cmsSignalError(Tab ->InterpParams->ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: too many points."); - return FALSE; - } - - memset(w, 0, nItems * sizeof(cmsFloat32Number)); - memset(y, 0, nItems * sizeof(cmsFloat32Number)); - memset(z, 0, nItems * sizeof(cmsFloat32Number)); - - for (i=0; i < nItems; i++) - { - y[i+1] = (cmsFloat32Number) Tab -> Table16[i]; - w[i+1] = 1.0; - } - - if (!smooth2(Tab ->InterpParams->ContextID, w, y, z, (cmsFloat32Number) lambda, nItems)) return FALSE; - - // Do some reality - checking... - Zeros = Poles = 0; - for (i=nItems; i > 1; --i) { - - if (z[i] == 0.) Zeros++; - if (z[i] >= 65535.) Poles++; - if (z[i] < z[i-1]) { - cmsSignalError(Tab ->InterpParams->ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Non-Monotonic."); - return FALSE; - } - } - - if (Zeros > (nItems / 3)) { - cmsSignalError(Tab ->InterpParams->ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Degenerated, mostly zeros."); - return FALSE; - } - if (Poles > (nItems / 3)) { - cmsSignalError(Tab ->InterpParams->ContextID, cmsERROR_RANGE, "cmsSmoothToneCurve: Degenerated, mostly poles."); - return FALSE; - } - - // Seems ok - for (i=0; i < nItems; i++) { - - // Clamp to cmsUInt16Number - Tab -> Table16[i] = _cmsQuickSaturateWord(z[i+1]); - } - - return TRUE; -} - -// Is a table linear? Do not use parametric since we cannot guarantee some weird parameters resulting -// in a linear table. This way assures it is linear in 12 bits, which should be enought in most cases. -cmsBool CMSEXPORT cmsIsToneCurveLinear(const cmsToneCurve* Curve) -{ - cmsUInt32Number i; - int diff; - - _cmsAssert(Curve != NULL); - - for (i=0; i < Curve ->nEntries; i++) { - - diff = abs((int) Curve->Table16[i] - (int) _cmsQuantizeVal(i, Curve ->nEntries)); - if (diff > 0x0f) - return FALSE; - } - - return TRUE; -} - -// Same, but for monotonicity -cmsBool CMSEXPORT cmsIsToneCurveMonotonic(const cmsToneCurve* t) -{ - int n; - int i, last; - cmsBool lDescending; - - _cmsAssert(t != NULL); - - // Degenerated curves are monotonic? Ok, let's pass them - n = t ->nEntries; - if (n < 2) return TRUE; - - // Curve direction - lDescending = cmsIsToneCurveDescending(t); - - if (lDescending) { - - last = t ->Table16[0]; - - for (i = 1; i < n; i++) { - - if (t ->Table16[i] - last > 2) // We allow some ripple - return FALSE; - else - last = t ->Table16[i]; - - } - } - else { - - last = t ->Table16[n-1]; - - for (i = n-2; i >= 0; --i) { - - if (t ->Table16[i] - last > 2) - return FALSE; - else - last = t ->Table16[i]; - - } - } - - return TRUE; -} - -// Same, but for descending tables -cmsBool CMSEXPORT cmsIsToneCurveDescending(const cmsToneCurve* t) -{ - _cmsAssert(t != NULL); - - return t ->Table16[0] > t ->Table16[t ->nEntries-1]; -} - - -// Another info fn: is out gamma table multisegment? -cmsBool CMSEXPORT cmsIsToneCurveMultisegment(const cmsToneCurve* t) -{ - _cmsAssert(t != NULL); - - return t -> nSegments > 1; -} - -cmsInt32Number CMSEXPORT cmsGetToneCurveParametricType(const cmsToneCurve* t) -{ - _cmsAssert(t != NULL); - - if (t -> nSegments != 1) return 0; - return t ->Segments[0].Type; -} - -// We need accuracy this time -cmsFloat32Number CMSEXPORT cmsEvalToneCurveFloat(const cmsToneCurve* Curve, cmsFloat32Number v) -{ - _cmsAssert(Curve != NULL); - - // Check for 16 bits table. If so, this is a limited-precision tone curve - if (Curve ->nSegments == 0) { - - cmsUInt16Number In, Out; - - In = (cmsUInt16Number) _cmsQuickSaturateWord(v * 65535.0); - Out = cmsEvalToneCurve16(Curve, In); - - return (cmsFloat32Number) (Out / 65535.0); - } - - return (cmsFloat32Number) EvalSegmentedFn(Curve, v); -} - -// We need xput over here -cmsUInt16Number CMSEXPORT cmsEvalToneCurve16(const cmsToneCurve* Curve, cmsUInt16Number v) -{ - cmsUInt16Number out; - - _cmsAssert(Curve != NULL); - - Curve ->InterpParams ->Interpolation.Lerp16(&v, &out, Curve ->InterpParams); - return out; -} - - -// Least squares fitting. -// A mathematical procedure for finding the best-fitting curve to a given set of points by -// minimizing the sum of the squares of the offsets ("the residuals") of the points from the curve. -// The sum of the squares of the offsets is used instead of the offset absolute values because -// this allows the residuals to be treated as a continuous differentiable quantity. -// -// y = f(x) = x ^ g -// -// R = (yi - (xi^g)) -// R2 = (yi - (xi^g))2 -// SUM R2 = SUM (yi - (xi^g))2 -// -// dR2/dg = -2 SUM x^g log(x)(y - x^g) -// solving for dR2/dg = 0 -// -// g = 1/n * SUM(log(y) / log(x)) - -cmsFloat64Number CMSEXPORT cmsEstimateGamma(const cmsToneCurve* t, cmsFloat64Number Precision) -{ - cmsFloat64Number gamma, sum, sum2; - cmsFloat64Number n, x, y, Std; - cmsUInt32Number i; - - _cmsAssert(t != NULL); - - sum = sum2 = n = 0; - - // Excluding endpoints - for (i=1; i < (MAX_NODES_IN_CURVE-1); i++) { - - x = (cmsFloat64Number) i / (MAX_NODES_IN_CURVE-1); - y = (cmsFloat64Number) cmsEvalToneCurveFloat(t, (cmsFloat32Number) x); - - // Avoid 7% on lower part to prevent - // artifacts due to linear ramps - - if (y > 0. && y < 1. && x > 0.07) { - - gamma = log(y) / log(x); - sum += gamma; - sum2 += gamma * gamma; - n++; - } - } - - // Take a look on SD to see if gamma isn't exponential at all - Std = sqrt((n * sum2 - sum * sum) / (n*(n-1))); - - if (Std > Precision) - return -1.0; - - return (sum / n); // The mean -} diff --git a/third_party/lcms2-2.6/src/cmsgmt.c b/third_party/lcms2-2.6/src/cmsgmt.c deleted file mode 100644 index 1103363a78..0000000000 --- a/third_party/lcms2-2.6/src/cmsgmt.c +++ /dev/null @@ -1,590 +0,0 @@ -//--------------------------------------------------------------------------------- -// -// 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/third_party/lcms2-2.6/src/cmshalf.c b/third_party/lcms2-2.6/src/cmshalf.c deleted file mode 100644 index f038b57b4c..0000000000 --- a/third_party/lcms2-2.6/src/cmshalf.c +++ /dev/null @@ -1,534 +0,0 @@ -//--------------------------------------------------------------------------------- -// -// 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" - -#ifndef CMS_NO_HALF_SUPPORT - -// This code is inspired in the paper "Fast Half Float Conversions" -// by Jeroen van der Zijp - -static cmsUInt32Number Mantissa[2048] = { - -0x00000000, 0x33800000, 0x34000000, 0x34400000, 0x34800000, 0x34a00000, -0x34c00000, 0x34e00000, 0x35000000, 0x35100000, 0x35200000, 0x35300000, -0x35400000, 0x35500000, 0x35600000, 0x35700000, 0x35800000, 0x35880000, -0x35900000, 0x35980000, 0x35a00000, 0x35a80000, 0x35b00000, 0x35b80000, -0x35c00000, 0x35c80000, 0x35d00000, 0x35d80000, 0x35e00000, 0x35e80000, -0x35f00000, 0x35f80000, 0x36000000, 0x36040000, 0x36080000, 0x360c0000, -0x36100000, 0x36140000, 0x36180000, 0x361c0000, 0x36200000, 0x36240000, -0x36280000, 0x362c0000, 0x36300000, 0x36340000, 0x36380000, 0x363c0000, -0x36400000, 0x36440000, 0x36480000, 0x364c0000, 0x36500000, 0x36540000, -0x36580000, 0x365c0000, 0x36600000, 0x36640000, 0x36680000, 0x366c0000, -0x36700000, 0x36740000, 0x36780000, 0x367c0000, 0x36800000, 0x36820000, -0x36840000, 0x36860000, 0x36880000, 0x368a0000, 0x368c0000, 0x368e0000, -0x36900000, 0x36920000, 0x36940000, 0x36960000, 0x36980000, 0x369a0000, -0x369c0000, 0x369e0000, 0x36a00000, 0x36a20000, 0x36a40000, 0x36a60000, -0x36a80000, 0x36aa0000, 0x36ac0000, 0x36ae0000, 0x36b00000, 0x36b20000, -0x36b40000, 0x36b60000, 0x36b80000, 0x36ba0000, 0x36bc0000, 0x36be0000, -0x36c00000, 0x36c20000, 0x36c40000, 0x36c60000, 0x36c80000, 0x36ca0000, -0x36cc0000, 0x36ce0000, 0x36d00000, 0x36d20000, 0x36d40000, 0x36d60000, -0x36d80000, 0x36da0000, 0x36dc0000, 0x36de0000, 0x36e00000, 0x36e20000, -0x36e40000, 0x36e60000, 0x36e80000, 0x36ea0000, 0x36ec0000, 0x36ee0000, -0x36f00000, 0x36f20000, 0x36f40000, 0x36f60000, 0x36f80000, 0x36fa0000, -0x36fc0000, 0x36fe0000, 0x37000000, 0x37010000, 0x37020000, 0x37030000, -0x37040000, 0x37050000, 0x37060000, 0x37070000, 0x37080000, 0x37090000, -0x370a0000, 0x370b0000, 0x370c0000, 0x370d0000, 0x370e0000, 0x370f0000, -0x37100000, 0x37110000, 0x37120000, 0x37130000, 0x37140000, 0x37150000, -0x37160000, 0x37170000, 0x37180000, 0x37190000, 0x371a0000, 0x371b0000, -0x371c0000, 0x371d0000, 0x371e0000, 0x371f0000, 0x37200000, 0x37210000, -0x37220000, 0x37230000, 0x37240000, 0x37250000, 0x37260000, 0x37270000, -0x37280000, 0x37290000, 0x372a0000, 0x372b0000, 0x372c0000, 0x372d0000, -0x372e0000, 0x372f0000, 0x37300000, 0x37310000, 0x37320000, 0x37330000, -0x37340000, 0x37350000, 0x37360000, 0x37370000, 0x37380000, 0x37390000, -0x373a0000, 0x373b0000, 0x373c0000, 0x373d0000, 0x373e0000, 0x373f0000, -0x37400000, 0x37410000, 0x37420000, 0x37430000, 0x37440000, 0x37450000, -0x37460000, 0x37470000, 0x37480000, 0x37490000, 0x374a0000, 0x374b0000, -0x374c0000, 0x374d0000, 0x374e0000, 0x374f0000, 0x37500000, 0x37510000, -0x37520000, 0x37530000, 0x37540000, 0x37550000, 0x37560000, 0x37570000, -0x37580000, 0x37590000, 0x375a0000, 0x375b0000, 0x375c0000, 0x375d0000, -0x375e0000, 0x375f0000, 0x37600000, 0x37610000, 0x37620000, 0x37630000, -0x37640000, 0x37650000, 0x37660000, 0x37670000, 0x37680000, 0x37690000, -0x376a0000, 0x376b0000, 0x376c0000, 0x376d0000, 0x376e0000, 0x376f0000, -0x37700000, 0x37710000, 0x37720000, 0x37730000, 0x37740000, 0x37750000, -0x37760000, 0x37770000, 0x37780000, 0x37790000, 0x377a0000, 0x377b0000, -0x377c0000, 0x377d0000, 0x377e0000, 0x377f0000, 0x37800000, 0x37808000, -0x37810000, 0x37818000, 0x37820000, 0x37828000, 0x37830000, 0x37838000, -0x37840000, 0x37848000, 0x37850000, 0x37858000, 0x37860000, 0x37868000, -0x37870000, 0x37878000, 0x37880000, 0x37888000, 0x37890000, 0x37898000, -0x378a0000, 0x378a8000, 0x378b0000, 0x378b8000, 0x378c0000, 0x378c8000, -0x378d0000, 0x378d8000, 0x378e0000, 0x378e8000, 0x378f0000, 0x378f8000, -0x37900000, 0x37908000, 0x37910000, 0x37918000, 0x37920000, 0x37928000, -0x37930000, 0x37938000, 0x37940000, 0x37948000, 0x37950000, 0x37958000, -0x37960000, 0x37968000, 0x37970000, 0x37978000, 0x37980000, 0x37988000, -0x37990000, 0x37998000, 0x379a0000, 0x379a8000, 0x379b0000, 0x379b8000, -0x379c0000, 0x379c8000, 0x379d0000, 0x379d8000, 0x379e0000, 0x379e8000, -0x379f0000, 0x379f8000, 0x37a00000, 0x37a08000, 0x37a10000, 0x37a18000, -0x37a20000, 0x37a28000, 0x37a30000, 0x37a38000, 0x37a40000, 0x37a48000, -0x37a50000, 0x37a58000, 0x37a60000, 0x37a68000, 0x37a70000, 0x37a78000, -0x37a80000, 0x37a88000, 0x37a90000, 0x37a98000, 0x37aa0000, 0x37aa8000, -0x37ab0000, 0x37ab8000, 0x37ac0000, 0x37ac8000, 0x37ad0000, 0x37ad8000, -0x37ae0000, 0x37ae8000, 0x37af0000, 0x37af8000, 0x37b00000, 0x37b08000, -0x37b10000, 0x37b18000, 0x37b20000, 0x37b28000, 0x37b30000, 0x37b38000, -0x37b40000, 0x37b48000, 0x37b50000, 0x37b58000, 0x37b60000, 0x37b68000, -0x37b70000, 0x37b78000, 0x37b80000, 0x37b88000, 0x37b90000, 0x37b98000, -0x37ba0000, 0x37ba8000, 0x37bb0000, 0x37bb8000, 0x37bc0000, 0x37bc8000, -0x37bd0000, 0x37bd8000, 0x37be0000, 0x37be8000, 0x37bf0000, 0x37bf8000, -0x37c00000, 0x37c08000, 0x37c10000, 0x37c18000, 0x37c20000, 0x37c28000, -0x37c30000, 0x37c38000, 0x37c40000, 0x37c48000, 0x37c50000, 0x37c58000, -0x37c60000, 0x37c68000, 0x37c70000, 0x37c78000, 0x37c80000, 0x37c88000, -0x37c90000, 0x37c98000, 0x37ca0000, 0x37ca8000, 0x37cb0000, 0x37cb8000, -0x37cc0000, 0x37cc8000, 0x37cd0000, 0x37cd8000, 0x37ce0000, 0x37ce8000, -0x37cf0000, 0x37cf8000, 0x37d00000, 0x37d08000, 0x37d10000, 0x37d18000, -0x37d20000, 0x37d28000, 0x37d30000, 0x37d38000, 0x37d40000, 0x37d48000, -0x37d50000, 0x37d58000, 0x37d60000, 0x37d68000, 0x37d70000, 0x37d78000, -0x37d80000, 0x37d88000, 0x37d90000, 0x37d98000, 0x37da0000, 0x37da8000, -0x37db0000, 0x37db8000, 0x37dc0000, 0x37dc8000, 0x37dd0000, 0x37dd8000, -0x37de0000, 0x37de8000, 0x37df0000, 0x37df8000, 0x37e00000, 0x37e08000, -0x37e10000, 0x37e18000, 0x37e20000, 0x37e28000, 0x37e30000, 0x37e38000, -0x37e40000, 0x37e48000, 0x37e50000, 0x37e58000, 0x37e60000, 0x37e68000, -0x37e70000, 0x37e78000, 0x37e80000, 0x37e88000, 0x37e90000, 0x37e98000, -0x37ea0000, 0x37ea8000, 0x37eb0000, 0x37eb8000, 0x37ec0000, 0x37ec8000, -0x37ed0000, 0x37ed8000, 0x37ee0000, 0x37ee8000, 0x37ef0000, 0x37ef8000, -0x37f00000, 0x37f08000, 0x37f10000, 0x37f18000, 0x37f20000, 0x37f28000, -0x37f30000, 0x37f38000, 0x37f40000, 0x37f48000, 0x37f50000, 0x37f58000, -0x37f60000, 0x37f68000, 0x37f70000, 0x37f78000, 0x37f80000, 0x37f88000, -0x37f90000, 0x37f98000, 0x37fa0000, 0x37fa8000, 0x37fb0000, 0x37fb8000, -0x37fc0000, 0x37fc8000, 0x37fd0000, 0x37fd8000, 0x37fe0000, 0x37fe8000, -0x37ff0000, 0x37ff8000, 0x38000000, 0x38004000, 0x38008000, 0x3800c000, -0x38010000, 0x38014000, 0x38018000, 0x3801c000, 0x38020000, 0x38024000, -0x38028000, 0x3802c000, 0x38030000, 0x38034000, 0x38038000, 0x3803c000, -0x38040000, 0x38044000, 0x38048000, 0x3804c000, 0x38050000, 0x38054000, -0x38058000, 0x3805c000, 0x38060000, 0x38064000, 0x38068000, 0x3806c000, -0x38070000, 0x38074000, 0x38078000, 0x3807c000, 0x38080000, 0x38084000, -0x38088000, 0x3808c000, 0x38090000, 0x38094000, 0x38098000, 0x3809c000, -0x380a0000, 0x380a4000, 0x380a8000, 0x380ac000, 0x380b0000, 0x380b4000, -0x380b8000, 0x380bc000, 0x380c0000, 0x380c4000, 0x380c8000, 0x380cc000, -0x380d0000, 0x380d4000, 0x380d8000, 0x380dc000, 0x380e0000, 0x380e4000, -0x380e8000, 0x380ec000, 0x380f0000, 0x380f4000, 0x380f8000, 0x380fc000, -0x38100000, 0x38104000, 0x38108000, 0x3810c000, 0x38110000, 0x38114000, -0x38118000, 0x3811c000, 0x38120000, 0x38124000, 0x38128000, 0x3812c000, -0x38130000, 0x38134000, 0x38138000, 0x3813c000, 0x38140000, 0x38144000, -0x38148000, 0x3814c000, 0x38150000, 0x38154000, 0x38158000, 0x3815c000, -0x38160000, 0x38164000, 0x38168000, 0x3816c000, 0x38170000, 0x38174000, -0x38178000, 0x3817c000, 0x38180000, 0x38184000, 0x38188000, 0x3818c000, -0x38190000, 0x38194000, 0x38198000, 0x3819c000, 0x381a0000, 0x381a4000, -0x381a8000, 0x381ac000, 0x381b0000, 0x381b4000, 0x381b8000, 0x381bc000, -0x381c0000, 0x381c4000, 0x381c8000, 0x381cc000, 0x381d0000, 0x381d4000, -0x381d8000, 0x381dc000, 0x381e0000, 0x381e4000, 0x381e8000, 0x381ec000, -0x381f0000, 0x381f4000, 0x381f8000, 0x381fc000, 0x38200000, 0x38204000, -0x38208000, 0x3820c000, 0x38210000, 0x38214000, 0x38218000, 0x3821c000, -0x38220000, 0x38224000, 0x38228000, 0x3822c000, 0x38230000, 0x38234000, -0x38238000, 0x3823c000, 0x38240000, 0x38244000, 0x38248000, 0x3824c000, -0x38250000, 0x38254000, 0x38258000, 0x3825c000, 0x38260000, 0x38264000, -0x38268000, 0x3826c000, 0x38270000, 0x38274000, 0x38278000, 0x3827c000, -0x38280000, 0x38284000, 0x38288000, 0x3828c000, 0x38290000, 0x38294000, -0x38298000, 0x3829c000, 0x382a0000, 0x382a4000, 0x382a8000, 0x382ac000, -0x382b0000, 0x382b4000, 0x382b8000, 0x382bc000, 0x382c0000, 0x382c4000, -0x382c8000, 0x382cc000, 0x382d0000, 0x382d4000, 0x382d8000, 0x382dc000, -0x382e0000, 0x382e4000, 0x382e8000, 0x382ec000, 0x382f0000, 0x382f4000, -0x382f8000, 0x382fc000, 0x38300000, 0x38304000, 0x38308000, 0x3830c000, -0x38310000, 0x38314000, 0x38318000, 0x3831c000, 0x38320000, 0x38324000, -0x38328000, 0x3832c000, 0x38330000, 0x38334000, 0x38338000, 0x3833c000, -0x38340000, 0x38344000, 0x38348000, 0x3834c000, 0x38350000, 0x38354000, -0x38358000, 0x3835c000, 0x38360000, 0x38364000, 0x38368000, 0x3836c000, -0x38370000, 0x38374000, 0x38378000, 0x3837c000, 0x38380000, 0x38384000, -0x38388000, 0x3838c000, 0x38390000, 0x38394000, 0x38398000, 0x3839c000, -0x383a0000, 0x383a4000, 0x383a8000, 0x383ac000, 0x383b0000, 0x383b4000, -0x383b8000, 0x383bc000, 0x383c0000, 0x383c4000, 0x383c8000, 0x383cc000, -0x383d0000, 0x383d4000, 0x383d8000, 0x383dc000, 0x383e0000, 0x383e4000, -0x383e8000, 0x383ec000, 0x383f0000, 0x383f4000, 0x383f8000, 0x383fc000, -0x38400000, 0x38404000, 0x38408000, 0x3840c000, 0x38410000, 0x38414000, -0x38418000, 0x3841c000, 0x38420000, 0x38424000, 0x38428000, 0x3842c000, -0x38430000, 0x38434000, 0x38438000, 0x3843c000, 0x38440000, 0x38444000, -0x38448000, 0x3844c000, 0x38450000, 0x38454000, 0x38458000, 0x3845c000, -0x38460000, 0x38464000, 0x38468000, 0x3846c000, 0x38470000, 0x38474000, -0x38478000, 0x3847c000, 0x38480000, 0x38484000, 0x38488000, 0x3848c000, -0x38490000, 0x38494000, 0x38498000, 0x3849c000, 0x384a0000, 0x384a4000, -0x384a8000, 0x384ac000, 0x384b0000, 0x384b4000, 0x384b8000, 0x384bc000, -0x384c0000, 0x384c4000, 0x384c8000, 0x384cc000, 0x384d0000, 0x384d4000, -0x384d8000, 0x384dc000, 0x384e0000, 0x384e4000, 0x384e8000, 0x384ec000, -0x384f0000, 0x384f4000, 0x384f8000, 0x384fc000, 0x38500000, 0x38504000, -0x38508000, 0x3850c000, 0x38510000, 0x38514000, 0x38518000, 0x3851c000, -0x38520000, 0x38524000, 0x38528000, 0x3852c000, 0x38530000, 0x38534000, -0x38538000, 0x3853c000, 0x38540000, 0x38544000, 0x38548000, 0x3854c000, -0x38550000, 0x38554000, 0x38558000, 0x3855c000, 0x38560000, 0x38564000, -0x38568000, 0x3856c000, 0x38570000, 0x38574000, 0x38578000, 0x3857c000, -0x38580000, 0x38584000, 0x38588000, 0x3858c000, 0x38590000, 0x38594000, -0x38598000, 0x3859c000, 0x385a0000, 0x385a4000, 0x385a8000, 0x385ac000, -0x385b0000, 0x385b4000, 0x385b8000, 0x385bc000, 0x385c0000, 0x385c4000, -0x385c8000, 0x385cc000, 0x385d0000, 0x385d4000, 0x385d8000, 0x385dc000, -0x385e0000, 0x385e4000, 0x385e8000, 0x385ec000, 0x385f0000, 0x385f4000, -0x385f8000, 0x385fc000, 0x38600000, 0x38604000, 0x38608000, 0x3860c000, -0x38610000, 0x38614000, 0x38618000, 0x3861c000, 0x38620000, 0x38624000, -0x38628000, 0x3862c000, 0x38630000, 0x38634000, 0x38638000, 0x3863c000, -0x38640000, 0x38644000, 0x38648000, 0x3864c000, 0x38650000, 0x38654000, -0x38658000, 0x3865c000, 0x38660000, 0x38664000, 0x38668000, 0x3866c000, -0x38670000, 0x38674000, 0x38678000, 0x3867c000, 0x38680000, 0x38684000, -0x38688000, 0x3868c000, 0x38690000, 0x38694000, 0x38698000, 0x3869c000, -0x386a0000, 0x386a4000, 0x386a8000, 0x386ac000, 0x386b0000, 0x386b4000, -0x386b8000, 0x386bc000, 0x386c0000, 0x386c4000, 0x386c8000, 0x386cc000, -0x386d0000, 0x386d4000, 0x386d8000, 0x386dc000, 0x386e0000, 0x386e4000, -0x386e8000, 0x386ec000, 0x386f0000, 0x386f4000, 0x386f8000, 0x386fc000, -0x38700000, 0x38704000, 0x38708000, 0x3870c000, 0x38710000, 0x38714000, -0x38718000, 0x3871c000, 0x38720000, 0x38724000, 0x38728000, 0x3872c000, -0x38730000, 0x38734000, 0x38738000, 0x3873c000, 0x38740000, 0x38744000, -0x38748000, 0x3874c000, 0x38750000, 0x38754000, 0x38758000, 0x3875c000, -0x38760000, 0x38764000, 0x38768000, 0x3876c000, 0x38770000, 0x38774000, -0x38778000, 0x3877c000, 0x38780000, 0x38784000, 0x38788000, 0x3878c000, -0x38790000, 0x38794000, 0x38798000, 0x3879c000, 0x387a0000, 0x387a4000, -0x387a8000, 0x387ac000, 0x387b0000, 0x387b4000, 0x387b8000, 0x387bc000, -0x387c0000, 0x387c4000, 0x387c8000, 0x387cc000, 0x387d0000, 0x387d4000, -0x387d8000, 0x387dc000, 0x387e0000, 0x387e4000, 0x387e8000, 0x387ec000, -0x387f0000, 0x387f4000, 0x387f8000, 0x387fc000, 0x38000000, 0x38002000, -0x38004000, 0x38006000, 0x38008000, 0x3800a000, 0x3800c000, 0x3800e000, -0x38010000, 0x38012000, 0x38014000, 0x38016000, 0x38018000, 0x3801a000, -0x3801c000, 0x3801e000, 0x38020000, 0x38022000, 0x38024000, 0x38026000, -0x38028000, 0x3802a000, 0x3802c000, 0x3802e000, 0x38030000, 0x38032000, -0x38034000, 0x38036000, 0x38038000, 0x3803a000, 0x3803c000, 0x3803e000, -0x38040000, 0x38042000, 0x38044000, 0x38046000, 0x38048000, 0x3804a000, -0x3804c000, 0x3804e000, 0x38050000, 0x38052000, 0x38054000, 0x38056000, -0x38058000, 0x3805a000, 0x3805c000, 0x3805e000, 0x38060000, 0x38062000, -0x38064000, 0x38066000, 0x38068000, 0x3806a000, 0x3806c000, 0x3806e000, -0x38070000, 0x38072000, 0x38074000, 0x38076000, 0x38078000, 0x3807a000, -0x3807c000, 0x3807e000, 0x38080000, 0x38082000, 0x38084000, 0x38086000, -0x38088000, 0x3808a000, 0x3808c000, 0x3808e000, 0x38090000, 0x38092000, -0x38094000, 0x38096000, 0x38098000, 0x3809a000, 0x3809c000, 0x3809e000, -0x380a0000, 0x380a2000, 0x380a4000, 0x380a6000, 0x380a8000, 0x380aa000, -0x380ac000, 0x380ae000, 0x380b0000, 0x380b2000, 0x380b4000, 0x380b6000, -0x380b8000, 0x380ba000, 0x380bc000, 0x380be000, 0x380c0000, 0x380c2000, -0x380c4000, 0x380c6000, 0x380c8000, 0x380ca000, 0x380cc000, 0x380ce000, -0x380d0000, 0x380d2000, 0x380d4000, 0x380d6000, 0x380d8000, 0x380da000, -0x380dc000, 0x380de000, 0x380e0000, 0x380e2000, 0x380e4000, 0x380e6000, -0x380e8000, 0x380ea000, 0x380ec000, 0x380ee000, 0x380f0000, 0x380f2000, -0x380f4000, 0x380f6000, 0x380f8000, 0x380fa000, 0x380fc000, 0x380fe000, -0x38100000, 0x38102000, 0x38104000, 0x38106000, 0x38108000, 0x3810a000, -0x3810c000, 0x3810e000, 0x38110000, 0x38112000, 0x38114000, 0x38116000, -0x38118000, 0x3811a000, 0x3811c000, 0x3811e000, 0x38120000, 0x38122000, -0x38124000, 0x38126000, 0x38128000, 0x3812a000, 0x3812c000, 0x3812e000, -0x38130000, 0x38132000, 0x38134000, 0x38136000, 0x38138000, 0x3813a000, -0x3813c000, 0x3813e000, 0x38140000, 0x38142000, 0x38144000, 0x38146000, -0x38148000, 0x3814a000, 0x3814c000, 0x3814e000, 0x38150000, 0x38152000, -0x38154000, 0x38156000, 0x38158000, 0x3815a000, 0x3815c000, 0x3815e000, -0x38160000, 0x38162000, 0x38164000, 0x38166000, 0x38168000, 0x3816a000, -0x3816c000, 0x3816e000, 0x38170000, 0x38172000, 0x38174000, 0x38176000, -0x38178000, 0x3817a000, 0x3817c000, 0x3817e000, 0x38180000, 0x38182000, -0x38184000, 0x38186000, 0x38188000, 0x3818a000, 0x3818c000, 0x3818e000, -0x38190000, 0x38192000, 0x38194000, 0x38196000, 0x38198000, 0x3819a000, -0x3819c000, 0x3819e000, 0x381a0000, 0x381a2000, 0x381a4000, 0x381a6000, -0x381a8000, 0x381aa000, 0x381ac000, 0x381ae000, 0x381b0000, 0x381b2000, -0x381b4000, 0x381b6000, 0x381b8000, 0x381ba000, 0x381bc000, 0x381be000, -0x381c0000, 0x381c2000, 0x381c4000, 0x381c6000, 0x381c8000, 0x381ca000, -0x381cc000, 0x381ce000, 0x381d0000, 0x381d2000, 0x381d4000, 0x381d6000, -0x381d8000, 0x381da000, 0x381dc000, 0x381de000, 0x381e0000, 0x381e2000, -0x381e4000, 0x381e6000, 0x381e8000, 0x381ea000, 0x381ec000, 0x381ee000, -0x381f0000, 0x381f2000, 0x381f4000, 0x381f6000, 0x381f8000, 0x381fa000, -0x381fc000, 0x381fe000, 0x38200000, 0x38202000, 0x38204000, 0x38206000, -0x38208000, 0x3820a000, 0x3820c000, 0x3820e000, 0x38210000, 0x38212000, -0x38214000, 0x38216000, 0x38218000, 0x3821a000, 0x3821c000, 0x3821e000, -0x38220000, 0x38222000, 0x38224000, 0x38226000, 0x38228000, 0x3822a000, -0x3822c000, 0x3822e000, 0x38230000, 0x38232000, 0x38234000, 0x38236000, -0x38238000, 0x3823a000, 0x3823c000, 0x3823e000, 0x38240000, 0x38242000, -0x38244000, 0x38246000, 0x38248000, 0x3824a000, 0x3824c000, 0x3824e000, -0x38250000, 0x38252000, 0x38254000, 0x38256000, 0x38258000, 0x3825a000, -0x3825c000, 0x3825e000, 0x38260000, 0x38262000, 0x38264000, 0x38266000, -0x38268000, 0x3826a000, 0x3826c000, 0x3826e000, 0x38270000, 0x38272000, -0x38274000, 0x38276000, 0x38278000, 0x3827a000, 0x3827c000, 0x3827e000, -0x38280000, 0x38282000, 0x38284000, 0x38286000, 0x38288000, 0x3828a000, -0x3828c000, 0x3828e000, 0x38290000, 0x38292000, 0x38294000, 0x38296000, -0x38298000, 0x3829a000, 0x3829c000, 0x3829e000, 0x382a0000, 0x382a2000, -0x382a4000, 0x382a6000, 0x382a8000, 0x382aa000, 0x382ac000, 0x382ae000, -0x382b0000, 0x382b2000, 0x382b4000, 0x382b6000, 0x382b8000, 0x382ba000, -0x382bc000, 0x382be000, 0x382c0000, 0x382c2000, 0x382c4000, 0x382c6000, -0x382c8000, 0x382ca000, 0x382cc000, 0x382ce000, 0x382d0000, 0x382d2000, -0x382d4000, 0x382d6000, 0x382d8000, 0x382da000, 0x382dc000, 0x382de000, -0x382e0000, 0x382e2000, 0x382e4000, 0x382e6000, 0x382e8000, 0x382ea000, -0x382ec000, 0x382ee000, 0x382f0000, 0x382f2000, 0x382f4000, 0x382f6000, -0x382f8000, 0x382fa000, 0x382fc000, 0x382fe000, 0x38300000, 0x38302000, -0x38304000, 0x38306000, 0x38308000, 0x3830a000, 0x3830c000, 0x3830e000, -0x38310000, 0x38312000, 0x38314000, 0x38316000, 0x38318000, 0x3831a000, -0x3831c000, 0x3831e000, 0x38320000, 0x38322000, 0x38324000, 0x38326000, -0x38328000, 0x3832a000, 0x3832c000, 0x3832e000, 0x38330000, 0x38332000, -0x38334000, 0x38336000, 0x38338000, 0x3833a000, 0x3833c000, 0x3833e000, -0x38340000, 0x38342000, 0x38344000, 0x38346000, 0x38348000, 0x3834a000, -0x3834c000, 0x3834e000, 0x38350000, 0x38352000, 0x38354000, 0x38356000, -0x38358000, 0x3835a000, 0x3835c000, 0x3835e000, 0x38360000, 0x38362000, -0x38364000, 0x38366000, 0x38368000, 0x3836a000, 0x3836c000, 0x3836e000, -0x38370000, 0x38372000, 0x38374000, 0x38376000, 0x38378000, 0x3837a000, -0x3837c000, 0x3837e000, 0x38380000, 0x38382000, 0x38384000, 0x38386000, -0x38388000, 0x3838a000, 0x3838c000, 0x3838e000, 0x38390000, 0x38392000, -0x38394000, 0x38396000, 0x38398000, 0x3839a000, 0x3839c000, 0x3839e000, -0x383a0000, 0x383a2000, 0x383a4000, 0x383a6000, 0x383a8000, 0x383aa000, -0x383ac000, 0x383ae000, 0x383b0000, 0x383b2000, 0x383b4000, 0x383b6000, -0x383b8000, 0x383ba000, 0x383bc000, 0x383be000, 0x383c0000, 0x383c2000, -0x383c4000, 0x383c6000, 0x383c8000, 0x383ca000, 0x383cc000, 0x383ce000, -0x383d0000, 0x383d2000, 0x383d4000, 0x383d6000, 0x383d8000, 0x383da000, -0x383dc000, 0x383de000, 0x383e0000, 0x383e2000, 0x383e4000, 0x383e6000, -0x383e8000, 0x383ea000, 0x383ec000, 0x383ee000, 0x383f0000, 0x383f2000, -0x383f4000, 0x383f6000, 0x383f8000, 0x383fa000, 0x383fc000, 0x383fe000, -0x38400000, 0x38402000, 0x38404000, 0x38406000, 0x38408000, 0x3840a000, -0x3840c000, 0x3840e000, 0x38410000, 0x38412000, 0x38414000, 0x38416000, -0x38418000, 0x3841a000, 0x3841c000, 0x3841e000, 0x38420000, 0x38422000, -0x38424000, 0x38426000, 0x38428000, 0x3842a000, 0x3842c000, 0x3842e000, -0x38430000, 0x38432000, 0x38434000, 0x38436000, 0x38438000, 0x3843a000, -0x3843c000, 0x3843e000, 0x38440000, 0x38442000, 0x38444000, 0x38446000, -0x38448000, 0x3844a000, 0x3844c000, 0x3844e000, 0x38450000, 0x38452000, -0x38454000, 0x38456000, 0x38458000, 0x3845a000, 0x3845c000, 0x3845e000, -0x38460000, 0x38462000, 0x38464000, 0x38466000, 0x38468000, 0x3846a000, -0x3846c000, 0x3846e000, 0x38470000, 0x38472000, 0x38474000, 0x38476000, -0x38478000, 0x3847a000, 0x3847c000, 0x3847e000, 0x38480000, 0x38482000, -0x38484000, 0x38486000, 0x38488000, 0x3848a000, 0x3848c000, 0x3848e000, -0x38490000, 0x38492000, 0x38494000, 0x38496000, 0x38498000, 0x3849a000, -0x3849c000, 0x3849e000, 0x384a0000, 0x384a2000, 0x384a4000, 0x384a6000, -0x384a8000, 0x384aa000, 0x384ac000, 0x384ae000, 0x384b0000, 0x384b2000, -0x384b4000, 0x384b6000, 0x384b8000, 0x384ba000, 0x384bc000, 0x384be000, -0x384c0000, 0x384c2000, 0x384c4000, 0x384c6000, 0x384c8000, 0x384ca000, -0x384cc000, 0x384ce000, 0x384d0000, 0x384d2000, 0x384d4000, 0x384d6000, -0x384d8000, 0x384da000, 0x384dc000, 0x384de000, 0x384e0000, 0x384e2000, -0x384e4000, 0x384e6000, 0x384e8000, 0x384ea000, 0x384ec000, 0x384ee000, -0x384f0000, 0x384f2000, 0x384f4000, 0x384f6000, 0x384f8000, 0x384fa000, -0x384fc000, 0x384fe000, 0x38500000, 0x38502000, 0x38504000, 0x38506000, -0x38508000, 0x3850a000, 0x3850c000, 0x3850e000, 0x38510000, 0x38512000, -0x38514000, 0x38516000, 0x38518000, 0x3851a000, 0x3851c000, 0x3851e000, -0x38520000, 0x38522000, 0x38524000, 0x38526000, 0x38528000, 0x3852a000, -0x3852c000, 0x3852e000, 0x38530000, 0x38532000, 0x38534000, 0x38536000, -0x38538000, 0x3853a000, 0x3853c000, 0x3853e000, 0x38540000, 0x38542000, -0x38544000, 0x38546000, 0x38548000, 0x3854a000, 0x3854c000, 0x3854e000, -0x38550000, 0x38552000, 0x38554000, 0x38556000, 0x38558000, 0x3855a000, -0x3855c000, 0x3855e000, 0x38560000, 0x38562000, 0x38564000, 0x38566000, -0x38568000, 0x3856a000, 0x3856c000, 0x3856e000, 0x38570000, 0x38572000, -0x38574000, 0x38576000, 0x38578000, 0x3857a000, 0x3857c000, 0x3857e000, -0x38580000, 0x38582000, 0x38584000, 0x38586000, 0x38588000, 0x3858a000, -0x3858c000, 0x3858e000, 0x38590000, 0x38592000, 0x38594000, 0x38596000, -0x38598000, 0x3859a000, 0x3859c000, 0x3859e000, 0x385a0000, 0x385a2000, -0x385a4000, 0x385a6000, 0x385a8000, 0x385aa000, 0x385ac000, 0x385ae000, -0x385b0000, 0x385b2000, 0x385b4000, 0x385b6000, 0x385b8000, 0x385ba000, -0x385bc000, 0x385be000, 0x385c0000, 0x385c2000, 0x385c4000, 0x385c6000, -0x385c8000, 0x385ca000, 0x385cc000, 0x385ce000, 0x385d0000, 0x385d2000, -0x385d4000, 0x385d6000, 0x385d8000, 0x385da000, 0x385dc000, 0x385de000, -0x385e0000, 0x385e2000, 0x385e4000, 0x385e6000, 0x385e8000, 0x385ea000, -0x385ec000, 0x385ee000, 0x385f0000, 0x385f2000, 0x385f4000, 0x385f6000, -0x385f8000, 0x385fa000, 0x385fc000, 0x385fe000, 0x38600000, 0x38602000, -0x38604000, 0x38606000, 0x38608000, 0x3860a000, 0x3860c000, 0x3860e000, -0x38610000, 0x38612000, 0x38614000, 0x38616000, 0x38618000, 0x3861a000, -0x3861c000, 0x3861e000, 0x38620000, 0x38622000, 0x38624000, 0x38626000, -0x38628000, 0x3862a000, 0x3862c000, 0x3862e000, 0x38630000, 0x38632000, -0x38634000, 0x38636000, 0x38638000, 0x3863a000, 0x3863c000, 0x3863e000, -0x38640000, 0x38642000, 0x38644000, 0x38646000, 0x38648000, 0x3864a000, -0x3864c000, 0x3864e000, 0x38650000, 0x38652000, 0x38654000, 0x38656000, -0x38658000, 0x3865a000, 0x3865c000, 0x3865e000, 0x38660000, 0x38662000, -0x38664000, 0x38666000, 0x38668000, 0x3866a000, 0x3866c000, 0x3866e000, -0x38670000, 0x38672000, 0x38674000, 0x38676000, 0x38678000, 0x3867a000, -0x3867c000, 0x3867e000, 0x38680000, 0x38682000, 0x38684000, 0x38686000, -0x38688000, 0x3868a000, 0x3868c000, 0x3868e000, 0x38690000, 0x38692000, -0x38694000, 0x38696000, 0x38698000, 0x3869a000, 0x3869c000, 0x3869e000, -0x386a0000, 0x386a2000, 0x386a4000, 0x386a6000, 0x386a8000, 0x386aa000, -0x386ac000, 0x386ae000, 0x386b0000, 0x386b2000, 0x386b4000, 0x386b6000, -0x386b8000, 0x386ba000, 0x386bc000, 0x386be000, 0x386c0000, 0x386c2000, -0x386c4000, 0x386c6000, 0x386c8000, 0x386ca000, 0x386cc000, 0x386ce000, -0x386d0000, 0x386d2000, 0x386d4000, 0x386d6000, 0x386d8000, 0x386da000, -0x386dc000, 0x386de000, 0x386e0000, 0x386e2000, 0x386e4000, 0x386e6000, -0x386e8000, 0x386ea000, 0x386ec000, 0x386ee000, 0x386f0000, 0x386f2000, -0x386f4000, 0x386f6000, 0x386f8000, 0x386fa000, 0x386fc000, 0x386fe000, -0x38700000, 0x38702000, 0x38704000, 0x38706000, 0x38708000, 0x3870a000, -0x3870c000, 0x3870e000, 0x38710000, 0x38712000, 0x38714000, 0x38716000, -0x38718000, 0x3871a000, 0x3871c000, 0x3871e000, 0x38720000, 0x38722000, -0x38724000, 0x38726000, 0x38728000, 0x3872a000, 0x3872c000, 0x3872e000, -0x38730000, 0x38732000, 0x38734000, 0x38736000, 0x38738000, 0x3873a000, -0x3873c000, 0x3873e000, 0x38740000, 0x38742000, 0x38744000, 0x38746000, -0x38748000, 0x3874a000, 0x3874c000, 0x3874e000, 0x38750000, 0x38752000, -0x38754000, 0x38756000, 0x38758000, 0x3875a000, 0x3875c000, 0x3875e000, -0x38760000, 0x38762000, 0x38764000, 0x38766000, 0x38768000, 0x3876a000, -0x3876c000, 0x3876e000, 0x38770000, 0x38772000, 0x38774000, 0x38776000, -0x38778000, 0x3877a000, 0x3877c000, 0x3877e000, 0x38780000, 0x38782000, -0x38784000, 0x38786000, 0x38788000, 0x3878a000, 0x3878c000, 0x3878e000, -0x38790000, 0x38792000, 0x38794000, 0x38796000, 0x38798000, 0x3879a000, -0x3879c000, 0x3879e000, 0x387a0000, 0x387a2000, 0x387a4000, 0x387a6000, -0x387a8000, 0x387aa000, 0x387ac000, 0x387ae000, 0x387b0000, 0x387b2000, -0x387b4000, 0x387b6000, 0x387b8000, 0x387ba000, 0x387bc000, 0x387be000, -0x387c0000, 0x387c2000, 0x387c4000, 0x387c6000, 0x387c8000, 0x387ca000, -0x387cc000, 0x387ce000, 0x387d0000, 0x387d2000, 0x387d4000, 0x387d6000, -0x387d8000, 0x387da000, 0x387dc000, 0x387de000, 0x387e0000, 0x387e2000, -0x387e4000, 0x387e6000, 0x387e8000, 0x387ea000, 0x387ec000, 0x387ee000, -0x387f0000, 0x387f2000, 0x387f4000, 0x387f6000, 0x387f8000, 0x387fa000, -0x387fc000, 0x387fe000 -}; - -static cmsUInt16Number Offset[64] = { -0x0000, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, -0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, -0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, -0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, -0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, -0x0400, 0x0400, 0x0000, 0x0400, 0x0400, 0x0400, -0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, -0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, -0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, -0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, -0x0400, 0x0400, 0x0400, 0x0400 -}; - -static cmsUInt32Number Exponent[64] = { -0x00000000, 0x00800000, 0x01000000, 0x01800000, 0x02000000, 0x02800000, -0x03000000, 0x03800000, 0x04000000, 0x04800000, 0x05000000, 0x05800000, -0x06000000, 0x06800000, 0x07000000, 0x07800000, 0x08000000, 0x08800000, -0x09000000, 0x09800000, 0x0a000000, 0x0a800000, 0x0b000000, 0x0b800000, -0x0c000000, 0x0c800000, 0x0d000000, 0x0d800000, 0x0e000000, 0x0e800000, -0x0f000000, 0x47800000, 0x80000000, 0x80800000, 0x81000000, 0x81800000, -0x82000000, 0x82800000, 0x83000000, 0x83800000, 0x84000000, 0x84800000, -0x85000000, 0x85800000, 0x86000000, 0x86800000, 0x87000000, 0x87800000, -0x88000000, 0x88800000, 0x89000000, 0x89800000, 0x8a000000, 0x8a800000, -0x8b000000, 0x8b800000, 0x8c000000, 0x8c800000, 0x8d000000, 0x8d800000, -0x8e000000, 0x8e800000, 0x8f000000, 0xc7800000 -}; - -static cmsUInt16Number Base[512] = { -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, -0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, -0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x0c00, 0x1000, 0x1400, 0x1800, 0x1c00, -0x2000, 0x2400, 0x2800, 0x2c00, 0x3000, 0x3400, 0x3800, 0x3c00, 0x4000, 0x4400, -0x4800, 0x4c00, 0x5000, 0x5400, 0x5800, 0x5c00, 0x6000, 0x6400, 0x6800, 0x6c00, -0x7000, 0x7400, 0x7800, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, -0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, -0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, -0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, -0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, -0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, -0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, -0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, -0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, -0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, -0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, -0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x7c00, 0x8000, 0x8000, 0x8000, 0x8000, -0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, -0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, -0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, -0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, -0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, -0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, -0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, -0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, -0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, -0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8000, 0x8001, -0x8002, 0x8004, 0x8008, 0x8010, 0x8020, 0x8040, 0x8080, 0x8100, 0x8200, 0x8400, -0x8800, 0x8c00, 0x9000, 0x9400, 0x9800, 0x9c00, 0xa000, 0xa400, 0xa800, 0xac00, -0xb000, 0xb400, 0xb800, 0xbc00, 0xc000, 0xc400, 0xc800, 0xcc00, 0xd000, 0xd400, -0xd800, 0xdc00, 0xe000, 0xe400, 0xe800, 0xec00, 0xf000, 0xf400, 0xf800, 0xfc00, -0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, -0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, -0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, -0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, -0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, -0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, -0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, -0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, -0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, -0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, -0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, 0xfc00, -0xfc00, 0xfc00 -}; - -static cmsUInt8Number Shift[512] = { -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, -0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, -0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, -0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x0d, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, -0x12, 0x11, 0x10, 0x0f, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, -0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, -0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -0x18, 0x18, 0x18, 0x18, 0x0d -}; - -cmsFloat32Number _cmsHalf2Float(cmsUInt16Number h) -{ - union { - cmsFloat32Number flt; - cmsUInt32Number num; - } out; - - int n = h >> 10; - - out.num = Mantissa[ (h & 0x3ff) + Offset[ n ] ] + Exponent[ n ]; - return out.flt; -} - -cmsUInt16Number _cmsFloat2Half(cmsFloat32Number flt) -{ - union { - cmsFloat32Number flt; - cmsUInt32Number num; - } in; - - cmsUInt32Number n, j; - - in.flt = flt; - n = in.num; - j = (n >> 23) & 0x1ff; - - return (cmsUInt16Number) ((cmsUInt32Number) Base[ j ] + (( n & 0x007fffff) >> Shift[ j ])); -} - -#endif diff --git a/third_party/lcms2-2.6/src/cmsintrp.c b/third_party/lcms2-2.6/src/cmsintrp.c deleted file mode 100644 index 14c68563ca..0000000000 --- a/third_party/lcms2-2.6/src/cmsintrp.c +++ /dev/null @@ -1,1506 +0,0 @@ -//--------------------------------------------------------------------------------- -// -// 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" - -// This module incorporates several interpolation routines, for 1 to 8 channels on input and -// up to 65535 channels on output. The user may change those by using the interpolation plug-in - -// Interpolation routines by default -static cmsInterpFunction DefaultInterpolatorsFactory(cmsUInt32Number nInputChannels, cmsUInt32Number nOutputChannels, cmsUInt32Number dwFlags); - -// This is the default factory -_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(cmsContext ContextID, cmsPluginBase* Data) -{ - cmsPluginInterpolation* Plugin = (cmsPluginInterpolation*) Data; - _cmsInterpPluginChunkType* ptr = (_cmsInterpPluginChunkType*) _cmsContextGetClientChunk(ContextID, InterpPlugin); - - if (Data == NULL) { - - ptr ->Interpolators = NULL; - return TRUE; - } - - // Set replacement functions - ptr ->Interpolators = Plugin ->InterpolatorsFactory; - return TRUE; -} - - -// Set the interpolation method -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) - p ->Interpolation = DefaultInterpolatorsFactory(p ->nInputs, p ->nOutputs, p ->dwFlags); - - // Check for valid interpolator (we just check one member of the union) - if (p ->Interpolation.Lerp16 == NULL) { - return FALSE; - } - - return TRUE; -} - - -// This function precalculates as many parameters as possible to speed up the interpolation. -cmsInterpParams* _cmsComputeInterpParamsEx(cmsContext ContextID, - const cmsUInt32Number nSamples[], - int InputChan, int OutputChan, - const void *Table, - cmsUInt32Number dwFlags) -{ - cmsInterpParams* p; - int i; - - // Check for maximum inputs - if (InputChan > MAX_INPUT_DIMENSIONS) { - cmsSignalError(ContextID, cmsERROR_RANGE, "Too many input channels (%d channels, max=%d)", InputChan, MAX_INPUT_DIMENSIONS); - return NULL; - } - - // Creates an empty object - p = (cmsInterpParams*) _cmsMallocZero(ContextID, sizeof(cmsInterpParams)); - if (p == NULL) return NULL; - - // Keep original parameters - p -> dwFlags = dwFlags; - p -> nInputs = InputChan; - p -> nOutputs = OutputChan; - p ->Table = Table; - p ->ContextID = ContextID; - - // Fill samples per input direction and domain (which is number of nodes minus one) - for (i=0; i < InputChan; i++) { - - p -> nSamples[i] = nSamples[i]; - p -> Domain[i] = nSamples[i] - 1; - } - - // Compute factors to apply to each component to index the grid array - p -> opta[0] = p -> nOutputs; - for (i=1; i < InputChan; i++) - p ->opta[i] = p ->opta[i-1] * nSamples[InputChan-i]; - - - if (!_cmsSetInterpolationRoutine(ContextID, p)) { - cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported interpolation (%d->%d channels)", InputChan, OutputChan); - _cmsFree(ContextID, p); - return NULL; - } - - // All seems ok - return p; -} - - -// This one is a wrapper on the anterior, but assuming all directions have same number of nodes -cmsInterpParams* _cmsComputeInterpParams(cmsContext ContextID, int nSamples, int InputChan, int OutputChan, const void* Table, cmsUInt32Number dwFlags) -{ - int i; - cmsUInt32Number Samples[MAX_INPUT_DIMENSIONS]; - - // Fill the auxiliar array - for (i=0; i < MAX_INPUT_DIMENSIONS; i++) - Samples[i] = nSamples; - - // Call the extended function - return _cmsComputeInterpParamsEx(ContextID, Samples, InputChan, OutputChan, Table, dwFlags); -} - - -// Free all associated memory -void _cmsFreeInterpParams(cmsInterpParams* p) -{ - if (p != NULL) _cmsFree(p ->ContextID, p); -} - - -// Inline fixed point interpolation -cmsINLINE cmsUInt16Number LinearInterp(cmsS15Fixed16Number a, cmsS15Fixed16Number l, cmsS15Fixed16Number h) -{ - cmsUInt32Number dif = (cmsUInt32Number) (h - l) * a + 0x8000; - dif = (dif >> 16) + l; - return (cmsUInt16Number) (dif); -} - - -// Linear interpolation (Fixed-point optimized) -static -void LinLerp1D(register const cmsUInt16Number Value[], - register cmsUInt16Number Output[], - register const cmsInterpParams* p) -{ - cmsUInt16Number y1, y0; - int cell0, rest; - int val3; - const cmsUInt16Number* LutTable = (cmsUInt16Number*) p ->Table; - - // if last value... - if (Value[0] == 0xffff) { - - Output[0] = LutTable[p -> Domain[0]]; - return; - } - - val3 = p -> Domain[0] * Value[0]; - val3 = _cmsToFixedDomain(val3); // To fixed 15.16 - - cell0 = FIXED_TO_INT(val3); // Cell is 16 MSB bits - rest = FIXED_REST_TO_INT(val3); // Rest is 16 LSB bits - - y0 = LutTable[cell0]; - y1 = LutTable[cell0+1]; - - - Output[0] = LinearInterp(rest, y0, y1); -} - -// To prevent out of bounds indexing -cmsINLINE cmsFloat32Number fclamp(cmsFloat32Number v) -{ - return ((v < 0.0f) || isnan(v)) ? 0.0f : (v > 1.0f ? 1.0f : v); -} - -// Floating-point version of 1D interpolation -static -void LinLerp1Dfloat(const cmsFloat32Number Value[], - cmsFloat32Number Output[], - const cmsInterpParams* p) -{ - cmsFloat32Number y1, y0; - cmsFloat32Number val2, rest; - int cell0, cell1; - const cmsFloat32Number* LutTable = (cmsFloat32Number*) p ->Table; - - val2 = fclamp(Value[0]); - - // if last value... - if (val2 == 1.0) { - Output[0] = LutTable[p -> Domain[0]]; - return; - } - - val2 *= p -> Domain[0]; - - cell0 = (int) floor(val2); - cell1 = (int) ceil(val2); - - // Rest is 16 LSB bits - rest = val2 - cell0; - - y0 = LutTable[cell0] ; - y1 = LutTable[cell1] ; - - Output[0] = y0 + (y1 - y0) * rest; -} - - - -// Eval gray LUT having only one input channel -static -void Eval1Input(register const cmsUInt16Number Input[], - register cmsUInt16Number Output[], - register const cmsInterpParams* p16) -{ - cmsS15Fixed16Number fk; - cmsS15Fixed16Number k0, k1, rk, K0, K1; - int v; - cmsUInt32Number OutChan; - const cmsUInt16Number* LutTable = (cmsUInt16Number*) p16 -> Table; - - v = Input[0] * p16 -> Domain[0]; - fk = _cmsToFixedDomain(v); - - k0 = FIXED_TO_INT(fk); - rk = (cmsUInt16Number) FIXED_REST_TO_INT(fk); - - k1 = k0 + (Input[0] != 0xFFFFU ? 1 : 0); - - K0 = p16 -> opta[0] * k0; - K1 = p16 -> opta[0] * k1; - - for (OutChan=0; OutChan < p16->nOutputs; OutChan++) { - - Output[OutChan] = LinearInterp(rk, LutTable[K0+OutChan], LutTable[K1+OutChan]); - } -} - - - -// Eval gray LUT having only one input channel -static -void Eval1InputFloat(const cmsFloat32Number Value[], - cmsFloat32Number Output[], - const cmsInterpParams* p) -{ - cmsFloat32Number y1, y0; - cmsFloat32Number val2, rest; - int cell0, cell1; - cmsUInt32Number OutChan; - const cmsFloat32Number* LutTable = (cmsFloat32Number*) p ->Table; - - val2 = fclamp(Value[0]); - - // if last value... - if (val2 == 1.0) { - Output[0] = LutTable[p -> Domain[0]]; - return; - } - - val2 *= p -> Domain[0]; - - cell0 = (int) floor(val2); - cell1 = (int) ceil(val2); - - // Rest is 16 LSB bits - rest = val2 - cell0; - - cell0 *= p -> opta[0]; - cell1 *= p -> opta[0]; - - for (OutChan=0; OutChan < p->nOutputs; OutChan++) { - - y0 = LutTable[cell0 + OutChan] ; - y1 = LutTable[cell1 + OutChan] ; - - Output[OutChan] = y0 + (y1 - y0) * rest; - } -} - -// Bilinear interpolation (16 bits) - cmsFloat32Number version -static -void BilinearInterpFloat(const cmsFloat32Number Input[], - cmsFloat32Number Output[], - const cmsInterpParams* p) - -{ -# define LERP(a,l,h) (cmsFloat32Number) ((l)+(((h)-(l))*(a))) -# define DENS(i,j) (LutTable[(i)+(j)+OutChan]) - - const cmsFloat32Number* LutTable = (cmsFloat32Number*) p ->Table; - cmsFloat32Number px, py; - int x0, y0, - X0, Y0, X1, Y1; - int TotalOut, OutChan; - cmsFloat32Number fx, fy, - d00, d01, d10, d11, - dx0, dx1, - dxy; - - TotalOut = p -> nOutputs; - px = fclamp(Input[0]) * p->Domain[0]; - py = fclamp(Input[1]) * p->Domain[1]; - - x0 = (int) _cmsQuickFloor(px); fx = px - (cmsFloat32Number) x0; - y0 = (int) _cmsQuickFloor(py); fy = py - (cmsFloat32Number) y0; - - X0 = p -> opta[1] * x0; - X1 = X0 + (Input[0] >= 1.0 ? 0 : p->opta[1]); - - Y0 = p -> opta[0] * y0; - Y1 = Y0 + (Input[1] >= 1.0 ? 0 : p->opta[0]); - - for (OutChan = 0; OutChan < TotalOut; OutChan++) { - - d00 = DENS(X0, Y0); - d01 = DENS(X0, Y1); - d10 = DENS(X1, Y0); - d11 = DENS(X1, Y1); - - dx0 = LERP(fx, d00, d10); - dx1 = LERP(fx, d01, d11); - - dxy = LERP(fy, dx0, dx1); - - Output[OutChan] = dxy; - } - - -# undef LERP -# undef DENS -} - -// Bilinear interpolation (16 bits) - optimized version -static -void BilinearInterp16(register const cmsUInt16Number Input[], - register cmsUInt16Number Output[], - register const cmsInterpParams* p) - -{ -#define DENS(i,j) (LutTable[(i)+(j)+OutChan]) -#define LERP(a,l,h) (cmsUInt16Number) (l + ROUND_FIXED_TO_INT(((h-l)*a))) - - const cmsUInt16Number* LutTable = (cmsUInt16Number*) p ->Table; - int OutChan, TotalOut; - cmsS15Fixed16Number fx, fy; - register int rx, ry; - int x0, y0; - register int X0, X1, Y0, Y1; - int d00, d01, d10, d11, - dx0, dx1, - dxy; - - TotalOut = p -> nOutputs; - - fx = _cmsToFixedDomain((int) Input[0] * p -> Domain[0]); - x0 = FIXED_TO_INT(fx); - rx = FIXED_REST_TO_INT(fx); // Rest in 0..1.0 domain - - - fy = _cmsToFixedDomain((int) Input[1] * p -> Domain[1]); - y0 = FIXED_TO_INT(fy); - ry = FIXED_REST_TO_INT(fy); - - - X0 = p -> opta[1] * x0; - X1 = X0 + (Input[0] == 0xFFFFU ? 0 : p->opta[1]); - - Y0 = p -> opta[0] * y0; - Y1 = Y0 + (Input[1] == 0xFFFFU ? 0 : p->opta[0]); - - for (OutChan = 0; OutChan < TotalOut; OutChan++) { - - d00 = DENS(X0, Y0); - d01 = DENS(X0, Y1); - d10 = DENS(X1, Y0); - d11 = DENS(X1, Y1); - - dx0 = LERP(rx, d00, d10); - dx1 = LERP(rx, d01, d11); - - dxy = LERP(ry, dx0, dx1); - - Output[OutChan] = (cmsUInt16Number) dxy; - } - - -# undef LERP -# undef DENS -} - - -// Trilinear interpolation (16 bits) - cmsFloat32Number version -static -void TrilinearInterpFloat(const cmsFloat32Number Input[], - cmsFloat32Number Output[], - const cmsInterpParams* p) - -{ -# define LERP(a,l,h) (cmsFloat32Number) ((l)+(((h)-(l))*(a))) -# define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan]) - - const cmsFloat32Number* LutTable = (cmsFloat32Number*) p ->Table; - cmsFloat32Number px, py, pz; - int x0, y0, z0, - X0, Y0, Z0, X1, Y1, Z1; - int TotalOut, OutChan; - cmsFloat32Number fx, fy, fz, - d000, d001, d010, d011, - d100, d101, d110, d111, - dx00, dx01, dx10, dx11, - dxy0, dxy1, dxyz; - - TotalOut = p -> nOutputs; - - // We need some clipping here - px = fclamp(Input[0]) * p->Domain[0]; - py = fclamp(Input[1]) * p->Domain[1]; - pz = fclamp(Input[2]) * p->Domain[2]; - - x0 = (int) _cmsQuickFloor(px); fx = px - (cmsFloat32Number) x0; - y0 = (int) _cmsQuickFloor(py); fy = py - (cmsFloat32Number) y0; - z0 = (int) _cmsQuickFloor(pz); fz = pz - (cmsFloat32Number) z0; - - X0 = p -> opta[2] * x0; - X1 = X0 + (Input[0] >= 1.0 ? 0 : p->opta[2]); - - Y0 = p -> opta[1] * y0; - Y1 = Y0 + (Input[1] >= 1.0 ? 0 : p->opta[1]); - - Z0 = p -> opta[0] * z0; - Z1 = Z0 + (Input[2] >= 1.0 ? 0 : p->opta[0]); - - for (OutChan = 0; OutChan < TotalOut; OutChan++) { - - d000 = DENS(X0, Y0, Z0); - d001 = DENS(X0, Y0, Z1); - d010 = DENS(X0, Y1, Z0); - d011 = DENS(X0, Y1, Z1); - - d100 = DENS(X1, Y0, Z0); - d101 = DENS(X1, Y0, Z1); - d110 = DENS(X1, Y1, Z0); - d111 = DENS(X1, Y1, Z1); - - - dx00 = LERP(fx, d000, d100); - dx01 = LERP(fx, d001, d101); - dx10 = LERP(fx, d010, d110); - dx11 = LERP(fx, d011, d111); - - dxy0 = LERP(fy, dx00, dx10); - dxy1 = LERP(fy, dx01, dx11); - - dxyz = LERP(fz, dxy0, dxy1); - - Output[OutChan] = dxyz; - } - - -# undef LERP -# undef DENS -} - -// Trilinear interpolation (16 bits) - optimized version -static -void TrilinearInterp16(register const cmsUInt16Number Input[], - register cmsUInt16Number Output[], - register const cmsInterpParams* p) - -{ -#define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan]) -#define LERP(a,l,h) (cmsUInt16Number) (l + ROUND_FIXED_TO_INT(((h-l)*a))) - - const cmsUInt16Number* LutTable = (cmsUInt16Number*) p ->Table; - int OutChan, TotalOut; - cmsS15Fixed16Number fx, fy, fz; - register int rx, ry, rz; - int x0, y0, z0; - register int X0, X1, Y0, Y1, Z0, Z1; - int d000, d001, d010, d011, - d100, d101, d110, d111, - dx00, dx01, dx10, dx11, - dxy0, dxy1, dxyz; - - TotalOut = p -> nOutputs; - - fx = _cmsToFixedDomain((int) Input[0] * p -> Domain[0]); - x0 = FIXED_TO_INT(fx); - rx = FIXED_REST_TO_INT(fx); // Rest in 0..1.0 domain - - - fy = _cmsToFixedDomain((int) Input[1] * p -> Domain[1]); - y0 = FIXED_TO_INT(fy); - ry = FIXED_REST_TO_INT(fy); - - fz = _cmsToFixedDomain((int) Input[2] * p -> Domain[2]); - z0 = FIXED_TO_INT(fz); - rz = FIXED_REST_TO_INT(fz); - - - X0 = p -> opta[2] * x0; - X1 = X0 + (Input[0] == 0xFFFFU ? 0 : p->opta[2]); - - Y0 = p -> opta[1] * y0; - Y1 = Y0 + (Input[1] == 0xFFFFU ? 0 : p->opta[1]); - - Z0 = p -> opta[0] * z0; - Z1 = Z0 + (Input[2] == 0xFFFFU ? 0 : p->opta[0]); - - for (OutChan = 0; OutChan < TotalOut; OutChan++) { - - d000 = DENS(X0, Y0, Z0); - d001 = DENS(X0, Y0, Z1); - d010 = DENS(X0, Y1, Z0); - d011 = DENS(X0, Y1, Z1); - - d100 = DENS(X1, Y0, Z0); - d101 = DENS(X1, Y0, Z1); - d110 = DENS(X1, Y1, Z0); - d111 = DENS(X1, Y1, Z1); - - - dx00 = LERP(rx, d000, d100); - dx01 = LERP(rx, d001, d101); - dx10 = LERP(rx, d010, d110); - dx11 = LERP(rx, d011, d111); - - dxy0 = LERP(ry, dx00, dx10); - dxy1 = LERP(ry, dx01, dx11); - - dxyz = LERP(rz, dxy0, dxy1); - - Output[OutChan] = (cmsUInt16Number) dxyz; - } - - -# undef LERP -# undef DENS -} - - -// Tetrahedral interpolation, using Sakamoto algorithm. -#define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan]) -static -void TetrahedralInterpFloat(const cmsFloat32Number Input[], - cmsFloat32Number Output[], - const cmsInterpParams* p) -{ - const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table; - cmsFloat32Number px, py, pz; - int x0, y0, z0, - X0, Y0, Z0, X1, Y1, Z1; - cmsFloat32Number rx, ry, rz; - cmsFloat32Number c0, c1=0, c2=0, c3=0; - int OutChan, TotalOut; - - TotalOut = p -> nOutputs; - - // We need some clipping here - px = fclamp(Input[0]) * p->Domain[0]; - py = fclamp(Input[1]) * p->Domain[1]; - pz = fclamp(Input[2]) * p->Domain[2]; - - x0 = (int) _cmsQuickFloor(px); rx = (px - (cmsFloat32Number) x0); - y0 = (int) _cmsQuickFloor(py); ry = (py - (cmsFloat32Number) y0); - z0 = (int) _cmsQuickFloor(pz); rz = (pz - (cmsFloat32Number) z0); - - - X0 = p -> opta[2] * x0; - X1 = X0 + (Input[0] >= 1.0 ? 0 : p->opta[2]); - - Y0 = p -> opta[1] * y0; - Y1 = Y0 + (Input[1] >= 1.0 ? 0 : p->opta[1]); - - Z0 = p -> opta[0] * z0; - Z1 = Z0 + (Input[2] >= 1.0 ? 0 : p->opta[0]); - - for (OutChan=0; OutChan < TotalOut; OutChan++) { - - // These are the 6 Tetrahedral - - c0 = DENS(X0, Y0, Z0); - - if (rx >= ry && ry >= rz) { - - c1 = DENS(X1, Y0, Z0) - c0; - c2 = DENS(X1, Y1, Z0) - DENS(X1, Y0, Z0); - c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); - - } - else - if (rx >= rz && rz >= ry) { - - c1 = DENS(X1, Y0, Z0) - c0; - c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); - c3 = DENS(X1, Y0, Z1) - DENS(X1, Y0, Z0); - - } - else - if (rz >= rx && rx >= ry) { - - c1 = DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1); - c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); - c3 = DENS(X0, Y0, Z1) - c0; - - } - else - if (ry >= rx && rx >= rz) { - - c1 = DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0); - c2 = DENS(X0, Y1, Z0) - c0; - c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); - - } - else - if (ry >= rz && rz >= rx) { - - c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); - c2 = DENS(X0, Y1, Z0) - c0; - c3 = DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0); - - } - else - if (rz >= ry && ry >= rx) { - - c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); - c2 = DENS(X0, Y1, Z1) - DENS(X0, Y0, Z1); - c3 = DENS(X0, Y0, Z1) - c0; - - } - else { - c1 = c2 = c3 = 0; - } - - Output[OutChan] = c0 + c1 * rx + c2 * ry + c3 * rz; - } - -} - -#undef DENS - - - - -static -void TetrahedralInterp16(register const cmsUInt16Number Input[], - register cmsUInt16Number Output[], - register const cmsInterpParams* p) -{ - const cmsUInt16Number* LutTable = (cmsUInt16Number*) p -> Table; - cmsS15Fixed16Number fx, fy, fz; - cmsS15Fixed16Number rx, ry, rz; - int x0, y0, z0; - cmsS15Fixed16Number c0, c1, c2, c3, Rest; - cmsS15Fixed16Number X0, X1, Y0, Y1, Z0, Z1; - cmsUInt32Number TotalOut = p -> nOutputs; - - fx = _cmsToFixedDomain((int) Input[0] * p -> Domain[0]); - fy = _cmsToFixedDomain((int) Input[1] * p -> Domain[1]); - fz = _cmsToFixedDomain((int) Input[2] * p -> Domain[2]); - - x0 = FIXED_TO_INT(fx); - y0 = FIXED_TO_INT(fy); - z0 = FIXED_TO_INT(fz); - - rx = FIXED_REST_TO_INT(fx); - ry = FIXED_REST_TO_INT(fy); - rz = FIXED_REST_TO_INT(fz); - - X0 = p -> opta[2] * x0; - X1 = (Input[0] == 0xFFFFU ? 0 : p->opta[2]); - - Y0 = p -> opta[1] * y0; - Y1 = (Input[1] == 0xFFFFU ? 0 : p->opta[1]); - - Z0 = p -> opta[0] * z0; - Z1 = (Input[2] == 0xFFFFU ? 0 : p->opta[0]); - - LutTable = &LutTable[X0+Y0+Z0]; - - // Output should be computed as x = ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest)) - // which expands as: x = (Rest + ((Rest+0x7fff)/0xFFFF) + 0x8000)>>16 - // This can be replaced by: t = Rest+0x8001, x = (t + (t>>16))>>16 - // at the cost of being off by one at 7fff and 17ffe. - - if (rx >= ry) { - if (ry >= rz) { - Y1 += X1; - Z1 += Y1; - for (; TotalOut; TotalOut--) { - c1 = LutTable[X1]; - c2 = LutTable[Y1]; - c3 = LutTable[Z1]; - c0 = *LutTable++; - c3 -= c2; - c2 -= c1; - c1 -= c0; - Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; - *Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16); - } - } else if (rz >= rx) { - X1 += Z1; - Y1 += X1; - for (; TotalOut; TotalOut--) { - c1 = LutTable[X1]; - c2 = LutTable[Y1]; - c3 = LutTable[Z1]; - c0 = *LutTable++; - c2 -= c1; - c1 -= c3; - c3 -= c0; - Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; - *Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16); - } - } else { - Z1 += X1; - Y1 += Z1; - for (; TotalOut; TotalOut--) { - c1 = LutTable[X1]; - c2 = LutTable[Y1]; - c3 = LutTable[Z1]; - c0 = *LutTable++; - c2 -= c3; - c3 -= c1; - c1 -= c0; - Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; - *Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16); - } - } - } else { - if (rx >= rz) { - X1 += Y1; - Z1 += X1; - for (; TotalOut; TotalOut--) { - c1 = LutTable[X1]; - c2 = LutTable[Y1]; - c3 = LutTable[Z1]; - c0 = *LutTable++; - c3 -= c1; - c1 -= c2; - c2 -= c0; - Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; - *Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16); - } - } else if (ry >= rz) { - Z1 += Y1; - X1 += Z1; - for (; TotalOut; TotalOut--) { - c1 = LutTable[X1]; - c2 = LutTable[Y1]; - c3 = LutTable[Z1]; - c0 = *LutTable++; - c1 -= c3; - c3 -= c2; - c2 -= c0; - Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; - *Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16); - } - } else { - Y1 += Z1; - X1 += Y1; - for (; TotalOut; TotalOut--) { - c1 = LutTable[X1]; - c2 = LutTable[Y1]; - c3 = LutTable[Z1]; - c0 = *LutTable++; - c1 -= c2; - c2 -= c3; - c3 -= c0; - Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; - *Output++ = (cmsUInt16Number) c0 + ((Rest + (Rest>>16))>>16); - } - } - } -} - - -#define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan]) -static -void Eval4Inputs(register const cmsUInt16Number Input[], - register cmsUInt16Number Output[], - register const cmsInterpParams* p16) -{ - const cmsUInt16Number* LutTable; - cmsS15Fixed16Number fk; - cmsS15Fixed16Number k0, rk; - int K0, K1; - cmsS15Fixed16Number fx, fy, fz; - cmsS15Fixed16Number rx, ry, rz; - int x0, y0, z0; - cmsS15Fixed16Number X0, X1, Y0, Y1, Z0, Z1; - cmsUInt32Number i; - cmsS15Fixed16Number c0, c1, c2, c3, Rest; - cmsUInt32Number OutChan; - cmsUInt16Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; - - - fk = _cmsToFixedDomain((int) Input[0] * p16 -> Domain[0]); - fx = _cmsToFixedDomain((int) Input[1] * p16 -> Domain[1]); - fy = _cmsToFixedDomain((int) Input[2] * p16 -> Domain[2]); - fz = _cmsToFixedDomain((int) Input[3] * p16 -> Domain[3]); - - k0 = FIXED_TO_INT(fk); - x0 = FIXED_TO_INT(fx); - y0 = FIXED_TO_INT(fy); - z0 = FIXED_TO_INT(fz); - - rk = FIXED_REST_TO_INT(fk); - rx = FIXED_REST_TO_INT(fx); - ry = FIXED_REST_TO_INT(fy); - rz = FIXED_REST_TO_INT(fz); - - K0 = p16 -> opta[3] * k0; - K1 = K0 + (Input[0] == 0xFFFFU ? 0 : p16->opta[3]); - - X0 = p16 -> opta[2] * x0; - X1 = X0 + (Input[1] == 0xFFFFU ? 0 : p16->opta[2]); - - Y0 = p16 -> opta[1] * y0; - Y1 = Y0 + (Input[2] == 0xFFFFU ? 0 : p16->opta[1]); - - Z0 = p16 -> opta[0] * z0; - Z1 = Z0 + (Input[3] == 0xFFFFU ? 0 : p16->opta[0]); - - LutTable = (cmsUInt16Number*) p16 -> Table; - LutTable += K0; - - for (OutChan=0; OutChan < p16 -> nOutputs; OutChan++) { - - c0 = DENS(X0, Y0, Z0); - - if (rx >= ry && ry >= rz) { - - c1 = DENS(X1, Y0, Z0) - c0; - c2 = DENS(X1, Y1, Z0) - DENS(X1, Y0, Z0); - c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); - - } - else - if (rx >= rz && rz >= ry) { - - c1 = DENS(X1, Y0, Z0) - c0; - c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); - c3 = DENS(X1, Y0, Z1) - DENS(X1, Y0, Z0); - - } - else - if (rz >= rx && rx >= ry) { - - c1 = DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1); - c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); - c3 = DENS(X0, Y0, Z1) - c0; - - } - else - if (ry >= rx && rx >= rz) { - - c1 = DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0); - c2 = DENS(X0, Y1, Z0) - c0; - c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); - - } - else - if (ry >= rz && rz >= rx) { - - c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); - c2 = DENS(X0, Y1, Z0) - c0; - c3 = DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0); - - } - else - if (rz >= ry && ry >= rx) { - - c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); - c2 = DENS(X0, Y1, Z1) - DENS(X0, Y0, Z1); - c3 = DENS(X0, Y0, Z1) - c0; - - } - else { - c1 = c2 = c3 = 0; - } - - Rest = c1 * rx + c2 * ry + c3 * rz; - - Tmp1[OutChan] = (cmsUInt16Number) c0 + ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest)); - } - - - LutTable = (cmsUInt16Number*) p16 -> Table; - LutTable += K1; - - for (OutChan=0; OutChan < p16 -> nOutputs; OutChan++) { - - c0 = DENS(X0, Y0, Z0); - - if (rx >= ry && ry >= rz) { - - c1 = DENS(X1, Y0, Z0) - c0; - c2 = DENS(X1, Y1, Z0) - DENS(X1, Y0, Z0); - c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); - - } - else - if (rx >= rz && rz >= ry) { - - c1 = DENS(X1, Y0, Z0) - c0; - c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); - c3 = DENS(X1, Y0, Z1) - DENS(X1, Y0, Z0); - - } - else - if (rz >= rx && rx >= ry) { - - c1 = DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1); - c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); - c3 = DENS(X0, Y0, Z1) - c0; - - } - else - if (ry >= rx && rx >= rz) { - - c1 = DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0); - c2 = DENS(X0, Y1, Z0) - c0; - c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); - - } - else - if (ry >= rz && rz >= rx) { - - c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); - c2 = DENS(X0, Y1, Z0) - c0; - c3 = DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0); - - } - else - if (rz >= ry && ry >= rx) { - - c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); - c2 = DENS(X0, Y1, Z1) - DENS(X0, Y0, Z1); - c3 = DENS(X0, Y0, Z1) - c0; - - } - else { - c1 = c2 = c3 = 0; - } - - Rest = c1 * rx + c2 * ry + c3 * rz; - - Tmp2[OutChan] = (cmsUInt16Number) c0 + ROUND_FIXED_TO_INT(_cmsToFixedDomain(Rest)); - } - - - - for (i=0; i < p16 -> nOutputs; i++) { - Output[i] = LinearInterp(rk, Tmp1[i], Tmp2[i]); - } -} -#undef DENS - - -// For more that 3 inputs (i.e., CMYK) -// evaluate two 3-dimensional interpolations and then linearly interpolate between them. - - -static -void Eval4InputsFloat(const cmsFloat32Number Input[], - cmsFloat32Number Output[], - const cmsInterpParams* p) -{ - const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table; - cmsFloat32Number rest; - cmsFloat32Number pk; - int k0, K0, K1; - const cmsFloat32Number* T; - cmsUInt32Number i; - cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; - cmsInterpParams p1; - - pk = fclamp(Input[0]) * p->Domain[0]; - k0 = _cmsQuickFloor(pk); - rest = pk - (cmsFloat32Number) k0; - - K0 = p -> opta[3] * k0; - K1 = K0 + (Input[0] >= 1.0 ? 0 : p->opta[3]); - - p1 = *p; - memmove(&p1.Domain[0], &p ->Domain[1], 3*sizeof(cmsUInt32Number)); - - T = LutTable + K0; - p1.Table = T; - - TetrahedralInterpFloat(Input + 1, Tmp1, &p1); - - T = LutTable + K1; - p1.Table = T; - TetrahedralInterpFloat(Input + 1, Tmp2, &p1); - - for (i=0; i < p -> nOutputs; i++) - { - cmsFloat32Number y0 = Tmp1[i]; - cmsFloat32Number y1 = Tmp2[i]; - - Output[i] = y0 + (y1 - y0) * rest; - } -} - - -static -void Eval5Inputs(register const cmsUInt16Number Input[], - register cmsUInt16Number Output[], - - register const cmsInterpParams* p16) -{ - const cmsUInt16Number* LutTable = (cmsUInt16Number*) p16 -> Table; - cmsS15Fixed16Number fk; - cmsS15Fixed16Number k0, rk; - int K0, K1; - const cmsUInt16Number* T; - cmsUInt32Number i; - cmsUInt16Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; - cmsInterpParams p1; - - - fk = _cmsToFixedDomain((cmsS15Fixed16Number) Input[0] * p16 -> Domain[0]); - k0 = FIXED_TO_INT(fk); - rk = FIXED_REST_TO_INT(fk); - - K0 = p16 -> opta[4] * k0; - K1 = p16 -> opta[4] * (k0 + (Input[0] != 0xFFFFU ? 1 : 0)); - - p1 = *p16; - memmove(&p1.Domain[0], &p16 ->Domain[1], 4*sizeof(cmsUInt32Number)); - - T = LutTable + K0; - p1.Table = T; - - Eval4Inputs(Input + 1, Tmp1, &p1); - - T = LutTable + K1; - p1.Table = T; - - Eval4Inputs(Input + 1, Tmp2, &p1); - - for (i=0; i < p16 -> nOutputs; i++) { - - Output[i] = LinearInterp(rk, Tmp1[i], Tmp2[i]); - } - -} - - -static -void Eval5InputsFloat(const cmsFloat32Number Input[], - cmsFloat32Number Output[], - const cmsInterpParams* p) -{ - const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table; - cmsFloat32Number rest; - cmsFloat32Number pk; - int k0, K0, K1; - const cmsFloat32Number* T; - cmsUInt32Number i; - cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; - cmsInterpParams p1; - - pk = fclamp(Input[0]) * p->Domain[0]; - k0 = _cmsQuickFloor(pk); - rest = pk - (cmsFloat32Number) k0; - - K0 = p -> opta[4] * k0; - K1 = K0 + (Input[0] >= 1.0 ? 0 : p->opta[4]); - - p1 = *p; - memmove(&p1.Domain[0], &p ->Domain[1], 4*sizeof(cmsUInt32Number)); - - T = LutTable + K0; - p1.Table = T; - - Eval4InputsFloat(Input + 1, Tmp1, &p1); - - T = LutTable + K1; - p1.Table = T; - - Eval4InputsFloat(Input + 1, Tmp2, &p1); - - for (i=0; i < p -> nOutputs; i++) { - - cmsFloat32Number y0 = Tmp1[i]; - cmsFloat32Number y1 = Tmp2[i]; - - Output[i] = y0 + (y1 - y0) * rest; - } -} - - - -static -void Eval6Inputs(register const cmsUInt16Number Input[], - register cmsUInt16Number Output[], - register const cmsInterpParams* p16) -{ - const cmsUInt16Number* LutTable = (cmsUInt16Number*) p16 -> Table; - cmsS15Fixed16Number fk; - cmsS15Fixed16Number k0, rk; - int K0, K1; - const cmsUInt16Number* T; - cmsUInt32Number i; - cmsUInt16Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; - cmsInterpParams p1; - - fk = _cmsToFixedDomain((cmsS15Fixed16Number) Input[0] * p16 -> Domain[0]); - k0 = FIXED_TO_INT(fk); - rk = FIXED_REST_TO_INT(fk); - - K0 = p16 -> opta[5] * k0; - K1 = p16 -> opta[5] * (k0 + (Input[0] != 0xFFFFU ? 1 : 0)); - - p1 = *p16; - memmove(&p1.Domain[0], &p16 ->Domain[1], 5*sizeof(cmsUInt32Number)); - - T = LutTable + K0; - p1.Table = T; - - Eval5Inputs(Input + 1, Tmp1, &p1); - - T = LutTable + K1; - p1.Table = T; - - Eval5Inputs(Input + 1, Tmp2, &p1); - - for (i=0; i < p16 -> nOutputs; i++) { - - Output[i] = LinearInterp(rk, Tmp1[i], Tmp2[i]); - } - -} - - -static -void Eval6InputsFloat(const cmsFloat32Number Input[], - cmsFloat32Number Output[], - const cmsInterpParams* p) -{ - const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table; - cmsFloat32Number rest; - cmsFloat32Number pk; - int k0, K0, K1; - const cmsFloat32Number* T; - cmsUInt32Number i; - cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; - cmsInterpParams p1; - - pk = fclamp(Input[0]) * p->Domain[0]; - k0 = _cmsQuickFloor(pk); - rest = pk - (cmsFloat32Number) k0; - - K0 = p -> opta[5] * k0; - K1 = K0 + (Input[0] >= 1.0 ? 0 : p->opta[5]); - - p1 = *p; - memmove(&p1.Domain[0], &p ->Domain[1], 5*sizeof(cmsUInt32Number)); - - T = LutTable + K0; - p1.Table = T; - - Eval5InputsFloat(Input + 1, Tmp1, &p1); - - T = LutTable + K1; - p1.Table = T; - - Eval5InputsFloat(Input + 1, Tmp2, &p1); - - for (i=0; i < p -> nOutputs; i++) { - - cmsFloat32Number y0 = Tmp1[i]; - cmsFloat32Number y1 = Tmp2[i]; - - Output[i] = y0 + (y1 - y0) * rest; - } -} - - -static -void Eval7Inputs(register const cmsUInt16Number Input[], - register cmsUInt16Number Output[], - register const cmsInterpParams* p16) -{ - const cmsUInt16Number* LutTable = (cmsUInt16Number*) p16 -> Table; - cmsS15Fixed16Number fk; - cmsS15Fixed16Number k0, rk; - int K0, K1; - const cmsUInt16Number* T; - cmsUInt32Number i; - cmsUInt16Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; - cmsInterpParams p1; - - - fk = _cmsToFixedDomain((cmsS15Fixed16Number) Input[0] * p16 -> Domain[0]); - k0 = FIXED_TO_INT(fk); - rk = FIXED_REST_TO_INT(fk); - - K0 = p16 -> opta[6] * k0; - K1 = p16 -> opta[6] * (k0 + (Input[0] != 0xFFFFU ? 1 : 0)); - - p1 = *p16; - memmove(&p1.Domain[0], &p16 ->Domain[1], 6*sizeof(cmsUInt32Number)); - - T = LutTable + K0; - p1.Table = T; - - Eval6Inputs(Input + 1, Tmp1, &p1); - - T = LutTable + K1; - p1.Table = T; - - Eval6Inputs(Input + 1, Tmp2, &p1); - - for (i=0; i < p16 -> nOutputs; i++) { - Output[i] = LinearInterp(rk, Tmp1[i], Tmp2[i]); - } -} - - -static -void Eval7InputsFloat(const cmsFloat32Number Input[], - cmsFloat32Number Output[], - const cmsInterpParams* p) -{ - const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table; - cmsFloat32Number rest; - cmsFloat32Number pk; - int k0, K0, K1; - const cmsFloat32Number* T; - cmsUInt32Number i; - cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; - cmsInterpParams p1; - - pk = fclamp(Input[0]) * p->Domain[0]; - k0 = _cmsQuickFloor(pk); - rest = pk - (cmsFloat32Number) k0; - - K0 = p -> opta[6] * k0; - K1 = K0 + (Input[0] >= 1.0 ? 0 : p->opta[6]); - - p1 = *p; - memmove(&p1.Domain[0], &p ->Domain[1], 6*sizeof(cmsUInt32Number)); - - T = LutTable + K0; - p1.Table = T; - - Eval6InputsFloat(Input + 1, Tmp1, &p1); - - T = LutTable + K1; - p1.Table = T; - - Eval6InputsFloat(Input + 1, Tmp2, &p1); - - - for (i=0; i < p -> nOutputs; i++) { - - cmsFloat32Number y0 = Tmp1[i]; - cmsFloat32Number y1 = Tmp2[i]; - - Output[i] = y0 + (y1 - y0) * rest; - - } -} - -static -void Eval8Inputs(register const cmsUInt16Number Input[], - register cmsUInt16Number Output[], - register const cmsInterpParams* p16) -{ - const cmsUInt16Number* LutTable = (cmsUInt16Number*) p16 -> Table; - cmsS15Fixed16Number fk; - cmsS15Fixed16Number k0, rk; - int K0, K1; - const cmsUInt16Number* T; - cmsUInt32Number i; - cmsUInt16Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; - cmsInterpParams p1; - - fk = _cmsToFixedDomain((cmsS15Fixed16Number) Input[0] * p16 -> Domain[0]); - k0 = FIXED_TO_INT(fk); - rk = FIXED_REST_TO_INT(fk); - - K0 = p16 -> opta[7] * k0; - K1 = p16 -> opta[7] * (k0 + (Input[0] != 0xFFFFU ? 1 : 0)); - - p1 = *p16; - memmove(&p1.Domain[0], &p16 ->Domain[1], 7*sizeof(cmsUInt32Number)); - - T = LutTable + K0; - p1.Table = T; - - Eval7Inputs(Input + 1, Tmp1, &p1); - - T = LutTable + K1; - p1.Table = T; - Eval7Inputs(Input + 1, Tmp2, &p1); - - for (i=0; i < p16 -> nOutputs; i++) { - Output[i] = LinearInterp(rk, Tmp1[i], Tmp2[i]); - } -} - - - -static -void Eval8InputsFloat(const cmsFloat32Number Input[], - cmsFloat32Number Output[], - const cmsInterpParams* p) -{ - const cmsFloat32Number* LutTable = (cmsFloat32Number*) p -> Table; - cmsFloat32Number rest; - cmsFloat32Number pk; - int k0, K0, K1; - const cmsFloat32Number* T; - cmsUInt32Number i; - cmsFloat32Number Tmp1[MAX_STAGE_CHANNELS], Tmp2[MAX_STAGE_CHANNELS]; - cmsInterpParams p1; - - pk = fclamp(Input[0]) * p->Domain[0]; - k0 = _cmsQuickFloor(pk); - rest = pk - (cmsFloat32Number) k0; - - K0 = p -> opta[7] * k0; - K1 = K0 + (Input[0] >= 1.0 ? 0 : p->opta[7]); - - p1 = *p; - memmove(&p1.Domain[0], &p ->Domain[1], 7*sizeof(cmsUInt32Number)); - - T = LutTable + K0; - p1.Table = T; - - Eval7InputsFloat(Input + 1, Tmp1, &p1); - - T = LutTable + K1; - p1.Table = T; - - Eval7InputsFloat(Input + 1, Tmp2, &p1); - - - for (i=0; i < p -> nOutputs; i++) { - - cmsFloat32Number y0 = Tmp1[i]; - cmsFloat32Number y1 = Tmp2[i]; - - Output[i] = y0 + (y1 - y0) * rest; - } -} - -// The default factory -static -cmsInterpFunction DefaultInterpolatorsFactory(cmsUInt32Number nInputChannels, cmsUInt32Number nOutputChannels, cmsUInt32Number dwFlags) -{ - - cmsInterpFunction Interpolation; - cmsBool IsFloat = (dwFlags & CMS_LERP_FLAGS_FLOAT); - cmsBool IsTrilinear = (dwFlags & CMS_LERP_FLAGS_TRILINEAR); - - memset(&Interpolation, 0, sizeof(Interpolation)); - - // Safety check - if (nInputChannels >= 4 && nOutputChannels >= MAX_STAGE_CHANNELS) - return Interpolation; - - switch (nInputChannels) { - - case 1: // Gray LUT / linear - - if (nOutputChannels == 1) { - - if (IsFloat) - Interpolation.LerpFloat = LinLerp1Dfloat; - else - Interpolation.Lerp16 = LinLerp1D; - - } - else { - - if (IsFloat) - Interpolation.LerpFloat = Eval1InputFloat; - else - Interpolation.Lerp16 = Eval1Input; - } - break; - - case 2: // Duotone - if (IsFloat) - Interpolation.LerpFloat = BilinearInterpFloat; - else - Interpolation.Lerp16 = BilinearInterp16; - break; - - case 3: // RGB et al - - if (IsTrilinear) { - - if (IsFloat) - Interpolation.LerpFloat = TrilinearInterpFloat; - else - Interpolation.Lerp16 = TrilinearInterp16; - } - else { - - if (IsFloat) - Interpolation.LerpFloat = TetrahedralInterpFloat; - else { - - Interpolation.Lerp16 = TetrahedralInterp16; - } - } - break; - - case 4: // CMYK lut - - if (IsFloat) - Interpolation.LerpFloat = Eval4InputsFloat; - else - Interpolation.Lerp16 = Eval4Inputs; - break; - - case 5: // 5 Inks - if (IsFloat) - Interpolation.LerpFloat = Eval5InputsFloat; - else - Interpolation.Lerp16 = Eval5Inputs; - break; - - case 6: // 6 Inks - if (IsFloat) - Interpolation.LerpFloat = Eval6InputsFloat; - else - Interpolation.Lerp16 = Eval6Inputs; - break; - - case 7: // 7 inks - if (IsFloat) - Interpolation.LerpFloat = Eval7InputsFloat; - else - Interpolation.Lerp16 = Eval7Inputs; - break; - - case 8: // 8 inks - if (IsFloat) - Interpolation.LerpFloat = Eval8InputsFloat; - else - Interpolation.Lerp16 = Eval8Inputs; - break; - - break; - - default: - Interpolation.Lerp16 = NULL; - } - - return Interpolation; -} diff --git a/third_party/lcms2-2.6/src/cmsio0.c b/third_party/lcms2-2.6/src/cmsio0.c deleted file mode 100644 index 3ed730a92a..0000000000 --- a/third_party/lcms2-2.6/src/cmsio0.c +++ /dev/null @@ -1,1895 +0,0 @@ -//--------------------------------------------------------------------------------- -// -// 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" - -// Generic I/O, tag dictionary management, profile struct - -// IOhandlers are abstractions used by littleCMS to read from whatever file, stream, -// memory block or any storage. Each IOhandler provides implementations for read, -// write, seek and tell functions. LittleCMS code deals with IO across those objects. -// In this way, is easier to add support for new storage media. - -// NULL stream, for taking care of used space ------------------------------------- - -// NULL IOhandler basically does nothing but keep track on how many bytes have been -// written. This is handy when creating profiles, where the file size is needed in the -// header. Then, whole profile is serialized across NULL IOhandler and a second pass -// writes the bytes to the pertinent IOhandler. - -typedef struct { - cmsUInt32Number Pointer; // Points to current location -} FILENULL; - -static -cmsUInt32Number NULLRead(cmsIOHANDLER* iohandler, void *Buffer, cmsUInt32Number size, cmsUInt32Number count) -{ - FILENULL* ResData = (FILENULL*) iohandler ->stream; - - cmsUInt32Number len = size * count; - ResData -> Pointer += len; - return count; - - cmsUNUSED_PARAMETER(Buffer); -} - -static -cmsBool NULLSeek(cmsIOHANDLER* iohandler, cmsUInt32Number offset) -{ - FILENULL* ResData = (FILENULL*) iohandler ->stream; - - ResData ->Pointer = offset; - return TRUE; -} - -static -cmsUInt32Number NULLTell(cmsIOHANDLER* iohandler) -{ - FILENULL* ResData = (FILENULL*) iohandler ->stream; - return ResData -> Pointer; -} - -static -cmsBool NULLWrite(cmsIOHANDLER* iohandler, cmsUInt32Number size, const void *Ptr) -{ - FILENULL* ResData = (FILENULL*) iohandler ->stream; - - ResData ->Pointer += size; - if (ResData ->Pointer > iohandler->UsedSpace) - iohandler->UsedSpace = ResData ->Pointer; - - return TRUE; - - cmsUNUSED_PARAMETER(Ptr); -} - -static -cmsBool NULLClose(cmsIOHANDLER* iohandler) -{ - FILENULL* ResData = (FILENULL*) iohandler ->stream; - - _cmsFree(iohandler ->ContextID, ResData); - _cmsFree(iohandler ->ContextID, iohandler); - return TRUE; -} - -// The NULL IOhandler creator -cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromNULL(cmsContext ContextID) -{ - struct _cms_io_handler* iohandler = NULL; - FILENULL* fm = NULL; - - iohandler = (struct _cms_io_handler*) _cmsMallocZero(ContextID, sizeof(struct _cms_io_handler)); - if (iohandler == NULL) return NULL; - - fm = (FILENULL*) _cmsMallocZero(ContextID, sizeof(FILENULL)); - if (fm == NULL) goto Error; - - fm ->Pointer = 0; - - iohandler ->ContextID = ContextID; - iohandler ->stream = (void*) fm; - iohandler ->UsedSpace = 0; - iohandler ->ReportedSize = 0; - iohandler ->PhysicalFile[0] = 0; - - iohandler ->Read = NULLRead; - iohandler ->Seek = NULLSeek; - iohandler ->Close = NULLClose; - iohandler ->Tell = NULLTell; - iohandler ->Write = NULLWrite; - - return iohandler; - -Error: - if (iohandler) _cmsFree(ContextID, iohandler); - return NULL; - -} - - -// Memory-based stream -------------------------------------------------------------- - -// Those functions implements an iohandler which takes a block of memory as storage medium. - -typedef struct { - cmsUInt8Number* Block; // Points to allocated memory - cmsUInt32Number Size; // Size of allocated memory - cmsUInt32Number Pointer; // Points to current location - int FreeBlockOnClose; // As title - -} FILEMEM; - -static -cmsUInt32Number MemoryRead(struct _cms_io_handler* iohandler, void *Buffer, cmsUInt32Number size, cmsUInt32Number count) -{ - FILEMEM* ResData = (FILEMEM*) iohandler ->stream; - cmsUInt8Number* Ptr; - cmsUInt32Number len = size * count; - - if (ResData -> Pointer + len > ResData -> Size){ - - len = (ResData -> Size - ResData -> Pointer); - cmsSignalError(iohandler ->ContextID, cmsERROR_READ, "Read from memory error. Got %d bytes, block should be of %d bytes", len, count * size); - return 0; - } - - Ptr = ResData -> Block; - Ptr += ResData -> Pointer; - memmove(Buffer, Ptr, len); - ResData -> Pointer += len; - - return count; -} - -// SEEK_CUR is assumed -static -cmsBool MemorySeek(struct _cms_io_handler* iohandler, cmsUInt32Number offset) -{ - FILEMEM* ResData = (FILEMEM*) iohandler ->stream; - - if (offset > ResData ->Size) { - cmsSignalError(iohandler ->ContextID, cmsERROR_SEEK, "Too few data; probably corrupted profile"); - return FALSE; - } - - ResData ->Pointer = offset; - return TRUE; -} - -// Tell for memory -static -cmsUInt32Number MemoryTell(struct _cms_io_handler* iohandler) -{ - FILEMEM* ResData = (FILEMEM*) iohandler ->stream; - - if (ResData == NULL) return 0; - return ResData -> Pointer; -} - - -// Writes data to memory, also keeps used space for further reference. -static -cmsBool MemoryWrite(struct _cms_io_handler* iohandler, cmsUInt32Number size, const void *Ptr) -{ - FILEMEM* ResData = (FILEMEM*) iohandler ->stream; - - if (ResData == NULL) return FALSE; // Housekeeping - - // Check for available space. Clip. - if (ResData->Pointer + size > ResData->Size) { - size = ResData ->Size - ResData->Pointer; - } - - if (size == 0) return TRUE; // Write zero bytes is ok, but does nothing - - memmove(ResData ->Block + ResData ->Pointer, Ptr, size); - ResData ->Pointer += size; - - if (ResData ->Pointer > iohandler->UsedSpace) - iohandler->UsedSpace = ResData ->Pointer; - - return TRUE; -} - - -static -cmsBool MemoryClose(struct _cms_io_handler* iohandler) -{ - FILEMEM* ResData = (FILEMEM*) iohandler ->stream; - - if (ResData ->FreeBlockOnClose) { - - if (ResData ->Block) _cmsFree(iohandler ->ContextID, ResData ->Block); - } - - _cmsFree(iohandler ->ContextID, ResData); - _cmsFree(iohandler ->ContextID, iohandler); - - return TRUE; -} - -// Create a iohandler for memory block. AccessMode=='r' assumes the iohandler is going to read, and makes -// a copy of the memory block for letting user to free the memory after invoking open profile. In write -// mode ("w"), Buffere points to the begin of memory block to be written. -cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromMem(cmsContext ContextID, void *Buffer, cmsUInt32Number size, const char* AccessMode) -{ - cmsIOHANDLER* iohandler = NULL; - FILEMEM* fm = NULL; - - _cmsAssert(AccessMode != NULL); - - iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER)); - if (iohandler == NULL) return NULL; - - switch (*AccessMode) { - - case 'r': - fm = (FILEMEM*) _cmsMallocZero(ContextID, sizeof(FILEMEM)); - if (fm == NULL) goto Error; - - if (Buffer == NULL) { - cmsSignalError(ContextID, cmsERROR_READ, "Couldn't read profile from NULL pointer"); - goto Error; - } - - fm ->Block = (cmsUInt8Number*) _cmsMalloc(ContextID, size); - if (fm ->Block == NULL) { - - _cmsFree(ContextID, fm); - _cmsFree(ContextID, iohandler); - cmsSignalError(ContextID, cmsERROR_READ, "Couldn't allocate %ld bytes for profile", size); - return NULL; - } - - - memmove(fm->Block, Buffer, size); - fm ->FreeBlockOnClose = TRUE; - fm ->Size = size; - fm ->Pointer = 0; - iohandler -> ReportedSize = size; - break; - - case 'w': - fm = (FILEMEM*) _cmsMallocZero(ContextID, sizeof(FILEMEM)); - if (fm == NULL) goto Error; - - fm ->Block = (cmsUInt8Number*) Buffer; - fm ->FreeBlockOnClose = FALSE; - fm ->Size = size; - fm ->Pointer = 0; - iohandler -> ReportedSize = 0; - break; - - default: - cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown access mode '%c'", *AccessMode); - return NULL; - } - - iohandler ->ContextID = ContextID; - iohandler ->stream = (void*) fm; - iohandler ->UsedSpace = 0; - iohandler ->PhysicalFile[0] = 0; - - iohandler ->Read = MemoryRead; - iohandler ->Seek = MemorySeek; - iohandler ->Close = MemoryClose; - iohandler ->Tell = MemoryTell; - iohandler ->Write = MemoryWrite; - - return iohandler; - -Error: - if (fm) _cmsFree(ContextID, fm); - if (iohandler) _cmsFree(ContextID, iohandler); - return NULL; -} - -// File-based stream ------------------------------------------------------- - -// Read count elements of size bytes each. Return number of elements read -static -cmsUInt32Number FileRead(cmsIOHANDLER* iohandler, void *Buffer, cmsUInt32Number size, cmsUInt32Number count) -{ - cmsUInt32Number nReaded = (cmsUInt32Number) fread(Buffer, size, count, (FILE*) iohandler->stream); - - if (nReaded != count) { - cmsSignalError(iohandler ->ContextID, cmsERROR_FILE, "Read error. Got %d bytes, block should be of %d bytes", nReaded * size, count * size); - return 0; - } - - return nReaded; -} - -// Postion file pointer in the file -static -cmsBool FileSeek(cmsIOHANDLER* iohandler, cmsUInt32Number offset) -{ - if (fseek((FILE*) iohandler ->stream, (long) offset, SEEK_SET) != 0) { - - cmsSignalError(iohandler ->ContextID, cmsERROR_FILE, "Seek error; probably corrupted file"); - return FALSE; - } - - return TRUE; -} - -// Returns file pointer position -static -cmsUInt32Number FileTell(cmsIOHANDLER* iohandler) -{ - return (cmsUInt32Number) ftell((FILE*)iohandler ->stream); -} - -// Writes data to stream, also keeps used space for further reference. Returns TRUE on success, FALSE on error -static -cmsBool FileWrite(cmsIOHANDLER* iohandler, cmsUInt32Number size, const void* Buffer) -{ - if (size == 0) return TRUE; // We allow to write 0 bytes, but nothing is written - - iohandler->UsedSpace += size; - return (fwrite(Buffer, size, 1, (FILE*) iohandler->stream) == 1); -} - -// Closes the file -static -cmsBool FileClose(cmsIOHANDLER* iohandler) -{ - if (fclose((FILE*) iohandler ->stream) != 0) return FALSE; - _cmsFree(iohandler ->ContextID, iohandler); - return TRUE; -} - -// Create a iohandler for disk based files. -cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromFile(cmsContext ContextID, const char* FileName, const char* AccessMode) -{ - cmsIOHANDLER* iohandler = NULL; - FILE* fm = NULL; - - _cmsAssert(FileName != NULL); - _cmsAssert(AccessMode != NULL); - - iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER)); - if (iohandler == NULL) return NULL; - - switch (*AccessMode) { - - case 'r': - fm = fopen(FileName, "rb"); - if (fm == NULL) { - _cmsFree(ContextID, iohandler); - cmsSignalError(ContextID, cmsERROR_FILE, "File '%s' not found", FileName); - return NULL; - } - iohandler -> ReportedSize = (cmsUInt32Number) cmsfilelength(fm); - break; - - case 'w': - fm = fopen(FileName, "wb"); - if (fm == NULL) { - _cmsFree(ContextID, iohandler); - cmsSignalError(ContextID, cmsERROR_FILE, "Couldn't create '%s'", FileName); - return NULL; - } - iohandler -> ReportedSize = 0; - break; - - default: - _cmsFree(ContextID, iohandler); - cmsSignalError(ContextID, cmsERROR_FILE, "Unknown access mode '%c'", *AccessMode); - return NULL; - } - - iohandler ->ContextID = ContextID; - iohandler ->stream = (void*) fm; - iohandler ->UsedSpace = 0; - - // Keep track of the original file - strncpy(iohandler -> PhysicalFile, FileName, sizeof(iohandler -> PhysicalFile)-1); - iohandler -> PhysicalFile[sizeof(iohandler -> PhysicalFile)-1] = 0; - - iohandler ->Read = FileRead; - iohandler ->Seek = FileSeek; - iohandler ->Close = FileClose; - iohandler ->Tell = FileTell; - iohandler ->Write = FileWrite; - - return iohandler; -} - -// Create a iohandler for stream based files -cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromStream(cmsContext ContextID, FILE* Stream) -{ - cmsIOHANDLER* iohandler = NULL; - - iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER)); - if (iohandler == NULL) return NULL; - - iohandler -> ContextID = ContextID; - iohandler -> stream = (void*) Stream; - iohandler -> UsedSpace = 0; - iohandler -> ReportedSize = (cmsUInt32Number) cmsfilelength(Stream); - iohandler -> PhysicalFile[0] = 0; - - iohandler ->Read = FileRead; - iohandler ->Seek = FileSeek; - iohandler ->Close = FileClose; - iohandler ->Tell = FileTell; - iohandler ->Write = FileWrite; - - return iohandler; -} - - - -// Close an open IO handler -cmsBool CMSEXPORT cmsCloseIOhandler(cmsIOHANDLER* io) -{ - return io -> Close(io); -} - -// ------------------------------------------------------------------------------------------------------- - -#ifdef _WIN32_WCE -time_t wceex_time(time_t *timer); -struct tm * wceex_gmtime(const time_t *timer); - -#define time wceex_time -#define gmtime wceex_gmtime -#endif - -// Creates an empty structure holding all required parameters -cmsHPROFILE CMSEXPORT cmsCreateProfilePlaceholder(cmsContext ContextID) -{ - time_t now = time(NULL); - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) _cmsMallocZero(ContextID, sizeof(_cmsICCPROFILE)); - if (Icc == NULL) return NULL; - - Icc ->ContextID = ContextID; - - // Set it to empty - Icc -> TagCount = 0; - - // Set default version - Icc ->Version = 0x02100000; - - // 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; -} - -cmsContext CMSEXPORT cmsGetProfileContextID(cmsHPROFILE hProfile) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - - if (Icc == NULL) return NULL; - return Icc -> ContextID; -} - - -// Return the number of tags -cmsInt32Number CMSEXPORT cmsGetTagCount(cmsHPROFILE hProfile) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - if (Icc == NULL) return -1; - - return Icc->TagCount; -} - -// Return the tag signature of a given tag number -cmsTagSignature CMSEXPORT cmsGetTagSignature(cmsHPROFILE hProfile, cmsUInt32Number n) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - - if (n > Icc->TagCount) return (cmsTagSignature) 0; // Mark as not available - if (n >= MAX_TABLE_TAG) return (cmsTagSignature) 0; // As double check - - return Icc ->TagNames[n]; -} - - -static -int SearchOneTag(_cmsICCPROFILE* Profile, cmsTagSignature sig) -{ - cmsUInt32Number i; - - for (i=0; i < Profile -> TagCount; i++) { - - if (sig == Profile -> TagNames[i]) - return i; - } - - return -1; -} - -// Search for a specific tag in tag dictionary. Returns position or -1 if tag not found. -// If followlinks is turned on, then the position of the linked tag is returned -int _cmsSearchTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, cmsBool lFollowLinks) -{ - int n; - cmsTagSignature LinkedSig; - - do { - - // Search for given tag in ICC profile directory - n = SearchOneTag(Icc, sig); - if (n < 0) - return -1; // Not found - - if (!lFollowLinks) - return n; // Found, don't follow links - - // Is this a linked tag? - LinkedSig = Icc ->TagLinked[n]; - - // Yes, follow link - if (LinkedSig != (cmsTagSignature) 0) { - // fix bug mantis id#0055942 - // assume that TRCTag and ColorantTag can't be linked. - // Xiaochuan Liu 2014-04-23 - if ((sig == cmsSigRedTRCTag || sig == cmsSigGreenTRCTag || sig == cmsSigBlueTRCTag) && - (LinkedSig == cmsSigRedColorantTag || LinkedSig == cmsSigGreenColorantTag || LinkedSig == cmsSigBlueColorantTag)) - { - return n; - } - sig = LinkedSig; - } - - } while (LinkedSig != (cmsTagSignature) 0); - - 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]); - Icc ->TagPtrs[i] = NULL; - } - } - - } -} - - -// Creates a new tag entry -static -cmsBool _cmsNewTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, int* NewPos) -{ - int i; - - // Search for the tag - i = _cmsSearchTag(Icc, sig, FALSE); - if (i >= 0) { - - // Already exists? delete it - _cmsDeleteTagByPos(Icc, i); - *NewPos = i; - } - else { - - // No, make a new one - - if (Icc -> TagCount >= MAX_TABLE_TAG) { - cmsSignalError(Icc ->ContextID, cmsERROR_RANGE, "Too many tags (%d)", MAX_TABLE_TAG); - return FALSE; - } - - *NewPos = Icc ->TagCount; - Icc -> TagCount++; - } - - return TRUE; -} - - -// Check existance -cmsBool CMSEXPORT cmsIsTag(cmsHPROFILE hProfile, cmsTagSignature sig) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) (void*) hProfile; - return _cmsSearchTag(Icc, sig, FALSE) >= 0; -} - - -// Read profile header and validate it -cmsBool _cmsReadHeader(_cmsICCPROFILE* Icc) -{ - cmsTagEntry Tag; - cmsICCHeader Header; - cmsUInt32Number i, j; - cmsUInt32Number HeaderSize; - cmsIOHANDLER* io = Icc ->IOhandler; - cmsUInt32Number TagCount; - - - // Read the header - if (io -> Read(io, &Header, sizeof(cmsICCHeader), 1) != 1) { - return FALSE; - } - - // Validate file as an ICC profile - if (_cmsAdjustEndianess32(Header.magic) != cmsMagicNumber) { - cmsSignalError(Icc ->ContextID, cmsERROR_BAD_SIGNATURE, "not an ICC profile, invalid signature"); - return FALSE; - } - - // Adjust endianess of the used parameters - Icc -> DeviceClass = (cmsProfileClassSignature) _cmsAdjustEndianess32(Header.deviceClass); - Icc -> ColorSpace = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Header.colorSpace); - Icc -> PCS = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Header.pcs); - - Icc -> RenderingIntent = _cmsAdjustEndianess32(Header.renderingIntent); - Icc -> flags = _cmsAdjustEndianess32(Header.flags); - Icc -> manufacturer = _cmsAdjustEndianess32(Header.manufacturer); - Icc -> model = _cmsAdjustEndianess32(Header.model); - Icc -> creator = _cmsAdjustEndianess32(Header.creator); - - _cmsAdjustEndianess64(&Icc -> attributes, &Header.attributes); - Icc -> Version = _cmsAdjustEndianess32(Header.version); - - // Get size as reported in header - HeaderSize = _cmsAdjustEndianess32(Header.size); - - // Make sure HeaderSize is lower than profile size - if (HeaderSize >= Icc ->IOhandler ->ReportedSize) - HeaderSize = Icc ->IOhandler ->ReportedSize; - - - // Get creation date/time - _cmsDecodeDateTimeNumber(&Header.date, &Icc ->Created); - - // The profile ID are 32 raw bytes - memmove(Icc ->ProfileID.ID32, Header.profileID.ID32, 16); - - - // Read tag directory - if (!_cmsReadUInt32Number(io, &TagCount)) return FALSE; - if (TagCount > MAX_TABLE_TAG) { - - cmsSignalError(Icc ->ContextID, cmsERROR_RANGE, "Too many tags (%d)", TagCount); - return FALSE; - } - - - // Read tag directory - Icc -> TagCount = 0; - for (i=0; i < TagCount; i++) { - - if (!_cmsReadUInt32Number(io, (cmsUInt32Number *) &Tag.sig)) return FALSE; - if (!_cmsReadUInt32Number(io, &Tag.offset)) return FALSE; - if (!_cmsReadUInt32Number(io, &Tag.size)) return FALSE; - - // Perform some sanity check. Offset + size should fall inside file. - if (Tag.offset + Tag.size > HeaderSize || - Tag.offset + Tag.size < Tag.offset) - continue; - - Icc -> TagNames[Icc ->TagCount] = Tag.sig; - Icc -> TagOffsets[Icc ->TagCount] = Tag.offset; - Icc -> TagSizes[Icc ->TagCount] = Tag.size; - - // Search for links - for (j=0; j < Icc ->TagCount; j++) { - - if ((Icc ->TagOffsets[j] == Tag.offset) && - (Icc ->TagSizes[j] == Tag.size) && - (Icc ->TagNames[j] == Tag.sig)) { - - Icc ->TagLinked[Icc ->TagCount] = Icc ->TagNames[j]; - } - - } - - Icc ->TagCount++; - } - - return TRUE; -} - -// Saves profile header -cmsBool _cmsWriteHeader(_cmsICCPROFILE* Icc, cmsUInt32Number UsedSpace) -{ - cmsICCHeader Header; - cmsUInt32Number i; - cmsTagEntry Tag; - cmsInt32Number Count = 0; - - Header.size = _cmsAdjustEndianess32(UsedSpace); - Header.cmmId = _cmsAdjustEndianess32(lcmsSignature); - Header.version = _cmsAdjustEndianess32(Icc ->Version); - - Header.deviceClass = (cmsProfileClassSignature) _cmsAdjustEndianess32(Icc -> DeviceClass); - Header.colorSpace = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Icc -> ColorSpace); - Header.pcs = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Icc -> PCS); - - // NOTE: in v4 Timestamp must be in UTC rather than in local time - _cmsEncodeDateTimeNumber(&Header.date, &Icc ->Created); - - Header.magic = _cmsAdjustEndianess32(cmsMagicNumber); - -#ifdef CMS_IS_WINDOWS_ - Header.platform = (cmsPlatformSignature) _cmsAdjustEndianess32(cmsSigMicrosoft); -#else - Header.platform = (cmsPlatformSignature) _cmsAdjustEndianess32(cmsSigMacintosh); -#endif - - Header.flags = _cmsAdjustEndianess32(Icc -> flags); - Header.manufacturer = _cmsAdjustEndianess32(Icc -> manufacturer); - Header.model = _cmsAdjustEndianess32(Icc -> model); - - _cmsAdjustEndianess64(&Header.attributes, &Icc -> attributes); - - // Rendering intent in the header (for embedded profiles) - Header.renderingIntent = _cmsAdjustEndianess32(Icc -> RenderingIntent); - - // Illuminant is always D50 - Header.illuminant.X = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(cmsD50_XYZ()->X)); - Header.illuminant.Y = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(cmsD50_XYZ()->Y)); - Header.illuminant.Z = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(cmsD50_XYZ()->Z)); - - // Created by LittleCMS (that's me!) - Header.creator = _cmsAdjustEndianess32(lcmsSignature); - - memset(&Header.reserved, 0, sizeof(Header.reserved)); - - // Set profile ID. Endianess is always big endian - memmove(&Header.profileID, &Icc ->ProfileID, 16); - - // Dump the header - if (!Icc -> IOhandler->Write(Icc->IOhandler, sizeof(cmsICCHeader), &Header)) return FALSE; - - // Saves Tag directory - - // Get true count - for (i=0; i < Icc -> TagCount; i++) { - if (Icc ->TagNames[i] != 0) - Count++; - } - - // Store number of tags - if (!_cmsWriteUInt32Number(Icc ->IOhandler, Count)) return FALSE; - - for (i=0; i < Icc -> TagCount; i++) { - - if (Icc ->TagNames[i] == 0) continue; // It is just a placeholder - - Tag.sig = (cmsTagSignature) _cmsAdjustEndianess32((cmsInt32Number) Icc -> TagNames[i]); - Tag.offset = _cmsAdjustEndianess32((cmsInt32Number) Icc -> TagOffsets[i]); - Tag.size = _cmsAdjustEndianess32((cmsInt32Number) Icc -> TagSizes[i]); - - if (!Icc ->IOhandler -> Write(Icc-> IOhandler, sizeof(cmsTagEntry), &Tag)) return FALSE; - } - - return TRUE; -} - -// ----------------------------------------------------------------------- Set/Get several struct members - - -cmsUInt32Number CMSEXPORT cmsGetHeaderRenderingIntent(cmsHPROFILE hProfile) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - return Icc -> RenderingIntent; -} - -void CMSEXPORT cmsSetHeaderRenderingIntent(cmsHPROFILE hProfile, cmsUInt32Number RenderingIntent) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - Icc -> RenderingIntent = RenderingIntent; -} - -cmsUInt32Number CMSEXPORT cmsGetHeaderFlags(cmsHPROFILE hProfile) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - return (cmsUInt32Number) Icc -> flags; -} - -void CMSEXPORT cmsSetHeaderFlags(cmsHPROFILE hProfile, cmsUInt32Number Flags) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - Icc -> flags = (cmsUInt32Number) Flags; -} - -cmsUInt32Number CMSEXPORT cmsGetHeaderManufacturer(cmsHPROFILE hProfile) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - return Icc ->manufacturer; -} - -void CMSEXPORT cmsSetHeaderManufacturer(cmsHPROFILE hProfile, cmsUInt32Number manufacturer) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - Icc -> manufacturer = manufacturer; -} - -cmsUInt32Number CMSEXPORT cmsGetHeaderCreator(cmsHPROFILE hProfile) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - return Icc ->creator; -} - -cmsUInt32Number CMSEXPORT cmsGetHeaderModel(cmsHPROFILE hProfile) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - return Icc ->model; -} - -void CMSEXPORT cmsSetHeaderModel(cmsHPROFILE hProfile, cmsUInt32Number model) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - Icc -> model = model; -} - -void CMSEXPORT cmsGetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number* Flags) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - memmove(Flags, &Icc -> attributes, sizeof(cmsUInt64Number)); -} - -void CMSEXPORT cmsSetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number Flags) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - memmove(&Icc -> attributes, &Flags, sizeof(cmsUInt64Number)); -} - -void CMSEXPORT cmsGetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - memmove(ProfileID, Icc ->ProfileID.ID8, 16); -} - -void CMSEXPORT cmsSetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - memmove(&Icc -> ProfileID, ProfileID, 16); -} - -cmsBool CMSEXPORT cmsGetHeaderCreationDateTime(cmsHPROFILE hProfile, struct tm *Dest) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - memmove(Dest, &Icc ->Created, sizeof(struct tm)); - return TRUE; -} - -cmsColorSpaceSignature CMSEXPORT cmsGetPCS(cmsHPROFILE hProfile) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - return Icc -> PCS; -} - -void CMSEXPORT cmsSetPCS(cmsHPROFILE hProfile, cmsColorSpaceSignature pcs) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - Icc -> PCS = pcs; -} - -cmsColorSpaceSignature CMSEXPORT cmsGetColorSpace(cmsHPROFILE hProfile) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - return Icc -> ColorSpace; -} - -void CMSEXPORT cmsSetColorSpace(cmsHPROFILE hProfile, cmsColorSpaceSignature sig) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - Icc -> ColorSpace = sig; -} - -cmsProfileClassSignature CMSEXPORT cmsGetDeviceClass(cmsHPROFILE hProfile) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - return Icc -> DeviceClass; -} - -void CMSEXPORT cmsSetDeviceClass(cmsHPROFILE hProfile, cmsProfileClassSignature sig) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - Icc -> DeviceClass = sig; -} - -cmsUInt32Number CMSEXPORT cmsGetEncodedICCversion(cmsHPROFILE hProfile) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - return Icc -> Version; -} - -void CMSEXPORT cmsSetEncodedICCversion(cmsHPROFILE hProfile, cmsUInt32Number Version) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - Icc -> Version = Version; -} - -// Get an hexadecimal number with same digits as v -static -cmsUInt32Number BaseToBase(cmsUInt32Number in, int BaseIn, int BaseOut) -{ - char Buff[100]; - int i, len; - cmsUInt32Number out; - - for (len=0; in > 0 && len < 100; len++) { - - Buff[len] = (char) (in % BaseIn); - in /= BaseIn; - } - - for (i=len-1, out=0; i >= 0; --i) { - out = out * BaseOut + Buff[i]; - } - - return out; -} - -void CMSEXPORT cmsSetProfileVersion(cmsHPROFILE hProfile, cmsFloat64Number Version) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - - // 4.2 -> 0x4200000 - - Icc -> Version = BaseToBase((cmsUInt32Number) floor(Version * 100.0 + 0.5), 10, 16) << 16; -} - -cmsFloat64Number CMSEXPORT cmsGetProfileVersion(cmsHPROFILE hProfile) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - cmsUInt32Number n = Icc -> Version >> 16; - - return BaseToBase(n, 16, 10) / 100.0; -} -// -------------------------------------------------------------------------------------------------------------- - - -// Create profile from IOhandler -cmsHPROFILE CMSEXPORT cmsOpenProfileFromIOhandlerTHR(cmsContext ContextID, cmsIOHANDLER* io) -{ - _cmsICCPROFILE* NewIcc; - cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID); - - if (hEmpty == NULL) return NULL; - - NewIcc = (_cmsICCPROFILE*) hEmpty; - - NewIcc ->IOhandler = io; - if (!_cmsReadHeader(NewIcc)) goto Error; - return hEmpty; - -Error: - cmsCloseProfile(hEmpty); - return NULL; -} - -// Create profile from IOhandler -cmsHPROFILE CMSEXPORT cmsOpenProfileFromIOhandler2THR(cmsContext ContextID, cmsIOHANDLER* io, cmsBool write) -{ - _cmsICCPROFILE* NewIcc; - cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID); - - if (hEmpty == NULL) return NULL; - - NewIcc = (_cmsICCPROFILE*) hEmpty; - - NewIcc ->IOhandler = io; - if (write) { - - NewIcc -> IsWrite = TRUE; - return hEmpty; - } - - if (!_cmsReadHeader(NewIcc)) goto Error; - return hEmpty; - -Error: - cmsCloseProfile(hEmpty); - return NULL; -} - - -// Create profile from disk file -cmsHPROFILE CMSEXPORT cmsOpenProfileFromFileTHR(cmsContext ContextID, const char *lpFileName, const char *sAccess) -{ - _cmsICCPROFILE* NewIcc; - cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID); - - if (hEmpty == NULL) return NULL; - - NewIcc = (_cmsICCPROFILE*) hEmpty; - - NewIcc ->IOhandler = cmsOpenIOhandlerFromFile(ContextID, lpFileName, sAccess); - if (NewIcc ->IOhandler == NULL) goto Error; - - if (*sAccess == 'W' || *sAccess == 'w') { - - NewIcc -> IsWrite = TRUE; - - return hEmpty; - } - - if (!_cmsReadHeader(NewIcc)) goto Error; - return hEmpty; - -Error: - cmsCloseProfile(hEmpty); - return NULL; -} - - -cmsHPROFILE CMSEXPORT cmsOpenProfileFromFile(const char *ICCProfile, const char *sAccess) -{ - return cmsOpenProfileFromFileTHR(NULL, ICCProfile, sAccess); -} - - -cmsHPROFILE CMSEXPORT cmsOpenProfileFromStreamTHR(cmsContext ContextID, FILE* ICCProfile, const char *sAccess) -{ - _cmsICCPROFILE* NewIcc; - cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID); - - if (hEmpty == NULL) return NULL; - - NewIcc = (_cmsICCPROFILE*) hEmpty; - - NewIcc ->IOhandler = cmsOpenIOhandlerFromStream(ContextID, ICCProfile); - if (NewIcc ->IOhandler == NULL) goto Error; - - if (*sAccess == 'w') { - - NewIcc -> IsWrite = TRUE; - return hEmpty; - } - - if (!_cmsReadHeader(NewIcc)) goto Error; - return hEmpty; - -Error: - cmsCloseProfile(hEmpty); - return NULL; - -} - -cmsHPROFILE CMSEXPORT cmsOpenProfileFromStream(FILE* ICCProfile, const char *sAccess) -{ - return cmsOpenProfileFromStreamTHR(NULL, ICCProfile, sAccess); -} - - -// Open from memory block -cmsHPROFILE CMSEXPORT cmsOpenProfileFromMemTHR(cmsContext ContextID, const void* MemPtr, cmsUInt32Number dwSize) -{ - _cmsICCPROFILE* NewIcc; - cmsHPROFILE hEmpty; - - hEmpty = cmsCreateProfilePlaceholder(ContextID); - if (hEmpty == NULL) return NULL; - - NewIcc = (_cmsICCPROFILE*) hEmpty; - - // Ok, in this case const void* is casted to void* just because open IO handler - // shares read and writting modes. Don't abuse this feature! - NewIcc ->IOhandler = cmsOpenIOhandlerFromMem(ContextID, (void*) MemPtr, dwSize, "r"); - if (NewIcc ->IOhandler == NULL) goto Error; - - if (!_cmsReadHeader(NewIcc)) goto Error; - - return hEmpty; - -Error: - cmsCloseProfile(hEmpty); - return NULL; -} - -cmsHPROFILE CMSEXPORT cmsOpenProfileFromMem(const void* MemPtr, cmsUInt32Number dwSize) -{ - return cmsOpenProfileFromMemTHR(NULL, MemPtr, dwSize); -} - - - -// Dump tag contents. If the profile is being modified, untouched tags are copied from FileOrig -static -cmsBool SaveTags(_cmsICCPROFILE* Icc, _cmsICCPROFILE* FileOrig) -{ - cmsUInt8Number* Data; - cmsUInt32Number i; - cmsUInt32Number Begin; - cmsIOHANDLER* io = Icc ->IOhandler; - cmsTagDescriptor* TagDescriptor; - cmsTagTypeSignature TypeBase; - cmsTagTypeSignature Type; - cmsTagTypeHandler* TypeHandler; - cmsFloat64Number Version = cmsGetProfileVersion((cmsHPROFILE) Icc); - cmsTagTypeHandler LocalTypeHandler; - - for (i=0; i < Icc -> TagCount; i++) { - - if (Icc ->TagNames[i] == 0) continue; - - // Linked tags are not written - if (Icc ->TagLinked[i] != (cmsTagSignature) 0) continue; - - Icc -> TagOffsets[i] = Begin = io ->UsedSpace; - - Data = (cmsUInt8Number*) Icc -> TagPtrs[i]; - - if (!Data) { - - // Reach here if we are copying a tag from a disk-based ICC profile which has not been modified by user. - // In this case a blind copy of the block data is performed - if (FileOrig != NULL && Icc -> TagOffsets[i]) { - - cmsUInt32Number TagSize = FileOrig -> TagSizes[i]; - cmsUInt32Number TagOffset = FileOrig -> TagOffsets[i]; - void* Mem; - - if (!FileOrig ->IOhandler->Seek(FileOrig ->IOhandler, TagOffset)) return FALSE; - - Mem = _cmsMalloc(Icc ->ContextID, TagSize); - if (Mem == NULL) return FALSE; - - if (FileOrig ->IOhandler->Read(FileOrig->IOhandler, Mem, TagSize, 1) != 1) return FALSE; - if (!io ->Write(io, TagSize, Mem)) return FALSE; - _cmsFree(Icc ->ContextID, Mem); - - Icc -> TagSizes[i] = (io ->UsedSpace - Begin); - - - // Align to 32 bit boundary. - if (! _cmsWriteAlignment(io)) - return FALSE; - } - - continue; - } - - - // Should this tag be saved as RAW? If so, tagsizes should be specified in advance (no further cooking is done) - if (Icc ->TagSaveAsRaw[i]) { - - if (io -> Write(io, Icc ->TagSizes[i], Data) != 1) return FALSE; - } - else { - - // Search for support on this tag - TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, Icc -> TagNames[i]); - if (TagDescriptor == NULL) continue; // Unsupported, ignore it - - if (TagDescriptor ->DecideType != NULL) { - - Type = TagDescriptor ->DecideType(Version, Data); - } - else { - - Type = TagDescriptor ->SupportedTypes[0]; - } - - TypeHandler = _cmsGetTagTypeHandler(Icc->ContextID, Type); - - if (TypeHandler == NULL) { - cmsSignalError(Icc ->ContextID, cmsERROR_INTERNAL, "(Internal) no handler for tag %x", Icc -> TagNames[i]); - continue; - } - - TypeBase = TypeHandler ->Signature; - if (!_cmsWriteTypeBase(io, TypeBase)) - return FALSE; - - LocalTypeHandler = *TypeHandler; - LocalTypeHandler.ContextID = Icc ->ContextID; - LocalTypeHandler.ICCVersion = Icc ->Version; - if (!LocalTypeHandler.WritePtr(&LocalTypeHandler, io, Data, TagDescriptor ->ElemCount)) { - - char String[5]; - - _cmsTagSignature2String(String, (cmsTagSignature) TypeBase); - cmsSignalError(Icc ->ContextID, cmsERROR_WRITE, "Couldn't write type '%s'", String); - return FALSE; - } - } - - - Icc -> TagSizes[i] = (io ->UsedSpace - Begin); - - // Align to 32 bit boundary. - if (! _cmsWriteAlignment(io)) - return FALSE; - } - - - return TRUE; -} - - -// Fill the offset and size fields for all linked tags -static -cmsBool SetLinks( _cmsICCPROFILE* Icc) -{ - cmsUInt32Number i; - - for (i=0; i < Icc -> TagCount; i++) { - - cmsTagSignature lnk = Icc ->TagLinked[i]; - if (lnk != (cmsTagSignature) 0) { - - int j = _cmsSearchTag(Icc, lnk, FALSE); - if (j >= 0) { - - Icc ->TagOffsets[i] = Icc ->TagOffsets[j]; - Icc ->TagSizes[i] = Icc ->TagSizes[j]; - } - - } - } - - return TRUE; -} - -// Low-level save to IOHANDLER. It returns the number of bytes used to -// store the profile, or zero on error. io may be NULL and in this case -// no data is written--only sizes are calculated -cmsUInt32Number CMSEXPORT cmsSaveProfileToIOhandler(cmsHPROFILE hProfile, cmsIOHANDLER* io) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - _cmsICCPROFILE Keep; - cmsIOHANDLER* PrevIO = NULL; - cmsUInt32Number UsedSpace; - cmsContext ContextID; - - _cmsAssert(hProfile != NULL); - - memmove(&Keep, Icc, sizeof(_cmsICCPROFILE)); - - ContextID = cmsGetProfileContextID(hProfile); - PrevIO = Icc ->IOhandler = cmsOpenIOhandlerFromNULL(ContextID); - if (PrevIO == NULL) return 0; - - // Pass #1 does compute offsets - - if (!_cmsWriteHeader(Icc, 0)) goto Error; - if (!SaveTags(Icc, &Keep)) goto Error; - - UsedSpace = PrevIO ->UsedSpace; - - // Pass #2 does save to iohandler - - if (io != NULL) { - - Icc ->IOhandler = io; - if (!SetLinks(Icc)) goto Error; - if (!_cmsWriteHeader(Icc, UsedSpace)) goto Error; - if (!SaveTags(Icc, &Keep)) goto Error; - } - - memmove(Icc, &Keep, sizeof(_cmsICCPROFILE)); - if (!cmsCloseIOhandler(PrevIO)) return 0; - - return UsedSpace; - - -Error: - cmsCloseIOhandler(PrevIO); - memmove(Icc, &Keep, sizeof(_cmsICCPROFILE)); - return 0; -} - -#ifdef _WIN32_WCE -int wceex_unlink(const char *filename); -#ifndef remove -# define remove wceex_unlink -#endif -#endif - -// Low-level save to disk. -cmsBool CMSEXPORT cmsSaveProfileToFile(cmsHPROFILE hProfile, const char* FileName) -{ - cmsContext ContextID = cmsGetProfileContextID(hProfile); - cmsIOHANDLER* io = cmsOpenIOhandlerFromFile(ContextID, FileName, "w"); - cmsBool rc; - - if (io == NULL) return FALSE; - - rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0); - rc &= cmsCloseIOhandler(io); - - if (rc == FALSE) { // remove() is C99 per 7.19.4.1 - remove(FileName); // We have to IGNORE return value in this case - } - return rc; -} - -// Same as anterior, but for streams -cmsBool CMSEXPORT cmsSaveProfileToStream(cmsHPROFILE hProfile, FILE* Stream) -{ - cmsBool rc; - cmsContext ContextID = cmsGetProfileContextID(hProfile); - cmsIOHANDLER* io = cmsOpenIOhandlerFromStream(ContextID, Stream); - - if (io == NULL) return FALSE; - - rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0); - rc &= cmsCloseIOhandler(io); - - return rc; -} - - -// Same as anterior, but for memory blocks. In this case, a NULL as MemPtr means calculate needed space only -cmsBool CMSEXPORT cmsSaveProfileToMem(cmsHPROFILE hProfile, void *MemPtr, cmsUInt32Number* BytesNeeded) -{ - cmsBool rc; - cmsIOHANDLER* io; - cmsContext ContextID = cmsGetProfileContextID(hProfile); - - _cmsAssert(BytesNeeded != NULL); - - // Should we just calculate the needed space? - if (MemPtr == NULL) { - - *BytesNeeded = cmsSaveProfileToIOhandler(hProfile, NULL); - return (*BytesNeeded == 0) ? FALSE : TRUE; - } - - // That is a real write operation - io = cmsOpenIOhandlerFromMem(ContextID, MemPtr, *BytesNeeded, "w"); - if (io == NULL) return FALSE; - - rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0); - rc &= cmsCloseIOhandler(io); - - return rc; -} - - - -// Closes a profile freeing any involved resources -cmsBool CMSEXPORT cmsCloseProfile(cmsHPROFILE hProfile) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - cmsBool rc = TRUE; - cmsUInt32Number i; - - if (!Icc) return FALSE; - - // Was open in write mode? - if (Icc ->IsWrite) { - - Icc ->IsWrite = FALSE; // Assure no further writting - rc &= cmsSaveProfileToFile(hProfile, Icc ->IOhandler->PhysicalFile); - } - - for (i=0; i < Icc -> TagCount; i++) { - - if (Icc -> TagPtrs[i]) { - - cmsTagTypeHandler* TypeHandler = Icc ->TagTypeHandlers[i]; - - if (TypeHandler != NULL) { - cmsTagTypeHandler LocalTypeHandler = *TypeHandler; - - LocalTypeHandler.ContextID = Icc ->ContextID; // As an additional parameters - LocalTypeHandler.ICCVersion = Icc ->Version; - LocalTypeHandler.FreePtr(&LocalTypeHandler, Icc -> TagPtrs[i]); - } - else - _cmsFree(Icc ->ContextID, Icc ->TagPtrs[i]); - } - } - - if (Icc ->IOhandler != NULL) { - rc &= cmsCloseIOhandler(Icc->IOhandler); - } - - _cmsDestroyMutex(Icc->ContextID, Icc->UsrMutex); - - _cmsFree(Icc ->ContextID, Icc); // Free placeholder memory - - return rc; -} - - -// ------------------------------------------------------------------------------------------------------------------- - - -// Returns TRUE if a given tag is supported by a plug-in -static -cmsBool IsTypeSupported(cmsTagDescriptor* TagDescriptor, cmsTagTypeSignature Type) -{ - cmsUInt32Number i, nMaxTypes; - - nMaxTypes = TagDescriptor->nSupportedTypes; - if (nMaxTypes >= MAX_TYPES_IN_LCMS_PLUGIN) - nMaxTypes = MAX_TYPES_IN_LCMS_PLUGIN; - - for (i=0; i < nMaxTypes; i++) { - if (Type == TagDescriptor ->SupportedTypes[i]) return TRUE; - } - - return FALSE; -} - - -// That's the main read function -void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - cmsIOHANDLER* io = Icc ->IOhandler; - cmsTagTypeHandler* TypeHandler; - cmsTagTypeHandler LocalTypeHandler; - cmsTagDescriptor* TagDescriptor; - cmsTagTypeSignature BaseType; - cmsUInt32Number Offset, TagSize; - cmsUInt32Number ElemCount; - int n; - - if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return NULL; - - n = _cmsSearchTag(Icc, sig, TRUE); - 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->TagTypeHandlers[n] == NULL) goto Error; - - // Sanity check - BaseType = Icc->TagTypeHandlers[n]->Signature; - if (BaseType == 0) goto Error; - - TagDescriptor = _cmsGetTagDescriptor(Icc->ContextID, sig); - if (TagDescriptor == NULL) goto Error; - - if (!IsTypeSupported(TagDescriptor, BaseType)) goto Error; - - if (Icc ->TagSaveAsRaw[n]) goto Error; // We don't support read raw tags as cooked - - _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); - return Icc -> TagPtrs[n]; - } - - // We need to read it. Get the offset and size to the file - Offset = Icc -> TagOffsets[n]; - TagSize = Icc -> TagSizes[n]; - - // Seek to its location - if (!io -> Seek(io, Offset)) - goto Error; - - // Search for support on this tag - 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) goto Error; - - if (!IsTypeSupported(TagDescriptor, BaseType)) goto Error; - - TagSize -= 8; // Alredy read by the type base logic - - // Get type handler - TypeHandler = _cmsGetTagTypeHandler(Icc ->ContextID, BaseType); - if (TypeHandler == NULL) goto Error; - LocalTypeHandler = *TypeHandler; - - - // Read the tag - Icc -> TagTypeHandlers[n] = TypeHandler; - - LocalTypeHandler.ContextID = Icc ->ContextID; - LocalTypeHandler.ICCVersion = Icc ->Version; - Icc -> TagPtrs[n] = LocalTypeHandler.ReadPtr(&LocalTypeHandler, io, &ElemCount, TagSize); - - // The tag type is supported, but something wrong happend and we cannot read the tag. - // let know the user about this (although it is just a warning) - if (Icc -> TagPtrs[n] == NULL) { - - char String[5]; - - _cmsTagSignature2String(String, sig); - cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "Corrupted tag '%s'", String); - goto Error; - } - - // This is a weird error that may be a symptom of something more serious, the number of - // stored item is actually less than the number of required elements. - if (ElemCount < TagDescriptor ->ElemCount) { - - char String[5]; - - _cmsTagSignature2String(String, sig); - cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "'%s' Inconsistent number of items: expected %d, got %d", - String, TagDescriptor ->ElemCount, ElemCount); - } - - - // 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; -} - - -// Get true type of data -cmsTagTypeSignature _cmsGetTagTrueType(cmsHPROFILE hProfile, cmsTagSignature sig) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - cmsTagTypeHandler* TypeHandler; - int n; - - // Search for given tag in ICC profile directory - n = _cmsSearchTag(Icc, sig, TRUE); - if (n < 0) return (cmsTagTypeSignature) 0; // Not found, return NULL - - // Get the handler. The true type is there - TypeHandler = Icc -> TagTypeHandlers[n]; - return TypeHandler ->Signature; -} - - -// Write a single tag. This just keeps track of the tak into a list of "to be written". If the tag is already -// in that list, the previous version is deleted. -cmsBool CMSEXPORT cmsWriteTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - cmsTagTypeHandler* TypeHandler = NULL; - cmsTagTypeHandler LocalTypeHandler; - cmsTagDescriptor* TagDescriptor = NULL; - cmsTagTypeSignature Type; - int i; - 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) { - - // Use zero as a mark of deleted - _cmsDeleteTagByPos(Icc, i); - Icc ->TagNames[i] = (cmsTagSignature) 0; - _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); - return TRUE; - } - // Didn't find the tag - goto Error; - } - - if (!_cmsNewTag(Icc, sig, &i)) goto Error; - - // This is not raw - Icc ->TagSaveAsRaw[i] = FALSE; - - // This is not a link - Icc ->TagLinked[i] = (cmsTagSignature) 0; - - // Get information about the TAG. - TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig); - if (TagDescriptor == NULL){ - cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported tag '%x'", sig); - goto Error; - } - - - // Now we need to know which type to use. It depends on the version. - Version = cmsGetProfileVersion(hProfile); - - if (TagDescriptor ->DecideType != NULL) { - - // Let the tag descriptor to decide the type base on depending on - // the data. This is useful for example on parametric curves, where - // curves specified by a table cannot be saved as parametric and needs - // to be casted to single v2-curves, even on v4 profiles. - - Type = TagDescriptor ->DecideType(Version, data); - } - else { - - Type = TagDescriptor ->SupportedTypes[0]; - } - - // Does the tag support this type? - if (!IsTypeSupported(TagDescriptor, Type)) { - - _cmsTagSignature2String(TypeString, (cmsTagSignature) Type); - _cmsTagSignature2String(SigString, sig); - - cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported type '%s' for tag '%s'", TypeString, SigString); - goto Error; - } - - // Does we have a handler for this 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); - goto Error; // Should never happen - } - - - // Fill fields on icc structure - Icc ->TagTypeHandlers[i] = TypeHandler; - Icc ->TagNames[i] = sig; - Icc ->TagSizes[i] = 0; - Icc ->TagOffsets[i] = 0; - - LocalTypeHandler = *TypeHandler; - LocalTypeHandler.ContextID = Icc ->ContextID; - LocalTypeHandler.ICCVersion = Icc ->Version; - Icc ->TagPtrs[i] = LocalTypeHandler.DupPtr(&LocalTypeHandler, data, TagDescriptor ->ElemCount); - - if (Icc ->TagPtrs[i] == NULL) { - - _cmsTagSignature2String(TypeString, (cmsTagSignature) Type); - _cmsTagSignature2String(SigString, sig); - cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "Malformed struct in type '%s' for tag '%s'", TypeString, SigString); - - 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 -// is to do an additional step of serialization. That means, readRaw would issue a normal read and then convert the obtained -// data to raw bytes by using the "write" serialization logic. And vice-versa. I know this may end in situations where -// raw data written does not exactly correspond with the raw data proposed to cmsWriteRaw data, but this approach allows -// to write a tag as raw data and the read it as handled. - -cmsInt32Number CMSEXPORT cmsReadRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, void* data, cmsUInt32Number BufferSize) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - void *Object; - int i; - cmsIOHANDLER* MemIO; - cmsTagTypeHandler* TypeHandler = NULL; - cmsTagTypeHandler LocalTypeHandler; - cmsTagDescriptor* TagDescriptor = NULL; - 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) goto Error; // Not found, - - // It is already read? - if (Icc -> TagPtrs[i] == NULL) { - - // No yet, get original position - Offset = Icc ->TagOffsets[i]; - TagSize = Icc ->TagSizes[i]; - - // read the data directly, don't keep copy - if (data != NULL) { - - if (BufferSize < TagSize) - TagSize = BufferSize; - - if (!Icc ->IOhandler ->Seek(Icc ->IOhandler, Offset)) goto Error; - if (!Icc ->IOhandler ->Read(Icc ->IOhandler, data, 1, TagSize)) goto Error; - - _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); - return TagSize; - } - - _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); - return Icc ->TagSizes[i]; - } - - // The data has been already read, or written. But wait!, maybe the user choosed to save as - // raw data. In this case, return the raw data directly - if (Icc ->TagSaveAsRaw[i]) { - - if (data != NULL) { - - TagSize = Icc ->TagSizes[i]; - if (BufferSize < TagSize) - TagSize = BufferSize; - - 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. - - _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); - Object = cmsReadTag(hProfile, sig); - if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0; - - if (Object == NULL) goto Error; - - // Now we need to serialize to a memory block: just use a memory iohandler - - if (data == NULL) { - MemIO = cmsOpenIOhandlerFromNULL(cmsGetProfileContextID(hProfile)); - } else{ - MemIO = cmsOpenIOhandlerFromMem(cmsGetProfileContextID(hProfile), data, BufferSize, "w"); - } - if (MemIO == NULL) goto Error; - - // Obtain type handling for the tag - TypeHandler = Icc ->TagTypeHandlers[i]; - TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig); - if (TagDescriptor == NULL) { - cmsCloseIOhandler(MemIO); - goto Error; - } - - if (TypeHandler == NULL) goto Error; - - // Serialize - LocalTypeHandler = *TypeHandler; - LocalTypeHandler.ContextID = Icc ->ContextID; - LocalTypeHandler.ICCVersion = Icc ->Version; - - if (!_cmsWriteTypeBase(MemIO, TypeHandler ->Signature)) { - cmsCloseIOhandler(MemIO); - goto Error; - } - - if (!LocalTypeHandler.WritePtr(&LocalTypeHandler, MemIO, Object, TagDescriptor ->ElemCount)) { - cmsCloseIOhandler(MemIO); - 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 -// checking anything. As a rule, mixing Raw with cooked doesn't work, so writting a tag as raw and then reading -// it as cooked without serializing does result into an error. If that is wha you want, you will need to dump -// the profile to memry or disk and then reopen it. -cmsBool CMSEXPORT cmsWriteRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data, cmsUInt32Number Size) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - int i; - - 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; - Icc ->TagNames[i] = sig; - Icc ->TagLinked[i] = (cmsTagSignature) 0; - - // Keep a copy of the block - Icc ->TagPtrs[i] = _cmsDupMem(Icc ->ContextID, data, Size); - Icc ->TagSizes[i] = Size; - - _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); - return TRUE; -} - -// Using this function you can collapse several tag entries to the same block in the profile -cmsBool CMSEXPORT cmsLinkTag(cmsHPROFILE hProfile, cmsTagSignature sig, cmsTagSignature dest) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - int i; - - 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; - Icc ->TagNames[i] = sig; - Icc ->TagLinked[i] = dest; - - Icc ->TagPtrs[i] = NULL; - Icc ->TagSizes[i] = 0; - Icc ->TagOffsets[i] = 0; - - _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); - return TRUE; -} - - -// Returns the tag linked to sig, in the case two tags are sharing same resource -cmsTagSignature CMSEXPORT cmsTagLinkedTo(cmsHPROFILE hProfile, cmsTagSignature sig) -{ - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - int i; - - // Search for given tag in ICC profile directory - i = _cmsSearchTag(Icc, sig, FALSE); - if (i < 0) return (cmsTagSignature) 0; // Not found, return 0 - - return Icc -> TagLinked[i]; -} diff --git a/third_party/lcms2-2.6/src/cmsio1.c b/third_party/lcms2-2.6/src/cmsio1.c deleted file mode 100644 index 778aa2b4fc..0000000000 --- a/third_party/lcms2-2.6/src/cmsio1.c +++ /dev/null @@ -1,1020 +0,0 @@ -//--------------------------------------------------------------------------------- -// -// 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" - -// Read tags using low-level functions, provides necessary glue code to adapt versions, etc. - -// LUT tags -static const cmsTagSignature Device2PCS16[] = {cmsSigAToB0Tag, // Perceptual - cmsSigAToB1Tag, // Relative colorimetric - cmsSigAToB2Tag, // Saturation - cmsSigAToB1Tag }; // Absolute colorimetric - -static const cmsTagSignature Device2PCSFloat[] = {cmsSigDToB0Tag, // Perceptual - cmsSigDToB1Tag, // Relative colorimetric - cmsSigDToB2Tag, // Saturation - cmsSigDToB3Tag }; // Absolute colorimetric - -static const cmsTagSignature PCS2Device16[] = {cmsSigBToA0Tag, // Perceptual - cmsSigBToA1Tag, // Relative colorimetric - cmsSigBToA2Tag, // Saturation - cmsSigBToA1Tag }; // Absolute colorimetric - -static const cmsTagSignature PCS2DeviceFloat[] = {cmsSigBToD0Tag, // Perceptual - cmsSigBToD1Tag, // Relative colorimetric - cmsSigBToD2Tag, // Saturation - cmsSigBToD3Tag }; // Absolute colorimetric - - -// Factors to convert from 1.15 fixed point to 0..1.0 range and vice-versa -#define InpAdj (1.0/MAX_ENCODEABLE_XYZ) // (65536.0/(65535.0*2.0)) -#define OutpAdj (MAX_ENCODEABLE_XYZ) // ((2.0*65535.0)/65536.0) - -// Several resources for gray conversions. -static const cmsFloat64Number GrayInputMatrix[] = { (InpAdj*cmsD50X), (InpAdj*cmsD50Y), (InpAdj*cmsD50Z) }; -static const cmsFloat64Number OneToThreeInputMatrix[] = { 1, 1, 1 }; -static const cmsFloat64Number PickYMatrix[] = { 0, (OutpAdj*cmsD50Y), 0 }; -static const cmsFloat64Number PickLstarMatrix[] = { 1, 0, 0 }; - -// Get a media white point fixing some issues found in certain old profiles -cmsBool _cmsReadMediaWhitePoint(cmsCIEXYZ* Dest, cmsHPROFILE hProfile) -{ - cmsCIEXYZ* Tag; - - _cmsAssert(Dest != NULL); - - Tag = (cmsCIEXYZ*) cmsReadTag(hProfile, cmsSigMediaWhitePointTag); - - // If no wp, take D50 - if (Tag == NULL) { - *Dest = *cmsD50_XYZ(); - return TRUE; - } - - // V2 display profiles should give D50 - if (cmsGetEncodedICCversion(hProfile) < 0x4000000) { - - if (cmsGetDeviceClass(hProfile) == cmsSigDisplayClass) { - *Dest = *cmsD50_XYZ(); - return TRUE; - } - } - - // All seems ok - *Dest = *Tag; - return TRUE; -} - - -// Chromatic adaptation matrix. Fix some issues as well -cmsBool _cmsReadCHAD(cmsMAT3* Dest, cmsHPROFILE hProfile) -{ - cmsMAT3* Tag; - - _cmsAssert(Dest != NULL); - - Tag = (cmsMAT3*) cmsReadTag(hProfile, cmsSigChromaticAdaptationTag); - - if (Tag != NULL) { - *Dest = *Tag; - return TRUE; - } - - // No CHAD available, default it to identity - _cmsMAT3identity(Dest); - - // V2 display profiles should give D50 - if (cmsGetEncodedICCversion(hProfile) < 0x4000000) { - - if (cmsGetDeviceClass(hProfile) == cmsSigDisplayClass) { - - cmsCIEXYZ* White = (cmsCIEXYZ*) cmsReadTag(hProfile, cmsSigMediaWhitePointTag); - - if (White == NULL) { - - _cmsMAT3identity(Dest); - return TRUE; - } - - return _cmsAdaptationMatrix(Dest, NULL, White, cmsD50_XYZ()); - } - } - - return TRUE; -} - - -// Auxiliar, read colorants as a MAT3 structure. Used by any function that needs a matrix-shaper -static -cmsBool ReadICCMatrixRGB2XYZ(cmsMAT3* r, cmsHPROFILE hProfile) -{ - cmsCIEXYZ *PtrRed, *PtrGreen, *PtrBlue; - - _cmsAssert(r != NULL); - - PtrRed = (cmsCIEXYZ *) cmsReadTag(hProfile, cmsSigRedColorantTag); - PtrGreen = (cmsCIEXYZ *) cmsReadTag(hProfile, cmsSigGreenColorantTag); - PtrBlue = (cmsCIEXYZ *) cmsReadTag(hProfile, cmsSigBlueColorantTag); - - if (PtrRed == NULL || PtrGreen == NULL || PtrBlue == NULL) - return FALSE; - - _cmsVEC3init(&r -> v[0], PtrRed -> X, PtrGreen -> X, PtrBlue -> X); - _cmsVEC3init(&r -> v[1], PtrRed -> Y, PtrGreen -> Y, PtrBlue -> Y); - _cmsVEC3init(&r -> v[2], PtrRed -> Z, PtrGreen -> Z, PtrBlue -> Z); - - return TRUE; -} - - -// Gray input pipeline -static -cmsPipeline* BuildGrayInputMatrixPipeline(cmsHPROFILE hProfile) -{ - cmsToneCurve *GrayTRC; - cmsPipeline* Lut; - cmsContext ContextID = cmsGetProfileContextID(hProfile); - - GrayTRC = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGrayTRCTag); - if (GrayTRC == NULL) return NULL; - - Lut = cmsPipelineAlloc(ContextID, 1, 3); - if (Lut == NULL) - goto Error; - - if (cmsGetPCS(hProfile) == cmsSigLabData) { - - // In this case we implement the profile as an identity matrix plus 3 tone curves - cmsUInt16Number Zero[2] = { 0x8080, 0x8080 }; - cmsToneCurve* EmptyTab; - cmsToneCurve* LabCurves[3]; - - EmptyTab = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero); - - if (EmptyTab == NULL) - goto Error; - - LabCurves[0] = GrayTRC; - LabCurves[1] = EmptyTab; - LabCurves[2] = EmptyTab; - - if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 1, OneToThreeInputMatrix, NULL)) || - !cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, LabCurves))) { - cmsFreeToneCurve(EmptyTab); - goto Error; - } - - cmsFreeToneCurve(EmptyTab); - - } - else { - - if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 1, &GrayTRC)) || - !cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 1, GrayInputMatrix, NULL))) - goto Error; - } - - return Lut; - -Error: - // memory pointed by GrayTRC is not a new malloc memory, so don't free it here, - // memory pointed by GrayTRC will be freed when hProfile is closed. - // test file :0047776_Pocket Medicine_ The Massachusetts General Hospital Handbook of Internal Medicine-2.pdf - // Xiaochuan Liu, 20140421 - //cmsFreeToneCurve(GrayTRC); - cmsPipelineFree(Lut); - return NULL; -} - -// RGB Matrix shaper -static -cmsPipeline* BuildRGBInputMatrixShaper(cmsHPROFILE hProfile) -{ - cmsPipeline* Lut; - cmsMAT3 Mat; - cmsToneCurve *Shapes[3]; - cmsContext ContextID = cmsGetProfileContextID(hProfile); - int i, j; - - if (!ReadICCMatrixRGB2XYZ(&Mat, hProfile)) return NULL; - - // XYZ PCS in encoded in 1.15 format, and the matrix output comes in 0..0xffff range, so - // we need to adjust the output by a factor of (0x10000/0xffff) to put data in - // a 1.16 range, and then a >> 1 to obtain 1.15. The total factor is (65536.0)/(65535.0*2) - - for (i=0; i < 3; i++) - for (j=0; j < 3; j++) - Mat.v[i].n[j] *= InpAdj; - - - Shapes[0] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigRedTRCTag); - Shapes[1] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGreenTRCTag); - Shapes[2] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigBlueTRCTag); - - if (!Shapes[0] || !Shapes[1] || !Shapes[2]) - return NULL; - - Lut = cmsPipelineAlloc(ContextID, 3, 3); - if (Lut != NULL) { - - if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, Shapes)) || - !cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 3, (cmsFloat64Number*) &Mat, NULL))) - goto Error; - - // Note that it is certainly possible a single profile would have a LUT based - // tag for output working in lab and a matrix-shaper for the fallback cases. - // This is not allowed by the spec, but this code is tolerant to those cases - if (cmsGetPCS(hProfile) == cmsSigLabData) { - - if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocXYZ2Lab(ContextID))) - goto Error; - } - - } - - return Lut; - -Error: - cmsPipelineFree(Lut); - return NULL; -} - - - -// Read the DToAX tag, adjusting the encoding of Lab or XYZ if neded -static -cmsPipeline* _cmsReadFloatInputTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat) -{ - cmsContext ContextID = cmsGetProfileContextID(hProfile); - cmsPipeline* Lut = cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat)); - cmsColorSpaceSignature spc = cmsGetColorSpace(hProfile); - cmsColorSpaceSignature PCS = cmsGetPCS(hProfile); - - if (Lut == NULL) return NULL; - - // input and output of transform are in lcms 0..1 encoding. If XYZ or Lab spaces are used, - // these need to be normalized into the appropriate ranges (Lab = 100,0,0, XYZ=1.0,1.0,1.0) - if ( spc == cmsSigLabData) - { - if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToLabFloat(ContextID))) - goto Error; - } - else if (spc == cmsSigXYZData) - { - if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToXyzFloat(ContextID))) - goto Error; - } - - if ( PCS == cmsSigLabData) - { - if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID))) - goto Error; - } - else if( PCS == cmsSigXYZData) - { - if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID))) - goto Error; - } - - return Lut; - -Error: - cmsPipelineFree(Lut); - return NULL; -} - - -// Read and create a BRAND NEW MPE LUT from a given profile. All stuff dependent of version, etc -// is adjusted here in order to create a LUT that takes care of all those details. -// We add intent = -1 as a way to read matrix shaper always, no matter of other LUT -cmsPipeline* _cmsReadInputLUT(cmsHPROFILE hProfile, int Intent) -{ - cmsTagTypeSignature OriginalType; - cmsTagSignature tag16 = Device2PCS16[Intent]; - cmsTagSignature tagFloat = Device2PCSFloat[Intent]; - cmsContext ContextID = cmsGetProfileContextID(hProfile); - - // On named color, take the appropiate tag - if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) { - - cmsPipeline* Lut; - cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) cmsReadTag(hProfile, cmsSigNamedColor2Tag); - - if (nc == NULL) return NULL; - - Lut = cmsPipelineAlloc(ContextID, 0, 0); - if (Lut == NULL) { - cmsFreeNamedColorList(nc); - return NULL; - } - - if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocNamedColor(nc, TRUE)) || - !cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) { - cmsPipelineFree(Lut); - return NULL; - } - return Lut; - } - - // This is an attempt to reuse this funtion to retrieve the matrix-shaper as pipeline no - // matter other LUT are present and have precedence. Intent = -1 means just this. - if (Intent != -1) { - - if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence - - // Floating point LUT are always V4, but the encoding range is no - // longer 0..1.0, so we need to add an stage depending on the color space - return _cmsReadFloatInputTag(hProfile, tagFloat); - } - - // Revert to perceptual if no tag is found - if (!cmsIsTag(hProfile, tag16)) { - tag16 = Device2PCS16[0]; - } - - if (cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table? - - // Check profile version and LUT type. Do the necessary adjustments if needed - - // First read the tag - cmsPipeline* Lut = (cmsPipeline*) cmsReadTag(hProfile, tag16); - if (Lut == NULL) return NULL; - - // After reading it, we have now info about the original type - OriginalType = _cmsGetTagTrueType(hProfile, tag16); - - // The profile owns the Lut, so we need to copy it - Lut = cmsPipelineDup(Lut); - - // We need to adjust data only for Lab16 on output - if (OriginalType != cmsSigLut16Type || cmsGetPCS(hProfile) != cmsSigLabData) - return Lut; - - // If the input is Lab, add also a conversion at the begin - if (cmsGetColorSpace(hProfile) == cmsSigLabData && - !cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID))) - goto Error; - - // Add a matrix for conversion V2 to V4 Lab PCS - if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) - goto Error; - - return Lut; -Error: - cmsPipelineFree(Lut); - return NULL; - } - } - - // Lut was not found, try to create a matrix-shaper - - // Check if this is a grayscale profile. - if (cmsGetColorSpace(hProfile) == cmsSigGrayData) { - - // if so, build appropiate conversion tables. - // The tables are the PCS iluminant, scaled across GrayTRC - return BuildGrayInputMatrixPipeline(hProfile); - } - - // Not gray, create a normal matrix-shaper - return BuildRGBInputMatrixShaper(hProfile); -} - -// --------------------------------------------------------------------------------------------------------------- - -// Gray output pipeline. -// XYZ -> Gray or Lab -> Gray. Since we only know the GrayTRC, we need to do some assumptions. Gray component will be -// given by Y on XYZ PCS and by L* on Lab PCS, Both across inverse TRC curve. -// The complete pipeline on XYZ is Matrix[3:1] -> Tone curve and in Lab Matrix[3:1] -> Tone Curve as well. - -static -cmsPipeline* BuildGrayOutputPipeline(cmsHPROFILE hProfile) -{ - cmsToneCurve *GrayTRC, *RevGrayTRC; - cmsPipeline* Lut; - cmsContext ContextID = cmsGetProfileContextID(hProfile); - - GrayTRC = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGrayTRCTag); - if (GrayTRC == NULL) return NULL; - - RevGrayTRC = cmsReverseToneCurve(GrayTRC); - if (RevGrayTRC == NULL) return NULL; - - Lut = cmsPipelineAlloc(ContextID, 3, 1); - if (Lut == NULL) { - cmsFreeToneCurve(RevGrayTRC); - return NULL; - } - - if (cmsGetPCS(hProfile) == cmsSigLabData) { - - if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickLstarMatrix, NULL))) - goto Error; - } - else { - if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 1, 3, PickYMatrix, NULL))) - goto Error; - } - - if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 1, &RevGrayTRC))) - goto Error; - - cmsFreeToneCurve(RevGrayTRC); - return Lut; - -Error: - cmsFreeToneCurve(RevGrayTRC); - cmsPipelineFree(Lut); - return NULL; -} - - -static -cmsPipeline* BuildRGBOutputMatrixShaper(cmsHPROFILE hProfile) -{ - cmsPipeline* Lut; - cmsToneCurve *Shapes[3], *InvShapes[3]; - cmsMAT3 Mat, Inv; - int i, j; - cmsContext ContextID = cmsGetProfileContextID(hProfile); - - if (!ReadICCMatrixRGB2XYZ(&Mat, hProfile)) - return NULL; - - if (!_cmsMAT3inverse(&Mat, &Inv)) - return NULL; - - // XYZ PCS in encoded in 1.15 format, and the matrix input should come in 0..0xffff range, so - // we need to adjust the input by a << 1 to obtain a 1.16 fixed and then by a factor of - // (0xffff/0x10000) to put data in 0..0xffff range. Total factor is (2.0*65535.0)/65536.0; - - for (i=0; i < 3; i++) - for (j=0; j < 3; j++) - Inv.v[i].n[j] *= OutpAdj; - - Shapes[0] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigRedTRCTag); - Shapes[1] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigGreenTRCTag); - Shapes[2] = (cmsToneCurve *) cmsReadTag(hProfile, cmsSigBlueTRCTag); - - if (!Shapes[0] || !Shapes[1] || !Shapes[2]) - return NULL; - - InvShapes[0] = cmsReverseToneCurve(Shapes[0]); - InvShapes[1] = cmsReverseToneCurve(Shapes[1]); - InvShapes[2] = cmsReverseToneCurve(Shapes[2]); - - if (!InvShapes[0] || !InvShapes[1] || !InvShapes[2]) { - return NULL; - } - - Lut = cmsPipelineAlloc(ContextID, 3, 3); - if (Lut != NULL) { - - // Note that it is certainly possible a single profile would have a LUT based - // tag for output working in lab and a matrix-shaper for the fallback cases. - // This is not allowed by the spec, but this code is tolerant to those cases - if (cmsGetPCS(hProfile) == cmsSigLabData) { - - if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLab2XYZ(ContextID))) - goto Error; - } - - if (!cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocMatrix(ContextID, 3, 3, (cmsFloat64Number*) &Inv, NULL)) || - !cmsPipelineInsertStage(Lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, 3, InvShapes))) - goto Error; - } - - cmsFreeToneCurveTriple(InvShapes); - return Lut; -Error: - cmsFreeToneCurveTriple(InvShapes); - cmsPipelineFree(Lut); - return NULL; -} - - -// Change CLUT interpolation to trilinear -static -void ChangeInterpolationToTrilinear(cmsPipeline* Lut) -{ - cmsStage* Stage; - - for (Stage = cmsPipelineGetPtrToFirstStage(Lut); - Stage != NULL; - Stage = cmsStageNext(Stage)) { - - if (cmsStageType(Stage) == cmsSigCLutElemType) { - - _cmsStageCLutData* CLUT = (_cmsStageCLutData*) Stage ->Data; - - CLUT ->Params->dwFlags |= CMS_LERP_FLAGS_TRILINEAR; - _cmsSetInterpolationRoutine(Lut->ContextID, CLUT ->Params); - } - } -} - - -// Read the DToAX tag, adjusting the encoding of Lab or XYZ if neded -static -cmsPipeline* _cmsReadFloatOutputTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat) -{ - cmsContext ContextID = cmsGetProfileContextID(hProfile); - cmsPipeline* Lut = cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat)); - cmsColorSpaceSignature PCS = cmsGetPCS(hProfile); - cmsColorSpaceSignature dataSpace = cmsGetColorSpace(hProfile); - - if (Lut == NULL) return NULL; - - // If PCS is Lab or XYZ, the floating point tag is accepting data in the space encoding, - // and since the formatter has already accomodated to 0..1.0, we should undo this change - if ( PCS == cmsSigLabData) - { - if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToLabFloat(ContextID))) - goto Error; - } - else - if (PCS == cmsSigXYZData) - { - if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToXyzFloat(ContextID))) - goto Error; - } - - // the output can be Lab or XYZ, in which case normalisation is needed on the end of the pipeline - if ( dataSpace == cmsSigLabData) - { - if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID))) - goto Error; - } - else if (dataSpace == cmsSigXYZData) - { - if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID))) - goto Error; - } - - return Lut; - -Error: - cmsPipelineFree(Lut); - return NULL; -} - -// Create an output MPE LUT from agiven profile. Version mismatches are handled here -cmsPipeline* _cmsReadOutputLUT(cmsHPROFILE hProfile, int Intent) -{ - cmsTagTypeSignature OriginalType; - cmsTagSignature tag16 = PCS2Device16[Intent]; - cmsTagSignature tagFloat = PCS2DeviceFloat[Intent]; - cmsContext ContextID = cmsGetProfileContextID(hProfile); - - - if (Intent != -1) { - - if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence - - // Floating point LUT are always V4 - return _cmsReadFloatOutputTag(hProfile, tagFloat); - } - - // Revert to perceptual if no tag is found - if (!cmsIsTag(hProfile, tag16)) { - tag16 = PCS2Device16[0]; - } - - if (cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table? - - // Check profile version and LUT type. Do the necessary adjustments if needed - - // First read the tag - cmsPipeline* Lut = (cmsPipeline*) cmsReadTag(hProfile, tag16); - if (Lut == NULL) return NULL; - - // After reading it, we have info about the original type - OriginalType = _cmsGetTagTrueType(hProfile, tag16); - - // The profile owns the Lut, so we need to copy it - Lut = cmsPipelineDup(Lut); - if (Lut == NULL) return NULL; - - // Now it is time for a controversial stuff. I found that for 3D LUTS using - // Lab used as indexer space, trilinear interpolation should be used - if (cmsGetPCS(hProfile) == cmsSigLabData) - ChangeInterpolationToTrilinear(Lut); - - // We need to adjust data only for Lab and Lut16 type - if (OriginalType != cmsSigLut16Type || cmsGetPCS(hProfile) != cmsSigLabData) - return Lut; - - // Add a matrix for conversion V4 to V2 Lab PCS - if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID))) - goto Error; - - // If the output is Lab, add also a conversion at the end - if (cmsGetColorSpace(hProfile) == cmsSigLabData) - if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) - goto Error; - - return Lut; -Error: - cmsPipelineFree(Lut); - return NULL; - } - } - - // Lut not found, try to create a matrix-shaper - - // Check if this is a grayscale profile. - if (cmsGetColorSpace(hProfile) == cmsSigGrayData) { - - // if so, build appropiate conversion tables. - // The tables are the PCS iluminant, scaled across GrayTRC - return BuildGrayOutputPipeline(hProfile); - } - - // Not gray, create a normal matrix-shaper, which only operates in XYZ space - return BuildRGBOutputMatrixShaper(hProfile); -} - -// --------------------------------------------------------------------------------------------------------------- - -// Read the AToD0 tag, adjusting the encoding of Lab or XYZ if neded -static -cmsPipeline* _cmsReadFloatDevicelinkTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat) -{ - cmsContext ContextID = cmsGetProfileContextID(hProfile); - cmsPipeline* Lut = cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat)); - cmsColorSpaceSignature PCS = cmsGetPCS(hProfile); - cmsColorSpaceSignature spc = cmsGetColorSpace(hProfile); - - if (Lut == NULL) return NULL; - - if (spc == cmsSigLabData) - { - if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToLabFloat(ContextID))) - goto Error; - } - else - if (spc == cmsSigXYZData) - { - if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageNormalizeToXyzFloat(ContextID))) - goto Error; - } - - if (PCS == cmsSigLabData) - { - if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromLabFloat(ContextID))) - goto Error; - } - else - if (PCS == cmsSigXYZData) - { - if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageNormalizeFromXyzFloat(ContextID))) - goto Error; - } - - return Lut; -Error: - cmsPipelineFree(Lut); - return NULL; -} - -// This one includes abstract profiles as well. Matrix-shaper cannot be obtained on that device class. The -// tag name here may default to AToB0 -cmsPipeline* _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, int Intent) -{ - cmsPipeline* Lut; - cmsTagTypeSignature OriginalType; - cmsTagSignature tag16 = Device2PCS16[Intent]; - cmsTagSignature tagFloat = Device2PCSFloat[Intent]; - cmsContext ContextID = cmsGetProfileContextID(hProfile); - - - // On named color, take the appropiate tag - if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) { - - cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) cmsReadTag(hProfile, cmsSigNamedColor2Tag); - - if (nc == NULL) return NULL; - - Lut = cmsPipelineAlloc(ContextID, 0, 0); - if (Lut == NULL) - goto Error; - - if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocNamedColor(nc, FALSE))) - goto Error; - - if (cmsGetColorSpace(hProfile) == cmsSigLabData) - if (!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) - goto Error; - - return Lut; -Error: - cmsPipelineFree(Lut); - cmsFreeNamedColorList(nc); - return NULL; - } - - if (cmsIsTag(hProfile, tagFloat)) { // Float tag takes precedence - - // Floating point LUT are always V - return _cmsReadFloatDevicelinkTag(hProfile, tagFloat); - } - - tagFloat = Device2PCSFloat[0]; - if (cmsIsTag(hProfile, tagFloat)) { - - return cmsPipelineDup((cmsPipeline*) cmsReadTag(hProfile, tagFloat)); - } - - if (!cmsIsTag(hProfile, tag16)) { // Is there any LUT-Based table? - - tag16 = Device2PCS16[0]; - if (!cmsIsTag(hProfile, tag16)) return NULL; - } - - // Check profile version and LUT type. Do the necessary adjustments if needed - - // Read the tag - Lut = (cmsPipeline*) cmsReadTag(hProfile, tag16); - if (Lut == NULL) return NULL; - - // The profile owns the Lut, so we need to copy it - Lut = cmsPipelineDup(Lut); - if (Lut == NULL) return NULL; - - // Now it is time for a controversial stuff. I found that for 3D LUTS using - // Lab used as indexer space, trilinear interpolation should be used - if (cmsGetPCS(hProfile) == cmsSigLabData) - ChangeInterpolationToTrilinear(Lut); - - // After reading it, we have info about the original type - OriginalType = _cmsGetTagTrueType(hProfile, tag16); - - // We need to adjust data for Lab16 on output - if (OriginalType != cmsSigLut16Type) return Lut; - - // Here it is possible to get Lab on both sides - - if (cmsGetColorSpace(hProfile) == cmsSigLabData) { - if(!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocLabV4ToV2(ContextID))) - goto Error2; - } - - if (cmsGetPCS(hProfile) == cmsSigLabData) { - if(!cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) - goto Error2; - } - - return Lut; - -Error2: - cmsPipelineFree(Lut); - return NULL; -} - -// --------------------------------------------------------------------------------------------------------------- - -// Returns TRUE if the profile is implemented as matrix-shaper -cmsBool CMSEXPORT cmsIsMatrixShaper(cmsHPROFILE hProfile) -{ - switch (cmsGetColorSpace(hProfile)) { - - case cmsSigGrayData: - - return cmsIsTag(hProfile, cmsSigGrayTRCTag); - - case cmsSigRgbData: - - return (cmsIsTag(hProfile, cmsSigRedColorantTag) && - cmsIsTag(hProfile, cmsSigGreenColorantTag) && - cmsIsTag(hProfile, cmsSigBlueColorantTag) && - cmsIsTag(hProfile, cmsSigRedTRCTag) && - cmsIsTag(hProfile, cmsSigGreenTRCTag) && - cmsIsTag(hProfile, cmsSigBlueTRCTag)); - - default: - - return FALSE; - } -} - -// Returns TRUE if the intent is implemented as CLUT -cmsBool CMSEXPORT cmsIsCLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number UsedDirection) -{ - const cmsTagSignature* TagTable; - - // For devicelinks, the supported intent is that one stated in the header - if (cmsGetDeviceClass(hProfile) == cmsSigLinkClass) { - return (cmsGetHeaderRenderingIntent(hProfile) == Intent); - } - - switch (UsedDirection) { - - case LCMS_USED_AS_INPUT: TagTable = Device2PCS16; break; - case LCMS_USED_AS_OUTPUT:TagTable = PCS2Device16; break; - - // For proofing, we need rel. colorimetric in output. Let's do some recursion - case LCMS_USED_AS_PROOF: - return cmsIsIntentSupported(hProfile, Intent, LCMS_USED_AS_INPUT) && - cmsIsIntentSupported(hProfile, INTENT_RELATIVE_COLORIMETRIC, LCMS_USED_AS_OUTPUT); - - default: - cmsSignalError(cmsGetProfileContextID(hProfile), cmsERROR_RANGE, "Unexpected direction (%d)", UsedDirection); - return FALSE; - } - - return cmsIsTag(hProfile, TagTable[Intent]); - -} - - -// Return info about supported intents -cmsBool CMSEXPORT cmsIsIntentSupported(cmsHPROFILE hProfile, - cmsUInt32Number Intent, cmsUInt32Number UsedDirection) -{ - - if (cmsIsCLUT(hProfile, Intent, UsedDirection)) return TRUE; - - // Is there any matrix-shaper? If so, the intent is supported. This is a bit odd, since V2 matrix shaper - // does not fully support relative colorimetric because they cannot deal with non-zero black points, but - // many profiles claims that, and this is certainly not true for V4 profiles. Lets answer "yes" no matter - // the accuracy would be less than optimal in rel.col and v2 case. - - return cmsIsMatrixShaper(hProfile); -} - - -// --------------------------------------------------------------------------------------------------------------- - -// Read both, profile sequence description and profile sequence id if present. Then combine both to -// create qa unique structure holding both. Shame on ICC to store things in such complicated way. -cmsSEQ* _cmsReadProfileSequence(cmsHPROFILE hProfile) -{ - cmsSEQ* ProfileSeq; - cmsSEQ* ProfileId; - cmsSEQ* NewSeq; - cmsUInt32Number i; - - // Take profile sequence description first - ProfileSeq = (cmsSEQ*) cmsReadTag(hProfile, cmsSigProfileSequenceDescTag); - - // Take profile sequence ID - ProfileId = (cmsSEQ*) cmsReadTag(hProfile, cmsSigProfileSequenceIdTag); - - if (ProfileSeq == NULL && ProfileId == NULL) return NULL; - - if (ProfileSeq == NULL) return cmsDupProfileSequenceDescription(ProfileId); - if (ProfileId == NULL) return cmsDupProfileSequenceDescription(ProfileSeq); - - // We have to mix both together. For that they must agree - if (ProfileSeq ->n != ProfileId ->n) return cmsDupProfileSequenceDescription(ProfileSeq); - - NewSeq = cmsDupProfileSequenceDescription(ProfileSeq); - - // Ok, proceed to the mixing - if (NewSeq != NULL) { - for (i=0; i < ProfileSeq ->n; i++) { - - memmove(&NewSeq ->seq[i].ProfileID, &ProfileId ->seq[i].ProfileID, sizeof(cmsProfileID)); - NewSeq ->seq[i].Description = cmsMLUdup(ProfileId ->seq[i].Description); - } - } - return NewSeq; -} - -// Dump the contents of profile sequence in both tags (if v4 available) -cmsBool _cmsWriteProfileSequence(cmsHPROFILE hProfile, const cmsSEQ* seq) -{ - if (!cmsWriteTag(hProfile, cmsSigProfileSequenceDescTag, seq)) return FALSE; - - if (cmsGetProfileVersion(hProfile) >= 4.0) { - - if (!cmsWriteTag(hProfile, cmsSigProfileSequenceIdTag, seq)) return FALSE; - } - - return TRUE; -} - - -// Auxiliar, read and duplicate a MLU if found. -static -cmsMLU* GetMLUFromProfile(cmsHPROFILE h, cmsTagSignature sig) -{ - cmsMLU* mlu = (cmsMLU*) cmsReadTag(h, sig); - if (mlu == NULL) return NULL; - - return cmsMLUdup(mlu); -} - -// Create a sequence description out of an array of profiles -cmsSEQ* _cmsCompileProfileSequence(cmsContext ContextID, cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[]) -{ - cmsUInt32Number i; - cmsSEQ* seq = cmsAllocProfileSequenceDescription(ContextID, nProfiles); - - if (seq == NULL) return NULL; - - for (i=0; i < nProfiles; i++) { - - cmsPSEQDESC* ps = &seq ->seq[i]; - cmsHPROFILE h = hProfiles[i]; - cmsTechnologySignature* techpt; - - cmsGetHeaderAttributes(h, &ps ->attributes); - cmsGetHeaderProfileID(h, ps ->ProfileID.ID8); - ps ->deviceMfg = cmsGetHeaderManufacturer(h); - ps ->deviceModel = cmsGetHeaderModel(h); - - techpt = (cmsTechnologySignature*) cmsReadTag(h, cmsSigTechnologyTag); - if (techpt == NULL) - ps ->technology = (cmsTechnologySignature) 0; - else - ps ->technology = *techpt; - - ps ->Manufacturer = GetMLUFromProfile(h, cmsSigDeviceMfgDescTag); - ps ->Model = GetMLUFromProfile(h, cmsSigDeviceModelDescTag); - ps ->Description = GetMLUFromProfile(h, cmsSigProfileDescriptionTag); - - } - - return seq; -} - -// ------------------------------------------------------------------------------------------------------------------- - - -static -const cmsMLU* GetInfo(cmsHPROFILE hProfile, cmsInfoType Info) -{ - cmsTagSignature sig; - - switch (Info) { - - case cmsInfoDescription: - sig = cmsSigProfileDescriptionTag; - break; - - case cmsInfoManufacturer: - sig = cmsSigDeviceMfgDescTag; - break; - - case cmsInfoModel: - sig = cmsSigDeviceModelDescTag; - break; - - case cmsInfoCopyright: - sig = cmsSigCopyrightTag; - break; - - default: return NULL; - } - - - return (cmsMLU*) cmsReadTag(hProfile, sig); -} - - - -cmsUInt32Number CMSEXPORT cmsGetProfileInfo(cmsHPROFILE hProfile, cmsInfoType Info, - const char LanguageCode[3], const char CountryCode[3], - wchar_t* Buffer, cmsUInt32Number BufferSize) -{ - const cmsMLU* mlu = GetInfo(hProfile, Info); - if (mlu == NULL) return 0; - - return cmsMLUgetWide(mlu, LanguageCode, CountryCode, Buffer, BufferSize); -} - - -cmsUInt32Number CMSEXPORT cmsGetProfileInfoASCII(cmsHPROFILE hProfile, cmsInfoType Info, - const char LanguageCode[3], const char CountryCode[3], - char* Buffer, cmsUInt32Number BufferSize) -{ - const cmsMLU* mlu = GetInfo(hProfile, Info); - if (mlu == NULL) return 0; - - return cmsMLUgetASCII(mlu, LanguageCode, CountryCode, Buffer, BufferSize); -} diff --git a/third_party/lcms2-2.6/src/cmslut.c b/third_party/lcms2-2.6/src/cmslut.c deleted file mode 100644 index 19d43361f0..0000000000 --- a/third_party/lcms2-2.6/src/cmslut.c +++ /dev/null @@ -1,1820 +0,0 @@ -//--------------------------------------------------------------------------------- -// -// 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" - - -// Allocates an empty multi profile element -cmsStage* CMSEXPORT _cmsStageAllocPlaceholder(cmsContext ContextID, - cmsStageSignature Type, - cmsUInt32Number InputChannels, - cmsUInt32Number OutputChannels, - _cmsStageEvalFn EvalPtr, - _cmsStageDupElemFn DupElemPtr, - _cmsStageFreeElemFn FreePtr, - void* Data) -{ - cmsStage* ph = (cmsStage*) _cmsMallocZero(ContextID, sizeof(cmsStage)); - - if (ph == NULL) return NULL; - - - ph ->ContextID = ContextID; - - ph ->Type = Type; - ph ->Implements = Type; // By default, no clue on what is implementing - - ph ->InputChannels = InputChannels; - ph ->OutputChannels = OutputChannels; - ph ->EvalPtr = EvalPtr; - ph ->DupElemPtr = DupElemPtr; - ph ->FreePtr = FreePtr; - ph ->Data = Data; - - return ph; -} - - -static -void EvaluateIdentity(const cmsFloat32Number In[], - cmsFloat32Number Out[], - const cmsStage *mpe) -{ - memmove(Out, In, mpe ->InputChannels * sizeof(cmsFloat32Number)); -} - - -cmsStage* CMSEXPORT cmsStageAllocIdentity(cmsContext ContextID, cmsUInt32Number nChannels) -{ - return _cmsStageAllocPlaceholder(ContextID, - cmsSigIdentityElemType, - nChannels, nChannels, - EvaluateIdentity, - NULL, - NULL, - NULL); - } - -// Conversion functions. From floating point to 16 bits -static -void FromFloatTo16(const cmsFloat32Number In[], cmsUInt16Number Out[], cmsUInt32Number n) -{ - cmsUInt32Number i; - - for (i=0; i < n; i++) { - Out[i] = _cmsQuickSaturateWord(In[i] * 65535.0); - } -} - -// From 16 bits to floating point -static -void From16ToFloat(const cmsUInt16Number In[], cmsFloat32Number Out[], cmsUInt32Number n) -{ - cmsUInt32Number i; - - for (i=0; i < n; i++) { - Out[i] = (cmsFloat32Number) In[i] / 65535.0F; - } -} - - -// This function is quite useful to analyze the structure of a LUT and retrieve the MPE elements -// that conform the LUT. It should be called with the LUT, the number of expected elements and -// then a list of expected types followed with a list of cmsFloat64Number pointers to MPE elements. If -// the function founds a match with current pipeline, it fills the pointers and returns TRUE -// if not, returns FALSE without touching anything. Setting pointers to NULL does bypass -// the storage process. -cmsBool CMSEXPORT cmsPipelineCheckAndRetreiveStages(const cmsPipeline* Lut, cmsUInt32Number n, ...) -{ - va_list args; - cmsUInt32Number i; - cmsStage* mpe; - cmsStageSignature Type; - void** ElemPtr; - - // Make sure same number of elements - if (cmsPipelineStageCount(Lut) != n) return FALSE; - - va_start(args, n); - - // Iterate across asked types - mpe = Lut ->Elements; - for (i=0; i < n; i++) { - - // Get asked type - Type = (cmsStageSignature)va_arg(args, cmsStageSignature); - if (mpe ->Type != Type) { - - va_end(args); // Mismatch. We are done. - return FALSE; - } - mpe = mpe ->Next; - } - - // Found a combination, fill pointers if not NULL - mpe = Lut ->Elements; - for (i=0; i < n; i++) { - - ElemPtr = va_arg(args, void**); - if (ElemPtr != NULL) - *ElemPtr = mpe; - - mpe = mpe ->Next; - } - - va_end(args); - return TRUE; -} - -// Below there are implementations for several types of elements. Each type may be implemented by a -// evaluation function, a duplication function, a function to free resources and a constructor. - -// ************************************************************************************************* -// Type cmsSigCurveSetElemType (curves) -// ************************************************************************************************* - -cmsToneCurve** _cmsStageGetPtrToCurveSet(const cmsStage* mpe) -{ - _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) mpe ->Data; - - return Data ->TheCurves; -} - -static -void EvaluateCurves(const cmsFloat32Number In[], - cmsFloat32Number Out[], - const cmsStage *mpe) -{ - _cmsStageToneCurvesData* Data; - cmsUInt32Number i; - - _cmsAssert(mpe != NULL); - - Data = (_cmsStageToneCurvesData*) mpe ->Data; - if (Data == NULL) return; - - if (Data ->TheCurves == NULL) return; - - for (i=0; i < Data ->nCurves; i++) { - Out[i] = cmsEvalToneCurveFloat(Data ->TheCurves[i], In[i]); - } -} - -static -void CurveSetElemTypeFree(cmsStage* mpe) -{ - _cmsStageToneCurvesData* Data; - cmsUInt32Number i; - - _cmsAssert(mpe != NULL); - - Data = (_cmsStageToneCurvesData*) mpe ->Data; - if (Data == NULL) return; - - if (Data ->TheCurves != NULL) { - for (i=0; i < Data ->nCurves; i++) { - if (Data ->TheCurves[i] != NULL) - cmsFreeToneCurve(Data ->TheCurves[i]); - } - } - _cmsFree(mpe ->ContextID, Data ->TheCurves); - _cmsFree(mpe ->ContextID, Data); -} - - -static -void* CurveSetDup(cmsStage* mpe) -{ - _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) mpe ->Data; - _cmsStageToneCurvesData* NewElem; - cmsUInt32Number i; - - NewElem = (_cmsStageToneCurvesData*) _cmsMallocZero(mpe ->ContextID, sizeof(_cmsStageToneCurvesData)); - if (NewElem == NULL) return NULL; - - NewElem ->nCurves = Data ->nCurves; - NewElem ->TheCurves = (cmsToneCurve**) _cmsCalloc(mpe ->ContextID, NewElem ->nCurves, sizeof(cmsToneCurve*)); - - if (NewElem ->TheCurves == NULL) goto Error; - - for (i=0; i < NewElem ->nCurves; i++) { - - // Duplicate each curve. It may fail. - NewElem ->TheCurves[i] = cmsDupToneCurve(Data ->TheCurves[i]); - if (NewElem ->TheCurves[i] == NULL) goto Error; - - - } - return (void*) NewElem; - -Error: - - if (NewElem ->TheCurves != NULL) { - for (i=0; i < NewElem ->nCurves; i++) { - if (NewElem ->TheCurves[i]) - cmsFreeToneCurve(NewElem ->TheCurves[i]); - } - } - _cmsFree(mpe ->ContextID, NewElem ->TheCurves); - _cmsFree(mpe ->ContextID, NewElem); - return NULL; -} - - -// Curves == NULL forces identity curves -cmsStage* CMSEXPORT cmsStageAllocToneCurves(cmsContext ContextID, cmsUInt32Number nChannels, cmsToneCurve* const Curves[]) -{ - cmsUInt32Number i; - _cmsStageToneCurvesData* NewElem; - cmsStage* NewMPE; - - - NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigCurveSetElemType, nChannels, nChannels, - EvaluateCurves, CurveSetDup, CurveSetElemTypeFree, NULL ); - if (NewMPE == NULL) return NULL; - - NewElem = (_cmsStageToneCurvesData*) _cmsMallocZero(ContextID, sizeof(_cmsStageToneCurvesData)); - if (NewElem == NULL) { - cmsStageFree(NewMPE); - return NULL; - } - - NewMPE ->Data = (void*) NewElem; - - NewElem ->nCurves = nChannels; - NewElem ->TheCurves = (cmsToneCurve**) _cmsCalloc(ContextID, nChannels, sizeof(cmsToneCurve*)); - if (NewElem ->TheCurves == NULL) { - cmsStageFree(NewMPE); - return NULL; - } - - for (i=0; i < nChannels; i++) { - - if (Curves == NULL) { - NewElem ->TheCurves[i] = cmsBuildGamma(ContextID, 1.0); - } - else { - NewElem ->TheCurves[i] = cmsDupToneCurve(Curves[i]); - } - - if (NewElem ->TheCurves[i] == NULL) { - cmsStageFree(NewMPE); - return NULL; - } - - } - - return NewMPE; -} - - -// Create a bunch of identity curves -cmsStage* _cmsStageAllocIdentityCurves(cmsContext ContextID, int nChannels) -{ - cmsStage* mpe = cmsStageAllocToneCurves(ContextID, nChannels, NULL); - - if (mpe == NULL) return NULL; - mpe ->Implements = cmsSigIdentityElemType; - return mpe; -} - - -// ************************************************************************************************* -// Type cmsSigMatrixElemType (Matrices) -// ************************************************************************************************* - - -// Special care should be taken here because precision loss. A temporary cmsFloat64Number buffer is being used -static -void EvaluateMatrix(const cmsFloat32Number In[], - cmsFloat32Number Out[], - const cmsStage *mpe) -{ - cmsUInt32Number i, j; - _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data; - cmsFloat64Number Tmp; - - // Input is already in 0..1.0 notation - for (i=0; i < mpe ->OutputChannels; i++) { - - Tmp = 0; - for (j=0; j < mpe->InputChannels; j++) { - Tmp += In[j] * Data->Double[i*mpe->InputChannels + j]; - } - - if (Data ->Offset != NULL) - Tmp += Data->Offset[i]; - - Out[i] = (cmsFloat32Number) Tmp; - } - - - // Output in 0..1.0 domain -} - - -// Duplicate a yet-existing matrix element -static -void* MatrixElemDup(cmsStage* mpe) -{ - _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data; - _cmsStageMatrixData* NewElem; - cmsUInt32Number sz; - - NewElem = (_cmsStageMatrixData*) _cmsMallocZero(mpe ->ContextID, sizeof(_cmsStageMatrixData)); - if (NewElem == NULL) return NULL; - - sz = mpe ->InputChannels * mpe ->OutputChannels; - - NewElem ->Double = (cmsFloat64Number*) _cmsDupMem(mpe ->ContextID, Data ->Double, sz * sizeof(cmsFloat64Number)) ; - - if (Data ->Offset) - NewElem ->Offset = (cmsFloat64Number*) _cmsDupMem(mpe ->ContextID, - Data ->Offset, mpe -> OutputChannels * sizeof(cmsFloat64Number)) ; - - return (void*) NewElem; -} - - -static -void MatrixElemTypeFree(cmsStage* mpe) -{ - _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data; - if (Data == NULL) - return; - if (Data ->Double) - _cmsFree(mpe ->ContextID, Data ->Double); - - if (Data ->Offset) - _cmsFree(mpe ->ContextID, Data ->Offset); - - _cmsFree(mpe ->ContextID, mpe ->Data); -} - - - -cmsStage* CMSEXPORT cmsStageAllocMatrix(cmsContext ContextID, cmsUInt32Number Rows, cmsUInt32Number Cols, - const cmsFloat64Number* Matrix, const cmsFloat64Number* Offset) -{ - cmsUInt32Number i, n; - _cmsStageMatrixData* NewElem; - cmsStage* NewMPE; - - n = Rows * Cols; - - // Check for overflow - if (n == 0) return NULL; - if (n >= UINT_MAX / Cols) return NULL; - if (n >= UINT_MAX / Rows) return NULL; - if (n < Rows || n < Cols) return NULL; - - NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigMatrixElemType, Cols, Rows, - EvaluateMatrix, MatrixElemDup, MatrixElemTypeFree, NULL ); - if (NewMPE == NULL) return NULL; - - - NewElem = (_cmsStageMatrixData*) _cmsMallocZero(ContextID, sizeof(_cmsStageMatrixData)); - if (NewElem == NULL) return NULL; - - - NewElem ->Double = (cmsFloat64Number*) _cmsCalloc(ContextID, n, sizeof(cmsFloat64Number)); - - if (NewElem->Double == NULL) { - MatrixElemTypeFree(NewMPE); - return NULL; - } - - for (i=0; i < n; i++) { - NewElem ->Double[i] = Matrix[i]; - } - - - if (Offset != NULL) { - - NewElem ->Offset = (cmsFloat64Number*) _cmsCalloc(ContextID, Rows, sizeof(cmsFloat64Number)); - if (NewElem->Offset == NULL) { - MatrixElemTypeFree(NewMPE); - return NULL; - } - - for (i=0; i < Rows; i++) { - NewElem ->Offset[i] = Offset[i]; - } - - } - - NewMPE ->Data = (void*) NewElem; - return NewMPE; -} - - -// ************************************************************************************************* -// Type cmsSigCLutElemType -// ************************************************************************************************* - - -// Evaluate in true floating point -static -void EvaluateCLUTfloat(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) -{ - _cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data; - - Data -> Params ->Interpolation.LerpFloat(In, Out, Data->Params); -} - - -// Convert to 16 bits, evaluate, and back to floating point -static -void EvaluateCLUTfloatIn16(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) -{ - _cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data; - cmsUInt16Number In16[MAX_STAGE_CHANNELS], Out16[MAX_STAGE_CHANNELS]; - - _cmsAssert(mpe ->InputChannels <= MAX_STAGE_CHANNELS); - _cmsAssert(mpe ->OutputChannels <= MAX_STAGE_CHANNELS); - - FromFloatTo16(In, In16, mpe ->InputChannels); - Data -> Params ->Interpolation.Lerp16(In16, Out16, Data->Params); - From16ToFloat(Out16, Out, mpe ->OutputChannels); -} - - -// Given an hypercube of b dimensions, with Dims[] number of nodes by dimension, calculate the total amount of nodes -static -cmsUInt32Number CubeSize(const cmsUInt32Number Dims[], cmsUInt32Number b) -{ - cmsUInt32Number rv, dim; - - _cmsAssert(Dims != NULL); - - for (rv = 1; b > 0; b--) { - - dim = Dims[b-1]; - if (dim == 0) return 0; // Error - - rv *= dim; - - // Check for overflow - if (rv > UINT_MAX / dim) return 0; - } - - return rv; -} - -static -void* CLUTElemDup(cmsStage* mpe) -{ - _cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data; - _cmsStageCLutData* NewElem; - - - NewElem = (_cmsStageCLutData*) _cmsMallocZero(mpe ->ContextID, sizeof(_cmsStageCLutData)); - if (NewElem == NULL) return NULL; - - NewElem ->nEntries = Data ->nEntries; - NewElem ->HasFloatValues = Data ->HasFloatValues; - - if (Data ->Tab.T) { - - if (Data ->HasFloatValues) { - NewElem ->Tab.TFloat = (cmsFloat32Number*) _cmsDupMem(mpe ->ContextID, Data ->Tab.TFloat, Data ->nEntries * sizeof (cmsFloat32Number)); - if (NewElem ->Tab.TFloat == NULL) - goto Error; - } else { - NewElem ->Tab.T = (cmsUInt16Number*) _cmsDupMem(mpe ->ContextID, Data ->Tab.T, Data ->nEntries * sizeof (cmsUInt16Number)); - if (NewElem ->Tab.TFloat == NULL) - goto Error; - } - } - - NewElem ->Params = _cmsComputeInterpParamsEx(mpe ->ContextID, - Data ->Params ->nSamples, - Data ->Params ->nInputs, - Data ->Params ->nOutputs, - NewElem ->Tab.T, - Data ->Params ->dwFlags); - if (NewElem->Params != NULL) - return (void*) NewElem; - Error: - if (NewElem->Tab.T) - // This works for both types - _cmsFree(mpe ->ContextID, NewElem -> Tab.T); - _cmsFree(mpe ->ContextID, NewElem); - return NULL; -} - - -static -void CLutElemTypeFree(cmsStage* mpe) -{ - - _cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data; - - // Already empty - if (Data == NULL) return; - - // This works for both types - if (Data -> Tab.T) - _cmsFree(mpe ->ContextID, Data -> Tab.T); - - _cmsFreeInterpParams(Data ->Params); - _cmsFree(mpe ->ContextID, mpe ->Data); -} - - -// Allocates a 16-bit multidimensional CLUT. This is evaluated at 16-bit precision. Table may have different -// granularity on each dimension. -cmsStage* CMSEXPORT cmsStageAllocCLut16bitGranular(cmsContext ContextID, - const cmsUInt32Number clutPoints[], - cmsUInt32Number inputChan, - cmsUInt32Number outputChan, - const cmsUInt16Number* Table) -{ - cmsUInt32Number i, n; - _cmsStageCLutData* NewElem; - cmsStage* NewMPE; - - _cmsAssert(clutPoints != NULL); - - if (inputChan > MAX_INPUT_DIMENSIONS) { - cmsSignalError(ContextID, cmsERROR_RANGE, "Too many input channels (%d channels, max=%d)", inputChan, MAX_INPUT_DIMENSIONS); - return NULL; - } - - NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigCLutElemType, inputChan, outputChan, - EvaluateCLUTfloatIn16, CLUTElemDup, CLutElemTypeFree, NULL ); - - if (NewMPE == NULL) return NULL; - - NewElem = (_cmsStageCLutData*) _cmsMallocZero(ContextID, sizeof(_cmsStageCLutData)); - if (NewElem == NULL) { - cmsStageFree(NewMPE); - return NULL; - } - - NewMPE ->Data = (void*) NewElem; - - NewElem -> nEntries = n = outputChan * CubeSize(clutPoints, inputChan); - NewElem -> HasFloatValues = FALSE; - - if (n == 0) { - cmsStageFree(NewMPE); - return NULL; - } - - - NewElem ->Tab.T = (cmsUInt16Number*) _cmsCalloc(ContextID, n, sizeof(cmsUInt16Number)); - if (NewElem ->Tab.T == NULL) { - cmsStageFree(NewMPE); - return NULL; - } - - if (Table != NULL) { - for (i=0; i < n; i++) { - NewElem ->Tab.T[i] = Table[i]; - } - } - - NewElem ->Params = _cmsComputeInterpParamsEx(ContextID, clutPoints, inputChan, outputChan, NewElem ->Tab.T, CMS_LERP_FLAGS_16BITS); - if (NewElem ->Params == NULL) { - cmsStageFree(NewMPE); - return NULL; - } - - return NewMPE; -} - -cmsStage* CMSEXPORT cmsStageAllocCLut16bit(cmsContext ContextID, - cmsUInt32Number nGridPoints, - cmsUInt32Number inputChan, - cmsUInt32Number outputChan, - const cmsUInt16Number* Table) -{ - cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS]; - int i; - - // Our resulting LUT would be same gridpoints on all dimensions - for (i=0; i < MAX_INPUT_DIMENSIONS; i++) - Dimensions[i] = nGridPoints; - - return cmsStageAllocCLut16bitGranular(ContextID, Dimensions, inputChan, outputChan, Table); -} - - -cmsStage* CMSEXPORT cmsStageAllocCLutFloat(cmsContext ContextID, - cmsUInt32Number nGridPoints, - cmsUInt32Number inputChan, - cmsUInt32Number outputChan, - const cmsFloat32Number* Table) -{ - cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS]; - int i; - - // Our resulting LUT would be same gridpoints on all dimensions - for (i=0; i < MAX_INPUT_DIMENSIONS; i++) - Dimensions[i] = nGridPoints; - - return cmsStageAllocCLutFloatGranular(ContextID, Dimensions, inputChan, outputChan, Table); -} - - - -cmsStage* CMSEXPORT cmsStageAllocCLutFloatGranular(cmsContext ContextID, const cmsUInt32Number clutPoints[], cmsUInt32Number inputChan, cmsUInt32Number outputChan, const cmsFloat32Number* Table) -{ - cmsUInt32Number i, n; - _cmsStageCLutData* NewElem; - cmsStage* NewMPE; - - _cmsAssert(clutPoints != NULL); - - if (inputChan > MAX_INPUT_DIMENSIONS) { - cmsSignalError(ContextID, cmsERROR_RANGE, "Too many input channels (%d channels, max=%d)", inputChan, MAX_INPUT_DIMENSIONS); - return NULL; - } - - NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigCLutElemType, inputChan, outputChan, - EvaluateCLUTfloat, CLUTElemDup, CLutElemTypeFree, NULL); - if (NewMPE == NULL) return NULL; - - - NewElem = (_cmsStageCLutData*) _cmsMallocZero(ContextID, sizeof(_cmsStageCLutData)); - if (NewElem == NULL) { - cmsStageFree(NewMPE); - return NULL; - } - - NewMPE ->Data = (void*) NewElem; - - // There is a potential integer overflow on conputing n and nEntries. - NewElem -> nEntries = n = outputChan * CubeSize(clutPoints, inputChan); - NewElem -> HasFloatValues = TRUE; - - if (n == 0) { - cmsStageFree(NewMPE); - return NULL; - } - - NewElem ->Tab.TFloat = (cmsFloat32Number*) _cmsCalloc(ContextID, n, sizeof(cmsFloat32Number)); - if (NewElem ->Tab.TFloat == NULL) { - cmsStageFree(NewMPE); - return NULL; - } - - if (Table != NULL) { - for (i=0; i < n; i++) { - NewElem ->Tab.TFloat[i] = Table[i]; - } - } - - NewElem ->Params = _cmsComputeInterpParamsEx(ContextID, clutPoints, inputChan, outputChan, NewElem ->Tab.TFloat, CMS_LERP_FLAGS_FLOAT); - if (NewElem ->Params == NULL) { - cmsStageFree(NewMPE); - return NULL; - } - - return NewMPE; -} - - -static -int IdentitySampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void * Cargo) -{ - int nChan = *(int*) Cargo; - int i; - - for (i=0; i < nChan; i++) - Out[i] = In[i]; - - return 1; -} - -// Creates an MPE that just copies input to output -cmsStage* _cmsStageAllocIdentityCLut(cmsContext ContextID, int nChan) -{ - cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS]; - cmsStage* mpe ; - int i; - - for (i=0; i < MAX_INPUT_DIMENSIONS; i++) - Dimensions[i] = 2; - - mpe = cmsStageAllocCLut16bitGranular(ContextID, Dimensions, nChan, nChan, NULL); - if (mpe == NULL) return NULL; - - if (!cmsStageSampleCLut16bit(mpe, IdentitySampler, &nChan, 0)) { - cmsStageFree(mpe); - return NULL; - } - - mpe ->Implements = cmsSigIdentityElemType; - return mpe; -} - - - -// Quantize a value 0 <= i < MaxSamples to 0..0xffff -cmsUInt16Number _cmsQuantizeVal(cmsFloat64Number i, int MaxSamples) -{ - cmsFloat64Number x; - - x = ((cmsFloat64Number) i * 65535.) / (cmsFloat64Number) (MaxSamples - 1); - return _cmsQuickSaturateWord(x); -} - - -// This routine does a sweep on whole input space, and calls its callback -// function on knots. returns TRUE if all ok, FALSE otherwise. -cmsBool CMSEXPORT cmsStageSampleCLut16bit(cmsStage* mpe, cmsSAMPLER16 Sampler, void * Cargo, cmsUInt32Number dwFlags) -{ - int i, t, nTotalPoints, index, rest; - int nInputs, nOutputs; - cmsUInt32Number* nSamples; - cmsUInt16Number In[MAX_INPUT_DIMENSIONS+1], Out[MAX_STAGE_CHANNELS]; - _cmsStageCLutData* clut; - - if (mpe == NULL) return FALSE; - - clut = (_cmsStageCLutData*) mpe->Data; - - if (clut == NULL) return FALSE; - - nSamples = clut->Params ->nSamples; - nInputs = clut->Params ->nInputs; - nOutputs = clut->Params ->nOutputs; - - if (nInputs <= 0) return FALSE; - if (nOutputs <= 0) return FALSE; - if (nInputs > MAX_INPUT_DIMENSIONS) return FALSE; - if (nOutputs >= MAX_STAGE_CHANNELS) return FALSE; - - nTotalPoints = CubeSize(nSamples, nInputs); - if (nTotalPoints == 0) return FALSE; - - index = 0; - for (i = 0; i < nTotalPoints; i++) { - - rest = i; - for (t = nInputs-1; t >=0; --t) { - - cmsUInt32Number Colorant = rest % nSamples[t]; - - rest /= nSamples[t]; - - In[t] = _cmsQuantizeVal(Colorant, nSamples[t]); - } - - if (clut ->Tab.T != NULL) { - for (t=0; t < nOutputs; t++) - Out[t] = clut->Tab.T[index + t]; - } - - if (!Sampler(In, Out, Cargo)) - return FALSE; - - if (!(dwFlags & SAMPLER_INSPECT)) { - - if (clut ->Tab.T != NULL) { - for (t=0; t < nOutputs; t++) - clut->Tab.T[index + t] = Out[t]; - } - } - - index += nOutputs; - } - - return TRUE; -} - -// Same as anterior, but for floting point -cmsBool CMSEXPORT cmsStageSampleCLutFloat(cmsStage* mpe, cmsSAMPLERFLOAT Sampler, void * Cargo, cmsUInt32Number dwFlags) -{ - int i, t, nTotalPoints, index, rest; - int nInputs, nOutputs; - cmsUInt32Number* nSamples; - cmsFloat32Number In[MAX_INPUT_DIMENSIONS+1], Out[MAX_STAGE_CHANNELS]; - _cmsStageCLutData* clut = (_cmsStageCLutData*) mpe->Data; - - nSamples = clut->Params ->nSamples; - nInputs = clut->Params ->nInputs; - nOutputs = clut->Params ->nOutputs; - - if (nInputs <= 0) return FALSE; - if (nOutputs <= 0) return FALSE; - if (nInputs > MAX_INPUT_DIMENSIONS) return FALSE; - if (nOutputs >= MAX_STAGE_CHANNELS) return FALSE; - - nTotalPoints = CubeSize(nSamples, nInputs); - if (nTotalPoints == 0) return FALSE; - - index = 0; - for (i = 0; i < nTotalPoints; i++) { - - rest = i; - for (t = nInputs-1; t >=0; --t) { - - cmsUInt32Number Colorant = rest % nSamples[t]; - - rest /= nSamples[t]; - - In[t] = (cmsFloat32Number) (_cmsQuantizeVal(Colorant, nSamples[t]) / 65535.0); - } - - if (clut ->Tab.TFloat != NULL) { - for (t=0; t < nOutputs; t++) - Out[t] = clut->Tab.TFloat[index + t]; - } - - if (!Sampler(In, Out, Cargo)) - return FALSE; - - if (!(dwFlags & SAMPLER_INSPECT)) { - - if (clut ->Tab.TFloat != NULL) { - for (t=0; t < nOutputs; t++) - clut->Tab.TFloat[index + t] = Out[t]; - } - } - - index += nOutputs; - } - - return TRUE; -} - - - -// This routine does a sweep on whole input space, and calls its callback -// function on knots. returns TRUE if all ok, FALSE otherwise. -cmsBool CMSEXPORT cmsSliceSpace16(cmsUInt32Number nInputs, const cmsUInt32Number clutPoints[], - cmsSAMPLER16 Sampler, void * Cargo) -{ - int i, t, nTotalPoints, rest; - cmsUInt16Number In[cmsMAXCHANNELS]; - - if (nInputs >= cmsMAXCHANNELS) return FALSE; - - nTotalPoints = CubeSize(clutPoints, nInputs); - if (nTotalPoints == 0) return FALSE; - - for (i = 0; i < nTotalPoints; i++) { - - rest = i; - for (t = nInputs-1; t >=0; --t) { - - cmsUInt32Number Colorant = rest % clutPoints[t]; - - rest /= clutPoints[t]; - In[t] = _cmsQuantizeVal(Colorant, clutPoints[t]); - - } - - if (!Sampler(In, NULL, Cargo)) - return FALSE; - } - - return TRUE; -} - -cmsInt32Number CMSEXPORT cmsSliceSpaceFloat(cmsUInt32Number nInputs, const cmsUInt32Number clutPoints[], - cmsSAMPLERFLOAT Sampler, void * Cargo) -{ - int i, t, nTotalPoints, rest; - cmsFloat32Number In[cmsMAXCHANNELS]; - - if (nInputs >= cmsMAXCHANNELS) return FALSE; - - nTotalPoints = CubeSize(clutPoints, nInputs); - if (nTotalPoints == 0) return FALSE; - - for (i = 0; i < nTotalPoints; i++) { - - rest = i; - for (t = nInputs-1; t >=0; --t) { - - cmsUInt32Number Colorant = rest % clutPoints[t]; - - rest /= clutPoints[t]; - In[t] = (cmsFloat32Number) (_cmsQuantizeVal(Colorant, clutPoints[t]) / 65535.0); - - } - - if (!Sampler(In, NULL, Cargo)) - return FALSE; - } - - return TRUE; -} - -// ******************************************************************************** -// Type cmsSigLab2XYZElemType -// ******************************************************************************** - - -static -void EvaluateLab2XYZ(const cmsFloat32Number In[], - cmsFloat32Number Out[], - const cmsStage *mpe) -{ - cmsCIELab Lab; - cmsCIEXYZ XYZ; - const cmsFloat64Number XYZadj = MAX_ENCODEABLE_XYZ; - - // V4 rules - Lab.L = In[0] * 100.0; - Lab.a = In[1] * 255.0 - 128.0; - Lab.b = In[2] * 255.0 - 128.0; - - cmsLab2XYZ(NULL, &XYZ, &Lab); - - // From XYZ, range 0..19997 to 0..1.0, note that 1.99997 comes from 0xffff - // encoded as 1.15 fixed point, so 1 + (32767.0 / 32768.0) - - Out[0] = (cmsFloat32Number) ((cmsFloat64Number) XYZ.X / XYZadj); - Out[1] = (cmsFloat32Number) ((cmsFloat64Number) XYZ.Y / XYZadj); - Out[2] = (cmsFloat32Number) ((cmsFloat64Number) XYZ.Z / XYZadj); - return; - - cmsUNUSED_PARAMETER(mpe); -} - - -// No dup or free routines needed, as the structure has no pointers in it. -cmsStage* _cmsStageAllocLab2XYZ(cmsContext ContextID) -{ - return _cmsStageAllocPlaceholder(ContextID, cmsSigLab2XYZElemType, 3, 3, EvaluateLab2XYZ, NULL, NULL, NULL); -} - -// ******************************************************************************** - -// v2 L=100 is supposed to be placed on 0xFF00. There is no reasonable -// number of gridpoints that would make exact match. However, a prelinearization -// of 258 entries, would map 0xFF00 exactly on entry 257, and this is good to avoid scum dot. -// Almost all what we need but unfortunately, the rest of entries should be scaled by -// (255*257/256) and this is not exact. - -cmsStage* _cmsStageAllocLabV2ToV4curves(cmsContext ContextID) -{ - cmsStage* mpe; - cmsToneCurve* LabTable[3]; - int i, j; - - LabTable[0] = cmsBuildTabulatedToneCurve16(ContextID, 258, NULL); - LabTable[1] = cmsBuildTabulatedToneCurve16(ContextID, 258, NULL); - LabTable[2] = cmsBuildTabulatedToneCurve16(ContextID, 258, NULL); - - for (j=0; j < 3; j++) { - - if (LabTable[j] == NULL) { - cmsFreeToneCurveTriple(LabTable); - return NULL; - } - - // We need to map * (0xffff / 0xff00), thats same as (257 / 256) - // So we can use 258-entry tables to do the trick (i / 257) * (255 * 257) * (257 / 256); - for (i=0; i < 257; i++) { - - LabTable[j]->Table16[i] = (cmsUInt16Number) ((i * 0xffff + 0x80) >> 8); - } - - LabTable[j] ->Table16[257] = 0xffff; - } - - mpe = cmsStageAllocToneCurves(ContextID, 3, LabTable); - cmsFreeToneCurveTriple(LabTable); - - if (mpe == NULL) return NULL; - mpe ->Implements = cmsSigLabV2toV4; - return mpe; -} - -// ******************************************************************************** - -// Matrix-based conversion, which is more accurate, but slower and cannot properly be saved in devicelink profiles -cmsStage* _cmsStageAllocLabV2ToV4(cmsContext ContextID) -{ - static const cmsFloat64Number V2ToV4[] = { 65535.0/65280.0, 0, 0, - 0, 65535.0/65280.0, 0, - 0, 0, 65535.0/65280.0 - }; - - cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, V2ToV4, NULL); - - if (mpe == NULL) return mpe; - mpe ->Implements = cmsSigLabV2toV4; - return mpe; -} - - -// Reverse direction -cmsStage* _cmsStageAllocLabV4ToV2(cmsContext ContextID) -{ - static const cmsFloat64Number V4ToV2[] = { 65280.0/65535.0, 0, 0, - 0, 65280.0/65535.0, 0, - 0, 0, 65280.0/65535.0 - }; - - cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, V4ToV2, NULL); - - if (mpe == NULL) return mpe; - mpe ->Implements = cmsSigLabV4toV2; - return mpe; -} - - -// To Lab to float. Note that the MPE gives numbers in normal Lab range -// and we need 0..1.0 range for the formatters -// L* : 0...100 => 0...1.0 (L* / 100) -// ab* : -128..+127 to 0..1 ((ab* + 128) / 255) - -cmsStage* _cmsStageNormalizeFromLabFloat(cmsContext ContextID) -{ - static const cmsFloat64Number a1[] = { - 1.0/100.0, 0, 0, - 0, 1.0/255.0, 0, - 0, 0, 1.0/255.0 - }; - - static const cmsFloat64Number o1[] = { - 0, - 128.0/255.0, - 128.0/255.0 - }; - - cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, o1); - - if (mpe == NULL) return mpe; - mpe ->Implements = cmsSigLab2FloatPCS; - return mpe; -} - -// Fom XYZ to floating point PCS -cmsStage* _cmsStageNormalizeFromXyzFloat(cmsContext ContextID) -{ -#define n (32768.0/65535.0) - static const cmsFloat64Number a1[] = { - n, 0, 0, - 0, n, 0, - 0, 0, n - }; -#undef n - - cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, NULL); - - if (mpe == NULL) return mpe; - mpe ->Implements = cmsSigXYZ2FloatPCS; - return mpe; -} - -cmsStage* _cmsStageNormalizeToLabFloat(cmsContext ContextID) -{ - static const cmsFloat64Number a1[] = { - 100.0, 0, 0, - 0, 255.0, 0, - 0, 0, 255.0 - }; - - static const cmsFloat64Number o1[] = { - 0, - -128.0, - -128.0 - }; - - cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, o1); - if (mpe == NULL) return mpe; - mpe ->Implements = cmsSigFloatPCS2Lab; - return mpe; -} - -cmsStage* _cmsStageNormalizeToXyzFloat(cmsContext ContextID) -{ -#define n (65535.0/32768.0) - - static const cmsFloat64Number a1[] = { - n, 0, 0, - 0, n, 0, - 0, 0, n - }; -#undef n - - cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, NULL); - if (mpe == NULL) return mpe; - mpe ->Implements = cmsSigFloatPCS2XYZ; - return mpe; -} - - - -// ******************************************************************************** -// Type cmsSigXYZ2LabElemType -// ******************************************************************************** - -static -void EvaluateXYZ2Lab(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) -{ - cmsCIELab Lab; - cmsCIEXYZ XYZ; - const cmsFloat64Number XYZadj = MAX_ENCODEABLE_XYZ; - - // From 0..1.0 to XYZ - - XYZ.X = In[0] * XYZadj; - XYZ.Y = In[1] * XYZadj; - XYZ.Z = In[2] * XYZadj; - - cmsXYZ2Lab(NULL, &Lab, &XYZ); - - // From V4 Lab to 0..1.0 - - Out[0] = (cmsFloat32Number) (Lab.L / 100.0); - Out[1] = (cmsFloat32Number) ((Lab.a + 128.0) / 255.0); - Out[2] = (cmsFloat32Number) ((Lab.b + 128.0) / 255.0); - return; - - cmsUNUSED_PARAMETER(mpe); -} - -cmsStage* _cmsStageAllocXYZ2Lab(cmsContext ContextID) -{ - return _cmsStageAllocPlaceholder(ContextID, cmsSigXYZ2LabElemType, 3, 3, EvaluateXYZ2Lab, NULL, NULL, NULL); - -} - -// ******************************************************************************** - -// For v4, S-Shaped curves are placed in a/b axis to increase resolution near gray - -cmsStage* _cmsStageAllocLabPrelin(cmsContext ContextID) -{ - cmsToneCurve* LabTable[3]; - cmsFloat64Number Params[1] = {2.4} ; - - LabTable[0] = cmsBuildGamma(ContextID, 1.0); - LabTable[1] = cmsBuildParametricToneCurve(ContextID, 108, Params); - LabTable[2] = cmsBuildParametricToneCurve(ContextID, 108, Params); - - return cmsStageAllocToneCurves(ContextID, 3, LabTable); -} - - -// Free a single MPE -void CMSEXPORT cmsStageFree(cmsStage* mpe) -{ - if (mpe ->FreePtr) - mpe ->FreePtr(mpe); - - _cmsFree(mpe ->ContextID, mpe); -} - - -cmsUInt32Number CMSEXPORT cmsStageInputChannels(const cmsStage* mpe) -{ - return mpe ->InputChannels; -} - -cmsUInt32Number CMSEXPORT cmsStageOutputChannels(const cmsStage* mpe) -{ - return mpe ->OutputChannels; -} - -cmsStageSignature CMSEXPORT cmsStageType(const cmsStage* mpe) -{ - return mpe -> Type; -} - -void* CMSEXPORT cmsStageData(const cmsStage* mpe) -{ - return mpe -> Data; -} - -cmsStage* CMSEXPORT cmsStageNext(const cmsStage* mpe) -{ - return mpe -> Next; -} - - -// Duplicates an MPE -cmsStage* CMSEXPORT cmsStageDup(cmsStage* mpe) -{ - cmsStage* NewMPE; - - if (mpe == NULL) return NULL; - NewMPE = _cmsStageAllocPlaceholder(mpe ->ContextID, - mpe ->Type, - mpe ->InputChannels, - mpe ->OutputChannels, - mpe ->EvalPtr, - mpe ->DupElemPtr, - mpe ->FreePtr, - NULL); - if (NewMPE == NULL) return NULL; - - NewMPE ->Implements = mpe ->Implements; - - if (mpe ->DupElemPtr) { - - NewMPE ->Data = mpe ->DupElemPtr(mpe); - - if (NewMPE->Data == NULL) { - - cmsStageFree(NewMPE); - return NULL; - } - - } else { - - NewMPE ->Data = NULL; - } - - return NewMPE; -} - - -// *********************************************************************************************************** - -// This function sets up the channel count -static -cmsBool BlessLUT(cmsPipeline* lut) -{ - // We can set the input/ouput channels only if we have elements. - if (lut ->Elements != NULL) { - - cmsStage* prev; - cmsStage* next; - cmsStage* First; - cmsStage* Last; - - First = cmsPipelineGetPtrToFirstStage(lut); - Last = cmsPipelineGetPtrToLastStage(lut); - - if (First == NULL || Last == NULL) return FALSE; - - lut->InputChannels = First->InputChannels; - lut->OutputChannels = Last->OutputChannels; - - // Check chain consistency - prev = First; - next = prev->Next; - - while (next != NULL) - { - if (next->InputChannels != prev->OutputChannels) - return FALSE; - - next = next->Next; - prev = prev->Next; - } - } - return TRUE; -} - - -// Default to evaluate the LUT on 16 bit-basis. Precision is retained. -static -void _LUTeval16(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register const void* D) -{ - cmsPipeline* lut = (cmsPipeline*) D; - cmsStage *mpe; - cmsFloat32Number Storage[2][MAX_STAGE_CHANNELS] = {0.0f}; - int Phase = 0, NextPhase; - - From16ToFloat(In, &Storage[Phase][0], lut ->InputChannels); - - for (mpe = lut ->Elements; - mpe != NULL; - mpe = mpe ->Next) { - - NextPhase = Phase ^ 1; - mpe ->EvalPtr(&Storage[Phase][0], &Storage[NextPhase][0], mpe); - Phase = NextPhase; - } - - - FromFloatTo16(&Storage[Phase][0], Out, lut ->OutputChannels); -} - - - -// Does evaluate the LUT on cmsFloat32Number-basis. -static -void _LUTevalFloat(register const cmsFloat32Number In[], register cmsFloat32Number Out[], const void* D) -{ - cmsPipeline* lut = (cmsPipeline*) D; - cmsStage *mpe; - cmsFloat32Number Storage[2][MAX_STAGE_CHANNELS] = {0.0f}; - int Phase = 0, NextPhase; - - memmove(&Storage[Phase][0], In, lut ->InputChannels * sizeof(cmsFloat32Number)); - - for (mpe = lut ->Elements; - mpe != NULL; - mpe = mpe ->Next) { - - NextPhase = Phase ^ 1; - mpe ->EvalPtr(&Storage[Phase][0], &Storage[NextPhase][0], mpe); - Phase = NextPhase; - } - - memmove(Out, &Storage[Phase][0], lut ->OutputChannels * sizeof(cmsFloat32Number)); -} - - - - -// LUT Creation & Destruction - -cmsPipeline* CMSEXPORT cmsPipelineAlloc(cmsContext ContextID, cmsUInt32Number InputChannels, cmsUInt32Number OutputChannels) -{ - cmsPipeline* NewLUT; - - // A value of zero in channels is allowed as placeholder - if (InputChannels >= cmsMAXCHANNELS || - OutputChannels >= cmsMAXCHANNELS) return NULL; - - NewLUT = (cmsPipeline*) _cmsMallocZero(ContextID, sizeof(cmsPipeline)); - if (NewLUT == NULL) return NULL; - - - NewLUT -> InputChannels = InputChannels; - NewLUT -> OutputChannels = OutputChannels; - - NewLUT ->Eval16Fn = _LUTeval16; - NewLUT ->EvalFloatFn = _LUTevalFloat; - NewLUT ->DupDataFn = NULL; - NewLUT ->FreeDataFn = NULL; - NewLUT ->Data = NewLUT; - NewLUT ->ContextID = ContextID; - - if (!BlessLUT(NewLUT)) - { - _cmsFree(ContextID, NewLUT); - return NULL; - } - - return NewLUT; -} - -cmsContext CMSEXPORT cmsGetPipelineContextID(const cmsPipeline* lut) -{ - _cmsAssert(lut != NULL); - return lut ->ContextID; -} - -cmsUInt32Number CMSEXPORT cmsPipelineInputChannels(const cmsPipeline* lut) -{ - _cmsAssert(lut != NULL); - return lut ->InputChannels; -} - -cmsUInt32Number CMSEXPORT cmsPipelineOutputChannels(const cmsPipeline* lut) -{ - _cmsAssert(lut != NULL); - return lut ->OutputChannels; -} - -// Free a profile elements LUT -void CMSEXPORT cmsPipelineFree(cmsPipeline* lut) -{ - cmsStage *mpe, *Next; - - if (lut == NULL) return; - - for (mpe = lut ->Elements; - mpe != NULL; - mpe = Next) { - - Next = mpe ->Next; - cmsStageFree(mpe); - } - - if (lut ->FreeDataFn) lut ->FreeDataFn(lut ->ContextID, lut ->Data); - - _cmsFree(lut ->ContextID, lut); -} - - -// Default to evaluate the LUT on 16 bit-basis. -void CMSEXPORT cmsPipelineEval16(const cmsUInt16Number In[], cmsUInt16Number Out[], const cmsPipeline* lut) -{ - _cmsAssert(lut != NULL); - lut ->Eval16Fn(In, Out, lut->Data); -} - - -// Does evaluate the LUT on cmsFloat32Number-basis. -void CMSEXPORT cmsPipelineEvalFloat(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsPipeline* lut) -{ - _cmsAssert(lut != NULL); - lut ->EvalFloatFn(In, Out, lut); -} - - - -// Duplicates a LUT -cmsPipeline* CMSEXPORT cmsPipelineDup(const cmsPipeline* lut) -{ - cmsPipeline* NewLUT; - cmsStage *NewMPE, *Anterior = NULL, *mpe; - cmsBool First = TRUE; - - if (lut == NULL) return NULL; - - NewLUT = cmsPipelineAlloc(lut ->ContextID, lut ->InputChannels, lut ->OutputChannels); - if (NewLUT == NULL) return NULL; - - for (mpe = lut ->Elements; - mpe != NULL; - mpe = mpe ->Next) { - - NewMPE = cmsStageDup(mpe); - - if (NewMPE == NULL) { - cmsPipelineFree(NewLUT); - return NULL; - } - - if (First) { - NewLUT ->Elements = NewMPE; - First = FALSE; - } - else { - Anterior ->Next = NewMPE; - } - - Anterior = NewMPE; - } - - NewLUT ->Eval16Fn = lut ->Eval16Fn; - NewLUT ->EvalFloatFn = lut ->EvalFloatFn; - NewLUT ->DupDataFn = lut ->DupDataFn; - NewLUT ->FreeDataFn = lut ->FreeDataFn; - - if (NewLUT ->DupDataFn != NULL) - NewLUT ->Data = NewLUT ->DupDataFn(lut ->ContextID, lut->Data); - - - NewLUT ->SaveAs8Bits = lut ->SaveAs8Bits; - - if (!BlessLUT(NewLUT)) - { - _cmsFree(lut->ContextID, NewLUT); - return NULL; - } - - return NewLUT; -} - - -int CMSEXPORT cmsPipelineInsertStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage* mpe) -{ - cmsStage* Anterior = NULL, *pt; - - if (lut == NULL || mpe == NULL) - return FALSE; - - switch (loc) { - - case cmsAT_BEGIN: - mpe ->Next = lut ->Elements; - lut ->Elements = mpe; - break; - - case cmsAT_END: - - if (lut ->Elements == NULL) - lut ->Elements = mpe; - else { - - for (pt = lut ->Elements; - pt != NULL; - pt = pt -> Next) Anterior = pt; - - Anterior ->Next = mpe; - mpe ->Next = NULL; - } - break; - default:; - return FALSE; - } - - return BlessLUT(lut); -} - -// Unlink an element and return the pointer to it -void CMSEXPORT cmsPipelineUnlinkStage(cmsPipeline* lut, cmsStageLoc loc, cmsStage** mpe) -{ - cmsStage *Anterior, *pt, *Last; - cmsStage *Unlinked = NULL; - - - // If empty LUT, there is nothing to remove - if (lut ->Elements == NULL) { - if (mpe) *mpe = NULL; - return; - } - - // On depending on the strategy... - switch (loc) { - - case cmsAT_BEGIN: - { - cmsStage* elem = lut ->Elements; - - lut ->Elements = elem -> Next; - elem ->Next = NULL; - Unlinked = elem; - - } - break; - - case cmsAT_END: - Anterior = Last = NULL; - for (pt = lut ->Elements; - pt != NULL; - pt = pt -> Next) { - Anterior = Last; - Last = pt; - } - - Unlinked = Last; // Next already points to NULL - - // Truncate the chain - if (Anterior) - Anterior ->Next = NULL; - else - lut ->Elements = NULL; - break; - default:; - } - - if (mpe) - *mpe = Unlinked; - else - cmsStageFree(Unlinked); - - // May fail, but we ignore it - BlessLUT(lut); -} - - -// Concatenate two LUT into a new single one -cmsBool CMSEXPORT cmsPipelineCat(cmsPipeline* l1, const cmsPipeline* l2) -{ - cmsStage* mpe; - - // If both LUTS does not have elements, we need to inherit - // the number of channels - if (l1 ->Elements == NULL && l2 ->Elements == NULL) { - l1 ->InputChannels = l2 ->InputChannels; - l1 ->OutputChannels = l2 ->OutputChannels; - } - - // Cat second - for (mpe = l2 ->Elements; - mpe != NULL; - mpe = mpe ->Next) { - - // We have to dup each element - if (!cmsPipelineInsertStage(l1, cmsAT_END, cmsStageDup(mpe))) - return FALSE; - } - - return BlessLUT(l1); -} - - -cmsBool CMSEXPORT cmsPipelineSetSaveAs8bitsFlag(cmsPipeline* lut, cmsBool On) -{ - cmsBool Anterior = lut ->SaveAs8Bits; - - lut ->SaveAs8Bits = On; - return Anterior; -} - - -cmsStage* CMSEXPORT cmsPipelineGetPtrToFirstStage(const cmsPipeline* lut) -{ - return lut ->Elements; -} - -cmsStage* CMSEXPORT cmsPipelineGetPtrToLastStage(const cmsPipeline* lut) -{ - cmsStage *mpe, *Anterior = NULL; - - for (mpe = lut ->Elements; mpe != NULL; mpe = mpe ->Next) - Anterior = mpe; - - return Anterior; -} - -cmsUInt32Number CMSEXPORT cmsPipelineStageCount(const cmsPipeline* lut) -{ - cmsStage *mpe; - cmsUInt32Number n; - - for (n=0, mpe = lut ->Elements; mpe != NULL; mpe = mpe ->Next) - n++; - - return n; -} - -// This function may be used to set the optional evaluator and a block of private data. If private data is being used, an optional -// duplicator and free functions should also be specified in order to duplicate the LUT construct. Use NULL to inhibit such functionality. -void CMSEXPORT _cmsPipelineSetOptimizationParameters(cmsPipeline* Lut, - _cmsOPTeval16Fn Eval16, - void* PrivateData, - _cmsFreeUserDataFn FreePrivateDataFn, - _cmsDupUserDataFn DupPrivateDataFn) -{ - - Lut ->Eval16Fn = Eval16; - Lut ->DupDataFn = DupPrivateDataFn; - Lut ->FreeDataFn = FreePrivateDataFn; - Lut ->Data = PrivateData; -} - - -// ----------------------------------------------------------- Reverse interpolation -// Here's how it goes. The derivative Df(x) of the function f is the linear -// transformation that best approximates f near the point x. It can be represented -// by a matrix A whose entries are the partial derivatives of the components of f -// with respect to all the coordinates. This is know as the Jacobian -// -// The best linear approximation to f is given by the matrix equation: -// -// y-y0 = A (x-x0) -// -// So, if x0 is a good "guess" for the zero of f, then solving for the zero of this -// linear approximation will give a "better guess" for the zero of f. Thus let y=0, -// and since y0=f(x0) one can solve the above equation for x. This leads to the -// Newton's method formula: -// -// xn+1 = xn - A-1 f(xn) -// -// where xn+1 denotes the (n+1)-st guess, obtained from the n-th guess xn in the -// fashion described above. Iterating this will give better and better approximations -// if you have a "good enough" initial guess. - - -#define JACOBIAN_EPSILON 0.001f -#define INVERSION_MAX_ITERATIONS 30 - -// Increment with reflexion on boundary -static -void IncDelta(cmsFloat32Number *Val) -{ - if (*Val < (1.0 - JACOBIAN_EPSILON)) - - *Val += JACOBIAN_EPSILON; - - else - *Val -= JACOBIAN_EPSILON; - -} - - - -// Euclidean distance between two vectors of n elements each one -static -cmsFloat32Number EuclideanDistance(cmsFloat32Number a[], cmsFloat32Number b[], int n) -{ - cmsFloat32Number sum = 0; - int i; - - for (i=0; i < n; i++) { - cmsFloat32Number dif = b[i] - a[i]; - sum += dif * dif; - } - - return sqrtf(sum); -} - - -// Evaluate a LUT in reverse direction. It only searches on 3->3 LUT. Uses Newton method -// -// x1 <- x - [J(x)]^-1 * f(x) -// -// lut: The LUT on where to do the search -// Target: LabK, 3 values of Lab plus destination K which is fixed -// Result: The obtained CMYK -// Hint: Location where begin the search - -cmsBool CMSEXPORT cmsPipelineEvalReverseFloat(cmsFloat32Number Target[], - cmsFloat32Number Result[], - cmsFloat32Number Hint[], - const cmsPipeline* lut) -{ - cmsUInt32Number i, j; - cmsFloat64Number error, LastError = 1E20; - cmsFloat32Number fx[4], x[4], xd[4], fxd[4]; - cmsVEC3 tmp, tmp2; - cmsMAT3 Jacobian; - - // Only 3->3 and 4->3 are supported - if (lut ->InputChannels != 3 && lut ->InputChannels != 4) return FALSE; - if (lut ->OutputChannels != 3) return FALSE; - - // Take the hint as starting point if specified - if (Hint == NULL) { - - // Begin at any point, we choose 1/3 of CMY axis - x[0] = x[1] = x[2] = 0.3f; - } - else { - - // Only copy 3 channels from hint... - for (j=0; j < 3; j++) - x[j] = Hint[j]; - } - - // If Lut is 4-dimensions, then grab target[3], which is fixed - if (lut ->InputChannels == 4) { - x[3] = Target[3]; - } - else x[3] = 0; // To keep lint happy - - - // Iterate - for (i = 0; i < INVERSION_MAX_ITERATIONS; i++) { - - // Get beginning fx - cmsPipelineEvalFloat(x, fx, lut); - - // Compute error - error = EuclideanDistance(fx, Target, 3); - - // If not convergent, return last safe value - if (error >= LastError) - break; - - // Keep latest values - LastError = error; - for (j=0; j < lut ->InputChannels; j++) - Result[j] = x[j]; - - // Found an exact match? - if (error <= 0) - break; - - // Obtain slope (the Jacobian) - for (j = 0; j < 3; j++) { - - xd[0] = x[0]; - xd[1] = x[1]; - xd[2] = x[2]; - xd[3] = x[3]; // Keep fixed channel - - IncDelta(&xd[j]); - - cmsPipelineEvalFloat(xd, fxd, lut); - - Jacobian.v[0].n[j] = ((fxd[0] - fx[0]) / JACOBIAN_EPSILON); - Jacobian.v[1].n[j] = ((fxd[1] - fx[1]) / JACOBIAN_EPSILON); - Jacobian.v[2].n[j] = ((fxd[2] - fx[2]) / JACOBIAN_EPSILON); - } - - // Solve system - tmp2.n[0] = fx[0] - Target[0]; - tmp2.n[1] = fx[1] - Target[1]; - tmp2.n[2] = fx[2] - Target[2]; - - if (!_cmsMAT3solve(&tmp, &Jacobian, &tmp2)) - return FALSE; - - // Move our guess - x[0] -= (cmsFloat32Number) tmp.n[0]; - x[1] -= (cmsFloat32Number) tmp.n[1]; - x[2] -= (cmsFloat32Number) tmp.n[2]; - - // Some clipping.... - for (j=0; j < 3; j++) { - if (x[j] < 0) x[j] = 0; - else - if (x[j] > 1.0) x[j] = 1.0; - } - } - - return TRUE; -} diff --git a/third_party/lcms2-2.6/src/cmsmd5.c b/third_party/lcms2-2.6/src/cmsmd5.c deleted file mode 100644 index a4758ff662..0000000000 --- a/third_party/lcms2-2.6/src/cmsmd5.c +++ /dev/null @@ -1,343 +0,0 @@ -//--------------------------------------------------------------------------------- -// -// 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" - -#ifdef CMS_USE_BIG_ENDIAN - -static -void byteReverse(cmsUInt8Number * buf, cmsUInt32Number longs) -{ - do { - - cmsUInt32Number t = _cmsAdjustEndianess32(*(cmsUInt32Number *) buf); - *(cmsUInt32Number *) buf = t; - buf += sizeof(cmsUInt32Number); - - } while (--longs); - -} - -#else -#define byteReverse(buf, len) -#endif - - -typedef struct { - - cmsUInt32Number buf[4]; - cmsUInt32Number bits[2]; - cmsUInt8Number in[64]; - cmsContext ContextID; - -} _cmsMD5; - -#define F1(x, y, z) (z ^ (x & (y ^ z))) -#define F2(x, y, z) F1(z, x, y) -#define F3(x, y, z) (x ^ y ^ z) -#define F4(x, y, z) (y ^ (x | ~z)) - -#define STEP(f, w, x, y, z, data, s) \ - ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) - - -static -void MD5_Transform(cmsUInt32Number buf[4], cmsUInt32Number in[16]) - -{ - register cmsUInt32Number a, b, c, d; - - a = buf[0]; - b = buf[1]; - c = buf[2]; - d = buf[3]; - - STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); - STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); - STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); - STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); - STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); - STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); - STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); - STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); - STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); - STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); - STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); - STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); - STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); - STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); - STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); - STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); - - STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); - STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); - STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); - STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); - STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); - STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); - STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); - STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); - STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); - STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); - STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); - STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); - STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); - STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); - STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); - STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); - - STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); - STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); - STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); - STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); - STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); - STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); - STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); - STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); - STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); - STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); - STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); - STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); - STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); - STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); - STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); - STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); - - STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); - STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); - STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); - STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); - STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); - STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); - STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); - STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); - STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); - STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); - STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); - STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); - STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); - STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); - STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); - STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); - - buf[0] += a; - buf[1] += b; - buf[2] += c; - buf[3] += d; -} - - -// Create a MD5 object -static -cmsHANDLE MD5alloc(cmsContext ContextID) -{ - _cmsMD5* ctx = (_cmsMD5*) _cmsMallocZero(ContextID, sizeof(_cmsMD5)); - if (ctx == NULL) return NULL; - - ctx ->ContextID = ContextID; - - ctx->buf[0] = 0x67452301; - ctx->buf[1] = 0xefcdab89; - ctx->buf[2] = 0x98badcfe; - ctx->buf[3] = 0x10325476; - - ctx->bits[0] = 0; - ctx->bits[1] = 0; - - return (cmsHANDLE) ctx; -} - - -static -void MD5add(cmsHANDLE Handle, cmsUInt8Number* buf, cmsUInt32Number len) -{ - _cmsMD5* ctx = (_cmsMD5*) Handle; - cmsUInt32Number t; - - t = ctx->bits[0]; - if ((ctx->bits[0] = t + (len << 3)) < t) - ctx->bits[1]++; - - ctx->bits[1] += len >> 29; - - t = (t >> 3) & 0x3f; - - if (t) { - - cmsUInt8Number *p = (cmsUInt8Number *) ctx->in + t; - - t = 64 - t; - if (len < t) { - memmove(p, buf, len); - return; - } - - memmove(p, buf, t); - byteReverse(ctx->in, 16); - - MD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in); - buf += t; - len -= t; - } - - while (len >= 64) { - memmove(ctx->in, buf, 64); - byteReverse(ctx->in, 16); - MD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in); - buf += 64; - len -= 64; - } - - memmove(ctx->in, buf, len); -} - -// Destroy the object and return the checksum -static -void MD5finish(cmsProfileID* ProfileID, cmsHANDLE Handle) -{ - _cmsMD5* ctx = (_cmsMD5*) Handle; - cmsUInt32Number count; - cmsUInt8Number *p; - - count = (ctx->bits[0] >> 3) & 0x3F; - - p = ctx->in + count; - *p++ = 0x80; - - count = 64 - 1 - count; - - if (count < 8) { - - memset(p, 0, count); - byteReverse(ctx->in, 16); - MD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in); - - memset(ctx->in, 0, 56); - } else { - memset(p, 0, count - 8); - } - byteReverse(ctx->in, 14); - - ((cmsUInt32Number *) ctx->in)[14] = ctx->bits[0]; - ((cmsUInt32Number *) ctx->in)[15] = ctx->bits[1]; - - MD5_Transform(ctx->buf, (cmsUInt32Number *) ctx->in); - - byteReverse((cmsUInt8Number *) ctx->buf, 4); - memmove(ProfileID ->ID8, ctx->buf, 16); - - _cmsFree(ctx ->ContextID, ctx); -} - - - -// Assuming io points to an ICC profile, compute and store MD5 checksum -// In the header, rendering intentent, attributes and ID should be set to zero -// before computing MD5 checksum (per 6.1.13 in ICC spec) - -cmsBool CMSEXPORT cmsMD5computeID(cmsHPROFILE hProfile) -{ - cmsContext ContextID; - cmsUInt32Number BytesNeeded; - cmsUInt8Number* Mem = NULL; - cmsHANDLE MD5 = NULL; - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; - _cmsICCPROFILE Keep; - - _cmsAssert(hProfile != NULL); - - ContextID = cmsGetProfileContextID(hProfile); - - // Save a copy of the profile header - memmove(&Keep, Icc, sizeof(_cmsICCPROFILE)); - - // Set RI, attributes and ID - memset(&Icc ->attributes, 0, sizeof(Icc ->attributes)); - Icc ->RenderingIntent = 0; - memset(&Icc ->ProfileID, 0, sizeof(Icc ->ProfileID)); - - // Compute needed storage - if (!cmsSaveProfileToMem(hProfile, NULL, &BytesNeeded)) goto Error; - - // Allocate memory - Mem = (cmsUInt8Number*) _cmsMalloc(ContextID, BytesNeeded); - if (Mem == NULL) goto Error; - - // Save to temporary storage - if (!cmsSaveProfileToMem(hProfile, Mem, &BytesNeeded)) goto Error; - - // Create MD5 object - MD5 = MD5alloc(ContextID); - if (MD5 == NULL) goto Error; - - // Add all bytes - MD5add(MD5, Mem, BytesNeeded); - - // Temp storage is no longer needed - _cmsFree(ContextID, Mem); - - // Restore header - memmove(Icc, &Keep, sizeof(_cmsICCPROFILE)); - - // And store the ID - MD5finish(&Icc ->ProfileID, MD5); - return TRUE; - -Error: - - // Free resources as something went wrong - // "MD5" cannot be other than NULL here, so no need to free it - if (Mem != NULL) _cmsFree(ContextID, Mem); - memmove(Icc, &Keep, sizeof(_cmsICCPROFILE)); - return FALSE; -} - -cmsBool CMSEXPORT cmsMD5computeIDExt(const void* buf, unsigned long size, unsigned char ProfileID[16]) -{ - cmsHANDLE MD5; - cmsUInt8Number* Mem; - - if (buf == NULL) - return FALSE; - MD5 = NULL; - Mem = (cmsUInt8Number*)_cmsMalloc(NULL,size); - memmove(Mem,buf,size); - // Create MD5 object - MD5 = MD5alloc(NULL); - if (MD5 == NULL) goto Error; - - // Add all bytes - MD5add(MD5, Mem, size); - - // Temp storage is no longer needed - _cmsFree(NULL, Mem); - - // And store the ID - MD5finish((cmsProfileID*)ProfileID, MD5); - return TRUE; -Error: - if (MD5 != NULL) _cmsFree(NULL, MD5); - return FALSE; -} diff --git a/third_party/lcms2-2.6/src/cmsmtrx.c b/third_party/lcms2-2.6/src/cmsmtrx.c deleted file mode 100644 index fb7b91caf1..0000000000 --- a/third_party/lcms2-2.6/src/cmsmtrx.c +++ /dev/null @@ -1,175 +0,0 @@ -//--------------------------------------------------------------------------------- -// -// 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" - - -#define DSWAP(x, y) {cmsFloat64Number tmp = (x); (x)=(y); (y)=tmp;} - - -// Initiate a vector -void CMSEXPORT _cmsVEC3init(cmsVEC3* r, cmsFloat64Number x, cmsFloat64Number y, cmsFloat64Number z) -{ - r -> n[VX] = x; - r -> n[VY] = y; - r -> n[VZ] = z; -} - -// Vector substraction -void CMSEXPORT _cmsVEC3minus(cmsVEC3* r, const cmsVEC3* a, const cmsVEC3* b) -{ - r -> n[VX] = a -> n[VX] - b -> n[VX]; - r -> n[VY] = a -> n[VY] - b -> n[VY]; - r -> n[VZ] = a -> n[VZ] - b -> n[VZ]; -} - -// Vector cross product -void CMSEXPORT _cmsVEC3cross(cmsVEC3* r, const cmsVEC3* u, const cmsVEC3* v) -{ - r ->n[VX] = u->n[VY] * v->n[VZ] - v->n[VY] * u->n[VZ]; - r ->n[VY] = u->n[VZ] * v->n[VX] - v->n[VZ] * u->n[VX]; - r ->n[VZ] = u->n[VX] * v->n[VY] - v->n[VX] * u->n[VY]; -} - -// Vector dot product -cmsFloat64Number CMSEXPORT _cmsVEC3dot(const cmsVEC3* u, const cmsVEC3* v) -{ - return u->n[VX] * v->n[VX] + u->n[VY] * v->n[VY] + u->n[VZ] * v->n[VZ]; -} - -// Euclidean length -cmsFloat64Number CMSEXPORT _cmsVEC3length(const cmsVEC3* a) -{ - return sqrt(a ->n[VX] * a ->n[VX] + - a ->n[VY] * a ->n[VY] + - a ->n[VZ] * a ->n[VZ]); -} - -// Euclidean distance -cmsFloat64Number CMSEXPORT _cmsVEC3distance(const cmsVEC3* a, const cmsVEC3* b) -{ - cmsFloat64Number d1 = a ->n[VX] - b ->n[VX]; - cmsFloat64Number d2 = a ->n[VY] - b ->n[VY]; - cmsFloat64Number d3 = a ->n[VZ] - b ->n[VZ]; - - return sqrt(d1*d1 + d2*d2 + d3*d3); -} - - - -// 3x3 Identity -void CMSEXPORT _cmsMAT3identity(cmsMAT3* a) -{ - _cmsVEC3init(&a-> v[0], 1.0, 0.0, 0.0); - _cmsVEC3init(&a-> v[1], 0.0, 1.0, 0.0); - _cmsVEC3init(&a-> v[2], 0.0, 0.0, 1.0); -} - -static -cmsBool CloseEnough(cmsFloat64Number a, cmsFloat64Number b) -{ - return fabs(b - a) < (1.0 / 65535.0); -} - - -cmsBool CMSEXPORT _cmsMAT3isIdentity(const cmsMAT3* a) -{ - cmsMAT3 Identity; - int i, j; - - _cmsMAT3identity(&Identity); - - for (i=0; i < 3; i++) - for (j=0; j < 3; j++) - if (!CloseEnough(a ->v[i].n[j], Identity.v[i].n[j])) return FALSE; - - return TRUE; -} - - -// Multiply two matrices -void CMSEXPORT _cmsMAT3per(cmsMAT3* r, const cmsMAT3* a, const cmsMAT3* b) -{ -#define ROWCOL(i, j) \ - a->v[i].n[0]*b->v[0].n[j] + a->v[i].n[1]*b->v[1].n[j] + a->v[i].n[2]*b->v[2].n[j] - - _cmsVEC3init(&r-> v[0], ROWCOL(0,0), ROWCOL(0,1), ROWCOL(0,2)); - _cmsVEC3init(&r-> v[1], ROWCOL(1,0), ROWCOL(1,1), ROWCOL(1,2)); - _cmsVEC3init(&r-> v[2], ROWCOL(2,0), ROWCOL(2,1), ROWCOL(2,2)); - -#undef ROWCOL //(i, j) -} - - - -// Inverse of a matrix b = a^(-1) -cmsBool CMSEXPORT _cmsMAT3inverse(const cmsMAT3* a, cmsMAT3* b) -{ - cmsFloat64Number det, c0, c1, c2; - - c0 = a -> v[1].n[1]*a -> v[2].n[2] - a -> v[1].n[2]*a -> v[2].n[1]; - c1 = -a -> v[1].n[0]*a -> v[2].n[2] + a -> v[1].n[2]*a -> v[2].n[0]; - c2 = a -> v[1].n[0]*a -> v[2].n[1] - a -> v[1].n[1]*a -> v[2].n[0]; - - det = a -> v[0].n[0]*c0 + a -> v[0].n[1]*c1 + a -> v[0].n[2]*c2; - - if (fabs(det) < MATRIX_DET_TOLERANCE) return FALSE; // singular matrix; can't invert - - b -> v[0].n[0] = c0/det; - b -> v[0].n[1] = (a -> v[0].n[2]*a -> v[2].n[1] - a -> v[0].n[1]*a -> v[2].n[2])/det; - b -> v[0].n[2] = (a -> v[0].n[1]*a -> v[1].n[2] - a -> v[0].n[2]*a -> v[1].n[1])/det; - b -> v[1].n[0] = c1/det; - b -> v[1].n[1] = (a -> v[0].n[0]*a -> v[2].n[2] - a -> v[0].n[2]*a -> v[2].n[0])/det; - b -> v[1].n[2] = (a -> v[0].n[2]*a -> v[1].n[0] - a -> v[0].n[0]*a -> v[1].n[2])/det; - b -> v[2].n[0] = c2/det; - b -> v[2].n[1] = (a -> v[0].n[1]*a -> v[2].n[0] - a -> v[0].n[0]*a -> v[2].n[1])/det; - b -> v[2].n[2] = (a -> v[0].n[0]*a -> v[1].n[1] - a -> v[0].n[1]*a -> v[1].n[0])/det; - - return TRUE; -} - - -// Solve a system in the form Ax = b -cmsBool CMSEXPORT _cmsMAT3solve(cmsVEC3* x, cmsMAT3* a, cmsVEC3* b) -{ - cmsMAT3 m, a_1; - - memmove(&m, a, sizeof(cmsMAT3)); - - if (!_cmsMAT3inverse(&m, &a_1)) return FALSE; // Singular matrix - - _cmsMAT3eval(x, &a_1, b); - return TRUE; -} - -// Evaluate a vector across a matrix -void CMSEXPORT _cmsMAT3eval(cmsVEC3* r, const cmsMAT3* a, const cmsVEC3* v) -{ - r->n[VX] = a->v[0].n[VX]*v->n[VX] + a->v[0].n[VY]*v->n[VY] + a->v[0].n[VZ]*v->n[VZ]; - r->n[VY] = a->v[1].n[VX]*v->n[VX] + a->v[1].n[VY]*v->n[VY] + a->v[1].n[VZ]*v->n[VZ]; - r->n[VZ] = a->v[2].n[VX]*v->n[VX] + a->v[2].n[VY]*v->n[VY] + a->v[2].n[VZ]*v->n[VZ]; -} - diff --git a/third_party/lcms2-2.6/src/cmsnamed.c b/third_party/lcms2-2.6/src/cmsnamed.c deleted file mode 100644 index ef1eb3089e..0000000000 --- a/third_party/lcms2-2.6/src/cmsnamed.c +++ /dev/null @@ -1,937 +0,0 @@ -//--------------------------------------------------------------------------------- -// -// 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" - -// Multilocalized unicode objects. That is an attempt to encapsulate i18n. - - -// Allocates an empty multi localizad unicode object -cmsMLU* CMSEXPORT cmsMLUalloc(cmsContext ContextID, cmsUInt32Number nItems) -{ - cmsMLU* mlu; - - // nItems should be positive if given - if (nItems <= 0) nItems = 2; - - // Create the container - mlu = (cmsMLU*) _cmsMallocZero(ContextID, sizeof(cmsMLU)); - if (mlu == NULL) return NULL; - - mlu ->ContextID = ContextID; - - // Create entry array - mlu ->Entries = (_cmsMLUentry*) _cmsCalloc(ContextID, nItems, sizeof(_cmsMLUentry)); - if (mlu ->Entries == NULL) { - _cmsFree(ContextID, mlu); - return NULL; - } - - // Ok, keep indexes up to date - mlu ->AllocatedEntries = nItems; - mlu ->UsedEntries = 0; - - return mlu; -} - - -// Grows a mempool table for a MLU. Each time this function is called, mempool size is multiplied times two. -static -cmsBool GrowMLUpool(cmsMLU* mlu) -{ - cmsUInt32Number size; - void *NewPtr; - - // Sanity check - if (mlu == NULL) return FALSE; - - if (mlu ->PoolSize == 0) - size = 256; - else - size = mlu ->PoolSize * 2; - - // Check for overflow - if (size < mlu ->PoolSize) return FALSE; - - // Reallocate the pool - NewPtr = _cmsRealloc(mlu ->ContextID, mlu ->MemPool, size); - if (NewPtr == NULL) return FALSE; - - - mlu ->MemPool = NewPtr; - mlu ->PoolSize = size; - - return TRUE; -} - - -// Grows a entry table for a MLU. Each time this function is called, table size is multiplied times two. -static -cmsBool GrowMLUtable(cmsMLU* mlu) -{ - int AllocatedEntries; - _cmsMLUentry *NewPtr; - - // Sanity check - if (mlu == NULL) return FALSE; - - AllocatedEntries = mlu ->AllocatedEntries * 2; - - // Check for overflow - if (AllocatedEntries / 2 != mlu ->AllocatedEntries) return FALSE; - - // Reallocate the memory - NewPtr = (_cmsMLUentry*)_cmsRealloc(mlu ->ContextID, mlu ->Entries, AllocatedEntries*sizeof(_cmsMLUentry)); - if (NewPtr == NULL) return FALSE; - - mlu ->Entries = NewPtr; - mlu ->AllocatedEntries = AllocatedEntries; - - return TRUE; -} - - -// Search for a specific entry in the structure. Language and Country are used. -static -int SearchMLUEntry(cmsMLU* mlu, cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode) -{ - int i; - - // Sanity check - if (mlu == NULL) return -1; - - // Iterate whole table - for (i=0; i < mlu ->UsedEntries; i++) { - - if (mlu ->Entries[i].Country == CountryCode && - mlu ->Entries[i].Language == LanguageCode) return i; - } - - // Not found - return -1; -} - -// Add a block of characters to the intended MLU. Language and country are specified. -// Only one entry for Language/country pair is allowed. -static -cmsBool AddMLUBlock(cmsMLU* mlu, cmsUInt32Number size, const wchar_t *Block, - cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode) -{ - cmsUInt32Number Offset; - cmsUInt8Number* Ptr; - - // Sanity check - if (mlu == NULL) return FALSE; - - // Is there any room available? - if (mlu ->UsedEntries >= mlu ->AllocatedEntries) { - if (!GrowMLUtable(mlu)) return FALSE; - } - - // Only one ASCII string - if (SearchMLUEntry(mlu, LanguageCode, CountryCode) >= 0) return FALSE; // Only one is allowed! - - // Check for size - while ((mlu ->PoolSize - mlu ->PoolUsed) < size) { - - if (!GrowMLUpool(mlu)) return FALSE; - } - - Offset = mlu ->PoolUsed; - - Ptr = (cmsUInt8Number*) mlu ->MemPool; - if (Ptr == NULL) return FALSE; - - // Set the entry - memmove(Ptr + Offset, Block, size); - mlu ->PoolUsed += size; - - mlu ->Entries[mlu ->UsedEntries].StrW = Offset; - mlu ->Entries[mlu ->UsedEntries].Len = size; - mlu ->Entries[mlu ->UsedEntries].Country = CountryCode; - mlu ->Entries[mlu ->UsedEntries].Language = LanguageCode; - mlu ->UsedEntries++; - - return TRUE; -} - - -// Add an ASCII entry. -cmsBool CMSEXPORT cmsMLUsetASCII(cmsMLU* mlu, const char LanguageCode[3], const char CountryCode[3], const char* ASCIIString) -{ - cmsUInt32Number i, len = (cmsUInt32Number) strlen(ASCIIString)+1; - wchar_t* WStr; - cmsBool rc; - cmsUInt16Number Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) LanguageCode); - cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) CountryCode); - - if (mlu == NULL) return FALSE; - - WStr = (wchar_t*) _cmsCalloc(mlu ->ContextID, len, sizeof(wchar_t)); - if (WStr == NULL) return FALSE; - - for (i=0; i < len; i++) - WStr[i] = (wchar_t) ASCIIString[i]; - - rc = AddMLUBlock(mlu, len * sizeof(wchar_t), WStr, Lang, Cntry); - - _cmsFree(mlu ->ContextID, WStr); - return rc; - -} - -// We don't need any wcs support library -static -cmsUInt32Number mywcslen(const wchar_t *s) -{ - const wchar_t *p; - - p = s; - while (*p) - p++; - - return (cmsUInt32Number)(p - s); -} - - -// Add a wide entry -cmsBool CMSEXPORT cmsMLUsetWide(cmsMLU* mlu, const char Language[3], const char Country[3], const wchar_t* WideString) -{ - cmsUInt16Number Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) Language); - cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) Country); - cmsUInt32Number len; - - if (mlu == NULL) return FALSE; - if (WideString == NULL) return FALSE; - - len = (cmsUInt32Number) (mywcslen(WideString) + 1) * sizeof(wchar_t); - return AddMLUBlock(mlu, len, WideString, Lang, Cntry); -} - -// Duplicating a MLU is as easy as copying all members -cmsMLU* CMSEXPORT cmsMLUdup(const cmsMLU* mlu) -{ - cmsMLU* NewMlu = NULL; - - // Duplicating a NULL obtains a NULL - if (mlu == NULL) return NULL; - - NewMlu = cmsMLUalloc(mlu ->ContextID, mlu ->UsedEntries); - if (NewMlu == NULL) return NULL; - - // Should never happen - if (NewMlu ->AllocatedEntries < mlu ->UsedEntries) - goto Error; - - // Sanitize... - if (NewMlu ->Entries == NULL || mlu ->Entries == NULL) goto Error; - - memmove(NewMlu ->Entries, mlu ->Entries, mlu ->UsedEntries * sizeof(_cmsMLUentry)); - NewMlu ->UsedEntries = mlu ->UsedEntries; - - // The MLU may be empty - if (mlu ->PoolUsed == 0) { - NewMlu ->MemPool = NULL; - } - else { - // It is not empty - NewMlu ->MemPool = _cmsMalloc(mlu ->ContextID, mlu ->PoolUsed); - if (NewMlu ->MemPool == NULL) goto Error; - } - - NewMlu ->PoolSize = mlu ->PoolUsed; - - if (NewMlu ->MemPool == NULL || mlu ->MemPool == NULL) goto Error; - - memmove(NewMlu ->MemPool, mlu->MemPool, mlu ->PoolUsed); - NewMlu ->PoolUsed = mlu ->PoolUsed; - - return NewMlu; - -Error: - - if (NewMlu != NULL) cmsMLUfree(NewMlu); - return NULL; -} - -// Free any used memory -void CMSEXPORT cmsMLUfree(cmsMLU* mlu) -{ - if (mlu) { - - if (mlu -> Entries) _cmsFree(mlu ->ContextID, mlu->Entries); - if (mlu -> MemPool) _cmsFree(mlu ->ContextID, mlu->MemPool); - - _cmsFree(mlu ->ContextID, mlu); - } -} - - -// The algorithm first searches for an exact match of country and language, if not found it uses -// the Language. If none is found, first entry is used instead. -static -const wchar_t* _cmsMLUgetWide(const cmsMLU* mlu, - cmsUInt32Number *len, - cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode, - cmsUInt16Number* UsedLanguageCode, cmsUInt16Number* UsedCountryCode) -{ - int i; - int Best = -1; - _cmsMLUentry* v; - - if (mlu == NULL) return NULL; - - if (mlu -> AllocatedEntries <= 0) return NULL; - - for (i=0; i < mlu ->UsedEntries; i++) { - - v = mlu ->Entries + i; - - if (v -> Language == LanguageCode) { - - if (Best == -1) Best = i; - - if (v -> Country == CountryCode) { - - if (UsedLanguageCode != NULL) *UsedLanguageCode = v ->Language; - if (UsedCountryCode != NULL) *UsedCountryCode = v ->Country; - - if (len != NULL) *len = v ->Len; - - return (wchar_t*) ((cmsUInt8Number*) mlu ->MemPool + v -> StrW); // Found exact match - } - } - } - - // No string found. Return First one - if (Best == -1) - Best = 0; - - v = mlu ->Entries + Best; - - if (UsedLanguageCode != NULL) *UsedLanguageCode = v ->Language; - if (UsedCountryCode != NULL) *UsedCountryCode = v ->Country; - - if (len != NULL) *len = v ->Len; - - return(wchar_t*) ((cmsUInt8Number*) mlu ->MemPool + v ->StrW); -} - - -// Obtain an ASCII representation of the wide string. Setting buffer to NULL returns the len -cmsUInt32Number CMSEXPORT cmsMLUgetASCII(const cmsMLU* mlu, - const char LanguageCode[3], const char CountryCode[3], - char* Buffer, cmsUInt32Number BufferSize) -{ - const wchar_t *Wide; - cmsUInt32Number StrLen = 0; - cmsUInt32Number ASCIIlen, i; - - cmsUInt16Number Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) LanguageCode); - cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) CountryCode); - - // Sanitize - if (mlu == NULL) return 0; - - // Get WideChar - Wide = _cmsMLUgetWide(mlu, &StrLen, Lang, Cntry, NULL, NULL); - if (Wide == NULL) return 0; - - ASCIIlen = StrLen / sizeof(wchar_t); - - // Maybe we want only to know the len? - if (Buffer == NULL) return ASCIIlen + 1; // Note the zero at the end - - // No buffer size means no data - if (BufferSize <= 0) return 0; - - // Some clipping may be required - if (BufferSize < ASCIIlen + 1) - ASCIIlen = BufferSize - 1; - - // Precess each character - for (i=0; i < ASCIIlen; i++) { - - if (Wide[i] == 0) - Buffer[i] = 0; - else - Buffer[i] = (char) Wide[i]; - } - - // We put a termination "\0" - Buffer[ASCIIlen] = 0; - return ASCIIlen + 1; -} - -// Obtain a wide representation of the MLU, on depending on current locale settings -cmsUInt32Number CMSEXPORT cmsMLUgetWide(const cmsMLU* mlu, - const char LanguageCode[3], const char CountryCode[3], - wchar_t* Buffer, cmsUInt32Number BufferSize) -{ - const wchar_t *Wide; - cmsUInt32Number StrLen = 0; - - cmsUInt16Number Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) LanguageCode); - cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) CountryCode); - - // Sanitize - if (mlu == NULL) return 0; - - Wide = _cmsMLUgetWide(mlu, &StrLen, Lang, Cntry, NULL, NULL); - if (Wide == NULL) return 0; - - // Maybe we want only to know the len? - if (Buffer == NULL) return StrLen + sizeof(wchar_t); - - // No buffer size means no data - if (BufferSize <= 0) return 0; - - // Some clipping may be required - if (BufferSize < StrLen + sizeof(wchar_t)) - StrLen = BufferSize - + sizeof(wchar_t); - - memmove(Buffer, Wide, StrLen); - Buffer[StrLen / sizeof(wchar_t)] = 0; - - return StrLen + sizeof(wchar_t); -} - - -// Get also the language and country -CMSAPI cmsBool CMSEXPORT cmsMLUgetTranslation(const cmsMLU* mlu, - const char LanguageCode[3], const char CountryCode[3], - char ObtainedLanguage[3], char ObtainedCountry[3]) -{ - const wchar_t *Wide; - - cmsUInt16Number Lang = _cmsAdjustEndianess16(*(cmsUInt16Number*) LanguageCode); - cmsUInt16Number Cntry = _cmsAdjustEndianess16(*(cmsUInt16Number*) CountryCode); - cmsUInt16Number ObtLang, ObtCode; - - // Sanitize - if (mlu == NULL) return FALSE; - - Wide = _cmsMLUgetWide(mlu, NULL, Lang, Cntry, &ObtLang, &ObtCode); - if (Wide == NULL) return FALSE; - - // Get used language and code - *(cmsUInt16Number *)ObtainedLanguage = _cmsAdjustEndianess16(ObtLang); - *(cmsUInt16Number *)ObtainedCountry = _cmsAdjustEndianess16(ObtCode); - - ObtainedLanguage[2] = ObtainedCountry[2] = 0; - return TRUE; -} - - - -// Get the number of translations in the MLU object -cmsUInt32Number CMSEXPORT cmsMLUtranslationsCount(const cmsMLU* mlu) -{ - if (mlu == NULL) return 0; - return mlu->UsedEntries; -} - -// Get the language and country codes for a specific MLU index -cmsBool CMSEXPORT cmsMLUtranslationsCodes(const cmsMLU* mlu, - cmsUInt32Number idx, - char LanguageCode[3], - char CountryCode[3]) -{ - _cmsMLUentry *entry; - - if (mlu == NULL) return FALSE; - - if (idx >= (cmsUInt32Number) mlu->UsedEntries) return FALSE; - - entry = &mlu->Entries[idx]; - - *(cmsUInt16Number *)LanguageCode = _cmsAdjustEndianess16(entry->Language); - *(cmsUInt16Number *)CountryCode = _cmsAdjustEndianess16(entry->Country); - - return TRUE; -} - - -// Named color lists -------------------------------------------------------------------------------------------- - -// Grow the list to keep at least NumElements -static -cmsBool GrowNamedColorList(cmsNAMEDCOLORLIST* v) -{ - cmsUInt32Number size; - _cmsNAMEDCOLOR * NewPtr; - - if (v == NULL) return FALSE; - - if (v ->Allocated == 0) - size = 64; // Initial guess - else - size = v ->Allocated * 2; - - // Keep a maximum color lists can grow, 100K entries seems reasonable - if (size > 1024*100) return FALSE; - - NewPtr = (_cmsNAMEDCOLOR*) _cmsRealloc(v ->ContextID, v ->List, size * sizeof(_cmsNAMEDCOLOR)); - if (NewPtr == NULL) - return FALSE; - - v ->List = NewPtr; - v ->Allocated = size; - return TRUE; -} - -// Allocate a list for n elements -cmsNAMEDCOLORLIST* CMSEXPORT cmsAllocNamedColorList(cmsContext ContextID, cmsUInt32Number n, cmsUInt32Number ColorantCount, const char* Prefix, const char* Suffix) -{ - cmsNAMEDCOLORLIST* v = (cmsNAMEDCOLORLIST*) _cmsMallocZero(ContextID, sizeof(cmsNAMEDCOLORLIST)); - - if (v == NULL) return NULL; - - v ->List = NULL; - v ->nColors = 0; - v ->ContextID = ContextID; - - while (v -> Allocated < n) { - if (!GrowNamedColorList(v)) { - cmsFreeNamedColorList(v); - return NULL; - } - } - - strncpy(v ->Prefix, Prefix, sizeof(v ->Prefix)-1); - strncpy(v ->Suffix, Suffix, sizeof(v ->Suffix)-1); - v->Prefix[32] = v->Suffix[32] = 0; - - v -> ColorantCount = ColorantCount; - - return v; -} - -// Free a list -void CMSEXPORT cmsFreeNamedColorList(cmsNAMEDCOLORLIST* v) -{ - if (v == NULL) return; - if (v ->List) _cmsFree(v ->ContextID, v ->List); - _cmsFree(v ->ContextID, v); -} - -cmsNAMEDCOLORLIST* CMSEXPORT cmsDupNamedColorList(const cmsNAMEDCOLORLIST* v) -{ - cmsNAMEDCOLORLIST* NewNC; - - if (v == NULL) return NULL; - - NewNC= cmsAllocNamedColorList(v ->ContextID, v -> nColors, v ->ColorantCount, v ->Prefix, v ->Suffix); - if (NewNC == NULL) return NULL; - - // For really large tables we need this - while (NewNC ->Allocated < v ->Allocated) { - if (!GrowNamedColorList(NewNC)) { - cmsFreeNamedColorList(NewNC); - return NULL; - } - } - - memmove(NewNC ->Prefix, v ->Prefix, sizeof(v ->Prefix)); - memmove(NewNC ->Suffix, v ->Suffix, sizeof(v ->Suffix)); - NewNC ->ColorantCount = v ->ColorantCount; - memmove(NewNC->List, v ->List, v->nColors * sizeof(_cmsNAMEDCOLOR)); - NewNC ->nColors = v ->nColors; - return NewNC; -} - - -// Append a color to a list. List pointer may change if reallocated -cmsBool CMSEXPORT cmsAppendNamedColor(cmsNAMEDCOLORLIST* NamedColorList, - const char* Name, - cmsUInt16Number PCS[3], cmsUInt16Number Colorant[cmsMAXCHANNELS]) -{ - cmsUInt32Number i; - - if (NamedColorList == NULL) return FALSE; - - if (NamedColorList ->nColors + 1 > NamedColorList ->Allocated) { - if (!GrowNamedColorList(NamedColorList)) return FALSE; - } - - for (i=0; i < NamedColorList ->ColorantCount; i++) - NamedColorList ->List[NamedColorList ->nColors].DeviceColorant[i] = Colorant == NULL? 0 : Colorant[i]; - - for (i=0; i < 3; i++) - NamedColorList ->List[NamedColorList ->nColors].PCS[i] = PCS == NULL ? 0 : PCS[i]; - - if (Name != NULL) { - - strncpy(NamedColorList ->List[NamedColorList ->nColors].Name, Name, cmsMAX_PATH-1); - NamedColorList ->List[NamedColorList ->nColors].Name[cmsMAX_PATH-1] = 0; - - } - else - NamedColorList ->List[NamedColorList ->nColors].Name[0] = 0; - - - NamedColorList ->nColors++; - return TRUE; -} - -// Returns number of elements -cmsUInt32Number CMSEXPORT cmsNamedColorCount(const cmsNAMEDCOLORLIST* NamedColorList) -{ - if (NamedColorList == NULL) return 0; - return NamedColorList ->nColors; -} - -// Info aboout a given color -cmsBool CMSEXPORT cmsNamedColorInfo(const cmsNAMEDCOLORLIST* NamedColorList, cmsUInt32Number nColor, - char* Name, - char* Prefix, - char* Suffix, - cmsUInt16Number* PCS, - cmsUInt16Number* Colorant) -{ - if (NamedColorList == NULL) return FALSE; - - if (nColor >= cmsNamedColorCount(NamedColorList)) return FALSE; - - if (Name) strcpy(Name, NamedColorList->List[nColor].Name); - if (Prefix) strcpy(Prefix, NamedColorList->Prefix); - if (Suffix) strcpy(Suffix, NamedColorList->Suffix); - if (PCS) - memmove(PCS, NamedColorList ->List[nColor].PCS, 3*sizeof(cmsUInt16Number)); - - if (Colorant) - memmove(Colorant, NamedColorList ->List[nColor].DeviceColorant, - sizeof(cmsUInt16Number) * NamedColorList ->ColorantCount); - - - return TRUE; -} - -// Search for a given color name (no prefix or suffix) -cmsInt32Number CMSEXPORT cmsNamedColorIndex(const cmsNAMEDCOLORLIST* NamedColorList, const char* Name) -{ - int i, n; - - if (NamedColorList == NULL) return -1; - n = cmsNamedColorCount(NamedColorList); - for (i=0; i < n; i++) { - if (cmsstrcasecmp(Name, NamedColorList->List[i].Name) == 0) - return i; - } - - return -1; -} - -// MPE support ----------------------------------------------------------------------------------------------------------------- - -static -void FreeNamedColorList(cmsStage* mpe) -{ - cmsNAMEDCOLORLIST* List = (cmsNAMEDCOLORLIST*) mpe ->Data; - cmsFreeNamedColorList(List); -} - -static -void* DupNamedColorList(cmsStage* mpe) -{ - cmsNAMEDCOLORLIST* List = (cmsNAMEDCOLORLIST*) mpe ->Data; - return cmsDupNamedColorList(List); -} - -static -void EvalNamedColorPCS(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) -{ - cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) mpe ->Data; - cmsUInt16Number index = (cmsUInt16Number) _cmsQuickSaturateWord(In[0] * 65535.0); - - if (index >= NamedColorList-> nColors) { - cmsSignalError(NamedColorList ->ContextID, cmsERROR_RANGE, "Color %d out of range; ignored", index); - } - else { - - // Named color always uses Lab - Out[0] = (cmsFloat32Number) (NamedColorList->List[index].PCS[0] / 65535.0); - Out[1] = (cmsFloat32Number) (NamedColorList->List[index].PCS[1] / 65535.0); - Out[2] = (cmsFloat32Number) (NamedColorList->List[index].PCS[2] / 65535.0); - } -} - -static -void EvalNamedColor(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) -{ - cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) mpe ->Data; - cmsUInt16Number index = (cmsUInt16Number) _cmsQuickSaturateWord(In[0] * 65535.0); - cmsUInt32Number j; - - if (index >= NamedColorList-> nColors) { - cmsSignalError(NamedColorList ->ContextID, cmsERROR_RANGE, "Color %d out of range; ignored", index); - } - else { - for (j=0; j < NamedColorList ->ColorantCount; j++) - Out[j] = (cmsFloat32Number) (NamedColorList->List[index].DeviceColorant[j] / 65535.0); - } -} - - -// Named color lookup element -cmsStage* _cmsStageAllocNamedColor(cmsNAMEDCOLORLIST* NamedColorList, cmsBool UsePCS) -{ - return _cmsStageAllocPlaceholder(NamedColorList ->ContextID, - cmsSigNamedColorElemType, - 1, UsePCS ? 3 : NamedColorList ->ColorantCount, - UsePCS ? EvalNamedColorPCS : EvalNamedColor, - DupNamedColorList, - FreeNamedColorList, - cmsDupNamedColorList(NamedColorList)); - -} - - -// Retrieve the named color list from a transform. Should be first element in the LUT -cmsNAMEDCOLORLIST* CMSEXPORT cmsGetNamedColorList(cmsHTRANSFORM xform) -{ - _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform; - cmsStage* mpe = v ->Lut->Elements; - - if (mpe ->Type != cmsSigNamedColorElemType) return NULL; - return (cmsNAMEDCOLORLIST*) mpe ->Data; -} - - -// Profile sequence description routines ------------------------------------------------------------------------------------- - -cmsSEQ* CMSEXPORT cmsAllocProfileSequenceDescription(cmsContext ContextID, cmsUInt32Number n) -{ - cmsSEQ* Seq; - cmsUInt32Number i; - - if (n == 0) return NULL; - - // In a absolutely arbitrary way, I hereby decide to allow a maxim of 255 profiles linked - // in a devicelink. It makes not sense anyway and may be used for exploits, so let's close the door! - if (n > 255) return NULL; - - Seq = (cmsSEQ*) _cmsMallocZero(ContextID, sizeof(cmsSEQ)); - if (Seq == NULL) return NULL; - - Seq -> ContextID = ContextID; - Seq -> seq = (cmsPSEQDESC*) _cmsCalloc(ContextID, n, sizeof(cmsPSEQDESC)); - Seq -> n = n; - - if (Seq -> seq == NULL) { - _cmsFree(ContextID, Seq); - return NULL; - } - - for (i=0; i < n; i++) { - Seq -> seq[i].Manufacturer = NULL; - Seq -> seq[i].Model = NULL; - Seq -> seq[i].Description = NULL; - } - - return Seq; -} - -void CMSEXPORT cmsFreeProfileSequenceDescription(cmsSEQ* pseq) -{ - cmsUInt32Number i; - - for (i=0; i < pseq ->n; i++) { - if (pseq ->seq[i].Manufacturer != NULL) cmsMLUfree(pseq ->seq[i].Manufacturer); - if (pseq ->seq[i].Model != NULL) cmsMLUfree(pseq ->seq[i].Model); - if (pseq ->seq[i].Description != NULL) cmsMLUfree(pseq ->seq[i].Description); - } - - if (pseq ->seq != NULL) _cmsFree(pseq ->ContextID, pseq ->seq); - _cmsFree(pseq -> ContextID, pseq); -} - -cmsSEQ* CMSEXPORT cmsDupProfileSequenceDescription(const cmsSEQ* pseq) -{ - cmsSEQ *NewSeq; - cmsUInt32Number i; - - if (pseq == NULL) - return NULL; - - NewSeq = (cmsSEQ*) _cmsMalloc(pseq -> ContextID, sizeof(cmsSEQ)); - if (NewSeq == NULL) return NULL; - - - NewSeq -> seq = (cmsPSEQDESC*) _cmsCalloc(pseq ->ContextID, pseq ->n, sizeof(cmsPSEQDESC)); - if (NewSeq ->seq == NULL) goto Error; - - NewSeq -> ContextID = pseq ->ContextID; - NewSeq -> n = pseq ->n; - - for (i=0; i < pseq->n; i++) { - - memmove(&NewSeq ->seq[i].attributes, &pseq ->seq[i].attributes, sizeof(cmsUInt64Number)); - - NewSeq ->seq[i].deviceMfg = pseq ->seq[i].deviceMfg; - NewSeq ->seq[i].deviceModel = pseq ->seq[i].deviceModel; - memmove(&NewSeq ->seq[i].ProfileID, &pseq ->seq[i].ProfileID, sizeof(cmsProfileID)); - NewSeq ->seq[i].technology = pseq ->seq[i].technology; - - NewSeq ->seq[i].Manufacturer = cmsMLUdup(pseq ->seq[i].Manufacturer); - NewSeq ->seq[i].Model = cmsMLUdup(pseq ->seq[i].Model); - NewSeq ->seq[i].Description = cmsMLUdup(pseq ->seq[i].Description); - - } - - return NewSeq; - -Error: - - cmsFreeProfileSequenceDescription(NewSeq); - return NULL; -} - -// Dictionaries -------------------------------------------------------------------------------------------------------- - -// Dictionaries are just very simple linked lists - - -typedef struct _cmsDICT_struct { - cmsDICTentry* head; - cmsContext ContextID; -} _cmsDICT; - - -// Allocate an empty dictionary -cmsHANDLE CMSEXPORT cmsDictAlloc(cmsContext ContextID) -{ - _cmsDICT* dict = (_cmsDICT*) _cmsMallocZero(ContextID, sizeof(_cmsDICT)); - if (dict == NULL) return NULL; - - dict ->ContextID = ContextID; - return (cmsHANDLE) dict; - -} - -// Dispose resources -void CMSEXPORT cmsDictFree(cmsHANDLE hDict) -{ - _cmsDICT* dict = (_cmsDICT*) hDict; - cmsDICTentry *entry, *next; - - _cmsAssert(dict != NULL); - - // Walk the list freeing all nodes - entry = dict ->head; - while (entry != NULL) { - - if (entry ->DisplayName != NULL) cmsMLUfree(entry ->DisplayName); - if (entry ->DisplayValue != NULL) cmsMLUfree(entry ->DisplayValue); - if (entry ->Name != NULL) _cmsFree(dict ->ContextID, entry -> Name); - if (entry ->Value != NULL) _cmsFree(dict ->ContextID, entry -> Value); - - // Don't fall in the habitual trap... - next = entry ->Next; - _cmsFree(dict ->ContextID, entry); - - entry = next; - } - - _cmsFree(dict ->ContextID, dict); -} - - -// Duplicate a wide char string -static -wchar_t* DupWcs(cmsContext ContextID, const wchar_t* ptr) -{ - if (ptr == NULL) return NULL; - return (wchar_t*) _cmsDupMem(ContextID, ptr, (mywcslen(ptr) + 1) * sizeof(wchar_t)); -} - -// Add a new entry to the linked list -cmsBool CMSEXPORT cmsDictAddEntry(cmsHANDLE hDict, const wchar_t* Name, const wchar_t* Value, const cmsMLU *DisplayName, const cmsMLU *DisplayValue) -{ - _cmsDICT* dict = (_cmsDICT*) hDict; - cmsDICTentry *entry; - - _cmsAssert(dict != NULL); - _cmsAssert(Name != NULL); - - entry = (cmsDICTentry*) _cmsMallocZero(dict ->ContextID, sizeof(cmsDICTentry)); - if (entry == NULL) return FALSE; - - entry ->DisplayName = cmsMLUdup(DisplayName); - entry ->DisplayValue = cmsMLUdup(DisplayValue); - entry ->Name = DupWcs(dict ->ContextID, Name); - entry ->Value = DupWcs(dict ->ContextID, Value); - - entry ->Next = dict ->head; - dict ->head = entry; - - return TRUE; -} - - -// Duplicates an existing dictionary -cmsHANDLE CMSEXPORT cmsDictDup(cmsHANDLE hDict) -{ - _cmsDICT* old_dict = (_cmsDICT*) hDict; - cmsHANDLE hNew; - cmsDICTentry *entry; - - _cmsAssert(old_dict != NULL); - - hNew = cmsDictAlloc(old_dict ->ContextID); - if (hNew == NULL) return NULL; - - // Walk the list freeing all nodes - entry = old_dict ->head; - while (entry != NULL) { - - if (!cmsDictAddEntry(hNew, entry ->Name, entry ->Value, entry ->DisplayName, entry ->DisplayValue)) { - - cmsDictFree(hNew); - return NULL; - } - - entry = entry -> Next; - } - - return hNew; -} - -// Get a pointer to the linked list -const cmsDICTentry* CMSEXPORT cmsDictGetEntryList(cmsHANDLE hDict) -{ - _cmsDICT* dict = (_cmsDICT*) hDict; - - if (dict == NULL) return NULL; - return dict ->head; -} - -// Helper For external languages -const cmsDICTentry* CMSEXPORT cmsDictNextEntry(const cmsDICTentry* e) -{ - if (e == NULL) return NULL; - return e ->Next; -} diff --git a/third_party/lcms2-2.6/src/cmsopt.c b/third_party/lcms2-2.6/src/cmsopt.c deleted file mode 100644 index 76de01554c..0000000000 --- a/third_party/lcms2-2.6/src/cmsopt.c +++ /dev/null @@ -1,1811 +0,0 @@ -//--------------------------------------------------------------------------------- -// -// Little Color Management System -// Copyright (c) 1998-2011 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" - - -//---------------------------------------------------------------------------------- - -// Optimization for 8 bits, Shaper-CLUT (3 inputs only) -typedef struct { - - cmsContext ContextID; - - const cmsInterpParams* p; // Tetrahedrical interpolation parameters. This is a not-owned pointer. - - cmsUInt16Number rx[256], ry[256], rz[256]; - cmsUInt32Number X0[256], Y0[256], Z0[256]; // Precomputed nodes and offsets for 8-bit input data - - -} Prelin8Data; - - -// Generic optimization for 16 bits Shaper-CLUT-Shaper (any inputs) -typedef struct { - - cmsContext ContextID; - - // Number of channels - int nInputs; - int nOutputs; - - _cmsInterpFn16 EvalCurveIn16[MAX_INPUT_DIMENSIONS]; // The maximum number of input channels is known in advance - cmsInterpParams* ParamsCurveIn16[MAX_INPUT_DIMENSIONS]; - - _cmsInterpFn16 EvalCLUT; // The evaluator for 3D grid - const cmsInterpParams* CLUTparams; // (not-owned pointer) - - - _cmsInterpFn16* EvalCurveOut16; // Points to an array of curve evaluators in 16 bits (not-owned pointer) - cmsInterpParams** ParamsCurveOut16; // Points to an array of references to interpolation params (not-owned pointer) - - -} Prelin16Data; - - -// Optimization for matrix-shaper in 8 bits. Numbers are operated in n.14 signed, tables are stored in 1.14 fixed - -typedef cmsInt32Number cmsS1Fixed14Number; // Note that this may hold more than 16 bits! - -#define DOUBLE_TO_1FIXED14(x) ((cmsS1Fixed14Number) floor((x) * 16384.0 + 0.5)) - -typedef struct { - - cmsContext ContextID; - - cmsS1Fixed14Number Shaper1R[256]; // from 0..255 to 1.14 (0.0...1.0) - cmsS1Fixed14Number Shaper1G[256]; - cmsS1Fixed14Number Shaper1B[256]; - - cmsS1Fixed14Number Mat[3][3]; // n.14 to n.14 (needs a saturation after that) - cmsS1Fixed14Number Off[3]; - - cmsUInt16Number Shaper2R[16385]; // 1.14 to 0..255 - cmsUInt16Number Shaper2G[16385]; - cmsUInt16Number Shaper2B[16385]; - -} MatShaper8Data; - -// Curves, optimization is shared between 8 and 16 bits -typedef struct { - - cmsContext ContextID; - - int nCurves; // Number of curves - int nElements; // Elements in curves - cmsUInt16Number** Curves; // Points to a dynamically allocated array - -} Curves16Data; - - -// Simple optimizations ---------------------------------------------------------------------------------------------------------- - - -// Remove an element in linked chain -static -void _RemoveElement(cmsStage** head) -{ - cmsStage* mpe = *head; - cmsStage* next = mpe ->Next; - *head = next; - cmsStageFree(mpe); -} - -// Remove all identities in chain. Note that pt actually is a double pointer to the element that holds the pointer. -static -cmsBool _Remove1Op(cmsPipeline* Lut, cmsStageSignature UnaryOp) -{ - cmsStage** pt = &Lut ->Elements; - cmsBool AnyOpt = FALSE; - - while (*pt != NULL) { - - if ((*pt) ->Implements == UnaryOp) { - _RemoveElement(pt); - AnyOpt = TRUE; - } - else - pt = &((*pt) -> Next); - } - - return AnyOpt; -} - -// Same, but only if two adjacent elements are found -static -cmsBool _Remove2Op(cmsPipeline* Lut, cmsStageSignature Op1, cmsStageSignature Op2) -{ - cmsStage** pt1; - cmsStage** pt2; - cmsBool AnyOpt = FALSE; - - pt1 = &Lut ->Elements; - if (*pt1 == NULL) return AnyOpt; - - while (*pt1 != NULL) { - - pt2 = &((*pt1) -> Next); - if (*pt2 == NULL) return AnyOpt; - - if ((*pt1) ->Implements == Op1 && (*pt2) ->Implements == Op2) { - _RemoveElement(pt2); - _RemoveElement(pt1); - AnyOpt = TRUE; - } - else - pt1 = &((*pt1) -> Next); - } - - return AnyOpt; -} - -// Preoptimize just gets rif of no-ops coming paired. Conversion from v2 to v4 followed -// by a v4 to v2 and vice-versa. The elements are then discarded. -static -cmsBool PreOptimize(cmsPipeline* Lut) -{ - cmsBool AnyOpt = FALSE, Opt; - - do { - - Opt = FALSE; - - // Remove all identities - Opt |= _Remove1Op(Lut, cmsSigIdentityElemType); - - // Remove XYZ2Lab followed by Lab2XYZ - Opt |= _Remove2Op(Lut, cmsSigXYZ2LabElemType, cmsSigLab2XYZElemType); - - // Remove Lab2XYZ followed by XYZ2Lab - Opt |= _Remove2Op(Lut, cmsSigLab2XYZElemType, cmsSigXYZ2LabElemType); - - // Remove V4 to V2 followed by V2 to V4 - Opt |= _Remove2Op(Lut, cmsSigLabV4toV2, cmsSigLabV2toV4); - - // Remove V2 to V4 followed by V4 to V2 - Opt |= _Remove2Op(Lut, cmsSigLabV2toV4, cmsSigLabV4toV2); - - // Remove float pcs Lab conversions - Opt |= _Remove2Op(Lut, cmsSigLab2FloatPCS, cmsSigFloatPCS2Lab); - - // Remove float pcs Lab conversions - Opt |= _Remove2Op(Lut, cmsSigXYZ2FloatPCS, cmsSigFloatPCS2XYZ); - - if (Opt) AnyOpt = TRUE; - - } while (Opt); - - return AnyOpt; -} - -static -void Eval16nop1D(register const cmsUInt16Number Input[], - register cmsUInt16Number Output[], - register const struct _cms_interp_struc* p) -{ - Output[0] = Input[0]; - - cmsUNUSED_PARAMETER(p); -} - -static -void PrelinEval16(register const cmsUInt16Number Input[], - register cmsUInt16Number Output[], - register const void* D) -{ - Prelin16Data* p16 = (Prelin16Data*) D; - cmsUInt16Number StageABC[MAX_INPUT_DIMENSIONS]; - cmsUInt16Number StageDEF[cmsMAXCHANNELS]; - int i; - - for (i=0; i < p16 ->nInputs; i++) { - - p16 ->EvalCurveIn16[i](&Input[i], &StageABC[i], p16 ->ParamsCurveIn16[i]); - } - - p16 ->EvalCLUT(StageABC, StageDEF, p16 ->CLUTparams); - - for (i=0; i < p16 ->nOutputs; i++) { - - p16 ->EvalCurveOut16[i](&StageDEF[i], &Output[i], p16 ->ParamsCurveOut16[i]); - } -} - - -static -void PrelinOpt16free(cmsContext ContextID, void* ptr) -{ - Prelin16Data* p16 = (Prelin16Data*) ptr; - - _cmsFree(ContextID, p16 ->EvalCurveOut16); - _cmsFree(ContextID, p16 ->ParamsCurveOut16); - - _cmsFree(ContextID, p16); -} - -static -void* Prelin16dup(cmsContext ContextID, const void* ptr) -{ - Prelin16Data* p16 = (Prelin16Data*) ptr; - Prelin16Data* Duped = _cmsDupMem(ContextID, p16, sizeof(Prelin16Data)); - - if (Duped == NULL) return NULL; - - Duped ->EvalCurveOut16 = (_cmsInterpFn16*)_cmsDupMem(ContextID, p16 ->EvalCurveOut16, p16 ->nOutputs * sizeof(_cmsInterpFn16)); - Duped ->ParamsCurveOut16 = (cmsInterpParams**)_cmsDupMem(ContextID, p16 ->ParamsCurveOut16, p16 ->nOutputs * sizeof(cmsInterpParams* )); - - return Duped; -} - - -static -Prelin16Data* PrelinOpt16alloc(cmsContext ContextID, - const cmsInterpParams* ColorMap, - int nInputs, cmsToneCurve** In, - int nOutputs, cmsToneCurve** Out ) -{ - int i; - Prelin16Data* p16 = _cmsMallocZero(ContextID, sizeof(Prelin16Data)); - if (p16 == NULL) return NULL; - - p16 ->nInputs = nInputs; - p16 -> nOutputs = nOutputs; - - - for (i=0; i < nInputs; i++) { - - if (In == NULL) { - p16 -> ParamsCurveIn16[i] = NULL; - p16 -> EvalCurveIn16[i] = Eval16nop1D; - - } - else { - p16 -> ParamsCurveIn16[i] = In[i] ->InterpParams; - p16 -> EvalCurveIn16[i] = p16 ->ParamsCurveIn16[i]->Interpolation.Lerp16; - } - } - - p16 ->CLUTparams = ColorMap; - p16 ->EvalCLUT = ColorMap ->Interpolation.Lerp16; - - - p16 -> EvalCurveOut16 = (_cmsInterpFn16*) _cmsCalloc(ContextID, nOutputs, sizeof(_cmsInterpFn16)); - p16 -> ParamsCurveOut16 = (cmsInterpParams**) _cmsCalloc(ContextID, nOutputs, sizeof(cmsInterpParams* )); - - for (i=0; i < nOutputs; i++) { - - if (Out == NULL) { - p16 ->ParamsCurveOut16[i] = NULL; - p16 -> EvalCurveOut16[i] = Eval16nop1D; - } - else { - - p16 ->ParamsCurveOut16[i] = Out[i] ->InterpParams; - p16 -> EvalCurveOut16[i] = p16 ->ParamsCurveOut16[i]->Interpolation.Lerp16; - } - } - - return p16; -} - - - -// Resampling --------------------------------------------------------------------------------- - -#define PRELINEARIZATION_POINTS 4096 - -// Sampler implemented by another LUT. This is a clean way to precalculate the devicelink 3D CLUT for -// almost any transform. We use floating point precision and then convert from floating point to 16 bits. -static -int XFormSampler16(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo) -{ - cmsPipeline* Lut = (cmsPipeline*) Cargo; - cmsFloat32Number InFloat[cmsMAXCHANNELS], OutFloat[cmsMAXCHANNELS]; - cmsUInt32Number i; - - _cmsAssert(Lut -> InputChannels < cmsMAXCHANNELS); - _cmsAssert(Lut -> OutputChannels < cmsMAXCHANNELS); - - // From 16 bit to floating point - for (i=0; i < Lut ->InputChannels; i++) - InFloat[i] = (cmsFloat32Number) (In[i] / 65535.0); - - // Evaluate in floating point - cmsPipelineEvalFloat(InFloat, OutFloat, Lut); - - // Back to 16 bits representation - for (i=0; i < Lut ->OutputChannels; i++) - Out[i] = _cmsQuickSaturateWord(OutFloat[i] * 65535.0); - - // Always succeed - return TRUE; -} - -// Try to see if the curves of a given MPE are linear -static -cmsBool AllCurvesAreLinear(cmsStage* mpe) -{ - cmsToneCurve** Curves; - cmsUInt32Number i, n; - - Curves = _cmsStageGetPtrToCurveSet(mpe); - if (Curves == NULL) return FALSE; - - n = cmsStageOutputChannels(mpe); - - for (i=0; i < n; i++) { - if (!cmsIsToneCurveLinear(Curves[i])) return FALSE; - } - - return TRUE; -} - -// This function replaces a specific node placed in "At" by the "Value" numbers. Its purpose -// is to fix scum dot on broken profiles/transforms. Works on 1, 3 and 4 channels -static -cmsBool PatchLUT(cmsStage* CLUT, cmsUInt16Number At[], cmsUInt16Number Value[], - int nChannelsOut, int nChannelsIn) -{ - _cmsStageCLutData* Grid = (_cmsStageCLutData*) CLUT ->Data; - cmsInterpParams* p16 = Grid ->Params; - cmsFloat64Number px, py, pz, pw; - int x0, y0, z0, w0; - int i, index; - - if (CLUT -> Type != cmsSigCLutElemType) { - cmsSignalError(CLUT->ContextID, cmsERROR_INTERNAL, "(internal) Attempt to PatchLUT on non-lut stage"); - return FALSE; - } - - if (nChannelsIn != 1 && nChannelsIn != 3 && nChannelsIn != 4) { - cmsSignalError(CLUT->ContextID, cmsERROR_INTERNAL, "(internal) %d Channels are not supported on PatchLUT", nChannelsIn); - return FALSE; - } - if (nChannelsIn == 4) { - - px = ((cmsFloat64Number) At[0] * (p16->Domain[0])) / 65535.0; - py = ((cmsFloat64Number) At[1] * (p16->Domain[1])) / 65535.0; - pz = ((cmsFloat64Number) At[2] * (p16->Domain[2])) / 65535.0; - pw = ((cmsFloat64Number) At[3] * (p16->Domain[3])) / 65535.0; - - x0 = (int) floor(px); - y0 = (int) floor(py); - z0 = (int) floor(pz); - w0 = (int) floor(pw); - - if (((px - x0) != 0) || - ((py - y0) != 0) || - ((pz - z0) != 0) || - ((pw - w0) != 0)) return FALSE; // Not on exact node - - index = p16 -> opta[3] * x0 + - p16 -> opta[2] * y0 + - p16 -> opta[1] * z0 + - p16 -> opta[0] * w0; - } - else - if (nChannelsIn == 3) { - - px = ((cmsFloat64Number) At[0] * (p16->Domain[0])) / 65535.0; - py = ((cmsFloat64Number) At[1] * (p16->Domain[1])) / 65535.0; - pz = ((cmsFloat64Number) At[2] * (p16->Domain[2])) / 65535.0; - - x0 = (int) floor(px); - y0 = (int) floor(py); - z0 = (int) floor(pz); - - if (((px - x0) != 0) || - ((py - y0) != 0) || - ((pz - z0) != 0)) return FALSE; // Not on exact node - - index = p16 -> opta[2] * x0 + - p16 -> opta[1] * y0 + - p16 -> opta[0] * z0; - } - else - if (nChannelsIn == 1) { - - px = ((cmsFloat64Number) At[0] * (p16->Domain[0])) / 65535.0; - - x0 = (int) floor(px); - - if (((px - x0) != 0)) return FALSE; // Not on exact node - - index = p16 -> opta[0] * x0; - } - else { - cmsSignalError(CLUT->ContextID, cmsERROR_INTERNAL, "(internal) %d Channels are not supported on PatchLUT", nChannelsIn); - return FALSE; - } - - for (i=0; i < nChannelsOut; i++) - Grid -> Tab.T[index + i] = Value[i]; - - return TRUE; -} - -// Auxiliar, to see if two values are equal or very different -static -cmsBool WhitesAreEqual(int n, cmsUInt16Number White1[], cmsUInt16Number White2[] ) -{ - int i; - - for (i=0; i < n; i++) { - - if (abs(White1[i] - White2[i]) > 0xf000) return TRUE; // Values are so extremly different that the fixup should be avoided - if (White1[i] != White2[i]) return FALSE; - } - return TRUE; -} - - -// Locate the node for the white point and fix it to pure white in order to avoid scum dot. -static -cmsBool FixWhiteMisalignment(cmsPipeline* Lut, cmsColorSpaceSignature EntryColorSpace, cmsColorSpaceSignature ExitColorSpace) -{ - cmsUInt16Number *WhitePointIn, *WhitePointOut; - cmsUInt16Number WhiteIn[cmsMAXCHANNELS], WhiteOut[cmsMAXCHANNELS], ObtainedOut[cmsMAXCHANNELS]; - cmsUInt32Number i, nOuts, nIns; - cmsStage *PreLin = NULL, *CLUT = NULL, *PostLin = NULL; - - if (!_cmsEndPointsBySpace(EntryColorSpace, - &WhitePointIn, NULL, &nIns)) return FALSE; - - if (!_cmsEndPointsBySpace(ExitColorSpace, - &WhitePointOut, NULL, &nOuts)) return FALSE; - - // It needs to be fixed? - if (Lut ->InputChannels != nIns) return FALSE; - if (Lut ->OutputChannels != nOuts) return FALSE; - - cmsPipelineEval16(WhitePointIn, ObtainedOut, Lut); - - if (WhitesAreEqual(nOuts, WhitePointOut, ObtainedOut)) return TRUE; // whites already match - - // Check if the LUT comes as Prelin, CLUT or Postlin. We allow all combinations - if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, &PreLin, &CLUT, &PostLin)) - if (!cmsPipelineCheckAndRetreiveStages(Lut, 2, cmsSigCurveSetElemType, cmsSigCLutElemType, &PreLin, &CLUT)) - if (!cmsPipelineCheckAndRetreiveStages(Lut, 2, cmsSigCLutElemType, cmsSigCurveSetElemType, &CLUT, &PostLin)) - if (!cmsPipelineCheckAndRetreiveStages(Lut, 1, cmsSigCLutElemType, &CLUT)) - return FALSE; - - // We need to interpolate white points of both, pre and post curves - if (PreLin) { - - cmsToneCurve** Curves = _cmsStageGetPtrToCurveSet(PreLin); - - for (i=0; i < nIns; i++) { - WhiteIn[i] = cmsEvalToneCurve16(Curves[i], WhitePointIn[i]); - } - } - else { - for (i=0; i < nIns; i++) - WhiteIn[i] = WhitePointIn[i]; - } - - // If any post-linearization, we need to find how is represented white before the curve, do - // a reverse interpolation in this case. - if (PostLin) { - - cmsToneCurve** Curves = _cmsStageGetPtrToCurveSet(PostLin); - - for (i=0; i < nOuts; i++) { - - cmsToneCurve* InversePostLin = cmsReverseToneCurve(Curves[i]); - if (InversePostLin == NULL) { - WhiteOut[i] = WhitePointOut[i]; - - } else { - - WhiteOut[i] = cmsEvalToneCurve16(InversePostLin, WhitePointOut[i]); - cmsFreeToneCurve(InversePostLin); - } - } - } - else { - for (i=0; i < nOuts; i++) - WhiteOut[i] = WhitePointOut[i]; - } - - // Ok, proceed with patching. May fail and we don't care if it fails - PatchLUT(CLUT, WhiteIn, WhiteOut, nOuts, nIns); - - return TRUE; -} - -// ----------------------------------------------------------------------------------------------------------------------------------------------- -// This function creates simple LUT from complex ones. The generated LUT has an optional set of -// prelinearization curves, a CLUT of nGridPoints and optional postlinearization tables. -// These curves have to exist in the original LUT in order to be used in the simplified output. -// Caller may also use the flags to allow this feature. -// LUTS with all curves will be simplified to a single curve. Parametric curves are lost. -// This function should be used on 16-bits LUTS only, as floating point losses precision when simplified -// ----------------------------------------------------------------------------------------------------------------------------------------------- - -static -cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) -{ - cmsPipeline* Src = NULL; - cmsPipeline* Dest = NULL; - cmsStage* mpe; - cmsStage* CLUT; - cmsStage *KeepPreLin = NULL, *KeepPostLin = NULL; - int nGridPoints; - cmsColorSpaceSignature ColorSpace, OutputColorSpace; - cmsStage *NewPreLin = NULL; - cmsStage *NewPostLin = NULL; - _cmsStageCLutData* DataCLUT; - cmsToneCurve** DataSetIn; - cmsToneCurve** DataSetOut; - Prelin16Data* p16; - - // This is a loosy optimization! does not apply in floating-point cases - if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) return FALSE; - - ColorSpace = _cmsICCcolorSpace(T_COLORSPACE(*InputFormat)); - OutputColorSpace = _cmsICCcolorSpace(T_COLORSPACE(*OutputFormat)); - nGridPoints = _cmsReasonableGridpointsByColorspace(ColorSpace, *dwFlags); - - // For empty LUTs, 2 points are enough - if (cmsPipelineStageCount(*Lut) == 0) - nGridPoints = 2; - - Src = *Lut; - - // Named color pipelines cannot be optimized either - for (mpe = cmsPipelineGetPtrToFirstStage(Src); - mpe != NULL; - mpe = cmsStageNext(mpe)) { - if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE; - } - - // Allocate an empty LUT - Dest = cmsPipelineAlloc(Src ->ContextID, Src ->InputChannels, Src ->OutputChannels); - if (!Dest) return FALSE; - - // Prelinearization tables are kept unless indicated by flags - if (*dwFlags & cmsFLAGS_CLUT_PRE_LINEARIZATION) { - - // Get a pointer to the prelinearization element - cmsStage* PreLin = cmsPipelineGetPtrToFirstStage(Src); - - // Check if suitable - if (PreLin ->Type == cmsSigCurveSetElemType) { - - // Maybe this is a linear tram, so we can avoid the whole stuff - if (!AllCurvesAreLinear(PreLin)) { - - // All seems ok, proceed. - NewPreLin = cmsStageDup(PreLin); - if(!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, NewPreLin)) - goto Error; - - // Remove prelinearization. Since we have duplicated the curve - // in destination LUT, the sampling shoud be applied after this stage. - cmsPipelineUnlinkStage(Src, cmsAT_BEGIN, &KeepPreLin); - } - } - } - - // Allocate the CLUT - CLUT = cmsStageAllocCLut16bit(Src ->ContextID, nGridPoints, Src ->InputChannels, Src->OutputChannels, NULL); - if (CLUT == NULL) goto Error; - - // Add the CLUT to the destination LUT - if (!cmsPipelineInsertStage(Dest, cmsAT_END, CLUT)) { - goto Error; - } - - // Postlinearization tables are kept unless indicated by flags - if (*dwFlags & cmsFLAGS_CLUT_POST_LINEARIZATION) { - - // Get a pointer to the postlinearization if present - cmsStage* PostLin = cmsPipelineGetPtrToLastStage(Src); - - // Check if suitable - if (cmsStageType(PostLin) == cmsSigCurveSetElemType) { - - // Maybe this is a linear tram, so we can avoid the whole stuff - if (!AllCurvesAreLinear(PostLin)) { - - // All seems ok, proceed. - NewPostLin = cmsStageDup(PostLin); - if (!cmsPipelineInsertStage(Dest, cmsAT_END, NewPostLin)) - goto Error; - - // In destination LUT, the sampling shoud be applied after this stage. - cmsPipelineUnlinkStage(Src, cmsAT_END, &KeepPostLin); - } - } - } - - // Now its time to do the sampling. We have to ignore pre/post linearization - // The source LUT whithout pre/post curves is passed as parameter. - if (!cmsStageSampleCLut16bit(CLUT, XFormSampler16, (void*) Src, 0)) { -Error: - // Ops, something went wrong, Restore stages - if (KeepPreLin != NULL) { - if (!cmsPipelineInsertStage(Src, cmsAT_BEGIN, KeepPreLin)) { - _cmsAssert(0); // This never happens - } - } - if (KeepPostLin != NULL) { - if (!cmsPipelineInsertStage(Src, cmsAT_END, KeepPostLin)) { - _cmsAssert(0); // This never happens - } - } - cmsPipelineFree(Dest); - return FALSE; - } - - // Done. - - if (KeepPreLin != NULL) cmsStageFree(KeepPreLin); - if (KeepPostLin != NULL) cmsStageFree(KeepPostLin); - cmsPipelineFree(Src); - - DataCLUT = (_cmsStageCLutData*) CLUT ->Data; - - if (NewPreLin == NULL) DataSetIn = NULL; - else DataSetIn = ((_cmsStageToneCurvesData*) NewPreLin ->Data) ->TheCurves; - - if (NewPostLin == NULL) DataSetOut = NULL; - else DataSetOut = ((_cmsStageToneCurvesData*) NewPostLin ->Data) ->TheCurves; - - - if (DataSetIn == NULL && DataSetOut == NULL) { - - _cmsPipelineSetOptimizationParameters(Dest, (_cmsOPTeval16Fn) DataCLUT->Params->Interpolation.Lerp16, DataCLUT->Params, NULL, NULL); - } - else { - - p16 = PrelinOpt16alloc(Dest ->ContextID, - DataCLUT ->Params, - Dest ->InputChannels, - DataSetIn, - Dest ->OutputChannels, - DataSetOut); - - _cmsPipelineSetOptimizationParameters(Dest, PrelinEval16, (void*) p16, PrelinOpt16free, Prelin16dup); - } - - - // Don't fix white on absolute colorimetric - if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) - *dwFlags |= cmsFLAGS_NOWHITEONWHITEFIXUP; - - if (!(*dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP)) { - - FixWhiteMisalignment(Dest, ColorSpace, OutputColorSpace); - } - - *Lut = Dest; - return TRUE; - - cmsUNUSED_PARAMETER(Intent); -} - - -// ----------------------------------------------------------------------------------------------------------------------------------------------- -// Fixes the gamma balancing of transform. This is described in my paper "Prelinearization Stages on -// Color-Management Application-Specific Integrated Circuits (ASICs)" presented at NIP24. It only works -// for RGB transforms. See the paper for more details -// ----------------------------------------------------------------------------------------------------------------------------------------------- - - -// Normalize endpoints by slope limiting max and min. This assures endpoints as well. -// Descending curves are handled as well. -static -void SlopeLimiting(cmsToneCurve* g) -{ - int BeginVal, EndVal; - int AtBegin = (int) floor((cmsFloat64Number) g ->nEntries * 0.02 + 0.5); // Cutoff at 2% - int AtEnd = g ->nEntries - AtBegin - 1; // And 98% - cmsFloat64Number Val, Slope, beta; - int i; - - if (cmsIsToneCurveDescending(g)) { - BeginVal = 0xffff; EndVal = 0; - } - else { - BeginVal = 0; EndVal = 0xffff; - } - - // Compute slope and offset for begin of curve - Val = g ->Table16[AtBegin]; - Slope = (Val - BeginVal) / AtBegin; - beta = Val - Slope * AtBegin; - - for (i=0; i < AtBegin; i++) - g ->Table16[i] = _cmsQuickSaturateWord(i * Slope + beta); - - // Compute slope and offset for the end - Val = g ->Table16[AtEnd]; - Slope = (EndVal - Val) / AtBegin; // AtBegin holds the X interval, which is same in both cases - beta = Val - Slope * AtEnd; - - for (i = AtEnd; i < (int) g ->nEntries; i++) - g ->Table16[i] = _cmsQuickSaturateWord(i * Slope + beta); -} - - -// Precomputes tables for 8-bit on input devicelink. -static -Prelin8Data* PrelinOpt8alloc(cmsContext ContextID, const cmsInterpParams* p, cmsToneCurve* G[3]) -{ - int i; - cmsUInt16Number Input[3]; - cmsS15Fixed16Number v1, v2, v3; - Prelin8Data* p8; - - p8 = (Prelin8Data*)_cmsMallocZero(ContextID, sizeof(Prelin8Data)); - if (p8 == NULL) return NULL; - - // Since this only works for 8 bit input, values comes always as x * 257, - // we can safely take msb byte (x << 8 + x) - - for (i=0; i < 256; i++) { - - if (G != NULL) { - - // Get 16-bit representation - Input[0] = cmsEvalToneCurve16(G[0], FROM_8_TO_16(i)); - Input[1] = cmsEvalToneCurve16(G[1], FROM_8_TO_16(i)); - Input[2] = cmsEvalToneCurve16(G[2], FROM_8_TO_16(i)); - } - else { - Input[0] = FROM_8_TO_16(i); - Input[1] = FROM_8_TO_16(i); - Input[2] = FROM_8_TO_16(i); - } - - - // Move to 0..1.0 in fixed domain - v1 = _cmsToFixedDomain(Input[0] * p -> Domain[0]); - v2 = _cmsToFixedDomain(Input[1] * p -> Domain[1]); - v3 = _cmsToFixedDomain(Input[2] * p -> Domain[2]); - - // Store the precalculated table of nodes - p8 ->X0[i] = (p->opta[2] * FIXED_TO_INT(v1)); - p8 ->Y0[i] = (p->opta[1] * FIXED_TO_INT(v2)); - p8 ->Z0[i] = (p->opta[0] * FIXED_TO_INT(v3)); - - // Store the precalculated table of offsets - p8 ->rx[i] = (cmsUInt16Number) FIXED_REST_TO_INT(v1); - p8 ->ry[i] = (cmsUInt16Number) FIXED_REST_TO_INT(v2); - p8 ->rz[i] = (cmsUInt16Number) FIXED_REST_TO_INT(v3); - } - - p8 ->ContextID = ContextID; - p8 ->p = p; - - return p8; -} - -static -void Prelin8free(cmsContext ContextID, void* ptr) -{ - _cmsFree(ContextID, ptr); -} - -static -void* Prelin8dup(cmsContext ContextID, const void* ptr) -{ - return _cmsDupMem(ContextID, ptr, sizeof(Prelin8Data)); -} - - - -// A optimized interpolation for 8-bit input. -#define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan]) -static -void PrelinEval8(register const cmsUInt16Number Input[], - register cmsUInt16Number Output[], - register const void* D) -{ - - cmsUInt8Number r, g, b; - cmsS15Fixed16Number rx, ry, rz; - cmsS15Fixed16Number c0, c1, c2, c3, Rest; - int OutChan; - register cmsS15Fixed16Number X0, X1, Y0, Y1, Z0, Z1; - Prelin8Data* p8 = (Prelin8Data*) D; - register const cmsInterpParams* p = p8 ->p; - int TotalOut = p -> nOutputs; - const cmsUInt16Number* LutTable = (const cmsUInt16Number*)p -> Table; - - r = Input[0] >> 8; - g = Input[1] >> 8; - b = Input[2] >> 8; - - X0 = X1 = p8->X0[r]; - Y0 = Y1 = p8->Y0[g]; - Z0 = Z1 = p8->Z0[b]; - - rx = p8 ->rx[r]; - ry = p8 ->ry[g]; - rz = p8 ->rz[b]; - - X1 = X0 + ((rx == 0) ? 0 : p ->opta[2]); - Y1 = Y0 + ((ry == 0) ? 0 : p ->opta[1]); - Z1 = Z0 + ((rz == 0) ? 0 : p ->opta[0]); - - - // These are the 6 Tetrahedral - for (OutChan=0; OutChan < TotalOut; OutChan++) { - - c0 = DENS(X0, Y0, Z0); - - if (rx >= ry && ry >= rz) - { - c1 = DENS(X1, Y0, Z0) - c0; - c2 = DENS(X1, Y1, Z0) - DENS(X1, Y0, Z0); - c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); - } - else - if (rx >= rz && rz >= ry) - { - c1 = DENS(X1, Y0, Z0) - c0; - c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); - c3 = DENS(X1, Y0, Z1) - DENS(X1, Y0, Z0); - } - else - if (rz >= rx && rx >= ry) - { - c1 = DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1); - c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1); - c3 = DENS(X0, Y0, Z1) - c0; - } - else - if (ry >= rx && rx >= rz) - { - c1 = DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0); - c2 = DENS(X0, Y1, Z0) - c0; - c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0); - } - else - if (ry >= rz && rz >= rx) - { - c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); - c2 = DENS(X0, Y1, Z0) - c0; - c3 = DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0); - } - else - if (rz >= ry && ry >= rx) - { - c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1); - c2 = DENS(X0, Y1, Z1) - DENS(X0, Y0, Z1); - c3 = DENS(X0, Y0, Z1) - c0; - } - else { - c1 = c2 = c3 = 0; - } - - - Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001; - Output[OutChan] = (cmsUInt16Number)c0 + ((Rest + (Rest>>16))>>16); - - } -} - -#undef DENS - - -// Curves that contain wide empty areas are not optimizeable -static -cmsBool IsDegenerated(const cmsToneCurve* g) -{ - int i, Zeros = 0, Poles = 0; - int nEntries = g ->nEntries; - - for (i=0; i < nEntries; i++) { - - if (g ->Table16[i] == 0x0000) Zeros++; - if (g ->Table16[i] == 0xffff) Poles++; - } - - if (Zeros == 1 && Poles == 1) return FALSE; // For linear tables - if (Zeros > (nEntries / 4)) return TRUE; // Degenerated, mostly zeros - if (Poles > (nEntries / 4)) return TRUE; // Degenerated, mostly poles - - return FALSE; -} - -// -------------------------------------------------------------------------------------------------------------- -// We need xput over here - -static -cmsBool OptimizeByComputingLinearization(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) -{ - cmsPipeline* OriginalLut; - int nGridPoints; - cmsToneCurve *Trans[cmsMAXCHANNELS], *TransReverse[cmsMAXCHANNELS]; - cmsUInt32Number t, i; - cmsFloat32Number v, In[cmsMAXCHANNELS], Out[cmsMAXCHANNELS]; - cmsBool lIsSuitable, lIsLinear; - cmsPipeline* OptimizedLUT = NULL, *LutPlusCurves = NULL; - cmsStage* OptimizedCLUTmpe; - cmsColorSpaceSignature ColorSpace, OutputColorSpace; - cmsStage* OptimizedPrelinMpe; - cmsStage* mpe; - cmsToneCurve** OptimizedPrelinCurves; - _cmsStageCLutData* OptimizedPrelinCLUT; - - - // This is a loosy optimization! does not apply in floating-point cases - if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) return FALSE; - - // Only on RGB - if (T_COLORSPACE(*InputFormat) != PT_RGB) return FALSE; - if (T_COLORSPACE(*OutputFormat) != PT_RGB) return FALSE; - - - // On 16 bits, user has to specify the feature - if (!_cmsFormatterIs8bit(*InputFormat)) { - if (!(*dwFlags & cmsFLAGS_CLUT_PRE_LINEARIZATION)) return FALSE; - } - - OriginalLut = *Lut; - - // Named color pipelines cannot be optimized either - for (mpe = cmsPipelineGetPtrToFirstStage(OriginalLut); - mpe != NULL; - mpe = cmsStageNext(mpe)) { - if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE; - } - - ColorSpace = _cmsICCcolorSpace(T_COLORSPACE(*InputFormat)); - OutputColorSpace = _cmsICCcolorSpace(T_COLORSPACE(*OutputFormat)); - nGridPoints = _cmsReasonableGridpointsByColorspace(ColorSpace, *dwFlags); - - // Empty gamma containers - memset(Trans, 0, sizeof(Trans)); - memset(TransReverse, 0, sizeof(TransReverse)); - - for (t = 0; t < OriginalLut ->InputChannels; t++) { - Trans[t] = cmsBuildTabulatedToneCurve16(OriginalLut ->ContextID, PRELINEARIZATION_POINTS, NULL); - if (Trans[t] == NULL) goto Error; - } - - // Populate the curves - for (i=0; i < PRELINEARIZATION_POINTS; i++) { - - v = (cmsFloat32Number) ((cmsFloat64Number) i / (PRELINEARIZATION_POINTS - 1)); - - // Feed input with a gray ramp - for (t=0; t < OriginalLut ->InputChannels; t++) - In[t] = v; - - // Evaluate the gray value - cmsPipelineEvalFloat(In, Out, OriginalLut); - - // Store result in curve - for (t=0; t < OriginalLut ->InputChannels; t++) - Trans[t] ->Table16[i] = _cmsQuickSaturateWord(Out[t] * 65535.0); - } - - // Slope-limit the obtained curves - for (t = 0; t < OriginalLut ->InputChannels; t++) - SlopeLimiting(Trans[t]); - - // Check for validity - lIsSuitable = TRUE; - lIsLinear = TRUE; - for (t=0; (lIsSuitable && (t < OriginalLut ->InputChannels)); t++) { - - // Exclude if already linear - if (!cmsIsToneCurveLinear(Trans[t])) - lIsLinear = FALSE; - - // Exclude if non-monotonic - if (!cmsIsToneCurveMonotonic(Trans[t])) - lIsSuitable = FALSE; - - if (IsDegenerated(Trans[t])) - lIsSuitable = FALSE; - } - - // If it is not suitable, just quit - if (!lIsSuitable) goto Error; - - // Invert curves if possible - for (t = 0; t < OriginalLut ->InputChannels; t++) { - TransReverse[t] = cmsReverseToneCurveEx(PRELINEARIZATION_POINTS, Trans[t]); - if (TransReverse[t] == NULL) goto Error; - } - - // Now inset the reversed curves at the begin of transform - LutPlusCurves = cmsPipelineDup(OriginalLut); - if (LutPlusCurves == NULL) goto Error; - - if (!cmsPipelineInsertStage(LutPlusCurves, cmsAT_BEGIN, cmsStageAllocToneCurves(OriginalLut ->ContextID, OriginalLut ->InputChannels, TransReverse))) - goto Error; - - // Create the result LUT - OptimizedLUT = cmsPipelineAlloc(OriginalLut ->ContextID, OriginalLut ->InputChannels, OriginalLut ->OutputChannels); - if (OptimizedLUT == NULL) goto Error; - - OptimizedPrelinMpe = cmsStageAllocToneCurves(OriginalLut ->ContextID, OriginalLut ->InputChannels, Trans); - - // Create and insert the curves at the beginning - if (!cmsPipelineInsertStage(OptimizedLUT, cmsAT_BEGIN, OptimizedPrelinMpe)) - goto Error; - - // Allocate the CLUT for result - OptimizedCLUTmpe = cmsStageAllocCLut16bit(OriginalLut ->ContextID, nGridPoints, OriginalLut ->InputChannels, OriginalLut ->OutputChannels, NULL); - - // Add the CLUT to the destination LUT - if (!cmsPipelineInsertStage(OptimizedLUT, cmsAT_END, OptimizedCLUTmpe)) - goto Error; - - // Resample the LUT - if (!cmsStageSampleCLut16bit(OptimizedCLUTmpe, XFormSampler16, (void*) LutPlusCurves, 0)) goto Error; - - // Free resources - for (t = 0; t < OriginalLut ->InputChannels; t++) { - - if (Trans[t]) cmsFreeToneCurve(Trans[t]); - if (TransReverse[t]) cmsFreeToneCurve(TransReverse[t]); - } - - cmsPipelineFree(LutPlusCurves); - - - OptimizedPrelinCurves = _cmsStageGetPtrToCurveSet(OptimizedPrelinMpe); - OptimizedPrelinCLUT = (_cmsStageCLutData*) OptimizedCLUTmpe ->Data; - - // Set the evaluator if 8-bit - if (_cmsFormatterIs8bit(*InputFormat)) { - - Prelin8Data* p8 = PrelinOpt8alloc(OptimizedLUT ->ContextID, - OptimizedPrelinCLUT ->Params, - OptimizedPrelinCurves); - if (p8 == NULL) return FALSE; - - _cmsPipelineSetOptimizationParameters(OptimizedLUT, PrelinEval8, (void*) p8, Prelin8free, Prelin8dup); - - } - else - { - Prelin16Data* p16 = PrelinOpt16alloc(OptimizedLUT ->ContextID, - OptimizedPrelinCLUT ->Params, - 3, OptimizedPrelinCurves, 3, NULL); - if (p16 == NULL) return FALSE; - - _cmsPipelineSetOptimizationParameters(OptimizedLUT, PrelinEval16, (void*) p16, PrelinOpt16free, Prelin16dup); - - } - - // Don't fix white on absolute colorimetric - if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) - *dwFlags |= cmsFLAGS_NOWHITEONWHITEFIXUP; - - if (!(*dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP)) { - - if (!FixWhiteMisalignment(OptimizedLUT, ColorSpace, OutputColorSpace)) { - - return FALSE; - } - } - - // And return the obtained LUT - - cmsPipelineFree(OriginalLut); - *Lut = OptimizedLUT; - return TRUE; - -Error: - - for (t = 0; t < OriginalLut ->InputChannels; t++) { - - if (Trans[t]) cmsFreeToneCurve(Trans[t]); - if (TransReverse[t]) cmsFreeToneCurve(TransReverse[t]); - } - - if (LutPlusCurves != NULL) cmsPipelineFree(LutPlusCurves); - if (OptimizedLUT != NULL) cmsPipelineFree(OptimizedLUT); - - return FALSE; - - cmsUNUSED_PARAMETER(Intent); -} - - -// Curves optimizer ------------------------------------------------------------------------------------------------------------------ - -static -void CurvesFree(cmsContext ContextID, void* ptr) -{ - Curves16Data* Data = (Curves16Data*) ptr; - int i; - - for (i=0; i < Data -> nCurves; i++) { - - _cmsFree(ContextID, Data ->Curves[i]); - } - - _cmsFree(ContextID, Data ->Curves); - _cmsFree(ContextID, ptr); -} - -static -void* CurvesDup(cmsContext ContextID, const void* ptr) -{ - Curves16Data* Data = (Curves16Data*)_cmsDupMem(ContextID, ptr, sizeof(Curves16Data)); - int i; - - if (Data == NULL) return NULL; - - Data ->Curves = (cmsUInt16Number**)_cmsDupMem(ContextID, Data ->Curves, Data ->nCurves * sizeof(cmsUInt16Number*)); - - for (i=0; i < Data -> nCurves; i++) { - Data ->Curves[i] = (cmsUInt16Number*)_cmsDupMem(ContextID, Data ->Curves[i], Data -> nElements * sizeof(cmsUInt16Number)); - } - - return (void*) Data; -} - -// Precomputes tables for 8-bit on input devicelink. -static -Curves16Data* CurvesAlloc(cmsContext ContextID, int nCurves, int nElements, cmsToneCurve** G) -{ - int i, j; - Curves16Data* c16; - - c16 = (Curves16Data*)_cmsMallocZero(ContextID, sizeof(Curves16Data)); - if (c16 == NULL) return NULL; - - c16 ->nCurves = nCurves; - c16 ->nElements = nElements; - - c16 ->Curves = (cmsUInt16Number**)_cmsCalloc(ContextID, nCurves, sizeof(cmsUInt16Number*)); - if (c16 ->Curves == NULL) return NULL; - - for (i=0; i < nCurves; i++) { - - c16->Curves[i] = (cmsUInt16Number*)_cmsCalloc(ContextID, nElements, sizeof(cmsUInt16Number)); - - if (c16->Curves[i] == NULL) { - - for (j=0; j < i; j++) { - _cmsFree(ContextID, c16->Curves[j]); - } - _cmsFree(ContextID, c16->Curves); - _cmsFree(ContextID, c16); - return NULL; - } - - if (nElements == 256) { - - for (j=0; j < nElements; j++) { - - c16 ->Curves[i][j] = cmsEvalToneCurve16(G[i], FROM_8_TO_16(j)); - } - } - else { - - for (j=0; j < nElements; j++) { - c16 ->Curves[i][j] = cmsEvalToneCurve16(G[i], (cmsUInt16Number) j); - } - } - } - - return c16; -} - -static -void FastEvaluateCurves8(register const cmsUInt16Number In[], - register cmsUInt16Number Out[], - register const void* D) -{ - Curves16Data* Data = (Curves16Data*) D; - cmsUInt8Number x; - int i; - - for (i=0; i < Data ->nCurves; i++) { - - x = (In[i] >> 8); - Out[i] = Data -> Curves[i][x]; - } -} - - -static -void FastEvaluateCurves16(register const cmsUInt16Number In[], - register cmsUInt16Number Out[], - register const void* D) -{ - Curves16Data* Data = (Curves16Data*) D; - int i; - - for (i=0; i < Data ->nCurves; i++) { - Out[i] = Data -> Curves[i][In[i]]; - } -} - - -static -void FastIdentity16(register const cmsUInt16Number In[], - register cmsUInt16Number Out[], - register const void* D) -{ - cmsPipeline* Lut = (cmsPipeline*) D; - cmsUInt32Number i; - - for (i=0; i < Lut ->InputChannels; i++) { - Out[i] = In[i]; - } -} - - -// If the target LUT holds only curves, the optimization procedure is to join all those -// curves together. That only works on curves and does not work on matrices. -static -cmsBool OptimizeByJoiningCurves(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) -{ - cmsToneCurve** GammaTables = NULL; - cmsFloat32Number InFloat[cmsMAXCHANNELS], OutFloat[cmsMAXCHANNELS]; - cmsUInt32Number i, j; - cmsPipeline* Src = *Lut; - cmsPipeline* Dest = NULL; - cmsStage* mpe; - cmsStage* ObtainedCurves = NULL; - - - // This is a loosy optimization! does not apply in floating-point cases - if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) return FALSE; - - // Only curves in this LUT? - for (mpe = cmsPipelineGetPtrToFirstStage(Src); - mpe != NULL; - mpe = cmsStageNext(mpe)) { - if (cmsStageType(mpe) != cmsSigCurveSetElemType) return FALSE; - } - - // Allocate an empty LUT - Dest = cmsPipelineAlloc(Src ->ContextID, Src ->InputChannels, Src ->OutputChannels); - if (Dest == NULL) return FALSE; - - // Create target curves - GammaTables = (cmsToneCurve**) _cmsCalloc(Src ->ContextID, Src ->InputChannels, sizeof(cmsToneCurve*)); - if (GammaTables == NULL) goto Error; - - for (i=0; i < Src ->InputChannels; i++) { - GammaTables[i] = cmsBuildTabulatedToneCurve16(Src ->ContextID, PRELINEARIZATION_POINTS, NULL); - if (GammaTables[i] == NULL) goto Error; - } - - // Compute 16 bit result by using floating point - for (i=0; i < PRELINEARIZATION_POINTS; i++) { - - for (j=0; j < Src ->InputChannels; j++) - InFloat[j] = (cmsFloat32Number) ((cmsFloat64Number) i / (PRELINEARIZATION_POINTS - 1)); - - cmsPipelineEvalFloat(InFloat, OutFloat, Src); - - for (j=0; j < Src ->InputChannels; j++) - GammaTables[j] -> Table16[i] = _cmsQuickSaturateWord(OutFloat[j] * 65535.0); - } - - ObtainedCurves = cmsStageAllocToneCurves(Src ->ContextID, Src ->InputChannels, GammaTables); - if (ObtainedCurves == NULL) goto Error; - - for (i=0; i < Src ->InputChannels; i++) { - cmsFreeToneCurve(GammaTables[i]); - GammaTables[i] = NULL; - } - - if (GammaTables != NULL) _cmsFree(Src ->ContextID, GammaTables); - - // Maybe the curves are linear at the end - if (!AllCurvesAreLinear(ObtainedCurves)) { - - if (!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, ObtainedCurves)) - goto Error; - - // If the curves are to be applied in 8 bits, we can save memory - if (_cmsFormatterIs8bit(*InputFormat)) { - - _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) ObtainedCurves ->Data; - Curves16Data* c16 = CurvesAlloc(Dest ->ContextID, Data ->nCurves, 256, Data ->TheCurves); - - if (c16 == NULL) goto Error; - *dwFlags |= cmsFLAGS_NOCACHE; - _cmsPipelineSetOptimizationParameters(Dest, FastEvaluateCurves8, c16, CurvesFree, CurvesDup); - - } - else { - - _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) cmsStageData(ObtainedCurves); - Curves16Data* c16 = CurvesAlloc(Dest ->ContextID, Data ->nCurves, 65536, Data ->TheCurves); - - if (c16 == NULL) goto Error; - *dwFlags |= cmsFLAGS_NOCACHE; - _cmsPipelineSetOptimizationParameters(Dest, FastEvaluateCurves16, c16, CurvesFree, CurvesDup); - } - } - else { - - // LUT optimizes to nothing. Set the identity LUT - cmsStageFree(ObtainedCurves); - - if (!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, cmsStageAllocIdentity(Dest ->ContextID, Src ->InputChannels))) - goto Error; - - *dwFlags |= cmsFLAGS_NOCACHE; - _cmsPipelineSetOptimizationParameters(Dest, FastIdentity16, (void*) Dest, NULL, NULL); - } - - // We are done. - cmsPipelineFree(Src); - *Lut = Dest; - return TRUE; - -Error: - - if (ObtainedCurves != NULL) cmsStageFree(ObtainedCurves); - if (GammaTables != NULL) { - for (i=0; i < Src ->InputChannels; i++) { - if (GammaTables[i] != NULL) cmsFreeToneCurve(GammaTables[i]); - } - - _cmsFree(Src ->ContextID, GammaTables); - } - - if (Dest != NULL) cmsPipelineFree(Dest); - return FALSE; - - cmsUNUSED_PARAMETER(Intent); - cmsUNUSED_PARAMETER(InputFormat); - cmsUNUSED_PARAMETER(OutputFormat); - cmsUNUSED_PARAMETER(dwFlags); -} - -// ------------------------------------------------------------------------------------------------------------------------------------- -// LUT is Shaper - Matrix - Matrix - Shaper, which is very frequent when combining two matrix-shaper profiles - - -static -void FreeMatShaper(cmsContext ContextID, void* Data) -{ - if (Data != NULL) _cmsFree(ContextID, Data); -} - -static -void* DupMatShaper(cmsContext ContextID, const void* Data) -{ - return _cmsDupMem(ContextID, Data, sizeof(MatShaper8Data)); -} - - -// A fast matrix-shaper evaluator for 8 bits. This is a bit ticky since I'm using 1.14 signed fixed point -// to accomplish some performance. Actually it takes 256x3 16 bits tables and 16385 x 3 tables of 8 bits, -// in total about 50K, and the performance boost is huge! -static -void MatShaperEval16(register const cmsUInt16Number In[], - register cmsUInt16Number Out[], - register const void* D) -{ - MatShaper8Data* p = (MatShaper8Data*) D; - cmsS1Fixed14Number l1, l2, l3, r, g, b; - cmsUInt32Number ri, gi, bi; - - // In this case (and only in this case!) we can use this simplification since - // In[] is assured to come from a 8 bit number. (a << 8 | a) - ri = In[0] & 0xFF; - gi = In[1] & 0xFF; - bi = In[2] & 0xFF; - - // Across first shaper, which also converts to 1.14 fixed point - r = p->Shaper1R[ri]; - g = p->Shaper1G[gi]; - b = p->Shaper1B[bi]; - - // Evaluate the matrix in 1.14 fixed point - l1 = (p->Mat[0][0] * r + p->Mat[0][1] * g + p->Mat[0][2] * b + p->Off[0] + 0x2000) >> 14; - l2 = (p->Mat[1][0] * r + p->Mat[1][1] * g + p->Mat[1][2] * b + p->Off[1] + 0x2000) >> 14; - l3 = (p->Mat[2][0] * r + p->Mat[2][1] * g + p->Mat[2][2] * b + p->Off[2] + 0x2000) >> 14; - - // Now we have to clip to 0..1.0 range - ri = (l1 < 0) ? 0 : ((l1 > 16384) ? 16384 : l1); - gi = (l2 < 0) ? 0 : ((l2 > 16384) ? 16384 : l2); - bi = (l3 < 0) ? 0 : ((l3 > 16384) ? 16384 : l3); - - // And across second shaper, - Out[0] = p->Shaper2R[ri]; - Out[1] = p->Shaper2G[gi]; - Out[2] = p->Shaper2B[bi]; - -} - -// This table converts from 8 bits to 1.14 after applying the curve -static -cmsBool FillFirstShaper(cmsS1Fixed14Number* Table, cmsToneCurve* Curve) -{ - int i; - cmsFloat32Number R, y; - - for (i=0; i < 256; i++) { - - R = (cmsFloat32Number) (i / 255.0); - y = cmsEvalToneCurveFloat(Curve, R); - if (isinf(y)) - return FALSE; - - Table[i] = DOUBLE_TO_1FIXED14(y); - } - return TRUE; -} - -// This table converts form 1.14 (being 0x4000 the last entry) to 8 bits after applying the curve -static -cmsBool FillSecondShaper(cmsUInt16Number* Table, cmsToneCurve* Curve, cmsBool Is8BitsOutput) -{ - int i; - cmsFloat32Number R, Val; - - for (i=0; i < 16385; i++) { - - R = (cmsFloat32Number) (i / 16384.0); - Val = cmsEvalToneCurveFloat(Curve, R); // Val comes 0..1.0 - if (isinf(Val)) - return FALSE; - - if (Is8BitsOutput) { - - // If 8 bits output, we can optimize further by computing the / 257 part. - // first we compute the resulting byte and then we store the byte times - // 257. This quantization allows to round very quick by doing a >> 8, but - // since the low byte is always equal to msb, we can do a & 0xff and this works! - cmsUInt16Number w = _cmsQuickSaturateWord(Val * 65535.0); - cmsUInt8Number b = FROM_16_TO_8(w); - - Table[i] = FROM_8_TO_16(b); - } - else Table[i] = _cmsQuickSaturateWord(Val * 65535.0); - } - return TRUE; -} - -// Compute the matrix-shaper structure -static -cmsBool SetMatShaper(cmsPipeline* Dest, cmsToneCurve* Curve1[3], cmsMAT3* Mat, cmsVEC3* Off, cmsToneCurve* Curve2[3], cmsUInt32Number* OutputFormat) -{ - MatShaper8Data* p; - int i, j; - cmsBool Is8Bits = _cmsFormatterIs8bit(*OutputFormat); - - // Allocate a big chuck of memory to store precomputed tables - p = (MatShaper8Data*) _cmsMalloc(Dest ->ContextID, sizeof(MatShaper8Data)); - if (p == NULL) return FALSE; - - p -> ContextID = Dest -> ContextID; - - // Precompute tables - if (!FillFirstShaper(p ->Shaper1R, Curve1[0])) - goto Error; - if (!FillFirstShaper(p ->Shaper1G, Curve1[1])) - goto Error; - if (!FillFirstShaper(p ->Shaper1B, Curve1[2])) - goto Error; - - if (!FillSecondShaper(p ->Shaper2R, Curve2[0], Is8Bits)) - goto Error; - if (!FillSecondShaper(p ->Shaper2G, Curve2[1], Is8Bits)) - goto Error; - if (!FillSecondShaper(p ->Shaper2B, Curve2[2], Is8Bits)) - goto Error; - - // Convert matrix to nFixed14. Note that those values may take more than 16 bits as - for (i=0; i < 3; i++) { - for (j=0; j < 3; j++) { - p ->Mat[i][j] = DOUBLE_TO_1FIXED14(Mat->v[i].n[j]); - } - } - - for (i=0; i < 3; i++) { - - if (Off == NULL) { - p ->Off[i] = 0; - } - else { - p ->Off[i] = DOUBLE_TO_1FIXED14(Off->n[i]); - } - } - - // Mark as optimized for faster formatter - if (Is8Bits) - *OutputFormat |= OPTIMIZED_SH(1); - - // Fill function pointers - _cmsPipelineSetOptimizationParameters(Dest, MatShaperEval16, (void*) p, FreeMatShaper, DupMatShaper); - return TRUE; -Error: - _cmsFree(Dest->ContextID, p); - return FALSE; -} - -// 8 bits on input allows matrix-shaper boot up to 25 Mpixels per second on RGB. That's fast! -// TODO: Allow a third matrix for abs. colorimetric -static -cmsBool OptimizeMatrixShaper(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags) -{ - cmsStage* Curve1, *Curve2; - cmsStage* Matrix1, *Matrix2; - _cmsStageMatrixData* Data1; - _cmsStageMatrixData* Data2; - cmsMAT3 res; - cmsBool IdentityMat; - cmsPipeline* Dest, *Src; - - // Only works on RGB to RGB - if (T_CHANNELS(*InputFormat) != 3 || T_CHANNELS(*OutputFormat) != 3) return FALSE; - - // Only works on 8 bit input - if (!_cmsFormatterIs8bit(*InputFormat)) return FALSE; - - // Seems suitable, proceed - Src = *Lut; - - // Check for shaper-matrix-matrix-shaper structure, that is what this optimizer stands for - if (!cmsPipelineCheckAndRetreiveStages(Src, 4, - cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, - &Curve1, &Matrix1, &Matrix2, &Curve2)) return FALSE; - - // Get both matrices - Data1 = (_cmsStageMatrixData*) cmsStageData(Matrix1); - Data2 = (_cmsStageMatrixData*) cmsStageData(Matrix2); - - // Input offset should be zero - if (Data1 ->Offset != NULL) return FALSE; - - // Multiply both matrices to get the result - _cmsMAT3per(&res, (cmsMAT3*) Data2 ->Double, (cmsMAT3*) Data1 ->Double); - - // Now the result is in res + Data2 -> Offset. Maybe is a plain identity? - IdentityMat = FALSE; - if (_cmsMAT3isIdentity(&res) && Data2 ->Offset == NULL) { - - // We can get rid of full matrix - IdentityMat = TRUE; - } - - // Allocate an empty LUT - Dest = cmsPipelineAlloc(Src ->ContextID, Src ->InputChannels, Src ->OutputChannels); - if (!Dest) return FALSE; - - // Assamble the new LUT - if (!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, cmsStageDup(Curve1))) - goto Error; - - if (!IdentityMat) - if (!cmsPipelineInsertStage(Dest, cmsAT_END, cmsStageAllocMatrix(Dest ->ContextID, 3, 3, (const cmsFloat64Number*) &res, Data2 ->Offset))) - goto Error; - if (!cmsPipelineInsertStage(Dest, cmsAT_END, cmsStageDup(Curve2))) - goto Error; - - // If identity on matrix, we can further optimize the curves, so call the join curves routine - if (IdentityMat) { - - OptimizeByJoiningCurves(&Dest, Intent, InputFormat, OutputFormat, dwFlags); - } - else { - _cmsStageToneCurvesData* mpeC1 = (_cmsStageToneCurvesData*) cmsStageData(Curve1); - _cmsStageToneCurvesData* mpeC2 = (_cmsStageToneCurvesData*) cmsStageData(Curve2); - - // In this particular optimization, cach?does not help as it takes more time to deal with - // the cach?that with the pixel handling - *dwFlags |= cmsFLAGS_NOCACHE; - - // Setup the optimizarion routines - if (!SetMatShaper(Dest, mpeC1 ->TheCurves, &res, (cmsVEC3*) Data2 ->Offset, mpeC2->TheCurves, OutputFormat)) - goto Error; - } - - cmsPipelineFree(Src); - *Lut = Dest; - return TRUE; -Error: - // Leave Src unchanged - cmsPipelineFree(Dest); - return FALSE; -} - - -// ------------------------------------------------------------------------------------------------------------------------------------- -// Optimization plug-ins - -// List of optimizations -typedef struct _cmsOptimizationCollection_st { - - _cmsOPToptimizeFn OptimizePtr; - - struct _cmsOptimizationCollection_st *Next; - -} _cmsOptimizationCollection; - - -// The built-in list. We currently implement 4 types of optimizations. Joining of curves, matrix-shaper, linearization and resampling -static _cmsOptimizationCollection DefaultOptimization[] = { - - { OptimizeByJoiningCurves, &DefaultOptimization[1] }, - { OptimizeMatrixShaper, &DefaultOptimization[2] }, - { OptimizeByComputingLinearization, &DefaultOptimization[3] }, - { OptimizeByResampling, NULL } -}; - -// The linked list head -_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 ContextID, cmsPluginBase* Data) -{ - cmsPluginOptimization* Plugin = (cmsPluginOptimization*) Data; - _cmsOptimizationPluginChunkType* ctx = ( _cmsOptimizationPluginChunkType*) _cmsContextGetClientChunk(ContextID, OptimizationPlugin); - _cmsOptimizationCollection* fl; - - if (Data == NULL) { - - ctx->OptimizationCollection = NULL; - return TRUE; - } - - // Optimizer callback is required - if (Plugin ->OptimizePtr == NULL) return FALSE; - - fl = (_cmsOptimizationCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsOptimizationCollection)); - if (fl == NULL) return FALSE; - - // Copy the parameters - fl ->OptimizePtr = Plugin ->OptimizePtr; - - // Keep linked list - fl ->Next = ctx->OptimizationCollection; - - // Set the head - ctx ->OptimizationCollection = fl; - - // All is ok - return TRUE; -} - -// The entry point for LUT optimization -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; - - // A CLUT is being asked, so force this specific optimization - if (*dwFlags & cmsFLAGS_FORCE_CLUT) { - - PreOptimize(*PtrLut); - return OptimizeByResampling(PtrLut, Intent, InputFormat, OutputFormat, dwFlags); - } - - // Anything to optimize? - if ((*PtrLut) ->Elements == NULL) { - _cmsPipelineSetOptimizationParameters(*PtrLut, FastIdentity16, (void*) *PtrLut, NULL, NULL); - return TRUE; - } - - // Try to get rid of identities and trivial conversions. - AnySuccess = PreOptimize(*PtrLut); - - // After removal do we end with an identity? - if ((*PtrLut) ->Elements == NULL) { - _cmsPipelineSetOptimizationParameters(*PtrLut, FastIdentity16, (void*) *PtrLut, NULL, NULL); - return TRUE; - } - - // Do not optimize, keep all precision - if (*dwFlags & cmsFLAGS_NOOPTIMIZE) - return FALSE; - - // Try plug-in optimizations - for (Opts = ctx->OptimizationCollection; - Opts != NULL; - Opts = Opts ->Next) { - - // If one schema succeeded, we are done - if (Opts ->OptimizePtr(PtrLut, Intent, InputFormat, OutputFormat, dwFlags)) { - - return TRUE; // Optimized! - } - } - - // 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/third_party/lcms2-2.6/src/cmspack.c b/third_party/lcms2-2.6/src/cmspack.c deleted file mode 100644 index 9323b53ec5..0000000000 --- a/third_party/lcms2-2.6/src/cmspack.c +++ /dev/null @@ -1,3369 +0,0 @@ -//--------------------------------------------------------------------------------- -// -// Little Color Management System -// Copyright (c) 1998-2010 Marti Maria Saguer -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the Software -// is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// -//--------------------------------------------------------------------------------- -// -#include "lcms2_internal.h" - -// This module handles all formats supported by lcms. There are two flavors, 16 bits and -// floating point. Floating point is supported only in a subset, those formats holding -// cmsFloat32Number (4 bytes per component) and double (marked as 0 bytes per component -// as special case) - -// --------------------------------------------------------------------------- - - -// This macro return words stored as big endian -#define CHANGE_ENDIAN(w) (cmsUInt16Number) ((cmsUInt16Number) ((w)<<8)|((w)>>8)) - -// These macros handles reversing (negative) -#define REVERSE_FLAVOR_8(x) ((cmsUInt8Number) (0xff-(x))) -#define REVERSE_FLAVOR_16(x) ((cmsUInt16Number)(0xffff-(x))) - -// * 0xffff / 0xff00 = (255 * 257) / (255 * 256) = 257 / 256 -cmsINLINE cmsUInt16Number FomLabV2ToLabV4(cmsUInt16Number x) -{ - int a = (x << 8 | x) >> 8; // * 257 / 256 - if ( a > 0xffff) return 0xffff; - return (cmsUInt16Number) a; -} - -// * 0xf00 / 0xffff = * 256 / 257 -cmsINLINE cmsUInt16Number FomLabV4ToLabV2(cmsUInt16Number x) -{ - return (cmsUInt16Number) (((x << 8) + 0x80) / 257); -} - - -typedef struct { - cmsUInt32Number Type; - cmsUInt32Number Mask; - cmsFormatter16 Frm; - -} cmsFormatters16; - -typedef struct { - cmsUInt32Number Type; - cmsUInt32Number Mask; - cmsFormatterFloat Frm; - -} cmsFormattersFloat; - - -#define ANYSPACE COLORSPACE_SH(31) -#define ANYCHANNELS CHANNELS_SH(15) -#define ANYEXTRA EXTRA_SH(7) -#define ANYPLANAR PLANAR_SH(1) -#define ANYENDIAN ENDIAN16_SH(1) -#define ANYSWAP DOSWAP_SH(1) -#define ANYSWAPFIRST SWAPFIRST_SH(1) -#define ANYFLAVOR FLAVOR_SH(1) - - -// Supress waning about info never being used - -#ifdef _MSC_VER -#pragma warning(disable : 4100) -#endif - -// Unpacking routines (16 bits) ---------------------------------------------------------------------------------------- - - -// Does almost everything but is slow -static -cmsUInt8Number* UnrollChunkyBytes(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - int nChan = T_CHANNELS(info -> InputFormat); - int DoSwap = T_DOSWAP(info ->InputFormat); - int Reverse = T_FLAVOR(info ->InputFormat); - int SwapFirst = T_SWAPFIRST(info -> InputFormat); - int Extra = T_EXTRA(info -> InputFormat); - int ExtraFirst = DoSwap ^ SwapFirst; - cmsUInt16Number v; - int i; - - if (ExtraFirst) { - accum += Extra; - } - - for (i=0; i < nChan; i++) { - int index = DoSwap ? (nChan - i - 1) : i; - - v = FROM_8_TO_16(*accum); - v = Reverse ? REVERSE_FLAVOR_16(v) : v; - wIn[index] = v; - accum++; - } - - if (!ExtraFirst) { - accum += Extra; - } - - if (Extra == 0 && SwapFirst) { - cmsUInt16Number tmp = wIn[0]; - - memmove(&wIn[0], &wIn[1], (nChan-1) * sizeof(cmsUInt16Number)); - wIn[nChan-1] = tmp; - } - - return accum; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); - -} - -// Extra channels are just ignored because come in the next planes -static -cmsUInt8Number* UnrollPlanarBytes(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - int nChan = T_CHANNELS(info -> InputFormat); - int DoSwap = T_DOSWAP(info ->InputFormat); - int SwapFirst = T_SWAPFIRST(info ->InputFormat); - int Reverse = T_FLAVOR(info ->InputFormat); - int i; - cmsUInt8Number* Init = accum; - - if (DoSwap ^ SwapFirst) { - accum += T_EXTRA(info -> InputFormat) * Stride; - } - - for (i=0; i < nChan; i++) { - - int index = DoSwap ? (nChan - i - 1) : i; - cmsUInt16Number v = FROM_8_TO_16(*accum); - - wIn[index] = Reverse ? REVERSE_FLAVOR_16(v) : v; - accum += Stride; - } - - return (Init + 1); -} - -// Special cases, provided for performance -static -cmsUInt8Number* Unroll4Bytes(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - wIn[0] = FROM_8_TO_16(*accum); accum++; // C - wIn[1] = FROM_8_TO_16(*accum); accum++; // M - wIn[2] = FROM_8_TO_16(*accum); accum++; // Y - wIn[3] = FROM_8_TO_16(*accum); accum++; // K - - return accum; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Unroll4BytesReverse(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - wIn[0] = FROM_8_TO_16(REVERSE_FLAVOR_8(*accum)); accum++; // C - wIn[1] = FROM_8_TO_16(REVERSE_FLAVOR_8(*accum)); accum++; // M - wIn[2] = FROM_8_TO_16(REVERSE_FLAVOR_8(*accum)); accum++; // Y - wIn[3] = FROM_8_TO_16(REVERSE_FLAVOR_8(*accum)); accum++; // K - - return accum; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Unroll4BytesSwapFirst(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - wIn[3] = FROM_8_TO_16(*accum); accum++; // K - wIn[0] = FROM_8_TO_16(*accum); accum++; // C - wIn[1] = FROM_8_TO_16(*accum); accum++; // M - wIn[2] = FROM_8_TO_16(*accum); accum++; // Y - - return accum; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -// KYMC -static -cmsUInt8Number* Unroll4BytesSwap(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - wIn[3] = FROM_8_TO_16(*accum); accum++; // K - wIn[2] = FROM_8_TO_16(*accum); accum++; // Y - wIn[1] = FROM_8_TO_16(*accum); accum++; // M - wIn[0] = FROM_8_TO_16(*accum); accum++; // C - - return accum; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Unroll4BytesSwapSwapFirst(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - wIn[2] = FROM_8_TO_16(*accum); accum++; // K - wIn[1] = FROM_8_TO_16(*accum); accum++; // Y - wIn[0] = FROM_8_TO_16(*accum); accum++; // M - wIn[3] = FROM_8_TO_16(*accum); accum++; // C - - return accum; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Unroll3Bytes(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - wIn[0] = FROM_8_TO_16(*accum); accum++; // R - wIn[1] = FROM_8_TO_16(*accum); accum++; // G - wIn[2] = FROM_8_TO_16(*accum); accum++; // B - - return accum; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Unroll3BytesSkip1Swap(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - accum++; // A - wIn[2] = FROM_8_TO_16(*accum); accum++; // B - wIn[1] = FROM_8_TO_16(*accum); accum++; // G - wIn[0] = FROM_8_TO_16(*accum); accum++; // R - - return accum; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Unroll3BytesSkip1SwapSwapFirst(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - wIn[2] = FROM_8_TO_16(*accum); accum++; // B - wIn[1] = FROM_8_TO_16(*accum); accum++; // G - wIn[0] = FROM_8_TO_16(*accum); accum++; // R - accum++; // A - - return accum; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Unroll3BytesSkip1SwapFirst(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - accum++; // A - wIn[0] = FROM_8_TO_16(*accum); accum++; // R - wIn[1] = FROM_8_TO_16(*accum); accum++; // G - wIn[2] = FROM_8_TO_16(*accum); accum++; // B - - return accum; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - - -// BRG -static -cmsUInt8Number* Unroll3BytesSwap(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - wIn[2] = FROM_8_TO_16(*accum); accum++; // B - wIn[1] = FROM_8_TO_16(*accum); accum++; // G - wIn[0] = FROM_8_TO_16(*accum); accum++; // R - - return accum; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* UnrollLabV2_8(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - wIn[0] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++; // L - wIn[1] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++; // a - wIn[2] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++; // b - - return accum; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* UnrollALabV2_8(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - accum++; // A - wIn[0] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++; // L - wIn[1] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++; // a - wIn[2] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++; // b - - return accum; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* UnrollLabV2_16(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - wIn[0] = FomLabV2ToLabV4(*(cmsUInt16Number*) accum); accum += 2; // L - wIn[1] = FomLabV2ToLabV4(*(cmsUInt16Number*) accum); accum += 2; // a - wIn[2] = FomLabV2ToLabV4(*(cmsUInt16Number*) accum); accum += 2; // b - - return accum; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -// for duplex -static -cmsUInt8Number* Unroll2Bytes(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - wIn[0] = FROM_8_TO_16(*accum); accum++; // ch1 - wIn[1] = FROM_8_TO_16(*accum); accum++; // ch2 - - return accum; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - - - - -// Monochrome duplicates L into RGB for null-transforms -static -cmsUInt8Number* Unroll1Byte(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - wIn[0] = wIn[1] = wIn[2] = FROM_8_TO_16(*accum); accum++; // L - - return accum; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - - -static -cmsUInt8Number* Unroll1ByteSkip1(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - wIn[0] = wIn[1] = wIn[2] = FROM_8_TO_16(*accum); accum++; // L - accum += 1; - - return accum; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Unroll1ByteSkip2(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - wIn[0] = wIn[1] = wIn[2] = FROM_8_TO_16(*accum); accum++; // L - accum += 2; - - return accum; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Unroll1ByteReversed(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - wIn[0] = wIn[1] = wIn[2] = REVERSE_FLAVOR_16(FROM_8_TO_16(*accum)); accum++; // L - - return accum; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - - -static -cmsUInt8Number* UnrollAnyWords(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - int nChan = T_CHANNELS(info -> InputFormat); - int SwapEndian = T_ENDIAN16(info -> InputFormat); - int DoSwap = T_DOSWAP(info ->InputFormat); - int Reverse = T_FLAVOR(info ->InputFormat); - int SwapFirst = T_SWAPFIRST(info -> InputFormat); - int Extra = T_EXTRA(info -> InputFormat); - int ExtraFirst = DoSwap ^ SwapFirst; - int i; - - if (ExtraFirst) { - accum += Extra * sizeof(cmsUInt16Number); - } - - for (i=0; i < nChan; i++) { - - int index = DoSwap ? (nChan - i - 1) : i; - cmsUInt16Number v = *(cmsUInt16Number*) accum; - - if (SwapEndian) - v = CHANGE_ENDIAN(v); - - wIn[index] = Reverse ? REVERSE_FLAVOR_16(v) : v; - - accum += sizeof(cmsUInt16Number); - } - - if (!ExtraFirst) { - accum += Extra * sizeof(cmsUInt16Number); - } - - if (Extra == 0 && SwapFirst) { - - cmsUInt16Number tmp = wIn[0]; - - memmove(&wIn[0], &wIn[1], (nChan-1) * sizeof(cmsUInt16Number)); - wIn[nChan-1] = tmp; - } - - return accum; - - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* UnrollPlanarWords(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - int nChan = T_CHANNELS(info -> InputFormat); - int DoSwap= T_DOSWAP(info ->InputFormat); - int Reverse= T_FLAVOR(info ->InputFormat); - int SwapEndian = T_ENDIAN16(info -> InputFormat); - int i; - cmsUInt8Number* Init = accum; - - if (DoSwap) { - accum += T_EXTRA(info -> InputFormat) * Stride * sizeof(cmsUInt16Number); - } - - for (i=0; i < nChan; i++) { - - int index = DoSwap ? (nChan - i - 1) : i; - cmsUInt16Number v = *(cmsUInt16Number*) accum; - - if (SwapEndian) - v = CHANGE_ENDIAN(v); - - wIn[index] = Reverse ? REVERSE_FLAVOR_16(v) : v; - - accum += Stride * sizeof(cmsUInt16Number); - } - - return (Init + sizeof(cmsUInt16Number)); -} - - -static -cmsUInt8Number* Unroll4Words(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - wIn[0] = *(cmsUInt16Number*) accum; accum+= 2; // C - wIn[1] = *(cmsUInt16Number*) accum; accum+= 2; // M - wIn[2] = *(cmsUInt16Number*) accum; accum+= 2; // Y - wIn[3] = *(cmsUInt16Number*) accum; accum+= 2; // K - - return accum; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Unroll4WordsReverse(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - wIn[0] = REVERSE_FLAVOR_16(*(cmsUInt16Number*) accum); accum+= 2; // C - wIn[1] = REVERSE_FLAVOR_16(*(cmsUInt16Number*) accum); accum+= 2; // M - wIn[2] = REVERSE_FLAVOR_16(*(cmsUInt16Number*) accum); accum+= 2; // Y - wIn[3] = REVERSE_FLAVOR_16(*(cmsUInt16Number*) accum); accum+= 2; // K - - return accum; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Unroll4WordsSwapFirst(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - wIn[3] = *(cmsUInt16Number*) accum; accum+= 2; // K - wIn[0] = *(cmsUInt16Number*) accum; accum+= 2; // C - wIn[1] = *(cmsUInt16Number*) accum; accum+= 2; // M - wIn[2] = *(cmsUInt16Number*) accum; accum+= 2; // Y - - return accum; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -// KYMC -static -cmsUInt8Number* Unroll4WordsSwap(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - wIn[3] = *(cmsUInt16Number*) accum; accum+= 2; // K - wIn[2] = *(cmsUInt16Number*) accum; accum+= 2; // Y - wIn[1] = *(cmsUInt16Number*) accum; accum+= 2; // M - wIn[0] = *(cmsUInt16Number*) accum; accum+= 2; // C - - return accum; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Unroll4WordsSwapSwapFirst(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - wIn[2] = *(cmsUInt16Number*) accum; accum+= 2; // K - wIn[1] = *(cmsUInt16Number*) accum; accum+= 2; // Y - wIn[0] = *(cmsUInt16Number*) accum; accum+= 2; // M - wIn[3] = *(cmsUInt16Number*) accum; accum+= 2; // C - - return accum; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Unroll3Words(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - wIn[0] = *(cmsUInt16Number*) accum; accum+= 2; // C R - wIn[1] = *(cmsUInt16Number*) accum; accum+= 2; // M G - wIn[2] = *(cmsUInt16Number*) accum; accum+= 2; // Y B - - return accum; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Unroll3WordsSwap(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - wIn[2] = *(cmsUInt16Number*) accum; accum+= 2; // C R - wIn[1] = *(cmsUInt16Number*) accum; accum+= 2; // M G - wIn[0] = *(cmsUInt16Number*) accum; accum+= 2; // Y B - - return accum; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Unroll3WordsSkip1Swap(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - accum += 2; // A - wIn[2] = *(cmsUInt16Number*) accum; accum += 2; // R - wIn[1] = *(cmsUInt16Number*) accum; accum += 2; // G - wIn[0] = *(cmsUInt16Number*) accum; accum += 2; // B - - return accum; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Unroll3WordsSkip1SwapFirst(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - accum += 2; // A - wIn[0] = *(cmsUInt16Number*) accum; accum += 2; // R - wIn[1] = *(cmsUInt16Number*) accum; accum += 2; // G - wIn[2] = *(cmsUInt16Number*) accum; accum += 2; // B - - return accum; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Unroll1Word(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - wIn[0] = wIn[1] = wIn[2] = *(cmsUInt16Number*) accum; accum+= 2; // L - - return accum; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Unroll1WordReversed(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - wIn[0] = wIn[1] = wIn[2] = REVERSE_FLAVOR_16(*(cmsUInt16Number*) accum); accum+= 2; - - return accum; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Unroll1WordSkip3(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - wIn[0] = wIn[1] = wIn[2] = *(cmsUInt16Number*) accum; - - accum += 8; - - return accum; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Unroll2Words(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - wIn[0] = *(cmsUInt16Number*) accum; accum += 2; // ch1 - wIn[1] = *(cmsUInt16Number*) accum; accum += 2; // ch2 - - return accum; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - - -// This is a conversion of Lab double to 16 bits -static -cmsUInt8Number* UnrollLabDoubleTo16(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - if (T_PLANAR(info -> InputFormat)) { - - cmsFloat64Number* Pt = (cmsFloat64Number*) accum; - - cmsCIELab Lab; - - Lab.L = Pt[0]; - Lab.a = Pt[Stride]; - Lab.b = Pt[Stride*2]; - - cmsFloat2LabEncoded(wIn, &Lab); - return accum + sizeof(cmsFloat64Number); - } - else { - - cmsFloat2LabEncoded(wIn, (cmsCIELab*) accum); - accum += sizeof(cmsCIELab) + T_EXTRA(info ->InputFormat) * sizeof(cmsFloat64Number); - return accum; - } -} - - -// This is a conversion of Lab float to 16 bits -static -cmsUInt8Number* UnrollLabFloatTo16(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - cmsCIELab Lab; - - if (T_PLANAR(info -> InputFormat)) { - - cmsFloat32Number* Pt = (cmsFloat32Number*) accum; - - - Lab.L = Pt[0]; - Lab.a = Pt[Stride]; - Lab.b = Pt[Stride*2]; - - cmsFloat2LabEncoded(wIn, &Lab); - return accum + sizeof(cmsFloat32Number); - } - else { - - Lab.L = ((cmsFloat32Number*) accum)[0]; - Lab.a = ((cmsFloat32Number*) accum)[1]; - Lab.b = ((cmsFloat32Number*) accum)[2]; - - cmsFloat2LabEncoded(wIn, &Lab); - accum += (3 + T_EXTRA(info ->InputFormat)) * sizeof(cmsFloat32Number); - return accum; - } -} - -// This is a conversion of XYZ double to 16 bits -static -cmsUInt8Number* UnrollXYZDoubleTo16(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - if (T_PLANAR(info -> InputFormat)) { - - cmsFloat64Number* Pt = (cmsFloat64Number*) accum; - cmsCIEXYZ XYZ; - - XYZ.X = Pt[0]; - XYZ.Y = Pt[Stride]; - XYZ.Z = Pt[Stride*2]; - cmsFloat2XYZEncoded(wIn, &XYZ); - - return accum + sizeof(cmsFloat64Number); - - } - - else { - cmsFloat2XYZEncoded(wIn, (cmsCIEXYZ*) accum); - accum += sizeof(cmsCIEXYZ) + T_EXTRA(info ->InputFormat) * sizeof(cmsFloat64Number); - - return accum; - } -} - -// This is a conversion of XYZ float to 16 bits -static -cmsUInt8Number* UnrollXYZFloatTo16(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - if (T_PLANAR(info -> InputFormat)) { - - cmsFloat32Number* Pt = (cmsFloat32Number*) accum; - cmsCIEXYZ XYZ; - - XYZ.X = Pt[0]; - XYZ.Y = Pt[Stride]; - XYZ.Z = Pt[Stride*2]; - cmsFloat2XYZEncoded(wIn, &XYZ); - - return accum + sizeof(cmsFloat32Number); - - } - - else { - cmsFloat32Number* Pt = (cmsFloat32Number*) accum; - cmsCIEXYZ XYZ; - - XYZ.X = Pt[0]; - XYZ.Y = Pt[1]; - XYZ.Z = Pt[2]; - cmsFloat2XYZEncoded(wIn, &XYZ); - - accum += 3 * sizeof(cmsFloat32Number) + T_EXTRA(info ->InputFormat) * sizeof(cmsFloat32Number); - - return accum; - } -} - -// Check if space is marked as ink -cmsINLINE cmsBool IsInkSpace(cmsUInt32Number Type) -{ - switch (T_COLORSPACE(Type)) { - - case PT_CMY: - case PT_CMYK: - case PT_MCH5: - case PT_MCH6: - case PT_MCH7: - case PT_MCH8: - case PT_MCH9: - case PT_MCH10: - case PT_MCH11: - case PT_MCH12: - case PT_MCH13: - case PT_MCH14: - case PT_MCH15: return TRUE; - - default: return FALSE; - } -} - -// Inks does come in percentage, remaining cases are between 0..1.0, again to 16 bits -static -cmsUInt8Number* UnrollDoubleTo16(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - - int nChan = T_CHANNELS(info -> InputFormat); - int DoSwap = T_DOSWAP(info ->InputFormat); - int Reverse = T_FLAVOR(info ->InputFormat); - int SwapFirst = T_SWAPFIRST(info -> InputFormat); - int Extra = T_EXTRA(info -> InputFormat); - int ExtraFirst = DoSwap ^ SwapFirst; - int Planar = T_PLANAR(info -> InputFormat); - cmsFloat64Number v; - cmsUInt16Number vi; - int i, start = 0; - cmsFloat64Number maximum = IsInkSpace(info ->InputFormat) ? 655.35 : 65535.0; - - - if (ExtraFirst) - start = Extra; - - for (i=0; i < nChan; i++) { - - int index = DoSwap ? (nChan - i - 1) : i; - - if (Planar) - v = (cmsFloat32Number) ((cmsFloat64Number*) accum)[(i + start) * Stride]; - else - v = (cmsFloat32Number) ((cmsFloat64Number*) accum)[i + start]; - - vi = _cmsQuickSaturateWord(v * maximum); - - if (Reverse) - vi = REVERSE_FLAVOR_16(vi); - - wIn[index] = vi; - } - - - if (Extra == 0 && SwapFirst) { - cmsUInt16Number tmp = wIn[0]; - - memmove(&wIn[0], &wIn[1], (nChan-1) * sizeof(cmsUInt16Number)); - wIn[nChan-1] = tmp; - } - - if (T_PLANAR(info -> InputFormat)) - return accum + sizeof(cmsFloat64Number); - else - return accum + (nChan + Extra) * sizeof(cmsFloat64Number); -} - - - -static -cmsUInt8Number* UnrollFloatTo16(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - - int nChan = T_CHANNELS(info -> InputFormat); - int DoSwap = T_DOSWAP(info ->InputFormat); - int Reverse = T_FLAVOR(info ->InputFormat); - int SwapFirst = T_SWAPFIRST(info -> InputFormat); - int Extra = T_EXTRA(info -> InputFormat); - int ExtraFirst = DoSwap ^ SwapFirst; - int Planar = T_PLANAR(info -> InputFormat); - cmsFloat32Number v; - cmsUInt16Number vi; - int i, start = 0; - cmsFloat64Number maximum = IsInkSpace(info ->InputFormat) ? 655.35 : 65535.0; - - - if (ExtraFirst) - start = Extra; - - for (i=0; i < nChan; i++) { - - int index = DoSwap ? (nChan - i - 1) : i; - - if (Planar) - v = (cmsFloat32Number) ((cmsFloat32Number*) accum)[(i + start) * Stride]; - else - v = (cmsFloat32Number) ((cmsFloat32Number*) accum)[i + start]; - - vi = _cmsQuickSaturateWord(v * maximum); - - if (Reverse) - vi = REVERSE_FLAVOR_16(vi); - - wIn[index] = vi; - } - - - if (Extra == 0 && SwapFirst) { - cmsUInt16Number tmp = wIn[0]; - - memmove(&wIn[0], &wIn[1], (nChan-1) * sizeof(cmsUInt16Number)); - wIn[nChan-1] = tmp; - } - - if (T_PLANAR(info -> InputFormat)) - return accum + sizeof(cmsFloat32Number); - else - return accum + (nChan + Extra) * sizeof(cmsFloat32Number); -} - - - - -// For 1 channel, we need to duplicate data (it comes in 0..1.0 range) -static -cmsUInt8Number* UnrollDouble1Chan(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - cmsFloat64Number* Inks = (cmsFloat64Number*) accum; - - wIn[0] = wIn[1] = wIn[2] = _cmsQuickSaturateWord(Inks[0] * 65535.0); - - return accum + sizeof(cmsFloat64Number); - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -//------------------------------------------------------------------------------------------------------------------- - -// For anything going from cmsFloat32Number -static -cmsUInt8Number* UnrollFloatsToFloat(_cmsTRANSFORM* info, - cmsFloat32Number wIn[], - cmsUInt8Number* accum, - cmsUInt32Number Stride) -{ - - int nChan = T_CHANNELS(info -> InputFormat); - int DoSwap = T_DOSWAP(info ->InputFormat); - int Reverse = T_FLAVOR(info ->InputFormat); - int SwapFirst = T_SWAPFIRST(info -> InputFormat); - int Extra = T_EXTRA(info -> InputFormat); - int ExtraFirst = DoSwap ^ SwapFirst; - int Planar = T_PLANAR(info -> InputFormat); - cmsFloat32Number v; - int i, start = 0; - cmsFloat32Number maximum = IsInkSpace(info ->InputFormat) ? 100.0F : 1.0F; - - - if (ExtraFirst) - start = Extra; - - for (i=0; i < nChan; i++) { - - int index = DoSwap ? (nChan - i - 1) : i; - - if (Planar) - v = (cmsFloat32Number) ((cmsFloat32Number*) accum)[(i + start) * Stride]; - else - v = (cmsFloat32Number) ((cmsFloat32Number*) accum)[i + start]; - - v /= maximum; - - wIn[index] = Reverse ? 1 - v : v; - } - - - if (Extra == 0 && SwapFirst) { - cmsFloat32Number tmp = wIn[0]; - - memmove(&wIn[0], &wIn[1], (nChan-1) * sizeof(cmsFloat32Number)); - wIn[nChan-1] = tmp; - } - - if (T_PLANAR(info -> InputFormat)) - return accum + sizeof(cmsFloat32Number); - else - return accum + (nChan + Extra) * sizeof(cmsFloat32Number); -} - -// For anything going from double - -static -cmsUInt8Number* UnrollDoublesToFloat(_cmsTRANSFORM* info, - cmsFloat32Number wIn[], - cmsUInt8Number* accum, - cmsUInt32Number Stride) -{ - - int nChan = T_CHANNELS(info -> InputFormat); - int DoSwap = T_DOSWAP(info ->InputFormat); - int Reverse = T_FLAVOR(info ->InputFormat); - int SwapFirst = T_SWAPFIRST(info -> InputFormat); - int Extra = T_EXTRA(info -> InputFormat); - int ExtraFirst = DoSwap ^ SwapFirst; - int Planar = T_PLANAR(info -> InputFormat); - cmsFloat64Number v; - int i, start = 0; - cmsFloat64Number maximum = IsInkSpace(info ->InputFormat) ? 100.0 : 1.0; - - - if (ExtraFirst) - start = Extra; - - for (i=0; i < nChan; i++) { - - int index = DoSwap ? (nChan - i - 1) : i; - - if (Planar) - v = (cmsFloat64Number) ((cmsFloat64Number*) accum)[(i + start) * Stride]; - else - v = (cmsFloat64Number) ((cmsFloat64Number*) accum)[i + start]; - - v /= maximum; - - wIn[index] = (cmsFloat32Number) (Reverse ? 1.0 - v : v); - } - - - if (Extra == 0 && SwapFirst) { - cmsFloat32Number tmp = wIn[0]; - - memmove(&wIn[0], &wIn[1], (nChan-1) * sizeof(cmsFloat32Number)); - wIn[nChan-1] = tmp; - } - - if (T_PLANAR(info -> InputFormat)) - return accum + sizeof(cmsFloat64Number); - else - return accum + (nChan + Extra) * sizeof(cmsFloat64Number); -} - - - -// From Lab double to cmsFloat32Number -static -cmsUInt8Number* UnrollLabDoubleToFloat(_cmsTRANSFORM* info, - cmsFloat32Number wIn[], - cmsUInt8Number* accum, - cmsUInt32Number Stride) -{ - cmsFloat64Number* Pt = (cmsFloat64Number*) accum; - - if (T_PLANAR(info -> InputFormat)) { - - wIn[0] = (cmsFloat32Number) (Pt[0] / 100.0); // from 0..100 to 0..1 - wIn[1] = (cmsFloat32Number) ((Pt[Stride] + 128) / 255.0); // form -128..+127 to 0..1 - wIn[2] = (cmsFloat32Number) ((Pt[Stride*2] + 128) / 255.0); - - return accum + sizeof(cmsFloat64Number); - } - else { - - wIn[0] = (cmsFloat32Number) (Pt[0] / 100.0); // from 0..100 to 0..1 - wIn[1] = (cmsFloat32Number) ((Pt[1] + 128) / 255.0); // form -128..+127 to 0..1 - wIn[2] = (cmsFloat32Number) ((Pt[2] + 128) / 255.0); - - accum += sizeof(cmsFloat64Number)*(3 + T_EXTRA(info ->InputFormat)); - return accum; - } -} - -// From Lab double to cmsFloat32Number -static -cmsUInt8Number* UnrollLabFloatToFloat(_cmsTRANSFORM* info, - cmsFloat32Number wIn[], - cmsUInt8Number* accum, - cmsUInt32Number Stride) -{ - cmsFloat32Number* Pt = (cmsFloat32Number*) accum; - - if (T_PLANAR(info -> InputFormat)) { - - wIn[0] = (cmsFloat32Number) (Pt[0] / 100.0); // from 0..100 to 0..1 - wIn[1] = (cmsFloat32Number) ((Pt[Stride] + 128) / 255.0); // form -128..+127 to 0..1 - wIn[2] = (cmsFloat32Number) ((Pt[Stride*2] + 128) / 255.0); - - return accum + sizeof(cmsFloat32Number); - } - else { - - wIn[0] = (cmsFloat32Number) (Pt[0] / 100.0); // from 0..100 to 0..1 - wIn[1] = (cmsFloat32Number) ((Pt[1] + 128) / 255.0); // form -128..+127 to 0..1 - wIn[2] = (cmsFloat32Number) ((Pt[2] + 128) / 255.0); - - accum += sizeof(cmsFloat32Number)*(3 + T_EXTRA(info ->InputFormat)); - return accum; - } -} - - - -// 1.15 fixed point, that means maximum value is MAX_ENCODEABLE_XYZ (0xFFFF) -static -cmsUInt8Number* UnrollXYZDoubleToFloat(_cmsTRANSFORM* info, - cmsFloat32Number wIn[], - cmsUInt8Number* accum, - cmsUInt32Number Stride) -{ - cmsFloat64Number* Pt = (cmsFloat64Number*) accum; - - if (T_PLANAR(info -> InputFormat)) { - - wIn[0] = (cmsFloat32Number) (Pt[0] / MAX_ENCODEABLE_XYZ); - wIn[1] = (cmsFloat32Number) (Pt[Stride] / MAX_ENCODEABLE_XYZ); - wIn[2] = (cmsFloat32Number) (Pt[Stride*2] / MAX_ENCODEABLE_XYZ); - - return accum + sizeof(cmsFloat64Number); - } - else { - - wIn[0] = (cmsFloat32Number) (Pt[0] / MAX_ENCODEABLE_XYZ); - wIn[1] = (cmsFloat32Number) (Pt[1] / MAX_ENCODEABLE_XYZ); - wIn[2] = (cmsFloat32Number) (Pt[2] / MAX_ENCODEABLE_XYZ); - - accum += sizeof(cmsFloat64Number)*(3 + T_EXTRA(info ->InputFormat)); - return accum; - } -} - -static -cmsUInt8Number* UnrollXYZFloatToFloat(_cmsTRANSFORM* info, - cmsFloat32Number wIn[], - cmsUInt8Number* accum, - cmsUInt32Number Stride) -{ - cmsFloat32Number* Pt = (cmsFloat32Number*) accum; - - if (T_PLANAR(info -> InputFormat)) { - - wIn[0] = (cmsFloat32Number) (Pt[0] / MAX_ENCODEABLE_XYZ); - wIn[1] = (cmsFloat32Number) (Pt[Stride] / MAX_ENCODEABLE_XYZ); - wIn[2] = (cmsFloat32Number) (Pt[Stride*2] / MAX_ENCODEABLE_XYZ); - - return accum + sizeof(cmsFloat32Number); - } - else { - - wIn[0] = (cmsFloat32Number) (Pt[0] / MAX_ENCODEABLE_XYZ); - wIn[1] = (cmsFloat32Number) (Pt[1] / MAX_ENCODEABLE_XYZ); - wIn[2] = (cmsFloat32Number) (Pt[2] / MAX_ENCODEABLE_XYZ); - - accum += sizeof(cmsFloat32Number)*(3 + T_EXTRA(info ->InputFormat)); - return accum; - } -} - - - -// Packing routines ----------------------------------------------------------------------------------------------------------- - - -// Generic chunky for byte - -static -cmsUInt8Number* PackAnyBytes(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - int nChan = T_CHANNELS(info -> OutputFormat); - int DoSwap = T_DOSWAP(info ->OutputFormat); - int Reverse = T_FLAVOR(info ->OutputFormat); - int Extra = T_EXTRA(info -> OutputFormat); - int SwapFirst = T_SWAPFIRST(info -> OutputFormat); - int ExtraFirst = DoSwap ^ SwapFirst; - cmsUInt8Number* swap1; - cmsUInt8Number v = 0; - int i; - - swap1 = output; - - if (ExtraFirst) { - output += Extra; - } - - for (i=0; i < nChan; i++) { - - int index = DoSwap ? (nChan - i - 1) : i; - - v = FROM_16_TO_8(wOut[index]); - - if (Reverse) - v = REVERSE_FLAVOR_8(v); - - *output++ = v; - } - - if (!ExtraFirst) { - output += Extra; - } - - if (Extra == 0 && SwapFirst) { - - memmove(swap1 + 1, swap1, nChan-1); - *swap1 = v; - } - - - return output; - - cmsUNUSED_PARAMETER(Stride); -} - - - -static -cmsUInt8Number* PackAnyWords(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - int nChan = T_CHANNELS(info -> OutputFormat); - int SwapEndian = T_ENDIAN16(info -> InputFormat); - int DoSwap = T_DOSWAP(info ->OutputFormat); - int Reverse = T_FLAVOR(info ->OutputFormat); - int Extra = T_EXTRA(info -> OutputFormat); - int SwapFirst = T_SWAPFIRST(info -> OutputFormat); - int ExtraFirst = DoSwap ^ SwapFirst; - cmsUInt16Number* swap1; - cmsUInt16Number v = 0; - int i; - - swap1 = (cmsUInt16Number*) output; - - if (ExtraFirst) { - output += Extra * sizeof(cmsUInt16Number); - } - - for (i=0; i < nChan; i++) { - - int index = DoSwap ? (nChan - i - 1) : i; - - v = wOut[index]; - - if (SwapEndian) - v = CHANGE_ENDIAN(v); - - if (Reverse) - v = REVERSE_FLAVOR_16(v); - - *(cmsUInt16Number*) output = v; - - output += sizeof(cmsUInt16Number); - } - - if (!ExtraFirst) { - output += Extra * sizeof(cmsUInt16Number); - } - - if (Extra == 0 && SwapFirst) { - - memmove(swap1 + 1, swap1, (nChan-1)* sizeof(cmsUInt16Number)); - *swap1 = v; - } - - - return output; - - cmsUNUSED_PARAMETER(Stride); -} - - -static -cmsUInt8Number* PackPlanarBytes(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - int nChan = T_CHANNELS(info -> OutputFormat); - int DoSwap = T_DOSWAP(info ->OutputFormat); - int SwapFirst = T_SWAPFIRST(info ->OutputFormat); - int Reverse = T_FLAVOR(info ->OutputFormat); - int i; - cmsUInt8Number* Init = output; - - - if (DoSwap ^ SwapFirst) { - output += T_EXTRA(info -> OutputFormat) * Stride; - } - - - for (i=0; i < nChan; i++) { - - int index = DoSwap ? (nChan - i - 1) : i; - cmsUInt8Number v = FROM_16_TO_8(wOut[index]); - - *(cmsUInt8Number*) output = (cmsUInt8Number) (Reverse ? REVERSE_FLAVOR_8(v) : v); - output += Stride; - } - - return (Init + 1); - - cmsUNUSED_PARAMETER(Stride); -} - - -static -cmsUInt8Number* PackPlanarWords(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - int nChan = T_CHANNELS(info -> OutputFormat); - int DoSwap = T_DOSWAP(info ->OutputFormat); - int Reverse= T_FLAVOR(info ->OutputFormat); - int SwapEndian = T_ENDIAN16(info -> OutputFormat); - int i; - cmsUInt8Number* Init = output; - cmsUInt16Number v; - - if (DoSwap) { - output += T_EXTRA(info -> OutputFormat) * Stride * sizeof(cmsUInt16Number); - } - - for (i=0; i < nChan; i++) { - - int index = DoSwap ? (nChan - i - 1) : i; - - v = wOut[index]; - - if (SwapEndian) - v = CHANGE_ENDIAN(v); - - if (Reverse) - v = REVERSE_FLAVOR_16(v); - - *(cmsUInt16Number*) output = v; - output += (Stride * sizeof(cmsUInt16Number)); - } - - return (Init + sizeof(cmsUInt16Number)); -} - -// CMYKcm (unrolled for speed) - -static -cmsUInt8Number* Pack6Bytes(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *output++ = FROM_16_TO_8(wOut[0]); - *output++ = FROM_16_TO_8(wOut[1]); - *output++ = FROM_16_TO_8(wOut[2]); - *output++ = FROM_16_TO_8(wOut[3]); - *output++ = FROM_16_TO_8(wOut[4]); - *output++ = FROM_16_TO_8(wOut[5]); - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -// KCMYcm - -static -cmsUInt8Number* Pack6BytesSwap(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *output++ = FROM_16_TO_8(wOut[5]); - *output++ = FROM_16_TO_8(wOut[4]); - *output++ = FROM_16_TO_8(wOut[3]); - *output++ = FROM_16_TO_8(wOut[2]); - *output++ = FROM_16_TO_8(wOut[1]); - *output++ = FROM_16_TO_8(wOut[0]); - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -// CMYKcm -static -cmsUInt8Number* Pack6Words(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *(cmsUInt16Number*) output = wOut[0]; - output+= 2; - *(cmsUInt16Number*) output = wOut[1]; - output+= 2; - *(cmsUInt16Number*) output = wOut[2]; - output+= 2; - *(cmsUInt16Number*) output = wOut[3]; - output+= 2; - *(cmsUInt16Number*) output = wOut[4]; - output+= 2; - *(cmsUInt16Number*) output = wOut[5]; - output+= 2; - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -// KCMYcm -static -cmsUInt8Number* Pack6WordsSwap(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *(cmsUInt16Number*) output = wOut[5]; - output+= 2; - *(cmsUInt16Number*) output = wOut[4]; - output+= 2; - *(cmsUInt16Number*) output = wOut[3]; - output+= 2; - *(cmsUInt16Number*) output = wOut[2]; - output+= 2; - *(cmsUInt16Number*) output = wOut[1]; - output+= 2; - *(cmsUInt16Number*) output = wOut[0]; - output+= 2; - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - - -static -cmsUInt8Number* Pack4Bytes(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *output++ = FROM_16_TO_8(wOut[0]); - *output++ = FROM_16_TO_8(wOut[1]); - *output++ = FROM_16_TO_8(wOut[2]); - *output++ = FROM_16_TO_8(wOut[3]); - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Pack4BytesReverse(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *output++ = REVERSE_FLAVOR_8(FROM_16_TO_8(wOut[0])); - *output++ = REVERSE_FLAVOR_8(FROM_16_TO_8(wOut[1])); - *output++ = REVERSE_FLAVOR_8(FROM_16_TO_8(wOut[2])); - *output++ = REVERSE_FLAVOR_8(FROM_16_TO_8(wOut[3])); - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - - -static -cmsUInt8Number* Pack4BytesSwapFirst(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *output++ = FROM_16_TO_8(wOut[3]); - *output++ = FROM_16_TO_8(wOut[0]); - *output++ = FROM_16_TO_8(wOut[1]); - *output++ = FROM_16_TO_8(wOut[2]); - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -// ABGR -static -cmsUInt8Number* Pack4BytesSwap(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *output++ = FROM_16_TO_8(wOut[3]); - *output++ = FROM_16_TO_8(wOut[2]); - *output++ = FROM_16_TO_8(wOut[1]); - *output++ = FROM_16_TO_8(wOut[0]); - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Pack4BytesSwapSwapFirst(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *output++ = FROM_16_TO_8(wOut[2]); - *output++ = FROM_16_TO_8(wOut[1]); - *output++ = FROM_16_TO_8(wOut[0]); - *output++ = FROM_16_TO_8(wOut[3]); - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Pack4Words(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *(cmsUInt16Number*) output = wOut[0]; - output+= 2; - *(cmsUInt16Number*) output = wOut[1]; - output+= 2; - *(cmsUInt16Number*) output = wOut[2]; - output+= 2; - *(cmsUInt16Number*) output = wOut[3]; - output+= 2; - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Pack4WordsReverse(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *(cmsUInt16Number*) output = REVERSE_FLAVOR_16(wOut[0]); - output+= 2; - *(cmsUInt16Number*) output = REVERSE_FLAVOR_16(wOut[1]); - output+= 2; - *(cmsUInt16Number*) output = REVERSE_FLAVOR_16(wOut[2]); - output+= 2; - *(cmsUInt16Number*) output = REVERSE_FLAVOR_16(wOut[3]); - output+= 2; - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -// ABGR -static -cmsUInt8Number* Pack4WordsSwap(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *(cmsUInt16Number*) output = wOut[3]; - output+= 2; - *(cmsUInt16Number*) output = wOut[2]; - output+= 2; - *(cmsUInt16Number*) output = wOut[1]; - output+= 2; - *(cmsUInt16Number*) output = wOut[0]; - output+= 2; - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -// CMYK -static -cmsUInt8Number* Pack4WordsBigEndian(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[0]); - output+= 2; - *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[1]); - output+= 2; - *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[2]); - output+= 2; - *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[3]); - output+= 2; - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - - -static -cmsUInt8Number* PackLabV2_8(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *output++ = FROM_16_TO_8(FomLabV4ToLabV2(wOut[0])); - *output++ = FROM_16_TO_8(FomLabV4ToLabV2(wOut[1])); - *output++ = FROM_16_TO_8(FomLabV4ToLabV2(wOut[2])); - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* PackALabV2_8(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - output++; - *output++ = FROM_16_TO_8(FomLabV4ToLabV2(wOut[0])); - *output++ = FROM_16_TO_8(FomLabV4ToLabV2(wOut[1])); - *output++ = FROM_16_TO_8(FomLabV4ToLabV2(wOut[2])); - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* PackLabV2_16(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *(cmsUInt16Number*) output = FomLabV4ToLabV2(wOut[0]); - output += 2; - *(cmsUInt16Number*) output = FomLabV4ToLabV2(wOut[1]); - output += 2; - *(cmsUInt16Number*) output = FomLabV4ToLabV2(wOut[2]); - output += 2; - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Pack3Bytes(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *output++ = FROM_16_TO_8(wOut[0]); - *output++ = FROM_16_TO_8(wOut[1]); - *output++ = FROM_16_TO_8(wOut[2]); - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Pack3BytesOptimized(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *output++ = (wOut[0] & 0xFF); - *output++ = (wOut[1] & 0xFF); - *output++ = (wOut[2] & 0xFF); - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Pack3BytesSwap(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *output++ = FROM_16_TO_8(wOut[2]); - *output++ = FROM_16_TO_8(wOut[1]); - *output++ = FROM_16_TO_8(wOut[0]); - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Pack3BytesSwapOptimized(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *output++ = (wOut[2] & 0xFF); - *output++ = (wOut[1] & 0xFF); - *output++ = (wOut[0] & 0xFF); - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - - -static -cmsUInt8Number* Pack3Words(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *(cmsUInt16Number*) output = wOut[0]; - output+= 2; - *(cmsUInt16Number*) output = wOut[1]; - output+= 2; - *(cmsUInt16Number*) output = wOut[2]; - output+= 2; - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Pack3WordsSwap(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *(cmsUInt16Number*) output = wOut[2]; - output+= 2; - *(cmsUInt16Number*) output = wOut[1]; - output+= 2; - *(cmsUInt16Number*) output = wOut[0]; - output+= 2; - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Pack3WordsBigEndian(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[0]); - output+= 2; - *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[1]); - output+= 2; - *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[2]); - output+= 2; - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Pack3BytesAndSkip1(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *output++ = FROM_16_TO_8(wOut[0]); - *output++ = FROM_16_TO_8(wOut[1]); - *output++ = FROM_16_TO_8(wOut[2]); - output++; - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Pack3BytesAndSkip1Optimized(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *output++ = (wOut[0] & 0xFF); - *output++ = (wOut[1] & 0xFF); - *output++ = (wOut[2] & 0xFF); - output++; - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - - -static -cmsUInt8Number* Pack3BytesAndSkip1SwapFirst(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - output++; - *output++ = FROM_16_TO_8(wOut[0]); - *output++ = FROM_16_TO_8(wOut[1]); - *output++ = FROM_16_TO_8(wOut[2]); - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Pack3BytesAndSkip1SwapFirstOptimized(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - output++; - *output++ = (wOut[0] & 0xFF); - *output++ = (wOut[1] & 0xFF); - *output++ = (wOut[2] & 0xFF); - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Pack3BytesAndSkip1Swap(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - output++; - *output++ = FROM_16_TO_8(wOut[2]); - *output++ = FROM_16_TO_8(wOut[1]); - *output++ = FROM_16_TO_8(wOut[0]); - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Pack3BytesAndSkip1SwapOptimized(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - output++; - *output++ = (wOut[2] & 0xFF); - *output++ = (wOut[1] & 0xFF); - *output++ = (wOut[0] & 0xFF); - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - - -static -cmsUInt8Number* Pack3BytesAndSkip1SwapSwapFirst(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *output++ = FROM_16_TO_8(wOut[2]); - *output++ = FROM_16_TO_8(wOut[1]); - *output++ = FROM_16_TO_8(wOut[0]); - output++; - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Pack3BytesAndSkip1SwapSwapFirstOptimized(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *output++ = (wOut[2] & 0xFF); - *output++ = (wOut[1] & 0xFF); - *output++ = (wOut[0] & 0xFF); - output++; - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Pack3WordsAndSkip1(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *(cmsUInt16Number*) output = wOut[0]; - output+= 2; - *(cmsUInt16Number*) output = wOut[1]; - output+= 2; - *(cmsUInt16Number*) output = wOut[2]; - output+= 2; - output+= 2; - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Pack3WordsAndSkip1Swap(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - output+= 2; - *(cmsUInt16Number*) output = wOut[2]; - output+= 2; - *(cmsUInt16Number*) output = wOut[1]; - output+= 2; - *(cmsUInt16Number*) output = wOut[0]; - output+= 2; - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - - -static -cmsUInt8Number* Pack3WordsAndSkip1SwapFirst(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - output+= 2; - *(cmsUInt16Number*) output = wOut[0]; - output+= 2; - *(cmsUInt16Number*) output = wOut[1]; - output+= 2; - *(cmsUInt16Number*) output = wOut[2]; - output+= 2; - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - - -static -cmsUInt8Number* Pack3WordsAndSkip1SwapSwapFirst(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *(cmsUInt16Number*) output = wOut[2]; - output+= 2; - *(cmsUInt16Number*) output = wOut[1]; - output+= 2; - *(cmsUInt16Number*) output = wOut[0]; - output+= 2; - output+= 2; - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - - - -static -cmsUInt8Number* Pack1Byte(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *output++ = FROM_16_TO_8(wOut[0]); - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - - -static -cmsUInt8Number* Pack1ByteReversed(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *output++ = FROM_16_TO_8(REVERSE_FLAVOR_16(wOut[0])); - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - - -static -cmsUInt8Number* Pack1ByteSkip1(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *output++ = FROM_16_TO_8(wOut[0]); - output++; - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - - -static -cmsUInt8Number* Pack1ByteSkip1SwapFirst(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - output++; - *output++ = FROM_16_TO_8(wOut[0]); - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Pack1Word(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *(cmsUInt16Number*) output = wOut[0]; - output+= 2; - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - - -static -cmsUInt8Number* Pack1WordReversed(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *(cmsUInt16Number*) output = REVERSE_FLAVOR_16(wOut[0]); - output+= 2; - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Pack1WordBigEndian(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *(cmsUInt16Number*) output = CHANGE_ENDIAN(wOut[0]); - output+= 2; - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - - -static -cmsUInt8Number* Pack1WordSkip1(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - *(cmsUInt16Number*) output = wOut[0]; - output+= 4; - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - -static -cmsUInt8Number* Pack1WordSkip1SwapFirst(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - output += 2; - *(cmsUInt16Number*) output = wOut[0]; - output+= 2; - - return output; - - cmsUNUSED_PARAMETER(info); - cmsUNUSED_PARAMETER(Stride); -} - - -// Unencoded Float values -- don't try optimize speed -static -cmsUInt8Number* PackLabDoubleFrom16(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - - if (T_PLANAR(info -> OutputFormat)) { - - cmsCIELab Lab; - cmsFloat64Number* Out = (cmsFloat64Number*) output; - cmsLabEncoded2Float(&Lab, wOut); - - Out[0] = Lab.L; - Out[Stride] = Lab.a; - Out[Stride*2] = Lab.b; - - return output + sizeof(cmsFloat64Number); - } - else { - - cmsLabEncoded2Float((cmsCIELab*) output, wOut); - return output + (sizeof(cmsCIELab) + T_EXTRA(info ->OutputFormat) * sizeof(cmsFloat64Number)); - } -} - - -static -cmsUInt8Number* PackLabFloatFrom16(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - cmsCIELab Lab; - cmsLabEncoded2Float(&Lab, wOut); - - if (T_PLANAR(info -> OutputFormat)) { - - cmsFloat32Number* Out = (cmsFloat32Number*) output; - - Out[0] = (cmsFloat32Number)Lab.L; - Out[Stride] = (cmsFloat32Number)Lab.a; - Out[Stride*2] = (cmsFloat32Number)Lab.b; - - return output + sizeof(cmsFloat32Number); - } - else { - - ((cmsFloat32Number*) output)[0] = (cmsFloat32Number) Lab.L; - ((cmsFloat32Number*) output)[1] = (cmsFloat32Number) Lab.a; - ((cmsFloat32Number*) output)[2] = (cmsFloat32Number) Lab.b; - - return output + (3 + T_EXTRA(info ->OutputFormat)) * sizeof(cmsFloat32Number); - } -} - -static -cmsUInt8Number* PackXYZDoubleFrom16(register _cmsTRANSFORM* Info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - if (T_PLANAR(Info -> OutputFormat)) { - - cmsCIEXYZ XYZ; - cmsFloat64Number* Out = (cmsFloat64Number*) output; - cmsXYZEncoded2Float(&XYZ, wOut); - - Out[0] = XYZ.X; - Out[Stride] = XYZ.Y; - Out[Stride*2] = XYZ.Z; - - return output + sizeof(cmsFloat64Number); - - } - else { - - cmsXYZEncoded2Float((cmsCIEXYZ*) output, wOut); - - return output + (sizeof(cmsCIEXYZ) + T_EXTRA(Info ->OutputFormat) * sizeof(cmsFloat64Number)); - } -} - -static -cmsUInt8Number* PackXYZFloatFrom16(register _cmsTRANSFORM* Info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - if (T_PLANAR(Info -> OutputFormat)) { - - cmsCIEXYZ XYZ; - cmsFloat32Number* Out = (cmsFloat32Number*) output; - cmsXYZEncoded2Float(&XYZ, wOut); - - Out[0] = (cmsFloat32Number) XYZ.X; - Out[Stride] = (cmsFloat32Number) XYZ.Y; - Out[Stride*2] = (cmsFloat32Number) XYZ.Z; - - return output + sizeof(cmsFloat32Number); - - } - else { - - cmsCIEXYZ XYZ; - cmsFloat32Number* Out = (cmsFloat32Number*) output; - cmsXYZEncoded2Float(&XYZ, wOut); - - 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)); - } -} - -static -cmsUInt8Number* PackDoubleFrom16(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - int nChan = T_CHANNELS(info -> OutputFormat); - int DoSwap = T_DOSWAP(info ->OutputFormat); - int Reverse = T_FLAVOR(info ->OutputFormat); - int Extra = T_EXTRA(info -> OutputFormat); - int SwapFirst = T_SWAPFIRST(info -> OutputFormat); - int Planar = T_PLANAR(info -> OutputFormat); - int ExtraFirst = DoSwap ^ SwapFirst; - cmsFloat64Number maximum = IsInkSpace(info ->OutputFormat) ? 655.35 : 65535.0; - cmsFloat64Number v = 0; - cmsFloat64Number* swap1 = (cmsFloat64Number*) output; - int i, start = 0; - - if (ExtraFirst) - start = Extra; - - for (i=0; i < nChan; i++) { - - int index = DoSwap ? (nChan - i - 1) : i; - - v = (cmsFloat64Number) wOut[index] / maximum; - - if (Reverse) - v = maximum - v; - - if (Planar) - ((cmsFloat64Number*) output)[(i + start) * Stride]= v; - else - ((cmsFloat64Number*) output)[i + start] = v; - } - - if (!ExtraFirst) { - output += Extra * sizeof(cmsFloat64Number); - } - - if (Extra == 0 && SwapFirst) { - - memmove(swap1 + 1, swap1, (nChan-1)* sizeof(cmsFloat64Number)); - *swap1 = v; - } - - if (T_PLANAR(info -> OutputFormat)) - return output + sizeof(cmsFloat64Number); - else - return output + nChan * sizeof(cmsFloat64Number); - -} - - -static -cmsUInt8Number* PackFloatFrom16(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - int nChan = T_CHANNELS(info -> OutputFormat); - int DoSwap = T_DOSWAP(info ->OutputFormat); - int Reverse = T_FLAVOR(info ->OutputFormat); - int Extra = T_EXTRA(info -> OutputFormat); - int SwapFirst = T_SWAPFIRST(info -> OutputFormat); - int Planar = T_PLANAR(info -> OutputFormat); - int ExtraFirst = DoSwap ^ SwapFirst; - cmsFloat64Number maximum = IsInkSpace(info ->OutputFormat) ? 655.35 : 65535.0; - cmsFloat64Number v = 0; - cmsFloat32Number* swap1 = (cmsFloat32Number*) output; - int i, start = 0; - - if (ExtraFirst) - start = Extra; - - for (i=0; i < nChan; i++) { - - int index = DoSwap ? (nChan - i - 1) : i; - - v = (cmsFloat64Number) wOut[index] / maximum; - - if (Reverse) - v = maximum - v; - - if (Planar) - ((cmsFloat32Number*) output)[(i + start ) * Stride]= (cmsFloat32Number) v; - else - ((cmsFloat32Number*) output)[i + start] = (cmsFloat32Number) v; - } - - if (!ExtraFirst) { - output += Extra * sizeof(cmsFloat32Number); - } - - if (Extra == 0 && SwapFirst) { - - memmove(swap1 + 1, swap1, (nChan-1)* sizeof(cmsFloat32Number)); - *swap1 = (cmsFloat32Number) v; - } - - if (T_PLANAR(info -> OutputFormat)) - return output + sizeof(cmsFloat32Number); - else - return output + nChan * sizeof(cmsFloat32Number); -} - - - -// -------------------------------------------------------------------------------------------------------- - -static -cmsUInt8Number* PackFloatsFromFloat(_cmsTRANSFORM* info, - cmsFloat32Number wOut[], - cmsUInt8Number* output, - cmsUInt32Number Stride) -{ - int nChan = T_CHANNELS(info -> OutputFormat); - int DoSwap = T_DOSWAP(info ->OutputFormat); - int Reverse = T_FLAVOR(info ->OutputFormat); - int Extra = T_EXTRA(info -> OutputFormat); - int SwapFirst = T_SWAPFIRST(info -> OutputFormat); - int Planar = T_PLANAR(info -> OutputFormat); - int ExtraFirst = DoSwap ^ SwapFirst; - cmsFloat64Number maximum = IsInkSpace(info ->OutputFormat) ? 100.0 : 1.0; - cmsFloat32Number* swap1 = (cmsFloat32Number*) output; - cmsFloat64Number v = 0; - int i, start = 0; - - if (ExtraFirst) - start = Extra; - - for (i=0; i < nChan; i++) { - - int index = DoSwap ? (nChan - i - 1) : i; - - v = wOut[index] * maximum; - - if (Reverse) - v = maximum - v; - - if (Planar) - ((cmsFloat32Number*) output)[(i + start)* Stride]= (cmsFloat32Number) v; - else - ((cmsFloat32Number*) output)[i + start] = (cmsFloat32Number) v; - } - - if (!ExtraFirst) { - output += Extra * sizeof(cmsFloat32Number); - } - - if (Extra == 0 && SwapFirst) { - - memmove(swap1 + 1, swap1, (nChan-1)* sizeof(cmsFloat32Number)); - *swap1 = (cmsFloat32Number) v; - } - - if (T_PLANAR(info -> OutputFormat)) - return output + sizeof(cmsFloat32Number); - else - return output + nChan * sizeof(cmsFloat32Number); -} - -static -cmsUInt8Number* PackDoublesFromFloat(_cmsTRANSFORM* info, - cmsFloat32Number wOut[], - cmsUInt8Number* output, - cmsUInt32Number Stride) -{ - int nChan = T_CHANNELS(info -> OutputFormat); - int DoSwap = T_DOSWAP(info ->OutputFormat); - int Reverse = T_FLAVOR(info ->OutputFormat); - int Extra = T_EXTRA(info -> OutputFormat); - int SwapFirst = T_SWAPFIRST(info -> OutputFormat); - int Planar = T_PLANAR(info -> OutputFormat); - int ExtraFirst = DoSwap ^ SwapFirst; - cmsFloat64Number maximum = IsInkSpace(info ->OutputFormat) ? 100.0 : 1.0; - cmsFloat64Number v = 0; - cmsFloat64Number* swap1 = (cmsFloat64Number*) output; - int i, start = 0; - - if (ExtraFirst) - start = Extra; - - for (i=0; i < nChan; i++) { - - int index = DoSwap ? (nChan - i - 1) : i; - - v = wOut[index] * maximum; - - if (Reverse) - v = maximum - v; - - if (Planar) - ((cmsFloat64Number*) output)[(i + start) * Stride] = v; - else - ((cmsFloat64Number*) output)[i + start] = v; - } - - if (!ExtraFirst) { - output += Extra * sizeof(cmsFloat64Number); - } - - if (Extra == 0 && SwapFirst) { - - memmove(swap1 + 1, swap1, (nChan-1)* sizeof(cmsFloat64Number)); - *swap1 = v; - } - - - if (T_PLANAR(info -> OutputFormat)) - return output + sizeof(cmsFloat64Number); - else - return output + nChan * sizeof(cmsFloat64Number); - -} - - - - - -static -cmsUInt8Number* PackLabFloatFromFloat(_cmsTRANSFORM* Info, - cmsFloat32Number wOut[], - cmsUInt8Number* output, - cmsUInt32Number Stride) -{ - cmsFloat32Number* Out = (cmsFloat32Number*) output; - - if (T_PLANAR(Info -> OutputFormat)) { - - Out[0] = (cmsFloat32Number) (wOut[0] * 100.0); - Out[Stride] = (cmsFloat32Number) (wOut[1] * 255.0 - 128.0); - Out[Stride*2] = (cmsFloat32Number) (wOut[2] * 255.0 - 128.0); - - return output + sizeof(cmsFloat32Number); - } - else { - - Out[0] = (cmsFloat32Number) (wOut[0] * 100.0); - Out[1] = (cmsFloat32Number) (wOut[1] * 255.0 - 128.0); - Out[2] = (cmsFloat32Number) (wOut[2] * 255.0 - 128.0); - - return output + (sizeof(cmsFloat32Number)*3 + T_EXTRA(Info ->OutputFormat) * sizeof(cmsFloat32Number)); - } - -} - - -static -cmsUInt8Number* PackLabDoubleFromFloat(_cmsTRANSFORM* Info, - cmsFloat32Number wOut[], - cmsUInt8Number* output, - cmsUInt32Number Stride) -{ - cmsFloat64Number* Out = (cmsFloat64Number*) output; - - if (T_PLANAR(Info -> OutputFormat)) { - - Out[0] = (cmsFloat64Number) (wOut[0] * 100.0); - Out[Stride] = (cmsFloat64Number) (wOut[1] * 255.0 - 128.0); - Out[Stride*2] = (cmsFloat64Number) (wOut[2] * 255.0 - 128.0); - - return output + sizeof(cmsFloat64Number); - } - else { - - Out[0] = (cmsFloat64Number) (wOut[0] * 100.0); - Out[1] = (cmsFloat64Number) (wOut[1] * 255.0 - 128.0); - Out[2] = (cmsFloat64Number) (wOut[2] * 255.0 - 128.0); - - return output + (sizeof(cmsFloat64Number)*3 + T_EXTRA(Info ->OutputFormat) * sizeof(cmsFloat64Number)); - } - -} - - -// From 0..1 range to 0..MAX_ENCODEABLE_XYZ -static -cmsUInt8Number* PackXYZFloatFromFloat(_cmsTRANSFORM* Info, - cmsFloat32Number wOut[], - cmsUInt8Number* output, - cmsUInt32Number Stride) -{ - cmsFloat32Number* Out = (cmsFloat32Number*) output; - - if (T_PLANAR(Info -> OutputFormat)) { - - Out[0] = (cmsFloat32Number) (wOut[0] * MAX_ENCODEABLE_XYZ); - Out[Stride] = (cmsFloat32Number) (wOut[1] * MAX_ENCODEABLE_XYZ); - Out[Stride*2] = (cmsFloat32Number) (wOut[2] * MAX_ENCODEABLE_XYZ); - - return output + sizeof(cmsFloat32Number); - } - else { - - Out[0] = (cmsFloat32Number) (wOut[0] * MAX_ENCODEABLE_XYZ); - Out[1] = (cmsFloat32Number) (wOut[1] * MAX_ENCODEABLE_XYZ); - Out[2] = (cmsFloat32Number) (wOut[2] * MAX_ENCODEABLE_XYZ); - - return output + (sizeof(cmsFloat32Number)*3 + T_EXTRA(Info ->OutputFormat) * sizeof(cmsFloat32Number)); - } - -} - -// Same, but convert to double -static -cmsUInt8Number* PackXYZDoubleFromFloat(_cmsTRANSFORM* Info, - cmsFloat32Number wOut[], - cmsUInt8Number* output, - cmsUInt32Number Stride) -{ - cmsFloat64Number* Out = (cmsFloat64Number*) output; - - if (T_PLANAR(Info -> OutputFormat)) { - - Out[0] = (cmsFloat64Number) (wOut[0] * MAX_ENCODEABLE_XYZ); - Out[Stride] = (cmsFloat64Number) (wOut[1] * MAX_ENCODEABLE_XYZ); - Out[Stride*2] = (cmsFloat64Number) (wOut[2] * MAX_ENCODEABLE_XYZ); - - return output + sizeof(cmsFloat64Number); - } - else { - - Out[0] = (cmsFloat64Number) (wOut[0] * MAX_ENCODEABLE_XYZ); - Out[1] = (cmsFloat64Number) (wOut[1] * MAX_ENCODEABLE_XYZ); - Out[2] = (cmsFloat64Number) (wOut[2] * MAX_ENCODEABLE_XYZ); - - return output + (sizeof(cmsFloat64Number)*3 + T_EXTRA(Info ->OutputFormat) * sizeof(cmsFloat64Number)); - } - -} - - -// ---------------------------------------------------------------------------------------------------------------- - -#ifndef CMS_NO_HALF_SUPPORT - -// Decodes an stream of half floats to wIn[] described by input format - -static -cmsUInt8Number* UnrollHalfTo16(register _cmsTRANSFORM* info, - register cmsUInt16Number wIn[], - register cmsUInt8Number* accum, - register cmsUInt32Number Stride) -{ - - int nChan = T_CHANNELS(info -> InputFormat); - int DoSwap = T_DOSWAP(info ->InputFormat); - int Reverse = T_FLAVOR(info ->InputFormat); - int SwapFirst = T_SWAPFIRST(info -> InputFormat); - int Extra = T_EXTRA(info -> InputFormat); - int ExtraFirst = DoSwap ^ SwapFirst; - int Planar = T_PLANAR(info -> InputFormat); - cmsFloat32Number v; - int i, start = 0; - cmsFloat32Number maximum = IsInkSpace(info ->InputFormat) ? 655.35F : 65535.0F; - - - if (ExtraFirst) - start = Extra; - - for (i=0; i < nChan; i++) { - - int index = DoSwap ? (nChan - i - 1) : i; - - if (Planar) - v = _cmsHalf2Float ( ((cmsUInt16Number*) accum)[(i + start) * Stride] ); - else - v = _cmsHalf2Float ( ((cmsUInt16Number*) accum)[i + start] ) ; - - if (Reverse) v = maximum - v; - - wIn[index] = _cmsQuickSaturateWord(v * maximum); - } - - - if (Extra == 0 && SwapFirst) { - cmsUInt16Number tmp = wIn[0]; - - memmove(&wIn[0], &wIn[1], (nChan-1) * sizeof(cmsUInt16Number)); - wIn[nChan-1] = tmp; - } - - if (T_PLANAR(info -> InputFormat)) - return accum + sizeof(cmsUInt16Number); - else - return accum + (nChan + Extra) * sizeof(cmsUInt16Number); -} - -// Decodes an stream of half floats to wIn[] described by input format - -static -cmsUInt8Number* UnrollHalfToFloat(_cmsTRANSFORM* info, - cmsFloat32Number wIn[], - cmsUInt8Number* accum, - cmsUInt32Number Stride) -{ - - int nChan = T_CHANNELS(info -> InputFormat); - int DoSwap = T_DOSWAP(info ->InputFormat); - int Reverse = T_FLAVOR(info ->InputFormat); - int SwapFirst = T_SWAPFIRST(info -> InputFormat); - int Extra = T_EXTRA(info -> InputFormat); - int ExtraFirst = DoSwap ^ SwapFirst; - int Planar = T_PLANAR(info -> InputFormat); - cmsFloat32Number v; - int i, start = 0; - cmsFloat32Number maximum = IsInkSpace(info ->InputFormat) ? 100.0F : 1.0F; - - - if (ExtraFirst) - start = Extra; - - for (i=0; i < nChan; i++) { - - int index = DoSwap ? (nChan - i - 1) : i; - - if (Planar) - v = _cmsHalf2Float ( ((cmsUInt16Number*) accum)[(i + start) * Stride] ); - else - v = _cmsHalf2Float ( ((cmsUInt16Number*) accum)[i + start] ) ; - - v /= maximum; - - wIn[index] = Reverse ? 1 - v : v; - } - - - if (Extra == 0 && SwapFirst) { - cmsFloat32Number tmp = wIn[0]; - - memmove(&wIn[0], &wIn[1], (nChan-1) * sizeof(cmsFloat32Number)); - wIn[nChan-1] = tmp; - } - - if (T_PLANAR(info -> InputFormat)) - return accum + sizeof(cmsUInt16Number); - else - return accum + (nChan + Extra) * sizeof(cmsUInt16Number); -} - - -static -cmsUInt8Number* PackHalfFrom16(register _cmsTRANSFORM* info, - register cmsUInt16Number wOut[], - register cmsUInt8Number* output, - register cmsUInt32Number Stride) -{ - int nChan = T_CHANNELS(info -> OutputFormat); - int DoSwap = T_DOSWAP(info ->OutputFormat); - int Reverse = T_FLAVOR(info ->OutputFormat); - int Extra = T_EXTRA(info -> OutputFormat); - int SwapFirst = T_SWAPFIRST(info -> OutputFormat); - int Planar = T_PLANAR(info -> OutputFormat); - int ExtraFirst = DoSwap ^ SwapFirst; - cmsFloat32Number maximum = IsInkSpace(info ->OutputFormat) ? 655.35F : 65535.0F; - cmsFloat32Number v = 0; - cmsUInt16Number* swap1 = (cmsUInt16Number*) output; - int i, start = 0; - - if (ExtraFirst) - start = Extra; - - for (i=0; i < nChan; i++) { - - int index = DoSwap ? (nChan - i - 1) : i; - - v = (cmsFloat32Number) wOut[index] / maximum; - - if (Reverse) - v = maximum - v; - - if (Planar) - ((cmsUInt16Number*) output)[(i + start ) * Stride]= _cmsFloat2Half(v); - else - ((cmsUInt16Number*) output)[i + start] = _cmsFloat2Half(v); - } - - if (!ExtraFirst) { - output += Extra * sizeof(cmsUInt16Number); - } - - if (Extra == 0 && SwapFirst) { - - memmove(swap1 + 1, swap1, (nChan-1)* sizeof(cmsUInt16Number)); - *swap1 = _cmsFloat2Half(v); - } - - if (T_PLANAR(info -> OutputFormat)) - return output + sizeof(cmsUInt16Number); - else - return output + nChan * sizeof(cmsUInt16Number); -} - - - -static -cmsUInt8Number* PackHalfFromFloat(_cmsTRANSFORM* info, - cmsFloat32Number wOut[], - cmsUInt8Number* output, - cmsUInt32Number Stride) -{ - int nChan = T_CHANNELS(info -> OutputFormat); - int DoSwap = T_DOSWAP(info ->OutputFormat); - int Reverse = T_FLAVOR(info ->OutputFormat); - int Extra = T_EXTRA(info -> OutputFormat); - int SwapFirst = T_SWAPFIRST(info -> OutputFormat); - int Planar = T_PLANAR(info -> OutputFormat); - int ExtraFirst = DoSwap ^ SwapFirst; - cmsFloat32Number maximum = IsInkSpace(info ->OutputFormat) ? 100.0F : 1.0F; - cmsUInt16Number* swap1 = (cmsUInt16Number*) output; - cmsFloat32Number v = 0; - int i, start = 0; - - if (ExtraFirst) - start = Extra; - - for (i=0; i < nChan; i++) { - - int index = DoSwap ? (nChan - i - 1) : i; - - v = wOut[index] * maximum; - - if (Reverse) - v = maximum - v; - - if (Planar) - ((cmsUInt16Number*) output)[(i + start)* Stride]= _cmsFloat2Half( v ); - else - ((cmsUInt16Number*) output)[i + start] = _cmsFloat2Half( v ); - } - - if (!ExtraFirst) { - output += Extra * sizeof(cmsUInt16Number); - } - - if (Extra == 0 && SwapFirst) { - - memmove(swap1 + 1, swap1, (nChan-1)* sizeof(cmsUInt16Number)); - *swap1 = (cmsUInt16Number) _cmsFloat2Half( v ); - } - - if (T_PLANAR(info -> OutputFormat)) - return output + sizeof(cmsUInt16Number); - else - return output + nChan * sizeof(cmsUInt16Number); -} - -#endif - -// ---------------------------------------------------------------------------------------------------------------- - - -static cmsFormatters16 InputFormatters16[] = { - - // Type Mask Function - // ---------------------------- ------------------------------------ ---------------------------- - { TYPE_Lab_DBL, ANYPLANAR|ANYEXTRA, UnrollLabDoubleTo16}, - { TYPE_XYZ_DBL, ANYPLANAR|ANYEXTRA, UnrollXYZDoubleTo16}, - { TYPE_Lab_FLT, ANYPLANAR|ANYEXTRA, UnrollLabFloatTo16}, - { TYPE_XYZ_FLT, ANYPLANAR|ANYEXTRA, UnrollXYZFloatTo16}, - { TYPE_GRAY_DBL, 0, UnrollDouble1Chan}, - { FLOAT_SH(1)|BYTES_SH(0), ANYCHANNELS|ANYPLANAR|ANYSWAPFIRST|ANYFLAVOR| - ANYSWAP|ANYEXTRA|ANYSPACE, UnrollDoubleTo16}, - { FLOAT_SH(1)|BYTES_SH(4), ANYCHANNELS|ANYPLANAR|ANYSWAPFIRST|ANYFLAVOR| - ANYSWAP|ANYEXTRA|ANYSPACE, UnrollFloatTo16}, -#ifndef CMS_NO_HALF_SUPPORT - { FLOAT_SH(1)|BYTES_SH(2), ANYCHANNELS|ANYPLANAR|ANYSWAPFIRST|ANYFLAVOR| - ANYEXTRA|ANYSWAP|ANYSPACE, UnrollHalfTo16}, -#endif - - { CHANNELS_SH(1)|BYTES_SH(1), ANYSPACE, Unroll1Byte}, - { CHANNELS_SH(1)|BYTES_SH(1)|EXTRA_SH(1), ANYSPACE, Unroll1ByteSkip1}, - { CHANNELS_SH(1)|BYTES_SH(1)|EXTRA_SH(2), ANYSPACE, Unroll1ByteSkip2}, - { CHANNELS_SH(1)|BYTES_SH(1)|FLAVOR_SH(1), ANYSPACE, Unroll1ByteReversed}, - { COLORSPACE_SH(PT_MCH2)|CHANNELS_SH(2)|BYTES_SH(1), 0, Unroll2Bytes}, - - { TYPE_LabV2_8, 0, UnrollLabV2_8 }, - { TYPE_ALabV2_8, 0, UnrollALabV2_8 }, - { TYPE_LabV2_16, 0, UnrollLabV2_16 }, - - { CHANNELS_SH(3)|BYTES_SH(1), ANYSPACE, Unroll3Bytes}, - { CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1), ANYSPACE, Unroll3BytesSwap}, - { CHANNELS_SH(3)|EXTRA_SH(1)|BYTES_SH(1)|DOSWAP_SH(1), ANYSPACE, Unroll3BytesSkip1Swap}, - { CHANNELS_SH(3)|EXTRA_SH(1)|BYTES_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Unroll3BytesSkip1SwapFirst}, - - { CHANNELS_SH(3)|EXTRA_SH(1)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1), - ANYSPACE, Unroll3BytesSkip1SwapSwapFirst}, - - { CHANNELS_SH(4)|BYTES_SH(1), ANYSPACE, Unroll4Bytes}, - { CHANNELS_SH(4)|BYTES_SH(1)|FLAVOR_SH(1), ANYSPACE, Unroll4BytesReverse}, - { CHANNELS_SH(4)|BYTES_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Unroll4BytesSwapFirst}, - { CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1), ANYSPACE, Unroll4BytesSwap}, - { CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Unroll4BytesSwapSwapFirst}, - - { BYTES_SH(1)|PLANAR_SH(1), ANYFLAVOR|ANYSWAPFIRST| - ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, UnrollPlanarBytes}, - - { BYTES_SH(1), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP| - ANYEXTRA|ANYCHANNELS|ANYSPACE, UnrollChunkyBytes}, - - { CHANNELS_SH(1)|BYTES_SH(2), ANYSPACE, Unroll1Word}, - { CHANNELS_SH(1)|BYTES_SH(2)|FLAVOR_SH(1), ANYSPACE, Unroll1WordReversed}, - { CHANNELS_SH(1)|BYTES_SH(2)|EXTRA_SH(3), ANYSPACE, Unroll1WordSkip3}, - - { CHANNELS_SH(2)|BYTES_SH(2), ANYSPACE, Unroll2Words}, - { CHANNELS_SH(3)|BYTES_SH(2), ANYSPACE, Unroll3Words}, - { CHANNELS_SH(4)|BYTES_SH(2), ANYSPACE, Unroll4Words}, - - { CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1), ANYSPACE, Unroll3WordsSwap}, - { CHANNELS_SH(3)|BYTES_SH(2)|EXTRA_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Unroll3WordsSkip1SwapFirst}, - { CHANNELS_SH(3)|BYTES_SH(2)|EXTRA_SH(1)|DOSWAP_SH(1), ANYSPACE, Unroll3WordsSkip1Swap}, - { CHANNELS_SH(4)|BYTES_SH(2)|FLAVOR_SH(1), ANYSPACE, Unroll4WordsReverse}, - { CHANNELS_SH(4)|BYTES_SH(2)|SWAPFIRST_SH(1), ANYSPACE, Unroll4WordsSwapFirst}, - { CHANNELS_SH(4)|BYTES_SH(2)|DOSWAP_SH(1), ANYSPACE, Unroll4WordsSwap}, - { CHANNELS_SH(4)|BYTES_SH(2)|DOSWAP_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Unroll4WordsSwapSwapFirst}, - - - { BYTES_SH(2)|PLANAR_SH(1), ANYFLAVOR|ANYSWAP|ANYENDIAN|ANYEXTRA|ANYCHANNELS|ANYSPACE, UnrollPlanarWords}, - { BYTES_SH(2), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYENDIAN|ANYEXTRA|ANYCHANNELS|ANYSPACE, UnrollAnyWords}, -}; - - - -static cmsFormattersFloat InputFormattersFloat[] = { - - // Type Mask Function - // ---------------------------- ------------------------------------ ---------------------------- - { TYPE_Lab_DBL, ANYPLANAR|ANYEXTRA, UnrollLabDoubleToFloat}, - { TYPE_Lab_FLT, ANYPLANAR|ANYEXTRA, UnrollLabFloatToFloat}, - - { TYPE_XYZ_DBL, ANYPLANAR|ANYEXTRA, UnrollXYZDoubleToFloat}, - { TYPE_XYZ_FLT, ANYPLANAR|ANYEXTRA, UnrollXYZFloatToFloat}, - - { FLOAT_SH(1)|BYTES_SH(4), ANYPLANAR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA| - ANYCHANNELS|ANYSPACE, UnrollFloatsToFloat}, - - { FLOAT_SH(1)|BYTES_SH(0), ANYPLANAR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA| - ANYCHANNELS|ANYSPACE, UnrollDoublesToFloat}, -#ifndef CMS_NO_HALF_SUPPORT - { FLOAT_SH(1)|BYTES_SH(2), ANYPLANAR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA| - ANYCHANNELS|ANYSPACE, UnrollHalfToFloat}, -#endif -}; - - -// Bit fields set to one in the mask are not compared -static -cmsFormatter _cmsGetStockInputFormatter(cmsUInt32Number dwInput, cmsUInt32Number dwFlags) -{ - cmsUInt32Number i; - cmsFormatter fr; - - switch (dwFlags) { - - case CMS_PACK_FLAGS_16BITS: { - for (i=0; i < sizeof(InputFormatters16) / sizeof(cmsFormatters16); i++) { - cmsFormatters16* f = InputFormatters16 + i; - - if ((dwInput & ~f ->Mask) == f ->Type) { - fr.Fmt16 = f ->Frm; - return fr; - } - } - } - break; - - case CMS_PACK_FLAGS_FLOAT: { - for (i=0; i < sizeof(InputFormattersFloat) / sizeof(cmsFormattersFloat); i++) { - cmsFormattersFloat* f = InputFormattersFloat + i; - - if ((dwInput & ~f ->Mask) == f ->Type) { - fr.FmtFloat = f ->Frm; - return fr; - } - } - } - break; - - default:; - - } - - fr.Fmt16 = NULL; - return fr; -} - -static cmsFormatters16 OutputFormatters16[] = { - // Type Mask Function - // ---------------------------- ------------------------------------ ---------------------------- - - { TYPE_Lab_DBL, ANYPLANAR|ANYEXTRA, PackLabDoubleFrom16}, - { TYPE_XYZ_DBL, ANYPLANAR|ANYEXTRA, PackXYZDoubleFrom16}, - - { TYPE_Lab_FLT, ANYPLANAR|ANYEXTRA, PackLabFloatFrom16}, - { TYPE_XYZ_FLT, ANYPLANAR|ANYEXTRA, PackXYZFloatFrom16}, - - { FLOAT_SH(1)|BYTES_SH(0), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP| - ANYCHANNELS|ANYPLANAR|ANYEXTRA|ANYSPACE, PackDoubleFrom16}, - { FLOAT_SH(1)|BYTES_SH(4), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP| - ANYCHANNELS|ANYPLANAR|ANYEXTRA|ANYSPACE, PackFloatFrom16}, -#ifndef CMS_NO_HALF_SUPPORT - { FLOAT_SH(1)|BYTES_SH(2), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP| - ANYCHANNELS|ANYPLANAR|ANYEXTRA|ANYSPACE, PackHalfFrom16}, -#endif - - { CHANNELS_SH(1)|BYTES_SH(1), ANYSPACE, Pack1Byte}, - { CHANNELS_SH(1)|BYTES_SH(1)|EXTRA_SH(1), ANYSPACE, Pack1ByteSkip1}, - { CHANNELS_SH(1)|BYTES_SH(1)|EXTRA_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Pack1ByteSkip1SwapFirst}, - - { CHANNELS_SH(1)|BYTES_SH(1)|FLAVOR_SH(1), ANYSPACE, Pack1ByteReversed}, - - { TYPE_LabV2_8, 0, PackLabV2_8 }, - { TYPE_ALabV2_8, 0, PackALabV2_8 }, - { TYPE_LabV2_16, 0, PackLabV2_16 }, - - { CHANNELS_SH(3)|BYTES_SH(1)|OPTIMIZED_SH(1), ANYSPACE, Pack3BytesOptimized}, - { CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|OPTIMIZED_SH(1), ANYSPACE, Pack3BytesAndSkip1Optimized}, - { CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|SWAPFIRST_SH(1)|OPTIMIZED_SH(1), - ANYSPACE, Pack3BytesAndSkip1SwapFirstOptimized}, - { CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1)|OPTIMIZED_SH(1), - ANYSPACE, Pack3BytesAndSkip1SwapSwapFirstOptimized}, - { CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|EXTRA_SH(1)|OPTIMIZED_SH(1), - ANYSPACE, Pack3BytesAndSkip1SwapOptimized}, - { CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|OPTIMIZED_SH(1), ANYSPACE, Pack3BytesSwapOptimized}, - - - - { CHANNELS_SH(3)|BYTES_SH(1), ANYSPACE, Pack3Bytes}, - { CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1), ANYSPACE, Pack3BytesAndSkip1}, - { CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Pack3BytesAndSkip1SwapFirst}, - { CHANNELS_SH(3)|BYTES_SH(1)|EXTRA_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1), - ANYSPACE, Pack3BytesAndSkip1SwapSwapFirst}, - { CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|EXTRA_SH(1), ANYSPACE, Pack3BytesAndSkip1Swap}, - { CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1), ANYSPACE, Pack3BytesSwap}, - { CHANNELS_SH(6)|BYTES_SH(1), ANYSPACE, Pack6Bytes}, - { CHANNELS_SH(6)|BYTES_SH(1)|DOSWAP_SH(1), ANYSPACE, Pack6BytesSwap}, - { CHANNELS_SH(4)|BYTES_SH(1), ANYSPACE, Pack4Bytes}, - { CHANNELS_SH(4)|BYTES_SH(1)|FLAVOR_SH(1), ANYSPACE, Pack4BytesReverse}, - { CHANNELS_SH(4)|BYTES_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Pack4BytesSwapFirst}, - { CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1), ANYSPACE, Pack4BytesSwap}, - { CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Pack4BytesSwapSwapFirst}, - - { BYTES_SH(1), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackAnyBytes}, - { BYTES_SH(1)|PLANAR_SH(1), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackPlanarBytes}, - - { CHANNELS_SH(1)|BYTES_SH(2), ANYSPACE, Pack1Word}, - { CHANNELS_SH(1)|BYTES_SH(2)|EXTRA_SH(1), ANYSPACE, Pack1WordSkip1}, - { CHANNELS_SH(1)|BYTES_SH(2)|EXTRA_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Pack1WordSkip1SwapFirst}, - { CHANNELS_SH(1)|BYTES_SH(2)|FLAVOR_SH(1), ANYSPACE, Pack1WordReversed}, - { CHANNELS_SH(1)|BYTES_SH(2)|ENDIAN16_SH(1), ANYSPACE, Pack1WordBigEndian}, - { CHANNELS_SH(3)|BYTES_SH(2), ANYSPACE, Pack3Words}, - { CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1), ANYSPACE, Pack3WordsSwap}, - { CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1), ANYSPACE, Pack3WordsBigEndian}, - { CHANNELS_SH(3)|BYTES_SH(2)|EXTRA_SH(1), ANYSPACE, Pack3WordsAndSkip1}, - { CHANNELS_SH(3)|BYTES_SH(2)|EXTRA_SH(1)|DOSWAP_SH(1), ANYSPACE, Pack3WordsAndSkip1Swap}, - { CHANNELS_SH(3)|BYTES_SH(2)|EXTRA_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Pack3WordsAndSkip1SwapFirst}, - - { CHANNELS_SH(3)|BYTES_SH(2)|EXTRA_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1), - ANYSPACE, Pack3WordsAndSkip1SwapSwapFirst}, - - { CHANNELS_SH(4)|BYTES_SH(2), ANYSPACE, Pack4Words}, - { CHANNELS_SH(4)|BYTES_SH(2)|FLAVOR_SH(1), ANYSPACE, Pack4WordsReverse}, - { CHANNELS_SH(4)|BYTES_SH(2)|DOSWAP_SH(1), ANYSPACE, Pack4WordsSwap}, - { CHANNELS_SH(4)|BYTES_SH(2)|ENDIAN16_SH(1), ANYSPACE, Pack4WordsBigEndian}, - - { CHANNELS_SH(6)|BYTES_SH(2), ANYSPACE, Pack6Words}, - { CHANNELS_SH(6)|BYTES_SH(2)|DOSWAP_SH(1), ANYSPACE, Pack6WordsSwap}, - - { BYTES_SH(2)|PLANAR_SH(1), ANYFLAVOR|ANYENDIAN|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackPlanarWords}, - { BYTES_SH(2), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYENDIAN|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackAnyWords} - -}; - - -static cmsFormattersFloat OutputFormattersFloat[] = { - // Type Mask Function - // ---------------------------- --------------------------------------------------- ---------------------------- - { TYPE_Lab_FLT, ANYPLANAR|ANYEXTRA, PackLabFloatFromFloat}, - { TYPE_XYZ_FLT, ANYPLANAR|ANYEXTRA, PackXYZFloatFromFloat}, - - { TYPE_Lab_DBL, ANYPLANAR|ANYEXTRA, PackLabDoubleFromFloat}, - { TYPE_XYZ_DBL, ANYPLANAR|ANYEXTRA, PackXYZDoubleFromFloat}, - - { FLOAT_SH(1)|BYTES_SH(4), ANYPLANAR| - ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackFloatsFromFloat }, - { FLOAT_SH(1)|BYTES_SH(0), ANYPLANAR| - ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackDoublesFromFloat }, -#ifndef CMS_NO_HALF_SUPPORT - { FLOAT_SH(1)|BYTES_SH(2), - ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackHalfFromFloat }, -#endif - - - -}; - - -// Bit fields set to one in the mask are not compared -static -cmsFormatter _cmsGetStockOutputFormatter(cmsUInt32Number dwInput, cmsUInt32Number dwFlags) -{ - cmsUInt32Number i; - cmsFormatter fr; - - - switch (dwFlags) - { - - case CMS_PACK_FLAGS_16BITS: { - - for (i=0; i < sizeof(OutputFormatters16) / sizeof(cmsFormatters16); i++) { - cmsFormatters16* f = OutputFormatters16 + i; - - if ((dwInput & ~f ->Mask) == f ->Type) { - fr.Fmt16 = f ->Frm; - return fr; - } - } - } - break; - - case CMS_PACK_FLAGS_FLOAT: { - - for (i=0; i < sizeof(OutputFormattersFloat) / sizeof(cmsFormattersFloat); i++) { - cmsFormattersFloat* f = OutputFormattersFloat + i; - - if ((dwInput & ~f ->Mask) == f ->Type) { - fr.FmtFloat = f ->Frm; - return fr; - } - } - } - break; - - default:; - - } - - fr.Fmt16 = NULL; - return fr; -} - - -typedef struct _cms_formatters_factory_list { - - cmsFormatterFactory Factory; - struct _cms_formatters_factory_list *Next; - -} cmsFormattersFactoryList; - -_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 ContextID, cmsPluginBase* Data) -{ - _cmsFormattersPluginChunkType* ctx = ( _cmsFormattersPluginChunkType*) _cmsContextGetClientChunk(ContextID, FormattersPlugin); - cmsPluginFormatters* Plugin = (cmsPluginFormatters*) Data; - cmsFormattersFactoryList* fl ; - - // Reset to built-in defaults - if (Data == NULL) { - - ctx ->FactoryList = NULL; - return TRUE; - } - - fl = (cmsFormattersFactoryList*) _cmsPluginMalloc(ContextID, sizeof(cmsFormattersFactoryList)); - if (fl == NULL) return FALSE; - - fl ->Factory = Plugin ->FormattersFactory; - - fl ->Next = ctx -> FactoryList; - ctx ->FactoryList = fl; - - return TRUE; -} - -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 =ctx->FactoryList; f != NULL; f = f ->Next) { - - cmsFormatter fn = f ->Factory(Type, Dir, dwFlags); - if (fn.Fmt16 != NULL) return fn; - } - - // Revert to default - if (Dir == cmsFormatterInput) - return _cmsGetStockInputFormatter(Type, dwFlags); - else - return _cmsGetStockOutputFormatter(Type, dwFlags); -} - - -// Return whatever given formatter refers to float values -cmsBool _cmsFormatterIsFloat(cmsUInt32Number Type) -{ - return T_FLOAT(Type) ? TRUE : FALSE; -} - -// Return whatever given formatter refers to 8 bits -cmsBool _cmsFormatterIs8bit(cmsUInt32Number Type) -{ - int Bytes = T_BYTES(Type); - - return (Bytes == 1); -} - -// Build a suitable formatter for the colorspace of this profile -cmsUInt32Number CMSEXPORT cmsFormatterForColorspaceOfProfile(cmsHPROFILE hProfile, cmsUInt32Number nBytes, cmsBool lIsFloat) -{ - - cmsColorSpaceSignature ColorSpace = cmsGetColorSpace(hProfile); - cmsUInt32Number ColorSpaceBits = _cmsLCMScolorSpace(ColorSpace); - cmsUInt32Number nOutputChans = cmsChannelsOf(ColorSpace); - cmsUInt32Number Float = lIsFloat ? 1 : 0; - - // Create a fake formatter for result - return FLOAT_SH(Float) | COLORSPACE_SH(ColorSpaceBits) | BYTES_SH(nBytes) | CHANNELS_SH(nOutputChans); -} - -// Build a suitable formatter for the colorspace of this profile -cmsUInt32Number CMSEXPORT cmsFormatterForPCSOfProfile(cmsHPROFILE hProfile, cmsUInt32Number nBytes, cmsBool lIsFloat) -{ - - cmsColorSpaceSignature ColorSpace = cmsGetPCS(hProfile); - int ColorSpaceBits = _cmsLCMScolorSpace(ColorSpace); - cmsUInt32Number nOutputChans = cmsChannelsOf(ColorSpace); - cmsUInt32Number Float = lIsFloat ? 1 : 0; - - // Create a fake formatter for result - return FLOAT_SH(Float) | COLORSPACE_SH(ColorSpaceBits) | BYTES_SH(nBytes) | CHANNELS_SH(nOutputChans); -} diff --git a/third_party/lcms2-2.6/src/cmspcs.c b/third_party/lcms2-2.6/src/cmspcs.c deleted file mode 100644 index 102cd7d21e..0000000000 --- a/third_party/lcms2-2.6/src/cmspcs.c +++ /dev/null @@ -1,931 +0,0 @@ -//--------------------------------------------------------------------------------- -// -// Little Color Management System -// Copyright (c) 1998-2010 Marti Maria Saguer -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the Software -// is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// -//--------------------------------------------------------------------------------- -// - -#include "lcms2_internal.h" - -// inter PCS conversions XYZ <-> CIE L* a* b* -/* - - - CIE 15:2004 CIELab is defined as: - - L* = 116*f(Y/Yn) - 16 0 <= L* <= 100 - a* = 500*[f(X/Xn) - f(Y/Yn)] - b* = 200*[f(Y/Yn) - f(Z/Zn)] - - and - - f(t) = t^(1/3) 1 >= t > (24/116)^3 - (841/108)*t + (16/116) 0 <= t <= (24/116)^3 - - - Reverse transform is: - - X = Xn*[a* / 500 + (L* + 16) / 116] ^ 3 if (X/Xn) > (24/116) - = Xn*(a* / 500 + L* / 116) / 7.787 if (X/Xn) <= (24/116) - - - - PCS in Lab2 is encoded as: - - 8 bit Lab PCS: - - L* 0..100 into a 0..ff byte. - a* t + 128 range is -128.0 +127.0 - b* - - 16 bit Lab PCS: - - L* 0..100 into a 0..ff00 word. - a* t + 128 range is -128.0 +127.9961 - b* - - - -Interchange Space Component Actual Range Encoded Range -CIE XYZ X 0 -> 1.99997 0x0000 -> 0xffff -CIE XYZ Y 0 -> 1.99997 0x0000 -> 0xffff -CIE XYZ Z 0 -> 1.99997 0x0000 -> 0xffff - -Version 2,3 ------------ - -CIELAB (16 bit) L* 0 -> 100.0 0x0000 -> 0xff00 -CIELAB (16 bit) a* -128.0 -> +127.996 0x0000 -> 0x8000 -> 0xffff -CIELAB (16 bit) b* -128.0 -> +127.996 0x0000 -> 0x8000 -> 0xffff - - -Version 4 ---------- - -CIELAB (16 bit) L* 0 -> 100.0 0x0000 -> 0xffff -CIELAB (16 bit) a* -128.0 -> +127 0x0000 -> 0x8080 -> 0xffff -CIELAB (16 bit) b* -128.0 -> +127 0x0000 -> 0x8080 -> 0xffff - -*/ - -// Conversions -void CMSEXPORT cmsXYZ2xyY(cmsCIExyY* Dest, const cmsCIEXYZ* Source) -{ - cmsFloat64Number ISum; - - ISum = 1./(Source -> X + Source -> Y + Source -> Z); - - Dest -> x = (Source -> X) * ISum; - Dest -> y = (Source -> Y) * ISum; - Dest -> Y = Source -> Y; -} - -void CMSEXPORT cmsxyY2XYZ(cmsCIEXYZ* Dest, const cmsCIExyY* Source) -{ - Dest -> X = (Source -> x / Source -> y) * Source -> Y; - Dest -> Y = Source -> Y; - Dest -> Z = ((1 - Source -> x - Source -> y) / Source -> y) * Source -> Y; -} - -static -cmsFloat64Number f(cmsFloat64Number t) -{ - const cmsFloat64Number Limit = (24.0/116.0) * (24.0/116.0) * (24.0/116.0); - - if (t <= Limit) - return (841.0/108.0) * t + (16.0/116.0); - else - return pow(t, 1.0/3.0); -} - -static -cmsFloat64Number f_1(cmsFloat64Number t) -{ - const cmsFloat64Number Limit = (24.0/116.0); - - if (t <= Limit) { - return (108.0/841.0) * (t - (16.0/116.0)); - } - - return t * t * t; -} - - -// Standard XYZ to Lab. it can handle negative XZY numbers in some cases -void CMSEXPORT cmsXYZ2Lab(const cmsCIEXYZ* WhitePoint, cmsCIELab* Lab, const cmsCIEXYZ* xyz) -{ - cmsFloat64Number fx, fy, fz; - - if (WhitePoint == NULL) - WhitePoint = cmsD50_XYZ(); - - fx = f(xyz->X / WhitePoint->X); - fy = f(xyz->Y / WhitePoint->Y); - fz = f(xyz->Z / WhitePoint->Z); - - Lab->L = 116.0*fy - 16.0; - Lab->a = 500.0*(fx - fy); - Lab->b = 200.0*(fy - fz); -} - - -// Standard XYZ to Lab. It can return negative XYZ in some cases -void CMSEXPORT cmsLab2XYZ(const cmsCIEXYZ* WhitePoint, cmsCIEXYZ* xyz, const cmsCIELab* Lab) -{ - cmsFloat64Number x, y, z; - - if (WhitePoint == NULL) - WhitePoint = cmsD50_XYZ(); - - y = (Lab-> L + 16.0) / 116.0; - x = y + 0.002 * Lab -> a; - z = y - 0.005 * Lab -> b; - - xyz -> X = f_1(x) * WhitePoint -> X; - xyz -> Y = f_1(y) * WhitePoint -> Y; - xyz -> Z = f_1(z) * WhitePoint -> Z; - -} - -static -cmsFloat64Number L2float2(cmsUInt16Number v) -{ - return (cmsFloat64Number) v / 652.800; -} - -// the a/b part -static -cmsFloat64Number ab2float2(cmsUInt16Number v) -{ - return ((cmsFloat64Number) v / 256.0) - 128.0; -} - -static -cmsUInt16Number L2Fix2(cmsFloat64Number L) -{ - return _cmsQuickSaturateWord(L * 652.8); -} - -static -cmsUInt16Number ab2Fix2(cmsFloat64Number ab) -{ - return _cmsQuickSaturateWord((ab + 128.0) * 256.0); -} - - -static -cmsFloat64Number L2float4(cmsUInt16Number v) -{ - return (cmsFloat64Number) v / 655.35; -} - -// the a/b part -static -cmsFloat64Number ab2float4(cmsUInt16Number v) -{ - return ((cmsFloat64Number) v / 257.0) - 128.0; -} - - -void CMSEXPORT cmsLabEncoded2FloatV2(cmsCIELab* Lab, const cmsUInt16Number wLab[3]) -{ - Lab->L = L2float2(wLab[0]); - Lab->a = ab2float2(wLab[1]); - Lab->b = ab2float2(wLab[2]); -} - - -void CMSEXPORT cmsLabEncoded2Float(cmsCIELab* Lab, const cmsUInt16Number wLab[3]) -{ - Lab->L = L2float4(wLab[0]); - Lab->a = ab2float4(wLab[1]); - Lab->b = ab2float4(wLab[2]); -} - -static -cmsFloat64Number Clamp_L_doubleV2(cmsFloat64Number L) -{ - const cmsFloat64Number L_max = (cmsFloat64Number) (0xFFFF * 100.0) / 0xFF00; - - if (L < 0) L = 0; - if (L > L_max) L = L_max; - - return L; -} - - -static -cmsFloat64Number Clamp_ab_doubleV2(cmsFloat64Number ab) -{ - if (ab < MIN_ENCODEABLE_ab2) ab = MIN_ENCODEABLE_ab2; - if (ab > MAX_ENCODEABLE_ab2) ab = MAX_ENCODEABLE_ab2; - - return ab; -} - -void CMSEXPORT cmsFloat2LabEncodedV2(cmsUInt16Number wLab[3], const cmsCIELab* fLab) -{ - cmsCIELab Lab; - - Lab.L = Clamp_L_doubleV2(fLab ->L); - Lab.a = Clamp_ab_doubleV2(fLab ->a); - Lab.b = Clamp_ab_doubleV2(fLab ->b); - - wLab[0] = L2Fix2(Lab.L); - wLab[1] = ab2Fix2(Lab.a); - wLab[2] = ab2Fix2(Lab.b); -} - - -static -cmsFloat64Number Clamp_L_doubleV4(cmsFloat64Number L) -{ - if (L < 0) L = 0; - if (L > 100.0) L = 100.0; - - return L; -} - -static -cmsFloat64Number Clamp_ab_doubleV4(cmsFloat64Number ab) -{ - if (ab < MIN_ENCODEABLE_ab4) ab = MIN_ENCODEABLE_ab4; - if (ab > MAX_ENCODEABLE_ab4) ab = MAX_ENCODEABLE_ab4; - - return ab; -} - -static -cmsUInt16Number L2Fix4(cmsFloat64Number L) -{ - return _cmsQuickSaturateWord(L * 655.35); -} - -static -cmsUInt16Number ab2Fix4(cmsFloat64Number ab) -{ - return _cmsQuickSaturateWord((ab + 128.0) * 257.0); -} - -void CMSEXPORT cmsFloat2LabEncoded(cmsUInt16Number wLab[3], const cmsCIELab* fLab) -{ - cmsCIELab Lab; - - Lab.L = Clamp_L_doubleV4(fLab ->L); - Lab.a = Clamp_ab_doubleV4(fLab ->a); - Lab.b = Clamp_ab_doubleV4(fLab ->b); - - wLab[0] = L2Fix4(Lab.L); - wLab[1] = ab2Fix4(Lab.a); - wLab[2] = ab2Fix4(Lab.b); -} - -// Auxiliar: convert to Radians -static -cmsFloat64Number RADIANS(cmsFloat64Number deg) -{ - return (deg * M_PI) / 180.; -} - - -// Auxiliar: atan2 but operating in degrees and returning 0 if a==b==0 -static -cmsFloat64Number atan2deg(cmsFloat64Number a, cmsFloat64Number b) -{ - cmsFloat64Number h; - - if (a == 0 && b == 0) - h = 0; - else - h = atan2(a, b); - - h *= (180. / M_PI); - - while (h > 360.) - h -= 360.; - - while ( h < 0) - h += 360.; - - return h; -} - - -// Auxiliar: Square -static -cmsFloat64Number Sqr(cmsFloat64Number v) -{ - return v * v; -} -// From cylindrical coordinates. No check is performed, then negative values are allowed -void CMSEXPORT cmsLab2LCh(cmsCIELCh* LCh, const cmsCIELab* Lab) -{ - LCh -> L = Lab -> L; - LCh -> C = pow(Sqr(Lab ->a) + Sqr(Lab ->b), 0.5); - LCh -> h = atan2deg(Lab ->b, Lab ->a); -} - - -// To cylindrical coordinates. No check is performed, then negative values are allowed -void CMSEXPORT cmsLCh2Lab(cmsCIELab* Lab, const cmsCIELCh* LCh) -{ - cmsFloat64Number h = (LCh -> h * M_PI) / 180.0; - - Lab -> L = LCh -> L; - Lab -> a = LCh -> C * cos(h); - Lab -> b = LCh -> C * sin(h); -} - -// In XYZ All 3 components are encoded using 1.15 fixed point -static -cmsUInt16Number XYZ2Fix(cmsFloat64Number d) -{ - return _cmsQuickSaturateWord(d * 32768.0); -} - -void CMSEXPORT cmsFloat2XYZEncoded(cmsUInt16Number XYZ[3], const cmsCIEXYZ* fXYZ) -{ - cmsCIEXYZ xyz; - - xyz.X = fXYZ -> X; - xyz.Y = fXYZ -> Y; - xyz.Z = fXYZ -> Z; - - // Clamp to encodeable values. - if (xyz.Y <= 0) { - - xyz.X = 0; - xyz.Y = 0; - xyz.Z = 0; - } - - if (xyz.X > MAX_ENCODEABLE_XYZ) - xyz.X = MAX_ENCODEABLE_XYZ; - - if (xyz.X < 0) - xyz.X = 0; - - if (xyz.Y > MAX_ENCODEABLE_XYZ) - xyz.Y = MAX_ENCODEABLE_XYZ; - - if (xyz.Y < 0) - xyz.Y = 0; - - if (xyz.Z > MAX_ENCODEABLE_XYZ) - xyz.Z = MAX_ENCODEABLE_XYZ; - - if (xyz.Z < 0) - xyz.Z = 0; - - - XYZ[0] = XYZ2Fix(xyz.X); - XYZ[1] = XYZ2Fix(xyz.Y); - XYZ[2] = XYZ2Fix(xyz.Z); -} - - -// To convert from Fixed 1.15 point to cmsFloat64Number -static -cmsFloat64Number XYZ2float(cmsUInt16Number v) -{ - cmsS15Fixed16Number fix32; - - // From 1.15 to 15.16 - fix32 = v << 1; - - // From fixed 15.16 to cmsFloat64Number - return _cms15Fixed16toDouble(fix32); -} - - -void CMSEXPORT cmsXYZEncoded2Float(cmsCIEXYZ* fXYZ, const cmsUInt16Number XYZ[3]) -{ - fXYZ -> X = XYZ2float(XYZ[0]); - fXYZ -> Y = XYZ2float(XYZ[1]); - fXYZ -> Z = XYZ2float(XYZ[2]); -} - - -// Returns dE on two Lab values -cmsFloat64Number CMSEXPORT cmsDeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2) -{ - cmsFloat64Number dL, da, db; - - dL = fabs(Lab1 -> L - Lab2 -> L); - da = fabs(Lab1 -> a - Lab2 -> a); - db = fabs(Lab1 -> b - Lab2 -> b); - - return pow(Sqr(dL) + Sqr(da) + Sqr(db), 0.5); -} - - -// Return the CIE94 Delta E -cmsFloat64Number CMSEXPORT cmsCIE94DeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2) -{ - cmsCIELCh LCh1, LCh2; - cmsFloat64Number dE, dL, dC, dh, dhsq; - cmsFloat64Number c12, sc, sh; - - dL = fabs(Lab1 ->L - Lab2 ->L); - - cmsLab2LCh(&LCh1, Lab1); - cmsLab2LCh(&LCh2, Lab2); - - dC = fabs(LCh1.C - LCh2.C); - dE = cmsDeltaE(Lab1, Lab2); - - dhsq = Sqr(dE) - Sqr(dL) - Sqr(dC); - if (dhsq < 0) - dh = 0; - else - dh = pow(dhsq, 0.5); - - c12 = sqrt(LCh1.C * LCh2.C); - - sc = 1.0 + (0.048 * c12); - sh = 1.0 + (0.014 * c12); - - return sqrt(Sqr(dL) + Sqr(dC) / Sqr(sc) + Sqr(dh) / Sqr(sh)); -} - - -// Auxiliary -static -cmsFloat64Number ComputeLBFD(const cmsCIELab* Lab) -{ - cmsFloat64Number yt; - - if (Lab->L > 7.996969) - yt = (Sqr((Lab->L+16)/116)*((Lab->L+16)/116))*100; - else - yt = 100 * (Lab->L / 903.3); - - return (54.6 * (M_LOG10E * (log(yt + 1.5))) - 9.6); -} - - - -// bfd - gets BFD(1:1) difference between Lab1, Lab2 -cmsFloat64Number CMSEXPORT cmsBFDdeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2) -{ - cmsFloat64Number lbfd1,lbfd2,AveC,Aveh,dE,deltaL, - deltaC,deltah,dc,t,g,dh,rh,rc,rt,bfd; - cmsCIELCh LCh1, LCh2; - - - lbfd1 = ComputeLBFD(Lab1); - lbfd2 = ComputeLBFD(Lab2); - deltaL = lbfd2 - lbfd1; - - cmsLab2LCh(&LCh1, Lab1); - cmsLab2LCh(&LCh2, Lab2); - - deltaC = LCh2.C - LCh1.C; - AveC = (LCh1.C+LCh2.C)/2; - Aveh = (LCh1.h+LCh2.h)/2; - - dE = cmsDeltaE(Lab1, Lab2); - - if (Sqr(dE)>(Sqr(Lab2->L-Lab1->L)+Sqr(deltaC))) - deltah = sqrt(Sqr(dE)-Sqr(Lab2->L-Lab1->L)-Sqr(deltaC)); - else - deltah =0; - - - dc = 0.035 * AveC / (1 + 0.00365 * AveC)+0.521; - g = sqrt(Sqr(Sqr(AveC))/(Sqr(Sqr(AveC))+14000)); - t = 0.627+(0.055*cos((Aveh-254)/(180/M_PI))- - 0.040*cos((2*Aveh-136)/(180/M_PI))+ - 0.070*cos((3*Aveh-31)/(180/M_PI))+ - 0.049*cos((4*Aveh+114)/(180/M_PI))- - 0.015*cos((5*Aveh-103)/(180/M_PI))); - - dh = dc*(g*t+1-g); - rh = -0.260*cos((Aveh-308)/(180/M_PI))- - 0.379*cos((2*Aveh-160)/(180/M_PI))- - 0.636*cos((3*Aveh+254)/(180/M_PI))+ - 0.226*cos((4*Aveh+140)/(180/M_PI))- - 0.194*cos((5*Aveh+280)/(180/M_PI)); - - rc = sqrt((AveC*AveC*AveC*AveC*AveC*AveC)/((AveC*AveC*AveC*AveC*AveC*AveC)+70000000)); - rt = rh*rc; - - bfd = sqrt(Sqr(deltaL)+Sqr(deltaC/dc)+Sqr(deltah/dh)+(rt*(deltaC/dc)*(deltah/dh))); - - return bfd; -} - - -// cmc - CMC(l:c) difference between Lab1, Lab2 -cmsFloat64Number CMSEXPORT cmsCMCdeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2, cmsFloat64Number l, cmsFloat64Number c) -{ - cmsFloat64Number dE,dL,dC,dh,sl,sc,sh,t,f,cmc; - cmsCIELCh LCh1, LCh2; - - if (Lab1 ->L == 0 && Lab2 ->L == 0) return 0; - - cmsLab2LCh(&LCh1, Lab1); - cmsLab2LCh(&LCh2, Lab2); - - - dL = Lab2->L-Lab1->L; - dC = LCh2.C-LCh1.C; - - dE = cmsDeltaE(Lab1, Lab2); - - if (Sqr(dE)>(Sqr(dL)+Sqr(dC))) - dh = sqrt(Sqr(dE)-Sqr(dL)-Sqr(dC)); - else - dh =0; - - if ((LCh1.h > 164) && (LCh1.h < 345)) - t = 0.56 + fabs(0.2 * cos(((LCh1.h + 168)/(180/M_PI)))); - else - t = 0.36 + fabs(0.4 * cos(((LCh1.h + 35 )/(180/M_PI)))); - - sc = 0.0638 * LCh1.C / (1 + 0.0131 * LCh1.C) + 0.638; - sl = 0.040975 * Lab1->L /(1 + 0.01765 * Lab1->L); - - if (Lab1->L<16) - sl = 0.511; - - f = sqrt((LCh1.C * LCh1.C * LCh1.C * LCh1.C)/((LCh1.C * LCh1.C * LCh1.C * LCh1.C)+1900)); - sh = sc*(t*f+1-f); - cmc = sqrt(Sqr(dL/(l*sl))+Sqr(dC/(c*sc))+Sqr(dh/sh)); - - return cmc; -} - -// dE2000 The weightings KL, KC and KH can be modified to reflect the relative -// importance of lightness, chroma and hue in different industrial applications -cmsFloat64Number CMSEXPORT cmsCIE2000DeltaE(const cmsCIELab* Lab1, const cmsCIELab* Lab2, - cmsFloat64Number Kl, cmsFloat64Number Kc, cmsFloat64Number Kh) -{ - cmsFloat64Number L1 = Lab1->L; - cmsFloat64Number a1 = Lab1->a; - cmsFloat64Number b1 = Lab1->b; - cmsFloat64Number C = sqrt( Sqr(a1) + Sqr(b1) ); - - cmsFloat64Number Ls = Lab2 ->L; - cmsFloat64Number as = Lab2 ->a; - cmsFloat64Number bs = Lab2 ->b; - cmsFloat64Number Cs = sqrt( Sqr(as) + Sqr(bs) ); - - cmsFloat64Number G = 0.5 * ( 1 - sqrt(pow((C + Cs) / 2 , 7.0) / (pow((C + Cs) / 2, 7.0) + pow(25.0, 7.0) ) )); - - cmsFloat64Number a_p = (1 + G ) * a1; - cmsFloat64Number b_p = b1; - cmsFloat64Number C_p = sqrt( Sqr(a_p) + Sqr(b_p)); - cmsFloat64Number h_p = atan2deg(b_p, a_p); - - - cmsFloat64Number a_ps = (1 + G) * as; - cmsFloat64Number b_ps = bs; - cmsFloat64Number C_ps = sqrt(Sqr(a_ps) + Sqr(b_ps)); - cmsFloat64Number h_ps = atan2deg(b_ps, a_ps); - - cmsFloat64Number meanC_p =(C_p + C_ps) / 2; - - cmsFloat64Number hps_plus_hp = h_ps + h_p; - cmsFloat64Number hps_minus_hp = h_ps - h_p; - - cmsFloat64Number meanh_p = fabs(hps_minus_hp) <= 180.000001 ? (hps_plus_hp)/2 : - (hps_plus_hp) < 360 ? (hps_plus_hp + 360)/2 : - (hps_plus_hp - 360)/2; - - cmsFloat64Number delta_h = (hps_minus_hp) <= -180.000001 ? (hps_minus_hp + 360) : - (hps_minus_hp) > 180 ? (hps_minus_hp - 360) : - (hps_minus_hp); - cmsFloat64Number delta_L = (Ls - L1); - cmsFloat64Number delta_C = (C_ps - C_p ); - - - cmsFloat64Number delta_H =2 * sqrt(C_ps*C_p) * sin(RADIANS(delta_h) / 2); - - cmsFloat64Number T = 1 - 0.17 * cos(RADIANS(meanh_p-30)) - + 0.24 * cos(RADIANS(2*meanh_p)) - + 0.32 * cos(RADIANS(3*meanh_p + 6)) - - 0.2 * cos(RADIANS(4*meanh_p - 63)); - - cmsFloat64Number Sl = 1 + (0.015 * Sqr((Ls + L1) /2- 50) )/ sqrt(20 + Sqr( (Ls+L1)/2 - 50) ); - - cmsFloat64Number Sc = 1 + 0.045 * (C_p + C_ps)/2; - cmsFloat64Number Sh = 1 + 0.015 * ((C_ps + C_p)/2) * T; - - cmsFloat64Number delta_ro = 30 * exp( -Sqr(((meanh_p - 275 ) / 25))); - - cmsFloat64Number Rc = 2 * sqrt(( pow(meanC_p, 7.0) )/( pow(meanC_p, 7.0) + pow(25.0, 7.0))); - - cmsFloat64Number Rt = -sin(2 * RADIANS(delta_ro)) * Rc; - - cmsFloat64Number deltaE00 = sqrt( Sqr(delta_L /(Sl * Kl)) + - Sqr(delta_C/(Sc * Kc)) + - Sqr(delta_H/(Sh * Kh)) + - Rt*(delta_C/(Sc * Kc)) * (delta_H / (Sh * Kh))); - - return deltaE00; -} - -// This function returns a number of gridpoints to be used as LUT table. It assumes same number -// of gripdpoints in all dimensions. Flags may override the choice. -int _cmsReasonableGridpointsByColorspace(cmsColorSpaceSignature Colorspace, cmsUInt32Number dwFlags) -{ - int nChannels; - - // Already specified? - if (dwFlags & 0x00FF0000) { - // Yes, grab'em - return (dwFlags >> 16) & 0xFF; - } - - nChannels = cmsChannelsOf(Colorspace); - - // HighResPrecalc is maximum resolution - if (dwFlags & cmsFLAGS_HIGHRESPRECALC) { - - if (nChannels > 4) - return 7; // 7 for Hifi - - if (nChannels == 4) // 23 for CMYK - return 23; - - return 49; // 49 for RGB and others - } - - - // LowResPrecal is lower resolution - if (dwFlags & cmsFLAGS_LOWRESPRECALC) { - - if (nChannels > 4) - return 6; // 6 for more than 4 channels - - if (nChannels == 1) - return 33; // For monochrome - - return 17; // 17 for remaining - } - - // Default values - if (nChannels > 4) - return 7; // 7 for Hifi - - if (nChannels == 4) - return 17; // 17 for CMYK - - return 33; // 33 for RGB -} - - -cmsBool _cmsEndPointsBySpace(cmsColorSpaceSignature Space, - cmsUInt16Number **White, - cmsUInt16Number **Black, - cmsUInt32Number *nOutputs) -{ - // Only most common spaces - - static cmsUInt16Number RGBblack[4] = { 0, 0, 0 }; - static cmsUInt16Number RGBwhite[4] = { 0xffff, 0xffff, 0xffff }; - static cmsUInt16Number CMYKblack[4] = { 0xffff, 0xffff, 0xffff, 0xffff }; // 400% of ink - static cmsUInt16Number CMYKwhite[4] = { 0, 0, 0, 0 }; - static cmsUInt16Number LABblack[4] = { 0, 0x8080, 0x8080 }; // V4 Lab encoding - static cmsUInt16Number LABwhite[4] = { 0xFFFF, 0x8080, 0x8080 }; - static cmsUInt16Number CMYblack[4] = { 0xffff, 0xffff, 0xffff }; - static cmsUInt16Number CMYwhite[4] = { 0, 0, 0 }; - static cmsUInt16Number Grayblack[4] = { 0 }; - static cmsUInt16Number GrayWhite[4] = { 0xffff }; - - switch (Space) { - - case cmsSigGrayData: if (White) *White = GrayWhite; - if (Black) *Black = Grayblack; - if (nOutputs) *nOutputs = 1; - return TRUE; - - case cmsSigRgbData: if (White) *White = RGBwhite; - if (Black) *Black = RGBblack; - if (nOutputs) *nOutputs = 3; - return TRUE; - - case cmsSigLabData: if (White) *White = LABwhite; - if (Black) *Black = LABblack; - if (nOutputs) *nOutputs = 3; - return TRUE; - - case cmsSigCmykData: if (White) *White = CMYKwhite; - if (Black) *Black = CMYKblack; - if (nOutputs) *nOutputs = 4; - return TRUE; - - case cmsSigCmyData: if (White) *White = CMYwhite; - if (Black) *Black = CMYblack; - if (nOutputs) *nOutputs = 3; - return TRUE; - - default:; - } - - return FALSE; -} - - - -// Several utilities ------------------------------------------------------- - -// Translate from our colorspace to ICC representation - -cmsColorSpaceSignature CMSEXPORT _cmsICCcolorSpace(int OurNotation) -{ - switch (OurNotation) { - - case 1: - case PT_GRAY: return cmsSigGrayData; - - case 2: - case PT_RGB: return cmsSigRgbData; - - case PT_CMY: return cmsSigCmyData; - case PT_CMYK: return cmsSigCmykData; - case PT_YCbCr:return cmsSigYCbCrData; - case PT_YUV: return cmsSigLuvData; - case PT_XYZ: return cmsSigXYZData; - - case PT_LabV2: - case PT_Lab: return cmsSigLabData; - - case PT_YUVK: return cmsSigLuvKData; - case PT_HSV: return cmsSigHsvData; - case PT_HLS: return cmsSigHlsData; - case PT_Yxy: return cmsSigYxyData; - - case PT_MCH1: return cmsSigMCH1Data; - case PT_MCH2: return cmsSigMCH2Data; - case PT_MCH3: return cmsSigMCH3Data; - case PT_MCH4: return cmsSigMCH4Data; - case PT_MCH5: return cmsSigMCH5Data; - case PT_MCH6: return cmsSigMCH6Data; - case PT_MCH7: return cmsSigMCH7Data; - case PT_MCH8: return cmsSigMCH8Data; - - case PT_MCH9: return cmsSigMCH9Data; - case PT_MCH10: return cmsSigMCHAData; - case PT_MCH11: return cmsSigMCHBData; - case PT_MCH12: return cmsSigMCHCData; - case PT_MCH13: return cmsSigMCHDData; - case PT_MCH14: return cmsSigMCHEData; - case PT_MCH15: return cmsSigMCHFData; - - default: return (cmsColorSpaceSignature) (-1); - } -} - - -int CMSEXPORT _cmsLCMScolorSpace(cmsColorSpaceSignature ProfileSpace) -{ - switch (ProfileSpace) { - - case cmsSigGrayData: return PT_GRAY; - case cmsSigRgbData: return PT_RGB; - case cmsSigCmyData: return PT_CMY; - case cmsSigCmykData: return PT_CMYK; - case cmsSigYCbCrData:return PT_YCbCr; - case cmsSigLuvData: return PT_YUV; - case cmsSigXYZData: return PT_XYZ; - case cmsSigLabData: return PT_Lab; - case cmsSigLuvKData: return PT_YUVK; - case cmsSigHsvData: return PT_HSV; - case cmsSigHlsData: return PT_HLS; - case cmsSigYxyData: return PT_Yxy; - - case cmsSig1colorData: - case cmsSigMCH1Data: return PT_MCH1; - - case cmsSig2colorData: - case cmsSigMCH2Data: return PT_MCH2; - - case cmsSig3colorData: - case cmsSigMCH3Data: return PT_MCH3; - - case cmsSig4colorData: - case cmsSigMCH4Data: return PT_MCH4; - - case cmsSig5colorData: - case cmsSigMCH5Data: return PT_MCH5; - - case cmsSig6colorData: - case cmsSigMCH6Data: return PT_MCH6; - - case cmsSigMCH7Data: - case cmsSig7colorData:return PT_MCH7; - - case cmsSigMCH8Data: - case cmsSig8colorData:return PT_MCH8; - - case cmsSigMCH9Data: - case cmsSig9colorData:return PT_MCH9; - - case cmsSigMCHAData: - case cmsSig10colorData:return PT_MCH10; - - case cmsSigMCHBData: - case cmsSig11colorData:return PT_MCH11; - - case cmsSigMCHCData: - case cmsSig12colorData:return PT_MCH12; - - case cmsSigMCHDData: - case cmsSig13colorData:return PT_MCH13; - - case cmsSigMCHEData: - case cmsSig14colorData:return PT_MCH14; - - case cmsSigMCHFData: - case cmsSig15colorData:return PT_MCH15; - - default: return (cmsColorSpaceSignature) (-1); - } -} - - -cmsUInt32Number CMSEXPORT cmsChannelsOf(cmsColorSpaceSignature ColorSpace) -{ - switch (ColorSpace) { - - case cmsSigMCH1Data: - case cmsSig1colorData: - case cmsSigGrayData: return 1; - - case cmsSigMCH2Data: - case cmsSig2colorData: return 2; - - case cmsSigXYZData: - case cmsSigLabData: - case cmsSigLuvData: - case cmsSigYCbCrData: - case cmsSigYxyData: - case cmsSigRgbData: - case cmsSigHsvData: - case cmsSigHlsData: - case cmsSigCmyData: - case cmsSigMCH3Data: - case cmsSig3colorData: return 3; - - case cmsSigLuvKData: - case cmsSigCmykData: - case cmsSigMCH4Data: - case cmsSig4colorData: return 4; - - case cmsSigMCH5Data: - case cmsSig5colorData: return 5; - - case cmsSigMCH6Data: - case cmsSig6colorData: return 6; - - case cmsSigMCH7Data: - case cmsSig7colorData: return 7; - - case cmsSigMCH8Data: - case cmsSig8colorData: return 8; - - case cmsSigMCH9Data: - case cmsSig9colorData: return 9; - - case cmsSigMCHAData: - case cmsSig10colorData: return 10; - - case cmsSigMCHBData: - case cmsSig11colorData: return 11; - - case cmsSigMCHCData: - case cmsSig12colorData: return 12; - - case cmsSigMCHDData: - case cmsSig13colorData: return 13; - - case cmsSigMCHEData: - case cmsSig14colorData: return 14; - - case cmsSigMCHFData: - case cmsSig15colorData: return 15; - - default: return 3; - } -} diff --git a/third_party/lcms2-2.6/src/cmsplugin.c b/third_party/lcms2-2.6/src/cmsplugin.c deleted file mode 100644 index 42c4002b55..0000000000 --- a/third_party/lcms2-2.6/src/cmsplugin.c +++ /dev/null @@ -1,959 +0,0 @@ -//--------------------------------------------------------------------------------- -// -// Little Color Management System -// Copyright (c) 1998-2010 Marti Maria Saguer -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the Software -// is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// -//--------------------------------------------------------------------------------- -// - -#include "lcms2_internal.h" - - -// ---------------------------------------------------------------------------------- -// Encoding & Decoding support functions -// ---------------------------------------------------------------------------------- - -// Little-Endian to Big-Endian - -// Adjust a word value after being readed/ before being written from/to an ICC profile -cmsUInt16Number CMSEXPORT _cmsAdjustEndianess16(cmsUInt16Number Word) -{ -#ifndef CMS_USE_BIG_ENDIAN - - cmsUInt8Number* pByte = (cmsUInt8Number*) &Word; - cmsUInt8Number tmp; - - tmp = pByte[0]; - pByte[0] = pByte[1]; - pByte[1] = tmp; -#endif - - return Word; -} - - -// Transports to properly encoded values - note that icc profiles does use big endian notation. - -// 1 2 3 4 -// 4 3 2 1 - -cmsUInt32Number CMSEXPORT _cmsAdjustEndianess32(cmsUInt32Number DWord) -{ -#ifndef CMS_USE_BIG_ENDIAN - - cmsUInt8Number* pByte = (cmsUInt8Number*) &DWord; - cmsUInt8Number temp1; - cmsUInt8Number temp2; - - temp1 = *pByte++; - temp2 = *pByte++; - *(pByte-1) = *pByte; - *pByte++ = temp2; - *(pByte-3) = *pByte; - *pByte = temp1; -#endif - return DWord; -} - -// 1 2 3 4 5 6 7 8 -// 8 7 6 5 4 3 2 1 - -void CMSEXPORT _cmsAdjustEndianess64(cmsUInt64Number* Result, cmsUInt64Number* QWord) -{ - -#ifndef CMS_USE_BIG_ENDIAN - - cmsUInt8Number* pIn = (cmsUInt8Number*) QWord; - cmsUInt8Number* pOut = (cmsUInt8Number*) Result; - - _cmsAssert(Result != NULL); - - pOut[7] = pIn[0]; - pOut[6] = pIn[1]; - pOut[5] = pIn[2]; - pOut[4] = pIn[3]; - pOut[3] = pIn[4]; - pOut[2] = pIn[5]; - pOut[1] = pIn[6]; - pOut[0] = pIn[7]; - -#else - _cmsAssert(Result != NULL); - -# ifdef CMS_DONT_USE_INT64 - (*Result)[0] = QWord[0]; - (*Result)[1] = QWord[1]; -# else - *Result = *QWord; -# endif -#endif -} - -// Auxiliar -- read 8, 16 and 32-bit numbers -cmsBool CMSEXPORT _cmsReadUInt8Number(cmsIOHANDLER* io, cmsUInt8Number* n) -{ - cmsUInt8Number tmp; - - _cmsAssert(io != NULL); - - if (io -> Read(io, &tmp, sizeof(cmsUInt8Number), 1) != 1) - return FALSE; - - if (n != NULL) *n = tmp; - return TRUE; -} - -cmsBool CMSEXPORT _cmsReadUInt16Number(cmsIOHANDLER* io, cmsUInt16Number* n) -{ - cmsUInt16Number tmp; - - _cmsAssert(io != NULL); - - if (io -> Read(io, &tmp, sizeof(cmsUInt16Number), 1) != 1) - return FALSE; - - if (n != NULL) *n = _cmsAdjustEndianess16(tmp); - return TRUE; -} - -cmsBool CMSEXPORT _cmsReadUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, cmsUInt16Number* Array) -{ - cmsUInt32Number i; - - _cmsAssert(io != NULL); - - for (i=0; i < n; i++) { - - if (Array != NULL) { - if (!_cmsReadUInt16Number(io, Array + i)) return FALSE; - } - else { - if (!_cmsReadUInt16Number(io, NULL)) return FALSE; - } - - } - return TRUE; -} - -cmsBool CMSEXPORT _cmsReadUInt32Number(cmsIOHANDLER* io, cmsUInt32Number* n) -{ - cmsUInt32Number tmp; - - _cmsAssert(io != NULL); - - if (io -> Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1) - return FALSE; - - if (n != NULL) *n = _cmsAdjustEndianess32(tmp); - return TRUE; -} - -cmsBool CMSEXPORT _cmsReadFloat32Number(cmsIOHANDLER* io, cmsFloat32Number* n) -{ - cmsUInt32Number tmp; - - _cmsAssert(io != NULL); - - if (io -> Read(io, &tmp, sizeof(cmsFloat32Number), 1) != 1) - return FALSE; - - if (n != NULL) { - - tmp = _cmsAdjustEndianess32(tmp); - *n = *(cmsFloat32Number*) &tmp; - if (isnan(*n)) - return FALSE; - } - - // fpclassify() required by C99 - return (fpclassify(*n) == FP_ZERO) || (fpclassify(*n) == FP_NORMAL); -} - - -cmsBool CMSEXPORT _cmsReadUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n) -{ - cmsUInt64Number tmp; - - _cmsAssert(io != NULL); - - if (io -> Read(io, &tmp, sizeof(cmsUInt64Number), 1) != 1) - return FALSE; - - if (n != NULL) _cmsAdjustEndianess64(n, &tmp); - return TRUE; -} - - -cmsBool CMSEXPORT _cmsRead15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number* n) -{ - cmsUInt32Number tmp; - - _cmsAssert(io != NULL); - - if (io -> Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1) - return FALSE; - - if (n != NULL) { - *n = _cms15Fixed16toDouble(_cmsAdjustEndianess32(tmp)); - } - - return TRUE; -} - - -// Jun-21-2000: Some profiles (those that comes with W2K) comes -// with the media white (media black?) x 100. Add a sanity check - -static -void NormalizeXYZ(cmsCIEXYZ* Dest) -{ - while (Dest -> X > 2. && - Dest -> Y > 2. && - Dest -> Z > 2.) { - - Dest -> X /= 10.; - Dest -> Y /= 10.; - Dest -> Z /= 10.; - } -} - -cmsBool CMSEXPORT _cmsReadXYZNumber(cmsIOHANDLER* io, cmsCIEXYZ* XYZ) -{ - cmsEncodedXYZNumber xyz; - - _cmsAssert(io != NULL); - - if (io ->Read(io, &xyz, sizeof(cmsEncodedXYZNumber), 1) != 1) return FALSE; - - if (XYZ != NULL) { - - XYZ->X = _cms15Fixed16toDouble(_cmsAdjustEndianess32(xyz.X)); - XYZ->Y = _cms15Fixed16toDouble(_cmsAdjustEndianess32(xyz.Y)); - XYZ->Z = _cms15Fixed16toDouble(_cmsAdjustEndianess32(xyz.Z)); - - NormalizeXYZ(XYZ); - } - return TRUE; -} - -cmsBool CMSEXPORT _cmsWriteUInt8Number(cmsIOHANDLER* io, cmsUInt8Number n) -{ - _cmsAssert(io != NULL); - - if (io -> Write(io, sizeof(cmsUInt8Number), &n) != 1) - return FALSE; - - return TRUE; -} - -cmsBool CMSEXPORT _cmsWriteUInt16Number(cmsIOHANDLER* io, cmsUInt16Number n) -{ - cmsUInt16Number tmp; - - _cmsAssert(io != NULL); - - tmp = _cmsAdjustEndianess16(n); - if (io -> Write(io, sizeof(cmsUInt16Number), &tmp) != 1) - return FALSE; - - return TRUE; -} - -cmsBool CMSEXPORT _cmsWriteUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, const cmsUInt16Number* Array) -{ - cmsUInt32Number i; - - _cmsAssert(io != NULL); - _cmsAssert(Array != NULL); - - for (i=0; i < n; i++) { - if (!_cmsWriteUInt16Number(io, Array[i])) return FALSE; - } - - return TRUE; -} - -cmsBool CMSEXPORT _cmsWriteUInt32Number(cmsIOHANDLER* io, cmsUInt32Number n) -{ - cmsUInt32Number tmp; - - _cmsAssert(io != NULL); - - tmp = _cmsAdjustEndianess32(n); - if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1) - return FALSE; - - return TRUE; -} - - -cmsBool CMSEXPORT _cmsWriteFloat32Number(cmsIOHANDLER* io, cmsFloat32Number n) -{ - cmsUInt32Number tmp; - - _cmsAssert(io != NULL); - - tmp = *(cmsUInt32Number*) &n; - tmp = _cmsAdjustEndianess32(tmp); - if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1) - return FALSE; - - return TRUE; -} - -cmsBool CMSEXPORT _cmsWriteUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n) -{ - cmsUInt64Number tmp; - - _cmsAssert(io != NULL); - - _cmsAdjustEndianess64(&tmp, n); - if (io -> Write(io, sizeof(cmsUInt64Number), &tmp) != 1) - return FALSE; - - return TRUE; -} - -cmsBool CMSEXPORT _cmsWrite15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number n) -{ - cmsUInt32Number tmp; - - _cmsAssert(io != NULL); - - tmp = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(n)); - if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1) - return FALSE; - - return TRUE; -} - -cmsBool CMSEXPORT _cmsWriteXYZNumber(cmsIOHANDLER* io, const cmsCIEXYZ* XYZ) -{ - cmsEncodedXYZNumber xyz; - - _cmsAssert(io != NULL); - _cmsAssert(XYZ != NULL); - - xyz.X = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(XYZ->X)); - xyz.Y = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(XYZ->Y)); - xyz.Z = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(XYZ->Z)); - - return io -> Write(io, sizeof(cmsEncodedXYZNumber), &xyz); -} - -// from Fixed point 8.8 to double -cmsFloat64Number CMSEXPORT _cms8Fixed8toDouble(cmsUInt16Number fixed8) -{ - cmsUInt8Number msb, lsb; - - lsb = (cmsUInt8Number) (fixed8 & 0xff); - msb = (cmsUInt8Number) (((cmsUInt16Number) fixed8 >> 8) & 0xff); - - return (cmsFloat64Number) ((cmsFloat64Number) msb + ((cmsFloat64Number) lsb / 256.0)); -} - -cmsUInt16Number CMSEXPORT _cmsDoubleTo8Fixed8(cmsFloat64Number val) -{ - cmsS15Fixed16Number GammaFixed32 = _cmsDoubleTo15Fixed16(val); - return (cmsUInt16Number) ((GammaFixed32 >> 8) & 0xFFFF); -} - -// from Fixed point 15.16 to double -cmsFloat64Number CMSEXPORT _cms15Fixed16toDouble(cmsS15Fixed16Number fix32) -{ - cmsFloat64Number floater, sign, mid; - int Whole, FracPart; - - sign = (fix32 < 0 ? -1 : 1); - fix32 = abs(fix32); - - Whole = (cmsUInt16Number)(fix32 >> 16) & 0xffff; - FracPart = (cmsUInt16Number)(fix32 & 0xffff); - - mid = (cmsFloat64Number) FracPart / 65536.0; - floater = (cmsFloat64Number) Whole + mid; - - return sign * floater; -} - -// from double to Fixed point 15.16 -cmsS15Fixed16Number CMSEXPORT _cmsDoubleTo15Fixed16(cmsFloat64Number v) -{ - return ((cmsS15Fixed16Number) floor((v)*65536.0 + 0.5)); -} - -// Date/Time functions - -void CMSEXPORT _cmsDecodeDateTimeNumber(const cmsDateTimeNumber *Source, struct tm *Dest) -{ - - _cmsAssert(Dest != NULL); - _cmsAssert(Source != NULL); - - Dest->tm_sec = _cmsAdjustEndianess16(Source->seconds); - Dest->tm_min = _cmsAdjustEndianess16(Source->minutes); - Dest->tm_hour = _cmsAdjustEndianess16(Source->hours); - Dest->tm_mday = _cmsAdjustEndianess16(Source->day); - Dest->tm_mon = _cmsAdjustEndianess16(Source->month) - 1; - Dest->tm_year = _cmsAdjustEndianess16(Source->year) - 1900; - Dest->tm_wday = -1; - Dest->tm_yday = -1; - Dest->tm_isdst = 0; -} - -void CMSEXPORT _cmsEncodeDateTimeNumber(cmsDateTimeNumber *Dest, const struct tm *Source) -{ - _cmsAssert(Dest != NULL); - _cmsAssert(Source != NULL); - - Dest->seconds = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_sec); - Dest->minutes = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_min); - Dest->hours = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_hour); - Dest->day = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_mday); - Dest->month = _cmsAdjustEndianess16((cmsUInt16Number) (Source->tm_mon + 1)); - Dest->year = _cmsAdjustEndianess16((cmsUInt16Number) (Source->tm_year + 1900)); -} - -// Read base and return type base -cmsTagTypeSignature CMSEXPORT _cmsReadTypeBase(cmsIOHANDLER* io) -{ - _cmsTagBase Base; - - _cmsAssert(io != NULL); - - if (io -> Read(io, &Base, sizeof(_cmsTagBase), 1) != 1) - return (cmsTagTypeSignature) 0; - - return (cmsTagTypeSignature) _cmsAdjustEndianess32(Base.sig); -} - -// Setup base marker -cmsBool CMSEXPORT _cmsWriteTypeBase(cmsIOHANDLER* io, cmsTagTypeSignature sig) -{ - _cmsTagBase Base; - - _cmsAssert(io != NULL); - - Base.sig = (cmsTagTypeSignature) _cmsAdjustEndianess32(sig); - memset(&Base.reserved, 0, sizeof(Base.reserved)); - return io -> Write(io, sizeof(_cmsTagBase), &Base); -} - -cmsBool CMSEXPORT _cmsReadAlignment(cmsIOHANDLER* io) -{ - cmsUInt8Number Buffer[4]; - cmsUInt32Number NextAligned, At; - cmsUInt32Number BytesToNextAlignedPos; - - _cmsAssert(io != NULL); - - At = io -> Tell(io); - NextAligned = _cmsALIGNLONG(At); - BytesToNextAlignedPos = NextAligned - At; - if (BytesToNextAlignedPos == 0) return TRUE; - if (BytesToNextAlignedPos > 4) return FALSE; - - return (io ->Read(io, Buffer, BytesToNextAlignedPos, 1) == 1); -} - -cmsBool CMSEXPORT _cmsWriteAlignment(cmsIOHANDLER* io) -{ - cmsUInt8Number Buffer[4]; - cmsUInt32Number NextAligned, At; - cmsUInt32Number BytesToNextAlignedPos; - - _cmsAssert(io != NULL); - - At = io -> Tell(io); - NextAligned = _cmsALIGNLONG(At); - BytesToNextAlignedPos = NextAligned - At; - if (BytesToNextAlignedPos == 0) return TRUE; - if (BytesToNextAlignedPos > 4) return FALSE; - - memset(Buffer, 0, BytesToNextAlignedPos); - return io -> Write(io, BytesToNextAlignedPos, Buffer); -} - - -// To deal with text streams. 2K at most -cmsBool CMSEXPORT _cmsIOPrintf(cmsIOHANDLER* io, const char* frm, ...) -{ - va_list args; - int len; - cmsUInt8Number Buffer[2048]; - cmsBool rc; - - _cmsAssert(io != NULL); - _cmsAssert(frm != NULL); - - va_start(args, frm); - - len = vsnprintf((char*) Buffer, 2047, frm, args); - if (len < 0) return FALSE; // Truncated, which is a fatal error for us - - rc = io ->Write(io, len, Buffer); - - va_end(args); - - return rc; -} - - -// Plugin memory management ------------------------------------------------------------------------------------------------- - -// Specialized malloc for plug-ins, that is freed upon exit. -void* _cmsPluginMalloc(cmsContext ContextID, cmsUInt32Number size) -{ - struct _cmsContext_struct* ctx = _cmsGetContext(ContextID); - - if (ctx ->MemPool == NULL) { - - if (ContextID == NULL) { - - ctx->MemPool = _cmsCreateSubAlloc(0, 2*1024); - } - else { - cmsSignalError(ContextID, cmsERROR_CORRUPTION_DETECTED, "NULL memory pool on context"); - return NULL; - } - } - - return _cmsSubAlloc(ctx->MemPool, size); -} - - -// Main plug-in dispatcher -cmsBool CMSEXPORT cmsPlugin(void* Plug_in) -{ - return cmsPluginTHR(NULL, Plug_in); -} - -cmsBool CMSEXPORT cmsPluginTHR(cmsContext id, void* Plug_in) -{ - cmsPluginBase* Plugin; - - for (Plugin = (cmsPluginBase*) Plug_in; - Plugin != NULL; - Plugin = Plugin -> Next) { - - if (Plugin -> Magic != cmsPluginMagicNumber) { - cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin"); - return FALSE; - } - - if (Plugin ->ExpectedVersion > LCMS_VERSION) { - cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "plugin needs Little CMS %d, current version is %d", - Plugin ->ExpectedVersion, LCMS_VERSION); - return FALSE; - } - - switch (Plugin -> Type) { - - case cmsPluginMemHandlerSig: - if (!_cmsRegisterMemHandlerPlugin(id, Plugin)) return FALSE; - break; - - case cmsPluginInterpolationSig: - if (!_cmsRegisterInterpPlugin(id, Plugin)) return FALSE; - break; - - case cmsPluginTagTypeSig: - if (!_cmsRegisterTagTypePlugin(id, Plugin)) return FALSE; - break; - - case cmsPluginTagSig: - if (!_cmsRegisterTagPlugin(id, Plugin)) return FALSE; - break; - - case cmsPluginFormattersSig: - if (!_cmsRegisterFormattersPlugin(id, Plugin)) return FALSE; - break; - - case cmsPluginRenderingIntentSig: - if (!_cmsRegisterRenderingIntentPlugin(id, Plugin)) return FALSE; - break; - - case cmsPluginParametricCurveSig: - if (!_cmsRegisterParametricCurvesPlugin(id, Plugin)) return FALSE; - break; - - case cmsPluginMultiProcessElementSig: - if (!_cmsRegisterMultiProcessElementPlugin(id, Plugin)) return FALSE; - break; - - case cmsPluginOptimizationSig: - if (!_cmsRegisterOptimizationPlugin(id, Plugin)) return FALSE; - break; - - case cmsPluginTransformSig: - if (!_cmsRegisterTransformPlugin(id, Plugin)) return FALSE; - break; - - case cmsPluginMutexSig: - if (!_cmsRegisterMutexPlugin(id, Plugin)) return FALSE; - break; - - default: - cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin type '%X'", Plugin -> Type); - return FALSE; - } - } - - // Keep a reference to the plug-in - return TRUE; -} - - -// Revert all plug-ins to default -void CMSEXPORT cmsUnregisterPlugins(void) -{ - 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 = { - - NULL, // Not in the linked list - 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 -}; - - -// The context pool (linked list head) -static _cmsMutex _cmsContextPoolHeadMutex = CMS_MUTEX_INITIALIZER; -static struct _cmsContext_struct* _cmsContextPoolHead = NULL; - -// Internal, get associated pointer, with guessing. Never returns NULL. -struct _cmsContext_struct* _cmsGetContext(cmsContext ContextID) -{ - struct _cmsContext_struct* id = (struct _cmsContext_struct*) ContextID; - struct _cmsContext_struct* ctx; - - - // On 0, use global settings - if (id == NULL) - return &globalContext; - - // Search - for (ctx = _cmsContextPoolHead; - ctx != NULL; - ctx = ctx ->Next) { - - // Found it? - if (id == ctx) - return ctx; // New-style context, - } - - return &globalContext; -} - - -// 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 >= MemoryClientMax) { - cmsSignalError(ContextID, cmsERROR_RANGE, "Bad context client"); - return NULL; - } - - ctx = _cmsGetContext(ContextID); - ptr = ctx ->chunks[mc]; - - if (ptr != NULL) - return ptr; - - // 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) -{ - 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)); - - // Maintain the linked list (with proper locking) - _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex); - ctx ->Next = _cmsContextPoolHead; - _cmsContextPoolHead = ctx; - _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex); - - 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; -} - -// 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) -{ - 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)); - - // Maintain the linked list - _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex); - ctx ->Next = _cmsContextPoolHead; - _cmsContextPoolHead = ctx; - _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex); - - 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; -} - - - -static -struct _cmsContext_struct* FindPrev(struct _cmsContext_struct* id) -{ - struct _cmsContext_struct* prev; - - // Search for previous - for (prev = _cmsContextPoolHead; - prev != NULL; - prev = prev ->Next) - { - if (prev ->Next == id) - return prev; - } - - return NULL; // List is empty or only one element! -} - -// 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) -{ - if (ContextID != NULL) { - - struct _cmsContext_struct* ctx = (struct _cmsContext_struct*) ContextID; - struct _cmsContext_struct fakeContext; - struct _cmsContext_struct* prev; - - 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; - - // Maintain list - _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex); - if (_cmsContextPoolHead == ctx) { - - _cmsContextPoolHead = ctx->Next; - } - else { - - // Search for previous - for (prev = _cmsContextPoolHead; - prev != NULL; - prev = prev ->Next) - { - if (prev -> Next == ctx) { - prev -> Next = ctx ->Next; - break; - } - } - } - _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex); - - // free the memory block itself - _cmsFree(&fakeContext, ctx); - } -} - -// 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) -{ - return _cmsContextGetClientChunk(ContextID, UserPtr); -} diff --git a/third_party/lcms2-2.6/src/cmsps2.c b/third_party/lcms2-2.6/src/cmsps2.c deleted file mode 100644 index 224b44b542..0000000000 --- a/third_party/lcms2-2.6/src/cmsps2.c +++ /dev/null @@ -1,1597 +0,0 @@ -//--------------------------------------------------------------------------------- -// -// Little Color Management System -// Copyright (c) 1998-2011 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" - -// PostScript ColorRenderingDictionary and ColorSpaceArray - - -#define MAXPSCOLS 60 // Columns on tables - -/* - Implementation - -------------- - - PostScript does use XYZ as its internal PCS. But since PostScript - interpolation tables are limited to 8 bits, I use Lab as a way to - improve the accuracy, favoring perceptual results. So, for the creation - of each CRD, CSA the profiles are converted to Lab via a device - link between profile -> Lab or Lab -> profile. The PS code necessary to - convert Lab <-> XYZ is also included. - - - - Color Space Arrays (CSA) - ================================================================================== - - In order to obtain precision, code chooses between three ways to implement - the device -> XYZ transform. These cases identifies monochrome profiles (often - implemented as a set of curves), matrix-shaper and Pipeline-based. - - Monochrome - ----------- - - This is implemented as /CIEBasedA CSA. The prelinearization curve is - placed into /DecodeA section, and matrix equals to D50. Since here is - no interpolation tables, I do the conversion directly to XYZ - - NOTE: CLUT-based monochrome profiles are NOT supported. So, cmsFLAGS_MATRIXINPUT - flag is forced on such profiles. - - [ /CIEBasedA - << - /DecodeA { transfer function } bind - /MatrixA [D50] - /RangeLMN [ 0.0 cmsD50X 0.0 cmsD50Y 0.0 cmsD50Z ] - /WhitePoint [D50] - /BlackPoint [BP] - /RenderingIntent (intent) - >> - ] - - On simpler profiles, the PCS is already XYZ, so no conversion is required. - - - Matrix-shaper based - ------------------- - - This is implemented both with /CIEBasedABC or /CIEBasedDEF on dependig - of profile implementation. Since here there are no interpolation tables, I do - the conversion directly to XYZ - - - - [ /CIEBasedABC - << - /DecodeABC [ {transfer1} {transfer2} {transfer3} ] - /MatrixABC [Matrix] - /RangeLMN [ 0.0 cmsD50X 0.0 cmsD50Y 0.0 cmsD50Z ] - /DecodeLMN [ { / 2} dup dup ] - /WhitePoint [D50] - /BlackPoint [BP] - /RenderingIntent (intent) - >> - ] - - - CLUT based - ---------- - - Lab is used in such cases. - - [ /CIEBasedDEF - << - /DecodeDEF [ ] - /Table [ p p p [<...>]] - /RangeABC [ 0 1 0 1 0 1] - /DecodeABC[ ] - /RangeLMN [ -0.236 1.254 0 1 -0.635 1.640 ] - % -128/500 1+127/500 0 1 -127/200 1+128/200 - /MatrixABC [ 1 1 1 1 0 0 0 0 -1] - /WhitePoint [D50] - /BlackPoint [BP] - /RenderingIntent (intent) - ] - - - Color Rendering Dictionaries (CRD) - ================================== - These are always implemented as CLUT, and always are using Lab. Since CRD are expected to - be used as resources, the code adds the definition as well. - - << - /ColorRenderingType 1 - /WhitePoint [ D50 ] - /BlackPoint [BP] - /MatrixPQR [ Bradford ] - /RangePQR [-0.125 1.375 -0.125 1.375 -0.125 1.375 ] - /TransformPQR [ - {4 index 3 get div 2 index 3 get mul exch pop exch pop exch pop exch pop } bind - {4 index 4 get div 2 index 4 get mul exch pop exch pop exch pop exch pop } bind - {4 index 5 get div 2 index 5 get mul exch pop exch pop exch pop exch pop } bind - ] - /MatrixABC <...> - /EncodeABC <...> - /RangeABC <.. used for XYZ -> Lab> - /EncodeLMN - /RenderTable [ p p p [<...>]] - - /RenderingIntent (Perceptual) - >> - /Current exch /ColorRendering defineresource pop - - - The following stages are used to convert from XYZ to Lab - -------------------------------------------------------- - - Input is given at LMN stage on X, Y, Z - - Encode LMN gives us f(X/Xn), f(Y/Yn), f(Z/Zn) - - /EncodeLMN [ - - { 0.964200 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind - { 1.000000 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind - { 0.824900 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind - - ] - - - MatrixABC is used to compute f(Y/Yn), f(X/Xn) - f(Y/Yn), f(Y/Yn) - f(Z/Zn) - - | 0 1 0| - | 1 -1 0| - | 0 1 -1| - - /MatrixABC [ 0 1 0 1 -1 1 0 0 -1 ] - - EncodeABC finally gives Lab values. - - /EncodeABC [ - { 116 mul 16 sub 100 div } bind - { 500 mul 128 add 255 div } bind - { 200 mul 128 add 255 div } bind - ] - - The following stages are used to convert Lab to XYZ - ---------------------------------------------------- - - /RangeABC [ 0 1 0 1 0 1] - /DecodeABC [ { 100 mul 16 add 116 div } bind - { 255 mul 128 sub 500 div } bind - { 255 mul 128 sub 200 div } bind - ] - - /MatrixABC [ 1 1 1 1 0 0 0 0 -1] - /DecodeLMN [ - {dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.964200 mul} bind - {dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse } bind - {dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.824900 mul} bind - ] - - -*/ - -/* - - PostScript algorithms discussion. - ========================================================================================================= - - 1D interpolation algorithm - - - 1D interpolation (float) - ------------------------ - - val2 = Domain * Value; - - cell0 = (int) floor(val2); - cell1 = (int) ceil(val2); - - rest = val2 - cell0; - - y0 = LutTable[cell0] ; - y1 = LutTable[cell1] ; - - y = y0 + (y1 - y0) * rest; - - - - PostScript code Stack - ================================================ - - { % v - - [array] % v tab - dup % v tab tab - length 1 sub % v tab dom - - 3 -1 roll % tab dom v - - mul % tab val2 - dup % tab val2 val2 - dup % tab val2 val2 val2 - floor cvi % tab val2 val2 cell0 - exch % tab val2 cell0 val2 - ceiling cvi % tab val2 cell0 cell1 - - 3 index % tab val2 cell0 cell1 tab - exch % tab val2 cell0 tab cell1 - get % tab val2 cell0 y1 - - 4 -1 roll % val2 cell0 y1 tab - 3 -1 roll % val2 y1 tab cell0 - get % val2 y1 y0 - - dup % val2 y1 y0 y0 - 3 1 roll % val2 y0 y1 y0 - - sub % val2 y0 (y1-y0) - 3 -1 roll % y0 (y1-y0) val2 - dup % y0 (y1-y0) val2 val2 - floor cvi % y0 (y1-y0) val2 floor(val2) - sub % y0 (y1-y0) rest - mul % y0 t1 - add % y - 65535 div % result - - } bind - - -*/ - - -// This struct holds the memory block currently being write -typedef struct { - _cmsStageCLutData* Pipeline; - cmsIOHANDLER* m; - - int FirstComponent; - int SecondComponent; - - const char* PreMaj; - const char* PostMaj; - const char* PreMin; - const char* PostMin; - - int FixWhite; // Force mapping of pure white - - cmsColorSpaceSignature ColorSpace; // ColorSpace of profile - - -} cmsPsSamplerCargo; - -static int _cmsPSActualColumn = 0; - - -// Convert to byte -static -cmsUInt8Number Word2Byte(cmsUInt16Number w) -{ - return (cmsUInt8Number) floor((cmsFloat64Number) w / 257.0 + 0.5); -} - - -// Convert to byte (using ICC2 notation) -/* -static -cmsUInt8Number L2Byte(cmsUInt16Number w) -{ - int ww = w + 0x0080; - - if (ww > 0xFFFF) return 0xFF; - - return (cmsUInt8Number) ((cmsUInt16Number) (ww >> 8) & 0xFF); -} -*/ - -// Write a cooked byte - -static -void WriteByte(cmsIOHANDLER* m, cmsUInt8Number b) -{ - _cmsIOPrintf(m, "%02x", b); - _cmsPSActualColumn += 2; - - if (_cmsPSActualColumn > MAXPSCOLS) { - - _cmsIOPrintf(m, "\n"); - _cmsPSActualColumn = 0; - } -} - -// ----------------------------------------------------------------- PostScript generation - - -// Removes offending Carriage returns -static -char* RemoveCR(const char* txt) -{ - static char Buffer[2048]; - char* pt; - - strncpy(Buffer, txt, 2047); - Buffer[2047] = 0; - for (pt = Buffer; *pt; pt++) - if (*pt == '\n' || *pt == '\r') *pt = ' '; - - return Buffer; - -} - -static -void EmitHeader(cmsIOHANDLER* m, const char* Title, cmsHPROFILE hProfile) -{ - time_t timer; - cmsMLU *Description, *Copyright; - char DescASCII[256], CopyrightASCII[256]; - - time(&timer); - - Description = (cmsMLU*) cmsReadTag(hProfile, cmsSigProfileDescriptionTag); - Copyright = (cmsMLU*) cmsReadTag(hProfile, cmsSigCopyrightTag); - - DescASCII[0] = DescASCII[255] = 0; - CopyrightASCII[0] = CopyrightASCII[255] = 0; - - if (Description != NULL) cmsMLUgetASCII(Description, cmsNoLanguage, cmsNoCountry, DescASCII, 255); - if (Copyright != NULL) cmsMLUgetASCII(Copyright, cmsNoLanguage, cmsNoCountry, CopyrightASCII, 255); - - _cmsIOPrintf(m, "%%!PS-Adobe-3.0\n"); - _cmsIOPrintf(m, "%%\n"); - _cmsIOPrintf(m, "%% %s\n", Title); - _cmsIOPrintf(m, "%% Source: %s\n", RemoveCR(DescASCII)); - _cmsIOPrintf(m, "%% %s\n", RemoveCR(CopyrightASCII)); - _cmsIOPrintf(m, "%% Created: %s", ctime(&timer)); // ctime appends a \n!!! - _cmsIOPrintf(m, "%%\n"); - _cmsIOPrintf(m, "%%%%BeginResource\n"); - -} - - -// Emits White & Black point. White point is always D50, Black point is the device -// Black point adapted to D50. - -static -void EmitWhiteBlackD50(cmsIOHANDLER* m, cmsCIEXYZ* BlackPoint) -{ - - _cmsIOPrintf(m, "/BlackPoint [%f %f %f]\n", BlackPoint -> X, - BlackPoint -> Y, - BlackPoint -> Z); - - _cmsIOPrintf(m, "/WhitePoint [%f %f %f]\n", cmsD50_XYZ()->X, - cmsD50_XYZ()->Y, - cmsD50_XYZ()->Z); -} - - -static -void EmitRangeCheck(cmsIOHANDLER* m) -{ - _cmsIOPrintf(m, "dup 0.0 lt { pop 0.0 } if " - "dup 1.0 gt { pop 1.0 } if "); - -} - -// Does write the intent - -static -void EmitIntent(cmsIOHANDLER* m, int RenderingIntent) -{ - const char *intent; - - switch (RenderingIntent) { - - case INTENT_PERCEPTUAL: intent = "Perceptual"; break; - case INTENT_RELATIVE_COLORIMETRIC: intent = "RelativeColorimetric"; break; - case INTENT_ABSOLUTE_COLORIMETRIC: intent = "AbsoluteColorimetric"; break; - case INTENT_SATURATION: intent = "Saturation"; break; - - default: intent = "Undefined"; break; - } - - _cmsIOPrintf(m, "/RenderingIntent (%s)\n", intent ); -} - -// -// Convert L* to Y -// -// Y = Yn*[ (L* + 16) / 116] ^ 3 if (L*) >= 6 / 29 -// = Yn*( L* / 116) / 7.787 if (L*) < 6 / 29 -// - -/* -static -void EmitL2Y(cmsIOHANDLER* m) -{ - _cmsIOPrintf(m, - "{ " - "100 mul 16 add 116 div " // (L * 100 + 16) / 116 - "dup 6 29 div ge " // >= 6 / 29 ? - "{ dup dup mul mul } " // yes, ^3 and done - "{ 4 29 div sub 108 841 div mul } " // no, slope limiting - "ifelse } bind "); -} -*/ - - -// Lab -> XYZ, see the discussion above - -static -void EmitLab2XYZ(cmsIOHANDLER* m) -{ - _cmsIOPrintf(m, "/RangeABC [ 0 1 0 1 0 1]\n"); - _cmsIOPrintf(m, "/DecodeABC [\n"); - _cmsIOPrintf(m, "{100 mul 16 add 116 div } bind\n"); - _cmsIOPrintf(m, "{255 mul 128 sub 500 div } bind\n"); - _cmsIOPrintf(m, "{255 mul 128 sub 200 div } bind\n"); - _cmsIOPrintf(m, "]\n"); - _cmsIOPrintf(m, "/MatrixABC [ 1 1 1 1 0 0 0 0 -1]\n"); - _cmsIOPrintf(m, "/RangeLMN [ -0.236 1.254 0 1 -0.635 1.640 ]\n"); - _cmsIOPrintf(m, "/DecodeLMN [\n"); - _cmsIOPrintf(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.964200 mul} bind\n"); - _cmsIOPrintf(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse } bind\n"); - _cmsIOPrintf(m, "{dup 6 29 div ge {dup dup mul mul} {4 29 div sub 108 841 div mul} ifelse 0.824900 mul} bind\n"); - _cmsIOPrintf(m, "]\n"); -} - - - -// Outputs a table of words. It does use 16 bits - -static -void Emit1Gamma(cmsIOHANDLER* m, cmsToneCurve* Table) -{ - cmsUInt32Number i; - cmsFloat64Number gamma; - - if (Table == NULL) return; // Error - - if (Table ->nEntries <= 0) return; // Empty table - - // Suppress whole if identity - if (cmsIsToneCurveLinear(Table)) return; - - // Check if is really an exponential. If so, emit "exp" - gamma = cmsEstimateGamma(Table, 0.001); - if (gamma > 0) { - _cmsIOPrintf(m, "{ %g exp } bind ", gamma); - return; - } - - _cmsIOPrintf(m, "{ "); - - // Bounds check - EmitRangeCheck(m); - - // Emit intepolation code - - // PostScript code Stack - // =============== ======================== - // v - _cmsIOPrintf(m, " ["); - - for (i=0; i < Table->nEntries; i++) { - _cmsIOPrintf(m, "%d ", Table->Table16[i]); - } - - _cmsIOPrintf(m, "] "); // v tab - - _cmsIOPrintf(m, "dup "); // v tab tab - _cmsIOPrintf(m, "length 1 sub "); // v tab dom - _cmsIOPrintf(m, "3 -1 roll "); // tab dom v - _cmsIOPrintf(m, "mul "); // tab val2 - _cmsIOPrintf(m, "dup "); // tab val2 val2 - _cmsIOPrintf(m, "dup "); // tab val2 val2 val2 - _cmsIOPrintf(m, "floor cvi "); // tab val2 val2 cell0 - _cmsIOPrintf(m, "exch "); // tab val2 cell0 val2 - _cmsIOPrintf(m, "ceiling cvi "); // tab val2 cell0 cell1 - _cmsIOPrintf(m, "3 index "); // tab val2 cell0 cell1 tab - _cmsIOPrintf(m, "exch "); // tab val2 cell0 tab cell1 - _cmsIOPrintf(m, "get "); // tab val2 cell0 y1 - _cmsIOPrintf(m, "4 -1 roll "); // val2 cell0 y1 tab - _cmsIOPrintf(m, "3 -1 roll "); // val2 y1 tab cell0 - _cmsIOPrintf(m, "get "); // val2 y1 y0 - _cmsIOPrintf(m, "dup "); // val2 y1 y0 y0 - _cmsIOPrintf(m, "3 1 roll "); // val2 y0 y1 y0 - _cmsIOPrintf(m, "sub "); // val2 y0 (y1-y0) - _cmsIOPrintf(m, "3 -1 roll "); // y0 (y1-y0) val2 - _cmsIOPrintf(m, "dup "); // y0 (y1-y0) val2 val2 - _cmsIOPrintf(m, "floor cvi "); // y0 (y1-y0) val2 floor(val2) - _cmsIOPrintf(m, "sub "); // y0 (y1-y0) rest - _cmsIOPrintf(m, "mul "); // y0 t1 - _cmsIOPrintf(m, "add "); // y - _cmsIOPrintf(m, "65535 div "); // result - - _cmsIOPrintf(m, " } bind "); -} - - -// Compare gamma table - -static -cmsBool GammaTableEquals(cmsUInt16Number* g1, cmsUInt16Number* g2, int nEntries) -{ - return memcmp(g1, g2, nEntries* sizeof(cmsUInt16Number)) == 0; -} - - -// Does write a set of gamma curves - -static -void EmitNGamma(cmsIOHANDLER* m, int n, cmsToneCurve* g[]) -{ - int i; - - for( i=0; i < n; i++ ) - { - if (g[i] == NULL) return; // Error - - if (i > 0 && GammaTableEquals(g[i-1]->Table16, g[i]->Table16, g[i]->nEntries)) { - - _cmsIOPrintf(m, "dup "); - } - else { - Emit1Gamma(m, g[i]); - } - } - -} - - - - - -// Following code dumps a LUT onto memory stream - - -// This is the sampler. Intended to work in SAMPLER_INSPECT mode, -// that is, the callback will be called for each knot with -// -// In[] The grid location coordinates, normalized to 0..ffff -// Out[] The Pipeline values, normalized to 0..ffff -// -// Returning a value other than 0 does terminate the sampling process -// -// Each row contains Pipeline values for all but first component. So, I -// detect row changing by keeping a copy of last value of first -// component. -1 is used to mark begining of whole block. - -static -int OutputValueSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo) -{ - cmsPsSamplerCargo* sc = (cmsPsSamplerCargo*) Cargo; - cmsUInt32Number i; - - - if (sc -> FixWhite) { - - if (In[0] == 0xFFFF) { // Only in L* = 100, ab = [-8..8] - - if ((In[1] >= 0x7800 && In[1] <= 0x8800) && - (In[2] >= 0x7800 && In[2] <= 0x8800)) { - - cmsUInt16Number* Black; - cmsUInt16Number* White; - cmsUInt32Number nOutputs; - - if (!_cmsEndPointsBySpace(sc ->ColorSpace, &White, &Black, &nOutputs)) - return 0; - - for (i=0; i < nOutputs; i++) - Out[i] = White[i]; - } - - - } - } - - - // Hadle the parenthesis on rows - - if (In[0] != sc ->FirstComponent) { - - if (sc ->FirstComponent != -1) { - - _cmsIOPrintf(sc ->m, sc ->PostMin); - sc ->SecondComponent = -1; - _cmsIOPrintf(sc ->m, sc ->PostMaj); - } - - // Begin block - _cmsPSActualColumn = 0; - - _cmsIOPrintf(sc ->m, sc ->PreMaj); - sc ->FirstComponent = In[0]; - } - - - if (In[1] != sc ->SecondComponent) { - - if (sc ->SecondComponent != -1) { - - _cmsIOPrintf(sc ->m, sc ->PostMin); - } - - _cmsIOPrintf(sc ->m, sc ->PreMin); - sc ->SecondComponent = In[1]; - } - - // Dump table. - - for (i=0; i < sc -> Pipeline ->Params->nOutputs; i++) { - - cmsUInt16Number wWordOut = Out[i]; - cmsUInt8Number wByteOut; // Value as byte - - - // We always deal with Lab4 - - wByteOut = Word2Byte(wWordOut); - WriteByte(sc -> m, wByteOut); - } - - return 1; -} - -// Writes a Pipeline on memstream. Could be 8 or 16 bits based - -static -void WriteCLUT(cmsIOHANDLER* m, cmsStage* mpe, const char* PreMaj, - const char* PostMaj, - const char* PreMin, - const char* PostMin, - int FixWhite, - cmsColorSpaceSignature ColorSpace) -{ - cmsUInt32Number i; - cmsPsSamplerCargo sc; - - sc.FirstComponent = -1; - sc.SecondComponent = -1; - sc.Pipeline = (_cmsStageCLutData *) mpe ->Data; - sc.m = m; - sc.PreMaj = PreMaj; - sc.PostMaj= PostMaj; - - sc.PreMin = PreMin; - sc.PostMin = PostMin; - sc.FixWhite = FixWhite; - sc.ColorSpace = ColorSpace; - - _cmsIOPrintf(m, "["); - - for (i=0; i < sc.Pipeline->Params->nInputs; i++) - _cmsIOPrintf(m, " %d ", sc.Pipeline->Params->nSamples[i]); - - _cmsIOPrintf(m, " [\n"); - - cmsStageSampleCLut16bit(mpe, OutputValueSampler, (void*) &sc, SAMPLER_INSPECT); - - _cmsIOPrintf(m, PostMin); - _cmsIOPrintf(m, PostMaj); - _cmsIOPrintf(m, "] "); - -} - - -// Dumps CIEBasedA Color Space Array - -static -int EmitCIEBasedA(cmsIOHANDLER* m, cmsToneCurve* Curve, cmsCIEXYZ* BlackPoint) -{ - - _cmsIOPrintf(m, "[ /CIEBasedA\n"); - _cmsIOPrintf(m, " <<\n"); - - _cmsIOPrintf(m, "/DecodeA "); - - Emit1Gamma(m, Curve); - - _cmsIOPrintf(m, " \n"); - - _cmsIOPrintf(m, "/MatrixA [ 0.9642 1.0000 0.8249 ]\n"); - _cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n"); - - EmitWhiteBlackD50(m, BlackPoint); - EmitIntent(m, INTENT_PERCEPTUAL); - - _cmsIOPrintf(m, ">>\n"); - _cmsIOPrintf(m, "]\n"); - - return 1; -} - - -// Dumps CIEBasedABC Color Space Array - -static -int EmitCIEBasedABC(cmsIOHANDLER* m, cmsFloat64Number* Matrix, cmsToneCurve** CurveSet, cmsCIEXYZ* BlackPoint) -{ - int i; - - _cmsIOPrintf(m, "[ /CIEBasedABC\n"); - _cmsIOPrintf(m, "<<\n"); - _cmsIOPrintf(m, "/DecodeABC [ "); - - EmitNGamma(m, 3, CurveSet); - - _cmsIOPrintf(m, "]\n"); - - _cmsIOPrintf(m, "/MatrixABC [ " ); - - for( i=0; i < 3; i++ ) { - - _cmsIOPrintf(m, "%.6f %.6f %.6f ", Matrix[i + 3*0], - Matrix[i + 3*1], - Matrix[i + 3*2]); - } - - - _cmsIOPrintf(m, "]\n"); - - _cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n"); - - EmitWhiteBlackD50(m, BlackPoint); - EmitIntent(m, INTENT_PERCEPTUAL); - - _cmsIOPrintf(m, ">>\n"); - _cmsIOPrintf(m, "]\n"); - - - return 1; -} - - -static -int EmitCIEBasedDEF(cmsIOHANDLER* m, cmsPipeline* Pipeline, int Intent, cmsCIEXYZ* BlackPoint) -{ - const char* PreMaj; - const char* PostMaj; - const char* PreMin, *PostMin; - cmsStage* mpe; - - mpe = Pipeline ->Elements; - - switch (cmsStageInputChannels(mpe)) { - case 3: - - _cmsIOPrintf(m, "[ /CIEBasedDEF\n"); - PreMaj ="<"; - PostMaj= ">\n"; - PreMin = PostMin = ""; - break; - case 4: - _cmsIOPrintf(m, "[ /CIEBasedDEFG\n"); - PreMaj = "["; - PostMaj = "]\n"; - PreMin = "<"; - PostMin = ">\n"; - break; - default: - return 0; - - } - - _cmsIOPrintf(m, "<<\n"); - - if (cmsStageType(mpe) == cmsSigCurveSetElemType) { - - _cmsIOPrintf(m, "/DecodeDEF [ "); - EmitNGamma(m, cmsStageOutputChannels(mpe), _cmsStageGetPtrToCurveSet(mpe)); - _cmsIOPrintf(m, "]\n"); - - mpe = mpe ->Next; - } - - if (cmsStageType(mpe) == cmsSigCLutElemType) { - - _cmsIOPrintf(m, "/Table "); - WriteCLUT(m, mpe, PreMaj, PostMaj, PreMin, PostMin, FALSE, (cmsColorSpaceSignature) 0); - _cmsIOPrintf(m, "]\n"); - } - - EmitLab2XYZ(m); - EmitWhiteBlackD50(m, BlackPoint); - EmitIntent(m, Intent); - - _cmsIOPrintf(m, " >>\n"); - _cmsIOPrintf(m, "]\n"); - - return 1; -} - -// Generates a curve from a gray profile - -static - cmsToneCurve* ExtractGray2Y(cmsContext ContextID, cmsHPROFILE hProfile, int Intent) -{ - cmsToneCurve* Out = cmsBuildTabulatedToneCurve16(ContextID, 256, NULL); - cmsHPROFILE hXYZ = cmsCreateXYZProfile(); - cmsHTRANSFORM xform = cmsCreateTransformTHR(ContextID, hProfile, TYPE_GRAY_8, hXYZ, TYPE_XYZ_DBL, Intent, cmsFLAGS_NOOPTIMIZE); - int i; - - if (Out != NULL) { - for (i=0; i < 256; i++) { - - cmsUInt8Number Gray = (cmsUInt8Number) i; - cmsCIEXYZ XYZ; - - cmsDoTransform(xform, &Gray, &XYZ, 1); - - Out ->Table16[i] =_cmsQuickSaturateWord(XYZ.Y * 65535.0); - } - } - - cmsDeleteTransform(xform); - cmsCloseProfile(hXYZ); - return Out; -} - - - -// Because PostScript has only 8 bits in /Table, we should use -// a more perceptually uniform space... I do choose Lab. - -static -int WriteInputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, int Intent, cmsUInt32Number dwFlags) -{ - cmsHPROFILE hLab; - cmsHTRANSFORM xform; - cmsUInt32Number nChannels; - cmsUInt32Number InputFormat; - int rc; - cmsHPROFILE Profiles[2]; - cmsCIEXYZ BlackPointAdaptedToD50; - - // Does create a device-link based transform. - // The DeviceLink is next dumped as working CSA. - - InputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE); - nChannels = T_CHANNELS(InputFormat); - - - cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0); - - // Adjust output to Lab4 - hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL); - - Profiles[0] = hProfile; - Profiles[1] = hLab; - - xform = cmsCreateMultiprofileTransform(Profiles, 2, InputFormat, TYPE_Lab_DBL, Intent, 0); - cmsCloseProfile(hLab); - - if (xform == NULL) { - - cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Profile -> Lab"); - return 0; - } - - // Only 1, 3 and 4 channels are allowed - - switch (nChannels) { - - case 1: { - cmsToneCurve* Gray2Y = ExtractGray2Y(m ->ContextID, hProfile, Intent); - EmitCIEBasedA(m, Gray2Y, &BlackPointAdaptedToD50); - cmsFreeToneCurve(Gray2Y); - } - break; - - case 3: - case 4: { - cmsUInt32Number OutFrm = TYPE_Lab_16; - cmsPipeline* DeviceLink; - _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform; - - DeviceLink = cmsPipelineDup(v ->Lut); - if (DeviceLink == NULL) return 0; - - dwFlags |= cmsFLAGS_FORCE_CLUT; - _cmsOptimizePipeline(m->ContextID, &DeviceLink, Intent, &InputFormat, &OutFrm, &dwFlags); - - rc = EmitCIEBasedDEF(m, DeviceLink, Intent, &BlackPointAdaptedToD50); - cmsPipelineFree(DeviceLink); - if (rc == 0) return 0; - } - break; - - default: - - cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Only 3, 4 channels supported for CSA. This profile has %d channels.", nChannels); - return 0; - } - - - cmsDeleteTransform(xform); - - return 1; -} - -static -cmsFloat64Number* GetPtrToMatrix(const cmsStage* mpe) -{ - _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data; - - return Data -> Double; -} - - -// Does create CSA based on matrix-shaper. Allowed types are gray and RGB based - -static -int WriteInputMatrixShaper(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsStage* Matrix, cmsStage* Shaper) -{ - cmsColorSpaceSignature ColorSpace; - int rc; - cmsCIEXYZ BlackPointAdaptedToD50; - - ColorSpace = cmsGetColorSpace(hProfile); - - cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, INTENT_RELATIVE_COLORIMETRIC, 0); - - if (ColorSpace == cmsSigGrayData) { - - cmsToneCurve** ShaperCurve = _cmsStageGetPtrToCurveSet(Shaper); - rc = EmitCIEBasedA(m, ShaperCurve[0], &BlackPointAdaptedToD50); - - } - else - if (ColorSpace == cmsSigRgbData) { - - cmsMAT3 Mat; - int i, j; - - memmove(&Mat, GetPtrToMatrix(Matrix), sizeof(Mat)); - - for (i=0; i < 3; i++) - for (j=0; j < 3; j++) - Mat.v[i].n[j] *= MAX_ENCODEABLE_XYZ; - - rc = EmitCIEBasedABC(m, (cmsFloat64Number *) &Mat, - _cmsStageGetPtrToCurveSet(Shaper), - &BlackPointAdaptedToD50); - } - else { - - cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Profile is not suitable for CSA. Unsupported colorspace."); - return 0; - } - - return rc; -} - - - -// Creates a PostScript color list from a named profile data. -// This is a HP extension, and it works in Lab instead of XYZ - -static -int WriteNamedColorCSA(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, int Intent) -{ - cmsHTRANSFORM xform; - cmsHPROFILE hLab; - int i, nColors; - char ColorName[32]; - cmsNAMEDCOLORLIST* NamedColorList; - - hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL); - xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, hLab, TYPE_Lab_DBL, Intent, 0); - if (xform == NULL) return 0; - - NamedColorList = cmsGetNamedColorList(xform); - if (NamedColorList == NULL) return 0; - - _cmsIOPrintf(m, "<<\n"); - _cmsIOPrintf(m, "(colorlistcomment) (%s)\n", "Named color CSA"); - _cmsIOPrintf(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n"); - _cmsIOPrintf(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n"); - - nColors = cmsNamedColorCount(NamedColorList); - - - for (i=0; i < nColors; i++) { - - cmsUInt16Number In[1]; - cmsCIELab Lab; - - In[0] = (cmsUInt16Number) i; - - if (!cmsNamedColorInfo(NamedColorList, i, ColorName, NULL, NULL, NULL, NULL)) - continue; - - cmsDoTransform(xform, In, &Lab, 1); - _cmsIOPrintf(m, " (%s) [ %.3f %.3f %.3f ]\n", ColorName, Lab.L, Lab.a, Lab.b); - } - - - - _cmsIOPrintf(m, ">>\n"); - - cmsDeleteTransform(xform); - cmsCloseProfile(hLab); - return 1; -} - - -// Does create a Color Space Array on XYZ colorspace for PostScript usage -static -cmsUInt32Number GenerateCSA(cmsContext ContextID, - cmsHPROFILE hProfile, - cmsUInt32Number Intent, - cmsUInt32Number dwFlags, - cmsIOHANDLER* mem) -{ - cmsUInt32Number dwBytesUsed; - cmsPipeline* lut = NULL; - cmsStage* Matrix, *Shaper; - - - // Is a named color profile? - if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) { - - if (!WriteNamedColorCSA(mem, hProfile, Intent)) goto Error; - } - else { - - - // Any profile class are allowed (including devicelink), but - // output (PCS) colorspace must be XYZ or Lab - cmsColorSpaceSignature ColorSpace = cmsGetPCS(hProfile); - - if (ColorSpace != cmsSigXYZData && - ColorSpace != cmsSigLabData) { - - cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Invalid output color space"); - goto Error; - } - - - // Read the lut with all necessary conversion stages - lut = _cmsReadInputLUT(hProfile, Intent); - if (lut == NULL) goto Error; - - - // Tone curves + matrix can be implemented without any LUT - if (cmsPipelineCheckAndRetreiveStages(lut, 2, cmsSigCurveSetElemType, cmsSigMatrixElemType, &Shaper, &Matrix)) { - - if (!WriteInputMatrixShaper(mem, hProfile, Matrix, Shaper)) goto Error; - - } - else { - // We need a LUT for the rest - if (!WriteInputLUT(mem, hProfile, Intent, dwFlags)) goto Error; - } - } - - - // Done, keep memory usage - dwBytesUsed = mem ->UsedSpace; - - // Get rid of LUT - if (lut != NULL) cmsPipelineFree(lut); - - // Finally, return used byte count - return dwBytesUsed; - -Error: - if (lut != NULL) cmsPipelineFree(lut); - return 0; -} - -// ------------------------------------------------------ Color Rendering Dictionary (CRD) - - - -/* - - Black point compensation plus chromatic adaptation: - - Step 1 - Chromatic adaptation - ============================= - - WPout - X = ------- PQR - Wpin - - Step 2 - Black point compensation - ================================= - - (WPout - BPout)*X - WPout*(BPin - BPout) - out = --------------------------------------- - WPout - BPin - - - Algorithm discussion - ==================== - - TransformPQR(WPin, BPin, WPout, BPout, PQR) - - Wpin,etc= { Xws Yws Zws Pws Qws Rws } - - - Algorithm Stack 0...n - =========================================================== - PQR BPout WPout BPin WPin - 4 index 3 get WPin PQR BPout WPout BPin WPin - div (PQR/WPin) BPout WPout BPin WPin - 2 index 3 get WPout (PQR/WPin) BPout WPout BPin WPin - mult WPout*(PQR/WPin) BPout WPout BPin WPin - - 2 index 3 get WPout WPout*(PQR/WPin) BPout WPout BPin WPin - 2 index 3 get BPout WPout WPout*(PQR/WPin) BPout WPout BPin WPin - sub (WPout-BPout) WPout*(PQR/WPin) BPout WPout BPin WPin - mult (WPout-BPout)* WPout*(PQR/WPin) BPout WPout BPin WPin - - 2 index 3 get WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin - 4 index 3 get BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin - 3 index 3 get BPout BPin WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin - - sub (BPin-BPout) WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin - mult (BPin-BPout)*WPout (BPout-WPout)* WPout*(PQR/WPin) BPout WPout BPin WPin - sub (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin - - 3 index 3 get BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin - 3 index 3 get WPout BPin (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin - exch - sub (WPout-BPin) (BPout-WPout)* WPout*(PQR/WPin)-(BPin-BPout)*WPout BPout WPout BPin WPin - div - - exch pop - exch pop - exch pop - exch pop - -*/ - - -static -void EmitPQRStage(cmsIOHANDLER* m, cmsHPROFILE hProfile, int DoBPC, int lIsAbsolute) -{ - - - if (lIsAbsolute) { - - // For absolute colorimetric intent, encode back to relative - // and generate a relative Pipeline - - // Relative encoding is obtained across XYZpcs*(D50/WhitePoint) - - cmsCIEXYZ White; - - _cmsReadMediaWhitePoint(&White, hProfile); - - _cmsIOPrintf(m,"/MatrixPQR [1 0 0 0 1 0 0 0 1 ]\n"); - _cmsIOPrintf(m,"/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n"); - - _cmsIOPrintf(m, "%% Absolute colorimetric -- encode to relative to maximize LUT usage\n" - "/TransformPQR [\n" - "{0.9642 mul %g div exch pop exch pop exch pop exch pop} bind\n" - "{1.0000 mul %g div exch pop exch pop exch pop exch pop} bind\n" - "{0.8249 mul %g div exch pop exch pop exch pop exch pop} bind\n]\n", - White.X, White.Y, White.Z); - return; - } - - - _cmsIOPrintf(m,"%% Bradford Cone Space\n" - "/MatrixPQR [0.8951 -0.7502 0.0389 0.2664 1.7135 -0.0685 -0.1614 0.0367 1.0296 ] \n"); - - _cmsIOPrintf(m, "/RangePQR [ -0.5 2 -0.5 2 -0.5 2 ]\n"); - - - // No BPC - - if (!DoBPC) { - - _cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space\n" - "/TransformPQR [\n" - "{exch pop exch 3 get mul exch pop exch 3 get div} bind\n" - "{exch pop exch 4 get mul exch pop exch 4 get div} bind\n" - "{exch pop exch 5 get mul exch pop exch 5 get div} bind\n]\n"); - } else { - - // BPC - - _cmsIOPrintf(m, "%% VonKries-like transform in Bradford Cone Space plus BPC\n" - "/TransformPQR [\n"); - - _cmsIOPrintf(m, "{4 index 3 get div 2 index 3 get mul " - "2 index 3 get 2 index 3 get sub mul " - "2 index 3 get 4 index 3 get 3 index 3 get sub mul sub " - "3 index 3 get 3 index 3 get exch sub div " - "exch pop exch pop exch pop exch pop } bind\n"); - - _cmsIOPrintf(m, "{4 index 4 get div 2 index 4 get mul " - "2 index 4 get 2 index 4 get sub mul " - "2 index 4 get 4 index 4 get 3 index 4 get sub mul sub " - "3 index 4 get 3 index 4 get exch sub div " - "exch pop exch pop exch pop exch pop } bind\n"); - - _cmsIOPrintf(m, "{4 index 5 get div 2 index 5 get mul " - "2 index 5 get 2 index 5 get sub mul " - "2 index 5 get 4 index 5 get 3 index 5 get sub mul sub " - "3 index 5 get 3 index 5 get exch sub div " - "exch pop exch pop exch pop exch pop } bind\n]\n"); - - } - - -} - - -static -void EmitXYZ2Lab(cmsIOHANDLER* m) -{ - _cmsIOPrintf(m, "/RangeLMN [ -0.635 2.0 0 2 -0.635 2.0 ]\n"); - _cmsIOPrintf(m, "/EncodeLMN [\n"); - _cmsIOPrintf(m, "{ 0.964200 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n"); - _cmsIOPrintf(m, "{ 1.000000 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n"); - _cmsIOPrintf(m, "{ 0.824900 div dup 0.008856 le {7.787 mul 16 116 div add}{1 3 div exp} ifelse } bind\n"); - _cmsIOPrintf(m, "]\n"); - _cmsIOPrintf(m, "/MatrixABC [ 0 1 0 1 -1 1 0 0 -1 ]\n"); - _cmsIOPrintf(m, "/EncodeABC [\n"); - - - _cmsIOPrintf(m, "{ 116 mul 16 sub 100 div } bind\n"); - _cmsIOPrintf(m, "{ 500 mul 128 add 256 div } bind\n"); - _cmsIOPrintf(m, "{ 200 mul 128 add 256 div } bind\n"); - - - _cmsIOPrintf(m, "]\n"); - - -} - -// Due to impedance mismatch between XYZ and almost all RGB and CMYK spaces -// I choose to dump LUTS in Lab instead of XYZ. There is still a lot of wasted -// space on 3D CLUT, but since space seems not to be a problem here, 33 points -// would give a reasonable accurancy. Note also that CRD tables must operate in -// 8 bits. - -static -int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, int Intent, cmsUInt32Number dwFlags) -{ - cmsHPROFILE hLab; - cmsHTRANSFORM xform; - int i, nChannels; - cmsUInt32Number OutputFormat; - _cmsTRANSFORM* v; - cmsPipeline* DeviceLink; - cmsHPROFILE Profiles[3]; - cmsCIEXYZ BlackPointAdaptedToD50; - cmsBool lDoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION); - cmsBool lFixWhite = !(dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP); - cmsUInt32Number InFrm = TYPE_Lab_16; - int RelativeEncodingIntent; - cmsColorSpaceSignature ColorSpace; - - - hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL); - if (hLab == NULL) return 0; - - OutputFormat = cmsFormatterForColorspaceOfProfile(hProfile, 2, FALSE); - nChannels = T_CHANNELS(OutputFormat); - - ColorSpace = cmsGetColorSpace(hProfile); - - // For absolute colorimetric, the LUT is encoded as relative in order to preserve precision. - - RelativeEncodingIntent = Intent; - if (RelativeEncodingIntent == INTENT_ABSOLUTE_COLORIMETRIC) - RelativeEncodingIntent = INTENT_RELATIVE_COLORIMETRIC; - - - // Use V4 Lab always - Profiles[0] = hLab; - Profiles[1] = hProfile; - - xform = cmsCreateMultiprofileTransformTHR(m ->ContextID, - Profiles, 2, TYPE_Lab_DBL, - OutputFormat, RelativeEncodingIntent, 0); - cmsCloseProfile(hLab); - - if (xform == NULL) { - - cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Lab -> Profile in CRD creation"); - return 0; - } - - // Get a copy of the internal devicelink - v = (_cmsTRANSFORM*) xform; - DeviceLink = cmsPipelineDup(v ->Lut); - if (DeviceLink == NULL) return 0; - - - // We need a CLUT - dwFlags |= cmsFLAGS_FORCE_CLUT; - _cmsOptimizePipeline(m->ContextID, &DeviceLink, RelativeEncodingIntent, &InFrm, &OutputFormat, &dwFlags); - - _cmsIOPrintf(m, "<<\n"); - _cmsIOPrintf(m, "/ColorRenderingType 1\n"); - - - cmsDetectBlackPoint(&BlackPointAdaptedToD50, hProfile, Intent, 0); - - // Emit headers, etc. - EmitWhiteBlackD50(m, &BlackPointAdaptedToD50); - EmitPQRStage(m, hProfile, lDoBPC, Intent == INTENT_ABSOLUTE_COLORIMETRIC); - EmitXYZ2Lab(m); - - - // FIXUP: map Lab (100, 0, 0) to perfect white, because the particular encoding for Lab - // does map a=b=0 not falling into any specific node. Since range a,b goes -128..127, - // zero is slightly moved towards right, so assure next node (in L=100 slice) is mapped to - // zero. This would sacrifice a bit of highlights, but failure to do so would cause - // scum dot. Ouch. - - if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) - lFixWhite = FALSE; - - _cmsIOPrintf(m, "/RenderTable "); - - - WriteCLUT(m, cmsPipelineGetPtrToFirstStage(DeviceLink), "<", ">\n", "", "", lFixWhite, ColorSpace); - - _cmsIOPrintf(m, " %d {} bind ", nChannels); - - for (i=1; i < nChannels; i++) - _cmsIOPrintf(m, "dup "); - - _cmsIOPrintf(m, "]\n"); - - - EmitIntent(m, Intent); - - _cmsIOPrintf(m, ">>\n"); - - if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) { - - _cmsIOPrintf(m, "/Current exch /ColorRendering defineresource pop\n"); - } - - cmsPipelineFree(DeviceLink); - cmsDeleteTransform(xform); - - return 1; -} - - -// Builds a ASCII string containing colorant list in 0..1.0 range -static -void BuildColorantList(char *Colorant, int nColorant, cmsUInt16Number Out[]) -{ - char Buff[32]; - int j; - - Colorant[0] = 0; - if (nColorant > cmsMAXCHANNELS) - nColorant = cmsMAXCHANNELS; - - for (j=0; j < nColorant; j++) { - - sprintf(Buff, "%.3f", Out[j] / 65535.0); - strcat(Colorant, Buff); - if (j < nColorant -1) - strcat(Colorant, " "); - - } -} - - -// Creates a PostScript color list from a named profile data. -// This is a HP extension. - -static -int WriteNamedColorCRD(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, int Intent, cmsUInt32Number dwFlags) -{ - cmsHTRANSFORM xform; - int i, nColors, nColorant; - cmsUInt32Number OutputFormat; - char ColorName[32]; - char Colorant[128]; - cmsNAMEDCOLORLIST* NamedColorList; - - - OutputFormat = cmsFormatterForColorspaceOfProfile(hNamedColor, 2, FALSE); - nColorant = T_CHANNELS(OutputFormat); - - - xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, NULL, OutputFormat, Intent, dwFlags); - if (xform == NULL) return 0; - - - NamedColorList = cmsGetNamedColorList(xform); - if (NamedColorList == NULL) return 0; - - _cmsIOPrintf(m, "<<\n"); - _cmsIOPrintf(m, "(colorlistcomment) (%s) \n", "Named profile"); - _cmsIOPrintf(m, "(Prefix) [ (Pantone ) (PANTONE ) ]\n"); - _cmsIOPrintf(m, "(Suffix) [ ( CV) ( CVC) ( C) ]\n"); - - nColors = cmsNamedColorCount(NamedColorList); - - for (i=0; i < nColors; i++) { - - cmsUInt16Number In[1]; - cmsUInt16Number Out[cmsMAXCHANNELS]; - - In[0] = (cmsUInt16Number) i; - - if (!cmsNamedColorInfo(NamedColorList, i, ColorName, NULL, NULL, NULL, NULL)) - continue; - - cmsDoTransform(xform, In, Out, 1); - BuildColorantList(Colorant, nColorant, Out); - _cmsIOPrintf(m, " (%s) [ %s ]\n", ColorName, Colorant); - } - - _cmsIOPrintf(m, " >>"); - - if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) { - - _cmsIOPrintf(m, " /Current exch /HPSpotTable defineresource pop\n"); - } - - cmsDeleteTransform(xform); - return 1; -} - - - -// This one does create a Color Rendering Dictionary. -// CRD are always LUT-Based, no matter if profile is -// implemented as matrix-shaper. - -static -cmsUInt32Number GenerateCRD(cmsContext ContextID, - cmsHPROFILE hProfile, - cmsUInt32Number Intent, cmsUInt32Number dwFlags, - cmsIOHANDLER* mem) -{ - cmsUInt32Number dwBytesUsed; - - if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) { - - EmitHeader(mem, "Color Rendering Dictionary (CRD)", hProfile); - } - - - // Is a named color profile? - if (cmsGetDeviceClass(hProfile) == cmsSigNamedColorClass) { - - if (!WriteNamedColorCRD(mem, hProfile, Intent, dwFlags)) { - return 0; - } - } - else { - - // CRD are always implemented as LUT - - if (!WriteOutputLUT(mem, hProfile, Intent, dwFlags)) { - return 0; - } - } - - if (!(dwFlags & cmsFLAGS_NODEFAULTRESOURCEDEF)) { - - _cmsIOPrintf(mem, "%%%%EndResource\n"); - _cmsIOPrintf(mem, "\n%% CRD End\n"); - } - - // Done, keep memory usage - dwBytesUsed = mem ->UsedSpace; - - // Finally, return used byte count - return dwBytesUsed; - - cmsUNUSED_PARAMETER(ContextID); -} - - - - -cmsUInt32Number CMSEXPORT cmsGetPostScriptColorResource(cmsContext ContextID, - cmsPSResourceType Type, - cmsHPROFILE hProfile, - cmsUInt32Number Intent, - cmsUInt32Number dwFlags, - cmsIOHANDLER* io) -{ - cmsUInt32Number rc; - - - switch (Type) { - - case cmsPS_RESOURCE_CSA: - rc = GenerateCSA(ContextID, hProfile, Intent, dwFlags, io); - break; - - default: - case cmsPS_RESOURCE_CRD: - rc = GenerateCRD(ContextID, hProfile, Intent, dwFlags, io); - break; - } - - return rc; -} - - - -cmsUInt32Number CMSEXPORT cmsGetPostScriptCRD(cmsContext ContextID, - cmsHPROFILE hProfile, - cmsUInt32Number Intent, cmsUInt32Number dwFlags, - void* Buffer, cmsUInt32Number dwBufferLen) -{ - cmsIOHANDLER* mem; - cmsUInt32Number dwBytesUsed; - - // Set up the serialization engine - if (Buffer == NULL) - mem = cmsOpenIOhandlerFromNULL(ContextID); - else - mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w"); - - if (!mem) return 0; - - dwBytesUsed = cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CRD, hProfile, Intent, dwFlags, mem); - - // Get rid of memory stream - cmsCloseIOhandler(mem); - - return dwBytesUsed; -} - - - -// Does create a Color Space Array on XYZ colorspace for PostScript usage -cmsUInt32Number CMSEXPORT cmsGetPostScriptCSA(cmsContext ContextID, - cmsHPROFILE hProfile, - cmsUInt32Number Intent, - cmsUInt32Number dwFlags, - void* Buffer, - cmsUInt32Number dwBufferLen) -{ - cmsIOHANDLER* mem; - cmsUInt32Number dwBytesUsed; - - if (Buffer == NULL) - mem = cmsOpenIOhandlerFromNULL(ContextID); - else - mem = cmsOpenIOhandlerFromMem(ContextID, Buffer, dwBufferLen, "w"); - - if (!mem) return 0; - - dwBytesUsed = cmsGetPostScriptColorResource(ContextID, cmsPS_RESOURCE_CSA, hProfile, Intent, dwFlags, mem); - - // Get rid of memory stream - cmsCloseIOhandler(mem); - - return dwBytesUsed; - -} diff --git a/third_party/lcms2-2.6/src/cmssamp.c b/third_party/lcms2-2.6/src/cmssamp.c deleted file mode 100644 index 70e469161f..0000000000 --- a/third_party/lcms2-2.6/src/cmssamp.c +++ /dev/null @@ -1,572 +0,0 @@ -//--------------------------------------------------------------------------------- -// -// Little Color Management System -// Copyright (c) 1998-2010 Marti Maria Saguer -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the Software -// is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// -//--------------------------------------------------------------------------------- -// - -#include "lcms2_internal.h" - - -#define cmsmin(a, b) (((a) < (b)) ? (a) : (b)) -#define cmsmax(a, b) (((a) > (b)) ? (a) : (b)) - -// This file contains routines for resampling and LUT optimization, black point detection -// and black preservation. - -// Black point detection ------------------------------------------------------------------------- - - -// PCS -> PCS round trip transform, always uses relative intent on the device -> pcs -static -cmsHTRANSFORM CreateRoundtripXForm(cmsHPROFILE hProfile, cmsUInt32Number nIntent) -{ - cmsContext ContextID = cmsGetProfileContextID(hProfile); - cmsHPROFILE hLab = cmsCreateLab4ProfileTHR(ContextID, NULL); - cmsHTRANSFORM xform; - cmsBool BPC[4] = { FALSE, FALSE, FALSE, FALSE }; - cmsFloat64Number States[4] = { 1.0, 1.0, 1.0, 1.0 }; - cmsHPROFILE hProfiles[4]; - cmsUInt32Number Intents[4]; - - hProfiles[0] = hLab; hProfiles[1] = hProfile; hProfiles[2] = hProfile; hProfiles[3] = hLab; - Intents[0] = INTENT_RELATIVE_COLORIMETRIC; Intents[1] = nIntent; Intents[2] = INTENT_RELATIVE_COLORIMETRIC; Intents[3] = INTENT_RELATIVE_COLORIMETRIC; - - xform = cmsCreateExtendedTransform(ContextID, 4, hProfiles, BPC, Intents, - States, NULL, 0, TYPE_Lab_DBL, TYPE_Lab_DBL, cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE); - - cmsCloseProfile(hLab); - return xform; -} - -// Use darker colorants to obtain black point. This works in the relative colorimetric intent and -// assumes more ink results in darker colors. No ink limit is assumed. -static -cmsBool BlackPointAsDarkerColorant(cmsHPROFILE hInput, - cmsUInt32Number Intent, - cmsCIEXYZ* BlackPoint, - cmsUInt32Number dwFlags) -{ - cmsUInt16Number *Black; - cmsHTRANSFORM xform; - cmsColorSpaceSignature Space; - cmsUInt32Number nChannels; - cmsUInt32Number dwFormat; - cmsHPROFILE hLab; - cmsCIELab Lab; - cmsCIEXYZ BlackXYZ; - cmsContext ContextID = cmsGetProfileContextID(hInput); - - // If the profile does not support input direction, assume Black point 0 - if (!cmsIsIntentSupported(hInput, Intent, LCMS_USED_AS_INPUT)) { - - BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; - return FALSE; - } - - // Create a formatter which has n channels and floating point - dwFormat = cmsFormatterForColorspaceOfProfile(hInput, 2, FALSE); - - // Try to get black by using black colorant - Space = cmsGetColorSpace(hInput); - - // This function returns darker colorant in 16 bits for several spaces - if (!_cmsEndPointsBySpace(Space, NULL, &Black, &nChannels)) { - - BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; - return FALSE; - } - - if (nChannels != T_CHANNELS(dwFormat)) { - BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; - return FALSE; - } - - // Lab will be used as the output space, but lab2 will avoid recursion - hLab = cmsCreateLab2ProfileTHR(ContextID, NULL); - if (hLab == NULL) { - BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; - return FALSE; - } - - // Create the transform - xform = cmsCreateTransformTHR(ContextID, hInput, dwFormat, - hLab, TYPE_Lab_DBL, Intent, cmsFLAGS_NOOPTIMIZE|cmsFLAGS_NOCACHE); - cmsCloseProfile(hLab); - - if (xform == NULL) { - - // Something went wrong. Get rid of open resources and return zero as black - BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; - return FALSE; - } - - // Convert black to Lab - cmsDoTransform(xform, Black, &Lab, 1); - - // Force it to be neutral, clip to max. L* of 50 - Lab.a = Lab.b = 0; - if (Lab.L > 50) Lab.L = 50; - - // Free the resources - cmsDeleteTransform(xform); - - // Convert from Lab (which is now clipped) to XYZ. - cmsLab2XYZ(NULL, &BlackXYZ, &Lab); - - if (BlackPoint != NULL) - *BlackPoint = BlackXYZ; - - return TRUE; - - cmsUNUSED_PARAMETER(dwFlags); -} - -// Get a black point of output CMYK profile, discounting any ink-limiting embedded -// in the profile. For doing that, we use perceptual intent in input direction: -// Lab (0, 0, 0) -> [Perceptual] Profile -> CMYK -> [Rel. colorimetric] Profile -> Lab -static -cmsBool BlackPointUsingPerceptualBlack(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile) -{ - cmsHTRANSFORM hRoundTrip; - cmsCIELab LabIn, LabOut; - cmsCIEXYZ BlackXYZ; - - // Is the intent supported by the profile? - if (!cmsIsIntentSupported(hProfile, INTENT_PERCEPTUAL, LCMS_USED_AS_INPUT)) { - - BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; - return TRUE; - } - - hRoundTrip = CreateRoundtripXForm(hProfile, INTENT_PERCEPTUAL); - if (hRoundTrip == NULL) { - BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; - return FALSE; - } - - LabIn.L = LabIn.a = LabIn.b = 0; - cmsDoTransform(hRoundTrip, &LabIn, &LabOut, 1); - - // Clip Lab to reasonable limits - if (LabOut.L > 50) LabOut.L = 50; - LabOut.a = LabOut.b = 0; - - cmsDeleteTransform(hRoundTrip); - - // Convert it to XYZ - cmsLab2XYZ(NULL, &BlackXYZ, &LabOut); - - if (BlackPoint != NULL) - *BlackPoint = BlackXYZ; - - return TRUE; -} - -// This function shouldn't exist at all -- there is such quantity of broken -// profiles on black point tag, that we must somehow fix chromaticity to -// avoid huge tint when doing Black point compensation. This function does -// just that. There is a special flag for using black point tag, but turned -// off by default because it is bogus on most profiles. The detection algorithm -// involves to turn BP to neutral and to use only L component. -cmsBool CMSEXPORT cmsDetectBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags) -{ - cmsProfileClassSignature devClass; - - // Make sure the device class is adequate - devClass = cmsGetDeviceClass(hProfile); - if (devClass == cmsSigLinkClass || - devClass == cmsSigAbstractClass || - devClass == cmsSigNamedColorClass) { - BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; - return FALSE; - } - - // Make sure intent is adequate - if (Intent != INTENT_PERCEPTUAL && - Intent != INTENT_RELATIVE_COLORIMETRIC && - Intent != INTENT_SATURATION) { - BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; - return FALSE; - } - - // v4 + perceptual & saturation intents does have its own black point, and it is - // well specified enough to use it. Black point tag is deprecated in V4. - if ((cmsGetEncodedICCversion(hProfile) >= 0x4000000) && - (Intent == INTENT_PERCEPTUAL || Intent == INTENT_SATURATION)) { - - // Matrix shaper share MRC & perceptual intents - if (cmsIsMatrixShaper(hProfile)) - return BlackPointAsDarkerColorant(hProfile, INTENT_RELATIVE_COLORIMETRIC, BlackPoint, 0); - - // Get Perceptual black out of v4 profiles. That is fixed for perceptual & saturation intents - BlackPoint -> X = cmsPERCEPTUAL_BLACK_X; - BlackPoint -> Y = cmsPERCEPTUAL_BLACK_Y; - BlackPoint -> Z = cmsPERCEPTUAL_BLACK_Z; - - return TRUE; - } - - -#ifdef CMS_USE_PROFILE_BLACK_POINT_TAG - - // v2, v4 rel/abs colorimetric - if (cmsIsTag(hProfile, cmsSigMediaBlackPointTag) && - Intent == INTENT_RELATIVE_COLORIMETRIC) { - - cmsCIEXYZ *BlackPtr, BlackXYZ, UntrustedBlackPoint, TrustedBlackPoint, MediaWhite; - cmsCIELab Lab; - - // If black point is specified, then use it, - - BlackPtr = cmsReadTag(hProfile, cmsSigMediaBlackPointTag); - if (BlackPtr != NULL) { - - BlackXYZ = *BlackPtr; - _cmsReadMediaWhitePoint(&MediaWhite, hProfile); - - // Black point is absolute XYZ, so adapt to D50 to get PCS value - cmsAdaptToIlluminant(&UntrustedBlackPoint, &MediaWhite, cmsD50_XYZ(), &BlackXYZ); - - // Force a=b=0 to get rid of any chroma - cmsXYZ2Lab(NULL, &Lab, &UntrustedBlackPoint); - Lab.a = Lab.b = 0; - if (Lab.L > 50) Lab.L = 50; // Clip to L* <= 50 - cmsLab2XYZ(NULL, &TrustedBlackPoint, &Lab); - - if (BlackPoint != NULL) - *BlackPoint = TrustedBlackPoint; - - return TRUE; - } - } -#endif - - // That is about v2 profiles. - - // If output profile, discount ink-limiting and that's all - if (Intent == INTENT_RELATIVE_COLORIMETRIC && - (cmsGetDeviceClass(hProfile) == cmsSigOutputClass) && - (cmsGetColorSpace(hProfile) == cmsSigCmykData)) - return BlackPointUsingPerceptualBlack(BlackPoint, hProfile); - - // Nope, compute BP using current intent. - return BlackPointAsDarkerColorant(hProfile, Intent, BlackPoint, dwFlags); -} - - - -// --------------------------------------------------------------------------------------------------------- - -// Least Squares Fit of a Quadratic Curve to Data -// http://www.personal.psu.edu/jhm/f90/lectures/lsq2.html - -static -cmsFloat64Number RootOfLeastSquaresFitQuadraticCurve(int n, cmsFloat64Number x[], cmsFloat64Number y[]) -{ - double sum_x = 0, sum_x2 = 0, sum_x3 = 0, sum_x4 = 0; - double sum_y = 0, sum_yx = 0, sum_yx2 = 0; - double d, a, b, c; - int i; - cmsMAT3 m; - cmsVEC3 v, res; - - if (n < 4) return 0; - - for (i=0; i < n; i++) { - - double xn = x[i]; - double yn = y[i]; - - sum_x += xn; - sum_x2 += xn*xn; - sum_x3 += xn*xn*xn; - sum_x4 += xn*xn*xn*xn; - - sum_y += yn; - sum_yx += yn*xn; - sum_yx2 += yn*xn*xn; - } - - _cmsVEC3init(&m.v[0], n, sum_x, sum_x2); - _cmsVEC3init(&m.v[1], sum_x, sum_x2, sum_x3); - _cmsVEC3init(&m.v[2], sum_x2, sum_x3, sum_x4); - - _cmsVEC3init(&v, sum_y, sum_yx, sum_yx2); - - if (!_cmsMAT3solve(&res, &m, &v)) return 0; - - - a = res.n[2]; - b = res.n[1]; - c = res.n[0]; - - if (fabs(a) < 1.0E-10) { - - return cmsmin(0, cmsmax(50, -c/b )); - } - else { - - d = b*b - 4.0 * a * c; - if (d <= 0) { - return 0; - } - else { - - double rt = (-b + sqrt(d)) / (2.0 * a); - - return cmsmax(0, cmsmin(50, rt)); - } - } - -} - -/* -static -cmsBool IsMonotonic(int n, const cmsFloat64Number Table[]) -{ - int i; - cmsFloat64Number last; - - last = Table[n-1]; - - for (i = n-2; i >= 0; --i) { - - if (Table[i] > last) - - return FALSE; - else - last = Table[i]; - - } - - return TRUE; -} -*/ - -// Calculates the black point of a destination profile. -// This algorithm comes from the Adobe paper disclosing its black point compensation method. -cmsBool CMSEXPORT cmsDetectDestinationBlackPoint(cmsCIEXYZ* BlackPoint, cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUInt32Number dwFlags) -{ - cmsColorSpaceSignature ColorSpace; - cmsHTRANSFORM hRoundTrip = NULL; - cmsCIELab InitialLab, destLab, Lab; - cmsFloat64Number inRamp[256], outRamp[256]; - cmsFloat64Number MinL, MaxL; - cmsBool NearlyStraightMidrange = TRUE; - cmsFloat64Number yRamp[256]; - cmsFloat64Number x[256], y[256]; - cmsFloat64Number lo, hi; - int n, l; - cmsProfileClassSignature devClass; - - // Make sure the device class is adequate - devClass = cmsGetDeviceClass(hProfile); - if (devClass == cmsSigLinkClass || - devClass == cmsSigAbstractClass || - devClass == cmsSigNamedColorClass) { - BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; - return FALSE; - } - - // Make sure intent is adequate - if (Intent != INTENT_PERCEPTUAL && - Intent != INTENT_RELATIVE_COLORIMETRIC && - Intent != INTENT_SATURATION) { - BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; - return FALSE; - } - - - // v4 + perceptual & saturation intents does have its own black point, and it is - // well specified enough to use it. Black point tag is deprecated in V4. - if ((cmsGetEncodedICCversion(hProfile) >= 0x4000000) && - (Intent == INTENT_PERCEPTUAL || Intent == INTENT_SATURATION)) { - - // Matrix shaper share MRC & perceptual intents - if (cmsIsMatrixShaper(hProfile)) - return BlackPointAsDarkerColorant(hProfile, INTENT_RELATIVE_COLORIMETRIC, BlackPoint, 0); - - // Get Perceptual black out of v4 profiles. That is fixed for perceptual & saturation intents - BlackPoint -> X = cmsPERCEPTUAL_BLACK_X; - BlackPoint -> Y = cmsPERCEPTUAL_BLACK_Y; - BlackPoint -> Z = cmsPERCEPTUAL_BLACK_Z; - return TRUE; - } - - - // Check if the profile is lut based and gray, rgb or cmyk (7.2 in Adobe's document) - ColorSpace = cmsGetColorSpace(hProfile); - if (!cmsIsCLUT(hProfile, Intent, LCMS_USED_AS_OUTPUT ) || - (ColorSpace != cmsSigGrayData && - ColorSpace != cmsSigRgbData && - ColorSpace != cmsSigCmykData)) { - - // In this case, handle as input case - return cmsDetectBlackPoint(BlackPoint, hProfile, Intent, dwFlags); - } - - // It is one of the valid cases!, use Adobe algorithm - - - // Set a first guess, that should work on good profiles. - if (Intent == INTENT_RELATIVE_COLORIMETRIC) { - - cmsCIEXYZ IniXYZ; - - // calculate initial Lab as source black point - if (!cmsDetectBlackPoint(&IniXYZ, hProfile, Intent, dwFlags)) { - return FALSE; - } - - // convert the XYZ to lab - cmsXYZ2Lab(NULL, &InitialLab, &IniXYZ); - - } else { - - // set the initial Lab to zero, that should be the black point for perceptual and saturation - InitialLab.L = 0; - InitialLab.a = 0; - InitialLab.b = 0; - } - - - // Step 2 - // ====== - - // Create a roundtrip. Define a Transform BT for all x in L*a*b* - hRoundTrip = CreateRoundtripXForm(hProfile, Intent); - if (hRoundTrip == NULL) return FALSE; - - // Compute ramps - - for (l=0; l < 256; l++) { - - Lab.L = (cmsFloat64Number) (l * 100.0) / 255.0; - Lab.a = cmsmin(50, cmsmax(-50, InitialLab.a)); - Lab.b = cmsmin(50, cmsmax(-50, InitialLab.b)); - - cmsDoTransform(hRoundTrip, &Lab, &destLab, 1); - - inRamp[l] = Lab.L; - outRamp[l] = destLab.L; - } - - // Make monotonic - for (l = 254; l > 0; --l) { - outRamp[l] = cmsmin(outRamp[l], outRamp[l+1]); - } - - // Check - if (! (outRamp[0] < outRamp[255])) { - - cmsDeleteTransform(hRoundTrip); - BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; - return FALSE; - } - - - // Test for mid range straight (only on relative colorimetric) - - NearlyStraightMidrange = TRUE; - MinL = outRamp[0]; MaxL = outRamp[255]; - if (Intent == INTENT_RELATIVE_COLORIMETRIC) { - - for (l=0; l < 256; l++) { - - if (! ((inRamp[l] <= MinL + 0.2 * (MaxL - MinL) ) || - (fabs(inRamp[l] - outRamp[l]) < 4.0 ))) - NearlyStraightMidrange = FALSE; - } - - // If the mid range is straight (as determined above) then the - // DestinationBlackPoint shall be the same as initialLab. - // Otherwise, the DestinationBlackPoint shall be determined - // using curve fitting. - - if (NearlyStraightMidrange) { - - cmsLab2XYZ(NULL, BlackPoint, &InitialLab); - cmsDeleteTransform(hRoundTrip); - return TRUE; - } - } - - - // curve fitting: The round-trip curve normally looks like a nearly constant section at the black point, - // with a corner and a nearly straight line to the white point. - - for (l=0; l < 256; l++) { - - yRamp[l] = (outRamp[l] - MinL) / (MaxL - MinL); - } - - // find the black point using the least squares error quadratic curve fitting - - if (Intent == INTENT_RELATIVE_COLORIMETRIC) { - lo = 0.1; - hi = 0.5; - } - else { - - // Perceptual and saturation - lo = 0.03; - hi = 0.25; - } - - // Capture shadow points for the fitting. - n = 0; - for (l=0; l < 256; l++) { - - cmsFloat64Number ff = yRamp[l]; - - if (ff >= lo && ff < hi) { - x[n] = inRamp[l]; - y[n] = yRamp[l]; - n++; - } - } - - - // No suitable points - if (n < 3 ) { - cmsDeleteTransform(hRoundTrip); - BlackPoint -> X = BlackPoint ->Y = BlackPoint -> Z = 0.0; - return FALSE; - } - - - // fit and get the vertex of quadratic curve - Lab.L = RootOfLeastSquaresFitQuadraticCurve(n, x, y); - - if (Lab.L < 0.0) { // clip to zero L* if the vertex is negative - Lab.L = 0; - } - - Lab.a = InitialLab.a; - Lab.b = InitialLab.b; - - cmsLab2XYZ(NULL, BlackPoint, &Lab); - - cmsDeleteTransform(hRoundTrip); - return TRUE; -} diff --git a/third_party/lcms2-2.6/src/cmssm.c b/third_party/lcms2-2.6/src/cmssm.c deleted file mode 100644 index 5836e15506..0000000000 --- a/third_party/lcms2-2.6/src/cmssm.c +++ /dev/null @@ -1,734 +0,0 @@ -//--------------------------------------------------------------------------------- -// -// Little Color Management System -// Copyright (c) 1998-2011 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" - - -// ------------------------------------------------------------------------ - -// Gamut boundary description by using Jan Morovic's Segment maxima method -// Many thanks to Jan for allowing me to use his algorithm. - -// r = C* -// alpha = Hab -// theta = L* - -#define SECTORS 16 // number of divisions in alpha and theta - -// Spherical coordinates -typedef struct { - - cmsFloat64Number r; - cmsFloat64Number alpha; - cmsFloat64Number theta; - -} cmsSpherical; - -typedef enum { - GP_EMPTY, - GP_SPECIFIED, - GP_MODELED - - } GDBPointType; - - -typedef struct { - - GDBPointType Type; - cmsSpherical p; // Keep also alpha & theta of maximum - -} cmsGDBPoint; - - -typedef struct { - - cmsContext ContextID; - cmsGDBPoint Gamut[SECTORS][SECTORS]; - -} cmsGDB; - - -// A line using the parametric form -// P = a + t*u -typedef struct { - - cmsVEC3 a; - cmsVEC3 u; - -} cmsLine; - - -// A plane using the parametric form -// Q = b + r*v + s*w -typedef struct { - - cmsVEC3 b; - cmsVEC3 v; - cmsVEC3 w; - -} cmsPlane; - - - -// -------------------------------------------------------------------------------------------- - -// ATAN2() which always returns degree positive numbers - -static -cmsFloat64Number _cmsAtan2(cmsFloat64Number y, cmsFloat64Number x) -{ - cmsFloat64Number a; - - // Deal with undefined case - if (x == 0.0 && y == 0.0) return 0; - - a = (atan2(y, x) * 180.0) / M_PI; - - while (a < 0) { - a += 360; - } - - return a; -} - -// Convert to spherical coordinates -static -void ToSpherical(cmsSpherical* sp, const cmsVEC3* v) -{ - - cmsFloat64Number L, a, b; - - L = v ->n[VX]; - a = v ->n[VY]; - b = v ->n[VZ]; - - sp ->r = sqrt( L*L + a*a + b*b ); - - if (sp ->r == 0) { - sp ->alpha = sp ->theta = 0; - return; - } - - sp ->alpha = _cmsAtan2(a, b); - sp ->theta = _cmsAtan2(sqrt(a*a + b*b), L); -} - - -// Convert to cartesian from spherical -static -void ToCartesian(cmsVEC3* v, const cmsSpherical* sp) -{ - cmsFloat64Number sin_alpha; - cmsFloat64Number cos_alpha; - cmsFloat64Number sin_theta; - cmsFloat64Number cos_theta; - cmsFloat64Number L, a, b; - - sin_alpha = sin((M_PI * sp ->alpha) / 180.0); - cos_alpha = cos((M_PI * sp ->alpha) / 180.0); - sin_theta = sin((M_PI * sp ->theta) / 180.0); - cos_theta = cos((M_PI * sp ->theta) / 180.0); - - a = sp ->r * sin_theta * sin_alpha; - b = sp ->r * sin_theta * cos_alpha; - L = sp ->r * cos_theta; - - v ->n[VX] = L; - v ->n[VY] = a; - v ->n[VZ] = b; -} - - -// Quantize sector of a spherical coordinate. Saturate 360, 180 to last sector -// The limits are the centers of each sector, so -static -void QuantizeToSector(const cmsSpherical* sp, int* alpha, int* theta) -{ - *alpha = (int) floor(((sp->alpha * (SECTORS)) / 360.0) ); - *theta = (int) floor(((sp->theta * (SECTORS)) / 180.0) ); - - if (*alpha >= SECTORS) - *alpha = SECTORS-1; - if (*theta >= SECTORS) - *theta = SECTORS-1; -} - - -// Line determined by 2 points -static -void LineOf2Points(cmsLine* line, cmsVEC3* a, cmsVEC3* b) -{ - - _cmsVEC3init(&line ->a, a ->n[VX], a ->n[VY], a ->n[VZ]); - _cmsVEC3init(&line ->u, b ->n[VX] - a ->n[VX], - b ->n[VY] - a ->n[VY], - b ->n[VZ] - a ->n[VZ]); -} - - -// Evaluate parametric line -static -void GetPointOfLine(cmsVEC3* p, const cmsLine* line, cmsFloat64Number t) -{ - p ->n[VX] = line ->a.n[VX] + t * line->u.n[VX]; - p ->n[VY] = line ->a.n[VY] + t * line->u.n[VY]; - p ->n[VZ] = line ->a.n[VZ] + t * line->u.n[VZ]; -} - - - -/* - Closest point in sector line1 to sector line2 (both are defined as 0 <=t <= 1) - http://softsurfer.com/Archive/algorithm_0106/algorithm_0106.htm - - Copyright 2001, softSurfer (www.softsurfer.com) - This code may be freely used and modified for any purpose - providing that this copyright notice is included with it. - SoftSurfer makes no warranty for this code, and cannot be held - liable for any real or imagined damage resulting from its use. - Users of this code must verify correctness for their application. - -*/ - -static -cmsBool ClosestLineToLine(cmsVEC3* r, const cmsLine* line1, const cmsLine* line2) -{ - cmsFloat64Number a, b, c, d, e, D; - cmsFloat64Number sc, sN, sD; - cmsFloat64Number tc, tN, tD; - cmsVEC3 w0; - - _cmsVEC3minus(&w0, &line1 ->a, &line2 ->a); - - a = _cmsVEC3dot(&line1 ->u, &line1 ->u); - b = _cmsVEC3dot(&line1 ->u, &line2 ->u); - c = _cmsVEC3dot(&line2 ->u, &line2 ->u); - d = _cmsVEC3dot(&line1 ->u, &w0); - e = _cmsVEC3dot(&line2 ->u, &w0); - - D = a*c - b * b; // Denominator - sD = tD = D; // default sD = D >= 0 - - if (D < MATRIX_DET_TOLERANCE) { // the lines are almost parallel - - sN = 0.0; // force using point P0 on segment S1 - sD = 1.0; // to prevent possible division by 0.0 later - tN = e; - tD = c; - } - else { // get the closest points on the infinite lines - - sN = (b*e - c*d); - tN = (a*e - b*d); - - if (sN < 0.0) { // sc < 0 => the s=0 edge is visible - - sN = 0.0; - tN = e; - tD = c; - } - else if (sN > sD) { // sc > 1 => the s=1 edge is visible - sN = sD; - tN = e + b; - tD = c; - } - } - - if (tN < 0.0) { // tc < 0 => the t=0 edge is visible - - tN = 0.0; - // recompute sc for this edge - if (-d < 0.0) - sN = 0.0; - else if (-d > a) - sN = sD; - else { - sN = -d; - sD = a; - } - } - else if (tN > tD) { // tc > 1 => the t=1 edge is visible - - tN = tD; - - // recompute sc for this edge - if ((-d + b) < 0.0) - sN = 0; - else if ((-d + b) > a) - sN = sD; - else { - sN = (-d + b); - sD = a; - } - } - // finally do the division to get sc and tc - sc = (fabs(sN) < MATRIX_DET_TOLERANCE ? 0.0 : sN / sD); - tc = (fabs(tN) < MATRIX_DET_TOLERANCE ? 0.0 : tN / tD); - - GetPointOfLine(r, line1, sc); - return TRUE; -} - - - -// ------------------------------------------------------------------ Wrapper - - -// Allocate & free structure -cmsHANDLE CMSEXPORT cmsGBDAlloc(cmsContext ContextID) -{ - cmsGDB* gbd = (cmsGDB*) _cmsMallocZero(ContextID, sizeof(cmsGDB)); - if (gbd == NULL) return NULL; - - gbd -> ContextID = ContextID; - - return (cmsHANDLE) gbd; -} - - -void CMSEXPORT cmsGBDFree(cmsHANDLE hGBD) -{ - cmsGDB* gbd = (cmsGDB*) hGBD; - if (hGBD != NULL) - _cmsFree(gbd->ContextID, (void*) gbd); -} - - -// Auxiliar to retrieve a pointer to the segmentr containing the Lab value -static -cmsGDBPoint* GetPoint(cmsGDB* gbd, const cmsCIELab* Lab, cmsSpherical* sp) -{ - cmsVEC3 v; - int alpha, theta; - - // Housekeeping - _cmsAssert(gbd != NULL); - _cmsAssert(Lab != NULL); - _cmsAssert(sp != NULL); - - // Center L* by substracting half of its domain, that's 50 - _cmsVEC3init(&v, Lab ->L - 50.0, Lab ->a, Lab ->b); - - // Convert to spherical coordinates - ToSpherical(sp, &v); - - if (sp ->r < 0 || sp ->alpha < 0 || sp->theta < 0) { - cmsSignalError(gbd ->ContextID, cmsERROR_RANGE, "spherical value out of range"); - return NULL; - } - - // On which sector it falls? - QuantizeToSector(sp, &alpha, &theta); - - if (alpha < 0 || theta < 0 || alpha >= SECTORS || theta >= SECTORS) { - cmsSignalError(gbd ->ContextID, cmsERROR_RANGE, " quadrant out of range"); - return NULL; - } - - // Get pointer to the sector - return &gbd ->Gamut[theta][alpha]; -} - -// Add a point to gamut descriptor. Point to add is in Lab color space. -// GBD is centered on a=b=0 and L*=50 -cmsBool CMSEXPORT cmsGDBAddPoint(cmsHANDLE hGBD, const cmsCIELab* Lab) -{ - cmsGDB* gbd = (cmsGDB*) hGBD; - cmsGDBPoint* ptr; - cmsSpherical sp; - - - // Get pointer to the sector - ptr = GetPoint(gbd, Lab, &sp); - if (ptr == NULL) return FALSE; - - // If no samples at this sector, add it - if (ptr ->Type == GP_EMPTY) { - - ptr -> Type = GP_SPECIFIED; - ptr -> p = sp; - } - else { - - - // Substitute only if radius is greater - if (sp.r > ptr -> p.r) { - - ptr -> Type = GP_SPECIFIED; - ptr -> p = sp; - } - } - - return TRUE; -} - -// Check if a given point falls inside gamut -cmsBool CMSEXPORT cmsGDBCheckPoint(cmsHANDLE hGBD, const cmsCIELab* Lab) -{ - cmsGDB* gbd = (cmsGDB*) hGBD; - cmsGDBPoint* ptr; - cmsSpherical sp; - - // Get pointer to the sector - ptr = GetPoint(gbd, Lab, &sp); - if (ptr == NULL) return FALSE; - - // If no samples at this sector, return no data - if (ptr ->Type == GP_EMPTY) return FALSE; - - // In gamut only if radius is greater - - return (sp.r <= ptr -> p.r); -} - -// ----------------------------------------------------------------------------------------------------------------------- - -// Find near sectors. The list of sectors found is returned on Close[]. -// The function returns the number of sectors as well. - -// 24 9 10 11 12 -// 23 8 1 2 13 -// 22 7 * 3 14 -// 21 6 5 4 15 -// 20 19 18 17 16 -// -// Those are the relative movements -// {-2,-2}, {-1, -2}, {0, -2}, {+1, -2}, {+2, -2}, -// {-2,-1}, {-1, -1}, {0, -1}, {+1, -1}, {+2, -1}, -// {-2, 0}, {-1, 0}, {0, 0}, {+1, 0}, {+2, 0}, -// {-2,+1}, {-1, +1}, {0, +1}, {+1, +1}, {+2, +1}, -// {-2,+2}, {-1, +2}, {0, +2}, {+1, +2}, {+2, +2}}; - - -static -const struct _spiral { - - int AdvX, AdvY; - - } Spiral[] = { {0, -1}, {+1, -1}, {+1, 0}, {+1, +1}, {0, +1}, {-1, +1}, - {-1, 0}, {-1, -1}, {-1, -2}, {0, -2}, {+1, -2}, {+2, -2}, - {+2, -1}, {+2, 0}, {+2, +1}, {+2, +2}, {+1, +2}, {0, +2}, - {-1, +2}, {-2, +2}, {-2, +1}, {-2, 0}, {-2, -1}, {-2, -2} }; - -#define NSTEPS (sizeof(Spiral) / sizeof(struct _spiral)) - -static -int FindNearSectors(cmsGDB* gbd, int alpha, int theta, cmsGDBPoint* Close[]) -{ - int nSectors = 0; - int a, t; - cmsUInt32Number i; - cmsGDBPoint* pt; - - for (i=0; i < NSTEPS; i++) { - - a = alpha + Spiral[i].AdvX; - t = theta + Spiral[i].AdvY; - - // Cycle at the end - a %= SECTORS; - t %= SECTORS; - - // Cycle at the begin - if (a < 0) a = SECTORS + a; - if (t < 0) t = SECTORS + t; - - pt = &gbd ->Gamut[t][a]; - - if (pt -> Type != GP_EMPTY) { - - Close[nSectors++] = pt; - } - } - - return nSectors; -} - - -// Interpolate a missing sector. Method identifies whatever this is top, bottom or mid -static -cmsBool InterpolateMissingSector(cmsGDB* gbd, int alpha, int theta) -{ - cmsSpherical sp; - cmsVEC3 Lab; - cmsVEC3 Centre; - cmsLine ray; - int nCloseSectors; - cmsGDBPoint* Close[NSTEPS + 1]; - cmsSpherical closel, templ; - cmsLine edge; - int k, m; - - // Is that point already specified? - if (gbd ->Gamut[theta][alpha].Type != GP_EMPTY) return TRUE; - - // Fill close points - nCloseSectors = FindNearSectors(gbd, alpha, theta, Close); - - - // Find a central point on the sector - sp.alpha = (cmsFloat64Number) ((alpha + 0.5) * 360.0) / (SECTORS); - sp.theta = (cmsFloat64Number) ((theta + 0.5) * 180.0) / (SECTORS); - sp.r = 50.0; - - // Convert to Cartesian - ToCartesian(&Lab, &sp); - - // Create a ray line from centre to this point - _cmsVEC3init(&Centre, 50.0, 0, 0); - LineOf2Points(&ray, &Lab, &Centre); - - // For all close sectors - closel.r = 0.0; - closel.alpha = 0; - closel.theta = 0; - - for (k=0; k < nCloseSectors; k++) { - - for(m = k+1; m < nCloseSectors; m++) { - - cmsVEC3 temp, a1, a2; - - // A line from sector to sector - ToCartesian(&a1, &Close[k]->p); - ToCartesian(&a2, &Close[m]->p); - - LineOf2Points(&edge, &a1, &a2); - - // Find a line - ClosestLineToLine(&temp, &ray, &edge); - - // Convert to spherical - ToSpherical(&templ, &temp); - - - if ( templ.r > closel.r && - templ.theta >= (theta*180.0/SECTORS) && - templ.theta <= ((theta+1)*180.0/SECTORS) && - templ.alpha >= (alpha*360.0/SECTORS) && - templ.alpha <= ((alpha+1)*360.0/SECTORS)) { - - closel = templ; - } - } - } - - gbd ->Gamut[theta][alpha].p = closel; - gbd ->Gamut[theta][alpha].Type = GP_MODELED; - - return TRUE; - -} - - -// Interpolate missing parts. The algorithm fist computes slices at -// theta=0 and theta=Max. -cmsBool CMSEXPORT cmsGDBCompute(cmsHANDLE hGBD, cmsUInt32Number dwFlags) -{ - int alpha, theta; - cmsGDB* gbd = (cmsGDB*) hGBD; - - _cmsAssert(hGBD != NULL); - - // Interpolate black - for (alpha = 0; alpha < SECTORS; alpha++) { - - if (!InterpolateMissingSector(gbd, alpha, 0)) return FALSE; - } - - // Interpolate white - for (alpha = 0; alpha < SECTORS; alpha++) { - - if (!InterpolateMissingSector(gbd, alpha, SECTORS-1)) return FALSE; - } - - - // Interpolate Mid - for (theta = 1; theta < SECTORS; theta++) { - for (alpha = 0; alpha < SECTORS; alpha++) { - - if (!InterpolateMissingSector(gbd, alpha, theta)) return FALSE; - } - } - - // Done - return TRUE; - - cmsUNUSED_PARAMETER(dwFlags); -} - - - - -// -------------------------------------------------------------------------------------------------------- - -// Great for debug, but not suitable for real use - -#if 0 -cmsBool cmsGBDdumpVRML(cmsHANDLE hGBD, const char* fname) -{ - FILE* fp; - int i, j; - cmsGDB* gbd = (cmsGDB*) hGBD; - cmsGDBPoint* pt; - - fp = fopen (fname, "wt"); - if (fp == NULL) - return FALSE; - - fprintf (fp, "#VRML V2.0 utf8\n"); - - // set the viewing orientation and distance - fprintf (fp, "DEF CamTest Group {\n"); - fprintf (fp, "\tchildren [\n"); - fprintf (fp, "\t\tDEF Cameras Group {\n"); - fprintf (fp, "\t\t\tchildren [\n"); - fprintf (fp, "\t\t\t\tDEF DefaultView Viewpoint {\n"); - fprintf (fp, "\t\t\t\t\tposition 0 0 340\n"); - fprintf (fp, "\t\t\t\t\torientation 0 0 1 0\n"); - fprintf (fp, "\t\t\t\t\tdescription \"default view\"\n"); - fprintf (fp, "\t\t\t\t}\n"); - fprintf (fp, "\t\t\t]\n"); - fprintf (fp, "\t\t},\n"); - fprintf (fp, "\t]\n"); - fprintf (fp, "}\n"); - - // Output the background stuff - fprintf (fp, "Background {\n"); - fprintf (fp, "\tskyColor [\n"); - fprintf (fp, "\t\t.5 .5 .5\n"); - fprintf (fp, "\t]\n"); - fprintf (fp, "}\n"); - - // Output the shape stuff - fprintf (fp, "Transform {\n"); - fprintf (fp, "\tscale .3 .3 .3\n"); - fprintf (fp, "\tchildren [\n"); - - // Draw the axes as a shape: - fprintf (fp, "\t\tShape {\n"); - fprintf (fp, "\t\t\tappearance Appearance {\n"); - fprintf (fp, "\t\t\t\tmaterial Material {\n"); - fprintf (fp, "\t\t\t\t\tdiffuseColor 0 0.8 0\n"); - fprintf (fp, "\t\t\t\t\temissiveColor 1.0 1.0 1.0\n"); - fprintf (fp, "\t\t\t\t\tshininess 0.8\n"); - fprintf (fp, "\t\t\t\t}\n"); - fprintf (fp, "\t\t\t}\n"); - fprintf (fp, "\t\t\tgeometry IndexedLineSet {\n"); - fprintf (fp, "\t\t\t\tcoord Coordinate {\n"); - fprintf (fp, "\t\t\t\t\tpoint [\n"); - fprintf (fp, "\t\t\t\t\t0.0 0.0 0.0,\n"); - fprintf (fp, "\t\t\t\t\t%f 0.0 0.0,\n", 255.0); - fprintf (fp, "\t\t\t\t\t0.0 %f 0.0,\n", 255.0); - fprintf (fp, "\t\t\t\t\t0.0 0.0 %f]\n", 255.0); - fprintf (fp, "\t\t\t\t}\n"); - fprintf (fp, "\t\t\t\tcoordIndex [\n"); - fprintf (fp, "\t\t\t\t\t0, 1, -1\n"); - fprintf (fp, "\t\t\t\t\t0, 2, -1\n"); - fprintf (fp, "\t\t\t\t\t0, 3, -1]\n"); - fprintf (fp, "\t\t\t}\n"); - fprintf (fp, "\t\t}\n"); - - - fprintf (fp, "\t\tShape {\n"); - fprintf (fp, "\t\t\tappearance Appearance {\n"); - fprintf (fp, "\t\t\t\tmaterial Material {\n"); - fprintf (fp, "\t\t\t\t\tdiffuseColor 0 0.8 0\n"); - fprintf (fp, "\t\t\t\t\temissiveColor 1 1 1\n"); - fprintf (fp, "\t\t\t\t\tshininess 0.8\n"); - fprintf (fp, "\t\t\t\t}\n"); - fprintf (fp, "\t\t\t}\n"); - fprintf (fp, "\t\t\tgeometry PointSet {\n"); - - // fill in the points here - fprintf (fp, "\t\t\t\tcoord Coordinate {\n"); - fprintf (fp, "\t\t\t\t\tpoint [\n"); - - // We need to transverse all gamut hull. - for (i=0; i < SECTORS; i++) - for (j=0; j < SECTORS; j++) { - - cmsVEC3 v; - - pt = &gbd ->Gamut[i][j]; - ToCartesian(&v, &pt ->p); - - fprintf (fp, "\t\t\t\t\t%g %g %g", v.n[0]+50, v.n[1], v.n[2]); - - if ((j == SECTORS - 1) && (i == SECTORS - 1)) - fprintf (fp, "]\n"); - else - fprintf (fp, ",\n"); - - } - - fprintf (fp, "\t\t\t\t}\n"); - - - - // fill in the face colors - fprintf (fp, "\t\t\t\tcolor Color {\n"); - fprintf (fp, "\t\t\t\t\tcolor [\n"); - - for (i=0; i < SECTORS; i++) - for (j=0; j < SECTORS; j++) { - - cmsVEC3 v; - - pt = &gbd ->Gamut[i][j]; - - - ToCartesian(&v, &pt ->p); - - - if (pt ->Type == GP_EMPTY) - fprintf (fp, "\t\t\t\t\t%g %g %g", 0.0, 0.0, 0.0); - else - if (pt ->Type == GP_MODELED) - fprintf (fp, "\t\t\t\t\t%g %g %g", 1.0, .5, .5); - else { - fprintf (fp, "\t\t\t\t\t%g %g %g", 1.0, 1.0, 1.0); - - } - - if ((j == SECTORS - 1) && (i == SECTORS - 1)) - fprintf (fp, "]\n"); - else - fprintf (fp, ",\n"); - } - fprintf (fp, "\t\t\t}\n"); - - - fprintf (fp, "\t\t\t}\n"); - fprintf (fp, "\t\t}\n"); - fprintf (fp, "\t]\n"); - fprintf (fp, "}\n"); - - fclose (fp); - - return TRUE; -} -#endif diff --git a/third_party/lcms2-2.6/src/cmstypes.c b/third_party/lcms2-2.6/src/cmstypes.c deleted file mode 100644 index 29806fb194..0000000000 --- a/third_party/lcms2-2.6/src/cmstypes.c +++ /dev/null @@ -1,5610 +0,0 @@ -//--------------------------------------------------------------------------------- -// -// 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 "lcms2_internal.h" - -// Tag Serialization ----------------------------------------------------------------------------- -// This file implements every single tag and tag type as described in the ICC spec. Some types -// have been deprecated, like ncl and Data. There is no implementation for those types as there -// are no profiles holding them. The programmer can also extend this list by defining his own types -// by using the appropiate plug-in. There are three types of plug ins regarding that. First type -// allows to define new tags using any existing type. Next plug-in type allows to define new types -// and the third one is very specific: allows to extend the number of elements in the multiprocessing -// elements special type. -//-------------------------------------------------------------------------------------------------- - -// Some broken types -#define cmsCorbisBrokenXYZtype ((cmsTagTypeSignature) 0x17A505B8) -#define cmsMonacoBrokenCurveType ((cmsTagTypeSignature) 0x9478ee00) - -// This is the linked list that keeps track of the defined types -typedef struct _cmsTagTypeLinkedList_st { - - cmsTagTypeHandler Handler; - struct _cmsTagTypeLinkedList_st* Next; - -} _cmsTagTypeLinkedList; - -// Some macros to define callbacks. -#define READ_FN(x) Type_##x##_Read -#define WRITE_FN(x) Type_##x##_Write -#define FREE_FN(x) Type_##x##_Free -#define DUP_FN(x) Type_##x##_Dup - -// Helper macro to define a handler. Callbacks do have a fixed naming convention. -#define TYPE_HANDLER(t, x) { (t), READ_FN(x), WRITE_FN(x), DUP_FN(x), FREE_FN(x), NULL, 0 } - -// 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. LinkedList points to the optional list head -static -cmsBool RegisterTypesPlugin(cmsContext id, cmsPluginBase* Data, _cmsMemoryClient pos) -{ - cmsPluginTagType* Plugin = (cmsPluginTagType*) Data; - _cmsTagTypePluginChunkType* ctx = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(id, pos); - _cmsTagTypeLinkedList *pt; - - // Calling the function with NULL as plug-in would unregister the plug in. - if (Data == NULL) { - - // There is no need to set free the memory, as pool is destroyed as a whole. - ctx ->TagTypes = NULL; - return TRUE; - } - - // Registering happens in plug-in memory pool. - pt = (_cmsTagTypeLinkedList*) _cmsPluginMalloc(id, sizeof(_cmsTagTypeLinkedList)); - if (pt == NULL) return FALSE; - - pt ->Handler = Plugin ->Handler; - 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. It first tries the additons -// made by plug-ins and then the built-in defaults. -static -cmsTagTypeHandler* GetHandler(cmsTagTypeSignature sig, _cmsTagTypeLinkedList* PluginLinkedList, _cmsTagTypeLinkedList* DefaultLinkedList) -{ - _cmsTagTypeLinkedList* pt; - - for (pt = PluginLinkedList; - pt != NULL; - pt = pt ->Next) { - - if (sig == pt -> Handler.Signature) return &pt ->Handler; - } - - for (pt = DefaultLinkedList; - pt != NULL; - pt = pt ->Next) { - - if (sig == pt -> Handler.Signature) return &pt ->Handler; - } - - return NULL; -} - - -// Auxiliar to convert UTF-32 to UTF-16 in some cases -static -cmsBool _cmsWriteWCharArray(cmsIOHANDLER* io, cmsUInt32Number n, const wchar_t* Array) -{ - cmsUInt32Number i; - - _cmsAssert(io != NULL); - _cmsAssert(!(Array == NULL && n > 0)); - - for (i=0; i < n; i++) { - if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) Array[i])) return FALSE; - } - - return TRUE; -} - -// Auxiliar to read an array of wchar_t -static -cmsBool _cmsReadWCharArray(cmsIOHANDLER* io, cmsUInt32Number n, wchar_t* Array) -{ - cmsUInt32Number i; - cmsUInt16Number tmp; - - _cmsAssert(io != NULL); - - for (i=0; i < n; i++) { - - if (Array != NULL) { - - if (!_cmsReadUInt16Number(io, &tmp)) return FALSE; - Array[i] = (wchar_t) tmp; - } - else { - if (!_cmsReadUInt16Number(io, NULL)) return FALSE; - } - - } - return TRUE; -} - -// To deal with position tables -typedef cmsBool (* PositionTableEntryFn)(struct _cms_typehandler_struct* self, - cmsIOHANDLER* io, - void* Cargo, - cmsUInt32Number n, - cmsUInt32Number SizeOfTag); - -// Helper function to deal with position tables as decribed in ICC spec 4.3 -// A table of n elements is readed, where first comes n records containing offsets and sizes and -// then a block containing the data itself. This allows to reuse same data in more than one entry -static -cmsBool ReadPositionTable(struct _cms_typehandler_struct* self, - cmsIOHANDLER* io, - cmsUInt32Number Count, - cmsUInt32Number BaseOffset, - void *Cargo, - PositionTableEntryFn ElementFn) -{ - cmsUInt32Number i; - cmsUInt32Number *ElementOffsets = NULL, *ElementSizes = NULL; - cmsUInt32Number currentPosition; - - currentPosition = io->Tell(io); - // Verify there is enough space left to read two cmsUInt32Number items for Count items. - if (((io->ReportedSize - currentPosition) / (2 * sizeof(cmsUInt32Number))) < Count) - return FALSE; - - // Let's take the offsets to each element - ElementOffsets = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number)); - if (ElementOffsets == NULL) goto Error; - - ElementSizes = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number)); - if (ElementSizes == NULL) goto Error; - - for (i=0; i < Count; i++) { - - if (!_cmsReadUInt32Number(io, &ElementOffsets[i])) goto Error; - if (!_cmsReadUInt32Number(io, &ElementSizes[i])) goto Error; - - ElementOffsets[i] += BaseOffset; - } - - // Seek to each element and read it - for (i=0; i < Count; i++) { - - if (!io -> Seek(io, ElementOffsets[i])) goto Error; - - // This is the reader callback - if (!ElementFn(self, io, Cargo, i, ElementSizes[i])) goto Error; - } - - // Success - if (ElementOffsets != NULL) _cmsFree(io ->ContextID, ElementOffsets); - if (ElementSizes != NULL) _cmsFree(io ->ContextID, ElementSizes); - return TRUE; - -Error: - if (ElementOffsets != NULL) _cmsFree(io ->ContextID, ElementOffsets); - if (ElementSizes != NULL) _cmsFree(io ->ContextID, ElementSizes); - return FALSE; -} - -// Same as anterior, but for write position tables -static -cmsBool WritePositionTable(struct _cms_typehandler_struct* self, - cmsIOHANDLER* io, - cmsUInt32Number SizeOfTag, - cmsUInt32Number Count, - cmsUInt32Number BaseOffset, - void *Cargo, - PositionTableEntryFn ElementFn) -{ - cmsUInt32Number i; - cmsUInt32Number DirectoryPos, CurrentPos, Before; - cmsUInt32Number *ElementOffsets = NULL, *ElementSizes = NULL; - - // Create table - ElementOffsets = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number)); - if (ElementOffsets == NULL) goto Error; - - ElementSizes = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number)); - if (ElementSizes == NULL) goto Error; - - // Keep starting position of curve offsets - DirectoryPos = io ->Tell(io); - - // Write a fake directory to be filled latter on - for (i=0; i < Count; i++) { - - if (!_cmsWriteUInt32Number(io, 0)) goto Error; // Offset - if (!_cmsWriteUInt32Number(io, 0)) goto Error; // size - } - - // Write each element. Keep track of the size as well. - for (i=0; i < Count; i++) { - - Before = io ->Tell(io); - ElementOffsets[i] = Before - BaseOffset; - - // Callback to write... - if (!ElementFn(self, io, Cargo, i, SizeOfTag)) goto Error; - - // Now the size - ElementSizes[i] = io ->Tell(io) - Before; - } - - // Write the directory - CurrentPos = io ->Tell(io); - if (!io ->Seek(io, DirectoryPos)) goto Error; - - for (i=0; i < Count; i++) { - if (!_cmsWriteUInt32Number(io, ElementOffsets[i])) goto Error; - if (!_cmsWriteUInt32Number(io, ElementSizes[i])) goto Error; - } - - if (!io ->Seek(io, CurrentPos)) goto Error; - - if (ElementOffsets != NULL) _cmsFree(io ->ContextID, ElementOffsets); - if (ElementSizes != NULL) _cmsFree(io ->ContextID, ElementSizes); - return TRUE; - -Error: - if (ElementOffsets != NULL) _cmsFree(io ->ContextID, ElementOffsets); - if (ElementSizes != NULL) _cmsFree(io ->ContextID, ElementSizes); - return FALSE; -} - - -// ******************************************************************************** -// Type XYZ. Only one value is allowed -// ******************************************************************************** - -//The XYZType contains an array of three encoded values for the XYZ tristimulus -//values. Tristimulus values must be non-negative. The signed encoding allows for -//implementation optimizations by minimizing the number of fixed formats. - - -static -void *Type_XYZ_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) -{ - cmsCIEXYZ* xyz; - - *nItems = 0; - xyz = (cmsCIEXYZ*) _cmsMallocZero(self ->ContextID, sizeof(cmsCIEXYZ)); - if (xyz == NULL) return NULL; - - if (!_cmsReadXYZNumber(io, xyz)) { - _cmsFree(self ->ContextID, xyz); - return NULL; - } - - *nItems = 1; - return (void*) xyz; - - cmsUNUSED_PARAMETER(SizeOfTag); -} - -static -cmsBool Type_XYZ_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) -{ - return _cmsWriteXYZNumber(io, (cmsCIEXYZ*) Ptr); - - cmsUNUSED_PARAMETER(nItems); - cmsUNUSED_PARAMETER(self); -} - -static -void* Type_XYZ_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) -{ - return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsCIEXYZ)); - - cmsUNUSED_PARAMETER(n); -} - -static -void Type_XYZ_Free(struct _cms_typehandler_struct* self, void *Ptr) -{ - _cmsFree(self ->ContextID, Ptr); -} - - -static -cmsTagTypeSignature DecideXYZtype(cmsFloat64Number ICCVersion, const void *Data) -{ - return cmsSigXYZType; - - cmsUNUSED_PARAMETER(ICCVersion); - cmsUNUSED_PARAMETER(Data); -} - - -// ******************************************************************************** -// Type chromaticity. Only one value is allowed -// ******************************************************************************** -// The chromaticity tag type provides basic chromaticity data and type of -// phosphors or colorants of a monitor to applications and utilities. - -static -void *Type_Chromaticity_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) -{ - cmsCIExyYTRIPLE* chrm; - cmsUInt16Number nChans, Table; - - *nItems = 0; - chrm = (cmsCIExyYTRIPLE*) _cmsMallocZero(self ->ContextID, sizeof(cmsCIExyYTRIPLE)); - if (chrm == NULL) return NULL; - - if (!_cmsReadUInt16Number(io, &nChans)) goto Error; - - // Let's recover from a bug introduced in early versions of lcms1 - if (nChans == 0 && SizeOfTag == 32) { - - if (!_cmsReadUInt16Number(io, NULL)) goto Error; - if (!_cmsReadUInt16Number(io, &nChans)) goto Error; - } - - if (nChans != 3) goto Error; - - if (!_cmsReadUInt16Number(io, &Table)) goto Error; - - if (!_cmsRead15Fixed16Number(io, &chrm ->Red.x)) goto Error; - if (!_cmsRead15Fixed16Number(io, &chrm ->Red.y)) goto Error; - - chrm ->Red.Y = 1.0; - - if (!_cmsRead15Fixed16Number(io, &chrm ->Green.x)) goto Error; - if (!_cmsRead15Fixed16Number(io, &chrm ->Green.y)) goto Error; - - chrm ->Green.Y = 1.0; - - if (!_cmsRead15Fixed16Number(io, &chrm ->Blue.x)) goto Error; - if (!_cmsRead15Fixed16Number(io, &chrm ->Blue.y)) goto Error; - - chrm ->Blue.Y = 1.0; - - *nItems = 1; - return (void*) chrm; - -Error: - _cmsFree(self ->ContextID, (void*) chrm); - return NULL; - - cmsUNUSED_PARAMETER(SizeOfTag); -} - -static -cmsBool SaveOneChromaticity(cmsFloat64Number x, cmsFloat64Number y, cmsIOHANDLER* io) -{ - if (!_cmsWriteUInt32Number(io, _cmsDoubleTo15Fixed16(x))) return FALSE; - if (!_cmsWriteUInt32Number(io, _cmsDoubleTo15Fixed16(y))) return FALSE; - - return TRUE; -} - -static -cmsBool Type_Chromaticity_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) -{ - cmsCIExyYTRIPLE* chrm = (cmsCIExyYTRIPLE*) Ptr; - - if (!_cmsWriteUInt16Number(io, 3)) return FALSE; // nChannels - if (!_cmsWriteUInt16Number(io, 0)) return FALSE; // Table - - if (!SaveOneChromaticity(chrm -> Red.x, chrm -> Red.y, io)) return FALSE; - if (!SaveOneChromaticity(chrm -> Green.x, chrm -> Green.y, io)) return FALSE; - if (!SaveOneChromaticity(chrm -> Blue.x, chrm -> Blue.y, io)) return FALSE; - - return TRUE; - - cmsUNUSED_PARAMETER(nItems); - cmsUNUSED_PARAMETER(self); -} - -static -void* Type_Chromaticity_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) -{ - return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsCIExyYTRIPLE)); - - cmsUNUSED_PARAMETER(n); -} - -static -void Type_Chromaticity_Free(struct _cms_typehandler_struct* self, void* Ptr) -{ - _cmsFree(self ->ContextID, Ptr); -} - - -// ******************************************************************************** -// Type cmsSigColorantOrderType -// ******************************************************************************** - -// This is an optional tag which specifies the laydown order in which colorants will -// be printed on an n-colorant device. The laydown order may be the same as the -// channel generation order listed in the colorantTableTag or the channel order of a -// colour space such as CMYK, in which case this tag is not needed. When this is not -// the case (for example, ink-towers sometimes use the order KCMY), this tag may be -// used to specify the laydown order of the colorants. - - -static -void *Type_ColorantOrderType_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) -{ - cmsUInt8Number* ColorantOrder; - cmsUInt32Number Count; - - *nItems = 0; - if (!_cmsReadUInt32Number(io, &Count)) return NULL; - if (Count > cmsMAXCHANNELS) return NULL; - - ColorantOrder = (cmsUInt8Number*) _cmsCalloc(self ->ContextID, cmsMAXCHANNELS, sizeof(cmsUInt8Number)); - if (ColorantOrder == NULL) return NULL; - - // We use FF as end marker - memset(ColorantOrder, 0xFF, cmsMAXCHANNELS * sizeof(cmsUInt8Number)); - - if (io ->Read(io, ColorantOrder, sizeof(cmsUInt8Number), Count) != Count) { - - _cmsFree(self ->ContextID, (void*) ColorantOrder); - return NULL; - } - - *nItems = 1; - return (void*) ColorantOrder; - - cmsUNUSED_PARAMETER(SizeOfTag); -} - -static -cmsBool Type_ColorantOrderType_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) -{ - cmsUInt8Number* ColorantOrder = (cmsUInt8Number*) Ptr; - cmsUInt32Number i, sz, Count; - - // Get the length - for (Count=i=0; i < cmsMAXCHANNELS; i++) { - if (ColorantOrder[i] != 0xFF) Count++; - } - - if (!_cmsWriteUInt32Number(io, Count)) return FALSE; - - sz = Count * sizeof(cmsUInt8Number); - if (!io -> Write(io, sz, ColorantOrder)) return FALSE; - - return TRUE; - - cmsUNUSED_PARAMETER(nItems); - cmsUNUSED_PARAMETER(self); -} - -static -void* Type_ColorantOrderType_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) -{ - return _cmsDupMem(self ->ContextID, Ptr, cmsMAXCHANNELS * sizeof(cmsUInt8Number)); - - cmsUNUSED_PARAMETER(n); -} - - -static -void Type_ColorantOrderType_Free(struct _cms_typehandler_struct* self, void* Ptr) -{ - _cmsFree(self ->ContextID, Ptr); -} - -// ******************************************************************************** -// Type cmsSigS15Fixed16ArrayType -// ******************************************************************************** -// This type represents an array of generic 4-byte/32-bit fixed point quantity. -// The number of values is determined from the size of the tag. - -static -void *Type_S15Fixed16_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) -{ - cmsFloat64Number* array_double; - cmsUInt32Number i, n; - - *nItems = 0; - n = SizeOfTag / sizeof(cmsUInt32Number); - array_double = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, n, sizeof(cmsFloat64Number)); - if (array_double == NULL) return NULL; - - for (i=0; i < n; i++) { - - if (!_cmsRead15Fixed16Number(io, &array_double[i])) { - - _cmsFree(self ->ContextID, array_double); - return NULL; - } - } - - *nItems = n; - return (void*) array_double; -} - -static -cmsBool Type_S15Fixed16_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) -{ - cmsFloat64Number* Value = (cmsFloat64Number*) Ptr; - cmsUInt32Number i; - - for (i=0; i < nItems; i++) { - - if (!_cmsWrite15Fixed16Number(io, Value[i])) return FALSE; - } - - return TRUE; - - cmsUNUSED_PARAMETER(self); -} - -static -void* Type_S15Fixed16_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) -{ - return _cmsDupMem(self ->ContextID, Ptr, n * sizeof(cmsFloat64Number)); -} - - -static -void Type_S15Fixed16_Free(struct _cms_typehandler_struct* self, void* Ptr) -{ - _cmsFree(self ->ContextID, Ptr); -} - -// ******************************************************************************** -// Type cmsSigU16Fixed16ArrayType -// ******************************************************************************** -// This type represents an array of generic 4-byte/32-bit quantity. -// The number of values is determined from the size of the tag. - - -static -void *Type_U16Fixed16_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) -{ - cmsFloat64Number* array_double; - cmsUInt32Number v; - cmsUInt32Number i, n; - - *nItems = 0; - n = SizeOfTag / sizeof(cmsUInt32Number); - array_double = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, n, sizeof(cmsFloat64Number)); - if (array_double == NULL) return NULL; - - for (i=0; i < n; i++) { - - if (!_cmsReadUInt32Number(io, &v)) { - _cmsFree(self ->ContextID, (void*) array_double); - return NULL; - } - - // Convert to cmsFloat64Number - array_double[i] = (cmsFloat64Number) (v / 65536.0); - } - - *nItems = n; - return (void*) array_double; -} - -static -cmsBool Type_U16Fixed16_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) -{ - cmsFloat64Number* Value = (cmsFloat64Number*) Ptr; - cmsUInt32Number i; - - for (i=0; i < nItems; i++) { - - cmsUInt32Number v = (cmsUInt32Number) floor(Value[i]*65536.0 + 0.5); - - if (!_cmsWriteUInt32Number(io, v)) return FALSE; - } - - return TRUE; - - cmsUNUSED_PARAMETER(self); -} - - -static -void* Type_U16Fixed16_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) -{ - return _cmsDupMem(self ->ContextID, Ptr, n * sizeof(cmsFloat64Number)); -} - -static -void Type_U16Fixed16_Free(struct _cms_typehandler_struct* self, void* Ptr) -{ - _cmsFree(self ->ContextID, Ptr); -} - -// ******************************************************************************** -// Type cmsSigSignatureType -// ******************************************************************************** -// -// The signatureType contains a four-byte sequence, Sequences of less than four -// characters are padded at the end with spaces, 20h. -// Typically this type is used for registered tags that can be displayed on many -// development systems as a sequence of four characters. - -static -void *Type_Signature_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) -{ - cmsSignature* SigPtr = (cmsSignature*) _cmsMalloc(self ->ContextID, sizeof(cmsSignature)); - if (SigPtr == NULL) return NULL; - - if (!_cmsReadUInt32Number(io, SigPtr)) return NULL; - *nItems = 1; - - return SigPtr; - - cmsUNUSED_PARAMETER(SizeOfTag); -} - -static -cmsBool Type_Signature_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) -{ - cmsSignature* SigPtr = (cmsSignature*) Ptr; - - return _cmsWriteUInt32Number(io, *SigPtr); - - cmsUNUSED_PARAMETER(nItems); - cmsUNUSED_PARAMETER(self); -} - -static -void* Type_Signature_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) -{ - return _cmsDupMem(self ->ContextID, Ptr, n * sizeof(cmsSignature)); -} - -static -void Type_Signature_Free(struct _cms_typehandler_struct* self, void* Ptr) -{ - _cmsFree(self ->ContextID, Ptr); -} - - -// ******************************************************************************** -// Type cmsSigTextType -// ******************************************************************************** -// -// The textType is a simple text structure that contains a 7-bit ASCII text string. -// The length of the string is obtained by subtracting 8 from the element size portion -// of the tag itself. This string must be terminated with a 00h byte. - -static -void *Type_Text_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) -{ - char* Text = NULL; - cmsMLU* mlu = NULL; - - // Create a container - mlu = cmsMLUalloc(self ->ContextID, 1); - if (mlu == NULL) return NULL; - - *nItems = 0; - - // We need to store the "\0" at the end, so +1 - if (SizeOfTag == UINT_MAX) goto Error; - - Text = (char*) _cmsMalloc(self ->ContextID, SizeOfTag + 1); - if (Text == NULL) goto Error; - - if (io -> Read(io, Text, sizeof(char), SizeOfTag) != SizeOfTag) goto Error; - - // Make sure text is properly ended - Text[SizeOfTag] = 0; - *nItems = 1; - - // Keep the result - if (!cmsMLUsetASCII(mlu, cmsNoLanguage, cmsNoCountry, Text)) goto Error; - - _cmsFree(self ->ContextID, Text); - return (void*) mlu; - -Error: - if (mlu != NULL) - cmsMLUfree(mlu); - if (Text != NULL) - _cmsFree(self ->ContextID, Text); - - return NULL; -} - -// The conversion implies to choose a language. So, we choose the actual language. -static -cmsBool Type_Text_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) -{ - cmsMLU* mlu = (cmsMLU*) Ptr; - cmsUInt32Number size; - cmsBool rc; - char* Text; - - // Get the size of the string. Note there is an extra "\0" at the end - size = cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, NULL, 0); - if (size == 0) return FALSE; // Cannot be zero! - - // Create memory - Text = (char*) _cmsMalloc(self ->ContextID, size); - if (Text == NULL) return FALSE; - - cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, Text, size); - - // Write it, including separator - rc = io ->Write(io, size, Text); - - _cmsFree(self ->ContextID, Text); - return rc; - - cmsUNUSED_PARAMETER(nItems); -} - -static -void* Type_Text_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) -{ - return (void*) cmsMLUdup((cmsMLU*) Ptr); - - cmsUNUSED_PARAMETER(n); - cmsUNUSED_PARAMETER(self); -} - - -static -void Type_Text_Free(struct _cms_typehandler_struct* self, void* Ptr) -{ - cmsMLU* mlu = (cmsMLU*) Ptr; - cmsMLUfree(mlu); - return; - - cmsUNUSED_PARAMETER(self); -} - -static -cmsTagTypeSignature DecideTextType(cmsFloat64Number ICCVersion, const void *Data) -{ - if (ICCVersion >= 4.0) - return cmsSigMultiLocalizedUnicodeType; - - return cmsSigTextType; - - cmsUNUSED_PARAMETER(Data); -} - - -// ******************************************************************************** -// Type cmsSigDataType -// ******************************************************************************** - -// General purpose data type -static -void *Type_Data_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) -{ - cmsICCData* BinData; - cmsUInt32Number LenOfData; - - *nItems = 0; - - if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL; - - LenOfData = SizeOfTag - sizeof(cmsUInt32Number); - if (LenOfData > INT_MAX) return NULL; - - BinData = (cmsICCData*) _cmsMalloc(self ->ContextID, sizeof(cmsICCData) + LenOfData - 1); - if (BinData == NULL) return NULL; - - BinData ->len = LenOfData; - if (!_cmsReadUInt32Number(io, &BinData->flag)) { - _cmsFree(self ->ContextID, BinData); - return NULL; - } - - if (io -> Read(io, BinData ->data, sizeof(cmsUInt8Number), LenOfData) != LenOfData) { - - _cmsFree(self ->ContextID, BinData); - return NULL; - } - - *nItems = 1; - - return (void*) BinData; -} - - -static -cmsBool Type_Data_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) -{ - cmsICCData* BinData = (cmsICCData*) Ptr; - - if (!_cmsWriteUInt32Number(io, BinData ->flag)) return FALSE; - - return io ->Write(io, BinData ->len, BinData ->data); - - cmsUNUSED_PARAMETER(nItems); - cmsUNUSED_PARAMETER(self); -} - - -static -void* Type_Data_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) -{ - cmsICCData* BinData = (cmsICCData*) Ptr; - - return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsICCData) + BinData ->len - 1); - - cmsUNUSED_PARAMETER(n); -} - -static -void Type_Data_Free(struct _cms_typehandler_struct* self, void* Ptr) -{ - _cmsFree(self ->ContextID, Ptr); -} - -// ******************************************************************************** -// Type cmsSigTextDescriptionType -// ******************************************************************************** - -static -void *Type_Text_Description_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) -{ - char* Text = NULL; - cmsMLU* mlu = NULL; - cmsUInt32Number AsciiCount; - cmsUInt32Number i, UnicodeCode, UnicodeCount; - cmsUInt16Number ScriptCodeCode, Dummy; - cmsUInt8Number ScriptCodeCount; - - *nItems = 0; - - // One dword should be there - if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL; - - // Read len of ASCII - if (!_cmsReadUInt32Number(io, &AsciiCount)) return NULL; - SizeOfTag -= sizeof(cmsUInt32Number); - - // Check for size - if (SizeOfTag < AsciiCount) return NULL; - - // All seems Ok, allocate the container - mlu = cmsMLUalloc(self ->ContextID, 1); - if (mlu == NULL) return NULL; - - // As many memory as size of tag - Text = (char*) _cmsMalloc(self ->ContextID, AsciiCount + 1); - if (Text == NULL) goto Error; - - // Read it - if (io ->Read(io, Text, sizeof(char), AsciiCount) != AsciiCount) goto Error; - SizeOfTag -= AsciiCount; - - // Make sure there is a terminator - Text[AsciiCount] = 0; - - // Set the MLU entry. From here we can be tolerant to wrong types - if (!cmsMLUsetASCII(mlu, cmsNoLanguage, cmsNoCountry, Text)) goto Error; - _cmsFree(self ->ContextID, (void*) Text); - Text = NULL; - - // Skip Unicode code - if (SizeOfTag < 2* sizeof(cmsUInt32Number)) goto Done; - if (!_cmsReadUInt32Number(io, &UnicodeCode)) goto Done; - if (!_cmsReadUInt32Number(io, &UnicodeCount)) goto Done; - SizeOfTag -= 2* sizeof(cmsUInt32Number); - - if (SizeOfTag < UnicodeCount*sizeof(cmsUInt16Number)) goto Done; - - for (i=0; i < UnicodeCount; i++) { - if (!io ->Read(io, &Dummy, sizeof(cmsUInt16Number), 1)) goto Done; - } - SizeOfTag -= UnicodeCount*sizeof(cmsUInt16Number); - - // Skip ScriptCode code if present. Some buggy profiles does have less - // data that stricttly required. We need to skip it as this type may come - // embedded in other types. - - if (SizeOfTag >= sizeof(cmsUInt16Number) + sizeof(cmsUInt8Number) + 67) { - - if (!_cmsReadUInt16Number(io, &ScriptCodeCode)) goto Done; - if (!_cmsReadUInt8Number(io, &ScriptCodeCount)) goto Done; - - // Skip rest of tag - for (i=0; i < 67; i++) { - if (!io ->Read(io, &Dummy, sizeof(cmsUInt8Number), 1)) goto Error; - } - } - -Done: - - *nItems = 1; - return mlu; - -Error: - if (Text) _cmsFree(self ->ContextID, (void*) Text); - if (mlu) cmsMLUfree(mlu); - return NULL; -} - - -// This tag can come IN UNALIGNED SIZE. In order to prevent issues, we force zeros on description to align it -static -cmsBool Type_Text_Description_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) -{ - cmsMLU* mlu = (cmsMLU*) Ptr; - char *Text = NULL; - wchar_t *Wide = NULL; - cmsUInt32Number len, len_aligned, len_filler_alignment; - cmsBool rc = FALSE; - char Filler[68]; - - // Used below for writting zeroes - memset(Filler, 0, sizeof(Filler)); - - // Get the len of string - len = cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, NULL, 0); - - // From ICC3.4: It has been found that textDescriptionType can contain misaligned data - //(see clause 4.1 for the definition of 'aligned'. Because the Unicode language - // code and Unicode count immediately follow the ASCII description, their - // alignment is not correct if the ASCII count is not a multiple of four. The - // ScriptCode code is misaligned when the ASCII count is odd. Profile reading and - // writing software must be written carefully in order to handle these alignment - // problems. - - // Compute an aligned size - len_aligned = _cmsALIGNLONG(len); - len_filler_alignment = len_aligned - len; - - // Null strings - if (len <= 0) { - - Text = (char*) _cmsDupMem(self ->ContextID, "", sizeof(char)); - Wide = (wchar_t*) _cmsDupMem(self ->ContextID, L"", sizeof(wchar_t)); - } - else { - // Create independent buffers - Text = (char*) _cmsCalloc(self ->ContextID, len, sizeof(char)); - if (Text == NULL) goto Error; - - Wide = (wchar_t*) _cmsCalloc(self ->ContextID, len, sizeof(wchar_t)); - if (Wide == NULL) goto Error; - - // Get both representations. - cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, Text, len * sizeof(char)); - cmsMLUgetWide(mlu, cmsNoLanguage, cmsNoCountry, Wide, len * sizeof(wchar_t)); - } - - // * cmsUInt32Number count; * Description length - // * cmsInt8Number desc[count] * NULL terminated ascii string - // * cmsUInt32Number ucLangCode; * UniCode language code - // * cmsUInt32Number ucCount; * UniCode description length - // * cmsInt16Number ucDesc[ucCount];* The UniCode description - // * cmsUInt16Number scCode; * ScriptCode code - // * cmsUInt8Number scCount; * ScriptCode count - // * cmsInt8Number scDesc[67]; * ScriptCode Description - - if (!_cmsWriteUInt32Number(io, len_aligned)) goto Error; - if (!io ->Write(io, len, Text)) goto Error; - if (!io ->Write(io, len_filler_alignment, Filler)) goto Error; - - if (!_cmsWriteUInt32Number(io, 0)) goto Error; // ucLanguageCode - - // This part is tricky: we need an aligned tag size, and the ScriptCode part - // takes 70 bytes, so we need 2 extra bytes to do the alignment - - if (!_cmsWriteUInt32Number(io, len_aligned+1)) goto Error; - - // Note that in some compilers sizeof(cmsUInt16Number) != sizeof(wchar_t) - if (!_cmsWriteWCharArray(io, len, Wide)) goto Error; - if (!_cmsWriteUInt16Array(io, len_filler_alignment+1, (cmsUInt16Number*) Filler)) goto Error; - - // ScriptCode Code & count (unused) - if (!_cmsWriteUInt16Number(io, 0)) goto Error; - if (!_cmsWriteUInt8Number(io, 0)) goto Error; - - if (!io ->Write(io, 67, Filler)) goto Error; - - rc = TRUE; - -Error: - if (Text) _cmsFree(self ->ContextID, Text); - if (Wide) _cmsFree(self ->ContextID, Wide); - - return rc; - - cmsUNUSED_PARAMETER(nItems); -} - - -static -void* Type_Text_Description_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) -{ - return (void*) cmsMLUdup((cmsMLU*) Ptr); - - cmsUNUSED_PARAMETER(n); - cmsUNUSED_PARAMETER(self); -} - -static -void Type_Text_Description_Free(struct _cms_typehandler_struct* self, void* Ptr) -{ - cmsMLU* mlu = (cmsMLU*) Ptr; - - cmsMLUfree(mlu); - return; - - cmsUNUSED_PARAMETER(self); -} - - -static -cmsTagTypeSignature DecideTextDescType(cmsFloat64Number ICCVersion, const void *Data) -{ - if (ICCVersion >= 4.0) - return cmsSigMultiLocalizedUnicodeType; - - return cmsSigTextDescriptionType; - - cmsUNUSED_PARAMETER(Data); -} - - -// ******************************************************************************** -// Type cmsSigCurveType -// ******************************************************************************** - -static -void *Type_Curve_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) -{ - cmsUInt32Number Count; - cmsToneCurve* NewGamma; - - *nItems = 0; - if (!_cmsReadUInt32Number(io, &Count)) return NULL; - - switch (Count) { - - case 0: // Linear. - { - cmsFloat64Number SingleGamma = 1.0; - - NewGamma = cmsBuildParametricToneCurve(self ->ContextID, 1, &SingleGamma); - if (!NewGamma) return NULL; - *nItems = 1; - return NewGamma; - } - - case 1: // Specified as the exponent of gamma function - { - cmsUInt16Number SingleGammaFixed; - cmsFloat64Number SingleGamma; - - if (!_cmsReadUInt16Number(io, &SingleGammaFixed)) return NULL; - SingleGamma = _cms8Fixed8toDouble(SingleGammaFixed); - - *nItems = 1; - return cmsBuildParametricToneCurve(self ->ContextID, 1, &SingleGamma); - } - - default: // Curve - - if (Count > 0x7FFF) - return NULL; // This is to prevent bad guys for doing bad things - - NewGamma = cmsBuildTabulatedToneCurve16(self ->ContextID, Count, NULL); - if (!NewGamma) return NULL; - - if (!_cmsReadUInt16Array(io, Count, NewGamma -> Table16)) { - cmsFreeToneCurve(NewGamma); - return NULL; - } - - *nItems = 1; - return NewGamma; - } - - cmsUNUSED_PARAMETER(SizeOfTag); -} - - -static -cmsBool Type_Curve_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) -{ - cmsToneCurve* Curve = (cmsToneCurve*) Ptr; - - if (Curve ->nSegments == 1 && Curve ->Segments[0].Type == 1) { - - // Single gamma, preserve number - cmsUInt16Number SingleGammaFixed = _cmsDoubleTo8Fixed8(Curve ->Segments[0].Params[0]); - - if (!_cmsWriteUInt32Number(io, 1)) return FALSE; - if (!_cmsWriteUInt16Number(io, SingleGammaFixed)) return FALSE; - return TRUE; - - } - - if (!_cmsWriteUInt32Number(io, Curve ->nEntries)) return FALSE; - return _cmsWriteUInt16Array(io, Curve ->nEntries, Curve ->Table16); - - cmsUNUSED_PARAMETER(nItems); - cmsUNUSED_PARAMETER(self); -} - - -static -void* Type_Curve_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) -{ - return (void*) cmsDupToneCurve((cmsToneCurve*) Ptr); - - cmsUNUSED_PARAMETER(n); - cmsUNUSED_PARAMETER(self); -} - -static -void Type_Curve_Free(struct _cms_typehandler_struct* self, void* Ptr) -{ - cmsToneCurve* gamma = (cmsToneCurve*) Ptr; - - cmsFreeToneCurve(gamma); - return; - - cmsUNUSED_PARAMETER(self); -} - - -// ******************************************************************************** -// Type cmsSigParametricCurveType -// ******************************************************************************** - - -// Decide which curve type to use on writting -static -cmsTagTypeSignature DecideCurveType(cmsFloat64Number ICCVersion, const void *Data) -{ - cmsToneCurve* Curve = (cmsToneCurve*) Data; - - if (ICCVersion < 4.0) return cmsSigCurveType; - if (Curve ->nSegments != 1) return cmsSigCurveType; // Only 1-segment curves can be saved as parametric - if (Curve ->Segments[0].Type < 0) return cmsSigCurveType; // Only non-inverted curves - if (Curve ->Segments[0].Type > 5) return cmsSigCurveType; // Only ICC parametric curves - - return cmsSigParametricCurveType; -} - -static -void *Type_ParametricCurve_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) -{ - static const int ParamsByType[] = { 1, 3, 4, 5, 7 }; - cmsFloat64Number Params[10]; - cmsUInt16Number Type; - int i, n; - cmsToneCurve* NewGamma; - - if (!_cmsReadUInt16Number(io, &Type)) return NULL; - if (!_cmsReadUInt16Number(io, NULL)) return NULL; // Reserved - - if (Type > 4) { - - cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown parametric curve type '%d'", Type); - return NULL; - } - - memset(Params, 0, sizeof(Params)); - n = ParamsByType[Type]; - - for (i=0; i < n; i++) { - - if (!_cmsRead15Fixed16Number(io, &Params[i])) return NULL; - } - - NewGamma = cmsBuildParametricToneCurve(self ->ContextID, Type+1, Params); - - *nItems = 1; - return NewGamma; - - cmsUNUSED_PARAMETER(SizeOfTag); -} - - -static -cmsBool Type_ParametricCurve_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) -{ - cmsToneCurve* Curve = (cmsToneCurve*) Ptr; - int i, nParams, typen; - static const int ParamsByType[] = { 0, 1, 3, 4, 5, 7 }; - - typen = Curve -> Segments[0].Type; - - if (Curve ->nSegments > 1 || typen < 1) { - - cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Multisegment or Inverted parametric curves cannot be written"); - return FALSE; - } - - if (typen > 5) { - cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported parametric curve"); - return FALSE; - } - - nParams = ParamsByType[typen]; - - if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) (Curve ->Segments[0].Type - 1))) return FALSE; - if (!_cmsWriteUInt16Number(io, 0)) return FALSE; // Reserved - - for (i=0; i < nParams; i++) { - - if (!_cmsWrite15Fixed16Number(io, Curve -> Segments[0].Params[i])) return FALSE; - } - - return TRUE; - - cmsUNUSED_PARAMETER(nItems); -} - -static -void* Type_ParametricCurve_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) -{ - return (void*) cmsDupToneCurve((cmsToneCurve*) Ptr); - - cmsUNUSED_PARAMETER(n); - cmsUNUSED_PARAMETER(self); -} - -static -void Type_ParametricCurve_Free(struct _cms_typehandler_struct* self, void* Ptr) -{ - cmsToneCurve* gamma = (cmsToneCurve*) Ptr; - - cmsFreeToneCurve(gamma); - return; - - cmsUNUSED_PARAMETER(self); -} - - -// ******************************************************************************** -// Type cmsSigDateTimeType -// ******************************************************************************** - -// A 12-byte value representation of the time and date, where the byte usage is assigned -// as specified in table 1. The actual values are encoded as 16-bit unsigned integers -// (uInt16Number - see 5.1.6). -// -// All the dateTimeNumber values in a profile shall be in Coordinated Universal Time -// (UTC, also known as GMT or ZULU Time). Profile writers are required to convert local -// time to UTC when setting these values. Programmes that display these values may show -// the dateTimeNumber as UTC, show the equivalent local time (at current locale), or -// display both UTC and local versions of the dateTimeNumber. - -static -void *Type_DateTime_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) -{ - cmsDateTimeNumber timestamp; - struct tm * NewDateTime; - - *nItems = 0; - NewDateTime = (struct tm*) _cmsMalloc(self ->ContextID, sizeof(struct tm)); - if (NewDateTime == NULL) return NULL; - - if (io->Read(io, ×tamp, sizeof(cmsDateTimeNumber), 1) != 1) return NULL; - - _cmsDecodeDateTimeNumber(×tamp, NewDateTime); - - *nItems = 1; - return NewDateTime; - - cmsUNUSED_PARAMETER(SizeOfTag); -} - - -static -cmsBool Type_DateTime_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) -{ - struct tm * DateTime = (struct tm*) Ptr; - cmsDateTimeNumber timestamp; - - _cmsEncodeDateTimeNumber(×tamp, DateTime); - if (!io ->Write(io, sizeof(cmsDateTimeNumber), ×tamp)) return FALSE; - - return TRUE; - - cmsUNUSED_PARAMETER(nItems); - cmsUNUSED_PARAMETER(self); -} - -static -void* Type_DateTime_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) -{ - return _cmsDupMem(self ->ContextID, Ptr, sizeof(struct tm)); - - cmsUNUSED_PARAMETER(n); -} - -static -void Type_DateTime_Free(struct _cms_typehandler_struct* self, void* Ptr) -{ - _cmsFree(self ->ContextID, Ptr); -} - - - -// ******************************************************************************** -// Type icMeasurementType -// ******************************************************************************** - -/* -The measurementType information refers only to the internal profile data and is -meant to provide profile makers an alternative to the default measurement -specifications. -*/ - -static -void *Type_Measurement_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) -{ - cmsICCMeasurementConditions mc; - - - memset(&mc, 0, sizeof(mc)); - - if (!_cmsReadUInt32Number(io, &mc.Observer)) return NULL; - if (!_cmsReadXYZNumber(io, &mc.Backing)) return NULL; - if (!_cmsReadUInt32Number(io, &mc.Geometry)) return NULL; - if (!_cmsRead15Fixed16Number(io, &mc.Flare)) return NULL; - if (!_cmsReadUInt32Number(io, &mc.IlluminantType)) return NULL; - - *nItems = 1; - return _cmsDupMem(self ->ContextID, &mc, sizeof(cmsICCMeasurementConditions)); - - cmsUNUSED_PARAMETER(SizeOfTag); -} - - -static -cmsBool Type_Measurement_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) -{ - cmsICCMeasurementConditions* mc =(cmsICCMeasurementConditions*) Ptr; - - if (!_cmsWriteUInt32Number(io, mc->Observer)) return FALSE; - if (!_cmsWriteXYZNumber(io, &mc->Backing)) return FALSE; - if (!_cmsWriteUInt32Number(io, mc->Geometry)) return FALSE; - if (!_cmsWrite15Fixed16Number(io, mc->Flare)) return FALSE; - if (!_cmsWriteUInt32Number(io, mc->IlluminantType)) return FALSE; - - return TRUE; - - cmsUNUSED_PARAMETER(nItems); - cmsUNUSED_PARAMETER(self); -} - -static -void* Type_Measurement_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) -{ - return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsICCMeasurementConditions)); - - cmsUNUSED_PARAMETER(n); -} - -static -void Type_Measurement_Free(struct _cms_typehandler_struct* self, void* Ptr) -{ - _cmsFree(self ->ContextID, Ptr); -} - - -// ******************************************************************************** -// Type cmsSigMultiLocalizedUnicodeType -// ******************************************************************************** -// -// Do NOT trust SizeOfTag as there is an issue on the definition of profileSequenceDescTag. See the TechNote from -// Max Derhak and Rohit Patil about this: basically the size of the string table should be guessed and cannot be -// taken from the size of tag if this tag is embedded as part of bigger structures (profileSequenceDescTag, for instance) -// - -static -void *Type_MLU_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) -{ - cmsMLU* mlu; - cmsUInt32Number Count, RecLen, NumOfWchar; - cmsUInt32Number SizeOfHeader; - cmsUInt32Number Len, Offset; - cmsUInt32Number i; - wchar_t* Block; - cmsUInt32Number BeginOfThisString, EndOfThisString, LargestPosition; - - *nItems = 0; - if (!_cmsReadUInt32Number(io, &Count)) return NULL; - if (!_cmsReadUInt32Number(io, &RecLen)) return NULL; - - if (RecLen != 12) { - - cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "multiLocalizedUnicodeType of len != 12 is not supported."); - return NULL; - } - - mlu = cmsMLUalloc(self ->ContextID, Count); - if (mlu == NULL) return NULL; - - mlu ->UsedEntries = Count; - - SizeOfHeader = 12 * Count + sizeof(_cmsTagBase); - LargestPosition = 0; - - for (i=0; i < Count; i++) { - - if (!_cmsReadUInt16Number(io, &mlu ->Entries[i].Language)) goto Error; - if (!_cmsReadUInt16Number(io, &mlu ->Entries[i].Country)) goto Error; - - // Now deal with Len and offset. - if (!_cmsReadUInt32Number(io, &Len)) goto Error; - if (!_cmsReadUInt32Number(io, &Offset)) goto Error; - - // Check for overflow - if (Offset < (SizeOfHeader + 8)) goto Error; - - // True begin of the string - BeginOfThisString = Offset - SizeOfHeader - 8; - - // Ajust to wchar_t elements - mlu ->Entries[i].Len = (Len * sizeof(wchar_t)) / sizeof(cmsUInt16Number); - mlu ->Entries[i].StrW = (BeginOfThisString * sizeof(wchar_t)) / sizeof(cmsUInt16Number); - - // To guess maximum size, add offset + len - EndOfThisString = BeginOfThisString + Len; - if (EndOfThisString > LargestPosition) - LargestPosition = EndOfThisString; - } - - // Now read the remaining of tag and fill all strings. Substract the directory - SizeOfTag = (LargestPosition * sizeof(wchar_t)) / sizeof(cmsUInt16Number); - if (SizeOfTag == 0) - { - Block = NULL; - NumOfWchar = 0; - - } - else - { - Block = (wchar_t*) _cmsMalloc(self ->ContextID, SizeOfTag); - if (Block == NULL) goto Error; - NumOfWchar = SizeOfTag / sizeof(wchar_t); - if (!_cmsReadWCharArray(io, NumOfWchar, Block)) goto Error; - } - - mlu ->MemPool = Block; - mlu ->PoolSize = SizeOfTag; - mlu ->PoolUsed = SizeOfTag; - - *nItems = 1; - return (void*) mlu; - -Error: - if (mlu) cmsMLUfree(mlu); - return NULL; -} - -static -cmsBool Type_MLU_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) -{ - cmsMLU* mlu =(cmsMLU*) Ptr; - cmsUInt32Number HeaderSize; - cmsUInt32Number Len, Offset; - int i; - - if (Ptr == NULL) { - - // Empty placeholder - if (!_cmsWriteUInt32Number(io, 0)) return FALSE; - if (!_cmsWriteUInt32Number(io, 12)) return FALSE; - return TRUE; - } - - if (!_cmsWriteUInt32Number(io, mlu ->UsedEntries)) return FALSE; - if (!_cmsWriteUInt32Number(io, 12)) return FALSE; - - HeaderSize = 12 * mlu ->UsedEntries + sizeof(_cmsTagBase); - - for (i=0; i < mlu ->UsedEntries; i++) { - - Len = mlu ->Entries[i].Len; - Offset = mlu ->Entries[i].StrW; - - Len = (Len * sizeof(cmsUInt16Number)) / sizeof(wchar_t); - Offset = (Offset * sizeof(cmsUInt16Number)) / sizeof(wchar_t) + HeaderSize + 8; - - if (!_cmsWriteUInt16Number(io, mlu ->Entries[i].Language)) return FALSE; - if (!_cmsWriteUInt16Number(io, mlu ->Entries[i].Country)) return FALSE; - if (!_cmsWriteUInt32Number(io, Len)) return FALSE; - if (!_cmsWriteUInt32Number(io, Offset)) return FALSE; - } - - if (!_cmsWriteWCharArray(io, mlu ->PoolUsed / sizeof(wchar_t), (wchar_t*) mlu ->MemPool)) return FALSE; - - return TRUE; - - cmsUNUSED_PARAMETER(nItems); - cmsUNUSED_PARAMETER(self); -} - - -static -void* Type_MLU_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) -{ - return (void*) cmsMLUdup((cmsMLU*) Ptr); - - cmsUNUSED_PARAMETER(n); - cmsUNUSED_PARAMETER(self); -} - -static -void Type_MLU_Free(struct _cms_typehandler_struct* self, void* Ptr) -{ - cmsMLUfree((cmsMLU*) Ptr); - return; - - cmsUNUSED_PARAMETER(self); -} - - -// ******************************************************************************** -// Type cmsSigLut8Type -// ******************************************************************************** - -// Decide which LUT type to use on writting -static -cmsTagTypeSignature DecideLUTtypeA2B(cmsFloat64Number ICCVersion, const void *Data) -{ - cmsPipeline* Lut = (cmsPipeline*) Data; - - if (ICCVersion < 4.0) { - if (Lut ->SaveAs8Bits) return cmsSigLut8Type; - return cmsSigLut16Type; - } - else { - return cmsSigLutAtoBType; - } -} - -static -cmsTagTypeSignature DecideLUTtypeB2A(cmsFloat64Number ICCVersion, const void *Data) -{ - cmsPipeline* Lut = (cmsPipeline*) Data; - - if (ICCVersion < 4.0) { - if (Lut ->SaveAs8Bits) return cmsSigLut8Type; - return cmsSigLut16Type; - } - else { - return cmsSigLutBtoAType; - } -} - -/* -This structure represents a colour transform using tables of 8-bit precision. -This type contains four processing elements: a 3 by 3 matrix (which shall be -the identity matrix unless the input colour space is XYZ), a set of one dimensional -input tables, a multidimensional lookup table, and a set of one dimensional output -tables. Data is processed using these elements via the following sequence: -(matrix) -> (1d input tables) -> (multidimensional lookup table - CLUT) -> (1d output tables) - -Byte Position Field Length (bytes) Content Encoded as... -8 1 Number of Input Channels (i) uInt8Number -9 1 Number of Output Channels (o) uInt8Number -10 1 Number of CLUT grid points (identical for each side) (g) uInt8Number -11 1 Reserved for padding (fill with 00h) - -12..15 4 Encoded e00 parameter s15Fixed16Number -*/ - - -// Read 8 bit tables as gamma functions -static -cmsBool Read8bitTables(cmsContext ContextID, cmsIOHANDLER* io, cmsPipeline* lut, int nChannels) -{ - cmsUInt8Number* Temp = NULL; - int i, j; - cmsToneCurve* Tables[cmsMAXCHANNELS]; - - if (nChannels > cmsMAXCHANNELS) return FALSE; - if (nChannels <= 0) return FALSE; - - memset(Tables, 0, sizeof(Tables)); - - Temp = (cmsUInt8Number*) _cmsMalloc(ContextID, 256); - if (Temp == NULL) return FALSE; - - for (i=0; i < nChannels; i++) { - Tables[i] = cmsBuildTabulatedToneCurve16(ContextID, 256, NULL); - if (Tables[i] == NULL) goto Error; - } - - for (i=0; i < nChannels; i++) { - - if (io ->Read(io, Temp, 256, 1) != 1) goto Error; - - for (j=0; j < 256; j++) - Tables[i]->Table16[j] = (cmsUInt16Number) FROM_8_TO_16(Temp[j]); - } - - _cmsFree(ContextID, Temp); - Temp = NULL; - - if (!cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, nChannels, Tables))) - goto Error; - - for (i=0; i < nChannels; i++) - cmsFreeToneCurve(Tables[i]); - - return TRUE; - -Error: - for (i=0; i < nChannels; i++) { - if (Tables[i]) cmsFreeToneCurve(Tables[i]); - } - - if (Temp) _cmsFree(ContextID, Temp); - return FALSE; -} - - -static -cmsBool Write8bitTables(cmsContext ContextID, cmsIOHANDLER* io, cmsUInt32Number n, _cmsStageToneCurvesData* Tables) -{ - int j; - cmsUInt32Number i; - cmsUInt8Number val; - - for (i=0; i < n; i++) { - - if (Tables) { - - // Usual case of identity curves - if ((Tables ->TheCurves[i]->nEntries == 2) && - (Tables->TheCurves[i]->Table16[0] == 0) && - (Tables->TheCurves[i]->Table16[1] == 65535)) { - - for (j=0; j < 256; j++) { - if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) j)) return FALSE; - } - } - else - if (Tables ->TheCurves[i]->nEntries != 256) { - cmsSignalError(ContextID, cmsERROR_RANGE, "LUT8 needs 256 entries on prelinearization"); - return FALSE; - } - else - for (j=0; j < 256; j++) { - - if (Tables != NULL) - val = (cmsUInt8Number) FROM_16_TO_8(Tables->TheCurves[i]->Table16[j]); - else - val = (cmsUInt8Number) j; - - if (!_cmsWriteUInt8Number(io, val)) return FALSE; - } - } - } - return TRUE; -} - - -// Check overflow -static -cmsUInt32Number uipow(cmsUInt32Number n, cmsUInt32Number a, cmsUInt32Number b) -{ - cmsUInt32Number rv = 1, rc; - - if (a == 0) return 0; - if (n == 0) return 0; - - for (; b > 0; b--) { - - rv *= a; - - // Check for overflow - if (rv > UINT_MAX / a) return (cmsUInt32Number) -1; - - } - - rc = rv * n; - - if (rv != rc / n) return (cmsUInt32Number) -1; - return rc; -} - - -// That will create a MPE LUT with Matrix, pre tables, CLUT and post tables. -// 8 bit lut may be scaled easely to v4 PCS, but we need also to properly adjust -// PCS on BToAxx tags and AtoB if abstract. We need to fix input direction. - -static -void *Type_LUT8_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) -{ - cmsUInt8Number InputChannels, OutputChannels, CLUTpoints; - cmsUInt8Number* Temp = NULL; - cmsPipeline* NewLUT = NULL; - cmsUInt32Number nTabSize, i; - cmsFloat64Number Matrix[3*3]; - - *nItems = 0; - - if (!_cmsReadUInt8Number(io, &InputChannels)) goto Error; - if (!_cmsReadUInt8Number(io, &OutputChannels)) goto Error; - if (!_cmsReadUInt8Number(io, &CLUTpoints)) goto Error; - - if (CLUTpoints == 1) goto Error; // Impossible value, 0 for no CLUT and then 2 at least - - // Padding - if (!_cmsReadUInt8Number(io, NULL)) goto Error; - - // Do some checking - if (InputChannels == 0 || InputChannels > cmsMAXCHANNELS) goto Error; - if (OutputChannels == 0 || OutputChannels > cmsMAXCHANNELS) goto Error; - - // Allocates an empty Pipeline - NewLUT = cmsPipelineAlloc(self ->ContextID, InputChannels, OutputChannels); - if (NewLUT == NULL) goto Error; - - // Read the Matrix - if (!_cmsRead15Fixed16Number(io, &Matrix[0])) goto Error; - if (!_cmsRead15Fixed16Number(io, &Matrix[1])) goto Error; - if (!_cmsRead15Fixed16Number(io, &Matrix[2])) goto Error; - if (!_cmsRead15Fixed16Number(io, &Matrix[3])) goto Error; - if (!_cmsRead15Fixed16Number(io, &Matrix[4])) goto Error; - if (!_cmsRead15Fixed16Number(io, &Matrix[5])) goto Error; - if (!_cmsRead15Fixed16Number(io, &Matrix[6])) goto Error; - if (!_cmsRead15Fixed16Number(io, &Matrix[7])) goto Error; - if (!_cmsRead15Fixed16Number(io, &Matrix[8])) goto Error; - - - // Only operates if not identity... - if ((InputChannels == 3) && !_cmsMAT3isIdentity((cmsMAT3*) Matrix)) { - - if (!cmsPipelineInsertStage(NewLUT, cmsAT_BEGIN, cmsStageAllocMatrix(self ->ContextID, 3, 3, Matrix, NULL))) - goto Error; - } - - // Get input tables - if (!Read8bitTables(self ->ContextID, io, NewLUT, InputChannels)) goto Error; - - // Get 3D CLUT. Check the overflow.... - nTabSize = uipow(OutputChannels, CLUTpoints, InputChannels); - if (nTabSize == (cmsUInt32Number) -1) goto Error; - if (nTabSize > 0) { - - cmsUInt16Number *PtrW, *T; - - PtrW = T = (cmsUInt16Number*) _cmsCalloc(self ->ContextID, nTabSize, sizeof(cmsUInt16Number)); - if (T == NULL) goto Error; - - Temp = (cmsUInt8Number*) _cmsMalloc(self ->ContextID, nTabSize); - if (Temp == NULL) { - _cmsFree(self ->ContextID, T); - goto Error; - } - - if (io ->Read(io, Temp, nTabSize, 1) != 1) { - _cmsFree(self ->ContextID, T); - _cmsFree(self ->ContextID, Temp); - goto Error; - } - - for (i = 0; i < nTabSize; i++) { - - *PtrW++ = FROM_8_TO_16(Temp[i]); - } - _cmsFree(self ->ContextID, Temp); - Temp = NULL; - - if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, cmsStageAllocCLut16bit(self ->ContextID, CLUTpoints, InputChannels, OutputChannels, T))) - goto Error; - _cmsFree(self ->ContextID, T); - } - - - // Get output tables - if (!Read8bitTables(self ->ContextID, io, NewLUT, OutputChannels)) goto Error; - - *nItems = 1; - return NewLUT; - -Error: - if (NewLUT != NULL) cmsPipelineFree(NewLUT); - return NULL; - - cmsUNUSED_PARAMETER(SizeOfTag); -} - -// We only allow a specific MPE structure: Matrix plus prelin, plus clut, plus post-lin. -static -cmsBool Type_LUT8_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) -{ - cmsUInt32Number j, nTabSize; - cmsUInt8Number val; - cmsPipeline* NewLUT = (cmsPipeline*) Ptr; - cmsStage* mpe; - _cmsStageToneCurvesData* PreMPE = NULL, *PostMPE = NULL; - _cmsStageMatrixData* MatMPE = NULL; - _cmsStageCLutData* clut = NULL; - int clutPoints; - - // Disassemble the LUT into components. - mpe = NewLUT -> Elements; - if (mpe ->Type == cmsSigMatrixElemType) { - - MatMPE = (_cmsStageMatrixData*) mpe ->Data; - mpe = mpe -> Next; - } - - if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) { - PreMPE = (_cmsStageToneCurvesData*) mpe ->Data; - mpe = mpe -> Next; - } - - if (mpe != NULL && mpe ->Type == cmsSigCLutElemType) { - clut = (_cmsStageCLutData*) mpe -> Data; - mpe = mpe ->Next; - } - - if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) { - PostMPE = (_cmsStageToneCurvesData*) mpe ->Data; - mpe = mpe -> Next; - } - - // That should be all - if (mpe != NULL) { - cmsSignalError(mpe->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT is not suitable to be saved as LUT8"); - return FALSE; - } - - - if (clut == NULL) - clutPoints = 0; - else - clutPoints = clut->Params->nSamples[0]; - - if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) NewLUT ->InputChannels)) return FALSE; - if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) NewLUT ->OutputChannels)) return FALSE; - if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) clutPoints)) return FALSE; - if (!_cmsWriteUInt8Number(io, 0)) return FALSE; // Padding - - - if (MatMPE != NULL) { - - if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[0])) return FALSE; - if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[1])) return FALSE; - if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[2])) return FALSE; - if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[3])) return FALSE; - if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[4])) return FALSE; - if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[5])) return FALSE; - if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[6])) return FALSE; - if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[7])) return FALSE; - if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[8])) return FALSE; - - } - else { - - if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE; - if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; - if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; - if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; - if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE; - if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; - if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; - if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; - if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE; - } - - // The prelinearization table - if (!Write8bitTables(self ->ContextID, io, NewLUT ->InputChannels, PreMPE)) return FALSE; - - nTabSize = uipow(NewLUT->OutputChannels, clutPoints, NewLUT ->InputChannels); - if (nTabSize == (cmsUInt32Number) -1) return FALSE; - if (nTabSize > 0) { - - // The 3D CLUT. - if (clut != NULL) { - - for (j=0; j < nTabSize; j++) { - - val = (cmsUInt8Number) FROM_16_TO_8(clut ->Tab.T[j]); - if (!_cmsWriteUInt8Number(io, val)) return FALSE; - } - } - } - - // The postlinearization table - if (!Write8bitTables(self ->ContextID, io, NewLUT ->OutputChannels, PostMPE)) return FALSE; - - return TRUE; - - cmsUNUSED_PARAMETER(nItems); -} - - -static -void* Type_LUT8_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) -{ - return (void*) cmsPipelineDup((cmsPipeline*) Ptr); - - cmsUNUSED_PARAMETER(n); - cmsUNUSED_PARAMETER(self); -} - -static -void Type_LUT8_Free(struct _cms_typehandler_struct* self, void* Ptr) -{ - cmsPipelineFree((cmsPipeline*) Ptr); - return; - - cmsUNUSED_PARAMETER(self); -} - -// ******************************************************************************** -// Type cmsSigLut16Type -// ******************************************************************************** - -// Read 16 bit tables as gamma functions -static -cmsBool Read16bitTables(cmsContext ContextID, cmsIOHANDLER* io, cmsPipeline* lut, int nChannels, int nEntries) -{ - int i; - cmsToneCurve* Tables[cmsMAXCHANNELS]; - - // Maybe an empty table? (this is a lcms extension) - if (nEntries <= 0) return TRUE; - - // Check for malicious profiles - if (nEntries < 2) return FALSE; - if (nChannels > cmsMAXCHANNELS) return FALSE; - - // Init table to zero - memset(Tables, 0, sizeof(Tables)); - - for (i=0; i < nChannels; i++) { - - Tables[i] = cmsBuildTabulatedToneCurve16(ContextID, nEntries, NULL); - if (Tables[i] == NULL) goto Error; - - if (!_cmsReadUInt16Array(io, nEntries, Tables[i]->Table16)) goto Error; - } - - - // Add the table (which may certainly be an identity, but this is up to the optimizer, not the reading code) - if (!cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, nChannels, Tables))) - goto Error; - - for (i=0; i < nChannels; i++) - cmsFreeToneCurve(Tables[i]); - - return TRUE; - -Error: - for (i=0; i < nChannels; i++) { - if (Tables[i]) cmsFreeToneCurve(Tables[i]); - } - - return FALSE; -} - -static -cmsBool Write16bitTables(cmsContext ContextID, cmsIOHANDLER* io, _cmsStageToneCurvesData* Tables) -{ - int j; - cmsUInt32Number i; - cmsUInt16Number val; - int nEntries; - - _cmsAssert(Tables != NULL); - - nEntries = Tables->TheCurves[0]->nEntries; - - for (i=0; i < Tables ->nCurves; i++) { - - for (j=0; j < nEntries; j++) { - - val = Tables->TheCurves[i]->Table16[j]; - if (!_cmsWriteUInt16Number(io, val)) return FALSE; - } - } - return TRUE; - - cmsUNUSED_PARAMETER(ContextID); -} - -static -void *Type_LUT16_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) -{ - cmsUInt8Number InputChannels, OutputChannels, CLUTpoints; - cmsPipeline* NewLUT = NULL; - cmsUInt32Number nTabSize; - cmsFloat64Number Matrix[3*3]; - cmsUInt16Number InputEntries, OutputEntries; - - *nItems = 0; - - if (!_cmsReadUInt8Number(io, &InputChannels)) return NULL; - if (!_cmsReadUInt8Number(io, &OutputChannels)) return NULL; - if (!_cmsReadUInt8Number(io, &CLUTpoints)) return NULL; // 255 maximum - - // Padding - if (!_cmsReadUInt8Number(io, NULL)) return NULL; - - // Do some checking - if (InputChannels == 0 || InputChannels > cmsMAXCHANNELS) goto Error; - if (OutputChannels == 0 || OutputChannels > cmsMAXCHANNELS) goto Error; - - // Allocates an empty LUT - NewLUT = cmsPipelineAlloc(self ->ContextID, InputChannels, OutputChannels); - if (NewLUT == NULL) goto Error; - - // Read the Matrix - if (!_cmsRead15Fixed16Number(io, &Matrix[0])) goto Error; - if (!_cmsRead15Fixed16Number(io, &Matrix[1])) goto Error; - if (!_cmsRead15Fixed16Number(io, &Matrix[2])) goto Error; - if (!_cmsRead15Fixed16Number(io, &Matrix[3])) goto Error; - if (!_cmsRead15Fixed16Number(io, &Matrix[4])) goto Error; - if (!_cmsRead15Fixed16Number(io, &Matrix[5])) goto Error; - if (!_cmsRead15Fixed16Number(io, &Matrix[6])) goto Error; - if (!_cmsRead15Fixed16Number(io, &Matrix[7])) goto Error; - if (!_cmsRead15Fixed16Number(io, &Matrix[8])) goto Error; - - - // Only operates on 3 channels - if ((InputChannels == 3) && !_cmsMAT3isIdentity((cmsMAT3*) Matrix)) { - - if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, cmsStageAllocMatrix(self ->ContextID, 3, 3, Matrix, NULL))) - goto Error; - } - - if (!_cmsReadUInt16Number(io, &InputEntries)) goto Error; - if (!_cmsReadUInt16Number(io, &OutputEntries)) goto Error; - - if (InputEntries > 0x7FFF || OutputEntries > 0x7FFF) goto Error; - if (CLUTpoints == 1) goto Error; // Impossible value, 0 for no CLUT and then 2 at least - - // Get input tables - if (!Read16bitTables(self ->ContextID, io, NewLUT, InputChannels, InputEntries)) goto Error; - - // Get 3D CLUT - nTabSize = uipow(OutputChannels, CLUTpoints, InputChannels); - if (nTabSize == (cmsUInt32Number) -1) goto Error; - if (nTabSize > 0) { - - cmsUInt16Number *T; - - T = (cmsUInt16Number*) _cmsCalloc(self ->ContextID, nTabSize, sizeof(cmsUInt16Number)); - if (T == NULL) goto Error; - - if (!_cmsReadUInt16Array(io, nTabSize, T)) { - _cmsFree(self ->ContextID, T); - goto Error; - } - - if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, cmsStageAllocCLut16bit(self ->ContextID, CLUTpoints, InputChannels, OutputChannels, T))) { - _cmsFree(self ->ContextID, T); - goto Error; - } - _cmsFree(self ->ContextID, T); - } - - - // Get output tables - if (!Read16bitTables(self ->ContextID, io, NewLUT, OutputChannels, OutputEntries)) goto Error; - - *nItems = 1; - return NewLUT; - -Error: - if (NewLUT != NULL) cmsPipelineFree(NewLUT); - return NULL; - - cmsUNUSED_PARAMETER(SizeOfTag); -} - -// We only allow some specific MPE structures: Matrix plus prelin, plus clut, plus post-lin. -// Some empty defaults are created for missing parts - -static -cmsBool Type_LUT16_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) -{ - cmsUInt32Number nTabSize; - cmsPipeline* NewLUT = (cmsPipeline*) Ptr; - cmsStage* mpe; - _cmsStageToneCurvesData* PreMPE = NULL, *PostMPE = NULL; - _cmsStageMatrixData* MatMPE = NULL; - _cmsStageCLutData* clut = NULL; - int i, InputChannels, OutputChannels, clutPoints; - - // Disassemble the LUT into components. - mpe = NewLUT -> Elements; - if (mpe != NULL && mpe ->Type == cmsSigMatrixElemType) { - - MatMPE = (_cmsStageMatrixData*) mpe ->Data; - mpe = mpe -> Next; - } - - - if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) { - PreMPE = (_cmsStageToneCurvesData*) mpe ->Data; - mpe = mpe -> Next; - } - - if (mpe != NULL && mpe ->Type == cmsSigCLutElemType) { - clut = (_cmsStageCLutData*) mpe -> Data; - mpe = mpe ->Next; - } - - if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) { - PostMPE = (_cmsStageToneCurvesData*) mpe ->Data; - mpe = mpe -> Next; - } - - // That should be all - if (mpe != NULL) { - cmsSignalError(mpe->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT is not suitable to be saved as LUT16"); - return FALSE; - } - - InputChannels = cmsPipelineInputChannels(NewLUT); - OutputChannels = cmsPipelineOutputChannels(NewLUT); - - if (clut == NULL) - clutPoints = 0; - else - clutPoints = clut->Params->nSamples[0]; - - if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) InputChannels)) return FALSE; - if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) OutputChannels)) return FALSE; - if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) clutPoints)) return FALSE; - if (!_cmsWriteUInt8Number(io, 0)) return FALSE; // Padding - - - if (MatMPE != NULL) { - - if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[0])) return FALSE; - if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[1])) return FALSE; - if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[2])) return FALSE; - if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[3])) return FALSE; - if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[4])) return FALSE; - if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[5])) return FALSE; - if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[6])) return FALSE; - if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[7])) return FALSE; - if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[8])) return FALSE; - } - else { - - if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE; - if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; - if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; - if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; - if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE; - if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; - if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; - if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; - if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE; - } - - - if (PreMPE != NULL) { - if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) PreMPE ->TheCurves[0]->nEntries)) return FALSE; - } else { - if (!_cmsWriteUInt16Number(io, 2)) return FALSE; - } - - if (PostMPE != NULL) { - if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) PostMPE ->TheCurves[0]->nEntries)) return FALSE; - } else { - if (!_cmsWriteUInt16Number(io, 2)) return FALSE; - - } - - // The prelinearization table - - if (PreMPE != NULL) { - if (!Write16bitTables(self ->ContextID, io, PreMPE)) return FALSE; - } - else { - for (i=0; i < InputChannels; i++) { - - if (!_cmsWriteUInt16Number(io, 0)) return FALSE; - if (!_cmsWriteUInt16Number(io, 0xffff)) return FALSE; - } - } - - nTabSize = uipow(OutputChannels, clutPoints, InputChannels); - if (nTabSize == (cmsUInt32Number) -1) return FALSE; - if (nTabSize > 0) { - // The 3D CLUT. - if (clut != NULL) { - if (!_cmsWriteUInt16Array(io, nTabSize, clut->Tab.T)) return FALSE; - } - } - - // The postlinearization table - if (PostMPE != NULL) { - if (!Write16bitTables(self ->ContextID, io, PostMPE)) return FALSE; - } - else { - for (i=0; i < OutputChannels; i++) { - - if (!_cmsWriteUInt16Number(io, 0)) return FALSE; - if (!_cmsWriteUInt16Number(io, 0xffff)) return FALSE; - } - } - - return TRUE; - - cmsUNUSED_PARAMETER(nItems); -} - -static -void* Type_LUT16_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) -{ - return (void*) cmsPipelineDup((cmsPipeline*) Ptr); - - cmsUNUSED_PARAMETER(n); - cmsUNUSED_PARAMETER(self); -} - -static -void Type_LUT16_Free(struct _cms_typehandler_struct* self, void* Ptr) -{ - cmsPipelineFree((cmsPipeline*) Ptr); - return; - - cmsUNUSED_PARAMETER(self); -} - - -// ******************************************************************************** -// Type cmsSigLutAToBType -// ******************************************************************************** - - -// V4 stuff. Read matrix for LutAtoB and LutBtoA - -static -cmsStage* ReadMatrix(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number Offset) -{ - cmsFloat64Number dMat[3*3]; - cmsFloat64Number dOff[3]; - cmsStage* Mat; - - // Go to address - if (!io -> Seek(io, Offset)) return NULL; - - // Read the Matrix - if (!_cmsRead15Fixed16Number(io, &dMat[0])) return NULL; - if (!_cmsRead15Fixed16Number(io, &dMat[1])) return NULL; - if (!_cmsRead15Fixed16Number(io, &dMat[2])) return NULL; - if (!_cmsRead15Fixed16Number(io, &dMat[3])) return NULL; - if (!_cmsRead15Fixed16Number(io, &dMat[4])) return NULL; - if (!_cmsRead15Fixed16Number(io, &dMat[5])) return NULL; - if (!_cmsRead15Fixed16Number(io, &dMat[6])) return NULL; - if (!_cmsRead15Fixed16Number(io, &dMat[7])) return NULL; - if (!_cmsRead15Fixed16Number(io, &dMat[8])) return NULL; - - if (!_cmsRead15Fixed16Number(io, &dOff[0])) return NULL; - if (!_cmsRead15Fixed16Number(io, &dOff[1])) return NULL; - if (!_cmsRead15Fixed16Number(io, &dOff[2])) return NULL; - - Mat = cmsStageAllocMatrix(self ->ContextID, 3, 3, dMat, dOff); - - return Mat; -} - - - - -// V4 stuff. Read CLUT part for LutAtoB and LutBtoA - -static -cmsStage* ReadCLUT(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number Offset, int InputChannels, int OutputChannels) -{ - cmsUInt8Number gridPoints8[cmsMAXCHANNELS]; // Number of grid points in each dimension. - cmsUInt32Number GridPoints[cmsMAXCHANNELS], i; - cmsUInt8Number Precision; - cmsStage* CLUT; - _cmsStageCLutData* Data; - - if (!io -> Seek(io, Offset)) return NULL; - if (io -> Read(io, gridPoints8, cmsMAXCHANNELS, 1) != 1) return NULL; - - - for (i=0; i < cmsMAXCHANNELS; i++) { - - if (gridPoints8[i] == 1) return NULL; // Impossible value, 0 for no CLUT and then 2 at least - GridPoints[i] = gridPoints8[i]; - } - - if (!_cmsReadUInt8Number(io, &Precision)) return NULL; - - if (!_cmsReadUInt8Number(io, NULL)) return NULL; - if (!_cmsReadUInt8Number(io, NULL)) return NULL; - if (!_cmsReadUInt8Number(io, NULL)) return NULL; - - CLUT = cmsStageAllocCLut16bitGranular(self ->ContextID, GridPoints, InputChannels, OutputChannels, NULL); - if (CLUT == NULL) return NULL; - - Data = (_cmsStageCLutData*) CLUT ->Data; - - // Precision can be 1 or 2 bytes - if (Precision == 1) { - - cmsUInt8Number v; - - for (i=0; i < Data ->nEntries; i++) { - - if (io ->Read(io, &v, sizeof(cmsUInt8Number), 1) != 1) { - cmsStageFree(CLUT); - return NULL; - } - Data ->Tab.T[i] = FROM_8_TO_16(v); - } - - } - else - if (Precision == 2) { - - if (!_cmsReadUInt16Array(io, Data->nEntries, Data ->Tab.T)) { - cmsStageFree(CLUT); - return NULL; - } - } - else { - cmsStageFree(CLUT); - cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown precision of '%d'", Precision); - return NULL; - } - - return CLUT; -} - -static -cmsToneCurve* ReadEmbeddedCurve(struct _cms_typehandler_struct* self, cmsIOHANDLER* io) -{ - cmsTagTypeSignature BaseType; - cmsUInt32Number nItems; - - BaseType = _cmsReadTypeBase(io); - switch (BaseType) { - - case cmsSigCurveType: - return (cmsToneCurve*) Type_Curve_Read(self, io, &nItems, 0); - - case cmsSigParametricCurveType: - return (cmsToneCurve*) Type_ParametricCurve_Read(self, io, &nItems, 0); - - default: - { - char String[5]; - - _cmsTagSignature2String(String, (cmsTagSignature) BaseType); - cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown curve type '%s'", String); - } - return NULL; - } -} - - -// Read a set of curves from specific offset -static -cmsStage* ReadSetOfCurves(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number Offset, cmsUInt32Number nCurves) -{ - cmsToneCurve* Curves[cmsMAXCHANNELS]; - cmsUInt32Number i; - cmsStage* Lin = NULL; - - if (nCurves > cmsMAXCHANNELS) return FALSE; - - if (!io -> Seek(io, Offset)) return FALSE; - - for (i=0; i < nCurves; i++) - Curves[i] = NULL; - - for (i=0; i < nCurves; i++) { - - Curves[i] = ReadEmbeddedCurve(self, io); - if (Curves[i] == NULL) goto Error; - if (!_cmsReadAlignment(io)) goto Error; - - } - - Lin = cmsStageAllocToneCurves(self ->ContextID, nCurves, Curves); - -Error: - for (i=0; i < nCurves; i++) - cmsFreeToneCurve(Curves[i]); - - return Lin; -} - - -// LutAtoB type - -// This structure represents a colour transform. The type contains up to five processing -// elements which are stored in the AtoBTag tag in the following order: a set of one -// dimensional curves, a 3 by 3 matrix with offset terms, a set of one dimensional curves, -// a multidimensional lookup table, and a set of one dimensional output curves. -// Data are processed using these elements via the following sequence: -// -//("A" curves) -> (multidimensional lookup table - CLUT) -> ("M" curves) -> (matrix) -> ("B" curves). -// -/* -It is possible to use any or all of these processing elements. At least one processing element -must be included.Only the following combinations are allowed: - -B -M - Matrix - B -A - CLUT - B -A - CLUT - M - Matrix - B - -*/ - -static -void* Type_LUTA2B_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) -{ - cmsUInt32Number BaseOffset; - cmsUInt8Number inputChan; // Number of input channels - cmsUInt8Number outputChan; // Number of output channels - cmsUInt32Number offsetB; // Offset to first "B" curve - cmsUInt32Number offsetMat; // Offset to matrix - cmsUInt32Number offsetM; // Offset to first "M" curve - cmsUInt32Number offsetC; // Offset to CLUT - cmsUInt32Number offsetA; // Offset to first "A" curve - cmsPipeline* NewLUT = NULL; - - - BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); - - if (!_cmsReadUInt8Number(io, &inputChan)) return NULL; - if (!_cmsReadUInt8Number(io, &outputChan)) return NULL; - - if (!_cmsReadUInt16Number(io, NULL)) return NULL; - - if (!_cmsReadUInt32Number(io, &offsetB)) return NULL; - if (!_cmsReadUInt32Number(io, &offsetMat)) return NULL; - if (!_cmsReadUInt32Number(io, &offsetM)) return NULL; - if (!_cmsReadUInt32Number(io, &offsetC)) return NULL; - if (!_cmsReadUInt32Number(io, &offsetA)) return NULL; - - if (inputChan == 0 || inputChan >= cmsMAXCHANNELS) return NULL; - if (outputChan == 0 || outputChan >= cmsMAXCHANNELS) return NULL; - - // Allocates an empty LUT - NewLUT = cmsPipelineAlloc(self ->ContextID, inputChan, outputChan); - if (NewLUT == NULL) return NULL; - - if (offsetA!= 0) { - if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetA, inputChan))) - goto Error; - } - - if (offsetC != 0) { - if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadCLUT(self, io, BaseOffset + offsetC, inputChan, outputChan))) - goto Error; - } - - if (offsetM != 0) { - if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetM, outputChan))) - goto Error; - } - - if (offsetMat != 0) { - if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadMatrix(self, io, BaseOffset + offsetMat))) - goto Error; - } - - if (offsetB != 0) { - if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetB, outputChan))) - goto Error; - } - - *nItems = 1; - return NewLUT; -Error: - cmsPipelineFree(NewLUT); - return NULL; - - cmsUNUSED_PARAMETER(SizeOfTag); -} - -// Write a set of curves -static -cmsBool WriteMatrix(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsStage* mpe) -{ - _cmsStageMatrixData* m = (_cmsStageMatrixData*) mpe -> Data; - - // Write the Matrix - if (!_cmsWrite15Fixed16Number(io, m -> Double[0])) return FALSE; - if (!_cmsWrite15Fixed16Number(io, m -> Double[1])) return FALSE; - if (!_cmsWrite15Fixed16Number(io, m -> Double[2])) return FALSE; - if (!_cmsWrite15Fixed16Number(io, m -> Double[3])) return FALSE; - if (!_cmsWrite15Fixed16Number(io, m -> Double[4])) return FALSE; - if (!_cmsWrite15Fixed16Number(io, m -> Double[5])) return FALSE; - if (!_cmsWrite15Fixed16Number(io, m -> Double[6])) return FALSE; - if (!_cmsWrite15Fixed16Number(io, m -> Double[7])) return FALSE; - if (!_cmsWrite15Fixed16Number(io, m -> Double[8])) return FALSE; - - if (m ->Offset != NULL) { - - if (!_cmsWrite15Fixed16Number(io, m -> Offset[0])) return FALSE; - if (!_cmsWrite15Fixed16Number(io, m -> Offset[1])) return FALSE; - if (!_cmsWrite15Fixed16Number(io, m -> Offset[2])) return FALSE; - } - else { - if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; - if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; - if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; - - } - - - return TRUE; - - cmsUNUSED_PARAMETER(self); -} - - -// Write a set of curves -static -cmsBool WriteSetOfCurves(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsTagTypeSignature Type, cmsStage* mpe) -{ - cmsUInt32Number i, n; - cmsTagTypeSignature CurrentType; - cmsToneCurve** Curves; - - - n = cmsStageOutputChannels(mpe); - Curves = _cmsStageGetPtrToCurveSet(mpe); - - for (i=0; i < n; i++) { - - // If this is a table-based curve, use curve type even on V4 - CurrentType = Type; - - if ((Curves[i] ->nSegments == 0)|| - ((Curves[i]->nSegments == 2) && (Curves[i] ->Segments[1].Type == 0)) ) - CurrentType = cmsSigCurveType; - else - if (Curves[i] ->Segments[0].Type < 0) - CurrentType = cmsSigCurveType; - - if (!_cmsWriteTypeBase(io, CurrentType)) return FALSE; - - switch (CurrentType) { - - case cmsSigCurveType: - if (!Type_Curve_Write(self, io, Curves[i], 1)) return FALSE; - break; - - case cmsSigParametricCurveType: - if (!Type_ParametricCurve_Write(self, io, Curves[i], 1)) return FALSE; - break; - - default: - { - char String[5]; - - _cmsTagSignature2String(String, (cmsTagSignature) Type); - cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown curve type '%s'", String); - } - return FALSE; - } - - if (!_cmsWriteAlignment(io)) return FALSE; - } - - - return TRUE; -} - - -static -cmsBool WriteCLUT(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt8Number Precision, cmsStage* mpe) -{ - cmsUInt8Number gridPoints[cmsMAXCHANNELS]; // Number of grid points in each dimension. - cmsUInt32Number i; - _cmsStageCLutData* CLUT = ( _cmsStageCLutData*) mpe -> Data; - - if (CLUT ->HasFloatValues) { - cmsSignalError(self ->ContextID, cmsERROR_NOT_SUITABLE, "Cannot save floating point data, CLUT are 8 or 16 bit only"); - return FALSE; - } - - memset(gridPoints, 0, sizeof(gridPoints)); - for (i=0; i < (cmsUInt32Number) CLUT ->Params ->nInputs; i++) - gridPoints[i] = (cmsUInt8Number) CLUT ->Params ->nSamples[i]; - - if (!io -> Write(io, cmsMAXCHANNELS*sizeof(cmsUInt8Number), gridPoints)) return FALSE; - - if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) Precision)) return FALSE; - if (!_cmsWriteUInt8Number(io, 0)) return FALSE; - if (!_cmsWriteUInt8Number(io, 0)) return FALSE; - if (!_cmsWriteUInt8Number(io, 0)) return FALSE; - - // Precision can be 1 or 2 bytes - if (Precision == 1) { - - for (i=0; i < CLUT->nEntries; i++) { - - if (!_cmsWriteUInt8Number(io, FROM_16_TO_8(CLUT->Tab.T[i]))) return FALSE; - } - } - else - if (Precision == 2) { - - if (!_cmsWriteUInt16Array(io, CLUT->nEntries, CLUT ->Tab.T)) return FALSE; - } - else { - cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown precision of '%d'", Precision); - return FALSE; - } - - if (!_cmsWriteAlignment(io)) return FALSE; - - return TRUE; -} - - - - -static -cmsBool Type_LUTA2B_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) -{ - cmsPipeline* Lut = (cmsPipeline*) Ptr; - int inputChan, outputChan; - cmsStage *A = NULL, *B = NULL, *M = NULL; - cmsStage * Matrix = NULL; - cmsStage * CLUT = NULL; - cmsUInt32Number offsetB = 0, offsetMat = 0, offsetM = 0, offsetC = 0, offsetA = 0; - cmsUInt32Number BaseOffset, DirectoryPos, CurrentPos; - - // Get the base for all offsets - BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); - - if (Lut ->Elements != NULL) - if (!cmsPipelineCheckAndRetreiveStages(Lut, 1, cmsSigCurveSetElemType, &B)) - if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, &M, &Matrix, &B)) - if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, &A, &CLUT, &B)) - if (!cmsPipelineCheckAndRetreiveStages(Lut, 5, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, - cmsSigMatrixElemType, cmsSigCurveSetElemType, &A, &CLUT, &M, &Matrix, &B)) { - - cmsSignalError(self->ContextID, cmsERROR_NOT_SUITABLE, "LUT is not suitable to be saved as LutAToB"); - return FALSE; - } - - // Get input, output channels - inputChan = cmsPipelineInputChannels(Lut); - outputChan = cmsPipelineOutputChannels(Lut); - - // Write channel count - if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) inputChan)) return FALSE; - if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) outputChan)) return FALSE; - if (!_cmsWriteUInt16Number(io, 0)) return FALSE; - - // Keep directory to be filled latter - DirectoryPos = io ->Tell(io); - - // Write the directory - if (!_cmsWriteUInt32Number(io, 0)) return FALSE; - if (!_cmsWriteUInt32Number(io, 0)) return FALSE; - if (!_cmsWriteUInt32Number(io, 0)) return FALSE; - if (!_cmsWriteUInt32Number(io, 0)) return FALSE; - if (!_cmsWriteUInt32Number(io, 0)) return FALSE; - - if (A != NULL) { - - offsetA = io ->Tell(io) - BaseOffset; - if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, A)) return FALSE; - } - - if (CLUT != NULL) { - offsetC = io ->Tell(io) - BaseOffset; - if (!WriteCLUT(self, io, Lut ->SaveAs8Bits ? 1 : 2, CLUT)) return FALSE; - - } - if (M != NULL) { - - offsetM = io ->Tell(io) - BaseOffset; - if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, M)) return FALSE; - } - - if (Matrix != NULL) { - offsetMat = io ->Tell(io) - BaseOffset; - if (!WriteMatrix(self, io, Matrix)) return FALSE; - } - - if (B != NULL) { - - offsetB = io ->Tell(io) - BaseOffset; - if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, B)) return FALSE; - } - - CurrentPos = io ->Tell(io); - - if (!io ->Seek(io, DirectoryPos)) return FALSE; - - if (!_cmsWriteUInt32Number(io, offsetB)) return FALSE; - if (!_cmsWriteUInt32Number(io, offsetMat)) return FALSE; - if (!_cmsWriteUInt32Number(io, offsetM)) return FALSE; - if (!_cmsWriteUInt32Number(io, offsetC)) return FALSE; - if (!_cmsWriteUInt32Number(io, offsetA)) return FALSE; - - if (!io ->Seek(io, CurrentPos)) return FALSE; - - return TRUE; - - cmsUNUSED_PARAMETER(nItems); -} - - -static -void* Type_LUTA2B_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) -{ - return (void*) cmsPipelineDup((cmsPipeline*) Ptr); - - cmsUNUSED_PARAMETER(n); - cmsUNUSED_PARAMETER(self); -} - -static -void Type_LUTA2B_Free(struct _cms_typehandler_struct* self, void* Ptr) -{ - cmsPipelineFree((cmsPipeline*) Ptr); - return; - - cmsUNUSED_PARAMETER(self); -} - - -// LutBToA type - -static -void* Type_LUTB2A_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) -{ - cmsUInt8Number inputChan; // Number of input channels - cmsUInt8Number outputChan; // Number of output channels - cmsUInt32Number BaseOffset; // Actual position in file - cmsUInt32Number offsetB; // Offset to first "B" curve - cmsUInt32Number offsetMat; // Offset to matrix - cmsUInt32Number offsetM; // Offset to first "M" curve - cmsUInt32Number offsetC; // Offset to CLUT - cmsUInt32Number offsetA; // Offset to first "A" curve - cmsPipeline* NewLUT = NULL; - - - BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); - - if (!_cmsReadUInt8Number(io, &inputChan)) return NULL; - if (!_cmsReadUInt8Number(io, &outputChan)) return NULL; - - if (inputChan == 0 || inputChan >= cmsMAXCHANNELS) return NULL; - if (outputChan == 0 || outputChan >= cmsMAXCHANNELS) return NULL; - - // Padding - if (!_cmsReadUInt16Number(io, NULL)) return NULL; - - if (!_cmsReadUInt32Number(io, &offsetB)) return NULL; - if (!_cmsReadUInt32Number(io, &offsetMat)) return NULL; - if (!_cmsReadUInt32Number(io, &offsetM)) return NULL; - if (!_cmsReadUInt32Number(io, &offsetC)) return NULL; - if (!_cmsReadUInt32Number(io, &offsetA)) return NULL; - - // Allocates an empty LUT - NewLUT = cmsPipelineAlloc(self ->ContextID, inputChan, outputChan); - if (NewLUT == NULL) return NULL; - - if (offsetB != 0) { - if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetB, inputChan))) - goto Error; - } - - if (offsetMat != 0) { - if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadMatrix(self, io, BaseOffset + offsetMat))) - goto Error; - } - - if (offsetM != 0) { - if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetM, inputChan))) - goto Error; - } - - if (offsetC != 0) { - if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadCLUT(self, io, BaseOffset + offsetC, inputChan, outputChan))) - goto Error; - } - - if (offsetA!= 0) { - if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetA, outputChan))) - goto Error; - } - - *nItems = 1; - return NewLUT; -Error: - cmsPipelineFree(NewLUT); - return NULL; - - cmsUNUSED_PARAMETER(SizeOfTag); -} - - -/* -B -B - Matrix - M -B - CLUT - A -B - Matrix - M - CLUT - A -*/ - -static -cmsBool Type_LUTB2A_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) -{ - cmsPipeline* Lut = (cmsPipeline*) Ptr; - int inputChan, outputChan; - cmsStage *A = NULL, *B = NULL, *M = NULL; - cmsStage *Matrix = NULL; - cmsStage *CLUT = NULL; - cmsUInt32Number offsetB = 0, offsetMat = 0, offsetM = 0, offsetC = 0, offsetA = 0; - cmsUInt32Number BaseOffset, DirectoryPos, CurrentPos; - - - BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); - - if (!cmsPipelineCheckAndRetreiveStages(Lut, 1, cmsSigCurveSetElemType, &B)) - if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, &B, &Matrix, &M)) - if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, &B, &CLUT, &A)) - if (!cmsPipelineCheckAndRetreiveStages(Lut, 5, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, - cmsSigCLutElemType, cmsSigCurveSetElemType, &B, &Matrix, &M, &CLUT, &A)) { - cmsSignalError(self->ContextID, cmsERROR_NOT_SUITABLE, "LUT is not suitable to be saved as LutBToA"); - return FALSE; - } - - inputChan = cmsPipelineInputChannels(Lut); - outputChan = cmsPipelineOutputChannels(Lut); - - if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) inputChan)) return FALSE; - if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) outputChan)) return FALSE; - if (!_cmsWriteUInt16Number(io, 0)) return FALSE; - - DirectoryPos = io ->Tell(io); - - if (!_cmsWriteUInt32Number(io, 0)) return FALSE; - if (!_cmsWriteUInt32Number(io, 0)) return FALSE; - if (!_cmsWriteUInt32Number(io, 0)) return FALSE; - if (!_cmsWriteUInt32Number(io, 0)) return FALSE; - if (!_cmsWriteUInt32Number(io, 0)) return FALSE; - - if (A != NULL) { - - offsetA = io ->Tell(io) - BaseOffset; - if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, A)) return FALSE; - } - - if (CLUT != NULL) { - offsetC = io ->Tell(io) - BaseOffset; - if (!WriteCLUT(self, io, Lut ->SaveAs8Bits ? 1 : 2, CLUT)) return FALSE; - - } - if (M != NULL) { - - offsetM = io ->Tell(io) - BaseOffset; - if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, M)) return FALSE; - } - - if (Matrix != NULL) { - offsetMat = io ->Tell(io) - BaseOffset; - if (!WriteMatrix(self, io, Matrix)) return FALSE; - } - - if (B != NULL) { - - offsetB = io ->Tell(io) - BaseOffset; - if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, B)) return FALSE; - } - - CurrentPos = io ->Tell(io); - - if (!io ->Seek(io, DirectoryPos)) return FALSE; - - if (!_cmsWriteUInt32Number(io, offsetB)) return FALSE; - if (!_cmsWriteUInt32Number(io, offsetMat)) return FALSE; - if (!_cmsWriteUInt32Number(io, offsetM)) return FALSE; - if (!_cmsWriteUInt32Number(io, offsetC)) return FALSE; - if (!_cmsWriteUInt32Number(io, offsetA)) return FALSE; - - if (!io ->Seek(io, CurrentPos)) return FALSE; - - return TRUE; - - cmsUNUSED_PARAMETER(nItems); -} - - - -static -void* Type_LUTB2A_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) -{ - return (void*) cmsPipelineDup((cmsPipeline*) Ptr); - - cmsUNUSED_PARAMETER(n); - cmsUNUSED_PARAMETER(self); -} - -static -void Type_LUTB2A_Free(struct _cms_typehandler_struct* self, void* Ptr) -{ - cmsPipelineFree((cmsPipeline*) Ptr); - return; - - cmsUNUSED_PARAMETER(self); -} - - - -// ******************************************************************************** -// Type cmsSigColorantTableType -// ******************************************************************************** -/* -The purpose of this tag is to identify the colorants used in the profile by a -unique name and set of XYZ or L*a*b* values to give the colorant an unambiguous -value. The first colorant listed is the colorant of the first device channel of -a lut tag. The second colorant listed is the colorant of the second device channel -of a lut tag, and so on. -*/ - -static -void *Type_ColorantTable_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) -{ - cmsUInt32Number i, Count; - cmsNAMEDCOLORLIST* List; - char Name[33]; - cmsUInt16Number PCS[3]; - - - if (!_cmsReadUInt32Number(io, &Count)) return NULL; - - if (Count > cmsMAXCHANNELS) { - cmsSignalError(self->ContextID, cmsERROR_RANGE, "Too many colorants '%d'", Count); - return NULL; - } - - List = cmsAllocNamedColorList(self ->ContextID, Count, 0, "", ""); - for (i=0; i < Count; i++) { - - if (io ->Read(io, Name, 32, 1) != 1) goto Error; - Name[32] = 0; - - if (!_cmsReadUInt16Array(io, 3, PCS)) goto Error; - - if (!cmsAppendNamedColor(List, Name, PCS, NULL)) goto Error; - - } - - *nItems = 1; - return List; - -Error: - *nItems = 0; - cmsFreeNamedColorList(List); - return NULL; - - cmsUNUSED_PARAMETER(SizeOfTag); -} - - - -// Saves a colorant table. It is using the named color structure for simplicity sake -static -cmsBool Type_ColorantTable_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) -{ - cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) Ptr; - int i, nColors; - - nColors = cmsNamedColorCount(NamedColorList); - - if (!_cmsWriteUInt32Number(io, nColors)) return FALSE; - - for (i=0; i < nColors; i++) { - - char root[33]; - cmsUInt16Number PCS[3]; - - if (!cmsNamedColorInfo(NamedColorList, i, root, NULL, NULL, PCS, NULL)) return 0; - root[32] = 0; - - if (!io ->Write(io, 32, root)) return FALSE; - if (!_cmsWriteUInt16Array(io, 3, PCS)) return FALSE; - } - - return TRUE; - - cmsUNUSED_PARAMETER(nItems); - cmsUNUSED_PARAMETER(self); -} - - -static -void* Type_ColorantTable_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n) -{ - cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) Ptr; - return (void*) cmsDupNamedColorList(nc); - - cmsUNUSED_PARAMETER(n); - cmsUNUSED_PARAMETER(self); -} - - -static -void Type_ColorantTable_Free(struct _cms_typehandler_struct* self, void* Ptr) -{ - cmsFreeNamedColorList((cmsNAMEDCOLORLIST*) Ptr); - return; - - cmsUNUSED_PARAMETER(self); -} - - -// ******************************************************************************** -// Type cmsSigNamedColor2Type -// ******************************************************************************** -// -//The namedColor2Type is a count value and array of structures that provide color -//coordinates for 7-bit ASCII color names. For each named color, a PCS and optional -//device representation of the color are given. Both representations are 16-bit values. -//The device representation corresponds to the header's 'color space of data' field. -//This representation should be consistent with the 'number of device components' -//field in the namedColor2Type. If this field is 0, device coordinates are not provided. -//The PCS representation corresponds to the header's PCS field. The PCS representation -//is always provided. Color names are fixed-length, 32-byte fields including null -//termination. In order to maintain maximum portability, it is strongly recommended -//that special characters of the 7-bit ASCII set not be used. - -static -void *Type_NamedColor_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) -{ - - cmsUInt32Number vendorFlag; // Bottom 16 bits for ICC use - cmsUInt32Number count; // Count of named colors - cmsUInt32Number nDeviceCoords; // Num of device coordinates - char prefix[32]; // Prefix for each color name - char suffix[32]; // Suffix for each color name - cmsNAMEDCOLORLIST* v; - cmsUInt32Number i; - - - *nItems = 0; - if (!_cmsReadUInt32Number(io, &vendorFlag)) return NULL; - if (!_cmsReadUInt32Number(io, &count)) return NULL; - if (!_cmsReadUInt32Number(io, &nDeviceCoords)) return NULL; - - if (io -> Read(io, prefix, 32, 1) != 1) return NULL; - if (io -> Read(io, suffix, 32, 1) != 1) return NULL; - - prefix[31] = suffix[31] = 0; - - v = cmsAllocNamedColorList(self ->ContextID, count, nDeviceCoords, prefix, suffix); - if (v == NULL) { - cmsSignalError(self->ContextID, cmsERROR_RANGE, "Too many named colors '%d'", count); - return NULL; - } - - if (nDeviceCoords > cmsMAXCHANNELS) { - cmsSignalError(self->ContextID, cmsERROR_RANGE, "Too many device coordinates '%d'", nDeviceCoords); - goto Error; - } - for (i=0; i < count; i++) { - - cmsUInt16Number PCS[3]; - cmsUInt16Number Colorant[cmsMAXCHANNELS]; - char Root[33]; - - memset(Colorant, 0, sizeof(Colorant)); - if (io -> Read(io, Root, 32, 1) != 1) goto Error; - Root[32] = 0; - if (!_cmsReadUInt16Array(io, 3, PCS)) goto Error; - if (!_cmsReadUInt16Array(io, nDeviceCoords, Colorant)) goto Error; - - if (!cmsAppendNamedColor(v, Root, PCS, Colorant)) goto Error; - } - - *nItems = 1; - return (void*) v ; - -Error: - cmsFreeNamedColorList(v); - return NULL; - - cmsUNUSED_PARAMETER(SizeOfTag); -} - - -// Saves a named color list into a named color profile -static -cmsBool Type_NamedColor_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) -{ - cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) Ptr; - char prefix[32]; // Prefix for each color name - char suffix[32]; // Suffix for each color name - int i, nColors; - - nColors = cmsNamedColorCount(NamedColorList); - - if (!_cmsWriteUInt32Number(io, 0)) return FALSE; - if (!_cmsWriteUInt32Number(io, nColors)) return FALSE; - if (!_cmsWriteUInt32Number(io, NamedColorList ->ColorantCount)) return FALSE; - - strncpy(prefix, (const char*) NamedColorList->Prefix, 32); - strncpy(suffix, (const char*) NamedColorList->Suffix, 32); - - suffix[31] = prefix[31] = 0; - - if (!io ->Write(io, 32, prefix)) return FALSE; - if (!io ->Write(io, 32, suffix)) return FALSE; - - for (i=0; i < nColors; i++) { - - cmsUInt16Number PCS[3]; - cmsUInt16Number Colorant[cmsMAXCHANNELS]; - char Root[33]; - - if (!cmsNamedColorInfo(NamedColorList, i, Root, NULL, NULL, PCS, Colorant)) return 0; - if (!io ->Write(io, 32 , Root)) return FALSE; - if (!_cmsWriteUInt16Array(io, 3, PCS)) return FALSE; - if (!_cmsWriteUInt16Array(io, NamedColorList ->ColorantCount, Colorant)) return FALSE; - } - - return TRUE; - - cmsUNUSED_PARAMETER(nItems); - cmsUNUSED_PARAMETER(self); -} - -static -void* Type_NamedColor_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n) -{ - cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) Ptr; - - return (void*) cmsDupNamedColorList(nc); - - cmsUNUSED_PARAMETER(n); - cmsUNUSED_PARAMETER(self); -} - - -static -void Type_NamedColor_Free(struct _cms_typehandler_struct* self, void* Ptr) -{ - cmsFreeNamedColorList((cmsNAMEDCOLORLIST*) Ptr); - return; - - cmsUNUSED_PARAMETER(self); -} - - -// ******************************************************************************** -// Type cmsSigProfileSequenceDescType -// ******************************************************************************** - -// This type is an array of structures, each of which contains information from the -// header fields and tags from the original profiles which were combined to create -// the final profile. The order of the structures is the order in which the profiles -// were combined and includes a structure for the final profile. This provides a -// description of the profile sequence from source to destination, -// typically used with the DeviceLink profile. - -static -cmsBool ReadEmbeddedText(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU** mlu, cmsUInt32Number SizeOfTag) -{ - cmsTagTypeSignature BaseType; - cmsUInt32Number nItems; - - BaseType = _cmsReadTypeBase(io); - - switch (BaseType) { - - case cmsSigTextType: - if (*mlu) cmsMLUfree(*mlu); - *mlu = (cmsMLU*)Type_Text_Read(self, io, &nItems, SizeOfTag); - return (*mlu != NULL); - - case cmsSigTextDescriptionType: - if (*mlu) cmsMLUfree(*mlu); - *mlu = (cmsMLU*) Type_Text_Description_Read(self, io, &nItems, SizeOfTag); - return (*mlu != NULL); - - /* - TBD: Size is needed for MLU, and we have no idea on which is the available size - */ - - case cmsSigMultiLocalizedUnicodeType: - if (*mlu) cmsMLUfree(*mlu); - *mlu = (cmsMLU*) Type_MLU_Read(self, io, &nItems, SizeOfTag); - return (*mlu != NULL); - - default: return FALSE; - } -} - - -static -void *Type_ProfileSequenceDesc_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) -{ - cmsSEQ* OutSeq; - cmsUInt32Number i, Count; - - *nItems = 0; - - if (!_cmsReadUInt32Number(io, &Count)) return NULL; - - if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL; - SizeOfTag -= sizeof(cmsUInt32Number); - - - OutSeq = cmsAllocProfileSequenceDescription(self ->ContextID, Count); - if (OutSeq == NULL) return NULL; - - OutSeq ->n = Count; - - // Get structures as well - - for (i=0; i < Count; i++) { - - cmsPSEQDESC* sec = &OutSeq -> seq[i]; - - if (!_cmsReadUInt32Number(io, &sec ->deviceMfg)) goto Error; - if (SizeOfTag < sizeof(cmsUInt32Number)) goto Error; - SizeOfTag -= sizeof(cmsUInt32Number); - - if (!_cmsReadUInt32Number(io, &sec ->deviceModel)) goto Error; - if (SizeOfTag < sizeof(cmsUInt32Number)) goto Error; - SizeOfTag -= sizeof(cmsUInt32Number); - - if (!_cmsReadUInt64Number(io, &sec ->attributes)) goto Error; - if (SizeOfTag < sizeof(cmsUInt64Number)) goto Error; - SizeOfTag -= sizeof(cmsUInt64Number); - - if (!_cmsReadUInt32Number(io, (cmsUInt32Number *)&sec ->technology)) goto Error; - if (SizeOfTag < sizeof(cmsUInt32Number)) goto Error; - SizeOfTag -= sizeof(cmsUInt32Number); - - if (!ReadEmbeddedText(self, io, &sec ->Manufacturer, SizeOfTag)) goto Error; - if (!ReadEmbeddedText(self, io, &sec ->Model, SizeOfTag)) goto Error; - } - - *nItems = 1; - return OutSeq; - -Error: - cmsFreeProfileSequenceDescription(OutSeq); - return NULL; -} - - -// Aux--Embed a text description type. It can be of type text description or multilocalized unicode -// and it depends of the version number passed on cmsTagDescriptor structure instead of stack -static -cmsBool SaveDescription(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* Text) -{ - if (self ->ICCVersion < 0x4000000) { - - if (!_cmsWriteTypeBase(io, cmsSigTextDescriptionType)) return FALSE; - return Type_Text_Description_Write(self, io, Text, 1); - } - else { - if (!_cmsWriteTypeBase(io, cmsSigMultiLocalizedUnicodeType)) return FALSE; - return Type_MLU_Write(self, io, Text, 1); - } -} - - -static -cmsBool Type_ProfileSequenceDesc_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) -{ - cmsSEQ* Seq = (cmsSEQ*) Ptr; - cmsUInt32Number i; - - if (!_cmsWriteUInt32Number(io, Seq->n)) return FALSE; - - for (i=0; i < Seq ->n; i++) { - - cmsPSEQDESC* sec = &Seq -> seq[i]; - - if (!_cmsWriteUInt32Number(io, sec ->deviceMfg)) return FALSE; - if (!_cmsWriteUInt32Number(io, sec ->deviceModel)) return FALSE; - if (!_cmsWriteUInt64Number(io, &sec ->attributes)) return FALSE; - if (!_cmsWriteUInt32Number(io, sec ->technology)) return FALSE; - - if (!SaveDescription(self, io, sec ->Manufacturer)) return FALSE; - if (!SaveDescription(self, io, sec ->Model)) return FALSE; - } - - return TRUE; - - cmsUNUSED_PARAMETER(nItems); -} - - -static -void* Type_ProfileSequenceDesc_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n) -{ - return (void*) cmsDupProfileSequenceDescription((cmsSEQ*) Ptr); - - cmsUNUSED_PARAMETER(n); - cmsUNUSED_PARAMETER(self); -} - -static -void Type_ProfileSequenceDesc_Free(struct _cms_typehandler_struct* self, void* Ptr) -{ - cmsFreeProfileSequenceDescription((cmsSEQ*) Ptr); - return; - - cmsUNUSED_PARAMETER(self); -} - - -// ******************************************************************************** -// Type cmsSigProfileSequenceIdType -// ******************************************************************************** -/* -In certain workflows using ICC Device Link Profiles, it is necessary to identify the -original profiles that were combined to create the Device Link Profile. -This type is an array of structures, each of which contains information for -identification of a profile used in a sequence -*/ - - -static -cmsBool ReadSeqID(struct _cms_typehandler_struct* self, - cmsIOHANDLER* io, - void* Cargo, - cmsUInt32Number n, - cmsUInt32Number SizeOfTag) -{ - cmsSEQ* OutSeq = (cmsSEQ*) Cargo; - cmsPSEQDESC* seq = &OutSeq ->seq[n]; - - if (io -> Read(io, seq ->ProfileID.ID8, 16, 1) != 1) return FALSE; - if (!ReadEmbeddedText(self, io, &seq ->Description, SizeOfTag)) return FALSE; - - return TRUE; -} - - - -static -void *Type_ProfileSequenceId_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) -{ - cmsSEQ* OutSeq; - cmsUInt32Number Count; - cmsUInt32Number BaseOffset; - - *nItems = 0; - - // Get actual position as a basis for element offsets - BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); - - // Get table count - if (!_cmsReadUInt32Number(io, &Count)) return NULL; - SizeOfTag -= sizeof(cmsUInt32Number); - - // Allocate an empty structure - OutSeq = cmsAllocProfileSequenceDescription(self ->ContextID, Count); - if (OutSeq == NULL) return NULL; - - - // Read the position table - if (!ReadPositionTable(self, io, Count, BaseOffset, OutSeq, ReadSeqID)) { - - cmsFreeProfileSequenceDescription(OutSeq); - return NULL; - } - - // Success - *nItems = 1; - return OutSeq; - -} - - -static -cmsBool WriteSeqID(struct _cms_typehandler_struct* self, - cmsIOHANDLER* io, - void* Cargo, - cmsUInt32Number n, - cmsUInt32Number SizeOfTag) -{ - cmsSEQ* Seq = (cmsSEQ*) Cargo; - - if (!io ->Write(io, 16, Seq ->seq[n].ProfileID.ID8)) return FALSE; - - // Store here the MLU - if (!SaveDescription(self, io, Seq ->seq[n].Description)) return FALSE; - - return TRUE; - - cmsUNUSED_PARAMETER(SizeOfTag); -} - -static -cmsBool Type_ProfileSequenceId_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) -{ - cmsSEQ* Seq = (cmsSEQ*) Ptr; - cmsUInt32Number BaseOffset; - - // Keep the base offset - BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); - - // This is the table count - if (!_cmsWriteUInt32Number(io, Seq ->n)) return FALSE; - - // This is the position table and content - if (!WritePositionTable(self, io, 0, Seq ->n, BaseOffset, Seq, WriteSeqID)) return FALSE; - - return TRUE; - - cmsUNUSED_PARAMETER(nItems); -} - -static -void* Type_ProfileSequenceId_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n) -{ - return (void*) cmsDupProfileSequenceDescription((cmsSEQ*) Ptr); - - cmsUNUSED_PARAMETER(n); - cmsUNUSED_PARAMETER(self); -} - -static -void Type_ProfileSequenceId_Free(struct _cms_typehandler_struct* self, void* Ptr) -{ - cmsFreeProfileSequenceDescription((cmsSEQ*) Ptr); - return; - - cmsUNUSED_PARAMETER(self); -} - - -// ******************************************************************************** -// Type cmsSigUcrBgType -// ******************************************************************************** -/* -This type contains curves representing the under color removal and black -generation and a text string which is a general description of the method used -for the ucr/bg. -*/ - -static -void *Type_UcrBg_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) -{ - cmsUcrBg* n = (cmsUcrBg*) _cmsMallocZero(self ->ContextID, sizeof(cmsUcrBg)); - cmsUInt32Number CountUcr, CountBg; - char* ASCIIString; - - *nItems = 0; - if (n == NULL) return NULL; - - // First curve is Under color removal - if (!_cmsReadUInt32Number(io, &CountUcr)) return NULL; - if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL; - SizeOfTag -= sizeof(cmsUInt32Number); - - n ->Ucr = cmsBuildTabulatedToneCurve16(self ->ContextID, CountUcr, NULL); - if (n ->Ucr == NULL) return NULL; - - if (!_cmsReadUInt16Array(io, CountUcr, n ->Ucr->Table16)) return NULL; - if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL; - SizeOfTag -= CountUcr * sizeof(cmsUInt16Number); - - // Second curve is Black generation - if (!_cmsReadUInt32Number(io, &CountBg)) return NULL; - if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL; - SizeOfTag -= sizeof(cmsUInt32Number); - - n ->Bg = cmsBuildTabulatedToneCurve16(self ->ContextID, CountBg, NULL); - if (n ->Bg == NULL) return NULL; - if (!_cmsReadUInt16Array(io, CountBg, n ->Bg->Table16)) return NULL; - if (SizeOfTag < CountBg * sizeof(cmsUInt16Number)) return NULL; - SizeOfTag -= CountBg * sizeof(cmsUInt16Number); - if (SizeOfTag == UINT_MAX) return NULL; - - // Now comes the text. The length is specified by the tag size - n ->Desc = cmsMLUalloc(self ->ContextID, 1); - if (n ->Desc == NULL) return NULL; - - ASCIIString = (char*) _cmsMalloc(self ->ContextID, SizeOfTag + 1); - if (io ->Read(io, ASCIIString, sizeof(char), SizeOfTag) != SizeOfTag) return NULL; - ASCIIString[SizeOfTag] = 0; - cmsMLUsetASCII(n ->Desc, cmsNoLanguage, cmsNoCountry, ASCIIString); - _cmsFree(self ->ContextID, ASCIIString); - - *nItems = 1; - return (void*) n; -} - -static -cmsBool Type_UcrBg_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) -{ - cmsUcrBg* Value = (cmsUcrBg*) Ptr; - cmsUInt32Number TextSize; - char* Text; - - // First curve is Under color removal - if (!_cmsWriteUInt32Number(io, Value ->Ucr ->nEntries)) return FALSE; - if (!_cmsWriteUInt16Array(io, Value ->Ucr ->nEntries, Value ->Ucr ->Table16)) return FALSE; - - // Then black generation - if (!_cmsWriteUInt32Number(io, Value ->Bg ->nEntries)) return FALSE; - if (!_cmsWriteUInt16Array(io, Value ->Bg ->nEntries, Value ->Bg ->Table16)) return FALSE; - - // Now comes the text. The length is specified by the tag size - TextSize = cmsMLUgetASCII(Value ->Desc, cmsNoLanguage, cmsNoCountry, NULL, 0); - Text = (char*) _cmsMalloc(self ->ContextID, TextSize); - if (cmsMLUgetASCII(Value ->Desc, cmsNoLanguage, cmsNoCountry, Text, TextSize) != TextSize) return FALSE; - - if (!io ->Write(io, TextSize, Text)) return FALSE; - _cmsFree(self ->ContextID, Text); - - return TRUE; - - cmsUNUSED_PARAMETER(nItems); -} - -static -void* Type_UcrBg_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) -{ - cmsUcrBg* Src = (cmsUcrBg*) Ptr; - cmsUcrBg* NewUcrBg = (cmsUcrBg*) _cmsMallocZero(self ->ContextID, sizeof(cmsUcrBg)); - - if (NewUcrBg == NULL) return NULL; - - NewUcrBg ->Bg = cmsDupToneCurve(Src ->Bg); - NewUcrBg ->Ucr = cmsDupToneCurve(Src ->Ucr); - NewUcrBg ->Desc = cmsMLUdup(Src ->Desc); - - return (void*) NewUcrBg; - - cmsUNUSED_PARAMETER(n); -} - -static -void Type_UcrBg_Free(struct _cms_typehandler_struct* self, void *Ptr) -{ - cmsUcrBg* Src = (cmsUcrBg*) Ptr; - - if (Src ->Ucr) cmsFreeToneCurve(Src ->Ucr); - if (Src ->Bg) cmsFreeToneCurve(Src ->Bg); - if (Src ->Desc) cmsMLUfree(Src ->Desc); - - _cmsFree(self ->ContextID, Ptr); -} - -// ******************************************************************************** -// Type cmsSigCrdInfoType -// ******************************************************************************** - -/* -This type contains the PostScript product name to which this profile corresponds -and the names of the companion CRDs. Recall that a single profile can generate -multiple CRDs. It is implemented as a MLU being the language code "PS" and then -country varies for each element: - - nm: PostScript product name - #0: Rendering intent 0 CRD name - #1: Rendering intent 1 CRD name - #2: Rendering intent 2 CRD name - #3: Rendering intent 3 CRD name -*/ - - - -// Auxiliar, read an string specified as count + string -static -cmsBool ReadCountAndSting(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* mlu, cmsUInt32Number* SizeOfTag, const char* Section) -{ - cmsUInt32Number Count; - char* Text; - - if (*SizeOfTag < sizeof(cmsUInt32Number)) return FALSE; - - if (!_cmsReadUInt32Number(io, &Count)) return FALSE; - - if (Count > UINT_MAX - sizeof(cmsUInt32Number)) return FALSE; - if (*SizeOfTag < Count + sizeof(cmsUInt32Number)) return FALSE; - - Text = (char*) _cmsMalloc(self ->ContextID, Count+1); - if (Text == NULL) return FALSE; - - if (io ->Read(io, Text, sizeof(cmsUInt8Number), Count) != Count) { - _cmsFree(self ->ContextID, Text); - return FALSE; - } - - Text[Count] = 0; - - cmsMLUsetASCII(mlu, "PS", Section, Text); - _cmsFree(self ->ContextID, Text); - - *SizeOfTag -= (Count + sizeof(cmsUInt32Number)); - return TRUE; -} - -static -cmsBool WriteCountAndSting(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* mlu, const char* Section) -{ - cmsUInt32Number TextSize; - char* Text; - - TextSize = cmsMLUgetASCII(mlu, "PS", Section, NULL, 0); - Text = (char*) _cmsMalloc(self ->ContextID, TextSize); - - if (!_cmsWriteUInt32Number(io, TextSize)) return FALSE; - - if (cmsMLUgetASCII(mlu, "PS", Section, Text, TextSize) == 0) return FALSE; - - if (!io ->Write(io, TextSize, Text)) return FALSE; - _cmsFree(self ->ContextID, Text); - - return TRUE; -} - -static -void *Type_CrdInfo_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) -{ - cmsMLU* mlu = cmsMLUalloc(self ->ContextID, 5); - - *nItems = 0; - if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "nm")) goto Error; - if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#0")) goto Error; - if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#1")) goto Error; - if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#2")) goto Error; - if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#3")) goto Error; - - *nItems = 1; - return (void*) mlu; - -Error: - cmsMLUfree(mlu); - return NULL; - -} - -static -cmsBool Type_CrdInfo_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) -{ - - cmsMLU* mlu = (cmsMLU*) Ptr; - - if (!WriteCountAndSting(self, io, mlu, "nm")) goto Error; - if (!WriteCountAndSting(self, io, mlu, "#0")) goto Error; - if (!WriteCountAndSting(self, io, mlu, "#1")) goto Error; - if (!WriteCountAndSting(self, io, mlu, "#2")) goto Error; - if (!WriteCountAndSting(self, io, mlu, "#3")) goto Error; - - return TRUE; - -Error: - return FALSE; - - cmsUNUSED_PARAMETER(nItems); -} - - -static -void* Type_CrdInfo_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) -{ - return (void*) cmsMLUdup((cmsMLU*) Ptr); - - cmsUNUSED_PARAMETER(n); - cmsUNUSED_PARAMETER(self); -} - -static -void Type_CrdInfo_Free(struct _cms_typehandler_struct* self, void *Ptr) -{ - cmsMLUfree((cmsMLU*) Ptr); - return; - - cmsUNUSED_PARAMETER(self); -} - -// ******************************************************************************** -// Type cmsSigScreeningType -// ******************************************************************************** -// -//The screeningType describes various screening parameters including screen -//frequency, screening angle, and spot shape. - -static -void *Type_Screening_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) -{ - cmsScreening* sc = NULL; - cmsUInt32Number i; - - sc = (cmsScreening*) _cmsMallocZero(self ->ContextID, sizeof(cmsScreening)); - if (sc == NULL) return NULL; - - *nItems = 0; - - if (!_cmsReadUInt32Number(io, &sc ->Flag)) goto Error; - if (!_cmsReadUInt32Number(io, &sc ->nChannels)) goto Error; - - if (sc ->nChannels > cmsMAXCHANNELS - 1) - sc ->nChannels = cmsMAXCHANNELS - 1; - - for (i=0; i < sc ->nChannels; i++) { - - if (!_cmsRead15Fixed16Number(io, &sc ->Channels[i].Frequency)) goto Error; - if (!_cmsRead15Fixed16Number(io, &sc ->Channels[i].ScreenAngle)) goto Error; - if (!_cmsReadUInt32Number(io, &sc ->Channels[i].SpotShape)) goto Error; - } - - - *nItems = 1; - - return (void*) sc; - -Error: - if (sc != NULL) - _cmsFree(self ->ContextID, sc); - - return NULL; - - cmsUNUSED_PARAMETER(SizeOfTag); -} - - -static -cmsBool Type_Screening_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) -{ - cmsScreening* sc = (cmsScreening* ) Ptr; - cmsUInt32Number i; - - if (!_cmsWriteUInt32Number(io, sc ->Flag)) return FALSE; - if (!_cmsWriteUInt32Number(io, sc ->nChannels)) return FALSE; - - for (i=0; i < sc ->nChannels; i++) { - - if (!_cmsWrite15Fixed16Number(io, sc ->Channels[i].Frequency)) return FALSE; - if (!_cmsWrite15Fixed16Number(io, sc ->Channels[i].ScreenAngle)) return FALSE; - if (!_cmsWriteUInt32Number(io, sc ->Channels[i].SpotShape)) return FALSE; - } - - return TRUE; - - cmsUNUSED_PARAMETER(nItems); - cmsUNUSED_PARAMETER(self); -} - - -static -void* Type_Screening_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) -{ - return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsScreening)); - - cmsUNUSED_PARAMETER(n); -} - - -static -void Type_Screening_Free(struct _cms_typehandler_struct* self, void* Ptr) -{ - _cmsFree(self ->ContextID, Ptr); -} - -// ******************************************************************************** -// Type cmsSigViewingConditionsType -// ******************************************************************************** -// -//This type represents a set of viewing condition parameters including: -//CIE 'absolute'illuminant white point tristimulus values and CIE 'absolute' -//surround tristimulus values. - -static -void *Type_ViewingConditions_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) -{ - cmsICCViewingConditions* vc = NULL; - - vc = (cmsICCViewingConditions*) _cmsMallocZero(self ->ContextID, sizeof(cmsICCViewingConditions)); - if (vc == NULL) return NULL; - - *nItems = 0; - - if (!_cmsReadXYZNumber(io, &vc ->IlluminantXYZ)) goto Error; - if (!_cmsReadXYZNumber(io, &vc ->SurroundXYZ)) goto Error; - if (!_cmsReadUInt32Number(io, &vc ->IlluminantType)) goto Error; - - *nItems = 1; - - return (void*) vc; - -Error: - if (vc != NULL) - _cmsFree(self ->ContextID, vc); - - return NULL; - - cmsUNUSED_PARAMETER(SizeOfTag); -} - - -static -cmsBool Type_ViewingConditions_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) -{ - cmsICCViewingConditions* sc = (cmsICCViewingConditions* ) Ptr; - - if (!_cmsWriteXYZNumber(io, &sc ->IlluminantXYZ)) return FALSE; - if (!_cmsWriteXYZNumber(io, &sc ->SurroundXYZ)) return FALSE; - if (!_cmsWriteUInt32Number(io, sc ->IlluminantType)) return FALSE; - - return TRUE; - - cmsUNUSED_PARAMETER(nItems); - cmsUNUSED_PARAMETER(self); -} - - -static -void* Type_ViewingConditions_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) -{ - return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsScreening)); - - cmsUNUSED_PARAMETER(n); -} - - -static -void Type_ViewingConditions_Free(struct _cms_typehandler_struct* self, void* Ptr) -{ - _cmsFree(self ->ContextID, Ptr); -} - - -// ******************************************************************************** -// Type cmsSigMultiProcessElementType -// ******************************************************************************** - - -static -void* GenericMPEdup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) -{ - return (void*) cmsStageDup((cmsStage*) Ptr); - - cmsUNUSED_PARAMETER(n); - cmsUNUSED_PARAMETER(self); -} - -static -void GenericMPEfree(struct _cms_typehandler_struct* self, void *Ptr) -{ - cmsStageFree((cmsStage*) Ptr); - return; - - cmsUNUSED_PARAMETER(self); -} - -// Each curve is stored in one or more curve segments, with break-points specified between curve segments. -// The first curve segment always starts at -Infinity, and the last curve segment always ends at +Infinity. The -// first and last curve segments shall be specified in terms of a formula, whereas the other segments shall be -// specified either in terms of a formula, or by a sampled curve. - - -// Read an embedded segmented curve -static -cmsToneCurve* ReadSegmentedCurve(struct _cms_typehandler_struct* self, cmsIOHANDLER* io) -{ - cmsCurveSegSignature ElementSig; - cmsUInt32Number i, j; - cmsUInt16Number nSegments; - cmsCurveSegment* Segments; - cmsToneCurve* Curve; - cmsFloat32Number PrevBreak = -1E22F; // - infinite - - // Take signature and channels for each element. - if (!_cmsReadUInt32Number(io, (cmsUInt32Number*) &ElementSig)) return NULL; - - // That should be a segmented curve - if (ElementSig != cmsSigSegmentedCurve) return NULL; - - if (!_cmsReadUInt32Number(io, NULL)) return NULL; - if (!_cmsReadUInt16Number(io, &nSegments)) return NULL; - if (!_cmsReadUInt16Number(io, NULL)) return NULL; - - if (nSegments < 1) return NULL; - Segments = (cmsCurveSegment*) _cmsCalloc(self ->ContextID, nSegments, sizeof(cmsCurveSegment)); - if (Segments == NULL) return NULL; - - // Read breakpoints - for (i=0; i < (cmsUInt32Number) nSegments - 1; i++) { - - Segments[i].x0 = PrevBreak; - if (!_cmsReadFloat32Number(io, &Segments[i].x1)) goto Error; - PrevBreak = Segments[i].x1; - } - - Segments[nSegments-1].x0 = PrevBreak; - Segments[nSegments-1].x1 = 1E22F; // A big cmsFloat32Number number - - // Read segments - for (i=0; i < nSegments; i++) { - - if (!_cmsReadUInt32Number(io, (cmsUInt32Number*) &ElementSig)) goto Error; - if (!_cmsReadUInt32Number(io, NULL)) goto Error; - - switch (ElementSig) { - - case cmsSigFormulaCurveSeg: { - - cmsUInt16Number Type; - cmsUInt32Number ParamsByType[] = {4, 5, 5 }; - - if (!_cmsReadUInt16Number(io, &Type)) goto Error; - if (!_cmsReadUInt16Number(io, NULL)) goto Error; - - Segments[i].Type = Type + 6; - if (Type > 2) goto Error; - - for (j=0; j < ParamsByType[Type]; j++) { - - cmsFloat32Number f; - if (!_cmsReadFloat32Number(io, &f)) goto Error; - Segments[i].Params[j] = f; - } - } - break; - - - case cmsSigSampledCurveSeg: { - cmsUInt32Number Count; - - if (!_cmsReadUInt32Number(io, &Count)) goto Error; - - Segments[i].nGridPoints = Count; - Segments[i].SampledPoints = (cmsFloat32Number*) _cmsCalloc(self ->ContextID, Count, sizeof(cmsFloat32Number)); - if (Segments[i].SampledPoints == NULL) goto Error; - - for (j=0; j < Count; j++) { - if (!_cmsReadFloat32Number(io, &Segments[i].SampledPoints[j])) goto Error; - } - } - break; - - default: - { - char String[5]; - - _cmsTagSignature2String(String, (cmsTagSignature) ElementSig); - cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown curve element type '%s' found.", String); - } - goto Error; - - } - } - - Curve = cmsBuildSegmentedToneCurve(self ->ContextID, nSegments, Segments); - - for (i=0; i < nSegments; i++) { - if (Segments[i].SampledPoints) _cmsFree(self ->ContextID, Segments[i].SampledPoints); - } - _cmsFree(self ->ContextID, Segments); - return Curve; - -Error: - if (Segments) { - for (i=0; i < nSegments; i++) { - if (Segments[i].SampledPoints) _cmsFree(self ->ContextID, Segments[i].SampledPoints); - } - _cmsFree(self ->ContextID, Segments); - } - return NULL; -} - - -static -cmsBool ReadMPECurve(struct _cms_typehandler_struct* self, - cmsIOHANDLER* io, - void* Cargo, - cmsUInt32Number n, - cmsUInt32Number SizeOfTag) -{ - cmsToneCurve** GammaTables = ( cmsToneCurve**) Cargo; - - GammaTables[n] = ReadSegmentedCurve(self, io); - return (GammaTables[n] != NULL); - - cmsUNUSED_PARAMETER(SizeOfTag); -} - -static -void *Type_MPEcurve_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) -{ - cmsStage* mpe = NULL; - cmsUInt16Number InputChans, OutputChans; - cmsUInt32Number i, BaseOffset; - cmsToneCurve** GammaTables; - - *nItems = 0; - - // Get actual position as a basis for element offsets - BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); - - if (!_cmsReadUInt16Number(io, &InputChans)) return NULL; - if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL; - - if (InputChans != OutputChans) return NULL; - - GammaTables = (cmsToneCurve**) _cmsCalloc(self ->ContextID, InputChans, sizeof(cmsToneCurve*)); - if (GammaTables == NULL) return NULL; - - if (ReadPositionTable(self, io, InputChans, BaseOffset, GammaTables, ReadMPECurve)) { - - mpe = cmsStageAllocToneCurves(self ->ContextID, InputChans, GammaTables); - } - else { - mpe = NULL; - } - - for (i=0; i < InputChans; i++) { - if (GammaTables[i]) cmsFreeToneCurve(GammaTables[i]); - } - - _cmsFree(self ->ContextID, GammaTables); - *nItems = (mpe != NULL) ? 1 : 0; - return mpe; - - cmsUNUSED_PARAMETER(SizeOfTag); -} - - -// Write a single segmented curve. NO CHECK IS PERFORMED ON VALIDITY -static -cmsBool WriteSegmentedCurve(cmsIOHANDLER* io, cmsToneCurve* g) -{ - cmsUInt32Number i, j; - cmsCurveSegment* Segments = g ->Segments; - cmsUInt32Number nSegments = g ->nSegments; - - if (!_cmsWriteUInt32Number(io, cmsSigSegmentedCurve)) goto Error; - if (!_cmsWriteUInt32Number(io, 0)) goto Error; - if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) nSegments)) goto Error; - if (!_cmsWriteUInt16Number(io, 0)) goto Error; - - // Write the break-points - for (i=0; i < nSegments - 1; i++) { - if (!_cmsWriteFloat32Number(io, Segments[i].x1)) goto Error; - } - - // Write the segments - for (i=0; i < g ->nSegments; i++) { - - cmsCurveSegment* ActualSeg = Segments + i; - - if (ActualSeg -> Type == 0) { - - // This is a sampled curve - if (!_cmsWriteUInt32Number(io, (cmsUInt32Number) cmsSigSampledCurveSeg)) goto Error; - if (!_cmsWriteUInt32Number(io, 0)) goto Error; - if (!_cmsWriteUInt32Number(io, ActualSeg -> nGridPoints)) goto Error; - - for (j=0; j < g ->Segments[i].nGridPoints; j++) { - if (!_cmsWriteFloat32Number(io, ActualSeg -> SampledPoints[j])) goto Error; - } - - } - else { - int Type; - cmsUInt32Number ParamsByType[] = { 4, 5, 5 }; - - // This is a formula-based - if (!_cmsWriteUInt32Number(io, (cmsUInt32Number) cmsSigFormulaCurveSeg)) goto Error; - if (!_cmsWriteUInt32Number(io, 0)) goto Error; - - // We only allow 1, 2 and 3 as types - Type = ActualSeg ->Type - 6; - if (Type > 2 || Type < 0) goto Error; - - if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) Type)) goto Error; - if (!_cmsWriteUInt16Number(io, 0)) goto Error; - - for (j=0; j < ParamsByType[Type]; j++) { - if (!_cmsWriteFloat32Number(io, (cmsFloat32Number) ActualSeg ->Params[j])) goto Error; - } - } - - // It seems there is no need to align. Code is here, and for safety commented out - // if (!_cmsWriteAlignment(io)) goto Error; - } - - return TRUE; - -Error: - return FALSE; -} - - -static -cmsBool WriteMPECurve(struct _cms_typehandler_struct* self, - cmsIOHANDLER* io, - void* Cargo, - cmsUInt32Number n, - cmsUInt32Number SizeOfTag) -{ - _cmsStageToneCurvesData* Curves = (_cmsStageToneCurvesData*) Cargo; - - return WriteSegmentedCurve(io, Curves ->TheCurves[n]); - - cmsUNUSED_PARAMETER(SizeOfTag); - cmsUNUSED_PARAMETER(self); -} - -// Write a curve, checking first for validity -static -cmsBool Type_MPEcurve_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) -{ - cmsUInt32Number BaseOffset; - cmsStage* mpe = (cmsStage*) Ptr; - _cmsStageToneCurvesData* Curves = (_cmsStageToneCurvesData*) mpe ->Data; - - BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); - - // Write the header. Since those are curves, input and output channels are same - if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->InputChannels)) return FALSE; - if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->InputChannels)) return FALSE; - - if (!WritePositionTable(self, io, 0, - mpe ->InputChannels, BaseOffset, Curves, WriteMPECurve)) return FALSE; - - - return TRUE; - - cmsUNUSED_PARAMETER(nItems); -} - - - -// The matrix is organized as an array of PxQ+Q elements, where P is the number of input channels to the -// matrix, and Q is the number of output channels. The matrix elements are each float32Numbers. The array -// is organized as follows: -// array = [e11, e12, ? e1P, e21, e22, ? e2P, ? eQ1, eQ2, ? eQP, e1, e2, ? eQ] - -static -void *Type_MPEmatrix_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) -{ - cmsStage* mpe; - cmsUInt16Number InputChans, OutputChans; - cmsUInt32Number nElems, i; - cmsFloat64Number* Matrix; - cmsFloat64Number* Offsets; - - if (!_cmsReadUInt16Number(io, &InputChans)) return NULL; - if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL; - - - // Input and output chans may be ANY (up to 0xffff), - // but we choose to limit to 16 channels for now - if (InputChans >= cmsMAXCHANNELS) return NULL; - if (OutputChans >= cmsMAXCHANNELS) return NULL; - - nElems = InputChans * OutputChans; - - Matrix = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, nElems, sizeof(cmsFloat64Number)); - if (Matrix == NULL) return NULL; - - Offsets = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, OutputChans, sizeof(cmsFloat64Number)); - if (Offsets == NULL) { - - _cmsFree(self ->ContextID, Matrix); - return NULL; - } - - for (i=0; i < nElems; i++) { - - cmsFloat32Number v; - - if (!_cmsReadFloat32Number(io, &v)) { - _cmsFree(self ->ContextID, Matrix); - _cmsFree(self ->ContextID, Offsets); - return NULL; - } - Matrix[i] = v; - } - - - for (i=0; i < OutputChans; i++) { - - cmsFloat32Number v; - - if (!_cmsReadFloat32Number(io, &v)) { - _cmsFree(self ->ContextID, Matrix); - _cmsFree(self ->ContextID, Offsets); - return NULL; - } - Offsets[i] = v; - } - - - mpe = cmsStageAllocMatrix(self ->ContextID, OutputChans, InputChans, Matrix, Offsets); - _cmsFree(self ->ContextID, Matrix); - _cmsFree(self ->ContextID, Offsets); - - *nItems = 1; - - return mpe; - - cmsUNUSED_PARAMETER(SizeOfTag); -} - -static -cmsBool Type_MPEmatrix_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) -{ - cmsUInt32Number i, nElems; - cmsStage* mpe = (cmsStage*) Ptr; - _cmsStageMatrixData* Matrix = (_cmsStageMatrixData*) mpe ->Data; - - if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->InputChannels)) return FALSE; - if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->OutputChannels)) return FALSE; - - nElems = mpe ->InputChannels * mpe ->OutputChannels; - - for (i=0; i < nElems; i++) { - if (!_cmsWriteFloat32Number(io, (cmsFloat32Number) Matrix->Double[i])) return FALSE; - } - - - for (i=0; i < mpe ->OutputChannels; i++) { - - if (Matrix ->Offset == NULL) { - - if (!_cmsWriteFloat32Number(io, 0)) return FALSE; - } - else { - if (!_cmsWriteFloat32Number(io, (cmsFloat32Number) Matrix->Offset[i])) return FALSE; - } - } - - return TRUE; - - cmsUNUSED_PARAMETER(nItems); - cmsUNUSED_PARAMETER(self); -} - - - -static -void *Type_MPEclut_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) -{ - cmsStage* mpe = NULL; - cmsUInt16Number InputChans, OutputChans; - cmsUInt8Number Dimensions8[16]; - cmsUInt32Number i, nMaxGrids, GridPoints[MAX_INPUT_DIMENSIONS]; - _cmsStageCLutData* clut; - - if (!_cmsReadUInt16Number(io, &InputChans)) return NULL; - if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL; - - if (InputChans == 0) goto Error; - if (OutputChans == 0) goto Error; - - if (io ->Read(io, Dimensions8, sizeof(cmsUInt8Number), 16) != 16) - goto Error; - - // Copy MAX_INPUT_DIMENSIONS at most. Expand to cmsUInt32Number - nMaxGrids = InputChans > MAX_INPUT_DIMENSIONS ? MAX_INPUT_DIMENSIONS : InputChans; - - for (i = 0; i < nMaxGrids; i++) { - if (Dimensions8[i] == 1) goto Error; // Impossible value, 0 for no CLUT and then 2 at least - GridPoints[i] = (cmsUInt32Number)Dimensions8[i]; - } - - // Allocate the true CLUT - mpe = cmsStageAllocCLutFloatGranular(self ->ContextID, GridPoints, InputChans, OutputChans, NULL); - if (mpe == NULL) goto Error; - - // Read the data - clut = (_cmsStageCLutData*) mpe ->Data; - for (i=0; i < clut ->nEntries; i++) { - - if (!_cmsReadFloat32Number(io, &clut ->Tab.TFloat[i])) goto Error; - } - - *nItems = 1; - return mpe; - -Error: - *nItems = 0; - if (mpe != NULL) cmsStageFree(mpe); - return NULL; - - cmsUNUSED_PARAMETER(SizeOfTag); -} - -// Write a CLUT in floating point -static -cmsBool Type_MPEclut_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) -{ - cmsUInt8Number Dimensions8[16]; - cmsUInt32Number i; - cmsStage* mpe = (cmsStage*) Ptr; - _cmsStageCLutData* clut = (_cmsStageCLutData*) mpe ->Data; - - // Check for maximum number of channels - if (mpe -> InputChannels > 15) return FALSE; - - // Only floats are supported in MPE - if (clut ->HasFloatValues == FALSE) return FALSE; - - if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->InputChannels)) return FALSE; - if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->OutputChannels)) return FALSE; - - memset(Dimensions8, 0, sizeof(Dimensions8)); - - for (i=0; i < mpe ->InputChannels; i++) - Dimensions8[i] = (cmsUInt8Number) clut ->Params ->nSamples[i]; - - if (!io ->Write(io, 16, Dimensions8)) return FALSE; - - for (i=0; i < clut ->nEntries; i++) { - - if (!_cmsWriteFloat32Number(io, clut ->Tab.TFloat[i])) return FALSE; - } - - return TRUE; - - cmsUNUSED_PARAMETER(nItems); - cmsUNUSED_PARAMETER(self); -} - - - -// This is the list of built-in MPE types -static _cmsTagTypeLinkedList SupportedMPEtypes[] = { - -{{ (cmsTagTypeSignature) cmsSigBAcsElemType, NULL, NULL, NULL, NULL, NULL, 0 }, &SupportedMPEtypes[1] }, // Ignore those elements for now -{{ (cmsTagTypeSignature) cmsSigEAcsElemType, NULL, NULL, NULL, NULL, NULL, 0 }, &SupportedMPEtypes[2] }, // (That's what the spec says) - -{TYPE_MPE_HANDLER((cmsTagTypeSignature) cmsSigCurveSetElemType, MPEcurve), &SupportedMPEtypes[3] }, -{TYPE_MPE_HANDLER((cmsTagTypeSignature) cmsSigMatrixElemType, MPEmatrix), &SupportedMPEtypes[4] }, -{TYPE_MPE_HANDLER((cmsTagTypeSignature) cmsSigCLutElemType, MPEclut), NULL }, -}; - -_cmsTagTypePluginChunkType _cmsMPETypePluginChunk = { NULL }; - -static -cmsBool ReadMPEElem(struct _cms_typehandler_struct* self, - cmsIOHANDLER* io, - void* Cargo, - cmsUInt32Number n, - cmsUInt32Number SizeOfTag) -{ - cmsStageSignature ElementSig; - 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; - - // The reserved placeholder - if (!_cmsReadUInt32Number(io, NULL)) return FALSE; - - // Read diverse MPE types - TypeHandler = GetHandler((cmsTagTypeSignature) ElementSig, MPETypePluginChunk ->TagTypes, SupportedMPEtypes); - if (TypeHandler == NULL) { - - char String[5]; - - _cmsTagSignature2String(String, (cmsTagSignature) ElementSig); - - // An unknown element was found. - cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown MPE type '%s' found.", String); - return FALSE; - } - - // If no read method, just ignore the element (valid for cmsSigBAcsElemType and cmsSigEAcsElemType) - // Read the MPE. No size is given - if (TypeHandler ->ReadPtr != NULL) { - - // This is a real element which should be read and processed - if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, (cmsStage*) TypeHandler ->ReadPtr(self, io, &nItems, SizeOfTag))) - return FALSE; - } - - return TRUE; - - cmsUNUSED_PARAMETER(SizeOfTag); - cmsUNUSED_PARAMETER(n); -} - - -// This is the main dispatcher for MPE -static -void *Type_MPE_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) -{ - cmsUInt16Number InputChans, OutputChans; - cmsUInt32Number ElementCount; - cmsPipeline *NewLUT = NULL; - cmsUInt32Number BaseOffset; - - // Get actual position as a basis for element offsets - BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); - - // Read channels and element count - if (!_cmsReadUInt16Number(io, &InputChans)) return NULL; - if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL; - - if (InputChans == 0 || InputChans >= cmsMAXCHANNELS) return NULL; - if (OutputChans == 0 || OutputChans >= cmsMAXCHANNELS) return NULL; - - // Allocates an empty LUT - NewLUT = cmsPipelineAlloc(self ->ContextID, InputChans, OutputChans); - if (NewLUT == NULL) return NULL; - - if (!_cmsReadUInt32Number(io, &ElementCount)) goto Error; - if (!ReadPositionTable(self, io, ElementCount, BaseOffset, NewLUT, ReadMPEElem)) goto Error; - - // Success - *nItems = 1; - return NewLUT; - - // Error -Error: - if (NewLUT != NULL) cmsPipelineFree(NewLUT); - *nItems = 0; - return NULL; - - cmsUNUSED_PARAMETER(SizeOfTag); -} - - - -// This one is a liitle bit more complex, so we don't use position tables this time. -static -cmsBool Type_MPE_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) -{ - cmsUInt32Number i, BaseOffset, DirectoryPos, CurrentPos; - int inputChan, outputChan; - cmsUInt32Number ElemCount; - cmsUInt32Number *ElementOffsets = NULL, *ElementSizes = NULL, Before; - cmsStageSignature ElementSig; - cmsPipeline* Lut = (cmsPipeline*) Ptr; - cmsStage* Elem = Lut ->Elements; - cmsTagTypeHandler* TypeHandler; - _cmsTagTypePluginChunkType* MPETypePluginChunk = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(self->ContextID, MPEPlugin); - - BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); - - inputChan = cmsPipelineInputChannels(Lut); - outputChan = cmsPipelineOutputChannels(Lut); - ElemCount = cmsPipelineStageCount(Lut); - - ElementOffsets = (cmsUInt32Number *) _cmsCalloc(self ->ContextID, ElemCount, sizeof(cmsUInt32Number)); - if (ElementOffsets == NULL) goto Error; - - ElementSizes = (cmsUInt32Number *) _cmsCalloc(self ->ContextID, ElemCount, sizeof(cmsUInt32Number)); - if (ElementSizes == NULL) goto Error; - - // Write the head - if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) inputChan)) goto Error; - if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) outputChan)) goto Error; - if (!_cmsWriteUInt32Number(io, (cmsUInt16Number) ElemCount)) goto Error; - - DirectoryPos = io ->Tell(io); - - // Write a fake directory to be filled latter on - for (i=0; i < ElemCount; i++) { - if (!_cmsWriteUInt32Number(io, 0)) goto Error; // Offset - if (!_cmsWriteUInt32Number(io, 0)) goto Error; // size - } - - // Write each single tag. Keep track of the size as well. - for (i=0; i < ElemCount; i++) { - - ElementOffsets[i] = io ->Tell(io) - BaseOffset; - - ElementSig = Elem ->Type; - - TypeHandler = GetHandler((cmsTagTypeSignature) ElementSig, MPETypePluginChunk->TagTypes, SupportedMPEtypes); - if (TypeHandler == NULL) { - - char String[5]; - - _cmsTagSignature2String(String, (cmsTagSignature) ElementSig); - - // An unknow element was found. - cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Found unknown MPE type '%s'", String); - goto Error; - } - - if (!_cmsWriteUInt32Number(io, ElementSig)) goto Error; - if (!_cmsWriteUInt32Number(io, 0)) goto Error; - Before = io ->Tell(io); - if (!TypeHandler ->WritePtr(self, io, Elem, 1)) goto Error; - if (!_cmsWriteAlignment(io)) goto Error; - - ElementSizes[i] = io ->Tell(io) - Before; - - Elem = Elem ->Next; - } - - // Write the directory - CurrentPos = io ->Tell(io); - - if (!io ->Seek(io, DirectoryPos)) goto Error; - - for (i=0; i < ElemCount; i++) { - if (!_cmsWriteUInt32Number(io, ElementOffsets[i])) goto Error; - if (!_cmsWriteUInt32Number(io, ElementSizes[i])) goto Error; - } - - if (!io ->Seek(io, CurrentPos)) goto Error; - - if (ElementOffsets != NULL) _cmsFree(self ->ContextID, ElementOffsets); - if (ElementSizes != NULL) _cmsFree(self ->ContextID, ElementSizes); - return TRUE; - -Error: - if (ElementOffsets != NULL) _cmsFree(self ->ContextID, ElementOffsets); - if (ElementSizes != NULL) _cmsFree(self ->ContextID, ElementSizes); - return FALSE; - - cmsUNUSED_PARAMETER(nItems); -} - - -static -void* Type_MPE_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) -{ - return (void*) cmsPipelineDup((cmsPipeline*) Ptr); - - cmsUNUSED_PARAMETER(n); - cmsUNUSED_PARAMETER(self); -} - -static -void Type_MPE_Free(struct _cms_typehandler_struct* self, void *Ptr) -{ - cmsPipelineFree((cmsPipeline*) Ptr); - return; - - cmsUNUSED_PARAMETER(self); -} - - -// ******************************************************************************** -// Type cmsSigVcgtType -// ******************************************************************************** - - -#define cmsVideoCardGammaTableType 0 -#define cmsVideoCardGammaFormulaType 1 - -// Used internally -typedef struct { - double Gamma; - double Min; - double Max; -} _cmsVCGTGAMMA; - - -static -void *Type_vcgt_Read(struct _cms_typehandler_struct* self, - cmsIOHANDLER* io, - cmsUInt32Number* nItems, - cmsUInt32Number SizeOfTag) -{ - cmsUInt32Number TagType, n, i; - cmsToneCurve** Curves; - - *nItems = 0; - - // Read tag type - if (!_cmsReadUInt32Number(io, &TagType)) return NULL; - - // Allocate space for the array - Curves = ( cmsToneCurve**) _cmsCalloc(self ->ContextID, 3, sizeof(cmsToneCurve*)); - if (Curves == NULL) return NULL; - - // There are two possible flavors - switch (TagType) { - - // Gamma is stored as a table - case cmsVideoCardGammaTableType: - { - cmsUInt16Number nChannels, nElems, nBytes; - - // Check channel count, which should be 3 (we don't support monochrome this time) - if (!_cmsReadUInt16Number(io, &nChannels)) goto Error; - - if (nChannels != 3) { - cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported number of channels for VCGT '%d'", nChannels); - goto Error; - } - - // Get Table element count and bytes per element - if (!_cmsReadUInt16Number(io, &nElems)) goto Error; - if (!_cmsReadUInt16Number(io, &nBytes)) goto Error; - - // Adobe's quirk fixup. Fixing broken profiles... - if (nElems == 256 && nBytes == 1 && SizeOfTag == 1576) - nBytes = 2; - - - // Populate tone curves - for (n=0; n < 3; n++) { - - Curves[n] = cmsBuildTabulatedToneCurve16(self ->ContextID, nElems, NULL); - if (Curves[n] == NULL) goto Error; - - // On depending on byte depth - switch (nBytes) { - - // One byte, 0..255 - case 1: - for (i=0; i < nElems; i++) { - - cmsUInt8Number v; - - if (!_cmsReadUInt8Number(io, &v)) goto Error; - Curves[n] ->Table16[i] = FROM_8_TO_16(v); - } - break; - - // One word 0..65535 - case 2: - if (!_cmsReadUInt16Array(io, nElems, Curves[n]->Table16)) goto Error; - break; - - // Unsupported - default: - cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported bit depth for VCGT '%d'", nBytes * 8); - goto Error; - } - } // For all 3 channels - } - break; - - // In this case, gamma is stored as a formula - case cmsVideoCardGammaFormulaType: - { - _cmsVCGTGAMMA Colorant[3]; - - // Populate tone curves - for (n=0; n < 3; n++) { - - double Params[10]; - - if (!_cmsRead15Fixed16Number(io, &Colorant[n].Gamma)) goto Error; - if (!_cmsRead15Fixed16Number(io, &Colorant[n].Min)) goto Error; - if (!_cmsRead15Fixed16Number(io, &Colorant[n].Max)) goto Error; - - // Parametric curve type 5 is: - // Y = (aX + b)^Gamma + e | X >= d - // Y = cX + f | X < d - - // vcgt formula is: - // Y = (Max ?Min) * (X ^ Gamma) + Min - - // So, the translation is - // a = (Max ?Min) ^ ( 1 / Gamma) - // e = Min - // b=c=d=f=0 - - Params[0] = Colorant[n].Gamma; - Params[1] = pow((Colorant[n].Max - Colorant[n].Min), (1.0 / Colorant[n].Gamma)); - Params[2] = 0; - Params[3] = 0; - Params[4] = 0; - Params[5] = Colorant[n].Min; - Params[6] = 0; - - Curves[n] = cmsBuildParametricToneCurve(self ->ContextID, 5, Params); - if (Curves[n] == NULL) goto Error; - } - } - break; - - // Unsupported - default: - cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported tag type for VCGT '%d'", TagType); - goto Error; - } - - *nItems = 1; - return (void*) Curves; - -// Regret, free all resources -Error: - - cmsFreeToneCurveTriple(Curves); - _cmsFree(self ->ContextID, Curves); - return NULL; - - cmsUNUSED_PARAMETER(SizeOfTag); -} - - -// We don't support all flavors, only 16bits tables and formula -static -cmsBool Type_vcgt_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) -{ - cmsToneCurve** Curves = (cmsToneCurve**) Ptr; - cmsUInt32Number i, j; - - if (cmsGetToneCurveParametricType(Curves[0]) == 5 && - cmsGetToneCurveParametricType(Curves[1]) == 5 && - cmsGetToneCurveParametricType(Curves[2]) == 5) { - - if (!_cmsWriteUInt32Number(io, cmsVideoCardGammaFormulaType)) return FALSE; - - // Save parameters - for (i=0; i < 3; i++) { - - _cmsVCGTGAMMA v; - - v.Gamma = Curves[i] ->Segments[0].Params[0]; - v.Min = Curves[i] ->Segments[0].Params[5]; - v.Max = pow(Curves[i] ->Segments[0].Params[1], v.Gamma) + v.Min; - - if (!_cmsWrite15Fixed16Number(io, v.Gamma)) return FALSE; - if (!_cmsWrite15Fixed16Number(io, v.Min)) return FALSE; - if (!_cmsWrite15Fixed16Number(io, v.Max)) return FALSE; - } - } - - else { - - // Always store as a table of 256 words - if (!_cmsWriteUInt32Number(io, cmsVideoCardGammaTableType)) return FALSE; - if (!_cmsWriteUInt16Number(io, 3)) return FALSE; - if (!_cmsWriteUInt16Number(io, 256)) return FALSE; - if (!_cmsWriteUInt16Number(io, 2)) return FALSE; - - for (i=0; i < 3; i++) { - for (j=0; j < 256; j++) { - - cmsFloat32Number v = cmsEvalToneCurveFloat(Curves[i], (cmsFloat32Number) (j / 255.0)); - cmsUInt16Number n = _cmsQuickSaturateWord(v * 65535.0); - - if (!_cmsWriteUInt16Number(io, n)) return FALSE; - } - } - } - - return TRUE; - - cmsUNUSED_PARAMETER(self); - cmsUNUSED_PARAMETER(nItems); -} - -static -void* Type_vcgt_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) -{ - cmsToneCurve** OldCurves = (cmsToneCurve**) Ptr; - cmsToneCurve** NewCurves; - - NewCurves = ( cmsToneCurve**) _cmsCalloc(self ->ContextID, 3, sizeof(cmsToneCurve*)); - if (NewCurves == NULL) return NULL; - - NewCurves[0] = cmsDupToneCurve(OldCurves[0]); - NewCurves[1] = cmsDupToneCurve(OldCurves[1]); - NewCurves[2] = cmsDupToneCurve(OldCurves[2]); - - return (void*) NewCurves; - - cmsUNUSED_PARAMETER(n); -} - - -static -void Type_vcgt_Free(struct _cms_typehandler_struct* self, void* Ptr) -{ - cmsFreeToneCurveTriple((cmsToneCurve**) Ptr); - _cmsFree(self ->ContextID, Ptr); -} - - -// ******************************************************************************** -// Type cmsSigDictType -// ******************************************************************************** - -// Single column of the table can point to wchar or MLUC elements. Holds arrays of data -typedef struct { - cmsContext ContextID; - cmsUInt32Number *Offsets; - cmsUInt32Number *Sizes; -} _cmsDICelem; - -typedef struct { - _cmsDICelem Name, Value, DisplayName, DisplayValue; - -} _cmsDICarray; - -// Allocate an empty array element -static -cmsBool AllocElem(cmsContext ContextID, _cmsDICelem* e, cmsUInt32Number Count) -{ - e->Offsets = (cmsUInt32Number *) _cmsCalloc(ContextID, Count, sizeof(cmsUInt32Number)); - if (e->Offsets == NULL) return FALSE; - - e->Sizes = (cmsUInt32Number *) _cmsCalloc(ContextID, Count, sizeof(cmsUInt32Number)); - if (e->Sizes == NULL) { - - _cmsFree(ContextID, e -> Offsets); - return FALSE; - } - - e ->ContextID = ContextID; - return TRUE; -} - -// Free an array element -static -void FreeElem(_cmsDICelem* e) -{ - if (e ->Offsets != NULL) _cmsFree(e -> ContextID, e -> Offsets); - if (e ->Sizes != NULL) _cmsFree(e -> ContextID, e -> Sizes); - e->Offsets = e ->Sizes = NULL; -} - -// Get rid of whole array -static -void FreeArray( _cmsDICarray* a) -{ - if (a ->Name.Offsets != NULL) FreeElem(&a->Name); - if (a ->Value.Offsets != NULL) FreeElem(&a ->Value); - if (a ->DisplayName.Offsets != NULL) FreeElem(&a->DisplayName); - if (a ->DisplayValue.Offsets != NULL) FreeElem(&a ->DisplayValue); -} - - -// Allocate whole array -static -cmsBool AllocArray(cmsContext ContextID, _cmsDICarray* a, cmsUInt32Number Count, cmsUInt32Number Length) -{ - // Empty values - memset(a, 0, sizeof(_cmsDICarray)); - - // On depending on record size, create column arrays - if (!AllocElem(ContextID, &a ->Name, Count)) goto Error; - if (!AllocElem(ContextID, &a ->Value, Count)) goto Error; - - if (Length > 16) { - if (!AllocElem(ContextID, &a -> DisplayName, Count)) goto Error; - - } - if (Length > 24) { - if (!AllocElem(ContextID, &a ->DisplayValue, Count)) goto Error; - } - return TRUE; - -Error: - FreeArray(a); - return FALSE; -} - -// Read one element -static -cmsBool ReadOneElem(cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i, cmsUInt32Number BaseOffset) -{ - if (!_cmsReadUInt32Number(io, &e->Offsets[i])) return FALSE; - if (!_cmsReadUInt32Number(io, &e ->Sizes[i])) return FALSE; - - // An offset of zero has special meaning and shal be preserved - if (e ->Offsets[i] > 0) - e ->Offsets[i] += BaseOffset; - return TRUE; -} - - -static -cmsBool ReadOffsetArray(cmsIOHANDLER* io, _cmsDICarray* a, cmsUInt32Number Count, cmsUInt32Number Length, cmsUInt32Number BaseOffset) -{ - cmsUInt32Number i; - - // Read column arrays - for (i=0; i < Count; i++) { - - if (!ReadOneElem(io, &a -> Name, i, BaseOffset)) return FALSE; - if (!ReadOneElem(io, &a -> Value, i, BaseOffset)) return FALSE; - - if (Length > 16) { - - if (!ReadOneElem(io, &a ->DisplayName, i, BaseOffset)) return FALSE; - - } - - if (Length > 24) { - - if (!ReadOneElem(io, & a -> DisplayValue, i, BaseOffset)) return FALSE; - } - } - return TRUE; -} - - -// Write one element -static -cmsBool WriteOneElem(cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i) -{ - if (!_cmsWriteUInt32Number(io, e->Offsets[i])) return FALSE; - if (!_cmsWriteUInt32Number(io, e ->Sizes[i])) return FALSE; - - return TRUE; -} - -static -cmsBool WriteOffsetArray(cmsIOHANDLER* io, _cmsDICarray* a, cmsUInt32Number Count, cmsUInt32Number Length) -{ - cmsUInt32Number i; - - for (i=0; i < Count; i++) { - - if (!WriteOneElem(io, &a -> Name, i)) return FALSE; - if (!WriteOneElem(io, &a -> Value, i)) return FALSE; - - if (Length > 16) { - - if (!WriteOneElem(io, &a -> DisplayName, i)) return FALSE; - } - - if (Length > 24) { - - if (!WriteOneElem(io, &a -> DisplayValue, i)) return FALSE; - } - } - - return TRUE; -} - -static -cmsBool ReadOneWChar(cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i, wchar_t ** wcstr) -{ - - cmsUInt32Number nChars; - - // Special case for undefined strings (see ICC Votable - // Proposal Submission, Dictionary Type and Metadata TAG Definition) - if (e -> Offsets[i] == 0) { - - *wcstr = NULL; - return TRUE; - } - - if (!io -> Seek(io, e -> Offsets[i])) return FALSE; - - nChars = e ->Sizes[i] / sizeof(cmsUInt16Number); - - - *wcstr = (wchar_t*) _cmsMallocZero(e ->ContextID, (nChars + 1) * sizeof(wchar_t)); - if (*wcstr == NULL) return FALSE; - - if (!_cmsReadWCharArray(io, nChars, *wcstr)) { - _cmsFree(e ->ContextID, *wcstr); - return FALSE; - } - - // End of string marker - (*wcstr)[nChars] = 0; - return TRUE; -} - -static -cmsUInt32Number mywcslen(const wchar_t *s) -{ - const wchar_t *p; - - p = s; - while (*p) - p++; - - return (cmsUInt32Number)(p - s); -} - -static -cmsBool WriteOneWChar(cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i, const wchar_t * wcstr, cmsUInt32Number BaseOffset) -{ - cmsUInt32Number Before = io ->Tell(io); - cmsUInt32Number n; - - e ->Offsets[i] = Before - BaseOffset; - - if (wcstr == NULL) { - e ->Sizes[i] = 0; - e ->Offsets[i] = 0; - return TRUE; - } - - n = mywcslen(wcstr); - if (!_cmsWriteWCharArray(io, n, wcstr)) return FALSE; - - e ->Sizes[i] = io ->Tell(io) - Before; - return TRUE; -} - -static -cmsBool ReadOneMLUC(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i, cmsMLU** mlu) -{ - cmsUInt32Number nItems = 0; - - // A way to get null MLUCs - if (e -> Offsets[i] == 0 || e ->Sizes[i] == 0) { - - *mlu = NULL; - return TRUE; - } - - if (!io -> Seek(io, e -> Offsets[i])) return FALSE; - - *mlu = (cmsMLU*) Type_MLU_Read(self, io, &nItems, e ->Sizes[i]); - return *mlu != NULL; -} - -static -cmsBool WriteOneMLUC(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i, const cmsMLU* mlu, cmsUInt32Number BaseOffset) -{ - cmsUInt32Number Before; - - // Special case for undefined strings (see ICC Votable - // Proposal Submission, Dictionary Type and Metadata TAG Definition) - if (mlu == NULL) { - e ->Sizes[i] = 0; - e ->Offsets[i] = 0; - return TRUE; - } - - Before = io ->Tell(io); - e ->Offsets[i] = Before - BaseOffset; - - if (!Type_MLU_Write(self, io, (void*) mlu, 1)) return FALSE; - - e ->Sizes[i] = io ->Tell(io) - Before; - return TRUE; -} - - -static -void *Type_Dictionary_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) -{ - cmsHANDLE hDict; - cmsUInt32Number i, Count, Length; - cmsUInt32Number BaseOffset; - _cmsDICarray a; - wchar_t *NameWCS = NULL, *ValueWCS = NULL; - cmsMLU *DisplayNameMLU = NULL, *DisplayValueMLU=NULL; - cmsBool rc; - - *nItems = 0; - - // Get actual position as a basis for element offsets - BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); - - // Get name-value record count - if (!_cmsReadUInt32Number(io, &Count)) return NULL; - SizeOfTag -= sizeof(cmsUInt32Number); - - // Get rec length - if (!_cmsReadUInt32Number(io, &Length)) return NULL; - SizeOfTag -= sizeof(cmsUInt32Number); - - // Check for valid lengths - if (Length != 16 && Length != 24 && Length != 32) { - cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown record length in dictionary '%d'", Length); - return NULL; - } - - // Creates an empty dictionary - hDict = cmsDictAlloc(self -> ContextID); - if (hDict == NULL) return NULL; - - // On depending on record size, create column arrays - if (!AllocArray(self -> ContextID, &a, Count, Length)) goto Error; - - // Read column arrays - if (!ReadOffsetArray(io, &a, Count, Length, BaseOffset)) goto Error; - - // Seek to each element and read it - for (i=0; i < Count; i++) { - - if (!ReadOneWChar(io, &a.Name, i, &NameWCS)) goto Error; - if (!ReadOneWChar(io, &a.Value, i, &ValueWCS)) goto Error; - - if (Length > 16) { - if (!ReadOneMLUC(self, io, &a.DisplayName, i, &DisplayNameMLU)) goto Error; - } - - if (Length > 24) { - if (!ReadOneMLUC(self, io, &a.DisplayValue, i, &DisplayValueMLU)) goto Error; - } - - if (NameWCS == NULL || ValueWCS == NULL) { - - cmsSignalError(self->ContextID, cmsERROR_CORRUPTION_DETECTED, "Bad dictionary Name/Value"); - rc = FALSE; - } - else { - - rc = cmsDictAddEntry(hDict, NameWCS, ValueWCS, DisplayNameMLU, DisplayValueMLU); - } - - if (NameWCS != NULL) _cmsFree(self ->ContextID, NameWCS); - if (ValueWCS != NULL) _cmsFree(self ->ContextID, ValueWCS); - if (DisplayNameMLU != NULL) cmsMLUfree(DisplayNameMLU); - if (DisplayValueMLU != NULL) cmsMLUfree(DisplayValueMLU); - - if (!rc) goto Error; - } - - FreeArray(&a); - *nItems = 1; - return (void*) hDict; - -Error: - FreeArray(&a); - cmsDictFree(hDict); - return NULL; -} - - -static -cmsBool Type_Dictionary_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) -{ - cmsHANDLE hDict = (cmsHANDLE) Ptr; - const cmsDICTentry* p; - cmsBool AnyName, AnyValue; - cmsUInt32Number i, Count, Length; - cmsUInt32Number DirectoryPos, CurrentPos, BaseOffset; - _cmsDICarray a; - - if (hDict == NULL) return FALSE; - - BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); - - // Let's inspect the dictionary - Count = 0; AnyName = FALSE; AnyValue = FALSE; - for (p = cmsDictGetEntryList(hDict); p != NULL; p = cmsDictNextEntry(p)) { - - if (p ->DisplayName != NULL) AnyName = TRUE; - if (p ->DisplayValue != NULL) AnyValue = TRUE; - Count++; - } - - Length = 16; - if (AnyName) Length += 8; - if (AnyValue) Length += 8; - - if (!_cmsWriteUInt32Number(io, Count)) return FALSE; - if (!_cmsWriteUInt32Number(io, Length)) return FALSE; - - // Keep starting position of offsets table - DirectoryPos = io ->Tell(io); - - // Allocate offsets array - if (!AllocArray(self ->ContextID, &a, Count, Length)) goto Error; - - // Write a fake directory to be filled latter on - if (!WriteOffsetArray(io, &a, Count, Length)) goto Error; - - // Write each element. Keep track of the size as well. - p = cmsDictGetEntryList(hDict); - for (i=0; i < Count; i++) { - - if (!WriteOneWChar(io, &a.Name, i, p ->Name, BaseOffset)) goto Error; - if (!WriteOneWChar(io, &a.Value, i, p ->Value, BaseOffset)) goto Error; - - if (p ->DisplayName != NULL) { - if (!WriteOneMLUC(self, io, &a.DisplayName, i, p ->DisplayName, BaseOffset)) goto Error; - } - - if (p ->DisplayValue != NULL) { - if (!WriteOneMLUC(self, io, &a.DisplayValue, i, p ->DisplayValue, BaseOffset)) goto Error; - } - - p = cmsDictNextEntry(p); - } - - // Write the directory - CurrentPos = io ->Tell(io); - if (!io ->Seek(io, DirectoryPos)) goto Error; - - if (!WriteOffsetArray(io, &a, Count, Length)) goto Error; - - if (!io ->Seek(io, CurrentPos)) goto Error; - - FreeArray(&a); - return TRUE; - -Error: - FreeArray(&a); - return FALSE; - - cmsUNUSED_PARAMETER(nItems); -} - - -static -void* Type_Dictionary_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) -{ - return (void*) cmsDictDup((cmsHANDLE) Ptr); - - cmsUNUSED_PARAMETER(n); - cmsUNUSED_PARAMETER(self); -} - - -static -void Type_Dictionary_Free(struct _cms_typehandler_struct* self, void* Ptr) -{ - cmsDictFree((cmsHANDLE) Ptr); - cmsUNUSED_PARAMETER(self); -} - - -// ******************************************************************************** -// Type support main routines -// ******************************************************************************** - - -// This is the list of built-in types -static _cmsTagTypeLinkedList SupportedTagTypes[] = { - -{TYPE_HANDLER(cmsSigChromaticityType, Chromaticity), &SupportedTagTypes[1] }, -{TYPE_HANDLER(cmsSigColorantOrderType, ColorantOrderType), &SupportedTagTypes[2] }, -{TYPE_HANDLER(cmsSigS15Fixed16ArrayType, S15Fixed16), &SupportedTagTypes[3] }, -{TYPE_HANDLER(cmsSigU16Fixed16ArrayType, U16Fixed16), &SupportedTagTypes[4] }, -{TYPE_HANDLER(cmsSigTextType, Text), &SupportedTagTypes[5] }, -{TYPE_HANDLER(cmsSigTextDescriptionType, Text_Description), &SupportedTagTypes[6] }, -{TYPE_HANDLER(cmsSigCurveType, Curve), &SupportedTagTypes[7] }, -{TYPE_HANDLER(cmsSigParametricCurveType, ParametricCurve), &SupportedTagTypes[8] }, -{TYPE_HANDLER(cmsSigDateTimeType, DateTime), &SupportedTagTypes[9] }, -{TYPE_HANDLER(cmsSigLut8Type, LUT8), &SupportedTagTypes[10] }, -{TYPE_HANDLER(cmsSigLut16Type, LUT16), &SupportedTagTypes[11] }, -{TYPE_HANDLER(cmsSigColorantTableType, ColorantTable), &SupportedTagTypes[12] }, -{TYPE_HANDLER(cmsSigNamedColor2Type, NamedColor), &SupportedTagTypes[13] }, -{TYPE_HANDLER(cmsSigMultiLocalizedUnicodeType, MLU), &SupportedTagTypes[14] }, -{TYPE_HANDLER(cmsSigProfileSequenceDescType, ProfileSequenceDesc), &SupportedTagTypes[15] }, -{TYPE_HANDLER(cmsSigSignatureType, Signature), &SupportedTagTypes[16] }, -{TYPE_HANDLER(cmsSigMeasurementType, Measurement), &SupportedTagTypes[17] }, -{TYPE_HANDLER(cmsSigDataType, Data), &SupportedTagTypes[18] }, -{TYPE_HANDLER(cmsSigLutAtoBType, LUTA2B), &SupportedTagTypes[19] }, -{TYPE_HANDLER(cmsSigLutBtoAType, LUTB2A), &SupportedTagTypes[20] }, -{TYPE_HANDLER(cmsSigUcrBgType, UcrBg), &SupportedTagTypes[21] }, -{TYPE_HANDLER(cmsSigCrdInfoType, CrdInfo), &SupportedTagTypes[22] }, -{TYPE_HANDLER(cmsSigMultiProcessElementType, MPE), &SupportedTagTypes[23] }, -{TYPE_HANDLER(cmsSigScreeningType, Screening), &SupportedTagTypes[24] }, -{TYPE_HANDLER(cmsSigViewingConditionsType, ViewingConditions), &SupportedTagTypes[25] }, -{TYPE_HANDLER(cmsSigXYZType, XYZ), &SupportedTagTypes[26] }, -{TYPE_HANDLER(cmsCorbisBrokenXYZtype, XYZ), &SupportedTagTypes[27] }, -{TYPE_HANDLER(cmsMonacoBrokenCurveType, Curve), &SupportedTagTypes[28] }, -{TYPE_HANDLER(cmsSigProfileSequenceIdType, ProfileSequenceId), &SupportedTagTypes[29] }, -{TYPE_HANDLER(cmsSigDictType, Dictionary), &SupportedTagTypes[30] }, -{TYPE_HANDLER(cmsSigVcgtType, vcgt), NULL } -}; - - -_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, TagTypePlugin); -} - -cmsBool _cmsRegisterMultiProcessElementPlugin(cmsContext id, cmsPluginBase* Data) -{ - return RegisterTypesPlugin(id, Data,MPEPlugin); -} - - -// Wrapper for tag types -cmsTagTypeHandler* _cmsGetTagTypeHandler(cmsContext ContextID, cmsTagTypeSignature sig) -{ - _cmsTagTypePluginChunkType* ctx = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(ContextID, TagTypePlugin); - - return GetHandler(sig, ctx->TagTypes, SupportedTagTypes); -} - -// ******************************************************************************** -// Tag support main routines -// ******************************************************************************** - -typedef struct _cmsTagLinkedList_st { - - cmsTagSignature Signature; - cmsTagDescriptor Descriptor; - struct _cmsTagLinkedList_st* Next; - -} _cmsTagLinkedList; - -// This is the list of built-in tags -static _cmsTagLinkedList SupportedTags[] = { - - { cmsSigAToB0Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutAtoBType, cmsSigLut8Type}, DecideLUTtypeA2B}, &SupportedTags[1]}, - { cmsSigAToB1Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutAtoBType, cmsSigLut8Type}, DecideLUTtypeA2B}, &SupportedTags[2]}, - { cmsSigAToB2Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutAtoBType, cmsSigLut8Type}, DecideLUTtypeA2B}, &SupportedTags[3]}, - { cmsSigBToA0Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type}, DecideLUTtypeB2A}, &SupportedTags[4]}, - { cmsSigBToA1Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type}, DecideLUTtypeB2A}, &SupportedTags[5]}, - { cmsSigBToA2Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type}, DecideLUTtypeB2A}, &SupportedTags[6]}, - - // Allow corbis and its broken XYZ type - { cmsSigRedColorantTag, { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, DecideXYZtype}, &SupportedTags[7]}, - { cmsSigGreenColorantTag, { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, DecideXYZtype}, &SupportedTags[8]}, - { cmsSigBlueColorantTag, { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, DecideXYZtype}, &SupportedTags[9]}, - - { cmsSigRedTRCTag, { 1, 3, { cmsSigCurveType, cmsSigParametricCurveType, cmsMonacoBrokenCurveType }, DecideCurveType}, &SupportedTags[10]}, - { cmsSigGreenTRCTag, { 1, 3, { cmsSigCurveType, cmsSigParametricCurveType, cmsMonacoBrokenCurveType }, DecideCurveType}, &SupportedTags[11]}, - { cmsSigBlueTRCTag, { 1, 3, { cmsSigCurveType, cmsSigParametricCurveType, cmsMonacoBrokenCurveType }, DecideCurveType}, &SupportedTags[12]}, - - { cmsSigCalibrationDateTimeTag, { 1, 1, { cmsSigDateTimeType }, NULL}, &SupportedTags[13]}, - { cmsSigCharTargetTag, { 1, 1, { cmsSigTextType }, NULL}, &SupportedTags[14]}, - - { cmsSigChromaticAdaptationTag, { 9, 1, { cmsSigS15Fixed16ArrayType }, NULL}, &SupportedTags[15]}, - { cmsSigChromaticityTag, { 1, 1, { cmsSigChromaticityType }, NULL}, &SupportedTags[16]}, - { cmsSigColorantOrderTag, { 1, 1, { cmsSigColorantOrderType }, NULL}, &SupportedTags[17]}, - { cmsSigColorantTableTag, { 1, 1, { cmsSigColorantTableType }, NULL}, &SupportedTags[18]}, - { cmsSigColorantTableOutTag, { 1, 1, { cmsSigColorantTableType }, NULL}, &SupportedTags[19]}, - - { cmsSigCopyrightTag, { 1, 3, { cmsSigTextType, cmsSigMultiLocalizedUnicodeType, cmsSigTextDescriptionType}, DecideTextType}, &SupportedTags[20]}, - { cmsSigDateTimeTag, { 1, 1, { cmsSigDateTimeType }, NULL}, &SupportedTags[21]}, - - { cmsSigDeviceMfgDescTag, { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[22]}, - { cmsSigDeviceModelDescTag, { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[23]}, - - { cmsSigGamutTag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[24]}, - - { cmsSigGrayTRCTag, { 1, 2, { cmsSigCurveType, cmsSigParametricCurveType }, DecideCurveType}, &SupportedTags[25]}, - { cmsSigLuminanceTag, { 1, 1, { cmsSigXYZType }, NULL}, &SupportedTags[26]}, - - { cmsSigMediaBlackPointTag, { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, NULL}, &SupportedTags[27]}, - { cmsSigMediaWhitePointTag, { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, NULL}, &SupportedTags[28]}, - - { cmsSigNamedColor2Tag, { 1, 1, { cmsSigNamedColor2Type }, NULL}, &SupportedTags[29]}, - - { cmsSigPreview0Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[30]}, - { cmsSigPreview1Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[31]}, - { cmsSigPreview2Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[32]}, - - { cmsSigProfileDescriptionTag, { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[33]}, - { cmsSigProfileSequenceDescTag, { 1, 1, { cmsSigProfileSequenceDescType }, NULL}, &SupportedTags[34]}, - { cmsSigTechnologyTag, { 1, 1, { cmsSigSignatureType }, NULL}, &SupportedTags[35]}, - - { cmsSigColorimetricIntentImageStateTag, { 1, 1, { cmsSigSignatureType }, NULL}, &SupportedTags[36]}, - { cmsSigPerceptualRenderingIntentGamutTag, { 1, 1, { cmsSigSignatureType }, NULL}, &SupportedTags[37]}, - { cmsSigSaturationRenderingIntentGamutTag, { 1, 1, { cmsSigSignatureType }, NULL}, &SupportedTags[38]}, - - { cmsSigMeasurementTag, { 1, 1, { cmsSigMeasurementType }, NULL}, &SupportedTags[39]}, - - { cmsSigPs2CRD0Tag, { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[40]}, - { cmsSigPs2CRD1Tag, { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[41]}, - { cmsSigPs2CRD2Tag, { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[42]}, - { cmsSigPs2CRD3Tag, { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[43]}, - { cmsSigPs2CSATag, { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[44]}, - { cmsSigPs2RenderingIntentTag, { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[45]}, - - { cmsSigViewingCondDescTag, { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[46]}, - - { cmsSigUcrBgTag, { 1, 1, { cmsSigUcrBgType}, NULL}, &SupportedTags[47]}, - { cmsSigCrdInfoTag, { 1, 1, { cmsSigCrdInfoType}, NULL}, &SupportedTags[48]}, - - { cmsSigDToB0Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[49]}, - { cmsSigDToB1Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[50]}, - { cmsSigDToB2Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[51]}, - { cmsSigDToB3Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[52]}, - { cmsSigBToD0Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[53]}, - { cmsSigBToD1Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[54]}, - { cmsSigBToD2Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[55]}, - { cmsSigBToD3Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[56]}, - - { cmsSigScreeningDescTag, { 1, 1, { cmsSigTextDescriptionType }, NULL}, &SupportedTags[57]}, - { cmsSigViewingConditionsTag, { 1, 1, { cmsSigViewingConditionsType }, NULL}, &SupportedTags[58]}, - - { cmsSigScreeningTag, { 1, 1, { cmsSigScreeningType}, NULL }, &SupportedTags[59]}, - { cmsSigVcgtTag, { 1, 1, { cmsSigVcgtType}, NULL }, &SupportedTags[60]}, - { cmsSigMetaTag, { 1, 1, { cmsSigDictType}, NULL }, &SupportedTags[61]}, - { cmsSigProfileSequenceIdTag, { 1, 1, { cmsSigProfileSequenceIdType}, NULL }, &SupportedTags[62]}, - { cmsSigProfileDescriptionMLTag,{ 1, 1, { cmsSigMultiLocalizedUnicodeType}, NULL}, NULL} - - -}; - -/* - Not supported Why - ======================= ========================================= - cmsSigOutputResponseTag ==> WARNING, POSSIBLE PATENT ON THIS SUBJECT! - cmsSigNamedColorTag ==> Deprecated - cmsSigDataTag ==> Ancient, unused - cmsSigDeviceSettingsTag ==> Deprecated, useless -*/ - - -_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) -{ - _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) { - - _cmsTagLinkedList *newEntry = ( _cmsTagLinkedList *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTagLinkedList)); - - 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)); - } - -} - -cmsBool _cmsRegisterTagPlugin(cmsContext id, cmsPluginBase* Data) -{ - cmsPluginTag* Plugin = (cmsPluginTag*) Data; - _cmsTagLinkedList *pt; - _cmsTagPluginChunkType* TagPluginChunk = ( _cmsTagPluginChunkType*) _cmsContextGetClientChunk(id, TagPlugin); - - if (Data == NULL) { - - TagPluginChunk->Tag = NULL; - return TRUE; - } - - pt = (_cmsTagLinkedList*) _cmsPluginMalloc(id, sizeof(_cmsTagLinkedList)); - if (pt == NULL) return FALSE; - - pt ->Signature = Plugin ->Signature; - pt ->Descriptor = Plugin ->Descriptor; - pt ->Next = TagPluginChunk ->Tag; - - TagPluginChunk ->Tag = pt; - - return TRUE; -} - -// Return a descriptor for a given tag or NULL -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; - pt = pt ->Next) { - - if (sig == pt -> Signature) return &pt ->Descriptor; - } - - return NULL; -} diff --git a/third_party/lcms2-2.6/src/cmsvirt.c b/third_party/lcms2-2.6/src/cmsvirt.c deleted file mode 100644 index d19ace1651..0000000000 --- a/third_party/lcms2-2.6/src/cmsvirt.c +++ /dev/null @@ -1,1194 +0,0 @@ -//--------------------------------------------------------------------------------- -// -// 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 "lcms2_internal.h" - -// Virtual (built-in) profiles -// ----------------------------------------------------------------------------------- - -static -cmsBool SetTextTags(cmsHPROFILE hProfile, const wchar_t* Description) -{ - cmsMLU *DescriptionMLU, *CopyrightMLU; - cmsBool rc = FALSE; - cmsContext ContextID = cmsGetProfileContextID(hProfile); - - DescriptionMLU = cmsMLUalloc(ContextID, 1); - CopyrightMLU = cmsMLUalloc(ContextID, 1); - - if (DescriptionMLU == NULL || CopyrightMLU == NULL) goto Error; - - if (!cmsMLUsetWide(DescriptionMLU, "en", "US", Description)) goto Error; - if (!cmsMLUsetWide(CopyrightMLU, "en", "US", L"No copyright, use freely")) goto Error; - - if (!cmsWriteTag(hProfile, cmsSigProfileDescriptionTag, DescriptionMLU)) goto Error; - if (!cmsWriteTag(hProfile, cmsSigCopyrightTag, CopyrightMLU)) goto Error; - - rc = TRUE; - -Error: - - if (DescriptionMLU) - cmsMLUfree(DescriptionMLU); - if (CopyrightMLU) - cmsMLUfree(CopyrightMLU); - return rc; -} - - -static -cmsBool SetSeqDescTag(cmsHPROFILE hProfile, const char* Model) -{ - cmsBool rc = FALSE; - cmsContext ContextID = cmsGetProfileContextID(hProfile); - cmsSEQ* Seq = cmsAllocProfileSequenceDescription(ContextID, 1); - - if (Seq == NULL) return FALSE; - - Seq->seq[0].deviceMfg = (cmsSignature) 0; - Seq->seq[0].deviceModel = (cmsSignature) 0; - -#ifdef CMS_DONT_USE_INT64 - Seq->seq[0].attributes[0] = 0; - Seq->seq[0].attributes[1] = 0; -#else - Seq->seq[0].attributes = 0; -#endif - - Seq->seq[0].technology = (cmsTechnologySignature) 0; - - cmsMLUsetASCII( Seq->seq[0].Manufacturer, cmsNoLanguage, cmsNoCountry, "Little CMS"); - cmsMLUsetASCII( Seq->seq[0].Model, cmsNoLanguage, cmsNoCountry, Model); - - if (!_cmsWriteProfileSequence(hProfile, Seq)) goto Error; - - rc = TRUE; - -Error: - if (Seq) - cmsFreeProfileSequenceDescription(Seq); - - return rc; -} - - - -// This function creates a profile based on White point, primaries and -// transfer functions. -cmsHPROFILE CMSEXPORT cmsCreateRGBProfileTHR(cmsContext ContextID, - const cmsCIExyY* WhitePoint, - const cmsCIExyYTRIPLE* Primaries, - cmsToneCurve* const TransferFunction[3]) -{ - cmsHPROFILE hICC; - cmsMAT3 MColorants; - cmsCIEXYZTRIPLE Colorants; - cmsCIExyY MaxWhite; - cmsMAT3 CHAD; - cmsCIEXYZ WhitePointXYZ; - - hICC = cmsCreateProfilePlaceholder(ContextID); - if (!hICC) // can't allocate - return NULL; - - cmsSetProfileVersion(hICC, 4.3); - - cmsSetDeviceClass(hICC, cmsSigDisplayClass); - cmsSetColorSpace(hICC, cmsSigRgbData); - cmsSetPCS(hICC, cmsSigXYZData); - - cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL); - - - // Implement profile using following tags: - // - // 1 cmsSigProfileDescriptionTag - // 2 cmsSigMediaWhitePointTag - // 3 cmsSigRedColorantTag - // 4 cmsSigGreenColorantTag - // 5 cmsSigBlueColorantTag - // 6 cmsSigRedTRCTag - // 7 cmsSigGreenTRCTag - // 8 cmsSigBlueTRCTag - // 9 Chromatic adaptation Tag - // This conforms a standard RGB DisplayProfile as says ICC, and then I add (As per addendum II) - // 10 cmsSigChromaticityTag - - - if (!SetTextTags(hICC, L"RGB built-in")) goto Error; - - if (WhitePoint) { - - if (!cmsWriteTag(hICC, cmsSigMediaWhitePointTag, cmsD50_XYZ())) goto Error; - - cmsxyY2XYZ(&WhitePointXYZ, WhitePoint); - _cmsAdaptationMatrix(&CHAD, NULL, &WhitePointXYZ, cmsD50_XYZ()); - - // This is a V4 tag, but many CMM does read and understand it no matter which version - if (!cmsWriteTag(hICC, cmsSigChromaticAdaptationTag, (void*) &CHAD)) goto Error; - } - - if (WhitePoint && Primaries) { - - MaxWhite.x = WhitePoint -> x; - MaxWhite.y = WhitePoint -> y; - MaxWhite.Y = 1.0; - - if (!_cmsBuildRGB2XYZtransferMatrix(&MColorants, &MaxWhite, Primaries)) goto Error; - - Colorants.Red.X = MColorants.v[0].n[0]; - Colorants.Red.Y = MColorants.v[1].n[0]; - Colorants.Red.Z = MColorants.v[2].n[0]; - - Colorants.Green.X = MColorants.v[0].n[1]; - Colorants.Green.Y = MColorants.v[1].n[1]; - Colorants.Green.Z = MColorants.v[2].n[1]; - - Colorants.Blue.X = MColorants.v[0].n[2]; - Colorants.Blue.Y = MColorants.v[1].n[2]; - Colorants.Blue.Z = MColorants.v[2].n[2]; - - if (!cmsWriteTag(hICC, cmsSigRedColorantTag, (void*) &Colorants.Red)) goto Error; - if (!cmsWriteTag(hICC, cmsSigBlueColorantTag, (void*) &Colorants.Blue)) goto Error; - if (!cmsWriteTag(hICC, cmsSigGreenColorantTag, (void*) &Colorants.Green)) goto Error; - } - - - if (TransferFunction) { - - // Tries to minimize space. Thanks to Richard Hughes for this nice idea - if (!cmsWriteTag(hICC, cmsSigRedTRCTag, (void*) TransferFunction[0])) goto Error; - - if (TransferFunction[1] == TransferFunction[0]) { - - if (!cmsLinkTag (hICC, cmsSigGreenTRCTag, cmsSigRedTRCTag)) goto Error; - - } else { - - if (!cmsWriteTag(hICC, cmsSigGreenTRCTag, (void*) TransferFunction[1])) goto Error; - } - - if (TransferFunction[2] == TransferFunction[0]) { - - if (!cmsLinkTag (hICC, cmsSigBlueTRCTag, cmsSigRedTRCTag)) goto Error; - - } else { - - if (!cmsWriteTag(hICC, cmsSigBlueTRCTag, (void*) TransferFunction[2])) goto Error; - } - } - - if (Primaries) { - if (!cmsWriteTag(hICC, cmsSigChromaticityTag, (void*) Primaries)) goto Error; - } - - - return hICC; - -Error: - if (hICC) - cmsCloseProfile(hICC); - return NULL; -} - -cmsHPROFILE CMSEXPORT cmsCreateRGBProfile(const cmsCIExyY* WhitePoint, - const cmsCIExyYTRIPLE* Primaries, - cmsToneCurve* const TransferFunction[3]) -{ - return cmsCreateRGBProfileTHR(NULL, WhitePoint, Primaries, TransferFunction); -} - - - -// This function creates a profile based on White point and transfer function. -cmsHPROFILE CMSEXPORT cmsCreateGrayProfileTHR(cmsContext ContextID, - const cmsCIExyY* WhitePoint, - const cmsToneCurve* TransferFunction) -{ - cmsHPROFILE hICC; - cmsCIEXYZ tmp; - - hICC = cmsCreateProfilePlaceholder(ContextID); - if (!hICC) // can't allocate - return NULL; - - cmsSetProfileVersion(hICC, 4.3); - - cmsSetDeviceClass(hICC, cmsSigDisplayClass); - cmsSetColorSpace(hICC, cmsSigGrayData); - cmsSetPCS(hICC, cmsSigXYZData); - cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL); - - - // Implement profile using following tags: - // - // 1 cmsSigProfileDescriptionTag - // 2 cmsSigMediaWhitePointTag - // 3 cmsSigGrayTRCTag - - // This conforms a standard Gray DisplayProfile - - // Fill-in the tags - - if (!SetTextTags(hICC, L"gray built-in")) goto Error; - - - if (WhitePoint) { - - cmsxyY2XYZ(&tmp, WhitePoint); - if (!cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*) &tmp)) goto Error; - } - - if (TransferFunction) { - - if (!cmsWriteTag(hICC, cmsSigGrayTRCTag, (void*) TransferFunction)) goto Error; - } - - return hICC; - -Error: - if (hICC) - cmsCloseProfile(hICC); - return NULL; -} - - - -cmsHPROFILE CMSEXPORT cmsCreateGrayProfile(const cmsCIExyY* WhitePoint, - const cmsToneCurve* TransferFunction) -{ - return cmsCreateGrayProfileTHR(NULL, WhitePoint, TransferFunction); -} - -// This is a devicelink operating in the target colorspace with as many transfer functions as components - -cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLinkTHR(cmsContext ContextID, - cmsColorSpaceSignature ColorSpace, - cmsToneCurve* const TransferFunctions[]) -{ - cmsHPROFILE hICC; - cmsPipeline* Pipeline; - int nChannels; - - hICC = cmsCreateProfilePlaceholder(ContextID); - if (!hICC) - return NULL; - - cmsSetProfileVersion(hICC, 4.3); - - cmsSetDeviceClass(hICC, cmsSigLinkClass); - cmsSetColorSpace(hICC, ColorSpace); - cmsSetPCS(hICC, ColorSpace); - - cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL); - - // Set up channels - nChannels = cmsChannelsOf(ColorSpace); - - // Creates a Pipeline with prelinearization step only - Pipeline = cmsPipelineAlloc(ContextID, nChannels, nChannels); - if (Pipeline == NULL) goto Error; - - - // Copy tables to Pipeline - if (!cmsPipelineInsertStage(Pipeline, cmsAT_BEGIN, cmsStageAllocToneCurves(ContextID, nChannels, TransferFunctions))) - goto Error; - - // Create tags - if (!SetTextTags(hICC, L"Linearization built-in")) goto Error; - if (!cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) Pipeline)) goto Error; - if (!SetSeqDescTag(hICC, "Linearization built-in")) goto Error; - - // Pipeline is already on virtual profile - cmsPipelineFree(Pipeline); - - // Ok, done - return hICC; - -Error: - cmsPipelineFree(Pipeline); - if (hICC) - cmsCloseProfile(hICC); - - - return NULL; -} - -cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLink(cmsColorSpaceSignature ColorSpace, - cmsToneCurve* const TransferFunctions[]) -{ - return cmsCreateLinearizationDeviceLinkTHR(NULL, ColorSpace, TransferFunctions); -} - -// Ink-limiting algorithm -// -// Sum = C + M + Y + K -// If Sum > InkLimit -// Ratio= 1 - (Sum - InkLimit) / (C + M + Y) -// if Ratio <0 -// Ratio=0 -// endif -// Else -// Ratio=1 -// endif -// -// C = Ratio * C -// M = Ratio * M -// Y = Ratio * Y -// K: Does not change - -static -int InkLimitingSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo) -{ - cmsFloat64Number InkLimit = *(cmsFloat64Number *) Cargo; - cmsFloat64Number SumCMY, SumCMYK, Ratio; - - InkLimit = (InkLimit * 655.35); - - SumCMY = In[0] + In[1] + In[2]; - SumCMYK = SumCMY + In[3]; - - if (SumCMYK > InkLimit) { - - Ratio = 1 - ((SumCMYK - InkLimit) / SumCMY); - if (Ratio < 0) - Ratio = 0; - } - else Ratio = 1; - - Out[0] = _cmsQuickSaturateWord(In[0] * Ratio); // C - Out[1] = _cmsQuickSaturateWord(In[1] * Ratio); // M - Out[2] = _cmsQuickSaturateWord(In[2] * Ratio); // Y - - Out[3] = In[3]; // K (untouched) - - return TRUE; -} - -// This is a devicelink operating in CMYK for ink-limiting - -cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLinkTHR(cmsContext ContextID, - cmsColorSpaceSignature ColorSpace, - cmsFloat64Number Limit) -{ - cmsHPROFILE hICC; - cmsPipeline* LUT; - cmsStage* CLUT; - int nChannels; - - if (ColorSpace != cmsSigCmykData) { - cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "InkLimiting: Only CMYK currently supported"); - return NULL; - } - - if (Limit < 0.0 || Limit > 400) { - - cmsSignalError(ContextID, cmsERROR_RANGE, "InkLimiting: Limit should be between 0..400"); - if (Limit < 0) Limit = 0; - if (Limit > 400) Limit = 400; - - } - - hICC = cmsCreateProfilePlaceholder(ContextID); - if (!hICC) // can't allocate - return NULL; - - cmsSetProfileVersion(hICC, 4.3); - - cmsSetDeviceClass(hICC, cmsSigLinkClass); - cmsSetColorSpace(hICC, ColorSpace); - cmsSetPCS(hICC, ColorSpace); - - cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL); - - - // Creates a Pipeline with 3D grid only - LUT = cmsPipelineAlloc(ContextID, 4, 4); - if (LUT == NULL) goto Error; - - - nChannels = cmsChannelsOf(ColorSpace); - - CLUT = cmsStageAllocCLut16bit(ContextID, 17, nChannels, nChannels, NULL); - if (CLUT == NULL) goto Error; - - if (!cmsStageSampleCLut16bit(CLUT, InkLimitingSampler, (void*) &Limit, 0)) goto Error; - - if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, nChannels)) || - !cmsPipelineInsertStage(LUT, cmsAT_END, CLUT) || - !cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, nChannels))) - goto Error; - - // Create tags - if (!SetTextTags(hICC, L"ink-limiting built-in")) goto Error; - - if (!cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) LUT)) goto Error; - if (!SetSeqDescTag(hICC, "ink-limiting built-in")) goto Error; - - // cmsPipeline is already on virtual profile - cmsPipelineFree(LUT); - - // Ok, done - return hICC; - -Error: - if (LUT != NULL) - cmsPipelineFree(LUT); - - if (hICC != NULL) - cmsCloseProfile(hICC); - - return NULL; -} - -cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLink(cmsColorSpaceSignature ColorSpace, cmsFloat64Number Limit) -{ - return cmsCreateInkLimitingDeviceLinkTHR(NULL, ColorSpace, Limit); -} - - -// Creates a fake Lab identity. -cmsHPROFILE CMSEXPORT cmsCreateLab2ProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint) -{ - cmsHPROFILE hProfile; - cmsPipeline* LUT = NULL; - - hProfile = cmsCreateRGBProfileTHR(ContextID, WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL); - if (hProfile == NULL) return NULL; - - cmsSetProfileVersion(hProfile, 2.1); - - cmsSetDeviceClass(hProfile, cmsSigAbstractClass); - cmsSetColorSpace(hProfile, cmsSigLabData); - cmsSetPCS(hProfile, cmsSigLabData); - - if (!SetTextTags(hProfile, L"Lab identity built-in")) return NULL; - - // An identity LUT is all we need - LUT = cmsPipelineAlloc(ContextID, 3, 3); - if (LUT == NULL) goto Error; - - if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCLut(ContextID, 3))) - goto Error; - - if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error; - cmsPipelineFree(LUT); - - return hProfile; - -Error: - - if (LUT != NULL) - cmsPipelineFree(LUT); - - if (hProfile != NULL) - cmsCloseProfile(hProfile); - - return NULL; -} - - -cmsHPROFILE CMSEXPORT cmsCreateLab2Profile(const cmsCIExyY* WhitePoint) -{ - return cmsCreateLab2ProfileTHR(NULL, WhitePoint); -} - - -// Creates a fake Lab V4 identity. -cmsHPROFILE CMSEXPORT cmsCreateLab4ProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint) -{ - cmsHPROFILE hProfile; - cmsPipeline* LUT = NULL; - - hProfile = cmsCreateRGBProfileTHR(ContextID, WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL); - if (hProfile == NULL) return NULL; - - cmsSetProfileVersion(hProfile, 4.3); - - cmsSetDeviceClass(hProfile, cmsSigAbstractClass); - cmsSetColorSpace(hProfile, cmsSigLabData); - cmsSetPCS(hProfile, cmsSigLabData); - - if (!SetTextTags(hProfile, L"Lab identity built-in")) goto Error; - - // An empty LUTs is all we need - LUT = cmsPipelineAlloc(ContextID, 3, 3); - if (LUT == NULL) goto Error; - - if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3))) - goto Error; - - if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error; - cmsPipelineFree(LUT); - - return hProfile; - -Error: - - if (LUT != NULL) - cmsPipelineFree(LUT); - - if (hProfile != NULL) - cmsCloseProfile(hProfile); - - return NULL; -} - -cmsHPROFILE CMSEXPORT cmsCreateLab4Profile(const cmsCIExyY* WhitePoint) -{ - return cmsCreateLab4ProfileTHR(NULL, WhitePoint); -} - - -// Creates a fake XYZ identity -cmsHPROFILE CMSEXPORT cmsCreateXYZProfileTHR(cmsContext ContextID) -{ - cmsHPROFILE hProfile; - cmsPipeline* LUT = NULL; - - hProfile = cmsCreateRGBProfileTHR(ContextID, cmsD50_xyY(), NULL, NULL); - if (hProfile == NULL) return NULL; - - cmsSetProfileVersion(hProfile, 4.3); - - cmsSetDeviceClass(hProfile, cmsSigAbstractClass); - cmsSetColorSpace(hProfile, cmsSigXYZData); - cmsSetPCS(hProfile, cmsSigXYZData); - - if (!SetTextTags(hProfile, L"XYZ identity built-in")) goto Error; - - // An identity LUT is all we need - LUT = cmsPipelineAlloc(ContextID, 3, 3); - if (LUT == NULL) goto Error; - - if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, 3))) - goto Error; - - if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, LUT)) goto Error; - cmsPipelineFree(LUT); - - return hProfile; - -Error: - - if (LUT != NULL) - cmsPipelineFree(LUT); - - if (hProfile != NULL) - cmsCloseProfile(hProfile); - - return NULL; -} - - -cmsHPROFILE CMSEXPORT cmsCreateXYZProfile(void) -{ - return cmsCreateXYZProfileTHR(NULL); -} - - -//sRGB Curves are defined by: -// -//If R'sRGB,G'sRGB, B'sRGB < 0.04045 -// -// R = R'sRGB / 12.92 -// G = G'sRGB / 12.92 -// B = B'sRGB / 12.92 -// -// -//else if R'sRGB,G'sRGB, B'sRGB >= 0.04045 -// -// R = ((R'sRGB + 0.055) / 1.055)^2.4 -// G = ((G'sRGB + 0.055) / 1.055)^2.4 -// B = ((B'sRGB + 0.055) / 1.055)^2.4 - -static -cmsToneCurve* Build_sRGBGamma(cmsContext ContextID) -{ - cmsFloat64Number Parameters[5]; - - Parameters[0] = 2.4; - Parameters[1] = 1. / 1.055; - Parameters[2] = 0.055 / 1.055; - Parameters[3] = 1. / 12.92; - Parameters[4] = 0.04045; - - return cmsBuildParametricToneCurve(ContextID, 4, Parameters); -} - -// Create the ICC virtual profile for sRGB space -cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfileTHR(cmsContext ContextID) -{ - cmsCIExyY D65; - cmsCIExyYTRIPLE Rec709Primaries = { - {0.6400, 0.3300, 1.0}, - {0.3000, 0.6000, 1.0}, - {0.1500, 0.0600, 1.0} - }; - cmsToneCurve* Gamma22[3]; - cmsHPROFILE hsRGB; - - cmsWhitePointFromTemp(&D65, 6504); - Gamma22[0] = Gamma22[1] = Gamma22[2] = Build_sRGBGamma(ContextID); - if (Gamma22[0] == NULL) return NULL; - - hsRGB = cmsCreateRGBProfileTHR(ContextID, &D65, &Rec709Primaries, Gamma22); - cmsFreeToneCurve(Gamma22[0]); - if (hsRGB == NULL) return NULL; - - if (!SetTextTags(hsRGB, L"sRGB built-in")) { - cmsCloseProfile(hsRGB); - return NULL; - } - - return hsRGB; -} - -cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfile(void) -{ - return cmsCreate_sRGBProfileTHR(NULL); -} - - - -typedef struct { - cmsFloat64Number Brightness; - cmsFloat64Number Contrast; - cmsFloat64Number Hue; - cmsFloat64Number Saturation; - cmsCIEXYZ WPsrc, WPdest; - -} BCHSWADJUSTS, *LPBCHSWADJUSTS; - - -static -int bchswSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo) -{ - cmsCIELab LabIn, LabOut; - cmsCIELCh LChIn, LChOut; - cmsCIEXYZ XYZ; - LPBCHSWADJUSTS bchsw = (LPBCHSWADJUSTS) Cargo; - - - cmsLabEncoded2Float(&LabIn, In); - - - cmsLab2LCh(&LChIn, &LabIn); - - // Do some adjusts on LCh - - LChOut.L = LChIn.L * bchsw ->Contrast + bchsw ->Brightness; - LChOut.C = LChIn.C + bchsw -> Saturation; - LChOut.h = LChIn.h + bchsw -> Hue; - - - cmsLCh2Lab(&LabOut, &LChOut); - - // Move white point in Lab - - cmsLab2XYZ(&bchsw ->WPsrc, &XYZ, &LabOut); - cmsXYZ2Lab(&bchsw ->WPdest, &LabOut, &XYZ); - - // Back to encoded - - cmsFloat2LabEncoded(Out, &LabOut); - - return TRUE; -} - - -// Creates an abstract profile operating in Lab space for Brightness, -// contrast, Saturation and white point displacement - -cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfileTHR(cmsContext ContextID, - int nLUTPoints, - cmsFloat64Number Bright, - cmsFloat64Number Contrast, - cmsFloat64Number Hue, - cmsFloat64Number Saturation, - int TempSrc, - int TempDest) -{ - cmsHPROFILE hICC; - cmsPipeline* Pipeline; - BCHSWADJUSTS bchsw; - cmsCIExyY WhitePnt; - cmsStage* CLUT; - cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS]; - int i; - - bchsw.Brightness = Bright; - bchsw.Contrast = Contrast; - bchsw.Hue = Hue; - bchsw.Saturation = Saturation; - - cmsWhitePointFromTemp(&WhitePnt, TempSrc ); - cmsxyY2XYZ(&bchsw.WPsrc, &WhitePnt); - - cmsWhitePointFromTemp(&WhitePnt, TempDest); - cmsxyY2XYZ(&bchsw.WPdest, &WhitePnt); - - hICC = cmsCreateProfilePlaceholder(ContextID); - if (!hICC) // can't allocate - return NULL; - - - cmsSetDeviceClass(hICC, cmsSigAbstractClass); - cmsSetColorSpace(hICC, cmsSigLabData); - cmsSetPCS(hICC, cmsSigLabData); - - cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL); - - // Creates a Pipeline with 3D grid only - Pipeline = cmsPipelineAlloc(ContextID, 3, 3); - if (Pipeline == NULL) { - cmsCloseProfile(hICC); - return NULL; - } - - for (i=0; i < MAX_INPUT_DIMENSIONS; i++) Dimensions[i] = nLUTPoints; - CLUT = cmsStageAllocCLut16bitGranular(ContextID, Dimensions, 3, 3, NULL); - if (CLUT == NULL) return NULL; - - - if (!cmsStageSampleCLut16bit(CLUT, bchswSampler, (void*) &bchsw, 0)) { - - // Shouldn't reach here - goto Error; - } - - if (!cmsPipelineInsertStage(Pipeline, cmsAT_END, CLUT)) { - goto Error; - } - - // Create tags - if (!SetTextTags(hICC, L"BCHS built-in")) return NULL; - - cmsWriteTag(hICC, cmsSigMediaWhitePointTag, (void*) cmsD50_XYZ()); - - cmsWriteTag(hICC, cmsSigAToB0Tag, (void*) Pipeline); - - // Pipeline is already on virtual profile - cmsPipelineFree(Pipeline); - - // Ok, done - return hICC; - -Error: - cmsPipelineFree(Pipeline); - cmsCloseProfile(hICC); - return NULL; -} - - -CMSAPI cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfile(int nLUTPoints, - cmsFloat64Number Bright, - cmsFloat64Number Contrast, - cmsFloat64Number Hue, - cmsFloat64Number Saturation, - int TempSrc, - int TempDest) -{ - return cmsCreateBCHSWabstractProfileTHR(NULL, nLUTPoints, Bright, Contrast, Hue, Saturation, TempSrc, TempDest); -} - - -// Creates a fake NULL profile. This profile return 1 channel as always 0. -// Is useful only for gamut checking tricks -cmsHPROFILE CMSEXPORT cmsCreateNULLProfileTHR(cmsContext ContextID) -{ - cmsHPROFILE hProfile; - cmsPipeline* LUT = NULL; - cmsStage* PostLin; - cmsToneCurve* EmptyTab; - cmsUInt16Number Zero[2] = { 0, 0 }; - - hProfile = cmsCreateProfilePlaceholder(ContextID); - if (!hProfile) // can't allocate - return NULL; - - cmsSetProfileVersion(hProfile, 4.3); - - if (!SetTextTags(hProfile, L"NULL profile built-in")) goto Error; - - - - cmsSetDeviceClass(hProfile, cmsSigOutputClass); - cmsSetColorSpace(hProfile, cmsSigGrayData); - cmsSetPCS(hProfile, cmsSigLabData); - - // An empty LUTs is all we need - LUT = cmsPipelineAlloc(ContextID, 1, 1); - if (LUT == NULL) goto Error; - - EmptyTab = cmsBuildTabulatedToneCurve16(ContextID, 2, Zero); - PostLin = cmsStageAllocToneCurves(ContextID, 1, &EmptyTab); - cmsFreeToneCurve(EmptyTab); - - if (!cmsPipelineInsertStage(LUT, cmsAT_END, PostLin)) - goto Error; - - if (!cmsWriteTag(hProfile, cmsSigBToA0Tag, (void*) LUT)) goto Error; - if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, cmsD50_XYZ())) goto Error; - - cmsPipelineFree(LUT); - return hProfile; - -Error: - - if (LUT != NULL) - cmsPipelineFree(LUT); - - if (hProfile != NULL) - cmsCloseProfile(hProfile); - - return NULL; -} - -cmsHPROFILE CMSEXPORT cmsCreateNULLProfile(void) -{ - return cmsCreateNULLProfileTHR(NULL); -} - - -static -int IsPCS(cmsColorSpaceSignature ColorSpace) -{ - return (ColorSpace == cmsSigXYZData || - ColorSpace == cmsSigLabData); -} - - -static -void FixColorSpaces(cmsHPROFILE hProfile, - cmsColorSpaceSignature ColorSpace, - cmsColorSpaceSignature PCS, - cmsUInt32Number dwFlags) -{ - if (dwFlags & cmsFLAGS_GUESSDEVICECLASS) { - - if (IsPCS(ColorSpace) && IsPCS(PCS)) { - - cmsSetDeviceClass(hProfile, cmsSigAbstractClass); - cmsSetColorSpace(hProfile, ColorSpace); - cmsSetPCS(hProfile, PCS); - return; - } - - if (IsPCS(ColorSpace) && !IsPCS(PCS)) { - - cmsSetDeviceClass(hProfile, cmsSigOutputClass); - cmsSetPCS(hProfile, ColorSpace); - cmsSetColorSpace(hProfile, PCS); - return; - } - - if (IsPCS(PCS) && !IsPCS(ColorSpace)) { - - cmsSetDeviceClass(hProfile, cmsSigInputClass); - cmsSetColorSpace(hProfile, ColorSpace); - cmsSetPCS(hProfile, PCS); - return; - } - } - - cmsSetDeviceClass(hProfile, cmsSigLinkClass); - cmsSetColorSpace(hProfile, ColorSpace); - cmsSetPCS(hProfile, PCS); -} - - - -// This function creates a named color profile dumping all the contents of transform to a single profile -// In this way, LittleCMS may be used to "group" several named color databases into a single profile. -// It has, however, several minor limitations. PCS is always Lab, which is not very critic since this -// is the normal PCS for named color profiles. -static -cmsHPROFILE CreateNamedColorDevicelink(cmsHTRANSFORM xform) -{ - _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform; - cmsHPROFILE hICC = NULL; - int i, nColors; - cmsNAMEDCOLORLIST *nc2 = NULL, *Original = NULL; - - // Create an empty placeholder - hICC = cmsCreateProfilePlaceholder(v->ContextID); - if (hICC == NULL) return NULL; - - // Critical information - cmsSetDeviceClass(hICC, cmsSigNamedColorClass); - cmsSetColorSpace(hICC, v ->ExitColorSpace); - cmsSetPCS(hICC, cmsSigLabData); - - // Tag profile with information - if (!SetTextTags(hICC, L"Named color devicelink")) goto Error; - - Original = cmsGetNamedColorList(xform); - if (Original == NULL) goto Error; - - nColors = cmsNamedColorCount(Original); - nc2 = cmsDupNamedColorList(Original); - if (nc2 == NULL) goto Error; - - // Colorant count now depends on the output space - nc2 ->ColorantCount = cmsPipelineOutputChannels(v ->Lut); - - // Make sure we have proper formatters - cmsChangeBuffersFormat(xform, TYPE_NAMED_COLOR_INDEX, - FLOAT_SH(0) | COLORSPACE_SH(_cmsLCMScolorSpace(v ->ExitColorSpace)) - | BYTES_SH(2) | CHANNELS_SH(cmsChannelsOf(v ->ExitColorSpace))); - - // Apply the transfor to colorants. - for (i=0; i < nColors; i++) { - cmsDoTransform(xform, &i, nc2 ->List[i].DeviceColorant, 1); - } - - if (!cmsWriteTag(hICC, cmsSigNamedColor2Tag, (void*) nc2)) goto Error; - cmsFreeNamedColorList(nc2); - - return hICC; - -Error: - if (hICC != NULL) cmsCloseProfile(hICC); - return NULL; -} - - -// This structure holds information about which MPU can be stored on a profile based on the version - -typedef struct { - cmsBool IsV4; // Is a V4 tag? - cmsTagSignature RequiredTag; // Set to 0 for both types - cmsTagTypeSignature LutType; // The LUT type - int nTypes; // Number of types (up to 5) - cmsStageSignature MpeTypes[5]; // 5 is the maximum number - -} cmsAllowedLUT; - -static const cmsAllowedLUT AllowedLUTTypes[] = { - - { 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 }}, - { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 3, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType } }, - { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType } }, - { TRUE , cmsSigAToB0Tag, cmsSigLutAtoBType, 5, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }}, - { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 1, { cmsSigCurveSetElemType }}, - { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 3, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType }}, - { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 3, { cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }}, - { TRUE , cmsSigBToA0Tag, cmsSigLutBtoAType, 5, { cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType }} -}; - -#define SIZE_OF_ALLOWED_LUT (sizeof(AllowedLUTTypes)/sizeof(cmsAllowedLUT)) - -// Check a single entry -static -cmsBool CheckOne(const cmsAllowedLUT* Tab, const cmsPipeline* Lut) -{ - cmsStage* mpe; - int n; - - for (n=0, mpe = Lut ->Elements; mpe != NULL; mpe = mpe ->Next, n++) { - - if (n > Tab ->nTypes) return FALSE; - if (cmsStageType(mpe) != Tab ->MpeTypes[n]) return FALSE; - } - - return (n == Tab ->nTypes); -} - - -static -const cmsAllowedLUT* FindCombination(const cmsPipeline* Lut, cmsBool IsV4, cmsTagSignature DestinationTag) -{ - cmsUInt32Number n; - - for (n=0; n < SIZE_OF_ALLOWED_LUT; n++) { - - const cmsAllowedLUT* Tab = AllowedLUTTypes + n; - - if (IsV4 ^ Tab -> IsV4) continue; - if ((Tab ->RequiredTag != 0) && (Tab ->RequiredTag != DestinationTag)) continue; - - if (CheckOne(Tab, Lut)) return Tab; - } - - return NULL; -} - - -// Does convert a transform into a device link profile -cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat64Number Version, cmsUInt32Number dwFlags) -{ - cmsHPROFILE hProfile = NULL; - cmsUInt32Number FrmIn, FrmOut, ChansIn, ChansOut; - cmsUInt32Number ColorSpaceBitsIn, ColorSpaceBitsOut; - _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; - cmsPipeline* LUT = NULL; - cmsStage* mpe; - cmsContext ContextID = cmsGetTransformContextID(hTransform); - const cmsAllowedLUT* AllowedLUT; - cmsTagSignature DestinationTag; - cmsProfileClassSignature deviceClass; - - _cmsAssert(hTransform != NULL); - - // Get the first mpe to check for named color - mpe = cmsPipelineGetPtrToFirstStage(xform ->Lut); - - // Check if is a named color transform - if (mpe != NULL) { - - if (cmsStageType(mpe) == cmsSigNamedColorElemType) { - return CreateNamedColorDevicelink(hTransform); - } - } - - // First thing to do is to get a copy of the transformation - LUT = cmsPipelineDup(xform ->Lut); - if (LUT == NULL) return NULL; - - // Time to fix the Lab2/Lab4 issue. - if ((xform ->EntryColorSpace == cmsSigLabData) && (Version < 4.0)) { - - if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocLabV2ToV4curves(ContextID))) - goto Error; - } - - // On the output side too - if ((xform ->ExitColorSpace) == cmsSigLabData && (Version < 4.0)) { - - if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocLabV4ToV2(ContextID))) - goto Error; - } - - - hProfile = cmsCreateProfilePlaceholder(ContextID); - if (!hProfile) goto Error; // can't allocate - - cmsSetProfileVersion(hProfile, Version); - - FixColorSpaces(hProfile, xform -> EntryColorSpace, xform -> ExitColorSpace, dwFlags); - - // Optimize the LUT and precalculate a devicelink - - ChansIn = cmsChannelsOf(xform -> EntryColorSpace); - ChansOut = cmsChannelsOf(xform -> ExitColorSpace); - - ColorSpaceBitsIn = _cmsLCMScolorSpace(xform -> EntryColorSpace); - ColorSpaceBitsOut = _cmsLCMScolorSpace(xform -> ExitColorSpace); - - FrmIn = COLORSPACE_SH(ColorSpaceBitsIn) | CHANNELS_SH(ChansIn)|BYTES_SH(2); - FrmOut = COLORSPACE_SH(ColorSpaceBitsOut) | CHANNELS_SH(ChansOut)|BYTES_SH(2); - - deviceClass = cmsGetDeviceClass(hProfile); - - if (deviceClass == cmsSigOutputClass) - DestinationTag = cmsSigBToA0Tag; - else - DestinationTag = cmsSigAToB0Tag; - - // Check if the profile/version can store the result - if (dwFlags & cmsFLAGS_FORCE_CLUT) - AllowedLUT = NULL; - else - AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag); - - if (AllowedLUT == NULL) { - - // Try to optimize - _cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags); - AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag); - - } - - // If no way, then force CLUT that for sure can be written - if (AllowedLUT == NULL) { - - dwFlags |= cmsFLAGS_FORCE_CLUT; - _cmsOptimizePipeline(ContextID, &LUT, xform ->RenderingIntent, &FrmIn, &FrmOut, &dwFlags); - - // Put identity curves if needed - if (cmsPipelineGetPtrToFirstStage(LUT) ->Type != cmsSigCurveSetElemType) - if (!cmsPipelineInsertStage(LUT, cmsAT_BEGIN, _cmsStageAllocIdentityCurves(ContextID, ChansIn))) - goto Error; - - if (cmsPipelineGetPtrToLastStage(LUT) ->Type != cmsSigCurveSetElemType) - if (!cmsPipelineInsertStage(LUT, cmsAT_END, _cmsStageAllocIdentityCurves(ContextID, ChansOut))) - goto Error; - - AllowedLUT = FindCombination(LUT, Version >= 4.0, DestinationTag); - } - - // Somethings is wrong... - if (AllowedLUT == NULL) { - goto Error; - } - - - if (dwFlags & cmsFLAGS_8BITS_DEVICELINK) - cmsPipelineSetSaveAs8bitsFlag(LUT, TRUE); - - // Tag profile with information - if (!SetTextTags(hProfile, L"devicelink")) goto Error; - - // Store result - if (!cmsWriteTag(hProfile, DestinationTag, LUT)) goto Error; - - - if (xform -> InputColorant != NULL) { - if (!cmsWriteTag(hProfile, cmsSigColorantTableTag, xform->InputColorant)) goto Error; - } - - if (xform -> OutputColorant != NULL) { - if (!cmsWriteTag(hProfile, cmsSigColorantTableOutTag, xform->OutputColorant)) goto Error; - } - - if ((deviceClass == cmsSigLinkClass) && (xform ->Sequence != NULL)) { - if (!_cmsWriteProfileSequence(hProfile, xform ->Sequence)) goto Error; - } - - // Set the white point - if (deviceClass == cmsSigInputClass) { - if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->EntryWhitePoint)) goto Error; - } - else { - if (!cmsWriteTag(hProfile, cmsSigMediaWhitePointTag, &xform ->ExitWhitePoint)) goto Error; - } - - - // Per 7.2.15 in spec 4.3 - cmsSetHeaderRenderingIntent(hProfile, xform ->RenderingIntent); - - cmsPipelineFree(LUT); - return hProfile; - -Error: - if (LUT != NULL) cmsPipelineFree(LUT); - cmsCloseProfile(hProfile); - return NULL; -} diff --git a/third_party/lcms2-2.6/src/cmswtpnt.c b/third_party/lcms2-2.6/src/cmswtpnt.c deleted file mode 100644 index 903fdd7497..0000000000 --- a/third_party/lcms2-2.6/src/cmswtpnt.c +++ /dev/null @@ -1,349 +0,0 @@ -//--------------------------------------------------------------------------------- -// -// 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 "lcms2_internal.h" - - -// D50 - Widely used -const cmsCIEXYZ* CMSEXPORT cmsD50_XYZ(void) -{ - static cmsCIEXYZ D50XYZ = {cmsD50X, cmsD50Y, cmsD50Z}; - - return &D50XYZ; -} - -const cmsCIExyY* CMSEXPORT cmsD50_xyY(void) -{ - static cmsCIExyY D50xyY; - - cmsXYZ2xyY(&D50xyY, cmsD50_XYZ()); - - return &D50xyY; -} - -// Obtains WhitePoint from Temperature -cmsBool CMSEXPORT cmsWhitePointFromTemp(cmsCIExyY* WhitePoint, cmsFloat64Number TempK) -{ - cmsFloat64Number x, y; - cmsFloat64Number T, T2, T3; - // cmsFloat64Number M1, M2; - - _cmsAssert(WhitePoint != NULL); - - T = TempK; - T2 = T*T; // Square - T3 = T2*T; // Cube - - // For correlated color temperature (T) between 4000K and 7000K: - - if (T >= 4000. && T <= 7000.) - { - x = -4.6070*(1E9/T3) + 2.9678*(1E6/T2) + 0.09911*(1E3/T) + 0.244063; - } - else - // or for correlated color temperature (T) between 7000K and 25000K: - - if (T > 7000.0 && T <= 25000.0) - { - x = -2.0064*(1E9/T3) + 1.9018*(1E6/T2) + 0.24748*(1E3/T) + 0.237040; - } - else { - cmsSignalError(0, cmsERROR_RANGE, "cmsWhitePointFromTemp: invalid temp"); - return FALSE; - } - - // Obtain y(x) - - y = -3.000*(x*x) + 2.870*x - 0.275; - - // wave factors (not used, but here for futures extensions) - - // M1 = (-1.3515 - 1.7703*x + 5.9114 *y)/(0.0241 + 0.2562*x - 0.7341*y); - // M2 = (0.0300 - 31.4424*x + 30.0717*y)/(0.0241 + 0.2562*x - 0.7341*y); - - WhitePoint -> x = x; - WhitePoint -> y = y; - WhitePoint -> Y = 1.0; - - return TRUE; -} - - - -typedef struct { - - cmsFloat64Number mirek; // temp (in microreciprocal kelvin) - cmsFloat64Number ut; // u coord of intersection w/ blackbody locus - cmsFloat64Number vt; // v coord of intersection w/ blackbody locus - cmsFloat64Number tt; // slope of ISOTEMPERATURE. line - - } ISOTEMPERATURE; - -static ISOTEMPERATURE isotempdata[] = { -// {Mirek, Ut, Vt, Tt } - {0, 0.18006, 0.26352, -0.24341}, - {10, 0.18066, 0.26589, -0.25479}, - {20, 0.18133, 0.26846, -0.26876}, - {30, 0.18208, 0.27119, -0.28539}, - {40, 0.18293, 0.27407, -0.30470}, - {50, 0.18388, 0.27709, -0.32675}, - {60, 0.18494, 0.28021, -0.35156}, - {70, 0.18611, 0.28342, -0.37915}, - {80, 0.18740, 0.28668, -0.40955}, - {90, 0.18880, 0.28997, -0.44278}, - {100, 0.19032, 0.29326, -0.47888}, - {125, 0.19462, 0.30141, -0.58204}, - {150, 0.19962, 0.30921, -0.70471}, - {175, 0.20525, 0.31647, -0.84901}, - {200, 0.21142, 0.32312, -1.0182 }, - {225, 0.21807, 0.32909, -1.2168 }, - {250, 0.22511, 0.33439, -1.4512 }, - {275, 0.23247, 0.33904, -1.7298 }, - {300, 0.24010, 0.34308, -2.0637 }, - {325, 0.24702, 0.34655, -2.4681 }, - {350, 0.25591, 0.34951, -2.9641 }, - {375, 0.26400, 0.35200, -3.5814 }, - {400, 0.27218, 0.35407, -4.3633 }, - {425, 0.28039, 0.35577, -5.3762 }, - {450, 0.28863, 0.35714, -6.7262 }, - {475, 0.29685, 0.35823, -8.5955 }, - {500, 0.30505, 0.35907, -11.324 }, - {525, 0.31320, 0.35968, -15.628 }, - {550, 0.32129, 0.36011, -23.325 }, - {575, 0.32931, 0.36038, -40.770 }, - {600, 0.33724, 0.36051, -116.45 } -}; - -#define NISO sizeof(isotempdata)/sizeof(ISOTEMPERATURE) - - -// Robertson's method -cmsBool CMSEXPORT cmsTempFromWhitePoint(cmsFloat64Number* TempK, const cmsCIExyY* WhitePoint) -{ - cmsUInt32Number j; - cmsFloat64Number us,vs; - cmsFloat64Number uj,vj,tj,di,dj,mi,mj; - cmsFloat64Number xs, ys; - - _cmsAssert(WhitePoint != NULL); - _cmsAssert(TempK != NULL); - - di = mi = 0; - xs = WhitePoint -> x; - ys = WhitePoint -> y; - - // convert (x,y) to CIE 1960 (u,WhitePoint) - - us = (2*xs) / (-xs + 6*ys + 1.5); - vs = (3*ys) / (-xs + 6*ys + 1.5); - - - for (j=0; j < NISO; j++) { - - uj = isotempdata[j].ut; - vj = isotempdata[j].vt; - tj = isotempdata[j].tt; - mj = isotempdata[j].mirek; - - dj = ((vs - vj) - tj * (us - uj)) / sqrt(1.0 + tj * tj); - - if ((j != 0) && (di/dj < 0.0)) { - - // Found a match - *TempK = 1000000.0 / (mi + (di / (di - dj)) * (mj - mi)); - return TRUE; - } - - di = dj; - mi = mj; - } - - // Not found - return FALSE; -} - - -// Compute chromatic adaptation matrix using Chad as cone matrix - -static -cmsBool ComputeChromaticAdaptation(cmsMAT3* Conversion, - const cmsCIEXYZ* SourceWhitePoint, - const cmsCIEXYZ* DestWhitePoint, - const cmsMAT3* Chad) - -{ - - cmsMAT3 Chad_Inv; - cmsVEC3 ConeSourceXYZ, ConeSourceRGB; - cmsVEC3 ConeDestXYZ, ConeDestRGB; - cmsMAT3 Cone, Tmp; - - - Tmp = *Chad; - if (!_cmsMAT3inverse(&Tmp, &Chad_Inv)) return FALSE; - - _cmsVEC3init(&ConeSourceXYZ, SourceWhitePoint -> X, - SourceWhitePoint -> Y, - SourceWhitePoint -> Z); - - _cmsVEC3init(&ConeDestXYZ, DestWhitePoint -> X, - DestWhitePoint -> Y, - DestWhitePoint -> Z); - - _cmsMAT3eval(&ConeSourceRGB, Chad, &ConeSourceXYZ); - _cmsMAT3eval(&ConeDestRGB, Chad, &ConeDestXYZ); - - // Build matrix - _cmsVEC3init(&Cone.v[0], ConeDestRGB.n[0]/ConeSourceRGB.n[0], 0.0, 0.0); - _cmsVEC3init(&Cone.v[1], 0.0, ConeDestRGB.n[1]/ConeSourceRGB.n[1], 0.0); - _cmsVEC3init(&Cone.v[2], 0.0, 0.0, ConeDestRGB.n[2]/ConeSourceRGB.n[2]); - - - // Normalize - _cmsMAT3per(&Tmp, &Cone, Chad); - _cmsMAT3per(Conversion, &Chad_Inv, &Tmp); - - return TRUE; -} - -// Returns the final chrmatic adaptation from illuminant FromIll to Illuminant ToIll -// The cone matrix can be specified in ConeMatrix. If NULL, Bradford is assumed -cmsBool _cmsAdaptationMatrix(cmsMAT3* r, const cmsMAT3* ConeMatrix, const cmsCIEXYZ* FromIll, const cmsCIEXYZ* ToIll) -{ - cmsMAT3 LamRigg = {{ // Bradford matrix - {{ 0.8951, 0.2664, -0.1614 }}, - {{ -0.7502, 1.7135, 0.0367 }}, - {{ 0.0389, -0.0685, 1.0296 }} - }}; - - if (ConeMatrix == NULL) - ConeMatrix = &LamRigg; - - return ComputeChromaticAdaptation(r, FromIll, ToIll, ConeMatrix); -} - -// Same as anterior, but assuming D50 destination. White point is given in xyY -static -cmsBool _cmsAdaptMatrixToD50(cmsMAT3* r, const cmsCIExyY* SourceWhitePt) -{ - cmsCIEXYZ Dn; - cmsMAT3 Bradford; - cmsMAT3 Tmp; - - cmsxyY2XYZ(&Dn, SourceWhitePt); - - if (!_cmsAdaptationMatrix(&Bradford, NULL, &Dn, cmsD50_XYZ())) return FALSE; - - Tmp = *r; - _cmsMAT3per(r, &Bradford, &Tmp); - - return TRUE; -} - -// Build a White point, primary chromas transfer matrix from RGB to CIE XYZ -// This is just an approximation, I am not handling all the non-linear -// aspects of the RGB to XYZ process, and assumming that the gamma correction -// has transitive property in the tranformation chain. -// -// the alghoritm: -// -// - First I build the absolute conversion matrix using -// primaries in XYZ. This matrix is next inverted -// - Then I eval the source white point across this matrix -// obtaining the coeficients of the transformation -// - Then, I apply these coeficients to the original matrix -// -cmsBool _cmsBuildRGB2XYZtransferMatrix(cmsMAT3* r, const cmsCIExyY* WhitePt, const cmsCIExyYTRIPLE* Primrs) -{ - cmsVEC3 WhitePoint, Coef; - cmsMAT3 Result, Primaries; - cmsFloat64Number xn, yn; - cmsFloat64Number xr, yr; - cmsFloat64Number xg, yg; - cmsFloat64Number xb, yb; - - xn = WhitePt -> x; - yn = WhitePt -> y; - xr = Primrs -> Red.x; - yr = Primrs -> Red.y; - xg = Primrs -> Green.x; - yg = Primrs -> Green.y; - xb = Primrs -> Blue.x; - yb = Primrs -> Blue.y; - - // Build Primaries matrix - _cmsVEC3init(&Primaries.v[0], xr, xg, xb); - _cmsVEC3init(&Primaries.v[1], yr, yg, yb); - _cmsVEC3init(&Primaries.v[2], (1-xr-yr), (1-xg-yg), (1-xb-yb)); - - - // Result = Primaries ^ (-1) inverse matrix - if (!_cmsMAT3inverse(&Primaries, &Result)) - return FALSE; - - - _cmsVEC3init(&WhitePoint, xn/yn, 1.0, (1.0-xn-yn)/yn); - - // Across inverse primaries ... - _cmsMAT3eval(&Coef, &Result, &WhitePoint); - - // Give us the Coefs, then I build transformation matrix - _cmsVEC3init(&r -> v[0], Coef.n[VX]*xr, Coef.n[VY]*xg, Coef.n[VZ]*xb); - _cmsVEC3init(&r -> v[1], Coef.n[VX]*yr, Coef.n[VY]*yg, Coef.n[VZ]*yb); - _cmsVEC3init(&r -> v[2], Coef.n[VX]*(1.0-xr-yr), Coef.n[VY]*(1.0-xg-yg), Coef.n[VZ]*(1.0-xb-yb)); - - - return _cmsAdaptMatrixToD50(r, WhitePt); - -} - - -// Adapts a color to a given illuminant. Original color is expected to have -// a SourceWhitePt white point. -cmsBool CMSEXPORT cmsAdaptToIlluminant(cmsCIEXYZ* Result, - const cmsCIEXYZ* SourceWhitePt, - const cmsCIEXYZ* Illuminant, - const cmsCIEXYZ* Value) -{ - cmsMAT3 Bradford; - cmsVEC3 In, Out; - - _cmsAssert(Result != NULL); - _cmsAssert(SourceWhitePt != NULL); - _cmsAssert(Illuminant != NULL); - _cmsAssert(Value != NULL); - - if (!_cmsAdaptationMatrix(&Bradford, NULL, SourceWhitePt, Illuminant)) return FALSE; - - _cmsVEC3init(&In, Value -> X, Value -> Y, Value -> Z); - _cmsMAT3eval(&Out, &Bradford, &In); - - Result -> X = Out.n[0]; - Result -> Y = Out.n[1]; - Result -> Z = Out.n[2]; - - return TRUE; -} diff --git a/third_party/lcms2-2.6/src/cmsxform.c b/third_party/lcms2-2.6/src/cmsxform.c deleted file mode 100644 index dffe6b2fb7..0000000000 --- a/third_party/lcms2-2.6/src/cmsxform.c +++ /dev/null @@ -1,1137 +0,0 @@ -//--------------------------------------------------------------------------------- -// -// 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 "lcms2_internal.h" - -// Transformations stuff -// ----------------------------------------------------------------------- - -#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) -{ - 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. - -#define DEFAULT_ALARM_CODES_VALUE {0x7F00, 0x7F00, 0x7F00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} - -_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)); -} - -// 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]) -{ - _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(ContextID, AlarmCodesContext); - - _cmsAssert(ContextAlarmCodes != NULL); // Can't happen - - memcpy(AlarmCodesP, ContextAlarmCodes->AlarmCodes, sizeof(ContextAlarmCodes->AlarmCodes)); -} - -void CMSEXPORT cmsSetAlarmCodes(const cmsUInt16Number NewAlarm[cmsMAXCHANNELS]) -{ - _cmsAssert(NewAlarm != NULL); - - cmsSetAlarmCodesTHR(NULL, NewAlarm); -} - -void CMSEXPORT cmsGetAlarmCodes(cmsUInt16Number OldAlarm[cmsMAXCHANNELS]) -{ - _cmsAssert(OldAlarm != NULL); - cmsGetAlarmCodesTHR(NULL, OldAlarm); -} - - -// 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) -{ - _cmsTRANSFORM* p = (_cmsTRANSFORM*) hTransform; - - _cmsAssert(p != NULL); - - if (p -> GamutCheck) - cmsPipelineFree(p -> GamutCheck); - - if (p -> Lut) - cmsPipelineFree(p -> Lut); - - if (p ->InputColorant) - cmsFreeNamedColorList(p ->InputColorant); - - if (p -> OutputColorant) - cmsFreeNamedColorList(p ->OutputColorant); - - if (p ->Sequence) - cmsFreeProfileSequenceDescription(p ->Sequence); - - if (p ->UserData) - p ->FreeUserData(p ->ContextID, p ->UserData); - - _cmsFree(p ->ContextID, (void *) p); -} - -// Apply transform. -void CMSEXPORT cmsDoTransform(cmsHTRANSFORM Transform, - const void* InputBuffer, - void* OutputBuffer, - cmsUInt32Number Size) - -{ - _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform; - - p -> xform(p, InputBuffer, OutputBuffer, Size, Size); -} - - -// Apply transform. -void CMSEXPORT cmsDoTransformStride(cmsHTRANSFORM Transform, - const void* InputBuffer, - void* OutputBuffer, - cmsUInt32Number Size, cmsUInt32Number Stride) - -{ - _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform; - - p -> xform(p, InputBuffer, OutputBuffer, Size, Stride); -} - - -// Transform routines ---------------------------------------------------------------------------------------------------------- - -// Float xform converts floats. Since there are no performance issues, one routine does all job, including gamut check. -// Note that because extended range, we can use a -1.0 value for out of gamut in this case. -static -void FloatXFORM(_cmsTRANSFORM* p, - const void* in, - void* out, cmsUInt32Number Size, cmsUInt32Number Stride) -{ - cmsUInt8Number* accum; - cmsUInt8Number* output; - cmsFloat32Number fIn[cmsMAXCHANNELS], fOut[cmsMAXCHANNELS]; - cmsFloat32Number OutOfGamut; - cmsUInt32Number i, j; - - accum = (cmsUInt8Number*) in; - output = (cmsUInt8Number*) out; - - for (i=0; i < Size; i++) { - - accum = p -> FromInputFloat(p, fIn, accum, Stride); - - // Any gamut chack to do? - if (p ->GamutCheck != NULL) { - - // Evaluate gamut marker. - cmsPipelineEvalFloat( fIn, &OutOfGamut, p ->GamutCheck); - - // Is current color out of gamut? - if (OutOfGamut > 0.0) { - - // Certainly, out of gamut - for (j=0; j < cmsMAXCHANNELS; j++) - fOut[j] = -1.0; - - } - else { - // No, proceed normally - cmsPipelineEvalFloat(fIn, fOut, p -> Lut); - } - } - else { - - // No gamut check at all - cmsPipelineEvalFloat(fIn, fOut, p -> Lut); - } - - // Back to asked representation - output = p -> ToOutputFloat(p, fOut, output, Stride); - } -} - - -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?static -void NullXFORM(_cmsTRANSFORM* p, - const void* in, - void* out, cmsUInt32Number Size, - cmsUInt32Number Stride) -{ - cmsUInt8Number* accum; - cmsUInt8Number* output; - cmsUInt16Number wIn[cmsMAXCHANNELS]; - cmsUInt32Number i, n; - - accum = (cmsUInt8Number*) in; - output = (cmsUInt8Number*) out; - n = Size; // Buffer len - - for (i=0; i < n; i++) { - - accum = p -> FromInput(p, wIn, accum, Stride); - output = p -> ToOutput(p, wIn, output, Stride); - } -} - - -// No gamut check, no cache, 16 bits -static -void PrecalculatedXFORM(_cmsTRANSFORM* p, - const void* in, - void* out, cmsUInt32Number Size, cmsUInt32Number Stride) -{ - register cmsUInt8Number* accum; - register cmsUInt8Number* output; - cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS]; - cmsUInt32Number i, n; - - accum = (cmsUInt8Number*) in; - output = (cmsUInt8Number*) out; - n = Size; - - for (i=0; i < n; i++) { - - accum = p -> FromInput(p, wIn, accum, Stride); - p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data); - output = p -> ToOutput(p, wOut, output, Stride); - } -} - - -// 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[], - cmsUInt16Number wOut[]) -{ - cmsUInt16Number wOutOfGamut; - - p ->GamutCheck ->Eval16Fn(wIn, &wOutOfGamut, p ->GamutCheck ->Data); - if (wOutOfGamut >= 1) { - - cmsUInt16Number i; - _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(p->ContextID, AlarmCodesContext); - - for (i=0; i < p ->Lut->OutputChannels; i++) { - - wOut[i] = ContextAlarmCodes ->AlarmCodes[i]; - } - } - else - p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data); -} - -// Gamut check, No cach? 16 bits. -static -void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p, - const void* in, - void* out, cmsUInt32Number Size, cmsUInt32Number Stride) -{ - cmsUInt8Number* accum; - cmsUInt8Number* output; - cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS]; - cmsUInt32Number i, n; - - accum = (cmsUInt8Number*) in; - output = (cmsUInt8Number*) out; - n = Size; // Buffer len - - for (i=0; i < n; i++) { - - accum = p -> FromInput(p, wIn, accum, Stride); - TransformOnePixelWithGamutCheck(p, wIn, wOut); - output = p -> ToOutput(p, wOut, output, Stride); - } -} - - -// No gamut check, Cach? 16 bits, -static -void CachedXFORM(_cmsTRANSFORM* p, - const void* in, - void* out, cmsUInt32Number Size, cmsUInt32Number Stride) -{ - cmsUInt8Number* accum; - cmsUInt8Number* output; - cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS]; - cmsUInt32Number i, n; - _cmsCACHE Cache; - - accum = (cmsUInt8Number*) in; - output = (cmsUInt8Number*) out; - n = Size; // Buffer len - - // Empty buffers for quick memcmp - memset(wIn, 0, sizeof(wIn)); - memset(wOut, 0, sizeof(wOut)); - - // Get copy of zero cache - memcpy(&Cache, &p ->Cache, sizeof(Cache)); - - for (i=0; i < n; i++) { - - accum = p -> FromInput(p, wIn, accum, Stride); - - if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) { - - memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut)); - } - else { - - p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data); - - memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn)); - memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut)); - } - - output = p -> ToOutput(p, wOut, output, Stride); - } - -} - - -// All those nice features together -static -void CachedXFORMGamutCheck(_cmsTRANSFORM* p, - const void* in, - void* out, cmsUInt32Number Size, cmsUInt32Number Stride) -{ - cmsUInt8Number* accum; - cmsUInt8Number* output; - cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS]; - cmsUInt32Number i, n; - _cmsCACHE Cache; - - accum = (cmsUInt8Number*) in; - output = (cmsUInt8Number*) out; - n = Size; // Buffer len - - // Empty buffers for quick memcmp - memset(wIn, 0, sizeof(cmsUInt16Number) * cmsMAXCHANNELS); - memset(wOut, 0, sizeof(cmsUInt16Number) * cmsMAXCHANNELS); - - // Get copy of zero cache - memcpy(&Cache, &p ->Cache, sizeof(Cache)); - - for (i=0; i < n; i++) { - - accum = p -> FromInput(p, wIn, accum, Stride); - - if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) { - memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut)); - } - else { - TransformOnePixelWithGamutCheck(p, wIn, wOut); - memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn)); - memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut)); - } - - output = p -> ToOutput(p, wOut, output, Stride); - } - -} - -// ------------------------------------------------------------------------------------------------------------- - -// List of used-defined transform factories -typedef struct _cmsTransformCollection_st { - - _cmsTransformFactory Factory; - struct _cmsTransformCollection_st *Next; - -} _cmsTransformCollection; - -// The linked list head -_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 ContextID, cmsPluginBase* Data) -{ - cmsPluginTransform* Plugin = (cmsPluginTransform*) Data; - _cmsTransformCollection* fl; - _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID,TransformPlugin); - - if (Data == NULL) { - - // Free the chain. Memory is safely freed at exit - ctx->TransformCollection = NULL; - return TRUE; - } - - // Factory callback is required - if (Plugin ->Factory == NULL) return FALSE; - - - fl = (_cmsTransformCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsTransformCollection)); - if (fl == NULL) return FALSE; - - // Copy the parameters - fl ->Factory = Plugin ->Factory; - - // Keep linked list - fl ->Next = ctx->TransformCollection; - ctx->TransformCollection = fl; - - // All is ok - return TRUE; -} - - -void CMSEXPORT _cmsSetTransformUserData(struct _cmstransform_struct *CMMcargo, void* ptr, _cmsFreeUserDataFn FreePrivateDataFn) -{ - _cmsAssert(CMMcargo != NULL); - CMMcargo ->UserData = ptr; - CMMcargo ->FreeUserData = FreePrivateDataFn; -} - -// returns the pointer defined by the plug-in to store private data -void * CMSEXPORT _cmsGetTransformUserData(struct _cmstransform_struct *CMMcargo) -{ - _cmsAssert(CMMcargo != NULL); - return CMMcargo ->UserData; -} - -// returns the current formatters -void CMSEXPORT _cmsGetTransformFormatters16(struct _cmstransform_struct *CMMcargo, cmsFormatter16* FromInput, cmsFormatter16* ToOutput) -{ - _cmsAssert(CMMcargo != NULL); - if (FromInput) *FromInput = CMMcargo ->FromInput; - if (ToOutput) *ToOutput = CMMcargo ->ToOutput; -} - -void CMSEXPORT _cmsGetTransformFormattersFloat(struct _cmstransform_struct *CMMcargo, cmsFormatterFloat* FromInput, cmsFormatterFloat* ToOutput) -{ - _cmsAssert(CMMcargo != NULL); - if (FromInput) *FromInput = CMMcargo ->FromInputFloat; - if (ToOutput) *ToOutput = CMMcargo ->ToOutputFloat; -} - - -// Allocate transform struct and set it to defaults. Ask the optimization plug-in about if those formats are proper -// for separated transforms. If this is the case, -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 - _cmsTRANSFORM* p = (_cmsTRANSFORM*) _cmsMallocZero(ContextID, sizeof(_cmsTRANSFORM)); - if (!p) { - cmsPipelineFree(lut); - return NULL; - } - - // Store the proposed pipeline - p ->Lut = lut; - - // Let's see if any plug-in want to do the transform by itself - for (Plugin = ctx ->TransformCollection; - Plugin != NULL; - Plugin = Plugin ->Next) { - - if (Plugin ->Factory(&p->xform, &p->UserData, &p ->FreeUserData, &p ->Lut, InputFormat, OutputFormat, dwFlags)) { - - // Last plugin in the declaration order takes control. We just keep - // the original parameters as a logging. - // Note that cmsFLAGS_CAN_CHANGE_FORMATTER is not set, so by default - // an optimized transform is not reusable. The plug-in can, however, change - // the flags and make it suitable. - - p ->ContextID = ContextID; - p ->InputFormat = *InputFormat; - p ->OutputFormat = *OutputFormat; - p ->dwOriginalFlags = *dwFlags; - - // 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(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; - } - } - - // Not suitable for the transform plug-in, let's check the pipeline plug-in - if (p ->Lut != NULL) - _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(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) { - - cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format"); - cmsDeleteTransform(p); - return NULL; - } - - if (*dwFlags & cmsFLAGS_NULLTRANSFORM) { - - p ->xform = NullFloatXFORM; - } - else { - // Float transforms don't use cach? always are non-NULL - p ->xform = FloatXFORM; - } - - } - else { - - if (*InputFormat == 0 && *OutputFormat == 0) { - p ->FromInput = p ->ToOutput = NULL; - *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER; - } - else { - - int BytesPerPixelInput; - - 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) { - - cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format"); - cmsDeleteTransform(p); - return NULL; - } - - BytesPerPixelInput = T_BYTES(p ->InputFormat); - if (BytesPerPixelInput == 0 || BytesPerPixelInput >= 2) - *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER; - - } - - if (*dwFlags & cmsFLAGS_NULLTRANSFORM) { - - p ->xform = NullXFORM; - } - else { - if (*dwFlags & cmsFLAGS_NOCACHE) { - - if (*dwFlags & cmsFLAGS_GAMUTCHECK) - p ->xform = PrecalculatedXFORMGamutCheck; // Gamut check, no cach? - else - p ->xform = PrecalculatedXFORM; // No cach? no gamut check - } - else { - - if (*dwFlags & cmsFLAGS_GAMUTCHECK) - p ->xform = CachedXFORMGamutCheck; // Gamut check, cach? - else - p ->xform = CachedXFORM; // No gamut check, cach? - } - } - } - - p ->InputFormat = *InputFormat; - p ->OutputFormat = *OutputFormat; - p ->dwOriginalFlags = *dwFlags; - p ->ContextID = ContextID; - p ->UserData = NULL; - return p; -} - -static -cmsBool GetXFormColorSpaces(int nProfiles, cmsHPROFILE hProfiles[], cmsColorSpaceSignature* Input, cmsColorSpaceSignature* Output) -{ - cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut; - cmsColorSpaceSignature PostColorSpace; - int i; - - if (nProfiles <= 0) return FALSE; - if (hProfiles[0] == NULL) return FALSE; - - *Input = PostColorSpace = cmsGetColorSpace(hProfiles[0]); - - for (i=0; i < nProfiles; i++) { - - cmsProfileClassSignature cls; - cmsHPROFILE hProfile = hProfiles[i]; - - int lIsInput = (PostColorSpace != cmsSigXYZData) && - (PostColorSpace != cmsSigLabData); - - if (hProfile == NULL) return FALSE; - - cls = cmsGetDeviceClass(hProfile); - - if (cls == cmsSigNamedColorClass) { - - ColorSpaceIn = cmsSig1colorData; - ColorSpaceOut = (nProfiles > 1) ? cmsGetPCS(hProfile) : cmsGetColorSpace(hProfile); - } - else - if (lIsInput || (cls == cmsSigLinkClass)) { - - ColorSpaceIn = cmsGetColorSpace(hProfile); - ColorSpaceOut = cmsGetPCS(hProfile); - } - else - { - ColorSpaceIn = cmsGetPCS(hProfile); - ColorSpaceOut = cmsGetColorSpace(hProfile); - } - - if (i==0) - *Input = ColorSpaceIn; - - PostColorSpace = ColorSpaceOut; - } - - *Output = PostColorSpace; - - return TRUE; -} - -// Check colorspace -static -cmsBool IsProperColorSpace(cmsColorSpaceSignature Check, cmsUInt32Number dwFormat) -{ - int Space1 = T_COLORSPACE(dwFormat); - int Space2 = _cmsLCMScolorSpace(Check); - - if (Space1 == PT_ANY) return TRUE; - if (Space1 == Space2) return TRUE; - - if (Space1 == PT_LabV2 && Space2 == PT_Lab) return TRUE; - if (Space1 == PT_Lab && Space2 == PT_LabV2) return TRUE; - - return FALSE; -} - -// ---------------------------------------------------------------------------------------------------------------- - -static -void SetWhitePoint(cmsCIEXYZ* wtPt, const cmsCIEXYZ* src) -{ - if (src == NULL) { - wtPt ->X = cmsD50X; - wtPt ->Y = cmsD50Y; - wtPt ->Z = cmsD50Z; - } - else { - wtPt ->X = src->X; - wtPt ->Y = src->Y; - wtPt ->Z = src->Z; - } - -} - -// New to lcms 2.0 -- have all parameters available. -cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID, - cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[], - cmsBool BPC[], - cmsUInt32Number Intents[], - cmsFloat64Number AdaptationStates[], - cmsHPROFILE hGamutProfile, - cmsUInt32Number nGamutPCSposition, - cmsUInt32Number InputFormat, - cmsUInt32Number OutputFormat, - cmsUInt32Number dwFlags) -{ - _cmsTRANSFORM* xform; - cmsColorSpaceSignature EntryColorSpace; - cmsColorSpaceSignature ExitColorSpace; - cmsPipeline* Lut; - cmsUInt32Number LastIntent = Intents[nProfiles-1]; - - // If it is a fake transform - if (dwFlags & cmsFLAGS_NULLTRANSFORM) - { - return AllocEmptyTransform(ContextID, NULL, INTENT_PERCEPTUAL, &InputFormat, &OutputFormat, &dwFlags); - } - - // If gamut check is requested, make sure we have a gamut profile - if (dwFlags & cmsFLAGS_GAMUTCHECK) { - if (hGamutProfile == NULL) dwFlags &= ~cmsFLAGS_GAMUTCHECK; - } - - // On floating point transforms, inhibit cache - if (_cmsFormatterIsFloat(InputFormat) || _cmsFormatterIsFloat(OutputFormat)) - dwFlags |= cmsFLAGS_NOCACHE; - - // Mark entry/exit spaces - if (!GetXFormColorSpaces(nProfiles, hProfiles, &EntryColorSpace, &ExitColorSpace)) { - cmsSignalError(ContextID, cmsERROR_NULL, "NULL input profiles on transform"); - return NULL; - } - - // Check if proper colorspaces - if (!IsProperColorSpace(EntryColorSpace, InputFormat)) { - cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong input color space on transform"); - return NULL; - } - - if (!IsProperColorSpace(ExitColorSpace, OutputFormat)) { - cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong output color space on transform"); - return NULL; - } - - // Create a pipeline with all transformations - Lut = _cmsLinkProfiles(ContextID, nProfiles, Intents, hProfiles, BPC, AdaptationStates, dwFlags); - if (Lut == NULL) { - cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Couldn't link the profiles"); - return NULL; - } - - // Check channel count - if ((cmsChannelsOf(EntryColorSpace) != cmsPipelineInputChannels(Lut)) || - (cmsChannelsOf(ExitColorSpace) != cmsPipelineOutputChannels(Lut))) { - cmsPipelineFree(Lut); - cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Channel count doesn't match. Profile is corrupted"); - return NULL; - } - - - // All seems ok - xform = AllocEmptyTransform(ContextID, Lut, LastIntent, &InputFormat, &OutputFormat, &dwFlags); - if (xform == NULL) { - return NULL; - } - - // Keep values - xform ->EntryColorSpace = EntryColorSpace; - xform ->ExitColorSpace = ExitColorSpace; - xform ->RenderingIntent = Intents[nProfiles-1]; - - // Take white points - SetWhitePoint(&xform->EntryWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[0], cmsSigMediaWhitePointTag)); - SetWhitePoint(&xform->ExitWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[nProfiles-1], cmsSigMediaWhitePointTag)); - - - // Create a gamut check LUT if requested - if (hGamutProfile != NULL && (dwFlags & cmsFLAGS_GAMUTCHECK)) - xform ->GamutCheck = _cmsCreateGamutCheckPipeline(ContextID, hProfiles, - BPC, Intents, - AdaptationStates, - nGamutPCSposition, - hGamutProfile); - - - // Try to read input and output colorant table - if (cmsIsTag(hProfiles[0], cmsSigColorantTableTag)) { - - // Input table can only come in this way. - xform ->InputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[0], cmsSigColorantTableTag)); - } - - // Output is a little bit more complex. - if (cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigLinkClass) { - - // This tag may exist only on devicelink profiles. - if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag)) { - - // It may be NULL if error - xform ->OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag)); - } - - } else { - - if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableTag)) { - - xform -> OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableTag)); - } - } - - // Store the sequence of profiles - if (dwFlags & cmsFLAGS_KEEP_SEQUENCE) { - xform ->Sequence = _cmsCompileProfileSequence(ContextID, nProfiles, hProfiles); - } - else - xform ->Sequence = NULL; - - // If this is a cached transform, init first value, which is zero (16 bits only) - if (!(dwFlags & cmsFLAGS_NOCACHE)) { - - memset(&xform ->Cache.CacheIn, 0, sizeof(xform ->Cache.CacheIn)); - - if (xform ->GamutCheck != NULL) { - TransformOnePixelWithGamutCheck(xform, xform ->Cache.CacheIn, xform->Cache.CacheOut); - } - else { - - xform ->Lut ->Eval16Fn(xform ->Cache.CacheIn, xform->Cache.CacheOut, xform -> Lut->Data); - } - - } - - return (cmsHTRANSFORM) xform; -} - -// Multiprofile transforms: Gamut check is not available here, as it is unclear from which profile the gamut comes. -cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransformTHR(cmsContext ContextID, - cmsHPROFILE hProfiles[], - cmsUInt32Number nProfiles, - cmsUInt32Number InputFormat, - cmsUInt32Number OutputFormat, - cmsUInt32Number Intent, - cmsUInt32Number dwFlags) -{ - cmsUInt32Number i; - cmsBool BPC[256]; - cmsUInt32Number Intents[256]; - cmsFloat64Number AdaptationStates[256]; - - if (nProfiles <= 0 || nProfiles > 255) { - cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles); - return NULL; - } - - for (i=0; i < nProfiles; i++) { - BPC[i] = dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION ? TRUE : FALSE; - Intents[i] = Intent; - AdaptationStates[i] = cmsSetAdaptationStateTHR(ContextID, -1); - } - - - return cmsCreateExtendedTransform(ContextID, nProfiles, hProfiles, BPC, Intents, AdaptationStates, NULL, 0, InputFormat, OutputFormat, dwFlags); -} - - - -cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles[], - cmsUInt32Number nProfiles, - cmsUInt32Number InputFormat, - cmsUInt32Number OutputFormat, - cmsUInt32Number Intent, - cmsUInt32Number dwFlags) -{ - - if (nProfiles <= 0 || nProfiles > 255) { - cmsSignalError(NULL, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles); - return NULL; - } - - return cmsCreateMultiprofileTransformTHR(cmsGetProfileContextID(hProfiles[0]), - hProfiles, - nProfiles, - InputFormat, - OutputFormat, - Intent, - dwFlags); -} - -cmsHTRANSFORM CMSEXPORT cmsCreateTransformTHR(cmsContext ContextID, - cmsHPROFILE Input, - cmsUInt32Number InputFormat, - cmsHPROFILE Output, - cmsUInt32Number OutputFormat, - cmsUInt32Number Intent, - cmsUInt32Number dwFlags) -{ - - cmsHPROFILE hArray[2]; - - hArray[0] = Input; - hArray[1] = Output; - - return cmsCreateMultiprofileTransformTHR(ContextID, hArray, Output == NULL ? 1 : 2, InputFormat, OutputFormat, Intent, dwFlags); -} - -CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateTransform(cmsHPROFILE Input, - cmsUInt32Number InputFormat, - cmsHPROFILE Output, - cmsUInt32Number OutputFormat, - cmsUInt32Number Intent, - cmsUInt32Number dwFlags) -{ - return cmsCreateTransformTHR(cmsGetProfileContextID(Input), Input, InputFormat, Output, OutputFormat, Intent, dwFlags); -} - - -cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransformTHR(cmsContext ContextID, - cmsHPROFILE InputProfile, - cmsUInt32Number InputFormat, - cmsHPROFILE OutputProfile, - cmsUInt32Number OutputFormat, - cmsHPROFILE ProofingProfile, - cmsUInt32Number nIntent, - cmsUInt32Number ProofingIntent, - cmsUInt32Number dwFlags) -{ - cmsHPROFILE hArray[4]; - cmsUInt32Number Intents[4]; - cmsBool BPC[4]; - cmsFloat64Number Adaptation[4]; - cmsBool DoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION) ? TRUE : FALSE; - - - hArray[0] = InputProfile; hArray[1] = ProofingProfile; hArray[2] = ProofingProfile; hArray[3] = OutputProfile; - 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] = cmsSetAdaptationStateTHR(ContextID, -1); - - if (!(dwFlags & (cmsFLAGS_SOFTPROOFING|cmsFLAGS_GAMUTCHECK))) - return cmsCreateTransformTHR(ContextID, InputProfile, InputFormat, OutputProfile, OutputFormat, nIntent, dwFlags); - - return cmsCreateExtendedTransform(ContextID, 4, hArray, BPC, Intents, Adaptation, - ProofingProfile, 1, InputFormat, OutputFormat, dwFlags); - -} - - -cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransform(cmsHPROFILE InputProfile, - cmsUInt32Number InputFormat, - cmsHPROFILE OutputProfile, - cmsUInt32Number OutputFormat, - cmsHPROFILE ProofingProfile, - cmsUInt32Number nIntent, - cmsUInt32Number ProofingIntent, - cmsUInt32Number dwFlags) -{ - return cmsCreateProofingTransformTHR(cmsGetProfileContextID(InputProfile), - InputProfile, - InputFormat, - OutputProfile, - OutputFormat, - ProofingProfile, - nIntent, - ProofingIntent, - dwFlags); -} - - -// Grab the ContextID from an open transform. Returns NULL if a NULL transform is passed -cmsContext CMSEXPORT cmsGetTransformContextID(cmsHTRANSFORM hTransform) -{ - _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; - - if (xform == NULL) return NULL; - return xform -> ContextID; -} - -// Grab the input/output formats -cmsUInt32Number CMSEXPORT cmsGetTransformInputFormat(cmsHTRANSFORM hTransform) -{ - _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; - - if (xform == NULL) return 0; - return xform->InputFormat; -} - -cmsUInt32Number CMSEXPORT cmsGetTransformOutputFormat(cmsHTRANSFORM hTransform) -{ - _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; - - if (xform == NULL) return 0; - return xform->OutputFormat; -} - -// For backwards compatibility -cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform, - cmsUInt32Number InputFormat, - cmsUInt32Number OutputFormat) -{ - - _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; - cmsFormatter16 FromInput, ToOutput; - - - // We only can afford to change formatters if previous transform is at least 16 bits - if (!(xform ->dwOriginalFlags & cmsFLAGS_CAN_CHANGE_FORMATTER)) { - - cmsSignalError(xform ->ContextID, cmsERROR_NOT_SUITABLE, "cmsChangeBuffersFormat works only on transforms created originally with at least 16 bits of precision"); - return FALSE; - } - - 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) { - - cmsSignalError(xform -> ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format"); - return FALSE; - } - - xform ->InputFormat = InputFormat; - xform ->OutputFormat = OutputFormat; - xform ->FromInput = FromInput; - xform ->ToOutput = ToOutput; - return TRUE; -} diff --git a/third_party/lcms2-2.6/src/lcms2_internal.h b/third_party/lcms2-2.6/src/lcms2_internal.h deleted file mode 100644 index cc76d488d0..0000000000 --- a/third_party/lcms2-2.6/src/lcms2_internal.h +++ /dev/null @@ -1,1032 +0,0 @@ -//<<<+++OPENSOURCE -//<<<+++OPENSOURCE_MUST_BEGIN COMMENT==TRUE -// -// 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 _lcms_internal_H - -// Include plug-in foundation -#ifndef _lcms_plugin_H -#include "third_party/lcms2-2.6/include/lcms2_plugin.h" -#endif - -// ctype is part of C99 as per 7.1.2 -#include - -// assert macro is part of C99 as per 7.2 -#include - -// Some needed constants -#ifndef M_PI -# define M_PI 3.14159265358979323846 -#endif - -#ifndef M_LOG10E -# define M_LOG10E 0.434294481903251827651 -#endif - -// BorlandC 5.5, VC2003 are broken on that -#if defined(__BORLANDC__) || (_MSC_VER < 1400) // 1400 == VC++ 8.0 -#define sinf(x) (float)sin((float)x) -#define sqrtf(x) (float)sqrt((float)x) -#endif - - -// Alignment of ICC file format uses 4 bytes (cmsUInt32Number) -#define _cmsALIGNLONG(x) (((x)+(sizeof(cmsUInt32Number)-1)) & ~(sizeof(cmsUInt32Number)-1)) - -// Alignment to memory pointer -#define _cmsALIGNMEM(x) (((x)+(sizeof(void *) - 1)) & ~(sizeof(void *) - 1)) - -// Maximum encodeable values in floating point -#define MAX_ENCODEABLE_XYZ (1.0 + 32767.0/32768.0) -#define MIN_ENCODEABLE_ab2 (-128.0) -#define MAX_ENCODEABLE_ab2 ((65535.0/256.0) - 128.0) -#define MIN_ENCODEABLE_ab4 (-128.0) -#define MAX_ENCODEABLE_ab4 (127.0) - -// Maximum of channels for internal pipeline evaluation -#define MAX_STAGE_CHANNELS 128 - -// Unused parameter warning supression -#define cmsUNUSED_PARAMETER(x) ((void)x) - -// The specification for "inline" is section 6.7.4 of the C99 standard (ISO/IEC 9899:1999). -// unfortunately VisualC++ does not conform that -#if defined(_MSC_VER) || defined(__BORLANDC__) -# define cmsINLINE __inline -#else -# define cmsINLINE static inline -#endif - -// Other replacement functions -#ifdef _MSC_VER -# ifndef snprintf -# define snprintf _snprintf -# endif -# ifndef vsnprintf -# define vsnprintf _vsnprintf -# endif -#endif - - -// A fast way to convert from/to 16 <-> 8 bits -#define FROM_8_TO_16(rgb) (cmsUInt16Number) ((((cmsUInt16Number) (rgb)) << 8)|(rgb)) -#define FROM_16_TO_8(rgb) (cmsUInt8Number) ((((cmsUInt32Number)(rgb) * 65281U + 8388608U) >> 24) & 0xFFU) - -// Code analysis is broken on asserts -#ifdef _MSC_VER -# if (_MSC_VER >= 1500) -# define _cmsAssert(a) { assert((a)); __analysis_assume((a)); } -# else -# define _cmsAssert(a) assert((a)) -# endif -#else -# define _cmsAssert(a) assert((a)) -#endif - -//--------------------------------------------------------------------------------- - -// Determinant lower than that are assumed zero (used on matrix invert) -#define MATRIX_DET_TOLERANCE 0.0001 - -//--------------------------------------------------------------------------------- - -// Fixed point -#define FIXED_TO_INT(x) ((x)>>16) -#define FIXED_REST_TO_INT(x) ((x)&0xFFFFU) -#define ROUND_FIXED_TO_INT(x) (((x)+0x8000)>>16) - -cmsINLINE cmsS15Fixed16Number _cmsToFixedDomain(int a) { return a + ((a + 0x7fff) / 0xffff); } -cmsINLINE int _cmsFromFixedDomain(cmsS15Fixed16Number a) { return a - ((a + 0x7fff) >> 16); } - -// ----------------------------------------------------------------------------------------------------------- - -// Fast floor conversion logic. Thanks to Sree Kotay and Stuart Nixon -// note than this only works in the range ..-32767...+32767 because -// mantissa is interpreted as 15.16 fixed point. -// The union is to avoid pointer aliasing overoptimization. -cmsINLINE int _cmsQuickFloor(cmsFloat64Number val) -{ -#ifdef CMS_DONT_USE_FAST_FLOOR - return (int) floor(val); -#else - const cmsFloat64Number _lcms_double2fixmagic = 68719476736.0 * 1.5; // 2^36 * 1.5, (52-16=36) uses limited precision to floor - union { - cmsFloat64Number val; - int halves[2]; - } temp; - - temp.val = val + _lcms_double2fixmagic; - -#ifdef CMS_USE_BIG_ENDIAN - return temp.halves[1] >> 16; -#else - return temp.halves[0] >> 16; -#endif -#endif -} - -// Fast floor restricted to 0..65535.0 -cmsINLINE cmsUInt16Number _cmsQuickFloorWord(cmsFloat64Number d) -{ - return (cmsUInt16Number) _cmsQuickFloor(d - 32767.0) + 32767U; -} - -// Floor to word, taking care of saturation -cmsINLINE cmsUInt16Number _cmsQuickSaturateWord(cmsFloat64Number d) -{ - d += 0.5; - if (d <= 0) return 0; - if (d >= 65535.0) return 0xffff; - - return _cmsQuickFloorWord(d); -} - - -// Pthread support -------------------------------------------------------------------- -#ifndef CMS_NO_PTHREADS - -// This is the threading support. Unfortunately, it has to be platform-dependent because -// windows does not support pthreads. - -#ifdef CMS_IS_WINDOWS_ - -#define WIN32_LEAN_AND_MEAN 1 -#include - - -// From: http://locklessinc.com/articles/pthreads_on_windows/ -// The pthreads API has an initialization macro that has no correspondence to anything in -// the windows API. By investigating the internal definition of the critical section type, -// one may work out how to initialize one without calling InitializeCriticalSection(). -// The trick here is that InitializeCriticalSection() is not allowed to fail. It tries -// to allocate a critical section debug object, but if no memory is available, it sets -// the pointer to a specific value. (One would expect that value to be NULL, but it is -// actually (void *)-1 for some reason.) Thus we can use this special value for that -// pointer, and the critical section code will work. - -// The other important part of the critical section type to initialize is the number -// of waiters. This controls whether or not the mutex is locked. Fortunately, this -// part of the critical section is unlikely to change. Apparently, many programs -// already test critical sections to see if they are locked using this value, so -// Microsoft felt that it was necessary to keep it set at -1 for an unlocked critical -// section, even when they changed the underlying algorithm to be more scalable. -// The final parts of the critical section object are unimportant, and can be set -// to zero for their defaults. This yields an initialization macro: - -typedef CRITICAL_SECTION _cmsMutex; - -#define CMS_MUTEX_INITIALIZER {(void*) -1,-1,0,0,0,0} - -cmsINLINE int _cmsLockPrimitive(_cmsMutex *m) -{ - EnterCriticalSection(m); - return 0; -} - -cmsINLINE int _cmsUnlockPrimitive(_cmsMutex *m) -{ - LeaveCriticalSection(m); - return 0; -} - -cmsINLINE int _cmsInitMutexPrimitive(_cmsMutex *m) -{ - InitializeCriticalSection(m); - return 0; -} - -cmsINLINE int _cmsDestroyMutexPrimitive(_cmsMutex *m) -{ - DeleteCriticalSection(m); - return 0; -} - -cmsINLINE int _cmsEnterCriticalSectionPrimitive(_cmsMutex *m) -{ - EnterCriticalSection(m); - return 0; -} - -cmsINLINE int _cmsLeaveCriticalSectionPrimitive(_cmsMutex *m) -{ - LeaveCriticalSection(m); - return 0; -} - -#else - -// Rest of the wide world -#include - -#define CMS_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER -typedef pthread_mutex_t _cmsMutex; - - -cmsINLINE int _cmsLockPrimitive(_cmsMutex *m) -{ - return pthread_mutex_lock(m); -} - -cmsINLINE int _cmsUnlockPrimitive(_cmsMutex *m) -{ - return pthread_mutex_unlock(m); -} - -cmsINLINE int _cmsInitMutexPrimitive(_cmsMutex *m) -{ - return pthread_mutex_init(m, NULL); -} - -cmsINLINE int _cmsDestroyMutexPrimitive(_cmsMutex *m) -{ - return pthread_mutex_destroy(m); -} - -cmsINLINE int _cmsEnterCriticalSectionPrimitive(_cmsMutex *m) -{ - return pthread_mutex_lock(m); -} - -cmsINLINE int _cmsLeaveCriticalSectionPrimitive(_cmsMutex *m) -{ - return pthread_mutex_unlock(m); -} - -#endif -#else - -#define CMS_MUTEX_INITIALIZER 0 -typedef int _cmsMutex; - - -cmsINLINE int _cmsLockPrimitive(_cmsMutex *m) -{ - return 0; - cmsUNUSED_PARAMETER(m); -} - -cmsINLINE int _cmsUnlockPrimitive(_cmsMutex *m) -{ - return 0; - cmsUNUSED_PARAMETER(m); -} - -cmsINLINE int _cmsInitMutexPrimitive(_cmsMutex *m) -{ - return 0; - cmsUNUSED_PARAMETER(m); -} - -cmsINLINE int _cmsDestroyMutexPrimitive(_cmsMutex *m) -{ - return 0; - cmsUNUSED_PARAMETER(m); -} - -cmsINLINE int _cmsEnterCriticalSectionPrimitive(_cmsMutex *m) -{ - return 0; - cmsUNUSED_PARAMETER(m); -} - -cmsINLINE int _cmsLeaveCriticalSectionPrimitive(_cmsMutex *m) -{ - return 0; - cmsUNUSED_PARAMETER(m); -} -#endif - -// 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(cmsContext ContextID, cmsPluginBase* Plugin); - -// Interpolation -cmsBool _cmsRegisterInterpPlugin(cmsContext ContextID, cmsPluginBase* Plugin); - -// Parametric curves -cmsBool _cmsRegisterParametricCurvesPlugin(cmsContext ContextID, cmsPluginBase* Plugin); - -// Formatters management -cmsBool _cmsRegisterFormattersPlugin(cmsContext ContextID, cmsPluginBase* Plugin); - -// Tag type management -cmsBool _cmsRegisterTagTypePlugin(cmsContext ContextID, cmsPluginBase* Plugin); - -// Tag management -cmsBool _cmsRegisterTagPlugin(cmsContext ContextID, cmsPluginBase* Plugin); - -// Intent management -cmsBool _cmsRegisterRenderingIntentPlugin(cmsContext ContextID, cmsPluginBase* Plugin); - -// Multi Process elements -cmsBool _cmsRegisterMultiProcessElementPlugin(cmsContext ContextID, cmsPluginBase* Plugin); - -// Optimization -cmsBool _cmsRegisterOptimizationPlugin(cmsContext ContextID, cmsPluginBase* Plugin); - -// Transform -cmsBool _cmsRegisterTransformPlugin(cmsContext ContextID, cmsPluginBase* Plugin); - -// Mutex -cmsBool _cmsRegisterMutexPlugin(cmsContext ContextID, cmsPluginBase* Plugin); - -// --------------------------------------------------------------------------------------------------------- - -// Suballocators. -typedef struct _cmsSubAllocator_chunk_st { - - cmsUInt8Number* Block; - cmsUInt32Number BlockSize; - cmsUInt32Number Used; - - struct _cmsSubAllocator_chunk_st* next; - -} _cmsSubAllocator_chunk; - - -typedef struct { - - cmsContext ContextID; - _cmsSubAllocator_chunk* h; - -} _cmsSubAllocator; - - -_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; - - -// 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 { - - struct _cmsContext_struct* Next; // Points to next context 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 { - - cmsUInt16Number Language; - cmsUInt16Number Country; - - cmsUInt32Number StrW; // Offset to current unicode string - cmsUInt32Number Len; // Length in bytes - -} _cmsMLUentry; - -struct _cms_MLU_struct { - - cmsContext ContextID; - - // The directory - int AllocatedEntries; - int UsedEntries; - _cmsMLUentry* Entries; // Array of pointers to strings allocated in MemPool - - // The Pool - cmsUInt32Number PoolSize; // The maximum allocated size - cmsUInt32Number PoolUsed; // The used size - void* MemPool; // Pointer to begin of memory pool -}; - -// Named color list internal representation -typedef struct { - - char Name[cmsMAX_PATH]; - cmsUInt16Number PCS[3]; - cmsUInt16Number DeviceColorant[cmsMAXCHANNELS]; - -} _cmsNAMEDCOLOR; - -struct _cms_NAMEDCOLORLIST_struct { - - cmsUInt32Number nColors; - cmsUInt32Number Allocated; - cmsUInt32Number ColorantCount; - - char Prefix[33]; // Prefix and suffix are defined to be 32 characters at most - char Suffix[33]; - - _cmsNAMEDCOLOR* List; - - cmsContext ContextID; -}; - - -// ---------------------------------------------------------------------------------- - -// This is the internal struct holding profile details. - -// Maximum supported tags in a profile -#define MAX_TABLE_TAG 100 - -typedef struct _cms_iccprofile_struct { - - // I/O handler - cmsIOHANDLER* IOhandler; - - // The thread ID - cmsContext ContextID; - - // Creation time - struct tm Created; - - // Only most important items found in ICC profiles - cmsUInt32Number Version; - cmsProfileClassSignature DeviceClass; - cmsColorSpaceSignature ColorSpace; - cmsColorSpaceSignature PCS; - cmsUInt32Number RenderingIntent; - - cmsUInt32Number flags; - cmsUInt32Number manufacturer, model; - cmsUInt64Number attributes; - cmsUInt32Number creator; - - cmsProfileID ProfileID; - - // Dictionary - cmsUInt32Number TagCount; - cmsTagSignature TagNames[MAX_TABLE_TAG]; - cmsTagSignature TagLinked[MAX_TABLE_TAG]; // The tag to wich is linked (0=none) - cmsUInt32Number TagSizes[MAX_TABLE_TAG]; // Size on disk - cmsUInt32Number TagOffsets[MAX_TABLE_TAG]; - 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. - // 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 -cmsBool _cmsReadHeader(_cmsICCPROFILE* Icc); -cmsBool _cmsWriteHeader(_cmsICCPROFILE* Icc, cmsUInt32Number UsedSpace); -int _cmsSearchTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, cmsBool lFollowLinks); - -// Tag types -cmsTagTypeHandler* _cmsGetTagTypeHandler(cmsContext ContextID, cmsTagTypeSignature sig); -cmsTagTypeSignature _cmsGetTagTrueType(cmsHPROFILE hProfile, cmsTagSignature sig); -cmsTagDescriptor* _cmsGetTagDescriptor(cmsContext ContextID, cmsTagSignature sig); - -// Error logging --------------------------------------------------------------------------------------------------------- - -void _cmsTagSignature2String(char String[5], cmsTagSignature sig); - -// Interpolation --------------------------------------------------------------------------------------------------------- - -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(cmsContext ContextID, cmsInterpParams* p); - -// Curves ---------------------------------------------------------------------------------------------------------------- - -// This struct holds information about a segment, plus a pointer to the function that implements the evaluation. -// In the case of table-based, Eval pointer is set to NULL - -// The gamma function main structure -struct _cms_curve_struct { - - cmsInterpParams* InterpParams; // Private optimizations for interpolation - - cmsUInt32Number nSegments; // Number of segments in the curve. Zero for a 16-bit based tables - cmsCurveSegment* Segments; // The segments - cmsInterpParams** SegInterp; // Array of private optimizations for interpolation in table-based segments - - cmsParametricCurveEvaluator* Evals; // Evaluators (one per segment) - - // 16 bit Table-based representation follows - cmsUInt32Number nEntries; // Number of table elements - cmsUInt16Number* Table16; // The table itself. -}; - - -// Pipelines & Stages --------------------------------------------------------------------------------------------- - -// A single stage -struct _cmsStage_struct { - - cmsContext ContextID; - - cmsStageSignature Type; // Identifies the stage - cmsStageSignature Implements; // Identifies the *function* of the stage (for optimizations) - - cmsUInt32Number InputChannels; // Input channels -- for optimization purposes - cmsUInt32Number OutputChannels; // Output channels -- for optimization purposes - - _cmsStageEvalFn EvalPtr; // Points to fn that evaluates the stage (always in floating point) - _cmsStageDupElemFn DupElemPtr; // Points to a fn that duplicates the *data* of the stage - _cmsStageFreeElemFn FreePtr; // Points to a fn that sets the *data* of the stage free - - // A generic pointer to whatever memory needed by the stage - void* Data; - - // Maintains linked list (used internally) - struct _cmsStage_struct* Next; -}; - - -// Special Stages (cannot be saved) -cmsStage* _cmsStageAllocLab2XYZ(cmsContext ContextID); -cmsStage* _cmsStageAllocXYZ2Lab(cmsContext ContextID); -cmsStage* _cmsStageAllocLabPrelin(cmsContext ContextID); -cmsStage* _cmsStageAllocLabV2ToV4(cmsContext ContextID); -cmsStage* _cmsStageAllocLabV2ToV4curves(cmsContext ContextID); -cmsStage* _cmsStageAllocLabV4ToV2(cmsContext ContextID); -cmsStage* _cmsStageAllocNamedColor(cmsNAMEDCOLORLIST* NamedColorList, cmsBool UsePCS); -cmsStage* _cmsStageAllocIdentityCurves(cmsContext ContextID, int nChannels); -cmsStage* _cmsStageAllocIdentityCLut(cmsContext ContextID, int nChan); -cmsStage* _cmsStageNormalizeFromLabFloat(cmsContext ContextID); -cmsStage* _cmsStageNormalizeFromXyzFloat(cmsContext ContextID); -cmsStage* _cmsStageNormalizeToLabFloat(cmsContext ContextID); -cmsStage* _cmsStageNormalizeToXyzFloat(cmsContext ContextID); - -// For curve set only -cmsToneCurve** _cmsStageGetPtrToCurveSet(const cmsStage* mpe); - - -// Pipeline Evaluator (in floating point) -typedef void (* _cmsPipelineEvalFloatFn)(const cmsFloat32Number In[], - cmsFloat32Number Out[], - const void* Data); - -struct _cmsPipeline_struct { - - cmsStage* Elements; // Points to elements chain - cmsUInt32Number InputChannels, OutputChannels; - - // Data & evaluators - void *Data; - - _cmsOPTeval16Fn Eval16Fn; - _cmsPipelineEvalFloatFn EvalFloatFn; - _cmsFreeUserDataFn FreeDataFn; - _cmsDupUserDataFn DupDataFn; - - cmsContext ContextID; // Environment - - cmsBool SaveAs8Bits; // Implementation-specific: save as 8 bits if possible -}; - -// LUT reading & creation ------------------------------------------------------------------------------------------- - -// Read tags using low-level function, provide necessary glue code to adapt versions, etc. All those return a brand new copy -// of the LUTS, since ownership of original is up to the profile. The user should free allocated resources. - -cmsPipeline* _cmsReadInputLUT(cmsHPROFILE hProfile, int Intent); -cmsPipeline* _cmsReadOutputLUT(cmsHPROFILE hProfile, int Intent); -cmsPipeline* _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, int Intent); - -// Special values -cmsBool _cmsReadMediaWhitePoint(cmsCIEXYZ* Dest, cmsHPROFILE hProfile); -cmsBool _cmsReadCHAD(cmsMAT3* Dest, cmsHPROFILE hProfile); - -// Profile linker -------------------------------------------------------------------------------------------------- - -cmsPipeline* _cmsLinkProfiles(cmsContext ContextID, - cmsUInt32Number nProfiles, - cmsUInt32Number TheIntents[], - cmsHPROFILE hProfiles[], - cmsBool BPC[], - cmsFloat64Number AdaptationStates[], - cmsUInt32Number dwFlags); - -// Sequence -------------------------------------------------------------------------------------------------------- - -cmsSEQ* _cmsReadProfileSequence(cmsHPROFILE hProfile); -cmsBool _cmsWriteProfileSequence(cmsHPROFILE hProfile, const cmsSEQ* seq); -cmsSEQ* _cmsCompileProfileSequence(cmsContext ContextID, cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[]); - - -// LUT optimization ------------------------------------------------------------------------------------------------ - -cmsUInt16Number _cmsQuantizeVal(cmsFloat64Number i, int MaxSamples); -int _cmsReasonableGridpointsByColorspace(cmsColorSpaceSignature Colorspace, cmsUInt32Number dwFlags); - -cmsBool _cmsEndPointsBySpace(cmsColorSpaceSignature Space, - cmsUInt16Number **White, - cmsUInt16Number **Black, - cmsUInt32Number *nOutputs); - -cmsBool _cmsOptimizePipeline(cmsContext ContextID, - cmsPipeline** Lut, - int Intent, - cmsUInt32Number* InputFormat, - cmsUInt32Number* OutputFormat, - cmsUInt32Number* dwFlags ); - - -// Hi level LUT building ---------------------------------------------------------------------------------------------- - -cmsPipeline* _cmsCreateGamutCheckPipeline(cmsContext ContextID, - cmsHPROFILE hProfiles[], - cmsBool BPC[], - cmsUInt32Number Intents[], - cmsFloat64Number AdaptationStates[], - cmsUInt32Number nGamutPCSposition, - cmsHPROFILE hGamut); - - -// Formatters ------------------------------------------------------------------------------------------------------------ - -#define cmsFLAGS_CAN_CHANGE_FORMATTER 0x02000000 // Allow change buffer format - -cmsBool _cmsFormatterIsFloat(cmsUInt32Number Type); -cmsBool _cmsFormatterIs8bit(cmsUInt32Number Type); - -cmsFormatter _cmsGetFormatter(cmsContext ContextID, - cmsUInt32Number Type, // Specific type, i.e. TYPE_RGB_8 - cmsFormatterDirection Dir, - cmsUInt32Number dwFlags); - - -#ifndef CMS_NO_HALF_SUPPORT - -// Half float -cmsFloat32Number _cmsHalf2Float(cmsUInt16Number h); -cmsUInt16Number _cmsFloat2Half(cmsFloat32Number flt); - -#endif - -// Transform logic ------------------------------------------------------------------------------------------------------ - -struct _cmstransform_struct; - -typedef struct { - - // 1-pixel cache (16 bits only) - cmsUInt16Number CacheIn[cmsMAXCHANNELS]; - cmsUInt16Number CacheOut[cmsMAXCHANNELS]; - -} _cmsCACHE; - - - -// Transformation -typedef struct _cmstransform_struct { - - cmsUInt32Number InputFormat, OutputFormat; // Keep formats for further reference - - // Points to transform code - _cmsTransformFn xform; - - // Formatters, cannot be embedded into LUT because cache - cmsFormatter16 FromInput; - cmsFormatter16 ToOutput; - - cmsFormatterFloat FromInputFloat; - cmsFormatterFloat ToOutputFloat; - - // 1-pixel cache seed for zero as input (16 bits, read only) - _cmsCACHE Cache; - - // A Pipeline holding the full (optimized) transform - cmsPipeline* Lut; - - // A Pipeline holding the gamut check. It goes from the input space to bilevel - cmsPipeline* GamutCheck; - - // Colorant tables - cmsNAMEDCOLORLIST* InputColorant; // Input Colorant table - cmsNAMEDCOLORLIST* OutputColorant; // Colorant table (for n chans > CMYK) - - // Informational only - cmsColorSpaceSignature EntryColorSpace; - cmsColorSpaceSignature ExitColorSpace; - - // White points (informative only) - cmsCIEXYZ EntryWhitePoint; - cmsCIEXYZ ExitWhitePoint; - - // Profiles used to create the transform - cmsSEQ* Sequence; - - cmsUInt32Number dwOriginalFlags; - cmsFloat64Number AdaptationState; - - // The intent of this transform. That is usually the last intent in the profilechain, but may differ - cmsUInt32Number RenderingIntent; - - // An id that uniquely identifies the running context. May be null. - cmsContext ContextID; - - // A user-defined pointer that can be used to store data for transform plug-ins - void* UserData; - _cmsFreeUserDataFn FreeUserData; - -} _cmsTRANSFORM; - -// -------------------------------------------------------------------------------------------------- - -cmsHTRANSFORM _cmsChain2Lab(cmsContext ContextID, - cmsUInt32Number nProfiles, - cmsUInt32Number InputFormat, - cmsUInt32Number OutputFormat, - const cmsUInt32Number Intents[], - const cmsHPROFILE hProfiles[], - const cmsBool BPC[], - const cmsFloat64Number AdaptationStates[], - cmsUInt32Number dwFlags); - - -cmsToneCurve* _cmsBuildKToneCurve(cmsContext ContextID, - cmsUInt32Number nPoints, - cmsUInt32Number nProfiles, - const cmsUInt32Number Intents[], - const cmsHPROFILE hProfiles[], - const cmsBool BPC[], - const cmsFloat64Number AdaptationStates[], - cmsUInt32Number dwFlags); - -cmsBool _cmsAdaptationMatrix(cmsMAT3* r, const cmsMAT3* ConeMatrix, const cmsCIEXYZ* FromIll, const cmsCIEXYZ* ToIll); - -cmsBool _cmsBuildRGB2XYZtransferMatrix(cmsMAT3* r, const cmsCIExyY* WhitePoint, const cmsCIExyYTRIPLE* Primaries); - - -#define _lcms_internal_H -#endif -//<<<+++OPENSOURCE_MUST_END -- cgit v1.2.3