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