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-2020 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 additons
 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 // Auxiliary to read an array of wchar_t
 167 static
 168 cmsBool _cmsReadWCharArray(cmsIOHANDLER* io, cmsUInt32Number n, wchar_t* Array)
 169 {
 170     cmsUInt32Number i;
 171     cmsUInt16Number tmp;
 172 
 173     _cmsAssert(io != NULL);
 174 
 175     for (i=0; i < n; i++) {
 176 
 177         if (Array != NULL) {
 178 
 179             if (!_cmsReadUInt16Number(io, &tmp)) return FALSE;
 180             Array[i] = (wchar_t) tmp;
 181         }
 182         else {
 183             if (!_cmsReadUInt16Number(io, NULL)) return FALSE;
 184         }
 185 
 186     }
 187     return TRUE;
 188 }
 189 
 190 // To deal with position tables
 191 typedef cmsBool (* PositionTableEntryFn)(struct _cms_typehandler_struct* self,
 192                                              cmsIOHANDLER* io,
 193                                              void* Cargo,
 194                                              cmsUInt32Number n,
 195                                              cmsUInt32Number SizeOfTag);
 196 
 197 // Helper function to deal with position tables as described in ICC spec 4.3
 198 // A table of n elements is read, where first comes n records containing offsets and sizes and
 199 // then a block containing the data itself. This allows to reuse same data in more than one entry
 200 static
 201 cmsBool ReadPositionTable(struct _cms_typehandler_struct* self,
 202                               cmsIOHANDLER* io,
 203                               cmsUInt32Number Count,
 204                               cmsUInt32Number BaseOffset,
 205                               void *Cargo,
 206                               PositionTableEntryFn ElementFn)
 207 {
 208     cmsUInt32Number i;
 209     cmsUInt32Number *ElementOffsets = NULL, *ElementSizes = NULL;
 210     cmsUInt32Number currentPosition;
 211 
 212     currentPosition = io->Tell(io);
 213 
 214     // Verify there is enough space left to read at least two cmsUInt32Number items for Count items.
 215     if (((io->ReportedSize - currentPosition) / (2 * sizeof(cmsUInt32Number))) < Count)
 216         return FALSE;
 217 
 218     // Let's take the offsets to each element
 219     ElementOffsets = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number));
 220     if (ElementOffsets == NULL) goto Error;
 221 
 222     ElementSizes = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number));
 223     if (ElementSizes == NULL) goto Error;
 224 
 225     for (i=0; i < Count; i++) {
 226 
 227         if (!_cmsReadUInt32Number(io, &ElementOffsets[i])) goto Error;
 228         if (!_cmsReadUInt32Number(io, &ElementSizes[i])) goto Error;
 229 
 230         ElementOffsets[i] += BaseOffset;
 231     }
 232 
 233     // Seek to each element and read it
 234     for (i=0; i < Count; i++) {
 235 
 236         if (!io -> Seek(io, ElementOffsets[i])) goto Error;
 237 
 238         // This is the reader callback
 239         if (!ElementFn(self, io, Cargo, i, ElementSizes[i])) goto Error;
 240     }
 241 
 242     // Success
 243     if (ElementOffsets != NULL) _cmsFree(io ->ContextID, ElementOffsets);
 244     if (ElementSizes != NULL) _cmsFree(io ->ContextID, ElementSizes);
 245     return TRUE;
 246 
 247 Error:
 248     if (ElementOffsets != NULL) _cmsFree(io ->ContextID, ElementOffsets);
 249     if (ElementSizes != NULL) _cmsFree(io ->ContextID, ElementSizes);
 250     return FALSE;
 251 }
 252 
 253 // Same as anterior, but for write position tables
 254 static
 255 cmsBool WritePositionTable(struct _cms_typehandler_struct* self,
 256                                cmsIOHANDLER* io,
 257                                cmsUInt32Number SizeOfTag,
 258                                cmsUInt32Number Count,
 259                                cmsUInt32Number BaseOffset,
 260                                void *Cargo,
 261                                PositionTableEntryFn ElementFn)
 262 {
 263     cmsUInt32Number i;
 264     cmsUInt32Number DirectoryPos, CurrentPos, Before;
 265     cmsUInt32Number *ElementOffsets = NULL, *ElementSizes = NULL;
 266 
 267      // Create table
 268     ElementOffsets = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number));
 269     if (ElementOffsets == NULL) goto Error;
 270 
 271     ElementSizes = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number));
 272     if (ElementSizes == NULL) goto Error;
 273 
 274     // Keep starting position of curve offsets
 275     DirectoryPos = io ->Tell(io);
 276 
 277     // Write a fake directory to be filled latter on
 278     for (i=0; i < Count; i++) {
 279 
 280         if (!_cmsWriteUInt32Number(io, 0)) goto Error;  // Offset
 281         if (!_cmsWriteUInt32Number(io, 0)) goto Error;  // size
 282     }
 283 
 284     // Write each element. Keep track of the size as well.
 285     for (i=0; i < Count; i++) {
 286 
 287         Before = io ->Tell(io);
 288         ElementOffsets[i] = Before - BaseOffset;
 289 
 290         // Callback to write...
 291         if (!ElementFn(self, io, Cargo, i, SizeOfTag)) goto Error;
 292 
 293         // Now the size
 294         ElementSizes[i] = io ->Tell(io) - Before;
 295     }
 296 
 297     // Write the directory
 298     CurrentPos = io ->Tell(io);
 299     if (!io ->Seek(io, DirectoryPos)) goto Error;
 300 
 301     for (i=0; i <  Count; i++) {
 302         if (!_cmsWriteUInt32Number(io, ElementOffsets[i])) goto Error;
 303         if (!_cmsWriteUInt32Number(io, ElementSizes[i])) goto Error;
 304     }
 305 
 306     if (!io ->Seek(io, CurrentPos)) goto Error;
 307 
 308     if (ElementOffsets != NULL) _cmsFree(io ->ContextID, ElementOffsets);
 309     if (ElementSizes != NULL) _cmsFree(io ->ContextID, ElementSizes);
 310     return TRUE;
 311 
 312 Error:
 313     if (ElementOffsets != NULL) _cmsFree(io ->ContextID, ElementOffsets);
 314     if (ElementSizes != NULL) _cmsFree(io ->ContextID, ElementSizes);
 315     return FALSE;
 316 }
 317 
 318 
 319 // ********************************************************************************
 320 // Type XYZ. Only one value is allowed
 321 // ********************************************************************************
 322 
 323 //The XYZType contains an array of three encoded values for the XYZ tristimulus
 324 //values. Tristimulus values must be non-negative. The signed encoding allows for
 325 //implementation optimizations by minimizing the number of fixed formats.
 326 
 327 
 328 static
 329 void *Type_XYZ_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
 330 {
 331     cmsCIEXYZ* xyz;
 332 
 333     *nItems = 0;
 334     xyz = (cmsCIEXYZ*) _cmsMallocZero(self ->ContextID, sizeof(cmsCIEXYZ));
 335     if (xyz == NULL) return NULL;
 336 
 337     if (!_cmsReadXYZNumber(io, xyz)) {
 338         _cmsFree(self ->ContextID, xyz);
 339         return NULL;
 340     }
 341 
 342     *nItems = 1;
 343     return (void*) xyz;
 344 
 345     cmsUNUSED_PARAMETER(SizeOfTag);
 346 }
 347 
 348 static
 349 cmsBool  Type_XYZ_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
 350 {
 351     return _cmsWriteXYZNumber(io, (cmsCIEXYZ*) Ptr);
 352 
 353     cmsUNUSED_PARAMETER(nItems);
 354     cmsUNUSED_PARAMETER(self);
 355 }
 356 
 357 static
 358 void* Type_XYZ_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
 359 {
 360     return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsCIEXYZ));
 361 
 362     cmsUNUSED_PARAMETER(n);
 363 }
 364 
 365 static
 366 void Type_XYZ_Free(struct _cms_typehandler_struct* self, void *Ptr)
 367 {
 368     _cmsFree(self ->ContextID, Ptr);
 369 }
 370 
 371 
 372 static
 373 cmsTagTypeSignature DecideXYZtype(cmsFloat64Number ICCVersion, const void *Data)
 374 {
 375     return cmsSigXYZType;
 376 
 377     cmsUNUSED_PARAMETER(ICCVersion);
 378     cmsUNUSED_PARAMETER(Data);
 379 }
 380 
 381 
 382 // ********************************************************************************
 383 // Type chromaticity. Only one value is allowed
 384 // ********************************************************************************
 385 // The chromaticity tag type provides basic chromaticity data and type of
 386 // phosphors or colorants of a monitor to applications and utilities.
 387 
 388 static
 389 void *Type_Chromaticity_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
 390 {
 391     cmsCIExyYTRIPLE* chrm;
 392     cmsUInt16Number nChans, Table;
 393 
 394     *nItems = 0;
 395     chrm =  (cmsCIExyYTRIPLE*) _cmsMallocZero(self ->ContextID, sizeof(cmsCIExyYTRIPLE));
 396     if (chrm == NULL) return NULL;
 397 
 398     if (!_cmsReadUInt16Number(io, &nChans)) goto Error;
 399 
 400     // Let's recover from a bug introduced in early versions of lcms1
 401     if (nChans == 0 && SizeOfTag == 32) {
 402 
 403         if (!_cmsReadUInt16Number(io, NULL)) goto Error;
 404         if (!_cmsReadUInt16Number(io, &nChans)) goto Error;
 405     }
 406 
 407     if (nChans != 3) goto Error;
 408 
 409     if (!_cmsReadUInt16Number(io, &Table)) goto Error;
 410 
 411     if (!_cmsRead15Fixed16Number(io, &chrm ->Red.x)) goto Error;
 412     if (!_cmsRead15Fixed16Number(io, &chrm ->Red.y)) goto Error;
 413 
 414     chrm ->Red.Y = 1.0;
 415 
 416     if (!_cmsRead15Fixed16Number(io, &chrm ->Green.x)) goto Error;
 417     if (!_cmsRead15Fixed16Number(io, &chrm ->Green.y)) goto Error;
 418 
 419     chrm ->Green.Y = 1.0;
 420 
 421     if (!_cmsRead15Fixed16Number(io, &chrm ->Blue.x)) goto Error;
 422     if (!_cmsRead15Fixed16Number(io, &chrm ->Blue.y)) goto Error;
 423 
 424     chrm ->Blue.Y = 1.0;
 425 
 426     *nItems = 1;
 427     return (void*) chrm;
 428 
 429 Error:
 430     _cmsFree(self ->ContextID, (void*) chrm);
 431     return NULL;
 432 
 433     cmsUNUSED_PARAMETER(SizeOfTag);
 434 }
 435 
 436 static
 437 cmsBool  SaveOneChromaticity(cmsFloat64Number x, cmsFloat64Number y, cmsIOHANDLER* io)
 438 {
 439     if (!_cmsWriteUInt32Number(io, (cmsUInt32Number) _cmsDoubleTo15Fixed16(x))) return FALSE;
 440     if (!_cmsWriteUInt32Number(io, (cmsUInt32Number) _cmsDoubleTo15Fixed16(y))) return FALSE;
 441 
 442     return TRUE;
 443 }
 444 
 445 static
 446 cmsBool  Type_Chromaticity_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
 447 {
 448     cmsCIExyYTRIPLE* chrm = (cmsCIExyYTRIPLE*) Ptr;
 449 
 450     if (!_cmsWriteUInt16Number(io, 3)) return FALSE;        // nChannels
 451     if (!_cmsWriteUInt16Number(io, 0)) return FALSE;        // Table
 452 
 453     if (!SaveOneChromaticity(chrm -> Red.x,   chrm -> Red.y, io)) return FALSE;
 454     if (!SaveOneChromaticity(chrm -> Green.x, chrm -> Green.y, io)) return FALSE;
 455     if (!SaveOneChromaticity(chrm -> Blue.x,  chrm -> Blue.y, io)) return FALSE;
 456 
 457     return TRUE;
 458 
 459     cmsUNUSED_PARAMETER(nItems);
 460     cmsUNUSED_PARAMETER(self);
 461 }
 462 
 463 static
 464 void* Type_Chromaticity_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
 465 {
 466     return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsCIExyYTRIPLE));
 467 
 468     cmsUNUSED_PARAMETER(n);
 469 }
 470 
 471 static
 472 void Type_Chromaticity_Free(struct _cms_typehandler_struct* self, void* Ptr)
 473 {
 474     _cmsFree(self ->ContextID, Ptr);
 475 }
 476 
 477 
 478 // ********************************************************************************
 479 // Type cmsSigColorantOrderType
 480 // ********************************************************************************
 481 
 482 // This is an optional tag which specifies the laydown order in which colorants will
 483 // be printed on an n-colorant device. The laydown order may be the same as the
 484 // channel generation order listed in the colorantTableTag or the channel order of a
 485 // colour space such as CMYK, in which case this tag is not needed. When this is not
 486 // the case (for example, ink-towers sometimes use the order KCMY), this tag may be
 487 // used to specify the laydown order of the colorants.
 488 
 489 
 490 static
 491 void *Type_ColorantOrderType_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
 492 {
 493     cmsUInt8Number* ColorantOrder;
 494     cmsUInt32Number Count;
 495 
 496     *nItems = 0;
 497     if (!_cmsReadUInt32Number(io, &Count)) return NULL;
 498     if (Count > cmsMAXCHANNELS) return NULL;
 499 
 500     ColorantOrder = (cmsUInt8Number*) _cmsCalloc(self ->ContextID, cmsMAXCHANNELS, sizeof(cmsUInt8Number));
 501     if (ColorantOrder == NULL) return NULL;
 502 
 503     // We use FF as end marker
 504     memset(ColorantOrder, 0xFF, cmsMAXCHANNELS * sizeof(cmsUInt8Number));
 505 
 506     if (io ->Read(io, ColorantOrder, sizeof(cmsUInt8Number), Count) != Count) {
 507 
 508         _cmsFree(self ->ContextID, (void*) ColorantOrder);
 509         return NULL;
 510     }
 511 
 512     *nItems = 1;
 513     return (void*) ColorantOrder;
 514 
 515     cmsUNUSED_PARAMETER(SizeOfTag);
 516 }
 517 
 518 static
 519 cmsBool Type_ColorantOrderType_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
 520 {
 521     cmsUInt8Number*  ColorantOrder = (cmsUInt8Number*) Ptr;
 522     cmsUInt32Number i, sz, Count;
 523 
 524     // Get the length
 525     for (Count=i=0; i < cmsMAXCHANNELS; i++) {
 526         if (ColorantOrder[i] != 0xFF) Count++;
 527     }
 528 
 529     if (!_cmsWriteUInt32Number(io, Count)) return FALSE;
 530 
 531     sz = Count * sizeof(cmsUInt8Number);
 532     if (!io -> Write(io, sz, ColorantOrder)) return FALSE;
 533 
 534     return TRUE;
 535 
 536     cmsUNUSED_PARAMETER(nItems);
 537     cmsUNUSED_PARAMETER(self);
 538 }
 539 
 540 static
 541 void* Type_ColorantOrderType_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
 542 {
 543     return _cmsDupMem(self ->ContextID, Ptr, cmsMAXCHANNELS * sizeof(cmsUInt8Number));
 544 
 545     cmsUNUSED_PARAMETER(n);
 546 }
 547 
 548 
 549 static
 550 void Type_ColorantOrderType_Free(struct _cms_typehandler_struct* self, void* Ptr)
 551 {
 552     _cmsFree(self ->ContextID, Ptr);
 553 }
 554 
 555 // ********************************************************************************
 556 // Type cmsSigS15Fixed16ArrayType
 557 // ********************************************************************************
 558 // This type represents an array of generic 4-byte/32-bit fixed point quantity.
 559 // The number of values is determined from the size of the tag.
 560 
 561 static
 562 void *Type_S15Fixed16_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
 563 {
 564     cmsFloat64Number*  array_double;
 565     cmsUInt32Number i, n;
 566 
 567     *nItems = 0;
 568     n = SizeOfTag / sizeof(cmsUInt32Number);
 569     array_double = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, n, sizeof(cmsFloat64Number));
 570     if (array_double == NULL) return NULL;
 571 
 572     for (i=0; i < n; i++) {
 573 
 574         if (!_cmsRead15Fixed16Number(io, &array_double[i])) {
 575 
 576             _cmsFree(self ->ContextID, array_double);
 577             return NULL;
 578         }
 579     }
 580 
 581     *nItems = n;
 582     return (void*) array_double;
 583 }
 584 
 585 static
 586 cmsBool Type_S15Fixed16_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
 587 {
 588     cmsFloat64Number* Value = (cmsFloat64Number*) Ptr;
 589     cmsUInt32Number i;
 590 
 591     for (i=0; i < nItems; i++) {
 592 
 593         if (!_cmsWrite15Fixed16Number(io, Value[i])) return FALSE;
 594     }
 595 
 596     return TRUE;
 597 
 598     cmsUNUSED_PARAMETER(self);
 599 }
 600 
 601 static
 602 void* Type_S15Fixed16_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
 603 {
 604     return _cmsDupMem(self ->ContextID, Ptr, n * sizeof(cmsFloat64Number));
 605 }
 606 
 607 
 608 static
 609 void Type_S15Fixed16_Free(struct _cms_typehandler_struct* self, void* Ptr)
 610 {
 611     _cmsFree(self ->ContextID, Ptr);
 612 }
 613 
 614 // ********************************************************************************
 615 // Type cmsSigU16Fixed16ArrayType
 616 // ********************************************************************************
 617 // This type represents an array of generic 4-byte/32-bit quantity.
 618 // The number of values is determined from the size of the tag.
 619 
 620 
 621 static
 622 void *Type_U16Fixed16_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
 623 {
 624     cmsFloat64Number*  array_double;
 625     cmsUInt32Number v;
 626     cmsUInt32Number i, n;
 627 
 628     *nItems = 0;
 629     n = SizeOfTag / sizeof(cmsUInt32Number);
 630     array_double = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, n, sizeof(cmsFloat64Number));
 631     if (array_double == NULL) return NULL;
 632 
 633     for (i=0; i < n; i++) {
 634 
 635         if (!_cmsReadUInt32Number(io, &v)) {
 636             _cmsFree(self ->ContextID, (void*) array_double);
 637             return NULL;
 638         }
 639 
 640         // Convert to cmsFloat64Number
 641         array_double[i] =  (cmsFloat64Number) (v / 65536.0);
 642     }
 643 
 644     *nItems = n;
 645     return (void*) array_double;
 646 }
 647 
 648 static
 649 cmsBool Type_U16Fixed16_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
 650 {
 651     cmsFloat64Number* Value = (cmsFloat64Number*) Ptr;
 652     cmsUInt32Number i;
 653 
 654     for (i=0; i < nItems; i++) {
 655 
 656         cmsUInt32Number v = (cmsUInt32Number) floor(Value[i]*65536.0 + 0.5);
 657 
 658         if (!_cmsWriteUInt32Number(io, v)) return FALSE;
 659     }
 660 
 661     return TRUE;
 662 
 663     cmsUNUSED_PARAMETER(self);
 664 }
 665 
 666 
 667 static
 668 void* Type_U16Fixed16_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
 669 {
 670     return _cmsDupMem(self ->ContextID, Ptr, n * sizeof(cmsFloat64Number));
 671 }
 672 
 673 static
 674 void Type_U16Fixed16_Free(struct _cms_typehandler_struct* self, void* Ptr)
 675 {
 676     _cmsFree(self ->ContextID, Ptr);
 677 }
 678 
 679 // ********************************************************************************
 680 // Type cmsSigSignatureType
 681 // ********************************************************************************
 682 //
 683 // The signatureType contains a four-byte sequence, Sequences of less than four
 684 // characters are padded at the end with spaces, 20h.
 685 // Typically this type is used for registered tags that can be displayed on many
 686 // development systems as a sequence of four characters.
 687 
 688 static
 689 void *Type_Signature_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
 690 {
 691     cmsSignature* SigPtr = (cmsSignature*) _cmsMalloc(self ->ContextID, sizeof(cmsSignature));
 692     if (SigPtr == NULL) return NULL;
 693 
 694      if (!_cmsReadUInt32Number(io, SigPtr)) return NULL;
 695      *nItems = 1;
 696 
 697      return SigPtr;
 698 
 699      cmsUNUSED_PARAMETER(SizeOfTag);
 700 }
 701 
 702 static
 703 cmsBool  Type_Signature_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
 704 {
 705     cmsSignature* SigPtr = (cmsSignature*) Ptr;
 706 
 707     return _cmsWriteUInt32Number(io, *SigPtr);
 708 
 709     cmsUNUSED_PARAMETER(nItems);
 710     cmsUNUSED_PARAMETER(self);
 711 }
 712 
 713 static
 714 void* Type_Signature_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
 715 {
 716     return _cmsDupMem(self ->ContextID, Ptr, n * sizeof(cmsSignature));
 717 }
 718 
 719 static
 720 void Type_Signature_Free(struct _cms_typehandler_struct* self, void* Ptr)
 721 {
 722     _cmsFree(self ->ContextID, Ptr);
 723 }
 724 
 725 
 726 // ********************************************************************************
 727 // Type cmsSigTextType
 728 // ********************************************************************************
 729 //
 730 // The textType is a simple text structure that contains a 7-bit ASCII text string.
 731 // The length of the string is obtained by subtracting 8 from the element size portion
 732 // of the tag itself. This string must be terminated with a 00h byte.
 733 
 734 static
 735 void *Type_Text_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
 736 {
 737     char* Text = NULL;
 738     cmsMLU* mlu = NULL;
 739 
 740     // Create a container
 741     mlu = cmsMLUalloc(self ->ContextID, 1);
 742     if (mlu == NULL) return NULL;
 743 
 744     *nItems = 0;
 745 
 746     // We need to store the "\0" at the end, so +1
 747     if (SizeOfTag == UINT_MAX) goto Error;
 748 
 749     Text = (char*) _cmsMalloc(self ->ContextID, SizeOfTag + 1);
 750     if (Text == NULL) goto Error;
 751 
 752     if (io -> Read(io, Text, sizeof(char), SizeOfTag) != SizeOfTag) goto Error;
 753 
 754     // Make sure text is properly ended
 755     Text[SizeOfTag] = 0;
 756     *nItems = 1;
 757 
 758     // Keep the result
 759     if (!cmsMLUsetASCII(mlu, cmsNoLanguage, cmsNoCountry, Text)) goto Error;
 760 
 761     _cmsFree(self ->ContextID, Text);
 762     return (void*) mlu;
 763 
 764 Error:
 765     if (mlu != NULL)
 766         cmsMLUfree(mlu);
 767     if (Text != NULL)
 768         _cmsFree(self ->ContextID, Text);
 769 
 770     return NULL;
 771 }
 772 
 773 // The conversion implies to choose a language. So, we choose the actual language.
 774 static
 775 cmsBool Type_Text_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
 776 {
 777     cmsMLU* mlu = (cmsMLU*) Ptr;
 778     cmsUInt32Number size;
 779     cmsBool  rc;
 780     char* Text;
 781 
 782     // Get the size of the string. Note there is an extra "\0" at the end
 783     size = cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, NULL, 0);
 784     if (size == 0) return FALSE;       // Cannot be zero!
 785 
 786     // Create memory
 787     Text = (char*) _cmsMalloc(self ->ContextID, size);
 788     if (Text == NULL) return FALSE;
 789 
 790     cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, Text, size);
 791 
 792     // Write it, including separator
 793     rc = io ->Write(io, size, Text);
 794 
 795     _cmsFree(self ->ContextID, Text);
 796     return rc;
 797 
 798     cmsUNUSED_PARAMETER(nItems);
 799 }
 800 
 801 static
 802 void* Type_Text_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
 803 {
 804     return (void*) cmsMLUdup((cmsMLU*) Ptr);
 805 
 806     cmsUNUSED_PARAMETER(n);
 807     cmsUNUSED_PARAMETER(self);
 808 }
 809 
 810 
 811 static
 812 void Type_Text_Free(struct _cms_typehandler_struct* self, void* Ptr)
 813 {
 814     cmsMLU* mlu = (cmsMLU*) Ptr;
 815     cmsMLUfree(mlu);
 816     return;
 817 
 818     cmsUNUSED_PARAMETER(self);
 819 }
 820 
 821 static
 822 cmsTagTypeSignature DecideTextType(cmsFloat64Number ICCVersion, const void *Data)
 823 {
 824     if (ICCVersion >= 4.0)
 825         return cmsSigMultiLocalizedUnicodeType;
 826 
 827     return cmsSigTextType;
 828 
 829     cmsUNUSED_PARAMETER(Data);
 830 }
 831 
 832 
 833 // ********************************************************************************
 834 // Type cmsSigDataType
 835 // ********************************************************************************
 836 
 837 // General purpose data type
 838 static
 839 void *Type_Data_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
 840 {
 841     cmsICCData* BinData;
 842     cmsUInt32Number LenOfData;
 843 
 844     *nItems = 0;
 845 
 846     if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL;
 847 
 848     LenOfData = SizeOfTag - sizeof(cmsUInt32Number);
 849     if (LenOfData > INT_MAX) return NULL;
 850 
 851     BinData = (cmsICCData*) _cmsMalloc(self ->ContextID, sizeof(cmsICCData) + LenOfData - 1);
 852     if (BinData == NULL) return NULL;
 853 
 854     BinData ->len = LenOfData;
 855     if (!_cmsReadUInt32Number(io, &BinData->flag)) {
 856         _cmsFree(self ->ContextID, BinData);
 857         return NULL;
 858     }
 859 
 860     if (io -> Read(io, BinData ->data, sizeof(cmsUInt8Number), LenOfData) != LenOfData) {
 861 
 862         _cmsFree(self ->ContextID, BinData);
 863         return NULL;
 864     }
 865 
 866     *nItems = 1;
 867 
 868     return (void*) BinData;
 869 }
 870 
 871 
 872 static
 873 cmsBool Type_Data_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
 874 {
 875    cmsICCData* BinData = (cmsICCData*) Ptr;
 876 
 877    if (!_cmsWriteUInt32Number(io, BinData ->flag)) return FALSE;
 878 
 879    return io ->Write(io, BinData ->len, BinData ->data);
 880 
 881    cmsUNUSED_PARAMETER(nItems);
 882    cmsUNUSED_PARAMETER(self);
 883 }
 884 
 885 
 886 static
 887 void* Type_Data_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
 888 {
 889     cmsICCData* BinData = (cmsICCData*) Ptr;
 890 
 891     return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsICCData) + BinData ->len - 1);
 892 
 893     cmsUNUSED_PARAMETER(n);
 894 }
 895 
 896 static
 897 void Type_Data_Free(struct _cms_typehandler_struct* self, void* Ptr)
 898 {
 899     _cmsFree(self ->ContextID, Ptr);
 900 }
 901 
 902 // ********************************************************************************
 903 // Type cmsSigTextDescriptionType
 904 // ********************************************************************************
 905 
 906 static
 907 void *Type_Text_Description_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
 908 {
 909     char* Text = NULL;
 910     cmsMLU* mlu = NULL;
 911     cmsUInt32Number  AsciiCount;
 912     cmsUInt32Number  i, UnicodeCode, UnicodeCount;
 913     cmsUInt16Number  ScriptCodeCode, Dummy;
 914     cmsUInt8Number   ScriptCodeCount;
 915 
 916     *nItems = 0;
 917 
 918     //  One dword should be there
 919     if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL;
 920 
 921     // Read len of ASCII
 922     if (!_cmsReadUInt32Number(io, &AsciiCount)) return NULL;
 923     SizeOfTag -= sizeof(cmsUInt32Number);
 924 
 925     // Check for size
 926     if (SizeOfTag < AsciiCount) return NULL;
 927 
 928     // All seems Ok, allocate the container
 929     mlu = cmsMLUalloc(self ->ContextID, 1);
 930     if (mlu == NULL) return NULL;
 931 
 932     // As many memory as size of tag
 933     Text = (char*) _cmsMalloc(self ->ContextID, AsciiCount + 1);
 934     if (Text == NULL) goto Error;
 935 
 936     // Read it
 937     if (io ->Read(io, Text, sizeof(char), AsciiCount) != AsciiCount) goto Error;
 938     SizeOfTag -= AsciiCount;
 939 
 940     // Make sure there is a terminator
 941     Text[AsciiCount] = 0;
 942 
 943     // Set the MLU entry. From here we can be tolerant to wrong types
 944     if (!cmsMLUsetASCII(mlu, cmsNoLanguage, cmsNoCountry, Text)) goto Error;
 945     _cmsFree(self ->ContextID, (void*) Text);
 946     Text = NULL;
 947 
 948     // Skip Unicode code
 949     if (SizeOfTag < 2* sizeof(cmsUInt32Number)) goto Done;
 950     if (!_cmsReadUInt32Number(io, &UnicodeCode)) goto Done;
 951     if (!_cmsReadUInt32Number(io, &UnicodeCount)) goto Done;
 952     SizeOfTag -= 2* sizeof(cmsUInt32Number);
 953 
 954     if (SizeOfTag < UnicodeCount*sizeof(cmsUInt16Number)) goto Done;
 955 
 956     for (i=0; i < UnicodeCount; i++) {
 957         if (!io ->Read(io, &Dummy, sizeof(cmsUInt16Number), 1)) goto Done;
 958     }
 959     SizeOfTag -= UnicodeCount*sizeof(cmsUInt16Number);
 960 
 961     // Skip ScriptCode code if present. Some buggy profiles does have less
 962     // data that stricttly required. We need to skip it as this type may come
 963     // embedded in other types.
 964 
 965     if (SizeOfTag >= sizeof(cmsUInt16Number) + sizeof(cmsUInt8Number) + 67) {
 966 
 967         if (!_cmsReadUInt16Number(io, &ScriptCodeCode)) goto Done;
 968         if (!_cmsReadUInt8Number(io,  &ScriptCodeCount)) goto Done;
 969 
 970         // Skip rest of tag
 971         for (i=0; i < 67; i++) {
 972             if (!io ->Read(io, &Dummy, sizeof(cmsUInt8Number), 1)) goto Error;
 973         }
 974     }
 975 
 976 Done:
 977 
 978     *nItems = 1;
 979     return mlu;
 980 
 981 Error:
 982     if (Text) _cmsFree(self ->ContextID, (void*) Text);
 983     if (mlu) cmsMLUfree(mlu);
 984     return NULL;
 985 }
 986 
 987 
 988 // This tag can come IN UNALIGNED SIZE. In order to prevent issues, we force zeros on description to align it
 989 static
 990 cmsBool  Type_Text_Description_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
 991 {
 992     cmsMLU* mlu = (cmsMLU*) Ptr;
 993     char *Text = NULL;
 994     wchar_t *Wide = NULL;
 995     cmsUInt32Number len, len_text, len_tag_requirement, len_aligned;
 996     cmsBool  rc = FALSE;
 997     char Filler[68];
 998 
 999     // Used below for writing zeroes
1000     memset(Filler, 0, sizeof(Filler));
1001 
1002     // Get the len of string
1003     len = cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, NULL, 0);
1004 
1005     // Specification ICC.1:2001-04 (v2.4.0): It has been found that textDescriptionType can contain misaligned data
1006     //(see clause 4.1 for the definition of 'aligned'). Because the Unicode language
1007     // code and Unicode count immediately follow the ASCII description, their
1008     // alignment is not correct if the ASCII count is not a multiple of four. The
1009     // ScriptCode code is misaligned when the ASCII count is odd. Profile reading and
1010     // writing software must be written carefully in order to handle these alignment
1011     // problems.
1012     //
1013     // The above last sentence suggest to handle alignment issues in the
1014     // parser. The provided example (Table 69 on Page 60) makes this clear.
1015     // The padding only in the ASCII count is not sufficient for a aligned tag
1016     // size, with the same text size in ASCII and Unicode.
1017 
1018     // Null strings
1019     if (len <= 0) {
1020 
1021         Text = (char*)    _cmsDupMem(self ->ContextID, "", sizeof(char));
1022         Wide = (wchar_t*) _cmsDupMem(self ->ContextID, L"", sizeof(wchar_t));
1023     }
1024     else {
1025         // Create independent buffers
1026         Text = (char*) _cmsCalloc(self ->ContextID, len, sizeof(char));
1027         if (Text == NULL) goto Error;
1028 
1029         Wide = (wchar_t*) _cmsCalloc(self ->ContextID, len, sizeof(wchar_t));
1030         if (Wide == NULL) goto Error;
1031 
1032         // Get both representations.
1033         cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry,  Text, len * sizeof(char));
1034         cmsMLUgetWide(mlu,  cmsNoLanguage, cmsNoCountry,  Wide, len * sizeof(wchar_t));
1035     }
1036 
1037     // Tell the real text len including the null terminator and padding
1038     len_text = (cmsUInt32Number) strlen(Text) + 1;
1039     // Compute an total tag size requirement
1040     len_tag_requirement = (8+4+len_text+4+4+2*len_text+2+1+67);
1041     len_aligned = _cmsALIGNLONG(len_tag_requirement);
1042 
1043   // * cmsUInt32Number       count;          * Description length
1044   // * cmsInt8Number         desc[count]     * NULL terminated ascii string
1045   // * cmsUInt32Number       ucLangCode;     * UniCode language code
1046   // * cmsUInt32Number       ucCount;        * UniCode description length
1047   // * cmsInt16Number        ucDesc[ucCount];* The UniCode description
1048   // * cmsUInt16Number       scCode;         * ScriptCode code
1049   // * cmsUInt8Number        scCount;        * ScriptCode count
1050   // * cmsInt8Number         scDesc[67];     * ScriptCode Description
1051 
1052     if (!_cmsWriteUInt32Number(io, len_text)) goto Error;
1053     if (!io ->Write(io, len_text, Text)) goto Error;
1054 
1055     if (!_cmsWriteUInt32Number(io, 0)) goto Error;  // ucLanguageCode
1056 
1057     if (!_cmsWriteUInt32Number(io, len_text)) goto Error;
1058     // Note that in some compilers sizeof(cmsUInt16Number) != sizeof(wchar_t)
1059     if (!_cmsWriteWCharArray(io, len_text, Wide)) goto Error;
1060 
1061     // ScriptCode Code & count (unused)
1062     if (!_cmsWriteUInt16Number(io, 0)) goto Error;
1063     if (!_cmsWriteUInt8Number(io, 0)) goto Error;
1064 
1065     if (!io ->Write(io, 67, Filler)) goto Error;
1066 
1067     // possibly add pad at the end of tag
1068     if(len_aligned - len_tag_requirement > 0)
1069       if (!io ->Write(io, len_aligned - len_tag_requirement, Filler)) goto Error;
1070 
1071     rc = TRUE;
1072 
1073 Error:
1074     if (Text) _cmsFree(self ->ContextID, Text);
1075     if (Wide) _cmsFree(self ->ContextID, Wide);
1076 
1077     return rc;
1078 
1079     cmsUNUSED_PARAMETER(nItems);
1080 }
1081 
1082 
1083 static
1084 void* Type_Text_Description_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
1085 {
1086     return (void*) cmsMLUdup((cmsMLU*) Ptr);
1087 
1088     cmsUNUSED_PARAMETER(n);
1089     cmsUNUSED_PARAMETER(self);
1090 }
1091 
1092 static
1093 void Type_Text_Description_Free(struct _cms_typehandler_struct* self, void* Ptr)
1094 {
1095     cmsMLU* mlu = (cmsMLU*) Ptr;
1096 
1097     cmsMLUfree(mlu);
1098     return;
1099 
1100     cmsUNUSED_PARAMETER(self);
1101 }
1102 
1103 
1104 static
1105 cmsTagTypeSignature DecideTextDescType(cmsFloat64Number ICCVersion, const void *Data)
1106 {
1107     if (ICCVersion >= 4.0)
1108         return cmsSigMultiLocalizedUnicodeType;
1109 
1110     return cmsSigTextDescriptionType;
1111 
1112     cmsUNUSED_PARAMETER(Data);
1113 }
1114 
1115 
1116 // ********************************************************************************
1117 // Type cmsSigCurveType
1118 // ********************************************************************************
1119 
1120 static
1121 void *Type_Curve_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
1122 {
1123     cmsUInt32Number Count;
1124     cmsToneCurve* NewGamma;
1125 
1126     *nItems = 0;
1127     if (!_cmsReadUInt32Number(io, &Count)) return NULL;
1128 
1129     switch (Count) {
1130 
1131            case 0:   // Linear.
1132                {
1133                    cmsFloat64Number SingleGamma = 1.0;
1134 
1135                    NewGamma = cmsBuildParametricToneCurve(self ->ContextID, 1, &SingleGamma);
1136                    if (!NewGamma) return NULL;
1137                    *nItems = 1;
1138                    return NewGamma;
1139                }
1140 
1141            case 1:  // Specified as the exponent of gamma function
1142                {
1143                    cmsUInt16Number SingleGammaFixed;
1144                    cmsFloat64Number SingleGamma;
1145 
1146                    if (!_cmsReadUInt16Number(io, &SingleGammaFixed)) return NULL;
1147                    SingleGamma = _cms8Fixed8toDouble(SingleGammaFixed);
1148 
1149                    *nItems = 1;
1150                    return cmsBuildParametricToneCurve(self ->ContextID, 1, &SingleGamma);
1151                }
1152 
1153            default:  // Curve
1154 
1155                if (Count > 0x7FFF)
1156                    return NULL; // This is to prevent bad guys for doing bad things
1157 
1158                NewGamma = cmsBuildTabulatedToneCurve16(self ->ContextID, Count, NULL);
1159                if (!NewGamma) return NULL;
1160 
1161                if (!_cmsReadUInt16Array(io, Count, NewGamma -> Table16)) {
1162                    cmsFreeToneCurve(NewGamma);
1163                    return NULL;
1164                }
1165 
1166                *nItems = 1;
1167                return NewGamma;
1168     }
1169 
1170     cmsUNUSED_PARAMETER(SizeOfTag);
1171 }
1172 
1173 
1174 static
1175 cmsBool  Type_Curve_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
1176 {
1177     cmsToneCurve* Curve = (cmsToneCurve*) Ptr;
1178 
1179     if (Curve ->nSegments == 1 && Curve ->Segments[0].Type == 1) {
1180 
1181             // Single gamma, preserve number
1182             cmsUInt16Number SingleGammaFixed = _cmsDoubleTo8Fixed8(Curve ->Segments[0].Params[0]);
1183 
1184             if (!_cmsWriteUInt32Number(io, 1)) return FALSE;
1185             if (!_cmsWriteUInt16Number(io, SingleGammaFixed)) return FALSE;
1186             return TRUE;
1187 
1188     }
1189 
1190     if (!_cmsWriteUInt32Number(io, Curve ->nEntries)) return FALSE;
1191     return _cmsWriteUInt16Array(io, Curve ->nEntries, Curve ->Table16);
1192 
1193     cmsUNUSED_PARAMETER(nItems);
1194     cmsUNUSED_PARAMETER(self);
1195 }
1196 
1197 
1198 static
1199 void* Type_Curve_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
1200 {
1201     return (void*) cmsDupToneCurve((cmsToneCurve*) Ptr);
1202 
1203     cmsUNUSED_PARAMETER(n);
1204     cmsUNUSED_PARAMETER(self);
1205 }
1206 
1207 static
1208 void Type_Curve_Free(struct _cms_typehandler_struct* self, void* Ptr)
1209 {
1210     cmsToneCurve* gamma = (cmsToneCurve*) Ptr;
1211 
1212     cmsFreeToneCurve(gamma);
1213     return;
1214 
1215     cmsUNUSED_PARAMETER(self);
1216 }
1217 
1218 
1219 // ********************************************************************************
1220 // Type cmsSigParametricCurveType
1221 // ********************************************************************************
1222 
1223 
1224 // Decide which curve type to use on writing
1225 static
1226 cmsTagTypeSignature DecideCurveType(cmsFloat64Number ICCVersion, const void *Data)
1227 {
1228     cmsToneCurve* Curve = (cmsToneCurve*) Data;
1229 
1230     if (ICCVersion < 4.0) return cmsSigCurveType;
1231     if (Curve ->nSegments != 1) return cmsSigCurveType;          // Only 1-segment curves can be saved as parametric
1232     if (Curve ->Segments[0].Type < 0) return cmsSigCurveType;    // Only non-inverted curves
1233     if (Curve ->Segments[0].Type > 5) return cmsSigCurveType;    // Only ICC parametric curves
1234 
1235     return cmsSigParametricCurveType;
1236 }
1237 
1238 static
1239 void *Type_ParametricCurve_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
1240 {
1241     static const int ParamsByType[] = { 1, 3, 4, 5, 7 };
1242     cmsFloat64Number Params[10];
1243     cmsUInt16Number Type;
1244     int i, n;
1245     cmsToneCurve* NewGamma;
1246 
1247     if (!_cmsReadUInt16Number(io, &Type)) return NULL;
1248     if (!_cmsReadUInt16Number(io, NULL)) return NULL;   // Reserved
1249 
1250     if (Type > 4) {
1251 
1252         cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown parametric curve type '%d'", Type);
1253         return NULL;
1254     }
1255 
1256     memset(Params, 0, sizeof(Params));
1257     n = ParamsByType[Type];
1258 
1259     for (i=0; i < n; i++) {
1260 
1261         if (!_cmsRead15Fixed16Number(io, &Params[i])) return NULL;
1262     }
1263 
1264     NewGamma = cmsBuildParametricToneCurve(self ->ContextID, Type+1, Params);
1265 
1266     *nItems = 1;
1267     return NewGamma;
1268 
1269     cmsUNUSED_PARAMETER(SizeOfTag);
1270 }
1271 
1272 
1273 static
1274 cmsBool  Type_ParametricCurve_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
1275 {
1276     cmsToneCurve* Curve = (cmsToneCurve*) Ptr;
1277     int i, nParams, typen;
1278     static const int ParamsByType[] = { 0, 1, 3, 4, 5, 7 };
1279 
1280     typen = Curve -> Segments[0].Type;
1281 
1282     if (Curve ->nSegments > 1 || typen < 1) {
1283 
1284         cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Multisegment or Inverted parametric curves cannot be written");
1285         return FALSE;
1286     }
1287 
1288     if (typen > 5) {
1289         cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported parametric curve");
1290         return FALSE;
1291     }
1292 
1293     nParams = ParamsByType[typen];
1294 
1295     if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) (Curve ->Segments[0].Type - 1))) return FALSE;
1296     if (!_cmsWriteUInt16Number(io, 0)) return FALSE;        // Reserved
1297 
1298     for (i=0; i < nParams; i++) {
1299 
1300         if (!_cmsWrite15Fixed16Number(io, Curve -> Segments[0].Params[i])) return FALSE;
1301     }
1302 
1303     return TRUE;
1304 
1305     cmsUNUSED_PARAMETER(nItems);
1306 }
1307 
1308 static
1309 void* Type_ParametricCurve_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
1310 {
1311     return (void*) cmsDupToneCurve((cmsToneCurve*) Ptr);
1312 
1313     cmsUNUSED_PARAMETER(n);
1314     cmsUNUSED_PARAMETER(self);
1315 }
1316 
1317 static
1318 void Type_ParametricCurve_Free(struct _cms_typehandler_struct* self, void* Ptr)
1319 {
1320     cmsToneCurve* gamma = (cmsToneCurve*) Ptr;
1321 
1322     cmsFreeToneCurve(gamma);
1323     return;
1324 
1325     cmsUNUSED_PARAMETER(self);
1326 }
1327 
1328 
1329 // ********************************************************************************
1330 // Type cmsSigDateTimeType
1331 // ********************************************************************************
1332 
1333 // A 12-byte value representation of the time and date, where the byte usage is assigned
1334 // as specified in table 1. The actual values are encoded as 16-bit unsigned integers
1335 // (uInt16Number - see 5.1.6).
1336 //
1337 // All the dateTimeNumber values in a profile shall be in Coordinated Universal Time
1338 // (UTC, also known as GMT or ZULU Time). Profile writers are required to convert local
1339 // time to UTC when setting these values. Programmes that display these values may show
1340 // the dateTimeNumber as UTC, show the equivalent local time (at current locale), or
1341 // display both UTC and local versions of the dateTimeNumber.
1342 
1343 static
1344 void *Type_DateTime_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
1345 {
1346     cmsDateTimeNumber timestamp;
1347     struct tm * NewDateTime;
1348 
1349     *nItems = 0;
1350     NewDateTime = (struct tm*) _cmsMalloc(self ->ContextID, sizeof(struct tm));
1351     if (NewDateTime == NULL) return NULL;
1352 
1353     if (io->Read(io, &timestamp, sizeof(cmsDateTimeNumber), 1) != 1) return NULL;
1354 
1355      _cmsDecodeDateTimeNumber(&timestamp, NewDateTime);
1356 
1357      *nItems = 1;
1358      return NewDateTime;
1359 
1360      cmsUNUSED_PARAMETER(SizeOfTag);
1361 }
1362 
1363 
1364 static
1365 cmsBool  Type_DateTime_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
1366 {
1367     struct tm * DateTime = (struct tm*) Ptr;
1368     cmsDateTimeNumber timestamp;
1369 
1370     _cmsEncodeDateTimeNumber(&timestamp, DateTime);
1371     if (!io ->Write(io, sizeof(cmsDateTimeNumber), &timestamp)) return FALSE;
1372 
1373     return TRUE;
1374 
1375     cmsUNUSED_PARAMETER(nItems);
1376     cmsUNUSED_PARAMETER(self);
1377 }
1378 
1379 static
1380 void* Type_DateTime_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
1381 {
1382     return _cmsDupMem(self ->ContextID, Ptr, sizeof(struct tm));
1383 
1384     cmsUNUSED_PARAMETER(n);
1385 }
1386 
1387 static
1388 void Type_DateTime_Free(struct _cms_typehandler_struct* self, void* Ptr)
1389 {
1390     _cmsFree(self ->ContextID, Ptr);
1391 }
1392 
1393 
1394 
1395 // ********************************************************************************
1396 // Type icMeasurementType
1397 // ********************************************************************************
1398 
1399 /*
1400 The measurementType information refers only to the internal profile data and is
1401 meant to provide profile makers an alternative to the default measurement
1402 specifications.
1403 */
1404 
1405 static
1406 void *Type_Measurement_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
1407 {
1408     cmsICCMeasurementConditions mc;
1409 
1410 
1411     memset(&mc, 0, sizeof(mc));
1412 
1413     if (!_cmsReadUInt32Number(io, &mc.Observer)) return NULL;
1414     if (!_cmsReadXYZNumber(io,    &mc.Backing)) return NULL;
1415     if (!_cmsReadUInt32Number(io, &mc.Geometry)) return NULL;
1416     if (!_cmsRead15Fixed16Number(io, &mc.Flare)) return NULL;
1417     if (!_cmsReadUInt32Number(io, &mc.IlluminantType)) return NULL;
1418 
1419     *nItems = 1;
1420     return _cmsDupMem(self ->ContextID, &mc, sizeof(cmsICCMeasurementConditions));
1421 
1422     cmsUNUSED_PARAMETER(SizeOfTag);
1423 }
1424 
1425 
1426 static
1427 cmsBool  Type_Measurement_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
1428 {
1429     cmsICCMeasurementConditions* mc =(cmsICCMeasurementConditions*) Ptr;
1430 
1431     if (!_cmsWriteUInt32Number(io, mc->Observer)) return FALSE;
1432     if (!_cmsWriteXYZNumber(io,    &mc->Backing)) return FALSE;
1433     if (!_cmsWriteUInt32Number(io, mc->Geometry)) return FALSE;
1434     if (!_cmsWrite15Fixed16Number(io, mc->Flare)) return FALSE;
1435     if (!_cmsWriteUInt32Number(io, mc->IlluminantType)) return FALSE;
1436 
1437     return TRUE;
1438 
1439     cmsUNUSED_PARAMETER(nItems);
1440     cmsUNUSED_PARAMETER(self);
1441 }
1442 
1443 static
1444 void* Type_Measurement_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
1445 {
1446      return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsICCMeasurementConditions));
1447 
1448      cmsUNUSED_PARAMETER(n);
1449 }
1450 
1451 static
1452 void Type_Measurement_Free(struct _cms_typehandler_struct* self, void* Ptr)
1453 {
1454    _cmsFree(self ->ContextID, Ptr);
1455 }
1456 
1457 
1458 // ********************************************************************************
1459 // Type cmsSigMultiLocalizedUnicodeType
1460 // ********************************************************************************
1461 //
1462 //   Do NOT trust SizeOfTag as there is an issue on the definition of profileSequenceDescTag. See the TechNote from
1463 //   Max Derhak and Rohit Patil about this: basically the size of the string table should be guessed and cannot be
1464 //   taken from the size of tag if this tag is embedded as part of bigger structures (profileSequenceDescTag, for instance)
1465 //
1466 
1467 static
1468 void *Type_MLU_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
1469 {
1470     cmsMLU* mlu;
1471     cmsUInt32Number Count, RecLen, NumOfWchar;
1472     cmsUInt32Number SizeOfHeader;
1473     cmsUInt32Number  Len, Offset;
1474     cmsUInt32Number  i;
1475     wchar_t*         Block;
1476     cmsUInt32Number  BeginOfThisString, EndOfThisString, LargestPosition;
1477 
1478     *nItems = 0;
1479     if (!_cmsReadUInt32Number(io, &Count)) return NULL;
1480     if (!_cmsReadUInt32Number(io, &RecLen)) return NULL;
1481 
1482     if (RecLen != 12) {
1483 
1484         cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "multiLocalizedUnicodeType of len != 12 is not supported.");
1485         return NULL;
1486     }
1487 
1488     mlu = cmsMLUalloc(self ->ContextID, Count);
1489     if (mlu == NULL) return NULL;
1490 
1491     mlu ->UsedEntries = Count;
1492 
1493     SizeOfHeader = 12 * Count + sizeof(_cmsTagBase);
1494     LargestPosition = 0;
1495 
1496     for (i=0; i < Count; i++) {
1497 
1498         if (!_cmsReadUInt16Number(io, &mlu ->Entries[i].Language)) goto Error;
1499         if (!_cmsReadUInt16Number(io, &mlu ->Entries[i].Country))  goto Error;
1500 
1501         // Now deal with Len and offset.
1502         if (!_cmsReadUInt32Number(io, &Len)) goto Error;
1503         if (!_cmsReadUInt32Number(io, &Offset)) goto Error;
1504 
1505         // Check for overflow
1506         if (Offset < (SizeOfHeader + 8)) goto Error;
1507         if (((Offset + Len) < Len) || ((Offset + Len) > SizeOfTag + 8)) goto Error;
1508 
1509         // True begin of the string
1510         BeginOfThisString = Offset - SizeOfHeader - 8;
1511 
1512         // Adjust to wchar_t elements
1513         mlu ->Entries[i].Len = (Len * sizeof(wchar_t)) / sizeof(cmsUInt16Number);
1514         mlu ->Entries[i].StrW = (BeginOfThisString * sizeof(wchar_t)) / sizeof(cmsUInt16Number);
1515 
1516         // To guess maximum size, add offset + len
1517         EndOfThisString = BeginOfThisString + Len;
1518         if (EndOfThisString > LargestPosition)
1519             LargestPosition = EndOfThisString;
1520     }
1521 
1522     // Now read the remaining of tag and fill all strings. Subtract the directory
1523     SizeOfTag   = (LargestPosition * sizeof(wchar_t)) / sizeof(cmsUInt16Number);
1524     if (SizeOfTag == 0)
1525     {
1526         Block = NULL;
1527         NumOfWchar = 0;
1528 
1529     }
1530     else
1531     {
1532         Block = (wchar_t*) _cmsMalloc(self ->ContextID, SizeOfTag);
1533         if (Block == NULL) goto Error;
1534         NumOfWchar = SizeOfTag / sizeof(wchar_t);
1535         if (!_cmsReadWCharArray(io, NumOfWchar, Block)) goto Error;
1536     }
1537 
1538     mlu ->MemPool  = Block;
1539     mlu ->PoolSize = SizeOfTag;
1540     mlu ->PoolUsed = SizeOfTag;
1541 
1542     *nItems = 1;
1543     return (void*) mlu;
1544 
1545 Error:
1546     if (mlu) cmsMLUfree(mlu);
1547     return NULL;
1548 }
1549 
1550 static
1551 cmsBool  Type_MLU_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
1552 {
1553     cmsMLU* mlu =(cmsMLU*) Ptr;
1554     cmsUInt32Number HeaderSize;
1555     cmsUInt32Number  Len, Offset;
1556     cmsUInt32Number i;
1557 
1558     if (Ptr == NULL) {
1559 
1560           // Empty placeholder
1561           if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
1562           if (!_cmsWriteUInt32Number(io, 12)) return FALSE;
1563           return TRUE;
1564     }
1565 
1566     if (!_cmsWriteUInt32Number(io, mlu ->UsedEntries)) return FALSE;
1567     if (!_cmsWriteUInt32Number(io, 12)) return FALSE;
1568 
1569     HeaderSize = 12 * mlu ->UsedEntries + sizeof(_cmsTagBase);
1570 
1571     for (i=0; i < mlu ->UsedEntries; i++) {
1572 
1573         Len    =  mlu ->Entries[i].Len;
1574         Offset =  mlu ->Entries[i].StrW;
1575 
1576         Len    = (Len * sizeof(cmsUInt16Number)) / sizeof(wchar_t);
1577         Offset = (Offset * sizeof(cmsUInt16Number)) / sizeof(wchar_t) + HeaderSize + 8;
1578 
1579         if (!_cmsWriteUInt16Number(io, mlu ->Entries[i].Language)) return FALSE;
1580         if (!_cmsWriteUInt16Number(io, mlu ->Entries[i].Country))  return FALSE;
1581         if (!_cmsWriteUInt32Number(io, Len)) return FALSE;
1582         if (!_cmsWriteUInt32Number(io, Offset)) return FALSE;
1583     }
1584 
1585     if (!_cmsWriteWCharArray(io, mlu ->PoolUsed / sizeof(wchar_t), (wchar_t*)  mlu ->MemPool)) return FALSE;
1586 
1587     return TRUE;
1588 
1589     cmsUNUSED_PARAMETER(nItems);
1590     cmsUNUSED_PARAMETER(self);
1591 }
1592 
1593 
1594 static
1595 void* Type_MLU_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
1596 {
1597     return (void*) cmsMLUdup((cmsMLU*) Ptr);
1598 
1599     cmsUNUSED_PARAMETER(n);
1600     cmsUNUSED_PARAMETER(self);
1601 }
1602 
1603 static
1604 void Type_MLU_Free(struct _cms_typehandler_struct* self, void* Ptr)
1605 {
1606     cmsMLUfree((cmsMLU*) Ptr);
1607     return;
1608 
1609     cmsUNUSED_PARAMETER(self);
1610 }
1611 
1612 
1613 // ********************************************************************************
1614 // Type cmsSigLut8Type
1615 // ********************************************************************************
1616 
1617 // Decide which LUT type to use on writing
1618 static
1619 cmsTagTypeSignature DecideLUTtypeA2B(cmsFloat64Number ICCVersion, const void *Data)
1620 {
1621     cmsPipeline* Lut = (cmsPipeline*) Data;
1622 
1623     if (ICCVersion < 4.0) {
1624         if (Lut ->SaveAs8Bits) return cmsSigLut8Type;
1625         return cmsSigLut16Type;
1626     }
1627     else {
1628          return cmsSigLutAtoBType;
1629     }
1630 }
1631 
1632 static
1633 cmsTagTypeSignature DecideLUTtypeB2A(cmsFloat64Number ICCVersion, const void *Data)
1634 {
1635     cmsPipeline* Lut = (cmsPipeline*) Data;
1636 
1637     if (ICCVersion < 4.0) {
1638         if (Lut ->SaveAs8Bits) return cmsSigLut8Type;
1639         return cmsSigLut16Type;
1640     }
1641     else {
1642          return cmsSigLutBtoAType;
1643     }
1644 }
1645 
1646 /*
1647 This structure represents a colour transform using tables of 8-bit precision.
1648 This type contains four processing elements: a 3 by 3 matrix (which shall be
1649 the identity matrix unless the input colour space is XYZ), a set of one dimensional
1650 input tables, a multidimensional lookup table, and a set of one dimensional output
1651 tables. Data is processed using these elements via the following sequence:
1652 (matrix) -> (1d input tables)  -> (multidimensional lookup table - CLUT) -> (1d output tables)
1653 
1654 Byte Position   Field Length (bytes)  Content Encoded as...
1655 8                  1          Number of Input Channels (i)    uInt8Number
1656 9                  1          Number of Output Channels (o)   uInt8Number
1657 10                 1          Number of CLUT grid points (identical for each side) (g) uInt8Number
1658 11                 1          Reserved for padding (fill with 00h)
1659 
1660 12..15             4          Encoded e00 parameter   s15Fixed16Number
1661 */
1662 
1663 
1664 // Read 8 bit tables as gamma functions
1665 static
1666 cmsBool  Read8bitTables(cmsContext ContextID, cmsIOHANDLER* io, cmsPipeline* lut, cmsUInt32Number nChannels)
1667 {
1668     cmsUInt8Number* Temp = NULL;
1669     cmsUInt32Number i, j;
1670     cmsToneCurve* Tables[cmsMAXCHANNELS];
1671 
1672     if (nChannels > cmsMAXCHANNELS) return FALSE;
1673     if (nChannels <= 0) return FALSE;
1674 
1675     memset(Tables, 0, sizeof(Tables));
1676 
1677     Temp = (cmsUInt8Number*) _cmsMalloc(ContextID, 256);
1678     if (Temp == NULL) return FALSE;
1679 
1680     for (i=0; i < nChannels; i++) {
1681         Tables[i] = cmsBuildTabulatedToneCurve16(ContextID, 256, NULL);
1682         if (Tables[i] == NULL) goto Error;
1683     }
1684 
1685     for (i=0; i < nChannels; i++) {
1686 
1687         if (io ->Read(io, Temp, 256, 1) != 1) goto Error;
1688 
1689         for (j=0; j < 256; j++)
1690             Tables[i]->Table16[j] = (cmsUInt16Number) FROM_8_TO_16(Temp[j]);
1691     }
1692 
1693     _cmsFree(ContextID, Temp);
1694     Temp = NULL;
1695 
1696     if (!cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, nChannels, Tables)))
1697         goto Error;
1698 
1699     for (i=0; i < nChannels; i++)
1700         cmsFreeToneCurve(Tables[i]);
1701 
1702     return TRUE;
1703 
1704 Error:
1705     for (i=0; i < nChannels; i++) {
1706         if (Tables[i]) cmsFreeToneCurve(Tables[i]);
1707     }
1708 
1709     if (Temp) _cmsFree(ContextID, Temp);
1710     return FALSE;
1711 }
1712 
1713 
1714 static
1715 cmsBool Write8bitTables(cmsContext ContextID, cmsIOHANDLER* io, cmsUInt32Number n, _cmsStageToneCurvesData* Tables)
1716 {
1717     int j;
1718     cmsUInt32Number i;
1719     cmsUInt8Number val;
1720 
1721     for (i=0; i < n; i++) {
1722 
1723         if (Tables) {
1724 
1725             // Usual case of identity curves
1726             if ((Tables ->TheCurves[i]->nEntries == 2) &&
1727                 (Tables->TheCurves[i]->Table16[0] == 0) &&
1728                 (Tables->TheCurves[i]->Table16[1] == 65535)) {
1729 
1730                     for (j=0; j < 256; j++) {
1731                         if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) j)) return FALSE;
1732                     }
1733             }
1734             else
1735                 if (Tables ->TheCurves[i]->nEntries != 256) {
1736                     cmsSignalError(ContextID, cmsERROR_RANGE, "LUT8 needs 256 entries on prelinearization");
1737                     return FALSE;
1738                 }
1739                 else
1740                     for (j=0; j < 256; j++) {
1741 
1742                         val = (cmsUInt8Number) FROM_16_TO_8(Tables->TheCurves[i]->Table16[j]);
1743 
1744                         if (!_cmsWriteUInt8Number(io, val)) return FALSE;
1745                     }
1746         }
1747     }
1748     return TRUE;
1749 }
1750 
1751 
1752 // Check overflow
1753 static
1754 cmsUInt32Number uipow(cmsUInt32Number n, cmsUInt32Number a, cmsUInt32Number b)
1755 {
1756     cmsUInt32Number rv = 1, rc;
1757 
1758     if (a == 0) return 0;
1759     if (n == 0) return 0;
1760 
1761     for (; b > 0; b--) {
1762 
1763         rv *= a;
1764 
1765         // Check for overflow
1766         if (rv > UINT_MAX / a) return (cmsUInt32Number) -1;
1767 
1768     }
1769 
1770     rc = rv * n;
1771 
1772     if (rv != rc / n) return (cmsUInt32Number) -1;
1773     return rc;
1774 }
1775 
1776 
1777 // That will create a MPE LUT with Matrix, pre tables, CLUT and post tables.
1778 // 8 bit lut may be scaled easely to v4 PCS, but we need also to properly adjust
1779 // PCS on BToAxx tags and AtoB if abstract. We need to fix input direction.
1780 
1781 static
1782 void *Type_LUT8_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
1783 {
1784     cmsUInt8Number InputChannels, OutputChannels, CLUTpoints;
1785     cmsUInt8Number* Temp = NULL;
1786     cmsPipeline* NewLUT = NULL;
1787     cmsUInt32Number nTabSize, i;
1788     cmsFloat64Number Matrix[3*3];
1789 
1790     *nItems = 0;
1791 
1792     if (!_cmsReadUInt8Number(io, &InputChannels)) goto Error;
1793     if (!_cmsReadUInt8Number(io, &OutputChannels)) goto Error;
1794     if (!_cmsReadUInt8Number(io, &CLUTpoints)) goto Error;
1795 
1796      if (CLUTpoints == 1) goto Error; // Impossible value, 0 for no CLUT and then 2 at least
1797 
1798     // Padding
1799     if (!_cmsReadUInt8Number(io, NULL)) goto Error;
1800 
1801     // Do some checking
1802     if (InputChannels == 0 || InputChannels > cmsMAXCHANNELS)  goto Error;
1803     if (OutputChannels == 0 || OutputChannels > cmsMAXCHANNELS) goto Error;
1804 
1805    // Allocates an empty Pipeline
1806     NewLUT = cmsPipelineAlloc(self ->ContextID, InputChannels, OutputChannels);
1807     if (NewLUT == NULL) goto Error;
1808 
1809     // Read the Matrix
1810     if (!_cmsRead15Fixed16Number(io,  &Matrix[0])) goto Error;
1811     if (!_cmsRead15Fixed16Number(io,  &Matrix[1])) goto Error;
1812     if (!_cmsRead15Fixed16Number(io,  &Matrix[2])) goto Error;
1813     if (!_cmsRead15Fixed16Number(io,  &Matrix[3])) goto Error;
1814     if (!_cmsRead15Fixed16Number(io,  &Matrix[4])) goto Error;
1815     if (!_cmsRead15Fixed16Number(io,  &Matrix[5])) goto Error;
1816     if (!_cmsRead15Fixed16Number(io,  &Matrix[6])) goto Error;
1817     if (!_cmsRead15Fixed16Number(io,  &Matrix[7])) goto Error;
1818     if (!_cmsRead15Fixed16Number(io,  &Matrix[8])) goto Error;
1819 
1820 
1821     // Only operates if not identity...
1822     if ((InputChannels == 3) && !_cmsMAT3isIdentity((cmsMAT3*) Matrix)) {
1823 
1824         if (!cmsPipelineInsertStage(NewLUT, cmsAT_BEGIN, cmsStageAllocMatrix(self ->ContextID, 3, 3, Matrix, NULL)))
1825             goto Error;
1826     }
1827 
1828     // Get input tables
1829     if (!Read8bitTables(self ->ContextID, io,  NewLUT, InputChannels)) goto Error;
1830 
1831     // Get 3D CLUT. Check the overflow....
1832     nTabSize = uipow(OutputChannels, CLUTpoints, InputChannels);
1833     if (nTabSize == (cmsUInt32Number) -1) goto Error;
1834     if (nTabSize > 0) {
1835 
1836         cmsUInt16Number *PtrW, *T;
1837 
1838         PtrW = T  = (cmsUInt16Number*) _cmsCalloc(self ->ContextID, nTabSize, sizeof(cmsUInt16Number));
1839         if (T  == NULL) goto Error;
1840 
1841         Temp = (cmsUInt8Number*) _cmsMalloc(self ->ContextID, nTabSize);
1842         if (Temp == NULL) {
1843             _cmsFree(self ->ContextID, T);
1844             goto Error;
1845         }
1846 
1847         if (io ->Read(io, Temp, nTabSize, 1) != 1) {
1848             _cmsFree(self ->ContextID, T);
1849             _cmsFree(self ->ContextID, Temp);
1850             goto Error;
1851         }
1852 
1853         for (i = 0; i < nTabSize; i++) {
1854 
1855             *PtrW++ = FROM_8_TO_16(Temp[i]);
1856         }
1857         _cmsFree(self ->ContextID, Temp);
1858         Temp = NULL;
1859 
1860         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, cmsStageAllocCLut16bit(self ->ContextID, CLUTpoints, InputChannels, OutputChannels, T))) {
1861             _cmsFree(self ->ContextID, T);
1862             goto Error;
1863         }
1864         _cmsFree(self ->ContextID, T);
1865     }
1866 
1867 
1868     // Get output tables
1869     if (!Read8bitTables(self ->ContextID, io,  NewLUT, OutputChannels)) goto Error;
1870 
1871     *nItems = 1;
1872     return NewLUT;
1873 
1874 Error:
1875     if (NewLUT != NULL) cmsPipelineFree(NewLUT);
1876     return NULL;
1877 
1878     cmsUNUSED_PARAMETER(SizeOfTag);
1879 }
1880 
1881 // We only allow a specific MPE structure: Matrix plus prelin, plus clut, plus post-lin.
1882 static
1883 cmsBool  Type_LUT8_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
1884 {
1885     cmsUInt32Number j, nTabSize, i, n;
1886     cmsUInt8Number  val;
1887     cmsPipeline* NewLUT = (cmsPipeline*) Ptr;
1888     cmsStage* mpe;
1889     _cmsStageToneCurvesData* PreMPE = NULL, *PostMPE = NULL;
1890     _cmsStageMatrixData* MatMPE = NULL;
1891     _cmsStageCLutData* clut = NULL;
1892     cmsUInt32Number clutPoints;
1893 
1894     // Disassemble the LUT into components.
1895     mpe = NewLUT -> Elements;
1896     if (mpe ->Type == cmsSigMatrixElemType) {
1897 
1898         MatMPE = (_cmsStageMatrixData*) mpe ->Data;
1899         mpe = mpe -> Next;
1900     }
1901 
1902     if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) {
1903         PreMPE = (_cmsStageToneCurvesData*) mpe ->Data;
1904         mpe = mpe -> Next;
1905     }
1906 
1907     if (mpe != NULL && mpe ->Type == cmsSigCLutElemType) {
1908         clut  = (_cmsStageCLutData*) mpe -> Data;
1909         mpe = mpe ->Next;
1910     }
1911 
1912     if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) {
1913         PostMPE = (_cmsStageToneCurvesData*) mpe ->Data;
1914         mpe = mpe -> Next;
1915     }
1916 
1917     // That should be all
1918     if (mpe != NULL) {
1919         cmsSignalError(mpe->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT is not suitable to be saved as LUT8");
1920         return FALSE;
1921     }
1922 
1923 
1924     if (clut == NULL)
1925         clutPoints = 0;
1926     else
1927         clutPoints    = clut->Params->nSamples[0];
1928 
1929     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) NewLUT ->InputChannels)) return FALSE;
1930     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) NewLUT ->OutputChannels)) return FALSE;
1931     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) clutPoints)) return FALSE;
1932     if (!_cmsWriteUInt8Number(io, 0)) return FALSE; // Padding
1933 
1934         n = NewLUT->InputChannels * NewLUT->OutputChannels;
1935 
1936     if (MatMPE != NULL) {
1937 
1938                 for (i = 0; i < n; i++)
1939                 {
1940                         if (!_cmsWrite15Fixed16Number(io, MatMPE->Double[i])) return FALSE;
1941                 }






1942     }
1943     else {
1944 
1945                 if (n != 9) return FALSE;
1946 
1947         if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE;
1948         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
1949         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
1950         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
1951         if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE;
1952         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
1953         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
1954         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
1955         if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE;
1956     }
1957 
1958     // The prelinearization table
1959     if (!Write8bitTables(self ->ContextID, io, NewLUT ->InputChannels, PreMPE)) return FALSE;
1960 
1961     nTabSize = uipow(NewLUT->OutputChannels, clutPoints, NewLUT ->InputChannels);
1962     if (nTabSize == (cmsUInt32Number) -1) return FALSE;
1963     if (nTabSize > 0) {
1964 
1965         // The 3D CLUT.
1966         if (clut != NULL) {
1967 
1968             for (j=0; j < nTabSize; j++) {
1969 
1970                 val = (cmsUInt8Number) FROM_16_TO_8(clut ->Tab.T[j]);
1971                 if (!_cmsWriteUInt8Number(io, val)) return FALSE;
1972             }
1973         }
1974     }
1975 
1976     // The postlinearization table
1977     if (!Write8bitTables(self ->ContextID, io, NewLUT ->OutputChannels, PostMPE)) return FALSE;
1978 
1979     return TRUE;
1980 
1981     cmsUNUSED_PARAMETER(nItems);
1982 }
1983 
1984 
1985 static
1986 void* Type_LUT8_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
1987 {
1988     return (void*) cmsPipelineDup((cmsPipeline*) Ptr);
1989 
1990     cmsUNUSED_PARAMETER(n);
1991     cmsUNUSED_PARAMETER(self);
1992 }
1993 
1994 static
1995 void Type_LUT8_Free(struct _cms_typehandler_struct* self, void* Ptr)
1996 {
1997     cmsPipelineFree((cmsPipeline*) Ptr);
1998     return;
1999 
2000     cmsUNUSED_PARAMETER(self);
2001 }
2002 
2003 // ********************************************************************************
2004 // Type cmsSigLut16Type
2005 // ********************************************************************************
2006 
2007 // Read 16 bit tables as gamma functions
2008 static
2009 cmsBool  Read16bitTables(cmsContext ContextID, cmsIOHANDLER* io, cmsPipeline* lut,
2010                                     cmsUInt32Number nChannels, cmsUInt32Number nEntries)
2011 {
2012     cmsUInt32Number i;
2013     cmsToneCurve* Tables[cmsMAXCHANNELS];
2014 
2015     // Maybe an empty table? (this is a lcms extension)
2016     if (nEntries <= 0) return TRUE;
2017 
2018     // Check for malicious profiles
2019     if (nEntries < 2) return FALSE;
2020     if (nChannels > cmsMAXCHANNELS) return FALSE;
2021 
2022     // Init table to zero
2023     memset(Tables, 0, sizeof(Tables));
2024 
2025     for (i=0; i < nChannels; i++) {
2026 
2027         Tables[i] = cmsBuildTabulatedToneCurve16(ContextID, nEntries, NULL);
2028         if (Tables[i] == NULL) goto Error;
2029 
2030         if (!_cmsReadUInt16Array(io, nEntries, Tables[i]->Table16)) goto Error;
2031     }
2032 
2033 
2034     // Add the table (which may certainly be an identity, but this is up to the optimizer, not the reading code)
2035     if (!cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, nChannels, Tables)))
2036         goto Error;
2037 
2038     for (i=0; i < nChannels; i++)
2039         cmsFreeToneCurve(Tables[i]);
2040 
2041     return TRUE;
2042 
2043 Error:
2044     for (i=0; i < nChannels; i++) {
2045         if (Tables[i]) cmsFreeToneCurve(Tables[i]);
2046     }
2047 
2048     return FALSE;
2049 }
2050 
2051 static
2052 cmsBool Write16bitTables(cmsContext ContextID, cmsIOHANDLER* io, _cmsStageToneCurvesData* Tables)
2053 {
2054     cmsUInt32Number j;
2055     cmsUInt32Number i;
2056     cmsUInt16Number val;
2057     cmsUInt32Number nEntries;
2058 
2059     _cmsAssert(Tables != NULL);
2060 
2061     nEntries = Tables->TheCurves[0]->nEntries;
2062 
2063     for (i=0; i < Tables ->nCurves; i++) {
2064 
2065         for (j=0; j < nEntries; j++) {
2066 
2067             val = Tables->TheCurves[i]->Table16[j];
2068             if (!_cmsWriteUInt16Number(io, val)) return FALSE;
2069         }
2070     }
2071     return TRUE;
2072 
2073     cmsUNUSED_PARAMETER(ContextID);
2074 }
2075 
2076 static
2077 void *Type_LUT16_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
2078 {
2079     cmsUInt8Number InputChannels, OutputChannels, CLUTpoints;
2080     cmsPipeline* NewLUT = NULL;
2081     cmsUInt32Number nTabSize;
2082     cmsFloat64Number Matrix[3*3];
2083     cmsUInt16Number InputEntries, OutputEntries;
2084 
2085     *nItems = 0;
2086 
2087     if (!_cmsReadUInt8Number(io, &InputChannels)) return NULL;
2088     if (!_cmsReadUInt8Number(io, &OutputChannels)) return NULL;
2089     if (!_cmsReadUInt8Number(io, &CLUTpoints)) return NULL;   // 255 maximum
2090 
2091     // Padding
2092     if (!_cmsReadUInt8Number(io, NULL)) return NULL;
2093 
2094     // Do some checking
2095     if (InputChannels == 0 || InputChannels > cmsMAXCHANNELS)  goto Error;
2096     if (OutputChannels == 0 || OutputChannels > cmsMAXCHANNELS) goto Error;
2097 
2098     // Allocates an empty LUT
2099     NewLUT = cmsPipelineAlloc(self ->ContextID, InputChannels, OutputChannels);
2100     if (NewLUT == NULL) goto Error;
2101 
2102     // Read the Matrix
2103     if (!_cmsRead15Fixed16Number(io,  &Matrix[0])) goto Error;
2104     if (!_cmsRead15Fixed16Number(io,  &Matrix[1])) goto Error;
2105     if (!_cmsRead15Fixed16Number(io,  &Matrix[2])) goto Error;
2106     if (!_cmsRead15Fixed16Number(io,  &Matrix[3])) goto Error;
2107     if (!_cmsRead15Fixed16Number(io,  &Matrix[4])) goto Error;
2108     if (!_cmsRead15Fixed16Number(io,  &Matrix[5])) goto Error;
2109     if (!_cmsRead15Fixed16Number(io,  &Matrix[6])) goto Error;
2110     if (!_cmsRead15Fixed16Number(io,  &Matrix[7])) goto Error;
2111     if (!_cmsRead15Fixed16Number(io,  &Matrix[8])) goto Error;
2112 
2113 
2114     // Only operates on 3 channels
2115     if ((InputChannels == 3) && !_cmsMAT3isIdentity((cmsMAT3*) Matrix)) {
2116 
2117         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, cmsStageAllocMatrix(self ->ContextID, 3, 3, Matrix, NULL)))
2118             goto Error;
2119     }
2120 
2121     if (!_cmsReadUInt16Number(io, &InputEntries)) goto Error;
2122     if (!_cmsReadUInt16Number(io, &OutputEntries)) goto Error;
2123 
2124     if (InputEntries > 0x7FFF || OutputEntries > 0x7FFF) goto Error;
2125     if (CLUTpoints == 1) goto Error; // Impossible value, 0 for no CLUT and then 2 at least
2126 
2127     // Get input tables
2128     if (!Read16bitTables(self ->ContextID, io,  NewLUT, InputChannels, InputEntries)) goto Error;
2129 
2130     // Get 3D CLUT
2131     nTabSize = uipow(OutputChannels, CLUTpoints, InputChannels);
2132     if (nTabSize == (cmsUInt32Number) -1) goto Error;
2133     if (nTabSize > 0) {
2134 
2135         cmsUInt16Number *T;
2136 
2137         T  = (cmsUInt16Number*) _cmsCalloc(self ->ContextID, nTabSize, sizeof(cmsUInt16Number));
2138         if (T  == NULL) goto Error;
2139 
2140         if (!_cmsReadUInt16Array(io, nTabSize, T)) {
2141             _cmsFree(self ->ContextID, T);
2142             goto Error;
2143         }
2144 
2145         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, cmsStageAllocCLut16bit(self ->ContextID, CLUTpoints, InputChannels, OutputChannels, T))) {
2146             _cmsFree(self ->ContextID, T);
2147             goto Error;
2148         }
2149         _cmsFree(self ->ContextID, T);
2150     }
2151 
2152 
2153     // Get output tables
2154     if (!Read16bitTables(self ->ContextID, io,  NewLUT, OutputChannels, OutputEntries)) goto Error;
2155 
2156     *nItems = 1;
2157     return NewLUT;
2158 
2159 Error:
2160     if (NewLUT != NULL) cmsPipelineFree(NewLUT);
2161     return NULL;
2162 
2163     cmsUNUSED_PARAMETER(SizeOfTag);
2164 }
2165 
2166 // We only allow some specific MPE structures: Matrix plus prelin, plus clut, plus post-lin.
2167 // Some empty defaults are created for missing parts
2168 
2169 static
2170 cmsBool Type_LUT16_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
2171 {
2172     cmsUInt32Number nTabSize;
2173     cmsPipeline* NewLUT = (cmsPipeline*) Ptr;
2174     cmsStage* mpe;
2175     _cmsStageToneCurvesData* PreMPE = NULL, *PostMPE = NULL;
2176     _cmsStageMatrixData* MatMPE = NULL;
2177     _cmsStageCLutData* clut = NULL;
2178     cmsUInt32Number i, InputChannels, OutputChannels, clutPoints;
2179 
2180     // Disassemble the LUT into components.
2181     mpe = NewLUT -> Elements;
2182     if (mpe != NULL && mpe ->Type == cmsSigMatrixElemType) {
2183 
2184         MatMPE = (_cmsStageMatrixData*) mpe ->Data;
2185         if (mpe->InputChannels != 3 || mpe->OutputChannels != 3) return FALSE;
2186         mpe = mpe -> Next;
2187     }
2188 
2189 
2190     if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) {
2191         PreMPE = (_cmsStageToneCurvesData*) mpe ->Data;
2192         mpe = mpe -> Next;
2193     }
2194 
2195     if (mpe != NULL && mpe ->Type == cmsSigCLutElemType) {
2196         clut  = (_cmsStageCLutData*) mpe -> Data;
2197         mpe = mpe ->Next;
2198     }
2199 
2200     if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) {
2201         PostMPE = (_cmsStageToneCurvesData*) mpe ->Data;
2202         mpe = mpe -> Next;
2203     }
2204 
2205     // That should be all
2206     if (mpe != NULL) {
2207         cmsSignalError(mpe->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT is not suitable to be saved as LUT16");
2208         return FALSE;
2209     }
2210 
2211     InputChannels  = cmsPipelineInputChannels(NewLUT);
2212     OutputChannels = cmsPipelineOutputChannels(NewLUT);
2213 
2214     if (clut == NULL)
2215         clutPoints = 0;
2216     else
2217         clutPoints    = clut->Params->nSamples[0];
2218 
2219     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) InputChannels)) return FALSE;
2220     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) OutputChannels)) return FALSE;
2221     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) clutPoints)) return FALSE;
2222     if (!_cmsWriteUInt8Number(io, 0)) return FALSE; // Padding
2223 

2224     if (MatMPE != NULL) {
2225 
2226                 for (i = 0; i < 9; i++)
2227                 {
2228                         if (!_cmsWrite15Fixed16Number(io, MatMPE->Double[i])) return FALSE;
2229                 }
2230 




2231     }
2232     else {
2233 
2234         if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE;
2235         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
2236         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
2237         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
2238         if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE;
2239         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
2240         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
2241         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
2242         if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE;
2243     }
2244 
2245 
2246     if (PreMPE != NULL) {
2247         if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) PreMPE ->TheCurves[0]->nEntries)) return FALSE;
2248     } else {
2249             if (!_cmsWriteUInt16Number(io, 2)) return FALSE;
2250     }
2251 
2252     if (PostMPE != NULL) {
2253         if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) PostMPE ->TheCurves[0]->nEntries)) return FALSE;
2254     } else {
2255         if (!_cmsWriteUInt16Number(io, 2)) return FALSE;
2256 
2257     }
2258 
2259     // The prelinearization table
2260 
2261     if (PreMPE != NULL) {
2262         if (!Write16bitTables(self ->ContextID, io, PreMPE)) return FALSE;
2263     }
2264     else {
2265         for (i=0; i < InputChannels; i++) {
2266 
2267             if (!_cmsWriteUInt16Number(io, 0)) return FALSE;
2268             if (!_cmsWriteUInt16Number(io, 0xffff)) return FALSE;
2269         }
2270     }
2271 
2272     nTabSize = uipow(OutputChannels, clutPoints, InputChannels);
2273     if (nTabSize == (cmsUInt32Number) -1) return FALSE;
2274     if (nTabSize > 0) {
2275         // The 3D CLUT.
2276         if (clut != NULL) {
2277             if (!_cmsWriteUInt16Array(io, nTabSize, clut->Tab.T)) return FALSE;
2278         }
2279     }
2280 
2281     // The postlinearization table
2282     if (PostMPE != NULL) {
2283         if (!Write16bitTables(self ->ContextID, io, PostMPE)) return FALSE;
2284     }
2285     else {
2286         for (i=0; i < OutputChannels; i++) {
2287 
2288             if (!_cmsWriteUInt16Number(io, 0)) return FALSE;
2289             if (!_cmsWriteUInt16Number(io, 0xffff)) return FALSE;
2290         }
2291     }
2292 
2293     return TRUE;
2294 
2295     cmsUNUSED_PARAMETER(nItems);
2296 }
2297 
2298 static
2299 void* Type_LUT16_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
2300 {
2301     return (void*) cmsPipelineDup((cmsPipeline*) Ptr);
2302 
2303     cmsUNUSED_PARAMETER(n);
2304     cmsUNUSED_PARAMETER(self);
2305 }
2306 
2307 static
2308 void Type_LUT16_Free(struct _cms_typehandler_struct* self, void* Ptr)
2309 {
2310     cmsPipelineFree((cmsPipeline*) Ptr);
2311     return;
2312 
2313     cmsUNUSED_PARAMETER(self);
2314 }
2315 
2316 
2317 // ********************************************************************************
2318 // Type cmsSigLutAToBType
2319 // ********************************************************************************
2320 
2321 
2322 // V4 stuff. Read matrix for LutAtoB and LutBtoA
2323 
2324 static
2325 cmsStage* ReadMatrix(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number Offset)
2326 {
2327     cmsFloat64Number dMat[3*3];
2328     cmsFloat64Number dOff[3];
2329     cmsStage* Mat;
2330 
2331     // Go to address
2332     if (!io -> Seek(io, Offset)) return NULL;
2333 
2334     // Read the Matrix
2335     if (!_cmsRead15Fixed16Number(io, &dMat[0])) return NULL;
2336     if (!_cmsRead15Fixed16Number(io, &dMat[1])) return NULL;
2337     if (!_cmsRead15Fixed16Number(io, &dMat[2])) return NULL;
2338     if (!_cmsRead15Fixed16Number(io, &dMat[3])) return NULL;
2339     if (!_cmsRead15Fixed16Number(io, &dMat[4])) return NULL;
2340     if (!_cmsRead15Fixed16Number(io, &dMat[5])) return NULL;
2341     if (!_cmsRead15Fixed16Number(io, &dMat[6])) return NULL;
2342     if (!_cmsRead15Fixed16Number(io, &dMat[7])) return NULL;
2343     if (!_cmsRead15Fixed16Number(io, &dMat[8])) return NULL;
2344 
2345     if (!_cmsRead15Fixed16Number(io, &dOff[0])) return NULL;
2346     if (!_cmsRead15Fixed16Number(io, &dOff[1])) return NULL;
2347     if (!_cmsRead15Fixed16Number(io, &dOff[2])) return NULL;
2348 
2349     Mat = cmsStageAllocMatrix(self ->ContextID, 3, 3, dMat, dOff);
2350 
2351      return Mat;
2352 }
2353 
2354 
2355 
2356 
2357 //  V4 stuff. Read CLUT part for LutAtoB and LutBtoA
2358 
2359 static
2360 cmsStage* ReadCLUT(struct _cms_typehandler_struct* self, cmsIOHANDLER* io,
2361                    cmsUInt32Number Offset, cmsUInt32Number InputChannels, cmsUInt32Number OutputChannels)
2362 {
2363     cmsUInt8Number  gridPoints8[cmsMAXCHANNELS]; // Number of grid points in each dimension.
2364     cmsUInt32Number GridPoints[cmsMAXCHANNELS], i;
2365     cmsUInt8Number  Precision;
2366     cmsStage* CLUT;
2367     _cmsStageCLutData* Data;
2368 
2369     if (!io -> Seek(io, Offset)) return NULL;
2370     if (io -> Read(io, gridPoints8, cmsMAXCHANNELS, 1) != 1) return NULL;
2371 
2372 
2373     for (i=0; i < cmsMAXCHANNELS; i++) {
2374 
2375         if (gridPoints8[i] == 1) return NULL; // Impossible value, 0 for no CLUT and then 2 at least
2376         GridPoints[i] = gridPoints8[i];
2377     }
2378 
2379     if (!_cmsReadUInt8Number(io, &Precision)) return NULL;
2380 
2381     if (!_cmsReadUInt8Number(io, NULL)) return NULL;
2382     if (!_cmsReadUInt8Number(io, NULL)) return NULL;
2383     if (!_cmsReadUInt8Number(io, NULL)) return NULL;
2384 
2385     CLUT = cmsStageAllocCLut16bitGranular(self ->ContextID, GridPoints, InputChannels, OutputChannels, NULL);
2386     if (CLUT == NULL) return NULL;
2387 
2388     Data = (_cmsStageCLutData*) CLUT ->Data;
2389 
2390     // Precision can be 1 or 2 bytes
2391     if (Precision == 1) {
2392 
2393         cmsUInt8Number  v;
2394 
2395         for (i=0; i < Data ->nEntries; i++) {
2396 
2397             if (io ->Read(io, &v, sizeof(cmsUInt8Number), 1) != 1) {
2398                 cmsStageFree(CLUT);
2399                 return NULL;
2400             }
2401             Data ->Tab.T[i] = FROM_8_TO_16(v);
2402         }
2403 
2404     }
2405     else
2406         if (Precision == 2) {
2407 
2408             if (!_cmsReadUInt16Array(io, Data->nEntries, Data ->Tab.T)) {
2409                 cmsStageFree(CLUT);
2410                 return NULL;
2411             }
2412         }
2413         else {
2414             cmsStageFree(CLUT);
2415             cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown precision of '%d'", Precision);
2416             return NULL;
2417         }
2418 
2419     return CLUT;
2420 }
2421 
2422 static
2423 cmsToneCurve* ReadEmbeddedCurve(struct _cms_typehandler_struct* self, cmsIOHANDLER* io)
2424 {
2425     cmsTagTypeSignature  BaseType;
2426     cmsUInt32Number nItems;
2427 
2428     BaseType = _cmsReadTypeBase(io);
2429     switch (BaseType) {
2430 
2431             case cmsSigCurveType:
2432                 return (cmsToneCurve*) Type_Curve_Read(self, io, &nItems, 0);
2433 
2434             case cmsSigParametricCurveType:
2435                 return (cmsToneCurve*) Type_ParametricCurve_Read(self, io, &nItems, 0);
2436 
2437             default:
2438                 {
2439                     char String[5];
2440 
2441                     _cmsTagSignature2String(String, (cmsTagSignature) BaseType);
2442                     cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown curve type '%s'", String);
2443                 }
2444                 return NULL;
2445     }
2446 }
2447 
2448 
2449 // Read a set of curves from specific offset
2450 static
2451 cmsStage* ReadSetOfCurves(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number Offset, cmsUInt32Number nCurves)
2452 {
2453     cmsToneCurve* Curves[cmsMAXCHANNELS];
2454     cmsUInt32Number i;
2455     cmsStage* Lin = NULL;
2456 
2457     if (nCurves > cmsMAXCHANNELS) return FALSE;
2458 
2459     if (!io -> Seek(io, Offset)) return FALSE;
2460 
2461     for (i=0; i < nCurves; i++)
2462         Curves[i] = NULL;
2463 
2464     for (i=0; i < nCurves; i++) {
2465 
2466         Curves[i] = ReadEmbeddedCurve(self, io);
2467         if (Curves[i] == NULL) goto Error;
2468         if (!_cmsReadAlignment(io)) goto Error;
2469 
2470     }
2471 
2472     Lin = cmsStageAllocToneCurves(self ->ContextID, nCurves, Curves);
2473 
2474 Error:
2475     for (i=0; i < nCurves; i++)
2476         cmsFreeToneCurve(Curves[i]);
2477 
2478     return Lin;
2479 }
2480 
2481 
2482 // LutAtoB type
2483 
2484 // This structure represents a colour transform. The type contains up to five processing
2485 // elements which are stored in the AtoBTag tag in the following order: a set of one
2486 // dimensional curves, a 3 by 3 matrix with offset terms, a set of one dimensional curves,
2487 // a multidimensional lookup table, and a set of one dimensional output curves.
2488 // Data are processed using these elements via the following sequence:
2489 //
2490 //("A" curves) -> (multidimensional lookup table - CLUT) -> ("M" curves) -> (matrix) -> ("B" curves).
2491 //
2492 /*
2493 It is possible to use any or all of these processing elements. At least one processing element
2494 must be included.Only the following combinations are allowed:
2495 
2496 B
2497 M - Matrix - B
2498 A - CLUT - B
2499 A - CLUT - M - Matrix - B
2500 
2501 */
2502 
2503 static
2504 void* Type_LUTA2B_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
2505 {
2506     cmsUInt32Number      BaseOffset;
2507     cmsUInt8Number       inputChan;      // Number of input channels
2508     cmsUInt8Number       outputChan;     // Number of output channels
2509     cmsUInt32Number      offsetB;        // Offset to first "B" curve
2510     cmsUInt32Number      offsetMat;      // Offset to matrix
2511     cmsUInt32Number      offsetM;        // Offset to first "M" curve
2512     cmsUInt32Number      offsetC;        // Offset to CLUT
2513     cmsUInt32Number      offsetA;        // Offset to first "A" curve
2514     cmsPipeline* NewLUT = NULL;
2515 
2516 
2517     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
2518 
2519     if (!_cmsReadUInt8Number(io, &inputChan)) return NULL;
2520     if (!_cmsReadUInt8Number(io, &outputChan)) return NULL;
2521 
2522     if (!_cmsReadUInt16Number(io, NULL)) return NULL;
2523 
2524     if (!_cmsReadUInt32Number(io, &offsetB)) return NULL;
2525     if (!_cmsReadUInt32Number(io, &offsetMat)) return NULL;
2526     if (!_cmsReadUInt32Number(io, &offsetM)) return NULL;
2527     if (!_cmsReadUInt32Number(io, &offsetC)) return NULL;
2528     if (!_cmsReadUInt32Number(io, &offsetA)) return NULL;
2529 
2530     if (inputChan == 0 || inputChan >= cmsMAXCHANNELS) return NULL;
2531     if (outputChan == 0 || outputChan >= cmsMAXCHANNELS) return NULL;
2532 
2533     // Allocates an empty LUT
2534     NewLUT = cmsPipelineAlloc(self ->ContextID, inputChan, outputChan);
2535     if (NewLUT == NULL) return NULL;
2536 
2537     if (offsetA!= 0) {
2538         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetA, inputChan)))
2539             goto Error;
2540     }
2541 
2542     if (offsetC != 0) {
2543         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadCLUT(self, io, BaseOffset + offsetC, inputChan, outputChan)))
2544             goto Error;
2545     }
2546 
2547     if (offsetM != 0) {
2548         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetM, outputChan)))
2549             goto Error;
2550     }
2551 
2552     if (offsetMat != 0) {
2553         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadMatrix(self, io, BaseOffset + offsetMat)))
2554             goto Error;
2555     }
2556 
2557     if (offsetB != 0) {
2558         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetB, outputChan)))
2559             goto Error;
2560     }
2561 
2562     *nItems = 1;
2563     return NewLUT;
2564 Error:
2565     cmsPipelineFree(NewLUT);
2566     return NULL;
2567 
2568     cmsUNUSED_PARAMETER(SizeOfTag);
2569 }
2570 
2571 // Write a set of curves
2572 static
2573 cmsBool  WriteMatrix(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsStage* mpe)
2574 {
2575         cmsUInt32Number i, n;
2576 
2577     _cmsStageMatrixData* m = (_cmsStageMatrixData*) mpe -> Data;
2578 
2579         n = mpe->InputChannels * mpe->OutputChannels;
2580 
2581         // Write the Matrix
2582         for (i = 0; i < n; i++)
2583         {
2584                 if (!_cmsWrite15Fixed16Number(io, m->Double[i])) return FALSE;
2585         }
2586 
2587         if (m->Offset != NULL) {
2588 
2589                 for (i = 0; i < mpe->OutputChannels; i++)
2590                 {
2591                         if (!_cmsWrite15Fixed16Number(io, m->Offset[i])) return FALSE;
2592                 }




2593         }
2594         else {
2595                 for (i = 0; i < mpe->OutputChannels; i++)
2596                 {
2597                         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
2598                 }


2599         }
2600 
2601 
2602     return TRUE;
2603 
2604     cmsUNUSED_PARAMETER(self);
2605 }
2606 
2607 
2608 // Write a set of curves
2609 static
2610 cmsBool WriteSetOfCurves(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsTagTypeSignature Type, cmsStage* mpe)
2611 {
2612     cmsUInt32Number i, n;
2613     cmsTagTypeSignature CurrentType;
2614     cmsToneCurve** Curves;
2615 
2616 
2617     n      = cmsStageOutputChannels(mpe);
2618     Curves = _cmsStageGetPtrToCurveSet(mpe);
2619 
2620     for (i=0; i < n; i++) {
2621 
2622         // If this is a table-based curve, use curve type even on V4
2623         CurrentType = Type;
2624 
2625         if ((Curves[i] ->nSegments == 0)||
2626             ((Curves[i]->nSegments == 2) && (Curves[i] ->Segments[1].Type == 0)) )
2627             CurrentType = cmsSigCurveType;
2628         else
2629         if (Curves[i] ->Segments[0].Type < 0)
2630             CurrentType = cmsSigCurveType;
2631 
2632         if (!_cmsWriteTypeBase(io, CurrentType)) return FALSE;
2633 
2634         switch (CurrentType) {
2635 
2636             case cmsSigCurveType:
2637                 if (!Type_Curve_Write(self, io, Curves[i], 1)) return FALSE;
2638                 break;
2639 
2640             case cmsSigParametricCurveType:
2641                 if (!Type_ParametricCurve_Write(self, io, Curves[i], 1)) return FALSE;
2642                 break;
2643 
2644             default:
2645                 {
2646                     char String[5];
2647 
2648                     _cmsTagSignature2String(String, (cmsTagSignature) Type);
2649                     cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown curve type '%s'", String);
2650                 }
2651                 return FALSE;
2652         }
2653 
2654         if (!_cmsWriteAlignment(io)) return FALSE;
2655     }
2656 
2657 
2658     return TRUE;
2659 }
2660 
2661 
2662 static
2663 cmsBool WriteCLUT(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt8Number  Precision, cmsStage* mpe)
2664 {
2665     cmsUInt8Number  gridPoints[cmsMAXCHANNELS]; // Number of grid points in each dimension.
2666     cmsUInt32Number i;
2667     _cmsStageCLutData* CLUT = ( _cmsStageCLutData*) mpe -> Data;
2668 
2669     if (CLUT ->HasFloatValues) {
2670          cmsSignalError(self ->ContextID, cmsERROR_NOT_SUITABLE, "Cannot save floating point data, CLUT are 8 or 16 bit only");
2671          return FALSE;
2672     }
2673 
2674     memset(gridPoints, 0, sizeof(gridPoints));
2675     for (i=0; i < (cmsUInt32Number) CLUT ->Params ->nInputs; i++)
2676         gridPoints[i] = (cmsUInt8Number) CLUT ->Params ->nSamples[i];
2677 
2678     if (!io -> Write(io, cmsMAXCHANNELS*sizeof(cmsUInt8Number), gridPoints)) return FALSE;
2679 
2680     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) Precision)) return FALSE;
2681     if (!_cmsWriteUInt8Number(io, 0)) return FALSE;
2682     if (!_cmsWriteUInt8Number(io, 0)) return FALSE;
2683     if (!_cmsWriteUInt8Number(io, 0)) return FALSE;
2684 
2685     // Precision can be 1 or 2 bytes
2686     if (Precision == 1) {
2687 
2688         for (i=0; i < CLUT->nEntries; i++) {
2689 
2690             if (!_cmsWriteUInt8Number(io, FROM_16_TO_8(CLUT->Tab.T[i]))) return FALSE;
2691         }
2692     }
2693     else
2694         if (Precision == 2) {
2695 
2696             if (!_cmsWriteUInt16Array(io, CLUT->nEntries, CLUT ->Tab.T)) return FALSE;
2697         }
2698         else {
2699              cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown precision of '%d'", Precision);
2700             return FALSE;
2701         }
2702 
2703     if (!_cmsWriteAlignment(io)) return FALSE;
2704 
2705     return TRUE;
2706 }
2707 
2708 
2709 
2710 
2711 static
2712 cmsBool Type_LUTA2B_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
2713 {
2714     cmsPipeline* Lut = (cmsPipeline*) Ptr;
2715     cmsUInt32Number inputChan, outputChan;
2716     cmsStage *A = NULL, *B = NULL, *M = NULL;
2717     cmsStage * Matrix = NULL;
2718     cmsStage * CLUT = NULL;
2719     cmsUInt32Number offsetB = 0, offsetMat = 0, offsetM = 0, offsetC = 0, offsetA = 0;
2720     cmsUInt32Number BaseOffset, DirectoryPos, CurrentPos;
2721 
2722     // Get the base for all offsets
2723     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
2724 
2725     if (Lut ->Elements != NULL)
2726         if (!cmsPipelineCheckAndRetreiveStages(Lut, 1, cmsSigCurveSetElemType, &B))
2727             if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, &M, &Matrix, &B))
2728                 if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, &A, &CLUT, &B))
2729                     if (!cmsPipelineCheckAndRetreiveStages(Lut, 5, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType,
2730                         cmsSigMatrixElemType, cmsSigCurveSetElemType, &A, &CLUT, &M, &Matrix, &B)) {
2731 
2732                             cmsSignalError(self->ContextID, cmsERROR_NOT_SUITABLE, "LUT is not suitable to be saved as LutAToB");
2733                             return FALSE;
2734                     }
2735 
2736     // Get input, output channels
2737     inputChan  = cmsPipelineInputChannels(Lut);
2738     outputChan = cmsPipelineOutputChannels(Lut);
2739 
2740     // Write channel count
2741     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) inputChan)) return FALSE;
2742     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) outputChan)) return FALSE;
2743     if (!_cmsWriteUInt16Number(io, 0)) return FALSE;
2744 
2745     // Keep directory to be filled latter
2746     DirectoryPos = io ->Tell(io);
2747 
2748     // Write the directory
2749     if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
2750     if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
2751     if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
2752     if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
2753     if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
2754 
2755     if (A != NULL) {
2756 
2757         offsetA = io ->Tell(io) - BaseOffset;
2758         if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, A)) return FALSE;
2759     }
2760 
2761     if (CLUT != NULL) {
2762         offsetC = io ->Tell(io) - BaseOffset;
2763         if (!WriteCLUT(self, io, (Lut ->SaveAs8Bits ? 1U : 2U), CLUT)) return FALSE;
2764 
2765     }
2766     if (M != NULL) {
2767 
2768         offsetM = io ->Tell(io) - BaseOffset;
2769         if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, M)) return FALSE;
2770     }
2771 
2772     if (Matrix != NULL) {
2773         offsetMat = io ->Tell(io) - BaseOffset;
2774         if (!WriteMatrix(self, io, Matrix)) return FALSE;
2775     }
2776 
2777     if (B != NULL) {
2778 
2779         offsetB = io ->Tell(io) - BaseOffset;
2780         if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, B)) return FALSE;
2781     }
2782 
2783     CurrentPos = io ->Tell(io);
2784 
2785     if (!io ->Seek(io, DirectoryPos)) return FALSE;
2786 
2787     if (!_cmsWriteUInt32Number(io, offsetB)) return FALSE;
2788     if (!_cmsWriteUInt32Number(io, offsetMat)) return FALSE;
2789     if (!_cmsWriteUInt32Number(io, offsetM)) return FALSE;
2790     if (!_cmsWriteUInt32Number(io, offsetC)) return FALSE;
2791     if (!_cmsWriteUInt32Number(io, offsetA)) return FALSE;
2792 
2793     if (!io ->Seek(io, CurrentPos)) return FALSE;
2794 
2795     return TRUE;
2796 
2797     cmsUNUSED_PARAMETER(nItems);
2798 }
2799 
2800 
2801 static
2802 void* Type_LUTA2B_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
2803 {
2804     return (void*) cmsPipelineDup((cmsPipeline*) Ptr);
2805 
2806     cmsUNUSED_PARAMETER(n);
2807     cmsUNUSED_PARAMETER(self);
2808 }
2809 
2810 static
2811 void Type_LUTA2B_Free(struct _cms_typehandler_struct* self, void* Ptr)
2812 {
2813     cmsPipelineFree((cmsPipeline*) Ptr);
2814     return;
2815 
2816     cmsUNUSED_PARAMETER(self);
2817 }
2818 
2819 
2820 // LutBToA type
2821 
2822 static
2823 void* Type_LUTB2A_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
2824 {
2825     cmsUInt8Number       inputChan;      // Number of input channels
2826     cmsUInt8Number       outputChan;     // Number of output channels
2827     cmsUInt32Number      BaseOffset;     // Actual position in file
2828     cmsUInt32Number      offsetB;        // Offset to first "B" curve
2829     cmsUInt32Number      offsetMat;      // Offset to matrix
2830     cmsUInt32Number      offsetM;        // Offset to first "M" curve
2831     cmsUInt32Number      offsetC;        // Offset to CLUT
2832     cmsUInt32Number      offsetA;        // Offset to first "A" curve
2833     cmsPipeline* NewLUT = NULL;
2834 
2835 
2836     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
2837 
2838     if (!_cmsReadUInt8Number(io, &inputChan)) return NULL;
2839     if (!_cmsReadUInt8Number(io, &outputChan)) return NULL;
2840 
2841     if (inputChan == 0 || inputChan >= cmsMAXCHANNELS) return NULL;
2842     if (outputChan == 0 || outputChan >= cmsMAXCHANNELS) return NULL;
2843 
2844     // Padding
2845     if (!_cmsReadUInt16Number(io, NULL)) return NULL;
2846 
2847     if (!_cmsReadUInt32Number(io, &offsetB)) return NULL;
2848     if (!_cmsReadUInt32Number(io, &offsetMat)) return NULL;
2849     if (!_cmsReadUInt32Number(io, &offsetM)) return NULL;
2850     if (!_cmsReadUInt32Number(io, &offsetC)) return NULL;
2851     if (!_cmsReadUInt32Number(io, &offsetA)) return NULL;
2852 
2853     // Allocates an empty LUT
2854     NewLUT = cmsPipelineAlloc(self ->ContextID, inputChan, outputChan);
2855     if (NewLUT == NULL) return NULL;
2856 
2857     if (offsetB != 0) {
2858         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetB, inputChan)))
2859             goto Error;
2860     }
2861 
2862     if (offsetMat != 0) {
2863         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadMatrix(self, io, BaseOffset + offsetMat)))
2864             goto Error;
2865     }
2866 
2867     if (offsetM != 0) {
2868         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetM, inputChan)))
2869             goto Error;
2870     }
2871 
2872     if (offsetC != 0) {
2873         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadCLUT(self, io, BaseOffset + offsetC, inputChan, outputChan)))
2874             goto Error;
2875     }
2876 
2877     if (offsetA!= 0) {
2878         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetA, outputChan)))
2879             goto Error;
2880     }
2881 
2882     *nItems = 1;
2883     return NewLUT;
2884 Error:
2885     cmsPipelineFree(NewLUT);
2886     return NULL;
2887 
2888     cmsUNUSED_PARAMETER(SizeOfTag);
2889 }
2890 
2891 
2892 /*
2893 B
2894 B - Matrix - M
2895 B - CLUT - A
2896 B - Matrix - M - CLUT - A
2897 */
2898 
2899 static
2900 cmsBool  Type_LUTB2A_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
2901 {
2902     cmsPipeline* Lut = (cmsPipeline*) Ptr;
2903     cmsUInt32Number inputChan, outputChan;
2904     cmsStage *A = NULL, *B = NULL, *M = NULL;
2905     cmsStage *Matrix = NULL;
2906     cmsStage *CLUT = NULL;
2907     cmsUInt32Number offsetB = 0, offsetMat = 0, offsetM = 0, offsetC = 0, offsetA = 0;
2908     cmsUInt32Number BaseOffset, DirectoryPos, CurrentPos;
2909 
2910 
2911     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
2912 
2913     if (!cmsPipelineCheckAndRetreiveStages(Lut, 1, cmsSigCurveSetElemType, &B))
2914         if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, &B, &Matrix, &M))
2915             if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, &B, &CLUT, &A))
2916                 if (!cmsPipelineCheckAndRetreiveStages(Lut, 5, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType,
2917                     cmsSigCLutElemType, cmsSigCurveSetElemType, &B, &Matrix, &M, &CLUT, &A)) {
2918                         cmsSignalError(self->ContextID, cmsERROR_NOT_SUITABLE, "LUT is not suitable to be saved as LutBToA");
2919                         return FALSE;
2920                 }
2921 
2922     inputChan  = cmsPipelineInputChannels(Lut);
2923     outputChan = cmsPipelineOutputChannels(Lut);
2924 
2925     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) inputChan)) return FALSE;
2926     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) outputChan)) return FALSE;
2927     if (!_cmsWriteUInt16Number(io, 0)) return FALSE;
2928 
2929     DirectoryPos = io ->Tell(io);
2930 
2931     if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
2932     if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
2933     if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
2934     if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
2935     if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
2936 
2937     if (A != NULL) {
2938 
2939         offsetA = io ->Tell(io) - BaseOffset;
2940         if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, A)) return FALSE;
2941     }
2942 
2943     if (CLUT != NULL) {
2944         offsetC = io ->Tell(io) - BaseOffset;
2945         if (!WriteCLUT(self, io, (Lut ->SaveAs8Bits ? 1U : 2U), CLUT)) return FALSE;
2946 
2947     }
2948     if (M != NULL) {
2949 
2950         offsetM = io ->Tell(io) - BaseOffset;
2951         if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, M)) return FALSE;
2952     }
2953 
2954     if (Matrix != NULL) {
2955         offsetMat = io ->Tell(io) - BaseOffset;
2956         if (!WriteMatrix(self, io, Matrix)) return FALSE;
2957     }
2958 
2959     if (B != NULL) {
2960 
2961         offsetB = io ->Tell(io) - BaseOffset;
2962         if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, B)) return FALSE;
2963     }
2964 
2965     CurrentPos = io ->Tell(io);
2966 
2967     if (!io ->Seek(io, DirectoryPos)) return FALSE;
2968 
2969     if (!_cmsWriteUInt32Number(io, offsetB)) return FALSE;
2970     if (!_cmsWriteUInt32Number(io, offsetMat)) return FALSE;
2971     if (!_cmsWriteUInt32Number(io, offsetM)) return FALSE;
2972     if (!_cmsWriteUInt32Number(io, offsetC)) return FALSE;
2973     if (!_cmsWriteUInt32Number(io, offsetA)) return FALSE;
2974 
2975     if (!io ->Seek(io, CurrentPos)) return FALSE;
2976 
2977     return TRUE;
2978 
2979     cmsUNUSED_PARAMETER(nItems);
2980 }
2981 
2982 
2983 
2984 static
2985 void* Type_LUTB2A_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
2986 {
2987     return (void*) cmsPipelineDup((cmsPipeline*) Ptr);
2988 
2989     cmsUNUSED_PARAMETER(n);
2990     cmsUNUSED_PARAMETER(self);
2991 }
2992 
2993 static
2994 void Type_LUTB2A_Free(struct _cms_typehandler_struct* self, void* Ptr)
2995 {
2996     cmsPipelineFree((cmsPipeline*) Ptr);
2997     return;
2998 
2999     cmsUNUSED_PARAMETER(self);
3000 }
3001 
3002 
3003 
3004 // ********************************************************************************
3005 // Type cmsSigColorantTableType
3006 // ********************************************************************************
3007 /*
3008 The purpose of this tag is to identify the colorants used in the profile by a
3009 unique name and set of XYZ or L*a*b* values to give the colorant an unambiguous
3010 value. The first colorant listed is the colorant of the first device channel of
3011 a lut tag. The second colorant listed is the colorant of the second device channel
3012 of a lut tag, and so on.
3013 */
3014 
3015 static
3016 void *Type_ColorantTable_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
3017 {
3018     cmsUInt32Number i, Count;
3019     cmsNAMEDCOLORLIST* List;
3020     char Name[34];
3021     cmsUInt16Number PCS[3];
3022 
3023 
3024     if (!_cmsReadUInt32Number(io, &Count)) return NULL;
3025 
3026     if (Count > cmsMAXCHANNELS) {
3027         cmsSignalError(self->ContextID, cmsERROR_RANGE, "Too many colorants '%d'", Count);
3028         return NULL;
3029     }
3030 
3031     List = cmsAllocNamedColorList(self ->ContextID, Count, 0, "", "");
3032     for (i=0; i < Count; i++) {
3033 
3034         if (io ->Read(io, Name, 32, 1) != 1) goto Error;
3035         Name[32] = 0;
3036 
3037         if (!_cmsReadUInt16Array(io, 3, PCS)) goto Error;
3038 
3039         if (!cmsAppendNamedColor(List, Name, PCS, NULL)) goto Error;
3040 
3041     }
3042 
3043     *nItems = 1;
3044     return List;
3045 
3046 Error:
3047     *nItems = 0;
3048     cmsFreeNamedColorList(List);
3049     return NULL;
3050 
3051     cmsUNUSED_PARAMETER(SizeOfTag);
3052 }
3053 
3054 
3055 
3056 // Saves a colorant table. It is using the named color structure for simplicity sake
3057 static
3058 cmsBool  Type_ColorantTable_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
3059 {
3060     cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) Ptr;
3061     cmsUInt32Number i, nColors;
3062 
3063     nColors = cmsNamedColorCount(NamedColorList);
3064 
3065     if (!_cmsWriteUInt32Number(io, nColors)) return FALSE;
3066 
3067     for (i=0; i < nColors; i++) {
3068 
3069         char root[cmsMAX_PATH];
3070         cmsUInt16Number PCS[3];
3071 
3072         memset(root, 0, sizeof(root));
3073 
3074         if (!cmsNamedColorInfo(NamedColorList, i, root, NULL, NULL, PCS, NULL)) return 0;
3075         root[32] = 0;
3076 
3077         if (!io ->Write(io, 32, root)) return FALSE;
3078         if (!_cmsWriteUInt16Array(io, 3, PCS)) return FALSE;
3079     }
3080 
3081     return TRUE;
3082 
3083     cmsUNUSED_PARAMETER(nItems);
3084     cmsUNUSED_PARAMETER(self);
3085 }
3086 
3087 
3088 static
3089 void* Type_ColorantTable_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n)
3090 {
3091     cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) Ptr;
3092     return (void*) cmsDupNamedColorList(nc);
3093 
3094     cmsUNUSED_PARAMETER(n);
3095     cmsUNUSED_PARAMETER(self);
3096 }
3097 
3098 
3099 static
3100 void Type_ColorantTable_Free(struct _cms_typehandler_struct* self, void* Ptr)
3101 {
3102     cmsFreeNamedColorList((cmsNAMEDCOLORLIST*) Ptr);
3103     return;
3104 
3105     cmsUNUSED_PARAMETER(self);
3106 }
3107 
3108 
3109 // ********************************************************************************
3110 // Type cmsSigNamedColor2Type
3111 // ********************************************************************************
3112 //
3113 //The namedColor2Type is a count value and array of structures that provide color
3114 //coordinates for 7-bit ASCII color names. For each named color, a PCS and optional
3115 //device representation of the color are given. Both representations are 16-bit values.
3116 //The device representation corresponds to the header's 'color space of data' field.
3117 //This representation should be consistent with the 'number of device components'
3118 //field in the namedColor2Type. If this field is 0, device coordinates are not provided.
3119 //The PCS representation corresponds to the header's PCS field. The PCS representation
3120 //is always provided. Color names are fixed-length, 32-byte fields including null
3121 //termination. In order to maintain maximum portability, it is strongly recommended
3122 //that special characters of the 7-bit ASCII set not be used.
3123 
3124 static
3125 void *Type_NamedColor_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
3126 {
3127 
3128     cmsUInt32Number      vendorFlag;     // Bottom 16 bits for ICC use
3129     cmsUInt32Number      count;          // Count of named colors
3130     cmsUInt32Number      nDeviceCoords;  // Num of device coordinates
3131     char                 prefix[32];     // Prefix for each color name
3132     char                 suffix[32];     // Suffix for each color name
3133     cmsNAMEDCOLORLIST*   v;
3134     cmsUInt32Number      i;
3135 
3136 
3137     *nItems = 0;
3138     if (!_cmsReadUInt32Number(io, &vendorFlag)) return NULL;
3139     if (!_cmsReadUInt32Number(io, &count)) return NULL;
3140     if (!_cmsReadUInt32Number(io, &nDeviceCoords)) return NULL;
3141 
3142     if (io -> Read(io, prefix, 32, 1) != 1) return NULL;
3143     if (io -> Read(io, suffix, 32, 1) != 1) return NULL;
3144 
3145     prefix[31] = suffix[31] = 0;
3146 
3147     v = cmsAllocNamedColorList(self ->ContextID, count, nDeviceCoords, prefix, suffix);
3148     if (v == NULL) {
3149         cmsSignalError(self->ContextID, cmsERROR_RANGE, "Too many named colors '%d'", count);
3150         return NULL;
3151     }
3152 
3153     if (nDeviceCoords > cmsMAXCHANNELS) {
3154         cmsSignalError(self->ContextID, cmsERROR_RANGE, "Too many device coordinates '%d'", nDeviceCoords);
3155         goto Error;
3156     }
3157     for (i=0; i < count; i++) {
3158 
3159         cmsUInt16Number PCS[3];
3160         cmsUInt16Number Colorant[cmsMAXCHANNELS];
3161         char Root[33];
3162 
3163         memset(Colorant, 0, sizeof(Colorant));
3164         if (io -> Read(io, Root, 32, 1) != 1) goto Error;
3165         Root[32] = 0;  // To prevent exploits
3166 
3167         if (!_cmsReadUInt16Array(io, 3, PCS)) goto Error;
3168         if (!_cmsReadUInt16Array(io, nDeviceCoords, Colorant)) goto Error;
3169 
3170         if (!cmsAppendNamedColor(v, Root, PCS, Colorant)) goto Error;
3171     }
3172 
3173     *nItems = 1;
3174     return (void*) v ;
3175 
3176 Error:
3177     cmsFreeNamedColorList(v);
3178     return NULL;
3179 
3180     cmsUNUSED_PARAMETER(SizeOfTag);
3181 }
3182 
3183 
3184 // Saves a named color list into a named color profile
3185 static
3186 cmsBool Type_NamedColor_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
3187 {
3188     cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) Ptr;
3189     char                prefix[33];     // Prefix for each color name
3190     char                suffix[33];     // Suffix for each color name
3191     cmsUInt32Number     i, nColors;
3192 
3193     nColors = cmsNamedColorCount(NamedColorList);
3194 
3195     if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
3196     if (!_cmsWriteUInt32Number(io, nColors)) return FALSE;
3197     if (!_cmsWriteUInt32Number(io, NamedColorList ->ColorantCount)) return FALSE;
3198 
3199     strncpy(prefix, (const char*) NamedColorList->Prefix, 32);
3200     strncpy(suffix, (const char*) NamedColorList->Suffix, 32);
3201 
3202     suffix[32] = prefix[32] = 0;
3203 
3204     if (!io ->Write(io, 32, prefix)) return FALSE;
3205     if (!io ->Write(io, 32, suffix)) return FALSE;
3206 
3207     for (i=0; i < nColors; i++) {
3208 
3209        cmsUInt16Number PCS[3];
3210        cmsUInt16Number Colorant[cmsMAXCHANNELS];
3211        char Root[cmsMAX_PATH];
3212 
3213         if (!cmsNamedColorInfo(NamedColorList, i, Root, NULL, NULL, PCS, Colorant)) return 0;
3214         Root[32] = 0;
3215         if (!io ->Write(io, 32 , Root)) return FALSE;
3216         if (!_cmsWriteUInt16Array(io, 3, PCS)) return FALSE;
3217         if (!_cmsWriteUInt16Array(io, NamedColorList ->ColorantCount, Colorant)) return FALSE;
3218     }
3219 
3220     return TRUE;
3221 
3222     cmsUNUSED_PARAMETER(nItems);
3223     cmsUNUSED_PARAMETER(self);
3224 }
3225 
3226 static
3227 void* Type_NamedColor_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n)
3228 {
3229     cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) Ptr;
3230 
3231     return (void*) cmsDupNamedColorList(nc);
3232 
3233     cmsUNUSED_PARAMETER(n);
3234     cmsUNUSED_PARAMETER(self);
3235 }
3236 
3237 
3238 static
3239 void Type_NamedColor_Free(struct _cms_typehandler_struct* self, void* Ptr)
3240 {
3241     cmsFreeNamedColorList((cmsNAMEDCOLORLIST*) Ptr);
3242     return;
3243 
3244     cmsUNUSED_PARAMETER(self);
3245 }
3246 
3247 
3248 // ********************************************************************************
3249 // Type cmsSigProfileSequenceDescType
3250 // ********************************************************************************
3251 
3252 // This type is an array of structures, each of which contains information from the
3253 // header fields and tags from the original profiles which were combined to create
3254 // the final profile. The order of the structures is the order in which the profiles
3255 // were combined and includes a structure for the final profile. This provides a
3256 // description of the profile sequence from source to destination,
3257 // typically used with the DeviceLink profile.
3258 
3259 static
3260 cmsBool ReadEmbeddedText(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU** mlu, cmsUInt32Number SizeOfTag)
3261 {
3262     cmsTagTypeSignature  BaseType;
3263     cmsUInt32Number nItems;
3264 
3265     BaseType = _cmsReadTypeBase(io);
3266 
3267     switch (BaseType) {
3268 
3269        case cmsSigTextType:
3270            if (*mlu) cmsMLUfree(*mlu);
3271            *mlu = (cmsMLU*)Type_Text_Read(self, io, &nItems, SizeOfTag);
3272            return (*mlu != NULL);
3273 
3274        case cmsSigTextDescriptionType:
3275            if (*mlu) cmsMLUfree(*mlu);
3276            *mlu =  (cmsMLU*) Type_Text_Description_Read(self, io, &nItems, SizeOfTag);
3277            return (*mlu != NULL);
3278 
3279            /*
3280            TBD: Size is needed for MLU, and we have no idea on which is the available size
3281            */
3282 
3283        case cmsSigMultiLocalizedUnicodeType:
3284            if (*mlu) cmsMLUfree(*mlu);
3285            *mlu =  (cmsMLU*) Type_MLU_Read(self, io, &nItems, SizeOfTag);
3286            return (*mlu != NULL);
3287 
3288        default: return FALSE;
3289     }
3290 }
3291 
3292 
3293 static
3294 void *Type_ProfileSequenceDesc_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
3295 {
3296     cmsSEQ* OutSeq;
3297     cmsUInt32Number i, Count;
3298 
3299     *nItems = 0;
3300 
3301     if (!_cmsReadUInt32Number(io, &Count)) return NULL;
3302 
3303     if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL;
3304     SizeOfTag -= sizeof(cmsUInt32Number);
3305 
3306 
3307     OutSeq = cmsAllocProfileSequenceDescription(self ->ContextID, Count);
3308     if (OutSeq == NULL) return NULL;
3309 
3310     OutSeq ->n = Count;
3311 
3312     // Get structures as well
3313 
3314     for (i=0; i < Count; i++) {
3315 
3316         cmsPSEQDESC* sec = &OutSeq -> seq[i];
3317 
3318         if (!_cmsReadUInt32Number(io, &sec ->deviceMfg)) goto Error;
3319         if (SizeOfTag < sizeof(cmsUInt32Number)) goto Error;
3320         SizeOfTag -= sizeof(cmsUInt32Number);
3321 
3322         if (!_cmsReadUInt32Number(io, &sec ->deviceModel)) goto Error;
3323         if (SizeOfTag < sizeof(cmsUInt32Number)) goto Error;
3324         SizeOfTag -= sizeof(cmsUInt32Number);
3325 
3326         if (!_cmsReadUInt64Number(io, &sec ->attributes)) goto Error;
3327         if (SizeOfTag < sizeof(cmsUInt64Number)) goto Error;
3328         SizeOfTag -= sizeof(cmsUInt64Number);
3329 
3330         if (!_cmsReadUInt32Number(io, (cmsUInt32Number *)&sec ->technology)) goto Error;
3331         if (SizeOfTag < sizeof(cmsUInt32Number)) goto Error;
3332         SizeOfTag -= sizeof(cmsUInt32Number);
3333 
3334         if (!ReadEmbeddedText(self, io, &sec ->Manufacturer, SizeOfTag)) goto Error;
3335         if (!ReadEmbeddedText(self, io, &sec ->Model, SizeOfTag)) goto Error;
3336     }
3337 
3338     *nItems = 1;
3339     return OutSeq;
3340 
3341 Error:
3342     cmsFreeProfileSequenceDescription(OutSeq);
3343     return NULL;
3344 }
3345 
3346 
3347 // Aux--Embed a text description type. It can be of type text description or multilocalized unicode
3348 // and it depends of the version number passed on cmsTagDescriptor structure instead of stack
3349 static
3350 cmsBool  SaveDescription(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* Text)
3351 {
3352     if (self ->ICCVersion < 0x4000000) {
3353 
3354         if (!_cmsWriteTypeBase(io, cmsSigTextDescriptionType)) return FALSE;
3355         return Type_Text_Description_Write(self, io, Text, 1);
3356     }
3357     else {
3358         if (!_cmsWriteTypeBase(io, cmsSigMultiLocalizedUnicodeType)) return FALSE;
3359         return Type_MLU_Write(self, io, Text, 1);
3360     }
3361 }
3362 
3363 
3364 static
3365 cmsBool  Type_ProfileSequenceDesc_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
3366 {
3367     cmsSEQ* Seq = (cmsSEQ*) Ptr;
3368     cmsUInt32Number i;
3369 
3370     if (!_cmsWriteUInt32Number(io, Seq->n)) return FALSE;
3371 
3372     for (i=0; i < Seq ->n; i++) {
3373 
3374         cmsPSEQDESC* sec = &Seq -> seq[i];
3375 
3376         if (!_cmsWriteUInt32Number(io, sec ->deviceMfg)) return FALSE;
3377         if (!_cmsWriteUInt32Number(io, sec ->deviceModel)) return FALSE;
3378         if (!_cmsWriteUInt64Number(io, &sec ->attributes)) return FALSE;
3379         if (!_cmsWriteUInt32Number(io, sec ->technology)) return FALSE;
3380 
3381         if (!SaveDescription(self, io, sec ->Manufacturer)) return FALSE;
3382         if (!SaveDescription(self, io, sec ->Model)) return FALSE;
3383     }
3384 
3385      return TRUE;
3386 
3387      cmsUNUSED_PARAMETER(nItems);
3388 }
3389 
3390 
3391 static
3392 void* Type_ProfileSequenceDesc_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n)
3393 {
3394     return (void*) cmsDupProfileSequenceDescription((cmsSEQ*) Ptr);
3395 
3396     cmsUNUSED_PARAMETER(n);
3397     cmsUNUSED_PARAMETER(self);
3398 }
3399 
3400 static
3401 void Type_ProfileSequenceDesc_Free(struct _cms_typehandler_struct* self, void* Ptr)
3402 {
3403     cmsFreeProfileSequenceDescription((cmsSEQ*) Ptr);
3404     return;
3405 
3406     cmsUNUSED_PARAMETER(self);
3407 }
3408 
3409 
3410 // ********************************************************************************
3411 // Type cmsSigProfileSequenceIdType
3412 // ********************************************************************************
3413 /*
3414 In certain workflows using ICC Device Link Profiles, it is necessary to identify the
3415 original profiles that were combined to create the Device Link Profile.
3416 This type is an array of structures, each of which contains information for
3417 identification of a profile used in a sequence
3418 */
3419 
3420 
3421 static
3422 cmsBool ReadSeqID(struct _cms_typehandler_struct* self,
3423                                              cmsIOHANDLER* io,
3424                                              void* Cargo,
3425                                              cmsUInt32Number n,
3426                                              cmsUInt32Number SizeOfTag)
3427 {
3428     cmsSEQ* OutSeq = (cmsSEQ*) Cargo;
3429     cmsPSEQDESC* seq = &OutSeq ->seq[n];
3430 
3431     if (io -> Read(io, seq ->ProfileID.ID8, 16, 1) != 1) return FALSE;
3432     if (!ReadEmbeddedText(self, io, &seq ->Description, SizeOfTag)) return FALSE;
3433 
3434     return TRUE;
3435 }
3436 
3437 
3438 
3439 static
3440 void *Type_ProfileSequenceId_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
3441 {
3442     cmsSEQ* OutSeq;
3443     cmsUInt32Number Count;
3444     cmsUInt32Number BaseOffset;
3445 
3446     *nItems = 0;
3447 
3448     // Get actual position as a basis for element offsets
3449     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
3450 
3451     // Get table count
3452     if (!_cmsReadUInt32Number(io, &Count)) return NULL;
3453     SizeOfTag -= sizeof(cmsUInt32Number);
3454 
3455     // Allocate an empty structure
3456     OutSeq = cmsAllocProfileSequenceDescription(self ->ContextID, Count);
3457     if (OutSeq == NULL) return NULL;
3458 
3459 
3460     // Read the position table
3461     if (!ReadPositionTable(self, io, Count, BaseOffset, OutSeq, ReadSeqID)) {
3462 
3463         cmsFreeProfileSequenceDescription(OutSeq);
3464         return NULL;
3465     }
3466 
3467     // Success
3468     *nItems = 1;
3469     return OutSeq;
3470 
3471 }
3472 
3473 
3474 static
3475 cmsBool WriteSeqID(struct _cms_typehandler_struct* self,
3476                                              cmsIOHANDLER* io,
3477                                              void* Cargo,
3478                                              cmsUInt32Number n,
3479                                              cmsUInt32Number SizeOfTag)
3480 {
3481     cmsSEQ* Seq = (cmsSEQ*) Cargo;
3482 
3483     if (!io ->Write(io, 16, Seq ->seq[n].ProfileID.ID8)) return FALSE;
3484 
3485     // Store here the MLU
3486     if (!SaveDescription(self, io, Seq ->seq[n].Description)) return FALSE;
3487 
3488     return TRUE;
3489 
3490     cmsUNUSED_PARAMETER(SizeOfTag);
3491 }
3492 
3493 static
3494 cmsBool  Type_ProfileSequenceId_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
3495 {
3496     cmsSEQ* Seq = (cmsSEQ*) Ptr;
3497     cmsUInt32Number BaseOffset;
3498 
3499     // Keep the base offset
3500     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
3501 
3502     // This is the table count
3503     if (!_cmsWriteUInt32Number(io, Seq ->n)) return FALSE;
3504 
3505     // This is the position table and content
3506     if (!WritePositionTable(self, io, 0, Seq ->n, BaseOffset, Seq, WriteSeqID)) return FALSE;
3507 
3508     return TRUE;
3509 
3510     cmsUNUSED_PARAMETER(nItems);
3511 }
3512 
3513 static
3514 void* Type_ProfileSequenceId_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n)
3515 {
3516     return (void*) cmsDupProfileSequenceDescription((cmsSEQ*) Ptr);
3517 
3518     cmsUNUSED_PARAMETER(n);
3519     cmsUNUSED_PARAMETER(self);
3520 }
3521 
3522 static
3523 void Type_ProfileSequenceId_Free(struct _cms_typehandler_struct* self, void* Ptr)
3524 {
3525     cmsFreeProfileSequenceDescription((cmsSEQ*) Ptr);
3526     return;
3527 
3528     cmsUNUSED_PARAMETER(self);
3529 }
3530 
3531 
3532 // ********************************************************************************
3533 // Type cmsSigUcrBgType
3534 // ********************************************************************************
3535 /*
3536 This type contains curves representing the under color removal and black
3537 generation and a text string which is a general description of the method used
3538 for the ucr/bg.
3539 */
3540 
3541 static
3542 void *Type_UcrBg_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
3543 {
3544     cmsUcrBg* n = (cmsUcrBg*) _cmsMallocZero(self ->ContextID, sizeof(cmsUcrBg));
3545     cmsUInt32Number CountUcr, CountBg;
3546     char* ASCIIString;
3547 
3548     *nItems = 0;
3549     if (n == NULL) return NULL;
3550 
3551     // First curve is Under color removal
3552     if (!_cmsReadUInt32Number(io, &CountUcr)) return NULL;
3553     if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL;
3554     SizeOfTag -= sizeof(cmsUInt32Number);
3555 
3556     n ->Ucr = cmsBuildTabulatedToneCurve16(self ->ContextID, CountUcr, NULL);
3557     if (n ->Ucr == NULL) return NULL;
3558 
3559     if (!_cmsReadUInt16Array(io, CountUcr, n ->Ucr->Table16)) return NULL;
3560     if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL;
3561     SizeOfTag -= CountUcr * sizeof(cmsUInt16Number);
3562 
3563     // Second curve is Black generation
3564     if (!_cmsReadUInt32Number(io, &CountBg)) return NULL;
3565     if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL;
3566     SizeOfTag -= sizeof(cmsUInt32Number);
3567 
3568     n ->Bg = cmsBuildTabulatedToneCurve16(self ->ContextID, CountBg, NULL);
3569     if (n ->Bg == NULL) return NULL;
3570     if (!_cmsReadUInt16Array(io, CountBg, n ->Bg->Table16)) return NULL;
3571     if (SizeOfTag < CountBg * sizeof(cmsUInt16Number)) return NULL;
3572     SizeOfTag -= CountBg * sizeof(cmsUInt16Number);
3573     if (SizeOfTag == UINT_MAX) return NULL;
3574 
3575     // Now comes the text. The length is specified by the tag size
3576     n ->Desc = cmsMLUalloc(self ->ContextID, 1);
3577     if (n ->Desc == NULL) return NULL;
3578 
3579     ASCIIString = (char*) _cmsMalloc(self ->ContextID, SizeOfTag + 1);
3580     if (io ->Read(io, ASCIIString, sizeof(char), SizeOfTag) != SizeOfTag) return NULL;
3581     ASCIIString[SizeOfTag] = 0;
3582     cmsMLUsetASCII(n ->Desc, cmsNoLanguage, cmsNoCountry, ASCIIString);
3583     _cmsFree(self ->ContextID, ASCIIString);
3584 
3585     *nItems = 1;
3586     return (void*) n;
3587 }
3588 
3589 static
3590 cmsBool  Type_UcrBg_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
3591 {
3592     cmsUcrBg* Value = (cmsUcrBg*) Ptr;
3593     cmsUInt32Number TextSize;
3594     char* Text;
3595 
3596     // First curve is Under color removal
3597     if (!_cmsWriteUInt32Number(io, Value ->Ucr ->nEntries)) return FALSE;
3598     if (!_cmsWriteUInt16Array(io, Value ->Ucr ->nEntries, Value ->Ucr ->Table16)) return FALSE;
3599 
3600     // Then black generation
3601     if (!_cmsWriteUInt32Number(io, Value ->Bg ->nEntries)) return FALSE;
3602     if (!_cmsWriteUInt16Array(io, Value ->Bg ->nEntries, Value ->Bg ->Table16)) return FALSE;
3603 
3604     // Now comes the text. The length is specified by the tag size
3605     TextSize = cmsMLUgetASCII(Value ->Desc, cmsNoLanguage, cmsNoCountry, NULL, 0);
3606     Text     = (char*) _cmsMalloc(self ->ContextID, TextSize);
3607     if (cmsMLUgetASCII(Value ->Desc, cmsNoLanguage, cmsNoCountry, Text, TextSize) != TextSize) return FALSE;
3608 
3609     if (!io ->Write(io, TextSize, Text)) return FALSE;
3610     _cmsFree(self ->ContextID, Text);
3611 
3612     return TRUE;
3613 
3614     cmsUNUSED_PARAMETER(nItems);
3615 }
3616 
3617 static
3618 void* Type_UcrBg_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
3619 {
3620     cmsUcrBg* Src = (cmsUcrBg*) Ptr;
3621     cmsUcrBg* NewUcrBg = (cmsUcrBg*) _cmsMallocZero(self ->ContextID, sizeof(cmsUcrBg));
3622 
3623     if (NewUcrBg == NULL) return NULL;
3624 
3625     NewUcrBg ->Bg   = cmsDupToneCurve(Src ->Bg);
3626     NewUcrBg ->Ucr  = cmsDupToneCurve(Src ->Ucr);
3627     NewUcrBg ->Desc = cmsMLUdup(Src ->Desc);
3628 
3629     return (void*) NewUcrBg;
3630 
3631     cmsUNUSED_PARAMETER(n);
3632 }
3633 
3634 static
3635 void Type_UcrBg_Free(struct _cms_typehandler_struct* self, void *Ptr)
3636 {
3637    cmsUcrBg* Src = (cmsUcrBg*) Ptr;
3638 
3639    if (Src ->Ucr) cmsFreeToneCurve(Src ->Ucr);
3640    if (Src ->Bg)  cmsFreeToneCurve(Src ->Bg);
3641    if (Src ->Desc) cmsMLUfree(Src ->Desc);
3642 
3643    _cmsFree(self ->ContextID, Ptr);
3644 }
3645 
3646 // ********************************************************************************
3647 // Type cmsSigCrdInfoType
3648 // ********************************************************************************
3649 
3650 /*
3651 This type contains the PostScript product name to which this profile corresponds
3652 and the names of the companion CRDs. Recall that a single profile can generate
3653 multiple CRDs. It is implemented as a MLU being the language code "PS" and then
3654 country varies for each element:
3655 
3656                 nm: PostScript product name
3657                 #0: Rendering intent 0 CRD name
3658                 #1: Rendering intent 1 CRD name
3659                 #2: Rendering intent 2 CRD name
3660                 #3: Rendering intent 3 CRD name
3661 */
3662 
3663 
3664 
3665 // Auxiliary, read an string specified as count + string
3666 static
3667 cmsBool  ReadCountAndSting(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* mlu, cmsUInt32Number* SizeOfTag, const char* Section)
3668 {
3669     cmsUInt32Number Count;
3670     char* Text;
3671 
3672     if (*SizeOfTag < sizeof(cmsUInt32Number)) return FALSE;
3673 
3674     if (!_cmsReadUInt32Number(io, &Count)) return FALSE;
3675 
3676     if (Count > UINT_MAX - sizeof(cmsUInt32Number)) return FALSE;
3677     if (*SizeOfTag < Count + sizeof(cmsUInt32Number)) return FALSE;
3678 
3679     Text     = (char*) _cmsMalloc(self ->ContextID, Count+1);
3680     if (Text == NULL) return FALSE;
3681 
3682     if (io ->Read(io, Text, sizeof(cmsUInt8Number), Count) != Count) {
3683         _cmsFree(self ->ContextID, Text);
3684         return FALSE;
3685     }
3686 
3687     Text[Count] = 0;
3688 
3689     cmsMLUsetASCII(mlu, "PS", Section, Text);
3690     _cmsFree(self ->ContextID, Text);
3691 
3692     *SizeOfTag -= (Count + sizeof(cmsUInt32Number));
3693     return TRUE;
3694 }
3695 
3696 static
3697 cmsBool  WriteCountAndSting(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* mlu, const char* Section)
3698 {
3699  cmsUInt32Number TextSize;
3700  char* Text;
3701 
3702     TextSize = cmsMLUgetASCII(mlu, "PS", Section, NULL, 0);
3703     Text     = (char*) _cmsMalloc(self ->ContextID, TextSize);
3704 
3705     if (!_cmsWriteUInt32Number(io, TextSize)) return FALSE;
3706 
3707     if (cmsMLUgetASCII(mlu, "PS", Section, Text, TextSize) == 0) return FALSE;
3708 
3709     if (!io ->Write(io, TextSize, Text)) return FALSE;
3710     _cmsFree(self ->ContextID, Text);
3711 
3712     return TRUE;
3713 }
3714 
3715 static
3716 void *Type_CrdInfo_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
3717 {
3718     cmsMLU* mlu = cmsMLUalloc(self ->ContextID, 5);
3719 
3720     *nItems = 0;
3721     if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "nm")) goto Error;
3722     if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#0")) goto Error;
3723     if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#1")) goto Error;
3724     if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#2")) goto Error;
3725     if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#3")) goto Error;
3726 
3727     *nItems = 1;
3728     return (void*) mlu;
3729 
3730 Error:
3731     cmsMLUfree(mlu);
3732     return NULL;
3733 
3734 }
3735 
3736 static
3737 cmsBool  Type_CrdInfo_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
3738 {
3739 
3740     cmsMLU* mlu = (cmsMLU*) Ptr;
3741 
3742     if (!WriteCountAndSting(self, io, mlu, "nm")) goto Error;
3743     if (!WriteCountAndSting(self, io, mlu, "#0")) goto Error;
3744     if (!WriteCountAndSting(self, io, mlu, "#1")) goto Error;
3745     if (!WriteCountAndSting(self, io, mlu, "#2")) goto Error;
3746     if (!WriteCountAndSting(self, io, mlu, "#3")) goto Error;
3747 
3748     return TRUE;
3749 
3750 Error:
3751     return FALSE;
3752 
3753     cmsUNUSED_PARAMETER(nItems);
3754 }
3755 
3756 
3757 static
3758 void* Type_CrdInfo_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
3759 {
3760     return (void*) cmsMLUdup((cmsMLU*) Ptr);
3761 
3762     cmsUNUSED_PARAMETER(n);
3763     cmsUNUSED_PARAMETER(self);
3764 }
3765 
3766 static
3767 void Type_CrdInfo_Free(struct _cms_typehandler_struct* self, void *Ptr)
3768 {
3769     cmsMLUfree((cmsMLU*) Ptr);
3770     return;
3771 
3772     cmsUNUSED_PARAMETER(self);
3773 }
3774 
3775 // ********************************************************************************
3776 // Type cmsSigScreeningType
3777 // ********************************************************************************
3778 //
3779 //The screeningType describes various screening parameters including screen
3780 //frequency, screening angle, and spot shape.
3781 
3782 static
3783 void *Type_Screening_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
3784 {
3785     cmsScreening* sc = NULL;
3786     cmsUInt32Number i;
3787 
3788     sc = (cmsScreening*) _cmsMallocZero(self ->ContextID, sizeof(cmsScreening));
3789     if (sc == NULL) return NULL;
3790 
3791     *nItems = 0;
3792 
3793     if (!_cmsReadUInt32Number(io, &sc ->Flag)) goto Error;
3794     if (!_cmsReadUInt32Number(io, &sc ->nChannels)) goto Error;
3795 
3796     if (sc ->nChannels > cmsMAXCHANNELS - 1)
3797         sc ->nChannels = cmsMAXCHANNELS - 1;
3798 
3799     for (i=0; i < sc ->nChannels; i++) {
3800 
3801         if (!_cmsRead15Fixed16Number(io, &sc ->Channels[i].Frequency)) goto Error;
3802         if (!_cmsRead15Fixed16Number(io, &sc ->Channels[i].ScreenAngle)) goto Error;
3803         if (!_cmsReadUInt32Number(io, &sc ->Channels[i].SpotShape)) goto Error;
3804     }
3805 
3806 
3807     *nItems = 1;
3808 
3809     return (void*) sc;
3810 
3811 Error:
3812     if (sc != NULL)
3813         _cmsFree(self ->ContextID, sc);
3814 
3815     return NULL;
3816 
3817     cmsUNUSED_PARAMETER(SizeOfTag);
3818 }
3819 
3820 
3821 static
3822 cmsBool Type_Screening_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
3823 {
3824     cmsScreening* sc = (cmsScreening* ) Ptr;
3825     cmsUInt32Number i;
3826 
3827     if (!_cmsWriteUInt32Number(io, sc ->Flag)) return FALSE;
3828     if (!_cmsWriteUInt32Number(io, sc ->nChannels)) return FALSE;
3829 
3830     for (i=0; i < sc ->nChannels; i++) {
3831 
3832         if (!_cmsWrite15Fixed16Number(io, sc ->Channels[i].Frequency)) return FALSE;
3833         if (!_cmsWrite15Fixed16Number(io, sc ->Channels[i].ScreenAngle)) return FALSE;
3834         if (!_cmsWriteUInt32Number(io, sc ->Channels[i].SpotShape)) return FALSE;
3835     }
3836 
3837     return TRUE;
3838 
3839     cmsUNUSED_PARAMETER(nItems);
3840     cmsUNUSED_PARAMETER(self);
3841 }
3842 
3843 
3844 static
3845 void* Type_Screening_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
3846 {
3847    return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsScreening));
3848 
3849    cmsUNUSED_PARAMETER(n);
3850 }
3851 
3852 
3853 static
3854 void Type_Screening_Free(struct _cms_typehandler_struct* self, void* Ptr)
3855 {
3856    _cmsFree(self ->ContextID, Ptr);
3857 }
3858 
3859 // ********************************************************************************
3860 // Type cmsSigViewingConditionsType
3861 // ********************************************************************************
3862 //
3863 //This type represents a set of viewing condition parameters including:
3864 //CIE 'absolute' illuminant white point tristimulus values and CIE 'absolute'
3865 //surround tristimulus values.
3866 
3867 static
3868 void *Type_ViewingConditions_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
3869 {
3870     cmsICCViewingConditions* vc = NULL;
3871 
3872     vc = (cmsICCViewingConditions*) _cmsMallocZero(self ->ContextID, sizeof(cmsICCViewingConditions));
3873     if (vc == NULL) return NULL;
3874 
3875     *nItems = 0;
3876 
3877     if (!_cmsReadXYZNumber(io, &vc ->IlluminantXYZ)) goto Error;
3878     if (!_cmsReadXYZNumber(io, &vc ->SurroundXYZ)) goto Error;
3879     if (!_cmsReadUInt32Number(io, &vc ->IlluminantType)) goto Error;
3880 
3881     *nItems = 1;
3882 
3883     return (void*) vc;
3884 
3885 Error:
3886     if (vc != NULL)
3887         _cmsFree(self ->ContextID, vc);
3888 
3889     return NULL;
3890 
3891     cmsUNUSED_PARAMETER(SizeOfTag);
3892 }
3893 
3894 
3895 static
3896 cmsBool Type_ViewingConditions_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
3897 {
3898     cmsICCViewingConditions* sc = (cmsICCViewingConditions* ) Ptr;
3899 
3900     if (!_cmsWriteXYZNumber(io, &sc ->IlluminantXYZ)) return FALSE;
3901     if (!_cmsWriteXYZNumber(io, &sc ->SurroundXYZ)) return FALSE;
3902     if (!_cmsWriteUInt32Number(io, sc ->IlluminantType)) return FALSE;
3903 
3904     return TRUE;
3905 
3906     cmsUNUSED_PARAMETER(nItems);
3907     cmsUNUSED_PARAMETER(self);
3908 }
3909 
3910 
3911 static
3912 void* Type_ViewingConditions_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
3913 {
3914    return _cmsDupMem(self->ContextID, Ptr, sizeof(cmsICCViewingConditions));
3915 
3916    cmsUNUSED_PARAMETER(n);
3917 }
3918 
3919 
3920 static
3921 void Type_ViewingConditions_Free(struct _cms_typehandler_struct* self, void* Ptr)
3922 {
3923    _cmsFree(self ->ContextID, Ptr);
3924 }
3925 
3926 
3927 // ********************************************************************************
3928 // Type cmsSigMultiProcessElementType
3929 // ********************************************************************************
3930 
3931 
3932 static
3933 void* GenericMPEdup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
3934 {
3935     return (void*) cmsStageDup((cmsStage*) Ptr);
3936 
3937     cmsUNUSED_PARAMETER(n);
3938     cmsUNUSED_PARAMETER(self);
3939 }
3940 
3941 static
3942 void GenericMPEfree(struct _cms_typehandler_struct* self, void *Ptr)
3943 {
3944     cmsStageFree((cmsStage*) Ptr);
3945     return;
3946 
3947     cmsUNUSED_PARAMETER(self);
3948 }
3949 
3950 // Each curve is stored in one or more curve segments, with break-points specified between curve segments.
3951 // The first curve segment always starts at -Infinity, and the last curve segment always ends at +Infinity. The
3952 // first and last curve segments shall be specified in terms of a formula, whereas the other segments shall be
3953 // specified either in terms of a formula, or by a sampled curve.
3954 
3955 
3956 // Read an embedded segmented curve
3957 static
3958 cmsToneCurve* ReadSegmentedCurve(struct _cms_typehandler_struct* self, cmsIOHANDLER* io)
3959 {
3960     cmsCurveSegSignature ElementSig;
3961     cmsUInt32Number i, j;
3962     cmsUInt16Number nSegments;
3963     cmsCurveSegment*  Segments;
3964     cmsToneCurve* Curve;
3965     cmsFloat32Number PrevBreak = MINUS_INF;    // - infinite
3966 
3967     // Take signature and channels for each element.
3968      if (!_cmsReadUInt32Number(io, (cmsUInt32Number*) &ElementSig)) return NULL;
3969 
3970      // That should be a segmented curve
3971      if (ElementSig != cmsSigSegmentedCurve) return NULL;
3972 
3973      if (!_cmsReadUInt32Number(io, NULL)) return NULL;
3974      if (!_cmsReadUInt16Number(io, &nSegments)) return NULL;
3975      if (!_cmsReadUInt16Number(io, NULL)) return NULL;
3976 
3977      if (nSegments < 1) return NULL;
3978      Segments = (cmsCurveSegment*) _cmsCalloc(self ->ContextID, nSegments, sizeof(cmsCurveSegment));
3979      if (Segments == NULL) return NULL;
3980 
3981      // Read breakpoints
3982      for (i=0; i < (cmsUInt32Number) nSegments - 1; i++) {
3983 
3984          Segments[i].x0 = PrevBreak;
3985          if (!_cmsReadFloat32Number(io, &Segments[i].x1)) goto Error;
3986          PrevBreak = Segments[i].x1;
3987      }
3988 
3989      Segments[nSegments-1].x0 = PrevBreak;
3990      Segments[nSegments-1].x1 = PLUS_INF;     // A big cmsFloat32Number number
3991 
3992      // Read segments
3993      for (i=0; i < nSegments; i++) {
3994 
3995           if (!_cmsReadUInt32Number(io, (cmsUInt32Number*) &ElementSig)) goto Error;
3996           if (!_cmsReadUInt32Number(io, NULL)) goto Error;
3997 
3998            switch (ElementSig) {
3999 
4000             case cmsSigFormulaCurveSeg: {
4001 
4002                 cmsUInt16Number Type;
4003                 cmsUInt32Number ParamsByType[] = {4, 5, 5 };
4004 
4005                 if (!_cmsReadUInt16Number(io, &Type)) goto Error;
4006                 if (!_cmsReadUInt16Number(io, NULL)) goto Error;
4007 
4008                 Segments[i].Type = Type + 6;
4009                 if (Type > 2) goto Error;
4010 
4011                 for (j=0; j < ParamsByType[Type]; j++) {
4012 
4013                     cmsFloat32Number f;
4014                     if (!_cmsReadFloat32Number(io, &f)) goto Error;
4015                     Segments[i].Params[j] = f;
4016                 }
4017                 }
4018                 break;
4019 
4020 
4021             case cmsSigSampledCurveSeg: {
4022                 cmsUInt32Number Count;
4023 
4024                 if (!_cmsReadUInt32Number(io, &Count)) goto Error;
4025 
4026                 Segments[i].nGridPoints = Count;
4027                 Segments[i].SampledPoints = (cmsFloat32Number*) _cmsCalloc(self ->ContextID, Count, sizeof(cmsFloat32Number));
4028                 if (Segments[i].SampledPoints == NULL) goto Error;
4029 
4030                 for (j=0; j < Count; j++) {
4031                     if (!_cmsReadFloat32Number(io, &Segments[i].SampledPoints[j])) goto Error;
4032                 }
4033                 }
4034                 break;
4035 
4036             default:
4037                 {
4038                 char String[5];
4039 
4040                 _cmsTagSignature2String(String, (cmsTagSignature) ElementSig);
4041                 cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown curve element type '%s' found.", String);
4042                 }
4043                 goto Error;
4044 
4045          }
4046      }
4047 
4048      Curve = cmsBuildSegmentedToneCurve(self ->ContextID, nSegments, Segments);
4049 
4050      for (i=0; i < nSegments; i++) {
4051          if (Segments[i].SampledPoints) _cmsFree(self ->ContextID, Segments[i].SampledPoints);
4052      }
4053      _cmsFree(self ->ContextID, Segments);
4054      return Curve;
4055 
4056 Error:
4057      if (Segments) {
4058          for (i=0; i < nSegments; i++) {
4059              if (Segments[i].SampledPoints) _cmsFree(self ->ContextID, Segments[i].SampledPoints);
4060          }
4061          _cmsFree(self ->ContextID, Segments);
4062      }
4063      return NULL;
4064 }
4065 
4066 
4067 static
4068 cmsBool ReadMPECurve(struct _cms_typehandler_struct* self,
4069                      cmsIOHANDLER* io,
4070                      void* Cargo,
4071                      cmsUInt32Number n,
4072                      cmsUInt32Number SizeOfTag)
4073 {
4074       cmsToneCurve** GammaTables = ( cmsToneCurve**) Cargo;
4075 
4076       GammaTables[n] = ReadSegmentedCurve(self, io);
4077       return (GammaTables[n] != NULL);
4078 
4079       cmsUNUSED_PARAMETER(SizeOfTag);
4080 }
4081 
4082 static
4083 void *Type_MPEcurve_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
4084 {
4085     cmsStage* mpe = NULL;
4086     cmsUInt16Number InputChans, OutputChans;
4087     cmsUInt32Number i, BaseOffset;
4088     cmsToneCurve** GammaTables;
4089 
4090     *nItems = 0;
4091 
4092     // Get actual position as a basis for element offsets
4093     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
4094 
4095     if (!_cmsReadUInt16Number(io, &InputChans)) return NULL;
4096     if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL;
4097 
4098     if (InputChans != OutputChans) return NULL;
4099 
4100     GammaTables = (cmsToneCurve**) _cmsCalloc(self ->ContextID, InputChans, sizeof(cmsToneCurve*));
4101     if (GammaTables == NULL) return NULL;
4102 
4103     if (ReadPositionTable(self, io, InputChans, BaseOffset, GammaTables, ReadMPECurve)) {
4104 
4105         mpe = cmsStageAllocToneCurves(self ->ContextID, InputChans, GammaTables);
4106     }
4107     else {
4108         mpe = NULL;
4109     }
4110 
4111     for (i=0; i < InputChans; i++) {
4112         if (GammaTables[i]) cmsFreeToneCurve(GammaTables[i]);
4113     }
4114 
4115     _cmsFree(self ->ContextID, GammaTables);
4116     *nItems = (mpe != NULL) ? 1U : 0;
4117     return mpe;
4118 
4119     cmsUNUSED_PARAMETER(SizeOfTag);
4120 }
4121 
4122 
4123 // Write a single segmented curve. NO CHECK IS PERFORMED ON VALIDITY
4124 static
4125 cmsBool WriteSegmentedCurve(cmsIOHANDLER* io, cmsToneCurve* g)
4126 {
4127     cmsUInt32Number i, j;
4128     cmsCurveSegment* Segments = g ->Segments;
4129     cmsUInt32Number nSegments = g ->nSegments;
4130 
4131     if (!_cmsWriteUInt32Number(io, cmsSigSegmentedCurve)) goto Error;
4132     if (!_cmsWriteUInt32Number(io, 0)) goto Error;
4133     if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) nSegments)) goto Error;
4134     if (!_cmsWriteUInt16Number(io, 0)) goto Error;
4135 
4136     // Write the break-points
4137     for (i=0; i < nSegments - 1; i++) {
4138         if (!_cmsWriteFloat32Number(io, Segments[i].x1)) goto Error;
4139     }
4140 
4141     // Write the segments
4142     for (i=0; i < g ->nSegments; i++) {
4143 
4144         cmsCurveSegment* ActualSeg = Segments + i;
4145 
4146         if (ActualSeg -> Type == 0) {
4147 
4148             // This is a sampled curve
4149             if (!_cmsWriteUInt32Number(io, (cmsUInt32Number) cmsSigSampledCurveSeg)) goto Error;
4150             if (!_cmsWriteUInt32Number(io, 0)) goto Error;
4151             if (!_cmsWriteUInt32Number(io, ActualSeg -> nGridPoints)) goto Error;
4152 
4153             for (j=0; j < g ->Segments[i].nGridPoints; j++) {
4154                 if (!_cmsWriteFloat32Number(io, ActualSeg -> SampledPoints[j])) goto Error;
4155             }
4156 
4157         }
4158         else {
4159             int Type;
4160             cmsUInt32Number ParamsByType[] = { 4, 5, 5 };
4161 
4162             // This is a formula-based
4163             if (!_cmsWriteUInt32Number(io, (cmsUInt32Number) cmsSigFormulaCurveSeg)) goto Error;
4164             if (!_cmsWriteUInt32Number(io, 0)) goto Error;
4165 
4166             // We only allow 1, 2 and 3 as types
4167             Type = ActualSeg ->Type - 6;
4168             if (Type > 2 || Type < 0) goto Error;
4169 
4170             if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) Type)) goto Error;
4171             if (!_cmsWriteUInt16Number(io, 0)) goto Error;
4172 
4173             for (j=0; j < ParamsByType[Type]; j++) {
4174                 if (!_cmsWriteFloat32Number(io, (cmsFloat32Number) ActualSeg ->Params[j])) goto Error;
4175             }
4176         }
4177 
4178         // It seems there is no need to align. Code is here, and for safety commented out
4179         // if (!_cmsWriteAlignment(io)) goto Error;
4180     }
4181 
4182     return TRUE;
4183 
4184 Error:
4185     return FALSE;
4186 }
4187 
4188 
4189 static
4190 cmsBool WriteMPECurve(struct _cms_typehandler_struct* self,
4191                       cmsIOHANDLER* io,
4192                       void* Cargo,
4193                       cmsUInt32Number n,
4194                       cmsUInt32Number SizeOfTag)
4195 {
4196     _cmsStageToneCurvesData* Curves  = (_cmsStageToneCurvesData*) Cargo;
4197 
4198     return WriteSegmentedCurve(io, Curves ->TheCurves[n]);
4199 
4200     cmsUNUSED_PARAMETER(SizeOfTag);
4201     cmsUNUSED_PARAMETER(self);
4202 }
4203 
4204 // Write a curve, checking first for validity
4205 static
4206 cmsBool  Type_MPEcurve_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
4207 {
4208     cmsUInt32Number BaseOffset;
4209     cmsStage* mpe = (cmsStage*) Ptr;
4210     _cmsStageToneCurvesData* Curves = (_cmsStageToneCurvesData*) mpe ->Data;
4211 
4212     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
4213 
4214     // Write the header. Since those are curves, input and output channels are same
4215     if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->InputChannels)) return FALSE;
4216     if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->InputChannels)) return FALSE;
4217 
4218     if (!WritePositionTable(self, io, 0,
4219                                 mpe ->InputChannels, BaseOffset, Curves, WriteMPECurve)) return FALSE;
4220 
4221 
4222     return TRUE;
4223 
4224     cmsUNUSED_PARAMETER(nItems);
4225 }
4226 
4227 
4228 
4229 // The matrix is organized as an array of PxQ+Q elements, where P is the number of input channels to the
4230 // matrix, and Q is the number of output channels. The matrix elements are each float32Numbers. The array
4231 // is organized as follows:
4232 // array = [e11, e12, ..., e1P, e21, e22, ..., e2P, ..., eQ1, eQ2, ..., eQP, e1, e2, ..., eQ]
4233 
4234 static
4235 void *Type_MPEmatrix_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
4236 {
4237     cmsStage* mpe;
4238     cmsUInt16Number   InputChans, OutputChans;
4239     cmsUInt32Number   nElems, i;
4240     cmsFloat64Number* Matrix;
4241     cmsFloat64Number* Offsets;
4242 
4243     if (!_cmsReadUInt16Number(io, &InputChans)) return NULL;
4244     if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL;
4245 
4246 
4247     // Input and output chans may be ANY (up to 0xffff),
4248     // but we choose to limit to 16 channels for now
4249     if (InputChans >= cmsMAXCHANNELS) return NULL;
4250     if (OutputChans >= cmsMAXCHANNELS) return NULL;
4251 
4252     nElems = (cmsUInt32Number) InputChans * OutputChans;
4253 
4254     Matrix = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, nElems, sizeof(cmsFloat64Number));
4255     if (Matrix == NULL) return NULL;
4256 
4257     Offsets = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, OutputChans, sizeof(cmsFloat64Number));
4258     if (Offsets == NULL) {
4259 
4260         _cmsFree(self ->ContextID, Matrix);
4261         return NULL;
4262     }
4263 
4264     for (i=0; i < nElems; i++) {
4265 
4266         cmsFloat32Number v;
4267 
4268         if (!_cmsReadFloat32Number(io, &v)) {
4269             _cmsFree(self ->ContextID, Matrix);
4270             _cmsFree(self ->ContextID, Offsets);
4271             return NULL;
4272         }
4273         Matrix[i] = v;
4274     }
4275 
4276 
4277     for (i=0; i < OutputChans; i++) {
4278 
4279         cmsFloat32Number v;
4280 
4281         if (!_cmsReadFloat32Number(io, &v)) {
4282             _cmsFree(self ->ContextID, Matrix);
4283             _cmsFree(self ->ContextID, Offsets);
4284             return NULL;
4285         }
4286         Offsets[i] = v;
4287     }
4288 
4289 
4290     mpe = cmsStageAllocMatrix(self ->ContextID, OutputChans, InputChans, Matrix, Offsets);
4291     _cmsFree(self ->ContextID, Matrix);
4292     _cmsFree(self ->ContextID, Offsets);
4293 
4294     *nItems = 1;
4295 
4296     return mpe;
4297 
4298     cmsUNUSED_PARAMETER(SizeOfTag);
4299 }
4300 
4301 static
4302 cmsBool  Type_MPEmatrix_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
4303 {
4304     cmsUInt32Number i, nElems;
4305     cmsStage* mpe = (cmsStage*) Ptr;
4306     _cmsStageMatrixData* Matrix = (_cmsStageMatrixData*) mpe ->Data;
4307 
4308     if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->InputChannels)) return FALSE;
4309     if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->OutputChannels)) return FALSE;
4310 
4311     nElems = mpe ->InputChannels * mpe ->OutputChannels;
4312 
4313     for (i=0; i < nElems; i++) {
4314         if (!_cmsWriteFloat32Number(io, (cmsFloat32Number) Matrix->Double[i])) return FALSE;
4315     }
4316 
4317 
4318     for (i=0; i < mpe ->OutputChannels; i++) {
4319 
4320         if (Matrix ->Offset == NULL) {
4321 
4322                if (!_cmsWriteFloat32Number(io, 0)) return FALSE;
4323         }
4324         else {
4325                if (!_cmsWriteFloat32Number(io, (cmsFloat32Number) Matrix->Offset[i])) return FALSE;
4326         }
4327     }
4328 
4329     return TRUE;
4330 
4331     cmsUNUSED_PARAMETER(nItems);
4332     cmsUNUSED_PARAMETER(self);
4333 }
4334 
4335 
4336 
4337 static
4338 void *Type_MPEclut_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
4339 {
4340     cmsStage* mpe = NULL;
4341     cmsUInt16Number InputChans, OutputChans;
4342     cmsUInt8Number Dimensions8[16];
4343     cmsUInt32Number i, nMaxGrids, GridPoints[MAX_INPUT_DIMENSIONS];
4344     _cmsStageCLutData* clut;
4345 
4346     if (!_cmsReadUInt16Number(io, &InputChans)) return NULL;
4347     if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL;
4348 
4349     if (InputChans == 0) goto Error;
4350     if (OutputChans == 0) goto Error;
4351 
4352     if (io ->Read(io, Dimensions8, sizeof(cmsUInt8Number), 16) != 16)
4353         goto Error;
4354 
4355     // Copy MAX_INPUT_DIMENSIONS at most. Expand to cmsUInt32Number
4356     nMaxGrids = InputChans > MAX_INPUT_DIMENSIONS ? (cmsUInt32Number) MAX_INPUT_DIMENSIONS : InputChans;
4357 
4358     for (i = 0; i < nMaxGrids; i++) {
4359         if (Dimensions8[i] == 1) goto Error; // Impossible value, 0 for no CLUT and then 2 at least
4360         GridPoints[i] = (cmsUInt32Number)Dimensions8[i];
4361     }
4362 
4363     // Allocate the true CLUT
4364     mpe = cmsStageAllocCLutFloatGranular(self ->ContextID, GridPoints, InputChans, OutputChans, NULL);
4365     if (mpe == NULL) goto Error;
4366 
4367     // Read and sanitize the data
4368     clut = (_cmsStageCLutData*) mpe ->Data;
4369     for (i=0; i < clut ->nEntries; i++) {
4370 
4371         if (!_cmsReadFloat32Number(io, &clut->Tab.TFloat[i])) goto Error;
4372     }
4373 
4374     *nItems = 1;
4375     return mpe;
4376 
4377 Error:
4378     *nItems = 0;
4379     if (mpe != NULL) cmsStageFree(mpe);
4380     return NULL;
4381 
4382     cmsUNUSED_PARAMETER(SizeOfTag);
4383 }
4384 
4385 // Write a CLUT in floating point
4386 static
4387 cmsBool  Type_MPEclut_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
4388 {
4389     cmsUInt8Number Dimensions8[16];  // 16 because the spec says 16 and not max number of channels
4390     cmsUInt32Number i;
4391     cmsStage* mpe = (cmsStage*) Ptr;
4392     _cmsStageCLutData* clut = (_cmsStageCLutData*) mpe ->Data;
4393 
4394     // Check for maximum number of channels supported by lcms
4395     if (mpe -> InputChannels > MAX_INPUT_DIMENSIONS) return FALSE;
4396 
4397     // Only floats are supported in MPE
4398     if (clut ->HasFloatValues == FALSE) return FALSE;
4399 
4400     if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->InputChannels)) return FALSE;
4401     if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->OutputChannels)) return FALSE;
4402 
4403     memset(Dimensions8, 0, sizeof(Dimensions8));
4404 
4405     for (i=0; i < mpe ->InputChannels; i++)
4406         Dimensions8[i] = (cmsUInt8Number) clut ->Params ->nSamples[i];
4407 
4408     if (!io ->Write(io, 16, Dimensions8)) return FALSE;
4409 
4410     for (i=0; i < clut ->nEntries; i++) {
4411 
4412         if (!_cmsWriteFloat32Number(io, clut ->Tab.TFloat[i])) return FALSE;
4413     }
4414 
4415     return TRUE;
4416 
4417     cmsUNUSED_PARAMETER(nItems);
4418     cmsUNUSED_PARAMETER(self);
4419 }
4420 
4421 
4422 
4423 // This is the list of built-in MPE types
4424 static _cmsTagTypeLinkedList SupportedMPEtypes[] = {
4425 
4426 {{ (cmsTagTypeSignature) cmsSigBAcsElemType, NULL, NULL, NULL, NULL, NULL, 0 }, &SupportedMPEtypes[1] },   // Ignore those elements for now
4427 {{ (cmsTagTypeSignature) cmsSigEAcsElemType, NULL, NULL, NULL, NULL, NULL, 0 }, &SupportedMPEtypes[2] },   // (That's what the spec says)
4428 
4429 {TYPE_MPE_HANDLER((cmsTagTypeSignature) cmsSigCurveSetElemType,     MPEcurve),      &SupportedMPEtypes[3] },
4430 {TYPE_MPE_HANDLER((cmsTagTypeSignature) cmsSigMatrixElemType,       MPEmatrix),     &SupportedMPEtypes[4] },
4431 {TYPE_MPE_HANDLER((cmsTagTypeSignature) cmsSigCLutElemType,         MPEclut),        NULL },
4432 };
4433 
4434 _cmsTagTypePluginChunkType _cmsMPETypePluginChunk = { NULL };
4435 
4436 static
4437 cmsBool ReadMPEElem(struct _cms_typehandler_struct* self,
4438                     cmsIOHANDLER* io,
4439                     void* Cargo,
4440                     cmsUInt32Number n,
4441                     cmsUInt32Number SizeOfTag)
4442 {
4443     cmsStageSignature ElementSig;
4444     cmsTagTypeHandler* TypeHandler;
4445     cmsUInt32Number nItems;
4446     cmsPipeline *NewLUT = (cmsPipeline *) Cargo;
4447     _cmsTagTypePluginChunkType* MPETypePluginChunk  = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(self->ContextID, MPEPlugin);
4448 
4449 
4450     // Take signature and channels for each element.
4451     if (!_cmsReadUInt32Number(io, (cmsUInt32Number*) &ElementSig)) return FALSE;
4452 
4453     // The reserved placeholder
4454     if (!_cmsReadUInt32Number(io, NULL)) return FALSE;
4455 
4456     // Read diverse MPE types
4457     TypeHandler = GetHandler((cmsTagTypeSignature) ElementSig, MPETypePluginChunk ->TagTypes, SupportedMPEtypes);
4458     if (TypeHandler == NULL)  {
4459 
4460         char String[5];
4461 
4462         _cmsTagSignature2String(String, (cmsTagSignature) ElementSig);
4463 
4464         // An unknown element was found.
4465         cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown MPE type '%s' found.", String);
4466         return FALSE;
4467     }
4468 
4469     // If no read method, just ignore the element (valid for cmsSigBAcsElemType and cmsSigEAcsElemType)
4470     // Read the MPE. No size is given
4471     if (TypeHandler ->ReadPtr != NULL) {
4472 
4473         // This is a real element which should be read and processed
4474         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, (cmsStage*) TypeHandler ->ReadPtr(self, io, &nItems, SizeOfTag)))
4475             return FALSE;
4476     }
4477 
4478     return TRUE;
4479 
4480     cmsUNUSED_PARAMETER(SizeOfTag);
4481     cmsUNUSED_PARAMETER(n);
4482 }
4483 
4484 
4485 // This is the main dispatcher for MPE
4486 static
4487 void *Type_MPE_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
4488 {
4489     cmsUInt16Number InputChans, OutputChans;
4490     cmsUInt32Number ElementCount;
4491     cmsPipeline *NewLUT = NULL;
4492     cmsUInt32Number BaseOffset;
4493 
4494     // Get actual position as a basis for element offsets
4495     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
4496 
4497     // Read channels and element count
4498     if (!_cmsReadUInt16Number(io, &InputChans)) return NULL;
4499     if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL;
4500 
4501     if (InputChans == 0 || InputChans >= cmsMAXCHANNELS) return NULL;
4502     if (OutputChans == 0 || OutputChans >= cmsMAXCHANNELS) return NULL;
4503 
4504     // Allocates an empty LUT
4505     NewLUT = cmsPipelineAlloc(self ->ContextID, InputChans, OutputChans);
4506     if (NewLUT == NULL) return NULL;
4507 
4508     if (!_cmsReadUInt32Number(io, &ElementCount)) goto Error;
4509     if (!ReadPositionTable(self, io, ElementCount, BaseOffset, NewLUT, ReadMPEElem)) goto Error;
4510 
4511     // Check channel count
4512     if (InputChans != NewLUT->InputChannels ||
4513         OutputChans != NewLUT->OutputChannels) goto Error;
4514 
4515     // Success
4516     *nItems = 1;
4517     return NewLUT;
4518 
4519     // Error
4520 Error:
4521     if (NewLUT != NULL) cmsPipelineFree(NewLUT);
4522     *nItems = 0;
4523     return NULL;
4524 
4525     cmsUNUSED_PARAMETER(SizeOfTag);
4526 }
4527 
4528 
4529 
4530 // This one is a liitle bit more complex, so we don't use position tables this time.
4531 static
4532 cmsBool Type_MPE_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
4533 {
4534     cmsUInt32Number i, BaseOffset, DirectoryPos, CurrentPos;
4535     cmsUInt32Number inputChan, outputChan;
4536     cmsUInt32Number ElemCount;
4537     cmsUInt32Number *ElementOffsets = NULL, *ElementSizes = NULL, Before;
4538     cmsStageSignature ElementSig;
4539     cmsPipeline* Lut = (cmsPipeline*) Ptr;
4540     cmsStage* Elem = Lut ->Elements;
4541     cmsTagTypeHandler* TypeHandler;
4542     _cmsTagTypePluginChunkType* MPETypePluginChunk  = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(self->ContextID, MPEPlugin);
4543 
4544     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
4545 
4546     inputChan  = cmsPipelineInputChannels(Lut);
4547     outputChan = cmsPipelineOutputChannels(Lut);
4548     ElemCount  = cmsPipelineStageCount(Lut);
4549 
4550     ElementOffsets = (cmsUInt32Number *) _cmsCalloc(self ->ContextID, ElemCount, sizeof(cmsUInt32Number));
4551     if (ElementOffsets == NULL) goto Error;
4552 
4553     ElementSizes = (cmsUInt32Number *) _cmsCalloc(self ->ContextID, ElemCount, sizeof(cmsUInt32Number));
4554     if (ElementSizes == NULL) goto Error;
4555 
4556     // Write the head
4557     if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) inputChan)) goto Error;
4558     if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) outputChan)) goto Error;
4559     if (!_cmsWriteUInt32Number(io, (cmsUInt16Number) ElemCount)) goto Error;
4560 
4561     DirectoryPos = io ->Tell(io);
4562 
4563     // Write a fake directory to be filled latter on
4564     for (i=0; i < ElemCount; i++) {
4565         if (!_cmsWriteUInt32Number(io, 0)) goto Error;  // Offset
4566         if (!_cmsWriteUInt32Number(io, 0)) goto Error;  // size
4567     }
4568 
4569     // Write each single tag. Keep track of the size as well.
4570     for (i=0; i < ElemCount; i++) {
4571 
4572         ElementOffsets[i] = io ->Tell(io) - BaseOffset;
4573 
4574         ElementSig = Elem ->Type;
4575 
4576         TypeHandler = GetHandler((cmsTagTypeSignature) ElementSig, MPETypePluginChunk->TagTypes, SupportedMPEtypes);
4577         if (TypeHandler == NULL)  {
4578 
4579                 char String[5];
4580 
4581                 _cmsTagSignature2String(String, (cmsTagSignature) ElementSig);
4582 
4583                  // An unknown element was found.
4584                  cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Found unknown MPE type '%s'", String);
4585                  goto Error;
4586         }
4587 
4588         if (!_cmsWriteUInt32Number(io, ElementSig)) goto Error;
4589         if (!_cmsWriteUInt32Number(io, 0)) goto Error;
4590         Before = io ->Tell(io);
4591         if (!TypeHandler ->WritePtr(self, io, Elem, 1)) goto Error;
4592         if (!_cmsWriteAlignment(io)) goto Error;
4593 
4594         ElementSizes[i] = io ->Tell(io) - Before;
4595 
4596         Elem = Elem ->Next;
4597     }
4598 
4599     // Write the directory
4600     CurrentPos = io ->Tell(io);
4601 
4602     if (!io ->Seek(io, DirectoryPos)) goto Error;
4603 
4604     for (i=0; i < ElemCount; i++) {
4605         if (!_cmsWriteUInt32Number(io, ElementOffsets[i])) goto Error;
4606         if (!_cmsWriteUInt32Number(io, ElementSizes[i])) goto Error;
4607     }
4608 
4609     if (!io ->Seek(io, CurrentPos)) goto Error;
4610 
4611     if (ElementOffsets != NULL) _cmsFree(self ->ContextID, ElementOffsets);
4612     if (ElementSizes != NULL) _cmsFree(self ->ContextID, ElementSizes);
4613     return TRUE;
4614 
4615 Error:
4616     if (ElementOffsets != NULL) _cmsFree(self ->ContextID, ElementOffsets);
4617     if (ElementSizes != NULL) _cmsFree(self ->ContextID, ElementSizes);
4618     return FALSE;
4619 
4620     cmsUNUSED_PARAMETER(nItems);
4621 }
4622 
4623 
4624 static
4625 void* Type_MPE_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
4626 {
4627     return (void*) cmsPipelineDup((cmsPipeline*) Ptr);
4628 
4629     cmsUNUSED_PARAMETER(n);
4630     cmsUNUSED_PARAMETER(self);
4631 }
4632 
4633 static
4634 void Type_MPE_Free(struct _cms_typehandler_struct* self, void *Ptr)
4635 {
4636     cmsPipelineFree((cmsPipeline*) Ptr);
4637     return;
4638 
4639     cmsUNUSED_PARAMETER(self);
4640 }
4641 
4642 
4643 // ********************************************************************************
4644 // Type cmsSigVcgtType
4645 // ********************************************************************************
4646 
4647 
4648 #define cmsVideoCardGammaTableType    0
4649 #define cmsVideoCardGammaFormulaType  1
4650 
4651 // Used internally
4652 typedef struct {
4653     double Gamma;
4654     double Min;
4655     double Max;
4656 } _cmsVCGTGAMMA;
4657 
4658 
4659 static
4660 void *Type_vcgt_Read(struct _cms_typehandler_struct* self,
4661                      cmsIOHANDLER* io,
4662                      cmsUInt32Number* nItems,
4663                      cmsUInt32Number SizeOfTag)
4664 {
4665     cmsUInt32Number TagType, n, i;
4666     cmsToneCurve** Curves;
4667 
4668     *nItems = 0;
4669 
4670     // Read tag type
4671     if (!_cmsReadUInt32Number(io, &TagType)) return NULL;
4672 
4673     // Allocate space for the array
4674     Curves = ( cmsToneCurve**) _cmsCalloc(self ->ContextID, 3, sizeof(cmsToneCurve*));
4675     if (Curves == NULL) return NULL;
4676 
4677     // There are two possible flavors
4678     switch (TagType) {
4679 
4680     // Gamma is stored as a table
4681     case cmsVideoCardGammaTableType:
4682     {
4683        cmsUInt16Number nChannels, nElems, nBytes;
4684 
4685        // Check channel count, which should be 3 (we don't support monochrome this time)
4686        if (!_cmsReadUInt16Number(io, &nChannels)) goto Error;
4687 
4688        if (nChannels != 3) {
4689            cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported number of channels for VCGT '%d'", nChannels);
4690            goto Error;
4691        }
4692 
4693        // Get Table element count and bytes per element
4694        if (!_cmsReadUInt16Number(io, &nElems)) goto Error;
4695        if (!_cmsReadUInt16Number(io, &nBytes)) goto Error;
4696 
4697        // Adobe's quirk fixup. Fixing broken profiles...
4698        if (nElems == 256 && nBytes == 1 && SizeOfTag == 1576)
4699            nBytes = 2;
4700 
4701 
4702        // Populate tone curves
4703        for (n=0; n < 3; n++) {
4704 
4705            Curves[n] = cmsBuildTabulatedToneCurve16(self ->ContextID, nElems, NULL);
4706            if (Curves[n] == NULL) goto Error;
4707 
4708            // On depending on byte depth
4709            switch (nBytes) {
4710 
4711            // One byte, 0..255
4712            case 1:
4713                for (i=0; i < nElems; i++) {
4714 
4715                    cmsUInt8Number v;
4716 
4717                       if (!_cmsReadUInt8Number(io, &v)) goto Error;
4718                       Curves[n] ->Table16[i] = FROM_8_TO_16(v);
4719                }
4720                break;
4721 
4722            // One word 0..65535
4723            case 2:
4724               if (!_cmsReadUInt16Array(io, nElems, Curves[n]->Table16)) goto Error;
4725               break;
4726 
4727           // Unsupported
4728            default:
4729               cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported bit depth for VCGT '%d'", nBytes * 8);
4730               goto Error;
4731            }
4732        } // For all 3 channels
4733     }
4734     break;
4735 
4736    // In this case, gamma is stored as a formula
4737    case cmsVideoCardGammaFormulaType:
4738    {
4739        _cmsVCGTGAMMA Colorant[3];
4740 
4741         // Populate tone curves
4742        for (n=0; n < 3; n++) {
4743 
4744            double Params[10];
4745 
4746            if (!_cmsRead15Fixed16Number(io, &Colorant[n].Gamma)) goto Error;
4747            if (!_cmsRead15Fixed16Number(io, &Colorant[n].Min)) goto Error;
4748            if (!_cmsRead15Fixed16Number(io, &Colorant[n].Max)) goto Error;
4749 
4750             // Parametric curve type 5 is:
4751             // Y = (aX + b)^Gamma + e | X >= d
4752             // Y = cX + f             | X < d
4753 
4754             // vcgt formula is:
4755             // Y = (Max - Min) * (X ^ Gamma) + Min
4756 
4757             // So, the translation is
4758             // a = (Max - Min) ^ ( 1 / Gamma)
4759             // e = Min
4760             // b=c=d=f=0
4761 
4762            Params[0] = Colorant[n].Gamma;
4763            Params[1] = pow((Colorant[n].Max - Colorant[n].Min), (1.0 / Colorant[n].Gamma));
4764            Params[2] = 0;
4765            Params[3] = 0;
4766            Params[4] = 0;
4767            Params[5] = Colorant[n].Min;
4768            Params[6] = 0;
4769 
4770            Curves[n] = cmsBuildParametricToneCurve(self ->ContextID, 5, Params);
4771            if (Curves[n] == NULL) goto Error;
4772        }
4773    }
4774    break;
4775 
4776    // Unsupported
4777    default:
4778       cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported tag type for VCGT '%d'", TagType);
4779       goto Error;
4780    }
4781 
4782    *nItems = 1;
4783    return (void*) Curves;
4784 
4785 // Regret,  free all resources
4786 Error:
4787 
4788     cmsFreeToneCurveTriple(Curves);
4789     _cmsFree(self ->ContextID, Curves);
4790     return NULL;
4791 
4792      cmsUNUSED_PARAMETER(SizeOfTag);
4793 }
4794 
4795 
4796 // We don't support all flavors, only 16bits tables and formula
4797 static
4798 cmsBool Type_vcgt_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
4799 {
4800     cmsToneCurve** Curves =  (cmsToneCurve**) Ptr;
4801     cmsUInt32Number i, j;
4802 
4803     if (cmsGetToneCurveParametricType(Curves[0]) == 5 &&
4804         cmsGetToneCurveParametricType(Curves[1]) == 5 &&
4805         cmsGetToneCurveParametricType(Curves[2]) == 5) {
4806 
4807             if (!_cmsWriteUInt32Number(io, cmsVideoCardGammaFormulaType)) return FALSE;
4808 
4809             // Save parameters
4810             for (i=0; i < 3; i++) {
4811 
4812                 _cmsVCGTGAMMA v;
4813 
4814                 v.Gamma = Curves[i] ->Segments[0].Params[0];
4815                 v.Min   = Curves[i] ->Segments[0].Params[5];
4816                 v.Max   = pow(Curves[i] ->Segments[0].Params[1], v.Gamma) + v.Min;
4817 
4818                 if (!_cmsWrite15Fixed16Number(io, v.Gamma)) return FALSE;
4819                 if (!_cmsWrite15Fixed16Number(io, v.Min)) return FALSE;
4820                 if (!_cmsWrite15Fixed16Number(io, v.Max)) return FALSE;
4821             }
4822     }
4823 
4824     else {
4825 
4826         // Always store as a table of 256 words
4827         if (!_cmsWriteUInt32Number(io, cmsVideoCardGammaTableType)) return FALSE;
4828         if (!_cmsWriteUInt16Number(io, 3)) return FALSE;
4829         if (!_cmsWriteUInt16Number(io, 256)) return FALSE;
4830         if (!_cmsWriteUInt16Number(io, 2)) return FALSE;
4831 
4832         for (i=0; i < 3; i++) {
4833             for (j=0; j < 256; j++) {
4834 
4835                 cmsFloat32Number v = cmsEvalToneCurveFloat(Curves[i], (cmsFloat32Number) (j / 255.0));
4836                 cmsUInt16Number  n = _cmsQuickSaturateWord(v * 65535.0);
4837 
4838                 if (!_cmsWriteUInt16Number(io, n)) return FALSE;
4839             }
4840         }
4841     }
4842 
4843     return TRUE;
4844 
4845     cmsUNUSED_PARAMETER(self);
4846     cmsUNUSED_PARAMETER(nItems);
4847 }
4848 
4849 static
4850 void* Type_vcgt_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
4851 {
4852     cmsToneCurve** OldCurves =  (cmsToneCurve**) Ptr;
4853     cmsToneCurve** NewCurves;
4854 
4855     NewCurves = ( cmsToneCurve**) _cmsCalloc(self ->ContextID, 3, sizeof(cmsToneCurve*));
4856     if (NewCurves == NULL) return NULL;
4857 
4858     NewCurves[0] = cmsDupToneCurve(OldCurves[0]);
4859     NewCurves[1] = cmsDupToneCurve(OldCurves[1]);
4860     NewCurves[2] = cmsDupToneCurve(OldCurves[2]);
4861 
4862     return (void*) NewCurves;
4863 
4864     cmsUNUSED_PARAMETER(n);
4865 }
4866 
4867 
4868 static
4869 void Type_vcgt_Free(struct _cms_typehandler_struct* self, void* Ptr)
4870 {
4871     cmsFreeToneCurveTriple((cmsToneCurve**) Ptr);
4872     _cmsFree(self ->ContextID, Ptr);
4873 }
4874 
4875 
4876 // ********************************************************************************
4877 // Type cmsSigDictType
4878 // ********************************************************************************
4879 
4880 // Single column of the table can point to wchar or MLUC elements. Holds arrays of data
4881 typedef struct {
4882     cmsContext ContextID;
4883     cmsUInt32Number *Offsets;
4884     cmsUInt32Number *Sizes;
4885 } _cmsDICelem;
4886 
4887 typedef struct {
4888     _cmsDICelem Name, Value, DisplayName, DisplayValue;
4889 
4890 } _cmsDICarray;
4891 
4892 // Allocate an empty array element
4893 static
4894 cmsBool AllocElem(cmsContext ContextID, _cmsDICelem* e,  cmsUInt32Number Count)
4895 {
4896     e->Offsets = (cmsUInt32Number *) _cmsCalloc(ContextID, Count, sizeof(cmsUInt32Number));
4897     if (e->Offsets == NULL) return FALSE;
4898 
4899     e->Sizes = (cmsUInt32Number *) _cmsCalloc(ContextID, Count, sizeof(cmsUInt32Number));
4900     if (e->Sizes == NULL) {
4901 
4902         _cmsFree(ContextID, e -> Offsets);
4903         return FALSE;
4904     }
4905 
4906     e ->ContextID = ContextID;
4907     return TRUE;
4908 }
4909 
4910 // Free an array element
4911 static
4912 void FreeElem(_cmsDICelem* e)
4913 {
4914     if (e ->Offsets != NULL)  _cmsFree(e -> ContextID, e -> Offsets);
4915     if (e ->Sizes   != NULL)  _cmsFree(e -> ContextID, e -> Sizes);
4916     e->Offsets = e ->Sizes = NULL;
4917 }
4918 
4919 // Get rid of whole array
4920 static
4921 void FreeArray( _cmsDICarray* a)
4922 {
4923     if (a ->Name.Offsets != NULL) FreeElem(&a->Name);
4924     if (a ->Value.Offsets != NULL) FreeElem(&a ->Value);
4925     if (a ->DisplayName.Offsets != NULL) FreeElem(&a->DisplayName);
4926     if (a ->DisplayValue.Offsets != NULL) FreeElem(&a ->DisplayValue);
4927 }
4928 
4929 
4930 // Allocate whole array
4931 static
4932 cmsBool AllocArray(cmsContext ContextID, _cmsDICarray* a, cmsUInt32Number Count, cmsUInt32Number Length)
4933 {
4934     // Empty values
4935     memset(a, 0, sizeof(_cmsDICarray));
4936 
4937     // On depending on record size, create column arrays
4938     if (!AllocElem(ContextID, &a ->Name, Count)) goto Error;
4939     if (!AllocElem(ContextID, &a ->Value, Count)) goto Error;
4940 
4941     if (Length > 16) {
4942         if (!AllocElem(ContextID, &a -> DisplayName, Count)) goto Error;
4943 
4944     }
4945     if (Length > 24) {
4946         if (!AllocElem(ContextID, &a ->DisplayValue, Count)) goto Error;
4947     }
4948     return TRUE;
4949 
4950 Error:
4951     FreeArray(a);
4952     return FALSE;
4953 }
4954 
4955 // Read one element
4956 static
4957 cmsBool ReadOneElem(cmsIOHANDLER* io,  _cmsDICelem* e, cmsUInt32Number i, cmsUInt32Number BaseOffset)
4958 {
4959     if (!_cmsReadUInt32Number(io, &e->Offsets[i])) return FALSE;
4960     if (!_cmsReadUInt32Number(io, &e ->Sizes[i])) return FALSE;
4961 
4962     // An offset of zero has special meaning and shal be preserved
4963     if (e ->Offsets[i] > 0)
4964         e ->Offsets[i] += BaseOffset;
4965     return TRUE;
4966 }
4967 
4968 
4969 static
4970 cmsBool ReadOffsetArray(cmsIOHANDLER* io,  _cmsDICarray* a, cmsUInt32Number Count, cmsUInt32Number Length, cmsUInt32Number BaseOffset)
4971 {
4972     cmsUInt32Number i;
4973 
4974     // Read column arrays
4975     for (i=0; i < Count; i++) {
4976 
4977         if (!ReadOneElem(io, &a -> Name, i, BaseOffset)) return FALSE;
4978         if (!ReadOneElem(io, &a -> Value, i, BaseOffset)) return FALSE;
4979 
4980         if (Length > 16) {
4981 
4982             if (!ReadOneElem(io, &a ->DisplayName, i, BaseOffset)) return FALSE;
4983 
4984         }
4985 
4986         if (Length > 24) {
4987 
4988             if (!ReadOneElem(io, & a -> DisplayValue, i, BaseOffset)) return FALSE;
4989         }
4990     }
4991     return TRUE;
4992 }
4993 
4994 
4995 // Write one element
4996 static
4997 cmsBool WriteOneElem(cmsIOHANDLER* io,  _cmsDICelem* e, cmsUInt32Number i)
4998 {
4999     if (!_cmsWriteUInt32Number(io, e->Offsets[i])) return FALSE;
5000     if (!_cmsWriteUInt32Number(io, e ->Sizes[i])) return FALSE;
5001 
5002     return TRUE;
5003 }
5004 
5005 static
5006 cmsBool WriteOffsetArray(cmsIOHANDLER* io,  _cmsDICarray* a, cmsUInt32Number Count, cmsUInt32Number Length)
5007 {
5008     cmsUInt32Number i;
5009 
5010     for (i=0; i < Count; i++) {
5011 
5012         if (!WriteOneElem(io, &a -> Name, i)) return FALSE;
5013         if (!WriteOneElem(io, &a -> Value, i))  return FALSE;
5014 
5015         if (Length > 16) {
5016 
5017             if (!WriteOneElem(io, &a -> DisplayName, i))  return FALSE;
5018         }
5019 
5020         if (Length > 24) {
5021 
5022             if (!WriteOneElem(io, &a -> DisplayValue, i))  return FALSE;
5023         }
5024     }
5025 
5026     return TRUE;
5027 }
5028 
5029 static
5030 cmsBool ReadOneWChar(cmsIOHANDLER* io,  _cmsDICelem* e, cmsUInt32Number i, wchar_t ** wcstr)
5031 {
5032 
5033     cmsUInt32Number nChars;
5034 
5035       // Special case for undefined strings (see ICC Votable
5036       // Proposal Submission, Dictionary Type and Metadata TAG Definition)
5037       if (e -> Offsets[i] == 0) {
5038 
5039           *wcstr = NULL;
5040           return TRUE;
5041       }
5042 
5043       if (!io -> Seek(io, e -> Offsets[i])) return FALSE;
5044 
5045       nChars = e ->Sizes[i] / sizeof(cmsUInt16Number);
5046 
5047 
5048       *wcstr = (wchar_t*) _cmsMallocZero(e ->ContextID, (nChars + 1) * sizeof(wchar_t));
5049       if (*wcstr == NULL) return FALSE;
5050 
5051       if (!_cmsReadWCharArray(io, nChars, *wcstr)) {
5052           _cmsFree(e ->ContextID, *wcstr);
5053           return FALSE;
5054       }
5055 
5056       // End of string marker
5057       (*wcstr)[nChars] = 0;
5058       return TRUE;
5059 }
5060 
5061 static
5062 cmsUInt32Number mywcslen(const wchar_t *s)
5063 {
5064     const wchar_t *p;
5065 
5066     p = s;
5067     while (*p)
5068         p++;
5069 
5070     return (cmsUInt32Number)(p - s);
5071 }
5072 
5073 static
5074 cmsBool WriteOneWChar(cmsIOHANDLER* io,  _cmsDICelem* e, cmsUInt32Number i, const wchar_t * wcstr, cmsUInt32Number BaseOffset)
5075 {
5076     cmsUInt32Number Before = io ->Tell(io);
5077     cmsUInt32Number n;
5078 
5079     e ->Offsets[i] = Before - BaseOffset;
5080 
5081     if (wcstr == NULL) {
5082         e ->Sizes[i] = 0;
5083         e ->Offsets[i] = 0;
5084         return TRUE;
5085     }
5086 
5087     n = mywcslen(wcstr);
5088     if (!_cmsWriteWCharArray(io,  n, wcstr)) return FALSE;
5089 
5090     e ->Sizes[i] = io ->Tell(io) - Before;
5091     return TRUE;
5092 }
5093 
5094 static
5095 cmsBool ReadOneMLUC(struct _cms_typehandler_struct* self, cmsIOHANDLER* io,  _cmsDICelem* e, cmsUInt32Number i, cmsMLU** mlu)
5096 {
5097     cmsUInt32Number nItems = 0;
5098 
5099     // A way to get null MLUCs
5100     if (e -> Offsets[i] == 0 || e ->Sizes[i] == 0) {
5101 
5102         *mlu = NULL;
5103         return TRUE;
5104     }
5105 
5106     if (!io -> Seek(io, e -> Offsets[i])) return FALSE;
5107 
5108     *mlu = (cmsMLU*) Type_MLU_Read(self, io, &nItems, e ->Sizes[i]);
5109     return *mlu != NULL;
5110 }
5111 
5112 static
5113 cmsBool WriteOneMLUC(struct _cms_typehandler_struct* self, cmsIOHANDLER* io,  _cmsDICelem* e, cmsUInt32Number i, const cmsMLU* mlu, cmsUInt32Number BaseOffset)
5114 {
5115     cmsUInt32Number Before;
5116 
5117      // Special case for undefined strings (see ICC Votable
5118      // Proposal Submission, Dictionary Type and Metadata TAG Definition)
5119      if (mlu == NULL) {
5120         e ->Sizes[i] = 0;
5121         e ->Offsets[i] = 0;
5122         return TRUE;
5123     }
5124 
5125     Before = io ->Tell(io);
5126     e ->Offsets[i] = Before - BaseOffset;
5127 
5128     if (!Type_MLU_Write(self, io, (void*) mlu, 1)) return FALSE;
5129 
5130     e ->Sizes[i] = io ->Tell(io) - Before;
5131     return TRUE;
5132 }
5133 
5134 
5135 static
5136 void *Type_Dictionary_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
5137 {
5138    cmsHANDLE hDict;
5139    cmsUInt32Number i, Count, Length;
5140    cmsUInt32Number BaseOffset;
5141    _cmsDICarray a;
5142    wchar_t *NameWCS = NULL, *ValueWCS = NULL;
5143    cmsMLU *DisplayNameMLU = NULL, *DisplayValueMLU=NULL;
5144    cmsBool rc;
5145 
5146     *nItems = 0;
5147 
5148     // Get actual position as a basis for element offsets
5149     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
5150 
5151     // Get name-value record count
5152     if (!_cmsReadUInt32Number(io, &Count)) return NULL;
5153     SizeOfTag -= sizeof(cmsUInt32Number);
5154 
5155     // Get rec length
5156     if (!_cmsReadUInt32Number(io, &Length)) return NULL;
5157     SizeOfTag -= sizeof(cmsUInt32Number);
5158 
5159     // Check for valid lengths
5160     if (Length != 16 && Length != 24 && Length != 32) {
5161          cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown record length in dictionary '%d'", Length);
5162          return NULL;
5163     }
5164 
5165     // Creates an empty dictionary
5166     hDict = cmsDictAlloc(self -> ContextID);
5167     if (hDict == NULL) return NULL;
5168 
5169     // On depending on record size, create column arrays
5170     if (!AllocArray(self -> ContextID, &a, Count, Length)) goto Error;
5171 
5172     // Read column arrays
5173     if (!ReadOffsetArray(io, &a, Count, Length, BaseOffset)) goto Error;
5174 
5175     // Seek to each element and read it
5176     for (i=0; i < Count; i++) {
5177 
5178         if (!ReadOneWChar(io, &a.Name, i, &NameWCS)) goto Error;
5179         if (!ReadOneWChar(io, &a.Value, i, &ValueWCS)) goto Error;
5180 
5181         if (Length > 16) {
5182             if (!ReadOneMLUC(self, io, &a.DisplayName, i, &DisplayNameMLU)) goto Error;
5183         }
5184 
5185         if (Length > 24) {
5186             if (!ReadOneMLUC(self, io, &a.DisplayValue, i, &DisplayValueMLU)) goto Error;
5187         }
5188 
5189         if (NameWCS == NULL || ValueWCS == NULL) {
5190 
5191             cmsSignalError(self->ContextID, cmsERROR_CORRUPTION_DETECTED, "Bad dictionary Name/Value");
5192             rc = FALSE;
5193         }
5194         else {
5195 
5196             rc = cmsDictAddEntry(hDict, NameWCS, ValueWCS, DisplayNameMLU, DisplayValueMLU);
5197         }
5198 
5199         if (NameWCS != NULL) _cmsFree(self ->ContextID, NameWCS);
5200         if (ValueWCS != NULL) _cmsFree(self ->ContextID, ValueWCS);
5201         if (DisplayNameMLU != NULL) cmsMLUfree(DisplayNameMLU);
5202         if (DisplayValueMLU != NULL) cmsMLUfree(DisplayValueMLU);
5203 
5204         if (!rc) goto Error;
5205     }
5206 
5207    FreeArray(&a);
5208    *nItems = 1;
5209    return (void*) hDict;
5210 
5211 Error:
5212    FreeArray(&a);
5213    cmsDictFree(hDict);
5214    return NULL;
5215 }
5216 
5217 
5218 static
5219 cmsBool Type_Dictionary_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
5220 {
5221     cmsHANDLE hDict = (cmsHANDLE) Ptr;
5222     const cmsDICTentry* p;
5223     cmsBool AnyName, AnyValue;
5224     cmsUInt32Number i, Count, Length;
5225     cmsUInt32Number DirectoryPos, CurrentPos, BaseOffset;
5226    _cmsDICarray a;
5227 
5228     if (hDict == NULL) return FALSE;
5229 
5230     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
5231 
5232     // Let's inspect the dictionary
5233     Count = 0; AnyName = FALSE; AnyValue = FALSE;
5234     for (p = cmsDictGetEntryList(hDict); p != NULL; p = cmsDictNextEntry(p)) {
5235 
5236         if (p ->DisplayName != NULL) AnyName = TRUE;
5237         if (p ->DisplayValue != NULL) AnyValue = TRUE;
5238         Count++;
5239     }
5240 
5241     Length = 16;
5242     if (AnyName)  Length += 8;
5243     if (AnyValue) Length += 8;
5244 
5245     if (!_cmsWriteUInt32Number(io, Count)) return FALSE;
5246     if (!_cmsWriteUInt32Number(io, Length)) return FALSE;
5247 
5248     // Keep starting position of offsets table
5249     DirectoryPos = io ->Tell(io);
5250 
5251     // Allocate offsets array
5252     if (!AllocArray(self ->ContextID, &a, Count, Length)) goto Error;
5253 
5254     // Write a fake directory to be filled latter on
5255     if (!WriteOffsetArray(io, &a, Count, Length)) goto Error;
5256 
5257     // Write each element. Keep track of the size as well.
5258     p = cmsDictGetEntryList(hDict);
5259     for (i=0; i < Count; i++) {
5260 
5261         if (!WriteOneWChar(io, &a.Name, i,  p ->Name, BaseOffset)) goto Error;
5262         if (!WriteOneWChar(io, &a.Value, i, p ->Value, BaseOffset)) goto Error;
5263 
5264         if (p ->DisplayName != NULL) {
5265             if (!WriteOneMLUC(self, io, &a.DisplayName, i, p ->DisplayName, BaseOffset)) goto Error;
5266         }
5267 
5268         if (p ->DisplayValue != NULL) {
5269             if (!WriteOneMLUC(self, io, &a.DisplayValue, i, p ->DisplayValue, BaseOffset)) goto Error;
5270         }
5271 
5272        p = cmsDictNextEntry(p);
5273     }
5274 
5275     // Write the directory
5276     CurrentPos = io ->Tell(io);
5277     if (!io ->Seek(io, DirectoryPos)) goto Error;
5278 
5279     if (!WriteOffsetArray(io, &a, Count, Length)) goto Error;
5280 
5281     if (!io ->Seek(io, CurrentPos)) goto Error;
5282 
5283     FreeArray(&a);
5284     return TRUE;
5285 
5286 Error:
5287     FreeArray(&a);
5288     return FALSE;
5289 
5290     cmsUNUSED_PARAMETER(nItems);
5291 }
5292 
5293 
5294 static
5295 void* Type_Dictionary_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
5296 {
5297     return (void*)  cmsDictDup((cmsHANDLE) Ptr);
5298 
5299     cmsUNUSED_PARAMETER(n);
5300     cmsUNUSED_PARAMETER(self);
5301 }
5302 
5303 
5304 static
5305 void Type_Dictionary_Free(struct _cms_typehandler_struct* self, void* Ptr)
5306 {
5307     cmsDictFree((cmsHANDLE) Ptr);
5308     cmsUNUSED_PARAMETER(self);
5309 }
5310 
5311 
5312 // ********************************************************************************
5313 // Type support main routines
5314 // ********************************************************************************
5315 
5316 
5317 // This is the list of built-in types
5318 static const _cmsTagTypeLinkedList SupportedTagTypes[] = {
5319 
5320 {TYPE_HANDLER(cmsSigChromaticityType,          Chromaticity),       (_cmsTagTypeLinkedList*) &SupportedTagTypes[1] },
5321 {TYPE_HANDLER(cmsSigColorantOrderType,         ColorantOrderType),  (_cmsTagTypeLinkedList*) &SupportedTagTypes[2] },
5322 {TYPE_HANDLER(cmsSigS15Fixed16ArrayType,       S15Fixed16),         (_cmsTagTypeLinkedList*) &SupportedTagTypes[3] },
5323 {TYPE_HANDLER(cmsSigU16Fixed16ArrayType,       U16Fixed16),         (_cmsTagTypeLinkedList*) &SupportedTagTypes[4] },
5324 {TYPE_HANDLER(cmsSigTextType,                  Text),               (_cmsTagTypeLinkedList*) &SupportedTagTypes[5] },
5325 {TYPE_HANDLER(cmsSigTextDescriptionType,       Text_Description),   (_cmsTagTypeLinkedList*) &SupportedTagTypes[6] },
5326 {TYPE_HANDLER(cmsSigCurveType,                 Curve),              (_cmsTagTypeLinkedList*) &SupportedTagTypes[7] },
5327 {TYPE_HANDLER(cmsSigParametricCurveType,       ParametricCurve),    (_cmsTagTypeLinkedList*) &SupportedTagTypes[8] },
5328 {TYPE_HANDLER(cmsSigDateTimeType,              DateTime),           (_cmsTagTypeLinkedList*) &SupportedTagTypes[9] },
5329 {TYPE_HANDLER(cmsSigLut8Type,                  LUT8),               (_cmsTagTypeLinkedList*) &SupportedTagTypes[10] },
5330 {TYPE_HANDLER(cmsSigLut16Type,                 LUT16),              (_cmsTagTypeLinkedList*) &SupportedTagTypes[11] },
5331 {TYPE_HANDLER(cmsSigColorantTableType,         ColorantTable),      (_cmsTagTypeLinkedList*) &SupportedTagTypes[12] },
5332 {TYPE_HANDLER(cmsSigNamedColor2Type,           NamedColor),         (_cmsTagTypeLinkedList*) &SupportedTagTypes[13] },
5333 {TYPE_HANDLER(cmsSigMultiLocalizedUnicodeType, MLU),                (_cmsTagTypeLinkedList*) &SupportedTagTypes[14] },
5334 {TYPE_HANDLER(cmsSigProfileSequenceDescType,   ProfileSequenceDesc),(_cmsTagTypeLinkedList*) &SupportedTagTypes[15] },
5335 {TYPE_HANDLER(cmsSigSignatureType,             Signature),          (_cmsTagTypeLinkedList*) &SupportedTagTypes[16] },
5336 {TYPE_HANDLER(cmsSigMeasurementType,           Measurement),        (_cmsTagTypeLinkedList*) &SupportedTagTypes[17] },
5337 {TYPE_HANDLER(cmsSigDataType,                  Data),               (_cmsTagTypeLinkedList*) &SupportedTagTypes[18] },
5338 {TYPE_HANDLER(cmsSigLutAtoBType,               LUTA2B),             (_cmsTagTypeLinkedList*) &SupportedTagTypes[19] },
5339 {TYPE_HANDLER(cmsSigLutBtoAType,               LUTB2A),             (_cmsTagTypeLinkedList*) &SupportedTagTypes[20] },
5340 {TYPE_HANDLER(cmsSigUcrBgType,                 UcrBg),              (_cmsTagTypeLinkedList*) &SupportedTagTypes[21] },
5341 {TYPE_HANDLER(cmsSigCrdInfoType,               CrdInfo),            (_cmsTagTypeLinkedList*) &SupportedTagTypes[22] },
5342 {TYPE_HANDLER(cmsSigMultiProcessElementType,   MPE),                (_cmsTagTypeLinkedList*) &SupportedTagTypes[23] },
5343 {TYPE_HANDLER(cmsSigScreeningType,             Screening),          (_cmsTagTypeLinkedList*) &SupportedTagTypes[24] },
5344 {TYPE_HANDLER(cmsSigViewingConditionsType,     ViewingConditions),  (_cmsTagTypeLinkedList*) &SupportedTagTypes[25] },
5345 {TYPE_HANDLER(cmsSigXYZType,                   XYZ),                (_cmsTagTypeLinkedList*) &SupportedTagTypes[26] },
5346 {TYPE_HANDLER(cmsCorbisBrokenXYZtype,          XYZ),                (_cmsTagTypeLinkedList*) &SupportedTagTypes[27] },
5347 {TYPE_HANDLER(cmsMonacoBrokenCurveType,        Curve),              (_cmsTagTypeLinkedList*) &SupportedTagTypes[28] },
5348 {TYPE_HANDLER(cmsSigProfileSequenceIdType,     ProfileSequenceId),  (_cmsTagTypeLinkedList*) &SupportedTagTypes[29] },
5349 {TYPE_HANDLER(cmsSigDictType,                  Dictionary),         (_cmsTagTypeLinkedList*) &SupportedTagTypes[30] },
5350 {TYPE_HANDLER(cmsSigVcgtType,                  vcgt),                NULL }
5351 };
5352 
5353 
5354 _cmsTagTypePluginChunkType _cmsTagTypePluginChunk = { NULL };
5355 
5356 
5357 
5358 // Duplicates the zone of memory used by the plug-in in the new context
5359 static
5360 void DupTagTypeList(struct _cmsContext_struct* ctx,
5361                     const struct _cmsContext_struct* src,
5362                     int loc)
5363 {
5364    _cmsTagTypePluginChunkType newHead = { NULL };
5365    _cmsTagTypeLinkedList*  entry;
5366    _cmsTagTypeLinkedList*  Anterior = NULL;
5367    _cmsTagTypePluginChunkType* head = (_cmsTagTypePluginChunkType*) src->chunks[loc];
5368 
5369    // Walk the list copying all nodes
5370    for (entry = head->TagTypes;
5371        entry != NULL;
5372        entry = entry ->Next) {
5373 
5374            _cmsTagTypeLinkedList *newEntry = ( _cmsTagTypeLinkedList *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTagTypeLinkedList));
5375 
5376            if (newEntry == NULL)
5377                return;
5378 
5379            // We want to keep the linked list order, so this is a little bit tricky
5380            newEntry -> Next = NULL;
5381            if (Anterior)
5382                Anterior -> Next = newEntry;
5383 
5384            Anterior = newEntry;
5385 
5386            if (newHead.TagTypes == NULL)
5387                newHead.TagTypes = newEntry;
5388    }
5389 
5390    ctx ->chunks[loc] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsTagTypePluginChunkType));
5391 }
5392 
5393 
5394 void _cmsAllocTagTypePluginChunk(struct _cmsContext_struct* ctx,
5395                                  const struct _cmsContext_struct* src)
5396 {
5397     if (src != NULL) {
5398 
5399         // Duplicate the LIST
5400         DupTagTypeList(ctx, src, TagTypePlugin);
5401     }
5402     else {
5403         static _cmsTagTypePluginChunkType TagTypePluginChunk = { NULL };
5404         ctx ->chunks[TagTypePlugin] = _cmsSubAllocDup(ctx ->MemPool, &TagTypePluginChunk, sizeof(_cmsTagTypePluginChunkType));
5405     }
5406 }
5407 
5408 void _cmsAllocMPETypePluginChunk(struct _cmsContext_struct* ctx,
5409                                const struct _cmsContext_struct* src)
5410 {
5411     if (src != NULL) {
5412 
5413         // Duplicate the LIST
5414         DupTagTypeList(ctx, src, MPEPlugin);
5415     }
5416     else {
5417         static _cmsTagTypePluginChunkType TagTypePluginChunk = { NULL };
5418         ctx ->chunks[MPEPlugin] = _cmsSubAllocDup(ctx ->MemPool, &TagTypePluginChunk, sizeof(_cmsTagTypePluginChunkType));
5419     }
5420 
5421 }
5422 
5423 
5424 // Both kind of plug-ins share same structure
5425 cmsBool  _cmsRegisterTagTypePlugin(cmsContext id, cmsPluginBase* Data)
5426 {
5427     return RegisterTypesPlugin(id, Data, TagTypePlugin);
5428 }
5429 
5430 cmsBool  _cmsRegisterMultiProcessElementPlugin(cmsContext id, cmsPluginBase* Data)
5431 {
5432     return RegisterTypesPlugin(id, Data,MPEPlugin);
5433 }
5434 
5435 
5436 // Wrapper for tag types
5437 cmsTagTypeHandler* _cmsGetTagTypeHandler(cmsContext ContextID, cmsTagTypeSignature sig)
5438 {
5439     _cmsTagTypePluginChunkType* ctx = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(ContextID, TagTypePlugin);
5440 
5441     return GetHandler(sig, ctx->TagTypes, (_cmsTagTypeLinkedList*) SupportedTagTypes);
5442 }
5443 
5444 // ********************************************************************************
5445 // Tag support main routines
5446 // ********************************************************************************
5447 
5448 typedef struct _cmsTagLinkedList_st {
5449 
5450             cmsTagSignature Signature;
5451             cmsTagDescriptor Descriptor;
5452             struct _cmsTagLinkedList_st* Next;
5453 
5454 } _cmsTagLinkedList;
5455 
5456 // This is the list of built-in tags. The data of this list can be modified by plug-ins
5457 static _cmsTagLinkedList SupportedTags[] = {
5458 
5459     { cmsSigAToB0Tag,               { 1, 3,  { cmsSigLut16Type,  cmsSigLutAtoBType, cmsSigLut8Type}, DecideLUTtypeA2B}, &SupportedTags[1]},
5460     { cmsSigAToB1Tag,               { 1, 3,  { cmsSigLut16Type,  cmsSigLutAtoBType, cmsSigLut8Type}, DecideLUTtypeA2B}, &SupportedTags[2]},
5461     { cmsSigAToB2Tag,               { 1, 3,  { cmsSigLut16Type,  cmsSigLutAtoBType, cmsSigLut8Type}, DecideLUTtypeA2B}, &SupportedTags[3]},
5462     { cmsSigBToA0Tag,               { 1, 3,  { cmsSigLut16Type,  cmsSigLutBtoAType, cmsSigLut8Type}, DecideLUTtypeB2A}, &SupportedTags[4]},
5463     { cmsSigBToA1Tag,               { 1, 3,  { cmsSigLut16Type,  cmsSigLutBtoAType, cmsSigLut8Type}, DecideLUTtypeB2A}, &SupportedTags[5]},
5464     { cmsSigBToA2Tag,               { 1, 3,  { cmsSigLut16Type,  cmsSigLutBtoAType, cmsSigLut8Type}, DecideLUTtypeB2A}, &SupportedTags[6]},
5465 
5466     // Allow corbis  and its broken XYZ type
5467     { cmsSigRedColorantTag,         { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, DecideXYZtype}, &SupportedTags[7]},
5468     { cmsSigGreenColorantTag,       { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, DecideXYZtype}, &SupportedTags[8]},
5469     { cmsSigBlueColorantTag,        { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, DecideXYZtype}, &SupportedTags[9]},
5470 
5471     { cmsSigRedTRCTag,              { 1, 3, { cmsSigCurveType, cmsSigParametricCurveType, cmsMonacoBrokenCurveType }, DecideCurveType}, &SupportedTags[10]},
5472     { cmsSigGreenTRCTag,            { 1, 3, { cmsSigCurveType, cmsSigParametricCurveType, cmsMonacoBrokenCurveType }, DecideCurveType}, &SupportedTags[11]},
5473     { cmsSigBlueTRCTag,             { 1, 3, { cmsSigCurveType, cmsSigParametricCurveType, cmsMonacoBrokenCurveType }, DecideCurveType}, &SupportedTags[12]},
5474 
5475     { cmsSigCalibrationDateTimeTag, { 1, 1, { cmsSigDateTimeType }, NULL}, &SupportedTags[13]},
5476     { cmsSigCharTargetTag,          { 1, 1, { cmsSigTextType },     NULL}, &SupportedTags[14]},
5477 
5478     { cmsSigChromaticAdaptationTag, { 9, 1, { cmsSigS15Fixed16ArrayType }, NULL}, &SupportedTags[15]},
5479     { cmsSigChromaticityTag,        { 1, 1, { cmsSigChromaticityType    }, NULL}, &SupportedTags[16]},
5480     { cmsSigColorantOrderTag,       { 1, 1, { cmsSigColorantOrderType   }, NULL}, &SupportedTags[17]},
5481     { cmsSigColorantTableTag,       { 1, 1, { cmsSigColorantTableType   }, NULL}, &SupportedTags[18]},
5482     { cmsSigColorantTableOutTag,    { 1, 1, { cmsSigColorantTableType   }, NULL}, &SupportedTags[19]},
5483 
5484     { cmsSigCopyrightTag,           { 1, 3, { cmsSigTextType,  cmsSigMultiLocalizedUnicodeType, cmsSigTextDescriptionType}, DecideTextType}, &SupportedTags[20]},
5485     { cmsSigDateTimeTag,            { 1, 1, { cmsSigDateTimeType }, NULL}, &SupportedTags[21]},
5486 
5487     { cmsSigDeviceMfgDescTag,       { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[22]},
5488     { cmsSigDeviceModelDescTag,     { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[23]},
5489 
5490     { cmsSigGamutTag,               { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[24]},
5491 
5492     { cmsSigGrayTRCTag,             { 1, 2, { cmsSigCurveType, cmsSigParametricCurveType }, DecideCurveType}, &SupportedTags[25]},
5493     { cmsSigLuminanceTag,           { 1, 1, { cmsSigXYZType }, NULL}, &SupportedTags[26]},
5494 
5495     { cmsSigMediaBlackPointTag,     { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, NULL}, &SupportedTags[27]},
5496     { cmsSigMediaWhitePointTag,     { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, NULL}, &SupportedTags[28]},
5497 
5498     { cmsSigNamedColor2Tag,         { 1, 1, { cmsSigNamedColor2Type }, NULL}, &SupportedTags[29]},
5499 
5500     { cmsSigPreview0Tag,            { 1, 3,  { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[30]},
5501     { cmsSigPreview1Tag,            { 1, 3,  { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[31]},
5502     { cmsSigPreview2Tag,            { 1, 3,  { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[32]},
5503 
5504     { cmsSigProfileDescriptionTag,  { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[33]},
5505     { cmsSigProfileSequenceDescTag, { 1, 1, { cmsSigProfileSequenceDescType }, NULL},  &SupportedTags[34]},
5506     { cmsSigTechnologyTag,          { 1, 1, { cmsSigSignatureType }, NULL},  &SupportedTags[35]},
5507 
5508     { cmsSigColorimetricIntentImageStateTag,   { 1, 1, { cmsSigSignatureType }, NULL}, &SupportedTags[36]},
5509     { cmsSigPerceptualRenderingIntentGamutTag, { 1, 1, { cmsSigSignatureType }, NULL}, &SupportedTags[37]},
5510     { cmsSigSaturationRenderingIntentGamutTag, { 1, 1, { cmsSigSignatureType }, NULL}, &SupportedTags[38]},
5511 
5512     { cmsSigMeasurementTag,         { 1, 1, { cmsSigMeasurementType }, NULL}, &SupportedTags[39]},
5513 
5514     { cmsSigPs2CRD0Tag,             { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[40]},
5515     { cmsSigPs2CRD1Tag,             { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[41]},
5516     { cmsSigPs2CRD2Tag,             { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[42]},
5517     { cmsSigPs2CRD3Tag,             { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[43]},
5518     { cmsSigPs2CSATag,              { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[44]},
5519     { cmsSigPs2RenderingIntentTag,  { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[45]},
5520 
5521     { cmsSigViewingCondDescTag,     { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[46]},
5522 
5523     { cmsSigUcrBgTag,               { 1, 1, { cmsSigUcrBgType}, NULL},    &SupportedTags[47]},
5524     { cmsSigCrdInfoTag,             { 1, 1, { cmsSigCrdInfoType}, NULL},  &SupportedTags[48]},
5525 
5526     { cmsSigDToB0Tag,               { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[49]},
5527     { cmsSigDToB1Tag,               { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[50]},
5528     { cmsSigDToB2Tag,               { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[51]},
5529     { cmsSigDToB3Tag,               { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[52]},
5530     { cmsSigBToD0Tag,               { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[53]},
5531     { cmsSigBToD1Tag,               { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[54]},
5532     { cmsSigBToD2Tag,               { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[55]},
5533     { cmsSigBToD3Tag,               { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[56]},
5534 
5535     { cmsSigScreeningDescTag,       { 1, 1, { cmsSigTextDescriptionType },    NULL}, &SupportedTags[57]},
5536     { cmsSigViewingConditionsTag,   { 1, 1, { cmsSigViewingConditionsType },  NULL}, &SupportedTags[58]},
5537 
5538     { cmsSigScreeningTag,           { 1, 1, { cmsSigScreeningType},          NULL }, &SupportedTags[59]},
5539     { cmsSigVcgtTag,                { 1, 1, { cmsSigVcgtType},               NULL }, &SupportedTags[60]},
5540     { cmsSigMetaTag,                { 1, 1, { cmsSigDictType},               NULL }, &SupportedTags[61]},
5541     { cmsSigProfileSequenceIdTag,   { 1, 1, { cmsSigProfileSequenceIdType},  NULL }, &SupportedTags[62]},
5542 
5543     { cmsSigProfileDescriptionMLTag,{ 1, 1, { cmsSigMultiLocalizedUnicodeType}, NULL}, &SupportedTags[63]},
5544     { cmsSigArgyllArtsTag,          { 9, 1, { cmsSigS15Fixed16ArrayType},    NULL}, NULL}
5545 
5546 };
5547 
5548 /*
5549     Not supported                 Why
5550     =======================       =========================================
5551     cmsSigOutputResponseTag   ==> WARNING, POSSIBLE PATENT ON THIS SUBJECT!
5552     cmsSigNamedColorTag       ==> Deprecated
5553     cmsSigDataTag             ==> Ancient, unused
5554     cmsSigDeviceSettingsTag   ==> Deprecated, useless
5555 */
5556 
5557 
5558 _cmsTagPluginChunkType _cmsTagPluginChunk = { NULL };
5559 
5560 
5561 // Duplicates the zone of memory used by the plug-in in the new context
5562 static
5563 void DupTagList(struct _cmsContext_struct* ctx,
5564                     const struct _cmsContext_struct* src)
5565 {
5566    _cmsTagPluginChunkType newHead = { NULL };
5567    _cmsTagLinkedList*  entry;
5568    _cmsTagLinkedList*  Anterior = NULL;
5569    _cmsTagPluginChunkType* head = (_cmsTagPluginChunkType*) src->chunks[TagPlugin];
5570 
5571    // Walk the list copying all nodes
5572    for (entry = head->Tag;
5573        entry != NULL;
5574        entry = entry ->Next) {
5575 
5576            _cmsTagLinkedList *newEntry = ( _cmsTagLinkedList *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTagLinkedList));
5577 
5578            if (newEntry == NULL)
5579                return;
5580 
5581            // We want to keep the linked list order, so this is a little bit tricky
5582            newEntry -> Next = NULL;
5583            if (Anterior)
5584                Anterior -> Next = newEntry;
5585 
5586            Anterior = newEntry;
5587 
5588            if (newHead.Tag == NULL)
5589                newHead.Tag = newEntry;
5590    }
5591 
5592    ctx ->chunks[TagPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsTagPluginChunkType));
5593 }
5594 
5595 void _cmsAllocTagPluginChunk(struct _cmsContext_struct* ctx,
5596                                  const struct _cmsContext_struct* src)
5597 {
5598     if (src != NULL) {
5599 
5600         DupTagList(ctx, src);
5601     }
5602     else {
5603         static _cmsTagPluginChunkType TagPluginChunk = { NULL };
5604         ctx ->chunks[TagPlugin] = _cmsSubAllocDup(ctx ->MemPool, &TagPluginChunk, sizeof(_cmsTagPluginChunkType));
5605     }
5606 
5607 }
5608 
5609 cmsBool  _cmsRegisterTagPlugin(cmsContext id, cmsPluginBase* Data)
5610 {
5611     cmsPluginTag* Plugin = (cmsPluginTag*) Data;
5612     _cmsTagLinkedList *pt;
5613     _cmsTagPluginChunkType* TagPluginChunk = ( _cmsTagPluginChunkType*) _cmsContextGetClientChunk(id, TagPlugin);
5614 
5615     if (Data == NULL) {
5616 
5617         TagPluginChunk->Tag = NULL;
5618         return TRUE;
5619     }
5620 
5621     pt = (_cmsTagLinkedList*) _cmsPluginMalloc(id, sizeof(_cmsTagLinkedList));
5622     if (pt == NULL) return FALSE;
5623 
5624     pt ->Signature  = Plugin ->Signature;
5625     pt ->Descriptor = Plugin ->Descriptor;
5626     pt ->Next       = TagPluginChunk ->Tag;
5627 
5628     TagPluginChunk ->Tag = pt;
5629 
5630     return TRUE;
5631 }
5632 
5633 // Return a descriptor for a given tag or NULL
5634 cmsTagDescriptor* _cmsGetTagDescriptor(cmsContext ContextID, cmsTagSignature sig)
5635 {
5636     _cmsTagLinkedList* pt;
5637     _cmsTagPluginChunkType* TagPluginChunk = ( _cmsTagPluginChunkType*) _cmsContextGetClientChunk(ContextID, TagPlugin);
5638 
5639     for (pt = TagPluginChunk->Tag;
5640              pt != NULL;
5641              pt = pt ->Next) {
5642 
5643                 if (sig == pt -> Signature) return &pt ->Descriptor;
5644     }
5645 
5646     for (pt = SupportedTags;
5647             pt != NULL;
5648             pt = pt ->Next) {
5649 
5650                 if (sig == pt -> Signature) return &pt ->Descriptor;
5651     }
5652 
5653     return NULL;
5654 }
5655 
--- EOF ---