Merge pull request #542 from mayeut/lcms2-2.6
[openjpeg.git] / thirdparty / liblcms2 / src / cmsxform.c
1 //---------------------------------------------------------------------------------
2 //
3 //  Little Color Management System
4 //  Copyright (c) 1998-2014 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 #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 {
41     static _cmsAdaptationStateChunkType AdaptationStateChunk = { DEFAULT_OBSERVER_ADAPTATION_STATE };
42     void* from;
43      
44     if (src != NULL) {
45         from = src ->chunks[AdaptationStateContext];       
46     }
47     else {
48        from = &AdaptationStateChunk;
49     }
50     
51     ctx ->chunks[AdaptationStateContext] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsAdaptationStateChunkType));     
52 }
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 {
59     cmsFloat64Number prev;
60     _cmsAdaptationStateChunkType* ptr = (_cmsAdaptationStateChunkType*) _cmsContextGetClientChunk(ContextID, AdaptationStateContext);
61
62     // Get previous value for return
63     prev = ptr ->AdaptationState;
64
65     // Set the value if d is positive or zero
66     if (d >= 0.0) {
67
68         ptr ->AdaptationState = d;
69     }
70
71     // Always return previous value
72     return prev;
73 }
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 {    
79     return cmsSetAdaptationStateTHR(NULL, d);
80 }
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 #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 {
95     _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(ContextID, AlarmCodesContext);
96        
97     _cmsAssert(ContextAlarmCodes != NULL); // Can't happen
98     
99     memcpy(ContextAlarmCodes->AlarmCodes, AlarmCodesP, sizeof(ContextAlarmCodes->AlarmCodes));    
100 }
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 {
106     _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(ContextID, AlarmCodesContext);
107
108     _cmsAssert(ContextAlarmCodes != NULL); // Can't happen
109
110     memcpy(AlarmCodesP, ContextAlarmCodes->AlarmCodes, sizeof(ContextAlarmCodes->AlarmCodes));
111 }
112
113 void CMSEXPORT cmsSetAlarmCodes(const cmsUInt16Number NewAlarm[cmsMAXCHANNELS])
114 {
115     _cmsAssert(NewAlarm != NULL);
116
117     cmsSetAlarmCodesTHR(NULL, NewAlarm);
118 }
119
120 void CMSEXPORT cmsGetAlarmCodes(cmsUInt16Number OldAlarm[cmsMAXCHANNELS])
121
122     _cmsAssert(OldAlarm != NULL);
123     cmsGetAlarmCodesTHR(NULL, OldAlarm);
124 }
125
126
127 // Init and duplicate alarm codes
128 void _cmsAllocAlarmCodesChunk(struct _cmsContext_struct* ctx, 
129                               const struct _cmsContext_struct* src)
130 {
131     static _cmsAlarmCodesChunkType AlarmCodesChunk = { DEFAULT_ALARM_CODES_VALUE };
132     void* from;
133      
134     if (src != NULL) {
135         from = src ->chunks[AlarmCodesContext];       
136     }
137     else {
138        from = &AlarmCodesChunk;
139     }
140     
141     ctx ->chunks[AlarmCodesContext] = _cmsSubAllocDup(ctx ->MemPool, from, sizeof(_cmsAlarmCodesChunkType));     
142 }
143
144 // -----------------------------------------------------------------------
145
146 // Get rid of transform resources
147 void CMSEXPORT cmsDeleteTransform(cmsHTRANSFORM hTransform)
148 {
149     _cmsTRANSFORM* p = (_cmsTRANSFORM*) hTransform;
150
151     _cmsAssert(p != NULL);
152
153     if (p -> GamutCheck)
154         cmsPipelineFree(p -> GamutCheck);
155
156     if (p -> Lut)
157         cmsPipelineFree(p -> Lut);
158
159     if (p ->InputColorant)
160         cmsFreeNamedColorList(p ->InputColorant);
161
162     if (p -> OutputColorant)
163         cmsFreeNamedColorList(p ->OutputColorant);
164
165     if (p ->Sequence)
166         cmsFreeProfileSequenceDescription(p ->Sequence);
167
168     if (p ->UserData)
169         p ->FreeUserData(p ->ContextID, p ->UserData);
170
171     _cmsFree(p ->ContextID, (void *) p);
172 }
173
174 // Apply transform.
175 void CMSEXPORT cmsDoTransform(cmsHTRANSFORM  Transform,
176                               const void* InputBuffer,
177                               void* OutputBuffer,
178                               cmsUInt32Number Size)
179
180 {
181     _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
182
183     p -> xform(p, InputBuffer, OutputBuffer, Size, Size);
184 }
185
186
187 // Apply transform.
188 void CMSEXPORT cmsDoTransformStride(cmsHTRANSFORM  Transform,
189                               const void* InputBuffer,
190                               void* OutputBuffer,
191                               cmsUInt32Number Size, cmsUInt32Number Stride)
192
193 {
194     _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
195
196     p -> xform(p, InputBuffer, OutputBuffer, Size, Stride);
197 }
198
199
200 // Transform routines ----------------------------------------------------------------------------------------------------------
201
202 // Float xform converts floats. Since there are no performance issues, one routine does all job, including gamut check.
203 // Note that because extended range, we can use a -1.0 value for out of gamut in this case.
204 static
205 void FloatXFORM(_cmsTRANSFORM* p,
206                 const void* in,
207                 void* out, cmsUInt32Number Size, cmsUInt32Number Stride)
208 {
209     cmsUInt8Number* accum;
210     cmsUInt8Number* output;
211     cmsFloat32Number fIn[cmsMAXCHANNELS], fOut[cmsMAXCHANNELS];
212     cmsFloat32Number OutOfGamut;
213     cmsUInt32Number i, j;
214
215     accum  = (cmsUInt8Number*)  in;
216     output = (cmsUInt8Number*)  out;
217
218     for (i=0; i < Size; i++) {
219
220         accum = p -> FromInputFloat(p, fIn, accum, Stride);
221
222         // Any gamut chack to do?
223         if (p ->GamutCheck != NULL) {
224
225             // Evaluate gamut marker.
226             cmsPipelineEvalFloat( fIn, &OutOfGamut, p ->GamutCheck);
227
228             // Is current color out of gamut?
229             if (OutOfGamut > 0.0) {
230
231                 // Certainly, out of gamut
232                 for (j=0; j < cmsMAXCHANNELS; j++)
233                     fOut[j] = -1.0;
234
235             }
236             else {
237                 // No, proceed normally
238                 cmsPipelineEvalFloat(fIn, fOut, p -> Lut);
239             }
240         }
241         else {
242
243             // No gamut check at all
244             cmsPipelineEvalFloat(fIn, fOut, p -> Lut);
245         }
246
247         // Back to asked representation
248         output = p -> ToOutputFloat(p, fOut, output, Stride);
249     }
250 }
251
252
253 static
254 void NullFloatXFORM(_cmsTRANSFORM* p,
255                     const void* in,
256                     void* out, 
257                     cmsUInt32Number Size,
258                     cmsUInt32Number Stride)
259 {
260     cmsUInt8Number* accum;
261     cmsUInt8Number* output;
262     cmsFloat32Number fIn[cmsMAXCHANNELS];
263     cmsUInt32Number i, n;
264
265     accum  = (cmsUInt8Number*)  in;
266     output = (cmsUInt8Number*)  out;
267     n = Size;                   
268
269     for (i=0; i < n; i++) {
270
271         accum  = p -> FromInputFloat(p, fIn, accum, Stride);
272         output = p -> ToOutputFloat(p, fIn, output, Stride);
273     }
274 }
275
276 // 16 bit precision -----------------------------------------------------------------------------------------------------------
277
278 // Null transformation, only applies formatters. No cach�
279 static
280 void NullXFORM(_cmsTRANSFORM* p,
281                const void* in,
282                void* out, cmsUInt32Number Size,
283                cmsUInt32Number Stride)
284 {
285     cmsUInt8Number* accum;
286     cmsUInt8Number* output;
287     cmsUInt16Number wIn[cmsMAXCHANNELS];
288     cmsUInt32Number i, n;
289
290     accum  = (cmsUInt8Number*)  in;
291     output = (cmsUInt8Number*)  out;
292     n = Size;                    // Buffer len
293
294     for (i=0; i < n; i++) {
295
296         accum  = p -> FromInput(p, wIn, accum, Stride);
297         output = p -> ToOutput(p, wIn, output, Stride);
298     }
299 }
300
301
302 // No gamut check, no cache, 16 bits
303 static
304 void PrecalculatedXFORM(_cmsTRANSFORM* p,
305                         const void* in,
306                         void* out, cmsUInt32Number Size, cmsUInt32Number Stride)
307 {
308     register cmsUInt8Number* accum;
309     register cmsUInt8Number* output;
310     cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
311     cmsUInt32Number i, n;
312
313     accum  = (cmsUInt8Number*)  in;
314     output = (cmsUInt8Number*)  out;
315     n = Size;
316
317     for (i=0; i < n; i++) {
318
319         accum = p -> FromInput(p, wIn, accum, Stride);
320         p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);
321         output = p -> ToOutput(p, wOut, output, Stride);
322     }
323 }
324
325
326 // Auxiliar: Handle precalculated gamut check. The retrieval of context may be alittle bit slow, but this function is not critical.
327 static
328 void TransformOnePixelWithGamutCheck(_cmsTRANSFORM* p,
329                                      const cmsUInt16Number wIn[],
330                                      cmsUInt16Number wOut[])
331 {
332     cmsUInt16Number wOutOfGamut;
333
334     p ->GamutCheck ->Eval16Fn(wIn, &wOutOfGamut, p ->GamutCheck ->Data);
335     if (wOutOfGamut >= 1) {
336
337         cmsUInt16Number i;
338         _cmsAlarmCodesChunkType* ContextAlarmCodes = (_cmsAlarmCodesChunkType*) _cmsContextGetClientChunk(p->ContextID, AlarmCodesContext);        
339
340         for (i=0; i < p ->Lut->OutputChannels; i++) {
341
342             wOut[i] = ContextAlarmCodes ->AlarmCodes[i];
343         }
344     }
345     else
346         p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);
347 }
348
349 // Gamut check, No cach�, 16 bits.
350 static
351 void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p,
352                                   const void* in,
353                                   void* out, cmsUInt32Number Size, cmsUInt32Number Stride)
354 {
355     cmsUInt8Number* accum;
356     cmsUInt8Number* output;
357     cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
358     cmsUInt32Number i, n;
359
360     accum  = (cmsUInt8Number*)  in;
361     output = (cmsUInt8Number*)  out;
362     n = Size;                    // Buffer len
363
364     for (i=0; i < n; i++) {
365
366         accum = p -> FromInput(p, wIn, accum, Stride);
367         TransformOnePixelWithGamutCheck(p, wIn, wOut);
368         output = p -> ToOutput(p, wOut, output, Stride);
369     }
370 }
371
372
373 // No gamut check, Cach�, 16 bits,
374 static
375 void CachedXFORM(_cmsTRANSFORM* p,
376                  const void* in,
377                  void* out, cmsUInt32Number Size, cmsUInt32Number Stride)
378 {
379     cmsUInt8Number* accum;
380     cmsUInt8Number* output;
381     cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
382     cmsUInt32Number i, n;
383     _cmsCACHE Cache;
384
385     accum  = (cmsUInt8Number*)  in;
386     output = (cmsUInt8Number*)  out;
387     n = Size;                    // Buffer len
388
389     // Empty buffers for quick memcmp
390     memset(wIn,  0, sizeof(wIn));
391     memset(wOut, 0, sizeof(wOut));
392
393     // Get copy of zero cache
394     memcpy(&Cache, &p ->Cache, sizeof(Cache));
395
396     for (i=0; i < n; i++) {
397
398         accum = p -> FromInput(p, wIn, accum, Stride);
399
400         if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) {
401
402             memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut));
403         }
404         else {
405
406             p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);
407
408             memcpy(Cache.CacheIn,  wIn,  sizeof(Cache.CacheIn));
409             memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut));
410         }
411
412         output = p -> ToOutput(p, wOut, output, Stride);
413     }
414
415 }
416
417
418 // All those nice features together
419 static
420 void CachedXFORMGamutCheck(_cmsTRANSFORM* p,
421                            const void* in,
422                            void* out, cmsUInt32Number Size, cmsUInt32Number Stride)
423 {
424        cmsUInt8Number* accum;
425        cmsUInt8Number* output;
426        cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
427        cmsUInt32Number i, n;
428        _cmsCACHE Cache;
429
430        accum  = (cmsUInt8Number*)  in;
431        output = (cmsUInt8Number*)  out;
432        n = Size;                    // Buffer len
433
434        // Empty buffers for quick memcmp
435        memset(wIn,  0, sizeof(cmsUInt16Number) * cmsMAXCHANNELS);
436        memset(wOut, 0, sizeof(cmsUInt16Number) * cmsMAXCHANNELS);
437
438        // Get copy of zero cache
439        memcpy(&Cache, &p ->Cache, sizeof(Cache));
440
441        for (i=0; i < n; i++) {
442
443             accum = p -> FromInput(p, wIn, accum, Stride);
444
445             if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) {
446                     memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut));
447             }
448             else {
449                     TransformOnePixelWithGamutCheck(p, wIn, wOut);
450                     memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn));
451                     memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut));
452             }
453
454             output = p -> ToOutput(p, wOut, output, Stride);
455        }
456
457 }
458
459 // -------------------------------------------------------------------------------------------------------------
460
461 // List of used-defined transform factories
462 typedef struct _cmsTransformCollection_st {
463
464     _cmsTransformFactory  Factory;
465     struct _cmsTransformCollection_st *Next;
466
467 } _cmsTransformCollection;
468
469 // The linked list head
470 _cmsTransformPluginChunkType _cmsTransformPluginChunk = { NULL };
471
472
473 // Duplicates the zone of memory used by the plug-in in the new context
474 static
475 void DupPluginTransformList(struct _cmsContext_struct* ctx, 
476                                                const struct _cmsContext_struct* src)
477 {
478    _cmsTransformPluginChunkType newHead = { NULL };
479    _cmsTransformCollection*  entry;
480    _cmsTransformCollection*  Anterior = NULL;
481    _cmsTransformPluginChunkType* head = (_cmsTransformPluginChunkType*) src->chunks[TransformPlugin];
482
483     // Walk the list copying all nodes
484    for (entry = head->TransformCollection;
485         entry != NULL;
486         entry = entry ->Next) {
487
488             _cmsTransformCollection *newEntry = ( _cmsTransformCollection *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTransformCollection));
489    
490             if (newEntry == NULL) 
491                 return;
492
493             // We want to keep the linked list order, so this is a little bit tricky
494             newEntry -> Next = NULL;
495             if (Anterior)
496                 Anterior -> Next = newEntry;
497      
498             Anterior = newEntry;
499
500             if (newHead.TransformCollection == NULL)
501                 newHead.TransformCollection = newEntry;
502     }
503
504   ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsTransformPluginChunkType));
505 }
506
507 void _cmsAllocTransformPluginChunk(struct _cmsContext_struct* ctx, 
508                                         const struct _cmsContext_struct* src)
509 {
510     if (src != NULL) {
511
512         // Copy all linked list
513         DupPluginTransformList(ctx, src);
514     }
515     else {
516         static _cmsTransformPluginChunkType TransformPluginChunkType = { NULL };
517         ctx ->chunks[TransformPlugin] = _cmsSubAllocDup(ctx ->MemPool, &TransformPluginChunkType, sizeof(_cmsTransformPluginChunkType));
518     }
519 }
520
521
522
523 // Register new ways to transform
524 cmsBool  _cmsRegisterTransformPlugin(cmsContext ContextID, cmsPluginBase* Data)
525 {
526     cmsPluginTransform* Plugin = (cmsPluginTransform*) Data;
527     _cmsTransformCollection* fl;
528     _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID,TransformPlugin);
529
530     if (Data == NULL) {
531
532         // Free the chain. Memory is safely freed at exit
533         ctx->TransformCollection = NULL;
534         return TRUE;
535     }
536
537     // Factory callback is required
538     if (Plugin ->Factory == NULL) return FALSE;
539
540
541     fl = (_cmsTransformCollection*) _cmsPluginMalloc(ContextID, sizeof(_cmsTransformCollection));
542     if (fl == NULL) return FALSE;
543
544     // Copy the parameters
545     fl ->Factory = Plugin ->Factory;
546
547     // Keep linked list
548     fl ->Next = ctx->TransformCollection;
549     ctx->TransformCollection = fl;
550
551     // All is ok
552     return TRUE;
553 }
554
555
556 void CMSEXPORT _cmsSetTransformUserData(struct _cmstransform_struct *CMMcargo, void* ptr, _cmsFreeUserDataFn FreePrivateDataFn)
557 {
558     _cmsAssert(CMMcargo != NULL);
559     CMMcargo ->UserData = ptr;
560     CMMcargo ->FreeUserData = FreePrivateDataFn;
561 }
562
563 // returns the pointer defined by the plug-in to store private data
564 void * CMSEXPORT _cmsGetTransformUserData(struct _cmstransform_struct *CMMcargo)
565 {
566     _cmsAssert(CMMcargo != NULL);
567     return CMMcargo ->UserData;
568 }
569
570 // returns the current formatters
571 void CMSEXPORT _cmsGetTransformFormatters16(struct _cmstransform_struct *CMMcargo, cmsFormatter16* FromInput, cmsFormatter16* ToOutput)
572 {
573      _cmsAssert(CMMcargo != NULL);
574      if (FromInput) *FromInput = CMMcargo ->FromInput;
575      if (ToOutput)  *ToOutput  = CMMcargo ->ToOutput;
576 }
577
578 void CMSEXPORT _cmsGetTransformFormattersFloat(struct _cmstransform_struct *CMMcargo, cmsFormatterFloat* FromInput, cmsFormatterFloat* ToOutput)
579 {
580      _cmsAssert(CMMcargo != NULL);
581      if (FromInput) *FromInput = CMMcargo ->FromInputFloat;
582      if (ToOutput)  *ToOutput  = CMMcargo ->ToOutputFloat;
583 }
584
585
586 // Allocate transform struct and set it to defaults. Ask the optimization plug-in about if those formats are proper
587 // for separated transforms. If this is the case,
588 static
589 _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut,
590                                                cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags)
591 {
592      _cmsTransformPluginChunkType* ctx = ( _cmsTransformPluginChunkType*) _cmsContextGetClientChunk(ContextID, TransformPlugin);
593      _cmsTransformCollection* Plugin;
594
595     // Allocate needed memory
596     _cmsTRANSFORM* p = (_cmsTRANSFORM*) _cmsMallocZero(ContextID, sizeof(_cmsTRANSFORM));
597     if (!p) return NULL;
598
599     // Store the proposed pipeline
600     p ->Lut = lut;
601
602     // Let's see if any plug-in want to do the transform by itself
603     for (Plugin = ctx ->TransformCollection;
604         Plugin != NULL;
605         Plugin = Plugin ->Next) {
606
607             if (Plugin ->Factory(&p->xform, &p->UserData, &p ->FreeUserData, &p ->Lut, InputFormat, OutputFormat, dwFlags)) {
608                 
609                 // Last plugin in the declaration order takes control. We just keep
610                 // the original parameters as a logging. 
611                 // Note that cmsFLAGS_CAN_CHANGE_FORMATTER is not set, so by default 
612                 // an optimized transform is not reusable. The plug-in can, however, change
613                 // the flags and make it suitable.
614
615                 p ->ContextID       = ContextID;               
616                 p ->InputFormat     = *InputFormat;
617                 p ->OutputFormat    = *OutputFormat;
618                 p ->dwOriginalFlags = *dwFlags;
619                
620                 // Fill the formatters just in case the optimized routine is interested.
621                 // No error is thrown if the formatter doesn't exist. It is up to the optimization 
622                 // factory to decide what to do in those cases.
623                 p ->FromInput      = _cmsGetFormatter(ContextID, *InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
624                 p ->ToOutput       = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
625                 p ->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
626                 p ->ToOutputFloat  = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
627
628                 return p;
629             }
630     }
631
632     // Not suitable for the transform plug-in, let's check  the pipeline plug-in
633     if (p ->Lut != NULL)
634         _cmsOptimizePipeline(ContextID, &p->Lut, Intent, InputFormat, OutputFormat, dwFlags);
635
636     // Check whatever this is a true floating point transform
637     if (_cmsFormatterIsFloat(*InputFormat) && _cmsFormatterIsFloat(*OutputFormat)) {
638
639         // Get formatter function always return a valid union, but the contents of this union may be NULL.
640         p ->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
641         p ->ToOutputFloat  = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
642         *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
643
644         if (p ->FromInputFloat == NULL || p ->ToOutputFloat == NULL) {
645
646             cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
647             _cmsFree(ContextID, p);
648             return NULL;
649         }
650
651         if (*dwFlags & cmsFLAGS_NULLTRANSFORM) {
652
653             p ->xform = NullFloatXFORM;
654         }
655         else {
656             // Float transforms don't use cach�, always are non-NULL
657             p ->xform = FloatXFORM;
658         }
659
660     }
661     else {
662
663         if (*InputFormat == 0 && *OutputFormat == 0) {
664             p ->FromInput = p ->ToOutput = NULL;
665             *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
666         }
667         else {
668
669             int BytesPerPixelInput;
670
671             p ->FromInput = _cmsGetFormatter(ContextID, *InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
672             p ->ToOutput  = _cmsGetFormatter(ContextID, *OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
673
674             if (p ->FromInput == NULL || p ->ToOutput == NULL) {
675
676                 cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
677                 _cmsFree(ContextID, p);
678                 return NULL;
679             }
680
681             BytesPerPixelInput = T_BYTES(p ->InputFormat);
682             if (BytesPerPixelInput == 0 || BytesPerPixelInput >= 2)
683                    *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
684
685         }
686
687         if (*dwFlags & cmsFLAGS_NULLTRANSFORM) {
688
689             p ->xform = NullXFORM;
690         }
691         else {
692             if (*dwFlags & cmsFLAGS_NOCACHE) {
693
694                 if (*dwFlags & cmsFLAGS_GAMUTCHECK)
695                     p ->xform = PrecalculatedXFORMGamutCheck;  // Gamut check, no cach�
696                 else
697                     p ->xform = PrecalculatedXFORM;  // No cach�, no gamut check
698             }
699             else {
700
701                 if (*dwFlags & cmsFLAGS_GAMUTCHECK)
702                     p ->xform = CachedXFORMGamutCheck;    // Gamut check, cach�
703                 else
704                     p ->xform = CachedXFORM;  // No gamut check, cach�
705
706             }
707         }
708     }
709
710     p ->InputFormat     = *InputFormat;
711     p ->OutputFormat    = *OutputFormat;
712     p ->dwOriginalFlags = *dwFlags;
713     p ->ContextID       = ContextID;
714     p ->UserData        = NULL;
715     return p;
716 }
717
718 static
719 cmsBool GetXFormColorSpaces(int nProfiles, cmsHPROFILE hProfiles[], cmsColorSpaceSignature* Input, cmsColorSpaceSignature* Output)
720 {
721     cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut;
722     cmsColorSpaceSignature PostColorSpace;
723     int i;
724
725     if (nProfiles <= 0) return FALSE;
726     if (hProfiles[0] == NULL) return FALSE;
727
728     *Input = PostColorSpace = cmsGetColorSpace(hProfiles[0]);
729
730     for (i=0; i < nProfiles; i++) {
731
732         cmsProfileClassSignature cls;
733         cmsHPROFILE hProfile = hProfiles[i];
734
735         int lIsInput = (PostColorSpace != cmsSigXYZData) &&
736                        (PostColorSpace != cmsSigLabData);
737
738         if (hProfile == NULL) return FALSE;
739
740         cls = cmsGetDeviceClass(hProfile);
741
742         if (cls == cmsSigNamedColorClass) {
743
744             ColorSpaceIn    = cmsSig1colorData;
745             ColorSpaceOut   = (nProfiles > 1) ? cmsGetPCS(hProfile) : cmsGetColorSpace(hProfile);
746         }
747         else
748         if (lIsInput || (cls == cmsSigLinkClass)) {
749
750             ColorSpaceIn    = cmsGetColorSpace(hProfile);
751             ColorSpaceOut   = cmsGetPCS(hProfile);
752         }
753         else
754         {
755             ColorSpaceIn    = cmsGetPCS(hProfile);
756             ColorSpaceOut   = cmsGetColorSpace(hProfile);
757         }
758
759         if (i==0)
760             *Input = ColorSpaceIn;
761
762         PostColorSpace = ColorSpaceOut;
763     }
764
765     *Output = PostColorSpace;
766
767     return TRUE;
768 }
769
770 // Check colorspace
771 static
772 cmsBool  IsProperColorSpace(cmsColorSpaceSignature Check, cmsUInt32Number dwFormat)
773 {
774     int Space1 = T_COLORSPACE(dwFormat);
775     int Space2 = _cmsLCMScolorSpace(Check);
776
777     if (Space1 == PT_ANY) return TRUE;
778     if (Space1 == Space2) return TRUE;
779
780     if (Space1 == PT_LabV2 && Space2 == PT_Lab) return TRUE;
781     if (Space1 == PT_Lab   && Space2 == PT_LabV2) return TRUE;
782
783     return FALSE;
784 }
785
786 // ----------------------------------------------------------------------------------------------------------------
787
788 static
789 void SetWhitePoint(cmsCIEXYZ* wtPt, const cmsCIEXYZ* src)
790 {
791     if (src == NULL) {
792         wtPt ->X = cmsD50X;
793         wtPt ->Y = cmsD50Y;
794         wtPt ->Z = cmsD50Z;
795     }
796     else {
797         wtPt ->X = src->X;
798         wtPt ->Y = src->Y;
799         wtPt ->Z = src->Z;
800     }
801
802 }
803
804 // New to lcms 2.0 -- have all parameters available.
805 cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
806                                                    cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[],
807                                                    cmsBool  BPC[],
808                                                    cmsUInt32Number Intents[],
809                                                    cmsFloat64Number AdaptationStates[],
810                                                    cmsHPROFILE hGamutProfile,
811                                                    cmsUInt32Number nGamutPCSposition,
812                                                    cmsUInt32Number InputFormat,
813                                                    cmsUInt32Number OutputFormat,
814                                                    cmsUInt32Number dwFlags)
815 {
816     _cmsTRANSFORM* xform;    
817     cmsColorSpaceSignature EntryColorSpace;
818     cmsColorSpaceSignature ExitColorSpace;
819     cmsPipeline* Lut;
820     cmsUInt32Number LastIntent = Intents[nProfiles-1];
821
822     // If it is a fake transform
823     if (dwFlags & cmsFLAGS_NULLTRANSFORM)
824     {
825         return AllocEmptyTransform(ContextID, NULL, INTENT_PERCEPTUAL, &InputFormat, &OutputFormat, &dwFlags);
826     }
827
828     // If gamut check is requested, make sure we have a gamut profile
829     if (dwFlags & cmsFLAGS_GAMUTCHECK) {
830         if (hGamutProfile == NULL) dwFlags &= ~cmsFLAGS_GAMUTCHECK;
831     }
832
833     // On floating point transforms, inhibit cache
834     if (_cmsFormatterIsFloat(InputFormat) || _cmsFormatterIsFloat(OutputFormat))
835         dwFlags |= cmsFLAGS_NOCACHE;
836
837     // Mark entry/exit spaces
838     if (!GetXFormColorSpaces(nProfiles, hProfiles, &EntryColorSpace, &ExitColorSpace)) {
839         cmsSignalError(ContextID, cmsERROR_NULL, "NULL input profiles on transform");
840         return NULL;
841     }
842
843     // Check if proper colorspaces
844     if (!IsProperColorSpace(EntryColorSpace, InputFormat)) {
845         cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong input color space on transform");
846         return NULL;
847     }
848
849     if (!IsProperColorSpace(ExitColorSpace, OutputFormat)) {
850         cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong output color space on transform");
851         return NULL;
852     }
853
854     // Create a pipeline with all transformations
855     Lut = _cmsLinkProfiles(ContextID, nProfiles, Intents, hProfiles, BPC, AdaptationStates, dwFlags);
856     if (Lut == NULL) {
857         cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Couldn't link the profiles");
858         return NULL;
859     }
860
861     // Check channel count
862     if ((cmsChannelsOf(EntryColorSpace) != cmsPipelineInputChannels(Lut)) ||
863         (cmsChannelsOf(ExitColorSpace)  != cmsPipelineOutputChannels(Lut))) {
864         cmsPipelineFree(Lut);
865         cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Channel count doesn't match. Profile is corrupted");
866         return NULL;
867     }
868
869
870     // All seems ok
871     xform = AllocEmptyTransform(ContextID, Lut, LastIntent, &InputFormat, &OutputFormat, &dwFlags);
872     if (xform == NULL) {
873         return NULL;
874     }
875
876     // Keep values
877     xform ->EntryColorSpace = EntryColorSpace;
878     xform ->ExitColorSpace  = ExitColorSpace;
879     xform ->RenderingIntent = Intents[nProfiles-1];
880
881     // Take white points
882     SetWhitePoint(&xform->EntryWhitePoint, (cmsCIEXYZ*) cmsReadTag(hProfiles[0], cmsSigMediaWhitePointTag));
883     SetWhitePoint(&xform->ExitWhitePoint,  (cmsCIEXYZ*) cmsReadTag(hProfiles[nProfiles-1], cmsSigMediaWhitePointTag));
884    
885
886     // Create a gamut check LUT if requested
887     if (hGamutProfile != NULL && (dwFlags & cmsFLAGS_GAMUTCHECK))
888         xform ->GamutCheck  = _cmsCreateGamutCheckPipeline(ContextID, hProfiles,
889                                                         BPC, Intents,
890                                                         AdaptationStates,
891                                                         nGamutPCSposition,
892                                                         hGamutProfile);
893
894
895     // Try to read input and output colorant table
896     if (cmsIsTag(hProfiles[0], cmsSigColorantTableTag)) {
897
898         // Input table can only come in this way.
899         xform ->InputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[0], cmsSigColorantTableTag));
900     }
901
902     // Output is a little bit more complex.
903     if (cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigLinkClass) {
904
905         // This tag may exist only on devicelink profiles.
906         if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag)) {
907
908             // It may be NULL if error
909             xform ->OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag));
910         }
911
912     } else {
913
914         if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableTag)) {
915
916             xform -> OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableTag));
917         }
918     }
919
920     // Store the sequence of profiles
921     if (dwFlags & cmsFLAGS_KEEP_SEQUENCE) {
922         xform ->Sequence = _cmsCompileProfileSequence(ContextID, nProfiles, hProfiles);
923     }
924     else
925         xform ->Sequence = NULL;
926
927     // If this is a cached transform, init first value, which is zero (16 bits only)
928     if (!(dwFlags & cmsFLAGS_NOCACHE)) {
929
930         memset(&xform ->Cache.CacheIn, 0, sizeof(xform ->Cache.CacheIn));
931
932         if (xform ->GamutCheck != NULL) {
933             TransformOnePixelWithGamutCheck(xform, xform ->Cache.CacheIn, xform->Cache.CacheOut);
934         }
935         else {
936
937             xform ->Lut ->Eval16Fn(xform ->Cache.CacheIn, xform->Cache.CacheOut, xform -> Lut->Data);
938         }
939
940     }
941
942     return (cmsHTRANSFORM) xform;
943 }
944
945 // Multiprofile transforms: Gamut check is not available here, as it is unclear from which profile the gamut comes.
946 cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransformTHR(cmsContext ContextID,
947                                                        cmsHPROFILE hProfiles[],
948                                                        cmsUInt32Number nProfiles,
949                                                        cmsUInt32Number InputFormat,
950                                                        cmsUInt32Number OutputFormat,
951                                                        cmsUInt32Number Intent,
952                                                        cmsUInt32Number dwFlags)
953 {
954     cmsUInt32Number i;
955     cmsBool BPC[256];
956     cmsUInt32Number Intents[256];
957     cmsFloat64Number AdaptationStates[256];
958
959     if (nProfiles <= 0 || nProfiles > 255) {
960          cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
961         return NULL;
962     }
963
964     for (i=0; i < nProfiles; i++) {
965         BPC[i] = dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION ? TRUE : FALSE;
966         Intents[i] = Intent;
967         AdaptationStates[i] = cmsSetAdaptationStateTHR(ContextID, -1);
968     }
969
970
971     return cmsCreateExtendedTransform(ContextID, nProfiles, hProfiles, BPC, Intents, AdaptationStates, NULL, 0, InputFormat, OutputFormat, dwFlags);
972 }
973
974
975
976 cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles[],
977                                                   cmsUInt32Number nProfiles,
978                                                   cmsUInt32Number InputFormat,
979                                                   cmsUInt32Number OutputFormat,
980                                                   cmsUInt32Number Intent,
981                                                   cmsUInt32Number dwFlags)
982 {
983
984     if (nProfiles <= 0 || nProfiles > 255) {
985          cmsSignalError(NULL, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
986          return NULL;
987     }
988
989     return cmsCreateMultiprofileTransformTHR(cmsGetProfileContextID(hProfiles[0]),
990                                                   hProfiles,
991                                                   nProfiles,
992                                                   InputFormat,
993                                                   OutputFormat,
994                                                   Intent,
995                                                   dwFlags);
996 }
997
998 cmsHTRANSFORM CMSEXPORT cmsCreateTransformTHR(cmsContext ContextID,
999                                               cmsHPROFILE Input,
1000                                               cmsUInt32Number InputFormat,
1001                                               cmsHPROFILE Output,
1002                                               cmsUInt32Number OutputFormat,
1003                                               cmsUInt32Number Intent,
1004                                               cmsUInt32Number dwFlags)
1005 {
1006
1007     cmsHPROFILE hArray[2];
1008
1009     hArray[0] = Input;
1010     hArray[1] = Output;
1011
1012     return cmsCreateMultiprofileTransformTHR(ContextID, hArray, Output == NULL ? 1 : 2, InputFormat, OutputFormat, Intent, dwFlags);
1013 }
1014
1015 CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateTransform(cmsHPROFILE Input,
1016                                                   cmsUInt32Number InputFormat,
1017                                                   cmsHPROFILE Output,
1018                                                   cmsUInt32Number OutputFormat,
1019                                                   cmsUInt32Number Intent,
1020                                                   cmsUInt32Number dwFlags)
1021 {
1022     return cmsCreateTransformTHR(cmsGetProfileContextID(Input), Input, InputFormat, Output, OutputFormat, Intent, dwFlags);
1023 }
1024
1025
1026 cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransformTHR(cmsContext ContextID,
1027                                                    cmsHPROFILE InputProfile,
1028                                                    cmsUInt32Number InputFormat,
1029                                                    cmsHPROFILE OutputProfile,
1030                                                    cmsUInt32Number OutputFormat,
1031                                                    cmsHPROFILE ProofingProfile,
1032                                                    cmsUInt32Number nIntent,
1033                                                    cmsUInt32Number ProofingIntent,
1034                                                    cmsUInt32Number dwFlags)
1035 {
1036     cmsHPROFILE hArray[4];
1037     cmsUInt32Number Intents[4];
1038     cmsBool  BPC[4];
1039     cmsFloat64Number Adaptation[4];
1040     cmsBool  DoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION) ? TRUE : FALSE;
1041
1042
1043     hArray[0]  = InputProfile; hArray[1] = ProofingProfile; hArray[2]  = ProofingProfile;               hArray[3] = OutputProfile;
1044     Intents[0] = nIntent;      Intents[1] = nIntent;        Intents[2] = INTENT_RELATIVE_COLORIMETRIC;  Intents[3] = ProofingIntent;
1045     BPC[0]     = DoBPC;        BPC[1] = DoBPC;              BPC[2] = 0;                                 BPC[3] = 0;
1046
1047     Adaptation[0] = Adaptation[1] = Adaptation[2] = Adaptation[3] = cmsSetAdaptationStateTHR(ContextID, -1);
1048
1049     if (!(dwFlags & (cmsFLAGS_SOFTPROOFING|cmsFLAGS_GAMUTCHECK)))
1050         return cmsCreateTransformTHR(ContextID, InputProfile, InputFormat, OutputProfile, OutputFormat, nIntent, dwFlags);
1051
1052     return cmsCreateExtendedTransform(ContextID, 4, hArray, BPC, Intents, Adaptation,
1053                                         ProofingProfile, 1, InputFormat, OutputFormat, dwFlags);
1054
1055 }
1056
1057
1058 cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransform(cmsHPROFILE InputProfile,
1059                                                    cmsUInt32Number InputFormat,
1060                                                    cmsHPROFILE OutputProfile,
1061                                                    cmsUInt32Number OutputFormat,
1062                                                    cmsHPROFILE ProofingProfile,
1063                                                    cmsUInt32Number nIntent,
1064                                                    cmsUInt32Number ProofingIntent,
1065                                                    cmsUInt32Number dwFlags)
1066 {
1067     return cmsCreateProofingTransformTHR(cmsGetProfileContextID(InputProfile),
1068                                                    InputProfile,
1069                                                    InputFormat,
1070                                                    OutputProfile,
1071                                                    OutputFormat,
1072                                                    ProofingProfile,
1073                                                    nIntent,
1074                                                    ProofingIntent,
1075                                                    dwFlags);
1076 }
1077
1078
1079 // Grab the ContextID from an open transform. Returns NULL if a NULL transform is passed
1080 cmsContext CMSEXPORT cmsGetTransformContextID(cmsHTRANSFORM hTransform)
1081 {
1082     _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1083
1084     if (xform == NULL) return NULL;
1085     return xform -> ContextID;
1086 }
1087
1088 // Grab the input/output formats
1089 cmsUInt32Number CMSEXPORT cmsGetTransformInputFormat(cmsHTRANSFORM hTransform)
1090 {
1091     _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1092
1093     if (xform == NULL) return 0;
1094     return xform->InputFormat;
1095 }
1096
1097 cmsUInt32Number CMSEXPORT cmsGetTransformOutputFormat(cmsHTRANSFORM hTransform)
1098 {
1099     _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1100
1101     if (xform == NULL) return 0;
1102     return xform->OutputFormat;
1103 }
1104
1105 // For backwards compatibility
1106 cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform,
1107                                          cmsUInt32Number InputFormat,
1108                                          cmsUInt32Number OutputFormat)
1109 {
1110
1111     _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
1112     cmsFormatter16 FromInput, ToOutput;
1113
1114
1115     // We only can afford to change formatters if previous transform is at least 16 bits
1116     if (!(xform ->dwOriginalFlags & cmsFLAGS_CAN_CHANGE_FORMATTER)) {
1117
1118         cmsSignalError(xform ->ContextID, cmsERROR_NOT_SUITABLE, "cmsChangeBuffersFormat works only on transforms created originally with at least 16 bits of precision");
1119         return FALSE;
1120     }
1121
1122     FromInput = _cmsGetFormatter(xform->ContextID, InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
1123     ToOutput  = _cmsGetFormatter(xform->ContextID, OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
1124
1125     if (FromInput == NULL || ToOutput == NULL) {
1126
1127         cmsSignalError(xform -> ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
1128         return FALSE;
1129     }
1130
1131     xform ->InputFormat  = InputFormat;
1132     xform ->OutputFormat = OutputFormat;
1133     xform ->FromInput    = FromInput;
1134     xform ->ToOutput     = ToOutput;
1135     return TRUE;
1136 }