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