Home | History | Annotate | Download | only in src
      1 //---------------------------------------------------------------------------------
      2 //
      3 //  Little Color Management System
      4 //  Copyright (c) 1998-2014 Marti Maria Saguer
      5 //
      6 // Permission is hereby granted, free of charge, to any person obtaining
      7 // a copy of this software and associated documentation files (the "Software"),
      8 // to deal in the Software without restriction, including without limitation
      9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
     10 // and/or sell copies of the Software, and to permit persons to whom the Software
     11 // is furnished to do so, subject to the following conditions:
     12 //
     13 // The above copyright notice and this permission notice shall be included in
     14 // all copies or substantial portions of the Software.
     15 //
     16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
     18 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
     20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     23 //
     24 //---------------------------------------------------------------------------------
     25 //
     26 
     27 #include "lcms2_internal.h"
     28 
     29 // 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 appropiate 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 //--------------------------------------------------------------------------------------------------
     38 
     39 // Some broken types
     40 #define cmsCorbisBrokenXYZtype    ((cmsTagTypeSignature) 0x17A505B8)
     41 #define cmsMonacoBrokenCurveType  ((cmsTagTypeSignature) 0x9478ee00)
     42 
     43 // This is the linked list that keeps track of the defined types
     44 typedef struct _cmsTagTypeLinkedList_st {
     45 
     46     cmsTagTypeHandler Handler;
     47     struct _cmsTagTypeLinkedList_st* Next;
     48 
     49 } _cmsTagTypeLinkedList;
     50 
     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
     56 
     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 }
     59 
     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 }
     62 
     63 // Register a new type handler. This routine is shared between normal types and MPE. LinkedList points to the optional list head
     64 static
     65 cmsBool RegisterTypesPlugin(cmsContext id, cmsPluginBase* Data, _cmsMemoryClient pos)
     66 {
     67     cmsPluginTagType* Plugin = (cmsPluginTagType*) Data;
     68     _cmsTagTypePluginChunkType* ctx = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(id, pos);
     69     _cmsTagTypeLinkedList *pt;
     70 
     71     // Calling the function with NULL as plug-in would unregister the plug in.
     72     if (Data == NULL) {
     73 
     74         // There is no need to set free the memory, as pool is destroyed as a whole.
     75         ctx ->TagTypes = NULL;
     76         return TRUE;
     77     }
     78 
     79     // Registering happens in plug-in memory pool.
     80     pt = (_cmsTagTypeLinkedList*) _cmsPluginMalloc(id, sizeof(_cmsTagTypeLinkedList));
     81     if (pt == NULL) return FALSE;
     82 
     83     pt ->Handler   = Plugin ->Handler;
     84     pt ->Next      = ctx ->TagTypes;
     85 
     86     ctx ->TagTypes = pt;
     87 
     88     return TRUE;
     89 }
     90 
     91 // Return handler for a given type or NULL if not found. Shared between normal types and MPE. It first tries the additons
     92 // made by plug-ins and then the built-in defaults.
     93 static
     94 cmsTagTypeHandler* GetHandler(cmsTagTypeSignature sig, _cmsTagTypeLinkedList* PluginLinkedList, _cmsTagTypeLinkedList* DefaultLinkedList)
     95 {
     96     _cmsTagTypeLinkedList* pt;
     97 
     98     for (pt = PluginLinkedList;
     99          pt != NULL;
    100          pt = pt ->Next) {
    101 
    102             if (sig == pt -> Handler.Signature) return &pt ->Handler;
    103     }
    104 
    105     for (pt = DefaultLinkedList;
    106          pt != NULL;
    107          pt = pt ->Next) {
    108 
    109             if (sig == pt -> Handler.Signature) return &pt ->Handler;
    110     }
    111 
    112     return NULL;
    113 }
    114 
    115 
    116 // Auxiliar to convert UTF-32 to UTF-16 in some cases
    117 static
    118 cmsBool _cmsWriteWCharArray(cmsIOHANDLER* io, cmsUInt32Number n, const wchar_t* Array)
    119 {
    120     cmsUInt32Number i;
    121 
    122     _cmsAssert(io != NULL);
    123     _cmsAssert(!(Array == NULL && n > 0));
    124 
    125     for (i=0; i < n; i++) {
    126         if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) Array[i])) return FALSE;
    127     }
    128 
    129     return TRUE;
    130 }
    131 
    132 // Auxiliar to read an array of wchar_t
    133 static
    134 cmsBool _cmsReadWCharArray(cmsIOHANDLER* io, cmsUInt32Number n, wchar_t* Array)
    135 {
    136     cmsUInt32Number i;
    137     cmsUInt16Number tmp;
    138 
    139     _cmsAssert(io != NULL);
    140 
    141     for (i=0; i < n; i++) {
    142 
    143         if (Array != NULL) {
    144 
    145             if (!_cmsReadUInt16Number(io, &tmp)) return FALSE;
    146             Array[i] = (wchar_t) tmp;
    147         }
    148         else {
    149             if (!_cmsReadUInt16Number(io, NULL)) return FALSE;
    150         }
    151 
    152     }
    153     return TRUE;
    154 }
    155 
    156 // To deal with position tables
    157 typedef cmsBool (* PositionTableEntryFn)(struct _cms_typehandler_struct* self,
    158                                              cmsIOHANDLER* io,
    159                                              void* Cargo,
    160                                              cmsUInt32Number n,
    161                                              cmsUInt32Number SizeOfTag);
    162 
    163 // Helper function to deal with position tables as decribed in ICC spec 4.3
    164 // A table of n elements is readed, where first comes n records containing offsets and sizes and
    165 // then a block containing the data itself. This allows to reuse same data in more than one entry
    166 static
    167 cmsBool ReadPositionTable(struct _cms_typehandler_struct* self,
    168                               cmsIOHANDLER* io,
    169                               cmsUInt32Number Count,
    170                               cmsUInt32Number BaseOffset,
    171                               void *Cargo,
    172                               PositionTableEntryFn ElementFn)
    173 {
    174     cmsUInt32Number i;
    175     cmsUInt32Number *ElementOffsets = NULL, *ElementSizes = NULL;
    176 
    177     // Let's take the offsets to each element
    178     ElementOffsets = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number));
    179     if (ElementOffsets == NULL) goto Error;
    180 
    181     ElementSizes = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number));
    182     if (ElementSizes == NULL) goto Error;
    183 
    184     for (i=0; i < Count; i++) {
    185 
    186         if (!_cmsReadUInt32Number(io, &ElementOffsets[i])) goto Error;
    187         if (!_cmsReadUInt32Number(io, &ElementSizes[i])) goto Error;
    188 
    189         ElementOffsets[i] += BaseOffset;
    190     }
    191 
    192     // Seek to each element and read it
    193     for (i=0; i < Count; i++) {
    194 
    195         if (!io -> Seek(io, ElementOffsets[i])) goto Error;
    196 
    197         // This is the reader callback
    198         if (!ElementFn(self, io, Cargo, i, ElementSizes[i])) goto Error;
    199     }
    200 
    201     // Success
    202     if (ElementOffsets != NULL) _cmsFree(io ->ContextID, ElementOffsets);
    203     if (ElementSizes != NULL) _cmsFree(io ->ContextID, ElementSizes);
    204     return TRUE;
    205 
    206 Error:
    207     if (ElementOffsets != NULL) _cmsFree(io ->ContextID, ElementOffsets);
    208     if (ElementSizes != NULL) _cmsFree(io ->ContextID, ElementSizes);
    209     return FALSE;
    210 }
    211 
    212 // Same as anterior, but for write position tables
    213 static
    214 cmsBool WritePositionTable(struct _cms_typehandler_struct* self,
    215                                cmsIOHANDLER* io,
    216                                cmsUInt32Number SizeOfTag,
    217                                cmsUInt32Number Count,
    218                                cmsUInt32Number BaseOffset,
    219                                void *Cargo,
    220                                PositionTableEntryFn ElementFn)
    221 {
    222     cmsUInt32Number i;
    223     cmsUInt32Number DirectoryPos, CurrentPos, Before;
    224     cmsUInt32Number *ElementOffsets = NULL, *ElementSizes = NULL;
    225 
    226      // Create table
    227     ElementOffsets = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number));
    228     if (ElementOffsets == NULL) goto Error;
    229 
    230     ElementSizes = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number));
    231     if (ElementSizes == NULL) goto Error;
    232 
    233     // Keep starting position of curve offsets
    234     DirectoryPos = io ->Tell(io);
    235 
    236     // Write a fake directory to be filled latter on
    237     for (i=0; i < Count; i++) {
    238 
    239         if (!_cmsWriteUInt32Number(io, 0)) goto Error;  // Offset
    240         if (!_cmsWriteUInt32Number(io, 0)) goto Error;  // size
    241     }
    242 
    243     // Write each element. Keep track of the size as well.
    244     for (i=0; i < Count; i++) {
    245 
    246         Before = io ->Tell(io);
    247         ElementOffsets[i] = Before - BaseOffset;
    248 
    249         // Callback to write...
    250         if (!ElementFn(self, io, Cargo, i, SizeOfTag)) goto Error;
    251 
    252         // Now the size
    253         ElementSizes[i] = io ->Tell(io) - Before;
    254     }
    255 
    256     // Write the directory
    257     CurrentPos = io ->Tell(io);
    258     if (!io ->Seek(io, DirectoryPos)) goto Error;
    259 
    260     for (i=0; i <  Count; i++) {
    261         if (!_cmsWriteUInt32Number(io, ElementOffsets[i])) goto Error;
    262         if (!_cmsWriteUInt32Number(io, ElementSizes[i])) goto Error;
    263     }
    264 
    265     if (!io ->Seek(io, CurrentPos)) goto Error;
    266 
    267     if (ElementOffsets != NULL) _cmsFree(io ->ContextID, ElementOffsets);
    268     if (ElementSizes != NULL) _cmsFree(io ->ContextID, ElementSizes);
    269     return TRUE;
    270 
    271 Error:
    272     if (ElementOffsets != NULL) _cmsFree(io ->ContextID, ElementOffsets);
    273     if (ElementSizes != NULL) _cmsFree(io ->ContextID, ElementSizes);
    274     return FALSE;
    275 }
    276 
    277 
    278 // ********************************************************************************
    279 // Type XYZ. Only one value is allowed
    280 // ********************************************************************************
    281 
    282 //The XYZType contains an array of three encoded values for the XYZ tristimulus
    283 //values. Tristimulus values must be non-negative. The signed encoding allows for
    284 //implementation optimizations by minimizing the number of fixed formats.
    285 
    286 
    287 static
    288 void *Type_XYZ_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
    289 {
    290     cmsCIEXYZ* xyz;
    291 
    292     *nItems = 0;
    293     xyz = (cmsCIEXYZ*) _cmsMallocZero(self ->ContextID, sizeof(cmsCIEXYZ));
    294     if (xyz == NULL) return NULL;
    295 
    296     if (!_cmsReadXYZNumber(io, xyz)) {
    297         _cmsFree(self ->ContextID, xyz);
    298         return NULL;
    299     }
    300 
    301     *nItems = 1;
    302     return (void*) xyz;
    303 
    304     cmsUNUSED_PARAMETER(SizeOfTag);
    305 }
    306 
    307 static
    308 cmsBool  Type_XYZ_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
    309 {
    310     return _cmsWriteXYZNumber(io, (cmsCIEXYZ*) Ptr);
    311 
    312     cmsUNUSED_PARAMETER(nItems);
    313     cmsUNUSED_PARAMETER(self);
    314 }
    315 
    316 static
    317 void* Type_XYZ_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
    318 {
    319     return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsCIEXYZ));
    320 
    321     cmsUNUSED_PARAMETER(n);
    322 }
    323 
    324 static
    325 void Type_XYZ_Free(struct _cms_typehandler_struct* self, void *Ptr)
    326 {
    327     _cmsFree(self ->ContextID, Ptr);
    328 }
    329 
    330 
    331 static
    332 cmsTagTypeSignature DecideXYZtype(cmsFloat64Number ICCVersion, const void *Data)
    333 {
    334     return cmsSigXYZType;
    335 
    336     cmsUNUSED_PARAMETER(ICCVersion);
    337     cmsUNUSED_PARAMETER(Data);
    338 }
    339 
    340 
    341 // ********************************************************************************
    342 // Type chromaticity. Only one value is allowed
    343 // ********************************************************************************
    344 // The chromaticity tag type provides basic chromaticity data and type of
    345 // phosphors or colorants of a monitor to applications and utilities.
    346 
    347 static
    348 void *Type_Chromaticity_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
    349 {
    350     cmsCIExyYTRIPLE* chrm;
    351     cmsUInt16Number nChans, Table;
    352 
    353     *nItems = 0;
    354     chrm =  (cmsCIExyYTRIPLE*) _cmsMallocZero(self ->ContextID, sizeof(cmsCIExyYTRIPLE));
    355     if (chrm == NULL) return NULL;
    356 
    357     if (!_cmsReadUInt16Number(io, &nChans)) goto Error;
    358 
    359     // Let's recover from a bug introduced in early versions of lcms1
    360     if (nChans == 0 && SizeOfTag == 32) {
    361 
    362         if (!_cmsReadUInt16Number(io, NULL)) goto Error;
    363         if (!_cmsReadUInt16Number(io, &nChans)) goto Error;
    364     }
    365 
    366     if (nChans != 3) goto Error;
    367 
    368     if (!_cmsReadUInt16Number(io, &Table)) goto Error;
    369 
    370     if (!_cmsRead15Fixed16Number(io, &chrm ->Red.x)) goto Error;
    371     if (!_cmsRead15Fixed16Number(io, &chrm ->Red.y)) goto Error;
    372 
    373     chrm ->Red.Y = 1.0;
    374 
    375     if (!_cmsRead15Fixed16Number(io, &chrm ->Green.x)) goto Error;
    376     if (!_cmsRead15Fixed16Number(io, &chrm ->Green.y)) goto Error;
    377 
    378     chrm ->Green.Y = 1.0;
    379 
    380     if (!_cmsRead15Fixed16Number(io, &chrm ->Blue.x)) goto Error;
    381     if (!_cmsRead15Fixed16Number(io, &chrm ->Blue.y)) goto Error;
    382 
    383     chrm ->Blue.Y = 1.0;
    384 
    385     *nItems = 1;
    386     return (void*) chrm;
    387 
    388 Error:
    389     _cmsFree(self ->ContextID, (void*) chrm);
    390     return NULL;
    391 
    392     cmsUNUSED_PARAMETER(SizeOfTag);
    393 }
    394 
    395 static
    396 cmsBool  SaveOneChromaticity(cmsFloat64Number x, cmsFloat64Number y, cmsIOHANDLER* io)
    397 {
    398     if (!_cmsWriteUInt32Number(io, _cmsDoubleTo15Fixed16(x))) return FALSE;
    399     if (!_cmsWriteUInt32Number(io, _cmsDoubleTo15Fixed16(y))) return FALSE;
    400 
    401     return TRUE;
    402 }
    403 
    404 static
    405 cmsBool  Type_Chromaticity_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
    406 {
    407     cmsCIExyYTRIPLE* chrm = (cmsCIExyYTRIPLE*) Ptr;
    408 
    409     if (!_cmsWriteUInt16Number(io, 3)) return FALSE;        // nChannels
    410     if (!_cmsWriteUInt16Number(io, 0)) return FALSE;        // Table
    411 
    412     if (!SaveOneChromaticity(chrm -> Red.x,   chrm -> Red.y, io)) return FALSE;
    413     if (!SaveOneChromaticity(chrm -> Green.x, chrm -> Green.y, io)) return FALSE;
    414     if (!SaveOneChromaticity(chrm -> Blue.x,  chrm -> Blue.y, io)) return FALSE;
    415 
    416     return TRUE;
    417 
    418     cmsUNUSED_PARAMETER(nItems);
    419     cmsUNUSED_PARAMETER(self);
    420 }
    421 
    422 static
    423 void* Type_Chromaticity_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
    424 {
    425     return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsCIExyYTRIPLE));
    426 
    427     cmsUNUSED_PARAMETER(n);
    428 }
    429 
    430 static
    431 void Type_Chromaticity_Free(struct _cms_typehandler_struct* self, void* Ptr)
    432 {
    433     _cmsFree(self ->ContextID, Ptr);
    434 }
    435 
    436 
    437 // ********************************************************************************
    438 // Type cmsSigColorantOrderType
    439 // ********************************************************************************
    440 
    441 // This is an optional tag which specifies the laydown order in which colorants will
    442 // be printed on an n-colorant device. The laydown order may be the same as the
    443 // channel generation order listed in the colorantTableTag or the channel order of a
    444 // colour space such as CMYK, in which case this tag is not needed. When this is not
    445 // the case (for example, ink-towers sometimes use the order KCMY), this tag may be
    446 // used to specify the laydown order of the colorants.
    447 
    448 
    449 static
    450 void *Type_ColorantOrderType_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
    451 {
    452     cmsUInt8Number* ColorantOrder;
    453     cmsUInt32Number Count;
    454 
    455     *nItems = 0;
    456     if (!_cmsReadUInt32Number(io, &Count)) return NULL;
    457     if (Count > cmsMAXCHANNELS) return NULL;
    458 
    459     ColorantOrder = (cmsUInt8Number*) _cmsCalloc(self ->ContextID, cmsMAXCHANNELS, sizeof(cmsUInt8Number));
    460     if (ColorantOrder == NULL) return NULL;
    461 
    462     // We use FF as end marker
    463     memset(ColorantOrder, 0xFF, cmsMAXCHANNELS * sizeof(cmsUInt8Number));
    464 
    465     if (io ->Read(io, ColorantOrder, sizeof(cmsUInt8Number), Count) != Count) {
    466 
    467         _cmsFree(self ->ContextID, (void*) ColorantOrder);
    468         return NULL;
    469     }
    470 
    471     *nItems = 1;
    472     return (void*) ColorantOrder;
    473 
    474     cmsUNUSED_PARAMETER(SizeOfTag);
    475 }
    476 
    477 static
    478 cmsBool Type_ColorantOrderType_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
    479 {
    480     cmsUInt8Number*  ColorantOrder = (cmsUInt8Number*) Ptr;
    481     cmsUInt32Number i, sz, Count;
    482 
    483     // Get the length
    484     for (Count=i=0; i < cmsMAXCHANNELS; i++) {
    485         if (ColorantOrder[i] != 0xFF) Count++;
    486     }
    487 
    488     if (!_cmsWriteUInt32Number(io, Count)) return FALSE;
    489 
    490     sz = Count * sizeof(cmsUInt8Number);
    491     if (!io -> Write(io, sz, ColorantOrder)) return FALSE;
    492 
    493     return TRUE;
    494 
    495     cmsUNUSED_PARAMETER(nItems);
    496     cmsUNUSED_PARAMETER(self);
    497 }
    498 
    499 static
    500 void* Type_ColorantOrderType_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
    501 {
    502     return _cmsDupMem(self ->ContextID, Ptr, cmsMAXCHANNELS * sizeof(cmsUInt8Number));
    503 
    504     cmsUNUSED_PARAMETER(n);
    505 }
    506 
    507 
    508 static
    509 void Type_ColorantOrderType_Free(struct _cms_typehandler_struct* self, void* Ptr)
    510 {
    511     _cmsFree(self ->ContextID, Ptr);
    512 }
    513 
    514 // ********************************************************************************
    515 // Type cmsSigS15Fixed16ArrayType
    516 // ********************************************************************************
    517 // This type represents an array of generic 4-byte/32-bit fixed point quantity.
    518 // The number of values is determined from the size of the tag.
    519 
    520 static
    521 void *Type_S15Fixed16_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
    522 {
    523     cmsFloat64Number*  array_double;
    524     cmsUInt32Number i, n;
    525 
    526     *nItems = 0;
    527     n = SizeOfTag / sizeof(cmsUInt32Number);
    528     array_double = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, n, sizeof(cmsFloat64Number));
    529     if (array_double == NULL) return NULL;
    530 
    531     for (i=0; i < n; i++) {
    532 
    533         if (!_cmsRead15Fixed16Number(io, &array_double[i])) {
    534 
    535             _cmsFree(self ->ContextID, array_double);
    536             return NULL;
    537         }
    538     }
    539 
    540     *nItems = n;
    541     return (void*) array_double;
    542 }
    543 
    544 static
    545 cmsBool Type_S15Fixed16_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
    546 {
    547     cmsFloat64Number* Value = (cmsFloat64Number*) Ptr;
    548     cmsUInt32Number i;
    549 
    550     for (i=0; i < nItems; i++) {
    551 
    552         if (!_cmsWrite15Fixed16Number(io, Value[i])) return FALSE;
    553     }
    554 
    555     return TRUE;
    556 
    557     cmsUNUSED_PARAMETER(self);
    558 }
    559 
    560 static
    561 void* Type_S15Fixed16_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
    562 {
    563     return _cmsDupMem(self ->ContextID, Ptr, n * sizeof(cmsFloat64Number));
    564 }
    565 
    566 
    567 static
    568 void Type_S15Fixed16_Free(struct _cms_typehandler_struct* self, void* Ptr)
    569 {
    570     _cmsFree(self ->ContextID, Ptr);
    571 }
    572 
    573 // ********************************************************************************
    574 // Type cmsSigU16Fixed16ArrayType
    575 // ********************************************************************************
    576 // This type represents an array of generic 4-byte/32-bit quantity.
    577 // The number of values is determined from the size of the tag.
    578 
    579 
    580 static
    581 void *Type_U16Fixed16_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
    582 {
    583     cmsFloat64Number*  array_double;
    584     cmsUInt32Number v;
    585     cmsUInt32Number i, n;
    586 
    587     *nItems = 0;
    588     n = SizeOfTag / sizeof(cmsUInt32Number);
    589     array_double = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, n, sizeof(cmsFloat64Number));
    590     if (array_double == NULL) return NULL;
    591 
    592     for (i=0; i < n; i++) {
    593 
    594         if (!_cmsReadUInt32Number(io, &v)) {
    595             _cmsFree(self ->ContextID, (void*) array_double);
    596             return NULL;
    597         }
    598 
    599         // Convert to cmsFloat64Number
    600         array_double[i] =  (cmsFloat64Number) (v / 65536.0);
    601     }
    602 
    603     *nItems = n;
    604     return (void*) array_double;
    605 }
    606 
    607 static
    608 cmsBool Type_U16Fixed16_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
    609 {
    610     cmsFloat64Number* Value = (cmsFloat64Number*) Ptr;
    611     cmsUInt32Number i;
    612 
    613     for (i=0; i < nItems; i++) {
    614 
    615         cmsUInt32Number v = (cmsUInt32Number) floor(Value[i]*65536.0 + 0.5);
    616 
    617         if (!_cmsWriteUInt32Number(io, v)) return FALSE;
    618     }
    619 
    620     return TRUE;
    621 
    622     cmsUNUSED_PARAMETER(self);
    623 }
    624 
    625 
    626 static
    627 void* Type_U16Fixed16_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
    628 {
    629     return _cmsDupMem(self ->ContextID, Ptr, n * sizeof(cmsFloat64Number));
    630 }
    631 
    632 static
    633 void Type_U16Fixed16_Free(struct _cms_typehandler_struct* self, void* Ptr)
    634 {
    635     _cmsFree(self ->ContextID, Ptr);
    636 }
    637 
    638 // ********************************************************************************
    639 // Type cmsSigSignatureType
    640 // ********************************************************************************
    641 //
    642 // The signatureType contains a four-byte sequence, Sequences of less than four
    643 // characters are padded at the end with spaces, 20h.
    644 // Typically this type is used for registered tags that can be displayed on many
    645 // development systems as a sequence of four characters.
    646 
    647 static
    648 void *Type_Signature_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
    649 {
    650     cmsSignature* SigPtr = (cmsSignature*) _cmsMalloc(self ->ContextID, sizeof(cmsSignature));
    651     if (SigPtr == NULL) return NULL;
    652 
    653      if (!_cmsReadUInt32Number(io, SigPtr)) return NULL;
    654      *nItems = 1;
    655 
    656      return SigPtr;
    657 
    658      cmsUNUSED_PARAMETER(SizeOfTag);
    659 }
    660 
    661 static
    662 cmsBool  Type_Signature_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
    663 {
    664     cmsSignature* SigPtr = (cmsSignature*) Ptr;
    665 
    666     return _cmsWriteUInt32Number(io, *SigPtr);
    667 
    668     cmsUNUSED_PARAMETER(nItems);
    669     cmsUNUSED_PARAMETER(self);
    670 }
    671 
    672 static
    673 void* Type_Signature_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
    674 {
    675     return _cmsDupMem(self ->ContextID, Ptr, n * sizeof(cmsSignature));
    676 }
    677 
    678 static
    679 void Type_Signature_Free(struct _cms_typehandler_struct* self, void* Ptr)
    680 {
    681     _cmsFree(self ->ContextID, Ptr);
    682 }
    683 
    684 
    685 // ********************************************************************************
    686 // Type cmsSigTextType
    687 // ********************************************************************************
    688 //
    689 // The textType is a simple text structure that contains a 7-bit ASCII text string.
    690 // The length of the string is obtained by subtracting 8 from the element size portion
    691 // of the tag itself. This string must be terminated with a 00h byte.
    692 
    693 static
    694 void *Type_Text_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
    695 {
    696     char* Text = NULL;
    697     cmsMLU* mlu = NULL;
    698 
    699     // Create a container
    700     mlu = cmsMLUalloc(self ->ContextID, 1);
    701     if (mlu == NULL) return NULL;
    702 
    703     *nItems = 0;
    704 
    705     // We need to store the "\0" at the end, so +1
    706     if (SizeOfTag == UINT_MAX) goto Error;
    707 
    708     Text = (char*) _cmsMalloc(self ->ContextID, SizeOfTag + 1);
    709     if (Text == NULL) goto Error;
    710 
    711     if (io -> Read(io, Text, sizeof(char), SizeOfTag) != SizeOfTag) goto Error;
    712 
    713     // Make sure text is properly ended
    714     Text[SizeOfTag] = 0;
    715     *nItems = 1;
    716 
    717     // Keep the result
    718     if (!cmsMLUsetASCII(mlu, cmsNoLanguage, cmsNoCountry, Text)) goto Error;
    719 
    720     _cmsFree(self ->ContextID, Text);
    721     return (void*) mlu;
    722 
    723 Error:
    724     if (mlu != NULL)
    725         cmsMLUfree(mlu);
    726     if (Text != NULL)
    727         _cmsFree(self ->ContextID, Text);
    728 
    729     return NULL;
    730 }
    731 
    732 // The conversion implies to choose a language. So, we choose the actual language.
    733 static
    734 cmsBool Type_Text_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
    735 {
    736     cmsMLU* mlu = (cmsMLU*) Ptr;
    737     cmsUInt32Number size;
    738     cmsBool  rc;
    739     char* Text;
    740 
    741     // Get the size of the string. Note there is an extra "\0" at the end
    742     size = cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, NULL, 0);
    743     if (size == 0) return FALSE;       // Cannot be zero!
    744 
    745     // Create memory
    746     Text = (char*) _cmsMalloc(self ->ContextID, size);
    747     if (Text == NULL) return FALSE;
    748 
    749     cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, Text, size);
    750 
    751     // Write it, including separator
    752     rc = io ->Write(io, size, Text);
    753 
    754     _cmsFree(self ->ContextID, Text);
    755     return rc;
    756 
    757     cmsUNUSED_PARAMETER(nItems);
    758 }
    759 
    760 static
    761 void* Type_Text_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
    762 {
    763     return (void*) cmsMLUdup((cmsMLU*) Ptr);
    764 
    765     cmsUNUSED_PARAMETER(n);
    766     cmsUNUSED_PARAMETER(self);
    767 }
    768 
    769 
    770 static
    771 void Type_Text_Free(struct _cms_typehandler_struct* self, void* Ptr)
    772 {
    773     cmsMLU* mlu = (cmsMLU*) Ptr;
    774     cmsMLUfree(mlu);
    775     return;
    776 
    777     cmsUNUSED_PARAMETER(self);
    778 }
    779 
    780 static
    781 cmsTagTypeSignature DecideTextType(cmsFloat64Number ICCVersion, const void *Data)
    782 {
    783     if (ICCVersion >= 4.0)
    784         return cmsSigMultiLocalizedUnicodeType;
    785 
    786     return cmsSigTextType;
    787 
    788     cmsUNUSED_PARAMETER(Data);
    789 }
    790 
    791 
    792 // ********************************************************************************
    793 // Type cmsSigDataType
    794 // ********************************************************************************
    795 
    796 // General purpose data type
    797 static
    798 void *Type_Data_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
    799 {
    800     cmsICCData* BinData;
    801     cmsUInt32Number LenOfData;
    802 
    803     *nItems = 0;
    804 
    805     if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL;
    806 
    807     LenOfData = SizeOfTag - sizeof(cmsUInt32Number);
    808     if (LenOfData > INT_MAX) return NULL;
    809 
    810     BinData = (cmsICCData*) _cmsMalloc(self ->ContextID, sizeof(cmsICCData) + LenOfData - 1);
    811     if (BinData == NULL) return NULL;
    812 
    813     BinData ->len = LenOfData;
    814     if (!_cmsReadUInt32Number(io, &BinData->flag)) {
    815         _cmsFree(self ->ContextID, BinData);
    816         return NULL;
    817     }
    818 
    819     if (io -> Read(io, BinData ->data, sizeof(cmsUInt8Number), LenOfData) != LenOfData) {
    820 
    821         _cmsFree(self ->ContextID, BinData);
    822         return NULL;
    823     }
    824 
    825     *nItems = 1;
    826 
    827     return (void*) BinData;
    828 }
    829 
    830 
    831 static
    832 cmsBool Type_Data_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
    833 {
    834    cmsICCData* BinData = (cmsICCData*) Ptr;
    835 
    836    if (!_cmsWriteUInt32Number(io, BinData ->flag)) return FALSE;
    837 
    838    return io ->Write(io, BinData ->len, BinData ->data);
    839 
    840    cmsUNUSED_PARAMETER(nItems);
    841    cmsUNUSED_PARAMETER(self);
    842 }
    843 
    844 
    845 static
    846 void* Type_Data_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
    847 {
    848     cmsICCData* BinData = (cmsICCData*) Ptr;
    849 
    850     return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsICCData) + BinData ->len - 1);
    851 
    852     cmsUNUSED_PARAMETER(n);
    853 }
    854 
    855 static
    856 void Type_Data_Free(struct _cms_typehandler_struct* self, void* Ptr)
    857 {
    858     _cmsFree(self ->ContextID, Ptr);
    859 }
    860 
    861 // ********************************************************************************
    862 // Type cmsSigTextDescriptionType
    863 // ********************************************************************************
    864 
    865 static
    866 void *Type_Text_Description_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
    867 {
    868     char* Text = NULL;
    869     cmsMLU* mlu = NULL;
    870     cmsUInt32Number  AsciiCount;
    871     cmsUInt32Number  i, UnicodeCode, UnicodeCount;
    872     cmsUInt16Number  ScriptCodeCode, Dummy;
    873     cmsUInt8Number   ScriptCodeCount;
    874 
    875     *nItems = 0;
    876 
    877     //  One dword should be there
    878     if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL;
    879 
    880     // Read len of ASCII
    881     if (!_cmsReadUInt32Number(io, &AsciiCount)) return NULL;
    882     SizeOfTag -= sizeof(cmsUInt32Number);
    883 
    884     // Check for size
    885     if (SizeOfTag < AsciiCount) return NULL;
    886 
    887     // All seems Ok, allocate the container
    888     mlu = cmsMLUalloc(self ->ContextID, 1);
    889     if (mlu == NULL) return NULL;
    890 
    891     // As many memory as size of tag
    892     Text = (char*) _cmsMalloc(self ->ContextID, AsciiCount + 1);
    893     if (Text == NULL) goto Error;
    894 
    895     // Read it
    896     if (io ->Read(io, Text, sizeof(char), AsciiCount) != AsciiCount) goto Error;
    897     SizeOfTag -= AsciiCount;
    898 
    899     // Make sure there is a terminator
    900     Text[AsciiCount] = 0;
    901 
    902     // Set the MLU entry. From here we can be tolerant to wrong types
    903     if (!cmsMLUsetASCII(mlu, cmsNoLanguage, cmsNoCountry, Text)) goto Error;
    904     _cmsFree(self ->ContextID, (void*) Text);
    905     Text = NULL;
    906 
    907     // Skip Unicode code
    908     if (SizeOfTag < 2* sizeof(cmsUInt32Number)) goto Done;
    909     if (!_cmsReadUInt32Number(io, &UnicodeCode)) goto Done;
    910     if (!_cmsReadUInt32Number(io, &UnicodeCount)) goto Done;
    911     SizeOfTag -= 2* sizeof(cmsUInt32Number);
    912 
    913     if (SizeOfTag < UnicodeCount*sizeof(cmsUInt16Number)) goto Done;
    914 
    915     for (i=0; i < UnicodeCount; i++) {
    916         if (!io ->Read(io, &Dummy, sizeof(cmsUInt16Number), 1)) goto Done;
    917     }
    918     SizeOfTag -= UnicodeCount*sizeof(cmsUInt16Number);
    919 
    920     // Skip ScriptCode code if present. Some buggy profiles does have less
    921     // data that stricttly required. We need to skip it as this type may come
    922     // embedded in other types.
    923 
    924     if (SizeOfTag >= sizeof(cmsUInt16Number) + sizeof(cmsUInt8Number) + 67) {
    925 
    926         if (!_cmsReadUInt16Number(io, &ScriptCodeCode)) goto Done;
    927         if (!_cmsReadUInt8Number(io,  &ScriptCodeCount)) goto Done;
    928 
    929         // Skip rest of tag
    930         for (i=0; i < 67; i++) {
    931             if (!io ->Read(io, &Dummy, sizeof(cmsUInt8Number), 1)) goto Error;
    932         }
    933     }
    934 
    935 Done:
    936 
    937     *nItems = 1;
    938     return mlu;
    939 
    940 Error:
    941     if (Text) _cmsFree(self ->ContextID, (void*) Text);
    942     if (mlu) cmsMLUfree(mlu);
    943     return NULL;
    944 }
    945 
    946 
    947 // This tag can come IN UNALIGNED SIZE. In order to prevent issues, we force zeros on description to align it
    948 static
    949 cmsBool  Type_Text_Description_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
    950 {
    951     cmsMLU* mlu = (cmsMLU*) Ptr;
    952     char *Text = NULL;
    953     wchar_t *Wide = NULL;
    954     cmsUInt32Number len, len_aligned, len_filler_alignment;
    955     cmsBool  rc = FALSE;
    956     char Filler[68];
    957 
    958     // Used below for writting zeroes
    959     memset(Filler, 0, sizeof(Filler));
    960 
    961     // Get the len of string
    962     len = cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, NULL, 0);
    963 
    964     // From ICC3.4: It has been found that textDescriptionType can contain misaligned data
    965     //(see clause 4.1 for the definition of aligned?. Because the Unicode language
    966     // code and Unicode count immediately follow the ASCII description, their
    967     // alignment is not correct if the ASCII count is not a multiple of four. The
    968     // ScriptCode code is misaligned when the ASCII count is odd. Profile reading and
    969     // writing software must be written carefully in order to handle these alignment
    970     // problems.
    971 
    972     // Compute an aligned size
    973     len_aligned = _cmsALIGNLONG(len);
    974     len_filler_alignment = len_aligned - len;
    975 
    976     // Null strings
    977     if (len <= 0) {
    978 
    979         Text = (char*)    _cmsDupMem(self ->ContextID, "", sizeof(char));
    980         Wide = (wchar_t*) _cmsDupMem(self ->ContextID, L"", sizeof(wchar_t));
    981     }
    982     else {
    983         // Create independent buffers
    984         Text = (char*) _cmsCalloc(self ->ContextID, len, sizeof(char));
    985         if (Text == NULL) goto Error;
    986 
    987         Wide = (wchar_t*) _cmsCalloc(self ->ContextID, len, sizeof(wchar_t));
    988         if (Wide == NULL) goto Error;
    989 
    990         // Get both representations.
    991         cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry,  Text, len * sizeof(char));
    992         cmsMLUgetWide(mlu,  cmsNoLanguage, cmsNoCountry,  Wide, len * sizeof(wchar_t));
    993     }
    994 
    995   // * cmsUInt32Number       count;          * Description length
    996   // * cmsInt8Number         desc[count]     * NULL terminated ascii string
    997   // * cmsUInt32Number       ucLangCode;     * UniCode language code
    998   // * cmsUInt32Number       ucCount;        * UniCode description length
    999   // * cmsInt16Number        ucDesc[ucCount];* The UniCode description
   1000   // * cmsUInt16Number       scCode;         * ScriptCode code
   1001   // * cmsUInt8Number        scCount;        * ScriptCode count
   1002   // * cmsInt8Number         scDesc[67];     * ScriptCode Description
   1003 
   1004     if (!_cmsWriteUInt32Number(io, len_aligned)) goto Error;
   1005     if (!io ->Write(io, len, Text)) goto Error;
   1006     if (!io ->Write(io, len_filler_alignment, Filler)) goto Error;
   1007 
   1008     if (!_cmsWriteUInt32Number(io, 0)) goto Error;  // ucLanguageCode
   1009 
   1010     // This part is tricky: we need an aligned tag size, and the ScriptCode part
   1011     // takes 70 bytes, so we need 2 extra bytes to do the alignment
   1012 
   1013     if (!_cmsWriteUInt32Number(io, len_aligned+1)) goto Error;
   1014 
   1015     // Note that in some compilers sizeof(cmsUInt16Number) != sizeof(wchar_t)
   1016     if (!_cmsWriteWCharArray(io, len, Wide)) goto Error;
   1017     if (!_cmsWriteUInt16Array(io, len_filler_alignment+1, (cmsUInt16Number*) Filler)) goto Error;
   1018 
   1019     // ScriptCode Code & count (unused)
   1020     if (!_cmsWriteUInt16Number(io, 0)) goto Error;
   1021     if (!_cmsWriteUInt8Number(io, 0)) goto Error;
   1022 
   1023     if (!io ->Write(io, 67, Filler)) goto Error;
   1024 
   1025     rc = TRUE;
   1026 
   1027 Error:
   1028     if (Text) _cmsFree(self ->ContextID, Text);
   1029     if (Wide) _cmsFree(self ->ContextID, Wide);
   1030 
   1031     return rc;
   1032 
   1033     cmsUNUSED_PARAMETER(nItems);
   1034 }
   1035 
   1036 
   1037 static
   1038 void* Type_Text_Description_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
   1039 {
   1040     return (void*) cmsMLUdup((cmsMLU*) Ptr);
   1041 
   1042     cmsUNUSED_PARAMETER(n);
   1043     cmsUNUSED_PARAMETER(self);
   1044 }
   1045 
   1046 static
   1047 void Type_Text_Description_Free(struct _cms_typehandler_struct* self, void* Ptr)
   1048 {
   1049     cmsMLU* mlu = (cmsMLU*) Ptr;
   1050 
   1051     cmsMLUfree(mlu);
   1052     return;
   1053 
   1054     cmsUNUSED_PARAMETER(self);
   1055 }
   1056 
   1057 
   1058 static
   1059 cmsTagTypeSignature DecideTextDescType(cmsFloat64Number ICCVersion, const void *Data)
   1060 {
   1061     if (ICCVersion >= 4.0)
   1062         return cmsSigMultiLocalizedUnicodeType;
   1063 
   1064     return cmsSigTextDescriptionType;
   1065 
   1066     cmsUNUSED_PARAMETER(Data);
   1067 }
   1068 
   1069 
   1070 // ********************************************************************************
   1071 // Type cmsSigCurveType
   1072 // ********************************************************************************
   1073 
   1074 static
   1075 void *Type_Curve_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
   1076 {
   1077     cmsUInt32Number Count;
   1078     cmsToneCurve* NewGamma;
   1079 
   1080     *nItems = 0;
   1081     if (!_cmsReadUInt32Number(io, &Count)) return NULL;
   1082 
   1083     switch (Count) {
   1084 
   1085            case 0:   // Linear.
   1086                {
   1087                    cmsFloat64Number SingleGamma = 1.0;
   1088 
   1089                    NewGamma = cmsBuildParametricToneCurve(self ->ContextID, 1, &SingleGamma);
   1090                    if (!NewGamma) return NULL;
   1091                    *nItems = 1;
   1092                    return NewGamma;
   1093                }
   1094 
   1095            case 1:  // Specified as the exponent of gamma function
   1096                {
   1097                    cmsUInt16Number SingleGammaFixed;
   1098                    cmsFloat64Number SingleGamma;
   1099 
   1100                    if (!_cmsReadUInt16Number(io, &SingleGammaFixed)) return NULL;
   1101                    SingleGamma = _cms8Fixed8toDouble(SingleGammaFixed);
   1102 
   1103                    *nItems = 1;
   1104                    return cmsBuildParametricToneCurve(self ->ContextID, 1, &SingleGamma);
   1105                }
   1106 
   1107            default:  // Curve
   1108 
   1109                if (Count > 0x7FFF)
   1110                    return NULL; // This is to prevent bad guys for doing bad things
   1111 
   1112                NewGamma = cmsBuildTabulatedToneCurve16(self ->ContextID, Count, NULL);
   1113                if (!NewGamma) return NULL;
   1114 
   1115                if (!_cmsReadUInt16Array(io, Count, NewGamma -> Table16)) return NULL;
   1116 
   1117                *nItems = 1;
   1118                return NewGamma;
   1119     }
   1120 
   1121     cmsUNUSED_PARAMETER(SizeOfTag);
   1122 }
   1123 
   1124 
   1125 static
   1126 cmsBool  Type_Curve_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
   1127 {
   1128     cmsToneCurve* Curve = (cmsToneCurve*) Ptr;
   1129 
   1130     if (Curve ->nSegments == 1 && Curve ->Segments[0].Type == 1) {
   1131 
   1132             // Single gamma, preserve number
   1133             cmsUInt16Number SingleGammaFixed = _cmsDoubleTo8Fixed8(Curve ->Segments[0].Params[0]);
   1134 
   1135             if (!_cmsWriteUInt32Number(io, 1)) return FALSE;
   1136             if (!_cmsWriteUInt16Number(io, SingleGammaFixed)) return FALSE;
   1137             return TRUE;
   1138 
   1139     }
   1140 
   1141     if (!_cmsWriteUInt32Number(io, Curve ->nEntries)) return FALSE;
   1142     return _cmsWriteUInt16Array(io, Curve ->nEntries, Curve ->Table16);
   1143 
   1144     cmsUNUSED_PARAMETER(nItems);
   1145     cmsUNUSED_PARAMETER(self);
   1146 }
   1147 
   1148 
   1149 static
   1150 void* Type_Curve_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
   1151 {
   1152     return (void*) cmsDupToneCurve((cmsToneCurve*) Ptr);
   1153 
   1154     cmsUNUSED_PARAMETER(n);
   1155     cmsUNUSED_PARAMETER(self);
   1156 }
   1157 
   1158 static
   1159 void Type_Curve_Free(struct _cms_typehandler_struct* self, void* Ptr)
   1160 {
   1161     cmsToneCurve* gamma = (cmsToneCurve*) Ptr;
   1162 
   1163     cmsFreeToneCurve(gamma);
   1164     return;
   1165 
   1166     cmsUNUSED_PARAMETER(self);
   1167 }
   1168 
   1169 
   1170 // ********************************************************************************
   1171 // Type cmsSigParametricCurveType
   1172 // ********************************************************************************
   1173 
   1174 
   1175 // Decide which curve type to use on writting
   1176 static
   1177 cmsTagTypeSignature DecideCurveType(cmsFloat64Number ICCVersion, const void *Data)
   1178 {
   1179     cmsToneCurve* Curve = (cmsToneCurve*) Data;
   1180 
   1181     if (ICCVersion < 4.0) return cmsSigCurveType;
   1182     if (Curve ->nSegments != 1) return cmsSigCurveType;          // Only 1-segment curves can be saved as parametric
   1183     if (Curve ->Segments[0].Type < 0) return cmsSigCurveType;    // Only non-inverted curves
   1184     if (Curve ->Segments[0].Type > 5) return cmsSigCurveType;    // Only ICC parametric curves
   1185 
   1186     return cmsSigParametricCurveType;
   1187 }
   1188 
   1189 static
   1190 void *Type_ParametricCurve_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
   1191 {
   1192     static const int ParamsByType[] = { 1, 3, 4, 5, 7 };
   1193     cmsFloat64Number Params[10];
   1194     cmsUInt16Number Type;
   1195     int i, n;
   1196     cmsToneCurve* NewGamma;
   1197 
   1198     if (!_cmsReadUInt16Number(io, &Type)) return NULL;
   1199     if (!_cmsReadUInt16Number(io, NULL)) return NULL;   // Reserved
   1200 
   1201     if (Type > 4) {
   1202 
   1203         cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown parametric curve type '%d'", Type);
   1204         return NULL;
   1205     }
   1206 
   1207     memset(Params, 0, sizeof(Params));
   1208     n = ParamsByType[Type];
   1209 
   1210     for (i=0; i < n; i++) {
   1211 
   1212         if (!_cmsRead15Fixed16Number(io, &Params[i])) return NULL;
   1213     }
   1214 
   1215     NewGamma = cmsBuildParametricToneCurve(self ->ContextID, Type+1, Params);
   1216 
   1217     *nItems = 1;
   1218     return NewGamma;
   1219 
   1220     cmsUNUSED_PARAMETER(SizeOfTag);
   1221 }
   1222 
   1223 
   1224 static
   1225 cmsBool  Type_ParametricCurve_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
   1226 {
   1227     cmsToneCurve* Curve = (cmsToneCurve*) Ptr;
   1228     int i, nParams, typen;
   1229     static const int ParamsByType[] = { 0, 1, 3, 4, 5, 7 };
   1230 
   1231     typen = Curve -> Segments[0].Type;
   1232 
   1233     if (Curve ->nSegments > 1 || typen < 1) {
   1234 
   1235         cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Multisegment or Inverted parametric curves cannot be written");
   1236         return FALSE;
   1237     }
   1238 
   1239     if (typen > 5) {
   1240         cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported parametric curve");
   1241         return FALSE;
   1242     }
   1243 
   1244     nParams = ParamsByType[typen];
   1245 
   1246     if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) (Curve ->Segments[0].Type - 1))) return FALSE;
   1247     if (!_cmsWriteUInt16Number(io, 0)) return FALSE;        // Reserved
   1248 
   1249     for (i=0; i < nParams; i++) {
   1250 
   1251         if (!_cmsWrite15Fixed16Number(io, Curve -> Segments[0].Params[i])) return FALSE;
   1252     }
   1253 
   1254     return TRUE;
   1255 
   1256     cmsUNUSED_PARAMETER(nItems);
   1257 }
   1258 
   1259 static
   1260 void* Type_ParametricCurve_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
   1261 {
   1262     return (void*) cmsDupToneCurve((cmsToneCurve*) Ptr);
   1263 
   1264     cmsUNUSED_PARAMETER(n);
   1265     cmsUNUSED_PARAMETER(self);
   1266 }
   1267 
   1268 static
   1269 void Type_ParametricCurve_Free(struct _cms_typehandler_struct* self, void* Ptr)
   1270 {
   1271     cmsToneCurve* gamma = (cmsToneCurve*) Ptr;
   1272 
   1273     cmsFreeToneCurve(gamma);
   1274     return;
   1275 
   1276     cmsUNUSED_PARAMETER(self);
   1277 }
   1278 
   1279 
   1280 // ********************************************************************************
   1281 // Type cmsSigDateTimeType
   1282 // ********************************************************************************
   1283 
   1284 // A 12-byte value representation of the time and date, where the byte usage is assigned
   1285 // as specified in table 1. The actual values are encoded as 16-bit unsigned integers
   1286 // (uInt16Number - see 5.1.6).
   1287 //
   1288 // All the dateTimeNumber values in a profile shall be in Coordinated Universal Time
   1289 // (UTC, also known as GMT or ZULU Time). Profile writers are required to convert local
   1290 // time to UTC when setting these values. Programmes that display these values may show
   1291 // the dateTimeNumber as UTC, show the equivalent local time (at current locale), or
   1292 // display both UTC and local versions of the dateTimeNumber.
   1293 
   1294 static
   1295 void *Type_DateTime_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
   1296 {
   1297     cmsDateTimeNumber timestamp;
   1298     struct tm * NewDateTime;
   1299 
   1300     *nItems = 0;
   1301     NewDateTime = (struct tm*) _cmsMalloc(self ->ContextID, sizeof(struct tm));
   1302     if (NewDateTime == NULL) return NULL;
   1303 
   1304     if (io->Read(io, &timestamp, sizeof(cmsDateTimeNumber), 1) != 1) return NULL;
   1305 
   1306      _cmsDecodeDateTimeNumber(&timestamp, NewDateTime);
   1307 
   1308      *nItems = 1;
   1309      return NewDateTime;
   1310 
   1311      cmsUNUSED_PARAMETER(SizeOfTag);
   1312 }
   1313 
   1314 
   1315 static
   1316 cmsBool  Type_DateTime_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
   1317 {
   1318     struct tm * DateTime = (struct tm*) Ptr;
   1319     cmsDateTimeNumber timestamp;
   1320 
   1321     _cmsEncodeDateTimeNumber(&timestamp, DateTime);
   1322     if (!io ->Write(io, sizeof(cmsDateTimeNumber), &timestamp)) return FALSE;
   1323 
   1324     return TRUE;
   1325 
   1326     cmsUNUSED_PARAMETER(nItems);
   1327     cmsUNUSED_PARAMETER(self);
   1328 }
   1329 
   1330 static
   1331 void* Type_DateTime_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
   1332 {
   1333     return _cmsDupMem(self ->ContextID, Ptr, sizeof(struct tm));
   1334 
   1335     cmsUNUSED_PARAMETER(n);
   1336 }
   1337 
   1338 static
   1339 void Type_DateTime_Free(struct _cms_typehandler_struct* self, void* Ptr)
   1340 {
   1341     _cmsFree(self ->ContextID, Ptr);
   1342 }
   1343 
   1344 
   1345 
   1346 // ********************************************************************************
   1347 // Type icMeasurementType
   1348 // ********************************************************************************
   1349 
   1350 /*
   1351 The measurementType information refers only to the internal profile data and is
   1352 meant to provide profile makers an alternative to the default measurement
   1353 specifications.
   1354 */
   1355 
   1356 static
   1357 void *Type_Measurement_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
   1358 {
   1359     cmsICCMeasurementConditions mc;
   1360 
   1361 
   1362     memset(&mc, 0, sizeof(mc));
   1363 
   1364     if (!_cmsReadUInt32Number(io, &mc.Observer)) return NULL;
   1365     if (!_cmsReadXYZNumber(io,    &mc.Backing)) return NULL;
   1366     if (!_cmsReadUInt32Number(io, &mc.Geometry)) return NULL;
   1367     if (!_cmsRead15Fixed16Number(io, &mc.Flare)) return NULL;
   1368     if (!_cmsReadUInt32Number(io, &mc.IlluminantType)) return NULL;
   1369 
   1370     *nItems = 1;
   1371     return _cmsDupMem(self ->ContextID, &mc, sizeof(cmsICCMeasurementConditions));
   1372 
   1373     cmsUNUSED_PARAMETER(SizeOfTag);
   1374 }
   1375 
   1376 
   1377 static
   1378 cmsBool  Type_Measurement_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
   1379 {
   1380     cmsICCMeasurementConditions* mc =(cmsICCMeasurementConditions*) Ptr;
   1381 
   1382     if (!_cmsWriteUInt32Number(io, mc->Observer)) return FALSE;
   1383     if (!_cmsWriteXYZNumber(io,    &mc->Backing)) return FALSE;
   1384     if (!_cmsWriteUInt32Number(io, mc->Geometry)) return FALSE;
   1385     if (!_cmsWrite15Fixed16Number(io, mc->Flare)) return FALSE;
   1386     if (!_cmsWriteUInt32Number(io, mc->IlluminantType)) return FALSE;
   1387 
   1388     return TRUE;
   1389 
   1390     cmsUNUSED_PARAMETER(nItems);
   1391     cmsUNUSED_PARAMETER(self);
   1392 }
   1393 
   1394 static
   1395 void* Type_Measurement_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
   1396 {
   1397      return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsICCMeasurementConditions));
   1398 
   1399      cmsUNUSED_PARAMETER(n);
   1400 }
   1401 
   1402 static
   1403 void Type_Measurement_Free(struct _cms_typehandler_struct* self, void* Ptr)
   1404 {
   1405    _cmsFree(self ->ContextID, Ptr);
   1406 }
   1407 
   1408 
   1409 // ********************************************************************************
   1410 // Type cmsSigMultiLocalizedUnicodeType
   1411 // ********************************************************************************
   1412 //
   1413 //   Do NOT trust SizeOfTag as there is an issue on the definition of profileSequenceDescTag. See the TechNote from
   1414 //   Max Derhak and Rohit Patil about this: basically the size of the string table should be guessed and cannot be
   1415 //   taken from the size of tag if this tag is embedded as part of bigger structures (profileSequenceDescTag, for instance)
   1416 //
   1417 
   1418 static
   1419 void *Type_MLU_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
   1420 {
   1421     cmsMLU* mlu;
   1422     cmsUInt32Number Count, RecLen, NumOfWchar;
   1423     cmsUInt32Number SizeOfHeader;
   1424     cmsUInt32Number  Len, Offset;
   1425     cmsUInt32Number  i;
   1426     wchar_t*         Block;
   1427     cmsUInt32Number  BeginOfThisString, EndOfThisString, LargestPosition;
   1428 
   1429     *nItems = 0;
   1430     if (!_cmsReadUInt32Number(io, &Count)) return NULL;
   1431     if (!_cmsReadUInt32Number(io, &RecLen)) return NULL;
   1432 
   1433     if (RecLen != 12) {
   1434 
   1435         cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "multiLocalizedUnicodeType of len != 12 is not supported.");
   1436         return NULL;
   1437     }
   1438 
   1439     mlu = cmsMLUalloc(self ->ContextID, Count);
   1440     if (mlu == NULL) return NULL;
   1441 
   1442     mlu ->UsedEntries = Count;
   1443 
   1444     SizeOfHeader = 12 * Count + sizeof(_cmsTagBase);
   1445     LargestPosition = 0;
   1446 
   1447     for (i=0; i < Count; i++) {
   1448 
   1449         if (!_cmsReadUInt16Number(io, &mlu ->Entries[i].Language)) goto Error;
   1450         if (!_cmsReadUInt16Number(io, &mlu ->Entries[i].Country))  goto Error;
   1451 
   1452         // Now deal with Len and offset.
   1453         if (!_cmsReadUInt32Number(io, &Len)) goto Error;
   1454         if (!_cmsReadUInt32Number(io, &Offset)) goto Error;
   1455 
   1456         // Check for overflow
   1457         if (Offset < (SizeOfHeader + 8)) goto Error;
   1458 
   1459         // True begin of the string
   1460         BeginOfThisString = Offset - SizeOfHeader - 8;
   1461 
   1462         // Ajust to wchar_t elements
   1463         mlu ->Entries[i].Len = (Len * sizeof(wchar_t)) / sizeof(cmsUInt16Number);
   1464         mlu ->Entries[i].StrW = (BeginOfThisString * sizeof(wchar_t)) / sizeof(cmsUInt16Number);
   1465 
   1466         // To guess maximum size, add offset + len
   1467         EndOfThisString = BeginOfThisString + Len;
   1468         if (EndOfThisString > LargestPosition)
   1469             LargestPosition = EndOfThisString;
   1470     }
   1471 
   1472     // Now read the remaining of tag and fill all strings. Substract the directory
   1473     SizeOfTag   = (LargestPosition * sizeof(wchar_t)) / sizeof(cmsUInt16Number);
   1474     if (SizeOfTag == 0)
   1475     {
   1476         Block = NULL;
   1477         NumOfWchar = 0;
   1478 
   1479     }
   1480     else
   1481     {
   1482         Block = (wchar_t*) _cmsMalloc(self ->ContextID, SizeOfTag);
   1483         if (Block == NULL) goto Error;
   1484         NumOfWchar = SizeOfTag / sizeof(wchar_t);
   1485         if (!_cmsReadWCharArray(io, NumOfWchar, Block)) goto Error;
   1486     }
   1487 
   1488     mlu ->MemPool  = Block;
   1489     mlu ->PoolSize = SizeOfTag;
   1490     mlu ->PoolUsed = SizeOfTag;
   1491 
   1492     *nItems = 1;
   1493     return (void*) mlu;
   1494 
   1495 Error:
   1496     if (mlu) cmsMLUfree(mlu);
   1497     return NULL;
   1498 }
   1499 
   1500 static
   1501 cmsBool  Type_MLU_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
   1502 {
   1503     cmsMLU* mlu =(cmsMLU*) Ptr;
   1504     cmsUInt32Number HeaderSize;
   1505     cmsUInt32Number  Len, Offset;
   1506     int i;
   1507 
   1508     if (Ptr == NULL) {
   1509 
   1510           // Empty placeholder
   1511           if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
   1512           if (!_cmsWriteUInt32Number(io, 12)) return FALSE;
   1513           return TRUE;
   1514     }
   1515 
   1516     if (!_cmsWriteUInt32Number(io, mlu ->UsedEntries)) return FALSE;
   1517     if (!_cmsWriteUInt32Number(io, 12)) return FALSE;
   1518 
   1519     HeaderSize = 12 * mlu ->UsedEntries + sizeof(_cmsTagBase);
   1520 
   1521     for (i=0; i < mlu ->UsedEntries; i++) {
   1522 
   1523         Len    =  mlu ->Entries[i].Len;
   1524         Offset =  mlu ->Entries[i].StrW;
   1525 
   1526         Len    = (Len * sizeof(cmsUInt16Number)) / sizeof(wchar_t);
   1527         Offset = (Offset * sizeof(cmsUInt16Number)) / sizeof(wchar_t) + HeaderSize + 8;
   1528 
   1529         if (!_cmsWriteUInt16Number(io, mlu ->Entries[i].Language)) return FALSE;
   1530         if (!_cmsWriteUInt16Number(io, mlu ->Entries[i].Country))  return FALSE;
   1531         if (!_cmsWriteUInt32Number(io, Len)) return FALSE;
   1532         if (!_cmsWriteUInt32Number(io, Offset)) return FALSE;
   1533     }
   1534 
   1535     if (!_cmsWriteWCharArray(io, mlu ->PoolUsed / sizeof(wchar_t), (wchar_t*)  mlu ->MemPool)) return FALSE;
   1536 
   1537     return TRUE;
   1538 
   1539     cmsUNUSED_PARAMETER(nItems);
   1540     cmsUNUSED_PARAMETER(self);
   1541 }
   1542 
   1543 
   1544 static
   1545 void* Type_MLU_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
   1546 {
   1547     return (void*) cmsMLUdup((cmsMLU*) Ptr);
   1548 
   1549     cmsUNUSED_PARAMETER(n);
   1550     cmsUNUSED_PARAMETER(self);
   1551 }
   1552 
   1553 static
   1554 void Type_MLU_Free(struct _cms_typehandler_struct* self, void* Ptr)
   1555 {
   1556     cmsMLUfree((cmsMLU*) Ptr);
   1557     return;
   1558 
   1559     cmsUNUSED_PARAMETER(self);
   1560 }
   1561 
   1562 
   1563 // ********************************************************************************
   1564 // Type cmsSigLut8Type
   1565 // ********************************************************************************
   1566 
   1567 // Decide which LUT type to use on writting
   1568 static
   1569 cmsTagTypeSignature DecideLUTtypeA2B(cmsFloat64Number ICCVersion, const void *Data)
   1570 {
   1571     cmsPipeline* Lut = (cmsPipeline*) Data;
   1572 
   1573     if (ICCVersion < 4.0) {
   1574         if (Lut ->SaveAs8Bits) return cmsSigLut8Type;
   1575         return cmsSigLut16Type;
   1576     }
   1577     else {
   1578          return cmsSigLutAtoBType;
   1579     }
   1580 }
   1581 
   1582 static
   1583 cmsTagTypeSignature DecideLUTtypeB2A(cmsFloat64Number ICCVersion, const void *Data)
   1584 {
   1585     cmsPipeline* Lut = (cmsPipeline*) Data;
   1586 
   1587     if (ICCVersion < 4.0) {
   1588         if (Lut ->SaveAs8Bits) return cmsSigLut8Type;
   1589         return cmsSigLut16Type;
   1590     }
   1591     else {
   1592          return cmsSigLutBtoAType;
   1593     }
   1594 }
   1595 
   1596 /*
   1597 This structure represents a colour transform using tables of 8-bit precision.
   1598 This type contains four processing elements: a 3 by 3 matrix (which shall be
   1599 the identity matrix unless the input colour space is XYZ), a set of one dimensional
   1600 input tables, a multidimensional lookup table, and a set of one dimensional output
   1601 tables. Data is processed using these elements via the following sequence:
   1602 (matrix) -> (1d input tables)  -> (multidimensional lookup table - CLUT) -> (1d output tables)
   1603 
   1604 Byte Position   Field Length (bytes)  Content Encoded as...
   1605 8                  1          Number of Input Channels (i)    uInt8Number
   1606 9                  1          Number of Output Channels (o)   uInt8Number
   1607 10                 1          Number of CLUT grid points (identical for each side) (g) uInt8Number
   1608 11                 1          Reserved for padding (fill with 00h)
   1609 
   1610 12..15             4          Encoded e00 parameter   s15Fixed16Number
   1611 */
   1612 
   1613 
   1614 // Read 8 bit tables as gamma functions
   1615 static
   1616 cmsBool  Read8bitTables(cmsContext ContextID, cmsIOHANDLER* io, cmsPipeline* lut, int nChannels)
   1617 {
   1618     cmsUInt8Number* Temp = NULL;
   1619     int i, j;
   1620     cmsToneCurve* Tables[cmsMAXCHANNELS];
   1621 
   1622     if (nChannels > cmsMAXCHANNELS) return FALSE;
   1623     if (nChannels <= 0) return FALSE;
   1624 
   1625     memset(Tables, 0, sizeof(Tables));
   1626 
   1627     Temp = (cmsUInt8Number*) _cmsMalloc(ContextID, 256);
   1628     if (Temp == NULL) return FALSE;
   1629 
   1630     for (i=0; i < nChannels; i++) {
   1631         Tables[i] = cmsBuildTabulatedToneCurve16(ContextID, 256, NULL);
   1632         if (Tables[i] == NULL) goto Error;
   1633     }
   1634 
   1635     for (i=0; i < nChannels; i++) {
   1636 
   1637         if (io ->Read(io, Temp, 256, 1) != 1) goto Error;
   1638 
   1639         for (j=0; j < 256; j++)
   1640             Tables[i]->Table16[j] = (cmsUInt16Number) FROM_8_TO_16(Temp[j]);
   1641     }
   1642 
   1643     _cmsFree(ContextID, Temp);
   1644     Temp = NULL;
   1645 
   1646     if (!cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, nChannels, Tables)))
   1647         goto Error;
   1648 
   1649     for (i=0; i < nChannels; i++)
   1650         cmsFreeToneCurve(Tables[i]);
   1651 
   1652     return TRUE;
   1653 
   1654 Error:
   1655     for (i=0; i < nChannels; i++) {
   1656         if (Tables[i]) cmsFreeToneCurve(Tables[i]);
   1657     }
   1658 
   1659     if (Temp) _cmsFree(ContextID, Temp);
   1660     return FALSE;
   1661 }
   1662 
   1663 
   1664 static
   1665 cmsBool Write8bitTables(cmsContext ContextID, cmsIOHANDLER* io, cmsUInt32Number n, _cmsStageToneCurvesData* Tables)
   1666 {
   1667     int j;
   1668     cmsUInt32Number i;
   1669     cmsUInt8Number val;
   1670 
   1671     for (i=0; i < n; i++) {
   1672 
   1673         if (Tables) {
   1674 
   1675             // Usual case of identity curves
   1676             if ((Tables ->TheCurves[i]->nEntries == 2) &&
   1677                 (Tables->TheCurves[i]->Table16[0] == 0) &&
   1678                 (Tables->TheCurves[i]->Table16[1] == 65535)) {
   1679 
   1680                     for (j=0; j < 256; j++) {
   1681                         if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) j)) return FALSE;
   1682                     }
   1683             }
   1684             else
   1685                 if (Tables ->TheCurves[i]->nEntries != 256) {
   1686                     cmsSignalError(ContextID, cmsERROR_RANGE, "LUT8 needs 256 entries on prelinearization");
   1687                     return FALSE;
   1688                 }
   1689                 else
   1690                     for (j=0; j < 256; j++) {
   1691 
   1692                         if (Tables != NULL)
   1693                             val = (cmsUInt8Number) FROM_16_TO_8(Tables->TheCurves[i]->Table16[j]);
   1694                         else
   1695                             val = (cmsUInt8Number) j;
   1696 
   1697                         if (!_cmsWriteUInt8Number(io, val)) return FALSE;
   1698                     }
   1699         }
   1700     }
   1701     return TRUE;
   1702 }
   1703 
   1704 
   1705 // Check overflow
   1706 static
   1707 cmsUInt32Number uipow(cmsUInt32Number n, cmsUInt32Number a, cmsUInt32Number b)
   1708 {
   1709     cmsUInt32Number rv = 1, rc;
   1710 
   1711     if (a == 0) return 0;
   1712     if (n == 0) return 0;
   1713 
   1714     for (; b > 0; b--) {
   1715 
   1716         rv *= a;
   1717 
   1718         // Check for overflow
   1719         if (rv > UINT_MAX / a) return (cmsUInt32Number) -1;
   1720 
   1721     }
   1722 
   1723     rc = rv * n;
   1724 
   1725     if (rv != rc / n) return (cmsUInt32Number) -1;
   1726     return rc;
   1727 }
   1728 
   1729 
   1730 // That will create a MPE LUT with Matrix, pre tables, CLUT and post tables.
   1731 // 8 bit lut may be scaled easely to v4 PCS, but we need also to properly adjust
   1732 // PCS on BToAxx tags and AtoB if abstract. We need to fix input direction.
   1733 
   1734 static
   1735 void *Type_LUT8_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
   1736 {
   1737     cmsUInt8Number InputChannels, OutputChannels, CLUTpoints;
   1738     cmsUInt8Number* Temp = NULL;
   1739     cmsPipeline* NewLUT = NULL;
   1740     cmsUInt32Number nTabSize, i;
   1741     cmsFloat64Number Matrix[3*3];
   1742 
   1743     *nItems = 0;
   1744 
   1745     if (!_cmsReadUInt8Number(io, &InputChannels)) goto Error;
   1746     if (!_cmsReadUInt8Number(io, &OutputChannels)) goto Error;
   1747     if (!_cmsReadUInt8Number(io, &CLUTpoints)) goto Error;
   1748 
   1749      if (CLUTpoints == 1) goto Error; // Impossible value, 0 for no CLUT and then 2 at least
   1750 
   1751     // Padding
   1752     if (!_cmsReadUInt8Number(io, NULL)) goto Error;
   1753 
   1754     // Do some checking
   1755     if (InputChannels > cmsMAXCHANNELS)  goto Error;
   1756     if (OutputChannels > cmsMAXCHANNELS) goto Error;
   1757 
   1758    // Allocates an empty Pipeline
   1759     NewLUT = cmsPipelineAlloc(self ->ContextID, InputChannels, OutputChannels);
   1760     if (NewLUT == NULL) goto Error;
   1761 
   1762     // Read the Matrix
   1763     if (!_cmsRead15Fixed16Number(io,  &Matrix[0])) goto Error;
   1764     if (!_cmsRead15Fixed16Number(io,  &Matrix[1])) goto Error;
   1765     if (!_cmsRead15Fixed16Number(io,  &Matrix[2])) goto Error;
   1766     if (!_cmsRead15Fixed16Number(io,  &Matrix[3])) goto Error;
   1767     if (!_cmsRead15Fixed16Number(io,  &Matrix[4])) goto Error;
   1768     if (!_cmsRead15Fixed16Number(io,  &Matrix[5])) goto Error;
   1769     if (!_cmsRead15Fixed16Number(io,  &Matrix[6])) goto Error;
   1770     if (!_cmsRead15Fixed16Number(io,  &Matrix[7])) goto Error;
   1771     if (!_cmsRead15Fixed16Number(io,  &Matrix[8])) goto Error;
   1772 
   1773 
   1774     // Only operates if not identity...
   1775     if ((InputChannels == 3) && !_cmsMAT3isIdentity((cmsMAT3*) Matrix)) {
   1776 
   1777         if (!cmsPipelineInsertStage(NewLUT, cmsAT_BEGIN, cmsStageAllocMatrix(self ->ContextID, 3, 3, Matrix, NULL)))
   1778             goto Error;
   1779     }
   1780 
   1781     // Get input tables
   1782     if (!Read8bitTables(self ->ContextID, io,  NewLUT, InputChannels)) goto Error;
   1783 
   1784     // Get 3D CLUT. Check the overflow....
   1785     nTabSize = uipow(OutputChannels, CLUTpoints, InputChannels);
   1786     if (nTabSize == (cmsUInt32Number) -1) goto Error;
   1787     if (nTabSize > 0) {
   1788 
   1789         cmsUInt16Number *PtrW, *T;
   1790 
   1791         PtrW = T  = (cmsUInt16Number*) _cmsCalloc(self ->ContextID, nTabSize, sizeof(cmsUInt16Number));
   1792         if (T  == NULL) goto Error;
   1793 
   1794         Temp = (cmsUInt8Number*) _cmsMalloc(self ->ContextID, nTabSize);
   1795         if (Temp == NULL) {
   1796             _cmsFree(self ->ContextID, T);
   1797             goto Error;
   1798         }
   1799 
   1800         if (io ->Read(io, Temp, nTabSize, 1) != 1) {
   1801             _cmsFree(self ->ContextID, T);
   1802             _cmsFree(self ->ContextID, Temp);
   1803             goto Error;
   1804         }
   1805 
   1806         for (i = 0; i < nTabSize; i++) {
   1807 
   1808             *PtrW++ = FROM_8_TO_16(Temp[i]);
   1809         }
   1810         _cmsFree(self ->ContextID, Temp);
   1811         Temp = NULL;
   1812 
   1813         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, cmsStageAllocCLut16bit(self ->ContextID, CLUTpoints, InputChannels, OutputChannels, T)))
   1814             goto Error;
   1815         _cmsFree(self ->ContextID, T);
   1816     }
   1817 
   1818 
   1819     // Get output tables
   1820     if (!Read8bitTables(self ->ContextID, io,  NewLUT, OutputChannels)) goto Error;
   1821 
   1822     *nItems = 1;
   1823     return NewLUT;
   1824 
   1825 Error:
   1826     if (NewLUT != NULL) cmsPipelineFree(NewLUT);
   1827     return NULL;
   1828 
   1829     cmsUNUSED_PARAMETER(SizeOfTag);
   1830 }
   1831 
   1832 // We only allow a specific MPE structure: Matrix plus prelin, plus clut, plus post-lin.
   1833 static
   1834 cmsBool  Type_LUT8_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
   1835 {
   1836     cmsUInt32Number j, nTabSize;
   1837     cmsUInt8Number  val;
   1838     cmsPipeline* NewLUT = (cmsPipeline*) Ptr;
   1839     cmsStage* mpe;
   1840     _cmsStageToneCurvesData* PreMPE = NULL, *PostMPE = NULL;
   1841     _cmsStageMatrixData* MatMPE = NULL;
   1842     _cmsStageCLutData* clut = NULL;
   1843     int clutPoints;
   1844 
   1845     // Disassemble the LUT into components.
   1846     mpe = NewLUT -> Elements;
   1847     if (mpe ->Type == cmsSigMatrixElemType) {
   1848 
   1849         MatMPE = (_cmsStageMatrixData*) mpe ->Data;
   1850         mpe = mpe -> Next;
   1851     }
   1852 
   1853     if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) {
   1854         PreMPE = (_cmsStageToneCurvesData*) mpe ->Data;
   1855         mpe = mpe -> Next;
   1856     }
   1857 
   1858     if (mpe != NULL && mpe ->Type == cmsSigCLutElemType) {
   1859         clut  = (_cmsStageCLutData*) mpe -> Data;
   1860         mpe = mpe ->Next;
   1861     }
   1862 
   1863     if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) {
   1864         PostMPE = (_cmsStageToneCurvesData*) mpe ->Data;
   1865         mpe = mpe -> Next;
   1866     }
   1867 
   1868     // That should be all
   1869     if (mpe != NULL) {
   1870         cmsSignalError(mpe->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT is not suitable to be saved as LUT8");
   1871         return FALSE;
   1872     }
   1873 
   1874 
   1875     if (clut == NULL)
   1876         clutPoints = 0;
   1877     else
   1878         clutPoints    = clut->Params->nSamples[0];
   1879 
   1880     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) NewLUT ->InputChannels)) return FALSE;
   1881     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) NewLUT ->OutputChannels)) return FALSE;
   1882     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) clutPoints)) return FALSE;
   1883     if (!_cmsWriteUInt8Number(io, 0)) return FALSE; // Padding
   1884 
   1885 
   1886     if (MatMPE != NULL) {
   1887 
   1888         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[0])) return FALSE;
   1889         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[1])) return FALSE;
   1890         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[2])) return FALSE;
   1891         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[3])) return FALSE;
   1892         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[4])) return FALSE;
   1893         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[5])) return FALSE;
   1894         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[6])) return FALSE;
   1895         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[7])) return FALSE;
   1896         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[8])) return FALSE;
   1897 
   1898     }
   1899     else {
   1900 
   1901         if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE;
   1902         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
   1903         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
   1904         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
   1905         if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE;
   1906         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
   1907         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
   1908         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
   1909         if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE;
   1910     }
   1911 
   1912     // The prelinearization table
   1913     if (!Write8bitTables(self ->ContextID, io, NewLUT ->InputChannels, PreMPE)) return FALSE;
   1914 
   1915     nTabSize = uipow(NewLUT->OutputChannels, clutPoints, NewLUT ->InputChannels);
   1916     if (nTabSize == (cmsUInt32Number) -1) return FALSE;
   1917     if (nTabSize > 0) {
   1918 
   1919         // The 3D CLUT.
   1920         if (clut != NULL) {
   1921 
   1922             for (j=0; j < nTabSize; j++) {
   1923 
   1924                 val = (cmsUInt8Number) FROM_16_TO_8(clut ->Tab.T[j]);
   1925                 if (!_cmsWriteUInt8Number(io, val)) return FALSE;
   1926             }
   1927         }
   1928     }
   1929 
   1930     // The postlinearization table
   1931     if (!Write8bitTables(self ->ContextID, io, NewLUT ->OutputChannels, PostMPE)) return FALSE;
   1932 
   1933     return TRUE;
   1934 
   1935     cmsUNUSED_PARAMETER(nItems);
   1936 }
   1937 
   1938 
   1939 static
   1940 void* Type_LUT8_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
   1941 {
   1942     return (void*) cmsPipelineDup((cmsPipeline*) Ptr);
   1943 
   1944     cmsUNUSED_PARAMETER(n);
   1945     cmsUNUSED_PARAMETER(self);
   1946 }
   1947 
   1948 static
   1949 void Type_LUT8_Free(struct _cms_typehandler_struct* self, void* Ptr)
   1950 {
   1951     cmsPipelineFree((cmsPipeline*) Ptr);
   1952     return;
   1953 
   1954     cmsUNUSED_PARAMETER(self);
   1955 }
   1956 
   1957 // ********************************************************************************
   1958 // Type cmsSigLut16Type
   1959 // ********************************************************************************
   1960 
   1961 // Read 16 bit tables as gamma functions
   1962 static
   1963 cmsBool  Read16bitTables(cmsContext ContextID, cmsIOHANDLER* io, cmsPipeline* lut, int nChannels, int nEntries)
   1964 {
   1965     int i;
   1966     cmsToneCurve* Tables[cmsMAXCHANNELS];
   1967 
   1968     // Maybe an empty table? (this is a lcms extension)
   1969     if (nEntries <= 0) return TRUE;
   1970 
   1971     // Check for malicious profiles
   1972     if (nEntries < 2) return FALSE;
   1973     if (nChannels > cmsMAXCHANNELS) return FALSE;
   1974 
   1975     // Init table to zero
   1976     memset(Tables, 0, sizeof(Tables));
   1977 
   1978     for (i=0; i < nChannels; i++) {
   1979 
   1980         Tables[i] = cmsBuildTabulatedToneCurve16(ContextID, nEntries, NULL);
   1981         if (Tables[i] == NULL) goto Error;
   1982 
   1983         if (!_cmsReadUInt16Array(io, nEntries, Tables[i]->Table16)) goto Error;
   1984     }
   1985 
   1986 
   1987     // Add the table (which may certainly be an identity, but this is up to the optimizer, not the reading code)
   1988     if (!cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, nChannels, Tables)))
   1989         goto Error;
   1990 
   1991     for (i=0; i < nChannels; i++)
   1992         cmsFreeToneCurve(Tables[i]);
   1993 
   1994     return TRUE;
   1995 
   1996 Error:
   1997     for (i=0; i < nChannels; i++) {
   1998         if (Tables[i]) cmsFreeToneCurve(Tables[i]);
   1999     }
   2000 
   2001     return FALSE;
   2002 }
   2003 
   2004 static
   2005 cmsBool Write16bitTables(cmsContext ContextID, cmsIOHANDLER* io, _cmsStageToneCurvesData* Tables)
   2006 {
   2007     int j;
   2008     cmsUInt32Number i;
   2009     cmsUInt16Number val;
   2010     int nEntries;
   2011 
   2012     _cmsAssert(Tables != NULL);
   2013 
   2014     nEntries = Tables->TheCurves[0]->nEntries;
   2015 
   2016     for (i=0; i < Tables ->nCurves; i++) {
   2017 
   2018         for (j=0; j < nEntries; j++) {
   2019 
   2020             val = Tables->TheCurves[i]->Table16[j];
   2021             if (!_cmsWriteUInt16Number(io, val)) return FALSE;
   2022         }
   2023     }
   2024     return TRUE;
   2025 
   2026     cmsUNUSED_PARAMETER(ContextID);
   2027 }
   2028 
   2029 static
   2030 void *Type_LUT16_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
   2031 {
   2032     cmsUInt8Number InputChannels, OutputChannels, CLUTpoints;
   2033     cmsPipeline* NewLUT = NULL;
   2034     cmsUInt32Number nTabSize;
   2035     cmsFloat64Number Matrix[3*3];
   2036     cmsUInt16Number InputEntries, OutputEntries;
   2037 
   2038     *nItems = 0;
   2039 
   2040     if (!_cmsReadUInt8Number(io, &InputChannels)) return NULL;
   2041     if (!_cmsReadUInt8Number(io, &OutputChannels)) return NULL;
   2042     if (!_cmsReadUInt8Number(io, &CLUTpoints)) return NULL;   // 255 maximum
   2043 
   2044     // Padding
   2045     if (!_cmsReadUInt8Number(io, NULL)) return NULL;
   2046 
   2047     // Do some checking
   2048     if (InputChannels > cmsMAXCHANNELS)  goto Error;
   2049     if (OutputChannels > cmsMAXCHANNELS) goto Error;
   2050 
   2051     // Allocates an empty LUT
   2052     NewLUT = cmsPipelineAlloc(self ->ContextID, InputChannels, OutputChannels);
   2053     if (NewLUT == NULL) goto Error;
   2054 
   2055     // Read the Matrix
   2056     if (!_cmsRead15Fixed16Number(io,  &Matrix[0])) goto Error;
   2057     if (!_cmsRead15Fixed16Number(io,  &Matrix[1])) goto Error;
   2058     if (!_cmsRead15Fixed16Number(io,  &Matrix[2])) goto Error;
   2059     if (!_cmsRead15Fixed16Number(io,  &Matrix[3])) goto Error;
   2060     if (!_cmsRead15Fixed16Number(io,  &Matrix[4])) goto Error;
   2061     if (!_cmsRead15Fixed16Number(io,  &Matrix[5])) goto Error;
   2062     if (!_cmsRead15Fixed16Number(io,  &Matrix[6])) goto Error;
   2063     if (!_cmsRead15Fixed16Number(io,  &Matrix[7])) goto Error;
   2064     if (!_cmsRead15Fixed16Number(io,  &Matrix[8])) goto Error;
   2065 
   2066 
   2067     // Only operates on 3 channels
   2068     if ((InputChannels == 3) && !_cmsMAT3isIdentity((cmsMAT3*) Matrix)) {
   2069 
   2070         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, cmsStageAllocMatrix(self ->ContextID, 3, 3, Matrix, NULL)))
   2071             goto Error;
   2072     }
   2073 
   2074     if (!_cmsReadUInt16Number(io, &InputEntries)) goto Error;
   2075     if (!_cmsReadUInt16Number(io, &OutputEntries)) goto Error;
   2076 
   2077     if (InputEntries > 0x7FFF || OutputEntries > 0x7FFF) goto Error;
   2078     if (CLUTpoints == 1) goto Error; // Impossible value, 0 for no CLUT and then 2 at least
   2079 
   2080     // Get input tables
   2081     if (!Read16bitTables(self ->ContextID, io,  NewLUT, InputChannels, InputEntries)) goto Error;
   2082 
   2083     // Get 3D CLUT
   2084     nTabSize = uipow(OutputChannels, CLUTpoints, InputChannels);
   2085     if (nTabSize == (cmsUInt32Number) -1) goto Error;
   2086     if (nTabSize > 0) {
   2087 
   2088         cmsUInt16Number *T;
   2089 
   2090         T  = (cmsUInt16Number*) _cmsCalloc(self ->ContextID, nTabSize, sizeof(cmsUInt16Number));
   2091         if (T  == NULL) goto Error;
   2092 
   2093         if (!_cmsReadUInt16Array(io, nTabSize, T)) {
   2094             _cmsFree(self ->ContextID, T);
   2095             goto Error;
   2096         }
   2097 
   2098         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, cmsStageAllocCLut16bit(self ->ContextID, CLUTpoints, InputChannels, OutputChannels, T))) {
   2099             _cmsFree(self ->ContextID, T);
   2100             goto Error;
   2101         }
   2102         _cmsFree(self ->ContextID, T);
   2103     }
   2104 
   2105 
   2106     // Get output tables
   2107     if (!Read16bitTables(self ->ContextID, io,  NewLUT, OutputChannels, OutputEntries)) goto Error;
   2108 
   2109     *nItems = 1;
   2110     return NewLUT;
   2111 
   2112 Error:
   2113     if (NewLUT != NULL) cmsPipelineFree(NewLUT);
   2114     return NULL;
   2115 
   2116     cmsUNUSED_PARAMETER(SizeOfTag);
   2117 }
   2118 
   2119 // We only allow some specific MPE structures: Matrix plus prelin, plus clut, plus post-lin.
   2120 // Some empty defaults are created for missing parts
   2121 
   2122 static
   2123 cmsBool  Type_LUT16_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
   2124 {
   2125     cmsUInt32Number nTabSize;
   2126     cmsPipeline* NewLUT = (cmsPipeline*) Ptr;
   2127     cmsStage* mpe;
   2128     _cmsStageToneCurvesData* PreMPE = NULL, *PostMPE = NULL;
   2129     _cmsStageMatrixData* MatMPE = NULL;
   2130     _cmsStageCLutData* clut = NULL;
   2131     int i, InputChannels, OutputChannels, clutPoints;
   2132 
   2133     // Disassemble the LUT into components.
   2134     mpe = NewLUT -> Elements;
   2135     if (mpe != NULL && mpe ->Type == cmsSigMatrixElemType) {
   2136 
   2137         MatMPE = (_cmsStageMatrixData*) mpe ->Data;
   2138         mpe = mpe -> Next;
   2139     }
   2140 
   2141 
   2142     if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) {
   2143         PreMPE = (_cmsStageToneCurvesData*) mpe ->Data;
   2144         mpe = mpe -> Next;
   2145     }
   2146 
   2147     if (mpe != NULL && mpe ->Type == cmsSigCLutElemType) {
   2148         clut  = (_cmsStageCLutData*) mpe -> Data;
   2149         mpe = mpe ->Next;
   2150     }
   2151 
   2152     if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) {
   2153         PostMPE = (_cmsStageToneCurvesData*) mpe ->Data;
   2154         mpe = mpe -> Next;
   2155     }
   2156 
   2157     // That should be all
   2158     if (mpe != NULL) {
   2159         cmsSignalError(mpe->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT is not suitable to be saved as LUT16");
   2160         return FALSE;
   2161     }
   2162 
   2163     InputChannels  = cmsPipelineInputChannels(NewLUT);
   2164     OutputChannels = cmsPipelineOutputChannels(NewLUT);
   2165 
   2166     if (clut == NULL)
   2167         clutPoints = 0;
   2168     else
   2169         clutPoints    = clut->Params->nSamples[0];
   2170 
   2171     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) InputChannels)) return FALSE;
   2172     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) OutputChannels)) return FALSE;
   2173     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) clutPoints)) return FALSE;
   2174     if (!_cmsWriteUInt8Number(io, 0)) return FALSE; // Padding
   2175 
   2176 
   2177     if (MatMPE != NULL) {
   2178 
   2179         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[0])) return FALSE;
   2180         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[1])) return FALSE;
   2181         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[2])) return FALSE;
   2182         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[3])) return FALSE;
   2183         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[4])) return FALSE;
   2184         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[5])) return FALSE;
   2185         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[6])) return FALSE;
   2186         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[7])) return FALSE;
   2187         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[8])) return FALSE;
   2188     }
   2189     else {
   2190 
   2191         if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE;
   2192         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
   2193         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
   2194         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
   2195         if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE;
   2196         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
   2197         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
   2198         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
   2199         if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE;
   2200     }
   2201 
   2202 
   2203     if (PreMPE != NULL) {
   2204         if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) PreMPE ->TheCurves[0]->nEntries)) return FALSE;
   2205     } else {
   2206             if (!_cmsWriteUInt16Number(io, 2)) return FALSE;
   2207     }
   2208 
   2209     if (PostMPE != NULL) {
   2210         if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) PostMPE ->TheCurves[0]->nEntries)) return FALSE;
   2211     } else {
   2212         if (!_cmsWriteUInt16Number(io, 2)) return FALSE;
   2213 
   2214     }
   2215 
   2216     // The prelinearization table
   2217 
   2218     if (PreMPE != NULL) {
   2219         if (!Write16bitTables(self ->ContextID, io, PreMPE)) return FALSE;
   2220     }
   2221     else {
   2222         for (i=0; i < InputChannels; i++) {
   2223 
   2224             if (!_cmsWriteUInt16Number(io, 0)) return FALSE;
   2225             if (!_cmsWriteUInt16Number(io, 0xffff)) return FALSE;
   2226         }
   2227     }
   2228 
   2229     nTabSize = uipow(OutputChannels, clutPoints, InputChannels);
   2230     if (nTabSize == (cmsUInt32Number) -1) return FALSE;
   2231     if (nTabSize > 0) {
   2232         // The 3D CLUT.
   2233         if (clut != NULL) {
   2234             if (!_cmsWriteUInt16Array(io, nTabSize, clut->Tab.T)) return FALSE;
   2235         }
   2236     }
   2237 
   2238     // The postlinearization table
   2239     if (PostMPE != NULL) {
   2240         if (!Write16bitTables(self ->ContextID, io, PostMPE)) return FALSE;
   2241     }
   2242     else {
   2243         for (i=0; i < OutputChannels; i++) {
   2244 
   2245             if (!_cmsWriteUInt16Number(io, 0)) return FALSE;
   2246             if (!_cmsWriteUInt16Number(io, 0xffff)) return FALSE;
   2247         }
   2248     }
   2249 
   2250     return TRUE;
   2251 
   2252     cmsUNUSED_PARAMETER(nItems);
   2253 }
   2254 
   2255 static
   2256 void* Type_LUT16_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
   2257 {
   2258     return (void*) cmsPipelineDup((cmsPipeline*) Ptr);
   2259 
   2260     cmsUNUSED_PARAMETER(n);
   2261     cmsUNUSED_PARAMETER(self);
   2262 }
   2263 
   2264 static
   2265 void Type_LUT16_Free(struct _cms_typehandler_struct* self, void* Ptr)
   2266 {
   2267     cmsPipelineFree((cmsPipeline*) Ptr);
   2268     return;
   2269 
   2270     cmsUNUSED_PARAMETER(self);
   2271 }
   2272 
   2273 
   2274 // ********************************************************************************
   2275 // Type cmsSigLutAToBType
   2276 // ********************************************************************************
   2277 
   2278 
   2279 // V4 stuff. Read matrix for LutAtoB and LutBtoA
   2280 
   2281 static
   2282 cmsStage* ReadMatrix(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number Offset)
   2283 {
   2284     cmsFloat64Number dMat[3*3];
   2285     cmsFloat64Number dOff[3];
   2286     cmsStage* Mat;
   2287 
   2288     // Go to address
   2289     if (!io -> Seek(io, Offset)) return NULL;
   2290 
   2291     // Read the Matrix
   2292     if (!_cmsRead15Fixed16Number(io, &dMat[0])) return NULL;
   2293     if (!_cmsRead15Fixed16Number(io, &dMat[1])) return NULL;
   2294     if (!_cmsRead15Fixed16Number(io, &dMat[2])) return NULL;
   2295     if (!_cmsRead15Fixed16Number(io, &dMat[3])) return NULL;
   2296     if (!_cmsRead15Fixed16Number(io, &dMat[4])) return NULL;
   2297     if (!_cmsRead15Fixed16Number(io, &dMat[5])) return NULL;
   2298     if (!_cmsRead15Fixed16Number(io, &dMat[6])) return NULL;
   2299     if (!_cmsRead15Fixed16Number(io, &dMat[7])) return NULL;
   2300     if (!_cmsRead15Fixed16Number(io, &dMat[8])) return NULL;
   2301 
   2302     if (!_cmsRead15Fixed16Number(io, &dOff[0])) return NULL;
   2303     if (!_cmsRead15Fixed16Number(io, &dOff[1])) return NULL;
   2304     if (!_cmsRead15Fixed16Number(io, &dOff[2])) return NULL;
   2305 
   2306     Mat = cmsStageAllocMatrix(self ->ContextID, 3, 3, dMat, dOff);
   2307 
   2308      return Mat;
   2309 }
   2310 
   2311 
   2312 
   2313 
   2314 //  V4 stuff. Read CLUT part for LutAtoB and LutBtoA
   2315 
   2316 static
   2317 cmsStage* ReadCLUT(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number Offset, int InputChannels, int OutputChannels)
   2318 {
   2319     cmsUInt8Number  gridPoints8[cmsMAXCHANNELS]; // Number of grid points in each dimension.
   2320     cmsUInt32Number GridPoints[cmsMAXCHANNELS], i;
   2321     cmsUInt8Number  Precision;
   2322     cmsStage* CLUT;
   2323     _cmsStageCLutData* Data;
   2324 
   2325     if (!io -> Seek(io, Offset)) return NULL;
   2326     if (io -> Read(io, gridPoints8, cmsMAXCHANNELS, 1) != 1) return NULL;
   2327 
   2328 
   2329     for (i=0; i < cmsMAXCHANNELS; i++) {
   2330 
   2331         if (gridPoints8[i] == 1) return NULL; // Impossible value, 0 for no CLUT and then 2 at least
   2332         GridPoints[i] = gridPoints8[i];
   2333     }
   2334 
   2335     if (!_cmsReadUInt8Number(io, &Precision)) return NULL;
   2336 
   2337     if (!_cmsReadUInt8Number(io, NULL)) return NULL;
   2338     if (!_cmsReadUInt8Number(io, NULL)) return NULL;
   2339     if (!_cmsReadUInt8Number(io, NULL)) return NULL;
   2340 
   2341     CLUT = cmsStageAllocCLut16bitGranular(self ->ContextID, GridPoints, InputChannels, OutputChannels, NULL);
   2342     if (CLUT == NULL) return NULL;
   2343 
   2344     Data = (_cmsStageCLutData*) CLUT ->Data;
   2345 
   2346     // Precision can be 1 or 2 bytes
   2347     if (Precision == 1) {
   2348 
   2349         cmsUInt8Number  v;
   2350 
   2351         for (i=0; i < Data ->nEntries; i++) {
   2352 
   2353             if (io ->Read(io, &v, sizeof(cmsUInt8Number), 1) != 1) return NULL;
   2354             Data ->Tab.T[i] = FROM_8_TO_16(v);
   2355         }
   2356 
   2357     }
   2358     else
   2359         if (Precision == 2) {
   2360 
   2361             if (!_cmsReadUInt16Array(io, Data->nEntries, Data ->Tab.T)) {
   2362                 cmsStageFree(CLUT);
   2363                 return NULL;
   2364             }
   2365         }
   2366         else {
   2367             cmsStageFree(CLUT);
   2368             cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown precision of '%d'", Precision);
   2369             return NULL;
   2370         }
   2371 
   2372         return CLUT;
   2373 }
   2374 
   2375 static
   2376 cmsToneCurve* ReadEmbeddedCurve(struct _cms_typehandler_struct* self, cmsIOHANDLER* io)
   2377 {
   2378     cmsTagTypeSignature  BaseType;
   2379     cmsUInt32Number nItems;
   2380 
   2381     BaseType = _cmsReadTypeBase(io);
   2382     switch (BaseType) {
   2383 
   2384             case cmsSigCurveType:
   2385                 return (cmsToneCurve*) Type_Curve_Read(self, io, &nItems, 0);
   2386 
   2387             case cmsSigParametricCurveType:
   2388                 return (cmsToneCurve*) Type_ParametricCurve_Read(self, io, &nItems, 0);
   2389 
   2390             default:
   2391                 {
   2392                     char String[5];
   2393 
   2394                     _cmsTagSignature2String(String, (cmsTagSignature) BaseType);
   2395                     cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown curve type '%s'", String);
   2396                 }
   2397                 return NULL;
   2398     }
   2399 }
   2400 
   2401 
   2402 // Read a set of curves from specific offset
   2403 static
   2404 cmsStage* ReadSetOfCurves(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number Offset, cmsUInt32Number nCurves)
   2405 {
   2406     cmsToneCurve* Curves[cmsMAXCHANNELS];
   2407     cmsUInt32Number i;
   2408     cmsStage* Lin = NULL;
   2409 
   2410     if (nCurves > cmsMAXCHANNELS) return FALSE;
   2411 
   2412     if (!io -> Seek(io, Offset)) return FALSE;
   2413 
   2414     for (i=0; i < nCurves; i++)
   2415         Curves[i] = NULL;
   2416 
   2417     for (i=0; i < nCurves; i++) {
   2418 
   2419         Curves[i] = ReadEmbeddedCurve(self, io);
   2420         if (Curves[i] == NULL) goto Error;
   2421         if (!_cmsReadAlignment(io)) goto Error;
   2422 
   2423     }
   2424 
   2425     Lin = cmsStageAllocToneCurves(self ->ContextID, nCurves, Curves);
   2426 
   2427 Error:
   2428     for (i=0; i < nCurves; i++)
   2429         cmsFreeToneCurve(Curves[i]);
   2430 
   2431     return Lin;
   2432 }
   2433 
   2434 
   2435 // LutAtoB type
   2436 
   2437 // This structure represents a colour transform. The type contains up to five processing
   2438 // elements which are stored in the AtoBTag tag in the following order: a set of one
   2439 // dimensional curves, a 3 by 3 matrix with offset terms, a set of one dimensional curves,
   2440 // a multidimensional lookup table, and a set of one dimensional output curves.
   2441 // Data are processed using these elements via the following sequence:
   2442 //
   2443 //("A" curves) -> (multidimensional lookup table - CLUT) -> ("M" curves) -> (matrix) -> ("B" curves).
   2444 //
   2445 /*
   2446 It is possible to use any or all of these processing elements. At least one processing element
   2447 must be included.Only the following combinations are allowed:
   2448 
   2449 B
   2450 M - Matrix - B
   2451 A - CLUT - B
   2452 A - CLUT - M - Matrix - B
   2453 
   2454 */
   2455 
   2456 static
   2457 void* Type_LUTA2B_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
   2458 {
   2459     cmsUInt32Number      BaseOffset;
   2460     cmsUInt8Number       inputChan;      // Number of input channels
   2461     cmsUInt8Number       outputChan;     // Number of output channels
   2462     cmsUInt32Number      offsetB;        // Offset to first "B" curve
   2463     cmsUInt32Number      offsetMat;      // Offset to matrix
   2464     cmsUInt32Number      offsetM;        // Offset to first "M" curve
   2465     cmsUInt32Number      offsetC;        // Offset to CLUT
   2466     cmsUInt32Number      offsetA;        // Offset to first "A" curve
   2467     cmsPipeline* NewLUT = NULL;
   2468 
   2469 
   2470     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
   2471 
   2472     if (!_cmsReadUInt8Number(io, &inputChan)) return NULL;
   2473     if (!_cmsReadUInt8Number(io, &outputChan)) return NULL;
   2474 
   2475     if (!_cmsReadUInt16Number(io, NULL)) return NULL;
   2476 
   2477     if (!_cmsReadUInt32Number(io, &offsetB)) return NULL;
   2478     if (!_cmsReadUInt32Number(io, &offsetMat)) return NULL;
   2479     if (!_cmsReadUInt32Number(io, &offsetM)) return NULL;
   2480     if (!_cmsReadUInt32Number(io, &offsetC)) return NULL;
   2481     if (!_cmsReadUInt32Number(io, &offsetA)) return NULL;
   2482 
   2483    // Allocates an empty LUT
   2484     NewLUT = cmsPipelineAlloc(self ->ContextID, inputChan, outputChan);
   2485     if (NewLUT == NULL) return NULL;
   2486 
   2487     if (offsetA!= 0) {
   2488         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetA, inputChan)))
   2489             goto Error;
   2490     }
   2491 
   2492     if (offsetC != 0) {
   2493         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadCLUT(self, io, BaseOffset + offsetC, inputChan, outputChan)))
   2494             goto Error;
   2495     }
   2496 
   2497     if (offsetM != 0) {
   2498         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetM, outputChan)))
   2499             goto Error;
   2500     }
   2501 
   2502     if (offsetMat != 0) {
   2503         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadMatrix(self, io, BaseOffset + offsetMat)))
   2504             goto Error;
   2505     }
   2506 
   2507     if (offsetB != 0) {
   2508         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetB, outputChan)))
   2509             goto Error;
   2510     }
   2511 
   2512     *nItems = 1;
   2513     return NewLUT;
   2514 Error:
   2515     cmsPipelineFree(NewLUT);
   2516     return NULL;
   2517 
   2518     cmsUNUSED_PARAMETER(SizeOfTag);
   2519 }
   2520 
   2521 // Write a set of curves
   2522 static
   2523 cmsBool  WriteMatrix(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsStage* mpe)
   2524 {
   2525     _cmsStageMatrixData* m = (_cmsStageMatrixData*) mpe -> Data;
   2526 
   2527     // Write the Matrix
   2528     if (!_cmsWrite15Fixed16Number(io, m -> Double[0])) return FALSE;
   2529     if (!_cmsWrite15Fixed16Number(io, m -> Double[1])) return FALSE;
   2530     if (!_cmsWrite15Fixed16Number(io, m -> Double[2])) return FALSE;
   2531     if (!_cmsWrite15Fixed16Number(io, m -> Double[3])) return FALSE;
   2532     if (!_cmsWrite15Fixed16Number(io, m -> Double[4])) return FALSE;
   2533     if (!_cmsWrite15Fixed16Number(io, m -> Double[5])) return FALSE;
   2534     if (!_cmsWrite15Fixed16Number(io, m -> Double[6])) return FALSE;
   2535     if (!_cmsWrite15Fixed16Number(io, m -> Double[7])) return FALSE;
   2536     if (!_cmsWrite15Fixed16Number(io, m -> Double[8])) return FALSE;
   2537 
   2538     if (m ->Offset != NULL) {
   2539 
   2540     if (!_cmsWrite15Fixed16Number(io, m -> Offset[0])) return FALSE;
   2541     if (!_cmsWrite15Fixed16Number(io, m -> Offset[1])) return FALSE;
   2542     if (!_cmsWrite15Fixed16Number(io, m -> Offset[2])) return FALSE;
   2543     }
   2544     else {
   2545         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
   2546         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
   2547         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
   2548 
   2549     }
   2550 
   2551 
   2552     return TRUE;
   2553 
   2554     cmsUNUSED_PARAMETER(self);
   2555 }
   2556 
   2557 
   2558 // Write a set of curves
   2559 static
   2560 cmsBool WriteSetOfCurves(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsTagTypeSignature Type, cmsStage* mpe)
   2561 {
   2562     cmsUInt32Number i, n;
   2563     cmsTagTypeSignature CurrentType;
   2564     cmsToneCurve** Curves;
   2565 
   2566 
   2567     n      = cmsStageOutputChannels(mpe);
   2568     Curves = _cmsStageGetPtrToCurveSet(mpe);
   2569 
   2570     for (i=0; i < n; i++) {
   2571 
   2572         // If this is a table-based curve, use curve type even on V4
   2573         CurrentType = Type;
   2574 
   2575         if ((Curves[i] ->nSegments == 0)||
   2576             ((Curves[i]->nSegments == 2) && (Curves[i] ->Segments[1].Type == 0)) )
   2577             CurrentType = cmsSigCurveType;
   2578         else
   2579         if (Curves[i] ->Segments[0].Type < 0)
   2580             CurrentType = cmsSigCurveType;
   2581 
   2582         if (!_cmsWriteTypeBase(io, CurrentType)) return FALSE;
   2583 
   2584         switch (CurrentType) {
   2585 
   2586             case cmsSigCurveType:
   2587                 if (!Type_Curve_Write(self, io, Curves[i], 1)) return FALSE;
   2588                 break;
   2589 
   2590             case cmsSigParametricCurveType:
   2591                 if (!Type_ParametricCurve_Write(self, io, Curves[i], 1)) return FALSE;
   2592                 break;
   2593 
   2594             default:
   2595                 {
   2596                     char String[5];
   2597 
   2598                     _cmsTagSignature2String(String, (cmsTagSignature) Type);
   2599                     cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown curve type '%s'", String);
   2600                 }
   2601                 return FALSE;
   2602         }
   2603 
   2604         if (!_cmsWriteAlignment(io)) return FALSE;
   2605     }
   2606 
   2607 
   2608     return TRUE;
   2609 }
   2610 
   2611 
   2612 static
   2613 cmsBool WriteCLUT(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt8Number  Precision, cmsStage* mpe)
   2614 {
   2615     cmsUInt8Number  gridPoints[cmsMAXCHANNELS]; // Number of grid points in each dimension.
   2616     cmsUInt32Number i;
   2617     _cmsStageCLutData* CLUT = ( _cmsStageCLutData*) mpe -> Data;
   2618 
   2619     if (CLUT ->HasFloatValues) {
   2620          cmsSignalError(self ->ContextID, cmsERROR_NOT_SUITABLE, "Cannot save floating point data, CLUT are 8 or 16 bit only");
   2621          return FALSE;
   2622     }
   2623 
   2624     memset(gridPoints, 0, sizeof(gridPoints));
   2625     for (i=0; i < (cmsUInt32Number) CLUT ->Params ->nInputs; i++)
   2626         gridPoints[i] = (cmsUInt8Number) CLUT ->Params ->nSamples[i];
   2627 
   2628     if (!io -> Write(io, cmsMAXCHANNELS*sizeof(cmsUInt8Number), gridPoints)) return FALSE;
   2629 
   2630     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) Precision)) return FALSE;
   2631     if (!_cmsWriteUInt8Number(io, 0)) return FALSE;
   2632     if (!_cmsWriteUInt8Number(io, 0)) return FALSE;
   2633     if (!_cmsWriteUInt8Number(io, 0)) return FALSE;
   2634 
   2635     // Precision can be 1 or 2 bytes
   2636     if (Precision == 1) {
   2637 
   2638         for (i=0; i < CLUT->nEntries; i++) {
   2639 
   2640             if (!_cmsWriteUInt8Number(io, FROM_16_TO_8(CLUT->Tab.T[i]))) return FALSE;
   2641         }
   2642     }
   2643     else
   2644         if (Precision == 2) {
   2645 
   2646             if (!_cmsWriteUInt16Array(io, CLUT->nEntries, CLUT ->Tab.T)) return FALSE;
   2647         }
   2648         else {
   2649              cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown precision of '%d'", Precision);
   2650             return FALSE;
   2651         }
   2652 
   2653         if (!_cmsWriteAlignment(io)) return FALSE;
   2654 
   2655         return TRUE;
   2656 }
   2657 
   2658 
   2659 
   2660 
   2661 static
   2662 cmsBool Type_LUTA2B_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
   2663 {
   2664     cmsPipeline* Lut = (cmsPipeline*) Ptr;
   2665     int inputChan, outputChan;
   2666     cmsStage *A = NULL, *B = NULL, *M = NULL;
   2667     cmsStage * Matrix = NULL;
   2668     cmsStage * CLUT = NULL;
   2669     cmsUInt32Number offsetB = 0, offsetMat = 0, offsetM = 0, offsetC = 0, offsetA = 0;
   2670     cmsUInt32Number BaseOffset, DirectoryPos, CurrentPos;
   2671 
   2672     // Get the base for all offsets
   2673     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
   2674 
   2675     if (Lut ->Elements != NULL)
   2676         if (!cmsPipelineCheckAndRetreiveStages(Lut, 1, cmsSigCurveSetElemType, &B))
   2677             if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, &M, &Matrix, &B))
   2678                 if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, &A, &CLUT, &B))
   2679                     if (!cmsPipelineCheckAndRetreiveStages(Lut, 5, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType,
   2680                         cmsSigMatrixElemType, cmsSigCurveSetElemType, &A, &CLUT, &M, &Matrix, &B)) {
   2681 
   2682                             cmsSignalError(self->ContextID, cmsERROR_NOT_SUITABLE, "LUT is not suitable to be saved as LutAToB");
   2683                             return FALSE;
   2684                     }
   2685 
   2686     // Get input, output channels
   2687     inputChan  = cmsPipelineInputChannels(Lut);
   2688     outputChan = cmsPipelineOutputChannels(Lut);
   2689 
   2690     // Write channel count
   2691     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) inputChan)) return FALSE;
   2692     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) outputChan)) return FALSE;
   2693     if (!_cmsWriteUInt16Number(io, 0)) return FALSE;
   2694 
   2695     // Keep directory to be filled latter
   2696     DirectoryPos = io ->Tell(io);
   2697 
   2698     // Write the directory
   2699     if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
   2700     if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
   2701     if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
   2702     if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
   2703     if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
   2704 
   2705     if (A != NULL) {
   2706 
   2707         offsetA = io ->Tell(io) - BaseOffset;
   2708         if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, A)) return FALSE;
   2709     }
   2710 
   2711     if (CLUT != NULL) {
   2712         offsetC = io ->Tell(io) - BaseOffset;
   2713         if (!WriteCLUT(self, io, Lut ->SaveAs8Bits ? 1 : 2, CLUT)) return FALSE;
   2714 
   2715     }
   2716     if (M != NULL) {
   2717 
   2718         offsetM = io ->Tell(io) - BaseOffset;
   2719         if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, M)) return FALSE;
   2720     }
   2721 
   2722     if (Matrix != NULL) {
   2723         offsetMat = io ->Tell(io) - BaseOffset;
   2724         if (!WriteMatrix(self, io, Matrix)) return FALSE;
   2725     }
   2726 
   2727     if (B != NULL) {
   2728 
   2729         offsetB = io ->Tell(io) - BaseOffset;
   2730         if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, B)) return FALSE;
   2731     }
   2732 
   2733     CurrentPos = io ->Tell(io);
   2734 
   2735     if (!io ->Seek(io, DirectoryPos)) return FALSE;
   2736 
   2737     if (!_cmsWriteUInt32Number(io, offsetB)) return FALSE;
   2738     if (!_cmsWriteUInt32Number(io, offsetMat)) return FALSE;
   2739     if (!_cmsWriteUInt32Number(io, offsetM)) return FALSE;
   2740     if (!_cmsWriteUInt32Number(io, offsetC)) return FALSE;
   2741     if (!_cmsWriteUInt32Number(io, offsetA)) return FALSE;
   2742 
   2743     if (!io ->Seek(io, CurrentPos)) return FALSE;
   2744 
   2745     return TRUE;
   2746 
   2747     cmsUNUSED_PARAMETER(nItems);
   2748 }
   2749 
   2750 
   2751 static
   2752 void* Type_LUTA2B_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
   2753 {
   2754     return (void*) cmsPipelineDup((cmsPipeline*) Ptr);
   2755 
   2756     cmsUNUSED_PARAMETER(n);
   2757     cmsUNUSED_PARAMETER(self);
   2758 }
   2759 
   2760 static
   2761 void Type_LUTA2B_Free(struct _cms_typehandler_struct* self, void* Ptr)
   2762 {
   2763     cmsPipelineFree((cmsPipeline*) Ptr);
   2764     return;
   2765 
   2766     cmsUNUSED_PARAMETER(self);
   2767 }
   2768 
   2769 
   2770 // LutBToA type
   2771 
   2772 static
   2773 void* Type_LUTB2A_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
   2774 {
   2775     cmsUInt8Number       inputChan;      // Number of input channels
   2776     cmsUInt8Number       outputChan;     // Number of output channels
   2777     cmsUInt32Number      BaseOffset;     // Actual position in file
   2778     cmsUInt32Number      offsetB;        // Offset to first "B" curve
   2779     cmsUInt32Number      offsetMat;      // Offset to matrix
   2780     cmsUInt32Number      offsetM;        // Offset to first "M" curve
   2781     cmsUInt32Number      offsetC;        // Offset to CLUT
   2782     cmsUInt32Number      offsetA;        // Offset to first "A" curve
   2783     cmsPipeline* NewLUT = NULL;
   2784 
   2785 
   2786     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
   2787 
   2788     if (!_cmsReadUInt8Number(io, &inputChan)) return NULL;
   2789     if (!_cmsReadUInt8Number(io, &outputChan)) return NULL;
   2790 
   2791     // Padding
   2792     if (!_cmsReadUInt16Number(io, NULL)) return NULL;
   2793 
   2794     if (!_cmsReadUInt32Number(io, &offsetB)) return NULL;
   2795     if (!_cmsReadUInt32Number(io, &offsetMat)) return NULL;
   2796     if (!_cmsReadUInt32Number(io, &offsetM)) return NULL;
   2797     if (!_cmsReadUInt32Number(io, &offsetC)) return NULL;
   2798     if (!_cmsReadUInt32Number(io, &offsetA)) return NULL;
   2799 
   2800     // Allocates an empty LUT
   2801     NewLUT = cmsPipelineAlloc(self ->ContextID, inputChan, outputChan);
   2802     if (NewLUT == NULL) return NULL;
   2803 
   2804     if (offsetB != 0) {
   2805         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetB, inputChan)))
   2806             goto Error;
   2807     }
   2808 
   2809     if (offsetMat != 0) {
   2810         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadMatrix(self, io, BaseOffset + offsetMat)))
   2811             goto Error;
   2812     }
   2813 
   2814     if (offsetM != 0) {
   2815         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetM, inputChan)))
   2816             goto Error;
   2817     }
   2818 
   2819     if (offsetC != 0) {
   2820         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadCLUT(self, io, BaseOffset + offsetC, inputChan, outputChan)))
   2821             goto Error;
   2822     }
   2823 
   2824     if (offsetA!= 0) {
   2825         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetA, outputChan)))
   2826             goto Error;
   2827     }
   2828 
   2829     *nItems = 1;
   2830     return NewLUT;
   2831 Error:
   2832     cmsPipelineFree(NewLUT);
   2833     return NULL;
   2834 
   2835     cmsUNUSED_PARAMETER(SizeOfTag);
   2836 }
   2837 
   2838 
   2839 /*
   2840 B
   2841 B - Matrix - M
   2842 B - CLUT - A
   2843 B - Matrix - M - CLUT - A
   2844 */
   2845 
   2846 static
   2847 cmsBool  Type_LUTB2A_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
   2848 {
   2849     cmsPipeline* Lut = (cmsPipeline*) Ptr;
   2850     int inputChan, outputChan;
   2851     cmsStage *A = NULL, *B = NULL, *M = NULL;
   2852     cmsStage *Matrix = NULL;
   2853     cmsStage *CLUT = NULL;
   2854     cmsUInt32Number offsetB = 0, offsetMat = 0, offsetM = 0, offsetC = 0, offsetA = 0;
   2855     cmsUInt32Number BaseOffset, DirectoryPos, CurrentPos;
   2856 
   2857 
   2858     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
   2859 
   2860     if (!cmsPipelineCheckAndRetreiveStages(Lut, 1, cmsSigCurveSetElemType, &B))
   2861         if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, &B, &Matrix, &M))
   2862             if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, &B, &CLUT, &A))
   2863                 if (!cmsPipelineCheckAndRetreiveStages(Lut, 5, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType,
   2864                     cmsSigCLutElemType, cmsSigCurveSetElemType, &B, &Matrix, &M, &CLUT, &A)) {
   2865                         cmsSignalError(self->ContextID, cmsERROR_NOT_SUITABLE, "LUT is not suitable to be saved as LutBToA");
   2866                         return FALSE;
   2867                 }
   2868 
   2869     inputChan  = cmsPipelineInputChannels(Lut);
   2870     outputChan = cmsPipelineOutputChannels(Lut);
   2871 
   2872     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) inputChan)) return FALSE;
   2873     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) outputChan)) return FALSE;
   2874     if (!_cmsWriteUInt16Number(io, 0)) return FALSE;
   2875 
   2876     DirectoryPos = io ->Tell(io);
   2877 
   2878     if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
   2879     if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
   2880     if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
   2881     if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
   2882     if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
   2883 
   2884     if (A != NULL) {
   2885 
   2886         offsetA = io ->Tell(io) - BaseOffset;
   2887         if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, A)) return FALSE;
   2888     }
   2889 
   2890     if (CLUT != NULL) {
   2891         offsetC = io ->Tell(io) - BaseOffset;
   2892         if (!WriteCLUT(self, io, Lut ->SaveAs8Bits ? 1 : 2, CLUT)) return FALSE;
   2893 
   2894     }
   2895     if (M != NULL) {
   2896 
   2897         offsetM = io ->Tell(io) - BaseOffset;
   2898         if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, M)) return FALSE;
   2899     }
   2900 
   2901     if (Matrix != NULL) {
   2902         offsetMat = io ->Tell(io) - BaseOffset;
   2903         if (!WriteMatrix(self, io, Matrix)) return FALSE;
   2904     }
   2905 
   2906     if (B != NULL) {
   2907 
   2908         offsetB = io ->Tell(io) - BaseOffset;
   2909         if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, B)) return FALSE;
   2910     }
   2911 
   2912     CurrentPos = io ->Tell(io);
   2913 
   2914     if (!io ->Seek(io, DirectoryPos)) return FALSE;
   2915 
   2916     if (!_cmsWriteUInt32Number(io, offsetB)) return FALSE;
   2917     if (!_cmsWriteUInt32Number(io, offsetMat)) return FALSE;
   2918     if (!_cmsWriteUInt32Number(io, offsetM)) return FALSE;
   2919     if (!_cmsWriteUInt32Number(io, offsetC)) return FALSE;
   2920     if (!_cmsWriteUInt32Number(io, offsetA)) return FALSE;
   2921 
   2922     if (!io ->Seek(io, CurrentPos)) return FALSE;
   2923 
   2924     return TRUE;
   2925 
   2926     cmsUNUSED_PARAMETER(nItems);
   2927 }
   2928 
   2929 
   2930 
   2931 static
   2932 void* Type_LUTB2A_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
   2933 {
   2934     return (void*) cmsPipelineDup((cmsPipeline*) Ptr);
   2935 
   2936     cmsUNUSED_PARAMETER(n);
   2937     cmsUNUSED_PARAMETER(self);
   2938 }
   2939 
   2940 static
   2941 void Type_LUTB2A_Free(struct _cms_typehandler_struct* self, void* Ptr)
   2942 {
   2943     cmsPipelineFree((cmsPipeline*) Ptr);
   2944     return;
   2945 
   2946     cmsUNUSED_PARAMETER(self);
   2947 }
   2948 
   2949 
   2950 
   2951 // ********************************************************************************
   2952 // Type cmsSigColorantTableType
   2953 // ********************************************************************************
   2954 /*
   2955 The purpose of this tag is to identify the colorants used in the profile by a
   2956 unique name and set of XYZ or L*a*b* values to give the colorant an unambiguous
   2957 value. The first colorant listed is the colorant of the first device channel of
   2958 a lut tag. The second colorant listed is the colorant of the second device channel
   2959 of a lut tag, and so on.
   2960 */
   2961 
   2962 static
   2963 void *Type_ColorantTable_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
   2964 {
   2965     cmsUInt32Number i, Count;
   2966     cmsNAMEDCOLORLIST* List;
   2967     char Name[34];
   2968     cmsUInt16Number PCS[3];
   2969 
   2970 
   2971     if (!_cmsReadUInt32Number(io, &Count)) return NULL;
   2972 
   2973     if (Count > cmsMAXCHANNELS) {
   2974         cmsSignalError(self->ContextID, cmsERROR_RANGE, "Too many colorants '%d'", Count);
   2975         return NULL;
   2976     }
   2977 
   2978     List = cmsAllocNamedColorList(self ->ContextID, Count, 0, "", "");
   2979     for (i=0; i < Count; i++) {
   2980 
   2981         if (io ->Read(io, Name, 32, 1) != 1) goto Error;
   2982         Name[33] = 0;
   2983 
   2984         if (!_cmsReadUInt16Array(io, 3, PCS)) goto Error;
   2985 
   2986         if (!cmsAppendNamedColor(List, Name, PCS, NULL)) goto Error;
   2987 
   2988     }
   2989 
   2990     *nItems = 1;
   2991     return List;
   2992 
   2993 Error:
   2994     *nItems = 0;
   2995     cmsFreeNamedColorList(List);
   2996     return NULL;
   2997 
   2998     cmsUNUSED_PARAMETER(SizeOfTag);
   2999 }
   3000 
   3001 
   3002 
   3003 // Saves a colorant table. It is using the named color structure for simplicity sake
   3004 static
   3005 cmsBool  Type_ColorantTable_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
   3006 {
   3007     cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) Ptr;
   3008     int i, nColors;
   3009 
   3010     nColors = cmsNamedColorCount(NamedColorList);
   3011 
   3012     if (!_cmsWriteUInt32Number(io, nColors)) return FALSE;
   3013 
   3014     for (i=0; i < nColors; i++) {
   3015 
   3016         char root[33];
   3017         cmsUInt16Number PCS[3];
   3018 
   3019         if (!cmsNamedColorInfo(NamedColorList, i, root, NULL, NULL, PCS, NULL)) return 0;
   3020         root[32] = 0;
   3021 
   3022         if (!io ->Write(io, 32, root)) return FALSE;
   3023         if (!_cmsWriteUInt16Array(io, 3, PCS)) return FALSE;
   3024     }
   3025 
   3026     return TRUE;
   3027 
   3028     cmsUNUSED_PARAMETER(nItems);
   3029     cmsUNUSED_PARAMETER(self);
   3030 }
   3031 
   3032 
   3033 static
   3034 void* Type_ColorantTable_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n)
   3035 {
   3036     cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) Ptr;
   3037     return (void*) cmsDupNamedColorList(nc);
   3038 
   3039     cmsUNUSED_PARAMETER(n);
   3040     cmsUNUSED_PARAMETER(self);
   3041 }
   3042 
   3043 
   3044 static
   3045 void Type_ColorantTable_Free(struct _cms_typehandler_struct* self, void* Ptr)
   3046 {
   3047     cmsFreeNamedColorList((cmsNAMEDCOLORLIST*) Ptr);
   3048     return;
   3049 
   3050     cmsUNUSED_PARAMETER(self);
   3051 }
   3052 
   3053 
   3054 // ********************************************************************************
   3055 // Type cmsSigNamedColor2Type
   3056 // ********************************************************************************
   3057 //
   3058 //The namedColor2Type is a count value and array of structures that provide color
   3059 //coordinates for 7-bit ASCII color names. For each named color, a PCS and optional
   3060 //device representation of the color are given. Both representations are 16-bit values.
   3061 //The device representation corresponds to the headers color space of data?field.
   3062 //This representation should be consistent with the number of device components?//field in the namedColor2Type. If this field is 0, device coordinates are not provided.
   3063 //The PCS representation corresponds to the headers PCS field. The PCS representation
   3064 //is always provided. Color names are fixed-length, 32-byte fields including null
   3065 //termination. In order to maintain maximum portability, it is strongly recommended
   3066 //that special characters of the 7-bit ASCII set not be used.
   3067 
   3068 static
   3069 void *Type_NamedColor_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
   3070 {
   3071 
   3072     cmsUInt32Number      vendorFlag;     // Bottom 16 bits for ICC use
   3073     cmsUInt32Number      count;          // Count of named colors
   3074     cmsUInt32Number      nDeviceCoords;  // Num of device coordinates
   3075     char                 prefix[32];     // Prefix for each color name
   3076     char                 suffix[32];     // Suffix for each color name
   3077     cmsNAMEDCOLORLIST*  v;
   3078     cmsUInt32Number i;
   3079 
   3080 
   3081     *nItems = 0;
   3082     if (!_cmsReadUInt32Number(io, &vendorFlag)) return NULL;
   3083     if (!_cmsReadUInt32Number(io, &count)) return NULL;
   3084     if (!_cmsReadUInt32Number(io, &nDeviceCoords)) return NULL;
   3085 
   3086     if (io -> Read(io, prefix, 32, 1) != 1) return NULL;
   3087     if (io -> Read(io, suffix, 32, 1) != 1) return NULL;
   3088 
   3089     prefix[31] = suffix[31] = 0;
   3090 
   3091     v = cmsAllocNamedColorList(self ->ContextID, count, nDeviceCoords, prefix, suffix);
   3092     if (v == NULL) {
   3093         cmsSignalError(self->ContextID, cmsERROR_RANGE, "Too many named colors '%d'", count);
   3094         return NULL;
   3095     }
   3096 
   3097     if (nDeviceCoords > cmsMAXCHANNELS) {
   3098         cmsSignalError(self->ContextID, cmsERROR_RANGE, "Too many device coordinates '%d'", nDeviceCoords);
   3099         return 0;
   3100     }
   3101     for (i=0; i < count; i++) {
   3102 
   3103         cmsUInt16Number PCS[3];
   3104         cmsUInt16Number Colorant[cmsMAXCHANNELS];
   3105         char Root[33];
   3106 
   3107         memset(Colorant, 0, sizeof(Colorant));
   3108         if (io -> Read(io, Root, 32, 1) != 1) return NULL;
   3109         if (!_cmsReadUInt16Array(io, 3, PCS)) goto Error;
   3110         if (!_cmsReadUInt16Array(io, nDeviceCoords, Colorant)) goto Error;
   3111 
   3112         if (!cmsAppendNamedColor(v, Root, PCS, Colorant)) goto Error;
   3113     }
   3114 
   3115     *nItems = 1;
   3116     return (void*) v ;
   3117 
   3118 Error:
   3119     cmsFreeNamedColorList(v);
   3120     return NULL;
   3121 
   3122     cmsUNUSED_PARAMETER(SizeOfTag);
   3123 }
   3124 
   3125 
   3126 // Saves a named color list into a named color profile
   3127 static
   3128 cmsBool Type_NamedColor_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
   3129 {
   3130     cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) Ptr;
   3131     char                prefix[32];     // Prefix for each color name
   3132     char                suffix[32];     // Suffix for each color name
   3133     int i, nColors;
   3134 
   3135     nColors = cmsNamedColorCount(NamedColorList);
   3136 
   3137     if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
   3138     if (!_cmsWriteUInt32Number(io, nColors)) return FALSE;
   3139     if (!_cmsWriteUInt32Number(io, NamedColorList ->ColorantCount)) return FALSE;
   3140 
   3141     strncpy(prefix, (const char*) NamedColorList->Prefix, 32);
   3142     strncpy(suffix, (const char*) NamedColorList->Suffix, 32);
   3143 
   3144     suffix[31] = prefix[31] = 0;
   3145 
   3146     if (!io ->Write(io, 32, prefix)) return FALSE;
   3147     if (!io ->Write(io, 32, suffix)) return FALSE;
   3148 
   3149     for (i=0; i < nColors; i++) {
   3150 
   3151        cmsUInt16Number PCS[3];
   3152        cmsUInt16Number Colorant[cmsMAXCHANNELS];
   3153        char Root[33];
   3154 
   3155         if (!cmsNamedColorInfo(NamedColorList, i, Root, NULL, NULL, PCS, Colorant)) return 0;
   3156         if (!io ->Write(io, 32 , Root)) return FALSE;
   3157         if (!_cmsWriteUInt16Array(io, 3, PCS)) return FALSE;
   3158         if (!_cmsWriteUInt16Array(io, NamedColorList ->ColorantCount, Colorant)) return FALSE;
   3159     }
   3160 
   3161     return TRUE;
   3162 
   3163     cmsUNUSED_PARAMETER(nItems);
   3164     cmsUNUSED_PARAMETER(self);
   3165 }
   3166 
   3167 static
   3168 void* Type_NamedColor_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n)
   3169 {
   3170     cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) Ptr;
   3171 
   3172     return (void*) cmsDupNamedColorList(nc);
   3173 
   3174     cmsUNUSED_PARAMETER(n);
   3175     cmsUNUSED_PARAMETER(self);
   3176 }
   3177 
   3178 
   3179 static
   3180 void Type_NamedColor_Free(struct _cms_typehandler_struct* self, void* Ptr)
   3181 {
   3182     cmsFreeNamedColorList((cmsNAMEDCOLORLIST*) Ptr);
   3183     return;
   3184 
   3185     cmsUNUSED_PARAMETER(self);
   3186 }
   3187 
   3188 
   3189 // ********************************************************************************
   3190 // Type cmsSigProfileSequenceDescType
   3191 // ********************************************************************************
   3192 
   3193 // This type is an array of structures, each of which contains information from the
   3194 // header fields and tags from the original profiles which were combined to create
   3195 // the final profile. The order of the structures is the order in which the profiles
   3196 // were combined and includes a structure for the final profile. This provides a
   3197 // description of the profile sequence from source to destination,
   3198 // typically used with the DeviceLink profile.
   3199 
   3200 static
   3201 cmsBool ReadEmbeddedText(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU** mlu, cmsUInt32Number SizeOfTag)
   3202 {
   3203     cmsTagTypeSignature  BaseType;
   3204     cmsUInt32Number nItems;
   3205 
   3206     BaseType = _cmsReadTypeBase(io);
   3207 
   3208     switch (BaseType) {
   3209 
   3210        case cmsSigTextType:
   3211            if (*mlu) cmsMLUfree(*mlu);
   3212            *mlu = (cmsMLU*)Type_Text_Read(self, io, &nItems, SizeOfTag);
   3213            return (*mlu != NULL);
   3214 
   3215        case cmsSigTextDescriptionType:
   3216            if (*mlu) cmsMLUfree(*mlu);
   3217            *mlu =  (cmsMLU*) Type_Text_Description_Read(self, io, &nItems, SizeOfTag);
   3218            return (*mlu != NULL);
   3219 
   3220            /*
   3221            TBD: Size is needed for MLU, and we have no idea on which is the available size
   3222            */
   3223 
   3224        case cmsSigMultiLocalizedUnicodeType:
   3225            if (*mlu) cmsMLUfree(*mlu);
   3226            *mlu =  (cmsMLU*) Type_MLU_Read(self, io, &nItems, SizeOfTag);
   3227            return (*mlu != NULL);
   3228 
   3229        default: return FALSE;
   3230     }
   3231 }
   3232 
   3233 
   3234 static
   3235 void *Type_ProfileSequenceDesc_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
   3236 {
   3237     cmsSEQ* OutSeq;
   3238     cmsUInt32Number i, Count;
   3239 
   3240     *nItems = 0;
   3241 
   3242     if (!_cmsReadUInt32Number(io, &Count)) return NULL;
   3243 
   3244     if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL;
   3245     SizeOfTag -= sizeof(cmsUInt32Number);
   3246 
   3247 
   3248     OutSeq = cmsAllocProfileSequenceDescription(self ->ContextID, Count);
   3249     if (OutSeq == NULL) return NULL;
   3250 
   3251     OutSeq ->n = Count;
   3252 
   3253     // Get structures as well
   3254 
   3255     for (i=0; i < Count; i++) {
   3256 
   3257         cmsPSEQDESC* sec = &OutSeq -> seq[i];
   3258 
   3259         if (!_cmsReadUInt32Number(io, &sec ->deviceMfg)) goto Error;
   3260         if (SizeOfTag < sizeof(cmsUInt32Number)) goto Error;
   3261         SizeOfTag -= sizeof(cmsUInt32Number);
   3262 
   3263         if (!_cmsReadUInt32Number(io, &sec ->deviceModel)) goto Error;
   3264         if (SizeOfTag < sizeof(cmsUInt32Number)) goto Error;
   3265         SizeOfTag -= sizeof(cmsUInt32Number);
   3266 
   3267         if (!_cmsReadUInt64Number(io, &sec ->attributes)) goto Error;
   3268         if (SizeOfTag < sizeof(cmsUInt64Number)) goto Error;
   3269         SizeOfTag -= sizeof(cmsUInt64Number);
   3270 
   3271         if (!_cmsReadUInt32Number(io, (cmsUInt32Number *)&sec ->technology)) goto Error;
   3272         if (SizeOfTag < sizeof(cmsUInt32Number)) goto Error;
   3273         SizeOfTag -= sizeof(cmsUInt32Number);
   3274 
   3275         if (!ReadEmbeddedText(self, io, &sec ->Manufacturer, SizeOfTag)) goto Error;
   3276         if (!ReadEmbeddedText(self, io, &sec ->Model, SizeOfTag)) goto Error;
   3277     }
   3278 
   3279     *nItems = 1;
   3280     return OutSeq;
   3281 
   3282 Error:
   3283     cmsFreeProfileSequenceDescription(OutSeq);
   3284     return NULL;
   3285 }
   3286 
   3287 
   3288 // Aux--Embed a text description type. It can be of type text description or multilocalized unicode
   3289 // and it depends of the version number passed on cmsTagDescriptor structure instead of stack
   3290 static
   3291 cmsBool  SaveDescription(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* Text)
   3292 {
   3293     if (self ->ICCVersion < 0x4000000) {
   3294 
   3295         if (!_cmsWriteTypeBase(io, cmsSigTextDescriptionType)) return FALSE;
   3296         return Type_Text_Description_Write(self, io, Text, 1);
   3297     }
   3298     else {
   3299         if (!_cmsWriteTypeBase(io, cmsSigMultiLocalizedUnicodeType)) return FALSE;
   3300         return Type_MLU_Write(self, io, Text, 1);
   3301     }
   3302 }
   3303 
   3304 
   3305 static
   3306 cmsBool  Type_ProfileSequenceDesc_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
   3307 {
   3308     cmsSEQ* Seq = (cmsSEQ*) Ptr;
   3309     cmsUInt32Number i;
   3310 
   3311     if (!_cmsWriteUInt32Number(io, Seq->n)) return FALSE;
   3312 
   3313     for (i=0; i < Seq ->n; i++) {
   3314 
   3315         cmsPSEQDESC* sec = &Seq -> seq[i];
   3316 
   3317         if (!_cmsWriteUInt32Number(io, sec ->deviceMfg)) return FALSE;
   3318         if (!_cmsWriteUInt32Number(io, sec ->deviceModel)) return FALSE;
   3319         if (!_cmsWriteUInt64Number(io, &sec ->attributes)) return FALSE;
   3320         if (!_cmsWriteUInt32Number(io, sec ->technology)) return FALSE;
   3321 
   3322         if (!SaveDescription(self, io, sec ->Manufacturer)) return FALSE;
   3323         if (!SaveDescription(self, io, sec ->Model)) return FALSE;
   3324     }
   3325 
   3326      return TRUE;
   3327 
   3328      cmsUNUSED_PARAMETER(nItems);
   3329 }
   3330 
   3331 
   3332 static
   3333 void* Type_ProfileSequenceDesc_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n)
   3334 {
   3335     return (void*) cmsDupProfileSequenceDescription((cmsSEQ*) Ptr);
   3336 
   3337     cmsUNUSED_PARAMETER(n);
   3338     cmsUNUSED_PARAMETER(self);
   3339 }
   3340 
   3341 static
   3342 void Type_ProfileSequenceDesc_Free(struct _cms_typehandler_struct* self, void* Ptr)
   3343 {
   3344     cmsFreeProfileSequenceDescription((cmsSEQ*) Ptr);
   3345     return;
   3346 
   3347     cmsUNUSED_PARAMETER(self);
   3348 }
   3349 
   3350 
   3351 // ********************************************************************************
   3352 // Type cmsSigProfileSequenceIdType
   3353 // ********************************************************************************
   3354 /*
   3355 In certain workflows using ICC Device Link Profiles, it is necessary to identify the
   3356 original profiles that were combined to create the Device Link Profile.
   3357 This type is an array of structures, each of which contains information for
   3358 identification of a profile used in a sequence
   3359 */
   3360 
   3361 
   3362 static
   3363 cmsBool ReadSeqID(struct _cms_typehandler_struct* self,
   3364                                              cmsIOHANDLER* io,
   3365                                              void* Cargo,
   3366                                              cmsUInt32Number n,
   3367                                              cmsUInt32Number SizeOfTag)
   3368 {
   3369     cmsSEQ* OutSeq = (cmsSEQ*) Cargo;
   3370     cmsPSEQDESC* seq = &OutSeq ->seq[n];
   3371 
   3372     if (io -> Read(io, seq ->ProfileID.ID8, 16, 1) != 1) return FALSE;
   3373     if (!ReadEmbeddedText(self, io, &seq ->Description, SizeOfTag)) return FALSE;
   3374 
   3375     return TRUE;
   3376 }
   3377 
   3378 
   3379 
   3380 static
   3381 void *Type_ProfileSequenceId_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
   3382 {
   3383     cmsSEQ* OutSeq;
   3384     cmsUInt32Number Count;
   3385     cmsUInt32Number BaseOffset;
   3386 
   3387     *nItems = 0;
   3388 
   3389     // Get actual position as a basis for element offsets
   3390     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
   3391 
   3392     // Get table count
   3393     if (!_cmsReadUInt32Number(io, &Count)) return NULL;
   3394     SizeOfTag -= sizeof(cmsUInt32Number);
   3395 
   3396     // Allocate an empty structure
   3397     OutSeq = cmsAllocProfileSequenceDescription(self ->ContextID, Count);
   3398     if (OutSeq == NULL) return NULL;
   3399 
   3400 
   3401     // Read the position table
   3402     if (!ReadPositionTable(self, io, Count, BaseOffset, OutSeq, ReadSeqID)) {
   3403 
   3404         cmsFreeProfileSequenceDescription(OutSeq);
   3405         return NULL;
   3406     }
   3407 
   3408     // Success
   3409     *nItems = 1;
   3410     return OutSeq;
   3411 
   3412 }
   3413 
   3414 
   3415 static
   3416 cmsBool WriteSeqID(struct _cms_typehandler_struct* self,
   3417                                              cmsIOHANDLER* io,
   3418                                              void* Cargo,
   3419                                              cmsUInt32Number n,
   3420                                              cmsUInt32Number SizeOfTag)
   3421 {
   3422     cmsSEQ* Seq = (cmsSEQ*) Cargo;
   3423 
   3424     if (!io ->Write(io, 16, Seq ->seq[n].ProfileID.ID8)) return FALSE;
   3425 
   3426     // Store here the MLU
   3427     if (!SaveDescription(self, io, Seq ->seq[n].Description)) return FALSE;
   3428 
   3429     return TRUE;
   3430 
   3431     cmsUNUSED_PARAMETER(SizeOfTag);
   3432 }
   3433 
   3434 static
   3435 cmsBool  Type_ProfileSequenceId_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
   3436 {
   3437     cmsSEQ* Seq = (cmsSEQ*) Ptr;
   3438     cmsUInt32Number BaseOffset;
   3439 
   3440     // Keep the base offset
   3441     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
   3442 
   3443     // This is the table count
   3444     if (!_cmsWriteUInt32Number(io, Seq ->n)) return FALSE;
   3445 
   3446     // This is the position table and content
   3447     if (!WritePositionTable(self, io, 0, Seq ->n, BaseOffset, Seq, WriteSeqID)) return FALSE;
   3448 
   3449     return TRUE;
   3450 
   3451     cmsUNUSED_PARAMETER(nItems);
   3452 }
   3453 
   3454 static
   3455 void* Type_ProfileSequenceId_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n)
   3456 {
   3457     return (void*) cmsDupProfileSequenceDescription((cmsSEQ*) Ptr);
   3458 
   3459     cmsUNUSED_PARAMETER(n);
   3460     cmsUNUSED_PARAMETER(self);
   3461 }
   3462 
   3463 static
   3464 void Type_ProfileSequenceId_Free(struct _cms_typehandler_struct* self, void* Ptr)
   3465 {
   3466     cmsFreeProfileSequenceDescription((cmsSEQ*) Ptr);
   3467     return;
   3468 
   3469     cmsUNUSED_PARAMETER(self);
   3470 }
   3471 
   3472 
   3473 // ********************************************************************************
   3474 // Type cmsSigUcrBgType
   3475 // ********************************************************************************
   3476 /*
   3477 This type contains curves representing the under color removal and black
   3478 generation and a text string which is a general description of the method used
   3479 for the ucr/bg.
   3480 */
   3481 
   3482 static
   3483 void *Type_UcrBg_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
   3484 {
   3485     cmsUcrBg* n = (cmsUcrBg*) _cmsMallocZero(self ->ContextID, sizeof(cmsUcrBg));
   3486     cmsUInt32Number CountUcr, CountBg;
   3487     char* ASCIIString;
   3488 
   3489     *nItems = 0;
   3490     if (n == NULL) return NULL;
   3491 
   3492     // First curve is Under color removal
   3493     if (!_cmsReadUInt32Number(io, &CountUcr)) return NULL;
   3494     if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL;
   3495     SizeOfTag -= sizeof(cmsUInt32Number);
   3496 
   3497     n ->Ucr = cmsBuildTabulatedToneCurve16(self ->ContextID, CountUcr, NULL);
   3498     if (n ->Ucr == NULL) return NULL;
   3499 
   3500     if (!_cmsReadUInt16Array(io, CountUcr, n ->Ucr->Table16)) return NULL;
   3501     if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL;
   3502     SizeOfTag -= CountUcr * sizeof(cmsUInt16Number);
   3503 
   3504     // Second curve is Black generation
   3505     if (!_cmsReadUInt32Number(io, &CountBg)) return NULL;
   3506     if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL;
   3507     SizeOfTag -= sizeof(cmsUInt32Number);
   3508 
   3509     n ->Bg = cmsBuildTabulatedToneCurve16(self ->ContextID, CountBg, NULL);
   3510     if (n ->Bg == NULL) return NULL;
   3511     if (!_cmsReadUInt16Array(io, CountBg, n ->Bg->Table16)) return NULL;
   3512     if (SizeOfTag < CountBg * sizeof(cmsUInt16Number)) return NULL;
   3513     SizeOfTag -= CountBg * sizeof(cmsUInt16Number);
   3514     if (SizeOfTag == UINT_MAX) return NULL;
   3515 
   3516     // Now comes the text. The length is specified by the tag size
   3517     n ->Desc = cmsMLUalloc(self ->ContextID, 1);
   3518     if (n ->Desc == NULL) return NULL;
   3519 
   3520     ASCIIString = (char*) _cmsMalloc(self ->ContextID, SizeOfTag + 1);
   3521     if (io ->Read(io, ASCIIString, sizeof(char), SizeOfTag) != SizeOfTag) return NULL;
   3522     ASCIIString[SizeOfTag] = 0;
   3523     cmsMLUsetASCII(n ->Desc, cmsNoLanguage, cmsNoCountry, ASCIIString);
   3524     _cmsFree(self ->ContextID, ASCIIString);
   3525 
   3526     *nItems = 1;
   3527     return (void*) n;
   3528 }
   3529 
   3530 static
   3531 cmsBool  Type_UcrBg_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
   3532 {
   3533     cmsUcrBg* Value = (cmsUcrBg*) Ptr;
   3534     cmsUInt32Number TextSize;
   3535     char* Text;
   3536 
   3537     // First curve is Under color removal
   3538     if (!_cmsWriteUInt32Number(io, Value ->Ucr ->nEntries)) return FALSE;
   3539     if (!_cmsWriteUInt16Array(io, Value ->Ucr ->nEntries, Value ->Ucr ->Table16)) return FALSE;
   3540 
   3541     // Then black generation
   3542     if (!_cmsWriteUInt32Number(io, Value ->Bg ->nEntries)) return FALSE;
   3543     if (!_cmsWriteUInt16Array(io, Value ->Bg ->nEntries, Value ->Bg ->Table16)) return FALSE;
   3544 
   3545     // Now comes the text. The length is specified by the tag size
   3546     TextSize = cmsMLUgetASCII(Value ->Desc, cmsNoLanguage, cmsNoCountry, NULL, 0);
   3547     Text     = (char*) _cmsMalloc(self ->ContextID, TextSize);
   3548     if (cmsMLUgetASCII(Value ->Desc, cmsNoLanguage, cmsNoCountry, Text, TextSize) != TextSize) return FALSE;
   3549 
   3550     if (!io ->Write(io, TextSize, Text)) return FALSE;
   3551     _cmsFree(self ->ContextID, Text);
   3552 
   3553     return TRUE;
   3554 
   3555     cmsUNUSED_PARAMETER(nItems);
   3556 }
   3557 
   3558 static
   3559 void* Type_UcrBg_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
   3560 {
   3561     cmsUcrBg* Src = (cmsUcrBg*) Ptr;
   3562     cmsUcrBg* NewUcrBg = (cmsUcrBg*) _cmsMallocZero(self ->ContextID, sizeof(cmsUcrBg));
   3563 
   3564     if (NewUcrBg == NULL) return NULL;
   3565 
   3566     NewUcrBg ->Bg   = cmsDupToneCurve(Src ->Bg);
   3567     NewUcrBg ->Ucr  = cmsDupToneCurve(Src ->Ucr);
   3568     NewUcrBg ->Desc = cmsMLUdup(Src ->Desc);
   3569 
   3570     return (void*) NewUcrBg;
   3571 
   3572     cmsUNUSED_PARAMETER(n);
   3573 }
   3574 
   3575 static
   3576 void Type_UcrBg_Free(struct _cms_typehandler_struct* self, void *Ptr)
   3577 {
   3578    cmsUcrBg* Src = (cmsUcrBg*) Ptr;
   3579 
   3580    if (Src ->Ucr) cmsFreeToneCurve(Src ->Ucr);
   3581    if (Src ->Bg)  cmsFreeToneCurve(Src ->Bg);
   3582    if (Src ->Desc) cmsMLUfree(Src ->Desc);
   3583 
   3584    _cmsFree(self ->ContextID, Ptr);
   3585 }
   3586 
   3587 // ********************************************************************************
   3588 // Type cmsSigCrdInfoType
   3589 // ********************************************************************************
   3590 
   3591 /*
   3592 This type contains the PostScript product name to which this profile corresponds
   3593 and the names of the companion CRDs. Recall that a single profile can generate
   3594 multiple CRDs. It is implemented as a MLU being the language code "PS" and then
   3595 country varies for each element:
   3596 
   3597                 nm: PostScript product name
   3598                 #0: Rendering intent 0 CRD name
   3599                 #1: Rendering intent 1 CRD name
   3600                 #2: Rendering intent 2 CRD name
   3601                 #3: Rendering intent 3 CRD name
   3602 */
   3603 
   3604 
   3605 
   3606 // Auxiliar, read an string specified as count + string
   3607 static
   3608 cmsBool  ReadCountAndSting(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* mlu, cmsUInt32Number* SizeOfTag, const char* Section)
   3609 {
   3610     cmsUInt32Number Count;
   3611     char* Text;
   3612 
   3613     if (*SizeOfTag < sizeof(cmsUInt32Number)) return FALSE;
   3614 
   3615     if (!_cmsReadUInt32Number(io, &Count)) return FALSE;
   3616 
   3617     if (Count > UINT_MAX - sizeof(cmsUInt32Number)) return FALSE;
   3618     if (*SizeOfTag < Count + sizeof(cmsUInt32Number)) return FALSE;
   3619 
   3620     Text     = (char*) _cmsMalloc(self ->ContextID, Count+1);
   3621     if (Text == NULL) return FALSE;
   3622 
   3623     if (io ->Read(io, Text, sizeof(cmsUInt8Number), Count) != Count) {
   3624         _cmsFree(self ->ContextID, Text);
   3625         return FALSE;
   3626     }
   3627 
   3628     Text[Count] = 0;
   3629 
   3630     cmsMLUsetASCII(mlu, "PS", Section, Text);
   3631     _cmsFree(self ->ContextID, Text);
   3632 
   3633     *SizeOfTag -= (Count + sizeof(cmsUInt32Number));
   3634     return TRUE;
   3635 }
   3636 
   3637 static
   3638 cmsBool  WriteCountAndSting(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* mlu, const char* Section)
   3639 {
   3640  cmsUInt32Number TextSize;
   3641  char* Text;
   3642 
   3643     TextSize = cmsMLUgetASCII(mlu, "PS", Section, NULL, 0);
   3644     Text     = (char*) _cmsMalloc(self ->ContextID, TextSize);
   3645 
   3646     if (!_cmsWriteUInt32Number(io, TextSize)) return FALSE;
   3647 
   3648     if (cmsMLUgetASCII(mlu, "PS", Section, Text, TextSize) == 0) return FALSE;
   3649 
   3650     if (!io ->Write(io, TextSize, Text)) return FALSE;
   3651     _cmsFree(self ->ContextID, Text);
   3652 
   3653     return TRUE;
   3654 }
   3655 
   3656 static
   3657 void *Type_CrdInfo_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
   3658 {
   3659     cmsMLU* mlu = cmsMLUalloc(self ->ContextID, 5);
   3660 
   3661     *nItems = 0;
   3662     if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "nm")) goto Error;
   3663     if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#0")) goto Error;
   3664     if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#1")) goto Error;
   3665     if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#2")) goto Error;
   3666     if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#3")) goto Error;
   3667 
   3668     *nItems = 1;
   3669     return (void*) mlu;
   3670 
   3671 Error:
   3672     cmsMLUfree(mlu);
   3673     return NULL;
   3674 
   3675 }
   3676 
   3677 static
   3678 cmsBool  Type_CrdInfo_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
   3679 {
   3680 
   3681     cmsMLU* mlu = (cmsMLU*) Ptr;
   3682 
   3683     if (!WriteCountAndSting(self, io, mlu, "nm")) goto Error;
   3684     if (!WriteCountAndSting(self, io, mlu, "#0")) goto Error;
   3685     if (!WriteCountAndSting(self, io, mlu, "#1")) goto Error;
   3686     if (!WriteCountAndSting(self, io, mlu, "#2")) goto Error;
   3687     if (!WriteCountAndSting(self, io, mlu, "#3")) goto Error;
   3688 
   3689     return TRUE;
   3690 
   3691 Error:
   3692     return FALSE;
   3693 
   3694     cmsUNUSED_PARAMETER(nItems);
   3695 }
   3696 
   3697 
   3698 static
   3699 void* Type_CrdInfo_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
   3700 {
   3701     return (void*) cmsMLUdup((cmsMLU*) Ptr);
   3702 
   3703     cmsUNUSED_PARAMETER(n);
   3704     cmsUNUSED_PARAMETER(self);
   3705 }
   3706 
   3707 static
   3708 void Type_CrdInfo_Free(struct _cms_typehandler_struct* self, void *Ptr)
   3709 {
   3710     cmsMLUfree((cmsMLU*) Ptr);
   3711     return;
   3712 
   3713     cmsUNUSED_PARAMETER(self);
   3714 }
   3715 
   3716 // ********************************************************************************
   3717 // Type cmsSigScreeningType
   3718 // ********************************************************************************
   3719 //
   3720 //The screeningType describes various screening parameters including screen
   3721 //frequency, screening angle, and spot shape.
   3722 
   3723 static
   3724 void *Type_Screening_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
   3725 {
   3726     cmsScreening* sc = NULL;
   3727     cmsUInt32Number i;
   3728 
   3729     sc = (cmsScreening*) _cmsMallocZero(self ->ContextID, sizeof(cmsScreening));
   3730     if (sc == NULL) return NULL;
   3731 
   3732     *nItems = 0;
   3733 
   3734     if (!_cmsReadUInt32Number(io, &sc ->Flag)) goto Error;
   3735     if (!_cmsReadUInt32Number(io, &sc ->nChannels)) goto Error;
   3736 
   3737     if (sc ->nChannels > cmsMAXCHANNELS - 1)
   3738         sc ->nChannels = cmsMAXCHANNELS - 1;
   3739 
   3740     for (i=0; i < sc ->nChannels; i++) {
   3741 
   3742         if (!_cmsRead15Fixed16Number(io, &sc ->Channels[i].Frequency)) goto Error;
   3743         if (!_cmsRead15Fixed16Number(io, &sc ->Channels[i].ScreenAngle)) goto Error;
   3744         if (!_cmsReadUInt32Number(io, &sc ->Channels[i].SpotShape)) goto Error;
   3745     }
   3746 
   3747 
   3748     *nItems = 1;
   3749 
   3750     return (void*) sc;
   3751 
   3752 Error:
   3753     if (sc != NULL)
   3754         _cmsFree(self ->ContextID, sc);
   3755 
   3756     return NULL;
   3757 
   3758     cmsUNUSED_PARAMETER(SizeOfTag);
   3759 }
   3760 
   3761 
   3762 static
   3763 cmsBool Type_Screening_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
   3764 {
   3765     cmsScreening* sc = (cmsScreening* ) Ptr;
   3766     cmsUInt32Number i;
   3767 
   3768     if (!_cmsWriteUInt32Number(io, sc ->Flag)) return FALSE;
   3769     if (!_cmsWriteUInt32Number(io, sc ->nChannels)) return FALSE;
   3770 
   3771     for (i=0; i < sc ->nChannels; i++) {
   3772 
   3773         if (!_cmsWrite15Fixed16Number(io, sc ->Channels[i].Frequency)) return FALSE;
   3774         if (!_cmsWrite15Fixed16Number(io, sc ->Channels[i].ScreenAngle)) return FALSE;
   3775         if (!_cmsWriteUInt32Number(io, sc ->Channels[i].SpotShape)) return FALSE;
   3776     }
   3777 
   3778     return TRUE;
   3779 
   3780     cmsUNUSED_PARAMETER(nItems);
   3781     cmsUNUSED_PARAMETER(self);
   3782 }
   3783 
   3784 
   3785 static
   3786 void* Type_Screening_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
   3787 {
   3788    return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsScreening));
   3789 
   3790    cmsUNUSED_PARAMETER(n);
   3791 }
   3792 
   3793 
   3794 static
   3795 void Type_Screening_Free(struct _cms_typehandler_struct* self, void* Ptr)
   3796 {
   3797    _cmsFree(self ->ContextID, Ptr);
   3798 }
   3799 
   3800 // ********************************************************************************
   3801 // Type cmsSigViewingConditionsType
   3802 // ********************************************************************************
   3803 //
   3804 //This type represents a set of viewing condition parameters including:
   3805 //CIE absolute?illuminant white point tristimulus values and CIE absolute?//surround tristimulus values.
   3806 
   3807 static
   3808 void *Type_ViewingConditions_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
   3809 {
   3810     cmsICCViewingConditions* vc = NULL;
   3811 
   3812     vc = (cmsICCViewingConditions*) _cmsMallocZero(self ->ContextID, sizeof(cmsICCViewingConditions));
   3813     if (vc == NULL) return NULL;
   3814 
   3815     *nItems = 0;
   3816 
   3817     if (!_cmsReadXYZNumber(io, &vc ->IlluminantXYZ)) goto Error;
   3818     if (!_cmsReadXYZNumber(io, &vc ->SurroundXYZ)) goto Error;
   3819     if (!_cmsReadUInt32Number(io, &vc ->IlluminantType)) goto Error;
   3820 
   3821     *nItems = 1;
   3822 
   3823     return (void*) vc;
   3824 
   3825 Error:
   3826     if (vc != NULL)
   3827         _cmsFree(self ->ContextID, vc);
   3828 
   3829     return NULL;
   3830 
   3831     cmsUNUSED_PARAMETER(SizeOfTag);
   3832 }
   3833 
   3834 
   3835 static
   3836 cmsBool Type_ViewingConditions_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
   3837 {
   3838     cmsICCViewingConditions* sc = (cmsICCViewingConditions* ) Ptr;
   3839 
   3840     if (!_cmsWriteXYZNumber(io, &sc ->IlluminantXYZ)) return FALSE;
   3841     if (!_cmsWriteXYZNumber(io, &sc ->SurroundXYZ)) return FALSE;
   3842     if (!_cmsWriteUInt32Number(io, sc ->IlluminantType)) return FALSE;
   3843 
   3844     return TRUE;
   3845 
   3846     cmsUNUSED_PARAMETER(nItems);
   3847     cmsUNUSED_PARAMETER(self);
   3848 }
   3849 
   3850 
   3851 static
   3852 void* Type_ViewingConditions_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
   3853 {
   3854    return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsScreening));
   3855 
   3856    cmsUNUSED_PARAMETER(n);
   3857 }
   3858 
   3859 
   3860 static
   3861 void Type_ViewingConditions_Free(struct _cms_typehandler_struct* self, void* Ptr)
   3862 {
   3863    _cmsFree(self ->ContextID, Ptr);
   3864 }
   3865 
   3866 
   3867 // ********************************************************************************
   3868 // Type cmsSigMultiProcessElementType
   3869 // ********************************************************************************
   3870 
   3871 
   3872 static
   3873 void* GenericMPEdup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
   3874 {
   3875     return (void*) cmsStageDup((cmsStage*) Ptr);
   3876 
   3877     cmsUNUSED_PARAMETER(n);
   3878     cmsUNUSED_PARAMETER(self);
   3879 }
   3880 
   3881 static
   3882 void GenericMPEfree(struct _cms_typehandler_struct* self, void *Ptr)
   3883 {
   3884     cmsStageFree((cmsStage*) Ptr);
   3885     return;
   3886 
   3887     cmsUNUSED_PARAMETER(self);
   3888 }
   3889 
   3890 // Each curve is stored in one or more curve segments, with break-points specified between curve segments.
   3891 // The first curve segment always starts at Infinity, and the last curve segment always ends at +Infinity. The
   3892 // first and last curve segments shall be specified in terms of a formula, whereas the other segments shall be
   3893 // specified either in terms of a formula, or by a sampled curve.
   3894 
   3895 
   3896 // Read an embedded segmented curve
   3897 static
   3898 cmsToneCurve* ReadSegmentedCurve(struct _cms_typehandler_struct* self, cmsIOHANDLER* io)
   3899 {
   3900     cmsCurveSegSignature ElementSig;
   3901     cmsUInt32Number i, j;
   3902     cmsUInt16Number nSegments;
   3903     cmsCurveSegment*  Segments;
   3904     cmsToneCurve* Curve;
   3905     cmsFloat32Number PrevBreak = -1E22F;    // - infinite
   3906 
   3907     // Take signature and channels for each element.
   3908      if (!_cmsReadUInt32Number(io, (cmsUInt32Number*) &ElementSig)) return NULL;
   3909 
   3910      // That should be a segmented curve
   3911      if (ElementSig != cmsSigSegmentedCurve) return NULL;
   3912 
   3913      if (!_cmsReadUInt32Number(io, NULL)) return NULL;
   3914      if (!_cmsReadUInt16Number(io, &nSegments)) return NULL;
   3915      if (!_cmsReadUInt16Number(io, NULL)) return NULL;
   3916 
   3917      if (nSegments < 1) return NULL;
   3918      Segments = (cmsCurveSegment*) _cmsCalloc(self ->ContextID, nSegments, sizeof(cmsCurveSegment));
   3919      if (Segments == NULL) return NULL;
   3920 
   3921      // Read breakpoints
   3922      for (i=0; i < (cmsUInt32Number) nSegments - 1; i++) {
   3923 
   3924          Segments[i].x0 = PrevBreak;
   3925          if (!_cmsReadFloat32Number(io, &Segments[i].x1)) goto Error;
   3926          PrevBreak = Segments[i].x1;
   3927      }
   3928 
   3929      Segments[nSegments-1].x0 = PrevBreak;
   3930      Segments[nSegments-1].x1 = 1E22F;     // A big cmsFloat32Number number
   3931 
   3932      // Read segments
   3933      for (i=0; i < nSegments; i++) {
   3934 
   3935           if (!_cmsReadUInt32Number(io, (cmsUInt32Number*) &ElementSig)) goto Error;
   3936           if (!_cmsReadUInt32Number(io, NULL)) goto Error;
   3937 
   3938            switch (ElementSig) {
   3939 
   3940             case cmsSigFormulaCurveSeg: {
   3941 
   3942                 cmsUInt16Number Type;
   3943                 cmsUInt32Number ParamsByType[] = {4, 5, 5 };
   3944 
   3945                 if (!_cmsReadUInt16Number(io, &Type)) goto Error;
   3946                 if (!_cmsReadUInt16Number(io, NULL)) goto Error;
   3947 
   3948                 Segments[i].Type = Type + 6;
   3949                 if (Type > 2) goto Error;
   3950 
   3951                 for (j=0; j < ParamsByType[Type]; j++) {
   3952 
   3953                     cmsFloat32Number f;
   3954                     if (!_cmsReadFloat32Number(io, &f)) goto Error;
   3955                     Segments[i].Params[j] = f;
   3956                 }
   3957                 }
   3958                 break;
   3959 
   3960 
   3961             case cmsSigSampledCurveSeg: {
   3962                 cmsUInt32Number Count;
   3963 
   3964                 if (!_cmsReadUInt32Number(io, &Count)) return NULL;
   3965 
   3966                 Segments[i].nGridPoints = Count;
   3967                 Segments[i].SampledPoints = (cmsFloat32Number*) _cmsCalloc(self ->ContextID, Count, sizeof(cmsFloat32Number));
   3968                 if (Segments[i].SampledPoints == NULL) goto Error;
   3969 
   3970                 for (j=0; j < Count; j++) {
   3971                     if (!_cmsReadFloat32Number(io, &Segments[i].SampledPoints[j])) goto Error;
   3972                 }
   3973                 }
   3974                 break;
   3975 
   3976             default:
   3977                 {
   3978                 char String[5];
   3979 
   3980                 _cmsTagSignature2String(String, (cmsTagSignature) ElementSig);
   3981                 cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown curve element type '%s' found.", String);
   3982                 }
   3983                 return NULL;
   3984 
   3985          }
   3986      }
   3987 
   3988      Curve = cmsBuildSegmentedToneCurve(self ->ContextID, nSegments, Segments);
   3989 
   3990      for (i=0; i < nSegments; i++) {
   3991          if (Segments[i].SampledPoints) _cmsFree(self ->ContextID, Segments[i].SampledPoints);
   3992      }
   3993      _cmsFree(self ->ContextID, Segments);
   3994      return Curve;
   3995 
   3996 Error:
   3997      if (Segments) _cmsFree(self ->ContextID, Segments);
   3998      return NULL;
   3999 }
   4000 
   4001 
   4002 static
   4003 cmsBool ReadMPECurve(struct _cms_typehandler_struct* self,
   4004                      cmsIOHANDLER* io,
   4005                      void* Cargo,
   4006                      cmsUInt32Number n,
   4007                      cmsUInt32Number SizeOfTag)
   4008 {
   4009       cmsToneCurve** GammaTables = ( cmsToneCurve**) Cargo;
   4010 
   4011       GammaTables[n] = ReadSegmentedCurve(self, io);
   4012       return (GammaTables[n] != NULL);
   4013 
   4014       cmsUNUSED_PARAMETER(SizeOfTag);
   4015 }
   4016 
   4017 static
   4018 void *Type_MPEcurve_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
   4019 {
   4020     cmsStage* mpe = NULL;
   4021     cmsUInt16Number InputChans, OutputChans;
   4022     cmsUInt32Number i, BaseOffset;
   4023     cmsToneCurve** GammaTables;
   4024 
   4025     *nItems = 0;
   4026 
   4027     // Get actual position as a basis for element offsets
   4028     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
   4029 
   4030     if (!_cmsReadUInt16Number(io, &InputChans)) return NULL;
   4031     if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL;
   4032 
   4033     if (InputChans != OutputChans) return NULL;
   4034 
   4035     GammaTables = (cmsToneCurve**) _cmsCalloc(self ->ContextID, InputChans, sizeof(cmsToneCurve*));
   4036     if (GammaTables == NULL) return NULL;
   4037 
   4038     if (ReadPositionTable(self, io, InputChans, BaseOffset, GammaTables, ReadMPECurve)) {
   4039 
   4040         mpe = cmsStageAllocToneCurves(self ->ContextID, InputChans, GammaTables);
   4041     }
   4042     else {
   4043         mpe = NULL;
   4044     }
   4045 
   4046     for (i=0; i < InputChans; i++) {
   4047         if (GammaTables[i]) cmsFreeToneCurve(GammaTables[i]);
   4048     }
   4049 
   4050     _cmsFree(self ->ContextID, GammaTables);
   4051     *nItems = (mpe != NULL) ? 1 : 0;
   4052     return mpe;
   4053 
   4054     cmsUNUSED_PARAMETER(SizeOfTag);
   4055 }
   4056 
   4057 
   4058 // Write a single segmented curve. NO CHECK IS PERFORMED ON VALIDITY
   4059 static
   4060 cmsBool WriteSegmentedCurve(cmsIOHANDLER* io, cmsToneCurve* g)
   4061 {
   4062     cmsUInt32Number i, j;
   4063     cmsCurveSegment* Segments = g ->Segments;
   4064     cmsUInt32Number nSegments = g ->nSegments;
   4065 
   4066     if (!_cmsWriteUInt32Number(io, cmsSigSegmentedCurve)) goto Error;
   4067     if (!_cmsWriteUInt32Number(io, 0)) goto Error;
   4068     if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) nSegments)) goto Error;
   4069     if (!_cmsWriteUInt16Number(io, 0)) goto Error;
   4070 
   4071     // Write the break-points
   4072     for (i=0; i < nSegments - 1; i++) {
   4073         if (!_cmsWriteFloat32Number(io, Segments[i].x1)) goto Error;
   4074     }
   4075 
   4076     // Write the segments
   4077     for (i=0; i < g ->nSegments; i++) {
   4078 
   4079         cmsCurveSegment* ActualSeg = Segments + i;
   4080 
   4081         if (ActualSeg -> Type == 0) {
   4082 
   4083             // This is a sampled curve
   4084             if (!_cmsWriteUInt32Number(io, (cmsUInt32Number) cmsSigSampledCurveSeg)) goto Error;
   4085             if (!_cmsWriteUInt32Number(io, 0)) goto Error;
   4086             if (!_cmsWriteUInt32Number(io, ActualSeg -> nGridPoints)) goto Error;
   4087 
   4088             for (j=0; j < g ->Segments[i].nGridPoints; j++) {
   4089                 if (!_cmsWriteFloat32Number(io, ActualSeg -> SampledPoints[j])) goto Error;
   4090             }
   4091 
   4092         }
   4093         else {
   4094             int Type;
   4095             cmsUInt32Number ParamsByType[] = { 4, 5, 5 };
   4096 
   4097             // This is a formula-based
   4098             if (!_cmsWriteUInt32Number(io, (cmsUInt32Number) cmsSigFormulaCurveSeg)) goto Error;
   4099             if (!_cmsWriteUInt32Number(io, 0)) goto Error;
   4100 
   4101             // We only allow 1, 2 and 3 as types
   4102             Type = ActualSeg ->Type - 6;
   4103             if (Type > 2 || Type < 0) goto Error;
   4104 
   4105             if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) Type)) goto Error;
   4106             if (!_cmsWriteUInt16Number(io, 0)) goto Error;
   4107 
   4108             for (j=0; j < ParamsByType[Type]; j++) {
   4109                 if (!_cmsWriteFloat32Number(io, (cmsFloat32Number) ActualSeg ->Params[j])) goto Error;
   4110             }
   4111         }
   4112 
   4113         // It seems there is no need to align. Code is here, and for safety commented out
   4114         // if (!_cmsWriteAlignment(io)) goto Error;
   4115     }
   4116 
   4117     return TRUE;
   4118 
   4119 Error:
   4120     return FALSE;
   4121 }
   4122 
   4123 
   4124 static
   4125 cmsBool WriteMPECurve(struct _cms_typehandler_struct* self,
   4126                       cmsIOHANDLER* io,
   4127                       void* Cargo,
   4128                       cmsUInt32Number n,
   4129                       cmsUInt32Number SizeOfTag)
   4130 {
   4131     _cmsStageToneCurvesData* Curves  = (_cmsStageToneCurvesData*) Cargo;
   4132 
   4133     return WriteSegmentedCurve(io, Curves ->TheCurves[n]);
   4134 
   4135     cmsUNUSED_PARAMETER(SizeOfTag);
   4136     cmsUNUSED_PARAMETER(self);
   4137 }
   4138 
   4139 // Write a curve, checking first for validity
   4140 static
   4141 cmsBool  Type_MPEcurve_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
   4142 {
   4143     cmsUInt32Number BaseOffset;
   4144     cmsStage* mpe = (cmsStage*) Ptr;
   4145     _cmsStageToneCurvesData* Curves = (_cmsStageToneCurvesData*) mpe ->Data;
   4146 
   4147     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
   4148 
   4149     // Write the header. Since those are curves, input and output channels are same
   4150     if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->InputChannels)) return FALSE;
   4151     if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->InputChannels)) return FALSE;
   4152 
   4153     if (!WritePositionTable(self, io, 0,
   4154                                 mpe ->InputChannels, BaseOffset, Curves, WriteMPECurve)) return FALSE;
   4155 
   4156 
   4157     return TRUE;
   4158 
   4159     cmsUNUSED_PARAMETER(nItems);
   4160 }
   4161 
   4162 
   4163 
   4164 // The matrix is organized as an array of PxQ+Q elements, where P is the number of input channels to the
   4165 // matrix, and Q is the number of output channels. The matrix elements are each float32Numbers. The array
   4166 // is organized as follows:
   4167 // array = [e11, e12, ? e1P, e21, e22, ? e2P, ? eQ1, eQ2, ? eQP, e1, e2, ? eQ]
   4168 
   4169 static
   4170 void *Type_MPEmatrix_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
   4171 {
   4172     cmsStage* mpe;
   4173     cmsUInt16Number   InputChans, OutputChans;
   4174     cmsUInt32Number   nElems, i;
   4175     cmsFloat64Number* Matrix;
   4176     cmsFloat64Number* Offsets;
   4177 
   4178     if (!_cmsReadUInt16Number(io, &InputChans)) return NULL;
   4179     if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL;
   4180 
   4181 
   4182     nElems = InputChans * OutputChans;
   4183 
   4184     // Input and output chans may be ANY (up to 0xffff)
   4185     Matrix = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, nElems, sizeof(cmsFloat64Number));
   4186     if (Matrix == NULL) return NULL;
   4187 
   4188     Offsets = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, OutputChans, sizeof(cmsFloat64Number));
   4189     if (Offsets == NULL) {
   4190 
   4191         _cmsFree(self ->ContextID, Matrix);
   4192         return NULL;
   4193     }
   4194 
   4195     for (i=0; i < nElems; i++) {
   4196 
   4197         cmsFloat32Number v;
   4198 
   4199         if (!_cmsReadFloat32Number(io, &v)) return NULL;
   4200         Matrix[i] = v;
   4201     }
   4202 
   4203 
   4204     for (i=0; i < OutputChans; i++) {
   4205 
   4206         cmsFloat32Number v;
   4207 
   4208         if (!_cmsReadFloat32Number(io, &v)) return NULL;
   4209         Offsets[i] = v;
   4210     }
   4211 
   4212 
   4213     mpe = cmsStageAllocMatrix(self ->ContextID, OutputChans, InputChans, Matrix, Offsets);
   4214     _cmsFree(self ->ContextID, Matrix);
   4215     _cmsFree(self ->ContextID, Offsets);
   4216 
   4217     *nItems = 1;
   4218 
   4219     return mpe;
   4220 
   4221     cmsUNUSED_PARAMETER(SizeOfTag);
   4222 }
   4223 
   4224 static
   4225 cmsBool  Type_MPEmatrix_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
   4226 {
   4227     cmsUInt32Number i, nElems;
   4228     cmsStage* mpe = (cmsStage*) Ptr;
   4229     _cmsStageMatrixData* Matrix = (_cmsStageMatrixData*) mpe ->Data;
   4230 
   4231     if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->InputChannels)) return FALSE;
   4232     if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->OutputChannels)) return FALSE;
   4233 
   4234     nElems = mpe ->InputChannels * mpe ->OutputChannels;
   4235 
   4236     for (i=0; i < nElems; i++) {
   4237         if (!_cmsWriteFloat32Number(io, (cmsFloat32Number) Matrix->Double[i])) return FALSE;
   4238     }
   4239 
   4240 
   4241     for (i=0; i < mpe ->OutputChannels; i++) {
   4242 
   4243         if (Matrix ->Offset == NULL) {
   4244 
   4245                if (!_cmsWriteFloat32Number(io, 0)) return FALSE;
   4246         }
   4247         else {
   4248                if (!_cmsWriteFloat32Number(io, (cmsFloat32Number) Matrix->Offset[i])) return FALSE;
   4249         }
   4250     }
   4251 
   4252     return TRUE;
   4253 
   4254     cmsUNUSED_PARAMETER(nItems);
   4255     cmsUNUSED_PARAMETER(self);
   4256 }
   4257 
   4258 
   4259 
   4260 static
   4261 void *Type_MPEclut_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
   4262 {
   4263     cmsStage* mpe = NULL;
   4264     cmsUInt16Number InputChans, OutputChans;
   4265     cmsUInt8Number Dimensions8[16];
   4266     cmsUInt32Number i, nMaxGrids, GridPoints[MAX_INPUT_DIMENSIONS];
   4267     _cmsStageCLutData* clut;
   4268 
   4269     if (!_cmsReadUInt16Number(io, &InputChans)) return NULL;
   4270     if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL;
   4271 
   4272     if (InputChans == 0) goto Error;
   4273     if (OutputChans == 0) goto Error;
   4274 
   4275     if (io ->Read(io, Dimensions8, sizeof(cmsUInt8Number), 16) != 16)
   4276         goto Error;
   4277 
   4278     // Copy MAX_INPUT_DIMENSIONS at most. Expand to cmsUInt32Number
   4279     nMaxGrids = InputChans > MAX_INPUT_DIMENSIONS ? MAX_INPUT_DIMENSIONS : InputChans;
   4280     for (i=0; i < nMaxGrids; i++) GridPoints[i] = (cmsUInt32Number) Dimensions8[i];
   4281 
   4282     // Allocate the true CLUT
   4283     mpe = cmsStageAllocCLutFloatGranular(self ->ContextID, GridPoints, InputChans, OutputChans, NULL);
   4284     if (mpe == NULL) goto Error;
   4285 
   4286     // Read the data
   4287     clut = (_cmsStageCLutData*) mpe ->Data;
   4288     for (i=0; i < clut ->nEntries; i++) {
   4289 
   4290         if (!_cmsReadFloat32Number(io, &clut ->Tab.TFloat[i])) goto Error;
   4291     }
   4292 
   4293     *nItems = 1;
   4294     return mpe;
   4295 
   4296 Error:
   4297     *nItems = 0;
   4298     if (mpe != NULL) cmsStageFree(mpe);
   4299     return NULL;
   4300 
   4301     cmsUNUSED_PARAMETER(SizeOfTag);
   4302 }
   4303 
   4304 // Write a CLUT in floating point
   4305 static
   4306 cmsBool  Type_MPEclut_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
   4307 {
   4308     cmsUInt8Number Dimensions8[16];
   4309     cmsUInt32Number i;
   4310     cmsStage* mpe = (cmsStage*) Ptr;
   4311     _cmsStageCLutData* clut = (_cmsStageCLutData*) mpe ->Data;
   4312 
   4313     // Check for maximum number of channels
   4314     if (mpe -> InputChannels > 15) return FALSE;
   4315 
   4316     // Only floats are supported in MPE
   4317     if (clut ->HasFloatValues == FALSE) return FALSE;
   4318 
   4319     if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->InputChannels)) return FALSE;
   4320     if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->OutputChannels)) return FALSE;
   4321 
   4322     memset(Dimensions8, 0, sizeof(Dimensions8));
   4323 
   4324     for (i=0; i < mpe ->InputChannels; i++)
   4325         Dimensions8[i] = (cmsUInt8Number) clut ->Params ->nSamples[i];
   4326 
   4327     if (!io ->Write(io, 16, Dimensions8)) return FALSE;
   4328 
   4329     for (i=0; i < clut ->nEntries; i++) {
   4330 
   4331         if (!_cmsWriteFloat32Number(io, clut ->Tab.TFloat[i])) return FALSE;
   4332     }
   4333 
   4334     return TRUE;
   4335 
   4336     cmsUNUSED_PARAMETER(nItems);
   4337     cmsUNUSED_PARAMETER(self);
   4338 }
   4339 
   4340 
   4341 
   4342 // This is the list of built-in MPE types
   4343 static _cmsTagTypeLinkedList SupportedMPEtypes[] = {
   4344 
   4345 {{ (cmsTagTypeSignature) cmsSigBAcsElemType, NULL, NULL, NULL, NULL, NULL, 0 }, &SupportedMPEtypes[1] },   // Ignore those elements for now
   4346 {{ (cmsTagTypeSignature) cmsSigEAcsElemType, NULL, NULL, NULL, NULL, NULL, 0 }, &SupportedMPEtypes[2] },   // (That's what the spec says)
   4347 
   4348 {TYPE_MPE_HANDLER((cmsTagTypeSignature) cmsSigCurveSetElemType,     MPEcurve),      &SupportedMPEtypes[3] },
   4349 {TYPE_MPE_HANDLER((cmsTagTypeSignature) cmsSigMatrixElemType,       MPEmatrix),     &SupportedMPEtypes[4] },
   4350 {TYPE_MPE_HANDLER((cmsTagTypeSignature) cmsSigCLutElemType,         MPEclut),        NULL },
   4351 };
   4352 
   4353 _cmsTagTypePluginChunkType _cmsMPETypePluginChunk = { NULL };
   4354 
   4355 static
   4356 cmsBool ReadMPEElem(struct _cms_typehandler_struct* self,
   4357                     cmsIOHANDLER* io,
   4358                     void* Cargo,
   4359                     cmsUInt32Number n,
   4360                     cmsUInt32Number SizeOfTag)
   4361 {
   4362     cmsStageSignature ElementSig;
   4363     cmsTagTypeHandler* TypeHandler;
   4364     cmsUInt32Number nItems;
   4365     cmsPipeline *NewLUT = (cmsPipeline *) Cargo;
   4366     _cmsTagTypePluginChunkType* MPETypePluginChunk  = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(self->ContextID, MPEPlugin);
   4367 
   4368 
   4369     // Take signature and channels for each element.
   4370     if (!_cmsReadUInt32Number(io, (cmsUInt32Number*) &ElementSig)) return FALSE;
   4371 
   4372     // The reserved placeholder
   4373     if (!_cmsReadUInt32Number(io, NULL)) return FALSE;
   4374 
   4375     // Read diverse MPE types
   4376     TypeHandler = GetHandler((cmsTagTypeSignature) ElementSig, MPETypePluginChunk ->TagTypes, SupportedMPEtypes);
   4377     if (TypeHandler == NULL)  {
   4378 
   4379         char String[5];
   4380 
   4381         _cmsTagSignature2String(String, (cmsTagSignature) ElementSig);
   4382 
   4383         // An unknown element was found.
   4384         cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown MPE type '%s' found.", String);
   4385         return FALSE;
   4386     }
   4387 
   4388     // If no read method, just ignore the element (valid for cmsSigBAcsElemType and cmsSigEAcsElemType)
   4389     // Read the MPE. No size is given
   4390     if (TypeHandler ->ReadPtr != NULL) {
   4391 
   4392         // This is a real element which should be read and processed
   4393         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, (cmsStage*) TypeHandler ->ReadPtr(self, io, &nItems, SizeOfTag)))
   4394             return FALSE;
   4395     }
   4396 
   4397     return TRUE;
   4398 
   4399     cmsUNUSED_PARAMETER(SizeOfTag);
   4400     cmsUNUSED_PARAMETER(n);
   4401 }
   4402 
   4403 
   4404 // This is the main dispatcher for MPE
   4405 static
   4406 void *Type_MPE_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
   4407 {
   4408     cmsUInt16Number InputChans, OutputChans;
   4409     cmsUInt32Number ElementCount;
   4410     cmsPipeline *NewLUT = NULL;
   4411     cmsUInt32Number BaseOffset;
   4412 
   4413     // Get actual position as a basis for element offsets
   4414     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
   4415 
   4416     // Read channels and element count
   4417     if (!_cmsReadUInt16Number(io, &InputChans)) return NULL;
   4418     if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL;
   4419 
   4420     // Allocates an empty LUT
   4421     NewLUT = cmsPipelineAlloc(self ->ContextID, InputChans, OutputChans);
   4422     if (NewLUT == NULL) return NULL;
   4423 
   4424     if (!_cmsReadUInt32Number(io, &ElementCount)) return NULL;
   4425 
   4426     if (!ReadPositionTable(self, io, ElementCount, BaseOffset, NewLUT, ReadMPEElem)) {
   4427         if (NewLUT != NULL) cmsPipelineFree(NewLUT);
   4428         *nItems = 0;
   4429         return NULL;
   4430     }
   4431 
   4432     // Success
   4433     *nItems = 1;
   4434     return NewLUT;
   4435 
   4436     cmsUNUSED_PARAMETER(SizeOfTag);
   4437 }
   4438 
   4439 
   4440 
   4441 // This one is a liitle bit more complex, so we don't use position tables this time.
   4442 static
   4443 cmsBool Type_MPE_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
   4444 {
   4445     cmsUInt32Number i, BaseOffset, DirectoryPos, CurrentPos;
   4446     int inputChan, outputChan;
   4447     cmsUInt32Number ElemCount;
   4448     cmsUInt32Number *ElementOffsets = NULL, *ElementSizes = NULL, Before;
   4449     cmsStageSignature ElementSig;
   4450     cmsPipeline* Lut = (cmsPipeline*) Ptr;
   4451     cmsStage* Elem = Lut ->Elements;
   4452     cmsTagTypeHandler* TypeHandler;
   4453     _cmsTagTypePluginChunkType* MPETypePluginChunk  = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(self->ContextID, MPEPlugin);
   4454 
   4455     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
   4456 
   4457     inputChan  = cmsPipelineInputChannels(Lut);
   4458     outputChan = cmsPipelineOutputChannels(Lut);
   4459     ElemCount  = cmsPipelineStageCount(Lut);
   4460 
   4461     ElementOffsets = (cmsUInt32Number *) _cmsCalloc(self ->ContextID, ElemCount, sizeof(cmsUInt32Number));
   4462     if (ElementOffsets == NULL) goto Error;
   4463 
   4464     ElementSizes = (cmsUInt32Number *) _cmsCalloc(self ->ContextID, ElemCount, sizeof(cmsUInt32Number));
   4465     if (ElementSizes == NULL) goto Error;
   4466 
   4467     // Write the head
   4468     if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) inputChan)) goto Error;
   4469     if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) outputChan)) goto Error;
   4470     if (!_cmsWriteUInt32Number(io, (cmsUInt16Number) ElemCount)) goto Error;
   4471 
   4472     DirectoryPos = io ->Tell(io);
   4473 
   4474     // Write a fake directory to be filled latter on
   4475     for (i=0; i < ElemCount; i++) {
   4476         if (!_cmsWriteUInt32Number(io, 0)) goto Error;  // Offset
   4477         if (!_cmsWriteUInt32Number(io, 0)) goto Error;  // size
   4478     }
   4479 
   4480     // Write each single tag. Keep track of the size as well.
   4481     for (i=0; i < ElemCount; i++) {
   4482 
   4483         ElementOffsets[i] = io ->Tell(io) - BaseOffset;
   4484 
   4485         ElementSig = Elem ->Type;
   4486 
   4487         TypeHandler = GetHandler((cmsTagTypeSignature) ElementSig, MPETypePluginChunk->TagTypes, SupportedMPEtypes);
   4488         if (TypeHandler == NULL)  {
   4489 
   4490                 char String[5];
   4491 
   4492                 _cmsTagSignature2String(String, (cmsTagSignature) ElementSig);
   4493 
   4494                  // An unknow element was found.
   4495                  cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Found unknown MPE type '%s'", String);
   4496                  goto Error;
   4497         }
   4498 
   4499         if (!_cmsWriteUInt32Number(io, ElementSig)) goto Error;
   4500         if (!_cmsWriteUInt32Number(io, 0)) goto Error;
   4501         Before = io ->Tell(io);
   4502         if (!TypeHandler ->WritePtr(self, io, Elem, 1)) goto Error;
   4503         if (!_cmsWriteAlignment(io)) goto Error;
   4504 
   4505         ElementSizes[i] = io ->Tell(io) - Before;
   4506 
   4507         Elem = Elem ->Next;
   4508     }
   4509 
   4510     // Write the directory
   4511     CurrentPos = io ->Tell(io);
   4512 
   4513     if (!io ->Seek(io, DirectoryPos)) goto Error;
   4514 
   4515     for (i=0; i < ElemCount; i++) {
   4516         if (!_cmsWriteUInt32Number(io, ElementOffsets[i])) goto Error;
   4517         if (!_cmsWriteUInt32Number(io, ElementSizes[i])) goto Error;
   4518     }
   4519 
   4520     if (!io ->Seek(io, CurrentPos)) goto Error;
   4521 
   4522     if (ElementOffsets != NULL) _cmsFree(self ->ContextID, ElementOffsets);
   4523     if (ElementSizes != NULL) _cmsFree(self ->ContextID, ElementSizes);
   4524     return TRUE;
   4525 
   4526 Error:
   4527     if (ElementOffsets != NULL) _cmsFree(self ->ContextID, ElementOffsets);
   4528     if (ElementSizes != NULL) _cmsFree(self ->ContextID, ElementSizes);
   4529     return FALSE;
   4530 
   4531     cmsUNUSED_PARAMETER(nItems);
   4532 }
   4533 
   4534 
   4535 static
   4536 void* Type_MPE_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
   4537 {
   4538     return (void*) cmsPipelineDup((cmsPipeline*) Ptr);
   4539 
   4540     cmsUNUSED_PARAMETER(n);
   4541     cmsUNUSED_PARAMETER(self);
   4542 }
   4543 
   4544 static
   4545 void Type_MPE_Free(struct _cms_typehandler_struct* self, void *Ptr)
   4546 {
   4547     cmsPipelineFree((cmsPipeline*) Ptr);
   4548     return;
   4549 
   4550     cmsUNUSED_PARAMETER(self);
   4551 }
   4552 
   4553 
   4554 // ********************************************************************************
   4555 // Type cmsSigVcgtType
   4556 // ********************************************************************************
   4557 
   4558 
   4559 #define cmsVideoCardGammaTableType    0
   4560 #define cmsVideoCardGammaFormulaType  1
   4561 
   4562 // Used internally
   4563 typedef struct {
   4564     double Gamma;
   4565     double Min;
   4566     double Max;
   4567 } _cmsVCGTGAMMA;
   4568 
   4569 
   4570 static
   4571 void *Type_vcgt_Read(struct _cms_typehandler_struct* self,
   4572                      cmsIOHANDLER* io,
   4573                      cmsUInt32Number* nItems,
   4574                      cmsUInt32Number SizeOfTag)
   4575 {
   4576     cmsUInt32Number TagType, n, i;
   4577     cmsToneCurve** Curves;
   4578 
   4579     *nItems = 0;
   4580 
   4581     // Read tag type
   4582     if (!_cmsReadUInt32Number(io, &TagType)) return NULL;
   4583 
   4584     // Allocate space for the array
   4585     Curves = ( cmsToneCurve**) _cmsCalloc(self ->ContextID, 3, sizeof(cmsToneCurve*));
   4586     if (Curves == NULL) return NULL;
   4587 
   4588     // There are two possible flavors
   4589     switch (TagType) {
   4590 
   4591     // Gamma is stored as a table
   4592     case cmsVideoCardGammaTableType:
   4593     {
   4594        cmsUInt16Number nChannels, nElems, nBytes;
   4595 
   4596        // Check channel count, which should be 3 (we don't support monochrome this time)
   4597        if (!_cmsReadUInt16Number(io, &nChannels)) goto Error;
   4598 
   4599        if (nChannels != 3) {
   4600            cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported number of channels for VCGT '%d'", nChannels);
   4601            goto Error;
   4602        }
   4603 
   4604        // Get Table element count and bytes per element
   4605        if (!_cmsReadUInt16Number(io, &nElems)) goto Error;
   4606        if (!_cmsReadUInt16Number(io, &nBytes)) goto Error;
   4607 
   4608        // Adobe's quirk fixup. Fixing broken profiles...
   4609        if (nElems == 256 && nBytes == 1 && SizeOfTag == 1576)
   4610            nBytes = 2;
   4611 
   4612 
   4613        // Populate tone curves
   4614        for (n=0; n < 3; n++) {
   4615 
   4616            Curves[n] = cmsBuildTabulatedToneCurve16(self ->ContextID, nElems, NULL);
   4617            if (Curves[n] == NULL) goto Error;
   4618 
   4619            // On depending on byte depth
   4620            switch (nBytes) {
   4621 
   4622            // One byte, 0..255
   4623            case 1:
   4624                for (i=0; i < nElems; i++) {
   4625 
   4626                    cmsUInt8Number v;
   4627 
   4628                       if (!_cmsReadUInt8Number(io, &v)) goto Error;
   4629                       Curves[n] ->Table16[i] = FROM_8_TO_16(v);
   4630                }
   4631                break;
   4632 
   4633            // One word 0..65535
   4634            case 2:
   4635               if (!_cmsReadUInt16Array(io, nElems, Curves[n]->Table16)) goto Error;
   4636               break;
   4637 
   4638           // Unsupported
   4639            default:
   4640               cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported bit depth for VCGT '%d'", nBytes * 8);
   4641               goto Error;
   4642            }
   4643        } // For all 3 channels
   4644     }
   4645     break;
   4646 
   4647    // In this case, gamma is stored as a formula
   4648    case cmsVideoCardGammaFormulaType:
   4649    {
   4650        _cmsVCGTGAMMA Colorant[3];
   4651 
   4652         // Populate tone curves
   4653        for (n=0; n < 3; n++) {
   4654 
   4655            double Params[10];
   4656 
   4657            if (!_cmsRead15Fixed16Number(io, &Colorant[n].Gamma)) goto Error;
   4658            if (!_cmsRead15Fixed16Number(io, &Colorant[n].Min)) goto Error;
   4659            if (!_cmsRead15Fixed16Number(io, &Colorant[n].Max)) goto Error;
   4660 
   4661             // Parametric curve type 5 is:
   4662             // Y = (aX + b)^Gamma + e | X >= d
   4663             // Y = cX + f             | X < d
   4664 
   4665             // vcgt formula is:
   4666             // Y = (Max ?Min) * (X ^ Gamma) + Min
   4667 
   4668             // So, the translation is
   4669             // a = (Max ?Min) ^ ( 1 / Gamma)
   4670             // e = Min
   4671             // b=c=d=f=0
   4672 
   4673            Params[0] = Colorant[n].Gamma;
   4674            Params[1] = pow((Colorant[n].Max - Colorant[n].Min), (1.0 / Colorant[n].Gamma));
   4675            Params[2] = 0;
   4676            Params[3] = 0;
   4677            Params[4] = 0;
   4678            Params[5] = Colorant[n].Min;
   4679            Params[6] = 0;
   4680 
   4681            Curves[n] = cmsBuildParametricToneCurve(self ->ContextID, 5, Params);
   4682            if (Curves[n] == NULL) goto Error;
   4683        }
   4684    }
   4685    break;
   4686 
   4687    // Unsupported
   4688    default:
   4689       cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported tag type for VCGT '%d'", TagType);
   4690       goto Error;
   4691    }
   4692 
   4693    *nItems = 1;
   4694    return (void*) Curves;
   4695 
   4696 // Regret,  free all resources
   4697 Error:
   4698 
   4699     cmsFreeToneCurveTriple(Curves);
   4700     _cmsFree(self ->ContextID, Curves);
   4701     return NULL;
   4702 
   4703      cmsUNUSED_PARAMETER(SizeOfTag);
   4704 }
   4705 
   4706 
   4707 // We don't support all flavors, only 16bits tables and formula
   4708 static
   4709 cmsBool Type_vcgt_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
   4710 {
   4711     cmsToneCurve** Curves =  (cmsToneCurve**) Ptr;
   4712     cmsUInt32Number i, j;
   4713 
   4714     if (cmsGetToneCurveParametricType(Curves[0]) == 5 &&
   4715         cmsGetToneCurveParametricType(Curves[1]) == 5 &&
   4716         cmsGetToneCurveParametricType(Curves[2]) == 5) {
   4717 
   4718             if (!_cmsWriteUInt32Number(io, cmsVideoCardGammaFormulaType)) return FALSE;
   4719 
   4720             // Save parameters
   4721             for (i=0; i < 3; i++) {
   4722 
   4723                 _cmsVCGTGAMMA v;
   4724 
   4725                 v.Gamma = Curves[i] ->Segments[0].Params[0];
   4726                 v.Min   = Curves[i] ->Segments[0].Params[5];
   4727                 v.Max   = pow(Curves[i] ->Segments[0].Params[1], v.Gamma) + v.Min;
   4728 
   4729                 if (!_cmsWrite15Fixed16Number(io, v.Gamma)) return FALSE;
   4730                 if (!_cmsWrite15Fixed16Number(io, v.Min)) return FALSE;
   4731                 if (!_cmsWrite15Fixed16Number(io, v.Max)) return FALSE;
   4732             }
   4733     }
   4734 
   4735     else {
   4736 
   4737         // Always store as a table of 256 words
   4738         if (!_cmsWriteUInt32Number(io, cmsVideoCardGammaTableType)) return FALSE;
   4739         if (!_cmsWriteUInt16Number(io, 3)) return FALSE;
   4740         if (!_cmsWriteUInt16Number(io, 256)) return FALSE;
   4741         if (!_cmsWriteUInt16Number(io, 2)) return FALSE;
   4742 
   4743         for (i=0; i < 3; i++) {
   4744             for (j=0; j < 256; j++) {
   4745 
   4746                 cmsFloat32Number v = cmsEvalToneCurveFloat(Curves[i], (cmsFloat32Number) (j / 255.0));
   4747                 cmsUInt16Number  n = _cmsQuickSaturateWord(v * 65535.0);
   4748 
   4749                 if (!_cmsWriteUInt16Number(io, n)) return FALSE;
   4750             }
   4751         }
   4752     }
   4753 
   4754     return TRUE;
   4755 
   4756     cmsUNUSED_PARAMETER(self);
   4757     cmsUNUSED_PARAMETER(nItems);
   4758 }
   4759 
   4760 static
   4761 void* Type_vcgt_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
   4762 {
   4763     cmsToneCurve** OldCurves =  (cmsToneCurve**) Ptr;
   4764     cmsToneCurve** NewCurves;
   4765 
   4766     NewCurves = ( cmsToneCurve**) _cmsCalloc(self ->ContextID, 3, sizeof(cmsToneCurve*));
   4767     if (NewCurves == NULL) return NULL;
   4768 
   4769     NewCurves[0] = cmsDupToneCurve(OldCurves[0]);
   4770     NewCurves[1] = cmsDupToneCurve(OldCurves[1]);
   4771     NewCurves[2] = cmsDupToneCurve(OldCurves[2]);
   4772 
   4773     return (void*) NewCurves;
   4774 
   4775     cmsUNUSED_PARAMETER(n);
   4776 }
   4777 
   4778 
   4779 static
   4780 void Type_vcgt_Free(struct _cms_typehandler_struct* self, void* Ptr)
   4781 {
   4782     cmsFreeToneCurveTriple((cmsToneCurve**) Ptr);
   4783     _cmsFree(self ->ContextID, Ptr);
   4784 }
   4785 
   4786 
   4787 // ********************************************************************************
   4788 // Type cmsSigDictType
   4789 // ********************************************************************************
   4790 
   4791 // Single column of the table can point to wchar or MLUC elements. Holds arrays of data
   4792 typedef struct {
   4793     cmsContext ContextID;
   4794     cmsUInt32Number *Offsets;
   4795     cmsUInt32Number *Sizes;
   4796 } _cmsDICelem;
   4797 
   4798 typedef struct {
   4799     _cmsDICelem Name, Value, DisplayName, DisplayValue;
   4800 
   4801 } _cmsDICarray;
   4802 
   4803 // Allocate an empty array element
   4804 static
   4805 cmsBool AllocElem(cmsContext ContextID, _cmsDICelem* e,  cmsUInt32Number Count)
   4806 {
   4807     e->Offsets = (cmsUInt32Number *) _cmsCalloc(ContextID, Count, sizeof(cmsUInt32Number));
   4808     if (e->Offsets == NULL) return FALSE;
   4809 
   4810     e->Sizes = (cmsUInt32Number *) _cmsCalloc(ContextID, Count, sizeof(cmsUInt32Number));
   4811     if (e->Sizes == NULL) {
   4812 
   4813         _cmsFree(ContextID, e -> Offsets);
   4814         return FALSE;
   4815     }
   4816 
   4817     e ->ContextID = ContextID;
   4818     return TRUE;
   4819 }
   4820 
   4821 // Free an array element
   4822 static
   4823 void FreeElem(_cmsDICelem* e)
   4824 {
   4825     if (e ->Offsets != NULL)  _cmsFree(e -> ContextID, e -> Offsets);
   4826     if (e ->Sizes   != NULL)  _cmsFree(e -> ContextID, e -> Sizes);
   4827     e->Offsets = e ->Sizes = NULL;
   4828 }
   4829 
   4830 // Get rid of whole array
   4831 static
   4832 void FreeArray( _cmsDICarray* a)
   4833 {
   4834     if (a ->Name.Offsets != NULL) FreeElem(&a->Name);
   4835     if (a ->Value.Offsets != NULL) FreeElem(&a ->Value);
   4836     if (a ->DisplayName.Offsets != NULL) FreeElem(&a->DisplayName);
   4837     if (a ->DisplayValue.Offsets != NULL) FreeElem(&a ->DisplayValue);
   4838 }
   4839 
   4840 
   4841 // Allocate whole array
   4842 static
   4843 cmsBool AllocArray(cmsContext ContextID, _cmsDICarray* a, cmsUInt32Number Count, cmsUInt32Number Length)
   4844 {
   4845     // Empty values
   4846     memset(a, 0, sizeof(_cmsDICarray));
   4847 
   4848     // On depending on record size, create column arrays
   4849     if (!AllocElem(ContextID, &a ->Name, Count)) goto Error;
   4850     if (!AllocElem(ContextID, &a ->Value, Count)) goto Error;
   4851 
   4852     if (Length > 16) {
   4853         if (!AllocElem(ContextID, &a -> DisplayName, Count)) goto Error;
   4854 
   4855     }
   4856     if (Length > 24) {
   4857         if (!AllocElem(ContextID, &a ->DisplayValue, Count)) goto Error;
   4858     }
   4859     return TRUE;
   4860 
   4861 Error:
   4862     FreeArray(a);
   4863     return FALSE;
   4864 }
   4865 
   4866 // Read one element
   4867 static
   4868 cmsBool ReadOneElem(cmsIOHANDLER* io,  _cmsDICelem* e, cmsUInt32Number i, cmsUInt32Number BaseOffset)
   4869 {
   4870     if (!_cmsReadUInt32Number(io, &e->Offsets[i])) return FALSE;
   4871     if (!_cmsReadUInt32Number(io, &e ->Sizes[i])) return FALSE;
   4872 
   4873     // An offset of zero has special meaning and shal be preserved
   4874     if (e ->Offsets[i] > 0)
   4875         e ->Offsets[i] += BaseOffset;
   4876     return TRUE;
   4877 }
   4878 
   4879 
   4880 static
   4881 cmsBool ReadOffsetArray(cmsIOHANDLER* io,  _cmsDICarray* a, cmsUInt32Number Count, cmsUInt32Number Length, cmsUInt32Number BaseOffset)
   4882 {
   4883     cmsUInt32Number i;
   4884 
   4885     // Read column arrays
   4886     for (i=0; i < Count; i++) {
   4887 
   4888         if (!ReadOneElem(io, &a -> Name, i, BaseOffset)) return FALSE;
   4889         if (!ReadOneElem(io, &a -> Value, i, BaseOffset)) return FALSE;
   4890 
   4891         if (Length > 16) {
   4892 
   4893             if (!ReadOneElem(io, &a ->DisplayName, i, BaseOffset)) return FALSE;
   4894 
   4895         }
   4896 
   4897         if (Length > 24) {
   4898 
   4899             if (!ReadOneElem(io, & a -> DisplayValue, i, BaseOffset)) return FALSE;
   4900         }
   4901     }
   4902     return TRUE;
   4903 }
   4904 
   4905 
   4906 // Write one element
   4907 static
   4908 cmsBool WriteOneElem(cmsIOHANDLER* io,  _cmsDICelem* e, cmsUInt32Number i)
   4909 {
   4910     if (!_cmsWriteUInt32Number(io, e->Offsets[i])) return FALSE;
   4911     if (!_cmsWriteUInt32Number(io, e ->Sizes[i])) return FALSE;
   4912 
   4913     return TRUE;
   4914 }
   4915 
   4916 static
   4917 cmsBool WriteOffsetArray(cmsIOHANDLER* io,  _cmsDICarray* a, cmsUInt32Number Count, cmsUInt32Number Length)
   4918 {
   4919     cmsUInt32Number i;
   4920 
   4921     for (i=0; i < Count; i++) {
   4922 
   4923         if (!WriteOneElem(io, &a -> Name, i)) return FALSE;
   4924         if (!WriteOneElem(io, &a -> Value, i))  return FALSE;
   4925 
   4926         if (Length > 16) {
   4927 
   4928             if (!WriteOneElem(io, &a -> DisplayName, i))  return FALSE;
   4929         }
   4930 
   4931         if (Length > 24) {
   4932 
   4933             if (!WriteOneElem(io, &a -> DisplayValue, i))  return FALSE;
   4934         }
   4935     }
   4936 
   4937     return TRUE;
   4938 }
   4939 
   4940 static
   4941 cmsBool ReadOneWChar(cmsIOHANDLER* io,  _cmsDICelem* e, cmsUInt32Number i, wchar_t ** wcstr)
   4942 {
   4943 
   4944     cmsUInt32Number nChars;
   4945 
   4946       // Special case for undefined strings (see ICC Votable
   4947       // Proposal Submission, Dictionary Type and Metadata TAG Definition)
   4948       if (e -> Offsets[i] == 0) {
   4949 
   4950           *wcstr = NULL;
   4951           return TRUE;
   4952       }
   4953 
   4954       if (!io -> Seek(io, e -> Offsets[i])) return FALSE;
   4955 
   4956       nChars = e ->Sizes[i] / sizeof(cmsUInt16Number);
   4957 
   4958 
   4959       *wcstr = (wchar_t*) _cmsMallocZero(e ->ContextID, (nChars + 1) * sizeof(wchar_t));
   4960       if (*wcstr == NULL) return FALSE;
   4961 
   4962       if (!_cmsReadWCharArray(io, nChars, *wcstr)) {
   4963           _cmsFree(e ->ContextID, *wcstr);
   4964           return FALSE;
   4965       }
   4966 
   4967       // End of string marker
   4968       (*wcstr)[nChars] = 0;
   4969       return TRUE;
   4970 }
   4971 
   4972 static
   4973 cmsUInt32Number mywcslen(const wchar_t *s)
   4974 {
   4975     const wchar_t *p;
   4976 
   4977     p = s;
   4978     while (*p)
   4979         p++;
   4980 
   4981     return (cmsUInt32Number)(p - s);
   4982 }
   4983 
   4984 static
   4985 cmsBool WriteOneWChar(cmsIOHANDLER* io,  _cmsDICelem* e, cmsUInt32Number i, const wchar_t * wcstr, cmsUInt32Number BaseOffset)
   4986 {
   4987     cmsUInt32Number Before = io ->Tell(io);
   4988     cmsUInt32Number n;
   4989 
   4990     e ->Offsets[i] = Before - BaseOffset;
   4991 
   4992     if (wcstr == NULL) {
   4993         e ->Sizes[i] = 0;
   4994         e ->Offsets[i] = 0;
   4995         return TRUE;
   4996     }
   4997 
   4998     n = mywcslen(wcstr);
   4999     if (!_cmsWriteWCharArray(io,  n, wcstr)) return FALSE;
   5000 
   5001     e ->Sizes[i] = io ->Tell(io) - Before;
   5002     return TRUE;
   5003 }
   5004 
   5005 static
   5006 cmsBool ReadOneMLUC(struct _cms_typehandler_struct* self, cmsIOHANDLER* io,  _cmsDICelem* e, cmsUInt32Number i, cmsMLU** mlu)
   5007 {
   5008     cmsUInt32Number nItems = 0;
   5009 
   5010     // A way to get null MLUCs
   5011     if (e -> Offsets[i] == 0 || e ->Sizes[i] == 0) {
   5012 
   5013         *mlu = NULL;
   5014         return TRUE;
   5015     }
   5016 
   5017     if (!io -> Seek(io, e -> Offsets[i])) return FALSE;
   5018 
   5019     *mlu = (cmsMLU*) Type_MLU_Read(self, io, &nItems, e ->Sizes[i]);
   5020     return *mlu != NULL;
   5021 }
   5022 
   5023 static
   5024 cmsBool WriteOneMLUC(struct _cms_typehandler_struct* self, cmsIOHANDLER* io,  _cmsDICelem* e, cmsUInt32Number i, const cmsMLU* mlu, cmsUInt32Number BaseOffset)
   5025 {
   5026     cmsUInt32Number Before;
   5027 
   5028      // Special case for undefined strings (see ICC Votable
   5029      // Proposal Submission, Dictionary Type and Metadata TAG Definition)
   5030      if (mlu == NULL) {
   5031         e ->Sizes[i] = 0;
   5032         e ->Offsets[i] = 0;
   5033         return TRUE;
   5034     }
   5035 
   5036     Before = io ->Tell(io);
   5037     e ->Offsets[i] = Before - BaseOffset;
   5038 
   5039     if (!Type_MLU_Write(self, io, (void*) mlu, 1)) return FALSE;
   5040 
   5041     e ->Sizes[i] = io ->Tell(io) - Before;
   5042     return TRUE;
   5043 }
   5044 
   5045 
   5046 static
   5047 void *Type_Dictionary_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
   5048 {
   5049    cmsHANDLE hDict;
   5050    cmsUInt32Number i, Count, Length;
   5051    cmsUInt32Number BaseOffset;
   5052    _cmsDICarray a;
   5053    wchar_t *NameWCS = NULL, *ValueWCS = NULL;
   5054    cmsMLU *DisplayNameMLU = NULL, *DisplayValueMLU=NULL;
   5055    cmsBool rc;
   5056 
   5057     *nItems = 0;
   5058 
   5059     // Get actual position as a basis for element offsets
   5060     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
   5061 
   5062     // Get name-value record count
   5063     if (!_cmsReadUInt32Number(io, &Count)) return NULL;
   5064     SizeOfTag -= sizeof(cmsUInt32Number);
   5065 
   5066     // Get rec length
   5067     if (!_cmsReadUInt32Number(io, &Length)) return NULL;
   5068     SizeOfTag -= sizeof(cmsUInt32Number);
   5069 
   5070     // Check for valid lengths
   5071     if (Length != 16 && Length != 24 && Length != 32) {
   5072          cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown record length in dictionary '%d'", Length);
   5073          return NULL;
   5074     }
   5075 
   5076     // Creates an empty dictionary
   5077     hDict = cmsDictAlloc(self -> ContextID);
   5078     if (hDict == NULL) return NULL;
   5079 
   5080     // On depending on record size, create column arrays
   5081     if (!AllocArray(self -> ContextID, &a, Count, Length)) goto Error;
   5082 
   5083     // Read column arrays
   5084     if (!ReadOffsetArray(io, &a, Count, Length, BaseOffset)) goto Error;
   5085 
   5086     // Seek to each element and read it
   5087     for (i=0; i < Count; i++) {
   5088 
   5089         if (!ReadOneWChar(io, &a.Name, i, &NameWCS)) goto Error;
   5090         if (!ReadOneWChar(io, &a.Value, i, &ValueWCS)) goto Error;
   5091 
   5092         if (Length > 16) {
   5093             if (!ReadOneMLUC(self, io, &a.DisplayName, i, &DisplayNameMLU)) goto Error;
   5094         }
   5095 
   5096         if (Length > 24) {
   5097             if (!ReadOneMLUC(self, io, &a.DisplayValue, i, &DisplayValueMLU)) goto Error;
   5098         }
   5099 
   5100         if (NameWCS == NULL || ValueWCS == NULL) {
   5101 
   5102             cmsSignalError(self->ContextID, cmsERROR_CORRUPTION_DETECTED, "Bad dictionary Name/Value");
   5103             rc = FALSE;
   5104         }
   5105         else {
   5106 
   5107             rc = cmsDictAddEntry(hDict, NameWCS, ValueWCS, DisplayNameMLU, DisplayValueMLU);
   5108         }
   5109 
   5110         if (NameWCS != NULL) _cmsFree(self ->ContextID, NameWCS);
   5111         if (ValueWCS != NULL) _cmsFree(self ->ContextID, ValueWCS);
   5112         if (DisplayNameMLU != NULL) cmsMLUfree(DisplayNameMLU);
   5113         if (DisplayValueMLU != NULL) cmsMLUfree(DisplayValueMLU);
   5114 
   5115         if (!rc) goto Error;
   5116     }
   5117 
   5118    FreeArray(&a);
   5119    *nItems = 1;
   5120    return (void*) hDict;
   5121 
   5122 Error:
   5123    FreeArray(&a);
   5124    cmsDictFree(hDict);
   5125    return NULL;
   5126 }
   5127 
   5128 
   5129 static
   5130 cmsBool Type_Dictionary_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
   5131 {
   5132     cmsHANDLE hDict = (cmsHANDLE) Ptr;
   5133     const cmsDICTentry* p;
   5134     cmsBool AnyName, AnyValue;
   5135     cmsUInt32Number i, Count, Length;
   5136     cmsUInt32Number DirectoryPos, CurrentPos, BaseOffset;
   5137    _cmsDICarray a;
   5138 
   5139     if (hDict == NULL) return FALSE;
   5140 
   5141     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
   5142 
   5143     // Let's inspect the dictionary
   5144     Count = 0; AnyName = FALSE; AnyValue = FALSE;
   5145     for (p = cmsDictGetEntryList(hDict); p != NULL; p = cmsDictNextEntry(p)) {
   5146 
   5147         if (p ->DisplayName != NULL) AnyName = TRUE;
   5148         if (p ->DisplayValue != NULL) AnyValue = TRUE;
   5149         Count++;
   5150     }
   5151 
   5152     Length = 16;
   5153     if (AnyName)  Length += 8;
   5154     if (AnyValue) Length += 8;
   5155 
   5156     if (!_cmsWriteUInt32Number(io, Count)) return FALSE;
   5157     if (!_cmsWriteUInt32Number(io, Length)) return FALSE;
   5158 
   5159     // Keep starting position of offsets table
   5160     DirectoryPos = io ->Tell(io);
   5161 
   5162     // Allocate offsets array
   5163     if (!AllocArray(self ->ContextID, &a, Count, Length)) goto Error;
   5164 
   5165     // Write a fake directory to be filled latter on
   5166     if (!WriteOffsetArray(io, &a, Count, Length)) goto Error;
   5167 
   5168     // Write each element. Keep track of the size as well.
   5169     p = cmsDictGetEntryList(hDict);
   5170     for (i=0; i < Count; i++) {
   5171 
   5172         if (!WriteOneWChar(io, &a.Name, i,  p ->Name, BaseOffset)) goto Error;
   5173         if (!WriteOneWChar(io, &a.Value, i, p ->Value, BaseOffset)) goto Error;
   5174 
   5175         if (p ->DisplayName != NULL) {
   5176             if (!WriteOneMLUC(self, io, &a.DisplayName, i, p ->DisplayName, BaseOffset)) goto Error;
   5177         }
   5178 
   5179         if (p ->DisplayValue != NULL) {
   5180             if (!WriteOneMLUC(self, io, &a.DisplayValue, i, p ->DisplayValue, BaseOffset)) goto Error;
   5181         }
   5182 
   5183        p = cmsDictNextEntry(p);
   5184     }
   5185 
   5186     // Write the directory
   5187     CurrentPos = io ->Tell(io);
   5188     if (!io ->Seek(io, DirectoryPos)) goto Error;
   5189 
   5190     if (!WriteOffsetArray(io, &a, Count, Length)) goto Error;
   5191 
   5192     if (!io ->Seek(io, CurrentPos)) goto Error;
   5193 
   5194     FreeArray(&a);
   5195     return TRUE;
   5196 
   5197 Error:
   5198     FreeArray(&a);
   5199     return FALSE;
   5200 
   5201     cmsUNUSED_PARAMETER(nItems);
   5202 }
   5203 
   5204 
   5205 static
   5206 void* Type_Dictionary_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
   5207 {
   5208     return (void*)  cmsDictDup((cmsHANDLE) Ptr);
   5209 
   5210     cmsUNUSED_PARAMETER(n);
   5211     cmsUNUSED_PARAMETER(self);
   5212 }
   5213 
   5214 
   5215 static
   5216 void Type_Dictionary_Free(struct _cms_typehandler_struct* self, void* Ptr)
   5217 {
   5218     cmsDictFree((cmsHANDLE) Ptr);
   5219     cmsUNUSED_PARAMETER(self);
   5220 }
   5221 
   5222 
   5223 // ********************************************************************************
   5224 // Type support main routines
   5225 // ********************************************************************************
   5226 
   5227 
   5228 // This is the list of built-in types
   5229 static _cmsTagTypeLinkedList SupportedTagTypes[] = {
   5230 
   5231 {TYPE_HANDLER(cmsSigChromaticityType,          Chromaticity),        &SupportedTagTypes[1] },
   5232 {TYPE_HANDLER(cmsSigColorantOrderType,         ColorantOrderType),   &SupportedTagTypes[2] },
   5233 {TYPE_HANDLER(cmsSigS15Fixed16ArrayType,       S15Fixed16),          &SupportedTagTypes[3] },
   5234 {TYPE_HANDLER(cmsSigU16Fixed16ArrayType,       U16Fixed16),          &SupportedTagTypes[4] },
   5235 {TYPE_HANDLER(cmsSigTextType,                  Text),                &SupportedTagTypes[5] },
   5236 {TYPE_HANDLER(cmsSigTextDescriptionType,       Text_Description),    &SupportedTagTypes[6] },
   5237 {TYPE_HANDLER(cmsSigCurveType,                 Curve),               &SupportedTagTypes[7] },
   5238 {TYPE_HANDLER(cmsSigParametricCurveType,       ParametricCurve),     &SupportedTagTypes[8] },
   5239 {TYPE_HANDLER(cmsSigDateTimeType,              DateTime),            &SupportedTagTypes[9] },
   5240 {TYPE_HANDLER(cmsSigLut8Type,                  LUT8),                &SupportedTagTypes[10] },
   5241 {TYPE_HANDLER(cmsSigLut16Type,                 LUT16),               &SupportedTagTypes[11] },
   5242 {TYPE_HANDLER(cmsSigColorantTableType,         ColorantTable),       &SupportedTagTypes[12] },
   5243 {TYPE_HANDLER(cmsSigNamedColor2Type,           NamedColor),          &SupportedTagTypes[13] },
   5244 {TYPE_HANDLER(cmsSigMultiLocalizedUnicodeType, MLU),                 &SupportedTagTypes[14] },
   5245 {TYPE_HANDLER(cmsSigProfileSequenceDescType,   ProfileSequenceDesc), &SupportedTagTypes[15] },
   5246 {TYPE_HANDLER(cmsSigSignatureType,             Signature),           &SupportedTagTypes[16] },
   5247 {TYPE_HANDLER(cmsSigMeasurementType,           Measurement),         &SupportedTagTypes[17] },
   5248 {TYPE_HANDLER(cmsSigDataType,                  Data),                &SupportedTagTypes[18] },
   5249 {TYPE_HANDLER(cmsSigLutAtoBType,               LUTA2B),              &SupportedTagTypes[19] },
   5250 {TYPE_HANDLER(cmsSigLutBtoAType,               LUTB2A),              &SupportedTagTypes[20] },
   5251 {TYPE_HANDLER(cmsSigUcrBgType,                 UcrBg),               &SupportedTagTypes[21] },
   5252 {TYPE_HANDLER(cmsSigCrdInfoType,               CrdInfo),             &SupportedTagTypes[22] },
   5253 {TYPE_HANDLER(cmsSigMultiProcessElementType,   MPE),                 &SupportedTagTypes[23] },
   5254 {TYPE_HANDLER(cmsSigScreeningType,             Screening),           &SupportedTagTypes[24] },
   5255 {TYPE_HANDLER(cmsSigViewingConditionsType,     ViewingConditions),   &SupportedTagTypes[25] },
   5256 {TYPE_HANDLER(cmsSigXYZType,                   XYZ),                 &SupportedTagTypes[26] },
   5257 {TYPE_HANDLER(cmsCorbisBrokenXYZtype,          XYZ),                 &SupportedTagTypes[27] },
   5258 {TYPE_HANDLER(cmsMonacoBrokenCurveType,        Curve),               &SupportedTagTypes[28] },
   5259 {TYPE_HANDLER(cmsSigProfileSequenceIdType,     ProfileSequenceId),   &SupportedTagTypes[29] },
   5260 {TYPE_HANDLER(cmsSigDictType,                  Dictionary),          &SupportedTagTypes[30] },
   5261 {TYPE_HANDLER(cmsSigVcgtType,                  vcgt),                NULL }
   5262 };
   5263 
   5264 
   5265 _cmsTagTypePluginChunkType _cmsTagTypePluginChunk = { NULL };
   5266 
   5267 
   5268 
   5269 // Duplicates the zone of memory used by the plug-in in the new context
   5270 static
   5271 void DupTagTypeList(struct _cmsContext_struct* ctx,
   5272                     const struct _cmsContext_struct* src,
   5273                     int loc)
   5274 {
   5275    _cmsTagTypePluginChunkType newHead = { NULL };
   5276    _cmsTagTypeLinkedList*  entry;
   5277    _cmsTagTypeLinkedList*  Anterior = NULL;
   5278    _cmsTagTypePluginChunkType* head = (_cmsTagTypePluginChunkType*) src->chunks[loc];
   5279 
   5280    // Walk the list copying all nodes
   5281    for (entry = head->TagTypes;
   5282        entry != NULL;
   5283        entry = entry ->Next) {
   5284 
   5285            _cmsTagTypeLinkedList *newEntry = ( _cmsTagTypeLinkedList *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTagTypeLinkedList));
   5286 
   5287            if (newEntry == NULL)
   5288                return;
   5289 
   5290            // We want to keep the linked list order, so this is a little bit tricky
   5291            newEntry -> Next = NULL;
   5292            if (Anterior)
   5293                Anterior -> Next = newEntry;
   5294 
   5295            Anterior = newEntry;
   5296 
   5297            if (newHead.TagTypes == NULL)
   5298                newHead.TagTypes = newEntry;
   5299    }
   5300 
   5301    ctx ->chunks[loc] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsTagTypePluginChunkType));
   5302 }
   5303 
   5304 
   5305 void _cmsAllocTagTypePluginChunk(struct _cmsContext_struct* ctx,
   5306                                  const struct _cmsContext_struct* src)
   5307 {
   5308     if (src != NULL) {
   5309 
   5310         // Duplicate the LIST
   5311         DupTagTypeList(ctx, src, TagTypePlugin);
   5312     }
   5313     else {
   5314         static _cmsTagTypePluginChunkType TagTypePluginChunk = { NULL };
   5315         ctx ->chunks[TagTypePlugin] = _cmsSubAllocDup(ctx ->MemPool, &TagTypePluginChunk, sizeof(_cmsTagTypePluginChunkType));
   5316     }
   5317 }
   5318 
   5319 void _cmsAllocMPETypePluginChunk(struct _cmsContext_struct* ctx,
   5320                                const struct _cmsContext_struct* src)
   5321 {
   5322     if (src != NULL) {
   5323 
   5324         // Duplicate the LIST
   5325         DupTagTypeList(ctx, src, MPEPlugin);
   5326     }
   5327     else {
   5328         static _cmsTagTypePluginChunkType TagTypePluginChunk = { NULL };
   5329         ctx ->chunks[MPEPlugin] = _cmsSubAllocDup(ctx ->MemPool, &TagTypePluginChunk, sizeof(_cmsTagTypePluginChunkType));
   5330     }
   5331 
   5332 }
   5333 
   5334 
   5335 // Both kind of plug-ins share same structure
   5336 cmsBool  _cmsRegisterTagTypePlugin(cmsContext id, cmsPluginBase* Data)
   5337 {
   5338     return RegisterTypesPlugin(id, Data, TagTypePlugin);
   5339 }
   5340 
   5341 cmsBool  _cmsRegisterMultiProcessElementPlugin(cmsContext id, cmsPluginBase* Data)
   5342 {
   5343     return RegisterTypesPlugin(id, Data,MPEPlugin);
   5344 }
   5345 
   5346 
   5347 // Wrapper for tag types
   5348 cmsTagTypeHandler* _cmsGetTagTypeHandler(cmsContext ContextID, cmsTagTypeSignature sig)
   5349 {
   5350     _cmsTagTypePluginChunkType* ctx = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(ContextID, TagTypePlugin);
   5351 
   5352     return GetHandler(sig, ctx->TagTypes, SupportedTagTypes);
   5353 }
   5354 
   5355 // ********************************************************************************
   5356 // Tag support main routines
   5357 // ********************************************************************************
   5358 
   5359 typedef struct _cmsTagLinkedList_st {
   5360 
   5361             cmsTagSignature Signature;
   5362             cmsTagDescriptor Descriptor;
   5363             struct _cmsTagLinkedList_st* Next;
   5364 
   5365 } _cmsTagLinkedList;
   5366 
   5367 // This is the list of built-in tags
   5368 static _cmsTagLinkedList SupportedTags[] = {
   5369 
   5370     { cmsSigAToB0Tag,               { 1, 3,  { cmsSigLut16Type,  cmsSigLutAtoBType, cmsSigLut8Type}, DecideLUTtypeA2B}, &SupportedTags[1]},
   5371     { cmsSigAToB1Tag,               { 1, 3,  { cmsSigLut16Type,  cmsSigLutAtoBType, cmsSigLut8Type}, DecideLUTtypeA2B}, &SupportedTags[2]},
   5372     { cmsSigAToB2Tag,               { 1, 3,  { cmsSigLut16Type,  cmsSigLutAtoBType, cmsSigLut8Type}, DecideLUTtypeA2B}, &SupportedTags[3]},
   5373     { cmsSigBToA0Tag,               { 1, 3,  { cmsSigLut16Type,  cmsSigLutBtoAType, cmsSigLut8Type}, DecideLUTtypeB2A}, &SupportedTags[4]},
   5374     { cmsSigBToA1Tag,               { 1, 3,  { cmsSigLut16Type,  cmsSigLutBtoAType, cmsSigLut8Type}, DecideLUTtypeB2A}, &SupportedTags[5]},
   5375     { cmsSigBToA2Tag,               { 1, 3,  { cmsSigLut16Type,  cmsSigLutBtoAType, cmsSigLut8Type}, DecideLUTtypeB2A}, &SupportedTags[6]},
   5376 
   5377     // Allow corbis  and its broken XYZ type
   5378     { cmsSigRedColorantTag,         { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, DecideXYZtype}, &SupportedTags[7]},
   5379     { cmsSigGreenColorantTag,       { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, DecideXYZtype}, &SupportedTags[8]},
   5380     { cmsSigBlueColorantTag,        { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, DecideXYZtype}, &SupportedTags[9]},
   5381 
   5382     { cmsSigRedTRCTag,              { 1, 3, { cmsSigCurveType, cmsSigParametricCurveType, cmsMonacoBrokenCurveType }, DecideCurveType}, &SupportedTags[10]},
   5383     { cmsSigGreenTRCTag,            { 1, 3, { cmsSigCurveType, cmsSigParametricCurveType, cmsMonacoBrokenCurveType }, DecideCurveType}, &SupportedTags[11]},
   5384     { cmsSigBlueTRCTag,             { 1, 3, { cmsSigCurveType, cmsSigParametricCurveType, cmsMonacoBrokenCurveType }, DecideCurveType}, &SupportedTags[12]},
   5385 
   5386     { cmsSigCalibrationDateTimeTag, { 1, 1, { cmsSigDateTimeType }, NULL}, &SupportedTags[13]},
   5387     { cmsSigCharTargetTag,          { 1, 1, { cmsSigTextType },     NULL}, &SupportedTags[14]},
   5388 
   5389     { cmsSigChromaticAdaptationTag, { 9, 1, { cmsSigS15Fixed16ArrayType }, NULL}, &SupportedTags[15]},
   5390     { cmsSigChromaticityTag,        { 1, 1, { cmsSigChromaticityType    }, NULL}, &SupportedTags[16]},
   5391     { cmsSigColorantOrderTag,       { 1, 1, { cmsSigColorantOrderType   }, NULL}, &SupportedTags[17]},
   5392     { cmsSigColorantTableTag,       { 1, 1, { cmsSigColorantTableType   }, NULL}, &SupportedTags[18]},
   5393     { cmsSigColorantTableOutTag,    { 1, 1, { cmsSigColorantTableType   }, NULL}, &SupportedTags[19]},
   5394 
   5395     { cmsSigCopyrightTag,           { 1, 3, { cmsSigTextType,  cmsSigMultiLocalizedUnicodeType, cmsSigTextDescriptionType}, DecideTextType}, &SupportedTags[20]},
   5396     { cmsSigDateTimeTag,            { 1, 1, { cmsSigDateTimeType }, NULL}, &SupportedTags[21]},
   5397 
   5398     { cmsSigDeviceMfgDescTag,       { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[22]},
   5399     { cmsSigDeviceModelDescTag,     { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[23]},
   5400 
   5401     { cmsSigGamutTag,               { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[24]},
   5402 
   5403     { cmsSigGrayTRCTag,             { 1, 2, { cmsSigCurveType, cmsSigParametricCurveType }, DecideCurveType}, &SupportedTags[25]},
   5404     { cmsSigLuminanceTag,           { 1, 1, { cmsSigXYZType }, NULL}, &SupportedTags[26]},
   5405 
   5406     { cmsSigMediaBlackPointTag,     { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, NULL}, &SupportedTags[27]},
   5407     { cmsSigMediaWhitePointTag,     { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, NULL}, &SupportedTags[28]},
   5408 
   5409     { cmsSigNamedColor2Tag,         { 1, 1, { cmsSigNamedColor2Type }, NULL}, &SupportedTags[29]},
   5410 
   5411     { cmsSigPreview0Tag,            { 1, 3,  { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[30]},
   5412     { cmsSigPreview1Tag,            { 1, 3,  { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[31]},
   5413     { cmsSigPreview2Tag,            { 1, 3,  { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[32]},
   5414 
   5415     { cmsSigProfileDescriptionTag,  { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[33]},
   5416     { cmsSigProfileSequenceDescTag, { 1, 1, { cmsSigProfileSequenceDescType }, NULL}, &SupportedTags[34]},
   5417     { cmsSigTechnologyTag,          { 1, 1, { cmsSigSignatureType }, NULL},  &SupportedTags[35]},
   5418 
   5419     { cmsSigColorimetricIntentImageStateTag,   { 1, 1, { cmsSigSignatureType }, NULL}, &SupportedTags[36]},
   5420     { cmsSigPerceptualRenderingIntentGamutTag, { 1, 1, { cmsSigSignatureType }, NULL}, &SupportedTags[37]},
   5421     { cmsSigSaturationRenderingIntentGamutTag, { 1, 1, { cmsSigSignatureType }, NULL}, &SupportedTags[38]},
   5422 
   5423     { cmsSigMeasurementTag,         { 1, 1, { cmsSigMeasurementType }, NULL}, &SupportedTags[39]},
   5424 
   5425     { cmsSigPs2CRD0Tag,             { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[40]},
   5426     { cmsSigPs2CRD1Tag,             { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[41]},
   5427     { cmsSigPs2CRD2Tag,             { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[42]},
   5428     { cmsSigPs2CRD3Tag,             { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[43]},
   5429     { cmsSigPs2CSATag,              { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[44]},
   5430     { cmsSigPs2RenderingIntentTag,  { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[45]},
   5431 
   5432     { cmsSigViewingCondDescTag,     { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[46]},
   5433 
   5434     { cmsSigUcrBgTag,               { 1, 1, { cmsSigUcrBgType}, NULL},    &SupportedTags[47]},
   5435     { cmsSigCrdInfoTag,             { 1, 1, { cmsSigCrdInfoType}, NULL},  &SupportedTags[48]},
   5436 
   5437     { cmsSigDToB0Tag,               { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[49]},
   5438     { cmsSigDToB1Tag,               { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[50]},
   5439     { cmsSigDToB2Tag,               { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[51]},
   5440     { cmsSigDToB3Tag,               { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[52]},
   5441     { cmsSigBToD0Tag,               { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[53]},
   5442     { cmsSigBToD1Tag,               { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[54]},
   5443     { cmsSigBToD2Tag,               { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[55]},
   5444     { cmsSigBToD3Tag,               { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[56]},
   5445 
   5446     { cmsSigScreeningDescTag,       { 1, 1, { cmsSigTextDescriptionType },    NULL}, &SupportedTags[57]},
   5447     { cmsSigViewingConditionsTag,   { 1, 1, { cmsSigViewingConditionsType },  NULL}, &SupportedTags[58]},
   5448 
   5449     { cmsSigScreeningTag,           { 1, 1, { cmsSigScreeningType},          NULL }, &SupportedTags[59]},
   5450     { cmsSigVcgtTag,                { 1, 1, { cmsSigVcgtType},               NULL }, &SupportedTags[60]},
   5451     { cmsSigMetaTag,                { 1, 1, { cmsSigDictType},               NULL }, &SupportedTags[61]},
   5452     { cmsSigProfileSequenceIdTag,   { 1, 1, { cmsSigProfileSequenceIdType},  NULL },  &SupportedTags[62]},
   5453     { cmsSigProfileDescriptionMLTag,{ 1, 1, { cmsSigMultiLocalizedUnicodeType}, NULL}, NULL}
   5454 
   5455 
   5456 };
   5457 
   5458 /*
   5459     Not supported                 Why
   5460     =======================       =========================================
   5461     cmsSigOutputResponseTag   ==> WARNING, POSSIBLE PATENT ON THIS SUBJECT!
   5462     cmsSigNamedColorTag       ==> Deprecated
   5463     cmsSigDataTag             ==> Ancient, unused
   5464     cmsSigDeviceSettingsTag   ==> Deprecated, useless
   5465 */
   5466 
   5467 
   5468 _cmsTagPluginChunkType _cmsTagPluginChunk = { NULL };
   5469 
   5470 
   5471 // Duplicates the zone of memory used by the plug-in in the new context
   5472 static
   5473 void DupTagList(struct _cmsContext_struct* ctx,
   5474                     const struct _cmsContext_struct* src)
   5475 {
   5476    _cmsTagPluginChunkType newHead = { NULL };
   5477    _cmsTagLinkedList*  entry;
   5478    _cmsTagLinkedList*  Anterior = NULL;
   5479    _cmsTagPluginChunkType* head = (_cmsTagPluginChunkType*) src->chunks[TagPlugin];
   5480 
   5481    // Walk the list copying all nodes
   5482    for (entry = head->Tag;
   5483        entry != NULL;
   5484        entry = entry ->Next) {
   5485 
   5486            _cmsTagLinkedList *newEntry = ( _cmsTagLinkedList *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTagLinkedList));
   5487 
   5488            if (newEntry == NULL)
   5489                return;
   5490 
   5491            // We want to keep the linked list order, so this is a little bit tricky
   5492            newEntry -> Next = NULL;
   5493            if (Anterior)
   5494                Anterior -> Next = newEntry;
   5495 
   5496            Anterior = newEntry;
   5497 
   5498            if (newHead.Tag == NULL)
   5499                newHead.Tag = newEntry;
   5500    }
   5501 
   5502    ctx ->chunks[TagPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsTagPluginChunkType));
   5503 }
   5504 
   5505 void _cmsAllocTagPluginChunk(struct _cmsContext_struct* ctx,
   5506                                  const struct _cmsContext_struct* src)
   5507 {
   5508     if (src != NULL) {
   5509 
   5510         DupTagList(ctx, src);
   5511     }
   5512     else {
   5513         static _cmsTagPluginChunkType TagPluginChunk = { NULL };
   5514         ctx ->chunks[TagPlugin] = _cmsSubAllocDup(ctx ->MemPool, &TagPluginChunk, sizeof(_cmsTagPluginChunkType));
   5515     }
   5516 
   5517 }
   5518 
   5519 cmsBool  _cmsRegisterTagPlugin(cmsContext id, cmsPluginBase* Data)
   5520 {
   5521     cmsPluginTag* Plugin = (cmsPluginTag*) Data;
   5522     _cmsTagLinkedList *pt;
   5523     _cmsTagPluginChunkType* TagPluginChunk = ( _cmsTagPluginChunkType*) _cmsContextGetClientChunk(id, TagPlugin);
   5524 
   5525     if (Data == NULL) {
   5526 
   5527         TagPluginChunk->Tag = NULL;
   5528         return TRUE;
   5529     }
   5530 
   5531     pt = (_cmsTagLinkedList*) _cmsPluginMalloc(id, sizeof(_cmsTagLinkedList));
   5532     if (pt == NULL) return FALSE;
   5533 
   5534     pt ->Signature  = Plugin ->Signature;
   5535     pt ->Descriptor = Plugin ->Descriptor;
   5536     pt ->Next       = TagPluginChunk ->Tag;
   5537 
   5538     TagPluginChunk ->Tag = pt;
   5539 
   5540     return TRUE;
   5541 }
   5542 
   5543 // Return a descriptor for a given tag or NULL
   5544 cmsTagDescriptor* _cmsGetTagDescriptor(cmsContext ContextID, cmsTagSignature sig)
   5545 {
   5546     _cmsTagLinkedList* pt;
   5547     _cmsTagPluginChunkType* TagPluginChunk = ( _cmsTagPluginChunkType*) _cmsContextGetClientChunk(ContextID, TagPlugin);
   5548 
   5549     for (pt = TagPluginChunk->Tag;
   5550              pt != NULL;
   5551              pt = pt ->Next) {
   5552 
   5553                 if (sig == pt -> Signature) return &pt ->Descriptor;
   5554     }
   5555 
   5556     for (pt = SupportedTags;
   5557             pt != NULL;
   5558             pt = pt ->Next) {
   5559 
   5560                 if (sig == pt -> Signature) return &pt ->Descriptor;
   5561     }
   5562 
   5563     return NULL;
   5564 }
   5565