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 
1488         // True begin of the string
1489         BeginOfThisString = Offset - SizeOfHeader - 8;
1490 
1491         // Ajust to wchar_t elements
1492         mlu ->Entries[i].Len = (Len * sizeof(wchar_t)) / sizeof(cmsUInt16Number);
1493         mlu ->Entries[i].StrW = (BeginOfThisString * sizeof(wchar_t)) / sizeof(cmsUInt16Number);
1494 
1495         // To guess maximum size, add offset + len
1496         EndOfThisString = BeginOfThisString + Len;
1497         if (EndOfThisString > LargestPosition)
1498             LargestPosition = EndOfThisString;
1499     }
1500 
1501     // Now read the remaining of tag and fill all strings. Substract the directory
1502     SizeOfTag   = (LargestPosition * sizeof(wchar_t)) / sizeof(cmsUInt16Number);
1503     if (SizeOfTag == 0)
1504     {
1505         Block = NULL;
1506         NumOfWchar = 0;
1507 
1508     }
1509     else
1510     {
1511         Block = (wchar_t*) _cmsMalloc(self ->ContextID, SizeOfTag);
1512         if (Block == NULL) goto Error;
1513         NumOfWchar = SizeOfTag / sizeof(wchar_t);
1514         if (!_cmsReadWCharArray(io, NumOfWchar, Block)) goto Error;
1515     }
1516 
1517     mlu ->MemPool  = Block;
1518     mlu ->PoolSize = SizeOfTag;
1519     mlu ->PoolUsed = SizeOfTag;
1520 
1521     *nItems = 1;
1522     return (void*) mlu;
1523 
1524 Error:
1525     if (mlu) cmsMLUfree(mlu);
1526     return NULL;
1527 }
1528 
1529 static
1530 cmsBool  Type_MLU_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
1531 {
1532     cmsMLU* mlu =(cmsMLU*) Ptr;
1533     cmsUInt32Number HeaderSize;
1534     cmsUInt32Number  Len, Offset;
1535     int i;
1536 
1537     if (Ptr == NULL) {
1538 
1539           // Empty placeholder
1540           if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
1541           if (!_cmsWriteUInt32Number(io, 12)) return FALSE;
1542           return TRUE;
1543     }
1544 
1545     if (!_cmsWriteUInt32Number(io, mlu ->UsedEntries)) return FALSE;
1546     if (!_cmsWriteUInt32Number(io, 12)) return FALSE;
1547 
1548     HeaderSize = 12 * mlu ->UsedEntries + sizeof(_cmsTagBase);
1549 
1550     for (i=0; i < mlu ->UsedEntries; i++) {
1551 
1552         Len    =  mlu ->Entries[i].Len;
1553         Offset =  mlu ->Entries[i].StrW;
1554 
1555         Len    = (Len * sizeof(cmsUInt16Number)) / sizeof(wchar_t);
1556         Offset = (Offset * sizeof(cmsUInt16Number)) / sizeof(wchar_t) + HeaderSize + 8;
1557 
1558         if (!_cmsWriteUInt16Number(io, mlu ->Entries[i].Language)) return FALSE;
1559         if (!_cmsWriteUInt16Number(io, mlu ->Entries[i].Country))  return FALSE;
1560         if (!_cmsWriteUInt32Number(io, Len)) return FALSE;
1561         if (!_cmsWriteUInt32Number(io, Offset)) return FALSE;
1562     }
1563 
1564     if (!_cmsWriteWCharArray(io, mlu ->PoolUsed / sizeof(wchar_t), (wchar_t*)  mlu ->MemPool)) return FALSE;
1565 
1566     return TRUE;
1567 
1568     cmsUNUSED_PARAMETER(nItems);
1569     cmsUNUSED_PARAMETER(self);
1570 }
1571 
1572 
1573 static
1574 void* Type_MLU_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
1575 {
1576     return (void*) cmsMLUdup((cmsMLU*) Ptr);
1577 
1578     cmsUNUSED_PARAMETER(n);
1579     cmsUNUSED_PARAMETER(self);
1580 }
1581 
1582 static
1583 void Type_MLU_Free(struct _cms_typehandler_struct* self, void* Ptr)
1584 {
1585     cmsMLUfree((cmsMLU*) Ptr);
1586     return;
1587 
1588     cmsUNUSED_PARAMETER(self);
1589 }
1590 
1591 
1592 // ********************************************************************************
1593 // Type cmsSigLut8Type
1594 // ********************************************************************************
1595 
1596 // Decide which LUT type to use on writting
1597 static
1598 cmsTagTypeSignature DecideLUTtypeA2B(cmsFloat64Number ICCVersion, const void *Data)
1599 {
1600     cmsPipeline* Lut = (cmsPipeline*) Data;
1601 
1602     if (ICCVersion < 4.0) {
1603         if (Lut ->SaveAs8Bits) return cmsSigLut8Type;
1604         return cmsSigLut16Type;
1605     }
1606     else {
1607          return cmsSigLutAtoBType;
1608     }
1609 }
1610 
1611 static
1612 cmsTagTypeSignature DecideLUTtypeB2A(cmsFloat64Number ICCVersion, const void *Data)
1613 {
1614     cmsPipeline* Lut = (cmsPipeline*) Data;
1615 
1616     if (ICCVersion < 4.0) {
1617         if (Lut ->SaveAs8Bits) return cmsSigLut8Type;
1618         return cmsSigLut16Type;
1619     }
1620     else {
1621          return cmsSigLutBtoAType;
1622     }
1623 }
1624 
1625 /*
1626 This structure represents a colour transform using tables of 8-bit precision.
1627 This type contains four processing elements: a 3 by 3 matrix (which shall be
1628 the identity matrix unless the input colour space is XYZ), a set of one dimensional
1629 input tables, a multidimensional lookup table, and a set of one dimensional output
1630 tables. Data is processed using these elements via the following sequence:
1631 (matrix) -> (1d input tables)  -> (multidimensional lookup table - CLUT) -> (1d output tables)
1632 
1633 Byte Position   Field Length (bytes)  Content Encoded as...
1634 8                  1          Number of Input Channels (i)    uInt8Number
1635 9                  1          Number of Output Channels (o)   uInt8Number
1636 10                 1          Number of CLUT grid points (identical for each side) (g) uInt8Number
1637 11                 1          Reserved for padding (fill with 00h)
1638 
1639 12..15             4          Encoded e00 parameter   s15Fixed16Number
1640 */
1641 
1642 
1643 // Read 8 bit tables as gamma functions
1644 static
1645 cmsBool  Read8bitTables(cmsContext ContextID, cmsIOHANDLER* io, cmsPipeline* lut, int nChannels)
1646 {
1647     cmsUInt8Number* Temp = NULL;
1648     int i, j;
1649     cmsToneCurve* Tables[cmsMAXCHANNELS];
1650 
1651     if (nChannels > cmsMAXCHANNELS) return FALSE;
1652     if (nChannels <= 0) return FALSE;
1653 
1654     memset(Tables, 0, sizeof(Tables));
1655 
1656     Temp = (cmsUInt8Number*) _cmsMalloc(ContextID, 256);
1657     if (Temp == NULL) return FALSE;
1658 
1659     for (i=0; i < nChannels; i++) {
1660         Tables[i] = cmsBuildTabulatedToneCurve16(ContextID, 256, NULL);
1661         if (Tables[i] == NULL) goto Error;
1662     }
1663 
1664     for (i=0; i < nChannels; i++) {
1665 
1666         if (io ->Read(io, Temp, 256, 1) != 1) goto Error;
1667 
1668         for (j=0; j < 256; j++)
1669             Tables[i]->Table16[j] = (cmsUInt16Number) FROM_8_TO_16(Temp[j]);
1670     }
1671 
1672     _cmsFree(ContextID, Temp);
1673     Temp = NULL;
1674 
1675     if (!cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, nChannels, Tables)))
1676         goto Error;
1677 
1678     for (i=0; i < nChannels; i++)
1679         cmsFreeToneCurve(Tables[i]);
1680 
1681     return TRUE;
1682 
1683 Error:
1684     for (i=0; i < nChannels; i++) {
1685         if (Tables[i]) cmsFreeToneCurve(Tables[i]);
1686     }
1687 
1688     if (Temp) _cmsFree(ContextID, Temp);
1689     return FALSE;
1690 }
1691 
1692 
1693 static
1694 cmsBool Write8bitTables(cmsContext ContextID, cmsIOHANDLER* io, cmsUInt32Number n, _cmsStageToneCurvesData* Tables)
1695 {
1696     int j;
1697     cmsUInt32Number i;
1698     cmsUInt8Number val;
1699 
1700     for (i=0; i < n; i++) {
1701 
1702         if (Tables) {
1703 
1704             // Usual case of identity curves
1705             if ((Tables ->TheCurves[i]->nEntries == 2) &&
1706                 (Tables->TheCurves[i]->Table16[0] == 0) &&
1707                 (Tables->TheCurves[i]->Table16[1] == 65535)) {
1708 
1709                     for (j=0; j < 256; j++) {
1710                         if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) j)) return FALSE;
1711                     }
1712             }
1713             else
1714                 if (Tables ->TheCurves[i]->nEntries != 256) {
1715                     cmsSignalError(ContextID, cmsERROR_RANGE, "LUT8 needs 256 entries on prelinearization");
1716                     return FALSE;
1717                 }
1718                 else
1719                     for (j=0; j < 256; j++) {
1720 
1721                         val = (cmsUInt8Number) FROM_16_TO_8(Tables->TheCurves[i]->Table16[j]);
1722 
1723                         if (!_cmsWriteUInt8Number(io, val)) return FALSE;
1724                     }
1725         }
1726     }
1727     return TRUE;
1728 }
1729 
1730 
1731 // Check overflow
1732 static
1733 cmsUInt32Number uipow(cmsUInt32Number n, cmsUInt32Number a, cmsUInt32Number b)
1734 {
1735     cmsUInt32Number rv = 1, rc;
1736 
1737     if (a == 0) return 0;
1738     if (n == 0) return 0;
1739 
1740     for (; b > 0; b--) {
1741 
1742         rv *= a;
1743 
1744         // Check for overflow
1745         if (rv > UINT_MAX / a) return (cmsUInt32Number) -1;
1746 
1747     }
1748 
1749     rc = rv * n;
1750 
1751     if (rv != rc / n) return (cmsUInt32Number) -1;
1752     return rc;
1753 }
1754 
1755 
1756 // That will create a MPE LUT with Matrix, pre tables, CLUT and post tables.
1757 // 8 bit lut may be scaled easely to v4 PCS, but we need also to properly adjust
1758 // PCS on BToAxx tags and AtoB if abstract. We need to fix input direction.
1759 
1760 static
1761 void *Type_LUT8_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
1762 {
1763     cmsUInt8Number InputChannels, OutputChannels, CLUTpoints;
1764     cmsUInt8Number* Temp = NULL;
1765     cmsPipeline* NewLUT = NULL;
1766     cmsUInt32Number nTabSize, i;
1767     cmsFloat64Number Matrix[3*3];
1768 
1769     *nItems = 0;
1770 
1771     if (!_cmsReadUInt8Number(io, &InputChannels)) goto Error;
1772     if (!_cmsReadUInt8Number(io, &OutputChannels)) goto Error;
1773     if (!_cmsReadUInt8Number(io, &CLUTpoints)) goto Error;
1774 
1775      if (CLUTpoints == 1) goto Error; // Impossible value, 0 for no CLUT and then 2 at least
1776 
1777     // Padding
1778     if (!_cmsReadUInt8Number(io, NULL)) goto Error;
1779 
1780     // Do some checking
1781     if (InputChannels > cmsMAXCHANNELS)  goto Error;
1782     if (OutputChannels > cmsMAXCHANNELS) goto Error;
1783 
1784    // Allocates an empty Pipeline
1785     NewLUT = cmsPipelineAlloc(self ->ContextID, InputChannels, OutputChannels);
1786     if (NewLUT == NULL) goto Error;
1787 
1788     // Read the Matrix
1789     if (!_cmsRead15Fixed16Number(io,  &Matrix[0])) goto Error;
1790     if (!_cmsRead15Fixed16Number(io,  &Matrix[1])) goto Error;
1791     if (!_cmsRead15Fixed16Number(io,  &Matrix[2])) goto Error;
1792     if (!_cmsRead15Fixed16Number(io,  &Matrix[3])) goto Error;
1793     if (!_cmsRead15Fixed16Number(io,  &Matrix[4])) goto Error;
1794     if (!_cmsRead15Fixed16Number(io,  &Matrix[5])) goto Error;
1795     if (!_cmsRead15Fixed16Number(io,  &Matrix[6])) goto Error;
1796     if (!_cmsRead15Fixed16Number(io,  &Matrix[7])) goto Error;
1797     if (!_cmsRead15Fixed16Number(io,  &Matrix[8])) goto Error;
1798 
1799 
1800     // Only operates if not identity...
1801     if ((InputChannels == 3) && !_cmsMAT3isIdentity((cmsMAT3*) Matrix)) {
1802 
1803         if (!cmsPipelineInsertStage(NewLUT, cmsAT_BEGIN, cmsStageAllocMatrix(self ->ContextID, 3, 3, Matrix, NULL)))
1804             goto Error;
1805     }
1806 
1807     // Get input tables
1808     if (!Read8bitTables(self ->ContextID, io,  NewLUT, InputChannels)) goto Error;
1809 
1810     // Get 3D CLUT. Check the overflow....
1811     nTabSize = uipow(OutputChannels, CLUTpoints, InputChannels);
1812     if (nTabSize == (cmsUInt32Number) -1) goto Error;
1813     if (nTabSize > 0) {
1814 
1815         cmsUInt16Number *PtrW, *T;
1816 
1817         PtrW = T  = (cmsUInt16Number*) _cmsCalloc(self ->ContextID, nTabSize, sizeof(cmsUInt16Number));
1818         if (T  == NULL) goto Error;
1819 
1820         Temp = (cmsUInt8Number*) _cmsMalloc(self ->ContextID, nTabSize);
1821         if (Temp == NULL) {
1822             _cmsFree(self ->ContextID, T);
1823             goto Error;
1824         }
1825 
1826         if (io ->Read(io, Temp, nTabSize, 1) != 1) {
1827             _cmsFree(self ->ContextID, T);
1828             _cmsFree(self ->ContextID, Temp);
1829             goto Error;
1830         }
1831 
1832         for (i = 0; i < nTabSize; i++) {
1833 
1834             *PtrW++ = FROM_8_TO_16(Temp[i]);
1835         }
1836         _cmsFree(self ->ContextID, Temp);
1837         Temp = NULL;
1838 
1839         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, cmsStageAllocCLut16bit(self ->ContextID, CLUTpoints, InputChannels, OutputChannels, T)))
1840             goto Error;
1841         _cmsFree(self ->ContextID, T);
1842     }
1843 
1844 
1845     // Get output tables
1846     if (!Read8bitTables(self ->ContextID, io,  NewLUT, OutputChannels)) goto Error;
1847 
1848     *nItems = 1;
1849     return NewLUT;
1850 
1851 Error:
1852     if (NewLUT != NULL) cmsPipelineFree(NewLUT);
1853     return NULL;
1854 
1855     cmsUNUSED_PARAMETER(SizeOfTag);
1856 }
1857 
1858 // We only allow a specific MPE structure: Matrix plus prelin, plus clut, plus post-lin.
1859 static
1860 cmsBool  Type_LUT8_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
1861 {
1862     cmsUInt32Number j, nTabSize;
1863     cmsUInt8Number  val;
1864     cmsPipeline* NewLUT = (cmsPipeline*) Ptr;
1865     cmsStage* mpe;
1866     _cmsStageToneCurvesData* PreMPE = NULL, *PostMPE = NULL;
1867     _cmsStageMatrixData* MatMPE = NULL;
1868     _cmsStageCLutData* clut = NULL;
1869     int clutPoints;
1870 
1871     // Disassemble the LUT into components.
1872     mpe = NewLUT -> Elements;
1873     if (mpe ->Type == cmsSigMatrixElemType) {
1874 
1875         MatMPE = (_cmsStageMatrixData*) mpe ->Data;
1876         mpe = mpe -> Next;
1877     }
1878 
1879     if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) {
1880         PreMPE = (_cmsStageToneCurvesData*) mpe ->Data;
1881         mpe = mpe -> Next;
1882     }
1883 
1884     if (mpe != NULL && mpe ->Type == cmsSigCLutElemType) {
1885         clut  = (_cmsStageCLutData*) mpe -> Data;
1886         mpe = mpe ->Next;
1887     }
1888 
1889     if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) {
1890         PostMPE = (_cmsStageToneCurvesData*) mpe ->Data;
1891         mpe = mpe -> Next;
1892     }
1893 
1894     // That should be all
1895     if (mpe != NULL) {
1896         cmsSignalError(mpe->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT is not suitable to be saved as LUT8");
1897         return FALSE;
1898     }
1899 
1900 
1901     if (clut == NULL)
1902         clutPoints = 0;
1903     else
1904         clutPoints    = clut->Params->nSamples[0];
1905 
1906     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) NewLUT ->InputChannels)) return FALSE;
1907     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) NewLUT ->OutputChannels)) return FALSE;
1908     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) clutPoints)) return FALSE;
1909     if (!_cmsWriteUInt8Number(io, 0)) return FALSE; // Padding
1910 
1911 
1912     if (MatMPE != NULL) {
1913 
1914         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[0])) return FALSE;
1915         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[1])) return FALSE;
1916         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[2])) return FALSE;
1917         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[3])) return FALSE;
1918         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[4])) return FALSE;
1919         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[5])) return FALSE;
1920         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[6])) return FALSE;
1921         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[7])) return FALSE;
1922         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[8])) return FALSE;
1923 
1924     }
1925     else {
1926 
1927         if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE;
1928         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
1929         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
1930         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
1931         if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE;
1932         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
1933         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
1934         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
1935         if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE;
1936     }
1937 
1938     // The prelinearization table
1939     if (!Write8bitTables(self ->ContextID, io, NewLUT ->InputChannels, PreMPE)) return FALSE;
1940 
1941     nTabSize = uipow(NewLUT->OutputChannels, clutPoints, NewLUT ->InputChannels);
1942     if (nTabSize == (cmsUInt32Number) -1) return FALSE;
1943     if (nTabSize > 0) {
1944 
1945         // The 3D CLUT.
1946         if (clut != NULL) {
1947 
1948             for (j=0; j < nTabSize; j++) {
1949 
1950                 val = (cmsUInt8Number) FROM_16_TO_8(clut ->Tab.T[j]);
1951                 if (!_cmsWriteUInt8Number(io, val)) return FALSE;
1952             }
1953         }
1954     }
1955 
1956     // The postlinearization table
1957     if (!Write8bitTables(self ->ContextID, io, NewLUT ->OutputChannels, PostMPE)) return FALSE;
1958 
1959     return TRUE;
1960 
1961     cmsUNUSED_PARAMETER(nItems);
1962 }
1963 
1964 
1965 static
1966 void* Type_LUT8_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
1967 {
1968     return (void*) cmsPipelineDup((cmsPipeline*) Ptr);
1969 
1970     cmsUNUSED_PARAMETER(n);
1971     cmsUNUSED_PARAMETER(self);
1972 }
1973 
1974 static
1975 void Type_LUT8_Free(struct _cms_typehandler_struct* self, void* Ptr)
1976 {
1977     cmsPipelineFree((cmsPipeline*) Ptr);
1978     return;
1979 
1980     cmsUNUSED_PARAMETER(self);
1981 }
1982 
1983 // ********************************************************************************
1984 // Type cmsSigLut16Type
1985 // ********************************************************************************
1986 
1987 // Read 16 bit tables as gamma functions
1988 static
1989 cmsBool  Read16bitTables(cmsContext ContextID, cmsIOHANDLER* io, cmsPipeline* lut, int nChannels, int nEntries)
1990 {
1991     int i;
1992     cmsToneCurve* Tables[cmsMAXCHANNELS];
1993 
1994     // Maybe an empty table? (this is a lcms extension)
1995     if (nEntries <= 0) return TRUE;
1996 
1997     // Check for malicious profiles
1998     if (nEntries < 2) return FALSE;
1999     if (nChannels > cmsMAXCHANNELS) return FALSE;
2000 
2001     // Init table to zero
2002     memset(Tables, 0, sizeof(Tables));
2003 
2004     for (i=0; i < nChannels; i++) {
2005 
2006         Tables[i] = cmsBuildTabulatedToneCurve16(ContextID, nEntries, NULL);
2007         if (Tables[i] == NULL) goto Error;
2008 
2009         if (!_cmsReadUInt16Array(io, nEntries, Tables[i]->Table16)) goto Error;
2010     }
2011 
2012 
2013     // Add the table (which may certainly be an identity, but this is up to the optimizer, not the reading code)
2014     if (!cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, nChannels, Tables)))
2015         goto Error;
2016 
2017     for (i=0; i < nChannels; i++)
2018         cmsFreeToneCurve(Tables[i]);
2019 
2020     return TRUE;
2021 
2022 Error:
2023     for (i=0; i < nChannels; i++) {
2024         if (Tables[i]) cmsFreeToneCurve(Tables[i]);
2025     }
2026 
2027     return FALSE;
2028 }
2029 
2030 static
2031 cmsBool Write16bitTables(cmsContext ContextID, cmsIOHANDLER* io, _cmsStageToneCurvesData* Tables)
2032 {
2033     int j;
2034     cmsUInt32Number i;
2035     cmsUInt16Number val;
2036     int nEntries;
2037 
2038     _cmsAssert(Tables != NULL);
2039 
2040     nEntries = Tables->TheCurves[0]->nEntries;
2041 
2042     for (i=0; i < Tables ->nCurves; i++) {
2043 
2044         for (j=0; j < nEntries; j++) {
2045 
2046             val = Tables->TheCurves[i]->Table16[j];
2047             if (!_cmsWriteUInt16Number(io, val)) return FALSE;
2048         }
2049     }
2050     return TRUE;
2051 
2052     cmsUNUSED_PARAMETER(ContextID);
2053 }
2054 
2055 static
2056 void *Type_LUT16_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
2057 {
2058     cmsUInt8Number InputChannels, OutputChannels, CLUTpoints;
2059     cmsPipeline* NewLUT = NULL;
2060     cmsUInt32Number nTabSize;
2061     cmsFloat64Number Matrix[3*3];
2062     cmsUInt16Number InputEntries, OutputEntries;
2063 
2064     *nItems = 0;
2065 
2066     if (!_cmsReadUInt8Number(io, &InputChannels)) return NULL;
2067     if (!_cmsReadUInt8Number(io, &OutputChannels)) return NULL;
2068     if (!_cmsReadUInt8Number(io, &CLUTpoints)) return NULL;   // 255 maximum
2069 
2070     // Padding
2071     if (!_cmsReadUInt8Number(io, NULL)) return NULL;
2072 
2073     // Do some checking
2074     if (InputChannels > cmsMAXCHANNELS)  goto Error;
2075     if (OutputChannels > cmsMAXCHANNELS) goto Error;
2076 
2077     // Allocates an empty LUT
2078     NewLUT = cmsPipelineAlloc(self ->ContextID, InputChannels, OutputChannels);
2079     if (NewLUT == NULL) goto Error;
2080 
2081     // Read the Matrix
2082     if (!_cmsRead15Fixed16Number(io,  &Matrix[0])) goto Error;
2083     if (!_cmsRead15Fixed16Number(io,  &Matrix[1])) goto Error;
2084     if (!_cmsRead15Fixed16Number(io,  &Matrix[2])) goto Error;
2085     if (!_cmsRead15Fixed16Number(io,  &Matrix[3])) goto Error;
2086     if (!_cmsRead15Fixed16Number(io,  &Matrix[4])) goto Error;
2087     if (!_cmsRead15Fixed16Number(io,  &Matrix[5])) goto Error;
2088     if (!_cmsRead15Fixed16Number(io,  &Matrix[6])) goto Error;
2089     if (!_cmsRead15Fixed16Number(io,  &Matrix[7])) goto Error;
2090     if (!_cmsRead15Fixed16Number(io,  &Matrix[8])) goto Error;
2091 
2092 
2093     // Only operates on 3 channels
2094     if ((InputChannels == 3) && !_cmsMAT3isIdentity((cmsMAT3*) Matrix)) {
2095 
2096         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, cmsStageAllocMatrix(self ->ContextID, 3, 3, Matrix, NULL)))
2097             goto Error;
2098     }
2099 
2100     if (!_cmsReadUInt16Number(io, &InputEntries)) goto Error;
2101     if (!_cmsReadUInt16Number(io, &OutputEntries)) goto Error;
2102 
2103     if (InputEntries > 0x7FFF || OutputEntries > 0x7FFF) goto Error;
2104     if (CLUTpoints == 1) goto Error; // Impossible value, 0 for no CLUT and then 2 at least
2105 
2106     // Get input tables
2107     if (!Read16bitTables(self ->ContextID, io,  NewLUT, InputChannels, InputEntries)) goto Error;
2108 
2109     // Get 3D CLUT
2110     nTabSize = uipow(OutputChannels, CLUTpoints, InputChannels);
2111     if (nTabSize == (cmsUInt32Number) -1) goto Error;
2112     if (nTabSize > 0) {
2113 
2114         cmsUInt16Number *T;
2115 
2116         T  = (cmsUInt16Number*) _cmsCalloc(self ->ContextID, nTabSize, sizeof(cmsUInt16Number));
2117         if (T  == NULL) goto Error;
2118 
2119         if (!_cmsReadUInt16Array(io, nTabSize, T)) {
2120             _cmsFree(self ->ContextID, T);
2121             goto Error;
2122         }
2123 
2124         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, cmsStageAllocCLut16bit(self ->ContextID, CLUTpoints, InputChannels, OutputChannels, T))) {
2125             _cmsFree(self ->ContextID, T);
2126             goto Error;
2127         }
2128         _cmsFree(self ->ContextID, T);
2129     }
2130 
2131 
2132     // Get output tables
2133     if (!Read16bitTables(self ->ContextID, io,  NewLUT, OutputChannels, OutputEntries)) goto Error;
2134 
2135     *nItems = 1;
2136     return NewLUT;
2137 
2138 Error:
2139     if (NewLUT != NULL) cmsPipelineFree(NewLUT);
2140     return NULL;
2141 
2142     cmsUNUSED_PARAMETER(SizeOfTag);
2143 }
2144 
2145 // We only allow some specific MPE structures: Matrix plus prelin, plus clut, plus post-lin.
2146 // Some empty defaults are created for missing parts
2147 
2148 static
2149 cmsBool  Type_LUT16_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
2150 {
2151     cmsUInt32Number nTabSize;
2152     cmsPipeline* NewLUT = (cmsPipeline*) Ptr;
2153     cmsStage* mpe;
2154     _cmsStageToneCurvesData* PreMPE = NULL, *PostMPE = NULL;
2155     _cmsStageMatrixData* MatMPE = NULL;
2156     _cmsStageCLutData* clut = NULL;
2157     int i, InputChannels, OutputChannels, clutPoints;
2158 
2159     // Disassemble the LUT into components.
2160     mpe = NewLUT -> Elements;
2161     if (mpe != NULL && mpe ->Type == cmsSigMatrixElemType) {
2162 
2163         MatMPE = (_cmsStageMatrixData*) mpe ->Data;
2164         mpe = mpe -> Next;
2165     }
2166 
2167 
2168     if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) {
2169         PreMPE = (_cmsStageToneCurvesData*) mpe ->Data;
2170         mpe = mpe -> Next;
2171     }
2172 
2173     if (mpe != NULL && mpe ->Type == cmsSigCLutElemType) {
2174         clut  = (_cmsStageCLutData*) mpe -> Data;
2175         mpe = mpe ->Next;
2176     }
2177 
2178     if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) {
2179         PostMPE = (_cmsStageToneCurvesData*) mpe ->Data;
2180         mpe = mpe -> Next;
2181     }
2182 
2183     // That should be all
2184     if (mpe != NULL) {
2185         cmsSignalError(mpe->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT is not suitable to be saved as LUT16");
2186         return FALSE;
2187     }
2188 
2189     InputChannels  = cmsPipelineInputChannels(NewLUT);
2190     OutputChannels = cmsPipelineOutputChannels(NewLUT);
2191 
2192     if (clut == NULL)
2193         clutPoints = 0;
2194     else
2195         clutPoints    = clut->Params->nSamples[0];
2196 
2197     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) InputChannels)) return FALSE;
2198     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) OutputChannels)) return FALSE;
2199     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) clutPoints)) return FALSE;
2200     if (!_cmsWriteUInt8Number(io, 0)) return FALSE; // Padding
2201 
2202 
2203     if (MatMPE != NULL) {
2204 
2205         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[0])) return FALSE;
2206         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[1])) return FALSE;
2207         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[2])) return FALSE;
2208         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[3])) return FALSE;
2209         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[4])) return FALSE;
2210         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[5])) return FALSE;
2211         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[6])) return FALSE;
2212         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[7])) return FALSE;
2213         if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[8])) return FALSE;
2214     }
2215     else {
2216 
2217         if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE;
2218         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
2219         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
2220         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
2221         if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE;
2222         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
2223         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
2224         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
2225         if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE;
2226     }
2227 
2228 
2229     if (PreMPE != NULL) {
2230         if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) PreMPE ->TheCurves[0]->nEntries)) return FALSE;
2231     } else {
2232             if (!_cmsWriteUInt16Number(io, 2)) return FALSE;
2233     }
2234 
2235     if (PostMPE != NULL) {
2236         if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) PostMPE ->TheCurves[0]->nEntries)) return FALSE;
2237     } else {
2238         if (!_cmsWriteUInt16Number(io, 2)) return FALSE;
2239 
2240     }
2241 
2242     // The prelinearization table
2243 
2244     if (PreMPE != NULL) {
2245         if (!Write16bitTables(self ->ContextID, io, PreMPE)) return FALSE;
2246     }
2247     else {
2248         for (i=0; i < InputChannels; i++) {
2249 
2250             if (!_cmsWriteUInt16Number(io, 0)) return FALSE;
2251             if (!_cmsWriteUInt16Number(io, 0xffff)) return FALSE;
2252         }
2253     }
2254 
2255     nTabSize = uipow(OutputChannels, clutPoints, InputChannels);
2256     if (nTabSize == (cmsUInt32Number) -1) return FALSE;
2257     if (nTabSize > 0) {
2258         // The 3D CLUT.
2259         if (clut != NULL) {
2260             if (!_cmsWriteUInt16Array(io, nTabSize, clut->Tab.T)) return FALSE;
2261         }
2262     }
2263 
2264     // The postlinearization table
2265     if (PostMPE != NULL) {
2266         if (!Write16bitTables(self ->ContextID, io, PostMPE)) return FALSE;
2267     }
2268     else {
2269         for (i=0; i < OutputChannels; i++) {
2270 
2271             if (!_cmsWriteUInt16Number(io, 0)) return FALSE;
2272             if (!_cmsWriteUInt16Number(io, 0xffff)) return FALSE;
2273         }
2274     }
2275 
2276     return TRUE;
2277 
2278     cmsUNUSED_PARAMETER(nItems);
2279 }
2280 
2281 static
2282 void* Type_LUT16_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
2283 {
2284     return (void*) cmsPipelineDup((cmsPipeline*) Ptr);
2285 
2286     cmsUNUSED_PARAMETER(n);
2287     cmsUNUSED_PARAMETER(self);
2288 }
2289 
2290 static
2291 void Type_LUT16_Free(struct _cms_typehandler_struct* self, void* Ptr)
2292 {
2293     cmsPipelineFree((cmsPipeline*) Ptr);
2294     return;
2295 
2296     cmsUNUSED_PARAMETER(self);
2297 }
2298 
2299 
2300 // ********************************************************************************
2301 // Type cmsSigLutAToBType
2302 // ********************************************************************************
2303 
2304 
2305 // V4 stuff. Read matrix for LutAtoB and LutBtoA
2306 
2307 static
2308 cmsStage* ReadMatrix(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number Offset)
2309 {
2310     cmsFloat64Number dMat[3*3];
2311     cmsFloat64Number dOff[3];
2312     cmsStage* Mat;
2313 
2314     // Go to address
2315     if (!io -> Seek(io, Offset)) return NULL;
2316 
2317     // Read the Matrix
2318     if (!_cmsRead15Fixed16Number(io, &dMat[0])) return NULL;
2319     if (!_cmsRead15Fixed16Number(io, &dMat[1])) return NULL;
2320     if (!_cmsRead15Fixed16Number(io, &dMat[2])) return NULL;
2321     if (!_cmsRead15Fixed16Number(io, &dMat[3])) return NULL;
2322     if (!_cmsRead15Fixed16Number(io, &dMat[4])) return NULL;
2323     if (!_cmsRead15Fixed16Number(io, &dMat[5])) return NULL;
2324     if (!_cmsRead15Fixed16Number(io, &dMat[6])) return NULL;
2325     if (!_cmsRead15Fixed16Number(io, &dMat[7])) return NULL;
2326     if (!_cmsRead15Fixed16Number(io, &dMat[8])) return NULL;
2327 
2328     if (!_cmsRead15Fixed16Number(io, &dOff[0])) return NULL;
2329     if (!_cmsRead15Fixed16Number(io, &dOff[1])) return NULL;
2330     if (!_cmsRead15Fixed16Number(io, &dOff[2])) return NULL;
2331 
2332     Mat = cmsStageAllocMatrix(self ->ContextID, 3, 3, dMat, dOff);
2333 
2334      return Mat;
2335 }
2336 
2337 
2338 
2339 
2340 //  V4 stuff. Read CLUT part for LutAtoB and LutBtoA
2341 
2342 static
2343 cmsStage* ReadCLUT(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number Offset, int InputChannels, int OutputChannels)
2344 {
2345     cmsUInt8Number  gridPoints8[cmsMAXCHANNELS]; // Number of grid points in each dimension.
2346     cmsUInt32Number GridPoints[cmsMAXCHANNELS], i;
2347     cmsUInt8Number  Precision;
2348     cmsStage* CLUT;
2349     _cmsStageCLutData* Data;
2350 
2351     if (!io -> Seek(io, Offset)) return NULL;
2352     if (io -> Read(io, gridPoints8, cmsMAXCHANNELS, 1) != 1) return NULL;
2353 
2354 
2355     for (i=0; i < cmsMAXCHANNELS; i++) {
2356 
2357         if (gridPoints8[i] == 1) return NULL; // Impossible value, 0 for no CLUT and then 2 at least
2358         GridPoints[i] = gridPoints8[i];
2359     }
2360 
2361     if (!_cmsReadUInt8Number(io, &Precision)) return NULL;
2362 
2363     if (!_cmsReadUInt8Number(io, NULL)) return NULL;
2364     if (!_cmsReadUInt8Number(io, NULL)) return NULL;
2365     if (!_cmsReadUInt8Number(io, NULL)) return NULL;
2366 
2367     CLUT = cmsStageAllocCLut16bitGranular(self ->ContextID, GridPoints, InputChannels, OutputChannels, NULL);
2368     if (CLUT == NULL) return NULL;
2369 
2370     Data = (_cmsStageCLutData*) CLUT ->Data;
2371 
2372     // Precision can be 1 or 2 bytes
2373     if (Precision == 1) {
2374 
2375         cmsUInt8Number  v;
2376 
2377         for (i=0; i < Data ->nEntries; i++) {
2378 
2379             if (io ->Read(io, &v, sizeof(cmsUInt8Number), 1) != 1) return NULL;
2380             Data ->Tab.T[i] = FROM_8_TO_16(v);
2381         }
2382 
2383     }
2384     else
2385         if (Precision == 2) {
2386 
2387             if (!_cmsReadUInt16Array(io, Data->nEntries, Data ->Tab.T)) {
2388                 cmsStageFree(CLUT);
2389                 return NULL;
2390             }
2391         }
2392         else {
2393             cmsStageFree(CLUT);
2394             cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown precision of '%d'", Precision);
2395             return NULL;
2396         }
2397 
2398         return CLUT;
2399 }
2400 
2401 static
2402 cmsToneCurve* ReadEmbeddedCurve(struct _cms_typehandler_struct* self, cmsIOHANDLER* io)
2403 {
2404     cmsTagTypeSignature  BaseType;
2405     cmsUInt32Number nItems;
2406 
2407     BaseType = _cmsReadTypeBase(io);
2408     switch (BaseType) {
2409 
2410             case cmsSigCurveType:
2411                 return (cmsToneCurve*) Type_Curve_Read(self, io, &nItems, 0);
2412 
2413             case cmsSigParametricCurveType:
2414                 return (cmsToneCurve*) Type_ParametricCurve_Read(self, io, &nItems, 0);
2415 
2416             default:
2417                 {
2418                     char String[5];
2419 
2420                     _cmsTagSignature2String(String, (cmsTagSignature) BaseType);
2421                     cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown curve type '%s'", String);
2422                 }
2423                 return NULL;
2424     }
2425 }
2426 
2427 
2428 // Read a set of curves from specific offset
2429 static
2430 cmsStage* ReadSetOfCurves(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number Offset, cmsUInt32Number nCurves)
2431 {
2432     cmsToneCurve* Curves[cmsMAXCHANNELS];
2433     cmsUInt32Number i;
2434     cmsStage* Lin = NULL;
2435 
2436     if (nCurves > cmsMAXCHANNELS) return FALSE;
2437 
2438     if (!io -> Seek(io, Offset)) return FALSE;
2439 
2440     for (i=0; i < nCurves; i++)
2441         Curves[i] = NULL;
2442 
2443     for (i=0; i < nCurves; i++) {
2444 
2445         Curves[i] = ReadEmbeddedCurve(self, io);
2446         if (Curves[i] == NULL) goto Error;
2447         if (!_cmsReadAlignment(io)) goto Error;
2448 
2449     }
2450 
2451     Lin = cmsStageAllocToneCurves(self ->ContextID, nCurves, Curves);
2452 
2453 Error:
2454     for (i=0; i < nCurves; i++)
2455         cmsFreeToneCurve(Curves[i]);
2456 
2457     return Lin;
2458 }
2459 
2460 
2461 // LutAtoB type
2462 
2463 // This structure represents a colour transform. The type contains up to five processing
2464 // elements which are stored in the AtoBTag tag in the following order: a set of one
2465 // dimensional curves, a 3 by 3 matrix with offset terms, a set of one dimensional curves,
2466 // a multidimensional lookup table, and a set of one dimensional output curves.
2467 // Data are processed using these elements via the following sequence:
2468 //
2469 //("A" curves) -> (multidimensional lookup table - CLUT) -> ("M" curves) -> (matrix) -> ("B" curves).
2470 //
2471 /*
2472 It is possible to use any or all of these processing elements. At least one processing element
2473 must be included.Only the following combinations are allowed:
2474 
2475 B
2476 M - Matrix - B
2477 A - CLUT - B
2478 A - CLUT - M - Matrix - B
2479 
2480 */
2481 
2482 static
2483 void* Type_LUTA2B_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
2484 {
2485     cmsUInt32Number      BaseOffset;
2486     cmsUInt8Number       inputChan;      // Number of input channels
2487     cmsUInt8Number       outputChan;     // Number of output channels
2488     cmsUInt32Number      offsetB;        // Offset to first "B" curve
2489     cmsUInt32Number      offsetMat;      // Offset to matrix
2490     cmsUInt32Number      offsetM;        // Offset to first "M" curve
2491     cmsUInt32Number      offsetC;        // Offset to CLUT
2492     cmsUInt32Number      offsetA;        // Offset to first "A" curve
2493     cmsPipeline* NewLUT = NULL;
2494 
2495 
2496     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
2497 
2498     if (!_cmsReadUInt8Number(io, &inputChan)) return NULL;
2499     if (!_cmsReadUInt8Number(io, &outputChan)) return NULL;
2500 
2501     if (!_cmsReadUInt16Number(io, NULL)) return NULL;
2502 
2503     if (!_cmsReadUInt32Number(io, &offsetB)) return NULL;
2504     if (!_cmsReadUInt32Number(io, &offsetMat)) return NULL;
2505     if (!_cmsReadUInt32Number(io, &offsetM)) return NULL;
2506     if (!_cmsReadUInt32Number(io, &offsetC)) return NULL;
2507     if (!_cmsReadUInt32Number(io, &offsetA)) return NULL;
2508 
2509    // Allocates an empty LUT
2510     NewLUT = cmsPipelineAlloc(self ->ContextID, inputChan, outputChan);
2511     if (NewLUT == NULL) return NULL;
2512 
2513     if (offsetA!= 0) {
2514         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetA, inputChan)))
2515             goto Error;
2516     }
2517 
2518     if (offsetC != 0) {
2519         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadCLUT(self, io, BaseOffset + offsetC, inputChan, outputChan)))
2520             goto Error;
2521     }
2522 
2523     if (offsetM != 0) {
2524         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetM, outputChan)))
2525             goto Error;
2526     }
2527 
2528     if (offsetMat != 0) {
2529         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadMatrix(self, io, BaseOffset + offsetMat)))
2530             goto Error;
2531     }
2532 
2533     if (offsetB != 0) {
2534         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetB, outputChan)))
2535             goto Error;
2536     }
2537 
2538     *nItems = 1;
2539     return NewLUT;
2540 Error:
2541     cmsPipelineFree(NewLUT);
2542     return NULL;
2543 
2544     cmsUNUSED_PARAMETER(SizeOfTag);
2545 }
2546 
2547 // Write a set of curves
2548 static
2549 cmsBool  WriteMatrix(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsStage* mpe)
2550 {
2551     _cmsStageMatrixData* m = (_cmsStageMatrixData*) mpe -> Data;
2552 
2553     // Write the Matrix
2554     if (!_cmsWrite15Fixed16Number(io, m -> Double[0])) return FALSE;
2555     if (!_cmsWrite15Fixed16Number(io, m -> Double[1])) return FALSE;
2556     if (!_cmsWrite15Fixed16Number(io, m -> Double[2])) return FALSE;
2557     if (!_cmsWrite15Fixed16Number(io, m -> Double[3])) return FALSE;
2558     if (!_cmsWrite15Fixed16Number(io, m -> Double[4])) return FALSE;
2559     if (!_cmsWrite15Fixed16Number(io, m -> Double[5])) return FALSE;
2560     if (!_cmsWrite15Fixed16Number(io, m -> Double[6])) return FALSE;
2561     if (!_cmsWrite15Fixed16Number(io, m -> Double[7])) return FALSE;
2562     if (!_cmsWrite15Fixed16Number(io, m -> Double[8])) return FALSE;
2563 
2564     if (m ->Offset != NULL) {
2565 
2566     if (!_cmsWrite15Fixed16Number(io, m -> Offset[0])) return FALSE;
2567     if (!_cmsWrite15Fixed16Number(io, m -> Offset[1])) return FALSE;
2568     if (!_cmsWrite15Fixed16Number(io, m -> Offset[2])) return FALSE;
2569     }
2570     else {
2571         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
2572         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
2573         if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE;
2574 
2575     }
2576 
2577 
2578     return TRUE;
2579 
2580     cmsUNUSED_PARAMETER(self);
2581 }
2582 
2583 
2584 // Write a set of curves
2585 static
2586 cmsBool WriteSetOfCurves(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsTagTypeSignature Type, cmsStage* mpe)
2587 {
2588     cmsUInt32Number i, n;
2589     cmsTagTypeSignature CurrentType;
2590     cmsToneCurve** Curves;
2591 
2592 
2593     n      = cmsStageOutputChannels(mpe);
2594     Curves = _cmsStageGetPtrToCurveSet(mpe);
2595 
2596     for (i=0; i < n; i++) {
2597 
2598         // If this is a table-based curve, use curve type even on V4
2599         CurrentType = Type;
2600 
2601         if ((Curves[i] ->nSegments == 0)||
2602             ((Curves[i]->nSegments == 2) && (Curves[i] ->Segments[1].Type == 0)) )
2603             CurrentType = cmsSigCurveType;
2604         else
2605         if (Curves[i] ->Segments[0].Type < 0)
2606             CurrentType = cmsSigCurveType;
2607 
2608         if (!_cmsWriteTypeBase(io, CurrentType)) return FALSE;
2609 
2610         switch (CurrentType) {
2611 
2612             case cmsSigCurveType:
2613                 if (!Type_Curve_Write(self, io, Curves[i], 1)) return FALSE;
2614                 break;
2615 
2616             case cmsSigParametricCurveType:
2617                 if (!Type_ParametricCurve_Write(self, io, Curves[i], 1)) return FALSE;
2618                 break;
2619 
2620             default:
2621                 {
2622                     char String[5];
2623 
2624                     _cmsTagSignature2String(String, (cmsTagSignature) Type);
2625                     cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown curve type '%s'", String);
2626                 }
2627                 return FALSE;
2628         }
2629 
2630         if (!_cmsWriteAlignment(io)) return FALSE;
2631     }
2632 
2633 
2634     return TRUE;
2635 }
2636 
2637 
2638 static
2639 cmsBool WriteCLUT(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt8Number  Precision, cmsStage* mpe)
2640 {
2641     cmsUInt8Number  gridPoints[cmsMAXCHANNELS]; // Number of grid points in each dimension.
2642     cmsUInt32Number i;
2643     _cmsStageCLutData* CLUT = ( _cmsStageCLutData*) mpe -> Data;
2644 
2645     if (CLUT ->HasFloatValues) {
2646          cmsSignalError(self ->ContextID, cmsERROR_NOT_SUITABLE, "Cannot save floating point data, CLUT are 8 or 16 bit only");
2647          return FALSE;
2648     }
2649 
2650     memset(gridPoints, 0, sizeof(gridPoints));
2651     for (i=0; i < (cmsUInt32Number) CLUT ->Params ->nInputs; i++)
2652         gridPoints[i] = (cmsUInt8Number) CLUT ->Params ->nSamples[i];
2653 
2654     if (!io -> Write(io, cmsMAXCHANNELS*sizeof(cmsUInt8Number), gridPoints)) return FALSE;
2655 
2656     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) Precision)) return FALSE;
2657     if (!_cmsWriteUInt8Number(io, 0)) return FALSE;
2658     if (!_cmsWriteUInt8Number(io, 0)) return FALSE;
2659     if (!_cmsWriteUInt8Number(io, 0)) return FALSE;
2660 
2661     // Precision can be 1 or 2 bytes
2662     if (Precision == 1) {
2663 
2664         for (i=0; i < CLUT->nEntries; i++) {
2665 
2666             if (!_cmsWriteUInt8Number(io, FROM_16_TO_8(CLUT->Tab.T[i]))) return FALSE;
2667         }
2668     }
2669     else
2670         if (Precision == 2) {
2671 
2672             if (!_cmsWriteUInt16Array(io, CLUT->nEntries, CLUT ->Tab.T)) return FALSE;
2673         }
2674         else {
2675              cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown precision of '%d'", Precision);
2676             return FALSE;
2677         }
2678 
2679         if (!_cmsWriteAlignment(io)) return FALSE;
2680 
2681         return TRUE;
2682 }
2683 
2684 
2685 
2686 
2687 static
2688 cmsBool Type_LUTA2B_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
2689 {
2690     cmsPipeline* Lut = (cmsPipeline*) Ptr;
2691     int inputChan, outputChan;
2692     cmsStage *A = NULL, *B = NULL, *M = NULL;
2693     cmsStage * Matrix = NULL;
2694     cmsStage * CLUT = NULL;
2695     cmsUInt32Number offsetB = 0, offsetMat = 0, offsetM = 0, offsetC = 0, offsetA = 0;
2696     cmsUInt32Number BaseOffset, DirectoryPos, CurrentPos;
2697 
2698     // Get the base for all offsets
2699     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
2700 
2701     if (Lut ->Elements != NULL)
2702         if (!cmsPipelineCheckAndRetreiveStages(Lut, 1, cmsSigCurveSetElemType, &B))
2703             if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, &M, &Matrix, &B))
2704                 if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, &A, &CLUT, &B))
2705                     if (!cmsPipelineCheckAndRetreiveStages(Lut, 5, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType,
2706                         cmsSigMatrixElemType, cmsSigCurveSetElemType, &A, &CLUT, &M, &Matrix, &B)) {
2707 
2708                             cmsSignalError(self->ContextID, cmsERROR_NOT_SUITABLE, "LUT is not suitable to be saved as LutAToB");
2709                             return FALSE;
2710                     }
2711 
2712     // Get input, output channels
2713     inputChan  = cmsPipelineInputChannels(Lut);
2714     outputChan = cmsPipelineOutputChannels(Lut);
2715 
2716     // Write channel count
2717     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) inputChan)) return FALSE;
2718     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) outputChan)) return FALSE;
2719     if (!_cmsWriteUInt16Number(io, 0)) return FALSE;
2720 
2721     // Keep directory to be filled latter
2722     DirectoryPos = io ->Tell(io);
2723 
2724     // Write the directory
2725     if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
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 
2731     if (A != NULL) {
2732 
2733         offsetA = io ->Tell(io) - BaseOffset;
2734         if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, A)) return FALSE;
2735     }
2736 
2737     if (CLUT != NULL) {
2738         offsetC = io ->Tell(io) - BaseOffset;
2739         if (!WriteCLUT(self, io, Lut ->SaveAs8Bits ? 1 : 2, CLUT)) return FALSE;
2740 
2741     }
2742     if (M != NULL) {
2743 
2744         offsetM = io ->Tell(io) - BaseOffset;
2745         if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, M)) return FALSE;
2746     }
2747 
2748     if (Matrix != NULL) {
2749         offsetMat = io ->Tell(io) - BaseOffset;
2750         if (!WriteMatrix(self, io, Matrix)) return FALSE;
2751     }
2752 
2753     if (B != NULL) {
2754 
2755         offsetB = io ->Tell(io) - BaseOffset;
2756         if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, B)) return FALSE;
2757     }
2758 
2759     CurrentPos = io ->Tell(io);
2760 
2761     if (!io ->Seek(io, DirectoryPos)) return FALSE;
2762 
2763     if (!_cmsWriteUInt32Number(io, offsetB)) return FALSE;
2764     if (!_cmsWriteUInt32Number(io, offsetMat)) return FALSE;
2765     if (!_cmsWriteUInt32Number(io, offsetM)) return FALSE;
2766     if (!_cmsWriteUInt32Number(io, offsetC)) return FALSE;
2767     if (!_cmsWriteUInt32Number(io, offsetA)) return FALSE;
2768 
2769     if (!io ->Seek(io, CurrentPos)) return FALSE;
2770 
2771     return TRUE;
2772 
2773     cmsUNUSED_PARAMETER(nItems);
2774 }
2775 
2776 
2777 static
2778 void* Type_LUTA2B_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
2779 {
2780     return (void*) cmsPipelineDup((cmsPipeline*) Ptr);
2781 
2782     cmsUNUSED_PARAMETER(n);
2783     cmsUNUSED_PARAMETER(self);
2784 }
2785 
2786 static
2787 void Type_LUTA2B_Free(struct _cms_typehandler_struct* self, void* Ptr)
2788 {
2789     cmsPipelineFree((cmsPipeline*) Ptr);
2790     return;
2791 
2792     cmsUNUSED_PARAMETER(self);
2793 }
2794 
2795 
2796 // LutBToA type
2797 
2798 static
2799 void* Type_LUTB2A_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
2800 {
2801     cmsUInt8Number       inputChan;      // Number of input channels
2802     cmsUInt8Number       outputChan;     // Number of output channels
2803     cmsUInt32Number      BaseOffset;     // Actual position in file
2804     cmsUInt32Number      offsetB;        // Offset to first "B" curve
2805     cmsUInt32Number      offsetMat;      // Offset to matrix
2806     cmsUInt32Number      offsetM;        // Offset to first "M" curve
2807     cmsUInt32Number      offsetC;        // Offset to CLUT
2808     cmsUInt32Number      offsetA;        // Offset to first "A" curve
2809     cmsPipeline* NewLUT = NULL;
2810 
2811 
2812     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
2813 
2814     if (!_cmsReadUInt8Number(io, &inputChan)) return NULL;
2815     if (!_cmsReadUInt8Number(io, &outputChan)) return NULL;
2816 
2817     // Padding
2818     if (!_cmsReadUInt16Number(io, NULL)) return NULL;
2819 
2820     if (!_cmsReadUInt32Number(io, &offsetB)) return NULL;
2821     if (!_cmsReadUInt32Number(io, &offsetMat)) return NULL;
2822     if (!_cmsReadUInt32Number(io, &offsetM)) return NULL;
2823     if (!_cmsReadUInt32Number(io, &offsetC)) return NULL;
2824     if (!_cmsReadUInt32Number(io, &offsetA)) return NULL;
2825 
2826     // Allocates an empty LUT
2827     NewLUT = cmsPipelineAlloc(self ->ContextID, inputChan, outputChan);
2828     if (NewLUT == NULL) return NULL;
2829 
2830     if (offsetB != 0) {
2831         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetB, inputChan)))
2832             goto Error;
2833     }
2834 
2835     if (offsetMat != 0) {
2836         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadMatrix(self, io, BaseOffset + offsetMat)))
2837             goto Error;
2838     }
2839 
2840     if (offsetM != 0) {
2841         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetM, inputChan)))
2842             goto Error;
2843     }
2844 
2845     if (offsetC != 0) {
2846         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadCLUT(self, io, BaseOffset + offsetC, inputChan, outputChan)))
2847             goto Error;
2848     }
2849 
2850     if (offsetA!= 0) {
2851         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetA, outputChan)))
2852             goto Error;
2853     }
2854 
2855     *nItems = 1;
2856     return NewLUT;
2857 Error:
2858     cmsPipelineFree(NewLUT);
2859     return NULL;
2860 
2861     cmsUNUSED_PARAMETER(SizeOfTag);
2862 }
2863 
2864 
2865 /*
2866 B
2867 B - Matrix - M
2868 B - CLUT - A
2869 B - Matrix - M - CLUT - A
2870 */
2871 
2872 static
2873 cmsBool  Type_LUTB2A_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
2874 {
2875     cmsPipeline* Lut = (cmsPipeline*) Ptr;
2876     int inputChan, outputChan;
2877     cmsStage *A = NULL, *B = NULL, *M = NULL;
2878     cmsStage *Matrix = NULL;
2879     cmsStage *CLUT = NULL;
2880     cmsUInt32Number offsetB = 0, offsetMat = 0, offsetM = 0, offsetC = 0, offsetA = 0;
2881     cmsUInt32Number BaseOffset, DirectoryPos, CurrentPos;
2882 
2883 
2884     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
2885 
2886     if (!cmsPipelineCheckAndRetreiveStages(Lut, 1, cmsSigCurveSetElemType, &B))
2887         if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, &B, &Matrix, &M))
2888             if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, &B, &CLUT, &A))
2889                 if (!cmsPipelineCheckAndRetreiveStages(Lut, 5, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType,
2890                     cmsSigCLutElemType, cmsSigCurveSetElemType, &B, &Matrix, &M, &CLUT, &A)) {
2891                         cmsSignalError(self->ContextID, cmsERROR_NOT_SUITABLE, "LUT is not suitable to be saved as LutBToA");
2892                         return FALSE;
2893                 }
2894 
2895     inputChan  = cmsPipelineInputChannels(Lut);
2896     outputChan = cmsPipelineOutputChannels(Lut);
2897 
2898     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) inputChan)) return FALSE;
2899     if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) outputChan)) return FALSE;
2900     if (!_cmsWriteUInt16Number(io, 0)) return FALSE;
2901 
2902     DirectoryPos = io ->Tell(io);
2903 
2904     if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
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 
2910     if (A != NULL) {
2911 
2912         offsetA = io ->Tell(io) - BaseOffset;
2913         if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, A)) return FALSE;
2914     }
2915 
2916     if (CLUT != NULL) {
2917         offsetC = io ->Tell(io) - BaseOffset;
2918         if (!WriteCLUT(self, io, Lut ->SaveAs8Bits ? 1 : 2, CLUT)) return FALSE;
2919 
2920     }
2921     if (M != NULL) {
2922 
2923         offsetM = io ->Tell(io) - BaseOffset;
2924         if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, M)) return FALSE;
2925     }
2926 
2927     if (Matrix != NULL) {
2928         offsetMat = io ->Tell(io) - BaseOffset;
2929         if (!WriteMatrix(self, io, Matrix)) return FALSE;
2930     }
2931 
2932     if (B != NULL) {
2933 
2934         offsetB = io ->Tell(io) - BaseOffset;
2935         if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, B)) return FALSE;
2936     }
2937 
2938     CurrentPos = io ->Tell(io);
2939 
2940     if (!io ->Seek(io, DirectoryPos)) return FALSE;
2941 
2942     if (!_cmsWriteUInt32Number(io, offsetB)) return FALSE;
2943     if (!_cmsWriteUInt32Number(io, offsetMat)) return FALSE;
2944     if (!_cmsWriteUInt32Number(io, offsetM)) return FALSE;
2945     if (!_cmsWriteUInt32Number(io, offsetC)) return FALSE;
2946     if (!_cmsWriteUInt32Number(io, offsetA)) return FALSE;
2947 
2948     if (!io ->Seek(io, CurrentPos)) return FALSE;
2949 
2950     return TRUE;
2951 
2952     cmsUNUSED_PARAMETER(nItems);
2953 }
2954 
2955 
2956 
2957 static
2958 void* Type_LUTB2A_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
2959 {
2960     return (void*) cmsPipelineDup((cmsPipeline*) Ptr);
2961 
2962     cmsUNUSED_PARAMETER(n);
2963     cmsUNUSED_PARAMETER(self);
2964 }
2965 
2966 static
2967 void Type_LUTB2A_Free(struct _cms_typehandler_struct* self, void* Ptr)
2968 {
2969     cmsPipelineFree((cmsPipeline*) Ptr);
2970     return;
2971 
2972     cmsUNUSED_PARAMETER(self);
2973 }
2974 
2975 
2976 
2977 // ********************************************************************************
2978 // Type cmsSigColorantTableType
2979 // ********************************************************************************
2980 /*
2981 The purpose of this tag is to identify the colorants used in the profile by a
2982 unique name and set of XYZ or L*a*b* values to give the colorant an unambiguous
2983 value. The first colorant listed is the colorant of the first device channel of
2984 a lut tag. The second colorant listed is the colorant of the second device channel
2985 of a lut tag, and so on.
2986 */
2987 
2988 static
2989 void *Type_ColorantTable_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
2990 {
2991     cmsUInt32Number i, Count;
2992     cmsNAMEDCOLORLIST* List;
2993     char Name[34];
2994     cmsUInt16Number PCS[3];
2995 
2996 
2997     if (!_cmsReadUInt32Number(io, &Count)) return NULL;
2998 
2999     if (Count > cmsMAXCHANNELS) {
3000         cmsSignalError(self->ContextID, cmsERROR_RANGE, "Too many colorants '%d'", Count);
3001         return NULL;
3002     }
3003 
3004     List = cmsAllocNamedColorList(self ->ContextID, Count, 0, "", "");
3005     for (i=0; i < Count; i++) {
3006 
3007         if (io ->Read(io, Name, 32, 1) != 1) goto Error;
3008         Name[33] = 0;
3009 
3010         if (!_cmsReadUInt16Array(io, 3, PCS)) goto Error;
3011 
3012         if (!cmsAppendNamedColor(List, Name, PCS, NULL)) goto Error;
3013 
3014     }
3015 
3016     *nItems = 1;
3017     return List;
3018 
3019 Error:
3020     *nItems = 0;
3021     cmsFreeNamedColorList(List);
3022     return NULL;
3023 
3024     cmsUNUSED_PARAMETER(SizeOfTag);
3025 }
3026 
3027 
3028 
3029 // Saves a colorant table. It is using the named color structure for simplicity sake
3030 static
3031 cmsBool  Type_ColorantTable_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
3032 {
3033     cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) Ptr;
3034     int i, nColors;
3035 
3036     nColors = cmsNamedColorCount(NamedColorList);
3037 
3038     if (!_cmsWriteUInt32Number(io, nColors)) return FALSE;
3039 
3040     for (i=0; i < nColors; i++) {
3041 
3042         char root[33];
3043         cmsUInt16Number PCS[3];
3044 
3045         if (!cmsNamedColorInfo(NamedColorList, i, root, NULL, NULL, PCS, NULL)) return 0;
3046         root[32] = 0;
3047 
3048         if (!io ->Write(io, 32, root)) return FALSE;
3049         if (!_cmsWriteUInt16Array(io, 3, PCS)) return FALSE;
3050     }
3051 
3052     return TRUE;
3053 
3054     cmsUNUSED_PARAMETER(nItems);
3055     cmsUNUSED_PARAMETER(self);
3056 }
3057 
3058 
3059 static
3060 void* Type_ColorantTable_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n)
3061 {
3062     cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) Ptr;
3063     return (void*) cmsDupNamedColorList(nc);
3064 
3065     cmsUNUSED_PARAMETER(n);
3066     cmsUNUSED_PARAMETER(self);
3067 }
3068 
3069 
3070 static
3071 void Type_ColorantTable_Free(struct _cms_typehandler_struct* self, void* Ptr)
3072 {
3073     cmsFreeNamedColorList((cmsNAMEDCOLORLIST*) Ptr);
3074     return;
3075 
3076     cmsUNUSED_PARAMETER(self);
3077 }
3078 
3079 
3080 // ********************************************************************************
3081 // Type cmsSigNamedColor2Type
3082 // ********************************************************************************
3083 //
3084 //The namedColor2Type is a count value and array of structures that provide color
3085 //coordinates for 7-bit ASCII color names. For each named color, a PCS and optional
3086 //device representation of the color are given. Both representations are 16-bit values.
3087 //The device representation corresponds to the header’s “color space of data” field.
3088 //This representation should be consistent with the “number of device components”
3089 //field in the namedColor2Type. If this field is 0, device coordinates are not provided.
3090 //The PCS representation corresponds to the header’s PCS field. The PCS representation
3091 //is always provided. Color names are fixed-length, 32-byte fields including null
3092 //termination. In order to maintain maximum portability, it is strongly recommended
3093 //that special characters of the 7-bit ASCII set not be used.
3094 
3095 static
3096 void *Type_NamedColor_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
3097 {
3098 
3099     cmsUInt32Number      vendorFlag;     // Bottom 16 bits for ICC use
3100     cmsUInt32Number      count;          // Count of named colors
3101     cmsUInt32Number      nDeviceCoords;  // Num of device coordinates
3102     char                 prefix[32];     // Prefix for each color name
3103     char                 suffix[32];     // Suffix for each color name
3104     cmsNAMEDCOLORLIST*  v;
3105     cmsUInt32Number i;
3106 
3107 
3108     *nItems = 0;
3109     if (!_cmsReadUInt32Number(io, &vendorFlag)) return NULL;
3110     if (!_cmsReadUInt32Number(io, &count)) return NULL;
3111     if (!_cmsReadUInt32Number(io, &nDeviceCoords)) return NULL;
3112 
3113     if (io -> Read(io, prefix, 32, 1) != 1) return NULL;
3114     if (io -> Read(io, suffix, 32, 1) != 1) return NULL;
3115 
3116     prefix[31] = suffix[31] = 0;
3117 
3118     v = cmsAllocNamedColorList(self ->ContextID, count, nDeviceCoords, prefix, suffix);
3119     if (v == NULL) {
3120         cmsSignalError(self->ContextID, cmsERROR_RANGE, "Too many named colors '%d'", count);
3121         return NULL;
3122     }
3123 
3124     if (nDeviceCoords > cmsMAXCHANNELS) {
3125         cmsSignalError(self->ContextID, cmsERROR_RANGE, "Too many device coordinates '%d'", nDeviceCoords);
3126         return 0;
3127     }
3128     for (i=0; i < count; i++) {
3129 
3130         cmsUInt16Number PCS[3];
3131         cmsUInt16Number Colorant[cmsMAXCHANNELS];
3132         char Root[33];
3133 
3134         memset(Colorant, 0, sizeof(Colorant));
3135         if (io -> Read(io, Root, 32, 1) != 1) return NULL;
3136         if (!_cmsReadUInt16Array(io, 3, PCS)) goto Error;
3137         if (!_cmsReadUInt16Array(io, nDeviceCoords, Colorant)) goto Error;
3138 
3139         if (!cmsAppendNamedColor(v, Root, PCS, Colorant)) goto Error;
3140     }
3141 
3142     *nItems = 1;
3143     return (void*) v ;
3144 
3145 Error:
3146     cmsFreeNamedColorList(v);
3147     return NULL;
3148 
3149     cmsUNUSED_PARAMETER(SizeOfTag);
3150 }
3151 
3152 
3153 // Saves a named color list into a named color profile
3154 static
3155 cmsBool Type_NamedColor_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
3156 {
3157     cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) Ptr;
3158     char                prefix[32];     // Prefix for each color name
3159     char                suffix[32];     // Suffix for each color name
3160     int i, nColors;
3161 
3162     nColors = cmsNamedColorCount(NamedColorList);
3163 
3164     if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
3165     if (!_cmsWriteUInt32Number(io, nColors)) return FALSE;
3166     if (!_cmsWriteUInt32Number(io, NamedColorList ->ColorantCount)) return FALSE;
3167 
3168     strncpy(prefix, (const char*) NamedColorList->Prefix, 32);
3169     strncpy(suffix, (const char*) NamedColorList->Suffix, 32);
3170 
3171     suffix[31] = prefix[31] = 0;
3172 
3173     if (!io ->Write(io, 32, prefix)) return FALSE;
3174     if (!io ->Write(io, 32, suffix)) return FALSE;
3175 
3176     for (i=0; i < nColors; i++) {
3177 
3178        cmsUInt16Number PCS[3];
3179        cmsUInt16Number Colorant[cmsMAXCHANNELS];
3180        char Root[33];
3181 
3182         if (!cmsNamedColorInfo(NamedColorList, i, Root, NULL, NULL, PCS, Colorant)) return 0;
3183         if (!io ->Write(io, 32 , Root)) return FALSE;
3184         if (!_cmsWriteUInt16Array(io, 3, PCS)) return FALSE;
3185         if (!_cmsWriteUInt16Array(io, NamedColorList ->ColorantCount, Colorant)) return FALSE;
3186     }
3187 
3188     return TRUE;
3189 
3190     cmsUNUSED_PARAMETER(nItems);
3191     cmsUNUSED_PARAMETER(self);
3192 }
3193 
3194 static
3195 void* Type_NamedColor_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n)
3196 {
3197     cmsNAMEDCOLORLIST* nc = (cmsNAMEDCOLORLIST*) Ptr;
3198 
3199     return (void*) cmsDupNamedColorList(nc);
3200 
3201     cmsUNUSED_PARAMETER(n);
3202     cmsUNUSED_PARAMETER(self);
3203 }
3204 
3205 
3206 static
3207 void Type_NamedColor_Free(struct _cms_typehandler_struct* self, void* Ptr)
3208 {
3209     cmsFreeNamedColorList((cmsNAMEDCOLORLIST*) Ptr);
3210     return;
3211 
3212     cmsUNUSED_PARAMETER(self);
3213 }
3214 
3215 
3216 // ********************************************************************************
3217 // Type cmsSigProfileSequenceDescType
3218 // ********************************************************************************
3219 
3220 // This type is an array of structures, each of which contains information from the
3221 // header fields and tags from the original profiles which were combined to create
3222 // the final profile. The order of the structures is the order in which the profiles
3223 // were combined and includes a structure for the final profile. This provides a
3224 // description of the profile sequence from source to destination,
3225 // typically used with the DeviceLink profile.
3226 
3227 static
3228 cmsBool ReadEmbeddedText(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU** mlu, cmsUInt32Number SizeOfTag)
3229 {
3230     cmsTagTypeSignature  BaseType;
3231     cmsUInt32Number nItems;
3232 
3233     BaseType = _cmsReadTypeBase(io);
3234 
3235     switch (BaseType) {
3236 
3237        case cmsSigTextType:
3238            if (*mlu) cmsMLUfree(*mlu);
3239            *mlu = (cmsMLU*)Type_Text_Read(self, io, &nItems, SizeOfTag);
3240            return (*mlu != NULL);
3241 
3242        case cmsSigTextDescriptionType:
3243            if (*mlu) cmsMLUfree(*mlu);
3244            *mlu =  (cmsMLU*) Type_Text_Description_Read(self, io, &nItems, SizeOfTag);
3245            return (*mlu != NULL);
3246 
3247            /*
3248            TBD: Size is needed for MLU, and we have no idea on which is the available size
3249            */
3250 
3251        case cmsSigMultiLocalizedUnicodeType:
3252            if (*mlu) cmsMLUfree(*mlu);
3253            *mlu =  (cmsMLU*) Type_MLU_Read(self, io, &nItems, SizeOfTag);
3254            return (*mlu != NULL);
3255 
3256        default: return FALSE;
3257     }
3258 }
3259 
3260 
3261 static
3262 void *Type_ProfileSequenceDesc_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
3263 {
3264     cmsSEQ* OutSeq;
3265     cmsUInt32Number i, Count;
3266 
3267     *nItems = 0;
3268 
3269     if (!_cmsReadUInt32Number(io, &Count)) return NULL;
3270 
3271     if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL;
3272     SizeOfTag -= sizeof(cmsUInt32Number);
3273 
3274 
3275     OutSeq = cmsAllocProfileSequenceDescription(self ->ContextID, Count);
3276     if (OutSeq == NULL) return NULL;
3277 
3278     OutSeq ->n = Count;
3279 
3280     // Get structures as well
3281 
3282     for (i=0; i < Count; i++) {
3283 
3284         cmsPSEQDESC* sec = &OutSeq -> seq[i];
3285 
3286         if (!_cmsReadUInt32Number(io, &sec ->deviceMfg)) goto Error;
3287         if (SizeOfTag < sizeof(cmsUInt32Number)) goto Error;
3288         SizeOfTag -= sizeof(cmsUInt32Number);
3289 
3290         if (!_cmsReadUInt32Number(io, &sec ->deviceModel)) goto Error;
3291         if (SizeOfTag < sizeof(cmsUInt32Number)) goto Error;
3292         SizeOfTag -= sizeof(cmsUInt32Number);
3293 
3294         if (!_cmsReadUInt64Number(io, &sec ->attributes)) goto Error;
3295         if (SizeOfTag < sizeof(cmsUInt64Number)) goto Error;
3296         SizeOfTag -= sizeof(cmsUInt64Number);
3297 
3298         if (!_cmsReadUInt32Number(io, (cmsUInt32Number *)&sec ->technology)) goto Error;
3299         if (SizeOfTag < sizeof(cmsUInt32Number)) goto Error;
3300         SizeOfTag -= sizeof(cmsUInt32Number);
3301 
3302         if (!ReadEmbeddedText(self, io, &sec ->Manufacturer, SizeOfTag)) goto Error;
3303         if (!ReadEmbeddedText(self, io, &sec ->Model, SizeOfTag)) goto Error;
3304     }
3305 
3306     *nItems = 1;
3307     return OutSeq;
3308 
3309 Error:
3310     cmsFreeProfileSequenceDescription(OutSeq);
3311     return NULL;
3312 }
3313 
3314 
3315 // Aux--Embed a text description type. It can be of type text description or multilocalized unicode
3316 // and it depends of the version number passed on cmsTagDescriptor structure instead of stack
3317 static
3318 cmsBool  SaveDescription(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* Text)
3319 {
3320     if (self ->ICCVersion < 0x4000000) {
3321 
3322         if (!_cmsWriteTypeBase(io, cmsSigTextDescriptionType)) return FALSE;
3323         return Type_Text_Description_Write(self, io, Text, 1);
3324     }
3325     else {
3326         if (!_cmsWriteTypeBase(io, cmsSigMultiLocalizedUnicodeType)) return FALSE;
3327         return Type_MLU_Write(self, io, Text, 1);
3328     }
3329 }
3330 
3331 
3332 static
3333 cmsBool  Type_ProfileSequenceDesc_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
3334 {
3335     cmsSEQ* Seq = (cmsSEQ*) Ptr;
3336     cmsUInt32Number i;
3337 
3338     if (!_cmsWriteUInt32Number(io, Seq->n)) return FALSE;
3339 
3340     for (i=0; i < Seq ->n; i++) {
3341 
3342         cmsPSEQDESC* sec = &Seq -> seq[i];
3343 
3344         if (!_cmsWriteUInt32Number(io, sec ->deviceMfg)) return FALSE;
3345         if (!_cmsWriteUInt32Number(io, sec ->deviceModel)) return FALSE;
3346         if (!_cmsWriteUInt64Number(io, &sec ->attributes)) return FALSE;
3347         if (!_cmsWriteUInt32Number(io, sec ->technology)) return FALSE;
3348 
3349         if (!SaveDescription(self, io, sec ->Manufacturer)) return FALSE;
3350         if (!SaveDescription(self, io, sec ->Model)) return FALSE;
3351     }
3352 
3353      return TRUE;
3354 
3355      cmsUNUSED_PARAMETER(nItems);
3356 }
3357 
3358 
3359 static
3360 void* Type_ProfileSequenceDesc_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n)
3361 {
3362     return (void*) cmsDupProfileSequenceDescription((cmsSEQ*) Ptr);
3363 
3364     cmsUNUSED_PARAMETER(n);
3365     cmsUNUSED_PARAMETER(self);
3366 }
3367 
3368 static
3369 void Type_ProfileSequenceDesc_Free(struct _cms_typehandler_struct* self, void* Ptr)
3370 {
3371     cmsFreeProfileSequenceDescription((cmsSEQ*) Ptr);
3372     return;
3373 
3374     cmsUNUSED_PARAMETER(self);
3375 }
3376 
3377 
3378 // ********************************************************************************
3379 // Type cmsSigProfileSequenceIdType
3380 // ********************************************************************************
3381 /*
3382 In certain workflows using ICC Device Link Profiles, it is necessary to identify the
3383 original profiles that were combined to create the Device Link Profile.
3384 This type is an array of structures, each of which contains information for
3385 identification of a profile used in a sequence
3386 */
3387 
3388 
3389 static
3390 cmsBool ReadSeqID(struct _cms_typehandler_struct* self,
3391                                              cmsIOHANDLER* io,
3392                                              void* Cargo,
3393                                              cmsUInt32Number n,
3394                                              cmsUInt32Number SizeOfTag)
3395 {
3396     cmsSEQ* OutSeq = (cmsSEQ*) Cargo;
3397     cmsPSEQDESC* seq = &OutSeq ->seq[n];
3398 
3399     if (io -> Read(io, seq ->ProfileID.ID8, 16, 1) != 1) return FALSE;
3400     if (!ReadEmbeddedText(self, io, &seq ->Description, SizeOfTag)) return FALSE;
3401 
3402     return TRUE;
3403 }
3404 
3405 
3406 
3407 static
3408 void *Type_ProfileSequenceId_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
3409 {
3410     cmsSEQ* OutSeq;
3411     cmsUInt32Number Count;
3412     cmsUInt32Number BaseOffset;
3413 
3414     *nItems = 0;
3415 
3416     // Get actual position as a basis for element offsets
3417     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
3418 
3419     // Get table count
3420     if (!_cmsReadUInt32Number(io, &Count)) return NULL;
3421     SizeOfTag -= sizeof(cmsUInt32Number);
3422 
3423     // Allocate an empty structure
3424     OutSeq = cmsAllocProfileSequenceDescription(self ->ContextID, Count);
3425     if (OutSeq == NULL) return NULL;
3426 
3427 
3428     // Read the position table
3429     if (!ReadPositionTable(self, io, Count, BaseOffset, OutSeq, ReadSeqID)) {
3430 
3431         cmsFreeProfileSequenceDescription(OutSeq);
3432         return NULL;
3433     }
3434 
3435     // Success
3436     *nItems = 1;
3437     return OutSeq;
3438 
3439 }
3440 
3441 
3442 static
3443 cmsBool WriteSeqID(struct _cms_typehandler_struct* self,
3444                                              cmsIOHANDLER* io,
3445                                              void* Cargo,
3446                                              cmsUInt32Number n,
3447                                              cmsUInt32Number SizeOfTag)
3448 {
3449     cmsSEQ* Seq = (cmsSEQ*) Cargo;
3450 
3451     if (!io ->Write(io, 16, Seq ->seq[n].ProfileID.ID8)) return FALSE;
3452 
3453     // Store here the MLU
3454     if (!SaveDescription(self, io, Seq ->seq[n].Description)) return FALSE;
3455 
3456     return TRUE;
3457 
3458     cmsUNUSED_PARAMETER(SizeOfTag);
3459 }
3460 
3461 static
3462 cmsBool  Type_ProfileSequenceId_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
3463 {
3464     cmsSEQ* Seq = (cmsSEQ*) Ptr;
3465     cmsUInt32Number BaseOffset;
3466 
3467     // Keep the base offset
3468     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
3469 
3470     // This is the table count
3471     if (!_cmsWriteUInt32Number(io, Seq ->n)) return FALSE;
3472 
3473     // This is the position table and content
3474     if (!WritePositionTable(self, io, 0, Seq ->n, BaseOffset, Seq, WriteSeqID)) return FALSE;
3475 
3476     return TRUE;
3477 
3478     cmsUNUSED_PARAMETER(nItems);
3479 }
3480 
3481 static
3482 void* Type_ProfileSequenceId_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n)
3483 {
3484     return (void*) cmsDupProfileSequenceDescription((cmsSEQ*) Ptr);
3485 
3486     cmsUNUSED_PARAMETER(n);
3487     cmsUNUSED_PARAMETER(self);
3488 }
3489 
3490 static
3491 void Type_ProfileSequenceId_Free(struct _cms_typehandler_struct* self, void* Ptr)
3492 {
3493     cmsFreeProfileSequenceDescription((cmsSEQ*) Ptr);
3494     return;
3495 
3496     cmsUNUSED_PARAMETER(self);
3497 }
3498 
3499 
3500 // ********************************************************************************
3501 // Type cmsSigUcrBgType
3502 // ********************************************************************************
3503 /*
3504 This type contains curves representing the under color removal and black
3505 generation and a text string which is a general description of the method used
3506 for the ucr/bg.
3507 */
3508 
3509 static
3510 void *Type_UcrBg_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
3511 {
3512     cmsUcrBg* n = (cmsUcrBg*) _cmsMallocZero(self ->ContextID, sizeof(cmsUcrBg));
3513     cmsUInt32Number CountUcr, CountBg;
3514     char* ASCIIString;
3515 
3516     *nItems = 0;
3517     if (n == NULL) return NULL;
3518 
3519     // First curve is Under color removal
3520     if (!_cmsReadUInt32Number(io, &CountUcr)) return NULL;
3521     if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL;
3522     SizeOfTag -= sizeof(cmsUInt32Number);
3523 
3524     n ->Ucr = cmsBuildTabulatedToneCurve16(self ->ContextID, CountUcr, NULL);
3525     if (n ->Ucr == NULL) return NULL;
3526 
3527     if (!_cmsReadUInt16Array(io, CountUcr, n ->Ucr->Table16)) return NULL;
3528     if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL;
3529     SizeOfTag -= CountUcr * sizeof(cmsUInt16Number);
3530 
3531     // Second curve is Black generation
3532     if (!_cmsReadUInt32Number(io, &CountBg)) return NULL;
3533     if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL;
3534     SizeOfTag -= sizeof(cmsUInt32Number);
3535 
3536     n ->Bg = cmsBuildTabulatedToneCurve16(self ->ContextID, CountBg, NULL);
3537     if (n ->Bg == NULL) return NULL;
3538     if (!_cmsReadUInt16Array(io, CountBg, n ->Bg->Table16)) return NULL;
3539     if (SizeOfTag < CountBg * sizeof(cmsUInt16Number)) return NULL;
3540     SizeOfTag -= CountBg * sizeof(cmsUInt16Number);
3541     if (SizeOfTag == UINT_MAX) return NULL;
3542 
3543     // Now comes the text. The length is specified by the tag size
3544     n ->Desc = cmsMLUalloc(self ->ContextID, 1);
3545     if (n ->Desc == NULL) return NULL;
3546 
3547     ASCIIString = (char*) _cmsMalloc(self ->ContextID, SizeOfTag + 1);
3548     if (io ->Read(io, ASCIIString, sizeof(char), SizeOfTag) != SizeOfTag) return NULL;
3549     ASCIIString[SizeOfTag] = 0;
3550     cmsMLUsetASCII(n ->Desc, cmsNoLanguage, cmsNoCountry, ASCIIString);
3551     _cmsFree(self ->ContextID, ASCIIString);
3552 
3553     *nItems = 1;
3554     return (void*) n;
3555 }
3556 
3557 static
3558 cmsBool  Type_UcrBg_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
3559 {
3560     cmsUcrBg* Value = (cmsUcrBg*) Ptr;
3561     cmsUInt32Number TextSize;
3562     char* Text;
3563 
3564     // First curve is Under color removal
3565     if (!_cmsWriteUInt32Number(io, Value ->Ucr ->nEntries)) return FALSE;
3566     if (!_cmsWriteUInt16Array(io, Value ->Ucr ->nEntries, Value ->Ucr ->Table16)) return FALSE;
3567 
3568     // Then black generation
3569     if (!_cmsWriteUInt32Number(io, Value ->Bg ->nEntries)) return FALSE;
3570     if (!_cmsWriteUInt16Array(io, Value ->Bg ->nEntries, Value ->Bg ->Table16)) return FALSE;
3571 
3572     // Now comes the text. The length is specified by the tag size
3573     TextSize = cmsMLUgetASCII(Value ->Desc, cmsNoLanguage, cmsNoCountry, NULL, 0);
3574     Text     = (char*) _cmsMalloc(self ->ContextID, TextSize);
3575     if (cmsMLUgetASCII(Value ->Desc, cmsNoLanguage, cmsNoCountry, Text, TextSize) != TextSize) return FALSE;
3576 
3577     if (!io ->Write(io, TextSize, Text)) return FALSE;
3578     _cmsFree(self ->ContextID, Text);
3579 
3580     return TRUE;
3581 
3582     cmsUNUSED_PARAMETER(nItems);
3583 }
3584 
3585 static
3586 void* Type_UcrBg_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
3587 {
3588     cmsUcrBg* Src = (cmsUcrBg*) Ptr;
3589     cmsUcrBg* NewUcrBg = (cmsUcrBg*) _cmsMallocZero(self ->ContextID, sizeof(cmsUcrBg));
3590 
3591     if (NewUcrBg == NULL) return NULL;
3592 
3593     NewUcrBg ->Bg   = cmsDupToneCurve(Src ->Bg);
3594     NewUcrBg ->Ucr  = cmsDupToneCurve(Src ->Ucr);
3595     NewUcrBg ->Desc = cmsMLUdup(Src ->Desc);
3596 
3597     return (void*) NewUcrBg;
3598 
3599     cmsUNUSED_PARAMETER(n);
3600 }
3601 
3602 static
3603 void Type_UcrBg_Free(struct _cms_typehandler_struct* self, void *Ptr)
3604 {
3605    cmsUcrBg* Src = (cmsUcrBg*) Ptr;
3606 
3607    if (Src ->Ucr) cmsFreeToneCurve(Src ->Ucr);
3608    if (Src ->Bg)  cmsFreeToneCurve(Src ->Bg);
3609    if (Src ->Desc) cmsMLUfree(Src ->Desc);
3610 
3611    _cmsFree(self ->ContextID, Ptr);
3612 }
3613 
3614 // ********************************************************************************
3615 // Type cmsSigCrdInfoType
3616 // ********************************************************************************
3617 
3618 /*
3619 This type contains the PostScript product name to which this profile corresponds
3620 and the names of the companion CRDs. Recall that a single profile can generate
3621 multiple CRDs. It is implemented as a MLU being the language code "PS" and then
3622 country varies for each element:
3623 
3624                 nm: PostScript product name
3625                 #0: Rendering intent 0 CRD name
3626                 #1: Rendering intent 1 CRD name
3627                 #2: Rendering intent 2 CRD name
3628                 #3: Rendering intent 3 CRD name
3629 */
3630 
3631 
3632 
3633 // Auxiliar, read an string specified as count + string
3634 static
3635 cmsBool  ReadCountAndSting(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* mlu, cmsUInt32Number* SizeOfTag, const char* Section)
3636 {
3637     cmsUInt32Number Count;
3638     char* Text;
3639 
3640     if (*SizeOfTag < sizeof(cmsUInt32Number)) return FALSE;
3641 
3642     if (!_cmsReadUInt32Number(io, &Count)) return FALSE;
3643 
3644     if (Count > UINT_MAX - sizeof(cmsUInt32Number)) return FALSE;
3645     if (*SizeOfTag < Count + sizeof(cmsUInt32Number)) return FALSE;
3646 
3647     Text     = (char*) _cmsMalloc(self ->ContextID, Count+1);
3648     if (Text == NULL) return FALSE;
3649 
3650     if (io ->Read(io, Text, sizeof(cmsUInt8Number), Count) != Count) {
3651         _cmsFree(self ->ContextID, Text);
3652         return FALSE;
3653     }
3654 
3655     Text[Count] = 0;
3656 
3657     cmsMLUsetASCII(mlu, "PS", Section, Text);
3658     _cmsFree(self ->ContextID, Text);
3659 
3660     *SizeOfTag -= (Count + sizeof(cmsUInt32Number));
3661     return TRUE;
3662 }
3663 
3664 static
3665 cmsBool  WriteCountAndSting(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* mlu, const char* Section)
3666 {
3667  cmsUInt32Number TextSize;
3668  char* Text;
3669 
3670     TextSize = cmsMLUgetASCII(mlu, "PS", Section, NULL, 0);
3671     Text     = (char*) _cmsMalloc(self ->ContextID, TextSize);
3672 
3673     if (!_cmsWriteUInt32Number(io, TextSize)) return FALSE;
3674 
3675     if (cmsMLUgetASCII(mlu, "PS", Section, Text, TextSize) == 0) return FALSE;
3676 
3677     if (!io ->Write(io, TextSize, Text)) return FALSE;
3678     _cmsFree(self ->ContextID, Text);
3679 
3680     return TRUE;
3681 }
3682 
3683 static
3684 void *Type_CrdInfo_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
3685 {
3686     cmsMLU* mlu = cmsMLUalloc(self ->ContextID, 5);
3687 
3688     *nItems = 0;
3689     if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "nm")) goto Error;
3690     if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#0")) goto Error;
3691     if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#1")) goto Error;
3692     if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#2")) goto Error;
3693     if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#3")) goto Error;
3694 
3695     *nItems = 1;
3696     return (void*) mlu;
3697 
3698 Error:
3699     cmsMLUfree(mlu);
3700     return NULL;
3701 
3702 }
3703 
3704 static
3705 cmsBool  Type_CrdInfo_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
3706 {
3707 
3708     cmsMLU* mlu = (cmsMLU*) Ptr;
3709 
3710     if (!WriteCountAndSting(self, io, mlu, "nm")) goto Error;
3711     if (!WriteCountAndSting(self, io, mlu, "#0")) goto Error;
3712     if (!WriteCountAndSting(self, io, mlu, "#1")) goto Error;
3713     if (!WriteCountAndSting(self, io, mlu, "#2")) goto Error;
3714     if (!WriteCountAndSting(self, io, mlu, "#3")) goto Error;
3715 
3716     return TRUE;
3717 
3718 Error:
3719     return FALSE;
3720 
3721     cmsUNUSED_PARAMETER(nItems);
3722 }
3723 
3724 
3725 static
3726 void* Type_CrdInfo_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
3727 {
3728     return (void*) cmsMLUdup((cmsMLU*) Ptr);
3729 
3730     cmsUNUSED_PARAMETER(n);
3731     cmsUNUSED_PARAMETER(self);
3732 }
3733 
3734 static
3735 void Type_CrdInfo_Free(struct _cms_typehandler_struct* self, void *Ptr)
3736 {
3737     cmsMLUfree((cmsMLU*) Ptr);
3738     return;
3739 
3740     cmsUNUSED_PARAMETER(self);
3741 }
3742 
3743 // ********************************************************************************
3744 // Type cmsSigScreeningType
3745 // ********************************************************************************
3746 //
3747 //The screeningType describes various screening parameters including screen
3748 //frequency, screening angle, and spot shape.
3749 
3750 static
3751 void *Type_Screening_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
3752 {
3753     cmsScreening* sc = NULL;
3754     cmsUInt32Number i;
3755 
3756     sc = (cmsScreening*) _cmsMallocZero(self ->ContextID, sizeof(cmsScreening));
3757     if (sc == NULL) return NULL;
3758 
3759     *nItems = 0;
3760 
3761     if (!_cmsReadUInt32Number(io, &sc ->Flag)) goto Error;
3762     if (!_cmsReadUInt32Number(io, &sc ->nChannels)) goto Error;
3763 
3764     if (sc ->nChannels > cmsMAXCHANNELS - 1)
3765         sc ->nChannels = cmsMAXCHANNELS - 1;
3766 
3767     for (i=0; i < sc ->nChannels; i++) {
3768 
3769         if (!_cmsRead15Fixed16Number(io, &sc ->Channels[i].Frequency)) goto Error;
3770         if (!_cmsRead15Fixed16Number(io, &sc ->Channels[i].ScreenAngle)) goto Error;
3771         if (!_cmsReadUInt32Number(io, &sc ->Channels[i].SpotShape)) goto Error;
3772     }
3773 
3774 
3775     *nItems = 1;
3776 
3777     return (void*) sc;
3778 
3779 Error:
3780     if (sc != NULL)
3781         _cmsFree(self ->ContextID, sc);
3782 
3783     return NULL;
3784 
3785     cmsUNUSED_PARAMETER(SizeOfTag);
3786 }
3787 
3788 
3789 static
3790 cmsBool Type_Screening_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
3791 {
3792     cmsScreening* sc = (cmsScreening* ) Ptr;
3793     cmsUInt32Number i;
3794 
3795     if (!_cmsWriteUInt32Number(io, sc ->Flag)) return FALSE;
3796     if (!_cmsWriteUInt32Number(io, sc ->nChannels)) return FALSE;
3797 
3798     for (i=0; i < sc ->nChannels; i++) {
3799 
3800         if (!_cmsWrite15Fixed16Number(io, sc ->Channels[i].Frequency)) return FALSE;
3801         if (!_cmsWrite15Fixed16Number(io, sc ->Channels[i].ScreenAngle)) return FALSE;
3802         if (!_cmsWriteUInt32Number(io, sc ->Channels[i].SpotShape)) return FALSE;
3803     }
3804 
3805     return TRUE;
3806 
3807     cmsUNUSED_PARAMETER(nItems);
3808     cmsUNUSED_PARAMETER(self);
3809 }
3810 
3811 
3812 static
3813 void* Type_Screening_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
3814 {
3815    return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsScreening));
3816 
3817    cmsUNUSED_PARAMETER(n);
3818 }
3819 
3820 
3821 static
3822 void Type_Screening_Free(struct _cms_typehandler_struct* self, void* Ptr)
3823 {
3824    _cmsFree(self ->ContextID, Ptr);
3825 }
3826 
3827 // ********************************************************************************
3828 // Type cmsSigViewingConditionsType
3829 // ********************************************************************************
3830 //
3831 //This type represents a set of viewing condition parameters including:
3832 //CIE ’absolute’ illuminant white point tristimulus values and CIE ’absolute’
3833 //surround tristimulus values.
3834 
3835 static
3836 void *Type_ViewingConditions_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
3837 {
3838     cmsICCViewingConditions* vc = NULL;
3839 
3840     vc = (cmsICCViewingConditions*) _cmsMallocZero(self ->ContextID, sizeof(cmsICCViewingConditions));
3841     if (vc == NULL) return NULL;
3842 
3843     *nItems = 0;
3844 
3845     if (!_cmsReadXYZNumber(io, &vc ->IlluminantXYZ)) goto Error;
3846     if (!_cmsReadXYZNumber(io, &vc ->SurroundXYZ)) goto Error;
3847     if (!_cmsReadUInt32Number(io, &vc ->IlluminantType)) goto Error;
3848 
3849     *nItems = 1;
3850 
3851     return (void*) vc;
3852 
3853 Error:
3854     if (vc != NULL)
3855         _cmsFree(self ->ContextID, vc);
3856 
3857     return NULL;
3858 
3859     cmsUNUSED_PARAMETER(SizeOfTag);
3860 }
3861 
3862 
3863 static
3864 cmsBool Type_ViewingConditions_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
3865 {
3866     cmsICCViewingConditions* sc = (cmsICCViewingConditions* ) Ptr;
3867 
3868     if (!_cmsWriteXYZNumber(io, &sc ->IlluminantXYZ)) return FALSE;
3869     if (!_cmsWriteXYZNumber(io, &sc ->SurroundXYZ)) return FALSE;
3870     if (!_cmsWriteUInt32Number(io, sc ->IlluminantType)) return FALSE;
3871 
3872     return TRUE;
3873 
3874     cmsUNUSED_PARAMETER(nItems);
3875     cmsUNUSED_PARAMETER(self);
3876 }
3877 
3878 
3879 static
3880 void* Type_ViewingConditions_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
3881 {
3882    return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsScreening));
3883 
3884    cmsUNUSED_PARAMETER(n);
3885 }
3886 
3887 
3888 static
3889 void Type_ViewingConditions_Free(struct _cms_typehandler_struct* self, void* Ptr)
3890 {
3891    _cmsFree(self ->ContextID, Ptr);
3892 }
3893 
3894 
3895 // ********************************************************************************
3896 // Type cmsSigMultiProcessElementType
3897 // ********************************************************************************
3898 
3899 
3900 static
3901 void* GenericMPEdup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
3902 {
3903     return (void*) cmsStageDup((cmsStage*) Ptr);
3904 
3905     cmsUNUSED_PARAMETER(n);
3906     cmsUNUSED_PARAMETER(self);
3907 }
3908 
3909 static
3910 void GenericMPEfree(struct _cms_typehandler_struct* self, void *Ptr)
3911 {
3912     cmsStageFree((cmsStage*) Ptr);
3913     return;
3914 
3915     cmsUNUSED_PARAMETER(self);
3916 }
3917 
3918 // Each curve is stored in one or more curve segments, with break-points specified between curve segments.
3919 // The first curve segment always starts at –Infinity, and the last curve segment always ends at +Infinity. The
3920 // first and last curve segments shall be specified in terms of a formula, whereas the other segments shall be
3921 // specified either in terms of a formula, or by a sampled curve.
3922 
3923 
3924 // Read an embedded segmented curve
3925 static
3926 cmsToneCurve* ReadSegmentedCurve(struct _cms_typehandler_struct* self, cmsIOHANDLER* io)
3927 {
3928     cmsCurveSegSignature ElementSig;
3929     cmsUInt32Number i, j;
3930     cmsUInt16Number nSegments;
3931     cmsCurveSegment*  Segments;
3932     cmsToneCurve* Curve;
3933     cmsFloat32Number PrevBreak = -1E22F;    // - infinite
3934 
3935     // Take signature and channels for each element.
3936      if (!_cmsReadUInt32Number(io, (cmsUInt32Number*) &ElementSig)) return NULL;
3937 
3938      // That should be a segmented curve
3939      if (ElementSig != cmsSigSegmentedCurve) return NULL;
3940 
3941      if (!_cmsReadUInt32Number(io, NULL)) return NULL;
3942      if (!_cmsReadUInt16Number(io, &nSegments)) return NULL;
3943      if (!_cmsReadUInt16Number(io, NULL)) return NULL;
3944 
3945      if (nSegments < 1) return NULL;
3946      Segments = (cmsCurveSegment*) _cmsCalloc(self ->ContextID, nSegments, sizeof(cmsCurveSegment));
3947      if (Segments == NULL) return NULL;
3948 
3949      // Read breakpoints
3950      for (i=0; i < (cmsUInt32Number) nSegments - 1; i++) {
3951 
3952          Segments[i].x0 = PrevBreak;
3953          if (!_cmsReadFloat32Number(io, &Segments[i].x1)) goto Error;
3954          PrevBreak = Segments[i].x1;
3955      }
3956 
3957      Segments[nSegments-1].x0 = PrevBreak;
3958      Segments[nSegments-1].x1 = 1E22F;     // A big cmsFloat32Number number
3959 
3960      // Read segments
3961      for (i=0; i < nSegments; i++) {
3962 
3963           if (!_cmsReadUInt32Number(io, (cmsUInt32Number*) &ElementSig)) goto Error;
3964           if (!_cmsReadUInt32Number(io, NULL)) goto Error;
3965 
3966            switch (ElementSig) {
3967 
3968             case cmsSigFormulaCurveSeg: {
3969 
3970                 cmsUInt16Number Type;
3971                 cmsUInt32Number ParamsByType[] = {4, 5, 5 };
3972 
3973                 if (!_cmsReadUInt16Number(io, &Type)) goto Error;
3974                 if (!_cmsReadUInt16Number(io, NULL)) goto Error;
3975 
3976                 Segments[i].Type = Type + 6;
3977                 if (Type > 2) goto Error;
3978 
3979                 for (j=0; j < ParamsByType[Type]; j++) {
3980 
3981                     cmsFloat32Number f;
3982                     if (!_cmsReadFloat32Number(io, &f)) goto Error;
3983                     Segments[i].Params[j] = f;
3984                 }
3985                 }
3986                 break;
3987 
3988 
3989             case cmsSigSampledCurveSeg: {
3990                 cmsUInt32Number Count;
3991 
3992                 if (!_cmsReadUInt32Number(io, &Count)) return NULL;
3993 
3994                 Segments[i].nGridPoints = Count;
3995                 Segments[i].SampledPoints = (cmsFloat32Number*) _cmsCalloc(self ->ContextID, Count, sizeof(cmsFloat32Number));
3996                 if (Segments[i].SampledPoints == NULL) goto Error;
3997 
3998                 for (j=0; j < Count; j++) {
3999                     if (!_cmsReadFloat32Number(io, &Segments[i].SampledPoints[j])) goto Error;
4000                 }
4001                 }
4002                 break;
4003 
4004             default:
4005                 {
4006                 char String[5];
4007 
4008                 _cmsTagSignature2String(String, (cmsTagSignature) ElementSig);
4009                 cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown curve element type '%s' found.", String);
4010                 }
4011                 return NULL;
4012 
4013          }
4014      }
4015 
4016      Curve = cmsBuildSegmentedToneCurve(self ->ContextID, nSegments, Segments);
4017 
4018      for (i=0; i < nSegments; i++) {
4019          if (Segments[i].SampledPoints) _cmsFree(self ->ContextID, Segments[i].SampledPoints);
4020      }
4021      _cmsFree(self ->ContextID, Segments);
4022      return Curve;
4023 
4024 Error:
4025      if (Segments) _cmsFree(self ->ContextID, Segments);
4026      return NULL;
4027 }
4028 
4029 
4030 static
4031 cmsBool ReadMPECurve(struct _cms_typehandler_struct* self,
4032                      cmsIOHANDLER* io,
4033                      void* Cargo,
4034                      cmsUInt32Number n,
4035                      cmsUInt32Number SizeOfTag)
4036 {
4037       cmsToneCurve** GammaTables = ( cmsToneCurve**) Cargo;
4038 
4039       GammaTables[n] = ReadSegmentedCurve(self, io);
4040       return (GammaTables[n] != NULL);
4041 
4042       cmsUNUSED_PARAMETER(SizeOfTag);
4043 }
4044 
4045 static
4046 void *Type_MPEcurve_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
4047 {
4048     cmsStage* mpe = NULL;
4049     cmsUInt16Number InputChans, OutputChans;
4050     cmsUInt32Number i, BaseOffset;
4051     cmsToneCurve** GammaTables;
4052 
4053     *nItems = 0;
4054 
4055     // Get actual position as a basis for element offsets
4056     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
4057 
4058     if (!_cmsReadUInt16Number(io, &InputChans)) return NULL;
4059     if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL;
4060 
4061     if (InputChans != OutputChans) return NULL;
4062 
4063     GammaTables = (cmsToneCurve**) _cmsCalloc(self ->ContextID, InputChans, sizeof(cmsToneCurve*));
4064     if (GammaTables == NULL) return NULL;
4065 
4066     if (ReadPositionTable(self, io, InputChans, BaseOffset, GammaTables, ReadMPECurve)) {
4067 
4068         mpe = cmsStageAllocToneCurves(self ->ContextID, InputChans, GammaTables);
4069     }
4070     else {
4071         mpe = NULL;
4072     }
4073 
4074     for (i=0; i < InputChans; i++) {
4075         if (GammaTables[i]) cmsFreeToneCurve(GammaTables[i]);
4076     }
4077 
4078     _cmsFree(self ->ContextID, GammaTables);
4079     *nItems = (mpe != NULL) ? 1 : 0;
4080     return mpe;
4081 
4082     cmsUNUSED_PARAMETER(SizeOfTag);
4083 }
4084 
4085 
4086 // Write a single segmented curve. NO CHECK IS PERFORMED ON VALIDITY
4087 static
4088 cmsBool WriteSegmentedCurve(cmsIOHANDLER* io, cmsToneCurve* g)
4089 {
4090     cmsUInt32Number i, j;
4091     cmsCurveSegment* Segments = g ->Segments;
4092     cmsUInt32Number nSegments = g ->nSegments;
4093 
4094     if (!_cmsWriteUInt32Number(io, cmsSigSegmentedCurve)) goto Error;
4095     if (!_cmsWriteUInt32Number(io, 0)) goto Error;
4096     if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) nSegments)) goto Error;
4097     if (!_cmsWriteUInt16Number(io, 0)) goto Error;
4098 
4099     // Write the break-points
4100     for (i=0; i < nSegments - 1; i++) {
4101         if (!_cmsWriteFloat32Number(io, Segments[i].x1)) goto Error;
4102     }
4103 
4104     // Write the segments
4105     for (i=0; i < g ->nSegments; i++) {
4106 
4107         cmsCurveSegment* ActualSeg = Segments + i;
4108 
4109         if (ActualSeg -> Type == 0) {
4110 
4111             // This is a sampled curve
4112             if (!_cmsWriteUInt32Number(io, (cmsUInt32Number) cmsSigSampledCurveSeg)) goto Error;
4113             if (!_cmsWriteUInt32Number(io, 0)) goto Error;
4114             if (!_cmsWriteUInt32Number(io, ActualSeg -> nGridPoints)) goto Error;
4115 
4116             for (j=0; j < g ->Segments[i].nGridPoints; j++) {
4117                 if (!_cmsWriteFloat32Number(io, ActualSeg -> SampledPoints[j])) goto Error;
4118             }
4119 
4120         }
4121         else {
4122             int Type;
4123             cmsUInt32Number ParamsByType[] = { 4, 5, 5 };
4124 
4125             // This is a formula-based
4126             if (!_cmsWriteUInt32Number(io, (cmsUInt32Number) cmsSigFormulaCurveSeg)) goto Error;
4127             if (!_cmsWriteUInt32Number(io, 0)) goto Error;
4128 
4129             // We only allow 1, 2 and 3 as types
4130             Type = ActualSeg ->Type - 6;
4131             if (Type > 2 || Type < 0) goto Error;
4132 
4133             if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) Type)) goto Error;
4134             if (!_cmsWriteUInt16Number(io, 0)) goto Error;
4135 
4136             for (j=0; j < ParamsByType[Type]; j++) {
4137                 if (!_cmsWriteFloat32Number(io, (cmsFloat32Number) ActualSeg ->Params[j])) goto Error;
4138             }
4139         }
4140 
4141         // It seems there is no need to align. Code is here, and for safety commented out
4142         // if (!_cmsWriteAlignment(io)) goto Error;
4143     }
4144 
4145     return TRUE;
4146 
4147 Error:
4148     return FALSE;
4149 }
4150 
4151 
4152 static
4153 cmsBool WriteMPECurve(struct _cms_typehandler_struct* self,
4154                       cmsIOHANDLER* io,
4155                       void* Cargo,
4156                       cmsUInt32Number n,
4157                       cmsUInt32Number SizeOfTag)
4158 {
4159     _cmsStageToneCurvesData* Curves  = (_cmsStageToneCurvesData*) Cargo;
4160 
4161     return WriteSegmentedCurve(io, Curves ->TheCurves[n]);
4162 
4163     cmsUNUSED_PARAMETER(SizeOfTag);
4164     cmsUNUSED_PARAMETER(self);
4165 }
4166 
4167 // Write a curve, checking first for validity
4168 static
4169 cmsBool  Type_MPEcurve_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
4170 {
4171     cmsUInt32Number BaseOffset;
4172     cmsStage* mpe = (cmsStage*) Ptr;
4173     _cmsStageToneCurvesData* Curves = (_cmsStageToneCurvesData*) mpe ->Data;
4174 
4175     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
4176 
4177     // Write the header. Since those are curves, input and output channels are same
4178     if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->InputChannels)) return FALSE;
4179     if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->InputChannels)) return FALSE;
4180 
4181     if (!WritePositionTable(self, io, 0,
4182                                 mpe ->InputChannels, BaseOffset, Curves, WriteMPECurve)) return FALSE;
4183 
4184 
4185     return TRUE;
4186 
4187     cmsUNUSED_PARAMETER(nItems);
4188 }
4189 
4190 
4191 
4192 // The matrix is organized as an array of PxQ+Q elements, where P is the number of input channels to the
4193 // matrix, and Q is the number of output channels. The matrix elements are each float32Numbers. The array
4194 // is organized as follows:
4195 // array = [e11, e12, …, e1P, e21, e22, …, e2P, …, eQ1, eQ2, …, eQP, e1, e2, …, eQ]
4196 
4197 static
4198 void *Type_MPEmatrix_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
4199 {
4200     cmsStage* mpe;
4201     cmsUInt16Number   InputChans, OutputChans;
4202     cmsUInt32Number   nElems, i;
4203     cmsFloat64Number* Matrix;
4204     cmsFloat64Number* Offsets;
4205 
4206     if (!_cmsReadUInt16Number(io, &InputChans)) return NULL;
4207     if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL;
4208 
4209 
4210     nElems = InputChans * OutputChans;
4211 
4212     // Input and output chans may be ANY (up to 0xffff)
4213     Matrix = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, nElems, sizeof(cmsFloat64Number));
4214     if (Matrix == NULL) return NULL;
4215 
4216     Offsets = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, OutputChans, sizeof(cmsFloat64Number));
4217     if (Offsets == NULL) {
4218 
4219         _cmsFree(self ->ContextID, Matrix);
4220         return NULL;
4221     }
4222 
4223     for (i=0; i < nElems; i++) {
4224 
4225         cmsFloat32Number v;
4226 
4227         if (!_cmsReadFloat32Number(io, &v)) return NULL;
4228         Matrix[i] = v;
4229     }
4230 
4231 
4232     for (i=0; i < OutputChans; i++) {
4233 
4234         cmsFloat32Number v;
4235 
4236         if (!_cmsReadFloat32Number(io, &v)) return NULL;
4237         Offsets[i] = v;
4238     }
4239 
4240 
4241     mpe = cmsStageAllocMatrix(self ->ContextID, OutputChans, InputChans, Matrix, Offsets);
4242     _cmsFree(self ->ContextID, Matrix);
4243     _cmsFree(self ->ContextID, Offsets);
4244 
4245     *nItems = 1;
4246 
4247     return mpe;
4248 
4249     cmsUNUSED_PARAMETER(SizeOfTag);
4250 }
4251 
4252 static
4253 cmsBool  Type_MPEmatrix_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
4254 {
4255     cmsUInt32Number i, nElems;
4256     cmsStage* mpe = (cmsStage*) Ptr;
4257     _cmsStageMatrixData* Matrix = (_cmsStageMatrixData*) mpe ->Data;
4258 
4259     if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->InputChannels)) return FALSE;
4260     if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->OutputChannels)) return FALSE;
4261 
4262     nElems = mpe ->InputChannels * mpe ->OutputChannels;
4263 
4264     for (i=0; i < nElems; i++) {
4265         if (!_cmsWriteFloat32Number(io, (cmsFloat32Number) Matrix->Double[i])) return FALSE;
4266     }
4267 
4268 
4269     for (i=0; i < mpe ->OutputChannels; i++) {
4270 
4271         if (Matrix ->Offset == NULL) {
4272 
4273                if (!_cmsWriteFloat32Number(io, 0)) return FALSE;
4274         }
4275         else {
4276                if (!_cmsWriteFloat32Number(io, (cmsFloat32Number) Matrix->Offset[i])) return FALSE;
4277         }
4278     }
4279 
4280     return TRUE;
4281 
4282     cmsUNUSED_PARAMETER(nItems);
4283     cmsUNUSED_PARAMETER(self);
4284 }
4285 
4286 
4287 
4288 static
4289 void *Type_MPEclut_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
4290 {
4291     cmsStage* mpe = NULL;
4292     cmsUInt16Number InputChans, OutputChans;
4293     cmsUInt8Number Dimensions8[16];
4294     cmsUInt32Number i, nMaxGrids, GridPoints[MAX_INPUT_DIMENSIONS];
4295     _cmsStageCLutData* clut;
4296 
4297     if (!_cmsReadUInt16Number(io, &InputChans)) return NULL;
4298     if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL;
4299 
4300     if (InputChans == 0) goto Error;
4301     if (OutputChans == 0) goto Error;
4302 
4303     if (io ->Read(io, Dimensions8, sizeof(cmsUInt8Number), 16) != 16)
4304         goto Error;
4305 
4306     // Copy MAX_INPUT_DIMENSIONS at most. Expand to cmsUInt32Number
4307     nMaxGrids = InputChans > MAX_INPUT_DIMENSIONS ? MAX_INPUT_DIMENSIONS : InputChans;
4308     for (i=0; i < nMaxGrids; i++) GridPoints[i] = (cmsUInt32Number) Dimensions8[i];
4309 
4310     // Allocate the true CLUT
4311     mpe = cmsStageAllocCLutFloatGranular(self ->ContextID, GridPoints, InputChans, OutputChans, NULL);
4312     if (mpe == NULL) goto Error;
4313 
4314     // Read the data
4315     clut = (_cmsStageCLutData*) mpe ->Data;
4316     for (i=0; i < clut ->nEntries; i++) {
4317 
4318         if (!_cmsReadFloat32Number(io, &clut ->Tab.TFloat[i])) goto Error;
4319     }
4320 
4321     *nItems = 1;
4322     return mpe;
4323 
4324 Error:
4325     *nItems = 0;
4326     if (mpe != NULL) cmsStageFree(mpe);
4327     return NULL;
4328 
4329     cmsUNUSED_PARAMETER(SizeOfTag);
4330 }
4331 
4332 // Write a CLUT in floating point
4333 static
4334 cmsBool  Type_MPEclut_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
4335 {
4336     cmsUInt8Number Dimensions8[16];
4337     cmsUInt32Number i;
4338     cmsStage* mpe = (cmsStage*) Ptr;
4339     _cmsStageCLutData* clut = (_cmsStageCLutData*) mpe ->Data;
4340 
4341     // Check for maximum number of channels
4342     if (mpe -> InputChannels > 15) return FALSE;
4343 
4344     // Only floats are supported in MPE
4345     if (clut ->HasFloatValues == FALSE) return FALSE;
4346 
4347     if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->InputChannels)) return FALSE;
4348     if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) mpe ->OutputChannels)) return FALSE;
4349 
4350     memset(Dimensions8, 0, sizeof(Dimensions8));
4351 
4352     for (i=0; i < mpe ->InputChannels; i++)
4353         Dimensions8[i] = (cmsUInt8Number) clut ->Params ->nSamples[i];
4354 
4355     if (!io ->Write(io, 16, Dimensions8)) return FALSE;
4356 
4357     for (i=0; i < clut ->nEntries; i++) {
4358 
4359         if (!_cmsWriteFloat32Number(io, clut ->Tab.TFloat[i])) return FALSE;
4360     }
4361 
4362     return TRUE;
4363 
4364     cmsUNUSED_PARAMETER(nItems);
4365     cmsUNUSED_PARAMETER(self);
4366 }
4367 
4368 
4369 
4370 // This is the list of built-in MPE types
4371 static _cmsTagTypeLinkedList SupportedMPEtypes[] = {
4372 
4373 {{ (cmsTagTypeSignature) cmsSigBAcsElemType, NULL, NULL, NULL, NULL, NULL, 0 }, &SupportedMPEtypes[1] },   // Ignore those elements for now
4374 {{ (cmsTagTypeSignature) cmsSigEAcsElemType, NULL, NULL, NULL, NULL, NULL, 0 }, &SupportedMPEtypes[2] },   // (That's what the spec says)
4375 
4376 {TYPE_MPE_HANDLER((cmsTagTypeSignature) cmsSigCurveSetElemType,     MPEcurve),      &SupportedMPEtypes[3] },
4377 {TYPE_MPE_HANDLER((cmsTagTypeSignature) cmsSigMatrixElemType,       MPEmatrix),     &SupportedMPEtypes[4] },
4378 {TYPE_MPE_HANDLER((cmsTagTypeSignature) cmsSigCLutElemType,         MPEclut),        NULL },
4379 };
4380 
4381 _cmsTagTypePluginChunkType _cmsMPETypePluginChunk = { NULL };
4382 
4383 static
4384 cmsBool ReadMPEElem(struct _cms_typehandler_struct* self,
4385                     cmsIOHANDLER* io,
4386                     void* Cargo,
4387                     cmsUInt32Number n,
4388                     cmsUInt32Number SizeOfTag)
4389 {
4390     cmsStageSignature ElementSig;
4391     cmsTagTypeHandler* TypeHandler;
4392     cmsUInt32Number nItems;
4393     cmsPipeline *NewLUT = (cmsPipeline *) Cargo;
4394     _cmsTagTypePluginChunkType* MPETypePluginChunk  = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(self->ContextID, MPEPlugin);
4395 
4396 
4397     // Take signature and channels for each element.
4398     if (!_cmsReadUInt32Number(io, (cmsUInt32Number*) &ElementSig)) return FALSE;
4399 
4400     // The reserved placeholder
4401     if (!_cmsReadUInt32Number(io, NULL)) return FALSE;
4402 
4403     // Read diverse MPE types
4404     TypeHandler = GetHandler((cmsTagTypeSignature) ElementSig, MPETypePluginChunk ->TagTypes, SupportedMPEtypes);
4405     if (TypeHandler == NULL)  {
4406 
4407         char String[5];
4408 
4409         _cmsTagSignature2String(String, (cmsTagSignature) ElementSig);
4410 
4411         // An unknown element was found.
4412         cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown MPE type '%s' found.", String);
4413         return FALSE;
4414     }
4415 
4416     // If no read method, just ignore the element (valid for cmsSigBAcsElemType and cmsSigEAcsElemType)
4417     // Read the MPE. No size is given
4418     if (TypeHandler ->ReadPtr != NULL) {
4419 
4420         // This is a real element which should be read and processed
4421         if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, (cmsStage*) TypeHandler ->ReadPtr(self, io, &nItems, SizeOfTag)))
4422             return FALSE;
4423     }
4424 
4425     return TRUE;
4426 
4427     cmsUNUSED_PARAMETER(SizeOfTag);
4428     cmsUNUSED_PARAMETER(n);
4429 }
4430 
4431 
4432 // This is the main dispatcher for MPE
4433 static
4434 void *Type_MPE_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
4435 {
4436     cmsUInt16Number InputChans, OutputChans;
4437     cmsUInt32Number ElementCount;
4438     cmsPipeline *NewLUT = NULL;
4439     cmsUInt32Number BaseOffset;
4440 
4441     // Get actual position as a basis for element offsets
4442     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
4443 
4444     // Read channels and element count
4445     if (!_cmsReadUInt16Number(io, &InputChans)) return NULL;
4446     if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL;
4447 
4448     // Allocates an empty LUT
4449     NewLUT = cmsPipelineAlloc(self ->ContextID, InputChans, OutputChans);
4450     if (NewLUT == NULL) return NULL;
4451 
4452     if (!_cmsReadUInt32Number(io, &ElementCount)) return NULL;
4453 
4454     if (!ReadPositionTable(self, io, ElementCount, BaseOffset, NewLUT, ReadMPEElem)) {
4455         if (NewLUT != NULL) cmsPipelineFree(NewLUT);
4456         *nItems = 0;
4457         return NULL;
4458     }
4459 
4460     // Success
4461     *nItems = 1;
4462     return NewLUT;
4463 
4464     cmsUNUSED_PARAMETER(SizeOfTag);
4465 }
4466 
4467 
4468 
4469 // This one is a liitle bit more complex, so we don't use position tables this time.
4470 static
4471 cmsBool Type_MPE_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
4472 {
4473     cmsUInt32Number i, BaseOffset, DirectoryPos, CurrentPos;
4474     int inputChan, outputChan;
4475     cmsUInt32Number ElemCount;
4476     cmsUInt32Number *ElementOffsets = NULL, *ElementSizes = NULL, Before;
4477     cmsStageSignature ElementSig;
4478     cmsPipeline* Lut = (cmsPipeline*) Ptr;
4479     cmsStage* Elem = Lut ->Elements;
4480     cmsTagTypeHandler* TypeHandler;
4481     _cmsTagTypePluginChunkType* MPETypePluginChunk  = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(self->ContextID, MPEPlugin);
4482 
4483     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
4484 
4485     inputChan  = cmsPipelineInputChannels(Lut);
4486     outputChan = cmsPipelineOutputChannels(Lut);
4487     ElemCount  = cmsPipelineStageCount(Lut);
4488 
4489     ElementOffsets = (cmsUInt32Number *) _cmsCalloc(self ->ContextID, ElemCount, sizeof(cmsUInt32Number));
4490     if (ElementOffsets == NULL) goto Error;
4491 
4492     ElementSizes = (cmsUInt32Number *) _cmsCalloc(self ->ContextID, ElemCount, sizeof(cmsUInt32Number));
4493     if (ElementSizes == NULL) goto Error;
4494 
4495     // Write the head
4496     if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) inputChan)) goto Error;
4497     if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) outputChan)) goto Error;
4498     if (!_cmsWriteUInt32Number(io, (cmsUInt16Number) ElemCount)) goto Error;
4499 
4500     DirectoryPos = io ->Tell(io);
4501 
4502     // Write a fake directory to be filled latter on
4503     for (i=0; i < ElemCount; i++) {
4504         if (!_cmsWriteUInt32Number(io, 0)) goto Error;  // Offset
4505         if (!_cmsWriteUInt32Number(io, 0)) goto Error;  // size
4506     }
4507 
4508     // Write each single tag. Keep track of the size as well.
4509     for (i=0; i < ElemCount; i++) {
4510 
4511         ElementOffsets[i] = io ->Tell(io) - BaseOffset;
4512 
4513         ElementSig = Elem ->Type;
4514 
4515         TypeHandler = GetHandler((cmsTagTypeSignature) ElementSig, MPETypePluginChunk->TagTypes, SupportedMPEtypes);
4516         if (TypeHandler == NULL)  {
4517 
4518                 char String[5];
4519 
4520                 _cmsTagSignature2String(String, (cmsTagSignature) ElementSig);
4521 
4522                  // An unknow element was found.
4523                  cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Found unknown MPE type '%s'", String);
4524                  goto Error;
4525         }
4526 
4527         if (!_cmsWriteUInt32Number(io, ElementSig)) goto Error;
4528         if (!_cmsWriteUInt32Number(io, 0)) goto Error;
4529         Before = io ->Tell(io);
4530         if (!TypeHandler ->WritePtr(self, io, Elem, 1)) goto Error;
4531         if (!_cmsWriteAlignment(io)) goto Error;
4532 
4533         ElementSizes[i] = io ->Tell(io) - Before;
4534 
4535         Elem = Elem ->Next;
4536     }
4537 
4538     // Write the directory
4539     CurrentPos = io ->Tell(io);
4540 
4541     if (!io ->Seek(io, DirectoryPos)) goto Error;
4542 
4543     for (i=0; i < ElemCount; i++) {
4544         if (!_cmsWriteUInt32Number(io, ElementOffsets[i])) goto Error;
4545         if (!_cmsWriteUInt32Number(io, ElementSizes[i])) goto Error;
4546     }
4547 
4548     if (!io ->Seek(io, CurrentPos)) goto Error;
4549 
4550     if (ElementOffsets != NULL) _cmsFree(self ->ContextID, ElementOffsets);
4551     if (ElementSizes != NULL) _cmsFree(self ->ContextID, ElementSizes);
4552     return TRUE;
4553 
4554 Error:
4555     if (ElementOffsets != NULL) _cmsFree(self ->ContextID, ElementOffsets);
4556     if (ElementSizes != NULL) _cmsFree(self ->ContextID, ElementSizes);
4557     return FALSE;
4558 
4559     cmsUNUSED_PARAMETER(nItems);
4560 }
4561 
4562 
4563 static
4564 void* Type_MPE_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
4565 {
4566     return (void*) cmsPipelineDup((cmsPipeline*) Ptr);
4567 
4568     cmsUNUSED_PARAMETER(n);
4569     cmsUNUSED_PARAMETER(self);
4570 }
4571 
4572 static
4573 void Type_MPE_Free(struct _cms_typehandler_struct* self, void *Ptr)
4574 {
4575     cmsPipelineFree((cmsPipeline*) Ptr);
4576     return;
4577 
4578     cmsUNUSED_PARAMETER(self);
4579 }
4580 
4581 
4582 // ********************************************************************************
4583 // Type cmsSigVcgtType
4584 // ********************************************************************************
4585 
4586 
4587 #define cmsVideoCardGammaTableType    0
4588 #define cmsVideoCardGammaFormulaType  1
4589 
4590 // Used internally
4591 typedef struct {
4592     double Gamma;
4593     double Min;
4594     double Max;
4595 } _cmsVCGTGAMMA;
4596 
4597 
4598 static
4599 void *Type_vcgt_Read(struct _cms_typehandler_struct* self,
4600                      cmsIOHANDLER* io,
4601                      cmsUInt32Number* nItems,
4602                      cmsUInt32Number SizeOfTag)
4603 {
4604     cmsUInt32Number TagType, n, i;
4605     cmsToneCurve** Curves;
4606 
4607     *nItems = 0;
4608 
4609     // Read tag type
4610     if (!_cmsReadUInt32Number(io, &TagType)) return NULL;
4611 
4612     // Allocate space for the array
4613     Curves = ( cmsToneCurve**) _cmsCalloc(self ->ContextID, 3, sizeof(cmsToneCurve*));
4614     if (Curves == NULL) return NULL;
4615 
4616     // There are two possible flavors
4617     switch (TagType) {
4618 
4619     // Gamma is stored as a table
4620     case cmsVideoCardGammaTableType:
4621     {
4622        cmsUInt16Number nChannels, nElems, nBytes;
4623 
4624        // Check channel count, which should be 3 (we don't support monochrome this time)
4625        if (!_cmsReadUInt16Number(io, &nChannels)) goto Error;
4626 
4627        if (nChannels != 3) {
4628            cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported number of channels for VCGT '%d'", nChannels);
4629            goto Error;
4630        }
4631 
4632        // Get Table element count and bytes per element
4633        if (!_cmsReadUInt16Number(io, &nElems)) goto Error;
4634        if (!_cmsReadUInt16Number(io, &nBytes)) goto Error;
4635 
4636        // Adobe's quirk fixup. Fixing broken profiles...
4637        if (nElems == 256 && nBytes == 1 && SizeOfTag == 1576)
4638            nBytes = 2;
4639 
4640 
4641        // Populate tone curves
4642        for (n=0; n < 3; n++) {
4643 
4644            Curves[n] = cmsBuildTabulatedToneCurve16(self ->ContextID, nElems, NULL);
4645            if (Curves[n] == NULL) goto Error;
4646 
4647            // On depending on byte depth
4648            switch (nBytes) {
4649 
4650            // One byte, 0..255
4651            case 1:
4652                for (i=0; i < nElems; i++) {
4653 
4654                    cmsUInt8Number v;
4655 
4656                       if (!_cmsReadUInt8Number(io, &v)) goto Error;
4657                       Curves[n] ->Table16[i] = FROM_8_TO_16(v);
4658                }
4659                break;
4660 
4661            // One word 0..65535
4662            case 2:
4663               if (!_cmsReadUInt16Array(io, nElems, Curves[n]->Table16)) goto Error;
4664               break;
4665 
4666           // Unsupported
4667            default:
4668               cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported bit depth for VCGT '%d'", nBytes * 8);
4669               goto Error;
4670            }
4671        } // For all 3 channels
4672     }
4673     break;
4674 
4675    // In this case, gamma is stored as a formula
4676    case cmsVideoCardGammaFormulaType:
4677    {
4678        _cmsVCGTGAMMA Colorant[3];
4679 
4680         // Populate tone curves
4681        for (n=0; n < 3; n++) {
4682 
4683            double Params[10];
4684 
4685            if (!_cmsRead15Fixed16Number(io, &Colorant[n].Gamma)) goto Error;
4686            if (!_cmsRead15Fixed16Number(io, &Colorant[n].Min)) goto Error;
4687            if (!_cmsRead15Fixed16Number(io, &Colorant[n].Max)) goto Error;
4688 
4689             // Parametric curve type 5 is:
4690             // Y = (aX + b)^Gamma + e | X >= d
4691             // Y = cX + f             | X < d
4692 
4693             // vcgt formula is:
4694             // Y = (Max – Min) * (X ^ Gamma) + Min
4695 
4696             // So, the translation is
4697             // a = (Max – Min) ^ ( 1 / Gamma)
4698             // e = Min
4699             // b=c=d=f=0
4700 
4701            Params[0] = Colorant[n].Gamma;
4702            Params[1] = pow((Colorant[n].Max - Colorant[n].Min), (1.0 / Colorant[n].Gamma));
4703            Params[2] = 0;
4704            Params[3] = 0;
4705            Params[4] = 0;
4706            Params[5] = Colorant[n].Min;
4707            Params[6] = 0;
4708 
4709            Curves[n] = cmsBuildParametricToneCurve(self ->ContextID, 5, Params);
4710            if (Curves[n] == NULL) goto Error;
4711        }
4712    }
4713    break;
4714 
4715    // Unsupported
4716    default:
4717       cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported tag type for VCGT '%d'", TagType);
4718       goto Error;
4719    }
4720 
4721    *nItems = 1;
4722    return (void*) Curves;
4723 
4724 // Regret,  free all resources
4725 Error:
4726 
4727     cmsFreeToneCurveTriple(Curves);
4728     _cmsFree(self ->ContextID, Curves);
4729     return NULL;
4730 
4731      cmsUNUSED_PARAMETER(SizeOfTag);
4732 }
4733 
4734 
4735 // We don't support all flavors, only 16bits tables and formula
4736 static
4737 cmsBool Type_vcgt_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
4738 {
4739     cmsToneCurve** Curves =  (cmsToneCurve**) Ptr;
4740     cmsUInt32Number i, j;
4741 
4742     if (cmsGetToneCurveParametricType(Curves[0]) == 5 &&
4743         cmsGetToneCurveParametricType(Curves[1]) == 5 &&
4744         cmsGetToneCurveParametricType(Curves[2]) == 5) {
4745 
4746             if (!_cmsWriteUInt32Number(io, cmsVideoCardGammaFormulaType)) return FALSE;
4747 
4748             // Save parameters
4749             for (i=0; i < 3; i++) {
4750 
4751                 _cmsVCGTGAMMA v;
4752 
4753                 v.Gamma = Curves[i] ->Segments[0].Params[0];
4754                 v.Min   = Curves[i] ->Segments[0].Params[5];
4755                 v.Max   = pow(Curves[i] ->Segments[0].Params[1], v.Gamma) + v.Min;
4756 
4757                 if (!_cmsWrite15Fixed16Number(io, v.Gamma)) return FALSE;
4758                 if (!_cmsWrite15Fixed16Number(io, v.Min)) return FALSE;
4759                 if (!_cmsWrite15Fixed16Number(io, v.Max)) return FALSE;
4760             }
4761     }
4762 
4763     else {
4764 
4765         // Always store as a table of 256 words
4766         if (!_cmsWriteUInt32Number(io, cmsVideoCardGammaTableType)) return FALSE;
4767         if (!_cmsWriteUInt16Number(io, 3)) return FALSE;
4768         if (!_cmsWriteUInt16Number(io, 256)) return FALSE;
4769         if (!_cmsWriteUInt16Number(io, 2)) return FALSE;
4770 
4771         for (i=0; i < 3; i++) {
4772             for (j=0; j < 256; j++) {
4773 
4774                 cmsFloat32Number v = cmsEvalToneCurveFloat(Curves[i], (cmsFloat32Number) (j / 255.0));
4775                 cmsUInt16Number  n = _cmsQuickSaturateWord(v * 65535.0);
4776 
4777                 if (!_cmsWriteUInt16Number(io, n)) return FALSE;
4778             }
4779         }
4780     }
4781 
4782     return TRUE;
4783 
4784     cmsUNUSED_PARAMETER(self);
4785     cmsUNUSED_PARAMETER(nItems);
4786 }
4787 
4788 static
4789 void* Type_vcgt_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
4790 {
4791     cmsToneCurve** OldCurves =  (cmsToneCurve**) Ptr;
4792     cmsToneCurve** NewCurves;
4793 
4794     NewCurves = ( cmsToneCurve**) _cmsCalloc(self ->ContextID, 3, sizeof(cmsToneCurve*));
4795     if (NewCurves == NULL) return NULL;
4796 
4797     NewCurves[0] = cmsDupToneCurve(OldCurves[0]);
4798     NewCurves[1] = cmsDupToneCurve(OldCurves[1]);
4799     NewCurves[2] = cmsDupToneCurve(OldCurves[2]);
4800 
4801     return (void*) NewCurves;
4802 
4803     cmsUNUSED_PARAMETER(n);
4804 }
4805 
4806 
4807 static
4808 void Type_vcgt_Free(struct _cms_typehandler_struct* self, void* Ptr)
4809 {
4810     cmsFreeToneCurveTriple((cmsToneCurve**) Ptr);
4811     _cmsFree(self ->ContextID, Ptr);
4812 }
4813 
4814 
4815 // ********************************************************************************
4816 // Type cmsSigDictType
4817 // ********************************************************************************
4818 
4819 // Single column of the table can point to wchar or MLUC elements. Holds arrays of data
4820 typedef struct {
4821     cmsContext ContextID;
4822     cmsUInt32Number *Offsets;
4823     cmsUInt32Number *Sizes;
4824 } _cmsDICelem;
4825 
4826 typedef struct {
4827     _cmsDICelem Name, Value, DisplayName, DisplayValue;
4828 
4829 } _cmsDICarray;
4830 
4831 // Allocate an empty array element
4832 static
4833 cmsBool AllocElem(cmsContext ContextID, _cmsDICelem* e,  cmsUInt32Number Count)
4834 {
4835     e->Offsets = (cmsUInt32Number *) _cmsCalloc(ContextID, Count, sizeof(cmsUInt32Number));
4836     if (e->Offsets == NULL) return FALSE;
4837 
4838     e->Sizes = (cmsUInt32Number *) _cmsCalloc(ContextID, Count, sizeof(cmsUInt32Number));
4839     if (e->Sizes == NULL) {
4840 
4841         _cmsFree(ContextID, e -> Offsets);
4842         return FALSE;
4843     }
4844 
4845     e ->ContextID = ContextID;
4846     return TRUE;
4847 }
4848 
4849 // Free an array element
4850 static
4851 void FreeElem(_cmsDICelem* e)
4852 {
4853     if (e ->Offsets != NULL)  _cmsFree(e -> ContextID, e -> Offsets);
4854     if (e ->Sizes   != NULL)  _cmsFree(e -> ContextID, e -> Sizes);
4855     e->Offsets = e ->Sizes = NULL;
4856 }
4857 
4858 // Get rid of whole array
4859 static
4860 void FreeArray( _cmsDICarray* a)
4861 {
4862     if (a ->Name.Offsets != NULL) FreeElem(&a->Name);
4863     if (a ->Value.Offsets != NULL) FreeElem(&a ->Value);
4864     if (a ->DisplayName.Offsets != NULL) FreeElem(&a->DisplayName);
4865     if (a ->DisplayValue.Offsets != NULL) FreeElem(&a ->DisplayValue);
4866 }
4867 
4868 
4869 // Allocate whole array
4870 static
4871 cmsBool AllocArray(cmsContext ContextID, _cmsDICarray* a, cmsUInt32Number Count, cmsUInt32Number Length)
4872 {
4873     // Empty values
4874     memset(a, 0, sizeof(_cmsDICarray));
4875 
4876     // On depending on record size, create column arrays
4877     if (!AllocElem(ContextID, &a ->Name, Count)) goto Error;
4878     if (!AllocElem(ContextID, &a ->Value, Count)) goto Error;
4879 
4880     if (Length > 16) {
4881         if (!AllocElem(ContextID, &a -> DisplayName, Count)) goto Error;
4882 
4883     }
4884     if (Length > 24) {
4885         if (!AllocElem(ContextID, &a ->DisplayValue, Count)) goto Error;
4886     }
4887     return TRUE;
4888 
4889 Error:
4890     FreeArray(a);
4891     return FALSE;
4892 }
4893 
4894 // Read one element
4895 static
4896 cmsBool ReadOneElem(cmsIOHANDLER* io,  _cmsDICelem* e, cmsUInt32Number i, cmsUInt32Number BaseOffset)
4897 {
4898     if (!_cmsReadUInt32Number(io, &e->Offsets[i])) return FALSE;
4899     if (!_cmsReadUInt32Number(io, &e ->Sizes[i])) return FALSE;
4900 
4901     // An offset of zero has special meaning and shal be preserved
4902     if (e ->Offsets[i] > 0)
4903         e ->Offsets[i] += BaseOffset;
4904     return TRUE;
4905 }
4906 
4907 
4908 static
4909 cmsBool ReadOffsetArray(cmsIOHANDLER* io,  _cmsDICarray* a, cmsUInt32Number Count, cmsUInt32Number Length, cmsUInt32Number BaseOffset)
4910 {
4911     cmsUInt32Number i;
4912 
4913     // Read column arrays
4914     for (i=0; i < Count; i++) {
4915 
4916         if (!ReadOneElem(io, &a -> Name, i, BaseOffset)) return FALSE;
4917         if (!ReadOneElem(io, &a -> Value, i, BaseOffset)) return FALSE;
4918 
4919         if (Length > 16) {
4920 
4921             if (!ReadOneElem(io, &a ->DisplayName, i, BaseOffset)) return FALSE;
4922 
4923         }
4924 
4925         if (Length > 24) {
4926 
4927             if (!ReadOneElem(io, & a -> DisplayValue, i, BaseOffset)) return FALSE;
4928         }
4929     }
4930     return TRUE;
4931 }
4932 
4933 
4934 // Write one element
4935 static
4936 cmsBool WriteOneElem(cmsIOHANDLER* io,  _cmsDICelem* e, cmsUInt32Number i)
4937 {
4938     if (!_cmsWriteUInt32Number(io, e->Offsets[i])) return FALSE;
4939     if (!_cmsWriteUInt32Number(io, e ->Sizes[i])) return FALSE;
4940 
4941     return TRUE;
4942 }
4943 
4944 static
4945 cmsBool WriteOffsetArray(cmsIOHANDLER* io,  _cmsDICarray* a, cmsUInt32Number Count, cmsUInt32Number Length)
4946 {
4947     cmsUInt32Number i;
4948 
4949     for (i=0; i < Count; i++) {
4950 
4951         if (!WriteOneElem(io, &a -> Name, i)) return FALSE;
4952         if (!WriteOneElem(io, &a -> Value, i))  return FALSE;
4953 
4954         if (Length > 16) {
4955 
4956             if (!WriteOneElem(io, &a -> DisplayName, i))  return FALSE;
4957         }
4958 
4959         if (Length > 24) {
4960 
4961             if (!WriteOneElem(io, &a -> DisplayValue, i))  return FALSE;
4962         }
4963     }
4964 
4965     return TRUE;
4966 }
4967 
4968 static
4969 cmsBool ReadOneWChar(cmsIOHANDLER* io,  _cmsDICelem* e, cmsUInt32Number i, wchar_t ** wcstr)
4970 {
4971 
4972     cmsUInt32Number nChars;
4973 
4974       // Special case for undefined strings (see ICC Votable
4975       // Proposal Submission, Dictionary Type and Metadata TAG Definition)
4976       if (e -> Offsets[i] == 0) {
4977 
4978           *wcstr = NULL;
4979           return TRUE;
4980       }
4981 
4982       if (!io -> Seek(io, e -> Offsets[i])) return FALSE;
4983 
4984       nChars = e ->Sizes[i] / sizeof(cmsUInt16Number);
4985 
4986 
4987       *wcstr = (wchar_t*) _cmsMallocZero(e ->ContextID, (nChars + 1) * sizeof(wchar_t));
4988       if (*wcstr == NULL) return FALSE;
4989 
4990       if (!_cmsReadWCharArray(io, nChars, *wcstr)) {
4991           _cmsFree(e ->ContextID, *wcstr);
4992           return FALSE;
4993       }
4994 
4995       // End of string marker
4996       (*wcstr)[nChars] = 0;
4997       return TRUE;
4998 }
4999 
5000 static
5001 cmsUInt32Number mywcslen(const wchar_t *s)
5002 {
5003     const wchar_t *p;
5004 
5005     p = s;
5006     while (*p)
5007         p++;
5008 
5009     return (cmsUInt32Number)(p - s);
5010 }
5011 
5012 static
5013 cmsBool WriteOneWChar(cmsIOHANDLER* io,  _cmsDICelem* e, cmsUInt32Number i, const wchar_t * wcstr, cmsUInt32Number BaseOffset)
5014 {
5015     cmsUInt32Number Before = io ->Tell(io);
5016     cmsUInt32Number n;
5017 
5018     e ->Offsets[i] = Before - BaseOffset;
5019 
5020     if (wcstr == NULL) {
5021         e ->Sizes[i] = 0;
5022         e ->Offsets[i] = 0;
5023         return TRUE;
5024     }
5025 
5026     n = mywcslen(wcstr);
5027     if (!_cmsWriteWCharArray(io,  n, wcstr)) return FALSE;
5028 
5029     e ->Sizes[i] = io ->Tell(io) - Before;
5030     return TRUE;
5031 }
5032 
5033 static
5034 cmsBool ReadOneMLUC(struct _cms_typehandler_struct* self, cmsIOHANDLER* io,  _cmsDICelem* e, cmsUInt32Number i, cmsMLU** mlu)
5035 {
5036     cmsUInt32Number nItems = 0;
5037 
5038     // A way to get null MLUCs
5039     if (e -> Offsets[i] == 0 || e ->Sizes[i] == 0) {
5040 
5041         *mlu = NULL;
5042         return TRUE;
5043     }
5044 
5045     if (!io -> Seek(io, e -> Offsets[i])) return FALSE;
5046 
5047     *mlu = (cmsMLU*) Type_MLU_Read(self, io, &nItems, e ->Sizes[i]);
5048     return *mlu != NULL;
5049 }
5050 
5051 static
5052 cmsBool WriteOneMLUC(struct _cms_typehandler_struct* self, cmsIOHANDLER* io,  _cmsDICelem* e, cmsUInt32Number i, const cmsMLU* mlu, cmsUInt32Number BaseOffset)
5053 {
5054     cmsUInt32Number Before;
5055 
5056      // Special case for undefined strings (see ICC Votable
5057      // Proposal Submission, Dictionary Type and Metadata TAG Definition)
5058      if (mlu == NULL) {
5059         e ->Sizes[i] = 0;
5060         e ->Offsets[i] = 0;
5061         return TRUE;
5062     }
5063 
5064     Before = io ->Tell(io);
5065     e ->Offsets[i] = Before - BaseOffset;
5066 
5067     if (!Type_MLU_Write(self, io, (void*) mlu, 1)) return FALSE;
5068 
5069     e ->Sizes[i] = io ->Tell(io) - Before;
5070     return TRUE;
5071 }
5072 
5073 
5074 static
5075 void *Type_Dictionary_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
5076 {
5077    cmsHANDLE hDict;
5078    cmsUInt32Number i, Count, Length;
5079    cmsUInt32Number BaseOffset;
5080    _cmsDICarray a;
5081    wchar_t *NameWCS = NULL, *ValueWCS = NULL;
5082    cmsMLU *DisplayNameMLU = NULL, *DisplayValueMLU=NULL;
5083    cmsBool rc;
5084 
5085     *nItems = 0;
5086 
5087     // Get actual position as a basis for element offsets
5088     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
5089 
5090     // Get name-value record count
5091     if (!_cmsReadUInt32Number(io, &Count)) return NULL;
5092     SizeOfTag -= sizeof(cmsUInt32Number);
5093 
5094     // Get rec length
5095     if (!_cmsReadUInt32Number(io, &Length)) return NULL;
5096     SizeOfTag -= sizeof(cmsUInt32Number);
5097 
5098     // Check for valid lengths
5099     if (Length != 16 && Length != 24 && Length != 32) {
5100          cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown record length in dictionary '%d'", Length);
5101          return NULL;
5102     }
5103 
5104     // Creates an empty dictionary
5105     hDict = cmsDictAlloc(self -> ContextID);
5106     if (hDict == NULL) return NULL;
5107 
5108     // On depending on record size, create column arrays
5109     if (!AllocArray(self -> ContextID, &a, Count, Length)) goto Error;
5110 
5111     // Read column arrays
5112     if (!ReadOffsetArray(io, &a, Count, Length, BaseOffset)) goto Error;
5113 
5114     // Seek to each element and read it
5115     for (i=0; i < Count; i++) {
5116 
5117         if (!ReadOneWChar(io, &a.Name, i, &NameWCS)) goto Error;
5118         if (!ReadOneWChar(io, &a.Value, i, &ValueWCS)) goto Error;
5119 
5120         if (Length > 16) {
5121             if (!ReadOneMLUC(self, io, &a.DisplayName, i, &DisplayNameMLU)) goto Error;
5122         }
5123 
5124         if (Length > 24) {
5125             if (!ReadOneMLUC(self, io, &a.DisplayValue, i, &DisplayValueMLU)) goto Error;
5126         }
5127 
5128         if (NameWCS == NULL || ValueWCS == NULL) {
5129 
5130             cmsSignalError(self->ContextID, cmsERROR_CORRUPTION_DETECTED, "Bad dictionary Name/Value");
5131             rc = FALSE;
5132         }
5133         else {
5134 
5135             rc = cmsDictAddEntry(hDict, NameWCS, ValueWCS, DisplayNameMLU, DisplayValueMLU);
5136         }
5137 
5138         if (NameWCS != NULL) _cmsFree(self ->ContextID, NameWCS);
5139         if (ValueWCS != NULL) _cmsFree(self ->ContextID, ValueWCS);
5140         if (DisplayNameMLU != NULL) cmsMLUfree(DisplayNameMLU);
5141         if (DisplayValueMLU != NULL) cmsMLUfree(DisplayValueMLU);
5142 
5143         if (!rc) goto Error;
5144     }
5145 
5146    FreeArray(&a);
5147    *nItems = 1;
5148    return (void*) hDict;
5149 
5150 Error:
5151    FreeArray(&a);
5152    cmsDictFree(hDict);
5153    return NULL;
5154 }
5155 
5156 
5157 static
5158 cmsBool Type_Dictionary_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
5159 {
5160     cmsHANDLE hDict = (cmsHANDLE) Ptr;
5161     const cmsDICTentry* p;
5162     cmsBool AnyName, AnyValue;
5163     cmsUInt32Number i, Count, Length;
5164     cmsUInt32Number DirectoryPos, CurrentPos, BaseOffset;
5165    _cmsDICarray a;
5166 
5167     if (hDict == NULL) return FALSE;
5168 
5169     BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase);
5170 
5171     // Let's inspect the dictionary
5172     Count = 0; AnyName = FALSE; AnyValue = FALSE;
5173     for (p = cmsDictGetEntryList(hDict); p != NULL; p = cmsDictNextEntry(p)) {
5174 
5175         if (p ->DisplayName != NULL) AnyName = TRUE;
5176         if (p ->DisplayValue != NULL) AnyValue = TRUE;
5177         Count++;
5178     }
5179 
5180     Length = 16;
5181     if (AnyName)  Length += 8;
5182     if (AnyValue) Length += 8;
5183 
5184     if (!_cmsWriteUInt32Number(io, Count)) return FALSE;
5185     if (!_cmsWriteUInt32Number(io, Length)) return FALSE;
5186 
5187     // Keep starting position of offsets table
5188     DirectoryPos = io ->Tell(io);
5189 
5190     // Allocate offsets array
5191     if (!AllocArray(self ->ContextID, &a, Count, Length)) goto Error;
5192 
5193     // Write a fake directory to be filled latter on
5194     if (!WriteOffsetArray(io, &a, Count, Length)) goto Error;
5195 
5196     // Write each element. Keep track of the size as well.
5197     p = cmsDictGetEntryList(hDict);
5198     for (i=0; i < Count; i++) {
5199 
5200         if (!WriteOneWChar(io, &a.Name, i,  p ->Name, BaseOffset)) goto Error;
5201         if (!WriteOneWChar(io, &a.Value, i, p ->Value, BaseOffset)) goto Error;
5202 
5203         if (p ->DisplayName != NULL) {
5204             if (!WriteOneMLUC(self, io, &a.DisplayName, i, p ->DisplayName, BaseOffset)) goto Error;
5205         }
5206 
5207         if (p ->DisplayValue != NULL) {
5208             if (!WriteOneMLUC(self, io, &a.DisplayValue, i, p ->DisplayValue, BaseOffset)) goto Error;
5209         }
5210 
5211        p = cmsDictNextEntry(p);
5212     }
5213 
5214     // Write the directory
5215     CurrentPos = io ->Tell(io);
5216     if (!io ->Seek(io, DirectoryPos)) goto Error;
5217 
5218     if (!WriteOffsetArray(io, &a, Count, Length)) goto Error;
5219 
5220     if (!io ->Seek(io, CurrentPos)) goto Error;
5221 
5222     FreeArray(&a);
5223     return TRUE;
5224 
5225 Error:
5226     FreeArray(&a);
5227     return FALSE;
5228 
5229     cmsUNUSED_PARAMETER(nItems);
5230 }
5231 
5232 
5233 static
5234 void* Type_Dictionary_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n)
5235 {
5236     return (void*)  cmsDictDup((cmsHANDLE) Ptr);
5237 
5238     cmsUNUSED_PARAMETER(n);
5239     cmsUNUSED_PARAMETER(self);
5240 }
5241 
5242 
5243 static
5244 void Type_Dictionary_Free(struct _cms_typehandler_struct* self, void* Ptr)
5245 {
5246     cmsDictFree((cmsHANDLE) Ptr);
5247     cmsUNUSED_PARAMETER(self);
5248 }
5249 
5250 
5251 // ********************************************************************************
5252 // Type support main routines
5253 // ********************************************************************************
5254 
5255 
5256 // This is the list of built-in types
5257 static _cmsTagTypeLinkedList SupportedTagTypes[] = {
5258 
5259 {TYPE_HANDLER(cmsSigChromaticityType,          Chromaticity),        &SupportedTagTypes[1] },
5260 {TYPE_HANDLER(cmsSigColorantOrderType,         ColorantOrderType),   &SupportedTagTypes[2] },
5261 {TYPE_HANDLER(cmsSigS15Fixed16ArrayType,       S15Fixed16),          &SupportedTagTypes[3] },
5262 {TYPE_HANDLER(cmsSigU16Fixed16ArrayType,       U16Fixed16),          &SupportedTagTypes[4] },
5263 {TYPE_HANDLER(cmsSigTextType,                  Text),                &SupportedTagTypes[5] },
5264 {TYPE_HANDLER(cmsSigTextDescriptionType,       Text_Description),    &SupportedTagTypes[6] },
5265 {TYPE_HANDLER(cmsSigCurveType,                 Curve),               &SupportedTagTypes[7] },
5266 {TYPE_HANDLER(cmsSigParametricCurveType,       ParametricCurve),     &SupportedTagTypes[8] },
5267 {TYPE_HANDLER(cmsSigDateTimeType,              DateTime),            &SupportedTagTypes[9] },
5268 {TYPE_HANDLER(cmsSigLut8Type,                  LUT8),                &SupportedTagTypes[10] },
5269 {TYPE_HANDLER(cmsSigLut16Type,                 LUT16),               &SupportedTagTypes[11] },
5270 {TYPE_HANDLER(cmsSigColorantTableType,         ColorantTable),       &SupportedTagTypes[12] },
5271 {TYPE_HANDLER(cmsSigNamedColor2Type,           NamedColor),          &SupportedTagTypes[13] },
5272 {TYPE_HANDLER(cmsSigMultiLocalizedUnicodeType, MLU),                 &SupportedTagTypes[14] },
5273 {TYPE_HANDLER(cmsSigProfileSequenceDescType,   ProfileSequenceDesc), &SupportedTagTypes[15] },
5274 {TYPE_HANDLER(cmsSigSignatureType,             Signature),           &SupportedTagTypes[16] },
5275 {TYPE_HANDLER(cmsSigMeasurementType,           Measurement),         &SupportedTagTypes[17] },
5276 {TYPE_HANDLER(cmsSigDataType,                  Data),                &SupportedTagTypes[18] },
5277 {TYPE_HANDLER(cmsSigLutAtoBType,               LUTA2B),              &SupportedTagTypes[19] },
5278 {TYPE_HANDLER(cmsSigLutBtoAType,               LUTB2A),              &SupportedTagTypes[20] },
5279 {TYPE_HANDLER(cmsSigUcrBgType,                 UcrBg),               &SupportedTagTypes[21] },
5280 {TYPE_HANDLER(cmsSigCrdInfoType,               CrdInfo),             &SupportedTagTypes[22] },
5281 {TYPE_HANDLER(cmsSigMultiProcessElementType,   MPE),                 &SupportedTagTypes[23] },
5282 {TYPE_HANDLER(cmsSigScreeningType,             Screening),           &SupportedTagTypes[24] },
5283 {TYPE_HANDLER(cmsSigViewingConditionsType,     ViewingConditions),   &SupportedTagTypes[25] },
5284 {TYPE_HANDLER(cmsSigXYZType,                   XYZ),                 &SupportedTagTypes[26] },
5285 {TYPE_HANDLER(cmsCorbisBrokenXYZtype,          XYZ),                 &SupportedTagTypes[27] },
5286 {TYPE_HANDLER(cmsMonacoBrokenCurveType,        Curve),               &SupportedTagTypes[28] },
5287 {TYPE_HANDLER(cmsSigProfileSequenceIdType,     ProfileSequenceId),   &SupportedTagTypes[29] },
5288 {TYPE_HANDLER(cmsSigDictType,                  Dictionary),          &SupportedTagTypes[30] },
5289 {TYPE_HANDLER(cmsSigVcgtType,                  vcgt),                NULL }
5290 };
5291 
5292 
5293 _cmsTagTypePluginChunkType _cmsTagTypePluginChunk = { NULL };
5294 
5295 
5296 
5297 // Duplicates the zone of memory used by the plug-in in the new context
5298 static
5299 void DupTagTypeList(struct _cmsContext_struct* ctx,
5300                     const struct _cmsContext_struct* src,
5301                     int loc)
5302 {
5303    _cmsTagTypePluginChunkType newHead = { NULL };
5304    _cmsTagTypeLinkedList*  entry;
5305    _cmsTagTypeLinkedList*  Anterior = NULL;
5306    _cmsTagTypePluginChunkType* head = (_cmsTagTypePluginChunkType*) src->chunks[loc];
5307 
5308    // Walk the list copying all nodes
5309    for (entry = head->TagTypes;
5310        entry != NULL;
5311        entry = entry ->Next) {
5312 
5313            _cmsTagTypeLinkedList *newEntry = ( _cmsTagTypeLinkedList *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTagTypeLinkedList));
5314 
5315            if (newEntry == NULL)
5316                return;
5317 
5318            // We want to keep the linked list order, so this is a little bit tricky
5319            newEntry -> Next = NULL;
5320            if (Anterior)
5321                Anterior -> Next = newEntry;
5322 
5323            Anterior = newEntry;
5324 
5325            if (newHead.TagTypes == NULL)
5326                newHead.TagTypes = newEntry;
5327    }
5328 
5329    ctx ->chunks[loc] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsTagTypePluginChunkType));
5330 }
5331 
5332 
5333 void _cmsAllocTagTypePluginChunk(struct _cmsContext_struct* ctx,
5334                                  const struct _cmsContext_struct* src)
5335 {
5336     if (src != NULL) {
5337 
5338         // Duplicate the LIST
5339         DupTagTypeList(ctx, src, TagTypePlugin);
5340     }
5341     else {
5342         static _cmsTagTypePluginChunkType TagTypePluginChunk = { NULL };
5343         ctx ->chunks[TagTypePlugin] = _cmsSubAllocDup(ctx ->MemPool, &TagTypePluginChunk, sizeof(_cmsTagTypePluginChunkType));
5344     }
5345 }
5346 
5347 void _cmsAllocMPETypePluginChunk(struct _cmsContext_struct* ctx,
5348                                const struct _cmsContext_struct* src)
5349 {
5350     if (src != NULL) {
5351 
5352         // Duplicate the LIST
5353         DupTagTypeList(ctx, src, MPEPlugin);
5354     }
5355     else {
5356         static _cmsTagTypePluginChunkType TagTypePluginChunk = { NULL };
5357         ctx ->chunks[MPEPlugin] = _cmsSubAllocDup(ctx ->MemPool, &TagTypePluginChunk, sizeof(_cmsTagTypePluginChunkType));
5358     }
5359 
5360 }
5361 
5362 
5363 // Both kind of plug-ins share same structure
5364 cmsBool  _cmsRegisterTagTypePlugin(cmsContext id, cmsPluginBase* Data)
5365 {
5366     return RegisterTypesPlugin(id, Data, TagTypePlugin);
5367 }
5368 
5369 cmsBool  _cmsRegisterMultiProcessElementPlugin(cmsContext id, cmsPluginBase* Data)
5370 {
5371     return RegisterTypesPlugin(id, Data,MPEPlugin);
5372 }
5373 
5374 
5375 // Wrapper for tag types
5376 cmsTagTypeHandler* _cmsGetTagTypeHandler(cmsContext ContextID, cmsTagTypeSignature sig)
5377 {
5378     _cmsTagTypePluginChunkType* ctx = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(ContextID, TagTypePlugin);
5379 
5380     return GetHandler(sig, ctx->TagTypes, SupportedTagTypes);
5381 }
5382 
5383 // ********************************************************************************
5384 // Tag support main routines
5385 // ********************************************************************************
5386 
5387 typedef struct _cmsTagLinkedList_st {
5388 
5389             cmsTagSignature Signature;
5390             cmsTagDescriptor Descriptor;
5391             struct _cmsTagLinkedList_st* Next;
5392 
5393 } _cmsTagLinkedList;
5394 
5395 // This is the list of built-in tags
5396 static _cmsTagLinkedList SupportedTags[] = {
5397 
5398     { cmsSigAToB0Tag,               { 1, 3,  { cmsSigLut16Type,  cmsSigLutAtoBType, cmsSigLut8Type}, DecideLUTtypeA2B}, &SupportedTags[1]},
5399     { cmsSigAToB1Tag,               { 1, 3,  { cmsSigLut16Type,  cmsSigLutAtoBType, cmsSigLut8Type}, DecideLUTtypeA2B}, &SupportedTags[2]},
5400     { cmsSigAToB2Tag,               { 1, 3,  { cmsSigLut16Type,  cmsSigLutAtoBType, cmsSigLut8Type}, DecideLUTtypeA2B}, &SupportedTags[3]},
5401     { cmsSigBToA0Tag,               { 1, 3,  { cmsSigLut16Type,  cmsSigLutBtoAType, cmsSigLut8Type}, DecideLUTtypeB2A}, &SupportedTags[4]},
5402     { cmsSigBToA1Tag,               { 1, 3,  { cmsSigLut16Type,  cmsSigLutBtoAType, cmsSigLut8Type}, DecideLUTtypeB2A}, &SupportedTags[5]},
5403     { cmsSigBToA2Tag,               { 1, 3,  { cmsSigLut16Type,  cmsSigLutBtoAType, cmsSigLut8Type}, DecideLUTtypeB2A}, &SupportedTags[6]},
5404 
5405     // Allow corbis  and its broken XYZ type
5406     { cmsSigRedColorantTag,         { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, DecideXYZtype}, &SupportedTags[7]},
5407     { cmsSigGreenColorantTag,       { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, DecideXYZtype}, &SupportedTags[8]},
5408     { cmsSigBlueColorantTag,        { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, DecideXYZtype}, &SupportedTags[9]},
5409 
5410     { cmsSigRedTRCTag,              { 1, 3, { cmsSigCurveType, cmsSigParametricCurveType, cmsMonacoBrokenCurveType }, DecideCurveType}, &SupportedTags[10]},
5411     { cmsSigGreenTRCTag,            { 1, 3, { cmsSigCurveType, cmsSigParametricCurveType, cmsMonacoBrokenCurveType }, DecideCurveType}, &SupportedTags[11]},
5412     { cmsSigBlueTRCTag,             { 1, 3, { cmsSigCurveType, cmsSigParametricCurveType, cmsMonacoBrokenCurveType }, DecideCurveType}, &SupportedTags[12]},
5413 
5414     { cmsSigCalibrationDateTimeTag, { 1, 1, { cmsSigDateTimeType }, NULL}, &SupportedTags[13]},
5415     { cmsSigCharTargetTag,          { 1, 1, { cmsSigTextType },     NULL}, &SupportedTags[14]},
5416 
5417     { cmsSigChromaticAdaptationTag, { 9, 1, { cmsSigS15Fixed16ArrayType }, NULL}, &SupportedTags[15]},
5418     { cmsSigChromaticityTag,        { 1, 1, { cmsSigChromaticityType    }, NULL}, &SupportedTags[16]},
5419     { cmsSigColorantOrderTag,       { 1, 1, { cmsSigColorantOrderType   }, NULL}, &SupportedTags[17]},
5420     { cmsSigColorantTableTag,       { 1, 1, { cmsSigColorantTableType   }, NULL}, &SupportedTags[18]},
5421     { cmsSigColorantTableOutTag,    { 1, 1, { cmsSigColorantTableType   }, NULL}, &SupportedTags[19]},
5422 
5423     { cmsSigCopyrightTag,           { 1, 3, { cmsSigTextType,  cmsSigMultiLocalizedUnicodeType, cmsSigTextDescriptionType}, DecideTextType}, &SupportedTags[20]},
5424     { cmsSigDateTimeTag,            { 1, 1, { cmsSigDateTimeType }, NULL}, &SupportedTags[21]},
5425 
5426     { cmsSigDeviceMfgDescTag,       { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[22]},
5427     { cmsSigDeviceModelDescTag,     { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[23]},
5428 
5429     { cmsSigGamutTag,               { 1, 3, { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[24]},
5430 
5431     { cmsSigGrayTRCTag,             { 1, 2, { cmsSigCurveType, cmsSigParametricCurveType }, DecideCurveType}, &SupportedTags[25]},
5432     { cmsSigLuminanceTag,           { 1, 1, { cmsSigXYZType }, NULL}, &SupportedTags[26]},
5433 
5434     { cmsSigMediaBlackPointTag,     { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, NULL}, &SupportedTags[27]},
5435     { cmsSigMediaWhitePointTag,     { 1, 2, { cmsSigXYZType, cmsCorbisBrokenXYZtype }, NULL}, &SupportedTags[28]},
5436 
5437     { cmsSigNamedColor2Tag,         { 1, 1, { cmsSigNamedColor2Type }, NULL}, &SupportedTags[29]},
5438 
5439     { cmsSigPreview0Tag,            { 1, 3,  { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[30]},
5440     { cmsSigPreview1Tag,            { 1, 3,  { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[31]},
5441     { cmsSigPreview2Tag,            { 1, 3,  { cmsSigLut16Type, cmsSigLutBtoAType, cmsSigLut8Type }, DecideLUTtypeB2A}, &SupportedTags[32]},
5442 
5443     { cmsSigProfileDescriptionTag,  { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[33]},
5444     { cmsSigProfileSequenceDescTag, { 1, 1, { cmsSigProfileSequenceDescType }, NULL}, &SupportedTags[34]},
5445     { cmsSigTechnologyTag,          { 1, 1, { cmsSigSignatureType }, NULL},  &SupportedTags[35]},
5446 
5447     { cmsSigColorimetricIntentImageStateTag,   { 1, 1, { cmsSigSignatureType }, NULL}, &SupportedTags[36]},
5448     { cmsSigPerceptualRenderingIntentGamutTag, { 1, 1, { cmsSigSignatureType }, NULL}, &SupportedTags[37]},
5449     { cmsSigSaturationRenderingIntentGamutTag, { 1, 1, { cmsSigSignatureType }, NULL}, &SupportedTags[38]},
5450 
5451     { cmsSigMeasurementTag,         { 1, 1, { cmsSigMeasurementType }, NULL}, &SupportedTags[39]},
5452 
5453     { cmsSigPs2CRD0Tag,             { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[40]},
5454     { cmsSigPs2CRD1Tag,             { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[41]},
5455     { cmsSigPs2CRD2Tag,             { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[42]},
5456     { cmsSigPs2CRD3Tag,             { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[43]},
5457     { cmsSigPs2CSATag,              { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[44]},
5458     { cmsSigPs2RenderingIntentTag,  { 1, 1, { cmsSigDataType }, NULL}, &SupportedTags[45]},
5459 
5460     { cmsSigViewingCondDescTag,     { 1, 3, { cmsSigTextDescriptionType, cmsSigMultiLocalizedUnicodeType, cmsSigTextType}, DecideTextDescType}, &SupportedTags[46]},
5461 
5462     { cmsSigUcrBgTag,               { 1, 1, { cmsSigUcrBgType}, NULL},    &SupportedTags[47]},
5463     { cmsSigCrdInfoTag,             { 1, 1, { cmsSigCrdInfoType}, NULL},  &SupportedTags[48]},
5464 
5465     { cmsSigDToB0Tag,               { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[49]},
5466     { cmsSigDToB1Tag,               { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[50]},
5467     { cmsSigDToB2Tag,               { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[51]},
5468     { cmsSigDToB3Tag,               { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[52]},
5469     { cmsSigBToD0Tag,               { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[53]},
5470     { cmsSigBToD1Tag,               { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[54]},
5471     { cmsSigBToD2Tag,               { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[55]},
5472     { cmsSigBToD3Tag,               { 1, 1, { cmsSigMultiProcessElementType}, NULL}, &SupportedTags[56]},
5473 
5474     { cmsSigScreeningDescTag,       { 1, 1, { cmsSigTextDescriptionType },    NULL}, &SupportedTags[57]},
5475     { cmsSigViewingConditionsTag,   { 1, 1, { cmsSigViewingConditionsType },  NULL}, &SupportedTags[58]},
5476 
5477     { cmsSigScreeningTag,           { 1, 1, { cmsSigScreeningType},          NULL }, &SupportedTags[59]},
5478     { cmsSigVcgtTag,                { 1, 1, { cmsSigVcgtType},               NULL }, &SupportedTags[60]},
5479     { cmsSigMetaTag,                { 1, 1, { cmsSigDictType},               NULL }, &SupportedTags[61]},
5480     { cmsSigProfileSequenceIdTag,   { 1, 1, { cmsSigProfileSequenceIdType},  NULL },  &SupportedTags[62]},
5481     { cmsSigProfileDescriptionMLTag,{ 1, 1, { cmsSigMultiLocalizedUnicodeType}, NULL}, NULL}
5482 
5483 
5484 };
5485 
5486 /*
5487     Not supported                 Why
5488     =======================       =========================================
5489     cmsSigOutputResponseTag   ==> WARNING, POSSIBLE PATENT ON THIS SUBJECT!
5490     cmsSigNamedColorTag       ==> Deprecated
5491     cmsSigDataTag             ==> Ancient, unused
5492     cmsSigDeviceSettingsTag   ==> Deprecated, useless
5493 */
5494 
5495 
5496 _cmsTagPluginChunkType _cmsTagPluginChunk = { NULL };
5497 
5498 
5499 // Duplicates the zone of memory used by the plug-in in the new context
5500 static
5501 void DupTagList(struct _cmsContext_struct* ctx,
5502                     const struct _cmsContext_struct* src)
5503 {
5504    _cmsTagPluginChunkType newHead = { NULL };
5505    _cmsTagLinkedList*  entry;
5506    _cmsTagLinkedList*  Anterior = NULL;
5507    _cmsTagPluginChunkType* head = (_cmsTagPluginChunkType*) src->chunks[TagPlugin];
5508 
5509    // Walk the list copying all nodes
5510    for (entry = head->Tag;
5511        entry != NULL;
5512        entry = entry ->Next) {
5513 
5514            _cmsTagLinkedList *newEntry = ( _cmsTagLinkedList *) _cmsSubAllocDup(ctx ->MemPool, entry, sizeof(_cmsTagLinkedList));
5515 
5516            if (newEntry == NULL)
5517                return;
5518 
5519            // We want to keep the linked list order, so this is a little bit tricky
5520            newEntry -> Next = NULL;
5521            if (Anterior)
5522                Anterior -> Next = newEntry;
5523 
5524            Anterior = newEntry;
5525 
5526            if (newHead.Tag == NULL)
5527                newHead.Tag = newEntry;
5528    }
5529 
5530    ctx ->chunks[TagPlugin] = _cmsSubAllocDup(ctx->MemPool, &newHead, sizeof(_cmsTagPluginChunkType));
5531 }
5532 
5533 void _cmsAllocTagPluginChunk(struct _cmsContext_struct* ctx,
5534                                  const struct _cmsContext_struct* src)
5535 {
5536     if (src != NULL) {
5537 
5538         DupTagList(ctx, src);
5539     }
5540     else {
5541         static _cmsTagPluginChunkType TagPluginChunk = { NULL };
5542         ctx ->chunks[TagPlugin] = _cmsSubAllocDup(ctx ->MemPool, &TagPluginChunk, sizeof(_cmsTagPluginChunkType));
5543     }
5544 
5545 }
5546 
5547 cmsBool  _cmsRegisterTagPlugin(cmsContext id, cmsPluginBase* Data)
5548 {
5549     cmsPluginTag* Plugin = (cmsPluginTag*) Data;
5550     _cmsTagLinkedList *pt;
5551     _cmsTagPluginChunkType* TagPluginChunk = ( _cmsTagPluginChunkType*) _cmsContextGetClientChunk(id, TagPlugin);
5552 
5553     if (Data == NULL) {
5554 
5555         TagPluginChunk->Tag = NULL;
5556         return TRUE;
5557     }
5558 
5559     pt = (_cmsTagLinkedList*) _cmsPluginMalloc(id, sizeof(_cmsTagLinkedList));
5560     if (pt == NULL) return FALSE;
5561 
5562     pt ->Signature  = Plugin ->Signature;
5563     pt ->Descriptor = Plugin ->Descriptor;
5564     pt ->Next       = TagPluginChunk ->Tag;
5565 
5566     TagPluginChunk ->Tag = pt;
5567 
5568     return TRUE;
5569 }
5570 
5571 // Return a descriptor for a given tag or NULL
5572 cmsTagDescriptor* _cmsGetTagDescriptor(cmsContext ContextID, cmsTagSignature sig)
5573 {
5574     _cmsTagLinkedList* pt;
5575     _cmsTagPluginChunkType* TagPluginChunk = ( _cmsTagPluginChunkType*) _cmsContextGetClientChunk(ContextID, TagPlugin);
5576 
5577     for (pt = TagPluginChunk->Tag;
5578              pt != NULL;
5579              pt = pt ->Next) {
5580 
5581                 if (sig == pt -> Signature) return &pt ->Descriptor;
5582     }
5583 
5584     for (pt = SupportedTags;
5585             pt != NULL;
5586             pt = pt ->Next) {
5587 
5588                 if (sig == pt -> Signature) return &pt ->Descriptor;
5589     }
5590 
5591     return NULL;
5592 }
5593