1ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov//--------------------------------------------------------------------------------- 2ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// 3ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Little Color Management System 4ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Copyright (c) 1998-2012 Marti Maria Saguer 5ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// 6ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Permission is hereby granted, free of charge, to any person obtaining 7ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// a copy of this software and associated documentation files (the "Software"), 8ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// to deal in the Software without restriction, including without limitation 9ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// the rights to use, copy, modify, merge, publish, distribute, sublicense, 10ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// and/or sell copies of the Software, and to permit persons to whom the Software 11ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// is furnished to do so, subject to the following conditions: 12ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// 13ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// The above copyright notice and this permission notice shall be included in 14ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// all copies or substantial portions of the Software. 15ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// 16ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 18ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// 24ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov//--------------------------------------------------------------------------------- 25ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// 26ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 27ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov#include "lcms2_internal.h" 28ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 29ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 30ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Link several profiles to obtain a single LUT modelling the whole color transform. Intents, Black point 31ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// compensation and Adaptation parameters may vary across profiles. BPC and Adaptation refers to the PCS 32ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// after the profile. I.e, BPC[0] refers to connexion between profile(0) and profile(1) 33ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsPipeline* _cmsLinkProfiles(cmsContext ContextID, 34ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number nProfiles, 35ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number Intents[], 36ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsHPROFILE hProfiles[], 37ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsBool BPC[], 38ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsFloat64Number AdaptationStates[], 39ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number dwFlags); 40ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 41ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov//--------------------------------------------------------------------------------- 42ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 43ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// This is the default routine for ICC-style intents. A user may decide to override it by using a plugin. 44ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Supported intents are perceptual, relative colorimetric, saturation and ICC-absolute colorimetric 45ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic 46ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsPipeline* DefaultICCintents(cmsContext ContextID, 47ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number nProfiles, 48ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number Intents[], 49ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsHPROFILE hProfiles[], 50ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsBool BPC[], 51ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsFloat64Number AdaptationStates[], 52ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number dwFlags); 53ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 54ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov//--------------------------------------------------------------------------------- 55ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 56ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// This is the entry for black-preserving K-only intents, which are non-ICC. Last profile have to be a output profile 57ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// to do the trick (no devicelinks allowed at that position) 58ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic 59ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsPipeline* BlackPreservingKOnlyIntents(cmsContext ContextID, 60ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number nProfiles, 61ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number Intents[], 62ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsHPROFILE hProfiles[], 63ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsBool BPC[], 64ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsFloat64Number AdaptationStates[], 65ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number dwFlags); 66ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 67ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov//--------------------------------------------------------------------------------- 68ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 69ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// This is the entry for black-plane preserving, which are non-ICC. Again, Last profile have to be a output profile 70ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// to do the trick (no devicelinks allowed at that position) 71ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic 72ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsPipeline* BlackPreservingKPlaneIntents(cmsContext ContextID, 73ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number nProfiles, 74ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number Intents[], 75ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsHPROFILE hProfiles[], 76ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsBool BPC[], 77ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsFloat64Number AdaptationStates[], 78ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number dwFlags); 79ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 80ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov//--------------------------------------------------------------------------------- 81ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 82ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 83ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// This is a structure holding implementations for all supported intents. 84ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovtypedef struct _cms_intents_list { 85ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 86ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number Intent; 87ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov char Description[256]; 88ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsIntentFn Link; 89ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov struct _cms_intents_list* Next; 90ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 91ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov} cmsIntentsList; 92ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 93ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 94ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Built-in intents 95ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic cmsIntentsList DefaultIntents[] = { 96ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 97ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { INTENT_PERCEPTUAL, "Perceptual", DefaultICCintents, &DefaultIntents[1] }, 98ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { INTENT_RELATIVE_COLORIMETRIC, "Relative colorimetric", DefaultICCintents, &DefaultIntents[2] }, 99ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { INTENT_SATURATION, "Saturation", DefaultICCintents, &DefaultIntents[3] }, 100ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { INTENT_ABSOLUTE_COLORIMETRIC, "Absolute colorimetric", DefaultICCintents, &DefaultIntents[4] }, 101ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { INTENT_PRESERVE_K_ONLY_PERCEPTUAL, "Perceptual preserving black ink", BlackPreservingKOnlyIntents, &DefaultIntents[5] }, 102ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { INTENT_PRESERVE_K_ONLY_RELATIVE_COLORIMETRIC, "Relative colorimetric preserving black ink", BlackPreservingKOnlyIntents, &DefaultIntents[6] }, 103ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { INTENT_PRESERVE_K_ONLY_SATURATION, "Saturation preserving black ink", BlackPreservingKOnlyIntents, &DefaultIntents[7] }, 104ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { INTENT_PRESERVE_K_PLANE_PERCEPTUAL, "Perceptual preserving black plane", BlackPreservingKPlaneIntents, &DefaultIntents[8] }, 105ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { INTENT_PRESERVE_K_PLANE_RELATIVE_COLORIMETRIC,"Relative colorimetric preserving black plane", BlackPreservingKPlaneIntents, &DefaultIntents[9] }, 106ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { INTENT_PRESERVE_K_PLANE_SATURATION, "Saturation preserving black plane", BlackPreservingKPlaneIntents, NULL } 107ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov}; 108ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 109ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 110ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// A pointer to the begining of the list 111ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov_cmsIntentsPluginChunkType _cmsIntentsPluginChunk = { NULL }; 112ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 113ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Duplicates the zone of memory used by the plug-in in the new context 114ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic 115ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovvoid DupPluginIntentsList(struct _cmsContext_struct* ctx, 116ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov const struct _cmsContext_struct* src) 117ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{ 118ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov _cmsIntentsPluginChunkType newHead = { NULL }; 119ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsIntentsList* entry; 120ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsIntentsList* Anterior = NULL; 121ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov _cmsIntentsPluginChunkType* head = (_cmsIntentsPluginChunkType*) src->chunks[IntentPlugin]; 122ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 123ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Walk the list copying all nodes 124ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov for (entry = head->Intents; 125ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov entry != NULL; 126ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov entry = entry ->Next) { 127ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 128ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsIntentsList *newEntry = ( cmsIntentsList *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(cmsIntentsList)); 129ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 130ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (newEntry == NULL) 131ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return; 132ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 133ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // We want to keep the linked list order, so this is a little bit tricky 134ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov newEntry -> Next = NULL; 135ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (Anterior) 136ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Anterior -> Next = newEntry; 137ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 138ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Anterior = newEntry; 139ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 140ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (newHead.Intents == NULL) 141ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov newHead.Intents = newEntry; 142ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 143ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 144ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ctx ->chunks[IntentPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsIntentsPluginChunkType)); 145ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov} 146ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 147ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovvoid _cmsAllocIntentsPluginChunk(struct _cmsContext_struct* ctx, 148ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov const struct _cmsContext_struct* src) 149ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{ 150ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (src != NULL) { 151ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 152ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Copy all linked list 153ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov DupPluginIntentsList(ctx, src); 154ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 155ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else { 156ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov static _cmsIntentsPluginChunkType IntentsPluginChunkType = { NULL }; 157ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ctx ->chunks[IntentPlugin] = _cmsSubAllocDup(ctx ->MemPool, &IntentsPluginChunkType, sizeof(_cmsIntentsPluginChunkType)); 158ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 159ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov} 160ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 161ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 162ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Search the list for a suitable intent. Returns NULL if not found 163ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic 164ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsIntentsList* SearchIntent(cmsContext ContextID, cmsUInt32Number Intent) 165ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{ 166ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov _cmsIntentsPluginChunkType* ctx = ( _cmsIntentsPluginChunkType*) _cmsContextGetClientChunk(ContextID, IntentPlugin); 167ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsIntentsList* pt; 168ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 169ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov for (pt = ctx -> Intents; pt != NULL; pt = pt -> Next) 170ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (pt ->Intent == Intent) return pt; 171ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 172ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov for (pt = DefaultIntents; pt != NULL; pt = pt -> Next) 173ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (pt ->Intent == Intent) return pt; 174ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 175ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return NULL; 176ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov} 177ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 178ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Black point compensation. Implemented as a linear scaling in XYZ. Black points 179ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// should come relative to the white point. Fills an matrix/offset element m 180ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// which is organized as a 4x4 matrix. 181ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic 182ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovvoid ComputeBlackPointCompensation(const cmsCIEXYZ* BlackPointIn, 183ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov const cmsCIEXYZ* BlackPointOut, 184ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsMAT3* m, cmsVEC3* off) 185ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{ 186ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsFloat64Number ax, ay, az, bx, by, bz, tx, ty, tz; 187ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 188ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Now we need to compute a matrix plus an offset m and of such of 189ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // [m]*bpin + off = bpout 190ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // [m]*D50 + off = D50 191ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // 192ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // This is a linear scaling in the form ax+b, where 193ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // a = (bpout - D50) / (bpin - D50) 194ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // b = - D50* (bpout - bpin) / (bpin - D50) 195ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 196ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov tx = BlackPointIn->X - cmsD50_XYZ()->X; 197ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ty = BlackPointIn->Y - cmsD50_XYZ()->Y; 198ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov tz = BlackPointIn->Z - cmsD50_XYZ()->Z; 199ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 200ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ax = (BlackPointOut->X - cmsD50_XYZ()->X) / tx; 201ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ay = (BlackPointOut->Y - cmsD50_XYZ()->Y) / ty; 202ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov az = (BlackPointOut->Z - cmsD50_XYZ()->Z) / tz; 203ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 204ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov bx = - cmsD50_XYZ()-> X * (BlackPointOut->X - BlackPointIn->X) / tx; 205ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov by = - cmsD50_XYZ()-> Y * (BlackPointOut->Y - BlackPointIn->Y) / ty; 206ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov bz = - cmsD50_XYZ()-> Z * (BlackPointOut->Z - BlackPointIn->Z) / tz; 207ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 208ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov _cmsVEC3init(&m ->v[0], ax, 0, 0); 209ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov _cmsVEC3init(&m ->v[1], 0, ay, 0); 210ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov _cmsVEC3init(&m ->v[2], 0, 0, az); 211ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov _cmsVEC3init(off, bx, by, bz); 212ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 213ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov} 214ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 215ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 216ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Approximate a blackbody illuminant based on CHAD information 217ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic 218ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsFloat64Number CHAD2Temp(const cmsMAT3* Chad) 219ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{ 220ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Convert D50 across inverse CHAD to get the absolute white point 221ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsVEC3 d, s; 222ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsCIEXYZ Dest; 223ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsCIExyY DestChromaticity; 224ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsFloat64Number TempK; 225ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsMAT3 m1, m2; 226ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 227ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov m1 = *Chad; 228ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (!_cmsMAT3inverse(&m1, &m2)) return FALSE; 229ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 230ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov s.n[VX] = cmsD50_XYZ() -> X; 231ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov s.n[VY] = cmsD50_XYZ() -> Y; 232ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov s.n[VZ] = cmsD50_XYZ() -> Z; 233ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 234ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov _cmsMAT3eval(&d, &m2, &s); 235ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 236ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Dest.X = d.n[VX]; 237ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Dest.Y = d.n[VY]; 238ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Dest.Z = d.n[VZ]; 239ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 240ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsXYZ2xyY(&DestChromaticity, &Dest); 241ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 242ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (!cmsTempFromWhitePoint(&TempK, &DestChromaticity)) 243ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return -1.0; 244ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 245ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return TempK; 246ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov} 247ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 248ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Compute a CHAD based on a given temperature 249ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic 250ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov void Temp2CHAD(cmsMAT3* Chad, cmsFloat64Number Temp) 251ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{ 252ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsCIEXYZ White; 253ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsCIExyY ChromaticityOfWhite; 254ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 255ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsWhitePointFromTemp(&ChromaticityOfWhite, Temp); 256ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsxyY2XYZ(&White, &ChromaticityOfWhite); 257ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov _cmsAdaptationMatrix(Chad, NULL, &White, cmsD50_XYZ()); 258ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov} 259ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 260ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Join scalings to obtain relative input to absolute and then to relative output. 261ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Result is stored in a 3x3 matrix 262ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic 263ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsBool ComputeAbsoluteIntent(cmsFloat64Number AdaptationState, 264ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov const cmsCIEXYZ* WhitePointIn, 265ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov const cmsMAT3* ChromaticAdaptationMatrixIn, 266ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov const cmsCIEXYZ* WhitePointOut, 267ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov const cmsMAT3* ChromaticAdaptationMatrixOut, 268ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsMAT3* m) 269ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{ 270ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsMAT3 Scale, m1, m2, m3, m4; 271ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 272ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Adaptation state 273ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (AdaptationState == 1.0) { 274ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 275ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Observer is fully adapted. Keep chromatic adaptation. 276ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // That is the standard V4 behaviour 277ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov _cmsVEC3init(&m->v[0], WhitePointIn->X / WhitePointOut->X, 0, 0); 278ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov _cmsVEC3init(&m->v[1], 0, WhitePointIn->Y / WhitePointOut->Y, 0); 279ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov _cmsVEC3init(&m->v[2], 0, 0, WhitePointIn->Z / WhitePointOut->Z); 280ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 281ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 282ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else { 283ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 284ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Incomplete adaptation. This is an advanced feature. 285ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov _cmsVEC3init(&Scale.v[0], WhitePointIn->X / WhitePointOut->X, 0, 0); 286ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov _cmsVEC3init(&Scale.v[1], 0, WhitePointIn->Y / WhitePointOut->Y, 0); 287ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov _cmsVEC3init(&Scale.v[2], 0, 0, WhitePointIn->Z / WhitePointOut->Z); 288ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 289ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 290ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (AdaptationState == 0.0) { 291ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 292ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov m1 = *ChromaticAdaptationMatrixOut; 293ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov _cmsMAT3per(&m2, &m1, &Scale); 294ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // m2 holds CHAD from output white to D50 times abs. col. scaling 295ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 296ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Observer is not adapted, undo the chromatic adaptation 297ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov _cmsMAT3per(m, &m2, ChromaticAdaptationMatrixOut); 298ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 299ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov m3 = *ChromaticAdaptationMatrixIn; 300ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (!_cmsMAT3inverse(&m3, &m4)) return FALSE; 301ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov _cmsMAT3per(m, &m2, &m4); 302ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 303ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } else { 304ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 305ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsMAT3 MixedCHAD; 306ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsFloat64Number TempSrc, TempDest, Temp; 307ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 308ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov m1 = *ChromaticAdaptationMatrixIn; 309ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (!_cmsMAT3inverse(&m1, &m2)) return FALSE; 310ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov _cmsMAT3per(&m3, &m2, &Scale); 311ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // m3 holds CHAD from input white to D50 times abs. col. scaling 312ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 313ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov TempSrc = CHAD2Temp(ChromaticAdaptationMatrixIn); 314ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov TempDest = CHAD2Temp(ChromaticAdaptationMatrixOut); 315ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 316ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (TempSrc < 0.0 || TempDest < 0.0) return FALSE; // Something went wrong 317ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 318ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (_cmsMAT3isIdentity(&Scale) && fabs(TempSrc - TempDest) < 0.01) { 319ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 320ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov _cmsMAT3identity(m); 321ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return TRUE; 322ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 323ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 324ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Temp = (1.0 - AdaptationState) * TempDest + AdaptationState * TempSrc; 325ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 326ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Get a CHAD from whatever output temperature to D50. This replaces output CHAD 327ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Temp2CHAD(&MixedCHAD, Temp); 328ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 329ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov _cmsMAT3per(m, &m3, &MixedCHAD); 330ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 331ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 332ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 333ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return TRUE; 334ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 335ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov} 336ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 337ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Just to see if m matrix should be applied 338ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic 339ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsBool IsEmptyLayer(cmsMAT3* m, cmsVEC3* off) 340ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{ 341ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsFloat64Number diff = 0; 342ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsMAT3 Ident; 343ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov int i; 344ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 345ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (m == NULL && off == NULL) return TRUE; // NULL is allowed as an empty layer 346ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (m == NULL && off != NULL) return FALSE; // This is an internal error 347ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 348ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov _cmsMAT3identity(&Ident); 349ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 350ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov for (i=0; i < 3*3; i++) 351ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov diff += fabs(((cmsFloat64Number*)m)[i] - ((cmsFloat64Number*)&Ident)[i]); 352ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 353ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov for (i=0; i < 3; i++) 354ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov diff += fabs(((cmsFloat64Number*)off)[i]); 355ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 356ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 357ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return (diff < 0.002); 358ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov} 359ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 360ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 361ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Compute the conversion layer 362ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic 363ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsBool ComputeConversion(int i, cmsHPROFILE hProfiles[], 364ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number Intent, 365ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsBool BPC, 366ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsFloat64Number AdaptationState, 367ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsMAT3* m, cmsVEC3* off) 368ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{ 369ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 370ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov int k; 371ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 372ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // m and off are set to identity and this is detected latter on 373ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov _cmsMAT3identity(m); 374ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov _cmsVEC3init(off, 0, 0, 0); 375ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 376ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // If intent is abs. colorimetric, 377ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (Intent == INTENT_ABSOLUTE_COLORIMETRIC) { 378ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 379ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsCIEXYZ WhitePointIn, WhitePointOut; 380ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsMAT3 ChromaticAdaptationMatrixIn, ChromaticAdaptationMatrixOut; 381ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 382ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov _cmsReadMediaWhitePoint(&WhitePointIn, hProfiles[i-1]); 383ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov _cmsReadCHAD(&ChromaticAdaptationMatrixIn, hProfiles[i-1]); 384ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 385ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov _cmsReadMediaWhitePoint(&WhitePointOut, hProfiles[i]); 386ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov _cmsReadCHAD(&ChromaticAdaptationMatrixOut, hProfiles[i]); 387ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 388ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (!ComputeAbsoluteIntent(AdaptationState, 389ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov &WhitePointIn, &ChromaticAdaptationMatrixIn, 390ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov &WhitePointOut, &ChromaticAdaptationMatrixOut, m)) return FALSE; 391ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 392ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 393ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else { 394ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Rest of intents may apply BPC. 395ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 396ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (BPC) { 397ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 398ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsCIEXYZ BlackPointIn, BlackPointOut; 399ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 400ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsDetectBlackPoint(&BlackPointIn, hProfiles[i-1], Intent, 0); 401ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsDetectDestinationBlackPoint(&BlackPointOut, hProfiles[i], Intent, 0); 402ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 403ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // If black points are equal, then do nothing 404ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (BlackPointIn.X != BlackPointOut.X || 405ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov BlackPointIn.Y != BlackPointOut.Y || 406ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov BlackPointIn.Z != BlackPointOut.Z) 407ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ComputeBlackPointCompensation(&BlackPointIn, &BlackPointOut, m, off); 408ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 409ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 410ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 411ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Offset should be adjusted because the encoding. We encode XYZ normalized to 0..1.0, 412ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // to do that, we divide by MAX_ENCODEABLE_XZY. The conversion stage goes XYZ -> XYZ so 413ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // we have first to convert from encoded to XYZ and then convert back to encoded. 414ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // y = Mx + Off 415ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // x = x'c 416ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // y = M x'c + Off 417ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // y = y'c; y' = y / c 418ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // y' = (Mx'c + Off) /c = Mx' + (Off / c) 419ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 420ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov for (k=0; k < 3; k++) { 421ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov off ->n[k] /= MAX_ENCODEABLE_XYZ; 422ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 423ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 424ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return TRUE; 425ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov} 426ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 427ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 428ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Add a conversion stage if needed. If a matrix/offset m is given, it applies to XYZ space 429ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic 430ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsBool AddConversion(cmsPipeline* Result, cmsColorSpaceSignature InPCS, cmsColorSpaceSignature OutPCS, cmsMAT3* m, cmsVEC3* off) 431ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{ 432ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsFloat64Number* m_as_dbl = (cmsFloat64Number*) m; 433ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsFloat64Number* off_as_dbl = (cmsFloat64Number*) off; 434ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 435ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Handle PCS mismatches. A specialized stage is added to the LUT in such case 436ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov switch (InPCS) { 437ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 438ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov case cmsSigXYZData: // Input profile operates in XYZ 439ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 440ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov switch (OutPCS) { 441ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 442ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov case cmsSigXYZData: // XYZ -> XYZ 443ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (!IsEmptyLayer(m, off) && 444ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov !cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl))) 445ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return FALSE; 446ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov break; 447ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 448ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov case cmsSigLabData: // XYZ -> Lab 449ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (!IsEmptyLayer(m, off) && 450ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov !cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl))) 451ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return FALSE; 452ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (!cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocXYZ2Lab(Result ->ContextID))) 453ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return FALSE; 454ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov break; 455ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 456ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov default: 457ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return FALSE; // Colorspace mismatch 458ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 459ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov break; 460ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 461ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov case cmsSigLabData: // Input profile operates in Lab 462ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 463ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov switch (OutPCS) { 464ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 465ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov case cmsSigXYZData: // Lab -> XYZ 466ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 467ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (!cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocLab2XYZ(Result ->ContextID))) 468ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return FALSE; 469ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (!IsEmptyLayer(m, off) && 470ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov !cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl))) 471ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return FALSE; 472ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov break; 473ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 474ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov case cmsSigLabData: // Lab -> Lab 475ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 476ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (!IsEmptyLayer(m, off)) { 477ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (!cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocLab2XYZ(Result ->ContextID)) || 478ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov !cmsPipelineInsertStage(Result, cmsAT_END, cmsStageAllocMatrix(Result ->ContextID, 3, 3, m_as_dbl, off_as_dbl)) || 479ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov !cmsPipelineInsertStage(Result, cmsAT_END, _cmsStageAllocXYZ2Lab(Result ->ContextID))) 480ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return FALSE; 481ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 482ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov break; 483ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 484ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov default: 485ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return FALSE; // Mismatch 486ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 487ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov break; 488ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 489ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // On colorspaces other than PCS, check for same space 490ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov default: 491ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (InPCS != OutPCS) return FALSE; 492ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov break; 493ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 494ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 495ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return TRUE; 496ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov} 497ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 498ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 499ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Is a given space compatible with another? 500ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic 501ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsBool ColorSpaceIsCompatible(cmsColorSpaceSignature a, cmsColorSpaceSignature b) 502ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{ 503ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // If they are same, they are compatible. 504ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (a == b) return TRUE; 505ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 506ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Check for MCH4 substitution of CMYK 507ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ((a == cmsSig4colorData) && (b == cmsSigCmykData)) return TRUE; 508ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ((a == cmsSigCmykData) && (b == cmsSig4colorData)) return TRUE; 509ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 510ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Check for XYZ/Lab. Those spaces are interchangeable as they can be computed one from other. 511ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ((a == cmsSigXYZData) && (b == cmsSigLabData)) return TRUE; 512ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ((a == cmsSigLabData) && (b == cmsSigXYZData)) return TRUE; 513ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 514ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return FALSE; 515ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov} 516ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 517ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 518ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Default handler for ICC-style intents 519ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic 520ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsPipeline* DefaultICCintents(cmsContext ContextID, 521ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number nProfiles, 522ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number TheIntents[], 523ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsHPROFILE hProfiles[], 524ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsBool BPC[], 525ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsFloat64Number AdaptationStates[], 526ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number dwFlags) 527ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{ 528ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsPipeline* Lut = NULL; 529ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsPipeline* Result; 530ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsHPROFILE hProfile; 531ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsMAT3 m; 532ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsVEC3 off; 533ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut, CurrentColorSpace; 534ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsProfileClassSignature ClassSig; 535ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number i, Intent; 536ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 537ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // For safety 538ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (nProfiles == 0) return NULL; 539ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 540ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Allocate an empty LUT for holding the result. 0 as channel count means 'undefined' 541ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Result = cmsPipelineAlloc(ContextID, 0, 0); 542ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (Result == NULL) return NULL; 543ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 544ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CurrentColorSpace = cmsGetColorSpace(hProfiles[0]); 545ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 546ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov for (i=0; i < nProfiles; i++) { 547ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 548ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsBool lIsDeviceLink, lIsInput; 549ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 550ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hProfile = hProfiles[i]; 551ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ClassSig = cmsGetDeviceClass(hProfile); 552ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov lIsDeviceLink = (ClassSig == cmsSigLinkClass || ClassSig == cmsSigAbstractClass ); 553ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 554ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // First profile is used as input unless devicelink or abstract 555ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ((i == 0) && !lIsDeviceLink) { 556ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov lIsInput = TRUE; 557ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 558ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else { 559ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Else use profile in the input direction if current space is not PCS 560ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov lIsInput = (CurrentColorSpace != cmsSigXYZData) && 561ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov (CurrentColorSpace != cmsSigLabData); 562ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 563ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 564ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Intent = TheIntents[i]; 565ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 566ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (lIsInput || lIsDeviceLink) { 567ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 568ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ColorSpaceIn = cmsGetColorSpace(hProfile); 569ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ColorSpaceOut = cmsGetPCS(hProfile); 570ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 571ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else { 572ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 573ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ColorSpaceIn = cmsGetPCS(hProfile); 574ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ColorSpaceOut = cmsGetColorSpace(hProfile); 575ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 576ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 577ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (!ColorSpaceIsCompatible(ColorSpaceIn, CurrentColorSpace)) { 578ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 579ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "ColorSpace mismatch"); 580ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov goto Error; 581ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 582ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 583ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // If devicelink is found, then no custom intent is allowed and we can 584ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // read the LUT to be applied. Settings don't apply here. 585ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (lIsDeviceLink || ((ClassSig == cmsSigNamedColorClass) && (nProfiles == 1))) { 586ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 587ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Get the involved LUT from the profile 588ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Lut = _cmsReadDevicelinkLUT(hProfile, Intent); 589ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (Lut == NULL) goto Error; 590ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 591ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // What about abstract profiles? 592ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (ClassSig == cmsSigAbstractClass && i > 0) { 593ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (!ComputeConversion(i, hProfiles, Intent, BPC[i], AdaptationStates[i], &m, &off)) goto Error; 594ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 595ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else { 596ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov _cmsMAT3identity(&m); 597ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov _cmsVEC3init(&off, 0, 0, 0); 598ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 599ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 600ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 601ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (!AddConversion(Result, CurrentColorSpace, ColorSpaceIn, &m, &off)) goto Error; 602ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 603ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 604ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else { 605ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 606ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (lIsInput) { 607ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Input direction means non-pcs connection, so proceed like devicelinks 608ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Lut = _cmsReadInputLUT(hProfile, Intent); 609ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (Lut == NULL) goto Error; 610ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 611ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else { 612ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 613ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Output direction means PCS connection. Intent may apply here 614ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Lut = _cmsReadOutputLUT(hProfile, Intent); 615ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (Lut == NULL) goto Error; 616ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 617ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 618ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (!ComputeConversion(i, hProfiles, Intent, BPC[i], AdaptationStates[i], &m, &off)) goto Error; 619ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (!AddConversion(Result, CurrentColorSpace, ColorSpaceIn, &m, &off)) goto Error; 620ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 621ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 622ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 623ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 624ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Concatenate to the output LUT 625ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (!cmsPipelineCat(Result, Lut)) 626ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov goto Error; 627ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 628ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsPipelineFree(Lut); 629ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Lut = NULL; 630ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 631ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Update current space 632ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CurrentColorSpace = ColorSpaceOut; 633ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 634ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 635ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return Result; 636ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 637ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovError: 638ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 639ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (Lut != NULL) cmsPipelineFree(Lut); 640ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (Result != NULL) cmsPipelineFree(Result); 641ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return NULL; 642ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 643ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUNUSED_PARAMETER(dwFlags); 644ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov} 645ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 646ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 647ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Wrapper for DLL calling convention 648ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsPipeline* CMSEXPORT _cmsDefaultICCintents(cmsContext ContextID, 649ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number nProfiles, 650ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number TheIntents[], 651ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsHPROFILE hProfiles[], 652ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsBool BPC[], 653ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsFloat64Number AdaptationStates[], 654ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number dwFlags) 655ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{ 656ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return DefaultICCintents(ContextID, nProfiles, TheIntents, hProfiles, BPC, AdaptationStates, dwFlags); 657ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov} 658ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 659ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Black preserving intents --------------------------------------------------------------------------------------------- 660ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 661ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Translate black-preserving intents to ICC ones 662ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic 663ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovint TranslateNonICCIntents(int Intent) 664ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{ 665ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov switch (Intent) { 666ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov case INTENT_PRESERVE_K_ONLY_PERCEPTUAL: 667ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov case INTENT_PRESERVE_K_PLANE_PERCEPTUAL: 668ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return INTENT_PERCEPTUAL; 669ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 670ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov case INTENT_PRESERVE_K_ONLY_RELATIVE_COLORIMETRIC: 671ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov case INTENT_PRESERVE_K_PLANE_RELATIVE_COLORIMETRIC: 672ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return INTENT_RELATIVE_COLORIMETRIC; 673ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 674ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov case INTENT_PRESERVE_K_ONLY_SATURATION: 675ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov case INTENT_PRESERVE_K_PLANE_SATURATION: 676ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return INTENT_SATURATION; 677ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 678ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov default: return Intent; 679ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 680ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov} 681ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 682ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Sampler for Black-only preserving CMYK->CMYK transforms 683ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 684ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovtypedef struct { 685ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsPipeline* cmyk2cmyk; // The original transform 686ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsToneCurve* KTone; // Black-to-black tone curve 687ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 688ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov} GrayOnlyParams; 689ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 690ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 691ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Preserve black only if that is the only ink used 692ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic 693ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovint BlackPreservingGrayOnlySampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo) 694ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{ 695ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov GrayOnlyParams* bp = (GrayOnlyParams*) Cargo; 696ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 697ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // If going across black only, keep black only 698ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (In[0] == 0 && In[1] == 0 && In[2] == 0) { 699ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 700ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // TAC does not apply because it is black ink! 701ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Out[0] = Out[1] = Out[2] = 0; 702ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Out[3] = cmsEvalToneCurve16(bp->KTone, In[3]); 703ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return TRUE; 704ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 705ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 706ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Keep normal transform for other colors 707ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov bp ->cmyk2cmyk ->Eval16Fn(In, Out, bp ->cmyk2cmyk->Data); 708ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return TRUE; 709ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov} 710ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 711ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// This is the entry for black-preserving K-only intents, which are non-ICC 712ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic 713ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsPipeline* BlackPreservingKOnlyIntents(cmsContext ContextID, 714ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number nProfiles, 715ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number TheIntents[], 716ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsHPROFILE hProfiles[], 717ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsBool BPC[], 718ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsFloat64Number AdaptationStates[], 719ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number dwFlags) 720ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{ 721ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov GrayOnlyParams bp; 722ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsPipeline* Result; 723ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number ICCIntents[256]; 724ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsStage* CLUT; 725ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number i, nGridPoints; 726ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 727ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 728ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Sanity check 729ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (nProfiles < 1 || nProfiles > 255) return NULL; 730ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 731ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Translate black-preserving intents to ICC ones 732ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov for (i=0; i < nProfiles; i++) 733ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ICCIntents[i] = TranslateNonICCIntents(TheIntents[i]); 734ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 735ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Check for non-cmyk profiles 736ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (cmsGetColorSpace(hProfiles[0]) != cmsSigCmykData || 737ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsGetColorSpace(hProfiles[nProfiles-1]) != cmsSigCmykData) 738ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return DefaultICCintents(ContextID, nProfiles, ICCIntents, hProfiles, BPC, AdaptationStates, dwFlags); 739ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 740ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov memset(&bp, 0, sizeof(bp)); 741ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 742ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Allocate an empty LUT for holding the result 743ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Result = cmsPipelineAlloc(ContextID, 4, 4); 744ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (Result == NULL) return NULL; 745ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 746ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Create a LUT holding normal ICC transform 747ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov bp.cmyk2cmyk = DefaultICCintents(ContextID, 748ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov nProfiles, 749ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ICCIntents, 750ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hProfiles, 751ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov BPC, 752ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov AdaptationStates, 753ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov dwFlags); 754ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 755ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (bp.cmyk2cmyk == NULL) goto Error; 756ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 757ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Now, compute the tone curve 758ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov bp.KTone = _cmsBuildKToneCurve(ContextID, 759ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 4096, 760ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov nProfiles, 761ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ICCIntents, 762ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hProfiles, 763ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov BPC, 764ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov AdaptationStates, 765ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov dwFlags); 766ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 767ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (bp.KTone == NULL) goto Error; 768ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 769ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 770ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // How many gridpoints are we going to use? 771ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov nGridPoints = _cmsReasonableGridpointsByColorspace(cmsSigCmykData, dwFlags); 772ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 773ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Create the CLUT. 16 bits 774ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CLUT = cmsStageAllocCLut16bit(ContextID, nGridPoints, 4, 4, NULL); 775ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (CLUT == NULL) goto Error; 776ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 777ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // This is the one and only MPE in this LUT 778ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (!cmsPipelineInsertStage(Result, cmsAT_BEGIN, CLUT)) 779ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov goto Error; 780ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 781ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Sample it. We cannot afford pre/post linearization this time. 782ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (!cmsStageSampleCLut16bit(CLUT, BlackPreservingGrayOnlySampler, (void*) &bp, 0)) 783ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov goto Error; 784ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 785ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Get rid of xform and tone curve 786ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsPipelineFree(bp.cmyk2cmyk); 787ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsFreeToneCurve(bp.KTone); 788ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 789ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return Result; 790ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 791ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovError: 792ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 793ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (bp.cmyk2cmyk != NULL) cmsPipelineFree(bp.cmyk2cmyk); 794ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (bp.KTone != NULL) cmsFreeToneCurve(bp.KTone); 795ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (Result != NULL) cmsPipelineFree(Result); 796ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return NULL; 797ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 798ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov} 799ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 800ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// K Plane-preserving CMYK to CMYK ------------------------------------------------------------------------------------ 801ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 802ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovtypedef struct { 803ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 804ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsPipeline* cmyk2cmyk; // The original transform 805ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsHTRANSFORM hProofOutput; // Output CMYK to Lab (last profile) 806ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsHTRANSFORM cmyk2Lab; // The input chain 807ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsToneCurve* KTone; // Black-to-black tone curve 808ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsPipeline* LabK2cmyk; // The output profile 809ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsFloat64Number MaxError; 810ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 811ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsHTRANSFORM hRoundTrip; 812ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsFloat64Number MaxTAC; 813ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 814ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 815ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov} PreserveKPlaneParams; 816ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 817ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 818ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// The CLUT will be stored at 16 bits, but calculations are performed at cmsFloat32Number precision 819ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic 820ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovint BlackPreservingSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo) 821ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{ 822ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov int i; 823ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsFloat32Number Inf[4], Outf[4]; 824ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsFloat32Number LabK[4]; 825ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsFloat64Number SumCMY, SumCMYK, Error, Ratio; 826ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsCIELab ColorimetricLab, BlackPreservingLab; 827ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov PreserveKPlaneParams* bp = (PreserveKPlaneParams*) Cargo; 828ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 829ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Convert from 16 bits to floating point 830ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov for (i=0; i < 4; i++) 831ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Inf[i] = (cmsFloat32Number) (In[i] / 65535.0); 832ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 833ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Get the K across Tone curve 834ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov LabK[3] = cmsEvalToneCurveFloat(bp ->KTone, Inf[3]); 835ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 836ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // If going across black only, keep black only 837ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (In[0] == 0 && In[1] == 0 && In[2] == 0) { 838ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 839ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Out[0] = Out[1] = Out[2] = 0; 840ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Out[3] = _cmsQuickSaturateWord(LabK[3] * 65535.0); 841ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return TRUE; 842ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 843ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 844ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Try the original transform, 845ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsPipelineEvalFloat( Inf, Outf, bp ->cmyk2cmyk); 846ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 847ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Store a copy of the floating point result into 16-bit 848ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov for (i=0; i < 4; i++) 849ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Out[i] = _cmsQuickSaturateWord(Outf[i] * 65535.0); 850ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 851ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Maybe K is already ok (mostly on K=0) 852ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( fabs(Outf[3] - LabK[3]) < (3.0 / 65535.0) ) { 853ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return TRUE; 854ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 855ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 856ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // K differ, mesure and keep Lab measurement for further usage 857ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // this is done in relative colorimetric intent 858ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsDoTransform(bp->hProofOutput, Out, &ColorimetricLab, 1); 859ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 860ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Is not black only and the transform doesn't keep black. 861ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Obtain the Lab of output CMYK. After that we have Lab + K 862ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsDoTransform(bp ->cmyk2Lab, Outf, LabK, 1); 863ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 864ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Obtain the corresponding CMY using reverse interpolation 865ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // (K is fixed in LabK[3]) 866ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (!cmsPipelineEvalReverseFloat(LabK, Outf, Outf, bp ->LabK2cmyk)) { 867ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 868ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Cannot find a suitable value, so use colorimetric xform 869ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // which is already stored in Out[] 870ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return TRUE; 871ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 872ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 873ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Make sure to pass thru K (which now is fixed) 874ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Outf[3] = LabK[3]; 875ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 876ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Apply TAC if needed 877ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov SumCMY = Outf[0] + Outf[1] + Outf[2]; 878ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov SumCMYK = SumCMY + Outf[3]; 879ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 880ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (SumCMYK > bp ->MaxTAC) { 881ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 882ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Ratio = 1 - ((SumCMYK - bp->MaxTAC) / SumCMY); 883ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (Ratio < 0) 884ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Ratio = 0; 885ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 886ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov else 887ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Ratio = 1.0; 888ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 889ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Out[0] = _cmsQuickSaturateWord(Outf[0] * Ratio * 65535.0); // C 890ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Out[1] = _cmsQuickSaturateWord(Outf[1] * Ratio * 65535.0); // M 891ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Out[2] = _cmsQuickSaturateWord(Outf[2] * Ratio * 65535.0); // Y 892ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Out[3] = _cmsQuickSaturateWord(Outf[3] * 65535.0); 893ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 894ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Estimate the error (this goes 16 bits to Lab DBL) 895ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsDoTransform(bp->hProofOutput, Out, &BlackPreservingLab, 1); 896ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Error = cmsDeltaE(&ColorimetricLab, &BlackPreservingLab); 897ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (Error > bp -> MaxError) 898ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov bp->MaxError = Error; 899ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 900ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return TRUE; 901ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov} 902ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 903ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// This is the entry for black-plane preserving, which are non-ICC 904ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganovstatic 905ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsPipeline* BlackPreservingKPlaneIntents(cmsContext ContextID, 906ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number nProfiles, 907ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number TheIntents[], 908ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsHPROFILE hProfiles[], 909ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsBool BPC[], 910ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsFloat64Number AdaptationStates[], 911ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number dwFlags) 912ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{ 913ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov PreserveKPlaneParams bp; 914ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsPipeline* Result = NULL; 915ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number ICCIntents[256]; 916ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsStage* CLUT; 917ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number i, nGridPoints; 918ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsHPROFILE hLab; 919ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 920ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Sanity check 921ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (nProfiles < 1 || nProfiles > 255) return NULL; 922ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 923ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Translate black-preserving intents to ICC ones 924ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov for (i=0; i < nProfiles; i++) 925ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ICCIntents[i] = TranslateNonICCIntents(TheIntents[i]); 926ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 927ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Check for non-cmyk profiles 928ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (cmsGetColorSpace(hProfiles[0]) != cmsSigCmykData || 929ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov !(cmsGetColorSpace(hProfiles[nProfiles-1]) == cmsSigCmykData || 930ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigOutputClass)) 931ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return DefaultICCintents(ContextID, nProfiles, ICCIntents, hProfiles, BPC, AdaptationStates, dwFlags); 932ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 933ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Allocate an empty LUT for holding the result 934ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Result = cmsPipelineAlloc(ContextID, 4, 4); 935ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (Result == NULL) return NULL; 936ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 937ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 938ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov memset(&bp, 0, sizeof(bp)); 939ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 940ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // We need the input LUT of the last profile, assuming this one is responsible of 941ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // black generation. This LUT will be seached in inverse order. 942ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov bp.LabK2cmyk = _cmsReadInputLUT(hProfiles[nProfiles-1], INTENT_RELATIVE_COLORIMETRIC); 943ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (bp.LabK2cmyk == NULL) goto Cleanup; 944ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 945ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Get total area coverage (in 0..1 domain) 946ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov bp.MaxTAC = cmsDetectTAC(hProfiles[nProfiles-1]) / 100.0; 947ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (bp.MaxTAC <= 0) goto Cleanup; 948ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 949ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 950ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Create a LUT holding normal ICC transform 951ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov bp.cmyk2cmyk = DefaultICCintents(ContextID, 952ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov nProfiles, 953ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ICCIntents, 954ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hProfiles, 955ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov BPC, 956ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov AdaptationStates, 957ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov dwFlags); 958ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (bp.cmyk2cmyk == NULL) goto Cleanup; 959ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 960ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Now the tone curve 961ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov bp.KTone = _cmsBuildKToneCurve(ContextID, 4096, nProfiles, 962ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ICCIntents, 963ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hProfiles, 964ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov BPC, 965ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov AdaptationStates, 966ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov dwFlags); 967ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (bp.KTone == NULL) goto Cleanup; 968ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 969ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // To measure the output, Last profile to Lab 970ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov hLab = cmsCreateLab4ProfileTHR(ContextID, NULL); 971ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov bp.hProofOutput = cmsCreateTransformTHR(ContextID, hProfiles[nProfiles-1], 972ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CHANNELS_SH(4)|BYTES_SH(2), hLab, TYPE_Lab_DBL, 973ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov INTENT_RELATIVE_COLORIMETRIC, 974ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE); 975ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if ( bp.hProofOutput == NULL) goto Cleanup; 976ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 977ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Same as anterior, but lab in the 0..1 range 978ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov bp.cmyk2Lab = cmsCreateTransformTHR(ContextID, hProfiles[nProfiles-1], 979ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FLOAT_SH(1)|CHANNELS_SH(4)|BYTES_SH(4), hLab, 980ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov FLOAT_SH(1)|CHANNELS_SH(3)|BYTES_SH(4), 981ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov INTENT_RELATIVE_COLORIMETRIC, 982ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsFLAGS_NOCACHE|cmsFLAGS_NOOPTIMIZE); 983ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (bp.cmyk2Lab == NULL) goto Cleanup; 984ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsCloseProfile(hLab); 985ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 986ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Error estimation (for debug only) 987ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov bp.MaxError = 0; 988ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 989ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // How many gridpoints are we going to use? 990ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov nGridPoints = _cmsReasonableGridpointsByColorspace(cmsSigCmykData, dwFlags); 991ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 992ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 993ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov CLUT = cmsStageAllocCLut16bit(ContextID, nGridPoints, 4, 4, NULL); 994ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (CLUT == NULL) goto Cleanup; 995ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 996ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (!cmsPipelineInsertStage(Result, cmsAT_BEGIN, CLUT)) 997ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov goto Cleanup; 998ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 999ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsStageSampleCLut16bit(CLUT, BlackPreservingSampler, (void*) &bp, 0); 1000ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1001ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovCleanup: 1002ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1003ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (bp.cmyk2cmyk) cmsPipelineFree(bp.cmyk2cmyk); 1004ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (bp.cmyk2Lab) cmsDeleteTransform(bp.cmyk2Lab); 1005ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (bp.hProofOutput) cmsDeleteTransform(bp.hProofOutput); 1006ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1007ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (bp.KTone) cmsFreeToneCurve(bp.KTone); 1008ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (bp.LabK2cmyk) cmsPipelineFree(bp.LabK2cmyk); 1009ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1010ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return Result; 1011ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov} 1012ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1013ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Link routines ------------------------------------------------------------------------------------------------------ 1014ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1015ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Chain several profiles into a single LUT. It just checks the parameters and then calls the handler 1016ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// for the first intent in chain. The handler may be user-defined. Is up to the handler to deal with the 1017ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// rest of intents in chain. A maximum of 255 profiles at time are supported, which is pretty reasonable. 1018ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsPipeline* _cmsLinkProfiles(cmsContext ContextID, 1019ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number nProfiles, 1020ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number TheIntents[], 1021ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsHPROFILE hProfiles[], 1022ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsBool BPC[], 1023ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsFloat64Number AdaptationStates[], 1024ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number dwFlags) 1025ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{ 1026ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number i; 1027ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsIntentsList* Intent; 1028ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1029ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Make sure a reasonable number of profiles is provided 1030ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (nProfiles <= 0 || nProfiles > 255) { 1031ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsSignalError(ContextID, cmsERROR_RANGE, "Couldn't link '%d' profiles", nProfiles); 1032ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return NULL; 1033ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1034ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1035ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov for (i=0; i < nProfiles; i++) { 1036ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1037ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Check if black point is really needed or allowed. Note that 1038ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // following Adobe's document: 1039ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // BPC does not apply to devicelink profiles, nor to abs colorimetric, 1040ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // and applies always on V4 perceptual and saturation. 1041ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1042ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (TheIntents[i] == INTENT_ABSOLUTE_COLORIMETRIC) 1043ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov BPC[i] = FALSE; 1044ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1045ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (TheIntents[i] == INTENT_PERCEPTUAL || TheIntents[i] == INTENT_SATURATION) { 1046ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1047ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Force BPC for V4 profiles in perceptual and saturation 1048ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (cmsGetProfileVersion(hProfiles[i]) >= 4.0) 1049ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov BPC[i] = TRUE; 1050ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1051ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1052ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1053ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Search for a handler. The first intent in the chain defines the handler. That would 1054ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // prevent using multiple custom intents in a multiintent chain, but the behaviour of 1055ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // this case would present some issues if the custom intent tries to do things like 1056ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // preserve primaries. This solution is not perfect, but works well on most cases. 1057ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1058ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Intent = SearchIntent(ContextID, TheIntents[0]); 1059ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (Intent == NULL) { 1060ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported intent '%d'", TheIntents[0]); 1061ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return NULL; 1062ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1063ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1064ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Call the handler 1065ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return Intent ->Link(ContextID, nProfiles, TheIntents, hProfiles, BPC, AdaptationStates, dwFlags); 1066ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov} 1067ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1068ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// ------------------------------------------------------------------------------------------------- 1069ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1070ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// Get information about available intents. nMax is the maximum space for the supplied "Codes" 1071ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// and "Descriptions" the function returns the total number of intents, which may be greater 1072ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// than nMax, although the matrices are not populated beyond this level. 1073ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsUInt32Number CMSEXPORT cmsGetSupportedIntentsTHR(cmsContext ContextID, cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions) 1074ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{ 1075ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov _cmsIntentsPluginChunkType* ctx = ( _cmsIntentsPluginChunkType*) _cmsContextGetClientChunk(ContextID, IntentPlugin); 1076ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsIntentsList* pt; 1077ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsUInt32Number nIntents; 1078ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1079ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1080ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov for (nIntents=0, pt = ctx->Intents; pt != NULL; pt = pt -> Next) 1081ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1082ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (nIntents < nMax) { 1083ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (Codes != NULL) 1084ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Codes[nIntents] = pt ->Intent; 1085ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1086ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (Descriptions != NULL) 1087ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Descriptions[nIntents] = pt ->Description; 1088ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1089ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1090ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov nIntents++; 1091ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1092ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1093ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov for (nIntents=0, pt = DefaultIntents; pt != NULL; pt = pt -> Next) 1094ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov { 1095ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (nIntents < nMax) { 1096ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (Codes != NULL) 1097ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Codes[nIntents] = pt ->Intent; 1098ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1099ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (Descriptions != NULL) 1100ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov Descriptions[nIntents] = pt ->Description; 1101ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1102ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1103ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov nIntents++; 1104ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1105ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return nIntents; 1106ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov} 1107ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1108ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsUInt32Number CMSEXPORT cmsGetSupportedIntents(cmsUInt32Number nMax, cmsUInt32Number* Codes, char** Descriptions) 1109ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{ 1110ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return cmsGetSupportedIntentsTHR(NULL, nMax, Codes, Descriptions); 1111ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov} 1112ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1113ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov// The plug-in registration. User can add new intents or override default routines 1114ee451cb395940862dad63c85adfe8f2fd55e864cSvet GanovcmsBool _cmsRegisterRenderingIntentPlugin(cmsContext id, cmsPluginBase* Data) 1115ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov{ 1116ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov _cmsIntentsPluginChunkType* ctx = ( _cmsIntentsPluginChunkType*) _cmsContextGetClientChunk(id, IntentPlugin); 1117ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsPluginRenderingIntent* Plugin = (cmsPluginRenderingIntent*) Data; 1118ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov cmsIntentsList* fl; 1119ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1120ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov // Do we have to reset the custom intents? 1121ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (Data == NULL) { 1122ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1123ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ctx->Intents = NULL; 1124ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return TRUE; 1125ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov } 1126ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1127ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov fl = (cmsIntentsList*) _cmsPluginMalloc(id, sizeof(cmsIntentsList)); 1128ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov if (fl == NULL) return FALSE; 1129ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1130ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1131ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov fl ->Intent = Plugin ->Intent; 1132ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov strncpy(fl ->Description, Plugin ->Description, sizeof(fl ->Description)-1); 1133ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov fl ->Description[sizeof(fl ->Description)-1] = 0; 1134ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1135ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov fl ->Link = Plugin ->Link; 1136ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1137ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov fl ->Next = ctx ->Intents; 1138ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov ctx ->Intents = fl; 1139ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1140ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov return TRUE; 1141ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov} 1142ee451cb395940862dad63c85adfe8f2fd55e864cSvet Ganov 1143