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