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