diff options
Diffstat (limited to 'third_party/lcms2-2.6/src/cmscnvrt.c')
-rw-r--r-- | third_party/lcms2-2.6/src/cmscnvrt.c | 1142 |
1 files changed, 0 insertions, 1142 deletions
diff --git a/third_party/lcms2-2.6/src/cmscnvrt.c b/third_party/lcms2-2.6/src/cmscnvrt.c deleted file mode 100644 index 1a93e83f90..0000000000 --- a/third_party/lcms2-2.6/src/cmscnvrt.c +++ /dev/null @@ -1,1142 +0,0 @@ -//--------------------------------------------------------------------------------- -// -// Little Color Management System -// Copyright (c) 1998-2012 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" - - -// Link several profiles to obtain a single LUT modelling the whole color transform. Intents, Black point -// compensation and Adaptation parameters may vary across profiles. BPC and Adaptation refers to the PCS -// after the profile. I.e, BPC[0] refers to connexion between profile(0) and profile(1) -cmsPipeline* _cmsLinkProfiles(cmsContext ContextID, - cmsUInt32Number nProfiles, - cmsUInt32Number Intents[], - cmsHPROFILE hProfiles[], - cmsBool BPC[], - cmsFloat64Number AdaptationStates[], - cmsUInt32Number dwFlags); - -//--------------------------------------------------------------------------------- - -// This is the default routine for ICC-style intents. A user may decide to override it by using a plugin. -// Supported intents are perceptual, relative colorimetric, saturation and ICC-absolute colorimetric -static -cmsPipeline* DefaultICCintents(cmsContext ContextID, - cmsUInt32Number nProfiles, - cmsUInt32Number Intents[], - cmsHPROFILE hProfiles[], - cmsBool BPC[], - cmsFloat64Number AdaptationStates[], - cmsUInt32Number dwFlags); - -//--------------------------------------------------------------------------------- - -// This is the entry for black-preserving K-only intents, which are non-ICC. Last profile have to be a output profile -// to do the trick (no devicelinks allowed at that position) -static -cmsPipeline* BlackPreservingKOnlyIntents(cmsContext ContextID, - cmsUInt32Number nProfiles, - cmsUInt32Number Intents[], - cmsHPROFILE hProfiles[], - cmsBool BPC[], - cmsFloat64Number AdaptationStates[], - cmsUInt32Number dwFlags); - -//--------------------------------------------------------------------------------- - -// This is the entry for black-plane preserving, which are non-ICC. Again, Last profile have to be a output profile -// to do the trick (no devicelinks allowed at that position) -static -cmsPipeline* BlackPreservingKPlaneIntents(cmsContext ContextID, - cmsUInt32Number nProfiles, - cmsUInt32Number Intents[], - cmsHPROFILE hProfiles[], - cmsBool BPC[], - cmsFloat64Number AdaptationStates[], - cmsUInt32Number dwFlags); - -//--------------------------------------------------------------------------------- - - -// This is a structure holding implementations for all supported intents. -typedef struct _cms_intents_list { - - cmsUInt32Number Intent; - char Description[256]; - cmsIntentFn Link; - struct _cms_intents_list* Next; - -} cmsIntentsList; - - -// Built-in intents -static cmsIntentsList DefaultIntents[] = { - - { INTENT_PERCEPTUAL, "Perceptual", DefaultICCintents, &DefaultIntents[1] }, - { INTENT_RELATIVE_COLORIMETRIC, "Relative colorimetric", DefaultICCintents, &DefaultIntents[2] }, - { INTENT_SATURATION, "Saturation", DefaultICCintents, &DefaultIntents[3] }, - { INTENT_ABSOLUTE_COLORIMETRIC, "Absolute colorimetric", DefaultICCintents, &DefaultIntents[4] }, - { INTENT_PRESERVE_K_ONLY_PERCEPTUAL, "Perceptual preserving black ink", BlackPreservingKOnlyIntents, &DefaultIntents[5] }, - { INTENT_PRESERVE_K_ONLY_RELATIVE_COLORIMETRIC, "Relative colorimetric preserving black ink", BlackPreservingKOnlyIntents, &DefaultIntents[6] }, - { INTENT_PRESERVE_K_ONLY_SATURATION, "Saturation preserving black ink", BlackPreservingKOnlyIntents, &DefaultIntents[7] }, - { INTENT_PRESERVE_K_PLANE_PERCEPTUAL, "Perceptual preserving black plane", BlackPreservingKPlaneIntents, &DefaultIntents[8] }, - { INTENT_PRESERVE_K_PLANE_RELATIVE_COLORIMETRIC,"Relative colorimetric preserving black plane", BlackPreservingKPlaneIntents, &DefaultIntents[9] }, - { INTENT_PRESERVE_K_PLANE_SATURATION, "Saturation preserving black plane", BlackPreservingKPlaneIntents, NULL } -}; - - -// A pointer to the begining of the list -_cmsIntentsPluginChunkType _cmsIntentsPluginChunk = { NULL }; - -// Duplicates the zone of memory used by the plug-in in the new context -static -void DupPluginIntentsList(struct _cmsContext_struct* ctx, - const struct _cmsContext_struct* src) -{ - _cmsIntentsPluginChunkType newHead = { NULL }; - cmsIntentsList* entry; - cmsIntentsList* Anterior = NULL; - _cmsIntentsPluginChunkType* head = (_cmsIntentsPluginChunkType*) src->chunks[IntentPlugin]; - - // Walk the list copying all nodes - for (entry = head->Intents; - entry != NULL; - entry = entry ->Next) { - - cmsIntentsList *newEntry = ( cmsIntentsList *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(cmsIntentsList)); - - if (newEntry == NULL) - return; - - // We want to keep the linked list order, so this is a little bit tricky - newEntry -> Next = NULL; - if (Anterior) - Anterior -> Next = newEntry; - - Anterior = newEntry; - - if (newHead.Intents == NULL) - newHead.Intents = newEntry; - } - - ctx ->chunks[IntentPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsIntentsPluginChunkType)); -} - -void _cmsAllocIntentsPluginChunk(struct _cmsContext_struct* ctx, - const struct _cmsContext_struct* src) -{ - if (src != NULL) { - - // Copy all linked list - DupPluginIntentsList(ctx, src); - } - else { - static _cmsIntentsPluginChunkType IntentsPluginChunkType = { NULL }; - ctx ->chunks[IntentPlugin] = _cmsSubAllocDup(ctx ->MemPool, &IntentsPluginChunkType, sizeof(_cmsIntentsPluginChunkType)); - } -} - - -// Search the list for a suitable intent. Returns NULL if not found -static -cmsIntentsList* SearchIntent(cmsContext ContextID, cmsUInt32Number Intent) -{ - _cmsIntentsPluginChunkType* ctx = ( _cmsIntentsPluginChunkType*) _cmsContextGetClientChunk(ContextID, IntentPlugin); - cmsIntentsList* pt; - - for (pt = ctx -> Intents; pt != NULL; pt = pt -> Next) - if (pt ->Intent == Intent) return pt; - - for (pt = DefaultIntents; pt != NULL; pt = pt -> Next) - if (pt ->Intent == Intent) return pt; - - return NULL; -} - -// Black point compensation. Implemented as a linear scaling in XYZ. Black points -// should come relative to the white point. Fills an matrix/offset element m -// which is organized as a 4x4 matrix. -static -void ComputeBlackPointCompensation(const cmsCIEXYZ* BlackPointIn, - const cmsCIEXYZ* BlackPointOut, - cmsMAT3* m, cmsVEC3* off) -{ - cmsFloat64Number ax, ay, az, bx, by, bz, tx, ty, tz; - - // Now we need to compute a matrix plus an offset m and of such of - // [m]*bpin + off = bpout - // [m]*D50 + off = D50 - // - // This is a linear scaling in the form ax+b, where - // a = (bpout - D50) / (bpin - D50) - // b = - D50* (bpout - bpin) / (bpin - D50) - - tx = BlackPointIn->X - cmsD50_XYZ()->X; - ty = BlackPointIn->Y - cmsD50_XYZ()->Y; - tz = BlackPointIn->Z - cmsD50_XYZ()->Z; - - ax = (BlackPointOut->X - cmsD50_XYZ()->X) / tx; - ay = (BlackPointOut->Y - cmsD50_XYZ()->Y) / ty; - az = (BlackPointOut->Z - cmsD50_XYZ()->Z) / tz; - - bx = - cmsD50_XYZ()-> X * (BlackPointOut->X - BlackPointIn->X) / tx; - by = - cmsD50_XYZ()-> Y * (BlackPointOut->Y - BlackPointIn->Y) / ty; - bz = - cmsD50_XYZ()-> Z * (BlackPointOut->Z - BlackPointIn->Z) / tz; - - _cmsVEC3init(&m ->v[0], ax, 0, 0); - _cmsVEC3init(&m ->v[1], 0, ay, 0); - _cmsVEC3init(&m ->v[2], 0, 0, az); - _cmsVEC3init(off, bx, by, bz); - -} - - -// Approximate a blackbody illuminant based on CHAD information -static -cmsFloat64Number CHAD2Temp(const cmsMAT3* Chad) -{ - // Convert D50 across inverse CHAD to get the absolute white point - cmsVEC3 d, s; - cmsCIEXYZ Dest; - cmsCIExyY DestChromaticity; - cmsFloat64Number TempK; - cmsMAT3 m1, m2; - - m1 = *Chad; - if (!_cmsMAT3inverse(&m1, &m2)) return FALSE; - - s.n[VX] = cmsD50_XYZ() -> X; - s.n[VY] = cmsD50_XYZ() -> Y; - s.n[VZ] = cmsD50_XYZ() -> Z; - - _cmsMAT3eval(&d, &m2, &s); - - Dest.X = d.n[VX]; - Dest.Y = d.n[VY]; - Dest.Z = d.n[VZ]; - - cmsXYZ2xyY(&DestChromaticity, &Dest); - - if (!cmsTempFromWhitePoint(&TempK, &DestChromaticity)) - return -1.0; - - return TempK; -} - -// Compute a CHAD based on a given temperature -static - void Temp2CHAD(cmsMAT3* Chad, cmsFloat64Number Temp) -{ - cmsCIEXYZ White; - cmsCIExyY ChromaticityOfWhite; - - cmsWhitePointFromTemp(&ChromaticityOfWhite, Temp); - cmsxyY2XYZ(&White, &ChromaticityOfWhite); - _cmsAdaptationMatrix(Chad, NULL, &White, cmsD50_XYZ()); -} - -// Join scalings to obtain relative input to absolute and then to relative output. -// Result is stored in a 3x3 matrix -static -cmsBool ComputeAbsoluteIntent(cmsFloat64Number AdaptationState, - const cmsCIEXYZ* WhitePointIn, - const cmsMAT3* ChromaticAdaptationMatrixIn, - const cmsCIEXYZ* WhitePointOut, - const cmsMAT3* ChromaticAdaptationMatrixOut, - cmsMAT3* m) -{ - cmsMAT3 Scale, m1, m2, m3, m4; - - // Adaptation state - if (AdaptationState == 1.0) { - - // Observer is fully adapted. Keep chromatic adaptation. - // That is the standard V4 behaviour - _cmsVEC3init(&m->v[0], WhitePointIn->X / WhitePointOut->X, 0, 0); - _cmsVEC3init(&m->v[1], 0, WhitePointIn->Y / WhitePointOut->Y, 0); - _cmsVEC3init(&m->v[2], 0, 0, WhitePointIn->Z / WhitePointOut->Z); - - } - else { - - // Incomplete adaptation. This is an advanced feature. - _cmsVEC3init(&Scale.v[0], WhitePointIn->X / WhitePointOut->X, 0, 0); - _cmsVEC3init(&Scale.v[1], 0, WhitePointIn->Y / WhitePointOut->Y, 0); - _cmsVEC3init(&Scale.v[2], 0, 0, WhitePointIn->Z / WhitePointOut->Z); - - - if (AdaptationState == 0.0) { - - m1 = *ChromaticAdaptationMatrixOut; - _cmsMAT3per(&m2, &m1, &Scale); - // m2 holds CHAD from output white to D50 times abs. col. scaling - - // Observer is not adapted, undo the chromatic adaptation - _cmsMAT3per(m, &m2, ChromaticAdaptationMatrixOut); - - m3 = *ChromaticAdaptationMatrixIn; - if (!_cmsMAT3inverse(&m3, &m4)) return FALSE; - _cmsMAT3per(m, &m2, &m4); - - } else { - - cmsMAT3 MixedCHAD; - cmsFloat64Number TempSrc, TempDest, Temp; - - m1 = *ChromaticAdaptationMatrixIn; - if (!_cmsMAT3inverse(&m1, &m2)) return FALSE; - _cmsMAT3per(&m3, &m2, &Scale); - // m3 holds CHAD from input white to D50 times abs. col. scaling - - TempSrc = CHAD2Temp(ChromaticAdaptationMatrixIn); - TempDest = CHAD2Temp(ChromaticAdaptationMatrixOut); - - if (TempSrc < 0.0 || TempDest < 0.0) return FALSE; // Something went wrong - - if (_cmsMAT3isIdentity(&Scale) && fabs(TempSrc - TempDest) < 0.01) { - - _cmsMAT3identity(m); - return TRUE; - } - - Temp = (1.0 - AdaptationState) * TempDest + AdaptationState * TempSrc; - - // Get a CHAD from whatever output temperature to D50. This replaces output CHAD - Temp2CHAD(&MixedCHAD, Temp); - - _cmsMAT3per(m, &m3, &MixedCHAD); - } - - } - return TRUE; - -} - -// Just to see if m matrix should be applied -static -cmsBool IsEmptyLayer(cmsMAT3* m, cmsVEC3* off) -{ - cmsFloat64Number diff = 0; - cmsMAT3 Ident; - int i; - - if (m == NULL && off == NULL) return TRUE; // NULL is allowed as an empty layer - if (m == NULL && off != NULL) return FALSE; // This is an internal error - - _cmsMAT3identity(&Ident); - - for (i=0; i < 3*3; i++) - diff += fabs(((cmsFloat64Number*)m)[i] - ((cmsFloat64Number*)&Ident)[i]); - - for (i=0; i < 3; i++) - diff += fabs(((cmsFloat64Number*)off)[i]); - - - return (diff < 0.002); -} - - -// Compute the conversion layer -static -cmsBool ComputeConversion(int i, cmsHPROFILE hProfiles[], - cmsUInt32Number Intent, - cmsBool BPC, - cmsFloat64Number AdaptationState, - cmsMAT3* m, cmsVEC3* off) -{ - - int k; - - // m and off are set to identity and this is detected latter on - _cmsMAT3identity(m); - _cmsVEC3init(off, 0, 0, 0); - - // If intent is abs. colorimetric, - if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) { - - cmsCIEXYZ WhitePointIn, WhitePointOut; - cmsMAT3 ChromaticAdaptationMatrixIn, ChromaticAdaptationMatrixOut; - - _cmsReadMediaWhitePoint(&WhitePointIn, hProfiles[i-1]); - _cmsReadCHAD(&ChromaticAdaptationMatrixIn, hProfiles[i-1]); - - _cmsReadMediaWhitePoint(&WhitePointOut, hProfiles[i]); - _cmsReadCHAD(&ChromaticAdaptationMatrixOut, hProfiles[i]); - - if (!ComputeAbsoluteIntent(AdaptationState, - &WhitePointIn, &ChromaticAdaptationMatrixIn, - &WhitePointOut, &ChromaticAdaptationMatrixOut, m)) return FALSE; - - } - else { - // Rest of intents may apply BPC. - - if (BPC) { - - cmsCIEXYZ BlackPointIn, BlackPointOut; - - cmsDetectBlackPoint(&BlackPointIn, hProfiles[i-1], Intent, 0); - cmsDetectDestinationBlackPoint(&BlackPointOut, hProfiles[i], Intent, 0); - - // If black points are equal, then do nothing - if (BlackPointIn.X != BlackPointOut.X || - BlackPointIn.Y != BlackPointOut.Y || - BlackPointIn.Z != BlackPointOut.Z) - ComputeBlackPointCompensation(&BlackPointIn, &BlackPointOut, m, off); - } - } - - // Offset should be adjusted because the encoding. We encode XYZ normalized to 0..1.0, - // to do that, we divide by MAX_ENCODEABLE_XZY. The conversion stage goes XYZ -> XYZ so - // we have first to convert from encoded to XYZ and then convert back to encoded. - // y = Mx + Off - // x = x'c - // y = M x'c + Off - // y = y'c; y' = y / c - // y' = (Mx'c + Off) /c = Mx' + (Off / c) - - for (k=0; k < 3; k++) { - off ->n[k] /= MAX_ENCODEABLE_XYZ; - } - - return TRUE; -} - - -// Add a conversion stage if needed. If a matrix/offset m is given, it applies to XYZ space -static -cmsBool AddConversion(cmsPipeline* Result, cmsColorSpaceSignature InPCS, cmsColorSpaceSignature OutPCS, cmsMAT3* m, cmsVEC3* off) -{ - cmsFloat64Number* m_as_dbl = (cmsFloat64Number*) m; - cmsFloat64Number* off_as_dbl = (cmsFloat64Number*) off; - - // Handle PCS mismatches. A specialized stage is added to the LUT in such case - switch (InPCS) { - - case cmsSigXYZData: // Input profile operates in XYZ - - switch (OutPCS) { - - case cmsSigXYZData: // XYZ -> XYZ - if (!IsEmptyLayer(m, off) && - !cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl))) - return FALSE; - break; - - case cmsSigLabData: // XYZ -> Lab - if (!IsEmptyLayer(m, off) && - !cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl))) - return FALSE; - if (!cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocXYZ2Lab(Result ->ContextID))) - return FALSE; - break; - - default: - return FALSE; // Colorspace mismatch - } - break; - - case cmsSigLabData: // Input profile operates in Lab - - switch (OutPCS) { - - case cmsSigXYZData: // Lab -> XYZ - - if (!cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocLab2XYZ(Result ->ContextID))) - return FALSE; - if (!IsEmptyLayer(m, off) && - !cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl))) - return FALSE; - break; - - case cmsSigLabData: // Lab -> Lab - - if (!IsEmptyLayer(m, off)) { - if (!cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocLab2XYZ(Result ->ContextID)) || - !cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl)) || - !cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocXYZ2Lab(Result ->ContextID))) - return FALSE; - } - break; - - default: - return FALSE; // Mismatch - } - break; - - // On colorspaces other than PCS, check for same space - default: - if (InPCS != OutPCS) return FALSE; - break; - } - - return TRUE; -} - - -// Is a given space compatible with another? -static -cmsBool ColorSpaceIsCompatible(cmsColorSpaceSignature a, cmsColorSpaceSignature b) -{ - // If they are same, they are compatible. - if (a == b) return TRUE; - - // Check for MCH4 substitution of CMYK - if ((a == cmsSig4colorData) && (b == cmsSigCmykData)) return TRUE; - if ((a == cmsSigCmykData) && (b == cmsSig4colorData)) return TRUE; - - // Check for XYZ/Lab. Those spaces are interchangeable as they can be computed one from other. - if ((a == cmsSigXYZData) && (b == cmsSigLabData)) return TRUE; - if ((a == cmsSigLabData) && (b == cmsSigXYZData)) return TRUE; - - return FALSE; -} - - -// Default handler for ICC-style intents -static -cmsPipeline* DefaultICCintents(cmsContext ContextID, - cmsUInt32Number nProfiles, - cmsUInt32Number TheIntents[], - cmsHPROFILE hProfiles[], - cmsBool BPC[], - cmsFloat64Number AdaptationStates[], - cmsUInt32Number dwFlags) -{ - cmsPipeline* Lut = NULL; - cmsPipeline* Result; - cmsHPROFILE hProfile; - cmsMAT3 m; - cmsVEC3 off; - cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut, CurrentColorSpace; - cmsProfileClassSignature ClassSig; - cmsUInt32Number i, Intent; - - // For safety - if (nProfiles == 0) return NULL; - - // Allocate an empty LUT for holding the result. 0 as channel count means 'undefined' - Result = cmsPipelineAlloc(ContextID, 0, 0); - if (Result == NULL) return NULL; - - CurrentColorSpace = cmsGetColorSpace(hProfiles[0]); - - for (i=0; i < nProfiles; i++) { - - cmsBool lIsDeviceLink, lIsInput; - - hProfile = hProfiles[i]; - ClassSig = cmsGetDeviceClass(hProfile); - lIsDeviceLink = (ClassSig == cmsSigLinkClass || ClassSig == cmsSigAbstractClass ); - - // First profile is used as input unless devicelink or abstract - if ((i == 0) && !lIsDeviceLink) { - lIsInput = TRUE; - } - else { - // Else use profile in the input direction if current space is not PCS - lIsInput = (CurrentColorSpace != cmsSigXYZData) && - (CurrentColorSpace != cmsSigLabData); - } - - Intent = TheIntents[i]; - - if (lIsInput || lIsDeviceLink) { - - ColorSpaceIn = cmsGetColorSpace(hProfile); - ColorSpaceOut = cmsGetPCS(hProfile); - } - else { - - ColorSpaceIn = cmsGetPCS(hProfile); - ColorSpaceOut = cmsGetColorSpace(hProfile); - } - - if (!ColorSpaceIsCompatible(ColorSpaceIn, CurrentColorSpace)) { - - cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "ColorSpace mismatch"); - goto Error; - } - - // If devicelink is found, then no custom intent is allowed and we can - // read the LUT to be applied. Settings don't apply here. - if (lIsDeviceLink || ((ClassSig == cmsSigNamedColorClass) && (nProfiles == 1))) { - - // Get the involved LUT from the profile - Lut = _cmsReadDevicelinkLUT(hProfile, Intent); - if (Lut == NULL) goto Error; - - // What about abstract profiles? - if (ClassSig == cmsSigAbstractClass && i > 0) { - if (!ComputeConversion(i, hProfiles, Intent, BPC[i], AdaptationStates[i], &m, &off)) goto Error; - } - else { - _cmsMAT3identity(&m); - _cmsVEC3init(&off, 0, 0, 0); - } - - - if (!AddConversion(Result, CurrentColorSpace, ColorSpaceIn, &m, &off)) goto Error; - - } - else { - - if (lIsInput) { - // Input direction means non-pcs connection, so proceed like devicelinks - Lut = _cmsReadInputLUT(hProfile, Intent); - if (Lut == NULL) goto Error; - } - else { - - // Output direction means PCS connection. Intent may apply here - Lut = _cmsReadOutputLUT(hProfile, Intent); - if (Lut == NULL) goto Error; - - - if (!ComputeConversion(i, hProfiles, Intent, BPC[i], AdaptationStates[i], &m, &off)) goto Error; - if (!AddConversion(Result, CurrentColorSpace, ColorSpaceIn, &m, &off)) goto Error; - - } - } - - // Concatenate to the output LUT - if (!cmsPipelineCat(Result, Lut)) - goto Error; - - cmsPipelineFree(Lut); - Lut = NULL; - - // Update current space - CurrentColorSpace = ColorSpaceOut; - } - - return Result; - -Error: - - if (Lut != NULL) cmsPipelineFree(Lut); - if (Result != NULL) cmsPipelineFree(Result); - return NULL; - - cmsUNUSED_PARAMETER(dwFlags); -} - - -// Wrapper for DLL calling convention -cmsPipeline* CMSEXPORT _cmsDefaultICCintents(cmsContext ContextID, - cmsUInt32Number nProfiles, - cmsUInt32Number TheIntents[], - cmsHPROFILE hProfiles[], - cmsBool BPC[], - cmsFloat64Number AdaptationStates[], - cmsUInt32Number dwFlags) -{ - return DefaultICCintents(ContextID, nProfiles, TheIntents, hProfiles, BPC, AdaptationStates, dwFlags); -} - -// Black preserving intents --------------------------------------------------------------------------------------------- - -// Translate black-preserving intents to ICC ones -static -int TranslateNonICCIntents(int Intent) -{ - switch (Intent) { - case INTENT_PRESERVE_K_ONLY_PERCEPTUAL: - case INTENT_PRESERVE_K_PLANE_PERCEPTUAL: - return INTENT_PERCEPTUAL; - - case INTENT_PRESERVE_K_ONLY_RELATIVE_COLORIMETRIC: - case INTENT_PRESERVE_K_PLANE_RELATIVE_COLORIMETRIC: - return INTENT_RELATIVE_COLORIMETRIC; - - case INTENT_PRESERVE_K_ONLY_SATURATION: - case INTENT_PRESERVE_K_PLANE_SATURATION: - return INTENT_SATURATION; - - default: return Intent; - } -} - -// Sampler for Black-only preserving CMYK->CMYK transforms - -typedef struct { - cmsPipeline* cmyk2cmyk; // The original transform - cmsToneCurve* KTone; // Black-to-black tone curve - -} GrayOnlyParams; - - -// Preserve black only if that is the only ink used -static -int BlackPreservingGrayOnlySampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo) -{ - GrayOnlyParams* bp = (GrayOnlyParams*) 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] = cmsEvalToneCurve16(bp->KTone, In[3]); - return TRUE; - } - - // Keep normal transform for other colors - bp ->cmyk2cmyk ->Eval16Fn(In, Out, bp ->cmyk2cmyk->Data); - return TRUE; -} - -// This is the entry for black-preserving K-only intents, which are non-ICC -static -cmsPipeline* BlackPreservingKOnlyIntents(cmsContext ContextID, - cmsUInt32Number nProfiles, - cmsUInt32Number TheIntents[], - cmsHPROFILE hProfiles[], - cmsBool BPC[], - cmsFloat64Number AdaptationStates[], - cmsUInt32Number dwFlags) -{ - GrayOnlyParams bp; - cmsPipeline* Result; - cmsUInt32Number ICCIntents[256]; - cmsStage* CLUT; - cmsUInt32Number i, nGridPoints; - - - // Sanity check - if (nProfiles < 1 || nProfiles > 255) return NULL; - - // Translate black-preserving intents to ICC ones - for (i=0; i < nProfiles; i++) - ICCIntents[i] = TranslateNonICCIntents(TheIntents[i]); - - // Check for non-cmyk profiles - if (cmsGetColorSpace(hProfiles[0]) != cmsSigCmykData || - cmsGetColorSpace(hProfiles[nProfiles-1]) != cmsSigCmykData) - return DefaultICCintents(ContextID, nProfiles, ICCIntents, hProfiles, BPC, AdaptationStates, dwFlags); - - memset(&bp, 0, sizeof(bp)); - - // Allocate an empty LUT for holding the result - Result = cmsPipelineAlloc(ContextID, 4, 4); - if (Result == NULL) return NULL; - - // Create a LUT holding normal ICC transform - bp.cmyk2cmyk = DefaultICCintents(ContextID, - nProfiles, - ICCIntents, - hProfiles, - BPC, - AdaptationStates, - dwFlags); - - if (bp.cmyk2cmyk == NULL) goto Error; - - // Now, compute the tone curve - bp.KTone = _cmsBuildKToneCurve(ContextID, - 4096, - nProfiles, - ICCIntents, - hProfiles, - BPC, - AdaptationStates, - dwFlags); - - if (bp.KTone == NULL) goto Error; - - - // How many gridpoints are we going to use? - nGridPoints = _cmsReasonableGridpointsByColorspace(cmsSigCmykData, dwFlags); - - // Create the CLUT. 16 bits - CLUT = cmsStageAllocCLut16bit(ContextID, nGridPoints, 4, 4, NULL); - if (CLUT == NULL) goto Error; - - // This is the one and only MPE in this LUT - if (!cmsPipelineInsertStage(Result, cmsAT_BEGIN, CLUT)) - goto Error; - - // Sample it. We cannot afford pre/post linearization this time. - if (!cmsStageSampleCLut16bit(CLUT, BlackPreservingGrayOnlySampler, (void*) &bp, 0)) - goto Error; - - // Get rid of xform and tone curve - cmsPipelineFree(bp.cmyk2cmyk); - cmsFreeToneCurve(bp.KTone); - - return Result; - -Error: - - if (bp.cmyk2cmyk != NULL) cmsPipelineFree(bp.cmyk2cmyk); - if (bp.KTone != NULL) cmsFreeToneCurve(bp.KTone); - if (Result != NULL) cmsPipelineFree(Result); - return NULL; - -} - -// K Plane-preserving CMYK to CMYK ------------------------------------------------------------------------------------ - -typedef struct { - - cmsPipeline* cmyk2cmyk; // The original transform - cmsHTRANSFORM hProofOutput; // Output CMYK to Lab (last profile) - cmsHTRANSFORM cmyk2Lab; // The input chain - cmsToneCurve* KTone; // Black-to-black tone curve - cmsPipeline* LabK2cmyk; // The output profile - cmsFloat64Number MaxError; - - cmsHTRANSFORM hRoundTrip; - cmsFloat64Number MaxTAC; - - -} PreserveKPlaneParams; - - -// 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; -} - -// This is the entry for black-plane preserving, which are non-ICC -static -cmsPipeline* BlackPreservingKPlaneIntents(cmsContext ContextID, - cmsUInt32Number nProfiles, - cmsUInt32Number TheIntents[], - cmsHPROFILE hProfiles[], - cmsBool BPC[], - cmsFloat64Number AdaptationStates[], - cmsUInt32Number dwFlags) -{ - PreserveKPlaneParams bp; - cmsPipeline* Result = NULL; - cmsUInt32Number ICCIntents[256]; - cmsStage* CLUT; - cmsUInt32Number i, nGridPoints; - cmsHPROFILE hLab; - - // Sanity check - if (nProfiles < 1 || nProfiles > 255) return NULL; - - // Translate black-preserving intents to ICC ones - for (i=0; i < nProfiles; i++) - ICCIntents[i] = TranslateNonICCIntents(TheIntents[i]); - - // Check for non-cmyk profiles - if (cmsGetColorSpace(hProfiles[0]) != cmsSigCmykData || - !(cmsGetColorSpace(hProfiles[nProfiles-1]) == cmsSigCmykData || - cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigOutputClass)) - return DefaultICCintents(ContextID, nProfiles, ICCIntents, hProfiles, BPC, AdaptationStates, dwFlags); - - // Allocate an empty LUT for holding the result - Result = cmsPipelineAlloc(ContextID, 4, 4); - if (Result == NULL) return NULL; - - - memset(&bp, 0, sizeof(bp)); - - // We need the input LUT of the last profile, assuming this one is responsible of - // black generation. This LUT will be seached in inverse order. - bp.LabK2cmyk = _cmsReadInputLUT(hProfiles[nProfiles-1], INTENT_RELATIVE_COLORIMETRIC); - if (bp.LabK2cmyk == NULL) goto Cleanup; - - // Get total area coverage (in 0..1 domain) - bp.MaxTAC = cmsDetectTAC(hProfiles[nProfiles-1]) / 100.0; - if (bp.MaxTAC <= 0) goto Cleanup; - - - // Create a LUT holding normal ICC transform - bp.cmyk2cmyk = DefaultICCintents(ContextID, - nProfiles, - ICCIntents, - hProfiles, - BPC, - AdaptationStates, - dwFlags); - if (bp.cmyk2cmyk == NULL) goto Cleanup; - - // Now the tone curve - bp.KTone = _cmsBuildKToneCurve(ContextID, 4096, nProfiles, - ICCIntents, - hProfiles, - BPC, - AdaptationStates, - dwFlags); - if (bp.KTone == NULL) goto Cleanup; - - // To measure the output, Last profile to Lab - hLab = cmsCreateLab4ProfileTHR(ContextID, NULL); - bp.hProofOutput = cmsCreateTransformTHR(ContextID, hProfiles[nProfiles-1], - CHANNELS_SH(4)|BYTES_SH(2), hLab, TYPE_Lab_DBL, - INTENT_RELATIVE_COLORIMETRIC, - cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE); - if ( bp.hProofOutput == NULL) goto Cleanup; - - // Same as anterior, but lab in the 0..1 range - bp.cmyk2Lab = cmsCreateTransformTHR(ContextID, hProfiles[nProfiles-1], - FLOAT_SH(1)|CHANNELS_SH(4)|BYTES_SH(4), hLab, - FLOAT_SH(1)|CHANNELS_SH(3)|BYTES_SH(4), - INTENT_RELATIVE_COLORIMETRIC, - cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE); - if (bp.cmyk2Lab == NULL) goto Cleanup; - cmsCloseProfile(hLab); - - // Error estimation (for debug only) - bp.MaxError = 0; - - // How many gridpoints are we going to use? - nGridPoints = _cmsReasonableGridpointsByColorspace(cmsSigCmykData, dwFlags); - - - CLUT = cmsStageAllocCLut16bit(ContextID, nGridPoints, 4, 4, NULL); - if (CLUT == NULL) goto Cleanup; - - if (!cmsPipelineInsertStage(Result, cmsAT_BEGIN, CLUT)) - goto Cleanup; - - cmsStageSampleCLut16bit(CLUT, BlackPreservingSampler, (void*) &bp, 0); - -Cleanup: - - if (bp.cmyk2cmyk) cmsPipelineFree(bp.cmyk2cmyk); - if (bp.cmyk2Lab) cmsDeleteTransform(bp.cmyk2Lab); - if (bp.hProofOutput) cmsDeleteTransform(bp.hProofOutput); - - if (bp.KTone) cmsFreeToneCurve(bp.KTone); - if (bp.LabK2cmyk) cmsPipelineFree(bp.LabK2cmyk); - - return Result; -} - -// Link routines ------------------------------------------------------------------------------------------------------ - -// Chain several profiles into a single LUT. It just checks the parameters and then calls the handler -// for the first intent in chain. The handler may be user-defined. Is up to the handler to deal with the -// rest of intents in chain. A maximum of 255 profiles at time are supported, which is pretty reasonable. -cmsPipeline* _cmsLinkProfiles(cmsContext ContextID, - cmsUInt32Number nProfiles, - cmsUInt32Number TheIntents[], - cmsHPROFILE hProfiles[], - cmsBool BPC[], - cmsFloat64Number AdaptationStates[], - cmsUInt32Number dwFlags) -{ - cmsUInt32Number i; - cmsIntentsList* Intent; - - // Make sure a reasonable number of profiles is provided - if (nProfiles <= 0 || nProfiles > 255) { - cmsSignalError(ContextID, cmsERROR_RANGE, "Couldn't link '%d' profiles", nProfiles); - return NULL; - } - - for (i=0; i < nProfiles; i++) { - - // Check if black point is really needed or allowed. Note that - // following Adobe's document: - // BPC does not apply to devicelink profiles, nor to abs colorimetric, - // and applies always on V4 perceptual and saturation. - - if (TheIntents[i] == INTENT_ABSOLUTE_COLORIMETRIC) - BPC[i] = FALSE; - - if (TheIntents[i] == INTENT_PERCEPTUAL || TheIntents[i] == INTENT_SATURATION) { - - // Force BPC for V4 profiles in perceptual and saturation - if (cmsGetProfileVersion(hProfiles[i]) >= 4.0) - BPC[i] = TRUE; - } - } - - // Search for a handler. The first intent in the chain defines the handler. That would - // prevent using multiple custom intents in a multiintent chain, but the behaviour of - // this case would present some issues if the custom intent tries to do things like - // preserve primaries. This solution is not perfect, but works well on most cases. - - Intent = SearchIntent(ContextID, TheIntents[0]); - if (Intent == NULL) { - cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported intent '%d'", TheIntents[0]); - return NULL; - } - - // Call the handler - return Intent ->Link(ContextID, nProfiles, TheIntents, hProfiles, BPC, AdaptationStates, dwFlags); -} - -// ------------------------------------------------------------------------------------------------- - -// Get information about available intents. nMax is the maximum space for the supplied "Codes" -// and "Descriptions" the function returns the total number of intents, which may be greater -// than nMax, although the matrices are not populated beyond this level. -cmsUInt32Number CMSEXPORT cmsGetSupportedIntentsTHR(cmsContext ContextID, cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions) -{ - _cmsIntentsPluginChunkType* ctx = ( _cmsIntentsPluginChunkType*) _cmsContextGetClientChunk(ContextID, IntentPlugin); - cmsIntentsList* pt; - cmsUInt32Number nIntents; - - - for (nIntents=0, pt = ctx->Intents; pt != NULL; pt = pt -> Next) - { - if (nIntents < nMax) { - if (Codes != NULL) - Codes[nIntents] = pt ->Intent; - - if (Descriptions != NULL) - Descriptions[nIntents] = pt ->Description; - } - - nIntents++; - } - - for (nIntents=0, pt = DefaultIntents; pt != NULL; pt = pt -> Next) - { - if (nIntents < nMax) { - if (Codes != NULL) - Codes[nIntents] = pt ->Intent; - - if (Descriptions != NULL) - Descriptions[nIntents] = pt ->Description; - } - - nIntents++; - } - return nIntents; -} - -cmsUInt32Number CMSEXPORT cmsGetSupportedIntents(cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions) -{ - return cmsGetSupportedIntentsTHR(NULL, nMax, Codes, Descriptions); -} - -// The plug-in registration. User can add new intents or override default routines -cmsBool _cmsRegisterRenderingIntentPlugin(cmsContext id, cmsPluginBase* Data) -{ - _cmsIntentsPluginChunkType* ctx = ( _cmsIntentsPluginChunkType*) _cmsContextGetClientChunk(id, IntentPlugin); - cmsPluginRenderingIntent* Plugin = (cmsPluginRenderingIntent*) Data; - cmsIntentsList* fl; - - // Do we have to reset the custom intents? - if (Data == NULL) { - - ctx->Intents = NULL; - return TRUE; - } - - fl = (cmsIntentsList*) _cmsPluginMalloc(id, sizeof(cmsIntentsList)); - if (fl == NULL) return FALSE; - - - fl ->Intent = Plugin ->Intent; - strncpy(fl ->Description, Plugin ->Description, sizeof(fl ->Description)-1); - fl ->Description[sizeof(fl ->Description)-1] = 0; - - fl ->Link = Plugin ->Link; - - fl ->Next = ctx ->Intents; - ctx ->Intents = fl; - - return TRUE; -} - |