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