• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //---------------------------------------------------------------------------------
2 //
3 //  Little Color Management System
4 //  Copyright (c) 1998-2017 Marti Maria Saguer
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining
7 // a copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the Software
11 // is furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
18 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 //
24 //---------------------------------------------------------------------------------
25 //
26 
27 #include "lcms2_internal.h"
28 
29 // Multilocalized unicode objects. That is an attempt to encapsulate i18n.
30 
31 
32 // Allocates an empty multi localizad unicode object
cmsMLUalloc(cmsContext ContextID,cmsUInt32Number nItems)33 cmsMLU* CMSEXPORT cmsMLUalloc(cmsContext ContextID, cmsUInt32Number nItems)
34 {
35     cmsMLU* mlu;
36 
37     // nItems should be positive if given
38     if (nItems <= 0) nItems = 2;
39 
40     // Create the container
41     mlu = (cmsMLU*) _cmsMallocZero(ContextID, sizeof(cmsMLU));
42     if (mlu == NULL) return NULL;
43 
44     mlu ->ContextID = ContextID;
45 
46     // Create entry array
47     mlu ->Entries = (_cmsMLUentry*) _cmsCalloc(ContextID, nItems, sizeof(_cmsMLUentry));
48     if (mlu ->Entries == NULL) {
49         _cmsFree(ContextID, mlu);
50         return NULL;
51     }
52 
53     // Ok, keep indexes up to date
54     mlu ->AllocatedEntries    = nItems;
55     mlu ->UsedEntries         = 0;
56 
57     return mlu;
58 }
59 
60 
61 // Grows a mempool table for a MLU. Each time this function is called, mempool size is multiplied times two.
62 static
GrowMLUpool(cmsMLU * mlu)63 cmsBool GrowMLUpool(cmsMLU* mlu)
64 {
65     cmsUInt32Number size;
66     void *NewPtr;
67 
68     // Sanity check
69     if (mlu == NULL) return FALSE;
70 
71     if (mlu ->PoolSize == 0)
72         size = 256;
73     else
74         size = mlu ->PoolSize * 2;
75 
76     // Check for overflow
77     if (size < mlu ->PoolSize) return FALSE;
78 
79     // Reallocate the pool
80     NewPtr = _cmsRealloc(mlu ->ContextID, mlu ->MemPool, size);
81     if (NewPtr == NULL) return FALSE;
82 
83 
84     mlu ->MemPool  = NewPtr;
85     mlu ->PoolSize = size;
86 
87     return TRUE;
88 }
89 
90 
91 // Grows a entry table for a MLU. Each time this function is called, table size is multiplied times two.
92 static
GrowMLUtable(cmsMLU * mlu)93 cmsBool GrowMLUtable(cmsMLU* mlu)
94 {
95     cmsUInt32Number AllocatedEntries;
96     _cmsMLUentry *NewPtr;
97 
98     // Sanity check
99     if (mlu == NULL) return FALSE;
100 
101     AllocatedEntries = mlu ->AllocatedEntries * 2;
102 
103     // Check for overflow
104     if (AllocatedEntries / 2 != mlu ->AllocatedEntries) return FALSE;
105 
106     // Reallocate the memory
107     NewPtr = (_cmsMLUentry*)_cmsRealloc(mlu ->ContextID, mlu ->Entries, AllocatedEntries*sizeof(_cmsMLUentry));
108     if (NewPtr == NULL) return FALSE;
109 
110     mlu ->Entries          = NewPtr;
111     mlu ->AllocatedEntries = AllocatedEntries;
112 
113     return TRUE;
114 }
115 
116 
117 // Search for a specific entry in the structure. Language and Country are used.
118 static
SearchMLUEntry(cmsMLU * mlu,cmsUInt16Number LanguageCode,cmsUInt16Number CountryCode)119 int SearchMLUEntry(cmsMLU* mlu, cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode)
120 {
121     cmsUInt32Number i;
122 
123     // Sanity check
124     if (mlu == NULL) return -1;
125 
126     // Iterate whole table
127     for (i=0; i < mlu ->UsedEntries; i++) {
128 
129         if (mlu ->Entries[i].Country  == CountryCode &&
130             mlu ->Entries[i].Language == LanguageCode) return (int) i;
131     }
132 
133     // Not found
134     return -1;
135 }
136 
137 // Add a block of characters to the intended MLU. Language and country are specified.
138 // Only one entry for Language/country pair is allowed.
139 static
AddMLUBlock(cmsMLU * mlu,cmsUInt32Number size,const wchar_t * Block,cmsUInt16Number LanguageCode,cmsUInt16Number CountryCode)140 cmsBool AddMLUBlock(cmsMLU* mlu, cmsUInt32Number size, const wchar_t *Block,
141                      cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode)
142 {
143     cmsUInt32Number Offset;
144     cmsUInt8Number* Ptr;
145 
146     // Sanity check
147     if (mlu == NULL) return FALSE;
148 
149     // Is there any room available?
150     if (mlu ->UsedEntries >= mlu ->AllocatedEntries) {
151         if (!GrowMLUtable(mlu)) return FALSE;
152     }
153 
154     // Only one ASCII string
155     if (SearchMLUEntry(mlu, LanguageCode, CountryCode) >= 0) return FALSE;  // Only one  is allowed!
156 
157     // Check for size
158     while ((mlu ->PoolSize - mlu ->PoolUsed) < size) {
159 
160             if (!GrowMLUpool(mlu)) return FALSE;
161     }
162 
163     Offset = mlu ->PoolUsed;
164 
165     Ptr = (cmsUInt8Number*) mlu ->MemPool;
166     if (Ptr == NULL) return FALSE;
167 
168     // Set the entry
169     memmove(Ptr + Offset, Block, size);
170     mlu ->PoolUsed += size;
171 
172     mlu ->Entries[mlu ->UsedEntries].StrW     = Offset;
173     mlu ->Entries[mlu ->UsedEntries].Len      = size;
174     mlu ->Entries[mlu ->UsedEntries].Country  = CountryCode;
175     mlu ->Entries[mlu ->UsedEntries].Language = LanguageCode;
176     mlu ->UsedEntries++;
177 
178     return TRUE;
179 }
180 
181 // Convert from a 3-char code to a cmsUInt16Number. It is done in this way because some
182 // compilers don't properly align beginning of strings
183 
184 static
strTo16(const char str[3])185 cmsUInt16Number strTo16(const char str[3])
186 {
187     const cmsUInt8Number* ptr8 = (const cmsUInt8Number*)str;
188     cmsUInt16Number n = (cmsUInt16Number) (((cmsUInt16Number) ptr8[1] << 8) | ptr8[0]);
189 
190     return _cmsAdjustEndianess16(n);
191 }
192 
193 static
strFrom16(char str[3],cmsUInt16Number n)194 void strFrom16(char str[3], cmsUInt16Number n)
195 {
196     // Assuming this would be aligned
197     union {
198 
199        cmsUInt16Number n;
200        cmsUInt8Number str[2];
201 
202     } c;
203 
204     c.n = _cmsAdjustEndianess16(n);
205 
206     str[0] = (char) c.str[0]; str[1] = (char) c.str[1]; str[2] = (char) 0;
207 
208 }
209 
210 // Add an ASCII entry. Do not add any \0 termination (ICC1v43_2010-12.pdf page 61)
cmsMLUsetASCII(cmsMLU * mlu,const char LanguageCode[3],const char CountryCode[3],const char * ASCIIString)211 cmsBool CMSEXPORT cmsMLUsetASCII(cmsMLU* mlu, const char LanguageCode[3], const char CountryCode[3], const char* ASCIIString)
212 {
213     cmsUInt32Number i, len = (cmsUInt32Number) strlen(ASCIIString);
214     wchar_t* WStr;
215     cmsBool  rc;
216     cmsUInt16Number Lang  = strTo16(LanguageCode);
217     cmsUInt16Number Cntry = strTo16(CountryCode);
218 
219     if (mlu == NULL) return FALSE;
220 
221     WStr = (wchar_t*) _cmsCalloc(mlu ->ContextID, len,  sizeof(wchar_t));
222     if (WStr == NULL) return FALSE;
223 
224     for (i=0; i < len; i++)
225         WStr[i] = (wchar_t) ASCIIString[i];
226 
227     rc = AddMLUBlock(mlu, len  * sizeof(wchar_t), WStr, Lang, Cntry);
228 
229     _cmsFree(mlu ->ContextID, WStr);
230     return rc;
231 
232 }
233 
234 // We don't need any wcs support library
235 static
mywcslen(const wchar_t * s)236 cmsUInt32Number mywcslen(const wchar_t *s)
237 {
238     const wchar_t *p;
239 
240     p = s;
241     while (*p)
242         p++;
243 
244     return (cmsUInt32Number)(p - s);
245 }
246 
247 // Add a wide entry. Do not add any \0 terminator (ICC1v43_2010-12.pdf page 61)
cmsMLUsetWide(cmsMLU * mlu,const char Language[3],const char Country[3],const wchar_t * WideString)248 cmsBool  CMSEXPORT cmsMLUsetWide(cmsMLU* mlu, const char Language[3], const char Country[3], const wchar_t* WideString)
249 {
250     cmsUInt16Number Lang  = strTo16(Language);
251     cmsUInt16Number Cntry = strTo16(Country);
252     cmsUInt32Number len;
253 
254     if (mlu == NULL) return FALSE;
255     if (WideString == NULL) return FALSE;
256 
257     len = (cmsUInt32Number) (mywcslen(WideString)) * sizeof(wchar_t);
258     return AddMLUBlock(mlu, len, WideString, Lang, Cntry);
259 }
260 
261 // Duplicating a MLU is as easy as copying all members
cmsMLUdup(const cmsMLU * mlu)262 cmsMLU* CMSEXPORT cmsMLUdup(const cmsMLU* mlu)
263 {
264     cmsMLU* NewMlu = NULL;
265 
266     // Duplicating a NULL obtains a NULL
267     if (mlu == NULL) return NULL;
268 
269     NewMlu = cmsMLUalloc(mlu ->ContextID, mlu ->UsedEntries);
270     if (NewMlu == NULL) return NULL;
271 
272     // Should never happen
273     if (NewMlu ->AllocatedEntries < mlu ->UsedEntries)
274         goto Error;
275 
276     // Sanitize...
277     if (NewMlu ->Entries == NULL || mlu ->Entries == NULL)  goto Error;
278 
279     memmove(NewMlu ->Entries, mlu ->Entries, mlu ->UsedEntries * sizeof(_cmsMLUentry));
280     NewMlu ->UsedEntries = mlu ->UsedEntries;
281 
282     // The MLU may be empty
283     if (mlu ->PoolUsed == 0) {
284         NewMlu ->MemPool = NULL;
285     }
286     else {
287         // It is not empty
288         NewMlu ->MemPool = _cmsMalloc(mlu ->ContextID, mlu ->PoolUsed);
289         if (NewMlu ->MemPool == NULL) goto Error;
290     }
291 
292     NewMlu ->PoolSize = mlu ->PoolUsed;
293 
294     if (NewMlu ->MemPool == NULL || mlu ->MemPool == NULL) goto Error;
295 
296     memmove(NewMlu ->MemPool, mlu->MemPool, mlu ->PoolUsed);
297     NewMlu ->PoolUsed = mlu ->PoolUsed;
298 
299     return NewMlu;
300 
301 Error:
302 
303     if (NewMlu != NULL) cmsMLUfree(NewMlu);
304     return NULL;
305 }
306 
307 // Free any used memory
cmsMLUfree(cmsMLU * mlu)308 void CMSEXPORT cmsMLUfree(cmsMLU* mlu)
309 {
310     if (mlu) {
311 
312         if (mlu -> Entries) _cmsFree(mlu ->ContextID, mlu->Entries);
313         if (mlu -> MemPool) _cmsFree(mlu ->ContextID, mlu->MemPool);
314 
315         _cmsFree(mlu ->ContextID, mlu);
316     }
317 }
318 
319 
320 // The algorithm first searches for an exact match of country and language, if not found it uses
321 // the Language. If none is found, first entry is used instead.
322 static
_cmsMLUgetWide(const cmsMLU * mlu,cmsUInt32Number * len,cmsUInt16Number LanguageCode,cmsUInt16Number CountryCode,cmsUInt16Number * UsedLanguageCode,cmsUInt16Number * UsedCountryCode)323 const wchar_t* _cmsMLUgetWide(const cmsMLU* mlu,
324                               cmsUInt32Number *len,
325                               cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode,
326                               cmsUInt16Number* UsedLanguageCode, cmsUInt16Number* UsedCountryCode)
327 {
328     cmsUInt32Number i;
329     int Best = -1;
330     _cmsMLUentry* v;
331 
332     if (mlu == NULL) return NULL;
333 
334     if (mlu -> AllocatedEntries <= 0) return NULL;
335 
336     for (i=0; i < mlu ->UsedEntries; i++) {
337 
338         v = mlu ->Entries + i;
339 
340         if (v -> Language == LanguageCode) {
341 
342             if (Best == -1) Best = (int) i;
343 
344             if (v -> Country == CountryCode) {
345 
346                 if (UsedLanguageCode != NULL) *UsedLanguageCode = v ->Language;
347                 if (UsedCountryCode  != NULL) *UsedCountryCode = v ->Country;
348 
349                 if (len != NULL) *len = v ->Len;
350 
351                 return (wchar_t*) ((cmsUInt8Number*) mlu ->MemPool + v -> StrW);        // Found exact match
352             }
353         }
354     }
355 
356     // No string found. Return First one
357     if (Best == -1)
358         Best = 0;
359 
360     v = mlu ->Entries + Best;
361 
362     if (UsedLanguageCode != NULL) *UsedLanguageCode = v ->Language;
363     if (UsedCountryCode  != NULL) *UsedCountryCode = v ->Country;
364 
365     if (len != NULL) *len   = v ->Len;
366 
367     return(wchar_t*) ((cmsUInt8Number*) mlu ->MemPool + v ->StrW);
368 }
369 
370 
371 // Obtain an ASCII representation of the wide string. Setting buffer to NULL returns the len
cmsMLUgetASCII(const cmsMLU * mlu,const char LanguageCode[3],const char CountryCode[3],char * Buffer,cmsUInt32Number BufferSize)372 cmsUInt32Number CMSEXPORT cmsMLUgetASCII(const cmsMLU* mlu,
373                                        const char LanguageCode[3], const char CountryCode[3],
374                                        char* Buffer, cmsUInt32Number BufferSize)
375 {
376     const wchar_t *Wide;
377     cmsUInt32Number  StrLen = 0;
378     cmsUInt32Number ASCIIlen, i;
379 
380     cmsUInt16Number Lang  = strTo16(LanguageCode);
381     cmsUInt16Number Cntry = strTo16(CountryCode);
382 
383     // Sanitize
384     if (mlu == NULL) return 0;
385 
386     // Get WideChar
387     Wide = _cmsMLUgetWide(mlu, &StrLen, Lang, Cntry, NULL, NULL);
388     if (Wide == NULL) return 0;
389 
390     ASCIIlen = StrLen / sizeof(wchar_t);
391 
392     // Maybe we want only to know the len?
393     if (Buffer == NULL) return ASCIIlen + 1; // Note the zero at the end
394 
395     // No buffer size means no data
396     if (BufferSize <= 0) return 0;
397 
398     // Some clipping may be required
399     if (BufferSize < ASCIIlen + 1)
400         ASCIIlen = BufferSize - 1;
401 
402     // Precess each character
403     for (i=0; i < ASCIIlen; i++) {
404 
405         if (Wide[i] == 0)
406             Buffer[i] = 0;
407         else
408             Buffer[i] = (char) Wide[i];
409     }
410 
411     // We put a termination "\0"
412     Buffer[ASCIIlen] = 0;
413     return ASCIIlen + 1;
414 }
415 
416 // Obtain a wide representation of the MLU, on depending on current locale settings
cmsMLUgetWide(const cmsMLU * mlu,const char LanguageCode[3],const char CountryCode[3],wchar_t * Buffer,cmsUInt32Number BufferSize)417 cmsUInt32Number CMSEXPORT cmsMLUgetWide(const cmsMLU* mlu,
418                                       const char LanguageCode[3], const char CountryCode[3],
419                                       wchar_t* Buffer, cmsUInt32Number BufferSize)
420 {
421     const wchar_t *Wide;
422     cmsUInt32Number  StrLen = 0;
423 
424     cmsUInt16Number Lang  = strTo16(LanguageCode);
425     cmsUInt16Number Cntry = strTo16(CountryCode);
426 
427     // Sanitize
428     if (mlu == NULL) return 0;
429 
430     Wide = _cmsMLUgetWide(mlu, &StrLen, Lang, Cntry, NULL, NULL);
431     if (Wide == NULL) return 0;
432 
433     // Maybe we want only to know the len?
434     if (Buffer == NULL) return StrLen + sizeof(wchar_t);
435 
436   // No buffer size means no data
437     if (BufferSize <= 0) return 0;
438 
439     // Some clipping may be required
440     if (BufferSize < StrLen + sizeof(wchar_t))
441         StrLen = BufferSize - + sizeof(wchar_t);
442 
443     memmove(Buffer, Wide, StrLen);
444     Buffer[StrLen / sizeof(wchar_t)] = 0;
445 
446     return StrLen + sizeof(wchar_t);
447 }
448 
449 
450 // Get also the language and country
cmsMLUgetTranslation(const cmsMLU * mlu,const char LanguageCode[3],const char CountryCode[3],char ObtainedLanguage[3],char ObtainedCountry[3])451 CMSAPI cmsBool CMSEXPORT cmsMLUgetTranslation(const cmsMLU* mlu,
452                                               const char LanguageCode[3], const char CountryCode[3],
453                                               char ObtainedLanguage[3], char ObtainedCountry[3])
454 {
455     const wchar_t *Wide;
456 
457     cmsUInt16Number Lang  = strTo16(LanguageCode);
458     cmsUInt16Number Cntry = strTo16(CountryCode);
459     cmsUInt16Number ObtLang, ObtCode;
460 
461     // Sanitize
462     if (mlu == NULL) return FALSE;
463 
464     Wide = _cmsMLUgetWide(mlu, NULL, Lang, Cntry, &ObtLang, &ObtCode);
465     if (Wide == NULL) return FALSE;
466 
467     // Get used language and code
468     strFrom16(ObtainedLanguage, ObtLang);
469     strFrom16(ObtainedCountry, ObtCode);
470 
471     return TRUE;
472 }
473 
474 
475 
476 // Get the number of translations in the MLU object
cmsMLUtranslationsCount(const cmsMLU * mlu)477 cmsUInt32Number CMSEXPORT cmsMLUtranslationsCount(const cmsMLU* mlu)
478 {
479     if (mlu == NULL) return 0;
480     return mlu->UsedEntries;
481 }
482 
483 // Get the language and country codes for a specific MLU index
cmsMLUtranslationsCodes(const cmsMLU * mlu,cmsUInt32Number idx,char LanguageCode[3],char CountryCode[3])484 cmsBool CMSEXPORT cmsMLUtranslationsCodes(const cmsMLU* mlu,
485                                           cmsUInt32Number idx,
486                                           char LanguageCode[3],
487                                           char CountryCode[3])
488 {
489     _cmsMLUentry *entry;
490 
491     if (mlu == NULL) return FALSE;
492 
493     if (idx >= mlu->UsedEntries) return FALSE;
494 
495     entry = &mlu->Entries[idx];
496 
497     strFrom16(LanguageCode, entry->Language);
498     strFrom16(CountryCode, entry->Country);
499 
500     return TRUE;
501 }
502 
503 
504 // Named color lists --------------------------------------------------------------------------------------------
505 
506 // Grow the list to keep at least NumElements
507 static
GrowNamedColorList(cmsNAMEDCOLORLIST * v)508 cmsBool  GrowNamedColorList(cmsNAMEDCOLORLIST* v)
509 {
510     cmsUInt32Number size;
511     _cmsNAMEDCOLOR * NewPtr;
512 
513     if (v == NULL) return FALSE;
514 
515     if (v ->Allocated == 0)
516         size = 64;   // Initial guess
517     else
518         size = v ->Allocated * 2;
519 
520     // Keep a maximum color lists can grow, 100K entries seems reasonable
521     if (size > 1024 * 100) {
522         _cmsFree(v->ContextID, (void*) v->List);
523         v->List = NULL;
524         return FALSE;
525     }
526 
527     NewPtr = (_cmsNAMEDCOLOR*) _cmsRealloc(v ->ContextID, v ->List, size * sizeof(_cmsNAMEDCOLOR));
528     if (NewPtr == NULL)
529         return FALSE;
530 
531     v ->List      = NewPtr;
532     v ->Allocated = size;
533     return TRUE;
534 }
535 
536 // Allocate a list for n elements
cmsAllocNamedColorList(cmsContext ContextID,cmsUInt32Number n,cmsUInt32Number ColorantCount,const char * Prefix,const char * Suffix)537 cmsNAMEDCOLORLIST* CMSEXPORT cmsAllocNamedColorList(cmsContext ContextID, cmsUInt32Number n, cmsUInt32Number ColorantCount, const char* Prefix, const char* Suffix)
538 {
539     cmsNAMEDCOLORLIST* v = (cmsNAMEDCOLORLIST*) _cmsMallocZero(ContextID, sizeof(cmsNAMEDCOLORLIST));
540 
541     if (v == NULL) return NULL;
542 
543     v ->List      = NULL;
544     v ->nColors   = 0;
545     v ->ContextID  = ContextID;
546 
547     while (v -> Allocated < n) {
548         if (!GrowNamedColorList(v)) {
549             cmsFreeNamedColorList(v);
550             return NULL;
551         }
552     }
553 
554     strncpy(v ->Prefix, Prefix, sizeof(v ->Prefix)-1);
555     strncpy(v ->Suffix, Suffix, sizeof(v ->Suffix)-1);
556     v->Prefix[32] = v->Suffix[32] = 0;
557 
558     v -> ColorantCount = ColorantCount;
559 
560     return v;
561 }
562 
563 // Free a list
cmsFreeNamedColorList(cmsNAMEDCOLORLIST * v)564 void CMSEXPORT cmsFreeNamedColorList(cmsNAMEDCOLORLIST* v)
565 {
566     if (v == NULL) return;
567     if (v ->List) _cmsFree(v ->ContextID, v ->List);
568     _cmsFree(v ->ContextID, v);
569 }
570 
cmsDupNamedColorList(const cmsNAMEDCOLORLIST * v)571 cmsNAMEDCOLORLIST* CMSEXPORT cmsDupNamedColorList(const cmsNAMEDCOLORLIST* v)
572 {
573     cmsNAMEDCOLORLIST* NewNC;
574 
575     if (v == NULL) return NULL;
576 
577     NewNC= cmsAllocNamedColorList(v ->ContextID, v -> nColors, v ->ColorantCount, v ->Prefix, v ->Suffix);
578     if (NewNC == NULL) return NULL;
579 
580     // For really large tables we need this
581     while (NewNC ->Allocated < v ->Allocated){
582         if (!GrowNamedColorList(NewNC)) {
583             cmsFreeNamedColorList(NewNC);
584             return NULL;
585         }
586     }
587 
588     memmove(NewNC ->Prefix, v ->Prefix, sizeof(v ->Prefix));
589     memmove(NewNC ->Suffix, v ->Suffix, sizeof(v ->Suffix));
590     NewNC ->ColorantCount = v ->ColorantCount;
591     memmove(NewNC->List, v ->List, v->nColors * sizeof(_cmsNAMEDCOLOR));
592     NewNC ->nColors = v ->nColors;
593     return NewNC;
594 }
595 
596 
597 // Append a color to a list. List pointer may change if reallocated
cmsAppendNamedColor(cmsNAMEDCOLORLIST * NamedColorList,const char * Name,cmsUInt16Number PCS[3],cmsUInt16Number Colorant[cmsMAXCHANNELS])598 cmsBool  CMSEXPORT cmsAppendNamedColor(cmsNAMEDCOLORLIST* NamedColorList,
599                                        const char* Name,
600                                        cmsUInt16Number PCS[3], cmsUInt16Number Colorant[cmsMAXCHANNELS])
601 {
602     cmsUInt32Number i;
603 
604     if (NamedColorList == NULL) return FALSE;
605 
606     if (NamedColorList ->nColors + 1 > NamedColorList ->Allocated) {
607         if (!GrowNamedColorList(NamedColorList)) return FALSE;
608     }
609 
610     for (i=0; i < NamedColorList ->ColorantCount; i++)
611         NamedColorList ->List[NamedColorList ->nColors].DeviceColorant[i] = Colorant == NULL ? (cmsUInt16Number)0 : Colorant[i];
612 
613     for (i=0; i < 3; i++)
614         NamedColorList ->List[NamedColorList ->nColors].PCS[i] = PCS == NULL ? (cmsUInt16Number) 0 : PCS[i];
615 
616     if (Name != NULL) {
617 
618         strncpy(NamedColorList ->List[NamedColorList ->nColors].Name, Name, cmsMAX_PATH-1);
619         NamedColorList ->List[NamedColorList ->nColors].Name[cmsMAX_PATH-1] = 0;
620 
621     }
622     else
623         NamedColorList ->List[NamedColorList ->nColors].Name[0] = 0;
624 
625 
626     NamedColorList ->nColors++;
627     return TRUE;
628 }
629 
630 // Returns number of elements
cmsNamedColorCount(const cmsNAMEDCOLORLIST * NamedColorList)631 cmsUInt32Number CMSEXPORT cmsNamedColorCount(const cmsNAMEDCOLORLIST* NamedColorList)
632 {
633      if (NamedColorList == NULL) return 0;
634      return NamedColorList ->nColors;
635 }
636 
637 // Info aboout a given color
cmsNamedColorInfo(const cmsNAMEDCOLORLIST * NamedColorList,cmsUInt32Number nColor,char * Name,char * Prefix,char * Suffix,cmsUInt16Number * PCS,cmsUInt16Number * Colorant)638 cmsBool  CMSEXPORT cmsNamedColorInfo(const cmsNAMEDCOLORLIST* NamedColorList, cmsUInt32Number nColor,
639                                      char* Name,
640                                      char* Prefix,
641                                      char* Suffix,
642                                      cmsUInt16Number* PCS,
643                                      cmsUInt16Number* Colorant)
644 {
645     if (NamedColorList == NULL) return FALSE;
646 
647     if (nColor >= cmsNamedColorCount(NamedColorList)) return FALSE;
648 
649     // strcpy instead of strncpy because many apps are using small buffers
650     if (Name) strcpy(Name, NamedColorList->List[nColor].Name);
651     if (Prefix) strcpy(Prefix, NamedColorList->Prefix);
652     if (Suffix) strcpy(Suffix, NamedColorList->Suffix);
653     if (PCS)
654         memmove(PCS, NamedColorList ->List[nColor].PCS, 3*sizeof(cmsUInt16Number));
655 
656     if (Colorant)
657         memmove(Colorant, NamedColorList ->List[nColor].DeviceColorant,
658                                 sizeof(cmsUInt16Number) * NamedColorList ->ColorantCount);
659 
660 
661     return TRUE;
662 }
663 
664 // Search for a given color name (no prefix or suffix)
cmsNamedColorIndex(const cmsNAMEDCOLORLIST * NamedColorList,const char * Name)665 cmsInt32Number CMSEXPORT cmsNamedColorIndex(const cmsNAMEDCOLORLIST* NamedColorList, const char* Name)
666 {
667     cmsUInt32Number i;
668     cmsUInt32Number n;
669 
670     if (NamedColorList == NULL) return -1;
671     n = cmsNamedColorCount(NamedColorList);
672     for (i=0; i < n; i++) {
673         if (cmsstrcasecmp(Name,  NamedColorList->List[i].Name) == 0)
674             return (cmsInt32Number) i;
675     }
676 
677     return -1;
678 }
679 
680 // MPE support -----------------------------------------------------------------------------------------------------------------
681 
682 static
FreeNamedColorList(cmsStage * mpe)683 void FreeNamedColorList(cmsStage* mpe)
684 {
685     cmsNAMEDCOLORLIST* List = (cmsNAMEDCOLORLIST*) mpe ->Data;
686     cmsFreeNamedColorList(List);
687 }
688 
689 static
DupNamedColorList(cmsStage * mpe)690 void* DupNamedColorList(cmsStage* mpe)
691 {
692     cmsNAMEDCOLORLIST* List = (cmsNAMEDCOLORLIST*) mpe ->Data;
693     return cmsDupNamedColorList(List);
694 }
695 
696 static
EvalNamedColorPCS(const cmsFloat32Number In[],cmsFloat32Number Out[],const cmsStage * mpe)697 void EvalNamedColorPCS(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe)
698 {
699     cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) mpe ->Data;
700     cmsUInt16Number index = (cmsUInt16Number) _cmsQuickSaturateWord(In[0] * 65535.0);
701 
702     if (index >= NamedColorList-> nColors) {
703         cmsSignalError(NamedColorList ->ContextID, cmsERROR_RANGE, "Color %d out of range", index);
704         Out[0] = Out[1] = Out[2] = 0.0f;
705     }
706     else {
707 
708             // Named color always uses Lab
709             Out[0] = (cmsFloat32Number) (NamedColorList->List[index].PCS[0] / 65535.0);
710             Out[1] = (cmsFloat32Number) (NamedColorList->List[index].PCS[1] / 65535.0);
711             Out[2] = (cmsFloat32Number) (NamedColorList->List[index].PCS[2] / 65535.0);
712     }
713 }
714 
715 static
EvalNamedColor(const cmsFloat32Number In[],cmsFloat32Number Out[],const cmsStage * mpe)716 void EvalNamedColor(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe)
717 {
718     cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) mpe ->Data;
719     cmsUInt16Number index = (cmsUInt16Number) _cmsQuickSaturateWord(In[0] * 65535.0);
720     cmsUInt32Number j;
721 
722     if (index >= NamedColorList-> nColors) {
723         cmsSignalError(NamedColorList ->ContextID, cmsERROR_RANGE, "Color %d out of range", index);
724         for (j = 0; j < NamedColorList->ColorantCount; j++)
725             Out[j] = 0.0f;
726 
727     }
728     else {
729         for (j=0; j < NamedColorList ->ColorantCount; j++)
730             Out[j] = (cmsFloat32Number) (NamedColorList->List[index].DeviceColorant[j] / 65535.0);
731     }
732 }
733 
734 
735 // Named color lookup element
_cmsStageAllocNamedColor(cmsNAMEDCOLORLIST * NamedColorList,cmsBool UsePCS)736 cmsStage* _cmsStageAllocNamedColor(cmsNAMEDCOLORLIST* NamedColorList, cmsBool UsePCS)
737 {
738     return _cmsStageAllocPlaceholder(NamedColorList ->ContextID,
739                                    cmsSigNamedColorElemType,
740                                    1, UsePCS ? 3 : NamedColorList ->ColorantCount,
741                                    UsePCS ? EvalNamedColorPCS : EvalNamedColor,
742                                    DupNamedColorList,
743                                    FreeNamedColorList,
744                                    cmsDupNamedColorList(NamedColorList));
745 
746 }
747 
748 
749 // Retrieve the named color list from a transform. Should be first element in the LUT
cmsGetNamedColorList(cmsHTRANSFORM xform)750 cmsNAMEDCOLORLIST* CMSEXPORT cmsGetNamedColorList(cmsHTRANSFORM xform)
751 {
752     _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;
753     cmsStage* mpe  = v ->Lut->Elements;
754 
755     if (mpe ->Type != cmsSigNamedColorElemType) return NULL;
756     return (cmsNAMEDCOLORLIST*) mpe ->Data;
757 }
758 
759 
760 // Profile sequence description routines -------------------------------------------------------------------------------------
761 
cmsAllocProfileSequenceDescription(cmsContext ContextID,cmsUInt32Number n)762 cmsSEQ* CMSEXPORT cmsAllocProfileSequenceDescription(cmsContext ContextID, cmsUInt32Number n)
763 {
764     cmsSEQ* Seq;
765     cmsUInt32Number i;
766 
767     if (n == 0) return NULL;
768 
769     // In a absolutely arbitrary way, I hereby decide to allow a maxim of 255 profiles linked
770     // in a devicelink. It makes not sense anyway and may be used for exploits, so let's close the door!
771     if (n > 255) return NULL;
772 
773     Seq = (cmsSEQ*) _cmsMallocZero(ContextID, sizeof(cmsSEQ));
774     if (Seq == NULL) return NULL;
775 
776     Seq -> ContextID = ContextID;
777     Seq -> seq      = (cmsPSEQDESC*) _cmsCalloc(ContextID, n, sizeof(cmsPSEQDESC));
778     Seq -> n        = n;
779 
780     if (Seq -> seq == NULL) {
781         _cmsFree(ContextID, Seq);
782         return NULL;
783     }
784 
785     for (i=0; i < n; i++) {
786         Seq -> seq[i].Manufacturer = NULL;
787         Seq -> seq[i].Model        = NULL;
788         Seq -> seq[i].Description  = NULL;
789     }
790 
791     return Seq;
792 }
793 
cmsFreeProfileSequenceDescription(cmsSEQ * pseq)794 void CMSEXPORT cmsFreeProfileSequenceDescription(cmsSEQ* pseq)
795 {
796     cmsUInt32Number i;
797 
798     for (i=0; i < pseq ->n; i++) {
799         if (pseq ->seq[i].Manufacturer != NULL) cmsMLUfree(pseq ->seq[i].Manufacturer);
800         if (pseq ->seq[i].Model != NULL) cmsMLUfree(pseq ->seq[i].Model);
801         if (pseq ->seq[i].Description != NULL) cmsMLUfree(pseq ->seq[i].Description);
802     }
803 
804     if (pseq ->seq != NULL) _cmsFree(pseq ->ContextID, pseq ->seq);
805     _cmsFree(pseq -> ContextID, pseq);
806 }
807 
cmsDupProfileSequenceDescription(const cmsSEQ * pseq)808 cmsSEQ* CMSEXPORT cmsDupProfileSequenceDescription(const cmsSEQ* pseq)
809 {
810     cmsSEQ *NewSeq;
811     cmsUInt32Number i;
812 
813     if (pseq == NULL)
814         return NULL;
815 
816     NewSeq = (cmsSEQ*) _cmsMalloc(pseq -> ContextID, sizeof(cmsSEQ));
817     if (NewSeq == NULL) return NULL;
818 
819 
820     NewSeq -> seq      = (cmsPSEQDESC*) _cmsCalloc(pseq ->ContextID, pseq ->n, sizeof(cmsPSEQDESC));
821     if (NewSeq ->seq == NULL) goto Error;
822 
823     NewSeq -> ContextID = pseq ->ContextID;
824     NewSeq -> n        = pseq ->n;
825 
826     for (i=0; i < pseq->n; i++) {
827 
828         memmove(&NewSeq ->seq[i].attributes, &pseq ->seq[i].attributes, sizeof(cmsUInt64Number));
829 
830         NewSeq ->seq[i].deviceMfg   = pseq ->seq[i].deviceMfg;
831         NewSeq ->seq[i].deviceModel = pseq ->seq[i].deviceModel;
832         memmove(&NewSeq ->seq[i].ProfileID, &pseq ->seq[i].ProfileID, sizeof(cmsProfileID));
833         NewSeq ->seq[i].technology  = pseq ->seq[i].technology;
834 
835         NewSeq ->seq[i].Manufacturer = cmsMLUdup(pseq ->seq[i].Manufacturer);
836         NewSeq ->seq[i].Model        = cmsMLUdup(pseq ->seq[i].Model);
837         NewSeq ->seq[i].Description  = cmsMLUdup(pseq ->seq[i].Description);
838 
839     }
840 
841     return NewSeq;
842 
843 Error:
844 
845     cmsFreeProfileSequenceDescription(NewSeq);
846     return NULL;
847 }
848 
849 // Dictionaries --------------------------------------------------------------------------------------------------------
850 
851 // Dictionaries are just very simple linked lists
852 
853 
854 typedef struct _cmsDICT_struct {
855     cmsDICTentry* head;
856     cmsContext ContextID;
857 } _cmsDICT;
858 
859 
860 // Allocate an empty dictionary
cmsDictAlloc(cmsContext ContextID)861 cmsHANDLE CMSEXPORT cmsDictAlloc(cmsContext ContextID)
862 {
863     _cmsDICT* dict = (_cmsDICT*) _cmsMallocZero(ContextID, sizeof(_cmsDICT));
864     if (dict == NULL) return NULL;
865 
866     dict ->ContextID = ContextID;
867     return (cmsHANDLE) dict;
868 
869 }
870 
871 // Dispose resources
cmsDictFree(cmsHANDLE hDict)872 void CMSEXPORT cmsDictFree(cmsHANDLE hDict)
873 {
874     _cmsDICT* dict = (_cmsDICT*) hDict;
875     cmsDICTentry *entry, *next;
876 
877     _cmsAssert(dict != NULL);
878 
879     // Walk the list freeing all nodes
880     entry = dict ->head;
881     while (entry != NULL) {
882 
883             if (entry ->DisplayName  != NULL) cmsMLUfree(entry ->DisplayName);
884             if (entry ->DisplayValue != NULL) cmsMLUfree(entry ->DisplayValue);
885             if (entry ->Name != NULL) _cmsFree(dict ->ContextID, entry -> Name);
886             if (entry ->Value != NULL) _cmsFree(dict ->ContextID, entry -> Value);
887 
888             // Don't fall in the habitual trap...
889             next = entry ->Next;
890             _cmsFree(dict ->ContextID, entry);
891 
892             entry = next;
893     }
894 
895     _cmsFree(dict ->ContextID, dict);
896 }
897 
898 
899 // Duplicate a wide char string
900 static
DupWcs(cmsContext ContextID,const wchar_t * ptr)901 wchar_t* DupWcs(cmsContext ContextID, const wchar_t* ptr)
902 {
903     if (ptr == NULL) return NULL;
904     return (wchar_t*) _cmsDupMem(ContextID, ptr, (mywcslen(ptr) + 1) * sizeof(wchar_t));
905 }
906 
907 // Add a new entry to the linked list
cmsDictAddEntry(cmsHANDLE hDict,const wchar_t * Name,const wchar_t * Value,const cmsMLU * DisplayName,const cmsMLU * DisplayValue)908 cmsBool CMSEXPORT cmsDictAddEntry(cmsHANDLE hDict, const wchar_t* Name, const wchar_t* Value, const cmsMLU *DisplayName, const cmsMLU *DisplayValue)
909 {
910     _cmsDICT* dict = (_cmsDICT*) hDict;
911     cmsDICTentry *entry;
912 
913     _cmsAssert(dict != NULL);
914     _cmsAssert(Name != NULL);
915 
916     entry = (cmsDICTentry*) _cmsMallocZero(dict ->ContextID, sizeof(cmsDICTentry));
917     if (entry == NULL) return FALSE;
918 
919     entry ->DisplayName  = cmsMLUdup(DisplayName);
920     entry ->DisplayValue = cmsMLUdup(DisplayValue);
921     entry ->Name         = DupWcs(dict ->ContextID, Name);
922     entry ->Value        = DupWcs(dict ->ContextID, Value);
923 
924     entry ->Next = dict ->head;
925     dict ->head = entry;
926 
927     return TRUE;
928 }
929 
930 
931 // Duplicates an existing dictionary
cmsDictDup(cmsHANDLE hDict)932 cmsHANDLE CMSEXPORT cmsDictDup(cmsHANDLE hDict)
933 {
934     _cmsDICT* old_dict = (_cmsDICT*) hDict;
935     cmsHANDLE hNew;
936     cmsDICTentry *entry;
937 
938     _cmsAssert(old_dict != NULL);
939 
940     hNew  = cmsDictAlloc(old_dict ->ContextID);
941     if (hNew == NULL) return NULL;
942 
943     // Walk the list freeing all nodes
944     entry = old_dict ->head;
945     while (entry != NULL) {
946 
947         if (!cmsDictAddEntry(hNew, entry ->Name, entry ->Value, entry ->DisplayName, entry ->DisplayValue)) {
948 
949             cmsDictFree(hNew);
950             return NULL;
951         }
952 
953         entry = entry -> Next;
954     }
955 
956     return hNew;
957 }
958 
959 // Get a pointer to the linked list
cmsDictGetEntryList(cmsHANDLE hDict)960 const cmsDICTentry* CMSEXPORT cmsDictGetEntryList(cmsHANDLE hDict)
961 {
962     _cmsDICT* dict = (_cmsDICT*) hDict;
963 
964     if (dict == NULL) return NULL;
965     return dict ->head;
966 }
967 
968 // Helper For external languages
cmsDictNextEntry(const cmsDICTentry * e)969 const cmsDICTentry* CMSEXPORT cmsDictNextEntry(const cmsDICTentry* e)
970 {
971      if (e == NULL) return NULL;
972      return e ->Next;
973 }
974