• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //---------------------------------------------------------------------------------
2 //
3 //  Little Color Management System
4 //  Copyright (c) 1998-2023 Marti Maria Saguer
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining
7 // a copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the Software
11 // is furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
18 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 //
24 //---------------------------------------------------------------------------------
25 //
26 
27 #include "lcms2_internal.h"
28 
29 
30 // ----------------------------------------------------------------------------------
31 // Encoding & Decoding support functions
32 // ----------------------------------------------------------------------------------
33 
34 //      Little-Endian to Big-Endian
35 
36 // Adjust a word value after being read/ before being written from/to an ICC profile
_cmsAdjustEndianess16(cmsUInt16Number Word)37 cmsUInt16Number CMSEXPORT  _cmsAdjustEndianess16(cmsUInt16Number Word)
38 {
39 #ifndef CMS_USE_BIG_ENDIAN
40 
41     cmsUInt8Number* pByte = (cmsUInt8Number*) &Word;
42     cmsUInt8Number tmp;
43 
44     tmp = pByte[0];
45     pByte[0] = pByte[1];
46     pByte[1] = tmp;
47 #endif
48 
49     return Word;
50 }
51 
52 
53 // Transports to properly encoded values - note that icc profiles does use big endian notation.
54 
55 // 1 2 3 4
56 // 4 3 2 1
57 
_cmsAdjustEndianess32(cmsUInt32Number DWord)58 cmsUInt32Number CMSEXPORT  _cmsAdjustEndianess32(cmsUInt32Number DWord)
59 {
60 #ifndef CMS_USE_BIG_ENDIAN
61 
62     cmsUInt8Number* pByte = (cmsUInt8Number*) &DWord;
63     cmsUInt8Number temp1;
64     cmsUInt8Number temp2;
65 
66     temp1 = *pByte++;
67     temp2 = *pByte++;
68     *(pByte-1) = *pByte;
69     *pByte++ = temp2;
70     *(pByte-3) = *pByte;
71     *pByte = temp1;
72 #endif
73     return DWord;
74 }
75 
76 // 1 2 3 4 5 6 7 8
77 // 8 7 6 5 4 3 2 1
78 
_cmsAdjustEndianess64(cmsUInt64Number * Result,cmsUInt64Number * QWord)79 void CMSEXPORT  _cmsAdjustEndianess64(cmsUInt64Number* Result, cmsUInt64Number* QWord)
80 {
81 
82 #ifndef CMS_USE_BIG_ENDIAN
83 
84     cmsUInt8Number* pIn  = (cmsUInt8Number*) QWord;
85     cmsUInt8Number* pOut = (cmsUInt8Number*) Result;
86 
87     _cmsAssert(Result != NULL);
88 
89     pOut[7] = pIn[0];
90     pOut[6] = pIn[1];
91     pOut[5] = pIn[2];
92     pOut[4] = pIn[3];
93     pOut[3] = pIn[4];
94     pOut[2] = pIn[5];
95     pOut[1] = pIn[6];
96     pOut[0] = pIn[7];
97 
98 #else
99     _cmsAssert(Result != NULL);
100 
101 #  ifdef CMS_DONT_USE_INT64
102     (*Result)[0] = (*QWord)[0];
103     (*Result)[1] = (*QWord)[1];
104 #  else
105     *Result = *QWord;
106 #  endif
107 #endif
108 }
109 
110 // Auxiliary -- read 8, 16 and 32-bit numbers
_cmsReadUInt8Number(cmsIOHANDLER * io,cmsUInt8Number * n)111 cmsBool CMSEXPORT  _cmsReadUInt8Number(cmsIOHANDLER* io, cmsUInt8Number* n)
112 {
113     cmsUInt8Number tmp;
114 
115     _cmsAssert(io != NULL);
116 
117     if (io -> Read(io, &tmp, sizeof(cmsUInt8Number), 1) != 1)
118             return FALSE;
119 
120     if (n != NULL) *n = tmp;
121     return TRUE;
122 }
123 
_cmsReadUInt16Number(cmsIOHANDLER * io,cmsUInt16Number * n)124 cmsBool CMSEXPORT  _cmsReadUInt16Number(cmsIOHANDLER* io, cmsUInt16Number* n)
125 {
126     cmsUInt16Number tmp;
127 
128     _cmsAssert(io != NULL);
129 
130     if (io -> Read(io, &tmp, sizeof(cmsUInt16Number), 1) != 1)
131             return FALSE;
132 
133     if (n != NULL) *n = _cmsAdjustEndianess16(tmp);
134     return TRUE;
135 }
136 
_cmsReadUInt16Array(cmsIOHANDLER * io,cmsUInt32Number n,cmsUInt16Number * Array)137 cmsBool CMSEXPORT  _cmsReadUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, cmsUInt16Number* Array)
138 {
139     cmsUInt32Number i;
140 
141     _cmsAssert(io != NULL);
142 
143     for (i=0; i < n; i++) {
144 
145         if (Array != NULL) {
146             if (!_cmsReadUInt16Number(io, Array + i)) return FALSE;
147         }
148         else {
149             if (!_cmsReadUInt16Number(io, NULL)) return FALSE;
150         }
151 
152     }
153     return TRUE;
154 }
155 
_cmsReadUInt32Number(cmsIOHANDLER * io,cmsUInt32Number * n)156 cmsBool CMSEXPORT  _cmsReadUInt32Number(cmsIOHANDLER* io, cmsUInt32Number* n)
157 {
158     cmsUInt32Number tmp;
159 
160     _cmsAssert(io != NULL);
161 
162     if (io -> Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1)
163             return FALSE;
164 
165     if (n != NULL) *n = _cmsAdjustEndianess32(tmp);
166     return TRUE;
167 }
168 
_cmsReadFloat32Number(cmsIOHANDLER * io,cmsFloat32Number * n)169 cmsBool CMSEXPORT  _cmsReadFloat32Number(cmsIOHANDLER* io, cmsFloat32Number* n)
170 {
171     union typeConverter {
172         cmsUInt32Number integer;
173         cmsFloat32Number floating_point;
174     } tmp;
175 
176     _cmsAssert(io != NULL);
177 
178     if (io->Read(io, &tmp.integer, sizeof(cmsUInt32Number), 1) != 1)
179         return FALSE;
180 
181     if (n != NULL) {
182 
183         tmp.integer = _cmsAdjustEndianess32(tmp.integer);
184         *n = tmp.floating_point;
185 
186         // Safeguard which covers against absurd values
187         if (*n > 1E+20 || *n < -1E+20) return FALSE;
188 
189         #if defined(_MSC_VER) && _MSC_VER < 1800
190            return TRUE;
191         #elif defined (__BORLANDC__)
192            return TRUE;
193         #elif !defined(_MSC_VER) && (defined(__STDC_VERSION__) && __STDC_VERSION__ < 199901L)
194            return TRUE;
195         #else
196 
197            // fpclassify() required by C99 (only provided by MSVC >= 1800, VS2013 onwards)
198            return ((fpclassify(*n) == FP_ZERO) || (fpclassify(*n) == FP_NORMAL));
199         #endif
200     }
201 
202     return TRUE;
203 }
204 
205 
_cmsReadUInt64Number(cmsIOHANDLER * io,cmsUInt64Number * n)206 cmsBool CMSEXPORT   _cmsReadUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n)
207 {
208     cmsUInt64Number tmp;
209 
210     _cmsAssert(io != NULL);
211 
212     if (io -> Read(io, &tmp, sizeof(cmsUInt64Number), 1) != 1)
213             return FALSE;
214 
215     if (n != NULL) {
216 
217         _cmsAdjustEndianess64(n, &tmp);
218     }
219 
220     return TRUE;
221 }
222 
223 
_cmsRead15Fixed16Number(cmsIOHANDLER * io,cmsFloat64Number * n)224 cmsBool CMSEXPORT  _cmsRead15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number* n)
225 {
226     cmsUInt32Number tmp;
227 
228     _cmsAssert(io != NULL);
229 
230     if (io -> Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1)
231             return FALSE;
232 
233     if (n != NULL) {
234         *n = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32(tmp));
235     }
236 
237     return TRUE;
238 }
239 
240 
_cmsReadXYZNumber(cmsIOHANDLER * io,cmsCIEXYZ * XYZ)241 cmsBool CMSEXPORT  _cmsReadXYZNumber(cmsIOHANDLER* io, cmsCIEXYZ* XYZ)
242 {
243     cmsEncodedXYZNumber xyz;
244 
245     _cmsAssert(io != NULL);
246 
247     if (io ->Read(io, &xyz, sizeof(cmsEncodedXYZNumber), 1) != 1) return FALSE;
248 
249     if (XYZ != NULL) {
250 
251         XYZ->X = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.X));
252         XYZ->Y = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.Y));
253         XYZ->Z = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.Z));
254     }
255     return TRUE;
256 }
257 
_cmsWriteUInt8Number(cmsIOHANDLER * io,cmsUInt8Number n)258 cmsBool CMSEXPORT  _cmsWriteUInt8Number(cmsIOHANDLER* io, cmsUInt8Number n)
259 {
260     _cmsAssert(io != NULL);
261 
262     if (io -> Write(io, sizeof(cmsUInt8Number), &n) != 1)
263             return FALSE;
264 
265     return TRUE;
266 }
267 
_cmsWriteUInt16Number(cmsIOHANDLER * io,cmsUInt16Number n)268 cmsBool CMSEXPORT  _cmsWriteUInt16Number(cmsIOHANDLER* io, cmsUInt16Number n)
269 {
270     cmsUInt16Number tmp;
271 
272     _cmsAssert(io != NULL);
273 
274     tmp = _cmsAdjustEndianess16(n);
275     if (io -> Write(io, sizeof(cmsUInt16Number), &tmp) != 1)
276             return FALSE;
277 
278     return TRUE;
279 }
280 
_cmsWriteUInt16Array(cmsIOHANDLER * io,cmsUInt32Number n,const cmsUInt16Number * Array)281 cmsBool CMSEXPORT  _cmsWriteUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, const cmsUInt16Number* Array)
282 {
283     cmsUInt32Number i;
284 
285     _cmsAssert(io != NULL);
286     _cmsAssert(Array != NULL);
287 
288     for (i=0; i < n; i++) {
289         if (!_cmsWriteUInt16Number(io, Array[i])) return FALSE;
290     }
291 
292     return TRUE;
293 }
294 
_cmsWriteUInt32Number(cmsIOHANDLER * io,cmsUInt32Number n)295 cmsBool CMSEXPORT  _cmsWriteUInt32Number(cmsIOHANDLER* io, cmsUInt32Number n)
296 {
297     cmsUInt32Number tmp;
298 
299     _cmsAssert(io != NULL);
300 
301     tmp = _cmsAdjustEndianess32(n);
302     if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1)
303             return FALSE;
304 
305     return TRUE;
306 }
307 
308 
_cmsWriteFloat32Number(cmsIOHANDLER * io,cmsFloat32Number n)309 cmsBool CMSEXPORT  _cmsWriteFloat32Number(cmsIOHANDLER* io, cmsFloat32Number n)
310 {
311     union typeConverter {
312         cmsUInt32Number integer;
313         cmsFloat32Number floating_point;
314     } tmp;
315 
316     tmp.floating_point = n;
317     tmp.integer = _cmsAdjustEndianess32(tmp.integer);
318     if (io -> Write(io, sizeof(cmsUInt32Number), &tmp.integer) != 1)
319             return FALSE;
320 
321     return TRUE;
322 }
323 
_cmsWriteUInt64Number(cmsIOHANDLER * io,cmsUInt64Number * n)324 cmsBool CMSEXPORT  _cmsWriteUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n)
325 {
326     cmsUInt64Number tmp;
327 
328     _cmsAssert(io != NULL);
329 
330     _cmsAdjustEndianess64(&tmp, n);
331     if (io -> Write(io, sizeof(cmsUInt64Number), &tmp) != 1)
332             return FALSE;
333 
334     return TRUE;
335 }
336 
_cmsWrite15Fixed16Number(cmsIOHANDLER * io,cmsFloat64Number n)337 cmsBool CMSEXPORT  _cmsWrite15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number n)
338 {
339     cmsUInt32Number tmp;
340 
341     _cmsAssert(io != NULL);
342 
343     tmp = _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(n));
344     if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1)
345             return FALSE;
346 
347     return TRUE;
348 }
349 
_cmsWriteXYZNumber(cmsIOHANDLER * io,const cmsCIEXYZ * XYZ)350 cmsBool CMSEXPORT  _cmsWriteXYZNumber(cmsIOHANDLER* io, const cmsCIEXYZ* XYZ)
351 {
352     cmsEncodedXYZNumber xyz;
353 
354     _cmsAssert(io != NULL);
355     _cmsAssert(XYZ != NULL);
356 
357     xyz.X = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(XYZ->X));
358     xyz.Y = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(XYZ->Y));
359     xyz.Z = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(XYZ->Z));
360 
361     return io -> Write(io,  sizeof(cmsEncodedXYZNumber), &xyz);
362 }
363 
364 // from Fixed point 8.8 to double
_cms8Fixed8toDouble(cmsUInt16Number fixed8)365 cmsFloat64Number CMSEXPORT _cms8Fixed8toDouble(cmsUInt16Number fixed8)
366 {
367        cmsUInt8Number  msb, lsb;
368 
369        lsb = (cmsUInt8Number) (fixed8 & 0xff);
370        msb = (cmsUInt8Number) (((cmsUInt16Number) fixed8 >> 8) & 0xff);
371 
372        return (cmsFloat64Number) ((cmsFloat64Number) msb + ((cmsFloat64Number) lsb / 256.0));
373 }
374 
_cmsDoubleTo8Fixed8(cmsFloat64Number val)375 cmsUInt16Number CMSEXPORT _cmsDoubleTo8Fixed8(cmsFloat64Number val)
376 {
377     cmsS15Fixed16Number GammaFixed32 = _cmsDoubleTo15Fixed16(val);
378     return  (cmsUInt16Number) ((GammaFixed32 >> 8) & 0xFFFF);
379 }
380 
381 // from Fixed point 15.16 to double
_cms15Fixed16toDouble(cmsS15Fixed16Number fix32)382 cmsFloat64Number CMSEXPORT _cms15Fixed16toDouble(cmsS15Fixed16Number fix32)
383 {
384     cmsFloat64Number floater, sign, mid;
385     int Whole, FracPart;
386 
387     sign  = (fix32 < 0 ? -1 : 1);
388     fix32 = abs(fix32);
389 
390     Whole     = (cmsUInt16Number)(fix32 >> 16) & 0xffff;
391     FracPart  = (cmsUInt16Number)(fix32 & 0xffff);
392 
393     mid     = (cmsFloat64Number) FracPart / 65536.0;
394     floater = (cmsFloat64Number) Whole + mid;
395 
396     return sign * floater;
397 }
398 
399 // from double to Fixed point 15.16
_cmsDoubleTo15Fixed16(cmsFloat64Number v)400 cmsS15Fixed16Number CMSEXPORT _cmsDoubleTo15Fixed16(cmsFloat64Number v)
401 {
402     return ((cmsS15Fixed16Number) floor((v)*65536.0 + 0.5));
403 }
404 
405 // Date/Time functions
406 
_cmsDecodeDateTimeNumber(const cmsDateTimeNumber * Source,struct tm * Dest)407 void CMSEXPORT _cmsDecodeDateTimeNumber(const cmsDateTimeNumber *Source, struct tm *Dest)
408 {
409 
410     _cmsAssert(Dest != NULL);
411     _cmsAssert(Source != NULL);
412 
413     Dest->tm_sec   = _cmsAdjustEndianess16(Source->seconds);
414     Dest->tm_min   = _cmsAdjustEndianess16(Source->minutes);
415     Dest->tm_hour  = _cmsAdjustEndianess16(Source->hours);
416     Dest->tm_mday  = _cmsAdjustEndianess16(Source->day);
417     Dest->tm_mon   = _cmsAdjustEndianess16(Source->month) - 1;
418     Dest->tm_year  = _cmsAdjustEndianess16(Source->year) - 1900;
419     Dest->tm_wday  = -1;
420     Dest->tm_yday  = -1;
421     Dest->tm_isdst = 0;
422 }
423 
_cmsEncodeDateTimeNumber(cmsDateTimeNumber * Dest,const struct tm * Source)424 void CMSEXPORT _cmsEncodeDateTimeNumber(cmsDateTimeNumber *Dest, const struct tm *Source)
425 {
426     _cmsAssert(Dest != NULL);
427     _cmsAssert(Source != NULL);
428 
429     Dest->seconds = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_sec);
430     Dest->minutes = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_min);
431     Dest->hours   = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_hour);
432     Dest->day     = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_mday);
433     Dest->month   = _cmsAdjustEndianess16((cmsUInt16Number) (Source->tm_mon + 1));
434     Dest->year    = _cmsAdjustEndianess16((cmsUInt16Number) (Source->tm_year + 1900));
435 }
436 
437 // Read base and return type base
_cmsReadTypeBase(cmsIOHANDLER * io)438 cmsTagTypeSignature CMSEXPORT _cmsReadTypeBase(cmsIOHANDLER* io)
439 {
440     _cmsTagBase Base;
441 
442     _cmsAssert(io != NULL);
443 
444     if (io -> Read(io, &Base, sizeof(_cmsTagBase), 1) != 1)
445         return (cmsTagTypeSignature) 0;
446 
447     return (cmsTagTypeSignature) _cmsAdjustEndianess32(Base.sig);
448 }
449 
450 // Setup base marker
_cmsWriteTypeBase(cmsIOHANDLER * io,cmsTagTypeSignature sig)451 cmsBool  CMSEXPORT _cmsWriteTypeBase(cmsIOHANDLER* io, cmsTagTypeSignature sig)
452 {
453     _cmsTagBase  Base;
454 
455     _cmsAssert(io != NULL);
456 
457     Base.sig = (cmsTagTypeSignature) _cmsAdjustEndianess32(sig);
458     memset(&Base.reserved, 0, sizeof(Base.reserved));
459     return io -> Write(io, sizeof(_cmsTagBase), &Base);
460 }
461 
_cmsReadAlignment(cmsIOHANDLER * io)462 cmsBool CMSEXPORT _cmsReadAlignment(cmsIOHANDLER* io)
463 {
464     cmsUInt8Number  Buffer[4];
465     cmsUInt32Number NextAligned, At;
466     cmsUInt32Number BytesToNextAlignedPos;
467 
468     _cmsAssert(io != NULL);
469 
470     At = io -> Tell(io);
471     NextAligned = _cmsALIGNLONG(At);
472     BytesToNextAlignedPos = NextAligned - At;
473     if (BytesToNextAlignedPos == 0) return TRUE;
474     if (BytesToNextAlignedPos > 4)  return FALSE;
475 
476     return (io ->Read(io, Buffer, BytesToNextAlignedPos, 1) == 1);
477 }
478 
_cmsWriteAlignment(cmsIOHANDLER * io)479 cmsBool CMSEXPORT _cmsWriteAlignment(cmsIOHANDLER* io)
480 {
481     cmsUInt8Number  Buffer[4];
482     cmsUInt32Number NextAligned, At;
483     cmsUInt32Number BytesToNextAlignedPos;
484 
485     _cmsAssert(io != NULL);
486 
487     At = io -> Tell(io);
488     NextAligned = _cmsALIGNLONG(At);
489     BytesToNextAlignedPos = NextAligned - At;
490     if (BytesToNextAlignedPos == 0) return TRUE;
491     if (BytesToNextAlignedPos > 4)  return FALSE;
492 
493     memset(Buffer, 0, BytesToNextAlignedPos);
494     return io -> Write(io, BytesToNextAlignedPos, Buffer);
495 }
496 
497 
498 // To deal with text streams. 2K at most
_cmsIOPrintf(cmsIOHANDLER * io,const char * frm,...)499 cmsBool CMSEXPORT _cmsIOPrintf(cmsIOHANDLER* io, const char* frm, ...)
500 {
501     va_list args;
502     int len;
503     cmsUInt8Number Buffer[2048];
504     cmsBool rc;
505     cmsUInt8Number* ptr;
506 
507     _cmsAssert(io != NULL);
508     _cmsAssert(frm != NULL);
509 
510     va_start(args, frm);
511 
512     len = vsnprintf((char*) Buffer, 2047, frm, args);
513     if (len < 0) {
514         va_end(args);
515         return FALSE;   // Truncated, which is a fatal error for us
516     }
517 
518     // setlocale may be active, no commas are needed in PS generator
519     // and PS generator is our only client
520     for (ptr = Buffer; *ptr; ptr++)
521     {
522         if (*ptr == ',') *ptr = '.';
523     }
524 
525     rc = io ->Write(io, (cmsUInt32Number) len, Buffer);
526 
527     va_end(args);
528 
529     return rc;
530 }
531 
532 
533 // Plugin memory management -------------------------------------------------------------------------------------------------
534 
535 // Specialized malloc for plug-ins, that is freed upon exit.
_cmsPluginMalloc(cmsContext ContextID,cmsUInt32Number size)536 void* _cmsPluginMalloc(cmsContext ContextID, cmsUInt32Number size)
537 {
538     struct _cmsContext_struct* ctx = _cmsGetContext(ContextID);
539 
540     if (ctx ->MemPool == NULL) {
541 
542         if (ContextID == NULL) {
543 
544             ctx->MemPool = _cmsCreateSubAlloc(0, 2*1024);
545             if (ctx->MemPool == NULL) return NULL;
546         }
547         else {
548             cmsSignalError(ContextID, cmsERROR_CORRUPTION_DETECTED, "NULL memory pool on context");
549             return NULL;
550         }
551     }
552 
553     return _cmsSubAlloc(ctx->MemPool, size);
554 }
555 
556 
557 // Main plug-in dispatcher
cmsPlugin(void * Plug_in)558 cmsBool CMSEXPORT cmsPlugin(void* Plug_in)
559 {
560     return cmsPluginTHR(NULL, Plug_in);
561 }
562 
cmsPluginTHR(cmsContext id,void * Plug_in)563 cmsBool CMSEXPORT cmsPluginTHR(cmsContext id, void* Plug_in)
564 {
565     cmsPluginBase* Plugin;
566 
567     for (Plugin = (cmsPluginBase*) Plug_in;
568          Plugin != NULL;
569          Plugin = Plugin -> Next) {
570 
571             if (Plugin -> Magic != cmsPluginMagicNumber) {
572                 cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin");
573                 return FALSE;
574             }
575 
576             if (Plugin ->ExpectedVersion > LCMS_VERSION) {
577                 cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "plugin needs Little CMS %d, current version is %d",
578                     Plugin ->ExpectedVersion, LCMS_VERSION);
579                 return FALSE;
580             }
581 
582             switch (Plugin -> Type) {
583 
584                 case cmsPluginMemHandlerSig:
585                     if (!_cmsRegisterMemHandlerPlugin(id, Plugin)) return FALSE;
586                     break;
587 
588                 case cmsPluginInterpolationSig:
589                     if (!_cmsRegisterInterpPlugin(id, Plugin)) return FALSE;
590                     break;
591 
592                 case cmsPluginTagTypeSig:
593                     if (!_cmsRegisterTagTypePlugin(id, Plugin)) return FALSE;
594                     break;
595 
596                 case cmsPluginTagSig:
597                     if (!_cmsRegisterTagPlugin(id, Plugin)) return FALSE;
598                     break;
599 
600                 case cmsPluginFormattersSig:
601                     if (!_cmsRegisterFormattersPlugin(id, Plugin)) return FALSE;
602                     break;
603 
604                 case cmsPluginRenderingIntentSig:
605                     if (!_cmsRegisterRenderingIntentPlugin(id, Plugin)) return FALSE;
606                     break;
607 
608                 case cmsPluginParametricCurveSig:
609                     if (!_cmsRegisterParametricCurvesPlugin(id, Plugin)) return FALSE;
610                     break;
611 
612                 case cmsPluginMultiProcessElementSig:
613                     if (!_cmsRegisterMultiProcessElementPlugin(id, Plugin)) return FALSE;
614                     break;
615 
616                 case cmsPluginOptimizationSig:
617                     if (!_cmsRegisterOptimizationPlugin(id, Plugin)) return FALSE;
618                     break;
619 
620                 case cmsPluginTransformSig:
621                     if (!_cmsRegisterTransformPlugin(id, Plugin)) return FALSE;
622                     break;
623 
624                 case cmsPluginMutexSig:
625                     if (!_cmsRegisterMutexPlugin(id, Plugin)) return FALSE;
626                     break;
627 
628                 case cmsPluginParalellizationSig:
629                     if (!_cmsRegisterParallelizationPlugin(id, Plugin)) return FALSE;
630                     break;
631 
632                 default:
633                     cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin type '%X'", Plugin -> Type);
634                     return FALSE;
635             }
636     }
637 
638     // Keep a reference to the plug-in
639     return TRUE;
640 }
641 
642 
643 // Revert all plug-ins to default
cmsUnregisterPlugins(void)644 void CMSEXPORT cmsUnregisterPlugins(void)
645 {
646     cmsUnregisterPluginsTHR(NULL);
647 }
648 
649 
650 // The Global storage for system context. This is the one and only global variable
651 // pointers structure. All global vars are referenced here.
652 static struct _cmsContext_struct globalContext = {
653 
654     NULL,                                // Not in the linked list
655     NULL,                                // No suballocator
656     {
657         NULL,                            //  UserPtr,
658         &_cmsLogErrorChunk,              //  Logger,
659         &_cmsAlarmCodesChunk,            //  AlarmCodes,
660         &_cmsAdaptationStateChunk,       //  AdaptationState,
661         &_cmsMemPluginChunk,             //  MemPlugin,
662         &_cmsInterpPluginChunk,          //  InterpPlugin,
663         &_cmsCurvesPluginChunk,          //  CurvesPlugin,
664         &_cmsFormattersPluginChunk,      //  FormattersPlugin,
665         &_cmsTagTypePluginChunk,         //  TagTypePlugin,
666         &_cmsTagPluginChunk,             //  TagPlugin,
667         &_cmsIntentsPluginChunk,         //  IntentPlugin,
668         &_cmsMPETypePluginChunk,         //  MPEPlugin,
669         &_cmsOptimizationPluginChunk,    //  OptimizationPlugin,
670         &_cmsTransformPluginChunk,       //  TransformPlugin,
671         &_cmsMutexPluginChunk,           //  MutexPlugin,
672         &_cmsParallelizationPluginChunk  //  ParallelizationPlugin
673     },
674 
675     { NULL, NULL, NULL, NULL, NULL, NULL } // The default memory allocator is not used for context 0
676 };
677 
678 
679 // The context pool (linked list head)
680 static _cmsMutex _cmsContextPoolHeadMutex = CMS_MUTEX_INITIALIZER;
681 static struct _cmsContext_struct* _cmsContextPoolHead = NULL;
682 
683 
684 // Make sure context is initialized (needed on windows)
685 static
InitContextMutex(void)686 cmsBool InitContextMutex(void)
687 {
688     // See the comments regarding locking in lcms2_internal.h
689     // for an explanation of why we need the following code.
690 #ifndef CMS_NO_PTHREADS
691 #ifdef CMS_IS_WINDOWS_
692 #ifndef CMS_RELY_ON_WINDOWS_STATIC_MUTEX_INIT
693 
694     static cmsBool already_initialized = FALSE;
695 
696     if (!already_initialized)
697     {
698         static HANDLE _cmsWindowsInitMutex = NULL;
699         static volatile HANDLE* mutex = &_cmsWindowsInitMutex;
700 
701         if (*mutex == NULL)
702         {
703             HANDLE p = CreateMutex(NULL, FALSE, NULL);
704             if (p && InterlockedCompareExchangePointer((void**)mutex, (void*)p, NULL) != NULL)
705                 CloseHandle(p);
706         }
707         if (*mutex == NULL || WaitForSingleObject(*mutex, INFINITE) == WAIT_FAILED)
708         {
709             cmsSignalError(0, cmsERROR_INTERNAL, "Mutex lock failed");
710             return FALSE;
711         }
712         if (((void**)&_cmsContextPoolHeadMutex)[0] == NULL)
713             InitializeCriticalSection(&_cmsContextPoolHeadMutex);
714         if (*mutex == NULL || !ReleaseMutex(*mutex))
715         {
716             cmsSignalError(0, cmsERROR_INTERNAL, "Mutex unlock failed");
717             return FALSE;
718         }
719         already_initialized = TRUE;
720     }
721 #endif
722 #endif
723 #endif
724 
725     return TRUE;
726 }
727 
728 
729 
730 // Internal, get associated pointer, with guessing. Never returns NULL.
_cmsGetContext(cmsContext ContextID)731 struct _cmsContext_struct* _cmsGetContext(cmsContext ContextID)
732 {
733     struct _cmsContext_struct* id = (struct _cmsContext_struct*) ContextID;
734     struct _cmsContext_struct* ctx;
735 
736     // On 0, use global settings
737     if (id == NULL)
738         return &globalContext;
739 
740     InitContextMutex();
741 
742     // Search
743     _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
744 
745     for (ctx = _cmsContextPoolHead;
746          ctx != NULL;
747          ctx = ctx ->Next) {
748 
749             // Found it?
750         if (id == ctx)
751         {
752             _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
753             return ctx; // New-style context
754         }
755     }
756 
757     _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
758     return &globalContext;
759 }
760 
761 
762 // Internal: get the memory area associanted with each context client
763 // Returns the block assigned to the specific zone. Never return NULL.
_cmsContextGetClientChunk(cmsContext ContextID,_cmsMemoryClient mc)764 void* _cmsContextGetClientChunk(cmsContext ContextID, _cmsMemoryClient mc)
765 {
766     struct _cmsContext_struct* ctx;
767     void *ptr;
768 
769     if ((int) mc < 0 || mc >= MemoryClientMax) {
770 
771            cmsSignalError(ContextID, cmsERROR_INTERNAL, "Bad context client -- possible corruption");
772 
773            // This is catastrophic. Should never reach here
774            _cmsAssert(0);
775 
776            // Reverts to global context
777            return globalContext.chunks[UserPtr];
778     }
779 
780     ctx = _cmsGetContext(ContextID);
781     ptr = ctx ->chunks[mc];
782 
783     if (ptr != NULL)
784         return ptr;
785 
786     // A null ptr means no special settings for that context, and this
787     // reverts to Context0 globals
788     return globalContext.chunks[mc];
789 }
790 
791 
792 // This function returns the given context its default pristine state,
793 // as no plug-ins were declared. There is no way to unregister a single
794 // plug-in, as a single call to cmsPluginTHR() function may register
795 // many different plug-ins simultaneously, then there is no way to
796 // identify which plug-in to unregister.
cmsUnregisterPluginsTHR(cmsContext ContextID)797 void CMSEXPORT cmsUnregisterPluginsTHR(cmsContext ContextID)
798 {
799     _cmsRegisterMemHandlerPlugin(ContextID, NULL);
800     _cmsRegisterInterpPlugin(ContextID, NULL);
801     _cmsRegisterTagTypePlugin(ContextID, NULL);
802     _cmsRegisterTagPlugin(ContextID, NULL);
803     _cmsRegisterFormattersPlugin(ContextID, NULL);
804     _cmsRegisterRenderingIntentPlugin(ContextID, NULL);
805     _cmsRegisterParametricCurvesPlugin(ContextID, NULL);
806     _cmsRegisterMultiProcessElementPlugin(ContextID, NULL);
807     _cmsRegisterOptimizationPlugin(ContextID, NULL);
808     _cmsRegisterTransformPlugin(ContextID, NULL);
809     _cmsRegisterMutexPlugin(ContextID, NULL);
810     _cmsRegisterParallelizationPlugin(ContextID, NULL);
811 
812 }
813 
814 
815 // Returns the memory manager plug-in, if any, from the Plug-in bundle
816 static
_cmsFindMemoryPlugin(void * PluginBundle)817 cmsPluginMemHandler* _cmsFindMemoryPlugin(void* PluginBundle)
818 {
819     cmsPluginBase* Plugin;
820 
821     for (Plugin = (cmsPluginBase*) PluginBundle;
822         Plugin != NULL;
823         Plugin = Plugin -> Next) {
824 
825             if (Plugin -> Magic == cmsPluginMagicNumber &&
826                 Plugin -> ExpectedVersion <= LCMS_VERSION &&
827                 Plugin -> Type == cmsPluginMemHandlerSig) {
828 
829                     // Found!
830                     return (cmsPluginMemHandler*) Plugin;
831             }
832     }
833 
834     // Nope, revert to defaults
835     return NULL;
836 }
837 
838 
839 // Creates a new context with optional associated plug-ins. Caller may also specify an optional pointer to user-defined
840 // data that will be forwarded to plug-ins and logger.
cmsCreateContext(void * Plugin,void * UserData)841 cmsContext CMSEXPORT cmsCreateContext(void* Plugin, void* UserData)
842 {
843     struct _cmsContext_struct* ctx;
844     struct _cmsContext_struct  fakeContext;
845 
846     if (!InitContextMutex()) return NULL;
847 
848     _cmsInstallAllocFunctions(_cmsFindMemoryPlugin(Plugin), &fakeContext.DefaultMemoryManager);
849 
850     fakeContext.chunks[UserPtr]     = UserData;
851     fakeContext.chunks[MemPlugin]   = &fakeContext.DefaultMemoryManager;
852 
853     // Create the context structure.
854     ctx = (struct _cmsContext_struct*) _cmsMalloc(&fakeContext, sizeof(struct _cmsContext_struct));
855     if (ctx == NULL)
856         return NULL;     // Something very wrong happened!
857 
858     // Init the structure and the memory manager
859     memset(ctx, 0, sizeof(struct _cmsContext_struct));
860 
861     // Keep memory manager
862     memcpy(&ctx->DefaultMemoryManager, &fakeContext.DefaultMemoryManager, sizeof(_cmsMemPluginChunk));
863 
864     // Maintain the linked list (with proper locking)
865     _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
866        ctx ->Next = _cmsContextPoolHead;
867        _cmsContextPoolHead = ctx;
868     _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
869 
870     ctx ->chunks[UserPtr]     = UserData;
871     ctx ->chunks[MemPlugin]   = &ctx->DefaultMemoryManager;
872 
873     // Now we can allocate the pool by using default memory manager
874     ctx ->MemPool = _cmsCreateSubAlloc(ctx, 22 * sizeof(void*));  // default size about 22 pointers
875     if (ctx ->MemPool == NULL) {
876 
877          cmsDeleteContext(ctx);
878         return NULL;
879     }
880 
881     _cmsAllocLogErrorChunk(ctx, NULL);
882     _cmsAllocAlarmCodesChunk(ctx, NULL);
883     _cmsAllocAdaptationStateChunk(ctx, NULL);
884     _cmsAllocMemPluginChunk(ctx, NULL);
885     _cmsAllocInterpPluginChunk(ctx, NULL);
886     _cmsAllocCurvesPluginChunk(ctx, NULL);
887     _cmsAllocFormattersPluginChunk(ctx, NULL);
888     _cmsAllocTagTypePluginChunk(ctx, NULL);
889     _cmsAllocMPETypePluginChunk(ctx, NULL);
890     _cmsAllocTagPluginChunk(ctx, NULL);
891     _cmsAllocIntentsPluginChunk(ctx, NULL);
892     _cmsAllocOptimizationPluginChunk(ctx, NULL);
893     _cmsAllocTransformPluginChunk(ctx, NULL);
894     _cmsAllocMutexPluginChunk(ctx, NULL);
895     _cmsAllocParallelizationPluginChunk(ctx, NULL);
896 
897     // Setup the plug-ins
898     if (!cmsPluginTHR(ctx, Plugin)) {
899 
900         cmsDeleteContext(ctx);
901         return NULL;
902     }
903 
904     return (cmsContext) ctx;
905 }
906 
907 // Duplicates a context with all associated plug-ins.
908 // Caller may specify an optional pointer to user-defined
909 // data that will be forwarded to plug-ins and logger.
cmsDupContext(cmsContext ContextID,void * NewUserData)910 cmsContext CMSEXPORT cmsDupContext(cmsContext ContextID, void* NewUserData)
911 {
912     int i;
913     struct _cmsContext_struct* ctx;
914     const struct _cmsContext_struct* src = _cmsGetContext(ContextID);
915 
916     void* userData = (NewUserData != NULL) ? NewUserData : src -> chunks[UserPtr];
917 
918 
919     ctx = (struct _cmsContext_struct*) _cmsMalloc(ContextID, sizeof(struct _cmsContext_struct));
920     if (ctx == NULL)
921         return NULL;     // Something very wrong happened
922 
923     if (!InitContextMutex()) return NULL;
924 
925     // Setup default memory allocators
926     memcpy(&ctx->DefaultMemoryManager, &src->DefaultMemoryManager, sizeof(ctx->DefaultMemoryManager));
927 
928     // Maintain the linked list
929     _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
930        ctx ->Next = _cmsContextPoolHead;
931        _cmsContextPoolHead = ctx;
932     _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
933 
934     ctx ->chunks[UserPtr]    = userData;
935     ctx ->chunks[MemPlugin]  = &ctx->DefaultMemoryManager;
936 
937     ctx ->MemPool = _cmsCreateSubAlloc(ctx, 22 * sizeof(void*));
938     if (ctx ->MemPool == NULL) {
939 
940          cmsDeleteContext(ctx);
941         return NULL;
942     }
943 
944     // Allocate all required chunks.
945     _cmsAllocLogErrorChunk(ctx, src);
946     _cmsAllocAlarmCodesChunk(ctx, src);
947     _cmsAllocAdaptationStateChunk(ctx, src);
948     _cmsAllocMemPluginChunk(ctx, src);
949     _cmsAllocInterpPluginChunk(ctx, src);
950     _cmsAllocCurvesPluginChunk(ctx, src);
951     _cmsAllocFormattersPluginChunk(ctx, src);
952     _cmsAllocTagTypePluginChunk(ctx, src);
953     _cmsAllocMPETypePluginChunk(ctx, src);
954     _cmsAllocTagPluginChunk(ctx, src);
955     _cmsAllocIntentsPluginChunk(ctx, src);
956     _cmsAllocOptimizationPluginChunk(ctx, src);
957     _cmsAllocTransformPluginChunk(ctx, src);
958     _cmsAllocMutexPluginChunk(ctx, src);
959     _cmsAllocParallelizationPluginChunk(ctx, src);
960 
961     // Make sure no one failed
962     for (i=Logger; i < MemoryClientMax; i++) {
963 
964         if (src ->chunks[i] == NULL) {
965             cmsDeleteContext((cmsContext) ctx);
966             return NULL;
967         }
968     }
969 
970     return (cmsContext) ctx;
971 }
972 
973 
974 // Frees any resources associated with the given context,
975 // and destroys the context placeholder.
976 // The ContextID can no longer be used in any THR operation.
cmsDeleteContext(cmsContext ContextID)977 void CMSEXPORT cmsDeleteContext(cmsContext ContextID)
978 {
979     if (ContextID == NULL) {
980 
981         cmsUnregisterPlugins();
982         if (globalContext.MemPool != NULL)
983             _cmsSubAllocDestroy(globalContext.MemPool);
984         globalContext.MemPool = NULL;
985     }
986     else {
987 
988         struct _cmsContext_struct* ctx = (struct _cmsContext_struct*) ContextID;
989         struct _cmsContext_struct  fakeContext;
990         struct _cmsContext_struct* prev;
991 
992 
993         InitContextMutex();
994 
995         memcpy(&fakeContext.DefaultMemoryManager, &ctx->DefaultMemoryManager, sizeof(ctx->DefaultMemoryManager));
996 
997         fakeContext.chunks[UserPtr]     = ctx ->chunks[UserPtr];
998         fakeContext.chunks[MemPlugin]   = &fakeContext.DefaultMemoryManager;
999 
1000         // Get rid of plugins
1001         cmsUnregisterPluginsTHR(ContextID);
1002 
1003         // Since all memory is allocated in the private pool, all what we need to do is destroy the pool
1004         if (ctx -> MemPool != NULL)
1005               _cmsSubAllocDestroy(ctx ->MemPool);
1006         ctx -> MemPool = NULL;
1007 
1008         // Maintain list
1009         _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
1010         if (_cmsContextPoolHead == ctx) {
1011 
1012             _cmsContextPoolHead = ctx->Next;
1013         }
1014         else {
1015 
1016             // Search for previous
1017             for (prev = _cmsContextPoolHead;
1018                  prev != NULL;
1019                  prev = prev ->Next)
1020             {
1021                 if (prev -> Next == ctx) {
1022                     prev -> Next = ctx ->Next;
1023                     break;
1024                 }
1025             }
1026         }
1027         _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
1028 
1029         // free the memory block itself
1030         _cmsFree(&fakeContext, ctx);
1031     }
1032 }
1033 
1034 // Returns the user data associated to the given ContextID, or NULL if no user data was attached on context creation
cmsGetContextUserData(cmsContext ContextID)1035 void* CMSEXPORT cmsGetContextUserData(cmsContext ContextID)
1036 {
1037     return _cmsContextGetClientChunk(ContextID, UserPtr);
1038 }
1039 
1040 
1041 // Use context mutex to provide thread-safe time
_cmsGetTime(struct tm * ptr_time)1042 cmsBool _cmsGetTime(struct tm* ptr_time)
1043 {
1044     struct tm* t;
1045 #if defined(HAVE_GMTIME_R) || defined(HAVE_GMTIME_S)
1046     struct tm tm;
1047 #endif
1048 
1049     time_t now = time(NULL);
1050 
1051 #ifdef HAVE_GMTIME_R
1052     t = gmtime_r(&now, &tm);
1053 #elif defined(HAVE_GMTIME_S)
1054     t = gmtime_s(&tm, &now) == 0 ? &tm : NULL;
1055 #else
1056     if (!InitContextMutex()) return FALSE;
1057 
1058     _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
1059     t = gmtime(&now);
1060     _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
1061 #endif
1062 
1063     if (t == NULL)
1064         return FALSE;
1065     else {
1066         *ptr_time = *t;
1067         return TRUE;
1068     }
1069 }
1070