diff mupdf-source/thirdparty/lcms2/src/cmsplugin.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mupdf-source/thirdparty/lcms2/src/cmsplugin.c	Mon Sep 15 11:43:07 2025 +0200
@@ -0,0 +1,1065 @@
+//---------------------------------------------------------------------------------
+//
+//  Little Color Management System
+//  Copyright (c) 1998-2023 Marti Maria Saguer
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the Software
+// is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
+// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+//---------------------------------------------------------------------------------
+//
+
+#include "lcms2_internal.h"
+
+
+// ----------------------------------------------------------------------------------
+// Encoding & Decoding support functions
+// ----------------------------------------------------------------------------------
+
+//      Little-Endian to Big-Endian
+
+// Adjust a word value after being read/ before being written from/to an ICC profile
+cmsUInt16Number CMSEXPORT  _cmsAdjustEndianess16(cmsUInt16Number Word)
+{
+#ifndef CMS_USE_BIG_ENDIAN
+
+    cmsUInt8Number* pByte = (cmsUInt8Number*) &Word;
+    cmsUInt8Number tmp;
+
+    tmp = pByte[0];
+    pByte[0] = pByte[1];
+    pByte[1] = tmp;
+#endif
+
+    return Word;
+}
+
+
+// Transports to properly encoded values - note that icc profiles does use big endian notation.
+
+// 1 2 3 4
+// 4 3 2 1
+
+cmsUInt32Number CMSEXPORT  _cmsAdjustEndianess32(cmsUInt32Number DWord)
+{
+#ifndef CMS_USE_BIG_ENDIAN
+    cmsUInt8Number* pByte = (cmsUInt8Number*) &DWord;
+    cmsUInt8Number temp1;
+    cmsUInt8Number temp2;
+
+    temp1 = *pByte++;
+    temp2 = *pByte++;
+    *(pByte-1) = *pByte;
+    *pByte++ = temp2;
+    *(pByte-3) = *pByte;
+    *pByte = temp1;
+#endif
+    return DWord;
+}
+
+// 1 2 3 4 5 6 7 8
+// 8 7 6 5 4 3 2 1
+
+void CMSEXPORT  _cmsAdjustEndianess64(cmsUInt64Number* Result, cmsUInt64Number* QWord)
+{
+
+#ifndef CMS_USE_BIG_ENDIAN
+
+    cmsUInt8Number* pIn  = (cmsUInt8Number*) QWord;
+    cmsUInt8Number* pOut = (cmsUInt8Number*) Result;
+
+    _cmsAssert(Result != NULL);
+
+    pOut[7] = pIn[0];
+    pOut[6] = pIn[1];
+    pOut[5] = pIn[2];
+    pOut[4] = pIn[3];
+    pOut[3] = pIn[4];
+    pOut[2] = pIn[5];
+    pOut[1] = pIn[6];
+    pOut[0] = pIn[7];
+
+#else
+    _cmsAssert(Result != NULL);
+
+#  ifdef CMS_DONT_USE_INT64
+    (*Result)[0] = (*QWord)[0];
+    (*Result)[1] = (*QWord)[1];
+#  else
+    *Result = *QWord;
+#  endif
+#endif
+}
+
+// Auxiliary -- read 8, 16 and 32-bit numbers
+cmsBool CMSEXPORT  _cmsReadUInt8Number(cmsContext ContextID, cmsIOHANDLER* io, cmsUInt8Number* n)
+{
+    cmsUInt8Number tmp;
+
+    _cmsAssert(io != NULL);
+
+    if (io -> Read(ContextID, io, &tmp, sizeof(cmsUInt8Number), 1) != 1)
+            return FALSE;
+
+    if (n != NULL) *n = tmp;
+    return TRUE;
+}
+
+cmsBool CMSEXPORT  _cmsReadUInt16Number(cmsContext ContextID, cmsIOHANDLER* io, cmsUInt16Number* n)
+{
+    cmsUInt16Number tmp;
+
+    _cmsAssert(io != NULL);
+
+    if (io -> Read(ContextID, io, &tmp, sizeof(cmsUInt16Number), 1) != 1)
+            return FALSE;
+
+    if (n != NULL) *n = _cmsAdjustEndianess16(tmp);
+    return TRUE;
+}
+
+cmsBool CMSEXPORT  _cmsReadUInt16Array(cmsContext ContextID, cmsIOHANDLER* io, cmsUInt32Number n, cmsUInt16Number* Array)
+{
+    cmsUInt32Number i;
+
+    _cmsAssert(io != NULL);
+
+    for (i=0; i < n; i++) {
+
+        if (Array != NULL) {
+            if (!_cmsReadUInt16Number(ContextID, io, Array + i)) return FALSE;
+        }
+        else {
+            if (!_cmsReadUInt16Number(ContextID, io, NULL)) return FALSE;
+        }
+
+    }
+    return TRUE;
+}
+
+cmsBool CMSEXPORT  _cmsReadUInt32Number(cmsContext ContextID, cmsIOHANDLER* io, cmsUInt32Number* n)
+{
+    cmsUInt32Number tmp;
+
+    _cmsAssert(io != NULL);
+
+    if (io -> Read(ContextID, io, &tmp, sizeof(cmsUInt32Number), 1) != 1)
+            return FALSE;
+
+    if (n != NULL) *n = _cmsAdjustEndianess32(tmp);
+    return TRUE;
+}
+
+cmsBool CMSEXPORT  _cmsReadFloat32Number(cmsContext ContextID, cmsIOHANDLER* io, cmsFloat32Number* n)
+{
+    union typeConverter {
+        cmsUInt32Number integer;
+        cmsFloat32Number floating_point;
+    } tmp;
+
+    _cmsAssert(io != NULL);
+
+    if (io->Read(ContextID, io, &tmp.integer, sizeof(cmsUInt32Number), 1) != 1)
+        return FALSE;
+
+    if (n != NULL) {
+
+        tmp.integer = _cmsAdjustEndianess32(tmp.integer);
+        *n = tmp.floating_point;
+
+        // Safeguard which covers against absurd values
+        if (*n > 1E+20 || *n < -1E+20) return FALSE;
+
+        #if defined(_MSC_VER) && _MSC_VER < 1800
+           return TRUE;
+        #elif defined (__BORLANDC__)
+           return TRUE;
+        #elif !defined(_MSC_VER) && (defined(__STDC_VERSION__) && __STDC_VERSION__ < 199901L) && !defined(HAVE_FPCLASSIFY)
+           return TRUE;
+        #else
+
+           // fpclassify() required by C99 (only provided by MSVC >= 1800, VS2013 onwards)
+           return ((fpclassify(*n) == FP_ZERO) || (fpclassify(*n) == FP_NORMAL));
+        #endif
+    }
+
+    return TRUE;
+}
+
+
+cmsBool CMSEXPORT   _cmsReadUInt64Number(cmsContext ContextID, cmsIOHANDLER* io, cmsUInt64Number* n)
+{
+    cmsUInt64Number tmp;
+
+    _cmsAssert(io != NULL);
+
+    if (io -> Read(ContextID, io, &tmp, sizeof(cmsUInt64Number), 1) != 1)
+            return FALSE;
+
+    if (n != NULL) {
+
+        _cmsAdjustEndianess64(n, &tmp);
+    }
+
+    return TRUE;
+}
+
+
+cmsBool CMSEXPORT  _cmsRead15Fixed16Number(cmsContext ContextID, cmsIOHANDLER* io, cmsFloat64Number* n)
+{
+    cmsUInt32Number tmp;
+
+    _cmsAssert(io != NULL);
+
+    if (io -> Read(ContextID, io, &tmp, sizeof(cmsUInt32Number), 1) != 1)
+            return FALSE;
+
+    if (n != NULL) {
+        *n = _cms15Fixed16toDouble(ContextID, (cmsS15Fixed16Number) _cmsAdjustEndianess32(tmp));
+    }
+
+    return TRUE;
+}
+
+
+cmsBool CMSEXPORT  _cmsReadXYZNumber(cmsContext ContextID, cmsIOHANDLER* io, cmsCIEXYZ* XYZ)
+{
+    cmsEncodedXYZNumber xyz;
+
+    _cmsAssert(io != NULL);
+
+    if (io ->Read(ContextID, io, &xyz, sizeof(cmsEncodedXYZNumber), 1) != 1) return FALSE;
+
+    if (XYZ != NULL) {
+
+        XYZ->X = _cms15Fixed16toDouble(ContextID, (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.X));
+        XYZ->Y = _cms15Fixed16toDouble(ContextID, (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.Y));
+        XYZ->Z = _cms15Fixed16toDouble(ContextID, (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.Z));
+    }
+    return TRUE;
+}
+
+cmsBool CMSEXPORT  _cmsWriteUInt8Number(cmsContext ContextID, cmsIOHANDLER* io, cmsUInt8Number n)
+{
+    _cmsAssert(io != NULL);
+
+    if (io -> Write(ContextID, io, sizeof(cmsUInt8Number), &n) != 1)
+            return FALSE;
+
+    return TRUE;
+}
+
+cmsBool CMSEXPORT  _cmsWriteUInt16Number(cmsContext ContextID, cmsIOHANDLER* io, cmsUInt16Number n)
+{
+    cmsUInt16Number tmp;
+
+    _cmsAssert(io != NULL);
+
+    tmp = _cmsAdjustEndianess16(n);
+    if (io -> Write(ContextID, io, sizeof(cmsUInt16Number), &tmp) != 1)
+            return FALSE;
+
+    return TRUE;
+}
+
+cmsBool CMSEXPORT  _cmsWriteUInt16Array(cmsContext ContextID, cmsIOHANDLER* io, cmsUInt32Number n, const cmsUInt16Number* Array)
+{
+    cmsUInt32Number i;
+
+    _cmsAssert(io != NULL);
+    _cmsAssert(Array != NULL);
+
+    for (i=0; i < n; i++) {
+        if (!_cmsWriteUInt16Number(ContextID, io, Array[i])) return FALSE;
+    }
+
+    return TRUE;
+}
+
+cmsBool CMSEXPORT  _cmsWriteUInt32Number(cmsContext ContextID, cmsIOHANDLER* io, cmsUInt32Number n)
+{
+    cmsUInt32Number tmp;
+
+    _cmsAssert(io != NULL);
+
+    tmp = _cmsAdjustEndianess32(n);
+    if (io -> Write(ContextID, io, sizeof(cmsUInt32Number), &tmp) != 1)
+            return FALSE;
+
+    return TRUE;
+}
+
+
+cmsBool CMSEXPORT  _cmsWriteFloat32Number(cmsContext ContextID, cmsIOHANDLER* io, cmsFloat32Number n)
+{
+    union typeConverter {
+        cmsUInt32Number integer;
+        cmsFloat32Number floating_point;
+    } tmp;
+
+    tmp.floating_point = n;
+    tmp.integer = _cmsAdjustEndianess32(tmp.integer);
+    if (io -> Write(ContextID, io, sizeof(cmsUInt32Number), &tmp.integer) != 1)
+            return FALSE;
+
+    return TRUE;
+}
+
+cmsBool CMSEXPORT  _cmsWriteUInt64Number(cmsContext ContextID, cmsIOHANDLER* io, cmsUInt64Number* n)
+{
+    cmsUInt64Number tmp;
+
+    _cmsAssert(io != NULL);
+
+    _cmsAdjustEndianess64(&tmp, n);
+    if (io -> Write(ContextID, io, sizeof(cmsUInt64Number), &tmp) != 1)
+            return FALSE;
+
+    return TRUE;
+}
+
+cmsBool CMSEXPORT  _cmsWrite15Fixed16Number(cmsContext ContextID, cmsIOHANDLER* io, cmsFloat64Number n)
+{
+    cmsUInt32Number tmp;
+
+    _cmsAssert(io != NULL);
+
+    tmp = _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(ContextID, n));
+    if (io -> Write(ContextID, io, sizeof(cmsUInt32Number), &tmp) != 1)
+            return FALSE;
+
+    return TRUE;
+}
+
+cmsBool CMSEXPORT  _cmsWriteXYZNumber(cmsContext ContextID, cmsIOHANDLER* io, const cmsCIEXYZ* XYZ)
+{
+    cmsEncodedXYZNumber xyz;
+
+    _cmsAssert(io != NULL);
+    _cmsAssert(XYZ != NULL);
+
+    xyz.X = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(ContextID, XYZ->X));
+    xyz.Y = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(ContextID, XYZ->Y));
+    xyz.Z = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(ContextID, XYZ->Z));
+
+    return io -> Write(ContextID, io,  sizeof(cmsEncodedXYZNumber), &xyz);
+}
+
+// from Fixed point 8.8 to double
+cmsFloat64Number CMSEXPORT _cms8Fixed8toDouble(cmsContext ContextID, cmsUInt16Number fixed8)
+{
+    return fixed8 / 256.0;
+}
+
+cmsUInt16Number CMSEXPORT _cmsDoubleTo8Fixed8(cmsContext ContextID, cmsFloat64Number val)
+{
+    cmsS15Fixed16Number GammaFixed32 = _cmsDoubleTo15Fixed16(ContextID, val);
+    return  (cmsUInt16Number) ((GammaFixed32 >> 8) & 0xFFFF);
+}
+
+// from Fixed point 15.16 to double
+cmsFloat64Number CMSEXPORT _cms15Fixed16toDouble(cmsContext ContextID, cmsS15Fixed16Number fix32)
+{
+    return fix32 / 65536.0;
+}
+
+// from double to Fixed point 15.16
+cmsS15Fixed16Number CMSEXPORT _cmsDoubleTo15Fixed16(cmsContext ContextID, cmsFloat64Number v)
+{
+    cmsUNUSED_PARAMETER(ContextID);
+    return ((cmsS15Fixed16Number) floor((v)*65536.0 + 0.5));
+}
+
+// Date/Time functions
+
+void CMSEXPORT _cmsDecodeDateTimeNumber(cmsContext ContextID, const cmsDateTimeNumber *Source, struct tm *Dest)
+{
+    cmsUNUSED_PARAMETER(ContextID);
+
+    _cmsAssert(Dest != NULL);
+    _cmsAssert(Source != NULL);
+
+    Dest->tm_sec   = _cmsAdjustEndianess16(Source->seconds);
+    Dest->tm_min   = _cmsAdjustEndianess16(Source->minutes);
+    Dest->tm_hour  = _cmsAdjustEndianess16(Source->hours);
+    Dest->tm_mday  = _cmsAdjustEndianess16(Source->day);
+    Dest->tm_mon   = _cmsAdjustEndianess16(Source->month) - 1;
+    Dest->tm_year  = _cmsAdjustEndianess16(Source->year) - 1900;
+    Dest->tm_wday  = -1;
+    Dest->tm_yday  = -1;
+    Dest->tm_isdst = 0;
+}
+
+void CMSEXPORT _cmsEncodeDateTimeNumber(cmsContext ContextID, cmsDateTimeNumber *Dest, const struct tm *Source)
+{
+    cmsUNUSED_PARAMETER(ContextID);
+
+    _cmsAssert(Dest != NULL);
+    _cmsAssert(Source != NULL);
+
+    Dest->seconds = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_sec);
+    Dest->minutes = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_min);
+    Dest->hours   = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_hour);
+    Dest->day     = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_mday);
+    Dest->month   = _cmsAdjustEndianess16((cmsUInt16Number) (Source->tm_mon + 1));
+    Dest->year    = _cmsAdjustEndianess16((cmsUInt16Number) (Source->tm_year + 1900));
+}
+
+// Read base and return type base
+cmsTagTypeSignature CMSEXPORT _cmsReadTypeBase(cmsContext ContextID, cmsIOHANDLER* io)
+{
+    _cmsTagBase Base;
+
+    _cmsAssert(io != NULL);
+
+    if (io -> Read(ContextID, io, &Base, sizeof(_cmsTagBase), 1) != 1)
+        return (cmsTagTypeSignature) 0;
+
+    return (cmsTagTypeSignature) _cmsAdjustEndianess32(Base.sig);
+}
+
+// Setup base marker
+cmsBool  CMSEXPORT _cmsWriteTypeBase(cmsContext ContextID, cmsIOHANDLER* io, cmsTagTypeSignature sig)
+{
+    _cmsTagBase  Base;
+
+    _cmsAssert(io != NULL);
+
+    Base.sig = (cmsTagTypeSignature) _cmsAdjustEndianess32(sig);
+    memset(&Base.reserved, 0, sizeof(Base.reserved));
+    return io -> Write(ContextID, io, sizeof(_cmsTagBase), &Base);
+}
+
+cmsBool CMSEXPORT _cmsReadAlignment(cmsContext ContextID, cmsIOHANDLER* io)
+{
+    cmsUInt8Number  Buffer[4];
+    cmsUInt32Number NextAligned, At;
+    cmsUInt32Number BytesToNextAlignedPos;
+
+    _cmsAssert(io != NULL);
+
+    At = io -> Tell(ContextID, io);
+    NextAligned = _cmsALIGNLONG(At);
+    BytesToNextAlignedPos = NextAligned - At;
+    if (BytesToNextAlignedPos == 0) return TRUE;
+    if (BytesToNextAlignedPos > 4)  return FALSE;
+
+    return (io ->Read(ContextID, io, Buffer, BytesToNextAlignedPos, 1) == 1);
+}
+
+cmsBool CMSEXPORT _cmsWriteAlignment(cmsContext ContextID, cmsIOHANDLER* io)
+{
+    cmsUInt8Number  Buffer[4];
+    cmsUInt32Number NextAligned, At;
+    cmsUInt32Number BytesToNextAlignedPos;
+
+    _cmsAssert(io != NULL);
+
+    At = io -> Tell(ContextID, io);
+    NextAligned = _cmsALIGNLONG(At);
+    BytesToNextAlignedPos = NextAligned - At;
+    if (BytesToNextAlignedPos == 0) return TRUE;
+    if (BytesToNextAlignedPos > 4)  return FALSE;
+
+    memset(Buffer, 0, BytesToNextAlignedPos);
+    return io -> Write(ContextID, io, BytesToNextAlignedPos, Buffer);
+}
+
+
+// To deal with text streams. 2K at most
+cmsBool CMSEXPORT _cmsIOPrintf(cmsContext ContextID, cmsIOHANDLER* io, const char* frm, ...)
+{
+    va_list args;
+    int len;
+    cmsUInt8Number Buffer[2048];
+    cmsBool rc;
+    cmsUInt8Number* ptr;
+
+    _cmsAssert(io != NULL);
+    _cmsAssert(frm != NULL);
+
+    va_start(args, frm);
+
+    len = vsnprintf((char*) Buffer, 2047, frm, args);
+    if (len < 0) {
+        va_end(args);
+        return FALSE;   // Truncated, which is a fatal error for us
+    }
+
+    // setlocale may be active, no commas are needed in PS generator
+    // and PS generator is our only client
+    for (ptr = Buffer; *ptr; ptr++)
+    {
+        if (*ptr == ',') *ptr = '.';
+    }
+
+    rc = io ->Write(ContextID, io, (cmsUInt32Number) len, Buffer);
+
+    va_end(args);
+
+    return rc;
+}
+
+
+// Plugin memory management -------------------------------------------------------------------------------------------------
+
+// Specialized malloc for plug-ins, that is freed upon exit.
+void* _cmsPluginMalloc(cmsContext ContextID, cmsUInt32Number size)
+{
+    struct _cmsContext_struct* ctx = _cmsGetContext(ContextID);
+
+    if (ctx ->MemPool == NULL) {
+
+        if (ContextID == NULL) {
+
+            ctx->MemPool = _cmsCreateSubAlloc(0, 2*1024);
+            if (ctx->MemPool == NULL) return NULL;
+        }
+        else {
+            cmsSignalError(ContextID, cmsERROR_CORRUPTION_DETECTED, "NULL memory pool on context");
+            return NULL;
+        }
+    }
+
+    return _cmsSubAlloc(ctx->MemPool, size);
+}
+
+
+// Main plug-in dispatcher
+cmsBool CMSEXPORT cmsPlugin(cmsContext id, void* Plug_in)
+{
+    cmsPluginBase* Plugin;
+
+    for (Plugin = (cmsPluginBase*) Plug_in;
+         Plugin != NULL;
+         Plugin = Plugin -> Next) {
+
+            if (Plugin -> Magic != cmsPluginMagicNumber) {
+                cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin");
+                return FALSE;
+            }
+
+            if (Plugin ->ExpectedVersion < LCMS2MT_VERSION_MIN ||
+                Plugin ->ExpectedVersion > LCMS2MT_VERSION_MAX) {
+                cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "plugin version %d not in acceptable version range. LCMS2.art cannot use LCMS2 plugins!",
+                    Plugin ->ExpectedVersion);
+                return FALSE;
+            }
+
+	    if (Plugin ->ExpectedVersion > LCMS_VERSION) {
+                cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "plugin needs Little CMS %d, current version is %d",
+                    Plugin ->ExpectedVersion, LCMS_VERSION);
+                return FALSE;
+            }
+
+            switch (Plugin -> Type) {
+
+                case cmsPluginMemHandlerSig:
+                    if (!_cmsRegisterMemHandlerPlugin(id, Plugin)) return FALSE;
+                    break;
+
+                case cmsPluginInterpolationSig:
+                    if (!_cmsRegisterInterpPlugin(id, Plugin)) return FALSE;
+                    break;
+
+                case cmsPluginTagTypeSig:
+                    if (!_cmsRegisterTagTypePlugin(id, Plugin)) return FALSE;
+                    break;
+
+                case cmsPluginTagSig:
+                    if (!_cmsRegisterTagPlugin(id, Plugin)) return FALSE;
+                    break;
+
+                case cmsPluginFormattersSig:
+                    if (!_cmsRegisterFormattersPlugin(id, Plugin)) return FALSE;
+                    break;
+
+                case cmsPluginRenderingIntentSig:
+                    if (!_cmsRegisterRenderingIntentPlugin(id, Plugin)) return FALSE;
+                    break;
+
+                case cmsPluginParametricCurveSig:
+                    if (!_cmsRegisterParametricCurvesPlugin(id, Plugin)) return FALSE;
+                    break;
+
+                case cmsPluginMultiProcessElementSig:
+                    if (!_cmsRegisterMultiProcessElementPlugin(id, Plugin)) return FALSE;
+                    break;
+
+                case cmsPluginOptimizationSig:
+                    if (!_cmsRegisterOptimizationPlugin(id, Plugin)) return FALSE;
+                    break;
+
+                case cmsPluginTransformSig:
+                    if (!_cmsRegisterTransformPlugin(id, Plugin)) return FALSE;
+                    break;
+
+                case cmsPluginMutexSig:
+                    if (!_cmsRegisterMutexPlugin(id, Plugin)) return FALSE;
+                    break;
+
+                case cmsPluginParalellizationSig:
+                    if (!_cmsRegisterParallelizationPlugin(id, Plugin)) return FALSE;
+                    break;
+
+                default:
+                    cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin type '%X'", Plugin -> Type);
+                    return FALSE;
+            }
+    }
+
+    // Keep a reference to the plug-in
+    return TRUE;
+}
+
+
+// The Global storage for system context. This is the one and only global variable
+// pointers structure. All global vars are referenced here.
+static struct _cmsContext_struct globalContext = {
+
+    NULL,                                // Not in the linked list
+    NULL,                                // No suballocator
+    {
+        NULL,                            //  UserPtr,
+        &_cmsLogErrorChunk,              //  Logger,
+        &_cmsAlarmCodesChunk,            //  AlarmCodes,
+        &_cmsAdaptationStateChunk,       //  AdaptationState,
+        &_cmsMemPluginChunk,             //  MemPlugin,
+        &_cmsInterpPluginChunk,          //  InterpPlugin,
+        &_cmsCurvesPluginChunk,          //  CurvesPlugin,
+        &_cmsFormattersPluginChunk,      //  FormattersPlugin,
+        &_cmsTagTypePluginChunk,         //  TagTypePlugin,
+        &_cmsTagPluginChunk,             //  TagPlugin,
+        &_cmsIntentsPluginChunk,         //  IntentPlugin,
+        &_cmsMPETypePluginChunk,         //  MPEPlugin,
+        &_cmsOptimizationPluginChunk,    //  OptimizationPlugin,
+        &_cmsTransformPluginChunk,       //  TransformPlugin,
+        &_cmsMutexPluginChunk,           //  MutexPlugin,
+        &_cmsParallelizationPluginChunk  //  ParallelizationPlugin
+    },
+
+    { NULL, NULL, NULL, NULL, NULL, NULL } // The default memory allocator is not used for context 0
+};
+
+
+// The context pool (linked list head)
+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);
+
+    for (ctx = _cmsContextPoolHead;
+         ctx != NULL;
+         ctx = ctx ->Next) {
+
+            // Found it?
+        if (id == ctx)
+        {
+            _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
+            return ctx; // New-style context
+        }
+    }
+
+    _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
+    return &globalContext;
+}
+
+
+// Internal: get the memory area associanted with each context client
+// Returns the block assigned to the specific zone. Never return NULL.
+void* _cmsContextGetClientChunk(cmsContext ContextID, _cmsMemoryClient mc)
+{
+    struct _cmsContext_struct* ctx;
+    void *ptr;
+
+    if ((int) mc < 0 || mc >= MemoryClientMax) {
+
+           cmsSignalError(ContextID, cmsERROR_INTERNAL, "Bad context client -- possible corruption");
+
+           // This is catastrophic. Should never reach here
+           _cmsAssert(0);
+
+           // Reverts to global context
+           return globalContext.chunks[UserPtr];
+    }
+
+    ctx = _cmsGetContext(ContextID);
+    ptr = ctx ->chunks[mc];
+
+    if (ptr != NULL)
+        return ptr;
+
+    // A null ptr means no special settings for that context, and this
+    // reverts to Context0 globals
+    return globalContext.chunks[mc];
+}
+
+
+// This function returns the given context its default pristine state,
+// as no plug-ins were declared. There is no way to unregister a single
+// plug-in, as a single call to cmsPlugin() function may register
+// many different plug-ins simultaneously, then there is no way to
+// identify which plug-in to unregister.
+void CMSEXPORT cmsUnregisterPlugins(cmsContext ContextID)
+{
+  //struct _cmsContext_struct* ctx = _cmsGetContext(ContextID);
+
+    _cmsRegisterMemHandlerPlugin(ContextID, NULL);
+    _cmsRegisterInterpPlugin(ContextID, NULL);
+    _cmsRegisterTagTypePlugin(ContextID, NULL);
+    _cmsRegisterTagPlugin(ContextID, NULL);
+    _cmsRegisterFormattersPlugin(ContextID, NULL);
+    _cmsRegisterRenderingIntentPlugin(ContextID, NULL);
+    _cmsRegisterParametricCurvesPlugin(ContextID, NULL);
+    _cmsRegisterMultiProcessElementPlugin(ContextID, NULL);
+    _cmsRegisterOptimizationPlugin(ContextID, NULL);
+    _cmsRegisterTransformPlugin(ContextID, NULL);
+    _cmsRegisterMutexPlugin(ContextID, NULL);
+    _cmsRegisterParallelizationPlugin(ContextID, NULL);
+
+}
+
+
+// Returns the memory manager plug-in, if any, from the Plug-in bundle
+static
+cmsPluginMemHandler* _cmsFindMemoryPlugin(void* PluginBundle)
+{
+    cmsPluginBase* Plugin;
+
+    for (Plugin = (cmsPluginBase*) PluginBundle;
+        Plugin != NULL;
+        Plugin = Plugin -> Next) {
+
+            if (Plugin -> Magic == cmsPluginMagicNumber &&
+                Plugin -> ExpectedVersion <= LCMS_VERSION &&
+                Plugin -> Type == cmsPluginMemHandlerSig) {
+
+                    // Found!
+                    return (cmsPluginMemHandler*) Plugin;
+            }
+    }
+
+    // Nope, revert to defaults
+    return NULL;
+}
+
+
+// Creates a new context with optional associated plug-ins. Caller may also specify an optional pointer to user-defined
+// data that will be forwarded to plug-ins and logger.
+cmsContext CMSEXPORT cmsCreateContext(void* Plugin, void* UserData)
+{
+    struct _cmsContext_struct* ctx;
+    struct _cmsContext_struct  fakeContext;
+
+    if (!InitContextMutex()) return NULL;
+
+    _cmsInstallAllocFunctions(_cmsFindMemoryPlugin(Plugin), &fakeContext.DefaultMemoryManager);
+
+    fakeContext.chunks[UserPtr]     = UserData;
+    fakeContext.chunks[MemPlugin]   = &fakeContext.DefaultMemoryManager;
+
+    // Create the context structure.
+    ctx = (struct _cmsContext_struct*) _cmsMalloc(&fakeContext, sizeof(struct _cmsContext_struct));
+    if (ctx == NULL)
+        return NULL;     // Something very wrong happened!
+
+    // Init the structure and the memory manager
+    memset(ctx, 0, sizeof(struct _cmsContext_struct));
+
+    // Keep memory manager
+    memcpy(&ctx->DefaultMemoryManager, &fakeContext.DefaultMemoryManager, sizeof(_cmsMemPluginChunk));
+
+    // Maintain the linked list (with proper locking)
+    _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
+       ctx ->Next = _cmsContextPoolHead;
+       _cmsContextPoolHead = ctx;
+    _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
+
+    ctx ->chunks[UserPtr]     = UserData;
+    ctx ->chunks[MemPlugin]   = &ctx->DefaultMemoryManager;
+
+    // Now we can allocate the pool by using default memory manager
+    ctx ->MemPool = _cmsCreateSubAlloc(ctx, 22 * sizeof(void*));  // default size about 22 pointers
+    if (ctx ->MemPool == NULL) {
+
+         cmsDeleteContext(ctx);
+        return NULL;
+    }
+
+    _cmsAllocLogErrorChunk(ctx, NULL);
+    _cmsAllocAlarmCodesChunk(ctx, NULL);
+    _cmsAllocAdaptationStateChunk(ctx, NULL);
+    _cmsAllocMemPluginChunk(ctx, NULL);
+    _cmsAllocInterpPluginChunk(ctx, NULL);
+    _cmsAllocCurvesPluginChunk(ctx, NULL);
+    _cmsAllocFormattersPluginChunk(ctx, NULL);
+    _cmsAllocTagTypePluginChunk(ctx, NULL);
+    _cmsAllocMPETypePluginChunk(ctx, NULL);
+    _cmsAllocTagPluginChunk(ctx, NULL);
+    _cmsAllocIntentsPluginChunk(ctx, NULL);
+    _cmsAllocOptimizationPluginChunk(ctx, NULL);
+    _cmsAllocTransformPluginChunk(ctx, NULL);
+    _cmsAllocMutexPluginChunk(ctx, NULL);
+    _cmsAllocParallelizationPluginChunk(ctx, NULL);
+
+    // Setup the plug-ins
+    if (!cmsPlugin(ctx, Plugin)) {
+
+        cmsDeleteContext(ctx);
+        return NULL;
+    }
+
+    return (cmsContext) ctx;
+}
+
+// Duplicates a context with all associated plug-ins.
+// Caller may specify an optional pointer to user-defined
+// data that will be forwarded to plug-ins and logger.
+cmsContext CMSEXPORT cmsDupContext(cmsContext ContextID, void* NewUserData)
+{
+    int i;
+    struct _cmsContext_struct* ctx;
+    const struct _cmsContext_struct* src = _cmsGetContext(ContextID);
+
+    void* userData = (NewUserData != NULL) ? NewUserData : src -> chunks[UserPtr];
+
+
+    ctx = (struct _cmsContext_struct*) _cmsMalloc(ContextID, sizeof(struct _cmsContext_struct));
+    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));
+
+    // Maintain the linked list
+    _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
+       ctx ->Next = _cmsContextPoolHead;
+       _cmsContextPoolHead = ctx;
+    _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
+
+    ctx ->chunks[UserPtr]    = userData;
+    ctx ->chunks[MemPlugin]  = &ctx->DefaultMemoryManager;
+
+    ctx ->MemPool = _cmsCreateSubAlloc(ctx, 22 * sizeof(void*));
+    if (ctx ->MemPool == NULL) {
+
+         cmsDeleteContext(ctx);
+        return NULL;
+    }
+
+    // Allocate all required chunks.
+    _cmsAllocLogErrorChunk(ctx, src);
+    _cmsAllocAlarmCodesChunk(ctx, src);
+    _cmsAllocAdaptationStateChunk(ctx, src);
+    _cmsAllocMemPluginChunk(ctx, src);
+    _cmsAllocInterpPluginChunk(ctx, src);
+    _cmsAllocCurvesPluginChunk(ctx, src);
+    _cmsAllocFormattersPluginChunk(ctx, src);
+    _cmsAllocTagTypePluginChunk(ctx, src);
+    _cmsAllocMPETypePluginChunk(ctx, src);
+    _cmsAllocTagPluginChunk(ctx, src);
+    _cmsAllocIntentsPluginChunk(ctx, src);
+    _cmsAllocOptimizationPluginChunk(ctx, src);
+    _cmsAllocTransformPluginChunk(ctx, src);
+    _cmsAllocMutexPluginChunk(ctx, src);
+    _cmsAllocParallelizationPluginChunk(ctx, src);
+
+    // Make sure no one failed
+    for (i=Logger; i < MemoryClientMax; i++) {
+
+        if (src ->chunks[i] == NULL) {
+            cmsDeleteContext((cmsContext) ctx);
+            return NULL;
+        }
+    }
+
+    return (cmsContext) ctx;
+}
+
+
+// Frees any resources associated with the given context,
+// and destroys the context placeholder.
+// The ContextID can no longer be used in any THR operation.
+void CMSEXPORT cmsDeleteContext(cmsContext ContextID)
+{
+    if (ContextID == NULL) {
+
+        cmsUnregisterPlugins(ContextID);
+        if (globalContext.MemPool != NULL)
+            _cmsSubAllocDestroy(globalContext.MemPool);
+        globalContext.MemPool = NULL;
+    }
+    else {
+
+        struct _cmsContext_struct* ctx = (struct _cmsContext_struct*) ContextID;
+        struct _cmsContext_struct  fakeContext;
+        struct _cmsContext_struct* prev;
+
+
+        InitContextMutex();
+
+        memcpy(&fakeContext.DefaultMemoryManager, &ctx->DefaultMemoryManager, sizeof(ctx->DefaultMemoryManager));
+
+        fakeContext.chunks[UserPtr]     = ctx ->chunks[UserPtr];
+        fakeContext.chunks[MemPlugin]   = &fakeContext.DefaultMemoryManager;
+
+        // Get rid of plugins
+        cmsUnregisterPlugins(ContextID);
+
+        // Since all memory is allocated in the private pool, all what we need to do is destroy the pool
+        if (ctx -> MemPool != NULL)
+              _cmsSubAllocDestroy(ctx ->MemPool);
+        ctx -> MemPool = NULL;
+
+        // Maintain list
+        _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
+        if (_cmsContextPoolHead == ctx) {
+
+            _cmsContextPoolHead = ctx->Next;
+        }
+        else {
+
+            // Search for previous
+            for (prev = _cmsContextPoolHead;
+                 prev != NULL;
+                 prev = prev ->Next)
+            {
+                if (prev -> Next == ctx) {
+                    prev -> Next = ctx ->Next;
+                    break;
+                }
+            }
+        }
+        _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
+
+        // free the memory block itself
+        _cmsFree(&fakeContext, ctx);
+    }
+}
+
+// Returns the user data associated to the given ContextID, or NULL if no user data was attached on context creation
+void* CMSEXPORT cmsGetContextUserData(cmsContext ContextID)
+{
+    return _cmsContextGetClientChunk(ContextID, UserPtr);
+}
+
+cmsUInt32Number _cmsAdjustReferenceCount(cmsUInt32Number *rc, int delta)
+{
+    cmsUInt32Number refs;
+
+    _cmsAssert(rc != NULL && *rc > 0);
+
+    _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
+    *rc += delta;
+    refs = *rc;
+    _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
+
+    return refs;
+}
+
+// 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;
+    }
+}