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; }
// 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; }
// 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 FALSE; // 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]) return FALSE; // Non-Monotonic } if (Zeros > (nItems / 3)) return FALSE; // Degenerated, mostly zeros if (Poles > (nItems / 3)) return FALSE; // Degenerated, mostly poles // Seems ok for (i=0; i < nItems; i++) { // Clamp to cmsUInt16Number Tab -> Table16[i] = _cmsQuickSaturateWord(z[i+1]); } return TRUE; }
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); } }
// 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); }
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 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; 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; }
// 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; }
static void fromDBLto16(void* dst, const void* src) { cmsFloat64Number n = *(cmsFloat64Number*)src; *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0f); }
static void fromHLFto16(void* dst, const void* src) { cmsFloat32Number n = _cmsHalf2Float(*(cmsUInt16Number*)src); *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0f); }
// 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) { 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; }