1 //---------------------------------------------------------------------------------
3 // Little Color Management System
4 // Copyright (c) 1998-2023 Marti Maria Saguer
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:
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
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.
24 //---------------------------------------------------------------------------------
27 #include "lcms2_internal.h"
29 // Multilocalized unicode objects. That is an attempt to encapsulate i18n.
32 // Allocates an empty multi localizad unicode object
33 cmsMLU
* CMSEXPORT
cmsMLUalloc(cmsContext ContextID
, cmsUInt32Number nItems
)
37 // nItems should be positive if given
38 if (nItems
<= 0) nItems
= 2;
40 // Create the container
41 mlu
= (cmsMLU
*) _cmsMallocZero(ContextID
, sizeof(cmsMLU
));
42 if (mlu
== NULL
) return NULL
;
44 mlu
->ContextID
= ContextID
;
47 mlu
->Entries
= (_cmsMLUentry
*) _cmsCalloc(ContextID
, nItems
, sizeof(_cmsMLUentry
));
48 if (mlu
->Entries
== NULL
) {
49 _cmsFree(ContextID
, mlu
);
53 // Ok, keep indexes up to date
54 mlu
->AllocatedEntries
= nItems
;
55 mlu
->UsedEntries
= 0;
61 // Grows a mempool table for a MLU. Each time this function is called, mempool size is multiplied times two.
63 cmsBool
GrowMLUpool(cmsMLU
* mlu
)
69 if (mlu
== NULL
) return FALSE
;
71 if (mlu
->PoolSize
== 0)
74 size
= mlu
->PoolSize
* 2;
77 if (size
< mlu
->PoolSize
) return FALSE
;
79 // Reallocate the pool
80 NewPtr
= _cmsRealloc(mlu
->ContextID
, mlu
->MemPool
, size
);
81 if (NewPtr
== NULL
) return FALSE
;
84 mlu
->MemPool
= NewPtr
;
85 mlu
->PoolSize
= size
;
91 // Grows a entry table for a MLU. Each time this function is called, table size is multiplied times two.
93 cmsBool
GrowMLUtable(cmsMLU
* mlu
)
95 cmsUInt32Number AllocatedEntries
;
99 if (mlu
== NULL
) return FALSE
;
101 AllocatedEntries
= mlu
->AllocatedEntries
* 2;
103 // Check for overflow
104 if (AllocatedEntries
/ 2 != mlu
->AllocatedEntries
) return FALSE
;
106 // Reallocate the memory
107 NewPtr
= (_cmsMLUentry
*)_cmsRealloc(mlu
->ContextID
, mlu
->Entries
, AllocatedEntries
*sizeof(_cmsMLUentry
));
108 if (NewPtr
== NULL
) return FALSE
;
110 mlu
->Entries
= NewPtr
;
111 mlu
->AllocatedEntries
= AllocatedEntries
;
117 // Search for a specific entry in the structure. Language and Country are used.
119 int SearchMLUEntry(cmsMLU
* mlu
, cmsUInt16Number LanguageCode
, cmsUInt16Number CountryCode
)
124 if (mlu
== NULL
) return -1;
126 // Iterate whole table
127 for (i
=0; i
< mlu
->UsedEntries
; i
++) {
129 if (mlu
->Entries
[i
].Country
== CountryCode
&&
130 mlu
->Entries
[i
].Language
== LanguageCode
) return (int) i
;
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.
140 cmsBool
AddMLUBlock(cmsMLU
* mlu
, cmsUInt32Number size
, const wchar_t *Block
,
141 cmsUInt16Number LanguageCode
, cmsUInt16Number CountryCode
)
143 cmsUInt32Number Offset
;
147 if (mlu
== NULL
) return FALSE
;
149 // Is there any room available?
150 if (mlu
->UsedEntries
>= mlu
->AllocatedEntries
) {
151 if (!GrowMLUtable(mlu
)) return FALSE
;
154 // Only one ASCII string
155 if (SearchMLUEntry(mlu
, LanguageCode
, CountryCode
) >= 0) return FALSE
; // Only one is allowed!
158 while ((mlu
->PoolSize
- mlu
->PoolUsed
) < size
) {
160 if (!GrowMLUpool(mlu
)) return FALSE
;
163 Offset
= mlu
->PoolUsed
;
165 Ptr
= (cmsUInt8Number
*) mlu
->MemPool
;
166 if (Ptr
== NULL
) return FALSE
;
169 memmove(Ptr
+ Offset
, Block
, size
);
170 mlu
->PoolUsed
+= size
;
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
;
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
184 cmsUInt16Number
strTo16(const char str
[3])
186 const cmsUInt8Number
* ptr8
;
189 // For non-existent strings
190 if (str
== NULL
) return 0;
191 ptr8
= (const cmsUInt8Number
*)str
;
192 n
= (cmsUInt16Number
)(((cmsUInt16Number
)ptr8
[0] << 8) | ptr8
[1]);
198 void strFrom16(char str
[3], cmsUInt16Number n
)
200 str
[0] = (char)(n
>> 8);
206 // Add an ASCII entry. Do not add any \0 termination (ICC1v43_2010-12.pdf page 61)
207 // In the case the user explicitly sets an empty string, we force a \0
208 cmsBool CMSEXPORT
cmsMLUsetASCII(cmsMLU
* mlu
, const char LanguageCode
[3], const char CountryCode
[3], const char* ASCIIString
)
210 cmsUInt32Number i
, len
= (cmsUInt32Number
) strlen(ASCIIString
);
213 cmsUInt16Number Lang
= strTo16(LanguageCode
);
214 cmsUInt16Number Cntry
= strTo16(CountryCode
);
216 if (mlu
== NULL
) return FALSE
;
218 // len == 0 would prevent operation, so we set a empty string pointing to zero
224 WStr
= (wchar_t*) _cmsCalloc(mlu
->ContextID
, len
, sizeof(wchar_t));
225 if (WStr
== NULL
) return FALSE
;
227 for (i
=0; i
< len
; i
++)
228 WStr
[i
] = (wchar_t) ASCIIString
[i
];
230 rc
= AddMLUBlock(mlu
, len
* sizeof(wchar_t), WStr
, Lang
, Cntry
);
232 _cmsFree(mlu
->ContextID
, WStr
);
237 // We don't need any wcs support library
239 cmsUInt32Number
mywcslen(const wchar_t *s
)
247 return (cmsUInt32Number
)(p
- s
);
250 // Add a wide entry. Do not add any \0 terminator (ICC1v43_2010-12.pdf page 61)
251 cmsBool CMSEXPORT
cmsMLUsetWide(cmsMLU
* mlu
, const char Language
[3], const char Country
[3], const wchar_t* WideString
)
253 cmsUInt16Number Lang
= strTo16(Language
);
254 cmsUInt16Number Cntry
= strTo16(Country
);
257 if (mlu
== NULL
) return FALSE
;
258 if (WideString
== NULL
) return FALSE
;
260 len
= (cmsUInt32Number
) (mywcslen(WideString
)) * sizeof(wchar_t);
262 len
= sizeof(wchar_t);
264 return AddMLUBlock(mlu
, len
, WideString
, Lang
, Cntry
);
267 // Duplicating a MLU is as easy as copying all members
268 cmsMLU
* CMSEXPORT
cmsMLUdup(const cmsMLU
* mlu
)
270 cmsMLU
* NewMlu
= NULL
;
272 // Duplicating a NULL obtains a NULL
273 if (mlu
== NULL
) return NULL
;
275 NewMlu
= cmsMLUalloc(mlu
->ContextID
, mlu
->UsedEntries
);
276 if (NewMlu
== NULL
) return NULL
;
278 // Should never happen
279 if (NewMlu
->AllocatedEntries
< mlu
->UsedEntries
)
283 if (NewMlu
->Entries
== NULL
|| mlu
->Entries
== NULL
) goto Error
;
285 memmove(NewMlu
->Entries
, mlu
->Entries
, mlu
->UsedEntries
* sizeof(_cmsMLUentry
));
286 NewMlu
->UsedEntries
= mlu
->UsedEntries
;
288 // The MLU may be empty
289 if (mlu
->PoolUsed
== 0) {
290 NewMlu
->MemPool
= NULL
;
294 NewMlu
->MemPool
= _cmsMalloc(mlu
->ContextID
, mlu
->PoolUsed
);
295 if (NewMlu
->MemPool
== NULL
) goto Error
;
298 NewMlu
->PoolSize
= mlu
->PoolUsed
;
300 if (NewMlu
->MemPool
== NULL
|| mlu
->MemPool
== NULL
) goto Error
;
302 memmove(NewMlu
->MemPool
, mlu
->MemPool
, mlu
->PoolUsed
);
303 NewMlu
->PoolUsed
= mlu
->PoolUsed
;
309 if (NewMlu
!= NULL
) cmsMLUfree(NewMlu
);
313 // Free any used memory
314 void CMSEXPORT
cmsMLUfree(cmsMLU
* mlu
)
318 if (mlu
-> Entries
) _cmsFree(mlu
->ContextID
, mlu
->Entries
);
319 if (mlu
-> MemPool
) _cmsFree(mlu
->ContextID
, mlu
->MemPool
);
321 _cmsFree(mlu
->ContextID
, mlu
);
326 // The algorithm first searches for an exact match of country and language, if not found it uses
327 // the Language. If none is found, first entry is used instead.
329 const wchar_t* _cmsMLUgetWide(const cmsMLU
* mlu
,
330 cmsUInt32Number
*len
,
331 cmsUInt16Number LanguageCode
, cmsUInt16Number CountryCode
,
332 cmsUInt16Number
* UsedLanguageCode
, cmsUInt16Number
* UsedCountryCode
)
338 if (mlu
== NULL
) return NULL
;
340 if (mlu
-> AllocatedEntries
<= 0) return NULL
;
342 for (i
=0; i
< mlu
->UsedEntries
; i
++) {
344 v
= mlu
->Entries
+ i
;
346 if (v
-> Language
== LanguageCode
) {
348 if (Best
== -1) Best
= (int) i
;
350 if (v
-> Country
== CountryCode
) {
352 if (UsedLanguageCode
!= NULL
) *UsedLanguageCode
= v
->Language
;
353 if (UsedCountryCode
!= NULL
) *UsedCountryCode
= v
->Country
;
355 if (len
!= NULL
) *len
= v
->Len
;
357 return (wchar_t*) ((cmsUInt8Number
*) mlu
->MemPool
+ v
-> StrW
); // Found exact match
362 // No string found. Return First one
366 v
= mlu
->Entries
+ Best
;
368 if (UsedLanguageCode
!= NULL
) *UsedLanguageCode
= v
->Language
;
369 if (UsedCountryCode
!= NULL
) *UsedCountryCode
= v
->Country
;
371 if (len
!= NULL
) *len
= v
->Len
;
373 if (v
->StrW
+ v
->Len
> mlu
->PoolSize
) return NULL
;
375 return(wchar_t*) ((cmsUInt8Number
*) mlu
->MemPool
+ v
->StrW
);
379 // Obtain an ASCII representation of the wide string. Setting buffer to NULL returns the len
380 cmsUInt32Number CMSEXPORT
cmsMLUgetASCII(const cmsMLU
* mlu
,
381 const char LanguageCode
[3], const char CountryCode
[3],
382 char* Buffer
, cmsUInt32Number BufferSize
)
385 cmsUInt32Number StrLen
= 0;
386 cmsUInt32Number ASCIIlen
, i
;
388 cmsUInt16Number Lang
= strTo16(LanguageCode
);
389 cmsUInt16Number Cntry
= strTo16(CountryCode
);
392 if (mlu
== NULL
) return 0;
395 Wide
= _cmsMLUgetWide(mlu
, &StrLen
, Lang
, Cntry
, NULL
, NULL
);
396 if (Wide
== NULL
) return 0;
398 ASCIIlen
= StrLen
/ sizeof(wchar_t);
400 // Maybe we want only to know the len?
401 if (Buffer
== NULL
) return ASCIIlen
+ 1; // Note the zero at the end
403 // No buffer size means no data
404 if (BufferSize
<= 0) return 0;
406 // Some clipping may be required
407 if (BufferSize
< ASCIIlen
+ 1)
408 ASCIIlen
= BufferSize
- 1;
410 // Precess each character
411 for (i
=0; i
< ASCIIlen
; i
++) {
416 Buffer
[i
] = (char) Wide
[i
];
419 // We put a termination "\0"
420 Buffer
[ASCIIlen
] = 0;
424 // Obtain a wide representation of the MLU, on depending on current locale settings
425 cmsUInt32Number CMSEXPORT
cmsMLUgetWide(const cmsMLU
* mlu
,
426 const char LanguageCode
[3], const char CountryCode
[3],
427 wchar_t* Buffer
, cmsUInt32Number BufferSize
)
430 cmsUInt32Number StrLen
= 0;
432 cmsUInt16Number Lang
= strTo16(LanguageCode
);
433 cmsUInt16Number Cntry
= strTo16(CountryCode
);
436 if (mlu
== NULL
) return 0;
438 Wide
= _cmsMLUgetWide(mlu
, &StrLen
, Lang
, Cntry
, NULL
, NULL
);
439 if (Wide
== NULL
) return 0;
441 // Maybe we want only to know the len?
442 if (Buffer
== NULL
) return StrLen
+ sizeof(wchar_t);
444 // No buffer size means no data
445 if (BufferSize
<= 0) return 0;
447 // Some clipping may be required
448 if (BufferSize
< StrLen
+ sizeof(wchar_t))
449 StrLen
= BufferSize
- + sizeof(wchar_t);
451 memmove(Buffer
, Wide
, StrLen
);
452 Buffer
[StrLen
/ sizeof(wchar_t)] = 0;
454 return StrLen
+ sizeof(wchar_t);
458 // Get also the language and country
459 CMSAPI cmsBool CMSEXPORT
cmsMLUgetTranslation(const cmsMLU
* mlu
,
460 const char LanguageCode
[3], const char CountryCode
[3],
461 char ObtainedLanguage
[3], char ObtainedCountry
[3])
465 cmsUInt16Number Lang
= strTo16(LanguageCode
);
466 cmsUInt16Number Cntry
= strTo16(CountryCode
);
467 cmsUInt16Number ObtLang
, ObtCode
;
470 if (mlu
== NULL
) return FALSE
;
472 Wide
= _cmsMLUgetWide(mlu
, NULL
, Lang
, Cntry
, &ObtLang
, &ObtCode
);
473 if (Wide
== NULL
) return FALSE
;
475 // Get used language and code
476 strFrom16(ObtainedLanguage
, ObtLang
);
477 strFrom16(ObtainedCountry
, ObtCode
);
484 // Get the number of translations in the MLU object
485 cmsUInt32Number CMSEXPORT
cmsMLUtranslationsCount(const cmsMLU
* mlu
)
487 if (mlu
== NULL
) return 0;
488 return mlu
->UsedEntries
;
491 // Get the language and country codes for a specific MLU index
492 cmsBool CMSEXPORT
cmsMLUtranslationsCodes(const cmsMLU
* mlu
,
494 char LanguageCode
[3],
499 if (mlu
== NULL
) return FALSE
;
501 if (idx
>= mlu
->UsedEntries
) return FALSE
;
503 entry
= &mlu
->Entries
[idx
];
505 strFrom16(LanguageCode
, entry
->Language
);
506 strFrom16(CountryCode
, entry
->Country
);
512 // Named color lists --------------------------------------------------------------------------------------------
514 // Grow the list to keep at least NumElements
516 cmsBool
GrowNamedColorList(cmsNAMEDCOLORLIST
* v
)
518 cmsUInt32Number size
;
519 _cmsNAMEDCOLOR
* NewPtr
;
521 if (v
== NULL
) return FALSE
;
523 if (v
->Allocated
== 0)
524 size
= 64; // Initial guess
526 size
= v
->Allocated
* 2;
528 // Keep a maximum color lists can grow, 100K entries seems reasonable
529 if (size
> 1024 * 100) {
530 _cmsFree(v
->ContextID
, (void*) v
->List
);
535 NewPtr
= (_cmsNAMEDCOLOR
*) _cmsRealloc(v
->ContextID
, v
->List
, size
* sizeof(_cmsNAMEDCOLOR
));
540 v
->Allocated
= size
;
544 // Allocate a list for n elements
545 cmsNAMEDCOLORLIST
* CMSEXPORT
cmsAllocNamedColorList(cmsContext ContextID
, cmsUInt32Number n
, cmsUInt32Number ColorantCount
, const char* Prefix
, const char* Suffix
)
547 cmsNAMEDCOLORLIST
* v
;
549 if (ColorantCount
> cmsMAXCHANNELS
)
552 v
= (cmsNAMEDCOLORLIST
*)_cmsMallocZero(ContextID
, sizeof(cmsNAMEDCOLORLIST
));
553 if (v
== NULL
) return NULL
;
557 v
->ContextID
= ContextID
;
559 while (v
-> Allocated
< n
) {
560 if (!GrowNamedColorList(v
)) {
561 cmsFreeNamedColorList(v
);
566 strncpy(v
->Prefix
, Prefix
, sizeof(v
->Prefix
)-1);
567 strncpy(v
->Suffix
, Suffix
, sizeof(v
->Suffix
)-1);
568 v
->Prefix
[32] = v
->Suffix
[32] = 0;
570 v
-> ColorantCount
= ColorantCount
;
576 void CMSEXPORT
cmsFreeNamedColorList(cmsNAMEDCOLORLIST
* v
)
578 if (v
== NULL
) return;
579 if (v
->List
) _cmsFree(v
->ContextID
, v
->List
);
580 _cmsFree(v
->ContextID
, v
);
583 cmsNAMEDCOLORLIST
* CMSEXPORT
cmsDupNamedColorList(const cmsNAMEDCOLORLIST
* v
)
585 cmsNAMEDCOLORLIST
* NewNC
;
587 if (v
== NULL
) return NULL
;
589 NewNC
= cmsAllocNamedColorList(v
->ContextID
, v
-> nColors
, v
->ColorantCount
, v
->Prefix
, v
->Suffix
);
590 if (NewNC
== NULL
) return NULL
;
592 // For really large tables we need this
593 while (NewNC
->Allocated
< v
->Allocated
){
594 if (!GrowNamedColorList(NewNC
))
596 cmsFreeNamedColorList(NewNC
);
601 memmove(NewNC
->Prefix
, v
->Prefix
, sizeof(v
->Prefix
));
602 memmove(NewNC
->Suffix
, v
->Suffix
, sizeof(v
->Suffix
));
603 NewNC
->ColorantCount
= v
->ColorantCount
;
604 memmove(NewNC
->List
, v
->List
, v
->nColors
* sizeof(_cmsNAMEDCOLOR
));
605 NewNC
->nColors
= v
->nColors
;
610 // Append a color to a list. List pointer may change if reallocated
611 cmsBool CMSEXPORT
cmsAppendNamedColor(cmsNAMEDCOLORLIST
* NamedColorList
,
613 cmsUInt16Number PCS
[3], cmsUInt16Number Colorant
[cmsMAXCHANNELS
])
617 if (NamedColorList
== NULL
) return FALSE
;
619 if (NamedColorList
->nColors
+ 1 > NamedColorList
->Allocated
) {
620 if (!GrowNamedColorList(NamedColorList
)) return FALSE
;
623 for (i
=0; i
< NamedColorList
->ColorantCount
; i
++)
624 NamedColorList
->List
[NamedColorList
->nColors
].DeviceColorant
[i
] = Colorant
== NULL
? (cmsUInt16Number
)0 : Colorant
[i
];
626 for (i
=0; i
< 3; i
++)
627 NamedColorList
->List
[NamedColorList
->nColors
].PCS
[i
] = PCS
== NULL
? (cmsUInt16Number
) 0 : PCS
[i
];
631 strncpy(NamedColorList
->List
[NamedColorList
->nColors
].Name
, Name
, cmsMAX_PATH
-1);
632 NamedColorList
->List
[NamedColorList
->nColors
].Name
[cmsMAX_PATH
-1] = 0;
636 NamedColorList
->List
[NamedColorList
->nColors
].Name
[0] = 0;
639 NamedColorList
->nColors
++;
643 // Returns number of elements
644 cmsUInt32Number CMSEXPORT
cmsNamedColorCount(const cmsNAMEDCOLORLIST
* NamedColorList
)
646 if (NamedColorList
== NULL
) return 0;
647 return NamedColorList
->nColors
;
650 // Info about a given color
651 cmsBool CMSEXPORT
cmsNamedColorInfo(const cmsNAMEDCOLORLIST
* NamedColorList
, cmsUInt32Number nColor
,
655 cmsUInt16Number
* PCS
,
656 cmsUInt16Number
* Colorant
)
658 if (NamedColorList
== NULL
) return FALSE
;
660 if (nColor
>= cmsNamedColorCount(NamedColorList
)) return FALSE
;
662 // strcpy instead of strncpy because many apps are using small buffers
663 if (Name
) strcpy(Name
, NamedColorList
->List
[nColor
].Name
);
664 if (Prefix
) strcpy(Prefix
, NamedColorList
->Prefix
);
665 if (Suffix
) strcpy(Suffix
, NamedColorList
->Suffix
);
667 memmove(PCS
, NamedColorList
->List
[nColor
].PCS
, 3*sizeof(cmsUInt16Number
));
670 memmove(Colorant
, NamedColorList
->List
[nColor
].DeviceColorant
,
671 sizeof(cmsUInt16Number
) * NamedColorList
->ColorantCount
);
677 // Search for a given color name (no prefix or suffix)
678 cmsInt32Number CMSEXPORT
cmsNamedColorIndex(const cmsNAMEDCOLORLIST
* NamedColorList
, const char* Name
)
683 if (NamedColorList
== NULL
) return -1;
684 n
= cmsNamedColorCount(NamedColorList
);
685 for (i
=0; i
< n
; i
++) {
686 if (cmsstrcasecmp(Name
, NamedColorList
->List
[i
].Name
) == 0)
687 return (cmsInt32Number
) i
;
693 // MPE support -----------------------------------------------------------------------------------------------------------------
696 void FreeNamedColorList(cmsStage
* mpe
)
698 cmsNAMEDCOLORLIST
* List
= (cmsNAMEDCOLORLIST
*) mpe
->Data
;
699 cmsFreeNamedColorList(List
);
703 void* DupNamedColorList(cmsStage
* mpe
)
705 cmsNAMEDCOLORLIST
* List
= (cmsNAMEDCOLORLIST
*) mpe
->Data
;
706 return cmsDupNamedColorList(List
);
710 void EvalNamedColorPCS(const cmsFloat32Number In
[], cmsFloat32Number Out
[], const cmsStage
*mpe
)
712 cmsNAMEDCOLORLIST
* NamedColorList
= (cmsNAMEDCOLORLIST
*) mpe
->Data
;
713 cmsUInt16Number index
= (cmsUInt16Number
) _cmsQuickSaturateWord(In
[0] * 65535.0);
715 if (index
>= NamedColorList
-> nColors
) {
716 cmsSignalError(NamedColorList
->ContextID
, cmsERROR_RANGE
, "Color %d out of range", index
);
717 Out
[0] = Out
[1] = Out
[2] = 0.0f
;
721 // Named color always uses Lab
722 Out
[0] = (cmsFloat32Number
) (NamedColorList
->List
[index
].PCS
[0] / 65535.0);
723 Out
[1] = (cmsFloat32Number
) (NamedColorList
->List
[index
].PCS
[1] / 65535.0);
724 Out
[2] = (cmsFloat32Number
) (NamedColorList
->List
[index
].PCS
[2] / 65535.0);
729 void EvalNamedColor(const cmsFloat32Number In
[], cmsFloat32Number Out
[], const cmsStage
*mpe
)
731 cmsNAMEDCOLORLIST
* NamedColorList
= (cmsNAMEDCOLORLIST
*) mpe
->Data
;
732 cmsUInt16Number index
= (cmsUInt16Number
) _cmsQuickSaturateWord(In
[0] * 65535.0);
735 if (index
>= NamedColorList
-> nColors
) {
736 cmsSignalError(NamedColorList
->ContextID
, cmsERROR_RANGE
, "Color %d out of range", index
);
737 for (j
= 0; j
< NamedColorList
->ColorantCount
; j
++)
742 for (j
=0; j
< NamedColorList
->ColorantCount
; j
++)
743 Out
[j
] = (cmsFloat32Number
) (NamedColorList
->List
[index
].DeviceColorant
[j
] / 65535.0);
748 // Named color lookup element
749 cmsStage
* CMSEXPORT
_cmsStageAllocNamedColor(cmsNAMEDCOLORLIST
* NamedColorList
, cmsBool UsePCS
)
751 return _cmsStageAllocPlaceholder(NamedColorList
->ContextID
,
752 cmsSigNamedColorElemType
,
753 1, UsePCS
? 3 : NamedColorList
->ColorantCount
,
754 UsePCS
? EvalNamedColorPCS
: EvalNamedColor
,
757 cmsDupNamedColorList(NamedColorList
));
762 // Retrieve the named color list from a transform. Should be first element in the LUT
763 cmsNAMEDCOLORLIST
* CMSEXPORT
cmsGetNamedColorList(cmsHTRANSFORM xform
)
765 _cmsTRANSFORM
* v
= (_cmsTRANSFORM
*) xform
;
768 if (v
== NULL
) return NULL
;
769 if (v
->Lut
== NULL
) return NULL
;
771 mpe
= v
->Lut
->Elements
;
772 if (mpe
== NULL
) return NULL
;
774 if (mpe
->Type
!= cmsSigNamedColorElemType
) return NULL
;
775 return (cmsNAMEDCOLORLIST
*) mpe
->Data
;
779 // Profile sequence description routines -------------------------------------------------------------------------------------
781 cmsSEQ
* CMSEXPORT
cmsAllocProfileSequenceDescription(cmsContext ContextID
, cmsUInt32Number n
)
786 if (n
== 0) return NULL
;
788 // In a absolutely arbitrary way, I hereby decide to allow a maxim of 255 profiles linked
789 // in a devicelink. It makes not sense anyway and may be used for exploits, so let's close the door!
790 if (n
> 255) return NULL
;
792 Seq
= (cmsSEQ
*) _cmsMallocZero(ContextID
, sizeof(cmsSEQ
));
793 if (Seq
== NULL
) return NULL
;
795 Seq
-> ContextID
= ContextID
;
796 Seq
-> seq
= (cmsPSEQDESC
*) _cmsCalloc(ContextID
, n
, sizeof(cmsPSEQDESC
));
799 if (Seq
-> seq
== NULL
) {
800 _cmsFree(ContextID
, Seq
);
804 for (i
=0; i
< n
; i
++) {
805 Seq
-> seq
[i
].Manufacturer
= NULL
;
806 Seq
-> seq
[i
].Model
= NULL
;
807 Seq
-> seq
[i
].Description
= NULL
;
813 void CMSEXPORT
cmsFreeProfileSequenceDescription(cmsSEQ
* pseq
)
817 for (i
=0; i
< pseq
->n
; i
++) {
818 if (pseq
->seq
[i
].Manufacturer
!= NULL
) cmsMLUfree(pseq
->seq
[i
].Manufacturer
);
819 if (pseq
->seq
[i
].Model
!= NULL
) cmsMLUfree(pseq
->seq
[i
].Model
);
820 if (pseq
->seq
[i
].Description
!= NULL
) cmsMLUfree(pseq
->seq
[i
].Description
);
823 if (pseq
->seq
!= NULL
) _cmsFree(pseq
->ContextID
, pseq
->seq
);
824 _cmsFree(pseq
-> ContextID
, pseq
);
827 cmsSEQ
* CMSEXPORT
cmsDupProfileSequenceDescription(const cmsSEQ
* pseq
)
835 NewSeq
= (cmsSEQ
*) _cmsMalloc(pseq
-> ContextID
, sizeof(cmsSEQ
));
836 if (NewSeq
== NULL
) return NULL
;
839 NewSeq
-> seq
= (cmsPSEQDESC
*) _cmsCalloc(pseq
->ContextID
, pseq
->n
, sizeof(cmsPSEQDESC
));
840 if (NewSeq
->seq
== NULL
) goto Error
;
842 NewSeq
-> ContextID
= pseq
->ContextID
;
843 NewSeq
-> n
= pseq
->n
;
845 for (i
=0; i
< pseq
->n
; i
++) {
847 memmove(&NewSeq
->seq
[i
].attributes
, &pseq
->seq
[i
].attributes
, sizeof(cmsUInt64Number
));
849 NewSeq
->seq
[i
].deviceMfg
= pseq
->seq
[i
].deviceMfg
;
850 NewSeq
->seq
[i
].deviceModel
= pseq
->seq
[i
].deviceModel
;
851 memmove(&NewSeq
->seq
[i
].ProfileID
, &pseq
->seq
[i
].ProfileID
, sizeof(cmsProfileID
));
852 NewSeq
->seq
[i
].technology
= pseq
->seq
[i
].technology
;
854 NewSeq
->seq
[i
].Manufacturer
= cmsMLUdup(pseq
->seq
[i
].Manufacturer
);
855 NewSeq
->seq
[i
].Model
= cmsMLUdup(pseq
->seq
[i
].Model
);
856 NewSeq
->seq
[i
].Description
= cmsMLUdup(pseq
->seq
[i
].Description
);
864 cmsFreeProfileSequenceDescription(NewSeq
);
868 // Dictionaries --------------------------------------------------------------------------------------------------------
870 // Dictionaries are just very simple linked lists
873 typedef struct _cmsDICT_struct
{
875 cmsContext ContextID
;
879 // Allocate an empty dictionary
880 cmsHANDLE CMSEXPORT
cmsDictAlloc(cmsContext ContextID
)
882 _cmsDICT
* dict
= (_cmsDICT
*) _cmsMallocZero(ContextID
, sizeof(_cmsDICT
));
883 if (dict
== NULL
) return NULL
;
885 dict
->ContextID
= ContextID
;
886 return (cmsHANDLE
) dict
;
891 void CMSEXPORT
cmsDictFree(cmsHANDLE hDict
)
893 _cmsDICT
* dict
= (_cmsDICT
*) hDict
;
894 cmsDICTentry
*entry
, *next
;
896 _cmsAssert(dict
!= NULL
);
898 // Walk the list freeing all nodes
900 while (entry
!= NULL
) {
902 if (entry
->DisplayName
!= NULL
) cmsMLUfree(entry
->DisplayName
);
903 if (entry
->DisplayValue
!= NULL
) cmsMLUfree(entry
->DisplayValue
);
904 if (entry
->Name
!= NULL
) _cmsFree(dict
->ContextID
, entry
-> Name
);
905 if (entry
->Value
!= NULL
) _cmsFree(dict
->ContextID
, entry
-> Value
);
907 // Don't fall in the habitual trap...
909 _cmsFree(dict
->ContextID
, entry
);
914 _cmsFree(dict
->ContextID
, dict
);
918 // Duplicate a wide char string
920 wchar_t* DupWcs(cmsContext ContextID
, const wchar_t* ptr
)
922 if (ptr
== NULL
) return NULL
;
923 return (wchar_t*) _cmsDupMem(ContextID
, ptr
, (mywcslen(ptr
) + 1) * sizeof(wchar_t));
926 // Add a new entry to the linked list
927 cmsBool CMSEXPORT
cmsDictAddEntry(cmsHANDLE hDict
, const wchar_t* Name
, const wchar_t* Value
, const cmsMLU
*DisplayName
, const cmsMLU
*DisplayValue
)
929 _cmsDICT
* dict
= (_cmsDICT
*) hDict
;
932 _cmsAssert(dict
!= NULL
);
933 _cmsAssert(Name
!= NULL
);
935 entry
= (cmsDICTentry
*) _cmsMallocZero(dict
->ContextID
, sizeof(cmsDICTentry
));
936 if (entry
== NULL
) return FALSE
;
938 entry
->DisplayName
= cmsMLUdup(DisplayName
);
939 entry
->DisplayValue
= cmsMLUdup(DisplayValue
);
940 entry
->Name
= DupWcs(dict
->ContextID
, Name
);
941 entry
->Value
= DupWcs(dict
->ContextID
, Value
);
943 entry
->Next
= dict
->head
;
950 // Duplicates an existing dictionary
951 cmsHANDLE CMSEXPORT
cmsDictDup(cmsHANDLE hDict
)
953 _cmsDICT
* old_dict
= (_cmsDICT
*) hDict
;
957 _cmsAssert(old_dict
!= NULL
);
959 hNew
= cmsDictAlloc(old_dict
->ContextID
);
960 if (hNew
== NULL
) return NULL
;
962 // Walk the list freeing all nodes
963 entry
= old_dict
->head
;
964 while (entry
!= NULL
) {
966 if (!cmsDictAddEntry(hNew
, entry
->Name
, entry
->Value
, entry
->DisplayName
, entry
->DisplayValue
)) {
972 entry
= entry
-> Next
;
978 // Get a pointer to the linked list
979 const cmsDICTentry
* CMSEXPORT
cmsDictGetEntryList(cmsHANDLE hDict
)
981 _cmsDICT
* dict
= (_cmsDICT
*) hDict
;
983 if (dict
== NULL
) return NULL
;
987 // Helper For external languages
988 const cmsDICTentry
* CMSEXPORT
cmsDictNextEntry(const cmsDICTentry
* e
)
990 if (e
== NULL
) return NULL
;