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
37cmsUInt16Number 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
58cmsUInt32Number 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
78void 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
110cmsBool 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
123cmsBool 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
136cmsBool 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
155cmsBool 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
168cmsBool 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
202cmsBool 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
220cmsBool 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
237cmsBool 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
254cmsBool 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
264cmsBool 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
277cmsBool 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
291cmsBool 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
305cmsBool 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
319cmsBool 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
332cmsBool 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
345cmsBool 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
360cmsFloat64Number 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
371cmsUInt16Number 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
378cmsFloat64Number 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
397cmsS15Fixed16Number 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
405void 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
423void 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
439cmsTagTypeSignature 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
452cmsBool 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
463cmsBool 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
480cmsBool 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
500cmsBool 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.
529void* _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
551cmsBool 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.
636static 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)
663static _cmsMutex _cmsContextPoolHeadMutex = CMS_MUTEX_INITIALIZER;
664static struct _cmsContext_struct* _cmsContextPoolHead = NULL;
665
666// Internal, get associated pointer, with guessing. Never returns NULL.
667struct _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.
693void* _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.
726void 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
743static
744cmsPluginMemHandler* _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.
768cmsContext 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.
858cmsContext 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/*
920static
921struct _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.
941void 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
989void* CMSEXPORT cmsGetContextUserData(cmsContext ContextID)
990{
991 return _cmsContextGetClientChunk(ContextID, UserPtr);
992}
993
994cmsUInt32Number _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