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