lcms2: Import upstream release 2.14.
[wine.git] / libs / lcms2 / src / cmstypes.c
blob2d00d9c50b73183df7e92dd5380fee3e6c873b81
1 //---------------------------------------------------------------------------------
2 //
3 // Little Color Management System
4 // Copyright (c) 1998-2022 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:
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
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.
24 //---------------------------------------------------------------------------------
27 #include "lcms2_internal.h"
29 // Tag Serialization -----------------------------------------------------------------------------
30 // This file implements every single tag and tag type as described in the ICC spec. Some types
31 // have been deprecated, like ncl and Data. There is no implementation for those types as there
32 // are no profiles holding them. The programmer can also extend this list by defining his own types
33 // by using the appropriate plug-in. There are three types of plug ins regarding that. First type
34 // allows to define new tags using any existing type. Next plug-in type allows to define new types
35 // and the third one is very specific: allows to extend the number of elements in the multiprocessing
36 // elements special type.
37 //--------------------------------------------------------------------------------------------------
39 // Some broken types
40 #define cmsCorbisBrokenXYZtype ((cmsTagTypeSignature) 0x17A505B8)
41 #define cmsMonacoBrokenCurveType ((cmsTagTypeSignature) 0x9478ee00)
43 // This is the linked list that keeps track of the defined types
44 typedef struct _cmsTagTypeLinkedList_st {
46 cmsTagTypeHandler Handler;
47 struct _cmsTagTypeLinkedList_st* Next;
49 } _cmsTagTypeLinkedList;
51 // Some macros to define callbacks.
52 #define READ_FN(x) Type_##x##_Read
53 #define WRITE_FN(x) Type_##x##_Write
54 #define FREE_FN(x) Type_##x##_Free
55 #define DUP_FN(x) Type_##x##_Dup
57 // Helper macro to define a handler. Callbacks do have a fixed naming convention.
58 #define TYPE_HANDLER(t, x) { (t), READ_FN(x), WRITE_FN(x), DUP_FN(x), FREE_FN(x), NULL, 0 }
60 // Helper macro to define a MPE handler. Callbacks do have a fixed naming convention
61 #define TYPE_MPE_HANDLER(t, x) { (t), READ_FN(x), WRITE_FN(x), GenericMPEdup, GenericMPEfree, NULL, 0 }
63 // Infinites
64 #define MINUS_INF (-1E22F)
65 #define PLUS_INF (+1E22F)
68 // Register a new type handler. This routine is shared between normal types and MPE. LinkedList points to the optional list head
69 static
70 cmsBool RegisterTypesPlugin(cmsContext id, cmsPluginBase* Data, _cmsMemoryClient pos)
72 cmsPluginTagType* Plugin = (cmsPluginTagType*) Data;
73 _cmsTagTypePluginChunkType* ctx = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(id, pos);
74 _cmsTagTypeLinkedList *pt;
76 // Calling the function with NULL as plug-in would unregister the plug in.
77 if (Data == NULL) {
79 // There is no need to set free the memory, as pool is destroyed as a whole.
80 ctx ->TagTypes = NULL;
81 return TRUE;
84 // Registering happens in plug-in memory pool.
85 pt = (_cmsTagTypeLinkedList*) _cmsPluginMalloc(id, sizeof(_cmsTagTypeLinkedList));
86 if (pt == NULL) return FALSE;
88 pt ->Handler = Plugin ->Handler;
89 pt ->Next = ctx ->TagTypes;
91 ctx ->TagTypes = pt;
93 return TRUE;
96 // Return handler for a given type or NULL if not found. Shared between normal types and MPE. It first tries the additons
97 // made by plug-ins and then the built-in defaults.
98 static
99 cmsTagTypeHandler* GetHandler(cmsTagTypeSignature sig, _cmsTagTypeLinkedList* PluginLinkedList, _cmsTagTypeLinkedList* DefaultLinkedList)
101 _cmsTagTypeLinkedList* pt;
103 for (pt = PluginLinkedList;
104 pt != NULL;
105 pt = pt ->Next) {
107 if (sig == pt -> Handler.Signature) return &pt ->Handler;
110 for (pt = DefaultLinkedList;
111 pt != NULL;
112 pt = pt ->Next) {
114 if (sig == pt -> Handler.Signature) return &pt ->Handler;
117 return NULL;
121 // Auxiliary to convert UTF-32 to UTF-16 in some cases
122 static
123 cmsBool _cmsWriteWCharArray(cmsIOHANDLER* io, cmsUInt32Number n, const wchar_t* Array)
125 cmsUInt32Number i;
127 _cmsAssert(io != NULL);
128 _cmsAssert(!(Array == NULL && n > 0));
130 for (i=0; i < n; i++) {
131 if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) Array[i])) return FALSE;
134 return TRUE;
137 // Try to promote correctly to wchar_t when 32 bits
138 cmsINLINE cmsBool is_surrogate(cmsUInt32Number uc) { return (uc - 0xd800u) < 2048u; }
139 cmsINLINE cmsBool is_high_surrogate(cmsUInt32Number uc) { return (uc & 0xfffffc00) == 0xd800; }
140 cmsINLINE cmsBool is_low_surrogate(cmsUInt32Number uc) { return (uc & 0xfffffc00) == 0xdc00; }
142 cmsINLINE cmsUInt32Number surrogate_to_utf32(cmsUInt32Number high, cmsUInt32Number low)
144 return (high << 10) + low - 0x35fdc00;
147 cmsINLINE cmsBool convert_utf16_to_utf32(cmsIOHANDLER* io, cmsInt32Number n, wchar_t* output)
149 cmsUInt16Number uc;
151 while (n > 0)
153 if (!_cmsReadUInt16Number(io, &uc)) return FALSE;
154 n--;
156 if (!is_surrogate(uc))
158 *output++ = (wchar_t)uc;
160 else {
162 cmsUInt16Number low;
164 if (!_cmsReadUInt16Number(io, &low)) return FALSE;
165 n--;
167 if (is_high_surrogate(uc) && is_low_surrogate(low))
168 *output++ = (wchar_t)surrogate_to_utf32(uc, low);
169 else
170 return FALSE; // Corrupted string, just ignore
174 return TRUE;
178 // Auxiliary to read an array of wchar_t
179 static
180 cmsBool _cmsReadWCharArray(cmsIOHANDLER* io, cmsUInt32Number n, wchar_t* Array)
182 cmsUInt32Number i;
183 cmsUInt16Number tmp;
184 cmsBool is32 = sizeof(wchar_t) > sizeof(cmsUInt16Number);
186 _cmsAssert(io != NULL);
188 if (is32 && Array != NULL)
190 return convert_utf16_to_utf32(io, n, Array);
193 for (i=0; i < n; i++) {
195 if (Array != NULL) {
197 if (!_cmsReadUInt16Number(io, &tmp)) return FALSE;
198 Array[i] = (wchar_t) tmp;
200 else {
201 if (!_cmsReadUInt16Number(io, NULL)) return FALSE;
205 return TRUE;
208 // To deal with position tables
209 typedef cmsBool (* PositionTableEntryFn)(struct _cms_typehandler_struct* self,
210 cmsIOHANDLER* io,
211 void* Cargo,
212 cmsUInt32Number n,
213 cmsUInt32Number SizeOfTag);
215 // Helper function to deal with position tables as described in ICC spec 4.3
216 // A table of n elements is read, where first comes n records containing offsets and sizes and
217 // then a block containing the data itself. This allows to reuse same data in more than one entry
218 static
219 cmsBool ReadPositionTable(struct _cms_typehandler_struct* self,
220 cmsIOHANDLER* io,
221 cmsUInt32Number Count,
222 cmsUInt32Number BaseOffset,
223 void *Cargo,
224 PositionTableEntryFn ElementFn)
226 cmsUInt32Number i;
227 cmsUInt32Number *ElementOffsets = NULL, *ElementSizes = NULL;
228 cmsUInt32Number currentPosition;
230 currentPosition = io->Tell(io);
232 // Verify there is enough space left to read at least two cmsUInt32Number items for Count items.
233 if (((io->ReportedSize - currentPosition) / (2 * sizeof(cmsUInt32Number))) < Count)
234 return FALSE;
236 // Let's take the offsets to each element
237 ElementOffsets = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number));
238 if (ElementOffsets == NULL) goto Error;
240 ElementSizes = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number));
241 if (ElementSizes == NULL) goto Error;
243 for (i=0; i < Count; i++) {
245 if (!_cmsReadUInt32Number(io, &ElementOffsets[i])) goto Error;
246 if (!_cmsReadUInt32Number(io, &ElementSizes[i])) goto Error;
248 ElementOffsets[i] += BaseOffset;
251 // Seek to each element and read it
252 for (i=0; i < Count; i++) {
254 if (!io -> Seek(io, ElementOffsets[i])) goto Error;
256 // This is the reader callback
257 if (!ElementFn(self, io, Cargo, i, ElementSizes[i])) goto Error;
260 // Success
261 if (ElementOffsets != NULL) _cmsFree(io ->ContextID, ElementOffsets);
262 if (ElementSizes != NULL) _cmsFree(io ->ContextID, ElementSizes);
263 return TRUE;
265 Error:
266 if (ElementOffsets != NULL) _cmsFree(io ->ContextID, ElementOffsets);
267 if (ElementSizes != NULL) _cmsFree(io ->ContextID, ElementSizes);
268 return FALSE;
271 // Same as anterior, but for write position tables
272 static
273 cmsBool WritePositionTable(struct _cms_typehandler_struct* self,
274 cmsIOHANDLER* io,
275 cmsUInt32Number SizeOfTag,
276 cmsUInt32Number Count,
277 cmsUInt32Number BaseOffset,
278 void *Cargo,
279 PositionTableEntryFn ElementFn)
281 cmsUInt32Number i;
282 cmsUInt32Number DirectoryPos, CurrentPos, Before;
283 cmsUInt32Number *ElementOffsets = NULL, *ElementSizes = NULL;
285 // Create table
286 ElementOffsets = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number));
287 if (ElementOffsets == NULL) goto Error;
289 ElementSizes = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number));
290 if (ElementSizes == NULL) goto Error;
292 // Keep starting position of curve offsets
293 DirectoryPos = io ->Tell(io);
295 // Write a fake directory to be filled latter on
296 for (i=0; i < Count; i++) {
298 if (!_cmsWriteUInt32Number(io, 0)) goto Error; // Offset
299 if (!_cmsWriteUInt32Number(io, 0)) goto Error; // size
302 // Write each element. Keep track of the size as well.
303 for (i=0; i < Count; i++) {
305 Before = io ->Tell(io);
306 ElementOffsets[i] = Before - BaseOffset;
308 // Callback to write...
309 if (!ElementFn(self, io, Cargo, i, SizeOfTag)) goto Error;
311 // Now the size
312 ElementSizes[i] = io ->Tell(io) - Before;
315 // Write the directory
316 CurrentPos = io ->Tell(io);
317 if (!io ->Seek(io, DirectoryPos)) goto Error;
319 for (i=0; i < Count; i++) {
320 if (!_cmsWriteUInt32Number(io, ElementOffsets[i])) goto Error;
321 if (!_cmsWriteUInt32Number(io, ElementSizes[i])) goto Error;
324 if (!io ->Seek(io, CurrentPos)) goto Error;
326 if (ElementOffsets != NULL) _cmsFree(io ->ContextID, ElementOffsets);
327 if (ElementSizes != NULL) _cmsFree(io ->ContextID, ElementSizes);
328 return TRUE;
330 Error:
331 if (ElementOffsets != NULL) _cmsFree(io ->ContextID, ElementOffsets);
332 if (ElementSizes != NULL) _cmsFree(io ->ContextID, ElementSizes);
333 return FALSE;
337 // ********************************************************************************
338 // Type XYZ. Only one value is allowed
339 // ********************************************************************************
341 //The XYZType contains an array of three encoded values for the XYZ tristimulus
342 //values. Tristimulus values must be non-negative. The signed encoding allows for
343 //implementation optimizations by minimizing the number of fixed formats.
346 static
347 void *Type_XYZ_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
349 cmsCIEXYZ* xyz;
351 *nItems = 0;
352 xyz = (cmsCIEXYZ*) _cmsMallocZero(self ->ContextID, sizeof(cmsCIEXYZ));
353 if (xyz == NULL) return NULL;
355 if (!_cmsReadXYZNumber(io, xyz)) {
356 _cmsFree(self ->ContextID, xyz);
357 return NULL;
360 *nItems = 1;
361 return (void*) xyz;
363 cmsUNUSED_PARAMETER(SizeOfTag);
366 static
367 cmsBool Type_XYZ_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
369 return _cmsWriteXYZNumber(io, (cmsCIEXYZ*) Ptr);
371 cmsUNUSED_PARAMETER(nItems);
372 cmsUNUSED_PARAMETER(self);
375 static
376 void* Type_XYZ_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
378 return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsCIEXYZ));
380 cmsUNUSED_PARAMETER(n);
383 static
384 void Type_XYZ_Free(struct _cms_typehandler_struct* self, void *Ptr)
386 _cmsFree(self ->ContextID, Ptr);
390 static
391 cmsTagTypeSignature DecideXYZtype(cmsFloat64Number ICCVersion, const void *Data)
393 return cmsSigXYZType;
395 cmsUNUSED_PARAMETER(ICCVersion);
396 cmsUNUSED_PARAMETER(Data);
400 // ********************************************************************************
401 // Type chromaticity. Only one value is allowed
402 // ********************************************************************************
403 // The chromaticity tag type provides basic chromaticity data and type of
404 // phosphors or colorants of a monitor to applications and utilities.
406 static
407 void *Type_Chromaticity_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
409 cmsCIExyYTRIPLE* chrm;
410 cmsUInt16Number nChans, Table;
412 *nItems = 0;
413 chrm = (cmsCIExyYTRIPLE*) _cmsMallocZero(self ->ContextID, sizeof(cmsCIExyYTRIPLE));
414 if (chrm == NULL) return NULL;
416 if (!_cmsReadUInt16Number(io, &nChans)) goto Error;
418 // Let's recover from a bug introduced in early versions of lcms1
419 if (nChans == 0 && SizeOfTag == 32) {
421 if (!_cmsReadUInt16Number(io, NULL)) goto Error;
422 if (!_cmsReadUInt16Number(io, &nChans)) goto Error;
425 if (nChans != 3) goto Error;
427 if (!_cmsReadUInt16Number(io, &Table)) goto Error;
429 if (!_cmsRead15Fixed16Number(io, &chrm ->Red.x)) goto Error;
430 if (!_cmsRead15Fixed16Number(io, &chrm ->Red.y)) goto Error;
432 chrm ->Red.Y = 1.0;
434 if (!_cmsRead15Fixed16Number(io, &chrm ->Green.x)) goto Error;
435 if (!_cmsRead15Fixed16Number(io, &chrm ->Green.y)) goto Error;
437 chrm ->Green.Y = 1.0;
439 if (!_cmsRead15Fixed16Number(io, &chrm ->Blue.x)) goto Error;
440 if (!_cmsRead15Fixed16Number(io, &chrm ->Blue.y)) goto Error;
442 chrm ->Blue.Y = 1.0;
444 *nItems = 1;
445 return (void*) chrm;
447 Error:
448 _cmsFree(self ->ContextID, (void*) chrm);
449 return NULL;
451 cmsUNUSED_PARAMETER(SizeOfTag);
454 static
455 cmsBool SaveOneChromaticity(cmsFloat64Number x, cmsFloat64Number y, cmsIOHANDLER* io)
457 if (!_cmsWriteUInt32Number(io, (cmsUInt32Number) _cmsDoubleTo15Fixed16(x))) return FALSE;
458 if (!_cmsWriteUInt32Number(io, (cmsUInt32Number) _cmsDoubleTo15Fixed16(y))) return FALSE;
460 return TRUE;
463 static
464 cmsBool Type_Chromaticity_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
466 cmsCIExyYTRIPLE* chrm = (cmsCIExyYTRIPLE*) Ptr;
468 if (!_cmsWriteUInt16Number(io, 3)) return FALSE; // nChannels
469 if (!_cmsWriteUInt16Number(io, 0)) return FALSE; // Table
471 if (!SaveOneChromaticity(chrm -> Red.x, chrm -> Red.y, io)) return FALSE;
472 if (!SaveOneChromaticity(chrm -> Green.x, chrm -> Green.y, io)) return FALSE;
473 if (!SaveOneChromaticity(chrm -> Blue.x, chrm -> Blue.y, io)) return FALSE;
475 return TRUE;
477 cmsUNUSED_PARAMETER(nItems);
478 cmsUNUSED_PARAMETER(self);
481 static
482 void* Type_Chromaticity_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
484 return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsCIExyYTRIPLE));
486 cmsUNUSED_PARAMETER(n);
489 static
490 void Type_Chromaticity_Free(struct _cms_typehandler_struct* self, void* Ptr)
492 _cmsFree(self ->ContextID, Ptr);
496 // ********************************************************************************
497 // Type cmsSigColorantOrderType
498 // ********************************************************************************
500 // This is an optional tag which specifies the laydown order in which colorants will
501 // be printed on an n-colorant device. The laydown order may be the same as the
502 // channel generation order listed in the colorantTableTag or the channel order of a
503 // colour space such as CMYK, in which case this tag is not needed. When this is not
504 // the case (for example, ink-towers sometimes use the order KCMY), this tag may be
505 // used to specify the laydown order of the colorants.
508 static
509 void *Type_ColorantOrderType_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
511 cmsUInt8Number* ColorantOrder;
512 cmsUInt32Number Count;
514 *nItems = 0;
515 if (!_cmsReadUInt32Number(io, &Count)) return NULL;
516 if (Count > cmsMAXCHANNELS) return NULL;
518 ColorantOrder = (cmsUInt8Number*) _cmsCalloc(self ->ContextID, cmsMAXCHANNELS, sizeof(cmsUInt8Number));
519 if (ColorantOrder == NULL) return NULL;
521 // We use FF as end marker
522 memset(ColorantOrder, 0xFF, cmsMAXCHANNELS * sizeof(cmsUInt8Number));
524 if (io ->Read(io, ColorantOrder, sizeof(cmsUInt8Number), Count) != Count) {
526 _cmsFree(self ->ContextID, (void*) ColorantOrder);
527 return NULL;
530 *nItems = 1;
531 return (void*) ColorantOrder;
533 cmsUNUSED_PARAMETER(SizeOfTag);
536 static
537 cmsBool Type_ColorantOrderType_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
539 cmsUInt8Number* ColorantOrder = (cmsUInt8Number*) Ptr;
540 cmsUInt32Number i, sz, Count;
542 // Get the length
543 for (Count=i=0; i < cmsMAXCHANNELS; i++) {
544 if (ColorantOrder[i] != 0xFF) Count++;
547 if (!_cmsWriteUInt32Number(io, Count)) return FALSE;
549 sz = Count * sizeof(cmsUInt8Number);
550 if (!io -> Write(io, sz, ColorantOrder)) return FALSE;
552 return TRUE;
554 cmsUNUSED_PARAMETER(nItems);
555 cmsUNUSED_PARAMETER(self);
558 static
559 void* Type_ColorantOrderType_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
561 return _cmsDupMem(self ->ContextID, Ptr, cmsMAXCHANNELS * sizeof(cmsUInt8Number));
563 cmsUNUSED_PARAMETER(n);
567 static
568 void Type_ColorantOrderType_Free(struct _cms_typehandler_struct* self, void* Ptr)
570 _cmsFree(self ->ContextID, Ptr);
573 // ********************************************************************************
574 // Type cmsSigS15Fixed16ArrayType
575 // ********************************************************************************
576 // This type represents an array of generic 4-byte/32-bit fixed point quantity.
577 // The number of values is determined from the size of the tag.
579 static
580 void *Type_S15Fixed16_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
582 cmsFloat64Number* array_double;
583 cmsUInt32Number i, n;
585 *nItems = 0;
586 n = SizeOfTag / sizeof(cmsUInt32Number);
587 array_double = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, n, sizeof(cmsFloat64Number));
588 if (array_double == NULL) return NULL;
590 for (i=0; i < n; i++) {
592 if (!_cmsRead15Fixed16Number(io, &array_double[i])) {
594 _cmsFree(self ->ContextID, array_double);
595 return NULL;
599 *nItems = n;
600 return (void*) array_double;
603 static
604 cmsBool Type_S15Fixed16_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
606 cmsFloat64Number* Value = (cmsFloat64Number*) Ptr;
607 cmsUInt32Number i;
609 for (i=0; i < nItems; i++) {
611 if (!_cmsWrite15Fixed16Number(io, Value[i])) return FALSE;
614 return TRUE;
616 cmsUNUSED_PARAMETER(self);
619 static
620 void* Type_S15Fixed16_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
622 return _cmsDupMem(self ->ContextID, Ptr, n * sizeof(cmsFloat64Number));
626 static
627 void Type_S15Fixed16_Free(struct _cms_typehandler_struct* self, void* Ptr)
629 _cmsFree(self ->ContextID, Ptr);
632 // ********************************************************************************
633 // Type cmsSigU16Fixed16ArrayType
634 // ********************************************************************************
635 // This type represents an array of generic 4-byte/32-bit quantity.
636 // The number of values is determined from the size of the tag.
639 static
640 void *Type_U16Fixed16_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
642 cmsFloat64Number* array_double;
643 cmsUInt32Number v;
644 cmsUInt32Number i, n;
646 *nItems = 0;
647 n = SizeOfTag / sizeof(cmsUInt32Number);
648 array_double = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, n, sizeof(cmsFloat64Number));
649 if (array_double == NULL) return NULL;
651 for (i=0; i < n; i++) {
653 if (!_cmsReadUInt32Number(io, &v)) {
654 _cmsFree(self ->ContextID, (void*) array_double);
655 return NULL;
658 // Convert to cmsFloat64Number
659 array_double[i] = (cmsFloat64Number) (v / 65536.0);
662 *nItems = n;
663 return (void*) array_double;
666 static
667 cmsBool Type_U16Fixed16_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
669 cmsFloat64Number* Value = (cmsFloat64Number*) Ptr;
670 cmsUInt32Number i;
672 for (i=0; i < nItems; i++) {
674 cmsUInt32Number v = (cmsUInt32Number) floor(Value[i]*65536.0 + 0.5);
676 if (!_cmsWriteUInt32Number(io, v)) return FALSE;
679 return TRUE;
681 cmsUNUSED_PARAMETER(self);
685 static
686 void* Type_U16Fixed16_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
688 return _cmsDupMem(self ->ContextID, Ptr, n * sizeof(cmsFloat64Number));
691 static
692 void Type_U16Fixed16_Free(struct _cms_typehandler_struct* self, void* Ptr)
694 _cmsFree(self ->ContextID, Ptr);
697 // ********************************************************************************
698 // Type cmsSigSignatureType
699 // ********************************************************************************
701 // The signatureType contains a four-byte sequence, Sequences of less than four
702 // characters are padded at the end with spaces, 20h.
703 // Typically this type is used for registered tags that can be displayed on many
704 // development systems as a sequence of four characters.
706 static
707 void *Type_Signature_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
709 cmsSignature* SigPtr = (cmsSignature*) _cmsMalloc(self ->ContextID, sizeof(cmsSignature));
710 if (SigPtr == NULL) return NULL;
712 if (!_cmsReadUInt32Number(io, SigPtr)) return NULL;
713 *nItems = 1;
715 return SigPtr;
717 cmsUNUSED_PARAMETER(SizeOfTag);
720 static
721 cmsBool Type_Signature_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
723 cmsSignature* SigPtr = (cmsSignature*) Ptr;
725 return _cmsWriteUInt32Number(io, *SigPtr);
727 cmsUNUSED_PARAMETER(nItems);
728 cmsUNUSED_PARAMETER(self);
731 static
732 void* Type_Signature_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
734 return _cmsDupMem(self ->ContextID, Ptr, n * sizeof(cmsSignature));
737 static
738 void Type_Signature_Free(struct _cms_typehandler_struct* self, void* Ptr)
740 _cmsFree(self ->ContextID, Ptr);
744 // ********************************************************************************
745 // Type cmsSigTextType
746 // ********************************************************************************
748 // The textType is a simple text structure that contains a 7-bit ASCII text string.
749 // The length of the string is obtained by subtracting 8 from the element size portion
750 // of the tag itself. This string must be terminated with a 00h byte.
752 static
753 void *Type_Text_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
755 char* Text = NULL;
756 cmsMLU* mlu = NULL;
758 // Create a container
759 mlu = cmsMLUalloc(self ->ContextID, 1);
760 if (mlu == NULL) return NULL;
762 *nItems = 0;
764 // We need to store the "\0" at the end, so +1
765 if (SizeOfTag == UINT_MAX) goto Error;
767 Text = (char*) _cmsMalloc(self ->ContextID, SizeOfTag + 1);
768 if (Text == NULL) goto Error;
770 if (io -> Read(io, Text, sizeof(char), SizeOfTag) != SizeOfTag) goto Error;
772 // Make sure text is properly ended
773 Text[SizeOfTag] = 0;
774 *nItems = 1;
776 // Keep the result
777 if (!cmsMLUsetASCII(mlu, cmsNoLanguage, cmsNoCountry, Text)) goto Error;
779 _cmsFree(self ->ContextID, Text);
780 return (void*) mlu;
782 Error:
783 if (mlu != NULL)
784 cmsMLUfree(mlu);
785 if (Text != NULL)
786 _cmsFree(self ->ContextID, Text);
788 return NULL;
791 // The conversion implies to choose a language. So, we choose the actual language.
792 static
793 cmsBool Type_Text_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
795 cmsMLU* mlu = (cmsMLU*) Ptr;
796 cmsUInt32Number size;
797 cmsBool rc;
798 char* Text;
800 // Get the size of the string. Note there is an extra "\0" at the end
801 size = cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, NULL, 0);
802 if (size == 0) return FALSE; // Cannot be zero!
804 // Create memory
805 Text = (char*) _cmsMalloc(self ->ContextID, size);
806 if (Text == NULL) return FALSE;
808 cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, Text, size);
810 // Write it, including separator
811 rc = io ->Write(io, size, Text);
813 _cmsFree(self ->ContextID, Text);
814 return rc;
816 cmsUNUSED_PARAMETER(nItems);
819 static
820 void* Type_Text_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
822 return (void*) cmsMLUdup((cmsMLU*) Ptr);
824 cmsUNUSED_PARAMETER(n);
825 cmsUNUSED_PARAMETER(self);
829 static
830 void Type_Text_Free(struct _cms_typehandler_struct* self, void* Ptr)
832 cmsMLU* mlu = (cmsMLU*) Ptr;
833 cmsMLUfree(mlu);
834 return;
836 cmsUNUSED_PARAMETER(self);
839 static
840 cmsTagTypeSignature DecideTextType(cmsFloat64Number ICCVersion, const void *Data)
842 if (ICCVersion >= 4.0)
843 return cmsSigMultiLocalizedUnicodeType;
845 return cmsSigTextType;
847 cmsUNUSED_PARAMETER(Data);
851 // ********************************************************************************
852 // Type cmsSigDataType
853 // ********************************************************************************
855 // General purpose data type
856 static
857 void *Type_Data_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
859 cmsICCData* BinData;
860 cmsUInt32Number LenOfData;
862 *nItems = 0;
864 if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL;
866 LenOfData = SizeOfTag - sizeof(cmsUInt32Number);
867 if (LenOfData > INT_MAX) return NULL;
869 BinData = (cmsICCData*) _cmsMalloc(self ->ContextID, sizeof(cmsICCData) + LenOfData - 1);
870 if (BinData == NULL) return NULL;
872 BinData ->len = LenOfData;
873 if (!_cmsReadUInt32Number(io, &BinData->flag)) {
874 _cmsFree(self ->ContextID, BinData);
875 return NULL;
878 if (io -> Read(io, BinData ->data, sizeof(cmsUInt8Number), LenOfData) != LenOfData) {
880 _cmsFree(self ->ContextID, BinData);
881 return NULL;
884 *nItems = 1;
886 return (void*) BinData;
890 static
891 cmsBool Type_Data_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
893 cmsICCData* BinData = (cmsICCData*) Ptr;
895 if (!_cmsWriteUInt32Number(io, BinData ->flag)) return FALSE;
897 return io ->Write(io, BinData ->len, BinData ->data);
899 cmsUNUSED_PARAMETER(nItems);
900 cmsUNUSED_PARAMETER(self);
904 static
905 void* Type_Data_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
907 cmsICCData* BinData = (cmsICCData*) Ptr;
909 return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsICCData) + BinData ->len - 1);
911 cmsUNUSED_PARAMETER(n);
914 static
915 void Type_Data_Free(struct _cms_typehandler_struct* self, void* Ptr)
917 _cmsFree(self ->ContextID, Ptr);
920 // ********************************************************************************
921 // Type cmsSigTextDescriptionType
922 // ********************************************************************************
924 static
925 void *Type_Text_Description_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
927 char* Text = NULL;
928 cmsMLU* mlu = NULL;
929 cmsUInt32Number AsciiCount;
930 cmsUInt32Number i, UnicodeCode, UnicodeCount;
931 cmsUInt16Number ScriptCodeCode, Dummy;
932 cmsUInt8Number ScriptCodeCount;
934 *nItems = 0;
936 // One dword should be there
937 if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL;
939 // Read len of ASCII
940 if (!_cmsReadUInt32Number(io, &AsciiCount)) return NULL;
941 SizeOfTag -= sizeof(cmsUInt32Number);
943 // Check for size
944 if (SizeOfTag < AsciiCount) return NULL;
946 // All seems Ok, allocate the container
947 mlu = cmsMLUalloc(self ->ContextID, 1);
948 if (mlu == NULL) return NULL;
950 // As many memory as size of tag
951 Text = (char*) _cmsMalloc(self ->ContextID, AsciiCount + 1);
952 if (Text == NULL) goto Error;
954 // Read it
955 if (io ->Read(io, Text, sizeof(char), AsciiCount) != AsciiCount) goto Error;
956 SizeOfTag -= AsciiCount;
958 // Make sure there is a terminator
959 Text[AsciiCount] = 0;
961 // Set the MLU entry. From here we can be tolerant to wrong types
962 if (!cmsMLUsetASCII(mlu, cmsNoLanguage, cmsNoCountry, Text)) goto Error;
963 _cmsFree(self ->ContextID, (void*) Text);
964 Text = NULL;
966 // Skip Unicode code
967 if (SizeOfTag < 2* sizeof(cmsUInt32Number)) goto Done;
968 if (!_cmsReadUInt32Number(io, &UnicodeCode)) goto Done;
969 if (!_cmsReadUInt32Number(io, &UnicodeCount)) goto Done;
970 SizeOfTag -= 2* sizeof(cmsUInt32Number);
972 if (SizeOfTag < UnicodeCount*sizeof(cmsUInt16Number)) goto Done;
974 for (i=0; i < UnicodeCount; i++) {
975 if (!io ->Read(io, &Dummy, sizeof(cmsUInt16Number), 1)) goto Done;
977 SizeOfTag -= UnicodeCount*sizeof(cmsUInt16Number);
979 // Skip ScriptCode code if present. Some buggy profiles does have less
980 // data that stricttly required. We need to skip it as this type may come
981 // embedded in other types.
983 if (SizeOfTag >= sizeof(cmsUInt16Number) + sizeof(cmsUInt8Number) + 67) {
985 if (!_cmsReadUInt16Number(io, &ScriptCodeCode)) goto Done;
986 if (!_cmsReadUInt8Number(io, &ScriptCodeCount)) goto Done;
988 // Skip rest of tag
989 for (i=0; i < 67; i++) {
990 if (!io ->Read(io, &Dummy, sizeof(cmsUInt8Number), 1)) goto Error;
994 Done:
996 *nItems = 1;
997 return mlu;
999 Error:
1000 if (Text) _cmsFree(self ->ContextID, (void*) Text);
1001 if (mlu) cmsMLUfree(mlu);
1002 return NULL;
1006 // This tag can come IN UNALIGNED SIZE. In order to prevent issues, we force zeros on description to align it
1007 static
1008 cmsBool Type_Text_Description_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
1010 cmsMLU* mlu = (cmsMLU*) Ptr;
1011 char *Text = NULL;
1012 wchar_t *Wide = NULL;
1013 cmsUInt32Number len, len_text, len_tag_requirement, len_aligned;
1014 cmsBool rc = FALSE;
1015 char Filler[68];
1017 // Used below for writing zeroes
1018 memset(Filler, 0, sizeof(Filler));
1020 // Get the len of string
1021 len = cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, NULL, 0);
1023 // Specification ICC.1:2001-04 (v2.4.0): It has been found that textDescriptionType can contain misaligned data
1024 //(see clause 4.1 for the definition of 'aligned'). Because the Unicode language
1025 // code and Unicode count immediately follow the ASCII description, their
1026 // alignment is not correct if the ASCII count is not a multiple of four. The
1027 // ScriptCode code is misaligned when the ASCII count is odd. Profile reading and
1028 // writing software must be written carefully in order to handle these alignment
1029 // problems.
1031 // The above last sentence suggest to handle alignment issues in the
1032 // parser. The provided example (Table 69 on Page 60) makes this clear.
1033 // The padding only in the ASCII count is not sufficient for a aligned tag
1034 // size, with the same text size in ASCII and Unicode.
1036 // Null strings
1037 if (len <= 0) {
1039 Text = (char*) _cmsDupMem(self ->ContextID, "", sizeof(char));
1040 Wide = (wchar_t*) _cmsDupMem(self ->ContextID, L"", sizeof(wchar_t));
1042 else {
1043 // Create independent buffers
1044 Text = (char*) _cmsCalloc(self ->ContextID, len, sizeof(char));
1045 if (Text == NULL) goto Error;
1047 Wide = (wchar_t*) _cmsCalloc(self ->ContextID, len, sizeof(wchar_t));
1048 if (Wide == NULL) goto Error;
1050 // Get both representations.
1051 cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, Text, len * sizeof(char));
1052 cmsMLUgetWide(mlu, cmsNoLanguage, cmsNoCountry, Wide, len * sizeof(wchar_t));
1055 // Tell the real text len including the null terminator and padding
1056 len_text = (cmsUInt32Number) strlen(Text) + 1;
1057 // Compute an total tag size requirement
1058 len_tag_requirement = (8+4+len_text+4+4+2*len_text+2+1+67);
1059 len_aligned = _cmsALIGNLONG(len_tag_requirement);
1061 // * cmsUInt32Number count; * Description length
1062 // * cmsInt8Number desc[count] * NULL terminated ascii string
1063 // * cmsUInt32Number ucLangCode; * UniCode language code
1064 // * cmsUInt32Number ucCount; * UniCode description length
1065 // * cmsInt16Number ucDesc[ucCount];* The UniCode description
1066 // * cmsUInt16Number scCode; * ScriptCode code
1067 // * cmsUInt8Number scCount; * ScriptCode count
1068 // * cmsInt8Number scDesc[67]; * ScriptCode Description
1070 if (!_cmsWriteUInt32Number(io, len_text)) goto Error;
1071 if (!io ->Write(io, len_text, Text)) goto Error;
1073 if (!_cmsWriteUInt32Number(io, 0)) goto Error; // ucLanguageCode
1075 if (!_cmsWriteUInt32Number(io, len_text)) goto Error;
1076 // Note that in some compilers sizeof(cmsUInt16Number) != sizeof(wchar_t)
1077 if (!_cmsWriteWCharArray(io, len_text, Wide)) goto Error;
1079 // ScriptCode Code & count (unused)
1080 if (!_cmsWriteUInt16Number(io, 0)) goto Error;
1081 if (!_cmsWriteUInt8Number(io, 0)) goto Error;
1083 if (!io ->Write(io, 67, Filler)) goto Error;
1085 // possibly add pad at the end of tag
1086 if(len_aligned - len_tag_requirement > 0)
1087 if (!io ->Write(io, len_aligned - len_tag_requirement, Filler)) goto Error;
1089 rc = TRUE;
1091 Error:
1092 if (Text) _cmsFree(self ->ContextID, Text);
1093 if (Wide) _cmsFree(self ->ContextID, Wide);
1095 return rc;
1097 cmsUNUSED_PARAMETER(nItems);
1101 static
1102 void* Type_Text_Description_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
1104 return (void*) cmsMLUdup((cmsMLU*) Ptr);
1106 cmsUNUSED_PARAMETER(n);
1107 cmsUNUSED_PARAMETER(self);
1110 static
1111 void Type_Text_Description_Free(struct _cms_typehandler_struct* self, void* Ptr)
1113 cmsMLU* mlu = (cmsMLU*) Ptr;
1115 cmsMLUfree(mlu);
1116 return;
1118 cmsUNUSED_PARAMETER(self);
1122 static
1123 cmsTagTypeSignature DecideTextDescType(cmsFloat64Number ICCVersion, const void *Data)
1125 if (ICCVersion >= 4.0)
1126 return cmsSigMultiLocalizedUnicodeType;
1128 return cmsSigTextDescriptionType;
1130 cmsUNUSED_PARAMETER(Data);
1134 // ********************************************************************************
1135 // Type cmsSigCurveType
1136 // ********************************************************************************
1138 static
1139 void *Type_Curve_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
1141 cmsUInt32Number Count;
1142 cmsToneCurve* NewGamma;
1144 *nItems = 0;
1145 if (!_cmsReadUInt32Number(io, &Count)) return NULL;
1147 switch (Count) {
1149 case 0: // Linear.
1151 cmsFloat64Number SingleGamma = 1.0;
1153 NewGamma = cmsBuildParametricToneCurve(self ->ContextID, 1, &SingleGamma);
1154 if (!NewGamma) return NULL;
1155 *nItems = 1;
1156 return NewGamma;
1159 case 1: // Specified as the exponent of gamma function
1161 cmsUInt16Number SingleGammaFixed;
1162 cmsFloat64Number SingleGamma;
1164 if (!_cmsReadUInt16Number(io, &SingleGammaFixed)) return NULL;
1165 SingleGamma = _cms8Fixed8toDouble(SingleGammaFixed);
1167 *nItems = 1;
1168 return cmsBuildParametricToneCurve(self ->ContextID, 1, &SingleGamma);
1171 default: // Curve
1173 if (Count > 0x7FFF)
1174 return NULL; // This is to prevent bad guys for doing bad things
1176 NewGamma = cmsBuildTabulatedToneCurve16(self ->ContextID, Count, NULL);
1177 if (!NewGamma) return NULL;
1179 if (!_cmsReadUInt16Array(io, Count, NewGamma -> Table16)) {
1180 cmsFreeToneCurve(NewGamma);
1181 return NULL;
1184 *nItems = 1;
1185 return NewGamma;
1188 cmsUNUSED_PARAMETER(SizeOfTag);
1192 static
1193 cmsBool Type_Curve_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
1195 cmsToneCurve* Curve = (cmsToneCurve*) Ptr;
1197 if (Curve ->nSegments == 1 && Curve ->Segments[0].Type == 1) {
1199 // Single gamma, preserve number
1200 cmsUInt16Number SingleGammaFixed = _cmsDoubleTo8Fixed8(Curve ->Segments[0].Params[0]);
1202 if (!_cmsWriteUInt32Number(io, 1)) return FALSE;
1203 if (!_cmsWriteUInt16Number(io, SingleGammaFixed)) return FALSE;
1204 return TRUE;
1208 if (!_cmsWriteUInt32Number(io, Curve ->nEntries)) return FALSE;
1209 return _cmsWriteUInt16Array(io, Curve ->nEntries, Curve ->Table16);
1211 cmsUNUSED_PARAMETER(nItems);
1212 cmsUNUSED_PARAMETER(self);
1216 static
1217 void* Type_Curve_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
1219 return (void*) cmsDupToneCurve((cmsToneCurve*) Ptr);
1221 cmsUNUSED_PARAMETER(n);
1222 cmsUNUSED_PARAMETER(self);
1225 static
1226 void Type_Curve_Free(struct _cms_typehandler_struct* self, void* Ptr)
1228 cmsToneCurve* gamma = (cmsToneCurve*) Ptr;
1230 cmsFreeToneCurve(gamma);
1231 return;
1233 cmsUNUSED_PARAMETER(self);
1237 // ********************************************************************************
1238 // Type cmsSigParametricCurveType
1239 // ********************************************************************************
1242 // Decide which curve type to use on writing
1243 static
1244 cmsTagTypeSignature DecideCurveType(cmsFloat64Number ICCVersion, const void *Data)
1246 cmsToneCurve* Curve = (cmsToneCurve*) Data;
1248 if (ICCVersion < 4.0) return cmsSigCurveType;
1249 if (Curve ->nSegments != 1) return cmsSigCurveType; // Only 1-segment curves can be saved as parametric
1250 if (Curve ->Segments[0].Type < 0) return cmsSigCurveType; // Only non-inverted curves
1251 if (Curve ->Segments[0].Type > 5) return cmsSigCurveType; // Only ICC parametric curves
1253 return cmsSigParametricCurveType;
1256 static
1257 void *Type_ParametricCurve_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
1259 static const int ParamsByType[] = { 1, 3, 4, 5, 7 };
1260 cmsFloat64Number Params[10];
1261 cmsUInt16Number Type;
1262 int i, n;
1263 cmsToneCurve* NewGamma;
1265 if (!_cmsReadUInt16Number(io, &Type)) return NULL;
1266 if (!_cmsReadUInt16Number(io, NULL)) return NULL; // Reserved
1268 if (Type > 4) {
1270 cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown parametric curve type '%d'", Type);
1271 return NULL;
1274 memset(Params, 0, sizeof(Params));
1275 n = ParamsByType[Type];
1277 for (i=0; i < n; i++) {
1279 if (!_cmsRead15Fixed16Number(io, &Params[i])) return NULL;
1282 NewGamma = cmsBuildParametricToneCurve(self ->ContextID, Type+1, Params);
1284 *nItems = 1;
1285 return NewGamma;
1287 cmsUNUSED_PARAMETER(SizeOfTag);
1291 static
1292 cmsBool Type_ParametricCurve_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
1294 cmsToneCurve* Curve = (cmsToneCurve*) Ptr;
1295 int i, nParams, typen;
1296 static const int ParamsByType[] = { 0, 1, 3, 4, 5, 7 };
1298 typen = Curve -> Segments[0].Type;
1300 if (Curve ->nSegments > 1 || typen < 1) {
1302 cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Multisegment or Inverted parametric curves cannot be written");
1303 return FALSE;
1306 if (typen > 5) {
1307 cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported parametric curve");
1308 return FALSE;
1311 nParams = ParamsByType[typen];
1313 if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) (Curve ->Segments[0].Type - 1))) return FALSE;
1314 if (!_cmsWriteUInt16Number(io, 0)) return FALSE; // Reserved
1316 for (i=0; i < nParams; i++) {
1318 if (!_cmsWrite15Fixed16Number(io, Curve -> Segments[0].Params[i])) return FALSE;
1321 return TRUE;
1323 cmsUNUSED_PARAMETER(nItems);
1326 static
1327 void* Type_ParametricCurve_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
1329 return (void*) cmsDupToneCurve((cmsToneCurve*) Ptr);
1331 cmsUNUSED_PARAMETER(n);
1332 cmsUNUSED_PARAMETER(self);
1335 static
1336 void Type_ParametricCurve_Free(struct _cms_typehandler_struct* self, void* Ptr)
1338 cmsToneCurve* gamma = (cmsToneCurve*) Ptr;
1340 cmsFreeToneCurve(gamma);
1341 return;
1343 cmsUNUSED_PARAMETER(self);
1347 // ********************************************************************************
1348 // Type cmsSigDateTimeType
1349 // ********************************************************************************
1351 // A 12-byte value representation of the time and date, where the byte usage is assigned
1352 // as specified in table 1. The actual values are encoded as 16-bit unsigned integers
1353 // (uInt16Number - see 5.1.6).
1355 // All the dateTimeNumber values in a profile shall be in Coordinated Universal Time
1356 // (UTC, also known as GMT or ZULU Time). Profile writers are required to convert local
1357 // time to UTC when setting these values. Programs that display these values may show
1358 // the dateTimeNumber as UTC, show the equivalent local time (at current locale), or
1359 // display both UTC and local versions of the dateTimeNumber.
1361 static
1362 void *Type_DateTime_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
1364 cmsDateTimeNumber timestamp;
1365 struct tm * NewDateTime;
1367 *nItems = 0;
1368 NewDateTime = (struct tm*) _cmsMalloc(self ->ContextID, sizeof(struct tm));
1369 if (NewDateTime == NULL) return NULL;
1371 if (io->Read(io, &timestamp, sizeof(cmsDateTimeNumber), 1) != 1) return NULL;
1373 _cmsDecodeDateTimeNumber(&timestamp, NewDateTime);
1375 *nItems = 1;
1376 return NewDateTime;
1378 cmsUNUSED_PARAMETER(SizeOfTag);
1382 static
1383 cmsBool Type_DateTime_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
1385 struct tm * DateTime = (struct tm*) Ptr;
1386 cmsDateTimeNumber timestamp;
1388 _cmsEncodeDateTimeNumber(&timestamp, DateTime);
1389 if (!io ->Write(io, sizeof(cmsDateTimeNumber), &timestamp)) return FALSE;
1391 return TRUE;
1393 cmsUNUSED_PARAMETER(nItems);
1394 cmsUNUSED_PARAMETER(self);
1397 static
1398 void* Type_DateTime_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
1400 return _cmsDupMem(self ->ContextID, Ptr, sizeof(struct tm));
1402 cmsUNUSED_PARAMETER(n);
1405 static
1406 void Type_DateTime_Free(struct _cms_typehandler_struct* self, void* Ptr)
1408 _cmsFree(self ->ContextID, Ptr);
1413 // ********************************************************************************
1414 // Type icMeasurementType
1415 // ********************************************************************************
1418 The measurementType information refers only to the internal profile data and is
1419 meant to provide profile makers an alternative to the default measurement
1420 specifications.
1423 static
1424 void *Type_Measurement_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
1426 cmsICCMeasurementConditions mc;
1429 memset(&mc, 0, sizeof(mc));
1431 if (!_cmsReadUInt32Number(io, &mc.Observer)) return NULL;
1432 if (!_cmsReadXYZNumber(io, &mc.Backing)) return NULL;
1433 if (!_cmsReadUInt32Number(io, &mc.Geometry)) return NULL;
1434 if (!_cmsRead15Fixed16Number(io, &mc.Flare)) return NULL;
1435 if (!_cmsReadUInt32Number(io, &mc.IlluminantType)) return NULL;
1437 *nItems = 1;
1438 return _cmsDupMem(self ->ContextID, &mc, sizeof(cmsICCMeasurementConditions));
1440 cmsUNUSED_PARAMETER(SizeOfTag);
1444 static
1445 cmsBool Type_Measurement_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
1447 cmsICCMeasurementConditions* mc =(cmsICCMeasurementConditions*) Ptr;
1449 if (!_cmsWriteUInt32Number(io, mc->Observer)) return FALSE;
1450 if (!_cmsWriteXYZNumber(io, &mc->Backing)) return FALSE;
1451 if (!_cmsWriteUInt32Number(io, mc->Geometry)) return FALSE;
1452 if (!_cmsWrite15Fixed16Number(io, mc->Flare)) return FALSE;
1453 if (!_cmsWriteUInt32Number(io, mc->IlluminantType)) return FALSE;
1455 return TRUE;
1457 cmsUNUSED_PARAMETER(nItems);
1458 cmsUNUSED_PARAMETER(self);
1461 static
1462 void* Type_Measurement_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
1464 return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsICCMeasurementConditions));
1466 cmsUNUSED_PARAMETER(n);
1469 static
1470 void Type_Measurement_Free(struct _cms_typehandler_struct* self, void* Ptr)
1472 _cmsFree(self ->ContextID, Ptr);
1476 // ********************************************************************************
1477 // Type cmsSigMultiLocalizedUnicodeType
1478 // ********************************************************************************
1480 // Do NOT trust SizeOfTag as there is an issue on the definition of profileSequenceDescTag. See the TechNote from
1481 // Max Derhak and Rohit Patil about this: basically the size of the string table should be guessed and cannot be
1482 // taken from the size of tag if this tag is embedded as part of bigger structures (profileSequenceDescTag, for instance)
1485 static
1486 void *Type_MLU_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
1488 cmsMLU* mlu;
1489 cmsUInt32Number Count, RecLen, NumOfWchar;
1490 cmsUInt32Number SizeOfHeader;
1491 cmsUInt32Number Len, Offset;
1492 cmsUInt32Number i;
1493 wchar_t* Block;
1494 cmsUInt32Number BeginOfThisString, EndOfThisString, LargestPosition;
1496 *nItems = 0;
1497 if (!_cmsReadUInt32Number(io, &Count)) return NULL;
1498 if (!_cmsReadUInt32Number(io, &RecLen)) return NULL;
1500 if (RecLen != 12) {
1502 cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "multiLocalizedUnicodeType of len != 12 is not supported.");
1503 return NULL;
1506 mlu = cmsMLUalloc(self ->ContextID, Count);
1507 if (mlu == NULL) return NULL;
1509 mlu ->UsedEntries = Count;
1511 SizeOfHeader = 12 * Count + sizeof(_cmsTagBase);
1512 LargestPosition = 0;
1514 for (i=0; i < Count; i++) {
1516 if (!_cmsReadUInt16Number(io, &mlu ->Entries[i].Language)) goto Error;
1517 if (!_cmsReadUInt16Number(io, &mlu ->Entries[i].Country)) goto Error;
1519 // Now deal with Len and offset.
1520 if (!_cmsReadUInt32Number(io, &Len)) goto Error;
1521 if (!_cmsReadUInt32Number(io, &Offset)) goto Error;
1523 // Check for overflow
1524 if (Offset < (SizeOfHeader + 8)) goto Error;
1525 if (((Offset + Len) < Len) || ((Offset + Len) > SizeOfTag + 8)) goto Error;
1527 // True begin of the string
1528 BeginOfThisString = Offset - SizeOfHeader - 8;
1530 // Adjust to wchar_t elements
1531 mlu ->Entries[i].Len = (Len * sizeof(wchar_t)) / sizeof(cmsUInt16Number);
1532 mlu ->Entries[i].StrW = (BeginOfThisString * sizeof(wchar_t)) / sizeof(cmsUInt16Number);
1534 // To guess maximum size, add offset + len
1535 EndOfThisString = BeginOfThisString + Len;
1536 if (EndOfThisString > LargestPosition)
1537 LargestPosition = EndOfThisString;
1540 // Now read the remaining of tag and fill all strings. Subtract the directory
1541 SizeOfTag = (LargestPosition * sizeof(wchar_t)) / sizeof(cmsUInt16Number);
1542 if (SizeOfTag == 0)
1544 Block = NULL;
1545 NumOfWchar = 0;
1548 else
1550 Block = (wchar_t*) _cmsMalloc(self ->ContextID, SizeOfTag);
1551 if (Block == NULL) goto Error;
1552 NumOfWchar = SizeOfTag / sizeof(wchar_t);
1553 if (!_cmsReadWCharArray(io, NumOfWchar, Block)) {
1554 _cmsFree(self->ContextID, Block);
1555 goto Error;
1559 mlu ->MemPool = Block;
1560 mlu ->PoolSize = SizeOfTag;
1561 mlu ->PoolUsed = SizeOfTag;
1563 *nItems = 1;
1564 return (void*) mlu;
1566 Error:
1567 if (mlu) cmsMLUfree(mlu);
1568 return NULL;
1571 static
1572 cmsBool Type_MLU_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
1574 cmsMLU* mlu =(cmsMLU*) Ptr;
1575 cmsUInt32Number HeaderSize;
1576 cmsUInt32Number Len, Offset;
1577 cmsUInt32Number i;
1579 if (Ptr == NULL) {
1581 // Empty placeholder
1582 if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
1583 if (!_cmsWriteUInt32Number(io, 12)) return FALSE;
1584 return TRUE;
1587 if (!_cmsWriteUInt32Number(io, mlu ->UsedEntries)) return FALSE;
1588 if (!_cmsWriteUInt32Number(io, 12)) return FALSE;
1590 HeaderSize = 12 * mlu ->UsedEntries + sizeof(_cmsTagBase);
1592 for (i=0; i < mlu ->UsedEntries; i++) {
1594 Len = mlu ->Entries[i].Len;
1595 Offset = mlu ->Entries[i].StrW;
1597 Len = (Len * sizeof(cmsUInt16Number)) / sizeof(wchar_t);
1598 Offset = (Offset * sizeof(cmsUInt16Number)) / sizeof(wchar_t) + HeaderSize + 8;
1600 if (!_cmsWriteUInt16Number(io, mlu ->Entries[i].Language)) return FALSE;
1601 if (!_cmsWriteUInt16Number(io, mlu ->Entries[i].Country)) return FALSE;
1602 if (!_cmsWriteUInt32Number(io, Len)) return FALSE;
1603 if (!_cmsWriteUInt32Number(io, Offset)) return FALSE;
1606 if (!_cmsWriteWCharArray(io, mlu ->PoolUsed / sizeof(wchar_t), (wchar_t*) mlu ->MemPool)) return FALSE;
1608 return TRUE;
1610 cmsUNUSED_PARAMETER(nItems);
1611 cmsUNUSED_PARAMETER(self);
1615 static
1616 void* Type_MLU_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
1618 return (void*) cmsMLUdup((cmsMLU*) Ptr);
1620 cmsUNUSED_PARAMETER(n);
1621 cmsUNUSED_PARAMETER(self);
1624 static
1625 void Type_MLU_Free(struct _cms_typehandler_struct* self, void* Ptr)
1627 cmsMLUfree((cmsMLU*) Ptr);
1628 return;
1630 cmsUNUSED_PARAMETER(self);
1634 // ********************************************************************************
1635 // Type cmsSigLut8Type
1636 // ********************************************************************************
1638 // Decide which LUT type to use on writing
1639 static
1640 cmsTagTypeSignature DecideLUTtypeA2B(cmsFloat64Number ICCVersion, const void *Data)
1642 cmsPipeline* Lut = (cmsPipeline*) Data;
1644 if (ICCVersion < 4.0) {
1645 if (Lut ->SaveAs8Bits) return cmsSigLut8Type;
1646 return cmsSigLut16Type;
1648 else {
1649 return cmsSigLutAtoBType;
1653 static
1654 cmsTagTypeSignature DecideLUTtypeB2A(cmsFloat64Number ICCVersion, const void *Data)
1656 cmsPipeline* Lut = (cmsPipeline*) Data;
1658 if (ICCVersion < 4.0) {
1659 if (Lut ->SaveAs8Bits) return cmsSigLut8Type;
1660 return cmsSigLut16Type;
1662 else {
1663 return cmsSigLutBtoAType;
1668 This structure represents a colour transform using tables of 8-bit precision.
1669 This type contains four processing elements: a 3 by 3 matrix (which shall be
1670 the identity matrix unless the input colour space is XYZ), a set of one dimensional
1671 input tables, a multidimensional lookup table, and a set of one dimensional output
1672 tables. Data is processed using these elements via the following sequence:
1673 (matrix) -> (1d input tables) -> (multidimensional lookup table - CLUT) -> (1d output tables)
1675 Byte Position Field Length (bytes) Content Encoded as...
1676 8 1 Number of Input Channels (i) uInt8Number
1677 9 1 Number of Output Channels (o) uInt8Number
1678 10 1 Number of CLUT grid points (identical for each side) (g) uInt8Number
1679 11 1 Reserved for padding (fill with 00h)
1681 12..15 4 Encoded e00 parameter s15Fixed16Number
1685 // Read 8 bit tables as gamma functions
1686 static
1687 cmsBool Read8bitTables(cmsContext ContextID, cmsIOHANDLER* io, cmsPipeline* lut, cmsUInt32Number nChannels)
1689 cmsUInt8Number* Temp = NULL;
1690 cmsUInt32Number i, j;
1691 cmsToneCurve* Tables[cmsMAXCHANNELS];
1693 if (nChannels > cmsMAXCHANNELS) return FALSE;
1694 if (nChannels <= 0) return FALSE;
1696 memset(Tables, 0, sizeof(Tables));
1698 Temp = (cmsUInt8Number*) _cmsMalloc(ContextID, 256);
1699 if (Temp == NULL) return FALSE;
1701 for (i=0; i < nChannels; i++) {
1702 Tables[i] = cmsBuildTabulatedToneCurve16(ContextID, 256, NULL);
1703 if (Tables[i] == NULL) goto Error;
1706 for (i=0; i < nChannels; i++) {
1708 if (io ->Read(io, Temp, 256, 1) != 1) goto Error;
1710 for (j=0; j < 256; j++)
1711 Tables[i]->Table16[j] = (cmsUInt16Number) FROM_8_TO_16(Temp[j]);
1714 _cmsFree(ContextID, Temp);
1715 Temp = NULL;
1717 if (!cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, nChannels, Tables)))
1718 goto Error;
1720 for (i=0; i < nChannels; i++)
1721 cmsFreeToneCurve(Tables[i]);
1723 return TRUE;
1725 Error:
1726 for (i=0; i < nChannels; i++) {
1727 if (Tables[i]) cmsFreeToneCurve(Tables[i]);
1730 if (Temp) _cmsFree(ContextID, Temp);
1731 return FALSE;
1735 static
1736 cmsBool Write8bitTables(cmsContext ContextID, cmsIOHANDLER* io, cmsUInt32Number n, _cmsStageToneCurvesData* Tables)
1738 int j;
1739 cmsUInt32Number i;
1740 cmsUInt8Number val;
1742 for (i=0; i < n; i++) {
1744 if (Tables) {
1746 // Usual case of identity curves
1747 if ((Tables ->TheCurves[i]->nEntries == 2) &&
1748 (Tables->TheCurves[i]->Table16[0] == 0) &&
1749 (Tables->TheCurves[i]->Table16[1] == 65535)) {
1751 for (j=0; j < 256; j++) {
1752 if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) j)) return FALSE;
1755 else
1756 if (Tables ->TheCurves[i]->nEntries != 256) {
1757 cmsSignalError(ContextID, cmsERROR_RANGE, "LUT8 needs 256 entries on prelinearization");
1758 return FALSE;
1760 else
1761 for (j=0; j < 256; j++) {
1763 val = (cmsUInt8Number) FROM_16_TO_8(Tables->TheCurves[i]->Table16[j]);
1765 if (!_cmsWriteUInt8Number(io, val)) return FALSE;
1769 return TRUE;
1773 // Check overflow
1774 static
1775 cmsUInt32Number uipow(cmsUInt32Number n, cmsUInt32Number a, cmsUInt32Number b)
1777 cmsUInt32Number rv = 1, rc;
1779 if (a == 0) return 0;
1780 if (n == 0) return 0;
1782 for (; b > 0; b--) {
1784 rv *= a;
1786 // Check for overflow
1787 if (rv > UINT_MAX / a) return (cmsUInt32Number) -1;
1791 rc = rv * n;
1793 if (rv != rc / n) return (cmsUInt32Number) -1;
1794 return rc;
1798 // That will create a MPE LUT with Matrix, pre tables, CLUT and post tables.
1799 // 8 bit lut may be scaled easily to v4 PCS, but we need also to properly adjust
1800 // PCS on BToAxx tags and AtoB if abstract. We need to fix input direction.
1802 static
1803 void *Type_LUT8_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
1805 cmsUInt8Number InputChannels, OutputChannels, CLUTpoints;
1806 cmsUInt8Number* Temp = NULL;
1807 cmsPipeline* NewLUT = NULL;
1808 cmsUInt32Number nTabSize, i;
1809 cmsFloat64Number Matrix[3*3];
1811 *nItems = 0;
1813 if (!_cmsReadUInt8Number(io, &InputChannels)) goto Error;
1814 if (!_cmsReadUInt8Number(io, &OutputChannels)) goto Error;
1815 if (!_cmsReadUInt8Number(io, &CLUTpoints)) goto Error;
1817 if (CLUTpoints == 1) goto Error; // Impossible value, 0 for no CLUT and then 2 at least
1819 // Padding
1820 if (!_cmsReadUInt8Number(io, NULL)) goto Error;
1822 // Do some checking
1823 if (InputChannels == 0 || InputChannels > cmsMAXCHANNELS) goto Error;
1824 if (OutputChannels == 0 || OutputChannels > cmsMAXCHANNELS) goto Error;
1826 // Allocates an empty Pipeline
1827 NewLUT = cmsPipelineAlloc(self ->ContextID, InputChannels, OutputChannels);
1828 if (NewLUT == NULL) goto Error;
1830 // Read the Matrix
1831 if (!_cmsRead15Fixed16Number(io, &Matrix[0])) goto Error;
1832 if (!_cmsRead15Fixed16Number(io, &Matrix[1])) goto Error;
1833 if (!_cmsRead15Fixed16Number(io, &Matrix[2])) goto Error;
1834 if (!_cmsRead15Fixed16Number(io, &Matrix[3])) goto Error;
1835 if (!_cmsRead15Fixed16Number(io, &Matrix[4])) goto Error;
1836 if (!_cmsRead15Fixed16Number(io, &Matrix[5])) goto Error;
1837 if (!_cmsRead15Fixed16Number(io, &Matrix[6])) goto Error;
1838 if (!_cmsRead15Fixed16Number(io, &Matrix[7])) goto Error;
1839 if (!_cmsRead15Fixed16Number(io, &Matrix[8])) goto Error;
1842 // Only operates if not identity...
1843 if ((InputChannels == 3) && !_cmsMAT3isIdentity((cmsMAT3*) Matrix)) {
1845 if (!cmsPipelineInsertStage(NewLUT, cmsAT_BEGIN, cmsStageAllocMatrix(self ->ContextID, 3, 3, Matrix, NULL)))
1846 goto Error;
1849 // Get input tables
1850 if (!Read8bitTables(self ->ContextID, io, NewLUT, InputChannels)) goto Error;
1852 // Get 3D CLUT. Check the overflow....
1853 nTabSize = uipow(OutputChannels, CLUTpoints, InputChannels);
1854 if (nTabSize == (cmsUInt32Number) -1) goto Error;
1855 if (nTabSize > 0) {
1857 cmsUInt16Number *PtrW, *T;
1859 PtrW = T = (cmsUInt16Number*) _cmsCalloc(self ->ContextID, nTabSize, sizeof(cmsUInt16Number));
1860 if (T == NULL) goto Error;
1862 Temp = (cmsUInt8Number*) _cmsMalloc(self ->ContextID, nTabSize);
1863 if (Temp == NULL) {
1864 _cmsFree(self ->ContextID, T);
1865 goto Error;
1868 if (io ->Read(io, Temp, nTabSize, 1) != 1) {
1869 _cmsFree(self ->ContextID, T);
1870 _cmsFree(self ->ContextID, Temp);
1871 goto Error;
1874 for (i = 0; i < nTabSize; i++) {
1876 *PtrW++ = FROM_8_TO_16(Temp[i]);
1878 _cmsFree(self ->ContextID, Temp);
1879 Temp = NULL;
1881 if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, cmsStageAllocCLut16bit(self ->ContextID, CLUTpoints, InputChannels, OutputChannels, T))) {
1882 _cmsFree(self ->ContextID, T);
1883 goto Error;
1885 _cmsFree(self ->ContextID, T);
1889 // Get output tables
1890 if (!Read8bitTables(self ->ContextID, io, NewLUT, OutputChannels)) goto Error;
1892 *nItems = 1;
1893 return NewLUT;
1895 Error:
1896 if (NewLUT != NULL) cmsPipelineFree(NewLUT);
1897 return NULL;
1899 cmsUNUSED_PARAMETER(SizeOfTag);
1902 // We only allow a specific MPE structure: Matrix plus prelin, plus clut, plus post-lin.
1903 static
1904 cmsBool Type_LUT8_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
1906 cmsUInt32Number j, nTabSize, i;
1907 cmsUInt8Number val;
1908 cmsPipeline* NewLUT = (cmsPipeline*) Ptr;
1909 cmsStage* mpe;
1910 _cmsStageToneCurvesData* PreMPE = NULL, *PostMPE = NULL;
1911 _cmsStageMatrixData* MatMPE = NULL;
1912 _cmsStageCLutData* clut = NULL;
1913 cmsUInt32Number clutPoints;
1915 // Disassemble the LUT into components.
1916 mpe = NewLUT -> Elements;
1917 if (mpe ->Type == cmsSigMatrixElemType) {
1919 if (mpe->InputChannels != 3 || mpe->OutputChannels != 3) return FALSE;
1920 MatMPE = (_cmsStageMatrixData*) mpe ->Data;
1921 mpe = mpe -> Next;
1924 if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) {
1925 PreMPE = (_cmsStageToneCurvesData*) mpe ->Data;
1926 mpe = mpe -> Next;
1929 if (mpe != NULL && mpe ->Type == cmsSigCLutElemType) {
1930 clut = (_cmsStageCLutData*) mpe -> Data;
1931 mpe = mpe ->Next;
1934 if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) {
1935 PostMPE = (_cmsStageToneCurvesData*) mpe ->Data;
1936 mpe = mpe -> Next;
1939 // That should be all
1940 if (mpe != NULL) {
1941 cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT is not suitable to be saved as LUT8");
1942 return FALSE;
1945 if (clut == NULL)
1946 clutPoints = 0;
1947 else {
1948 // Lut8 only allows same CLUT points in all dimensions
1949 clutPoints = clut->Params->nSamples[0];
1950 for (i = 1; i < cmsPipelineInputChannels(NewLUT); i++) {
1951 if (clut->Params->nSamples[i] != clutPoints) {
1952 cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT with different samples per dimension not suitable to be saved as LUT16");
1953 return FALSE;
1958 if (!_cmsWriteUInt8Number(io, (cmsUInt8Number)cmsPipelineInputChannels(NewLUT))) return FALSE;
1959 if (!_cmsWriteUInt8Number(io, (cmsUInt8Number)cmsPipelineOutputChannels(NewLUT))) return FALSE;
1960 if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) clutPoints)) return FALSE;
1961 if (!_cmsWriteUInt8Number(io, 0)) return FALSE; // Padding
1963 if (MatMPE != NULL) {
1965 for (i = 0; i < 9; i++)
1967 if (!_cmsWrite15Fixed16Number(io, MatMPE->Double[i])) return FALSE;
1970 else {
1972 if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE;
1973 if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
1974 if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
1975 if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
1976 if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE;
1977 if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
1978 if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
1979 if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
1980 if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE;
1983 // The prelinearization table
1984 if (!Write8bitTables(self ->ContextID, io, NewLUT ->InputChannels, PreMPE)) return FALSE;
1986 nTabSize = uipow(NewLUT->OutputChannels, clutPoints, NewLUT ->InputChannels);
1987 if (nTabSize == (cmsUInt32Number) -1) return FALSE;
1988 if (nTabSize > 0) {
1990 // The 3D CLUT.
1991 if (clut != NULL) {
1993 for (j=0; j < nTabSize; j++) {
1995 val = (cmsUInt8Number) FROM_16_TO_8(clut ->Tab.T[j]);
1996 if (!_cmsWriteUInt8Number(io, val)) return FALSE;
2001 // The postlinearization table
2002 if (!Write8bitTables(self ->ContextID, io, NewLUT ->OutputChannels, PostMPE)) return FALSE;
2004 return TRUE;
2006 cmsUNUSED_PARAMETER(nItems);
2010 static
2011 void* Type_LUT8_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
2013 return (void*) cmsPipelineDup((cmsPipeline*) Ptr);
2015 cmsUNUSED_PARAMETER(n);
2016 cmsUNUSED_PARAMETER(self);
2019 static
2020 void Type_LUT8_Free(struct _cms_typehandler_struct* self, void* Ptr)
2022 cmsPipelineFree((cmsPipeline*) Ptr);
2023 return;
2025 cmsUNUSED_PARAMETER(self);
2028 // ********************************************************************************
2029 // Type cmsSigLut16Type
2030 // ********************************************************************************
2032 // Read 16 bit tables as gamma functions
2033 static
2034 cmsBool Read16bitTables(cmsContext ContextID, cmsIOHANDLER* io, cmsPipeline* lut,
2035 cmsUInt32Number nChannels, cmsUInt32Number nEntries)
2037 cmsUInt32Number i;
2038 cmsToneCurve* Tables[cmsMAXCHANNELS];
2040 // Maybe an empty table? (this is a lcms extension)
2041 if (nEntries <= 0) return TRUE;
2043 // Check for malicious profiles
2044 if (nEntries < 2) return FALSE;
2045 if (nChannels > cmsMAXCHANNELS) return FALSE;
2047 // Init table to zero
2048 memset(Tables, 0, sizeof(Tables));
2050 for (i=0; i < nChannels; i++) {
2052 Tables[i] = cmsBuildTabulatedToneCurve16(ContextID, nEntries, NULL);
2053 if (Tables[i] == NULL) goto Error;
2055 if (!_cmsReadUInt16Array(io, nEntries, Tables[i]->Table16)) goto Error;
2059 // Add the table (which may certainly be an identity, but this is up to the optimizer, not the reading code)
2060 if (!cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, nChannels, Tables)))
2061 goto Error;
2063 for (i=0; i < nChannels; i++)
2064 cmsFreeToneCurve(Tables[i]);
2066 return TRUE;
2068 Error:
2069 for (i=0; i < nChannels; i++) {
2070 if (Tables[i]) cmsFreeToneCurve(Tables[i]);
2073 return FALSE;
2076 static
2077 cmsBool Write16bitTables(cmsContext ContextID, cmsIOHANDLER* io, _cmsStageToneCurvesData* Tables)
2079 cmsUInt32Number j;
2080 cmsUInt32Number i;
2081 cmsUInt16Number val;
2082 cmsUInt32Number nEntries;
2084 _cmsAssert(Tables != NULL);
2086 for (i=0; i < Tables ->nCurves; i++) {
2088 nEntries = Tables->TheCurves[i]->nEntries;
2090 for (j=0; j < nEntries; j++) {
2092 val = Tables->TheCurves[i]->Table16[j];
2093 if (!_cmsWriteUInt16Number(io, val)) return FALSE;
2096 return TRUE;
2098 cmsUNUSED_PARAMETER(ContextID);
2101 static
2102 void *Type_LUT16_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
2104 cmsUInt8Number InputChannels, OutputChannels, CLUTpoints;
2105 cmsPipeline* NewLUT = NULL;
2106 cmsUInt32Number nTabSize;
2107 cmsFloat64Number Matrix[3*3];
2108 cmsUInt16Number InputEntries, OutputEntries;
2110 *nItems = 0;
2112 if (!_cmsReadUInt8Number(io, &InputChannels)) return NULL;
2113 if (!_cmsReadUInt8Number(io, &OutputChannels)) return NULL;
2114 if (!_cmsReadUInt8Number(io, &CLUTpoints)) return NULL; // 255 maximum
2116 // Padding
2117 if (!_cmsReadUInt8Number(io, NULL)) return NULL;
2119 // Do some checking
2120 if (InputChannels == 0 || InputChannels > cmsMAXCHANNELS) goto Error;
2121 if (OutputChannels == 0 || OutputChannels > cmsMAXCHANNELS) goto Error;
2123 // Allocates an empty LUT
2124 NewLUT = cmsPipelineAlloc(self ->ContextID, InputChannels, OutputChannels);
2125 if (NewLUT == NULL) goto Error;
2127 // Read the Matrix
2128 if (!_cmsRead15Fixed16Number(io, &Matrix[0])) goto Error;
2129 if (!_cmsRead15Fixed16Number(io, &Matrix[1])) goto Error;
2130 if (!_cmsRead15Fixed16Number(io, &Matrix[2])) goto Error;
2131 if (!_cmsRead15Fixed16Number(io, &Matrix[3])) goto Error;
2132 if (!_cmsRead15Fixed16Number(io, &Matrix[4])) goto Error;
2133 if (!_cmsRead15Fixed16Number(io, &Matrix[5])) goto Error;
2134 if (!_cmsRead15Fixed16Number(io, &Matrix[6])) goto Error;
2135 if (!_cmsRead15Fixed16Number(io, &Matrix[7])) goto Error;
2136 if (!_cmsRead15Fixed16Number(io, &Matrix[8])) goto Error;
2139 // Only operates on 3 channels
2140 if ((InputChannels == 3) && !_cmsMAT3isIdentity((cmsMAT3*) Matrix)) {
2142 if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, cmsStageAllocMatrix(self ->ContextID, 3, 3, Matrix, NULL)))
2143 goto Error;
2146 if (!_cmsReadUInt16Number(io, &InputEntries)) goto Error;
2147 if (!_cmsReadUInt16Number(io, &OutputEntries)) goto Error;
2149 if (InputEntries > 0x7FFF || OutputEntries > 0x7FFF) goto Error;
2150 if (CLUTpoints == 1) goto Error; // Impossible value, 0 for no CLUT and then 2 at least
2152 // Get input tables
2153 if (!Read16bitTables(self ->ContextID, io, NewLUT, InputChannels, InputEntries)) goto Error;
2155 // Get 3D CLUT
2156 nTabSize = uipow(OutputChannels, CLUTpoints, InputChannels);
2157 if (nTabSize == (cmsUInt32Number) -1) goto Error;
2158 if (nTabSize > 0) {
2160 cmsUInt16Number *T;
2162 T = (cmsUInt16Number*) _cmsCalloc(self ->ContextID, nTabSize, sizeof(cmsUInt16Number));
2163 if (T == NULL) goto Error;
2165 if (!_cmsReadUInt16Array(io, nTabSize, T)) {
2166 _cmsFree(self ->ContextID, T);
2167 goto Error;
2170 if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, cmsStageAllocCLut16bit(self ->ContextID, CLUTpoints, InputChannels, OutputChannels, T))) {
2171 _cmsFree(self ->ContextID, T);
2172 goto Error;
2174 _cmsFree(self ->ContextID, T);
2178 // Get output tables
2179 if (!Read16bitTables(self ->ContextID, io, NewLUT, OutputChannels, OutputEntries)) goto Error;
2181 *nItems = 1;
2182 return NewLUT;
2184 Error:
2185 if (NewLUT != NULL) cmsPipelineFree(NewLUT);
2186 return NULL;
2188 cmsUNUSED_PARAMETER(SizeOfTag);
2191 // We only allow some specific MPE structures: Matrix plus prelin, plus clut, plus post-lin.
2192 // Some empty defaults are created for missing parts
2194 static
2195 cmsBool Type_LUT16_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
2197 cmsUInt32Number nTabSize;
2198 cmsPipeline* NewLUT = (cmsPipeline*) Ptr;
2199 cmsStage* mpe;
2200 _cmsStageToneCurvesData* PreMPE = NULL, *PostMPE = NULL;
2201 _cmsStageMatrixData* MatMPE = NULL;
2202 _cmsStageCLutData* clut = NULL;
2203 cmsUInt32Number i, InputChannels, OutputChannels, clutPoints;
2205 // Disassemble the LUT into components.
2206 mpe = NewLUT -> Elements;
2207 if (mpe != NULL && mpe ->Type == cmsSigMatrixElemType) {
2209 MatMPE = (_cmsStageMatrixData*) mpe ->Data;
2210 if (mpe->InputChannels != 3 || mpe->OutputChannels != 3) return FALSE;
2211 mpe = mpe -> Next;
2215 if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) {
2216 PreMPE = (_cmsStageToneCurvesData*) mpe ->Data;
2217 mpe = mpe -> Next;
2220 if (mpe != NULL && mpe ->Type == cmsSigCLutElemType) {
2221 clut = (_cmsStageCLutData*) mpe -> Data;
2222 mpe = mpe ->Next;
2225 if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) {
2226 PostMPE = (_cmsStageToneCurvesData*) mpe ->Data;
2227 mpe = mpe -> Next;
2230 // That should be all
2231 if (mpe != NULL) {
2232 cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT is not suitable to be saved as LUT16");
2233 return FALSE;
2236 InputChannels = cmsPipelineInputChannels(NewLUT);
2237 OutputChannels = cmsPipelineOutputChannels(NewLUT);
2239 if (clut == NULL)
2240 clutPoints = 0;
2241 else {
2242 // Lut16 only allows same CLUT points in all dimensions
2243 clutPoints = clut->Params->nSamples[0];
2244 for (i = 1; i < InputChannels; i++) {
2245 if (clut->Params->nSamples[i] != clutPoints) {
2246 cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT with different samples per dimension not suitable to be saved as LUT16");
2247 return FALSE;
2252 if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) InputChannels)) return FALSE;
2253 if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) OutputChannels)) return FALSE;
2254 if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) clutPoints)) return FALSE;
2255 if (!_cmsWriteUInt8Number(io, 0)) return FALSE; // Padding
2257 if (MatMPE != NULL) {
2259 for (i = 0; i < 9; i++)
2261 if (!_cmsWrite15Fixed16Number(io, MatMPE->Double[i])) return FALSE;
2265 else {
2267 if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE;
2268 if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
2269 if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
2270 if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
2271 if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE;
2272 if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
2273 if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
2274 if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
2275 if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE;
2279 if (PreMPE != NULL) {
2280 if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) PreMPE ->TheCurves[0]->nEntries)) return FALSE;
2281 } else {
2282 if (!_cmsWriteUInt16Number(io, 2)) return FALSE;
2285 if (PostMPE != NULL) {
2286 if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) PostMPE ->TheCurves[0]->nEntries)) return FALSE;
2287 } else {
2288 if (!_cmsWriteUInt16Number(io, 2)) return FALSE;
2292 // The prelinearization table
2294 if (PreMPE != NULL) {
2295 if (!Write16bitTables(self ->ContextID, io, PreMPE)) return FALSE;
2297 else {
2298 for (i=0; i < InputChannels; i++) {
2300 if (!_cmsWriteUInt16Number(io, 0)) return FALSE;
2301 if (!_cmsWriteUInt16Number(io, 0xffff)) return FALSE;
2305 nTabSize = uipow(OutputChannels, clutPoints, InputChannels);
2306 if (nTabSize == (cmsUInt32Number) -1) return FALSE;
2307 if (nTabSize > 0) {
2308 // The 3D CLUT.
2309 if (clut != NULL) {
2310 if (!_cmsWriteUInt16Array(io, nTabSize, clut->Tab.T)) return FALSE;
2314 // The postlinearization table
2315 if (PostMPE != NULL) {
2316 if (!Write16bitTables(self ->ContextID, io, PostMPE)) return FALSE;
2318 else {
2319 for (i=0; i < OutputChannels; i++) {
2321 if (!_cmsWriteUInt16Number(io, 0)) return FALSE;
2322 if (!_cmsWriteUInt16Number(io, 0xffff)) return FALSE;
2326 return TRUE;
2328 cmsUNUSED_PARAMETER(nItems);
2331 static
2332 void* Type_LUT16_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
2334 return (void*) cmsPipelineDup((cmsPipeline*) Ptr);
2336 cmsUNUSED_PARAMETER(n);
2337 cmsUNUSED_PARAMETER(self);
2340 static
2341 void Type_LUT16_Free(struct _cms_typehandler_struct* self, void* Ptr)
2343 cmsPipelineFree((cmsPipeline*) Ptr);
2344 return;
2346 cmsUNUSED_PARAMETER(self);
2350 // ********************************************************************************
2351 // Type cmsSigLutAToBType
2352 // ********************************************************************************
2355 // V4 stuff. Read matrix for LutAtoB and LutBtoA
2357 static
2358 cmsStage* ReadMatrix(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number Offset)
2360 cmsFloat64Number dMat[3*3];
2361 cmsFloat64Number dOff[3];
2362 cmsStage* Mat;
2364 // Go to address
2365 if (!io -> Seek(io, Offset)) return NULL;
2367 // Read the Matrix
2368 if (!_cmsRead15Fixed16Number(io, &dMat[0])) return NULL;
2369 if (!_cmsRead15Fixed16Number(io, &dMat[1])) return NULL;
2370 if (!_cmsRead15Fixed16Number(io, &dMat[2])) return NULL;
2371 if (!_cmsRead15Fixed16Number(io, &dMat[3])) return NULL;
2372 if (!_cmsRead15Fixed16Number(io, &dMat[4])) return NULL;
2373 if (!_cmsRead15Fixed16Number(io, &dMat[5])) return NULL;
2374 if (!_cmsRead15Fixed16Number(io, &dMat[6])) return NULL;
2375 if (!_cmsRead15Fixed16Number(io, &dMat[7])) return NULL;
2376 if (!_cmsRead15Fixed16Number(io, &dMat[8])) return NULL;
2378 if (!_cmsRead15Fixed16Number(io, &dOff[0])) return NULL;
2379 if (!_cmsRead15Fixed16Number(io, &dOff[1])) return NULL;
2380 if (!_cmsRead15Fixed16Number(io, &dOff[2])) return NULL;
2382 Mat = cmsStageAllocMatrix(self ->ContextID, 3, 3, dMat, dOff);
2384 return Mat;
2390 // V4 stuff. Read CLUT part for LutAtoB and LutBtoA
2392 static
2393 cmsStage* ReadCLUT(struct _cms_typehandler_struct* self, cmsIOHANDLER* io,
2394 cmsUInt32Number Offset, cmsUInt32Number InputChannels, cmsUInt32Number OutputChannels)
2396 cmsUInt8Number gridPoints8[cmsMAXCHANNELS]; // Number of grid points in each dimension.
2397 cmsUInt32Number GridPoints[cmsMAXCHANNELS], i;
2398 cmsUInt8Number Precision;
2399 cmsStage* CLUT;
2400 _cmsStageCLutData* Data;
2402 if (!io -> Seek(io, Offset)) return NULL;
2403 if (io -> Read(io, gridPoints8, cmsMAXCHANNELS, 1) != 1) return NULL;
2406 for (i=0; i < cmsMAXCHANNELS; i++) {
2408 if (gridPoints8[i] == 1) return NULL; // Impossible value, 0 for no CLUT and then 2 at least
2409 GridPoints[i] = gridPoints8[i];
2412 if (!_cmsReadUInt8Number(io, &Precision)) return NULL;
2414 if (!_cmsReadUInt8Number(io, NULL)) return NULL;
2415 if (!_cmsReadUInt8Number(io, NULL)) return NULL;
2416 if (!_cmsReadUInt8Number(io, NULL)) return NULL;
2418 CLUT = cmsStageAllocCLut16bitGranular(self ->ContextID, GridPoints, InputChannels, OutputChannels, NULL);
2419 if (CLUT == NULL) return NULL;
2421 Data = (_cmsStageCLutData*) CLUT ->Data;
2423 // Precision can be 1 or 2 bytes
2424 if (Precision == 1) {
2426 cmsUInt8Number v;
2428 for (i=0; i < Data ->nEntries; i++) {
2430 if (io ->Read(io, &v, sizeof(cmsUInt8Number), 1) != 1) {
2431 cmsStageFree(CLUT);
2432 return NULL;
2434 Data ->Tab.T[i] = FROM_8_TO_16(v);
2438 else
2439 if (Precision == 2) {
2441 if (!_cmsReadUInt16Array(io, Data->nEntries, Data ->Tab.T)) {
2442 cmsStageFree(CLUT);
2443 return NULL;
2446 else {
2447 cmsStageFree(CLUT);
2448 cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown precision of '%d'", Precision);
2449 return NULL;
2452 return CLUT;
2455 static
2456 cmsToneCurve* ReadEmbeddedCurve(struct _cms_typehandler_struct* self, cmsIOHANDLER* io)
2458 cmsTagTypeSignature BaseType;
2459 cmsUInt32Number nItems;
2461 BaseType = _cmsReadTypeBase(io);
2462 switch (BaseType) {
2464 case cmsSigCurveType:
2465 return (cmsToneCurve*) Type_Curve_Read(self, io, &nItems, 0);
2467 case cmsSigParametricCurveType:
2468 return (cmsToneCurve*) Type_ParametricCurve_Read(self, io, &nItems, 0);
2470 default:
2472 char String[5];
2474 _cmsTagSignature2String(String, (cmsTagSignature) BaseType);
2475 cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown curve type '%s'", String);
2477 return NULL;
2482 // Read a set of curves from specific offset
2483 static
2484 cmsStage* ReadSetOfCurves(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number Offset, cmsUInt32Number nCurves)
2486 cmsToneCurve* Curves[cmsMAXCHANNELS];
2487 cmsUInt32Number i;
2488 cmsStage* Lin = NULL;
2490 if (nCurves > cmsMAXCHANNELS) return FALSE;
2492 if (!io -> Seek(io, Offset)) return FALSE;
2494 for (i=0; i < nCurves; i++)
2495 Curves[i] = NULL;
2497 for (i=0; i < nCurves; i++) {
2499 Curves[i] = ReadEmbeddedCurve(self, io);
2500 if (Curves[i] == NULL) goto Error;
2501 if (!_cmsReadAlignment(io)) goto Error;
2505 Lin = cmsStageAllocToneCurves(self ->ContextID, nCurves, Curves);
2507 Error:
2508 for (i=0; i < nCurves; i++)
2509 cmsFreeToneCurve(Curves[i]);
2511 return Lin;
2515 // LutAtoB type
2517 // This structure represents a colour transform. The type contains up to five processing
2518 // elements which are stored in the AtoBTag tag in the following order: a set of one
2519 // dimensional curves, a 3 by 3 matrix with offset terms, a set of one dimensional curves,
2520 // a multidimensional lookup table, and a set of one dimensional output curves.
2521 // Data are processed using these elements via the following sequence:
2523 //("A" curves) -> (multidimensional lookup table - CLUT) -> ("M" curves) -> (matrix) -> ("B" curves).
2526 It is possible to use any or all of these processing elements. At least one processing element
2527 must be included.Only the following combinations are allowed:
2530 M - Matrix - B
2531 A - CLUT - B
2532 A - CLUT - M - Matrix - B
2536 static
2537 void* Type_LUTA2B_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
2539 cmsUInt32Number BaseOffset;
2540 cmsUInt8Number inputChan; // Number of input channels
2541 cmsUInt8Number outputChan; // Number of output channels
2542 cmsUInt32Number offsetB; // Offset to first "B" curve
2543 cmsUInt32Number offsetMat; // Offset to matrix
2544 cmsUInt32Number offsetM; // Offset to first "M" curve
2545 cmsUInt32Number offsetC; // Offset to CLUT
2546 cmsUInt32Number offsetA; // Offset to first "A" curve
2547 cmsPipeline* NewLUT = NULL;
2550 BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
2552 if (!_cmsReadUInt8Number(io, &inputChan)) return NULL;
2553 if (!_cmsReadUInt8Number(io, &outputChan)) return NULL;
2555 if (!_cmsReadUInt16Number(io, NULL)) return NULL;
2557 if (!_cmsReadUInt32Number(io, &offsetB)) return NULL;
2558 if (!_cmsReadUInt32Number(io, &offsetMat)) return NULL;
2559 if (!_cmsReadUInt32Number(io, &offsetM)) return NULL;
2560 if (!_cmsReadUInt32Number(io, &offsetC)) return NULL;
2561 if (!_cmsReadUInt32Number(io, &offsetA)) return NULL;
2563 if (inputChan == 0 || inputChan >= cmsMAXCHANNELS) return NULL;
2564 if (outputChan == 0 || outputChan >= cmsMAXCHANNELS) return NULL;
2566 // Allocates an empty LUT
2567 NewLUT = cmsPipelineAlloc(self ->ContextID, inputChan, outputChan);
2568 if (NewLUT == NULL) return NULL;
2570 if (offsetA!= 0) {
2571 if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetA, inputChan)))
2572 goto Error;
2575 if (offsetC != 0) {
2576 if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadCLUT(self, io, BaseOffset + offsetC, inputChan, outputChan)))
2577 goto Error;
2580 if (offsetM != 0) {
2581 if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetM, outputChan)))
2582 goto Error;
2585 if (offsetMat != 0) {
2586 if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadMatrix(self, io, BaseOffset + offsetMat)))
2587 goto Error;
2590 if (offsetB != 0) {
2591 if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetB, outputChan)))
2592 goto Error;
2595 *nItems = 1;
2596 return NewLUT;
2597 Error:
2598 cmsPipelineFree(NewLUT);
2599 return NULL;
2601 cmsUNUSED_PARAMETER(SizeOfTag);
2604 // Write a set of curves
2605 static
2606 cmsBool WriteMatrix(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsStage* mpe)
2608 cmsUInt32Number i, n;
2610 _cmsStageMatrixData* m = (_cmsStageMatrixData*) mpe -> Data;
2612 n = mpe->InputChannels * mpe->OutputChannels;
2614 // Write the Matrix
2615 for (i = 0; i < n; i++)
2617 if (!_cmsWrite15Fixed16Number(io, m->Double[i])) return FALSE;
2620 if (m->Offset != NULL) {
2622 for (i = 0; i < mpe->OutputChannels; i++)
2624 if (!_cmsWrite15Fixed16Number(io, m->Offset[i])) return FALSE;
2627 else {
2628 for (i = 0; i < mpe->OutputChannels; i++)
2630 if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
2635 return TRUE;
2637 cmsUNUSED_PARAMETER(self);
2641 // Write a set of curves
2642 static
2643 cmsBool WriteSetOfCurves(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsTagTypeSignature Type, cmsStage* mpe)
2645 cmsUInt32Number i, n;
2646 cmsTagTypeSignature CurrentType;
2647 cmsToneCurve** Curves;
2650 n = cmsStageOutputChannels(mpe);
2651 Curves = _cmsStageGetPtrToCurveSet(mpe);
2653 for (i=0; i < n; i++) {
2655 // If this is a table-based curve, use curve type even on V4
2656 CurrentType = Type;
2658 if ((Curves[i] ->nSegments == 0)||
2659 ((Curves[i]->nSegments == 2) && (Curves[i] ->Segments[1].Type == 0)) )
2660 CurrentType = cmsSigCurveType;
2661 else
2662 if (Curves[i] ->Segments[0].Type < 0)
2663 CurrentType = cmsSigCurveType;
2665 if (!_cmsWriteTypeBase(io, CurrentType)) return FALSE;
2667 switch (CurrentType) {
2669 case cmsSigCurveType:
2670 if (!Type_Curve_Write(self, io, Curves[i], 1)) return FALSE;
2671 break;
2673 case cmsSigParametricCurveType:
2674 if (!Type_ParametricCurve_Write(self, io, Curves[i], 1)) return FALSE;
2675 break;
2677 default:
2679 char String[5];
2681 _cmsTagSignature2String(String, (cmsTagSignature) Type);
2682 cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown curve type '%s'", String);
2684 return FALSE;
2687 if (!_cmsWriteAlignment(io)) return FALSE;
2691 return TRUE;
2695 static
2696 cmsBool WriteCLUT(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt8Number Precision, cmsStage* mpe)
2698 cmsUInt8Number gridPoints[cmsMAXCHANNELS]; // Number of grid points in each dimension.
2699 cmsUInt32Number i;
2700 _cmsStageCLutData* CLUT = ( _cmsStageCLutData*) mpe -> Data;
2702 if (CLUT ->HasFloatValues) {
2703 cmsSignalError(self ->ContextID, cmsERROR_NOT_SUITABLE, "Cannot save floating point data, CLUT are 8 or 16 bit only");
2704 return FALSE;
2707 memset(gridPoints, 0, sizeof(gridPoints));
2708 for (i=0; i < (cmsUInt32Number) CLUT ->Params ->nInputs; i++)
2709 gridPoints[i] = (cmsUInt8Number) CLUT ->Params ->nSamples[i];
2711 if (!io -> Write(io, cmsMAXCHANNELS*sizeof(cmsUInt8Number), gridPoints)) return FALSE;
2713 if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) Precision)) return FALSE;
2714 if (!_cmsWriteUInt8Number(io, 0)) return FALSE;
2715 if (!_cmsWriteUInt8Number(io, 0)) return FALSE;
2716 if (!_cmsWriteUInt8Number(io, 0)) return FALSE;
2718 // Precision can be 1 or 2 bytes
2719 if (Precision == 1) {
2721 for (i=0; i < CLUT->nEntries; i++) {
2723 if (!_cmsWriteUInt8Number(io, FROM_16_TO_8(CLUT->Tab.T[i]))) return FALSE;
2726 else
2727 if (Precision == 2) {
2729 if (!_cmsWriteUInt16Array(io, CLUT->nEntries, CLUT ->Tab.T)) return FALSE;
2731 else {
2732 cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown precision of '%d'", Precision);
2733 return FALSE;
2736 if (!_cmsWriteAlignment(io)) return FALSE;
2738 return TRUE;
2744 static
2745 cmsBool Type_LUTA2B_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
2747 cmsPipeline* Lut = (cmsPipeline*) Ptr;
2748 cmsUInt32Number inputChan, outputChan;
2749 cmsStage *A = NULL, *B = NULL, *M = NULL;
2750 cmsStage * Matrix = NULL;
2751 cmsStage * CLUT = NULL;
2752 cmsUInt32Number offsetB = 0, offsetMat = 0, offsetM = 0, offsetC = 0, offsetA = 0;
2753 cmsUInt32Number BaseOffset, DirectoryPos, CurrentPos;
2755 // Get the base for all offsets
2756 BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
2758 if (Lut ->Elements != NULL)
2759 if (!cmsPipelineCheckAndRetreiveStages(Lut, 1, cmsSigCurveSetElemType, &B))
2760 if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, &M, &Matrix, &B))
2761 if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, &A, &CLUT, &B))
2762 if (!cmsPipelineCheckAndRetreiveStages(Lut, 5, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType,
2763 cmsSigMatrixElemType, cmsSigCurveSetElemType, &A, &CLUT, &M, &Matrix, &B)) {
2765 cmsSignalError(self->ContextID, cmsERROR_NOT_SUITABLE, "LUT is not suitable to be saved as LutAToB");
2766 return FALSE;
2769 // Get input, output channels
2770 inputChan = cmsPipelineInputChannels(Lut);
2771 outputChan = cmsPipelineOutputChannels(Lut);
2773 // Write channel count
2774 if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) inputChan)) return FALSE;
2775 if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) outputChan)) return FALSE;
2776 if (!_cmsWriteUInt16Number(io, 0)) return FALSE;
2778 // Keep directory to be filled latter
2779 DirectoryPos = io ->Tell(io);
2781 // Write the directory
2782 if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
2783 if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
2784 if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
2785 if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
2786 if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
2788 if (A != NULL) {
2790 offsetA = io ->Tell(io) - BaseOffset;
2791 if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, A)) return FALSE;
2794 if (CLUT != NULL) {
2795 offsetC = io ->Tell(io) - BaseOffset;
2796 if (!WriteCLUT(self, io, (Lut ->SaveAs8Bits ? 1U : 2U), CLUT)) return FALSE;
2799 if (M != NULL) {
2801 offsetM = io ->Tell(io) - BaseOffset;
2802 if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, M)) return FALSE;
2805 if (Matrix != NULL) {
2806 offsetMat = io ->Tell(io) - BaseOffset;
2807 if (!WriteMatrix(self, io, Matrix)) return FALSE;
2810 if (B != NULL) {
2812 offsetB = io ->Tell(io) - BaseOffset;
2813 if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, B)) return FALSE;
2816 CurrentPos = io ->Tell(io);
2818 if (!io ->Seek(io, DirectoryPos)) return FALSE;
2820 if (!_cmsWriteUInt32Number(io, offsetB)) return FALSE;
2821 if (!_cmsWriteUInt32Number(io, offsetMat)) return FALSE;
2822 if (!_cmsWriteUInt32Number(io, offsetM)) return FALSE;
2823 if (!_cmsWriteUInt32Number(io, offsetC)) return FALSE;
2824 if (!_cmsWriteUInt32Number(io, offsetA)) return FALSE;
2826 if (!io ->Seek(io, CurrentPos)) return FALSE;
2828 return TRUE;
2830 cmsUNUSED_PARAMETER(nItems);
2834 static
2835 void* Type_LUTA2B_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
2837 return (void*) cmsPipelineDup((cmsPipeline*) Ptr);
2839 cmsUNUSED_PARAMETER(n);
2840 cmsUNUSED_PARAMETER(self);
2843 static
2844 void Type_LUTA2B_Free(struct _cms_typehandler_struct* self, void* Ptr)
2846 cmsPipelineFree((cmsPipeline*) Ptr);
2847 return;
2849 cmsUNUSED_PARAMETER(self);
2853 // LutBToA type
2855 static
2856 void* Type_LUTB2A_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
2858 cmsUInt8Number inputChan; // Number of input channels
2859 cmsUInt8Number outputChan; // Number of output channels
2860 cmsUInt32Number BaseOffset; // Actual position in file
2861 cmsUInt32Number offsetB; // Offset to first "B" curve
2862 cmsUInt32Number offsetMat; // Offset to matrix
2863 cmsUInt32Number offsetM; // Offset to first "M" curve
2864 cmsUInt32Number offsetC; // Offset to CLUT
2865 cmsUInt32Number offsetA; // Offset to first "A" curve
2866 cmsPipeline* NewLUT = NULL;
2869 BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
2871 if (!_cmsReadUInt8Number(io, &inputChan)) return NULL;
2872 if (!_cmsReadUInt8Number(io, &outputChan)) return NULL;
2874 if (inputChan == 0 || inputChan >= cmsMAXCHANNELS) return NULL;
2875 if (outputChan == 0 || outputChan >= cmsMAXCHANNELS) return NULL;
2877 // Padding
2878 if (!_cmsReadUInt16Number(io, NULL)) return NULL;
2880 if (!_cmsReadUInt32Number(io, &offsetB)) return NULL;
2881 if (!_cmsReadUInt32Number(io, &offsetMat)) return NULL;
2882 if (!_cmsReadUInt32Number(io, &offsetM)) return NULL;
2883 if (!_cmsReadUInt32Number(io, &offsetC)) return NULL;
2884 if (!_cmsReadUInt32Number(io, &offsetA)) return NULL;
2886 // Allocates an empty LUT
2887 NewLUT = cmsPipelineAlloc(self ->ContextID, inputChan, outputChan);
2888 if (NewLUT == NULL) return NULL;
2890 if (offsetB != 0) {
2891 if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetB, inputChan)))
2892 goto Error;
2895 if (offsetMat != 0) {
2896 if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadMatrix(self, io, BaseOffset + offsetMat)))
2897 goto Error;
2900 if (offsetM != 0) {
2901 if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetM, inputChan)))
2902 goto Error;
2905 if (offsetC != 0) {
2906 if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadCLUT(self, io, BaseOffset + offsetC, inputChan, outputChan)))
2907 goto Error;
2910 if (offsetA!= 0) {
2911 if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetA, outputChan)))
2912 goto Error;
2915 *nItems = 1;
2916 return NewLUT;
2917 Error:
2918 cmsPipelineFree(NewLUT);
2919 return NULL;
2921 cmsUNUSED_PARAMETER(SizeOfTag);
2927 B - Matrix - M
2928 B - CLUT - A
2929 B - Matrix - M - CLUT - A
2932 static
2933 cmsBool Type_LUTB2A_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
2935 cmsPipeline* Lut = (cmsPipeline*) Ptr;
2936 cmsUInt32Number inputChan, outputChan;
2937 cmsStage *A = NULL, *B = NULL, *M = NULL;
2938 cmsStage *Matrix = NULL;
2939 cmsStage *CLUT = NULL;
2940 cmsUInt32Number offsetB = 0, offsetMat = 0, offsetM = 0, offsetC = 0, offsetA = 0;
2941 cmsUInt32Number BaseOffset, DirectoryPos, CurrentPos;
2944 BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
2946 if (!cmsPipelineCheckAndRetreiveStages(Lut, 1, cmsSigCurveSetElemType, &B))
2947 if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, &B, &Matrix, &M))
2948 if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, &B, &CLUT, &A))
2949 if (!cmsPipelineCheckAndRetreiveStages(Lut, 5, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType,
2950 cmsSigCLutElemType, cmsSigCurveSetElemType, &B, &Matrix, &M, &CLUT, &A)) {
2951 cmsSignalError(self->ContextID, cmsERROR_NOT_SUITABLE, "LUT is not suitable to be saved as LutBToA");
2952 return FALSE;
2955 inputChan = cmsPipelineInputChannels(Lut);
2956 outputChan = cmsPipelineOutputChannels(Lut);
2958 if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) inputChan)) return FALSE;
2959 if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) outputChan)) return FALSE;
2960 if (!_cmsWriteUInt16Number(io, 0)) return FALSE;
2962 DirectoryPos = io ->Tell(io);
2964 if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
2965 if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
2966 if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
2967 if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
2968 if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
2970 if (A != NULL) {
2972 offsetA = io ->Tell(io) - BaseOffset;
2973 if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, A)) return FALSE;
2976 if (CLUT != NULL) {
2977 offsetC = io ->Tell(io) - BaseOffset;
2978 if (!WriteCLUT(self, io, (Lut ->SaveAs8Bits ? 1U : 2U), CLUT)) return FALSE;
2981 if (M != NULL) {
2983 offsetM = io ->Tell(io) - BaseOffset;
2984 if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, M)) return FALSE;
2987 if (Matrix != NULL) {
2988 offsetMat = io ->Tell(io) - BaseOffset;
2989 if (!WriteMatrix(self, io, Matrix)) return FALSE;
2992 if (B != NULL) {
2994 offsetB = io ->Tell(io) - BaseOffset;
2995 if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, B)) return FALSE;
2998 CurrentPos = io ->Tell(io);
3000 if (!io ->Seek(io, DirectoryPos)) return FALSE;
3002 if (!_cmsWriteUInt32Number(io, offsetB)) return FALSE;
3003 if (!_cmsWriteUInt32Number(io, offsetMat)) return FALSE;
3004 if (!_cmsWriteUInt32Number(io, offsetM)) return FALSE;
3005 if (!_cmsWriteUInt32Number(io, offsetC)) return FALSE;
3006 if (!_cmsWriteUInt32Number(io, offsetA)) return FALSE;
3008 if (!io ->Seek(io, CurrentPos)) return FALSE;
3010 return TRUE;
3012 cmsUNUSED_PARAMETER(nItems);
3017 static
3018 void* Type_LUTB2A_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
3020 return (void*) cmsPipelineDup((cmsPipeline*) Ptr);
3022 cmsUNUSED_PARAMETER(n);
3023 cmsUNUSED_PARAMETER(self);
3026 static
3027 void Type_LUTB2A_Free(struct _cms_typehandler_struct* self, void* Ptr)
3029 cmsPipelineFree((cmsPipeline*) Ptr);
3030 return;
3032 cmsUNUSED_PARAMETER(self);
3037 // ********************************************************************************
3038 // Type cmsSigColorantTableType
3039 // ********************************************************************************
3041 The purpose of this tag is to identify the colorants used in the profile by a
3042 unique name and set of XYZ or L*a*b* values to give the colorant an unambiguous
3043 value. The first colorant listed is the colorant of the first device channel of
3044 a lut tag. The second colorant listed is the colorant of the second device channel
3045 of a lut tag, and so on.
3048 static
3049 void *Type_ColorantTable_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
3051 cmsUInt32Number i, Count;
3052 cmsNAMEDCOLORLIST* List;
3053 char Name[34];
3054 cmsUInt16Number PCS[3];
3057 if (!_cmsReadUInt32Number(io, &Count)) return NULL;
3059 if (Count > cmsMAXCHANNELS) {
3060 cmsSignalError(self->ContextID, cmsERROR_RANGE, "Too many colorants '%d'", Count);
3061 return NULL;
3064 List = cmsAllocNamedColorList(self ->ContextID, Count, 0, "", "");
3065 if (List == NULL)
3066 return NULL;
3068 for (i=0; i < Count; i++) {
3070 if (io ->Read(io, Name, 32, 1) != 1) goto Error;
3071 Name[32] = 0;
3073 if (!_cmsReadUInt16Array(io, 3, PCS)) goto Error;
3075 if (!cmsAppendNamedColor(List, Name, PCS, NULL)) goto Error;
3079 *nItems = 1;
3080 return List;
3082 Error:
3083 *nItems = 0;
3084 cmsFreeNamedColorList(List);
3085 return NULL;
3087 cmsUNUSED_PARAMETER(SizeOfTag);
3092 // Saves a colorant table. It is using the named color structure for simplicity sake
3093 static
3094 cmsBool Type_ColorantTable_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
3096 cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) Ptr;
3097 cmsUInt32Number i, nColors;
3099 nColors = cmsNamedColorCount(NamedColorList);
3101 if (!_cmsWriteUInt32Number(io, nColors)) return FALSE;
3103 for (i=0; i < nColors; i++) {
3105 char root[cmsMAX_PATH];
3106 cmsUInt16Number PCS[3];
3108 memset(root, 0, sizeof(root));
3110 if (!cmsNamedColorInfo(NamedColorList, i, root, NULL, NULL, PCS, NULL)) return 0;
3111 root[32] = 0;
3113 if (!io ->Write(io, 32, root)) return FALSE;
3114 if (!_cmsWriteUInt16Array(io, 3, PCS)) return FALSE;
3117 return TRUE;
3119 cmsUNUSED_PARAMETER(nItems);
3120 cmsUNUSED_PARAMETER(self);
3124 static
3125 void* Type_ColorantTable_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n)
3127 cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) Ptr;
3128 return (void*) cmsDupNamedColorList(nc);
3130 cmsUNUSED_PARAMETER(n);
3131 cmsUNUSED_PARAMETER(self);
3135 static
3136 void Type_ColorantTable_Free(struct _cms_typehandler_struct* self, void* Ptr)
3138 cmsFreeNamedColorList((cmsNAMEDCOLORLIST*) Ptr);
3139 return;
3141 cmsUNUSED_PARAMETER(self);
3145 // ********************************************************************************
3146 // Type cmsSigNamedColor2Type
3147 // ********************************************************************************
3149 //The namedColor2Type is a count value and array of structures that provide color
3150 //coordinates for 7-bit ASCII color names. For each named color, a PCS and optional
3151 //device representation of the color are given. Both representations are 16-bit values.
3152 //The device representation corresponds to the header's 'color space of data' field.
3153 //This representation should be consistent with the 'number of device components'
3154 //field in the namedColor2Type. If this field is 0, device coordinates are not provided.
3155 //The PCS representation corresponds to the header's PCS field. The PCS representation
3156 //is always provided. Color names are fixed-length, 32-byte fields including null
3157 //termination. In order to maintain maximum portability, it is strongly recommended
3158 //that special characters of the 7-bit ASCII set not be used.
3160 static
3161 void *Type_NamedColor_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
3163 cmsUInt32Number vendorFlag; // Bottom 16 bits for ICC use
3164 cmsUInt32Number count; // Count of named colors
3165 cmsUInt32Number nDeviceCoords; // Num of device coordinates
3166 char prefix[32]; // Prefix for each color name
3167 char suffix[32]; // Suffix for each color name
3168 cmsNAMEDCOLORLIST* v;
3169 cmsUInt32Number i;
3172 *nItems = 0;
3173 if (!_cmsReadUInt32Number(io, &vendorFlag)) return NULL;
3174 if (!_cmsReadUInt32Number(io, &count)) return NULL;
3175 if (!_cmsReadUInt32Number(io, &nDeviceCoords)) return NULL;
3177 if (io -> Read(io, prefix, 32, 1) != 1) return NULL;
3178 if (io -> Read(io, suffix, 32, 1) != 1) return NULL;
3180 prefix[31] = suffix[31] = 0;
3182 v = cmsAllocNamedColorList(self ->ContextID, count, nDeviceCoords, prefix, suffix);
3183 if (v == NULL) {
3184 cmsSignalError(self->ContextID, cmsERROR_RANGE, "Too many named colors '%d'", count);
3185 return NULL;
3188 if (nDeviceCoords > cmsMAXCHANNELS) {
3189 cmsSignalError(self->ContextID, cmsERROR_RANGE, "Too many device coordinates '%d'", nDeviceCoords);
3190 goto Error;
3192 for (i=0; i < count; i++) {
3194 cmsUInt16Number PCS[3];
3195 cmsUInt16Number Colorant[cmsMAXCHANNELS];
3196 char Root[33];
3198 memset(Colorant, 0, sizeof(Colorant));
3199 if (io -> Read(io, Root, 32, 1) != 1) goto Error;
3200 Root[32] = 0; // To prevent exploits
3202 if (!_cmsReadUInt16Array(io, 3, PCS)) goto Error;
3203 if (!_cmsReadUInt16Array(io, nDeviceCoords, Colorant)) goto Error;
3205 if (!cmsAppendNamedColor(v, Root, PCS, Colorant)) goto Error;
3208 *nItems = 1;
3209 return (void*) v ;
3211 Error:
3212 cmsFreeNamedColorList(v);
3213 return NULL;
3215 cmsUNUSED_PARAMETER(SizeOfTag);
3219 // Saves a named color list into a named color profile
3220 static
3221 cmsBool Type_NamedColor_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
3223 cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) Ptr;
3224 char prefix[33]; // Prefix for each color name
3225 char suffix[33]; // Suffix for each color name
3226 cmsUInt32Number i, nColors;
3228 nColors = cmsNamedColorCount(NamedColorList);
3230 if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
3231 if (!_cmsWriteUInt32Number(io, nColors)) return FALSE;
3232 if (!_cmsWriteUInt32Number(io, NamedColorList ->ColorantCount)) return FALSE;
3234 memcpy(prefix, (const char*) NamedColorList->Prefix, sizeof(prefix));
3235 memcpy(suffix, (const char*) NamedColorList->Suffix, sizeof(suffix));
3237 suffix[32] = prefix[32] = 0;
3239 if (!io ->Write(io, 32, prefix)) return FALSE;
3240 if (!io ->Write(io, 32, suffix)) return FALSE;
3242 for (i=0; i < nColors; i++) {
3244 cmsUInt16Number PCS[3];
3245 cmsUInt16Number Colorant[cmsMAXCHANNELS];
3246 char Root[cmsMAX_PATH];
3248 memset(Root, 0, sizeof(Root));
3249 memset(PCS, 0, sizeof(PCS));
3250 memset(Colorant, 0, sizeof(Colorant));
3252 if (!cmsNamedColorInfo(NamedColorList, i, Root, NULL, NULL, PCS, Colorant)) return 0;
3253 Root[32] = 0;
3254 if (!io ->Write(io, 32 , Root)) return FALSE;
3255 if (!_cmsWriteUInt16Array(io, 3, PCS)) return FALSE;
3256 if (!_cmsWriteUInt16Array(io, NamedColorList ->ColorantCount, Colorant)) return FALSE;
3259 return TRUE;
3261 cmsUNUSED_PARAMETER(nItems);
3262 cmsUNUSED_PARAMETER(self);
3265 static
3266 void* Type_NamedColor_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n)
3268 cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) Ptr;
3270 return (void*) cmsDupNamedColorList(nc);
3272 cmsUNUSED_PARAMETER(n);
3273 cmsUNUSED_PARAMETER(self);
3277 static
3278 void Type_NamedColor_Free(struct _cms_typehandler_struct* self, void* Ptr)
3280 cmsFreeNamedColorList((cmsNAMEDCOLORLIST*) Ptr);
3281 return;
3283 cmsUNUSED_PARAMETER(self);
3287 // ********************************************************************************
3288 // Type cmsSigProfileSequenceDescType
3289 // ********************************************************************************
3291 // This type is an array of structures, each of which contains information from the
3292 // header fields and tags from the original profiles which were combined to create
3293 // the final profile. The order of the structures is the order in which the profiles
3294 // were combined and includes a structure for the final profile. This provides a
3295 // description of the profile sequence from source to destination,
3296 // typically used with the DeviceLink profile.
3298 static
3299 cmsBool ReadEmbeddedText(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU** mlu, cmsUInt32Number SizeOfTag)
3301 cmsTagTypeSignature BaseType;
3302 cmsUInt32Number nItems;
3304 BaseType = _cmsReadTypeBase(io);
3306 switch (BaseType) {
3308 case cmsSigTextType:
3309 if (*mlu) cmsMLUfree(*mlu);
3310 *mlu = (cmsMLU*)Type_Text_Read(self, io, &nItems, SizeOfTag);
3311 return (*mlu != NULL);
3313 case cmsSigTextDescriptionType:
3314 if (*mlu) cmsMLUfree(*mlu);
3315 *mlu = (cmsMLU*) Type_Text_Description_Read(self, io, &nItems, SizeOfTag);
3316 return (*mlu != NULL);
3319 TBD: Size is needed for MLU, and we have no idea on which is the available size
3322 case cmsSigMultiLocalizedUnicodeType:
3323 if (*mlu) cmsMLUfree(*mlu);
3324 *mlu = (cmsMLU*) Type_MLU_Read(self, io, &nItems, SizeOfTag);
3325 return (*mlu != NULL);
3327 default: return FALSE;
3332 static
3333 void *Type_ProfileSequenceDesc_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
3335 cmsSEQ* OutSeq;
3336 cmsUInt32Number i, Count;
3338 *nItems = 0;
3340 if (!_cmsReadUInt32Number(io, &Count)) return NULL;
3342 if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL;
3343 SizeOfTag -= sizeof(cmsUInt32Number);
3346 OutSeq = cmsAllocProfileSequenceDescription(self ->ContextID, Count);
3347 if (OutSeq == NULL) return NULL;
3349 OutSeq ->n = Count;
3351 // Get structures as well
3353 for (i=0; i < Count; i++) {
3355 cmsPSEQDESC* sec = &OutSeq -> seq[i];
3357 if (!_cmsReadUInt32Number(io, &sec ->deviceMfg)) goto Error;
3358 if (SizeOfTag < sizeof(cmsUInt32Number)) goto Error;
3359 SizeOfTag -= sizeof(cmsUInt32Number);
3361 if (!_cmsReadUInt32Number(io, &sec ->deviceModel)) goto Error;
3362 if (SizeOfTag < sizeof(cmsUInt32Number)) goto Error;
3363 SizeOfTag -= sizeof(cmsUInt32Number);
3365 if (!_cmsReadUInt64Number(io, &sec ->attributes)) goto Error;
3366 if (SizeOfTag < sizeof(cmsUInt64Number)) goto Error;
3367 SizeOfTag -= sizeof(cmsUInt64Number);
3369 if (!_cmsReadUInt32Number(io, (cmsUInt32Number *)&sec ->technology)) goto Error;
3370 if (SizeOfTag < sizeof(cmsUInt32Number)) goto Error;
3371 SizeOfTag -= sizeof(cmsUInt32Number);
3373 if (!ReadEmbeddedText(self, io, &sec ->Manufacturer, SizeOfTag)) goto Error;
3374 if (!ReadEmbeddedText(self, io, &sec ->Model, SizeOfTag)) goto Error;
3377 *nItems = 1;
3378 return OutSeq;
3380 Error:
3381 cmsFreeProfileSequenceDescription(OutSeq);
3382 return NULL;
3386 // Aux--Embed a text description type. It can be of type text description or multilocalized unicode
3387 // and it depends of the version number passed on cmsTagDescriptor structure instead of stack
3388 static
3389 cmsBool SaveDescription(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* Text)
3391 if (self ->ICCVersion < 0x4000000) {
3393 if (!_cmsWriteTypeBase(io, cmsSigTextDescriptionType)) return FALSE;
3394 return Type_Text_Description_Write(self, io, Text, 1);
3396 else {
3397 if (!_cmsWriteTypeBase(io, cmsSigMultiLocalizedUnicodeType)) return FALSE;
3398 return Type_MLU_Write(self, io, Text, 1);
3403 static
3404 cmsBool Type_ProfileSequenceDesc_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
3406 cmsSEQ* Seq = (cmsSEQ*) Ptr;
3407 cmsUInt32Number i;
3409 if (!_cmsWriteUInt32Number(io, Seq->n)) return FALSE;
3411 for (i=0; i < Seq ->n; i++) {
3413 cmsPSEQDESC* sec = &Seq -> seq[i];
3415 if (!_cmsWriteUInt32Number(io, sec ->deviceMfg)) return FALSE;
3416 if (!_cmsWriteUInt32Number(io, sec ->deviceModel)) return FALSE;
3417 if (!_cmsWriteUInt64Number(io, &sec ->attributes)) return FALSE;
3418 if (!_cmsWriteUInt32Number(io, sec ->technology)) return FALSE;
3420 if (!SaveDescription(self, io, sec ->Manufacturer)) return FALSE;
3421 if (!SaveDescription(self, io, sec ->Model)) return FALSE;
3424 return TRUE;
3426 cmsUNUSED_PARAMETER(nItems);
3430 static
3431 void* Type_ProfileSequenceDesc_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n)
3433 return (void*) cmsDupProfileSequenceDescription((cmsSEQ*) Ptr);
3435 cmsUNUSED_PARAMETER(n);
3436 cmsUNUSED_PARAMETER(self);
3439 static
3440 void Type_ProfileSequenceDesc_Free(struct _cms_typehandler_struct* self, void* Ptr)
3442 cmsFreeProfileSequenceDescription((cmsSEQ*) Ptr);
3443 return;
3445 cmsUNUSED_PARAMETER(self);
3449 // ********************************************************************************
3450 // Type cmsSigProfileSequenceIdType
3451 // ********************************************************************************
3453 In certain workflows using ICC Device Link Profiles, it is necessary to identify the
3454 original profiles that were combined to create the Device Link Profile.
3455 This type is an array of structures, each of which contains information for
3456 identification of a profile used in a sequence
3460 static
3461 cmsBool ReadSeqID(struct _cms_typehandler_struct* self,
3462 cmsIOHANDLER* io,
3463 void* Cargo,
3464 cmsUInt32Number n,
3465 cmsUInt32Number SizeOfTag)
3467 cmsSEQ* OutSeq = (cmsSEQ*) Cargo;
3468 cmsPSEQDESC* seq = &OutSeq ->seq[n];
3470 if (io -> Read(io, seq ->ProfileID.ID8, 16, 1) != 1) return FALSE;
3471 if (!ReadEmbeddedText(self, io, &seq ->Description, SizeOfTag)) return FALSE;
3473 return TRUE;
3478 static
3479 void *Type_ProfileSequenceId_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
3481 cmsSEQ* OutSeq;
3482 cmsUInt32Number Count;
3483 cmsUInt32Number BaseOffset;
3485 *nItems = 0;
3487 // Get actual position as a basis for element offsets
3488 BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
3490 // Get table count
3491 if (!_cmsReadUInt32Number(io, &Count)) return NULL;
3493 // Allocate an empty structure
3494 OutSeq = cmsAllocProfileSequenceDescription(self ->ContextID, Count);
3495 if (OutSeq == NULL) return NULL;
3498 // Read the position table
3499 if (!ReadPositionTable(self, io, Count, BaseOffset, OutSeq, ReadSeqID)) {
3501 cmsFreeProfileSequenceDescription(OutSeq);
3502 return NULL;
3505 // Success
3506 *nItems = 1;
3507 return OutSeq;
3509 cmsUNUSED_PARAMETER(SizeOfTag);
3513 static
3514 cmsBool WriteSeqID(struct _cms_typehandler_struct* self,
3515 cmsIOHANDLER* io,
3516 void* Cargo,
3517 cmsUInt32Number n,
3518 cmsUInt32Number SizeOfTag)
3520 cmsSEQ* Seq = (cmsSEQ*) Cargo;
3522 if (!io ->Write(io, 16, Seq ->seq[n].ProfileID.ID8)) return FALSE;
3524 // Store here the MLU
3525 if (!SaveDescription(self, io, Seq ->seq[n].Description)) return FALSE;
3527 return TRUE;
3529 cmsUNUSED_PARAMETER(SizeOfTag);
3532 static
3533 cmsBool Type_ProfileSequenceId_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
3535 cmsSEQ* Seq = (cmsSEQ*) Ptr;
3536 cmsUInt32Number BaseOffset;
3538 // Keep the base offset
3539 BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
3541 // This is the table count
3542 if (!_cmsWriteUInt32Number(io, Seq ->n)) return FALSE;
3544 // This is the position table and content
3545 if (!WritePositionTable(self, io, 0, Seq ->n, BaseOffset, Seq, WriteSeqID)) return FALSE;
3547 return TRUE;
3549 cmsUNUSED_PARAMETER(nItems);
3552 static
3553 void* Type_ProfileSequenceId_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n)
3555 return (void*) cmsDupProfileSequenceDescription((cmsSEQ*) Ptr);
3557 cmsUNUSED_PARAMETER(n);
3558 cmsUNUSED_PARAMETER(self);
3561 static
3562 void Type_ProfileSequenceId_Free(struct _cms_typehandler_struct* self, void* Ptr)
3564 cmsFreeProfileSequenceDescription((cmsSEQ*) Ptr);
3565 return;
3567 cmsUNUSED_PARAMETER(self);
3571 // ********************************************************************************
3572 // Type cmsSigUcrBgType
3573 // ********************************************************************************
3575 This type contains curves representing the under color removal and black
3576 generation and a text string which is a general description of the method used
3577 for the ucr/bg.
3580 static
3581 void *Type_UcrBg_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
3583 cmsUcrBg* n = (cmsUcrBg*) _cmsMallocZero(self ->ContextID, sizeof(cmsUcrBg));
3584 cmsUInt32Number CountUcr, CountBg;
3585 cmsInt32Number SignedSizeOfTag = (cmsInt32Number)SizeOfTag;
3586 char* ASCIIString;
3588 *nItems = 0;
3589 if (n == NULL) return NULL;
3591 // First curve is Under color removal
3593 if (SignedSizeOfTag < (cmsInt32Number) sizeof(cmsUInt32Number)) return NULL;
3594 if (!_cmsReadUInt32Number(io, &CountUcr)) return NULL;
3595 SignedSizeOfTag -= sizeof(cmsUInt32Number);
3597 n ->Ucr = cmsBuildTabulatedToneCurve16(self ->ContextID, CountUcr, NULL);
3598 if (n ->Ucr == NULL) goto error;
3600 if (SignedSizeOfTag < (cmsInt32Number)(CountUcr * sizeof(cmsUInt16Number))) goto error;
3601 if (!_cmsReadUInt16Array(io, CountUcr, n ->Ucr->Table16)) goto error;
3603 SignedSizeOfTag -= CountUcr * sizeof(cmsUInt16Number);
3605 // Second curve is Black generation
3607 if (SignedSizeOfTag < (cmsInt32Number)sizeof(cmsUInt32Number)) goto error;
3608 if (!_cmsReadUInt32Number(io, &CountBg)) goto error;
3609 SignedSizeOfTag -= sizeof(cmsUInt32Number);
3611 n ->Bg = cmsBuildTabulatedToneCurve16(self ->ContextID, CountBg, NULL);
3612 if (n ->Bg == NULL) goto error;
3614 if (SignedSizeOfTag < (cmsInt32Number) (CountBg * sizeof(cmsUInt16Number))) goto error;
3615 if (!_cmsReadUInt16Array(io, CountBg, n ->Bg->Table16)) goto error;
3616 SignedSizeOfTag -= CountBg * sizeof(cmsUInt16Number);
3618 if (SignedSizeOfTag < 0 || SignedSizeOfTag > 32000) goto error;
3620 // Now comes the text. The length is specified by the tag size
3621 n ->Desc = cmsMLUalloc(self ->ContextID, 1);
3622 if (n ->Desc == NULL) goto error;
3624 ASCIIString = (char*) _cmsMalloc(self ->ContextID, SignedSizeOfTag + 1);
3625 if (io->Read(io, ASCIIString, sizeof(char), SignedSizeOfTag) != (cmsUInt32Number)SignedSizeOfTag)
3627 _cmsFree(self->ContextID, ASCIIString);
3628 goto error;
3631 ASCIIString[SignedSizeOfTag] = 0;
3632 cmsMLUsetASCII(n ->Desc, cmsNoLanguage, cmsNoCountry, ASCIIString);
3633 _cmsFree(self ->ContextID, ASCIIString);
3635 *nItems = 1;
3636 return (void*) n;
3638 error:
3640 if (n->Ucr) cmsFreeToneCurve(n->Ucr);
3641 if (n->Bg) cmsFreeToneCurve(n->Bg);
3642 if (n->Desc) cmsMLUfree(n->Desc);
3643 _cmsFree(self->ContextID, n);
3644 *nItems = 0;
3645 return NULL;
3649 static
3650 cmsBool Type_UcrBg_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
3652 cmsUcrBg* Value = (cmsUcrBg*) Ptr;
3653 cmsUInt32Number TextSize;
3654 char* Text;
3656 // First curve is Under color removal
3657 if (!_cmsWriteUInt32Number(io, Value ->Ucr ->nEntries)) return FALSE;
3658 if (!_cmsWriteUInt16Array(io, Value ->Ucr ->nEntries, Value ->Ucr ->Table16)) return FALSE;
3660 // Then black generation
3661 if (!_cmsWriteUInt32Number(io, Value ->Bg ->nEntries)) return FALSE;
3662 if (!_cmsWriteUInt16Array(io, Value ->Bg ->nEntries, Value ->Bg ->Table16)) return FALSE;
3664 // Now comes the text. The length is specified by the tag size
3665 TextSize = cmsMLUgetASCII(Value ->Desc, cmsNoLanguage, cmsNoCountry, NULL, 0);
3666 Text = (char*) _cmsMalloc(self ->ContextID, TextSize);
3667 if (cmsMLUgetASCII(Value ->Desc, cmsNoLanguage, cmsNoCountry, Text, TextSize) != TextSize) return FALSE;
3669 if (!io ->Write(io, TextSize, Text)) return FALSE;
3670 _cmsFree(self ->ContextID, Text);
3672 return TRUE;
3674 cmsUNUSED_PARAMETER(nItems);
3677 static
3678 void* Type_UcrBg_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
3680 cmsUcrBg* Src = (cmsUcrBg*) Ptr;
3681 cmsUcrBg* NewUcrBg = (cmsUcrBg*) _cmsMallocZero(self ->ContextID, sizeof(cmsUcrBg));
3683 if (NewUcrBg == NULL) return NULL;
3685 NewUcrBg ->Bg = cmsDupToneCurve(Src ->Bg);
3686 NewUcrBg ->Ucr = cmsDupToneCurve(Src ->Ucr);
3687 NewUcrBg ->Desc = cmsMLUdup(Src ->Desc);
3689 return (void*) NewUcrBg;
3691 cmsUNUSED_PARAMETER(n);
3694 static
3695 void Type_UcrBg_Free(struct _cms_typehandler_struct* self, void *Ptr)
3697 cmsUcrBg* Src = (cmsUcrBg*) Ptr;
3699 if (Src ->Ucr) cmsFreeToneCurve(Src ->Ucr);
3700 if (Src ->Bg) cmsFreeToneCurve(Src ->Bg);
3701 if (Src ->Desc) cmsMLUfree(Src ->Desc);
3703 _cmsFree(self ->ContextID, Ptr);
3706 // ********************************************************************************
3707 // Type cmsSigCrdInfoType
3708 // ********************************************************************************
3711 This type contains the PostScript product name to which this profile corresponds
3712 and the names of the companion CRDs. Recall that a single profile can generate
3713 multiple CRDs. It is implemented as a MLU being the language code "PS" and then
3714 country varies for each element:
3716 nm: PostScript product name
3717 #0: Rendering intent 0 CRD name
3718 #1: Rendering intent 1 CRD name
3719 #2: Rendering intent 2 CRD name
3720 #3: Rendering intent 3 CRD name
3725 // Auxiliary, read an string specified as count + string
3726 static
3727 cmsBool ReadCountAndString(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* mlu, cmsUInt32Number* SizeOfTag, const char* Section)
3729 cmsUInt32Number Count;
3730 char* Text;
3732 if (*SizeOfTag < sizeof(cmsUInt32Number)) return FALSE;
3734 if (!_cmsReadUInt32Number(io, &Count)) return FALSE;
3736 if (Count > UINT_MAX - sizeof(cmsUInt32Number)) return FALSE;
3737 if (*SizeOfTag < Count + sizeof(cmsUInt32Number)) return FALSE;
3739 Text = (char*) _cmsMalloc(self ->ContextID, Count+1);
3740 if (Text == NULL) return FALSE;
3742 if (io ->Read(io, Text, sizeof(cmsUInt8Number), Count) != Count) {
3743 _cmsFree(self ->ContextID, Text);
3744 return FALSE;
3747 Text[Count] = 0;
3749 cmsMLUsetASCII(mlu, "PS", Section, Text);
3750 _cmsFree(self ->ContextID, Text);
3752 *SizeOfTag -= (Count + sizeof(cmsUInt32Number));
3753 return TRUE;
3756 static
3757 cmsBool WriteCountAndString(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* mlu, const char* Section)
3759 cmsUInt32Number TextSize;
3760 char* Text;
3762 TextSize = cmsMLUgetASCII(mlu, "PS", Section, NULL, 0);
3763 Text = (char*) _cmsMalloc(self ->ContextID, TextSize);
3765 if (!_cmsWriteUInt32Number(io, TextSize)) return FALSE;
3767 if (cmsMLUgetASCII(mlu, "PS", Section, Text, TextSize) == 0) return FALSE;
3769 if (!io ->Write(io, TextSize, Text)) return FALSE;
3770 _cmsFree(self ->ContextID, Text);
3772 return TRUE;
3775 static
3776 void *Type_CrdInfo_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
3778 cmsMLU* mlu = cmsMLUalloc(self ->ContextID, 5);
3780 *nItems = 0;
3781 if (!ReadCountAndString(self, io, mlu, &SizeOfTag, "nm")) goto Error;
3782 if (!ReadCountAndString(self, io, mlu, &SizeOfTag, "#0")) goto Error;
3783 if (!ReadCountAndString(self, io, mlu, &SizeOfTag, "#1")) goto Error;
3784 if (!ReadCountAndString(self, io, mlu, &SizeOfTag, "#2")) goto Error;
3785 if (!ReadCountAndString(self, io, mlu, &SizeOfTag, "#3")) goto Error;
3787 *nItems = 1;
3788 return (void*) mlu;
3790 Error:
3791 cmsMLUfree(mlu);
3792 return NULL;
3796 static
3797 cmsBool Type_CrdInfo_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
3800 cmsMLU* mlu = (cmsMLU*) Ptr;
3802 if (!WriteCountAndString(self, io, mlu, "nm")) goto Error;
3803 if (!WriteCountAndString(self, io, mlu, "#0")) goto Error;
3804 if (!WriteCountAndString(self, io, mlu, "#1")) goto Error;
3805 if (!WriteCountAndString(self, io, mlu, "#2")) goto Error;
3806 if (!WriteCountAndString(self, io, mlu, "#3")) goto Error;
3808 return TRUE;
3810 Error:
3811 return FALSE;
3813 cmsUNUSED_PARAMETER(nItems);
3817 static
3818 void* Type_CrdInfo_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
3820 return (void*) cmsMLUdup((cmsMLU*) Ptr);
3822 cmsUNUSED_PARAMETER(n);
3823 cmsUNUSED_PARAMETER(self);
3826 static
3827 void Type_CrdInfo_Free(struct _cms_typehandler_struct* self, void *Ptr)
3829 cmsMLUfree((cmsMLU*) Ptr);
3830 return;
3832 cmsUNUSED_PARAMETER(self);
3835 // ********************************************************************************
3836 // Type cmsSigScreeningType
3837 // ********************************************************************************
3839 //The screeningType describes various screening parameters including screen
3840 //frequency, screening angle, and spot shape.
3842 static
3843 void *Type_Screening_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
3845 cmsScreening* sc = NULL;
3846 cmsUInt32Number i;
3848 sc = (cmsScreening*) _cmsMallocZero(self ->ContextID, sizeof(cmsScreening));
3849 if (sc == NULL) return NULL;
3851 *nItems = 0;
3853 if (!_cmsReadUInt32Number(io, &sc ->Flag)) goto Error;
3854 if (!_cmsReadUInt32Number(io, &sc ->nChannels)) goto Error;
3856 if (sc ->nChannels > cmsMAXCHANNELS - 1)
3857 sc ->nChannels = cmsMAXCHANNELS - 1;
3859 for (i=0; i < sc ->nChannels; i++) {
3861 if (!_cmsRead15Fixed16Number(io, &sc ->Channels[i].Frequency)) goto Error;
3862 if (!_cmsRead15Fixed16Number(io, &sc ->Channels[i].ScreenAngle)) goto Error;
3863 if (!_cmsReadUInt32Number(io, &sc ->Channels[i].SpotShape)) goto Error;
3867 *nItems = 1;
3869 return (void*) sc;
3871 Error:
3872 if (sc != NULL)
3873 _cmsFree(self ->ContextID, sc);
3875 return NULL;
3877 cmsUNUSED_PARAMETER(SizeOfTag);
3881 static
3882 cmsBool Type_Screening_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
3884 cmsScreening* sc = (cmsScreening* ) Ptr;
3885 cmsUInt32Number i;
3887 if (!_cmsWriteUInt32Number(io, sc ->Flag)) return FALSE;
3888 if (!_cmsWriteUInt32Number(io, sc ->nChannels)) return FALSE;
3890 for (i=0; i < sc ->nChannels; i++) {
3892 if (!_cmsWrite15Fixed16Number(io, sc ->Channels[i].Frequency)) return FALSE;
3893 if (!_cmsWrite15Fixed16Number(io, sc ->Channels[i].ScreenAngle)) return FALSE;
3894 if (!_cmsWriteUInt32Number(io, sc ->Channels[i].SpotShape)) return FALSE;
3897 return TRUE;
3899 cmsUNUSED_PARAMETER(nItems);
3900 cmsUNUSED_PARAMETER(self);
3904 static
3905 void* Type_Screening_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
3907 return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsScreening));
3909 cmsUNUSED_PARAMETER(n);
3913 static
3914 void Type_Screening_Free(struct _cms_typehandler_struct* self, void* Ptr)
3916 _cmsFree(self ->ContextID, Ptr);
3919 // ********************************************************************************
3920 // Type cmsSigViewingConditionsType
3921 // ********************************************************************************
3923 //This type represents a set of viewing condition parameters including:
3924 //CIE 'absolute' illuminant white point tristimulus values and CIE 'absolute'
3925 //surround tristimulus values.
3927 static
3928 void *Type_ViewingConditions_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
3930 cmsICCViewingConditions* vc = NULL;
3932 vc = (cmsICCViewingConditions*) _cmsMallocZero(self ->ContextID, sizeof(cmsICCViewingConditions));
3933 if (vc == NULL) return NULL;
3935 *nItems = 0;
3937 if (!_cmsReadXYZNumber(io, &vc ->IlluminantXYZ)) goto Error;
3938 if (!_cmsReadXYZNumber(io, &vc ->SurroundXYZ)) goto Error;
3939 if (!_cmsReadUInt32Number(io, &vc ->IlluminantType)) goto Error;
3941 *nItems = 1;
3943 return (void*) vc;
3945 Error:
3946 if (vc != NULL)
3947 _cmsFree(self ->ContextID, vc);
3949 return NULL;
3951 cmsUNUSED_PARAMETER(SizeOfTag);
3955 static
3956 cmsBool Type_ViewingConditions_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
3958 cmsICCViewingConditions* sc = (cmsICCViewingConditions* ) Ptr;
3960 if (!_cmsWriteXYZNumber(io, &sc ->IlluminantXYZ)) return FALSE;
3961 if (!_cmsWriteXYZNumber(io, &sc ->SurroundXYZ)) return FALSE;
3962 if (!_cmsWriteUInt32Number(io, sc ->IlluminantType)) return FALSE;
3964 return TRUE;
3966 cmsUNUSED_PARAMETER(nItems);
3967 cmsUNUSED_PARAMETER(self);
3971 static
3972 void* Type_ViewingConditions_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
3974 return _cmsDupMem(self->ContextID, Ptr, sizeof(cmsICCViewingConditions));
3976 cmsUNUSED_PARAMETER(n);
3980 static
3981 void Type_ViewingConditions_Free(struct _cms_typehandler_struct* self, void* Ptr)
3983 _cmsFree(self ->ContextID, Ptr);
3987 // ********************************************************************************
3988 // Type cmsSigMultiProcessElementType
3989 // ********************************************************************************
3992 static
3993 void* GenericMPEdup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
3995 return (void*) cmsStageDup((cmsStage*) Ptr);
3997 cmsUNUSED_PARAMETER(n);
3998 cmsUNUSED_PARAMETER(self);
4001 static
4002 void GenericMPEfree(struct _cms_typehandler_struct* self, void *Ptr)
4004 cmsStageFree((cmsStage*) Ptr);
4005 return;
4007 cmsUNUSED_PARAMETER(self);
4010 // Each curve is stored in one or more curve segments, with break-points specified between curve segments.
4011 // The first curve segment always starts at -Infinity, and the last curve segment always ends at +Infinity. The
4012 // first and last curve segments shall be specified in terms of a formula, whereas the other segments shall be
4013 // specified either in terms of a formula, or by a sampled curve.
4016 // Read an embedded segmented curve
4017 static
4018 cmsToneCurve* ReadSegmentedCurve(struct _cms_typehandler_struct* self, cmsIOHANDLER* io)
4020 cmsCurveSegSignature ElementSig;
4021 cmsUInt32Number i, j;
4022 cmsUInt16Number nSegments;
4023 cmsCurveSegment* Segments;
4024 cmsToneCurve* Curve;
4025 cmsFloat32Number PrevBreak = MINUS_INF; // - infinite
4027 // Take signature and channels for each element.
4028 if (!_cmsReadUInt32Number(io, (cmsUInt32Number*) &ElementSig)) return NULL;
4030 // That should be a segmented curve
4031 if (ElementSig != cmsSigSegmentedCurve) return NULL;
4033 if (!_cmsReadUInt32Number(io, NULL)) return NULL;
4034 if (!_cmsReadUInt16Number(io, &nSegments)) return NULL;
4035 if (!_cmsReadUInt16Number(io, NULL)) return NULL;
4037 if (nSegments < 1) return NULL;
4038 Segments = (cmsCurveSegment*) _cmsCalloc(self ->ContextID, nSegments, sizeof(cmsCurveSegment));
4039 if (Segments == NULL) return NULL;
4041 // Read breakpoints
4042 for (i=0; i < (cmsUInt32Number) nSegments - 1; i++) {
4044 Segments[i].x0 = PrevBreak;
4045 if (!_cmsReadFloat32Number(io, &Segments[i].x1)) goto Error;
4046 PrevBreak = Segments[i].x1;
4049 Segments[nSegments-1].x0 = PrevBreak;
4050 Segments[nSegments-1].x1 = PLUS_INF; // A big cmsFloat32Number number
4052 // Read segments
4053 for (i=0; i < nSegments; i++) {
4055 if (!_cmsReadUInt32Number(io, (cmsUInt32Number*) &ElementSig)) goto Error;
4056 if (!_cmsReadUInt32Number(io, NULL)) goto Error;
4058 switch (ElementSig) {
4060 case cmsSigFormulaCurveSeg: {
4062 cmsUInt16Number Type;
4063 cmsUInt32Number ParamsByType[] = { 4, 5, 5 };
4065 if (!_cmsReadUInt16Number(io, &Type)) goto Error;
4066 if (!_cmsReadUInt16Number(io, NULL)) goto Error;
4068 Segments[i].Type = Type + 6;
4069 if (Type > 2) goto Error;
4071 for (j = 0; j < ParamsByType[Type]; j++) {
4073 cmsFloat32Number f;
4074 if (!_cmsReadFloat32Number(io, &f)) goto Error;
4075 Segments[i].Params[j] = f;
4078 break;
4081 case cmsSigSampledCurveSeg: {
4082 cmsUInt32Number Count;
4084 if (!_cmsReadUInt32Number(io, &Count)) goto Error;
4086 // The first point is implicit in the last stage, we allocate an extra note to be populated latter on
4087 Count++;
4088 Segments[i].nGridPoints = Count;
4089 Segments[i].SampledPoints = (cmsFloat32Number*)_cmsCalloc(self->ContextID, Count, sizeof(cmsFloat32Number));
4090 if (Segments[i].SampledPoints == NULL) goto Error;
4092 Segments[i].SampledPoints[0] = 0;
4093 for (j = 1; j < Count; j++) {
4094 if (!_cmsReadFloat32Number(io, &Segments[i].SampledPoints[j])) goto Error;
4097 break;
4099 default:
4101 char String[5];
4103 _cmsTagSignature2String(String, (cmsTagSignature) ElementSig);
4104 cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown curve element type '%s' found.", String);
4106 goto Error;
4111 Curve = cmsBuildSegmentedToneCurve(self ->ContextID, nSegments, Segments);
4113 for (i=0; i < nSegments; i++) {
4114 if (Segments[i].SampledPoints) _cmsFree(self ->ContextID, Segments[i].SampledPoints);
4116 _cmsFree(self ->ContextID, Segments);
4118 // Explore for missing implicit points
4119 for (i = 0; i < nSegments; i++) {
4121 // If sampled curve, fix it
4122 if (Curve->Segments[i].Type == 0) {
4124 Curve->Segments[i].SampledPoints[0] = cmsEvalToneCurveFloat(Curve, Curve->Segments[i].x0);
4128 return Curve;
4130 Error:
4131 if (Segments) {
4132 for (i=0; i < nSegments; i++) {
4133 if (Segments[i].SampledPoints) _cmsFree(self ->ContextID, Segments[i].SampledPoints);
4135 _cmsFree(self ->ContextID, Segments);
4137 return NULL;
4141 static
4142 cmsBool ReadMPECurve(struct _cms_typehandler_struct* self,
4143 cmsIOHANDLER* io,
4144 void* Cargo,
4145 cmsUInt32Number n,
4146 cmsUInt32Number SizeOfTag)
4148 cmsToneCurve** GammaTables = ( cmsToneCurve**) Cargo;
4150 GammaTables[n] = ReadSegmentedCurve(self, io);
4151 return (GammaTables[n] != NULL);
4153 cmsUNUSED_PARAMETER(SizeOfTag);
4156 static
4157 void *Type_MPEcurve_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
4159 cmsStage* mpe = NULL;
4160 cmsUInt16Number InputChans, OutputChans;
4161 cmsUInt32Number i, BaseOffset;
4162 cmsToneCurve** GammaTables;
4164 *nItems = 0;
4166 // Get actual position as a basis for element offsets
4167 BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
4169 if (!_cmsReadUInt16Number(io, &InputChans)) return NULL;
4170 if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL;
4172 if (InputChans != OutputChans) return NULL;
4174 GammaTables = (cmsToneCurve**) _cmsCalloc(self ->ContextID, InputChans, sizeof(cmsToneCurve*));
4175 if (GammaTables == NULL) return NULL;
4177 if (ReadPositionTable(self, io, InputChans, BaseOffset, GammaTables, ReadMPECurve)) {
4179 mpe = cmsStageAllocToneCurves(self ->ContextID, InputChans, GammaTables);
4181 else {
4182 mpe = NULL;
4185 for (i=0; i < InputChans; i++) {
4186 if (GammaTables[i]) cmsFreeToneCurve(GammaTables[i]);
4189 _cmsFree(self ->ContextID, GammaTables);
4190 *nItems = (mpe != NULL) ? 1U : 0;
4191 return mpe;
4193 cmsUNUSED_PARAMETER(SizeOfTag);
4197 // Write a single segmented curve. NO CHECK IS PERFORMED ON VALIDITY
4198 static
4199 cmsBool WriteSegmentedCurve(cmsIOHANDLER* io, cmsToneCurve* g)
4201 cmsUInt32Number i, j;
4202 cmsCurveSegment* Segments = g ->Segments;
4203 cmsUInt32Number nSegments = g ->nSegments;
4205 if (!_cmsWriteUInt32Number(io, cmsSigSegmentedCurve)) goto Error;
4206 if (!_cmsWriteUInt32Number(io, 0)) goto Error;
4207 if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) nSegments)) goto Error;
4208 if (!_cmsWriteUInt16Number(io, 0)) goto Error;
4210 // Write the break-points
4211 for (i=0; i < nSegments - 1; i++) {
4212 if (!_cmsWriteFloat32Number(io, Segments[i].x1)) goto Error;
4215 // Write the segments
4216 for (i=0; i < g ->nSegments; i++) {
4218 cmsCurveSegment* ActualSeg = Segments + i;
4220 if (ActualSeg -> Type == 0) {
4222 // This is a sampled curve. First point is implicit in the ICC format, but not in our representation
4223 if (!_cmsWriteUInt32Number(io, (cmsUInt32Number) cmsSigSampledCurveSeg)) goto Error;
4224 if (!_cmsWriteUInt32Number(io, 0)) goto Error;
4225 if (!_cmsWriteUInt32Number(io, ActualSeg -> nGridPoints - 1)) goto Error;
4227 for (j=1; j < g ->Segments[i].nGridPoints; j++) {
4228 if (!_cmsWriteFloat32Number(io, ActualSeg -> SampledPoints[j])) goto Error;
4232 else {
4233 int Type;
4234 cmsUInt32Number ParamsByType[] = { 4, 5, 5 };
4236 // This is a formula-based
4237 if (!_cmsWriteUInt32Number(io, (cmsUInt32Number) cmsSigFormulaCurveSeg)) goto Error;
4238 if (!_cmsWriteUInt32Number(io, 0)) goto Error;
4240 // We only allow 1, 2 and 3 as types
4241 Type = ActualSeg ->Type - 6;
4242 if (Type > 2 || Type < 0) goto Error;
4244 if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) Type)) goto Error;
4245 if (!_cmsWriteUInt16Number(io, 0)) goto Error;
4247 for (j=0; j < ParamsByType[Type]; j++) {
4248 if (!_cmsWriteFloat32Number(io, (cmsFloat32Number) ActualSeg ->Params[j])) goto Error;
4252 // It seems there is no need to align. Code is here, and for safety commented out
4253 // if (!_cmsWriteAlignment(io)) goto Error;
4256 return TRUE;
4258 Error:
4259 return FALSE;
4263 static
4264 cmsBool WriteMPECurve(struct _cms_typehandler_struct* self,
4265 cmsIOHANDLER* io,
4266 void* Cargo,
4267 cmsUInt32Number n,
4268 cmsUInt32Number SizeOfTag)
4270 _cmsStageToneCurvesData* Curves = (_cmsStageToneCurvesData*) Cargo;
4272 return WriteSegmentedCurve(io, Curves ->TheCurves[n]);
4274 cmsUNUSED_PARAMETER(SizeOfTag);
4275 cmsUNUSED_PARAMETER(self);
4278 // Write a curve, checking first for validity
4279 static
4280 cmsBool Type_MPEcurve_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
4282 cmsUInt32Number BaseOffset;
4283 cmsStage* mpe = (cmsStage*) Ptr;
4284 _cmsStageToneCurvesData* Curves = (_cmsStageToneCurvesData*) mpe ->Data;
4286 BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
4288 // Write the header. Since those are curves, input and output channels are same
4289 if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->InputChannels)) return FALSE;
4290 if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->InputChannels)) return FALSE;
4292 if (!WritePositionTable(self, io, 0,
4293 mpe ->InputChannels, BaseOffset, Curves, WriteMPECurve)) return FALSE;
4296 return TRUE;
4298 cmsUNUSED_PARAMETER(nItems);
4303 // The matrix is organized as an array of PxQ+Q elements, where P is the number of input channels to the
4304 // matrix, and Q is the number of output channels. The matrix elements are each float32Numbers. The array
4305 // is organized as follows:
4306 // array = [e11, e12, ..., e1P, e21, e22, ..., e2P, ..., eQ1, eQ2, ..., eQP, e1, e2, ..., eQ]
4308 static
4309 void *Type_MPEmatrix_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
4311 cmsStage* mpe;
4312 cmsUInt16Number InputChans, OutputChans;
4313 cmsUInt32Number nElems, i;
4314 cmsFloat64Number* Matrix;
4315 cmsFloat64Number* Offsets;
4317 if (!_cmsReadUInt16Number(io, &InputChans)) return NULL;
4318 if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL;
4321 // Input and output chans may be ANY (up to 0xffff),
4322 // but we choose to limit to 16 channels for now
4323 if (InputChans >= cmsMAXCHANNELS) return NULL;
4324 if (OutputChans >= cmsMAXCHANNELS) return NULL;
4326 nElems = (cmsUInt32Number) InputChans * OutputChans;
4328 Matrix = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, nElems, sizeof(cmsFloat64Number));
4329 if (Matrix == NULL) return NULL;
4331 Offsets = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, OutputChans, sizeof(cmsFloat64Number));
4332 if (Offsets == NULL) {
4334 _cmsFree(self ->ContextID, Matrix);
4335 return NULL;
4338 for (i=0; i < nElems; i++) {
4340 cmsFloat32Number v;
4342 if (!_cmsReadFloat32Number(io, &v)) {
4343 _cmsFree(self ->ContextID, Matrix);
4344 _cmsFree(self ->ContextID, Offsets);
4345 return NULL;
4347 Matrix[i] = v;
4351 for (i=0; i < OutputChans; i++) {
4353 cmsFloat32Number v;
4355 if (!_cmsReadFloat32Number(io, &v)) {
4356 _cmsFree(self ->ContextID, Matrix);
4357 _cmsFree(self ->ContextID, Offsets);
4358 return NULL;
4360 Offsets[i] = v;
4364 mpe = cmsStageAllocMatrix(self ->ContextID, OutputChans, InputChans, Matrix, Offsets);
4365 _cmsFree(self ->ContextID, Matrix);
4366 _cmsFree(self ->ContextID, Offsets);
4368 *nItems = 1;
4370 return mpe;
4372 cmsUNUSED_PARAMETER(SizeOfTag);
4375 static
4376 cmsBool Type_MPEmatrix_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
4378 cmsUInt32Number i, nElems;
4379 cmsStage* mpe = (cmsStage*) Ptr;
4380 _cmsStageMatrixData* Matrix = (_cmsStageMatrixData*) mpe ->Data;
4382 if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->InputChannels)) return FALSE;
4383 if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->OutputChannels)) return FALSE;
4385 nElems = mpe ->InputChannels * mpe ->OutputChannels;
4387 for (i=0; i < nElems; i++) {
4388 if (!_cmsWriteFloat32Number(io, (cmsFloat32Number) Matrix->Double[i])) return FALSE;
4392 for (i=0; i < mpe ->OutputChannels; i++) {
4394 if (Matrix ->Offset == NULL) {
4396 if (!_cmsWriteFloat32Number(io, 0)) return FALSE;
4398 else {
4399 if (!_cmsWriteFloat32Number(io, (cmsFloat32Number) Matrix->Offset[i])) return FALSE;
4403 return TRUE;
4405 cmsUNUSED_PARAMETER(nItems);
4406 cmsUNUSED_PARAMETER(self);
4411 static
4412 void *Type_MPEclut_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
4414 cmsStage* mpe = NULL;
4415 cmsUInt16Number InputChans, OutputChans;
4416 cmsUInt8Number Dimensions8[16];
4417 cmsUInt32Number i, nMaxGrids, GridPoints[MAX_INPUT_DIMENSIONS];
4418 _cmsStageCLutData* clut;
4420 if (!_cmsReadUInt16Number(io, &InputChans)) return NULL;
4421 if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL;
4423 if (InputChans == 0) goto Error;
4424 if (OutputChans == 0) goto Error;
4426 if (io ->Read(io, Dimensions8, sizeof(cmsUInt8Number), 16) != 16)
4427 goto Error;
4429 // Copy MAX_INPUT_DIMENSIONS at most. Expand to cmsUInt32Number
4430 nMaxGrids = InputChans > MAX_INPUT_DIMENSIONS ? (cmsUInt32Number) MAX_INPUT_DIMENSIONS : InputChans;
4432 for (i = 0; i < nMaxGrids; i++) {
4433 if (Dimensions8[i] == 1) goto Error; // Impossible value, 0 for no CLUT and then 2 at least
4434 GridPoints[i] = (cmsUInt32Number)Dimensions8[i];
4437 // Allocate the true CLUT
4438 mpe = cmsStageAllocCLutFloatGranular(self ->ContextID, GridPoints, InputChans, OutputChans, NULL);
4439 if (mpe == NULL) goto Error;
4441 // Read and sanitize the data
4442 clut = (_cmsStageCLutData*) mpe ->Data;
4443 for (i=0; i < clut ->nEntries; i++) {
4445 if (!_cmsReadFloat32Number(io, &clut->Tab.TFloat[i])) goto Error;
4448 *nItems = 1;
4449 return mpe;
4451 Error:
4452 *nItems = 0;
4453 if (mpe != NULL) cmsStageFree(mpe);
4454 return NULL;
4456 cmsUNUSED_PARAMETER(SizeOfTag);
4459 // Write a CLUT in floating point
4460 static
4461 cmsBool Type_MPEclut_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
4463 cmsUInt8Number Dimensions8[16]; // 16 because the spec says 16 and not max number of channels
4464 cmsUInt32Number i;
4465 cmsStage* mpe = (cmsStage*) Ptr;
4466 _cmsStageCLutData* clut = (_cmsStageCLutData*) mpe ->Data;
4468 // Check for maximum number of channels supported by lcms
4469 if (mpe -> InputChannels > MAX_INPUT_DIMENSIONS) return FALSE;
4471 // Only floats are supported in MPE
4472 if (clut ->HasFloatValues == FALSE) return FALSE;
4474 if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->InputChannels)) return FALSE;
4475 if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->OutputChannels)) return FALSE;
4477 memset(Dimensions8, 0, sizeof(Dimensions8));
4479 for (i=0; i < mpe ->InputChannels; i++)
4480 Dimensions8[i] = (cmsUInt8Number) clut ->Params ->nSamples[i];
4482 if (!io ->Write(io, 16, Dimensions8)) return FALSE;
4484 for (i=0; i < clut ->nEntries; i++) {
4486 if (!_cmsWriteFloat32Number(io, clut ->Tab.TFloat[i])) return FALSE;
4489 return TRUE;
4491 cmsUNUSED_PARAMETER(nItems);
4492 cmsUNUSED_PARAMETER(self);
4497 // This is the list of built-in MPE types
4498 static _cmsTagTypeLinkedList SupportedMPEtypes[] = {
4500 {{ (cmsTagTypeSignature) cmsSigBAcsElemType, NULL, NULL, NULL, NULL, NULL, 0 }, &SupportedMPEtypes[1] }, // Ignore those elements for now
4501 {{ (cmsTagTypeSignature) cmsSigEAcsElemType, NULL, NULL, NULL, NULL, NULL, 0 }, &SupportedMPEtypes[2] }, // (That's what the spec says)
4503 {TYPE_MPE_HANDLER((cmsTagTypeSignature) cmsSigCurveSetElemType, MPEcurve), &SupportedMPEtypes[3] },
4504 {TYPE_MPE_HANDLER((cmsTagTypeSignature) cmsSigMatrixElemType, MPEmatrix), &SupportedMPEtypes[4] },
4505 {TYPE_MPE_HANDLER((cmsTagTypeSignature) cmsSigCLutElemType, MPEclut), NULL },
4508 _cmsTagTypePluginChunkType _cmsMPETypePluginChunk = { NULL };
4510 static
4511 cmsBool ReadMPEElem(struct _cms_typehandler_struct* self,
4512 cmsIOHANDLER* io,
4513 void* Cargo,
4514 cmsUInt32Number n,
4515 cmsUInt32Number SizeOfTag)
4517 cmsStageSignature ElementSig;
4518 cmsTagTypeHandler* TypeHandler;
4519 cmsUInt32Number nItems;
4520 cmsPipeline *NewLUT = (cmsPipeline *) Cargo;
4521 _cmsTagTypePluginChunkType* MPETypePluginChunk = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(self->ContextID, MPEPlugin);
4524 // Take signature and channels for each element.
4525 if (!_cmsReadUInt32Number(io, (cmsUInt32Number*) &ElementSig)) return FALSE;
4527 // The reserved placeholder
4528 if (!_cmsReadUInt32Number(io, NULL)) return FALSE;
4530 // Read diverse MPE types
4531 TypeHandler = GetHandler((cmsTagTypeSignature) ElementSig, MPETypePluginChunk ->TagTypes, SupportedMPEtypes);
4532 if (TypeHandler == NULL) {
4534 char String[5];
4536 _cmsTagSignature2String(String, (cmsTagSignature) ElementSig);
4538 // An unknown element was found.
4539 cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown MPE type '%s' found.", String);
4540 return FALSE;
4543 // If no read method, just ignore the element (valid for cmsSigBAcsElemType and cmsSigEAcsElemType)
4544 // Read the MPE. No size is given
4545 if (TypeHandler ->ReadPtr != NULL) {
4547 // This is a real element which should be read and processed
4548 if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, (cmsStage*) TypeHandler ->ReadPtr(self, io, &nItems, SizeOfTag)))
4549 return FALSE;
4552 return TRUE;
4554 cmsUNUSED_PARAMETER(SizeOfTag);
4555 cmsUNUSED_PARAMETER(n);
4559 // This is the main dispatcher for MPE
4560 static
4561 void *Type_MPE_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
4563 cmsUInt16Number InputChans, OutputChans;
4564 cmsUInt32Number ElementCount;
4565 cmsPipeline *NewLUT = NULL;
4566 cmsUInt32Number BaseOffset;
4568 // Get actual position as a basis for element offsets
4569 BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
4571 // Read channels and element count
4572 if (!_cmsReadUInt16Number(io, &InputChans)) return NULL;
4573 if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL;
4575 if (InputChans == 0 || InputChans >= cmsMAXCHANNELS) return NULL;
4576 if (OutputChans == 0 || OutputChans >= cmsMAXCHANNELS) return NULL;
4578 // Allocates an empty LUT
4579 NewLUT = cmsPipelineAlloc(self ->ContextID, InputChans, OutputChans);
4580 if (NewLUT == NULL) return NULL;
4582 if (!_cmsReadUInt32Number(io, &ElementCount)) goto Error;
4583 if (!ReadPositionTable(self, io, ElementCount, BaseOffset, NewLUT, ReadMPEElem)) goto Error;
4585 // Check channel count
4586 if (InputChans != NewLUT->InputChannels ||
4587 OutputChans != NewLUT->OutputChannels) goto Error;
4589 // Success
4590 *nItems = 1;
4591 return NewLUT;
4593 // Error
4594 Error:
4595 if (NewLUT != NULL) cmsPipelineFree(NewLUT);
4596 *nItems = 0;
4597 return NULL;
4599 cmsUNUSED_PARAMETER(SizeOfTag);
4604 // This one is a little bit more complex, so we don't use position tables this time.
4605 static
4606 cmsBool Type_MPE_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
4608 cmsUInt32Number i, BaseOffset, DirectoryPos, CurrentPos;
4609 cmsUInt32Number inputChan, outputChan;
4610 cmsUInt32Number ElemCount;
4611 cmsUInt32Number *ElementOffsets = NULL, *ElementSizes = NULL, Before;
4612 cmsStageSignature ElementSig;
4613 cmsPipeline* Lut = (cmsPipeline*) Ptr;
4614 cmsStage* Elem = Lut ->Elements;
4615 cmsTagTypeHandler* TypeHandler;
4616 _cmsTagTypePluginChunkType* MPETypePluginChunk = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(self->ContextID, MPEPlugin);
4618 BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
4620 inputChan = cmsPipelineInputChannels(Lut);
4621 outputChan = cmsPipelineOutputChannels(Lut);
4622 ElemCount = cmsPipelineStageCount(Lut);
4624 ElementOffsets = (cmsUInt32Number *) _cmsCalloc(self ->ContextID, ElemCount, sizeof(cmsUInt32Number));
4625 if (ElementOffsets == NULL) goto Error;
4627 ElementSizes = (cmsUInt32Number *) _cmsCalloc(self ->ContextID, ElemCount, sizeof(cmsUInt32Number));
4628 if (ElementSizes == NULL) goto Error;
4630 // Write the head
4631 if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) inputChan)) goto Error;
4632 if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) outputChan)) goto Error;
4633 if (!_cmsWriteUInt32Number(io, (cmsUInt16Number) ElemCount)) goto Error;
4635 DirectoryPos = io ->Tell(io);
4637 // Write a fake directory to be filled latter on
4638 for (i=0; i < ElemCount; i++) {
4639 if (!_cmsWriteUInt32Number(io, 0)) goto Error; // Offset
4640 if (!_cmsWriteUInt32Number(io, 0)) goto Error; // size
4643 // Write each single tag. Keep track of the size as well.
4644 for (i=0; i < ElemCount; i++) {
4646 ElementOffsets[i] = io ->Tell(io) - BaseOffset;
4648 ElementSig = Elem ->Type;
4650 TypeHandler = GetHandler((cmsTagTypeSignature) ElementSig, MPETypePluginChunk->TagTypes, SupportedMPEtypes);
4651 if (TypeHandler == NULL) {
4653 char String[5];
4655 _cmsTagSignature2String(String, (cmsTagSignature) ElementSig);
4657 // An unknown element was found.
4658 cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Found unknown MPE type '%s'", String);
4659 goto Error;
4662 if (!_cmsWriteUInt32Number(io, ElementSig)) goto Error;
4663 if (!_cmsWriteUInt32Number(io, 0)) goto Error;
4664 Before = io ->Tell(io);
4665 if (!TypeHandler ->WritePtr(self, io, Elem, 1)) goto Error;
4666 if (!_cmsWriteAlignment(io)) goto Error;
4668 ElementSizes[i] = io ->Tell(io) - Before;
4670 Elem = Elem ->Next;
4673 // Write the directory
4674 CurrentPos = io ->Tell(io);
4676 if (!io ->Seek(io, DirectoryPos)) goto Error;
4678 for (i=0; i < ElemCount; i++) {
4679 if (!_cmsWriteUInt32Number(io, ElementOffsets[i])) goto Error;
4680 if (!_cmsWriteUInt32Number(io, ElementSizes[i])) goto Error;
4683 if (!io ->Seek(io, CurrentPos)) goto Error;
4685 if (ElementOffsets != NULL) _cmsFree(self ->ContextID, ElementOffsets);
4686 if (ElementSizes != NULL) _cmsFree(self ->ContextID, ElementSizes);
4687 return TRUE;
4689 Error:
4690 if (ElementOffsets != NULL) _cmsFree(self ->ContextID, ElementOffsets);
4691 if (ElementSizes != NULL) _cmsFree(self ->ContextID, ElementSizes);
4692 return FALSE;
4694 cmsUNUSED_PARAMETER(nItems);
4698 static
4699 void* Type_MPE_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
4701 return (void*) cmsPipelineDup((cmsPipeline*) Ptr);
4703 cmsUNUSED_PARAMETER(n);
4704 cmsUNUSED_PARAMETER(self);
4707 static
4708 void Type_MPE_Free(struct _cms_typehandler_struct* self, void *Ptr)
4710 cmsPipelineFree((cmsPipeline*) Ptr);
4711 return;
4713 cmsUNUSED_PARAMETER(self);
4717 // ********************************************************************************
4718 // Type cmsSigVcgtType
4719 // ********************************************************************************
4722 #define cmsVideoCardGammaTableType 0
4723 #define cmsVideoCardGammaFormulaType 1
4725 // Used internally
4726 typedef struct {
4727 double Gamma;
4728 double Min;
4729 double Max;
4730 } _cmsVCGTGAMMA;
4733 static
4734 void *Type_vcgt_Read(struct _cms_typehandler_struct* self,
4735 cmsIOHANDLER* io,
4736 cmsUInt32Number* nItems,
4737 cmsUInt32Number SizeOfTag)
4739 cmsUInt32Number TagType, n, i;
4740 cmsToneCurve** Curves;
4742 *nItems = 0;
4744 // Read tag type
4745 if (!_cmsReadUInt32Number(io, &TagType)) return NULL;
4747 // Allocate space for the array
4748 Curves = ( cmsToneCurve**) _cmsCalloc(self ->ContextID, 3, sizeof(cmsToneCurve*));
4749 if (Curves == NULL) return NULL;
4751 // There are two possible flavors
4752 switch (TagType) {
4754 // Gamma is stored as a table
4755 case cmsVideoCardGammaTableType:
4757 cmsUInt16Number nChannels, nElems, nBytes;
4759 // Check channel count, which should be 3 (we don't support monochrome this time)
4760 if (!_cmsReadUInt16Number(io, &nChannels)) goto Error;
4762 if (nChannels != 3) {
4763 cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported number of channels for VCGT '%d'", nChannels);
4764 goto Error;
4767 // Get Table element count and bytes per element
4768 if (!_cmsReadUInt16Number(io, &nElems)) goto Error;
4769 if (!_cmsReadUInt16Number(io, &nBytes)) goto Error;
4771 // Adobe's quirk fixup. Fixing broken profiles...
4772 if (nElems == 256 && nBytes == 1 && SizeOfTag == 1576)
4773 nBytes = 2;
4776 // Populate tone curves
4777 for (n=0; n < 3; n++) {
4779 Curves[n] = cmsBuildTabulatedToneCurve16(self ->ContextID, nElems, NULL);
4780 if (Curves[n] == NULL) goto Error;
4782 // On depending on byte depth
4783 switch (nBytes) {
4785 // One byte, 0..255
4786 case 1:
4787 for (i=0; i < nElems; i++) {
4789 cmsUInt8Number v;
4791 if (!_cmsReadUInt8Number(io, &v)) goto Error;
4792 Curves[n] ->Table16[i] = FROM_8_TO_16(v);
4794 break;
4796 // One word 0..65535
4797 case 2:
4798 if (!_cmsReadUInt16Array(io, nElems, Curves[n]->Table16)) goto Error;
4799 break;
4801 // Unsupported
4802 default:
4803 cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported bit depth for VCGT '%d'", nBytes * 8);
4804 goto Error;
4806 } // For all 3 channels
4808 break;
4810 // In this case, gamma is stored as a formula
4811 case cmsVideoCardGammaFormulaType:
4813 _cmsVCGTGAMMA Colorant[3];
4815 // Populate tone curves
4816 for (n=0; n < 3; n++) {
4818 double Params[10];
4820 if (!_cmsRead15Fixed16Number(io, &Colorant[n].Gamma)) goto Error;
4821 if (!_cmsRead15Fixed16Number(io, &Colorant[n].Min)) goto Error;
4822 if (!_cmsRead15Fixed16Number(io, &Colorant[n].Max)) goto Error;
4824 // Parametric curve type 5 is:
4825 // Y = (aX + b)^Gamma + e | X >= d
4826 // Y = cX + f | X < d
4828 // vcgt formula is:
4829 // Y = (Max - Min) * (X ^ Gamma) + Min
4831 // So, the translation is
4832 // a = (Max - Min) ^ ( 1 / Gamma)
4833 // e = Min
4834 // b=c=d=f=0
4836 Params[0] = Colorant[n].Gamma;
4837 Params[1] = pow((Colorant[n].Max - Colorant[n].Min), (1.0 / Colorant[n].Gamma));
4838 Params[2] = 0;
4839 Params[3] = 0;
4840 Params[4] = 0;
4841 Params[5] = Colorant[n].Min;
4842 Params[6] = 0;
4844 Curves[n] = cmsBuildParametricToneCurve(self ->ContextID, 5, Params);
4845 if (Curves[n] == NULL) goto Error;
4848 break;
4850 // Unsupported
4851 default:
4852 cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported tag type for VCGT '%d'", TagType);
4853 goto Error;
4856 *nItems = 1;
4857 return (void*) Curves;
4859 // Regret, free all resources
4860 Error:
4862 cmsFreeToneCurveTriple(Curves);
4863 _cmsFree(self ->ContextID, Curves);
4864 return NULL;
4866 cmsUNUSED_PARAMETER(SizeOfTag);
4870 // We don't support all flavors, only 16bits tables and formula
4871 static
4872 cmsBool Type_vcgt_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
4874 cmsToneCurve** Curves = (cmsToneCurve**) Ptr;
4875 cmsUInt32Number i, j;
4877 if (cmsGetToneCurveParametricType(Curves[0]) == 5 &&
4878 cmsGetToneCurveParametricType(Curves[1]) == 5 &&
4879 cmsGetToneCurveParametricType(Curves[2]) == 5) {
4881 if (!_cmsWriteUInt32Number(io, cmsVideoCardGammaFormulaType)) return FALSE;
4883 // Save parameters
4884 for (i=0; i < 3; i++) {
4886 _cmsVCGTGAMMA v;
4888 v.Gamma = Curves[i] ->Segments[0].Params[0];
4889 v.Min = Curves[i] ->Segments[0].Params[5];
4890 v.Max = pow(Curves[i] ->Segments[0].Params[1], v.Gamma) + v.Min;
4892 if (!_cmsWrite15Fixed16Number(io, v.Gamma)) return FALSE;
4893 if (!_cmsWrite15Fixed16Number(io, v.Min)) return FALSE;
4894 if (!_cmsWrite15Fixed16Number(io, v.Max)) return FALSE;
4898 else {
4900 // Always store as a table of 256 words
4901 if (!_cmsWriteUInt32Number(io, cmsVideoCardGammaTableType)) return FALSE;
4902 if (!_cmsWriteUInt16Number(io, 3)) return FALSE;
4903 if (!_cmsWriteUInt16Number(io, 256)) return FALSE;
4904 if (!_cmsWriteUInt16Number(io, 2)) return FALSE;
4906 for (i=0; i < 3; i++) {
4907 for (j=0; j < 256; j++) {
4909 cmsFloat32Number v = cmsEvalToneCurveFloat(Curves[i], (cmsFloat32Number) (j / 255.0));
4910 cmsUInt16Number n = _cmsQuickSaturateWord(v * 65535.0);
4912 if (!_cmsWriteUInt16Number(io, n)) return FALSE;
4917 return TRUE;
4919 cmsUNUSED_PARAMETER(self);
4920 cmsUNUSED_PARAMETER(nItems);
4923 static
4924 void* Type_vcgt_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
4926 cmsToneCurve** OldCurves = (cmsToneCurve**) Ptr;
4927 cmsToneCurve** NewCurves;
4929 NewCurves = ( cmsToneCurve**) _cmsCalloc(self ->ContextID, 3, sizeof(cmsToneCurve*));
4930 if (NewCurves == NULL) return NULL;
4932 NewCurves[0] = cmsDupToneCurve(OldCurves[0]);
4933 NewCurves[1] = cmsDupToneCurve(OldCurves[1]);
4934 NewCurves[2] = cmsDupToneCurve(OldCurves[2]);
4936 return (void*) NewCurves;
4938 cmsUNUSED_PARAMETER(n);
4942 static
4943 void Type_vcgt_Free(struct _cms_typehandler_struct* self, void* Ptr)
4945 cmsFreeToneCurveTriple((cmsToneCurve**) Ptr);
4946 _cmsFree(self ->ContextID, Ptr);
4950 // ********************************************************************************
4951 // Type cmsSigDictType
4952 // ********************************************************************************
4954 // Single column of the table can point to wchar or MLUC elements. Holds arrays of data
4955 typedef struct {
4956 cmsContext ContextID;
4957 cmsUInt32Number *Offsets;
4958 cmsUInt32Number *Sizes;
4959 } _cmsDICelem;
4961 typedef struct {
4962 _cmsDICelem Name, Value, DisplayName, DisplayValue;
4964 } _cmsDICarray;
4966 // Allocate an empty array element
4967 static
4968 cmsBool AllocElem(cmsContext ContextID, _cmsDICelem* e, cmsUInt32Number Count)
4970 e->Offsets = (cmsUInt32Number *) _cmsCalloc(ContextID, Count, sizeof(cmsUInt32Number));
4971 if (e->Offsets == NULL) return FALSE;
4973 e->Sizes = (cmsUInt32Number *) _cmsCalloc(ContextID, Count, sizeof(cmsUInt32Number));
4974 if (e->Sizes == NULL) {
4976 _cmsFree(ContextID, e -> Offsets);
4977 return FALSE;
4980 e ->ContextID = ContextID;
4981 return TRUE;
4984 // Free an array element
4985 static
4986 void FreeElem(_cmsDICelem* e)
4988 if (e ->Offsets != NULL) _cmsFree(e -> ContextID, e -> Offsets);
4989 if (e ->Sizes != NULL) _cmsFree(e -> ContextID, e -> Sizes);
4990 e->Offsets = e ->Sizes = NULL;
4993 // Get rid of whole array
4994 static
4995 void FreeArray( _cmsDICarray* a)
4997 if (a ->Name.Offsets != NULL) FreeElem(&a->Name);
4998 if (a ->Value.Offsets != NULL) FreeElem(&a ->Value);
4999 if (a ->DisplayName.Offsets != NULL) FreeElem(&a->DisplayName);
5000 if (a ->DisplayValue.Offsets != NULL) FreeElem(&a ->DisplayValue);
5004 // Allocate whole array
5005 static
5006 cmsBool AllocArray(cmsContext ContextID, _cmsDICarray* a, cmsUInt32Number Count, cmsUInt32Number Length)
5008 // Empty values
5009 memset(a, 0, sizeof(_cmsDICarray));
5011 // On depending on record size, create column arrays
5012 if (!AllocElem(ContextID, &a ->Name, Count)) goto Error;
5013 if (!AllocElem(ContextID, &a ->Value, Count)) goto Error;
5015 if (Length > 16) {
5016 if (!AllocElem(ContextID, &a -> DisplayName, Count)) goto Error;
5019 if (Length > 24) {
5020 if (!AllocElem(ContextID, &a ->DisplayValue, Count)) goto Error;
5022 return TRUE;
5024 Error:
5025 FreeArray(a);
5026 return FALSE;
5029 // Read one element
5030 static
5031 cmsBool ReadOneElem(cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i, cmsUInt32Number BaseOffset)
5033 if (!_cmsReadUInt32Number(io, &e->Offsets[i])) return FALSE;
5034 if (!_cmsReadUInt32Number(io, &e ->Sizes[i])) return FALSE;
5036 // An offset of zero has special meaning and shall be preserved
5037 if (e ->Offsets[i] > 0)
5038 e ->Offsets[i] += BaseOffset;
5039 return TRUE;
5043 static
5044 cmsBool ReadOffsetArray(cmsIOHANDLER* io, _cmsDICarray* a,
5045 cmsUInt32Number Count, cmsUInt32Number Length, cmsUInt32Number BaseOffset,
5046 cmsInt32Number* SignedSizeOfTagPtr)
5048 cmsUInt32Number i;
5049 cmsInt32Number SignedSizeOfTag = *SignedSizeOfTagPtr;
5051 // Read column arrays
5052 for (i=0; i < Count; i++) {
5054 if (SignedSizeOfTag < 4 * (cmsInt32Number) sizeof(cmsUInt32Number)) return FALSE;
5055 SignedSizeOfTag -= 4 * sizeof(cmsUInt32Number);
5057 if (!ReadOneElem(io, &a -> Name, i, BaseOffset)) return FALSE;
5058 if (!ReadOneElem(io, &a -> Value, i, BaseOffset)) return FALSE;
5060 if (Length > 16) {
5062 if (SignedSizeOfTag < 2 * (cmsInt32Number) sizeof(cmsUInt32Number)) return FALSE;
5063 SignedSizeOfTag -= 2 * sizeof(cmsUInt32Number);
5065 if (!ReadOneElem(io, &a ->DisplayName, i, BaseOffset)) return FALSE;
5069 if (Length > 24) {
5071 if (SignedSizeOfTag < 2 * (cmsInt32Number) sizeof(cmsUInt32Number)) return FALSE;
5072 SignedSizeOfTag -= 2 * (cmsInt32Number) sizeof(cmsUInt32Number);
5074 if (!ReadOneElem(io, & a -> DisplayValue, i, BaseOffset)) return FALSE;
5078 *SignedSizeOfTagPtr = SignedSizeOfTag;
5079 return TRUE;
5083 // Write one element
5084 static
5085 cmsBool WriteOneElem(cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i)
5087 if (!_cmsWriteUInt32Number(io, e->Offsets[i])) return FALSE;
5088 if (!_cmsWriteUInt32Number(io, e ->Sizes[i])) return FALSE;
5090 return TRUE;
5093 static
5094 cmsBool WriteOffsetArray(cmsIOHANDLER* io, _cmsDICarray* a, cmsUInt32Number Count, cmsUInt32Number Length)
5096 cmsUInt32Number i;
5098 for (i=0; i < Count; i++) {
5100 if (!WriteOneElem(io, &a -> Name, i)) return FALSE;
5101 if (!WriteOneElem(io, &a -> Value, i)) return FALSE;
5103 if (Length > 16) {
5105 if (!WriteOneElem(io, &a -> DisplayName, i)) return FALSE;
5108 if (Length > 24) {
5110 if (!WriteOneElem(io, &a -> DisplayValue, i)) return FALSE;
5114 return TRUE;
5117 static
5118 cmsBool ReadOneWChar(cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i, wchar_t ** wcstr)
5121 cmsUInt32Number nChars;
5123 // Special case for undefined strings (see ICC Votable
5124 // Proposal Submission, Dictionary Type and Metadata TAG Definition)
5125 if (e -> Offsets[i] == 0) {
5127 *wcstr = NULL;
5128 return TRUE;
5131 if (!io -> Seek(io, e -> Offsets[i])) return FALSE;
5133 nChars = e ->Sizes[i] / sizeof(cmsUInt16Number);
5136 *wcstr = (wchar_t*) _cmsMallocZero(e ->ContextID, (nChars + 1) * sizeof(wchar_t));
5137 if (*wcstr == NULL) return FALSE;
5139 if (!_cmsReadWCharArray(io, nChars, *wcstr)) {
5140 _cmsFree(e ->ContextID, *wcstr);
5141 return FALSE;
5144 // End of string marker
5145 (*wcstr)[nChars] = 0;
5146 return TRUE;
5149 static
5150 cmsUInt32Number mywcslen(const wchar_t *s)
5152 const wchar_t *p;
5154 p = s;
5155 while (*p)
5156 p++;
5158 return (cmsUInt32Number)(p - s);
5161 static
5162 cmsBool WriteOneWChar(cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i, const wchar_t * wcstr, cmsUInt32Number BaseOffset)
5164 cmsUInt32Number Before = io ->Tell(io);
5165 cmsUInt32Number n;
5167 e ->Offsets[i] = Before - BaseOffset;
5169 if (wcstr == NULL) {
5170 e ->Sizes[i] = 0;
5171 e ->Offsets[i] = 0;
5172 return TRUE;
5175 n = mywcslen(wcstr);
5176 if (!_cmsWriteWCharArray(io, n, wcstr)) return FALSE;
5178 e ->Sizes[i] = io ->Tell(io) - Before;
5179 return TRUE;
5182 static
5183 cmsBool ReadOneMLUC(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i, cmsMLU** mlu)
5185 cmsUInt32Number nItems = 0;
5187 // A way to get null MLUCs
5188 if (e -> Offsets[i] == 0 || e ->Sizes[i] == 0) {
5190 *mlu = NULL;
5191 return TRUE;
5194 if (!io -> Seek(io, e -> Offsets[i])) return FALSE;
5196 *mlu = (cmsMLU*) Type_MLU_Read(self, io, &nItems, e ->Sizes[i]);
5197 return *mlu != NULL;
5200 static
5201 cmsBool WriteOneMLUC(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i, const cmsMLU* mlu, cmsUInt32Number BaseOffset)
5203 cmsUInt32Number Before;
5205 // Special case for undefined strings (see ICC Votable
5206 // Proposal Submission, Dictionary Type and Metadata TAG Definition)
5207 if (mlu == NULL) {
5208 e ->Sizes[i] = 0;
5209 e ->Offsets[i] = 0;
5210 return TRUE;
5213 Before = io ->Tell(io);
5214 e ->Offsets[i] = Before - BaseOffset;
5216 if (!Type_MLU_Write(self, io, (void*) mlu, 1)) return FALSE;
5218 e ->Sizes[i] = io ->Tell(io) - Before;
5219 return TRUE;
5223 static
5224 void *Type_Dictionary_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
5226 cmsHANDLE hDict = NULL;
5227 cmsUInt32Number i, Count, Length;
5228 cmsUInt32Number BaseOffset;
5229 _cmsDICarray a;
5230 wchar_t *NameWCS = NULL, *ValueWCS = NULL;
5231 cmsMLU *DisplayNameMLU = NULL, *DisplayValueMLU=NULL;
5232 cmsBool rc;
5233 cmsInt32Number SignedSizeOfTag = (cmsInt32Number)SizeOfTag;
5235 *nItems = 0;
5236 memset(&a, 0, sizeof(a));
5238 // Get actual position as a basis for element offsets
5239 BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
5241 // Get name-value record count
5242 SignedSizeOfTag -= sizeof(cmsUInt32Number);
5243 if (SignedSizeOfTag < 0) goto Error;
5244 if (!_cmsReadUInt32Number(io, &Count)) return NULL;
5246 // Get rec length
5247 SignedSizeOfTag -= sizeof(cmsUInt32Number);
5248 if (SignedSizeOfTag < 0) goto Error;
5249 if (!_cmsReadUInt32Number(io, &Length)) return NULL;
5252 // Check for valid lengths
5253 if (Length != 16 && Length != 24 && Length != 32) {
5254 cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown record length in dictionary '%d'", Length);
5255 return NULL;
5258 // Creates an empty dictionary
5259 hDict = cmsDictAlloc(self -> ContextID);
5260 if (hDict == NULL) return NULL;
5262 // On depending on record size, create column arrays
5263 if (!AllocArray(self -> ContextID, &a, Count, Length)) goto Error;
5265 // Read column arrays
5266 if (!ReadOffsetArray(io, &a, Count, Length, BaseOffset, &SignedSizeOfTag)) goto Error;
5268 // Seek to each element and read it
5269 for (i=0; i < Count; i++) {
5271 if (!ReadOneWChar(io, &a.Name, i, &NameWCS)) goto Error;
5272 if (!ReadOneWChar(io, &a.Value, i, &ValueWCS)) goto Error;
5274 if (Length > 16) {
5275 if (!ReadOneMLUC(self, io, &a.DisplayName, i, &DisplayNameMLU)) goto Error;
5278 if (Length > 24) {
5279 if (!ReadOneMLUC(self, io, &a.DisplayValue, i, &DisplayValueMLU)) goto Error;
5282 if (NameWCS == NULL || ValueWCS == NULL) {
5284 cmsSignalError(self->ContextID, cmsERROR_CORRUPTION_DETECTED, "Bad dictionary Name/Value");
5285 rc = FALSE;
5287 else {
5289 rc = cmsDictAddEntry(hDict, NameWCS, ValueWCS, DisplayNameMLU, DisplayValueMLU);
5292 if (NameWCS != NULL) _cmsFree(self ->ContextID, NameWCS);
5293 if (ValueWCS != NULL) _cmsFree(self ->ContextID, ValueWCS);
5294 if (DisplayNameMLU != NULL) cmsMLUfree(DisplayNameMLU);
5295 if (DisplayValueMLU != NULL) cmsMLUfree(DisplayValueMLU);
5297 if (!rc) goto Error;
5300 FreeArray(&a);
5301 *nItems = 1;
5302 return (void*) hDict;
5304 Error:
5305 FreeArray(&a);
5306 if (hDict != NULL) cmsDictFree(hDict);
5307 return NULL;
5311 static
5312 cmsBool Type_Dictionary_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
5314 cmsHANDLE hDict = (cmsHANDLE) Ptr;
5315 const cmsDICTentry* p;
5316 cmsBool AnyName, AnyValue;
5317 cmsUInt32Number i, Count, Length;
5318 cmsUInt32Number DirectoryPos, CurrentPos, BaseOffset;
5319 _cmsDICarray a;
5321 if (hDict == NULL) return FALSE;
5323 BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
5325 // Let's inspect the dictionary
5326 Count = 0; AnyName = FALSE; AnyValue = FALSE;
5327 for (p = cmsDictGetEntryList(hDict); p != NULL; p = cmsDictNextEntry(p)) {
5329 if (p ->DisplayName != NULL) AnyName = TRUE;
5330 if (p ->DisplayValue != NULL) AnyValue = TRUE;
5331 Count++;
5334 Length = 16;
5335 if (AnyName) Length += 8;
5336 if (AnyValue) Length += 8;
5338 if (!_cmsWriteUInt32Number(io, Count)) return FALSE;
5339 if (!_cmsWriteUInt32Number(io, Length)) return FALSE;
5341 // Keep starting position of offsets table
5342 DirectoryPos = io ->Tell(io);
5344 // Allocate offsets array
5345 if (!AllocArray(self ->ContextID, &a, Count, Length)) goto Error;
5347 // Write a fake directory to be filled latter on
5348 if (!WriteOffsetArray(io, &a, Count, Length)) goto Error;
5350 // Write each element. Keep track of the size as well.
5351 p = cmsDictGetEntryList(hDict);
5352 for (i=0; i < Count; i++) {
5354 if (!WriteOneWChar(io, &a.Name, i, p ->Name, BaseOffset)) goto Error;
5355 if (!WriteOneWChar(io, &a.Value, i, p ->Value, BaseOffset)) goto Error;
5357 if (p ->DisplayName != NULL) {
5358 if (!WriteOneMLUC(self, io, &a.DisplayName, i, p ->DisplayName, BaseOffset)) goto Error;
5361 if (p ->DisplayValue != NULL) {
5362 if (!WriteOneMLUC(self, io, &a.DisplayValue, i, p ->DisplayValue, BaseOffset)) goto Error;
5365 p = cmsDictNextEntry(p);
5368 // Write the directory
5369 CurrentPos = io ->Tell(io);
5370 if (!io ->Seek(io, DirectoryPos)) goto Error;
5372 if (!WriteOffsetArray(io, &a, Count, Length)) goto Error;
5374 if (!io ->Seek(io, CurrentPos)) goto Error;
5376 FreeArray(&a);
5377 return TRUE;
5379 Error:
5380 FreeArray(&a);
5381 return FALSE;
5383 cmsUNUSED_PARAMETER(nItems);
5387 static
5388 void* Type_Dictionary_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
5390 return (void*) cmsDictDup((cmsHANDLE) Ptr);
5392 cmsUNUSED_PARAMETER(n);
5393 cmsUNUSED_PARAMETER(self);
5397 static
5398 void Type_Dictionary_Free(struct _cms_typehandler_struct* self, void* Ptr)
5400 cmsDictFree((cmsHANDLE) Ptr);
5401 cmsUNUSED_PARAMETER(self);
5404 // cicp VideoSignalType
5406 static
5407 void* Type_VideoSignal_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
5409 cmsVideoSignalType* cicp = NULL;
5411 if (SizeOfTag != 8) return NULL;
5413 if (!_cmsReadUInt32Number(io, NULL)) return NULL;
5415 cicp = (cmsVideoSignalType*)_cmsCalloc(self->ContextID, 1, sizeof(cmsVideoSignalType));
5416 if (cicp == NULL) return NULL;
5418 if (!_cmsReadUInt8Number(io, &cicp->ColourPrimaries)) goto Error;
5419 if (!_cmsReadUInt8Number(io, &cicp->TransferCharacteristics)) goto Error;
5420 if (!_cmsReadUInt8Number(io, &cicp->MatrixCoefficients)) goto Error;
5421 if (!_cmsReadUInt8Number(io, &cicp->VideoFullRangeFlag)) goto Error;
5423 // Success
5424 *nItems = 1;
5425 return cicp;
5427 Error:
5428 if (cicp != NULL) _cmsFree(self->ContextID, cicp);
5429 return NULL;
5432 static
5433 cmsBool Type_VideoSignal_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
5435 cmsVideoSignalType* cicp = (cmsVideoSignalType*)Ptr;
5437 if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
5438 if (!_cmsWriteUInt8Number(io, cicp->ColourPrimaries)) return FALSE;
5439 if (!_cmsWriteUInt8Number(io, cicp->TransferCharacteristics)) return FALSE;
5440 if (!_cmsWriteUInt8Number(io, cicp->MatrixCoefficients)) return FALSE;
5441 if (!_cmsWriteUInt8Number(io, cicp->VideoFullRangeFlag)) return FALSE;
5443 return TRUE;
5445 cmsUNUSED_PARAMETER(self);
5446 cmsUNUSED_PARAMETER(nItems);
5449 void* Type_VideoSignal_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n)
5451 return _cmsDupMem(self->ContextID, Ptr, sizeof(cmsVideoSignalType));
5453 cmsUNUSED_PARAMETER(n);
5457 static
5458 void Type_VideoSignal_Free(struct _cms_typehandler_struct* self, void* Ptr)
5460 _cmsFree(self->ContextID, Ptr);
5463 // ********************************************************************************
5464 // Type support main routines
5465 // ********************************************************************************
5468 // This is the list of built-in types
5469 static const _cmsTagTypeLinkedList SupportedTagTypes[] = {
5471 {TYPE_HANDLER(cmsSigChromaticityType, Chromaticity), (_cmsTagTypeLinkedList*) &SupportedTagTypes[1] },
5472 {TYPE_HANDLER(cmsSigColorantOrderType, ColorantOrderType), (_cmsTagTypeLinkedList*) &SupportedTagTypes[2] },
5473 {TYPE_HANDLER(cmsSigS15Fixed16ArrayType, S15Fixed16), (_cmsTagTypeLinkedList*) &SupportedTagTypes[3] },
5474 {TYPE_HANDLER(cmsSigU16Fixed16ArrayType, U16Fixed16), (_cmsTagTypeLinkedList*) &SupportedTagTypes[4] },
5475 {TYPE_HANDLER(cmsSigTextType, Text), (_cmsTagTypeLinkedList*) &SupportedTagTypes[5] },
5476 {TYPE_HANDLER(cmsSigTextDescriptionType, Text_Description), (_cmsTagTypeLinkedList*) &SupportedTagTypes[6] },
5477 {TYPE_HANDLER(cmsSigCurveType, Curve), (_cmsTagTypeLinkedList*) &SupportedTagTypes[7] },
5478 {TYPE_HANDLER(cmsSigParametricCurveType, ParametricCurve), (_cmsTagTypeLinkedList*) &SupportedTagTypes[8] },
5479 {TYPE_HANDLER(cmsSigDateTimeType, DateTime), (_cmsTagTypeLinkedList*) &SupportedTagTypes[9] },
5480 {TYPE_HANDLER(cmsSigLut8Type, LUT8), (_cmsTagTypeLinkedList*) &SupportedTagTypes[10] },
5481 {TYPE_HANDLER(cmsSigLut16Type, LUT16), (_cmsTagTypeLinkedList*) &SupportedTagTypes[11] },
5482 {TYPE_HANDLER(cmsSigColorantTableType, ColorantTable), (_cmsTagTypeLinkedList*) &SupportedTagTypes[12] },
5483 {TYPE_HANDLER(cmsSigNamedColor2Type, NamedColor), (_cmsTagTypeLinkedList*) &SupportedTagTypes[13] },
5484 {TYPE_HANDLER(cmsSigMultiLocalizedUnicodeType, MLU), (_cmsTagTypeLinkedList*) &SupportedTagTypes[14] },
5485 {TYPE_HANDLER(cmsSigProfileSequenceDescType, ProfileSequenceDesc),(_cmsTagTypeLinkedList*) &SupportedTagTypes[15] },
5486 {TYPE_HANDLER(cmsSigSignatureType, Signature), (_cmsTagTypeLinkedList*) &SupportedTagTypes[16] },
5487 {TYPE_HANDLER(cmsSigMeasurementType, Measurement), (_cmsTagTypeLinkedList*) &SupportedTagTypes[17] },
5488 {TYPE_HANDLER(cmsSigDataType, Data), (_cmsTagTypeLinkedList*) &SupportedTagTypes[18] },
5489 {TYPE_HANDLER(cmsSigLutAtoBType, LUTA2B), (_cmsTagTypeLinkedList*) &SupportedTagTypes[19] },
5490 {TYPE_HANDLER(cmsSigLutBtoAType, LUTB2A), (_cmsTagTypeLinkedList*) &SupportedTagTypes[20] },
5491 {TYPE_HANDLER(cmsSigUcrBgType, UcrBg), (_cmsTagTypeLinkedList*) &SupportedTagTypes[21] },
5492 {TYPE_HANDLER(cmsSigCrdInfoType, CrdInfo), (_cmsTagTypeLinkedList*) &SupportedTagTypes[22] },
5493 {TYPE_HANDLER(cmsSigMultiProcessElementType, MPE), (_cmsTagTypeLinkedList*) &SupportedTagTypes[23] },
5494 {TYPE_HANDLER(cmsSigScreeningType, Screening), (_cmsTagTypeLinkedList*) &SupportedTagTypes[24] },
5495 {TYPE_HANDLER(cmsSigViewingConditionsType, ViewingConditions), (_cmsTagTypeLinkedList*) &SupportedTagTypes[25] },
5496 {TYPE_HANDLER(cmsSigXYZType, XYZ), (_cmsTagTypeLinkedList*) &SupportedTagTypes[26] },
5497 {TYPE_HANDLER(cmsCorbisBrokenXYZtype, XYZ), (_cmsTagTypeLinkedList*) &SupportedTagTypes[27] },
5498 {TYPE_HANDLER(cmsMonacoBrokenCurveType, Curve), (_cmsTagTypeLinkedList*) &SupportedTagTypes[28] },
5499 {TYPE_HANDLER(cmsSigProfileSequenceIdType, ProfileSequenceId), (_cmsTagTypeLinkedList*) &SupportedTagTypes[29] },
5500 {TYPE_HANDLER(cmsSigDictType, Dictionary), (_cmsTagTypeLinkedList*) &SupportedTagTypes[30] },
5501 {TYPE_HANDLER(cmsSigcicpType, VideoSignal), (_cmsTagTypeLinkedList*) &SupportedTagTypes[31] },
5502 {TYPE_HANDLER(cmsSigVcgtType, vcgt), NULL }
5506 _cmsTagTypePluginChunkType _cmsTagTypePluginChunk = { NULL };
5510 // Duplicates the zone of memory used by the plug-in in the new context
5511 static
5512 void DupTagTypeList(struct _cmsContext_struct* ctx,
5513 const struct _cmsContext_struct* src,
5514 int loc)
5516 _cmsTagTypePluginChunkType newHead = { NULL };
5517 _cmsTagTypeLinkedList* entry;
5518 _cmsTagTypeLinkedList* Anterior = NULL;
5519 _cmsTagTypePluginChunkType* head = (_cmsTagTypePluginChunkType*) src->chunks[loc];
5521 // Walk the list copying all nodes
5522 for (entry = head->TagTypes;
5523 entry != NULL;
5524 entry = entry ->Next) {
5526 _cmsTagTypeLinkedList *newEntry = ( _cmsTagTypeLinkedList *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTagTypeLinkedList));
5528 if (newEntry == NULL)
5529 return;
5531 // We want to keep the linked list order, so this is a little bit tricky
5532 newEntry -> Next = NULL;
5533 if (Anterior)
5534 Anterior -> Next = newEntry;
5536 Anterior = newEntry;
5538 if (newHead.TagTypes == NULL)
5539 newHead.TagTypes = newEntry;
5542 ctx ->chunks[loc] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsTagTypePluginChunkType));
5546 void _cmsAllocTagTypePluginChunk(struct _cmsContext_struct* ctx,
5547 const struct _cmsContext_struct* src)
5549 if (src != NULL) {
5551 // Duplicate the LIST
5552 DupTagTypeList(ctx, src, TagTypePlugin);
5554 else {
5555 static _cmsTagTypePluginChunkType TagTypePluginChunk = { NULL };
5556 ctx ->chunks[TagTypePlugin] = _cmsSubAllocDup(ctx ->MemPool, &TagTypePluginChunk, sizeof(_cmsTagTypePluginChunkType));
5560 void _cmsAllocMPETypePluginChunk(struct _cmsContext_struct* ctx,
5561 const struct _cmsContext_struct* src)
5563 if (src != NULL) {
5565 // Duplicate the LIST
5566 DupTagTypeList(ctx, src, MPEPlugin);
5568 else {
5569 static _cmsTagTypePluginChunkType TagTypePluginChunk = { NULL };
5570 ctx ->chunks[MPEPlugin] = _cmsSubAllocDup(ctx ->MemPool, &TagTypePluginChunk, sizeof(_cmsTagTypePluginChunkType));
5576 // Both kind of plug-ins share same structure
5577 cmsBool _cmsRegisterTagTypePlugin(cmsContext id, cmsPluginBase* Data)
5579 return RegisterTypesPlugin(id, Data, TagTypePlugin);
5582 cmsBool _cmsRegisterMultiProcessElementPlugin(cmsContext id, cmsPluginBase* Data)
5584 return RegisterTypesPlugin(id, Data,MPEPlugin);
5588 // Wrapper for tag types
5589 cmsTagTypeHandler* _cmsGetTagTypeHandler(cmsContext ContextID, cmsTagTypeSignature sig)
5591 _cmsTagTypePluginChunkType* ctx = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(ContextID, TagTypePlugin);
5593 return GetHandler(sig, ctx->TagTypes, (_cmsTagTypeLinkedList*) SupportedTagTypes);
5596 // ********************************************************************************
5597 // Tag support main routines
5598 // ********************************************************************************
5600 typedef struct _cmsTagLinkedList_st {
5602 cmsTagSignature Signature;
5603 cmsTagDescriptor Descriptor;
5604 struct _cmsTagLinkedList_st* Next;
5606 } _cmsTagLinkedList;
5608 // This is the list of built-in tags. The data of this list can be modified by plug-ins
5609 static _cmsTagLinkedList SupportedTags[] = {
5611 { cmsSigAToB0Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutAtoBType, cmsSigLut8Type}, DecideLUTtypeA2B}, &SupportedTags[1]},
5612 { cmsSigAToB1Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutAtoBType, cmsSigLut8Type}, DecideLUTtypeA2B}, &SupportedTags[2]},
5613 { cmsSigAToB2Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutAtoBType, cmsSigLut8Type}, DecideLUTtypeA2B}, &SupportedTags[3]},
5614 { cmsSigBToA0Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type}, DecideLUTtypeB2A}, &SupportedTags[4]},
5615 { cmsSigBToA1Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type}, DecideLUTtypeB2A}, &SupportedTags[5]},
5616 { cmsSigBToA2Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type}, DecideLUTtypeB2A}, &SupportedTags[6]},
5618 // Allow corbis and its broken XYZ type
5619 { cmsSigRedColorantTag, { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, DecideXYZtype}, &SupportedTags[7]},
5620 { cmsSigGreenColorantTag, { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, DecideXYZtype}, &SupportedTags[8]},
5621 { cmsSigBlueColorantTag, { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, DecideXYZtype}, &SupportedTags[9]},
5623 { cmsSigRedTRCTag, { 1, 3, { cmsSigCurveType, cmsSigParametricCurveType, cmsMonacoBrokenCurveType }, DecideCurveType}, &SupportedTags[10]},
5624 { cmsSigGreenTRCTag, { 1, 3, { cmsSigCurveType, cmsSigParametricCurveType, cmsMonacoBrokenCurveType }, DecideCurveType}, &SupportedTags[11]},
5625 { cmsSigBlueTRCTag, { 1, 3, { cmsSigCurveType, cmsSigParametricCurveType, cmsMonacoBrokenCurveType }, DecideCurveType}, &SupportedTags[12]},
5627 { cmsSigCalibrationDateTimeTag, { 1, 1, { cmsSigDateTimeType }, NULL}, &SupportedTags[13]},
5628 { cmsSigCharTargetTag, { 1, 1, { cmsSigTextType }, NULL}, &SupportedTags[14]},
5630 { cmsSigChromaticAdaptationTag, { 9, 1, { cmsSigS15Fixed16ArrayType }, NULL}, &SupportedTags[15]},
5631 { cmsSigChromaticityTag, { 1, 1, { cmsSigChromaticityType }, NULL}, &SupportedTags[16]},
5632 { cmsSigColorantOrderTag, { 1, 1, { cmsSigColorantOrderType }, NULL}, &SupportedTags[17]},
5633 { cmsSigColorantTableTag, { 1, 1, { cmsSigColorantTableType }, NULL}, &SupportedTags[18]},
5634 { cmsSigColorantTableOutTag, { 1, 1, { cmsSigColorantTableType }, NULL}, &SupportedTags[19]},
5636 { cmsSigCopyrightTag, { 1, 3, { cmsSigTextType, cmsSigMultiLocalizedUnicodeType, cmsSigTextDescriptionType}, DecideTextType}, &SupportedTags[20]},
5637 { cmsSigDateTimeTag, { 1, 1, { cmsSigDateTimeType }, NULL}, &SupportedTags[21]},
5639 { cmsSigDeviceMfgDescTag, { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[22]},
5640 { cmsSigDeviceModelDescTag, { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[23]},
5642 { cmsSigGamutTag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[24]},
5644 { cmsSigGrayTRCTag, { 1, 2, { cmsSigCurveType, cmsSigParametricCurveType }, DecideCurveType}, &SupportedTags[25]},
5645 { cmsSigLuminanceTag, { 1, 1, { cmsSigXYZType }, NULL}, &SupportedTags[26]},
5647 { cmsSigMediaBlackPointTag, { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, NULL}, &SupportedTags[27]},
5648 { cmsSigMediaWhitePointTag, { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, NULL}, &SupportedTags[28]},
5650 { cmsSigNamedColor2Tag, { 1, 1, { cmsSigNamedColor2Type }, NULL}, &SupportedTags[29]},
5652 { cmsSigPreview0Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[30]},
5653 { cmsSigPreview1Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[31]},
5654 { cmsSigPreview2Tag, { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[32]},
5656 { cmsSigProfileDescriptionTag, { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[33]},
5657 { cmsSigProfileSequenceDescTag, { 1, 1, { cmsSigProfileSequenceDescType }, NULL}, &SupportedTags[34]},
5658 { cmsSigTechnologyTag, { 1, 1, { cmsSigSignatureType }, NULL}, &SupportedTags[35]},
5660 { cmsSigColorimetricIntentImageStateTag, { 1, 1, { cmsSigSignatureType }, NULL}, &SupportedTags[36]},
5661 { cmsSigPerceptualRenderingIntentGamutTag, { 1, 1, { cmsSigSignatureType }, NULL}, &SupportedTags[37]},
5662 { cmsSigSaturationRenderingIntentGamutTag, { 1, 1, { cmsSigSignatureType }, NULL}, &SupportedTags[38]},
5664 { cmsSigMeasurementTag, { 1, 1, { cmsSigMeasurementType }, NULL}, &SupportedTags[39]},
5666 { cmsSigPs2CRD0Tag, { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[40]},
5667 { cmsSigPs2CRD1Tag, { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[41]},
5668 { cmsSigPs2CRD2Tag, { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[42]},
5669 { cmsSigPs2CRD3Tag, { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[43]},
5670 { cmsSigPs2CSATag, { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[44]},
5671 { cmsSigPs2RenderingIntentTag, { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[45]},
5673 { cmsSigViewingCondDescTag, { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[46]},
5675 { cmsSigUcrBgTag, { 1, 1, { cmsSigUcrBgType}, NULL}, &SupportedTags[47]},
5676 { cmsSigCrdInfoTag, { 1, 1, { cmsSigCrdInfoType}, NULL}, &SupportedTags[48]},
5678 { cmsSigDToB0Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[49]},
5679 { cmsSigDToB1Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[50]},
5680 { cmsSigDToB2Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[51]},
5681 { cmsSigDToB3Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[52]},
5682 { cmsSigBToD0Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[53]},
5683 { cmsSigBToD1Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[54]},
5684 { cmsSigBToD2Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[55]},
5685 { cmsSigBToD3Tag, { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[56]},
5687 { cmsSigScreeningDescTag, { 1, 1, { cmsSigTextDescriptionType }, NULL}, &SupportedTags[57]},
5688 { cmsSigViewingConditionsTag, { 1, 1, { cmsSigViewingConditionsType }, NULL}, &SupportedTags[58]},
5690 { cmsSigScreeningTag, { 1, 1, { cmsSigScreeningType}, NULL }, &SupportedTags[59]},
5691 { cmsSigVcgtTag, { 1, 1, { cmsSigVcgtType}, NULL }, &SupportedTags[60]},
5692 { cmsSigMetaTag, { 1, 1, { cmsSigDictType}, NULL }, &SupportedTags[61]},
5693 { cmsSigProfileSequenceIdTag, { 1, 1, { cmsSigProfileSequenceIdType}, NULL }, &SupportedTags[62]},
5695 { cmsSigProfileDescriptionMLTag,{ 1, 1, { cmsSigMultiLocalizedUnicodeType}, NULL}, &SupportedTags[63]},
5696 { cmsSigcicpTag, { 1, 1, { cmsSigcicpType}, NULL }, &SupportedTags[64]},
5698 { cmsSigArgyllArtsTag, { 9, 1, { cmsSigS15Fixed16ArrayType}, NULL}, NULL}
5703 Not supported Why
5704 ======================= =========================================
5705 cmsSigOutputResponseTag ==> WARNING, POSSIBLE PATENT ON THIS SUBJECT!
5706 cmsSigNamedColorTag ==> Deprecated
5707 cmsSigDataTag ==> Ancient, unused
5708 cmsSigDeviceSettingsTag ==> Deprecated, useless
5712 _cmsTagPluginChunkType _cmsTagPluginChunk = { NULL };
5715 // Duplicates the zone of memory used by the plug-in in the new context
5716 static
5717 void DupTagList(struct _cmsContext_struct* ctx,
5718 const struct _cmsContext_struct* src)
5720 _cmsTagPluginChunkType newHead = { NULL };
5721 _cmsTagLinkedList* entry;
5722 _cmsTagLinkedList* Anterior = NULL;
5723 _cmsTagPluginChunkType* head = (_cmsTagPluginChunkType*) src->chunks[TagPlugin];
5725 // Walk the list copying all nodes
5726 for (entry = head->Tag;
5727 entry != NULL;
5728 entry = entry ->Next) {
5730 _cmsTagLinkedList *newEntry = ( _cmsTagLinkedList *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTagLinkedList));
5732 if (newEntry == NULL)
5733 return;
5735 // We want to keep the linked list order, so this is a little bit tricky
5736 newEntry -> Next = NULL;
5737 if (Anterior)
5738 Anterior -> Next = newEntry;
5740 Anterior = newEntry;
5742 if (newHead.Tag == NULL)
5743 newHead.Tag = newEntry;
5746 ctx ->chunks[TagPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsTagPluginChunkType));
5749 void _cmsAllocTagPluginChunk(struct _cmsContext_struct* ctx,
5750 const struct _cmsContext_struct* src)
5752 if (src != NULL) {
5754 DupTagList(ctx, src);
5756 else {
5757 static _cmsTagPluginChunkType TagPluginChunk = { NULL };
5758 ctx ->chunks[TagPlugin] = _cmsSubAllocDup(ctx ->MemPool, &TagPluginChunk, sizeof(_cmsTagPluginChunkType));
5763 cmsBool _cmsRegisterTagPlugin(cmsContext id, cmsPluginBase* Data)
5765 cmsPluginTag* Plugin = (cmsPluginTag*) Data;
5766 _cmsTagLinkedList *pt;
5767 _cmsTagPluginChunkType* TagPluginChunk = ( _cmsTagPluginChunkType*) _cmsContextGetClientChunk(id, TagPlugin);
5769 if (Data == NULL) {
5771 TagPluginChunk->Tag = NULL;
5772 return TRUE;
5775 pt = (_cmsTagLinkedList*) _cmsPluginMalloc(id, sizeof(_cmsTagLinkedList));
5776 if (pt == NULL) return FALSE;
5778 pt ->Signature = Plugin ->Signature;
5779 pt ->Descriptor = Plugin ->Descriptor;
5780 pt ->Next = TagPluginChunk ->Tag;
5782 TagPluginChunk ->Tag = pt;
5784 return TRUE;
5787 // Return a descriptor for a given tag or NULL
5788 cmsTagDescriptor* _cmsGetTagDescriptor(cmsContext ContextID, cmsTagSignature sig)
5790 _cmsTagLinkedList* pt;
5791 _cmsTagPluginChunkType* TagPluginChunk = ( _cmsTagPluginChunkType*) _cmsContextGetClientChunk(ContextID, TagPlugin);
5793 for (pt = TagPluginChunk->Tag;
5794 pt != NULL;
5795 pt = pt ->Next) {
5797 if (sig == pt -> Signature) return &pt ->Descriptor;
5800 for (pt = SupportedTags;
5801 pt != NULL;
5802 pt = pt ->Next) {
5804 if (sig == pt -> Signature) return &pt ->Descriptor;
5807 return NULL;