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; }
static int bchswSampler(register WORD In[], register WORD Out[], register LPVOID Cargo) { cmsCIELab LabIn, LabOut; cmsCIELCh LChIn, LChOut; cmsCIEXYZ XYZ; double l; double power; gboolean shift; LPBCHSWADJUSTS bchsw = (LPBCHSWADJUSTS) Cargo; cmsLabEncoded2Float(&LabIn, In); // Move white point in Lab cmsLab2XYZ(&bchsw ->WPsrc, &XYZ, &LabIn); cmsXYZ2Lab(&bchsw ->WPdest, &LabIn, &XYZ); shift = (LabIn.L > 0.5); l = LabIn.L / 100; if (shift) l = 1.0 - l; if (l < 0.0) l = 0.0; if (bchsw->Contrast < 0) power = 1.0 + bchsw->Contrast; else power = (bchsw->Contrast == 1.0) ? 127 : 1.0 / (1.0 - bchsw->Contrast); l = 0.5 * pow (l * 2.0 , power); if (shift) l = 1.0 - l; LabIn.L = l * 100; cmsLab2LCh(&LChIn, &LabIn); // Do some adjusts on LCh LChOut.L = LChIn.L * bchsw ->Exposure + bchsw ->Brightness; LChOut.C = MAX (0, LChIn.C + bchsw ->Saturation); LChOut.h = LChIn.h + bchsw ->Hue; cmsLCh2Lab(&LabOut, &LChOut); // Back to encoded cmsFloat2LabEncoded(Out, &LabOut); return TRUE; }
static int PCS2ITU(register cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo) { cmsCIELab Lab; cmsLabEncoded2Float(&Lab, In); cmsDesaturateLab(&Lab, 85, -85, 125, -75); // This function does the necessary gamut remapping Lab2ITU(&Lab, Out); return TRUE; }
// Evaluates the CLUT part of a LUT (4 -> 3 only) static void EvalLUTdoubleKLab(LPLUT Lut, const VEC3* In, WORD FixedK, LPcmsCIELab Out) { WORD wIn[4], wOut[3]; wIn[0] = (WORD) floor(In ->n[0] * 65535.0 + 0.5); wIn[1] = (WORD) floor(In ->n[1] * 65535.0 + 0.5); wIn[2] = (WORD) floor(In ->n[2] * 65535.0 + 0.5); wIn[3] = FixedK; cmsEvalLUT(Lut, wIn, wOut); cmsLabEncoded2Float(Out, wOut); }
static int Forward(register WORD In[], register WORD Out[], register LPVOID Cargo) { cmsCIELab Lab; cmsLabEncoded2Float(&Lab, In); Lab.a = Lab.b = 0; cmsFloat2LabEncoded(Out, &Lab); return TRUE; }
static int RegressionSamplerB2A(register WORD In[], register WORD Out[], register LPVOID Cargo) { cmsCIELab Lab; cmsCIEXYZ xyz; VEC3 vxyz, RGB; /* cmsJCh JCh; */ WORD Lin[3], Llab[3]; LPMONITORPROFILERDATA sys = (LPMONITORPROFILERDATA) Cargo; double L; /* Pass L back to 0..0xff00 domain */ L = (double) (In[0] * 65280.0) / 65535.0; In[0] = (WORD) floor(L + .5); /* To float values */ cmsLabEncoded2Float(&Lab, In); cmsLab2XYZ(NULL, &xyz, &Lab); cmsxChromaticAdaptationAndNormalization(&sys ->hdr, &xyz, true); vxyz.n[0] = xyz.X; vxyz.n[1] = xyz.Y; vxyz.n[2] = xyz.Z; MAT3eval(&RGB, &sys-> PrimariesMatrixRev, &vxyz); /* Clamp RGB */ ClampRGB(&RGB); /* Encode output */ Lin[0] = (WORD) ((double) RGB.n[0] * 65535. + .5); Lin[1] = (WORD) ((double) RGB.n[1] * 65535. + .5); Lin[2] = (WORD) ((double) RGB.n[2] * 65535. + .5); cmsxApplyLinearizationGamma(Lin, sys ->ReverseTables, Llab); cmsxApplyLinearizationGamma(Llab, sys ->PreLabRev, Out); return true; /* And done witch success */ }
LCMSAPI double LCMSEXPORT cmsEvalLUTreverse(LPLUT Lut, WORD Target[], WORD Result[], LPWORD Hint) { int i; double error, LastError = 1E20; cmsCIELab fx, Goal; VEC3 tmp, tmp2, x; MAT3 Jacobian; WORD FixedK; WORD LastResult[4]; // This is our Lab goal cmsLabEncoded2Float(&Goal, Target); // Special case for CMYK->Lab if (Lut ->InputChan == 4) FixedK = Target[3]; else FixedK = 0; // Take the hint as starting point if specified if (Hint == NULL) { // Begin at any point, we choose 1/3 of neutral CMY gray x.n[0] = x.n[1] = x.n[2] = 0.3; } else { FromEncoded(&x, Hint); } // Iterate for (i = 0; i < INVERSION_MAX_ITERATIONS; i++) { // Get beginning fx EvalLUTdoubleKLab(Lut, &x, FixedK, &fx); // Compute error error = cmsDeltaE(&fx, &Goal); // If not convergent, return last safe value if (error >= LastError) break; // Keep latest values LastError = error; ToEncoded(LastResult, &x); LastResult[3] = FixedK; // Obtain slope ComputeJacobianLab(Lut, &Jacobian, &x, FixedK); // Solve system tmp2.n[0] = fx.L - Goal.L; tmp2.n[1] = fx.a - Goal.a; tmp2.n[2] = fx.b - Goal.b; if (!MAT3solve(&tmp, &Jacobian, &tmp2)) break; // Move our guess x.n[0] -= tmp.n[0]; x.n[1] -= tmp.n[1]; x.n[2] -= tmp.n[2]; // Some clipping.... VEC3saturate(&x); } Result[0] = LastResult[0]; Result[1] = LastResult[1]; Result[2] = LastResult[2]; Result[3] = LastResult[3]; return LastError; }
static int GamutSampler(register WORD In[], register WORD Out[], register LPVOID Cargo) { LPGAMUTCHAIN t = (LPGAMUTCHAIN) Cargo; WORD Proof[MAXCHANNELS], Check[MAXCHANNELS]; WORD Proof2[MAXCHANNELS], Check2[MAXCHANNELS]; cmsCIELab LabIn1, LabOut1; cmsCIELab LabIn2, LabOut2; double dE1, dE2, ErrorRatio; // Assume in-gamut by default. dE1 = 0.; dE2 = 0; ErrorRatio = 1.0; // Any input space? I can use In[] no matter channels // because is just one pixel if (t -> hInput != NULL) cmsDoTransform(t -> hInput, In, In, 1); // converts from PCS to colorant. This always // does return in-gamut values, cmsDoTransform(t -> hForward, In, Proof, 1); // Now, do the inverse, from colorant to PCS. cmsDoTransform(t -> hReverse, Proof, Check, 1); // Try again, but this time taking Check as input cmsDoTransform(t -> hForward, Check, Proof2, 1); cmsDoTransform(t -> hReverse, Proof2, Check2, 1); // Does the transform returns out-of-gamut? if (Check[0] == 0xFFFF && Check[1] == 0xFFFF && Check[2] == 0xFFFF) Out[0] = 0xFF00; // Out of gamut! else { // Transport encoded values cmsLabEncoded2Float(&LabIn1, In); cmsLabEncoded2Float(&LabOut1, Check); // Take difference of direct value dE1 = cmsDeltaE(&LabIn1, &LabOut1); cmsLabEncoded2Float(&LabIn2, Check); cmsLabEncoded2Float(&LabOut2, Check2); // 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] = (WORD) _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] = (WORD) _cmsQuickFloor((ErrorRatio - t->Thereshold) + .5); else Out[0] = 0; } } return TRUE; }
static void PrintResults(WORD Encoded[], icColorSpaceSignature ColorSpace) { int i; switch (ColorSpace) { case icSigXYZData: cmsXYZEncoded2Float(&xyz, Encoded); PrintCooked("X", xyz.X * 100.); PrintCooked("Y", xyz.Y * 100.); PrintCooked("Z", xyz.Z * 100.); break; case icSigLabData: cmsLabEncoded2Float(&Lab, Encoded); PrintCooked("L*", Lab.L); PrintCooked("a*", Lab.a); PrintCooked("b*", Lab.b); break; case icSigLuvData: PrintOne("L", Encoded[0]); PrintOne("u", Encoded[1]); PrintOne("v", Encoded[2]); break; case icSigYCbCrData: PrintOne("Y", Encoded[0]); PrintOne("Cb", Encoded[1]); PrintOne("Cr", Encoded[2]); break; case icSigYxyData: PrintOne("Y", Encoded[0]); PrintOne("x", Encoded[1]); PrintOne("y", Encoded[2]); break; case icSigRgbData: PrintOne("R", Encoded[0]); PrintOne("G", Encoded[1]); PrintOne("B", Encoded[2]); break; case icSigGrayData: PrintOne("G", Encoded[0]); break; case icSigHsvData: PrintOne("H", Encoded[0]); PrintOne("s", Encoded[1]); PrintOne("v", Encoded[2]); break; case icSigHlsData: PrintOne("H", Encoded[0]); PrintOne("l", Encoded[1]); PrintOne("s", Encoded[2]); break; case icSigCmykData: Print100("C", Encoded[0]); Print100("M", Encoded[1]); Print100("Y", Encoded[2]); Print100("K", Encoded[3]); break; case icSigCmyData: Print100("C", Encoded[0]); Print100("M", Encoded[1]); Print100("Y", Encoded[2]); break; case icSigHexachromeData: case icSig6colorData: Print100("C", Encoded[0]); Print100("M", Encoded[1]); Print100("Y", Encoded[2]); Print100("K", Encoded[3]); Print100("c", Encoded[1]); Print100("m", Encoded[2]); break; default: for (i=0; i < _cmsChannelsOf(OutputColorSpace); i++) { char Buffer[10]; sprintf(Buffer, "CHAN%d", i + 1); PrintOne(Buffer, Encoded[i]); } } }