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