Coverage Report

Created: 2025-07-18 06:06

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