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 |
44 | typedef 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 |
69 | static |
70 | cmsBool 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. |
98 | static |
99 | cmsTagTypeHandler* 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 |
122 | static |
123 | cmsBool _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 |
138 | static |
139 | cmsBool _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 |
162 | typedef 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 |
171 | static |
172 | cmsBool 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 | |
218 | Error: |
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 |
225 | static |
226 | cmsBool 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 | |
283 | Error: |
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 | |
299 | static |
300 | void *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 | |
320 | static |
321 | cmsBool 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 | |
329 | static |
330 | void* 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 | |
338 | static |
339 | void Type_XYZ_Free(cmsContext ContextID, struct _cms_typehandler_struct* self, void *Ptr) |
340 | { |
341 | cmsUNUSED_PARAMETER(self); |
342 | _cmsFree(ContextID, Ptr); |
343 | } |
344 | |
345 | |
346 | static |
347 | cmsTagTypeSignature 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 | |
363 | static |
364 | void *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 | |
405 | Error: |
406 | _cmsFree(ContextID, (void*) chrm); |
407 | return NULL; |
408 | |
409 | cmsUNUSED_PARAMETER(SizeOfTag); |
410 | } |
411 | |
412 | static |
413 | cmsBool 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 | |
421 | static |
422 | cmsBool 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 | |
439 | static |
440 | void* 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 | |
448 | static |
449 | void 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 | |
468 | static |
469 | void *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 | |
497 | static |
498 | cmsBool 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 | |
519 | static |
520 | void* 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 | |
529 | static |
530 | void 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 | |
542 | static |
543 | void *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 | |
567 | static |
568 | cmsBool 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 | |
583 | static |
584 | void* 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 | |
591 | static |
592 | void 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 | |
605 | static |
606 | void *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 | |
633 | static |
634 | cmsBool 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 | |
652 | static |
653 | void* 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 | |
659 | static |
660 | void 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 | |
675 | static |
676 | void *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 | |
690 | static |
691 | cmsBool 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 | |
701 | static |
702 | void* 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 | |
708 | static |
709 | void 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 | |
724 | static |
725 | void *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 | |
755 | Error: |
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. |
765 | static |
766 | cmsBool 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 | |
793 | static |
794 | void* 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 | |
803 | static |
804 | void 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 | |
813 | static |
814 | cmsTagTypeSignature 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 |
831 | static |
832 | void *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 | |
866 | static |
867 | cmsBool 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 | |
880 | static |
881 | void* 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 | |
891 | static |
892 | void 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 | |
902 | static |
903 | void *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 | |
973 | Done: |
974 | |
975 | *nItems = 1; |
976 | return mlu; |
977 | |
978 | Error: |
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 |
986 | static |
987 | cmsBool 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 | |
1071 | Error: |
1072 | if (Text) _cmsFree(ContextID, Text); |
1073 | if (Wide) _cmsFree(ContextID, Wide); |
1074 | |
1075 | return rc; |
1076 | |
1077 | cmsUNUSED_PARAMETER(nItems); |
1078 | } |
1079 | |
1080 | |
1081 | static |
1082 | void* 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 | |
1090 | static |
1091 | void 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 | |
1102 | static |
1103 | cmsTagTypeSignature 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 | |
1119 | static |
1120 | void *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 | |
1174 | static |
1175 | cmsBool 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 | |
1198 | static |
1199 | void* 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 | |
1207 | static |
1208 | void 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 |
1225 | static |
1226 | cmsTagTypeSignature 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 | |
1239 | static |
1240 | void *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 | |
1275 | static |
1276 | cmsBool 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 | |
1311 | static |
1312 | void* 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 | |
1320 | static |
1321 | void 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 | |
1346 | static |
1347 | void *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, ×tamp, sizeof(cmsDateTimeNumber), 1) != 1) return NULL; |
1358 | |
1359 | _cmsDecodeDateTimeNumber(ContextID, ×tamp, NewDateTime); |
1360 | |
1361 | *nItems = 1; |
1362 | return NewDateTime; |
1363 | |
1364 | cmsUNUSED_PARAMETER(SizeOfTag); |
1365 | } |
1366 | |
1367 | |
1368 | static |
1369 | cmsBool 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, ×tamp, DateTime); |
1375 | if (!io ->Write(ContextID, io, sizeof(cmsDateTimeNumber), ×tamp)) return FALSE; |
1376 | |
1377 | return TRUE; |
1378 | |
1379 | cmsUNUSED_PARAMETER(nItems); |
1380 | cmsUNUSED_PARAMETER(self); |
1381 | } |
1382 | |
1383 | static |
1384 | void* 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 | |
1391 | static |
1392 | void 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 | /* |
1405 | The measurementType information refers only to the internal profile data and is |
1406 | meant to provide profile makers an alternative to the default measurement |
1407 | specifications. |
1408 | */ |
1409 | |
1410 | static |
1411 | void *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 | |
1432 | static |
1433 | cmsBool 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 | |
1449 | static |
1450 | void* 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 | |
1457 | static |
1458 | void 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 | |
1474 | static |
1475 | void *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 ; |
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 | |
1553 | Error: |
1554 | if (mlu) cmsMLUfree(ContextID, mlu); |
1555 | return NULL; |
1556 | } |
1557 | |
1558 | static |
1559 | cmsBool Type_MLU_Write(cmsContext ContextID, struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) |
1560 | { |
1561 | cmsMLU* mlu =(cmsMLU*) Ptr; |
1562 | cmsUInt32Number ; |
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 | |
1602 | static |
1603 | void* 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 | |
1611 | static |
1612 | void 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 |
1626 | static |
1627 | cmsTagTypeSignature 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 | |
1641 | static |
1642 | cmsTagTypeSignature 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 | /* |
1657 | This structure represents a colour transform using tables of 8-bit precision. |
1658 | This type contains four processing elements: a 3 by 3 matrix (which shall be |
1659 | the identity matrix unless the input colour space is XYZ), a set of one dimensional |
1660 | input tables, a multidimensional lookup table, and a set of one dimensional output |
1661 | tables. Data is processed using these elements via the following sequence: |
1662 | (matrix) -> (1d input tables) -> (multidimensional lookup table - CLUT) -> (1d output tables) |
1663 | |
1664 | Byte Position Field Length (bytes) Content Encoded as... |
1665 | 8 1 Number of Input Channels (i) uInt8Number |
1666 | 9 1 Number of Output Channels (o) uInt8Number |
1667 | 10 1 Number of CLUT grid points (identical for each side) (g) uInt8Number |
1668 | 11 1 Reserved for padding (fill with 00h) |
1669 | |
1670 | 12..15 4 Encoded e00 parameter s15Fixed16Number |
1671 | */ |
1672 | |
1673 | |
1674 | // Read 8 bit tables as gamma functions |
1675 | static |
1676 | cmsBool 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 | |
1714 | Error: |
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 | |
1724 | static |
1725 | cmsBool 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 |
1763 | static |
1764 | cmsUInt32Number 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 | |
1791 | static |
1792 | void *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 | |
1885 | Error: |
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. |
1893 | static |
1894 | cmsBool 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 | |
2000 | static |
2001 | void* 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 | |
2009 | static |
2010 | void 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 |
2023 | static |
2024 | cmsBool 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 | |
2058 | Error: |
2059 | for (i=0; i < nChannels; i++) { |
2060 | if (Tables[i]) cmsFreeToneCurve(ContextID, Tables[i]); |
2061 | } |
2062 | |
2063 | return FALSE; |
2064 | } |
2065 | |
2066 | static |
2067 | cmsBool 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 | |
2089 | static |
2090 | void *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 | |
2173 | Error: |
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 | |
2183 | static |
2184 | cmsBool 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 | |
2317 | static |
2318 | void* 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 | |
2326 | static |
2327 | void 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 | |
2343 | static |
2344 | cmsStage* 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 | |
2379 | static |
2380 | cmsStage* 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 | |
2443 | static |
2444 | cmsToneCurve* 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 |
2471 | static |
2472 | cmsStage* 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 | |
2495 | Error: |
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 | /* |
2514 | It is possible to use any or all of these processing elements. At least one processing element |
2515 | must be included.Only the following combinations are allowed: |
2516 | |
2517 | B |
2518 | M - Matrix - B |
2519 | A - CLUT - B |
2520 | A - CLUT - M - Matrix - B |
2521 | |
2522 | */ |
2523 | |
2524 | static |
2525 | void* 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; |
2585 | Error: |
2586 | cmsPipelineFree(ContextID, NewLUT); |
2587 | return NULL; |
2588 | |
2589 | cmsUNUSED_PARAMETER(SizeOfTag); |
2590 | } |
2591 | |
2592 | // Write a set of curves |
2593 | static |
2594 | cmsBool 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 |
2630 | static |
2631 | cmsBool 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 | |
2683 | static |
2684 | cmsBool 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 | |
2733 | static |
2734 | cmsBool 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 | |
2823 | static |
2824 | void* 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 | |
2832 | static |
2833 | void 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 | |
2844 | static |
2845 | void* 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; |
2906 | Error: |
2907 | cmsPipelineFree(ContextID, NewLUT); |
2908 | return NULL; |
2909 | |
2910 | cmsUNUSED_PARAMETER(SizeOfTag); |
2911 | } |
2912 | |
2913 | |
2914 | /* |
2915 | B |
2916 | B - Matrix - M |
2917 | B - CLUT - A |
2918 | B - Matrix - M - CLUT - A |
2919 | */ |
2920 | |
2921 | static |
2922 | cmsBool 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 | |
3006 | static |
3007 | void* 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 | |
3015 | static |
3016 | void 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 | /* |
3030 | The purpose of this tag is to identify the colorants used in the profile by a |
3031 | unique name and set of XYZ or L*a*b* values to give the colorant an unambiguous |
3032 | value. The first colorant listed is the colorant of the first device channel of |
3033 | a lut tag. The second colorant listed is the colorant of the second device channel |
3034 | of a lut tag, and so on. |
3035 | */ |
3036 | |
3037 | static |
3038 | void *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 | |
3069 | Error: |
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 |
3080 | static |
3081 | cmsBool 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 | |
3111 | static |
3112 | void* 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 | |
3122 | static |
3123 | void 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 | |
3147 | static |
3148 | void *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 | |
3200 | Error: |
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 |
3209 | static |
3210 | cmsBool 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 | |
3250 | static |
3251 | void* 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 | |
3262 | static |
3263 | void 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 | |
3283 | static |
3284 | cmsBool 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 | |
3317 | static |
3318 | void *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 | |
3365 | Error: |
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 |
3373 | static |
3374 | cmsBool 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 | |
3388 | static |
3389 | cmsBool 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 | |
3415 | static |
3416 | void* 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 | |
3424 | static |
3425 | void 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 | /* |
3438 | In certain workflows using ICC Device Link Profiles, it is necessary to identify the |
3439 | original profiles that were combined to create the Device Link Profile. |
3440 | This type is an array of structures, each of which contains information for |
3441 | identification of a profile used in a sequence |
3442 | */ |
3443 | |
3444 | |
3445 | static |
3446 | cmsBool 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 | |
3463 | static |
3464 | void *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 | |
3498 | static |
3499 | cmsBool 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 | |
3517 | static |
3518 | cmsBool 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 | |
3537 | static |
3538 | void* 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 | |
3546 | static |
3547 | void 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 | /* |
3560 | This type contains curves representing the under color removal and black |
3561 | generation and a text string which is a general description of the method used |
3562 | for the ucr/bg. |
3563 | */ |
3564 | |
3565 | static |
3566 | void *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 | |
3614 | static |
3615 | cmsBool 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 | |
3643 | static |
3644 | void* 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 | |
3661 | static |
3662 | void 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 | /* |
3679 | This type contains the PostScript product name to which this profile corresponds |
3680 | and the names of the companion CRDs. Recall that a single profile can generate |
3681 | multiple CRDs. It is implemented as a MLU being the language code "PS" and then |
3682 | country 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 |
3694 | static |
3695 | cmsBool 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 | |
3725 | static |
3726 | cmsBool 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 | |
3745 | static |
3746 | void *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 | |
3760 | Error: |
3761 | cmsMLUfree(ContextID, mlu); |
3762 | return NULL; |
3763 | |
3764 | } |
3765 | |
3766 | static |
3767 | cmsBool 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 | |
3780 | Error: |
3781 | return FALSE; |
3782 | |
3783 | cmsUNUSED_PARAMETER(nItems); |
3784 | } |
3785 | |
3786 | |
3787 | static |
3788 | void* 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 | |
3796 | static |
3797 | void 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 | |
3812 | static |
3813 | void *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 | |
3841 | Error: |
3842 | if (sc != NULL) |
3843 | _cmsFree(ContextID, sc); |
3844 | |
3845 | return NULL; |
3846 | cmsUNUSED_PARAMETER(self); |
3847 | cmsUNUSED_PARAMETER(SizeOfTag); |
3848 | } |
3849 | |
3850 | |
3851 | static |
3852 | cmsBool 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 | |
3874 | static |
3875 | void* 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 | |
3884 | static |
3885 | void 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 | |
3899 | static |
3900 | void *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 | |
3918 | Error: |
3919 | if (vc != NULL) |
3920 | _cmsFree(ContextID, vc); |
3921 | |
3922 | return NULL; |
3923 | |
3924 | cmsUNUSED_PARAMETER(SizeOfTag); |
3925 | } |
3926 | |
3927 | |
3928 | static |
3929 | cmsBool 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 | |
3944 | static |
3945 | void* 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 | |
3954 | static |
3955 | void 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 | |
3967 | static |
3968 | void* 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 | |
3976 | static |
3977 | void 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 |
3992 | static |
3993 | cmsToneCurve* 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 | |
4092 | Error: |
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 | |
4103 | static |
4104 | cmsBool 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 | |
4118 | static |
4119 | void *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 |
4160 | static |
4161 | cmsBool 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 | |
4220 | Error: |
4221 | return FALSE; |
4222 | } |
4223 | |
4224 | |
4225 | static |
4226 | cmsBool 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 |
4241 | static |
4242 | cmsBool 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 | |
4270 | static |
4271 | void *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 | |
4338 | static |
4339 | cmsBool 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 | |
4374 | static |
4375 | void *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 | |
4415 | Error: |
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 |
4424 | static |
4425 | cmsBool 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 |
4462 | static _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 | |
4474 | static |
4475 | cmsBool 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 |
4524 | static |
4525 | void *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 |
4558 | Error: |
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. |
4569 | static |
4570 | cmsBool 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 | |
4653 | Error: |
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 | |
4662 | static |
4663 | void* 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 | |
4671 | static |
4672 | void 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 |
4690 | typedef struct { |
4691 | double Gamma; |
4692 | double Min; |
4693 | double Max; |
4694 | } _cmsVCGTGAMMA; |
4695 | |
4696 | |
4697 | static |
4698 | void *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 |
4825 | Error: |
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 |
4836 | static |
4837 | cmsBool 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 | |
4888 | static |
4889 | void* 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 | |
4908 | static |
4909 | void 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 |
4922 | typedef struct { |
4923 | cmsContext ContextID; |
4924 | cmsUInt32Number *Offsets; |
4925 | cmsUInt32Number *Sizes; |
4926 | } _cmsDICelem; |
4927 | |
4928 | typedef struct { |
4929 | _cmsDICelem Name, Value, DisplayName, DisplayValue; |
4930 | |
4931 | } _cmsDICarray; |
4932 | |
4933 | // Allocate an empty array element |
4934 | static |
4935 | cmsBool 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 |
4951 | static |
4952 | void 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 |
4960 | static |
4961 | void 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 |
4971 | static |
4972 | cmsBool 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 | |
4990 | Error: |
4991 | FreeArray(ContextID, a); |
4992 | return FALSE; |
4993 | } |
4994 | |
4995 | // Read one element |
4996 | static |
4997 | cmsBool 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 | |
5009 | static |
5010 | cmsBool 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 |
5036 | static |
5037 | cmsBool 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 | |
5045 | static |
5046 | cmsBool 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 | |
5069 | static |
5070 | cmsBool 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 | |
5101 | static |
5102 | cmsUInt32Number 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 | |
5113 | static |
5114 | cmsBool 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 | |
5134 | static |
5135 | cmsBool 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 | |
5152 | static |
5153 | cmsBool 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 | |
5175 | static |
5176 | void *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 | |
5251 | Error: |
5252 | FreeArray(ContextID, &a); |
5253 | cmsDictFree(ContextID, hDict); |
5254 | return NULL; |
5255 | } |
5256 | |
5257 | |
5258 | static |
5259 | cmsBool 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 | |
5326 | Error: |
5327 | FreeArray(ContextID, &a); |
5328 | return FALSE; |
5329 | |
5330 | cmsUNUSED_PARAMETER(nItems); |
5331 | } |
5332 | |
5333 | |
5334 | static |
5335 | void* 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 | |
5344 | static |
5345 | void 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 |
5358 | static _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 |
5399 | static |
5400 | void 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 | |
5434 | void _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 | |
5448 | void _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 |
5465 | cmsBool _cmsRegisterTagTypePlugin(cmsContext ContextID, cmsPluginBase* Data) |
5466 | { |
5467 | return RegisterTypesPlugin(ContextID, Data, TagTypePlugin); |
5468 | } |
5469 | |
5470 | cmsBool _cmsRegisterMultiProcessElementPlugin(cmsContext ContextID, cmsPluginBase* Data) |
5471 | { |
5472 | return RegisterTypesPlugin(ContextID, Data,MPEPlugin); |
5473 | } |
5474 | |
5475 | |
5476 | // Wrapper for tag types |
5477 | cmsTagTypeHandler* _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 | |
5488 | typedef 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 |
5497 | static _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 |
5602 | static |
5603 | void 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 | |
5635 | void _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 | |
5649 | cmsBool _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 |
5674 | cmsTagDescriptor* _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 | |