1//---------------------------------------------------------------------------------
2//
3// Little Color Management System
4// Copyright (c) 1998-2017 Marti Maria Saguer
5//
6// Permission is hereby granted, free of charge, to any person obtaining
7// a copy of this software and associated documentation files (the "Software"),
8// to deal in the Software without restriction, including without limitation
9// the rights to use, copy, modify, merge, publish, distribute, sublicense,
10// and/or sell copies of the Software, and to permit persons to whom the Software
11// is furnished to do so, subject to the following conditions:
12//
13// The above copyright notice and this permission notice shall be included in
14// all copies or substantial portions of the Software.
15//
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
18// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23//
24//---------------------------------------------------------------------------------
25//
26
27#include "lcms2_internal.h"
28
29
30// Allocates an empty multi profile element
31cmsStage* CMSEXPORT _cmsStageAllocPlaceholder(cmsContext ContextID,
32 cmsStageSignature Type,
33 cmsUInt32Number InputChannels,
34 cmsUInt32Number OutputChannels,
35 _cmsStageEvalFn EvalPtr,
36 _cmsStageDupElemFn DupElemPtr,
37 _cmsStageFreeElemFn FreePtr,
38 void* Data)
39{
40 cmsStage* ph = (cmsStage*) _cmsMallocZero(ContextID, sizeof(cmsStage));
41
42 if (ph == NULL) return NULL;
43
44 ph ->Type = Type;
45 ph ->Implements = Type; // By default, no clue on what is implementing
46
47 ph ->InputChannels = InputChannels;
48 ph ->OutputChannels = OutputChannels;
49 ph ->EvalPtr = EvalPtr;
50 ph ->DupElemPtr = DupElemPtr;
51 ph ->FreePtr = FreePtr;
52 ph ->Data = Data;
53
54 return ph;
55}
56
57
58static
59void EvaluateIdentity(cmsContext ContextID, const cmsFloat32Number In[],
60 cmsFloat32Number Out[],
61 const cmsStage *mpe)
62{
63 cmsUNUSED_PARAMETER(ContextID);
64 memmove(Out, In, mpe ->InputChannels * sizeof(cmsFloat32Number));
65}
66
67
68cmsStage* CMSEXPORT cmsStageAllocIdentity(cmsContext ContextID, cmsUInt32Number nChannels)
69{
70 return _cmsStageAllocPlaceholder(ContextID,
71 cmsSigIdentityElemType,
72 nChannels, nChannels,
73 EvaluateIdentity,
74 NULL,
75 NULL,
76 NULL);
77 }
78
79// Conversion functions. From floating point to 16 bits
80static
81void FromFloatTo16(const cmsFloat32Number In[], cmsUInt16Number Out[], cmsUInt32Number n)
82{
83 cmsUInt32Number i;
84
85 for (i=0; i < n; i++) {
86 Out[i] = _cmsQuickSaturateWord(In[i] * 65535.0);
87 }
88}
89
90// From 16 bits to floating point
91static
92void From16ToFloat(const cmsUInt16Number In[], cmsFloat32Number Out[], cmsUInt32Number n)
93{
94 cmsUInt32Number i;
95
96 for (i=0; i < n; i++) {
97 Out[i] = (cmsFloat32Number) In[i] / 65535.0F;
98 }
99}
100
101
102// This function is quite useful to analyze the structure of a LUT and retrieve the MPE elements
103// that conform the LUT. It should be called with the LUT, the number of expected elements and
104// then a list of expected types followed with a list of cmsFloat64Number pointers to MPE elements. If
105// the function founds a match with current pipeline, it fills the pointers and returns TRUE
106// if not, returns FALSE without touching anything. Setting pointers to NULL does bypass
107// the storage process.
108cmsBool CMSEXPORT cmsPipelineCheckAndRetreiveStages(cmsContext ContextID, const cmsPipeline* Lut, cmsUInt32Number n, ...)
109{
110 va_list args;
111 cmsUInt32Number i;
112 cmsStage* mpe;
113 cmsStageSignature Type;
114 void** ElemPtr;
115
116 // Make sure same number of elements
117 if (cmsPipelineStageCount(ContextID, Lut) != n) return FALSE;
118
119 va_start(args, n);
120
121 // Iterate across asked types
122 mpe = Lut ->Elements;
123 for (i=0; i < n; i++) {
124
125 // Get asked type. cmsStageSignature is promoted to int by compiler
126 Type = (cmsStageSignature)va_arg(args, int);
127 if (mpe ->Type != Type) {
128
129 va_end(args); // Mismatch. We are done.
130 return FALSE;
131 }
132 mpe = mpe ->Next;
133 }
134
135 // Found a combination, fill pointers if not NULL
136 mpe = Lut ->Elements;
137 for (i=0; i < n; i++) {
138
139 ElemPtr = va_arg(args, void**);
140 if (ElemPtr != NULL)
141 *ElemPtr = mpe;
142
143 mpe = mpe ->Next;
144 }
145
146 va_end(args);
147 return TRUE;
148}
149
150// Below there are implementations for several types of elements. Each type may be implemented by a
151// evaluation function, a duplication function, a function to free resources and a constructor.
152
153// *************************************************************************************************
154// Type cmsSigCurveSetElemType (curves)
155// *************************************************************************************************
156
157cmsToneCurve** _cmsStageGetPtrToCurveSet(const cmsStage* mpe)
158{
159 _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) mpe ->Data;
160
161 return Data ->TheCurves;
162}
163
164static
165void EvaluateCurves(cmsContext ContextID, const cmsFloat32Number In[],
166 cmsFloat32Number Out[],
167 const cmsStage *mpe)
168{
169 _cmsStageToneCurvesData* Data;
170 cmsUInt32Number i;
171
172 _cmsAssert(mpe != NULL);
173
174 Data = (_cmsStageToneCurvesData*) mpe ->Data;
175 if (Data == NULL) return;
176
177 if (Data ->TheCurves == NULL) return;
178
179 for (i=0; i < Data ->nCurves; i++) {
180 Out[i] = cmsEvalToneCurveFloat(ContextID, Data ->TheCurves[i], In[i]);
181 }
182}
183
184static
185void CurveSetElemTypeFree(cmsContext ContextID, cmsStage* mpe)
186{
187 _cmsStageToneCurvesData* Data;
188 cmsUInt32Number i;
189
190 _cmsAssert(mpe != NULL);
191
192 Data = (_cmsStageToneCurvesData*) mpe ->Data;
193 if (Data == NULL) return;
194
195 if (Data ->TheCurves != NULL) {
196 for (i=0; i < Data ->nCurves; i++) {
197 if (Data ->TheCurves[i] != NULL)
198 cmsFreeToneCurve(ContextID, Data ->TheCurves[i]);
199 }
200 }
201 _cmsFree(ContextID, Data ->TheCurves);
202 _cmsFree(ContextID, Data);
203}
204
205
206static
207void* CurveSetDup(cmsContext ContextID, cmsStage* mpe)
208{
209 _cmsStageToneCurvesData* Data = (_cmsStageToneCurvesData*) mpe ->Data;
210 _cmsStageToneCurvesData* NewElem;
211 cmsUInt32Number i;
212
213 NewElem = (_cmsStageToneCurvesData*) _cmsMallocZero(ContextID, sizeof(_cmsStageToneCurvesData));
214 if (NewElem == NULL) return NULL;
215
216 NewElem ->nCurves = Data ->nCurves;
217 NewElem ->TheCurves = (cmsToneCurve**) _cmsCalloc(ContextID, NewElem ->nCurves, sizeof(cmsToneCurve*));
218
219 if (NewElem ->TheCurves == NULL) goto Error;
220
221 for (i=0; i < NewElem ->nCurves; i++) {
222
223 // Duplicate each curve. It may fail.
224 NewElem ->TheCurves[i] = cmsDupToneCurve(ContextID, Data ->TheCurves[i]);
225 if (NewElem ->TheCurves[i] == NULL) goto Error;
226
227
228 }
229 return (void*) NewElem;
230
231Error:
232
233 if (NewElem ->TheCurves != NULL) {
234 for (i=0; i < NewElem ->nCurves; i++) {
235 if (NewElem ->TheCurves[i])
236 cmsFreeToneCurve(ContextID, NewElem ->TheCurves[i]);
237 }
238 }
239 _cmsFree(ContextID, NewElem ->TheCurves);
240 _cmsFree(ContextID, NewElem);
241 return NULL;
242}
243
244
245// Curves == NULL forces identity curves
246cmsStage* CMSEXPORT cmsStageAllocToneCurves(cmsContext ContextID, cmsUInt32Number nChannels, cmsToneCurve* const Curves[])
247{
248 cmsUInt32Number i;
249 _cmsStageToneCurvesData* NewElem;
250 cmsStage* NewMPE;
251
252
253 NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigCurveSetElemType, nChannels, nChannels,
254 EvaluateCurves, CurveSetDup, CurveSetElemTypeFree, NULL );
255 if (NewMPE == NULL) return NULL;
256
257 NewElem = (_cmsStageToneCurvesData*) _cmsMallocZero(ContextID, sizeof(_cmsStageToneCurvesData));
258 if (NewElem == NULL) {
259 cmsStageFree(ContextID, NewMPE);
260 return NULL;
261 }
262
263 NewMPE ->Data = (void*) NewElem;
264
265 NewElem ->nCurves = nChannels;
266 NewElem ->TheCurves = (cmsToneCurve**) _cmsCalloc(ContextID, nChannels, sizeof(cmsToneCurve*));
267 if (NewElem ->TheCurves == NULL) {
268 cmsStageFree(ContextID, NewMPE);
269 return NULL;
270 }
271
272 for (i=0; i < nChannels; i++) {
273
274 if (Curves == NULL) {
275 NewElem ->TheCurves[i] = cmsBuildGamma(ContextID, 1.0);
276 }
277 else {
278 NewElem ->TheCurves[i] = cmsDupToneCurve(ContextID, Curves[i]);
279 }
280
281 if (NewElem ->TheCurves[i] == NULL) {
282 cmsStageFree(ContextID, NewMPE);
283 return NULL;
284 }
285
286 }
287
288 return NewMPE;
289}
290
291
292// Create a bunch of identity curves
293cmsStage* CMSEXPORT _cmsStageAllocIdentityCurves(cmsContext ContextID, cmsUInt32Number nChannels)
294{
295 cmsStage* mpe = cmsStageAllocToneCurves(ContextID, nChannels, NULL);
296
297 if (mpe == NULL) return NULL;
298 mpe ->Implements = cmsSigIdentityElemType;
299 return mpe;
300}
301
302
303// *************************************************************************************************
304// Type cmsSigMatrixElemType (Matrices)
305// *************************************************************************************************
306
307
308// Special care should be taken here because precision loss. A temporary cmsFloat64Number buffer is being used
309static
310void EvaluateMatrix(cmsContext ContextID, const cmsFloat32Number In[],
311 cmsFloat32Number Out[],
312 const cmsStage *mpe)
313{
314 cmsUInt32Number i, j;
315 _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data;
316 cmsFloat64Number Tmp;
317 cmsUNUSED_PARAMETER(ContextID);
318
319 // Input is already in 0..1.0 notation
320 for (i=0; i < mpe ->OutputChannels; i++) {
321
322 Tmp = 0;
323 for (j=0; j < mpe->InputChannels; j++) {
324 Tmp += In[j] * Data->Double[i*mpe->InputChannels + j];
325 }
326
327 if (Data ->Offset != NULL)
328 Tmp += Data->Offset[i];
329
330 Out[i] = (cmsFloat32Number) Tmp;
331 }
332
333
334 // Output in 0..1.0 domain
335}
336
337
338// Duplicate a yet-existing matrix element
339static
340void* MatrixElemDup(cmsContext ContextID, cmsStage* mpe)
341{
342 _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data;
343 _cmsStageMatrixData* NewElem;
344 cmsUInt32Number sz;
345
346 NewElem = (_cmsStageMatrixData*) _cmsMallocZero(ContextID, sizeof(_cmsStageMatrixData));
347 if (NewElem == NULL) return NULL;
348
349 sz = mpe ->InputChannels * mpe ->OutputChannels;
350
351 NewElem ->Double = (cmsFloat64Number*) _cmsDupMem(ContextID, Data ->Double, sz * sizeof(cmsFloat64Number)) ;
352
353 if (Data ->Offset)
354 NewElem ->Offset = (cmsFloat64Number*) _cmsDupMem(ContextID,
355 Data ->Offset, mpe -> OutputChannels * sizeof(cmsFloat64Number)) ;
356
357 return (void*) NewElem;
358}
359
360
361static
362void MatrixElemTypeFree(cmsContext ContextID, cmsStage* mpe)
363{
364 _cmsStageMatrixData* Data = (_cmsStageMatrixData*) mpe ->Data;
365 if (Data == NULL)
366 return;
367 if (Data ->Double)
368 _cmsFree(ContextID, Data ->Double);
369
370 if (Data ->Offset)
371 _cmsFree(ContextID, Data ->Offset);
372
373 _cmsFree(ContextID, mpe ->Data);
374}
375
376
377
378cmsStage* CMSEXPORT cmsStageAllocMatrix(cmsContext ContextID, cmsUInt32Number Rows, cmsUInt32Number Cols,
379 const cmsFloat64Number* Matrix, const cmsFloat64Number* Offset)
380{
381 cmsUInt32Number i, n;
382 _cmsStageMatrixData* NewElem;
383 cmsStage* NewMPE;
384
385 n = Rows * Cols;
386
387 // Check for overflow
388 if (n == 0) return NULL;
389 if (n >= UINT_MAX / Cols) return NULL;
390 if (n >= UINT_MAX / Rows) return NULL;
391 if (n < Rows || n < Cols) return NULL;
392
393 NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigMatrixElemType, Cols, Rows,
394 EvaluateMatrix, MatrixElemDup, MatrixElemTypeFree, NULL );
395 if (NewMPE == NULL) return NULL;
396
397
398 NewElem = (_cmsStageMatrixData*) _cmsMallocZero(ContextID, sizeof(_cmsStageMatrixData));
399 if (NewElem == NULL) goto Error;
400 NewMPE ->Data = (void*) NewElem;
401
402
403 NewElem ->Double = (cmsFloat64Number*) _cmsCalloc(ContextID, n, sizeof(cmsFloat64Number));
404 if (NewElem->Double == NULL) goto Error;
405
406 for (i=0; i < n; i++) {
407 NewElem ->Double[i] = Matrix[i];
408 }
409
410
411 if (Offset != NULL) {
412
413 NewElem ->Offset = (cmsFloat64Number*) _cmsCalloc(ContextID, Rows, sizeof(cmsFloat64Number));
414 if (NewElem->Offset == NULL) goto Error;
415
416 for (i=0; i < Rows; i++) {
417 NewElem ->Offset[i] = Offset[i];
418 }
419
420 }
421
422 return NewMPE;
423
424Error:
425 cmsStageFree(ContextID, NewMPE);
426 return NULL;
427}
428
429
430// *************************************************************************************************
431// Type cmsSigCLutElemType
432// *************************************************************************************************
433
434
435// Evaluate in true floating point
436static
437void EvaluateCLUTfloat(cmsContext ContextID, const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe)
438{
439 _cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data;
440
441 Data -> Params ->Interpolation.LerpFloat(ContextID, In, Out, Data->Params);
442}
443
444
445// Convert to 16 bits, evaluate, and back to floating point
446static
447void EvaluateCLUTfloatIn16(cmsContext ContextID, const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe)
448{
449 _cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data;
450 cmsUInt16Number In16[MAX_STAGE_CHANNELS], Out16[MAX_STAGE_CHANNELS];
451
452 _cmsAssert(mpe ->InputChannels <= MAX_STAGE_CHANNELS);
453 _cmsAssert(mpe ->OutputChannels <= MAX_STAGE_CHANNELS);
454
455 FromFloatTo16(In, In16, mpe ->InputChannels);
456 Data -> Params ->Interpolation.Lerp16(ContextID, In16, Out16, Data->Params);
457 From16ToFloat(Out16, Out, mpe ->OutputChannels);
458}
459
460
461// Given an hypercube of b dimensions, with Dims[] number of nodes by dimension, calculate the total amount of nodes
462static
463cmsUInt32Number CubeSize(const cmsUInt32Number Dims[], cmsUInt32Number b)
464{
465 cmsUInt32Number rv, dim;
466
467 _cmsAssert(Dims != NULL);
468
469 for (rv = 1; b > 0; b--) {
470
471 dim = Dims[b-1];
472 if (dim == 0) return 0; // Error
473
474 rv *= dim;
475
476 // Check for overflow
477 if (rv > UINT_MAX / dim) return 0;
478 }
479
480 return rv;
481}
482
483static
484void* CLUTElemDup(cmsContext ContextID, cmsStage* mpe)
485{
486 _cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data;
487 _cmsStageCLutData* NewElem;
488
489
490 NewElem = (_cmsStageCLutData*) _cmsMallocZero(ContextID, sizeof(_cmsStageCLutData));
491 if (NewElem == NULL) return NULL;
492
493 NewElem ->nEntries = Data ->nEntries;
494 NewElem ->HasFloatValues = Data ->HasFloatValues;
495
496 if (Data ->Tab.T) {
497
498 if (Data ->HasFloatValues) {
499 NewElem ->Tab.TFloat = (cmsFloat32Number*) _cmsDupMem(ContextID, Data ->Tab.TFloat, Data ->nEntries * sizeof (cmsFloat32Number));
500 if (NewElem ->Tab.TFloat == NULL)
501 goto Error;
502 } else {
503 NewElem ->Tab.T = (cmsUInt16Number*) _cmsDupMem(ContextID, Data ->Tab.T, Data ->nEntries * sizeof (cmsUInt16Number));
504 if (NewElem ->Tab.T == NULL)
505 goto Error;
506 }
507 }
508
509 NewElem ->Params = _cmsComputeInterpParamsEx(ContextID,
510 Data ->Params ->nSamples,
511 Data ->Params ->nInputs,
512 Data ->Params ->nOutputs,
513 NewElem ->Tab.T,
514 Data ->Params ->dwFlags);
515 if (NewElem->Params != NULL)
516 return (void*) NewElem;
517 Error:
518 if (NewElem->Tab.T)
519 // This works for both types
520 _cmsFree(ContextID, NewElem -> Tab.T);
521 _cmsFree(ContextID, NewElem);
522 return NULL;
523}
524
525
526static
527void CLutElemTypeFree(cmsContext ContextID, cmsStage* mpe)
528{
529
530 _cmsStageCLutData* Data = (_cmsStageCLutData*) mpe ->Data;
531
532 // Already empty
533 if (Data == NULL) return;
534
535 // This works for both types
536 if (Data -> Tab.T)
537 _cmsFree(ContextID, Data -> Tab.T);
538
539 _cmsFreeInterpParams(ContextID, Data ->Params);
540 _cmsFree(ContextID, mpe ->Data);
541}
542
543
544// Allocates a 16-bit multidimensional CLUT. This is evaluated at 16-bit precision. Table may have different
545// granularity on each dimension.
546cmsStage* CMSEXPORT cmsStageAllocCLut16bitGranular(cmsContext ContextID,
547 const cmsUInt32Number clutPoints[],
548 cmsUInt32Number inputChan,
549 cmsUInt32Number outputChan,
550 const cmsUInt16Number* Table)
551{
552 cmsUInt32Number i, n;
553 _cmsStageCLutData* NewElem;
554 cmsStage* NewMPE;
555
556 _cmsAssert(clutPoints != NULL);
557
558 if (inputChan > MAX_INPUT_DIMENSIONS) {
559 cmsSignalError(ContextID, cmsERROR_RANGE, "Too many input channels (%d channels, max=%d)", inputChan, MAX_INPUT_DIMENSIONS);
560 return NULL;
561 }
562
563 NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigCLutElemType, inputChan, outputChan,
564 EvaluateCLUTfloatIn16, CLUTElemDup, CLutElemTypeFree, NULL );
565
566 if (NewMPE == NULL) return NULL;
567
568 NewElem = (_cmsStageCLutData*) _cmsMallocZero(ContextID, sizeof(_cmsStageCLutData));
569 if (NewElem == NULL) {
570 cmsStageFree(ContextID, NewMPE);
571 return NULL;
572 }
573
574 NewMPE ->Data = (void*) NewElem;
575
576 NewElem -> nEntries = n = outputChan * CubeSize(clutPoints, inputChan);
577 NewElem -> HasFloatValues = FALSE;
578
579 if (n == 0) {
580 cmsStageFree(ContextID, NewMPE);
581 return NULL;
582 }
583
584
585 NewElem ->Tab.T = (cmsUInt16Number*) _cmsCalloc(ContextID, n, sizeof(cmsUInt16Number));
586 if (NewElem ->Tab.T == NULL) {
587 cmsStageFree(ContextID, NewMPE);
588 return NULL;
589 }
590
591 if (Table != NULL) {
592 for (i=0; i < n; i++) {
593 NewElem ->Tab.T[i] = Table[i];
594 }
595 }
596
597 NewElem ->Params = _cmsComputeInterpParamsEx(ContextID, clutPoints, inputChan, outputChan, NewElem ->Tab.T, CMS_LERP_FLAGS_16BITS);
598 if (NewElem ->Params == NULL) {
599 cmsStageFree(ContextID, NewMPE);
600 return NULL;
601 }
602
603 return NewMPE;
604}
605
606cmsStage* CMSEXPORT cmsStageAllocCLut16bit(cmsContext ContextID,
607 cmsUInt32Number nGridPoints,
608 cmsUInt32Number inputChan,
609 cmsUInt32Number outputChan,
610 const cmsUInt16Number* Table)
611{
612 cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS];
613 int i;
614
615 // Our resulting LUT would be same gridpoints on all dimensions
616 for (i=0; i < MAX_INPUT_DIMENSIONS; i++)
617 Dimensions[i] = nGridPoints;
618
619 return cmsStageAllocCLut16bitGranular(ContextID, Dimensions, inputChan, outputChan, Table);
620}
621
622
623cmsStage* CMSEXPORT cmsStageAllocCLutFloat(cmsContext ContextID,
624 cmsUInt32Number nGridPoints,
625 cmsUInt32Number inputChan,
626 cmsUInt32Number outputChan,
627 const cmsFloat32Number* Table)
628{
629 cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS];
630 int i;
631
632 // Our resulting LUT would be same gridpoints on all dimensions
633 for (i=0; i < MAX_INPUT_DIMENSIONS; i++)
634 Dimensions[i] = nGridPoints;
635
636 return cmsStageAllocCLutFloatGranular(ContextID, Dimensions, inputChan, outputChan, Table);
637}
638
639
640
641cmsStage* CMSEXPORT cmsStageAllocCLutFloatGranular(cmsContext ContextID, const cmsUInt32Number clutPoints[], cmsUInt32Number inputChan, cmsUInt32Number outputChan, const cmsFloat32Number* Table)
642{
643 cmsUInt32Number i, n;
644 _cmsStageCLutData* NewElem;
645 cmsStage* NewMPE;
646
647 _cmsAssert(clutPoints != NULL);
648
649 if (inputChan > MAX_INPUT_DIMENSIONS) {
650 cmsSignalError(ContextID, cmsERROR_RANGE, "Too many input channels (%d channels, max=%d)", inputChan, MAX_INPUT_DIMENSIONS);
651 return NULL;
652 }
653
654 NewMPE = _cmsStageAllocPlaceholder(ContextID, cmsSigCLutElemType, inputChan, outputChan,
655 EvaluateCLUTfloat, CLUTElemDup, CLutElemTypeFree, NULL);
656 if (NewMPE == NULL) return NULL;
657
658
659 NewElem = (_cmsStageCLutData*) _cmsMallocZero(ContextID, sizeof(_cmsStageCLutData));
660 if (NewElem == NULL) {
661 cmsStageFree(ContextID, NewMPE);
662 return NULL;
663 }
664
665 NewMPE ->Data = (void*) NewElem;
666
667 // There is a potential integer overflow on conputing n and nEntries.
668 NewElem -> nEntries = n = outputChan * CubeSize(clutPoints, inputChan);
669 NewElem -> HasFloatValues = TRUE;
670
671 if (n == 0) {
672 cmsStageFree(ContextID, NewMPE);
673 return NULL;
674 }
675
676 NewElem ->Tab.TFloat = (cmsFloat32Number*) _cmsCalloc(ContextID, n, sizeof(cmsFloat32Number));
677 if (NewElem ->Tab.TFloat == NULL) {
678 cmsStageFree(ContextID, NewMPE);
679 return NULL;
680 }
681
682 if (Table != NULL) {
683 for (i=0; i < n; i++) {
684 NewElem ->Tab.TFloat[i] = Table[i];
685 }
686 }
687
688 NewElem ->Params = _cmsComputeInterpParamsEx(ContextID, clutPoints, inputChan, outputChan, NewElem ->Tab.TFloat, CMS_LERP_FLAGS_FLOAT);
689 if (NewElem ->Params == NULL) {
690 cmsStageFree(ContextID, NewMPE);
691 return NULL;
692 }
693
694 return NewMPE;
695}
696
697
698static
699int IdentitySampler(cmsContext ContextID, register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void * Cargo)
700{
701 int nChan = *(int*) Cargo;
702 int i;
703 cmsUNUSED_PARAMETER(ContextID);
704
705 for (i=0; i < nChan; i++)
706 Out[i] = In[i];
707
708 return 1;
709}
710
711// Creates an MPE that just copies input to output
712cmsStage* CMSEXPORT _cmsStageAllocIdentityCLut(cmsContext ContextID, cmsUInt32Number nChan)
713{
714 cmsUInt32Number Dimensions[MAX_INPUT_DIMENSIONS];
715 cmsStage* mpe ;
716 int i;
717
718 for (i=0; i < MAX_INPUT_DIMENSIONS; i++)
719 Dimensions[i] = 2;
720
721 mpe = cmsStageAllocCLut16bitGranular(ContextID, Dimensions, nChan, nChan, NULL);
722 if (mpe == NULL) return NULL;
723
724 if (!cmsStageSampleCLut16bit(ContextID, mpe, IdentitySampler, &nChan, 0)) {
725 cmsStageFree(ContextID, mpe);
726 return NULL;
727 }
728
729 mpe ->Implements = cmsSigIdentityElemType;
730 return mpe;
731}
732
733
734
735// Quantize a value 0 <= i < MaxSamples to 0..0xffff
736cmsUInt16Number CMSEXPORT _cmsQuantizeVal(cmsFloat64Number i, cmsUInt32Number MaxSamples)
737{
738 cmsFloat64Number x;
739
740 x = ((cmsFloat64Number) i * 65535.) / (cmsFloat64Number) (MaxSamples - 1);
741 return _cmsQuickSaturateWord(x);
742}
743
744
745// This routine does a sweep on whole input space, and calls its callback
746// function on knots. returns TRUE if all ok, FALSE otherwise.
747cmsBool CMSEXPORT cmsStageSampleCLut16bit(cmsContext ContextID, cmsStage* mpe, cmsSAMPLER16 Sampler, void * Cargo, cmsUInt32Number dwFlags)
748{
749 int i, t, index, rest;
750 cmsUInt32Number nTotalPoints;
751 cmsUInt32Number nInputs, nOutputs;
752 cmsUInt32Number* nSamples;
753 cmsUInt16Number In[MAX_INPUT_DIMENSIONS+1], Out[MAX_STAGE_CHANNELS];
754 _cmsStageCLutData* clut;
755
756 if (mpe == NULL) return FALSE;
757
758 clut = (_cmsStageCLutData*) mpe->Data;
759
760 if (clut == NULL) return FALSE;
761
762 nSamples = clut->Params ->nSamples;
763 nInputs = clut->Params ->nInputs;
764 nOutputs = clut->Params ->nOutputs;
765
766 if (nInputs <= 0) return FALSE;
767 if (nOutputs <= 0) return FALSE;
768 if (nInputs > MAX_INPUT_DIMENSIONS) return FALSE;
769 if (nOutputs >= MAX_STAGE_CHANNELS) return FALSE;
770
771 memset(In, 0, sizeof(In));
772 memset(Out, 0, sizeof(Out));
773
774 nTotalPoints = CubeSize(nSamples, nInputs);
775 if (nTotalPoints == 0) return FALSE;
776
777 index = 0;
778 for (i = 0; i < (int) nTotalPoints; i++) {
779
780 rest = i;
781 for (t = (int)nInputs - 1; t >= 0; --t) {
782
783 cmsUInt32Number Colorant = rest % nSamples[t];
784
785 rest /= nSamples[t];
786
787 In[t] = _cmsQuantizeVal(Colorant, nSamples[t]);
788 }
789
790 if (clut ->Tab.T != NULL) {
791 for (t = 0; t < (int)nOutputs; t++)
792 Out[t] = clut->Tab.T[index + t];
793 }
794
795 if (!Sampler(ContextID, In, Out, Cargo))
796 return FALSE;
797
798 if (!(dwFlags & SAMPLER_INSPECT)) {
799
800 if (clut ->Tab.T != NULL) {
801 for (t=0; t < (int) nOutputs; t++)
802 clut->Tab.T[index + t] = Out[t];
803 }
804 }
805
806 index += nOutputs;
807 }
808
809 return TRUE;
810}
811
812// Same as anterior, but for floating point
813cmsBool CMSEXPORT cmsStageSampleCLutFloat(cmsContext ContextID, cmsStage* mpe, cmsSAMPLERFLOAT Sampler, void * Cargo, cmsUInt32Number dwFlags)
814{
815 int i, t, index, rest;
816 cmsUInt32Number nTotalPoints;
817 cmsUInt32Number nInputs, nOutputs;
818 cmsUInt32Number* nSamples;
819 cmsFloat32Number In[MAX_INPUT_DIMENSIONS+1], Out[MAX_STAGE_CHANNELS];
820 _cmsStageCLutData* clut = (_cmsStageCLutData*) mpe->Data;
821
822 nSamples = clut->Params ->nSamples;
823 nInputs = clut->Params ->nInputs;
824 nOutputs = clut->Params ->nOutputs;
825
826 if (nInputs <= 0) return FALSE;
827 if (nOutputs <= 0) return FALSE;
828 if (nInputs > MAX_INPUT_DIMENSIONS) return FALSE;
829 if (nOutputs >= MAX_STAGE_CHANNELS) return FALSE;
830
831 nTotalPoints = CubeSize(nSamples, nInputs);
832 if (nTotalPoints == 0) return FALSE;
833
834 index = 0;
835 for (i = 0; i < (int)nTotalPoints; i++) {
836
837 rest = i;
838 for (t = (int) nInputs-1; t >=0; --t) {
839
840 cmsUInt32Number Colorant = rest % nSamples[t];
841
842 rest /= nSamples[t];
843
844 In[t] = (cmsFloat32Number) (_cmsQuantizeVal(Colorant, nSamples[t]) / 65535.0);
845 }
846
847 if (clut ->Tab.TFloat != NULL) {
848 for (t=0; t < (int) nOutputs; t++)
849 Out[t] = clut->Tab.TFloat[index + t];
850 }
851
852 if (!Sampler(ContextID, In, Out, Cargo))
853 return FALSE;
854
855 if (!(dwFlags & SAMPLER_INSPECT)) {
856
857 if (clut ->Tab.TFloat != NULL) {
858 for (t=0; t < (int) nOutputs; t++)
859 clut->Tab.TFloat[index + t] = Out[t];
860 }
861 }
862
863 index += nOutputs;
864 }
865
866 return TRUE;
867}
868
869
870
871// This routine does a sweep on whole input space, and calls its callback
872// function on knots. returns TRUE if all ok, FALSE otherwise.
873cmsBool CMSEXPORT cmsSliceSpace16(cmsContext ContextID, cmsUInt32Number nInputs, const cmsUInt32Number clutPoints[],
874 cmsSAMPLER16 Sampler, void * Cargo)
875{
876 int i, t, rest;
877 cmsUInt32Number nTotalPoints;
878 cmsUInt16Number In[cmsMAXCHANNELS];
879
880 if (nInputs >= cmsMAXCHANNELS) return FALSE;
881
882 nTotalPoints = CubeSize(clutPoints, nInputs);
883 if (nTotalPoints == 0) return FALSE;
884
885 for (i = 0; i < (int) nTotalPoints; i++) {
886
887 rest = i;
888 for (t = (int) nInputs-1; t >=0; --t) {
889
890 cmsUInt32Number Colorant = rest % clutPoints[t];
891
892 rest /= clutPoints[t];
893 In[t] = _cmsQuantizeVal(Colorant, clutPoints[t]);
894
895 }
896
897 if (!Sampler(ContextID, In, NULL, Cargo))
898 return FALSE;
899 }
900
901 return TRUE;
902}
903
904cmsInt32Number CMSEXPORT cmsSliceSpaceFloat(cmsContext ContextID, cmsUInt32Number nInputs, const cmsUInt32Number clutPoints[],
905 cmsSAMPLERFLOAT Sampler, void * Cargo)
906{
907 int i, t, rest;
908 cmsUInt32Number nTotalPoints;
909 cmsFloat32Number In[cmsMAXCHANNELS];
910
911 if (nInputs >= cmsMAXCHANNELS) return FALSE;
912
913 nTotalPoints = CubeSize(clutPoints, nInputs);
914 if (nTotalPoints == 0) return FALSE;
915
916 for (i = 0; i < (int) nTotalPoints; i++) {
917
918 rest = i;
919 for (t = (int) nInputs-1; t >=0; --t) {
920
921 cmsUInt32Number Colorant = rest % clutPoints[t];
922
923 rest /= clutPoints[t];
924 In[t] = (cmsFloat32Number) (_cmsQuantizeVal(Colorant, clutPoints[t]) / 65535.0);
925
926 }
927
928 if (!Sampler(ContextID, In, NULL, Cargo))
929 return FALSE;
930 }
931
932 return TRUE;
933}
934
935// ********************************************************************************
936// Type cmsSigLab2XYZElemType
937// ********************************************************************************
938
939
940static
941void EvaluateLab2XYZ(cmsContext ContextID, const cmsFloat32Number In[],
942 cmsFloat32Number Out[],
943 const cmsStage *mpe)
944{
945 cmsCIELab Lab;
946 cmsCIEXYZ XYZ;
947 const cmsFloat64Number XYZadj = MAX_ENCODEABLE_XYZ;
948
949 // V4 rules
950 Lab.L = In[0] * 100.0;
951 Lab.a = In[1] * 255.0 - 128.0;
952 Lab.b = In[2] * 255.0 - 128.0;
953
954 cmsLab2XYZ(ContextID, NULL, &XYZ, &Lab);
955
956 // From XYZ, range 0..19997 to 0..1.0, note that 1.99997 comes from 0xffff
957 // encoded as 1.15 fixed point, so 1 + (32767.0 / 32768.0)
958
959 Out[0] = (cmsFloat32Number) ((cmsFloat64Number) XYZ.X / XYZadj);
960 Out[1] = (cmsFloat32Number) ((cmsFloat64Number) XYZ.Y / XYZadj);
961 Out[2] = (cmsFloat32Number) ((cmsFloat64Number) XYZ.Z / XYZadj);
962 return;
963
964 cmsUNUSED_PARAMETER(mpe);
965}
966
967
968// No dup or free routines needed, as the structure has no pointers in it.
969cmsStage* CMSEXPORT _cmsStageAllocLab2XYZ(cmsContext ContextID)
970{
971 return _cmsStageAllocPlaceholder(ContextID, cmsSigLab2XYZElemType, 3, 3, EvaluateLab2XYZ, NULL, NULL, NULL);
972}
973
974// ********************************************************************************
975
976// v2 L=100 is supposed to be placed on 0xFF00. There is no reasonable
977// number of gridpoints that would make exact match. However, a prelinearization
978// of 258 entries, would map 0xFF00 exactly on entry 257, and this is good to avoid scum dot.
979// Almost all what we need but unfortunately, the rest of entries should be scaled by
980// (255*257/256) and this is not exact.
981
982cmsStage* _cmsStageAllocLabV2ToV4curves(cmsContext ContextID)
983{
984 cmsStage* mpe;
985 cmsToneCurve* LabTable[3];
986 int i, j;
987
988 LabTable[0] = cmsBuildTabulatedToneCurve16(ContextID, 258, NULL);
989 LabTable[1] = cmsBuildTabulatedToneCurve16(ContextID, 258, NULL);
990 LabTable[2] = cmsBuildTabulatedToneCurve16(ContextID, 258, NULL);
991
992 for (j=0; j < 3; j++) {
993
994 if (LabTable[j] == NULL) {
995 cmsFreeToneCurveTriple(ContextID, LabTable);
996 return NULL;
997 }
998
999 // We need to map * (0xffff / 0xff00), that's same as (257 / 256)
1000 // So we can use 258-entry tables to do the trick (i / 257) * (255 * 257) * (257 / 256);
1001 for (i=0; i < 257; i++) {
1002
1003 LabTable[j]->Table16[i] = (cmsUInt16Number) ((i * 0xffff + 0x80) >> 8);
1004 }
1005
1006 LabTable[j] ->Table16[257] = 0xffff;
1007 }
1008
1009 mpe = cmsStageAllocToneCurves(ContextID, 3, LabTable);
1010 cmsFreeToneCurveTriple(ContextID, LabTable);
1011
1012 if (mpe == NULL) return NULL;
1013 mpe ->Implements = cmsSigLabV2toV4;
1014 return mpe;
1015}
1016
1017// ********************************************************************************
1018
1019// Matrix-based conversion, which is more accurate, but slower and cannot properly be saved in devicelink profiles
1020cmsStage* CMSEXPORT _cmsStageAllocLabV2ToV4(cmsContext ContextID)
1021{
1022 static const cmsFloat64Number V2ToV4[] = { 65535.0/65280.0, 0, 0,
1023 0, 65535.0/65280.0, 0,
1024 0, 0, 65535.0/65280.0
1025 };
1026
1027 cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, V2ToV4, NULL);
1028
1029 if (mpe == NULL) return mpe;
1030 mpe ->Implements = cmsSigLabV2toV4;
1031 return mpe;
1032}
1033
1034
1035// Reverse direction
1036cmsStage* CMSEXPORT _cmsStageAllocLabV4ToV2(cmsContext ContextID)
1037{
1038 static const cmsFloat64Number V4ToV2[] = { 65280.0/65535.0, 0, 0,
1039 0, 65280.0/65535.0, 0,
1040 0, 0, 65280.0/65535.0
1041 };
1042
1043 cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, V4ToV2, NULL);
1044
1045 if (mpe == NULL) return mpe;
1046 mpe ->Implements = cmsSigLabV4toV2;
1047 return mpe;
1048}
1049
1050
1051// To Lab to float. Note that the MPE gives numbers in normal Lab range
1052// and we need 0..1.0 range for the formatters
1053// L* : 0...100 => 0...1.0 (L* / 100)
1054// ab* : -128..+127 to 0..1 ((ab* + 128) / 255)
1055
1056cmsStage* _cmsStageNormalizeFromLabFloat(cmsContext ContextID)
1057{
1058 static const cmsFloat64Number a1[] = {
1059 1.0/100.0, 0, 0,
1060 0, 1.0/255.0, 0,
1061 0, 0, 1.0/255.0
1062 };
1063
1064 static const cmsFloat64Number o1[] = {
1065 0,
1066 128.0/255.0,
1067 128.0/255.0
1068 };
1069
1070 cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, o1);
1071
1072 if (mpe == NULL) return mpe;
1073 mpe ->Implements = cmsSigLab2FloatPCS;
1074 return mpe;
1075}
1076
1077// Fom XYZ to floating point PCS
1078cmsStage* _cmsStageNormalizeFromXyzFloat(cmsContext ContextID)
1079{
1080#define n (32768.0/65535.0)
1081 static const cmsFloat64Number a1[] = {
1082 n, 0, 0,
1083 0, n, 0,
1084 0, 0, n
1085 };
1086#undef n
1087
1088 cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, NULL);
1089
1090 if (mpe == NULL) return mpe;
1091 mpe ->Implements = cmsSigXYZ2FloatPCS;
1092 return mpe;
1093}
1094
1095cmsStage* _cmsStageNormalizeToLabFloat(cmsContext ContextID)
1096{
1097 static const cmsFloat64Number a1[] = {
1098 100.0, 0, 0,
1099 0, 255.0, 0,
1100 0, 0, 255.0
1101 };
1102
1103 static const cmsFloat64Number o1[] = {
1104 0,
1105 -128.0,
1106 -128.0
1107 };
1108
1109 cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, o1);
1110 if (mpe == NULL) return mpe;
1111 mpe ->Implements = cmsSigFloatPCS2Lab;
1112 return mpe;
1113}
1114
1115cmsStage* _cmsStageNormalizeToXyzFloat(cmsContext ContextID)
1116{
1117#define n (65535.0/32768.0)
1118
1119 static const cmsFloat64Number a1[] = {
1120 n, 0, 0,
1121 0, n, 0,
1122 0, 0, n
1123 };
1124#undef n
1125
1126 cmsStage *mpe = cmsStageAllocMatrix(ContextID, 3, 3, a1, NULL);
1127 if (mpe == NULL) return mpe;
1128 mpe ->Implements = cmsSigFloatPCS2XYZ;
1129 return mpe;
1130}
1131
1132// Clips values smaller than zero
1133static
1134void Clipper(cmsContext ContextID, const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe)
1135{
1136 cmsUInt32Number i;
1137 cmsUNUSED_PARAMETER(ContextID);
1138 for (i = 0; i < mpe->InputChannels; i++) {
1139
1140 cmsFloat32Number n = In[i];
1141 Out[i] = n < 0 ? 0 : n;
1142 }
1143}
1144
1145cmsStage* _cmsStageClipNegatives(cmsContext ContextID, cmsUInt32Number nChannels)
1146{
1147 return _cmsStageAllocPlaceholder(ContextID, cmsSigClipNegativesElemType,
1148 nChannels, nChannels, Clipper, NULL, NULL, NULL);
1149}
1150
1151// ********************************************************************************
1152// Type cmsSigXYZ2LabElemType
1153// ********************************************************************************
1154
1155static
1156void EvaluateXYZ2Lab(cmsContext ContextID, const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe)
1157{
1158 cmsCIELab Lab;
1159 cmsCIEXYZ XYZ;
1160 const cmsFloat64Number XYZadj = MAX_ENCODEABLE_XYZ;
1161
1162 // From 0..1.0 to XYZ
1163
1164 XYZ.X = In[0] * XYZadj;
1165 XYZ.Y = In[1] * XYZadj;
1166 XYZ.Z = In[2] * XYZadj;
1167
1168 cmsXYZ2Lab(ContextID, NULL, &Lab, &XYZ);
1169
1170 // From V4 Lab to 0..1.0
1171
1172 Out[0] = (cmsFloat32Number) (Lab.L / 100.0);
1173 Out[1] = (cmsFloat32Number) ((Lab.a + 128.0) / 255.0);
1174 Out[2] = (cmsFloat32Number) ((Lab.b + 128.0) / 255.0);
1175 return;
1176
1177 cmsUNUSED_PARAMETER(mpe);
1178}
1179
1180cmsStage* CMSEXPORT _cmsStageAllocXYZ2Lab(cmsContext ContextID)
1181{
1182 return _cmsStageAllocPlaceholder(ContextID, cmsSigXYZ2LabElemType, 3, 3, EvaluateXYZ2Lab, NULL, NULL, NULL);
1183
1184}
1185
1186// ********************************************************************************
1187
1188// For v4, S-Shaped curves are placed in a/b axis to increase resolution near gray
1189
1190cmsStage* _cmsStageAllocLabPrelin(cmsContext ContextID)
1191{
1192 cmsToneCurve* LabTable[3];
1193 cmsFloat64Number Params[1] = {2.4} ;
1194
1195 LabTable[0] = cmsBuildGamma(ContextID, 1.0);
1196 LabTable[1] = cmsBuildParametricToneCurve(ContextID, 108, Params);
1197 LabTable[2] = cmsBuildParametricToneCurve(ContextID, 108, Params);
1198
1199 return cmsStageAllocToneCurves(ContextID, 3, LabTable);
1200}
1201
1202
1203// Free a single MPE
1204void CMSEXPORT cmsStageFree(cmsContext ContextID, cmsStage* mpe)
1205{
1206 if (mpe ->FreePtr)
1207 mpe ->FreePtr(ContextID, mpe);
1208
1209 _cmsFree(ContextID, mpe);
1210}
1211
1212
1213cmsUInt32Number CMSEXPORT cmsStageInputChannels(cmsContext ContextID, const cmsStage* mpe)
1214{
1215 cmsUNUSED_PARAMETER(ContextID);
1216 return mpe ->InputChannels;
1217}
1218
1219cmsUInt32Number CMSEXPORT cmsStageOutputChannels(cmsContext ContextID, const cmsStage* mpe)
1220{
1221 cmsUNUSED_PARAMETER(ContextID);
1222 return mpe ->OutputChannels;
1223}
1224
1225cmsStageSignature CMSEXPORT cmsStageType(cmsContext ContextID, const cmsStage* mpe)
1226{
1227 cmsUNUSED_PARAMETER(ContextID);
1228 return mpe -> Type;
1229}
1230
1231void* CMSEXPORT cmsStageData(cmsContext ContextID, const cmsStage* mpe)
1232{
1233 cmsUNUSED_PARAMETER(ContextID);
1234 return mpe -> Data;
1235}
1236
1237cmsStage* CMSEXPORT cmsStageNext(cmsContext ContextID, const cmsStage* mpe)
1238{
1239 cmsUNUSED_PARAMETER(ContextID);
1240 return mpe -> Next;
1241}
1242
1243
1244// Duplicates an MPE
1245cmsStage* CMSEXPORT cmsStageDup(cmsContext ContextID, cmsStage* mpe)
1246{
1247 cmsStage* NewMPE;
1248
1249 if (mpe == NULL) return NULL;
1250 NewMPE = _cmsStageAllocPlaceholder(ContextID,
1251 mpe ->Type,
1252 mpe ->InputChannels,
1253 mpe ->OutputChannels,
1254 mpe ->EvalPtr,
1255 mpe ->DupElemPtr,
1256 mpe ->FreePtr,
1257 NULL);
1258 if (NewMPE == NULL) return NULL;
1259
1260 NewMPE ->Implements = mpe ->Implements;
1261
1262 if (mpe ->DupElemPtr) {
1263
1264 NewMPE ->Data = mpe ->DupElemPtr(ContextID, mpe);
1265
1266 if (NewMPE->Data == NULL) {
1267
1268 cmsStageFree(ContextID, NewMPE);
1269 return NULL;
1270 }
1271
1272 } else {
1273
1274 NewMPE ->Data = NULL;
1275 }
1276
1277 return NewMPE;
1278}
1279
1280
1281// ***********************************************************************************************************
1282
1283// This function sets up the channel count
1284static
1285cmsBool BlessLUT(cmsContext ContextID, cmsPipeline* lut)
1286{
1287 // We can set the input/output channels only if we have elements.
1288 if (lut ->Elements != NULL) {
1289
1290 cmsStage* prev;
1291 cmsStage* next;
1292 cmsStage* First;
1293 cmsStage* Last;
1294
1295 First = cmsPipelineGetPtrToFirstStage(ContextID, lut);
1296 Last = cmsPipelineGetPtrToLastStage(ContextID, lut);
1297
1298 if (First == NULL || Last == NULL) return FALSE;
1299
1300 lut->InputChannels = First->InputChannels;
1301 lut->OutputChannels = Last->OutputChannels;
1302
1303 // Check chain consistency
1304 prev = First;
1305 next = prev->Next;
1306
1307 while (next != NULL)
1308 {
1309 if (next->InputChannels != prev->OutputChannels)
1310 return FALSE;
1311
1312 next = next->Next;
1313 prev = prev->Next;
1314 }
1315}
1316
1317 return TRUE;
1318}
1319
1320
1321// Default to evaluate the LUT on 16 bit-basis. Precision is retained.
1322static
1323void _LUTeval16(cmsContext ContextID, register const cmsUInt16Number In[], register cmsUInt16Number Out[], register const void* D)
1324{
1325 cmsPipeline* lut = (cmsPipeline*) D;
1326 cmsStage *mpe;
1327 cmsFloat32Number Storage[2][MAX_STAGE_CHANNELS];
1328 int Phase = 0, NextPhase;
1329
1330 From16ToFloat(In, &Storage[Phase][0], lut ->InputChannels);
1331
1332 for (mpe = lut ->Elements;
1333 mpe != NULL;
1334 mpe = mpe ->Next) {
1335
1336 NextPhase = Phase ^ 1;
1337 mpe ->EvalPtr(ContextID, &Storage[Phase][0], &Storage[NextPhase][0], mpe);
1338 Phase = NextPhase;
1339 }
1340
1341
1342 FromFloatTo16(&Storage[Phase][0], Out, lut ->OutputChannels);
1343}
1344
1345
1346
1347// Does evaluate the LUT on cmsFloat32Number-basis.
1348static
1349void _LUTevalFloat(cmsContext ContextID, register const cmsFloat32Number In[], register cmsFloat32Number Out[], const void* D)
1350{
1351 cmsPipeline* lut = (cmsPipeline*) D;
1352 cmsStage *mpe;
1353 cmsFloat32Number Storage[2][MAX_STAGE_CHANNELS];
1354 int Phase = 0, NextPhase;
1355
1356 memmove(&Storage[Phase][0], In, lut ->InputChannels * sizeof(cmsFloat32Number));
1357
1358 for (mpe = lut ->Elements;
1359 mpe != NULL;
1360 mpe = mpe ->Next) {
1361
1362 NextPhase = Phase ^ 1;
1363 mpe ->EvalPtr(ContextID, &Storage[Phase][0], &Storage[NextPhase][0], mpe);
1364 Phase = NextPhase;
1365 }
1366
1367 memmove(Out, &Storage[Phase][0], lut ->OutputChannels * sizeof(cmsFloat32Number));
1368}
1369
1370
1371// LUT Creation & Destruction
1372cmsPipeline* CMSEXPORT cmsPipelineAlloc(cmsContext ContextID, cmsUInt32Number InputChannels, cmsUInt32Number OutputChannels)
1373{
1374 cmsPipeline* NewLUT;
1375
1376 // A value of zero in channels is allowed as placeholder
1377 if (InputChannels >= cmsMAXCHANNELS ||
1378 OutputChannels >= cmsMAXCHANNELS) return NULL;
1379
1380 NewLUT = (cmsPipeline*) _cmsMallocZero(ContextID, sizeof(cmsPipeline));
1381 if (NewLUT == NULL) return NULL;
1382
1383 NewLUT -> InputChannels = InputChannels;
1384 NewLUT -> OutputChannels = OutputChannels;
1385
1386 NewLUT ->Eval16Fn = _LUTeval16;
1387 NewLUT ->EvalFloatFn = _LUTevalFloat;
1388 NewLUT ->DupDataFn = NULL;
1389 NewLUT ->FreeDataFn = NULL;
1390 NewLUT ->Data = NewLUT;
1391
1392 if (!BlessLUT(ContextID, NewLUT))
1393 {
1394 _cmsFree(ContextID, NewLUT);
1395 return NULL;
1396 }
1397
1398 return NewLUT;
1399}
1400
1401cmsUInt32Number CMSEXPORT cmsPipelineInputChannels(cmsContext ContextID, const cmsPipeline* lut)
1402{
1403 cmsUNUSED_PARAMETER(ContextID);
1404 _cmsAssert(lut != NULL);
1405 return lut ->InputChannels;
1406}
1407
1408cmsUInt32Number CMSEXPORT cmsPipelineOutputChannels(cmsContext ContextID, const cmsPipeline* lut)
1409{
1410 cmsUNUSED_PARAMETER(ContextID);
1411 _cmsAssert(lut != NULL);
1412 return lut ->OutputChannels;
1413}
1414
1415// Free a profile elements LUT
1416void CMSEXPORT cmsPipelineFree(cmsContext ContextID, cmsPipeline* lut)
1417{
1418 cmsStage *mpe, *Next;
1419
1420 if (lut == NULL) return;
1421
1422 for (mpe = lut ->Elements;
1423 mpe != NULL;
1424 mpe = Next) {
1425
1426 Next = mpe ->Next;
1427 cmsStageFree(ContextID, mpe);
1428 }
1429
1430 if (lut ->FreeDataFn) lut ->FreeDataFn(ContextID, lut ->Data);
1431
1432 _cmsFree(ContextID, lut);
1433}
1434
1435
1436// Default to evaluate the LUT on 16 bit-basis.
1437void CMSEXPORT cmsPipelineEval16(cmsContext ContextID, const cmsUInt16Number In[], cmsUInt16Number Out[], const cmsPipeline* lut)
1438{
1439 _cmsAssert(lut != NULL);
1440 lut ->Eval16Fn(ContextID, In, Out, lut->Data);
1441}
1442
1443
1444// Does evaluate the LUT on cmsFloat32Number-basis.
1445void CMSEXPORT cmsPipelineEvalFloat(cmsContext ContextID, const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsPipeline* lut)
1446{
1447 _cmsAssert(lut != NULL);
1448 lut ->EvalFloatFn(ContextID, In, Out, lut);
1449}
1450
1451// Duplicates a LUT
1452cmsPipeline* CMSEXPORT cmsPipelineDup(cmsContext ContextID, const cmsPipeline* lut)
1453{
1454 cmsPipeline* NewLUT;
1455 cmsStage *NewMPE, *Anterior = NULL, *mpe;
1456 cmsBool First = TRUE;
1457
1458 if (lut == NULL) return NULL;
1459
1460 NewLUT = cmsPipelineAlloc(ContextID, lut ->InputChannels, lut ->OutputChannels);
1461 if (NewLUT == NULL) return NULL;
1462
1463 for (mpe = lut ->Elements;
1464 mpe != NULL;
1465 mpe = mpe ->Next) {
1466
1467 NewMPE = cmsStageDup(ContextID, mpe);
1468
1469 if (NewMPE == NULL) {
1470 cmsPipelineFree(ContextID, NewLUT);
1471 return NULL;
1472 }
1473
1474 if (First) {
1475 NewLUT ->Elements = NewMPE;
1476 First = FALSE;
1477 }
1478 else {
1479 if (Anterior != NULL)
1480 Anterior ->Next = NewMPE;
1481 }
1482
1483 Anterior = NewMPE;
1484 }
1485
1486 NewLUT ->Eval16Fn = lut ->Eval16Fn;
1487 NewLUT ->EvalFloatFn = lut ->EvalFloatFn;
1488 NewLUT ->DupDataFn = lut ->DupDataFn;
1489 NewLUT ->FreeDataFn = lut ->FreeDataFn;
1490
1491 if (NewLUT ->DupDataFn != NULL)
1492 NewLUT ->Data = NewLUT ->DupDataFn(ContextID, lut->Data);
1493
1494
1495 NewLUT ->SaveAs8Bits = lut ->SaveAs8Bits;
1496
1497 if (!BlessLUT(ContextID, NewLUT))
1498 {
1499 _cmsFree(ContextID, NewLUT);
1500 return NULL;
1501 }
1502
1503 return NewLUT;
1504}
1505
1506
1507int CMSEXPORT cmsPipelineInsertStage(cmsContext ContextID, cmsPipeline* lut, cmsStageLoc loc, cmsStage* mpe)
1508{
1509 cmsStage* Anterior = NULL, *pt;
1510
1511 if (lut == NULL || mpe == NULL)
1512 return FALSE;
1513
1514 switch (loc) {
1515
1516 case cmsAT_BEGIN:
1517 mpe ->Next = lut ->Elements;
1518 lut ->Elements = mpe;
1519 break;
1520
1521 case cmsAT_END:
1522
1523 if (lut ->Elements == NULL)
1524 lut ->Elements = mpe;
1525 else {
1526
1527 for (pt = lut ->Elements;
1528 pt != NULL;
1529 pt = pt -> Next) Anterior = pt;
1530
1531 Anterior ->Next = mpe;
1532 mpe ->Next = NULL;
1533 }
1534 break;
1535 default:;
1536 return FALSE;
1537 }
1538
1539 return BlessLUT(ContextID, lut);
1540}
1541
1542// Unlink an element and return the pointer to it
1543void CMSEXPORT cmsPipelineUnlinkStage(cmsContext ContextID, cmsPipeline* lut, cmsStageLoc loc, cmsStage** mpe)
1544{
1545 cmsStage *Anterior, *pt, *Last;
1546 cmsStage *Unlinked = NULL;
1547
1548
1549 // If empty LUT, there is nothing to remove
1550 if (lut ->Elements == NULL) {
1551 if (mpe) *mpe = NULL;
1552 return;
1553 }
1554
1555 // On depending on the strategy...
1556 switch (loc) {
1557
1558 case cmsAT_BEGIN:
1559 {
1560 cmsStage* elem = lut ->Elements;
1561
1562 lut ->Elements = elem -> Next;
1563 elem ->Next = NULL;
1564 Unlinked = elem;
1565
1566 }
1567 break;
1568
1569 case cmsAT_END:
1570 Anterior = Last = NULL;
1571 for (pt = lut ->Elements;
1572 pt != NULL;
1573 pt = pt -> Next) {
1574 Anterior = Last;
1575 Last = pt;
1576 }
1577
1578 Unlinked = Last; // Next already points to NULL
1579
1580 // Truncate the chain
1581 if (Anterior)
1582 Anterior ->Next = NULL;
1583 else
1584 lut ->Elements = NULL;
1585 break;
1586 default:;
1587 }
1588
1589 if (mpe)
1590 *mpe = Unlinked;
1591 else
1592 cmsStageFree(ContextID, Unlinked);
1593
1594 // May fail, but we ignore it
1595 BlessLUT(ContextID, lut);
1596}
1597
1598
1599// Concatenate two LUT into a new single one
1600cmsBool CMSEXPORT cmsPipelineCat(cmsContext ContextID, cmsPipeline* l1, const cmsPipeline* l2)
1601{
1602 cmsStage* mpe;
1603
1604 // If both LUTS does not have elements, we need to inherit
1605 // the number of channels
1606 if (l1 ->Elements == NULL && l2 ->Elements == NULL) {
1607 l1 ->InputChannels = l2 ->InputChannels;
1608 l1 ->OutputChannels = l2 ->OutputChannels;
1609 }
1610
1611 // Cat second
1612 for (mpe = l2 ->Elements;
1613 mpe != NULL;
1614 mpe = mpe ->Next) {
1615
1616 // We have to dup each element
1617 if (!cmsPipelineInsertStage(ContextID, l1, cmsAT_END, cmsStageDup(ContextID, mpe)))
1618 return FALSE;
1619 }
1620
1621 return BlessLUT(ContextID, l1);
1622}
1623
1624
1625cmsBool CMSEXPORT cmsPipelineSetSaveAs8bitsFlag(cmsContext ContextID, cmsPipeline* lut, cmsBool On)
1626{
1627 cmsBool Anterior = lut ->SaveAs8Bits;
1628 cmsUNUSED_PARAMETER(ContextID);
1629
1630 lut ->SaveAs8Bits = On;
1631 return Anterior;
1632}
1633
1634
1635cmsStage* CMSEXPORT cmsPipelineGetPtrToFirstStage(cmsContext ContextID, const cmsPipeline* lut)
1636{
1637 cmsUNUSED_PARAMETER(ContextID);
1638 return lut ->Elements;
1639}
1640
1641cmsStage* CMSEXPORT cmsPipelineGetPtrToLastStage(cmsContext ContextID, const cmsPipeline* lut)
1642{
1643 cmsStage *mpe, *Anterior = NULL;
1644 cmsUNUSED_PARAMETER(ContextID);
1645
1646 for (mpe = lut ->Elements; mpe != NULL; mpe = mpe ->Next)
1647 Anterior = mpe;
1648
1649 return Anterior;
1650}
1651
1652cmsUInt32Number CMSEXPORT cmsPipelineStageCount(cmsContext ContextID, const cmsPipeline* lut)
1653{
1654 cmsStage *mpe;
1655 cmsUInt32Number n;
1656 cmsUNUSED_PARAMETER(ContextID);
1657
1658 for (n=0, mpe = lut ->Elements; mpe != NULL; mpe = mpe ->Next)
1659 n++;
1660
1661 return n;
1662}
1663
1664// This function may be used to set the optional evaluator and a block of private data. If private data is being used, an optional
1665// duplicator and free functions should also be specified in order to duplicate the LUT construct. Use NULL to inhibit such functionality.
1666void CMSEXPORT _cmsPipelineSetOptimizationParameters(cmsContext ContextID, cmsPipeline* Lut,
1667 _cmsOPTeval16Fn Eval16,
1668 void* PrivateData,
1669 _cmsFreeUserDataFn FreePrivateDataFn,
1670 _cmsDupUserDataFn DupPrivateDataFn)
1671{
1672 cmsUNUSED_PARAMETER(ContextID);
1673
1674 Lut ->Eval16Fn = Eval16;
1675 Lut ->DupDataFn = DupPrivateDataFn;
1676 Lut ->FreeDataFn = FreePrivateDataFn;
1677 Lut ->Data = PrivateData;
1678}
1679
1680
1681// ----------------------------------------------------------- Reverse interpolation
1682// Here's how it goes. The derivative Df(x) of the function f is the linear
1683// transformation that best approximates f near the point x. It can be represented
1684// by a matrix A whose entries are the partial derivatives of the components of f
1685// with respect to all the coordinates. This is know as the Jacobian
1686//
1687// The best linear approximation to f is given by the matrix equation:
1688//
1689// y-y0 = A (x-x0)
1690//
1691// So, if x0 is a good "guess" for the zero of f, then solving for the zero of this
1692// linear approximation will give a "better guess" for the zero of f. Thus let y=0,
1693// and since y0=f(x0) one can solve the above equation for x. This leads to the
1694// Newton's method formula:
1695//
1696// xn+1 = xn - A-1 f(xn)
1697//
1698// where xn+1 denotes the (n+1)-st guess, obtained from the n-th guess xn in the
1699// fashion described above. Iterating this will give better and better approximations
1700// if you have a "good enough" initial guess.
1701
1702
1703#define JACOBIAN_EPSILON 0.001f
1704#define INVERSION_MAX_ITERATIONS 30
1705
1706// Increment with reflexion on boundary
1707static
1708void IncDelta(cmsFloat32Number *Val)
1709{
1710 if (*Val < (1.0 - JACOBIAN_EPSILON))
1711
1712 *Val += JACOBIAN_EPSILON;
1713
1714 else
1715 *Val -= JACOBIAN_EPSILON;
1716
1717}
1718
1719
1720
1721// Euclidean distance between two vectors of n elements each one
1722static
1723cmsFloat32Number EuclideanDistance(cmsFloat32Number a[], cmsFloat32Number b[], int n)
1724{
1725 cmsFloat32Number sum = 0;
1726 int i;
1727
1728 for (i=0; i < n; i++) {
1729 cmsFloat32Number dif = b[i] - a[i];
1730 sum += dif * dif;
1731 }
1732
1733 return sqrtf(sum);
1734}
1735
1736
1737// Evaluate a LUT in reverse direction. It only searches on 3->3 LUT. Uses Newton method
1738//
1739// x1 <- x - [J(x)]^-1 * f(x)
1740//
1741// lut: The LUT on where to do the search
1742// Target: LabK, 3 values of Lab plus destination K which is fixed
1743// Result: The obtained CMYK
1744// Hint: Location where begin the search
1745
1746cmsBool CMSEXPORT cmsPipelineEvalReverseFloat(cmsContext ContextID,
1747 cmsFloat32Number Target[],
1748 cmsFloat32Number Result[],
1749 cmsFloat32Number Hint[],
1750 const cmsPipeline* lut)
1751{
1752 cmsUInt32Number i, j;
1753 cmsFloat64Number error, LastError = 1E20;
1754 cmsFloat32Number fx[4], x[4], xd[4], fxd[4];
1755 cmsVEC3 tmp, tmp2;
1756 cmsMAT3 Jacobian;
1757
1758 // Only 3->3 and 4->3 are supported
1759 if (lut ->InputChannels != 3 && lut ->InputChannels != 4) return FALSE;
1760 if (lut ->OutputChannels != 3) return FALSE;
1761
1762 // Take the hint as starting point if specified
1763 if (Hint == NULL) {
1764
1765 // Begin at any point, we choose 1/3 of CMY axis
1766 x[0] = x[1] = x[2] = 0.3f;
1767 }
1768 else {
1769
1770 // Only copy 3 channels from hint...
1771 for (j=0; j < 3; j++)
1772 x[j] = Hint[j];
1773 }
1774
1775 // If Lut is 4-dimensions, then grab target[3], which is fixed
1776 if (lut ->InputChannels == 4) {
1777 x[3] = Target[3];
1778 }
1779 else x[3] = 0; // To keep lint happy
1780
1781
1782 // Iterate
1783 for (i = 0; i < INVERSION_MAX_ITERATIONS; i++) {
1784
1785 // Get beginning fx
1786 cmsPipelineEvalFloat(ContextID, x, fx, lut);
1787
1788 // Compute error
1789 error = EuclideanDistance(fx, Target, 3);
1790
1791 // If not convergent, return last safe value
1792 if (error >= LastError)
1793 break;
1794
1795 // Keep latest values
1796 LastError = error;
1797 for (j=0; j < lut ->InputChannels; j++)
1798 Result[j] = x[j];
1799
1800 // Found an exact match?
1801 if (error <= 0)
1802 break;
1803
1804 // Obtain slope (the Jacobian)
1805 for (j = 0; j < 3; j++) {
1806
1807 xd[0] = x[0];
1808 xd[1] = x[1];
1809 xd[2] = x[2];
1810 xd[3] = x[3]; // Keep fixed channel
1811
1812 IncDelta(&xd[j]);
1813
1814 cmsPipelineEvalFloat(ContextID, xd, fxd, lut);
1815
1816 Jacobian.v[0].n[j] = ((fxd[0] - fx[0]) / JACOBIAN_EPSILON);
1817 Jacobian.v[1].n[j] = ((fxd[1] - fx[1]) / JACOBIAN_EPSILON);
1818 Jacobian.v[2].n[j] = ((fxd[2] - fx[2]) / JACOBIAN_EPSILON);
1819 }
1820
1821 // Solve system
1822 tmp2.n[0] = fx[0] - Target[0];
1823 tmp2.n[1] = fx[1] - Target[1];
1824 tmp2.n[2] = fx[2] - Target[2];
1825
1826 if (!_cmsMAT3solve(ContextID, &tmp, &Jacobian, &tmp2))
1827 return FALSE;
1828
1829 // Move our guess
1830 x[0] -= (cmsFloat32Number) tmp.n[0];
1831 x[1] -= (cmsFloat32Number) tmp.n[1];
1832 x[2] -= (cmsFloat32Number) tmp.n[2];
1833
1834 // Some clipping....
1835 for (j=0; j < 3; j++) {
1836 if (x[j] < 0) x[j] = 0;
1837 else
1838 if (x[j] > 1.0) x[j] = 1.0;
1839 }
1840 }
1841
1842 return TRUE;
1843}
1844
1845
1846