Coverage Report

Created: 2026-05-16 07:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/Little-CMS/src/cmsxform.c
Line
Count
Source
1
//---------------------------------------------------------------------------------
2
//
3
//  Little Color Management System
4
//  Copyright (c) 1998-2026 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
// Transformations stuff
30
// -----------------------------------------------------------------------
31
32
0
#define DEFAULT_OBSERVER_ADAPTATION_STATE 1.0
33
34
// The Context0 observer adaptation state.
35
_cmsAdaptationStateChunkType _cmsAdaptationStateChunk = { DEFAULT_OBSERVER_ADAPTATION_STATE };
36
37
// Init and duplicate observer adaptation state
38
void _cmsAllocAdaptationStateChunk(struct _cmsContext_struct* ctx, 
39
                                   const struct _cmsContext_struct* src)
40
0
{
41
0
    static _cmsAdaptationStateChunkType AdaptationStateChunk = { DEFAULT_OBSERVER_ADAPTATION_STATE };
42
0
    void* from;
43
     
44
0
    if (src != NULL) {
45
0
        from = src ->chunks[AdaptationStateContext];       
46
0
    }
47
0
    else {
48
0
       from = &AdaptationStateChunk;
49
0
    }
50
    
51
0
    ctx ->chunks[AdaptationStateContext] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsAdaptationStateChunkType));     
52
0
}
53
54
55
// Sets adaptation state for absolute colorimetric intent in the given context.  Adaptation state applies on all 
56
// but cmsCreateExtendedTransformTHR().  Little CMS can handle incomplete adaptation states.
57
cmsFloat64Number CMSEXPORT cmsSetAdaptationStateTHR(cmsContext ContextID, cmsFloat64Number d)
58
533k
{
59
533k
    cmsFloat64Number prev;
60
533k
    _cmsAdaptationStateChunkType* ptr = (_cmsAdaptationStateChunkType*) _cmsContextGetClientChunk(ContextID, AdaptationStateContext);
61
62
    // Get previous value for return
63
533k
    prev = ptr ->AdaptationState;
64
65
    // Set the value if d is positive or zero
66
533k
    if (d >= 0.0) {
67
68
0
        ptr ->AdaptationState = d;
69
0
    }
70
71
    // Always return previous value
72
533k
    return prev;
73
533k
}
74
75
76
// The adaptation state may be defaulted by this function. If you don't like it, use the extended transform routine
77
cmsFloat64Number CMSEXPORT cmsSetAdaptationState(cmsFloat64Number d)
78
0
{    
79
0
    return cmsSetAdaptationStateTHR(NULL, d);
80
0
}
81
82
// -----------------------------------------------------------------------
83
84
// Alarm codes for 16-bit transformations, because the fixed range of containers there are
85
// no values left to mark out of gamut. 
86
87
0
#define DEFAULT_ALARM_CODES_VALUE {0x7F00, 0x7F00, 0x7F00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
88
89
_cmsAlarmCodesChunkType _cmsAlarmCodesChunk = { DEFAULT_ALARM_CODES_VALUE };
90
91
// Sets the codes used to mark out-out-gamut on Proofing transforms for a given context. Values are meant to be 
92
// encoded in 16 bits.
93
void CMSEXPORT cmsSetAlarmCodesTHR(cmsContext ContextID, const cmsUInt16Number AlarmCodesP[cmsMAXCHANNELS])
94
0
{
95
0
    _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(ContextID, AlarmCodesContext);
96
       
97
0
    _cmsAssert(ContextAlarmCodes != NULL); // Can't happen
98
    
99
0
    memcpy(ContextAlarmCodes->AlarmCodes, AlarmCodesP, sizeof(ContextAlarmCodes->AlarmCodes));    
100
0
}
101
102
// Gets the current codes used to mark out-out-gamut on Proofing transforms for the given context.
103
// Values are meant to be encoded in 16 bits.
104
void CMSEXPORT cmsGetAlarmCodesTHR(cmsContext ContextID, cmsUInt16Number AlarmCodesP[cmsMAXCHANNELS])
105
0
{
106
0
    _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(ContextID, AlarmCodesContext);
107
108
0
    _cmsAssert(ContextAlarmCodes != NULL); // Can't happen
109
110
0
    memcpy(AlarmCodesP, ContextAlarmCodes->AlarmCodes, sizeof(ContextAlarmCodes->AlarmCodes));
111
0
}
112
113
void CMSEXPORT cmsSetAlarmCodes(const cmsUInt16Number NewAlarm[cmsMAXCHANNELS])
114
0
{
115
0
    _cmsAssert(NewAlarm != NULL);
116
117
0
    cmsSetAlarmCodesTHR(NULL, NewAlarm);
118
0
}
119
120
void CMSEXPORT cmsGetAlarmCodes(cmsUInt16Number OldAlarm[cmsMAXCHANNELS])
121
0
{ 
122
0
    _cmsAssert(OldAlarm != NULL);
123
0
    cmsGetAlarmCodesTHR(NULL, OldAlarm);
124
0
}
125
126
127
// Init and duplicate alarm codes
128
void _cmsAllocAlarmCodesChunk(struct _cmsContext_struct* ctx, 
129
                              const struct _cmsContext_struct* src)
130
0
{
131
0
    static _cmsAlarmCodesChunkType AlarmCodesChunk = { DEFAULT_ALARM_CODES_VALUE };
132
0
    void* from;
133
     
134
0
    if (src != NULL) {
135
0
        from = src ->chunks[AlarmCodesContext];       
136
0
    }
137
0
    else {
138
0
       from = &AlarmCodesChunk;
139
0
    }
140
    
141
0
    ctx ->chunks[AlarmCodesContext] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsAlarmCodesChunkType));     
142
0
}
143
144
// -----------------------------------------------------------------------
145
146
// Get rid of transform resources
147
void CMSEXPORT cmsDeleteTransform(cmsHTRANSFORM hTransform)
148
185k
{
149
185k
    _cmsTRANSFORM* p = (_cmsTRANSFORM*) hTransform;
150
151
185k
    _cmsAssert(p != NULL);
152
153
185k
    if (p -> GamutCheck)
154
0
        cmsPipelineFree(p -> GamutCheck);
155
156
185k
    if (p -> Lut)
157
185k
        cmsPipelineFree(p -> Lut);
158
159
185k
    if (p ->InputColorant)
160
0
        cmsFreeNamedColorList(p ->InputColorant);
161
162
185k
    if (p -> OutputColorant)
163
0
        cmsFreeNamedColorList(p ->OutputColorant);
164
165
185k
    if (p ->Sequence)
166
0
        cmsFreeProfileSequenceDescription(p ->Sequence);
167
168
185k
    if (p ->UserData)
169
0
        p ->FreeUserData(p ->ContextID, p ->UserData);
170
171
185k
    _cmsFree(p ->ContextID, (void *) p);
172
185k
}
173
174
175
static
176
cmsUInt32Number PixelSize(cmsUInt32Number Format)
177
4.54M
{
178
4.54M
    cmsUInt32Number fmt_bytes = T_BYTES(Format);
179
180
    // For double, the T_BYTES field is zero
181
4.54M
    if (fmt_bytes == 0)
182
123k
        return sizeof(cmsUInt64Number);
183
184
    // Otherwise, it is already correct for all formats
185
4.42M
    return fmt_bytes;
186
4.54M
}
187
188
189
190
191
// Apply transform.
192
void CMSEXPORT cmsDoTransform(cmsHTRANSFORM  Transform,
193
                              const void* InputBuffer,
194
                              void* OutputBuffer,
195
                              cmsUInt32Number Size)
196
197
2.27M
{
198
2.27M
    _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
199
2.27M
    cmsStride stride;
200
201
2.27M
    stride.BytesPerLineIn = 0;  // Not used
202
2.27M
    stride.BytesPerLineOut = 0;
203
2.27M
    stride.BytesPerPlaneIn = Size * PixelSize(p->InputFormat);
204
2.27M
    stride.BytesPerPlaneOut = Size * PixelSize(p->OutputFormat);
205
           
206
2.27M
    p -> xform(p, InputBuffer, OutputBuffer, Size, 1, &stride);
207
2.27M
}
208
209
210
// This is a legacy stride for planar
211
void CMSEXPORT cmsDoTransformStride(cmsHTRANSFORM  Transform,
212
                              const void* InputBuffer,
213
                              void* OutputBuffer,
214
                              cmsUInt32Number Size, cmsUInt32Number Stride)
215
216
0
{
217
0
    _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
218
0
    cmsStride stride;
219
220
0
    stride.BytesPerLineIn = 0;  
221
0
    stride.BytesPerLineOut = 0;
222
0
    stride.BytesPerPlaneIn = Stride;
223
0
    stride.BytesPerPlaneOut = Stride;
224
225
0
    p -> xform(p, InputBuffer, OutputBuffer, Size, 1, &stride);
226
0
}
227
228
// This is the "fast" function for plugins
229
void CMSEXPORT cmsDoTransformLineStride(cmsHTRANSFORM  Transform,
230
                              const void* InputBuffer,
231
                              void* OutputBuffer,
232
                              cmsUInt32Number PixelsPerLine,
233
                              cmsUInt32Number LineCount,
234
                              cmsUInt32Number BytesPerLineIn,
235
                              cmsUInt32Number BytesPerLineOut,
236
                              cmsUInt32Number BytesPerPlaneIn,
237
                              cmsUInt32Number BytesPerPlaneOut)
238
239
0
{
240
0
    _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
241
0
    cmsStride stride;
242
243
0
    stride.BytesPerLineIn = BytesPerLineIn;
244
0
    stride.BytesPerLineOut = BytesPerLineOut;
245
0
    stride.BytesPerPlaneIn = BytesPerPlaneIn;
246
0
    stride.BytesPerPlaneOut = BytesPerPlaneOut;
247
248
0
    p->xform(p, InputBuffer, OutputBuffer, PixelsPerLine, LineCount, &stride);
249
0
}
250
251
252
253
// Transform routines ----------------------------------------------------------------------------------------------------------
254
255
// Float xform converts floats. Since there are no performance issues, one routine does all job, including gamut check.
256
// Note that because extended range, we can use a -1.0 value for out of gamut in this case.
257
static
258
void FloatXFORM(_cmsTRANSFORM* p,
259
                const void* in,
260
                void* out, 
261
                cmsUInt32Number PixelsPerLine,
262
                cmsUInt32Number LineCount,
263
                const cmsStride* Stride)
264
123k
{
265
123k
    cmsUInt8Number* accum;
266
123k
    cmsUInt8Number* output;
267
123k
    cmsFloat32Number fIn[cmsMAXCHANNELS], fOut[cmsMAXCHANNELS];
268
123k
    cmsFloat32Number OutOfGamut;
269
123k
    size_t i, j, c, strideIn, strideOut;
270
271
123k
    _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
272
273
123k
    strideIn = 0;
274
123k
    strideOut = 0;
275
123k
    memset(fIn, 0, sizeof(fIn));
276
123k
    memset(fOut, 0, sizeof(fOut));
277
278
247k
    for (i = 0; i < LineCount; i++) {
279
280
123k
        accum = (cmsUInt8Number*)in + strideIn;
281
123k
        output = (cmsUInt8Number*)out + strideOut;
282
283
247k
        for (j = 0; j < PixelsPerLine; j++) {
284
285
123k
            accum = p->FromInputFloat(p, fIn, accum, Stride->BytesPerPlaneIn);
286
287
            // Any gamut check to do?
288
123k
            if (p->GamutCheck != NULL) {
289
290
                // Evaluate gamut marker.
291
0
                cmsPipelineEvalFloat(fIn, &OutOfGamut, p->GamutCheck);
292
293
                // Is current color out of gamut?
294
0
                if (OutOfGamut > 0.0) {
295
296
0
                    _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*)_cmsContextGetClientChunk(p->ContextID, AlarmCodesContext);
297
298
                    // Certainly, out of gamut
299
0
                    for (c = 0; c < cmsMAXCHANNELS; c++)
300
0
                        fOut[c] = ContextAlarmCodes->AlarmCodes[c] / 65535.0F;
301
302
0
                }
303
0
                else {
304
                    // No, proceed normally
305
0
                    cmsPipelineEvalFloat(fIn, fOut, p->Lut);
306
0
                }
307
0
            }
308
123k
            else {
309
310
                // No gamut check at all
311
123k
                cmsPipelineEvalFloat(fIn, fOut, p->Lut);
312
123k
            }
313
314
315
123k
            output = p->ToOutputFloat(p, fOut, output, Stride->BytesPerPlaneOut);
316
123k
        }
317
318
123k
        strideIn += Stride->BytesPerLineIn;
319
123k
        strideOut += Stride->BytesPerLineOut;
320
123k
    }
321
322
123k
}
323
324
325
static
326
void NullFloatXFORM(_cmsTRANSFORM* p,
327
                    const void* in,
328
                    void* out, 
329
                    cmsUInt32Number PixelsPerLine,
330
                    cmsUInt32Number LineCount,
331
                    const cmsStride* Stride)
332
333
0
{
334
0
    cmsUInt8Number* accum;
335
0
    cmsUInt8Number* output;
336
0
    cmsFloat32Number fIn[cmsMAXCHANNELS];
337
0
    size_t i, j, strideIn, strideOut;
338
339
0
    _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
340
341
0
    strideIn = 0;
342
0
    strideOut = 0;
343
0
    memset(fIn, 0, sizeof(fIn));
344
345
0
    for (i = 0; i < LineCount; i++) {
346
347
0
           accum = (cmsUInt8Number*) in + strideIn;
348
0
           output = (cmsUInt8Number*) out + strideOut;
349
350
0
           for (j = 0; j < PixelsPerLine; j++) {
351
352
0
                  accum = p->FromInputFloat(p, fIn, accum, Stride ->BytesPerPlaneIn);
353
0
                  output = p->ToOutputFloat(p, fIn, output, Stride->BytesPerPlaneOut);
354
0
           }
355
356
0
           strideIn += Stride->BytesPerLineIn;
357
0
           strideOut += Stride->BytesPerLineOut;
358
0
    }
359
0
}
360
361
// 16 bit precision -----------------------------------------------------------------------------------------------------------
362
363
// Null transformation, only applies formatters. No cache
364
static
365
void NullXFORM(_cmsTRANSFORM* p,
366
               const void* in,
367
               void* out,
368
               cmsUInt32Number PixelsPerLine,
369
               cmsUInt32Number LineCount,
370
               const cmsStride* Stride)
371
0
{
372
0
    cmsUInt8Number* accum;
373
0
    cmsUInt8Number* output;
374
0
    cmsUInt16Number wIn[cmsMAXCHANNELS];
375
0
    size_t i, j, strideIn, strideOut;
376
377
0
    _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
378
379
0
    strideIn = 0;
380
0
    strideOut = 0;
381
0
    memset(wIn, 0, sizeof(wIn));
382
383
0
    for (i = 0; i < LineCount; i++) {
384
385
0
        accum = (cmsUInt8Number*)in + strideIn;
386
0
        output = (cmsUInt8Number*)out + strideOut;
387
388
0
        for (j = 0; j < PixelsPerLine; j++) {
389
390
0
            accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn);
391
0
            output = p->ToOutput(p, wIn, output, Stride->BytesPerPlaneOut);
392
0
        }
393
394
0
        strideIn += Stride->BytesPerLineIn;
395
0
        strideOut += Stride->BytesPerLineOut;
396
0
    }
397
398
0
}
399
400
401
// No gamut check, no cache, 16 bits
402
static
403
void PrecalculatedXFORM(_cmsTRANSFORM* p,
404
                        const void* in,
405
                        void* out, 
406
                        cmsUInt32Number PixelsPerLine,
407
                        cmsUInt32Number LineCount,
408
                        const cmsStride* Stride)
409
0
{
410
0
    CMSREGISTER cmsUInt8Number* accum;
411
0
    CMSREGISTER cmsUInt8Number* output;
412
0
    cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
413
0
    size_t i, j, strideIn, strideOut;
414
415
0
    _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
416
417
0
    strideIn = 0;
418
0
    strideOut = 0;
419
0
    memset(wIn, 0, sizeof(wIn));
420
0
    memset(wOut, 0, sizeof(wOut));
421
422
0
    for (i = 0; i < LineCount; i++) {
423
424
0
        accum = (cmsUInt8Number*)in + strideIn;
425
0
        output = (cmsUInt8Number*)out + strideOut;
426
427
0
        for (j = 0; j < PixelsPerLine; j++) {
428
429
0
            accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn);
430
0
            p->Lut->Eval16Fn(wIn, wOut, p->Lut->Data);
431
0
            output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut);
432
0
        }
433
434
0
        strideIn += Stride->BytesPerLineIn;
435
0
        strideOut += Stride->BytesPerLineOut;
436
0
    }
437
438
0
}
439
440
441
// Auxiliary: Handle precalculated gamut check. The retrieval of context may be alittle bit slow, but this function is not critical.
442
static
443
void TransformOnePixelWithGamutCheck(_cmsTRANSFORM* p,
444
                                     const cmsUInt16Number wIn[],
445
                                     cmsUInt16Number wOut[])
446
0
{
447
0
    cmsUInt16Number wOutOfGamut;
448
449
0
    p ->GamutCheck ->Eval16Fn(wIn, &wOutOfGamut, p ->GamutCheck ->Data);
450
0
    if (wOutOfGamut >= 1) {
451
452
0
        cmsUInt32Number i;
453
0
        _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(p->ContextID, AlarmCodesContext);        
454
455
0
        for (i=0; i < p ->Lut->OutputChannels; i++) {
456
457
0
            wOut[i] = ContextAlarmCodes ->AlarmCodes[i];
458
0
        }
459
0
    }
460
0
    else
461
0
        p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);
462
0
}
463
464
// Gamut check, No cache, 16 bits.
465
static
466
void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p,
467
                                  const void* in,
468
                                  void* out, 
469
                                  cmsUInt32Number PixelsPerLine,
470
                                  cmsUInt32Number LineCount,
471
                                  const cmsStride* Stride)
472
0
{
473
0
    cmsUInt8Number* accum;
474
0
    cmsUInt8Number* output;
475
0
    cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
476
0
    size_t i, j, strideIn, strideOut;
477
478
0
    _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
479
480
0
    strideIn = 0;
481
0
    strideOut = 0;
482
0
    memset(wIn, 0, sizeof(wIn));
483
0
    memset(wOut, 0, sizeof(wOut));
484
485
0
    for (i = 0; i < LineCount; i++) {
486
487
0
        accum = (cmsUInt8Number*)in + strideIn;
488
0
        output = (cmsUInt8Number*)out + strideOut;
489
490
0
        for (j = 0; j < PixelsPerLine; j++) {
491
492
0
            accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn);
493
0
            TransformOnePixelWithGamutCheck(p, wIn, wOut);
494
0
            output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut);
495
0
        }
496
497
0
        strideIn += Stride->BytesPerLineIn;
498
0
        strideOut += Stride->BytesPerLineOut;
499
0
    }
500
0
}
501
502
// No gamut check, Cache, 16 bits,
503
static
504
void CachedXFORM(_cmsTRANSFORM* p,
505
                 const void* in,
506
                 void* out,
507
                 cmsUInt32Number PixelsPerLine,
508
                 cmsUInt32Number LineCount,
509
                 const cmsStride* Stride)
510
2.14M
{
511
2.14M
    cmsUInt8Number* accum;
512
2.14M
    cmsUInt8Number* output;
513
2.14M
    cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
514
2.14M
    _cmsCACHE Cache;
515
2.14M
    size_t i, j, strideIn, strideOut;
516
517
2.14M
    _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
518
519
    // Empty buffers for quick memcmp
520
2.14M
    memset(wIn, 0, sizeof(wIn));
521
2.14M
    memset(wOut, 0, sizeof(wOut));
522
523
    // Get copy of zero cache
524
2.14M
    memcpy(&Cache, &p->Cache, sizeof(Cache));
525
526
2.14M
    strideIn = 0;
527
2.14M
    strideOut = 0;
528
529
4.29M
    for (i = 0; i < LineCount; i++) {
530
531
2.14M
        accum = (cmsUInt8Number*)in + strideIn;
532
2.14M
        output = (cmsUInt8Number*)out + strideOut;
533
534
209M
        for (j = 0; j < PixelsPerLine; j++) {
535
536
206M
            accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn);
537
538
206M
            if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) {
539
540
195M
                memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut));
541
195M
            }
542
11.4M
            else {
543
11.4M
                p->Lut->Eval16Fn(wIn, wOut, p->Lut->Data);
544
545
11.4M
                memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn));
546
11.4M
                memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut));
547
11.4M
            }
548
549
206M
            output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut);
550
206M
        }
551
552
2.14M
        strideIn += Stride->BytesPerLineIn;
553
2.14M
        strideOut += Stride->BytesPerLineOut;
554
2.14M
    }
555
2.14M
}
556
557
// All those nice features together
558
static
559
void CachedXFORMGamutCheck(_cmsTRANSFORM* p,
560
                           const void* in,
561
                           void* out, 
562
                           cmsUInt32Number PixelsPerLine,
563
                           cmsUInt32Number LineCount,
564
                           const cmsStride* Stride)
565
0
{
566
0
    cmsUInt8Number* accum;
567
0
    cmsUInt8Number* output;
568
0
    cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
569
0
    _cmsCACHE Cache;
570
0
    size_t i, j, strideIn, strideOut;
571
572
0
    _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
573
574
    // Empty buffers for quick memcmp
575
0
    memset(wIn, 0, sizeof(wIn));
576
0
    memset(wOut, 0, sizeof(wOut));
577
578
    // Get copy of zero cache
579
0
    memcpy(&Cache, &p->Cache, sizeof(Cache));
580
581
0
    strideIn = 0;
582
0
    strideOut = 0;
583
584
0
    for (i = 0; i < LineCount; i++) {
585
586
0
        accum = (cmsUInt8Number*)in + strideIn;
587
0
        output = (cmsUInt8Number*)out + strideOut;
588
589
0
        for (j = 0; j < PixelsPerLine; j++) {
590
591
0
            accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn);
592
593
0
            if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) {
594
595
0
                memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut));
596
0
            }
597
0
            else {
598
0
                TransformOnePixelWithGamutCheck(p, wIn, wOut);
599
600
0
                memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn));
601
0
                memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut));
602
0
            }
603
604
0
            output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut);
605
0
        }
606
607
0
        strideIn += Stride->BytesPerLineIn;
608
0
        strideOut += Stride->BytesPerLineOut;
609
0
    }
610
0
}
611
612
// Transform plug-ins ----------------------------------------------------------------------------------------------------
613
614
// List of used-defined transform factories
615
typedef struct _cmsTransformCollection_st {
616
617
    _cmsTransform2Factory  Factory;
618
    cmsBool                OldXform;   // Factory returns xform function in the old style
619
620
    struct _cmsTransformCollection_st *Next;
621
622
} _cmsTransformCollection;
623
624
// The linked list head
625
_cmsTransformPluginChunkType _cmsTransformPluginChunk = { NULL };
626
627
628
// Duplicates the zone of memory used by the plug-in in the new context
629
static
630
void DupPluginTransformList(struct _cmsContext_struct* ctx, 
631
                                               const struct _cmsContext_struct* src)
632
0
{
633
0
   _cmsTransformPluginChunkType newHead = { NULL };
634
0
   _cmsTransformCollection*  entry;
635
0
   _cmsTransformCollection*  Anterior = NULL;
636
0
   _cmsTransformPluginChunkType* head = (_cmsTransformPluginChunkType*) src->chunks[TransformPlugin];
637
638
    // Walk the list copying all nodes
639
0
   for (entry = head->TransformCollection;
640
0
        entry != NULL;
641
0
        entry = entry ->Next) {
642
643
0
            _cmsTransformCollection *newEntry = ( _cmsTransformCollection *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTransformCollection));
644
   
645
0
            if (newEntry == NULL) 
646
0
                return;
647
648
            // We want to keep the linked list order, so this is a little bit tricky
649
0
            newEntry -> Next = NULL;
650
0
            if (Anterior)
651
0
                Anterior -> Next = newEntry;
652
     
653
0
            Anterior = newEntry;
654
655
0
            if (newHead.TransformCollection == NULL)
656
0
                newHead.TransformCollection = newEntry;
657
0
    }
658
659
0
  ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsTransformPluginChunkType));
660
0
}
661
662
// Allocates memory for transform plugin factory
663
void _cmsAllocTransformPluginChunk(struct _cmsContext_struct* ctx, 
664
                                        const struct _cmsContext_struct* src)
665
0
{
666
0
    if (src != NULL) {
667
668
        // Copy all linked list
669
0
        DupPluginTransformList(ctx, src);
670
0
    }
671
0
    else {
672
0
        static _cmsTransformPluginChunkType TransformPluginChunkType = { NULL };
673
0
        ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx ->MemPool, &TransformPluginChunkType, sizeof(_cmsTransformPluginChunkType));
674
0
    }
675
0
}
676
677
// Adaptor for old versions of plug-in
678
static
679
void _cmsTransform2toTransformAdaptor(struct _cmstransform_struct *CMMcargo,
680
                                      const void* InputBuffer,
681
                                      void* OutputBuffer,
682
                                      cmsUInt32Number PixelsPerLine,
683
                                      cmsUInt32Number LineCount,
684
                                      const cmsStride* Stride)
685
0
{
686
     
687
0
       size_t i, strideIn, strideOut;
688
689
0
       _cmsHandleExtraChannels(CMMcargo, InputBuffer, OutputBuffer, PixelsPerLine, LineCount, Stride);
690
691
0
       strideIn = 0;
692
0
       strideOut = 0;
693
694
0
       for (i = 0; i < LineCount; i++) {
695
696
0
              void *accum = (cmsUInt8Number*)InputBuffer + strideIn;
697
0
              void *output = (cmsUInt8Number*)OutputBuffer + strideOut;
698
699
0
              CMMcargo->OldXform(CMMcargo, accum, output, PixelsPerLine, Stride->BytesPerPlaneIn);
700
701
0
              strideIn += Stride->BytesPerLineIn;
702
0
              strideOut += Stride->BytesPerLineOut;
703
0
       }
704
0
}
705
706
707
708
// Register new ways to transform
709
cmsBool  _cmsRegisterTransformPlugin(cmsContext ContextID, cmsPluginBase* Data)
710
0
{
711
0
    cmsPluginTransform* Plugin = (cmsPluginTransform*) Data;
712
0
    _cmsTransformCollection* fl;
713
0
    _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID,TransformPlugin);
714
715
0
    if (Data == NULL) {
716
717
        // Free the chain. Memory is safely freed at exit
718
0
        ctx->TransformCollection = NULL;
719
0
        return TRUE;
720
0
    }
721
722
    // Factory callback is required
723
0
    if (Plugin->factories.xform == NULL) return FALSE;
724
725
726
0
    fl = (_cmsTransformCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsTransformCollection));
727
0
    if (fl == NULL) return FALSE;
728
729
    // Check for full xform plug-ins previous to 2.8, we would need an adapter in that case
730
0
    if (Plugin->base.ExpectedVersion < 2080) {
731
732
0
           fl->OldXform = TRUE;
733
0
    }
734
0
    else
735
0
           fl->OldXform = FALSE;
736
737
    // Copy the parameters
738
0
    fl->Factory = Plugin->factories.xform;
739
740
    // Keep linked list
741
0
    fl ->Next = ctx->TransformCollection;
742
0
    ctx->TransformCollection = fl;
743
744
    // All is ok
745
0
    return TRUE;
746
0
}
747
748
749
void CMSEXPORT _cmsSetTransformUserData(struct _cmstransform_struct *CMMcargo, void* ptr, _cmsFreeUserDataFn FreePrivateDataFn)
750
0
{
751
0
    _cmsAssert(CMMcargo != NULL);
752
0
    CMMcargo ->UserData = ptr;
753
0
    CMMcargo ->FreeUserData = FreePrivateDataFn;
754
0
}
755
756
// returns the pointer defined by the plug-in to store private data
757
void * CMSEXPORT _cmsGetTransformUserData(struct _cmstransform_struct *CMMcargo)
758
0
{
759
0
    _cmsAssert(CMMcargo != NULL);
760
0
    return CMMcargo ->UserData;
761
0
}
762
763
// returns the current formatters
764
void CMSEXPORT _cmsGetTransformFormatters16(struct _cmstransform_struct *CMMcargo, cmsFormatter16* FromInput, cmsFormatter16* ToOutput)
765
0
{
766
0
     _cmsAssert(CMMcargo != NULL);
767
0
     if (FromInput) *FromInput = CMMcargo ->FromInput;
768
0
     if (ToOutput)  *ToOutput  = CMMcargo ->ToOutput;
769
0
}
770
771
void CMSEXPORT _cmsGetTransformFormattersFloat(struct _cmstransform_struct *CMMcargo, cmsFormatterFloat* FromInput, cmsFormatterFloat* ToOutput)
772
0
{
773
0
     _cmsAssert(CMMcargo != NULL);
774
0
     if (FromInput) *FromInput = CMMcargo ->FromInputFloat;
775
0
     if (ToOutput)  *ToOutput  = CMMcargo ->ToOutputFloat;
776
0
}
777
778
// returns original flags
779
cmsUInt32Number CMSEXPORT _cmsGetTransformFlags(struct _cmstransform_struct* CMMcargo)
780
0
{
781
0
    _cmsAssert(CMMcargo != NULL);
782
0
    return CMMcargo->dwOriginalFlags;
783
0
}
784
785
// Returns the worker callback for parallelization plug-ins
786
_cmsTransform2Fn CMSEXPORT _cmsGetTransformWorker(struct _cmstransform_struct* CMMcargo)
787
0
{
788
0
    _cmsAssert(CMMcargo != NULL);
789
0
    return CMMcargo->Worker;
790
0
}
791
792
// This field holds maximum number of workers or -1 to auto 
793
cmsInt32Number CMSEXPORT _cmsGetTransformMaxWorkers(struct _cmstransform_struct* CMMcargo)
794
0
{
795
0
    _cmsAssert(CMMcargo != NULL);
796
0
    return CMMcargo->MaxWorkers;
797
0
}
798
799
// This field is actually unused and reserved
800
cmsUInt32Number CMSEXPORT _cmsGetTransformWorkerFlags(struct _cmstransform_struct* CMMcargo)
801
0
{
802
0
    _cmsAssert(CMMcargo != NULL);
803
0
    return CMMcargo->WorkerFlags;
804
0
}
805
806
// In the case there is a parallelization plug-in, let it to do its job
807
static
808
void ParalellizeIfSuitable(_cmsTRANSFORM* p)
809
185k
{
810
185k
    _cmsParallelizationPluginChunkType* ctx = (_cmsParallelizationPluginChunkType*)_cmsContextGetClientChunk(p->ContextID, ParallelizationPlugin);
811
812
185k
    _cmsAssert(p != NULL);
813
185k
    if (ctx != NULL && ctx->SchedulerFn != NULL) {
814
815
0
        p->Worker = p->xform;
816
0
        p->xform = ctx->SchedulerFn;
817
0
        p->MaxWorkers = ctx->MaxWorkers;
818
0
        p->WorkerFlags = ctx->WorkerFlags;
819
0
    }
820
185k
}
821
822
823
/**
824
* An empty unroll to avoid a check with NULL on cmsDoTransform()
825
*/
826
static
827
cmsUInt8Number* UnrollNothing(CMSREGISTER _cmsTRANSFORM* info,
828
                              CMSREGISTER cmsUInt16Number wIn[],
829
                              CMSREGISTER cmsUInt8Number* accum,
830
                              CMSREGISTER cmsUInt32Number Stride)
831
0
{    
832
0
    return accum;
833
834
0
    cmsUNUSED_PARAMETER(info);
835
0
    cmsUNUSED_PARAMETER(wIn);
836
0
    cmsUNUSED_PARAMETER(Stride);
837
0
}
838
839
static
840
cmsUInt8Number* PackNothing(CMSREGISTER _cmsTRANSFORM* info,
841
                           CMSREGISTER cmsUInt16Number wOut[],
842
                           CMSREGISTER cmsUInt8Number* output,
843
                           CMSREGISTER cmsUInt32Number Stride)
844
0
{
845
0
    return output;
846
847
0
    cmsUNUSED_PARAMETER(info);
848
0
    cmsUNUSED_PARAMETER(wOut);
849
0
    cmsUNUSED_PARAMETER(Stride);
850
0
}
851
852
// Allocate transform struct and set it to defaults. Ask the optimization plug-in about if those formats are proper
853
// for separated transforms. If this is the case,
854
static
855
_cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut,
856
                                               cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags)
857
185k
{
858
185k
     _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID, TransformPlugin);
859
185k
     _cmsTransformCollection* Plugin;
860
861
       // Allocate needed memory
862
185k
       _cmsTRANSFORM* p = (_cmsTRANSFORM*)_cmsMallocZero(ContextID, sizeof(_cmsTRANSFORM));
863
185k
       if (!p) {
864
0
              cmsPipelineFree(lut);
865
0
              return NULL;
866
0
       }
867
868
       // Store the proposed pipeline
869
185k
       p->Lut = lut;
870
871
       // Let's see if any plug-in want to do the transform by itself
872
185k
       if (p->Lut != NULL) {
873
874
185k
           if (!(*dwFlags & cmsFLAGS_NOOPTIMIZE))
875
4
           {
876
4
               for (Plugin = ctx->TransformCollection;
877
4
                   Plugin != NULL;
878
4
                   Plugin = Plugin->Next) {
879
880
0
                   if (Plugin->Factory(&p->xform, &p->UserData, &p->FreeUserData, &p->Lut, InputFormat, OutputFormat, dwFlags)) {
881
882
                       // Last plugin in the declaration order takes control. We just keep
883
                       // the original parameters as a logging. 
884
                       // Note that cmsFLAGS_CAN_CHANGE_FORMATTER is not set, so by default 
885
                       // an optimized transform is not reusable. The plug-in can, however, change
886
                       // the flags and make it suitable.
887
888
0
                       p->ContextID = ContextID;
889
0
                       p->InputFormat = *InputFormat;
890
0
                       p->OutputFormat = *OutputFormat;
891
0
                       p->dwOriginalFlags = *dwFlags;
892
893
                       // Fill the formatters just in case the optimized routine is interested.
894
                       // No error is thrown if the formatter doesn't exist. It is up to the optimization 
895
                       // factory to decide what to do in those cases.
896
0
                       p->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
897
0
                       p->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
898
0
                       p->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
899
0
                       p->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
900
901
                       // Save the day? (Ignore the warning)
902
0
                       if (Plugin->OldXform) {
903
0
                           p->OldXform = (_cmsTransformFn)(void*)p->xform;
904
0
                           p->xform = _cmsTransform2toTransformAdaptor;
905
0
                       }
906
907
0
                       ParalellizeIfSuitable(p);
908
0
                       return p;
909
0
                   }
910
0
               }
911
4
           }
912
913
           // Not suitable for the transform plug-in, let's check  the pipeline plug-in
914
185k
           _cmsOptimizePipeline(ContextID, &p->Lut, Intent, InputFormat, OutputFormat, dwFlags);
915
185k
       }
916
917
    // Check whatever this is a true floating point transform
918
185k
    if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) {
919
920
        // Get formatter function always return a valid union, but the contents of this union may be NULL.
921
126k
        p ->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
922
126k
        p ->ToOutputFloat  = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
923
126k
        *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
924
925
126k
        if (p ->FromInputFloat == NULL || p ->ToOutputFloat == NULL) {
926
927
0
            cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
928
0
            cmsDeleteTransform(p);
929
0
            return NULL;
930
0
        }
931
932
126k
        if (*dwFlags & cmsFLAGS_NULLTRANSFORM) {
933
934
0
            p ->xform = NullFloatXFORM;
935
0
        }
936
126k
        else {
937
            // Float transforms don't use cache, always are non-NULL
938
126k
            p ->xform = FloatXFORM;
939
126k
        }
940
941
126k
    }
942
59.6k
    else {
943
944
        // Formats are intended to be changed before use
945
59.6k
        if (*InputFormat == 0 && *OutputFormat == 0) {
946
0
            p->FromInput = UnrollNothing;
947
0
            p->ToOutput = PackNothing;
948
0
            *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
949
0
        }
950
59.6k
        else {
951
952
59.6k
            cmsUInt32Number BytesPerPixelInput;
953
954
59.6k
            p ->FromInput = _cmsGetFormatter(ContextID, *InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
955
59.6k
            p ->ToOutput  = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
956
957
59.6k
            if (p ->FromInput == NULL || p ->ToOutput == NULL) {
958
959
0
                cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
960
0
                cmsDeleteTransform(p);
961
0
                return NULL;
962
0
            }
963
964
59.6k
            BytesPerPixelInput = T_BYTES(*InputFormat);
965
59.6k
            if (BytesPerPixelInput == 0 || BytesPerPixelInput >= 2)
966
0
                   *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
967
968
59.6k
        }
969
970
59.6k
        if (*dwFlags & cmsFLAGS_NULLTRANSFORM) {
971
972
0
            p ->xform = NullXFORM;
973
0
        }
974
59.6k
        else {
975
59.6k
            if (*dwFlags & cmsFLAGS_NOCACHE) {
976
977
0
                if (*dwFlags & cmsFLAGS_GAMUTCHECK)
978
0
                    p ->xform = PrecalculatedXFORMGamutCheck;  // Gamut check, no cache
979
0
                else
980
0
                    p ->xform = PrecalculatedXFORM;  // No cache, no gamut check
981
0
            }
982
59.6k
            else {
983
984
59.6k
                if (*dwFlags & cmsFLAGS_GAMUTCHECK)
985
0
                    p ->xform = CachedXFORMGamutCheck;    // Gamut check, cache
986
59.6k
                else
987
59.6k
                    p ->xform = CachedXFORM;  // No gamut check, cache
988
989
59.6k
            }
990
59.6k
        }
991
59.6k
    }
992
993
    /**
994
    * Check consistency for alpha channel copy
995
    */
996
185k
    if (*dwFlags & cmsFLAGS_COPY_ALPHA)
997
0
    {
998
0
        if (T_EXTRA(*InputFormat) != T_EXTRA(*OutputFormat))
999
0
        {
1000
0
            cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Mismatched alpha channels");
1001
0
            cmsDeleteTransform(p);
1002
0
            return NULL;
1003
0
        }
1004
0
    }
1005
1006
185k
    p ->InputFormat     = *InputFormat;
1007
185k
    p ->OutputFormat    = *OutputFormat;
1008
185k
    p ->dwOriginalFlags = *dwFlags;
1009
185k
    p ->ContextID       = ContextID;
1010
185k
    p ->UserData        = NULL;
1011
185k
    ParalellizeIfSuitable(p);
1012
185k
    return p;
1013
185k
}
1014
1015
static
1016
cmsBool GetXFormColorSpaces(cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[], cmsColorSpaceSignature* Input, cmsColorSpaceSignature* Output)
1017
266k
{
1018
266k
    cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut;
1019
266k
    cmsColorSpaceSignature PostColorSpace;
1020
266k
    cmsUInt32Number i;
1021
1022
266k
    if (nProfiles == 0) return FALSE;
1023
266k
    if (hProfiles[0] == NULL) return FALSE;
1024
1025
266k
    *Input = PostColorSpace = cmsGetColorSpace(hProfiles[0]);
1026
1027
800k
    for (i=0; i < nProfiles; i++) {
1028
1029
533k
        cmsProfileClassSignature cls;
1030
533k
        cmsHPROFILE hProfile = hProfiles[i];
1031
1032
533k
        int lIsInput = (PostColorSpace != cmsSigXYZData) &&
1033
269k
                       (PostColorSpace != cmsSigLabData);
1034
1035
533k
        if (hProfile == NULL) return FALSE;
1036
1037
533k
        cls = cmsGetDeviceClass(hProfile);
1038
1039
533k
        if (cls == cmsSigNamedColorClass) {
1040
1041
0
            ColorSpaceIn    = cmsSig1colorData;
1042
0
            ColorSpaceOut   = (nProfiles > 1) ? cmsGetPCS(hProfile) : cmsGetColorSpace(hProfile);
1043
0
        }
1044
533k
        else
1045
533k
        if (lIsInput || (cls == cmsSigLinkClass)) {
1046
1047
269k
            ColorSpaceIn    = cmsGetColorSpace(hProfile);
1048
269k
            ColorSpaceOut   = cmsGetPCS(hProfile);
1049
269k
        }
1050
264k
        else
1051
264k
        {
1052
264k
            ColorSpaceIn    = cmsGetPCS(hProfile);
1053
264k
            ColorSpaceOut   = cmsGetColorSpace(hProfile);
1054
264k
        }
1055
1056
533k
        if (i==0)
1057
266k
            *Input = ColorSpaceIn;
1058
1059
533k
        PostColorSpace = ColorSpaceOut;
1060
533k
    }
1061
1062
266k
    *Output = PostColorSpace;
1063
1064
266k
    return TRUE;
1065
266k
}
1066
1067
// Check colorspace
1068
static
1069
cmsBool  IsProperColorSpace(cmsColorSpaceSignature Check, cmsUInt32Number dwFormat)
1070
526k
{
1071
526k
    int Space1 = (int) T_COLORSPACE(dwFormat);
1072
526k
    int Space2 = _cmsLCMScolorSpace(Check);
1073
1074
526k
    if (dwFormat == 0) return TRUE; // Bypass used by linkicc
1075
1076
526k
    if (Space1 == PT_ANY) return (T_CHANNELS(dwFormat) == cmsChannelsOf(Check));
1077
456k
    if (Space1 == Space2) return TRUE;
1078
1079
9.10k
    if (Space1 == PT_LabV2 && Space2 == PT_Lab) return TRUE;
1080
9.10k
    if (Space1 == PT_Lab   && Space2 == PT_LabV2) return TRUE;
1081
1082
9.10k
    return FALSE;
1083
9.10k
}
1084
1085
// ----------------------------------------------------------------------------------------------------------------
1086
1087
// Jun-21-2000: Some profiles (those that comes with W2K) comes
1088
// with the media white (media black?) x 100. Add a sanity check
1089
1090
static
1091
void NormalizeXYZ(cmsCIEXYZ* Dest)
1092
366k
{
1093
372k
    while (Dest -> X > 2. &&
1094
10.7k
           Dest -> Y > 2. &&
1095
8.62k
           Dest -> Z > 2.) {
1096
1097
6.79k
               Dest -> X /= 10.;
1098
6.79k
               Dest -> Y /= 10.;
1099
6.79k
               Dest -> Z /= 10.;
1100
6.79k
       }
1101
366k
}
1102
1103
static
1104
void SetWhitePoint(cmsCIEXYZ* wtPt, const cmsCIEXYZ* src)
1105
371k
{
1106
371k
    if (src == NULL) {
1107
5.74k
        wtPt ->X = cmsD50X;
1108
5.74k
        wtPt ->Y = cmsD50Y;
1109
5.74k
        wtPt ->Z = cmsD50Z;
1110
5.74k
    }
1111
366k
    else {
1112
366k
        wtPt ->X = src->X;
1113
366k
        wtPt ->Y = src->Y;
1114
366k
        wtPt ->Z = src->Z;
1115
1116
366k
        NormalizeXYZ(wtPt);
1117
366k
    }
1118
1119
371k
}
1120
1121
// New to lcms 2.0 -- have all parameters available.
1122
cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
1123
                                                   cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[],
1124
                                                   cmsBool  BPC[],
1125
                                                   cmsUInt32Number Intents[],
1126
                                                   cmsFloat64Number AdaptationStates[],
1127
                                                   cmsHPROFILE hGamutProfile,
1128
                                                   cmsUInt32Number nGamutPCSposition,
1129
                                                   cmsUInt32Number InputFormat,
1130
                                                   cmsUInt32Number OutputFormat,
1131
                                                   cmsUInt32Number dwFlags)
1132
266k
{
1133
266k
    _cmsTRANSFORM* xform;    
1134
266k
    cmsColorSpaceSignature EntryColorSpace;
1135
266k
    cmsColorSpaceSignature ExitColorSpace;
1136
266k
    cmsPipeline* Lut;
1137
266k
    cmsUInt32Number LastIntent;
1138
1139
    // Safeguard
1140
266k
    if (nProfiles <= 0 || nProfiles > 255) {
1141
0
        cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
1142
0
        return NULL;
1143
0
    }
1144
1145
266k
    LastIntent = Intents[nProfiles - 1];
1146
1147
    // If it is a fake transform
1148
266k
    if (dwFlags & cmsFLAGS_NULLTRANSFORM)
1149
0
    {
1150
0
        return AllocEmptyTransform(ContextID, NULL, INTENT_PERCEPTUAL, &InputFormat, &OutputFormat, &dwFlags);
1151
0
    }
1152
1153
    // If gamut check is requested, make sure we have a gamut profile
1154
266k
    if (dwFlags & cmsFLAGS_GAMUTCHECK) {
1155
0
        if (hGamutProfile == NULL) dwFlags &= ~cmsFLAGS_GAMUTCHECK;
1156
0
    }
1157
1158
266k
    if ((dwFlags & cmsFLAGS_GAMUTCHECK) && (nGamutPCSposition <= 0 || nGamutPCSposition >= nProfiles - 1)) {
1159
0
        cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong gamut PCS position '%d'", nGamutPCSposition);
1160
0
        return NULL;
1161
0
    }
1162
1163
    // On floating point transforms, inhibit cache
1164
266k
    if (_cmsFormatterIsFloat(InputFormat) || _cmsFormatterIsFloat(OutputFormat))
1165
127k
        dwFlags |= cmsFLAGS_NOCACHE;
1166
1167
    // Mark entry/exit spaces
1168
266k
    if (!GetXFormColorSpaces(nProfiles, hProfiles, &EntryColorSpace, &ExitColorSpace)) {
1169
0
        cmsSignalError(ContextID, cmsERROR_NULL, "NULL input profiles on transform");
1170
0
        return NULL;
1171
0
    }
1172
1173
    // Check if proper colorspaces
1174
266k
    if (!IsProperColorSpace(EntryColorSpace, InputFormat)) {
1175
7.79k
        cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong input color space on transform");
1176
7.79k
        return NULL;
1177
7.79k
    }
1178
1179
259k
    if (!IsProperColorSpace(ExitColorSpace, OutputFormat)) {
1180
4.09k
        cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong output color space on transform");
1181
4.09k
        return NULL;
1182
4.09k
    }
1183
1184
    // Check whatever the transform is 16 bits and involves linear RGB in first profile. If so, disable optimizations
1185
255k
    if (EntryColorSpace == cmsSigRgbData && T_BYTES(InputFormat) == 2 && !(dwFlags & cmsFLAGS_NOOPTIMIZE))
1186
0
    {
1187
0
        cmsFloat64Number gamma = cmsDetectRGBProfileGamma(hProfiles[0], 0.1);
1188
1189
0
        if (gamma > 0 && gamma < 1.6)
1190
0
            dwFlags |= cmsFLAGS_NOOPTIMIZE;
1191
0
    }
1192
1193
    // Create a pipeline with all transformations
1194
255k
    Lut = _cmsLinkProfiles(ContextID, nProfiles, Intents, hProfiles, BPC, AdaptationStates, dwFlags);
1195
255k
    if (Lut == NULL) {
1196
67.5k
        cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Couldn't link the profiles");
1197
67.5k
        return NULL;
1198
67.5k
    }
1199
1200
    // Check channel count
1201
187k
    if ((cmsChannelsOfColorSpace(EntryColorSpace) != (cmsInt32Number) cmsPipelineInputChannels(Lut)) ||
1202
185k
        (cmsChannelsOfColorSpace(ExitColorSpace)  != (cmsInt32Number) cmsPipelineOutputChannels(Lut))) {
1203
1.53k
        cmsPipelineFree(Lut);
1204
1.53k
        cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Channel count doesn't match. Profile is corrupted");
1205
1.53k
        return NULL;
1206
1.53k
    }
1207
1208
1209
    // All seems ok
1210
185k
    xform = AllocEmptyTransform(ContextID, Lut, LastIntent, &InputFormat, &OutputFormat, &dwFlags);
1211
185k
    if (xform == NULL) {
1212
0
        return NULL;
1213
0
    }
1214
1215
    // Keep values
1216
185k
    xform ->EntryColorSpace = EntryColorSpace;
1217
185k
    xform ->ExitColorSpace  = ExitColorSpace;
1218
185k
    xform ->RenderingIntent = Intents[nProfiles-1];
1219
1220
    // Take white points
1221
185k
    SetWhitePoint(&xform->EntryWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[0], cmsSigMediaWhitePointTag));
1222
185k
    SetWhitePoint(&xform->ExitWhitePoint,  (cmsCIEXYZ*) cmsReadTag(hProfiles[nProfiles-1], cmsSigMediaWhitePointTag));
1223
   
1224
1225
    // Create a gamut check LUT if requested
1226
185k
    if (hGamutProfile != NULL && (dwFlags & cmsFLAGS_GAMUTCHECK))
1227
0
        xform ->GamutCheck  = _cmsCreateGamutCheckPipeline(ContextID, hProfiles,
1228
0
                                                        BPC, Intents,
1229
0
                                                        AdaptationStates,
1230
0
                                                        nGamutPCSposition,
1231
0
                                                        hGamutProfile);
1232
1233
1234
    // Try to read input and output colorant table
1235
185k
    if (cmsIsTag(hProfiles[0], cmsSigColorantTableTag)) {
1236
1237
        // Input table can only come in this way.
1238
552
        xform ->InputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[0], cmsSigColorantTableTag));
1239
552
    }
1240
1241
    // Output is a little bit more complex.
1242
185k
    if (cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigLinkClass) {
1243
1244
        // This tag may exist only on devicelink profiles.
1245
0
        if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag)) {
1246
1247
            // It may be NULL if error
1248
0
            xform ->OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag));
1249
0
        }
1250
1251
185k
    } else {
1252
1253
185k
        if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableTag)) {
1254
1255
0
            xform -> OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableTag));
1256
0
        }
1257
185k
    }
1258
1259
    // Store the sequence of profiles
1260
185k
    if (dwFlags & cmsFLAGS_KEEP_SEQUENCE) {
1261
0
        xform ->Sequence = _cmsCompileProfileSequence(ContextID, nProfiles, hProfiles);
1262
0
    }
1263
185k
    else
1264
185k
        xform ->Sequence = NULL;
1265
1266
    // If this is a cached transform, init first value, which is zero (16 bits only)
1267
185k
    if (!(dwFlags & cmsFLAGS_NOCACHE)) {
1268
1269
59.6k
        memset(&xform ->Cache.CacheIn, 0, sizeof(xform ->Cache.CacheIn));
1270
1271
59.6k
        if (xform ->GamutCheck != NULL) {
1272
0
            TransformOnePixelWithGamutCheck(xform, xform ->Cache.CacheIn, xform->Cache.CacheOut);
1273
0
        }
1274
59.6k
        else {
1275
1276
59.6k
            xform ->Lut ->Eval16Fn(xform ->Cache.CacheIn, xform->Cache.CacheOut, xform -> Lut->Data);
1277
59.6k
        }
1278
1279
59.6k
    }
1280
1281
185k
    return (cmsHTRANSFORM) xform;
1282
185k
}
1283
1284
// Multiprofile transforms: Gamut check is not available here, as it is unclear from which profile the gamut comes.
1285
cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransformTHR(cmsContext ContextID,
1286
                                                       cmsHPROFILE hProfiles[],
1287
                                                       cmsUInt32Number nProfiles,
1288
                                                       cmsUInt32Number InputFormat,
1289
                                                       cmsUInt32Number OutputFormat,
1290
                                                       cmsUInt32Number Intent,
1291
                                                       cmsUInt32Number dwFlags)
1292
266k
{
1293
266k
    cmsUInt32Number i;
1294
266k
    cmsBool BPC[256];
1295
266k
    cmsUInt32Number Intents[256];
1296
266k
    cmsFloat64Number AdaptationStates[256];
1297
1298
266k
    if (nProfiles <= 0 || nProfiles > 255) {
1299
0
         cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
1300
0
        return NULL;
1301
0
    }
1302
1303
800k
    for (i=0; i < nProfiles; i++) {
1304
533k
        BPC[i] = dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION ? TRUE : FALSE;
1305
533k
        Intents[i] = Intent;
1306
533k
        AdaptationStates[i] = cmsSetAdaptationStateTHR(ContextID, -1);
1307
533k
    }
1308
1309
1310
266k
    return cmsCreateExtendedTransform(ContextID, nProfiles, hProfiles, BPC, Intents, AdaptationStates, NULL, 0, InputFormat, OutputFormat, dwFlags);
1311
266k
}
1312
1313
1314
1315
cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles[],
1316
                                                  cmsUInt32Number nProfiles,
1317
                                                  cmsUInt32Number InputFormat,
1318
                                                  cmsUInt32Number OutputFormat,
1319
                                                  cmsUInt32Number Intent,
1320
                                                  cmsUInt32Number dwFlags)
1321
4
{
1322
1323
4
    if (nProfiles <= 0 || nProfiles > 255) {
1324
0
         cmsSignalError(NULL, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
1325
0
         return NULL;
1326
0
    }
1327
1328
4
    return cmsCreateMultiprofileTransformTHR(cmsGetProfileContextID(hProfiles[0]),
1329
4
                                                  hProfiles,
1330
4
                                                  nProfiles,
1331
4
                                                  InputFormat,
1332
4
                                                  OutputFormat,
1333
4
                                                  Intent,
1334
4
                                                  dwFlags);
1335
4
}
1336
1337
cmsHTRANSFORM CMSEXPORT cmsCreateTransformTHR(cmsContext ContextID,
1338
                                              cmsHPROFILE Input,
1339
                                              cmsUInt32Number InputFormat,
1340
                                              cmsHPROFILE Output,
1341
                                              cmsUInt32Number OutputFormat,
1342
                                              cmsUInt32Number Intent,
1343
                                              cmsUInt32Number dwFlags)
1344
266k
{
1345
1346
266k
    cmsHPROFILE hArray[2];
1347
1348
266k
    hArray[0] = Input;
1349
266k
    hArray[1] = Output;
1350
1351
266k
    return cmsCreateMultiprofileTransformTHR(ContextID, hArray, Output == NULL ? 1U : 2U, InputFormat, OutputFormat, Intent, dwFlags);
1352
266k
}
1353
1354
CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateTransform(cmsHPROFILE Input,
1355
                                                  cmsUInt32Number InputFormat,
1356
                                                  cmsHPROFILE Output,
1357
                                                  cmsUInt32Number OutputFormat,
1358
                                                  cmsUInt32Number Intent,
1359
                                                  cmsUInt32Number dwFlags)
1360
143k
{
1361
143k
    return cmsCreateTransformTHR(cmsGetProfileContextID(Input), Input, InputFormat, Output, OutputFormat, Intent, dwFlags);
1362
143k
}
1363
1364
1365
cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransformTHR(cmsContext ContextID,
1366
                                                   cmsHPROFILE InputProfile,
1367
                                                   cmsUInt32Number InputFormat,
1368
                                                   cmsHPROFILE OutputProfile,
1369
                                                   cmsUInt32Number OutputFormat,
1370
                                                   cmsHPROFILE ProofingProfile,
1371
                                                   cmsUInt32Number nIntent,
1372
                                                   cmsUInt32Number ProofingIntent,
1373
                                                   cmsUInt32Number dwFlags)
1374
0
{
1375
0
    cmsHPROFILE hArray[4];
1376
0
    cmsUInt32Number Intents[4];
1377
0
    cmsBool  BPC[4];
1378
0
    cmsFloat64Number Adaptation[4];
1379
0
    cmsBool  DoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION) ? TRUE : FALSE;
1380
1381
1382
0
    hArray[0]  = InputProfile; hArray[1] = ProofingProfile; hArray[2]  = ProofingProfile;               hArray[3] = OutputProfile;
1383
0
    Intents[0] = nIntent;      Intents[1] = nIntent;        Intents[2] = INTENT_RELATIVE_COLORIMETRIC;  Intents[3] = ProofingIntent;
1384
0
    BPC[0]     = DoBPC;        BPC[1] = DoBPC;              BPC[2] = 0;                                 BPC[3] = 0;
1385
1386
0
    Adaptation[0] = Adaptation[1] = Adaptation[2] = Adaptation[3] = cmsSetAdaptationStateTHR(ContextID, -1);
1387
1388
0
    if (!(dwFlags & (cmsFLAGS_SOFTPROOFING|cmsFLAGS_GAMUTCHECK)))
1389
0
        return cmsCreateTransformTHR(ContextID, InputProfile, InputFormat, OutputProfile, OutputFormat, nIntent, dwFlags);
1390
1391
0
    return cmsCreateExtendedTransform(ContextID, 4, hArray, BPC, Intents, Adaptation,
1392
0
                                        ProofingProfile, 1, InputFormat, OutputFormat, dwFlags);
1393
1394
0
}
1395
1396
1397
cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransform(cmsHPROFILE InputProfile,
1398
                                                   cmsUInt32Number InputFormat,
1399
                                                   cmsHPROFILE OutputProfile,
1400
                                                   cmsUInt32Number OutputFormat,
1401
                                                   cmsHPROFILE ProofingProfile,
1402
                                                   cmsUInt32Number nIntent,
1403
                                                   cmsUInt32Number ProofingIntent,
1404
                                                   cmsUInt32Number dwFlags)
1405
0
{
1406
0
    return cmsCreateProofingTransformTHR(cmsGetProfileContextID(InputProfile),
1407
0
                                                   InputProfile,
1408
0
                                                   InputFormat,
1409
0
                                                   OutputProfile,
1410
0
                                                   OutputFormat,
1411
0
                                                   ProofingProfile,
1412
0
                                                   nIntent,
1413
0
                                                   ProofingIntent,
1414
0
                                                   dwFlags);
1415
0
}
1416
1417
1418
// Grab the ContextID from an open transform. Returns NULL if a NULL transform is passed
1419
cmsContext CMSEXPORT cmsGetTransformContextID(cmsHTRANSFORM hTransform)
1420
0
{
1421
0
    _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1422
1423
0
    if (xform == NULL) return NULL;
1424
0
    return xform -> ContextID;
1425
0
}
1426
1427
// Grab the input/output formats
1428
cmsUInt32Number CMSEXPORT cmsGetTransformInputFormat(cmsHTRANSFORM hTransform)
1429
0
{
1430
0
    _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1431
1432
0
    if (xform == NULL) return 0;
1433
0
    return xform->InputFormat;
1434
0
}
1435
1436
cmsUInt32Number CMSEXPORT cmsGetTransformOutputFormat(cmsHTRANSFORM hTransform)
1437
0
{
1438
0
    _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1439
1440
0
    if (xform == NULL) return 0;
1441
0
    return xform->OutputFormat;
1442
0
}
1443
1444
// Returns the optimized pipeline (Lut) inside a transform. Read-only; do not free.
1445
cmsPipeline* CMSEXPORT cmsGetTransformPipeline(cmsHTRANSFORM hTransform)
1446
0
{
1447
0
    _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1448
0
    if (xform == NULL) return NULL;
1449
0
    return xform->Lut;
1450
0
}
1451
1452
// Returns the gamut-check pipeline inside a transform. Read-only; do not free.
1453
cmsPipeline* CMSEXPORT cmsGetTransformGamutCheckPipeline(cmsHTRANSFORM hTransform)
1454
0
{
1455
0
    _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1456
0
    if (xform == NULL) return NULL;
1457
0
    return xform->GamutCheck;
1458
0
}
1459
1460
// For backwards compatibility
1461
cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform,
1462
                                         cmsUInt32Number InputFormat,
1463
                                         cmsUInt32Number OutputFormat)
1464
0
{
1465
0
    _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1466
0
    cmsFormatter16 FromInput, ToOutput;
1467
1468
1469
    // We only can afford to change formatters if previous transform is at least 16 bits
1470
0
    if (!(xform ->dwOriginalFlags & cmsFLAGS_CAN_CHANGE_FORMATTER)) {
1471
1472
0
        cmsSignalError(xform ->ContextID, cmsERROR_NOT_SUITABLE, "cmsChangeBuffersFormat works only on transforms created originally with at least 16 bits of precision");
1473
0
        return FALSE;
1474
0
    }
1475
1476
0
    FromInput = _cmsGetFormatter(xform->ContextID, InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
1477
0
    ToOutput  = _cmsGetFormatter(xform->ContextID, OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
1478
1479
0
    if (FromInput == NULL || ToOutput == NULL) {
1480
1481
0
        cmsSignalError(xform -> ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
1482
0
        return FALSE;
1483
0
    }
1484
1485
0
    xform ->InputFormat  = InputFormat;
1486
0
    xform ->OutputFormat = OutputFormat;
1487
0
    xform ->FromInput    = FromInput;
1488
0
    xform ->ToOutput     = ToOutput;
1489
0
    return TRUE;
1490
0
}
1491
1492
cmsNAMEDCOLORLIST* CMSEXPORT cmsGetTransformInputColorants(cmsHTRANSFORM hTransform)
1493
0
{
1494
0
    _cmsTRANSFORM* xform = (_cmsTRANSFORM*)hTransform;
1495
1496
0
    if (xform == NULL) return NULL;
1497
0
    return xform->InputColorant;
1498
0
}
1499
1500
cmsNAMEDCOLORLIST* CMSEXPORT cmsGetTransformOutputColorants(cmsHTRANSFORM hTransform)
1501
0
{
1502
0
    _cmsTRANSFORM* xform = (_cmsTRANSFORM*)hTransform;
1503
1504
0
    if (xform == NULL) return NULL;
1505
0
    return xform->OutputColorant;
1506
0
}