Upgrade to lcms 2.13.

Update 0000-cmserr-changes.patch to patch cleanly since upstream fixed a
typo. Remove 0034-uninitialized-reads.patch as it was recently
cherry-picked from upstream just before the 2.13 release.

Change-Id: I10020318f40faa4ad048675f133a635e1880b305
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/90172
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Commit-Queue: Lei Zhang <thestig@chromium.org>
diff --git a/third_party/lcms/0000-cmserr-changes.patch b/third_party/lcms/0000-cmserr-changes.patch
index 31f56fd..13a854a 100644
--- a/third_party/lcms/0000-cmserr-changes.patch
+++ b/third_party/lcms/0000-cmserr-changes.patch
@@ -27,7 +27,7 @@
 -
 -// User may override this behaviour by using a memory plug-in, which basically replaces
 -// the default memory management functions. In this case, no check is performed and it
--// is up to the plug-in writter to keep in the safe side. There are only three functions
+-// is up to the plug-in writer to keep in the safe side. There are only three functions
 -// required to be implemented: malloc, realloc and free, although the user may want to
 -// replace the optional mallocZero, calloc and dup as well.
 -
diff --git a/third_party/lcms/0034-uninitialized-reads.patch b/third_party/lcms/0034-uninitialized-reads.patch
deleted file mode 100644
index 303266e..0000000
--- a/third_party/lcms/0034-uninitialized-reads.patch
+++ /dev/null
@@ -1,161 +0,0 @@
-diff --git a/third_party/lcms/src/cmsintrp.c b/third_party/lcms/src/cmsintrp.c
-index 3d9a40530..22de3e550 100644
---- a/third_party/lcms/src/cmsintrp.c
-+++ b/third_party/lcms/src/cmsintrp.c
-@@ -200,8 +200,8 @@ void LinLerp1D(CMSREGISTER const cmsUInt16Number Value[],
-     int val3;
-     const cmsUInt16Number* LutTable = (cmsUInt16Number*) p ->Table;
- 
--    // if last value...
--    if (Value[0] == 0xffff) {
-+    // if last value or just one point
-+    if (Value[0] == 0xffff || p->Domain[0] == 0) {
- 
-         Output[0] = LutTable[p -> Domain[0]];      
-     }
-@@ -240,7 +240,7 @@ void LinLerp1Dfloat(const cmsFloat32Number Value[],
-        val2 = fclamp(Value[0]);
- 
-        // if last value...
--       if (val2 == 1.0) {
-+       if (val2 == 1.0 || p->Domain[0] == 0) {
-            Output[0] = LutTable[p -> Domain[0]];          
-        }
-        else
-@@ -274,20 +274,34 @@ void Eval1Input(CMSREGISTER const cmsUInt16Number Input[],
-        cmsUInt32Number OutChan;
-        const cmsUInt16Number* LutTable = (cmsUInt16Number*) p16 -> Table;
- 
--       v = Input[0] * p16 -> Domain[0];
--       fk = _cmsToFixedDomain(v);
- 
--       k0 = FIXED_TO_INT(fk);
--       rk = (cmsUInt16Number) FIXED_REST_TO_INT(fk);
-+       // if last value...
-+       if (Input[0] == 0xffff || p16->Domain[0] == 0) {
-+
-+           cmsUInt16Number y0 = LutTable[p16->Domain[0]];
-+
-+           for (OutChan = 0; OutChan < p16->nOutputs; OutChan++) {
-+               Output[OutChan] = y0;
-+           }
-+       }
-+       else
-+       {
-+
-+           v = Input[0] * p16->Domain[0];
-+           fk = _cmsToFixedDomain(v);
-+
-+           k0 = FIXED_TO_INT(fk);
-+           rk = (cmsUInt16Number)FIXED_REST_TO_INT(fk);
- 
--       k1 = k0 + (Input[0] != 0xFFFFU ? 1 : 0);
-+           k1 = k0 + (Input[0] != 0xFFFFU ? 1 : 0);
- 
--       K0 = p16 -> opta[0] * k0;
--       K1 = p16 -> opta[0] * k1;
-+           K0 = p16->opta[0] * k0;
-+           K1 = p16->opta[0] * k1;
- 
--       for (OutChan=0; OutChan < p16->nOutputs; OutChan++) {
-+           for (OutChan = 0; OutChan < p16->nOutputs; OutChan++) {
- 
--           Output[OutChan] = LinearInterp(rk, LutTable[K0+OutChan], LutTable[K1+OutChan]);
-+               Output[OutChan] = LinearInterp(rk, LutTable[K0 + OutChan], LutTable[K1 + OutChan]);
-+           }
-        }
- }
- 
-@@ -308,7 +322,7 @@ void Eval1InputFloat(const cmsFloat32Number Value[],
-     val2 = fclamp(Value[0]);
- 
-     // if last value...
--    if (val2 == 1.0) {
-+    if (val2 == 1.0 || p->Domain[0] == 0) {
- 
-         y0 = LutTable[p->Domain[0]];
- 
-diff --git a/third_party/lcms/src/cmsio0.c b/third_party/lcms/src/cmsio0.c
-index 743b958df..ff8c464fc 100644
---- a/third_party/lcms/src/cmsio0.c
-+++ b/third_party/lcms/src/cmsio0.c
-@@ -1538,8 +1538,12 @@ void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig)
-     if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return NULL;
- 
-     n = _cmsSearchTag(Icc, sig, TRUE);
--    if (n < 0) goto Error;               // Not found, return NULL
--
-+    if (n < 0)
-+    {
-+        // Not found, return NULL
-+        _cmsUnlockMutex(Icc->ContextID, Icc->UsrMutex);
-+        return NULL;
-+    }
- 
-     // If the element is already in memory, return the pointer
-     if (Icc -> TagPtrs[n]) {
-diff --git a/third_party/lcms/src/cmstypes.c b/third_party/lcms/src/cmstypes.c
-index 825b11eb1..49207735b 100644
---- a/third_party/lcms/src/cmstypes.c
-+++ b/third_party/lcms/src/cmstypes.c
-@@ -3515,42 +3515,48 @@ void *Type_UcrBg_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cm
- {
-     cmsUcrBg* n = (cmsUcrBg*) _cmsMallocZero(self ->ContextID, sizeof(cmsUcrBg));
-     cmsUInt32Number CountUcr, CountBg;
-+    cmsInt32Number SignedSizeOfTag = (cmsInt32Number)SizeOfTag;
-     char* ASCIIString;
- 
-     *nItems = 0;
-     if (n == NULL) return NULL;
- 
-     // First curve is Under color removal
-+
-+    if (SignedSizeOfTag < sizeof(cmsUInt32Number)) return NULL;    
-     if (!_cmsReadUInt32Number(io, &CountUcr)) return NULL;
--    if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL;
--    SizeOfTag -= sizeof(cmsUInt32Number);
-+    SignedSizeOfTag -= sizeof(cmsUInt32Number);
- 
-     n ->Ucr = cmsBuildTabulatedToneCurve16(self ->ContextID, CountUcr, NULL);
-     if (n ->Ucr == NULL) return NULL;
- 
-+    if (SignedSizeOfTag < (cmsInt32Number)CountUcr * sizeof(cmsUInt16Number)) return NULL;
-     if (!_cmsReadUInt16Array(io, CountUcr, n ->Ucr->Table16)) return NULL;
--    if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL;
--    SizeOfTag -= CountUcr * sizeof(cmsUInt16Number);
-+  
-+    SignedSizeOfTag -= CountUcr * sizeof(cmsUInt16Number);
- 
-     // Second curve is Black generation
-+
-+    if (SignedSizeOfTag < sizeof(cmsUInt32Number)) return NULL;    
-     if (!_cmsReadUInt32Number(io, &CountBg)) return NULL;
--    if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL;
--    SizeOfTag -= sizeof(cmsUInt32Number);
-+    SignedSizeOfTag -= sizeof(cmsUInt32Number);
- 
-     n ->Bg = cmsBuildTabulatedToneCurve16(self ->ContextID, CountBg, NULL);
-     if (n ->Bg == NULL) return NULL;
-+
-+    if (SignedSizeOfTag < (cmsInt32Number) CountBg * sizeof(cmsUInt16Number)) return NULL;    
-     if (!_cmsReadUInt16Array(io, CountBg, n ->Bg->Table16)) return NULL;
--    if (SizeOfTag < CountBg * sizeof(cmsUInt16Number)) return NULL;
--    SizeOfTag -= CountBg * sizeof(cmsUInt16Number);
--    if (SizeOfTag == UINT_MAX) return NULL;
-+    SignedSizeOfTag -= CountBg * sizeof(cmsUInt16Number);
-+
-+    if (SignedSizeOfTag < 0 || SignedSizeOfTag > 32000) return NULL;
- 
-     // Now comes the text. The length is specified by the tag size
-     n ->Desc = cmsMLUalloc(self ->ContextID, 1);
-     if (n ->Desc == NULL) return NULL;
- 
--    ASCIIString = (char*) _cmsMalloc(self ->ContextID, SizeOfTag + 1);
--    if (io ->Read(io, ASCIIString, sizeof(char), SizeOfTag) != SizeOfTag) return NULL;
--    ASCIIString[SizeOfTag] = 0;
-+    ASCIIString = (char*) _cmsMalloc(self ->ContextID, SignedSizeOfTag + 1);
-+    if (io ->Read(io, ASCIIString, sizeof(char), SignedSizeOfTag) != (cmsUInt32Number) SignedSizeOfTag) return NULL;
-+    ASCIIString[SignedSizeOfTag] = 0;
-     cmsMLUsetASCII(n ->Desc, cmsNoLanguage, cmsNoCountry, ASCIIString);
-     _cmsFree(self ->ContextID, ASCIIString);
- 
diff --git a/third_party/lcms/README.pdfium b/third_party/lcms/README.pdfium
index 8a0fafb..e5c3645 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.9
+Version: 2.13
 Security Critical: yes
 License: MIT License
 
@@ -20,4 +20,3 @@
 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-uninitialized-reads.patch: Protect against corrupt profiles.
diff --git a/third_party/lcms/include/lcms2.h b/third_party/lcms/include/lcms2.h
index f60f5f7..11d0c53 100644
--- a/third_party/lcms/include/lcms2.h
+++ b/third_party/lcms/include/lcms2.h
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2021 Marti Maria Saguer
+//  Copyright (c) 1998-2022 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"),
@@ -23,7 +23,7 @@
 //
 //---------------------------------------------------------------------------------
 //
-// Version 2.12
+// Version 2.13
 //
 
 #ifndef _lcms2_H
@@ -81,7 +81,7 @@
 #endif
 
 // Version/release
-#define LCMS_VERSION        2120
+#define LCMS_VERSION        2130
 
 // I will give the chance of redefining basic types for compilers that are not fully C99 compliant
 #ifndef CMS_BASIC_TYPES_ALREADY_DEFINED
@@ -666,9 +666,10 @@
 // Format of pixel is defined by one cmsUInt32Number, using bit fields as follows
 //
 //                               2                1          0
-//                          3 2 10987 6 5 4 3 2 1 098 7654 321
-//                          A O TTTTT U Y F P X S EEE CCCC BBB
+//                        4 3 2 10987 6 5 4 3 2 1 098 7654 321
+//                        M A O TTTTT U Y F P X S EEE CCCC BBB
 //
+//            M: Premultiplied alpha (only works when extra samples is 1)
 //            A: Floating point -- With this flag we can differentiate 16 bits as float and as int
 //            O: Optimized -- previous optimization already returns the final 8-bit value
 //            T: Pixeltype
@@ -681,6 +682,7 @@
 //            B: bytes per sample
 //            Y: Swap first - changes ABGR to BGRA and KCMY to CMYK
 
+#define PREMUL_SH(m)           ((m) << 23)
 #define FLOAT_SH(a)            ((a) << 22)
 #define OPTIMIZED_SH(s)        ((s) << 21)
 #define COLORSPACE_SH(s)       ((s) << 16)
@@ -694,6 +696,7 @@
 #define BYTES_SH(b)            (b)
 
 // These macros unpack format specifiers into integers
+#define T_PREMUL(m)           (((m)>>23)&1)
 #define T_FLOAT(a)            (((a)>>22)&1)
 #define T_OPTIMIZED(o)        (((o)>>21)&1)
 #define T_COLORSPACE(s)       (((s)>>16)&31)
@@ -722,7 +725,6 @@
 #define PT_HSV       12
 #define PT_HLS       13
 #define PT_Yxy       14
-
 #define PT_MCH1      15
 #define PT_MCH2      16
 #define PT_MCH3      17
@@ -738,7 +740,6 @@
 #define PT_MCH13     27
 #define PT_MCH14     28
 #define PT_MCH15     29
-
 #define PT_LabV2     30     // Identical to PT_Lab, but using the V2 old encoding
 
 // Some (not all!) representations
@@ -752,7 +753,9 @@
 #define TYPE_GRAY_16_REV       (COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(2)|FLAVOR_SH(1))
 #define TYPE_GRAY_16_SE        (COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(2)|ENDIAN16_SH(1))
 #define TYPE_GRAYA_8           (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(1))
+#define TYPE_GRAYA_8_PREMUL    (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(1)|PREMUL_SH(1))
 #define TYPE_GRAYA_16          (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(2))
+#define TYPE_GRAYA_16_PREMUL   (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(2)|PREMUL_SH(1))
 #define TYPE_GRAYA_16_SE       (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(2)|ENDIAN16_SH(1))
 #define TYPE_GRAYA_8_PLANAR    (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(1)|PLANAR_SH(1))
 #define TYPE_GRAYA_16_PLANAR   (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(2)|PLANAR_SH(1))
@@ -769,24 +772,32 @@
 #define TYPE_BGR_16_SE         (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1))
 
 #define TYPE_RGBA_8            (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1))
+#define TYPE_RGBA_8_PREMUL     (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|PREMUL_SH(1))
 #define TYPE_RGBA_8_PLANAR     (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1))
 #define TYPE_RGBA_16           (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2))
+#define TYPE_RGBA_16_PREMUL    (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|PREMUL_SH(1))
 #define TYPE_RGBA_16_PLANAR    (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1))
 #define TYPE_RGBA_16_SE        (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1))
 
 #define TYPE_ARGB_8            (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|SWAPFIRST_SH(1))
+#define TYPE_ARGB_8_PREMUL     (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|SWAPFIRST_SH(1)|PREMUL_SH(1))
 #define TYPE_ARGB_8_PLANAR     (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|SWAPFIRST_SH(1)|PLANAR_SH(1))
 #define TYPE_ARGB_16           (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|SWAPFIRST_SH(1))
+#define TYPE_ARGB_16_PREMUL    (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|SWAPFIRST_SH(1)|PREMUL_SH(1))
 
 #define TYPE_ABGR_8            (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1))
+#define TYPE_ABGR_8_PREMUL     (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|PREMUL_SH(1))
 #define TYPE_ABGR_8_PLANAR     (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|PLANAR_SH(1))
 #define TYPE_ABGR_16           (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1))
+#define TYPE_ABGR_16_PREMUL    (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|PREMUL_SH(1))
 #define TYPE_ABGR_16_PLANAR    (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|PLANAR_SH(1))
 #define TYPE_ABGR_16_SE        (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1))
 
 #define TYPE_BGRA_8            (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1))
+#define TYPE_BGRA_8_PREMUL     (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1)|PREMUL_SH(1))
 #define TYPE_BGRA_8_PLANAR     (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1)|PLANAR_SH(1))
 #define TYPE_BGRA_16           (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|SWAPFIRST_SH(1))
+#define TYPE_BGRA_16_PREMUL    (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|SWAPFIRST_SH(1)|PREMUL_SH(1))
 #define TYPE_BGRA_16_SE        (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1))
 
 #define TYPE_CMY_8             (COLORSPACE_SH(PT_CMY)|CHANNELS_SH(3)|BYTES_SH(1))
@@ -903,7 +914,7 @@
 #define TYPE_HSV_16_PLANAR     (COLORSPACE_SH(PT_HSV)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1))
 #define TYPE_HSV_16_SE         (COLORSPACE_SH(PT_HSV)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1))
 
-// Named color index. Only 16 bits allowed (don't check colorspace)
+// Named color index. Only 16 bits is allowed (don't check colorspace)
 #define TYPE_NAMED_COLOR_INDEX (CHANNELS_SH(1)|BYTES_SH(2))
 
 // Float formatters.
@@ -911,13 +922,19 @@
 #define TYPE_Lab_FLT          (FLOAT_SH(1)|COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(4))
 #define TYPE_LabA_FLT         (FLOAT_SH(1)|COLORSPACE_SH(PT_Lab)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4))
 #define TYPE_GRAY_FLT         (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(4))
+#define TYPE_GRAYA_FLT        (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(4)|EXTRA_SH(1))
+#define TYPE_GRAYA_FLT_PREMUL (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(4)|EXTRA_SH(1)|PREMUL_SH(1))
 #define TYPE_RGB_FLT          (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(4))
 
 #define TYPE_RGBA_FLT         (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4))
+#define TYPE_RGBA_FLT_PREMUL  (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|PREMUL_SH(1))
 #define TYPE_ARGB_FLT         (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|SWAPFIRST_SH(1))
+#define TYPE_ARGB_FLT_PREMUL  (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|SWAPFIRST_SH(1)|PREMUL_SH(1))
 #define TYPE_BGR_FLT          (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(4)|DOSWAP_SH(1))
 #define TYPE_BGRA_FLT         (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|DOSWAP_SH(1)|SWAPFIRST_SH(1))
+#define TYPE_BGRA_FLT_PREMUL  (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|DOSWAP_SH(1)|SWAPFIRST_SH(1)|PREMUL_SH(1))
 #define TYPE_ABGR_FLT         (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|DOSWAP_SH(1))
+#define TYPE_ABGR_FLT_PREMUL  (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|DOSWAP_SH(1)|PREMUL_SH(1))
 
 #define TYPE_CMYK_FLT         (FLOAT_SH(1)|COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(4))
 
@@ -1256,6 +1273,7 @@
 CMSAPI cmsUInt32Number   CMSEXPORT cmsStageOutputChannels(const cmsStage* mpe);
 CMSAPI cmsStageSignature CMSEXPORT cmsStageType(const cmsStage* mpe);
 CMSAPI void*             CMSEXPORT cmsStageData(const cmsStage* mpe);
+CMSAPI cmsContext        CMSEXPORT cmsGetStageContextID(const cmsStage* mpe);
 
 // Sampling
 typedef cmsInt32Number (* cmsSAMPLER16)   (CMSREGISTER const cmsUInt16Number In[],
@@ -1906,6 +1924,8 @@
 // Estimate total area coverage
 CMSAPI cmsFloat64Number CMSEXPORT cmsDetectTAC(cmsHPROFILE hProfile);
 
+// Estimate gamma space, always positive. Returns -1 on error.
+CMSAPI cmsFloat64Number CMSEXPORT cmsDetectRGBProfileGamma(cmsHPROFILE hProfile, cmsFloat64Number threshold);
 
 // Poor man's gamut mapping
 CMSAPI cmsBool          CMSEXPORT cmsDesaturateLab(cmsCIELab* Lab,
diff --git a/third_party/lcms/include/lcms2_plugin.h b/third_party/lcms/include/lcms2_plugin.h
index 5bc8227..27fdb6a 100644
--- a/third_party/lcms/include/lcms2_plugin.h
+++ b/third_party/lcms/include/lcms2_plugin.h
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2020 Marti Maria Saguer
+//  Copyright (c) 1998-2022 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"),
@@ -24,7 +24,7 @@
 //---------------------------------------------------------------------------------
 //
 // This is the plug-in header file. Normal LittleCMS clients should not use it.
-// It is provided for plug-in writters that may want to access the support
+// It is provided for plug-in writers that may want to access the support
 // functions to do low level operations. All plug-in related structures
 // are defined here. Including this file forces to include the standard API too.
 
diff --git a/third_party/lcms/src/cmsalpha.c b/third_party/lcms/src/cmsalpha.c
index 36fdc9c..b34d82b 100644
--- a/third_party/lcms/src/cmsalpha.c
+++ b/third_party/lcms/src/cmsalpha.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2020 Marti Maria Saguer
+//  Copyright (c) 1998-2022 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"),
@@ -191,21 +191,21 @@
 void fromFLTto8(void* dst, const void* src)
 {
     cmsFloat32Number n = *(cmsFloat32Number*)src;
-    *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0f);
+    *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0);
 }
 
 static
 void fromFLTto16(void* dst, const void* src)
 {
     cmsFloat32Number n = *(cmsFloat32Number*)src;
-    *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0f);
+    *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0);
 }
 
 static
 void fromFLTto16SE(void* dst, const void* src)
 {
     cmsFloat32Number n = *(cmsFloat32Number*)src;
-    cmsUInt16Number i = _cmsQuickSaturateWord(n * 65535.0f);
+    cmsUInt16Number i = _cmsQuickSaturateWord(n * 65535.0);
 
     *(cmsUInt16Number*)dst = CHANGE_ENDIAN(i);
 }
@@ -243,7 +243,7 @@
 {
 #ifndef CMS_NO_HALF_SUPPORT
        cmsFloat32Number n = _cmsHalf2Float(*(cmsUInt16Number*)src);
-       *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0f);
+       *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0);
 #else
     cmsUNUSED_PARAMETER(dst);
     cmsUNUSED_PARAMETER(src);
@@ -256,7 +256,7 @@
 {
 #ifndef CMS_NO_HALF_SUPPORT
        cmsFloat32Number n = _cmsHalf2Float(*(cmsUInt16Number*)src);
-       *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0f);
+       *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0);
 #else
     cmsUNUSED_PARAMETER(dst);
     cmsUNUSED_PARAMETER(src);
@@ -268,7 +268,7 @@
 {
 #ifndef CMS_NO_HALF_SUPPORT
     cmsFloat32Number n = _cmsHalf2Float(*(cmsUInt16Number*)src);
-    cmsUInt16Number i = _cmsQuickSaturateWord(n * 65535.0f);
+    cmsUInt16Number i = _cmsQuickSaturateWord(n * 65535.0);
     *(cmsUInt16Number*)dst = CHANGE_ENDIAN(i);
 #else
     cmsUNUSED_PARAMETER(dst);
@@ -414,9 +414,9 @@
        cmsUInt32Number channelSize = trueBytesSize(Format);
        cmsUInt32Number pixelSize = channelSize * total_chans;
        
-	   // Sanity check
-	   if (total_chans <= 0 || total_chans >= cmsMAXCHANNELS)
-		   return;
+       // Sanity check
+       if (total_chans <= 0 || total_chans >= cmsMAXCHANNELS)
+           return;
 
         memset(channels, 0, sizeof(channels));
 
diff --git a/third_party/lcms/src/cmscam02.c b/third_party/lcms/src/cmscam02.c
index 5444b38..f7838ee 100644
--- a/third_party/lcms/src/cmscam02.c
+++ b/third_party/lcms/src/cmscam02.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2020 Marti Maria Saguer
+//  Copyright (c) 1998-2022 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"),
diff --git a/third_party/lcms/src/cmscgats.c b/third_party/lcms/src/cmscgats.c
index e0d5c12..241a88f 100644
--- a/third_party/lcms/src/cmscgats.c
+++ b/third_party/lcms/src/cmscgats.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2020 Marti Maria Saguer
+//  Copyright (c) 1998-2022 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"),
@@ -136,9 +136,18 @@
         FILE*          Stream;                   // File stream or NULL if holded in memory
     } FILECTX;
 
-// This struct hold all information about an open IT8 handler.
+//Very simple string 
 typedef struct {
 
+        struct struct_it8* it8;
+        cmsInt32Number max;
+        cmsInt32Number len;
+        char* begin;
+    } string;
+
+
+// This struct hold all information about an open IT8 handler.
+typedef struct struct_it8 {
 
         cmsUInt32Number  TablesCount;                     // How many tables in this stream
         cmsUInt32Number  nTable;                          // The actual table
@@ -156,8 +165,8 @@
         cmsInt32Number     inum;              // integer value
         cmsFloat64Number   dnum;              // real value
 
-        char           id[MAXID];             // identifier
-        char           str[MAXSTR];           // string
+        string*        id;            // identifier
+        string*        str;           // string
 
         // Allowed keywords & datasets. They have visibility on whole stream
         KEYVALUE*      ValidKeywords;
@@ -242,7 +251,7 @@
 
         {"MATERIAL",         WRITE_STRINGIFY},    // Identifies the material on which the target was produced using a code
                                                   // uniquely identifying th e material. This is intend ed to be used for IT8.7
-                                                  // physical targets only (i.e . IT8.7/1 a nd IT8.7/2).
+                                                  // physical targets only (i.e . IT8.7/1 and IT8.7/2).
 
         {"INSTRUMENTATION",  WRITE_STRINGIFY},    // Used to report the specific instrumentation used (manufacturer and
                                                   // model number) to generate the data reported. This data will often
@@ -357,6 +366,59 @@
 //Forward declaration of some internal functions
 static void* AllocChunk(cmsIT8* it8, cmsUInt32Number size);
 
+static
+string* StringAlloc(cmsIT8* it8, int max)
+{
+    string* s = (string*) AllocChunk(it8, sizeof(string));
+
+    s->it8 = it8;
+    s->max = max;
+    s->len = 0;
+    s->begin = (char*) AllocChunk(it8, s->max);
+
+    return s;
+}
+
+static
+void StringClear(string* s)
+{
+    s->len = 0;
+}
+
+static
+void StringAppend(string* s, char c)
+{
+    if (s->len + 1 >= s->max)
+    {
+        char* new_ptr;
+
+        s->max *= 10;
+        new_ptr = AllocChunk(s->it8, s->max);
+        memcpy(new_ptr, s->begin, s->len);
+        s->begin = new_ptr;
+    }
+
+    s->begin[s->len++] = c;
+    s->begin[s->len] = 0;
+}
+
+static
+char* StringPtr(string* s)
+{
+    return s->begin;
+}
+
+static
+void StringCat(string* s, const char* c)
+{
+    while (*c)
+    {
+        StringAppend(s, *c);
+        c++;
+    }
+}
+
+
 // Checks whatever c is a separator
 static
 cmsBool isseparator(int c)
@@ -682,14 +744,44 @@
 }
 
 
+// Reads a string, special case to avoid infinite resursion on .include
+static
+void InStringSymbol(cmsIT8* it8)
+{
+    while (isseparator(it8->ch))
+        NextCh(it8);
+
+    if (it8->ch == '\'' || it8->ch == '\"')
+    {
+        int sng;
+
+        sng = it8->ch;
+        StringClear(it8->str);
+
+        NextCh(it8);
+
+        while (it8->ch != sng) {
+
+            if (it8->ch == '\n' || it8->ch == '\r' || it8->ch == 0) break;
+            else {
+                StringAppend(it8->str, (char)it8->ch);
+                NextCh(it8);
+            }
+        }
+
+        it8->sy = SSTRING;
+        NextCh(it8);        
+    }
+    else
+        SynError(it8, "String expected");
+
+}
+
 // Reads next symbol
 static
 void InSymbol(cmsIT8* it8)
 {
-    CMSREGISTER char *idptr;
-    CMSREGISTER int k;
     SYMBOL key;
-    int sng;
     
     do {
 
@@ -698,21 +790,18 @@
 
         if (isfirstidchar(it8->ch)) {          // Identifier
 
-            k = 0;
-            idptr = it8->id;
+            StringClear(it8->id);
 
             do {
 
-                if (++k < MAXID) *idptr++ = (char) it8->ch;
+                StringAppend(it8->id, (char) it8->ch);
 
                 NextCh(it8);
 
             } while (isidchar(it8->ch));
 
-            *idptr = '\0';
 
-
-            key = BinSrchKey(it8->id);
+            key = BinSrchKey(StringPtr(it8->id));
             if (key == SUNDEFINED) it8->sy = SIDENT;
             else it8->sy = key;
 
@@ -808,26 +897,27 @@
 
                 if (isidchar(it8 ->ch)) {
 
+                    char buffer[127];
+
                     if (it8 ->sy == SINUM) {
 
-                        snprintf(it8->id, 127, "%d", it8->inum);
+                        snprintf(buffer, sizeof(buffer), "%d", it8->inum);
                     }
                     else {
 
-                        snprintf(it8->id, 127, it8 ->DoubleFormatter, it8->dnum);
+                        snprintf(buffer, sizeof(buffer), it8 ->DoubleFormatter, it8->dnum);
                     }
 
-                    k = (int) strlen(it8 ->id);
-                    idptr = it8 ->id + k;
+                    StringCat(it8->id, buffer);
+
                     do {
 
-                        if (++k < MAXID) *idptr++ = (char) it8->ch;
+                        StringAppend(it8->id, (char) it8->ch);
 
                         NextCh(it8);
 
                     } while (isidchar(it8->ch));
 
-                    *idptr = '\0';
                     it8->sy = SIDENT;
                 }
                 return;
@@ -875,24 +965,7 @@
         // String.
         case '\'':
         case '\"':
-            idptr = it8->str;
-            sng = it8->ch;
-            k = 0;
-            NextCh(it8);
-
-            while (k < (MAXSTR-1) && it8->ch != sng) {
-
-                if (it8->ch == '\n'|| it8->ch == '\r') k = MAXSTR+1;
-                else {
-                    *idptr++ = (char) it8->ch;
-                    NextCh(it8);
-                    k++;
-                }
-            }
-
-            it8->sy = SSTRING;
-            *idptr = '\0';
-            NextCh(it8);
+            InStringSymbol(it8);
             break;
 
 
@@ -915,7 +988,7 @@
                     return;
                 }
 
-                InSymbol(it8);
+                InStringSymbol(it8);
                 if (!Check(it8, SSTRING, "Filename expected")) return;
 
                 FileNest = it8 -> FileStack[it8 -> IncludeSP + 1];
@@ -926,7 +999,7 @@
                     //  TODO: how to manage out-of-memory conditions?
                 }
 
-                if (BuildAbsolutePath(it8->str,
+                if (BuildAbsolutePath(StringPtr(it8->str),
                                       it8->FileStack[it8->IncludeSP]->FileName,
                                       FileNest->FileName, cmsMAX_PATH-1) == FALSE) {
                     SynError(it8, "File path too long");
@@ -987,12 +1060,12 @@
     case SEOLN:   // Empty value
                   Buffer[0]=0;
                   break;
-    case SIDENT:  strncpy(Buffer, it8->id, max);
+    case SIDENT:  strncpy(Buffer, StringPtr(it8->id), max);
                   Buffer[max-1]=0;
                   break;
     case SINUM:   snprintf(Buffer, max, "%d", it8 -> inum); break;
     case SDNUM:   snprintf(Buffer, max, it8->DoubleFormatter, it8 -> dnum); break;
-    case SSTRING: strncpy(Buffer, it8->str, max);
+    case SSTRING: strncpy(Buffer, StringPtr(it8->str), max);
                   Buffer[max-1] = 0;
                   break;
 
@@ -1117,7 +1190,7 @@
 
 
     ptr = (char *) AllocChunk(it8, Size);
-    if (ptr) strncpy (ptr, str, Size-1);
+    if (ptr) memcpy(ptr, str, Size-1);
 
     return ptr;
 }
@@ -1316,6 +1389,9 @@
     it8->IncludeSP   = 0;
     it8 -> lineno = 1;
 
+    it8->id = StringAlloc(it8, MAXSTR);
+    it8->str = StringAlloc(it8, MAXSTR);
+
     strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT);
     cmsIT8SetSheetType((cmsHANDLE) it8, "CGATS.17");
 
@@ -1507,6 +1583,25 @@
     return atoi(b);
 }
 
+// Convert to binary
+static
+const char* satob(const char* v)
+{
+    cmsUInt32Number x;
+    static char buf[33];
+    char *s = buf + 33;
+    
+    if (v == NULL) return "0";
+    
+    x = atoi(v);
+    *--s = 0;
+    if (!x) *--s = '0';
+    for (; x; x /= 2) *--s = '0' + x%2;
+    
+    return s;
+}
+
+
 static
 void AllocateDataSet(cmsIT8* it8)
 {
@@ -1692,7 +1787,7 @@
                     break;
 
             case WRITE_BINARY:
-                    Writef(fp, "\t0x%B", satoi(p ->Value));
+                    Writef(fp, "\t0b%s", satob(p ->Value));
                     break;
 
             case WRITE_PAIR:
@@ -1862,7 +1957,7 @@
                 return SynError(it8, "Sample type expected");
             }
 
-            if (!SetDataFormat(it8, iField, it8->id)) return FALSE;
+            if (!SetDataFormat(it8, iField, StringPtr(it8->id))) return FALSE;
             iField++;
 
             InSymbol(it8);
@@ -1908,11 +2003,28 @@
 
         if (it8->sy != SEND_DATA && it8->sy != SEOF) {
 
+            switch (it8->sy)
+            {
+
+            // To keep very long data
+            case SIDENT:  
+                if (!SetData(it8, iSet, iField, StringPtr(it8->id)))
+                    return FALSE;
+                break;
+
+            case SSTRING:
+                if (!SetData(it8, iSet, iField, StringPtr(it8->str)))
+                    return FALSE;
+                break;
+
+            default:
+
             if (!GetVal(it8, Buffer, 255, "Sample data expected"))
                 return FALSE;
 
             if (!SetData(it8, iSet, iField, Buffer))
                 return FALSE;
+            }
 
             iField++;
 
@@ -1968,7 +2080,7 @@
 
 
         case SIDENT:
-            strncpy(VarName, it8->id, MAXID - 1);
+            strncpy(VarName, StringPtr(it8->id), MAXID - 1);
             VarName[MAXID - 1] = 0;
 
             if (!IsAvailableOnList(it8->ValidKeywords, VarName, NULL, &Key)) {
@@ -2112,7 +2224,7 @@
                                      // If a newline is found, then this is a type string
                                     if (it8 ->ch == '\n' || it8->ch == '\r') {
 
-                                         cmsIT8SetSheetType(it8, it8 ->id);
+                                         cmsIT8SetSheetType(it8, StringPtr(it8 ->id));
                                          InSymbol(it8);
                                     }
                                     else
@@ -2124,7 +2236,7 @@
                                 else
                                     // Validate quoted strings
                                     if (it8 ->sy == SSTRING) {
-                                        cmsIT8SetSheetType(it8, it8 ->str);
+                                        cmsIT8SetSheetType(it8, StringPtr(it8 ->str));
                                         InSymbol(it8);
                                     }
                            }
diff --git a/third_party/lcms/src/cmscnvrt.c b/third_party/lcms/src/cmscnvrt.c
index b471da7..fe25525 100644
--- a/third_party/lcms/src/cmscnvrt.c
+++ b/third_party/lcms/src/cmscnvrt.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2020 Marti Maria Saguer
+//  Copyright (c) 1998-2022 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"),
diff --git a/third_party/lcms/src/cmserr.c b/third_party/lcms/src/cmserr.c
index eb96d25..c65dce5 100644
--- a/third_party/lcms/src/cmserr.c
+++ b/third_party/lcms/src/cmserr.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2020 Marti Maria Saguer
+//  Copyright (c) 1998-2022 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"),
@@ -166,7 +166,7 @@
 
 // Sub allocation takes care of many pointers of small size. The memory allocated in
 // this way have be freed at once. Next function allocates a single chunk for linked list
-// I prefer this method over realloc due to the big inpact on xput realloc may have if
+// I prefer this method over realloc due to the big impact on xput realloc may have if
 // memory is being swapped to disk. This approach is safer (although that may not be true on all platforms)
 static
 _cmsSubAllocator_chunk* _cmsCreateSubAllocChunk(cmsContext ContextID, cmsUInt32Number Initial)
diff --git a/third_party/lcms/src/cmsgamma.c b/third_party/lcms/src/cmsgamma.c
index 08a7c22..dee1b4b 100644
--- a/third_party/lcms/src/cmsgamma.c
+++ b/third_party/lcms/src/cmsgamma.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2020 Marti Maria Saguer
+//  Copyright (c) 1998-2022 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"),
@@ -207,7 +207,7 @@
 }
 
 // Low level allocate, which takes care of memory details. nEntries may be zero, and in this case
-// no optimation curve is computed. nSegments may also be zero in the inverse case, where only the
+// no optimization curve is computed. nSegments may also be zero in the inverse case, where only the
 // optimization curve is given. Both features simultaneously is an error
 static
 cmsToneCurve* AllocateToneCurveStruct(cmsContext ContextID, cmsUInt32Number nEntries,
@@ -507,28 +507,31 @@
     // X=Y/c              | Y< (ad+b)^g
     case -4:
     {
-        if (fabs(Params[0]) < MATRIX_DET_TOLERANCE ||
-            fabs(Params[1]) < MATRIX_DET_TOLERANCE ||
-            fabs(Params[3]) < MATRIX_DET_TOLERANCE)
-        {
-            Val = 0;
-        }
+
+        e = Params[1] * Params[4] + Params[2];
+        if (e < 0)
+            disc = 0;
         else
-        {
-            e = Params[1] * Params[4] + Params[2];
-            if (e < 0)
-                disc = 0;
+            disc = pow(e, Params[0]);
+
+        if (R >= disc) {
+
+            if (fabs(Params[0]) < MATRIX_DET_TOLERANCE ||
+                fabs(Params[1]) < MATRIX_DET_TOLERANCE)
+
+                Val = 0;
+
             else
-                disc = pow(e, Params[0]);
-
-            if (R >= disc) {
-
                 Val = (pow(R, 1.0 / Params[0]) - Params[2]) / Params[1];
-            }
-            else {
-                Val = R / Params[3];
-            }
         }
+        else {
+
+            if (fabs(Params[3]) < MATRIX_DET_TOLERANCE)
+                Val = 0;
+            else
+                Val = R / Params[3];
+        }
+
     }
     break;
 
@@ -555,26 +558,29 @@
     // X=(Y-f)/c          | else
     case -5:
     {
-        if (fabs(Params[1]) < MATRIX_DET_TOLERANCE ||
-            fabs(Params[3]) < MATRIX_DET_TOLERANCE)
-        {
-            Val = 0;
-        }
-        else
-        {
-            disc = Params[3] * Params[4] + Params[6];
-            if (R >= disc) {
+        disc = Params[3] * Params[4] + Params[6];
+        if (R >= disc) {
 
-                e = R - Params[5];
-                if (e < 0)
+            e = R - Params[5];
+            if (e < 0)
+                Val = 0;
+            else
+            {
+                if (fabs(Params[0]) < MATRIX_DET_TOLERANCE ||
+                    fabs(Params[1]) < MATRIX_DET_TOLERANCE)
+
                     Val = 0;
                 else
                     Val = (pow(e, 1.0 / Params[0]) - Params[2]) / Params[1];
             }
-            else {
-                Val = (R - Params[6]) / Params[3];
-            }
         }
+        else {
+            if (fabs(Params[3]) < MATRIX_DET_TOLERANCE)
+                Val = 0;
+            else
+                Val = (R - Params[6]) / Params[3];
+        }
+
     }
     break;
 
diff --git a/third_party/lcms/src/cmsgmt.c b/third_party/lcms/src/cmsgmt.c
index 7432137..70f2803 100644
--- a/third_party/lcms/src/cmsgmt.c
+++ b/third_party/lcms/src/cmsgmt.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2020 Marti Maria Saguer
+//  Copyright (c) 1998-2021 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"),
@@ -199,15 +199,15 @@
 
     cmsHTRANSFORM hInput;               // From whatever input color space. 16 bits to DBL
     cmsHTRANSFORM hForward, hReverse;   // Transforms going from Lab to colorant and back
-    cmsFloat64Number Thereshold;        // The thereshold after which is considered out of gamut
+    cmsFloat64Number Threshold;         // The threshold after which is considered out of gamut
 
     } GAMUTCHAIN;
 
 // This sampler does compute gamut boundaries by comparing original
-// values with a transform going back and forth. Values above ERR_THERESHOLD
+// values with a transform going back and forth. Values above ERR_THRESHOLD
 // of maximum are considered out of gamut.
 
-#define ERR_THERESHOLD      5
+#define ERR_THRESHOLD      5
 
 
 static
@@ -246,17 +246,17 @@
 
 
     // if dE1 is small and dE2 is small, value is likely to be in gamut
-    if (dE1 < t->Thereshold && dE2 < t->Thereshold)
+    if (dE1 < t->Threshold && dE2 < t->Threshold)
         Out[0] = 0;
     else {
 
         // if dE1 is small and dE2 is big, undefined. Assume in gamut
-        if (dE1 < t->Thereshold && dE2 > t->Thereshold)
+        if (dE1 < t->Threshold && dE2 > t->Threshold)
             Out[0] = 0;
         else
             // dE1 is big and dE2 is small, clearly out of gamut
-            if (dE1 > t->Thereshold && dE2 < t->Thereshold)
-                Out[0] = (cmsUInt16Number) _cmsQuickFloor((dE1 - t->Thereshold) + .5);
+            if (dE1 > t->Threshold && dE2 < t->Threshold)
+                Out[0] = (cmsUInt16Number) _cmsQuickFloor((dE1 - t->Threshold) + .5);
             else  {
 
                 // dE1 is big and dE2 is also big, could be due to perceptual mapping
@@ -266,8 +266,8 @@
                 else
                     ErrorRatio = dE1 / dE2;
 
-                if (ErrorRatio > t->Thereshold)
-                    Out[0] = (cmsUInt16Number)  _cmsQuickFloor((ErrorRatio - t->Thereshold) + .5);
+                if (ErrorRatio > t->Threshold)
+                    Out[0] = (cmsUInt16Number)  _cmsQuickFloor((ErrorRatio - t->Threshold) + .5);
                 else
                     Out[0] = 0;
             }
@@ -323,10 +323,10 @@
 
     if (cmsIsMatrixShaper(hGamut)) {
 
-        Chain.Thereshold = 1.0;
+        Chain.Threshold = 1.0;
     }
     else {
-        Chain.Thereshold = ERR_THERESHOLD;
+        Chain.Threshold = ERR_THRESHOLD;
     }
 
 
@@ -588,3 +588,67 @@
 
     return TRUE;
 }
+
+// Detect whatever a given ICC profile works in linear (gamma 1.0) space
+// Actually, doing that "well" is quite hard, since every component may behave completely different.
+// Since the true point of this function is to detect suitable optimizations, I am imposing some requirements 
+// that simplifies things: only RGB, and only profiles that can got in both directions.
+// The algorithm obtains Y from a syntetical gray R=G=B. Then least squares fitting is used to estimate gamma. 
+// For gamma close to 1.0, RGB is linear. On profiles not supported, -1 is returned.
+
+cmsFloat64Number CMSEXPORT cmsDetectRGBProfileGamma(cmsHPROFILE hProfile, cmsFloat64Number threshold)
+{
+    cmsContext ContextID;
+    cmsHPROFILE hXYZ;
+    cmsHTRANSFORM xform;
+    cmsToneCurve* Y_curve;
+    cmsUInt16Number rgb[256][3];
+    cmsCIEXYZ XYZ[256];
+    cmsFloat32Number Y_normalized[256];
+    cmsFloat64Number gamma;
+    cmsProfileClassSignature cl;
+    int i;
+
+    if (cmsGetColorSpace(hProfile) != cmsSigRgbData)
+        return -1;
+
+    cl = cmsGetDeviceClass(hProfile);
+    if (cl != cmsSigInputClass && cl != cmsSigDisplayClass && 
+        cl != cmsSigOutputClass && cl != cmsSigColorSpaceClass)
+        return -1;
+
+    ContextID = cmsGetProfileContextID(hProfile);
+    hXYZ = cmsCreateXYZProfileTHR(ContextID);
+    xform = cmsCreateTransformTHR(ContextID, hProfile, TYPE_RGB_16, hXYZ, TYPE_XYZ_DBL, 
+                                    INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOOPTIMIZE);
+
+    if (xform == NULL) { // If not RGB or forward direction is not supported, regret with the previous error
+
+        cmsCloseProfile(hXYZ);        
+        return -1;
+    }
+
+    for (i = 0; i < 256; i++) {
+        rgb[i][0] = rgb[i][1] = rgb[i][2] = FROM_8_TO_16(i);       
+    }
+
+    cmsDoTransform(xform, rgb, XYZ, 256);
+
+    cmsDeleteTransform(xform);
+    cmsCloseProfile(hXYZ);
+
+    for (i = 0; i < 256; i++) {
+        Y_normalized[i] = (cmsFloat32Number) XYZ[i].Y;
+    }
+
+    Y_curve = cmsBuildTabulatedToneCurveFloat(ContextID, 256, Y_normalized);
+    if (Y_curve == NULL)     
+        return -1;
+    
+    gamma = cmsEstimateGamma(Y_curve, threshold);
+
+    cmsFreeToneCurve(Y_curve);
+
+    return gamma;
+}
+
diff --git a/third_party/lcms/src/cmshalf.c b/third_party/lcms/src/cmshalf.c
index 52c0a2a..57cb5b0 100644
--- a/third_party/lcms/src/cmshalf.c
+++ b/third_party/lcms/src/cmshalf.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2020 Marti Maria Saguer
+//  Copyright (c) 1998-2022 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"),
diff --git a/third_party/lcms/src/cmsintrp.c b/third_party/lcms/src/cmsintrp.c
index 22de3e5..5cd6d16 100644
--- a/third_party/lcms/src/cmsintrp.c
+++ b/third_party/lcms/src/cmsintrp.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2020 Marti Maria Saguer
+//  Copyright (c) 1998-2022 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"),
diff --git a/third_party/lcms/src/cmsio0.c b/third_party/lcms/src/cmsio0.c
index ff8c464..0724eaa 100644
--- a/third_party/lcms/src/cmsio0.c
+++ b/third_party/lcms/src/cmsio0.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2020 Marti Maria Saguer
+//  Copyright (c) 1998-2022 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"),
@@ -479,10 +479,10 @@
 
 cmsIOHANDLER* CMSEXPORT cmsGetProfileIOhandler(cmsHPROFILE hProfile)
 {
-	_cmsICCPROFILE* Icc = (_cmsICCPROFILE*)hProfile;
+    _cmsICCPROFILE* Icc = (_cmsICCPROFILE*)hProfile;
 
-	if (Icc == NULL) return NULL;
-	return Icc->IOhandler;
+    if (Icc == NULL) return NULL;
+    return Icc->IOhandler;
 }
 
 #ifdef _WIN32_WCE
@@ -496,7 +496,6 @@
 // Creates an empty structure holding all required parameters
 cmsHPROFILE CMSEXPORT cmsCreateProfilePlaceholder(cmsContext ContextID)
 {
-    time_t now = time(NULL);
     _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) _cmsMallocZero(ContextID, sizeof(_cmsICCPROFILE));
     if (Icc == NULL) return NULL;
 
@@ -507,15 +506,20 @@
 
     // Set default version
     Icc ->Version =  0x02100000;
-
+    
     // Set creation date/time
-    memmove(&Icc ->Created, gmtime(&now), sizeof(Icc ->Created));
+    if (!_cmsGetTime(&Icc->Created))
+        goto Error;
 
     // Create a mutex if the user provided proper plugin. NULL otherwise
     Icc ->UsrMutex = _cmsCreateMutex(ContextID);
 
     // Return the handle
     return (cmsHPROFILE) Icc;
+
+Error:
+    _cmsFree(ContextID, Icc);
+    return NULL;
 }
 
 cmsContext CMSEXPORT cmsGetProfileContextID(cmsHPROFILE hProfile)
@@ -1453,7 +1457,25 @@
     return rc;
 }
 
+// Free one tag contents
+static
+void freeOneTag(_cmsICCPROFILE* Icc, cmsUInt32Number i)
+{
+    if (Icc->TagPtrs[i]) {
 
+        cmsTagTypeHandler* TypeHandler = Icc->TagTypeHandlers[i];
+
+        if (TypeHandler != NULL) {
+            cmsTagTypeHandler LocalTypeHandler = *TypeHandler;
+
+            LocalTypeHandler.ContextID = Icc->ContextID;             
+            LocalTypeHandler.ICCVersion = Icc->Version;
+            LocalTypeHandler.FreePtr(&LocalTypeHandler, Icc->TagPtrs[i]);
+        }
+        else
+            _cmsFree(Icc->ContextID, Icc->TagPtrs[i]);
+    }
+}
 
 // Closes a profile freeing any involved resources
 cmsBool  CMSEXPORT cmsCloseProfile(cmsHPROFILE hProfile)
@@ -1473,20 +1495,7 @@
 
     for (i=0; i < Icc -> TagCount; i++) {
 
-        if (Icc -> TagPtrs[i]) {
-
-            cmsTagTypeHandler* TypeHandler = Icc ->TagTypeHandlers[i];
-
-            if (TypeHandler != NULL) {
-                cmsTagTypeHandler LocalTypeHandler = *TypeHandler;
-
-                LocalTypeHandler.ContextID = Icc ->ContextID;              // As an additional parameters
-                LocalTypeHandler.ICCVersion = Icc ->Version;
-                LocalTypeHandler.FreePtr(&LocalTypeHandler, Icc -> TagPtrs[i]);
-            }
-            else
-                _cmsFree(Icc ->ContextID, Icc ->TagPtrs[i]);
-        }
+        freeOneTag(Icc, i);        
     }
 
     if (Icc ->IOhandler != NULL) {
@@ -1526,7 +1535,7 @@
 void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig)
 {
     _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
-    cmsIOHANDLER* io = Icc ->IOhandler;
+    cmsIOHANDLER* io;
     cmsTagTypeHandler* TypeHandler;
     cmsTagTypeHandler LocalTypeHandler;
     cmsTagDescriptor*  TagDescriptor;
@@ -1571,6 +1580,7 @@
 
     if (TagSize < 8) goto Error;
 
+    io = Icc ->IOhandler;
     // Seek to its location
     if (!io -> Seek(io, Offset))
         goto Error;
@@ -1638,8 +1648,12 @@
     return Icc -> TagPtrs[n];
 
 
-    // Return error and unlock tha data
+    // Return error and unlock the data
 Error:
+
+    freeOneTag(Icc, n);    
+    Icc->TagPtrs[n] = NULL;
+    
     _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
     return NULL;
 }
@@ -1805,7 +1819,7 @@
     // It is already read?
     if (Icc -> TagPtrs[i] == NULL) {
 
-        // No yet, get original position
+        // Not yet, get original position
         Offset   = Icc ->TagOffsets[i];
         TagSize  = Icc ->TagSizes[i];
 
diff --git a/third_party/lcms/src/cmsio1.c b/third_party/lcms/src/cmsio1.c
index f2a3970..db18b9f 100644
--- a/third_party/lcms/src/cmsio1.c
+++ b/third_party/lcms/src/cmsio1.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2020 Marti Maria Saguer
+//  Copyright (c) 1998-2022 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"),
@@ -260,7 +260,7 @@
 
 
 
-// Read the DToAX tag, adjusting the encoding of Lab or XYZ if neded
+// Read the DToAX tag, adjusting the encoding of Lab or XYZ if needed
 static
 cmsPipeline* _cmsReadFloatInputTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat)
 {
@@ -536,7 +536,7 @@
 }
 
 
-// Read the DToAX tag, adjusting the encoding of Lab or XYZ if neded
+// Read the DToAX tag, adjusting the encoding of Lab or XYZ if needed
 static
 cmsPipeline* _cmsReadFloatOutputTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat)
 {
@@ -661,7 +661,7 @@
 
 // ---------------------------------------------------------------------------------------------------------------
 
-// Read the AToD0 tag, adjusting the encoding of Lab or XYZ if neded
+// Read the AToD0 tag, adjusting the encoding of Lab or XYZ if needed
 static
 cmsPipeline* _cmsReadFloatDevicelinkTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat)
 {
diff --git a/third_party/lcms/src/cmslut.c b/third_party/lcms/src/cmslut.c
index 04ece7f..49cc63b 100644
--- a/third_party/lcms/src/cmslut.c
+++ b/third_party/lcms/src/cmslut.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2020 Marti Maria Saguer
+//  Copyright (c) 1998-2022 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"),
@@ -1226,6 +1226,11 @@
     return mpe -> Data;
 }
 
+cmsContext CMSEXPORT cmsGetStageContextID(const cmsStage* mpe)
+{
+    return mpe -> ContextID;
+}
+
 cmsStage*  CMSEXPORT cmsStageNext(const cmsStage* mpe)
 {
     return mpe -> Next;
diff --git a/third_party/lcms/src/cmsmd5.c b/third_party/lcms/src/cmsmd5.c
index 88b2d77..f0af214 100644
--- a/third_party/lcms/src/cmsmd5.c
+++ b/third_party/lcms/src/cmsmd5.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2020 Marti Maria Saguer
+//  Copyright (c) 1998-2022 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"),
diff --git a/third_party/lcms/src/cmsmtrx.c b/third_party/lcms/src/cmsmtrx.c
index 9e05355..86a66f7 100644
--- a/third_party/lcms/src/cmsmtrx.c
+++ b/third_party/lcms/src/cmsmtrx.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2020 Marti Maria Saguer
+//  Copyright (c) 1998-2022 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"),
diff --git a/third_party/lcms/src/cmsnamed.c b/third_party/lcms/src/cmsnamed.c
index c0595dd..28e67f2 100644
--- a/third_party/lcms/src/cmsnamed.c
+++ b/third_party/lcms/src/cmsnamed.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2020 Marti Maria Saguer
+//  Copyright (c) 1998-2022 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"),
@@ -204,7 +204,7 @@
 }
 
 // Add an ASCII entry. Do not add any \0 termination (ICC1v43_2010-12.pdf page 61)
-// In the case the user explicitely sets an empty string, we force a \0
+// In the case the user explicitly sets an empty string, we force a \0
 cmsBool CMSEXPORT cmsMLUsetASCII(cmsMLU* mlu, const char LanguageCode[3], const char CountryCode[3], const char* ASCIIString)
 {
     cmsUInt32Number i, len = (cmsUInt32Number) strlen(ASCIIString);
@@ -641,7 +641,7 @@
      return NamedColorList ->nColors;
 }
 
-// Info aboout a given color
+// Info about a given color
 cmsBool  CMSEXPORT cmsNamedColorInfo(const cmsNAMEDCOLORLIST* NamedColorList, cmsUInt32Number nColor,
                                      char* Name,
                                      char* Prefix,
diff --git a/third_party/lcms/src/cmsopt.c b/third_party/lcms/src/cmsopt.c
index 1f1173f..3eb8f62 100644
--- a/third_party/lcms/src/cmsopt.c
+++ b/third_party/lcms/src/cmsopt.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2020 Marti Maria Saguer
+//  Copyright (c) 1998-2022 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"),
@@ -680,7 +680,6 @@
 {
     cmsPipeline* Src = NULL;
     cmsPipeline* Dest = NULL;
-    cmsStage* mpe;
     cmsStage* CLUT;
     cmsStage *KeepPreLin = NULL, *KeepPostLin = NULL;
     cmsUInt32Number nGridPoints;
@@ -702,7 +701,7 @@
     if (ColorSpace == (cmsColorSpaceSignature)0 ||
         OutputColorSpace == (cmsColorSpaceSignature)0) return FALSE;
 
-    nGridPoints      = _cmsReasonableGridpointsByColorspace(ColorSpace, *dwFlags);
+    nGridPoints = _cmsReasonableGridpointsByColorspace(ColorSpace, *dwFlags);
 
     // For empty LUTs, 2 points are enough
     if (cmsPipelineStageCount(*Lut) == 0)
@@ -710,13 +709,6 @@
 
     Src = *Lut;
 
-    // Named color pipelines cannot be optimized either
-    for (mpe = cmsPipelineGetPtrToFirstStage(Src);
-        mpe != NULL;
-        mpe = cmsStageNext(mpe)) {
-            if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE;
-    }
-
     // Allocate an empty LUT
     Dest =  cmsPipelineAlloc(Src ->ContextID, Src ->InputChannels, Src ->OutputChannels);
     if (!Dest) return FALSE;
@@ -1084,7 +1076,6 @@
     cmsStage* OptimizedCLUTmpe;
     cmsColorSpaceSignature ColorSpace, OutputColorSpace;
     cmsStage* OptimizedPrelinMpe;
-    cmsStage* mpe;
     cmsToneCurve** OptimizedPrelinCurves;
     _cmsStageCLutData* OptimizedPrelinCLUT;
 
@@ -1105,14 +1096,7 @@
     }
 
     OriginalLut = *Lut;
-
-   // Named color pipelines cannot be optimized either
-   for (mpe = cmsPipelineGetPtrToFirstStage(OriginalLut);
-         mpe != NULL;
-         mpe = cmsStageNext(mpe)) {
-            if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE;
-    }
-
+   
     ColorSpace       = _cmsICCcolorSpace((int) T_COLORSPACE(*InputFormat));
     OutputColorSpace = _cmsICCcolorSpace((int) T_COLORSPACE(*OutputFormat));
 
@@ -1952,6 +1936,7 @@
     _cmsOptimizationPluginChunkType* ctx = ( _cmsOptimizationPluginChunkType*) _cmsContextGetClientChunk(ContextID, OptimizationPlugin);
     _cmsOptimizationCollection* Opts;
     cmsBool AnySuccess = FALSE;
+    cmsStage* mpe;
 
     // A CLUT is being asked, so force this specific optimization
     if (*dwFlags & cmsFLAGS_FORCE_CLUT) {
@@ -1966,6 +1951,13 @@
         return TRUE;
     }
 
+    // Named color pipelines cannot be optimized 
+    for (mpe = cmsPipelineGetPtrToFirstStage(*PtrLut);
+        mpe != NULL;
+        mpe = cmsStageNext(mpe)) {
+        if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE;
+    }
+
     // Try to get rid of identities and trivial conversions.
     AnySuccess = PreOptimize(*PtrLut);
 
diff --git a/third_party/lcms/src/cmspack.c b/third_party/lcms/src/cmspack.c
index 9510802..955687c 100644
--- a/third_party/lcms/src/cmspack.c
+++ b/third_party/lcms/src/cmspack.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2020 Marti Maria Saguer
+//  Copyright (c) 1998-2022 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"),
@@ -79,6 +79,7 @@
 #define ANYSWAP         DOSWAP_SH(1)
 #define ANYSWAPFIRST    SWAPFIRST_SH(1)
 #define ANYFLAVOR       FLAVOR_SH(1)
+#define ANYPREMUL       PREMUL_SH(1)
 
 
 // Suppress waning about info never being used
@@ -102,20 +103,40 @@
     cmsUInt32Number Reverse    = T_FLAVOR(info ->InputFormat);
     cmsUInt32Number SwapFirst  = T_SWAPFIRST(info -> InputFormat);
     cmsUInt32Number Extra      = T_EXTRA(info -> InputFormat);
+    cmsUInt32Number Premul     = T_PREMUL(info->InputFormat);
+
     cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
-    cmsUInt16Number v;
-    cmsUInt32Number i;
+    cmsUInt32Number v;
+    cmsUInt32Number i;  
+    cmsUInt32Number alpha_factor = 1;
 
     if (ExtraFirst) {
+        
+        if (Premul && Extra)
+            alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(accum[0]));
+
         accum += Extra;
     }
+    else
+    {
+        if (Premul && Extra)        
+            alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(accum[nChan]));
+    }
 
     for (i=0; i < nChan; i++) {
+
         cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
 
         v = FROM_8_TO_16(*accum);
         v = Reverse ? REVERSE_FLAVOR_16(v) : v;
-        wIn[index] = v;
+
+        if (Premul && alpha_factor > 0)
+        {
+            v = ((cmsUInt32Number)((cmsUInt32Number)v << 16) / alpha_factor);
+            if (v > 0xffff) v = 0xffff;
+        }
+
+        wIn[index] = (cmsUInt16Number) v;
         accum++;
     }
 
@@ -137,6 +158,7 @@
 
 }
 
+
 // Extra channels are just ignored because come in the next planes
 static
 cmsUInt8Number* UnrollPlanarBytes(CMSREGISTER _cmsTRANSFORM* info,
@@ -149,24 +171,47 @@
     cmsUInt32Number SwapFirst = T_SWAPFIRST(info ->InputFormat);
     cmsUInt32Number Reverse   = T_FLAVOR(info ->InputFormat);
     cmsUInt32Number i;
+    cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
+    cmsUInt32Number Extra = T_EXTRA(info->InputFormat);
+    cmsUInt32Number Premul = T_PREMUL(info->InputFormat);
     cmsUInt8Number* Init = accum;
+    cmsUInt32Number alpha_factor = 1;
 
-    if (DoSwap ^ SwapFirst) {
-        accum += T_EXTRA(info -> InputFormat) * Stride;
+    if (ExtraFirst) {
+
+        if (Premul && Extra)        
+            alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(accum[0]));
+
+
+        accum += Extra * Stride;
+    }
+    else
+    {
+        if (Premul && Extra)
+            alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(accum[(nChan) * Stride]));
     }
 
     for (i=0; i < nChan; i++) {
 
         cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
-        cmsUInt16Number v = FROM_8_TO_16(*accum);
+        cmsUInt32Number v = FROM_8_TO_16(*accum);
+        
+        v = Reverse ? REVERSE_FLAVOR_16(v) : v;
 
-        wIn[index] = Reverse ? REVERSE_FLAVOR_16(v) : v;
+        if (Premul && alpha_factor > 0)
+        {
+            v = ((cmsUInt32Number)((cmsUInt32Number)v << 16) / alpha_factor);
+            if (v > 0xffff) v = 0xffff;
+        }
+
+        wIn[index] = (cmsUInt16Number) v;
         accum += Stride;
     }
 
     return (Init + 1);
 }
 
+
 // Special cases, provided for performance
 static
 cmsUInt8Number* Unroll4Bytes(CMSREGISTER _cmsTRANSFORM* info,
@@ -517,6 +562,55 @@
     cmsUNUSED_PARAMETER(Stride);
 }
 
+
+static
+cmsUInt8Number* UnrollAnyWordsPremul(CMSREGISTER _cmsTRANSFORM* info,
+                                     CMSREGISTER cmsUInt16Number wIn[],
+                                     CMSREGISTER cmsUInt8Number* accum,
+                                     CMSREGISTER cmsUInt32Number Stride)
+{
+   cmsUInt32Number nChan       = T_CHANNELS(info -> InputFormat);
+   cmsUInt32Number SwapEndian  = T_ENDIAN16(info -> InputFormat);
+   cmsUInt32Number DoSwap      = T_DOSWAP(info ->InputFormat);
+   cmsUInt32Number Reverse     = T_FLAVOR(info ->InputFormat);
+   cmsUInt32Number SwapFirst   = T_SWAPFIRST(info -> InputFormat);   
+   cmsUInt32Number ExtraFirst  = DoSwap ^ SwapFirst;
+   cmsUInt32Number i;
+
+   cmsUInt16Number alpha = (ExtraFirst ? accum[0] : accum[nChan - 1]);
+   cmsUInt32Number alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(alpha));
+
+    if (ExtraFirst) {
+        accum += sizeof(cmsUInt16Number);
+    }
+
+    for (i=0; i < nChan; i++) {
+
+        cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
+        cmsUInt32Number v = *(cmsUInt16Number*) accum;
+
+        if (SwapEndian)
+            v = CHANGE_ENDIAN(v);
+
+        v = (v << 16) / alpha_factor;
+        if (v > 0xffff) v = 0xffff;
+
+        wIn[index] = (cmsUInt16Number) (Reverse ? REVERSE_FLAVOR_16(v) : v);
+
+        accum += sizeof(cmsUInt16Number);
+    }
+
+    if (!ExtraFirst) {
+        accum += sizeof(cmsUInt16Number);
+    }
+
+    return accum;
+
+    cmsUNUSED_PARAMETER(Stride);
+}
+
+
+
 static
 cmsUInt8Number* UnrollPlanarWords(CMSREGISTER _cmsTRANSFORM* info,
                                   CMSREGISTER cmsUInt16Number wIn[],
@@ -550,6 +644,46 @@
     return (Init + sizeof(cmsUInt16Number));
 }
 
+static
+cmsUInt8Number* UnrollPlanarWordsPremul(CMSREGISTER _cmsTRANSFORM* info,
+                                        CMSREGISTER cmsUInt16Number wIn[],
+                                        CMSREGISTER cmsUInt8Number* accum,
+                                        CMSREGISTER cmsUInt32Number Stride)
+{
+    cmsUInt32Number nChan = T_CHANNELS(info -> InputFormat);
+    cmsUInt32Number DoSwap= T_DOSWAP(info ->InputFormat);
+    cmsUInt32Number SwapFirst = T_SWAPFIRST(info->InputFormat);
+    cmsUInt32Number Reverse= T_FLAVOR(info ->InputFormat);
+    cmsUInt32Number SwapEndian = T_ENDIAN16(info -> InputFormat);
+    cmsUInt32Number i;
+    cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
+    cmsUInt8Number* Init = accum;
+
+    cmsUInt16Number  alpha = (ExtraFirst ? accum[0] : accum[(nChan - 1) * Stride]);
+    cmsUInt32Number alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(alpha));
+
+    if (ExtraFirst) {
+        accum += Stride;
+    }
+
+    for (i=0; i < nChan; i++) {
+
+        cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
+        cmsUInt32Number v = (cmsUInt32Number) *(cmsUInt16Number*) accum;
+
+        if (SwapEndian)
+            v = CHANGE_ENDIAN(v);
+
+        v = (v << 16) / alpha_factor;
+        if (v > 0xffff) v = 0xffff;
+
+        wIn[index] = (cmsUInt16Number) (Reverse ? REVERSE_FLAVOR_16(v) : v);
+
+        accum +=  Stride;
+    }
+
+    return (Init + sizeof(cmsUInt16Number));
+}
 
 static
 cmsUInt8Number* Unroll4Words(CMSREGISTER _cmsTRANSFORM* info,
@@ -1087,6 +1221,110 @@
 
 //-------------------------------------------------------------------------------------------------------------------
 
+// For anything going from cmsUInt8Number
+static
+cmsUInt8Number* Unroll8ToFloat(_cmsTRANSFORM* info,
+                               cmsFloat32Number wIn[],
+                               cmsUInt8Number* accum,
+                               cmsUInt32Number Stride)
+{
+
+    cmsUInt32Number nChan = T_CHANNELS(info->InputFormat);
+    cmsUInt32Number DoSwap = T_DOSWAP(info->InputFormat);
+    cmsUInt32Number Reverse = T_FLAVOR(info->InputFormat);
+    cmsUInt32Number SwapFirst = T_SWAPFIRST(info->InputFormat);
+    cmsUInt32Number Extra = T_EXTRA(info->InputFormat);
+    cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
+    cmsUInt32Number Planar = T_PLANAR(info->InputFormat);
+    cmsFloat32Number v;
+    cmsUInt32Number i, start = 0;
+    
+    Stride /= PixelSize(info->InputFormat);
+
+    if (ExtraFirst)
+        start = Extra;
+
+    for (i = 0; i < nChan; i++) {
+
+        cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
+
+        if (Planar)
+            v = (cmsFloat32Number) ((cmsUInt8Number *)accum)[(i + start) * Stride];
+        else
+            v = (cmsFloat32Number) ((cmsUInt8Number *)accum)[i + start];
+
+        v /= 255.0F;
+
+        wIn[index] = Reverse ? 1 - v : v;
+    }
+
+
+    if (Extra == 0 && SwapFirst) {
+        cmsFloat32Number tmp = wIn[0];
+
+        memmove(&wIn[0], &wIn[1], (nChan - 1) * sizeof(cmsFloat32Number));
+        wIn[nChan - 1] = tmp;
+    }
+
+    if (T_PLANAR(info->InputFormat))
+        return accum + sizeof(cmsUInt8Number);
+    else
+        return accum + (nChan + Extra) * sizeof(cmsUInt8Number);
+}
+
+
+// For anything going from cmsUInt16Number
+static
+cmsUInt8Number* Unroll16ToFloat(_cmsTRANSFORM* info,
+                                cmsFloat32Number wIn[],
+                                cmsUInt8Number* accum,
+                                cmsUInt32Number Stride)
+{
+
+    cmsUInt32Number nChan = T_CHANNELS(info->InputFormat);
+    cmsUInt32Number DoSwap = T_DOSWAP(info->InputFormat);
+    cmsUInt32Number Reverse = T_FLAVOR(info->InputFormat);
+    cmsUInt32Number SwapFirst = T_SWAPFIRST(info->InputFormat);
+    cmsUInt32Number Extra = T_EXTRA(info->InputFormat);
+    cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
+    cmsUInt32Number Planar = T_PLANAR(info->InputFormat);
+    cmsFloat32Number v;
+    cmsUInt32Number i, start = 0;
+
+    Stride /= PixelSize(info->InputFormat);
+
+    if (ExtraFirst)
+        start = Extra;
+
+    for (i = 0; i < nChan; i++) {
+
+        cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
+
+        if (Planar)
+            v = (cmsFloat32Number)((cmsUInt16Number*)accum)[(i + start) * Stride];
+        else
+            v = (cmsFloat32Number)((cmsUInt16Number*)accum)[i + start];
+
+        v /= 65535.0F;
+
+        wIn[index] = Reverse ? 1 - v : v;
+    }
+
+
+    if (Extra == 0 && SwapFirst) {
+        cmsFloat32Number tmp = wIn[0];
+
+        memmove(&wIn[0], &wIn[1], (nChan - 1) * sizeof(cmsFloat32Number));
+        wIn[nChan - 1] = tmp;
+    }
+
+    if (T_PLANAR(info->InputFormat))
+        return accum + sizeof(cmsUInt16Number);
+    else
+        return accum + (nChan + Extra) * sizeof(cmsUInt16Number);
+}
+
+
 // For anything going from cmsFloat32Number
 static
 cmsUInt8Number* UnrollFloatsToFloat(_cmsTRANSFORM* info,
@@ -1095,19 +1333,30 @@
                                     cmsUInt32Number Stride)
 {
 
-    cmsUInt32Number nChan  = T_CHANNELS(info -> InputFormat);
-    cmsUInt32Number DoSwap   = T_DOSWAP(info ->InputFormat);
-    cmsUInt32Number Reverse    = T_FLAVOR(info ->InputFormat);
-    cmsUInt32Number SwapFirst  = T_SWAPFIRST(info -> InputFormat);
-    cmsUInt32Number Extra   = T_EXTRA(info -> InputFormat);
+    cmsUInt32Number nChan = T_CHANNELS(info->InputFormat);
+    cmsUInt32Number DoSwap = T_DOSWAP(info->InputFormat);
+    cmsUInt32Number Reverse = T_FLAVOR(info->InputFormat);
+    cmsUInt32Number SwapFirst = T_SWAPFIRST(info->InputFormat);
+    cmsUInt32Number Extra = T_EXTRA(info->InputFormat);
     cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
-    cmsUInt32Number Planar     = T_PLANAR(info -> InputFormat);
+    cmsUInt32Number Planar = T_PLANAR(info->InputFormat);
+    cmsUInt32Number Premul = T_PREMUL(info->InputFormat);
     cmsFloat32Number v;
     cmsUInt32Number i, start = 0;
-    cmsFloat32Number maximum = IsInkSpace(info ->InputFormat) ? 100.0F : 1.0F;
+    cmsFloat32Number maximum = IsInkSpace(info->InputFormat) ? 100.0F : 1.0F;
+    cmsFloat32Number alpha_factor = 1.0f;
+    cmsFloat32Number* ptr = (cmsFloat32Number*)accum;
 
     Stride /= PixelSize(info->InputFormat);
 
+    if (Premul && Extra)
+    {        
+        if (Planar)
+            alpha_factor = (ExtraFirst ? ptr[0] : ptr[nChan * Stride]) / maximum;            
+        else
+            alpha_factor = (ExtraFirst ? ptr[0] : ptr[nChan]) / maximum;        
+    }
+
     if (ExtraFirst)
             start = Extra;
 
@@ -1116,9 +1365,12 @@
         cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
 
         if (Planar)
-            v = (cmsFloat32Number) ((cmsFloat32Number*) accum)[(i + start) * Stride];
+            v = ptr[(i + start) * Stride];
         else
-            v = (cmsFloat32Number) ((cmsFloat32Number*) accum)[i + start];
+            v = ptr[i + start];
+
+        if (Premul && alpha_factor > 0)
+            v /= alpha_factor;
 
         v /= maximum;
 
@@ -1148,19 +1400,30 @@
                                     cmsUInt32Number Stride)
 {
 
-    cmsUInt32Number nChan  = T_CHANNELS(info -> InputFormat);
-    cmsUInt32Number DoSwap   = T_DOSWAP(info ->InputFormat);
-    cmsUInt32Number Reverse    = T_FLAVOR(info ->InputFormat);
-    cmsUInt32Number SwapFirst  = T_SWAPFIRST(info -> InputFormat);
-    cmsUInt32Number Extra   = T_EXTRA(info -> InputFormat);
+    cmsUInt32Number nChan = T_CHANNELS(info->InputFormat);
+    cmsUInt32Number DoSwap = T_DOSWAP(info->InputFormat);
+    cmsUInt32Number Reverse = T_FLAVOR(info->InputFormat);
+    cmsUInt32Number SwapFirst = T_SWAPFIRST(info->InputFormat);
+    cmsUInt32Number Extra = T_EXTRA(info->InputFormat);
     cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
-    cmsUInt32Number Planar     = T_PLANAR(info -> InputFormat);
+    cmsUInt32Number Planar = T_PLANAR(info->InputFormat);
+    cmsUInt32Number Premul = T_PREMUL(info->InputFormat);
     cmsFloat64Number v;
     cmsUInt32Number i, start = 0;
     cmsFloat64Number maximum = IsInkSpace(info ->InputFormat) ? 100.0 : 1.0;
+    cmsFloat64Number alpha_factor = 1.0;
+    cmsFloat64Number* ptr = (cmsFloat64Number*)accum;
 
     Stride /= PixelSize(info->InputFormat);
 
+    if (Premul && Extra)
+    {
+        if (Planar)
+            alpha_factor = (ExtraFirst ? ptr[0] : ptr[(nChan) * Stride]) / maximum;
+        else
+            alpha_factor = (ExtraFirst ? ptr[0] : ptr[nChan]) / maximum;
+    }
+   
     if (ExtraFirst)
             start = Extra;
 
@@ -1173,6 +1436,10 @@
         else
             v = (cmsFloat64Number) ((cmsFloat64Number*) accum)[i + start];
 
+
+        if (Premul && alpha_factor > 0)
+            v /= alpha_factor;
+
         v /= maximum;
 
         wIn[index] = (cmsFloat32Number) (Reverse ? 1.0 - v : v);
@@ -1254,8 +1521,6 @@
     }
 }
 
-
-
 // 1.15 fixed point, that means maximum value is MAX_ENCODEABLE_XYZ (0xFFFF)
 static
 cmsUInt8Number* UnrollXYZDoubleToFloat(_cmsTRANSFORM* info,
@@ -1316,44 +1581,132 @@
 }
 
 
+cmsINLINE void lab4toFloat(cmsFloat32Number wIn[], cmsUInt16Number lab4[3])
+{        
+    cmsFloat32Number L = (cmsFloat32Number) lab4[0] / 655.35F;
+    cmsFloat32Number a = ((cmsFloat32Number) lab4[1] / 257.0F) - 128.0F;
+    cmsFloat32Number b = ((cmsFloat32Number) lab4[2] / 257.0F) - 128.0F;
+    
+    wIn[0] = (L / 100.0F);                    // from 0..100 to 0..1
+    wIn[1] = ((a + 128.0F) / 255.0F);         // form -128..+127 to 0..1
+    wIn[2] = ((b + 128.0F) / 255.0F);
+
+}
+
+static
+cmsUInt8Number* UnrollLabV2_8ToFloat(_cmsTRANSFORM* info,
+                                      cmsFloat32Number wIn[],
+                                      cmsUInt8Number* accum,
+                                      cmsUInt32Number Stride)
+{
+    cmsUInt16Number lab4[3];
+
+    lab4[0] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++;     // L
+    lab4[1] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++;     // a
+    lab4[2] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++;     // b
+
+    lab4toFloat(wIn, lab4);
+
+    return accum;
+
+    cmsUNUSED_PARAMETER(info);
+    cmsUNUSED_PARAMETER(Stride);
+}
+
+static
+cmsUInt8Number* UnrollALabV2_8ToFloat(_cmsTRANSFORM* info,
+                                      cmsFloat32Number wIn[],
+                                      cmsUInt8Number* accum,
+                                      cmsUInt32Number Stride)
+{
+    cmsUInt16Number lab4[3];
+
+    accum++;  // A
+    lab4[0] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++;     // L
+    lab4[1] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++;     // a
+    lab4[2] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++;     // b
+
+    lab4toFloat(wIn, lab4);
+
+    return accum;
+
+    cmsUNUSED_PARAMETER(info);
+    cmsUNUSED_PARAMETER(Stride);
+}
+
+static
+cmsUInt8Number* UnrollLabV2_16ToFloat(_cmsTRANSFORM* info,
+                                      cmsFloat32Number wIn[],
+                                      cmsUInt8Number* accum,
+                                      cmsUInt32Number Stride)
+{
+    cmsUInt16Number lab4[3];
+
+    lab4[0] = FomLabV2ToLabV4(*(cmsUInt16Number*) accum); accum += 2;     // L
+    lab4[1] = FomLabV2ToLabV4(*(cmsUInt16Number*) accum); accum += 2;     // a
+    lab4[2] = FomLabV2ToLabV4(*(cmsUInt16Number*) accum); accum += 2;     // b
+
+    lab4toFloat(wIn, lab4);
+
+    return accum;
+
+    cmsUNUSED_PARAMETER(info);
+    cmsUNUSED_PARAMETER(Stride);
+}
+
 
 // Packing routines -----------------------------------------------------------------------------------------------------------
 
 
 // Generic chunky for byte
-
 static
-cmsUInt8Number* PackAnyBytes(CMSREGISTER _cmsTRANSFORM* info,
-                             CMSREGISTER cmsUInt16Number wOut[],
-                             CMSREGISTER cmsUInt8Number* output,
-                             CMSREGISTER cmsUInt32Number Stride)
+cmsUInt8Number* PackChunkyBytes(CMSREGISTER _cmsTRANSFORM* info,
+                                CMSREGISTER cmsUInt16Number wOut[],
+                                CMSREGISTER cmsUInt8Number* output,
+                                CMSREGISTER cmsUInt32Number Stride)
 {
-    cmsUInt32Number nChan  = T_CHANNELS(info -> OutputFormat);
-    cmsUInt32Number DoSwap   = T_DOSWAP(info ->OutputFormat);
-    cmsUInt32Number Reverse    = T_FLAVOR(info ->OutputFormat);
-    cmsUInt32Number Extra   = T_EXTRA(info -> OutputFormat);
-    cmsUInt32Number SwapFirst  = T_SWAPFIRST(info -> OutputFormat);
+    cmsUInt32Number nChan = T_CHANNELS(info->OutputFormat);
+    cmsUInt32Number DoSwap = T_DOSWAP(info->OutputFormat);
+    cmsUInt32Number Reverse = T_FLAVOR(info->OutputFormat);
+    cmsUInt32Number Extra = T_EXTRA(info->OutputFormat);
+    cmsUInt32Number SwapFirst = T_SWAPFIRST(info->OutputFormat);
+    cmsUInt32Number Premul = T_PREMUL(info->OutputFormat);
     cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
     cmsUInt8Number* swap1;
-    cmsUInt8Number v = 0;
+    cmsUInt16Number v = 0;
     cmsUInt32Number i;
+    cmsUInt32Number alpha_factor = 0;
 
     swap1 = output;
 
     if (ExtraFirst) {
+        
+        if (Premul && Extra)
+            alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(output[0]));
+
         output += Extra;
     }
+    else
+    {
+        if (Premul && Extra)
+            alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(output[nChan]));
+    }
 
     for (i=0; i < nChan; i++) {
 
         cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
 
-        v = FROM_16_TO_8(wOut[index]);
+        v = wOut[index];
 
         if (Reverse)
-            v = REVERSE_FLAVOR_8(v);
+            v = REVERSE_FLAVOR_16(v);
 
-        *output++ = v;
+        if (Premul && alpha_factor != 0)
+        {
+            v = (cmsUInt16Number)((cmsUInt32Number)((cmsUInt32Number)v * alpha_factor + 0x8000) >> 16);            
+        }
+
+        *output++ = FROM_16_TO_8(v);
     }
 
     if (!ExtraFirst) {
@@ -1363,39 +1716,47 @@
     if (Extra == 0 && SwapFirst) {
 
         memmove(swap1 + 1, swap1, nChan-1);
-        *swap1 = v;
+        *swap1 = FROM_16_TO_8(v);
     }
 
-
     return output;
 
     cmsUNUSED_PARAMETER(Stride);
 }
 
-
-
 static
-cmsUInt8Number* PackAnyWords(CMSREGISTER _cmsTRANSFORM* info,
-                             CMSREGISTER cmsUInt16Number wOut[],
-                             CMSREGISTER cmsUInt8Number* output,
-                             CMSREGISTER cmsUInt32Number Stride)
+cmsUInt8Number* PackChunkyWords(CMSREGISTER _cmsTRANSFORM* info,
+                                CMSREGISTER cmsUInt16Number wOut[],
+                                CMSREGISTER cmsUInt8Number* output,
+                                CMSREGISTER cmsUInt32Number Stride)
 {
-    cmsUInt32Number nChan  = T_CHANNELS(info -> OutputFormat);
-    cmsUInt32Number SwapEndian = T_ENDIAN16(info -> OutputFormat);
-    cmsUInt32Number DoSwap   = T_DOSWAP(info ->OutputFormat);
-    cmsUInt32Number Reverse    = T_FLAVOR(info ->OutputFormat);
-    cmsUInt32Number Extra   = T_EXTRA(info -> OutputFormat);
-    cmsUInt32Number SwapFirst  = T_SWAPFIRST(info -> OutputFormat);
+    cmsUInt32Number nChan = T_CHANNELS(info->OutputFormat);
+    cmsUInt32Number SwapEndian = T_ENDIAN16(info->OutputFormat);
+    cmsUInt32Number DoSwap = T_DOSWAP(info->OutputFormat);
+    cmsUInt32Number Reverse = T_FLAVOR(info->OutputFormat);
+    cmsUInt32Number Extra = T_EXTRA(info->OutputFormat);
+    cmsUInt32Number SwapFirst = T_SWAPFIRST(info->OutputFormat);
+    cmsUInt32Number Premul = T_PREMUL(info->OutputFormat);
     cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
     cmsUInt16Number* swap1;
     cmsUInt16Number v = 0;
     cmsUInt32Number i;
-
+    cmsUInt32Number alpha_factor = 0;
+    
     swap1 = (cmsUInt16Number*) output;
 
     if (ExtraFirst) {
+
+        if (Premul && Extra)
+            alpha_factor = _cmsToFixedDomain(*(cmsUInt16Number*) output);
+
         output += Extra * sizeof(cmsUInt16Number);
     }
+    else
+    {
+        if (Premul && Extra)
+            alpha_factor = _cmsToFixedDomain(((cmsUInt16Number*) output)[nChan]);
+    }
 
     for (i=0; i < nChan; i++) {
 
@@ -1409,6 +1770,11 @@
         if (Reverse)
             v = REVERSE_FLAVOR_16(v);
 
+        if (Premul && alpha_factor != 0)
+        {
+            v = (cmsUInt16Number)((cmsUInt32Number)((cmsUInt32Number)v * alpha_factor + 0x8000) >> 16);
+        }
+
         *(cmsUInt16Number*) output = v;
 
         output += sizeof(cmsUInt16Number);
@@ -1424,38 +1790,60 @@
         *swap1 = v;
     }
 
-
     return output;
 
     cmsUNUSED_PARAMETER(Stride);
 }
 
 
+
 static
 cmsUInt8Number* PackPlanarBytes(CMSREGISTER _cmsTRANSFORM* info,
                                 CMSREGISTER cmsUInt16Number wOut[],
                                 CMSREGISTER cmsUInt8Number* output,
                                 CMSREGISTER cmsUInt32Number Stride)
 {
-    cmsUInt32Number nChan     = T_CHANNELS(info -> OutputFormat);
-    cmsUInt32Number DoSwap    = T_DOSWAP(info ->OutputFormat);
-    cmsUInt32Number SwapFirst = T_SWAPFIRST(info ->OutputFormat);
-    cmsUInt32Number Reverse   = T_FLAVOR(info ->OutputFormat);
+    cmsUInt32Number nChan = T_CHANNELS(info->OutputFormat);
+    cmsUInt32Number DoSwap = T_DOSWAP(info->OutputFormat);
+    cmsUInt32Number SwapFirst = T_SWAPFIRST(info->OutputFormat);
+    cmsUInt32Number Reverse = T_FLAVOR(info->OutputFormat);
+    cmsUInt32Number Extra = T_EXTRA(info->OutputFormat);
+    cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
+    cmsUInt32Number Premul = T_PREMUL(info->OutputFormat);
     cmsUInt32Number i;
     cmsUInt8Number* Init = output;
+    cmsUInt32Number alpha_factor = 0;
 
 
-    if (DoSwap ^ SwapFirst) {
-        output += T_EXTRA(info -> OutputFormat) * Stride;
+    if (ExtraFirst) {
+
+        if (Premul && Extra)
+            alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(output[0]));
+
+        output += Extra * Stride;
+    }
+    else
+    {
+        if (Premul && Extra)
+            alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(output[nChan * Stride]));
     }
 
 
     for (i=0; i < nChan; i++) {
 
         cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
-        cmsUInt8Number v = FROM_16_TO_8(wOut[index]);
+        cmsUInt16Number v = wOut[index];
 
-        *(cmsUInt8Number*)  output = (cmsUInt8Number) (Reverse ? REVERSE_FLAVOR_8(v) : v);
+        if (Reverse)
+            v = REVERSE_FLAVOR_16(v);
+
+        if (Premul && alpha_factor != 0)
+        {
+            v = (cmsUInt16Number)((cmsUInt32Number)((cmsUInt32Number)v * alpha_factor + 0x8000) >> 16);
+        }
+
+        *(cmsUInt8Number*)output = FROM_16_TO_8(v);
+
         output += Stride;
     }
 
@@ -1471,16 +1859,30 @@
                                 CMSREGISTER cmsUInt8Number* output,
                                 CMSREGISTER cmsUInt32Number Stride)
 {
-    cmsUInt32Number nChan      = T_CHANNELS(info -> OutputFormat);
-    cmsUInt32Number DoSwap     = T_DOSWAP(info ->OutputFormat);
-    cmsUInt32Number Reverse    = T_FLAVOR(info ->OutputFormat);
-    cmsUInt32Number SwapEndian = T_ENDIAN16(info -> OutputFormat);
+    cmsUInt32Number nChan = T_CHANNELS(info->OutputFormat);
+    cmsUInt32Number DoSwap = T_DOSWAP(info->OutputFormat);
+    cmsUInt32Number SwapFirst = T_SWAPFIRST(info->OutputFormat);
+    cmsUInt32Number Reverse = T_FLAVOR(info->OutputFormat);
+    cmsUInt32Number Extra = T_EXTRA(info->OutputFormat);
+    cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
+    cmsUInt32Number Premul = T_PREMUL(info->OutputFormat);
+    cmsUInt32Number SwapEndian = T_ENDIAN16(info->OutputFormat);
     cmsUInt32Number i;
     cmsUInt8Number* Init = output;
     cmsUInt16Number v;
+    cmsUInt32Number alpha_factor = 0;
 
-    if (DoSwap) {
-        output += T_EXTRA(info -> OutputFormat) * Stride;
+    if (ExtraFirst) {
+
+        if (Premul && Extra)
+            alpha_factor = _cmsToFixedDomain(((cmsUInt16Number*) output)[0]);
+
+        output += Extra * Stride;
+    }
+    else
+    {
+        if (Premul && Extra)
+            alpha_factor = _cmsToFixedDomain(((cmsUInt16Number*)output)[nChan * Stride]);
     }
 
     for (i=0; i < nChan; i++) {
@@ -1495,6 +1897,11 @@
         if (Reverse)
             v =  REVERSE_FLAVOR_16(v);
 
+        if (Premul && alpha_factor != 0)
+        {
+            v = (cmsUInt16Number)((cmsUInt32Number)((cmsUInt32Number)v * alpha_factor + 0x8000) >> 16);
+        }
+
         *(cmsUInt16Number*) output = v;
         output += Stride;
     }
@@ -2641,10 +3048,6 @@
 
 }
 
-
-
-
-
 static
 cmsUInt8Number* PackLabFloatFromFloat(_cmsTRANSFORM* Info,
                                       cmsFloat32Number wOut[],
@@ -3029,12 +3432,12 @@
     { CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1),                 ANYSPACE,  Unroll4BytesSwap},
     { CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1), ANYSPACE,  Unroll4BytesSwapSwapFirst},
 
-    { BYTES_SH(1)|PLANAR_SH(1), ANYFLAVOR|ANYSWAPFIRST|
+    { BYTES_SH(1)|PLANAR_SH(1), ANYFLAVOR|ANYSWAPFIRST|ANYPREMUL|
                                    ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, UnrollPlanarBytes},
 
-    { BYTES_SH(1),    ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|
+    { BYTES_SH(1),    ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYPREMUL|
                                            ANYEXTRA|ANYCHANNELS|ANYSPACE, UnrollChunkyBytes},
-
+    
     { CHANNELS_SH(1)|BYTES_SH(2),                              ANYSPACE,  Unroll1Word},
     { CHANNELS_SH(1)|BYTES_SH(2)|FLAVOR_SH(1),                 ANYSPACE,  Unroll1WordReversed},
     { CHANNELS_SH(1)|BYTES_SH(2)|EXTRA_SH(3),                  ANYSPACE,  Unroll1WordSkip3},
@@ -3054,6 +3457,10 @@
 
     { BYTES_SH(2)|PLANAR_SH(1),  ANYFLAVOR|ANYSWAP|ANYENDIAN|ANYEXTRA|ANYCHANNELS|ANYSPACE,  UnrollPlanarWords},
     { BYTES_SH(2),  ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYENDIAN|ANYEXTRA|ANYCHANNELS|ANYSPACE,  UnrollAnyWords},
+
+    { BYTES_SH(2)|PLANAR_SH(1),  ANYFLAVOR|ANYSWAP|ANYENDIAN|ANYEXTRA|ANYCHANNELS|ANYSPACE|PREMUL_SH(1),  UnrollPlanarWordsPremul},
+    { BYTES_SH(2),  ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYENDIAN|ANYEXTRA|ANYCHANNELS|ANYSPACE|PREMUL_SH(1),  UnrollAnyWordsPremul}
+
 };
 
 
@@ -3069,13 +3476,23 @@
     {     TYPE_XYZ_FLT,                                ANYPLANAR|ANYEXTRA,   UnrollXYZFloatToFloat},
 
     {     FLOAT_SH(1)|BYTES_SH(4), ANYPLANAR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|
-                                                      ANYCHANNELS|ANYSPACE,  UnrollFloatsToFloat},
+                                            ANYPREMUL|ANYCHANNELS|ANYSPACE,  UnrollFloatsToFloat},
 
     {     FLOAT_SH(1)|BYTES_SH(0), ANYPLANAR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|
-                                                        ANYCHANNELS|ANYSPACE,  UnrollDoublesToFloat},
+                                              ANYCHANNELS|ANYSPACE|ANYPREMUL, UnrollDoublesToFloat},
+
+    {     TYPE_LabV2_8,                                                   0,  UnrollLabV2_8ToFloat },
+    {     TYPE_ALabV2_8,                                                  0,  UnrollALabV2_8ToFloat },
+    {     TYPE_LabV2_16,                                                  0,  UnrollLabV2_16ToFloat },
+
+    {     BYTES_SH(1),              ANYPLANAR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|
+                                                        ANYCHANNELS|ANYSPACE, Unroll8ToFloat},
+
+    {     BYTES_SH(2),              ANYPLANAR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|
+                                                        ANYCHANNELS|ANYSPACE, Unroll16ToFloat},
 #ifndef CMS_NO_HALF_SUPPORT 
     {     FLOAT_SH(1)|BYTES_SH(2), ANYPLANAR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|
-                                                        ANYCHANNELS|ANYSPACE,  UnrollHalfToFloat},
+                                                        ANYCHANNELS|ANYSPACE, UnrollHalfToFloat},
 #endif
 };
 
@@ -3169,16 +3586,20 @@
                                                                    ANYSPACE,  Pack3BytesAndSkip1SwapSwapFirst},
     { CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|EXTRA_SH(1),         ANYSPACE,  Pack3BytesAndSkip1Swap},
     { CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1),                     ANYSPACE,  Pack3BytesSwap},
-    { CHANNELS_SH(6)|BYTES_SH(1),                                  ANYSPACE,  Pack6Bytes},
-    { CHANNELS_SH(6)|BYTES_SH(1)|DOSWAP_SH(1),                     ANYSPACE,  Pack6BytesSwap},
     { CHANNELS_SH(4)|BYTES_SH(1),                                  ANYSPACE,  Pack4Bytes},
     { CHANNELS_SH(4)|BYTES_SH(1)|FLAVOR_SH(1),                     ANYSPACE,  Pack4BytesReverse},
     { CHANNELS_SH(4)|BYTES_SH(1)|SWAPFIRST_SH(1),                  ANYSPACE,  Pack4BytesSwapFirst},
     { CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1),                     ANYSPACE,  Pack4BytesSwap},
     { CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1),     ANYSPACE,  Pack4BytesSwapSwapFirst},
+    { CHANNELS_SH(6)|BYTES_SH(1),                                  ANYSPACE,  Pack6Bytes},
+    { CHANNELS_SH(6)|BYTES_SH(1)|DOSWAP_SH(1),                     ANYSPACE,  Pack6BytesSwap},
 
-    { BYTES_SH(1),                 ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackAnyBytes},
-    { BYTES_SH(1)|PLANAR_SH(1),    ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackPlanarBytes},
+    { BYTES_SH(1),    ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|
+                                                          ANYSPACE|ANYPREMUL, PackChunkyBytes},
+
+    { BYTES_SH(1)|PLANAR_SH(1),    ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|
+                                              ANYCHANNELS|ANYSPACE|ANYPREMUL, PackPlanarBytes},
+        
 
     { CHANNELS_SH(1)|BYTES_SH(2),                                  ANYSPACE,  Pack1Word},
     { CHANNELS_SH(1)|BYTES_SH(2)|EXTRA_SH(1),                      ANYSPACE,  Pack1WordSkip1},
@@ -3203,9 +3624,11 @@
     { CHANNELS_SH(6)|BYTES_SH(2),                                  ANYSPACE,  Pack6Words},
     { CHANNELS_SH(6)|BYTES_SH(2)|DOSWAP_SH(1),                     ANYSPACE,  Pack6WordsSwap},
 
-    { BYTES_SH(2)|PLANAR_SH(1),     ANYFLAVOR|ANYENDIAN|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackPlanarWords},
-    { BYTES_SH(2),                  ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYENDIAN|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackAnyWords}
-
+    { BYTES_SH(2),                  ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYENDIAN|
+                                     ANYEXTRA|ANYCHANNELS|ANYSPACE|ANYPREMUL, PackChunkyWords},
+    { BYTES_SH(2)|PLANAR_SH(1),     ANYFLAVOR|ANYENDIAN|ANYSWAP|ANYEXTRA|
+                                     ANYCHANNELS|ANYSPACE|ANYPREMUL,          PackPlanarWords}
+    
 };
 
 
diff --git a/third_party/lcms/src/cmspcs.c b/third_party/lcms/src/cmspcs.c
index 9f03fc5..3d6323a 100644
--- a/third_party/lcms/src/cmspcs.c
+++ b/third_party/lcms/src/cmspcs.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2020 Marti Maria Saguer
+//  Copyright (c) 1998-2022 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"),
diff --git a/third_party/lcms/src/cmsplugin.c b/third_party/lcms/src/cmsplugin.c
index 2b86b64..dbda3fd 100644
--- a/third_party/lcms/src/cmsplugin.c
+++ b/third_party/lcms/src/cmsplugin.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2020 Marti Maria Saguer
+//  Copyright (c) 1998-2022 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"),
@@ -671,17 +671,65 @@
 static _cmsMutex _cmsContextPoolHeadMutex = CMS_MUTEX_INITIALIZER;
 static struct _cmsContext_struct* _cmsContextPoolHead = NULL;
 
+
+// Make sure context is initialized (needed on windows)
+static
+cmsBool InitContextMutex(void)
+{
+    // See the comments regarding locking in lcms2_internal.h
+    // for an explanation of why we need the following code.
+#ifndef CMS_NO_PTHREADS
+#ifdef CMS_IS_WINDOWS_
+#ifndef CMS_RELY_ON_WINDOWS_STATIC_MUTEX_INIT
+
+    static cmsBool already_initialized = FALSE;
+
+    if (!already_initialized)
+    {
+        static HANDLE _cmsWindowsInitMutex = NULL;
+        static volatile HANDLE* mutex = &_cmsWindowsInitMutex;
+
+        if (*mutex == NULL)
+        {
+            HANDLE p = CreateMutex(NULL, FALSE, NULL);
+            if (p && InterlockedCompareExchangePointer((void**)mutex, (void*)p, NULL) != NULL)
+                CloseHandle(p);
+        }
+        if (*mutex == NULL || WaitForSingleObject(*mutex, INFINITE) == WAIT_FAILED)
+        {
+            cmsSignalError(0, cmsERROR_INTERNAL, "Mutex lock failed");
+            return FALSE;
+        }
+        if (((void**)&_cmsContextPoolHeadMutex)[0] == NULL)
+            InitializeCriticalSection(&_cmsContextPoolHeadMutex);
+        if (*mutex == NULL || !ReleaseMutex(*mutex))
+        {
+            cmsSignalError(0, cmsERROR_INTERNAL, "Mutex unlock failed");
+            return FALSE;
+        }
+        already_initialized = TRUE;
+    }
+#endif
+#endif
+#endif
+
+    return TRUE;
+}
+
+
+
 // Internal, get associated pointer, with guessing. Never returns NULL.
 struct _cmsContext_struct* _cmsGetContext(cmsContext ContextID)
 {
     struct _cmsContext_struct* id = (struct _cmsContext_struct*) ContextID;
     struct _cmsContext_struct* ctx;
 
-
     // On 0, use global settings
     if (id == NULL) 
         return &globalContext;
 
+    InitContextMutex();
+
     // Search
     _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
 
@@ -784,31 +832,7 @@
     struct _cmsContext_struct* ctx;
     struct _cmsContext_struct  fakeContext;
         
-    // See the comments regarding locking in lcms2_internal.h
-    // for an explanation of why we need the following code.
-#ifndef CMS_NO_PTHREADS
-#ifdef CMS_IS_WINDOWS_
-#ifndef CMS_RELY_ON_WINDOWS_STATIC_MUTEX_INIT
-    {
-        static HANDLE _cmsWindowsInitMutex = NULL;
-        static volatile HANDLE* mutex = &_cmsWindowsInitMutex;
-
-        if (*mutex == NULL)
-        {
-            HANDLE p = CreateMutex(NULL, FALSE, NULL);
-            if (p && InterlockedCompareExchangePointer((void **)mutex, (void*)p, NULL) != NULL)
-                CloseHandle(p);
-        }
-        if (*mutex == NULL || WaitForSingleObject(*mutex, INFINITE) == WAIT_FAILED)
-            return NULL;
-        if (((void **)&_cmsContextPoolHeadMutex)[0] == NULL)
-            InitializeCriticalSection(&_cmsContextPoolHeadMutex);
-        if (*mutex == NULL || !ReleaseMutex(*mutex))
-            return NULL;
-    }
-#endif
-#endif
-#endif
+    if (!InitContextMutex()) return NULL;
 
     _cmsInstallAllocFunctions(_cmsFindMemoryPlugin(Plugin), &fakeContext.DefaultMemoryManager);
     
@@ -884,6 +908,8 @@
     if (ctx == NULL)   
         return NULL;     // Something very wrong happened
 
+    if (!InitContextMutex()) return NULL;
+
     // Setup default memory allocators
     memcpy(&ctx->DefaultMemoryManager, &src->DefaultMemoryManager, sizeof(ctx->DefaultMemoryManager));
 
@@ -943,6 +969,9 @@
         struct _cmsContext_struct  fakeContext;  
         struct _cmsContext_struct* prev;
 
+
+        InitContextMutex();
+
         memcpy(&fakeContext.DefaultMemoryManager, &ctx->DefaultMemoryManager, sizeof(ctx->DefaultMemoryManager));
 
         fakeContext.chunks[UserPtr]     = ctx ->chunks[UserPtr];
@@ -989,3 +1018,32 @@
 }
 
 
+// Use context mutex to provide thread-safe time
+cmsBool _cmsGetTime(struct tm* ptr_time)
+{
+    struct tm* t;
+#if defined(HAVE_GMTIME_R) || defined(HAVE_GMTIME_S)
+    struct tm tm;
+#endif
+
+    time_t now = time(NULL);
+
+#ifdef HAVE_GMTIME_R
+    t = gmtime_r(&now, &tm);
+#elif defined(HAVE_GMTIME_S)
+    t = gmtime_s(&tm, &now) == 0 ? &tm : NULL;
+#else
+    if (!InitContextMutex()) return FALSE;
+
+    _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
+    t = gmtime(&now);
+    _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
+#endif
+
+    if (t == NULL) 
+        return FALSE;
+    else {
+        *ptr_time = *t;
+        return TRUE;
+    }
+}
diff --git a/third_party/lcms/src/cmsps2.c b/third_party/lcms/src/cmsps2.c
index 43c9f74..4bb17b0 100644
--- a/third_party/lcms/src/cmsps2.c
+++ b/third_party/lcms/src/cmsps2.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2020 Marti Maria Saguer
+//  Copyright (c) 1998-2022 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"),
@@ -475,7 +475,7 @@
     _cmsIOPrintf(m, "/lcms2gammatable [");
 
     for (i=0; i < Table->nEntries; i++) {
-	if (i % 10 == 0)
+    if (i % 10 == 0)
             _cmsIOPrintf(m, "\n  ");
         _cmsIOPrintf(m, "%d ", Table->Table16[i]);
     }
@@ -487,7 +487,7 @@
 
     // PostScript code                            Stack
     // ===============                            ========================
-                                            	  // v
+                                                  // v
     _cmsIOPrintf(m, "/%s {\n  ", name);
 
     // Bounds check
@@ -554,7 +554,7 @@
         }
         else {
             snprintf(buffer, sizeof(buffer), "%s%d", nameprefix, (int) i);
-	    buffer[sizeof(buffer)-1] = '\0';
+        buffer[sizeof(buffer)-1] = '\0';
             Emit1Gamma(m, g[i], buffer);
         }
     }
diff --git a/third_party/lcms/src/cmssamp.c b/third_party/lcms/src/cmssamp.c
index 6434d53..868664a 100644
--- a/third_party/lcms/src/cmssamp.c
+++ b/third_party/lcms/src/cmssamp.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2020 Marti Maria Saguer
+//  Copyright (c) 1998-2022 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"),
diff --git a/third_party/lcms/src/cmssm.c b/third_party/lcms/src/cmssm.c
index 284c77a..fb6965e 100644
--- a/third_party/lcms/src/cmssm.c
+++ b/third_party/lcms/src/cmssm.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2020 Marti Maria Saguer
+//  Copyright (c) 1998-2022 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"),
diff --git a/third_party/lcms/src/cmstypes.c b/third_party/lcms/src/cmstypes.c
index 4920773..c05a3ec 100644
--- a/third_party/lcms/src/cmstypes.c
+++ b/third_party/lcms/src/cmstypes.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2020 Marti Maria Saguer
+//  Copyright (c) 1998-2022 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"),
@@ -134,15 +134,62 @@
     return TRUE;
 }
 
+// Try to promote correctly to wchar_t when 32 bits
+cmsINLINE cmsBool is_surrogate(cmsUInt32Number uc) { return (uc - 0xd800u) < 2048u; }
+cmsINLINE cmsBool is_high_surrogate(cmsUInt32Number uc) { return (uc & 0xfffffc00) == 0xd800; }
+cmsINLINE cmsBool is_low_surrogate(cmsUInt32Number uc)  { return (uc & 0xfffffc00) == 0xdc00; }
+
+cmsINLINE cmsUInt32Number surrogate_to_utf32(cmsUInt32Number high, cmsUInt32Number low)
+{
+    return (high << 10) + low - 0x35fdc00;
+}
+
+cmsINLINE cmsBool convert_utf16_to_utf32(cmsIOHANDLER* io, cmsInt32Number n, wchar_t* output)
+{
+    cmsUInt16Number uc;
+
+    while (n > 0)
+    {
+        if (!_cmsReadUInt16Number(io, &uc)) return FALSE;
+        n--;
+
+        if (!is_surrogate(uc))
+        {
+            *output++ = (wchar_t)uc;
+        }
+        else {
+
+            cmsUInt16Number low;
+
+            if (!_cmsReadUInt16Number(io, &low)) return FALSE;
+            n--;
+
+            if (is_high_surrogate(uc) && is_low_surrogate(low))
+                *output++ = (wchar_t)surrogate_to_utf32(uc, low);
+            else
+                return FALSE;   // Corrupted string, just ignore
+        }
+    }
+
+    return TRUE;
+}
+
+
 // Auxiliary to read an array of wchar_t
 static
 cmsBool _cmsReadWCharArray(cmsIOHANDLER* io, cmsUInt32Number n, wchar_t* Array)
 {
     cmsUInt32Number i;
     cmsUInt16Number tmp;
+    cmsBool is32 = sizeof(wchar_t) > sizeof(cmsUInt16Number);
 
     _cmsAssert(io != NULL);
 
+    if (is32 && Array != NULL)
+    {
+        return convert_utf16_to_utf32(io, n, Array);
+    }
+
     for (i=0; i < n; i++) {
 
         if (Array != NULL) {
@@ -1307,7 +1354,7 @@
 //
 // All the dateTimeNumber values in a profile shall be in Coordinated Universal Time
 // (UTC, also known as GMT or ZULU Time). Profile writers are required to convert local
-// time to UTC when setting these values. Programmes that display these values may show
+// time to UTC when setting these values. Programs that display these values may show
 // the dateTimeNumber as UTC, show the equivalent local time (at current locale), or
 // display both UTC and local versions of the dateTimeNumber.
 
@@ -1746,7 +1793,7 @@
 
 
 // That will create a MPE LUT with Matrix, pre tables, CLUT and post tables.
-// 8 bit lut may be scaled easely to v4 PCS, but we need also to properly adjust
+// 8 bit lut may be scaled easily to v4 PCS, but we need also to properly adjust
 // PCS on BToAxx tags and AtoB if abstract. We need to fix input direction.
 
 static
@@ -1853,7 +1900,7 @@
 static
 cmsBool  Type_LUT8_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
 {
-    cmsUInt32Number j, nTabSize, i, n;
+    cmsUInt32Number j, nTabSize, i;
     cmsUInt8Number  val;
     cmsPipeline* NewLUT = (cmsPipeline*) Ptr;
     cmsStage* mpe;
@@ -1902,8 +1949,6 @@
     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) clutPoints)) return FALSE;
     if (!_cmsWriteUInt8Number(io, 0)) return FALSE; // Padding
 
-	n = NewLUT->InputChannels * NewLUT->OutputChannels;
-
     if (MatMPE != NULL) {
         
 		for (i = 0; i < 9; i++)
@@ -3168,8 +3213,8 @@
     if (!_cmsWriteUInt32Number(io, nColors)) return FALSE;
     if (!_cmsWriteUInt32Number(io, NamedColorList ->ColorantCount)) return FALSE;
 
-    strncpy(prefix, (const char*) NamedColorList->Prefix, 32);
-    strncpy(suffix, (const char*) NamedColorList->Suffix, 32);
+    memcpy(prefix, (const char*) NamedColorList->Prefix, sizeof(prefix));
+    memcpy(suffix, (const char*) NamedColorList->Suffix, sizeof(suffix));
 
     suffix[32] = prefix[32] = 0;
 
@@ -3182,6 +3227,10 @@
        cmsUInt16Number Colorant[cmsMAXCHANNELS];
        char Root[cmsMAX_PATH];
 
+       memset(Root, 0, sizeof(Root));
+       memset(PCS, 0, sizeof(PCS));
+       memset(Colorant, 0, sizeof(Colorant));
+
         if (!cmsNamedColorInfo(NamedColorList, i, Root, NULL, NULL, PCS, Colorant)) return 0;
         Root[32] = 0;
         if (!io ->Write(io, 32 , Root)) return FALSE;
@@ -3523,45 +3572,60 @@
 
     // First curve is Under color removal
 
-    if (SignedSizeOfTag < sizeof(cmsUInt32Number)) return NULL;    
+    if (SignedSizeOfTag < (cmsInt32Number) sizeof(cmsUInt32Number)) return NULL;
     if (!_cmsReadUInt32Number(io, &CountUcr)) return NULL;
     SignedSizeOfTag -= sizeof(cmsUInt32Number);
 
     n ->Ucr = cmsBuildTabulatedToneCurve16(self ->ContextID, CountUcr, NULL);
-    if (n ->Ucr == NULL) return NULL;
+    if (n ->Ucr == NULL) goto error;
 
-    if (SignedSizeOfTag < (cmsInt32Number)CountUcr * sizeof(cmsUInt16Number)) return NULL;
-    if (!_cmsReadUInt16Array(io, CountUcr, n ->Ucr->Table16)) return NULL;
+    if (SignedSizeOfTag < (cmsInt32Number)(CountUcr * sizeof(cmsUInt16Number))) goto error;
+    if (!_cmsReadUInt16Array(io, CountUcr, n ->Ucr->Table16)) goto error;
   
     SignedSizeOfTag -= CountUcr * sizeof(cmsUInt16Number);
 
     // Second curve is Black generation
 
-    if (SignedSizeOfTag < sizeof(cmsUInt32Number)) return NULL;    
-    if (!_cmsReadUInt32Number(io, &CountBg)) return NULL;
+    if (SignedSizeOfTag < (cmsInt32Number)sizeof(cmsUInt32Number)) goto error;
+    if (!_cmsReadUInt32Number(io, &CountBg)) goto error;
     SignedSizeOfTag -= sizeof(cmsUInt32Number);
 
     n ->Bg = cmsBuildTabulatedToneCurve16(self ->ContextID, CountBg, NULL);
-    if (n ->Bg == NULL) return NULL;
+    if (n ->Bg == NULL) goto error;
 
-    if (SignedSizeOfTag < (cmsInt32Number) CountBg * sizeof(cmsUInt16Number)) return NULL;    
-    if (!_cmsReadUInt16Array(io, CountBg, n ->Bg->Table16)) return NULL;
+    if (SignedSizeOfTag < (cmsInt32Number) (CountBg * sizeof(cmsUInt16Number))) goto error;
+    if (!_cmsReadUInt16Array(io, CountBg, n ->Bg->Table16)) goto error;
     SignedSizeOfTag -= CountBg * sizeof(cmsUInt16Number);
 
-    if (SignedSizeOfTag < 0 || SignedSizeOfTag > 32000) return NULL;
+    if (SignedSizeOfTag < 0 || SignedSizeOfTag > 32000) goto error;
 
     // Now comes the text. The length is specified by the tag size
     n ->Desc = cmsMLUalloc(self ->ContextID, 1);
-    if (n ->Desc == NULL) return NULL;
+    if (n ->Desc == NULL) goto error;
 
     ASCIIString = (char*) _cmsMalloc(self ->ContextID, SignedSizeOfTag + 1);
-    if (io ->Read(io, ASCIIString, sizeof(char), SignedSizeOfTag) != (cmsUInt32Number) SignedSizeOfTag) return NULL;
+    if (io->Read(io, ASCIIString, sizeof(char), SignedSizeOfTag) != (cmsUInt32Number)SignedSizeOfTag)
+    {
+        _cmsFree(self->ContextID, ASCIIString);
+        goto error;
+    }
+
     ASCIIString[SignedSizeOfTag] = 0;
     cmsMLUsetASCII(n ->Desc, cmsNoLanguage, cmsNoCountry, ASCIIString);
     _cmsFree(self ->ContextID, ASCIIString);
 
     *nItems = 1;
     return (void*) n;
+
+error:
+
+    if (n->Ucr) cmsFreeToneCurve(n->Ucr);
+    if (n->Bg) cmsFreeToneCurve(n->Bg);
+    if (n->Desc) cmsMLUfree(n->Desc);
+    _cmsFree(self->ContextID, n);
+    *nItems = 0;
+    return NULL;
+
 }
 
 static
@@ -3975,41 +4039,44 @@
 
            switch (ElementSig) {
 
-            case cmsSigFormulaCurveSeg: {
+           case cmsSigFormulaCurveSeg: {
 
-                cmsUInt16Number Type;
-                cmsUInt32Number ParamsByType[] = {4, 5, 5 };
+               cmsUInt16Number Type;
+               cmsUInt32Number ParamsByType[] = { 4, 5, 5 };
 
-                if (!_cmsReadUInt16Number(io, &Type)) goto Error;
-                if (!_cmsReadUInt16Number(io, NULL)) goto Error;
+               if (!_cmsReadUInt16Number(io, &Type)) goto Error;
+               if (!_cmsReadUInt16Number(io, NULL)) goto Error;
 
-                Segments[i].Type = Type + 6;
-                if (Type > 2) goto Error;
+               Segments[i].Type = Type + 6;
+               if (Type > 2) goto Error;
 
-                for (j=0; j < ParamsByType[Type]; j++) {
+               for (j = 0; j < ParamsByType[Type]; j++) {
 
-                    cmsFloat32Number f;
-                    if (!_cmsReadFloat32Number(io, &f)) goto Error;
-                    Segments[i].Params[j] = f;
-                }
-                }
-                break;
+                   cmsFloat32Number f;
+                   if (!_cmsReadFloat32Number(io, &f)) goto Error;
+                   Segments[i].Params[j] = f;
+               }
+           }
+           break;
 
 
-            case cmsSigSampledCurveSeg: {
-                cmsUInt32Number Count;
+           case cmsSigSampledCurveSeg: {
+               cmsUInt32Number Count;
 
-                if (!_cmsReadUInt32Number(io, &Count)) goto Error;
+               if (!_cmsReadUInt32Number(io, &Count)) goto Error;
 
-                Segments[i].nGridPoints = Count;
-                Segments[i].SampledPoints = (cmsFloat32Number*) _cmsCalloc(self ->ContextID, Count, sizeof(cmsFloat32Number));
-                if (Segments[i].SampledPoints == NULL) goto Error;
+               // The first point is implicit in the last stage, we allocate an extra note to be populated latter on
+               Count++;
+               Segments[i].nGridPoints = Count;
+               Segments[i].SampledPoints = (cmsFloat32Number*)_cmsCalloc(self->ContextID, Count, sizeof(cmsFloat32Number));
+               if (Segments[i].SampledPoints == NULL) goto Error;
 
-                for (j=0; j < Count; j++) {
-                    if (!_cmsReadFloat32Number(io, &Segments[i].SampledPoints[j])) goto Error;
-                }
-                }
-                break;
+               Segments[i].SampledPoints[0] = 0;
+               for (j = 1; j < Count; j++) {
+                   if (!_cmsReadFloat32Number(io, &Segments[i].SampledPoints[j])) goto Error;
+               }
+           }
+           break;
 
             default:
                 {
@@ -4029,6 +4096,17 @@
          if (Segments[i].SampledPoints) _cmsFree(self ->ContextID, Segments[i].SampledPoints);
      }
      _cmsFree(self ->ContextID, Segments);
+
+     // Explore for missing implicit points 
+     for (i = 0; i < nSegments; i++) {
+
+         // If sampled curve, fix it
+         if (Curve->Segments[i].Type == 0) {
+
+             Curve->Segments[i].SampledPoints[0] = cmsEvalToneCurveFloat(Curve, Curve->Segments[i].x0);
+         }
+     }
+
      return Curve;
 
 Error:
@@ -4123,12 +4201,12 @@
 
         if (ActualSeg -> Type == 0) {
 
-            // This is a sampled curve
+            // This is a sampled curve. First point is implicit in the ICC format, but not in our representation
             if (!_cmsWriteUInt32Number(io, (cmsUInt32Number) cmsSigSampledCurveSeg)) goto Error;
             if (!_cmsWriteUInt32Number(io, 0)) goto Error;
-            if (!_cmsWriteUInt32Number(io, ActualSeg -> nGridPoints)) goto Error;
+            if (!_cmsWriteUInt32Number(io, ActualSeg -> nGridPoints - 1)) goto Error;
 
-            for (j=0; j < g ->Segments[i].nGridPoints; j++) {
+            for (j=1; j < g ->Segments[i].nGridPoints; j++) {
                 if (!_cmsWriteFloat32Number(io, ActualSeg -> SampledPoints[j])) goto Error;
             }
 
@@ -4505,7 +4583,7 @@
 
 
 
-// This one is a liitle bit more complex, so we don't use position tables this time.
+// This one is a little bit more complex, so we don't use position tables this time.
 static
 cmsBool Type_MPE_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
 {
@@ -4937,7 +5015,7 @@
     if (!_cmsReadUInt32Number(io, &e->Offsets[i])) return FALSE;
     if (!_cmsReadUInt32Number(io, &e ->Sizes[i])) return FALSE;
 
-    // An offset of zero has special meaning and shal be preserved
+    // An offset of zero has special meaning and shall be preserved
     if (e ->Offsets[i] > 0)
         e ->Offsets[i] += BaseOffset;
     return TRUE;
@@ -4945,27 +5023,41 @@
 
 
 static
-cmsBool ReadOffsetArray(cmsIOHANDLER* io,  _cmsDICarray* a, cmsUInt32Number Count, cmsUInt32Number Length, cmsUInt32Number BaseOffset)
+cmsBool ReadOffsetArray(cmsIOHANDLER* io,  _cmsDICarray* a, 
+                        cmsUInt32Number Count, cmsUInt32Number Length, cmsUInt32Number BaseOffset,
+                        cmsInt32Number* SignedSizeOfTagPtr)
 {
     cmsUInt32Number i;
+    cmsInt32Number SignedSizeOfTag = *SignedSizeOfTagPtr;
 
     // Read column arrays
     for (i=0; i < Count; i++) {
 
+        if (SignedSizeOfTag < 4 * (cmsInt32Number) sizeof(cmsUInt32Number)) return FALSE;
+        SignedSizeOfTag -= 4 * sizeof(cmsUInt32Number);
+
         if (!ReadOneElem(io, &a -> Name, i, BaseOffset)) return FALSE;
         if (!ReadOneElem(io, &a -> Value, i, BaseOffset)) return FALSE;
 
         if (Length > 16) {
 
+            if (SignedSizeOfTag < 2 * (cmsInt32Number) sizeof(cmsUInt32Number)) return FALSE;
+            SignedSizeOfTag -= 2 * sizeof(cmsUInt32Number);
+
             if (!ReadOneElem(io, &a ->DisplayName, i, BaseOffset)) return FALSE;
 
         }
 
         if (Length > 24) {
 
+            if (SignedSizeOfTag < 2 * (cmsInt32Number) sizeof(cmsUInt32Number)) return FALSE;
+            SignedSizeOfTag -= 2 * (cmsInt32Number) sizeof(cmsUInt32Number);
+
             if (!ReadOneElem(io, & a -> DisplayValue, i, BaseOffset)) return FALSE;
         }
     }
+
+    *SignedSizeOfTagPtr = SignedSizeOfTag;
     return TRUE;
 }
 
@@ -5113,26 +5205,31 @@
 static
 void *Type_Dictionary_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
 {
-   cmsHANDLE hDict;
+   cmsHANDLE hDict = NULL;
    cmsUInt32Number i, Count, Length;
    cmsUInt32Number BaseOffset;
    _cmsDICarray a;
    wchar_t *NameWCS = NULL, *ValueWCS = NULL;
    cmsMLU *DisplayNameMLU = NULL, *DisplayValueMLU=NULL;
    cmsBool rc;
+   cmsInt32Number SignedSizeOfTag = (cmsInt32Number)SizeOfTag;
 
     *nItems = 0;
+    memset(&a, 0, sizeof(a));
 
     // Get actual position as a basis for element offsets
     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
 
     // Get name-value record count
+    SignedSizeOfTag -= sizeof(cmsUInt32Number);
+    if (SignedSizeOfTag < 0) goto Error;
     if (!_cmsReadUInt32Number(io, &Count)) return NULL;
-    SizeOfTag -= sizeof(cmsUInt32Number);
-
+    
     // Get rec length
+    SignedSizeOfTag -= sizeof(cmsUInt32Number);
+    if (SignedSizeOfTag < 0) goto Error;
     if (!_cmsReadUInt32Number(io, &Length)) return NULL;
-    SizeOfTag -= sizeof(cmsUInt32Number);
+    
 
     // Check for valid lengths
     if (Length != 16 && Length != 24 && Length != 32) {
@@ -5148,7 +5245,7 @@
     if (!AllocArray(self -> ContextID, &a, Count, Length)) goto Error;
 
     // Read column arrays
-    if (!ReadOffsetArray(io, &a, Count, Length, BaseOffset)) goto Error;
+    if (!ReadOffsetArray(io, &a, Count, Length, BaseOffset, &SignedSizeOfTag)) goto Error;
 
     // Seek to each element and read it
     for (i=0; i < Count; i++) {
@@ -5188,7 +5285,7 @@
 
 Error:
    FreeArray(&a);
-   cmsDictFree(hDict);
+   if (hDict != NULL) cmsDictFree(hDict);
    return NULL;
 }
 
diff --git a/third_party/lcms/src/cmsvirt.c b/third_party/lcms/src/cmsvirt.c
index 6ab820b..bffb88e 100644
--- a/third_party/lcms/src/cmsvirt.c
+++ b/third_party/lcms/src/cmsvirt.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2020 Marti Maria Saguer
+//  Copyright (c) 1998-2022 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"),
@@ -368,7 +368,7 @@
 
     InkLimit = (InkLimit * 655.35);
 
-    SumCMY   = In[0]  + In[1] + In[2];
+    SumCMY   = (cmsFloat64Number) In[0]  + In[1] + In[2];
     SumCMYK  = SumCMY + In[3];
 
     if (SumCMYK > InkLimit) {
diff --git a/third_party/lcms/src/cmswtpnt.c b/third_party/lcms/src/cmswtpnt.c
index c7184c3..a8e41b2 100644
--- a/third_party/lcms/src/cmswtpnt.c
+++ b/third_party/lcms/src/cmswtpnt.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2020 Marti Maria Saguer
+//  Copyright (c) 1998-2022 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"),
@@ -264,16 +264,16 @@
 
 // Build a White point, primary chromas transfer matrix from RGB to CIE XYZ
 // This is just an approximation, I am not handling all the non-linear
-// aspects of the RGB to XYZ process, and assumming that the gamma correction
+// aspects of the RGB to XYZ process, and assuming that the gamma correction
 // has transitive property in the transformation chain.
 //
-// the alghoritm:
+// the algorithm:
 //
 //            - First I build the absolute conversion matrix using
 //              primaries in XYZ. This matrix is next inverted
 //            - Then I eval the source white point across this matrix
-//              obtaining the coeficients of the transformation
-//            - Then, I apply these coeficients to the original matrix
+//              obtaining the coefficients of the transformation
+//            - Then, I apply these coefficients to the original matrix
 //
 cmsBool _cmsBuildRGB2XYZtransferMatrix(cmsMAT3* r, const cmsCIExyY* WhitePt, const cmsCIExyYTRIPLE* Primrs)
 {
diff --git a/third_party/lcms/src/cmsxform.c b/third_party/lcms/src/cmsxform.c
index 9d182e1..c7183fc 100644
--- a/third_party/lcms/src/cmsxform.c
+++ b/third_party/lcms/src/cmsxform.c
@@ -1,7 +1,7 @@
 //---------------------------------------------------------------------------------
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2020 Marti Maria Saguer
+//  Copyright (c) 1998-2022 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"),
@@ -284,7 +284,7 @@
 
             accum = p->FromInputFloat(p, fIn, accum, Stride->BytesPerPlaneIn);
 
-            // Any gamut chack to do?
+            // Any gamut check to do?
             if (p->GamutCheck != NULL) {
 
                 // Evaluate gamut marker.
@@ -846,7 +846,7 @@
        }
 
     // Check whatever this is a true floating point transform
-    if (_cmsFormatterIsFloat(*InputFormat) && _cmsFormatterIsFloat(*OutputFormat)) {
+    if (_cmsFormatterIsFloat(*OutputFormat)) {
 
         // Get formatter function always return a valid union, but the contents of this union may be NULL.
         p ->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat,  cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
@@ -1081,6 +1081,15 @@
         return NULL;
     }
 
+    // Check whatever the transform is 16 bits and involves linear RGB in first profile. If so, disable optimizations
+    if (EntryColorSpace == cmsSigRgbData && T_BYTES(InputFormat) == 2 && !(dwFlags & cmsFLAGS_NOOPTIMIZE))
+    {
+        cmsFloat64Number gamma = cmsDetectRGBProfileGamma(hProfiles[0], 0.1);
+
+        if (gamma > 0 && gamma < 1.6)
+            dwFlags |= cmsFLAGS_NOOPTIMIZE;
+    }
+
     // Create a pipeline with all transformations
     Lut = _cmsLinkProfiles(ContextID, nProfiles, Intents, hProfiles, BPC, AdaptationStates, dwFlags);
     if (Lut == NULL) {
diff --git a/third_party/lcms/src/lcms2_internal.h b/third_party/lcms/src/lcms2_internal.h
index 9560fc4..9eecf6e 100644
--- a/third_party/lcms/src/lcms2_internal.h
+++ b/third_party/lcms/src/lcms2_internal.h
@@ -1,7 +1,7 @@
 
 //
 //  Little Color Management System
-//  Copyright (c) 1998-2020 Marti Maria Saguer
+//  Copyright (c) 1998-2022 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"),
@@ -1118,5 +1118,8 @@
 cmsBool   _cmsBuildRGB2XYZtransferMatrix(cmsMAT3* r, const cmsCIExyY* WhitePoint, const cmsCIExyYTRIPLE* Primaries);
 
 
+// thread-safe gettime
+cmsBool _cmsGetTime(struct tm* ptr_time);
+
 #define _lcms_internal_H
 #endif