blob: d54b4d19e49138b26601172afb39eb076874624c [file] [log] [blame]
//---------------------------------------------------------------------------------
//
// Little Color Management System
// Copyright (c) 1998-2017 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 readed/ 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(cmsIOHANDLER* io, cmsUInt8Number* n)
{
cmsUInt8Number tmp;
_cmsAssert(io != NULL);
if (io -> Read(io, &tmp, sizeof(cmsUInt8Number), 1) != 1)
return FALSE;
if (n != NULL) *n = tmp;
return TRUE;
}
cmsBool CMSEXPORT _cmsReadUInt16Number(cmsIOHANDLER* io, cmsUInt16Number* n)
{
cmsUInt16Number tmp;
_cmsAssert(io != NULL);
if (io -> Read(io, &tmp, sizeof(cmsUInt16Number), 1) != 1)
return FALSE;
if (n != NULL) *n = _cmsAdjustEndianess16(tmp);
return TRUE;
}
cmsBool CMSEXPORT _cmsReadUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, cmsUInt16Number* Array)
{
cmsUInt32Number i;
_cmsAssert(io != NULL);
for (i=0; i < n; i++) {
if (Array != NULL) {
if (!_cmsReadUInt16Number(io, Array + i)) return FALSE;
}
else {
if (!_cmsReadUInt16Number(io, NULL)) return FALSE;
}
}
return TRUE;
}
cmsBool CMSEXPORT _cmsReadUInt32Number(cmsIOHANDLER* io, cmsUInt32Number* n)
{
cmsUInt32Number tmp;
_cmsAssert(io != NULL);
if (io -> Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1)
return FALSE;
if (n != NULL) *n = _cmsAdjustEndianess32(tmp);
return TRUE;
}
cmsBool CMSEXPORT _cmsReadFloat32Number(cmsIOHANDLER* io, cmsFloat32Number* n)
{
cmsUInt32Number tmp;
_cmsAssert(io != NULL);
if (io->Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1)
return FALSE;
if (n != NULL) {
tmp = _cmsAdjustEndianess32(tmp);
*n = *(cmsFloat32Number*)(void*)&tmp;
// 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;
#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(cmsIOHANDLER* io, cmsUInt64Number* n)
{
cmsUInt64Number tmp;
_cmsAssert(io != NULL);
if (io -> Read(io, &tmp, sizeof(cmsUInt64Number), 1) != 1)
return FALSE;
if (n != NULL) {
_cmsAdjustEndianess64(n, &tmp);
}
return TRUE;
}
cmsBool CMSEXPORT _cmsRead15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number* n)
{
cmsUInt32Number tmp;
_cmsAssert(io != NULL);
if (io -> Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1)
return FALSE;
if (n != NULL) {
*n = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32(tmp));
}
return TRUE;
}
cmsBool CMSEXPORT _cmsReadXYZNumber(cmsIOHANDLER* io, cmsCIEXYZ* XYZ)
{
cmsEncodedXYZNumber xyz;
_cmsAssert(io != NULL);
if (io ->Read(io, &xyz, sizeof(cmsEncodedXYZNumber), 1) != 1) return FALSE;
if (XYZ != NULL) {
XYZ->X = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.X));
XYZ->Y = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.Y));
XYZ->Z = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.Z));
}
return TRUE;
}
cmsBool CMSEXPORT _cmsWriteUInt8Number(cmsIOHANDLER* io, cmsUInt8Number n)
{
_cmsAssert(io != NULL);
if (io -> Write(io, sizeof(cmsUInt8Number), &n) != 1)
return FALSE;
return TRUE;
}
cmsBool CMSEXPORT _cmsWriteUInt16Number(cmsIOHANDLER* io, cmsUInt16Number n)
{
cmsUInt16Number tmp;
_cmsAssert(io != NULL);
tmp = _cmsAdjustEndianess16(n);
if (io -> Write(io, sizeof(cmsUInt16Number), &tmp) != 1)
return FALSE;
return TRUE;
}
cmsBool CMSEXPORT _cmsWriteUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, const cmsUInt16Number* Array)
{
cmsUInt32Number i;
_cmsAssert(io != NULL);
_cmsAssert(Array != NULL);
for (i=0; i < n; i++) {
if (!_cmsWriteUInt16Number(io, Array[i])) return FALSE;
}
return TRUE;
}
cmsBool CMSEXPORT _cmsWriteUInt32Number(cmsIOHANDLER* io, cmsUInt32Number n)
{
cmsUInt32Number tmp;
_cmsAssert(io != NULL);
tmp = _cmsAdjustEndianess32(n);
if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1)
return FALSE;
return TRUE;
}
cmsBool CMSEXPORT _cmsWriteFloat32Number(cmsIOHANDLER* io, cmsFloat32Number n)
{
cmsUInt32Number tmp;
_cmsAssert(io != NULL);
tmp = *(cmsUInt32Number*) (void*) &n;
tmp = _cmsAdjustEndianess32(tmp);
if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1)
return FALSE;
return TRUE;
}
cmsBool CMSEXPORT _cmsWriteUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n)
{
cmsUInt64Number tmp;
_cmsAssert(io != NULL);
_cmsAdjustEndianess64(&tmp, n);
if (io -> Write(io, sizeof(cmsUInt64Number), &tmp) != 1)
return FALSE;
return TRUE;
}
cmsBool CMSEXPORT _cmsWrite15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number n)
{
cmsUInt32Number tmp;
_cmsAssert(io != NULL);
tmp = _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(n));
if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1)
return FALSE;
return TRUE;
}
cmsBool CMSEXPORT _cmsWriteXYZNumber(cmsIOHANDLER* io, const cmsCIEXYZ* XYZ)
{
cmsEncodedXYZNumber xyz;
_cmsAssert(io != NULL);
_cmsAssert(XYZ != NULL);
xyz.X = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(XYZ->X));
xyz.Y = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(XYZ->Y));
xyz.Z = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(XYZ->Z));
return io -> Write(io, sizeof(cmsEncodedXYZNumber), &xyz);
}
// from Fixed point 8.8 to double
cmsFloat64Number CMSEXPORT _cms8Fixed8toDouble(cmsUInt16Number fixed8)
{
cmsUInt8Number msb, lsb;
lsb = (cmsUInt8Number) (fixed8 & 0xff);
msb = (cmsUInt8Number) (((cmsUInt16Number) fixed8 >> 8) & 0xff);
return (cmsFloat64Number) ((cmsFloat64Number) msb + ((cmsFloat64Number) lsb / 256.0));
}
cmsUInt16Number CMSEXPORT _cmsDoubleTo8Fixed8(cmsFloat64Number val)
{
cmsS15Fixed16Number GammaFixed32 = _cmsDoubleTo15Fixed16(val);
return (cmsUInt16Number) ((GammaFixed32 >> 8) & 0xFFFF);
}
// from Fixed point 15.16 to double
cmsFloat64Number CMSEXPORT _cms15Fixed16toDouble(cmsS15Fixed16Number fix32)
{
cmsFloat64Number floater, sign, mid;
int Whole, FracPart;
sign = (fix32 < 0 ? -1 : 1);
fix32 = abs(fix32);
Whole = (cmsUInt16Number)(fix32 >> 16) & 0xffff;
FracPart = (cmsUInt16Number)(fix32 & 0xffff);
mid = (cmsFloat64Number) FracPart / 65536.0;
floater = (cmsFloat64Number) Whole + mid;
return sign * floater;
}
// from double to Fixed point 15.16
cmsS15Fixed16Number CMSEXPORT _cmsDoubleTo15Fixed16(cmsFloat64Number v)
{
return ((cmsS15Fixed16Number) floor((v)*65536.0 + 0.5));
}
// Date/Time functions
void CMSEXPORT _cmsDecodeDateTimeNumber(const cmsDateTimeNumber *Source, struct tm *Dest)
{
_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(cmsDateTimeNumber *Dest, const struct tm *Source)
{
_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(cmsIOHANDLER* io)
{
_cmsTagBase Base;
_cmsAssert(io != NULL);
if (io -> Read(io, &Base, sizeof(_cmsTagBase), 1) != 1)
return (cmsTagTypeSignature) 0;
return (cmsTagTypeSignature) _cmsAdjustEndianess32(Base.sig);
}
// Setup base marker
cmsBool CMSEXPORT _cmsWriteTypeBase(cmsIOHANDLER* io, cmsTagTypeSignature sig)
{
_cmsTagBase Base;
_cmsAssert(io != NULL);
Base.sig = (cmsTagTypeSignature) _cmsAdjustEndianess32(sig);
memset(&Base.reserved, 0, sizeof(Base.reserved));
return io -> Write(io, sizeof(_cmsTagBase), &Base);
}
cmsBool CMSEXPORT _cmsReadAlignment(cmsIOHANDLER* io)
{
cmsUInt8Number Buffer[4];
cmsUInt32Number NextAligned, At;
cmsUInt32Number BytesToNextAlignedPos;
_cmsAssert(io != NULL);
At = io -> Tell(io);
NextAligned = _cmsALIGNLONG(At);
BytesToNextAlignedPos = NextAligned - At;
if (BytesToNextAlignedPos == 0) return TRUE;
if (BytesToNextAlignedPos > 4) return FALSE;
return (io ->Read(io, Buffer, BytesToNextAlignedPos, 1) == 1);
}
cmsBool CMSEXPORT _cmsWriteAlignment(cmsIOHANDLER* io)
{
cmsUInt8Number Buffer[4];
cmsUInt32Number NextAligned, At;
cmsUInt32Number BytesToNextAlignedPos;
_cmsAssert(io != NULL);
At = io -> Tell(io);
NextAligned = _cmsALIGNLONG(At);
BytesToNextAlignedPos = NextAligned - At;
if (BytesToNextAlignedPos == 0) return TRUE;
if (BytesToNextAlignedPos > 4) return FALSE;
memset(Buffer, 0, BytesToNextAlignedPos);
return io -> Write(io, BytesToNextAlignedPos, Buffer);
}
// To deal with text streams. 2K at most
cmsBool CMSEXPORT _cmsIOPrintf(cmsIOHANDLER* io, const char* frm, ...)
{
va_list args;
int len;
cmsUInt8Number Buffer[2048];
cmsBool rc;
_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
}
rc = io ->Write(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(void* Plug_in)
{
return cmsPluginTHR(NULL, Plug_in);
}
cmsBool CMSEXPORT cmsPluginTHR(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 > 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;
default:
cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin type '%X'", Plugin -> Type);
return FALSE;
}
}
// Keep a reference to the plug-in
return TRUE;
}
// Revert all plug-ins to default
void CMSEXPORT cmsUnregisterPlugins(void)
{
cmsUnregisterPluginsTHR(NULL);
}
// 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
},
{ 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;
// 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;
// Search
for (ctx = _cmsContextPoolHead;
ctx != NULL;
ctx = ctx ->Next) {
// Found it?
if (id == ctx)
return ctx; // New-style context,
}
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 cmsPluginTHR() function may register
// many different plug-ins simultaneously, then there is no way to
// identify which plug-in to unregister.
void CMSEXPORT cmsUnregisterPluginsTHR(cmsContext 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);
}
// 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;
// See the comments regarding locking in lcms2_internal.h
// for an explanation of why we need the following code.
#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
_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);
// Setup the plug-ins
if (!cmsPluginTHR(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
// 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);
// Make sure no one failed
for (i=Logger; i < MemoryClientMax; i++) {
if (src ->chunks[i] == NULL) {
cmsDeleteContext((cmsContext) ctx);
return NULL;
}
}
return (cmsContext) ctx;
}
/*
static
struct _cmsContext_struct* FindPrev(struct _cmsContext_struct* id)
{
struct _cmsContext_struct* prev;
// Search for previous
for (prev = _cmsContextPoolHead;
prev != NULL;
prev = prev ->Next)
{
if (prev ->Next == id)
return prev;
}
return NULL; // List is empty or only one element!
}
*/
// 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) {
struct _cmsContext_struct* ctx = (struct _cmsContext_struct*) ContextID;
struct _cmsContext_struct fakeContext;
struct _cmsContext_struct* prev;
memcpy(&fakeContext.DefaultMemoryManager, &ctx->DefaultMemoryManager, sizeof(ctx->DefaultMemoryManager));
fakeContext.chunks[UserPtr] = ctx ->chunks[UserPtr];
fakeContext.chunks[MemPlugin] = &fakeContext.DefaultMemoryManager;
// Get rid of plugins
cmsUnregisterPluginsTHR(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);
}