summaryrefslogtreecommitdiffstats
path: root/debian/lcms/lcms-1.19.dfsg2/src/cmsxform.c
diff options
context:
space:
mode:
Diffstat (limited to 'debian/lcms/lcms-1.19.dfsg2/src/cmsxform.c')
-rwxr-xr-xdebian/lcms/lcms-1.19.dfsg2/src/cmsxform.c2018
1 files changed, 2018 insertions, 0 deletions
diff --git a/debian/lcms/lcms-1.19.dfsg2/src/cmsxform.c b/debian/lcms/lcms-1.19.dfsg2/src/cmsxform.c
new file mode 100755
index 00000000..0943fda5
--- /dev/null
+++ b/debian/lcms/lcms-1.19.dfsg2/src/cmsxform.c
@@ -0,0 +1,2018 @@
+//
+// Little cms
+// Copyright (C) 1998-2007 Marti Maria
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the Software
+// is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
+// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+#include "lcms.h"
+
+
+// Transformations stuff
+// -----------------------------------------------------------------------
+
+
+// Interface
+
+cmsHTRANSFORM LCMSEXPORT cmsCreateTransform(cmsHPROFILE Input,
+ DWORD InputFormat,
+ cmsHPROFILE Output,
+ DWORD OutputFormat,
+ int Intent,
+ DWORD dwFlags);
+
+cmsHTRANSFORM LCMSEXPORT cmsCreateProofingTransform(cmsHPROFILE Input,
+ DWORD InputFormat,
+ cmsHPROFILE Output,
+ DWORD OutputFormat,
+ cmsHPROFILE Proofing,
+ int Intent,
+ int ProofingIntent,
+ DWORD dwFlags);
+
+
+void LCMSEXPORT cmsDeleteTransform(cmsHTRANSFORM hTransform);
+
+void LCMSEXPORT cmsDoTransform(cmsHTRANSFORM Transform,
+ LPVOID InputBuffer,
+ LPVOID OutputBuffer, unsigned int Size);
+
+void LCMSEXPORT cmsGetAlarmCodes(int *r, int *g, int *b);
+void LCMSEXPORT cmsSetAlarmCodes(int r, int g, int b);
+LCMSBOOL LCMSEXPORT cmsIsIntentSupported(cmsHPROFILE hProfile,
+ int Intent, int UsedDirection);
+
+// -------------------------------------------------------------------------
+
+
+// Alarm RGB codes
+
+static WORD AlarmR = 0x8fff, AlarmG = 0x8fff, AlarmB = 0x8fff;
+
+// Tag tables, soted by intents
+
+static icTagSignature Device2PCS[] = {icSigAToB0Tag, // Perceptual
+ icSigAToB1Tag, // Relative colorimetric
+ icSigAToB2Tag, // Saturation
+ icSigAToB1Tag }; // Absolute colorimetric
+ // (Relative/WhitePoint)
+
+static icTagSignature PCS2Device[] = {icSigBToA0Tag, // Perceptual
+ icSigBToA1Tag, // Relative colorimetric
+ icSigBToA2Tag, // Saturation
+ icSigBToA1Tag }; // Absolute colorimetric
+ // (Relative/WhitePoint)
+
+
+static icTagSignature Preview[] = {icSigPreview0Tag,
+ icSigPreview1Tag,
+ icSigPreview2Tag,
+ icSigPreview1Tag };
+
+
+
+static volatile double GlobalAdaptationState = 0;
+
+// --------------------------------Stages--------------------------------------
+
+// Following routines does implement several kind of steps inside
+// transform. On building the transform, code chooses adequate.
+
+
+// From Shaper-Matrix to PCS
+
+static
+void ShaperMatrixToPCS(struct _cmstransform_struct *p,
+ WORD In[3], WORD Out[3])
+{
+ cmsEvalMatShaper(p -> InMatShaper, In, Out);
+}
+
+// From LUT to PCS
+
+static
+void LUTtoPCS(struct _cmstransform_struct *p,
+ WORD In[], WORD Out[3])
+{
+ cmsEvalLUT(p -> Device2PCS, In, Out);
+}
+
+// From indexed named color to PCS
+
+static
+void NC2toPCS(struct _cmstransform_struct *p,
+ WORD In[], WORD Out[3])
+{
+ int index = In[0];
+
+ if (index >= p ->NamedColorList-> nColors)
+ cmsSignalError(LCMS_ERRC_WARNING, "Color %d out of range", index);
+ else
+ CopyMemory(Out, p ->NamedColorList->List[index].PCS, 3 * sizeof(WORD));
+}
+
+// From PCS to Shaper-Matrix
+
+static
+void PCStoShaperMatrix(struct _cmstransform_struct *p,
+ WORD In[3], WORD Out[3])
+{
+ cmsEvalMatShaper(p -> OutMatShaper, In, Out);
+}
+
+// From PCS to LUT
+
+static
+void PCStoLUT(struct _cmstransform_struct *p,
+ WORD In[3], WORD Out[])
+{
+ cmsEvalLUT(p -> PCS2Device, In, Out);
+}
+
+
+
+
+// ----------------------- TRANSFORMATIONS --------------------------
+
+
+// Inlining some assignations
+
+#define COPY_3CHANS(to, from) { to[0]=from[0]; to[1]=from[1]; to[2]=from[2]; }
+
+
+// Null transformation, only hold channels
+
+static
+void NullXFORM(_LPcmsTRANSFORM p,
+ LPVOID in,
+ LPVOID out, unsigned int Size)
+{
+ register LPBYTE accum;
+ register LPBYTE output;
+ WORD wIn[MAXCHANNELS];
+ register unsigned int i, n;
+
+
+ accum = (LPBYTE) in;
+ output = (LPBYTE) out;
+ n = Size; // Buffer len
+
+ for (i=0; i < n; i++)
+ {
+ accum = p -> FromInput(p, wIn, accum);
+ output = p -> ToOutput(p, wIn, output);
+ }
+
+}
+
+
+// This is the "normal" proofing transform
+
+static
+void NormalXFORM(_LPcmsTRANSFORM p,
+ LPVOID in,
+ LPVOID out, unsigned int Size)
+{
+ register LPBYTE accum;
+ register LPBYTE output;
+ WORD wIn[MAXCHANNELS], wOut[MAXCHANNELS];
+ WORD wStageABC[3], wPCS[3], wStageLMN[MAXCHANNELS];
+ WORD wGamut[1];
+ register unsigned int i, n;
+
+
+
+ accum = (LPBYTE) in;
+ output = (LPBYTE) out;
+ n = Size; // Buffer len
+
+ for (i=0; i < n; i++)
+ {
+
+ accum = p -> FromInput(p, wIn, accum);
+
+ p -> FromDevice(p, wIn, wStageABC);
+
+ if (p -> Stage1) {
+
+ p -> Stage1(wStageABC, wPCS, &p->m1, &p->of1);
+
+ if (wPCS[0] == 0xFFFF &&
+ wPCS[1] == 0xFFFF &&
+ wPCS[2] == 0xFFFF) {
+
+ // White cutoff
+
+ output = p -> ToOutput((_LPcmsTRANSFORM) p,
+ _cmsWhiteBySpace(cmsGetColorSpace(p -> OutputProfile)),
+ output);
+ continue;
+ }
+ }
+ else
+ COPY_3CHANS(wPCS, wStageABC);
+
+
+ if (p->Gamut) {
+
+ // Gamut check, enabled across CLUT
+
+ cmsEvalLUT(p -> Gamut, wPCS, wGamut);
+
+ if (wGamut[0] >= 1) {
+
+ wOut[0] = AlarmR; // Gamut alarm
+ wOut[1] = AlarmG;
+ wOut[2] = AlarmB;
+ wOut[3] = 0;
+
+ output = p -> ToOutput((_LPcmsTRANSFORM)p, wOut, output);
+ continue;
+ }
+ }
+
+ if (p -> Preview)
+ {
+ WORD wPreview[3]; // PCS
+
+ cmsEvalLUT(p -> Preview, wPCS, wPreview);
+ COPY_3CHANS(wPCS, wPreview);
+ }
+
+ if (p -> Stage2) {
+
+ p -> Stage2(wPCS, wStageLMN, &p->m2, &p->of2);
+
+ if (wPCS[0] == 0xFFFF &&
+ wPCS[1] == 0xFFFF &&
+ wPCS[2] == 0xFFFF) {
+
+ // White cutoff
+
+ output = p -> ToOutput((_LPcmsTRANSFORM)p,
+ _cmsWhiteBySpace(cmsGetColorSpace(p -> OutputProfile)),
+ output);
+
+ continue;
+ }
+
+ }
+ else
+ COPY_3CHANS(wStageLMN, wPCS);
+
+ // Here wOut may come as MAXCHANNELS channels
+
+ p -> ToDevice(p, wStageLMN, wOut);
+
+ output = p -> ToOutput((_LPcmsTRANSFORM)p, wOut, output);
+ }
+}
+
+// Using precalculated LUT
+
+static
+void PrecalculatedXFORM(_LPcmsTRANSFORM p,
+ LPVOID in,
+ LPVOID out, unsigned int Size)
+{
+ register LPBYTE accum;
+ register LPBYTE output;
+ WORD wIn[MAXCHANNELS], wOut[MAXCHANNELS];
+ unsigned int i, n;
+
+
+ accum = (LPBYTE) in;
+ output = (LPBYTE) out;
+ n = Size; // Buffer len
+
+
+ for (i=0; i < n; i++) {
+
+ accum = p -> FromInput(p, wIn, accum);
+
+ // Try to speedup things on plain devicelinks
+
+ if (p ->DeviceLink ->wFlags == LUT_HAS3DGRID) {
+
+ p ->DeviceLink ->CLut16params.Interp3D(wIn, wOut,
+ p ->DeviceLink -> T,
+ &p ->DeviceLink -> CLut16params);
+ }
+ else
+ cmsEvalLUT(p -> DeviceLink, wIn, wOut);
+
+
+ output = p -> ToOutput(p, wOut, output);
+ }
+}
+
+// Auxiliar: Handle precalculated gamut check
+
+static
+void TransformOnePixelWithGamutCheck(_LPcmsTRANSFORM p, WORD wIn[], WORD wOut[])
+{
+ WORD wOutOfGamut;
+
+ cmsEvalLUT(p ->GamutCheck, wIn, &wOutOfGamut);
+
+ if (wOutOfGamut >= 1) {
+
+ ZeroMemory(wOut, sizeof(WORD) * MAXCHANNELS);
+
+ wOut[0] = AlarmR;
+ wOut[1] = AlarmG;
+ wOut[2] = AlarmB;
+
+ }
+ else
+ cmsEvalLUT(p -> DeviceLink, wIn, wOut);
+
+}
+
+
+static
+void PrecalculatedXFORMGamutCheck(_LPcmsTRANSFORM p,
+ LPVOID in,
+ LPVOID out, unsigned int Size)
+{
+ register LPBYTE accum;
+ register LPBYTE output;
+ WORD wIn[MAXCHANNELS], wOut[MAXCHANNELS];
+ register unsigned int i, n;
+
+
+ accum = (LPBYTE) in;
+ output = (LPBYTE) out;
+ n = Size; // Buffer len
+
+ for (i=0; i < n; i++) {
+
+ accum = p -> FromInput(p, wIn, accum);
+
+ TransformOnePixelWithGamutCheck(p, wIn, wOut);
+
+ output = p -> ToOutput(p, wOut, output);
+ }
+}
+
+
+
+// Using precalculated LUT + Cache
+
+static
+void CachedXFORM(_LPcmsTRANSFORM p,
+ LPVOID in,
+ LPVOID out, unsigned int Size)
+{
+ register LPBYTE accum;
+ register LPBYTE output;
+ WORD wIn[MAXCHANNELS], wOut[MAXCHANNELS];
+ register unsigned int i, n;
+ WORD CacheIn[MAXCHANNELS], CacheOut[MAXCHANNELS];
+
+
+ accum = (LPBYTE) in;
+ output = (LPBYTE) out;
+ n = Size; // Buffer len
+
+ // Empty buffers for quick memcmp
+
+ ZeroMemory(wIn, sizeof(WORD) * MAXCHANNELS);
+ ZeroMemory(wOut, sizeof(WORD) * MAXCHANNELS);
+
+
+ LCMS_READ_LOCK(&p ->rwlock);
+ CopyMemory(CacheIn, p ->CacheIn, sizeof(WORD) * MAXCHANNELS);
+ CopyMemory(CacheOut, p ->CacheOut, sizeof(WORD) * MAXCHANNELS);
+ LCMS_UNLOCK(&p ->rwlock);
+
+ for (i=0; i < n; i++) {
+
+ accum = p -> FromInput(p, wIn, accum);
+
+
+ if (memcmp(wIn, CacheIn, sizeof(WORD) * MAXCHANNELS) == 0) {
+
+ CopyMemory(wOut, CacheOut, sizeof(WORD) * MAXCHANNELS);
+ }
+ else {
+
+ // Try to speedup things on plain devicelinks
+
+ if (p ->DeviceLink ->wFlags == LUT_HAS3DGRID) {
+
+ p ->DeviceLink ->CLut16params.Interp3D(wIn, wOut,
+ p ->DeviceLink -> T,
+ &p ->DeviceLink -> CLut16params);
+ }
+ else
+ cmsEvalLUT(p -> DeviceLink, wIn, wOut);
+
+
+ CopyMemory(CacheIn, wIn, sizeof(WORD) * MAXCHANNELS);
+ CopyMemory(CacheOut, wOut, sizeof(WORD) * MAXCHANNELS);
+ }
+
+ output = p -> ToOutput(p, wOut, output);
+ }
+
+
+ LCMS_WRITE_LOCK(&p ->rwlock);
+ CopyMemory(p->CacheIn, CacheIn, sizeof(WORD) * MAXCHANNELS);
+ CopyMemory(p->CacheOut, CacheOut, sizeof(WORD) * MAXCHANNELS);
+ LCMS_UNLOCK(&p ->rwlock);
+
+}
+
+
+
+// Using precalculated LUT + Cache
+
+static
+void CachedXFORMGamutCheck(_LPcmsTRANSFORM p,
+ LPVOID in,
+ LPVOID out, unsigned int Size)
+{
+ register LPBYTE accum;
+ register LPBYTE output;
+ WORD wIn[MAXCHANNELS], wOut[MAXCHANNELS];
+ register unsigned int i, n;
+ WORD CacheIn[MAXCHANNELS], CacheOut[MAXCHANNELS];
+
+
+ accum = (LPBYTE) in;
+ output = (LPBYTE) out;
+ n = Size; // Buffer len
+
+ // Empty buffers for quick memcmp
+
+ ZeroMemory(wIn, sizeof(WORD) * MAXCHANNELS);
+ ZeroMemory(wOut, sizeof(WORD) * MAXCHANNELS);
+
+ LCMS_READ_LOCK(&p ->rwlock);
+ CopyMemory(CacheIn, p ->CacheIn, sizeof(WORD) * MAXCHANNELS);
+ CopyMemory(CacheOut, p ->CacheOut, sizeof(WORD) * MAXCHANNELS);
+ LCMS_UNLOCK(&p ->rwlock);
+
+
+ for (i=0; i < n; i++) {
+
+ accum = p -> FromInput(p, wIn, accum);
+
+ if (memcmp(wIn, CacheIn, sizeof(WORD) * MAXCHANNELS) == 0) {
+
+ CopyMemory(wOut, CacheOut, sizeof(WORD) * MAXCHANNELS);
+ }
+ else {
+
+ TransformOnePixelWithGamutCheck(p, wIn, wOut);
+
+ CopyMemory(CacheIn, wIn, sizeof(WORD) * MAXCHANNELS);
+ CopyMemory(CacheOut, wOut, sizeof(WORD) * MAXCHANNELS);
+ }
+
+ output = p -> ToOutput(p, wOut, output);
+ }
+
+ LCMS_WRITE_LOCK(&p ->rwlock);
+ CopyMemory(p->CacheIn, CacheIn, sizeof(WORD) * MAXCHANNELS);
+ CopyMemory(p->CacheOut, CacheOut, sizeof(WORD) * MAXCHANNELS);
+ LCMS_UNLOCK(&p ->rwlock);
+}
+
+
+// Using smelted Matrix/Shaper
+
+static
+void MatrixShaperXFORM(_LPcmsTRANSFORM p,
+ LPVOID in,
+ LPVOID out, unsigned int Size)
+{
+ register LPBYTE accum;
+ register LPBYTE output;
+ WORD wIn[MAXCHANNELS], wOut[MAXCHANNELS];
+ register unsigned int i, n;
+
+
+ accum = (LPBYTE) in;
+ output = (LPBYTE) out;
+ n = Size; // Buffer len
+
+ for (i=0; i < n; i++)
+ {
+ accum = p -> FromInput(p, wIn, accum);
+ cmsEvalMatShaper(p -> SmeltMatShaper, wIn, wOut);
+ output = p -> ToOutput(p, wOut, output);
+ }
+}
+
+
+// Using Named color input table
+
+static
+void NC2deviceXform(_LPcmsTRANSFORM p,
+ LPVOID in,
+ LPVOID out, unsigned int Size)
+{
+
+ register LPBYTE accum;
+ register LPBYTE output;
+ WORD wIn[MAXCHANNELS], wOut[MAXCHANNELS];
+ register unsigned int i;
+
+
+ accum = (LPBYTE) in;
+ output = (LPBYTE) out;
+
+ for (i=0; i < Size; i++) {
+
+ accum = p -> FromInput(p, wIn, accum);
+ CopyMemory(wOut, p ->NamedColorList->List[wIn[0]].DeviceColorant, sizeof(WORD) * MAXCHANNELS);
+ output = p -> ToOutput(p, wOut, output);
+ }
+
+}
+
+
+
+// --------------------------------------------------------------------------
+// Build a LUT based on shape-matrix method.
+
+
+// Some non-conformant gray profiles are using kTCR as L*,
+// this function converts the curve to XYZ PCS.
+
+static
+void FromLstarToXYZ(LPGAMMATABLE g, LPGAMMATABLE gxyz[3])
+{
+ int i;
+ int nPoints = 4096;
+ cmsCIELab Lab;
+ cmsCIEXYZ XYZ;
+ L16PARAMS L16;
+
+ // Setup interpolation across origin
+ cmsCalcL16Params(g ->nEntries, &L16);
+
+ // Allocate curves
+ gxyz[0] = cmsAllocGamma(nPoints);
+ gxyz[1] = cmsAllocGamma(nPoints);
+ gxyz[2] = cmsAllocGamma(nPoints);
+
+ // Transport from Lab to XYZ
+
+ for (i=0; i < nPoints; i++) {
+
+ WORD val = _cmsQuantizeVal(i, nPoints);
+ WORD w = cmsLinearInterpLUT16(val, g->GammaTable, &L16);
+
+ Lab.L = ((double) 100.0 * w ) / 65535.0;
+ Lab.a = Lab.b = 0;
+
+ cmsLab2XYZ(NULL, &XYZ, &Lab);
+
+ // Should be same curve
+ gxyz[0] ->GammaTable[i] = (WORD) floor((65535.0 * XYZ.X) / D50X + 0.5);
+ gxyz[1] ->GammaTable[i] = (WORD) floor((65535.0 * XYZ.Y) / D50Y + 0.5);
+ gxyz[2] ->GammaTable[i] = (WORD) floor((65535.0 * XYZ.Z) / D50Z + 0.5);
+ }
+}
+
+// Monochrome version
+
+static
+LPMATSHAPER cmsBuildGrayInputMatrixShaper(cmsHPROFILE hProfile)
+{
+ cmsCIEXYZ Illuminant;
+ LPGAMMATABLE GrayTRC, Shapes[3];
+ LPMATSHAPER MatShaper;
+ MAT3 Scale;
+
+ GrayTRC = cmsReadICCGamma(hProfile, icSigGrayTRCTag); // Y
+ if (GrayTRC == NULL) return NULL;
+
+ cmsTakeIluminant(&Illuminant, hProfile);
+
+ if (cmsGetPCS(hProfile) == icSigLabData) {
+
+ // Fixup for Lab monochrome
+ FromLstarToXYZ(GrayTRC, Shapes);
+ }
+ else {
+ Shapes[0] = cmsDupGamma(GrayTRC);
+ Shapes[1] = cmsDupGamma(GrayTRC);
+ Shapes[2] = cmsDupGamma(GrayTRC);
+ }
+
+ if (!Shapes[0] || !Shapes[1] || !Shapes[2])
+ return NULL;
+
+ cmsFreeGamma(GrayTRC);
+
+ // R=G=B as precondition
+
+ VEC3init(&Scale.v[0], Illuminant.X/3, Illuminant.X/3, Illuminant.X/3);
+ VEC3init(&Scale.v[1], Illuminant.Y/3, Illuminant.Y/3, Illuminant.Y/3);
+ VEC3init(&Scale.v[2], Illuminant.Z/3, Illuminant.Z/3, Illuminant.Z/3);
+
+
+ MatShaper = cmsAllocMatShaper(&Scale, Shapes, MATSHAPER_INPUT);
+ cmsFreeGammaTriple(Shapes);
+ return MatShaper;
+
+}
+
+
+// Monochrome as output
+
+static
+LPMATSHAPER cmsBuildGrayOutputMatrixShaper(cmsHPROFILE hProfile)
+{
+ cmsCIEXYZ Illuminant;
+ LPGAMMATABLE GrayTRC, Shapes[3];
+ LPMATSHAPER MatShaper;
+ MAT3 Scale;
+
+ cmsTakeIluminant(&Illuminant, hProfile);
+
+ // That is a special case for non-compliant profiles.
+
+ if (cmsGetPCS(hProfile) == icSigLabData) {
+
+ LPGAMMATABLE Shapes1[3];
+
+ GrayTRC = cmsReadICCGamma(hProfile, icSigGrayTRCTag);
+ if (GrayTRC == NULL) return NULL;
+ FromLstarToXYZ(GrayTRC, Shapes1);
+
+
+
+ // Reversing must be done after curve translation
+
+ Shapes[0] = cmsReverseGamma(Shapes1[0]->nEntries, Shapes1[0]);
+ Shapes[1] = cmsReverseGamma(Shapes1[1]->nEntries, Shapes1[1]);
+ Shapes[2] = cmsReverseGamma(Shapes1[2]->nEntries, Shapes1[2]);
+
+ cmsFreeGammaTriple(Shapes1);
+
+ }
+ else {
+
+ // Normal case
+
+ GrayTRC = cmsReadICCGammaReversed(hProfile, icSigGrayTRCTag); // Y
+ if (GrayTRC == NULL) return NULL;
+
+ Shapes[0] = cmsDupGamma(GrayTRC);
+ Shapes[1] = cmsDupGamma(GrayTRC);
+ Shapes[2] = cmsDupGamma(GrayTRC);
+ }
+
+ if (!Shapes[0] || !Shapes[1] || !Shapes[2])
+ return NULL;
+
+ cmsFreeGamma(GrayTRC);
+
+ VEC3init(&Scale.v[0], 0, 1.0/Illuminant.Y, 0);
+ VEC3init(&Scale.v[1], 0, 1.0/Illuminant.Y, 0);
+ VEC3init(&Scale.v[2], 0, 1.0/Illuminant.Y, 0);
+
+
+ MatShaper = cmsAllocMatShaper(&Scale, Shapes, MATSHAPER_OUTPUT);
+ cmsFreeGammaTriple(Shapes);
+ return MatShaper;
+
+}
+
+
+
+// Input matrix, only in XYZ
+
+LPMATSHAPER cmsBuildInputMatrixShaper(cmsHPROFILE InputProfile)
+{
+ MAT3 DoubleMat;
+ LPGAMMATABLE Shapes[3];
+ LPMATSHAPER InMatSh;
+
+ // Check if this is a grayscale profile. If so, build
+ // appropiate conversion tables. The tables are the PCS
+ // iluminant, scaled across GrayTRC
+
+ if (cmsGetColorSpace(InputProfile) == icSigGrayData)
+ {
+ return cmsBuildGrayInputMatrixShaper(InputProfile);
+ }
+
+ if (!cmsReadICCMatrixRGB2XYZ(&DoubleMat, InputProfile))
+ return NULL;
+
+ Shapes[0] = cmsReadICCGamma(InputProfile, icSigRedTRCTag);
+ Shapes[1] = cmsReadICCGamma(InputProfile, icSigGreenTRCTag);
+ Shapes[2] = cmsReadICCGamma(InputProfile, icSigBlueTRCTag);
+
+ if (!Shapes[0] || !Shapes[1] || !Shapes[2])
+ return NULL;
+
+ InMatSh = cmsAllocMatShaper(&DoubleMat, Shapes, MATSHAPER_INPUT);
+
+ cmsFreeGammaTriple(Shapes);
+
+ return InMatSh;
+}
+
+
+// Output style matrix-shaper
+
+
+LPMATSHAPER cmsBuildOutputMatrixShaper(cmsHPROFILE OutputProfile)
+{
+ MAT3 DoubleMat, DoubleInv;
+ LPGAMMATABLE InverseShapes[3];
+ LPMATSHAPER OutMatSh;
+
+
+
+ if (cmsGetColorSpace(OutputProfile) == icSigGrayData)
+ {
+ return cmsBuildGrayOutputMatrixShaper(OutputProfile);
+ }
+
+
+ if (!cmsReadICCMatrixRGB2XYZ(&DoubleMat, OutputProfile))
+ return NULL;
+
+ if (MAT3inverse(&DoubleMat, &DoubleInv) < 0)
+ return NULL;
+
+
+ InverseShapes[0] = cmsReadICCGammaReversed(OutputProfile, icSigRedTRCTag);
+ InverseShapes[1] = cmsReadICCGammaReversed(OutputProfile, icSigGreenTRCTag);
+ InverseShapes[2] = cmsReadICCGammaReversed(OutputProfile, icSigBlueTRCTag);
+
+ if (InverseShapes[0] == NULL ||
+ InverseShapes[1] == NULL ||
+ InverseShapes[2] == NULL) return NULL;
+
+ OutMatSh = cmsAllocMatShaper(&DoubleInv, InverseShapes, MATSHAPER_OUTPUT);
+
+ cmsFreeGammaTriple(InverseShapes);
+
+ return OutMatSh;
+}
+
+
+
+// This function builds a transform matrix chaining parameters
+
+static
+LCMSBOOL cmsBuildSmeltMatShaper(_LPcmsTRANSFORM p)
+{
+ MAT3 From, To, ToInv, Transfer;
+ LPGAMMATABLE In[3], InverseOut[3];
+
+
+ if (!cmsReadICCMatrixRGB2XYZ(&From, p -> InputProfile))
+ return FALSE;
+
+
+ if (!cmsReadICCMatrixRGB2XYZ(&To, p -> OutputProfile))
+ return FALSE;
+
+ // invert dest
+
+ if (MAT3inverse(&To, &ToInv) < 0)
+ return FALSE;
+
+ // Multiply
+ MAT3per(&Transfer, &ToInv, &From);
+
+
+ // Read gamma curves
+
+ In[0] = cmsReadICCGamma(p -> InputProfile, icSigRedTRCTag);
+ In[1] = cmsReadICCGamma(p -> InputProfile, icSigGreenTRCTag);
+ In[2] = cmsReadICCGamma(p -> InputProfile, icSigBlueTRCTag);
+
+ if (!In[0] || !In[1] || !In[2])
+ return FALSE;
+
+
+ InverseOut[0] = cmsReadICCGammaReversed(p -> OutputProfile, icSigRedTRCTag);
+ InverseOut[1] = cmsReadICCGammaReversed(p -> OutputProfile, icSigGreenTRCTag);
+ InverseOut[2] = cmsReadICCGammaReversed(p -> OutputProfile, icSigBlueTRCTag);
+
+ if (!InverseOut[0] || !InverseOut[1] || !InverseOut[2]) {
+ cmsFreeGammaTriple(In);
+ return FALSE;
+ }
+
+ p -> SmeltMatShaper = cmsAllocMatShaper2(&Transfer, In, InverseOut, MATSHAPER_ALLSMELTED);
+
+ cmsFreeGammaTriple(In);
+ cmsFreeGammaTriple(InverseOut);
+
+ return (p -> SmeltMatShaper != NULL);
+}
+
+
+
+
+// Conversion between PCS ------------------------------------------
+
+// Identifies intent archieved by LUT
+
+static
+int GetPhase(cmsHPROFILE hProfile)
+{
+ switch (cmsGetPCS(hProfile)) {
+
+ case icSigXYZData: return XYZRel;
+
+ case icSigLabData: return LabRel;
+
+ default: cmsSignalError(LCMS_ERRC_ABORTED, "Invalid PCS");
+ }
+
+ return XYZRel;
+}
+
+
+
+
+static
+void TakeConversionRoutines(_LPcmsTRANSFORM p, int DoBPC)
+{
+ cmsCIEXYZ BlackPointIn, WhitePointIn, IlluminantIn;
+ cmsCIEXYZ BlackPointOut, WhitePointOut, IlluminantOut;
+ cmsCIEXYZ BlackPointProof, WhitePointProof, IlluminantProof;
+ MAT3 ChromaticAdaptationMatrixIn, ChromaticAdaptationMatrixOut;
+ MAT3 ChromaticAdaptationMatrixProof;
+
+
+ cmsTakeIluminant(&IlluminantIn, p -> InputProfile);
+ cmsTakeMediaWhitePoint(&WhitePointIn, p -> InputProfile);
+ cmsTakeMediaBlackPoint(&BlackPointIn, p -> InputProfile);
+ cmsReadChromaticAdaptationMatrix(&ChromaticAdaptationMatrixIn, p -> InputProfile);
+
+ cmsTakeIluminant(&IlluminantOut, p -> OutputProfile);
+ cmsTakeMediaWhitePoint(&WhitePointOut, p -> OutputProfile);
+ cmsTakeMediaBlackPoint(&BlackPointOut, p -> OutputProfile);
+ cmsReadChromaticAdaptationMatrix(&ChromaticAdaptationMatrixOut, p -> OutputProfile);
+
+
+ if (p -> Preview == NULL && p ->Gamut == NULL) // Non-proofing
+ {
+ if (p ->Intent == INTENT_PERCEPTUAL ||
+ p ->Intent == INTENT_SATURATION) {
+
+
+ // For v4 profiles, Perceptual PCS has a reference black point
+ // which v2 profiles should scale to.
+
+ if ((cmsGetProfileICCversion(p ->InputProfile) >= 0x4000000) ||
+ (cmsGetProfileICCversion(p ->OutputProfile) >= 0x4000000)) {
+
+ DoBPC = TRUE;
+ }
+ }
+
+ // Black point compensation does not apply to absolute intent
+
+ if (p ->Intent == INTENT_ABSOLUTE_COLORIMETRIC)
+ DoBPC = FALSE;
+
+ // Black point compensation does not apply to devicelink profiles
+
+ if (cmsGetDeviceClass(p ->InputProfile) == icSigLinkClass)
+ DoBPC = FALSE;
+
+ if (cmsGetDeviceClass(p ->OutputProfile) == icSigLinkClass)
+ DoBPC = FALSE;
+
+ if (DoBPC) {
+
+ // Detect Black points
+
+ cmsDetectBlackPoint(&BlackPointIn, p->InputProfile, p->Intent, 0);
+ cmsDetectBlackPoint(&BlackPointOut, p->OutputProfile, p->Intent, 0);
+
+ // If equal black points, then do nothing. This often applies to BP=0
+
+ if (BlackPointIn.X == BlackPointOut.X &&
+ BlackPointIn.Y == BlackPointOut.Y &&
+ BlackPointIn.Z == BlackPointOut.Z)
+ DoBPC = FALSE;
+
+
+ }
+
+ cmsChooseCnvrt(p -> Intent == INTENT_ABSOLUTE_COLORIMETRIC,
+
+ p -> Phase1,
+ &BlackPointIn,
+ &WhitePointIn,
+ &IlluminantIn,
+ &ChromaticAdaptationMatrixIn,
+
+ p -> Phase3,
+ &BlackPointOut,
+ &WhitePointOut,
+ &IlluminantOut,
+ &ChromaticAdaptationMatrixOut,
+
+ DoBPC,
+ p ->AdaptationState,
+ &p->Stage1,
+ &p->m1, &p->of1);
+
+ }
+ else // Proofing
+ {
+
+
+ cmsTakeIluminant(&IlluminantProof, p -> PreviewProfile);
+ cmsTakeMediaWhitePoint(&WhitePointProof, p -> PreviewProfile);
+ cmsTakeMediaBlackPoint(&BlackPointProof, p -> PreviewProfile);
+ cmsReadChromaticAdaptationMatrix(&ChromaticAdaptationMatrixProof, p -> PreviewProfile);
+
+ if (DoBPC) {
+
+ cmsDetectBlackPoint(&BlackPointProof, p->PreviewProfile, p->Intent, 0);
+ cmsDetectBlackPoint(&BlackPointIn, p->InputProfile, p->Intent, 0);
+ cmsDetectBlackPoint(&BlackPointOut, p->OutputProfile, p->Intent, 0);
+
+ // Reality check
+
+ if (BlackPointIn.X == BlackPointProof.X &&
+ BlackPointIn.Y == BlackPointProof.Y &&
+ BlackPointIn.Z == BlackPointProof.Z)
+ DoBPC = FALSE;
+
+
+ }
+
+
+
+ cmsChooseCnvrt(p -> Intent == INTENT_ABSOLUTE_COLORIMETRIC,
+
+ p -> Phase1,
+ &BlackPointIn,
+ &WhitePointIn,
+ &IlluminantIn,
+ &ChromaticAdaptationMatrixIn,
+
+ p -> Phase2,
+ &BlackPointProof,
+ &WhitePointProof,
+ &IlluminantProof,
+ &ChromaticAdaptationMatrixProof,
+ DoBPC,
+ p ->AdaptationState,
+ &p->Stage1,
+ &p->m1, &p->of1);
+
+ cmsChooseCnvrt(p -> ProofIntent == INTENT_ABSOLUTE_COLORIMETRIC,
+
+ p -> Phase2,
+ &BlackPointProof,
+ &WhitePointProof,
+ &IlluminantProof,
+ &ChromaticAdaptationMatrixProof,
+
+ p -> Phase3,
+ &BlackPointOut,
+ &WhitePointOut,
+ &IlluminantOut,
+ &ChromaticAdaptationMatrixOut,
+ 0,
+ 0.0,
+ &p->Stage2,
+ &p->m2, &p->of2);
+ }
+
+}
+
+
+// Check colorspace
+
+static
+LCMSBOOL IsProperColorSpace(cmsHPROFILE hProfile, DWORD dwFormat, LCMSBOOL lUsePCS)
+{
+ int Space = T_COLORSPACE(dwFormat);
+
+ if (Space == PT_ANY) return TRUE;
+
+ if (lUsePCS)
+ return (Space == _cmsLCMScolorSpace(cmsGetPCS(hProfile)));
+ else
+ return (Space == _cmsLCMScolorSpace(cmsGetColorSpace(hProfile)));
+}
+
+
+// Auxiliary: allocate transform struct and set to defaults
+
+static
+_LPcmsTRANSFORM AllocEmptyTransform(void)
+{
+ // Allocate needed memory
+
+ _LPcmsTRANSFORM p = (_LPcmsTRANSFORM) _cmsMalloc(sizeof(_cmsTRANSFORM));
+ if (!p) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "cmsCreateTransform: _cmsMalloc() failed");
+ return NULL;
+ }
+
+ ZeroMemory(p, sizeof(_cmsTRANSFORM));
+
+ // Initialize default methods
+
+ p -> xform = NULL;
+ p -> Intent = INTENT_PERCEPTUAL;
+ p -> ProofIntent = INTENT_ABSOLUTE_COLORIMETRIC;
+ p -> DoGamutCheck = FALSE;
+ p -> InputProfile = NULL;
+ p -> OutputProfile = NULL;
+ p -> PreviewProfile = NULL;
+ p -> Preview = NULL;
+ p -> Gamut = NULL;
+ p -> DeviceLink = NULL;
+ p -> InMatShaper = NULL;
+ p -> OutMatShaper = NULL;
+ p -> SmeltMatShaper = NULL;
+ p -> NamedColorList = NULL;
+ p -> EntryColorSpace = (icColorSpaceSignature) 0;
+ p -> ExitColorSpace = (icColorSpaceSignature) 0;
+ p -> AdaptationState = GlobalAdaptationState;
+
+ LCMS_CREATE_LOCK(&p->rwlock);
+
+ return p;
+}
+
+
+// Identify whatever a transform is to be cached
+
+static
+void SetPrecalculatedTransform(_LPcmsTRANSFORM p)
+{
+ if ((p->dwOriginalFlags & cmsFLAGS_GAMUTCHECK) && p ->GamutCheck != NULL) {
+
+ p -> xform = PrecalculatedXFORMGamutCheck;
+
+ if (!(p->dwOriginalFlags & cmsFLAGS_NOTCACHE)) {
+
+ ZeroMemory(p ->CacheIn, sizeof(WORD) * MAXCHANNELS);
+ TransformOnePixelWithGamutCheck(p, p->CacheIn, p ->CacheOut);
+ p ->xform = CachedXFORMGamutCheck;
+ }
+
+ }
+ else {
+
+ p -> xform = PrecalculatedXFORM;
+
+ if (!(p->dwOriginalFlags & cmsFLAGS_NOTCACHE)) {
+
+ ZeroMemory(p ->CacheIn, sizeof(WORD) * MAXCHANNELS);
+ cmsEvalLUT(p ->DeviceLink, p->CacheIn, p ->CacheOut);
+ p ->xform = CachedXFORM;
+ }
+ }
+}
+
+
+// Transform is identified as device-link
+static
+cmsHPROFILE CreateDeviceLinkTransform(_LPcmsTRANSFORM p)
+{
+
+ if (!IsProperColorSpace(p->InputProfile, p->InputFormat, FALSE)) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "Device link is operating on wrong colorspace on input");
+ return NULL;
+ }
+
+ if (!IsProperColorSpace(p->InputProfile, p->OutputFormat, TRUE)) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "Device link is operating on wrong colorspace on output");
+ return NULL;
+ }
+
+ // Device link does only have AToB0Tag (ICC-Spec 1998/09)
+
+ p->DeviceLink = cmsReadICCLut(p->InputProfile, icSigAToB0Tag);
+
+ if (!p->DeviceLink) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "Noncompliant device-link profile");
+ cmsDeleteTransform((cmsHTRANSFORM) p);
+ return NULL;
+ }
+
+ if (p ->PreviewProfile != NULL) {
+ cmsSignalError(LCMS_ERRC_WARNING, "Proofing not supported on device link transforms");
+ }
+
+ if (p ->OutputProfile != NULL) {
+ cmsSignalError(LCMS_ERRC_WARNING, "Output profile should be NULL, since this is a device-link transform");
+ }
+
+ p -> Phase1 = -1;
+ p -> Phase2 = -1;
+ p -> Phase3 = -1;
+
+ SetPrecalculatedTransform(p);
+
+ p -> EntryColorSpace = cmsGetColorSpace(p -> InputProfile);
+ p -> ExitColorSpace = cmsGetPCS(p -> InputProfile);
+
+ if (p ->EntryColorSpace == icSigRgbData ||
+ p ->EntryColorSpace == icSigCmyData) {
+
+ p->DeviceLink -> CLut16params.Interp3D = cmsTetrahedralInterp16;
+ }
+
+ // Precalculated device-link profile is ready
+ return (cmsHTRANSFORM) p;
+}
+
+
+// Transform that includes proofing
+static
+void CreateProof(_LPcmsTRANSFORM p, icTagSignature *ToTagPtr)
+
+{
+ icTagSignature ProofTag;
+
+ if (p -> dwOriginalFlags & cmsFLAGS_SOFTPROOFING) {
+
+ // Apr-15, 2002 - Too much profiles does have bogus content
+ // on preview tag, so I do compute it by my own.
+
+ p -> Preview = _cmsComputeSoftProofLUT(p ->PreviewProfile, p ->Intent);
+ p -> Phase2 = LabRel;
+
+ // That's a proofing transfor, so use second intent for output.
+
+ *ToTagPtr = PCS2Device[p->ProofIntent];
+
+ if (p -> Preview == NULL) {
+
+ ProofTag = Preview[p -> Intent];
+
+ if (!cmsIsTag(p ->PreviewProfile, ProofTag)) {
+
+ ProofTag = Preview[0];
+ if (!cmsIsTag(p ->PreviewProfile, ProofTag))
+ ProofTag = (icTagSignature)0;
+ }
+
+ if (ProofTag) {
+
+ p -> Preview = cmsReadICCLut(p ->PreviewProfile, ProofTag);
+ p -> Phase2 = GetPhase(p ->PreviewProfile);
+
+ }
+ else
+ {
+ p -> Preview = NULL;
+ p ->PreviewProfile = NULL;
+ cmsSignalError(LCMS_ERRC_WARNING, "Sorry, the proof profile has not previewing capabilities");
+ }
+ }
+
+ }
+
+
+ // Aug-31, 2001 - Too much profiles does have bogus content
+ // on gamut tag, so I do compute it by my own.
+
+ if ((p -> dwOriginalFlags & cmsFLAGS_GAMUTCHECK) && (p -> dwOriginalFlags & cmsFLAGS_NOTPRECALC)) {
+
+
+ p -> Gamut = _cmsComputeGamutLUT(p->PreviewProfile, p ->Intent);
+ p -> Phase2 = LabRel;
+
+ if (p -> Gamut == NULL) {
+
+ // Profile goes only in one direction... try to see
+ // if profile has the tag, and use it, no matter it
+ // could be bogus. This is the last chance!
+
+ if (cmsIsTag(p ->PreviewProfile, icSigGamutTag)) {
+
+ p -> Gamut = cmsReadICCLut(p ->PreviewProfile, icSigGamutTag);
+
+ }
+ else {
+
+ // Nope, cannot be done.
+
+ cmsSignalError(LCMS_ERRC_WARNING, "Sorry, the proof profile has not gamut checking capabilities");
+ p -> Gamut = NULL;
+ }
+ }
+
+ }
+
+}
+
+// Choose the adequate transform routine
+
+static
+_LPcmsTRANSFORM PickTransformRoutine(_LPcmsTRANSFORM p,
+ icTagSignature *FromTagPtr,
+ icTagSignature *ToTagPtr)
+{
+
+
+
+
+ // Is a named color profile?
+ if (cmsGetDeviceClass(p->InputProfile) == icSigNamedColorClass) {
+
+ // Yes, and used as input
+ p ->FromDevice = NC2toPCS;
+ }
+ else {
+ // Can we optimize matrix-shaper only transform?
+
+ if ((*FromTagPtr == 0) &&
+ (*ToTagPtr == 0) &&
+ (!p->PreviewProfile) &&
+ (p -> Intent != INTENT_ABSOLUTE_COLORIMETRIC) &&
+ (p -> EntryColorSpace == icSigRgbData) &&
+ (p -> ExitColorSpace == icSigRgbData) &&
+ !(p -> dwOriginalFlags & cmsFLAGS_BLACKPOINTCOMPENSATION)) {
+
+ // Yes... try to smelt matrix-shapers
+ p -> xform = MatrixShaperXFORM;
+ p -> dwOriginalFlags |= cmsFLAGS_NOTPRECALC;
+
+ if (!cmsBuildSmeltMatShaper(p))
+ {
+ cmsSignalError(LCMS_ERRC_ABORTED, "unable to smelt shaper-matrix, required tags missing");
+ return NULL;
+ }
+
+ p -> Phase1 = p -> Phase3 = XYZRel;
+ return p;
+
+ }
+
+ // No, is a transform involving LUT
+
+ if (*FromTagPtr != 0) {
+
+ p -> FromDevice = LUTtoPCS;
+ p -> Device2PCS = cmsReadICCLut(p -> InputProfile, *FromTagPtr);
+ if (!p -> Device2PCS) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "profile is unsuitable for input");
+ return NULL;
+ }
+
+ }
+ else
+ {
+ p -> FromDevice = ShaperMatrixToPCS;
+ p -> InMatShaper = cmsBuildInputMatrixShaper(p -> InputProfile);
+
+ if (!p ->InMatShaper) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "profile is unsuitable for input");
+ return NULL;
+ }
+
+ p -> Phase1 = XYZRel;
+
+ }
+ }
+
+ if (*ToTagPtr != 0) {
+
+ p -> ToDevice = PCStoLUT;
+ p -> PCS2Device = cmsReadICCLut(p -> OutputProfile, *ToTagPtr);
+ if (!p -> PCS2Device) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "profile is unsuitable for output");
+ return NULL;
+ }
+
+ }
+ else
+ {
+ p -> ToDevice = PCStoShaperMatrix;
+ p -> OutMatShaper = cmsBuildOutputMatrixShaper(p->OutputProfile);
+
+ if (!p -> OutMatShaper) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "profile is unsuitable for output");
+ return NULL;
+ }
+ p -> Phase3 = XYZRel;
+
+ }
+
+
+ return p;
+}
+
+
+
+
+// Create a transform.
+
+cmsHTRANSFORM LCMSEXPORT cmsCreateProofingTransform(cmsHPROFILE InputProfile,
+ DWORD InputFormat,
+ cmsHPROFILE OutputProfile,
+ DWORD OutputFormat,
+ cmsHPROFILE ProofingProfile,
+ int nIntent,
+ int ProofingIntent,
+ DWORD dwFlags)
+
+{
+ _LPcmsTRANSFORM p;
+ icTagSignature FromTag;
+ icTagSignature ToTag;
+
+ if (nIntent < 0 || nIntent > 3 ||
+ ProofingIntent < 0 || ProofingIntent > 3) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "cmsCreateTransform: intent mismatch");
+ return NULL;
+ }
+
+ p = AllocEmptyTransform();
+ if (p == NULL) return NULL;
+
+ p -> xform = NormalXFORM;
+ p -> Intent = nIntent;
+ p -> ProofIntent = ProofingIntent;
+ p -> DoGamutCheck = FALSE;
+ p -> InputProfile = InputProfile;
+ p -> OutputProfile = OutputProfile;
+ p -> PreviewProfile = ProofingProfile;
+ p -> InputFormat = InputFormat;
+ p -> OutputFormat = OutputFormat;
+ p -> dwOriginalFlags = dwFlags;
+
+ p -> lInputV4Lab = p ->lOutputV4Lab = FALSE;
+
+
+ p -> FromInput = _cmsIdentifyInputFormat(p, InputFormat);
+ p -> ToOutput = _cmsIdentifyOutputFormat(p, OutputFormat);
+
+ // Null transform can be done without profiles
+ if ((p->dwOriginalFlags & cmsFLAGS_NULLTRANSFORM) ||
+ ((InputProfile == NULL) &&
+ (OutputProfile == NULL))) {
+
+ p -> xform = NullXFORM;
+ return (cmsHTRANSFORM) p;
+ }
+
+ // From here we need at least one input profile
+ if (InputProfile == NULL) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "Input profile cannot be NULL!");
+ cmsDeleteTransform((cmsHTRANSFORM) p);
+ return NULL;
+ }
+
+
+ // Device link are means to store precalculated transform grids.
+ if (cmsGetDeviceClass(InputProfile) == icSigLinkClass) {
+
+ return CreateDeviceLinkTransform(p);
+ }
+
+ if (!IsProperColorSpace(InputProfile, InputFormat, FALSE)) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "Input profile is operating on wrong colorspace");
+ cmsDeleteTransform((cmsHTRANSFORM) p);
+ return NULL;
+ }
+
+ p ->EntryColorSpace = cmsGetColorSpace(InputProfile);
+
+ // Oct-21-2002: Added named color transforms
+ if (cmsGetDeviceClass(InputProfile) == icSigNamedColorClass) {
+
+ if (p ->NamedColorList == NULL)
+ p ->NamedColorList = cmsAllocNamedColorList(0);
+
+ cmsReadICCnamedColorList(p, InputProfile, icSigNamedColor2Tag);
+
+ // Special case. If output profile == NULL, then the transform gives
+ // device values from named colors.
+
+ if (OutputProfile == NULL) {
+
+ p ->ExitColorSpace = p -> EntryColorSpace;
+ p ->xform = NC2deviceXform;
+ return (cmsHTRANSFORM) p;
+ }
+
+ // Named color doesn't precalc anything
+ p -> dwOriginalFlags |= cmsFLAGS_NOTPRECALC;
+ }
+
+
+ // From here we need also output profile.
+ if (OutputProfile == NULL) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "Output profile cannot be NULL!");
+ cmsDeleteTransform((cmsHTRANSFORM) p);
+ return NULL;
+ }
+
+
+ if (!IsProperColorSpace(OutputProfile, OutputFormat, FALSE)) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "Output profile is operating on wrong colorspace");
+ cmsDeleteTransform((cmsHTRANSFORM) p);
+ return NULL;
+ }
+
+ p -> ExitColorSpace = cmsGetColorSpace(OutputProfile);
+
+ // Named color only on input
+ if (cmsGetDeviceClass(OutputProfile) == icSigNamedColorClass) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "Named color profiles are not supported as output");
+ cmsDeleteTransform((cmsHTRANSFORM) p);
+ return NULL;
+ }
+
+ p -> Phase1 = GetPhase(InputProfile);
+ p -> Phase2 = -1;
+ p -> Phase3 = GetPhase(OutputProfile);
+
+ // Try to locate a LUT
+
+ FromTag = Device2PCS[nIntent];
+ ToTag = PCS2Device[nIntent];
+
+ if (!cmsIsTag(InputProfile, FromTag)) {
+
+ FromTag = Device2PCS[0];
+
+ if (!cmsIsTag(InputProfile, FromTag)) {
+ FromTag = (icTagSignature)0;
+ }
+ }
+
+ // If proofing is needed, add required tags/parameters
+ if (ProofingProfile)
+ CreateProof(p, &ToTag);
+
+
+ if (!cmsIsTag(OutputProfile, ToTag)) {
+
+ ToTag = PCS2Device[0];
+
+ // 12-Dec-2003, Abstract profiles can be placed as output and still using AToB0
+ if (cmsGetDeviceClass(OutputProfile) == icSigAbstractClass) {
+
+ if (!cmsIsTag(OutputProfile, ToTag)) {
+ ToTag = (icTagSignature) icSigAToB0Tag;
+ }
+ }
+
+ if (!cmsIsTag(OutputProfile, ToTag))
+ ToTag = (icTagSignature)0;
+ }
+
+
+ if (p-> dwOriginalFlags & cmsFLAGS_MATRIXINPUT)
+ FromTag = (icTagSignature)0;
+
+ if (p -> dwOriginalFlags & cmsFLAGS_MATRIXOUTPUT)
+ ToTag = (icTagSignature)0;
+
+
+
+ if (PickTransformRoutine(p, &FromTag, &ToTag) == NULL) {
+
+ cmsDeleteTransform((cmsHTRANSFORM) p);
+ return NULL;
+
+ }
+
+ TakeConversionRoutines(p, dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION);
+
+ if (!(p -> dwOriginalFlags & cmsFLAGS_NOTPRECALC)) {
+
+ LPLUT DeviceLink;
+ LPLUT GamutCheck = NULL;
+
+
+ if (p ->EntryColorSpace == icSigCmykData &&
+ p ->ExitColorSpace == icSigCmykData &&
+ (dwFlags & cmsFLAGS_PRESERVEBLACK)) {
+
+ DeviceLink = _cmsPrecalculateBlackPreservingDeviceLink((cmsHTRANSFORM) p, dwFlags);
+
+ // Cannot be done at all?
+ if (DeviceLink == NULL)
+ DeviceLink = _cmsPrecalculateDeviceLink((cmsHTRANSFORM) p, dwFlags);
+
+ }
+ else {
+
+ DeviceLink = _cmsPrecalculateDeviceLink((cmsHTRANSFORM) p, dwFlags);
+ }
+
+ // Allow to specify cmsFLAGS_GAMUTCHECK, even if no proofing profile is given
+ if ((p ->PreviewProfile != NULL) && (p -> dwOriginalFlags & cmsFLAGS_GAMUTCHECK)) {
+
+ GamutCheck = _cmsPrecalculateGamutCheck((cmsHTRANSFORM) p);
+ }
+
+ // If input colorspace is Rgb, Cmy, then use tetrahedral interpolation
+ // for speed reasons (it only works well on spaces on Luma is diagonal, and
+ // not if luma is in separate channel)
+ if (p ->EntryColorSpace == icSigRgbData ||
+ p ->EntryColorSpace == icSigCmyData) {
+
+
+ cmsCalcCLUT16ParamsEx(DeviceLink->CLut16params.nSamples,
+ DeviceLink->CLut16params.nInputs,
+ DeviceLink->CLut16params.nOutputs,
+ TRUE, &DeviceLink->CLut16params);
+
+ }
+
+ // If this is a 8-bit transform, optimize LUT further.
+
+ if ((T_BYTES(InputFormat) == 1) && (T_CHANNELS(InputFormat) == 3)) {
+
+ DeviceLink = _cmsBlessLUT8(DeviceLink);
+ if (DeviceLink == NULL) return NULL;
+
+ }
+
+
+ p ->GamutCheck = GamutCheck;
+
+ if (DeviceLink) {
+
+ p ->DeviceLink = DeviceLink;
+
+ if ((nIntent != INTENT_ABSOLUTE_COLORIMETRIC) &&
+ !(p -> dwOriginalFlags & cmsFLAGS_NOWHITEONWHITEFIXUP))
+
+ _cmsFixWhiteMisalignment(p);
+
+ }
+ else
+ {
+
+ cmsSignalError(LCMS_ERRC_ABORTED,
+ "Cannot precalculate %d->%d channels transform!",
+ T_CHANNELS(InputFormat), T_CHANNELS(OutputFormat));
+
+ cmsDeleteTransform(p);
+ return NULL;
+ }
+
+
+ SetPrecalculatedTransform(p);
+
+
+ }
+
+ // Re-Identify formats
+ p -> FromInput = _cmsIdentifyInputFormat(p, InputFormat);
+ p -> ToOutput = _cmsIdentifyOutputFormat(p, OutputFormat);
+
+
+ return p;
+}
+
+
+// Wrapper por simpler non-proofing transforms.
+
+cmsHTRANSFORM LCMSEXPORT cmsCreateTransform(cmsHPROFILE Input,
+ DWORD InputFormat,
+ cmsHPROFILE Output,
+ DWORD OutputFormat,
+ int Intent,
+ DWORD dwFlags)
+
+{
+ return cmsCreateProofingTransform(Input, InputFormat,
+ Output, OutputFormat,
+ NULL,
+ Intent, INTENT_ABSOLUTE_COLORIMETRIC,
+ dwFlags);
+}
+
+
+// Profiles are *NOT* closed
+
+void LCMSEXPORT cmsDeleteTransform(cmsHTRANSFORM hTransform)
+{
+ _LPcmsTRANSFORM p = (_LPcmsTRANSFORM) (LPSTR) hTransform;
+
+ if (p -> Device2PCS)
+ cmsFreeLUT(p -> Device2PCS);
+ if (p -> PCS2Device)
+ cmsFreeLUT(p -> PCS2Device);
+ if (p -> Gamut)
+ cmsFreeLUT(p -> Gamut);
+ if (p -> Preview)
+ cmsFreeLUT(p -> Preview);
+ if (p -> DeviceLink)
+ cmsFreeLUT(p -> DeviceLink);
+ if (p -> InMatShaper)
+ cmsFreeMatShaper(p -> InMatShaper);
+ if (p -> OutMatShaper)
+ cmsFreeMatShaper(p -> OutMatShaper);
+ if (p -> SmeltMatShaper)
+ cmsFreeMatShaper(p -> SmeltMatShaper);
+ if (p ->NamedColorList)
+ cmsFreeNamedColorList(p ->NamedColorList);
+ if (p -> GamutCheck)
+ cmsFreeLUT(p -> GamutCheck);
+
+ LCMS_FREE_LOCK(&p->rwlock);
+
+ _cmsFree((void *) p);
+}
+
+
+// Apply transform code
+void LCMSEXPORT cmsDoTransform(cmsHTRANSFORM Transform,
+ LPVOID InputBuffer,
+ LPVOID OutputBuffer, unsigned int Size)
+
+{
+
+ _LPcmsTRANSFORM p = (_LPcmsTRANSFORM) (LPSTR) Transform;
+
+ p -> StrideIn = p -> StrideOut = Size;
+
+ p -> xform(p, InputBuffer, OutputBuffer, Size);
+
+}
+
+
+void LCMSEXPORT cmsSetAlarmCodes(int r, int g, int b)
+{
+ AlarmR = RGB_8_TO_16(r);
+ AlarmG = RGB_8_TO_16(g);
+ AlarmB = RGB_8_TO_16(b);
+}
+
+void LCMSEXPORT cmsGetAlarmCodes(int *r, int *g, int *b)
+{
+ *r = RGB_16_TO_8(AlarmR);
+ *g = RGB_16_TO_8(AlarmG);
+ *b = RGB_16_TO_8(AlarmB);
+}
+
+// Returns TRUE if the profile is implemented as matrix-shaper
+
+LCMSBOOL LCMSEXPORT _cmsIsMatrixShaper(cmsHPROFILE hProfile)
+{
+ switch (cmsGetColorSpace(hProfile)) {
+
+ case icSigGrayData:
+
+ return cmsIsTag(hProfile, icSigGrayTRCTag);
+
+ case icSigRgbData:
+
+ return (cmsIsTag(hProfile, icSigRedColorantTag) &&
+ cmsIsTag(hProfile, icSigGreenColorantTag) &&
+ cmsIsTag(hProfile, icSigBlueColorantTag) &&
+ cmsIsTag(hProfile, icSigRedTRCTag) &&
+ cmsIsTag(hProfile, icSigGreenTRCTag) &&
+ cmsIsTag(hProfile, icSigBlueTRCTag));
+
+ default:
+
+ return FALSE;
+ }
+}
+
+
+LCMSBOOL LCMSEXPORT cmsIsIntentSupported(cmsHPROFILE hProfile,
+ int Intent, int UsedDirection)
+{
+
+ icTagSignature* TagTable;
+
+ // Device link profiles only implements the intent in header
+
+ if (cmsGetDeviceClass(hProfile) != icSigLinkClass) {
+
+ switch (UsedDirection) {
+
+ case LCMS_USED_AS_INPUT: TagTable = Device2PCS; break;
+ case LCMS_USED_AS_OUTPUT:TagTable = PCS2Device; break;
+ case LCMS_USED_AS_PROOF: TagTable = Preview; break;
+
+ default:
+ cmsSignalError(LCMS_ERRC_ABORTED, "Unexpected direction (%d)", UsedDirection);
+ return FALSE;
+ }
+
+ if (cmsIsTag(hProfile, TagTable[Intent])) return TRUE;
+ return _cmsIsMatrixShaper(hProfile);
+ }
+
+ return (cmsTakeRenderingIntent(hProfile) == Intent);
+}
+
+// Multiple profile transform.
+static
+int MultiprofileSampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
+{
+ cmsHTRANSFORM* Transforms = (cmsHTRANSFORM*) Cargo;
+ int i;
+
+ cmsDoTransform(Transforms[0], In, Out, 1);
+
+ for (i=1; Transforms[i]; i++)
+ cmsDoTransform(Transforms[i], Out, Out, 1);
+
+
+
+ return TRUE;
+}
+
+
+static
+int IsAllowedInSingleXform(icProfileClassSignature aClass)
+{
+ return (aClass == icSigInputClass) ||
+ (aClass == icSigDisplayClass) ||
+ (aClass == icSigOutputClass) ||
+ (aClass == icSigColorSpaceClass);
+}
+
+
+// A multiprofile transform does chain several profiles into a single
+// devicelink. It couls also be used to merge named color profiles into
+// a single database.
+
+
+cmsHTRANSFORM LCMSEXPORT cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles[],
+ int nProfiles,
+ DWORD dwInput,
+ DWORD dwOutput,
+ int Intent,
+ DWORD dwFlags)
+{
+ cmsHTRANSFORM Transforms[257];
+ DWORD dwPrecalcFlags = (dwFlags|cmsFLAGS_NOTPRECALC|cmsFLAGS_NOTCACHE);
+ DWORD FormatInput, FormatOutput;
+ cmsHPROFILE hLab, hXYZ, hProfile;
+ icColorSpaceSignature ColorSpace, CurrentColorSpace;
+ icColorSpaceSignature ColorSpaceIn, ColorSpaceOut;
+ LPLUT Grid;
+ int nGridPoints, ChannelsInput, ChannelsOutput = 3, i;
+ _LPcmsTRANSFORM p;
+ int nNamedColor;
+
+ if (nProfiles > 255) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "What are you trying to do with more that 255 profiles?!?, of course aborted");
+ return NULL;
+ }
+
+ // There is a simple case with just two profiles, try to catch it in order of getting
+ // black preservation to work on this function, at least with two profiles.
+
+
+ if (nProfiles == 2) {
+
+ icProfileClassSignature Class1 = cmsGetDeviceClass(hProfiles[0]);
+ icProfileClassSignature Class2 = cmsGetDeviceClass(hProfiles[1]);
+
+ // Only input, output and display are allowed
+
+ if (IsAllowedInSingleXform(Class1) &&
+ IsAllowedInSingleXform(Class2))
+ return cmsCreateTransform(hProfiles[0], dwInput, hProfiles[1], dwOutput, Intent, dwFlags);
+ }
+
+
+ // Creates a phantom transform for latter filling
+ p = (_LPcmsTRANSFORM) cmsCreateTransform(NULL, dwInput,
+ NULL, dwOutput, Intent, cmsFLAGS_NULLTRANSFORM);
+
+ // If user wants null one, give it
+ if (dwFlags & cmsFLAGS_NULLTRANSFORM) return (cmsHPROFILE) p;
+
+ // Is a bunch of named color profiles?
+ nNamedColor = 0;
+ for (i=0; i < nProfiles; i++) {
+ if (cmsGetDeviceClass(hProfiles[i]) == icSigNamedColorClass)
+ nNamedColor++;
+ }
+
+
+ if (nNamedColor == nProfiles) {
+
+ // Yes, only named color. Create a named color-device
+ // and append to named color table
+
+ cmsDeleteTransform((cmsHTRANSFORM) p);
+
+ p = (_LPcmsTRANSFORM) cmsCreateTransform(hProfiles[0], dwInput, NULL, dwOutput, Intent, dwFlags);
+ for (i=1; i < nProfiles; i++) {
+ cmsReadICCnamedColorList(p, hProfiles[i], icSigNamedColor2Tag);
+ }
+
+ return p; // Ok, done so far
+ }
+ else
+ if (nNamedColor > 0) {
+
+ cmsDeleteTransform((cmsHTRANSFORM) p);
+ cmsSignalError(LCMS_ERRC_ABORTED, "Could not mix named color profiles with other types in multiprofile transform");
+ return NULL;
+ }
+
+
+ // We will need a 3DCLUT for device link
+ Grid = cmsAllocLUT();
+ if (!Grid) return NULL;
+
+ // This one is our PCS (Always Lab)
+ hLab = cmsCreateLabProfile(NULL);
+ hXYZ = cmsCreateXYZProfile();
+
+ if (!hLab || !hXYZ) goto ErrorCleanup;
+
+ // Take some info....
+
+ p ->EntryColorSpace = CurrentColorSpace = cmsGetColorSpace(hProfiles[0]);
+
+
+ for (i=0; i < nProfiles; i++) {
+
+ int lIsDeviceLink, lIsInput;
+
+ // Check colorspace
+
+ hProfile = hProfiles[i];
+ lIsDeviceLink = (cmsGetDeviceClass(hProfile) == icSigLinkClass);
+ lIsInput = (CurrentColorSpace != icSigXYZData) &&
+ (CurrentColorSpace != icSigLabData);
+
+ if (lIsInput || lIsDeviceLink) {
+
+ ColorSpaceIn = cmsGetColorSpace(hProfile);
+ ColorSpaceOut = cmsGetPCS(hProfile);
+
+ }
+ else {
+
+ ColorSpaceIn = cmsGetPCS(hProfile);
+ ColorSpaceOut = cmsGetColorSpace(hProfile);
+ }
+
+ ChannelsInput = _cmsChannelsOf(ColorSpaceIn);
+ ChannelsOutput = _cmsChannelsOf(ColorSpaceOut);
+
+ FormatInput = BYTES_SH(2)|CHANNELS_SH(ChannelsInput);
+ FormatOutput = BYTES_SH(2)|CHANNELS_SH(ChannelsOutput);
+
+ ColorSpace = ColorSpaceIn;
+
+
+ if (ColorSpace == CurrentColorSpace) {
+
+ if (lIsDeviceLink) {
+
+ Transforms[i] = cmsCreateTransform(hProfile, FormatInput,
+ NULL, FormatOutput,
+ Intent, dwPrecalcFlags);
+ }
+
+ else {
+
+ if (lIsInput) {
+
+ Transforms[i] = cmsCreateTransform(hProfile, FormatInput,
+ (ColorSpaceOut == icSigLabData ? hLab : hXYZ), FormatOutput,
+ Intent, dwPrecalcFlags);
+ }
+ else {
+ Transforms[i] = cmsCreateTransform((ColorSpaceIn == icSigLabData ? hLab : hXYZ), FormatInput,
+ hProfile, FormatOutput,
+ Intent, dwPrecalcFlags);
+
+ }
+ }
+
+
+ }
+ else // Can come from pcs?
+ if (CurrentColorSpace == icSigXYZData) {
+
+ Transforms[i] = cmsCreateTransform(hXYZ, FormatInput,
+ hProfile, FormatOutput,
+ Intent, dwPrecalcFlags);
+
+ }
+ else
+ if (CurrentColorSpace == icSigLabData) {
+
+ Transforms[i] = cmsCreateTransform(hLab, FormatInput,
+ hProfile, FormatOutput,
+ Intent, dwPrecalcFlags);
+
+ }
+ else {
+ cmsSignalError(LCMS_ERRC_ABORTED, "cmsCreateMultiprofileTransform: ColorSpace mismatch");
+ goto ErrorCleanup;
+ }
+
+ CurrentColorSpace = ColorSpaceOut;
+
+ }
+
+ p ->ExitColorSpace = CurrentColorSpace;
+ Transforms[i] = NULL; // End marker
+
+ p ->InputProfile = hProfiles[0];
+ p ->OutputProfile = hProfiles[nProfiles - 1];
+
+ nGridPoints = _cmsReasonableGridpointsByColorspace(p ->EntryColorSpace, dwFlags);
+
+ ChannelsInput = _cmsChannelsOf(cmsGetColorSpace(p ->InputProfile));
+
+ Grid = cmsAlloc3DGrid(Grid, nGridPoints, ChannelsInput, ChannelsOutput);
+
+ if (!(dwFlags & cmsFLAGS_NOPRELINEARIZATION))
+ _cmsComputePrelinearizationTablesFromXFORM(Transforms, nProfiles, Grid);
+
+ // Compute device link on 16-bit basis
+ if (!cmsSample3DGrid(Grid, MultiprofileSampler, (LPVOID) Transforms, Grid -> wFlags)) {
+
+ cmsFreeLUT(Grid);
+ goto ErrorCleanup;
+ }
+
+ // All ok, store the newly created LUT
+ p -> DeviceLink = Grid;
+
+ SetPrecalculatedTransform(p);
+
+ for (i=nProfiles-1; i >= 0; --i)
+ cmsDeleteTransform(Transforms[i]);
+
+
+ if (hLab) cmsCloseProfile(hLab);
+ if (hXYZ) cmsCloseProfile(hXYZ);
+
+
+ if (p ->EntryColorSpace == icSigRgbData ||
+ p ->EntryColorSpace == icSigCmyData) {
+
+ p->DeviceLink -> CLut16params.Interp3D = cmsTetrahedralInterp16;
+ }
+
+
+ if ((Intent != INTENT_ABSOLUTE_COLORIMETRIC) &&
+ !(dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP))
+ _cmsFixWhiteMisalignment(p);
+
+ return (cmsHTRANSFORM) p;
+
+
+ErrorCleanup:
+
+ if (hLab) cmsCloseProfile(hLab);
+ if (hXYZ) cmsCloseProfile(hXYZ);
+ return NULL;
+}
+
+
+
+double LCMSEXPORT cmsSetAdaptationState(double d)
+{
+ double OldVal = GlobalAdaptationState;
+
+ if (d >= 0)
+ GlobalAdaptationState = d;
+
+ return OldVal;
+
+}