blob: 3876506dacdf1f0c06eded832abcb9cd7a971df5 [file] [log] [blame]
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -07001//---------------------------------------------------------------------------------
2//
3// Little Color Management System
kumarashishg826308d2023-06-23 13:21:22 +00004// Copyright (c) 1998-2023 Marti Maria Saguer
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -07005//
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
kumarashishg826308d2023-06-23 13:21:22 +000036// Adjust a word value after being read/ before being written from/to an ICC profile
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -070037cmsUInt16Number 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
kumarashishg826308d2023-06-23 13:21:22 +0000102 (*Result)[0] = (*QWord)[0];
103 (*Result)[1] = (*QWord)[1];
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -0700104# else
105 *Result = *QWord;
106# endif
107#endif
108}
109
110// Auxiliary -- 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{
kumarashishg826308d2023-06-23 13:21:22 +0000171 union typeConverter {
172 cmsUInt32Number integer;
173 cmsFloat32Number floating_point;
174 } tmp;
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -0700175
176 _cmsAssert(io != NULL);
177
kumarashishg826308d2023-06-23 13:21:22 +0000178 if (io->Read(io, &tmp.integer, sizeof(cmsUInt32Number), 1) != 1)
Haibo Huang49cc9302020-04-27 16:14:24 -0700179 return FALSE;
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -0700180
181 if (n != NULL) {
182
kumarashishg826308d2023-06-23 13:21:22 +0000183 tmp.integer = _cmsAdjustEndianess32(tmp.integer);
184 *n = tmp.floating_point;
185
Haibo Huang49cc9302020-04-27 16:14:24 -0700186 // Safeguard which covers against absurd values
187 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;
kumarashishg826308d2023-06-23 13:21:22 +0000193 #elif !defined(_MSC_VER) && (defined(__STDC_VERSION__) && __STDC_VERSION__ < 199901L)
194 return TRUE;
Haibo Huang49cc9302020-04-27 16:14:24 -0700195 #else
196
197 // fpclassify() required by C99 (only provided by MSVC >= 1800, VS2013 onwards)
198 return ((fpclassify(*n) == FP_ZERO) || (fpclassify(*n) == FP_NORMAL));
199 #endif
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -0700200 }
201
Haibo Huang49cc9302020-04-27 16:14:24 -0700202 return TRUE;
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -0700203}
204
205
206cmsBool CMSEXPORT _cmsReadUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n)
207{
208 cmsUInt64Number tmp;
209
210 _cmsAssert(io != NULL);
211
212 if (io -> Read(io, &tmp, sizeof(cmsUInt64Number), 1) != 1)
213 return FALSE;
214
Haibo Huang49cc9302020-04-27 16:14:24 -0700215 if (n != NULL) {
216
217 _cmsAdjustEndianess64(n, &tmp);
218 }
219
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -0700220 return TRUE;
221}
222
223
224cmsBool CMSEXPORT _cmsRead15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number* n)
225{
226 cmsUInt32Number tmp;
227
228 _cmsAssert(io != NULL);
229
230 if (io -> Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1)
231 return FALSE;
232
233 if (n != NULL) {
Haibo Huang49cc9302020-04-27 16:14:24 -0700234 *n = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32(tmp));
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -0700235 }
236
237 return TRUE;
238}
239
240
241cmsBool CMSEXPORT _cmsReadXYZNumber(cmsIOHANDLER* io, cmsCIEXYZ* XYZ)
242{
243 cmsEncodedXYZNumber xyz;
244
245 _cmsAssert(io != NULL);
246
247 if (io ->Read(io, &xyz, sizeof(cmsEncodedXYZNumber), 1) != 1) return FALSE;
248
249 if (XYZ != NULL) {
250
Haibo Huang49cc9302020-04-27 16:14:24 -0700251 XYZ->X = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.X));
252 XYZ->Y = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.Y));
253 XYZ->Z = _cms15Fixed16toDouble((cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) xyz.Z));
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -0700254 }
255 return TRUE;
256}
257
258cmsBool CMSEXPORT _cmsWriteUInt8Number(cmsIOHANDLER* io, cmsUInt8Number n)
259{
260 _cmsAssert(io != NULL);
261
262 if (io -> Write(io, sizeof(cmsUInt8Number), &n) != 1)
263 return FALSE;
264
265 return TRUE;
266}
267
268cmsBool CMSEXPORT _cmsWriteUInt16Number(cmsIOHANDLER* io, cmsUInt16Number n)
269{
270 cmsUInt16Number tmp;
271
272 _cmsAssert(io != NULL);
273
274 tmp = _cmsAdjustEndianess16(n);
275 if (io -> Write(io, sizeof(cmsUInt16Number), &tmp) != 1)
276 return FALSE;
277
278 return TRUE;
279}
280
281cmsBool CMSEXPORT _cmsWriteUInt16Array(cmsIOHANDLER* io, cmsUInt32Number n, const cmsUInt16Number* Array)
282{
283 cmsUInt32Number i;
284
285 _cmsAssert(io != NULL);
286 _cmsAssert(Array != NULL);
287
288 for (i=0; i < n; i++) {
289 if (!_cmsWriteUInt16Number(io, Array[i])) return FALSE;
290 }
291
292 return TRUE;
293}
294
295cmsBool CMSEXPORT _cmsWriteUInt32Number(cmsIOHANDLER* io, cmsUInt32Number n)
296{
297 cmsUInt32Number tmp;
298
299 _cmsAssert(io != NULL);
300
301 tmp = _cmsAdjustEndianess32(n);
302 if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1)
303 return FALSE;
304
305 return TRUE;
306}
307
308
309cmsBool CMSEXPORT _cmsWriteFloat32Number(cmsIOHANDLER* io, cmsFloat32Number n)
310{
kumarashishg826308d2023-06-23 13:21:22 +0000311 union typeConverter {
312 cmsUInt32Number integer;
313 cmsFloat32Number floating_point;
314 } tmp;
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -0700315
kumarashishg826308d2023-06-23 13:21:22 +0000316 tmp.floating_point = n;
317 tmp.integer = _cmsAdjustEndianess32(tmp.integer);
318 if (io -> Write(io, sizeof(cmsUInt32Number), &tmp.integer) != 1)
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -0700319 return FALSE;
320
321 return TRUE;
322}
323
324cmsBool CMSEXPORT _cmsWriteUInt64Number(cmsIOHANDLER* io, cmsUInt64Number* n)
325{
326 cmsUInt64Number tmp;
327
328 _cmsAssert(io != NULL);
329
330 _cmsAdjustEndianess64(&tmp, n);
331 if (io -> Write(io, sizeof(cmsUInt64Number), &tmp) != 1)
332 return FALSE;
333
334 return TRUE;
335}
336
337cmsBool CMSEXPORT _cmsWrite15Fixed16Number(cmsIOHANDLER* io, cmsFloat64Number n)
338{
339 cmsUInt32Number tmp;
340
341 _cmsAssert(io != NULL);
342
Haibo Huang49cc9302020-04-27 16:14:24 -0700343 tmp = _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(n));
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -0700344 if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1)
345 return FALSE;
346
347 return TRUE;
348}
349
350cmsBool CMSEXPORT _cmsWriteXYZNumber(cmsIOHANDLER* io, const cmsCIEXYZ* XYZ)
351{
352 cmsEncodedXYZNumber xyz;
353
354 _cmsAssert(io != NULL);
355 _cmsAssert(XYZ != NULL);
356
Haibo Huang49cc9302020-04-27 16:14:24 -0700357 xyz.X = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(XYZ->X));
358 xyz.Y = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(XYZ->Y));
359 xyz.Z = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(XYZ->Z));
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -0700360
361 return io -> Write(io, sizeof(cmsEncodedXYZNumber), &xyz);
362}
363
364// from Fixed point 8.8 to double
365cmsFloat64Number CMSEXPORT _cms8Fixed8toDouble(cmsUInt16Number fixed8)
366{
367 cmsUInt8Number msb, lsb;
368
369 lsb = (cmsUInt8Number) (fixed8 & 0xff);
370 msb = (cmsUInt8Number) (((cmsUInt16Number) fixed8 >> 8) & 0xff);
371
372 return (cmsFloat64Number) ((cmsFloat64Number) msb + ((cmsFloat64Number) lsb / 256.0));
373}
374
375cmsUInt16Number CMSEXPORT _cmsDoubleTo8Fixed8(cmsFloat64Number val)
376{
377 cmsS15Fixed16Number GammaFixed32 = _cmsDoubleTo15Fixed16(val);
378 return (cmsUInt16Number) ((GammaFixed32 >> 8) & 0xFFFF);
379}
380
381// from Fixed point 15.16 to double
382cmsFloat64Number CMSEXPORT _cms15Fixed16toDouble(cmsS15Fixed16Number fix32)
383{
384 cmsFloat64Number floater, sign, mid;
385 int Whole, FracPart;
386
387 sign = (fix32 < 0 ? -1 : 1);
388 fix32 = abs(fix32);
389
390 Whole = (cmsUInt16Number)(fix32 >> 16) & 0xffff;
391 FracPart = (cmsUInt16Number)(fix32 & 0xffff);
392
393 mid = (cmsFloat64Number) FracPart / 65536.0;
394 floater = (cmsFloat64Number) Whole + mid;
395
396 return sign * floater;
397}
398
399// from double to Fixed point 15.16
400cmsS15Fixed16Number CMSEXPORT _cmsDoubleTo15Fixed16(cmsFloat64Number v)
401{
402 return ((cmsS15Fixed16Number) floor((v)*65536.0 + 0.5));
403}
404
405// Date/Time functions
406
407void CMSEXPORT _cmsDecodeDateTimeNumber(const cmsDateTimeNumber *Source, struct tm *Dest)
408{
409
410 _cmsAssert(Dest != NULL);
411 _cmsAssert(Source != NULL);
412
413 Dest->tm_sec = _cmsAdjustEndianess16(Source->seconds);
414 Dest->tm_min = _cmsAdjustEndianess16(Source->minutes);
415 Dest->tm_hour = _cmsAdjustEndianess16(Source->hours);
416 Dest->tm_mday = _cmsAdjustEndianess16(Source->day);
417 Dest->tm_mon = _cmsAdjustEndianess16(Source->month) - 1;
418 Dest->tm_year = _cmsAdjustEndianess16(Source->year) - 1900;
419 Dest->tm_wday = -1;
420 Dest->tm_yday = -1;
421 Dest->tm_isdst = 0;
422}
423
424void CMSEXPORT _cmsEncodeDateTimeNumber(cmsDateTimeNumber *Dest, const struct tm *Source)
425{
426 _cmsAssert(Dest != NULL);
427 _cmsAssert(Source != NULL);
428
429 Dest->seconds = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_sec);
430 Dest->minutes = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_min);
431 Dest->hours = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_hour);
432 Dest->day = _cmsAdjustEndianess16((cmsUInt16Number) Source->tm_mday);
433 Dest->month = _cmsAdjustEndianess16((cmsUInt16Number) (Source->tm_mon + 1));
434 Dest->year = _cmsAdjustEndianess16((cmsUInt16Number) (Source->tm_year + 1900));
435}
436
437// Read base and return type base
438cmsTagTypeSignature CMSEXPORT _cmsReadTypeBase(cmsIOHANDLER* io)
439{
440 _cmsTagBase Base;
441
442 _cmsAssert(io != NULL);
443
444 if (io -> Read(io, &Base, sizeof(_cmsTagBase), 1) != 1)
445 return (cmsTagTypeSignature) 0;
446
447 return (cmsTagTypeSignature) _cmsAdjustEndianess32(Base.sig);
448}
449
450// Setup base marker
451cmsBool CMSEXPORT _cmsWriteTypeBase(cmsIOHANDLER* io, cmsTagTypeSignature sig)
452{
453 _cmsTagBase Base;
454
455 _cmsAssert(io != NULL);
456
457 Base.sig = (cmsTagTypeSignature) _cmsAdjustEndianess32(sig);
458 memset(&Base.reserved, 0, sizeof(Base.reserved));
459 return io -> Write(io, sizeof(_cmsTagBase), &Base);
460}
461
462cmsBool CMSEXPORT _cmsReadAlignment(cmsIOHANDLER* io)
463{
464 cmsUInt8Number Buffer[4];
465 cmsUInt32Number NextAligned, At;
466 cmsUInt32Number BytesToNextAlignedPos;
467
468 _cmsAssert(io != NULL);
469
470 At = io -> Tell(io);
471 NextAligned = _cmsALIGNLONG(At);
472 BytesToNextAlignedPos = NextAligned - At;
473 if (BytesToNextAlignedPos == 0) return TRUE;
474 if (BytesToNextAlignedPos > 4) return FALSE;
475
476 return (io ->Read(io, Buffer, BytesToNextAlignedPos, 1) == 1);
477}
478
479cmsBool CMSEXPORT _cmsWriteAlignment(cmsIOHANDLER* io)
480{
481 cmsUInt8Number Buffer[4];
482 cmsUInt32Number NextAligned, At;
483 cmsUInt32Number BytesToNextAlignedPos;
484
485 _cmsAssert(io != NULL);
486
487 At = io -> Tell(io);
488 NextAligned = _cmsALIGNLONG(At);
489 BytesToNextAlignedPos = NextAligned - At;
490 if (BytesToNextAlignedPos == 0) return TRUE;
491 if (BytesToNextAlignedPos > 4) return FALSE;
492
493 memset(Buffer, 0, BytesToNextAlignedPos);
494 return io -> Write(io, BytesToNextAlignedPos, Buffer);
495}
496
497
498// To deal with text streams. 2K at most
499cmsBool CMSEXPORT _cmsIOPrintf(cmsIOHANDLER* io, const char* frm, ...)
500{
501 va_list args;
502 int len;
503 cmsUInt8Number Buffer[2048];
504 cmsBool rc;
kumarashishg826308d2023-06-23 13:21:22 +0000505 cmsUInt8Number* ptr;
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -0700506
507 _cmsAssert(io != NULL);
508 _cmsAssert(frm != NULL);
509
510 va_start(args, frm);
511
512 len = vsnprintf((char*) Buffer, 2047, frm, args);
513 if (len < 0) {
514 va_end(args);
515 return FALSE; // Truncated, which is a fatal error for us
516 }
517
kumarashishg826308d2023-06-23 13:21:22 +0000518 // setlocale may be active, no commas are needed in PS generator
519 // and PS generator is our only client
520 for (ptr = Buffer; *ptr; ptr++)
521 {
522 if (*ptr == ',') *ptr = '.';
523 }
524
Haibo Huang49cc9302020-04-27 16:14:24 -0700525 rc = io ->Write(io, (cmsUInt32Number) len, Buffer);
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -0700526
527 va_end(args);
528
529 return rc;
530}
531
532
533// Plugin memory management -------------------------------------------------------------------------------------------------
534
535// Specialized malloc for plug-ins, that is freed upon exit.
536void* _cmsPluginMalloc(cmsContext ContextID, cmsUInt32Number size)
537{
538 struct _cmsContext_struct* ctx = _cmsGetContext(ContextID);
539
540 if (ctx ->MemPool == NULL) {
541
542 if (ContextID == NULL) {
543
544 ctx->MemPool = _cmsCreateSubAlloc(0, 2*1024);
545 if (ctx->MemPool == NULL) return NULL;
546 }
547 else {
548 cmsSignalError(ContextID, cmsERROR_CORRUPTION_DETECTED, "NULL memory pool on context");
549 return NULL;
550 }
551 }
552
553 return _cmsSubAlloc(ctx->MemPool, size);
554}
555
556
557// Main plug-in dispatcher
558cmsBool CMSEXPORT cmsPlugin(void* Plug_in)
559{
560 return cmsPluginTHR(NULL, Plug_in);
561}
562
563cmsBool CMSEXPORT cmsPluginTHR(cmsContext id, void* Plug_in)
564{
565 cmsPluginBase* Plugin;
566
567 for (Plugin = (cmsPluginBase*) Plug_in;
568 Plugin != NULL;
569 Plugin = Plugin -> Next) {
570
571 if (Plugin -> Magic != cmsPluginMagicNumber) {
572 cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin");
573 return FALSE;
574 }
575
576 if (Plugin ->ExpectedVersion > LCMS_VERSION) {
577 cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "plugin needs Little CMS %d, current version is %d",
578 Plugin ->ExpectedVersion, LCMS_VERSION);
579 return FALSE;
580 }
581
582 switch (Plugin -> Type) {
583
584 case cmsPluginMemHandlerSig:
585 if (!_cmsRegisterMemHandlerPlugin(id, Plugin)) return FALSE;
586 break;
587
588 case cmsPluginInterpolationSig:
589 if (!_cmsRegisterInterpPlugin(id, Plugin)) return FALSE;
590 break;
591
592 case cmsPluginTagTypeSig:
593 if (!_cmsRegisterTagTypePlugin(id, Plugin)) return FALSE;
594 break;
595
596 case cmsPluginTagSig:
597 if (!_cmsRegisterTagPlugin(id, Plugin)) return FALSE;
598 break;
599
600 case cmsPluginFormattersSig:
601 if (!_cmsRegisterFormattersPlugin(id, Plugin)) return FALSE;
602 break;
603
604 case cmsPluginRenderingIntentSig:
605 if (!_cmsRegisterRenderingIntentPlugin(id, Plugin)) return FALSE;
606 break;
607
608 case cmsPluginParametricCurveSig:
609 if (!_cmsRegisterParametricCurvesPlugin(id, Plugin)) return FALSE;
610 break;
611
612 case cmsPluginMultiProcessElementSig:
613 if (!_cmsRegisterMultiProcessElementPlugin(id, Plugin)) return FALSE;
614 break;
615
616 case cmsPluginOptimizationSig:
617 if (!_cmsRegisterOptimizationPlugin(id, Plugin)) return FALSE;
618 break;
619
620 case cmsPluginTransformSig:
621 if (!_cmsRegisterTransformPlugin(id, Plugin)) return FALSE;
622 break;
623
624 case cmsPluginMutexSig:
625 if (!_cmsRegisterMutexPlugin(id, Plugin)) return FALSE;
626 break;
627
kumarashishg826308d2023-06-23 13:21:22 +0000628 case cmsPluginParalellizationSig:
629 if (!_cmsRegisterParallelizationPlugin(id, Plugin)) return FALSE;
630 break;
631
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -0700632 default:
633 cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin type '%X'", Plugin -> Type);
634 return FALSE;
635 }
636 }
637
638 // Keep a reference to the plug-in
639 return TRUE;
640}
641
642
643// Revert all plug-ins to default
644void CMSEXPORT cmsUnregisterPlugins(void)
645{
646 cmsUnregisterPluginsTHR(NULL);
647}
648
649
650// The Global storage for system context. This is the one and only global variable
651// pointers structure. All global vars are referenced here.
652static struct _cmsContext_struct globalContext = {
653
kumarashishg826308d2023-06-23 13:21:22 +0000654 NULL, // Not in the linked list
655 NULL, // No suballocator
656 {
657 NULL, // UserPtr,
658 &_cmsLogErrorChunk, // Logger,
659 &_cmsAlarmCodesChunk, // AlarmCodes,
660 &_cmsAdaptationStateChunk, // AdaptationState,
661 &_cmsMemPluginChunk, // MemPlugin,
662 &_cmsInterpPluginChunk, // InterpPlugin,
663 &_cmsCurvesPluginChunk, // CurvesPlugin,
664 &_cmsFormattersPluginChunk, // FormattersPlugin,
665 &_cmsTagTypePluginChunk, // TagTypePlugin,
666 &_cmsTagPluginChunk, // TagPlugin,
667 &_cmsIntentsPluginChunk, // IntentPlugin,
668 &_cmsMPETypePluginChunk, // MPEPlugin,
669 &_cmsOptimizationPluginChunk, // OptimizationPlugin,
670 &_cmsTransformPluginChunk, // TransformPlugin,
671 &_cmsMutexPluginChunk, // MutexPlugin,
672 &_cmsParallelizationPluginChunk // ParallelizationPlugin
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -0700673 },
674
675 { NULL, NULL, NULL, NULL, NULL, NULL } // The default memory allocator is not used for context 0
676};
677
678
679// The context pool (linked list head)
680static _cmsMutex _cmsContextPoolHeadMutex = CMS_MUTEX_INITIALIZER;
681static struct _cmsContext_struct* _cmsContextPoolHead = NULL;
682
kumarashishg826308d2023-06-23 13:21:22 +0000683
684// Make sure context is initialized (needed on windows)
685static
686cmsBool InitContextMutex(void)
687{
688 // See the comments regarding locking in lcms2_internal.h
689 // for an explanation of why we need the following code.
690#ifndef CMS_NO_PTHREADS
691#ifdef CMS_IS_WINDOWS_
692#ifndef CMS_RELY_ON_WINDOWS_STATIC_MUTEX_INIT
693
694 static cmsBool already_initialized = FALSE;
695
696 if (!already_initialized)
697 {
698 static HANDLE _cmsWindowsInitMutex = NULL;
699 static volatile HANDLE* mutex = &_cmsWindowsInitMutex;
700
701 if (*mutex == NULL)
702 {
703 HANDLE p = CreateMutex(NULL, FALSE, NULL);
704 if (p && InterlockedCompareExchangePointer((void**)mutex, (void*)p, NULL) != NULL)
705 CloseHandle(p);
706 }
707 if (*mutex == NULL || WaitForSingleObject(*mutex, INFINITE) == WAIT_FAILED)
708 {
709 cmsSignalError(0, cmsERROR_INTERNAL, "Mutex lock failed");
710 return FALSE;
711 }
712 if (((void**)&_cmsContextPoolHeadMutex)[0] == NULL)
713 InitializeCriticalSection(&_cmsContextPoolHeadMutex);
714 if (*mutex == NULL || !ReleaseMutex(*mutex))
715 {
716 cmsSignalError(0, cmsERROR_INTERNAL, "Mutex unlock failed");
717 return FALSE;
718 }
719 already_initialized = TRUE;
720 }
721#endif
722#endif
723#endif
724
725 return TRUE;
726}
727
728
729
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -0700730// Internal, get associated pointer, with guessing. Never returns NULL.
731struct _cmsContext_struct* _cmsGetContext(cmsContext ContextID)
732{
733 struct _cmsContext_struct* id = (struct _cmsContext_struct*) ContextID;
734 struct _cmsContext_struct* ctx;
735
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -0700736 // On 0, use global settings
737 if (id == NULL)
738 return &globalContext;
739
kumarashishg826308d2023-06-23 13:21:22 +0000740 InitContextMutex();
741
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -0700742 // Search
kumarashishg826308d2023-06-23 13:21:22 +0000743 _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
744
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -0700745 for (ctx = _cmsContextPoolHead;
746 ctx != NULL;
747 ctx = ctx ->Next) {
748
749 // Found it?
kumarashishg826308d2023-06-23 13:21:22 +0000750 if (id == ctx)
751 {
752 _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
753 return ctx; // New-style context
754 }
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -0700755 }
756
kumarashishg826308d2023-06-23 13:21:22 +0000757 _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -0700758 return &globalContext;
759}
760
761
762// Internal: get the memory area associanted with each context client
763// Returns the block assigned to the specific zone. Never return NULL.
764void* _cmsContextGetClientChunk(cmsContext ContextID, _cmsMemoryClient mc)
765{
766 struct _cmsContext_struct* ctx;
767 void *ptr;
768
769 if ((int) mc < 0 || mc >= MemoryClientMax) {
770
771 cmsSignalError(ContextID, cmsERROR_INTERNAL, "Bad context client -- possible corruption");
772
773 // This is catastrophic. Should never reach here
774 _cmsAssert(0);
775
776 // Reverts to global context
777 return globalContext.chunks[UserPtr];
778 }
779
780 ctx = _cmsGetContext(ContextID);
781 ptr = ctx ->chunks[mc];
782
783 if (ptr != NULL)
784 return ptr;
785
786 // A null ptr means no special settings for that context, and this
787 // reverts to Context0 globals
788 return globalContext.chunks[mc];
789}
790
791
792// This function returns the given context its default pristine state,
793// as no plug-ins were declared. There is no way to unregister a single
794// plug-in, as a single call to cmsPluginTHR() function may register
795// many different plug-ins simultaneously, then there is no way to
796// identify which plug-in to unregister.
797void CMSEXPORT cmsUnregisterPluginsTHR(cmsContext ContextID)
kumarashishg826308d2023-06-23 13:21:22 +0000798{
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -0700799 _cmsRegisterMemHandlerPlugin(ContextID, NULL);
800 _cmsRegisterInterpPlugin(ContextID, NULL);
801 _cmsRegisterTagTypePlugin(ContextID, NULL);
802 _cmsRegisterTagPlugin(ContextID, NULL);
803 _cmsRegisterFormattersPlugin(ContextID, NULL);
804 _cmsRegisterRenderingIntentPlugin(ContextID, NULL);
805 _cmsRegisterParametricCurvesPlugin(ContextID, NULL);
806 _cmsRegisterMultiProcessElementPlugin(ContextID, NULL);
807 _cmsRegisterOptimizationPlugin(ContextID, NULL);
808 _cmsRegisterTransformPlugin(ContextID, NULL);
809 _cmsRegisterMutexPlugin(ContextID, NULL);
kumarashishg826308d2023-06-23 13:21:22 +0000810 _cmsRegisterParallelizationPlugin(ContextID, NULL);
811
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -0700812}
813
814
815// Returns the memory manager plug-in, if any, from the Plug-in bundle
816static
817cmsPluginMemHandler* _cmsFindMemoryPlugin(void* PluginBundle)
818{
819 cmsPluginBase* Plugin;
820
821 for (Plugin = (cmsPluginBase*) PluginBundle;
822 Plugin != NULL;
823 Plugin = Plugin -> Next) {
824
825 if (Plugin -> Magic == cmsPluginMagicNumber &&
826 Plugin -> ExpectedVersion <= LCMS_VERSION &&
827 Plugin -> Type == cmsPluginMemHandlerSig) {
828
829 // Found!
830 return (cmsPluginMemHandler*) Plugin;
831 }
832 }
833
834 // Nope, revert to defaults
835 return NULL;
836}
837
838
839// Creates a new context with optional associated plug-ins. Caller may also specify an optional pointer to user-defined
840// data that will be forwarded to plug-ins and logger.
841cmsContext CMSEXPORT cmsCreateContext(void* Plugin, void* UserData)
842{
843 struct _cmsContext_struct* ctx;
844 struct _cmsContext_struct fakeContext;
845
kumarashishg826308d2023-06-23 13:21:22 +0000846 if (!InitContextMutex()) return NULL;
Haibo Huang49cc9302020-04-27 16:14:24 -0700847
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -0700848 _cmsInstallAllocFunctions(_cmsFindMemoryPlugin(Plugin), &fakeContext.DefaultMemoryManager);
849
850 fakeContext.chunks[UserPtr] = UserData;
851 fakeContext.chunks[MemPlugin] = &fakeContext.DefaultMemoryManager;
852
853 // Create the context structure.
854 ctx = (struct _cmsContext_struct*) _cmsMalloc(&fakeContext, sizeof(struct _cmsContext_struct));
855 if (ctx == NULL)
856 return NULL; // Something very wrong happened!
857
858 // Init the structure and the memory manager
859 memset(ctx, 0, sizeof(struct _cmsContext_struct));
860
861 // Keep memory manager
862 memcpy(&ctx->DefaultMemoryManager, &fakeContext.DefaultMemoryManager, sizeof(_cmsMemPluginChunk));
863
864 // Maintain the linked list (with proper locking)
865 _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
866 ctx ->Next = _cmsContextPoolHead;
867 _cmsContextPoolHead = ctx;
868 _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
869
870 ctx ->chunks[UserPtr] = UserData;
871 ctx ->chunks[MemPlugin] = &ctx->DefaultMemoryManager;
872
873 // Now we can allocate the pool by using default memory manager
Haibo Huang49cc9302020-04-27 16:14:24 -0700874 ctx ->MemPool = _cmsCreateSubAlloc(ctx, 22 * sizeof(void*)); // default size about 22 pointers
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -0700875 if (ctx ->MemPool == NULL) {
876
877 cmsDeleteContext(ctx);
878 return NULL;
879 }
880
881 _cmsAllocLogErrorChunk(ctx, NULL);
882 _cmsAllocAlarmCodesChunk(ctx, NULL);
883 _cmsAllocAdaptationStateChunk(ctx, NULL);
884 _cmsAllocMemPluginChunk(ctx, NULL);
885 _cmsAllocInterpPluginChunk(ctx, NULL);
886 _cmsAllocCurvesPluginChunk(ctx, NULL);
887 _cmsAllocFormattersPluginChunk(ctx, NULL);
888 _cmsAllocTagTypePluginChunk(ctx, NULL);
889 _cmsAllocMPETypePluginChunk(ctx, NULL);
890 _cmsAllocTagPluginChunk(ctx, NULL);
891 _cmsAllocIntentsPluginChunk(ctx, NULL);
892 _cmsAllocOptimizationPluginChunk(ctx, NULL);
893 _cmsAllocTransformPluginChunk(ctx, NULL);
894 _cmsAllocMutexPluginChunk(ctx, NULL);
kumarashishg826308d2023-06-23 13:21:22 +0000895 _cmsAllocParallelizationPluginChunk(ctx, NULL);
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -0700896
897 // Setup the plug-ins
898 if (!cmsPluginTHR(ctx, Plugin)) {
899
900 cmsDeleteContext(ctx);
901 return NULL;
902 }
903
904 return (cmsContext) ctx;
905}
906
907// Duplicates a context with all associated plug-ins.
908// Caller may specify an optional pointer to user-defined
909// data that will be forwarded to plug-ins and logger.
910cmsContext CMSEXPORT cmsDupContext(cmsContext ContextID, void* NewUserData)
911{
912 int i;
913 struct _cmsContext_struct* ctx;
914 const struct _cmsContext_struct* src = _cmsGetContext(ContextID);
915
916 void* userData = (NewUserData != NULL) ? NewUserData : src -> chunks[UserPtr];
917
918
919 ctx = (struct _cmsContext_struct*) _cmsMalloc(ContextID, sizeof(struct _cmsContext_struct));
920 if (ctx == NULL)
921 return NULL; // Something very wrong happened
922
kumarashishg826308d2023-06-23 13:21:22 +0000923 if (!InitContextMutex()) return NULL;
924
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -0700925 // Setup default memory allocators
926 memcpy(&ctx->DefaultMemoryManager, &src->DefaultMemoryManager, sizeof(ctx->DefaultMemoryManager));
927
928 // Maintain the linked list
929 _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
930 ctx ->Next = _cmsContextPoolHead;
931 _cmsContextPoolHead = ctx;
932 _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
933
934 ctx ->chunks[UserPtr] = userData;
935 ctx ->chunks[MemPlugin] = &ctx->DefaultMemoryManager;
936
937 ctx ->MemPool = _cmsCreateSubAlloc(ctx, 22 * sizeof(void*));
938 if (ctx ->MemPool == NULL) {
939
940 cmsDeleteContext(ctx);
941 return NULL;
942 }
943
944 // Allocate all required chunks.
945 _cmsAllocLogErrorChunk(ctx, src);
946 _cmsAllocAlarmCodesChunk(ctx, src);
947 _cmsAllocAdaptationStateChunk(ctx, src);
948 _cmsAllocMemPluginChunk(ctx, src);
949 _cmsAllocInterpPluginChunk(ctx, src);
950 _cmsAllocCurvesPluginChunk(ctx, src);
951 _cmsAllocFormattersPluginChunk(ctx, src);
952 _cmsAllocTagTypePluginChunk(ctx, src);
953 _cmsAllocMPETypePluginChunk(ctx, src);
954 _cmsAllocTagPluginChunk(ctx, src);
955 _cmsAllocIntentsPluginChunk(ctx, src);
956 _cmsAllocOptimizationPluginChunk(ctx, src);
957 _cmsAllocTransformPluginChunk(ctx, src);
958 _cmsAllocMutexPluginChunk(ctx, src);
kumarashishg826308d2023-06-23 13:21:22 +0000959 _cmsAllocParallelizationPluginChunk(ctx, src);
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -0700960
961 // Make sure no one failed
962 for (i=Logger; i < MemoryClientMax; i++) {
963
964 if (src ->chunks[i] == NULL) {
965 cmsDeleteContext((cmsContext) ctx);
966 return NULL;
967 }
968 }
969
970 return (cmsContext) ctx;
971}
972
973
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -0700974// Frees any resources associated with the given context,
975// and destroys the context placeholder.
976// The ContextID can no longer be used in any THR operation.
977void CMSEXPORT cmsDeleteContext(cmsContext ContextID)
978{
kumarashishg826308d2023-06-23 13:21:22 +0000979 if (ContextID == NULL) {
980
981 cmsUnregisterPlugins();
982 if (globalContext.MemPool != NULL)
983 _cmsSubAllocDestroy(globalContext.MemPool);
984 globalContext.MemPool = NULL;
985 }
986 else {
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -0700987
988 struct _cmsContext_struct* ctx = (struct _cmsContext_struct*) ContextID;
989 struct _cmsContext_struct fakeContext;
990 struct _cmsContext_struct* prev;
991
kumarashishg826308d2023-06-23 13:21:22 +0000992
993 InitContextMutex();
994
Philip P. Moltmannd904c1e2018-03-19 09:26:45 -0700995 memcpy(&fakeContext.DefaultMemoryManager, &ctx->DefaultMemoryManager, sizeof(ctx->DefaultMemoryManager));
996
997 fakeContext.chunks[UserPtr] = ctx ->chunks[UserPtr];
998 fakeContext.chunks[MemPlugin] = &fakeContext.DefaultMemoryManager;
999
1000 // Get rid of plugins
1001 cmsUnregisterPluginsTHR(ContextID);
1002
1003 // Since all memory is allocated in the private pool, all what we need to do is destroy the pool
1004 if (ctx -> MemPool != NULL)
1005 _cmsSubAllocDestroy(ctx ->MemPool);
1006 ctx -> MemPool = NULL;
1007
1008 // Maintain list
1009 _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
1010 if (_cmsContextPoolHead == ctx) {
1011
1012 _cmsContextPoolHead = ctx->Next;
1013 }
1014 else {
1015
1016 // Search for previous
1017 for (prev = _cmsContextPoolHead;
1018 prev != NULL;
1019 prev = prev ->Next)
1020 {
1021 if (prev -> Next == ctx) {
1022 prev -> Next = ctx ->Next;
1023 break;
1024 }
1025 }
1026 }
1027 _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
1028
1029 // free the memory block itself
1030 _cmsFree(&fakeContext, ctx);
1031 }
1032}
1033
1034// Returns the user data associated to the given ContextID, or NULL if no user data was attached on context creation
1035void* CMSEXPORT cmsGetContextUserData(cmsContext ContextID)
1036{
1037 return _cmsContextGetClientChunk(ContextID, UserPtr);
1038}
1039
1040
kumarashishg826308d2023-06-23 13:21:22 +00001041// Use context mutex to provide thread-safe time
1042cmsBool _cmsGetTime(struct tm* ptr_time)
1043{
1044 struct tm* t;
1045#if defined(HAVE_GMTIME_R) || defined(HAVE_GMTIME_S)
1046 struct tm tm;
1047#endif
1048
1049 time_t now = time(NULL);
1050
1051#ifdef HAVE_GMTIME_R
1052 t = gmtime_r(&now, &tm);
1053#elif defined(HAVE_GMTIME_S)
1054 t = gmtime_s(&tm, &now) == 0 ? &tm : NULL;
1055#else
1056 if (!InitContextMutex()) return FALSE;
1057
1058 _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
1059 t = gmtime(&now);
1060 _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex);
1061#endif
1062
1063 if (t == NULL)
1064 return FALSE;
1065 else {
1066 *ptr_time = *t;
1067 return TRUE;
1068 }
1069}