2 //---------------------------------------------------------------------------------
4 // Little Color Management System
5 // Copyright (c) 1998-2011 Marti Maria Saguer
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:
14 // The above copyright notice and this permission notice shall be included in
15 // all copies or substantial portions of the Software.
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.
25 //---------------------------------------------------------------------------------
28 #include "lcms2_internal.h"
31 //----------------------------------------------------------------------------------
33 // Optimization for 8 bits, Shaper-CLUT (3 inputs only)
38 const cmsInterpParams
* p
; // Tetrahedrical interpolation parameters. This is a not-owned pointer.
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
47 // Generic optimization for 16 bits Shaper-CLUT-Shaper (any inputs)
56 _cmsInterpFn16 EvalCurveIn16
[MAX_INPUT_DIMENSIONS
]; // The maximum number of input channels is known in advance
57 cmsInterpParams
* ParamsCurveIn16
[MAX_INPUT_DIMENSIONS
];
59 _cmsInterpFn16 EvalCLUT
; // The evaluator for 3D grid
60 const cmsInterpParams
* CLUTparams
; // (not-owned pointer)
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)
70 // Optimization for matrix-shaper in 8 bits. Numbers are operated in n.14 signed, tables are stored in 1.14 fixed
72 typedef cmsInt32Number cmsS1Fixed14Number
; // Note that this may hold more than 16 bits!
74 #define DOUBLE_TO_1FIXED14(x) ((cmsS1Fixed14Number) floor((x) * 16384.0 + 0.5))
80 cmsS1Fixed14Number Shaper1R
[256]; // from 0..255 to 1.14 (0.0...1.0)
81 cmsS1Fixed14Number Shaper1G
[256];
82 cmsS1Fixed14Number Shaper1B
[256];
84 cmsS1Fixed14Number Mat
[3][3]; // n.14 to n.14 (needs a saturation after that)
85 cmsS1Fixed14Number Off
[3];
87 cmsUInt16Number Shaper2R
[16385]; // 1.14 to 0..255
88 cmsUInt16Number Shaper2G
[16385];
89 cmsUInt16Number Shaper2B
[16385];
93 // Curves, optimization is shared between 8 and 16 bits
98 int nCurves
; // Number of curves
99 int nElements
; // Elements in curves
100 cmsUInt16Number
** Curves
; // Points to a dynamically allocated array
105 // Simple optimizations ----------------------------------------------------------------------------------------------------------
108 // Remove an element in linked chain
110 void _RemoveElement(cmsStage
** head
)
112 cmsStage
* mpe
= *head
;
113 cmsStage
* next
= mpe
->Next
;
118 // Remove all identities in chain. Note that pt actually is a double pointer to the element that holds the pointer.
120 cmsBool
_Remove1Op(cmsPipeline
* Lut
, cmsStageSignature UnaryOp
)
122 cmsStage
** pt
= &Lut
->Elements
;
123 cmsBool AnyOpt
= FALSE
;
125 while (*pt
!= NULL
) {
127 if ((*pt
) ->Implements
== UnaryOp
) {
132 pt
= &((*pt
) -> Next
);
138 // Same, but only if two adjacent elements are found
140 cmsBool
_Remove2Op(cmsPipeline
* Lut
, cmsStageSignature Op1
, cmsStageSignature Op2
)
144 cmsBool AnyOpt
= FALSE
;
146 pt1
= &Lut
->Elements
;
147 if (*pt1
== NULL
) return AnyOpt
;
149 while (*pt1
!= NULL
) {
151 pt2
= &((*pt1
) -> Next
);
152 if (*pt2
== NULL
) return AnyOpt
;
154 if ((*pt1
) ->Implements
== Op1
&& (*pt2
) ->Implements
== Op2
) {
160 pt1
= &((*pt1
) -> Next
);
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.
169 cmsBool
PreOptimize(cmsPipeline
* Lut
)
171 cmsBool AnyOpt
= FALSE
, Opt
;
177 // Remove all identities
178 Opt
|= _Remove1Op(Lut
, cmsSigIdentityElemType
);
180 // Remove XYZ2Lab followed by Lab2XYZ
181 Opt
|= _Remove2Op(Lut
, cmsSigXYZ2LabElemType
, cmsSigLab2XYZElemType
);
183 // Remove Lab2XYZ followed by XYZ2Lab
184 Opt
|= _Remove2Op(Lut
, cmsSigLab2XYZElemType
, cmsSigXYZ2LabElemType
);
186 // Remove V4 to V2 followed by V2 to V4
187 Opt
|= _Remove2Op(Lut
, cmsSigLabV4toV2
, cmsSigLabV2toV4
);
189 // Remove V2 to V4 followed by V4 to V2
190 Opt
|= _Remove2Op(Lut
, cmsSigLabV2toV4
, cmsSigLabV4toV2
);
192 // Remove float pcs Lab conversions
193 Opt
|= _Remove2Op(Lut
, cmsSigLab2FloatPCS
, cmsSigFloatPCS2Lab
);
195 // Remove float pcs Lab conversions
196 Opt
|= _Remove2Op(Lut
, cmsSigXYZ2FloatPCS
, cmsSigFloatPCS2XYZ
);
198 if (Opt
) AnyOpt
= TRUE
;
206 void Eval16nop1D(register const cmsUInt16Number Input
[],
207 register cmsUInt16Number Output
[],
208 register const struct _cms_interp_struc
* p
)
210 Output
[0] = Input
[0];
212 cmsUNUSED_PARAMETER(p
);
216 void PrelinEval16(register const cmsUInt16Number Input
[],
217 register cmsUInt16Number Output
[],
218 register const void* D
)
220 Prelin16Data
* p16
= (Prelin16Data
*) D
;
221 cmsUInt16Number StageABC
[MAX_INPUT_DIMENSIONS
];
222 cmsUInt16Number StageDEF
[cmsMAXCHANNELS
];
225 for (i
=0; i
< p16
->nInputs
; i
++) {
227 p16
->EvalCurveIn16
[i
](&Input
[i
], &StageABC
[i
], p16
->ParamsCurveIn16
[i
]);
230 p16
->EvalCLUT(StageABC
, StageDEF
, p16
->CLUTparams
);
232 for (i
=0; i
< p16
->nOutputs
; i
++) {
234 p16
->EvalCurveOut16
[i
](&StageDEF
[i
], &Output
[i
], p16
->ParamsCurveOut16
[i
]);
240 void PrelinOpt16free(cmsContext ContextID
, void* ptr
)
242 Prelin16Data
* p16
= (Prelin16Data
*) ptr
;
244 _cmsFree(ContextID
, p16
->EvalCurveOut16
);
245 _cmsFree(ContextID
, p16
->ParamsCurveOut16
);
247 _cmsFree(ContextID
, p16
);
251 void* Prelin16dup(cmsContext ContextID
, const void* ptr
)
253 Prelin16Data
* p16
= (Prelin16Data
*) ptr
;
254 Prelin16Data
* Duped
= _cmsDupMem(ContextID
, p16
, sizeof(Prelin16Data
));
256 if (Duped
== NULL
) return NULL
;
258 Duped
->EvalCurveOut16
= _cmsDupMem(ContextID
, p16
->EvalCurveOut16
, p16
->nOutputs
* sizeof(_cmsInterpFn16
));
259 Duped
->ParamsCurveOut16
= _cmsDupMem(ContextID
, p16
->ParamsCurveOut16
, p16
->nOutputs
* sizeof(cmsInterpParams
* ));
266 Prelin16Data
* PrelinOpt16alloc(cmsContext ContextID
,
267 const cmsInterpParams
* ColorMap
,
268 int nInputs
, cmsToneCurve
** In
,
269 int nOutputs
, cmsToneCurve
** Out
)
272 Prelin16Data
* p16
= _cmsMallocZero(ContextID
, sizeof(Prelin16Data
));
273 if (p16
== NULL
) return NULL
;
275 p16
->nInputs
= nInputs
;
276 p16
-> nOutputs
= nOutputs
;
279 for (i
=0; i
< nInputs
; i
++) {
282 p16
-> ParamsCurveIn16
[i
] = NULL
;
283 p16
-> EvalCurveIn16
[i
] = Eval16nop1D
;
287 p16
-> ParamsCurveIn16
[i
] = In
[i
] ->InterpParams
;
288 p16
-> EvalCurveIn16
[i
] = p16
->ParamsCurveIn16
[i
]->Interpolation
.Lerp16
;
292 p16
->CLUTparams
= ColorMap
;
293 p16
->EvalCLUT
= ColorMap
->Interpolation
.Lerp16
;
296 p16
-> EvalCurveOut16
= (_cmsInterpFn16
*) _cmsCalloc(ContextID
, nOutputs
, sizeof(_cmsInterpFn16
));
297 p16
-> ParamsCurveOut16
= (cmsInterpParams
**) _cmsCalloc(ContextID
, nOutputs
, sizeof(cmsInterpParams
* ));
299 for (i
=0; i
< nOutputs
; i
++) {
302 p16
->ParamsCurveOut16
[i
] = NULL
;
303 p16
-> EvalCurveOut16
[i
] = Eval16nop1D
;
307 p16
->ParamsCurveOut16
[i
] = Out
[i
] ->InterpParams
;
308 p16
-> EvalCurveOut16
[i
] = p16
->ParamsCurveOut16
[i
]->Interpolation
.Lerp16
;
317 // Resampling ---------------------------------------------------------------------------------
319 #define PRELINEARIZATION_POINTS 4096
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.
324 int XFormSampler16(register const cmsUInt16Number In
[], register cmsUInt16Number Out
[], register void* Cargo
)
326 cmsPipeline
* Lut
= (cmsPipeline
*) Cargo
;
327 cmsFloat32Number InFloat
[cmsMAXCHANNELS
], OutFloat
[cmsMAXCHANNELS
];
330 _cmsAssert(Lut
-> InputChannels
< cmsMAXCHANNELS
);
331 _cmsAssert(Lut
-> OutputChannels
< cmsMAXCHANNELS
);
333 // From 16 bit to floating point
334 for (i
=0; i
< Lut
->InputChannels
; i
++)
335 InFloat
[i
] = (cmsFloat32Number
) (In
[i
] / 65535.0);
337 // Evaluate in floating point
338 cmsPipelineEvalFloat(InFloat
, OutFloat
, Lut
);
340 // Back to 16 bits representation
341 for (i
=0; i
< Lut
->OutputChannels
; i
++)
342 Out
[i
] = _cmsQuickSaturateWord(OutFloat
[i
] * 65535.0);
348 // Try to see if the curves of a given MPE are linear
350 cmsBool
AllCurvesAreLinear(cmsStage
* mpe
)
352 cmsToneCurve
** Curves
;
353 cmsUInt32Number i
, n
;
355 Curves
= _cmsStageGetPtrToCurveSet(mpe
);
356 if (Curves
== NULL
) return FALSE
;
358 n
= cmsStageOutputChannels(mpe
);
360 for (i
=0; i
< n
; i
++) {
361 if (!cmsIsToneCurveLinear(Curves
[i
])) return FALSE
;
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
370 cmsBool
PatchLUT(cmsStage
* CLUT
, cmsUInt16Number At
[], cmsUInt16Number Value
[],
371 int nChannelsOut
, int nChannelsIn
)
373 _cmsStageCLutData
* Grid
= (_cmsStageCLutData
*) CLUT
->Data
;
374 cmsInterpParams
* p16
= Grid
->Params
;
375 cmsFloat64Number px
, py
, pz
, pw
;
379 if (CLUT
-> Type
!= cmsSigCLutElemType
) {
380 cmsSignalError(CLUT
->ContextID
, cmsERROR_INTERNAL
, "(internal) Attempt to PatchLUT on non-lut stage");
384 if (nChannelsIn
== 4) {
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;
391 x0
= (int) floor(px
);
392 y0
= (int) floor(py
);
393 z0
= (int) floor(pz
);
394 w0
= (int) floor(pw
);
396 if (((px
- x0
) != 0) ||
399 ((pw
- w0
) != 0)) return FALSE
; // Not on exact node
401 index
= p16
-> opta
[3] * x0
+
402 p16
-> opta
[2] * y0
+
403 p16
-> opta
[1] * z0
+
407 if (nChannelsIn
== 3) {
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;
413 x0
= (int) floor(px
);
414 y0
= (int) floor(py
);
415 z0
= (int) floor(pz
);
417 if (((px
- x0
) != 0) ||
419 ((pz
- z0
) != 0)) return FALSE
; // Not on exact node
421 index
= p16
-> opta
[2] * x0
+
422 p16
-> opta
[1] * y0
+
426 if (nChannelsIn
== 1) {
428 px
= ((cmsFloat64Number
) At
[0] * (p16
->Domain
[0])) / 65535.0;
430 x0
= (int) floor(px
);
432 if (((px
- x0
) != 0)) return FALSE
; // Not on exact node
434 index
= p16
-> opta
[0] * x0
;
437 cmsSignalError(CLUT
->ContextID
, cmsERROR_INTERNAL
, "(internal) %d Channels are not supported on PatchLUT", nChannelsIn
);
441 for (i
=0; i
< nChannelsOut
; i
++)
442 Grid
-> Tab
.T
[index
+ i
] = Value
[i
];
447 // Auxiliar, to see if two values are equal or very different
449 cmsBool
WhitesAreEqual(int n
, cmsUInt16Number White1
[], cmsUInt16Number White2
[] )
453 for (i
=0; i
< n
; i
++) {
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
;
462 // Locate the node for the white point and fix it to pure white in order to avoid scum dot.
464 cmsBool
FixWhiteMisalignment(cmsPipeline
* Lut
, cmsColorSpaceSignature EntryColorSpace
, cmsColorSpaceSignature ExitColorSpace
)
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
;
471 if (!_cmsEndPointsBySpace(EntryColorSpace
,
472 &WhitePointIn
, NULL
, &nIns
)) return FALSE
;
474 if (!_cmsEndPointsBySpace(ExitColorSpace
,
475 &WhitePointOut
, NULL
, &nOuts
)) return FALSE
;
477 // It needs to be fixed?
478 if (Lut
->InputChannels
!= nIns
) return FALSE
;
479 if (Lut
->OutputChannels
!= nOuts
) return FALSE
;
481 cmsPipelineEval16(WhitePointIn
, ObtainedOut
, Lut
);
483 if (WhitesAreEqual(nOuts
, WhitePointOut
, ObtainedOut
)) return TRUE
; // whites already match
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
))
492 // We need to interpolate white points of both, pre and post curves
495 cmsToneCurve
** Curves
= _cmsStageGetPtrToCurveSet(PreLin
);
497 for (i
=0; i
< nIns
; i
++) {
498 WhiteIn
[i
] = cmsEvalToneCurve16(Curves
[i
], WhitePointIn
[i
]);
502 for (i
=0; i
< nIns
; i
++)
503 WhiteIn
[i
] = WhitePointIn
[i
];
506 // If any post-linearization, we need to find how is represented white before the curve, do
507 // a reverse interpolation in this case.
510 cmsToneCurve
** Curves
= _cmsStageGetPtrToCurveSet(PostLin
);
512 for (i
=0; i
< nOuts
; i
++) {
514 cmsToneCurve
* InversePostLin
= cmsReverseToneCurve(Curves
[i
]);
515 WhiteOut
[i
] = cmsEvalToneCurve16(InversePostLin
, WhitePointOut
[i
]);
516 cmsFreeToneCurve(InversePostLin
);
520 for (i
=0; i
< nOuts
; i
++)
521 WhiteOut
[i
] = WhitePointOut
[i
];
524 // Ok, proceed with patching. May fail and we don't care if it fails
525 PatchLUT(CLUT
, WhiteIn
, WhiteOut
, nOuts
, nIns
);
530 // -----------------------------------------------------------------------------------------------------------------------------------------------
531 // This function creates simple LUT from complex ones. The generated LUT has an optional set of
532 // prelinearization curves, a CLUT of nGridPoints and optional postlinearization tables.
533 // These curves have to exist in the original LUT in order to be used in the simplified output.
534 // Caller may also use the flags to allow this feature.
535 // LUTS with all curves will be simplified to a single curve. Parametric curves are lost.
536 // This function should be used on 16-bits LUTS only, as floating point losses precision when simplified
537 // -----------------------------------------------------------------------------------------------------------------------------------------------
540 cmsBool
OptimizeByResampling(cmsPipeline
** Lut
, cmsUInt32Number Intent
, cmsUInt32Number
* InputFormat
, cmsUInt32Number
* OutputFormat
, cmsUInt32Number
* dwFlags
)
542 cmsPipeline
* Src
= NULL
;
543 cmsPipeline
* Dest
= NULL
;
546 cmsStage
*KeepPreLin
= NULL
, *KeepPostLin
= NULL
;
548 cmsColorSpaceSignature ColorSpace
, OutputColorSpace
;
549 cmsStage
*NewPreLin
= NULL
;
550 cmsStage
*NewPostLin
= NULL
;
551 _cmsStageCLutData
* DataCLUT
;
552 cmsToneCurve
** DataSetIn
;
553 cmsToneCurve
** DataSetOut
;
556 // This is a loosy optimization! does not apply in floating-point cases
557 if (_cmsFormatterIsFloat(*InputFormat
) || _cmsFormatterIsFloat(*OutputFormat
)) return FALSE
;
559 ColorSpace
= _cmsICCcolorSpace(T_COLORSPACE(*InputFormat
));
560 OutputColorSpace
= _cmsICCcolorSpace(T_COLORSPACE(*OutputFormat
));
561 nGridPoints
= _cmsReasonableGridpointsByColorspace(ColorSpace
, *dwFlags
);
563 // For empty LUTs, 2 points are enough
564 if (cmsPipelineStageCount(*Lut
) == 0)
569 // Named color pipelines cannot be optimized either
570 for (mpe
= cmsPipelineGetPtrToFirstStage(Src
);
572 mpe
= cmsStageNext(mpe
)) {
573 if (cmsStageType(mpe
) == cmsSigNamedColorElemType
) return FALSE
;
576 // Allocate an empty LUT
577 Dest
= cmsPipelineAlloc(Src
->ContextID
, Src
->InputChannels
, Src
->OutputChannels
);
578 if (!Dest
) return FALSE
;
580 // Prelinearization tables are kept unless indicated by flags
581 if (*dwFlags
& cmsFLAGS_CLUT_PRE_LINEARIZATION
) {
583 // Get a pointer to the prelinearization element
584 cmsStage
* PreLin
= cmsPipelineGetPtrToFirstStage(Src
);
587 if (PreLin
->Type
== cmsSigCurveSetElemType
) {
589 // Maybe this is a linear tram, so we can avoid the whole stuff
590 if (!AllCurvesAreLinear(PreLin
)) {
592 // All seems ok, proceed.
593 NewPreLin
= cmsStageDup(PreLin
);
594 if(!cmsPipelineInsertStage(Dest
, cmsAT_BEGIN
, NewPreLin
))
597 // Remove prelinearization. Since we have duplicated the curve
598 // in destination LUT, the sampling shoud be applied after this stage.
599 cmsPipelineUnlinkStage(Src
, cmsAT_BEGIN
, &KeepPreLin
);
605 CLUT
= cmsStageAllocCLut16bit(Src
->ContextID
, nGridPoints
, Src
->InputChannels
, Src
->OutputChannels
, NULL
);
606 if (CLUT
== NULL
) return FALSE
;
608 // Add the CLUT to the destination LUT
609 if (!cmsPipelineInsertStage(Dest
, cmsAT_END
, CLUT
)) {
613 // Postlinearization tables are kept unless indicated by flags
614 if (*dwFlags
& cmsFLAGS_CLUT_POST_LINEARIZATION
) {
616 // Get a pointer to the postlinearization if present
617 cmsStage
* PostLin
= cmsPipelineGetPtrToLastStage(Src
);
620 if (cmsStageType(PostLin
) == cmsSigCurveSetElemType
) {
622 // Maybe this is a linear tram, so we can avoid the whole stuff
623 if (!AllCurvesAreLinear(PostLin
)) {
625 // All seems ok, proceed.
626 NewPostLin
= cmsStageDup(PostLin
);
627 if (!cmsPipelineInsertStage(Dest
, cmsAT_END
, NewPostLin
))
630 // In destination LUT, the sampling shoud be applied after this stage.
631 cmsPipelineUnlinkStage(Src
, cmsAT_END
, &KeepPostLin
);
636 // Now its time to do the sampling. We have to ignore pre/post linearization
637 // The source LUT whithout pre/post curves is passed as parameter.
638 if (!cmsStageSampleCLut16bit(CLUT
, XFormSampler16
, (void*) Src
, 0)) {
640 // Ops, something went wrong, Restore stages
641 if (KeepPreLin
!= NULL
) {
642 if (!cmsPipelineInsertStage(Src
, cmsAT_BEGIN
, KeepPreLin
)) {
643 _cmsAssert(0); // This never happens
646 if (KeepPostLin
!= NULL
) {
647 if (!cmsPipelineInsertStage(Src
, cmsAT_END
, KeepPostLin
)) {
648 _cmsAssert(0); // This never happens
651 cmsPipelineFree(Dest
);
657 if (KeepPreLin
!= NULL
) cmsStageFree(KeepPreLin
);
658 if (KeepPostLin
!= NULL
) cmsStageFree(KeepPostLin
);
659 cmsPipelineFree(Src
);
661 DataCLUT
= (_cmsStageCLutData
*) CLUT
->Data
;
663 if (NewPreLin
== NULL
) DataSetIn
= NULL
;
664 else DataSetIn
= ((_cmsStageToneCurvesData
*) NewPreLin
->Data
) ->TheCurves
;
666 if (NewPostLin
== NULL
) DataSetOut
= NULL
;
667 else DataSetOut
= ((_cmsStageToneCurvesData
*) NewPostLin
->Data
) ->TheCurves
;
670 if (DataSetIn
== NULL
&& DataSetOut
== NULL
) {
672 _cmsPipelineSetOptimizationParameters(Dest
, (_cmsOPTeval16Fn
) DataCLUT
->Params
->Interpolation
.Lerp16
, DataCLUT
->Params
, NULL
, NULL
);
676 p16
= PrelinOpt16alloc(Dest
->ContextID
,
678 Dest
->InputChannels
,
680 Dest
->OutputChannels
,
683 _cmsPipelineSetOptimizationParameters(Dest
, PrelinEval16
, (void*) p16
, PrelinOpt16free
, Prelin16dup
);
687 // Don't fix white on absolute colorimetric
688 if (Intent
== INTENT_ABSOLUTE_COLORIMETRIC
)
689 *dwFlags
|= cmsFLAGS_NOWHITEONWHITEFIXUP
;
691 if (!(*dwFlags
& cmsFLAGS_NOWHITEONWHITEFIXUP
)) {
693 FixWhiteMisalignment(Dest
, ColorSpace
, OutputColorSpace
);
699 cmsUNUSED_PARAMETER(Intent
);
703 // -----------------------------------------------------------------------------------------------------------------------------------------------
704 // Fixes the gamma balancing of transform. This is described in my paper "Prelinearization Stages on
705 // Color-Management Application-Specific Integrated Circuits (ASICs)" presented at NIP24. It only works
706 // for RGB transforms. See the paper for more details
707 // -----------------------------------------------------------------------------------------------------------------------------------------------
710 // Normalize endpoints by slope limiting max and min. This assures endpoints as well.
711 // Descending curves are handled as well.
713 void SlopeLimiting(cmsToneCurve
* g
)
715 int BeginVal
, EndVal
;
716 int AtBegin
= (int) floor((cmsFloat64Number
) g
->nEntries
* 0.02 + 0.5); // Cutoff at 2%
717 int AtEnd
= g
->nEntries
- AtBegin
- 1; // And 98%
718 cmsFloat64Number Val
, Slope
, beta
;
721 if (cmsIsToneCurveDescending(g
)) {
722 BeginVal
= 0xffff; EndVal
= 0;
725 BeginVal
= 0; EndVal
= 0xffff;
728 // Compute slope and offset for begin of curve
729 Val
= g
->Table16
[AtBegin
];
730 Slope
= (Val
- BeginVal
) / AtBegin
;
731 beta
= Val
- Slope
* AtBegin
;
733 for (i
=0; i
< AtBegin
; i
++)
734 g
->Table16
[i
] = _cmsQuickSaturateWord(i
* Slope
+ beta
);
736 // Compute slope and offset for the end
737 Val
= g
->Table16
[AtEnd
];
738 Slope
= (EndVal
- Val
) / AtBegin
; // AtBegin holds the X interval, which is same in both cases
739 beta
= Val
- Slope
* AtEnd
;
741 for (i
= AtEnd
; i
< (int) g
->nEntries
; i
++)
742 g
->Table16
[i
] = _cmsQuickSaturateWord(i
* Slope
+ beta
);
746 // Precomputes tables for 8-bit on input devicelink.
748 Prelin8Data
* PrelinOpt8alloc(cmsContext ContextID
, const cmsInterpParams
* p
, cmsToneCurve
* G
[3])
751 cmsUInt16Number Input
[3];
752 cmsS15Fixed16Number v1
, v2
, v3
;
755 p8
= _cmsMallocZero(ContextID
, sizeof(Prelin8Data
));
756 if (p8
== NULL
) return NULL
;
758 // Since this only works for 8 bit input, values comes always as x * 257,
759 // we can safely take msb byte (x << 8 + x)
761 for (i
=0; i
< 256; i
++) {
765 // Get 16-bit representation
766 Input
[0] = cmsEvalToneCurve16(G
[0], FROM_8_TO_16(i
));
767 Input
[1] = cmsEvalToneCurve16(G
[1], FROM_8_TO_16(i
));
768 Input
[2] = cmsEvalToneCurve16(G
[2], FROM_8_TO_16(i
));
771 Input
[0] = FROM_8_TO_16(i
);
772 Input
[1] = FROM_8_TO_16(i
);
773 Input
[2] = FROM_8_TO_16(i
);
777 // Move to 0..1.0 in fixed domain
778 v1
= _cmsToFixedDomain(Input
[0] * p
-> Domain
[0]);
779 v2
= _cmsToFixedDomain(Input
[1] * p
-> Domain
[1]);
780 v3
= _cmsToFixedDomain(Input
[2] * p
-> Domain
[2]);
782 // Store the precalculated table of nodes
783 p8
->X0
[i
] = (p
->opta
[2] * FIXED_TO_INT(v1
));
784 p8
->Y0
[i
] = (p
->opta
[1] * FIXED_TO_INT(v2
));
785 p8
->Z0
[i
] = (p
->opta
[0] * FIXED_TO_INT(v3
));
787 // Store the precalculated table of offsets
788 p8
->rx
[i
] = (cmsUInt16Number
) FIXED_REST_TO_INT(v1
);
789 p8
->ry
[i
] = (cmsUInt16Number
) FIXED_REST_TO_INT(v2
);
790 p8
->rz
[i
] = (cmsUInt16Number
) FIXED_REST_TO_INT(v3
);
793 p8
->ContextID
= ContextID
;
800 void Prelin8free(cmsContext ContextID
, void* ptr
)
802 _cmsFree(ContextID
, ptr
);
806 void* Prelin8dup(cmsContext ContextID
, const void* ptr
)
808 return _cmsDupMem(ContextID
, ptr
, sizeof(Prelin8Data
));
813 // A optimized interpolation for 8-bit input.
814 #define DENS(i,j,k) (LutTable[(i)+(j)+(k)+OutChan])
816 void PrelinEval8(register const cmsUInt16Number Input
[],
817 register cmsUInt16Number Output
[],
818 register const void* D
)
821 cmsUInt8Number r
, g
, b
;
822 cmsS15Fixed16Number rx
, ry
, rz
;
823 cmsS15Fixed16Number c0
, c1
, c2
, c3
, Rest
;
825 register cmsS15Fixed16Number X0
, X1
, Y0
, Y1
, Z0
, Z1
;
826 Prelin8Data
* p8
= (Prelin8Data
*) D
;
827 register const cmsInterpParams
* p
= p8
->p
;
828 int TotalOut
= p
-> nOutputs
;
829 const cmsUInt16Number
* LutTable
= p
-> Table
;
843 X1
= X0
+ ((rx
== 0) ? 0 : p
->opta
[2]);
844 Y1
= Y0
+ ((ry
== 0) ? 0 : p
->opta
[1]);
845 Z1
= Z0
+ ((rz
== 0) ? 0 : p
->opta
[0]);
848 // These are the 6 Tetrahedral
849 for (OutChan
=0; OutChan
< TotalOut
; OutChan
++) {
851 c0
= DENS(X0
, Y0
, Z0
);
853 if (rx
>= ry
&& ry
>= rz
)
855 c1
= DENS(X1
, Y0
, Z0
) - c0
;
856 c2
= DENS(X1
, Y1
, Z0
) - DENS(X1
, Y0
, Z0
);
857 c3
= DENS(X1
, Y1
, Z1
) - DENS(X1
, Y1
, Z0
);
860 if (rx
>= rz
&& rz
>= ry
)
862 c1
= DENS(X1
, Y0
, Z0
) - c0
;
863 c2
= DENS(X1
, Y1
, Z1
) - DENS(X1
, Y0
, Z1
);
864 c3
= DENS(X1
, Y0
, Z1
) - DENS(X1
, Y0
, Z0
);
867 if (rz
>= rx
&& rx
>= ry
)
869 c1
= DENS(X1
, Y0
, Z1
) - DENS(X0
, Y0
, Z1
);
870 c2
= DENS(X1
, Y1
, Z1
) - DENS(X1
, Y0
, Z1
);
871 c3
= DENS(X0
, Y0
, Z1
) - c0
;
874 if (ry
>= rx
&& rx
>= rz
)
876 c1
= DENS(X1
, Y1
, Z0
) - DENS(X0
, Y1
, Z0
);
877 c2
= DENS(X0
, Y1
, Z0
) - c0
;
878 c3
= DENS(X1
, Y1
, Z1
) - DENS(X1
, Y1
, Z0
);
881 if (ry
>= rz
&& rz
>= rx
)
883 c1
= DENS(X1
, Y1
, Z1
) - DENS(X0
, Y1
, Z1
);
884 c2
= DENS(X0
, Y1
, Z0
) - c0
;
885 c3
= DENS(X0
, Y1
, Z1
) - DENS(X0
, Y1
, Z0
);
888 if (rz
>= ry
&& ry
>= rx
)
890 c1
= DENS(X1
, Y1
, Z1
) - DENS(X0
, Y1
, Z1
);
891 c2
= DENS(X0
, Y1
, Z1
) - DENS(X0
, Y0
, Z1
);
892 c3
= DENS(X0
, Y0
, Z1
) - c0
;
899 Rest
= c1
* rx
+ c2
* ry
+ c3
* rz
+ 0x8001;
900 Output
[OutChan
] = (cmsUInt16Number
)c0
+ ((Rest
+ (Rest
>>16))>>16);
908 // Curves that contain wide empty areas are not optimizeable
910 cmsBool
IsDegenerated(const cmsToneCurve
* g
)
912 int i
, Zeros
= 0, Poles
= 0;
913 int nEntries
= g
->nEntries
;
915 for (i
=0; i
< nEntries
; i
++) {
917 if (g
->Table16
[i
] == 0x0000) Zeros
++;
918 if (g
->Table16
[i
] == 0xffff) Poles
++;
921 if (Zeros
== 1 && Poles
== 1) return FALSE
; // For linear tables
922 if (Zeros
> (nEntries
/ 4)) return TRUE
; // Degenerated, mostly zeros
923 if (Poles
> (nEntries
/ 4)) return TRUE
; // Degenerated, mostly poles
928 // --------------------------------------------------------------------------------------------------------------
929 // We need xput over here
932 cmsBool
OptimizeByComputingLinearization(cmsPipeline
** Lut
, cmsUInt32Number Intent
, cmsUInt32Number
* InputFormat
, cmsUInt32Number
* OutputFormat
, cmsUInt32Number
* dwFlags
)
934 cmsPipeline
* OriginalLut
;
936 cmsToneCurve
*Trans
[cmsMAXCHANNELS
], *TransReverse
[cmsMAXCHANNELS
];
937 cmsUInt32Number t
, i
;
938 cmsFloat32Number v
, In
[cmsMAXCHANNELS
], Out
[cmsMAXCHANNELS
];
939 cmsBool lIsSuitable
, lIsLinear
;
940 cmsPipeline
* OptimizedLUT
= NULL
, *LutPlusCurves
= NULL
;
941 cmsStage
* OptimizedCLUTmpe
;
942 cmsColorSpaceSignature ColorSpace
, OutputColorSpace
;
943 cmsStage
* OptimizedPrelinMpe
;
945 cmsToneCurve
** OptimizedPrelinCurves
;
946 _cmsStageCLutData
* OptimizedPrelinCLUT
;
949 // This is a loosy optimization! does not apply in floating-point cases
950 if (_cmsFormatterIsFloat(*InputFormat
) || _cmsFormatterIsFloat(*OutputFormat
)) return FALSE
;
953 if (T_COLORSPACE(*InputFormat
) != PT_RGB
) return FALSE
;
954 if (T_COLORSPACE(*OutputFormat
) != PT_RGB
) return FALSE
;
957 // On 16 bits, user has to specify the feature
958 if (!_cmsFormatterIs8bit(*InputFormat
)) {
959 if (!(*dwFlags
& cmsFLAGS_CLUT_PRE_LINEARIZATION
)) return FALSE
;
964 // Named color pipelines cannot be optimized either
965 for (mpe
= cmsPipelineGetPtrToFirstStage(OriginalLut
);
967 mpe
= cmsStageNext(mpe
)) {
968 if (cmsStageType(mpe
) == cmsSigNamedColorElemType
) return FALSE
;
971 ColorSpace
= _cmsICCcolorSpace(T_COLORSPACE(*InputFormat
));
972 OutputColorSpace
= _cmsICCcolorSpace(T_COLORSPACE(*OutputFormat
));
973 nGridPoints
= _cmsReasonableGridpointsByColorspace(ColorSpace
, *dwFlags
);
975 // Empty gamma containers
976 memset(Trans
, 0, sizeof(Trans
));
977 memset(TransReverse
, 0, sizeof(TransReverse
));
979 for (t
= 0; t
< OriginalLut
->InputChannels
; t
++) {
980 Trans
[t
] = cmsBuildTabulatedToneCurve16(OriginalLut
->ContextID
, PRELINEARIZATION_POINTS
, NULL
);
981 if (Trans
[t
] == NULL
) goto Error
;
984 // Populate the curves
985 for (i
=0; i
< PRELINEARIZATION_POINTS
; i
++) {
987 v
= (cmsFloat32Number
) ((cmsFloat64Number
) i
/ (PRELINEARIZATION_POINTS
- 1));
989 // Feed input with a gray ramp
990 for (t
=0; t
< OriginalLut
->InputChannels
; t
++)
993 // Evaluate the gray value
994 cmsPipelineEvalFloat(In
, Out
, OriginalLut
);
996 // Store result in curve
997 for (t
=0; t
< OriginalLut
->InputChannels
; t
++)
998 Trans
[t
] ->Table16
[i
] = _cmsQuickSaturateWord(Out
[t
] * 65535.0);
1001 // Slope-limit the obtained curves
1002 for (t
= 0; t
< OriginalLut
->InputChannels
; t
++)
1003 SlopeLimiting(Trans
[t
]);
1005 // Check for validity
1008 for (t
=0; (lIsSuitable
&& (t
< OriginalLut
->InputChannels
)); t
++) {
1010 // Exclude if already linear
1011 if (!cmsIsToneCurveLinear(Trans
[t
]))
1014 // Exclude if non-monotonic
1015 if (!cmsIsToneCurveMonotonic(Trans
[t
]))
1016 lIsSuitable
= FALSE
;
1018 if (IsDegenerated(Trans
[t
]))
1019 lIsSuitable
= FALSE
;
1022 // If it is not suitable, just quit
1023 if (!lIsSuitable
) goto Error
;
1025 // Invert curves if possible
1026 for (t
= 0; t
< OriginalLut
->InputChannels
; t
++) {
1027 TransReverse
[t
] = cmsReverseToneCurveEx(PRELINEARIZATION_POINTS
, Trans
[t
]);
1028 if (TransReverse
[t
] == NULL
) goto Error
;
1031 // Now inset the reversed curves at the begin of transform
1032 LutPlusCurves
= cmsPipelineDup(OriginalLut
);
1033 if (LutPlusCurves
== NULL
) goto Error
;
1035 if (!cmsPipelineInsertStage(LutPlusCurves
, cmsAT_BEGIN
, cmsStageAllocToneCurves(OriginalLut
->ContextID
, OriginalLut
->InputChannels
, TransReverse
)))
1038 // Create the result LUT
1039 OptimizedLUT
= cmsPipelineAlloc(OriginalLut
->ContextID
, OriginalLut
->InputChannels
, OriginalLut
->OutputChannels
);
1040 if (OptimizedLUT
== NULL
) goto Error
;
1042 OptimizedPrelinMpe
= cmsStageAllocToneCurves(OriginalLut
->ContextID
, OriginalLut
->InputChannels
, Trans
);
1044 // Create and insert the curves at the beginning
1045 if (!cmsPipelineInsertStage(OptimizedLUT
, cmsAT_BEGIN
, OptimizedPrelinMpe
))
1048 // Allocate the CLUT for result
1049 OptimizedCLUTmpe
= cmsStageAllocCLut16bit(OriginalLut
->ContextID
, nGridPoints
, OriginalLut
->InputChannels
, OriginalLut
->OutputChannels
, NULL
);
1051 // Add the CLUT to the destination LUT
1052 if (!cmsPipelineInsertStage(OptimizedLUT
, cmsAT_END
, OptimizedCLUTmpe
))
1056 if (!cmsStageSampleCLut16bit(OptimizedCLUTmpe
, XFormSampler16
, (void*) LutPlusCurves
, 0)) goto Error
;
1059 for (t
= 0; t
< OriginalLut
->InputChannels
; t
++) {
1061 if (Trans
[t
]) cmsFreeToneCurve(Trans
[t
]);
1062 if (TransReverse
[t
]) cmsFreeToneCurve(TransReverse
[t
]);
1065 cmsPipelineFree(LutPlusCurves
);
1068 OptimizedPrelinCurves
= _cmsStageGetPtrToCurveSet(OptimizedPrelinMpe
);
1069 OptimizedPrelinCLUT
= (_cmsStageCLutData
*) OptimizedCLUTmpe
->Data
;
1071 // Set the evaluator if 8-bit
1072 if (_cmsFormatterIs8bit(*InputFormat
)) {
1074 Prelin8Data
* p8
= PrelinOpt8alloc(OptimizedLUT
->ContextID
,
1075 OptimizedPrelinCLUT
->Params
,
1076 OptimizedPrelinCurves
);
1077 if (p8
== NULL
) return FALSE
;
1079 _cmsPipelineSetOptimizationParameters(OptimizedLUT
, PrelinEval8
, (void*) p8
, Prelin8free
, Prelin8dup
);
1084 Prelin16Data
* p16
= PrelinOpt16alloc(OptimizedLUT
->ContextID
,
1085 OptimizedPrelinCLUT
->Params
,
1086 3, OptimizedPrelinCurves
, 3, NULL
);
1087 if (p16
== NULL
) return FALSE
;
1089 _cmsPipelineSetOptimizationParameters(OptimizedLUT
, PrelinEval16
, (void*) p16
, PrelinOpt16free
, Prelin16dup
);
1093 // Don't fix white on absolute colorimetric
1094 if (Intent
== INTENT_ABSOLUTE_COLORIMETRIC
)
1095 *dwFlags
|= cmsFLAGS_NOWHITEONWHITEFIXUP
;
1097 if (!(*dwFlags
& cmsFLAGS_NOWHITEONWHITEFIXUP
)) {
1099 if (!FixWhiteMisalignment(OptimizedLUT
, ColorSpace
, OutputColorSpace
)) {
1105 // And return the obtained LUT
1107 cmsPipelineFree(OriginalLut
);
1108 *Lut
= OptimizedLUT
;
1113 for (t
= 0; t
< OriginalLut
->InputChannels
; t
++) {
1115 if (Trans
[t
]) cmsFreeToneCurve(Trans
[t
]);
1116 if (TransReverse
[t
]) cmsFreeToneCurve(TransReverse
[t
]);
1119 if (LutPlusCurves
!= NULL
) cmsPipelineFree(LutPlusCurves
);
1120 if (OptimizedLUT
!= NULL
) cmsPipelineFree(OptimizedLUT
);
1124 cmsUNUSED_PARAMETER(Intent
);
1128 // Curves optimizer ------------------------------------------------------------------------------------------------------------------
1131 void CurvesFree(cmsContext ContextID
, void* ptr
)
1133 Curves16Data
* Data
= (Curves16Data
*) ptr
;
1136 for (i
=0; i
< Data
-> nCurves
; i
++) {
1138 _cmsFree(ContextID
, Data
->Curves
[i
]);
1141 _cmsFree(ContextID
, Data
->Curves
);
1142 _cmsFree(ContextID
, ptr
);
1146 void* CurvesDup(cmsContext ContextID
, const void* ptr
)
1148 Curves16Data
* Data
= _cmsDupMem(ContextID
, ptr
, sizeof(Curves16Data
));
1151 if (Data
== NULL
) return NULL
;
1153 Data
->Curves
= _cmsDupMem(ContextID
, Data
->Curves
, Data
->nCurves
* sizeof(cmsUInt16Number
*));
1155 for (i
=0; i
< Data
-> nCurves
; i
++) {
1156 Data
->Curves
[i
] = _cmsDupMem(ContextID
, Data
->Curves
[i
], Data
-> nElements
* sizeof(cmsUInt16Number
));
1159 return (void*) Data
;
1162 // Precomputes tables for 8-bit on input devicelink.
1164 Curves16Data
* CurvesAlloc(cmsContext ContextID
, int nCurves
, int nElements
, cmsToneCurve
** G
)
1169 c16
= _cmsMallocZero(ContextID
, sizeof(Curves16Data
));
1170 if (c16
== NULL
) return NULL
;
1172 c16
->nCurves
= nCurves
;
1173 c16
->nElements
= nElements
;
1175 c16
->Curves
= _cmsCalloc(ContextID
, nCurves
, sizeof(cmsUInt16Number
*));
1176 if (c16
->Curves
== NULL
) return NULL
;
1178 for (i
=0; i
< nCurves
; i
++) {
1180 c16
->Curves
[i
] = _cmsCalloc(ContextID
, nElements
, sizeof(cmsUInt16Number
));
1182 if (c16
->Curves
[i
] == NULL
) {
1184 for (j
=0; j
< i
; j
++) {
1185 _cmsFree(ContextID
, c16
->Curves
[j
]);
1187 _cmsFree(ContextID
, c16
->Curves
);
1188 _cmsFree(ContextID
, c16
);
1192 if (nElements
== 256) {
1194 for (j
=0; j
< nElements
; j
++) {
1196 c16
->Curves
[i
][j
] = cmsEvalToneCurve16(G
[i
], FROM_8_TO_16(j
));
1201 for (j
=0; j
< nElements
; j
++) {
1202 c16
->Curves
[i
][j
] = cmsEvalToneCurve16(G
[i
], (cmsUInt16Number
) j
);
1211 void FastEvaluateCurves8(register const cmsUInt16Number In
[],
1212 register cmsUInt16Number Out
[],
1213 register const void* D
)
1215 Curves16Data
* Data
= (Curves16Data
*) D
;
1219 for (i
=0; i
< Data
->nCurves
; i
++) {
1222 Out
[i
] = Data
-> Curves
[i
][x
];
1228 void FastEvaluateCurves16(register const cmsUInt16Number In
[],
1229 register cmsUInt16Number Out
[],
1230 register const void* D
)
1232 Curves16Data
* Data
= (Curves16Data
*) D
;
1235 for (i
=0; i
< Data
->nCurves
; i
++) {
1236 Out
[i
] = Data
-> Curves
[i
][In
[i
]];
1242 void FastIdentity16(register const cmsUInt16Number In
[],
1243 register cmsUInt16Number Out
[],
1244 register const void* D
)
1246 cmsPipeline
* Lut
= (cmsPipeline
*) D
;
1249 for (i
=0; i
< Lut
->InputChannels
; i
++) {
1255 // If the target LUT holds only curves, the optimization procedure is to join all those
1256 // curves together. That only works on curves and does not work on matrices.
1258 cmsBool
OptimizeByJoiningCurves(cmsPipeline
** Lut
, cmsUInt32Number Intent
, cmsUInt32Number
* InputFormat
, cmsUInt32Number
* OutputFormat
, cmsUInt32Number
* dwFlags
)
1260 cmsToneCurve
** GammaTables
= NULL
;
1261 cmsFloat32Number InFloat
[cmsMAXCHANNELS
], OutFloat
[cmsMAXCHANNELS
];
1262 cmsUInt32Number i
, j
;
1263 cmsPipeline
* Src
= *Lut
;
1264 cmsPipeline
* Dest
= NULL
;
1266 cmsStage
* ObtainedCurves
= NULL
;
1269 // This is a loosy optimization! does not apply in floating-point cases
1270 if (_cmsFormatterIsFloat(*InputFormat
) || _cmsFormatterIsFloat(*OutputFormat
)) return FALSE
;
1272 // Only curves in this LUT?
1273 for (mpe
= cmsPipelineGetPtrToFirstStage(Src
);
1275 mpe
= cmsStageNext(mpe
)) {
1276 if (cmsStageType(mpe
) != cmsSigCurveSetElemType
) return FALSE
;
1279 // Allocate an empty LUT
1280 Dest
= cmsPipelineAlloc(Src
->ContextID
, Src
->InputChannels
, Src
->OutputChannels
);
1281 if (Dest
== NULL
) return FALSE
;
1283 // Create target curves
1284 GammaTables
= (cmsToneCurve
**) _cmsCalloc(Src
->ContextID
, Src
->InputChannels
, sizeof(cmsToneCurve
*));
1285 if (GammaTables
== NULL
) goto Error
;
1287 for (i
=0; i
< Src
->InputChannels
; i
++) {
1288 GammaTables
[i
] = cmsBuildTabulatedToneCurve16(Src
->ContextID
, PRELINEARIZATION_POINTS
, NULL
);
1289 if (GammaTables
[i
] == NULL
) goto Error
;
1292 // Compute 16 bit result by using floating point
1293 for (i
=0; i
< PRELINEARIZATION_POINTS
; i
++) {
1295 for (j
=0; j
< Src
->InputChannels
; j
++)
1296 InFloat
[j
] = (cmsFloat32Number
) ((cmsFloat64Number
) i
/ (PRELINEARIZATION_POINTS
- 1));
1298 cmsPipelineEvalFloat(InFloat
, OutFloat
, Src
);
1300 for (j
=0; j
< Src
->InputChannels
; j
++)
1301 GammaTables
[j
] -> Table16
[i
] = _cmsQuickSaturateWord(OutFloat
[j
] * 65535.0);
1304 ObtainedCurves
= cmsStageAllocToneCurves(Src
->ContextID
, Src
->InputChannels
, GammaTables
);
1305 if (ObtainedCurves
== NULL
) goto Error
;
1307 for (i
=0; i
< Src
->InputChannels
; i
++) {
1308 cmsFreeToneCurve(GammaTables
[i
]);
1309 GammaTables
[i
] = NULL
;
1312 if (GammaTables
!= NULL
) _cmsFree(Src
->ContextID
, GammaTables
);
1314 // Maybe the curves are linear at the end
1315 if (!AllCurvesAreLinear(ObtainedCurves
)) {
1317 if (!cmsPipelineInsertStage(Dest
, cmsAT_BEGIN
, ObtainedCurves
))
1320 // If the curves are to be applied in 8 bits, we can save memory
1321 if (_cmsFormatterIs8bit(*InputFormat
)) {
1323 _cmsStageToneCurvesData
* Data
= (_cmsStageToneCurvesData
*) ObtainedCurves
->Data
;
1324 Curves16Data
* c16
= CurvesAlloc(Dest
->ContextID
, Data
->nCurves
, 256, Data
->TheCurves
);
1326 if (c16
== NULL
) goto Error
;
1327 *dwFlags
|= cmsFLAGS_NOCACHE
;
1328 _cmsPipelineSetOptimizationParameters(Dest
, FastEvaluateCurves8
, c16
, CurvesFree
, CurvesDup
);
1333 _cmsStageToneCurvesData
* Data
= (_cmsStageToneCurvesData
*) cmsStageData(ObtainedCurves
);
1334 Curves16Data
* c16
= CurvesAlloc(Dest
->ContextID
, Data
->nCurves
, 65536, Data
->TheCurves
);
1336 if (c16
== NULL
) goto Error
;
1337 *dwFlags
|= cmsFLAGS_NOCACHE
;
1338 _cmsPipelineSetOptimizationParameters(Dest
, FastEvaluateCurves16
, c16
, CurvesFree
, CurvesDup
);
1343 // LUT optimizes to nothing. Set the identity LUT
1344 cmsStageFree(ObtainedCurves
);
1346 if (!cmsPipelineInsertStage(Dest
, cmsAT_BEGIN
, cmsStageAllocIdentity(Dest
->ContextID
, Src
->InputChannels
)))
1349 *dwFlags
|= cmsFLAGS_NOCACHE
;
1350 _cmsPipelineSetOptimizationParameters(Dest
, FastIdentity16
, (void*) Dest
, NULL
, NULL
);
1354 cmsPipelineFree(Src
);
1360 if (ObtainedCurves
!= NULL
) cmsStageFree(ObtainedCurves
);
1361 if (GammaTables
!= NULL
) {
1362 for (i
=0; i
< Src
->InputChannels
; i
++) {
1363 if (GammaTables
[i
] != NULL
) cmsFreeToneCurve(GammaTables
[i
]);
1366 _cmsFree(Src
->ContextID
, GammaTables
);
1369 if (Dest
!= NULL
) cmsPipelineFree(Dest
);
1372 cmsUNUSED_PARAMETER(Intent
);
1373 cmsUNUSED_PARAMETER(InputFormat
);
1374 cmsUNUSED_PARAMETER(OutputFormat
);
1375 cmsUNUSED_PARAMETER(dwFlags
);
1378 // -------------------------------------------------------------------------------------------------------------------------------------
1379 // LUT is Shaper - Matrix - Matrix - Shaper, which is very frequent when combining two matrix-shaper profiles
1383 void FreeMatShaper(cmsContext ContextID
, void* Data
)
1385 if (Data
!= NULL
) _cmsFree(ContextID
, Data
);
1389 void* DupMatShaper(cmsContext ContextID
, const void* Data
)
1391 return _cmsDupMem(ContextID
, Data
, sizeof(MatShaper8Data
));
1395 // A fast matrix-shaper evaluator for 8 bits. This is a bit ticky since I'm using 1.14 signed fixed point
1396 // to accomplish some performance. Actually it takes 256x3 16 bits tables and 16385 x 3 tables of 8 bits,
1397 // in total about 50K, and the performance boost is huge!
1399 void MatShaperEval16(register const cmsUInt16Number In
[],
1400 register cmsUInt16Number Out
[],
1401 register const void* D
)
1403 MatShaper8Data
* p
= (MatShaper8Data
*) D
;
1404 cmsS1Fixed14Number l1
, l2
, l3
, r
, g
, b
;
1405 cmsUInt32Number ri
, gi
, bi
;
1407 // In this case (and only in this case!) we can use this simplification since
1408 // In[] is assured to come from a 8 bit number. (a << 8 | a)
1413 // Across first shaper, which also converts to 1.14 fixed point
1414 r
= p
->Shaper1R
[ri
];
1415 g
= p
->Shaper1G
[gi
];
1416 b
= p
->Shaper1B
[bi
];
1418 // Evaluate the matrix in 1.14 fixed point
1419 l1
= (p
->Mat
[0][0] * r
+ p
->Mat
[0][1] * g
+ p
->Mat
[0][2] * b
+ p
->Off
[0] + 0x2000) >> 14;
1420 l2
= (p
->Mat
[1][0] * r
+ p
->Mat
[1][1] * g
+ p
->Mat
[1][2] * b
+ p
->Off
[1] + 0x2000) >> 14;
1421 l3
= (p
->Mat
[2][0] * r
+ p
->Mat
[2][1] * g
+ p
->Mat
[2][2] * b
+ p
->Off
[2] + 0x2000) >> 14;
1423 // Now we have to clip to 0..1.0 range
1424 ri
= (l1
< 0) ? 0 : ((l1
> 16384) ? 16384 : l1
);
1425 gi
= (l2
< 0) ? 0 : ((l2
> 16384) ? 16384 : l2
);
1426 bi
= (l3
< 0) ? 0 : ((l3
> 16384) ? 16384 : l3
);
1428 // And across second shaper,
1429 Out
[0] = p
->Shaper2R
[ri
];
1430 Out
[1] = p
->Shaper2G
[gi
];
1431 Out
[2] = p
->Shaper2B
[bi
];
1435 // This table converts from 8 bits to 1.14 after applying the curve
1437 void FillFirstShaper(cmsS1Fixed14Number
* Table
, cmsToneCurve
* Curve
)
1440 cmsFloat32Number R
, y
;
1442 for (i
=0; i
< 256; i
++) {
1444 R
= (cmsFloat32Number
) (i
/ 255.0);
1445 y
= cmsEvalToneCurveFloat(Curve
, R
);
1447 Table
[i
] = DOUBLE_TO_1FIXED14(y
);
1451 // This table converts form 1.14 (being 0x4000 the last entry) to 8 bits after applying the curve
1453 void FillSecondShaper(cmsUInt16Number
* Table
, cmsToneCurve
* Curve
, cmsBool Is8BitsOutput
)
1456 cmsFloat32Number R
, Val
;
1458 for (i
=0; i
< 16385; i
++) {
1460 R
= (cmsFloat32Number
) (i
/ 16384.0);
1461 Val
= cmsEvalToneCurveFloat(Curve
, R
); // Val comes 0..1.0
1463 if (Is8BitsOutput
) {
1465 // If 8 bits output, we can optimize further by computing the / 257 part.
1466 // first we compute the resulting byte and then we store the byte times
1467 // 257. This quantization allows to round very quick by doing a >> 8, but
1468 // since the low byte is always equal to msb, we can do a & 0xff and this works!
1469 cmsUInt16Number w
= _cmsQuickSaturateWord(Val
* 65535.0);
1470 cmsUInt8Number b
= FROM_16_TO_8(w
);
1472 Table
[i
] = FROM_8_TO_16(b
);
1474 else Table
[i
] = _cmsQuickSaturateWord(Val
* 65535.0);
1478 // Compute the matrix-shaper structure
1480 cmsBool
SetMatShaper(cmsPipeline
* Dest
, cmsToneCurve
* Curve1
[3], cmsMAT3
* Mat
, cmsVEC3
* Off
, cmsToneCurve
* Curve2
[3], cmsUInt32Number
* OutputFormat
)
1484 cmsBool Is8Bits
= _cmsFormatterIs8bit(*OutputFormat
);
1486 // Allocate a big chuck of memory to store precomputed tables
1487 p
= (MatShaper8Data
*) _cmsMalloc(Dest
->ContextID
, sizeof(MatShaper8Data
));
1488 if (p
== NULL
) return FALSE
;
1490 p
-> ContextID
= Dest
-> ContextID
;
1492 // Precompute tables
1493 FillFirstShaper(p
->Shaper1R
, Curve1
[0]);
1494 FillFirstShaper(p
->Shaper1G
, Curve1
[1]);
1495 FillFirstShaper(p
->Shaper1B
, Curve1
[2]);
1497 FillSecondShaper(p
->Shaper2R
, Curve2
[0], Is8Bits
);
1498 FillSecondShaper(p
->Shaper2G
, Curve2
[1], Is8Bits
);
1499 FillSecondShaper(p
->Shaper2B
, Curve2
[2], Is8Bits
);
1501 // Convert matrix to nFixed14. Note that those values may take more than 16 bits as
1502 for (i
=0; i
< 3; i
++) {
1503 for (j
=0; j
< 3; j
++) {
1504 p
->Mat
[i
][j
] = DOUBLE_TO_1FIXED14(Mat
->v
[i
].n
[j
]);
1508 for (i
=0; i
< 3; i
++) {
1514 p
->Off
[i
] = DOUBLE_TO_1FIXED14(Off
->n
[i
]);
1518 // Mark as optimized for faster formatter
1520 *OutputFormat
|= OPTIMIZED_SH(1);
1522 // Fill function pointers
1523 _cmsPipelineSetOptimizationParameters(Dest
, MatShaperEval16
, (void*) p
, FreeMatShaper
, DupMatShaper
);
1527 // 8 bits on input allows matrix-shaper boot up to 25 Mpixels per second on RGB. That's fast!
1528 // TODO: Allow a third matrix for abs. colorimetric
1530 cmsBool
OptimizeMatrixShaper(cmsPipeline
** Lut
, cmsUInt32Number Intent
, cmsUInt32Number
* InputFormat
, cmsUInt32Number
* OutputFormat
, cmsUInt32Number
* dwFlags
)
1532 cmsStage
* Curve1
, *Curve2
;
1533 cmsStage
* Matrix1
, *Matrix2
;
1534 _cmsStageMatrixData
* Data1
;
1535 _cmsStageMatrixData
* Data2
;
1537 cmsBool IdentityMat
;
1538 cmsPipeline
* Dest
, *Src
;
1540 // Only works on RGB to RGB
1541 if (T_CHANNELS(*InputFormat
) != 3 || T_CHANNELS(*OutputFormat
) != 3) return FALSE
;
1543 // Only works on 8 bit input
1544 if (!_cmsFormatterIs8bit(*InputFormat
)) return FALSE
;
1546 // Seems suitable, proceed
1549 // Check for shaper-matrix-matrix-shaper structure, that is what this optimizer stands for
1550 if (!cmsPipelineCheckAndRetreiveStages(Src
, 4,
1551 cmsSigCurveSetElemType
, cmsSigMatrixElemType
, cmsSigMatrixElemType
, cmsSigCurveSetElemType
,
1552 &Curve1
, &Matrix1
, &Matrix2
, &Curve2
)) return FALSE
;
1554 // Get both matrices
1555 Data1
= (_cmsStageMatrixData
*) cmsStageData(Matrix1
);
1556 Data2
= (_cmsStageMatrixData
*) cmsStageData(Matrix2
);
1558 // Input offset should be zero
1559 if (Data1
->Offset
!= NULL
) return FALSE
;
1561 // Multiply both matrices to get the result
1562 _cmsMAT3per(&res
, (cmsMAT3
*) Data2
->Double
, (cmsMAT3
*) Data1
->Double
);
1564 // Now the result is in res + Data2 -> Offset. Maybe is a plain identity?
1565 IdentityMat
= FALSE
;
1566 if (_cmsMAT3isIdentity(&res
) && Data2
->Offset
== NULL
) {
1568 // We can get rid of full matrix
1572 // Allocate an empty LUT
1573 Dest
= cmsPipelineAlloc(Src
->ContextID
, Src
->InputChannels
, Src
->OutputChannels
);
1574 if (!Dest
) return FALSE
;
1576 // Assamble the new LUT
1577 if (!cmsPipelineInsertStage(Dest
, cmsAT_BEGIN
, cmsStageDup(Curve1
)))
1581 if (!cmsPipelineInsertStage(Dest
, cmsAT_END
, cmsStageAllocMatrix(Dest
->ContextID
, 3, 3, (const cmsFloat64Number
*) &res
, Data2
->Offset
)))
1583 if (!cmsPipelineInsertStage(Dest
, cmsAT_END
, cmsStageDup(Curve2
)))
1586 // If identity on matrix, we can further optimize the curves, so call the join curves routine
1589 OptimizeByJoiningCurves(&Dest
, Intent
, InputFormat
, OutputFormat
, dwFlags
);
1592 _cmsStageToneCurvesData
* mpeC1
= (_cmsStageToneCurvesData
*) cmsStageData(Curve1
);
1593 _cmsStageToneCurvesData
* mpeC2
= (_cmsStageToneCurvesData
*) cmsStageData(Curve2
);
1595 // In this particular optimization, caché does not help as it takes more time to deal with
1596 // the caché that with the pixel handling
1597 *dwFlags
|= cmsFLAGS_NOCACHE
;
1599 // Setup the optimizarion routines
1600 SetMatShaper(Dest
, mpeC1
->TheCurves
, &res
, (cmsVEC3
*) Data2
->Offset
, mpeC2
->TheCurves
, OutputFormat
);
1603 cmsPipelineFree(Src
);
1607 // Leave Src unchanged
1608 cmsPipelineFree(Dest
);
1613 // -------------------------------------------------------------------------------------------------------------------------------------
1614 // Optimization plug-ins
1616 // List of optimizations
1617 typedef struct _cmsOptimizationCollection_st
{
1619 _cmsOPToptimizeFn OptimizePtr
;
1621 struct _cmsOptimizationCollection_st
*Next
;
1623 } _cmsOptimizationCollection
;
1626 // The built-in list. We currently implement 4 types of optimizations. Joining of curves, matrix-shaper, linearization and resampling
1627 static _cmsOptimizationCollection DefaultOptimization
[] = {
1629 { OptimizeByJoiningCurves
, &DefaultOptimization
[1] },
1630 { OptimizeMatrixShaper
, &DefaultOptimization
[2] },
1631 { OptimizeByComputingLinearization
, &DefaultOptimization
[3] },
1632 { OptimizeByResampling
, NULL
}
1635 // The linked list head
1636 static _cmsOptimizationCollection
* OptimizationCollection
= DefaultOptimization
;
1638 // Register new ways to optimize
1639 cmsBool
_cmsRegisterOptimizationPlugin(cmsContext id
, cmsPluginBase
* Data
)
1641 cmsPluginOptimization
* Plugin
= (cmsPluginOptimization
*) Data
;
1642 _cmsOptimizationCollection
* fl
;
1646 OptimizationCollection
= DefaultOptimization
;
1650 // Optimizer callback is required
1651 if (Plugin
->OptimizePtr
== NULL
) return FALSE
;
1653 fl
= (_cmsOptimizationCollection
*) _cmsPluginMalloc(id
, sizeof(_cmsOptimizationCollection
));
1654 if (fl
== NULL
) return FALSE
;
1656 // Copy the parameters
1657 fl
->OptimizePtr
= Plugin
->OptimizePtr
;
1660 fl
->Next
= OptimizationCollection
;
1661 OptimizationCollection
= fl
;
1667 // The entry point for LUT optimization
1668 cmsBool
_cmsOptimizePipeline(cmsPipeline
** PtrLut
,
1670 cmsUInt32Number
* InputFormat
,
1671 cmsUInt32Number
* OutputFormat
,
1672 cmsUInt32Number
* dwFlags
)
1674 _cmsOptimizationCollection
* Opts
;
1675 cmsBool AnySuccess
= FALSE
;
1677 // A CLUT is being asked, so force this specific optimization
1678 if (*dwFlags
& cmsFLAGS_FORCE_CLUT
) {
1680 PreOptimize(*PtrLut
);
1681 return OptimizeByResampling(PtrLut
, Intent
, InputFormat
, OutputFormat
, dwFlags
);
1684 // Anything to optimize?
1685 if ((*PtrLut
) ->Elements
== NULL
) {
1686 _cmsPipelineSetOptimizationParameters(*PtrLut
, FastIdentity16
, (void*) *PtrLut
, NULL
, NULL
);
1690 // Try to get rid of identities and trivial conversions.
1691 AnySuccess
= PreOptimize(*PtrLut
);
1693 // After removal do we end with an identity?
1694 if ((*PtrLut
) ->Elements
== NULL
) {
1695 _cmsPipelineSetOptimizationParameters(*PtrLut
, FastIdentity16
, (void*) *PtrLut
, NULL
, NULL
);
1699 // Do not optimize, keep all precision
1700 if (*dwFlags
& cmsFLAGS_NOOPTIMIZE
)
1703 // Try built-in optimizations and plug-in
1704 for (Opts
= OptimizationCollection
;
1706 Opts
= Opts
->Next
) {
1708 // If one schema succeeded, we are done
1709 if (Opts
->OptimizePtr(PtrLut
, Intent
, InputFormat
, OutputFormat
, dwFlags
)) {
1711 return TRUE
; // Optimized!
1715 // Only simple optimizations succeeded