| //--------------------------------------------------------------------------------- |
| // |
| // Little Color Management System |
| // Copyright (c) 1998-2017 Marti Maria Saguer |
| // |
| // Permission is hereby granted, free of charge, to any person obtaining |
| // a copy of this software and associated documentation files (the "Software"), |
| // to deal in the Software without restriction, including without limitation |
| // the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| // and/or sell copies of the Software, and to permit persons to whom the Software |
| // is furnished to do so, subject to the following conditions: |
| // |
| // The above copyright notice and this permission notice shall be included in |
| // all copies or substantial portions of the Software. |
| // |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO |
| // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
| // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
| // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
| // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| // |
| //--------------------------------------------------------------------------------- |
| // |
| |
| #include "lcms2_internal.h" |
| |
| // Tag Serialization ----------------------------------------------------------------------------- |
| // This file implements every single tag and tag type as described in the ICC spec. Some types |
| // have been deprecated, like ncl and Data. There is no implementation for those types as there |
| // are no profiles holding them. The programmer can also extend this list by defining his own types |
| // by using the appropriate plug-in. There are three types of plug ins regarding that. First type |
| // allows to define new tags using any existing type. Next plug-in type allows to define new types |
| // and the third one is very specific: allows to extend the number of elements in the multiprocessing |
| // elements special type. |
| //-------------------------------------------------------------------------------------------------- |
| |
| // Some broken types |
| #define cmsCorbisBrokenXYZtype ((cmsTagTypeSignature) 0x17A505B8) |
| #define cmsMonacoBrokenCurveType ((cmsTagTypeSignature) 0x9478ee00) |
| |
| // This is the linked list that keeps track of the defined types |
| typedef struct _cmsTagTypeLinkedList_st { |
| |
| cmsTagTypeHandler Handler; |
| struct _cmsTagTypeLinkedList_st* Next; |
| |
| } _cmsTagTypeLinkedList; |
| |
| // Some macros to define callbacks. |
| #define READ_FN(x) Type_##x##_Read |
| #define WRITE_FN(x) Type_##x##_Write |
| #define FREE_FN(x) Type_##x##_Free |
| #define DUP_FN(x) Type_##x##_Dup |
| |
| // Helper macro to define a handler. Callbacks do have a fixed naming convention. |
| #define TYPE_HANDLER(t, x) { (t), READ_FN(x), WRITE_FN(x), DUP_FN(x), FREE_FN(x), NULL, 0 } |
| |
| // Helper macro to define a MPE handler. Callbacks do have a fixed naming convention |
| #define TYPE_MPE_HANDLER(t, x) { (t), READ_FN(x), WRITE_FN(x), GenericMPEdup, GenericMPEfree, NULL, 0 } |
| |
| // Infinites |
| #define MINUS_INF (-1E22F) |
| #define PLUS_INF (+1E22F) |
| |
| |
| // Register a new type handler. This routine is shared between normal types and MPE. LinkedList points to the optional list head |
| static |
| cmsBool RegisterTypesPlugin(cmsContext id, cmsPluginBase* Data, _cmsMemoryClient pos) |
| { |
| cmsPluginTagType* Plugin = (cmsPluginTagType*) Data; |
| _cmsTagTypePluginChunkType* ctx = ( _cmsTagTypePluginChunkType*) _cmsContextGetClientChunk(id, pos); |
| _cmsTagTypeLinkedList *pt; |
| |
| // Calling the function with NULL as plug-in would unregister the plug in. |
| if (Data == NULL) { |
| |
| // There is no need to set free the memory, as pool is destroyed as a whole. |
| ctx ->TagTypes = NULL; |
| return TRUE; |
| } |
| |
| // Registering happens in plug-in memory pool. |
| pt = (_cmsTagTypeLinkedList*) _cmsPluginMalloc(id, sizeof(_cmsTagTypeLinkedList)); |
| if (pt == NULL) return FALSE; |
| |
| pt ->Handler = Plugin ->Handler; |
| pt ->Next = ctx ->TagTypes; |
| |
| ctx ->TagTypes = pt; |
| |
| return TRUE; |
| } |
| |
| // Return handler for a given type or NULL if not found. Shared between normal types and MPE. It first tries the additons |
| // made by plug-ins and then the built-in defaults. |
| static |
| cmsTagTypeHandler* GetHandler(cmsTagTypeSignature sig, _cmsTagTypeLinkedList* PluginLinkedList, _cmsTagTypeLinkedList* DefaultLinkedList) |
| { |
| _cmsTagTypeLinkedList* pt; |
| |
| for (pt = PluginLinkedList; |
| pt != NULL; |
| pt = pt ->Next) { |
| |
| if (sig == pt -> Handler.Signature) return &pt ->Handler; |
| } |
| |
| for (pt = DefaultLinkedList; |
| pt != NULL; |
| pt = pt ->Next) { |
| |
| if (sig == pt -> Handler.Signature) return &pt ->Handler; |
| } |
| |
| return NULL; |
| } |
| |
| |
| // Auxiliary to convert UTF-32 to UTF-16 in some cases |
| static |
| cmsBool _cmsWriteWCharArray(cmsIOHANDLER* io, cmsUInt32Number n, const wchar_t* Array) |
| { |
| cmsUInt32Number i; |
| |
| _cmsAssert(io != NULL); |
| _cmsAssert(!(Array == NULL && n > 0)); |
| |
| for (i=0; i < n; i++) { |
| if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) Array[i])) return FALSE; |
| } |
| |
| return TRUE; |
| } |
| |
| // Auxiliary to read an array of wchar_t |
| static |
| cmsBool _cmsReadWCharArray(cmsIOHANDLER* io, cmsUInt32Number n, wchar_t* Array) |
| { |
| cmsUInt32Number i; |
| cmsUInt16Number tmp; |
| |
| _cmsAssert(io != NULL); |
| |
| for (i=0; i < n; i++) { |
| |
| if (Array != NULL) { |
| |
| if (!_cmsReadUInt16Number(io, &tmp)) return FALSE; |
| Array[i] = (wchar_t) tmp; |
| } |
| else { |
| if (!_cmsReadUInt16Number(io, NULL)) return FALSE; |
| } |
| |
| } |
| return TRUE; |
| } |
| |
| // To deal with position tables |
| typedef cmsBool (* PositionTableEntryFn)(struct _cms_typehandler_struct* self, |
| cmsIOHANDLER* io, |
| void* Cargo, |
| cmsUInt32Number n, |
| cmsUInt32Number SizeOfTag); |
| |
| // Helper function to deal with position tables as described in ICC spec 4.3 |
| // A table of n elements is readed, where first comes n records containing offsets and sizes and |
| // then a block containing the data itself. This allows to reuse same data in more than one entry |
| static |
| cmsBool ReadPositionTable(struct _cms_typehandler_struct* self, |
| cmsIOHANDLER* io, |
| cmsUInt32Number Count, |
| cmsUInt32Number BaseOffset, |
| void *Cargo, |
| PositionTableEntryFn ElementFn) |
| { |
| cmsUInt32Number i; |
| cmsUInt32Number *ElementOffsets = NULL, *ElementSizes = NULL; |
| cmsUInt32Number currentPosition; |
| |
| currentPosition = io->Tell(io); |
| |
| // Verify there is enough space left to read at least two cmsUInt32Number items for Count items. |
| if (((io->ReportedSize - currentPosition) / (2 * sizeof(cmsUInt32Number))) < Count) |
| return FALSE; |
| |
| // Let's take the offsets to each element |
| ElementOffsets = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number)); |
| if (ElementOffsets == NULL) goto Error; |
| |
| ElementSizes = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number)); |
| if (ElementSizes == NULL) goto Error; |
| |
| for (i=0; i < Count; i++) { |
| |
| if (!_cmsReadUInt32Number(io, &ElementOffsets[i])) goto Error; |
| if (!_cmsReadUInt32Number(io, &ElementSizes[i])) goto Error; |
| |
| ElementOffsets[i] += BaseOffset; |
| } |
| |
| // Seek to each element and read it |
| for (i=0; i < Count; i++) { |
| |
| if (!io -> Seek(io, ElementOffsets[i])) goto Error; |
| |
| // This is the reader callback |
| if (!ElementFn(self, io, Cargo, i, ElementSizes[i])) goto Error; |
| } |
| |
| // Success |
| if (ElementOffsets != NULL) _cmsFree(io ->ContextID, ElementOffsets); |
| if (ElementSizes != NULL) _cmsFree(io ->ContextID, ElementSizes); |
| return TRUE; |
| |
| Error: |
| if (ElementOffsets != NULL) _cmsFree(io ->ContextID, ElementOffsets); |
| if (ElementSizes != NULL) _cmsFree(io ->ContextID, ElementSizes); |
| return FALSE; |
| } |
| |
| // Same as anterior, but for write position tables |
| static |
| cmsBool WritePositionTable(struct _cms_typehandler_struct* self, |
| cmsIOHANDLER* io, |
| cmsUInt32Number SizeOfTag, |
| cmsUInt32Number Count, |
| cmsUInt32Number BaseOffset, |
| void *Cargo, |
| PositionTableEntryFn ElementFn) |
| { |
| cmsUInt32Number i; |
| cmsUInt32Number DirectoryPos, CurrentPos, Before; |
| cmsUInt32Number *ElementOffsets = NULL, *ElementSizes = NULL; |
| |
| // Create table |
| ElementOffsets = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number)); |
| if (ElementOffsets == NULL) goto Error; |
| |
| ElementSizes = (cmsUInt32Number *) _cmsCalloc(io ->ContextID, Count, sizeof(cmsUInt32Number)); |
| if (ElementSizes == NULL) goto Error; |
| |
| // Keep starting position of curve offsets |
| DirectoryPos = io ->Tell(io); |
| |
| // Write a fake directory to be filled latter on |
| for (i=0; i < Count; i++) { |
| |
| if (!_cmsWriteUInt32Number(io, 0)) goto Error; // Offset |
| if (!_cmsWriteUInt32Number(io, 0)) goto Error; // size |
| } |
| |
| // Write each element. Keep track of the size as well. |
| for (i=0; i < Count; i++) { |
| |
| Before = io ->Tell(io); |
| ElementOffsets[i] = Before - BaseOffset; |
| |
| // Callback to write... |
| if (!ElementFn(self, io, Cargo, i, SizeOfTag)) goto Error; |
| |
| // Now the size |
| ElementSizes[i] = io ->Tell(io) - Before; |
| } |
| |
| // Write the directory |
| CurrentPos = io ->Tell(io); |
| if (!io ->Seek(io, DirectoryPos)) goto Error; |
| |
| for (i=0; i < Count; i++) { |
| if (!_cmsWriteUInt32Number(io, ElementOffsets[i])) goto Error; |
| if (!_cmsWriteUInt32Number(io, ElementSizes[i])) goto Error; |
| } |
| |
| if (!io ->Seek(io, CurrentPos)) goto Error; |
| |
| if (ElementOffsets != NULL) _cmsFree(io ->ContextID, ElementOffsets); |
| if (ElementSizes != NULL) _cmsFree(io ->ContextID, ElementSizes); |
| return TRUE; |
| |
| Error: |
| if (ElementOffsets != NULL) _cmsFree(io ->ContextID, ElementOffsets); |
| if (ElementSizes != NULL) _cmsFree(io ->ContextID, ElementSizes); |
| return FALSE; |
| } |
| |
| |
| // ******************************************************************************** |
| // Type XYZ. Only one value is allowed |
| // ******************************************************************************** |
| |
| //The XYZType contains an array of three encoded values for the XYZ tristimulus |
| //values. Tristimulus values must be non-negative. The signed encoding allows for |
| //implementation optimizations by minimizing the number of fixed formats. |
| |
| |
| static |
| void *Type_XYZ_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) |
| { |
| cmsCIEXYZ* xyz; |
| |
| *nItems = 0; |
| xyz = (cmsCIEXYZ*) _cmsMallocZero(self ->ContextID, sizeof(cmsCIEXYZ)); |
| if (xyz == NULL) return NULL; |
| |
| if (!_cmsReadXYZNumber(io, xyz)) { |
| _cmsFree(self ->ContextID, xyz); |
| return NULL; |
| } |
| |
| *nItems = 1; |
| return (void*) xyz; |
| |
| cmsUNUSED_PARAMETER(SizeOfTag); |
| } |
| |
| static |
| cmsBool Type_XYZ_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) |
| { |
| return _cmsWriteXYZNumber(io, (cmsCIEXYZ*) Ptr); |
| |
| cmsUNUSED_PARAMETER(nItems); |
| cmsUNUSED_PARAMETER(self); |
| } |
| |
| static |
| void* Type_XYZ_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) |
| { |
| return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsCIEXYZ)); |
| |
| cmsUNUSED_PARAMETER(n); |
| } |
| |
| static |
| void Type_XYZ_Free(struct _cms_typehandler_struct* self, void *Ptr) |
| { |
| _cmsFree(self ->ContextID, Ptr); |
| } |
| |
| |
| static |
| cmsTagTypeSignature DecideXYZtype(cmsFloat64Number ICCVersion, const void *Data) |
| { |
| return cmsSigXYZType; |
| |
| cmsUNUSED_PARAMETER(ICCVersion); |
| cmsUNUSED_PARAMETER(Data); |
| } |
| |
| |
| // ******************************************************************************** |
| // Type chromaticity. Only one value is allowed |
| // ******************************************************************************** |
| // The chromaticity tag type provides basic chromaticity data and type of |
| // phosphors or colorants of a monitor to applications and utilities. |
| |
| static |
| void *Type_Chromaticity_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) |
| { |
| cmsCIExyYTRIPLE* chrm; |
| cmsUInt16Number nChans, Table; |
| |
| *nItems = 0; |
| chrm = (cmsCIExyYTRIPLE*) _cmsMallocZero(self ->ContextID, sizeof(cmsCIExyYTRIPLE)); |
| if (chrm == NULL) return NULL; |
| |
| if (!_cmsReadUInt16Number(io, &nChans)) goto Error; |
| |
| // Let's recover from a bug introduced in early versions of lcms1 |
| if (nChans == 0 && SizeOfTag == 32) { |
| |
| if (!_cmsReadUInt16Number(io, NULL)) goto Error; |
| if (!_cmsReadUInt16Number(io, &nChans)) goto Error; |
| } |
| |
| if (nChans != 3) goto Error; |
| |
| if (!_cmsReadUInt16Number(io, &Table)) goto Error; |
| |
| if (!_cmsRead15Fixed16Number(io, &chrm ->Red.x)) goto Error; |
| if (!_cmsRead15Fixed16Number(io, &chrm ->Red.y)) goto Error; |
| |
| chrm ->Red.Y = 1.0; |
| |
| if (!_cmsRead15Fixed16Number(io, &chrm ->Green.x)) goto Error; |
| if (!_cmsRead15Fixed16Number(io, &chrm ->Green.y)) goto Error; |
| |
| chrm ->Green.Y = 1.0; |
| |
| if (!_cmsRead15Fixed16Number(io, &chrm ->Blue.x)) goto Error; |
| if (!_cmsRead15Fixed16Number(io, &chrm ->Blue.y)) goto Error; |
| |
| chrm ->Blue.Y = 1.0; |
| |
| *nItems = 1; |
| return (void*) chrm; |
| |
| Error: |
| _cmsFree(self ->ContextID, (void*) chrm); |
| return NULL; |
| |
| cmsUNUSED_PARAMETER(SizeOfTag); |
| } |
| |
| static |
| cmsBool SaveOneChromaticity(cmsFloat64Number x, cmsFloat64Number y, cmsIOHANDLER* io) |
| { |
| if (!_cmsWriteUInt32Number(io, (cmsUInt32Number) _cmsDoubleTo15Fixed16(x))) return FALSE; |
| if (!_cmsWriteUInt32Number(io, (cmsUInt32Number) _cmsDoubleTo15Fixed16(y))) return FALSE; |
| |
| return TRUE; |
| } |
| |
| static |
| cmsBool Type_Chromaticity_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) |
| { |
| cmsCIExyYTRIPLE* chrm = (cmsCIExyYTRIPLE*) Ptr; |
| |
| if (!_cmsWriteUInt16Number(io, 3)) return FALSE; // nChannels |
| if (!_cmsWriteUInt16Number(io, 0)) return FALSE; // Table |
| |
| if (!SaveOneChromaticity(chrm -> Red.x, chrm -> Red.y, io)) return FALSE; |
| if (!SaveOneChromaticity(chrm -> Green.x, chrm -> Green.y, io)) return FALSE; |
| if (!SaveOneChromaticity(chrm -> Blue.x, chrm -> Blue.y, io)) return FALSE; |
| |
| return TRUE; |
| |
| cmsUNUSED_PARAMETER(nItems); |
| cmsUNUSED_PARAMETER(self); |
| } |
| |
| static |
| void* Type_Chromaticity_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) |
| { |
| return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsCIExyYTRIPLE)); |
| |
| cmsUNUSED_PARAMETER(n); |
| } |
| |
| static |
| void Type_Chromaticity_Free(struct _cms_typehandler_struct* self, void* Ptr) |
| { |
| _cmsFree(self ->ContextID, Ptr); |
| } |
| |
| |
| // ******************************************************************************** |
| // Type cmsSigColorantOrderType |
| // ******************************************************************************** |
| |
| // This is an optional tag which specifies the laydown order in which colorants will |
| // be printed on an n-colorant device. The laydown order may be the same as the |
| // channel generation order listed in the colorantTableTag or the channel order of a |
| // colour space such as CMYK, in which case this tag is not needed. When this is not |
| // the case (for example, ink-towers sometimes use the order KCMY), this tag may be |
| // used to specify the laydown order of the colorants. |
| |
| |
| static |
| void *Type_ColorantOrderType_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) |
| { |
| cmsUInt8Number* ColorantOrder; |
| cmsUInt32Number Count; |
| |
| *nItems = 0; |
| if (!_cmsReadUInt32Number(io, &Count)) return NULL; |
| if (Count > cmsMAXCHANNELS) return NULL; |
| |
| ColorantOrder = (cmsUInt8Number*) _cmsCalloc(self ->ContextID, cmsMAXCHANNELS, sizeof(cmsUInt8Number)); |
| if (ColorantOrder == NULL) return NULL; |
| |
| // We use FF as end marker |
| memset(ColorantOrder, 0xFF, cmsMAXCHANNELS * sizeof(cmsUInt8Number)); |
| |
| if (io ->Read(io, ColorantOrder, sizeof(cmsUInt8Number), Count) != Count) { |
| |
| _cmsFree(self ->ContextID, (void*) ColorantOrder); |
| return NULL; |
| } |
| |
| *nItems = 1; |
| return (void*) ColorantOrder; |
| |
| cmsUNUSED_PARAMETER(SizeOfTag); |
| } |
| |
| static |
| cmsBool Type_ColorantOrderType_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) |
| { |
| cmsUInt8Number* ColorantOrder = (cmsUInt8Number*) Ptr; |
| cmsUInt32Number i, sz, Count; |
| |
| // Get the length |
| for (Count=i=0; i < cmsMAXCHANNELS; i++) { |
| if (ColorantOrder[i] != 0xFF) Count++; |
| } |
| |
| if (!_cmsWriteUInt32Number(io, Count)) return FALSE; |
| |
| sz = Count * sizeof(cmsUInt8Number); |
| if (!io -> Write(io, sz, ColorantOrder)) return FALSE; |
| |
| return TRUE; |
| |
| cmsUNUSED_PARAMETER(nItems); |
| cmsUNUSED_PARAMETER(self); |
| } |
| |
| static |
| void* Type_ColorantOrderType_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) |
| { |
| return _cmsDupMem(self ->ContextID, Ptr, cmsMAXCHANNELS * sizeof(cmsUInt8Number)); |
| |
| cmsUNUSED_PARAMETER(n); |
| } |
| |
| |
| static |
| void Type_ColorantOrderType_Free(struct _cms_typehandler_struct* self, void* Ptr) |
| { |
| _cmsFree(self ->ContextID, Ptr); |
| } |
| |
| // ******************************************************************************** |
| // Type cmsSigS15Fixed16ArrayType |
| // ******************************************************************************** |
| // This type represents an array of generic 4-byte/32-bit fixed point quantity. |
| // The number of values is determined from the size of the tag. |
| |
| static |
| void *Type_S15Fixed16_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) |
| { |
| cmsFloat64Number* array_double; |
| cmsUInt32Number i, n; |
| |
| *nItems = 0; |
| n = SizeOfTag / sizeof(cmsUInt32Number); |
| array_double = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, n, sizeof(cmsFloat64Number)); |
| if (array_double == NULL) return NULL; |
| |
| for (i=0; i < n; i++) { |
| |
| if (!_cmsRead15Fixed16Number(io, &array_double[i])) { |
| |
| _cmsFree(self ->ContextID, array_double); |
| return NULL; |
| } |
| } |
| |
| *nItems = n; |
| return (void*) array_double; |
| } |
| |
| static |
| cmsBool Type_S15Fixed16_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) |
| { |
| cmsFloat64Number* Value = (cmsFloat64Number*) Ptr; |
| cmsUInt32Number i; |
| |
| for (i=0; i < nItems; i++) { |
| |
| if (!_cmsWrite15Fixed16Number(io, Value[i])) return FALSE; |
| } |
| |
| return TRUE; |
| |
| cmsUNUSED_PARAMETER(self); |
| } |
| |
| static |
| void* Type_S15Fixed16_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) |
| { |
| return _cmsDupMem(self ->ContextID, Ptr, n * sizeof(cmsFloat64Number)); |
| } |
| |
| |
| static |
| void Type_S15Fixed16_Free(struct _cms_typehandler_struct* self, void* Ptr) |
| { |
| _cmsFree(self ->ContextID, Ptr); |
| } |
| |
| // ******************************************************************************** |
| // Type cmsSigU16Fixed16ArrayType |
| // ******************************************************************************** |
| // This type represents an array of generic 4-byte/32-bit quantity. |
| // The number of values is determined from the size of the tag. |
| |
| |
| static |
| void *Type_U16Fixed16_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) |
| { |
| cmsFloat64Number* array_double; |
| cmsUInt32Number v; |
| cmsUInt32Number i, n; |
| |
| *nItems = 0; |
| n = SizeOfTag / sizeof(cmsUInt32Number); |
| array_double = (cmsFloat64Number*) _cmsCalloc(self ->ContextID, n, sizeof(cmsFloat64Number)); |
| if (array_double == NULL) return NULL; |
| |
| for (i=0; i < n; i++) { |
| |
| if (!_cmsReadUInt32Number(io, &v)) { |
| _cmsFree(self ->ContextID, (void*) array_double); |
| return NULL; |
| } |
| |
| // Convert to cmsFloat64Number |
| array_double[i] = (cmsFloat64Number) (v / 65536.0); |
| } |
| |
| *nItems = n; |
| return (void*) array_double; |
| } |
| |
| static |
| cmsBool Type_U16Fixed16_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) |
| { |
| cmsFloat64Number* Value = (cmsFloat64Number*) Ptr; |
| cmsUInt32Number i; |
| |
| for (i=0; i < nItems; i++) { |
| |
| cmsUInt32Number v = (cmsUInt32Number) floor(Value[i]*65536.0 + 0.5); |
| |
| if (!_cmsWriteUInt32Number(io, v)) return FALSE; |
| } |
| |
| return TRUE; |
| |
| cmsUNUSED_PARAMETER(self); |
| } |
| |
| |
| static |
| void* Type_U16Fixed16_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) |
| { |
| return _cmsDupMem(self ->ContextID, Ptr, n * sizeof(cmsFloat64Number)); |
| } |
| |
| static |
| void Type_U16Fixed16_Free(struct _cms_typehandler_struct* self, void* Ptr) |
| { |
| _cmsFree(self ->ContextID, Ptr); |
| } |
| |
| // ******************************************************************************** |
| // Type cmsSigSignatureType |
| // ******************************************************************************** |
| // |
| // The signatureType contains a four-byte sequence, Sequences of less than four |
| // characters are padded at the end with spaces, 20h. |
| // Typically this type is used for registered tags that can be displayed on many |
| // development systems as a sequence of four characters. |
| |
| static |
| void *Type_Signature_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) |
| { |
| cmsSignature* SigPtr = (cmsSignature*) _cmsMalloc(self ->ContextID, sizeof(cmsSignature)); |
| if (SigPtr == NULL) return NULL; |
| |
| if (!_cmsReadUInt32Number(io, SigPtr)) return NULL; |
| *nItems = 1; |
| |
| return SigPtr; |
| |
| cmsUNUSED_PARAMETER(SizeOfTag); |
| } |
| |
| static |
| cmsBool Type_Signature_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) |
| { |
| cmsSignature* SigPtr = (cmsSignature*) Ptr; |
| |
| return _cmsWriteUInt32Number(io, *SigPtr); |
| |
| cmsUNUSED_PARAMETER(nItems); |
| cmsUNUSED_PARAMETER(self); |
| } |
| |
| static |
| void* Type_Signature_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) |
| { |
| return _cmsDupMem(self ->ContextID, Ptr, n * sizeof(cmsSignature)); |
| } |
| |
| static |
| void Type_Signature_Free(struct _cms_typehandler_struct* self, void* Ptr) |
| { |
| _cmsFree(self ->ContextID, Ptr); |
| } |
| |
| |
| // ******************************************************************************** |
| // Type cmsSigTextType |
| // ******************************************************************************** |
| // |
| // The textType is a simple text structure that contains a 7-bit ASCII text string. |
| // The length of the string is obtained by subtracting 8 from the element size portion |
| // of the tag itself. This string must be terminated with a 00h byte. |
| |
| static |
| void *Type_Text_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) |
| { |
| char* Text = NULL; |
| cmsMLU* mlu = NULL; |
| |
| // Create a container |
| mlu = cmsMLUalloc(self ->ContextID, 1); |
| if (mlu == NULL) return NULL; |
| |
| *nItems = 0; |
| |
| // We need to store the "\0" at the end, so +1 |
| if (SizeOfTag == UINT_MAX) goto Error; |
| |
| Text = (char*) _cmsMalloc(self ->ContextID, SizeOfTag + 1); |
| if (Text == NULL) goto Error; |
| |
| if (io -> Read(io, Text, sizeof(char), SizeOfTag) != SizeOfTag) goto Error; |
| |
| // Make sure text is properly ended |
| Text[SizeOfTag] = 0; |
| *nItems = 1; |
| |
| // Keep the result |
| if (!cmsMLUsetASCII(mlu, cmsNoLanguage, cmsNoCountry, Text)) goto Error; |
| |
| _cmsFree(self ->ContextID, Text); |
| return (void*) mlu; |
| |
| Error: |
| if (mlu != NULL) |
| cmsMLUfree(mlu); |
| if (Text != NULL) |
| _cmsFree(self ->ContextID, Text); |
| |
| return NULL; |
| } |
| |
| // The conversion implies to choose a language. So, we choose the actual language. |
| static |
| cmsBool Type_Text_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) |
| { |
| cmsMLU* mlu = (cmsMLU*) Ptr; |
| cmsUInt32Number size; |
| cmsBool rc; |
| char* Text; |
| |
| // Get the size of the string. Note there is an extra "\0" at the end |
| size = cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, NULL, 0); |
| if (size == 0) return FALSE; // Cannot be zero! |
| |
| // Create memory |
| Text = (char*) _cmsMalloc(self ->ContextID, size); |
| if (Text == NULL) return FALSE; |
| |
| cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, Text, size); |
| |
| // Write it, including separator |
| rc = io ->Write(io, size, Text); |
| |
| _cmsFree(self ->ContextID, Text); |
| return rc; |
| |
| cmsUNUSED_PARAMETER(nItems); |
| } |
| |
| static |
| void* Type_Text_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) |
| { |
| return (void*) cmsMLUdup((cmsMLU*) Ptr); |
| |
| cmsUNUSED_PARAMETER(n); |
| cmsUNUSED_PARAMETER(self); |
| } |
| |
| |
| static |
| void Type_Text_Free(struct _cms_typehandler_struct* self, void* Ptr) |
| { |
| cmsMLU* mlu = (cmsMLU*) Ptr; |
| cmsMLUfree(mlu); |
| return; |
| |
| cmsUNUSED_PARAMETER(self); |
| } |
| |
| static |
| cmsTagTypeSignature DecideTextType(cmsFloat64Number ICCVersion, const void *Data) |
| { |
| if (ICCVersion >= 4.0) |
| return cmsSigMultiLocalizedUnicodeType; |
| |
| return cmsSigTextType; |
| |
| cmsUNUSED_PARAMETER(Data); |
| } |
| |
| |
| // ******************************************************************************** |
| // Type cmsSigDataType |
| // ******************************************************************************** |
| |
| // General purpose data type |
| static |
| void *Type_Data_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) |
| { |
| cmsICCData* BinData; |
| cmsUInt32Number LenOfData; |
| |
| *nItems = 0; |
| |
| if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL; |
| |
| LenOfData = SizeOfTag - sizeof(cmsUInt32Number); |
| if (LenOfData > INT_MAX) return NULL; |
| |
| BinData = (cmsICCData*) _cmsMalloc(self ->ContextID, sizeof(cmsICCData) + LenOfData - 1); |
| if (BinData == NULL) return NULL; |
| |
| BinData ->len = LenOfData; |
| if (!_cmsReadUInt32Number(io, &BinData->flag)) { |
| _cmsFree(self ->ContextID, BinData); |
| return NULL; |
| } |
| |
| if (io -> Read(io, BinData ->data, sizeof(cmsUInt8Number), LenOfData) != LenOfData) { |
| |
| _cmsFree(self ->ContextID, BinData); |
| return NULL; |
| } |
| |
| *nItems = 1; |
| |
| return (void*) BinData; |
| } |
| |
| |
| static |
| cmsBool Type_Data_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) |
| { |
| cmsICCData* BinData = (cmsICCData*) Ptr; |
| |
| if (!_cmsWriteUInt32Number(io, BinData ->flag)) return FALSE; |
| |
| return io ->Write(io, BinData ->len, BinData ->data); |
| |
| cmsUNUSED_PARAMETER(nItems); |
| cmsUNUSED_PARAMETER(self); |
| } |
| |
| |
| static |
| void* Type_Data_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) |
| { |
| cmsICCData* BinData = (cmsICCData*) Ptr; |
| |
| return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsICCData) + BinData ->len - 1); |
| |
| cmsUNUSED_PARAMETER(n); |
| } |
| |
| static |
| void Type_Data_Free(struct _cms_typehandler_struct* self, void* Ptr) |
| { |
| _cmsFree(self ->ContextID, Ptr); |
| } |
| |
| // ******************************************************************************** |
| // Type cmsSigTextDescriptionType |
| // ******************************************************************************** |
| |
| static |
| void *Type_Text_Description_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) |
| { |
| char* Text = NULL; |
| cmsMLU* mlu = NULL; |
| cmsUInt32Number AsciiCount; |
| cmsUInt32Number i, UnicodeCode, UnicodeCount; |
| cmsUInt16Number ScriptCodeCode, Dummy; |
| cmsUInt8Number ScriptCodeCount; |
| |
| *nItems = 0; |
| |
| // One dword should be there |
| if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL; |
| |
| // Read len of ASCII |
| if (!_cmsReadUInt32Number(io, &AsciiCount)) return NULL; |
| SizeOfTag -= sizeof(cmsUInt32Number); |
| |
| // Check for size |
| if (SizeOfTag < AsciiCount) return NULL; |
| |
| // All seems Ok, allocate the container |
| mlu = cmsMLUalloc(self ->ContextID, 1); |
| if (mlu == NULL) return NULL; |
| |
| // As many memory as size of tag |
| Text = (char*) _cmsMalloc(self ->ContextID, AsciiCount + 1); |
| if (Text == NULL) goto Error; |
| |
| // Read it |
| if (io ->Read(io, Text, sizeof(char), AsciiCount) != AsciiCount) goto Error; |
| SizeOfTag -= AsciiCount; |
| |
| // Make sure there is a terminator |
| Text[AsciiCount] = 0; |
| |
| // Set the MLU entry. From here we can be tolerant to wrong types |
| if (!cmsMLUsetASCII(mlu, cmsNoLanguage, cmsNoCountry, Text)) goto Error; |
| _cmsFree(self ->ContextID, (void*) Text); |
| Text = NULL; |
| |
| // Skip Unicode code |
| if (SizeOfTag < 2* sizeof(cmsUInt32Number)) goto Done; |
| if (!_cmsReadUInt32Number(io, &UnicodeCode)) goto Done; |
| if (!_cmsReadUInt32Number(io, &UnicodeCount)) goto Done; |
| SizeOfTag -= 2* sizeof(cmsUInt32Number); |
| |
| if (SizeOfTag < UnicodeCount*sizeof(cmsUInt16Number)) goto Done; |
| |
| for (i=0; i < UnicodeCount; i++) { |
| if (!io ->Read(io, &Dummy, sizeof(cmsUInt16Number), 1)) goto Done; |
| } |
| SizeOfTag -= UnicodeCount*sizeof(cmsUInt16Number); |
| |
| // Skip ScriptCode code if present. Some buggy profiles does have less |
| // data that stricttly required. We need to skip it as this type may come |
| // embedded in other types. |
| |
| if (SizeOfTag >= sizeof(cmsUInt16Number) + sizeof(cmsUInt8Number) + 67) { |
| |
| if (!_cmsReadUInt16Number(io, &ScriptCodeCode)) goto Done; |
| if (!_cmsReadUInt8Number(io, &ScriptCodeCount)) goto Done; |
| |
| // Skip rest of tag |
| for (i=0; i < 67; i++) { |
| if (!io ->Read(io, &Dummy, sizeof(cmsUInt8Number), 1)) goto Error; |
| } |
| } |
| |
| Done: |
| |
| *nItems = 1; |
| return mlu; |
| |
| Error: |
| if (Text) _cmsFree(self ->ContextID, (void*) Text); |
| if (mlu) cmsMLUfree(mlu); |
| return NULL; |
| } |
| |
| |
| // This tag can come IN UNALIGNED SIZE. In order to prevent issues, we force zeros on description to align it |
| static |
| cmsBool Type_Text_Description_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) |
| { |
| cmsMLU* mlu = (cmsMLU*) Ptr; |
| char *Text = NULL; |
| wchar_t *Wide = NULL; |
| cmsUInt32Number len, len_text, len_tag_requirement, len_aligned; |
| cmsBool rc = FALSE; |
| char Filler[68]; |
| |
| // Used below for writing zeroes |
| memset(Filler, 0, sizeof(Filler)); |
| |
| // Get the len of string |
| len = cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, NULL, 0); |
| |
| // Specification ICC.1:2001-04 (v2.4.0): It has been found that textDescriptionType can contain misaligned data |
| //(see clause 4.1 for the definition of "aligned"). Because the Unicode language |
| // code and Unicode count immediately follow the ASCII description, their |
| // alignment is not correct if the ASCII count is not a multiple of four. The |
| // ScriptCode code is misaligned when the ASCII count is odd. Profile reading and |
| // writing software must be written carefully in order to handle these alignment |
| // problems. |
| // |
| // The above last sentence suggest to handle alignment issues in the |
| // parser. The provided example (Table 69 on Page 60) makes this clear. |
| // The padding only in the ASCII count is not sufficient for a aligned tag |
| // size, with the same text size in ASCII and Unicode. |
| |
| // Null strings |
| if (len <= 0) { |
| |
| Text = (char*) _cmsDupMem(self ->ContextID, "", sizeof(char)); |
| Wide = (wchar_t*) _cmsDupMem(self ->ContextID, L"", sizeof(wchar_t)); |
| } |
| else { |
| // Create independent buffers |
| Text = (char*) _cmsCalloc(self ->ContextID, len, sizeof(char)); |
| if (Text == NULL) goto Error; |
| |
| Wide = (wchar_t*) _cmsCalloc(self ->ContextID, len, sizeof(wchar_t)); |
| if (Wide == NULL) goto Error; |
| |
| // Get both representations. |
| cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, Text, len * sizeof(char)); |
| cmsMLUgetWide(mlu, cmsNoLanguage, cmsNoCountry, Wide, len * sizeof(wchar_t)); |
| } |
| |
| // Tell the real text len including the null terminator and padding |
| len_text = (cmsUInt32Number) strlen(Text) + 1; |
| // Compute an total tag size requirement |
| len_tag_requirement = (8+4+len_text+4+4+2*len_text+2+1+67); |
| len_aligned = _cmsALIGNLONG(len_tag_requirement); |
| |
| // * cmsUInt32Number count; * Description length |
| // * cmsInt8Number desc[count] * NULL terminated ascii string |
| // * cmsUInt32Number ucLangCode; * UniCode language code |
| // * cmsUInt32Number ucCount; * UniCode description length |
| // * cmsInt16Number ucDesc[ucCount];* The UniCode description |
| // * cmsUInt16Number scCode; * ScriptCode code |
| // * cmsUInt8Number scCount; * ScriptCode count |
| // * cmsInt8Number scDesc[67]; * ScriptCode Description |
| |
| if (!_cmsWriteUInt32Number(io, len_text)) goto Error; |
| if (!io ->Write(io, len_text, Text)) goto Error; |
| |
| if (!_cmsWriteUInt32Number(io, 0)) goto Error; // ucLanguageCode |
| |
| if (!_cmsWriteUInt32Number(io, len_text)) goto Error; |
| // Note that in some compilers sizeof(cmsUInt16Number) != sizeof(wchar_t) |
| if (!_cmsWriteWCharArray(io, len_text, Wide)) goto Error; |
| |
| // ScriptCode Code & count (unused) |
| if (!_cmsWriteUInt16Number(io, 0)) goto Error; |
| if (!_cmsWriteUInt8Number(io, 0)) goto Error; |
| |
| if (!io ->Write(io, 67, Filler)) goto Error; |
| |
| // possibly add pad at the end of tag |
| if(len_aligned - len_tag_requirement > 0) |
| if (!io ->Write(io, len_aligned - len_tag_requirement, Filler)) goto Error; |
| |
| rc = TRUE; |
| |
| Error: |
| if (Text) _cmsFree(self ->ContextID, Text); |
| if (Wide) _cmsFree(self ->ContextID, Wide); |
| |
| return rc; |
| |
| cmsUNUSED_PARAMETER(nItems); |
| } |
| |
| |
| static |
| void* Type_Text_Description_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) |
| { |
| return (void*) cmsMLUdup((cmsMLU*) Ptr); |
| |
| cmsUNUSED_PARAMETER(n); |
| cmsUNUSED_PARAMETER(self); |
| } |
| |
| static |
| void Type_Text_Description_Free(struct _cms_typehandler_struct* self, void* Ptr) |
| { |
| cmsMLU* mlu = (cmsMLU*) Ptr; |
| |
| cmsMLUfree(mlu); |
| return; |
| |
| cmsUNUSED_PARAMETER(self); |
| } |
| |
| |
| static |
| cmsTagTypeSignature DecideTextDescType(cmsFloat64Number ICCVersion, const void *Data) |
| { |
| if (ICCVersion >= 4.0) |
| return cmsSigMultiLocalizedUnicodeType; |
| |
| return cmsSigTextDescriptionType; |
| |
| cmsUNUSED_PARAMETER(Data); |
| } |
| |
| |
| // ******************************************************************************** |
| // Type cmsSigCurveType |
| // ******************************************************************************** |
| |
| static |
| void *Type_Curve_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) |
| { |
| cmsUInt32Number Count; |
| cmsToneCurve* NewGamma; |
| |
| *nItems = 0; |
| if (!_cmsReadUInt32Number(io, &Count)) return NULL; |
| |
| switch (Count) { |
| |
| case 0: // Linear. |
| { |
| cmsFloat64Number SingleGamma = 1.0; |
| |
| NewGamma = cmsBuildParametricToneCurve(self ->ContextID, 1, &SingleGamma); |
| if (!NewGamma) return NULL; |
| *nItems = 1; |
| return NewGamma; |
| } |
| |
| case 1: // Specified as the exponent of gamma function |
| { |
| cmsUInt16Number SingleGammaFixed; |
| cmsFloat64Number SingleGamma; |
| |
| if (!_cmsReadUInt16Number(io, &SingleGammaFixed)) return NULL; |
| SingleGamma = _cms8Fixed8toDouble(SingleGammaFixed); |
| |
| *nItems = 1; |
| return cmsBuildParametricToneCurve(self ->ContextID, 1, &SingleGamma); |
| } |
| |
| default: // Curve |
| |
| if (Count > 0x7FFF) |
| return NULL; // This is to prevent bad guys for doing bad things |
| |
| NewGamma = cmsBuildTabulatedToneCurve16(self ->ContextID, Count, NULL); |
| if (!NewGamma) return NULL; |
| |
| if (!_cmsReadUInt16Array(io, Count, NewGamma -> Table16)) { |
| cmsFreeToneCurve(NewGamma); |
| return NULL; |
| } |
| |
| *nItems = 1; |
| return NewGamma; |
| } |
| |
| cmsUNUSED_PARAMETER(SizeOfTag); |
| } |
| |
| |
| static |
| cmsBool Type_Curve_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) |
| { |
| cmsToneCurve* Curve = (cmsToneCurve*) Ptr; |
| |
| if (Curve ->nSegments == 1 && Curve ->Segments[0].Type == 1) { |
| |
| // Single gamma, preserve number |
| cmsUInt16Number SingleGammaFixed = _cmsDoubleTo8Fixed8(Curve ->Segments[0].Params[0]); |
| |
| if (!_cmsWriteUInt32Number(io, 1)) return FALSE; |
| if (!_cmsWriteUInt16Number(io, SingleGammaFixed)) return FALSE; |
| return TRUE; |
| |
| } |
| |
| if (!_cmsWriteUInt32Number(io, Curve ->nEntries)) return FALSE; |
| return _cmsWriteUInt16Array(io, Curve ->nEntries, Curve ->Table16); |
| |
| cmsUNUSED_PARAMETER(nItems); |
| cmsUNUSED_PARAMETER(self); |
| } |
| |
| |
| static |
| void* Type_Curve_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) |
| { |
| return (void*) cmsDupToneCurve((cmsToneCurve*) Ptr); |
| |
| cmsUNUSED_PARAMETER(n); |
| cmsUNUSED_PARAMETER(self); |
| } |
| |
| static |
| void Type_Curve_Free(struct _cms_typehandler_struct* self, void* Ptr) |
| { |
| cmsToneCurve* gamma = (cmsToneCurve*) Ptr; |
| |
| cmsFreeToneCurve(gamma); |
| return; |
| |
| cmsUNUSED_PARAMETER(self); |
| } |
| |
| |
| // ******************************************************************************** |
| // Type cmsSigParametricCurveType |
| // ******************************************************************************** |
| |
| |
| // Decide which curve type to use on writing |
| static |
| cmsTagTypeSignature DecideCurveType(cmsFloat64Number ICCVersion, const void *Data) |
| { |
| cmsToneCurve* Curve = (cmsToneCurve*) Data; |
| |
| if (ICCVersion < 4.0) return cmsSigCurveType; |
| if (Curve ->nSegments != 1) return cmsSigCurveType; // Only 1-segment curves can be saved as parametric |
| if (Curve ->Segments[0].Type < 0) return cmsSigCurveType; // Only non-inverted curves |
| if (Curve ->Segments[0].Type > 5) return cmsSigCurveType; // Only ICC parametric curves |
| |
| return cmsSigParametricCurveType; |
| } |
| |
| static |
| void *Type_ParametricCurve_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) |
| { |
| static const int ParamsByType[] = { 1, 3, 4, 5, 7 }; |
| cmsFloat64Number Params[10]; |
| cmsUInt16Number Type; |
| int i, n; |
| cmsToneCurve* NewGamma; |
| |
| if (!_cmsReadUInt16Number(io, &Type)) return NULL; |
| if (!_cmsReadUInt16Number(io, NULL)) return NULL; // Reserved |
| |
| if (Type > 4) { |
| |
| cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown parametric curve type '%d'", Type); |
| return NULL; |
| } |
| |
| memset(Params, 0, sizeof(Params)); |
| n = ParamsByType[Type]; |
| |
| for (i=0; i < n; i++) { |
| |
| if (!_cmsRead15Fixed16Number(io, &Params[i])) return NULL; |
| } |
| |
| NewGamma = cmsBuildParametricToneCurve(self ->ContextID, Type+1, Params); |
| |
| *nItems = 1; |
| return NewGamma; |
| |
| cmsUNUSED_PARAMETER(SizeOfTag); |
| } |
| |
| |
| static |
| cmsBool Type_ParametricCurve_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) |
| { |
| cmsToneCurve* Curve = (cmsToneCurve*) Ptr; |
| int i, nParams, typen; |
| static const int ParamsByType[] = { 0, 1, 3, 4, 5, 7 }; |
| |
| typen = Curve -> Segments[0].Type; |
| |
| if (Curve ->nSegments > 1 || typen < 1) { |
| |
| cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Multisegment or Inverted parametric curves cannot be written"); |
| return FALSE; |
| } |
| |
| if (typen > 5) { |
| cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported parametric curve"); |
| return FALSE; |
| } |
| |
| nParams = ParamsByType[typen]; |
| |
| if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) (Curve ->Segments[0].Type - 1))) return FALSE; |
| if (!_cmsWriteUInt16Number(io, 0)) return FALSE; // Reserved |
| |
| for (i=0; i < nParams; i++) { |
| |
| if (!_cmsWrite15Fixed16Number(io, Curve -> Segments[0].Params[i])) return FALSE; |
| } |
| |
| return TRUE; |
| |
| cmsUNUSED_PARAMETER(nItems); |
| } |
| |
| static |
| void* Type_ParametricCurve_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) |
| { |
| return (void*) cmsDupToneCurve((cmsToneCurve*) Ptr); |
| |
| cmsUNUSED_PARAMETER(n); |
| cmsUNUSED_PARAMETER(self); |
| } |
| |
| static |
| void Type_ParametricCurve_Free(struct _cms_typehandler_struct* self, void* Ptr) |
| { |
| cmsToneCurve* gamma = (cmsToneCurve*) Ptr; |
| |
| cmsFreeToneCurve(gamma); |
| return; |
| |
| cmsUNUSED_PARAMETER(self); |
| } |
| |
| |
| // ******************************************************************************** |
| // Type cmsSigDateTimeType |
| // ******************************************************************************** |
| |
| // A 12-byte value representation of the time and date, where the byte usage is assigned |
| // as specified in table 1. The actual values are encoded as 16-bit unsigned integers |
| // (uInt16Number - see 5.1.6). |
| // |
| // All the dateTimeNumber values in a profile shall be in Coordinated Universal Time |
| // (UTC, also known as GMT or ZULU Time). Profile writers are required to convert local |
| // time to UTC when setting these values. Programmes that display these values may show |
| // the dateTimeNumber as UTC, show the equivalent local time (at current locale), or |
| // display both UTC and local versions of the dateTimeNumber. |
| |
| static |
| void *Type_DateTime_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) |
| { |
| cmsDateTimeNumber timestamp; |
| struct tm * NewDateTime; |
| |
| *nItems = 0; |
| NewDateTime = (struct tm*) _cmsMalloc(self ->ContextID, sizeof(struct tm)); |
| if (NewDateTime == NULL) return NULL; |
| |
| if (io->Read(io, ×tamp, sizeof(cmsDateTimeNumber), 1) != 1) return NULL; |
| |
| _cmsDecodeDateTimeNumber(×tamp, NewDateTime); |
| |
| *nItems = 1; |
| return NewDateTime; |
| |
| cmsUNUSED_PARAMETER(SizeOfTag); |
| } |
| |
| |
| static |
| cmsBool Type_DateTime_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) |
| { |
| struct tm * DateTime = (struct tm*) Ptr; |
| cmsDateTimeNumber timestamp; |
| |
| _cmsEncodeDateTimeNumber(×tamp, DateTime); |
| if (!io ->Write(io, sizeof(cmsDateTimeNumber), ×tamp)) return FALSE; |
| |
| return TRUE; |
| |
| cmsUNUSED_PARAMETER(nItems); |
| cmsUNUSED_PARAMETER(self); |
| } |
| |
| static |
| void* Type_DateTime_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) |
| { |
| return _cmsDupMem(self ->ContextID, Ptr, sizeof(struct tm)); |
| |
| cmsUNUSED_PARAMETER(n); |
| } |
| |
| static |
| void Type_DateTime_Free(struct _cms_typehandler_struct* self, void* Ptr) |
| { |
| _cmsFree(self ->ContextID, Ptr); |
| } |
| |
| |
| |
| // ******************************************************************************** |
| // Type icMeasurementType |
| // ******************************************************************************** |
| |
| /* |
| The measurementType information refers only to the internal profile data and is |
| meant to provide profile makers an alternative to the default measurement |
| specifications. |
| */ |
| |
| static |
| void *Type_Measurement_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) |
| { |
| cmsICCMeasurementConditions mc; |
| |
| |
| memset(&mc, 0, sizeof(mc)); |
| |
| if (!_cmsReadUInt32Number(io, &mc.Observer)) return NULL; |
| if (!_cmsReadXYZNumber(io, &mc.Backing)) return NULL; |
| if (!_cmsReadUInt32Number(io, &mc.Geometry)) return NULL; |
| if (!_cmsRead15Fixed16Number(io, &mc.Flare)) return NULL; |
| if (!_cmsReadUInt32Number(io, &mc.IlluminantType)) return NULL; |
| |
| *nItems = 1; |
| return _cmsDupMem(self ->ContextID, &mc, sizeof(cmsICCMeasurementConditions)); |
| |
| cmsUNUSED_PARAMETER(SizeOfTag); |
| } |
| |
| |
| static |
| cmsBool Type_Measurement_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) |
| { |
| cmsICCMeasurementConditions* mc =(cmsICCMeasurementConditions*) Ptr; |
| |
| if (!_cmsWriteUInt32Number(io, mc->Observer)) return FALSE; |
| if (!_cmsWriteXYZNumber(io, &mc->Backing)) return FALSE; |
| if (!_cmsWriteUInt32Number(io, mc->Geometry)) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, mc->Flare)) return FALSE; |
| if (!_cmsWriteUInt32Number(io, mc->IlluminantType)) return FALSE; |
| |
| return TRUE; |
| |
| cmsUNUSED_PARAMETER(nItems); |
| cmsUNUSED_PARAMETER(self); |
| } |
| |
| static |
| void* Type_Measurement_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) |
| { |
| return _cmsDupMem(self ->ContextID, Ptr, sizeof(cmsICCMeasurementConditions)); |
| |
| cmsUNUSED_PARAMETER(n); |
| } |
| |
| static |
| void Type_Measurement_Free(struct _cms_typehandler_struct* self, void* Ptr) |
| { |
| _cmsFree(self ->ContextID, Ptr); |
| } |
| |
| |
| // ******************************************************************************** |
| // Type cmsSigMultiLocalizedUnicodeType |
| // ******************************************************************************** |
| // |
| // Do NOT trust SizeOfTag as there is an issue on the definition of profileSequenceDescTag. See the TechNote from |
| // Max Derhak and Rohit Patil about this: basically the size of the string table should be guessed and cannot be |
| // taken from the size of tag if this tag is embedded as part of bigger structures (profileSequenceDescTag, for instance) |
| // |
| |
| static |
| void *Type_MLU_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) |
| { |
| cmsMLU* mlu; |
| cmsUInt32Number Count, RecLen, NumOfWchar; |
| cmsUInt32Number SizeOfHeader; |
| cmsUInt32Number Len, Offset; |
| cmsUInt32Number i; |
| wchar_t* Block; |
| cmsUInt32Number BeginOfThisString, EndOfThisString, LargestPosition; |
| |
| *nItems = 0; |
| if (!_cmsReadUInt32Number(io, &Count)) return NULL; |
| if (!_cmsReadUInt32Number(io, &RecLen)) return NULL; |
| |
| if (RecLen != 12) { |
| |
| cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "multiLocalizedUnicodeType of len != 12 is not supported."); |
| return NULL; |
| } |
| |
| mlu = cmsMLUalloc(self ->ContextID, Count); |
| if (mlu == NULL) return NULL; |
| |
| mlu ->UsedEntries = Count; |
| |
| SizeOfHeader = 12 * Count + sizeof(_cmsTagBase); |
| LargestPosition = 0; |
| |
| for (i=0; i < Count; i++) { |
| |
| if (!_cmsReadUInt16Number(io, &mlu ->Entries[i].Language)) goto Error; |
| if (!_cmsReadUInt16Number(io, &mlu ->Entries[i].Country)) goto Error; |
| |
| // Now deal with Len and offset. |
| if (!_cmsReadUInt32Number(io, &Len)) goto Error; |
| if (!_cmsReadUInt32Number(io, &Offset)) goto Error; |
| |
| // Check for overflow |
| if (Offset < (SizeOfHeader + 8)) goto Error; |
| if (((Offset + Len) < Len) || ((Offset + Len) > SizeOfTag + 8)) goto Error; |
| |
| // True begin of the string |
| BeginOfThisString = Offset - SizeOfHeader - 8; |
| |
| // Ajust to wchar_t elements |
| mlu ->Entries[i].Len = (Len * sizeof(wchar_t)) / sizeof(cmsUInt16Number); |
| mlu ->Entries[i].StrW = (BeginOfThisString * sizeof(wchar_t)) / sizeof(cmsUInt16Number); |
| |
| // To guess maximum size, add offset + len |
| EndOfThisString = BeginOfThisString + Len; |
| if (EndOfThisString > LargestPosition) |
| LargestPosition = EndOfThisString; |
| } |
| |
| // Now read the remaining of tag and fill all strings. Subtract the directory |
| SizeOfTag = (LargestPosition * sizeof(wchar_t)) / sizeof(cmsUInt16Number); |
| if (SizeOfTag == 0) |
| { |
| Block = NULL; |
| NumOfWchar = 0; |
| |
| } |
| else |
| { |
| Block = (wchar_t*) _cmsMalloc(self ->ContextID, SizeOfTag); |
| if (Block == NULL) goto Error; |
| NumOfWchar = SizeOfTag / sizeof(wchar_t); |
| if (!_cmsReadWCharArray(io, NumOfWchar, Block)) goto Error; |
| } |
| |
| mlu ->MemPool = Block; |
| mlu ->PoolSize = SizeOfTag; |
| mlu ->PoolUsed = SizeOfTag; |
| |
| *nItems = 1; |
| return (void*) mlu; |
| |
| Error: |
| if (mlu) cmsMLUfree(mlu); |
| return NULL; |
| } |
| |
| static |
| cmsBool Type_MLU_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) |
| { |
| cmsMLU* mlu =(cmsMLU*) Ptr; |
| cmsUInt32Number HeaderSize; |
| cmsUInt32Number Len, Offset; |
| cmsUInt32Number i; |
| |
| if (Ptr == NULL) { |
| |
| // Empty placeholder |
| if (!_cmsWriteUInt32Number(io, 0)) return FALSE; |
| if (!_cmsWriteUInt32Number(io, 12)) return FALSE; |
| return TRUE; |
| } |
| |
| if (!_cmsWriteUInt32Number(io, mlu ->UsedEntries)) return FALSE; |
| if (!_cmsWriteUInt32Number(io, 12)) return FALSE; |
| |
| HeaderSize = 12 * mlu ->UsedEntries + sizeof(_cmsTagBase); |
| |
| for (i=0; i < mlu ->UsedEntries; i++) { |
| |
| Len = mlu ->Entries[i].Len; |
| Offset = mlu ->Entries[i].StrW; |
| |
| Len = (Len * sizeof(cmsUInt16Number)) / sizeof(wchar_t); |
| Offset = (Offset * sizeof(cmsUInt16Number)) / sizeof(wchar_t) + HeaderSize + 8; |
| |
| if (!_cmsWriteUInt16Number(io, mlu ->Entries[i].Language)) return FALSE; |
| if (!_cmsWriteUInt16Number(io, mlu ->Entries[i].Country)) return FALSE; |
| if (!_cmsWriteUInt32Number(io, Len)) return FALSE; |
| if (!_cmsWriteUInt32Number(io, Offset)) return FALSE; |
| } |
| |
| if (!_cmsWriteWCharArray(io, mlu ->PoolUsed / sizeof(wchar_t), (wchar_t*) mlu ->MemPool)) return FALSE; |
| |
| return TRUE; |
| |
| cmsUNUSED_PARAMETER(nItems); |
| cmsUNUSED_PARAMETER(self); |
| } |
| |
| |
| static |
| void* Type_MLU_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) |
| { |
| return (void*) cmsMLUdup((cmsMLU*) Ptr); |
| |
| cmsUNUSED_PARAMETER(n); |
| cmsUNUSED_PARAMETER(self); |
| } |
| |
| static |
| void Type_MLU_Free(struct _cms_typehandler_struct* self, void* Ptr) |
| { |
| cmsMLUfree((cmsMLU*) Ptr); |
| return; |
| |
| cmsUNUSED_PARAMETER(self); |
| } |
| |
| |
| // ******************************************************************************** |
| // Type cmsSigLut8Type |
| // ******************************************************************************** |
| |
| // Decide which LUT type to use on writing |
| static |
| cmsTagTypeSignature DecideLUTtypeA2B(cmsFloat64Number ICCVersion, const void *Data) |
| { |
| cmsPipeline* Lut = (cmsPipeline*) Data; |
| |
| if (ICCVersion < 4.0) { |
| if (Lut ->SaveAs8Bits) return cmsSigLut8Type; |
| return cmsSigLut16Type; |
| } |
| else { |
| return cmsSigLutAtoBType; |
| } |
| } |
| |
| static |
| cmsTagTypeSignature DecideLUTtypeB2A(cmsFloat64Number ICCVersion, const void *Data) |
| { |
| cmsPipeline* Lut = (cmsPipeline*) Data; |
| |
| if (ICCVersion < 4.0) { |
| if (Lut ->SaveAs8Bits) return cmsSigLut8Type; |
| return cmsSigLut16Type; |
| } |
| else { |
| return cmsSigLutBtoAType; |
| } |
| } |
| |
| /* |
| This structure represents a colour transform using tables of 8-bit precision. |
| This type contains four processing elements: a 3 by 3 matrix (which shall be |
| the identity matrix unless the input colour space is XYZ), a set of one dimensional |
| input tables, a multidimensional lookup table, and a set of one dimensional output |
| tables. Data is processed using these elements via the following sequence: |
| (matrix) -> (1d input tables) -> (multidimensional lookup table - CLUT) -> (1d output tables) |
| |
| Byte Position Field Length (bytes) Content Encoded as... |
| 8 1 Number of Input Channels (i) uInt8Number |
| 9 1 Number of Output Channels (o) uInt8Number |
| 10 1 Number of CLUT grid points (identical for each side) (g) uInt8Number |
| 11 1 Reserved for padding (fill with 00h) |
| |
| 12..15 4 Encoded e00 parameter s15Fixed16Number |
| */ |
| |
| |
| // Read 8 bit tables as gamma functions |
| static |
| cmsBool Read8bitTables(cmsContext ContextID, cmsIOHANDLER* io, cmsPipeline* lut, cmsUInt32Number nChannels) |
| { |
| cmsUInt8Number* Temp = NULL; |
| cmsUInt32Number i, j; |
| cmsToneCurve* Tables[cmsMAXCHANNELS]; |
| |
| if (nChannels > cmsMAXCHANNELS) return FALSE; |
| if (nChannels <= 0) return FALSE; |
| |
| memset(Tables, 0, sizeof(Tables)); |
| |
| Temp = (cmsUInt8Number*) _cmsMalloc(ContextID, 256); |
| if (Temp == NULL) return FALSE; |
| |
| for (i=0; i < nChannels; i++) { |
| Tables[i] = cmsBuildTabulatedToneCurve16(ContextID, 256, NULL); |
| if (Tables[i] == NULL) goto Error; |
| } |
| |
| for (i=0; i < nChannels; i++) { |
| |
| if (io ->Read(io, Temp, 256, 1) != 1) goto Error; |
| |
| for (j=0; j < 256; j++) |
| Tables[i]->Table16[j] = (cmsUInt16Number) FROM_8_TO_16(Temp[j]); |
| } |
| |
| _cmsFree(ContextID, Temp); |
| Temp = NULL; |
| |
| if (!cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, nChannels, Tables))) |
| goto Error; |
| |
| for (i=0; i < nChannels; i++) |
| cmsFreeToneCurve(Tables[i]); |
| |
| return TRUE; |
| |
| Error: |
| for (i=0; i < nChannels; i++) { |
| if (Tables[i]) cmsFreeToneCurve(Tables[i]); |
| } |
| |
| if (Temp) _cmsFree(ContextID, Temp); |
| return FALSE; |
| } |
| |
| |
| static |
| cmsBool Write8bitTables(cmsContext ContextID, cmsIOHANDLER* io, cmsUInt32Number n, _cmsStageToneCurvesData* Tables) |
| { |
| int j; |
| cmsUInt32Number i; |
| cmsUInt8Number val; |
| |
| for (i=0; i < n; i++) { |
| |
| if (Tables) { |
| |
| // Usual case of identity curves |
| if ((Tables ->TheCurves[i]->nEntries == 2) && |
| (Tables->TheCurves[i]->Table16[0] == 0) && |
| (Tables->TheCurves[i]->Table16[1] == 65535)) { |
| |
| for (j=0; j < 256; j++) { |
| if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) j)) return FALSE; |
| } |
| } |
| else |
| if (Tables ->TheCurves[i]->nEntries != 256) { |
| cmsSignalError(ContextID, cmsERROR_RANGE, "LUT8 needs 256 entries on prelinearization"); |
| return FALSE; |
| } |
| else |
| for (j=0; j < 256; j++) { |
| |
| val = (cmsUInt8Number) FROM_16_TO_8(Tables->TheCurves[i]->Table16[j]); |
| |
| if (!_cmsWriteUInt8Number(io, val)) return FALSE; |
| } |
| } |
| } |
| return TRUE; |
| } |
| |
| |
| // Check overflow |
| static |
| cmsUInt32Number uipow(cmsUInt32Number n, cmsUInt32Number a, cmsUInt32Number b) |
| { |
| cmsUInt32Number rv = 1, rc; |
| |
| if (a == 0) return 0; |
| if (n == 0) return 0; |
| |
| for (; b > 0; b--) { |
| |
| rv *= a; |
| |
| // Check for overflow |
| if (rv > UINT_MAX / a) return (cmsUInt32Number) -1; |
| |
| } |
| |
| rc = rv * n; |
| |
| if (rv != rc / n) return (cmsUInt32Number) -1; |
| return rc; |
| } |
| |
| |
| // That will create a MPE LUT with Matrix, pre tables, CLUT and post tables. |
| // 8 bit lut may be scaled easely to v4 PCS, but we need also to properly adjust |
| // PCS on BToAxx tags and AtoB if abstract. We need to fix input direction. |
| |
| static |
| void *Type_LUT8_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) |
| { |
| cmsUInt8Number InputChannels, OutputChannels, CLUTpoints; |
| cmsUInt8Number* Temp = NULL; |
| cmsPipeline* NewLUT = NULL; |
| cmsUInt32Number nTabSize, i; |
| cmsFloat64Number Matrix[3*3]; |
| |
| *nItems = 0; |
| |
| if (!_cmsReadUInt8Number(io, &InputChannels)) goto Error; |
| if (!_cmsReadUInt8Number(io, &OutputChannels)) goto Error; |
| if (!_cmsReadUInt8Number(io, &CLUTpoints)) goto Error; |
| |
| if (CLUTpoints == 1) goto Error; // Impossible value, 0 for no CLUT and then 2 at least |
| |
| // Padding |
| if (!_cmsReadUInt8Number(io, NULL)) goto Error; |
| |
| // Do some checking |
| if (InputChannels == 0 || InputChannels > cmsMAXCHANNELS) goto Error; |
| if (OutputChannels == 0 || OutputChannels > cmsMAXCHANNELS) goto Error; |
| |
| // Allocates an empty Pipeline |
| NewLUT = cmsPipelineAlloc(self ->ContextID, InputChannels, OutputChannels); |
| if (NewLUT == NULL) goto Error; |
| |
| // Read the Matrix |
| if (!_cmsRead15Fixed16Number(io, &Matrix[0])) goto Error; |
| if (!_cmsRead15Fixed16Number(io, &Matrix[1])) goto Error; |
| if (!_cmsRead15Fixed16Number(io, &Matrix[2])) goto Error; |
| if (!_cmsRead15Fixed16Number(io, &Matrix[3])) goto Error; |
| if (!_cmsRead15Fixed16Number(io, &Matrix[4])) goto Error; |
| if (!_cmsRead15Fixed16Number(io, &Matrix[5])) goto Error; |
| if (!_cmsRead15Fixed16Number(io, &Matrix[6])) goto Error; |
| if (!_cmsRead15Fixed16Number(io, &Matrix[7])) goto Error; |
| if (!_cmsRead15Fixed16Number(io, &Matrix[8])) goto Error; |
| |
| |
| // Only operates if not identity... |
| if ((InputChannels == 3) && !_cmsMAT3isIdentity((cmsMAT3*) Matrix)) { |
| |
| if (!cmsPipelineInsertStage(NewLUT, cmsAT_BEGIN, cmsStageAllocMatrix(self ->ContextID, 3, 3, Matrix, NULL))) |
| goto Error; |
| } |
| |
| // Get input tables |
| if (!Read8bitTables(self ->ContextID, io, NewLUT, InputChannels)) goto Error; |
| |
| // Get 3D CLUT. Check the overflow.... |
| nTabSize = uipow(OutputChannels, CLUTpoints, InputChannels); |
| if (nTabSize == (cmsUInt32Number) -1) goto Error; |
| if (nTabSize > 0) { |
| |
| cmsUInt16Number *PtrW, *T; |
| |
| PtrW = T = (cmsUInt16Number*) _cmsCalloc(self ->ContextID, nTabSize, sizeof(cmsUInt16Number)); |
| if (T == NULL) goto Error; |
| |
| Temp = (cmsUInt8Number*) _cmsMalloc(self ->ContextID, nTabSize); |
| if (Temp == NULL) { |
| _cmsFree(self ->ContextID, T); |
| goto Error; |
| } |
| |
| if (io ->Read(io, Temp, nTabSize, 1) != 1) { |
| _cmsFree(self ->ContextID, T); |
| _cmsFree(self ->ContextID, Temp); |
| goto Error; |
| } |
| |
| for (i = 0; i < nTabSize; i++) { |
| |
| *PtrW++ = FROM_8_TO_16(Temp[i]); |
| } |
| _cmsFree(self ->ContextID, Temp); |
| Temp = NULL; |
| |
| if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, cmsStageAllocCLut16bit(self ->ContextID, CLUTpoints, InputChannels, OutputChannels, T))) { |
| _cmsFree(self ->ContextID, T); |
| goto Error; |
| } |
| _cmsFree(self ->ContextID, T); |
| } |
| |
| |
| // Get output tables |
| if (!Read8bitTables(self ->ContextID, io, NewLUT, OutputChannels)) goto Error; |
| |
| *nItems = 1; |
| return NewLUT; |
| |
| Error: |
| if (NewLUT != NULL) cmsPipelineFree(NewLUT); |
| return NULL; |
| |
| cmsUNUSED_PARAMETER(SizeOfTag); |
| } |
| |
| // We only allow a specific MPE structure: Matrix plus prelin, plus clut, plus post-lin. |
| static |
| cmsBool Type_LUT8_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) |
| { |
| cmsUInt32Number j, nTabSize; |
| cmsUInt8Number val; |
| cmsPipeline* NewLUT = (cmsPipeline*) Ptr; |
| cmsStage* mpe; |
| _cmsStageToneCurvesData* PreMPE = NULL, *PostMPE = NULL; |
| _cmsStageMatrixData* MatMPE = NULL; |
| _cmsStageCLutData* clut = NULL; |
| cmsUInt32Number clutPoints; |
| |
| // Disassemble the LUT into components. |
| mpe = NewLUT -> Elements; |
| if (mpe ->Type == cmsSigMatrixElemType) { |
| |
| MatMPE = (_cmsStageMatrixData*) mpe ->Data; |
| mpe = mpe -> Next; |
| } |
| |
| if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) { |
| PreMPE = (_cmsStageToneCurvesData*) mpe ->Data; |
| mpe = mpe -> Next; |
| } |
| |
| if (mpe != NULL && mpe ->Type == cmsSigCLutElemType) { |
| clut = (_cmsStageCLutData*) mpe -> Data; |
| mpe = mpe ->Next; |
| } |
| |
| if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) { |
| PostMPE = (_cmsStageToneCurvesData*) mpe ->Data; |
| mpe = mpe -> Next; |
| } |
| |
| // That should be all |
| if (mpe != NULL) { |
| cmsSignalError(mpe->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT is not suitable to be saved as LUT8"); |
| return FALSE; |
| } |
| |
| |
| if (clut == NULL) |
| clutPoints = 0; |
| else |
| clutPoints = clut->Params->nSamples[0]; |
| |
| if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) NewLUT ->InputChannels)) return FALSE; |
| if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) NewLUT ->OutputChannels)) return FALSE; |
| if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) clutPoints)) return FALSE; |
| if (!_cmsWriteUInt8Number(io, 0)) return FALSE; // Padding |
| |
| |
| if (MatMPE != NULL) { |
| |
| if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[0])) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[1])) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[2])) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[3])) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[4])) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[5])) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[6])) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[7])) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[8])) return FALSE; |
| |
| } |
| else { |
| |
| if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE; |
| } |
| |
| // The prelinearization table |
| if (!Write8bitTables(self ->ContextID, io, NewLUT ->InputChannels, PreMPE)) return FALSE; |
| |
| nTabSize = uipow(NewLUT->OutputChannels, clutPoints, NewLUT ->InputChannels); |
| if (nTabSize == (cmsUInt32Number) -1) return FALSE; |
| if (nTabSize > 0) { |
| |
| // The 3D CLUT. |
| if (clut != NULL) { |
| |
| for (j=0; j < nTabSize; j++) { |
| |
| val = (cmsUInt8Number) FROM_16_TO_8(clut ->Tab.T[j]); |
| if (!_cmsWriteUInt8Number(io, val)) return FALSE; |
| } |
| } |
| } |
| |
| // The postlinearization table |
| if (!Write8bitTables(self ->ContextID, io, NewLUT ->OutputChannels, PostMPE)) return FALSE; |
| |
| return TRUE; |
| |
| cmsUNUSED_PARAMETER(nItems); |
| } |
| |
| |
| static |
| void* Type_LUT8_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) |
| { |
| return (void*) cmsPipelineDup((cmsPipeline*) Ptr); |
| |
| cmsUNUSED_PARAMETER(n); |
| cmsUNUSED_PARAMETER(self); |
| } |
| |
| static |
| void Type_LUT8_Free(struct _cms_typehandler_struct* self, void* Ptr) |
| { |
| cmsPipelineFree((cmsPipeline*) Ptr); |
| return; |
| |
| cmsUNUSED_PARAMETER(self); |
| } |
| |
| // ******************************************************************************** |
| // Type cmsSigLut16Type |
| // ******************************************************************************** |
| |
| // Read 16 bit tables as gamma functions |
| static |
| cmsBool Read16bitTables(cmsContext ContextID, cmsIOHANDLER* io, cmsPipeline* lut, |
| cmsUInt32Number nChannels, cmsUInt32Number nEntries) |
| { |
| cmsUInt32Number i; |
| cmsToneCurve* Tables[cmsMAXCHANNELS]; |
| |
| // Maybe an empty table? (this is a lcms extension) |
| if (nEntries <= 0) return TRUE; |
| |
| // Check for malicious profiles |
| if (nEntries < 2) return FALSE; |
| if (nChannels > cmsMAXCHANNELS) return FALSE; |
| |
| // Init table to zero |
| memset(Tables, 0, sizeof(Tables)); |
| |
| for (i=0; i < nChannels; i++) { |
| |
| Tables[i] = cmsBuildTabulatedToneCurve16(ContextID, nEntries, NULL); |
| if (Tables[i] == NULL) goto Error; |
| |
| if (!_cmsReadUInt16Array(io, nEntries, Tables[i]->Table16)) goto Error; |
| } |
| |
| |
| // Add the table (which may certainly be an identity, but this is up to the optimizer, not the reading code) |
| if (!cmsPipelineInsertStage(lut, cmsAT_END, cmsStageAllocToneCurves(ContextID, nChannels, Tables))) |
| goto Error; |
| |
| for (i=0; i < nChannels; i++) |
| cmsFreeToneCurve(Tables[i]); |
| |
| return TRUE; |
| |
| Error: |
| for (i=0; i < nChannels; i++) { |
| if (Tables[i]) cmsFreeToneCurve(Tables[i]); |
| } |
| |
| return FALSE; |
| } |
| |
| static |
| cmsBool Write16bitTables(cmsContext ContextID, cmsIOHANDLER* io, _cmsStageToneCurvesData* Tables) |
| { |
| cmsUInt32Number j; |
| cmsUInt32Number i; |
| cmsUInt16Number val; |
| cmsUInt32Number nEntries; |
| |
| _cmsAssert(Tables != NULL); |
| |
| nEntries = Tables->TheCurves[0]->nEntries; |
| |
| for (i=0; i < Tables ->nCurves; i++) { |
| |
| for (j=0; j < nEntries; j++) { |
| |
| val = Tables->TheCurves[i]->Table16[j]; |
| if (!_cmsWriteUInt16Number(io, val)) return FALSE; |
| } |
| } |
| return TRUE; |
| |
| cmsUNUSED_PARAMETER(ContextID); |
| } |
| |
| static |
| void *Type_LUT16_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) |
| { |
| cmsUInt8Number InputChannels, OutputChannels, CLUTpoints; |
| cmsPipeline* NewLUT = NULL; |
| cmsUInt32Number nTabSize; |
| cmsFloat64Number Matrix[3*3]; |
| cmsUInt16Number InputEntries, OutputEntries; |
| |
| *nItems = 0; |
| |
| if (!_cmsReadUInt8Number(io, &InputChannels)) return NULL; |
| if (!_cmsReadUInt8Number(io, &OutputChannels)) return NULL; |
| if (!_cmsReadUInt8Number(io, &CLUTpoints)) return NULL; // 255 maximum |
| |
| // Padding |
| if (!_cmsReadUInt8Number(io, NULL)) return NULL; |
| |
| // Do some checking |
| if (InputChannels == 0 || InputChannels > cmsMAXCHANNELS) goto Error; |
| if (OutputChannels == 0 || OutputChannels > cmsMAXCHANNELS) goto Error; |
| |
| // Allocates an empty LUT |
| NewLUT = cmsPipelineAlloc(self ->ContextID, InputChannels, OutputChannels); |
| if (NewLUT == NULL) goto Error; |
| |
| // Read the Matrix |
| if (!_cmsRead15Fixed16Number(io, &Matrix[0])) goto Error; |
| if (!_cmsRead15Fixed16Number(io, &Matrix[1])) goto Error; |
| if (!_cmsRead15Fixed16Number(io, &Matrix[2])) goto Error; |
| if (!_cmsRead15Fixed16Number(io, &Matrix[3])) goto Error; |
| if (!_cmsRead15Fixed16Number(io, &Matrix[4])) goto Error; |
| if (!_cmsRead15Fixed16Number(io, &Matrix[5])) goto Error; |
| if (!_cmsRead15Fixed16Number(io, &Matrix[6])) goto Error; |
| if (!_cmsRead15Fixed16Number(io, &Matrix[7])) goto Error; |
| if (!_cmsRead15Fixed16Number(io, &Matrix[8])) goto Error; |
| |
| |
| // Only operates on 3 channels |
| if ((InputChannels == 3) && !_cmsMAT3isIdentity((cmsMAT3*) Matrix)) { |
| |
| if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, cmsStageAllocMatrix(self ->ContextID, 3, 3, Matrix, NULL))) |
| goto Error; |
| } |
| |
| if (!_cmsReadUInt16Number(io, &InputEntries)) goto Error; |
| if (!_cmsReadUInt16Number(io, &OutputEntries)) goto Error; |
| |
| if (InputEntries > 0x7FFF || OutputEntries > 0x7FFF) goto Error; |
| if (CLUTpoints == 1) goto Error; // Impossible value, 0 for no CLUT and then 2 at least |
| |
| // Get input tables |
| if (!Read16bitTables(self ->ContextID, io, NewLUT, InputChannels, InputEntries)) goto Error; |
| |
| // Get 3D CLUT |
| nTabSize = uipow(OutputChannels, CLUTpoints, InputChannels); |
| if (nTabSize == (cmsUInt32Number) -1) goto Error; |
| if (nTabSize > 0) { |
| |
| cmsUInt16Number *T; |
| |
| T = (cmsUInt16Number*) _cmsCalloc(self ->ContextID, nTabSize, sizeof(cmsUInt16Number)); |
| if (T == NULL) goto Error; |
| |
| if (!_cmsReadUInt16Array(io, nTabSize, T)) { |
| _cmsFree(self ->ContextID, T); |
| goto Error; |
| } |
| |
| if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, cmsStageAllocCLut16bit(self ->ContextID, CLUTpoints, InputChannels, OutputChannels, T))) { |
| _cmsFree(self ->ContextID, T); |
| goto Error; |
| } |
| _cmsFree(self ->ContextID, T); |
| } |
| |
| |
| // Get output tables |
| if (!Read16bitTables(self ->ContextID, io, NewLUT, OutputChannels, OutputEntries)) goto Error; |
| |
| *nItems = 1; |
| return NewLUT; |
| |
| Error: |
| if (NewLUT != NULL) cmsPipelineFree(NewLUT); |
| return NULL; |
| |
| cmsUNUSED_PARAMETER(SizeOfTag); |
| } |
| |
| // We only allow some specific MPE structures: Matrix plus prelin, plus clut, plus post-lin. |
| // Some empty defaults are created for missing parts |
| |
| static |
| cmsBool Type_LUT16_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) |
| { |
| cmsUInt32Number nTabSize; |
| cmsPipeline* NewLUT = (cmsPipeline*) Ptr; |
| cmsStage* mpe; |
| _cmsStageToneCurvesData* PreMPE = NULL, *PostMPE = NULL; |
| _cmsStageMatrixData* MatMPE = NULL; |
| _cmsStageCLutData* clut = NULL; |
| cmsUInt32Number i, InputChannels, OutputChannels, clutPoints; |
| |
| // Disassemble the LUT into components. |
| mpe = NewLUT -> Elements; |
| if (mpe != NULL && mpe ->Type == cmsSigMatrixElemType) { |
| |
| MatMPE = (_cmsStageMatrixData*) mpe ->Data; |
| mpe = mpe -> Next; |
| } |
| |
| |
| if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) { |
| PreMPE = (_cmsStageToneCurvesData*) mpe ->Data; |
| mpe = mpe -> Next; |
| } |
| |
| if (mpe != NULL && mpe ->Type == cmsSigCLutElemType) { |
| clut = (_cmsStageCLutData*) mpe -> Data; |
| mpe = mpe ->Next; |
| } |
| |
| if (mpe != NULL && mpe ->Type == cmsSigCurveSetElemType) { |
| PostMPE = (_cmsStageToneCurvesData*) mpe ->Data; |
| mpe = mpe -> Next; |
| } |
| |
| // That should be all |
| if (mpe != NULL) { |
| cmsSignalError(mpe->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT is not suitable to be saved as LUT16"); |
| return FALSE; |
| } |
| |
| InputChannels = cmsPipelineInputChannels(NewLUT); |
| OutputChannels = cmsPipelineOutputChannels(NewLUT); |
| |
| if (clut == NULL) |
| clutPoints = 0; |
| else |
| clutPoints = clut->Params->nSamples[0]; |
| |
| if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) InputChannels)) return FALSE; |
| if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) OutputChannels)) return FALSE; |
| if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) clutPoints)) return FALSE; |
| if (!_cmsWriteUInt8Number(io, 0)) return FALSE; // Padding |
| |
| |
| if (MatMPE != NULL) { |
| |
| if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[0])) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[1])) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[2])) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[3])) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[4])) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[5])) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[6])) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[7])) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, MatMPE -> Double[8])) return FALSE; |
| } |
| else { |
| |
| if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, 1)) return FALSE; |
| } |
| |
| |
| if (PreMPE != NULL) { |
| if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) PreMPE ->TheCurves[0]->nEntries)) return FALSE; |
| } else { |
| if (!_cmsWriteUInt16Number(io, 2)) return FALSE; |
| } |
| |
| if (PostMPE != NULL) { |
| if (!_cmsWriteUInt16Number(io, (cmsUInt16Number) PostMPE ->TheCurves[0]->nEntries)) return FALSE; |
| } else { |
| if (!_cmsWriteUInt16Number(io, 2)) return FALSE; |
| |
| } |
| |
| // The prelinearization table |
| |
| if (PreMPE != NULL) { |
| if (!Write16bitTables(self ->ContextID, io, PreMPE)) return FALSE; |
| } |
| else { |
| for (i=0; i < InputChannels; i++) { |
| |
| if (!_cmsWriteUInt16Number(io, 0)) return FALSE; |
| if (!_cmsWriteUInt16Number(io, 0xffff)) return FALSE; |
| } |
| } |
| |
| nTabSize = uipow(OutputChannels, clutPoints, InputChannels); |
| if (nTabSize == (cmsUInt32Number) -1) return FALSE; |
| if (nTabSize > 0) { |
| // The 3D CLUT. |
| if (clut != NULL) { |
| if (!_cmsWriteUInt16Array(io, nTabSize, clut->Tab.T)) return FALSE; |
| } |
| } |
| |
| // The postlinearization table |
| if (PostMPE != NULL) { |
| if (!Write16bitTables(self ->ContextID, io, PostMPE)) return FALSE; |
| } |
| else { |
| for (i=0; i < OutputChannels; i++) { |
| |
| if (!_cmsWriteUInt16Number(io, 0)) return FALSE; |
| if (!_cmsWriteUInt16Number(io, 0xffff)) return FALSE; |
| } |
| } |
| |
| return TRUE; |
| |
| cmsUNUSED_PARAMETER(nItems); |
| } |
| |
| static |
| void* Type_LUT16_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) |
| { |
| return (void*) cmsPipelineDup((cmsPipeline*) Ptr); |
| |
| cmsUNUSED_PARAMETER(n); |
| cmsUNUSED_PARAMETER(self); |
| } |
| |
| static |
| void Type_LUT16_Free(struct _cms_typehandler_struct* self, void* Ptr) |
| { |
| cmsPipelineFree((cmsPipeline*) Ptr); |
| return; |
| |
| cmsUNUSED_PARAMETER(self); |
| } |
| |
| |
| // ******************************************************************************** |
| // Type cmsSigLutAToBType |
| // ******************************************************************************** |
| |
| |
| // V4 stuff. Read matrix for LutAtoB and LutBtoA |
| |
| static |
| cmsStage* ReadMatrix(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number Offset) |
| { |
| cmsFloat64Number dMat[3*3]; |
| cmsFloat64Number dOff[3]; |
| cmsStage* Mat; |
| |
| // Go to address |
| if (!io -> Seek(io, Offset)) return NULL; |
| |
| // Read the Matrix |
| if (!_cmsRead15Fixed16Number(io, &dMat[0])) return NULL; |
| if (!_cmsRead15Fixed16Number(io, &dMat[1])) return NULL; |
| if (!_cmsRead15Fixed16Number(io, &dMat[2])) return NULL; |
| if (!_cmsRead15Fixed16Number(io, &dMat[3])) return NULL; |
| if (!_cmsRead15Fixed16Number(io, &dMat[4])) return NULL; |
| if (!_cmsRead15Fixed16Number(io, &dMat[5])) return NULL; |
| if (!_cmsRead15Fixed16Number(io, &dMat[6])) return NULL; |
| if (!_cmsRead15Fixed16Number(io, &dMat[7])) return NULL; |
| if (!_cmsRead15Fixed16Number(io, &dMat[8])) return NULL; |
| |
| if (!_cmsRead15Fixed16Number(io, &dOff[0])) return NULL; |
| if (!_cmsRead15Fixed16Number(io, &dOff[1])) return NULL; |
| if (!_cmsRead15Fixed16Number(io, &dOff[2])) return NULL; |
| |
| Mat = cmsStageAllocMatrix(self ->ContextID, 3, 3, dMat, dOff); |
| |
| return Mat; |
| } |
| |
| |
| |
| |
| // V4 stuff. Read CLUT part for LutAtoB and LutBtoA |
| |
| static |
| cmsStage* ReadCLUT(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, |
| cmsUInt32Number Offset, cmsUInt32Number InputChannels, cmsUInt32Number OutputChannels) |
| { |
| cmsUInt8Number gridPoints8[cmsMAXCHANNELS]; // Number of grid points in each dimension. |
| cmsUInt32Number GridPoints[cmsMAXCHANNELS], i; |
| cmsUInt8Number Precision; |
| cmsStage* CLUT; |
| _cmsStageCLutData* Data; |
| |
| if (!io -> Seek(io, Offset)) return NULL; |
| if (io -> Read(io, gridPoints8, cmsMAXCHANNELS, 1) != 1) return NULL; |
| |
| |
| for (i=0; i < cmsMAXCHANNELS; i++) { |
| |
| if (gridPoints8[i] == 1) return NULL; // Impossible value, 0 for no CLUT and then 2 at least |
| GridPoints[i] = gridPoints8[i]; |
| } |
| |
| if (!_cmsReadUInt8Number(io, &Precision)) return NULL; |
| |
| if (!_cmsReadUInt8Number(io, NULL)) return NULL; |
| if (!_cmsReadUInt8Number(io, NULL)) return NULL; |
| if (!_cmsReadUInt8Number(io, NULL)) return NULL; |
| |
| CLUT = cmsStageAllocCLut16bitGranular(self ->ContextID, GridPoints, InputChannels, OutputChannels, NULL); |
| if (CLUT == NULL) return NULL; |
| |
| Data = (_cmsStageCLutData*) CLUT ->Data; |
| |
| // Precision can be 1 or 2 bytes |
| if (Precision == 1) { |
| |
| cmsUInt8Number v; |
| |
| for (i=0; i < Data ->nEntries; i++) { |
| |
| if (io ->Read(io, &v, sizeof(cmsUInt8Number), 1) != 1) { |
| cmsStageFree(CLUT); |
| return NULL; |
| } |
| Data ->Tab.T[i] = FROM_8_TO_16(v); |
| } |
| |
| } |
| else |
| if (Precision == 2) { |
| |
| if (!_cmsReadUInt16Array(io, Data->nEntries, Data ->Tab.T)) { |
| cmsStageFree(CLUT); |
| return NULL; |
| } |
| } |
| else { |
| cmsStageFree(CLUT); |
| cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown precision of '%d'", Precision); |
| return NULL; |
| } |
| |
| return CLUT; |
| } |
| |
| static |
| cmsToneCurve* ReadEmbeddedCurve(struct _cms_typehandler_struct* self, cmsIOHANDLER* io) |
| { |
| cmsTagTypeSignature BaseType; |
| cmsUInt32Number nItems; |
| |
| BaseType = _cmsReadTypeBase(io); |
| switch (BaseType) { |
| |
| case cmsSigCurveType: |
| return (cmsToneCurve*) Type_Curve_Read(self, io, &nItems, 0); |
| |
| case cmsSigParametricCurveType: |
| return (cmsToneCurve*) Type_ParametricCurve_Read(self, io, &nItems, 0); |
| |
| default: |
| { |
| char String[5]; |
| |
| _cmsTagSignature2String(String, (cmsTagSignature) BaseType); |
| cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown curve type '%s'", String); |
| } |
| return NULL; |
| } |
| } |
| |
| |
| // Read a set of curves from specific offset |
| static |
| cmsStage* ReadSetOfCurves(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number Offset, cmsUInt32Number nCurves) |
| { |
| cmsToneCurve* Curves[cmsMAXCHANNELS]; |
| cmsUInt32Number i; |
| cmsStage* Lin = NULL; |
| |
| if (nCurves > cmsMAXCHANNELS) return FALSE; |
| |
| if (!io -> Seek(io, Offset)) return FALSE; |
| |
| for (i=0; i < nCurves; i++) |
| Curves[i] = NULL; |
| |
| for (i=0; i < nCurves; i++) { |
| |
| Curves[i] = ReadEmbeddedCurve(self, io); |
| if (Curves[i] == NULL) goto Error; |
| if (!_cmsReadAlignment(io)) goto Error; |
| |
| } |
| |
| Lin = cmsStageAllocToneCurves(self ->ContextID, nCurves, Curves); |
| |
| Error: |
| for (i=0; i < nCurves; i++) |
| cmsFreeToneCurve(Curves[i]); |
| |
| return Lin; |
| } |
| |
| |
| // LutAtoB type |
| |
| // This structure represents a colour transform. The type contains up to five processing |
| // elements which are stored in the AtoBTag tag in the following order: a set of one |
| // dimensional curves, a 3 by 3 matrix with offset terms, a set of one dimensional curves, |
| // a multidimensional lookup table, and a set of one dimensional output curves. |
| // Data are processed using these elements via the following sequence: |
| // |
| //("A" curves) -> (multidimensional lookup table - CLUT) -> ("M" curves) -> (matrix) -> ("B" curves). |
| // |
| /* |
| It is possible to use any or all of these processing elements. At least one processing element |
| must be included.Only the following combinations are allowed: |
| |
| B |
| M - Matrix - B |
| A - CLUT - B |
| A - CLUT - M - Matrix - B |
| |
| */ |
| |
| static |
| void* Type_LUTA2B_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) |
| { |
| cmsUInt32Number BaseOffset; |
| cmsUInt8Number inputChan; // Number of input channels |
| cmsUInt8Number outputChan; // Number of output channels |
| cmsUInt32Number offsetB; // Offset to first "B" curve |
| cmsUInt32Number offsetMat; // Offset to matrix |
| cmsUInt32Number offsetM; // Offset to first "M" curve |
| cmsUInt32Number offsetC; // Offset to CLUT |
| cmsUInt32Number offsetA; // Offset to first "A" curve |
| cmsPipeline* NewLUT = NULL; |
| |
| |
| BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); |
| |
| if (!_cmsReadUInt8Number(io, &inputChan)) return NULL; |
| if (!_cmsReadUInt8Number(io, &outputChan)) return NULL; |
| |
| if (!_cmsReadUInt16Number(io, NULL)) return NULL; |
| |
| if (!_cmsReadUInt32Number(io, &offsetB)) return NULL; |
| if (!_cmsReadUInt32Number(io, &offsetMat)) return NULL; |
| if (!_cmsReadUInt32Number(io, &offsetM)) return NULL; |
| if (!_cmsReadUInt32Number(io, &offsetC)) return NULL; |
| if (!_cmsReadUInt32Number(io, &offsetA)) return NULL; |
| |
| if (inputChan == 0 || inputChan >= cmsMAXCHANNELS) return NULL; |
| if (outputChan == 0 || outputChan >= cmsMAXCHANNELS) return NULL; |
| |
| // Allocates an empty LUT |
| NewLUT = cmsPipelineAlloc(self ->ContextID, inputChan, outputChan); |
| if (NewLUT == NULL) return NULL; |
| |
| if (offsetA!= 0) { |
| if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetA, inputChan))) |
| goto Error; |
| } |
| |
| if (offsetC != 0) { |
| if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadCLUT(self, io, BaseOffset + offsetC, inputChan, outputChan))) |
| goto Error; |
| } |
| |
| if (offsetM != 0) { |
| if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetM, outputChan))) |
| goto Error; |
| } |
| |
| if (offsetMat != 0) { |
| if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadMatrix(self, io, BaseOffset + offsetMat))) |
| goto Error; |
| } |
| |
| if (offsetB != 0) { |
| if (!cmsPipelineInsertStage(NewLUT, cmsAT_END, ReadSetOfCurves(self, io, BaseOffset + offsetB, outputChan))) |
| goto Error; |
| } |
| |
| *nItems = 1; |
| return NewLUT; |
| Error: |
| cmsPipelineFree(NewLUT); |
| return NULL; |
| |
| cmsUNUSED_PARAMETER(SizeOfTag); |
| } |
| |
| // Write a set of curves |
| static |
| cmsBool WriteMatrix(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsStage* mpe) |
| { |
| _cmsStageMatrixData* m = (_cmsStageMatrixData*) mpe -> Data; |
| |
| // Write the Matrix |
| if (!_cmsWrite15Fixed16Number(io, m -> Double[0])) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, m -> Double[1])) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, m -> Double[2])) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, m -> Double[3])) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, m -> Double[4])) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, m -> Double[5])) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, m -> Double[6])) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, m -> Double[7])) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, m -> Double[8])) return FALSE; |
| |
| if (m ->Offset != NULL) { |
| |
| if (!_cmsWrite15Fixed16Number(io, m -> Offset[0])) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, m -> Offset[1])) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, m -> Offset[2])) return FALSE; |
| } |
| else { |
| if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; |
| if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; |
| |
| } |
| |
| |
| return TRUE; |
| |
| cmsUNUSED_PARAMETER(self); |
| } |
| |
| |
| // Write a set of curves |
| static |
| cmsBool WriteSetOfCurves(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsTagTypeSignature Type, cmsStage* mpe) |
| { |
| cmsUInt32Number i, n; |
| cmsTagTypeSignature CurrentType; |
| cmsToneCurve** Curves; |
| |
| |
| n = cmsStageOutputChannels(mpe); |
| Curves = _cmsStageGetPtrToCurveSet(mpe); |
| |
| for (i=0; i < n; i++) { |
| |
| // If this is a table-based curve, use curve type even on V4 |
| CurrentType = Type; |
| |
| if ((Curves[i] ->nSegments == 0)|| |
| ((Curves[i]->nSegments == 2) && (Curves[i] ->Segments[1].Type == 0)) ) |
| CurrentType = cmsSigCurveType; |
| else |
| if (Curves[i] ->Segments[0].Type < 0) |
| CurrentType = cmsSigCurveType; |
| |
| if (!_cmsWriteTypeBase(io, CurrentType)) return FALSE; |
| |
| switch (CurrentType) { |
| |
| case cmsSigCurveType: |
| if (!Type_Curve_Write(self, io, Curves[i], 1)) return FALSE; |
| break; |
| |
| case cmsSigParametricCurveType: |
| if (!Type_ParametricCurve_Write(self, io, Curves[i], 1)) return FALSE; |
| break; |
| |
| default: |
| { |
| char String[5]; |
| |
| _cmsTagSignature2String(String, (cmsTagSignature) Type); |
| cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown curve type '%s'", String); |
| } |
| return FALSE; |
| } |
| |
| if (!_cmsWriteAlignment(io)) return FALSE; |
| } |
| |
| |
| return TRUE; |
| } |
| |
| |
| static |
| cmsBool WriteCLUT(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt8Number Precision, cmsStage* mpe) |
| { |
| cmsUInt8Number gridPoints[cmsMAXCHANNELS]; // Number of grid points in each dimension. |
| cmsUInt32Number i; |
| _cmsStageCLutData* CLUT = ( _cmsStageCLutData*) mpe -> Data; |
| |
| if (CLUT ->HasFloatValues) { |
| cmsSignalError(self ->ContextID, cmsERROR_NOT_SUITABLE, "Cannot save floating point data, CLUT are 8 or 16 bit only"); |
| return FALSE; |
| } |
| |
| memset(gridPoints, 0, sizeof(gridPoints)); |
| for (i=0; i < (cmsUInt32Number) CLUT ->Params ->nInputs; i++) |
| gridPoints[i] = (cmsUInt8Number) CLUT ->Params ->nSamples[i]; |
| |
| if (!io -> Write(io, cmsMAXCHANNELS*sizeof(cmsUInt8Number), gridPoints)) return FALSE; |
| |
| if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) Precision)) return FALSE; |
| if (!_cmsWriteUInt8Number(io, 0)) return FALSE; |
| if (!_cmsWriteUInt8Number(io, 0)) return FALSE; |
| if (!_cmsWriteUInt8Number(io, 0)) return FALSE; |
| |
| // Precision can be 1 or 2 bytes |
| if (Precision == 1) { |
| |
| for (i=0; i < CLUT->nEntries; i++) { |
| |
| if (!_cmsWriteUInt8Number(io, FROM_16_TO_8(CLUT->Tab.T[i]))) return FALSE; |
| } |
| } |
| else |
| if (Precision == 2) { |
| |
| if (!_cmsWriteUInt16Array(io, CLUT->nEntries, CLUT ->Tab.T)) return FALSE; |
| } |
| else { |
| cmsSignalError(self ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown precision of '%d'", Precision); |
| return FALSE; |
| } |
| |
| if (!_cmsWriteAlignment(io)) return FALSE; |
| |
| return TRUE; |
| } |
| |
| |
| |
| |
| static |
| cmsBool Type_LUTA2B_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) |
| { |
| cmsPipeline* Lut = (cmsPipeline*) Ptr; |
| cmsUInt32Number inputChan, outputChan; |
| cmsStage *A = NULL, *B = NULL, *M = NULL; |
| cmsStage * Matrix = NULL; |
| cmsStage * CLUT = NULL; |
| cmsUInt32Number offsetB = 0, offsetMat = 0, offsetM = 0, offsetC = 0, offsetA = 0; |
| cmsUInt32Number BaseOffset, DirectoryPos, CurrentPos; |
| |
| // Get the base for all offsets |
| BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); |
| |
| if (Lut ->Elements != NULL) |
| if (!cmsPipelineCheckAndRetreiveStages(Lut, 1, cmsSigCurveSetElemType, &B)) |
| if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigMatrixElemType, cmsSigCurveSetElemType, &M, &Matrix, &B)) |
| if (!cmsPipelineCheckAndRetreiveStages(Lut, 3, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, &A, &CLUT, &B)) |
| if (!cmsPipelineCheckAndRetreiveStages(Lut, 5, cmsSigCurveSetElemType, cmsSigCLutElemType, cmsSigCurveSetElemType, |
| cmsSigMatrixElemType, cmsSigCurveSetElemType, &A, &CLUT, &M, &Matrix, &B)) { |
| |
| cmsSignalError(self->ContextID, cmsERROR_NOT_SUITABLE, "LUT is not suitable to be saved as LutAToB"); |
| return FALSE; |
| } |
| |
| // Get input, output channels |
| inputChan = cmsPipelineInputChannels(Lut); |
| outputChan = cmsPipelineOutputChannels(Lut); |
| |
| // Write channel count |
| if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) inputChan)) return FALSE; |
| if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) outputChan)) return FALSE; |
| if (!_cmsWriteUInt16Number(io, 0)) return FALSE; |
| |
| // Keep directory to be filled latter |
| DirectoryPos = io ->Tell(io); |
| |
| // Write the directory |
| if (!_cmsWriteUInt32Number(io, 0)) return FALSE; |
| if (!_cmsWriteUInt32Number(io, 0)) return FALSE; |
| if (!_cmsWriteUInt32Number(io, 0)) return FALSE; |
| if (!_cmsWriteUInt32Number(io, 0)) return FALSE; |
| if (!_cmsWriteUInt32Number(io, 0)) return FALSE; |
| |
| if (A != NULL) { |
| |
| offsetA = io ->Tell(io) - BaseOffset; |
| if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, A)) return FALSE; |
| } |
| |
| if (CLUT != NULL) { |
| offsetC = io ->Tell(io) - BaseOffset; |
| if (!WriteCLUT(self, io, (Lut ->SaveAs8Bits ? 1U : 2U), CLUT)) return FALSE; |
| |
| } |
| if (M != NULL) { |
| |
| offsetM = io ->Tell(io) - BaseOffset; |
| if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, M)) return FALSE; |
| } |
| |
| if (Matrix != NULL) { |
| offsetMat = io ->Tell(io) - BaseOffset; |
| if (!WriteMatrix(self, io, Matrix)) return FALSE; |
| } |
| |
| if (B != NULL) { |
| |
| offsetB = io ->Tell(io) - BaseOffset; |
| if (!WriteSetOfCurves(self, io, cmsSigParametricCurveType, B)) return FALSE; |
| } |
| |
| CurrentPos = io ->Tell(io); |
| |
| if (!io ->Seek(io, DirectoryPos)) return FALSE; |
| |
| if (!_cmsWriteUInt32Number(io, offsetB)) return FALSE; |
| if (!_cmsWriteUInt32Number(io, offsetMat)) return FALSE; |
| if (!_cmsWriteUInt32Number(io, offsetM)) return FALSE; |
| if (!_cmsWriteUInt32Number(io, offsetC)) return FALSE; |
| if (!_cmsWriteUInt32Number(io, offsetA)) return FALSE; |
| |
| if (!io ->Seek(io, CurrentPos)) return FALSE; |
| |
| return TRUE; |
| |
| cmsUNUSED_PARAMETER(nItems); |
| } |
| |
| |
| static |
| void* Type_LUTA2B_Dup(struct _cms_typehandler_struct* self, const void *Ptr, cmsUInt32Number n) |
| { |
| return (void*) cmsPipelineDup((cmsPipeline*) Ptr); |
| |
| cmsUNUSED_PARAMETER(n); |
| cmsUNUSED_PARAMETER(self); |
| } |
| |
| static |
| void Type_LUTA2B_Free(struct _cms_typehandler_struct* self, void* Ptr) |
| { |
| cmsPipelineFree((cmsPipeline*) Ptr); |
| return; |
| |
<
|