|  | //--------------------------------------------------------------------------------- | 
|  | // | 
|  | //  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(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) | 
|  | { | 
|  | union typeConverter { | 
|  | cmsUInt32Number integer; | 
|  | cmsFloat32Number floating_point; | 
|  | } tmp; | 
|  |  | 
|  | _cmsAssert(io != NULL); | 
|  |  | 
|  | if (io->Read(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) | 
|  | 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) | 
|  | { | 
|  | union typeConverter { | 
|  | cmsUInt32Number integer; | 
|  | cmsFloat32Number floating_point; | 
|  | } tmp; | 
|  |  | 
|  | tmp.floating_point = n; | 
|  | tmp.integer = _cmsAdjustEndianess32(tmp.integer); | 
|  | if (io -> Write(io, sizeof(cmsUInt32Number), &tmp.integer) != 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; | 
|  | 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(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; | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  |  | 
|  | // 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, | 
|  | &_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 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); | 
|  | _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 (!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 | 
|  |  | 
|  | 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(); | 
|  | 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 | 
|  | 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); | 
|  | } | 
|  |  | 
|  |  | 
|  | // 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; | 
|  | } | 
|  | } |