//--------------------------------------------------------------------------------- // // 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 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) { cmsUInt32Number tmp; _cmsAssert(io != NULL); if (io->Read(ContextID, 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; #elif !defined(_MSC_VER) && !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) { cmsUInt32Number tmp; _cmsAssert(io != NULL); tmp = *(cmsUInt32Number*) (void*) &n; tmp = _cmsAdjustEndianess32(tmp); if (io -> Write(ContextID, io, sizeof(cmsUInt32Number), &tmp) != 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) { cmsUInt8Number msb, lsb; cmsUNUSED_PARAMETER(ContextID); lsb = (cmsUInt8Number) (fixed8 & 0xff); msb = (cmsUInt8Number) (((cmsUInt16Number) fixed8 >> 8) & 0xff); return (cmsFloat64Number) ((cmsFloat64Number) msb + ((cmsFloat64Number) lsb / 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) { cmsFloat64Number floater, sign, mid; int Whole, FracPart; cmsUNUSED_PARAMETER(ContextID); 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(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; _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(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; 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 }, { 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 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) { _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 (!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 // 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 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; }