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