1 //---------------------------------------------------------------------------------
2 //
3 //  Little Color Management System
4 //  Copyright (c) 1998-2020 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     cmsUInt32Number tmp;
172 
173     _cmsAssert(io != NULL);
174 
175     if (io->Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1)
176         return FALSE;
177 
178     if (n != NULL) {
179 
180         tmp = _cmsAdjustEndianess32(tmp);
181         *n = *(cmsFloat32Number*)(void*)&tmp;
182 
183         // Safeguard which covers against absurd values
184         if (*n > 1E+20 || *n < -1E+20) return FALSE;
185 
186         #if defined(_MSC_VER) && _MSC_VER < 1800
187            return TRUE;
188         #elif defined (__BORLANDC__)
189            return TRUE;
190         #elif !defined(_MSC_VER) && (defined(__STDC_VERSION__) && __STDC_VERSION__ < 199901L)
191            return TRUE;
192         #else
193 
194            // fpclassify() required by C99 (only provided by MSVC >= 1800, VS2013 onwards)
195            return ((fpclassify(*n) == FP_ZERO) || (fpclassify(*n) == FP_NORMAL));
196         #endif
197     }
198 
199     return TRUE;
200 }
201 
202 
_cmsReadUInt64Number(cmsIOHANDLER * io,cmsUInt64Number * n)203 cmsBool CMSEXPORT   _cmsReadUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n)
204 {
205     cmsUInt64Number tmp;
206 
207     _cmsAssert(io != NULL);
208 
209     if (io -> Read(io, &tmp, sizeof(cmsUInt64Number), 1) != 1)
210             return FALSE;
211 
212     if (n != NULL) {
213 
214         _cmsAdjustEndianess64(n, &tmp);
215     }
216 
217     return TRUE;
218 }
219 
220 
_cmsRead15Fixed16Number(cmsIOHANDLER * io,cmsFloat64Number * n)221 cmsBool CMSEXPORT  _cmsRead15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number* n)
222 {
223     cmsUInt32Number tmp;
224 
225     _cmsAssert(io != NULL);
226 
227     if (io -> Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1)
228             return FALSE;
229 
230     if (n != NULL) {
231         *n = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32(tmp));
232     }
233 
234     return TRUE;
235 }
236 
237 
_cmsReadXYZNumber(cmsIOHANDLER * io,cmsCIEXYZ * XYZ)238 cmsBool CMSEXPORT  _cmsReadXYZNumber(cmsIOHANDLER* io, cmsCIEXYZ* XYZ)
239 {
240     cmsEncodedXYZNumber xyz;
241 
242     _cmsAssert(io != NULL);
243 
244     if (io ->Read(io, &xyz, sizeof(cmsEncodedXYZNumber), 1) != 1) return FALSE;
245 
246     if (XYZ != NULL) {
247 
248         XYZ->X = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.X));
249         XYZ->Y = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.Y));
250         XYZ->Z = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.Z));
251     }
252     return TRUE;
253 }
254 
_cmsWriteUInt8Number(cmsIOHANDLER * io,cmsUInt8Number n)255 cmsBool CMSEXPORT  _cmsWriteUInt8Number(cmsIOHANDLER* io, cmsUInt8Number n)
256 {
257     _cmsAssert(io != NULL);
258 
259     if (io -> Write(io, sizeof(cmsUInt8Number), &n) != 1)
260             return FALSE;
261 
262     return TRUE;
263 }
264 
_cmsWriteUInt16Number(cmsIOHANDLER * io,cmsUInt16Number n)265 cmsBool CMSEXPORT  _cmsWriteUInt16Number(cmsIOHANDLER* io, cmsUInt16Number n)
266 {
267     cmsUInt16Number tmp;
268 
269     _cmsAssert(io != NULL);
270 
271     tmp = _cmsAdjustEndianess16(n);
272     if (io -> Write(io, sizeof(cmsUInt16Number), &tmp) != 1)
273             return FALSE;
274 
275     return TRUE;
276 }
277 
_cmsWriteUInt16Array(cmsIOHANDLER * io,cmsUInt32Number n,const cmsUInt16Number * Array)278 cmsBool CMSEXPORT  _cmsWriteUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, const cmsUInt16Number* Array)
279 {
280     cmsUInt32Number i;
281 
282     _cmsAssert(io != NULL);
283     _cmsAssert(Array != NULL);
284 
285     for (i=0; i < n; i++) {
286         if (!_cmsWriteUInt16Number(io, Array[i])) return FALSE;
287     }
288 
289     return TRUE;
290 }
291 
_cmsWriteUInt32Number(cmsIOHANDLER * io,cmsUInt32Number n)292 cmsBool CMSEXPORT  _cmsWriteUInt32Number(cmsIOHANDLER* io, cmsUInt32Number n)
293 {
294     cmsUInt32Number tmp;
295 
296     _cmsAssert(io != NULL);
297 
298     tmp = _cmsAdjustEndianess32(n);
299     if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1)
300             return FALSE;
301 
302     return TRUE;
303 }
304 
305 
_cmsWriteFloat32Number(cmsIOHANDLER * io,cmsFloat32Number n)306 cmsBool CMSEXPORT  _cmsWriteFloat32Number(cmsIOHANDLER* io, cmsFloat32Number n)
307 {
308     cmsUInt32Number tmp;
309 
310     _cmsAssert(io != NULL);
311 
312     tmp = *(cmsUInt32Number*) (void*) &n;
313     tmp = _cmsAdjustEndianess32(tmp);
314     if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1)
315             return FALSE;
316 
317     return TRUE;
318 }
319 
_cmsWriteUInt64Number(cmsIOHANDLER * io,cmsUInt64Number * n)320 cmsBool CMSEXPORT  _cmsWriteUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n)
321 {
322     cmsUInt64Number tmp;
323 
324     _cmsAssert(io != NULL);
325 
326     _cmsAdjustEndianess64(&tmp, n);
327     if (io -> Write(io, sizeof(cmsUInt64Number), &tmp) != 1)
328             return FALSE;
329 
330     return TRUE;
331 }
332 
_cmsWrite15Fixed16Number(cmsIOHANDLER * io,cmsFloat64Number n)333 cmsBool CMSEXPORT  _cmsWrite15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number n)
334 {
335     cmsUInt32Number tmp;
336 
337     _cmsAssert(io != NULL);
338 
339     tmp = _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(n));
340     if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1)
341             return FALSE;
342 
343     return TRUE;
344 }
345 
_cmsWriteXYZNumber(cmsIOHANDLER * io,const cmsCIEXYZ * XYZ)346 cmsBool CMSEXPORT  _cmsWriteXYZNumber(cmsIOHANDLER* io, const cmsCIEXYZ* XYZ)
347 {
348     cmsEncodedXYZNumber xyz;
349 
350     _cmsAssert(io != NULL);
351     _cmsAssert(XYZ != NULL);
352 
353     xyz.X = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(XYZ->X));
354     xyz.Y = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(XYZ->Y));
355     xyz.Z = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(XYZ->Z));
356 
357     return io -> Write(io,  sizeof(cmsEncodedXYZNumber), &xyz);
358 }
359 
360 // from Fixed point 8.8 to double
_cms8Fixed8toDouble(cmsUInt16Number fixed8)361 cmsFloat64Number CMSEXPORT _cms8Fixed8toDouble(cmsUInt16Number fixed8)
362 {
363        cmsUInt8Number  msb, lsb;
364 
365        lsb = (cmsUInt8Number) (fixed8 & 0xff);
366        msb = (cmsUInt8Number) (((cmsUInt16Number) fixed8 >> 8) & 0xff);
367 
368        return (cmsFloat64Number) ((cmsFloat64Number) msb + ((cmsFloat64Number) lsb / 256.0));
369 }
370 
_cmsDoubleTo8Fixed8(cmsFloat64Number val)371 cmsUInt16Number CMSEXPORT _cmsDoubleTo8Fixed8(cmsFloat64Number val)
372 {
373     cmsS15Fixed16Number GammaFixed32 = _cmsDoubleTo15Fixed16(val);
374     return  (cmsUInt16Number) ((GammaFixed32 >> 8) & 0xFFFF);
375 }
376 
377 // from Fixed point 15.16 to double
_cms15Fixed16toDouble(cmsS15Fixed16Number fix32)378 cmsFloat64Number CMSEXPORT _cms15Fixed16toDouble(cmsS15Fixed16Number fix32)
379 {
380     cmsFloat64Number floater, sign, mid;
381     int Whole, FracPart;
382 
383     sign  = (fix32 < 0 ? -1 : 1);
384     fix32 = abs(fix32);
385 
386     Whole     = (cmsUInt16Number)(fix32 >> 16) & 0xffff;
387     FracPart  = (cmsUInt16Number)(fix32 & 0xffff);
388 
389     mid     = (cmsFloat64Number) FracPart / 65536.0;
390     floater = (cmsFloat64Number) Whole + mid;
391 
392     return sign * floater;
393 }
394 
395 // from double to Fixed point 15.16
_cmsDoubleTo15Fixed16(cmsFloat64Number v)396 cmsS15Fixed16Number CMSEXPORT _cmsDoubleTo15Fixed16(cmsFloat64Number v)
397 {
398     return ((cmsS15Fixed16Number) floor((v)*65536.0 + 0.5));
399 }
400 
401 // Date/Time functions
402 
_cmsDecodeDateTimeNumber(const cmsDateTimeNumber * Source,struct tm * Dest)403 void CMSEXPORT _cmsDecodeDateTimeNumber(const cmsDateTimeNumber *Source, struct tm *Dest)
404 {
405 
406     _cmsAssert(Dest != NULL);
407     _cmsAssert(Source != NULL);
408 
409     Dest->tm_sec   = _cmsAdjustEndianess16(Source->seconds);
410     Dest->tm_min   = _cmsAdjustEndianess16(Source->minutes);
411     Dest->tm_hour  = _cmsAdjustEndianess16(Source->hours);
412     Dest->tm_mday  = _cmsAdjustEndianess16(Source->day);
413     Dest->tm_mon   = _cmsAdjustEndianess16(Source->month) - 1;
414     Dest->tm_year  = _cmsAdjustEndianess16(Source->year) - 1900;
415     Dest->tm_wday  = -1;
416     Dest->tm_yday  = -1;
417     Dest->tm_isdst = 0;
418 }
419 
_cmsEncodeDateTimeNumber(cmsDateTimeNumber * Dest,const struct tm * Source)420 void CMSEXPORT _cmsEncodeDateTimeNumber(cmsDateTimeNumber *Dest, const struct tm *Source)
421 {
422     _cmsAssert(Dest != NULL);
423     _cmsAssert(Source != NULL);
424 
425     Dest->seconds = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_sec);
426     Dest->minutes = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_min);
427     Dest->hours   = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_hour);
428     Dest->day     = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_mday);
429     Dest->month   = _cmsAdjustEndianess16((cmsUInt16Number) (Source->tm_mon + 1));
430     Dest->year    = _cmsAdjustEndianess16((cmsUInt16Number) (Source->tm_year + 1900));
431 }
432 
433 // Read base and return type base
_cmsReadTypeBase(cmsIOHANDLER * io)434 cmsTagTypeSignature CMSEXPORT _cmsReadTypeBase(cmsIOHANDLER* io)
435 {
436     _cmsTagBase Base;
437 
438     _cmsAssert(io != NULL);
439 
440     if (io -> Read(io, &Base, sizeof(_cmsTagBase), 1) != 1)
441         return (cmsTagTypeSignature) 0;
442 
443     return (cmsTagTypeSignature) _cmsAdjustEndianess32(Base.sig);
444 }
445 
446 // Setup base marker
_cmsWriteTypeBase(cmsIOHANDLER * io,cmsTagTypeSignature sig)447 cmsBool  CMSEXPORT _cmsWriteTypeBase(cmsIOHANDLER* io, cmsTagTypeSignature sig)
448 {
449     _cmsTagBase  Base;
450 
451     _cmsAssert(io != NULL);
452 
453     Base.sig = (cmsTagTypeSignature) _cmsAdjustEndianess32(sig);
454     memset(&Base.reserved, 0, sizeof(Base.reserved));
455     return io -> Write(io, sizeof(_cmsTagBase), &Base);
456 }
457 
_cmsReadAlignment(cmsIOHANDLER * io)458 cmsBool CMSEXPORT _cmsReadAlignment(cmsIOHANDLER* io)
459 {
460     cmsUInt8Number  Buffer[4];
461     cmsUInt32Number NextAligned, At;
462     cmsUInt32Number BytesToNextAlignedPos;
463 
464     _cmsAssert(io != NULL);
465 
466     At = io -> Tell(io);
467     NextAligned = _cmsALIGNLONG(At);
468     BytesToNextAlignedPos = NextAligned - At;
469     if (BytesToNextAlignedPos == 0) return TRUE;
470     if (BytesToNextAlignedPos > 4)  return FALSE;
471 
472     return (io ->Read(io, Buffer, BytesToNextAlignedPos, 1) == 1);
473 }
474 
_cmsWriteAlignment(cmsIOHANDLER * io)475 cmsBool CMSEXPORT _cmsWriteAlignment(cmsIOHANDLER* io)
476 {
477     cmsUInt8Number  Buffer[4];
478     cmsUInt32Number NextAligned, At;
479     cmsUInt32Number BytesToNextAlignedPos;
480 
481     _cmsAssert(io != NULL);
482 
483     At = io -> Tell(io);
484     NextAligned = _cmsALIGNLONG(At);
485     BytesToNextAlignedPos = NextAligned - At;
486     if (BytesToNextAlignedPos == 0) return TRUE;
487     if (BytesToNextAlignedPos > 4)  return FALSE;
488 
489     memset(Buffer, 0, BytesToNextAlignedPos);
490     return io -> Write(io, BytesToNextAlignedPos, Buffer);
491 }
492 
493 
494 // To deal with text streams. 2K at most
_cmsIOPrintf(cmsIOHANDLER * io,const char * frm,...)495 cmsBool CMSEXPORT _cmsIOPrintf(cmsIOHANDLER* io, const char* frm, ...)
496 {
497     va_list args;
498     int len;
499     cmsUInt8Number Buffer[2048];
500     cmsBool rc;
501     cmsUInt8Number* ptr;
502 
503     _cmsAssert(io != NULL);
504     _cmsAssert(frm != NULL);
505 
506     va_start(args, frm);
507 
508     len = vsnprintf((char*) Buffer, 2047, frm, args);
509     if (len < 0) {
510         va_end(args);
511         return FALSE;   // Truncated, which is a fatal error for us
512     }
513 
514     // setlocale may be active, no commas are needed in PS generator
515     // and PS generator is our only client
516     for (ptr = Buffer; *ptr; ptr++)
517     {
518         if (*ptr == ',') *ptr = '.';
519     }
520 
521     rc = io ->Write(io, (cmsUInt32Number) len, Buffer);
522 
523     va_end(args);
524 
525     return rc;
526 }
527 
528 
529 // Plugin memory management -------------------------------------------------------------------------------------------------
530 
531 // Specialized malloc for plug-ins, that is freed upon exit.
_cmsPluginMalloc(cmsContext ContextID,cmsUInt32Number size)532 void* _cmsPluginMalloc(cmsContext ContextID, cmsUInt32Number size)
533 {
534     struct _cmsContext_struct* ctx = _cmsGetContext(ContextID);
535 
536     if (ctx ->MemPool == NULL) {
537 
538         if (ContextID == NULL) {
539 
540             ctx->MemPool = _cmsCreateSubAlloc(0, 2*1024);
541             if (ctx->MemPool == NULL) return NULL;
542         }
543         else {
544             cmsSignalError(ContextID, cmsERROR_CORRUPTION_DETECTED, "NULL memory pool on context");
545             return NULL;
546         }
547     }
548 
549     return _cmsSubAlloc(ctx->MemPool, size);
550 }
551 
552 
553 // Main plug-in dispatcher
cmsPlugin(void * Plug_in)554 cmsBool CMSEXPORT cmsPlugin(void* Plug_in)
555 {
556     return cmsPluginTHR(NULL, Plug_in);
557 }
558 
cmsPluginTHR(cmsContext id,void * Plug_in)559 cmsBool CMSEXPORT cmsPluginTHR(cmsContext id, void* Plug_in)
560 {
561     cmsPluginBase* Plugin;
562 
563     for (Plugin = (cmsPluginBase*) Plug_in;
564          Plugin != NULL;
565          Plugin = Plugin -> Next) {
566 
567             if (Plugin -> Magic != cmsPluginMagicNumber) {
568                 cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin");
569                 return FALSE;
570             }
571 
572             if (Plugin ->ExpectedVersion > LCMS_VERSION) {
573                 cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "plugin needs Little CMS %d, current version is %d",
574                     Plugin ->ExpectedVersion, LCMS_VERSION);
575                 return FALSE;
576             }
577 
578             switch (Plugin -> Type) {
579 
580                 case cmsPluginMemHandlerSig:
581                     if (!_cmsRegisterMemHandlerPlugin(id, Plugin)) return FALSE;
582                     break;
583 
584                 case cmsPluginInterpolationSig:
585                     if (!_cmsRegisterInterpPlugin(id, Plugin)) return FALSE;
586                     break;
587 
588                 case cmsPluginTagTypeSig:
589                     if (!_cmsRegisterTagTypePlugin(id, Plugin)) return FALSE;
590                     break;
591 
592                 case cmsPluginTagSig:
593                     if (!_cmsRegisterTagPlugin(id, Plugin)) return FALSE;
594                     break;
595 
596                 case cmsPluginFormattersSig:
597                     if (!_cmsRegisterFormattersPlugin(id, Plugin)) return FALSE;
598                     break;
599 
600                 case cmsPluginRenderingIntentSig:
601                     if (!_cmsRegisterRenderingIntentPlugin(id, Plugin)) return FALSE;
602                     break;
603 
604                 case cmsPluginParametricCurveSig:
605                     if (!_cmsRegisterParametricCurvesPlugin(id, Plugin)) return FALSE;
606                     break;
607 
608                 case cmsPluginMultiProcessElementSig:
609                     if (!_cmsRegisterMultiProcessElementPlugin(id, Plugin)) return FALSE;
610                     break;
611 
612                 case cmsPluginOptimizationSig:
613                     if (!_cmsRegisterOptimizationPlugin(id, Plugin)) return FALSE;
614                     break;
615 
616                 case cmsPluginTransformSig:
617                     if (!_cmsRegisterTransformPlugin(id, Plugin)) return FALSE;
618                     break;
619 
620                 case cmsPluginMutexSig:
621                     if (!_cmsRegisterMutexPlugin(id, Plugin)) return FALSE;
622                     break;
623 
624                 default:
625                     cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin type '%X'", Plugin -> Type);
626                     return FALSE;
627             }
628     }
629 
630     // Keep a reference to the plug-in
631     return TRUE;
632 }
633 
634 
635 // Revert all plug-ins to default
cmsUnregisterPlugins(void)636 void CMSEXPORT cmsUnregisterPlugins(void)
637 {
638     cmsUnregisterPluginsTHR(NULL);
639 }
640 
641 
642 // The Global storage for system context. This is the one and only global variable
643 // pointers structure. All global vars are referenced here.
644 static struct _cmsContext_struct globalContext = {
645 
646     NULL,                              // Not in the linked list
647     NULL,                              // No suballocator
648     {
649         NULL,                          //  UserPtr,
650         &_cmsLogErrorChunk,            //  Logger,
651         &_cmsAlarmCodesChunk,          //  AlarmCodes,
652         &_cmsAdaptationStateChunk,     //  AdaptationState,
653         &_cmsMemPluginChunk,           //  MemPlugin,
654         &_cmsInterpPluginChunk,        //  InterpPlugin,
655         &_cmsCurvesPluginChunk,        //  CurvesPlugin,
656         &_cmsFormattersPluginChunk,    //  FormattersPlugin,
657         &_cmsTagTypePluginChunk,       //  TagTypePlugin,
658         &_cmsTagPluginChunk,           //  TagPlugin,
659         &_cmsIntentsPluginChunk,       //  IntentPlugin,
660         &_cmsMPETypePluginChunk,       //  MPEPlugin,
661         &_cmsOptimizationPluginChunk,  //  OptimizationPlugin,
662         &_cmsTransformPluginChunk,     //  TransformPlugin,
663         &_cmsMutexPluginChunk          //  MutexPlugin
664     },
665 
666     { NULL, NULL, NULL, NULL, NULL, NULL } // The default memory allocator is not used for context 0
667 };
668 
669 
670 // The context pool (linked list head)
671 static _cmsMutex _cmsContextPoolHeadMutex = CMS_MUTEX_INITIALIZER;
672 static struct _cmsContext_struct* _cmsContextPoolHead = NULL;
673 
674 // Internal, get associated pointer, with guessing. Never returns NULL.
_cmsGetContext(cmsContext ContextID)675 struct _cmsContext_struct* _cmsGetContext(cmsContext ContextID)
676 {
677     struct _cmsContext_struct* id = (struct _cmsContext_struct*) ContextID;
678     struct _cmsContext_struct* ctx;
679 
680 
681     // On 0, use global settings
682     if (id == NULL)
683         return &globalContext;
684 
685     // Search
686     _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
687 
688     for (ctx = _cmsContextPoolHead;
689          ctx != NULL;
690          ctx = ctx ->Next) {
691 
692             // Found it?
693         if (id == ctx)
694         {
695             _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
696             return ctx; // New-style context
697         }
698     }
699 
700     _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
701     return &globalContext;
702 }
703 
704 
705 // Internal: get the memory area associanted with each context client
706 // Returns the block assigned to the specific zone. Never return NULL.
_cmsContextGetClientChunk(cmsContext ContextID,_cmsMemoryClient mc)707 void* _cmsContextGetClientChunk(cmsContext ContextID, _cmsMemoryClient mc)
708 {
709     struct _cmsContext_struct* ctx;
710     void *ptr;
711 
712     if ((int) mc < 0 || mc >= MemoryClientMax) {
713 
714            cmsSignalError(ContextID, cmsERROR_INTERNAL, "Bad context client -- possible corruption");
715 
716            // This is catastrophic. Should never reach here
717            _cmsAssert(0);
718 
719            // Reverts to global context
720            return globalContext.chunks[UserPtr];
721     }
722 
723     ctx = _cmsGetContext(ContextID);
724     ptr = ctx ->chunks[mc];
725 
726     if (ptr != NULL)
727         return ptr;
728 
729     // A null ptr means no special settings for that context, and this
730     // reverts to Context0 globals
731     return globalContext.chunks[mc];
732 }
733 
734 
735 // This function returns the given context its default pristine state,
736 // as no plug-ins were declared. There is no way to unregister a single
737 // plug-in, as a single call to cmsPluginTHR() function may register
738 // many different plug-ins simultaneously, then there is no way to
739 // identify which plug-in to unregister.
cmsUnregisterPluginsTHR(cmsContext ContextID)740 void CMSEXPORT cmsUnregisterPluginsTHR(cmsContext ContextID)
741 {
742     _cmsRegisterMemHandlerPlugin(ContextID, NULL);
743     _cmsRegisterInterpPlugin(ContextID, NULL);
744     _cmsRegisterTagTypePlugin(ContextID, NULL);
745     _cmsRegisterTagPlugin(ContextID, NULL);
746     _cmsRegisterFormattersPlugin(ContextID, NULL);
747     _cmsRegisterRenderingIntentPlugin(ContextID, NULL);
748     _cmsRegisterParametricCurvesPlugin(ContextID, NULL);
749     _cmsRegisterMultiProcessElementPlugin(ContextID, NULL);
750     _cmsRegisterOptimizationPlugin(ContextID, NULL);
751     _cmsRegisterTransformPlugin(ContextID, NULL);
752     _cmsRegisterMutexPlugin(ContextID, NULL);
753 }
754 
755 
756 // Returns the memory manager plug-in, if any, from the Plug-in bundle
757 static
_cmsFindMemoryPlugin(void * PluginBundle)758 cmsPluginMemHandler* _cmsFindMemoryPlugin(void* PluginBundle)
759 {
760     cmsPluginBase* Plugin;
761 
762     for (Plugin = (cmsPluginBase*) PluginBundle;
763         Plugin != NULL;
764         Plugin = Plugin -> Next) {
765 
766             if (Plugin -> Magic == cmsPluginMagicNumber &&
767                 Plugin -> ExpectedVersion <= LCMS_VERSION &&
768                 Plugin -> Type == cmsPluginMemHandlerSig) {
769 
770                     // Found!
771                     return (cmsPluginMemHandler*) Plugin;
772             }
773     }
774 
775     // Nope, revert to defaults
776     return NULL;
777 }
778 
779 
780 // Creates a new context with optional associated plug-ins. Caller may also specify an optional pointer to user-defined
781 // data that will be forwarded to plug-ins and logger.
cmsCreateContext(void * Plugin,void * UserData)782 cmsContext CMSEXPORT cmsCreateContext(void* Plugin, void* UserData)
783 {
784     struct _cmsContext_struct* ctx;
785     struct _cmsContext_struct  fakeContext;
786 
787     // See the comments regarding locking in lcms2_internal.h
788     // for an explanation of why we need the following code.
789 #ifndef CMS_NO_PTHREADS
790 #ifdef CMS_IS_WINDOWS_
791 #ifndef CMS_RELY_ON_WINDOWS_STATIC_MUTEX_INIT
792     {
793         static HANDLE _cmsWindowsInitMutex = NULL;
794         static volatile HANDLE* mutex = &_cmsWindowsInitMutex;
795 
796         if (*mutex == NULL)
797         {
798             HANDLE p = CreateMutex(NULL, FALSE, NULL);
799             if (p && InterlockedCompareExchangePointer((void **)mutex, (void*)p, NULL) != NULL)
800                 CloseHandle(p);
801         }
802         if (*mutex == NULL || WaitForSingleObject(*mutex, INFINITE) == WAIT_FAILED)
803             return NULL;
804         if (((void **)&_cmsContextPoolHeadMutex)[0] == NULL)
805             InitializeCriticalSection(&_cmsContextPoolHeadMutex);
806         if (*mutex == NULL || !ReleaseMutex(*mutex))
807             return NULL;
808     }
809 #endif
810 #endif
811 #endif
812 
813     _cmsInstallAllocFunctions(_cmsFindMemoryPlugin(Plugin), &fakeContext.DefaultMemoryManager);
814 
815     fakeContext.chunks[UserPtr]     = UserData;
816     fakeContext.chunks[MemPlugin]   = &fakeContext.DefaultMemoryManager;
817 
818     // Create the context structure.
819     ctx = (struct _cmsContext_struct*) _cmsMalloc(&fakeContext, sizeof(struct _cmsContext_struct));
820     if (ctx == NULL)
821         return NULL;     // Something very wrong happened!
822 
823     // Init the structure and the memory manager
824     memset(ctx, 0, sizeof(struct _cmsContext_struct));
825 
826     // Keep memory manager
827     memcpy(&ctx->DefaultMemoryManager, &fakeContext.DefaultMemoryManager, sizeof(_cmsMemPluginChunk));
828 
829     // Maintain the linked list (with proper locking)
830     _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
831        ctx ->Next = _cmsContextPoolHead;
832        _cmsContextPoolHead = ctx;
833     _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
834 
835     ctx ->chunks[UserPtr]     = UserData;
836     ctx ->chunks[MemPlugin]   = &ctx->DefaultMemoryManager;
837 
838     // Now we can allocate the pool by using default memory manager
839     ctx ->MemPool = _cmsCreateSubAlloc(ctx, 22 * sizeof(void*));  // default size about 22 pointers
840     if (ctx ->MemPool == NULL) {
841 
842          cmsDeleteContext(ctx);
843         return NULL;
844     }
845 
846     _cmsAllocLogErrorChunk(ctx, NULL);
847     _cmsAllocAlarmCodesChunk(ctx, NULL);
848     _cmsAllocAdaptationStateChunk(ctx, NULL);
849     _cmsAllocMemPluginChunk(ctx, NULL);
850     _cmsAllocInterpPluginChunk(ctx, NULL);
851     _cmsAllocCurvesPluginChunk(ctx, NULL);
852     _cmsAllocFormattersPluginChunk(ctx, NULL);
853     _cmsAllocTagTypePluginChunk(ctx, NULL);
854     _cmsAllocMPETypePluginChunk(ctx, NULL);
855     _cmsAllocTagPluginChunk(ctx, NULL);
856     _cmsAllocIntentsPluginChunk(ctx, NULL);
857     _cmsAllocOptimizationPluginChunk(ctx, NULL);
858     _cmsAllocTransformPluginChunk(ctx, NULL);
859     _cmsAllocMutexPluginChunk(ctx, NULL);
860 
861     // Setup the plug-ins
862     if (!cmsPluginTHR(ctx, Plugin)) {
863 
864         cmsDeleteContext(ctx);
865         return NULL;
866     }
867 
868     return (cmsContext) ctx;
869 }
870 
871 // Duplicates a context with all associated plug-ins.
872 // Caller may specify an optional pointer to user-defined
873 // data that will be forwarded to plug-ins and logger.
cmsDupContext(cmsContext ContextID,void * NewUserData)874 cmsContext CMSEXPORT cmsDupContext(cmsContext ContextID, void* NewUserData)
875 {
876     int i;
877     struct _cmsContext_struct* ctx;
878     const struct _cmsContext_struct* src = _cmsGetContext(ContextID);
879 
880     void* userData = (NewUserData != NULL) ? NewUserData : src -> chunks[UserPtr];
881 
882 
883     ctx = (struct _cmsContext_struct*) _cmsMalloc(ContextID, sizeof(struct _cmsContext_struct));
884     if (ctx == NULL)
885         return NULL;     // Something very wrong happened
886 
887     // Setup default memory allocators
888     memcpy(&ctx->DefaultMemoryManager, &src->DefaultMemoryManager, sizeof(ctx->DefaultMemoryManager));
889 
890     // Maintain the linked list
891     _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
892        ctx ->Next = _cmsContextPoolHead;
893        _cmsContextPoolHead = ctx;
894     _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
895 
896     ctx ->chunks[UserPtr]    = userData;
897     ctx ->chunks[MemPlugin]  = &ctx->DefaultMemoryManager;
898 
899     ctx ->MemPool = _cmsCreateSubAlloc(ctx, 22 * sizeof(void*));
900     if (ctx ->MemPool == NULL) {
901 
902          cmsDeleteContext(ctx);
903         return NULL;
904     }
905 
906     // Allocate all required chunks.
907     _cmsAllocLogErrorChunk(ctx, src);
908     _cmsAllocAlarmCodesChunk(ctx, src);
909     _cmsAllocAdaptationStateChunk(ctx, src);
910     _cmsAllocMemPluginChunk(ctx, src);
911     _cmsAllocInterpPluginChunk(ctx, src);
912     _cmsAllocCurvesPluginChunk(ctx, src);
913     _cmsAllocFormattersPluginChunk(ctx, src);
914     _cmsAllocTagTypePluginChunk(ctx, src);
915     _cmsAllocMPETypePluginChunk(ctx, src);
916     _cmsAllocTagPluginChunk(ctx, src);
917     _cmsAllocIntentsPluginChunk(ctx, src);
918     _cmsAllocOptimizationPluginChunk(ctx, src);
919     _cmsAllocTransformPluginChunk(ctx, src);
920     _cmsAllocMutexPluginChunk(ctx, src);
921 
922     // Make sure no one failed
923     for (i=Logger; i < MemoryClientMax; i++) {
924 
925         if (src ->chunks[i] == NULL) {
926             cmsDeleteContext((cmsContext) ctx);
927             return NULL;
928         }
929     }
930 
931     return (cmsContext) ctx;
932 }
933 
934 
935 // Frees any resources associated with the given context,
936 // and destroys the context placeholder.
937 // The ContextID can no longer be used in any THR operation.
cmsDeleteContext(cmsContext ContextID)938 void CMSEXPORT cmsDeleteContext(cmsContext ContextID)
939 {
940     if (ContextID != NULL) {
941 
942         struct _cmsContext_struct* ctx = (struct _cmsContext_struct*) ContextID;
943         struct _cmsContext_struct  fakeContext;
944         struct _cmsContext_struct* prev;
945 
946         memcpy(&fakeContext.DefaultMemoryManager, &ctx->DefaultMemoryManager, sizeof(ctx->DefaultMemoryManager));
947 
948         fakeContext.chunks[UserPtr]     = ctx ->chunks[UserPtr];
949         fakeContext.chunks[MemPlugin]   = &fakeContext.DefaultMemoryManager;
950 
951         // Get rid of plugins
952         cmsUnregisterPluginsTHR(ContextID);
953 
954         // Since all memory is allocated in the private pool, all what we need to do is destroy the pool
955         if (ctx -> MemPool != NULL)
956               _cmsSubAllocDestroy(ctx ->MemPool);
957         ctx -> MemPool = NULL;
958 
959         // Maintain list
960         _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
961         if (_cmsContextPoolHead == ctx) {
962 
963             _cmsContextPoolHead = ctx->Next;
964         }
965         else {
966 
967             // Search for previous
968             for (prev = _cmsContextPoolHead;
969                  prev != NULL;
970                  prev = prev ->Next)
971             {
972                 if (prev -> Next == ctx) {
973                     prev -> Next = ctx ->Next;
974                     break;
975                 }
976             }
977         }
978         _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
979 
980         // free the memory block itself
981         _cmsFree(&fakeContext, ctx);
982     }
983 }
984 
985 // Returns the user data associated to the given ContextID, or NULL if no user data was attached on context creation
cmsGetContextUserData(cmsContext ContextID)986 void* CMSEXPORT cmsGetContextUserData(cmsContext ContextID)
987 {
988     return _cmsContextGetClientChunk(ContextID, UserPtr);
989 }
990 
991 
992