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