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 //----------------------------------------------------------------------------------
31*3ac0a46fSAndroid Build Coastguard Worker
32*3ac0a46fSAndroid Build Coastguard Worker // Optimization for 8 bits, Shaper-CLUT (3 inputs only)
33*3ac0a46fSAndroid Build Coastguard Worker typedef struct {
34*3ac0a46fSAndroid Build Coastguard Worker
35*3ac0a46fSAndroid Build Coastguard Worker cmsContext ContextID;
36*3ac0a46fSAndroid Build Coastguard Worker
37*3ac0a46fSAndroid Build Coastguard Worker const cmsInterpParams* p; // Tetrahedrical interpolation parameters. This is a not-owned pointer.
38*3ac0a46fSAndroid Build Coastguard Worker
39*3ac0a46fSAndroid Build Coastguard Worker cmsUInt16Number rx[256], ry[256], rz[256];
40*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number X0[256], Y0[256], Z0[256]; // Precomputed nodes and offsets for 8-bit input data
41*3ac0a46fSAndroid Build Coastguard Worker
42*3ac0a46fSAndroid Build Coastguard Worker
43*3ac0a46fSAndroid Build Coastguard Worker } Prelin8Data;
44*3ac0a46fSAndroid Build Coastguard Worker
45*3ac0a46fSAndroid Build Coastguard Worker
46*3ac0a46fSAndroid Build Coastguard Worker // Generic optimization for 16 bits Shaper-CLUT-Shaper (any inputs)
47*3ac0a46fSAndroid Build Coastguard Worker typedef struct {
48*3ac0a46fSAndroid Build Coastguard Worker
49*3ac0a46fSAndroid Build Coastguard Worker cmsContext ContextID;
50*3ac0a46fSAndroid Build Coastguard Worker
51*3ac0a46fSAndroid Build Coastguard Worker // Number of channels
52*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number nInputs;
53*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number nOutputs;
54*3ac0a46fSAndroid Build Coastguard Worker
55*3ac0a46fSAndroid Build Coastguard Worker _cmsInterpFn16 EvalCurveIn16[MAX_INPUT_DIMENSIONS]; // The maximum number of input channels is known in advance
56*3ac0a46fSAndroid Build Coastguard Worker cmsInterpParams* ParamsCurveIn16[MAX_INPUT_DIMENSIONS];
57*3ac0a46fSAndroid Build Coastguard Worker
58*3ac0a46fSAndroid Build Coastguard Worker _cmsInterpFn16 EvalCLUT; // The evaluator for 3D grid
59*3ac0a46fSAndroid Build Coastguard Worker const cmsInterpParams* CLUTparams; // (not-owned pointer)
60*3ac0a46fSAndroid Build Coastguard Worker
61*3ac0a46fSAndroid Build Coastguard Worker
62*3ac0a46fSAndroid Build Coastguard Worker _cmsInterpFn16* EvalCurveOut16; // Points to an array of curve evaluators in 16 bits (not-owned pointer)
63*3ac0a46fSAndroid Build Coastguard Worker cmsInterpParams** ParamsCurveOut16; // Points to an array of references to interpolation params (not-owned pointer)
64*3ac0a46fSAndroid Build Coastguard Worker
65*3ac0a46fSAndroid Build Coastguard Worker
66*3ac0a46fSAndroid Build Coastguard Worker } Prelin16Data;
67*3ac0a46fSAndroid Build Coastguard Worker
68*3ac0a46fSAndroid Build Coastguard Worker
69*3ac0a46fSAndroid Build Coastguard Worker // Optimization for matrix-shaper in 8 bits. Numbers are operated in n.14 signed, tables are stored in 1.14 fixed
70*3ac0a46fSAndroid Build Coastguard Worker
71*3ac0a46fSAndroid Build Coastguard Worker typedef cmsInt32Number cmsS1Fixed14Number; // Note that this may hold more than 16 bits!
72*3ac0a46fSAndroid Build Coastguard Worker
73*3ac0a46fSAndroid Build Coastguard Worker #define DOUBLE_TO_1FIXED14(x) ((cmsS1Fixed14Number) floor((x) * 16384.0 + 0.5))
74*3ac0a46fSAndroid Build Coastguard Worker
75*3ac0a46fSAndroid Build Coastguard Worker typedef struct {
76*3ac0a46fSAndroid Build Coastguard Worker
77*3ac0a46fSAndroid Build Coastguard Worker cmsContext ContextID;
78*3ac0a46fSAndroid Build Coastguard Worker
79*3ac0a46fSAndroid Build Coastguard Worker cmsS1Fixed14Number Shaper1R[256]; // from 0..255 to 1.14 (0.0...1.0)
80*3ac0a46fSAndroid Build Coastguard Worker cmsS1Fixed14Number Shaper1G[256];
81*3ac0a46fSAndroid Build Coastguard Worker cmsS1Fixed14Number Shaper1B[256];
82*3ac0a46fSAndroid Build Coastguard Worker
83*3ac0a46fSAndroid Build Coastguard Worker cmsS1Fixed14Number Mat[3][3]; // n.14 to n.14 (needs a saturation after that)
84*3ac0a46fSAndroid Build Coastguard Worker cmsS1Fixed14Number Off[3];
85*3ac0a46fSAndroid Build Coastguard Worker
86*3ac0a46fSAndroid Build Coastguard Worker cmsUInt16Number Shaper2R[16385]; // 1.14 to 0..255
87*3ac0a46fSAndroid Build Coastguard Worker cmsUInt16Number Shaper2G[16385];
88*3ac0a46fSAndroid Build Coastguard Worker cmsUInt16Number Shaper2B[16385];
89*3ac0a46fSAndroid Build Coastguard Worker
90*3ac0a46fSAndroid Build Coastguard Worker } MatShaper8Data;
91*3ac0a46fSAndroid Build Coastguard Worker
92*3ac0a46fSAndroid Build Coastguard Worker // Curves, optimization is shared between 8 and 16 bits
93*3ac0a46fSAndroid Build Coastguard Worker typedef struct {
94*3ac0a46fSAndroid Build Coastguard Worker
95*3ac0a46fSAndroid Build Coastguard Worker cmsContext ContextID;
96*3ac0a46fSAndroid Build Coastguard Worker
97*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number nCurves; // Number of curves
98*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number nElements; // Elements in curves
99*3ac0a46fSAndroid Build Coastguard Worker cmsUInt16Number** Curves; // Points to a dynamically allocated array
100*3ac0a46fSAndroid Build Coastguard Worker
101*3ac0a46fSAndroid Build Coastguard Worker } Curves16Data;
102*3ac0a46fSAndroid Build Coastguard Worker
103*3ac0a46fSAndroid Build Coastguard Worker // A simple adapter to prevent _cmsPipelineEval16Fn vs. _cmsInterpFn16
104*3ac0a46fSAndroid Build Coastguard Worker // confusion, which trips up UBSAN.
105*3ac0a46fSAndroid Build Coastguard Worker static
Lerp16Adapter(CMSREGISTER const cmsUInt16Number in[],CMSREGISTER cmsUInt16Number out[],const void * data)106*3ac0a46fSAndroid Build Coastguard Worker void Lerp16Adapter(CMSREGISTER const cmsUInt16Number in[],
107*3ac0a46fSAndroid Build Coastguard Worker CMSREGISTER cmsUInt16Number out[],
108*3ac0a46fSAndroid Build Coastguard Worker const void* data) {
109*3ac0a46fSAndroid Build Coastguard Worker cmsInterpParams* params = (cmsInterpParams*)data;
110*3ac0a46fSAndroid Build Coastguard Worker params->Interpolation.Lerp16(in, out, params);
111*3ac0a46fSAndroid Build Coastguard Worker }
112*3ac0a46fSAndroid Build Coastguard Worker
113*3ac0a46fSAndroid Build Coastguard Worker // Simple optimizations ----------------------------------------------------------------------------------------------------------
114*3ac0a46fSAndroid Build Coastguard Worker
115*3ac0a46fSAndroid Build Coastguard Worker
116*3ac0a46fSAndroid Build Coastguard Worker // Clamp a fixed point integer to signed 28 bits to avoid overflow in
117*3ac0a46fSAndroid Build Coastguard Worker // calculations. Clamp is intended for use with colorants, requiring one bit
118*3ac0a46fSAndroid Build Coastguard Worker // for a colorant and another two bits to avoid overflow when combining the
119*3ac0a46fSAndroid Build Coastguard Worker // colors.
_FixedClamp(cmsS1Fixed14Number n)120*3ac0a46fSAndroid Build Coastguard Worker cmsINLINE cmsS1Fixed14Number _FixedClamp(cmsS1Fixed14Number n) {
121*3ac0a46fSAndroid Build Coastguard Worker const cmsS1Fixed14Number max_positive = 268435455; // 0x0FFFFFFF;
122*3ac0a46fSAndroid Build Coastguard Worker const cmsS1Fixed14Number max_negative = -268435456; // 0xF0000000;
123*3ac0a46fSAndroid Build Coastguard Worker // Normally expect the provided number to be in the range [0..1] (but in
124*3ac0a46fSAndroid Build Coastguard Worker // fixed 1.14 format), so can perform a quick check for this typical case
125*3ac0a46fSAndroid Build Coastguard Worker // to reduce number of compares.
126*3ac0a46fSAndroid Build Coastguard Worker const cmsS1Fixed14Number typical_range_mask = 0xFFFF8000;
127*3ac0a46fSAndroid Build Coastguard Worker
128*3ac0a46fSAndroid Build Coastguard Worker if (!(n & typical_range_mask))
129*3ac0a46fSAndroid Build Coastguard Worker return n;
130*3ac0a46fSAndroid Build Coastguard Worker if (n < max_negative)
131*3ac0a46fSAndroid Build Coastguard Worker return max_negative;
132*3ac0a46fSAndroid Build Coastguard Worker if (n > max_positive)
133*3ac0a46fSAndroid Build Coastguard Worker return max_positive;
134*3ac0a46fSAndroid Build Coastguard Worker return n;
135*3ac0a46fSAndroid Build Coastguard Worker }
136*3ac0a46fSAndroid Build Coastguard Worker
137*3ac0a46fSAndroid Build Coastguard Worker // Perform one row of matrix multiply with translation for MatShaperEval16().
_MatShaperEvaluateRow(cmsS1Fixed14Number * mat,cmsS1Fixed14Number off,cmsS1Fixed14Number r,cmsS1Fixed14Number g,cmsS1Fixed14Number b)138*3ac0a46fSAndroid Build Coastguard Worker cmsINLINE cmsInt64Number _MatShaperEvaluateRow(cmsS1Fixed14Number* mat,
139*3ac0a46fSAndroid Build Coastguard Worker cmsS1Fixed14Number off,
140*3ac0a46fSAndroid Build Coastguard Worker cmsS1Fixed14Number r,
141*3ac0a46fSAndroid Build Coastguard Worker cmsS1Fixed14Number g,
142*3ac0a46fSAndroid Build Coastguard Worker cmsS1Fixed14Number b) {
143*3ac0a46fSAndroid Build Coastguard Worker return ((cmsInt64Number)mat[0] * r +
144*3ac0a46fSAndroid Build Coastguard Worker (cmsInt64Number)mat[1] * g +
145*3ac0a46fSAndroid Build Coastguard Worker (cmsInt64Number)mat[2] * b +
146*3ac0a46fSAndroid Build Coastguard Worker off + 0x2000) >> 14;
147*3ac0a46fSAndroid Build Coastguard Worker }
148*3ac0a46fSAndroid Build Coastguard Worker
149*3ac0a46fSAndroid Build Coastguard Worker // Remove an element in linked chain
150*3ac0a46fSAndroid Build Coastguard Worker static
_RemoveElement(cmsStage ** head)151*3ac0a46fSAndroid Build Coastguard Worker void _RemoveElement(cmsStage** head)
152*3ac0a46fSAndroid Build Coastguard Worker {
153*3ac0a46fSAndroid Build Coastguard Worker cmsStage* mpe = *head;
154*3ac0a46fSAndroid Build Coastguard Worker cmsStage* next = mpe ->Next;
155*3ac0a46fSAndroid Build Coastguard Worker *head = next;
156*3ac0a46fSAndroid Build Coastguard Worker cmsStageFree(mpe);
157*3ac0a46fSAndroid Build Coastguard Worker }
158*3ac0a46fSAndroid Build Coastguard Worker
159*3ac0a46fSAndroid Build Coastguard Worker // Remove all identities in chain. Note that pt actually is a double pointer to the element that holds the pointer.
160*3ac0a46fSAndroid Build Coastguard Worker static
_Remove1Op(cmsPipeline * Lut,cmsStageSignature UnaryOp)161*3ac0a46fSAndroid Build Coastguard Worker cmsBool _Remove1Op(cmsPipeline* Lut, cmsStageSignature UnaryOp)
162*3ac0a46fSAndroid Build Coastguard Worker {
163*3ac0a46fSAndroid Build Coastguard Worker cmsStage** pt = &Lut ->Elements;
164*3ac0a46fSAndroid Build Coastguard Worker cmsBool AnyOpt = FALSE;
165*3ac0a46fSAndroid Build Coastguard Worker
166*3ac0a46fSAndroid Build Coastguard Worker while (*pt != NULL) {
167*3ac0a46fSAndroid Build Coastguard Worker
168*3ac0a46fSAndroid Build Coastguard Worker if ((*pt) ->Implements == UnaryOp) {
169*3ac0a46fSAndroid Build Coastguard Worker _RemoveElement(pt);
170*3ac0a46fSAndroid Build Coastguard Worker AnyOpt = TRUE;
171*3ac0a46fSAndroid Build Coastguard Worker }
172*3ac0a46fSAndroid Build Coastguard Worker else
173*3ac0a46fSAndroid Build Coastguard Worker pt = &((*pt) -> Next);
174*3ac0a46fSAndroid Build Coastguard Worker }
175*3ac0a46fSAndroid Build Coastguard Worker
176*3ac0a46fSAndroid Build Coastguard Worker return AnyOpt;
177*3ac0a46fSAndroid Build Coastguard Worker }
178*3ac0a46fSAndroid Build Coastguard Worker
179*3ac0a46fSAndroid Build Coastguard Worker // Same, but only if two adjacent elements are found
180*3ac0a46fSAndroid Build Coastguard Worker static
_Remove2Op(cmsPipeline * Lut,cmsStageSignature Op1,cmsStageSignature Op2)181*3ac0a46fSAndroid Build Coastguard Worker cmsBool _Remove2Op(cmsPipeline* Lut, cmsStageSignature Op1, cmsStageSignature Op2)
182*3ac0a46fSAndroid Build Coastguard Worker {
183*3ac0a46fSAndroid Build Coastguard Worker cmsStage** pt1;
184*3ac0a46fSAndroid Build Coastguard Worker cmsStage** pt2;
185*3ac0a46fSAndroid Build Coastguard Worker cmsBool AnyOpt = FALSE;
186*3ac0a46fSAndroid Build Coastguard Worker
187*3ac0a46fSAndroid Build Coastguard Worker pt1 = &Lut ->Elements;
188*3ac0a46fSAndroid Build Coastguard Worker if (*pt1 == NULL) return AnyOpt;
189*3ac0a46fSAndroid Build Coastguard Worker
190*3ac0a46fSAndroid Build Coastguard Worker while (*pt1 != NULL) {
191*3ac0a46fSAndroid Build Coastguard Worker
192*3ac0a46fSAndroid Build Coastguard Worker pt2 = &((*pt1) -> Next);
193*3ac0a46fSAndroid Build Coastguard Worker if (*pt2 == NULL) return AnyOpt;
194*3ac0a46fSAndroid Build Coastguard Worker
195*3ac0a46fSAndroid Build Coastguard Worker if ((*pt1) ->Implements == Op1 && (*pt2) ->Implements == Op2) {
196*3ac0a46fSAndroid Build Coastguard Worker _RemoveElement(pt2);
197*3ac0a46fSAndroid Build Coastguard Worker _RemoveElement(pt1);
198*3ac0a46fSAndroid Build Coastguard Worker AnyOpt = TRUE;
199*3ac0a46fSAndroid Build Coastguard Worker }
200*3ac0a46fSAndroid Build Coastguard Worker else
201*3ac0a46fSAndroid Build Coastguard Worker pt1 = &((*pt1) -> Next);
202*3ac0a46fSAndroid Build Coastguard Worker }
203*3ac0a46fSAndroid Build Coastguard Worker
204*3ac0a46fSAndroid Build Coastguard Worker return AnyOpt;
205*3ac0a46fSAndroid Build Coastguard Worker }
206*3ac0a46fSAndroid Build Coastguard Worker
207*3ac0a46fSAndroid Build Coastguard Worker
208*3ac0a46fSAndroid Build Coastguard Worker static
CloseEnoughFloat(cmsFloat64Number a,cmsFloat64Number b)209*3ac0a46fSAndroid Build Coastguard Worker cmsBool CloseEnoughFloat(cmsFloat64Number a, cmsFloat64Number b)
210*3ac0a46fSAndroid Build Coastguard Worker {
211*3ac0a46fSAndroid Build Coastguard Worker return fabs(b - a) < 0.00001f;
212*3ac0a46fSAndroid Build Coastguard Worker }
213*3ac0a46fSAndroid Build Coastguard Worker
214*3ac0a46fSAndroid Build Coastguard Worker static
isFloatMatrixIdentity(const cmsMAT3 * a)215*3ac0a46fSAndroid Build Coastguard Worker cmsBool isFloatMatrixIdentity(const cmsMAT3* a)
216*3ac0a46fSAndroid Build Coastguard Worker {
217*3ac0a46fSAndroid Build Coastguard Worker cmsMAT3 Identity;
218*3ac0a46fSAndroid Build Coastguard Worker int i, j;
219*3ac0a46fSAndroid Build Coastguard Worker
220*3ac0a46fSAndroid Build Coastguard Worker _cmsMAT3identity(&Identity);
221*3ac0a46fSAndroid Build Coastguard Worker
222*3ac0a46fSAndroid Build Coastguard Worker for (i = 0; i < 3; i++)
223*3ac0a46fSAndroid Build Coastguard Worker for (j = 0; j < 3; j++)
224*3ac0a46fSAndroid Build Coastguard Worker if (!CloseEnoughFloat(a->v[i].n[j], Identity.v[i].n[j])) return FALSE;
225*3ac0a46fSAndroid Build Coastguard Worker
226*3ac0a46fSAndroid Build Coastguard Worker return TRUE;
227*3ac0a46fSAndroid Build Coastguard Worker }
228*3ac0a46fSAndroid Build Coastguard Worker // if two adjacent matrices are found, multiply them.
229*3ac0a46fSAndroid Build Coastguard Worker static
_MultiplyMatrix(cmsPipeline * Lut)230*3ac0a46fSAndroid Build Coastguard Worker cmsBool _MultiplyMatrix(cmsPipeline* Lut)
231*3ac0a46fSAndroid Build Coastguard Worker {
232*3ac0a46fSAndroid Build Coastguard Worker cmsStage** pt1;
233*3ac0a46fSAndroid Build Coastguard Worker cmsStage** pt2;
234*3ac0a46fSAndroid Build Coastguard Worker cmsStage* chain;
235*3ac0a46fSAndroid Build Coastguard Worker cmsBool AnyOpt = FALSE;
236*3ac0a46fSAndroid Build Coastguard Worker
237*3ac0a46fSAndroid Build Coastguard Worker pt1 = &Lut->Elements;
238*3ac0a46fSAndroid Build Coastguard Worker if (*pt1 == NULL) return AnyOpt;
239*3ac0a46fSAndroid Build Coastguard Worker
240*3ac0a46fSAndroid Build Coastguard Worker while (*pt1 != NULL) {
241*3ac0a46fSAndroid Build Coastguard Worker
242*3ac0a46fSAndroid Build Coastguard Worker pt2 = &((*pt1)->Next);
243*3ac0a46fSAndroid Build Coastguard Worker if (*pt2 == NULL) return AnyOpt;
244*3ac0a46fSAndroid Build Coastguard Worker
245*3ac0a46fSAndroid Build Coastguard Worker if ((*pt1)->Implements == cmsSigMatrixElemType && (*pt2)->Implements == cmsSigMatrixElemType) {
246*3ac0a46fSAndroid Build Coastguard Worker
247*3ac0a46fSAndroid Build Coastguard Worker // Get both matrices
248*3ac0a46fSAndroid Build Coastguard Worker _cmsStageMatrixData* m1 = (_cmsStageMatrixData*) cmsStageData(*pt1);
249*3ac0a46fSAndroid Build Coastguard Worker _cmsStageMatrixData* m2 = (_cmsStageMatrixData*) cmsStageData(*pt2);
250*3ac0a46fSAndroid Build Coastguard Worker cmsMAT3 res;
251*3ac0a46fSAndroid Build Coastguard Worker
252*3ac0a46fSAndroid Build Coastguard Worker // Input offset and output offset should be zero to use this optimization
253*3ac0a46fSAndroid Build Coastguard Worker if (m1->Offset != NULL || m2 ->Offset != NULL ||
254*3ac0a46fSAndroid Build Coastguard Worker cmsStageInputChannels(*pt1) != 3 || cmsStageOutputChannels(*pt1) != 3 ||
255*3ac0a46fSAndroid Build Coastguard Worker cmsStageInputChannels(*pt2) != 3 || cmsStageOutputChannels(*pt2) != 3)
256*3ac0a46fSAndroid Build Coastguard Worker return FALSE;
257*3ac0a46fSAndroid Build Coastguard Worker
258*3ac0a46fSAndroid Build Coastguard Worker // Multiply both matrices to get the result
259*3ac0a46fSAndroid Build Coastguard Worker _cmsMAT3per(&res, (cmsMAT3*)m2->Double, (cmsMAT3*)m1->Double);
260*3ac0a46fSAndroid Build Coastguard Worker
261*3ac0a46fSAndroid Build Coastguard Worker // Get the next in chain after the matrices
262*3ac0a46fSAndroid Build Coastguard Worker chain = (*pt2)->Next;
263*3ac0a46fSAndroid Build Coastguard Worker
264*3ac0a46fSAndroid Build Coastguard Worker // Remove both matrices
265*3ac0a46fSAndroid Build Coastguard Worker _RemoveElement(pt2);
266*3ac0a46fSAndroid Build Coastguard Worker _RemoveElement(pt1);
267*3ac0a46fSAndroid Build Coastguard Worker
268*3ac0a46fSAndroid Build Coastguard Worker // Now what if the result is a plain identity?
269*3ac0a46fSAndroid Build Coastguard Worker if (!isFloatMatrixIdentity(&res)) {
270*3ac0a46fSAndroid Build Coastguard Worker
271*3ac0a46fSAndroid Build Coastguard Worker // We can not get rid of full matrix
272*3ac0a46fSAndroid Build Coastguard Worker cmsStage* Multmat = cmsStageAllocMatrix(Lut->ContextID, 3, 3, (const cmsFloat64Number*) &res, NULL);
273*3ac0a46fSAndroid Build Coastguard Worker if (Multmat == NULL) return FALSE; // Should never happen
274*3ac0a46fSAndroid Build Coastguard Worker
275*3ac0a46fSAndroid Build Coastguard Worker // Recover the chain
276*3ac0a46fSAndroid Build Coastguard Worker Multmat->Next = chain;
277*3ac0a46fSAndroid Build Coastguard Worker *pt1 = Multmat;
278*3ac0a46fSAndroid Build Coastguard Worker }
279*3ac0a46fSAndroid Build Coastguard Worker
280*3ac0a46fSAndroid Build Coastguard Worker AnyOpt = TRUE;
281*3ac0a46fSAndroid Build Coastguard Worker }
282*3ac0a46fSAndroid Build Coastguard Worker else
283*3ac0a46fSAndroid Build Coastguard Worker pt1 = &((*pt1)->Next);
284*3ac0a46fSAndroid Build Coastguard Worker }
285*3ac0a46fSAndroid Build Coastguard Worker
286*3ac0a46fSAndroid Build Coastguard Worker return AnyOpt;
287*3ac0a46fSAndroid Build Coastguard Worker }
288*3ac0a46fSAndroid Build Coastguard Worker
289*3ac0a46fSAndroid Build Coastguard Worker
290*3ac0a46fSAndroid Build Coastguard Worker // Preoptimize just gets rif of no-ops coming paired. Conversion from v2 to v4 followed
291*3ac0a46fSAndroid Build Coastguard Worker // by a v4 to v2 and vice-versa. The elements are then discarded.
292*3ac0a46fSAndroid Build Coastguard Worker static
PreOptimize(cmsPipeline * Lut)293*3ac0a46fSAndroid Build Coastguard Worker cmsBool PreOptimize(cmsPipeline* Lut)
294*3ac0a46fSAndroid Build Coastguard Worker {
295*3ac0a46fSAndroid Build Coastguard Worker cmsBool AnyOpt = FALSE, Opt;
296*3ac0a46fSAndroid Build Coastguard Worker
297*3ac0a46fSAndroid Build Coastguard Worker do {
298*3ac0a46fSAndroid Build Coastguard Worker
299*3ac0a46fSAndroid Build Coastguard Worker Opt = FALSE;
300*3ac0a46fSAndroid Build Coastguard Worker
301*3ac0a46fSAndroid Build Coastguard Worker // Remove all identities
302*3ac0a46fSAndroid Build Coastguard Worker Opt |= _Remove1Op(Lut, cmsSigIdentityElemType);
303*3ac0a46fSAndroid Build Coastguard Worker
304*3ac0a46fSAndroid Build Coastguard Worker // Remove XYZ2Lab followed by Lab2XYZ
305*3ac0a46fSAndroid Build Coastguard Worker Opt |= _Remove2Op(Lut, cmsSigXYZ2LabElemType, cmsSigLab2XYZElemType);
306*3ac0a46fSAndroid Build Coastguard Worker
307*3ac0a46fSAndroid Build Coastguard Worker // Remove Lab2XYZ followed by XYZ2Lab
308*3ac0a46fSAndroid Build Coastguard Worker Opt |= _Remove2Op(Lut, cmsSigLab2XYZElemType, cmsSigXYZ2LabElemType);
309*3ac0a46fSAndroid Build Coastguard Worker
310*3ac0a46fSAndroid Build Coastguard Worker // Remove V4 to V2 followed by V2 to V4
311*3ac0a46fSAndroid Build Coastguard Worker Opt |= _Remove2Op(Lut, cmsSigLabV4toV2, cmsSigLabV2toV4);
312*3ac0a46fSAndroid Build Coastguard Worker
313*3ac0a46fSAndroid Build Coastguard Worker // Remove V2 to V4 followed by V4 to V2
314*3ac0a46fSAndroid Build Coastguard Worker Opt |= _Remove2Op(Lut, cmsSigLabV2toV4, cmsSigLabV4toV2);
315*3ac0a46fSAndroid Build Coastguard Worker
316*3ac0a46fSAndroid Build Coastguard Worker // Remove float pcs Lab conversions
317*3ac0a46fSAndroid Build Coastguard Worker Opt |= _Remove2Op(Lut, cmsSigLab2FloatPCS, cmsSigFloatPCS2Lab);
318*3ac0a46fSAndroid Build Coastguard Worker
319*3ac0a46fSAndroid Build Coastguard Worker // Remove float pcs Lab conversions
320*3ac0a46fSAndroid Build Coastguard Worker Opt |= _Remove2Op(Lut, cmsSigXYZ2FloatPCS, cmsSigFloatPCS2XYZ);
321*3ac0a46fSAndroid Build Coastguard Worker
322*3ac0a46fSAndroid Build Coastguard Worker // Simplify matrix.
323*3ac0a46fSAndroid Build Coastguard Worker Opt |= _MultiplyMatrix(Lut);
324*3ac0a46fSAndroid Build Coastguard Worker
325*3ac0a46fSAndroid Build Coastguard Worker if (Opt) AnyOpt = TRUE;
326*3ac0a46fSAndroid Build Coastguard Worker
327*3ac0a46fSAndroid Build Coastguard Worker } while (Opt);
328*3ac0a46fSAndroid Build Coastguard Worker
329*3ac0a46fSAndroid Build Coastguard Worker return AnyOpt;
330*3ac0a46fSAndroid Build Coastguard Worker }
331*3ac0a46fSAndroid Build Coastguard Worker
332*3ac0a46fSAndroid Build Coastguard Worker static
Eval16nop1D(CMSREGISTER const cmsUInt16Number Input[],CMSREGISTER cmsUInt16Number Output[],CMSREGISTER const struct _cms_interp_struc * p)333*3ac0a46fSAndroid Build Coastguard Worker void Eval16nop1D(CMSREGISTER const cmsUInt16Number Input[],
334*3ac0a46fSAndroid Build Coastguard Worker CMSREGISTER cmsUInt16Number Output[],
335*3ac0a46fSAndroid Build Coastguard Worker CMSREGISTER const struct _cms_interp_struc* p)
336*3ac0a46fSAndroid Build Coastguard Worker {
337*3ac0a46fSAndroid Build Coastguard Worker Output[0] = Input[0];
338*3ac0a46fSAndroid Build Coastguard Worker
339*3ac0a46fSAndroid Build Coastguard Worker cmsUNUSED_PARAMETER(p);
340*3ac0a46fSAndroid Build Coastguard Worker }
341*3ac0a46fSAndroid Build Coastguard Worker
342*3ac0a46fSAndroid Build Coastguard Worker static
PrelinEval16(CMSREGISTER const cmsUInt16Number Input[],CMSREGISTER cmsUInt16Number Output[],CMSREGISTER const void * D)343*3ac0a46fSAndroid Build Coastguard Worker void PrelinEval16(CMSREGISTER const cmsUInt16Number Input[],
344*3ac0a46fSAndroid Build Coastguard Worker CMSREGISTER cmsUInt16Number Output[],
345*3ac0a46fSAndroid Build Coastguard Worker CMSREGISTER const void* D)
346*3ac0a46fSAndroid Build Coastguard Worker {
347*3ac0a46fSAndroid Build Coastguard Worker Prelin16Data* p16 = (Prelin16Data*) D;
348*3ac0a46fSAndroid Build Coastguard Worker cmsUInt16Number StageABC[MAX_INPUT_DIMENSIONS];
349*3ac0a46fSAndroid Build Coastguard Worker cmsUInt16Number StageDEF[cmsMAXCHANNELS];
350*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number i;
351*3ac0a46fSAndroid Build Coastguard Worker
352*3ac0a46fSAndroid Build Coastguard Worker for (i=0; i < p16 ->nInputs; i++) {
353*3ac0a46fSAndroid Build Coastguard Worker
354*3ac0a46fSAndroid Build Coastguard Worker p16 ->EvalCurveIn16[i](&Input[i], &StageABC[i], p16 ->ParamsCurveIn16[i]);
355*3ac0a46fSAndroid Build Coastguard Worker }
356*3ac0a46fSAndroid Build Coastguard Worker
357*3ac0a46fSAndroid Build Coastguard Worker p16 ->EvalCLUT(StageABC, StageDEF, p16 ->CLUTparams);
358*3ac0a46fSAndroid Build Coastguard Worker
359*3ac0a46fSAndroid Build Coastguard Worker for (i=0; i < p16 ->nOutputs; i++) {
360*3ac0a46fSAndroid Build Coastguard Worker
361*3ac0a46fSAndroid Build Coastguard Worker p16 ->EvalCurveOut16[i](&StageDEF[i], &Output[i], p16 ->ParamsCurveOut16[i]);
362*3ac0a46fSAndroid Build Coastguard Worker }
363*3ac0a46fSAndroid Build Coastguard Worker }
364*3ac0a46fSAndroid Build Coastguard Worker
365*3ac0a46fSAndroid Build Coastguard Worker
366*3ac0a46fSAndroid Build Coastguard Worker static
PrelinOpt16free(cmsContext ContextID,void * ptr)367*3ac0a46fSAndroid Build Coastguard Worker void PrelinOpt16free(cmsContext ContextID, void* ptr)
368*3ac0a46fSAndroid Build Coastguard Worker {
369*3ac0a46fSAndroid Build Coastguard Worker Prelin16Data* p16 = (Prelin16Data*) ptr;
370*3ac0a46fSAndroid Build Coastguard Worker
371*3ac0a46fSAndroid Build Coastguard Worker _cmsFree(ContextID, p16 ->EvalCurveOut16);
372*3ac0a46fSAndroid Build Coastguard Worker _cmsFree(ContextID, p16 ->ParamsCurveOut16);
373*3ac0a46fSAndroid Build Coastguard Worker
374*3ac0a46fSAndroid Build Coastguard Worker _cmsFree(ContextID, p16);
375*3ac0a46fSAndroid Build Coastguard Worker }
376*3ac0a46fSAndroid Build Coastguard Worker
377*3ac0a46fSAndroid Build Coastguard Worker static
Prelin16dup(cmsContext ContextID,const void * ptr)378*3ac0a46fSAndroid Build Coastguard Worker void* Prelin16dup(cmsContext ContextID, const void* ptr)
379*3ac0a46fSAndroid Build Coastguard Worker {
380*3ac0a46fSAndroid Build Coastguard Worker Prelin16Data* p16 = (Prelin16Data*) ptr;
381*3ac0a46fSAndroid Build Coastguard Worker Prelin16Data* Duped = (Prelin16Data*) _cmsDupMem(ContextID, p16, sizeof(Prelin16Data));
382*3ac0a46fSAndroid Build Coastguard Worker
383*3ac0a46fSAndroid Build Coastguard Worker if (Duped == NULL) return NULL;
384*3ac0a46fSAndroid Build Coastguard Worker
385*3ac0a46fSAndroid Build Coastguard Worker Duped->EvalCurveOut16 = (_cmsInterpFn16*) _cmsDupMem(ContextID, p16->EvalCurveOut16, p16->nOutputs * sizeof(_cmsInterpFn16));
386*3ac0a46fSAndroid Build Coastguard Worker Duped->ParamsCurveOut16 = (cmsInterpParams**)_cmsDupMem(ContextID, p16->ParamsCurveOut16, p16->nOutputs * sizeof(cmsInterpParams*));
387*3ac0a46fSAndroid Build Coastguard Worker
388*3ac0a46fSAndroid Build Coastguard Worker return Duped;
389*3ac0a46fSAndroid Build Coastguard Worker }
390*3ac0a46fSAndroid Build Coastguard Worker
391*3ac0a46fSAndroid Build Coastguard Worker
392*3ac0a46fSAndroid Build Coastguard Worker static
PrelinOpt16alloc(cmsContext ContextID,const cmsInterpParams * ColorMap,cmsUInt32Number nInputs,cmsToneCurve ** In,cmsUInt32Number nOutputs,cmsToneCurve ** Out)393*3ac0a46fSAndroid Build Coastguard Worker Prelin16Data* PrelinOpt16alloc(cmsContext ContextID,
394*3ac0a46fSAndroid Build Coastguard Worker const cmsInterpParams* ColorMap,
395*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number nInputs, cmsToneCurve** In,
396*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number nOutputs, cmsToneCurve** Out )
397*3ac0a46fSAndroid Build Coastguard Worker {
398*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number i;
399*3ac0a46fSAndroid Build Coastguard Worker Prelin16Data* p16 = (Prelin16Data*)_cmsMallocZero(ContextID, sizeof(Prelin16Data));
400*3ac0a46fSAndroid Build Coastguard Worker if (p16 == NULL) return NULL;
401*3ac0a46fSAndroid Build Coastguard Worker
402*3ac0a46fSAndroid Build Coastguard Worker p16 ->nInputs = nInputs;
403*3ac0a46fSAndroid Build Coastguard Worker p16 ->nOutputs = nOutputs;
404*3ac0a46fSAndroid Build Coastguard Worker
405*3ac0a46fSAndroid Build Coastguard Worker
406*3ac0a46fSAndroid Build Coastguard Worker for (i=0; i < nInputs; i++) {
407*3ac0a46fSAndroid Build Coastguard Worker
408*3ac0a46fSAndroid Build Coastguard Worker if (In == NULL) {
409*3ac0a46fSAndroid Build Coastguard Worker p16 -> ParamsCurveIn16[i] = NULL;
410*3ac0a46fSAndroid Build Coastguard Worker p16 -> EvalCurveIn16[i] = Eval16nop1D;
411*3ac0a46fSAndroid Build Coastguard Worker
412*3ac0a46fSAndroid Build Coastguard Worker }
413*3ac0a46fSAndroid Build Coastguard Worker else {
414*3ac0a46fSAndroid Build Coastguard Worker p16 -> ParamsCurveIn16[i] = In[i] ->InterpParams;
415*3ac0a46fSAndroid Build Coastguard Worker p16 -> EvalCurveIn16[i] = p16 ->ParamsCurveIn16[i]->Interpolation.Lerp16;
416*3ac0a46fSAndroid Build Coastguard Worker }
417*3ac0a46fSAndroid Build Coastguard Worker }
418*3ac0a46fSAndroid Build Coastguard Worker
419*3ac0a46fSAndroid Build Coastguard Worker p16 ->CLUTparams = ColorMap;
420*3ac0a46fSAndroid Build Coastguard Worker p16 ->EvalCLUT = ColorMap ->Interpolation.Lerp16;
421*3ac0a46fSAndroid Build Coastguard Worker
422*3ac0a46fSAndroid Build Coastguard Worker
423*3ac0a46fSAndroid Build Coastguard Worker p16 -> EvalCurveOut16 = (_cmsInterpFn16*) _cmsCalloc(ContextID, nOutputs, sizeof(_cmsInterpFn16));
424*3ac0a46fSAndroid Build Coastguard Worker if (p16->EvalCurveOut16 == NULL)
425*3ac0a46fSAndroid Build Coastguard Worker {
426*3ac0a46fSAndroid Build Coastguard Worker _cmsFree(ContextID, p16);
427*3ac0a46fSAndroid Build Coastguard Worker return NULL;
428*3ac0a46fSAndroid Build Coastguard Worker }
429*3ac0a46fSAndroid Build Coastguard Worker
430*3ac0a46fSAndroid Build Coastguard Worker p16 -> ParamsCurveOut16 = (cmsInterpParams**) _cmsCalloc(ContextID, nOutputs, sizeof(cmsInterpParams* ));
431*3ac0a46fSAndroid Build Coastguard Worker if (p16->ParamsCurveOut16 == NULL)
432*3ac0a46fSAndroid Build Coastguard Worker {
433*3ac0a46fSAndroid Build Coastguard Worker
434*3ac0a46fSAndroid Build Coastguard Worker _cmsFree(ContextID, p16->EvalCurveOut16);
435*3ac0a46fSAndroid Build Coastguard Worker _cmsFree(ContextID, p16);
436*3ac0a46fSAndroid Build Coastguard Worker return NULL;
437*3ac0a46fSAndroid Build Coastguard Worker }
438*3ac0a46fSAndroid Build Coastguard Worker
439*3ac0a46fSAndroid Build Coastguard Worker for (i=0; i < nOutputs; i++) {
440*3ac0a46fSAndroid Build Coastguard Worker
441*3ac0a46fSAndroid Build Coastguard Worker if (Out == NULL) {
442*3ac0a46fSAndroid Build Coastguard Worker p16 ->ParamsCurveOut16[i] = NULL;
443*3ac0a46fSAndroid Build Coastguard Worker p16 -> EvalCurveOut16[i] = Eval16nop1D;
444*3ac0a46fSAndroid Build Coastguard Worker }
445*3ac0a46fSAndroid Build Coastguard Worker else {
446*3ac0a46fSAndroid Build Coastguard Worker
447*3ac0a46fSAndroid Build Coastguard Worker p16 ->ParamsCurveOut16[i] = Out[i] ->InterpParams;
448*3ac0a46fSAndroid Build Coastguard Worker p16 -> EvalCurveOut16[i] = p16 ->ParamsCurveOut16[i]->Interpolation.Lerp16;
449*3ac0a46fSAndroid Build Coastguard Worker }
450*3ac0a46fSAndroid Build Coastguard Worker }
451*3ac0a46fSAndroid Build Coastguard Worker
452*3ac0a46fSAndroid Build Coastguard Worker return p16;
453*3ac0a46fSAndroid Build Coastguard Worker }
454*3ac0a46fSAndroid Build Coastguard Worker
455*3ac0a46fSAndroid Build Coastguard Worker
456*3ac0a46fSAndroid Build Coastguard Worker
457*3ac0a46fSAndroid Build Coastguard Worker // Resampling ---------------------------------------------------------------------------------
458*3ac0a46fSAndroid Build Coastguard Worker
459*3ac0a46fSAndroid Build Coastguard Worker #define PRELINEARIZATION_POINTS 4096
460*3ac0a46fSAndroid Build Coastguard Worker
461*3ac0a46fSAndroid Build Coastguard Worker // Sampler implemented by another LUT. This is a clean way to precalculate the devicelink 3D CLUT for
462*3ac0a46fSAndroid Build Coastguard Worker // almost any transform. We use floating point precision and then convert from floating point to 16 bits.
463*3ac0a46fSAndroid Build Coastguard Worker static
XFormSampler16(CMSREGISTER const cmsUInt16Number In[],CMSREGISTER cmsUInt16Number Out[],CMSREGISTER void * Cargo)464*3ac0a46fSAndroid Build Coastguard Worker cmsInt32Number XFormSampler16(CMSREGISTER const cmsUInt16Number In[],
465*3ac0a46fSAndroid Build Coastguard Worker CMSREGISTER cmsUInt16Number Out[],
466*3ac0a46fSAndroid Build Coastguard Worker CMSREGISTER void* Cargo)
467*3ac0a46fSAndroid Build Coastguard Worker {
468*3ac0a46fSAndroid Build Coastguard Worker cmsPipeline* Lut = (cmsPipeline*) Cargo;
469*3ac0a46fSAndroid Build Coastguard Worker cmsFloat32Number InFloat[cmsMAXCHANNELS], OutFloat[cmsMAXCHANNELS];
470*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number i;
471*3ac0a46fSAndroid Build Coastguard Worker
472*3ac0a46fSAndroid Build Coastguard Worker _cmsAssert(Lut -> InputChannels < cmsMAXCHANNELS);
473*3ac0a46fSAndroid Build Coastguard Worker _cmsAssert(Lut -> OutputChannels < cmsMAXCHANNELS);
474*3ac0a46fSAndroid Build Coastguard Worker
475*3ac0a46fSAndroid Build Coastguard Worker // From 16 bit to floating point
476*3ac0a46fSAndroid Build Coastguard Worker for (i=0; i < Lut ->InputChannels; i++)
477*3ac0a46fSAndroid Build Coastguard Worker InFloat[i] = (cmsFloat32Number) (In[i] / 65535.0);
478*3ac0a46fSAndroid Build Coastguard Worker
479*3ac0a46fSAndroid Build Coastguard Worker // Evaluate in floating point
480*3ac0a46fSAndroid Build Coastguard Worker cmsPipelineEvalFloat(InFloat, OutFloat, Lut);
481*3ac0a46fSAndroid Build Coastguard Worker
482*3ac0a46fSAndroid Build Coastguard Worker // Back to 16 bits representation
483*3ac0a46fSAndroid Build Coastguard Worker for (i=0; i < Lut ->OutputChannels; i++)
484*3ac0a46fSAndroid Build Coastguard Worker Out[i] = _cmsQuickSaturateWord(OutFloat[i] * 65535.0);
485*3ac0a46fSAndroid Build Coastguard Worker
486*3ac0a46fSAndroid Build Coastguard Worker // Always succeed
487*3ac0a46fSAndroid Build Coastguard Worker return TRUE;
488*3ac0a46fSAndroid Build Coastguard Worker }
489*3ac0a46fSAndroid Build Coastguard Worker
490*3ac0a46fSAndroid Build Coastguard Worker // Try to see if the curves of a given MPE are linear
491*3ac0a46fSAndroid Build Coastguard Worker static
AllCurvesAreLinear(cmsStage * mpe)492*3ac0a46fSAndroid Build Coastguard Worker cmsBool AllCurvesAreLinear(cmsStage* mpe)
493*3ac0a46fSAndroid Build Coastguard Worker {
494*3ac0a46fSAndroid Build Coastguard Worker cmsToneCurve** Curves;
495*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number i, n;
496*3ac0a46fSAndroid Build Coastguard Worker
497*3ac0a46fSAndroid Build Coastguard Worker Curves = _cmsStageGetPtrToCurveSet(mpe);
498*3ac0a46fSAndroid Build Coastguard Worker if (Curves == NULL) return FALSE;
499*3ac0a46fSAndroid Build Coastguard Worker
500*3ac0a46fSAndroid Build Coastguard Worker n = cmsStageOutputChannels(mpe);
501*3ac0a46fSAndroid Build Coastguard Worker
502*3ac0a46fSAndroid Build Coastguard Worker for (i=0; i < n; i++) {
503*3ac0a46fSAndroid Build Coastguard Worker if (!cmsIsToneCurveLinear(Curves[i])) return FALSE;
504*3ac0a46fSAndroid Build Coastguard Worker }
505*3ac0a46fSAndroid Build Coastguard Worker
506*3ac0a46fSAndroid Build Coastguard Worker return TRUE;
507*3ac0a46fSAndroid Build Coastguard Worker }
508*3ac0a46fSAndroid Build Coastguard Worker
509*3ac0a46fSAndroid Build Coastguard Worker // This function replaces a specific node placed in "At" by the "Value" numbers. Its purpose
510*3ac0a46fSAndroid Build Coastguard Worker // is to fix scum dot on broken profiles/transforms. Works on 1, 3 and 4 channels
511*3ac0a46fSAndroid Build Coastguard Worker static
PatchLUT(cmsStage * CLUT,cmsUInt16Number At[],cmsUInt16Number Value[],cmsUInt32Number nChannelsOut,cmsUInt32Number nChannelsIn)512*3ac0a46fSAndroid Build Coastguard Worker cmsBool PatchLUT(cmsStage* CLUT, cmsUInt16Number At[], cmsUInt16Number Value[],
513*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number nChannelsOut, cmsUInt32Number nChannelsIn)
514*3ac0a46fSAndroid Build Coastguard Worker {
515*3ac0a46fSAndroid Build Coastguard Worker _cmsStageCLutData* Grid = (_cmsStageCLutData*) CLUT ->Data;
516*3ac0a46fSAndroid Build Coastguard Worker cmsInterpParams* p16 = Grid ->Params;
517*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number px, py, pz, pw;
518*3ac0a46fSAndroid Build Coastguard Worker int x0, y0, z0, w0;
519*3ac0a46fSAndroid Build Coastguard Worker int i, index;
520*3ac0a46fSAndroid Build Coastguard Worker
521*3ac0a46fSAndroid Build Coastguard Worker if (CLUT -> Type != cmsSigCLutElemType) {
522*3ac0a46fSAndroid Build Coastguard Worker cmsSignalError(CLUT->ContextID, cmsERROR_INTERNAL, "(internal) Attempt to PatchLUT on non-lut stage");
523*3ac0a46fSAndroid Build Coastguard Worker return FALSE;
524*3ac0a46fSAndroid Build Coastguard Worker }
525*3ac0a46fSAndroid Build Coastguard Worker
526*3ac0a46fSAndroid Build Coastguard Worker if (nChannelsIn == 4) {
527*3ac0a46fSAndroid Build Coastguard Worker
528*3ac0a46fSAndroid Build Coastguard Worker px = ((cmsFloat64Number) At[0] * (p16->Domain[0])) / 65535.0;
529*3ac0a46fSAndroid Build Coastguard Worker py = ((cmsFloat64Number) At[1] * (p16->Domain[1])) / 65535.0;
530*3ac0a46fSAndroid Build Coastguard Worker pz = ((cmsFloat64Number) At[2] * (p16->Domain[2])) / 65535.0;
531*3ac0a46fSAndroid Build Coastguard Worker pw = ((cmsFloat64Number) At[3] * (p16->Domain[3])) / 65535.0;
532*3ac0a46fSAndroid Build Coastguard Worker
533*3ac0a46fSAndroid Build Coastguard Worker x0 = (int) floor(px);
534*3ac0a46fSAndroid Build Coastguard Worker y0 = (int) floor(py);
535*3ac0a46fSAndroid Build Coastguard Worker z0 = (int) floor(pz);
536*3ac0a46fSAndroid Build Coastguard Worker w0 = (int) floor(pw);
537*3ac0a46fSAndroid Build Coastguard Worker
538*3ac0a46fSAndroid Build Coastguard Worker if (((px - x0) != 0) ||
539*3ac0a46fSAndroid Build Coastguard Worker ((py - y0) != 0) ||
540*3ac0a46fSAndroid Build Coastguard Worker ((pz - z0) != 0) ||
541*3ac0a46fSAndroid Build Coastguard Worker ((pw - w0) != 0)) return FALSE; // Not on exact node
542*3ac0a46fSAndroid Build Coastguard Worker
543*3ac0a46fSAndroid Build Coastguard Worker index = (int) p16 -> opta[3] * x0 +
544*3ac0a46fSAndroid Build Coastguard Worker (int) p16 -> opta[2] * y0 +
545*3ac0a46fSAndroid Build Coastguard Worker (int) p16 -> opta[1] * z0 +
546*3ac0a46fSAndroid Build Coastguard Worker (int) p16 -> opta[0] * w0;
547*3ac0a46fSAndroid Build Coastguard Worker }
548*3ac0a46fSAndroid Build Coastguard Worker else
549*3ac0a46fSAndroid Build Coastguard Worker if (nChannelsIn == 3) {
550*3ac0a46fSAndroid Build Coastguard Worker
551*3ac0a46fSAndroid Build Coastguard Worker px = ((cmsFloat64Number) At[0] * (p16->Domain[0])) / 65535.0;
552*3ac0a46fSAndroid Build Coastguard Worker py = ((cmsFloat64Number) At[1] * (p16->Domain[1])) / 65535.0;
553*3ac0a46fSAndroid Build Coastguard Worker pz = ((cmsFloat64Number) At[2] * (p16->Domain[2])) / 65535.0;
554*3ac0a46fSAndroid Build Coastguard Worker
555*3ac0a46fSAndroid Build Coastguard Worker x0 = (int) floor(px);
556*3ac0a46fSAndroid Build Coastguard Worker y0 = (int) floor(py);
557*3ac0a46fSAndroid Build Coastguard Worker z0 = (int) floor(pz);
558*3ac0a46fSAndroid Build Coastguard Worker
559*3ac0a46fSAndroid Build Coastguard Worker if (((px - x0) != 0) ||
560*3ac0a46fSAndroid Build Coastguard Worker ((py - y0) != 0) ||
561*3ac0a46fSAndroid Build Coastguard Worker ((pz - z0) != 0)) return FALSE; // Not on exact node
562*3ac0a46fSAndroid Build Coastguard Worker
563*3ac0a46fSAndroid Build Coastguard Worker index = (int) p16 -> opta[2] * x0 +
564*3ac0a46fSAndroid Build Coastguard Worker (int) p16 -> opta[1] * y0 +
565*3ac0a46fSAndroid Build Coastguard Worker (int) p16 -> opta[0] * z0;
566*3ac0a46fSAndroid Build Coastguard Worker }
567*3ac0a46fSAndroid Build Coastguard Worker else
568*3ac0a46fSAndroid Build Coastguard Worker if (nChannelsIn == 1) {
569*3ac0a46fSAndroid Build Coastguard Worker
570*3ac0a46fSAndroid Build Coastguard Worker px = ((cmsFloat64Number) At[0] * (p16->Domain[0])) / 65535.0;
571*3ac0a46fSAndroid Build Coastguard Worker
572*3ac0a46fSAndroid Build Coastguard Worker x0 = (int) floor(px);
573*3ac0a46fSAndroid Build Coastguard Worker
574*3ac0a46fSAndroid Build Coastguard Worker if (((px - x0) != 0)) return FALSE; // Not on exact node
575*3ac0a46fSAndroid Build Coastguard Worker
576*3ac0a46fSAndroid Build Coastguard Worker index = (int) p16 -> opta[0] * x0;
577*3ac0a46fSAndroid Build Coastguard Worker }
578*3ac0a46fSAndroid Build Coastguard Worker else {
579*3ac0a46fSAndroid Build Coastguard Worker cmsSignalError(CLUT->ContextID, cmsERROR_INTERNAL, "(internal) %d Channels are not supported on PatchLUT", nChannelsIn);
580*3ac0a46fSAndroid Build Coastguard Worker return FALSE;
581*3ac0a46fSAndroid Build Coastguard Worker }
582*3ac0a46fSAndroid Build Coastguard Worker
583*3ac0a46fSAndroid Build Coastguard Worker for (i = 0; i < (int) nChannelsOut; i++)
584*3ac0a46fSAndroid Build Coastguard Worker Grid->Tab.T[index + i] = Value[i];
585*3ac0a46fSAndroid Build Coastguard Worker
586*3ac0a46fSAndroid Build Coastguard Worker return TRUE;
587*3ac0a46fSAndroid Build Coastguard Worker }
588*3ac0a46fSAndroid Build Coastguard Worker
589*3ac0a46fSAndroid Build Coastguard Worker // Auxiliary, to see if two values are equal or very different
590*3ac0a46fSAndroid Build Coastguard Worker static
WhitesAreEqual(cmsUInt32Number n,cmsUInt16Number White1[],cmsUInt16Number White2[])591*3ac0a46fSAndroid Build Coastguard Worker cmsBool WhitesAreEqual(cmsUInt32Number n, cmsUInt16Number White1[], cmsUInt16Number White2[] )
592*3ac0a46fSAndroid Build Coastguard Worker {
593*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number i;
594*3ac0a46fSAndroid Build Coastguard Worker
595*3ac0a46fSAndroid Build Coastguard Worker for (i=0; i < n; i++) {
596*3ac0a46fSAndroid Build Coastguard Worker
597*3ac0a46fSAndroid Build Coastguard Worker if (abs(White1[i] - White2[i]) > 0xf000) return TRUE; // Values are so extremely different that the fixup should be avoided
598*3ac0a46fSAndroid Build Coastguard Worker if (White1[i] != White2[i]) return FALSE;
599*3ac0a46fSAndroid Build Coastguard Worker }
600*3ac0a46fSAndroid Build Coastguard Worker return TRUE;
601*3ac0a46fSAndroid Build Coastguard Worker }
602*3ac0a46fSAndroid Build Coastguard Worker
603*3ac0a46fSAndroid Build Coastguard Worker
604*3ac0a46fSAndroid Build Coastguard Worker // Locate the node for the white point and fix it to pure white in order to avoid scum dot.
605*3ac0a46fSAndroid Build Coastguard Worker static
FixWhiteMisalignment(cmsPipeline * Lut,cmsColorSpaceSignature EntryColorSpace,cmsColorSpaceSignature ExitColorSpace)606*3ac0a46fSAndroid Build Coastguard Worker cmsBool FixWhiteMisalignment(cmsPipeline* Lut, cmsColorSpaceSignature EntryColorSpace, cmsColorSpaceSignature ExitColorSpace)
607*3ac0a46fSAndroid Build Coastguard Worker {
608*3ac0a46fSAndroid Build Coastguard Worker cmsUInt16Number *WhitePointIn, *WhitePointOut;
609*3ac0a46fSAndroid Build Coastguard Worker cmsUInt16Number WhiteIn[cmsMAXCHANNELS], WhiteOut[cmsMAXCHANNELS], ObtainedOut[cmsMAXCHANNELS];
610*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number i, nOuts, nIns;
611*3ac0a46fSAndroid Build Coastguard Worker cmsStage *PreLin = NULL, *CLUT = NULL, *PostLin = NULL;
612*3ac0a46fSAndroid Build Coastguard Worker
613*3ac0a46fSAndroid Build Coastguard Worker if (!_cmsEndPointsBySpace(EntryColorSpace,
614*3ac0a46fSAndroid Build Coastguard Worker &WhitePointIn, NULL, &nIns)) return FALSE;
615*3ac0a46fSAndroid Build Coastguard Worker
616*3ac0a46fSAndroid Build Coastguard Worker if (!_cmsEndPointsBySpace(ExitColorSpace,
617*3ac0a46fSAndroid Build Coastguard Worker &WhitePointOut, NULL, &nOuts)) return FALSE;
618*3ac0a46fSAndroid Build Coastguard Worker
619*3ac0a46fSAndroid Build Coastguard Worker // It needs to be fixed?
620*3ac0a46fSAndroid Build Coastguard Worker if (Lut ->InputChannels != nIns) return FALSE;
621*3ac0a46fSAndroid Build Coastguard Worker if (Lut ->OutputChannels != nOuts) return FALSE;
622*3ac0a46fSAndroid Build Coastguard Worker
623*3ac0a46fSAndroid Build Coastguard Worker cmsPipelineEval16(WhitePointIn, ObtainedOut, Lut);
624*3ac0a46fSAndroid Build Coastguard Worker
625*3ac0a46fSAndroid Build Coastguard Worker if (WhitesAreEqual(nOuts, WhitePointOut, ObtainedOut)) return TRUE; // whites already match
626*3ac0a46fSAndroid Build Coastguard Worker
627*3ac0a46fSAndroid Build Coastguard Worker // Check if the LUT comes as Prelin, CLUT or Postlin. We allow all combinations
628*3ac0a46fSAndroid Build Coastguard Worker if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, &PreLin, &CLUT, &PostLin))
629*3ac0a46fSAndroid Build Coastguard Worker if (!cmsPipelineCheckAndRetreiveStages(Lut, 2, cmsSigCurveSetElemType, cmsSigCLutElemType, &PreLin, &CLUT))
630*3ac0a46fSAndroid Build Coastguard Worker if (!cmsPipelineCheckAndRetreiveStages(Lut, 2, cmsSigCLutElemType, cmsSigCurveSetElemType, &CLUT, &PostLin))
631*3ac0a46fSAndroid Build Coastguard Worker if (!cmsPipelineCheckAndRetreiveStages(Lut, 1, cmsSigCLutElemType, &CLUT))
632*3ac0a46fSAndroid Build Coastguard Worker return FALSE;
633*3ac0a46fSAndroid Build Coastguard Worker
634*3ac0a46fSAndroid Build Coastguard Worker // We need to interpolate white points of both, pre and post curves
635*3ac0a46fSAndroid Build Coastguard Worker if (PreLin) {
636*3ac0a46fSAndroid Build Coastguard Worker
637*3ac0a46fSAndroid Build Coastguard Worker cmsToneCurve** Curves = _cmsStageGetPtrToCurveSet(PreLin);
638*3ac0a46fSAndroid Build Coastguard Worker
639*3ac0a46fSAndroid Build Coastguard Worker for (i=0; i < nIns; i++) {
640*3ac0a46fSAndroid Build Coastguard Worker WhiteIn[i] = cmsEvalToneCurve16(Curves[i], WhitePointIn[i]);
641*3ac0a46fSAndroid Build Coastguard Worker }
642*3ac0a46fSAndroid Build Coastguard Worker }
643*3ac0a46fSAndroid Build Coastguard Worker else {
644*3ac0a46fSAndroid Build Coastguard Worker for (i=0; i < nIns; i++)
645*3ac0a46fSAndroid Build Coastguard Worker WhiteIn[i] = WhitePointIn[i];
646*3ac0a46fSAndroid Build Coastguard Worker }
647*3ac0a46fSAndroid Build Coastguard Worker
648*3ac0a46fSAndroid Build Coastguard Worker // If any post-linearization, we need to find how is represented white before the curve, do
649*3ac0a46fSAndroid Build Coastguard Worker // a reverse interpolation in this case.
650*3ac0a46fSAndroid Build Coastguard Worker if (PostLin) {
651*3ac0a46fSAndroid Build Coastguard Worker
652*3ac0a46fSAndroid Build Coastguard Worker cmsToneCurve** Curves = _cmsStageGetPtrToCurveSet(PostLin);
653*3ac0a46fSAndroid Build Coastguard Worker
654*3ac0a46fSAndroid Build Coastguard Worker for (i=0; i < nOuts; i++) {
655*3ac0a46fSAndroid Build Coastguard Worker
656*3ac0a46fSAndroid Build Coastguard Worker cmsToneCurve* InversePostLin = cmsReverseToneCurve(Curves[i]);
657*3ac0a46fSAndroid Build Coastguard Worker if (InversePostLin == NULL) {
658*3ac0a46fSAndroid Build Coastguard Worker WhiteOut[i] = WhitePointOut[i];
659*3ac0a46fSAndroid Build Coastguard Worker
660*3ac0a46fSAndroid Build Coastguard Worker } else {
661*3ac0a46fSAndroid Build Coastguard Worker
662*3ac0a46fSAndroid Build Coastguard Worker WhiteOut[i] = cmsEvalToneCurve16(InversePostLin, WhitePointOut[i]);
663*3ac0a46fSAndroid Build Coastguard Worker cmsFreeToneCurve(InversePostLin);
664*3ac0a46fSAndroid Build Coastguard Worker }
665*3ac0a46fSAndroid Build Coastguard Worker }
666*3ac0a46fSAndroid Build Coastguard Worker }
667*3ac0a46fSAndroid Build Coastguard Worker else {
668*3ac0a46fSAndroid Build Coastguard Worker for (i=0; i < nOuts; i++)
669*3ac0a46fSAndroid Build Coastguard Worker WhiteOut[i] = WhitePointOut[i];
670*3ac0a46fSAndroid Build Coastguard Worker }
671*3ac0a46fSAndroid Build Coastguard Worker
672*3ac0a46fSAndroid Build Coastguard Worker // Ok, proceed with patching. May fail and we don't care if it fails
673*3ac0a46fSAndroid Build Coastguard Worker PatchLUT(CLUT, WhiteIn, WhiteOut, nOuts, nIns);
674*3ac0a46fSAndroid Build Coastguard Worker
675*3ac0a46fSAndroid Build Coastguard Worker return TRUE;
676*3ac0a46fSAndroid Build Coastguard Worker }
677*3ac0a46fSAndroid Build Coastguard Worker
678*3ac0a46fSAndroid Build Coastguard Worker // -----------------------------------------------------------------------------------------------------------------------------------------------
679*3ac0a46fSAndroid Build Coastguard Worker // This function creates simple LUT from complex ones. The generated LUT has an optional set of
680*3ac0a46fSAndroid Build Coastguard Worker // prelinearization curves, a CLUT of nGridPoints and optional postlinearization tables.
681*3ac0a46fSAndroid Build Coastguard Worker // These curves have to exist in the original LUT in order to be used in the simplified output.
682*3ac0a46fSAndroid Build Coastguard Worker // Caller may also use the flags to allow this feature.
683*3ac0a46fSAndroid Build Coastguard Worker // LUTS with all curves will be simplified to a single curve. Parametric curves are lost.
684*3ac0a46fSAndroid Build Coastguard Worker // This function should be used on 16-bits LUTS only, as floating point losses precision when simplified
685*3ac0a46fSAndroid Build Coastguard Worker // -----------------------------------------------------------------------------------------------------------------------------------------------
686*3ac0a46fSAndroid Build Coastguard Worker
687*3ac0a46fSAndroid Build Coastguard Worker static
OptimizeByResampling(cmsPipeline ** Lut,cmsUInt32Number Intent,cmsUInt32Number * InputFormat,cmsUInt32Number * OutputFormat,cmsUInt32Number * dwFlags)688*3ac0a46fSAndroid Build Coastguard Worker cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags)
689*3ac0a46fSAndroid Build Coastguard Worker {
690*3ac0a46fSAndroid Build Coastguard Worker cmsPipeline* Src = NULL;
691*3ac0a46fSAndroid Build Coastguard Worker cmsPipeline* Dest = NULL;
692*3ac0a46fSAndroid Build Coastguard Worker cmsStage* CLUT;
693*3ac0a46fSAndroid Build Coastguard Worker cmsStage *KeepPreLin = NULL, *KeepPostLin = NULL;
694*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number nGridPoints;
695*3ac0a46fSAndroid Build Coastguard Worker cmsColorSpaceSignature ColorSpace, OutputColorSpace;
696*3ac0a46fSAndroid Build Coastguard Worker cmsStage *NewPreLin = NULL;
697*3ac0a46fSAndroid Build Coastguard Worker cmsStage *NewPostLin = NULL;
698*3ac0a46fSAndroid Build Coastguard Worker _cmsStageCLutData* DataCLUT;
699*3ac0a46fSAndroid Build Coastguard Worker cmsToneCurve** DataSetIn;
700*3ac0a46fSAndroid Build Coastguard Worker cmsToneCurve** DataSetOut;
701*3ac0a46fSAndroid Build Coastguard Worker Prelin16Data* p16;
702*3ac0a46fSAndroid Build Coastguard Worker
703*3ac0a46fSAndroid Build Coastguard Worker // This is a lossy optimization! does not apply in floating-point cases
704*3ac0a46fSAndroid Build Coastguard Worker if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) return FALSE;
705*3ac0a46fSAndroid Build Coastguard Worker
706*3ac0a46fSAndroid Build Coastguard Worker ColorSpace = _cmsICCcolorSpace((int) T_COLORSPACE(*InputFormat));
707*3ac0a46fSAndroid Build Coastguard Worker OutputColorSpace = _cmsICCcolorSpace((int) T_COLORSPACE(*OutputFormat));
708*3ac0a46fSAndroid Build Coastguard Worker
709*3ac0a46fSAndroid Build Coastguard Worker // Color space must be specified
710*3ac0a46fSAndroid Build Coastguard Worker if (ColorSpace == (cmsColorSpaceSignature)0 ||
711*3ac0a46fSAndroid Build Coastguard Worker OutputColorSpace == (cmsColorSpaceSignature)0) return FALSE;
712*3ac0a46fSAndroid Build Coastguard Worker
713*3ac0a46fSAndroid Build Coastguard Worker nGridPoints = _cmsReasonableGridpointsByColorspace(ColorSpace, *dwFlags);
714*3ac0a46fSAndroid Build Coastguard Worker
715*3ac0a46fSAndroid Build Coastguard Worker // For empty LUTs, 2 points are enough
716*3ac0a46fSAndroid Build Coastguard Worker if (cmsPipelineStageCount(*Lut) == 0)
717*3ac0a46fSAndroid Build Coastguard Worker nGridPoints = 2;
718*3ac0a46fSAndroid Build Coastguard Worker
719*3ac0a46fSAndroid Build Coastguard Worker Src = *Lut;
720*3ac0a46fSAndroid Build Coastguard Worker
721*3ac0a46fSAndroid Build Coastguard Worker // Allocate an empty LUT
722*3ac0a46fSAndroid Build Coastguard Worker Dest = cmsPipelineAlloc(Src ->ContextID, Src ->InputChannels, Src ->OutputChannels);
723*3ac0a46fSAndroid Build Coastguard Worker if (!Dest) return FALSE;
724*3ac0a46fSAndroid Build Coastguard Worker
725*3ac0a46fSAndroid Build Coastguard Worker // Prelinearization tables are kept unless indicated by flags
726*3ac0a46fSAndroid Build Coastguard Worker if (*dwFlags & cmsFLAGS_CLUT_PRE_LINEARIZATION) {
727*3ac0a46fSAndroid Build Coastguard Worker
728*3ac0a46fSAndroid Build Coastguard Worker // Get a pointer to the prelinearization element
729*3ac0a46fSAndroid Build Coastguard Worker cmsStage* PreLin = cmsPipelineGetPtrToFirstStage(Src);
730*3ac0a46fSAndroid Build Coastguard Worker
731*3ac0a46fSAndroid Build Coastguard Worker // Check if suitable
732*3ac0a46fSAndroid Build Coastguard Worker if (PreLin && PreLin ->Type == cmsSigCurveSetElemType) {
733*3ac0a46fSAndroid Build Coastguard Worker
734*3ac0a46fSAndroid Build Coastguard Worker // Maybe this is a linear tram, so we can avoid the whole stuff
735*3ac0a46fSAndroid Build Coastguard Worker if (!AllCurvesAreLinear(PreLin)) {
736*3ac0a46fSAndroid Build Coastguard Worker
737*3ac0a46fSAndroid Build Coastguard Worker // All seems ok, proceed.
738*3ac0a46fSAndroid Build Coastguard Worker NewPreLin = cmsStageDup(PreLin);
739*3ac0a46fSAndroid Build Coastguard Worker if(!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, NewPreLin))
740*3ac0a46fSAndroid Build Coastguard Worker goto Error;
741*3ac0a46fSAndroid Build Coastguard Worker
742*3ac0a46fSAndroid Build Coastguard Worker // Remove prelinearization. Since we have duplicated the curve
743*3ac0a46fSAndroid Build Coastguard Worker // in destination LUT, the sampling should be applied after this stage.
744*3ac0a46fSAndroid Build Coastguard Worker cmsPipelineUnlinkStage(Src, cmsAT_BEGIN, &KeepPreLin);
745*3ac0a46fSAndroid Build Coastguard Worker }
746*3ac0a46fSAndroid Build Coastguard Worker }
747*3ac0a46fSAndroid Build Coastguard Worker }
748*3ac0a46fSAndroid Build Coastguard Worker
749*3ac0a46fSAndroid Build Coastguard Worker // Allocate the CLUT
750*3ac0a46fSAndroid Build Coastguard Worker CLUT = cmsStageAllocCLut16bit(Src ->ContextID, nGridPoints, Src ->InputChannels, Src->OutputChannels, NULL);
751*3ac0a46fSAndroid Build Coastguard Worker if (CLUT == NULL) goto Error;
752*3ac0a46fSAndroid Build Coastguard Worker
753*3ac0a46fSAndroid Build Coastguard Worker // Add the CLUT to the destination LUT
754*3ac0a46fSAndroid Build Coastguard Worker if (!cmsPipelineInsertStage(Dest, cmsAT_END, CLUT)) {
755*3ac0a46fSAndroid Build Coastguard Worker goto Error;
756*3ac0a46fSAndroid Build Coastguard Worker }
757*3ac0a46fSAndroid Build Coastguard Worker
758*3ac0a46fSAndroid Build Coastguard Worker // Postlinearization tables are kept unless indicated by flags
759*3ac0a46fSAndroid Build Coastguard Worker if (*dwFlags & cmsFLAGS_CLUT_POST_LINEARIZATION) {
760*3ac0a46fSAndroid Build Coastguard Worker
761*3ac0a46fSAndroid Build Coastguard Worker // Get a pointer to the postlinearization if present
762*3ac0a46fSAndroid Build Coastguard Worker cmsStage* PostLin = cmsPipelineGetPtrToLastStage(Src);
763*3ac0a46fSAndroid Build Coastguard Worker
764*3ac0a46fSAndroid Build Coastguard Worker // Check if suitable
765*3ac0a46fSAndroid Build Coastguard Worker if (PostLin && cmsStageType(PostLin) == cmsSigCurveSetElemType) {
766*3ac0a46fSAndroid Build Coastguard Worker
767*3ac0a46fSAndroid Build Coastguard Worker // Maybe this is a linear tram, so we can avoid the whole stuff
768*3ac0a46fSAndroid Build Coastguard Worker if (!AllCurvesAreLinear(PostLin)) {
769*3ac0a46fSAndroid Build Coastguard Worker
770*3ac0a46fSAndroid Build Coastguard Worker // All seems ok, proceed.
771*3ac0a46fSAndroid Build Coastguard Worker NewPostLin = cmsStageDup(PostLin);
772*3ac0a46fSAndroid Build Coastguard Worker if (!cmsPipelineInsertStage(Dest, cmsAT_END, NewPostLin))
773*3ac0a46fSAndroid Build Coastguard Worker goto Error;
774*3ac0a46fSAndroid Build Coastguard Worker
775*3ac0a46fSAndroid Build Coastguard Worker // In destination LUT, the sampling should be applied after this stage.
776*3ac0a46fSAndroid Build Coastguard Worker cmsPipelineUnlinkStage(Src, cmsAT_END, &KeepPostLin);
777*3ac0a46fSAndroid Build Coastguard Worker }
778*3ac0a46fSAndroid Build Coastguard Worker }
779*3ac0a46fSAndroid Build Coastguard Worker }
780*3ac0a46fSAndroid Build Coastguard Worker
781*3ac0a46fSAndroid Build Coastguard Worker // Now its time to do the sampling. We have to ignore pre/post linearization
782*3ac0a46fSAndroid Build Coastguard Worker // The source LUT without pre/post curves is passed as parameter.
783*3ac0a46fSAndroid Build Coastguard Worker if (!cmsStageSampleCLut16bit(CLUT, XFormSampler16, (void*) Src, 0)) {
784*3ac0a46fSAndroid Build Coastguard Worker Error:
785*3ac0a46fSAndroid Build Coastguard Worker // Ops, something went wrong, Restore stages
786*3ac0a46fSAndroid Build Coastguard Worker if (KeepPreLin != NULL) {
787*3ac0a46fSAndroid Build Coastguard Worker if (!cmsPipelineInsertStage(Src, cmsAT_BEGIN, KeepPreLin)) {
788*3ac0a46fSAndroid Build Coastguard Worker _cmsAssert(0); // This never happens
789*3ac0a46fSAndroid Build Coastguard Worker }
790*3ac0a46fSAndroid Build Coastguard Worker }
791*3ac0a46fSAndroid Build Coastguard Worker if (KeepPostLin != NULL) {
792*3ac0a46fSAndroid Build Coastguard Worker if (!cmsPipelineInsertStage(Src, cmsAT_END, KeepPostLin)) {
793*3ac0a46fSAndroid Build Coastguard Worker _cmsAssert(0); // This never happens
794*3ac0a46fSAndroid Build Coastguard Worker }
795*3ac0a46fSAndroid Build Coastguard Worker }
796*3ac0a46fSAndroid Build Coastguard Worker cmsPipelineFree(Dest);
797*3ac0a46fSAndroid Build Coastguard Worker return FALSE;
798*3ac0a46fSAndroid Build Coastguard Worker }
799*3ac0a46fSAndroid Build Coastguard Worker
800*3ac0a46fSAndroid Build Coastguard Worker // Done.
801*3ac0a46fSAndroid Build Coastguard Worker
802*3ac0a46fSAndroid Build Coastguard Worker if (KeepPreLin != NULL) cmsStageFree(KeepPreLin);
803*3ac0a46fSAndroid Build Coastguard Worker if (KeepPostLin != NULL) cmsStageFree(KeepPostLin);
804*3ac0a46fSAndroid Build Coastguard Worker cmsPipelineFree(Src);
805*3ac0a46fSAndroid Build Coastguard Worker
806*3ac0a46fSAndroid Build Coastguard Worker DataCLUT = (_cmsStageCLutData*) CLUT ->Data;
807*3ac0a46fSAndroid Build Coastguard Worker
808*3ac0a46fSAndroid Build Coastguard Worker if (NewPreLin == NULL) DataSetIn = NULL;
809*3ac0a46fSAndroid Build Coastguard Worker else DataSetIn = ((_cmsStageToneCurvesData*) NewPreLin ->Data) ->TheCurves;
810*3ac0a46fSAndroid Build Coastguard Worker
811*3ac0a46fSAndroid Build Coastguard Worker if (NewPostLin == NULL) DataSetOut = NULL;
812*3ac0a46fSAndroid Build Coastguard Worker else DataSetOut = ((_cmsStageToneCurvesData*) NewPostLin ->Data) ->TheCurves;
813*3ac0a46fSAndroid Build Coastguard Worker
814*3ac0a46fSAndroid Build Coastguard Worker
815*3ac0a46fSAndroid Build Coastguard Worker if (DataSetIn == NULL && DataSetOut == NULL) {
816*3ac0a46fSAndroid Build Coastguard Worker
817*3ac0a46fSAndroid Build Coastguard Worker _cmsPipelineSetOptimizationParameters(Dest, Lerp16Adapter, DataCLUT->Params, NULL, NULL);
818*3ac0a46fSAndroid Build Coastguard Worker }
819*3ac0a46fSAndroid Build Coastguard Worker else {
820*3ac0a46fSAndroid Build Coastguard Worker
821*3ac0a46fSAndroid Build Coastguard Worker p16 = PrelinOpt16alloc(Dest ->ContextID,
822*3ac0a46fSAndroid Build Coastguard Worker DataCLUT ->Params,
823*3ac0a46fSAndroid Build Coastguard Worker Dest ->InputChannels,
824*3ac0a46fSAndroid Build Coastguard Worker DataSetIn,
825*3ac0a46fSAndroid Build Coastguard Worker Dest ->OutputChannels,
826*3ac0a46fSAndroid Build Coastguard Worker DataSetOut);
827*3ac0a46fSAndroid Build Coastguard Worker
828*3ac0a46fSAndroid Build Coastguard Worker _cmsPipelineSetOptimizationParameters(Dest, PrelinEval16, (void*) p16, PrelinOpt16free, Prelin16dup);
829*3ac0a46fSAndroid Build Coastguard Worker }
830*3ac0a46fSAndroid Build Coastguard Worker
831*3ac0a46fSAndroid Build Coastguard Worker
832*3ac0a46fSAndroid Build Coastguard Worker // Don't fix white on absolute colorimetric
833*3ac0a46fSAndroid Build Coastguard Worker if (Intent == INTENT_ABSOLUTE_COLORIMETRIC)
834*3ac0a46fSAndroid Build Coastguard Worker *dwFlags |= cmsFLAGS_NOWHITEONWHITEFIXUP;
835*3ac0a46fSAndroid Build Coastguard Worker
836*3ac0a46fSAndroid Build Coastguard Worker if (!(*dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP)) {
837*3ac0a46fSAndroid Build Coastguard Worker
838*3ac0a46fSAndroid Build Coastguard Worker FixWhiteMisalignment(Dest, ColorSpace, OutputColorSpace);
839*3ac0a46fSAndroid Build Coastguard Worker }
840*3ac0a46fSAndroid Build Coastguard Worker
841*3ac0a46fSAndroid Build Coastguard Worker *Lut = Dest;
842*3ac0a46fSAndroid Build Coastguard Worker return TRUE;
843*3ac0a46fSAndroid Build Coastguard Worker
844*3ac0a46fSAndroid Build Coastguard Worker cmsUNUSED_PARAMETER(Intent);
845*3ac0a46fSAndroid Build Coastguard Worker }
846*3ac0a46fSAndroid Build Coastguard Worker
847*3ac0a46fSAndroid Build Coastguard Worker
848*3ac0a46fSAndroid Build Coastguard Worker // -----------------------------------------------------------------------------------------------------------------------------------------------
849*3ac0a46fSAndroid Build Coastguard Worker // Fixes the gamma balancing of transform. This is described in my paper "Prelinearization Stages on
850*3ac0a46fSAndroid Build Coastguard Worker // Color-Management Application-Specific Integrated Circuits (ASICs)" presented at NIP24. It only works
851*3ac0a46fSAndroid Build Coastguard Worker // for RGB transforms. See the paper for more details
852*3ac0a46fSAndroid Build Coastguard Worker // -----------------------------------------------------------------------------------------------------------------------------------------------
853*3ac0a46fSAndroid Build Coastguard Worker
854*3ac0a46fSAndroid Build Coastguard Worker
855*3ac0a46fSAndroid Build Coastguard Worker // Normalize endpoints by slope limiting max and min. This assures endpoints as well.
856*3ac0a46fSAndroid Build Coastguard Worker // Descending curves are handled as well.
857*3ac0a46fSAndroid Build Coastguard Worker static
SlopeLimiting(cmsToneCurve * g)858*3ac0a46fSAndroid Build Coastguard Worker void SlopeLimiting(cmsToneCurve* g)
859*3ac0a46fSAndroid Build Coastguard Worker {
860*3ac0a46fSAndroid Build Coastguard Worker int BeginVal, EndVal;
861*3ac0a46fSAndroid Build Coastguard Worker int AtBegin = (int) floor((cmsFloat64Number) g ->nEntries * 0.02 + 0.5); // Cutoff at 2%
862*3ac0a46fSAndroid Build Coastguard Worker int AtEnd = (int) g ->nEntries - AtBegin - 1; // And 98%
863*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number Val, Slope, beta;
864*3ac0a46fSAndroid Build Coastguard Worker int i;
865*3ac0a46fSAndroid Build Coastguard Worker
866*3ac0a46fSAndroid Build Coastguard Worker if (cmsIsToneCurveDescending(g)) {
867*3ac0a46fSAndroid Build Coastguard Worker BeginVal = 0xffff; EndVal = 0;
868*3ac0a46fSAndroid Build Coastguard Worker }
869*3ac0a46fSAndroid Build Coastguard Worker else {
870*3ac0a46fSAndroid Build Coastguard Worker BeginVal = 0; EndVal = 0xffff;
871*3ac0a46fSAndroid Build Coastguard Worker }
872*3ac0a46fSAndroid Build Coastguard Worker
873*3ac0a46fSAndroid Build Coastguard Worker // Compute slope and offset for begin of curve
874*3ac0a46fSAndroid Build Coastguard Worker Val = g ->Table16[AtBegin];
875*3ac0a46fSAndroid Build Coastguard Worker Slope = (Val - BeginVal) / AtBegin;
876*3ac0a46fSAndroid Build Coastguard Worker beta = Val - Slope * AtBegin;
877*3ac0a46fSAndroid Build Coastguard Worker
878*3ac0a46fSAndroid Build Coastguard Worker for (i=0; i < AtBegin; i++)
879*3ac0a46fSAndroid Build Coastguard Worker g ->Table16[i] = _cmsQuickSaturateWord(i * Slope + beta);
880*3ac0a46fSAndroid Build Coastguard Worker
881*3ac0a46fSAndroid Build Coastguard Worker // Compute slope and offset for the end
882*3ac0a46fSAndroid Build Coastguard Worker Val = g ->Table16[AtEnd];
883*3ac0a46fSAndroid Build Coastguard Worker Slope = (EndVal - Val) / AtBegin; // AtBegin holds the X interval, which is same in both cases
884*3ac0a46fSAndroid Build Coastguard Worker beta = Val - Slope * AtEnd;
885*3ac0a46fSAndroid Build Coastguard Worker
886*3ac0a46fSAndroid Build Coastguard Worker for (i = AtEnd; i < (int) g ->nEntries; i++)
887*3ac0a46fSAndroid Build Coastguard Worker g ->Table16[i] = _cmsQuickSaturateWord(i * Slope + beta);
888*3ac0a46fSAndroid Build Coastguard Worker }
889*3ac0a46fSAndroid Build Coastguard Worker
890*3ac0a46fSAndroid Build Coastguard Worker
891*3ac0a46fSAndroid Build Coastguard Worker // Precomputes tables for 8-bit on input devicelink.
892*3ac0a46fSAndroid Build Coastguard Worker static
PrelinOpt8alloc(cmsContext ContextID,const cmsInterpParams * p,cmsToneCurve * G[3])893*3ac0a46fSAndroid Build Coastguard Worker Prelin8Data* PrelinOpt8alloc(cmsContext ContextID, const cmsInterpParams* p, cmsToneCurve* G[3])
894*3ac0a46fSAndroid Build Coastguard Worker {
895*3ac0a46fSAndroid Build Coastguard Worker int i;
896*3ac0a46fSAndroid Build Coastguard Worker cmsUInt16Number Input[3];
897*3ac0a46fSAndroid Build Coastguard Worker cmsS15Fixed16Number v1, v2, v3;
898*3ac0a46fSAndroid Build Coastguard Worker Prelin8Data* p8;
899*3ac0a46fSAndroid Build Coastguard Worker
900*3ac0a46fSAndroid Build Coastguard Worker p8 = (Prelin8Data*)_cmsMallocZero(ContextID, sizeof(Prelin8Data));
901*3ac0a46fSAndroid Build Coastguard Worker if (p8 == NULL) return NULL;
902*3ac0a46fSAndroid Build Coastguard Worker
903*3ac0a46fSAndroid Build Coastguard Worker // Since this only works for 8 bit input, values comes always as x * 257,
904*3ac0a46fSAndroid Build Coastguard Worker // we can safely take msb byte (x << 8 + x)
905*3ac0a46fSAndroid Build Coastguard Worker
906*3ac0a46fSAndroid Build Coastguard Worker for (i=0; i < 256; i++) {
907*3ac0a46fSAndroid Build Coastguard Worker
908*3ac0a46fSAndroid Build Coastguard Worker if (G != NULL) {
909*3ac0a46fSAndroid Build Coastguard Worker
910*3ac0a46fSAndroid Build Coastguard Worker // Get 16-bit representation
911*3ac0a46fSAndroid Build Coastguard Worker Input[0] = cmsEvalToneCurve16(G[0], FROM_8_TO_16(i));
912*3ac0a46fSAndroid Build Coastguard Worker Input[1] = cmsEvalToneCurve16(G[1], FROM_8_TO_16(i));
913*3ac0a46fSAndroid Build Coastguard Worker Input[2] = cmsEvalToneCurve16(G[2], FROM_8_TO_16(i));
914*3ac0a46fSAndroid Build Coastguard Worker }
915*3ac0a46fSAndroid Build Coastguard Worker else {
916*3ac0a46fSAndroid Build Coastguard Worker Input[0] = FROM_8_TO_16(i);
917*3ac0a46fSAndroid Build Coastguard Worker Input[1] = FROM_8_TO_16(i);
918*3ac0a46fSAndroid Build Coastguard Worker Input[2] = FROM_8_TO_16(i);
919*3ac0a46fSAndroid Build Coastguard Worker }
920*3ac0a46fSAndroid Build Coastguard Worker
921*3ac0a46fSAndroid Build Coastguard Worker
922*3ac0a46fSAndroid Build Coastguard Worker // Move to 0..1.0 in fixed domain
923*3ac0a46fSAndroid Build Coastguard Worker v1 = _cmsToFixedDomain((int) (Input[0] * p -> Domain[0]));
924*3ac0a46fSAndroid Build Coastguard Worker v2 = _cmsToFixedDomain((int) (Input[1] * p -> Domain[1]));
925*3ac0a46fSAndroid Build Coastguard Worker v3 = _cmsToFixedDomain((int) (Input[2] * p -> Domain[2]));
926*3ac0a46fSAndroid Build Coastguard Worker
927*3ac0a46fSAndroid Build Coastguard Worker // Store the precalculated table of nodes
928*3ac0a46fSAndroid Build Coastguard Worker p8 ->X0[i] = (p->opta[2] * FIXED_TO_INT(v1));
929*3ac0a46fSAndroid Build Coastguard Worker p8 ->Y0[i] = (p->opta[1] * FIXED_TO_INT(v2));
930*3ac0a46fSAndroid Build Coastguard Worker p8 ->Z0[i] = (p->opta[0] * FIXED_TO_INT(v3));
931*3ac0a46fSAndroid Build Coastguard Worker
932*3ac0a46fSAndroid Build Coastguard Worker // Store the precalculated table of offsets
933*3ac0a46fSAndroid Build Coastguard Worker p8 ->rx[i] = (cmsUInt16Number) FIXED_REST_TO_INT(v1);
934*3ac0a46fSAndroid Build Coastguard Worker p8 ->ry[i] = (cmsUInt16Number) FIXED_REST_TO_INT(v2);
935*3ac0a46fSAndroid Build Coastguard Worker p8 ->rz[i] = (cmsUInt16Number) FIXED_REST_TO_INT(v3);
936*3ac0a46fSAndroid Build Coastguard Worker }
937*3ac0a46fSAndroid Build Coastguard Worker
938*3ac0a46fSAndroid Build Coastguard Worker p8 ->ContextID = ContextID;
939*3ac0a46fSAndroid Build Coastguard Worker p8 ->p = p;
940*3ac0a46fSAndroid Build Coastguard Worker
941*3ac0a46fSAndroid Build Coastguard Worker return p8;
942*3ac0a46fSAndroid Build Coastguard Worker }
943*3ac0a46fSAndroid Build Coastguard Worker
944*3ac0a46fSAndroid Build Coastguard Worker static
Prelin8free(cmsContext ContextID,void * ptr)945*3ac0a46fSAndroid Build Coastguard Worker void Prelin8free(cmsContext ContextID, void* ptr)
946*3ac0a46fSAndroid Build Coastguard Worker {
947*3ac0a46fSAndroid Build Coastguard Worker _cmsFree(ContextID, ptr);
948*3ac0a46fSAndroid Build Coastguard Worker }
949*3ac0a46fSAndroid Build Coastguard Worker
950*3ac0a46fSAndroid Build Coastguard Worker static
Prelin8dup(cmsContext ContextID,const void * ptr)951*3ac0a46fSAndroid Build Coastguard Worker void* Prelin8dup(cmsContext ContextID, const void* ptr)
952*3ac0a46fSAndroid Build Coastguard Worker {
953*3ac0a46fSAndroid Build Coastguard Worker return _cmsDupMem(ContextID, ptr, sizeof(Prelin8Data));
954*3ac0a46fSAndroid Build Coastguard Worker }
955*3ac0a46fSAndroid Build Coastguard Worker
956*3ac0a46fSAndroid Build Coastguard Worker
957*3ac0a46fSAndroid Build Coastguard Worker
958*3ac0a46fSAndroid Build Coastguard Worker // A optimized interpolation for 8-bit input.
959*3ac0a46fSAndroid Build Coastguard Worker #define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan])
960*3ac0a46fSAndroid Build Coastguard Worker static CMS_NO_SANITIZE
PrelinEval8(CMSREGISTER const cmsUInt16Number Input[],CMSREGISTER cmsUInt16Number Output[],CMSREGISTER const void * D)961*3ac0a46fSAndroid Build Coastguard Worker void PrelinEval8(CMSREGISTER const cmsUInt16Number Input[],
962*3ac0a46fSAndroid Build Coastguard Worker CMSREGISTER cmsUInt16Number Output[],
963*3ac0a46fSAndroid Build Coastguard Worker CMSREGISTER const void* D)
964*3ac0a46fSAndroid Build Coastguard Worker {
965*3ac0a46fSAndroid Build Coastguard Worker
966*3ac0a46fSAndroid Build Coastguard Worker cmsUInt8Number r, g, b;
967*3ac0a46fSAndroid Build Coastguard Worker cmsS15Fixed16Number rx, ry, rz;
968*3ac0a46fSAndroid Build Coastguard Worker cmsS15Fixed16Number c0, c1, c2, c3, Rest;
969*3ac0a46fSAndroid Build Coastguard Worker int OutChan;
970*3ac0a46fSAndroid Build Coastguard Worker CMSREGISTER cmsS15Fixed16Number X0, X1, Y0, Y1, Z0, Z1;
971*3ac0a46fSAndroid Build Coastguard Worker Prelin8Data* p8 = (Prelin8Data*) D;
972*3ac0a46fSAndroid Build Coastguard Worker CMSREGISTER const cmsInterpParams* p = p8 ->p;
973*3ac0a46fSAndroid Build Coastguard Worker int TotalOut = (int) p -> nOutputs;
974*3ac0a46fSAndroid Build Coastguard Worker const cmsUInt16Number* LutTable = (const cmsUInt16Number*) p->Table;
975*3ac0a46fSAndroid Build Coastguard Worker
976*3ac0a46fSAndroid Build Coastguard Worker r = (cmsUInt8Number) (Input[0] >> 8);
977*3ac0a46fSAndroid Build Coastguard Worker g = (cmsUInt8Number) (Input[1] >> 8);
978*3ac0a46fSAndroid Build Coastguard Worker b = (cmsUInt8Number) (Input[2] >> 8);
979*3ac0a46fSAndroid Build Coastguard Worker
980*3ac0a46fSAndroid Build Coastguard Worker X0 = (cmsS15Fixed16Number) p8->X0[r];
981*3ac0a46fSAndroid Build Coastguard Worker Y0 = (cmsS15Fixed16Number) p8->Y0[g];
982*3ac0a46fSAndroid Build Coastguard Worker Z0 = (cmsS15Fixed16Number) p8->Z0[b];
983*3ac0a46fSAndroid Build Coastguard Worker
984*3ac0a46fSAndroid Build Coastguard Worker rx = p8 ->rx[r];
985*3ac0a46fSAndroid Build Coastguard Worker ry = p8 ->ry[g];
986*3ac0a46fSAndroid Build Coastguard Worker rz = p8 ->rz[b];
987*3ac0a46fSAndroid Build Coastguard Worker
988*3ac0a46fSAndroid Build Coastguard Worker X1 = X0 + (cmsS15Fixed16Number)((rx == 0) ? 0 : p ->opta[2]);
989*3ac0a46fSAndroid Build Coastguard Worker Y1 = Y0 + (cmsS15Fixed16Number)((ry == 0) ? 0 : p ->opta[1]);
990*3ac0a46fSAndroid Build Coastguard Worker Z1 = Z0 + (cmsS15Fixed16Number)((rz == 0) ? 0 : p ->opta[0]);
991*3ac0a46fSAndroid Build Coastguard Worker
992*3ac0a46fSAndroid Build Coastguard Worker
993*3ac0a46fSAndroid Build Coastguard Worker // These are the 6 Tetrahedral
994*3ac0a46fSAndroid Build Coastguard Worker for (OutChan=0; OutChan < TotalOut; OutChan++) {
995*3ac0a46fSAndroid Build Coastguard Worker
996*3ac0a46fSAndroid Build Coastguard Worker c0 = DENS(X0, Y0, Z0);
997*3ac0a46fSAndroid Build Coastguard Worker
998*3ac0a46fSAndroid Build Coastguard Worker if (rx >= ry && ry >= rz)
999*3ac0a46fSAndroid Build Coastguard Worker {
1000*3ac0a46fSAndroid Build Coastguard Worker c1 = DENS(X1, Y0, Z0) - c0;
1001*3ac0a46fSAndroid Build Coastguard Worker c2 = DENS(X1, Y1, Z0) - DENS(X1, Y0, Z0);
1002*3ac0a46fSAndroid Build Coastguard Worker c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0);
1003*3ac0a46fSAndroid Build Coastguard Worker }
1004*3ac0a46fSAndroid Build Coastguard Worker else
1005*3ac0a46fSAndroid Build Coastguard Worker if (rx >= rz && rz >= ry)
1006*3ac0a46fSAndroid Build Coastguard Worker {
1007*3ac0a46fSAndroid Build Coastguard Worker c1 = DENS(X1, Y0, Z0) - c0;
1008*3ac0a46fSAndroid Build Coastguard Worker c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1);
1009*3ac0a46fSAndroid Build Coastguard Worker c3 = DENS(X1, Y0, Z1) - DENS(X1, Y0, Z0);
1010*3ac0a46fSAndroid Build Coastguard Worker }
1011*3ac0a46fSAndroid Build Coastguard Worker else
1012*3ac0a46fSAndroid Build Coastguard Worker if (rz >= rx && rx >= ry)
1013*3ac0a46fSAndroid Build Coastguard Worker {
1014*3ac0a46fSAndroid Build Coastguard Worker c1 = DENS(X1, Y0, Z1) - DENS(X0, Y0, Z1);
1015*3ac0a46fSAndroid Build Coastguard Worker c2 = DENS(X1, Y1, Z1) - DENS(X1, Y0, Z1);
1016*3ac0a46fSAndroid Build Coastguard Worker c3 = DENS(X0, Y0, Z1) - c0;
1017*3ac0a46fSAndroid Build Coastguard Worker }
1018*3ac0a46fSAndroid Build Coastguard Worker else
1019*3ac0a46fSAndroid Build Coastguard Worker if (ry >= rx && rx >= rz)
1020*3ac0a46fSAndroid Build Coastguard Worker {
1021*3ac0a46fSAndroid Build Coastguard Worker c1 = DENS(X1, Y1, Z0) - DENS(X0, Y1, Z0);
1022*3ac0a46fSAndroid Build Coastguard Worker c2 = DENS(X0, Y1, Z0) - c0;
1023*3ac0a46fSAndroid Build Coastguard Worker c3 = DENS(X1, Y1, Z1) - DENS(X1, Y1, Z0);
1024*3ac0a46fSAndroid Build Coastguard Worker }
1025*3ac0a46fSAndroid Build Coastguard Worker else
1026*3ac0a46fSAndroid Build Coastguard Worker if (ry >= rz && rz >= rx)
1027*3ac0a46fSAndroid Build Coastguard Worker {
1028*3ac0a46fSAndroid Build Coastguard Worker c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1);
1029*3ac0a46fSAndroid Build Coastguard Worker c2 = DENS(X0, Y1, Z0) - c0;
1030*3ac0a46fSAndroid Build Coastguard Worker c3 = DENS(X0, Y1, Z1) - DENS(X0, Y1, Z0);
1031*3ac0a46fSAndroid Build Coastguard Worker }
1032*3ac0a46fSAndroid Build Coastguard Worker else
1033*3ac0a46fSAndroid Build Coastguard Worker if (rz >= ry && ry >= rx)
1034*3ac0a46fSAndroid Build Coastguard Worker {
1035*3ac0a46fSAndroid Build Coastguard Worker c1 = DENS(X1, Y1, Z1) - DENS(X0, Y1, Z1);
1036*3ac0a46fSAndroid Build Coastguard Worker c2 = DENS(X0, Y1, Z1) - DENS(X0, Y0, Z1);
1037*3ac0a46fSAndroid Build Coastguard Worker c3 = DENS(X0, Y0, Z1) - c0;
1038*3ac0a46fSAndroid Build Coastguard Worker }
1039*3ac0a46fSAndroid Build Coastguard Worker else {
1040*3ac0a46fSAndroid Build Coastguard Worker c1 = c2 = c3 = 0;
1041*3ac0a46fSAndroid Build Coastguard Worker }
1042*3ac0a46fSAndroid Build Coastguard Worker
1043*3ac0a46fSAndroid Build Coastguard Worker Rest = c1 * rx + c2 * ry + c3 * rz + 0x8001;
1044*3ac0a46fSAndroid Build Coastguard Worker Output[OutChan] = (cmsUInt16Number) (c0 + ((Rest + (Rest >> 16)) >> 16));
1045*3ac0a46fSAndroid Build Coastguard Worker
1046*3ac0a46fSAndroid Build Coastguard Worker }
1047*3ac0a46fSAndroid Build Coastguard Worker }
1048*3ac0a46fSAndroid Build Coastguard Worker
1049*3ac0a46fSAndroid Build Coastguard Worker #undef DENS
1050*3ac0a46fSAndroid Build Coastguard Worker
1051*3ac0a46fSAndroid Build Coastguard Worker
1052*3ac0a46fSAndroid Build Coastguard Worker // Curves that contain wide empty areas are not optimizeable
1053*3ac0a46fSAndroid Build Coastguard Worker static
IsDegenerated(const cmsToneCurve * g)1054*3ac0a46fSAndroid Build Coastguard Worker cmsBool IsDegenerated(const cmsToneCurve* g)
1055*3ac0a46fSAndroid Build Coastguard Worker {
1056*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number i, Zeros = 0, Poles = 0;
1057*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number nEntries = g ->nEntries;
1058*3ac0a46fSAndroid Build Coastguard Worker
1059*3ac0a46fSAndroid Build Coastguard Worker for (i=0; i < nEntries; i++) {
1060*3ac0a46fSAndroid Build Coastguard Worker
1061*3ac0a46fSAndroid Build Coastguard Worker if (g ->Table16[i] == 0x0000) Zeros++;
1062*3ac0a46fSAndroid Build Coastguard Worker if (g ->Table16[i] == 0xffff) Poles++;
1063*3ac0a46fSAndroid Build Coastguard Worker }
1064*3ac0a46fSAndroid Build Coastguard Worker
1065*3ac0a46fSAndroid Build Coastguard Worker if (Zeros == 1 && Poles == 1) return FALSE; // For linear tables
1066*3ac0a46fSAndroid Build Coastguard Worker if (Zeros > (nEntries / 20)) return TRUE; // Degenerated, many zeros
1067*3ac0a46fSAndroid Build Coastguard Worker if (Poles > (nEntries / 20)) return TRUE; // Degenerated, many poles
1068*3ac0a46fSAndroid Build Coastguard Worker
1069*3ac0a46fSAndroid Build Coastguard Worker return FALSE;
1070*3ac0a46fSAndroid Build Coastguard Worker }
1071*3ac0a46fSAndroid Build Coastguard Worker
1072*3ac0a46fSAndroid Build Coastguard Worker // --------------------------------------------------------------------------------------------------------------
1073*3ac0a46fSAndroid Build Coastguard Worker // We need xput over here
1074*3ac0a46fSAndroid Build Coastguard Worker
1075*3ac0a46fSAndroid Build Coastguard Worker static
OptimizeByComputingLinearization(cmsPipeline ** Lut,cmsUInt32Number Intent,cmsUInt32Number * InputFormat,cmsUInt32Number * OutputFormat,cmsUInt32Number * dwFlags)1076*3ac0a46fSAndroid Build Coastguard Worker cmsBool OptimizeByComputingLinearization(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags)
1077*3ac0a46fSAndroid Build Coastguard Worker {
1078*3ac0a46fSAndroid Build Coastguard Worker cmsPipeline* OriginalLut;
1079*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number nGridPoints;
1080*3ac0a46fSAndroid Build Coastguard Worker cmsToneCurve *Trans[cmsMAXCHANNELS], *TransReverse[cmsMAXCHANNELS];
1081*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number t, i;
1082*3ac0a46fSAndroid Build Coastguard Worker cmsFloat32Number v, In[cmsMAXCHANNELS], Out[cmsMAXCHANNELS];
1083*3ac0a46fSAndroid Build Coastguard Worker cmsBool lIsSuitable, lIsLinear;
1084*3ac0a46fSAndroid Build Coastguard Worker cmsPipeline* OptimizedLUT = NULL, *LutPlusCurves = NULL;
1085*3ac0a46fSAndroid Build Coastguard Worker cmsStage* OptimizedCLUTmpe;
1086*3ac0a46fSAndroid Build Coastguard Worker cmsColorSpaceSignature ColorSpace, OutputColorSpace;
1087*3ac0a46fSAndroid Build Coastguard Worker cmsStage* OptimizedPrelinMpe;
1088*3ac0a46fSAndroid Build Coastguard Worker cmsToneCurve** OptimizedPrelinCurves;
1089*3ac0a46fSAndroid Build Coastguard Worker _cmsStageCLutData* OptimizedPrelinCLUT;
1090*3ac0a46fSAndroid Build Coastguard Worker
1091*3ac0a46fSAndroid Build Coastguard Worker
1092*3ac0a46fSAndroid Build Coastguard Worker // This is a lossy optimization! does not apply in floating-point cases
1093*3ac0a46fSAndroid Build Coastguard Worker if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) return FALSE;
1094*3ac0a46fSAndroid Build Coastguard Worker
1095*3ac0a46fSAndroid Build Coastguard Worker // Only on chunky RGB
1096*3ac0a46fSAndroid Build Coastguard Worker if (T_COLORSPACE(*InputFormat) != PT_RGB) return FALSE;
1097*3ac0a46fSAndroid Build Coastguard Worker if (T_PLANAR(*InputFormat)) return FALSE;
1098*3ac0a46fSAndroid Build Coastguard Worker
1099*3ac0a46fSAndroid Build Coastguard Worker if (T_COLORSPACE(*OutputFormat) != PT_RGB) return FALSE;
1100*3ac0a46fSAndroid Build Coastguard Worker if (T_PLANAR(*OutputFormat)) return FALSE;
1101*3ac0a46fSAndroid Build Coastguard Worker
1102*3ac0a46fSAndroid Build Coastguard Worker // On 16 bits, user has to specify the feature
1103*3ac0a46fSAndroid Build Coastguard Worker if (!_cmsFormatterIs8bit(*InputFormat)) {
1104*3ac0a46fSAndroid Build Coastguard Worker if (!(*dwFlags & cmsFLAGS_CLUT_PRE_LINEARIZATION)) return FALSE;
1105*3ac0a46fSAndroid Build Coastguard Worker }
1106*3ac0a46fSAndroid Build Coastguard Worker
1107*3ac0a46fSAndroid Build Coastguard Worker OriginalLut = *Lut;
1108*3ac0a46fSAndroid Build Coastguard Worker
1109*3ac0a46fSAndroid Build Coastguard Worker ColorSpace = _cmsICCcolorSpace((int) T_COLORSPACE(*InputFormat));
1110*3ac0a46fSAndroid Build Coastguard Worker OutputColorSpace = _cmsICCcolorSpace((int) T_COLORSPACE(*OutputFormat));
1111*3ac0a46fSAndroid Build Coastguard Worker
1112*3ac0a46fSAndroid Build Coastguard Worker // Color space must be specified
1113*3ac0a46fSAndroid Build Coastguard Worker if (ColorSpace == (cmsColorSpaceSignature)0 ||
1114*3ac0a46fSAndroid Build Coastguard Worker OutputColorSpace == (cmsColorSpaceSignature)0) return FALSE;
1115*3ac0a46fSAndroid Build Coastguard Worker
1116*3ac0a46fSAndroid Build Coastguard Worker nGridPoints = _cmsReasonableGridpointsByColorspace(ColorSpace, *dwFlags);
1117*3ac0a46fSAndroid Build Coastguard Worker
1118*3ac0a46fSAndroid Build Coastguard Worker // Empty gamma containers
1119*3ac0a46fSAndroid Build Coastguard Worker memset(Trans, 0, sizeof(Trans));
1120*3ac0a46fSAndroid Build Coastguard Worker memset(TransReverse, 0, sizeof(TransReverse));
1121*3ac0a46fSAndroid Build Coastguard Worker
1122*3ac0a46fSAndroid Build Coastguard Worker // If the last stage of the original lut are curves, and those curves are
1123*3ac0a46fSAndroid Build Coastguard Worker // degenerated, it is likely the transform is squeezing and clipping
1124*3ac0a46fSAndroid Build Coastguard Worker // the output from previous CLUT. We cannot optimize this case
1125*3ac0a46fSAndroid Build Coastguard Worker {
1126*3ac0a46fSAndroid Build Coastguard Worker cmsStage* last = cmsPipelineGetPtrToLastStage(OriginalLut);
1127*3ac0a46fSAndroid Build Coastguard Worker
1128*3ac0a46fSAndroid Build Coastguard Worker if (last == NULL) goto Error;
1129*3ac0a46fSAndroid Build Coastguard Worker if (cmsStageType(last) == cmsSigCurveSetElemType) {
1130*3ac0a46fSAndroid Build Coastguard Worker
1131*3ac0a46fSAndroid Build Coastguard Worker _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*)cmsStageData(last);
1132*3ac0a46fSAndroid Build Coastguard Worker for (i = 0; i < Data->nCurves; i++) {
1133*3ac0a46fSAndroid Build Coastguard Worker if (IsDegenerated(Data->TheCurves[i]))
1134*3ac0a46fSAndroid Build Coastguard Worker goto Error;
1135*3ac0a46fSAndroid Build Coastguard Worker }
1136*3ac0a46fSAndroid Build Coastguard Worker }
1137*3ac0a46fSAndroid Build Coastguard Worker }
1138*3ac0a46fSAndroid Build Coastguard Worker
1139*3ac0a46fSAndroid Build Coastguard Worker for (t = 0; t < OriginalLut ->InputChannels; t++) {
1140*3ac0a46fSAndroid Build Coastguard Worker Trans[t] = cmsBuildTabulatedToneCurve16(OriginalLut ->ContextID, PRELINEARIZATION_POINTS, NULL);
1141*3ac0a46fSAndroid Build Coastguard Worker if (Trans[t] == NULL) goto Error;
1142*3ac0a46fSAndroid Build Coastguard Worker }
1143*3ac0a46fSAndroid Build Coastguard Worker
1144*3ac0a46fSAndroid Build Coastguard Worker // Populate the curves
1145*3ac0a46fSAndroid Build Coastguard Worker for (i=0; i < PRELINEARIZATION_POINTS; i++) {
1146*3ac0a46fSAndroid Build Coastguard Worker
1147*3ac0a46fSAndroid Build Coastguard Worker v = (cmsFloat32Number) ((cmsFloat64Number) i / (PRELINEARIZATION_POINTS - 1));
1148*3ac0a46fSAndroid Build Coastguard Worker
1149*3ac0a46fSAndroid Build Coastguard Worker // Feed input with a gray ramp
1150*3ac0a46fSAndroid Build Coastguard Worker for (t=0; t < OriginalLut ->InputChannels; t++)
1151*3ac0a46fSAndroid Build Coastguard Worker In[t] = v;
1152*3ac0a46fSAndroid Build Coastguard Worker
1153*3ac0a46fSAndroid Build Coastguard Worker // Evaluate the gray value
1154*3ac0a46fSAndroid Build Coastguard Worker cmsPipelineEvalFloat(In, Out, OriginalLut);
1155*3ac0a46fSAndroid Build Coastguard Worker
1156*3ac0a46fSAndroid Build Coastguard Worker // Store result in curve
1157*3ac0a46fSAndroid Build Coastguard Worker for (t=0; t < OriginalLut ->InputChannels; t++)
1158*3ac0a46fSAndroid Build Coastguard Worker Trans[t] ->Table16[i] = _cmsQuickSaturateWord(Out[t] * 65535.0);
1159*3ac0a46fSAndroid Build Coastguard Worker }
1160*3ac0a46fSAndroid Build Coastguard Worker
1161*3ac0a46fSAndroid Build Coastguard Worker // Slope-limit the obtained curves
1162*3ac0a46fSAndroid Build Coastguard Worker for (t = 0; t < OriginalLut ->InputChannels; t++)
1163*3ac0a46fSAndroid Build Coastguard Worker SlopeLimiting(Trans[t]);
1164*3ac0a46fSAndroid Build Coastguard Worker
1165*3ac0a46fSAndroid Build Coastguard Worker // Check for validity
1166*3ac0a46fSAndroid Build Coastguard Worker lIsSuitable = TRUE;
1167*3ac0a46fSAndroid Build Coastguard Worker lIsLinear = TRUE;
1168*3ac0a46fSAndroid Build Coastguard Worker for (t=0; (lIsSuitable && (t < OriginalLut ->InputChannels)); t++) {
1169*3ac0a46fSAndroid Build Coastguard Worker
1170*3ac0a46fSAndroid Build Coastguard Worker // Exclude if already linear
1171*3ac0a46fSAndroid Build Coastguard Worker if (!cmsIsToneCurveLinear(Trans[t]))
1172*3ac0a46fSAndroid Build Coastguard Worker lIsLinear = FALSE;
1173*3ac0a46fSAndroid Build Coastguard Worker
1174*3ac0a46fSAndroid Build Coastguard Worker // Exclude if non-monotonic
1175*3ac0a46fSAndroid Build Coastguard Worker if (!cmsIsToneCurveMonotonic(Trans[t]))
1176*3ac0a46fSAndroid Build Coastguard Worker lIsSuitable = FALSE;
1177*3ac0a46fSAndroid Build Coastguard Worker
1178*3ac0a46fSAndroid Build Coastguard Worker if (IsDegenerated(Trans[t]))
1179*3ac0a46fSAndroid Build Coastguard Worker lIsSuitable = FALSE;
1180*3ac0a46fSAndroid Build Coastguard Worker }
1181*3ac0a46fSAndroid Build Coastguard Worker
1182*3ac0a46fSAndroid Build Coastguard Worker // If it is not suitable, just quit
1183*3ac0a46fSAndroid Build Coastguard Worker if (!lIsSuitable) goto Error;
1184*3ac0a46fSAndroid Build Coastguard Worker
1185*3ac0a46fSAndroid Build Coastguard Worker // Invert curves if possible
1186*3ac0a46fSAndroid Build Coastguard Worker for (t = 0; t < OriginalLut ->InputChannels; t++) {
1187*3ac0a46fSAndroid Build Coastguard Worker TransReverse[t] = cmsReverseToneCurveEx(PRELINEARIZATION_POINTS, Trans[t]);
1188*3ac0a46fSAndroid Build Coastguard Worker if (TransReverse[t] == NULL) goto Error;
1189*3ac0a46fSAndroid Build Coastguard Worker }
1190*3ac0a46fSAndroid Build Coastguard Worker
1191*3ac0a46fSAndroid Build Coastguard Worker // Now inset the reversed curves at the begin of transform
1192*3ac0a46fSAndroid Build Coastguard Worker LutPlusCurves = cmsPipelineDup(OriginalLut);
1193*3ac0a46fSAndroid Build Coastguard Worker if (LutPlusCurves == NULL) goto Error;
1194*3ac0a46fSAndroid Build Coastguard Worker
1195*3ac0a46fSAndroid Build Coastguard Worker if (!cmsPipelineInsertStage(LutPlusCurves, cmsAT_BEGIN, cmsStageAllocToneCurves(OriginalLut ->ContextID, OriginalLut ->InputChannels, TransReverse)))
1196*3ac0a46fSAndroid Build Coastguard Worker goto Error;
1197*3ac0a46fSAndroid Build Coastguard Worker
1198*3ac0a46fSAndroid Build Coastguard Worker // Create the result LUT
1199*3ac0a46fSAndroid Build Coastguard Worker OptimizedLUT = cmsPipelineAlloc(OriginalLut ->ContextID, OriginalLut ->InputChannels, OriginalLut ->OutputChannels);
1200*3ac0a46fSAndroid Build Coastguard Worker if (OptimizedLUT == NULL) goto Error;
1201*3ac0a46fSAndroid Build Coastguard Worker
1202*3ac0a46fSAndroid Build Coastguard Worker OptimizedPrelinMpe = cmsStageAllocToneCurves(OriginalLut ->ContextID, OriginalLut ->InputChannels, Trans);
1203*3ac0a46fSAndroid Build Coastguard Worker
1204*3ac0a46fSAndroid Build Coastguard Worker // Create and insert the curves at the beginning
1205*3ac0a46fSAndroid Build Coastguard Worker if (!cmsPipelineInsertStage(OptimizedLUT, cmsAT_BEGIN, OptimizedPrelinMpe))
1206*3ac0a46fSAndroid Build Coastguard Worker goto Error;
1207*3ac0a46fSAndroid Build Coastguard Worker
1208*3ac0a46fSAndroid Build Coastguard Worker // Allocate the CLUT for result
1209*3ac0a46fSAndroid Build Coastguard Worker OptimizedCLUTmpe = cmsStageAllocCLut16bit(OriginalLut ->ContextID, nGridPoints, OriginalLut ->InputChannels, OriginalLut ->OutputChannels, NULL);
1210*3ac0a46fSAndroid Build Coastguard Worker
1211*3ac0a46fSAndroid Build Coastguard Worker // Add the CLUT to the destination LUT
1212*3ac0a46fSAndroid Build Coastguard Worker if (!cmsPipelineInsertStage(OptimizedLUT, cmsAT_END, OptimizedCLUTmpe))
1213*3ac0a46fSAndroid Build Coastguard Worker goto Error;
1214*3ac0a46fSAndroid Build Coastguard Worker
1215*3ac0a46fSAndroid Build Coastguard Worker // Resample the LUT
1216*3ac0a46fSAndroid Build Coastguard Worker if (!cmsStageSampleCLut16bit(OptimizedCLUTmpe, XFormSampler16, (void*) LutPlusCurves, 0)) goto Error;
1217*3ac0a46fSAndroid Build Coastguard Worker
1218*3ac0a46fSAndroid Build Coastguard Worker // Free resources
1219*3ac0a46fSAndroid Build Coastguard Worker for (t = 0; t < OriginalLut ->InputChannels; t++) {
1220*3ac0a46fSAndroid Build Coastguard Worker
1221*3ac0a46fSAndroid Build Coastguard Worker if (Trans[t]) cmsFreeToneCurve(Trans[t]);
1222*3ac0a46fSAndroid Build Coastguard Worker if (TransReverse[t]) cmsFreeToneCurve(TransReverse[t]);
1223*3ac0a46fSAndroid Build Coastguard Worker }
1224*3ac0a46fSAndroid Build Coastguard Worker
1225*3ac0a46fSAndroid Build Coastguard Worker cmsPipelineFree(LutPlusCurves);
1226*3ac0a46fSAndroid Build Coastguard Worker
1227*3ac0a46fSAndroid Build Coastguard Worker
1228*3ac0a46fSAndroid Build Coastguard Worker OptimizedPrelinCurves = _cmsStageGetPtrToCurveSet(OptimizedPrelinMpe);
1229*3ac0a46fSAndroid Build Coastguard Worker OptimizedPrelinCLUT = (_cmsStageCLutData*) OptimizedCLUTmpe ->Data;
1230*3ac0a46fSAndroid Build Coastguard Worker
1231*3ac0a46fSAndroid Build Coastguard Worker // Set the evaluator if 8-bit
1232*3ac0a46fSAndroid Build Coastguard Worker if (_cmsFormatterIs8bit(*InputFormat)) {
1233*3ac0a46fSAndroid Build Coastguard Worker
1234*3ac0a46fSAndroid Build Coastguard Worker Prelin8Data* p8 = PrelinOpt8alloc(OptimizedLUT ->ContextID,
1235*3ac0a46fSAndroid Build Coastguard Worker OptimizedPrelinCLUT ->Params,
1236*3ac0a46fSAndroid Build Coastguard Worker OptimizedPrelinCurves);
1237*3ac0a46fSAndroid Build Coastguard Worker if (p8 == NULL) return FALSE;
1238*3ac0a46fSAndroid Build Coastguard Worker
1239*3ac0a46fSAndroid Build Coastguard Worker _cmsPipelineSetOptimizationParameters(OptimizedLUT, PrelinEval8, (void*) p8, Prelin8free, Prelin8dup);
1240*3ac0a46fSAndroid Build Coastguard Worker
1241*3ac0a46fSAndroid Build Coastguard Worker }
1242*3ac0a46fSAndroid Build Coastguard Worker else
1243*3ac0a46fSAndroid Build Coastguard Worker {
1244*3ac0a46fSAndroid Build Coastguard Worker Prelin16Data* p16 = PrelinOpt16alloc(OptimizedLUT ->ContextID,
1245*3ac0a46fSAndroid Build Coastguard Worker OptimizedPrelinCLUT ->Params,
1246*3ac0a46fSAndroid Build Coastguard Worker 3, OptimizedPrelinCurves, 3, NULL);
1247*3ac0a46fSAndroid Build Coastguard Worker if (p16 == NULL) return FALSE;
1248*3ac0a46fSAndroid Build Coastguard Worker
1249*3ac0a46fSAndroid Build Coastguard Worker _cmsPipelineSetOptimizationParameters(OptimizedLUT, PrelinEval16, (void*) p16, PrelinOpt16free, Prelin16dup);
1250*3ac0a46fSAndroid Build Coastguard Worker
1251*3ac0a46fSAndroid Build Coastguard Worker }
1252*3ac0a46fSAndroid Build Coastguard Worker
1253*3ac0a46fSAndroid Build Coastguard Worker // Don't fix white on absolute colorimetric
1254*3ac0a46fSAndroid Build Coastguard Worker if (Intent == INTENT_ABSOLUTE_COLORIMETRIC)
1255*3ac0a46fSAndroid Build Coastguard Worker *dwFlags |= cmsFLAGS_NOWHITEONWHITEFIXUP;
1256*3ac0a46fSAndroid Build Coastguard Worker
1257*3ac0a46fSAndroid Build Coastguard Worker if (!(*dwFlags & cmsFLAGS_NOWHITEONWHITEFIXUP)) {
1258*3ac0a46fSAndroid Build Coastguard Worker
1259*3ac0a46fSAndroid Build Coastguard Worker if (!FixWhiteMisalignment(OptimizedLUT, ColorSpace, OutputColorSpace)) {
1260*3ac0a46fSAndroid Build Coastguard Worker
1261*3ac0a46fSAndroid Build Coastguard Worker return FALSE;
1262*3ac0a46fSAndroid Build Coastguard Worker }
1263*3ac0a46fSAndroid Build Coastguard Worker }
1264*3ac0a46fSAndroid Build Coastguard Worker
1265*3ac0a46fSAndroid Build Coastguard Worker // And return the obtained LUT
1266*3ac0a46fSAndroid Build Coastguard Worker
1267*3ac0a46fSAndroid Build Coastguard Worker cmsPipelineFree(OriginalLut);
1268*3ac0a46fSAndroid Build Coastguard Worker *Lut = OptimizedLUT;
1269*3ac0a46fSAndroid Build Coastguard Worker return TRUE;
1270*3ac0a46fSAndroid Build Coastguard Worker
1271*3ac0a46fSAndroid Build Coastguard Worker Error:
1272*3ac0a46fSAndroid Build Coastguard Worker
1273*3ac0a46fSAndroid Build Coastguard Worker for (t = 0; t < OriginalLut ->InputChannels; t++) {
1274*3ac0a46fSAndroid Build Coastguard Worker
1275*3ac0a46fSAndroid Build Coastguard Worker if (Trans[t]) cmsFreeToneCurve(Trans[t]);
1276*3ac0a46fSAndroid Build Coastguard Worker if (TransReverse[t]) cmsFreeToneCurve(TransReverse[t]);
1277*3ac0a46fSAndroid Build Coastguard Worker }
1278*3ac0a46fSAndroid Build Coastguard Worker
1279*3ac0a46fSAndroid Build Coastguard Worker if (LutPlusCurves != NULL) cmsPipelineFree(LutPlusCurves);
1280*3ac0a46fSAndroid Build Coastguard Worker if (OptimizedLUT != NULL) cmsPipelineFree(OptimizedLUT);
1281*3ac0a46fSAndroid Build Coastguard Worker
1282*3ac0a46fSAndroid Build Coastguard Worker return FALSE;
1283*3ac0a46fSAndroid Build Coastguard Worker
1284*3ac0a46fSAndroid Build Coastguard Worker cmsUNUSED_PARAMETER(Intent);
1285*3ac0a46fSAndroid Build Coastguard Worker cmsUNUSED_PARAMETER(lIsLinear);
1286*3ac0a46fSAndroid Build Coastguard Worker }
1287*3ac0a46fSAndroid Build Coastguard Worker
1288*3ac0a46fSAndroid Build Coastguard Worker
1289*3ac0a46fSAndroid Build Coastguard Worker // Curves optimizer ------------------------------------------------------------------------------------------------------------------
1290*3ac0a46fSAndroid Build Coastguard Worker
1291*3ac0a46fSAndroid Build Coastguard Worker static
CurvesFree(cmsContext ContextID,void * ptr)1292*3ac0a46fSAndroid Build Coastguard Worker void CurvesFree(cmsContext ContextID, void* ptr)
1293*3ac0a46fSAndroid Build Coastguard Worker {
1294*3ac0a46fSAndroid Build Coastguard Worker Curves16Data* Data = (Curves16Data*) ptr;
1295*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number i;
1296*3ac0a46fSAndroid Build Coastguard Worker
1297*3ac0a46fSAndroid Build Coastguard Worker for (i=0; i < Data -> nCurves; i++) {
1298*3ac0a46fSAndroid Build Coastguard Worker
1299*3ac0a46fSAndroid Build Coastguard Worker _cmsFree(ContextID, Data ->Curves[i]);
1300*3ac0a46fSAndroid Build Coastguard Worker }
1301*3ac0a46fSAndroid Build Coastguard Worker
1302*3ac0a46fSAndroid Build Coastguard Worker _cmsFree(ContextID, Data ->Curves);
1303*3ac0a46fSAndroid Build Coastguard Worker _cmsFree(ContextID, ptr);
1304*3ac0a46fSAndroid Build Coastguard Worker }
1305*3ac0a46fSAndroid Build Coastguard Worker
1306*3ac0a46fSAndroid Build Coastguard Worker static
CurvesDup(cmsContext ContextID,const void * ptr)1307*3ac0a46fSAndroid Build Coastguard Worker void* CurvesDup(cmsContext ContextID, const void* ptr)
1308*3ac0a46fSAndroid Build Coastguard Worker {
1309*3ac0a46fSAndroid Build Coastguard Worker Curves16Data* Data = (Curves16Data*)_cmsDupMem(ContextID, ptr, sizeof(Curves16Data));
1310*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number i;
1311*3ac0a46fSAndroid Build Coastguard Worker
1312*3ac0a46fSAndroid Build Coastguard Worker if (Data == NULL) return NULL;
1313*3ac0a46fSAndroid Build Coastguard Worker
1314*3ac0a46fSAndroid Build Coastguard Worker Data->Curves = (cmsUInt16Number**) _cmsDupMem(ContextID, Data->Curves, Data->nCurves * sizeof(cmsUInt16Number*));
1315*3ac0a46fSAndroid Build Coastguard Worker
1316*3ac0a46fSAndroid Build Coastguard Worker for (i=0; i < Data -> nCurves; i++) {
1317*3ac0a46fSAndroid Build Coastguard Worker Data->Curves[i] = (cmsUInt16Number*) _cmsDupMem(ContextID, Data->Curves[i], Data->nElements * sizeof(cmsUInt16Number));
1318*3ac0a46fSAndroid Build Coastguard Worker }
1319*3ac0a46fSAndroid Build Coastguard Worker
1320*3ac0a46fSAndroid Build Coastguard Worker return (void*) Data;
1321*3ac0a46fSAndroid Build Coastguard Worker }
1322*3ac0a46fSAndroid Build Coastguard Worker
1323*3ac0a46fSAndroid Build Coastguard Worker // Precomputes tables for 8-bit on input devicelink.
1324*3ac0a46fSAndroid Build Coastguard Worker static
CurvesAlloc(cmsContext ContextID,cmsUInt32Number nCurves,cmsUInt32Number nElements,cmsToneCurve ** G)1325*3ac0a46fSAndroid Build Coastguard Worker Curves16Data* CurvesAlloc(cmsContext ContextID, cmsUInt32Number nCurves, cmsUInt32Number nElements, cmsToneCurve** G)
1326*3ac0a46fSAndroid Build Coastguard Worker {
1327*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number i, j;
1328*3ac0a46fSAndroid Build Coastguard Worker Curves16Data* c16;
1329*3ac0a46fSAndroid Build Coastguard Worker
1330*3ac0a46fSAndroid Build Coastguard Worker c16 = (Curves16Data*)_cmsMallocZero(ContextID, sizeof(Curves16Data));
1331*3ac0a46fSAndroid Build Coastguard Worker if (c16 == NULL) return NULL;
1332*3ac0a46fSAndroid Build Coastguard Worker
1333*3ac0a46fSAndroid Build Coastguard Worker c16 ->nCurves = nCurves;
1334*3ac0a46fSAndroid Build Coastguard Worker c16 ->nElements = nElements;
1335*3ac0a46fSAndroid Build Coastguard Worker
1336*3ac0a46fSAndroid Build Coastguard Worker c16->Curves = (cmsUInt16Number**) _cmsCalloc(ContextID, nCurves, sizeof(cmsUInt16Number*));
1337*3ac0a46fSAndroid Build Coastguard Worker if (c16->Curves == NULL) {
1338*3ac0a46fSAndroid Build Coastguard Worker _cmsFree(ContextID, c16);
1339*3ac0a46fSAndroid Build Coastguard Worker return NULL;
1340*3ac0a46fSAndroid Build Coastguard Worker }
1341*3ac0a46fSAndroid Build Coastguard Worker
1342*3ac0a46fSAndroid Build Coastguard Worker for (i=0; i < nCurves; i++) {
1343*3ac0a46fSAndroid Build Coastguard Worker
1344*3ac0a46fSAndroid Build Coastguard Worker c16->Curves[i] = (cmsUInt16Number*) _cmsCalloc(ContextID, nElements, sizeof(cmsUInt16Number));
1345*3ac0a46fSAndroid Build Coastguard Worker
1346*3ac0a46fSAndroid Build Coastguard Worker if (c16->Curves[i] == NULL) {
1347*3ac0a46fSAndroid Build Coastguard Worker
1348*3ac0a46fSAndroid Build Coastguard Worker for (j=0; j < i; j++) {
1349*3ac0a46fSAndroid Build Coastguard Worker _cmsFree(ContextID, c16->Curves[j]);
1350*3ac0a46fSAndroid Build Coastguard Worker }
1351*3ac0a46fSAndroid Build Coastguard Worker _cmsFree(ContextID, c16->Curves);
1352*3ac0a46fSAndroid Build Coastguard Worker _cmsFree(ContextID, c16);
1353*3ac0a46fSAndroid Build Coastguard Worker return NULL;
1354*3ac0a46fSAndroid Build Coastguard Worker }
1355*3ac0a46fSAndroid Build Coastguard Worker
1356*3ac0a46fSAndroid Build Coastguard Worker if (nElements == 256U) {
1357*3ac0a46fSAndroid Build Coastguard Worker
1358*3ac0a46fSAndroid Build Coastguard Worker for (j=0; j < nElements; j++) {
1359*3ac0a46fSAndroid Build Coastguard Worker
1360*3ac0a46fSAndroid Build Coastguard Worker c16 ->Curves[i][j] = cmsEvalToneCurve16(G[i], FROM_8_TO_16(j));
1361*3ac0a46fSAndroid Build Coastguard Worker }
1362*3ac0a46fSAndroid Build Coastguard Worker }
1363*3ac0a46fSAndroid Build Coastguard Worker else {
1364*3ac0a46fSAndroid Build Coastguard Worker
1365*3ac0a46fSAndroid Build Coastguard Worker for (j=0; j < nElements; j++) {
1366*3ac0a46fSAndroid Build Coastguard Worker c16 ->Curves[i][j] = cmsEvalToneCurve16(G[i], (cmsUInt16Number) j);
1367*3ac0a46fSAndroid Build Coastguard Worker }
1368*3ac0a46fSAndroid Build Coastguard Worker }
1369*3ac0a46fSAndroid Build Coastguard Worker }
1370*3ac0a46fSAndroid Build Coastguard Worker
1371*3ac0a46fSAndroid Build Coastguard Worker return c16;
1372*3ac0a46fSAndroid Build Coastguard Worker }
1373*3ac0a46fSAndroid Build Coastguard Worker
1374*3ac0a46fSAndroid Build Coastguard Worker static
FastEvaluateCurves8(CMSREGISTER const cmsUInt16Number In[],CMSREGISTER cmsUInt16Number Out[],CMSREGISTER const void * D)1375*3ac0a46fSAndroid Build Coastguard Worker void FastEvaluateCurves8(CMSREGISTER const cmsUInt16Number In[],
1376*3ac0a46fSAndroid Build Coastguard Worker CMSREGISTER cmsUInt16Number Out[],
1377*3ac0a46fSAndroid Build Coastguard Worker CMSREGISTER const void* D)
1378*3ac0a46fSAndroid Build Coastguard Worker {
1379*3ac0a46fSAndroid Build Coastguard Worker Curves16Data* Data = (Curves16Data*) D;
1380*3ac0a46fSAndroid Build Coastguard Worker int x;
1381*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number i;
1382*3ac0a46fSAndroid Build Coastguard Worker
1383*3ac0a46fSAndroid Build Coastguard Worker for (i=0; i < Data ->nCurves; i++) {
1384*3ac0a46fSAndroid Build Coastguard Worker
1385*3ac0a46fSAndroid Build Coastguard Worker x = (In[i] >> 8);
1386*3ac0a46fSAndroid Build Coastguard Worker Out[i] = Data -> Curves[i][x];
1387*3ac0a46fSAndroid Build Coastguard Worker }
1388*3ac0a46fSAndroid Build Coastguard Worker }
1389*3ac0a46fSAndroid Build Coastguard Worker
1390*3ac0a46fSAndroid Build Coastguard Worker
1391*3ac0a46fSAndroid Build Coastguard Worker static
FastEvaluateCurves16(CMSREGISTER const cmsUInt16Number In[],CMSREGISTER cmsUInt16Number Out[],CMSREGISTER const void * D)1392*3ac0a46fSAndroid Build Coastguard Worker void FastEvaluateCurves16(CMSREGISTER const cmsUInt16Number In[],
1393*3ac0a46fSAndroid Build Coastguard Worker CMSREGISTER cmsUInt16Number Out[],
1394*3ac0a46fSAndroid Build Coastguard Worker CMSREGISTER const void* D)
1395*3ac0a46fSAndroid Build Coastguard Worker {
1396*3ac0a46fSAndroid Build Coastguard Worker Curves16Data* Data = (Curves16Data*) D;
1397*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number i;
1398*3ac0a46fSAndroid Build Coastguard Worker
1399*3ac0a46fSAndroid Build Coastguard Worker for (i=0; i < Data ->nCurves; i++) {
1400*3ac0a46fSAndroid Build Coastguard Worker Out[i] = Data -> Curves[i][In[i]];
1401*3ac0a46fSAndroid Build Coastguard Worker }
1402*3ac0a46fSAndroid Build Coastguard Worker }
1403*3ac0a46fSAndroid Build Coastguard Worker
1404*3ac0a46fSAndroid Build Coastguard Worker
1405*3ac0a46fSAndroid Build Coastguard Worker static
FastIdentity16(CMSREGISTER const cmsUInt16Number In[],CMSREGISTER cmsUInt16Number Out[],CMSREGISTER const void * D)1406*3ac0a46fSAndroid Build Coastguard Worker void FastIdentity16(CMSREGISTER const cmsUInt16Number In[],
1407*3ac0a46fSAndroid Build Coastguard Worker CMSREGISTER cmsUInt16Number Out[],
1408*3ac0a46fSAndroid Build Coastguard Worker CMSREGISTER const void* D)
1409*3ac0a46fSAndroid Build Coastguard Worker {
1410*3ac0a46fSAndroid Build Coastguard Worker cmsPipeline* Lut = (cmsPipeline*) D;
1411*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number i;
1412*3ac0a46fSAndroid Build Coastguard Worker
1413*3ac0a46fSAndroid Build Coastguard Worker for (i=0; i < Lut ->InputChannels; i++) {
1414*3ac0a46fSAndroid Build Coastguard Worker Out[i] = In[i];
1415*3ac0a46fSAndroid Build Coastguard Worker }
1416*3ac0a46fSAndroid Build Coastguard Worker }
1417*3ac0a46fSAndroid Build Coastguard Worker
1418*3ac0a46fSAndroid Build Coastguard Worker
1419*3ac0a46fSAndroid Build Coastguard Worker // If the target LUT holds only curves, the optimization procedure is to join all those
1420*3ac0a46fSAndroid Build Coastguard Worker // curves together. That only works on curves and does not work on matrices.
1421*3ac0a46fSAndroid Build Coastguard Worker static
OptimizeByJoiningCurves(cmsPipeline ** Lut,cmsUInt32Number Intent,cmsUInt32Number * InputFormat,cmsUInt32Number * OutputFormat,cmsUInt32Number * dwFlags)1422*3ac0a46fSAndroid Build Coastguard Worker cmsBool OptimizeByJoiningCurves(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags)
1423*3ac0a46fSAndroid Build Coastguard Worker {
1424*3ac0a46fSAndroid Build Coastguard Worker cmsToneCurve** GammaTables = NULL;
1425*3ac0a46fSAndroid Build Coastguard Worker cmsFloat32Number InFloat[cmsMAXCHANNELS], OutFloat[cmsMAXCHANNELS];
1426*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number i, j;
1427*3ac0a46fSAndroid Build Coastguard Worker cmsPipeline* Src = *Lut;
1428*3ac0a46fSAndroid Build Coastguard Worker cmsPipeline* Dest = NULL;
1429*3ac0a46fSAndroid Build Coastguard Worker cmsStage* mpe;
1430*3ac0a46fSAndroid Build Coastguard Worker cmsStage* ObtainedCurves = NULL;
1431*3ac0a46fSAndroid Build Coastguard Worker
1432*3ac0a46fSAndroid Build Coastguard Worker
1433*3ac0a46fSAndroid Build Coastguard Worker // This is a lossy optimization! does not apply in floating-point cases
1434*3ac0a46fSAndroid Build Coastguard Worker if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) return FALSE;
1435*3ac0a46fSAndroid Build Coastguard Worker
1436*3ac0a46fSAndroid Build Coastguard Worker // Only curves in this LUT?
1437*3ac0a46fSAndroid Build Coastguard Worker for (mpe = cmsPipelineGetPtrToFirstStage(Src);
1438*3ac0a46fSAndroid Build Coastguard Worker mpe != NULL;
1439*3ac0a46fSAndroid Build Coastguard Worker mpe = cmsStageNext(mpe)) {
1440*3ac0a46fSAndroid Build Coastguard Worker if (cmsStageType(mpe) != cmsSigCurveSetElemType) return FALSE;
1441*3ac0a46fSAndroid Build Coastguard Worker }
1442*3ac0a46fSAndroid Build Coastguard Worker
1443*3ac0a46fSAndroid Build Coastguard Worker // Allocate an empty LUT
1444*3ac0a46fSAndroid Build Coastguard Worker Dest = cmsPipelineAlloc(Src ->ContextID, Src ->InputChannels, Src ->OutputChannels);
1445*3ac0a46fSAndroid Build Coastguard Worker if (Dest == NULL) return FALSE;
1446*3ac0a46fSAndroid Build Coastguard Worker
1447*3ac0a46fSAndroid Build Coastguard Worker // Create target curves
1448*3ac0a46fSAndroid Build Coastguard Worker GammaTables = (cmsToneCurve**) _cmsCalloc(Src ->ContextID, Src ->InputChannels, sizeof(cmsToneCurve*));
1449*3ac0a46fSAndroid Build Coastguard Worker if (GammaTables == NULL) goto Error;
1450*3ac0a46fSAndroid Build Coastguard Worker
1451*3ac0a46fSAndroid Build Coastguard Worker for (i=0; i < Src ->InputChannels; i++) {
1452*3ac0a46fSAndroid Build Coastguard Worker GammaTables[i] = cmsBuildTabulatedToneCurve16(Src ->ContextID, PRELINEARIZATION_POINTS, NULL);
1453*3ac0a46fSAndroid Build Coastguard Worker if (GammaTables[i] == NULL) goto Error;
1454*3ac0a46fSAndroid Build Coastguard Worker }
1455*3ac0a46fSAndroid Build Coastguard Worker
1456*3ac0a46fSAndroid Build Coastguard Worker // Compute 16 bit result by using floating point
1457*3ac0a46fSAndroid Build Coastguard Worker for (i=0; i < PRELINEARIZATION_POINTS; i++) {
1458*3ac0a46fSAndroid Build Coastguard Worker
1459*3ac0a46fSAndroid Build Coastguard Worker for (j=0; j < Src ->InputChannels; j++)
1460*3ac0a46fSAndroid Build Coastguard Worker InFloat[j] = (cmsFloat32Number) ((cmsFloat64Number) i / (PRELINEARIZATION_POINTS - 1));
1461*3ac0a46fSAndroid Build Coastguard Worker
1462*3ac0a46fSAndroid Build Coastguard Worker cmsPipelineEvalFloat(InFloat, OutFloat, Src);
1463*3ac0a46fSAndroid Build Coastguard Worker
1464*3ac0a46fSAndroid Build Coastguard Worker for (j=0; j < Src ->InputChannels; j++)
1465*3ac0a46fSAndroid Build Coastguard Worker GammaTables[j] -> Table16[i] = _cmsQuickSaturateWord(OutFloat[j] * 65535.0);
1466*3ac0a46fSAndroid Build Coastguard Worker }
1467*3ac0a46fSAndroid Build Coastguard Worker
1468*3ac0a46fSAndroid Build Coastguard Worker ObtainedCurves = cmsStageAllocToneCurves(Src ->ContextID, Src ->InputChannels, GammaTables);
1469*3ac0a46fSAndroid Build Coastguard Worker if (ObtainedCurves == NULL) goto Error;
1470*3ac0a46fSAndroid Build Coastguard Worker
1471*3ac0a46fSAndroid Build Coastguard Worker for (i=0; i < Src ->InputChannels; i++) {
1472*3ac0a46fSAndroid Build Coastguard Worker cmsFreeToneCurve(GammaTables[i]);
1473*3ac0a46fSAndroid Build Coastguard Worker GammaTables[i] = NULL;
1474*3ac0a46fSAndroid Build Coastguard Worker }
1475*3ac0a46fSAndroid Build Coastguard Worker
1476*3ac0a46fSAndroid Build Coastguard Worker if (GammaTables != NULL) {
1477*3ac0a46fSAndroid Build Coastguard Worker _cmsFree(Src->ContextID, GammaTables);
1478*3ac0a46fSAndroid Build Coastguard Worker GammaTables = NULL;
1479*3ac0a46fSAndroid Build Coastguard Worker }
1480*3ac0a46fSAndroid Build Coastguard Worker
1481*3ac0a46fSAndroid Build Coastguard Worker // Maybe the curves are linear at the end
1482*3ac0a46fSAndroid Build Coastguard Worker if (!AllCurvesAreLinear(ObtainedCurves)) {
1483*3ac0a46fSAndroid Build Coastguard Worker _cmsStageToneCurvesData* Data;
1484*3ac0a46fSAndroid Build Coastguard Worker
1485*3ac0a46fSAndroid Build Coastguard Worker if (!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, ObtainedCurves))
1486*3ac0a46fSAndroid Build Coastguard Worker goto Error;
1487*3ac0a46fSAndroid Build Coastguard Worker Data = (_cmsStageToneCurvesData*) cmsStageData(ObtainedCurves);
1488*3ac0a46fSAndroid Build Coastguard Worker ObtainedCurves = NULL;
1489*3ac0a46fSAndroid Build Coastguard Worker
1490*3ac0a46fSAndroid Build Coastguard Worker // If the curves are to be applied in 8 bits, we can save memory
1491*3ac0a46fSAndroid Build Coastguard Worker if (_cmsFormatterIs8bit(*InputFormat)) {
1492*3ac0a46fSAndroid Build Coastguard Worker Curves16Data* c16 = CurvesAlloc(Dest ->ContextID, Data ->nCurves, 256, Data ->TheCurves);
1493*3ac0a46fSAndroid Build Coastguard Worker
1494*3ac0a46fSAndroid Build Coastguard Worker if (c16 == NULL) goto Error;
1495*3ac0a46fSAndroid Build Coastguard Worker *dwFlags |= cmsFLAGS_NOCACHE;
1496*3ac0a46fSAndroid Build Coastguard Worker _cmsPipelineSetOptimizationParameters(Dest, FastEvaluateCurves8, c16, CurvesFree, CurvesDup);
1497*3ac0a46fSAndroid Build Coastguard Worker
1498*3ac0a46fSAndroid Build Coastguard Worker }
1499*3ac0a46fSAndroid Build Coastguard Worker else {
1500*3ac0a46fSAndroid Build Coastguard Worker Curves16Data* c16 = CurvesAlloc(Dest ->ContextID, Data ->nCurves, 65536, Data ->TheCurves);
1501*3ac0a46fSAndroid Build Coastguard Worker
1502*3ac0a46fSAndroid Build Coastguard Worker if (c16 == NULL) goto Error;
1503*3ac0a46fSAndroid Build Coastguard Worker *dwFlags |= cmsFLAGS_NOCACHE;
1504*3ac0a46fSAndroid Build Coastguard Worker _cmsPipelineSetOptimizationParameters(Dest, FastEvaluateCurves16, c16, CurvesFree, CurvesDup);
1505*3ac0a46fSAndroid Build Coastguard Worker }
1506*3ac0a46fSAndroid Build Coastguard Worker }
1507*3ac0a46fSAndroid Build Coastguard Worker else {
1508*3ac0a46fSAndroid Build Coastguard Worker
1509*3ac0a46fSAndroid Build Coastguard Worker // LUT optimizes to nothing. Set the identity LUT
1510*3ac0a46fSAndroid Build Coastguard Worker cmsStageFree(ObtainedCurves);
1511*3ac0a46fSAndroid Build Coastguard Worker ObtainedCurves = NULL;
1512*3ac0a46fSAndroid Build Coastguard Worker
1513*3ac0a46fSAndroid Build Coastguard Worker if (!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, cmsStageAllocIdentity(Dest ->ContextID, Src ->InputChannels)))
1514*3ac0a46fSAndroid Build Coastguard Worker goto Error;
1515*3ac0a46fSAndroid Build Coastguard Worker
1516*3ac0a46fSAndroid Build Coastguard Worker *dwFlags |= cmsFLAGS_NOCACHE;
1517*3ac0a46fSAndroid Build Coastguard Worker _cmsPipelineSetOptimizationParameters(Dest, FastIdentity16, (void*) Dest, NULL, NULL);
1518*3ac0a46fSAndroid Build Coastguard Worker }
1519*3ac0a46fSAndroid Build Coastguard Worker
1520*3ac0a46fSAndroid Build Coastguard Worker // We are done.
1521*3ac0a46fSAndroid Build Coastguard Worker cmsPipelineFree(Src);
1522*3ac0a46fSAndroid Build Coastguard Worker *Lut = Dest;
1523*3ac0a46fSAndroid Build Coastguard Worker return TRUE;
1524*3ac0a46fSAndroid Build Coastguard Worker
1525*3ac0a46fSAndroid Build Coastguard Worker Error:
1526*3ac0a46fSAndroid Build Coastguard Worker
1527*3ac0a46fSAndroid Build Coastguard Worker if (ObtainedCurves != NULL) cmsStageFree(ObtainedCurves);
1528*3ac0a46fSAndroid Build Coastguard Worker if (GammaTables != NULL) {
1529*3ac0a46fSAndroid Build Coastguard Worker for (i=0; i < Src ->InputChannels; i++) {
1530*3ac0a46fSAndroid Build Coastguard Worker if (GammaTables[i] != NULL) cmsFreeToneCurve(GammaTables[i]);
1531*3ac0a46fSAndroid Build Coastguard Worker }
1532*3ac0a46fSAndroid Build Coastguard Worker
1533*3ac0a46fSAndroid Build Coastguard Worker _cmsFree(Src ->ContextID, GammaTables);
1534*3ac0a46fSAndroid Build Coastguard Worker }
1535*3ac0a46fSAndroid Build Coastguard Worker
1536*3ac0a46fSAndroid Build Coastguard Worker if (Dest != NULL) cmsPipelineFree(Dest);
1537*3ac0a46fSAndroid Build Coastguard Worker return FALSE;
1538*3ac0a46fSAndroid Build Coastguard Worker
1539*3ac0a46fSAndroid Build Coastguard Worker cmsUNUSED_PARAMETER(Intent);
1540*3ac0a46fSAndroid Build Coastguard Worker cmsUNUSED_PARAMETER(InputFormat);
1541*3ac0a46fSAndroid Build Coastguard Worker cmsUNUSED_PARAMETER(OutputFormat);
1542*3ac0a46fSAndroid Build Coastguard Worker cmsUNUSED_PARAMETER(dwFlags);
1543*3ac0a46fSAndroid Build Coastguard Worker }
1544*3ac0a46fSAndroid Build Coastguard Worker
1545*3ac0a46fSAndroid Build Coastguard Worker // -------------------------------------------------------------------------------------------------------------------------------------
1546*3ac0a46fSAndroid Build Coastguard Worker // LUT is Shaper - Matrix - Matrix - Shaper, which is very frequent when combining two matrix-shaper profiles
1547*3ac0a46fSAndroid Build Coastguard Worker
1548*3ac0a46fSAndroid Build Coastguard Worker
1549*3ac0a46fSAndroid Build Coastguard Worker static
FreeMatShaper(cmsContext ContextID,void * Data)1550*3ac0a46fSAndroid Build Coastguard Worker void FreeMatShaper(cmsContext ContextID, void* Data)
1551*3ac0a46fSAndroid Build Coastguard Worker {
1552*3ac0a46fSAndroid Build Coastguard Worker if (Data != NULL) _cmsFree(ContextID, Data);
1553*3ac0a46fSAndroid Build Coastguard Worker }
1554*3ac0a46fSAndroid Build Coastguard Worker
1555*3ac0a46fSAndroid Build Coastguard Worker static
DupMatShaper(cmsContext ContextID,const void * Data)1556*3ac0a46fSAndroid Build Coastguard Worker void* DupMatShaper(cmsContext ContextID, const void* Data)
1557*3ac0a46fSAndroid Build Coastguard Worker {
1558*3ac0a46fSAndroid Build Coastguard Worker return _cmsDupMem(ContextID, Data, sizeof(MatShaper8Data));
1559*3ac0a46fSAndroid Build Coastguard Worker }
1560*3ac0a46fSAndroid Build Coastguard Worker
1561*3ac0a46fSAndroid Build Coastguard Worker
1562*3ac0a46fSAndroid Build Coastguard Worker // A fast matrix-shaper evaluator for 8 bits. This is a bit tricky since I'm using 1.14 signed fixed point
1563*3ac0a46fSAndroid Build Coastguard Worker // to accomplish some performance. Actually it takes 256x3 16 bits tables and 16385 x 3 tables of 8 bits,
1564*3ac0a46fSAndroid Build Coastguard Worker // in total about 50K, and the performance boost is huge!
1565*3ac0a46fSAndroid Build Coastguard Worker static CMS_NO_SANITIZE
MatShaperEval16(CMSREGISTER const cmsUInt16Number In[],CMSREGISTER cmsUInt16Number Out[],CMSREGISTER const void * D)1566*3ac0a46fSAndroid Build Coastguard Worker void MatShaperEval16(CMSREGISTER const cmsUInt16Number In[],
1567*3ac0a46fSAndroid Build Coastguard Worker CMSREGISTER cmsUInt16Number Out[],
1568*3ac0a46fSAndroid Build Coastguard Worker CMSREGISTER const void* D)
1569*3ac0a46fSAndroid Build Coastguard Worker {
1570*3ac0a46fSAndroid Build Coastguard Worker MatShaper8Data* p = (MatShaper8Data*) D;
1571*3ac0a46fSAndroid Build Coastguard Worker cmsS1Fixed14Number r, g, b;
1572*3ac0a46fSAndroid Build Coastguard Worker cmsInt64Number l1, l2, l3;
1573*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number ri, gi, bi;
1574*3ac0a46fSAndroid Build Coastguard Worker
1575*3ac0a46fSAndroid Build Coastguard Worker // In this case (and only in this case!) we can use this simplification since
1576*3ac0a46fSAndroid Build Coastguard Worker // In[] is assured to come from a 8 bit number. (a << 8 | a)
1577*3ac0a46fSAndroid Build Coastguard Worker ri = In[0] & 0xFFU;
1578*3ac0a46fSAndroid Build Coastguard Worker gi = In[1] & 0xFFU;
1579*3ac0a46fSAndroid Build Coastguard Worker bi = In[2] & 0xFFU;
1580*3ac0a46fSAndroid Build Coastguard Worker
1581*3ac0a46fSAndroid Build Coastguard Worker // Across first shaper, which also converts to 1.14 fixed point
1582*3ac0a46fSAndroid Build Coastguard Worker r = _FixedClamp(p->Shaper1R[ri]);
1583*3ac0a46fSAndroid Build Coastguard Worker g = _FixedClamp(p->Shaper1G[gi]);
1584*3ac0a46fSAndroid Build Coastguard Worker b = _FixedClamp(p->Shaper1B[bi]);
1585*3ac0a46fSAndroid Build Coastguard Worker
1586*3ac0a46fSAndroid Build Coastguard Worker // Evaluate the matrix in 1.14 fixed point
1587*3ac0a46fSAndroid Build Coastguard Worker l1 = _MatShaperEvaluateRow(p->Mat[0], p->Off[0], r, g, b);
1588*3ac0a46fSAndroid Build Coastguard Worker l2 = _MatShaperEvaluateRow(p->Mat[1], p->Off[1], r, g, b);
1589*3ac0a46fSAndroid Build Coastguard Worker l3 = _MatShaperEvaluateRow(p->Mat[2], p->Off[2], r, g, b);
1590*3ac0a46fSAndroid Build Coastguard Worker
1591*3ac0a46fSAndroid Build Coastguard Worker // Now we have to clip to 0..1.0 range
1592*3ac0a46fSAndroid Build Coastguard Worker ri = (l1 < 0) ? 0 : ((l1 > 16384) ? 16384U : (cmsUInt32Number) l1);
1593*3ac0a46fSAndroid Build Coastguard Worker gi = (l2 < 0) ? 0 : ((l2 > 16384) ? 16384U : (cmsUInt32Number) l2);
1594*3ac0a46fSAndroid Build Coastguard Worker bi = (l3 < 0) ? 0 : ((l3 > 16384) ? 16384U : (cmsUInt32Number) l3);
1595*3ac0a46fSAndroid Build Coastguard Worker
1596*3ac0a46fSAndroid Build Coastguard Worker // And across second shaper,
1597*3ac0a46fSAndroid Build Coastguard Worker Out[0] = p->Shaper2R[ri];
1598*3ac0a46fSAndroid Build Coastguard Worker Out[1] = p->Shaper2G[gi];
1599*3ac0a46fSAndroid Build Coastguard Worker Out[2] = p->Shaper2B[bi];
1600*3ac0a46fSAndroid Build Coastguard Worker
1601*3ac0a46fSAndroid Build Coastguard Worker }
1602*3ac0a46fSAndroid Build Coastguard Worker
1603*3ac0a46fSAndroid Build Coastguard Worker // This table converts from 8 bits to 1.14 after applying the curve
1604*3ac0a46fSAndroid Build Coastguard Worker static
FillFirstShaper(cmsS1Fixed14Number * Table,cmsToneCurve * Curve)1605*3ac0a46fSAndroid Build Coastguard Worker void FillFirstShaper(cmsS1Fixed14Number* Table, cmsToneCurve* Curve)
1606*3ac0a46fSAndroid Build Coastguard Worker {
1607*3ac0a46fSAndroid Build Coastguard Worker int i;
1608*3ac0a46fSAndroid Build Coastguard Worker cmsFloat32Number R, y;
1609*3ac0a46fSAndroid Build Coastguard Worker
1610*3ac0a46fSAndroid Build Coastguard Worker for (i=0; i < 256; i++) {
1611*3ac0a46fSAndroid Build Coastguard Worker
1612*3ac0a46fSAndroid Build Coastguard Worker R = (cmsFloat32Number) (i / 255.0);
1613*3ac0a46fSAndroid Build Coastguard Worker y = cmsEvalToneCurveFloat(Curve, R);
1614*3ac0a46fSAndroid Build Coastguard Worker
1615*3ac0a46fSAndroid Build Coastguard Worker if (y < 131072.0)
1616*3ac0a46fSAndroid Build Coastguard Worker Table[i] = DOUBLE_TO_1FIXED14(y);
1617*3ac0a46fSAndroid Build Coastguard Worker else
1618*3ac0a46fSAndroid Build Coastguard Worker Table[i] = 0x7fffffff;
1619*3ac0a46fSAndroid Build Coastguard Worker }
1620*3ac0a46fSAndroid Build Coastguard Worker }
1621*3ac0a46fSAndroid Build Coastguard Worker
1622*3ac0a46fSAndroid Build Coastguard Worker // This table converts form 1.14 (being 0x4000 the last entry) to 8 bits after applying the curve
1623*3ac0a46fSAndroid Build Coastguard Worker static
FillSecondShaper(cmsUInt16Number * Table,cmsToneCurve * Curve,cmsBool Is8BitsOutput)1624*3ac0a46fSAndroid Build Coastguard Worker void FillSecondShaper(cmsUInt16Number* Table, cmsToneCurve* Curve, cmsBool Is8BitsOutput)
1625*3ac0a46fSAndroid Build Coastguard Worker {
1626*3ac0a46fSAndroid Build Coastguard Worker int i;
1627*3ac0a46fSAndroid Build Coastguard Worker cmsFloat32Number R, Val;
1628*3ac0a46fSAndroid Build Coastguard Worker
1629*3ac0a46fSAndroid Build Coastguard Worker for (i=0; i < 16385; i++) {
1630*3ac0a46fSAndroid Build Coastguard Worker
1631*3ac0a46fSAndroid Build Coastguard Worker R = (cmsFloat32Number) (i / 16384.0);
1632*3ac0a46fSAndroid Build Coastguard Worker Val = cmsEvalToneCurveFloat(Curve, R); // Val comes 0..1.0
1633*3ac0a46fSAndroid Build Coastguard Worker
1634*3ac0a46fSAndroid Build Coastguard Worker if (Val < 0)
1635*3ac0a46fSAndroid Build Coastguard Worker Val = 0;
1636*3ac0a46fSAndroid Build Coastguard Worker
1637*3ac0a46fSAndroid Build Coastguard Worker if (Val > 1.0)
1638*3ac0a46fSAndroid Build Coastguard Worker Val = 1.0;
1639*3ac0a46fSAndroid Build Coastguard Worker
1640*3ac0a46fSAndroid Build Coastguard Worker if (Is8BitsOutput) {
1641*3ac0a46fSAndroid Build Coastguard Worker
1642*3ac0a46fSAndroid Build Coastguard Worker // If 8 bits output, we can optimize further by computing the / 257 part.
1643*3ac0a46fSAndroid Build Coastguard Worker // first we compute the resulting byte and then we store the byte times
1644*3ac0a46fSAndroid Build Coastguard Worker // 257. This quantization allows to round very quick by doing a >> 8, but
1645*3ac0a46fSAndroid Build Coastguard Worker // since the low byte is always equal to msb, we can do a & 0xff and this works!
1646*3ac0a46fSAndroid Build Coastguard Worker cmsUInt16Number w = _cmsQuickSaturateWord(Val * 65535.0);
1647*3ac0a46fSAndroid Build Coastguard Worker cmsUInt8Number b = FROM_16_TO_8(w);
1648*3ac0a46fSAndroid Build Coastguard Worker
1649*3ac0a46fSAndroid Build Coastguard Worker Table[i] = FROM_8_TO_16(b);
1650*3ac0a46fSAndroid Build Coastguard Worker }
1651*3ac0a46fSAndroid Build Coastguard Worker else Table[i] = _cmsQuickSaturateWord(Val * 65535.0);
1652*3ac0a46fSAndroid Build Coastguard Worker }
1653*3ac0a46fSAndroid Build Coastguard Worker }
1654*3ac0a46fSAndroid Build Coastguard Worker
1655*3ac0a46fSAndroid Build Coastguard Worker // Compute the matrix-shaper structure
1656*3ac0a46fSAndroid Build Coastguard Worker static
SetMatShaper(cmsPipeline * Dest,cmsToneCurve * Curve1[3],cmsMAT3 * Mat,cmsVEC3 * Off,cmsToneCurve * Curve2[3],cmsUInt32Number * OutputFormat)1657*3ac0a46fSAndroid Build Coastguard Worker cmsBool SetMatShaper(cmsPipeline* Dest, cmsToneCurve* Curve1[3], cmsMAT3* Mat, cmsVEC3* Off, cmsToneCurve* Curve2[3], cmsUInt32Number* OutputFormat)
1658*3ac0a46fSAndroid Build Coastguard Worker {
1659*3ac0a46fSAndroid Build Coastguard Worker MatShaper8Data* p;
1660*3ac0a46fSAndroid Build Coastguard Worker int i, j;
1661*3ac0a46fSAndroid Build Coastguard Worker cmsBool Is8Bits = _cmsFormatterIs8bit(*OutputFormat);
1662*3ac0a46fSAndroid Build Coastguard Worker
1663*3ac0a46fSAndroid Build Coastguard Worker // Allocate a big chuck of memory to store precomputed tables
1664*3ac0a46fSAndroid Build Coastguard Worker p = (MatShaper8Data*) _cmsMalloc(Dest ->ContextID, sizeof(MatShaper8Data));
1665*3ac0a46fSAndroid Build Coastguard Worker if (p == NULL) return FALSE;
1666*3ac0a46fSAndroid Build Coastguard Worker
1667*3ac0a46fSAndroid Build Coastguard Worker p -> ContextID = Dest -> ContextID;
1668*3ac0a46fSAndroid Build Coastguard Worker
1669*3ac0a46fSAndroid Build Coastguard Worker // Precompute tables
1670*3ac0a46fSAndroid Build Coastguard Worker FillFirstShaper(p ->Shaper1R, Curve1[0]);
1671*3ac0a46fSAndroid Build Coastguard Worker FillFirstShaper(p ->Shaper1G, Curve1[1]);
1672*3ac0a46fSAndroid Build Coastguard Worker FillFirstShaper(p ->Shaper1B, Curve1[2]);
1673*3ac0a46fSAndroid Build Coastguard Worker
1674*3ac0a46fSAndroid Build Coastguard Worker FillSecondShaper(p ->Shaper2R, Curve2[0], Is8Bits);
1675*3ac0a46fSAndroid Build Coastguard Worker FillSecondShaper(p ->Shaper2G, Curve2[1], Is8Bits);
1676*3ac0a46fSAndroid Build Coastguard Worker FillSecondShaper(p ->Shaper2B, Curve2[2], Is8Bits);
1677*3ac0a46fSAndroid Build Coastguard Worker
1678*3ac0a46fSAndroid Build Coastguard Worker // Convert matrix to nFixed14. Note that those values may take more than 16 bits
1679*3ac0a46fSAndroid Build Coastguard Worker for (i=0; i < 3; i++) {
1680*3ac0a46fSAndroid Build Coastguard Worker for (j=0; j < 3; j++) {
1681*3ac0a46fSAndroid Build Coastguard Worker p ->Mat[i][j] = DOUBLE_TO_1FIXED14(Mat->v[i].n[j]);
1682*3ac0a46fSAndroid Build Coastguard Worker }
1683*3ac0a46fSAndroid Build Coastguard Worker }
1684*3ac0a46fSAndroid Build Coastguard Worker
1685*3ac0a46fSAndroid Build Coastguard Worker for (i=0; i < 3; i++) {
1686*3ac0a46fSAndroid Build Coastguard Worker
1687*3ac0a46fSAndroid Build Coastguard Worker if (Off == NULL) {
1688*3ac0a46fSAndroid Build Coastguard Worker p ->Off[i] = 0;
1689*3ac0a46fSAndroid Build Coastguard Worker }
1690*3ac0a46fSAndroid Build Coastguard Worker else {
1691*3ac0a46fSAndroid Build Coastguard Worker p ->Off[i] = DOUBLE_TO_1FIXED14(Off->n[i]);
1692*3ac0a46fSAndroid Build Coastguard Worker }
1693*3ac0a46fSAndroid Build Coastguard Worker }
1694*3ac0a46fSAndroid Build Coastguard Worker
1695*3ac0a46fSAndroid Build Coastguard Worker // Mark as optimized for faster formatter
1696*3ac0a46fSAndroid Build Coastguard Worker if (Is8Bits)
1697*3ac0a46fSAndroid Build Coastguard Worker *OutputFormat |= OPTIMIZED_SH(1);
1698*3ac0a46fSAndroid Build Coastguard Worker
1699*3ac0a46fSAndroid Build Coastguard Worker // Fill function pointers
1700*3ac0a46fSAndroid Build Coastguard Worker _cmsPipelineSetOptimizationParameters(Dest, MatShaperEval16, (void*) p, FreeMatShaper, DupMatShaper);
1701*3ac0a46fSAndroid Build Coastguard Worker return TRUE;
1702*3ac0a46fSAndroid Build Coastguard Worker }
1703*3ac0a46fSAndroid Build Coastguard Worker
1704*3ac0a46fSAndroid Build Coastguard Worker // 8 bits on input allows matrix-shaper boot up to 25 Mpixels per second on RGB. That's fast!
1705*3ac0a46fSAndroid Build Coastguard Worker static
OptimizeMatrixShaper(cmsPipeline ** Lut,cmsUInt32Number Intent,cmsUInt32Number * InputFormat,cmsUInt32Number * OutputFormat,cmsUInt32Number * dwFlags)1706*3ac0a46fSAndroid Build Coastguard Worker cmsBool OptimizeMatrixShaper(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags)
1707*3ac0a46fSAndroid Build Coastguard Worker {
1708*3ac0a46fSAndroid Build Coastguard Worker cmsStage* Curve1, *Curve2;
1709*3ac0a46fSAndroid Build Coastguard Worker cmsStage* Matrix1, *Matrix2;
1710*3ac0a46fSAndroid Build Coastguard Worker cmsMAT3 res;
1711*3ac0a46fSAndroid Build Coastguard Worker cmsBool IdentityMat;
1712*3ac0a46fSAndroid Build Coastguard Worker cmsPipeline* Dest, *Src;
1713*3ac0a46fSAndroid Build Coastguard Worker cmsFloat64Number* Offset;
1714*3ac0a46fSAndroid Build Coastguard Worker
1715*3ac0a46fSAndroid Build Coastguard Worker // Only works on RGB to RGB
1716*3ac0a46fSAndroid Build Coastguard Worker if (T_CHANNELS(*InputFormat) != 3 || T_CHANNELS(*OutputFormat) != 3) return FALSE;
1717*3ac0a46fSAndroid Build Coastguard Worker
1718*3ac0a46fSAndroid Build Coastguard Worker // Only works on 8 bit input
1719*3ac0a46fSAndroid Build Coastguard Worker if (!_cmsFormatterIs8bit(*InputFormat)) return FALSE;
1720*3ac0a46fSAndroid Build Coastguard Worker
1721*3ac0a46fSAndroid Build Coastguard Worker // Seems suitable, proceed
1722*3ac0a46fSAndroid Build Coastguard Worker Src = *Lut;
1723*3ac0a46fSAndroid Build Coastguard Worker
1724*3ac0a46fSAndroid Build Coastguard Worker // Check for:
1725*3ac0a46fSAndroid Build Coastguard Worker //
1726*3ac0a46fSAndroid Build Coastguard Worker // shaper-matrix-matrix-shaper
1727*3ac0a46fSAndroid Build Coastguard Worker // shaper-matrix-shaper
1728*3ac0a46fSAndroid Build Coastguard Worker //
1729*3ac0a46fSAndroid Build Coastguard Worker // Both of those constructs are possible (first because abs. colorimetric).
1730*3ac0a46fSAndroid Build Coastguard Worker // additionally, In the first case, the input matrix offset should be zero.
1731*3ac0a46fSAndroid Build Coastguard Worker
1732*3ac0a46fSAndroid Build Coastguard Worker IdentityMat = FALSE;
1733*3ac0a46fSAndroid Build Coastguard Worker if (cmsPipelineCheckAndRetreiveStages(Src, 4,
1734*3ac0a46fSAndroid Build Coastguard Worker cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType,
1735*3ac0a46fSAndroid Build Coastguard Worker &Curve1, &Matrix1, &Matrix2, &Curve2)) {
1736*3ac0a46fSAndroid Build Coastguard Worker
1737*3ac0a46fSAndroid Build Coastguard Worker // Get both matrices
1738*3ac0a46fSAndroid Build Coastguard Worker _cmsStageMatrixData* Data1 = (_cmsStageMatrixData*)cmsStageData(Matrix1);
1739*3ac0a46fSAndroid Build Coastguard Worker _cmsStageMatrixData* Data2 = (_cmsStageMatrixData*)cmsStageData(Matrix2);
1740*3ac0a46fSAndroid Build Coastguard Worker
1741*3ac0a46fSAndroid Build Coastguard Worker // Only RGB to RGB
1742*3ac0a46fSAndroid Build Coastguard Worker if (Matrix1->InputChannels != 3 || Matrix1->OutputChannels != 3 ||
1743*3ac0a46fSAndroid Build Coastguard Worker Matrix2->InputChannels != 3 || Matrix2->OutputChannels != 3) return FALSE;
1744*3ac0a46fSAndroid Build Coastguard Worker
1745*3ac0a46fSAndroid Build Coastguard Worker // Input offset should be zero
1746*3ac0a46fSAndroid Build Coastguard Worker if (Data1->Offset != NULL) return FALSE;
1747*3ac0a46fSAndroid Build Coastguard Worker
1748*3ac0a46fSAndroid Build Coastguard Worker // Multiply both matrices to get the result
1749*3ac0a46fSAndroid Build Coastguard Worker _cmsMAT3per(&res, (cmsMAT3*)Data2->Double, (cmsMAT3*)Data1->Double);
1750*3ac0a46fSAndroid Build Coastguard Worker
1751*3ac0a46fSAndroid Build Coastguard Worker // Only 2nd matrix has offset, or it is zero
1752*3ac0a46fSAndroid Build Coastguard Worker Offset = Data2->Offset;
1753*3ac0a46fSAndroid Build Coastguard Worker
1754*3ac0a46fSAndroid Build Coastguard Worker // Now the result is in res + Data2 -> Offset. Maybe is a plain identity?
1755*3ac0a46fSAndroid Build Coastguard Worker if (_cmsMAT3isIdentity(&res) && Offset == NULL) {
1756*3ac0a46fSAndroid Build Coastguard Worker
1757*3ac0a46fSAndroid Build Coastguard Worker // We can get rid of full matrix
1758*3ac0a46fSAndroid Build Coastguard Worker IdentityMat = TRUE;
1759*3ac0a46fSAndroid Build Coastguard Worker }
1760*3ac0a46fSAndroid Build Coastguard Worker
1761*3ac0a46fSAndroid Build Coastguard Worker }
1762*3ac0a46fSAndroid Build Coastguard Worker else {
1763*3ac0a46fSAndroid Build Coastguard Worker
1764*3ac0a46fSAndroid Build Coastguard Worker if (cmsPipelineCheckAndRetreiveStages(Src, 3,
1765*3ac0a46fSAndroid Build Coastguard Worker cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType,
1766*3ac0a46fSAndroid Build Coastguard Worker &Curve1, &Matrix1, &Curve2)) {
1767*3ac0a46fSAndroid Build Coastguard Worker
1768*3ac0a46fSAndroid Build Coastguard Worker _cmsStageMatrixData* Data = (_cmsStageMatrixData*)cmsStageData(Matrix1);
1769*3ac0a46fSAndroid Build Coastguard Worker
1770*3ac0a46fSAndroid Build Coastguard Worker // Copy the matrix to our result
1771*3ac0a46fSAndroid Build Coastguard Worker memcpy(&res, Data->Double, sizeof(res));
1772*3ac0a46fSAndroid Build Coastguard Worker
1773*3ac0a46fSAndroid Build Coastguard Worker // Preserve the Odffset (may be NULL as a zero offset)
1774*3ac0a46fSAndroid Build Coastguard Worker Offset = Data->Offset;
1775*3ac0a46fSAndroid Build Coastguard Worker
1776*3ac0a46fSAndroid Build Coastguard Worker if (_cmsMAT3isIdentity(&res) && Offset == NULL) {
1777*3ac0a46fSAndroid Build Coastguard Worker
1778*3ac0a46fSAndroid Build Coastguard Worker // We can get rid of full matrix
1779*3ac0a46fSAndroid Build Coastguard Worker IdentityMat = TRUE;
1780*3ac0a46fSAndroid Build Coastguard Worker }
1781*3ac0a46fSAndroid Build Coastguard Worker }
1782*3ac0a46fSAndroid Build Coastguard Worker else
1783*3ac0a46fSAndroid Build Coastguard Worker return FALSE; // Not optimizeable this time
1784*3ac0a46fSAndroid Build Coastguard Worker
1785*3ac0a46fSAndroid Build Coastguard Worker }
1786*3ac0a46fSAndroid Build Coastguard Worker
1787*3ac0a46fSAndroid Build Coastguard Worker // Allocate an empty LUT
1788*3ac0a46fSAndroid Build Coastguard Worker Dest = cmsPipelineAlloc(Src ->ContextID, Src ->InputChannels, Src ->OutputChannels);
1789*3ac0a46fSAndroid Build Coastguard Worker if (!Dest) return FALSE;
1790*3ac0a46fSAndroid Build Coastguard Worker
1791*3ac0a46fSAndroid Build Coastguard Worker // Assamble the new LUT
1792*3ac0a46fSAndroid Build Coastguard Worker if (!cmsPipelineInsertStage(Dest, cmsAT_BEGIN, cmsStageDup(Curve1)))
1793*3ac0a46fSAndroid Build Coastguard Worker goto Error;
1794*3ac0a46fSAndroid Build Coastguard Worker
1795*3ac0a46fSAndroid Build Coastguard Worker if (!IdentityMat) {
1796*3ac0a46fSAndroid Build Coastguard Worker
1797*3ac0a46fSAndroid Build Coastguard Worker if (!cmsPipelineInsertStage(Dest, cmsAT_END, cmsStageAllocMatrix(Dest->ContextID, 3, 3, (const cmsFloat64Number*)&res, Offset)))
1798*3ac0a46fSAndroid Build Coastguard Worker goto Error;
1799*3ac0a46fSAndroid Build Coastguard Worker }
1800*3ac0a46fSAndroid Build Coastguard Worker
1801*3ac0a46fSAndroid Build Coastguard Worker if (!cmsPipelineInsertStage(Dest, cmsAT_END, cmsStageDup(Curve2)))
1802*3ac0a46fSAndroid Build Coastguard Worker goto Error;
1803*3ac0a46fSAndroid Build Coastguard Worker
1804*3ac0a46fSAndroid Build Coastguard Worker // If identity on matrix, we can further optimize the curves, so call the join curves routine
1805*3ac0a46fSAndroid Build Coastguard Worker if (IdentityMat) {
1806*3ac0a46fSAndroid Build Coastguard Worker
1807*3ac0a46fSAndroid Build Coastguard Worker OptimizeByJoiningCurves(&Dest, Intent, InputFormat, OutputFormat, dwFlags);
1808*3ac0a46fSAndroid Build Coastguard Worker }
1809*3ac0a46fSAndroid Build Coastguard Worker else {
1810*3ac0a46fSAndroid Build Coastguard Worker _cmsStageToneCurvesData* mpeC1 = (_cmsStageToneCurvesData*) cmsStageData(Curve1);
1811*3ac0a46fSAndroid Build Coastguard Worker _cmsStageToneCurvesData* mpeC2 = (_cmsStageToneCurvesData*) cmsStageData(Curve2);
1812*3ac0a46fSAndroid Build Coastguard Worker
1813*3ac0a46fSAndroid Build Coastguard Worker // In this particular optimization, cache does not help as it takes more time to deal with
1814*3ac0a46fSAndroid Build Coastguard Worker // the cache that with the pixel handling
1815*3ac0a46fSAndroid Build Coastguard Worker *dwFlags |= cmsFLAGS_NOCACHE;
1816*3ac0a46fSAndroid Build Coastguard Worker
1817*3ac0a46fSAndroid Build Coastguard Worker // Setup the optimizarion routines
1818*3ac0a46fSAndroid Build Coastguard Worker SetMatShaper(Dest, mpeC1 ->TheCurves, &res, (cmsVEC3*) Offset, mpeC2->TheCurves, OutputFormat);
1819*3ac0a46fSAndroid Build Coastguard Worker }
1820*3ac0a46fSAndroid Build Coastguard Worker
1821*3ac0a46fSAndroid Build Coastguard Worker cmsPipelineFree(Src);
1822*3ac0a46fSAndroid Build Coastguard Worker *Lut = Dest;
1823*3ac0a46fSAndroid Build Coastguard Worker return TRUE;
1824*3ac0a46fSAndroid Build Coastguard Worker Error:
1825*3ac0a46fSAndroid Build Coastguard Worker // Leave Src unchanged
1826*3ac0a46fSAndroid Build Coastguard Worker cmsPipelineFree(Dest);
1827*3ac0a46fSAndroid Build Coastguard Worker return FALSE;
1828*3ac0a46fSAndroid Build Coastguard Worker }
1829*3ac0a46fSAndroid Build Coastguard Worker
1830*3ac0a46fSAndroid Build Coastguard Worker
1831*3ac0a46fSAndroid Build Coastguard Worker // -------------------------------------------------------------------------------------------------------------------------------------
1832*3ac0a46fSAndroid Build Coastguard Worker // Optimization plug-ins
1833*3ac0a46fSAndroid Build Coastguard Worker
1834*3ac0a46fSAndroid Build Coastguard Worker // List of optimizations
1835*3ac0a46fSAndroid Build Coastguard Worker typedef struct _cmsOptimizationCollection_st {
1836*3ac0a46fSAndroid Build Coastguard Worker
1837*3ac0a46fSAndroid Build Coastguard Worker _cmsOPToptimizeFn OptimizePtr;
1838*3ac0a46fSAndroid Build Coastguard Worker
1839*3ac0a46fSAndroid Build Coastguard Worker struct _cmsOptimizationCollection_st *Next;
1840*3ac0a46fSAndroid Build Coastguard Worker
1841*3ac0a46fSAndroid Build Coastguard Worker } _cmsOptimizationCollection;
1842*3ac0a46fSAndroid Build Coastguard Worker
1843*3ac0a46fSAndroid Build Coastguard Worker
1844*3ac0a46fSAndroid Build Coastguard Worker // The built-in list. We currently implement 4 types of optimizations. Joining of curves, matrix-shaper, linearization and resampling
1845*3ac0a46fSAndroid Build Coastguard Worker static _cmsOptimizationCollection DefaultOptimization[] = {
1846*3ac0a46fSAndroid Build Coastguard Worker
1847*3ac0a46fSAndroid Build Coastguard Worker { OptimizeByJoiningCurves, &DefaultOptimization[1] },
1848*3ac0a46fSAndroid Build Coastguard Worker { OptimizeMatrixShaper, &DefaultOptimization[2] },
1849*3ac0a46fSAndroid Build Coastguard Worker { OptimizeByComputingLinearization, &DefaultOptimization[3] },
1850*3ac0a46fSAndroid Build Coastguard Worker { OptimizeByResampling, NULL }
1851*3ac0a46fSAndroid Build Coastguard Worker };
1852*3ac0a46fSAndroid Build Coastguard Worker
1853*3ac0a46fSAndroid Build Coastguard Worker // The linked list head
1854*3ac0a46fSAndroid Build Coastguard Worker _cmsOptimizationPluginChunkType _cmsOptimizationPluginChunk = { NULL };
1855*3ac0a46fSAndroid Build Coastguard Worker
1856*3ac0a46fSAndroid Build Coastguard Worker
1857*3ac0a46fSAndroid Build Coastguard Worker // Duplicates the zone of memory used by the plug-in in the new context
1858*3ac0a46fSAndroid Build Coastguard Worker static
DupPluginOptimizationList(struct _cmsContext_struct * ctx,const struct _cmsContext_struct * src)1859*3ac0a46fSAndroid Build Coastguard Worker void DupPluginOptimizationList(struct _cmsContext_struct* ctx,
1860*3ac0a46fSAndroid Build Coastguard Worker const struct _cmsContext_struct* src)
1861*3ac0a46fSAndroid Build Coastguard Worker {
1862*3ac0a46fSAndroid Build Coastguard Worker _cmsOptimizationPluginChunkType newHead = { NULL };
1863*3ac0a46fSAndroid Build Coastguard Worker _cmsOptimizationCollection* entry;
1864*3ac0a46fSAndroid Build Coastguard Worker _cmsOptimizationCollection* Anterior = NULL;
1865*3ac0a46fSAndroid Build Coastguard Worker _cmsOptimizationPluginChunkType* head = (_cmsOptimizationPluginChunkType*) src->chunks[OptimizationPlugin];
1866*3ac0a46fSAndroid Build Coastguard Worker
1867*3ac0a46fSAndroid Build Coastguard Worker _cmsAssert(ctx != NULL);
1868*3ac0a46fSAndroid Build Coastguard Worker _cmsAssert(head != NULL);
1869*3ac0a46fSAndroid Build Coastguard Worker
1870*3ac0a46fSAndroid Build Coastguard Worker // Walk the list copying all nodes
1871*3ac0a46fSAndroid Build Coastguard Worker for (entry = head->OptimizationCollection;
1872*3ac0a46fSAndroid Build Coastguard Worker entry != NULL;
1873*3ac0a46fSAndroid Build Coastguard Worker entry = entry ->Next) {
1874*3ac0a46fSAndroid Build Coastguard Worker
1875*3ac0a46fSAndroid Build Coastguard Worker _cmsOptimizationCollection *newEntry = ( _cmsOptimizationCollection *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsOptimizationCollection));
1876*3ac0a46fSAndroid Build Coastguard Worker
1877*3ac0a46fSAndroid Build Coastguard Worker if (newEntry == NULL)
1878*3ac0a46fSAndroid Build Coastguard Worker return;
1879*3ac0a46fSAndroid Build Coastguard Worker
1880*3ac0a46fSAndroid Build Coastguard Worker // We want to keep the linked list order, so this is a little bit tricky
1881*3ac0a46fSAndroid Build Coastguard Worker newEntry -> Next = NULL;
1882*3ac0a46fSAndroid Build Coastguard Worker if (Anterior)
1883*3ac0a46fSAndroid Build Coastguard Worker Anterior -> Next = newEntry;
1884*3ac0a46fSAndroid Build Coastguard Worker
1885*3ac0a46fSAndroid Build Coastguard Worker Anterior = newEntry;
1886*3ac0a46fSAndroid Build Coastguard Worker
1887*3ac0a46fSAndroid Build Coastguard Worker if (newHead.OptimizationCollection == NULL)
1888*3ac0a46fSAndroid Build Coastguard Worker newHead.OptimizationCollection = newEntry;
1889*3ac0a46fSAndroid Build Coastguard Worker }
1890*3ac0a46fSAndroid Build Coastguard Worker
1891*3ac0a46fSAndroid Build Coastguard Worker ctx ->chunks[OptimizationPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsOptimizationPluginChunkType));
1892*3ac0a46fSAndroid Build Coastguard Worker }
1893*3ac0a46fSAndroid Build Coastguard Worker
_cmsAllocOptimizationPluginChunk(struct _cmsContext_struct * ctx,const struct _cmsContext_struct * src)1894*3ac0a46fSAndroid Build Coastguard Worker void _cmsAllocOptimizationPluginChunk(struct _cmsContext_struct* ctx,
1895*3ac0a46fSAndroid Build Coastguard Worker const struct _cmsContext_struct* src)
1896*3ac0a46fSAndroid Build Coastguard Worker {
1897*3ac0a46fSAndroid Build Coastguard Worker if (src != NULL) {
1898*3ac0a46fSAndroid Build Coastguard Worker
1899*3ac0a46fSAndroid Build Coastguard Worker // Copy all linked list
1900*3ac0a46fSAndroid Build Coastguard Worker DupPluginOptimizationList(ctx, src);
1901*3ac0a46fSAndroid Build Coastguard Worker }
1902*3ac0a46fSAndroid Build Coastguard Worker else {
1903*3ac0a46fSAndroid Build Coastguard Worker static _cmsOptimizationPluginChunkType OptimizationPluginChunkType = { NULL };
1904*3ac0a46fSAndroid Build Coastguard Worker ctx ->chunks[OptimizationPlugin] = _cmsSubAllocDup(ctx ->MemPool, &OptimizationPluginChunkType, sizeof(_cmsOptimizationPluginChunkType));
1905*3ac0a46fSAndroid Build Coastguard Worker }
1906*3ac0a46fSAndroid Build Coastguard Worker }
1907*3ac0a46fSAndroid Build Coastguard Worker
1908*3ac0a46fSAndroid Build Coastguard Worker
1909*3ac0a46fSAndroid Build Coastguard Worker // Register new ways to optimize
_cmsRegisterOptimizationPlugin(cmsContext ContextID,cmsPluginBase * Data)1910*3ac0a46fSAndroid Build Coastguard Worker cmsBool _cmsRegisterOptimizationPlugin(cmsContext ContextID, cmsPluginBase* Data)
1911*3ac0a46fSAndroid Build Coastguard Worker {
1912*3ac0a46fSAndroid Build Coastguard Worker cmsPluginOptimization* Plugin = (cmsPluginOptimization*) Data;
1913*3ac0a46fSAndroid Build Coastguard Worker _cmsOptimizationPluginChunkType* ctx = ( _cmsOptimizationPluginChunkType*) _cmsContextGetClientChunk(ContextID, OptimizationPlugin);
1914*3ac0a46fSAndroid Build Coastguard Worker _cmsOptimizationCollection* fl;
1915*3ac0a46fSAndroid Build Coastguard Worker
1916*3ac0a46fSAndroid Build Coastguard Worker if (Data == NULL) {
1917*3ac0a46fSAndroid Build Coastguard Worker
1918*3ac0a46fSAndroid Build Coastguard Worker ctx->OptimizationCollection = NULL;
1919*3ac0a46fSAndroid Build Coastguard Worker return TRUE;
1920*3ac0a46fSAndroid Build Coastguard Worker }
1921*3ac0a46fSAndroid Build Coastguard Worker
1922*3ac0a46fSAndroid Build Coastguard Worker // Optimizer callback is required
1923*3ac0a46fSAndroid Build Coastguard Worker if (Plugin ->OptimizePtr == NULL) return FALSE;
1924*3ac0a46fSAndroid Build Coastguard Worker
1925*3ac0a46fSAndroid Build Coastguard Worker fl = (_cmsOptimizationCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsOptimizationCollection));
1926*3ac0a46fSAndroid Build Coastguard Worker if (fl == NULL) return FALSE;
1927*3ac0a46fSAndroid Build Coastguard Worker
1928*3ac0a46fSAndroid Build Coastguard Worker // Copy the parameters
1929*3ac0a46fSAndroid Build Coastguard Worker fl ->OptimizePtr = Plugin ->OptimizePtr;
1930*3ac0a46fSAndroid Build Coastguard Worker
1931*3ac0a46fSAndroid Build Coastguard Worker // Keep linked list
1932*3ac0a46fSAndroid Build Coastguard Worker fl ->Next = ctx->OptimizationCollection;
1933*3ac0a46fSAndroid Build Coastguard Worker
1934*3ac0a46fSAndroid Build Coastguard Worker // Set the head
1935*3ac0a46fSAndroid Build Coastguard Worker ctx ->OptimizationCollection = fl;
1936*3ac0a46fSAndroid Build Coastguard Worker
1937*3ac0a46fSAndroid Build Coastguard Worker // All is ok
1938*3ac0a46fSAndroid Build Coastguard Worker return TRUE;
1939*3ac0a46fSAndroid Build Coastguard Worker }
1940*3ac0a46fSAndroid Build Coastguard Worker
1941*3ac0a46fSAndroid Build Coastguard Worker // The entry point for LUT optimization
_cmsOptimizePipeline(cmsContext ContextID,cmsPipeline ** PtrLut,cmsUInt32Number Intent,cmsUInt32Number * InputFormat,cmsUInt32Number * OutputFormat,cmsUInt32Number * dwFlags)1942*3ac0a46fSAndroid Build Coastguard Worker cmsBool CMSEXPORT _cmsOptimizePipeline(cmsContext ContextID,
1943*3ac0a46fSAndroid Build Coastguard Worker cmsPipeline** PtrLut,
1944*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number Intent,
1945*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number* InputFormat,
1946*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number* OutputFormat,
1947*3ac0a46fSAndroid Build Coastguard Worker cmsUInt32Number* dwFlags)
1948*3ac0a46fSAndroid Build Coastguard Worker {
1949*3ac0a46fSAndroid Build Coastguard Worker _cmsOptimizationPluginChunkType* ctx = ( _cmsOptimizationPluginChunkType*) _cmsContextGetClientChunk(ContextID, OptimizationPlugin);
1950*3ac0a46fSAndroid Build Coastguard Worker _cmsOptimizationCollection* Opts;
1951*3ac0a46fSAndroid Build Coastguard Worker cmsBool AnySuccess = FALSE;
1952*3ac0a46fSAndroid Build Coastguard Worker cmsStage* mpe;
1953*3ac0a46fSAndroid Build Coastguard Worker
1954*3ac0a46fSAndroid Build Coastguard Worker // A CLUT is being asked, so force this specific optimization
1955*3ac0a46fSAndroid Build Coastguard Worker if (*dwFlags & cmsFLAGS_FORCE_CLUT) {
1956*3ac0a46fSAndroid Build Coastguard Worker
1957*3ac0a46fSAndroid Build Coastguard Worker PreOptimize(*PtrLut);
1958*3ac0a46fSAndroid Build Coastguard Worker return OptimizeByResampling(PtrLut, Intent, InputFormat, OutputFormat, dwFlags);
1959*3ac0a46fSAndroid Build Coastguard Worker }
1960*3ac0a46fSAndroid Build Coastguard Worker
1961*3ac0a46fSAndroid Build Coastguard Worker // Anything to optimize?
1962*3ac0a46fSAndroid Build Coastguard Worker if ((*PtrLut) ->Elements == NULL) {
1963*3ac0a46fSAndroid Build Coastguard Worker _cmsPipelineSetOptimizationParameters(*PtrLut, FastIdentity16, (void*) *PtrLut, NULL, NULL);
1964*3ac0a46fSAndroid Build Coastguard Worker return TRUE;
1965*3ac0a46fSAndroid Build Coastguard Worker }
1966*3ac0a46fSAndroid Build Coastguard Worker
1967*3ac0a46fSAndroid Build Coastguard Worker // Named color pipelines cannot be optimized
1968*3ac0a46fSAndroid Build Coastguard Worker for (mpe = cmsPipelineGetPtrToFirstStage(*PtrLut);
1969*3ac0a46fSAndroid Build Coastguard Worker mpe != NULL;
1970*3ac0a46fSAndroid Build Coastguard Worker mpe = cmsStageNext(mpe)) {
1971*3ac0a46fSAndroid Build Coastguard Worker if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE;
1972*3ac0a46fSAndroid Build Coastguard Worker }
1973*3ac0a46fSAndroid Build Coastguard Worker
1974*3ac0a46fSAndroid Build Coastguard Worker // Try to get rid of identities and trivial conversions.
1975*3ac0a46fSAndroid Build Coastguard Worker AnySuccess = PreOptimize(*PtrLut);
1976*3ac0a46fSAndroid Build Coastguard Worker
1977*3ac0a46fSAndroid Build Coastguard Worker // After removal do we end with an identity?
1978*3ac0a46fSAndroid Build Coastguard Worker if ((*PtrLut) ->Elements == NULL) {
1979*3ac0a46fSAndroid Build Coastguard Worker _cmsPipelineSetOptimizationParameters(*PtrLut, FastIdentity16, (void*) *PtrLut, NULL, NULL);
1980*3ac0a46fSAndroid Build Coastguard Worker return TRUE;
1981*3ac0a46fSAndroid Build Coastguard Worker }
1982*3ac0a46fSAndroid Build Coastguard Worker
1983*3ac0a46fSAndroid Build Coastguard Worker // Do not optimize, keep all precision
1984*3ac0a46fSAndroid Build Coastguard Worker if (*dwFlags & cmsFLAGS_NOOPTIMIZE)
1985*3ac0a46fSAndroid Build Coastguard Worker return FALSE;
1986*3ac0a46fSAndroid Build Coastguard Worker
1987*3ac0a46fSAndroid Build Coastguard Worker // Try plug-in optimizations
1988*3ac0a46fSAndroid Build Coastguard Worker for (Opts = ctx->OptimizationCollection;
1989*3ac0a46fSAndroid Build Coastguard Worker Opts != NULL;
1990*3ac0a46fSAndroid Build Coastguard Worker Opts = Opts ->Next) {
1991*3ac0a46fSAndroid Build Coastguard Worker
1992*3ac0a46fSAndroid Build Coastguard Worker // If one schema succeeded, we are done
1993*3ac0a46fSAndroid Build Coastguard Worker if (Opts ->OptimizePtr(PtrLut, Intent, InputFormat, OutputFormat, dwFlags)) {
1994*3ac0a46fSAndroid Build Coastguard Worker
1995*3ac0a46fSAndroid Build Coastguard Worker return TRUE; // Optimized!
1996*3ac0a46fSAndroid Build Coastguard Worker }
1997*3ac0a46fSAndroid Build Coastguard Worker }
1998*3ac0a46fSAndroid Build Coastguard Worker
1999*3ac0a46fSAndroid Build Coastguard Worker // Try built-in optimizations
2000*3ac0a46fSAndroid Build Coastguard Worker for (Opts = DefaultOptimization;
2001*3ac0a46fSAndroid Build Coastguard Worker Opts != NULL;
2002*3ac0a46fSAndroid Build Coastguard Worker Opts = Opts ->Next) {
2003*3ac0a46fSAndroid Build Coastguard Worker
2004*3ac0a46fSAndroid Build Coastguard Worker if (Opts ->OptimizePtr(PtrLut, Intent, InputFormat, OutputFormat, dwFlags)) {
2005*3ac0a46fSAndroid Build Coastguard Worker
2006*3ac0a46fSAndroid Build Coastguard Worker return TRUE;
2007*3ac0a46fSAndroid Build Coastguard Worker }
2008*3ac0a46fSAndroid Build Coastguard Worker }
2009*3ac0a46fSAndroid Build Coastguard Worker
2010*3ac0a46fSAndroid Build Coastguard Worker // Only simple optimizations succeeded
2011*3ac0a46fSAndroid Build Coastguard Worker return AnySuccess;
2012*3ac0a46fSAndroid Build Coastguard Worker }
2013*3ac0a46fSAndroid Build Coastguard Worker
2014*3ac0a46fSAndroid Build Coastguard Worker
2015*3ac0a46fSAndroid Build Coastguard Worker
2016