Coverage Report

Created: 2025-07-23 08:18

/src/Little-CMS/src/cmsxform.c
Line
Count
Source (jump to first uncovered line)
1
//---------------------------------------------------------------------------------
2
//
3
//  Little Color Management System
4
//  Copyright (c) 1998-2024 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
0
{
59
0
    cmsFloat64Number prev;
60
0
    _cmsAdaptationStateChunkType* ptr = (_cmsAdaptationStateChunkType*) _cmsContextGetClientChunk(ContextID, AdaptationStateContext);
61
62
    // Get previous value for return
63
0
    prev = ptr ->AdaptationState;
64
65
    // Set the value if d is positive or zero
66
0
    if (d >= 0.0) {
67
68
0
        ptr ->AdaptationState = d;
69
0
    }
70
71
    // Always return previous value
72
0
    return prev;
73
0
}
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
0
{
149
0
    _cmsTRANSFORM* p = (_cmsTRANSFORM*) hTransform;
150
151
0
    _cmsAssert(p != NULL);
152
153
0
    if (p -> GamutCheck)
154
0
        cmsPipelineFree(p -> GamutCheck);
155
156
0
    if (p -> Lut)
157
0
        cmsPipelineFree(p -> Lut);
158
159
0
    if (p ->InputColorant)
160
0
        cmsFreeNamedColorList(p ->InputColorant);
161
162
0
    if (p -> OutputColorant)
163
0
        cmsFreeNamedColorList(p ->OutputColorant);
164
165
0
    if (p ->Sequence)
166
0
        cmsFreeProfileSequenceDescription(p ->Sequence);
167
168
0
    if (p ->UserData)
169
0
        p ->FreeUserData(p ->ContextID, p ->UserData);
170
171
0
    _cmsFree(p ->ContextID, (void *) p);
172
0
}
173
174
175
static
176
cmsUInt32Number PixelSize(cmsUInt32Number Format)
177
0
{
178
0
    cmsUInt32Number fmt_bytes = T_BYTES(Format);
179
180
    // For double, the T_BYTES field is zero
181
0
    if (fmt_bytes == 0)
182
0
        return sizeof(cmsUInt64Number);
183
184
    // Otherwise, it is already correct for all formats
185
0
    return fmt_bytes;
186
0
}
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
0
{
198
0
    _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
199
0
    cmsStride stride;
200
201
0
    stride.BytesPerLineIn = 0;  // Not used
202
0
    stride.BytesPerLineOut = 0;
203
0
    stride.BytesPerPlaneIn = Size * PixelSize(p->InputFormat);
204
0
    stride.BytesPerPlaneOut = Size * PixelSize(p->OutputFormat);
205
           
206
0
    p -> xform(p, InputBuffer, OutputBuffer, Size, 1, &stride);
207
0
}
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
0
{
265
0
    cmsUInt8Number* accum;
266
0
    cmsUInt8Number* output;
267
0
    cmsFloat32Number fIn[cmsMAXCHANNELS], fOut[cmsMAXCHANNELS];
268
0
    cmsFloat32Number OutOfGamut;
269
0
    size_t i, j, c, strideIn, strideOut;
270
271
0
    _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
272
273
0
    strideIn = 0;
274
0
    strideOut = 0;
275
0
    memset(fIn, 0, sizeof(fIn));
276
0
    memset(fOut, 0, sizeof(fOut));
277
278
0
    for (i = 0; i < LineCount; i++) {
279
280
0
        accum = (cmsUInt8Number*)in + strideIn;
281
0
        output = (cmsUInt8Number*)out + strideOut;
282
283
0
        for (j = 0; j < PixelsPerLine; j++) {
284
285
0
            accum = p->FromInputFloat(p, fIn, accum, Stride->BytesPerPlaneIn);
286
287
            // Any gamut check to do?
288
0
            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
0
            else {
309
310
                // No gamut check at all
311
0
                cmsPipelineEvalFloat(fIn, fOut, p->Lut);
312
0
            }
313
314
315
0
            output = p->ToOutputFloat(p, fOut, output, Stride->BytesPerPlaneOut);
316
0
        }
317
318
0
        strideIn += Stride->BytesPerLineIn;
319
0
        strideOut += Stride->BytesPerLineOut;
320
0
    }
321
322
0
}
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
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
0
{
512
0
    cmsUInt8Number* accum;
513
0
    cmsUInt8Number* output;
514
0
    cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
515
0
    _cmsCACHE Cache;
516
0
    size_t i, j, strideIn, strideOut;
517
518
0
    _cmsHandleExtraChannels(p, in, out, PixelsPerLine, LineCount, Stride);
519
520
    // Empty buffers for quick memcmp
521
0
    memset(wIn, 0, sizeof(wIn));
522
0
    memset(wOut, 0, sizeof(wOut));
523
524
    // Get copy of zero cache
525
0
    memcpy(&Cache, &p->Cache, sizeof(Cache));
526
527
0
    strideIn = 0;
528
0
    strideOut = 0;
529
530
0
    for (i = 0; i < LineCount; i++) {
531
532
0
        accum = (cmsUInt8Number*)in + strideIn;
533
0
        output = (cmsUInt8Number*)out + strideOut;
534
535
0
        for (j = 0; j < PixelsPerLine; j++) {
536
537
0
            accum = p->FromInput(p, wIn, accum, Stride->BytesPerPlaneIn);
538
539
0
            if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) {
540
541
0
                memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut));
542
0
            }
543
0
            else {
544
0
                p->Lut->Eval16Fn(wIn, wOut, p->Lut->Data);
545
546
0
                memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn));
547
0
                memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut));
548
0
            }
549
550
0
            output = p->ToOutput(p, wOut, output, Stride->BytesPerPlaneOut);
551
0
        }
552
553
0
        strideIn += Stride->BytesPerLineIn;
554
0
        strideOut += Stride->BytesPerLineOut;
555
0
    }
556
0
}
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
0
{
811
0
    _cmsParallelizationPluginChunkType* ctx = (_cmsParallelizationPluginChunkType*)_cmsContextGetClientChunk(p->ContextID, ParallelizationPlugin);
812
813
0
    _cmsAssert(p != NULL);
814
0
    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
0
}
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
0
{
859
0
     _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID, TransformPlugin);
860
0
     _cmsTransformCollection* Plugin;
861
862
       // Allocate needed memory
863
0
       _cmsTRANSFORM* p = (_cmsTRANSFORM*)_cmsMallocZero(ContextID, sizeof(_cmsTRANSFORM));
864
0
       if (!p) {
865
0
              cmsPipelineFree(lut);
866
0
              return NULL;
867
0
       }
868
869
       // Store the proposed pipeline
870
0
       p->Lut = lut;
871
872
       // Let's see if any plug-in want to do the transform by itself
873
0
       if (p->Lut != NULL) {
874
875
0
           if (!(*dwFlags & cmsFLAGS_NOOPTIMIZE))
876
0
           {
877
0
               for (Plugin = ctx->TransformCollection;
878
0
                   Plugin != NULL;
879
0
                   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
0
           }
913
914
           // Not suitable for the transform plug-in, let's check  the pipeline plug-in
915
0
           _cmsOptimizePipeline(ContextID, &p->Lut, Intent, InputFormat, OutputFormat, dwFlags);
916
0
       }
917
918
    // Check whatever this is a true floating point transform
919
0
    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
0
        p ->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
923
0
        p ->ToOutputFloat  = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
924
0
        *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
925
926
0
        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
0
        if (*dwFlags & cmsFLAGS_NULLTRANSFORM) {
934
935
0
            p ->xform = NullFloatXFORM;
936
0
        }
937
0
        else {
938
            // Float transforms don't use cache, always are non-NULL
939
0
            p ->xform = FloatXFORM;
940
0
        }
941
942
0
    }
943
0
    else {
944
945
        // Formats are intended to be changed before use
946
0
        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
0
        else {
952
953
0
            cmsUInt32Number BytesPerPixelInput;
954
955
0
            p ->FromInput = _cmsGetFormatter(ContextID, *InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
956
0
            p ->ToOutput  = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
957
958
0
            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
0
            BytesPerPixelInput = T_BYTES(*InputFormat);
966
0
            if (BytesPerPixelInput == 0 || BytesPerPixelInput >= 2)
967
0
                   *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
968
969
0
        }
970
971
0
        if (*dwFlags & cmsFLAGS_NULLTRANSFORM) {
972
973
0
            p ->xform = NullXFORM;
974
0
        }
975
0
        else {
976
0
            if (*dwFlags & cmsFLAGS_NOCACHE) {
977
978
0
                if (*dwFlags & cmsFLAGS_GAMUTCHECK)
979
0
                    p ->xform = PrecalculatedXFORMGamutCheck;  // Gamut check, no cache
980
0
                else
981
0
                    p ->xform = PrecalculatedXFORM;  // No cache, no gamut check
982
0
            }
983
0
            else {
984
985
0
                if (*dwFlags & cmsFLAGS_GAMUTCHECK)
986
0
                    p ->xform = CachedXFORMGamutCheck;    // Gamut check, cache
987
0
                else
988
0
                    p ->xform = CachedXFORM;  // No gamut check, cache
989
990
0
            }
991
0
        }
992
0
    }
993
994
    /**
995
    * Check consistency for alpha channel copy
996
    */
997
0
    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
0
    p ->InputFormat     = *InputFormat;
1008
0
    p ->OutputFormat    = *OutputFormat;
1009
0
    p ->dwOriginalFlags = *dwFlags;
1010
0
    p ->ContextID       = ContextID;
1011
0
    p ->UserData        = NULL;
1012
0
    ParalellizeIfSuitable(p);
1013
0
    return p;
1014
0
}
1015
1016
static
1017
cmsBool GetXFormColorSpaces(cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[], cmsColorSpaceSignature* Input, cmsColorSpaceSignature* Output)
1018
0
{
1019
0
    cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut;
1020
0
    cmsColorSpaceSignature PostColorSpace;
1021
0
    cmsUInt32Number i;
1022
1023
0
    if (nProfiles == 0) return FALSE;
1024
0
    if (hProfiles[0] == NULL) return FALSE;
1025
1026
0
    *Input = PostColorSpace = cmsGetColorSpace(hProfiles[0]);
1027
1028
0
    for (i=0; i < nProfiles; i++) {
1029
1030
0
        cmsProfileClassSignature cls;
1031
0
        cmsHPROFILE hProfile = hProfiles[i];
1032
1033
0
        int lIsInput = (PostColorSpace != cmsSigXYZData) &&
1034
0
                       (PostColorSpace != cmsSigLabData);
1035
1036
0
        if (hProfile == NULL) return FALSE;
1037
1038
0
        cls = cmsGetDeviceClass(hProfile);
1039
1040
0
        if (cls == cmsSigNamedColorClass) {
1041
1042
0
            ColorSpaceIn    = cmsSig1colorData;
1043
0
            ColorSpaceOut   = (nProfiles > 1) ? cmsGetPCS(hProfile) : cmsGetColorSpace(hProfile);
1044
0
        }
1045
0
        else
1046
0
        if (lIsInput || (cls == cmsSigLinkClass)) {
1047
1048
0
            ColorSpaceIn    = cmsGetColorSpace(hProfile);
1049
0
            ColorSpaceOut   = cmsGetPCS(hProfile);
1050
0
        }
1051
0
        else
1052
0
        {
1053
0
            ColorSpaceIn    = cmsGetPCS(hProfile);
1054
0
            ColorSpaceOut   = cmsGetColorSpace(hProfile);
1055
0
        }
1056
1057
0
        if (i==0)
1058
0
            *Input = ColorSpaceIn;
1059
1060
0
        PostColorSpace = ColorSpaceOut;
1061
0
    }
1062
1063
0
    *Output = PostColorSpace;
1064
1065
0
    return TRUE;
1066
0
}
1067
1068
// Check colorspace
1069
static
1070
cmsBool  IsProperColorSpace(cmsColorSpaceSignature Check, cmsUInt32Number dwFormat)
1071
0
{
1072
0
    int Space1 = (int) T_COLORSPACE(dwFormat);
1073
0
    int Space2 = _cmsLCMScolorSpace(Check);
1074
1075
0
    if (dwFormat == 0) return TRUE; // Bypass used by linkicc
1076
1077
0
    if (Space1 == PT_ANY) return (T_CHANNELS(dwFormat) == cmsChannelsOf(Check));
1078
0
    if (Space1 == Space2) return TRUE;
1079
1080
0
    if (Space1 == PT_LabV2 && Space2 == PT_Lab) return TRUE;
1081
0
    if (Space1 == PT_Lab   && Space2 == PT_LabV2) return TRUE;
1082
1083
0
    return FALSE;
1084
0
}
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
0
{
1094
0
    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
0
}
1103
1104
static
1105
void SetWhitePoint(cmsCIEXYZ* wtPt, const cmsCIEXYZ* src)
1106
0
{
1107
0
    if (src == NULL) {
1108
0
        wtPt ->X = cmsD50X;
1109
0
        wtPt ->Y = cmsD50Y;
1110
0
        wtPt ->Z = cmsD50Z;
1111
0
    }
1112
0
    else {
1113
0
        wtPt ->X = src->X;
1114
0
        wtPt ->Y = src->Y;
1115
0
        wtPt ->Z = src->Z;
1116
1117
0
        NormalizeXYZ(wtPt);
1118
0
    }
1119
1120
0
}
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
0
{
1134
0
    _cmsTRANSFORM* xform;    
1135
0
    cmsColorSpaceSignature EntryColorSpace;
1136
0
    cmsColorSpaceSignature ExitColorSpace;
1137
0
    cmsPipeline* Lut;
1138
0
    cmsUInt32Number LastIntent;
1139
1140
    // Safeguard
1141
0
    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
0
    LastIntent = Intents[nProfiles - 1];
1147
1148
    // If it is a fake transform
1149
0
    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
0
    if (dwFlags & cmsFLAGS_GAMUTCHECK) {
1156
0
        if (hGamutProfile == NULL) dwFlags &= ~cmsFLAGS_GAMUTCHECK;
1157
0
    }
1158
1159
    // On floating point transforms, inhibit cache
1160
0
    if (_cmsFormatterIsFloat(InputFormat) || _cmsFormatterIsFloat(OutputFormat))
1161
0
        dwFlags |= cmsFLAGS_NOCACHE;
1162
1163
    // Mark entry/exit spaces
1164
0
    if (!GetXFormColorSpaces(nProfiles, hProfiles, &EntryColorSpace, &ExitColorSpace)) {
1165
0
        cmsSignalError(ContextID, cmsERROR_NULL, "NULL input profiles on transform");
1166
0
        return NULL;
1167
0
    }
1168
1169
    // Check if proper colorspaces
1170
0
    if (!IsProperColorSpace(EntryColorSpace, InputFormat)) {
1171
0
        cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong input color space on transform");
1172
0
        return NULL;
1173
0
    }
1174
1175
0
    if (!IsProperColorSpace(ExitColorSpace, OutputFormat)) {
1176
0
        cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong output color space on transform");
1177
0
        return NULL;
1178
0
    }
1179
1180
    // Check whatever the transform is 16 bits and involves linear RGB in first profile. If so, disable optimizations
1181
0
    if (EntryColorSpace == cmsSigRgbData && T_BYTES(InputFormat) == 2 && !(dwFlags & cmsFLAGS_NOOPTIMIZE))
1182
0
    {
1183
0
        cmsFloat64Number gamma = cmsDetectRGBProfileGamma(hProfiles[0], 0.1);
1184
1185
0
        if (gamma > 0 && gamma < 1.6)
1186
0
            dwFlags |= cmsFLAGS_NOOPTIMIZE;
1187
0
    }
1188
1189
    // Create a pipeline with all transformations
1190
0
    Lut = _cmsLinkProfiles(ContextID, nProfiles, Intents, hProfiles, BPC, AdaptationStates, dwFlags);
1191
0
    if (Lut == NULL) {
1192
0
        cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Couldn't link the profiles");
1193
0
        return NULL;
1194
0
    }
1195
1196
    // Check channel count
1197
0
    if ((cmsChannelsOfColorSpace(EntryColorSpace) != (cmsInt32Number) cmsPipelineInputChannels(Lut)) ||
1198
0
        (cmsChannelsOfColorSpace(ExitColorSpace)  != (cmsInt32Number) cmsPipelineOutputChannels(Lut))) {
1199
0
        cmsPipelineFree(Lut);
1200
0
        cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Channel count doesn't match. Profile is corrupted");
1201
0
        return NULL;
1202
0
    }
1203
1204
1205
    // All seems ok
1206
0
    xform = AllocEmptyTransform(ContextID, Lut, LastIntent, &InputFormat, &OutputFormat, &dwFlags);
1207
0
    if (xform == NULL) {
1208
0
        return NULL;
1209
0
    }
1210
1211
    // Keep values
1212
0
    xform ->EntryColorSpace = EntryColorSpace;
1213
0
    xform ->ExitColorSpace  = ExitColorSpace;
1214
0
    xform ->RenderingIntent = Intents[nProfiles-1];
1215
1216
    // Take white points
1217
0
    SetWhitePoint(&xform->EntryWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[0], cmsSigMediaWhitePointTag));
1218
0
    SetWhitePoint(&xform->ExitWhitePoint,  (cmsCIEXYZ*) cmsReadTag(hProfiles[nProfiles-1], cmsSigMediaWhitePointTag));
1219
   
1220
1221
    // Create a gamut check LUT if requested
1222
0
    if (hGamutProfile != NULL && (dwFlags & cmsFLAGS_GAMUTCHECK))
1223
0
        xform ->GamutCheck  = _cmsCreateGamutCheckPipeline(ContextID, hProfiles,
1224
0
                                                        BPC, Intents,
1225
0
                                                        AdaptationStates,
1226
0
                                                        nGamutPCSposition,
1227
0
                                                        hGamutProfile);
1228
1229
1230
    // Try to read input and output colorant table
1231
0
    if (cmsIsTag(hProfiles[0], cmsSigColorantTableTag)) {
1232
1233
        // Input table can only come in this way.
1234
0
        xform ->InputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[0], cmsSigColorantTableTag));
1235
0
    }
1236
1237
    // Output is a little bit more complex.
1238
0
    if (cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigLinkClass) {
1239
1240
        // This tag may exist only on devicelink profiles.
1241
0
        if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag)) {
1242
1243
            // It may be NULL if error
1244
0
            xform ->OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag));
1245
0
        }
1246
1247
0
    } else {
1248
1249
0
        if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableTag)) {
1250
1251
0
            xform -> OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableTag));
1252
0
        }
1253
0
    }
1254
1255
    // Store the sequence of profiles
1256
0
    if (dwFlags & cmsFLAGS_KEEP_SEQUENCE) {
1257
0
        xform ->Sequence = _cmsCompileProfileSequence(ContextID, nProfiles, hProfiles);
1258
0
    }
1259
0
    else
1260
0
        xform ->Sequence = NULL;
1261
1262
    // If this is a cached transform, init first value, which is zero (16 bits only)
1263
0
    if (!(dwFlags & cmsFLAGS_NOCACHE)) {
1264
1265
0
        memset(&xform ->Cache.CacheIn, 0, sizeof(xform ->Cache.CacheIn));
1266
1267
0
        if (xform ->GamutCheck != NULL) {
1268
0
            TransformOnePixelWithGamutCheck(xform, xform ->Cache.CacheIn, xform->Cache.CacheOut);
1269
0
        }
1270
0
        else {
1271
1272
0
            xform ->Lut ->Eval16Fn(xform ->Cache.CacheIn, xform->Cache.CacheOut, xform -> Lut->Data);
1273
0
        }
1274
1275
0
    }
1276
1277
0
    return (cmsHTRANSFORM) xform;
1278
0
}
1279
1280
// Multiprofile transforms: Gamut check is not available here, as it is unclear from which profile the gamut comes.
1281
cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransformTHR(cmsContext ContextID,
1282
                                                       cmsHPROFILE hProfiles[],
1283
                                                       cmsUInt32Number nProfiles,
1284
                                                       cmsUInt32Number InputFormat,
1285
                                                       cmsUInt32Number OutputFormat,
1286
                                                       cmsUInt32Number Intent,
1287
                                                       cmsUInt32Number dwFlags)
1288
0
{
1289
0
    cmsUInt32Number i;
1290
0
    cmsBool BPC[256];
1291
0
    cmsUInt32Number Intents[256];
1292
0
    cmsFloat64Number AdaptationStates[256];
1293
1294
0
    if (nProfiles <= 0 || nProfiles > 255) {
1295
0
         cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
1296
0
        return NULL;
1297
0
    }
1298
1299
0
    for (i=0; i < nProfiles; i++) {
1300
0
        BPC[i] = dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION ? TRUE : FALSE;
1301
0
        Intents[i] = Intent;
1302
0
        AdaptationStates[i] = cmsSetAdaptationStateTHR(ContextID, -1);
1303
0
    }
1304
1305
1306
0
    return cmsCreateExtendedTransform(ContextID, nProfiles, hProfiles, BPC, Intents, AdaptationStates, NULL, 0, InputFormat, OutputFormat, dwFlags);
1307
0
}
1308
1309
1310
1311
cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles[],
1312
                                                  cmsUInt32Number nProfiles,
1313
                                                  cmsUInt32Number InputFormat,
1314
                                                  cmsUInt32Number OutputFormat,
1315
                                                  cmsUInt32Number Intent,
1316
                                                  cmsUInt32Number dwFlags)
1317
0
{
1318
1319
0
    if (nProfiles <= 0 || nProfiles > 255) {
1320
0
         cmsSignalError(NULL, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
1321
0
         return NULL;
1322
0
    }
1323
1324
0
    return cmsCreateMultiprofileTransformTHR(cmsGetProfileContextID(hProfiles[0]),
1325
0
                                                  hProfiles,
1326
0
                                                  nProfiles,
1327
0
                                                  InputFormat,
1328
0
                                                  OutputFormat,
1329
0
                                                  Intent,
1330
0
                                                  dwFlags);
1331
0
}
1332
1333
cmsHTRANSFORM CMSEXPORT cmsCreateTransformTHR(cmsContext ContextID,
1334
                                              cmsHPROFILE Input,
1335
                                              cmsUInt32Number InputFormat,
1336
                                              cmsHPROFILE Output,
1337
                                              cmsUInt32Number OutputFormat,
1338
                                              cmsUInt32Number Intent,
1339
                                              cmsUInt32Number dwFlags)
1340
0
{
1341
1342
0
    cmsHPROFILE hArray[2];
1343
1344
0
    hArray[0] = Input;
1345
0
    hArray[1] = Output;
1346
1347
0
    return cmsCreateMultiprofileTransformTHR(ContextID, hArray, Output == NULL ? 1U : 2U, InputFormat, OutputFormat, Intent, dwFlags);
1348
0
}
1349
1350
CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateTransform(cmsHPROFILE Input,
1351
                                                  cmsUInt32Number InputFormat,
1352
                                                  cmsHPROFILE Output,
1353
                                                  cmsUInt32Number OutputFormat,
1354
                                                  cmsUInt32Number Intent,
1355
                                                  cmsUInt32Number dwFlags)
1356
0
{
1357
0
    return cmsCreateTransformTHR(cmsGetProfileContextID(Input), Input, InputFormat, Output, OutputFormat, Intent, dwFlags);
1358
0
}
1359
1360
1361
cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransformTHR(cmsContext ContextID,
1362
                                                   cmsHPROFILE InputProfile,
1363
                                                   cmsUInt32Number InputFormat,
1364
                                                   cmsHPROFILE OutputProfile,
1365
                                                   cmsUInt32Number OutputFormat,
1366
                                                   cmsHPROFILE ProofingProfile,
1367
                                                   cmsUInt32Number nIntent,
1368
                                                   cmsUInt32Number ProofingIntent,
1369
                                                   cmsUInt32Number dwFlags)
1370
0
{
1371
0
    cmsHPROFILE hArray[4];
1372
0
    cmsUInt32Number Intents[4];
1373
0
    cmsBool  BPC[4];
1374
0
    cmsFloat64Number Adaptation[4];
1375
0
    cmsBool  DoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION) ? TRUE : FALSE;
1376
1377
1378
0
    hArray[0]  = InputProfile; hArray[1] = ProofingProfile; hArray[2]  = ProofingProfile;               hArray[3] = OutputProfile;
1379
0
    Intents[0] = nIntent;      Intents[1] = nIntent;        Intents[2] = INTENT_RELATIVE_COLORIMETRIC;  Intents[3] = ProofingIntent;
1380
0
    BPC[0]     = DoBPC;        BPC[1] = DoBPC;              BPC[2] = 0;                                 BPC[3] = 0;
1381
1382
0
    Adaptation[0] = Adaptation[1] = Adaptation[2] = Adaptation[3] = cmsSetAdaptationStateTHR(ContextID, -1);
1383
1384
0
    if (!(dwFlags & (cmsFLAGS_SOFTPROOFING|cmsFLAGS_GAMUTCHECK)))
1385
0
        return cmsCreateTransformTHR(ContextID, InputProfile, InputFormat, OutputProfile, OutputFormat, nIntent, dwFlags);
1386
1387
0
    return cmsCreateExtendedTransform(ContextID, 4, hArray, BPC, Intents, Adaptation,
1388
0
                                        ProofingProfile, 1, InputFormat, OutputFormat, dwFlags);
1389
1390
0
}
1391
1392
1393
cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransform(cmsHPROFILE InputProfile,
1394
                                                   cmsUInt32Number InputFormat,
1395
                                                   cmsHPROFILE OutputProfile,
1396
                                                   cmsUInt32Number OutputFormat,
1397
                                                   cmsHPROFILE ProofingProfile,
1398
                                                   cmsUInt32Number nIntent,
1399
                                                   cmsUInt32Number ProofingIntent,
1400
                                                   cmsUInt32Number dwFlags)
1401
0
{
1402
0
    return cmsCreateProofingTransformTHR(cmsGetProfileContextID(InputProfile),
1403
0
                                                   InputProfile,
1404
0
                                                   InputFormat,
1405
0
                                                   OutputProfile,
1406
0
                                                   OutputFormat,
1407
0
                                                   ProofingProfile,
1408
0
                                                   nIntent,
1409
0
                                                   ProofingIntent,
1410
0
                                                   dwFlags);
1411
0
}
1412
1413
1414
// Grab the ContextID from an open transform. Returns NULL if a NULL transform is passed
1415
cmsContext CMSEXPORT cmsGetTransformContextID(cmsHTRANSFORM hTransform)
1416
0
{
1417
0
    _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1418
1419
0
    if (xform == NULL) return NULL;
1420
0
    return xform -> ContextID;
1421
0
}
1422
1423
// Grab the input/output formats
1424
cmsUInt32Number CMSEXPORT cmsGetTransformInputFormat(cmsHTRANSFORM hTransform)
1425
0
{
1426
0
    _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1427
1428
0
    if (xform == NULL) return 0;
1429
0
    return xform->InputFormat;
1430
0
}
1431
1432
cmsUInt32Number CMSEXPORT cmsGetTransformOutputFormat(cmsHTRANSFORM hTransform)
1433
0
{
1434
0
    _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1435
1436
0
    if (xform == NULL) return 0;
1437
0
    return xform->OutputFormat;
1438
0
}
1439
1440
// For backwards compatibility
1441
cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform,
1442
                                         cmsUInt32Number InputFormat,
1443
                                         cmsUInt32Number OutputFormat)
1444
0
{
1445
0
    _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1446
0
    cmsFormatter16 FromInput, ToOutput;
1447
1448
1449
    // We only can afford to change formatters if previous transform is at least 16 bits
1450
0
    if (!(xform ->dwOriginalFlags & cmsFLAGS_CAN_CHANGE_FORMATTER)) {
1451
1452
0
        cmsSignalError(xform ->ContextID, cmsERROR_NOT_SUITABLE, "cmsChangeBuffersFormat works only on transforms created originally with at least 16 bits of precision");
1453
0
        return FALSE;
1454
0
    }
1455
1456
0
    FromInput = _cmsGetFormatter(xform->ContextID, InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
1457
0
    ToOutput  = _cmsGetFormatter(xform->ContextID, OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
1458
1459
0
    if (FromInput == NULL || ToOutput == NULL) {
1460
1461
0
        cmsSignalError(xform -> ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
1462
0
        return FALSE;
1463
0
    }
1464
1465
0
    xform ->InputFormat  = InputFormat;
1466
0
    xform ->OutputFormat = OutputFormat;
1467
0
    xform ->FromInput    = FromInput;
1468
0
    xform ->ToOutput     = ToOutput;
1469
0
    return TRUE;
1470
0
}