diff options
Diffstat (limited to 'debian/lcms/lcms-1.19.dfsg2/src/cmssamp.c')
| -rwxr-xr-x | debian/lcms/lcms-1.19.dfsg2/src/cmssamp.c | 668 |
1 files changed, 668 insertions, 0 deletions
diff --git a/debian/lcms/lcms-1.19.dfsg2/src/cmssamp.c b/debian/lcms/lcms-1.19.dfsg2/src/cmssamp.c new file mode 100755 index 00000000..b6bfd855 --- /dev/null +++ b/debian/lcms/lcms-1.19.dfsg2/src/cmssamp.c @@ -0,0 +1,668 @@ +// +// 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" + + +// --------------------------------------------------------------------------------- + +static volatile int GlobalBlackPreservationStrategy = 0; + +// Quantize a value 0 <= i < MaxSamples + +WORD _cmsQuantizeVal(double i, int MaxSamples) +{ + double x; + + x = ((double) i * 65535.) / (double) (MaxSamples - 1); + + return (WORD) floor(x + .5); +} + + +// Is a table linear? + +int cmsIsLinear(WORD Table[], int nEntries) +{ + register int i; + int diff; + + for (i=0; i < nEntries; i++) { + + diff = abs((int) Table[i] - (int) _cmsQuantizeVal(i, nEntries)); + if (diff > 3) + return 0; + } + + return 1; +} + + + +// pow() restricted to integer + +static +int ipow(int base, int exp) +{ + int res = base; + + while (--exp) + res *= base; + + return res; +} + + +// Given n, 0<=n<=clut^dim, returns the colorant. + +static +int ComponentOf(int n, int clut, int nColorant) +{ + if (nColorant <= 0) + return (n % clut); + + n /= ipow(clut, nColorant); + + return (n % clut); +} + + + +// This routine does a sweep on whole input space, and calls its callback +// function on knots. returns TRUE if all ok, FALSE otherwise. + +LCMSBOOL LCMSEXPORT cmsSample3DGrid(LPLUT Lut, _cmsSAMPLER Sampler, LPVOID Cargo, DWORD dwFlags) +{ + int i, t, nTotalPoints, Colorant, index; + WORD In[MAXCHANNELS], Out[MAXCHANNELS]; + + nTotalPoints = ipow(Lut->cLutPoints, Lut -> InputChan); + + index = 0; + for (i = 0; i < nTotalPoints; i++) { + + for (t=0; t < (int) Lut -> InputChan; t++) { + + Colorant = ComponentOf(i, Lut -> cLutPoints, (Lut -> InputChan - t - 1 )); + In[t] = _cmsQuantizeVal(Colorant, Lut -> cLutPoints); + } + + + if (dwFlags & SAMPLER_HASTL1) { + + for (t=0; t < (int) Lut -> InputChan; t++) + In[t] = cmsReverseLinearInterpLUT16(In[t], + Lut -> L1[t], + &Lut -> In16params); + } + + for (t=0; t < (int) Lut -> OutputChan; t++) + Out[t] = Lut->T[index + t]; + + if (dwFlags & SAMPLER_HASTL2) { + + for (t=0; t < (int) Lut -> OutputChan; t++) + Out[t] = cmsLinearInterpLUT16(Out[t], + Lut -> L2[t], + &Lut -> Out16params); + } + + + if (!Sampler(In, Out, Cargo)) + return FALSE; + + if (!(dwFlags & SAMPLER_INSPECT)) { + + if (dwFlags & SAMPLER_HASTL2) { + + for (t=0; t < (int) Lut -> OutputChan; t++) + Out[t] = cmsReverseLinearInterpLUT16(Out[t], + Lut -> L2[t], + &Lut -> Out16params); + } + + + for (t=0; t < (int) Lut -> OutputChan; t++) + Lut->T[index + t] = Out[t]; + + } + + index += Lut -> OutputChan; + + } + + return TRUE; +} + + + + + + +// choose reasonable resolution +int _cmsReasonableGridpointsByColorspace(icColorSpaceSignature Colorspace, DWORD 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 stripped resolution + + if (dwFlags & cmsFLAGS_LOWRESPRECALC) { + + if (nChannels > 4) + return 6; // 6 for Hifi + + 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 + +} + +// Sampler implemented by another transform. This is a clean way to +// precalculate the devicelink 3D CLUT for almost any transform + +static +int XFormSampler(register WORD In[], register WORD Out[], register LPVOID Cargo) +{ + cmsDoTransform((cmsHTRANSFORM) Cargo, In, Out, 1); + return TRUE; +} + +// This routine does compute the devicelink CLUT containing whole +// transform. Handles any channel number. + +LPLUT _cmsPrecalculateDeviceLink(cmsHTRANSFORM h, DWORD dwFlags) +{ + _LPcmsTRANSFORM p = (_LPcmsTRANSFORM) h; + LPLUT Grid; + int nGridPoints; + DWORD dwFormatIn, dwFormatOut; + DWORD SaveFormatIn, SaveFormatOut; + int ChannelsIn, ChannelsOut; + LPLUT SaveGamutLUT; + + + // Remove any gamut checking + SaveGamutLUT = p ->Gamut; + p ->Gamut = NULL; + + ChannelsIn = _cmsChannelsOf(p -> EntryColorSpace); + ChannelsOut = _cmsChannelsOf(p -> ExitColorSpace); + + nGridPoints = _cmsReasonableGridpointsByColorspace(p -> EntryColorSpace, dwFlags); + + Grid = cmsAllocLUT(); + if (!Grid) return NULL; + + Grid = cmsAlloc3DGrid(Grid, nGridPoints, ChannelsIn, ChannelsOut); + + // Compute device link on 16-bit basis + dwFormatIn = (CHANNELS_SH(ChannelsIn)|BYTES_SH(2)); + dwFormatOut = (CHANNELS_SH(ChannelsOut)|BYTES_SH(2)); + + SaveFormatIn = p ->InputFormat; + SaveFormatOut = p ->OutputFormat; + + p -> InputFormat = dwFormatIn; + p -> OutputFormat = dwFormatOut; + p -> FromInput = _cmsIdentifyInputFormat(p, dwFormatIn); + p -> ToOutput = _cmsIdentifyOutputFormat(p, dwFormatOut); + + // Fix gamut & gamma possible mismatches. + + if (!(dwFlags & cmsFLAGS_NOPRELINEARIZATION)) { + + cmsHTRANSFORM hOne[1]; + hOne[0] = h; + + _cmsComputePrelinearizationTablesFromXFORM(hOne, 1, Grid); + } + + // Attention to this typecast! we can take the luxury to + // do this since cmsHTRANSFORM is only an alias to a pointer + // to the transform struct. + + if (!cmsSample3DGrid(Grid, XFormSampler, (LPVOID) p, Grid -> wFlags)) { + + cmsFreeLUT(Grid); + Grid = NULL; + } + + p ->Gamut = SaveGamutLUT; + p ->InputFormat = SaveFormatIn; + p ->OutputFormat = SaveFormatOut; + + return Grid; +} + + + +// Sampler for Black-preserving CMYK->CMYK transforms + +typedef struct { + cmsHTRANSFORM cmyk2cmyk; + cmsHTRANSFORM cmyk2Lab; + LPGAMMATABLE KTone; + L16PARAMS KToneParams; + LPLUT LabK2cmyk; + double MaxError; + + cmsHTRANSFORM hRoundTrip; + int MaxTAC; + + cmsHTRANSFORM hProofOutput; + + } BPCARGO, *LPBPCARGO; + + + +// Preserve black only if that is the only ink used +static +int BlackPreservingGrayOnlySampler(register WORD In[], register WORD Out[], register LPVOID Cargo) +{ + BPCARGO* bp = (LPBPCARGO) 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] = cmsLinearInterpLUT16(In[3], bp->KTone ->GammaTable, &bp->KToneParams); + return 1; + } + + // Keep normal transform for other colors + cmsDoTransform(bp ->cmyk2cmyk, In, Out, 1); + return 1; +} + + + +// Preserve all K plane. +static +int BlackPreservingSampler(register WORD In[], register WORD Out[], register LPVOID Cargo) +{ + + WORD LabK[4]; + double SumCMY, SumCMYK, Error; + cmsCIELab ColorimetricLab, BlackPreservingLab; + BPCARGO* bp = (LPBPCARGO) Cargo; + + // Get the K across Tone curve + LabK[3] = cmsLinearInterpLUT16(In[3], bp->KTone ->GammaTable, &bp->KToneParams); + + // 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] = LabK[3]; + return 1; + } + + // Try the original transform, maybe K is already ok (valid on K=0) + cmsDoTransform(bp ->cmyk2cmyk, In, Out, 1); + if (Out[3] == LabK[3]) return 1; + + + // No, mesure and keep Lab measurement for further usage + cmsDoTransform(bp->hProofOutput, Out, &ColorimetricLab, 1); + + // Is not black only and the transform doesn't keep black. + // Obtain the Lab of CMYK. After that we have Lab + K + cmsDoTransform(bp ->cmyk2Lab, In, LabK, 1); + + // Obtain the corresponding CMY using reverse interpolation. + // As a seed, we use the colorimetric CMY + cmsEvalLUTreverse(bp ->LabK2cmyk, LabK, Out, Out); + + // Estimate the error + cmsDoTransform(bp->hProofOutput, Out, &BlackPreservingLab, 1); + Error = cmsDeltaE(&ColorimetricLab, &BlackPreservingLab); + + + // Apply TAC if needed + + SumCMY = Out[0] + Out[1] + Out[2]; + SumCMYK = SumCMY + Out[3]; + + if (SumCMYK > bp ->MaxTAC) { + + double Ratio = 1 - ((SumCMYK - bp->MaxTAC) / SumCMY); + if (Ratio < 0) + Ratio = 0; + + Out[0] = (WORD) floor(Out[0] * Ratio + 0.5); // C + Out[1] = (WORD) floor(Out[1] * Ratio + 0.5); // M + Out[2] = (WORD) floor(Out[2] * Ratio + 0.5); // Y + } + + return 1; +} + + +// Sample whole gamut to estimate maximum TAC + +#ifdef _MSC_VER +#pragma warning(disable : 4100) +#endif + +static +int EstimateTAC(register WORD In[], register WORD Out[], register LPVOID Cargo) +{ + BPCARGO* bp = (LPBPCARGO) Cargo; + WORD RoundTrip[4]; + int Sum; + + cmsDoTransform(bp->hRoundTrip, In, RoundTrip, 1); + + Sum = RoundTrip[0] + RoundTrip[1] + RoundTrip[2] + RoundTrip[3]; + + if (Sum > bp ->MaxTAC) + bp ->MaxTAC = Sum; + + return 1; +} + + +// Estimate the maximum error +static +int BlackPreservingEstimateErrorSampler(register WORD In[], register WORD Out[], register LPVOID Cargo) +{ + BPCARGO* bp = (LPBPCARGO) Cargo; + WORD ColorimetricOut[4]; + cmsCIELab ColorimetricLab, BlackPreservingLab; + double Error; + + if (In[0] == 0 && In[1] == 0 && In[2] == 0) return 1; + + cmsDoTransform(bp->cmyk2cmyk, In, ColorimetricOut, 1); + + cmsDoTransform(bp->hProofOutput, ColorimetricOut, &ColorimetricLab, 1); + cmsDoTransform(bp->hProofOutput, Out, &BlackPreservingLab, 1); + + Error = cmsDeltaE(&ColorimetricLab, &BlackPreservingLab); + + if (Error > bp ->MaxError) + bp ->MaxError = Error; + + return 1; +} + +// Setup the K preservation strategy +int LCMSEXPORT cmsSetCMYKPreservationStrategy(int n) +{ + int OldVal = GlobalBlackPreservationStrategy; + + if (n >= 0) + GlobalBlackPreservationStrategy = n; + + return OldVal; +} + +#pragma warning(disable: 4550) + +// Get a pointer to callback on depending of strategy +static +_cmsSAMPLER _cmsGetBlackPreservationSampler(void) +{ + switch (GlobalBlackPreservationStrategy) { + + case 0: return BlackPreservingGrayOnlySampler; + default: return BlackPreservingSampler; + } + +} + +// This is the black-preserving devicelink generator +LPLUT _cmsPrecalculateBlackPreservingDeviceLink(cmsHTRANSFORM hCMYK2CMYK, DWORD dwFlags) +{ + _LPcmsTRANSFORM p = (_LPcmsTRANSFORM) hCMYK2CMYK; + BPCARGO Cargo; + LPLUT Grid; + DWORD LocalFlags; + cmsHPROFILE hLab = cmsCreateLabProfile(NULL); + int nGridPoints; + icTagSignature Device2PCS[] = {icSigAToB0Tag, // Perceptual + icSigAToB1Tag, // Relative colorimetric + icSigAToB2Tag, // Saturation + icSigAToB1Tag }; // Absolute colorimetric + // (Relative/WhitePoint) + + nGridPoints = _cmsReasonableGridpointsByColorspace(p -> EntryColorSpace, dwFlags); + + // Get a copy of inteserting flags for this kind of xform + LocalFlags = cmsFLAGS_NOTPRECALC; + if (p -> dwOriginalFlags & cmsFLAGS_BLACKPOINTCOMPENSATION) + LocalFlags |= cmsFLAGS_BLACKPOINTCOMPENSATION; + + // Fill in cargo struct + Cargo.cmyk2cmyk = hCMYK2CMYK; + + // Compute tone curve. + Cargo.KTone = _cmsBuildKToneCurve(hCMYK2CMYK, 256); + if (Cargo.KTone == NULL) return NULL; + cmsCalcL16Params(Cargo.KTone ->nEntries, &Cargo.KToneParams); + + + // Create a CMYK->Lab "normal" transform on input, without K-preservation + Cargo.cmyk2Lab = cmsCreateTransform(p ->InputProfile, TYPE_CMYK_16, + hLab, TYPE_Lab_16, p->Intent, LocalFlags); + + // We are going to use the reverse of proof direction + Cargo.LabK2cmyk = cmsReadICCLut(p->OutputProfile, Device2PCS[p->Intent]); + + // Is there any table available? + if (Cargo.LabK2cmyk == NULL) { + + Grid = NULL; + goto Cleanup; + } + + // Setup a roundtrip on output profile for TAC estimation + Cargo.hRoundTrip = cmsCreateTransform(p ->OutputProfile, TYPE_CMYK_16, + p ->OutputProfile, TYPE_CMYK_16, p->Intent, cmsFLAGS_NOTPRECALC); + + + // Setup a proof CMYK->Lab on output + Cargo.hProofOutput = cmsCreateTransform(p ->OutputProfile, TYPE_CMYK_16, + hLab, TYPE_Lab_DBL, p->Intent, LocalFlags); + + + // Create an empty LUT for holding K-preserving xform + Grid = cmsAllocLUT(); + if (!Grid) goto Cleanup; + + Grid = cmsAlloc3DGrid(Grid, nGridPoints, 4, 4); + + // Setup formatters + p -> FromInput = _cmsIdentifyInputFormat(p, TYPE_CMYK_16); + p -> ToOutput = _cmsIdentifyOutputFormat(p, TYPE_CMYK_16); + + + + // Step #1, estimate TAC + Cargo.MaxTAC = 0; + if (!cmsSample3DGrid(Grid, EstimateTAC, (LPVOID) &Cargo, 0)) { + + cmsFreeLUT(Grid); + Grid = NULL; + goto Cleanup; + } + + + // Step #2, compute approximation + if (!cmsSample3DGrid(Grid, _cmsGetBlackPreservationSampler(), (LPVOID) &Cargo, 0)) { + + cmsFreeLUT(Grid); + Grid = NULL; + goto Cleanup; + } + + // Step #3, estimate error + Cargo.MaxError = 0; + cmsSample3DGrid(Grid, BlackPreservingEstimateErrorSampler, (LPVOID) &Cargo, SAMPLER_INSPECT); + + +Cleanup: + + if (Cargo.cmyk2Lab) cmsDeleteTransform(Cargo.cmyk2Lab); + if (Cargo.hRoundTrip) cmsDeleteTransform(Cargo.hRoundTrip); + if (Cargo.hProofOutput) cmsDeleteTransform(Cargo.hProofOutput); + + if (hLab) cmsCloseProfile(hLab); + if (Cargo.KTone) cmsFreeGamma(Cargo.KTone); + if (Cargo.LabK2cmyk) cmsFreeLUT(Cargo.LabK2cmyk); + + return Grid; +} + + + +// Fix broken LUT. just to obtain other CMS compatibility + +static +void PatchLUT(LPLUT Grid, WORD At[], WORD Value[], + int nChannelsOut, int nChannelsIn) +{ + LPL16PARAMS p16 = &Grid -> CLut16params; + double px, py, pz, pw; + int x0, y0, z0, w0; + int i, index; + + + if (Grid ->wFlags & LUT_HASTL1) return; // There is a prelinearization + + px = ((double) At[0] * (p16->Domain)) / 65535.0; + py = ((double) At[1] * (p16->Domain)) / 65535.0; + pz = ((double) At[2] * (p16->Domain)) / 65535.0; + pw = ((double) At[3] * (p16->Domain)) / 65535.0; + + x0 = (int) floor(px); + y0 = (int) floor(py); + z0 = (int) floor(pz); + w0 = (int) floor(pw); + + if (nChannelsIn == 4) { + + if (((px - x0) != 0) || + ((py - y0) != 0) || + ((pz - z0) != 0) || + ((pw - w0) != 0)) return; // Not on exact node + + index = p16 -> opta4 * x0 + + p16 -> opta3 * y0 + + p16 -> opta2 * z0 + + p16 -> opta1 * w0; + } + else + if (nChannelsIn == 3) { + + if (((px - x0) != 0) || + ((py - y0) != 0) || + ((pz - z0) != 0)) return; // Not on exact node + + index = p16 -> opta3 * x0 + + p16 -> opta2 * y0 + + p16 -> opta1 * z0; + } + else + if (nChannelsIn == 1) { + + if (((px - x0) != 0)) return; // Not on exact node + + index = p16 -> opta1 * x0; + } + else { + cmsSignalError(LCMS_ERRC_ABORTED, "(internal) %d Channels are not supported on PatchLUT", nChannelsIn); + return; + } + + for (i=0; i < nChannelsOut; i++) + Grid -> T[index + i] = Value[i]; + +} + + + +LCMSBOOL _cmsFixWhiteMisalignment(_LPcmsTRANSFORM p) +{ + + WORD *WhitePointIn, *WhitePointOut, *BlackPointIn, *BlackPointOut; + int nOuts, nIns; + + + if (!p -> DeviceLink) return FALSE; + + if (p ->Intent == INTENT_ABSOLUTE_COLORIMETRIC) return FALSE; + if ((p ->PreviewProfile != NULL) && + (p ->ProofIntent == INTENT_ABSOLUTE_COLORIMETRIC)) return FALSE; + + + if (!_cmsEndPointsBySpace(p -> EntryColorSpace, + &WhitePointIn, &BlackPointIn, &nIns)) return FALSE; + + + if (!_cmsEndPointsBySpace(p -> ExitColorSpace, + &WhitePointOut, &BlackPointOut, &nOuts)) return FALSE; + + // Fix white only + + PatchLUT(p -> DeviceLink, WhitePointIn, WhitePointOut, nOuts, nIns); + // PatchLUT(p -> DeviceLink, BlackPointIn, BlackPointOut, nOuts, nIns); + + return TRUE; +} + |
