8321489: Update LCMS to 2.16

Reviewed-by: serb, dnguyen, prr
This commit is contained in:
Alisen Chung 2024-01-12 22:16:09 +00:00
parent 84cf4cb350
commit dc7d3b182d
21 changed files with 1495 additions and 389 deletions

@ -1,34 +1,29 @@
## Little Color Management System (LCMS) v2.15
## Little Color Management System (LCMS) v2.16
### LCMS License
<pre>
README.1ST file information
LittleCMS core is released under MIT License
MIT License
---------------------------------
Little CMS
Copyright (c) 1998-2023 Marti Maria Saguer
Copyright (C) 1998-2023 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:
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 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.
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.
---------------------------------
The below license applies to the following files:
@ -47,7 +42,6 @@ Users of this code must verify correctness for their application.
### AUTHORS File Information
```
Main Author
------------
Marti Maria
@ -91,6 +85,7 @@ Philipp Knechtges
Amyspark
Lovell Fuller
Eli Schwartz
Diogo Teles Sant'Anna
Special Thanks
--------------

@ -431,7 +431,7 @@ static cmsFormatterAlphaFn FormattersAlpha[6][6] = {
// This function computes the distance from each component to the next one in bytes.
static
void ComputeIncrementsForChunky(cmsUInt32Number Format,
cmsBool ComputeIncrementsForChunky(cmsUInt32Number Format,
cmsUInt32Number ComponentStartingOrder[],
cmsUInt32Number ComponentPointerIncrements[])
{
@ -445,7 +445,7 @@ void ComputeIncrementsForChunky(cmsUInt32Number Format,
// Sanity check
if (total_chans <= 0 || total_chans >= cmsMAXCHANNELS)
return;
return FALSE;
memset(channels, 0, sizeof(channels));
@ -482,13 +482,15 @@ void ComputeIncrementsForChunky(cmsUInt32Number Format,
for (i = 0; i < extra; i++)
ComponentStartingOrder[i] = channels[i + nchannels];
return TRUE;
}
// On planar configurations, the distance is the stride added to any non-negative
static
void ComputeIncrementsForPlanar(cmsUInt32Number Format,
cmsBool ComputeIncrementsForPlanar(cmsUInt32Number Format,
cmsUInt32Number BytesPerPlane,
cmsUInt32Number ComponentStartingOrder[],
cmsUInt32Number ComponentPointerIncrements[])
@ -502,7 +504,7 @@ void ComputeIncrementsForPlanar(cmsUInt32Number Format,
// Sanity check
if (total_chans <= 0 || total_chans >= cmsMAXCHANNELS)
return;
return FALSE;
memset(channels, 0, sizeof(channels));
@ -538,29 +540,29 @@ void ComputeIncrementsForPlanar(cmsUInt32Number Format,
for (i = 0; i < extra; i++)
ComponentStartingOrder[i] = channels[i + nchannels];
return TRUE;
}
// Dispatcher por chunky and planar RGB
static
void ComputeComponentIncrements(cmsUInt32Number Format,
cmsBool ComputeComponentIncrements(cmsUInt32Number Format,
cmsUInt32Number BytesPerPlane,
cmsUInt32Number ComponentStartingOrder[],
cmsUInt32Number ComponentPointerIncrements[])
{
if (T_PLANAR(Format)) {
ComputeIncrementsForPlanar(Format, BytesPerPlane, ComponentStartingOrder, ComponentPointerIncrements);
return ComputeIncrementsForPlanar(Format, BytesPerPlane, ComponentStartingOrder, ComponentPointerIncrements);
}
else {
ComputeIncrementsForChunky(Format, ComponentStartingOrder, ComponentPointerIncrements);
return ComputeIncrementsForChunky(Format, ComponentStartingOrder, ComponentPointerIncrements);
}
}
// Handles extra channels copying alpha if requested by the flags
void _cmsHandleExtraChannels(_cmsTRANSFORM* p, const void* in,
void* out,
@ -595,8 +597,10 @@ void _cmsHandleExtraChannels(_cmsTRANSFORM* p, const void* in,
return;
// Compute the increments
ComputeComponentIncrements(p->InputFormat, Stride->BytesPerPlaneIn, SourceStartingOrder, SourceIncrements);
ComputeComponentIncrements(p->OutputFormat, Stride->BytesPerPlaneOut, DestStartingOrder, DestIncrements);
if (!ComputeComponentIncrements(p->InputFormat, Stride->BytesPerPlaneIn, SourceStartingOrder, SourceIncrements))
return;
if (!ComputeComponentIncrements(p->OutputFormat, Stride->BytesPerPlaneOut, DestStartingOrder, DestIncrements))
return;
// Check for conversions 8, 16, half, float, dbl
copyValueFn = _cmsGetFormatterAlpha(p->ContextID, p->InputFormat, p->OutputFormat);

@ -87,7 +87,7 @@ typedef enum {
SEOF, // End of stream
SSYNERROR, // Syntax error found on stream
// Keywords
// IT8 symbols
SBEGIN_DATA,
SBEGIN_DATA_FORMAT,
@ -95,7 +95,19 @@ typedef enum {
SEND_DATA_FORMAT,
SKEYWORD,
SDATA_FORMAT_ID,
SINCLUDE
SINCLUDE,
// Cube symbols
SDOMAIN_MAX,
SDOMAIN_MIN,
S_LUT1D_SIZE,
S_LUT1D_INPUT_RANGE,
S_LUT3D_SIZE,
S_LUT3D_INPUT_RANGE,
S_LUT_IN_VIDEO_RANGE,
S_LUT_OUT_VIDEO_RANGE,
STITLE
} SYMBOL;
@ -178,6 +190,10 @@ typedef struct struct_it8 {
cmsUInt32Number TablesCount; // How many tables in this stream
cmsUInt32Number nTable; // The actual table
// Partser type
cmsBool IsCUBE;
// Tables
TABLE Tab[MAXTABLES];
// Memory management
@ -237,8 +253,8 @@ typedef struct {
} KEYWORD;
// The keyword->symbol translation table. Sorting is required.
static const KEYWORD TabKeys[] = {
// The keyword->symbol translation tables. Sorting is required.
static const KEYWORD TabKeysIT8[] = {
{"$INCLUDE", SINCLUDE}, // This is an extension!
{".INCLUDE", SINCLUDE}, // This is an extension!
@ -251,7 +267,25 @@ static const KEYWORD TabKeys[] = {
{"KEYWORD", SKEYWORD}
};
#define NUMKEYS (sizeof(TabKeys)/sizeof(KEYWORD))
#define NUMKEYS_IT8 (sizeof(TabKeysIT8)/sizeof(KEYWORD))
static const KEYWORD TabKeysCUBE[] = {
{"DOMAIN_MAX", SDOMAIN_MAX },
{"DOMAIN_MIN", SDOMAIN_MIN },
{"LUT_1D_SIZE", S_LUT1D_SIZE },
{"LUT_1D_INPUT_RANGE", S_LUT1D_INPUT_RANGE },
{"LUT_3D_SIZE", S_LUT3D_SIZE },
{"LUT_3D_INPUT_RANGE", S_LUT3D_INPUT_RANGE },
{"LUT_IN_VIDEO_RANGE", S_LUT_IN_VIDEO_RANGE },
{"LUT_OUT_VIDEO_RANGE", S_LUT_OUT_VIDEO_RANGE },
{"TITLE", STITLE }
};
#define NUMKEYS_CUBE (sizeof(TabKeysCUBE)/sizeof(KEYWORD))
// Predefined properties
@ -455,7 +489,7 @@ void StringCat(string* s, const char* c)
static
cmsBool isseparator(int c)
{
return (c == ' ') || (c == '\t') ;
return (c == ' ') || (c == '\t');
}
// Checks whatever c is a valid identifier char
@ -476,7 +510,7 @@ cmsBool isidchar(int c)
static
cmsBool isfirstidchar(int c)
{
return !isdigit(c) && ismiddle(c);
return c != '-' && !isdigit(c) && ismiddle(c);
}
// Guess whether the supplied path looks like an absolute path
@ -515,13 +549,13 @@ cmsBool BuildAbsolutePath(const char *relPath, const char *basePath, char *buffe
// Already absolute?
if (isabsolutepath(relPath)) {
strncpy(buffer, relPath, MaxLen);
memcpy(buffer, relPath, MaxLen);
buffer[MaxLen-1] = 0;
return TRUE;
}
// No, search for last
strncpy(buffer, basePath, MaxLen);
memcpy(buffer, basePath, MaxLen);
buffer[MaxLen-1] = 0;
tail = strrchr(buffer, DIR_CHAR);
@ -603,10 +637,10 @@ void NextCh(cmsIT8* it8)
// Try to see if current identifier is a keyword, if so return the referred symbol
static
SYMBOL BinSrchKey(const char *id)
SYMBOL BinSrchKey(const char *id, int NumKeys, const KEYWORD* TabKeys)
{
int l = 1;
int r = NUMKEYS;
int r = NumKeys;
int x, res;
while (r >= l)
@ -776,7 +810,7 @@ cmsFloat64Number ParseFloatNumber(const char *Buffer)
}
// Reads a string, special case to avoid infinite resursion on .include
// Reads a string, special case to avoid infinite recursion on .include
static
void InStringSymbol(cmsIT8* it8)
{
@ -833,7 +867,9 @@ void InSymbol(cmsIT8* it8)
} while (isidchar(it8->ch));
key = BinSrchKey(StringPtr(it8->id));
key = BinSrchKey(StringPtr(it8->id),
it8->IsCUBE ? NUMKEYS_CUBE : NUMKEYS_IT8,
it8->IsCUBE ? TabKeysCUBE : TabKeysIT8);
if (key == SUNDEFINED) it8->sy = SIDENT;
else it8->sy = key;
@ -942,6 +978,7 @@ void InSymbol(cmsIT8* it8)
snprintf(buffer, sizeof(buffer), it8 ->DoubleFormatter, it8->dnum);
}
StringClear(it8->id);
StringCat(it8->id, buffer);
do {
@ -971,7 +1008,7 @@ void InSymbol(cmsIT8* it8)
// Next line
case '\r':
NextCh(it8);
if (it8 ->ch == '\n')
if (it8->ch == '\n')
NextCh(it8);
it8->sy = SEOLN;
it8->lineno++;
@ -1292,7 +1329,12 @@ KEYVALUE* AddToList(cmsIT8* it8, KEYVALUE** Head, const char *Key, const char *S
// This may work for editing properties
// return SynError(it8, "duplicate key <%s>", Key);
if (cmsstrcasecmp(Key, "NUMBER_OF_FIELDS") == 0 ||
cmsstrcasecmp(Key, "NUMBER_OF_SETS") == 0) {
SynError(it8, "duplicate key <%s>", Key);
return NULL;
}
}
else {
@ -1413,6 +1455,8 @@ cmsHANDLE CMSEXPORT cmsIT8Alloc(cmsContext ContextID)
it8->MemoryBlock = NULL;
it8->MemorySink = NULL;
it8->IsCUBE = FALSE;
it8 ->nTable = 0;
it8->ContextID = ContextID;
@ -1694,7 +1738,7 @@ char* GetData(cmsIT8* it8, int nSet, int nField)
int nSamples = t -> nSamples;
int nPatches = t -> nPatches;
if (nSet >= nPatches || nField >= nSamples)
if (nSet < 0 || nSet >= nPatches || nField < 0 || nField >= nSamples)
return NULL;
if (!t->Data) return NULL;
@ -1879,11 +1923,14 @@ void WriteDataFormat(SAVESTREAM* fp, cmsIT8* it8)
WriteStr(fp, " ");
nSamples = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
for (i = 0; i < nSamples; i++) {
if (nSamples <= t->nSamples) {
WriteStr(fp, t->DataFormat[i]);
WriteStr(fp, ((i == (nSamples-1)) ? "\n" : "\t"));
}
for (i = 0; i < nSamples; i++) {
WriteStr(fp, t->DataFormat[i]);
WriteStr(fp, ((i == (nSamples - 1)) ? "\n" : "\t"));
}
}
WriteStr (fp, "END_DATA_FORMAT\n");
}
@ -1893,39 +1940,42 @@ void WriteDataFormat(SAVESTREAM* fp, cmsIT8* it8)
static
void WriteData(SAVESTREAM* fp, cmsIT8* it8)
{
int i, j;
int i, j, nPatches;
TABLE* t = GetTable(it8);
if (!t->Data) return;
WriteStr (fp, "BEGIN_DATA\n");
t->nPatches = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS"));
nPatches = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS"));
for (i = 0; i < t-> nPatches; i++) {
if (nPatches <= t->nPatches) {
WriteStr(fp, " ");
for (i = 0; i < nPatches; i++) {
for (j = 0; j < t->nSamples; j++) {
WriteStr(fp, " ");
char *ptr = t->Data[i*t->nSamples+j];
for (j = 0; j < t->nSamples; j++) {
if (ptr == NULL) WriteStr(fp, "\"\"");
else {
// If value contains whitespace, enclose within quote
char* ptr = t->Data[i * t->nSamples + j];
if (strchr(ptr, ' ') != NULL) {
if (ptr == NULL) WriteStr(fp, "\"\"");
else {
// If value contains whitespace, enclose within quote
WriteStr(fp, "\"");
WriteStr(fp, ptr);
WriteStr(fp, "\"");
}
else
WriteStr(fp, ptr);
}
if (strchr(ptr, ' ') != NULL) {
WriteStr(fp, ((j == (t->nSamples-1)) ? "\n" : "\t"));
}
WriteStr(fp, "\"");
WriteStr(fp, ptr);
WriteStr(fp, "\"");
}
else
WriteStr(fp, ptr);
}
WriteStr(fp, ((j == (t->nSamples - 1)) ? "\n" : "\t"));
}
}
}
WriteStr (fp, "END_DATA\n");
}
@ -1946,15 +1996,29 @@ cmsBool CMSEXPORT cmsIT8SaveToFile(cmsHANDLE hIT8, const char* cFileName)
for (i=0; i < it8 ->TablesCount; i++) {
cmsIT8SetTable(hIT8, i);
WriteHeader(it8, &sd);
WriteDataFormat(&sd, it8);
WriteData(&sd, it8);
TABLE* t;
if (cmsIT8SetTable(hIT8, i) < 0) goto Error;
/**
* Check for wrong data
*/
t = GetTable(it8);
if (t->Data == NULL) goto Error;
if (t->DataFormat == NULL) goto Error;
WriteHeader(it8, &sd);
WriteDataFormat(&sd, it8);
WriteData(&sd, it8);
}
if (fclose(sd.stream) != 0) return FALSE;
return TRUE;
Error:
fclose(sd.stream);
return FALSE;
}
@ -2331,78 +2395,72 @@ void CookPointers(cmsIT8* it8)
int idField, i;
char* Fld;
cmsUInt32Number j;
cmsUInt32Number nOldTable = it8 ->nTable;
cmsUInt32Number nOldTable = it8->nTable;
for (j=0; j < it8 ->TablesCount; j++) {
for (j = 0; j < it8->TablesCount; j++) {
TABLE* t = it8 ->Tab + j;
TABLE* t = it8->Tab + j;
t -> SampleID = 0;
it8 ->nTable = j;
for (idField = 0; idField < t -> nSamples; idField++)
{
if (t ->DataFormat == NULL){
SynError(it8, "Undefined DATA_FORMAT");
return;
}
Fld = t->DataFormat[idField];
if (!Fld) continue;
if (cmsstrcasecmp(Fld, "SAMPLE_ID") == 0) {
t -> SampleID = idField;
}
// "LABEL" is an extension. It keeps references to forward tables
if ((cmsstrcasecmp(Fld, "LABEL") == 0) || Fld[0] == '$') {
// Search for table references...
for (i = 0; i < t->nPatches; i++) {
char* Label = GetData(it8, i, idField);
if (Label) {
cmsUInt32Number k;
// This is the label, search for a table containing
// this property
for (k = 0; k < it8->TablesCount; k++) {
TABLE* Table = it8->Tab + k;
KEYVALUE* p;
if (IsAvailableOnList(Table->HeaderList, Label, NULL, &p)) {
// Available, keep type and table
char Buffer[256];
char* Type = p->Value;
int nTable = (int)k;
snprintf(Buffer, 255, "%s %d %s", Label, nTable, Type);
SetData(it8, i, idField, Buffer);
}
}
}
t->SampleID = 0;
it8->nTable = j;
for (idField = 0; idField < t->nSamples; idField++)
{
if (t->DataFormat == NULL) {
SynError(it8, "Undefined DATA_FORMAT");
return;
}
Fld = t->DataFormat[idField];
if (!Fld) continue;
if (cmsstrcasecmp(Fld, "SAMPLE_ID") == 0) {
t->SampleID = idField;
}
// "LABEL" is an extension. It keeps references to forward tables
if ((cmsstrcasecmp(Fld, "LABEL") == 0) || Fld[0] == '$') {
// Search for table references...
for (i = 0; i < t->nPatches; i++) {
char* Label = GetData(it8, i, idField);
if (Label) {
cmsUInt32Number k;
// This is the label, search for a table containing
// this property
for (k = 0; k < it8->TablesCount; k++) {
TABLE* Table = it8->Tab + k;
KEYVALUE* p;
if (IsAvailableOnList(Table->HeaderList, Label, NULL, &p)) {
// Available, keep type and table
char Buffer[256];
char* Type = p->Value;
int nTable = (int)k;
snprintf(Buffer, 255, "%s %d %s", Label, nTable, Type);
SetData(it8, i, idField, Buffer);
}
}
}
}
}
}
}
}
it8 ->nTable = nOldTable;
it8->nTable = nOldTable;
}
// Try to infere if the file is a CGATS/IT8 file at all. Read first line
@ -2493,7 +2551,7 @@ cmsHANDLE CMSEXPORT cmsIT8LoadFromMem(cmsContext ContextID, const void *Ptr, cm
if (it8->MemoryBlock == NULL)
{
cmsIT8Free(hIT8);
return FALSE;
return NULL;
}
strncpy(it8 ->MemoryBlock, (const char*) Ptr, len);
@ -2505,7 +2563,7 @@ cmsHANDLE CMSEXPORT cmsIT8LoadFromMem(cmsContext ContextID, const void *Ptr, cm
if (!ParseIT8(it8, type-1)) {
cmsIT8Free(hIT8);
return FALSE;
return NULL;
}
CookPointers(it8);
@ -2602,17 +2660,17 @@ cmsUInt32Number CMSEXPORT cmsIT8EnumProperties(cmsHANDLE hIT8, char ***PropertyN
}
Props = (char**)AllocChunk(it8, sizeof(char*) * n);
if (Props != NULL) {
// Pass#2 - Fill pointers
n = 0;
for (p = t->HeaderList; p != NULL; p = p->Next) {
Props[n++] = p->Keyword;
}
Props = (char**)AllocChunk(it8, sizeof(char*) * n);
if (Props != NULL) {
// Pass#2 - Fill pointers
n = 0;
for (p = t->HeaderList; p != NULL; p = p->Next) {
Props[n++] = p->Keyword;
}
*PropertyNames = Props;
}
*PropertyNames = Props;
return n;
}
@ -2972,3 +3030,236 @@ void CMSEXPORT cmsIT8DefineDblFormat(cmsHANDLE hIT8, const char* Formatter)
it8 ->DoubleFormatter[sizeof(it8 ->DoubleFormatter)-1] = 0;
}
static
cmsBool ReadNumbers(cmsIT8* cube, int n, cmsFloat64Number* arr)
{
int i;
for (i = 0; i < n; i++) {
if (cube->sy == SINUM)
arr[i] = cube->inum;
else
if (cube->sy == SDNUM)
arr[i] = cube->dnum;
else
return SynError(cube, "Number expected");
InSymbol(cube);
}
return CheckEOLN(cube);
}
static
cmsBool ParseCube(cmsIT8* cube, cmsStage** Shaper, cmsStage** CLUT, char title[])
{
cmsFloat64Number domain_min[3] = { 0, 0, 0 };
cmsFloat64Number domain_max[3] = { 1.0, 1.0, 1.0 };
cmsFloat64Number check_0_1[2] = { 0, 1.0 };
int shaper_size = 0;
int lut_size = 0;
int i;
InSymbol(cube);
while (cube->sy != SEOF) {
switch (cube->sy)
{
// Set profile description
case STITLE:
InSymbol(cube);
if (!Check(cube, SSTRING, "Title string expected")) return FALSE;
memcpy(title, StringPtr(cube->str), MAXSTR);
title[MAXSTR - 1] = 0;
InSymbol(cube);
break;
// Define domain
case SDOMAIN_MIN:
InSymbol(cube);
if (!ReadNumbers(cube, 3, domain_min)) return FALSE;
break;
case SDOMAIN_MAX:
InSymbol(cube);
if (!ReadNumbers(cube, 3, domain_max)) return FALSE;
break;
// Define shaper
case S_LUT1D_SIZE:
InSymbol(cube);
if (!Check(cube, SINUM, "Shaper size expected")) return FALSE;
shaper_size = cube->inum;
InSymbol(cube);
break;
// Deefine CLUT
case S_LUT3D_SIZE:
InSymbol(cube);
if (!Check(cube, SINUM, "LUT size expected")) return FALSE;
lut_size = cube->inum;
InSymbol(cube);
break;
// Range. If present, has to be 0..1.0
case S_LUT1D_INPUT_RANGE:
case S_LUT3D_INPUT_RANGE:
InSymbol(cube);
if (!ReadNumbers(cube, 2, check_0_1)) return FALSE;
if (check_0_1[0] != 0 || check_0_1[1] != 1.0) {
return SynError(cube, "Unsupported format");
}
break;
case SEOLN:
InSymbol(cube);
break;
default:
case S_LUT_IN_VIDEO_RANGE:
case S_LUT_OUT_VIDEO_RANGE:
return SynError(cube, "Unsupported format");
// Read and create tables
case SINUM:
case SDNUM:
if (shaper_size > 0) {
cmsToneCurve* curves[3];
cmsFloat32Number* shapers = (cmsFloat32Number*)_cmsMalloc(cube->ContextID, 3 * shaper_size * sizeof(cmsFloat32Number));
if (shapers == NULL) return FALSE;
for (i = 0; i < shaper_size; i++) {
cmsFloat64Number nums[3];
if (!ReadNumbers(cube, 3, nums)) return FALSE;
shapers[i + 0] = (cmsFloat32Number) ((nums[0] - domain_min[0]) / (domain_max[0] - domain_min[0]));
shapers[i + 1 * shaper_size] = (cmsFloat32Number) ((nums[1] - domain_min[1]) / (domain_max[1] - domain_min[1]));
shapers[i + 2 * shaper_size] = (cmsFloat32Number) ((nums[2] - domain_min[2]) / (domain_max[2] - domain_min[2]));
}
for (i = 0; i < 3; i++) {
curves[i] = cmsBuildTabulatedToneCurveFloat(cube->ContextID, shaper_size,
&shapers[i * shaper_size]);
if (curves[i] == NULL) return FALSE;
}
*Shaper = cmsStageAllocToneCurves(cube->ContextID, 3, curves);
cmsFreeToneCurveTriple(curves);
}
if (lut_size > 0) {
int nodes = lut_size * lut_size * lut_size;
cmsFloat32Number* lut_table = _cmsMalloc(cube->ContextID, nodes * 3 * sizeof(cmsFloat32Number));
if (lut_table == NULL) return FALSE;
for (i = 0; i < nodes; i++) {
cmsFloat64Number nums[3];
if (!ReadNumbers(cube, 3, nums)) return FALSE;
lut_table[i * 3 + 2] = (cmsFloat32Number) ((nums[0] - domain_min[0]) / (domain_max[0] - domain_min[0]));
lut_table[i * 3 + 1] = (cmsFloat32Number) ((nums[1] - domain_min[1]) / (domain_max[1] - domain_min[1]));
lut_table[i * 3 + 0] = (cmsFloat32Number) ((nums[2] - domain_min[2]) / (domain_max[2] - domain_min[2]));
}
*CLUT = cmsStageAllocCLutFloat(cube->ContextID, lut_size, 3, 3, lut_table);
_cmsFree(cube->ContextID, lut_table);
}
if (!Check(cube, SEOF, "Extra symbols found in file")) return FALSE;
}
}
return TRUE;
}
// Share the parser to read .cube format and create RGB devicelink profiles
cmsHPROFILE CMSEXPORT cmsCreateDeviceLinkFromCubeFileTHR(cmsContext ContextID, const char* cFileName)
{
cmsHPROFILE hProfile = NULL;
cmsIT8* cube = NULL;
cmsPipeline* Pipeline = NULL;
cmsStage* CLUT = NULL;
cmsStage* Shaper = NULL;
cmsMLU* DescriptionMLU = NULL;
char title[MAXSTR];
_cmsAssert(cFileName != NULL);
cube = (cmsIT8*) cmsIT8Alloc(ContextID);
if (!cube) return NULL;
cube->IsCUBE = TRUE;
cube->FileStack[0]->Stream = fopen(cFileName, "rt");
if (!cube->FileStack[0]->Stream) goto Done;
strncpy(cube->FileStack[0]->FileName, cFileName, cmsMAX_PATH - 1);
cube->FileStack[0]->FileName[cmsMAX_PATH - 1] = 0;
if (!ParseCube(cube, &Shaper, &CLUT, title)) goto Done;
// Success on parsing, let's create the profile
hProfile = cmsCreateProfilePlaceholder(ContextID);
if (!hProfile) goto Done;
cmsSetProfileVersion(hProfile, 4.4);
cmsSetDeviceClass(hProfile, cmsSigLinkClass);
cmsSetColorSpace(hProfile, cmsSigRgbData);
cmsSetPCS(hProfile, cmsSigRgbData);
cmsSetHeaderRenderingIntent(hProfile, INTENT_PERCEPTUAL);
// Creates a Pipeline to hold CLUT and shaper
Pipeline = cmsPipelineAlloc(ContextID, 3, 3);
if (Pipeline == NULL) goto Done;
// Populates the pipeline
if (Shaper != NULL) {
if (!cmsPipelineInsertStage(Pipeline, cmsAT_BEGIN, Shaper))
goto Done;
}
if (CLUT != NULL) {
if (!cmsPipelineInsertStage(Pipeline, cmsAT_END, CLUT))
goto Done;
}
// Propagate the description. We put no copyright because we know
// nothing on the copyrighted state of the .cube
DescriptionMLU = cmsMLUalloc(ContextID, 1);
if (!cmsMLUsetUTF8(DescriptionMLU, cmsNoLanguage, cmsNoCountry, title)) goto Done;
// Flush the tags
if (!cmsWriteTag(hProfile, cmsSigProfileDescriptionTag, DescriptionMLU)) goto Done;
if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, (void*)Pipeline)) goto Done;
Done:
if (DescriptionMLU != NULL)
cmsMLUfree(DescriptionMLU);
if (Pipeline != NULL)
cmsPipelineFree(Pipeline);
cmsIT8Free((cmsHANDLE) cube);
return hProfile;
}
cmsHPROFILE CMSEXPORT cmsCreateDeviceLinkFromCubeFile(const char* cFileName)
{
return cmsCreateDeviceLinkFromCubeFileTHR(NULL, cFileName);
}

@ -263,7 +263,7 @@ cmsFloat64Number CHAD2Temp(const cmsMAT3* Chad)
// Compute a CHAD based on a given temperature
static
void Temp2CHAD(cmsMAT3* Chad, cmsFloat64Number Temp)
void Temp2CHAD(cmsMAT3* Chad, cmsFloat64Number Temp)
{
cmsCIEXYZ White;
cmsCIExyY ChromaticityOfWhite;
@ -744,6 +744,16 @@ int BlackPreservingGrayOnlySampler(CMSREGISTER const cmsUInt16Number In[], CMSRE
return TRUE;
}
// Check whatever the profile is a CMYK->CMYK devicelink
static
cmsBool is_cmyk_devicelink(cmsHPROFILE hProfile)
{
return cmsGetDeviceClass(hProfile) == cmsSigLinkClass &&
cmsGetColorSpace(hProfile) == cmsSigCmykData &&
cmsGetColorSpace(hProfile) == cmsSigCmykData;
}
// This is the entry for black-preserving K-only intents, which are non-ICC
static
cmsPipeline* BlackPreservingKOnlyIntents(cmsContext ContextID,
@ -776,14 +786,16 @@ cmsPipeline* BlackPreservingKOnlyIntents(cmsContext ContextID,
lastProfilePos = nProfiles - 1;
hLastProfile = hProfiles[lastProfilePos];
while (lastProfilePos > 1)
// Skip CMYK->CMYK devicelinks on ending
while (is_cmyk_devicelink(hLastProfile))
{
hLastProfile = hProfiles[--lastProfilePos];
if (cmsGetColorSpace(hLastProfile) != cmsSigCmykData ||
cmsGetDeviceClass(hLastProfile) != cmsSigLinkClass)
if (lastProfilePos < 2)
break;
hLastProfile = hProfiles[--lastProfilePos];
}
preservationProfilesCount = lastProfilePos + 1;
// Check for non-cmyk profiles
@ -800,7 +812,7 @@ cmsPipeline* BlackPreservingKOnlyIntents(cmsContext ContextID,
// Create a LUT holding normal ICC transform
bp.cmyk2cmyk = DefaultICCintents(ContextID,
preservationProfilesCount,
preservationProfilesCount,
ICCIntents,
hProfiles,
BPC,
@ -812,7 +824,7 @@ cmsPipeline* BlackPreservingKOnlyIntents(cmsContext ContextID,
// Now, compute the tone curve
bp.KTone = _cmsBuildKToneCurve(ContextID,
4096,
preservationProfilesCount,
preservationProfilesCount,
ICCIntents,
hProfiles,
BPC,
@ -1002,12 +1014,13 @@ cmsPipeline* BlackPreservingKPlaneIntents(cmsContext ContextID,
lastProfilePos = nProfiles - 1;
hLastProfile = hProfiles[lastProfilePos];
while (lastProfilePos > 1)
// Skip CMYK->CMYK devicelinks on ending
while (is_cmyk_devicelink(hLastProfile))
{
hLastProfile = hProfiles[--lastProfilePos];
if (cmsGetColorSpace(hLastProfile) != cmsSigCmykData ||
cmsGetDeviceClass(hLastProfile) != cmsSigLinkClass)
if (lastProfilePos < 2)
break;
hLastProfile = hProfiles[--lastProfilePos];
}
preservationProfilesCount = lastProfilePos + 1;
@ -1177,20 +1190,6 @@ cmsUInt32Number CMSEXPORT cmsGetSupportedIntentsTHR(cmsContext ContextID, cmsUIn
cmsIntentsList* pt;
cmsUInt32Number nIntents;
for (nIntents=0, pt = ctx->Intents; pt != NULL; pt = pt -> Next)
{
if (nIntents < nMax) {
if (Codes != NULL)
Codes[nIntents] = pt ->Intent;
if (Descriptions != NULL)
Descriptions[nIntents] = pt ->Description;
}
nIntents++;
}
for (nIntents=0, pt = DefaultIntents; pt != NULL; pt = pt -> Next)
{
if (nIntents < nMax) {
@ -1203,6 +1202,20 @@ cmsUInt32Number CMSEXPORT cmsGetSupportedIntentsTHR(cmsContext ContextID, cmsUIn
nIntents++;
}
for (pt = ctx->Intents; pt != NULL; pt = pt -> Next)
{
if (nIntents < nMax) {
if (Codes != NULL)
Codes[nIntents] = pt ->Intent;
if (Descriptions != NULL)
Descriptions[nIntents] = pt ->Description;
}
nIntents++;
}
return nIntents;
}

@ -101,7 +101,7 @@ long int CMSEXPORT cmsfilelength(FILE* f)
//
// This is the interface to low-level memory management routines. By default a simple
// wrapping to malloc/free/realloc is provided, although there is a limit on the max
// amount of memoy that can be reclaimed. This is mostly as a safety feature to prevent
// amount of memory that can be reclaimed. This is mostly as a safety feature to prevent
// bogus or evil code to allocate huge blocks that otherwise lcms would never need.
#define MAX_MEMORY_FOR_ALLOC ((cmsUInt32Number)(1024U*1024U*512U))
@ -121,7 +121,8 @@ cmsBool _cmsRegisterMemHandlerPlugin(cmsContext ContextID, cmsPluginBase* Plug
static
void* _cmsMallocDefaultFn(cmsContext ContextID, cmsUInt32Number size)
{
if (size > MAX_MEMORY_FOR_ALLOC) return NULL; // Never allow over maximum
// Never allow 0 or over maximum
if (size == 0 || size > MAX_MEMORY_FOR_ALLOC) return NULL;
return (void*) malloc(size);
@ -263,7 +264,7 @@ cmsBool _cmsRegisterMemHandlerPlugin(cmsContext ContextID, cmsPluginBase *Data)
// NULL forces to reset to defaults. In this special case, the defaults are stored in the context structure.
// Remaining plug-ins does NOT have any copy in the context structure, but this is somehow special as the
// context internal data should be malloce'd by using those functions.
// context internal data should be malloc'ed by using those functions.
if (Data == NULL) {
struct _cmsContext_struct* ctx = ( struct _cmsContext_struct*) ContextID;

@ -329,6 +329,10 @@ cmsToneCurve* AllocateToneCurveStruct(cmsContext ContextID, cmsUInt32Number nEnt
return p;
Error:
for (i=0; i < nSegments; i++) {
if (p ->Segments && p ->Segments[i].SampledPoints) _cmsFree(ContextID, p ->Segments[i].SampledPoints);
if (p ->SegInterp && p ->SegInterp[i]) _cmsFree(ContextID, p ->SegInterp[i]);
}
if (p -> SegInterp) _cmsFree(ContextID, p -> SegInterp);
if (p -> Segments) _cmsFree(ContextID, p -> Segments);
if (p -> Evals) _cmsFree(ContextID, p -> Evals);
@ -622,10 +626,16 @@ cmsFloat64Number DefaultEvalParametricFn(cmsInt32Number Type, const cmsFloat64Nu
case 6:
e = Params[1]*R + Params[2];
if (e < 0)
Val = Params[3];
else
Val = pow(e, Params[0]) + Params[3];
// On gamma 1.0, don't clamp
if (Params[0] == 1.0) {
Val = e + Params[3];
}
else {
if (e < 0)
Val = Params[3];
else
Val = pow(e, Params[0]) + Params[3];
}
break;
// ((Y - c) ^1/Gamma - b) / a
@ -1520,13 +1530,13 @@ cmsFloat64Number CMSEXPORT cmsEstimateGamma(const cmsToneCurve* t, cmsFloat64Num
return (sum / n); // The mean
}
// Retrieve segments on tone curves
// Retrieve parameters on one-segment tone curves
cmsFloat64Number* CMSEXPORT cmsGetToneCurveParams(const cmsToneCurve* t)
const cmsCurveSegment* CMSEXPORT cmsGetToneCurveSegment(cmsInt32Number n, const cmsToneCurve* t)
{
_cmsAssert(t != NULL);
if (t->nSegments != 1) return NULL;
return t->Segments[0].Params;
if (n < 0 || n >= (cmsInt32Number) t->nSegments) return NULL;
return t->Segments + n;
}

@ -248,7 +248,7 @@ int GamutSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Nu
cmsUInt16Number Proof[cmsMAXCHANNELS], Proof2[cmsMAXCHANNELS];
cmsFloat64Number dE1, dE2, ErrorRatio;
// Assume in-gamut by default.
// Assume in-gamut by default. NEVER READ, USED FOR DEBUG PURPOSES.
ErrorRatio = 1.0;
// Convert input to Lab
@ -625,7 +625,7 @@ cmsBool CMSEXPORT cmsDesaturateLab(cmsCIELab* Lab,
// Actually, doing that "well" is quite hard, since every component may behave completely different.
// Since the true point of this function is to detect suitable optimizations, I am imposing some requirements
// that simplifies things: only RGB, and only profiles that can got in both directions.
// The algorithm obtains Y from a syntetical gray R=G=B. Then least squares fitting is used to estimate gamma.
// The algorithm obtains Y from a synthetical gray R=G=B. Then least squares fitting is used to estimate gamma.
// For gamma close to 1.0, RGB is linear. On profiles not supported, -1 is returned.
cmsFloat64Number CMSEXPORT cmsDetectRGBProfileGamma(cmsHPROFILE hProfile, cmsFloat64Number threshold)

@ -560,6 +560,20 @@ cmsHPROFILE CMSEXPORT cmsCreateProfilePlaceholder(cmsContext ContextID)
// Set default version
Icc ->Version = 0x02100000;
// Set default CMM (that's me!)
Icc ->CMM = lcmsSignature;
// Set default creator
// Created by LittleCMS (that's me!)
Icc ->creator = lcmsSignature;
// Set default platform
#ifdef CMS_IS_WINDOWS_
Icc ->platform = cmsSigMicrosoft;
#else
Icc ->platform = cmsSigMacintosh;
#endif
// Set default device class
Icc->DeviceClass = cmsSigDisplayClass;
@ -813,11 +827,13 @@ cmsBool _cmsReadHeader(_cmsICCPROFILE* Icc)
}
// Adjust endianness of the used parameters
Icc -> CMM = _cmsAdjustEndianess32(Header.cmmId);
Icc -> DeviceClass = (cmsProfileClassSignature) _cmsAdjustEndianess32(Header.deviceClass);
Icc -> ColorSpace = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Header.colorSpace);
Icc -> PCS = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Header.pcs);
Icc -> RenderingIntent = _cmsAdjustEndianess32(Header.renderingIntent);
Icc -> platform = (cmsPlatformSignature)_cmsAdjustEndianess32(Header.platform);
Icc -> flags = _cmsAdjustEndianess32(Header.flags);
Icc -> manufacturer = _cmsAdjustEndianess32(Header.manufacturer);
Icc -> model = _cmsAdjustEndianess32(Header.model);
@ -922,7 +938,7 @@ cmsBool _cmsWriteHeader(_cmsICCPROFILE* Icc, cmsUInt32Number UsedSpace)
cmsUInt32Number Count;
Header.size = _cmsAdjustEndianess32(UsedSpace);
Header.cmmId = _cmsAdjustEndianess32(lcmsSignature);
Header.cmmId = _cmsAdjustEndianess32(Icc ->CMM);
Header.version = _cmsAdjustEndianess32(Icc ->Version);
Header.deviceClass = (cmsProfileClassSignature) _cmsAdjustEndianess32(Icc -> DeviceClass);
@ -934,11 +950,7 @@ cmsBool _cmsWriteHeader(_cmsICCPROFILE* Icc, cmsUInt32Number UsedSpace)
Header.magic = _cmsAdjustEndianess32(cmsMagicNumber);
#ifdef CMS_IS_WINDOWS_
Header.platform = (cmsPlatformSignature) _cmsAdjustEndianess32(cmsSigMicrosoft);
#else
Header.platform = (cmsPlatformSignature) _cmsAdjustEndianess32(cmsSigMacintosh);
#endif
Header.platform = (cmsPlatformSignature) _cmsAdjustEndianess32(Icc -> platform);
Header.flags = _cmsAdjustEndianess32(Icc -> flags);
Header.manufacturer = _cmsAdjustEndianess32(Icc -> manufacturer);
@ -954,8 +966,7 @@ cmsBool _cmsWriteHeader(_cmsICCPROFILE* Icc, cmsUInt32Number UsedSpace)
Header.illuminant.Y = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(cmsD50_XYZ()->Y));
Header.illuminant.Z = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(cmsD50_XYZ()->Z));
// Created by LittleCMS (that's me!)
Header.creator = _cmsAdjustEndianess32(lcmsSignature);
Header.creator = _cmsAdjustEndianess32(Icc ->creator);
memset(&Header.reserved, 0, sizeof(Header.reserved));

@ -607,7 +607,7 @@ Error:
return NULL;
}
// Create an output MPE LUT from agiven profile. Version mismatches are handled here
// Create an output MPE LUT from a given profile. Version mismatches are handled here
cmsPipeline* CMSEXPORT _cmsReadOutputLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent)
{
cmsTagTypeSignature OriginalType;
@ -1056,3 +1056,13 @@ cmsUInt32Number CMSEXPORT cmsGetProfileInfoASCII(cmsHPROFILE hProfile, cmsInfoT
return cmsMLUgetASCII(mlu, LanguageCode, CountryCode, Buffer, BufferSize);
}
cmsUInt32Number CMSEXPORT cmsGetProfileInfoUTF8(cmsHPROFILE hProfile, cmsInfoType Info,
const char LanguageCode[3], const char CountryCode[3],
char* Buffer, cmsUInt32Number BufferSize)
{
const cmsMLU* mlu = GetInfo(hProfile, Info);
if (mlu == NULL) return 0;
return cmsMLUgetUTF8(mlu, LanguageCode, CountryCode, Buffer, BufferSize);
}

@ -504,6 +504,9 @@ cmsUInt32Number CubeSize(const cmsUInt32Number Dims[], cmsUInt32Number b)
if (rv > UINT_MAX / dim) return 0;
}
// Again, prevent overflow
if (rv > UINT_MAX / 15) return 0;
return rv;
}
@ -843,7 +846,13 @@ cmsBool CMSEXPORT cmsStageSampleCLutFloat(cmsStage* mpe, cmsSAMPLERFLOAT Sampler
cmsUInt32Number nInputs, nOutputs;
cmsUInt32Number* nSamples;
cmsFloat32Number In[MAX_INPUT_DIMENSIONS+1], Out[MAX_STAGE_CHANNELS];
_cmsStageCLutData* clut = (_cmsStageCLutData*) mpe->Data;
_cmsStageCLutData* clut;
if (mpe == NULL) return FALSE;
clut = (_cmsStageCLutData*)mpe->Data;
if (clut == NULL) return FALSE;
nSamples = clut->Params ->nSamples;
nInputs = clut->Params ->nInputs;

@ -229,17 +229,145 @@ void strFrom16(char str[3], cmsUInt16Number n)
str[0] = (char)(n >> 8);
str[1] = (char)n;
str[2] = (char)0;
}
// Convert from UTF8 to wchar, returns len.
static
cmsUInt32Number decodeUTF8(wchar_t* out, const char* in)
{
cmsUInt32Number codepoint = 0;
cmsUInt32Number size = 0;
while (*in)
{
cmsUInt8Number ch = (cmsUInt8Number) *in;
if (ch <= 0x7f)
{
codepoint = ch;
}
else if (ch <= 0xbf)
{
codepoint = (codepoint << 6) | (ch & 0x3f);
}
else if (ch <= 0xdf)
{
codepoint = ch & 0x1f;
}
else if (ch <= 0xef)
{
codepoint = ch & 0x0f;
}
else
{
codepoint = ch & 0x07;
}
in++;
if (((*in & 0xc0) != 0x80) && (codepoint <= 0x10ffff))
{
if (sizeof(wchar_t) > 2)
{
if (out) *out++ = (wchar_t) codepoint;
size++;
}
else
if (codepoint > 0xffff)
{
if (out)
{
*out++ = (wchar_t)(0xd800 + (codepoint >> 10));
*out++ = (wchar_t)(0xdc00 + (codepoint & 0x03ff));
size += 2;
}
}
else
if (codepoint < 0xd800 || codepoint >= 0xe000)
{
if (out) *out++ = (wchar_t) codepoint;
size++;
}
}
}
return size;
}
// Convert from wchar_t to UTF8
static
cmsUInt32Number encodeUTF8(char* out, const wchar_t* in, cmsUInt32Number max_wchars, cmsUInt32Number max_chars)
{
cmsUInt32Number codepoint = 0;
cmsUInt32Number size = 0;
cmsUInt32Number len_w = 0;
while (*in && len_w < max_wchars)
{
if (*in >= 0xd800 && *in <= 0xdbff)
codepoint = ((*in - 0xd800) << 10) + 0x10000;
else
{
if (*in >= 0xdc00 && *in <= 0xdfff)
codepoint |= *in - 0xdc00;
else
codepoint = *in;
if (codepoint <= 0x7f)
{
if (out && (size + 1 < max_chars)) *out++ = (char)codepoint;
size++;
}
else if (codepoint <= 0x7ff)
{
if (out && (max_chars > 0) && (size + 2 < max_chars))
{
*out++ = (char)(cmsUInt32Number)(0xc0 | ((codepoint >> 6) & 0x1f));
*out++ = (char)(cmsUInt32Number)(0x80 | (codepoint & 0x3f));
}
size += 2;
}
else if (codepoint <= 0xffff)
{
if (out && (max_chars > 0) && (size + 3 < max_chars))
{
*out++ = (char)(cmsUInt32Number)(0xe0 | ((codepoint >> 12) & 0x0f));
*out++ = (char)(cmsUInt32Number)(0x80 | ((codepoint >> 6) & 0x3f));
*out++ = (char)(cmsUInt32Number)(0x80 | (codepoint & 0x3f));
}
size += 3;
}
else
{
if (out && (max_chars > 0) && (size + 4 < max_chars))
{
*out++ = (char)(cmsUInt32Number)(0xf0 | ((codepoint >> 18) & 0x07));
*out++ = (char)(cmsUInt32Number)(0x80 | ((codepoint >> 12) & 0x3f));
*out++ = (char)(cmsUInt32Number)(0x80 | ((codepoint >> 6) & 0x3f));
*out++ = (char)(cmsUInt32Number)(0x80 | (codepoint & 0x3f));
}
size += 4;
}
codepoint = 0;
}
in++; len_w++;
}
return size;
}
// Add an ASCII entry. Do not add any \0 termination (ICC1v43_2010-12.pdf page 61)
// In the case the user explicitly sets an empty string, we force a \0
cmsBool CMSEXPORT cmsMLUsetASCII(cmsMLU* mlu, const char LanguageCode[3], const char CountryCode[3], const char* ASCIIString)
{
cmsUInt32Number i, len = (cmsUInt32Number) strlen(ASCIIString);
cmsUInt32Number i, len = (cmsUInt32Number)strlen(ASCIIString);
wchar_t* WStr;
cmsBool rc;
cmsUInt16Number Lang = strTo16(LanguageCode);
cmsUInt16Number Lang = strTo16(LanguageCode);
cmsUInt16Number Cntry = strTo16(CountryCode);
if (mlu == NULL) return FALSE;
@ -247,20 +375,54 @@ cmsBool CMSEXPORT cmsMLUsetASCII(cmsMLU* mlu, const char LanguageCode[3], const
// len == 0 would prevent operation, so we set a empty string pointing to zero
if (len == 0)
{
len = 1;
wchar_t empty = 0;
return AddMLUBlock(mlu, sizeof(wchar_t), &empty, Lang, Cntry);
}
WStr = (wchar_t*) _cmsCalloc(mlu ->ContextID, len, sizeof(wchar_t));
WStr = (wchar_t*)_cmsCalloc(mlu->ContextID, len, sizeof(wchar_t));
if (WStr == NULL) return FALSE;
for (i=0; i < len; i++)
WStr[i] = (wchar_t) ASCIIString[i];
for (i = 0; i < len; i++)
WStr[i] = (wchar_t)ASCIIString[i];
rc = AddMLUBlock(mlu, len * sizeof(wchar_t), WStr, Lang, Cntry);
rc = AddMLUBlock(mlu, len * sizeof(wchar_t), WStr, Lang, Cntry);
_cmsFree(mlu->ContextID, WStr);
return rc;
}
// Add an UTF8 entry. Do not add any \0 termination (ICC1v43_2010-12.pdf page 61)
// In the case the user explicitly sets an empty string, we force a \0
cmsBool CMSEXPORT cmsMLUsetUTF8(cmsMLU* mlu, const char LanguageCode[3], const char CountryCode[3], const char* UTF8String)
{
cmsUInt32Number UTF8len;
wchar_t* WStr;
cmsBool rc;
cmsUInt16Number Lang = strTo16(LanguageCode);
cmsUInt16Number Cntry = strTo16(CountryCode);
if (mlu == NULL) return FALSE;
if (*UTF8String == '\0')
{
wchar_t empty = 0;
return AddMLUBlock(mlu, sizeof(wchar_t), &empty, Lang, Cntry);
}
// Len excluding terminator 0
UTF8len = decodeUTF8(NULL, UTF8String);
// Get space for dest
WStr = (wchar_t*) _cmsCalloc(mlu ->ContextID, UTF8len, sizeof(wchar_t));
if (WStr == NULL) return FALSE;
decodeUTF8(WStr, UTF8String);
rc = AddMLUBlock(mlu, UTF8len * sizeof(wchar_t), WStr, Lang, Cntry);
_cmsFree(mlu ->ContextID, WStr);
return rc;
}
// We don't need any wcs support library
@ -401,7 +563,7 @@ const wchar_t* _cmsMLUgetWide(const cmsMLU* mlu,
if (v->StrW + v->Len > mlu->PoolSize) return NULL;
return(wchar_t*) ((cmsUInt8Number*) mlu ->MemPool + v ->StrW);
return (wchar_t*) ((cmsUInt8Number*) mlu ->MemPool + v ->StrW);
}
@ -439,10 +601,12 @@ cmsUInt32Number CMSEXPORT cmsMLUgetASCII(const cmsMLU* mlu,
// Precess each character
for (i=0; i < ASCIIlen; i++) {
if (Wide[i] == 0)
Buffer[i] = 0;
wchar_t wc = Wide[i];
if (wc < 0xff)
Buffer[i] = (char)wc;
else
Buffer[i] = (char) Wide[i];
Buffer[i] = '?';
}
// We put a termination "\0"
@ -450,6 +614,46 @@ cmsUInt32Number CMSEXPORT cmsMLUgetASCII(const cmsMLU* mlu,
return ASCIIlen + 1;
}
// Obtain a UTF8 representation of the wide string. Setting buffer to NULL returns the len
cmsUInt32Number CMSEXPORT cmsMLUgetUTF8(const cmsMLU* mlu,
const char LanguageCode[3], const char CountryCode[3],
char* Buffer, cmsUInt32Number BufferSize)
{
const wchar_t *Wide;
cmsUInt32Number StrLen = 0;
cmsUInt32Number UTF8len;
cmsUInt16Number Lang = strTo16(LanguageCode);
cmsUInt16Number Cntry = strTo16(CountryCode);
// Sanitize
if (mlu == NULL) return 0;
// Get WideChar
Wide = _cmsMLUgetWide(mlu, &StrLen, Lang, Cntry, NULL, NULL);
if (Wide == NULL) return 0;
UTF8len = encodeUTF8(NULL, Wide, StrLen / sizeof(wchar_t), BufferSize);
// Maybe we want only to know the len?
if (Buffer == NULL) return UTF8len + 1; // Note the zero at the end
// No buffer size means no data
if (BufferSize <= 0) return 0;
// Some clipping may be required
if (BufferSize < UTF8len + 1)
UTF8len = BufferSize - 1;
// Process it
encodeUTF8(Buffer, Wide, StrLen / sizeof(wchar_t), BufferSize);
// We put a termination "\0"
Buffer[UTF8len] = 0;
return UTF8len + 1;
}
// Obtain a wide representation of the MLU, on depending on current locale settings
cmsUInt32Number CMSEXPORT cmsMLUgetWide(const cmsMLU* mlu,
const char LanguageCode[3], const char CountryCode[3],
@ -470,12 +674,12 @@ cmsUInt32Number CMSEXPORT cmsMLUgetWide(const cmsMLU* mlu,
// Maybe we want only to know the len?
if (Buffer == NULL) return StrLen + sizeof(wchar_t);
// No buffer size means no data
if (BufferSize <= 0) return 0;
// Invalid buffer size means no data
if (BufferSize < sizeof(wchar_t)) return 0;
// Some clipping may be required
if (BufferSize < StrLen + sizeof(wchar_t))
StrLen = BufferSize - + sizeof(wchar_t);
StrLen = BufferSize - sizeof(wchar_t);
memmove(Buffer, Wide, StrLen);
Buffer[StrLen / sizeof(wchar_t)] = 0;
@ -843,13 +1047,19 @@ void CMSEXPORT cmsFreeProfileSequenceDescription(cmsSEQ* pseq)
{
cmsUInt32Number i;
for (i=0; i < pseq ->n; i++) {
if (pseq ->seq[i].Manufacturer != NULL) cmsMLUfree(pseq ->seq[i].Manufacturer);
if (pseq ->seq[i].Model != NULL) cmsMLUfree(pseq ->seq[i].Model);
if (pseq ->seq[i].Description != NULL) cmsMLUfree(pseq ->seq[i].Description);
if (pseq == NULL)
return;
if (pseq ->seq != NULL) {
for (i=0; i < pseq ->n; i++) {
if (pseq ->seq[i].Manufacturer != NULL) cmsMLUfree(pseq ->seq[i].Manufacturer);
if (pseq ->seq[i].Model != NULL) cmsMLUfree(pseq ->seq[i].Model);
if (pseq ->seq[i].Description != NULL) cmsMLUfree(pseq ->seq[i].Description);
}
_cmsFree(pseq ->ContextID, pseq ->seq);
}
if (pseq ->seq != NULL) _cmsFree(pseq ->ContextID, pseq ->seq);
_cmsFree(pseq -> ContextID, pseq);
}

@ -212,6 +212,7 @@ cmsBool isFloatMatrixIdentity(const cmsMAT3* a)
return TRUE;
}
// if two adjacent matrices are found, multiply them.
static
cmsBool _MultiplyMatrix(cmsPipeline* Lut)
@ -1142,14 +1143,17 @@ cmsBool OptimizeByComputingLinearization(cmsPipeline** Lut, cmsUInt32Number Inte
// Store result in curve
for (t=0; t < OriginalLut ->InputChannels; t++)
Trans[t] ->Table16[i] = _cmsQuickSaturateWord(Out[t] * 65535.0);
{
if (Trans[t]->Table16 != NULL)
Trans[t] ->Table16[i] = _cmsQuickSaturateWord(Out[t] * 65535.0);
}
}
// Slope-limit the obtained curves
for (t = 0; t < OriginalLut ->InputChannels; t++)
SlopeLimiting(Trans[t]);
// Check for validity
// Check for validity. lIsLinear is here for debug purposes
lIsSuitable = TRUE;
lIsLinear = TRUE;
for (t=0; (lIsSuitable && (t < OriginalLut ->InputChannels)); t++) {
@ -1753,6 +1757,8 @@ cmsBool OptimizeMatrixShaper(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3
_cmsStageMatrixData* Data = (_cmsStageMatrixData*)cmsStageData(Matrix1);
if (Matrix1->InputChannels != 3 || Matrix1->OutputChannels != 3) return FALSE;
// Copy the matrix to our result
memcpy(&res, Data->Double, sizeof(res));
@ -1797,7 +1803,7 @@ cmsBool OptimizeMatrixShaper(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3
_cmsStageToneCurvesData* mpeC2 = (_cmsStageToneCurvesData*) cmsStageData(Curve2);
// In this particular optimization, cache does not help as it takes more time to deal with
// the cache that with the pixel handling
// the cache than with the pixel handling
*dwFlags |= cmsFLAGS_NOCACHE;
// Setup the optimizarion routines
@ -1954,7 +1960,7 @@ cmsBool CMSEXPORT _cmsOptimizePipeline(cmsContext ContextID,
for (mpe = cmsPipelineGetPtrToFirstStage(*PtrLut);
mpe != NULL;
mpe = cmsStageNext(mpe)) {
if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE;
if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE;
}
// Try to get rid of identities and trivial conversions.

@ -2980,6 +2980,108 @@ cmsUInt8Number* PackFloatFrom16(CMSREGISTER _cmsTRANSFORM* info,
// --------------------------------------------------------------------------------------------------------
static
cmsUInt8Number* PackBytesFromFloat(_cmsTRANSFORM* info,
cmsFloat32Number wOut[],
cmsUInt8Number* output,
cmsUInt32Number Stride)
{
cmsUInt32Number nChan = T_CHANNELS(info->OutputFormat);
cmsUInt32Number DoSwap = T_DOSWAP(info->OutputFormat);
cmsUInt32Number Reverse = T_FLAVOR(info->OutputFormat);
cmsUInt32Number Extra = T_EXTRA(info->OutputFormat);
cmsUInt32Number SwapFirst = T_SWAPFIRST(info->OutputFormat);
cmsUInt32Number Planar = T_PLANAR(info->OutputFormat);
cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
cmsUInt8Number* swap1 = (cmsUInt8Number*)output;
cmsFloat64Number v = 0;
cmsUInt8Number vv = 0;
cmsUInt32Number i, start = 0;
if (ExtraFirst)
start = Extra;
for (i = 0; i < nChan; i++) {
cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
v = wOut[index] * 65535.0;
if (Reverse)
v = 65535.0 - v;
vv = FROM_16_TO_8(_cmsQuickSaturateWord(v));
if (Planar)
((cmsUInt8Number*)output)[(i + start) * Stride] = vv;
else
((cmsUInt8Number*)output)[i + start] = vv;
}
if (Extra == 0 && SwapFirst) {
memmove(swap1 + 1, swap1, (nChan - 1) * sizeof(cmsUInt8Number));
*swap1 = vv;
}
if (T_PLANAR(info->OutputFormat))
return output + sizeof(cmsUInt8Number);
else
return output + (nChan + Extra) * sizeof(cmsUInt8Number);
}
static
cmsUInt8Number* PackWordsFromFloat(_cmsTRANSFORM* info,
cmsFloat32Number wOut[],
cmsUInt8Number* output,
cmsUInt32Number Stride)
{
cmsUInt32Number nChan = T_CHANNELS(info->OutputFormat);
cmsUInt32Number DoSwap = T_DOSWAP(info->OutputFormat);
cmsUInt32Number Reverse = T_FLAVOR(info->OutputFormat);
cmsUInt32Number Extra = T_EXTRA(info->OutputFormat);
cmsUInt32Number SwapFirst = T_SWAPFIRST(info->OutputFormat);
cmsUInt32Number Planar = T_PLANAR(info->OutputFormat);
cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst;
cmsUInt16Number* swap1 = (cmsUInt16Number*)output;
cmsFloat64Number v = 0;
cmsUInt16Number vv = 0;
cmsUInt32Number i, start = 0;
if (ExtraFirst)
start = Extra;
for (i = 0; i < nChan; i++) {
cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i;
v = wOut[index] * 65535.0;
if (Reverse)
v = 65535.0 - v;
vv = _cmsQuickSaturateWord(v);
if (Planar)
((cmsUInt16Number*)output)[(i + start) * Stride] = vv;
else
((cmsUInt16Number*)output)[i + start] = vv;
}
if (Extra == 0 && SwapFirst) {
memmove(swap1 + 1, swap1, (nChan - 1) * sizeof(cmsUInt16Number));
*swap1 = vv;
}
if (T_PLANAR(info->OutputFormat))
return output + sizeof(cmsUInt16Number);
else
return output + (nChan + Extra) * sizeof(cmsUInt16Number);
}
static
cmsUInt8Number* PackFloatsFromFloat(_cmsTRANSFORM* info,
cmsFloat32Number wOut[],
@ -3143,6 +3245,77 @@ cmsUInt8Number* PackLabDoubleFromFloat(_cmsTRANSFORM* Info,
}
static
cmsUInt8Number* PackEncodedBytesLabV2FromFloat(_cmsTRANSFORM* Info,
cmsFloat32Number wOut[],
cmsUInt8Number* output,
cmsUInt32Number Stride)
{
cmsCIELab Lab;
cmsUInt16Number wlab[3];
Lab.L = (cmsFloat64Number)(wOut[0] * 100.0);
Lab.a = (cmsFloat64Number)(wOut[1] * 255.0 - 128.0);
Lab.b = (cmsFloat64Number)(wOut[2] * 255.0 - 128.0);
cmsFloat2LabEncoded(wlab, &Lab);
if (T_PLANAR(Info -> OutputFormat)) {
Stride /= PixelSize(Info->OutputFormat);
output[0] = wlab[0] >> 8;
output[Stride] = wlab[1] >> 8;
output[Stride*2] = wlab[2] >> 8;
return output + 1;
}
else {
output[0] = wlab[0] >> 8;
output[1] = wlab[1] >> 8;
output[2] = wlab[2] >> 8;
return output + (3 + T_EXTRA(Info ->OutputFormat));
}
}
static
cmsUInt8Number* PackEncodedWordsLabV2FromFloat(_cmsTRANSFORM* Info,
cmsFloat32Number wOut[],
cmsUInt8Number* output,
cmsUInt32Number Stride)
{
cmsCIELab Lab;
cmsUInt16Number wlab[3];
Lab.L = (cmsFloat64Number)(wOut[0] * 100.0);
Lab.a = (cmsFloat64Number)(wOut[1] * 255.0 - 128.0);
Lab.b = (cmsFloat64Number)(wOut[2] * 255.0 - 128.0);
cmsFloat2LabEncodedV2(wlab, &Lab);
if (T_PLANAR(Info -> OutputFormat)) {
Stride /= PixelSize(Info->OutputFormat);
((cmsUInt16Number*) output)[0] = wlab[0];
((cmsUInt16Number*) output)[Stride] = wlab[1];
((cmsUInt16Number*) output)[Stride*2] = wlab[2];
return output + sizeof(cmsUInt16Number);
}
else {
((cmsUInt16Number*) output)[0] = wlab[0];
((cmsUInt16Number*) output)[1] = wlab[1];
((cmsUInt16Number*) output)[2] = wlab[2];
return output + (3 + T_EXTRA(Info ->OutputFormat)) * sizeof(cmsUInt16Number);
}
}
// From 0..1 range to 0..MAX_ENCODEABLE_XYZ
static
cmsUInt8Number* PackXYZFloatFromFloat(_cmsTRANSFORM* Info,
@ -3676,10 +3849,20 @@ static const cmsFormattersFloat OutputFormattersFloat[] = {
{ TYPE_Lab_DBL, ANYPLANAR|ANYEXTRA, PackLabDoubleFromFloat},
{ TYPE_XYZ_DBL, ANYPLANAR|ANYEXTRA, PackXYZDoubleFromFloat},
{ TYPE_LabV2_8, ANYPLANAR|ANYEXTRA, PackEncodedBytesLabV2FromFloat},
{ TYPE_LabV2_16, ANYPLANAR|ANYEXTRA, PackEncodedWordsLabV2FromFloat},
{ FLOAT_SH(1)|BYTES_SH(4), ANYPLANAR|
ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackFloatsFromFloat },
{ FLOAT_SH(1)|BYTES_SH(0), ANYPLANAR|
ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackDoublesFromFloat },
{ BYTES_SH(2), ANYPLANAR|
ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackWordsFromFloat },
{ BYTES_SH(1), ANYPLANAR|
ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackBytesFromFloat },
#ifndef CMS_NO_HALF_SUPPORT
{ FLOAT_SH(1)|BYTES_SH(2),
ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackHalfFromFloat },
@ -3890,7 +4073,7 @@ cmsUInt32Number CMSEXPORT cmsFormatterForPCSOfProfile(cmsHPROFILE hProfile, cmsU
cmsColorSpaceSignature ColorSpace = cmsGetPCS(hProfile);
cmsUInt32Number ColorSpaceBits = (cmsUInt32Number) _cmsLCMScolorSpace(ColorSpace);
cmsUInt32Number nOutputChans = cmsChannelsOf(ColorSpace);
cmsInt32Number nOutputChans = cmsChannelsOfColorSpace(ColorSpace);
cmsUInt32Number Float = lIsFloat ? 1U : 0;
// Unsupported color space?

@ -393,12 +393,7 @@ cmsBool CMSEXPORT _cmsWriteXYZNumber(cmsIOHANDLER* io, const cmsCIEXYZ* XYZ)
// from Fixed point 8.8 to double
cmsFloat64Number CMSEXPORT _cms8Fixed8toDouble(cmsUInt16Number fixed8)
{
cmsUInt8Number msb, lsb;
lsb = (cmsUInt8Number) (fixed8 & 0xff);
msb = (cmsUInt8Number) (((cmsUInt16Number) fixed8 >> 8) & 0xff);
return (cmsFloat64Number) ((cmsFloat64Number) msb + ((cmsFloat64Number) lsb / 256.0));
return fixed8 / 256.0;
}
cmsUInt16Number CMSEXPORT _cmsDoubleTo8Fixed8(cmsFloat64Number val)
@ -410,19 +405,7 @@ cmsUInt16Number CMSEXPORT _cmsDoubleTo8Fixed8(cmsFloat64Number val)
// from Fixed point 15.16 to double
cmsFloat64Number CMSEXPORT _cms15Fixed16toDouble(cmsS15Fixed16Number fix32)
{
cmsFloat64Number floater, sign, mid;
int Whole, FracPart;
sign = (fix32 < 0 ? -1 : 1);
fix32 = abs(fix32);
Whole = (cmsUInt16Number)(fix32 >> 16) & 0xffff;
FracPart = (cmsUInt16Number)(fix32 & 0xffff);
mid = (cmsFloat64Number) FracPart / 65536.0;
floater = (cmsFloat64Number) Whole + mid;
return sign * floater;
return fix32 / 65536.0;
}
// from double to Fixed point 15.16

@ -460,48 +460,46 @@ void EmitLab2XYZ(cmsIOHANDLER* m)
_cmsIOPrintf(m, "]\n");
}
static
void EmitSafeGuardBegin(cmsIOHANDLER* m, const char* name)
{
_cmsIOPrintf(m, "%%LCMS2: Save previous definition of %s on the operand stack\n", name);
_cmsIOPrintf(m, "currentdict /%s known { /%s load } { null } ifelse\n", name, name);
}
static
void EmitSafeGuardEnd(cmsIOHANDLER* m, const char* name, int depth)
{
_cmsIOPrintf(m, "%%LCMS2: Restore previous definition of %s\n", name);
if (depth > 1) {
// cycle topmost items on the stack to bring the previous definition to the front
_cmsIOPrintf(m, "%d -1 roll ", depth);
}
_cmsIOPrintf(m, "dup null eq { pop currentdict /%s undef } { /%s exch def } ifelse\n", name, name);
}
// Outputs a table of words. It does use 16 bits
static
void Emit1Gamma(cmsIOHANDLER* m, cmsToneCurve* Table, const char* name)
void Emit1Gamma(cmsIOHANDLER* m, cmsToneCurve* Table)
{
cmsUInt32Number i;
cmsFloat64Number gamma;
if (Table == NULL) return; // Error
/**
* On error, empty tables or lienar assume gamma 1.0
*/
if (Table == NULL ||
Table->nEntries <= 0 ||
cmsIsToneCurveLinear(Table)) {
if (Table ->nEntries <= 0) return; // Empty table
_cmsIOPrintf(m, "{ 1 } bind ");
return;
}
// Suppress whole if identity
if (cmsIsToneCurveLinear(Table)) return;
// Check if is really an exponential. If so, emit "exp"
gamma = cmsEstimateGamma(Table, 0.001);
if (gamma > 0) {
_cmsIOPrintf(m, "/%s { %g exp } bind def\n", name, gamma);
_cmsIOPrintf(m, "{ %g exp } bind ", gamma);
return;
}
EmitSafeGuardBegin(m, "lcms2gammatable");
_cmsIOPrintf(m, "/lcms2gammatable [");
_cmsIOPrintf(m, "{ ");
// Bounds check
EmitRangeCheck(m);
// Emit intepolation code
// PostScript code Stack
// =============== ========================
// v
_cmsIOPrintf(m, " [");
for (i=0; i < Table->nEntries; i++) {
if (i % 10 == 0)
@ -509,20 +507,8 @@ void Emit1Gamma(cmsIOHANDLER* m, cmsToneCurve* Table, const char* name)
_cmsIOPrintf(m, "%d ", Table->Table16[i]);
}
_cmsIOPrintf(m, "] def\n");
_cmsIOPrintf(m, "] "); // v tab
// Emit interpolation code
// PostScript code Stack
// =============== ========================
// v
_cmsIOPrintf(m, "/%s {\n ", name);
// Bounds check
EmitRangeCheck(m);
_cmsIOPrintf(m, "\n //lcms2gammatable "); // v tab
_cmsIOPrintf(m, "dup "); // v tab tab
_cmsIOPrintf(m, "length 1 sub "); // v tab dom
_cmsIOPrintf(m, "3 -1 roll "); // tab dom v
@ -549,9 +535,7 @@ void Emit1Gamma(cmsIOHANDLER* m, cmsToneCurve* Table, const char* name)
_cmsIOPrintf(m, "add "); // y
_cmsIOPrintf(m, "65535 div\n"); // result
_cmsIOPrintf(m, "} bind def\n");
EmitSafeGuardEnd(m, "lcms2gammatable", 1);
_cmsIOPrintf(m, " } bind ");
}
@ -568,10 +552,10 @@ cmsBool GammaTableEquals(cmsUInt16Number* g1, cmsUInt16Number* g2, cmsUInt32Numb
// Does write a set of gamma curves
static
void EmitNGamma(cmsIOHANDLER* m, cmsUInt32Number n, cmsToneCurve* g[], const char* nameprefix)
void EmitNGamma(cmsIOHANDLER* m, cmsUInt32Number n, cmsToneCurve* g[])
{
cmsUInt32Number i;
static char buffer[2048];
for( i=0; i < n; i++ )
{
@ -579,12 +563,10 @@ void EmitNGamma(cmsIOHANDLER* m, cmsUInt32Number n, cmsToneCurve* g[], const cha
if (i > 0 && GammaTableEquals(g[i-1]->Table16, g[i]->Table16, g[i-1]->nEntries, g[i]->nEntries)) {
_cmsIOPrintf(m, "/%s%d /%s%d load def\n", nameprefix, i, nameprefix, i-1);
_cmsIOPrintf(m, "dup ");
}
else {
snprintf(buffer, sizeof(buffer), "%s%d", nameprefix, (int) i);
buffer[sizeof(buffer)-1] = '\0';
Emit1Gamma(m, g[i], buffer);
Emit1Gamma(m, g[i]);
}
}
@ -708,18 +690,21 @@ void WriteCLUT(cmsIOHANDLER* m, cmsStage* mpe, const char* PreMaj,
sc.FixWhite = FixWhite;
sc.ColorSpace = ColorSpace;
_cmsIOPrintf(m, "[");
if (sc.Pipeline != NULL && sc.Pipeline->Params != NULL) {
for (i=0; i < sc.Pipeline->Params->nInputs; i++)
_cmsIOPrintf(m, " %d ", sc.Pipeline->Params->nSamples[i]);
_cmsIOPrintf(m, "[");
_cmsIOPrintf(m, " [\n");
for (i = 0; i < sc.Pipeline->Params->nInputs; i++)
_cmsIOPrintf(m, " %d ", sc.Pipeline->Params->nSamples[i]);
cmsStageSampleCLut16bit(mpe, OutputValueSampler, (void*) &sc, SAMPLER_INSPECT);
_cmsIOPrintf(m, " [\n");
_cmsIOPrintf(m, PostMin);
_cmsIOPrintf(m, PostMaj);
_cmsIOPrintf(m, "] ");
cmsStageSampleCLut16bit(mpe, OutputValueSampler, (void*)&sc, SAMPLER_INSPECT);
_cmsIOPrintf(m, PostMin);
_cmsIOPrintf(m, PostMaj);
_cmsIOPrintf(m, "] ");
}
}
@ -733,11 +718,11 @@ int EmitCIEBasedA(cmsIOHANDLER* m, cmsToneCurve* Curve, cmsCIEXYZ* BlackPoint)
_cmsIOPrintf(m, "[ /CIEBasedA\n");
_cmsIOPrintf(m, " <<\n");
EmitSafeGuardBegin(m, "lcms2gammaproc");
Emit1Gamma(m, Curve, "lcms2gammaproc");
_cmsIOPrintf(m, "/DecodeA ");
_cmsIOPrintf(m, "/DecodeA /lcms2gammaproc load\n");
EmitSafeGuardEnd(m, "lcms2gammaproc", 3);
Emit1Gamma(m, Curve);
_cmsIOPrintf(m, " \n");
_cmsIOPrintf(m, "/MatrixA [ 0.9642 1.0000 0.8249 ]\n");
_cmsIOPrintf(m, "/RangeLMN [ 0.0 0.9642 0.0 1.0000 0.0 0.8249 ]\n");
@ -761,19 +746,11 @@ int EmitCIEBasedABC(cmsIOHANDLER* m, cmsFloat64Number* Matrix, cmsToneCurve** Cu
_cmsIOPrintf(m, "[ /CIEBasedABC\n");
_cmsIOPrintf(m, "<<\n");
_cmsIOPrintf(m, "/DecodeABC [ ");
EmitNGamma(m, 3, CurveSet);
EmitSafeGuardBegin(m, "lcms2gammaproc0");
EmitSafeGuardBegin(m, "lcms2gammaproc1");
EmitSafeGuardBegin(m, "lcms2gammaproc2");
EmitNGamma(m, 3, CurveSet, "lcms2gammaproc");
_cmsIOPrintf(m, "/DecodeABC [\n");
_cmsIOPrintf(m, " /lcms2gammaproc0 load\n");
_cmsIOPrintf(m, " /lcms2gammaproc1 load\n");
_cmsIOPrintf(m, " /lcms2gammaproc2 load\n");
_cmsIOPrintf(m, "]\n");
EmitSafeGuardEnd(m, "lcms2gammaproc2", 3);
EmitSafeGuardEnd(m, "lcms2gammaproc1", 3);
EmitSafeGuardEnd(m, "lcms2gammaproc0", 3);
_cmsIOPrintf(m, "/MatrixABC [ " );
@ -805,10 +782,8 @@ int EmitCIEBasedDEF(cmsIOHANDLER* m, cmsPipeline* Pipeline, cmsUInt32Number Inte
{
const char* PreMaj;
const char* PostMaj;
const char* PreMin, * PostMin;
const char* PreMin, *PostMin;
cmsStage* mpe;
int i, numchans;
static char buffer[2048];
mpe = Pipeline->Elements;
@ -837,34 +812,18 @@ int EmitCIEBasedDEF(cmsIOHANDLER* m, cmsPipeline* Pipeline, cmsUInt32Number Inte
if (cmsStageType(mpe) == cmsSigCurveSetElemType) {
numchans = (int) cmsStageOutputChannels(mpe);
for (i = 0; i < numchans; ++i) {
snprintf(buffer, sizeof(buffer), "lcms2gammaproc%d", i);
buffer[sizeof(buffer) - 1] = '\0';
EmitSafeGuardBegin(m, buffer);
}
EmitNGamma(m, cmsStageOutputChannels(mpe), _cmsStageGetPtrToCurveSet(mpe), "lcms2gammaproc");
_cmsIOPrintf(m, "/DecodeDEF [\n");
for (i = 0; i < numchans; ++i) {
snprintf(buffer, sizeof(buffer), " /lcms2gammaproc%d load\n", i);
buffer[sizeof(buffer) - 1] = '\0';
_cmsIOPrintf(m, buffer);
}
_cmsIOPrintf(m, "/DecodeDEF [ ");
EmitNGamma(m, cmsStageOutputChannels(mpe), _cmsStageGetPtrToCurveSet(mpe));
_cmsIOPrintf(m, "]\n");
for (i = numchans - 1; i >= 0; --i) {
snprintf(buffer, sizeof(buffer), "lcms2gammaproc%d", i);
buffer[sizeof(buffer) - 1] = '\0';
EmitSafeGuardEnd(m, buffer, 3);
}
mpe = mpe->Next;
mpe = mpe ->Next;
}
if (cmsStageType(mpe) == cmsSigCLutElemType) {
_cmsIOPrintf(m, "/Table ");
WriteCLUT(m, mpe, PreMaj, PostMaj, PreMin, PostMin, FALSE, (cmsColorSpaceSignature)0);
_cmsIOPrintf(m, "]\n");
_cmsIOPrintf(m, "/Table ");
WriteCLUT(m, mpe, PreMaj, PostMaj, PreMin, PostMin, FALSE, (cmsColorSpaceSignature) 0);
_cmsIOPrintf(m, "]\n");
}
EmitLab2XYZ(m);
@ -1024,9 +983,9 @@ int WriteInputMatrixShaper(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsStage* Matr
for (j = 0; j < 3; j++)
Mat.v[i].n[j] *= MAX_ENCODEABLE_XYZ;
rc = EmitCIEBasedABC(m, (cmsFloat64Number *)&Mat,
_cmsStageGetPtrToCurveSet(Shaper),
&BlackPointAdaptedToD50);
rc = EmitCIEBasedABC(m, (cmsFloat64Number *) &Mat,
_cmsStageGetPtrToCurveSet(Shaper),
&BlackPointAdaptedToD50);
}
else {
@ -1053,10 +1012,15 @@ int WriteNamedColorCSA(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, cmsUInt32Number
hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
xform = cmsCreateTransform(hNamedColor, TYPE_NAMED_COLOR_INDEX, hLab, TYPE_Lab_DBL, Intent, 0);
cmsCloseProfile(hLab);
if (xform == NULL) return 0;
NamedColorList = cmsGetNamedColorList(xform);
if (NamedColorList == NULL) return 0;
if (NamedColorList == NULL) {
cmsDeleteTransform(xform);
return 0;
}
_cmsIOPrintf(m, "<<\n");
_cmsIOPrintf(m, "(colorlistcomment) (%s)\n", "Named color CSA");
@ -1065,7 +1029,6 @@ int WriteNamedColorCSA(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, cmsUInt32Number
nColors = cmsNamedColorCount(NamedColorList);
for (i=0; i < nColors; i++) {
cmsUInt16Number In[1];
@ -1080,12 +1043,9 @@ int WriteNamedColorCSA(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, cmsUInt32Number
_cmsIOPrintf(m, " (%s) [ %.3f %.3f %.3f ]\n", ColorName, Lab.L, Lab.a, Lab.b);
}
_cmsIOPrintf(m, ">>\n");
cmsDeleteTransform(xform);
cmsCloseProfile(hLab);
return 1;
}
@ -1339,7 +1299,7 @@ int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent
cmsUInt32Number InFrm = TYPE_Lab_16;
cmsUInt32Number RelativeEncodingIntent;
cmsColorSpaceSignature ColorSpace;
cmsStage* first;
hLab = cmsCreateLab4ProfileTHR(m ->ContextID, NULL);
if (hLab == NULL) return 0;
@ -1366,7 +1326,6 @@ int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent
cmsCloseProfile(hLab);
if (xform == NULL) {
cmsSignalError(m ->ContextID, cmsERROR_COLORSPACE_CHECK, "Cannot create transform Lab -> Profile in CRD creation");
return 0;
}
@ -1374,10 +1333,12 @@ int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent
// Get a copy of the internal devicelink
v = (_cmsTRANSFORM*) xform;
DeviceLink = cmsPipelineDup(v ->Lut);
if (DeviceLink == NULL) return 0;
if (DeviceLink == NULL) {
cmsDeleteTransform(xform);
return 0;
}
// We need a CLUT
// We need a CLUT
dwFlags |= cmsFLAGS_FORCE_CLUT;
_cmsOptimizePipeline(m->ContextID, &DeviceLink, RelativeEncodingIntent, &InFrm, &OutputFormat, &dwFlags);
@ -1404,8 +1365,10 @@ int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent
_cmsIOPrintf(m, "/RenderTable ");
WriteCLUT(m, cmsPipelineGetPtrToFirstStage(DeviceLink), "<", ">\n", "", "", lFixWhite, ColorSpace);
first = cmsPipelineGetPtrToFirstStage(DeviceLink);
if (first != NULL) {
WriteCLUT(m, first, "<", ">\n", "", "", lFixWhite, ColorSpace);
}
_cmsIOPrintf(m, " %d {} bind ", nChannels);
@ -1414,7 +1377,6 @@ int WriteOutputLUT(cmsIOHANDLER* m, cmsHPROFILE hProfile, cmsUInt32Number Intent
_cmsIOPrintf(m, "]\n");
EmitIntent(m, Intent);
_cmsIOPrintf(m, ">>\n");
@ -1477,7 +1439,10 @@ int WriteNamedColorCRD(cmsIOHANDLER* m, cmsHPROFILE hNamedColor, cmsUInt32Number
NamedColorList = cmsGetNamedColorList(xform);
if (NamedColorList == NULL) return 0;
if (NamedColorList == NULL) {
cmsDeleteTransform(xform);
return 0;
}
_cmsIOPrintf(m, "<<\n");
_cmsIOPrintf(m, "(colorlistcomment) (%s) \n", "Named profile");

@ -152,7 +152,7 @@ cmsBool BlackPointAsDarkerColorant(cmsHPROFILE hInput,
// Convert black to Lab
cmsDoTransform(xform, Black, &Lab, 1);
// Force it to be neutral, check for inconsistences
// Force it to be neutral, check for inconsistencies
Lab.a = Lab.b = 0;
if (Lab.L > 50 || Lab.L < 0) Lab.L = 0;

@ -122,7 +122,7 @@ cmsBool RegisterTypesPlugin(cmsContext id, cmsPluginBase* Data, _cmsMemoryClient
return TRUE;
}
// Return handler for a given type or NULL if not found. Shared between normal types and MPE. It first tries the additons
// Return handler for a given type or NULL if not found. Shared between normal types and MPE. It first tries the additions
// made by plug-ins and then the built-in defaults.
static
cmsTagTypeHandler* GetHandler(cmsTagTypeSignature sig, _cmsTagTypeLinkedList* PluginLinkedList, _cmsTagTypeLinkedList* DefaultLinkedList)
@ -954,6 +954,7 @@ static
void *Type_Text_Description_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
{
char* Text = NULL;
wchar_t* UnicodeString = NULL;
cmsMLU* mlu = NULL;
cmsUInt32Number AsciiCount;
cmsUInt32Number i, UnicodeCode, UnicodeCount;
@ -973,7 +974,7 @@ void *Type_Text_Description_Read(struct _cms_typehandler_struct* self, cmsIOHAND
if (SizeOfTag < AsciiCount) return NULL;
// All seems Ok, allocate the container
mlu = cmsMLUalloc(self ->ContextID, 1);
mlu = cmsMLUalloc(self ->ContextID, 2);
if (mlu == NULL) return NULL;
// As many memory as size of tag
@ -998,15 +999,30 @@ void *Type_Text_Description_Read(struct _cms_typehandler_struct* self, cmsIOHAND
if (!_cmsReadUInt32Number(io, &UnicodeCount)) goto Done;
SizeOfTag -= 2* sizeof(cmsUInt32Number);
if (SizeOfTag < UnicodeCount*sizeof(cmsUInt16Number)) goto Done;
if (UnicodeCount == 0 || SizeOfTag < UnicodeCount*sizeof(cmsUInt16Number)) goto Done;
for (i=0; i < UnicodeCount; i++) {
if (!io ->Read(io, &Dummy, sizeof(cmsUInt16Number), 1)) goto Done;
UnicodeString = (wchar_t*)_cmsMallocZero(self->ContextID, (UnicodeCount + 1) * sizeof(wchar_t));
if (UnicodeString == NULL) goto Done;
if (!_cmsReadWCharArray(io, UnicodeCount, UnicodeString)) {
_cmsFree(self->ContextID, (void*)UnicodeString);
goto Done;
}
UnicodeString[UnicodeCount] = 0;
if (!cmsMLUsetWide(mlu, cmsV2Unicode, cmsV2Unicode, UnicodeString)) {
_cmsFree(self->ContextID, (void*)UnicodeString);
goto Done;
}
_cmsFree(self->ContextID, (void*)UnicodeString);
UnicodeString = NULL;
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
// data that strictly required. We need to skip it as this type may come
// embedded in other types.
if (SizeOfTag >= sizeof(cmsUInt16Number) + sizeof(cmsUInt8Number) + 67) {
@ -1026,6 +1042,7 @@ Done:
return mlu;
Error:
if (UnicodeString) _cmsFree(self->ContextID, (void*)UnicodeString);
if (Text) _cmsFree(self ->ContextID, (void*) Text);
if (mlu) cmsMLUfree(mlu);
return NULL;
@ -1078,7 +1095,7 @@ cmsBool Type_Text_Description_Write(struct _cms_typehandler_struct* self, cmsIO
// Get both representations.
cmsMLUgetASCII(mlu, cmsNoLanguage, cmsNoCountry, Text, len * sizeof(char));
cmsMLUgetWide(mlu, cmsNoLanguage, cmsNoCountry, Wide, len * sizeof(wchar_t));
cmsMLUgetWide(mlu, cmsV2Unicode, cmsV2Unicode, Wide, len * sizeof(wchar_t));
}
// Tell the real text len including the null terminator and padding
@ -1577,8 +1594,6 @@ void *Type_MLU_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsU
if (SizeOfTag == 0)
{
Block = NULL;
NumOfWchar = 0;
}
else
{
@ -1940,7 +1955,7 @@ Error:
// 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)
cmsBool Type_LUT8_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
{
cmsUInt32Number j, nTabSize, i;
cmsUInt8Number val;
@ -1953,6 +1968,12 @@ cmsBool Type_LUT8_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io,
// Disassemble the LUT into components.
mpe = NewLUT -> Elements;
if (mpe == NULL) { // Should never be empty. Corrupted?
cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "empty LUT8 is not supported");
return FALSE;
}
if (mpe ->Type == cmsSigMatrixElemType) {
if (mpe->InputChannels != 3 || mpe->OutputChannels != 3) return FALSE;
@ -2694,8 +2715,8 @@ cmsBool WriteSetOfCurves(struct _cms_typehandler_struct* self, cmsIOHANDLER* io,
// 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)) )
if ((Curves[i] ->nSegments == 0) || // 16 bits tabulated
((Curves[i]->nSegments == 3) && (Curves[i] ->Segments[1].Type == 0)) ) // Floating-point tabulated
CurrentType = cmsSigCurveType;
else
if (Curves[i] ->Segments[0].Type < 0)
@ -4459,8 +4480,8 @@ void *Type_MPEclut_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io,
if (!_cmsReadUInt16Number(io, &InputChans)) return NULL;
if (!_cmsReadUInt16Number(io, &OutputChans)) return NULL;
if (InputChans == 0) goto Error;
if (OutputChans == 0) goto Error;
if (InputChans == 0 || InputChans >= cmsMAXCHANNELS) goto Error;
if (OutputChans == 0 || OutputChans >= cmsMAXCHANNELS) goto Error;
if (io ->Read(io, Dimensions8, sizeof(cmsUInt8Number), 16) != 16)
goto Error;
@ -5250,11 +5271,13 @@ cmsBool WriteOneMLUC(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, _c
}
Before = io ->Tell(io);
e ->Offsets[i] = Before - BaseOffset;
if (e->Offsets != NULL)
e ->Offsets[i] = Before - BaseOffset;
if (!Type_MLU_Write(self, io, (void*) mlu, 1)) return FALSE;
e ->Sizes[i] = io ->Tell(io) - Before;
if (e->Sizes != NULL)
e ->Sizes[i] = io ->Tell(io) - Before;
return TRUE;
}
@ -5499,6 +5522,216 @@ void Type_VideoSignal_Free(struct _cms_typehandler_struct* self, void* Ptr)
_cmsFree(self->ContextID, Ptr);
}
// ********************************************************************************
// Microsoft's MHC2 Type support
// ********************************************************************************
static
void SetIdentity(cmsFloat64Number XYZ2XYZmatrix[3][4])
{
XYZ2XYZmatrix[0][0] = 1.0; XYZ2XYZmatrix[0][1] = 0.0; XYZ2XYZmatrix[0][2] = 0.0; XYZ2XYZmatrix[0][3] = 0.0;
XYZ2XYZmatrix[1][0] = 0.0; XYZ2XYZmatrix[1][1] = 1.0; XYZ2XYZmatrix[1][2] = 0.0; XYZ2XYZmatrix[1][3] = 0.0;
XYZ2XYZmatrix[2][0] = 0.0; XYZ2XYZmatrix[2][1] = 0.0; XYZ2XYZmatrix[2][2] = 1.0; XYZ2XYZmatrix[2][3] = 0.0;
}
static
cmsBool CloseEnough(cmsFloat64Number a, cmsFloat64Number b)
{
return fabs(b - a) < (1.0 / 65535.0);
}
cmsBool IsIdentity(cmsFloat64Number XYZ2XYZmatrix[3][4])
{
cmsFloat64Number Identity[3][4];
int i, j;
SetIdentity(Identity);
for (i = 0; i < 3; i++)
for (j = 0; j < 4; j++)
if (!CloseEnough(XYZ2XYZmatrix[i][j], Identity[i][j])) return FALSE;
return TRUE;
}
static
void Type_MHC2_Free(struct _cms_typehandler_struct* self, void* Ptr)
{
cmsMHC2Type* mhc2 = (cmsMHC2Type*)Ptr;
if (mhc2->RedCurve != NULL) _cmsFree(self->ContextID, mhc2->RedCurve);
if (mhc2->GreenCurve != NULL) _cmsFree(self->ContextID, mhc2->GreenCurve);
if (mhc2->BlueCurve != NULL) _cmsFree(self->ContextID, mhc2->BlueCurve);
_cmsFree(self->ContextID, Ptr);
}
void* Type_MHC2_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n)
{
cmsMHC2Type* mhc2 = _cmsDupMem(self->ContextID, Ptr, sizeof(cmsMHC2Type));
mhc2->RedCurve = _cmsDupMem(self->ContextID, mhc2->RedCurve, mhc2->CurveEntries*sizeof(cmsFloat64Number));
mhc2->GreenCurve = _cmsDupMem(self->ContextID, mhc2->GreenCurve, mhc2->CurveEntries * sizeof(cmsFloat64Number));
mhc2->BlueCurve = _cmsDupMem(self->ContextID, mhc2->BlueCurve, mhc2->CurveEntries * sizeof(cmsFloat64Number));
if (mhc2->RedCurve == NULL ||
mhc2->GreenCurve == NULL ||
mhc2->BlueCurve == NULL) {
Type_MHC2_Free(self, mhc2);
return NULL;
}
return mhc2;
cmsUNUSED_PARAMETER(n);
}
static
cmsBool WriteDoubles(cmsIOHANDLER* io, cmsUInt32Number n, cmsFloat64Number* Values)
{
cmsUInt32Number i;
for (i = 0; i < n; i++) {
if (!_cmsWrite15Fixed16Number(io, *Values++)) return FALSE;
}
return TRUE;
}
static
cmsBool Type_MHC2_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems)
{
cmsMHC2Type* mhc2 = (cmsMHC2Type*)Ptr;
cmsUInt32Number BaseOffset = io->Tell(io) - sizeof(_cmsTagBase);
cmsUInt32Number TablesOffsetPos;
cmsUInt32Number MatrixOffset;
cmsUInt32Number OffsetRedTable, OffsetGreenTable, OffsetBlueTable;
if (!_cmsWriteUInt32Number(io, 0)) return FALSE;
if (!_cmsWriteUInt32Number(io, mhc2->CurveEntries)) return FALSE;
if (!_cmsWrite15Fixed16Number(io, mhc2->MinLuminance)) return FALSE;
if (!_cmsWrite15Fixed16Number(io, mhc2->PeakLuminance)) return FALSE;
TablesOffsetPos = io->Tell(io);
if (!_cmsWriteUInt32Number(io, 0)) return FALSE; // Matrix
if (!_cmsWriteUInt32Number(io, 0)) return FALSE; // Curve R
if (!_cmsWriteUInt32Number(io, 0)) return FALSE; // Curve G
if (!_cmsWriteUInt32Number(io, 0)) return FALSE; // Curve B
if (IsIdentity(mhc2->XYZ2XYZmatrix))
{
MatrixOffset = 0;
}
else
{
MatrixOffset = io->Tell(io) - BaseOffset;
if (!WriteDoubles(io, 3 * 4, &mhc2->XYZ2XYZmatrix[0][0])) return FALSE;
}
OffsetRedTable = io->Tell(io) - BaseOffset;
if (!WriteDoubles(io, mhc2->CurveEntries, mhc2->RedCurve)) return FALSE;
OffsetGreenTable = io->Tell(io) - BaseOffset;
if (!WriteDoubles(io, mhc2->CurveEntries, mhc2->GreenCurve)) return FALSE;
OffsetBlueTable = io->Tell(io) - BaseOffset;
if (!WriteDoubles(io, mhc2->CurveEntries, mhc2->BlueCurve)) return FALSE;
if (!io->Seek(io, TablesOffsetPos)) return FALSE;
if (!_cmsWriteUInt32Number(io, MatrixOffset)) return FALSE;
if (!_cmsWriteUInt32Number(io, OffsetRedTable)) return FALSE;
if (!_cmsWriteUInt32Number(io, OffsetGreenTable)) return FALSE;
if (!_cmsWriteUInt32Number(io, OffsetBlueTable)) return FALSE;
return TRUE;
cmsUNUSED_PARAMETER(self);
cmsUNUSED_PARAMETER(nItems);
}
static
cmsBool ReadDoublesAt(cmsIOHANDLER* io, cmsUInt32Number At, cmsUInt32Number n, cmsFloat64Number* Values)
{
cmsUInt32Number CurrentPos = io->Tell(io);
cmsUInt32Number i;
if (!io->Seek(io, At)) return FALSE;
for (i = 0; i < n; i++) {
if (!_cmsRead15Fixed16Number(io, Values++)) return FALSE;
}
if (!io->Seek(io, CurrentPos)) return FALSE;
return TRUE;
}
static
void* Type_MHC2_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag)
{
cmsMHC2Type* mhc2 = NULL;
cmsUInt32Number BaseOffset = io->Tell(io) - sizeof(_cmsTagBase);
cmsUInt32Number MatrixOffset;
cmsUInt32Number OffsetRedTable, OffsetGreenTable, OffsetBlueTable;
if (!_cmsReadUInt32Number(io, NULL)) return NULL;
mhc2 = (cmsMHC2Type*)_cmsCalloc(self->ContextID, 1, sizeof(cmsMHC2Type));
if (mhc2 == NULL) return NULL;
if (!_cmsReadUInt32Number(io, &mhc2->CurveEntries)) goto Error;
if (mhc2->CurveEntries > 4096) goto Error;
mhc2->RedCurve = (cmsFloat64Number*)_cmsCalloc(self->ContextID, mhc2->CurveEntries, sizeof(cmsFloat64Number));
mhc2->GreenCurve = (cmsFloat64Number*)_cmsCalloc(self->ContextID, mhc2->CurveEntries, sizeof(cmsFloat64Number));
mhc2->BlueCurve = (cmsFloat64Number*)_cmsCalloc(self->ContextID, mhc2->CurveEntries, sizeof(cmsFloat64Number));
if (mhc2->RedCurve == NULL ||
mhc2->GreenCurve == NULL ||
mhc2->BlueCurve == NULL) goto Error;
if (!_cmsRead15Fixed16Number(io, &mhc2->MinLuminance)) goto Error;
if (!_cmsRead15Fixed16Number(io, &mhc2->PeakLuminance)) goto Error;
if (!_cmsReadUInt32Number(io, &MatrixOffset)) goto Error;
if (!_cmsReadUInt32Number(io, &OffsetRedTable)) goto Error;
if (!_cmsReadUInt32Number(io, &OffsetGreenTable)) goto Error;
if (!_cmsReadUInt32Number(io, &OffsetBlueTable)) goto Error;
if (MatrixOffset == 0)
SetIdentity(mhc2->XYZ2XYZmatrix);
else
{
if (!ReadDoublesAt(io, BaseOffset + MatrixOffset, 3*4, &mhc2->XYZ2XYZmatrix[0][0])) goto Error;
}
if (!ReadDoublesAt(io, BaseOffset + OffsetRedTable, mhc2->CurveEntries, mhc2->RedCurve)) goto Error;
if (!ReadDoublesAt(io, BaseOffset + OffsetGreenTable, mhc2->CurveEntries, mhc2->GreenCurve)) goto Error;
if (!ReadDoublesAt(io, BaseOffset + OffsetBlueTable, mhc2->CurveEntries, mhc2->BlueCurve)) goto Error;
// Success
*nItems = 1;
return mhc2;
Error:
Type_MHC2_Free(self, mhc2);
return NULL;
cmsUNUSED_PARAMETER(SizeOfTag);
}
// ********************************************************************************
// Type support main routines
// ********************************************************************************
@ -5538,7 +5771,8 @@ static const _cmsTagTypeLinkedList SupportedTagTypes[] = {
{TYPE_HANDLER(cmsSigProfileSequenceIdType, ProfileSequenceId), (_cmsTagTypeLinkedList*) &SupportedTagTypes[29] },
{TYPE_HANDLER(cmsSigDictType, Dictionary), (_cmsTagTypeLinkedList*) &SupportedTagTypes[30] },
{TYPE_HANDLER(cmsSigcicpType, VideoSignal), (_cmsTagTypeLinkedList*) &SupportedTagTypes[31] },
{TYPE_HANDLER(cmsSigVcgtType, vcgt), NULL }
{TYPE_HANDLER(cmsSigVcgtType, vcgt), (_cmsTagTypeLinkedList*) &SupportedTagTypes[32] },
{TYPE_HANDLER(cmsSigMHC2Type, MHC2), NULL }
};
@ -5734,7 +5968,8 @@ static _cmsTagLinkedList SupportedTags[] = {
{ cmsSigProfileDescriptionMLTag,{ 1, 1, { cmsSigMultiLocalizedUnicodeType}, NULL}, &SupportedTags[63]},
{ cmsSigcicpTag, { 1, 1, { cmsSigcicpType}, NULL }, &SupportedTags[64]},
{ cmsSigArgyllArtsTag, { 9, 1, { cmsSigS15Fixed16ArrayType}, NULL}, NULL}
{ cmsSigArgyllArtsTag, { 9, 1, { cmsSigS15Fixed16ArrayType}, NULL}, &SupportedTags[65]},
{ cmsSigMHC2Tag, { 1, 1, { cmsSigMHC2Type }, NULL}, NULL}
};

@ -435,10 +435,9 @@ cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLinkTHR(cmsContext ContextID,
if (Limit < 0.0 || Limit > 400) {
cmsSignalError(ContextID, cmsERROR_RANGE, "InkLimiting: Limit should be between 0..400");
if (Limit < 0) Limit = 0;
cmsSignalError(ContextID, cmsERROR_RANGE, "InkLimiting: Limit should be between 1..400");
if (Limit < 1) Limit = 1;
if (Limit > 400) Limit = 400;
}
hICC = cmsCreateProfilePlaceholder(ContextID);
@ -701,6 +700,127 @@ cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfile(void)
return cmsCreate_sRGBProfileTHR(NULL);
}
/**
* Oklab colorspace profile (experimental)
*
* This virtual profile cannot be saved as an ICC file
*/
cmsHPROFILE cmsCreate_OkLabProfile(cmsContext ctx)
{
cmsStage* XYZPCS = _cmsStageNormalizeFromXyzFloat(ctx);
cmsStage* PCSXYZ = _cmsStageNormalizeToXyzFloat(ctx);
const double M_D65_D50[] =
{
1.047886, 0.022919, -0.050216,
0.029582, 0.990484, -0.017079,
-0.009252, 0.015073, 0.751678
};
const double M_D50_D65[] =
{
0.955512609517083, -0.023073214184645, 0.063308961782107,
-0.028324949364887, 1.009942432477107, 0.021054814890112,
0.012328875695483, -0.020535835374141, 1.330713916450354
};
cmsStage* D65toD50 = cmsStageAllocMatrix(ctx, 3, 3, M_D65_D50, NULL);
cmsStage* D50toD65 = cmsStageAllocMatrix(ctx, 3, 3, M_D50_D65, NULL);
const double M_D65_LMS[] =
{
0.8189330101, 0.3618667424, -0.1288597137,
0.0329845436, 0.9293118715, 0.0361456387,
0.0482003018, 0.2643662691, 0.6338517070
};
const double M_LMS_D65[] =
{
1.227013851103521, -0.557799980651822, 0.281256148966468,
-0.040580178423281, 1.112256869616830, -0.071676678665601,
-0.076381284505707, -0.421481978418013, 1.586163220440795
};
cmsStage* D65toLMS = cmsStageAllocMatrix(ctx, 3, 3, M_D65_LMS, NULL);
cmsStage* LMStoD65 = cmsStageAllocMatrix(ctx, 3, 3, M_LMS_D65, NULL);
cmsToneCurve* CubeRoot = cmsBuildGamma(ctx, 1.0 / 3.0);
cmsToneCurve* Cube = cmsBuildGamma(ctx, 3.0);
cmsToneCurve* Roots[3] = { CubeRoot, CubeRoot, CubeRoot };
cmsToneCurve* Cubes[3] = { Cube, Cube, Cube };
cmsStage* NonLinearityFw = cmsStageAllocToneCurves(ctx, 3, Roots);
cmsStage* NonLinearityRv = cmsStageAllocToneCurves(ctx, 3, Cubes);
const double M_LMSprime_OkLab[] =
{
0.2104542553, 0.7936177850, -0.0040720468,
1.9779984951, -2.4285922050, 0.4505937099,
0.0259040371, 0.7827717662, -0.8086757660
};
const double M_OkLab_LMSprime[] =
{
0.999999998450520, 0.396337792173768, 0.215803758060759,
1.000000008881761, -0.105561342323656, -0.063854174771706,
1.000000054672411, -0.089484182094966, -1.291485537864092
};
cmsStage* LMSprime_OkLab = cmsStageAllocMatrix(ctx, 3, 3, M_LMSprime_OkLab, NULL);
cmsStage* OkLab_LMSprime = cmsStageAllocMatrix(ctx, 3, 3, M_OkLab_LMSprime, NULL);
cmsPipeline* AToB = cmsPipelineAlloc(ctx, 3, 3);
cmsPipeline* BToA = cmsPipelineAlloc(ctx, 3, 3);
cmsHPROFILE hProfile = cmsCreateProfilePlaceholder(ctx);
cmsSetProfileVersion(hProfile, 4.4);
cmsSetDeviceClass(hProfile, cmsSigColorSpaceClass);
cmsSetColorSpace(hProfile, cmsSig3colorData);
cmsSetPCS(hProfile, cmsSigXYZData);
cmsSetHeaderRenderingIntent(hProfile, INTENT_RELATIVE_COLORIMETRIC);
/**
* Conversion PCS (XYZ/D50) to OkLab
*/
if (!cmsPipelineInsertStage(BToA, cmsAT_END, PCSXYZ)) goto error;
if (!cmsPipelineInsertStage(BToA, cmsAT_END, D50toD65)) goto error;
if (!cmsPipelineInsertStage(BToA, cmsAT_END, D65toLMS)) goto error;
if (!cmsPipelineInsertStage(BToA, cmsAT_END, NonLinearityFw)) goto error;
if (!cmsPipelineInsertStage(BToA, cmsAT_END, LMSprime_OkLab)) goto error;
if (!cmsWriteTag(hProfile, cmsSigBToA0Tag, BToA)) goto error;
if (!cmsPipelineInsertStage(AToB, cmsAT_END, OkLab_LMSprime)) goto error;
if (!cmsPipelineInsertStage(AToB, cmsAT_END, NonLinearityRv)) goto error;
if (!cmsPipelineInsertStage(AToB, cmsAT_END, LMStoD65)) goto error;
if (!cmsPipelineInsertStage(AToB, cmsAT_END, D65toD50)) goto error;
if (!cmsPipelineInsertStage(AToB, cmsAT_END, XYZPCS)) goto error;
if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, AToB)) goto error;
cmsPipelineFree(BToA);
cmsPipelineFree(AToB);
cmsFreeToneCurve(CubeRoot);
cmsFreeToneCurve(Cube);
return hProfile;
error:
cmsPipelineFree(BToA);
cmsPipelineFree(AToB);
cmsFreeToneCurve(CubeRoot);
cmsFreeToneCurve(Cube);
cmsCloseProfile(hProfile);
return NULL;
}
typedef struct {
@ -1060,7 +1180,7 @@ cmsBool CheckOne(const cmsAllowedLUT* Tab, const cmsPipeline* Lut)
for (n=0, mpe = Lut ->Elements; mpe != NULL; mpe = mpe ->Next, n++) {
if (n > Tab ->nTypes) return FALSE;
if (n >= Tab ->nTypes) return FALSE;
if (cmsStageType(mpe) != Tab ->MpeTypes[n]) return FALSE;
}
@ -1091,9 +1211,9 @@ const cmsAllowedLUT* FindCombination(const cmsPipeline* Lut, cmsBool IsV4, cmsTa
cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat64Number Version, cmsUInt32Number dwFlags)
{
cmsHPROFILE hProfile = NULL;
cmsUInt32Number FrmIn, FrmOut;
cmsInt32Number ChansIn, ChansOut;
int ColorSpaceBitsIn, ColorSpaceBitsOut;
cmsUInt32Number FrmIn, FrmOut;
cmsInt32Number ChansIn, ChansOut;
int ColorSpaceBitsIn, ColorSpaceBitsOut;
_cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform;
cmsPipeline* LUT = NULL;
cmsStage* mpe;
@ -1104,6 +1224,9 @@ cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat
_cmsAssert(hTransform != NULL);
// Check if the pipeline holding is valid
if (xform -> Lut == NULL) return NULL;
// Get the first mpe to check for named color
mpe = cmsPipelineGetPtrToFirstStage(xform ->Lut);

@ -943,7 +943,7 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut,
}
// Check whatever this is a true floating point transform
if (_cmsFormatterIsFloat(*OutputFormat)) {
if (_cmsFormatterIsFloat(*InputFormat) || _cmsFormatterIsFloat(*OutputFormat)) {
// Get formatter function always return a valid union, but the contents of this union may be NULL.
p ->FromInputFloat = _cmsGetFormatter(ContextID, *InputFormat, cmsFormatterInput, CMS_PACK_FLAGS_FLOAT).FmtFloat;
@ -1018,6 +1018,19 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut,
}
}
/**
* Check consistency for alpha channel copy
*/
if (*dwFlags & cmsFLAGS_COPY_ALPHA)
{
if (T_EXTRA(*InputFormat) != T_EXTRA(*OutputFormat))
{
cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Mismatched alpha channels");
cmsDeleteTransform(p);
return NULL;
}
}
p ->InputFormat = *InputFormat;
p ->OutputFormat = *OutputFormat;
p ->dwOriginalFlags = *dwFlags;

@ -52,7 +52,7 @@
//
//---------------------------------------------------------------------------------
//
// Version 2.15
// Version 2.16
//
#ifndef _lcms2_H
@ -105,12 +105,15 @@
#ifndef CMS_USE_CPP_API
# ifdef __cplusplus
# if __cplusplus >= 201703L
# define CMS_NO_REGISTER_KEYWORD 1
# endif
extern "C" {
# endif
#endif
// Version/release
#define LCMS_VERSION 2150
#define LCMS_VERSION 2160
// I will give the chance of redefining basic types for compilers that are not fully C99 compliant
#ifndef CMS_BASIC_TYPES_ALREADY_DEFINED
@ -354,7 +357,8 @@ typedef enum {
cmsSigUInt8ArrayType = 0x75693038, // 'ui08'
cmsSigVcgtType = 0x76636774, // 'vcgt'
cmsSigViewingConditionsType = 0x76696577, // 'view'
cmsSigXYZType = 0x58595A20 // 'XYZ '
cmsSigXYZType = 0x58595A20, // 'XYZ '
cmsSigMHC2Type = 0x4D484332 // 'MHC2'
} cmsTagTypeSignature;
@ -432,7 +436,8 @@ typedef enum {
cmsSigVcgtTag = 0x76636774, // 'vcgt'
cmsSigMetaTag = 0x6D657461, // 'meta'
cmsSigcicpTag = 0x63696370, // 'cicp'
cmsSigArgyllArtsTag = 0x61727473 // 'arts'
cmsSigArgyllArtsTag = 0x61727473, // 'arts'
cmsSigMHC2Tag = 0x4D484332 // 'MHC2'
} cmsTagSignature;
@ -977,6 +982,7 @@ typedef void* cmsHTRANSFORM;
#define TYPE_RGB_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(0))
#define TYPE_BGR_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(0)|DOSWAP_SH(1))
#define TYPE_CMYK_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(0))
#define TYPE_OKLAB_DBL (FLOAT_SH(1)|COLORSPACE_SH(PT_MCH3)|CHANNELS_SH(3)|BYTES_SH(0))
// IEEE 754-2008 "half"
#define TYPE_GRAY_HALF_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(2))
@ -1077,6 +1083,19 @@ typedef struct {
} cmsVideoSignalType;
typedef struct {
cmsUInt32Number CurveEntries;
cmsFloat64Number* RedCurve;
cmsFloat64Number* GreenCurve;
cmsFloat64Number* BlueCurve;
cmsFloat64Number MinLuminance; // ST.2086 min luminance in nits
cmsFloat64Number PeakLuminance; // ST.2086 peak luminance in nits
cmsFloat64Number XYZ2XYZmatrix[3][4];
} cmsMHC2Type;
// Get LittleCMS version (for shared objects) -----------------------------------------------------------------------------
@ -1249,7 +1268,8 @@ CMSAPI cmsBool CMSEXPORT cmsIsToneCurveMonotonic(const cmsToneCurve* t
CMSAPI cmsBool CMSEXPORT cmsIsToneCurveDescending(const cmsToneCurve* t);
CMSAPI cmsInt32Number CMSEXPORT cmsGetToneCurveParametricType(const cmsToneCurve* t);
CMSAPI cmsFloat64Number CMSEXPORT cmsEstimateGamma(const cmsToneCurve* t, cmsFloat64Number Precision);
CMSAPI cmsFloat64Number* CMSEXPORT cmsGetToneCurveParams(const cmsToneCurve* t);
CMSAPI const cmsCurveSegment* CMSEXPORT cmsGetToneCurveSegment(cmsInt32Number n, const cmsToneCurve* t);
// Tone curve tabular estimation
CMSAPI cmsUInt32Number CMSEXPORT cmsGetToneCurveEstimatedTableEntries(const cmsToneCurve* t);
@ -1343,8 +1363,11 @@ CMSAPI cmsBool CMSEXPORT cmsSliceSpaceFloat(cmsUInt32Number nInputs, c
typedef struct _cms_MLU_struct cmsMLU;
#define cmsNoLanguage "\0\0"
#define cmsNoCountry "\0\0"
#define cmsNoLanguage "\0\0"
#define cmsNoCountry "\0\0"
// Special language/country to retrieve unicode field for description in V2 profiles. Use with care.
#define cmsV2Unicode "\xff\xff"
CMSAPI cmsMLU* CMSEXPORT cmsMLUalloc(cmsContext ContextID, cmsUInt32Number nItems);
CMSAPI void CMSEXPORT cmsMLUfree(cmsMLU* mlu);
@ -1356,6 +1379,9 @@ CMSAPI cmsBool CMSEXPORT cmsMLUsetASCII(cmsMLU* mlu,
CMSAPI cmsBool CMSEXPORT cmsMLUsetWide(cmsMLU* mlu,
const char LanguageCode[3], const char CountryCode[3],
const wchar_t* WideString);
CMSAPI cmsBool CMSEXPORT cmsMLUsetUTF8(cmsMLU* mlu,
const char LanguageCode[3], const char CountryCode[3],
const char* UTF8String);
CMSAPI cmsUInt32Number CMSEXPORT cmsMLUgetASCII(const cmsMLU* mlu,
const char LanguageCode[3], const char CountryCode[3],
@ -1364,6 +1390,10 @@ CMSAPI cmsUInt32Number CMSEXPORT cmsMLUgetASCII(const cmsMLU* mlu,
CMSAPI cmsUInt32Number CMSEXPORT cmsMLUgetWide(const cmsMLU* mlu,
const char LanguageCode[3], const char CountryCode[3],
wchar_t* Buffer, cmsUInt32Number BufferSize);
CMSAPI cmsUInt32Number CMSEXPORT cmsMLUgetUTF8(const cmsMLU* mlu,
const char LanguageCode[3], const char CountryCode[3],
char* Buffer, cmsUInt32Number BufferSize);
CMSAPI cmsBool CMSEXPORT cmsMLUgetTranslation(const cmsMLU* mlu,
const char LanguageCode[3], const char CountryCode[3],
@ -1588,6 +1618,10 @@ CMSAPI cmsUInt32Number CMSEXPORT cmsGetProfileInfoASCII(cmsHPROFILE hProfile,
const char LanguageCode[3], const char CountryCode[3],
char* Buffer, cmsUInt32Number BufferSize);
CMSAPI cmsUInt32Number CMSEXPORT cmsGetProfileInfoUTF8(cmsHPROFILE hProfile, cmsInfoType Info,
const char LanguageCode[3], const char CountryCode[3],
char* Buffer, cmsUInt32Number BufferSize);
// IO handlers ----------------------------------------------------------------------------------------------------------
typedef struct _cms_io_handler cmsIOHANDLER;
@ -1650,6 +1684,9 @@ CMSAPI cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLinkTHR(cmsContext C
CMSAPI cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLink(cmsColorSpaceSignature ColorSpace, cmsFloat64Number Limit);
CMSAPI cmsHPROFILE CMSEXPORT cmsCreateDeviceLinkFromCubeFile(const char* cFileName);
CMSAPI cmsHPROFILE CMSEXPORT cmsCreateDeviceLinkFromCubeFileTHR(cmsContext ContextID, const char* cFileName);
CMSAPI cmsHPROFILE CMSEXPORT cmsCreateLab2ProfileTHR(cmsContext ContextID, const cmsCIExyY* WhitePoint);
CMSAPI cmsHPROFILE CMSEXPORT cmsCreateLab2Profile(const cmsCIExyY* WhitePoint);
@ -1662,6 +1699,8 @@ CMSAPI cmsHPROFILE CMSEXPORT cmsCreateXYZProfile(void);
CMSAPI cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfileTHR(cmsContext ContextID);
CMSAPI cmsHPROFILE CMSEXPORT cmsCreate_sRGBProfile(void);
CMSAPI cmsHPROFILE CMSEXPORT cmsCreate_OkLabProfile(cmsContext ctx);
CMSAPI cmsHPROFILE CMSEXPORT cmsCreateBCHSWabstractProfileTHR(cmsContext ContextID,
cmsUInt32Number nLUTPoints,
cmsFloat64Number Bright,

@ -288,6 +288,7 @@ typedef CRITICAL_SECTION _cmsMutex;
#ifdef _MSC_VER
# if (_MSC_VER >= 1800)
# pragma warning(disable : 26135)
# pragma warning(disable : 4127)
# endif
#endif
@ -545,7 +546,7 @@ struct _cmsContext_struct {
struct _cmsContext_struct* Next; // Points to next context in the new style
_cmsSubAllocator* MemPool; // The memory pool that stores context data
void* chunks[MemoryClientMax]; // array of pointers to client chunks. Memory itself is hold in the suballocator.
void* chunks[MemoryClientMax]; // array of pointers to client chunks. Memory itself is held in the suballocator.
// If NULL, then it reverts to global Context0
_cmsMemPluginChunkType DefaultMemoryManager; // The allocators used for creating the context itself. Cannot be overridden
@ -839,6 +840,9 @@ typedef struct _cms_iccprofile_struct {
// Creation time
struct tm Created;
// Color management module identification
cmsUInt32Number CMM;
// Only most important items found in ICC profiles
cmsUInt32Number Version;
cmsProfileClassSignature DeviceClass;
@ -846,6 +850,7 @@ typedef struct _cms_iccprofile_struct {
cmsColorSpaceSignature PCS;
cmsUInt32Number RenderingIntent;
cmsPlatformSignature platform;
cmsUInt32Number flags;
cmsUInt32Number manufacturer, model;
cmsUInt64Number attributes;