diff --git a/THIRD_PARTY_README b/THIRD_PARTY_README index 2b296592728..4df2d4ebc53 100644 --- a/THIRD_PARTY_README +++ b/THIRD_PARTY_README @@ -1725,31 +1725,104 @@ THE SOFTWARE. ------------------------------------------------------------------------------- -%% This notice is provided with respect to Little CMS 2.12, which may be +%% This notice is provided with respect to Little CMS 2.16, which may be included with JRE 8, JDK 8, and OpenJDK 8. --- begin of LICENSE --- -Little CMS -Copyright (c) 1998-2020 Marti Maria Saguer +MIT License -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: +Copyright (C) 1998-2023 Marti Maria Saguer -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +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 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. +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. + +--------------------------------- +The below license applies to the following files: +liblcms/cmssm.c + +Copyright 2001, softSurfer (www.softsurfer.com) + +This code may be freely used and modified for any purpose +providing that this copyright notice is included with it. +SoftSurfer makes no warranty for this code, and cannot be held +liable for any real or imagined damage resulting from its use. +Users of this code must verify correctness for their application. + +AUTHORS File Information: + +Main Author +------------ +Marti Maria + + +Contributors +------------ +Bob Friesenhahn +Kai-Uwe Behrmann +Stuart Nixon +Jordi Vilar +Richard Hughes +Auke Nauta +Chris Evans (Google) +Lorenzo Ridolfi +Robin Watts (Artifex) +Shawn Pedersen +Andrew Brygin +Samuli Suominen +Florian Hˆch +Aurelien Jarno +Claudiu Cebuc +Michael Vhrel (Artifex) +Michal Cihar +Daniel Kaneider +Mateusz Jurczyk (Google) +Paul Miller +SÈbastien LÈon +Christian Schmitz +XhmikosR +Stanislav Brabec (SuSe) +Leonhard Gruenschloss (Google) +Patrick Noffke +Christopher James Halse Rogers +John Hein +Thomas Weber (Debian) +Mark Allen +Noel Carboni +Sergei Trofimovic +Philipp Knechtges +Amyspark +Lovell Fuller +Eli Schwartz +Diogo Teles Sant'Anna + +Special Thanks +-------------- +Artifex software +AlienSkin software +libVIPS +Jan Morovic +Jos Vernon (WebSupergoo) +Harald Schneider (Maxon) +Christian Albrecht +Dimitrios Anastassakis +Lemke Software +Tim Zaman --- end of LICENSE --- diff --git a/jdk/src/share/native/sun/java2d/cmm/lcms/UPDATING.txt b/jdk/src/share/native/sun/java2d/cmm/lcms/UPDATING.txt new file mode 100644 index 00000000000..01d8b65d2b6 --- /dev/null +++ b/jdk/src/share/native/sun/java2d/cmm/lcms/UPDATING.txt @@ -0,0 +1,23 @@ +# Tips and tasks when updating LittleCMS sources to a newer version. + +Download and unzip latest release from https://sourceforge.net/projects/lcms/files/ +Replace files in jdk/src/share/native/sun/java2d/cmm/lcms with files from unzipped src and include folders +Replace is important because the lcms sources here are just the subset needed by JDK. +This is deliberate, so when updating be sure to import only the same files. +If a file has been removed from upstream you will notice it during the copy. +It should then be removed from the JDK sources. +If a new file is needed then the build will fail. Manually copy that in. + +Some re-editing of these updated files will be needed. +Use "expand" and "sed" to remove tabs and trailing white space from the imported files. +Re-apply the GPL headers used by the JDK. If done correctly these should then not +show up in the PR diff. + +Update THIRD_PARTY_README per the current license/copyrights/attributions etc. + +Build on all platforms. +If there are compiler warnings causing build failures, update the disabled warnings in +jdk/make/lib/Awt2dLibraries.gmk +Run all automated client tests on all platforms. +Also run J2Ddemo and pay particular attention to the "color" tab. +Visually verify the color transforms against the same with the current/previous JDK diff --git a/jdk/src/share/native/sun/java2d/cmm/lcms/cmsalpha.c b/jdk/src/share/native/sun/java2d/cmm/lcms/cmsalpha.c index 49ff22df764..78d3ca6b671 100644 --- a/jdk/src/share/native/sun/java2d/cmm/lcms/cmsalpha.c +++ b/jdk/src/share/native/sun/java2d/cmm/lcms/cmsalpha.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2020 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"), @@ -220,21 +220,21 @@ static void fromFLTto8(void* dst, const void* src) { cmsFloat32Number n = *(cmsFloat32Number*)src; - *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0f); + *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0); } static void fromFLTto16(void* dst, const void* src) { cmsFloat32Number n = *(cmsFloat32Number*)src; - *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0f); + *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0); } static void fromFLTto16SE(void* dst, const void* src) { cmsFloat32Number n = *(cmsFloat32Number*)src; - cmsUInt16Number i = _cmsQuickSaturateWord(n * 65535.0f); + cmsUInt16Number i = _cmsQuickSaturateWord(n * 65535.0); *(cmsUInt16Number*)dst = CHANGE_ENDIAN(i); } @@ -272,7 +272,7 @@ void fromHLFto8(void* dst, const void* src) { #ifndef CMS_NO_HALF_SUPPORT cmsFloat32Number n = _cmsHalf2Float(*(cmsUInt16Number*)src); - *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0f); + *(cmsUInt8Number*)dst = _cmsQuickSaturateByte(n * 255.0); #else cmsUNUSED_PARAMETER(dst); cmsUNUSED_PARAMETER(src); @@ -285,7 +285,7 @@ void fromHLFto16(void* dst, const void* src) { #ifndef CMS_NO_HALF_SUPPORT cmsFloat32Number n = _cmsHalf2Float(*(cmsUInt16Number*)src); - *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0f); + *(cmsUInt16Number*)dst = _cmsQuickSaturateWord(n * 65535.0); #else cmsUNUSED_PARAMETER(dst); cmsUNUSED_PARAMETER(src); @@ -297,7 +297,7 @@ void fromHLFto16SE(void* dst, const void* src) { #ifndef CMS_NO_HALF_SUPPORT cmsFloat32Number n = _cmsHalf2Float(*(cmsUInt16Number*)src); - cmsUInt16Number i = _cmsQuickSaturateWord(n * 65535.0f); + cmsUInt16Number i = _cmsQuickSaturateWord(n * 65535.0); *(cmsUInt16Number*)dst = CHANGE_ENDIAN(i); #else cmsUNUSED_PARAMETER(dst); @@ -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[]) { @@ -443,9 +443,9 @@ void ComputeIncrementsForChunky(cmsUInt32Number Format, cmsUInt32Number channelSize = trueBytesSize(Format); cmsUInt32Number pixelSize = channelSize * total_chans; - // Sanity check - if (total_chans <= 0 || total_chans >= cmsMAXCHANNELS) - return; + // Sanity check + if (total_chans <= 0 || total_chans >= cmsMAXCHANNELS) + 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); diff --git a/jdk/src/share/native/sun/java2d/cmm/lcms/cmscam02.c b/jdk/src/share/native/sun/java2d/cmm/lcms/cmscam02.c index ba902e06709..71a48d43de4 100644 --- a/jdk/src/share/native/sun/java2d/cmm/lcms/cmscam02.c +++ b/jdk/src/share/native/sun/java2d/cmm/lcms/cmscam02.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2020 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"), @@ -353,7 +353,7 @@ CAM02COLOR HPEtoCAT02(CAM02COLOR clr) M[5] = ((-0.7036 * 0.201908) + (1.6975 * 0.000008) + 0.0061); M[6] = (( 0.0030 * 1.910197) + (0.0136 * 0.370950)); M[7] = (( 0.0030 * -1.112124) + (0.0136 * 0.629054)); - M[8] = (( 0.0030 * 0.201908) + (0.0136 * 0.000008) + 0.9834);; + M[8] = (( 0.0030 * 0.201908) + (0.0136 * 0.000008) + 0.9834); clr.RGBc[0] = (clr.RGBp[0] * M[0]) + (clr.RGBp[1] * M[1]) + (clr.RGBp[2] * M[2]); clr.RGBc[1] = (clr.RGBp[0] * M[3]) + (clr.RGBp[1] * M[4]) + (clr.RGBp[2] * M[5]); diff --git a/jdk/src/share/native/sun/java2d/cmm/lcms/cmscgats.c b/jdk/src/share/native/sun/java2d/cmm/lcms/cmscgats.c index 07109b1e449..57725ae4731 100644 --- a/jdk/src/share/native/sun/java2d/cmm/lcms/cmscgats.c +++ b/jdk/src/share/native/sun/java2d/cmm/lcms/cmscgats.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2020 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"), @@ -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; @@ -162,13 +174,26 @@ typedef struct _FileContext { FILE* Stream; // File stream or NULL if holded in memory } FILECTX; -// This struct hold all information about an open IT8 handler. +//Very simple string typedef struct { + struct struct_it8* it8; + cmsInt32Number max; + cmsInt32Number len; + char* begin; + } string; + + +// This struct hold all information about an open IT8 handler. +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 @@ -182,8 +207,8 @@ typedef struct { cmsInt32Number inum; // integer value cmsFloat64Number dnum; // real value - char id[MAXID]; // identifier - char str[MAXSTR]; // string + string* id; // identifier + string* str; // string // Allowed keywords & datasets. They have visibility on whole stream KEYVALUE* ValidKeywords; @@ -228,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! @@ -242,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 @@ -268,7 +311,7 @@ static PROPERTY PredefinedProperties[] = { {"MATERIAL", WRITE_STRINGIFY}, // Identifies the material on which the target was produced using a code // uniquely identifying th e material. This is intend ed to be used for IT8.7 - // physical targets only (i.e . IT8.7/1 a nd IT8.7/2). + // physical targets only (i.e . IT8.7/1 and IT8.7/2). {"INSTRUMENTATION", WRITE_STRINGIFY}, // Used to report the specific instrumentation used (manufacturer and // model number) to generate the data reported. This data will often @@ -383,11 +426,70 @@ static const char* PredefinedSampleID[] = { //Forward declaration of some internal functions static void* AllocChunk(cmsIT8* it8, cmsUInt32Number size); +static +string* StringAlloc(cmsIT8* it8, int max) +{ + string* s = (string*) AllocChunk(it8, sizeof(string)); + if (s == NULL) return NULL; + + s->it8 = it8; + s->max = max; + s->len = 0; + s->begin = (char*) AllocChunk(it8, s->max); + + return s; +} + +static +void StringClear(string* s) +{ + s->len = 0; +} + +static +void StringAppend(string* s, char c) +{ + if (s->len + 1 >= s->max) + { + char* new_ptr; + + s->max *= 10; + new_ptr = (char*) AllocChunk(s->it8, s->max); + if (new_ptr != NULL && s->begin != NULL) + memcpy(new_ptr, s->begin, s->len); + + s->begin = new_ptr; + } + + if (s->begin != NULL) + { + s->begin[s->len++] = c; + s->begin[s->len] = 0; + } +} + +static +char* StringPtr(string* s) +{ + return s->begin; +} + +static +void StringCat(string* s, const char* c) +{ + while (*c) + { + StringAppend(s, *c); + c++; + } +} + + // Checks whatever c is a separator static cmsBool isseparator(int c) { - return (c == ' ') || (c == '\t') ; + return (c == ' ') || (c == '\t'); } // Checks whatever c is a valid identifier char @@ -408,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 @@ -447,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); @@ -535,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) @@ -708,14 +810,44 @@ cmsFloat64Number ParseFloatNumber(const char *Buffer) } +// Reads a string, special case to avoid infinite recursion on .include +static +void InStringSymbol(cmsIT8* it8) +{ + while (isseparator(it8->ch)) + NextCh(it8); + + if (it8->ch == '\'' || it8->ch == '\"') + { + int sng; + + sng = it8->ch; + StringClear(it8->str); + + NextCh(it8); + + while (it8->ch != sng) { + + if (it8->ch == '\n' || it8->ch == '\r' || it8->ch == 0) break; + else { + StringAppend(it8->str, (char)it8->ch); + NextCh(it8); + } + } + + it8->sy = SSTRING; + NextCh(it8); + } + else + SynError(it8, "String expected"); + +} + // Reads next symbol static void InSymbol(cmsIT8* it8) { - CMSREGISTER char *idptr; - CMSREGISTER int k; SYMBOL key; - int sng; do { @@ -724,21 +856,20 @@ void InSymbol(cmsIT8* it8) if (isfirstidchar(it8->ch)) { // Identifier - k = 0; - idptr = it8->id; + StringClear(it8->id); do { - if (++k < MAXID) *idptr++ = (char) it8->ch; + StringAppend(it8->id, (char) it8->ch); NextCh(it8); } while (isidchar(it8->ch)); - *idptr = '\0'; - - key = BinSrchKey(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; @@ -773,6 +904,7 @@ void InSymbol(cmsIT8* it8) if ((cmsFloat64Number) it8->inum * 16.0 + (cmsFloat64Number) j > (cmsFloat64Number)+2147483647.0) { SynError(it8, "Invalid hexadecimal number"); + it8->sy = SEOF; return; } @@ -794,6 +926,7 @@ void InSymbol(cmsIT8* it8) if ((cmsFloat64Number) it8->inum * 2.0 + j > (cmsFloat64Number)+2147483647.0) { SynError(it8, "Invalid binary number"); + it8->sy = SEOF; return; } @@ -834,26 +967,28 @@ void InSymbol(cmsIT8* it8) if (isidchar(it8 ->ch)) { + char buffer[127]; + if (it8 ->sy == SINUM) { - snprintf(it8->id, 127, "%d", it8->inum); + snprintf(buffer, sizeof(buffer), "%d", it8->inum); } else { - snprintf(it8->id, 127, it8 ->DoubleFormatter, it8->dnum); + snprintf(buffer, sizeof(buffer), it8 ->DoubleFormatter, it8->dnum); } - k = (int) strlen(it8 ->id); - idptr = it8 ->id + k; + StringClear(it8->id); + StringCat(it8->id, buffer); + do { - if (++k < MAXID) *idptr++ = (char) it8->ch; + StringAppend(it8->id, (char) it8->ch); NextCh(it8); } while (isidchar(it8->ch)); - *idptr = '\0'; it8->sy = SIDENT; } return; @@ -862,12 +997,8 @@ void InSymbol(cmsIT8* it8) else switch ((int) it8->ch) { - // EOF marker -- ignore it - case '\x1a': - NextCh(it8); - break; - // Eof stream markers + case '\x1a': case 0: case -1: it8->sy = SEOF; @@ -877,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++; @@ -901,29 +1032,13 @@ void InSymbol(cmsIT8* it8) // String. case '\'': case '\"': - idptr = it8->str; - sng = it8->ch; - k = 0; - NextCh(it8); - - while (k < (MAXSTR-1) && it8->ch != sng) { - - if (it8->ch == '\n'|| it8->ch == '\r') k = MAXSTR+1; - else { - *idptr++ = (char) it8->ch; - NextCh(it8); - k++; - } - } - - it8->sy = SSTRING; - *idptr = '\0'; - NextCh(it8); + InStringSymbol(it8); break; default: SynError(it8, "Unrecognized character: 0x%x", it8 ->ch); + it8->sy = SEOF; return; } @@ -938,24 +1053,33 @@ void InSymbol(cmsIT8* it8) if(it8 -> IncludeSP >= (MAXINCLUDE-1)) { SynError(it8, "Too many recursion levels"); + it8->sy = SEOF; return; } - InSymbol(it8); - if (!Check(it8, SSTRING, "Filename expected")) return; + InStringSymbol(it8); + if (!Check(it8, SSTRING, "Filename expected")) + { + it8->sy = SEOF; + return; + } FileNest = it8 -> FileStack[it8 -> IncludeSP + 1]; if(FileNest == NULL) { FileNest = it8 ->FileStack[it8 -> IncludeSP + 1] = (FILECTX*)AllocChunk(it8, sizeof(FILECTX)); - //if(FileNest == NULL) - // TODO: how to manage out-of-memory conditions? + if (FileNest == NULL) { + SynError(it8, "Out of memory"); + it8->sy = SEOF; + return; + } } - if (BuildAbsolutePath(it8->str, + if (BuildAbsolutePath(StringPtr(it8->str), it8->FileStack[it8->IncludeSP]->FileName, FileNest->FileName, cmsMAX_PATH-1) == FALSE) { SynError(it8, "File path too long"); + it8->sy = SEOF; return; } @@ -963,6 +1087,7 @@ void InSymbol(cmsIT8* it8) if (FileNest->Stream == NULL) { SynError(it8, "File %s not found", FileNest->FileName); + it8->sy = SEOF; return; } it8->IncludeSP++; @@ -1013,12 +1138,12 @@ cmsBool GetVal(cmsIT8* it8, char* Buffer, cmsUInt32Number max, const char* Error case SEOLN: // Empty value Buffer[0]=0; break; - case SIDENT: strncpy(Buffer, it8->id, max); + case SIDENT: strncpy(Buffer, StringPtr(it8->id), max); Buffer[max-1]=0; break; case SINUM: snprintf(Buffer, max, "%d", it8 -> inum); break; case SDNUM: snprintf(Buffer, max, it8->DoubleFormatter, it8 -> dnum); break; - case SSTRING: strncpy(Buffer, it8->str, max); + case SSTRING: strncpy(Buffer, StringPtr(it8->str), max); Buffer[max-1] = 0; break; @@ -1123,9 +1248,12 @@ void* AllocChunk(cmsIT8* it8, cmsUInt32Number size) it8 ->Allocator.BlockSize = size; it8 ->Allocator.Used = 0; - it8 ->Allocator.Block = (cmsUInt8Number*) AllocBigBlock(it8, it8 ->Allocator.BlockSize); + it8 ->Allocator.Block = (cmsUInt8Number*) AllocBigBlock(it8, it8 ->Allocator.BlockSize); } + if (it8->Allocator.Block == NULL) + return NULL; + ptr = it8 ->Allocator.Block + it8 ->Allocator.Used; it8 ->Allocator.Used += size; @@ -1143,7 +1271,7 @@ char *AllocString(cmsIT8* it8, const char* str) ptr = (char *) AllocChunk(it8, Size); - if (ptr) strncpy (ptr, str, Size-1); + if (ptr) memcpy(ptr, str, Size-1); return ptr; } @@ -1201,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 { @@ -1322,6 +1455,8 @@ cmsHANDLE CMSEXPORT cmsIT8Alloc(cmsContext ContextID) it8->MemoryBlock = NULL; it8->MemorySink = NULL; + it8->IsCUBE = FALSE; + it8 ->nTable = 0; it8->ContextID = ContextID; @@ -1342,6 +1477,9 @@ cmsHANDLE CMSEXPORT cmsIT8Alloc(cmsContext ContextID) it8->IncludeSP = 0; it8 -> lineno = 1; + it8->id = StringAlloc(it8, MAXSTR); + it8->str = StringAlloc(it8, MAXSTR); + strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT); cmsIT8SetSheetType((cmsHANDLE) it8, "CGATS.17"); @@ -1463,28 +1601,45 @@ const char* CMSEXPORT cmsIT8GetPropertyMulti(cmsHANDLE hIT8, const char* Key, co // ----------------------------------------------------------------- Datasets +// A safe atoi that returns 0 when NULL input is given +static +cmsInt32Number satoi(const char* b) +{ + int n; + + if (b == NULL) return 0; + + n = atoi(b); + if (n > 0x7fffffffL) return 0x7fffffffL; + if (n < -0x7ffffffeL) return -0x7ffffffeL; + + return (cmsInt32Number)n; +} + static -void AllocateDataFormat(cmsIT8* it8) +cmsBool AllocateDataFormat(cmsIT8* it8) { TABLE* t = GetTable(it8); - if (t -> DataFormat) return; // Already allocated + if (t -> DataFormat) return TRUE; // Already allocated - t -> nSamples = (int) cmsIT8GetPropertyDbl(it8, "NUMBER_OF_FIELDS"); + t -> nSamples = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS")); if (t -> nSamples <= 0) { SynError(it8, "AllocateDataFormat: Unknown NUMBER_OF_FIELDS"); - t -> nSamples = 10; + return FALSE; } t -> DataFormat = (char**) AllocChunk (it8, ((cmsUInt32Number) t->nSamples + 1) * sizeof(char *)); if (t->DataFormat == NULL) { SynError(it8, "AllocateDataFormat: Unable to allocate dataFormat array"); + return FALSE; } + return TRUE; } static @@ -1503,8 +1658,11 @@ cmsBool SetDataFormat(cmsIT8* it8, int n, const char *label) { TABLE* t = GetTable(it8); - if (!t->DataFormat) - AllocateDataFormat(it8); + if (!t->DataFormat) { + + if (!AllocateDataFormat(it8)) + return FALSE; + } if (n > t -> nSamples) { SynError(it8, "More than NUMBER_OF_FIELDS fields."); @@ -1513,6 +1671,7 @@ cmsBool SetDataFormat(cmsIT8* it8, int n, const char *label) if (t->DataFormat) { t->DataFormat[n] = AllocString(it8, label); + if (t->DataFormat[n] == NULL) return FALSE; } return TRUE; @@ -1525,20 +1684,31 @@ cmsBool CMSEXPORT cmsIT8SetDataFormat(cmsHANDLE h, int n, const char *Sample) return SetDataFormat(it8, n, Sample); } -// A safe atoi that returns 0 when NULL input is given +// Convert to binary static -cmsInt32Number satoi(const char* b) +const char* satob(const char* v) { - if (b == NULL) return 0; - return atoi(b); + cmsUInt32Number x; + static char buf[33]; + char *s = buf + 33; + + if (v == NULL) return "0"; + + x = atoi(v); + *--s = 0; + if (!x) *--s = '0'; + for (; x; x /= 2) *--s = '0' + x%2; + + return s; } + static -void AllocateDataSet(cmsIT8* it8) +cmsBool AllocateDataSet(cmsIT8* it8) { TABLE* t = GetTable(it8); - if (t -> Data) return; // Already allocated + if (t -> Data) return TRUE; // Already allocated t-> nSamples = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS")); t-> nPatches = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS")); @@ -1546,6 +1716,7 @@ void AllocateDataSet(cmsIT8* it8) if (t -> nSamples < 0 || t->nSamples > 0x7ffe || t->nPatches < 0 || t->nPatches > 0x7ffe) { SynError(it8, "AllocateDataSet: too much data"); + return FALSE; } else { // Some dumb analizers warns of possible overflow here, just take a look couple of lines above. @@ -1553,9 +1724,11 @@ void AllocateDataSet(cmsIT8* it8) if (t->Data == NULL) { SynError(it8, "AllocateDataSet: Unable to allocate data array"); + return FALSE; } } + return TRUE; } static @@ -1565,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; @@ -1577,8 +1750,9 @@ cmsBool SetData(cmsIT8* it8, int nSet, int nField, const char *Val) { TABLE* t = GetTable(it8); - if (!t->Data) - AllocateDataSet(it8); + if (!t->Data) { + if (!AllocateDataSet(it8)) return FALSE; + } if (!t->Data) return FALSE; @@ -1718,7 +1892,7 @@ void WriteHeader(cmsIT8* it8, SAVESTREAM* fp) break; case WRITE_BINARY: - Writef(fp, "\t0x%B", satoi(p ->Value)); + Writef(fp, "\t0b%s", satob(p ->Value)); break; case WRITE_PAIR: @@ -1749,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) { + + for (i = 0; i < nSamples; i++) { - WriteStr(fp, t->DataFormat[i]); - WriteStr(fp, ((i == (nSamples-1)) ? "\n" : "\t")); - } + WriteStr(fp, t->DataFormat[i]); + WriteStr(fp, ((i == (nSamples - 1)) ? "\n" : "\t")); + } + } WriteStr (fp, "END_DATA_FORMAT\n"); } @@ -1763,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"); } @@ -1816,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; + } @@ -1838,13 +2032,15 @@ cmsBool CMSEXPORT cmsIT8SaveToMem(cmsHANDLE hIT8, void *MemPtr, cmsUInt32Number* memset(&sd, 0, sizeof(sd)); sd.stream = NULL; - sd.Base = (cmsUInt8Number*) MemPtr; + sd.Base = (cmsUInt8Number*) MemPtr; sd.Ptr = sd.Base; sd.Used = 0; - if (sd.Base) - sd.Max = *BytesNeeded; // Write to memory? + if (sd.Base && (*BytesNeeded > 0)) { + + sd.Max = (*BytesNeeded) - 1; // Write to memory? + } else sd.Max = 0; // Just counting the needed bytes @@ -1888,7 +2084,7 @@ cmsBool DataFormatSection(cmsIT8* it8) return SynError(it8, "Sample type expected"); } - if (!SetDataFormat(it8, iField, it8->id)) return FALSE; + if (!SetDataFormat(it8, iField, StringPtr(it8->id))) return FALSE; iField++; InSymbol(it8); @@ -1921,8 +2117,9 @@ cmsBool DataSection (cmsIT8* it8) InSymbol(it8); // Eats "BEGIN_DATA" CheckEOLN(it8); - if (!t->Data) - AllocateDataSet(it8); + if (!t->Data) { + if (!AllocateDataSet(it8)) return FALSE; + } while (it8->sy != SEND_DATA && it8->sy != SEOF) { @@ -1934,11 +2131,28 @@ cmsBool DataSection (cmsIT8* it8) if (it8->sy != SEND_DATA && it8->sy != SEOF) { + switch (it8->sy) + { + + // To keep very long data + case SIDENT: + if (!SetData(it8, iSet, iField, StringPtr(it8->id))) + return FALSE; + break; + + case SSTRING: + if (!SetData(it8, iSet, iField, StringPtr(it8->str))) + return FALSE; + break; + + default: + if (!GetVal(it8, Buffer, 255, "Sample data expected")) return FALSE; if (!SetData(it8, iSet, iField, Buffer)) return FALSE; + } iField++; @@ -1994,7 +2208,7 @@ cmsBool HeaderSection(cmsIT8* it8) case SIDENT: - strncpy(VarName, it8->id, MAXID - 1); + strncpy(VarName, StringPtr(it8->id), MAXID - 1); VarName[MAXID - 1] = 0; if (!IsAvailableOnList(it8->ValidKeywords, VarName, NULL, &Key)) { @@ -2138,7 +2352,7 @@ cmsBool ParseIT8(cmsIT8* it8, cmsBool nosheet) // If a newline is found, then this is a type string if (it8 ->ch == '\n' || it8->ch == '\r') { - cmsIT8SetSheetType(it8, it8 ->id); + cmsIT8SetSheetType(it8, StringPtr(it8 ->id)); InSymbol(it8); } else @@ -2150,7 +2364,7 @@ cmsBool ParseIT8(cmsIT8* it8, cmsBool nosheet) else // Validate quoted strings if (it8 ->sy == SSTRING) { - cmsIT8SetSheetType(it8, it8 ->str); + cmsIT8SetSheetType(it8, StringPtr(it8 ->str)); InSymbol(it8); } } @@ -2181,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; + t->SampleID = 0; + it8->nTable = j; - for (idField = 0; idField < t -> nSamples; idField++) - { - if (t ->DataFormat == NULL){ - SynError(it8, "Undefined DATA_FORMAT"); - return; - } + for (idField = 0; idField < t->nSamples; idField++) + { + if (t->DataFormat == NULL) { + SynError(it8, "Undefined DATA_FORMAT"); + return; + } - Fld = t->DataFormat[idField]; - if (!Fld) continue; + Fld = t->DataFormat[idField]; + if (!Fld) continue; - if (cmsstrcasecmp(Fld, "SAMPLE_ID") == 0) { + if (cmsstrcasecmp(Fld, "SAMPLE_ID") == 0) { - t -> SampleID = idField; - } + t->SampleID = idField; + } - // "LABEL" is an extension. It keeps references to forward tables + // "LABEL" is an extension. It keeps references to forward tables - if ((cmsstrcasecmp(Fld, "LABEL") == 0) || Fld[0] == '$') { + if ((cmsstrcasecmp(Fld, "LABEL") == 0) || Fld[0] == '$') { - // Search for table references... - for (i = 0; i < t->nPatches; i++) { + // Search for table references... + for (i = 0; i < t->nPatches; i++) { - char* Label = GetData(it8, i, idField); + char* Label = GetData(it8, i, idField); - if (Label) { + if (Label) { - cmsUInt32Number k; + cmsUInt32Number k; - // This is the label, search for a table containing - // this property + // This is the label, search for a table containing + // this property - for (k = 0; k < it8->TablesCount; k++) { + for (k = 0; k < it8->TablesCount; k++) { - TABLE* Table = it8->Tab + k; - KEYVALUE* p; + TABLE* Table = it8->Tab + k; + KEYVALUE* p; - if (IsAvailableOnList(Table->HeaderList, Label, NULL, &p)) { + if (IsAvailableOnList(Table->HeaderList, Label, NULL, &p)) { - // Available, keep type and table - char Buffer[256]; + // Available, keep type and table + char Buffer[256]; - char* Type = p->Value; - int nTable = (int)k; + char* Type = p->Value; + int nTable = (int)k; - snprintf(Buffer, 255, "%s %d %s", Label, nTable, Type); + snprintf(Buffer, 255, "%s %d %s", Label, nTable, Type); - SetData(it8, i, idField, Buffer); + 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 @@ -2343,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); @@ -2355,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); @@ -2452,15 +2660,18 @@ cmsUInt32Number CMSEXPORT cmsIT8EnumProperties(cmsHANDLE hIT8, char ***PropertyN } - Props = (char **) AllocChunk(it8, sizeof(char *) * n); + 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; - } + // Pass#2 - Fill pointers + n = 0; + for (p = t->HeaderList; p != NULL; p = p->Next) { + Props[n++] = p->Keyword; + } + } *PropertyNames = Props; + return n; } @@ -2492,12 +2703,14 @@ cmsUInt32Number CMSEXPORT cmsIT8EnumPropertyMulti(cmsHANDLE hIT8, const char* cP Props = (const char **) AllocChunk(it8, sizeof(char *) * n); + if (Props != NULL) { - // Pass#2 - Fill pointers - n = 0; - for (tmp = p; tmp != NULL; tmp = tmp->NextSubkey) { - if(tmp->Subkey != NULL) - Props[n++] = p ->Subkey; + // Pass#2 - Fill pointers + n = 0; + for (tmp = p; tmp != NULL; tmp = tmp->NextSubkey) { + if (tmp->Subkey != NULL) + Props[n++] = p->Subkey; + } } *SubpropertyNames = Props; @@ -2673,8 +2886,12 @@ cmsBool CMSEXPORT cmsIT8SetData(cmsHANDLE hIT8, const char* cPatch, const char* if (t-> nPatches == 0) { - AllocateDataFormat(it8); - AllocateDataSet(it8); + if (!AllocateDataFormat(it8)) + return FALSE; + + if (!AllocateDataSet(it8)) + return FALSE; + CookPointers(it8); } @@ -2813,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); +} diff --git a/jdk/src/share/native/sun/java2d/cmm/lcms/cmscnvrt.c b/jdk/src/share/native/sun/java2d/cmm/lcms/cmscnvrt.c index 4bee13d6491..d18865b15b9 100644 --- a/jdk/src/share/native/sun/java2d/cmm/lcms/cmscnvrt.c +++ b/jdk/src/share/native/sun/java2d/cmm/lcms/cmscnvrt.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2020 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"), @@ -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; @@ -399,11 +399,11 @@ cmsBool ComputeConversion(cmsUInt32Number i, cmsCIEXYZ WhitePointIn, WhitePointOut; cmsMAT3 ChromaticAdaptationMatrixIn, ChromaticAdaptationMatrixOut; - _cmsReadMediaWhitePoint(&WhitePointIn, hProfiles[i-1]); - _cmsReadCHAD(&ChromaticAdaptationMatrixIn, hProfiles[i-1]); + if (!_cmsReadMediaWhitePoint(&WhitePointIn, hProfiles[i - 1])) return FALSE; + if (!_cmsReadCHAD(&ChromaticAdaptationMatrixIn, hProfiles[i - 1])) return FALSE; - _cmsReadMediaWhitePoint(&WhitePointOut, hProfiles[i]); - _cmsReadCHAD(&ChromaticAdaptationMatrixOut, hProfiles[i]); + if (!_cmsReadMediaWhitePoint(&WhitePointOut, hProfiles[i])) return FALSE; + if (!_cmsReadCHAD(&ChromaticAdaptationMatrixOut, hProfiles[i])) return FALSE; if (!ComputeAbsoluteIntent(AdaptationState, &WhitePointIn, &ChromaticAdaptationMatrixIn, @@ -415,7 +415,7 @@ cmsBool ComputeConversion(cmsUInt32Number i, if (BPC) { - cmsCIEXYZ BlackPointIn, BlackPointOut; + cmsCIEXYZ BlackPointIn = { 0, 0, 0}, BlackPointOut = { 0, 0, 0 }; cmsDetectBlackPoint(&BlackPointIn, hProfiles[i-1], Intent, 0); cmsDetectDestinationBlackPoint(&BlackPointOut, hProfiles[i], Intent, 0); @@ -659,7 +659,7 @@ cmsPipeline* DefaultICCintents(cmsContext ContextID, ColorSpaceOut == cmsSigRgbData || ColorSpaceOut == cmsSigCmykData) { - cmsStage* clip = _cmsStageClipNegatives(Result->ContextID, cmsChannelsOf(ColorSpaceOut)); + cmsStage* clip = _cmsStageClipNegatives(Result->ContextID, cmsChannelsOfColorSpace(ColorSpaceOut)); if (clip == NULL) goto Error; if (!cmsPipelineInsertStage(Result, cmsAT_END, clip)) @@ -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,8 +1190,7 @@ cmsUInt32Number CMSEXPORT cmsGetSupportedIntentsTHR(cmsContext ContextID, cmsUIn cmsIntentsList* pt; cmsUInt32Number nIntents; - - for (nIntents=0, pt = ctx->Intents; pt != NULL; pt = pt -> Next) + for (nIntents=0, pt = DefaultIntents; pt != NULL; pt = pt -> Next) { if (nIntents < nMax) { if (Codes != NULL) @@ -1191,7 +1203,7 @@ cmsUInt32Number CMSEXPORT cmsGetSupportedIntentsTHR(cmsContext ContextID, cmsUIn nIntents++; } - for (nIntents=0, pt = DefaultIntents; pt != NULL; pt = pt -> Next) + for (pt = ctx->Intents; pt != NULL; pt = pt -> Next) { if (nIntents < nMax) { if (Codes != NULL) @@ -1203,6 +1215,7 @@ cmsUInt32Number CMSEXPORT cmsGetSupportedIntentsTHR(cmsContext ContextID, cmsUIn nIntents++; } + return nIntents; } diff --git a/jdk/src/share/native/sun/java2d/cmm/lcms/cmserr.c b/jdk/src/share/native/sun/java2d/cmm/lcms/cmserr.c index 999bc493d21..9fb7db89c9a 100644 --- a/jdk/src/share/native/sun/java2d/cmm/lcms/cmserr.c +++ b/jdk/src/share/native/sun/java2d/cmm/lcms/cmserr.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2020 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"), @@ -101,14 +101,14 @@ 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)) // User may override this behaviour by using a memory plug-in, which basically replaces // the default memory management functions. In this case, no check is performed and it -// is up to the plug-in writter to keep in the safe side. There are only three functions +// is up to the plug-in writer to keep in the safe side. There are only three functions // required to be implemented: malloc, realloc and free, although the user may want to // replace the optional mallocZero, calloc and dup as well. @@ -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; @@ -337,7 +338,7 @@ void* CMSEXPORT _cmsDupMem(cmsContext ContextID, const void* Org, cmsUInt32Numbe // Sub allocation takes care of many pointers of small size. The memory allocated in // this way have be freed at once. Next function allocates a single chunk for linked list -// I prefer this method over realloc due to the big inpact on xput realloc may have if +// I prefer this method over realloc due to the big impact on xput realloc may have if // memory is being swapped to disk. This approach is safer (although that may not be true on all platforms) static _cmsSubAllocator_chunk* _cmsCreateSubAllocChunk(cmsContext ContextID, cmsUInt32Number Initial) @@ -642,7 +643,6 @@ cmsBool _cmsRegisterMutexPlugin(cmsContext ContextID, cmsPluginBase* Data) if (Plugin ->CreateMutexPtr == NULL || Plugin ->DestroyMutexPtr == NULL || Plugin ->LockMutexPtr == NULL || Plugin ->UnlockMutexPtr == NULL) return FALSE; - ctx->CreateMutexPtr = Plugin->CreateMutexPtr; ctx->DestroyMutexPtr = Plugin ->DestroyMutexPtr; ctx ->LockMutexPtr = Plugin ->LockMutexPtr; @@ -690,3 +690,47 @@ void CMSEXPORT _cmsUnlockMutex(cmsContext ContextID, void* mtx) ptr ->UnlockMutexPtr(ContextID, mtx); } } + +// The global Context0 storage for parallelization plug-in + _cmsParallelizationPluginChunkType _cmsParallelizationPluginChunk = { 0 }; + +// Allocate parallelization container. +void _cmsAllocParallelizationPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src) +{ + if (src != NULL) { + void* from = src->chunks[ParallelizationPlugin]; + ctx->chunks[ParallelizationPlugin] = _cmsSubAllocDup(ctx->MemPool, from, sizeof(_cmsParallelizationPluginChunkType)); + } + else { + _cmsParallelizationPluginChunkType ParallelizationPluginChunk = { 0 }; + ctx->chunks[ParallelizationPlugin] = _cmsSubAllocDup(ctx->MemPool, &ParallelizationPluginChunk, sizeof(_cmsParallelizationPluginChunkType)); + } +} + +// Register parallel processing +cmsBool _cmsRegisterParallelizationPlugin(cmsContext ContextID, cmsPluginBase* Data) +{ + cmsPluginParalellization* Plugin = (cmsPluginParalellization*)Data; + _cmsParallelizationPluginChunkType* ctx = (_cmsParallelizationPluginChunkType*)_cmsContextGetClientChunk(ContextID, ParallelizationPlugin); + + if (Data == NULL) { + + // No parallelization routines + ctx->MaxWorkers = 0; + ctx->WorkerFlags = 0; + ctx->SchedulerFn = NULL; + return TRUE; + } + + // callback is required + if (Plugin->SchedulerFn == NULL) return FALSE; + + ctx->MaxWorkers = Plugin->MaxWorkers; + ctx->WorkerFlags = Plugin->WorkerFlags; + ctx->SchedulerFn = Plugin->SchedulerFn; + + // All is ok + return TRUE; +} + diff --git a/jdk/src/share/native/sun/java2d/cmm/lcms/cmsgamma.c b/jdk/src/share/native/sun/java2d/cmm/lcms/cmsgamma.c index f266f2e05a6..8e489a43c55 100644 --- a/jdk/src/share/native/sun/java2d/cmm/lcms/cmsgamma.c +++ b/jdk/src/share/native/sun/java2d/cmm/lcms/cmsgamma.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2020 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"), @@ -236,7 +236,7 @@ _cmsParametricCurvesCollection *GetParametricCurveByType(cmsContext ContextID, i } // Low level allocate, which takes care of memory details. nEntries may be zero, and in this case -// no optimation curve is computed. nSegments may also be zero in the inverse case, where only the +// no optimization curve is computed. nSegments may also be zero in the inverse case, where only the // optimization curve is given. Both features simultaneously is an error static cmsToneCurve* AllocateToneCurveStruct(cmsContext ContextID, cmsUInt32Number nEntries, @@ -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); @@ -456,8 +460,8 @@ cmsFloat64Number DefaultEvalParametricFn(cmsInt32Number Type, const cmsFloat64Nu // IEC 61966-3 - // Y = (aX + b)^Gamma | X <= -b/a - // Y = c | else + // Y = (aX + b)^Gamma + c | X <= -b/a + // Y = c | else case 3: { if (fabs(Params[1]) < MATRIX_DET_TOLERANCE) @@ -491,7 +495,8 @@ cmsFloat64Number DefaultEvalParametricFn(cmsInt32Number Type, const cmsFloat64Nu // X=-b/a | (Y= disc) { + if (R >= disc) { + if (fabs(Params[0]) < MATRIX_DET_TOLERANCE || + fabs(Params[1]) < MATRIX_DET_TOLERANCE) + + Val = 0; + + else Val = (pow(R, 1.0 / Params[0]) - Params[2]) / Params[1]; - } - else { + } + else { + + if (fabs(Params[3]) < MATRIX_DET_TOLERANCE) + Val = 0; + else Val = R / Params[3]; - } } + } break; @@ -584,26 +592,29 @@ cmsFloat64Number DefaultEvalParametricFn(cmsInt32Number Type, const cmsFloat64Nu // X=(Y-f)/c | else case -5: { - if (fabs(Params[1]) < MATRIX_DET_TOLERANCE || - fabs(Params[3]) < MATRIX_DET_TOLERANCE) - { - Val = 0; - } - else - { - disc = Params[3] * Params[4] + Params[6]; - if (R >= disc) { + disc = Params[3] * Params[4] + Params[6]; + if (R >= disc) { + + e = R - Params[5]; + if (e < 0) + Val = 0; + else + { + if (fabs(Params[0]) < MATRIX_DET_TOLERANCE || + fabs(Params[1]) < MATRIX_DET_TOLERANCE) - e = R - Params[5]; - if (e < 0) Val = 0; else Val = (pow(e, 1.0 / Params[0]) - Params[2]) / Params[1]; } - else { + } + else { + if (fabs(Params[3]) < MATRIX_DET_TOLERANCE) + Val = 0; + else Val = (R - Params[6]) / Params[3]; - } } + } break; @@ -615,16 +626,23 @@ 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 case -6: { - if (fabs(Params[1]) < MATRIX_DET_TOLERANCE) + if (fabs(Params[0]) < MATRIX_DET_TOLERANCE || + fabs(Params[1]) < MATRIX_DET_TOLERANCE) { Val = 0; } @@ -844,6 +862,10 @@ cmsToneCurve* CMSEXPORT cmsBuildTabulatedToneCurveFloat(cmsContext ContextID, cm { cmsCurveSegment Seg[3]; + // Do some housekeeping + if (nEntries == 0 || values == NULL) + return NULL; + // A segmented tone curve should have function segments in the first and last positions // Initialize segmented curve part up to 0 to constant value = samples[0] Seg[0].x0 = MINUS_INF; @@ -1496,6 +1518,9 @@ cmsFloat64Number CMSEXPORT cmsEstimateGamma(const cmsToneCurve* t, cmsFloat64Num } } + // We need enough valid samples + if (n <= 1) return -1.0; + // Take a look on SD to see if gamma isn't exponential at all Std = sqrt((n * sum2 - sum * sum) / (n*(n-1))); @@ -1505,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; } + diff --git a/jdk/src/share/native/sun/java2d/cmm/lcms/cmsgmt.c b/jdk/src/share/native/sun/java2d/cmm/lcms/cmsgmt.c index 34c9a6c1bf6..e9ee73b52cd 100644 --- a/jdk/src/share/native/sun/java2d/cmm/lcms/cmsgmt.c +++ b/jdk/src/share/native/sun/java2d/cmm/lcms/cmsgmt.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2020 Marti Maria Saguer +// Copyright (c) 1998-2021 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"), @@ -228,15 +228,15 @@ typedef struct { cmsHTRANSFORM hInput; // From whatever input color space. 16 bits to DBL cmsHTRANSFORM hForward, hReverse; // Transforms going from Lab to colorant and back - cmsFloat64Number Thereshold; // The thereshold after which is considered out of gamut + cmsFloat64Number Threshold; // The threshold after which is considered out of gamut } GAMUTCHAIN; // This sampler does compute gamut boundaries by comparing original -// values with a transform going back and forth. Values above ERR_THERESHOLD +// values with a transform going back and forth. Values above ERR_THRESHOLD // of maximum are considered out of gamut. -#define ERR_THERESHOLD 5 +#define ERR_THRESHOLD 5 static @@ -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 @@ -275,17 +275,17 @@ int GamutSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Nu // if dE1 is small and dE2 is small, value is likely to be in gamut - if (dE1 < t->Thereshold && dE2 < t->Thereshold) + if (dE1 < t->Threshold && dE2 < t->Threshold) Out[0] = 0; else { // if dE1 is small and dE2 is big, undefined. Assume in gamut - if (dE1 < t->Thereshold && dE2 > t->Thereshold) + if (dE1 < t->Threshold && dE2 > t->Threshold) Out[0] = 0; else // dE1 is big and dE2 is small, clearly out of gamut - if (dE1 > t->Thereshold && dE2 < t->Thereshold) - Out[0] = (cmsUInt16Number) _cmsQuickFloor((dE1 - t->Thereshold) + .5); + if (dE1 > t->Threshold && dE2 < t->Threshold) + Out[0] = (cmsUInt16Number) _cmsQuickFloor((dE1 - t->Threshold) + .5); else { // dE1 is big and dE2 is also big, could be due to perceptual mapping @@ -295,8 +295,8 @@ int GamutSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Nu else ErrorRatio = dE1 / dE2; - if (ErrorRatio > t->Thereshold) - Out[0] = (cmsUInt16Number) _cmsQuickFloor((ErrorRatio - t->Thereshold) + .5); + if (ErrorRatio > t->Threshold) + Out[0] = (cmsUInt16Number) _cmsQuickFloor((ErrorRatio - t->Threshold) + .5); else Out[0] = 0; } @@ -326,7 +326,8 @@ cmsPipeline* _cmsCreateGamutCheckPipeline(cmsContext ContextID, cmsStage* CLUT; cmsUInt32Number dwFormat; GAMUTCHAIN Chain; - cmsUInt32Number nChannels, nGridpoints; + cmsUInt32Number nGridpoints; + cmsInt32Number nChannels; cmsColorSpaceSignature ColorSpace; cmsUInt32Number i; cmsHPROFILE ProfileList[256]; @@ -352,10 +353,10 @@ cmsPipeline* _cmsCreateGamutCheckPipeline(cmsContext ContextID, if (cmsIsMatrixShaper(hGamut)) { - Chain.Thereshold = 1.0; + Chain.Threshold = 1.0; } else { - Chain.Thereshold = ERR_THERESHOLD; + Chain.Threshold = ERR_THRESHOLD; } @@ -375,8 +376,7 @@ cmsPipeline* _cmsCreateGamutCheckPipeline(cmsContext ContextID, ColorSpace = cmsGetColorSpace(hGamut); - - nChannels = cmsChannelsOf(ColorSpace); + nChannels = cmsChannelsOfColorSpace(ColorSpace); nGridpoints = _cmsReasonableGridpointsByColorspace(ColorSpace, cmsFLAGS_HIGHRESPRECALC); dwFormat = (CHANNELS_SH(nChannels)|BYTES_SH(2)); @@ -501,6 +501,9 @@ cmsFloat64Number CMSEXPORT cmsDetectTAC(cmsHPROFILE hProfile) // Create a fake formatter for result dwFormatter = cmsFormatterForColorspaceOfProfile(hProfile, 4, TRUE); + // Unsupported color space? + if (dwFormatter == 0) return 0; + bp.nOutputChans = T_CHANNELS(dwFormatter); bp.MaxTAC = 0; // Initial TAC is 0 @@ -617,3 +620,69 @@ cmsBool CMSEXPORT cmsDesaturateLab(cmsCIELab* Lab, return TRUE; } + +// Detect whatever a given ICC profile works in linear (gamma 1.0) space +// 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 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) +{ + cmsContext ContextID; + cmsHPROFILE hXYZ; + cmsHTRANSFORM xform; + cmsToneCurve* Y_curve; + cmsUInt16Number rgb[256][3]; + cmsCIEXYZ XYZ[256]; + cmsFloat32Number Y_normalized[256]; + cmsFloat64Number gamma; + cmsProfileClassSignature cl; + int i; + + if (cmsGetColorSpace(hProfile) != cmsSigRgbData) + return -1; + + cl = cmsGetDeviceClass(hProfile); + if (cl != cmsSigInputClass && cl != cmsSigDisplayClass && + cl != cmsSigOutputClass && cl != cmsSigColorSpaceClass) + return -1; + + ContextID = cmsGetProfileContextID(hProfile); + hXYZ = cmsCreateXYZProfileTHR(ContextID); + if (hXYZ == NULL) + return -1; + xform = cmsCreateTransformTHR(ContextID, hProfile, TYPE_RGB_16, hXYZ, TYPE_XYZ_DBL, + INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_NOOPTIMIZE); + + if (xform == NULL) { // If not RGB or forward direction is not supported, regret with the previous error + + cmsCloseProfile(hXYZ); + return -1; + } + + for (i = 0; i < 256; i++) { + rgb[i][0] = rgb[i][1] = rgb[i][2] = FROM_8_TO_16(i); + } + + cmsDoTransform(xform, rgb, XYZ, 256); + + cmsDeleteTransform(xform); + cmsCloseProfile(hXYZ); + + for (i = 0; i < 256; i++) { + Y_normalized[i] = (cmsFloat32Number) XYZ[i].Y; + } + + Y_curve = cmsBuildTabulatedToneCurveFloat(ContextID, 256, Y_normalized); + if (Y_curve == NULL) + return -1; + + gamma = cmsEstimateGamma(Y_curve, threshold); + + cmsFreeToneCurve(Y_curve); + + return gamma; +} + diff --git a/jdk/src/share/native/sun/java2d/cmm/lcms/cmshalf.c b/jdk/src/share/native/sun/java2d/cmm/lcms/cmshalf.c index f2e28af400e..5babb063eb0 100644 --- a/jdk/src/share/native/sun/java2d/cmm/lcms/cmshalf.c +++ b/jdk/src/share/native/sun/java2d/cmm/lcms/cmshalf.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2020 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"), @@ -406,7 +406,7 @@ static const cmsUInt32Number Mantissa[2048] = { 0x387fc000, 0x387fe000 }; -static cmsUInt16Number Offset[64] = { +static const cmsUInt16Number Offset[64] = { 0x0000, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, diff --git a/jdk/src/share/native/sun/java2d/cmm/lcms/cmsintrp.c b/jdk/src/share/native/sun/java2d/cmm/lcms/cmsintrp.c index 8f2d0130e82..9837454df0b 100644 --- a/jdk/src/share/native/sun/java2d/cmm/lcms/cmsintrp.c +++ b/jdk/src/share/native/sun/java2d/cmm/lcms/cmsintrp.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2020 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"), @@ -229,8 +229,8 @@ void LinLerp1D(CMSREGISTER const cmsUInt16Number Value[], int val3; const cmsUInt16Number* LutTable = (cmsUInt16Number*) p ->Table; - // if last value... - if (Value[0] == 0xffff) { + // if last value or just one point + if (Value[0] == 0xffff || p->Domain[0] == 0) { Output[0] = LutTable[p -> Domain[0]]; } @@ -269,7 +269,7 @@ void LinLerp1Dfloat(const cmsFloat32Number Value[], val2 = fclamp(Value[0]); // if last value... - if (val2 == 1.0) { + if (val2 == 1.0 || p->Domain[0] == 0) { Output[0] = LutTable[p -> Domain[0]]; } else @@ -303,20 +303,34 @@ void Eval1Input(CMSREGISTER const cmsUInt16Number Input[], cmsUInt32Number OutChan; const cmsUInt16Number* LutTable = (cmsUInt16Number*) p16 -> Table; - v = Input[0] * p16 -> Domain[0]; - fk = _cmsToFixedDomain(v); - k0 = FIXED_TO_INT(fk); - rk = (cmsUInt16Number) FIXED_REST_TO_INT(fk); + // if last value... + if (Input[0] == 0xffff || p16->Domain[0] == 0) { + + cmsUInt32Number y0 = p16->Domain[0] * p16->opta[0]; + + for (OutChan = 0; OutChan < p16->nOutputs; OutChan++) { + Output[OutChan] = LutTable[y0 + OutChan]; + } + } + else + { + + v = Input[0] * p16->Domain[0]; + fk = _cmsToFixedDomain(v); + + k0 = FIXED_TO_INT(fk); + rk = (cmsUInt16Number)FIXED_REST_TO_INT(fk); - k1 = k0 + (Input[0] != 0xFFFFU ? 1 : 0); + k1 = k0 + (Input[0] != 0xFFFFU ? 1 : 0); - K0 = p16 -> opta[0] * k0; - K1 = p16 -> opta[0] * k1; + K0 = p16->opta[0] * k0; + K1 = p16->opta[0] * k1; - for (OutChan=0; OutChan < p16->nOutputs; OutChan++) { + for (OutChan = 0; OutChan < p16->nOutputs; OutChan++) { - Output[OutChan] = LinearInterp(rk, LutTable[K0+OutChan], LutTable[K1+OutChan]); + Output[OutChan] = LinearInterp(rk, LutTable[K0 + OutChan], LutTable[K1 + OutChan]); + } } } @@ -337,12 +351,12 @@ void Eval1InputFloat(const cmsFloat32Number Value[], val2 = fclamp(Value[0]); // if last value... - if (val2 == 1.0) { + if (val2 == 1.0 || p->Domain[0] == 0) { - y0 = LutTable[p->Domain[0]]; + cmsUInt32Number start = p->Domain[0] * p->opta[0]; for (OutChan = 0; OutChan < p->nOutputs; OutChan++) { - Output[OutChan] = y0; + Output[OutChan] = LutTable[start + OutChan]; } } else diff --git a/jdk/src/share/native/sun/java2d/cmm/lcms/cmsio0.c b/jdk/src/share/native/sun/java2d/cmm/lcms/cmsio0.c index 70be6a6ad14..1b32f9f4159 100644 --- a/jdk/src/share/native/sun/java2d/cmm/lcms/cmsio0.c +++ b/jdk/src/share/native/sun/java2d/cmm/lcms/cmsio0.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2020 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"), @@ -404,6 +404,7 @@ cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromFile(cmsContext ContextID, const cha cmsIOHANDLER* iohandler = NULL; FILE* fm = NULL; cmsInt32Number fileLen; + char mode[4] = { 0,0,0,0 }; _cmsAssert(FileName != NULL); _cmsAssert(AccessMode != NULL); @@ -411,16 +412,49 @@ cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromFile(cmsContext ContextID, const cha iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER)); if (iohandler == NULL) return NULL; - switch (*AccessMode) { + // Validate access mode + while (*AccessMode) { + + switch (*AccessMode) + { + case 'r': + case 'w': + + if (mode[0] == 0) { + mode[0] = *AccessMode; + mode[1] = 'b'; + } + else { + _cmsFree(ContextID, iohandler); + cmsSignalError(ContextID, cmsERROR_FILE, "Access mode already specified '%c'", *AccessMode); + return NULL; + } + break; + + // Close on exec. Not all runtime supports that. Up to the caller to decide. + case 'e': + mode[2] = 'e'; + break; + + default: + _cmsFree(ContextID, iohandler); + cmsSignalError(ContextID, cmsERROR_FILE, "Wrong access mode '%c'", *AccessMode); + return NULL; + } + + AccessMode++; + } + + switch (mode[0]) { case 'r': - fm = fopen(FileName, "rb"); + fm = fopen(FileName, mode); if (fm == NULL) { _cmsFree(ContextID, iohandler); cmsSignalError(ContextID, cmsERROR_FILE, "File '%s' not found", FileName); return NULL; } - fileLen = cmsfilelength(fm); + fileLen = (cmsInt32Number)cmsfilelength(fm); if (fileLen < 0) { fclose(fm); @@ -428,12 +462,11 @@ cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromFile(cmsContext ContextID, const cha cmsSignalError(ContextID, cmsERROR_FILE, "Cannot get size of file '%s'", FileName); return NULL; } - iohandler -> ReportedSize = (cmsUInt32Number) fileLen; break; case 'w': - fm = fopen(FileName, "wb"); + fm = fopen(FileName, mode); if (fm == NULL) { _cmsFree(ContextID, iohandler); cmsSignalError(ContextID, cmsERROR_FILE, "Couldn't create '%s'", FileName); @@ -443,8 +476,7 @@ cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromFile(cmsContext ContextID, const cha break; default: - _cmsFree(ContextID, iohandler); - cmsSignalError(ContextID, cmsERROR_FILE, "Unknown access mode '%c'", *AccessMode); + _cmsFree(ContextID, iohandler); // Would never reach return NULL; } @@ -471,7 +503,7 @@ cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromStream(cmsContext ContextID, FILE* S cmsIOHANDLER* iohandler = NULL; cmsInt32Number fileSize; - fileSize = cmsfilelength(Stream); + fileSize = (cmsInt32Number)cmsfilelength(Stream); if (fileSize < 0) { cmsSignalError(ContextID, cmsERROR_FILE, "Cannot get size of stream"); @@ -508,16 +540,15 @@ cmsBool CMSEXPORT cmsCloseIOhandler(cmsIOHANDLER* io) cmsIOHANDLER* CMSEXPORT cmsGetProfileIOhandler(cmsHPROFILE hProfile) { - _cmsICCPROFILE* Icc = (_cmsICCPROFILE*)hProfile; + _cmsICCPROFILE* Icc = (_cmsICCPROFILE*)hProfile; - if (Icc == NULL) return NULL; - return Icc->IOhandler; + if (Icc == NULL) return NULL; + return Icc->IOhandler; } // Creates an empty structure holding all required parameters cmsHPROFILE CMSEXPORT cmsCreateProfilePlaceholder(cmsContext ContextID) { - time_t now = time(NULL); _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) _cmsMallocZero(ContextID, sizeof(_cmsICCPROFILE)); if (Icc == NULL) return NULL; @@ -529,14 +560,36 @@ 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; + // Set creation date/time - memmove(&Icc ->Created, gmtime(&now), sizeof(Icc ->Created)); + if (!_cmsGetTime(&Icc->Created)) + goto Error; // Create a mutex if the user provided proper plugin. NULL otherwise Icc ->UsrMutex = _cmsCreateMutex(ContextID); // Return the handle return (cmsHPROFILE) Icc; + +Error: + _cmsFree(ContextID, Icc); + return NULL; } cmsContext CMSEXPORT cmsGetProfileContextID(cmsHPROFILE hProfile) @@ -682,6 +735,27 @@ cmsBool CMSEXPORT cmsIsTag(cmsHPROFILE hProfile, cmsTagSignature sig) return _cmsSearchTag(Icc, sig, FALSE) >= 0; } + + +// Checks for link compatibility +static +cmsBool CompatibleTypes(const cmsTagDescriptor* desc1, const cmsTagDescriptor* desc2) +{ + cmsUInt32Number i; + + if (desc1 == NULL || desc2 == NULL) return FALSE; + + if (desc1->nSupportedTypes != desc2->nSupportedTypes) return FALSE; + if (desc1->ElemCount != desc2->ElemCount) return FALSE; + + for (i = 0; i < desc1->nSupportedTypes; i++) + { + if (desc1->SupportedTypes[i] != desc2->SupportedTypes[i]) return FALSE; + } + + return TRUE; +} + // Enforces that the profile version is per. spec. // Operates on the big endian bytes from the profile. // Called before converting to platform endianness. @@ -707,6 +781,29 @@ cmsUInt32Number _validatedVersion(cmsUInt32Number DWord) return DWord; } +// Check device class +static +cmsBool validDeviceClass(cmsProfileClassSignature cl) +{ + if ((int)cl == 0) return TRUE; // We allow zero because older lcms versions defaulted to that. + + switch (cl) + { + case cmsSigInputClass: + case cmsSigDisplayClass: + case cmsSigOutputClass: + case cmsSigLinkClass: + case cmsSigAbstractClass: + case cmsSigColorSpaceClass: + case cmsSigNamedColorClass: + return TRUE; + + default: + return FALSE; + } + +} + // Read profile header and validate it cmsBool _cmsReadHeader(_cmsICCPROFILE* Icc) { @@ -730,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); @@ -743,6 +842,16 @@ cmsBool _cmsReadHeader(_cmsICCPROFILE* Icc) _cmsAdjustEndianess64(&Icc -> attributes, &Header.attributes); Icc -> Version = _cmsAdjustEndianess32(_validatedVersion(Header.version)); + if (Icc->Version > 0x5000000) { + cmsSignalError(Icc->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported profile version '0x%x'", Icc->Version); + return FALSE; + } + + if (!validDeviceClass(Icc->DeviceClass)) { + cmsSignalError(Icc->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported device class '0x%x'", Icc->DeviceClass); + return FALSE; + } + // Get size as reported in header HeaderSize = _cmsAdjustEndianess32(Header.size); @@ -776,6 +885,7 @@ cmsBool _cmsReadHeader(_cmsICCPROFILE* Icc) if (!_cmsReadUInt32Number(io, &Tag.size)) return FALSE; // Perform some sanity check. Offset + size should fall inside file. + if (Tag.size == 0 || Tag.offset == 0) continue; if (Tag.offset + Tag.size > HeaderSize || Tag.offset + Tag.size < Tag.offset) continue; @@ -790,7 +900,12 @@ cmsBool _cmsReadHeader(_cmsICCPROFILE* Icc) if ((Icc ->TagOffsets[j] == Tag.offset) && (Icc ->TagSizes[j] == Tag.size)) { - Icc ->TagLinked[Icc ->TagCount] = Icc ->TagNames[j]; + // Check types. + if (CompatibleTypes(_cmsGetTagDescriptor(Icc->ContextID, Icc->TagNames[j]), + _cmsGetTagDescriptor(Icc->ContextID, Tag.sig))) { + + Icc->TagLinked[Icc->TagCount] = Icc->TagNames[j]; + } } } @@ -798,6 +913,19 @@ cmsBool _cmsReadHeader(_cmsICCPROFILE* Icc) Icc ->TagCount++; } + + for (i = 0; i < Icc->TagCount; i++) { + for (j = 0; j < Icc->TagCount; j++) { + + // Tags cannot be duplicate + if ((i != j) && (Icc->TagNames[i] == Icc->TagNames[j])) { + cmsSignalError(Icc->ContextID, cmsERROR_RANGE, "Duplicate tag found"); + return FALSE; + } + + } + } + return TRUE; } @@ -810,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); @@ -822,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); @@ -842,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)); @@ -1459,7 +1582,25 @@ cmsBool CMSEXPORT cmsSaveProfileToMem(cmsHPROFILE hProfile, void *MemPtr, cmsUIn return rc; } +// Free one tag contents +static +void freeOneTag(_cmsICCPROFILE* Icc, cmsUInt32Number i) +{ + if (Icc->TagPtrs[i]) { + + cmsTagTypeHandler* TypeHandler = Icc->TagTypeHandlers[i]; + if (TypeHandler != NULL) { + cmsTagTypeHandler LocalTypeHandler = *TypeHandler; + + LocalTypeHandler.ContextID = Icc->ContextID; + LocalTypeHandler.ICCVersion = Icc->Version; + LocalTypeHandler.FreePtr(&LocalTypeHandler, Icc->TagPtrs[i]); + } + else + _cmsFree(Icc->ContextID, Icc->TagPtrs[i]); + } +} // Closes a profile freeing any involved resources cmsBool CMSEXPORT cmsCloseProfile(cmsHPROFILE hProfile) @@ -1479,20 +1620,7 @@ cmsBool CMSEXPORT cmsCloseProfile(cmsHPROFILE hProfile) for (i=0; i < Icc -> TagCount; i++) { - if (Icc -> TagPtrs[i]) { - - cmsTagTypeHandler* TypeHandler = Icc ->TagTypeHandlers[i]; - - if (TypeHandler != NULL) { - cmsTagTypeHandler LocalTypeHandler = *TypeHandler; - - LocalTypeHandler.ContextID = Icc ->ContextID; // As an additional parameters - LocalTypeHandler.ICCVersion = Icc ->Version; - LocalTypeHandler.FreePtr(&LocalTypeHandler, Icc -> TagPtrs[i]); - } - else - _cmsFree(Icc ->ContextID, Icc ->TagPtrs[i]); - } + freeOneTag(Icc, i); } if (Icc ->IOhandler != NULL) { @@ -1544,8 +1672,12 @@ void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig) if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return NULL; n = _cmsSearchTag(Icc, sig, TRUE); - if (n < 0) goto Error; // Not found, return NULL - + if (n < 0) + { + // Not found, return NULL + _cmsUnlockMutex(Icc->ContextID, Icc->UsrMutex); + return NULL; + } // If the element is already in memory, return the pointer if (Icc -> TagPtrs[n]) { @@ -1573,6 +1705,12 @@ void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig) if (TagSize < 8) goto Error; + if (io == NULL) { // This is a built-in profile that has been manipulated, abort early + + cmsSignalError(Icc->ContextID, cmsERROR_CORRUPTION_DETECTED, "Corrupted built-in profile."); + goto Error; + } + // Seek to its location if (!io -> Seek(io, Offset)) goto Error; @@ -1640,8 +1778,12 @@ void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig) return Icc -> TagPtrs[n]; - // Return error and unlock tha data + // Return error and unlock the data Error: + + freeOneTag(Icc, n); + Icc->TagPtrs[n] = NULL; + _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); return NULL; } @@ -1780,11 +1922,9 @@ cmsBool CMSEXPORT cmsWriteTag(cmsHPROFILE hProfile, cmsTagSignature sig, const v } -// Read and write raw data. The only way those function would work and keep consistence with normal read and write -// is to do an additional step of serialization. That means, readRaw would issue a normal read and then convert the obtained -// data to raw bytes by using the "write" serialization logic. And vice-versa. I know this may end in situations where -// raw data written does not exactly correspond with the raw data proposed to cmsWriteRaw data, but this approach allows -// to write a tag as raw data and the read it as handled. +// Read and write raw data. Read/Write Raw/cooked pairs try to maintain consistency within the pair. Some sequences +// raw/cooked would work, but at a cost. Data "cooked" may be converted to "raw" by using the "write" serialization logic. +// In general it is better to avoid mixing pairs. cmsUInt32Number CMSEXPORT cmsReadRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, void* data, cmsUInt32Number BufferSize) { @@ -1798,20 +1938,25 @@ cmsUInt32Number CMSEXPORT cmsReadRawTag(cmsHPROFILE hProfile, cmsTagSignature si cmsUInt32Number rc; cmsUInt32Number Offset, TagSize; + // Sanity check + if (data != NULL && BufferSize == 0) return 0; + if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0; // Search for given tag in ICC profile directory + i = _cmsSearchTag(Icc, sig, TRUE); if (i < 0) goto Error; // Not found, // It is already read? if (Icc -> TagPtrs[i] == NULL) { - // No yet, get original position + // Not yet, get original position Offset = Icc ->TagOffsets[i]; TagSize = Icc ->TagSizes[i]; // read the data directly, don't keep copy + if (data != NULL) { if (BufferSize < TagSize) @@ -1828,8 +1973,9 @@ cmsUInt32Number CMSEXPORT cmsReadRawTag(cmsHPROFILE hProfile, cmsTagSignature si return Icc ->TagSizes[i]; } - // The data has been already read, or written. But wait!, maybe the user chose to save as + // The data has been already read, or written. But wait!, maybe the user choose to save as // raw data. In this case, return the raw data directly + if (Icc ->TagSaveAsRaw[i]) { if (data != NULL) { @@ -1849,7 +1995,7 @@ cmsUInt32Number CMSEXPORT cmsReadRawTag(cmsHPROFILE hProfile, cmsTagSignature si } // Already read, or previously set by cmsWriteTag(). We need to serialize that - // data to raw in order to maintain consistency. + // data to raw to get something that makes sense _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); Object = cmsReadTag(hProfile, sig); diff --git a/jdk/src/share/native/sun/java2d/cmm/lcms/cmsio1.c b/jdk/src/share/native/sun/java2d/cmm/lcms/cmsio1.c index 23f32108a81..e42d4d38987 100644 --- a/jdk/src/share/native/sun/java2d/cmm/lcms/cmsio1.c +++ b/jdk/src/share/native/sun/java2d/cmm/lcms/cmsio1.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2020 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"), @@ -289,7 +289,7 @@ cmsPipeline* BuildRGBInputMatrixShaper(cmsHPROFILE hProfile) -// Read the DToAX tag, adjusting the encoding of Lab or XYZ if neded +// Read the DToAX tag, adjusting the encoding of Lab or XYZ if needed static cmsPipeline* _cmsReadFloatInputTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat) { @@ -351,10 +351,8 @@ cmsPipeline* CMSEXPORT _cmsReadInputLUT(cmsHPROFILE hProfile, cmsUInt32Number In if (nc == NULL) return NULL; Lut = cmsPipelineAlloc(ContextID, 0, 0); - if (Lut == NULL) { - cmsFreeNamedColorList(nc); + if (Lut == NULL) return NULL; - } if (!cmsPipelineInsertStage(Lut, cmsAT_BEGIN, _cmsStageAllocNamedColor(nc, TRUE)) || !cmsPipelineInsertStage(Lut, cmsAT_END, _cmsStageAllocLabV2ToV4(ContextID))) { @@ -565,7 +563,7 @@ void ChangeInterpolationToTrilinear(cmsPipeline* Lut) } -// Read the DToAX tag, adjusting the encoding of Lab or XYZ if neded +// Read the DToAX tag, adjusting the encoding of Lab or XYZ if needed static cmsPipeline* _cmsReadFloatOutputTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat) { @@ -609,7 +607,7 @@ cmsPipeline* _cmsReadFloatOutputTag(cmsHPROFILE hProfile, cmsTagSignature tagFlo 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; @@ -690,7 +688,7 @@ cmsPipeline* CMSEXPORT _cmsReadOutputLUT(cmsHPROFILE hProfile, cmsUInt32Number I // --------------------------------------------------------------------------------------------------------------- -// Read the AToD0 tag, adjusting the encoding of Lab or XYZ if neded +// Read the AToD0 tag, adjusting the encoding of Lab or XYZ if needed static cmsPipeline* _cmsReadFloatDevicelinkTag(cmsHPROFILE hProfile, cmsTagSignature tagFloat) { @@ -769,7 +767,6 @@ cmsPipeline* CMSEXPORT _cmsReadDevicelinkLUT(cmsHPROFILE hProfile, cmsUInt32Numb return Lut; Error: cmsPipelineFree(Lut); - cmsFreeNamedColorList(nc); return NULL; } @@ -883,6 +880,10 @@ cmsBool CMSEXPORT cmsIsCLUT(cmsHPROFILE hProfile, cmsUInt32Number Intent, cmsUI return FALSE; } + // Extended intents are not strictly CLUT-based + if (Intent > INTENT_ABSOLUTE_COLORIMETRIC) + return FALSE; + return cmsIsTag(hProfile, TagTable[Intent]); } @@ -1055,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); +} diff --git a/jdk/src/share/native/sun/java2d/cmm/lcms/cmslut.c b/jdk/src/share/native/sun/java2d/cmm/lcms/cmslut.c index 6eb803fe558..b544c948625 100644 --- a/jdk/src/share/native/sun/java2d/cmm/lcms/cmslut.c +++ b/jdk/src/share/native/sun/java2d/cmm/lcms/cmslut.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2020 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"), @@ -496,7 +496,7 @@ cmsUInt32Number CubeSize(const cmsUInt32Number Dims[], cmsUInt32Number b) for (rv = 1; b > 0; b--) { dim = Dims[b-1]; - if (dim == 0) return 0; // Error + if (dim <= 1) return 0; // Error rv *= dim; @@ -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; @@ -1255,6 +1264,11 @@ void* CMSEXPORT cmsStageData(const cmsStage* mpe) return mpe -> Data; } +cmsContext CMSEXPORT cmsGetStageContextID(const cmsStage* mpe) +{ + return mpe -> ContextID; +} + cmsStage* CMSEXPORT cmsStageNext(const cmsStage* mpe) { return mpe -> Next; diff --git a/jdk/src/share/native/sun/java2d/cmm/lcms/cmsmd5.c b/jdk/src/share/native/sun/java2d/cmm/lcms/cmsmd5.c index 2c37b34ea15..01aa44de85a 100644 --- a/jdk/src/share/native/sun/java2d/cmm/lcms/cmsmd5.c +++ b/jdk/src/share/native/sun/java2d/cmm/lcms/cmsmd5.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2020 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"), diff --git a/jdk/src/share/native/sun/java2d/cmm/lcms/cmsmtrx.c b/jdk/src/share/native/sun/java2d/cmm/lcms/cmsmtrx.c index 6bf18e0b798..599c290bd70 100644 --- a/jdk/src/share/native/sun/java2d/cmm/lcms/cmsmtrx.c +++ b/jdk/src/share/native/sun/java2d/cmm/lcms/cmsmtrx.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2020 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"), diff --git a/jdk/src/share/native/sun/java2d/cmm/lcms/cmsnamed.c b/jdk/src/share/native/sun/java2d/cmm/lcms/cmsnamed.c index 95a3b5ef8e1..d3cd97d4aea 100644 --- a/jdk/src/share/native/sun/java2d/cmm/lcms/cmsnamed.c +++ b/jdk/src/share/native/sun/java2d/cmm/lcms/cmsnamed.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2020 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"), @@ -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 explicitely sets an empty string, we force a \0 +// 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,22 +375,56 @@ 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); + _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 static cmsUInt32Number mywcslen(const wchar_t *s) @@ -399,7 +561,9 @@ const wchar_t* _cmsMLUgetWide(const cmsMLU* mlu, if (len != NULL) *len = v ->Len; - return(wchar_t*) ((cmsUInt8Number*) mlu ->MemPool + v ->StrW); + if (v->StrW + v->Len > mlu->PoolSize) return NULL; + + return (wchar_t*) ((cmsUInt8Number*) mlu ->MemPool + v ->StrW); } @@ -437,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" @@ -448,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], @@ -468,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; @@ -571,8 +777,12 @@ cmsBool GrowNamedColorList(cmsNAMEDCOLORLIST* v) // Allocate a list for n elements cmsNAMEDCOLORLIST* CMSEXPORT cmsAllocNamedColorList(cmsContext ContextID, cmsUInt32Number n, cmsUInt32Number ColorantCount, const char* Prefix, const char* Suffix) { - cmsNAMEDCOLORLIST* v = (cmsNAMEDCOLORLIST*) _cmsMallocZero(ContextID, sizeof(cmsNAMEDCOLORLIST)); + cmsNAMEDCOLORLIST* v; + if (ColorantCount > cmsMAXCHANNELS) + return NULL; + + v = (cmsNAMEDCOLORLIST*)_cmsMallocZero(ContextID, sizeof(cmsNAMEDCOLORLIST)); if (v == NULL) return NULL; v ->List = NULL; @@ -670,7 +880,7 @@ cmsUInt32Number CMSEXPORT cmsNamedColorCount(const cmsNAMEDCOLORLIST* NamedColor return NamedColorList ->nColors; } -// Info aboout a given color +// Info about a given color cmsBool CMSEXPORT cmsNamedColorInfo(const cmsNAMEDCOLORLIST* NamedColorList, cmsUInt32Number nColor, char* Name, char* Prefix, @@ -786,7 +996,13 @@ cmsStage* CMSEXPORT _cmsStageAllocNamedColor(cmsNAMEDCOLORLIST* NamedColorList, cmsNAMEDCOLORLIST* CMSEXPORT cmsGetNamedColorList(cmsHTRANSFORM xform) { _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform; - cmsStage* mpe = v ->Lut->Elements; + cmsStage* mpe; + + if (v == NULL) return NULL; + if (v->Lut == NULL) return NULL; + + mpe = v->Lut->Elements; + if (mpe == NULL) return NULL; if (mpe ->Type != cmsSigNamedColorElemType) return NULL; return (cmsNAMEDCOLORLIST*) mpe ->Data; @@ -831,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); } diff --git a/jdk/src/share/native/sun/java2d/cmm/lcms/cmsopt.c b/jdk/src/share/native/sun/java2d/cmm/lcms/cmsopt.c index e83b2e0be6d..421a4f4a701 100644 --- a/jdk/src/share/native/sun/java2d/cmm/lcms/cmsopt.c +++ b/jdk/src/share/native/sun/java2d/cmm/lcms/cmsopt.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2020 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"), @@ -212,6 +212,7 @@ cmsBool isFloatMatrixIdentity(const cmsMAT3* a) return TRUE; } + // if two adjacent matrices are found, multiply them. static cmsBool _MultiplyMatrix(cmsPipeline* Lut) @@ -676,7 +677,6 @@ cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3 { cmsPipeline* Src = NULL; cmsPipeline* Dest = NULL; - cmsStage* mpe; cmsStage* CLUT; cmsStage *KeepPreLin = NULL, *KeepPostLin = NULL; cmsUInt32Number nGridPoints; @@ -698,7 +698,7 @@ cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3 if (ColorSpace == (cmsColorSpaceSignature)0 || OutputColorSpace == (cmsColorSpaceSignature)0) return FALSE; - nGridPoints = _cmsReasonableGridpointsByColorspace(ColorSpace, *dwFlags); + nGridPoints = _cmsReasonableGridpointsByColorspace(ColorSpace, *dwFlags); // For empty LUTs, 2 points are enough if (cmsPipelineStageCount(*Lut) == 0) @@ -706,13 +706,6 @@ cmsBool OptimizeByResampling(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3 Src = *Lut; - // Named color pipelines cannot be optimized either - for (mpe = cmsPipelineGetPtrToFirstStage(Src); - mpe != NULL; - mpe = cmsStageNext(mpe)) { - if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE; - } - // Allocate an empty LUT Dest = cmsPipelineAlloc(Src ->ContextID, Src ->InputChannels, Src ->OutputChannels); if (!Dest) return FALSE; @@ -1080,7 +1073,6 @@ cmsBool OptimizeByComputingLinearization(cmsPipeline** Lut, cmsUInt32Number Inte cmsStage* OptimizedCLUTmpe; cmsColorSpaceSignature ColorSpace, OutputColorSpace; cmsStage* OptimizedPrelinMpe; - cmsStage* mpe; cmsToneCurve** OptimizedPrelinCurves; _cmsStageCLutData* OptimizedPrelinCLUT; @@ -1102,13 +1094,6 @@ cmsBool OptimizeByComputingLinearization(cmsPipeline** Lut, cmsUInt32Number Inte OriginalLut = *Lut; - // Named color pipelines cannot be optimized either - for (mpe = cmsPipelineGetPtrToFirstStage(OriginalLut); - mpe != NULL; - mpe = cmsStageNext(mpe)) { - if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE; - } - ColorSpace = _cmsICCcolorSpace((int) T_COLORSPACE(*InputFormat)); OutputColorSpace = _cmsICCcolorSpace((int) T_COLORSPACE(*OutputFormat)); @@ -1158,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++) { @@ -1562,10 +1550,10 @@ void* DupMatShaper(cmsContext ContextID, const void* Data) } -// A fast matrix-shaper evaluator for 8 bits. This is a bit ticky since I'm using 1.14 signed fixed point +// A fast matrix-shaper evaluator for 8 bits. This is a bit tricky since I'm using 1.14 signed fixed point // to accomplish some performance. Actually it takes 256x3 16 bits tables and 16385 x 3 tables of 8 bits, // in total about 50K, and the performance boost is huge! -static +static CMS_NO_SANITIZE void MatShaperEval16(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUInt16Number Out[], CMSREGISTER const void* D) @@ -1740,6 +1728,10 @@ cmsBool OptimizeMatrixShaper(cmsPipeline** Lut, cmsUInt32Number Intent, cmsUInt3 _cmsStageMatrixData* Data1 = (_cmsStageMatrixData*)cmsStageData(Matrix1); _cmsStageMatrixData* Data2 = (_cmsStageMatrixData*)cmsStageData(Matrix2); + // Only RGB to RGB + if (Matrix1->InputChannels != 3 || Matrix1->OutputChannels != 3 || + Matrix2->InputChannels != 3 || Matrix2->OutputChannels != 3) return FALSE; + // Input offset should be zero if (Data1->Offset != NULL) return FALSE; @@ -1765,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)); @@ -1809,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 @@ -1947,6 +1941,7 @@ cmsBool CMSEXPORT _cmsOptimizePipeline(cmsContext ContextID, _cmsOptimizationPluginChunkType* ctx = ( _cmsOptimizationPluginChunkType*) _cmsContextGetClientChunk(ContextID, OptimizationPlugin); _cmsOptimizationCollection* Opts; cmsBool AnySuccess = FALSE; + cmsStage* mpe; // A CLUT is being asked, so force this specific optimization if (*dwFlags & cmsFLAGS_FORCE_CLUT) { @@ -1961,6 +1956,13 @@ cmsBool CMSEXPORT _cmsOptimizePipeline(cmsContext ContextID, return TRUE; } + // Named color pipelines cannot be optimized + for (mpe = cmsPipelineGetPtrToFirstStage(*PtrLut); + mpe != NULL; + mpe = cmsStageNext(mpe)) { + if (cmsStageType(mpe) == cmsSigNamedColorElemType) return FALSE; + } + // Try to get rid of identities and trivial conversions. AnySuccess = PreOptimize(*PtrLut); diff --git a/jdk/src/share/native/sun/java2d/cmm/lcms/cmspack.c b/jdk/src/share/native/sun/java2d/cmm/lcms/cmspack.c index 0ec3edab5d5..fc875995a80 100644 --- a/jdk/src/share/native/sun/java2d/cmm/lcms/cmspack.c +++ b/jdk/src/share/native/sun/java2d/cmm/lcms/cmspack.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2020 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"), @@ -108,6 +108,7 @@ typedef struct { #define ANYSWAP DOSWAP_SH(1) #define ANYSWAPFIRST SWAPFIRST_SH(1) #define ANYFLAVOR FLAVOR_SH(1) +#define ANYPREMUL PREMUL_SH(1) // Suppress waning about info never being used @@ -131,20 +132,40 @@ cmsUInt8Number* UnrollChunkyBytes(CMSREGISTER _cmsTRANSFORM* info, cmsUInt32Number Reverse = T_FLAVOR(info ->InputFormat); cmsUInt32Number SwapFirst = T_SWAPFIRST(info -> InputFormat); cmsUInt32Number Extra = T_EXTRA(info -> InputFormat); + cmsUInt32Number Premul = T_PREMUL(info->InputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; - cmsUInt16Number v; + cmsUInt32Number v; cmsUInt32Number i; + cmsUInt32Number alpha_factor = 1; if (ExtraFirst) { + + if (Premul && Extra) + alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(accum[0])); + accum += Extra; } + else + { + if (Premul && Extra) + alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(accum[nChan])); + } for (i=0; i < nChan; i++) { + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; v = FROM_8_TO_16(*accum); v = Reverse ? REVERSE_FLAVOR_16(v) : v; - wIn[index] = v; + + if (Premul && alpha_factor > 0) + { + v = ((cmsUInt32Number)((cmsUInt32Number)v << 16) / alpha_factor); + if (v > 0xffff) v = 0xffff; + } + + wIn[index] = (cmsUInt16Number) v; accum++; } @@ -166,6 +187,7 @@ cmsUInt8Number* UnrollChunkyBytes(CMSREGISTER _cmsTRANSFORM* info, } + // Extra channels are just ignored because come in the next planes static cmsUInt8Number* UnrollPlanarBytes(CMSREGISTER _cmsTRANSFORM* info, @@ -178,24 +200,47 @@ cmsUInt8Number* UnrollPlanarBytes(CMSREGISTER _cmsTRANSFORM* info, cmsUInt32Number SwapFirst = T_SWAPFIRST(info ->InputFormat); cmsUInt32Number Reverse = T_FLAVOR(info ->InputFormat); cmsUInt32Number i; + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsUInt32Number Extra = T_EXTRA(info->InputFormat); + cmsUInt32Number Premul = T_PREMUL(info->InputFormat); cmsUInt8Number* Init = accum; + cmsUInt32Number alpha_factor = 1; - if (DoSwap ^ SwapFirst) { - accum += T_EXTRA(info -> InputFormat) * Stride; + if (ExtraFirst) { + + if (Premul && Extra) + alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(accum[0])); + + + accum += Extra * Stride; + } + else + { + if (Premul && Extra) + alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(accum[(nChan) * Stride])); } for (i=0; i < nChan; i++) { cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; - cmsUInt16Number v = FROM_8_TO_16(*accum); + cmsUInt32Number v = FROM_8_TO_16(*accum); - wIn[index] = Reverse ? REVERSE_FLAVOR_16(v) : v; + v = Reverse ? REVERSE_FLAVOR_16(v) : v; + + if (Premul && alpha_factor > 0) + { + v = ((cmsUInt32Number)((cmsUInt32Number)v << 16) / alpha_factor); + if (v > 0xffff) v = 0xffff; + } + + wIn[index] = (cmsUInt16Number) v; accum += Stride; } return (Init + 1); } + // Special cases, provided for performance static cmsUInt8Number* Unroll4Bytes(CMSREGISTER _cmsTRANSFORM* info, @@ -546,6 +591,58 @@ cmsUInt8Number* UnrollAnyWords(CMSREGISTER _cmsTRANSFORM* info, cmsUNUSED_PARAMETER(Stride); } + +static +cmsUInt8Number* UnrollAnyWordsPremul(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + cmsUInt32Number nChan = T_CHANNELS(info -> InputFormat); + cmsUInt32Number SwapEndian = T_ENDIAN16(info -> InputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info ->InputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info ->InputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info -> InputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsUInt32Number i; + + cmsUInt16Number alpha = (ExtraFirst ? accum[0] : accum[nChan - 1]); + cmsUInt32Number alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(alpha)); + + if (ExtraFirst) { + accum += sizeof(cmsUInt16Number); + } + + for (i=0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + cmsUInt32Number v = *(cmsUInt16Number*) accum; + + if (SwapEndian) + v = CHANGE_ENDIAN(v); + + if (alpha_factor > 0) { + + v = (v << 16) / alpha_factor; + if (v > 0xffff) v = 0xffff; + } + + wIn[index] = (cmsUInt16Number) (Reverse ? REVERSE_FLAVOR_16(v) : v); + + accum += sizeof(cmsUInt16Number); + } + + if (!ExtraFirst) { + accum += sizeof(cmsUInt16Number); + } + + return accum; + + cmsUNUSED_PARAMETER(Stride); +} + + + static cmsUInt8Number* UnrollPlanarWords(CMSREGISTER _cmsTRANSFORM* info, CMSREGISTER cmsUInt16Number wIn[], @@ -579,6 +676,49 @@ cmsUInt8Number* UnrollPlanarWords(CMSREGISTER _cmsTRANSFORM* info, return (Init + sizeof(cmsUInt16Number)); } +static +cmsUInt8Number* UnrollPlanarWordsPremul(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + cmsUInt32Number nChan = T_CHANNELS(info -> InputFormat); + cmsUInt32Number DoSwap= T_DOSWAP(info ->InputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info->InputFormat); + cmsUInt32Number Reverse= T_FLAVOR(info ->InputFormat); + cmsUInt32Number SwapEndian = T_ENDIAN16(info -> InputFormat); + cmsUInt32Number i; + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsUInt8Number* Init = accum; + + cmsUInt16Number alpha = (ExtraFirst ? accum[0] : accum[(nChan - 1) * Stride]); + cmsUInt32Number alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(alpha)); + + if (ExtraFirst) { + accum += Stride; + } + + for (i=0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + cmsUInt32Number v = (cmsUInt32Number) *(cmsUInt16Number*) accum; + + if (SwapEndian) + v = CHANGE_ENDIAN(v); + + if (alpha_factor > 0) { + + v = (v << 16) / alpha_factor; + if (v > 0xffff) v = 0xffff; + } + + wIn[index] = (cmsUInt16Number) (Reverse ? REVERSE_FLAVOR_16(v) : v); + + accum += Stride; + } + + return (Init + sizeof(cmsUInt16Number)); +} static cmsUInt8Number* Unroll4Words(CMSREGISTER _cmsTRANSFORM* info, @@ -1116,6 +1256,110 @@ cmsUInt8Number* UnrollDouble1Chan(CMSREGISTER _cmsTRANSFORM* info, //------------------------------------------------------------------------------------------------------------------- +// For anything going from cmsUInt8Number +static +cmsUInt8Number* Unroll8ToFloat(_cmsTRANSFORM* info, + cmsFloat32Number wIn[], + cmsUInt8Number* accum, + cmsUInt32Number Stride) +{ + + cmsUInt32Number nChan = T_CHANNELS(info->InputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info->InputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info->InputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info->InputFormat); + cmsUInt32Number Extra = T_EXTRA(info->InputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsUInt32Number Planar = T_PLANAR(info->InputFormat); + cmsFloat32Number v; + cmsUInt32Number i, start = 0; + + Stride /= PixelSize(info->InputFormat); + + if (ExtraFirst) + start = Extra; + + for (i = 0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + if (Planar) + v = (cmsFloat32Number) ((cmsUInt8Number *)accum)[(i + start) * Stride]; + else + v = (cmsFloat32Number) ((cmsUInt8Number *)accum)[i + start]; + + v /= 255.0F; + + wIn[index] = Reverse ? 1 - v : v; + } + + + if (Extra == 0 && SwapFirst) { + cmsFloat32Number tmp = wIn[0]; + + memmove(&wIn[0], &wIn[1], (nChan - 1) * sizeof(cmsFloat32Number)); + wIn[nChan - 1] = tmp; + } + + if (T_PLANAR(info->InputFormat)) + return accum + sizeof(cmsUInt8Number); + else + return accum + (nChan + Extra) * sizeof(cmsUInt8Number); +} + + +// For anything going from cmsUInt16Number +static +cmsUInt8Number* Unroll16ToFloat(_cmsTRANSFORM* info, + cmsFloat32Number wIn[], + cmsUInt8Number* accum, + cmsUInt32Number Stride) +{ + + cmsUInt32Number nChan = T_CHANNELS(info->InputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info->InputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info->InputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info->InputFormat); + cmsUInt32Number Extra = T_EXTRA(info->InputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsUInt32Number Planar = T_PLANAR(info->InputFormat); + cmsFloat32Number v; + cmsUInt32Number i, start = 0; + + Stride /= PixelSize(info->InputFormat); + + if (ExtraFirst) + start = Extra; + + for (i = 0; i < nChan; i++) { + + cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; + + if (Planar) + v = (cmsFloat32Number)((cmsUInt16Number*)accum)[(i + start) * Stride]; + else + v = (cmsFloat32Number)((cmsUInt16Number*)accum)[i + start]; + + v /= 65535.0F; + + wIn[index] = Reverse ? 1 - v : v; + } + + + if (Extra == 0 && SwapFirst) { + cmsFloat32Number tmp = wIn[0]; + + memmove(&wIn[0], &wIn[1], (nChan - 1) * sizeof(cmsFloat32Number)); + wIn[nChan - 1] = tmp; + } + + if (T_PLANAR(info->InputFormat)) + return accum + sizeof(cmsUInt16Number); + else + return accum + (nChan + Extra) * sizeof(cmsUInt16Number); +} + + // For anything going from cmsFloat32Number static cmsUInt8Number* UnrollFloatsToFloat(_cmsTRANSFORM* info, @@ -1124,19 +1368,30 @@ cmsUInt8Number* UnrollFloatsToFloat(_cmsTRANSFORM* info, cmsUInt32Number Stride) { - cmsUInt32Number nChan = T_CHANNELS(info -> InputFormat); - cmsUInt32Number DoSwap = T_DOSWAP(info ->InputFormat); - cmsUInt32Number Reverse = T_FLAVOR(info ->InputFormat); - cmsUInt32Number SwapFirst = T_SWAPFIRST(info -> InputFormat); - cmsUInt32Number Extra = T_EXTRA(info -> InputFormat); + cmsUInt32Number nChan = T_CHANNELS(info->InputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info->InputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info->InputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info->InputFormat); + cmsUInt32Number Extra = T_EXTRA(info->InputFormat); cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; - cmsUInt32Number Planar = T_PLANAR(info -> InputFormat); + cmsUInt32Number Planar = T_PLANAR(info->InputFormat); + cmsUInt32Number Premul = T_PREMUL(info->InputFormat); cmsFloat32Number v; cmsUInt32Number i, start = 0; - cmsFloat32Number maximum = IsInkSpace(info ->InputFormat) ? 100.0F : 1.0F; + cmsFloat32Number maximum = IsInkSpace(info->InputFormat) ? 100.0F : 1.0F; + cmsFloat32Number alpha_factor = 1.0f; + cmsFloat32Number* ptr = (cmsFloat32Number*)accum; Stride /= PixelSize(info->InputFormat); + if (Premul && Extra) + { + if (Planar) + alpha_factor = (ExtraFirst ? ptr[0] : ptr[nChan * Stride]) / maximum; + else + alpha_factor = (ExtraFirst ? ptr[0] : ptr[nChan]) / maximum; + } + if (ExtraFirst) start = Extra; @@ -1145,9 +1400,12 @@ cmsUInt8Number* UnrollFloatsToFloat(_cmsTRANSFORM* info, cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; if (Planar) - v = (cmsFloat32Number) ((cmsFloat32Number*) accum)[(i + start) * Stride]; + v = ptr[(i + start) * Stride]; else - v = (cmsFloat32Number) ((cmsFloat32Number*) accum)[i + start]; + v = ptr[i + start]; + + if (Premul && alpha_factor > 0) + v /= alpha_factor; v /= maximum; @@ -1177,19 +1435,30 @@ cmsUInt8Number* UnrollDoublesToFloat(_cmsTRANSFORM* info, cmsUInt32Number Stride) { - cmsUInt32Number nChan = T_CHANNELS(info -> InputFormat); - cmsUInt32Number DoSwap = T_DOSWAP(info ->InputFormat); - cmsUInt32Number Reverse = T_FLAVOR(info ->InputFormat); - cmsUInt32Number SwapFirst = T_SWAPFIRST(info -> InputFormat); - cmsUInt32Number Extra = T_EXTRA(info -> InputFormat); + cmsUInt32Number nChan = T_CHANNELS(info->InputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info->InputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info->InputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info->InputFormat); + cmsUInt32Number Extra = T_EXTRA(info->InputFormat); cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; - cmsUInt32Number Planar = T_PLANAR(info -> InputFormat); + cmsUInt32Number Planar = T_PLANAR(info->InputFormat); + cmsUInt32Number Premul = T_PREMUL(info->InputFormat); cmsFloat64Number v; cmsUInt32Number i, start = 0; cmsFloat64Number maximum = IsInkSpace(info ->InputFormat) ? 100.0 : 1.0; + cmsFloat64Number alpha_factor = 1.0; + cmsFloat64Number* ptr = (cmsFloat64Number*)accum; Stride /= PixelSize(info->InputFormat); + if (Premul && Extra) + { + if (Planar) + alpha_factor = (ExtraFirst ? ptr[0] : ptr[(nChan) * Stride]) / maximum; + else + alpha_factor = (ExtraFirst ? ptr[0] : ptr[nChan]) / maximum; + } + if (ExtraFirst) start = Extra; @@ -1202,6 +1471,10 @@ cmsUInt8Number* UnrollDoublesToFloat(_cmsTRANSFORM* info, else v = (cmsFloat64Number) ((cmsFloat64Number*) accum)[i + start]; + + if (Premul && alpha_factor > 0) + v /= alpha_factor; + v /= maximum; wIn[index] = (cmsFloat32Number) (Reverse ? 1.0 - v : v); @@ -1283,8 +1556,6 @@ cmsUInt8Number* UnrollLabFloatToFloat(_cmsTRANSFORM* info, } } - - // 1.15 fixed point, that means maximum value is MAX_ENCODEABLE_XYZ (0xFFFF) static cmsUInt8Number* UnrollXYZDoubleToFloat(_cmsTRANSFORM* info, @@ -1345,44 +1616,132 @@ cmsUInt8Number* UnrollXYZFloatToFloat(_cmsTRANSFORM* info, } +cmsINLINE void lab4toFloat(cmsFloat32Number wIn[], cmsUInt16Number lab4[3]) +{ + cmsFloat32Number L = (cmsFloat32Number) lab4[0] / 655.35F; + cmsFloat32Number a = ((cmsFloat32Number) lab4[1] / 257.0F) - 128.0F; + cmsFloat32Number b = ((cmsFloat32Number) lab4[2] / 257.0F) - 128.0F; + + wIn[0] = (L / 100.0F); // from 0..100 to 0..1 + wIn[1] = ((a + 128.0F) / 255.0F); // form -128..+127 to 0..1 + wIn[2] = ((b + 128.0F) / 255.0F); + +} + +static +cmsUInt8Number* UnrollLabV2_8ToFloat(_cmsTRANSFORM* info, + cmsFloat32Number wIn[], + cmsUInt8Number* accum, + cmsUInt32Number Stride) +{ + cmsUInt16Number lab4[3]; + + lab4[0] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++; // L + lab4[1] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++; // a + lab4[2] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++; // b + + lab4toFloat(wIn, lab4); + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* UnrollALabV2_8ToFloat(_cmsTRANSFORM* info, + cmsFloat32Number wIn[], + cmsUInt8Number* accum, + cmsUInt32Number Stride) +{ + cmsUInt16Number lab4[3]; + + accum++; // A + lab4[0] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++; // L + lab4[1] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++; // a + lab4[2] = FomLabV2ToLabV4(FROM_8_TO_16(*accum)); accum++; // b + + lab4toFloat(wIn, lab4); + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* UnrollLabV2_16ToFloat(_cmsTRANSFORM* info, + cmsFloat32Number wIn[], + cmsUInt8Number* accum, + cmsUInt32Number Stride) +{ + cmsUInt16Number lab4[3]; + + lab4[0] = FomLabV2ToLabV4(*(cmsUInt16Number*) accum); accum += 2; // L + lab4[1] = FomLabV2ToLabV4(*(cmsUInt16Number*) accum); accum += 2; // a + lab4[2] = FomLabV2ToLabV4(*(cmsUInt16Number*) accum); accum += 2; // b + + lab4toFloat(wIn, lab4); + + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(Stride); +} + // Packing routines ----------------------------------------------------------------------------------------------------------- // Generic chunky for byte - static -cmsUInt8Number* PackAnyBytes(CMSREGISTER _cmsTRANSFORM* info, - CMSREGISTER cmsUInt16Number wOut[], - CMSREGISTER cmsUInt8Number* output, - CMSREGISTER cmsUInt32Number Stride) +cmsUInt8Number* PackChunkyBytes(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER 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 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 Premul = T_PREMUL(info->OutputFormat); cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; cmsUInt8Number* swap1; - cmsUInt8Number v = 0; + cmsUInt16Number v = 0; cmsUInt32Number i; + cmsUInt32Number alpha_factor = 0; swap1 = output; if (ExtraFirst) { + + if (Premul && Extra) + alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(output[0])); + output += Extra; } + else + { + if (Premul && Extra) + alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(output[nChan])); + } for (i=0; i < nChan; i++) { cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; - v = FROM_16_TO_8(wOut[index]); + v = wOut[index]; if (Reverse) - v = REVERSE_FLAVOR_8(v); + v = REVERSE_FLAVOR_16(v); - *output++ = v; + if (Premul) + { + v = (cmsUInt16Number)((cmsUInt32Number)((cmsUInt32Number)v * alpha_factor + 0x8000) >> 16); + } + + *output++ = FROM_16_TO_8(v); } if (!ExtraFirst) { @@ -1392,39 +1751,47 @@ cmsUInt8Number* PackAnyBytes(CMSREGISTER _cmsTRANSFORM* info, if (Extra == 0 && SwapFirst) { memmove(swap1 + 1, swap1, nChan-1); - *swap1 = v; + *swap1 = FROM_16_TO_8(v); } - return output; cmsUNUSED_PARAMETER(Stride); } - - static -cmsUInt8Number* PackAnyWords(CMSREGISTER _cmsTRANSFORM* info, - CMSREGISTER cmsUInt16Number wOut[], - CMSREGISTER cmsUInt8Number* output, - CMSREGISTER cmsUInt32Number Stride) +cmsUInt8Number* PackChunkyWords(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) { - cmsUInt32Number nChan = T_CHANNELS(info -> OutputFormat); - cmsUInt32Number SwapEndian = T_ENDIAN16(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 nChan = T_CHANNELS(info->OutputFormat); + cmsUInt32Number SwapEndian = T_ENDIAN16(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 Premul = T_PREMUL(info->OutputFormat); cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; cmsUInt16Number* swap1; cmsUInt16Number v = 0; cmsUInt32Number i; + cmsUInt32Number alpha_factor = 0; swap1 = (cmsUInt16Number*) output; if (ExtraFirst) { + + if (Premul && Extra) + alpha_factor = _cmsToFixedDomain(*(cmsUInt16Number*) output); + output += Extra * sizeof(cmsUInt16Number); } + else + { + if (Premul && Extra) + alpha_factor = _cmsToFixedDomain(((cmsUInt16Number*) output)[nChan]); + } for (i=0; i < nChan; i++) { @@ -1438,6 +1805,11 @@ cmsUInt8Number* PackAnyWords(CMSREGISTER _cmsTRANSFORM* info, if (Reverse) v = REVERSE_FLAVOR_16(v); + if (Premul) + { + v = (cmsUInt16Number)((cmsUInt32Number)((cmsUInt32Number)v * alpha_factor + 0x8000) >> 16); + } + *(cmsUInt16Number*) output = v; output += sizeof(cmsUInt16Number); @@ -1453,38 +1825,60 @@ cmsUInt8Number* PackAnyWords(CMSREGISTER _cmsTRANSFORM* info, *swap1 = v; } - return output; cmsUNUSED_PARAMETER(Stride); } + static cmsUInt8Number* PackPlanarBytes(CMSREGISTER _cmsTRANSFORM* info, CMSREGISTER cmsUInt16Number wOut[], CMSREGISTER cmsUInt8Number* output, CMSREGISTER cmsUInt32Number Stride) { - cmsUInt32Number nChan = T_CHANNELS(info -> OutputFormat); - cmsUInt32Number DoSwap = T_DOSWAP(info ->OutputFormat); - cmsUInt32Number SwapFirst = T_SWAPFIRST(info ->OutputFormat); - cmsUInt32Number Reverse = T_FLAVOR(info ->OutputFormat); + cmsUInt32Number nChan = T_CHANNELS(info->OutputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info->OutputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info->OutputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info->OutputFormat); + cmsUInt32Number Extra = T_EXTRA(info->OutputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsUInt32Number Premul = T_PREMUL(info->OutputFormat); cmsUInt32Number i; cmsUInt8Number* Init = output; + cmsUInt32Number alpha_factor = 0; + + + if (ExtraFirst) { + if (Premul && Extra) + alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(output[0])); - if (DoSwap ^ SwapFirst) { - output += T_EXTRA(info -> OutputFormat) * Stride; + output += Extra * Stride; + } + else + { + if (Premul && Extra) + alpha_factor = _cmsToFixedDomain(FROM_8_TO_16(output[nChan * Stride])); } for (i=0; i < nChan; i++) { cmsUInt32Number index = DoSwap ? (nChan - i - 1) : i; - cmsUInt8Number v = FROM_16_TO_8(wOut[index]); + cmsUInt16Number v = wOut[index]; + + if (Reverse) + v = REVERSE_FLAVOR_16(v); + + if (Premul) + { + v = (cmsUInt16Number)((cmsUInt32Number)((cmsUInt32Number)v * alpha_factor + 0x8000) >> 16); + } + + *(cmsUInt8Number*)output = FROM_16_TO_8(v); - *(cmsUInt8Number*) output = (cmsUInt8Number) (Reverse ? REVERSE_FLAVOR_8(v) : v); output += Stride; } @@ -1500,16 +1894,30 @@ cmsUInt8Number* PackPlanarWords(CMSREGISTER _cmsTRANSFORM* info, CMSREGISTER cmsUInt8Number* output, CMSREGISTER cmsUInt32Number Stride) { - cmsUInt32Number nChan = T_CHANNELS(info -> OutputFormat); - cmsUInt32Number DoSwap = T_DOSWAP(info ->OutputFormat); - cmsUInt32Number Reverse = T_FLAVOR(info ->OutputFormat); - cmsUInt32Number SwapEndian = T_ENDIAN16(info -> OutputFormat); + cmsUInt32Number nChan = T_CHANNELS(info->OutputFormat); + cmsUInt32Number DoSwap = T_DOSWAP(info->OutputFormat); + cmsUInt32Number SwapFirst = T_SWAPFIRST(info->OutputFormat); + cmsUInt32Number Reverse = T_FLAVOR(info->OutputFormat); + cmsUInt32Number Extra = T_EXTRA(info->OutputFormat); + cmsUInt32Number ExtraFirst = DoSwap ^ SwapFirst; + cmsUInt32Number Premul = T_PREMUL(info->OutputFormat); + cmsUInt32Number SwapEndian = T_ENDIAN16(info->OutputFormat); cmsUInt32Number i; cmsUInt8Number* Init = output; cmsUInt16Number v; + cmsUInt32Number alpha_factor = 0; - if (DoSwap) { - output += T_EXTRA(info -> OutputFormat) * Stride; + if (ExtraFirst) { + + if (Premul && Extra) + alpha_factor = _cmsToFixedDomain(((cmsUInt16Number*) output)[0]); + + output += Extra * Stride; + } + else + { + if (Premul && Extra) + alpha_factor = _cmsToFixedDomain(((cmsUInt16Number*)output)[nChan * Stride]); } for (i=0; i < nChan; i++) { @@ -1524,6 +1932,11 @@ cmsUInt8Number* PackPlanarWords(CMSREGISTER _cmsTRANSFORM* info, if (Reverse) v = REVERSE_FLAVOR_16(v); + if (Premul) + { + v = (cmsUInt16Number)((cmsUInt32Number)((cmsUInt32Number)v * alpha_factor + 0x8000) >> 16); + } + *(cmsUInt16Number*) output = v; output += Stride; } @@ -2567,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[], @@ -2670,10 +3185,6 @@ cmsUInt8Number* PackDoublesFromFloat(_cmsTRANSFORM* info, } - - - - static cmsUInt8Number* PackLabFloatFromFloat(_cmsTRANSFORM* Info, cmsFloat32Number wOut[], @@ -2734,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, @@ -3058,10 +3640,10 @@ static const cmsFormatters16 InputFormatters16[] = { { CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1), ANYSPACE, Unroll4BytesSwap}, { CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Unroll4BytesSwapSwapFirst}, - { BYTES_SH(1)|PLANAR_SH(1), ANYFLAVOR|ANYSWAPFIRST| + { BYTES_SH(1)|PLANAR_SH(1), ANYFLAVOR|ANYSWAPFIRST|ANYPREMUL| ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, UnrollPlanarBytes}, - { BYTES_SH(1), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP| + { BYTES_SH(1), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYPREMUL| ANYEXTRA|ANYCHANNELS|ANYSPACE, UnrollChunkyBytes}, { CHANNELS_SH(1)|BYTES_SH(2), ANYSPACE, Unroll1Word}, @@ -3083,6 +3665,10 @@ static const cmsFormatters16 InputFormatters16[] = { { BYTES_SH(2)|PLANAR_SH(1), ANYFLAVOR|ANYSWAP|ANYENDIAN|ANYEXTRA|ANYCHANNELS|ANYSPACE, UnrollPlanarWords}, { BYTES_SH(2), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYENDIAN|ANYEXTRA|ANYCHANNELS|ANYSPACE, UnrollAnyWords}, + + { BYTES_SH(2)|PLANAR_SH(1), ANYFLAVOR|ANYSWAP|ANYENDIAN|ANYEXTRA|ANYCHANNELS|ANYSPACE|PREMUL_SH(1), UnrollPlanarWordsPremul}, + { BYTES_SH(2), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYENDIAN|ANYEXTRA|ANYCHANNELS|ANYSPACE|PREMUL_SH(1), UnrollAnyWordsPremul} + }; @@ -3098,13 +3684,23 @@ static const cmsFormattersFloat InputFormattersFloat[] = { { TYPE_XYZ_FLT, ANYPLANAR|ANYEXTRA, UnrollXYZFloatToFloat}, { FLOAT_SH(1)|BYTES_SH(4), ANYPLANAR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA| - ANYCHANNELS|ANYSPACE, UnrollFloatsToFloat}, + ANYPREMUL|ANYCHANNELS|ANYSPACE, UnrollFloatsToFloat}, { FLOAT_SH(1)|BYTES_SH(0), ANYPLANAR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA| - ANYCHANNELS|ANYSPACE, UnrollDoublesToFloat}, + ANYCHANNELS|ANYSPACE|ANYPREMUL, UnrollDoublesToFloat}, + + { TYPE_LabV2_8, 0, UnrollLabV2_8ToFloat }, + { TYPE_ALabV2_8, 0, UnrollALabV2_8ToFloat }, + { TYPE_LabV2_16, 0, UnrollLabV2_16ToFloat }, + + { BYTES_SH(1), ANYPLANAR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA| + ANYCHANNELS|ANYSPACE, Unroll8ToFloat}, + + { BYTES_SH(2), ANYPLANAR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA| + ANYCHANNELS|ANYSPACE, Unroll16ToFloat}, #ifndef CMS_NO_HALF_SUPPORT { FLOAT_SH(1)|BYTES_SH(2), ANYPLANAR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA| - ANYCHANNELS|ANYSPACE, UnrollHalfToFloat}, + ANYCHANNELS|ANYSPACE, UnrollHalfToFloat}, #endif }; @@ -3198,16 +3794,20 @@ static const cmsFormatters16 OutputFormatters16[] = { ANYSPACE, Pack3BytesAndSkip1SwapSwapFirst}, { CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|EXTRA_SH(1), ANYSPACE, Pack3BytesAndSkip1Swap}, { CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1), ANYSPACE, Pack3BytesSwap}, - { CHANNELS_SH(6)|BYTES_SH(1), ANYSPACE, Pack6Bytes}, - { CHANNELS_SH(6)|BYTES_SH(1)|DOSWAP_SH(1), ANYSPACE, Pack6BytesSwap}, { CHANNELS_SH(4)|BYTES_SH(1), ANYSPACE, Pack4Bytes}, { CHANNELS_SH(4)|BYTES_SH(1)|FLAVOR_SH(1), ANYSPACE, Pack4BytesReverse}, { CHANNELS_SH(4)|BYTES_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Pack4BytesSwapFirst}, { CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1), ANYSPACE, Pack4BytesSwap}, { CHANNELS_SH(4)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1), ANYSPACE, Pack4BytesSwapSwapFirst}, + { CHANNELS_SH(6)|BYTES_SH(1), ANYSPACE, Pack6Bytes}, + { CHANNELS_SH(6)|BYTES_SH(1)|DOSWAP_SH(1), ANYSPACE, Pack6BytesSwap}, + + { BYTES_SH(1), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS| + ANYSPACE|ANYPREMUL, PackChunkyBytes}, + + { BYTES_SH(1)|PLANAR_SH(1), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA| + ANYCHANNELS|ANYSPACE|ANYPREMUL, PackPlanarBytes}, - { BYTES_SH(1), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackAnyBytes}, - { BYTES_SH(1)|PLANAR_SH(1), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackPlanarBytes}, { CHANNELS_SH(1)|BYTES_SH(2), ANYSPACE, Pack1Word}, { CHANNELS_SH(1)|BYTES_SH(2)|EXTRA_SH(1), ANYSPACE, Pack1WordSkip1}, @@ -3232,8 +3832,10 @@ static const cmsFormatters16 OutputFormatters16[] = { { CHANNELS_SH(6)|BYTES_SH(2), ANYSPACE, Pack6Words}, { CHANNELS_SH(6)|BYTES_SH(2)|DOSWAP_SH(1), ANYSPACE, Pack6WordsSwap}, - { BYTES_SH(2)|PLANAR_SH(1), ANYFLAVOR|ANYENDIAN|ANYSWAP|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackPlanarWords}, - { BYTES_SH(2), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYENDIAN|ANYEXTRA|ANYCHANNELS|ANYSPACE, PackAnyWords} + { BYTES_SH(2), ANYFLAVOR|ANYSWAPFIRST|ANYSWAP|ANYENDIAN| + ANYEXTRA|ANYCHANNELS|ANYSPACE|ANYPREMUL, PackChunkyWords}, + { BYTES_SH(2)|PLANAR_SH(1), ANYFLAVOR|ANYENDIAN|ANYSWAP|ANYEXTRA| + ANYCHANNELS|ANYSPACE|ANYPREMUL, PackPlanarWords} }; @@ -3247,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 }, @@ -3405,6 +4017,11 @@ cmsFormatter CMSEXPORT _cmsGetFormatter(cmsContext ContextID, _cmsFormattersPluginChunkType* ctx = ( _cmsFormattersPluginChunkType*) _cmsContextGetClientChunk(ContextID, FormattersPlugin); cmsFormattersFactoryList* f; + if (T_CHANNELS(Type) == 0) { + static const cmsFormatter nullFormatter = { 0 }; + return nullFormatter; + } + for (f =ctx->FactoryList; f != NULL; f = f ->Next) { cmsFormatter fn = f ->Factory(Type, Dir, dwFlags); @@ -3439,9 +4056,12 @@ cmsUInt32Number CMSEXPORT cmsFormatterForColorspaceOfProfile(cmsHPROFILE hProfil cmsColorSpaceSignature ColorSpace = cmsGetColorSpace(hProfile); cmsUInt32Number ColorSpaceBits = (cmsUInt32Number) _cmsLCMScolorSpace(ColorSpace); - cmsUInt32Number nOutputChans = cmsChannelsOf(ColorSpace); + cmsInt32Number nOutputChans = cmsChannelsOfColorSpace(ColorSpace); cmsUInt32Number Float = lIsFloat ? 1U : 0; + // Unsupported color space? + if (nOutputChans < 0) return 0; + // Create a fake formatter for result return FLOAT_SH(Float) | COLORSPACE_SH(ColorSpaceBits) | BYTES_SH(nBytes) | CHANNELS_SH(nOutputChans); } @@ -3453,9 +4073,12 @@ 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? + if (nOutputChans < 0) return 0; + // Create a fake formatter for result return FLOAT_SH(Float) | COLORSPACE_SH(ColorSpaceBits) | BYTES_SH(nBytes) | CHANNELS_SH(nOutputChans); } diff --git a/jdk/src/share/native/sun/java2d/cmm/lcms/cmspcs.c b/jdk/src/share/native/sun/java2d/cmm/lcms/cmspcs.c index 6c628ed5e46..c4739840053 100644 --- a/jdk/src/share/native/sun/java2d/cmm/lcms/cmspcs.c +++ b/jdk/src/share/native/sun/java2d/cmm/lcms/cmspcs.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2020 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"), @@ -903,7 +903,7 @@ int CMSEXPORT _cmsLCMScolorSpace(cmsColorSpaceSignature ProfileSpace) } -cmsUInt32Number CMSEXPORT cmsChannelsOf(cmsColorSpaceSignature ColorSpace) +cmsInt32Number CMSEXPORT cmsChannelsOfColorSpace(cmsColorSpaceSignature ColorSpace) { switch (ColorSpace) { @@ -964,6 +964,16 @@ cmsUInt32Number CMSEXPORT cmsChannelsOf(cmsColorSpaceSignature ColorSpace) case cmsSigMCHFData: case cmsSig15colorData: return 15; - default: return 3; + default: return -1; } } + +/** +* DEPRECATED: Provided for compatibility only +*/ +cmsUInt32Number CMSEXPORT cmsChannelsOf(cmsColorSpaceSignature ColorSpace) +{ + int n = cmsChannelsOfColorSpace(ColorSpace); + if (n < 0) return 3; + return (cmsUInt32Number)n; +} \ No newline at end of file diff --git a/jdk/src/share/native/sun/java2d/cmm/lcms/cmsplugin.c b/jdk/src/share/native/sun/java2d/cmm/lcms/cmsplugin.c index 037e6cbcdf1..f84e0172c81 100644 --- a/jdk/src/share/native/sun/java2d/cmm/lcms/cmsplugin.c +++ b/jdk/src/share/native/sun/java2d/cmm/lcms/cmsplugin.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2020 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"), @@ -197,17 +197,20 @@ cmsBool CMSEXPORT _cmsReadUInt32Number(cmsIOHANDLER* io, cmsUInt32Number* n) cmsBool CMSEXPORT _cmsReadFloat32Number(cmsIOHANDLER* io, cmsFloat32Number* n) { - cmsUInt32Number tmp; + union typeConverter { + cmsUInt32Number integer; + cmsFloat32Number floating_point; + } tmp; _cmsAssert(io != NULL); - if (io->Read(io, &tmp, sizeof(cmsUInt32Number), 1) != 1) + if (io->Read(io, &tmp.integer, sizeof(cmsUInt32Number), 1) != 1) return FALSE; if (n != NULL) { - tmp = _cmsAdjustEndianess32(tmp); - *n = *(cmsFloat32Number*)(void*)&tmp; + tmp.integer = _cmsAdjustEndianess32(tmp.integer); + *n = tmp.floating_point; // Safeguard which covers against absurd values if (*n > 1E+20 || *n < -1E+20) return FALSE; @@ -334,13 +337,14 @@ cmsBool CMSEXPORT _cmsWriteUInt32Number(cmsIOHANDLER* io, cmsUInt32Number n) cmsBool CMSEXPORT _cmsWriteFloat32Number(cmsIOHANDLER* io, cmsFloat32Number n) { - cmsUInt32Number tmp; - - _cmsAssert(io != NULL); - - tmp = *(cmsUInt32Number*) (void*) &n; - tmp = _cmsAdjustEndianess32(tmp); - if (io -> Write(io, sizeof(cmsUInt32Number), &tmp) != 1) + union typeConverter { + cmsUInt32Number integer; + cmsFloat32Number floating_point; + } tmp; + + tmp.floating_point = n; + tmp.integer = _cmsAdjustEndianess32(tmp.integer); + if (io -> Write(io, sizeof(cmsUInt32Number), &tmp.integer) != 1) return FALSE; return TRUE; @@ -389,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) @@ -406,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 @@ -650,6 +637,10 @@ cmsBool CMSEXPORT cmsPluginTHR(cmsContext id, void* Plug_in) if (!_cmsRegisterMutexPlugin(id, Plugin)) return FALSE; break; + case cmsPluginParalellizationSig: + if (!_cmsRegisterParallelizationPlugin(id, Plugin)) return FALSE; + break; + default: cmsSignalError(id, cmsERROR_UNKNOWN_EXTENSION, "Unrecognized plugin type '%X'", Plugin -> Type); return FALSE; @@ -672,24 +663,25 @@ void CMSEXPORT cmsUnregisterPlugins(void) // pointers structure. All global vars are referenced here. static struct _cmsContext_struct globalContext = { - NULL, // Not in the linked list - NULL, // No suballocator + NULL, // Not in the linked list + NULL, // No suballocator { - NULL, // UserPtr, - &_cmsLogErrorChunk, // Logger, - &_cmsAlarmCodesChunk, // AlarmCodes, - &_cmsAdaptationStateChunk, // AdaptationState, - &_cmsMemPluginChunk, // MemPlugin, - &_cmsInterpPluginChunk, // InterpPlugin, - &_cmsCurvesPluginChunk, // CurvesPlugin, - &_cmsFormattersPluginChunk, // FormattersPlugin, - &_cmsTagTypePluginChunk, // TagTypePlugin, - &_cmsTagPluginChunk, // TagPlugin, - &_cmsIntentsPluginChunk, // IntentPlugin, - &_cmsMPETypePluginChunk, // MPEPlugin, - &_cmsOptimizationPluginChunk, // OptimizationPlugin, - &_cmsTransformPluginChunk, // TransformPlugin, - &_cmsMutexPluginChunk // MutexPlugin + NULL, // UserPtr, + &_cmsLogErrorChunk, // Logger, + &_cmsAlarmCodesChunk, // AlarmCodes, + &_cmsAdaptationStateChunk, // AdaptationState, + &_cmsMemPluginChunk, // MemPlugin, + &_cmsInterpPluginChunk, // InterpPlugin, + &_cmsCurvesPluginChunk, // CurvesPlugin, + &_cmsFormattersPluginChunk, // FormattersPlugin, + &_cmsTagTypePluginChunk, // TagTypePlugin, + &_cmsTagPluginChunk, // TagPlugin, + &_cmsIntentsPluginChunk, // IntentPlugin, + &_cmsMPETypePluginChunk, // MPEPlugin, + &_cmsOptimizationPluginChunk, // OptimizationPlugin, + &_cmsTransformPluginChunk, // TransformPlugin, + &_cmsMutexPluginChunk, // MutexPlugin, + &_cmsParallelizationPluginChunk // ParallelizationPlugin }, { NULL, NULL, NULL, NULL, NULL, NULL } // The default memory allocator is not used for context 0 @@ -700,17 +692,65 @@ static struct _cmsContext_struct globalContext = { static _cmsMutex _cmsContextPoolHeadMutex = CMS_MUTEX_INITIALIZER; static struct _cmsContext_struct* _cmsContextPoolHead = NULL; + +// Make sure context is initialized (needed on windows) +static +cmsBool InitContextMutex(void) +{ + // See the comments regarding locking in lcms2_internal.h + // for an explanation of why we need the following code. +#ifndef CMS_NO_PTHREADS +#ifdef CMS_IS_WINDOWS_ +#ifndef CMS_RELY_ON_WINDOWS_STATIC_MUTEX_INIT + + static cmsBool already_initialized = FALSE; + + if (!already_initialized) + { + static HANDLE _cmsWindowsInitMutex = NULL; + static volatile HANDLE* mutex = &_cmsWindowsInitMutex; + + if (*mutex == NULL) + { + HANDLE p = CreateMutex(NULL, FALSE, NULL); + if (p && InterlockedCompareExchangePointer((void**)mutex, (void*)p, NULL) != NULL) + CloseHandle(p); + } + if (*mutex == NULL || WaitForSingleObject(*mutex, INFINITE) == WAIT_FAILED) + { + cmsSignalError(0, cmsERROR_INTERNAL, "Mutex lock failed"); + return FALSE; + } + if (((void**)&_cmsContextPoolHeadMutex)[0] == NULL) + InitializeCriticalSection(&_cmsContextPoolHeadMutex); + if (*mutex == NULL || !ReleaseMutex(*mutex)) + { + cmsSignalError(0, cmsERROR_INTERNAL, "Mutex unlock failed"); + return FALSE; + } + already_initialized = TRUE; + } +#endif +#endif +#endif + + return TRUE; +} + + + // Internal, get associated pointer, with guessing. Never returns NULL. struct _cmsContext_struct* _cmsGetContext(cmsContext ContextID) { struct _cmsContext_struct* id = (struct _cmsContext_struct*) ContextID; struct _cmsContext_struct* ctx; - // On 0, use global settings if (id == NULL) return &globalContext; + InitContextMutex(); + // Search _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex); @@ -779,6 +819,8 @@ void CMSEXPORT cmsUnregisterPluginsTHR(cmsContext ContextID) _cmsRegisterOptimizationPlugin(ContextID, NULL); _cmsRegisterTransformPlugin(ContextID, NULL); _cmsRegisterMutexPlugin(ContextID, NULL); + _cmsRegisterParallelizationPlugin(ContextID, NULL); + } @@ -813,31 +855,7 @@ cmsContext CMSEXPORT cmsCreateContext(void* Plugin, void* UserData) struct _cmsContext_struct* ctx; struct _cmsContext_struct fakeContext; - // See the comments regarding locking in lcms2_internal.h - // for an explanation of why we need the following code. -#ifndef CMS_NO_PTHREADS -#ifdef CMS_IS_WINDOWS_ -#ifndef CMS_RELY_ON_WINDOWS_STATIC_MUTEX_INIT - { - static HANDLE _cmsWindowsInitMutex = NULL; - static volatile HANDLE* mutex = &_cmsWindowsInitMutex; - - if (*mutex == NULL) - { - HANDLE p = CreateMutex(NULL, FALSE, NULL); - if (p && InterlockedCompareExchangePointer((void **)mutex, (void*)p, NULL) != NULL) - CloseHandle(p); - } - if (*mutex == NULL || WaitForSingleObject(*mutex, INFINITE) == WAIT_FAILED) - return NULL; - if (((void **)&_cmsContextPoolHeadMutex)[0] == NULL) - InitializeCriticalSection(&_cmsContextPoolHeadMutex); - if (*mutex == NULL || !ReleaseMutex(*mutex)) - return NULL; - } -#endif -#endif -#endif + if (!InitContextMutex()) return NULL; _cmsInstallAllocFunctions(_cmsFindMemoryPlugin(Plugin), &fakeContext.DefaultMemoryManager); @@ -886,6 +904,7 @@ cmsContext CMSEXPORT cmsCreateContext(void* Plugin, void* UserData) _cmsAllocOptimizationPluginChunk(ctx, NULL); _cmsAllocTransformPluginChunk(ctx, NULL); _cmsAllocMutexPluginChunk(ctx, NULL); + _cmsAllocParallelizationPluginChunk(ctx, NULL); // Setup the plug-ins if (!cmsPluginTHR(ctx, Plugin)) { @@ -913,6 +932,8 @@ cmsContext CMSEXPORT cmsDupContext(cmsContext ContextID, void* NewUserData) if (ctx == NULL) return NULL; // Something very wrong happened + if (!InitContextMutex()) return NULL; + // Setup default memory allocators memcpy(&ctx->DefaultMemoryManager, &src->DefaultMemoryManager, sizeof(ctx->DefaultMemoryManager)); @@ -947,6 +968,7 @@ cmsContext CMSEXPORT cmsDupContext(cmsContext ContextID, void* NewUserData) _cmsAllocOptimizationPluginChunk(ctx, src); _cmsAllocTransformPluginChunk(ctx, src); _cmsAllocMutexPluginChunk(ctx, src); + _cmsAllocParallelizationPluginChunk(ctx, src); // Make sure no one failed for (i=Logger; i < MemoryClientMax; i++) { @@ -966,12 +988,22 @@ cmsContext CMSEXPORT cmsDupContext(cmsContext ContextID, void* NewUserData) // The ContextID can no longer be used in any THR operation. void CMSEXPORT cmsDeleteContext(cmsContext ContextID) { - if (ContextID != NULL) { + if (ContextID == NULL) { + + cmsUnregisterPlugins(); + if (globalContext.MemPool != NULL) + _cmsSubAllocDestroy(globalContext.MemPool); + globalContext.MemPool = NULL; + } + else { struct _cmsContext_struct* ctx = (struct _cmsContext_struct*) ContextID; struct _cmsContext_struct fakeContext; struct _cmsContext_struct* prev; + + InitContextMutex(); + memcpy(&fakeContext.DefaultMemoryManager, &ctx->DefaultMemoryManager, sizeof(ctx->DefaultMemoryManager)); fakeContext.chunks[UserPtr] = ctx ->chunks[UserPtr]; @@ -1018,3 +1050,32 @@ void* CMSEXPORT cmsGetContextUserData(cmsContext ContextID) } +// Use context mutex to provide thread-safe time +cmsBool _cmsGetTime(struct tm* ptr_time) +{ + struct tm* t; +#if defined(HAVE_GMTIME_R) || defined(HAVE_GMTIME_S) + struct tm tm; +#endif + + time_t now = time(NULL); + +#ifdef HAVE_GMTIME_R + t = gmtime_r(&now, &tm); +#elif defined(HAVE_GMTIME_S) + t = gmtime_s(&tm, &now) == 0 ? &tm : NULL; +#else + if (!InitContextMutex()) return FALSE; + + _cmsEnterCriticalSectionPrimitive(&_cmsContextPoolHeadMutex); + t = gmtime(&now); + _cmsLeaveCriticalSectionPrimitive(&_cmsContextPoolHeadMutex); +#endif + + if (t == NULL) + return FALSE; + else { + *ptr_time = *t; + return TRUE; + } +} diff --git a/jdk/src/share/native/sun/java2d/cmm/lcms/cmsps2.c b/jdk/src/share/native/sun/java2d/cmm/lcms/cmsps2.c index e5274540064..9a2ab464f31 100644 --- a/jdk/src/share/native/sun/java2d/cmm/lcms/cmsps2.c +++ b/jdk/src/share/native/sun/java2d/cmm/lcms/cmsps2.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2020 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"), @@ -460,69 +460,55 @@ 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) + if (i % 10 == 0) _cmsIOPrintf(m, "\n "); _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 "); + + Emit1Gamma(m, Curve); - _cmsIOPrintf(m, "/DecodeA /lcms2gammaproc load\n"); - EmitSafeGuardEnd(m, "lcms2gammaproc", 3); + _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"); diff --git a/jdk/src/share/native/sun/java2d/cmm/lcms/cmssamp.c b/jdk/src/share/native/sun/java2d/cmm/lcms/cmssamp.c index d3836420b3f..74f5f4bff29 100644 --- a/jdk/src/share/native/sun/java2d/cmm/lcms/cmssamp.c +++ b/jdk/src/share/native/sun/java2d/cmm/lcms/cmssamp.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2020 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"), @@ -152,9 +152,9 @@ cmsBool BlackPointAsDarkerColorant(cmsHPROFILE hInput, // Convert black to Lab cmsDoTransform(xform, Black, &Lab, 1); - // Force it to be neutral, clip to max. L* of 50 + // Force it to be neutral, check for inconsistencies Lab.a = Lab.b = 0; - if (Lab.L > 50) Lab.L = 50; + if (Lab.L > 50 || Lab.L < 0) Lab.L = 0; // Free the resources cmsDeleteTransform(xform); @@ -351,6 +351,7 @@ cmsFloat64Number RootOfLeastSquaresFitQuadraticCurve(int n, cmsFloat64Number x[] if (fabs(a) < 1.0E-10) { + if (fabs(b) < 1.0E-10) return 0; return cmsmin(0, cmsmax(50, -c/b )); } else { @@ -361,7 +362,11 @@ cmsFloat64Number RootOfLeastSquaresFitQuadraticCurve(int n, cmsFloat64Number x[] } else { - double rt = (-b + sqrt(d)) / (2.0 * a); + double rt; + + if (fabs(a) < 1.0E-10) return 0; + + rt = (-b + sqrt(d)) / (2.0 * a); return cmsmax(0, cmsmin(50, rt)); } diff --git a/jdk/src/share/native/sun/java2d/cmm/lcms/cmssm.c b/jdk/src/share/native/sun/java2d/cmm/lcms/cmssm.c index 3d898d2c82a..3e1486ae3ae 100644 --- a/jdk/src/share/native/sun/java2d/cmm/lcms/cmssm.c +++ b/jdk/src/share/native/sun/java2d/cmm/lcms/cmssm.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2020 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"), diff --git a/jdk/src/share/native/sun/java2d/cmm/lcms/cmstypes.c b/jdk/src/share/native/sun/java2d/cmm/lcms/cmstypes.c index 006f98b084d..862f393497a 100644 --- a/jdk/src/share/native/sun/java2d/cmm/lcms/cmstypes.c +++ b/jdk/src/share/native/sun/java2d/cmm/lcms/cmstypes.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2020 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"), @@ -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) @@ -163,15 +163,62 @@ cmsBool _cmsWriteWCharArray(cmsIOHANDLER* io, cmsUInt32Number n, const wchar_t* return TRUE; } +// Try to promote correctly to wchar_t when 32 bits +cmsINLINE cmsBool is_surrogate(cmsUInt32Number uc) { return (uc - 0xd800u) < 2048u; } +cmsINLINE cmsBool is_high_surrogate(cmsUInt32Number uc) { return (uc & 0xfffffc00) == 0xd800; } +cmsINLINE cmsBool is_low_surrogate(cmsUInt32Number uc) { return (uc & 0xfffffc00) == 0xdc00; } + +cmsINLINE cmsUInt32Number surrogate_to_utf32(cmsUInt32Number high, cmsUInt32Number low) +{ + return (high << 10) + low - 0x35fdc00; +} + +cmsINLINE cmsBool convert_utf16_to_utf32(cmsIOHANDLER* io, cmsInt32Number n, wchar_t* output) +{ + cmsUInt16Number uc; + + while (n > 0) + { + if (!_cmsReadUInt16Number(io, &uc)) return FALSE; + n--; + + if (!is_surrogate(uc)) + { + *output++ = (wchar_t)uc; + } + else { + + cmsUInt16Number low; + + if (!_cmsReadUInt16Number(io, &low)) return FALSE; + n--; + + if (is_high_surrogate(uc) && is_low_surrogate(low)) + *output++ = (wchar_t)surrogate_to_utf32(uc, low); + else + return FALSE; // Corrupted string, just ignore + } + } + + return TRUE; +} + + // Auxiliary to read an array of wchar_t static cmsBool _cmsReadWCharArray(cmsIOHANDLER* io, cmsUInt32Number n, wchar_t* Array) { cmsUInt32Number i; cmsUInt16Number tmp; + cmsBool is32 = sizeof(wchar_t) > sizeof(cmsUInt16Number); _cmsAssert(io != NULL); + if (is32 && Array != NULL) + { + return convert_utf16_to_utf32(io, n, Array); + } + for (i=0; i < n; i++) { if (Array != NULL) { @@ -907,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; @@ -926,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 @@ -951,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; + + UnicodeString = (wchar_t*)_cmsMallocZero(self->ContextID, (UnicodeCount + 1) * sizeof(wchar_t)); + if (UnicodeString == NULL) goto Done; - for (i=0; i < UnicodeCount; i++) { - if (!io ->Read(io, &Dummy, sizeof(cmsUInt16Number), 1)) 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) { @@ -979,6 +1042,7 @@ void *Type_Text_Description_Read(struct _cms_typehandler_struct* self, cmsIOHAND return mlu; Error: + if (UnicodeString) _cmsFree(self->ContextID, (void*)UnicodeString); if (Text) _cmsFree(self ->ContextID, (void*) Text); if (mlu) cmsMLUfree(mlu); return NULL; @@ -1031,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 @@ -1336,7 +1400,7 @@ void Type_ParametricCurve_Free(struct _cms_typehandler_struct* self, void* Ptr) // // 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 +// time to UTC when setting these values. Programs 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. @@ -1502,6 +1566,12 @@ void *Type_MLU_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsU if (!_cmsReadUInt32Number(io, &Len)) goto Error; if (!_cmsReadUInt32Number(io, &Offset)) goto Error; + // Offset MUST be even because it indexes a block of utf16 chars. + // Tricky profiles that uses odd positions will not work anyway + // because the whole utf16 block is previously converted to wchar_t + // and sizeof this type may be of 4 bytes. On Linux systems, for example. + if (Offset & 1) goto Error; + // Check for overflow if (Offset < (SizeOfHeader + 8)) goto Error; if (((Offset + Len) < Len) || ((Offset + Len) > SizeOfTag + 8)) goto Error; @@ -1524,15 +1594,20 @@ void *Type_MLU_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsU if (SizeOfTag == 0) { Block = NULL; - NumOfWchar = 0; - } else { - Block = (wchar_t*) _cmsMalloc(self ->ContextID, SizeOfTag); + // Make sure this is an even utf16 size. + if (SizeOfTag & 1) goto Error; + + Block = (wchar_t*) _cmsCalloc(self ->ContextID, 1, SizeOfTag); if (Block == NULL) goto Error; + NumOfWchar = SizeOfTag / sizeof(wchar_t); - if (!_cmsReadWCharArray(io, NumOfWchar, Block)) goto Error; + if (!_cmsReadWCharArray(io, NumOfWchar, Block)) { + _cmsFree(self->ContextID, Block); + goto Error; + } } mlu ->MemPool = Block; @@ -1775,7 +1850,7 @@ cmsUInt32Number uipow(cmsUInt32Number n, cmsUInt32Number a, cmsUInt32Number b) // 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 +// 8 bit lut may be scaled easily 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 @@ -1880,9 +1955,9 @@ void *Type_LUT8_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cms // 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, n; + cmsUInt32Number j, nTabSize, i; cmsUInt8Number val; cmsPipeline* NewLUT = (cmsPipeline*) Ptr; cmsStage* mpe; @@ -1893,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; @@ -1917,28 +1998,34 @@ cmsBool Type_LUT8_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, // That should be all if (mpe != NULL) { - cmsSignalError(mpe->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT is not suitable to be saved as LUT8"); + cmsSignalError(self->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]; + else { + // Lut8 only allows same CLUT points in all dimensions + clutPoints = clut->Params->nSamples[0]; + for (i = 1; i < cmsPipelineInputChannels(NewLUT); i++) { + if (clut->Params->nSamples[i] != clutPoints) { + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT with different samples per dimension not suitable to be saved as LUT16"); + return FALSE; + } + } + } - if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) NewLUT ->InputChannels)) return FALSE; - if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) NewLUT ->OutputChannels)) return FALSE; + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number)cmsPipelineInputChannels(NewLUT))) return FALSE; + if (!_cmsWriteUInt8Number(io, (cmsUInt8Number)cmsPipelineOutputChannels(NewLUT))) return FALSE; if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) clutPoints)) return FALSE; if (!_cmsWriteUInt8Number(io, 0)) return FALSE; // Padding - n = NewLUT->InputChannels * NewLUT->OutputChannels; - if (MatMPE != NULL) { - for (i = 0; i < 9; i++) - { - if (!_cmsWrite15Fixed16Number(io, MatMPE->Double[i])) return FALSE; - } + for (i = 0; i < 9; i++) + { + if (!_cmsWrite15Fixed16Number(io, MatMPE->Double[i])) return FALSE; + } } else { @@ -2056,10 +2143,10 @@ cmsBool Write16bitTables(cmsContext ContextID, cmsIOHANDLER* io, _cmsStageToneCu _cmsAssert(Tables != NULL); - nEntries = Tables->TheCurves[0]->nEntries; - for (i=0; i < Tables ->nCurves; i++) { + nEntries = Tables->TheCurves[i]->nEntries; + for (j=0; j < nEntries; j++) { val = Tables->TheCurves[i]->Table16[j]; @@ -2202,7 +2289,7 @@ cmsBool Type_LUT16_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, // That should be all if (mpe != NULL) { - cmsSignalError(mpe->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT is not suitable to be saved as LUT16"); + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT is not suitable to be saved as LUT16"); return FALSE; } @@ -2211,8 +2298,16 @@ cmsBool Type_LUT16_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, if (clut == NULL) clutPoints = 0; - else - clutPoints = clut->Params->nSamples[0]; + else { + // Lut16 only allows same CLUT points in all dimensions + clutPoints = clut->Params->nSamples[0]; + for (i = 1; i < InputChannels; i++) { + if (clut->Params->nSamples[i] != clutPoints) { + cmsSignalError(self->ContextID, cmsERROR_UNKNOWN_EXTENSION, "LUT with different samples per dimension not suitable to be saved as LUT16"); + return FALSE; + } + } + } if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) InputChannels)) return FALSE; if (!_cmsWriteUInt8Number(io, (cmsUInt8Number) OutputChannels)) return FALSE; @@ -2221,10 +2316,10 @@ cmsBool Type_LUT16_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, if (MatMPE != NULL) { - for (i = 0; i < 9; i++) - { - if (!_cmsWrite15Fixed16Number(io, MatMPE->Double[i])) return FALSE; - } + for (i = 0; i < 9; i++) + { + if (!_cmsWrite15Fixed16Number(io, MatMPE->Double[i])) return FALSE; + } } else { @@ -2570,31 +2665,31 @@ void* Type_LUTA2B_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, c static cmsBool WriteMatrix(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsStage* mpe) { - cmsUInt32Number i, n; + cmsUInt32Number i, n; _cmsStageMatrixData* m = (_cmsStageMatrixData*) mpe -> Data; - n = mpe->InputChannels * mpe->OutputChannels; + n = mpe->InputChannels * mpe->OutputChannels; - // Write the Matrix - for (i = 0; i < n; i++) - { - if (!_cmsWrite15Fixed16Number(io, m->Double[i])) return FALSE; - } + // Write the Matrix + for (i = 0; i < n; i++) + { + if (!_cmsWrite15Fixed16Number(io, m->Double[i])) return FALSE; + } - if (m->Offset != NULL) { + if (m->Offset != NULL) { - for (i = 0; i < mpe->OutputChannels; i++) - { - if (!_cmsWrite15Fixed16Number(io, m->Offset[i])) return FALSE; - } + for (i = 0; i < mpe->OutputChannels; i++) + { + if (!_cmsWrite15Fixed16Number(io, m->Offset[i])) return FALSE; } - else { - for (i = 0; i < mpe->OutputChannels; i++) - { - if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; - } + } + else { + for (i = 0; i < mpe->OutputChannels; i++) + { + if (!_cmsWrite15Fixed16Number(io, 0)) return FALSE; } + } return TRUE; @@ -2620,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) @@ -3125,7 +3220,6 @@ void Type_ColorantTable_Free(struct _cms_typehandler_struct* self, void* Ptr) static void *Type_NamedColor_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) { - cmsUInt32Number vendorFlag; // Bottom 16 bits for ICC use cmsUInt32Number count; // Count of named colors cmsUInt32Number nDeviceCoords; // Num of device coordinates @@ -3197,8 +3291,8 @@ cmsBool Type_NamedColor_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER if (!_cmsWriteUInt32Number(io, nColors)) return FALSE; if (!_cmsWriteUInt32Number(io, NamedColorList ->ColorantCount)) return FALSE; - strncpy(prefix, (const char*) NamedColorList->Prefix, 32); - strncpy(suffix, (const char*) NamedColorList->Suffix, 32); + memcpy(prefix, (const char*) NamedColorList->Prefix, sizeof(prefix)); + memcpy(suffix, (const char*) NamedColorList->Suffix, sizeof(suffix)); suffix[32] = prefix[32] = 0; @@ -3211,6 +3305,10 @@ cmsBool Type_NamedColor_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER cmsUInt16Number Colorant[cmsMAXCHANNELS]; char Root[cmsMAX_PATH]; + memset(Root, 0, sizeof(Root)); + memset(PCS, 0, sizeof(PCS)); + memset(Colorant, 0, sizeof(Colorant)); + if (!cmsNamedColorInfo(NamedColorList, i, Root, NULL, NULL, PCS, Colorant)) return 0; Root[32] = 0; if (!io ->Write(io, 32 , Root)) return FALSE; @@ -3451,7 +3549,6 @@ void *Type_ProfileSequenceId_Read(struct _cms_typehandler_struct* self, cmsIOHAN // Get table count if (!_cmsReadUInt32Number(io, &Count)) return NULL; - SizeOfTag -= sizeof(cmsUInt32Number); // Allocate an empty structure OutSeq = cmsAllocProfileSequenceDescription(self ->ContextID, Count); @@ -3469,6 +3566,7 @@ void *Type_ProfileSequenceId_Read(struct _cms_typehandler_struct* self, cmsIOHAN *nItems = 1; return OutSeq; + cmsUNUSED_PARAMETER(SizeOfTag); } @@ -3544,47 +3642,68 @@ void *Type_UcrBg_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cm { cmsUcrBg* n = (cmsUcrBg*) _cmsMallocZero(self ->ContextID, sizeof(cmsUcrBg)); cmsUInt32Number CountUcr, CountBg; + cmsInt32Number SignedSizeOfTag = (cmsInt32Number)SizeOfTag; char* ASCIIString; *nItems = 0; if (n == NULL) return NULL; // First curve is Under color removal + + if (SignedSizeOfTag < (cmsInt32Number) sizeof(cmsUInt32Number)) return NULL; if (!_cmsReadUInt32Number(io, &CountUcr)) return NULL; - if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL; - SizeOfTag -= sizeof(cmsUInt32Number); + SignedSizeOfTag -= sizeof(cmsUInt32Number); n ->Ucr = cmsBuildTabulatedToneCurve16(self ->ContextID, CountUcr, NULL); - if (n ->Ucr == NULL) return NULL; + if (n ->Ucr == NULL) goto error; - if (!_cmsReadUInt16Array(io, CountUcr, n ->Ucr->Table16)) return NULL; - if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL; - SizeOfTag -= CountUcr * sizeof(cmsUInt16Number); + if (SignedSizeOfTag < (cmsInt32Number)(CountUcr * sizeof(cmsUInt16Number))) goto error; + if (!_cmsReadUInt16Array(io, CountUcr, n ->Ucr->Table16)) goto error; + + SignedSizeOfTag -= CountUcr * sizeof(cmsUInt16Number); // Second curve is Black generation - if (!_cmsReadUInt32Number(io, &CountBg)) return NULL; - if (SizeOfTag < sizeof(cmsUInt32Number)) return NULL; - SizeOfTag -= sizeof(cmsUInt32Number); + + if (SignedSizeOfTag < (cmsInt32Number)sizeof(cmsUInt32Number)) goto error; + if (!_cmsReadUInt32Number(io, &CountBg)) goto error; + SignedSizeOfTag -= sizeof(cmsUInt32Number); n ->Bg = cmsBuildTabulatedToneCurve16(self ->ContextID, CountBg, NULL); - if (n ->Bg == NULL) return NULL; - if (!_cmsReadUInt16Array(io, CountBg, n ->Bg->Table16)) return NULL; - if (SizeOfTag < CountBg * sizeof(cmsUInt16Number)) return NULL; - SizeOfTag -= CountBg * sizeof(cmsUInt16Number); - if (SizeOfTag == UINT_MAX) return NULL; + if (n ->Bg == NULL) goto error; + + if (SignedSizeOfTag < (cmsInt32Number) (CountBg * sizeof(cmsUInt16Number))) goto error; + if (!_cmsReadUInt16Array(io, CountBg, n ->Bg->Table16)) goto error; + SignedSizeOfTag -= CountBg * sizeof(cmsUInt16Number); + + if (SignedSizeOfTag < 0 || SignedSizeOfTag > 32000) goto error; // Now comes the text. The length is specified by the tag size n ->Desc = cmsMLUalloc(self ->ContextID, 1); - if (n ->Desc == NULL) return NULL; + if (n ->Desc == NULL) goto error; - ASCIIString = (char*) _cmsMalloc(self ->ContextID, SizeOfTag + 1); - if (io ->Read(io, ASCIIString, sizeof(char), SizeOfTag) != SizeOfTag) return NULL; - ASCIIString[SizeOfTag] = 0; + ASCIIString = (char*) _cmsMalloc(self ->ContextID, SignedSizeOfTag + 1); + if (io->Read(io, ASCIIString, sizeof(char), SignedSizeOfTag) != (cmsUInt32Number)SignedSizeOfTag) + { + _cmsFree(self->ContextID, ASCIIString); + goto error; + } + + ASCIIString[SignedSizeOfTag] = 0; cmsMLUsetASCII(n ->Desc, cmsNoLanguage, cmsNoCountry, ASCIIString); _cmsFree(self ->ContextID, ASCIIString); *nItems = 1; return (void*) n; + +error: + + if (n->Ucr) cmsFreeToneCurve(n->Ucr); + if (n->Bg) cmsFreeToneCurve(n->Bg); + if (n->Desc) cmsMLUfree(n->Desc); + _cmsFree(self->ContextID, n); + *nItems = 0; + return NULL; + } static @@ -3665,7 +3784,7 @@ country varies for each element: // Auxiliary, read an string specified as count + string static -cmsBool ReadCountAndSting(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* mlu, cmsUInt32Number* SizeOfTag, const char* Section) +cmsBool ReadCountAndString(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* mlu, cmsUInt32Number* SizeOfTag, const char* Section) { cmsUInt32Number Count; char* Text; @@ -3695,7 +3814,7 @@ cmsBool ReadCountAndSting(struct _cms_typehandler_struct* self, cmsIOHANDLER* i } static -cmsBool WriteCountAndSting(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* mlu, const char* Section) +cmsBool WriteCountAndString(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* mlu, const char* Section) { cmsUInt32Number TextSize; char* Text; @@ -3719,11 +3838,11 @@ void *Type_CrdInfo_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsMLU* mlu = cmsMLUalloc(self ->ContextID, 5); *nItems = 0; - if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "nm")) goto Error; - if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#0")) goto Error; - if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#1")) goto Error; - if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#2")) goto Error; - if (!ReadCountAndSting(self, io, mlu, &SizeOfTag, "#3")) goto Error; + if (!ReadCountAndString(self, io, mlu, &SizeOfTag, "nm")) goto Error; + if (!ReadCountAndString(self, io, mlu, &SizeOfTag, "#0")) goto Error; + if (!ReadCountAndString(self, io, mlu, &SizeOfTag, "#1")) goto Error; + if (!ReadCountAndString(self, io, mlu, &SizeOfTag, "#2")) goto Error; + if (!ReadCountAndString(self, io, mlu, &SizeOfTag, "#3")) goto Error; *nItems = 1; return (void*) mlu; @@ -3740,11 +3859,11 @@ cmsBool Type_CrdInfo_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* cmsMLU* mlu = (cmsMLU*) Ptr; - if (!WriteCountAndSting(self, io, mlu, "nm")) goto Error; - if (!WriteCountAndSting(self, io, mlu, "#0")) goto Error; - if (!WriteCountAndSting(self, io, mlu, "#1")) goto Error; - if (!WriteCountAndSting(self, io, mlu, "#2")) goto Error; - if (!WriteCountAndSting(self, io, mlu, "#3")) goto Error; + if (!WriteCountAndString(self, io, mlu, "nm")) goto Error; + if (!WriteCountAndString(self, io, mlu, "#0")) goto Error; + if (!WriteCountAndString(self, io, mlu, "#1")) goto Error; + if (!WriteCountAndString(self, io, mlu, "#2")) goto Error; + if (!WriteCountAndString(self, io, mlu, "#3")) goto Error; return TRUE; @@ -3998,41 +4117,44 @@ cmsToneCurve* ReadSegmentedCurve(struct _cms_typehandler_struct* self, cmsIOHAND switch (ElementSig) { - case cmsSigFormulaCurveSeg: { + case cmsSigFormulaCurveSeg: { - cmsUInt16Number Type; - cmsUInt32Number ParamsByType[] = {4, 5, 5 }; + cmsUInt16Number Type; + cmsUInt32Number ParamsByType[] = { 4, 5, 5 }; - if (!_cmsReadUInt16Number(io, &Type)) goto Error; - if (!_cmsReadUInt16Number(io, NULL)) goto Error; + if (!_cmsReadUInt16Number(io, &Type)) goto Error; + if (!_cmsReadUInt16Number(io, NULL)) goto Error; - Segments[i].Type = Type + 6; - if (Type > 2) goto Error; + Segments[i].Type = Type + 6; + if (Type > 2) goto Error; - for (j=0; j < ParamsByType[Type]; j++) { + for (j = 0; j < ParamsByType[Type]; j++) { - cmsFloat32Number f; - if (!_cmsReadFloat32Number(io, &f)) goto Error; - Segments[i].Params[j] = f; - } - } - break; + cmsFloat32Number f; + if (!_cmsReadFloat32Number(io, &f)) goto Error; + Segments[i].Params[j] = f; + } + } + break; - case cmsSigSampledCurveSeg: { - cmsUInt32Number Count; + case cmsSigSampledCurveSeg: { + cmsUInt32Number Count; - if (!_cmsReadUInt32Number(io, &Count)) goto Error; + if (!_cmsReadUInt32Number(io, &Count)) goto Error; - Segments[i].nGridPoints = Count; - Segments[i].SampledPoints = (cmsFloat32Number*) _cmsCalloc(self ->ContextID, Count, sizeof(cmsFloat32Number)); - if (Segments[i].SampledPoints == NULL) goto Error; + // The first point is implicit in the last stage, we allocate an extra note to be populated latter on + Count++; + Segments[i].nGridPoints = Count; + Segments[i].SampledPoints = (cmsFloat32Number*)_cmsCalloc(self->ContextID, Count, sizeof(cmsFloat32Number)); + if (Segments[i].SampledPoints == NULL) goto Error; - for (j=0; j < Count; j++) { - if (!_cmsReadFloat32Number(io, &Segments[i].SampledPoints[j])) goto Error; - } - } - break; + Segments[i].SampledPoints[0] = 0; + for (j = 1; j < Count; j++) { + if (!_cmsReadFloat32Number(io, &Segments[i].SampledPoints[j])) goto Error; + } + } + break; default: { @@ -4052,6 +4174,17 @@ cmsToneCurve* ReadSegmentedCurve(struct _cms_typehandler_struct* self, cmsIOHAND if (Segments[i].SampledPoints) _cmsFree(self ->ContextID, Segments[i].SampledPoints); } _cmsFree(self ->ContextID, Segments); + + // Explore for missing implicit points + for (i = 0; i < nSegments; i++) { + + // If sampled curve, fix it + if (Curve->Segments[i].Type == 0) { + + Curve->Segments[i].SampledPoints[0] = cmsEvalToneCurveFloat(Curve, Curve->Segments[i].x0); + } + } + return Curve; Error: @@ -4146,12 +4279,12 @@ cmsBool WriteSegmentedCurve(cmsIOHANDLER* io, cmsToneCurve* g) if (ActualSeg -> Type == 0) { - // This is a sampled curve + // This is a sampled curve. First point is implicit in the ICC format, but not in our representation if (!_cmsWriteUInt32Number(io, (cmsUInt32Number) cmsSigSampledCurveSeg)) goto Error; if (!_cmsWriteUInt32Number(io, 0)) goto Error; - if (!_cmsWriteUInt32Number(io, ActualSeg -> nGridPoints)) goto Error; + if (!_cmsWriteUInt32Number(io, ActualSeg -> nGridPoints - 1)) goto Error; - for (j=0; j < g ->Segments[i].nGridPoints; j++) { + for (j=1; j < g ->Segments[i].nGridPoints; j++) { if (!_cmsWriteFloat32Number(io, ActualSeg -> SampledPoints[j])) goto Error; } @@ -4347,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; @@ -4528,7 +4661,7 @@ void *Type_MPE_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsU -// This one is a liitle bit more complex, so we don't use position tables this time. +// This one is a little bit more complex, so we don't use position tables this time. static cmsBool Type_MPE_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) { @@ -4960,7 +5093,7 @@ cmsBool ReadOneElem(cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i, cmsUIn if (!_cmsReadUInt32Number(io, &e->Offsets[i])) return FALSE; if (!_cmsReadUInt32Number(io, &e ->Sizes[i])) return FALSE; - // An offset of zero has special meaning and shal be preserved + // An offset of zero has special meaning and shall be preserved if (e ->Offsets[i] > 0) e ->Offsets[i] += BaseOffset; return TRUE; @@ -4968,27 +5101,41 @@ cmsBool ReadOneElem(cmsIOHANDLER* io, _cmsDICelem* e, cmsUInt32Number i, cmsUIn static -cmsBool ReadOffsetArray(cmsIOHANDLER* io, _cmsDICarray* a, cmsUInt32Number Count, cmsUInt32Number Length, cmsUInt32Number BaseOffset) +cmsBool ReadOffsetArray(cmsIOHANDLER* io, _cmsDICarray* a, + cmsUInt32Number Count, cmsUInt32Number Length, cmsUInt32Number BaseOffset, + cmsInt32Number* SignedSizeOfTagPtr) { cmsUInt32Number i; + cmsInt32Number SignedSizeOfTag = *SignedSizeOfTagPtr; // Read column arrays for (i=0; i < Count; i++) { + if (SignedSizeOfTag < 4 * (cmsInt32Number) sizeof(cmsUInt32Number)) return FALSE; + SignedSizeOfTag -= 4 * sizeof(cmsUInt32Number); + if (!ReadOneElem(io, &a -> Name, i, BaseOffset)) return FALSE; if (!ReadOneElem(io, &a -> Value, i, BaseOffset)) return FALSE; if (Length > 16) { + if (SignedSizeOfTag < 2 * (cmsInt32Number) sizeof(cmsUInt32Number)) return FALSE; + SignedSizeOfTag -= 2 * sizeof(cmsUInt32Number); + if (!ReadOneElem(io, &a ->DisplayName, i, BaseOffset)) return FALSE; } if (Length > 24) { + if (SignedSizeOfTag < 2 * (cmsInt32Number) sizeof(cmsUInt32Number)) return FALSE; + SignedSizeOfTag -= 2 * (cmsInt32Number) sizeof(cmsUInt32Number); + if (!ReadOneElem(io, & a -> DisplayValue, i, BaseOffset)) return FALSE; } } + + *SignedSizeOfTagPtr = SignedSizeOfTag; return TRUE; } @@ -5124,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; } @@ -5136,26 +5285,31 @@ cmsBool WriteOneMLUC(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, _c static void *Type_Dictionary_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) { - cmsHANDLE hDict; + cmsHANDLE hDict = NULL; cmsUInt32Number i, Count, Length; cmsUInt32Number BaseOffset; _cmsDICarray a; wchar_t *NameWCS = NULL, *ValueWCS = NULL; cmsMLU *DisplayNameMLU = NULL, *DisplayValueMLU=NULL; cmsBool rc; + cmsInt32Number SignedSizeOfTag = (cmsInt32Number)SizeOfTag; *nItems = 0; + memset(&a, 0, sizeof(a)); // Get actual position as a basis for element offsets BaseOffset = io ->Tell(io) - sizeof(_cmsTagBase); // Get name-value record count + SignedSizeOfTag -= sizeof(cmsUInt32Number); + if (SignedSizeOfTag < 0) goto Error; if (!_cmsReadUInt32Number(io, &Count)) return NULL; - SizeOfTag -= sizeof(cmsUInt32Number); // Get rec length + SignedSizeOfTag -= sizeof(cmsUInt32Number); + if (SignedSizeOfTag < 0) goto Error; if (!_cmsReadUInt32Number(io, &Length)) return NULL; - SizeOfTag -= sizeof(cmsUInt32Number); + // Check for valid lengths if (Length != 16 && Length != 24 && Length != 32) { @@ -5171,7 +5325,7 @@ void *Type_Dictionary_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* i if (!AllocArray(self -> ContextID, &a, Count, Length)) goto Error; // Read column arrays - if (!ReadOffsetArray(io, &a, Count, Length, BaseOffset)) goto Error; + if (!ReadOffsetArray(io, &a, Count, Length, BaseOffset, &SignedSizeOfTag)) goto Error; // Seek to each element and read it for (i=0; i < Count; i++) { @@ -5211,7 +5365,7 @@ void *Type_Dictionary_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* i Error: FreeArray(&a); - cmsDictFree(hDict); + if (hDict != NULL) cmsDictFree(hDict); return NULL; } @@ -5309,6 +5463,274 @@ void Type_Dictionary_Free(struct _cms_typehandler_struct* self, void* Ptr) cmsUNUSED_PARAMETER(self); } +// cicp VideoSignalType + +static +void* Type_VideoSignal_Read(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, cmsUInt32Number* nItems, cmsUInt32Number SizeOfTag) +{ + cmsVideoSignalType* cicp = NULL; + + if (SizeOfTag != 8) return NULL; + + if (!_cmsReadUInt32Number(io, NULL)) return NULL; + + cicp = (cmsVideoSignalType*)_cmsCalloc(self->ContextID, 1, sizeof(cmsVideoSignalType)); + if (cicp == NULL) return NULL; + + if (!_cmsReadUInt8Number(io, &cicp->ColourPrimaries)) goto Error; + if (!_cmsReadUInt8Number(io, &cicp->TransferCharacteristics)) goto Error; + if (!_cmsReadUInt8Number(io, &cicp->MatrixCoefficients)) goto Error; + if (!_cmsReadUInt8Number(io, &cicp->VideoFullRangeFlag)) goto Error; + + // Success + *nItems = 1; + return cicp; + +Error: + if (cicp != NULL) _cmsFree(self->ContextID, cicp); + return NULL; +} + +static +cmsBool Type_VideoSignal_Write(struct _cms_typehandler_struct* self, cmsIOHANDLER* io, void* Ptr, cmsUInt32Number nItems) +{ + cmsVideoSignalType* cicp = (cmsVideoSignalType*)Ptr; + + if (!_cmsWriteUInt32Number(io, 0)) return FALSE; + if (!_cmsWriteUInt8Number(io, cicp->ColourPrimaries)) return FALSE; + if (!_cmsWriteUInt8Number(io, cicp->TransferCharacteristics)) return FALSE; + if (!_cmsWriteUInt8Number(io, cicp->MatrixCoefficients)) return FALSE; + if (!_cmsWriteUInt8Number(io, cicp->VideoFullRangeFlag)) return FALSE; + + return TRUE; + + cmsUNUSED_PARAMETER(self); + cmsUNUSED_PARAMETER(nItems); +} + +void* Type_VideoSignal_Dup(struct _cms_typehandler_struct* self, const void* Ptr, cmsUInt32Number n) +{ + return _cmsDupMem(self->ContextID, Ptr, sizeof(cmsVideoSignalType)); + + cmsUNUSED_PARAMETER(n); +} + + +static +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 @@ -5348,7 +5770,9 @@ static const _cmsTagTypeLinkedList SupportedTagTypes[] = { {TYPE_HANDLER(cmsMonacoBrokenCurveType, Curve), (_cmsTagTypeLinkedList*) &SupportedTagTypes[28] }, {TYPE_HANDLER(cmsSigProfileSequenceIdType, ProfileSequenceId), (_cmsTagTypeLinkedList*) &SupportedTagTypes[29] }, {TYPE_HANDLER(cmsSigDictType, Dictionary), (_cmsTagTypeLinkedList*) &SupportedTagTypes[30] }, -{TYPE_HANDLER(cmsSigVcgtType, vcgt), NULL } +{TYPE_HANDLER(cmsSigcicpType, VideoSignal), (_cmsTagTypeLinkedList*) &SupportedTagTypes[31] }, +{TYPE_HANDLER(cmsSigVcgtType, vcgt), (_cmsTagTypeLinkedList*) &SupportedTagTypes[32] }, +{TYPE_HANDLER(cmsSigMHC2Type, MHC2), NULL } }; @@ -5542,7 +5966,10 @@ static _cmsTagLinkedList SupportedTags[] = { { cmsSigProfileSequenceIdTag, { 1, 1, { cmsSigProfileSequenceIdType}, NULL }, &SupportedTags[62]}, { cmsSigProfileDescriptionMLTag,{ 1, 1, { cmsSigMultiLocalizedUnicodeType}, NULL}, &SupportedTags[63]}, - { cmsSigArgyllArtsTag, { 9, 1, { cmsSigS15Fixed16ArrayType}, NULL}, NULL} + { cmsSigcicpTag, { 1, 1, { cmsSigcicpType}, NULL }, &SupportedTags[64]}, + + { cmsSigArgyllArtsTag, { 9, 1, { cmsSigS15Fixed16ArrayType}, NULL}, &SupportedTags[65]}, + { cmsSigMHC2Tag, { 1, 1, { cmsSigMHC2Type }, NULL}, NULL} }; diff --git a/jdk/src/share/native/sun/java2d/cmm/lcms/cmsvirt.c b/jdk/src/share/native/sun/java2d/cmm/lcms/cmsvirt.c index c185b347efb..e8d18d4ca9f 100644 --- a/jdk/src/share/native/sun/java2d/cmm/lcms/cmsvirt.c +++ b/jdk/src/share/native/sun/java2d/cmm/lcms/cmsvirt.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2020 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"), @@ -143,7 +143,7 @@ cmsHPROFILE CMSEXPORT cmsCreateRGBProfileTHR(cmsContext ContextID, if (!hICC) // can't allocate return NULL; - cmsSetProfileVersion(hICC, 4.3); + cmsSetProfileVersion(hICC, 4.4); cmsSetDeviceClass(hICC, cmsSigDisplayClass); cmsSetColorSpace(hICC, cmsSigRgbData); @@ -264,7 +264,7 @@ cmsHPROFILE CMSEXPORT cmsCreateGrayProfileTHR(cmsContext ContextID, if (!hICC) // can't allocate return NULL; - cmsSetProfileVersion(hICC, 4.3); + cmsSetProfileVersion(hICC, 4.4); cmsSetDeviceClass(hICC, cmsSigDisplayClass); cmsSetColorSpace(hICC, cmsSigGrayData); @@ -320,13 +320,13 @@ cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLinkTHR(cmsContext ContextID, { cmsHPROFILE hICC; cmsPipeline* Pipeline; - cmsUInt32Number nChannels; + cmsInt32Number nChannels; hICC = cmsCreateProfilePlaceholder(ContextID); if (!hICC) return NULL; - cmsSetProfileVersion(hICC, 4.3); + cmsSetProfileVersion(hICC, 4.4); cmsSetDeviceClass(hICC, cmsSigLinkClass); cmsSetColorSpace(hICC, ColorSpace); @@ -335,7 +335,7 @@ cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLinkTHR(cmsContext ContextID, cmsSetHeaderRenderingIntent(hICC, INTENT_PERCEPTUAL); // Set up channels - nChannels = cmsChannelsOf(ColorSpace); + nChannels = cmsChannelsOfColorSpace(ColorSpace); // Creates a Pipeline with prelinearization step only Pipeline = cmsPipelineAlloc(ContextID, nChannels, nChannels); @@ -397,7 +397,7 @@ int InkLimitingSampler(CMSREGISTER const cmsUInt16Number In[], CMSREGISTER cmsUI InkLimit = (InkLimit * 655.35); - SumCMY = In[0] + In[1] + In[2]; + SumCMY = (cmsFloat64Number) In[0] + In[1] + In[2]; SumCMYK = SumCMY + In[3]; if (SumCMYK > InkLimit) { @@ -426,7 +426,7 @@ cmsHPROFILE CMSEXPORT cmsCreateInkLimitingDeviceLinkTHR(cmsContext ContextID, cmsHPROFILE hICC; cmsPipeline* LUT; cmsStage* CLUT; - cmsUInt32Number nChannels; + cmsInt32Number nChannels; if (ColorSpace != cmsSigCmykData) { cmsSignalError(ContextID, cmsERROR_COLORSPACE_CHECK, "InkLimiting: Only CMYK currently supported"); @@ -435,17 +435,16 @@ 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); if (!hICC) // can't allocate return NULL; - cmsSetProfileVersion(hICC, 4.3); + cmsSetProfileVersion(hICC, 4.4); cmsSetDeviceClass(hICC, cmsSigLinkClass); cmsSetColorSpace(hICC, ColorSpace); @@ -555,7 +554,7 @@ cmsHPROFILE CMSEXPORT cmsCreateLab4ProfileTHR(cmsContext ContextID, const cmsCIE hProfile = cmsCreateRGBProfileTHR(ContextID, WhitePoint == NULL ? cmsD50_xyY() : WhitePoint, NULL, NULL); if (hProfile == NULL) return NULL; - cmsSetProfileVersion(hProfile, 4.3); + cmsSetProfileVersion(hProfile, 4.4); cmsSetDeviceClass(hProfile, cmsSigAbstractClass); cmsSetColorSpace(hProfile, cmsSigLabData); @@ -601,7 +600,7 @@ cmsHPROFILE CMSEXPORT cmsCreateXYZProfileTHR(cmsContext ContextID) hProfile = cmsCreateRGBProfileTHR(ContextID, cmsD50_xyY(), NULL, NULL); if (hProfile == NULL) return NULL; - cmsSetProfileVersion(hProfile, 4.3); + cmsSetProfileVersion(hProfile, 4.4); cmsSetDeviceClass(hProfile, cmsSigAbstractClass); cmsSetColorSpace(hProfile, cmsSigXYZData); @@ -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 { @@ -868,7 +988,7 @@ cmsHPROFILE CMSEXPORT cmsCreateNULLProfileTHR(cmsContext ContextID) if (!hProfile) // can't allocate return NULL; - cmsSetProfileVersion(hProfile, 4.3); + cmsSetProfileVersion(hProfile, 4.4); if (!SetTextTags(hProfile, L"NULL profile built-in")) goto Error; @@ -1003,7 +1123,7 @@ cmsHPROFILE CreateNamedColorDevicelink(cmsHTRANSFORM xform) // Make sure we have proper formatters cmsChangeBuffersFormat(xform, TYPE_NAMED_COLOR_INDEX, FLOAT_SH(0) | COLORSPACE_SH(_cmsLCMScolorSpace(v ->ExitColorSpace)) - | BYTES_SH(2) | CHANNELS_SH(cmsChannelsOf(v ->ExitColorSpace))); + | BYTES_SH(2) | CHANNELS_SH(cmsChannelsOfColorSpace(v ->ExitColorSpace))); // Apply the transfor to colorants. for (i=0; i < nColors; i++) { @@ -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,7 +1211,8 @@ const cmsAllowedLUT* FindCombination(const cmsPipeline* Lut, cmsBool IsV4, cmsTa cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat64Number Version, cmsUInt32Number dwFlags) { cmsHPROFILE hProfile = NULL; - cmsUInt32Number FrmIn, FrmOut, ChansIn, ChansOut; + cmsUInt32Number FrmIn, FrmOut; + cmsInt32Number ChansIn, ChansOut; int ColorSpaceBitsIn, ColorSpaceBitsOut; _cmsTRANSFORM* xform = (_cmsTRANSFORM*) hTransform; cmsPipeline* LUT = NULL; @@ -1103,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); @@ -1143,8 +1267,8 @@ cmsHPROFILE CMSEXPORT cmsTransform2DeviceLink(cmsHTRANSFORM hTransform, cmsFloat // Optimize the LUT and precalculate a devicelink - ChansIn = cmsChannelsOf(xform -> EntryColorSpace); - ChansOut = cmsChannelsOf(xform -> ExitColorSpace); + ChansIn = cmsChannelsOfColorSpace(xform -> EntryColorSpace); + ChansOut = cmsChannelsOfColorSpace(xform -> ExitColorSpace); ColorSpaceBitsIn = _cmsLCMScolorSpace(xform -> EntryColorSpace); ColorSpaceBitsOut = _cmsLCMScolorSpace(xform -> ExitColorSpace); diff --git a/jdk/src/share/native/sun/java2d/cmm/lcms/cmswtpnt.c b/jdk/src/share/native/sun/java2d/cmm/lcms/cmswtpnt.c index d93889114e6..27d71803531 100644 --- a/jdk/src/share/native/sun/java2d/cmm/lcms/cmswtpnt.c +++ b/jdk/src/share/native/sun/java2d/cmm/lcms/cmswtpnt.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2020 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"), @@ -244,12 +244,15 @@ cmsBool ComputeChromaticAdaptation(cmsMAT3* Conversion, _cmsMAT3eval(&ConeSourceRGB, Chad, &ConeSourceXYZ); _cmsMAT3eval(&ConeDestRGB, Chad, &ConeDestXYZ); + if ((fabs(ConeSourceRGB.n[0]) < MATRIX_DET_TOLERANCE) || + (fabs(ConeSourceRGB.n[1]) < MATRIX_DET_TOLERANCE) || + (fabs(ConeSourceRGB.n[2]) < MATRIX_DET_TOLERANCE)) return FALSE; + // Build matrix _cmsVEC3init(&Cone.v[0], ConeDestRGB.n[0]/ConeSourceRGB.n[0], 0.0, 0.0); _cmsVEC3init(&Cone.v[1], 0.0, ConeDestRGB.n[1]/ConeSourceRGB.n[1], 0.0); _cmsVEC3init(&Cone.v[2], 0.0, 0.0, ConeDestRGB.n[2]/ConeSourceRGB.n[2]); - // Normalize _cmsMAT3per(&Tmp, &Cone, Chad); _cmsMAT3per(Conversion, &Chad_Inv, &Tmp); @@ -293,16 +296,16 @@ cmsBool _cmsAdaptMatrixToD50(cmsMAT3* r, const cmsCIExyY* SourceWhitePt) // Build a White point, primary chromas transfer matrix from RGB to CIE XYZ // This is just an approximation, I am not handling all the non-linear -// aspects of the RGB to XYZ process, and assumming that the gamma correction +// aspects of the RGB to XYZ process, and assuming that the gamma correction // has transitive property in the transformation chain. // -// the alghoritm: +// the algorithm: // // - First I build the absolute conversion matrix using // primaries in XYZ. This matrix is next inverted // - Then I eval the source white point across this matrix -// obtaining the coeficients of the transformation -// - Then, I apply these coeficients to the original matrix +// obtaining the coefficients of the transformation +// - Then, I apply these coefficients to the original matrix // cmsBool _cmsBuildRGB2XYZtransferMatrix(cmsMAT3* r, const cmsCIExyY* WhitePt, const cmsCIExyYTRIPLE* Primrs) { diff --git a/jdk/src/share/native/sun/java2d/cmm/lcms/cmsxform.c b/jdk/src/share/native/sun/java2d/cmm/lcms/cmsxform.c index 6a2170c8fd2..86afd7202fd 100644 --- a/jdk/src/share/native/sun/java2d/cmm/lcms/cmsxform.c +++ b/jdk/src/share/native/sun/java2d/cmm/lcms/cmsxform.c @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2020 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"), @@ -313,7 +313,7 @@ void FloatXFORM(_cmsTRANSFORM* p, accum = p->FromInputFloat(p, fIn, accum, Stride->BytesPerPlaneIn); - // Any gamut chack to do? + // Any gamut check to do? if (p->GamutCheck != NULL) { // Evaluate gamut marker. @@ -810,6 +810,73 @@ cmsUInt32Number CMSEXPORT _cmsGetTransformFlags(struct _cmstransform_struct* CMM return CMMcargo->dwOriginalFlags; } +// Returns the worker callback for parallelization plug-ins +_cmsTransform2Fn CMSEXPORT _cmsGetTransformWorker(struct _cmstransform_struct* CMMcargo) +{ + _cmsAssert(CMMcargo != NULL); + return CMMcargo->Worker; +} + +// This field holds maximum number of workers or -1 to auto +cmsInt32Number CMSEXPORT _cmsGetTransformMaxWorkers(struct _cmstransform_struct* CMMcargo) +{ + _cmsAssert(CMMcargo != NULL); + return CMMcargo->MaxWorkers; +} + +// This field is actually unused and reserved +cmsUInt32Number CMSEXPORT _cmsGetTransformWorkerFlags(struct _cmstransform_struct* CMMcargo) +{ + _cmsAssert(CMMcargo != NULL); + return CMMcargo->WorkerFlags; +} + +// In the case there is a parallelization plug-in, let it to do its job +static +void ParalellizeIfSuitable(_cmsTRANSFORM* p) +{ + _cmsParallelizationPluginChunkType* ctx = (_cmsParallelizationPluginChunkType*)_cmsContextGetClientChunk(p->ContextID, ParallelizationPlugin); + + _cmsAssert(p != NULL); + if (ctx != NULL && ctx->SchedulerFn != NULL) { + + p->Worker = p->xform; + p->xform = ctx->SchedulerFn; + p->MaxWorkers = ctx->MaxWorkers; + p->WorkerFlags = ctx->WorkerFlags; + } +} + + +/** +* An empty unroll to avoid a check with NULL on cmsDoTransform() +*/ +static +cmsUInt8Number* UnrollNothing(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wIn[], + CMSREGISTER cmsUInt8Number* accum, + CMSREGISTER cmsUInt32Number Stride) +{ + return accum; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(wIn); + cmsUNUSED_PARAMETER(Stride); +} + +static +cmsUInt8Number* PackNothing(CMSREGISTER _cmsTRANSFORM* info, + CMSREGISTER cmsUInt16Number wOut[], + CMSREGISTER cmsUInt8Number* output, + CMSREGISTER cmsUInt32Number Stride) +{ + return output; + + cmsUNUSED_PARAMETER(info); + cmsUNUSED_PARAMETER(wOut); + cmsUNUSED_PARAMETER(Stride); +} + // Allocate transform struct and set it to defaults. Ask the optimization plug-in about if those formats are proper // for separated transforms. If this is the case, static @@ -865,6 +932,7 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut, p->xform = _cmsTransform2toTransformAdaptor; } + ParalellizeIfSuitable(p); return p; } } @@ -875,7 +943,7 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut, } // Check whatever this is a true floating point transform - if (_cmsFormatterIsFloat(*InputFormat) && _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; @@ -901,8 +969,10 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut, } else { + // Formats are intended to be changed before use if (*InputFormat == 0 && *OutputFormat == 0) { - p ->FromInput = p ->ToOutput = NULL; + p->FromInput = UnrollNothing; + p->ToOutput = PackNothing; *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER; } else { @@ -919,7 +989,7 @@ _cmsTRANSFORM* AllocEmptyTransform(cmsContext ContextID, cmsPipeline* lut, return NULL; } - BytesPerPixelInput = T_BYTES(p ->InputFormat); + BytesPerPixelInput = T_BYTES(*InputFormat); if (BytesPerPixelInput == 0 || BytesPerPixelInput >= 2) *dwFlags |= cmsFLAGS_CAN_CHANGE_FORMATTER; @@ -948,11 +1018,25 @@ _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; p ->ContextID = ContextID; p ->UserData = NULL; + ParalellizeIfSuitable(p); return p; } @@ -1110,6 +1194,15 @@ cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID, return NULL; } + // Check whatever the transform is 16 bits and involves linear RGB in first profile. If so, disable optimizations + if (EntryColorSpace == cmsSigRgbData && T_BYTES(InputFormat) == 2 && !(dwFlags & cmsFLAGS_NOOPTIMIZE)) + { + cmsFloat64Number gamma = cmsDetectRGBProfileGamma(hProfiles[0], 0.1); + + if (gamma > 0 && gamma < 1.6) + dwFlags |= cmsFLAGS_NOOPTIMIZE; + } + // Create a pipeline with all transformations Lut = _cmsLinkProfiles(ContextID, nProfiles, Intents, hProfiles, BPC, AdaptationStates, dwFlags); if (Lut == NULL) { @@ -1118,8 +1211,8 @@ cmsHTRANSFORM CMSEXPORT cmsCreateExtendedTransform(cmsContext ContextID, } // Check channel count - if ((cmsChannelsOf(EntryColorSpace) != cmsPipelineInputChannels(Lut)) || - (cmsChannelsOf(ExitColorSpace) != cmsPipelineOutputChannels(Lut))) { + if ((cmsChannelsOfColorSpace(EntryColorSpace) != (cmsInt32Number) cmsPipelineInputChannels(Lut)) || + (cmsChannelsOfColorSpace(ExitColorSpace) != (cmsInt32Number) cmsPipelineOutputChannels(Lut))) { cmsPipelineFree(Lut); cmsSignalError(ContextID, cmsERROR_NOT_SUITABLE, "Channel count doesn't match. Profile is corrupted"); return NULL; diff --git a/jdk/src/share/native/sun/java2d/cmm/lcms/lcms2.h b/jdk/src/share/native/sun/java2d/cmm/lcms/lcms2.h index 69ada9ede64..2d9a8b1248f 100644 --- a/jdk/src/share/native/sun/java2d/cmm/lcms/lcms2.h +++ b/jdk/src/share/native/sun/java2d/cmm/lcms/lcms2.h @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2021 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"), @@ -52,7 +52,7 @@ // //--------------------------------------------------------------------------------- // -// Version 2.12 +// 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 2120 +#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 @@ -181,7 +184,7 @@ typedef double cmsFloat64Number; #endif // Handle "register" keyword -#if defined(CMS_NO_REGISTER_KEYWORD) && !defined(CMS_DLL) && !defined(CMS_DLL_BUILD) +#if defined(CMS_NO_REGISTER_KEYWORD) # define CMSREGISTER #else # define CMSREGISTER register @@ -256,7 +259,7 @@ typedef int cmsBool; // Calling convention -- this is hardly platform and compiler dependent -#ifdef CMS_IS_WINDOWS_ +#if defined(CMS_IS_WINDOWS_) && !defined(__GNUC__) # if defined(CMS_DLL) || defined(CMS_DLL_BUILD) # ifdef __BORLANDC__ # define CMSEXPORT __stdcall _export @@ -319,6 +322,7 @@ typedef int cmsBool; // Base ICC type definitions typedef enum { cmsSigChromaticityType = 0x6368726D, // 'chrm' + cmsSigcicpType = 0x63696370, // 'cicp' cmsSigColorantOrderType = 0x636C726F, // 'clro' cmsSigColorantTableType = 0x636C7274, // 'clrt' cmsSigCrdInfoType = 0x63726469, // 'crdi' @@ -353,7 +357,8 @@ typedef enum { cmsSigUInt8ArrayType = 0x75693038, // 'ui08' cmsSigVcgtType = 0x76636774, // 'vcgt' cmsSigViewingConditionsType = 0x76696577, // 'view' - cmsSigXYZType = 0x58595A20 // 'XYZ ' + cmsSigXYZType = 0x58595A20, // 'XYZ ' + cmsSigMHC2Type = 0x4D484332 // 'MHC2' } cmsTagTypeSignature; @@ -430,7 +435,9 @@ typedef enum { cmsSigViewingConditionsTag = 0x76696577, // 'view' cmsSigVcgtTag = 0x76636774, // 'vcgt' cmsSigMetaTag = 0x6D657461, // 'meta' - cmsSigArgyllArtsTag = 0x61727473 // 'arts' + cmsSigcicpTag = 0x63696370, // 'cicp' + cmsSigArgyllArtsTag = 0x61727473, // 'arts' + cmsSigMHC2Tag = 0x4D484332 // 'MHC2' } cmsTagSignature; @@ -695,9 +702,10 @@ typedef void* cmsHTRANSFORM; // Format of pixel is defined by one cmsUInt32Number, using bit fields as follows // // 2 1 0 -// 3 2 10987 6 5 4 3 2 1 098 7654 321 -// A O TTTTT U Y F P X S EEE CCCC BBB +// 4 3 2 10987 6 5 4 3 2 1 098 7654 321 +// M A O TTTTT U Y F P X S EEE CCCC BBB // +// M: Premultiplied alpha (only works when extra samples is 1) // A: Floating point -- With this flag we can differentiate 16 bits as float and as int // O: Optimized -- previous optimization already returns the final 8-bit value // T: Pixeltype @@ -710,6 +718,7 @@ typedef void* cmsHTRANSFORM; // B: bytes per sample // Y: Swap first - changes ABGR to BGRA and KCMY to CMYK +#define PREMUL_SH(m) ((m) << 23) #define FLOAT_SH(a) ((a) << 22) #define OPTIMIZED_SH(s) ((s) << 21) #define COLORSPACE_SH(s) ((s) << 16) @@ -723,6 +732,7 @@ typedef void* cmsHTRANSFORM; #define BYTES_SH(b) (b) // These macros unpack format specifiers into integers +#define T_PREMUL(m) (((m)>>23)&1) #define T_FLOAT(a) (((a)>>22)&1) #define T_OPTIMIZED(o) (((o)>>21)&1) #define T_COLORSPACE(s) (((s)>>16)&31) @@ -751,7 +761,6 @@ typedef void* cmsHTRANSFORM; #define PT_HSV 12 #define PT_HLS 13 #define PT_Yxy 14 - #define PT_MCH1 15 #define PT_MCH2 16 #define PT_MCH3 17 @@ -767,7 +776,6 @@ typedef void* cmsHTRANSFORM; #define PT_MCH13 27 #define PT_MCH14 28 #define PT_MCH15 29 - #define PT_LabV2 30 // Identical to PT_Lab, but using the V2 old encoding // Some (not all!) representations @@ -781,7 +789,9 @@ typedef void* cmsHTRANSFORM; #define TYPE_GRAY_16_REV (COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(2)|FLAVOR_SH(1)) #define TYPE_GRAY_16_SE (COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(2)|ENDIAN16_SH(1)) #define TYPE_GRAYA_8 (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(1)) +#define TYPE_GRAYA_8_PREMUL (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(1)|PREMUL_SH(1)) #define TYPE_GRAYA_16 (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(2)) +#define TYPE_GRAYA_16_PREMUL (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(2)|PREMUL_SH(1)) #define TYPE_GRAYA_16_SE (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(2)|ENDIAN16_SH(1)) #define TYPE_GRAYA_8_PLANAR (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(1)|PLANAR_SH(1)) #define TYPE_GRAYA_16_PLANAR (COLORSPACE_SH(PT_GRAY)|EXTRA_SH(1)|CHANNELS_SH(1)|BYTES_SH(2)|PLANAR_SH(1)) @@ -798,24 +808,32 @@ typedef void* cmsHTRANSFORM; #define TYPE_BGR_16_SE (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) #define TYPE_RGBA_8 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)) +#define TYPE_RGBA_8_PREMUL (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|PREMUL_SH(1)) #define TYPE_RGBA_8_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|PLANAR_SH(1)) #define TYPE_RGBA_16 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)) +#define TYPE_RGBA_16_PREMUL (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|PREMUL_SH(1)) #define TYPE_RGBA_16_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) #define TYPE_RGBA_16_SE (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) #define TYPE_ARGB_8 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_ARGB_8_PREMUL (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|SWAPFIRST_SH(1)|PREMUL_SH(1)) #define TYPE_ARGB_8_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|SWAPFIRST_SH(1)|PLANAR_SH(1)) #define TYPE_ARGB_16 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|SWAPFIRST_SH(1)) +#define TYPE_ARGB_16_PREMUL (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|SWAPFIRST_SH(1)|PREMUL_SH(1)) #define TYPE_ABGR_8 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)) +#define TYPE_ABGR_8_PREMUL (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|PREMUL_SH(1)) #define TYPE_ABGR_8_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|PLANAR_SH(1)) #define TYPE_ABGR_16 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)) +#define TYPE_ABGR_16_PREMUL (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|PREMUL_SH(1)) #define TYPE_ABGR_16_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|PLANAR_SH(1)) #define TYPE_ABGR_16_SE (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|ENDIAN16_SH(1)) #define TYPE_BGRA_8 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_BGRA_8_PREMUL (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1)|PREMUL_SH(1)) #define TYPE_BGRA_8_PLANAR (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1)|PLANAR_SH(1)) #define TYPE_BGRA_16 (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_BGRA_16_PREMUL (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|DOSWAP_SH(1)|SWAPFIRST_SH(1)|PREMUL_SH(1)) #define TYPE_BGRA_16_SE (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)|DOSWAP_SH(1)|SWAPFIRST_SH(1)) #define TYPE_CMY_8 (COLORSPACE_SH(PT_CMY)|CHANNELS_SH(3)|BYTES_SH(1)) @@ -932,7 +950,7 @@ typedef void* cmsHTRANSFORM; #define TYPE_HSV_16_PLANAR (COLORSPACE_SH(PT_HSV)|CHANNELS_SH(3)|BYTES_SH(2)|PLANAR_SH(1)) #define TYPE_HSV_16_SE (COLORSPACE_SH(PT_HSV)|CHANNELS_SH(3)|BYTES_SH(2)|ENDIAN16_SH(1)) -// Named color index. Only 16 bits allowed (don't check colorspace) +// Named color index. Only 16 bits is allowed (don't check colorspace) #define TYPE_NAMED_COLOR_INDEX (CHANNELS_SH(1)|BYTES_SH(2)) // Float formatters. @@ -940,13 +958,19 @@ typedef void* cmsHTRANSFORM; #define TYPE_Lab_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_Lab)|CHANNELS_SH(3)|BYTES_SH(4)) #define TYPE_LabA_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_Lab)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)) #define TYPE_GRAY_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(4)) +#define TYPE_GRAYA_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(4)|EXTRA_SH(1)) +#define TYPE_GRAYA_FLT_PREMUL (FLOAT_SH(1)|COLORSPACE_SH(PT_GRAY)|CHANNELS_SH(1)|BYTES_SH(4)|EXTRA_SH(1)|PREMUL_SH(1)) #define TYPE_RGB_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(4)) #define TYPE_RGBA_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)) +#define TYPE_RGBA_FLT_PREMUL (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|PREMUL_SH(1)) #define TYPE_ARGB_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|SWAPFIRST_SH(1)) +#define TYPE_ARGB_FLT_PREMUL (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|SWAPFIRST_SH(1)|PREMUL_SH(1)) #define TYPE_BGR_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(4)|DOSWAP_SH(1)) #define TYPE_BGRA_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|DOSWAP_SH(1)|SWAPFIRST_SH(1)) +#define TYPE_BGRA_FLT_PREMUL (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|DOSWAP_SH(1)|SWAPFIRST_SH(1)|PREMUL_SH(1)) #define TYPE_ABGR_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|DOSWAP_SH(1)) +#define TYPE_ABGR_FLT_PREMUL (FLOAT_SH(1)|COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|DOSWAP_SH(1)|PREMUL_SH(1)) #define TYPE_CMYK_FLT (FLOAT_SH(1)|COLORSPACE_SH(PT_CMYK)|CHANNELS_SH(4)|BYTES_SH(4)) @@ -958,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)) @@ -1050,6 +1075,29 @@ typedef struct { } cmsICCViewingConditions; +typedef struct { + cmsUInt8Number ColourPrimaries; // Recommendation ITU-T H.273 + cmsUInt8Number TransferCharacteristics; // (ISO/IEC 23091-2) + cmsUInt8Number MatrixCoefficients; + cmsUInt8Number VideoFullRangeFlag; + +} 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) ----------------------------------------------------------------------------- CMSAPI int CMSEXPORT cmsGetEncodedCMMversion(void); @@ -1220,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); @@ -1285,6 +1334,7 @@ CMSAPI cmsUInt32Number CMSEXPORT cmsStageInputChannels(const cmsStage* mpe); CMSAPI cmsUInt32Number CMSEXPORT cmsStageOutputChannels(const cmsStage* mpe); CMSAPI cmsStageSignature CMSEXPORT cmsStageType(const cmsStage* mpe); CMSAPI void* CMSEXPORT cmsStageData(const cmsStage* mpe); +CMSAPI cmsContext CMSEXPORT cmsGetStageContextID(const cmsStage* mpe); // Sampling typedef cmsInt32Number (* cmsSAMPLER16) (CMSREGISTER const cmsUInt16Number In[], @@ -1313,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); @@ -1326,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], @@ -1334,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], @@ -1531,8 +1591,12 @@ CMSAPI cmsBool CMSEXPORT cmsIsCLUT(cmsHPROFILE hProfile, cmsUInt32Numb CMSAPI cmsColorSpaceSignature CMSEXPORT _cmsICCcolorSpace(int OurNotation); CMSAPI int CMSEXPORT _cmsLCMScolorSpace(cmsColorSpaceSignature ProfileSpace); +// Deprecated, use cmsChannelsOfColorSpace instead CMSAPI cmsUInt32Number CMSEXPORT cmsChannelsOf(cmsColorSpaceSignature ColorSpace); +// Get number of channels of color space or -1 if color space is not listed/supported +CMSAPI cmsInt32Number CMSEXPORT cmsChannelsOfColorSpace(cmsColorSpaceSignature ColorSpace); + // Build a suitable formatter for the colorspace of this profile. nBytes=1 means 8 bits, nBytes=2 means 16 bits. CMSAPI cmsUInt32Number CMSEXPORT cmsFormatterForColorspaceOfProfile(cmsHPROFILE hProfile, cmsUInt32Number nBytes, cmsBool lIsFloat); CMSAPI cmsUInt32Number CMSEXPORT cmsFormatterForPCSOfProfile(cmsHPROFILE hProfile, cmsUInt32Number nBytes, cmsBool lIsFloat); @@ -1554,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; @@ -1616,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); @@ -1628,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, @@ -1935,6 +2008,8 @@ CMSAPI cmsBool CMSEXPORT cmsDetectDestinationBlackPoint(cmsCIEXYZ* Blac // Estimate total area coverage CMSAPI cmsFloat64Number CMSEXPORT cmsDetectTAC(cmsHPROFILE hProfile); +// Estimate gamma space, always positive. Returns -1 on error. +CMSAPI cmsFloat64Number CMSEXPORT cmsDetectRGBProfileGamma(cmsHPROFILE hProfile, cmsFloat64Number threshold); // Poor man's gamut mapping CMSAPI cmsBool CMSEXPORT cmsDesaturateLab(cmsCIELab* Lab, diff --git a/jdk/src/share/native/sun/java2d/cmm/lcms/lcms2_internal.h b/jdk/src/share/native/sun/java2d/cmm/lcms/lcms2_internal.h index d1c78208b82..75973edad0d 100644 --- a/jdk/src/share/native/sun/java2d/cmm/lcms/lcms2_internal.h +++ b/jdk/src/share/native/sun/java2d/cmm/lcms/lcms2_internal.h @@ -27,10 +27,9 @@ // However, the following notice accompanied the original version of this // file: // - // // Little Color Management System -// Copyright (c) 1998-2020 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"), @@ -289,6 +288,7 @@ typedef CRITICAL_SECTION _cmsMutex; #ifdef _MSC_VER # if (_MSC_VER >= 1800) # pragma warning(disable : 26135) +# pragma warning(disable : 4127) # endif #endif @@ -312,38 +312,38 @@ typedef CRITICAL_SECTION _cmsMutex; cmsINLINE int _cmsLockPrimitive(_cmsMutex *m) { - EnterCriticalSection(m); - return 0; + EnterCriticalSection(m); + return 0; } cmsINLINE int _cmsUnlockPrimitive(_cmsMutex *m) { - LeaveCriticalSection(m); - return 0; + LeaveCriticalSection(m); + return 0; } cmsINLINE int _cmsInitMutexPrimitive(_cmsMutex *m) { - InitializeCriticalSection(m); - return 0; + InitializeCriticalSection(m); + return 0; } cmsINLINE int _cmsDestroyMutexPrimitive(_cmsMutex *m) { - DeleteCriticalSection(m); - return 0; + DeleteCriticalSection(m); + return 0; } cmsINLINE int _cmsEnterCriticalSectionPrimitive(_cmsMutex *m) { - EnterCriticalSection(m); - return 0; + EnterCriticalSection(m); + return 0; } cmsINLINE int _cmsLeaveCriticalSectionPrimitive(_cmsMutex *m) { - LeaveCriticalSection(m); - return 0; + LeaveCriticalSection(m); + return 0; } #else @@ -357,32 +357,32 @@ typedef pthread_mutex_t _cmsMutex; cmsINLINE int _cmsLockPrimitive(_cmsMutex *m) { - return pthread_mutex_lock(m); + return pthread_mutex_lock(m); } cmsINLINE int _cmsUnlockPrimitive(_cmsMutex *m) { - return pthread_mutex_unlock(m); + return pthread_mutex_unlock(m); } cmsINLINE int _cmsInitMutexPrimitive(_cmsMutex *m) { - return pthread_mutex_init(m, NULL); + return pthread_mutex_init(m, NULL); } cmsINLINE int _cmsDestroyMutexPrimitive(_cmsMutex *m) { - return pthread_mutex_destroy(m); + return pthread_mutex_destroy(m); } cmsINLINE int _cmsEnterCriticalSectionPrimitive(_cmsMutex *m) { - return pthread_mutex_lock(m); + return pthread_mutex_lock(m); } cmsINLINE int _cmsLeaveCriticalSectionPrimitive(_cmsMutex *m) { - return pthread_mutex_unlock(m); + return pthread_mutex_unlock(m); } #endif @@ -395,37 +395,37 @@ typedef int _cmsMutex; cmsINLINE int _cmsLockPrimitive(_cmsMutex *m) { cmsUNUSED_PARAMETER(m); - return 0; + return 0; } cmsINLINE int _cmsUnlockPrimitive(_cmsMutex *m) { cmsUNUSED_PARAMETER(m); - return 0; + return 0; } cmsINLINE int _cmsInitMutexPrimitive(_cmsMutex *m) { cmsUNUSED_PARAMETER(m); - return 0; + return 0; } cmsINLINE int _cmsDestroyMutexPrimitive(_cmsMutex *m) { cmsUNUSED_PARAMETER(m); - return 0; + return 0; } cmsINLINE int _cmsEnterCriticalSectionPrimitive(_cmsMutex *m) { cmsUNUSED_PARAMETER(m); - return 0; + return 0; } cmsINLINE int _cmsLeaveCriticalSectionPrimitive(_cmsMutex *m) { cmsUNUSED_PARAMETER(m); - return 0; + return 0; } #endif @@ -467,6 +467,9 @@ cmsBool _cmsRegisterTransformPlugin(cmsContext ContextID, cmsPluginBase* Plugin // Mutex cmsBool _cmsRegisterMutexPlugin(cmsContext ContextID, cmsPluginBase* Plugin); +// Paralellization +cmsBool _cmsRegisterParallelizationPlugin(cmsContext ContextID, cmsPluginBase* Plugin); + // --------------------------------------------------------------------------------------------------------- // Suballocators. @@ -514,6 +517,7 @@ typedef enum { OptimizationPlugin, TransformPlugin, MutexPlugin, + ParallelizationPlugin, // Last in list MemoryClientMax @@ -542,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 @@ -749,6 +753,24 @@ extern _cmsMutexPluginChunkType _cmsMutexPluginChunk; void _cmsAllocMutexPluginChunk(struct _cmsContext_struct* ctx, const struct _cmsContext_struct* src); +// Container for parallelization plug-in +typedef struct { + + cmsInt32Number MaxWorkers; // Number of workers to do as maximum + cmsInt32Number WorkerFlags; // reserved + _cmsTransform2Fn SchedulerFn; // callback to setup functions + +} _cmsParallelizationPluginChunkType; + +// The global Context0 storage for parallelization plug-in +extern _cmsParallelizationPluginChunkType _cmsParallelizationPluginChunk; + +// Allocate parallelization container. +void _cmsAllocParallelizationPluginChunk(struct _cmsContext_struct* ctx, + const struct _cmsContext_struct* src); + + + // ---------------------------------------------------------------------------------- // MLU internal representation typedef struct { @@ -818,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; @@ -825,6 +850,7 @@ typedef struct _cms_iccprofile_struct { cmsColorSpaceSignature PCS; cmsUInt32Number RenderingIntent; + cmsPlatformSignature platform; cmsUInt32Number flags; cmsUInt32Number manufacturer, model; cmsUInt64Number attributes; @@ -1110,6 +1136,11 @@ typedef struct _cmstransform_struct { // A way to provide backwards compatibility with full xform plugins _cmsTransformFn OldXform; + // A one-worker transform entry for parallelization + _cmsTransform2Fn Worker; + cmsInt32Number MaxWorkers; + cmsUInt32Number WorkerFlags; + } _cmsTRANSFORM; // Copies extra channels from input to output if the original flags in the transform structure @@ -1147,5 +1178,8 @@ cmsBool _cmsAdaptationMatrix(cmsMAT3* r, const cmsMAT3* ConeMatrix, const cmsC cmsBool _cmsBuildRGB2XYZtransferMatrix(cmsMAT3* r, const cmsCIExyY* WhitePoint, const cmsCIExyYTRIPLE* Primaries); +// thread-safe gettime +cmsBool _cmsGetTime(struct tm* ptr_time); + #define _lcms_internal_H #endif diff --git a/jdk/src/share/native/sun/java2d/cmm/lcms/lcms2_plugin.h b/jdk/src/share/native/sun/java2d/cmm/lcms/lcms2_plugin.h index 61e963ff5f8..e7e7bd1f0ce 100644 --- a/jdk/src/share/native/sun/java2d/cmm/lcms/lcms2_plugin.h +++ b/jdk/src/share/native/sun/java2d/cmm/lcms/lcms2_plugin.h @@ -30,7 +30,7 @@ //--------------------------------------------------------------------------------- // // Little Color Management System -// Copyright (c) 1998-2020 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"), @@ -53,7 +53,7 @@ //--------------------------------------------------------------------------------- // // This is the plug-in header file. Normal LittleCMS clients should not use it. -// It is provided for plug-in writters that may want to access the support +// It is provided for plug-in writers that may want to access the support // functions to do low level operations. All plug-in related structures // are defined here. Including this file forces to include the standard API too. @@ -238,6 +238,7 @@ typedef void* (* _cmsDupUserDataFn)(cmsContext ContextID, const void* Data); #define cmsPluginOptimizationSig 0x6F707448 // 'optH' #define cmsPluginTransformSig 0x7A666D48 // 'xfmH' #define cmsPluginMutexSig 0x6D747A48 // 'mtxH' +#define cmsPluginParalellizationSig 0x70726C48 // 'prlH typedef struct _cmsPluginBaseStruct { @@ -625,7 +626,7 @@ typedef void (* _cmsTransformFn)(struct _cmstransform_struct *CMMcargo, // const void* InputBuffer, void* OutputBuffer, cmsUInt32Number Size, - cmsUInt32Number Stride); // Stride in bytes to the next plana in planar formats + cmsUInt32Number Stride); // Stride in bytes to the next plane in planar formats typedef void (*_cmsTransform2Fn)(struct _cmstransform_struct *CMMcargo, @@ -698,6 +699,25 @@ CMSAPI void CMSEXPORT _cmsDestroyMutex(cmsContext ContextID, void* mtx); CMSAPI cmsBool CMSEXPORT _cmsLockMutex(cmsContext ContextID, void* mtx); CMSAPI void CMSEXPORT _cmsUnlockMutex(cmsContext ContextID, void* mtx); +//---------------------------------------------------------------------------------------------------------- +// Parallelization + +CMSAPI _cmsTransform2Fn CMSEXPORT _cmsGetTransformWorker(struct _cmstransform_struct* CMMcargo); +CMSAPI cmsInt32Number CMSEXPORT _cmsGetTransformMaxWorkers(struct _cmstransform_struct* CMMcargo); +CMSAPI cmsUInt32Number CMSEXPORT _cmsGetTransformWorkerFlags(struct _cmstransform_struct* CMMcargo); + +// Let's plug-in to guess the best number of workers +#define CMS_GUESS_MAX_WORKERS -1 + +typedef struct { + cmsPluginBase base; + + cmsInt32Number MaxWorkers; // Number of starts to do as maximum + cmsUInt32Number WorkerFlags; // Reserved + _cmsTransform2Fn SchedulerFn; // callback to setup functions + +} cmsPluginParalellization; + #ifndef CMS_USE_CPP_API # ifdef __cplusplus