Imported Upstream version 2.4
[platform/upstream/lcms2.git] / src / cmsxform.c
index 8a76dae..5649801 100644 (file)
@@ -3,22 +3,22 @@
 //  Little Color Management System
 //  Copyright (c) 1998-2011 Marti Maria Saguer
 //
-// Permission is hereby granted, free of charge, to any person obtaining 
-// a copy of this software and associated documentation files (the "Software"), 
-// to deal in the Software without restriction, including without limitation 
-// the rights to use, copy, modify, merge, publish, distribute, sublicense, 
-// and/or sell copies of the Software, and to permit persons to whom the Software 
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the Software
 // is furnished to do so, subject to the following conditions:
 //
-// The above copyright notice and this permission notice shall be included in 
+// The above copyright notice and this permission notice shall be included in
 // all copies or substantial portions of the Software.
 //
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
-// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
+// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 //---------------------------------------------------------------------------------
@@ -39,10 +39,10 @@ cmsFloat64Number CMSEXPORT cmsSetAdaptationState(cmsFloat64Number d)
 {
     cmsFloat64Number OldVal = GlobalAdaptationState;
 
-    if (d >= 0) 
+    if (d >= 0)
         GlobalAdaptationState = d;
 
-    return OldVal;  
+    return OldVal;
 }
 
 // Alarm codes are always global
@@ -52,7 +52,7 @@ void CMSEXPORT cmsSetAlarmCodes(cmsUInt16Number NewAlarm[cmsMAXCHANNELS])
 
     _cmsAssert(NewAlarm != NULL);
 
-    for (i=0; i < cmsMAXCHANNELS; i++) 
+    for (i=0; i < cmsMAXCHANNELS; i++)
         Alarm[i] = NewAlarm[i];
 }
 
@@ -63,7 +63,7 @@ void CMSEXPORT cmsGetAlarmCodes(cmsUInt16Number OldAlarm[cmsMAXCHANNELS])
 
     _cmsAssert(OldAlarm != NULL);
 
-    for (i=0; i < cmsMAXCHANNELS; i++) 
+    for (i=0; i < cmsMAXCHANNELS; i++)
         OldAlarm[i] = Alarm[i];
 }
 
@@ -89,19 +89,35 @@ void CMSEXPORT cmsDeleteTransform(cmsHTRANSFORM hTransform)
     if (p ->Sequence)
         cmsFreeProfileSequenceDescription(p ->Sequence);
 
+    if (p ->UserData)
+        p ->FreeUserData(p ->ContextID, p ->UserData);
+
     _cmsFree(p ->ContextID, (void *) p);
 }
 
-// Apply transform. 
+// Apply transform.
 void CMSEXPORT cmsDoTransform(cmsHTRANSFORM  Transform,
                               const void* InputBuffer,
-                              void* OutputBuffer, 
+                              void* OutputBuffer,
                               cmsUInt32Number Size)
 
 {
     _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
-    
-    p -> xform(p, InputBuffer, OutputBuffer, Size);
+
+    p -> xform(p, InputBuffer, OutputBuffer, Size, Size);
+}
+
+
+// Apply transform.
+void CMSEXPORT cmsDoTransformStride(cmsHTRANSFORM  Transform,
+                              const void* InputBuffer,
+                              void* OutputBuffer,
+                              cmsUInt32Number Size, cmsUInt32Number Stride)
+
+{
+    _cmsTRANSFORM* p = (_cmsTRANSFORM*) Transform;
+
+    p -> xform(p, InputBuffer, OutputBuffer, Size, Stride);
 }
 
 
@@ -110,9 +126,9 @@ void CMSEXPORT cmsDoTransform(cmsHTRANSFORM  Transform,
 // Float xform converts floats. Since there are no performance issues, one routine does all job, including gamut check.
 // Note that because extended range, we can use a -1.0 value for out of gamut in this case.
 static
-void FloatXFORM(_cmsTRANSFORM* p,               
+void FloatXFORM(_cmsTRANSFORM* p,
                 const void* in,
-                void* out, cmsUInt32Number Size)
+                void* out, cmsUInt32Number Size, cmsUInt32Number Stride)
 {
     cmsUInt8Number* accum;
     cmsUInt8Number* output;
@@ -125,7 +141,7 @@ void FloatXFORM(_cmsTRANSFORM* p,
 
     for (i=0; i < Size; i++) {
 
-        accum = p -> FromInputFloat(p, fIn, accum, Size);         
+        accum = p -> FromInputFloat(p, fIn, accum, Stride);
 
         // Any gamut chack to do?
         if (p ->GamutCheck != NULL) {
@@ -143,17 +159,17 @@ void FloatXFORM(_cmsTRANSFORM* p,
             }
             else {
                 // No, proceed normally
-                cmsPipelineEvalFloat(fIn, fOut, p -> Lut); 
+                cmsPipelineEvalFloat(fIn, fOut, p -> Lut);
             }
         }
         else {
 
             // No gamut check at all
-            cmsPipelineEvalFloat(fIn, fOut, p -> Lut);     
+            cmsPipelineEvalFloat(fIn, fOut, p -> Lut);
         }
 
         // Back to asked representation
-        output = p -> ToOutputFloat(p, fOut, output, Size);
+        output = p -> ToOutputFloat(p, fOut, output, Stride);
     }
 }
 
@@ -163,7 +179,8 @@ void FloatXFORM(_cmsTRANSFORM* p,
 static
 void NullXFORM(_cmsTRANSFORM* p,
                const void* in,
-               void* out, cmsUInt32Number Size)
+               void* out, cmsUInt32Number Size,
+               cmsUInt32Number Stride)
 {
     cmsUInt8Number* accum;
     cmsUInt8Number* output;
@@ -176,8 +193,8 @@ void NullXFORM(_cmsTRANSFORM* p,
 
     for (i=0; i < n; i++) {
 
-        accum  = p -> FromInput(p, wIn, accum, Size);
-        output = p -> ToOutput(p, wIn, output, Size);
+        accum  = p -> FromInput(p, wIn, accum, Stride);
+        output = p -> ToOutput(p, wIn, output, Stride);
     }
 }
 
@@ -186,7 +203,7 @@ void NullXFORM(_cmsTRANSFORM* p,
 static
 void PrecalculatedXFORM(_cmsTRANSFORM* p,
                         const void* in,
-                        void* out, cmsUInt32Number Size)
+                        void* out, cmsUInt32Number Size, cmsUInt32Number Stride)
 {
     register cmsUInt8Number* accum;
     register cmsUInt8Number* output;
@@ -195,42 +212,42 @@ void PrecalculatedXFORM(_cmsTRANSFORM* p,
 
     accum  = (cmsUInt8Number*)  in;
     output = (cmsUInt8Number*)  out;
-    n = Size;                    
+    n = Size;
 
     for (i=0; i < n; i++) {
 
-        accum = p -> FromInput(p, wIn, accum, Size);         
-        p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);     
-        output = p -> ToOutput(p, wOut, output, Size);
+        accum = p -> FromInput(p, wIn, accum, Stride);
+        p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);
+        output = p -> ToOutput(p, wOut, output, Stride);
     }
 }
 
 
 // Auxiliar: Handle precalculated gamut check
 static
-void TransformOnePixelWithGamutCheck(_cmsTRANSFORM* p, 
-                                     const cmsUInt16Number wIn[], 
+void TransformOnePixelWithGamutCheck(_cmsTRANSFORM* p,
+                                     const cmsUInt16Number wIn[],
                                      cmsUInt16Number wOut[])
 {
     cmsUInt16Number wOutOfGamut;
 
-    p ->GamutCheck ->Eval16Fn(wIn, &wOutOfGamut, p ->GamutCheck ->Data);   
+    p ->GamutCheck ->Eval16Fn(wIn, &wOutOfGamut, p ->GamutCheck ->Data);
     if (wOutOfGamut >= 1) {
 
         cmsUInt16Number i;
 
         for (i=0; i < p ->Lut->OutputChannels; i++)
-            wOut[i] = Alarm[i];                      
+            wOut[i] = Alarm[i];
     }
     else
-        p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);   
+        p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);
 }
 
 // Gamut check, No caché, 16 bits.
 static
 void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p,
                                   const void* in,
-                                  void* out, cmsUInt32Number Size)
+                                  void* out, cmsUInt32Number Size, cmsUInt32Number Stride)
 {
     cmsUInt8Number* accum;
     cmsUInt8Number* output;
@@ -243,18 +260,18 @@ void PrecalculatedXFORMGamutCheck(_cmsTRANSFORM* p,
 
     for (i=0; i < n; i++) {
 
-        accum = p -> FromInput(p, wIn, accum, Size);
+        accum = p -> FromInput(p, wIn, accum, Stride);
         TransformOnePixelWithGamutCheck(p, wIn, wOut);
-        output = p -> ToOutput(p, wOut, output, Size);
+        output = p -> ToOutput(p, wOut, output, Stride);
     }
 }
 
 
-// No gamut check, Caché, 16 bits, 
+// No gamut check, Caché, 16 bits,
 static
 void CachedXFORM(_cmsTRANSFORM* p,
                  const void* in,
-                 void* out, cmsUInt32Number Size)
+                 void* out, cmsUInt32Number Size, cmsUInt32Number Stride)
 {
     cmsUInt8Number* accum;
     cmsUInt8Number* output;
@@ -275,23 +292,23 @@ void CachedXFORM(_cmsTRANSFORM* p,
 
     for (i=0; i < n; i++) {
 
-        accum = p -> FromInput(p, wIn, accum, Size);
+        accum = p -> FromInput(p, wIn, accum, Stride);
 
         if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) {
 
             memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut));
         }
-        else {   
+        else {
 
-            p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);  
+            p ->Lut ->Eval16Fn(wIn, wOut, p -> Lut->Data);
 
             memcpy(Cache.CacheIn,  wIn,  sizeof(Cache.CacheIn));
             memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut));
         }
 
-        output = p -> ToOutput(p, wOut, output, Size);            
+        output = p -> ToOutput(p, wOut, output, Stride);
     }
+
 }
 
 
@@ -299,7 +316,7 @@ void CachedXFORM(_cmsTRANSFORM* p,
 static
 void CachedXFORMGamutCheck(_cmsTRANSFORM* p,
                            const void* in,
-                           void* out, cmsUInt32Number Size)
+                           void* out, cmsUInt32Number Size, cmsUInt32Number Stride)
 {
        cmsUInt8Number* accum;
        cmsUInt8Number* output;
@@ -320,43 +337,156 @@ void CachedXFORMGamutCheck(_cmsTRANSFORM* p,
 
        for (i=0; i < n; i++) {
 
-            accum = p -> FromInput(p, wIn, accum, Size);
-     
+            accum = p -> FromInput(p, wIn, accum, Stride);
+
             if (memcmp(wIn, Cache.CacheIn, sizeof(Cache.CacheIn)) == 0) {
                     memcpy(wOut, Cache.CacheOut, sizeof(Cache.CacheOut));
             }
-            else {            
+            else {
                     TransformOnePixelWithGamutCheck(p, wIn, wOut);
                     memcpy(Cache.CacheIn, wIn, sizeof(Cache.CacheIn));
                     memcpy(Cache.CacheOut, wOut, sizeof(Cache.CacheOut));
             }
 
-            output = p -> ToOutput(p, wOut, output, Size);
+            output = p -> ToOutput(p, wOut, output, Stride);
        }
-       
+
+}
+
+// -------------------------------------------------------------------------------------------------------------
+
+// List of used-defined transform factories
+typedef struct _cmsTransformCollection_st {
+
+    _cmsTransformFactory  Factory;
+    struct _cmsTransformCollection_st *Next;
+
+} _cmsTransformCollection;
+
+// The linked list head
+static _cmsTransformCollection* TransformCollection = NULL;
+
+// Register new ways to transform
+cmsBool  _cmsRegisterTransformPlugin(cmsPluginBase* Data)
+{
+    cmsPluginTransform* Plugin = (cmsPluginTransform*) Data;
+    _cmsTransformCollection* fl;
+
+      if (Data == NULL) {
+
+        // Free the chain. Memory is safely freed at exit
+        TransformCollection = NULL;
+        return TRUE;
+    }
+
+    // Factory callback is required
+   if (Plugin ->Factory == NULL) return FALSE;
+
+
+    fl = (_cmsTransformCollection*) _cmsPluginMalloc(sizeof(_cmsTransformCollection));
+    if (fl == NULL) return FALSE;
+
+      // Copy the parameters
+    fl ->Factory = Plugin ->Factory;
+
+    // Keep linked list
+    fl ->Next = TransformCollection;
+    TransformCollection = fl;
+
+    // All is ok
+    return TRUE;
 }
 
 
+void CMSEXPORT _cmsSetTransformUserData(struct _cmstransform_struct *CMMcargo, void* ptr, _cmsFreeUserDataFn FreePrivateDataFn)
+{
+    _cmsAssert(CMMcargo != NULL);
+    CMMcargo ->UserData = ptr;
+    CMMcargo ->FreeUserData = FreePrivateDataFn;
+}
 
+// returns the pointer defined by the plug-in to store private data
+void * CMSEXPORT _cmsGetTransformUserData(struct _cmstransform_struct *CMMcargo)
+{
+    _cmsAssert(CMMcargo != NULL);
+    return CMMcargo ->UserData;
+}
 
-// Allocate transform struct and set it to defaults
+// returns the current formatters
+void CMSEXPORT _cmsGetTransformFormatters16(struct _cmstransform_struct *CMMcargo, cmsFormatter16* FromInput, cmsFormatter16* ToOutput)
+{
+     _cmsAssert(CMMcargo != NULL);
+     if (FromInput) *FromInput = CMMcargo ->FromInput;
+     if (ToOutput)  *ToOutput  = CMMcargo ->ToOutput;
+}
+
+void CMSEXPORT _cmsGetTransformFormattersFloat(struct _cmstransform_struct *CMMcargo, cmsFormatterFloat* FromInput, cmsFormatterFloat* ToOutput)
+{
+     _cmsAssert(CMMcargo != NULL);
+     if (FromInput) *FromInput = CMMcargo ->FromInputFloat;
+     if (ToOutput)  *ToOutput  = CMMcargo ->ToOutputFloat;
+}
+
+
+// Allocate transform struct and set it to defaults. Ask the optimization plug-in about if those formats are proper
+// for separated transforms. If this is the case,
 static
-_cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsUInt32Number InputFormat, cmsUInt32Number OutputFormat, cmsUInt32Number dwFlags)
+_cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut,
+                                               cmsUInt32Number Intent, cmsUInt32Number* InputFormat, cmsUInt32Number* OutputFormat, cmsUInt32Number* dwFlags)
 {
+     _cmsTransformCollection* Plugin;
+
     // Allocate needed memory
     _cmsTRANSFORM* p = (_cmsTRANSFORM*) _cmsMallocZero(ContextID, sizeof(_cmsTRANSFORM));
     if (!p) return NULL;
 
+    // Store the proposed pipeline
+    p ->Lut = lut;
+
+    // Let's see if any plug-in want to do the transform by itself
+    for (Plugin = TransformCollection;
+        Plugin != NULL;
+        Plugin = Plugin ->Next) {
+
+            if (Plugin ->Factory(&p->xform, &p->UserData, &p ->FreeUserData, &p ->Lut, InputFormat, OutputFormat, dwFlags)) {
+                
+                // Last plugin in the declaration order takes control. We just keep
+                // the original parameters as a logging. 
+                // Note that cmsFLAGS_CAN_CHANGE_FORMATTER is not set, so by default 
+                // an optimized transform is not reusable. The plug-in can, however, change
+                // the flags and make it suitable.
+
+                p ->ContextID       = ContextID;               
+                p ->InputFormat     = *InputFormat;
+                p ->OutputFormat    = *OutputFormat;
+                p ->dwOriginalFlags = *dwFlags;
+               
+                // Fill the formatters just in case the optimized routine is interested.
+                // No error is thrown if the formatter doesn't exist. It is up to the optimization 
+                // factory to decide what to do in those cases.
+                p ->FromInput      = _cmsGetFormatter(*InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
+                p ->ToOutput       = _cmsGetFormatter(*OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
+                p ->FromInputFloat = _cmsGetFormatter(*InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
+                p ->ToOutputFloat  = _cmsGetFormatter(*OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
+
+                return p;
+            }
+    }
+
+    // Not suitable for the transform plug-in, let's check  the pipeline plug-in
+    if (p ->Lut != NULL)
+        _cmsOptimizePipeline(&p->Lut, Intent, InputFormat, OutputFormat, dwFlags);
+
     // Check whatever this is a true floating point transform
-    if (_cmsFormatterIsFloat(InputFormat) && _cmsFormatterIsFloat(OutputFormat)) {
+    if (_cmsFormatterIsFloat(*InputFormat) && _cmsFormatterIsFloat(*OutputFormat)) {
 
         // Get formatter function always return a valid union, but the contents of this union may be NULL.
-        p ->FromInputFloat = _cmsGetFormatter(InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
-        p ->ToOutputFloat  = _cmsGetFormatter(OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
-        dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
+        p ->FromInputFloat = _cmsGetFormatter(*InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
+        p ->ToOutputFloat  = _cmsGetFormatter(*OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
+        *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
 
         if (p ->FromInputFloat == NULL || p ->ToOutputFloat == NULL) {
-        
+
             cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported raster format");
             _cmsFree(ContextID, p);
             return NULL;
@@ -367,16 +497,16 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsUInt32Number InputFo
     }
     else {
 
-        if (InputFormat == 0 && OutputFormat == 0) {
+        if (*InputFormat == 0 && *OutputFormat == 0) {
             p ->FromInput = p ->ToOutput = NULL;
+            *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
         }
         else {
 
             int BytesPerPixelInput;
 
-            p ->FromInput = _cmsGetFormatter(InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
-            p ->ToOutput  = _cmsGetFormatter(OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
-
+            p ->FromInput = _cmsGetFormatter(*InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_16BITS).Fmt16;
+            p ->ToOutput  = _cmsGetFormatter(*OutputFormat, cmsFormatterOutput, CMS_PACK_FLAGS_16BITS).Fmt16;
 
             if (p ->FromInput == NULL || p ->ToOutput == NULL) {
 
@@ -386,26 +516,26 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsUInt32Number InputFo
             }
 
             BytesPerPixelInput = T_BYTES(p ->InputFormat);
-            if (BytesPerPixelInput == 0 || BytesPerPixelInput >= 2) 
-                   dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
+            if (BytesPerPixelInput == 0 || BytesPerPixelInput >= 2)
+                   *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
 
         }
 
-        if (dwFlags & cmsFLAGS_NULLTRANSFORM) {
+        if (*dwFlags & cmsFLAGS_NULLTRANSFORM) {
 
             p ->xform = NullXFORM;
         }
         else {
-            if (dwFlags & cmsFLAGS_NOCACHE) {
+            if (*dwFlags & cmsFLAGS_NOCACHE) {
 
-                if (dwFlags & cmsFLAGS_GAMUTCHECK) 
+                if (*dwFlags & cmsFLAGS_GAMUTCHECK)
                     p ->xform = PrecalculatedXFORMGamutCheck;  // Gamut check, no caché
                 else
                     p ->xform = PrecalculatedXFORM;  // No caché, no gamut check
             }
             else {
 
-                if (dwFlags & cmsFLAGS_GAMUTCHECK) 
+                if (*dwFlags & cmsFLAGS_GAMUTCHECK)
                     p ->xform = CachedXFORMGamutCheck;    // Gamut check, caché
                 else
                     p ->xform = CachedXFORM;  // No gamut check, caché
@@ -414,58 +544,60 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsUInt32Number InputFo
         }
     }
 
-    
-    p ->InputFormat     = InputFormat;
-    p ->OutputFormat    = OutputFormat;
-    p ->dwOriginalFlags = dwFlags;
+    p ->InputFormat     = *InputFormat;
+    p ->OutputFormat    = *OutputFormat;
+    p ->dwOriginalFlags = *dwFlags;
     p ->ContextID       = ContextID;
+    p ->UserData        = NULL;
     return p;
 }
 
 static
-cmsBool GetXFormColorSpaces(int nProfiles, cmsHPROFILE hProfiles[], cmsColorSpaceSignature* Input, cmsColorSpaceSignature* Output) 
-{    
-    cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut;   
-    cmsColorSpaceSignature PostColorSpace;   
+cmsBool GetXFormColorSpaces(int nProfiles, cmsHPROFILE hProfiles[], cmsColorSpaceSignature* Input, cmsColorSpaceSignature* Output)
+{
+    cmsColorSpaceSignature ColorSpaceIn, ColorSpaceOut;
+    cmsColorSpaceSignature PostColorSpace;
     int i;
 
+    if (nProfiles <= 0) return FALSE;
     if (hProfiles[0] == NULL) return FALSE;
 
     *Input = PostColorSpace = cmsGetColorSpace(hProfiles[0]);
 
-    // Special handling for named color profiles as devicelinks
-    if (nProfiles == 1 && cmsGetDeviceClass(hProfiles[0]) == cmsSigNamedColorClass) {
-            *Input  = cmsSig1colorData;
-            *Output = PostColorSpace;
-            return TRUE;
-    }
-
     for (i=0; i < nProfiles; i++) {
 
+        cmsProfileClassSignature cls;
         cmsHPROFILE hProfile = hProfiles[i];
 
         int lIsInput = (PostColorSpace != cmsSigXYZData) &&
                        (PostColorSpace != cmsSigLabData);
 
-        int lIsDeviceLink;
-               
         if (hProfile == NULL) return FALSE;
 
-        lIsDeviceLink = (cmsGetDeviceClass(hProfile) == cmsSigLinkClass);
+        cls = cmsGetDeviceClass(hProfile);
 
-        if (lIsInput || lIsDeviceLink) {
+        if (cls == cmsSigNamedColorClass) {
+
+            ColorSpaceIn    = cmsSig1colorData;
+            ColorSpaceOut   = (nProfiles > 1) ? cmsGetPCS(hProfile) : cmsGetColorSpace(hProfile);
+        }
+        else
+        if (lIsInput || (cls == cmsSigLinkClass)) {
 
             ColorSpaceIn    = cmsGetColorSpace(hProfile);
             ColorSpaceOut   = cmsGetPCS(hProfile);
         }
-        else {
-
+        else
+        {
             ColorSpaceIn    = cmsGetPCS(hProfile);
             ColorSpaceOut   = cmsGetColorSpace(hProfile);
         }
 
-        PostColorSpace = ColorSpaceOut;     
-    }  
+        if (i==0)
+            *Input = ColorSpaceIn;
+
+        PostColorSpace = ColorSpaceOut;
+    }
 
     *Output = PostColorSpace;
 
@@ -492,12 +624,12 @@ cmsBool  IsProperColorSpace(cmsColorSpaceSignature Check, cmsUInt32Number dwForm
 
 // New to lcms 2.0 -- have all parameters available.
 cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
-                                                   cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[], 
-                                                   cmsBool  BPC[], 
-                                                   cmsUInt32Number Intents[], 
+                                                   cmsUInt32Number nProfiles, cmsHPROFILE hProfiles[],
+                                                   cmsBool  BPC[],
+                                                   cmsUInt32Number Intents[],
                                                    cmsFloat64Number AdaptationStates[],
                                                    cmsHPROFILE hGamutProfile,
-                                                   cmsUInt32Number nGamutPCSposition,           
+                                                   cmsUInt32Number nGamutPCSposition,
                                                    cmsUInt32Number InputFormat,
                                                    cmsUInt32Number OutputFormat,
                                                    cmsUInt32Number dwFlags)
@@ -509,12 +641,18 @@ cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
     cmsPipeline* Lut;
     cmsUInt32Number LastIntent = Intents[nProfiles-1];
 
+    // If it is a fake transform
+    if (dwFlags & cmsFLAGS_NULLTRANSFORM)
+    {
+        return AllocEmptyTransform(ContextID, NULL, INTENT_PERCEPTUAL, &InputFormat, &OutputFormat, &dwFlags);
+    }
+
     // If gamut check is requested, make sure we have a gamut profile
     if (dwFlags & cmsFLAGS_GAMUTCHECK) {
         if (hGamutProfile == NULL) dwFlags &= ~cmsFLAGS_GAMUTCHECK;
     }
 
-    // On floating point transforms, inhibit optimizations 
+    // On floating point transforms, inhibit optimizations
     FloatTransform = (_cmsFormatterIsFloat(InputFormat) && _cmsFormatterIsFloat(OutputFormat));
 
     if (_cmsFormatterIsFloat(InputFormat) || _cmsFormatterIsFloat(OutputFormat))
@@ -522,36 +660,39 @@ cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
 
     // Mark entry/exit spaces
     if (!GetXFormColorSpaces(nProfiles, hProfiles, &EntryColorSpace, &ExitColorSpace)) {
-        cmsSignalError(ContextID, cmsERROR_NULL, "NULL input profiles on transform");        
+        cmsSignalError(ContextID, cmsERROR_NULL, "NULL input profiles on transform");
         return NULL;
     }
 
     // Check if proper colorspaces
-    if (!IsProperColorSpace(EntryColorSpace, InputFormat)) {        
-        cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong input color space on transform");        
+    if (!IsProperColorSpace(EntryColorSpace, InputFormat)) {
+        cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong input color space on transform");
         return NULL;
     }
 
     if (!IsProperColorSpace(ExitColorSpace, OutputFormat)) {
-        cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong output color space on transform");       
+        cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "Wrong output color space on transform");
         return NULL;
     }
 
     // Create a pipeline with all transformations
     Lut = _cmsLinkProfiles(ContextID, nProfiles, Intents, hProfiles, BPC, AdaptationStates, dwFlags);
     if (Lut == NULL) {
-        cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Couldn't link the profiles");   
+        cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Couldn't link the profiles");
         return NULL;
     }
 
-    // Optimize the LUT if possible
-    _cmsOptimizePipeline(&Lut, LastIntent, &InputFormat, &OutputFormat, &dwFlags);       
+    // Check channel count
+    if ((cmsChannelsOf(EntryColorSpace) != cmsPipelineInputChannels(Lut)) ||
+        (cmsChannelsOf(ExitColorSpace)  != cmsPipelineOutputChannels(Lut))) {
+        cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Channel count doesn't match. Profile is corrupted");
+        return NULL;
+    }
 
 
     // All seems ok
-    xform = AllocEmptyTransform(ContextID, InputFormat, OutputFormat, dwFlags);
+    xform = AllocEmptyTransform(ContextID, Lut, LastIntent, &InputFormat, &OutputFormat, &dwFlags);
     if (xform == NULL) {
-        cmsPipelineFree(Lut);
         return NULL;
     }
 
@@ -559,28 +700,28 @@ cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
     xform ->EntryColorSpace = EntryColorSpace;
     xform ->ExitColorSpace  = ExitColorSpace;
     xform ->RenderingIntent = Intents[nProfiles-1];
-    xform ->Lut             = Lut;
-    
+
+
     // Create a gamut check LUT if requested
-    if (hGamutProfile != NULL && (dwFlags & cmsFLAGS_GAMUTCHECK))       
-        xform ->GamutCheck  = _cmsCreateGamutCheckPipeline(ContextID, hProfiles, 
-                                                        BPC, Intents, 
-                                                        AdaptationStates, 
-                                                        nGamutPCSposition, 
+    if (hGamutProfile != NULL && (dwFlags & cmsFLAGS_GAMUTCHECK))
+        xform ->GamutCheck  = _cmsCreateGamutCheckPipeline(ContextID, hProfiles,
+                                                        BPC, Intents,
+                                                        AdaptationStates,
+                                                        nGamutPCSposition,
                                                         hGamutProfile);
 
 
     // Try to read input and output colorant table
     if (cmsIsTag(hProfiles[0], cmsSigColorantTableTag)) {
 
-        // Input table can only come in this way.       
+        // Input table can only come in this way.
         xform ->InputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[0], cmsSigColorantTableTag));
     }
 
-    // Output is a little bit more complex.    
+    // Output is a little bit more complex.
     if (cmsGetDeviceClass(hProfiles[nProfiles-1]) == cmsSigLinkClass) {
 
-        // This tag may exist only on devicelink profiles.        
+        // This tag may exist only on devicelink profiles.
         if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableOutTag)) {
 
             // It may be NULL if error
@@ -592,14 +733,14 @@ cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
         if (cmsIsTag(hProfiles[nProfiles-1], cmsSigColorantTableTag)) {
 
             xform -> OutputColorant = cmsDupNamedColorList((cmsNAMEDCOLORLIST*) cmsReadTag(hProfiles[nProfiles-1], cmsSigColorantTableTag));
-        }     
+        }
     }
 
     // Store the sequence of profiles
     if (dwFlags & cmsFLAGS_KEEP_SEQUENCE) {
         xform ->Sequence = _cmsCompileProfileSequence(ContextID, nProfiles, hProfiles);
     }
-    else 
+    else
         xform ->Sequence = NULL;
 
     // If this is a cached transform, init first value, which is zero (16 bits only)
@@ -612,12 +753,12 @@ cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID,
         }
         else {
 
-            xform ->Lut ->Eval16Fn(xform ->Cache.CacheIn, xform->Cache.CacheOut, xform -> Lut->Data);  
+            xform ->Lut ->Eval16Fn(xform ->Cache.CacheIn, xform->Cache.CacheOut, xform -> Lut->Data);
         }
 
     }
 
-    return (cmsHTRANSFORM) xform; 
+    return (cmsHTRANSFORM) xform;
 }
 
 // Multiprofile transforms: Gamut check is not available here, as it is unclear from which profile the gamut comes.
@@ -683,7 +824,7 @@ cmsHTRANSFORM CMSEXPORT cmsCreateTransformTHR(cmsContext ContextID,
 {
 
     cmsHPROFILE hArray[2];
-    
+
     hArray[0] = Input;
     hArray[1] = Output;
 
@@ -710,7 +851,7 @@ cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransformTHR(cmsContext ContextID,
                                                    cmsUInt32Number nIntent,
                                                    cmsUInt32Number ProofingIntent,
                                                    cmsUInt32Number dwFlags)
-{   
+{
     cmsHPROFILE hArray[4];
     cmsUInt32Number Intents[4];
     cmsBool  BPC[4];
@@ -721,13 +862,13 @@ cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransformTHR(cmsContext ContextID,
     hArray[0]  = InputProfile; hArray[1] = ProofingProfile; hArray[2]  = ProofingProfile;               hArray[3] = OutputProfile;
     Intents[0] = nIntent;      Intents[1] = nIntent;        Intents[2] = INTENT_RELATIVE_COLORIMETRIC;  Intents[3] = ProofingIntent;
     BPC[0]     = DoBPC;        BPC[1] = DoBPC;              BPC[2] = 0;                                 BPC[3] = 0;
-    
+
     Adaptation[0] = Adaptation[1] = Adaptation[2] = Adaptation[3] = GlobalAdaptationState;
 
-    if (!(dwFlags & (cmsFLAGS_SOFTPROOFING|cmsFLAGS_GAMUTCHECK))) 
+    if (!(dwFlags & (cmsFLAGS_SOFTPROOFING|cmsFLAGS_GAMUTCHECK)))
         return cmsCreateTransformTHR(ContextID, InputProfile, InputFormat, OutputProfile, OutputFormat, nIntent, dwFlags);
-  
-    return cmsCreateExtendedTransform(ContextID, 4, hArray, BPC, Intents, Adaptation, 
+
+    return cmsCreateExtendedTransform(ContextID, 4, hArray, BPC, Intents, Adaptation,
                                         ProofingProfile, 1, InputFormat, OutputFormat, dwFlags);
 
 }
@@ -742,7 +883,7 @@ cmsHTRANSFORM CMSEXPORT cmsCreateProofingTransform(cmsHPROFILE InputProfile,
                                                    cmsUInt32Number ProofingIntent,
                                                    cmsUInt32Number dwFlags)
 {
-    return cmsCreateProofingTransformTHR(cmsGetProfileContextID(InputProfile), 
+    return cmsCreateProofingTransformTHR(cmsGetProfileContextID(InputProfile),
                                                    InputProfile,
                                                    InputFormat,
                                                    OutputProfile,
@@ -781,17 +922,16 @@ cmsUInt32Number CMSEXPORT cmsGetTransformOutputFormat(cmsHTRANSFORM hTransform)
 }
 
 // For backwards compatibility
-cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform, 
-                                         cmsUInt32Number InputFormat, 
+cmsBool CMSEXPORT cmsChangeBuffersFormat(cmsHTRANSFORM hTransform,
+                                         cmsUInt32Number InputFormat,
                                          cmsUInt32Number OutputFormat)
 {
 
     _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
     cmsFormatter16 FromInput, ToOutput;
-    cmsUInt32Number BytesPerPixelInput;
+
 
     // We only can afford to change formatters if previous transform is at least 16 bits
-    BytesPerPixelInput = T_BYTES(xform ->InputFormat);
     if (!(xform ->dwOriginalFlags & cmsFLAGS_CAN_CHANGE_FORMATTER)) {
 
         cmsSignalError(xform ->ContextID, cmsERROR_NOT_SUITABLE, "cmsChangeBuffersFormat works only on transforms created originally with at least 16 bits of precision");