Coverage Report

Created: 2025-11-07 06:58

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