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