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