Upgrade to lcms 2.14.

- Remove already applied part of 0033-opt-integer-overflow.patch.
- Add 0034-dead-code.patch to delete some useless code.

Change-Id: If00ae157835c3f7e797274397674d4c154a82ff9
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/102174
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/third_party/lcms/0030-const-data.patch b/third_party/lcms/0030-const-data.patch
index 42a3b93..0c42dde 100644
--- a/third_party/lcms/0030-const-data.patch
+++ b/third_party/lcms/0030-const-data.patch
@@ -63,16 +63,3 @@
  
      _cmsAssert(Params != NULL);
  
-diff --git a/third_party/lcms/src/cmshalf.c b/third_party/lcms/src/cmshalf.c
-index 292b6fdf9..52c0a2a22 100644
---- a/third_party/lcms/src/cmshalf.c
-+++ b/third_party/lcms/src/cmshalf.c
-@@ -377,7 +377,7 @@ static const cmsUInt32Number Mantissa[2048] = {
- 0x387fc000, 0x387fe000
- };
- 
--static cmsUInt16Number Offset[64] = {
-+static const cmsUInt16Number Offset[64] = {
- 0x0000, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
- 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
- 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400,
diff --git a/third_party/lcms/0034-dead-code.patch b/third_party/lcms/0034-dead-code.patch
new file mode 100644
index 0000000..d02e60b
--- /dev/null
+++ b/third_party/lcms/0034-dead-code.patch
@@ -0,0 +1,14 @@
+diff --git a/third_party/lcms/src/cmspack.c b/third_party/lcms/src/cmspack.c
+index 3982cc564..7f55ce304 100644
+--- a/third_party/lcms/src/cmspack.c
++++ b/third_party/lcms/src/cmspack.c
+@@ -3864,9 +3864,6 @@ cmsUInt32Number CMSEXPORT cmsFormatterForPCSOfProfile(cmsHPROFILE hProfile, cmsU
+     cmsUInt32Number nOutputChans = cmsChannelsOf(ColorSpace);
+     cmsUInt32Number Float = lIsFloat ? 1U : 0;
+ 
+-    // Unsupported color space?
+-    if (nOutputChans < 0) return 0;
+-
+     // Create a fake formatter for result
+     return FLOAT_SH(Float) | COLORSPACE_SH(ColorSpaceBits) | BYTES_SH(nBytes) | CHANNELS_SH(nOutputChans);
+ }
diff --git a/third_party/lcms/README.pdfium b/third_party/lcms/README.pdfium
index e5c3645..4f20a08 100644
--- a/third_party/lcms/README.pdfium
+++ b/third_party/lcms/README.pdfium
@@ -1,6 +1,6 @@
 Name: Little CMS
 URL: http://www.littlecms.com/
-Version: 2.13
+Version: 2.14
 Security Critical: yes
 License: MIT License
 
@@ -20,3 +20,4 @@
 0029-drop-register-keyword.patch: Remove deprecated 'register' keyword.
 0030-const-data.patch: Mark many data structures as const.
 0033-opt-integer-overflow.patch: Protect against integer overflow.
+0034-dead-code.patch: Remove dead code.
diff --git a/third_party/lcms/include/lcms2.h b/third_party/lcms/include/lcms2.h
index 11d0c53..330709d 100644
--- a/third_party/lcms/include/lcms2.h
+++ b/third_party/lcms/include/lcms2.h
@@ -23,7 +23,7 @@
 //
 //---------------------------------------------------------------------------------
 //
-// Version 2.13
+// Version 2.14 
 //
 
 #ifndef _lcms2_H
@@ -81,7 +81,7 @@
 #endif
 
 // Version/release
-#define LCMS_VERSION        2130
+#define LCMS_VERSION        2140
 
 // I will give the chance of redefining basic types for compilers that are not fully C99 compliant
 #ifndef CMS_BASIC_TYPES_ALREADY_DEFINED
@@ -152,7 +152,7 @@
 #endif
 
 // Handle "register" keyword
-#if defined(CMS_NO_REGISTER_KEYWORD) && !defined(CMS_DLL) && !defined(CMS_DLL_BUILD) 
+#if defined(CMS_NO_REGISTER_KEYWORD)
 #  define CMSREGISTER
 #else
 #  define CMSREGISTER register
@@ -290,6 +290,7 @@
 // Base ICC type definitions
 typedef enum {
     cmsSigChromaticityType                  = 0x6368726D,  // 'chrm'
+    cmsSigcicpType                          = 0x63696370,  // 'cicp' 
     cmsSigColorantOrderType                 = 0x636C726F,  // 'clro'
     cmsSigColorantTableType                 = 0x636C7274,  // 'clrt'
     cmsSigCrdInfoType                       = 0x63726469,  // 'crdi'
@@ -401,6 +402,7 @@
     cmsSigViewingConditionsTag              = 0x76696577,  // 'view'
     cmsSigVcgtTag                           = 0x76636774,  // 'vcgt'
     cmsSigMetaTag                           = 0x6D657461,  // 'meta'
+    cmsSigcicpTag                           = 0x63696370,  // 'cicp'
     cmsSigArgyllArtsTag                     = 0x61727473   // 'arts'
 
 } cmsTagSignature;
@@ -1038,6 +1040,16 @@
 
     } cmsICCViewingConditions;
 
+typedef struct {
+    cmsUInt8Number  ColourPrimaries;            // Recommendation ITU-T H.273
+    cmsUInt8Number  TransferCharacteristics;    //  (ISO/IEC 23091-2)
+    cmsUInt8Number  MatrixCoefficients;
+    cmsUInt8Number  VideoFullRangeFlag;
+
+} cmsVideoSignalType;
+
+
+
 // Get LittleCMS version (for shared objects) -----------------------------------------------------------------------------
 
 CMSAPI int               CMSEXPORT cmsGetEncodedCMMversion(void);
@@ -1520,8 +1532,12 @@
 CMSAPI cmsColorSpaceSignature   CMSEXPORT _cmsICCcolorSpace(int OurNotation);
 CMSAPI int                      CMSEXPORT _cmsLCMScolorSpace(cmsColorSpaceSignature ProfileSpace);
 
+// Deprecated, use cmsChannelsOfColorSpace instead
 CMSAPI cmsUInt32Number   CMSEXPORT cmsChannelsOf(cmsColorSpaceSignature ColorSpace);
 
+// Get number of channels of color space or -1 if color space is not listed/supported
+CMSAPI cmsInt32Number CMSEXPORT cmsChannelsOfColorSpace(cmsColorSpaceSignature ColorSpace);
+
 // Build a suitable formatter for the colorspace of this profile. nBytes=1 means 8 bits, nBytes=2 means 16 bits. 
 CMSAPI cmsUInt32Number   CMSEXPORT cmsFormatterForColorspaceOfProfile(cmsHPROFILE hProfile, cmsUInt32Number nBytes, cmsBool lIsFloat);
 CMSAPI cmsUInt32Number   CMSEXPORT cmsFormatterForPCSOfProfile(cmsHPROFILE hProfile, cmsUInt32Number nBytes, cmsBool lIsFloat);
diff --git a/third_party/lcms/include/lcms2_plugin.h b/third_party/lcms/include/lcms2_plugin.h
index 27fdb6a..33540b8 100644
--- a/third_party/lcms/include/lcms2_plugin.h
+++ b/third_party/lcms/include/lcms2_plugin.h
@@ -209,6 +209,7 @@
 #define cmsPluginOptimizationSig             0x6F707448     // 'optH'
 #define cmsPluginTransformSig                0x7A666D48     // 'xfmH'
 #define cmsPluginMutexSig                    0x6D747A48     // 'mtxH'
+#define cmsPluginParalellizationSig          0x70726C48     // 'prlH
 
 typedef struct _cmsPluginBaseStruct {
 
@@ -596,7 +597,7 @@
                                      const void* InputBuffer,
                                      void* OutputBuffer,
                                      cmsUInt32Number Size,
-                                     cmsUInt32Number Stride);                 // Stride in bytes to the next plana in planar formats
+                                     cmsUInt32Number Stride);                 // Stride in bytes to the next plane in planar formats
 
 
 typedef void     (*_cmsTransform2Fn)(struct _cmstransform_struct *CMMcargo,
@@ -669,6 +670,25 @@
 CMSAPI cmsBool CMSEXPORT _cmsLockMutex(cmsContext ContextID, void* mtx);
 CMSAPI void    CMSEXPORT _cmsUnlockMutex(cmsContext ContextID, void* mtx);
 
+//----------------------------------------------------------------------------------------------------------
+// Parallelization 
+
+CMSAPI _cmsTransform2Fn CMSEXPORT _cmsGetTransformWorker(struct _cmstransform_struct* CMMcargo);
+CMSAPI cmsInt32Number   CMSEXPORT _cmsGetTransformMaxWorkers(struct _cmstransform_struct* CMMcargo);
+CMSAPI cmsUInt32Number  CMSEXPORT _cmsGetTransformWorkerFlags(struct _cmstransform_struct* CMMcargo);
+
+// Let's plug-in to guess the best number of workers
+#define CMS_GUESS_MAX_WORKERS -1
+
+typedef struct {
+    cmsPluginBase       base;
+
+    cmsInt32Number      MaxWorkers;       // Number of starts to do as maximum
+    cmsUInt32Number     WorkerFlags;      // Reserved
+    _cmsTransform2Fn    SchedulerFn;      // callback to setup functions     
+
+}  cmsPluginParalellization;
+
 
 #ifndef CMS_USE_CPP_API
 #   ifdef __cplusplus
diff --git a/third_party/lcms/src/cmscgats.c b/third_party/lcms/src/cmscgats.c
index 241a88f..352f126 100644
--- a/third_party/lcms/src/cmscgats.c
+++ b/third_party/lcms/src/cmscgats.c
@@ -370,6 +370,7 @@
 string* StringAlloc(cmsIT8* it8, int max)
 {
     string* s = (string*) AllocChunk(it8, sizeof(string));
+    if (s == NULL) return NULL;
 
     s->it8 = it8;
     s->max = max;
@@ -393,13 +394,18 @@
         char* new_ptr;
 
         s->max *= 10;
-        new_ptr = AllocChunk(s->it8, s->max);
-        memcpy(new_ptr, s->begin, s->len);
+        new_ptr = (char*) AllocChunk(s->it8, s->max);
+        if (new_ptr != NULL && s->begin != NULL)
+            memcpy(new_ptr, s->begin, s->len);
+
         s->begin = new_ptr;
     }
 
-    s->begin[s->len++] = c;
-    s->begin[s->len] = 0;
+    if (s->begin != NULL)
+    {
+        s->begin[s->len++] = c;
+        s->begin[s->len] = 0;
+    }
 }
 
 static
@@ -836,6 +842,7 @@
                             if ((cmsFloat64Number) it8->inum * 16.0 + (cmsFloat64Number) j > (cmsFloat64Number)+2147483647.0)
                             {
                                 SynError(it8, "Invalid hexadecimal number");
+                                it8->sy = SEOF;
                                 return;
                             }
 
@@ -857,6 +864,7 @@
                             if ((cmsFloat64Number) it8->inum * 2.0 + j > (cmsFloat64Number)+2147483647.0)
                             {
                                 SynError(it8, "Invalid binary number");
+                                it8->sy = SEOF;
                                 return;
                             }
 
@@ -925,13 +933,9 @@
             }
             else
                 switch ((int) it8->ch) {
-
-        // EOF marker -- ignore it
-        case '\x1a':
-            NextCh(it8);
-            break;
-
+        
         // Eof stream markers
+        case '\x1a':
         case 0:
         case -1:
             it8->sy = SEOF;
@@ -971,6 +975,7 @@
 
         default:
             SynError(it8, "Unrecognized character: 0x%x", it8 ->ch);
+            it8->sy = SEOF;
             return;
             }
 
@@ -985,24 +990,33 @@
                 if(it8 -> IncludeSP >= (MAXINCLUDE-1)) {
 
                     SynError(it8, "Too many recursion levels");
+                    it8->sy = SEOF;
                     return;
                 }
 
                 InStringSymbol(it8);
-                if (!Check(it8, SSTRING, "Filename expected")) return;
+                if (!Check(it8, SSTRING, "Filename expected"))
+                {
+                    it8->sy = SEOF;
+                    return;
+                }
 
                 FileNest = it8 -> FileStack[it8 -> IncludeSP + 1];
                 if(FileNest == NULL) {
 
                     FileNest = it8 ->FileStack[it8 -> IncludeSP + 1] = (FILECTX*)AllocChunk(it8, sizeof(FILECTX));
-                    //if(FileNest == NULL)
-                    //  TODO: how to manage out-of-memory conditions?
+                    if (FileNest == NULL) {
+                        SynError(it8, "Out of memory");
+                        it8->sy = SEOF;
+                        return;
+                    }
                 }
 
                 if (BuildAbsolutePath(StringPtr(it8->str),
                                       it8->FileStack[it8->IncludeSP]->FileName,
                                       FileNest->FileName, cmsMAX_PATH-1) == FALSE) {
                     SynError(it8, "File path too long");
+                    it8->sy = SEOF;
                     return;
                 }
 
@@ -1010,6 +1024,7 @@
                 if (FileNest->Stream == NULL) {
 
                         SynError(it8, "File %s not found", FileNest->FileName);
+                        it8->sy = SEOF;
                         return;
                 }
                 it8->IncludeSP++;
@@ -1170,9 +1185,12 @@
                 it8 ->Allocator.BlockSize = size;
 
         it8 ->Allocator.Used = 0;
-        it8 ->Allocator.Block = (cmsUInt8Number*)  AllocBigBlock(it8, it8 ->Allocator.BlockSize);
+        it8 ->Allocator.Block = (cmsUInt8Number*) AllocBigBlock(it8, it8 ->Allocator.BlockSize);       
     }
 
+    if (it8->Allocator.Block == NULL)
+        return NULL;
+
     ptr = it8 ->Allocator.Block + it8 ->Allocator.Used;
     it8 ->Allocator.Used += size;
 
@@ -1513,28 +1531,45 @@
 
 // ----------------------------------------------------------------- Datasets
 
+// A safe atoi that returns 0 when NULL input is given
+static
+cmsInt32Number satoi(const char* b)
+{
+    int n;
+
+    if (b == NULL) return 0;
+
+    n = atoi(b);
+    if (n > 0x7fffffffL) return 0x7fffffffL;
+    if (n < -0x7ffffffeL) return -0x7ffffffeL;
+
+    return (cmsInt32Number)n;
+}
+
 
 static
-void AllocateDataFormat(cmsIT8* it8)
+cmsBool AllocateDataFormat(cmsIT8* it8)
 {
     TABLE* t = GetTable(it8);
 
-    if (t -> DataFormat) return;    // Already allocated
+    if (t -> DataFormat) return TRUE;    // Already allocated
 
-    t -> nSamples  = (int) cmsIT8GetPropertyDbl(it8, "NUMBER_OF_FIELDS");
+    t -> nSamples  = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
 
     if (t -> nSamples <= 0) {
 
         SynError(it8, "AllocateDataFormat: Unknown NUMBER_OF_FIELDS");
-        t -> nSamples = 10;
+        return FALSE;        
         }
 
     t -> DataFormat = (char**) AllocChunk (it8, ((cmsUInt32Number) t->nSamples + 1) * sizeof(char *));
     if (t->DataFormat == NULL) {
 
         SynError(it8, "AllocateDataFormat: Unable to allocate dataFormat array");
+        return FALSE;
     }
 
+    return TRUE;
 }
 
 static
@@ -1553,8 +1588,11 @@
 {
     TABLE* t = GetTable(it8);
 
-    if (!t->DataFormat)
-        AllocateDataFormat(it8);
+    if (!t->DataFormat) {
+
+        if (!AllocateDataFormat(it8))
+            return FALSE;
+    }
 
     if (n > t -> nSamples) {
         SynError(it8, "More than NUMBER_OF_FIELDS fields.");
@@ -1563,6 +1601,7 @@
 
     if (t->DataFormat) {
         t->DataFormat[n] = AllocString(it8, label);
+        if (t->DataFormat[n] == NULL) return FALSE;
     }
 
     return TRUE;
@@ -1575,14 +1614,6 @@
     return SetDataFormat(it8, n, Sample);
 }
 
-// A safe atoi that returns 0 when NULL input is given
-static
-cmsInt32Number satoi(const char* b)
-{
-    if (b == NULL) return 0;
-    return atoi(b);
-}
-
 // Convert to binary
 static
 const char* satob(const char* v)
@@ -1603,11 +1634,11 @@
 
 
 static
-void AllocateDataSet(cmsIT8* it8)
+cmsBool AllocateDataSet(cmsIT8* it8)
 {
     TABLE* t = GetTable(it8);
 
-    if (t -> Data) return;    // Already allocated
+    if (t -> Data) return TRUE;    // Already allocated
 
     t-> nSamples   = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
     t-> nPatches   = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS"));
@@ -1615,6 +1646,7 @@
     if (t -> nSamples < 0 || t->nSamples > 0x7ffe || t->nPatches < 0 || t->nPatches > 0x7ffe)
     {
         SynError(it8, "AllocateDataSet: too much data");
+        return FALSE;
     }
     else {
         // Some dumb analizers warns of possible overflow here, just take a look couple of lines above.
@@ -1622,9 +1654,11 @@
         if (t->Data == NULL) {
 
             SynError(it8, "AllocateDataSet: Unable to allocate data array");
+            return FALSE;
         }
     }
 
+    return TRUE;
 }
 
 static
@@ -1646,8 +1680,9 @@
 {
     TABLE* t = GetTable(it8);
 
-    if (!t->Data)
-        AllocateDataSet(it8);
+    if (!t->Data) {
+        if (!AllocateDataSet(it8)) return FALSE;
+    }
 
     if (!t->Data) return FALSE;
 
@@ -1990,8 +2025,9 @@
     InSymbol(it8);   // Eats "BEGIN_DATA"
     CheckEOLN(it8);
 
-    if (!t->Data)
-        AllocateDataSet(it8);
+    if (!t->Data) {
+        if (!AllocateDataSet(it8)) return FALSE;
+    }
 
     while (it8->sy != SEND_DATA && it8->sy != SEOF)
     {
@@ -2324,19 +2360,19 @@
                             snprintf(Buffer, 255, "%s %d %s", Label, nTable, Type);
 
                             SetData(it8, i, idField, Buffer);
-                        }
-                    }
+						}
+					}
 
 
-                }
+				}
 
-            }
+			}
 
 
-        }
+		}
 
-    }
-    }
+	}
+	}
 
     it8 ->nTable = nOldTable;
 }
@@ -2538,15 +2574,18 @@
     }
 
 
-    Props = (char **) AllocChunk(it8, sizeof(char *) * n);
+	Props = (char**)AllocChunk(it8, sizeof(char*) * n);
+	if (Props != NULL) {
 
-    // Pass#2 - Fill pointers
-    n = 0;
-    for (p = t -> HeaderList;  p != NULL; p = p->Next) {
-        Props[n++] = p -> Keyword;
-    }
+		// Pass#2 - Fill pointers
+		n = 0;
+		for (p = t->HeaderList; p != NULL; p = p->Next) {
+			Props[n++] = p->Keyword;
+		}
 
-    *PropertyNames = Props;
+	}
+	*PropertyNames = Props;
+
     return n;
 }
 
@@ -2578,12 +2617,14 @@
 
 
     Props = (const char **) AllocChunk(it8, sizeof(char *) * n);
+    if (Props != NULL) {
 
-    // Pass#2 - Fill pointers
-    n = 0;
-    for (tmp = p;  tmp != NULL; tmp = tmp->NextSubkey) {
-        if(tmp->Subkey != NULL)
-            Props[n++] = p ->Subkey;
+        // Pass#2 - Fill pointers
+        n = 0;
+        for (tmp = p; tmp != NULL; tmp = tmp->NextSubkey) {
+            if (tmp->Subkey != NULL)
+                Props[n++] = p->Subkey;
+        }
     }
 
     *SubpropertyNames = Props;
@@ -2759,8 +2800,12 @@
 
     if (t-> nPatches == 0) {
 
-        AllocateDataFormat(it8);
-        AllocateDataSet(it8);
+        if (!AllocateDataFormat(it8))
+            return FALSE;
+
+        if (!AllocateDataSet(it8))
+            return FALSE;
+
         CookPointers(it8);
     }
 
diff --git a/third_party/lcms/src/cmscnvrt.c b/third_party/lcms/src/cmscnvrt.c
index fe25525..50e5a1e 100644
--- a/third_party/lcms/src/cmscnvrt.c
+++ b/third_party/lcms/src/cmscnvrt.c
@@ -386,7 +386,7 @@
 
         if (BPC) {
 
-            cmsCIEXYZ BlackPointIn, BlackPointOut;
+            cmsCIEXYZ BlackPointIn = { 0, 0, 0}, BlackPointOut = { 0, 0, 0 };
 
             cmsDetectBlackPoint(&BlackPointIn,  hProfiles[i-1], Intent, 0);
             cmsDetectDestinationBlackPoint(&BlackPointOut, hProfiles[i], Intent, 0);
@@ -630,7 +630,7 @@
                   ColorSpaceOut == cmsSigRgbData ||
                   ColorSpaceOut == cmsSigCmykData) {
 
-                  cmsStage* clip = _cmsStageClipNegatives(Result->ContextID, cmsChannelsOf(ColorSpaceOut));
+                  cmsStage* clip = _cmsStageClipNegatives(Result->ContextID, cmsChannelsOfColorSpace(ColorSpaceOut));
                   if (clip == NULL) goto Error;
 
                   if (!cmsPipelineInsertStage(Result, cmsAT_END, clip))
diff --git a/third_party/lcms/src/cmserr.c b/third_party/lcms/src/cmserr.c
index c65dce5..29fe9ca 100644
--- a/third_party/lcms/src/cmserr.c
+++ b/third_party/lcms/src/cmserr.c
@@ -471,7 +471,6 @@
     if (Plugin ->CreateMutexPtr == NULL || Plugin ->DestroyMutexPtr == NULL || 
         Plugin ->LockMutexPtr == NULL || Plugin ->UnlockMutexPtr == NULL) return FALSE;
 
-
     ctx->CreateMutexPtr  = Plugin->CreateMutexPtr;
     ctx->DestroyMutexPtr = Plugin ->DestroyMutexPtr;
     ctx ->LockMutexPtr   = Plugin ->LockMutexPtr;
@@ -519,3 +518,47 @@
         ptr ->UnlockMutexPtr(ContextID, mtx);
     }
 }
+
+// The global Context0 storage for parallelization plug-in
+ _cmsParallelizationPluginChunkType _cmsParallelizationPluginChunk = { 0 };
+
+// Allocate parallelization container.
+void _cmsAllocParallelizationPluginChunk(struct _cmsContext_struct* ctx,
+                                         const struct _cmsContext_struct* src)
+{    
+    if (src != NULL) {
+        void* from = src->chunks[ParallelizationPlugin];
+        ctx->chunks[ParallelizationPlugin] = _cmsSubAllocDup(ctx->MemPool, from, sizeof(_cmsParallelizationPluginChunkType));
+    }
+    else {        
+        _cmsParallelizationPluginChunkType ParallelizationPluginChunk = { 0 };
+        ctx->chunks[ParallelizationPlugin] = _cmsSubAllocDup(ctx->MemPool, &ParallelizationPluginChunk, sizeof(_cmsParallelizationPluginChunkType));
+    }         
+}
+
+// Register parallel processing
+cmsBool _cmsRegisterParallelizationPlugin(cmsContext ContextID, cmsPluginBase* Data)
+{
+    cmsPluginParalellization* Plugin = (cmsPluginParalellization*)Data;
+    _cmsParallelizationPluginChunkType* ctx = (_cmsParallelizationPluginChunkType*)_cmsContextGetClientChunk(ContextID, ParallelizationPlugin);
+
+    if (Data == NULL) {
+
+        // No parallelization routines
+        ctx->MaxWorkers = 0;
+        ctx->WorkerFlags = 0;
+        ctx->SchedulerFn = NULL;        
+        return TRUE;
+    }
+
+    // callback is required
+    if (Plugin->SchedulerFn == NULL) return FALSE;
+
+    ctx->MaxWorkers = Plugin->MaxWorkers;
+    ctx->WorkerFlags = Plugin->WorkerFlags;
+    ctx->SchedulerFn = Plugin->SchedulerFn;
+    
+    // All is ok
+    return TRUE;
+}
+
diff --git a/third_party/lcms/src/cmsgamma.c b/third_party/lcms/src/cmsgamma.c
index dee1b4b..1dc3244 100644
--- a/third_party/lcms/src/cmsgamma.c
+++ b/third_party/lcms/src/cmsgamma.c
@@ -427,8 +427,8 @@
 
 
     // IEC 61966-3
-    // Y = (aX + b)^Gamma | X <= -b/a
-    // Y = c              | else
+    // Y = (aX + b)^Gamma + c | X <= -b/a
+    // Y = c                  | else
     case 3:
     {
         if (fabs(Params[1]) < MATRIX_DET_TOLERANCE)
@@ -462,7 +462,8 @@
     // X=-b/a                   | (Y<c)
     case -3:
     {
-        if (fabs(Params[1]) < MATRIX_DET_TOLERANCE)
+        if (fabs(Params[0]) < MATRIX_DET_TOLERANCE ||
+            fabs(Params[1]) < MATRIX_DET_TOLERANCE)
         {
             Val = 0;
         }
@@ -601,7 +602,8 @@
     // ((Y - c) ^1/Gamma - b) / a
     case -6:
     {
-        if (fabs(Params[1]) < MATRIX_DET_TOLERANCE)
+        if (fabs(Params[0]) < MATRIX_DET_TOLERANCE ||
+            fabs(Params[1]) < MATRIX_DET_TOLERANCE)
         {
             Val = 0;
         }
@@ -1488,6 +1490,9 @@
         }
     }
 
+    // We need enough valid samples
+    if (n <= 1) return -1.0;
+
     // Take a look on SD to see if gamma isn't exponential at all
     Std = sqrt((n * sum2 - sum * sum) / (n*(n-1)));
 
diff --git a/third_party/lcms/src/cmsgmt.c b/third_party/lcms/src/cmsgmt.c
index 70f2803..5109bfa 100644
--- a/third_party/lcms/src/cmsgmt.c
+++ b/third_party/lcms/src/cmsgmt.c
@@ -297,7 +297,8 @@
     cmsStage* CLUT;
     cmsUInt32Number dwFormat;
     GAMUTCHAIN Chain;
-    cmsUInt32Number nChannels, nGridpoints;
+    cmsUInt32Number nGridpoints;
+    cmsInt32Number nChannels;
     cmsColorSpaceSignature ColorSpace;
     cmsUInt32Number i;
     cmsHPROFILE ProfileList[256];
@@ -346,8 +347,7 @@
 
 
     ColorSpace  = cmsGetColorSpace(hGamut);
-
-    nChannels   = cmsChannelsOf(ColorSpace);
+    nChannels   = cmsChannelsOfColorSpace(ColorSpace);
     nGridpoints = _cmsReasonableGridpointsByColorspace(ColorSpace, cmsFLAGS_HIGHRESPRECALC);
     dwFormat    = (CHANNELS_SH(nChannels)|BYTES_SH(2));
 
@@ -472,6 +472,9 @@
     // Create a fake formatter for result
     dwFormatter = cmsFormatterForColorspaceOfProfile(hProfile, 4, TRUE);
 
+    // Unsupported color space?
+    if (dwFormatter == 0) return 0;
+
     bp.nOutputChans = T_CHANNELS(dwFormatter);
     bp.MaxTAC = 0;    // Initial TAC is 0
 
@@ -619,6 +622,8 @@
 
     ContextID = cmsGetProfileContextID(hProfile);
     hXYZ = cmsCreateXYZProfileTHR(ContextID);
+    if (hXYZ == NULL)
+        return -1;
     xform = cmsCreateTransformTHR(ContextID, hProfile, TYPE_RGB_16, hXYZ, TYPE_XYZ_DBL, 
                                     INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOOPTIMIZE);
 
diff --git a/third_party/lcms/src/cmsintrp.c b/third_party/lcms/src/cmsintrp.c
index 5cd6d16..5eb98e6 100644
--- a/third_party/lcms/src/cmsintrp.c
+++ b/third_party/lcms/src/cmsintrp.c
@@ -278,10 +278,10 @@
        // if last value...
        if (Input[0] == 0xffff || p16->Domain[0] == 0) {
 
-           cmsUInt16Number y0 = LutTable[p16->Domain[0]];
-
+           cmsUInt32Number y0 = p16->Domain[0] * p16->opta[0];
+           
            for (OutChan = 0; OutChan < p16->nOutputs; OutChan++) {
-               Output[OutChan] = y0;
+               Output[OutChan] = LutTable[y0 + OutChan];
            }
        }
        else
@@ -324,10 +324,10 @@
     // if last value...
     if (val2 == 1.0 || p->Domain[0] == 0) {
 
-        y0 = LutTable[p->Domain[0]];
+        cmsUInt32Number start = p->Domain[0] * p->opta[0];
 
         for (OutChan = 0; OutChan < p->nOutputs; OutChan++) {
-            Output[OutChan] = y0;
+            Output[OutChan] = LutTable[start + OutChan];
         }        
     }
     else
diff --git a/third_party/lcms/src/cmsio0.c b/third_party/lcms/src/cmsio0.c
index 0724eaa..413b106 100644
--- a/third_party/lcms/src/cmsio0.c
+++ b/third_party/lcms/src/cmsio0.c
@@ -374,24 +374,58 @@
 {
     cmsIOHANDLER* iohandler = NULL;
     FILE* fm = NULL;
-    cmsInt32Number fileLen;
+    cmsInt32Number fileLen;    
+    char mode[4] = { 0,0,0,0 };
 
     _cmsAssert(FileName != NULL);
     _cmsAssert(AccessMode != NULL);
 
     iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER));
     if (iohandler == NULL) return NULL;
+           
+    // Validate access mode
+    while (*AccessMode) {
 
-    switch (*AccessMode) {
+        switch (*AccessMode)
+        {        
+        case 'r':
+        case 'w':
+
+            if (mode[0] == 0) {
+                mode[0] = *AccessMode;
+                mode[1] = 'b';                
+            }
+            else {
+                _cmsFree(ContextID, iohandler);
+                cmsSignalError(ContextID, cmsERROR_FILE, "Access mode already specified '%c'", *AccessMode);
+                return NULL;
+            }
+            break;
+
+        // Close on exec. Not all runtime supports that. Up to the caller to decide.
+        case 'e':
+            mode[2] = 'e';
+            break;
+
+        default:
+            _cmsFree(ContextID, iohandler);
+            cmsSignalError(ContextID, cmsERROR_FILE, "Wrong access mode '%c'", *AccessMode);
+            return NULL;
+        }
+
+        AccessMode++;
+    }
+        
+    switch (mode[0]) {
 
     case 'r':
-        fm = fopen(FileName, "rb");
+        fm = fopen(FileName, mode);
         if (fm == NULL) {
             _cmsFree(ContextID, iohandler);
              cmsSignalError(ContextID, cmsERROR_FILE, "File '%s' not found", FileName);
             return NULL;
         }                                     
-        fileLen = cmsfilelength(fm);
+        fileLen = (cmsInt32Number)cmsfilelength(fm);
         if (fileLen < 0)
         {
             fclose(fm);
@@ -399,12 +433,11 @@
             cmsSignalError(ContextID, cmsERROR_FILE, "Cannot get size of file '%s'", FileName);
             return NULL;
         }
-
         iohandler -> ReportedSize = (cmsUInt32Number) fileLen;
         break;
 
     case 'w':
-        fm = fopen(FileName, "wb");
+        fm = fopen(FileName, mode);
         if (fm == NULL) {
             _cmsFree(ContextID, iohandler);
              cmsSignalError(ContextID, cmsERROR_FILE, "Couldn't create '%s'", FileName);
@@ -414,8 +447,7 @@
         break;
 
     default:
-        _cmsFree(ContextID, iohandler);
-         cmsSignalError(ContextID, cmsERROR_FILE, "Unknown access mode '%c'", *AccessMode);
+        _cmsFree(ContextID, iohandler);   // Would never reach      
         return NULL;
     }
 
@@ -442,7 +474,7 @@
     cmsIOHANDLER* iohandler = NULL;
     cmsInt32Number fileSize;
 
-    fileSize = cmsfilelength(Stream);
+    fileSize = (cmsInt32Number)cmsfilelength(Stream);
     if (fileSize < 0)
     {
         cmsSignalError(ContextID, cmsERROR_FILE, "Cannot get size of stream");
@@ -507,6 +539,9 @@
     // Set default version
     Icc ->Version =  0x02100000;
     
+    // Set default device class
+    Icc->DeviceClass = cmsSigDisplayClass;
+
     // Set creation date/time
     if (!_cmsGetTime(&Icc->Created))
         goto Error;
@@ -673,6 +708,27 @@
        return _cmsSearchTag(Icc, sig, FALSE) >= 0;
 }
 
+
+
+// Checks for link compatibility
+static
+cmsBool CompatibleTypes(const cmsTagDescriptor* desc1, const cmsTagDescriptor* desc2)
+{
+    cmsUInt32Number i;
+
+    if (desc1 == NULL || desc2 == NULL) return FALSE;
+
+    if (desc1->nSupportedTypes != desc2->nSupportedTypes) return FALSE;
+    if (desc1->ElemCount != desc2->ElemCount) return FALSE;
+
+    for (i = 0; i < desc1->nSupportedTypes; i++)
+    {
+        if (desc1->SupportedTypes[i] != desc2->SupportedTypes[i]) return FALSE;
+    }
+
+    return TRUE;
+}
+
 // Enforces that the profile version is per. spec.
 // Operates on the big endian bytes from the profile.
 // Called before converting to platform endianness.
@@ -698,6 +754,29 @@
     return DWord;
 }
 
+// Check device class
+static 
+cmsBool validDeviceClass(cmsProfileClassSignature cl)
+{
+    if ((int)cl == 0) return TRUE; // We allow zero because older lcms versions defaulted to that.
+
+    switch (cl)
+    {    
+    case cmsSigInputClass:
+    case cmsSigDisplayClass:
+    case cmsSigOutputClass:
+    case cmsSigLinkClass:
+    case cmsSigAbstractClass:
+    case cmsSigColorSpaceClass:
+    case cmsSigNamedColorClass:
+        return TRUE;
+
+    default:
+        return FALSE;
+    }
+
+}
+
 // Read profile header and validate it
 cmsBool _cmsReadHeader(_cmsICCPROFILE* Icc)
 {
@@ -734,6 +813,16 @@
     _cmsAdjustEndianess64(&Icc -> attributes, &Header.attributes);
     Icc -> Version         = _cmsAdjustEndianess32(_validatedVersion(Header.version));
 
+    if (Icc->Version > 0x5000000) {
+        cmsSignalError(Icc->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported profile version '0x%x'", Icc->Version);
+        return FALSE;
+    }
+
+    if (!validDeviceClass(Icc->DeviceClass)) {
+        cmsSignalError(Icc->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported device class '0x%x'", Icc->DeviceClass);
+        return FALSE;
+    }
+
     // Get size as reported in header
     HeaderSize = _cmsAdjustEndianess32(Header.size);
 
@@ -767,6 +856,7 @@
         if (!_cmsReadUInt32Number(io, &Tag.size)) return FALSE;
 
         // Perform some sanity check. Offset + size should fall inside file.
+        if (Tag.size == 0 || Tag.offset == 0) continue;
         if (Tag.offset + Tag.size > HeaderSize ||
             Tag.offset + Tag.size < Tag.offset)
                   continue;
@@ -777,12 +867,17 @@
 
        // Search for links
         for (j=0; j < Icc ->TagCount; j++) {
-
+           
             if ((Icc ->TagOffsets[j] == Tag.offset) &&
                 (Icc ->TagSizes[j]   == Tag.size) &&
                 (Icc ->TagNames[j]   == Tag.sig)) {
 
-                Icc ->TagLinked[Icc ->TagCount] = Icc ->TagNames[j];
+                // Check types. 
+                if (CompatibleTypes(_cmsGetTagDescriptor(Icc->ContextID, Icc->TagNames[j]),
+                                    _cmsGetTagDescriptor(Icc->ContextID, Tag.sig))) {
+
+                    Icc->TagLinked[Icc->TagCount] = Icc->TagNames[j];
+                }
             }
 
         }
@@ -790,6 +885,19 @@
         Icc ->TagCount++;
     }
 
+
+    for (i = 0; i < Icc->TagCount; i++) {
+        for (j = 0; j < Icc->TagCount; j++) {
+
+            // Tags cannot be duplicate
+            if ((i != j) && (Icc->TagNames[i] == Icc->TagNames[j])) {
+                cmsSignalError(Icc->ContextID, cmsERROR_RANGE, "Duplicate tag found");
+                return FALSE;
+            }
+
+        }
+    }
+
     return TRUE;
 }
 
@@ -1581,6 +1689,13 @@
     if (TagSize < 8) goto Error;
 
     io = Icc ->IOhandler;
+
+    if (io == NULL) { // This is a built-in profile that has been manipulated, abort early
+
+        cmsSignalError(Icc->ContextID, cmsERROR_CORRUPTION_DETECTED, "Corrupted built-in profile.");
+        goto Error;
+    }
+
     // Seek to its location
     if (!io -> Seek(io, Offset))
         goto Error;
@@ -1792,11 +1907,9 @@
 
 }
 
-// Read and write raw data. The only way those function would work and keep consistence with normal read and write
-// is to do an additional step of serialization. That means, readRaw would issue a normal read and then convert the obtained
-// data to raw bytes by using the "write" serialization logic. And vice-versa. I know this may end in situations where
-// raw data written does not exactly correspond with the raw data proposed to cmsWriteRaw data, but this approach allows
-// to write a tag as raw data and the read it as handled.
+// Read and write raw data. Read/Write Raw/cooked pairs try to maintain consistency within the pair. Some sequences
+// raw/cooked would work, but at a cost. Data "cooked" may be converted to "raw" by using the "write" serialization logic.
+// In general it is better to avoid mixing pairs.
 
 cmsUInt32Number CMSEXPORT cmsReadRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, void* data, cmsUInt32Number BufferSize)
 {
@@ -1810,9 +1923,13 @@
     cmsUInt32Number rc;
     cmsUInt32Number Offset, TagSize;
 
+    // Sanity check
+    if (data != NULL && BufferSize == 0) return 0;
+
     if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0;
 
     // Search for given tag in ICC profile directory
+    
     i = _cmsSearchTag(Icc, sig, TRUE);
     if (i < 0) goto Error;                 // Not found, 
 
@@ -1824,10 +1941,11 @@
         TagSize  = Icc ->TagSizes[i];
 
         // read the data directly, don't keep copy
+        
         if (data != NULL) {
 
             if (BufferSize < TagSize)
-                TagSize = BufferSize;
+                goto Error;
 
             if (!Icc ->IOhandler ->Seek(Icc ->IOhandler, Offset)) goto Error;
             if (!Icc ->IOhandler ->Read(Icc ->IOhandler, data, 1, TagSize)) goto Error;
@@ -1842,13 +1960,14 @@
 
     // The data has been already read, or written. But wait!, maybe the user chose to save as
     // raw data. In this case, return the raw data directly
+    
     if (Icc ->TagSaveAsRaw[i]) {
 
         if (data != NULL)  {
 
             TagSize  = Icc ->TagSizes[i];
             if (BufferSize < TagSize)
-                TagSize = BufferSize;
+                goto Error;
 
             memmove(data, Icc ->TagPtrs[i], TagSize);
 
@@ -1861,8 +1980,8 @@
     }
 
     // Already read, or previously set by cmsWriteTag(). We need to serialize that
-    // data to raw in order to maintain consistency.
-
+    // data to raw to get something that makes sense
+    
     _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
     Object = cmsReadTag(hProfile, sig);
     if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0;
diff --git a/third_party/lcms/src/cmsio1.c b/third_party/lcms/src/cmsio1.c
index db18b9f..2d7deee 100644
--- a/third_party/lcms/src/cmsio1.c
+++ b/third_party/lcms/src/cmsio1.c
@@ -322,10 +322,8 @@
         if (nc == NULL) return NULL;
 
         Lut = cmsPipelineAlloc(ContextID, 0, 0);
-        if (Lut == NULL) {
-            cmsFreeNamedColorList(nc);
-            return NULL;
-        }
+        if (Lut == NULL)            
+            return NULL;        
 
         if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocNamedColor(nc, TRUE)) ||
             !cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) {
@@ -739,8 +737,7 @@
 
         return Lut;
     Error:
-        cmsPipelineFree(Lut);
-        cmsFreeNamedColorList(nc);
+        cmsPipelineFree(Lut);        
         return NULL;
     }
 
diff --git a/third_party/lcms/src/cmslut.c b/third_party/lcms/src/cmslut.c
index 49cc63b..152e6d6 100644
--- a/third_party/lcms/src/cmslut.c
+++ b/third_party/lcms/src/cmslut.c
@@ -467,7 +467,7 @@
     for (rv = 1; b > 0; b--) {
 
         dim = Dims[b-1];
-        if (dim == 0) return 0;  // Error
+        if (dim <= 1) return 0;  // Error
 
         rv *= dim;
 
diff --git a/third_party/lcms/src/cmsnamed.c b/third_party/lcms/src/cmsnamed.c
index 28e67f2..87f3936 100644
--- a/third_party/lcms/src/cmsnamed.c
+++ b/third_party/lcms/src/cmsnamed.c
@@ -542,10 +542,14 @@
 // Allocate a list for n elements
 cmsNAMEDCOLORLIST* CMSEXPORT cmsAllocNamedColorList(cmsContext ContextID, cmsUInt32Number n, cmsUInt32Number ColorantCount, const char* Prefix, const char* Suffix)
 {
-    cmsNAMEDCOLORLIST* v = (cmsNAMEDCOLORLIST*) _cmsMallocZero(ContextID, sizeof(cmsNAMEDCOLORLIST));
-
+    cmsNAMEDCOLORLIST* v;
+    
+    if (ColorantCount > cmsMAXCHANNELS) 
+        return NULL;
+   
+    v = (cmsNAMEDCOLORLIST*)_cmsMallocZero(ContextID, sizeof(cmsNAMEDCOLORLIST));
     if (v == NULL) return NULL;
-
+    
     v ->List      = NULL;
     v ->nColors   = 0;
     v ->ContextID  = ContextID;
diff --git a/third_party/lcms/src/cmsopt.c b/third_party/lcms/src/cmsopt.c
index 3eb8f62..f65fc87 100644
--- a/third_party/lcms/src/cmsopt.c
+++ b/third_party/lcms/src/cmsopt.c
@@ -1550,10 +1550,10 @@
 }
 
 
-// A fast matrix-shaper evaluator for 8 bits. This is a bit ticky since I'm using 1.14 signed fixed point
+// A fast matrix-shaper evaluator for 8 bits. This is a bit tricky since I'm using 1.14 signed fixed point
 // to accomplish some performance. Actually it takes 256x3 16 bits tables and 16385 x 3 tables of 8 bits,
 // in total about 50K, and the performance boost is huge!
-static
+static CMS_NO_SANITIZE
 void MatShaperEval16(CMSREGISTER const cmsUInt16Number In[],
                      CMSREGISTER cmsUInt16Number Out[],
                      CMSREGISTER const void* D)
diff --git a/third_party/lcms/src/cmspack.c b/third_party/lcms/src/cmspack.c
index 955687c..7f55ce3 100644
--- a/third_party/lcms/src/cmspack.c
+++ b/third_party/lcms/src/cmspack.c
@@ -592,8 +592,11 @@
         if (SwapEndian)
             v = CHANGE_ENDIAN(v);
 
-        v = (v << 16) / alpha_factor;
-        if (v > 0xffff) v = 0xffff;
+        if (alpha_factor > 0) {
+
+            v = (v << 16) / alpha_factor;
+            if (v > 0xffff) v = 0xffff;
+        }
 
         wIn[index] = (cmsUInt16Number) (Reverse ? REVERSE_FLAVOR_16(v) : v);
 
@@ -674,8 +677,11 @@
         if (SwapEndian)
             v = CHANGE_ENDIAN(v);
 
-        v = (v << 16) / alpha_factor;
-        if (v > 0xffff) v = 0xffff;
+        if (alpha_factor > 0) {
+
+            v = (v << 16) / alpha_factor;
+            if (v > 0xffff) v = 0xffff;
+        }
 
         wIn[index] = (cmsUInt16Number) (Reverse ? REVERSE_FLAVOR_16(v) : v);
 
@@ -3799,6 +3805,11 @@
     _cmsFormattersPluginChunkType* ctx = ( _cmsFormattersPluginChunkType*) _cmsContextGetClientChunk(ContextID, FormattersPlugin);
     cmsFormattersFactoryList* f;
 
+    if (T_CHANNELS(Type) == 0) {
+        static const cmsFormatter nullFormatter = { 0 };
+        return nullFormatter;
+    }
+
     for (f =ctx->FactoryList; f != NULL; f = f ->Next) {
 
         cmsFormatter fn = f ->Factory(Type, Dir, dwFlags);
@@ -3833,9 +3844,12 @@
 
     cmsColorSpaceSignature ColorSpace      = cmsGetColorSpace(hProfile);
     cmsUInt32Number        ColorSpaceBits  = (cmsUInt32Number) _cmsLCMScolorSpace(ColorSpace);
-    cmsUInt32Number        nOutputChans    = cmsChannelsOf(ColorSpace);
+    cmsInt32Number         nOutputChans    = cmsChannelsOfColorSpace(ColorSpace);
     cmsUInt32Number        Float           = lIsFloat ? 1U : 0;
 
+    // Unsupported color space?
+    if (nOutputChans < 0) return 0;
+
     // Create a fake formatter for result
     return FLOAT_SH(Float) | COLORSPACE_SH(ColorSpaceBits) | BYTES_SH(nBytes) | CHANNELS_SH(nOutputChans);
 }
diff --git a/third_party/lcms/src/cmspcs.c b/third_party/lcms/src/cmspcs.c
index 3d6323a..a0f1952 100644
--- a/third_party/lcms/src/cmspcs.c
+++ b/third_party/lcms/src/cmspcs.c
@@ -874,7 +874,7 @@
 }
 
 
-cmsUInt32Number CMSEXPORT cmsChannelsOf(cmsColorSpaceSignature ColorSpace)
+cmsInt32Number CMSEXPORT cmsChannelsOfColorSpace(cmsColorSpaceSignature ColorSpace)
 {
     switch (ColorSpace) {
 
@@ -935,6 +935,16 @@
     case cmsSigMCHFData:
     case cmsSig15colorData: return 15;
 
-    default: return 3;
+    default: return -1;
     }
 }
+
+/**
+* DEPRECATED: Provided for compatibility only
+*/
+cmsUInt32Number CMSEXPORT cmsChannelsOf(cmsColorSpaceSignature ColorSpace)
+{
+    int n = cmsChannelsOfColorSpace(ColorSpace);
+    if (n < 0) return 3;
+    return (cmsUInt32Number)n;
+}
\ No newline at end of file
diff --git a/third_party/lcms/src/cmsplugin.c b/third_party/lcms/src/cmsplugin.c
index dbda3fd..7d038d2 100644
--- a/third_party/lcms/src/cmsplugin.c
+++ b/third_party/lcms/src/cmsplugin.c
@@ -168,18 +168,21 @@
 
 cmsBool CMSEXPORT  _cmsReadFloat32Number(cmsIOHANDLER* io, cmsFloat32Number* n)
 {
-    cmsUInt32Number tmp;
+    union typeConverter {
+        cmsUInt32Number integer;
+        cmsFloat32Number floating_point;
+    } tmp;
 
     _cmsAssert(io != NULL);
 
-    if (io->Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1)
+    if (io->Read(io, &tmp.integer, sizeof(cmsUInt32Number), 1) != 1)
         return FALSE;
 
     if (n != NULL) {
 
-        tmp = _cmsAdjustEndianess32(tmp);
-        *n = *(cmsFloat32Number*)(void*)&tmp;
-        
+        tmp.integer = _cmsAdjustEndianess32(tmp.integer);
+        *n = tmp.floating_point;
+
         // Safeguard which covers against absurd values
         if (*n > 1E+20 || *n < -1E+20) return FALSE;
 
@@ -305,13 +308,14 @@
 
 cmsBool CMSEXPORT  _cmsWriteFloat32Number(cmsIOHANDLER* io, cmsFloat32Number n)
 {
-    cmsUInt32Number tmp;
+    union typeConverter {
+        cmsUInt32Number integer;
+        cmsFloat32Number floating_point;
+    } tmp;
 
-    _cmsAssert(io != NULL);
-
-    tmp = *(cmsUInt32Number*) (void*) &n;
-    tmp = _cmsAdjustEndianess32(tmp);
-    if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1)
+    tmp.floating_point = n;
+    tmp.integer = _cmsAdjustEndianess32(tmp.integer);
+    if (io -> Write(io, sizeof(cmsUInt32Number), &tmp.integer) != 1)
             return FALSE;
 
     return TRUE;
@@ -621,6 +625,10 @@
                     if (!_cmsRegisterMutexPlugin(id, Plugin)) return FALSE;
                     break;
 
+                case cmsPluginParalellizationSig:
+                    if (!_cmsRegisterParallelizationPlugin(id, Plugin)) return FALSE;
+                    break;
+
                 default:
                     cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin type '%X'", Plugin -> Type);
                     return FALSE;
@@ -643,24 +651,25 @@
 // pointers structure. All global vars are referenced here.
 static struct _cmsContext_struct globalContext = {
 
-    NULL,                              // Not in the linked list
-    NULL,                              // No suballocator
-    {
-        NULL,                          //  UserPtr,            
-        &_cmsLogErrorChunk,            //  Logger,
-        &_cmsAlarmCodesChunk,          //  AlarmCodes,
-        &_cmsAdaptationStateChunk,     //  AdaptationState, 
-        &_cmsMemPluginChunk,           //  MemPlugin,
-        &_cmsInterpPluginChunk,        //  InterpPlugin,
-        &_cmsCurvesPluginChunk,        //  CurvesPlugin,
-        &_cmsFormattersPluginChunk,    //  FormattersPlugin,
-        &_cmsTagTypePluginChunk,       //  TagTypePlugin,
-        &_cmsTagPluginChunk,           //  TagPlugin,
-        &_cmsIntentsPluginChunk,       //  IntentPlugin,
-        &_cmsMPETypePluginChunk,       //  MPEPlugin,
-        &_cmsOptimizationPluginChunk,  //  OptimizationPlugin,
-        &_cmsTransformPluginChunk,     //  TransformPlugin,
-        &_cmsMutexPluginChunk          //  MutexPlugin
+    NULL,                                // Not in the linked list
+    NULL,                                // No suballocator
+    {                                    
+        NULL,                            //  UserPtr,            
+        &_cmsLogErrorChunk,              //  Logger,
+        &_cmsAlarmCodesChunk,            //  AlarmCodes,
+        &_cmsAdaptationStateChunk,       //  AdaptationState, 
+        &_cmsMemPluginChunk,             //  MemPlugin,
+        &_cmsInterpPluginChunk,          //  InterpPlugin,
+        &_cmsCurvesPluginChunk,          //  CurvesPlugin,
+        &_cmsFormattersPluginChunk,      //  FormattersPlugin,
+        &_cmsTagTypePluginChunk,         //  TagTypePlugin,
+        &_cmsTagPluginChunk,             //  TagPlugin,
+        &_cmsIntentsPluginChunk,         //  IntentPlugin,
+        &_cmsMPETypePluginChunk,         //  MPEPlugin,
+        &_cmsOptimizationPluginChunk,    //  OptimizationPlugin,
+        &_cmsTransformPluginChunk,       //  TransformPlugin,
+        &_cmsMutexPluginChunk,           //  MutexPlugin,
+        &_cmsParallelizationPluginChunk  //  ParallelizationPlugin
     },
     
     { NULL, NULL, NULL, NULL, NULL, NULL } // The default memory allocator is not used for context 0
@@ -787,6 +796,8 @@
 // identify which plug-in to unregister.
 void CMSEXPORT cmsUnregisterPluginsTHR(cmsContext ContextID)
 {
+    struct _cmsContext_struct* ctx = _cmsGetContext(ContextID);
+
     _cmsRegisterMemHandlerPlugin(ContextID, NULL);
     _cmsRegisterInterpPlugin(ContextID, NULL);
     _cmsRegisterTagTypePlugin(ContextID, NULL);
@@ -798,6 +809,11 @@
     _cmsRegisterOptimizationPlugin(ContextID, NULL);
     _cmsRegisterTransformPlugin(ContextID, NULL);    
     _cmsRegisterMutexPlugin(ContextID, NULL);
+    _cmsRegisterParallelizationPlugin(ContextID, NULL);
+
+   if (ctx->MemPool != NULL)
+       _cmsSubAllocDestroy(ctx->MemPool);
+   ctx->MemPool = NULL;
 }
 
 
@@ -881,6 +897,7 @@
     _cmsAllocOptimizationPluginChunk(ctx, NULL);
     _cmsAllocTransformPluginChunk(ctx, NULL);
     _cmsAllocMutexPluginChunk(ctx, NULL);
+    _cmsAllocParallelizationPluginChunk(ctx, NULL);
 
     // Setup the plug-ins
     if (!cmsPluginTHR(ctx, Plugin)) {
@@ -944,6 +961,7 @@
     _cmsAllocOptimizationPluginChunk(ctx, src);
     _cmsAllocTransformPluginChunk(ctx, src);
     _cmsAllocMutexPluginChunk(ctx, src);
+    _cmsAllocParallelizationPluginChunk(ctx, src);
 
     // Make sure no one failed
     for (i=Logger; i < MemoryClientMax; i++) {
diff --git a/third_party/lcms/src/cmssamp.c b/third_party/lcms/src/cmssamp.c
index 868664a..f5957d1 100644
--- a/third_party/lcms/src/cmssamp.c
+++ b/third_party/lcms/src/cmssamp.c
@@ -126,6 +126,7 @@
     // Force it to be neutral, clip to max. L* of 50
     Lab.a = Lab.b = 0;
     if (Lab.L > 50) Lab.L = 50;
+    if (Lab.L < 0) Lab.L = 0;
 
     // Free the resources
     cmsDeleteTransform(xform);
@@ -322,6 +323,7 @@
 
     if (fabs(a) < 1.0E-10) {
     
+        if (fabs(b) < 1.0E-10) return 0;
         return cmsmin(0, cmsmax(50, -c/b ));
     }
     else {
@@ -332,7 +334,11 @@
          }
          else {
 
-             double rt = (-b + sqrt(d)) / (2.0 * a);
+             double rt;
+             
+             if (fabs(a) < 1.0E-10) return 0;
+
+             rt = (-b + sqrt(d)) / (2.0 * a);
 
              return cmsmax(0, cmsmin(50, rt));
          }
diff --git a/third_party/lcms/src/cmstypes.c b/third_party/lcms/src/cmstypes.c
index c05a3ec..1144d98 100644
--- a/third_party/lcms/src/cmstypes.c
+++ b/third_party/lcms/src/cmstypes.c
@@ -1425,9 +1425,9 @@
 {
     cmsICCMeasurementConditions mc;
 
-	
+    
     memset(&mc, 0, sizeof(mc));
-	
+    
     if (!_cmsReadUInt32Number(io, &mc.Observer)) return NULL;
     if (!_cmsReadXYZNumber(io,    &mc.Backing)) return NULL;
     if (!_cmsReadUInt32Number(io, &mc.Geometry)) return NULL;
@@ -1550,7 +1550,10 @@
         Block = (wchar_t*) _cmsMalloc(self ->ContextID, SizeOfTag);
         if (Block == NULL) goto Error;
         NumOfWchar = SizeOfTag / sizeof(wchar_t);
-        if (!_cmsReadWCharArray(io, NumOfWchar, Block)) goto Error;
+        if (!_cmsReadWCharArray(io, NumOfWchar, Block)) {
+            _cmsFree(self->ContextID, Block);
+            goto Error;
+        }
     }
 
     mlu ->MemPool  = Block;
@@ -1935,29 +1938,37 @@
 
     // That should be all
     if (mpe != NULL) {
-        cmsSignalError(mpe->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT is not suitable to be saved as LUT8");
+        cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT is not suitable to be saved as LUT8");
         return FALSE;
     }
 
     if (clut == NULL)
         clutPoints = 0;
-    else
-        clutPoints    = clut->Params->nSamples[0];
-
-    if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) NewLUT ->InputChannels)) return FALSE;
-    if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) NewLUT ->OutputChannels)) return FALSE;
+    else {
+        // Lut8 only allows same CLUT points in all dimensions        
+        clutPoints = clut->Params->nSamples[0];
+        for (i = 1; i < cmsPipelineInputChannels(NewLUT); i++) {
+            if (clut->Params->nSamples[i] != clutPoints) {
+                cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT with different samples per dimension not suitable to be saved as LUT16");
+                return FALSE;
+            }
+        }
+    }
+        
+    if (!_cmsWriteUInt8Number(io, (cmsUInt8Number)cmsPipelineInputChannels(NewLUT))) return FALSE;
+    if (!_cmsWriteUInt8Number(io, (cmsUInt8Number)cmsPipelineOutputChannels(NewLUT))) return FALSE;
     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) clutPoints)) return FALSE;
     if (!_cmsWriteUInt8Number(io, 0)) return FALSE; // Padding
 
     if (MatMPE != NULL) {
         
-		for (i = 0; i < 9; i++)
-		{
-			if (!_cmsWrite15Fixed16Number(io, MatMPE->Double[i])) return FALSE;
-		}
+        for (i = 0; i < 9; i++)
+        {
+            if (!_cmsWrite15Fixed16Number(io, MatMPE->Double[i])) return FALSE;
+        }
     }
     else {
-		
+        
         if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE;
         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
@@ -2071,11 +2082,11 @@
     cmsUInt32Number nEntries;
 
     _cmsAssert(Tables != NULL);
-
-    nEntries = Tables->TheCurves[0]->nEntries;
-
+   
     for (i=0; i < Tables ->nCurves; i++) {
 
+        nEntries = Tables->TheCurves[i]->nEntries;
+
         for (j=0; j < nEntries; j++) {
 
             val = Tables->TheCurves[i]->Table16[j];        
@@ -2218,7 +2229,7 @@
 
     // That should be all
     if (mpe != NULL) {
-        cmsSignalError(mpe->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT is not suitable to be saved as LUT16");
+        cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT is not suitable to be saved as LUT16");
         return FALSE;
     }
 
@@ -2227,24 +2238,32 @@
 
     if (clut == NULL)
         clutPoints = 0;
-    else
-        clutPoints    = clut->Params->nSamples[0];
+    else {
+        // Lut16 only allows same CLUT points in all dimensions        
+        clutPoints = clut->Params->nSamples[0];
+        for (i = 1; i < InputChannels; i++) {
+            if (clut->Params->nSamples[i] != clutPoints) {
+                cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT with different samples per dimension not suitable to be saved as LUT16");
+                return FALSE;
+            }
+        }
+    }
 
     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) InputChannels)) return FALSE;
     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) OutputChannels)) return FALSE;
     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) clutPoints)) return FALSE;
     if (!_cmsWriteUInt8Number(io, 0)) return FALSE; // Padding
-	
+    
     if (MatMPE != NULL) {
                 
-		for (i = 0; i < 9; i++)
-		{
-			if (!_cmsWrite15Fixed16Number(io, MatMPE->Double[i])) return FALSE;
-		}
+        for (i = 0; i < 9; i++)
+        {
+            if (!_cmsWrite15Fixed16Number(io, MatMPE->Double[i])) return FALSE;
+        }
       
     }
     else {
-		
+        
         if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE;
         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
@@ -2586,31 +2605,31 @@
 static
 cmsBool  WriteMatrix(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsStage* mpe)
 {
-	cmsUInt32Number i, n;
+    cmsUInt32Number i, n;
 
     _cmsStageMatrixData* m = (_cmsStageMatrixData*) mpe -> Data;
 
-	n = mpe->InputChannels * mpe->OutputChannels;
+    n = mpe->InputChannels * mpe->OutputChannels;
 
-	// Write the Matrix
-	for (i = 0; i < n; i++)
-	{
-		if (!_cmsWrite15Fixed16Number(io, m->Double[i])) return FALSE;
-	}
+    // Write the Matrix
+    for (i = 0; i < n; i++)
+    {
+        if (!_cmsWrite15Fixed16Number(io, m->Double[i])) return FALSE;
+    }
 
-	if (m->Offset != NULL) {
+    if (m->Offset != NULL) {
 
-		for (i = 0; i < mpe->OutputChannels; i++)
-		{
-			if (!_cmsWrite15Fixed16Number(io, m->Offset[i])) return FALSE;
-		}
-	}
-	else {
-		for (i = 0; i < mpe->OutputChannels; i++)
-		{
-			if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
-		}
-	}
+        for (i = 0; i < mpe->OutputChannels; i++)
+        {
+            if (!_cmsWrite15Fixed16Number(io, m->Offset[i])) return FALSE;
+        }
+    }
+    else {
+        for (i = 0; i < mpe->OutputChannels; i++)
+        {
+            if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
+        }
+    }
 
 
     return TRUE;
@@ -3141,7 +3160,6 @@
 static
 void *Type_NamedColor_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
 {
-
     cmsUInt32Number      vendorFlag;     // Bottom 16 bits for ICC use
     cmsUInt32Number      count;          // Count of named colors
     cmsUInt32Number      nDeviceCoords;  // Num of device coordinates
@@ -3471,7 +3489,6 @@
 
     // Get table count
     if (!_cmsReadUInt32Number(io, &Count)) return NULL;
-    SizeOfTag -= sizeof(cmsUInt32Number);
 
     // Allocate an empty structure
     OutSeq = cmsAllocProfileSequenceDescription(self ->ContextID, Count);
@@ -3489,6 +3506,7 @@
     *nItems = 1;
     return OutSeq;
 
+    cmsUNUSED_PARAMETER(SizeOfTag);
 }
 
 
@@ -3706,7 +3724,7 @@
 
 // Auxiliary, read an string specified as count + string
 static
-cmsBool  ReadCountAndSting(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* mlu, cmsUInt32Number* SizeOfTag, const char* Section)
+cmsBool  ReadCountAndString(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* mlu, cmsUInt32Number* SizeOfTag, const char* Section)
 {
     cmsUInt32Number Count;
     char* Text;
@@ -3736,7 +3754,7 @@
 }
 
 static
-cmsBool  WriteCountAndSting(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* mlu, const char* Section)
+cmsBool  WriteCountAndString(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* mlu, const char* Section)
 {
  cmsUInt32Number TextSize;
  char* Text;
@@ -3760,11 +3778,11 @@
     cmsMLU* mlu = cmsMLUalloc(self ->ContextID, 5);
 
     *nItems = 0;
-    if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "nm")) goto Error;
-    if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#0")) goto Error;
-    if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#1")) goto Error;
-    if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#2")) goto Error;
-    if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#3")) goto Error;
+    if (!ReadCountAndString(self, io, mlu, &SizeOfTag, "nm")) goto Error;
+    if (!ReadCountAndString(self, io, mlu, &SizeOfTag, "#0")) goto Error;
+    if (!ReadCountAndString(self, io, mlu, &SizeOfTag, "#1")) goto Error;
+    if (!ReadCountAndString(self, io, mlu, &SizeOfTag, "#2")) goto Error;
+    if (!ReadCountAndString(self, io, mlu, &SizeOfTag, "#3")) goto Error;
 
     *nItems = 1;
     return (void*) mlu;
@@ -3781,11 +3799,11 @@
 
     cmsMLU* mlu = (cmsMLU*) Ptr;
 
-    if (!WriteCountAndSting(self, io, mlu, "nm")) goto Error;
-    if (!WriteCountAndSting(self, io, mlu, "#0")) goto Error;
-    if (!WriteCountAndSting(self, io, mlu, "#1")) goto Error;
-    if (!WriteCountAndSting(self, io, mlu, "#2")) goto Error;
-    if (!WriteCountAndSting(self, io, mlu, "#3")) goto Error;
+    if (!WriteCountAndString(self, io, mlu, "nm")) goto Error;
+    if (!WriteCountAndString(self, io, mlu, "#0")) goto Error;
+    if (!WriteCountAndString(self, io, mlu, "#1")) goto Error;
+    if (!WriteCountAndString(self, io, mlu, "#2")) goto Error;
+    if (!WriteCountAndString(self, io, mlu, "#3")) goto Error;
 
     return TRUE;
 
@@ -5383,6 +5401,64 @@
     cmsUNUSED_PARAMETER(self);
 }
 
+// cicp VideoSignalType
+
+static
+void* Type_VideoSignal_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
+{
+    cmsVideoSignalType* cicp = NULL;
+
+    if (SizeOfTag != 8) return NULL; 
+
+    if (!_cmsReadUInt32Number(io, NULL)) return NULL;
+
+    cicp = (cmsVideoSignalType*)_cmsCalloc(self->ContextID, 1, sizeof(cmsVideoSignalType));
+    if (cicp == NULL) return NULL;
+
+    if (!_cmsReadUInt8Number(io, &cicp->ColourPrimaries)) goto Error;
+    if (!_cmsReadUInt8Number(io, &cicp->TransferCharacteristics)) goto Error;
+    if (!_cmsReadUInt8Number(io, &cicp->MatrixCoefficients)) goto Error;
+    if (!_cmsReadUInt8Number(io, &cicp->VideoFullRangeFlag)) goto Error;
+
+    // Success
+    *nItems = 1;
+    return cicp;
+
+Error:
+    if (cicp != NULL) _cmsFree(self->ContextID, cicp);
+    return NULL;
+}
+
+static
+cmsBool Type_VideoSignal_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
+{
+    cmsVideoSignalType* cicp = (cmsVideoSignalType*)Ptr;
+
+    if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
+    if (!_cmsWriteUInt8Number(io, cicp->ColourPrimaries)) return FALSE;
+    if (!_cmsWriteUInt8Number(io, cicp->TransferCharacteristics)) return FALSE;
+    if (!_cmsWriteUInt8Number(io, cicp->MatrixCoefficients)) return FALSE;
+    if (!_cmsWriteUInt8Number(io, cicp->VideoFullRangeFlag)) return FALSE;
+
+    return TRUE;
+
+    cmsUNUSED_PARAMETER(self);
+    cmsUNUSED_PARAMETER(nItems);
+}
+
+void* Type_VideoSignal_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n)
+{
+    return _cmsDupMem(self->ContextID, Ptr, sizeof(cmsVideoSignalType));
+
+    cmsUNUSED_PARAMETER(n);
+}
+
+
+static
+void Type_VideoSignal_Free(struct _cms_typehandler_struct* self, void* Ptr)
+{
+    _cmsFree(self->ContextID, Ptr);
+}
 
 // ********************************************************************************
 // Type support main routines
@@ -5422,6 +5498,7 @@
 {TYPE_HANDLER(cmsMonacoBrokenCurveType,        Curve),              (_cmsTagTypeLinkedList*) &SupportedTagTypes[28] },
 {TYPE_HANDLER(cmsSigProfileSequenceIdType,     ProfileSequenceId),  (_cmsTagTypeLinkedList*) &SupportedTagTypes[29] },
 {TYPE_HANDLER(cmsSigDictType,                  Dictionary),         (_cmsTagTypeLinkedList*) &SupportedTagTypes[30] },
+{TYPE_HANDLER(cmsSigcicpType,                  VideoSignal),        (_cmsTagTypeLinkedList*) &SupportedTagTypes[31] },
 {TYPE_HANDLER(cmsSigVcgtType,                  vcgt),                NULL }
 };
 
@@ -5616,6 +5693,8 @@
     { cmsSigProfileSequenceIdTag,   { 1, 1, { cmsSigProfileSequenceIdType},  NULL }, &SupportedTags[62]},
 
     { cmsSigProfileDescriptionMLTag,{ 1, 1, { cmsSigMultiLocalizedUnicodeType}, NULL}, &SupportedTags[63]},
+    { cmsSigcicpTag,                { 1, 1, { cmsSigcicpType},               NULL },   &SupportedTags[64]},
+
     { cmsSigArgyllArtsTag,          { 9, 1, { cmsSigS15Fixed16ArrayType},    NULL}, NULL}
 
 };
diff --git a/third_party/lcms/src/cmsvirt.c b/third_party/lcms/src/cmsvirt.c
index bffb88e..b8ef607 100644
--- a/third_party/lcms/src/cmsvirt.c
+++ b/third_party/lcms/src/cmsvirt.c
@@ -114,7 +114,7 @@
     if (!hICC)                          // can't allocate
         return NULL;
 
-    cmsSetProfileVersion(hICC, 4.3);
+    cmsSetProfileVersion(hICC, 4.4);
 
     cmsSetDeviceClass(hICC,      cmsSigDisplayClass);
     cmsSetColorSpace(hICC,       cmsSigRgbData);
@@ -235,7 +235,7 @@
     if (!hICC)                          // can't allocate
         return NULL;
 
-    cmsSetProfileVersion(hICC, 4.3);
+    cmsSetProfileVersion(hICC, 4.4);
 
     cmsSetDeviceClass(hICC,      cmsSigDisplayClass);
     cmsSetColorSpace(hICC,       cmsSigGrayData);
@@ -291,13 +291,13 @@
 {
     cmsHPROFILE hICC;
     cmsPipeline* Pipeline;
-    cmsUInt32Number nChannels;
+    cmsInt32Number nChannels;
 
     hICC = cmsCreateProfilePlaceholder(ContextID);
     if (!hICC)
         return NULL;
 
-    cmsSetProfileVersion(hICC, 4.3);
+    cmsSetProfileVersion(hICC, 4.4);
 
     cmsSetDeviceClass(hICC,      cmsSigLinkClass);
     cmsSetColorSpace(hICC,       ColorSpace);
@@ -306,7 +306,7 @@
     cmsSetHeaderRenderingIntent(hICC,  INTENT_PERCEPTUAL);
 
     // Set up channels
-    nChannels = cmsChannelsOf(ColorSpace);
+    nChannels = cmsChannelsOfColorSpace(ColorSpace);
 
     // Creates a Pipeline with prelinearization step only
     Pipeline = cmsPipelineAlloc(ContextID, nChannels, nChannels);
@@ -397,7 +397,7 @@
     cmsHPROFILE hICC;
     cmsPipeline* LUT;
     cmsStage* CLUT;
-    cmsUInt32Number nChannels;
+    cmsInt32Number nChannels;
 
     if (ColorSpace != cmsSigCmykData) {
         cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "InkLimiting: Only CMYK currently supported");
@@ -416,7 +416,7 @@
     if (!hICC)                          // can't allocate
         return NULL;
 
-    cmsSetProfileVersion(hICC, 4.3);
+    cmsSetProfileVersion(hICC, 4.4);
 
     cmsSetDeviceClass(hICC,      cmsSigLinkClass);
     cmsSetColorSpace(hICC,       ColorSpace);
@@ -526,7 +526,7 @@
     hProfile = cmsCreateRGBProfileTHR(ContextID, WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL);
     if (hProfile == NULL) return NULL;
 
-    cmsSetProfileVersion(hProfile, 4.3);
+    cmsSetProfileVersion(hProfile, 4.4);
 
     cmsSetDeviceClass(hProfile, cmsSigAbstractClass);
     cmsSetColorSpace(hProfile,  cmsSigLabData);
@@ -572,7 +572,7 @@
     hProfile = cmsCreateRGBProfileTHR(ContextID, cmsD50_xyY(), NULL, NULL);
     if (hProfile == NULL) return NULL;
 
-    cmsSetProfileVersion(hProfile, 4.3);
+    cmsSetProfileVersion(hProfile, 4.4);
 
     cmsSetDeviceClass(hProfile, cmsSigAbstractClass);
     cmsSetColorSpace(hProfile,  cmsSigXYZData);
@@ -839,7 +839,7 @@
     if (!hProfile)                          // can't allocate
         return NULL;
 
-    cmsSetProfileVersion(hProfile, 4.3);
+    cmsSetProfileVersion(hProfile, 4.4);
 
     if (!SetTextTags(hProfile, L"NULL profile built-in")) goto Error;
 
@@ -974,7 +974,7 @@
     // Make sure we have proper formatters
     cmsChangeBuffersFormat(xform, TYPE_NAMED_COLOR_INDEX,
         FLOAT_SH(0) | COLORSPACE_SH(_cmsLCMScolorSpace(v ->ExitColorSpace))
-        | BYTES_SH(2) | CHANNELS_SH(cmsChannelsOf(v ->ExitColorSpace)));
+        | BYTES_SH(2) | CHANNELS_SH(cmsChannelsOfColorSpace(v ->ExitColorSpace)));
 
     // Apply the transfor to colorants.
     for (i=0; i < nColors; i++) {
@@ -1062,8 +1062,9 @@
 cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat64Number Version, cmsUInt32Number dwFlags)
 {
     cmsHPROFILE hProfile = NULL;
-    cmsUInt32Number FrmIn, FrmOut, ChansIn, ChansOut;
-    int ColorSpaceBitsIn, ColorSpaceBitsOut;
+	cmsUInt32Number FrmIn, FrmOut;
+	cmsInt32Number ChansIn, ChansOut;
+	int ColorSpaceBitsIn, ColorSpaceBitsOut;
     _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
     cmsPipeline* LUT = NULL;
     cmsStage* mpe;
@@ -1114,8 +1115,8 @@
 
     // Optimize the LUT and precalculate a devicelink
 
-    ChansIn  = cmsChannelsOf(xform -> EntryColorSpace);
-    ChansOut = cmsChannelsOf(xform -> ExitColorSpace);
+    ChansIn  = cmsChannelsOfColorSpace(xform -> EntryColorSpace);
+    ChansOut = cmsChannelsOfColorSpace(xform -> ExitColorSpace);
 
     ColorSpaceBitsIn  = _cmsLCMScolorSpace(xform -> EntryColorSpace);
     ColorSpaceBitsOut = _cmsLCMScolorSpace(xform -> ExitColorSpace);
diff --git a/third_party/lcms/src/cmsxform.c b/third_party/lcms/src/cmsxform.c
index c7183fc..38ea624 100644
--- a/third_party/lcms/src/cmsxform.c
+++ b/third_party/lcms/src/cmsxform.c
@@ -781,6 +781,73 @@
     return CMMcargo->dwOriginalFlags;
 }
 
+// Returns the worker callback for parallelization plug-ins
+_cmsTransform2Fn CMSEXPORT _cmsGetTransformWorker(struct _cmstransform_struct* CMMcargo)
+{
+    _cmsAssert(CMMcargo != NULL);
+    return CMMcargo->Worker;
+}
+
+// This field holds maximum number of workers or -1 to auto 
+cmsInt32Number CMSEXPORT _cmsGetTransformMaxWorkers(struct _cmstransform_struct* CMMcargo)
+{
+    _cmsAssert(CMMcargo != NULL);
+    return CMMcargo->MaxWorkers;
+}
+
+// This field is actually unused and reserved
+cmsUInt32Number CMSEXPORT _cmsGetTransformWorkerFlags(struct _cmstransform_struct* CMMcargo)
+{
+    _cmsAssert(CMMcargo != NULL);
+    return CMMcargo->WorkerFlags;
+}
+
+// In the case there is a parallelization plug-in, let it to do its job
+static
+void ParalellizeIfSuitable(_cmsTRANSFORM* p)
+{
+    _cmsParallelizationPluginChunkType* ctx = (_cmsParallelizationPluginChunkType*)_cmsContextGetClientChunk(p->ContextID, ParallelizationPlugin);
+
+    _cmsAssert(p != NULL);
+    if (ctx != NULL && ctx->SchedulerFn != NULL) {
+
+        p->Worker = p->xform;
+        p->xform = ctx->SchedulerFn;
+        p->MaxWorkers = ctx->MaxWorkers;
+        p->WorkerFlags = ctx->WorkerFlags;
+    }
+}
+
+
+/**
+* An empty unroll to avoid a check with NULL on cmsDoTransform()
+*/
+static
+cmsUInt8Number* UnrollNothing(CMSREGISTER _cmsTRANSFORM* info,
+                              CMSREGISTER cmsUInt16Number wIn[],
+                              CMSREGISTER cmsUInt8Number* accum,
+                              CMSREGISTER cmsUInt32Number Stride)
+{    
+    return accum;
+
+    cmsUNUSED_PARAMETER(info);
+    cmsUNUSED_PARAMETER(wIn);
+    cmsUNUSED_PARAMETER(Stride);
+}
+
+static
+cmsUInt8Number* PackNothing(CMSREGISTER _cmsTRANSFORM* info,
+                           CMSREGISTER cmsUInt16Number wOut[],
+                           CMSREGISTER cmsUInt8Number* output,
+                           CMSREGISTER cmsUInt32Number Stride)
+{
+    return output;
+
+    cmsUNUSED_PARAMETER(info);
+    cmsUNUSED_PARAMETER(wOut);
+    cmsUNUSED_PARAMETER(Stride);
+}
+
 // 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
@@ -836,6 +903,7 @@
                            p->xform = _cmsTransform2toTransformAdaptor;
                        }
 
+                       ParalellizeIfSuitable(p);
                        return p;
                    }
                }
@@ -872,8 +940,10 @@
     }
     else {
 
+        // Formats are intended to be changed before use
         if (*InputFormat == 0 && *OutputFormat == 0) {
-            p ->FromInput = p ->ToOutput = NULL;
+            p->FromInput = UnrollNothing;
+            p->ToOutput = PackNothing;
             *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
         }
         else {
@@ -890,7 +960,7 @@
                 return NULL;
             }
 
-            BytesPerPixelInput = T_BYTES(p ->InputFormat);
+            BytesPerPixelInput = T_BYTES(*InputFormat);
             if (BytesPerPixelInput == 0 || BytesPerPixelInput >= 2)
                    *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER;
 
@@ -924,6 +994,7 @@
     p ->dwOriginalFlags = *dwFlags;
     p ->ContextID       = ContextID;
     p ->UserData        = NULL;
+    ParalellizeIfSuitable(p);
     return p;
 }
 
@@ -1098,8 +1169,8 @@
     }
 
     // Check channel count
-    if ((cmsChannelsOf(EntryColorSpace) != cmsPipelineInputChannels(Lut)) ||
-        (cmsChannelsOf(ExitColorSpace)  != cmsPipelineOutputChannels(Lut))) {
+    if ((cmsChannelsOfColorSpace(EntryColorSpace) != (cmsInt32Number) cmsPipelineInputChannels(Lut)) ||
+        (cmsChannelsOfColorSpace(ExitColorSpace)  != (cmsInt32Number) cmsPipelineOutputChannels(Lut))) {
         cmsPipelineFree(Lut);
         cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Channel count doesn't match. Profile is corrupted");
         return NULL;
diff --git a/third_party/lcms/src/lcms2_internal.h b/third_party/lcms/src/lcms2_internal.h
index 9eecf6e..4aa98c5 100644
--- a/third_party/lcms/src/lcms2_internal.h
+++ b/third_party/lcms/src/lcms2_internal.h
@@ -283,38 +283,38 @@
 
 cmsINLINE int _cmsLockPrimitive(_cmsMutex *m)
 {
-	EnterCriticalSection(m);
-	return 0;
+    EnterCriticalSection(m);
+    return 0;
 }
 
 cmsINLINE int _cmsUnlockPrimitive(_cmsMutex *m)
 {
-	LeaveCriticalSection(m);
-	return 0;
+    LeaveCriticalSection(m);
+    return 0;
 }
-	
+    
 cmsINLINE int _cmsInitMutexPrimitive(_cmsMutex *m)
 {
-	InitializeCriticalSection(m);
-	return 0;
+    InitializeCriticalSection(m);
+    return 0;
 }
 
 cmsINLINE int _cmsDestroyMutexPrimitive(_cmsMutex *m)
 {
-	DeleteCriticalSection(m);
-	return 0;
+    DeleteCriticalSection(m);
+    return 0;
 }
 
 cmsINLINE int _cmsEnterCriticalSectionPrimitive(_cmsMutex *m)
 {
-	EnterCriticalSection(m);
-	return 0;
+    EnterCriticalSection(m);
+    return 0;
 }
 
 cmsINLINE int _cmsLeaveCriticalSectionPrimitive(_cmsMutex *m)
 {
-	LeaveCriticalSection(m);
-	return 0;
+    LeaveCriticalSection(m);
+    return 0;
 }
 
 #else
@@ -328,32 +328,32 @@
 
 cmsINLINE int _cmsLockPrimitive(_cmsMutex *m)
 {
-	return pthread_mutex_lock(m);
+    return pthread_mutex_lock(m);
 }
 
 cmsINLINE int _cmsUnlockPrimitive(_cmsMutex *m)
 {
-	return pthread_mutex_unlock(m);
+    return pthread_mutex_unlock(m);
 }
-	
+    
 cmsINLINE int _cmsInitMutexPrimitive(_cmsMutex *m)
 {
-	return pthread_mutex_init(m, NULL);
+    return pthread_mutex_init(m, NULL);
 }
 
 cmsINLINE int _cmsDestroyMutexPrimitive(_cmsMutex *m)
 {
-	return pthread_mutex_destroy(m);
+    return pthread_mutex_destroy(m);
 }
 
 cmsINLINE int _cmsEnterCriticalSectionPrimitive(_cmsMutex *m)
 {
-	return pthread_mutex_lock(m);
+    return pthread_mutex_lock(m);
 }
 
 cmsINLINE int _cmsLeaveCriticalSectionPrimitive(_cmsMutex *m)
 {
-	return pthread_mutex_unlock(m);
+    return pthread_mutex_unlock(m);
 }
 
 #endif
@@ -366,37 +366,37 @@
 cmsINLINE int _cmsLockPrimitive(_cmsMutex *m)
 {
     cmsUNUSED_PARAMETER(m);
-	return 0;
+    return 0;
 }
 
 cmsINLINE int _cmsUnlockPrimitive(_cmsMutex *m)
 {
     cmsUNUSED_PARAMETER(m);
-	return 0;
+    return 0;
 }
-	
+    
 cmsINLINE int _cmsInitMutexPrimitive(_cmsMutex *m)
 {
     cmsUNUSED_PARAMETER(m);
-	return 0;
+    return 0;
 }
 
 cmsINLINE int _cmsDestroyMutexPrimitive(_cmsMutex *m)
 {
     cmsUNUSED_PARAMETER(m);
-	return 0;
+    return 0;
 }
 
 cmsINLINE int _cmsEnterCriticalSectionPrimitive(_cmsMutex *m)
 {
     cmsUNUSED_PARAMETER(m);
-	return 0;
+    return 0;
 }
 
 cmsINLINE int _cmsLeaveCriticalSectionPrimitive(_cmsMutex *m)
 {
     cmsUNUSED_PARAMETER(m);
-	return 0;
+    return 0;
 }
 #endif
 
@@ -438,6 +438,9 @@
 // Mutex
 cmsBool _cmsRegisterMutexPlugin(cmsContext ContextID, cmsPluginBase* Plugin);
 
+// Paralellization
+cmsBool _cmsRegisterParallelizationPlugin(cmsContext ContextID, cmsPluginBase* Plugin);
+
 // ---------------------------------------------------------------------------------------------------------
 
 // Suballocators. 
@@ -485,6 +488,7 @@
     OptimizationPlugin,
     TransformPlugin,
     MutexPlugin,
+    ParallelizationPlugin,
 
     // Last in list
     MemoryClientMax
@@ -720,6 +724,24 @@
 void _cmsAllocMutexPluginChunk(struct _cmsContext_struct* ctx, 
                                         const struct _cmsContext_struct* src);
 
+// Container for parallelization plug-in
+typedef struct {
+
+    cmsInt32Number      MaxWorkers;       // Number of workers to do as maximum
+    cmsInt32Number      WorkerFlags;      // reserved
+    _cmsTransform2Fn    SchedulerFn;      // callback to setup functions 
+    
+} _cmsParallelizationPluginChunkType;
+
+// The global Context0 storage for parallelization plug-in
+extern  _cmsParallelizationPluginChunkType _cmsParallelizationPluginChunk;
+
+// Allocate parallelization container.
+void _cmsAllocParallelizationPluginChunk(struct _cmsContext_struct* ctx,
+                                         const struct _cmsContext_struct* src);
+
+
+
 // ----------------------------------------------------------------------------------
 // MLU internal representation
 typedef struct {
@@ -1081,6 +1103,11 @@
     // A way to provide backwards compatibility with full xform plugins
     _cmsTransformFn OldXform;
 
+    // A one-worker transform entry for parallelization 
+    _cmsTransform2Fn Worker;
+    cmsInt32Number   MaxWorkers;
+    cmsUInt32Number  WorkerFlags;
+
 } _cmsTRANSFORM;
 
 // Copies extra channels from input to output if the original flags in the transform structure