Tizen 2.0 Release
[external/lcms.git] / src / cmsxform.c
1 //---------------------------------------------------------------------------------
2 //
3 //  Little Color Management System
4 //  Copyright (c) 1998-2011 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 // Alarm codes for 16-bit transformations, because the fixed range of containers there are
33 // no values left to mark out of gamut. volatile is C99 per 6.2.5
34 static volatile cmsUInt16Number Alarm[cmsMAXCHANNELS] = { 0x7F00, 0x7F00, 0x7F00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
35 static volatile cmsFloat64Number GlobalAdaptationState = 1;
36
37 // The adaptation state may be defaulted by this function. If you don't like it, use the extended transform routine
38 cmsFloat64Number CMSEXPORT cmsSetAdaptationState(cmsFloat64Number d)
39 {
40     cmsFloat64Number OldVal = GlobalAdaptationState;
41
42     if (d >= 0) 
43         GlobalAdaptationState = d;
44
45     return OldVal;  
46 }
47
48 // Alarm codes are always global
49 void CMSEXPORT cmsSetAlarmCodes(cmsUInt16Number NewAlarm[cmsMAXCHANNELS])
50 {
51     int i;
52
53     _cmsAssert(NewAlarm != NULL);
54
55     for (i=0; i < cmsMAXCHANNELS; i++) 
56         Alarm[i] = NewAlarm[i];
57 }
58
59 // You can get the codes cas well
60 void CMSEXPORT cmsGetAlarmCodes(cmsUInt16Number OldAlarm[cmsMAXCHANNELS])
61 {
62     int i;
63
64     _cmsAssert(OldAlarm != NULL);
65
66     for (i=0; i < cmsMAXCHANNELS; i++) 
67         OldAlarm[i] = Alarm[i];
68 }
69
70 // Get rid of transform resources
71 void CMSEXPORT cmsDeleteTransform(cmsHTRANSFORM hTransform)
72 {
73     _cmsTRANSFORM* p = (_cmsTRANSFORM*) hTransform;
74
75     _cmsAssert(p != NULL);
76
77     if (p -> GamutCheck)
78         cmsPipelineFree(p -> GamutCheck);
79
80     if (p -> Lut)
81         cmsPipelineFree(p -> Lut);
82
83     if (p ->InputColorant)
84         cmsFreeNamedColorList(p ->InputColorant);
85
86     if (p -> OutputColorant)
87         cmsFreeNamedColorList(p ->OutputColorant);
88
89     if (p ->Sequence)
90         cmsFreeProfileSequenceDescription(p ->Sequence);
91
92     _cmsFree(p ->ContextID, (void *) p);
93 }
94
95 // Apply transform. 
96 void CMSEXPORT cmsDoTransform(cmsHTRANSFORM  Transform,
97                               const void* InputBuffer,
98                               void* OutputBuffer, 
99                               cmsUInt32Number Size)
100
101 {
102     _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
103     
104     p -> xform(p, InputBuffer, OutputBuffer, Size);
105 }
106
107
108 // Transform routines ----------------------------------------------------------------------------------------------------------
109
110 // Float xform converts floats. Since there are no performance issues, one routine does all job, including gamut check.
111 // Note that because extended range, we can use a -1.0 value for out of gamut in this case.
112 static
113 void FloatXFORM(_cmsTRANSFORM* p,               
114                 const void* in,
115                 void* out, cmsUInt32Number Size)
116 {
117     cmsUInt8Number* accum;
118     cmsUInt8Number* output;
119     cmsFloat32Number fIn[cmsMAXCHANNELS], fOut[cmsMAXCHANNELS];
120     cmsFloat32Number OutOfGamut;
121     cmsUInt32Number i, j;
122
123     accum  = (cmsUInt8Number*)  in;
124     output = (cmsUInt8Number*)  out;
125
126     for (i=0; i < Size; i++) {
127
128         accum = p -> FromInputFloat(p, fIn, accum, Size);         
129
130         // Any gamut chack to do?
131         if (p ->GamutCheck != NULL) {
132
133             // Evaluate gamut marker.
134             cmsPipelineEvalFloat( fIn, &OutOfGamut, p ->GamutCheck);
135
136             // Is current color out of gamut?
137             if (OutOfGamut > 0.0) {
138
139                 // Certainly, out of gamut
140                 for (j=0; j < cmsMAXCHANNELS; j++)
141                     fOut[j] = -1.0;
142
143             }
144             else {
145                 // No, proceed normally
146                 cmsPipelineEvalFloat(fIn, fOut, p -> Lut); 
147             }
148         }
149         else {
150
151             // No gamut check at all
152             cmsPipelineEvalFloat(fIn, fOut, p -> Lut);     
153         }
154
155         // Back to asked representation
156         output = p -> ToOutputFloat(p, fOut, output, Size);
157     }
158 }
159
160 // 16 bit precision -----------------------------------------------------------------------------------------------------------
161
162 // Null transformation, only applies formatters. No caché
163 static
164 void NullXFORM(_cmsTRANSFORM* p,
165                const void* in,
166                void* out, cmsUInt32Number Size)
167 {
168     cmsUInt8Number* accum;
169     cmsUInt8Number* output;
170     cmsUInt16Number wIn[cmsMAXCHANNELS];
171     cmsUInt32Number i, n;
172
173     accum  = (cmsUInt8Number*)  in;
174     output = (cmsUInt8Number*)  out;
175     n = Size;                    // Buffer len
176
177     for (i=0; i < n; i++) {
178
179         accum  = p -> FromInput(p, wIn, accum, Size);
180         output = p -> ToOutput(p, wIn, output, Size);
181     }
182 }
183
184
185 // No gamut check, no cache, 16 bits
186 static
187 void PrecalculatedXFORM(_cmsTRANSFORM* p,
188                         const void* in,
189                         void* out, cmsUInt32Number Size)
190 {
191     register cmsUInt8Number* accum;
192     register cmsUInt8Number* output;
193     cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
194     cmsUInt32Number i, n;
195
196     accum  = (cmsUInt8Number*)  in;
197     output = (cmsUInt8Number*)  out;
198     n = Size;                    
199
200     for (i=0; i < n; i++) {
201
202         accum = p -> FromInput(p, wIn, accum, Size);         
203         p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);     
204         output = p -> ToOutput(p, wOut, output, Size);
205     }
206 }
207
208
209 // Auxiliar: Handle precalculated gamut check
210 static
211 void TransformOnePixelWithGamutCheck(_cmsTRANSFORM* p, 
212                                      const cmsUInt16Number wIn[], 
213                                      cmsUInt16Number wOut[])
214 {
215     cmsUInt16Number wOutOfGamut;
216
217     p ->GamutCheck ->Eval16Fn(wIn, &wOutOfGamut, p ->GamutCheck ->Data);   
218     if (wOutOfGamut >= 1) {
219
220         cmsUInt16Number i;
221
222         for (i=0; i < p ->Lut->OutputChannels; i++)
223             wOut[i] = Alarm[i];                      
224     }
225     else
226         p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);   
227 }
228
229 // Gamut check, No caché, 16 bits.
230 static
231 void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p,
232                                   const void* in,
233                                   void* out, cmsUInt32Number Size)
234 {
235     cmsUInt8Number* accum;
236     cmsUInt8Number* output;
237     cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
238     cmsUInt32Number i, n;
239
240     accum  = (cmsUInt8Number*)  in;
241     output = (cmsUInt8Number*)  out;
242     n = Size;                    // Buffer len
243
244     for (i=0; i < n; i++) {
245
246         accum = p -> FromInput(p, wIn, accum, Size);
247         TransformOnePixelWithGamutCheck(p, wIn, wOut);
248         output = p -> ToOutput(p, wOut, output, Size);
249     }
250 }
251
252
253 // No gamut check, Caché, 16 bits, 
254 static
255 void CachedXFORM(_cmsTRANSFORM* p,
256                  const void* in,
257                  void* out, cmsUInt32Number Size)
258 {
259     cmsUInt8Number* accum;
260     cmsUInt8Number* output;
261     cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
262     cmsUInt32Number i, n;
263     _cmsCACHE Cache;
264
265     accum  = (cmsUInt8Number*)  in;
266     output = (cmsUInt8Number*)  out;
267     n = Size;                    // Buffer len
268
269     // Empty buffers for quick memcmp
270     memset(wIn,  0, sizeof(wIn));
271     memset(wOut, 0, sizeof(wOut));
272
273     // Get copy of zero cache
274     memcpy(&Cache, &p ->Cache, sizeof(Cache));
275
276     for (i=0; i < n; i++) {
277
278         accum = p -> FromInput(p, wIn, accum, Size);
279
280         if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) {
281
282             memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut));
283         }
284         else {   
285
286             p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);  
287
288             memcpy(Cache.CacheIn,  wIn,  sizeof(Cache.CacheIn));
289             memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut));
290         }
291
292         output = p -> ToOutput(p, wOut, output, Size);            
293     }
294  
295 }
296
297
298 // All those nice features together
299 static
300 void CachedXFORMGamutCheck(_cmsTRANSFORM* p,
301                            const void* in,
302                            void* out, cmsUInt32Number Size)
303 {
304        cmsUInt8Number* accum;
305        cmsUInt8Number* output;
306        cmsUInt16Number wIn[cmsMAXCHANNELS], wOut[cmsMAXCHANNELS];
307        cmsUInt32Number i, n;
308        _cmsCACHE Cache;
309
310        accum  = (cmsUInt8Number*)  in;
311        output = (cmsUInt8Number*)  out;
312        n = Size;                    // Buffer len
313
314        // Empty buffers for quick memcmp
315        memset(wIn,  0, sizeof(cmsUInt16Number) * cmsMAXCHANNELS);
316        memset(wOut, 0, sizeof(cmsUInt16Number) * cmsMAXCHANNELS);
317
318        // Get copy of zero cache
319        memcpy(&Cache, &p ->Cache, sizeof(Cache));
320
321        for (i=0; i < n; i++) {
322
323             accum = p -> FromInput(p, wIn, accum, Size);
324      
325             if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) {
326                     memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut));
327             }
328             else {            
329                     TransformOnePixelWithGamutCheck(p, wIn, wOut);
330                     memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn));
331                     memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut));
332             }
333
334             output = p -> ToOutput(p, wOut, output, Size);
335        }
336        
337 }
338
339
340
341
342 // Allocate transform struct and set it to defaults
343 static
344 _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsUInt32Number InputFormat, cmsUInt32Number OutputFormat, cmsUInt32Number dwFlags)
345 {
346     // Allocate needed memory
347     _cmsTRANSFORM* p = (_cmsTRANSFORM*) _cmsMallocZero(ContextID, sizeof(_cmsTRANSFORM));
348     if (!p) return NULL;
349
350     // Check whatever this is a true floating point transform
351     if (_cmsFormatterIsFloat(InputFormat) && _cmsFormatterIsFloat(OutputFormat)) {
352
353         // Get formatter function always return a valid union, but the contents of this union may be NULL.
354         p ->FromInputFloat = _cmsGetFormatter(InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
355         p ->ToOutputFloat  = _cmsGetFormatter(OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
356         dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
357
358         if (p ->FromInputFloat == NULL || p ->ToOutputFloat == NULL) {
359         
360             cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
361             _cmsFree(ContextID, p);
362             return NULL;
363         }
364
365         // Float transforms don't use caché, always are non-NULL
366         p ->xform = FloatXFORM;
367     }
368     else {
369
370         if (InputFormat == 0 && OutputFormat == 0) {
371             p ->FromInput = p ->ToOutput = NULL;
372         }
373         else {
374
375             int BytesPerPixelInput;
376
377             p ->FromInput = _cmsGetFormatter(InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
378             p ->ToOutput  = _cmsGetFormatter(OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
379
380
381             if (p ->FromInput == NULL || p ->ToOutput == NULL) {
382
383                 cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
384                 _cmsFree(ContextID, p);
385                 return NULL;
386             }
387
388             BytesPerPixelInput = T_BYTES(p ->InputFormat);
389             if (BytesPerPixelInput == 0 || BytesPerPixelInput >= 2) 
390                    dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
391
392         }
393
394         if (dwFlags & cmsFLAGS_NULLTRANSFORM) {
395
396             p ->xform = NullXFORM;
397         }
398         else {
399             if (dwFlags & cmsFLAGS_NOCACHE) {
400
401                 if (dwFlags & cmsFLAGS_GAMUTCHECK) 
402                     p ->xform = PrecalculatedXFORMGamutCheck;  // Gamut check, no caché
403                 else
404                     p ->xform = PrecalculatedXFORM;  // No caché, no gamut check
405             }
406             else {
407
408                 if (dwFlags & cmsFLAGS_GAMUTCHECK) 
409                     p ->xform = CachedXFORMGamutCheck;    // Gamut check, caché
410                 else
411                     p ->xform = CachedXFORM;  // No gamut check, caché
412
413             }
414         }
415     }
416
417     
418     p ->InputFormat     = InputFormat;
419     p ->OutputFormat    = OutputFormat;
420     p ->dwOriginalFlags = dwFlags;
421     p ->ContextID       = ContextID;
422     return p;
423 }
424
425 static
426 cmsBool GetXFormColorSpaces(int nProfiles, cmsHPROFILE hProfiles[], cmsColorSpaceSignature* Input, cmsColorSpaceSignature* Output) 
427 {    
428     cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut;   
429     cmsColorSpaceSignature PostColorSpace;   
430     int i;
431
432     if (hProfiles[0] == NULL) return FALSE;
433
434     *Input = PostColorSpace = cmsGetColorSpace(hProfiles[0]);
435
436     // Special handling for named color profiles as devicelinks
437     if (nProfiles == 1 && cmsGetDeviceClass(hProfiles[0]) == cmsSigNamedColorClass) {
438             *Input  = cmsSig1colorData;
439             *Output = PostColorSpace;
440             return TRUE;
441     }
442
443     for (i=0; i < nProfiles; i++) {
444
445         cmsHPROFILE hProfile = hProfiles[i];
446
447         int lIsInput = (PostColorSpace != cmsSigXYZData) &&
448                        (PostColorSpace != cmsSigLabData);
449
450         int lIsDeviceLink;
451                
452         if (hProfile == NULL) return FALSE;
453
454         lIsDeviceLink = (cmsGetDeviceClass(hProfile) == cmsSigLinkClass);
455
456         if (lIsInput || lIsDeviceLink) {
457
458             ColorSpaceIn    = cmsGetColorSpace(hProfile);
459             ColorSpaceOut   = cmsGetPCS(hProfile);
460         }
461         else {
462
463             ColorSpaceIn    = cmsGetPCS(hProfile);
464             ColorSpaceOut   = cmsGetColorSpace(hProfile);
465         }
466
467         PostColorSpace = ColorSpaceOut;     
468     }  
469
470     *Output = PostColorSpace;
471
472     return TRUE;
473 }
474
475 // Check colorspace
476 static
477 cmsBool  IsProperColorSpace(cmsColorSpaceSignature Check, cmsUInt32Number dwFormat)
478 {
479     int Space1 = T_COLORSPACE(dwFormat);
480     int Space2 = _cmsLCMScolorSpace(Check);
481
482     if (Space1 == PT_ANY) return TRUE;
483     if (Space1 == Space2) return TRUE;
484
485     if (Space1 == PT_LabV2 && Space2 == PT_Lab) return TRUE;
486     if (Space1 == PT_Lab   && Space2 == PT_LabV2) return TRUE;
487
488     return FALSE;
489 }
490
491 // ----------------------------------------------------------------------------------------------------------------
492
493 // New to lcms 2.0 -- have all parameters available.
494 cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
495                                                    cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[], 
496                                                    cmsBool  BPC[], 
497                                                    cmsUInt32Number Intents[], 
498                                                    cmsFloat64Number AdaptationStates[],
499                                                    cmsHPROFILE hGamutProfile,
500                                                    cmsUInt32Number nGamutPCSposition,           
501                                                    cmsUInt32Number InputFormat,
502                                                    cmsUInt32Number OutputFormat,
503                                                    cmsUInt32Number dwFlags)
504 {
505     _cmsTRANSFORM* xform;
506     cmsBool  FloatTransform;
507     cmsColorSpaceSignature EntryColorSpace;
508     cmsColorSpaceSignature ExitColorSpace;
509     cmsPipeline* Lut;
510     cmsUInt32Number LastIntent = Intents[nProfiles-1];
511
512     // If gamut check is requested, make sure we have a gamut profile
513     if (dwFlags & cmsFLAGS_GAMUTCHECK) {
514         if (hGamutProfile == NULL) dwFlags &= ~cmsFLAGS_GAMUTCHECK;
515     }
516
517     // On floating point transforms, inhibit optimizations 
518     FloatTransform = (_cmsFormatterIsFloat(InputFormat) && _cmsFormatterIsFloat(OutputFormat));
519
520     if (_cmsFormatterIsFloat(InputFormat) || _cmsFormatterIsFloat(OutputFormat))
521         dwFlags |= cmsFLAGS_NOCACHE;
522
523     // Mark entry/exit spaces
524     if (!GetXFormColorSpaces(nProfiles, hProfiles, &EntryColorSpace, &ExitColorSpace)) {
525         cmsSignalError(ContextID, cmsERROR_NULL, "NULL input profiles on transform");        
526         return NULL;
527     }
528
529     // Check if proper colorspaces
530     if (!IsProperColorSpace(EntryColorSpace, InputFormat)) {        
531         cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong input color space on transform");        
532         return NULL;
533     }
534
535     if (!IsProperColorSpace(ExitColorSpace, OutputFormat)) {
536         cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong output color space on transform");       
537         return NULL;
538     }
539
540     // Create a pipeline with all transformations
541     Lut = _cmsLinkProfiles(ContextID, nProfiles, Intents, hProfiles, BPC, AdaptationStates, dwFlags);
542     if (Lut == NULL) {
543         cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Couldn't link the profiles");   
544         return NULL;
545     }
546
547     // Optimize the LUT if possible
548     _cmsOptimizePipeline(&Lut, LastIntent, &InputFormat, &OutputFormat, &dwFlags);       
549
550
551     // All seems ok
552     xform = AllocEmptyTransform(ContextID, InputFormat, OutputFormat, dwFlags);
553     if (xform == NULL) {
554         cmsPipelineFree(Lut);
555         return NULL;
556     }
557
558     // Keep values
559     xform ->EntryColorSpace = EntryColorSpace;
560     xform ->ExitColorSpace  = ExitColorSpace;
561     xform ->RenderingIntent = Intents[nProfiles-1];
562     xform ->Lut             = Lut;
563     
564     // Create a gamut check LUT if requested
565     if (hGamutProfile != NULL && (dwFlags & cmsFLAGS_GAMUTCHECK))       
566         xform ->GamutCheck  = _cmsCreateGamutCheckPipeline(ContextID, hProfiles, 
567                                                         BPC, Intents, 
568                                                         AdaptationStates, 
569                                                         nGamutPCSposition, 
570                                                         hGamutProfile);
571
572
573     // Try to read input and output colorant table
574     if (cmsIsTag(hProfiles[0], cmsSigColorantTableTag)) {
575
576         // Input table can only come in this way.       
577         xform ->InputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[0], cmsSigColorantTableTag));
578     }
579
580     // Output is a little bit more complex.    
581     if (cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigLinkClass) {
582
583         // This tag may exist only on devicelink profiles.        
584         if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag)) {
585
586             // It may be NULL if error
587             xform ->OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag));
588         }
589
590     } else {
591
592         if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableTag)) {
593
594             xform -> OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableTag));
595         }     
596     }
597
598     // Store the sequence of profiles
599     if (dwFlags & cmsFLAGS_KEEP_SEQUENCE) {
600         xform ->Sequence = _cmsCompileProfileSequence(ContextID, nProfiles, hProfiles);
601     }
602     else 
603         xform ->Sequence = NULL;
604
605     // If this is a cached transform, init first value, which is zero (16 bits only)
606     if (!(dwFlags & cmsFLAGS_NOCACHE)) {
607
608         memset(&xform ->Cache.CacheIn, 0, sizeof(xform ->Cache.CacheIn));
609
610         if (xform ->GamutCheck != NULL) {
611             TransformOnePixelWithGamutCheck(xform, xform ->Cache.CacheIn, xform->Cache.CacheOut);
612         }
613         else {
614
615             xform ->Lut ->Eval16Fn(xform ->Cache.CacheIn, xform->Cache.CacheOut, xform -> Lut->Data);  
616         }
617
618     }
619
620     return (cmsHTRANSFORM) xform; 
621 }
622
623 // Multiprofile transforms: Gamut check is not available here, as it is unclear from which profile the gamut comes.
624 cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransformTHR(cmsContext ContextID,
625                                                        cmsHPROFILE hProfiles[],
626                                                        cmsUInt32Number nProfiles,
627                                                        cmsUInt32Number InputFormat,
628                                                        cmsUInt32Number OutputFormat,
629                                                        cmsUInt32Number Intent,
630                                                        cmsUInt32Number dwFlags)
631 {
632     cmsUInt32Number i;
633     cmsBool BPC[256];
634     cmsUInt32Number Intents[256];
635     cmsFloat64Number AdaptationStates[256];
636
637     if (nProfiles <= 0 || nProfiles > 255) {
638          cmsSignalError(ContextID, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
639         return NULL;
640     }
641
642     for (i=0; i < nProfiles; i++) {
643         BPC[i] = dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION ? TRUE : FALSE;
644         Intents[i] = Intent;
645         AdaptationStates[i] = GlobalAdaptationState;
646     }
647
648
649     return cmsCreateExtendedTransform(ContextID, nProfiles, hProfiles, BPC, Intents, AdaptationStates, NULL, 0, InputFormat, OutputFormat, dwFlags);
650 }
651
652
653
654 cmsHTRANSFORM CMSEXPORT cmsCreateMultiprofileTransform(cmsHPROFILE hProfiles[],
655                                                   cmsUInt32Number nProfiles,
656                                                   cmsUInt32Number InputFormat,
657                                                   cmsUInt32Number OutputFormat,
658                                                   cmsUInt32Number Intent,
659                                                   cmsUInt32Number dwFlags)
660 {
661
662     if (nProfiles <= 0 || nProfiles > 255) {
663          cmsSignalError(NULL, cmsERROR_RANGE, "Wrong number of profiles. 1..255 expected, %d found.", nProfiles);
664          return NULL;
665     }
666
667     return cmsCreateMultiprofileTransformTHR(cmsGetProfileContextID(hProfiles[0]),
668                                                   hProfiles,
669                                                   nProfiles,
670                                                   InputFormat,
671                                                   OutputFormat,
672                                                   Intent,
673                                                   dwFlags);
674 }
675
676 cmsHTRANSFORM CMSEXPORT cmsCreateTransformTHR(cmsContext ContextID,
677                                               cmsHPROFILE Input,
678                                               cmsUInt32Number InputFormat,
679                                               cmsHPROFILE Output,
680                                               cmsUInt32Number OutputFormat,
681                                               cmsUInt32Number Intent,
682                                               cmsUInt32Number dwFlags)
683 {
684
685     cmsHPROFILE hArray[2];
686     
687     hArray[0] = Input;
688     hArray[1] = Output;
689
690     return cmsCreateMultiprofileTransformTHR(ContextID, hArray, Output == NULL ? 1 : 2, InputFormat, OutputFormat, Intent, dwFlags);
691 }
692
693 CMSAPI cmsHTRANSFORM CMSEXPORT cmsCreateTransform(cmsHPROFILE Input,
694                                                   cmsUInt32Number InputFormat,
695                                                   cmsHPROFILE Output,
696                                                   cmsUInt32Number OutputFormat,
697                                                   cmsUInt32Number Intent,
698                                                   cmsUInt32Number dwFlags)
699 {
700     return cmsCreateTransformTHR(cmsGetProfileContextID(Input), Input, InputFormat, Output, OutputFormat, Intent, dwFlags);
701 }
702
703
704 cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransformTHR(cmsContext ContextID,
705                                                    cmsHPROFILE InputProfile,
706                                                    cmsUInt32Number InputFormat,
707                                                    cmsHPROFILE OutputProfile,
708                                                    cmsUInt32Number OutputFormat,
709                                                    cmsHPROFILE ProofingProfile,
710                                                    cmsUInt32Number nIntent,
711                                                    cmsUInt32Number ProofingIntent,
712                                                    cmsUInt32Number dwFlags)
713 {   
714     cmsHPROFILE hArray[4];
715     cmsUInt32Number Intents[4];
716     cmsBool  BPC[4];
717     cmsFloat64Number Adaptation[4];
718     cmsBool  DoBPC = (dwFlags & cmsFLAGS_BLACKPOINTCOMPENSATION) ? TRUE : FALSE;
719
720
721     hArray[0]  = InputProfile; hArray[1] = ProofingProfile; hArray[2]  = ProofingProfile;               hArray[3] = OutputProfile;
722     Intents[0] = nIntent;      Intents[1] = nIntent;        Intents[2] = INTENT_RELATIVE_COLORIMETRIC;  Intents[3] = ProofingIntent;
723     BPC[0]     = DoBPC;        BPC[1] = DoBPC;              BPC[2] = 0;                                 BPC[3] = 0;
724     
725     Adaptation[0] = Adaptation[1] = Adaptation[2] = Adaptation[3] = GlobalAdaptationState;
726
727     if (!(dwFlags & (cmsFLAGS_SOFTPROOFING|cmsFLAGS_GAMUTCHECK))) 
728         return cmsCreateTransformTHR(ContextID, InputProfile, InputFormat, OutputProfile, OutputFormat, nIntent, dwFlags);
729   
730     return cmsCreateExtendedTransform(ContextID, 4, hArray, BPC, Intents, Adaptation, 
731                                         ProofingProfile, 1, InputFormat, OutputFormat, dwFlags);
732
733 }
734
735
736 cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransform(cmsHPROFILE InputProfile,
737                                                    cmsUInt32Number InputFormat,
738                                                    cmsHPROFILE OutputProfile,
739                                                    cmsUInt32Number OutputFormat,
740                                                    cmsHPROFILE ProofingProfile,
741                                                    cmsUInt32Number nIntent,
742                                                    cmsUInt32Number ProofingIntent,
743                                                    cmsUInt32Number dwFlags)
744 {
745     return cmsCreateProofingTransformTHR(cmsGetProfileContextID(InputProfile), 
746                                                    InputProfile,
747                                                    InputFormat,
748                                                    OutputProfile,
749                                                    OutputFormat,
750                                                    ProofingProfile,
751                                                    nIntent,
752                                                    ProofingIntent,
753                                                    dwFlags);
754 }
755
756
757 // Grab the ContextID from an open transform. Returns NULL if a NULL transform is passed
758 cmsContext CMSEXPORT cmsGetTransformContextID(cmsHTRANSFORM hTransform)
759 {
760     _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
761
762     if (xform == NULL) return NULL;
763     return xform -> ContextID;
764 }
765
766 // Grab the input/output formats
767 cmsUInt32Number CMSEXPORT cmsGetTransformInputFormat(cmsHTRANSFORM hTransform)
768 {
769     _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
770
771     if (xform == NULL) return 0;
772     return xform->InputFormat;
773 }
774
775 cmsUInt32Number CMSEXPORT cmsGetTransformOutputFormat(cmsHTRANSFORM hTransform)
776 {
777     _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
778
779     if (xform == NULL) return 0;
780     return xform->OutputFormat;
781 }
782
783 // For backwards compatibility
784 cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform, 
785                                          cmsUInt32Number InputFormat, 
786                                          cmsUInt32Number OutputFormat)
787 {
788
789     _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
790     cmsFormatter16 FromInput, ToOutput;
791     cmsUInt32Number BytesPerPixelInput;
792
793     // We only can afford to change formatters if previous transform is at least 16 bits
794     BytesPerPixelInput = T_BYTES(xform ->InputFormat);
795     if (!(xform ->dwOriginalFlags & cmsFLAGS_CAN_CHANGE_FORMATTER)) {
796
797         cmsSignalError(xform ->ContextID, cmsERROR_NOT_SUITABLE, "cmsChangeBuffersFormat works only on transforms created originally with at least 16 bits of precision");
798         return FALSE;
799     }
800
801     FromInput = _cmsGetFormatter(InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
802     ToOutput  = _cmsGetFormatter(OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
803
804     if (FromInput == NULL || ToOutput == NULL) {
805
806         cmsSignalError(xform -> ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
807         return FALSE;
808     }
809
810     xform ->InputFormat  = InputFormat;
811     xform ->OutputFormat = OutputFormat;
812     xform ->FromInput    = FromInput;
813     xform ->ToOutput     = ToOutput;
814     return TRUE;
815 }