Coverage Report

Created: 2025-06-10 07:27

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