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