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