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 |
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 | // 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. |
60 | static |
61 | cmsBool 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. |
90 | static |
91 | cmsBool 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. |
116 | static |
117 | int 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. |
137 | static |
138 | cmsBool 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 | |
182 | static |
183 | cmsUInt16Number 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 | |
191 | static |
192 | void 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) |
201 | cmsBool 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 |
225 | static |
226 | cmsUInt32Number 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) |
238 | cmsBool 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 |
252 | cmsMLU* 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 | |
291 | Error: |
292 | |
293 | if (NewMlu != NULL) cmsMLUfree(ContextID, NewMlu); |
294 | return NULL; |
295 | } |
296 | |
297 | // Free any used memory |
298 | void 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. |
312 | static |
313 | const 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 |
362 | cmsUInt32Number 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 |
408 | cmsUInt32Number 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 |
443 | CMSAPI 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 |
470 | cmsUInt32Number 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 |
478 | cmsBool 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 |
503 | static |
504 | cmsBool 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 |
533 | cmsNAMEDCOLORLIST* 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 |
559 | void 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 | |
566 | cmsNAMEDCOLORLIST* 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 |
590 | cmsBool 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 |
623 | cmsUInt32Number 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 |
631 | cmsBool 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) |
658 | cmsInt32Number 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 | |
675 | static |
676 | void FreeNamedColorList(cmsContext ContextID, cmsStage* mpe) |
677 | { |
678 | cmsNAMEDCOLORLIST* List = (cmsNAMEDCOLORLIST*) mpe ->Data; |
679 | cmsFreeNamedColorList(ContextID, List); |
680 | } |
681 | |
682 | static |
683 | void* DupNamedColorList(cmsContext ContextID, cmsStage* mpe) |
684 | { |
685 | cmsNAMEDCOLORLIST* List = (cmsNAMEDCOLORLIST*) mpe ->Data; |
686 | return cmsDupNamedColorList(ContextID, List); |
687 | } |
688 | |
689 | static |
690 | void 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 | |
708 | static |
709 | void 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 |
728 | cmsStage* 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 |
742 | cmsNAMEDCOLORLIST* 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 | |
754 | cmsSEQ* 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 | |
785 | void 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 | |
799 | cmsSEQ* 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 | |
833 | Error: |
834 | |
835 | cmsFreeProfileSequenceDescription(ContextID, NewSeq); |
836 | return NULL; |
837 | } |
838 | |
839 | // Dictionaries -------------------------------------------------------------------------------------------------------- |
840 | |
841 | // Dictionaries are just very simple linked lists |
842 | |
843 | |
844 | typedef struct _cmsDICT_struct { |
845 | cmsDICTentry* head; |
846 | } _cmsDICT; |
847 | |
848 | |
849 | // Allocate an empty dictionary |
850 | cmsHANDLE 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 |
860 | void 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 |
888 | static |
889 | wchar_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 |
896 | cmsBool 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 |
920 | cmsHANDLE 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 |
948 | const 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 |
958 | const 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 | |