//--------------------------------------------------------------------------------- // // Little Color Management System // Copyright (c) 1998-2021 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" // Auxiliary: 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) { … } // 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) { … } // 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) { … } // Gamut LUT Creation ----------------------------------------------------------------------------------------- // Used by gamut & softproofing GAMUTCHAIN; // This sampler does compute gamut boundaries by comparing original // values with a transform going back and forth. Values above ERR_THRESHOLD // of maximum are considered out of gamut. #define ERR_THRESHOLD … static int GamutSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void* Cargo) { … } // 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 truly 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) { … } // Total Area Coverage estimation ---------------------------------------------------------------- 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(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER void * Cargo) { … } // Detect Total area coverage of the profile cmsFloat64Number CMSEXPORT cmsDetectTAC(cmsHPROFILE hProfile) { … } // Carefully, clamp on CIELab space. cmsBool CMSEXPORT cmsDesaturateLab(cmsCIELab* Lab, double amax, double amin, double bmax, double bmin) { … } // Detect whatever a given ICC profile works in linear (gamma 1.0) space // Actually, doing that "well" is quite hard, since every component may behave completely different. // Since the true point of this function is to detect suitable optimizations, I am imposing some requirements // that simplifies things: only RGB, and only profiles that can got in both directions. // The algorithm obtains Y from a syntetical gray R=G=B. Then least squares fitting is used to estimate gamma. // For gamma close to 1.0, RGB is linear. On profiles not supported, -1 is returned. cmsFloat64Number CMSEXPORT cmsDetectRGBProfileGamma(cmsHPROFILE hProfile, cmsFloat64Number threshold) { … }