blob: 06742b5ad6ce953c84d451ae218fef3c424779fc [file] [log] [blame]
//---------------------------------------------------------------------------------
//
// Little Color Management System
// Copyright (c) 1998-2014 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 appropiate 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 }
// 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;
}
// Auxiliar 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;
}
// Auxiliar 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 decribed 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;
// 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, _cmsDoubleTo15Fixed16(x))) return FALSE;
if (!_cmsWriteUInt32Number(io, _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_aligned, len_filler_alignment;
cmsBool rc = FALSE;
char Filler[68];
// Used below for writting zeroes
memset(Filler, 0, sizeof(Filler));
// Get the len of string
len = cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, NULL, 0);
// From ICC3.4: 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.
// Compute an aligned size
len_aligned = _cmsALIGNLONG(len);
len_filler_alignment = len_aligned - len;
// 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));
}
// * 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_aligned)) goto Error;
if (!io ->Write(io, len, Text)) goto Error;
if (!io ->Write(io, len_filler_alignment, Filler)) goto Error;
if (!_cmsWriteUInt32Number(io, 0)) goto Error; // ucLanguageCode
// This part is tricky: we need an aligned tag size, and the ScriptCode part
// takes 70 bytes, so we need 2 extra bytes to do the alignment
if (!_cmsWriteUInt32Number(io, len_aligned+1)) goto Error;
// Note that in some compilers sizeof(cmsUInt16Number) != sizeof(wchar_t)
if (!_cmsWriteWCharArray(io, len, Wide)) goto Error;
if (!_cmsWriteUInt16Array(io, len_filler_alignment+1, (cmsUInt16Number*) Filler)) 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;
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)) 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 writting
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, &timestamp, sizeof(cmsDateTimeNumber), 1) != 1) return NULL;
_cmsDecodeDateTimeNumber(&timestamp, 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(&timestamp, DateTime);
if (!io ->Write(io, sizeof(cmsDateTimeNumber), &timestamp)) 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;
// 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. Substract 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;
int 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 writting
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, int nChannels)
{
cmsUInt8Number* Temp = NULL;
int 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++) {
if (Tables != NULL)
val = (cmsUInt8Number) FROM_16_TO_8(Tables->TheCurves[i]->Table16[j]);
else
val = (cmsUInt8Number) 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 > cmsMAXCHANNELS) goto Error;
if (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)))
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;
int 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, int nChannels, int nEntries)
{
int 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)
{
int j;
cmsUInt32Number i;
cmsUInt16Number val;
int 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 > cmsMAXCHANNELS) goto Error;
if (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;
int 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, int InputChannels, int 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) 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;
// 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