Path: blob/master/src/java.desktop/share/native/liblcms/cmsnamed.c
41152 views
/*1* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.2*3* This code is free software; you can redistribute it and/or modify it4* under the terms of the GNU General Public License version 2 only, as5* published by the Free Software Foundation. Oracle designates this6* particular file as subject to the "Classpath" exception as provided7* by Oracle in the LICENSE file that accompanied this code.8*9* This code is distributed in the hope that it will be useful, but WITHOUT10* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or11* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License12* version 2 for more details (a copy is included in the LICENSE file that13* accompanied this code).14*15* You should have received a copy of the GNU General Public License version16* 2 along with this work; if not, write to the Free Software Foundation,17* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.18*19* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA20* or visit www.oracle.com if you need additional information or have any21* questions.22*/2324// This file is available under and governed by the GNU General Public25// License version 2 only, as published by the Free Software Foundation.26// However, the following notice accompanied the original version of this27// file:28//29//---------------------------------------------------------------------------------30//31// Little Color Management System32// Copyright (c) 1998-2020 Marti Maria Saguer33//34// Permission is hereby granted, free of charge, to any person obtaining35// a copy of this software and associated documentation files (the "Software"),36// to deal in the Software without restriction, including without limitation37// the rights to use, copy, modify, merge, publish, distribute, sublicense,38// and/or sell copies of the Software, and to permit persons to whom the Software39// is furnished to do so, subject to the following conditions:40//41// The above copyright notice and this permission notice shall be included in42// all copies or substantial portions of the Software.43//44// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,45// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO46// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND47// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE48// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION49// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION50// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.51//52//---------------------------------------------------------------------------------53//5455#include "lcms2_internal.h"5657// Multilocalized unicode objects. That is an attempt to encapsulate i18n.585960// Allocates an empty multi localizad unicode object61cmsMLU* CMSEXPORT cmsMLUalloc(cmsContext ContextID, cmsUInt32Number nItems)62{63cmsMLU* mlu;6465// nItems should be positive if given66if (nItems <= 0) nItems = 2;6768// Create the container69mlu = (cmsMLU*) _cmsMallocZero(ContextID, sizeof(cmsMLU));70if (mlu == NULL) return NULL;7172mlu ->ContextID = ContextID;7374// Create entry array75mlu ->Entries = (_cmsMLUentry*) _cmsCalloc(ContextID, nItems, sizeof(_cmsMLUentry));76if (mlu ->Entries == NULL) {77_cmsFree(ContextID, mlu);78return NULL;79}8081// Ok, keep indexes up to date82mlu ->AllocatedEntries = nItems;83mlu ->UsedEntries = 0;8485return mlu;86}878889// Grows a mempool table for a MLU. Each time this function is called, mempool size is multiplied times two.90static91cmsBool GrowMLUpool(cmsMLU* mlu)92{93cmsUInt32Number size;94void *NewPtr;9596// Sanity check97if (mlu == NULL) return FALSE;9899if (mlu ->PoolSize == 0)100size = 256;101else102size = mlu ->PoolSize * 2;103104// Check for overflow105if (size < mlu ->PoolSize) return FALSE;106107// Reallocate the pool108NewPtr = _cmsRealloc(mlu ->ContextID, mlu ->MemPool, size);109if (NewPtr == NULL) return FALSE;110111112mlu ->MemPool = NewPtr;113mlu ->PoolSize = size;114115return TRUE;116}117118119// Grows a entry table for a MLU. Each time this function is called, table size is multiplied times two.120static121cmsBool GrowMLUtable(cmsMLU* mlu)122{123cmsUInt32Number AllocatedEntries;124_cmsMLUentry *NewPtr;125126// Sanity check127if (mlu == NULL) return FALSE;128129AllocatedEntries = mlu ->AllocatedEntries * 2;130131// Check for overflow132if (AllocatedEntries / 2 != mlu ->AllocatedEntries) return FALSE;133134// Reallocate the memory135NewPtr = (_cmsMLUentry*)_cmsRealloc(mlu ->ContextID, mlu ->Entries, AllocatedEntries*sizeof(_cmsMLUentry));136if (NewPtr == NULL) return FALSE;137138mlu ->Entries = NewPtr;139mlu ->AllocatedEntries = AllocatedEntries;140141return TRUE;142}143144145// Search for a specific entry in the structure. Language and Country are used.146static147int SearchMLUEntry(cmsMLU* mlu, cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode)148{149cmsUInt32Number i;150151// Sanity check152if (mlu == NULL) return -1;153154// Iterate whole table155for (i=0; i < mlu ->UsedEntries; i++) {156157if (mlu ->Entries[i].Country == CountryCode &&158mlu ->Entries[i].Language == LanguageCode) return (int) i;159}160161// Not found162return -1;163}164165// Add a block of characters to the intended MLU. Language and country are specified.166// Only one entry for Language/country pair is allowed.167static168cmsBool AddMLUBlock(cmsMLU* mlu, cmsUInt32Number size, const wchar_t *Block,169cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode)170{171cmsUInt32Number Offset;172cmsUInt8Number* Ptr;173174// Sanity check175if (mlu == NULL) return FALSE;176177// Is there any room available?178if (mlu ->UsedEntries >= mlu ->AllocatedEntries) {179if (!GrowMLUtable(mlu)) return FALSE;180}181182// Only one ASCII string183if (SearchMLUEntry(mlu, LanguageCode, CountryCode) >= 0) return FALSE; // Only one is allowed!184185// Check for size186while ((mlu ->PoolSize - mlu ->PoolUsed) < size) {187188if (!GrowMLUpool(mlu)) return FALSE;189}190191Offset = mlu ->PoolUsed;192193Ptr = (cmsUInt8Number*) mlu ->MemPool;194if (Ptr == NULL) return FALSE;195196// Set the entry197memmove(Ptr + Offset, Block, size);198mlu ->PoolUsed += size;199200mlu ->Entries[mlu ->UsedEntries].StrW = Offset;201mlu ->Entries[mlu ->UsedEntries].Len = size;202mlu ->Entries[mlu ->UsedEntries].Country = CountryCode;203mlu ->Entries[mlu ->UsedEntries].Language = LanguageCode;204mlu ->UsedEntries++;205206return TRUE;207}208209// Convert from a 3-char code to a cmsUInt16Number. It is done in this way because some210// compilers don't properly align beginning of strings211static212cmsUInt16Number strTo16(const char str[3])213{214const cmsUInt8Number* ptr8;215cmsUInt16Number n;216217// For non-existent strings218if (str == NULL) return 0;219ptr8 = (const cmsUInt8Number*)str;220n = (cmsUInt16Number)(((cmsUInt16Number)ptr8[0] << 8) | ptr8[1]);221222return n;223}224225static226void strFrom16(char str[3], cmsUInt16Number n)227{228str[0] = (char)(n >> 8);229str[1] = (char)n;230str[2] = (char)0;231232}233234// Add an ASCII entry. Do not add any \0 termination (ICC1v43_2010-12.pdf page 61)235// In the case the user explicitely sets an empty string, we force a \0236cmsBool CMSEXPORT cmsMLUsetASCII(cmsMLU* mlu, const char LanguageCode[3], const char CountryCode[3], const char* ASCIIString)237{238cmsUInt32Number i, len = (cmsUInt32Number) strlen(ASCIIString);239wchar_t* WStr;240cmsBool rc;241cmsUInt16Number Lang = strTo16(LanguageCode);242cmsUInt16Number Cntry = strTo16(CountryCode);243244if (mlu == NULL) return FALSE;245246// len == 0 would prevent operation, so we set a empty string pointing to zero247if (len == 0)248{249len = 1;250}251252WStr = (wchar_t*) _cmsCalloc(mlu ->ContextID, len, sizeof(wchar_t));253if (WStr == NULL) return FALSE;254255for (i=0; i < len; i++)256WStr[i] = (wchar_t) ASCIIString[i];257258rc = AddMLUBlock(mlu, len * sizeof(wchar_t), WStr, Lang, Cntry);259260_cmsFree(mlu ->ContextID, WStr);261return rc;262263}264265// We don't need any wcs support library266static267cmsUInt32Number mywcslen(const wchar_t *s)268{269const wchar_t *p;270271p = s;272while (*p)273p++;274275return (cmsUInt32Number)(p - s);276}277278// Add a wide entry. Do not add any \0 terminator (ICC1v43_2010-12.pdf page 61)279cmsBool CMSEXPORT cmsMLUsetWide(cmsMLU* mlu, const char Language[3], const char Country[3], const wchar_t* WideString)280{281cmsUInt16Number Lang = strTo16(Language);282cmsUInt16Number Cntry = strTo16(Country);283cmsUInt32Number len;284285if (mlu == NULL) return FALSE;286if (WideString == NULL) return FALSE;287288len = (cmsUInt32Number) (mywcslen(WideString)) * sizeof(wchar_t);289if (len == 0)290len = sizeof(wchar_t);291292return AddMLUBlock(mlu, len, WideString, Lang, Cntry);293}294295// Duplicating a MLU is as easy as copying all members296cmsMLU* CMSEXPORT cmsMLUdup(const cmsMLU* mlu)297{298cmsMLU* NewMlu = NULL;299300// Duplicating a NULL obtains a NULL301if (mlu == NULL) return NULL;302303NewMlu = cmsMLUalloc(mlu ->ContextID, mlu ->UsedEntries);304if (NewMlu == NULL) return NULL;305306// Should never happen307if (NewMlu ->AllocatedEntries < mlu ->UsedEntries)308goto Error;309310// Sanitize...311if (NewMlu ->Entries == NULL || mlu ->Entries == NULL) goto Error;312313memmove(NewMlu ->Entries, mlu ->Entries, mlu ->UsedEntries * sizeof(_cmsMLUentry));314NewMlu ->UsedEntries = mlu ->UsedEntries;315316// The MLU may be empty317if (mlu ->PoolUsed == 0) {318NewMlu ->MemPool = NULL;319}320else {321// It is not empty322NewMlu ->MemPool = _cmsMalloc(mlu ->ContextID, mlu ->PoolUsed);323if (NewMlu ->MemPool == NULL) goto Error;324}325326NewMlu ->PoolSize = mlu ->PoolUsed;327328if (NewMlu ->MemPool == NULL || mlu ->MemPool == NULL) goto Error;329330memmove(NewMlu ->MemPool, mlu->MemPool, mlu ->PoolUsed);331NewMlu ->PoolUsed = mlu ->PoolUsed;332333return NewMlu;334335Error:336337if (NewMlu != NULL) cmsMLUfree(NewMlu);338return NULL;339}340341// Free any used memory342void CMSEXPORT cmsMLUfree(cmsMLU* mlu)343{344if (mlu) {345346if (mlu -> Entries) _cmsFree(mlu ->ContextID, mlu->Entries);347if (mlu -> MemPool) _cmsFree(mlu ->ContextID, mlu->MemPool);348349_cmsFree(mlu ->ContextID, mlu);350}351}352353354// The algorithm first searches for an exact match of country and language, if not found it uses355// the Language. If none is found, first entry is used instead.356static357const wchar_t* _cmsMLUgetWide(const cmsMLU* mlu,358cmsUInt32Number *len,359cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode,360cmsUInt16Number* UsedLanguageCode, cmsUInt16Number* UsedCountryCode)361{362cmsUInt32Number i;363int Best = -1;364_cmsMLUentry* v;365366if (mlu == NULL) return NULL;367368if (mlu -> AllocatedEntries <= 0) return NULL;369370for (i=0; i < mlu ->UsedEntries; i++) {371372v = mlu ->Entries + i;373374if (v -> Language == LanguageCode) {375376if (Best == -1) Best = (int) i;377378if (v -> Country == CountryCode) {379380if (UsedLanguageCode != NULL) *UsedLanguageCode = v ->Language;381if (UsedCountryCode != NULL) *UsedCountryCode = v ->Country;382383if (len != NULL) *len = v ->Len;384385return (wchar_t*) ((cmsUInt8Number*) mlu ->MemPool + v -> StrW); // Found exact match386}387}388}389390// No string found. Return First one391if (Best == -1)392Best = 0;393394v = mlu ->Entries + Best;395396if (UsedLanguageCode != NULL) *UsedLanguageCode = v ->Language;397if (UsedCountryCode != NULL) *UsedCountryCode = v ->Country;398399if (len != NULL) *len = v ->Len;400401return(wchar_t*) ((cmsUInt8Number*) mlu ->MemPool + v ->StrW);402}403404405// Obtain an ASCII representation of the wide string. Setting buffer to NULL returns the len406cmsUInt32Number CMSEXPORT cmsMLUgetASCII(const cmsMLU* mlu,407const char LanguageCode[3], const char CountryCode[3],408char* Buffer, cmsUInt32Number BufferSize)409{410const wchar_t *Wide;411cmsUInt32Number StrLen = 0;412cmsUInt32Number ASCIIlen, i;413414cmsUInt16Number Lang = strTo16(LanguageCode);415cmsUInt16Number Cntry = strTo16(CountryCode);416417// Sanitize418if (mlu == NULL) return 0;419420// Get WideChar421Wide = _cmsMLUgetWide(mlu, &StrLen, Lang, Cntry, NULL, NULL);422if (Wide == NULL) return 0;423424ASCIIlen = StrLen / sizeof(wchar_t);425426// Maybe we want only to know the len?427if (Buffer == NULL) return ASCIIlen + 1; // Note the zero at the end428429// No buffer size means no data430if (BufferSize <= 0) return 0;431432// Some clipping may be required433if (BufferSize < ASCIIlen + 1)434ASCIIlen = BufferSize - 1;435436// Precess each character437for (i=0; i < ASCIIlen; i++) {438439if (Wide[i] == 0)440Buffer[i] = 0;441else442Buffer[i] = (char) Wide[i];443}444445// We put a termination "\0"446Buffer[ASCIIlen] = 0;447return ASCIIlen + 1;448}449450// Obtain a wide representation of the MLU, on depending on current locale settings451cmsUInt32Number CMSEXPORT cmsMLUgetWide(const cmsMLU* mlu,452const char LanguageCode[3], const char CountryCode[3],453wchar_t* Buffer, cmsUInt32Number BufferSize)454{455const wchar_t *Wide;456cmsUInt32Number StrLen = 0;457458cmsUInt16Number Lang = strTo16(LanguageCode);459cmsUInt16Number Cntry = strTo16(CountryCode);460461// Sanitize462if (mlu == NULL) return 0;463464Wide = _cmsMLUgetWide(mlu, &StrLen, Lang, Cntry, NULL, NULL);465if (Wide == NULL) return 0;466467// Maybe we want only to know the len?468if (Buffer == NULL) return StrLen + sizeof(wchar_t);469470// No buffer size means no data471if (BufferSize <= 0) return 0;472473// Some clipping may be required474if (BufferSize < StrLen + sizeof(wchar_t))475StrLen = BufferSize - + sizeof(wchar_t);476477memmove(Buffer, Wide, StrLen);478Buffer[StrLen / sizeof(wchar_t)] = 0;479480return StrLen + sizeof(wchar_t);481}482483484// Get also the language and country485CMSAPI cmsBool CMSEXPORT cmsMLUgetTranslation(const cmsMLU* mlu,486const char LanguageCode[3], const char CountryCode[3],487char ObtainedLanguage[3], char ObtainedCountry[3])488{489const wchar_t *Wide;490491cmsUInt16Number Lang = strTo16(LanguageCode);492cmsUInt16Number Cntry = strTo16(CountryCode);493cmsUInt16Number ObtLang, ObtCode;494495// Sanitize496if (mlu == NULL) return FALSE;497498Wide = _cmsMLUgetWide(mlu, NULL, Lang, Cntry, &ObtLang, &ObtCode);499if (Wide == NULL) return FALSE;500501// Get used language and code502strFrom16(ObtainedLanguage, ObtLang);503strFrom16(ObtainedCountry, ObtCode);504505return TRUE;506}507508509510// Get the number of translations in the MLU object511cmsUInt32Number CMSEXPORT cmsMLUtranslationsCount(const cmsMLU* mlu)512{513if (mlu == NULL) return 0;514return mlu->UsedEntries;515}516517// Get the language and country codes for a specific MLU index518cmsBool CMSEXPORT cmsMLUtranslationsCodes(const cmsMLU* mlu,519cmsUInt32Number idx,520char LanguageCode[3],521char CountryCode[3])522{523_cmsMLUentry *entry;524525if (mlu == NULL) return FALSE;526527if (idx >= mlu->UsedEntries) return FALSE;528529entry = &mlu->Entries[idx];530531strFrom16(LanguageCode, entry->Language);532strFrom16(CountryCode, entry->Country);533534return TRUE;535}536537538// Named color lists --------------------------------------------------------------------------------------------539540// Grow the list to keep at least NumElements541static542cmsBool GrowNamedColorList(cmsNAMEDCOLORLIST* v)543{544cmsUInt32Number size;545_cmsNAMEDCOLOR * NewPtr;546547if (v == NULL) return FALSE;548549if (v ->Allocated == 0)550size = 64; // Initial guess551else552size = v ->Allocated * 2;553554// Keep a maximum color lists can grow, 100K entries seems reasonable555if (size > 1024 * 100) {556_cmsFree(v->ContextID, (void*) v->List);557v->List = NULL;558return FALSE;559}560561NewPtr = (_cmsNAMEDCOLOR*) _cmsRealloc(v ->ContextID, v ->List, size * sizeof(_cmsNAMEDCOLOR));562if (NewPtr == NULL)563return FALSE;564565v ->List = NewPtr;566v ->Allocated = size;567return TRUE;568}569570// Allocate a list for n elements571cmsNAMEDCOLORLIST* CMSEXPORT cmsAllocNamedColorList(cmsContext ContextID, cmsUInt32Number n, cmsUInt32Number ColorantCount, const char* Prefix, const char* Suffix)572{573cmsNAMEDCOLORLIST* v = (cmsNAMEDCOLORLIST*) _cmsMallocZero(ContextID, sizeof(cmsNAMEDCOLORLIST));574575if (v == NULL) return NULL;576577v ->List = NULL;578v ->nColors = 0;579v ->ContextID = ContextID;580581while (v -> Allocated < n) {582if (!GrowNamedColorList(v)) {583cmsFreeNamedColorList(v);584return NULL;585}586}587588strncpy(v ->Prefix, Prefix, sizeof(v ->Prefix)-1);589strncpy(v ->Suffix, Suffix, sizeof(v ->Suffix)-1);590v->Prefix[32] = v->Suffix[32] = 0;591592v -> ColorantCount = ColorantCount;593594return v;595}596597// Free a list598void CMSEXPORT cmsFreeNamedColorList(cmsNAMEDCOLORLIST* v)599{600if (v == NULL) return;601if (v ->List) _cmsFree(v ->ContextID, v ->List);602_cmsFree(v ->ContextID, v);603}604605cmsNAMEDCOLORLIST* CMSEXPORT cmsDupNamedColorList(const cmsNAMEDCOLORLIST* v)606{607cmsNAMEDCOLORLIST* NewNC;608609if (v == NULL) return NULL;610611NewNC= cmsAllocNamedColorList(v ->ContextID, v -> nColors, v ->ColorantCount, v ->Prefix, v ->Suffix);612if (NewNC == NULL) return NULL;613614// For really large tables we need this615while (NewNC ->Allocated < v ->Allocated){616if (!GrowNamedColorList(NewNC))617{618cmsFreeNamedColorList(NewNC);619return NULL;620}621}622623memmove(NewNC ->Prefix, v ->Prefix, sizeof(v ->Prefix));624memmove(NewNC ->Suffix, v ->Suffix, sizeof(v ->Suffix));625NewNC ->ColorantCount = v ->ColorantCount;626memmove(NewNC->List, v ->List, v->nColors * sizeof(_cmsNAMEDCOLOR));627NewNC ->nColors = v ->nColors;628return NewNC;629}630631632// Append a color to a list. List pointer may change if reallocated633cmsBool CMSEXPORT cmsAppendNamedColor(cmsNAMEDCOLORLIST* NamedColorList,634const char* Name,635cmsUInt16Number PCS[3], cmsUInt16Number Colorant[cmsMAXCHANNELS])636{637cmsUInt32Number i;638639if (NamedColorList == NULL) return FALSE;640641if (NamedColorList ->nColors + 1 > NamedColorList ->Allocated) {642if (!GrowNamedColorList(NamedColorList)) return FALSE;643}644645for (i=0; i < NamedColorList ->ColorantCount; i++)646NamedColorList ->List[NamedColorList ->nColors].DeviceColorant[i] = Colorant == NULL ? (cmsUInt16Number)0 : Colorant[i];647648for (i=0; i < 3; i++)649NamedColorList ->List[NamedColorList ->nColors].PCS[i] = PCS == NULL ? (cmsUInt16Number) 0 : PCS[i];650651if (Name != NULL) {652653strncpy(NamedColorList ->List[NamedColorList ->nColors].Name, Name, cmsMAX_PATH-1);654NamedColorList ->List[NamedColorList ->nColors].Name[cmsMAX_PATH-1] = 0;655656}657else658NamedColorList ->List[NamedColorList ->nColors].Name[0] = 0;659660661NamedColorList ->nColors++;662return TRUE;663}664665// Returns number of elements666cmsUInt32Number CMSEXPORT cmsNamedColorCount(const cmsNAMEDCOLORLIST* NamedColorList)667{668if (NamedColorList == NULL) return 0;669return NamedColorList ->nColors;670}671672// Info aboout a given color673cmsBool CMSEXPORT cmsNamedColorInfo(const cmsNAMEDCOLORLIST* NamedColorList, cmsUInt32Number nColor,674char* Name,675char* Prefix,676char* Suffix,677cmsUInt16Number* PCS,678cmsUInt16Number* Colorant)679{680if (NamedColorList == NULL) return FALSE;681682if (nColor >= cmsNamedColorCount(NamedColorList)) return FALSE;683684// strcpy instead of strncpy because many apps are using small buffers685if (Name) strcpy(Name, NamedColorList->List[nColor].Name);686if (Prefix) strcpy(Prefix, NamedColorList->Prefix);687if (Suffix) strcpy(Suffix, NamedColorList->Suffix);688if (PCS)689memmove(PCS, NamedColorList ->List[nColor].PCS, 3*sizeof(cmsUInt16Number));690691if (Colorant)692memmove(Colorant, NamedColorList ->List[nColor].DeviceColorant,693sizeof(cmsUInt16Number) * NamedColorList ->ColorantCount);694695696return TRUE;697}698699// Search for a given color name (no prefix or suffix)700cmsInt32Number CMSEXPORT cmsNamedColorIndex(const cmsNAMEDCOLORLIST* NamedColorList, const char* Name)701{702cmsUInt32Number i;703cmsUInt32Number n;704705if (NamedColorList == NULL) return -1;706n = cmsNamedColorCount(NamedColorList);707for (i=0; i < n; i++) {708if (cmsstrcasecmp(Name, NamedColorList->List[i].Name) == 0)709return (cmsInt32Number) i;710}711712return -1;713}714715// MPE support -----------------------------------------------------------------------------------------------------------------716717static718void FreeNamedColorList(cmsStage* mpe)719{720cmsNAMEDCOLORLIST* List = (cmsNAMEDCOLORLIST*) mpe ->Data;721cmsFreeNamedColorList(List);722}723724static725void* DupNamedColorList(cmsStage* mpe)726{727cmsNAMEDCOLORLIST* List = (cmsNAMEDCOLORLIST*) mpe ->Data;728return cmsDupNamedColorList(List);729}730731static732void EvalNamedColorPCS(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe)733{734cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) mpe ->Data;735cmsUInt16Number index = (cmsUInt16Number) _cmsQuickSaturateWord(In[0] * 65535.0);736737if (index >= NamedColorList-> nColors) {738cmsSignalError(NamedColorList ->ContextID, cmsERROR_RANGE, "Color %d out of range", index);739Out[0] = Out[1] = Out[2] = 0.0f;740}741else {742743// Named color always uses Lab744Out[0] = (cmsFloat32Number) (NamedColorList->List[index].PCS[0] / 65535.0);745Out[1] = (cmsFloat32Number) (NamedColorList->List[index].PCS[1] / 65535.0);746Out[2] = (cmsFloat32Number) (NamedColorList->List[index].PCS[2] / 65535.0);747}748}749750static751void EvalNamedColor(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe)752{753cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) mpe ->Data;754cmsUInt16Number index = (cmsUInt16Number) _cmsQuickSaturateWord(In[0] * 65535.0);755cmsUInt32Number j;756757if (index >= NamedColorList-> nColors) {758cmsSignalError(NamedColorList ->ContextID, cmsERROR_RANGE, "Color %d out of range", index);759for (j = 0; j < NamedColorList->ColorantCount; j++)760Out[j] = 0.0f;761762}763else {764for (j=0; j < NamedColorList ->ColorantCount; j++)765Out[j] = (cmsFloat32Number) (NamedColorList->List[index].DeviceColorant[j] / 65535.0);766}767}768769770// Named color lookup element771cmsStage* CMSEXPORT _cmsStageAllocNamedColor(cmsNAMEDCOLORLIST* NamedColorList, cmsBool UsePCS)772{773return _cmsStageAllocPlaceholder(NamedColorList ->ContextID,774cmsSigNamedColorElemType,7751, UsePCS ? 3 : NamedColorList ->ColorantCount,776UsePCS ? EvalNamedColorPCS : EvalNamedColor,777DupNamedColorList,778FreeNamedColorList,779cmsDupNamedColorList(NamedColorList));780781}782783784// Retrieve the named color list from a transform. Should be first element in the LUT785cmsNAMEDCOLORLIST* CMSEXPORT cmsGetNamedColorList(cmsHTRANSFORM xform)786{787_cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;788cmsStage* mpe = v ->Lut->Elements;789790if (mpe ->Type != cmsSigNamedColorElemType) return NULL;791return (cmsNAMEDCOLORLIST*) mpe ->Data;792}793794795// Profile sequence description routines -------------------------------------------------------------------------------------796797cmsSEQ* CMSEXPORT cmsAllocProfileSequenceDescription(cmsContext ContextID, cmsUInt32Number n)798{799cmsSEQ* Seq;800cmsUInt32Number i;801802if (n == 0) return NULL;803804// In a absolutely arbitrary way, I hereby decide to allow a maxim of 255 profiles linked805// in a devicelink. It makes not sense anyway and may be used for exploits, so let's close the door!806if (n > 255) return NULL;807808Seq = (cmsSEQ*) _cmsMallocZero(ContextID, sizeof(cmsSEQ));809if (Seq == NULL) return NULL;810811Seq -> ContextID = ContextID;812Seq -> seq = (cmsPSEQDESC*) _cmsCalloc(ContextID, n, sizeof(cmsPSEQDESC));813Seq -> n = n;814815if (Seq -> seq == NULL) {816_cmsFree(ContextID, Seq);817return NULL;818}819820for (i=0; i < n; i++) {821Seq -> seq[i].Manufacturer = NULL;822Seq -> seq[i].Model = NULL;823Seq -> seq[i].Description = NULL;824}825826return Seq;827}828829void CMSEXPORT cmsFreeProfileSequenceDescription(cmsSEQ* pseq)830{831cmsUInt32Number i;832833for (i=0; i < pseq ->n; i++) {834if (pseq ->seq[i].Manufacturer != NULL) cmsMLUfree(pseq ->seq[i].Manufacturer);835if (pseq ->seq[i].Model != NULL) cmsMLUfree(pseq ->seq[i].Model);836if (pseq ->seq[i].Description != NULL) cmsMLUfree(pseq ->seq[i].Description);837}838839if (pseq ->seq != NULL) _cmsFree(pseq ->ContextID, pseq ->seq);840_cmsFree(pseq -> ContextID, pseq);841}842843cmsSEQ* CMSEXPORT cmsDupProfileSequenceDescription(const cmsSEQ* pseq)844{845cmsSEQ *NewSeq;846cmsUInt32Number i;847848if (pseq == NULL)849return NULL;850851NewSeq = (cmsSEQ*) _cmsMalloc(pseq -> ContextID, sizeof(cmsSEQ));852if (NewSeq == NULL) return NULL;853854855NewSeq -> seq = (cmsPSEQDESC*) _cmsCalloc(pseq ->ContextID, pseq ->n, sizeof(cmsPSEQDESC));856if (NewSeq ->seq == NULL) goto Error;857858NewSeq -> ContextID = pseq ->ContextID;859NewSeq -> n = pseq ->n;860861for (i=0; i < pseq->n; i++) {862863memmove(&NewSeq ->seq[i].attributes, &pseq ->seq[i].attributes, sizeof(cmsUInt64Number));864865NewSeq ->seq[i].deviceMfg = pseq ->seq[i].deviceMfg;866NewSeq ->seq[i].deviceModel = pseq ->seq[i].deviceModel;867memmove(&NewSeq ->seq[i].ProfileID, &pseq ->seq[i].ProfileID, sizeof(cmsProfileID));868NewSeq ->seq[i].technology = pseq ->seq[i].technology;869870NewSeq ->seq[i].Manufacturer = cmsMLUdup(pseq ->seq[i].Manufacturer);871NewSeq ->seq[i].Model = cmsMLUdup(pseq ->seq[i].Model);872NewSeq ->seq[i].Description = cmsMLUdup(pseq ->seq[i].Description);873874}875876return NewSeq;877878Error:879880cmsFreeProfileSequenceDescription(NewSeq);881return NULL;882}883884// Dictionaries --------------------------------------------------------------------------------------------------------885886// Dictionaries are just very simple linked lists887888889typedef struct _cmsDICT_struct {890cmsDICTentry* head;891cmsContext ContextID;892} _cmsDICT;893894895// Allocate an empty dictionary896cmsHANDLE CMSEXPORT cmsDictAlloc(cmsContext ContextID)897{898_cmsDICT* dict = (_cmsDICT*) _cmsMallocZero(ContextID, sizeof(_cmsDICT));899if (dict == NULL) return NULL;900901dict ->ContextID = ContextID;902return (cmsHANDLE) dict;903904}905906// Dispose resources907void CMSEXPORT cmsDictFree(cmsHANDLE hDict)908{909_cmsDICT* dict = (_cmsDICT*) hDict;910cmsDICTentry *entry, *next;911912_cmsAssert(dict != NULL);913914// Walk the list freeing all nodes915entry = dict ->head;916while (entry != NULL) {917918if (entry ->DisplayName != NULL) cmsMLUfree(entry ->DisplayName);919if (entry ->DisplayValue != NULL) cmsMLUfree(entry ->DisplayValue);920if (entry ->Name != NULL) _cmsFree(dict ->ContextID, entry -> Name);921if (entry ->Value != NULL) _cmsFree(dict ->ContextID, entry -> Value);922923// Don't fall in the habitual trap...924next = entry ->Next;925_cmsFree(dict ->ContextID, entry);926927entry = next;928}929930_cmsFree(dict ->ContextID, dict);931}932933934// Duplicate a wide char string935static936wchar_t* DupWcs(cmsContext ContextID, const wchar_t* ptr)937{938if (ptr == NULL) return NULL;939return (wchar_t*) _cmsDupMem(ContextID, ptr, (mywcslen(ptr) + 1) * sizeof(wchar_t));940}941942// Add a new entry to the linked list943cmsBool CMSEXPORT cmsDictAddEntry(cmsHANDLE hDict, const wchar_t* Name, const wchar_t* Value, const cmsMLU *DisplayName, const cmsMLU *DisplayValue)944{945_cmsDICT* dict = (_cmsDICT*) hDict;946cmsDICTentry *entry;947948_cmsAssert(dict != NULL);949_cmsAssert(Name != NULL);950951entry = (cmsDICTentry*) _cmsMallocZero(dict ->ContextID, sizeof(cmsDICTentry));952if (entry == NULL) return FALSE;953954entry ->DisplayName = cmsMLUdup(DisplayName);955entry ->DisplayValue = cmsMLUdup(DisplayValue);956entry ->Name = DupWcs(dict ->ContextID, Name);957entry ->Value = DupWcs(dict ->ContextID, Value);958959entry ->Next = dict ->head;960dict ->head = entry;961962return TRUE;963}964965966// Duplicates an existing dictionary967cmsHANDLE CMSEXPORT cmsDictDup(cmsHANDLE hDict)968{969_cmsDICT* old_dict = (_cmsDICT*) hDict;970cmsHANDLE hNew;971cmsDICTentry *entry;972973_cmsAssert(old_dict != NULL);974975hNew = cmsDictAlloc(old_dict ->ContextID);976if (hNew == NULL) return NULL;977978// Walk the list freeing all nodes979entry = old_dict ->head;980while (entry != NULL) {981982if (!cmsDictAddEntry(hNew, entry ->Name, entry ->Value, entry ->DisplayName, entry ->DisplayValue)) {983984cmsDictFree(hNew);985return NULL;986}987988entry = entry -> Next;989}990991return hNew;992}993994// Get a pointer to the linked list995const cmsDICTentry* CMSEXPORT cmsDictGetEntryList(cmsHANDLE hDict)996{997_cmsDICT* dict = (_cmsDICT*) hDict;998999if (dict == NULL) return NULL;1000return dict ->head;1001}10021003// Helper For external languages1004const cmsDICTentry* CMSEXPORT cmsDictNextEntry(const cmsDICTentry* e)1005{1006if (e == NULL) return NULL;1007return e ->Next;1008}100910101011