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#include "lcms2_internal.h" 27 28// Generic I/O, tag dictionary management, profile struct 29 30// IOhandlers are abstractions used by littleCMS to read from whatever file, stream, 31// memory block or any storage. Each IOhandler provides implementations for read, 32// write, seek and tell functions. LittleCMS code deals with IO across those objects. 33// In this way, is easier to add support for new storage media. 34 35// NULL stream, for taking care of used space ------------------------------------- 36 37// NULL IOhandler basically does nothing but keep track on how many bytes have been 38// written. This is handy when creating profiles, where the file size is needed in the 39// header. Then, whole profile is serialized across NULL IOhandler and a second pass 40// writes the bytes to the pertinent IOhandler. 41 42typedef struct { 43 cmsUInt32Number Pointer; // Points to current location 44} FILENULL; 45 46static 47cmsUInt32Number NULLRead(cmsIOHANDLER* iohandler, void *Buffer, cmsUInt32Number size, cmsUInt32Number count) 48{ 49 FILENULL* ResData = (FILENULL*) iohandler ->stream; 50 51 cmsUInt32Number len = size * count; 52 ResData -> Pointer += len; 53 return count; 54 55 cmsUNUSED_PARAMETER(Buffer); 56} 57 58static 59cmsBool NULLSeek(cmsIOHANDLER* iohandler, cmsUInt32Number offset) 60{ 61 FILENULL* ResData = (FILENULL*) iohandler ->stream; 62 63 ResData ->Pointer = offset; 64 return TRUE; 65} 66 67static 68cmsUInt32Number NULLTell(cmsIOHANDLER* iohandler) 69{ 70 FILENULL* ResData = (FILENULL*) iohandler ->stream; 71 return ResData -> Pointer; 72} 73 74static 75cmsBool NULLWrite(cmsIOHANDLER* iohandler, cmsUInt32Number size, const void *Ptr) 76{ 77 FILENULL* ResData = (FILENULL*) iohandler ->stream; 78 79 ResData ->Pointer += size; 80 if (ResData ->Pointer > iohandler->UsedSpace) 81 iohandler->UsedSpace = ResData ->Pointer; 82 83 return TRUE; 84 85 cmsUNUSED_PARAMETER(Ptr); 86} 87 88static 89cmsBool NULLClose(cmsIOHANDLER* iohandler) 90{ 91 FILENULL* ResData = (FILENULL*) iohandler ->stream; 92 93 _cmsFree(iohandler ->ContextID, ResData); 94 _cmsFree(iohandler ->ContextID, iohandler); 95 return TRUE; 96} 97 98// The NULL IOhandler creator 99cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromNULL(cmsContext ContextID) 100{ 101 struct _cms_io_handler* iohandler = NULL; 102 FILENULL* fm = NULL; 103 104 iohandler = (struct _cms_io_handler*) _cmsMallocZero(ContextID, sizeof(struct _cms_io_handler)); 105 if (iohandler == NULL) return NULL; 106 107 fm = (FILENULL*) _cmsMallocZero(ContextID, sizeof(FILENULL)); 108 if (fm == NULL) goto Error; 109 110 fm ->Pointer = 0; 111 112 iohandler ->ContextID = ContextID; 113 iohandler ->stream = (void*) fm; 114 iohandler ->UsedSpace = 0; 115 iohandler ->ReportedSize = 0; 116 iohandler ->PhysicalFile[0] = 0; 117 118 iohandler ->Read = NULLRead; 119 iohandler ->Seek = NULLSeek; 120 iohandler ->Close = NULLClose; 121 iohandler ->Tell = NULLTell; 122 iohandler ->Write = NULLWrite; 123 124 return iohandler; 125 126Error: 127 if (iohandler) _cmsFree(ContextID, iohandler); 128 return NULL; 129 130} 131 132 133// Memory-based stream -------------------------------------------------------------- 134 135// Those functions implements an iohandler which takes a block of memory as storage medium. 136 137typedef struct { 138 cmsUInt8Number* Block; // Points to allocated memory 139 cmsUInt32Number Size; // Size of allocated memory 140 cmsUInt32Number Pointer; // Points to current location 141 int FreeBlockOnClose; // As title 142 143} FILEMEM; 144 145static 146cmsUInt32Number MemoryRead(struct _cms_io_handler* iohandler, void *Buffer, cmsUInt32Number size, cmsUInt32Number count) 147{ 148 FILEMEM* ResData = (FILEMEM*) iohandler ->stream; 149 cmsUInt8Number* Ptr; 150 cmsUInt32Number len = size * count; 151 152 if (ResData -> Pointer + len > ResData -> Size){ 153 154 len = (ResData -> Size - ResData -> Pointer); 155 cmsSignalError(iohandler ->ContextID, cmsERROR_READ, "Read from memory error. Got %d bytes, block should be of %d bytes", len, count * size); 156 return 0; 157 } 158 159 Ptr = ResData -> Block; 160 Ptr += ResData -> Pointer; 161 memmove(Buffer, Ptr, len); 162 ResData -> Pointer += len; 163 164 return count; 165} 166 167// SEEK_CUR is assumed 168static 169cmsBool MemorySeek(struct _cms_io_handler* iohandler, cmsUInt32Number offset) 170{ 171 FILEMEM* ResData = (FILEMEM*) iohandler ->stream; 172 173 if (offset > ResData ->Size) { 174 cmsSignalError(iohandler ->ContextID, cmsERROR_SEEK, "Too few data; probably corrupted profile"); 175 return FALSE; 176 } 177 178 ResData ->Pointer = offset; 179 return TRUE; 180} 181 182// Tell for memory 183static 184cmsUInt32Number MemoryTell(struct _cms_io_handler* iohandler) 185{ 186 FILEMEM* ResData = (FILEMEM*) iohandler ->stream; 187 188 if (ResData == NULL) return 0; 189 return ResData -> Pointer; 190} 191 192 193// Writes data to memory, also keeps used space for further reference. 194static 195cmsBool MemoryWrite(struct _cms_io_handler* iohandler, cmsUInt32Number size, const void *Ptr) 196{ 197 FILEMEM* ResData = (FILEMEM*) iohandler ->stream; 198 199 if (ResData == NULL) return FALSE; // Housekeeping 200 201 // Check for available space. Clip. 202 if (ResData->Pointer + size > ResData->Size) { 203 size = ResData ->Size - ResData->Pointer; 204 } 205 206 if (size == 0) return TRUE; // Write zero bytes is ok, but does nothing 207 208 memmove(ResData ->Block + ResData ->Pointer, Ptr, size); 209 ResData ->Pointer += size; 210 211 if (ResData ->Pointer > iohandler->UsedSpace) 212 iohandler->UsedSpace = ResData ->Pointer; 213 214 return TRUE; 215} 216 217 218static 219cmsBool MemoryClose(struct _cms_io_handler* iohandler) 220{ 221 FILEMEM* ResData = (FILEMEM*) iohandler ->stream; 222 223 if (ResData ->FreeBlockOnClose) { 224 225 if (ResData ->Block) _cmsFree(iohandler ->ContextID, ResData ->Block); 226 } 227 228 _cmsFree(iohandler ->ContextID, ResData); 229 _cmsFree(iohandler ->ContextID, iohandler); 230 231 return TRUE; 232} 233 234// Create a iohandler for memory block. AccessMode=='r' assumes the iohandler is going to read, and makes 235// a copy of the memory block for letting user to free the memory after invoking open profile. In write 236// mode ("w"), Buffere points to the begin of memory block to be written. 237cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromMem(cmsContext ContextID, void *Buffer, cmsUInt32Number size, const char* AccessMode) 238{ 239 cmsIOHANDLER* iohandler = NULL; 240 FILEMEM* fm = NULL; 241 242 _cmsAssert(AccessMode != NULL); 243 244 iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER)); 245 if (iohandler == NULL) return NULL; 246 247 switch (*AccessMode) { 248 249 case 'r': 250 fm = (FILEMEM*) _cmsMallocZero(ContextID, sizeof(FILEMEM)); 251 if (fm == NULL) goto Error; 252 253 if (Buffer == NULL) { 254 cmsSignalError(ContextID, cmsERROR_READ, "Couldn't read profile from NULL pointer"); 255 goto Error; 256 } 257 258 fm ->Block = (cmsUInt8Number*) _cmsMalloc(ContextID, size); 259 if (fm ->Block == NULL) { 260 261 _cmsFree(ContextID, fm); 262 _cmsFree(ContextID, iohandler); 263 cmsSignalError(ContextID, cmsERROR_READ, "Couldn't allocate %ld bytes for profile", size); 264 return NULL; 265 } 266 267 268 memmove(fm->Block, Buffer, size); 269 fm ->FreeBlockOnClose = TRUE; 270 fm ->Size = size; 271 fm ->Pointer = 0; 272 iohandler -> ReportedSize = size; 273 break; 274 275 case 'w': 276 fm = (FILEMEM*) _cmsMallocZero(ContextID, sizeof(FILEMEM)); 277 if (fm == NULL) goto Error; 278 279 fm ->Block = (cmsUInt8Number*) Buffer; 280 fm ->FreeBlockOnClose = FALSE; 281 fm ->Size = size; 282 fm ->Pointer = 0; 283 iohandler -> ReportedSize = 0; 284 break; 285 286 default: 287 cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown access mode '%c'", *AccessMode); 288 return NULL; 289 } 290 291 iohandler ->ContextID = ContextID; 292 iohandler ->stream = (void*) fm; 293 iohandler ->UsedSpace = 0; 294 iohandler ->PhysicalFile[0] = 0; 295 296 iohandler ->Read = MemoryRead; 297 iohandler ->Seek = MemorySeek; 298 iohandler ->Close = MemoryClose; 299 iohandler ->Tell = MemoryTell; 300 iohandler ->Write = MemoryWrite; 301 302 return iohandler; 303 304Error: 305 if (fm) _cmsFree(ContextID, fm); 306 if (iohandler) _cmsFree(ContextID, iohandler); 307 return NULL; 308} 309 310// File-based stream ------------------------------------------------------- 311 312// Read count elements of size bytes each. Return number of elements read 313static 314cmsUInt32Number FileRead(cmsIOHANDLER* iohandler, void *Buffer, cmsUInt32Number size, cmsUInt32Number count) 315{ 316 cmsUInt32Number nReaded = (cmsUInt32Number) fread(Buffer, size, count, (FILE*) iohandler->stream); 317 318 if (nReaded != count) { 319 cmsSignalError(iohandler ->ContextID, cmsERROR_FILE, "Read error. Got %d bytes, block should be of %d bytes", nReaded * size, count * size); 320 return 0; 321 } 322 323 return nReaded; 324} 325 326// Postion file pointer in the file 327static 328cmsBool FileSeek(cmsIOHANDLER* iohandler, cmsUInt32Number offset) 329{ 330 if (fseek((FILE*) iohandler ->stream, (long) offset, SEEK_SET) != 0) { 331 332 cmsSignalError(iohandler ->ContextID, cmsERROR_FILE, "Seek error; probably corrupted file"); 333 return FALSE; 334 } 335 336 return TRUE; 337} 338 339// Returns file pointer position 340static 341cmsUInt32Number FileTell(cmsIOHANDLER* iohandler) 342{ 343 return (cmsUInt32Number) ftell((FILE*)iohandler ->stream); 344} 345 346// Writes data to stream, also keeps used space for further reference. Returns TRUE on success, FALSE on error 347static 348cmsBool FileWrite(cmsIOHANDLER* iohandler, cmsUInt32Number size, const void* Buffer) 349{ 350 if (size == 0) return TRUE; // We allow to write 0 bytes, but nothing is written 351 352 iohandler->UsedSpace += size; 353 return (fwrite(Buffer, size, 1, (FILE*) iohandler->stream) == 1); 354} 355 356// Closes the file 357static 358cmsBool FileClose(cmsIOHANDLER* iohandler) 359{ 360 if (fclose((FILE*) iohandler ->stream) != 0) return FALSE; 361 _cmsFree(iohandler ->ContextID, iohandler); 362 return TRUE; 363} 364 365// Create a iohandler for disk based files. 366cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromFile(cmsContext ContextID, const char* FileName, const char* AccessMode) 367{ 368 cmsIOHANDLER* iohandler = NULL; 369 FILE* fm = NULL; 370 371 _cmsAssert(FileName != NULL); 372 _cmsAssert(AccessMode != NULL); 373 374 iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER)); 375 if (iohandler == NULL) return NULL; 376 377 switch (*AccessMode) { 378 379 case 'r': 380 fm = fopen(FileName, "rb"); 381 if (fm == NULL) { 382 _cmsFree(ContextID, iohandler); 383 cmsSignalError(ContextID, cmsERROR_FILE, "File '%s' not found", FileName); 384 return NULL; 385 } 386 iohandler -> ReportedSize = (cmsUInt32Number) cmsfilelength(fm); 387 break; 388 389 case 'w': 390 fm = fopen(FileName, "wb"); 391 if (fm == NULL) { 392 _cmsFree(ContextID, iohandler); 393 cmsSignalError(ContextID, cmsERROR_FILE, "Couldn't create '%s'", FileName); 394 return NULL; 395 } 396 iohandler -> ReportedSize = 0; 397 break; 398 399 default: 400 _cmsFree(ContextID, iohandler); 401 cmsSignalError(ContextID, cmsERROR_FILE, "Unknown access mode '%c'", *AccessMode); 402 return NULL; 403 } 404 405 iohandler ->ContextID = ContextID; 406 iohandler ->stream = (void*) fm; 407 iohandler ->UsedSpace = 0; 408 409 // Keep track of the original file 410 strncpy(iohandler -> PhysicalFile, FileName, sizeof(iohandler -> PhysicalFile)-1); 411 iohandler -> PhysicalFile[sizeof(iohandler -> PhysicalFile)-1] = 0; 412 413 iohandler ->Read = FileRead; 414 iohandler ->Seek = FileSeek; 415 iohandler ->Close = FileClose; 416 iohandler ->Tell = FileTell; 417 iohandler ->Write = FileWrite; 418 419 return iohandler; 420} 421 422// Create a iohandler for stream based files 423cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromStream(cmsContext ContextID, FILE* Stream) 424{ 425 cmsIOHANDLER* iohandler = NULL; 426 427 iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER)); 428 if (iohandler == NULL) return NULL; 429 430 iohandler -> ContextID = ContextID; 431 iohandler -> stream = (void*) Stream; 432 iohandler -> UsedSpace = 0; 433 iohandler -> ReportedSize = (cmsUInt32Number) cmsfilelength(Stream); 434 iohandler -> PhysicalFile[0] = 0; 435 436 iohandler ->Read = FileRead; 437 iohandler ->Seek = FileSeek; 438 iohandler ->Close = FileClose; 439 iohandler ->Tell = FileTell; 440 iohandler ->Write = FileWrite; 441 442 return iohandler; 443} 444 445 446 447// Close an open IO handler 448cmsBool CMSEXPORT cmsCloseIOhandler(cmsIOHANDLER* io) 449{ 450 return io -> Close(io); 451} 452 453// ------------------------------------------------------------------------------------------------------- 454 455#ifdef _WIN32_WCE 456time_t wceex_time(time_t *timer); 457struct tm * wceex_gmtime(const time_t *timer); 458 459#define time wceex_time 460#define gmtime wceex_gmtime 461#endif 462 463// Creates an empty structure holding all required parameters 464cmsHPROFILE CMSEXPORT cmsCreateProfilePlaceholder(cmsContext ContextID) 465{ 466 time_t now = time(NULL); 467 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) _cmsMallocZero(ContextID, sizeof(_cmsICCPROFILE)); 468 if (Icc == NULL) return NULL; 469 470 Icc ->ContextID = ContextID; 471 472 // Set it to empty 473 Icc -> TagCount = 0; 474 475 // Set default version 476 Icc ->Version = 0x02100000; 477 478 // Set creation date/time 479 memmove(&Icc ->Created, gmtime(&now), sizeof(Icc ->Created)); 480 481 // Create a mutex if the user provided proper plugin. NULL otherwise 482 Icc ->UsrMutex = _cmsCreateMutex(ContextID); 483 484 // Return the handle 485 return (cmsHPROFILE) Icc; 486} 487 488cmsContext CMSEXPORT cmsGetProfileContextID(cmsHPROFILE hProfile) 489{ 490 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 491 492 if (Icc == NULL) return NULL; 493 return Icc -> ContextID; 494} 495 496 497// Return the number of tags 498cmsInt32Number CMSEXPORT cmsGetTagCount(cmsHPROFILE hProfile) 499{ 500 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 501 if (Icc == NULL) return -1; 502 503 return Icc->TagCount; 504} 505 506// Return the tag signature of a given tag number 507cmsTagSignature CMSEXPORT cmsGetTagSignature(cmsHPROFILE hProfile, cmsUInt32Number n) 508{ 509 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 510 511 if (n > Icc->TagCount) return (cmsTagSignature) 0; // Mark as not available 512 if (n >= MAX_TABLE_TAG) return (cmsTagSignature) 0; // As double check 513 514 return Icc ->TagNames[n]; 515} 516 517 518static 519int SearchOneTag(_cmsICCPROFILE* Profile, cmsTagSignature sig) 520{ 521 cmsUInt32Number i; 522 523 for (i=0; i < Profile -> TagCount; i++) { 524 525 if (sig == Profile -> TagNames[i]) 526 return i; 527 } 528 529 return -1; 530} 531 532// Search for a specific tag in tag dictionary. Returns position or -1 if tag not found. 533// If followlinks is turned on, then the position of the linked tag is returned 534int _cmsSearchTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, cmsBool lFollowLinks) 535{ 536 int n; 537 cmsTagSignature LinkedSig; 538 539 do { 540 541 // Search for given tag in ICC profile directory 542 n = SearchOneTag(Icc, sig); 543 if (n < 0) 544 return -1; // Not found 545 546 if (!lFollowLinks) 547 return n; // Found, don't follow links 548 549 // Is this a linked tag? 550 LinkedSig = Icc ->TagLinked[n]; 551 552 // Yes, follow link 553 if (LinkedSig != (cmsTagSignature) 0) { 554 // fix bug mantis id#0055942 555 // assume that TRCTag and ColorantTag can't be linked. 556 // Xiaochuan Liu 2014-04-23 557 if ((sig == cmsSigRedTRCTag || sig == cmsSigGreenTRCTag || sig == cmsSigBlueTRCTag) && 558 (LinkedSig == cmsSigRedColorantTag || LinkedSig == cmsSigGreenColorantTag || LinkedSig == cmsSigBlueColorantTag)) 559 { 560 return n; 561 } 562 sig = LinkedSig; 563 } 564 565 } while (LinkedSig != (cmsTagSignature) 0); 566 567 return n; 568} 569 570// Deletes a tag entry 571 572static 573void _cmsDeleteTagByPos(_cmsICCPROFILE* Icc, int i) 574{ 575 _cmsAssert(Icc != NULL); 576 _cmsAssert(i >= 0); 577 578 579 if (Icc -> TagPtrs[i] != NULL) { 580 581 // Free previous version 582 if (Icc ->TagSaveAsRaw[i]) { 583 _cmsFree(Icc ->ContextID, Icc ->TagPtrs[i]); 584 } 585 else { 586 cmsTagTypeHandler* TypeHandler = Icc ->TagTypeHandlers[i]; 587 588 if (TypeHandler != NULL) { 589 590 cmsTagTypeHandler LocalTypeHandler = *TypeHandler; 591 LocalTypeHandler.ContextID = Icc ->ContextID; // As an additional parameter 592 LocalTypeHandler.ICCVersion = Icc ->Version; 593 LocalTypeHandler.FreePtr(&LocalTypeHandler, Icc -> TagPtrs[i]); 594 Icc ->TagPtrs[i] = NULL; 595 } 596 } 597 598 } 599} 600 601 602// Creates a new tag entry 603static 604cmsBool _cmsNewTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, int* NewPos) 605{ 606 int i; 607 608 // Search for the tag 609 i = _cmsSearchTag(Icc, sig, FALSE); 610 if (i >= 0) { 611 612 // Already exists? delete it 613 _cmsDeleteTagByPos(Icc, i); 614 *NewPos = i; 615 } 616 else { 617 618 // No, make a new one 619 620 if (Icc -> TagCount >= MAX_TABLE_TAG) { 621 cmsSignalError(Icc ->ContextID, cmsERROR_RANGE, "Too many tags (%d)", MAX_TABLE_TAG); 622 return FALSE; 623 } 624 625 *NewPos = Icc ->TagCount; 626 Icc -> TagCount++; 627 } 628 629 return TRUE; 630} 631 632 633// Check existance 634cmsBool CMSEXPORT cmsIsTag(cmsHPROFILE hProfile, cmsTagSignature sig) 635{ 636 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) (void*) hProfile; 637 return _cmsSearchTag(Icc, sig, FALSE) >= 0; 638} 639 640 641// Read profile header and validate it 642cmsBool _cmsReadHeader(_cmsICCPROFILE* Icc) 643{ 644 cmsTagEntry Tag; 645 cmsICCHeader Header; 646 cmsUInt32Number i, j; 647 cmsUInt32Number HeaderSize; 648 cmsIOHANDLER* io = Icc ->IOhandler; 649 cmsUInt32Number TagCount; 650 651 652 // Read the header 653 if (io -> Read(io, &Header, sizeof(cmsICCHeader), 1) != 1) { 654 return FALSE; 655 } 656 657 // Validate file as an ICC profile 658 if (_cmsAdjustEndianess32(Header.magic) != cmsMagicNumber) { 659 cmsSignalError(Icc ->ContextID, cmsERROR_BAD_SIGNATURE, "not an ICC profile, invalid signature"); 660 return FALSE; 661 } 662 663 // Adjust endianess of the used parameters 664 Icc -> DeviceClass = (cmsProfileClassSignature) _cmsAdjustEndianess32(Header.deviceClass); 665 Icc -> ColorSpace = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Header.colorSpace); 666 Icc -> PCS = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Header.pcs); 667 668 Icc -> RenderingIntent = _cmsAdjustEndianess32(Header.renderingIntent); 669 Icc -> flags = _cmsAdjustEndianess32(Header.flags); 670 Icc -> manufacturer = _cmsAdjustEndianess32(Header.manufacturer); 671 Icc -> model = _cmsAdjustEndianess32(Header.model); 672 Icc -> creator = _cmsAdjustEndianess32(Header.creator); 673 674 _cmsAdjustEndianess64(&Icc -> attributes, &Header.attributes); 675 Icc -> Version = _cmsAdjustEndianess32(Header.version); 676 677 // Get size as reported in header 678 HeaderSize = _cmsAdjustEndianess32(Header.size); 679 680 // Make sure HeaderSize is lower than profile size 681 if (HeaderSize >= Icc ->IOhandler ->ReportedSize) 682 HeaderSize = Icc ->IOhandler ->ReportedSize; 683 684 685 // Get creation date/time 686 _cmsDecodeDateTimeNumber(&Header.date, &Icc ->Created); 687 688 // The profile ID are 32 raw bytes 689 memmove(Icc ->ProfileID.ID32, Header.profileID.ID32, 16); 690 691 692 // Read tag directory 693 if (!_cmsReadUInt32Number(io, &TagCount)) return FALSE; 694 if (TagCount > MAX_TABLE_TAG) { 695 696 cmsSignalError(Icc ->ContextID, cmsERROR_RANGE, "Too many tags (%d)", TagCount); 697 return FALSE; 698 } 699 700 701 // Read tag directory 702 Icc -> TagCount = 0; 703 for (i=0; i < TagCount; i++) { 704 705 if (!_cmsReadUInt32Number(io, (cmsUInt32Number *) &Tag.sig)) return FALSE; 706 if (!_cmsReadUInt32Number(io, &Tag.offset)) return FALSE; 707 if (!_cmsReadUInt32Number(io, &Tag.size)) return FALSE; 708 709 // Perform some sanity check. Offset + size should fall inside file. 710 if (Tag.offset + Tag.size > HeaderSize || 711 Tag.offset + Tag.size < Tag.offset) 712 continue; 713 714 Icc -> TagNames[Icc ->TagCount] = Tag.sig; 715 Icc -> TagOffsets[Icc ->TagCount] = Tag.offset; 716 Icc -> TagSizes[Icc ->TagCount] = Tag.size; 717 718 // Search for links 719 for (j=0; j < Icc ->TagCount; j++) { 720 721 if ((Icc ->TagOffsets[j] == Tag.offset) && 722 (Icc ->TagSizes[j] == Tag.size)) { 723 724 Icc ->TagLinked[Icc ->TagCount] = Icc ->TagNames[j]; 725 } 726 727 } 728 729 Icc ->TagCount++; 730 } 731 732 return TRUE; 733} 734 735// Saves profile header 736cmsBool _cmsWriteHeader(_cmsICCPROFILE* Icc, cmsUInt32Number UsedSpace) 737{ 738 cmsICCHeader Header; 739 cmsUInt32Number i; 740 cmsTagEntry Tag; 741 cmsInt32Number Count = 0; 742 743 Header.size = _cmsAdjustEndianess32(UsedSpace); 744 Header.cmmId = _cmsAdjustEndianess32(lcmsSignature); 745 Header.version = _cmsAdjustEndianess32(Icc ->Version); 746 747 Header.deviceClass = (cmsProfileClassSignature) _cmsAdjustEndianess32(Icc -> DeviceClass); 748 Header.colorSpace = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Icc -> ColorSpace); 749 Header.pcs = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Icc -> PCS); 750 751 // NOTE: in v4 Timestamp must be in UTC rather than in local time 752 _cmsEncodeDateTimeNumber(&Header.date, &Icc ->Created); 753 754 Header.magic = _cmsAdjustEndianess32(cmsMagicNumber); 755 756#ifdef CMS_IS_WINDOWS_ 757 Header.platform = (cmsPlatformSignature) _cmsAdjustEndianess32(cmsSigMicrosoft); 758#else 759 Header.platform = (cmsPlatformSignature) _cmsAdjustEndianess32(cmsSigMacintosh); 760#endif 761 762 Header.flags = _cmsAdjustEndianess32(Icc -> flags); 763 Header.manufacturer = _cmsAdjustEndianess32(Icc -> manufacturer); 764 Header.model = _cmsAdjustEndianess32(Icc -> model); 765 766 _cmsAdjustEndianess64(&Header.attributes, &Icc -> attributes); 767 768 // Rendering intent in the header (for embedded profiles) 769 Header.renderingIntent = _cmsAdjustEndianess32(Icc -> RenderingIntent); 770 771 // Illuminant is always D50 772 Header.illuminant.X = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(cmsD50_XYZ()->X)); 773 Header.illuminant.Y = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(cmsD50_XYZ()->Y)); 774 Header.illuminant.Z = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(cmsD50_XYZ()->Z)); 775 776 // Created by LittleCMS (that's me!) 777 Header.creator = _cmsAdjustEndianess32(lcmsSignature); 778 779 memset(&Header.reserved, 0, sizeof(Header.reserved)); 780 781 // Set profile ID. Endianess is always big endian 782 memmove(&Header.profileID, &Icc ->ProfileID, 16); 783 784 // Dump the header 785 if (!Icc -> IOhandler->Write(Icc->IOhandler, sizeof(cmsICCHeader), &Header)) return FALSE; 786 787 // Saves Tag directory 788 789 // Get true count 790 for (i=0; i < Icc -> TagCount; i++) { 791 if (Icc ->TagNames[i] != 0) 792 Count++; 793 } 794 795 // Store number of tags 796 if (!_cmsWriteUInt32Number(Icc ->IOhandler, Count)) return FALSE; 797 798 for (i=0; i < Icc -> TagCount; i++) { 799 800 if (Icc ->TagNames[i] == 0) continue; // It is just a placeholder 801 802 Tag.sig = (cmsTagSignature) _cmsAdjustEndianess32((cmsInt32Number) Icc -> TagNames[i]); 803 Tag.offset = _cmsAdjustEndianess32((cmsInt32Number) Icc -> TagOffsets[i]); 804 Tag.size = _cmsAdjustEndianess32((cmsInt32Number) Icc -> TagSizes[i]); 805 806 if (!Icc ->IOhandler -> Write(Icc-> IOhandler, sizeof(cmsTagEntry), &Tag)) return FALSE; 807 } 808 809 return TRUE; 810} 811 812// ----------------------------------------------------------------------- Set/Get several struct members 813 814 815cmsUInt32Number CMSEXPORT cmsGetHeaderRenderingIntent(cmsHPROFILE hProfile) 816{ 817 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 818 return Icc -> RenderingIntent; 819} 820 821void CMSEXPORT cmsSetHeaderRenderingIntent(cmsHPROFILE hProfile, cmsUInt32Number RenderingIntent) 822{ 823 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 824 Icc -> RenderingIntent = RenderingIntent; 825} 826 827cmsUInt32Number CMSEXPORT cmsGetHeaderFlags(cmsHPROFILE hProfile) 828{ 829 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 830 return (cmsUInt32Number) Icc -> flags; 831} 832 833void CMSEXPORT cmsSetHeaderFlags(cmsHPROFILE hProfile, cmsUInt32Number Flags) 834{ 835 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 836 Icc -> flags = (cmsUInt32Number) Flags; 837} 838 839cmsUInt32Number CMSEXPORT cmsGetHeaderManufacturer(cmsHPROFILE hProfile) 840{ 841 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 842 return Icc ->manufacturer; 843} 844 845void CMSEXPORT cmsSetHeaderManufacturer(cmsHPROFILE hProfile, cmsUInt32Number manufacturer) 846{ 847 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 848 Icc -> manufacturer = manufacturer; 849} 850 851cmsUInt32Number CMSEXPORT cmsGetHeaderCreator(cmsHPROFILE hProfile) 852{ 853 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 854 return Icc ->creator; 855} 856 857cmsUInt32Number CMSEXPORT cmsGetHeaderModel(cmsHPROFILE hProfile) 858{ 859 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 860 return Icc ->model; 861} 862 863void CMSEXPORT cmsSetHeaderModel(cmsHPROFILE hProfile, cmsUInt32Number model) 864{ 865 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 866 Icc -> model = model; 867} 868 869void CMSEXPORT cmsGetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number* Flags) 870{ 871 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 872 memmove(Flags, &Icc -> attributes, sizeof(cmsUInt64Number)); 873} 874 875void CMSEXPORT cmsSetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number Flags) 876{ 877 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 878 memmove(&Icc -> attributes, &Flags, sizeof(cmsUInt64Number)); 879} 880 881void CMSEXPORT cmsGetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID) 882{ 883 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 884 memmove(ProfileID, Icc ->ProfileID.ID8, 16); 885} 886 887void CMSEXPORT cmsSetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID) 888{ 889 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 890 memmove(&Icc -> ProfileID, ProfileID, 16); 891} 892 893cmsBool CMSEXPORT cmsGetHeaderCreationDateTime(cmsHPROFILE hProfile, struct tm *Dest) 894{ 895 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 896 memmove(Dest, &Icc ->Created, sizeof(struct tm)); 897 return TRUE; 898} 899 900cmsColorSpaceSignature CMSEXPORT cmsGetPCS(cmsHPROFILE hProfile) 901{ 902 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 903 return Icc -> PCS; 904} 905 906void CMSEXPORT cmsSetPCS(cmsHPROFILE hProfile, cmsColorSpaceSignature pcs) 907{ 908 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 909 Icc -> PCS = pcs; 910} 911 912cmsColorSpaceSignature CMSEXPORT cmsGetColorSpace(cmsHPROFILE hProfile) 913{ 914 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 915 return Icc -> ColorSpace; 916} 917 918void CMSEXPORT cmsSetColorSpace(cmsHPROFILE hProfile, cmsColorSpaceSignature sig) 919{ 920 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 921 Icc -> ColorSpace = sig; 922} 923 924cmsProfileClassSignature CMSEXPORT cmsGetDeviceClass(cmsHPROFILE hProfile) 925{ 926 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 927 return Icc -> DeviceClass; 928} 929 930void CMSEXPORT cmsSetDeviceClass(cmsHPROFILE hProfile, cmsProfileClassSignature sig) 931{ 932 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 933 Icc -> DeviceClass = sig; 934} 935 936cmsUInt32Number CMSEXPORT cmsGetEncodedICCversion(cmsHPROFILE hProfile) 937{ 938 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 939 return Icc -> Version; 940} 941 942void CMSEXPORT cmsSetEncodedICCversion(cmsHPROFILE hProfile, cmsUInt32Number Version) 943{ 944 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 945 Icc -> Version = Version; 946} 947 948// Get an hexadecimal number with same digits as v 949static 950cmsUInt32Number BaseToBase(cmsUInt32Number in, int BaseIn, int BaseOut) 951{ 952 char Buff[100]; 953 int i, len; 954 cmsUInt32Number out; 955 956 for (len=0; in > 0 && len < 100; len++) { 957 958 Buff[len] = (char) (in % BaseIn); 959 in /= BaseIn; 960 } 961 962 for (i=len-1, out=0; i >= 0; --i) { 963 out = out * BaseOut + Buff[i]; 964 } 965 966 return out; 967} 968 969void CMSEXPORT cmsSetProfileVersion(cmsHPROFILE hProfile, cmsFloat64Number Version) 970{ 971 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 972 973 // 4.2 -> 0x4200000 974 975 Icc -> Version = BaseToBase((cmsUInt32Number) floor(Version * 100.0 + 0.5), 10, 16) << 16; 976} 977 978cmsFloat64Number CMSEXPORT cmsGetProfileVersion(cmsHPROFILE hProfile) 979{ 980 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 981 cmsUInt32Number n = Icc -> Version >> 16; 982 983 return BaseToBase(n, 16, 10) / 100.0; 984} 985// -------------------------------------------------------------------------------------------------------------- 986 987 988// Create profile from IOhandler 989cmsHPROFILE CMSEXPORT cmsOpenProfileFromIOhandlerTHR(cmsContext ContextID, cmsIOHANDLER* io) 990{ 991 _cmsICCPROFILE* NewIcc; 992 cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID); 993 994 if (hEmpty == NULL) return NULL; 995 996 NewIcc = (_cmsICCPROFILE*) hEmpty; 997 998 NewIcc ->IOhandler = io; 999 if (!_cmsReadHeader(NewIcc)) goto Error; 1000 return hEmpty; 1001 1002Error: 1003 cmsCloseProfile(hEmpty); 1004 return NULL; 1005} 1006 1007// Create profile from IOhandler 1008cmsHPROFILE CMSEXPORT cmsOpenProfileFromIOhandler2THR(cmsContext ContextID, cmsIOHANDLER* io, cmsBool write) 1009{ 1010 _cmsICCPROFILE* NewIcc; 1011 cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID); 1012 1013 if (hEmpty == NULL) return NULL; 1014 1015 NewIcc = (_cmsICCPROFILE*) hEmpty; 1016 1017 NewIcc ->IOhandler = io; 1018 if (write) { 1019 1020 NewIcc -> IsWrite = TRUE; 1021 return hEmpty; 1022 } 1023 1024 if (!_cmsReadHeader(NewIcc)) goto Error; 1025 return hEmpty; 1026 1027Error: 1028 cmsCloseProfile(hEmpty); 1029 return NULL; 1030} 1031 1032 1033// Create profile from disk file 1034cmsHPROFILE CMSEXPORT cmsOpenProfileFromFileTHR(cmsContext ContextID, const char *lpFileName, const char *sAccess) 1035{ 1036 _cmsICCPROFILE* NewIcc; 1037 cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID); 1038 1039 if (hEmpty == NULL) return NULL; 1040 1041 NewIcc = (_cmsICCPROFILE*) hEmpty; 1042 1043 NewIcc ->IOhandler = cmsOpenIOhandlerFromFile(ContextID, lpFileName, sAccess); 1044 if (NewIcc ->IOhandler == NULL) goto Error; 1045 1046 if (*sAccess == 'W' || *sAccess == 'w') { 1047 1048 NewIcc -> IsWrite = TRUE; 1049 1050 return hEmpty; 1051 } 1052 1053 if (!_cmsReadHeader(NewIcc)) goto Error; 1054 return hEmpty; 1055 1056Error: 1057 cmsCloseProfile(hEmpty); 1058 return NULL; 1059} 1060 1061 1062cmsHPROFILE CMSEXPORT cmsOpenProfileFromFile(const char *ICCProfile, const char *sAccess) 1063{ 1064 return cmsOpenProfileFromFileTHR(NULL, ICCProfile, sAccess); 1065} 1066 1067 1068cmsHPROFILE CMSEXPORT cmsOpenProfileFromStreamTHR(cmsContext ContextID, FILE* ICCProfile, const char *sAccess) 1069{ 1070 _cmsICCPROFILE* NewIcc; 1071 cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID); 1072 1073 if (hEmpty == NULL) return NULL; 1074 1075 NewIcc = (_cmsICCPROFILE*) hEmpty; 1076 1077 NewIcc ->IOhandler = cmsOpenIOhandlerFromStream(ContextID, ICCProfile); 1078 if (NewIcc ->IOhandler == NULL) goto Error; 1079 1080 if (*sAccess == 'w') { 1081 1082 NewIcc -> IsWrite = TRUE; 1083 return hEmpty; 1084 } 1085 1086 if (!_cmsReadHeader(NewIcc)) goto Error; 1087 return hEmpty; 1088 1089Error: 1090 cmsCloseProfile(hEmpty); 1091 return NULL; 1092 1093} 1094 1095cmsHPROFILE CMSEXPORT cmsOpenProfileFromStream(FILE* ICCProfile, const char *sAccess) 1096{ 1097 return cmsOpenProfileFromStreamTHR(NULL, ICCProfile, sAccess); 1098} 1099 1100 1101// Open from memory block 1102cmsHPROFILE CMSEXPORT cmsOpenProfileFromMemTHR(cmsContext ContextID, const void* MemPtr, cmsUInt32Number dwSize) 1103{ 1104 _cmsICCPROFILE* NewIcc; 1105 cmsHPROFILE hEmpty; 1106 1107 hEmpty = cmsCreateProfilePlaceholder(ContextID); 1108 if (hEmpty == NULL) return NULL; 1109 1110 NewIcc = (_cmsICCPROFILE*) hEmpty; 1111 1112 // Ok, in this case const void* is casted to void* just because open IO handler 1113 // shares read and writting modes. Don't abuse this feature! 1114 NewIcc ->IOhandler = cmsOpenIOhandlerFromMem(ContextID, (void*) MemPtr, dwSize, "r"); 1115 if (NewIcc ->IOhandler == NULL) goto Error; 1116 1117 if (!_cmsReadHeader(NewIcc)) goto Error; 1118 1119 return hEmpty; 1120 1121Error: 1122 cmsCloseProfile(hEmpty); 1123 return NULL; 1124} 1125 1126cmsHPROFILE CMSEXPORT cmsOpenProfileFromMem(const void* MemPtr, cmsUInt32Number dwSize) 1127{ 1128 return cmsOpenProfileFromMemTHR(NULL, MemPtr, dwSize); 1129} 1130 1131 1132 1133// Dump tag contents. If the profile is being modified, untouched tags are copied from FileOrig 1134static 1135cmsBool SaveTags(_cmsICCPROFILE* Icc, _cmsICCPROFILE* FileOrig) 1136{ 1137 cmsUInt8Number* Data; 1138 cmsUInt32Number i; 1139 cmsUInt32Number Begin; 1140 cmsIOHANDLER* io = Icc ->IOhandler; 1141 cmsTagDescriptor* TagDescriptor; 1142 cmsTagTypeSignature TypeBase; 1143 cmsTagTypeSignature Type; 1144 cmsTagTypeHandler* TypeHandler; 1145 cmsFloat64Number Version = cmsGetProfileVersion((cmsHPROFILE) Icc); 1146 cmsTagTypeHandler LocalTypeHandler; 1147 1148 for (i=0; i < Icc -> TagCount; i++) { 1149 1150 if (Icc ->TagNames[i] == 0) continue; 1151 1152 // Linked tags are not written 1153 if (Icc ->TagLinked[i] != (cmsTagSignature) 0) continue; 1154 1155 Icc -> TagOffsets[i] = Begin = io ->UsedSpace; 1156 1157 Data = (cmsUInt8Number*) Icc -> TagPtrs[i]; 1158 1159 if (!Data) { 1160 1161 // Reach here if we are copying a tag from a disk-based ICC profile which has not been modified by user. 1162 // In this case a blind copy of the block data is performed 1163 if (FileOrig != NULL && Icc -> TagOffsets[i]) { 1164 1165 cmsUInt32Number TagSize = FileOrig -> TagSizes[i]; 1166 cmsUInt32Number TagOffset = FileOrig -> TagOffsets[i]; 1167 void* Mem; 1168 1169 if (!FileOrig ->IOhandler->Seek(FileOrig ->IOhandler, TagOffset)) return FALSE; 1170 1171 Mem = _cmsMalloc(Icc ->ContextID, TagSize); 1172 if (Mem == NULL) return FALSE; 1173 1174 if (FileOrig ->IOhandler->Read(FileOrig->IOhandler, Mem, TagSize, 1) != 1) return FALSE; 1175 if (!io ->Write(io, TagSize, Mem)) return FALSE; 1176 _cmsFree(Icc ->ContextID, Mem); 1177 1178 Icc -> TagSizes[i] = (io ->UsedSpace - Begin); 1179 1180 1181 // Align to 32 bit boundary. 1182 if (! _cmsWriteAlignment(io)) 1183 return FALSE; 1184 } 1185 1186 continue; 1187 } 1188 1189 1190 // Should this tag be saved as RAW? If so, tagsizes should be specified in advance (no further cooking is done) 1191 if (Icc ->TagSaveAsRaw[i]) { 1192 1193 if (io -> Write(io, Icc ->TagSizes[i], Data) != 1) return FALSE; 1194 } 1195 else { 1196 1197 // Search for support on this tag 1198 TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, Icc -> TagNames[i]); 1199 if (TagDescriptor == NULL) continue; // Unsupported, ignore it 1200 1201 if (TagDescriptor ->DecideType != NULL) { 1202 1203 Type = TagDescriptor ->DecideType(Version, Data); 1204 } 1205 else { 1206 1207 Type = TagDescriptor ->SupportedTypes[0]; 1208 } 1209 1210 TypeHandler = _cmsGetTagTypeHandler(Icc->ContextID, Type); 1211 1212 if (TypeHandler == NULL) { 1213 cmsSignalError(Icc ->ContextID, cmsERROR_INTERNAL, "(Internal) no handler for tag %x", Icc -> TagNames[i]); 1214 continue; 1215 } 1216 1217 TypeBase = TypeHandler ->Signature; 1218 if (!_cmsWriteTypeBase(io, TypeBase)) 1219 return FALSE; 1220 1221 LocalTypeHandler = *TypeHandler; 1222 LocalTypeHandler.ContextID = Icc ->ContextID; 1223 LocalTypeHandler.ICCVersion = Icc ->Version; 1224 if (!LocalTypeHandler.WritePtr(&LocalTypeHandler, io, Data, TagDescriptor ->ElemCount)) { 1225 1226 char String[5]; 1227 1228 _cmsTagSignature2String(String, (cmsTagSignature) TypeBase); 1229 cmsSignalError(Icc ->ContextID, cmsERROR_WRITE, "Couldn't write type '%s'", String); 1230 return FALSE; 1231 } 1232 } 1233 1234 1235 Icc -> TagSizes[i] = (io ->UsedSpace - Begin); 1236 1237 // Align to 32 bit boundary. 1238 if (! _cmsWriteAlignment(io)) 1239 return FALSE; 1240 } 1241 1242 1243 return TRUE; 1244} 1245 1246 1247// Fill the offset and size fields for all linked tags 1248static 1249cmsBool SetLinks( _cmsICCPROFILE* Icc) 1250{ 1251 cmsUInt32Number i; 1252 1253 for (i=0; i < Icc -> TagCount; i++) { 1254 1255 cmsTagSignature lnk = Icc ->TagLinked[i]; 1256 if (lnk != (cmsTagSignature) 0) { 1257 1258 int j = _cmsSearchTag(Icc, lnk, FALSE); 1259 if (j >= 0) { 1260 1261 Icc ->TagOffsets[i] = Icc ->TagOffsets[j]; 1262 Icc ->TagSizes[i] = Icc ->TagSizes[j]; 1263 } 1264 1265 } 1266 } 1267 1268 return TRUE; 1269} 1270 1271// Low-level save to IOHANDLER. It returns the number of bytes used to 1272// store the profile, or zero on error. io may be NULL and in this case 1273// no data is written--only sizes are calculated 1274cmsUInt32Number CMSEXPORT cmsSaveProfileToIOhandler(cmsHPROFILE hProfile, cmsIOHANDLER* io) 1275{ 1276 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 1277 _cmsICCPROFILE Keep; 1278 cmsIOHANDLER* PrevIO = NULL; 1279 cmsUInt32Number UsedSpace; 1280 cmsContext ContextID; 1281 1282 _cmsAssert(hProfile != NULL); 1283 1284 memmove(&Keep, Icc, sizeof(_cmsICCPROFILE)); 1285 1286 ContextID = cmsGetProfileContextID(hProfile); 1287 PrevIO = Icc ->IOhandler = cmsOpenIOhandlerFromNULL(ContextID); 1288 if (PrevIO == NULL) return 0; 1289 1290 // Pass #1 does compute offsets 1291 1292 if (!_cmsWriteHeader(Icc, 0)) goto Error; 1293 if (!SaveTags(Icc, &Keep)) goto Error; 1294 1295 UsedSpace = PrevIO ->UsedSpace; 1296 1297 // Pass #2 does save to iohandler 1298 1299 if (io != NULL) { 1300 1301 Icc ->IOhandler = io; 1302 if (!SetLinks(Icc)) goto Error; 1303 if (!_cmsWriteHeader(Icc, UsedSpace)) goto Error; 1304 if (!SaveTags(Icc, &Keep)) goto Error; 1305 } 1306 1307 memmove(Icc, &Keep, sizeof(_cmsICCPROFILE)); 1308 if (!cmsCloseIOhandler(PrevIO)) return 0; 1309 1310 return UsedSpace; 1311 1312 1313Error: 1314 cmsCloseIOhandler(PrevIO); 1315 memmove(Icc, &Keep, sizeof(_cmsICCPROFILE)); 1316 return 0; 1317} 1318 1319#ifdef _WIN32_WCE 1320int wceex_unlink(const char *filename); 1321#ifndef remove 1322# define remove wceex_unlink 1323#endif 1324#endif 1325 1326// Low-level save to disk. 1327cmsBool CMSEXPORT cmsSaveProfileToFile(cmsHPROFILE hProfile, const char* FileName) 1328{ 1329 cmsContext ContextID = cmsGetProfileContextID(hProfile); 1330 cmsIOHANDLER* io = cmsOpenIOhandlerFromFile(ContextID, FileName, "w"); 1331 cmsBool rc; 1332 1333 if (io == NULL) return FALSE; 1334 1335 rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0); 1336 rc &= cmsCloseIOhandler(io); 1337 1338 if (rc == FALSE) { // remove() is C99 per 7.19.4.1 1339 remove(FileName); // We have to IGNORE return value in this case 1340 } 1341 return rc; 1342} 1343 1344// Same as anterior, but for streams 1345cmsBool CMSEXPORT cmsSaveProfileToStream(cmsHPROFILE hProfile, FILE* Stream) 1346{ 1347 cmsBool rc; 1348 cmsContext ContextID = cmsGetProfileContextID(hProfile); 1349 cmsIOHANDLER* io = cmsOpenIOhandlerFromStream(ContextID, Stream); 1350 1351 if (io == NULL) return FALSE; 1352 1353 rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0); 1354 rc &= cmsCloseIOhandler(io); 1355 1356 return rc; 1357} 1358 1359 1360// Same as anterior, but for memory blocks. In this case, a NULL as MemPtr means calculate needed space only 1361cmsBool CMSEXPORT cmsSaveProfileToMem(cmsHPROFILE hProfile, void *MemPtr, cmsUInt32Number* BytesNeeded) 1362{ 1363 cmsBool rc; 1364 cmsIOHANDLER* io; 1365 cmsContext ContextID = cmsGetProfileContextID(hProfile); 1366 1367 _cmsAssert(BytesNeeded != NULL); 1368 1369 // Should we just calculate the needed space? 1370 if (MemPtr == NULL) { 1371 1372 *BytesNeeded = cmsSaveProfileToIOhandler(hProfile, NULL); 1373 return (*BytesNeeded == 0) ? FALSE : TRUE; 1374 } 1375 1376 // That is a real write operation 1377 io = cmsOpenIOhandlerFromMem(ContextID, MemPtr, *BytesNeeded, "w"); 1378 if (io == NULL) return FALSE; 1379 1380 rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0); 1381 rc &= cmsCloseIOhandler(io); 1382 1383 return rc; 1384} 1385 1386 1387 1388// Closes a profile freeing any involved resources 1389cmsBool CMSEXPORT cmsCloseProfile(cmsHPROFILE hProfile) 1390{ 1391 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 1392 cmsBool rc = TRUE; 1393 cmsUInt32Number i; 1394 1395 if (!Icc) return FALSE; 1396 1397 // Was open in write mode? 1398 if (Icc ->IsWrite) { 1399 1400 Icc ->IsWrite = FALSE; // Assure no further writting 1401 rc &= cmsSaveProfileToFile(hProfile, Icc ->IOhandler->PhysicalFile); 1402 } 1403 1404 for (i=0; i < Icc -> TagCount; i++) { 1405 1406 if (Icc -> TagPtrs[i]) { 1407 1408 cmsTagTypeHandler* TypeHandler = Icc ->TagTypeHandlers[i]; 1409 1410 if (TypeHandler != NULL) { 1411 cmsTagTypeHandler LocalTypeHandler = *TypeHandler; 1412 1413 LocalTypeHandler.ContextID = Icc ->ContextID; // As an additional parameters 1414 LocalTypeHandler.ICCVersion = Icc ->Version; 1415 LocalTypeHandler.FreePtr(&LocalTypeHandler, Icc -> TagPtrs[i]); 1416 } 1417 else 1418 _cmsFree(Icc ->ContextID, Icc ->TagPtrs[i]); 1419 } 1420 } 1421 1422 if (Icc ->IOhandler != NULL) { 1423 rc &= cmsCloseIOhandler(Icc->IOhandler); 1424 } 1425 1426 _cmsDestroyMutex(Icc->ContextID, Icc->UsrMutex); 1427 1428 _cmsFree(Icc ->ContextID, Icc); // Free placeholder memory 1429 1430 return rc; 1431} 1432 1433 1434// ------------------------------------------------------------------------------------------------------------------- 1435 1436 1437// Returns TRUE if a given tag is supported by a plug-in 1438static 1439cmsBool IsTypeSupported(cmsTagDescriptor* TagDescriptor, cmsTagTypeSignature Type) 1440{ 1441 cmsUInt32Number i, nMaxTypes; 1442 1443 nMaxTypes = TagDescriptor->nSupportedTypes; 1444 if (nMaxTypes >= MAX_TYPES_IN_LCMS_PLUGIN) 1445 nMaxTypes = MAX_TYPES_IN_LCMS_PLUGIN; 1446 1447 for (i=0; i < nMaxTypes; i++) { 1448 if (Type == TagDescriptor ->SupportedTypes[i]) return TRUE; 1449 } 1450 1451 return FALSE; 1452} 1453 1454 1455// That's the main read function 1456void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig) 1457{ 1458 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 1459 cmsIOHANDLER* io = Icc ->IOhandler; 1460 cmsTagTypeHandler* TypeHandler; 1461 cmsTagTypeHandler LocalTypeHandler; 1462 cmsTagDescriptor* TagDescriptor; 1463 cmsTagTypeSignature BaseType; 1464 cmsUInt32Number Offset, TagSize; 1465 cmsUInt32Number ElemCount; 1466 int n; 1467 1468 if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return NULL; 1469 1470 n = _cmsSearchTag(Icc, sig, TRUE); 1471 if (n < 0) goto Error; // Not found, return NULL 1472 1473 1474 // If the element is already in memory, return the pointer 1475 if (Icc -> TagPtrs[n]) { 1476 1477 if (Icc ->TagSaveAsRaw[n]) goto Error; // We don't support read raw tags as cooked 1478 1479 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1480 return Icc -> TagPtrs[n]; 1481 } 1482 1483 // We need to read it. Get the offset and size to the file 1484 Offset = Icc -> TagOffsets[n]; 1485 TagSize = Icc -> TagSizes[n]; 1486 1487 // Seek to its location 1488 if (!io -> Seek(io, Offset)) 1489 goto Error; 1490 1491 // Search for support on this tag 1492 TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig); 1493 if (TagDescriptor == NULL) { 1494 1495 char String[5]; 1496 1497 _cmsTagSignature2String(String, sig); 1498 1499 // An unknown element was found. 1500 cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown tag type '%s' found.", String); 1501 goto Error; // Unsupported. 1502 } 1503 1504 // if supported, get type and check if in list 1505 BaseType = _cmsReadTypeBase(io); 1506 if (BaseType == 0) goto Error; 1507 1508 if (!IsTypeSupported(TagDescriptor, BaseType)) goto Error; 1509 1510 TagSize -= 8; // Alredy read by the type base logic 1511 1512 // Get type handler 1513 TypeHandler = _cmsGetTagTypeHandler(Icc ->ContextID, BaseType); 1514 if (TypeHandler == NULL) goto Error; 1515 LocalTypeHandler = *TypeHandler; 1516 1517 1518 // Read the tag 1519 Icc -> TagTypeHandlers[n] = TypeHandler; 1520 1521 LocalTypeHandler.ContextID = Icc ->ContextID; 1522 LocalTypeHandler.ICCVersion = Icc ->Version; 1523 Icc -> TagPtrs[n] = LocalTypeHandler.ReadPtr(&LocalTypeHandler, io, &ElemCount, TagSize); 1524 1525 // The tag type is supported, but something wrong happend and we cannot read the tag. 1526 // let know the user about this (although it is just a warning) 1527 if (Icc -> TagPtrs[n] == NULL) { 1528 1529 char String[5]; 1530 1531 _cmsTagSignature2String(String, sig); 1532 cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "Corrupted tag '%s'", String); 1533 goto Error; 1534 } 1535 1536 // This is a weird error that may be a symptom of something more serious, the number of 1537 // stored item is actually less than the number of required elements. 1538 if (ElemCount < TagDescriptor ->ElemCount) { 1539 1540 char String[5]; 1541 1542 _cmsTagSignature2String(String, sig); 1543 cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "'%s' Inconsistent number of items: expected %d, got %d", 1544 String, TagDescriptor ->ElemCount, ElemCount); 1545 } 1546 1547 1548 // Return the data 1549 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1550 return Icc -> TagPtrs[n]; 1551 1552 1553 // Return error and unlock tha data 1554Error: 1555 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1556 return NULL; 1557} 1558 1559 1560// Get true type of data 1561cmsTagTypeSignature _cmsGetTagTrueType(cmsHPROFILE hProfile, cmsTagSignature sig) 1562{ 1563 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 1564 cmsTagTypeHandler* TypeHandler; 1565 int n; 1566 1567 // Search for given tag in ICC profile directory 1568 n = _cmsSearchTag(Icc, sig, TRUE); 1569 if (n < 0) return (cmsTagTypeSignature) 0; // Not found, return NULL 1570 1571 // Get the handler. The true type is there 1572 TypeHandler = Icc -> TagTypeHandlers[n]; 1573 return TypeHandler ->Signature; 1574} 1575 1576 1577// Write a single tag. This just keeps track of the tak into a list of "to be written". If the tag is already 1578// in that list, the previous version is deleted. 1579cmsBool CMSEXPORT cmsWriteTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data) 1580{ 1581 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 1582 cmsTagTypeHandler* TypeHandler = NULL; 1583 cmsTagTypeHandler LocalTypeHandler; 1584 cmsTagDescriptor* TagDescriptor = NULL; 1585 cmsTagTypeSignature Type; 1586 int i; 1587 cmsFloat64Number Version; 1588 char TypeString[5], SigString[5]; 1589 1590 if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return FALSE; 1591 1592 // To delete tags. 1593 if (data == NULL) { 1594 1595 // Delete the tag 1596 i = _cmsSearchTag(Icc, sig, FALSE); 1597 if (i >= 0) { 1598 1599 // Use zero as a mark of deleted 1600 _cmsDeleteTagByPos(Icc, i); 1601 Icc ->TagNames[i] = (cmsTagSignature) 0; 1602 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1603 return TRUE; 1604 } 1605 // Didn't find the tag 1606 goto Error; 1607 } 1608 1609 if (!_cmsNewTag(Icc, sig, &i)) goto Error; 1610 1611 // This is not raw 1612 Icc ->TagSaveAsRaw[i] = FALSE; 1613 1614 // This is not a link 1615 Icc ->TagLinked[i] = (cmsTagSignature) 0; 1616 1617 // Get information about the TAG. 1618 TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig); 1619 if (TagDescriptor == NULL){ 1620 cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported tag '%x'", sig); 1621 goto Error; 1622 } 1623 1624 1625 // Now we need to know which type to use. It depends on the version. 1626 Version = cmsGetProfileVersion(hProfile); 1627 1628 if (TagDescriptor ->DecideType != NULL) { 1629 1630 // Let the tag descriptor to decide the type base on depending on 1631 // the data. This is useful for example on parametric curves, where 1632 // curves specified by a table cannot be saved as parametric and needs 1633 // to be casted to single v2-curves, even on v4 profiles. 1634 1635 Type = TagDescriptor ->DecideType(Version, data); 1636 } 1637 else { 1638 1639 Type = TagDescriptor ->SupportedTypes[0]; 1640 } 1641 1642 // Does the tag support this type? 1643 if (!IsTypeSupported(TagDescriptor, Type)) { 1644 1645 _cmsTagSignature2String(TypeString, (cmsTagSignature) Type); 1646 _cmsTagSignature2String(SigString, sig); 1647 1648 cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported type '%s' for tag '%s'", TypeString, SigString); 1649 goto Error; 1650 } 1651 1652 // Does we have a handler for this type? 1653 TypeHandler = _cmsGetTagTypeHandler(Icc->ContextID, Type); 1654 if (TypeHandler == NULL) { 1655 1656 _cmsTagSignature2String(TypeString, (cmsTagSignature) Type); 1657 _cmsTagSignature2String(SigString, sig); 1658 1659 cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported type '%s' for tag '%s'", TypeString, SigString); 1660 goto Error; // Should never happen 1661 } 1662 1663 1664 // Fill fields on icc structure 1665 Icc ->TagTypeHandlers[i] = TypeHandler; 1666 Icc ->TagNames[i] = sig; 1667 Icc ->TagSizes[i] = 0; 1668 Icc ->TagOffsets[i] = 0; 1669 1670 LocalTypeHandler = *TypeHandler; 1671 LocalTypeHandler.ContextID = Icc ->ContextID; 1672 LocalTypeHandler.ICCVersion = Icc ->Version; 1673 Icc ->TagPtrs[i] = LocalTypeHandler.DupPtr(&LocalTypeHandler, data, TagDescriptor ->ElemCount); 1674 1675 if (Icc ->TagPtrs[i] == NULL) { 1676 1677 _cmsTagSignature2String(TypeString, (cmsTagSignature) Type); 1678 _cmsTagSignature2String(SigString, sig); 1679 cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "Malformed struct in type '%s' for tag '%s'", TypeString, SigString); 1680 1681 goto Error; 1682 } 1683 1684 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1685 return TRUE; 1686 1687Error: 1688 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1689 return FALSE; 1690 1691} 1692 1693// Read and write raw data. The only way those function would work and keep consistence with normal read and write 1694// is to do an additional step of serialization. That means, readRaw would issue a normal read and then convert the obtained 1695// data to raw bytes by using the "write" serialization logic. And vice-versa. I know this may end in situations where 1696// raw data written does not exactly correspond with the raw data proposed to cmsWriteRaw data, but this approach allows 1697// to write a tag as raw data and the read it as handled. 1698 1699cmsInt32Number CMSEXPORT cmsReadRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, void* data, cmsUInt32Number BufferSize) 1700{ 1701 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 1702 void *Object; 1703 int i; 1704 cmsIOHANDLER* MemIO; 1705 cmsTagTypeHandler* TypeHandler = NULL; 1706 cmsTagTypeHandler LocalTypeHandler; 1707 cmsTagDescriptor* TagDescriptor = NULL; 1708 cmsUInt32Number rc; 1709 cmsUInt32Number Offset, TagSize; 1710 1711 if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0; 1712 1713 // Search for given tag in ICC profile directory 1714 i = _cmsSearchTag(Icc, sig, TRUE); 1715 if (i < 0) goto Error; // Not found, 1716 1717 // It is already read? 1718 if (Icc -> TagPtrs[i] == NULL) { 1719 1720 // No yet, get original position 1721 Offset = Icc ->TagOffsets[i]; 1722 TagSize = Icc ->TagSizes[i]; 1723 1724 // read the data directly, don't keep copy 1725 if (data != NULL) { 1726 1727 if (BufferSize < TagSize) 1728 TagSize = BufferSize; 1729 1730 if (!Icc ->IOhandler ->Seek(Icc ->IOhandler, Offset)) goto Error; 1731 if (!Icc ->IOhandler ->Read(Icc ->IOhandler, data, 1, TagSize)) goto Error; 1732 1733 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1734 return TagSize; 1735 } 1736 1737 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1738 return Icc ->TagSizes[i]; 1739 } 1740 1741 // The data has been already read, or written. But wait!, maybe the user choosed to save as 1742 // raw data. In this case, return the raw data directly 1743 if (Icc ->TagSaveAsRaw[i]) { 1744 1745 if (data != NULL) { 1746 1747 TagSize = Icc ->TagSizes[i]; 1748 if (BufferSize < TagSize) 1749 TagSize = BufferSize; 1750 1751 memmove(data, Icc ->TagPtrs[i], TagSize); 1752 1753 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1754 return TagSize; 1755 } 1756 1757 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1758 return Icc ->TagSizes[i]; 1759 } 1760 1761 // Already readed, or previously set by cmsWriteTag(). We need to serialize that 1762 // data to raw in order to maintain consistency. 1763 1764 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1765 Object = cmsReadTag(hProfile, sig); 1766 if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0; 1767 1768 if (Object == NULL) goto Error; 1769 1770 // Now we need to serialize to a memory block: just use a memory iohandler 1771 1772 if (data == NULL) { 1773 MemIO = cmsOpenIOhandlerFromNULL(cmsGetProfileContextID(hProfile)); 1774 } else{ 1775 MemIO = cmsOpenIOhandlerFromMem(cmsGetProfileContextID(hProfile), data, BufferSize, "w"); 1776 } 1777 if (MemIO == NULL) goto Error; 1778 1779 // Obtain type handling for the tag 1780 TypeHandler = Icc ->TagTypeHandlers[i]; 1781 TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig); 1782 if (TagDescriptor == NULL) { 1783 cmsCloseIOhandler(MemIO); 1784 goto Error; 1785 } 1786 1787 if (TypeHandler == NULL) goto Error; 1788 1789 // Serialize 1790 LocalTypeHandler = *TypeHandler; 1791 LocalTypeHandler.ContextID = Icc ->ContextID; 1792 LocalTypeHandler.ICCVersion = Icc ->Version; 1793 1794 if (!_cmsWriteTypeBase(MemIO, TypeHandler ->Signature)) { 1795 cmsCloseIOhandler(MemIO); 1796 goto Error; 1797 } 1798 1799 if (!LocalTypeHandler.WritePtr(&LocalTypeHandler, MemIO, Object, TagDescriptor ->ElemCount)) { 1800 cmsCloseIOhandler(MemIO); 1801 goto Error; 1802 } 1803 1804 // Get Size and close 1805 rc = MemIO ->Tell(MemIO); 1806 cmsCloseIOhandler(MemIO); // Ignore return code this time 1807 1808 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1809 return rc; 1810 1811Error: 1812 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1813 return 0; 1814} 1815 1816// Similar to the anterior. This function allows to write directly to the ICC profile any data, without 1817// checking anything. As a rule, mixing Raw with cooked doesn't work, so writting a tag as raw and then reading 1818// it as cooked without serializing does result into an error. If that is wha you want, you will need to dump 1819// the profile to memry or disk and then reopen it. 1820cmsBool CMSEXPORT cmsWriteRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data, cmsUInt32Number Size) 1821{ 1822 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 1823 int i; 1824 1825 if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0; 1826 1827 if (!_cmsNewTag(Icc, sig, &i)) { 1828 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1829 return FALSE; 1830 } 1831 1832 // Mark the tag as being written as RAW 1833 Icc ->TagSaveAsRaw[i] = TRUE; 1834 Icc ->TagNames[i] = sig; 1835 Icc ->TagLinked[i] = (cmsTagSignature) 0; 1836 1837 // Keep a copy of the block 1838 Icc ->TagPtrs[i] = _cmsDupMem(Icc ->ContextID, data, Size); 1839 Icc ->TagSizes[i] = Size; 1840 1841 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1842 return TRUE; 1843} 1844 1845// Using this function you can collapse several tag entries to the same block in the profile 1846cmsBool CMSEXPORT cmsLinkTag(cmsHPROFILE hProfile, cmsTagSignature sig, cmsTagSignature dest) 1847{ 1848 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 1849 int i; 1850 1851 if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return FALSE; 1852 1853 if (!_cmsNewTag(Icc, sig, &i)) { 1854 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1855 return FALSE; 1856 } 1857 1858 // Keep necessary information 1859 Icc ->TagSaveAsRaw[i] = FALSE; 1860 Icc ->TagNames[i] = sig; 1861 Icc ->TagLinked[i] = dest; 1862 1863 Icc ->TagPtrs[i] = NULL; 1864 Icc ->TagSizes[i] = 0; 1865 Icc ->TagOffsets[i] = 0; 1866 1867 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1868 return TRUE; 1869} 1870 1871 1872// Returns the tag linked to sig, in the case two tags are sharing same resource 1873cmsTagSignature CMSEXPORT cmsTagLinkedTo(cmsHPROFILE hProfile, cmsTagSignature sig) 1874{ 1875 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 1876 int i; 1877 1878 // Search for given tag in ICC profile directory 1879 i = _cmsSearchTag(Icc, sig, FALSE); 1880 if (i < 0) return (cmsTagSignature) 0; // Not found, return 0 1881 1882 return Icc -> TagLinked[i]; 1883} 1884