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