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