Coverage Report

Created: 2026-06-13 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/lcms2-2.18/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
607k
{
59
607k
    cmsFloat64Number prev;
60
607k
    _cmsAdaptationStateChunkType* ptr = (_cmsAdaptationStateChunkType*) _cmsContextGetClientChunk(ContextID, AdaptationStateContext);
61
62
    // Get previous value for return
63
607k
    prev = ptr ->AdaptationState;
64
65
    // Set the value if d is positive or zero
66
607k
    if (d >= 0.0) {
67
68
0
        ptr ->AdaptationState = d;
69
0
    }
70
71
    // Always return previous value
72
607k
    return prev;
73
607k
}
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
303k
{
149
303k
    _cmsTRANSFORM* p = (_cmsTRANSFORM*) hTransform;
150
151
303k
    _cmsAssert(p != NULL);
152
153
303k
    if (p -> GamutCheck)
154
0
        cmsPipelineFree(p -> GamutCheck);
155
156
303k
    if (p -> Lut)
157
303k
        cmsPipelineFree(p -> Lut);
158
159
303k
    if (p ->InputColorant)
160
0
        cmsFreeNamedColorList(p ->InputColorant);
161
162
303k
    if (p -> OutputColorant)
163
0
        cmsFreeNamedColorList(p ->OutputColorant);
164
165
303k
    if (p ->Sequence)
166
0
        cmsFreeProfileSequenceDescription(p ->Sequence);
167
168
303k
    if (p ->UserData)
169
0
        p ->FreeUserData(p ->ContextID, p ->UserData);
170
171
303k
    _cmsFree(p ->ContextID, (void *) p);
172
303k
}
173
174
175
static
176
cmsUInt32Number PixelSize(cmsUInt32Number Format)
177
3.66M
{
178
3.66M
    cmsUInt32Number fmt_bytes = T_BYTES(Format);
179
180
    // For double, the T_BYTES field is zero
181
3.66M
    if (fmt_bytes == 0)
182
378k
        return sizeof(cmsUInt64Number);
183
184
    // Otherwise, it is already correct for all formats
185
3.28M
    return fmt_bytes;
186
3.66M
}
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
1.83M
{
198
1.83M
    _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
199
1.83M
    cmsStride stride;
200
201
1.83M
    stride.BytesPerLineIn = 0;  // Not used
202
1.83M
    stride.BytesPerLineOut = 0;
203
1.83M
    stride.BytesPerPlaneIn = Size * PixelSize(p->InputFormat);
204
1.83M
    stride.BytesPerPlaneOut = Size * PixelSize(p->OutputFormat);
205
           
206
1.83M
    p -> xform(p, InputBuffer, OutputBuffer, Size, 1, &stride);
207
1.83M
}
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
378k
{
265
378k
    cmsUInt8Number* accum;
266
378k
    cmsUInt8Number* output;
267
378k
    cmsFloat32Number fIn[cmsMAXCHANNELS], fOut[cmsMAXCHANNELS];
268
378k
    cmsFloat32Number OutOfGamut;
269
378k
    size_t i, j, c, strideIn, strideOut;
270
271
378k
    _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
272
273
378k
    strideIn = 0;
274
378k
    strideOut = 0;
275
378k
    memset(fIn, 0, sizeof(fIn));
276
378k
    memset(fOut, 0, sizeof(fOut));
277
278
757k
    for (i = 0; i < LineCount; i++) {
279
280
378k
        accum = (cmsUInt8Number*)in + strideIn;
281
378k
        output = (cmsUInt8Number*)out + strideOut;
282
283
757k
        for (j = 0; j < PixelsPerLine; j++) {
284
285
378k
            accum = p->FromInputFloat(p, fIn, accum, Stride->BytesPerPlaneIn);
286
287
            // Any gamut check to do?
288
378k
            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
378k
            else {
309
310
                // No gamut check at all
311
378k
                cmsPipelineEvalFloat(fIn, fOut, p->Lut);
312
378k
            }
313
314
315
378k
            output = p->ToOutputFloat(p, fOut, output, Stride->BytesPerPlaneOut);
316
378k
        }
317
318
378k
        strideIn += Stride->BytesPerLineIn;
319
378k
        strideOut += Stride->BytesPerLineOut;
320
378k
    }
321
322
378k
}
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
1.45M
{
410
1.45M
    CMSREGISTER cmsUInt8Number* accum;
411
1.45M
    CMSREGISTER cmsUInt8Number* output;
412
1.45M
    cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
413
1.45M
    size_t i, j, strideIn, strideOut;
414
415
1.45M
    _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
416
417
1.45M
    strideIn = 0;
418
1.45M
    strideOut = 0;
419
1.45M
    memset(wIn, 0, sizeof(wIn));
420
1.45M
    memset(wOut, 0, sizeof(wOut));
421
422
2.90M
    for (i = 0; i < LineCount; i++) {
423
424
1.45M
        accum = (cmsUInt8Number*)in + strideIn;
425
1.45M
        output = (cmsUInt8Number*)out + strideOut;
426
427
2.90M
        for (j = 0; j < PixelsPerLine; j++) {
428
429
1.45M
            accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn);
430
1.45M
            p->Lut->Eval16Fn(wIn, wOut, p->Lut->Data);
431
1.45M
            output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut);
432
1.45M
        }
433
434
1.45M
        strideIn += Stride->BytesPerLineIn;
435
1.45M
        strideOut += Stride->BytesPerLineOut;
436
1.45M
    }
437
438
1.45M
}
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
503
// No gamut check, Cache, 16 bits,
504
static
505
void CachedXFORM(_cmsTRANSFORM* p,
506
                 const void* in,
507
                 void* out,
508
                 cmsUInt32Number PixelsPerLine,
509
                 cmsUInt32Number LineCount,
510
                 const cmsStride* Stride)
511
1.70k
{
512
1.70k
    cmsUInt8Number* accum;
513
1.70k
    cmsUInt8Number* output;
514
1.70k
    cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
515
1.70k
    _cmsCACHE Cache;
516
1.70k
    size_t i, j, strideIn, strideOut;
517
518
1.70k
    _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
519
520
    // Empty buffers for quick memcmp
521
1.70k
    memset(wIn, 0, sizeof(wIn));
522
1.70k
    memset(wOut, 0, sizeof(wOut));
523
524
    // Get copy of zero cache
525
1.70k
    memcpy(&Cache, &p->Cache, sizeof(Cache));
526
527
1.70k
    strideIn = 0;
528
1.70k
    strideOut = 0;
529
530
3.40k
    for (i = 0; i < LineCount; i++) {
531
532
1.70k
        accum = (cmsUInt8Number*)in + strideIn;
533
1.70k
        output = (cmsUInt8Number*)out + strideOut;
534
535
3.40k
        for (j = 0; j < PixelsPerLine; j++) {
536
537
1.70k
            accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn);
538
539
1.70k
            if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) {
540
541
1.16k
                memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut));
542
1.16k
            }
543
539
            else {
544
539
                p->Lut->Eval16Fn(wIn, wOut, p->Lut->Data);
545
546
539
                memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn));
547
539
                memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut));
548
539
            }
549
550
1.70k
            output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut);
551
1.70k
        }
552
553
1.70k
        strideIn += Stride->BytesPerLineIn;
554
1.70k
        strideOut += Stride->BytesPerLineOut;
555
1.70k
    }
556
1.70k
}
557
558
// All those nice features together
559
static
560
void CachedXFORMGamutCheck(_cmsTRANSFORM* p,
561
                           const void* in,
562
                           void* out, 
563
                           cmsUInt32Number PixelsPerLine,
564
                           cmsUInt32Number LineCount,
565
                           const cmsStride* Stride)
566
0
{
567
0
    cmsUInt8Number* accum;
568
0
    cmsUInt8Number* output;
569
0
    cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
570
0
    _cmsCACHE Cache;
571
0
    size_t i, j, strideIn, strideOut;
572
573
0
    _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
574
575
    // Empty buffers for quick memcmp
576
0
    memset(wIn, 0, sizeof(wIn));
577
0
    memset(wOut, 0, sizeof(wOut));
578
579
    // Get copy of zero cache
580
0
    memcpy(&Cache, &p->Cache, sizeof(Cache));
581
582
0
    strideIn = 0;
583
0
    strideOut = 0;
584
585
0
    for (i = 0; i < LineCount; i++) {
586
587
0
        accum = (cmsUInt8Number*)in + strideIn;
588
0
        output = (cmsUInt8Number*)out + strideOut;
589
590
0
        for (j = 0; j < PixelsPerLine; j++) {
591
592
0
            accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn);
593
594
0
            if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) {
595
596
0
                memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut));
597
0
            }
598
0
            else {
599
0
                TransformOnePixelWithGamutCheck(p, wIn, wOut);
600
601
0
                memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn));
602
0
                memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut));
603
0
            }
604
605
0
            output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut);
606
0
        }
607
608
0
        strideIn += Stride->BytesPerLineIn;
609
0
        strideOut += Stride->BytesPerLineOut;
610
0
    }
611
0
}
612
613
// Transform plug-ins ----------------------------------------------------------------------------------------------------
614
615
// List of used-defined transform factories
616
typedef struct _cmsTransformCollection_st {
617
618
    _cmsTransform2Factory  Factory;
619
    cmsBool                OldXform;   // Factory returns xform function in the old style
620
621
    struct _cmsTransformCollection_st *Next;
622
623
} _cmsTransformCollection;
624
625
// The linked list head
626
_cmsTransformPluginChunkType _cmsTransformPluginChunk = { NULL };
627
628
629
// Duplicates the zone of memory used by the plug-in in the new context
630
static
631
void DupPluginTransformList(struct _cmsContext_struct* ctx, 
632
                                               const struct _cmsContext_struct* src)
633
0
{
634
0
   _cmsTransformPluginChunkType newHead = { NULL };
635
0
   _cmsTransformCollection*  entry;
636
0
   _cmsTransformCollection*  Anterior = NULL;
637
0
   _cmsTransformPluginChunkType* head = (_cmsTransformPluginChunkType*) src->chunks[TransformPlugin];
638
639
    // Walk the list copying all nodes
640
0
   for (entry = head->TransformCollection;
641
0
        entry != NULL;
642
0
        entry = entry ->Next) {
643
644
0
            _cmsTransformCollection *newEntry = ( _cmsTransformCollection *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTransformCollection));
645
   
646
0
            if (newEntry == NULL) 
647
0
                return;
648
649
            // We want to keep the linked list order, so this is a little bit tricky
650
0
            newEntry -> Next = NULL;
651
0
            if (Anterior)
652
0
                Anterior -> Next = newEntry;
653
     
654
0
            Anterior = newEntry;
655
656
0
            if (newHead.TransformCollection == NULL)
657
0
                newHead.TransformCollection = newEntry;
658
0
    }
659
660
0
  ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsTransformPluginChunkType));
661
0
}
662
663
// Allocates memory for transform plugin factory
664
void _cmsAllocTransformPluginChunk(struct _cmsContext_struct* ctx, 
665
                                        const struct _cmsContext_struct* src)
666
0
{
667
0
    if (src != NULL) {
668
669
        // Copy all linked list
670
0
        DupPluginTransformList(ctx, src);
671
0
    }
672
0
    else {
673
0
        static _cmsTransformPluginChunkType TransformPluginChunkType = { NULL };
674
0
        ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx ->MemPool, &TransformPluginChunkType, sizeof(_cmsTransformPluginChunkType));
675
0
    }
676
0
}
677
678
// Adaptor for old versions of plug-in
679
static
680
void _cmsTransform2toTransformAdaptor(struct _cmstransform_struct *CMMcargo,
681
                                      const void* InputBuffer,
682
                                      void* OutputBuffer,
683
                                      cmsUInt32Number PixelsPerLine,
684
                                      cmsUInt32Number LineCount,
685
                                      const cmsStride* Stride)
686
0
{
687
     
688
0
       size_t i, strideIn, strideOut;
689
690
0
       _cmsHandleExtraChannels(CMMcargo, InputBuffer, OutputBuffer, PixelsPerLine, LineCount, Stride);
691
692
0
       strideIn = 0;
693
0
       strideOut = 0;
694
695
0
       for (i = 0; i < LineCount; i++) {
696
697
0
              void *accum = (cmsUInt8Number*)InputBuffer + strideIn;
698
0
              void *output = (cmsUInt8Number*)OutputBuffer + strideOut;
699
700
0
              CMMcargo->OldXform(CMMcargo, accum, output, PixelsPerLine, Stride->BytesPerPlaneIn);
701
702
0
              strideIn += Stride->BytesPerLineIn;
703
0
              strideOut += Stride->BytesPerLineOut;
704
0
       }
705
0
}
706
707
708
709
// Register new ways to transform
710
cmsBool  _cmsRegisterTransformPlugin(cmsContext ContextID, cmsPluginBase* Data)
711
0
{
712
0
    cmsPluginTransform* Plugin = (cmsPluginTransform*) Data;
713
0
    _cmsTransformCollection* fl;
714
0
    _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID,TransformPlugin);
715
716
0
    if (Data == NULL) {
717
718
        // Free the chain. Memory is safely freed at exit
719
0
        ctx->TransformCollection = NULL;
720
0
        return TRUE;
721
0
    }
722
723
    // Factory callback is required
724
0
    if (Plugin->factories.xform == NULL) return FALSE;
725
726
727
0
    fl = (_cmsTransformCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsTransformCollection));
728
0
    if (fl == NULL) return FALSE;
729
730
    // Check for full xform plug-ins previous to 2.8, we would need an adapter in that case
731
0
    if (Plugin->base.ExpectedVersion < 2080) {
732
733
0
           fl->OldXform = TRUE;
734
0
    }
735
0
    else
736
0
           fl->OldXform = FALSE;
737
738
    // Copy the parameters
739
0
    fl->Factory = Plugin->factories.xform;
740
741
    // Keep linked list
742
0
    fl ->Next = ctx->TransformCollection;
743
0
    ctx->TransformCollection = fl;
744
745
    // All is ok
746
0
    return TRUE;
747
0
}
748
749
750
void CMSEXPORT _cmsSetTransformUserData(struct _cmstransform_struct *CMMcargo, void* ptr, _cmsFreeUserDataFn FreePrivateDataFn)
751
0
{
752
0
    _cmsAssert(CMMcargo != NULL);
753
0
    CMMcargo ->UserData = ptr;
754
0
    CMMcargo ->FreeUserData = FreePrivateDataFn;
755
0
}
756
757
// returns the pointer defined by the plug-in to store private data
758
void * CMSEXPORT _cmsGetTransformUserData(struct _cmstransform_struct *CMMcargo)
759
0
{
760
0
    _cmsAssert(CMMcargo != NULL);
761
0
    return CMMcargo ->UserData;
762
0
}
763
764
// returns the current formatters
765
void CMSEXPORT _cmsGetTransformFormatters16(struct _cmstransform_struct *CMMcargo, cmsFormatter16* FromInput, cmsFormatter16* ToOutput)
766
0
{
767
0
     _cmsAssert(CMMcargo != NULL);
768
0
     if (FromInput) *FromInput = CMMcargo ->FromInput;
769
0
     if (ToOutput)  *ToOutput  = CMMcargo ->ToOutput;
770
0
}
771
772
void CMSEXPORT _cmsGetTransformFormattersFloat(struct _cmstransform_struct *CMMcargo, cmsFormatterFloat* FromInput, cmsFormatterFloat* ToOutput)
773
0
{
774
0
     _cmsAssert(CMMcargo != NULL);
775
0
     if (FromInput) *FromInput = CMMcargo ->FromInputFloat;
776
0
     if (ToOutput)  *ToOutput  = CMMcargo ->ToOutputFloat;
777
0
}
778
779
// returns original flags
780
cmsUInt32Number CMSEXPORT _cmsGetTransformFlags(struct _cmstransform_struct* CMMcargo)
781
0
{
782
0
    _cmsAssert(CMMcargo != NULL);
783
0
    return CMMcargo->dwOriginalFlags;
784
0
}
785
786
// Returns the worker callback for parallelization plug-ins
787
_cmsTransform2Fn CMSEXPORT _cmsGetTransformWorker(struct _cmstransform_struct* CMMcargo)
788
0
{
789
0
    _cmsAssert(CMMcargo != NULL);
790
0
    return CMMcargo->Worker;
791
0
}
792
793
// This field holds maximum number of workers or -1 to auto 
794
cmsInt32Number CMSEXPORT _cmsGetTransformMaxWorkers(struct _cmstransform_struct* CMMcargo)
795
0
{
796
0
    _cmsAssert(CMMcargo != NULL);
797
0
    return CMMcargo->MaxWorkers;
798
0
}
799
800
// This field is actually unused and reserved
801
cmsUInt32Number CMSEXPORT _cmsGetTransformWorkerFlags(struct _cmstransform_struct* CMMcargo)
802
0
{
803
0
    _cmsAssert(CMMcargo != NULL);
804
0
    return CMMcargo->WorkerFlags;
805
0
}
806
807
// In the case there is a parallelization plug-in, let it to do its job
808
static
809
void ParalellizeIfSuitable(_cmsTRANSFORM* p)
810
303k
{
811
303k
    _cmsParallelizationPluginChunkType* ctx = (_cmsParallelizationPluginChunkType*)_cmsContextGetClientChunk(p->ContextID, ParallelizationPlugin);
812
813
303k
    _cmsAssert(p != NULL);
814
303k
    if (ctx != NULL && ctx->SchedulerFn != NULL) {
815
816
0
        p->Worker = p->xform;
817
0
        p->xform = ctx->SchedulerFn;
818
0
        p->MaxWorkers = ctx->MaxWorkers;
819
0
        p->WorkerFlags = ctx->WorkerFlags;
820
0
    }
821
303k
}
822
823
824
/**
825
* An empty unroll to avoid a check with NULL on cmsDoTransform()
826
*/
827
static
828
cmsUInt8Number* UnrollNothing(CMSREGISTER _cmsTRANSFORM* info,
829
                              CMSREGISTER cmsUInt16Number wIn[],
830
                              CMSREGISTER cmsUInt8Number* accum,
831
                              CMSREGISTER cmsUInt32Number Stride)
832
0
{    
833
0
    return accum;
834
835
0
    cmsUNUSED_PARAMETER(info);
836
0
    cmsUNUSED_PARAMETER(wIn);
837
0
    cmsUNUSED_PARAMETER(Stride);
838
0
}
839
840
static
841
cmsUInt8Number* PackNothing(CMSREGISTER _cmsTRANSFORM* info,
842
                           CMSREGISTER cmsUInt16Number wOut[],
843
                           CMSREGISTER cmsUInt8Number* output,
844
                           CMSREGISTER cmsUInt32Number Stride)
845
0
{
846
0
    return output;
847
848
0
    cmsUNUSED_PARAMETER(info);
849
0
    cmsUNUSED_PARAMETER(wOut);
850
0
    cmsUNUSED_PARAMETER(Stride);
851
0
}
852
853
// Allocate transform struct and set it to defaults. Ask the optimization plug-in about if those formats are proper
854
// for separated transforms. If this is the case,
855
static
856
_cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut,
857
                                               cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags)
858
303k
{
859
303k
     _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID, TransformPlugin);
860
303k
     _cmsTransformCollection* Plugin;
861
862
       // Allocate needed memory
863
303k
       _cmsTRANSFORM* p = (_cmsTRANSFORM*)_cmsMallocZero(ContextID, sizeof(_cmsTRANSFORM));
864
303k
       if (!p) {
865
0
              cmsPipelineFree(lut);
866
0
              return NULL;
867
0
       }
868
869
       // Store the proposed pipeline
870
303k
       p->Lut = lut;
871
872
       // Let's see if any plug-in want to do the transform by itself
873
303k
       if (p->Lut != NULL) {
874
875
303k
           if (!(*dwFlags & cmsFLAGS_NOOPTIMIZE))
876
131k
           {
877
131k
               for (Plugin = ctx->TransformCollection;
878
131k
                   Plugin != NULL;
879
131k
                   Plugin = Plugin->Next) {
880
881
0
                   if (Plugin->Factory(&p->xform, &p->UserData, &p->FreeUserData, &p->Lut, InputFormat, OutputFormat, dwFlags)) {
882
883
                       // Last plugin in the declaration order takes control. We just keep
884
                       // the original parameters as a logging. 
885
                       // Note that cmsFLAGS_CAN_CHANGE_FORMATTER is not set, so by default 
886
                       // an optimized transform is not reusable. The plug-in can, however, change
887
                       // the flags and make it suitable.
888
889
0
                       p->ContextID = ContextID;
890
0
                       p->InputFormat = *InputFormat;
891
0
                       p->OutputFormat = *OutputFormat;
892
0
                       p->dwOriginalFlags = *dwFlags;
893
894
                       // Fill the formatters just in case the optimized routine is interested.
895
                       // No error is thrown if the formatter doesn't exist. It is up to the optimization 
896
                       // factory to decide what to do in those cases.
897
0
                       p->FromInput = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
898
0
                       p->ToOutput = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
899
0
                       p->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
900
0
                       p->ToOutputFloat = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
901
902
                       // Save the day? (Ignore the warning)
903
0
                       if (Plugin->OldXform) {
904
0
                           p->OldXform = (_cmsTransformFn)(void*)p->xform;
905
0
                           p->xform = _cmsTransform2toTransformAdaptor;
906
0
                       }
907
908
0
                       ParalellizeIfSuitable(p);
909
0
                       return p;
910
0
                   }
911
0
               }
912
131k
           }
913
914
           // Not suitable for the transform plug-in, let's check  the pipeline plug-in
915
303k
           _cmsOptimizePipeline(ContextID, &p->Lut, Intent, InputFormat, OutputFormat, dwFlags);
916
303k
       }
917
918
    // Check whatever this is a true floating point transform
919
303k
    if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) {
920
921
        // Get formatter function always return a valid union, but the contents of this union may be NULL.
922
254k
        p ->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
923
254k
        p ->ToOutputFloat  = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
924
254k
        *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
925
926
254k
        if (p ->FromInputFloat == NULL || p ->ToOutputFloat == NULL) {
927
928
0
            cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
929
0
            cmsDeleteTransform(p);
930
0
            return NULL;
931
0
        }
932
933
254k
        if (*dwFlags & cmsFLAGS_NULLTRANSFORM) {
934
935
0
            p ->xform = NullFloatXFORM;
936
0
        }
937
254k
        else {
938
            // Float transforms don't use cache, always are non-NULL
939
254k
            p ->xform = FloatXFORM;
940
254k
        }
941
942
254k
    }
943
49.2k
    else {
944
945
        // Formats are intended to be changed before use
946
49.2k
        if (*InputFormat == 0 && *OutputFormat == 0) {
947
0
            p->FromInput = UnrollNothing;
948
0
            p->ToOutput = PackNothing;
949
0
            *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
950
0
        }
951
49.2k
        else {
952
953
49.2k
            cmsUInt32Number BytesPerPixelInput;
954
955
49.2k
            p ->FromInput = _cmsGetFormatter(ContextID, *InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
956
49.2k
            p ->ToOutput  = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
957
958
49.2k
            if (p ->FromInput == NULL || p ->ToOutput == NULL) {
959
960
0
                cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
961
0
                cmsDeleteTransform(p);
962
0
                return NULL;
963
0
            }
964
965
49.2k
            BytesPerPixelInput = T_BYTES(*InputFormat);
966
49.2k
            if (BytesPerPixelInput == 0 || BytesPerPixelInput >= 2)
967
8.13k
                   *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
968
969
49.2k
        }
970
971
49.2k
        if (*dwFlags & cmsFLAGS_NULLTRANSFORM) {
972
973
0
            p ->xform = NullXFORM;
974
0
        }
975
49.2k
        else {
976
49.2k
            if (*dwFlags & cmsFLAGS_NOCACHE) {
977
978
41.0k
                if (*dwFlags & cmsFLAGS_GAMUTCHECK)
979
0
                    p ->xform = PrecalculatedXFORMGamutCheck;  // Gamut check, no cache
980
41.0k
                else
981
41.0k
                    p ->xform = PrecalculatedXFORM;  // No cache, no gamut check
982
41.0k
            }
983
8.13k
            else {
984
985
8.13k
                if (*dwFlags & cmsFLAGS_GAMUTCHECK)
986
0
                    p ->xform = CachedXFORMGamutCheck;    // Gamut check, cache
987
8.13k
                else
988
8.13k
                    p ->xform = CachedXFORM;  // No gamut check, cache
989
990
8.13k
            }
991
49.2k
        }
992
49.2k
    }
993
994
    /**
995
    * Check consistency for alpha channel copy
996
    */
997
303k
    if (*dwFlags & cmsFLAGS_COPY_ALPHA)
998
0
    {
999
0
        if (T_EXTRA(*InputFormat) != T_EXTRA(*OutputFormat))
1000
0
        {
1001
0
            cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Mismatched alpha channels");
1002
0
            cmsDeleteTransform(p);
1003
0
            return NULL;
1004
0
        }
1005
0
    }
1006
1007
303k
    p ->InputFormat     = *InputFormat;
1008
303k
    p ->OutputFormat    = *OutputFormat;
1009
303k
    p ->dwOriginalFlags = *dwFlags;
1010
303k
    p ->ContextID       = ContextID;
1011
303k
    p ->UserData        = NULL;
1012
303k
    ParalellizeIfSuitable(p);
1013
303k
    return p;
1014
303k
}
1015
1016
static
1017
cmsBool GetXFormColorSpaces(cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[], cmsColorSpaceSignature* Input, cmsColorSpaceSignature* Output)
1018
303k
{
1019
303k
    cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut;
1020
303k
    cmsColorSpaceSignature PostColorSpace;
1021
303k
    cmsUInt32Number i;
1022
1023
303k
    if (nProfiles == 0) return FALSE;
1024
303k
    if (hProfiles[0] == NULL) return FALSE;
1025
1026
303k
    *Input = PostColorSpace = cmsGetColorSpace(hProfiles[0]);
1027
1028
911k
    for (i=0; i < nProfiles; i++) {
1029
1030
607k
        cmsProfileClassSignature cls;
1031
607k
        cmsHPROFILE hProfile = hProfiles[i];
1032
1033
607k
        int lIsInput = (PostColorSpace != cmsSigXYZData) &&
1034
394k
                       (PostColorSpace != cmsSigLabData);
1035
1036
607k
        if (hProfile == NULL) return FALSE;
1037
1038
607k
        cls = cmsGetDeviceClass(hProfile);
1039
1040
607k
        if (cls == cmsSigNamedColorClass) {
1041
1042
0
            ColorSpaceIn    = cmsSig1colorData;
1043
0
            ColorSpaceOut   = (nProfiles > 1) ? cmsGetPCS(hProfile) : cmsGetColorSpace(hProfile);
1044
0
        }
1045
607k
        else
1046
607k
        if (lIsInput || (cls == cmsSigLinkClass)) {
1047
1048
262k
            ColorSpaceIn    = cmsGetColorSpace(hProfile);
1049
262k
            ColorSpaceOut   = cmsGetPCS(hProfile);
1050
262k
        }
1051
344k
        else
1052
344k
        {
1053
344k
            ColorSpaceIn    = cmsGetPCS(hProfile);
1054
344k
            ColorSpaceOut   = cmsGetColorSpace(hProfile);
1055
344k
        }
1056
1057
607k
        if (i==0)
1058
303k
            *Input = ColorSpaceIn;
1059
1060
607k
        PostColorSpace = ColorSpaceOut;
1061
607k
    }
1062
1063
303k
    *Output = PostColorSpace;
1064
1065
303k
    return TRUE;
1066
303k
}
1067
1068
// Check colorspace
1069
static
1070
cmsBool  IsProperColorSpace(cmsColorSpaceSignature Check, cmsUInt32Number dwFormat)
1071
607k
{
1072
607k
    int Space1 = (int) T_COLORSPACE(dwFormat);
1073
607k
    int Space2 = _cmsLCMScolorSpace(Check);
1074
1075
607k
    if (dwFormat == 0) return TRUE; // Bypass used by linkicc
1076
1077
607k
    if (Space1 == PT_ANY) return (T_CHANNELS(dwFormat) == cmsChannelsOf(Check));
1078
607k
    if (Space1 == Space2) return TRUE;
1079
1080
18
    if (Space1 == PT_LabV2 && Space2 == PT_Lab) return TRUE;
1081
18
    if (Space1 == PT_Lab   && Space2 == PT_LabV2) return TRUE;
1082
1083
18
    return FALSE;
1084
18
}
1085
1086
// ----------------------------------------------------------------------------------------------------------------
1087
1088
// Jun-21-2000: Some profiles (those that comes with W2K) comes
1089
// with the media white (media black?) x 100. Add a sanity check
1090
1091
static
1092
void NormalizeXYZ(cmsCIEXYZ* Dest)
1093
558k
{
1094
558k
    while (Dest -> X > 2. &&
1095
0
           Dest -> Y > 2. &&
1096
0
           Dest -> Z > 2.) {
1097
1098
0
               Dest -> X /= 10.;
1099
0
               Dest -> Y /= 10.;
1100
0
               Dest -> Z /= 10.;
1101
0
       }
1102
558k
}
1103
1104
static
1105
void SetWhitePoint(cmsCIEXYZ* wtPt, const cmsCIEXYZ* src)
1106
607k
{
1107
607k
    if (src == NULL) {
1108
49.2k
        wtPt ->X = cmsD50X;
1109
49.2k
        wtPt ->Y = cmsD50Y;
1110
49.2k
        wtPt ->Z = cmsD50Z;
1111
49.2k
    }
1112
558k
    else {
1113
558k
        wtPt ->X = src->X;
1114
558k
        wtPt ->Y = src->Y;
1115
558k
        wtPt ->Z = src->Z;
1116
1117
558k
        NormalizeXYZ(wtPt);
1118
558k
    }
1119
1120
607k
}
1121
1122
// New to lcms 2.0 -- have all parameters available.
1123
cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
1124
                                                   cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[],
1125
                                                   cmsBool  BPC[],
1126
                                                   cmsUInt32Number Intents[],
1127
                                                   cmsFloat64Number AdaptationStates[],
1128
                                                   cmsHPROFILE hGamutProfile,
1129
                                                   cmsUInt32Number nGamutPCSposition,
1130
                                                   cmsUInt32Number InputFormat,
1131
                                                   cmsUInt32Number OutputFormat,
1132
                                                   cmsUInt32Number dwFlags)
1133
303k
{
1134
303k
    _cmsTRANSFORM* xform;    
1135
303k
    cmsColorSpaceSignature EntryColorSpace;
1136
303k
    cmsColorSpaceSignature ExitColorSpace;
1137
303k
    cmsPipeline* Lut;
1138
303k
    cmsUInt32Number LastIntent;
1139
1140
    // Safeguard
1141
303k
    if (nProfiles <= 0 || nProfiles > 255) {
1142
0
        cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
1143
0
        return NULL;
1144
0
    }
1145
1146
303k
    LastIntent = Intents[nProfiles - 1];
1147
1148
    // If it is a fake transform
1149
303k
    if (dwFlags & cmsFLAGS_NULLTRANSFORM)
1150
0
    {
1151
0
        return AllocEmptyTransform(ContextID, NULL, INTENT_PERCEPTUAL, &InputFormat, &OutputFormat, &dwFlags);
1152
0
    }
1153
1154
    // If gamut check is requested, make sure we have a gamut profile
1155
303k
    if (dwFlags & cmsFLAGS_GAMUTCHECK) {
1156
0
        if (hGamutProfile == NULL) dwFlags &= ~cmsFLAGS_GAMUTCHECK;
1157
0
    }
1158
1159
303k
    if ((dwFlags & cmsFLAGS_GAMUTCHECK) && (nGamutPCSposition <= 0 || nGamutPCSposition >= nProfiles - 1)) {
1160
0
        cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong gamut PCS position '%d'", nGamutPCSposition);
1161
0
        return NULL;
1162
0
    }
1163
1164
    // On floating point transforms, inhibit cache
1165
303k
    if (_cmsFormatterIsFloat(InputFormat) || _cmsFormatterIsFloat(OutputFormat))
1166
254k
        dwFlags |= cmsFLAGS_NOCACHE;
1167
1168
    // Mark entry/exit spaces
1169
303k
    if (!GetXFormColorSpaces(nProfiles, hProfiles, &EntryColorSpace, &ExitColorSpace)) {
1170
0
        cmsSignalError(ContextID, cmsERROR_NULL, "NULL input profiles on transform");
1171
0
        return NULL;
1172
0
    }
1173
1174
    // Check if proper colorspaces
1175
303k
    if (!IsProperColorSpace(EntryColorSpace, InputFormat)) {
1176
0
        cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong input color space on transform");
1177
0
        return NULL;
1178
0
    }
1179
1180
303k
    if (!IsProperColorSpace(ExitColorSpace, OutputFormat)) {
1181
18
        cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong output color space on transform");
1182
18
        return NULL;
1183
18
    }
1184
1185
    // Check whatever the transform is 16 bits and involves linear RGB in first profile. If so, disable optimizations
1186
303k
    if (EntryColorSpace == cmsSigRgbData && T_BYTES(InputFormat) == 2 && !(dwFlags & cmsFLAGS_NOOPTIMIZE))
1187
0
    {
1188
0
        cmsFloat64Number gamma = cmsDetectRGBProfileGamma(hProfiles[0], 0.1);
1189
1190
0
        if (gamma > 0 && gamma < 1.6)
1191
0
            dwFlags |= cmsFLAGS_NOOPTIMIZE;
1192
0
    }
1193
1194
    // Create a pipeline with all transformations
1195
303k
    Lut = _cmsLinkProfiles(ContextID, nProfiles, Intents, hProfiles, BPC, AdaptationStates, dwFlags);
1196
303k
    if (Lut == NULL) {
1197
0
        cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Couldn't link the profiles");
1198
0
        return NULL;
1199
0
    }
1200
1201
    // Check channel count
1202
303k
    if ((cmsChannelsOfColorSpace(EntryColorSpace) != (cmsInt32Number) cmsPipelineInputChannels(Lut)) ||
1203
303k
        (cmsChannelsOfColorSpace(ExitColorSpace)  != (cmsInt32Number) cmsPipelineOutputChannels(Lut))) {
1204
0
        cmsPipelineFree(Lut);
1205
0
        cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Channel count doesn't match. Profile is corrupted");
1206
0
        return NULL;
1207
0
    }
1208
1209
1210
    // All seems ok
1211
303k
    xform = AllocEmptyTransform(ContextID, Lut, LastIntent, &InputFormat, &OutputFormat, &dwFlags);
1212
303k
    if (xform == NULL) {
1213
0
        return NULL;
1214
0
    }
1215
1216
    // Keep values
1217
303k
    xform ->EntryColorSpace = EntryColorSpace;
1218
303k
    xform ->ExitColorSpace  = ExitColorSpace;
1219
303k
    xform ->RenderingIntent = Intents[nProfiles-1];
1220
1221
    // Take white points
1222
303k
    SetWhitePoint(&xform->EntryWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[0], cmsSigMediaWhitePointTag));
1223
303k
    SetWhitePoint(&xform->ExitWhitePoint,  (cmsCIEXYZ*) cmsReadTag(hProfiles[nProfiles-1], cmsSigMediaWhitePointTag));
1224
   
1225
1226
    // Create a gamut check LUT if requested
1227
303k
    if (hGamutProfile != NULL && (dwFlags & cmsFLAGS_GAMUTCHECK))
1228
0
        xform ->GamutCheck  = _cmsCreateGamutCheckPipeline(ContextID, hProfiles,
1229
0
                                                        BPC, Intents,
1230
0
                                                        AdaptationStates,
1231
0
                                                        nGamutPCSposition,
1232
0
                                                        hGamutProfile);
1233
1234
1235
    // Try to read input and output colorant table
1236
303k
    if (cmsIsTag(hProfiles[0], cmsSigColorantTableTag)) {
1237
1238
        // Input table can only come in this way.
1239
0
        xform ->InputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[0], cmsSigColorantTableTag));
1240
0
    }
1241
1242
    // Output is a little bit more complex.
1243
303k
    if (cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigLinkClass) {
1244
1245
        // This tag may exist only on devicelink profiles.
1246
0
        if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag)) {
1247
1248
            // It may be NULL if error
1249
0
            xform ->OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag));
1250
0
        }
1251
1252
303k
    } else {
1253
1254
303k
        if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableTag)) {
1255
1256
0
            xform -> OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableTag));
1257
0
        }
1258
303k
    }
1259
1260
    // Store the sequence of profiles
1261
303k
    if (dwFlags & cmsFLAGS_KEEP_SEQUENCE) {
1262
0
        xform ->Sequence = _cmsCompileProfileSequence(ContextID, nProfiles, hProfiles);
1263
0
    }
1264
303k
    else
1265
303k
        xform ->Sequence = NULL;
1266
1267
    // If this is a cached transform, init first value, which is zero (16 bits only)
1268
303k
    if (!(dwFlags & cmsFLAGS_NOCACHE)) {
1269
1270
8.13k
        memset(&xform ->Cache.CacheIn, 0, sizeof(xform ->Cache.CacheIn));
1271
1272
8.13k
        if (xform ->GamutCheck != NULL) {
1273
0
            TransformOnePixelWithGamutCheck(xform, xform ->Cache.CacheIn, xform->Cache.CacheOut);
1274
0
        }
1275
8.13k
        else {
1276
1277
8.13k
            xform ->Lut ->Eval16Fn(xform ->Cache.CacheIn, xform->Cache.CacheOut, xform -> Lut->Data);
1278
8.13k
        }
1279
1280
8.13k
    }
1281
1282
303k
    return (cmsHTRANSFORM) xform;
1283
303k
}
1284
1285
// Multiprofile transforms: Gamut check is not available here, as it is unclear from which profile the gamut comes.
1286
cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransformTHR(cmsContext ContextID,
1287
                                                       cmsHPROFILE hProfiles[],
1288
                                                       cmsUInt32Number nProfiles,
1289
                                                       cmsUInt32Number InputFormat,
1290
                                                       cmsUInt32Number OutputFormat,
1291
                                                       cmsUInt32Number Intent,
1292
                                                       cmsUInt32Number dwFlags)
1293
303k
{
1294
303k
    cmsUInt32Number i;
1295
303k
    cmsBool BPC[256];
1296
303k
    cmsUInt32Number Intents[256];
1297
303k
    cmsFloat64Number AdaptationStates[256];
1298
1299
303k
    if (nProfiles <= 0 || nProfiles > 255) {
1300
0
         cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
1301
0
        return NULL;
1302
0
    }
1303
1304
911k
    for (i=0; i < nProfiles; i++) {
1305
607k
        BPC[i] = dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION ? TRUE : FALSE;
1306
607k
        Intents[i] = Intent;
1307
607k
        AdaptationStates[i] = cmsSetAdaptationStateTHR(ContextID, -1);
1308
607k
    }
1309
1310
1311
303k
    return cmsCreateExtendedTransform(ContextID, nProfiles, hProfiles, BPC, Intents, AdaptationStates, NULL, 0, InputFormat, OutputFormat, dwFlags);
1312
303k
}
1313
1314
1315
1316
cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles[],
1317
                                                  cmsUInt32Number nProfiles,
1318
                                                  cmsUInt32Number InputFormat,
1319
                                                  cmsUInt32Number OutputFormat,
1320
                                                  cmsUInt32Number Intent,
1321
                                                  cmsUInt32Number dwFlags)
1322
0
{
1323
1324
0
    if (nProfiles <= 0 || nProfiles > 255) {
1325
0
         cmsSignalError(NULL, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
1326
0
         return NULL;
1327
0
    }
1328
1329
0
    return cmsCreateMultiprofileTransformTHR(cmsGetProfileContextID(hProfiles[0]),
1330
0
                                                  hProfiles,
1331
0
                                                  nProfiles,
1332
0
                                                  InputFormat,
1333
0
                                                  OutputFormat,
1334
0
                                                  Intent,
1335
0
                                                  dwFlags);
1336
0
}
1337
1338
cmsHTRANSFORM CMSEXPORT cmsCreateTransformTHR(cmsContext ContextID,
1339
                                              cmsHPROFILE Input,
1340
                                              cmsUInt32Number InputFormat,
1341
                                              cmsHPROFILE Output,
1342
                                              cmsUInt32Number OutputFormat,
1343
                                              cmsUInt32Number Intent,
1344
                                              cmsUInt32Number dwFlags)
1345
303k
{
1346
1347
303k
    cmsHPROFILE hArray[2];
1348
1349
303k
    hArray[0] = Input;
1350
303k
    hArray[1] = Output;
1351
1352
303k
    return cmsCreateMultiprofileTransformTHR(ContextID, hArray, Output == NULL ? 1U : 2U, InputFormat, OutputFormat, Intent, dwFlags);
1353
303k
}
1354
1355
CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateTransform(cmsHPROFILE Input,
1356
                                                  cmsUInt32Number InputFormat,
1357
                                                  cmsHPROFILE Output,
1358
                                                  cmsUInt32Number OutputFormat,
1359
                                                  cmsUInt32Number Intent,
1360
                                                  cmsUInt32Number dwFlags)
1361
131k
{
1362
131k
    return cmsCreateTransformTHR(cmsGetProfileContextID(Input), Input, InputFormat, Output, OutputFormat, Intent, dwFlags);
1363
131k
}
1364
1365
1366
cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransformTHR(cmsContext ContextID,
1367
                                                   cmsHPROFILE InputProfile,
1368
                                                   cmsUInt32Number InputFormat,
1369
                                                   cmsHPROFILE OutputProfile,
1370
                                                   cmsUInt32Number OutputFormat,
1371
                                                   cmsHPROFILE ProofingProfile,
1372
                                                   cmsUInt32Number nIntent,
1373
                                                   cmsUInt32Number ProofingIntent,
1374
                                                   cmsUInt32Number dwFlags)
1375
0
{
1376
0
    cmsHPROFILE hArray[4];
1377
0
    cmsUInt32Number Intents[4];
1378
0
    cmsBool  BPC[4];
1379
0
    cmsFloat64Number Adaptation[4];
1380
0
    cmsBool  DoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION) ? TRUE : FALSE;
1381
1382
1383
0
    hArray[0]  = InputProfile; hArray[1] = ProofingProfile; hArray[2]  = ProofingProfile;               hArray[3] = OutputProfile;
1384
0
    Intents[0] = nIntent;      Intents[1] = nIntent;        Intents[2] = INTENT_RELATIVE_COLORIMETRIC;  Intents[3] = ProofingIntent;
1385
0
    BPC[0]     = DoBPC;        BPC[1] = DoBPC;              BPC[2] = 0;                                 BPC[3] = 0;
1386
1387
0
    Adaptation[0] = Adaptation[1] = Adaptation[2] = Adaptation[3] = cmsSetAdaptationStateTHR(ContextID, -1);
1388
1389
0
    if (!(dwFlags & (cmsFLAGS_SOFTPROOFING|cmsFLAGS_GAMUTCHECK)))
1390
0
        return cmsCreateTransformTHR(ContextID, InputProfile, InputFormat, OutputProfile, OutputFormat, nIntent, dwFlags);
1391
1392
0
    return cmsCreateExtendedTransform(ContextID, 4, hArray, BPC, Intents, Adaptation,
1393
0
                                        ProofingProfile, 1, InputFormat, OutputFormat, dwFlags);
1394
1395
0
}
1396
1397
1398
cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransform(cmsHPROFILE InputProfile,
1399
                                                   cmsUInt32Number InputFormat,
1400
                                                   cmsHPROFILE OutputProfile,
1401
                                                   cmsUInt32Number OutputFormat,
1402
                                                   cmsHPROFILE ProofingProfile,
1403
                                                   cmsUInt32Number nIntent,
1404
                                                   cmsUInt32Number ProofingIntent,
1405
                                                   cmsUInt32Number dwFlags)
1406
0
{
1407
0
    return cmsCreateProofingTransformTHR(cmsGetProfileContextID(InputProfile),
1408
0
                                                   InputProfile,
1409
0
                                                   InputFormat,
1410
0
                                                   OutputProfile,
1411
0
                                                   OutputFormat,
1412
0
                                                   ProofingProfile,
1413
0
                                                   nIntent,
1414
0
                                                   ProofingIntent,
1415
0
                                                   dwFlags);
1416
0
}
1417
1418
1419
// Grab the ContextID from an open transform. Returns NULL if a NULL transform is passed
1420
cmsContext CMSEXPORT cmsGetTransformContextID(cmsHTRANSFORM hTransform)
1421
0
{
1422
0
    _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1423
1424
0
    if (xform == NULL) return NULL;
1425
0
    return xform -> ContextID;
1426
0
}
1427
1428
// Grab the input/output formats
1429
cmsUInt32Number CMSEXPORT cmsGetTransformInputFormat(cmsHTRANSFORM hTransform)
1430
0
{
1431
0
    _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1432
1433
0
    if (xform == NULL) return 0;
1434
0
    return xform->InputFormat;
1435
0
}
1436
1437
cmsUInt32Number CMSEXPORT cmsGetTransformOutputFormat(cmsHTRANSFORM hTransform)
1438
0
{
1439
0
    _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1440
1441
0
    if (xform == NULL) return 0;
1442
0
    return xform->OutputFormat;
1443
0
}
1444
1445
// For backwards compatibility
1446
cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform,
1447
                                         cmsUInt32Number InputFormat,
1448
                                         cmsUInt32Number OutputFormat)
1449
0
{
1450
0
    _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1451
0
    cmsFormatter16 FromInput, ToOutput;
1452
1453
1454
    // We only can afford to change formatters if previous transform is at least 16 bits
1455
0
    if (!(xform ->dwOriginalFlags & cmsFLAGS_CAN_CHANGE_FORMATTER)) {
1456
1457
0
        cmsSignalError(xform ->ContextID, cmsERROR_NOT_SUITABLE, "cmsChangeBuffersFormat works only on transforms created originally with at least 16 bits of precision");
1458
0
        return FALSE;
1459
0
    }
1460
1461
0
    FromInput = _cmsGetFormatter(xform->ContextID, InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
1462
0
    ToOutput  = _cmsGetFormatter(xform->ContextID, OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
1463
1464
0
    if (FromInput == NULL || ToOutput == NULL) {
1465
1466
0
        cmsSignalError(xform -> ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
1467
0
        return FALSE;
1468
0
    }
1469
1470
0
    xform ->InputFormat  = InputFormat;
1471
0
    xform ->OutputFormat = OutputFormat;
1472
0
    xform ->FromInput    = FromInput;
1473
0
    xform ->ToOutput     = ToOutput;
1474
0
    return TRUE;
1475
0
}