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-2020 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 cmsFreeNamedColorList(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)) 604 { 605 cmsFreeNamedColorList(NewNC); 606 return NULL; 607 } 608 } 609 610 memmove(NewNC ->Prefix, v ->Prefix, sizeof(v ->Prefix)); 611 memmove(NewNC ->Suffix, v ->Suffix, sizeof(v ->Suffix)); 612 NewNC ->ColorantCount = v ->ColorantCount; 613 memmove(NewNC->List, v ->List, v->nColors * sizeof(_cmsNAMEDCOLOR)); 614 NewNC ->nColors = v ->nColors; 615 return NewNC; 616 } 617 618 619 // Append a color to a list. List pointer may change if reallocated 620 cmsBool CMSEXPORT cmsAppendNamedColor(cmsNAMEDCOLORLIST* NamedColorList, 621 const char* Name, 622 cmsUInt16Number PCS[3], cmsUInt16Number Colorant[cmsMAXCHANNELS]) 623 { 624 cmsUInt32Number i; 625 626 if (NamedColorList == NULL) return FALSE; 627 628 if (NamedColorList ->nColors + 1 > NamedColorList ->Allocated) { 629 if (!GrowNamedColorList(NamedColorList)) return FALSE; 630 } 631 632 for (i=0; i < NamedColorList ->ColorantCount; i++) 633 NamedColorList ->List[NamedColorList ->nColors].DeviceColorant[i] = Colorant == NULL ? (cmsUInt16Number)0 : Colorant[i]; 634 635 for (i=0; i < 3; i++) 636 NamedColorList ->List[NamedColorList ->nColors].PCS[i] = PCS == NULL ? (cmsUInt16Number) 0 : PCS[i]; 637 638 if (Name != NULL) { 639 640 strncpy(NamedColorList ->List[NamedColorList ->nColors].Name, Name, cmsMAX_PATH-1); 641 NamedColorList ->List[NamedColorList ->nColors].Name[cmsMAX_PATH-1] = 0; 642 643 } 644 else 645 NamedColorList ->List[NamedColorList ->nColors].Name[0] = 0; 646 647 648 NamedColorList ->nColors++; 649 return TRUE; 650 } 651 652 // Returns number of elements 653 cmsUInt32Number CMSEXPORT cmsNamedColorCount(const cmsNAMEDCOLORLIST* NamedColorList) 654 { 655 if (NamedColorList == NULL) return 0; 656 return NamedColorList ->nColors; 657 } 658 659 // Info aboout a given color 660 cmsBool CMSEXPORT cmsNamedColorInfo(const cmsNAMEDCOLORLIST* NamedColorList, cmsUInt32Number nColor, 661 char* Name, 662 char* Prefix, 663 char* Suffix, 664 cmsUInt16Number* PCS, 665 cmsUInt16Number* Colorant) 666 { 667 if (NamedColorList == NULL) return FALSE; 668 669 if (nColor >= cmsNamedColorCount(NamedColorList)) return FALSE; 670 671 // strcpy instead of strncpy because many apps are using small buffers 672 if (Name) strcpy(Name, NamedColorList->List[nColor].Name); 673 if (Prefix) strcpy(Prefix, NamedColorList->Prefix); 674 if (Suffix) strcpy(Suffix, NamedColorList->Suffix); 675 if (PCS) 676 memmove(PCS, NamedColorList ->List[nColor].PCS, 3*sizeof(cmsUInt16Number)); 677 678 if (Colorant) 679 memmove(Colorant, NamedColorList ->List[nColor].DeviceColorant, 680 sizeof(cmsUInt16Number) * NamedColorList ->ColorantCount); 681 682 683 return TRUE; 684 } 685 686 // Search for a given color name (no prefix or suffix) 687 cmsInt32Number CMSEXPORT cmsNamedColorIndex(const cmsNAMEDCOLORLIST* NamedColorList, const char* Name) 688 { 689 cmsUInt32Number i; 690 cmsUInt32Number n; 691 692 if (NamedColorList == NULL) return -1; 693 n = cmsNamedColorCount(NamedColorList); 694 for (i=0; i < n; i++) { 695 if (cmsstrcasecmp(Name, NamedColorList->List[i].Name) == 0) 696 return (cmsInt32Number) i; 697 } 698 699 return -1; 700 } 701 702 // MPE support ----------------------------------------------------------------------------------------------------------------- 703 704 static 705 void FreeNamedColorList(cmsStage* mpe) 706 { 707 cmsNAMEDCOLORLIST* List = (cmsNAMEDCOLORLIST*) mpe ->Data; 708 cmsFreeNamedColorList(List); 709 } 710 711 static 712 void* DupNamedColorList(cmsStage* mpe) 713 { 714 cmsNAMEDCOLORLIST* List = (cmsNAMEDCOLORLIST*) mpe ->Data; 715 return cmsDupNamedColorList(List); 716 } 717 718 static 719 void EvalNamedColorPCS(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) 720 { 721 cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) mpe ->Data; 722 cmsUInt16Number index = (cmsUInt16Number) _cmsQuickSaturateWord(In[0] * 65535.0); 723 724 if (index >= NamedColorList-> nColors) { 725 cmsSignalError(NamedColorList ->ContextID, cmsERROR_RANGE, "Color %d out of range", index); 726 Out[0] = Out[1] = Out[2] = 0.0f; 727 } 728 else { 729 730 // Named color always uses Lab 731 Out[0] = (cmsFloat32Number) (NamedColorList->List[index].PCS[0] / 65535.0); 732 Out[1] = (cmsFloat32Number) (NamedColorList->List[index].PCS[1] / 65535.0); 733 Out[2] = (cmsFloat32Number) (NamedColorList->List[index].PCS[2] / 65535.0); 734 } 735 } 736 737 static 738 void EvalNamedColor(const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe) 739 { 740 cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) mpe ->Data; 741 cmsUInt16Number index = (cmsUInt16Number) _cmsQuickSaturateWord(In[0] * 65535.0); 742 cmsUInt32Number j; 743 744 if (index >= NamedColorList-> nColors) { 745 cmsSignalError(NamedColorList ->ContextID, cmsERROR_RANGE, "Color %d out of range", index); 746 for (j = 0; j < NamedColorList->ColorantCount; j++) 747 Out[j] = 0.0f; 748 749 } 750 else { 751 for (j=0; j < NamedColorList ->ColorantCount; j++) 752 Out[j] = (cmsFloat32Number) (NamedColorList->List[index].DeviceColorant[j] / 65535.0); 753 } 754 } 755 756 757 // Named color lookup element 758 cmsStage* CMSEXPORT _cmsStageAllocNamedColor(cmsNAMEDCOLORLIST* NamedColorList, cmsBool UsePCS) 759 { 760 return _cmsStageAllocPlaceholder(NamedColorList ->ContextID, 761 cmsSigNamedColorElemType, 762 1, UsePCS ? 3 : NamedColorList ->ColorantCount, 763 UsePCS ? EvalNamedColorPCS : EvalNamedColor, 764 DupNamedColorList, 765 FreeNamedColorList, 766 cmsDupNamedColorList(NamedColorList)); 767 768 } 769 770 771 // Retrieve the named color list from a transform. Should be first element in the LUT 772 cmsNAMEDCOLORLIST* CMSEXPORT cmsGetNamedColorList(cmsHTRANSFORM xform) 773 { 774 _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform; 775 cmsStage* mpe = v ->Lut->Elements; 776 777 if (mpe ->Type != cmsSigNamedColorElemType) return NULL; 778 return (cmsNAMEDCOLORLIST*) mpe ->Data; 779 } 780 781 782 // Profile sequence description routines ------------------------------------------------------------------------------------- 783 784 cmsSEQ* CMSEXPORT cmsAllocProfileSequenceDescription(cmsContext ContextID, cmsUInt32Number n) 785 { 786 cmsSEQ* Seq; 787 cmsUInt32Number i; 788 789 if (n == 0) return NULL; 790 791 // In a absolutely arbitrary way, I hereby decide to allow a maxim of 255 profiles linked 792 // in a devicelink. It makes not sense anyway and may be used for exploits, so let's close the door! 793 if (n > 255) return NULL; 794 795 Seq = (cmsSEQ*) _cmsMallocZero(ContextID, sizeof(cmsSEQ)); 796 if (Seq == NULL) return NULL; 797 798 Seq -> ContextID = ContextID; 799 Seq -> seq = (cmsPSEQDESC*) _cmsCalloc(ContextID, n, sizeof(cmsPSEQDESC)); 800 Seq -> n = n; 801 802 if (Seq -> seq == NULL) { 803 _cmsFree(ContextID, Seq); 804 return NULL; 805 } 806 807 for (i=0; i < n; i++) { 808 Seq -> seq[i].Manufacturer = NULL; 809 Seq -> seq[i].Model = NULL; 810 Seq -> seq[i].Description = NULL; 811 } 812 813 return Seq; 814 } 815 816 void CMSEXPORT cmsFreeProfileSequenceDescription(cmsSEQ* pseq) 817 { 818 cmsUInt32Number i; 819 820 for (i=0; i < pseq ->n; i++) { 821 if (pseq ->seq[i].Manufacturer != NULL) cmsMLUfree(pseq ->seq[i].Manufacturer); 822 if (pseq ->seq[i].Model != NULL) cmsMLUfree(pseq ->seq[i].Model); 823 if (pseq ->seq[i].Description != NULL) cmsMLUfree(pseq ->seq[i].Description); 824 } 825 826 if (pseq ->seq != NULL) _cmsFree(pseq ->ContextID, pseq ->seq); 827 _cmsFree(pseq -> ContextID, pseq); 828 } 829 830 cmsSEQ* CMSEXPORT cmsDupProfileSequenceDescription(const cmsSEQ* pseq) 831 { 832 cmsSEQ *NewSeq; 833 cmsUInt32Number i; 834 835 if (pseq == NULL) 836 return NULL; 837 838 NewSeq = (cmsSEQ*) _cmsMalloc(pseq -> ContextID, sizeof(cmsSEQ)); 839 if (NewSeq == NULL) return NULL; 840 841 842 NewSeq -> seq = (cmsPSEQDESC*) _cmsCalloc(pseq ->ContextID, pseq ->n, sizeof(cmsPSEQDESC)); 843 if (NewSeq ->seq == NULL) goto Error; 844 845 NewSeq -> ContextID = pseq ->ContextID; 846 NewSeq -> n = pseq ->n; 847 848 for (i=0; i < pseq->n; i++) { 849 850 memmove(&NewSeq ->seq[i].attributes, &pseq ->seq[i].attributes, sizeof(cmsUInt64Number)); 851 852 NewSeq ->seq[i].deviceMfg = pseq ->seq[i].deviceMfg; 853 NewSeq ->seq[i].deviceModel = pseq ->seq[i].deviceModel; 854 memmove(&NewSeq ->seq[i].ProfileID, &pseq ->seq[i].ProfileID, sizeof(cmsProfileID)); 855 NewSeq ->seq[i].technology = pseq ->seq[i].technology; 856 857 NewSeq ->seq[i].Manufacturer = cmsMLUdup(pseq ->seq[i].Manufacturer); 858 NewSeq ->seq[i].Model = cmsMLUdup(pseq ->seq[i].Model); 859 NewSeq ->seq[i].Description = cmsMLUdup(pseq ->seq[i].Description); 860 861 } 862 863 return NewSeq; 864 865 Error: 866 867 cmsFreeProfileSequenceDescription(NewSeq); 868 return NULL; 869 } 870 871 // Dictionaries -------------------------------------------------------------------------------------------------------- 872 873 // Dictionaries are just very simple linked lists 874 875 876 typedef struct _cmsDICT_struct { 877 cmsDICTentry* head; 878 cmsContext ContextID; 879 } _cmsDICT; 880 881 882 // Allocate an empty dictionary 883 cmsHANDLE CMSEXPORT cmsDictAlloc(cmsContext ContextID) 884 { 885 _cmsDICT* dict = (_cmsDICT*) _cmsMallocZero(ContextID, sizeof(_cmsDICT)); 886 if (dict == NULL) return NULL; 887 888 dict ->ContextID = ContextID; 889 return (cmsHANDLE) dict; 890 891 } 892 893 // Dispose resources 894 void CMSEXPORT cmsDictFree(cmsHANDLE hDict) 895 { 896 _cmsDICT* dict = (_cmsDICT*) hDict; 897 cmsDICTentry *entry, *next; 898 899 _cmsAssert(dict != NULL); 900 901 // Walk the list freeing all nodes 902 entry = dict ->head; 903 while (entry != NULL) { 904 905 if (entry ->DisplayName != NULL) cmsMLUfree(entry ->DisplayName); 906 if (entry ->DisplayValue != NULL) cmsMLUfree(entry ->DisplayValue); 907 if (entry ->Name != NULL) _cmsFree(dict ->ContextID, entry -> Name); 908 if (entry ->Value != NULL) _cmsFree(dict ->ContextID, entry -> Value); 909 910 // Don't fall in the habitual trap... 911 next = entry ->Next; 912 _cmsFree(dict ->ContextID, entry); 913 914 entry = next; 915 } 916 917 _cmsFree(dict ->ContextID, dict); 918 } 919 920 921 // Duplicate a wide char string 922 static 923 wchar_t* DupWcs(cmsContext ContextID, const wchar_t* ptr) 924 { 925 if (ptr == NULL) return NULL; 926 return (wchar_t*) _cmsDupMem(ContextID, ptr, (mywcslen(ptr) + 1) * sizeof(wchar_t)); 927 } 928 929 // Add a new entry to the linked list 930 cmsBool CMSEXPORT cmsDictAddEntry(cmsHANDLE hDict, const wchar_t* Name, const wchar_t* Value, const cmsMLU *DisplayName, const cmsMLU *DisplayValue) 931 { 932 _cmsDICT* dict = (_cmsDICT*) hDict; 933 cmsDICTentry *entry; 934 935 _cmsAssert(dict != NULL); 936 _cmsAssert(Name != NULL); 937 938 entry = (cmsDICTentry*) _cmsMallocZero(dict ->ContextID, sizeof(cmsDICTentry)); 939 if (entry == NULL) return FALSE; 940 941 entry ->DisplayName = cmsMLUdup(DisplayName); 942 entry ->DisplayValue = cmsMLUdup(DisplayValue); 943 entry ->Name = DupWcs(dict ->ContextID, Name); 944 entry ->Value = DupWcs(dict ->ContextID, Value); 945 946 entry ->Next = dict ->head; 947 dict ->head = entry; 948 949 return TRUE; 950 } 951 952 953 // Duplicates an existing dictionary 954 cmsHANDLE CMSEXPORT cmsDictDup(cmsHANDLE hDict) 955 { 956 _cmsDICT* old_dict = (_cmsDICT*) hDict; 957 cmsHANDLE hNew; 958 cmsDICTentry *entry; 959 960 _cmsAssert(old_dict != NULL); 961 962 hNew = cmsDictAlloc(old_dict ->ContextID); 963 if (hNew == NULL) return NULL; 964 965 // Walk the list freeing all nodes 966 entry = old_dict ->head; 967 while (entry != NULL) { 968 969 if (!cmsDictAddEntry(hNew, entry ->Name, entry ->Value, entry ->DisplayName, entry ->DisplayValue)) { 970 971 cmsDictFree(hNew); 972 return NULL; 973 } 974 975 entry = entry -> Next; 976 } 977 978 return hNew; 979 } 980 981 // Get a pointer to the linked list 982 const cmsDICTentry* CMSEXPORT cmsDictGetEntryList(cmsHANDLE hDict) 983 { 984 _cmsDICT* dict = (_cmsDICT*) hDict; 985 986 if (dict == NULL) return NULL; 987 return dict ->head; 988 } 989 990 // Helper For external languages 991 const cmsDICTentry* CMSEXPORT cmsDictNextEntry(const cmsDICTentry* e) 992 { 993 if (e == NULL) return NULL; 994 return e ->Next; 995 }