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 42 typedef struct { 43 cmsUInt32Number Pointer; // Points to current location 44 } FILENULL; 45 46 static 47 cmsUInt32Number 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 58 static 59 cmsBool NULLSeek(cmsIOHANDLER* iohandler, cmsUInt32Number offset) 60 { 61 FILENULL* ResData = (FILENULL*) iohandler ->stream; 62 63 ResData ->Pointer = offset; 64 return TRUE; 65 } 66 67 static 68 cmsUInt32Number NULLTell(cmsIOHANDLER* iohandler) 69 { 70 FILENULL* ResData = (FILENULL*) iohandler ->stream; 71 return ResData -> Pointer; 72 } 73 74 static 75 cmsBool 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 88 static 89 cmsBool 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 99 cmsIOHANDLER* 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 126 Error: 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 137 typedef 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 145 static 146 cmsUInt32Number 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 168 static 169 cmsBool 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 183 static 184 cmsUInt32Number 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. 194 static 195 cmsBool 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 218 static 219 cmsBool 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. 237 cmsIOHANDLER* 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 304 Error: 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 313 static 314 cmsUInt32Number 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 327 static 328 cmsBool 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 340 static 341 cmsUInt32Number 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 347 static 348 cmsBool 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 357 static 358 cmsBool 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. 366 cmsIOHANDLER* 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 423 cmsIOHANDLER* 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 448 cmsBool CMSEXPORT cmsCloseIOhandler(cmsIOHANDLER* io) 449 { 450 return io -> Close(io); 451 } 452 453 // ------------------------------------------------------------------------------------------------------- 454 455 #ifdef _WIN32_WCE 456 time_t wceex_time(time_t *timer); 457 struct 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 464 cmsHPROFILE 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 488 cmsContext 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 498 cmsInt32Number 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 507 cmsTagSignature 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 518 static 519 int 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 534 int _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 572 static 573 void _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 603 static 604 cmsBool _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 634 cmsBool 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 642 cmsBool _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 (Icc ->TagNames[j] == Tag.sig)) { 724 725 Icc ->TagLinked[Icc ->TagCount] = Icc ->TagNames[j]; 726 } 727 728 } 729 730 Icc ->TagCount++; 731 } 732 733 return TRUE; 734 } 735 736 // Saves profile header 737 cmsBool _cmsWriteHeader(_cmsICCPROFILE* Icc, cmsUInt32Number UsedSpace) 738 { 739 cmsICCHeader Header; 740 cmsUInt32Number i; 741 cmsTagEntry Tag; 742 cmsInt32Number Count = 0; 743 744 Header.size = _cmsAdjustEndianess32(UsedSpace); 745 Header.cmmId = _cmsAdjustEndianess32(lcmsSignature); 746 Header.version = _cmsAdjustEndianess32(Icc ->Version); 747 748 Header.deviceClass = (cmsProfileClassSignature) _cmsAdjustEndianess32(Icc -> DeviceClass); 749 Header.colorSpace = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Icc -> ColorSpace); 750 Header.pcs = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Icc -> PCS); 751 752 // NOTE: in v4 Timestamp must be in UTC rather than in local time 753 _cmsEncodeDateTimeNumber(&Header.date, &Icc ->Created); 754 755 Header.magic = _cmsAdjustEndianess32(cmsMagicNumber); 756 757 #ifdef CMS_IS_WINDOWS_ 758 Header.platform = (cmsPlatformSignature) _cmsAdjustEndianess32(cmsSigMicrosoft); 759 #else 760 Header.platform = (cmsPlatformSignature) _cmsAdjustEndianess32(cmsSigMacintosh); 761 #endif 762 763 Header.flags = _cmsAdjustEndianess32(Icc -> flags); 764 Header.manufacturer = _cmsAdjustEndianess32(Icc -> manufacturer); 765 Header.model = _cmsAdjustEndianess32(Icc -> model); 766 767 _cmsAdjustEndianess64(&Header.attributes, &Icc -> attributes); 768 769 // Rendering intent in the header (for embedded profiles) 770 Header.renderingIntent = _cmsAdjustEndianess32(Icc -> RenderingIntent); 771 772 // Illuminant is always D50 773 Header.illuminant.X = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(cmsD50_XYZ()->X)); 774 Header.illuminant.Y = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(cmsD50_XYZ()->Y)); 775 Header.illuminant.Z = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(cmsD50_XYZ()->Z)); 776 777 // Created by LittleCMS (that's me!) 778 Header.creator = _cmsAdjustEndianess32(lcmsSignature); 779 780 memset(&Header.reserved, 0, sizeof(Header.reserved)); 781 782 // Set profile ID. Endianess is always big endian 783 memmove(&Header.profileID, &Icc ->ProfileID, 16); 784 785 // Dump the header 786 if (!Icc -> IOhandler->Write(Icc->IOhandler, sizeof(cmsICCHeader), &Header)) return FALSE; 787 788 // Saves Tag directory 789 790 // Get true count 791 for (i=0; i < Icc -> TagCount; i++) { 792 if (Icc ->TagNames[i] != 0) 793 Count++; 794 } 795 796 // Store number of tags 797 if (!_cmsWriteUInt32Number(Icc ->IOhandler, Count)) return FALSE; 798 799 for (i=0; i < Icc -> TagCount; i++) { 800 801 if (Icc ->TagNames[i] == 0) continue; // It is just a placeholder 802 803 Tag.sig = (cmsTagSignature) _cmsAdjustEndianess32((cmsInt32Number) Icc -> TagNames[i]); 804 Tag.offset = _cmsAdjustEndianess32((cmsInt32Number) Icc -> TagOffsets[i]); 805 Tag.size = _cmsAdjustEndianess32((cmsInt32Number) Icc -> TagSizes[i]); 806 807 if (!Icc ->IOhandler -> Write(Icc-> IOhandler, sizeof(cmsTagEntry), &Tag)) return FALSE; 808 } 809 810 return TRUE; 811 } 812 813 // ----------------------------------------------------------------------- Set/Get several struct members 814 815 816 cmsUInt32Number CMSEXPORT cmsGetHeaderRenderingIntent(cmsHPROFILE hProfile) 817 { 818 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 819 return Icc -> RenderingIntent; 820 } 821 822 void CMSEXPORT cmsSetHeaderRenderingIntent(cmsHPROFILE hProfile, cmsUInt32Number RenderingIntent) 823 { 824 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 825 Icc -> RenderingIntent = RenderingIntent; 826 } 827 828 cmsUInt32Number CMSEXPORT cmsGetHeaderFlags(cmsHPROFILE hProfile) 829 { 830 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 831 return (cmsUInt32Number) Icc -> flags; 832 } 833 834 void CMSEXPORT cmsSetHeaderFlags(cmsHPROFILE hProfile, cmsUInt32Number Flags) 835 { 836 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 837 Icc -> flags = (cmsUInt32Number) Flags; 838 } 839 840 cmsUInt32Number CMSEXPORT cmsGetHeaderManufacturer(cmsHPROFILE hProfile) 841 { 842 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 843 return Icc ->manufacturer; 844 } 845 846 void CMSEXPORT cmsSetHeaderManufacturer(cmsHPROFILE hProfile, cmsUInt32Number manufacturer) 847 { 848 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 849 Icc -> manufacturer = manufacturer; 850 } 851 852 cmsUInt32Number CMSEXPORT cmsGetHeaderCreator(cmsHPROFILE hProfile) 853 { 854 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 855 return Icc ->creator; 856 } 857 858 cmsUInt32Number CMSEXPORT cmsGetHeaderModel(cmsHPROFILE hProfile) 859 { 860 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 861 return Icc ->model; 862 } 863 864 void CMSEXPORT cmsSetHeaderModel(cmsHPROFILE hProfile, cmsUInt32Number model) 865 { 866 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 867 Icc -> model = model; 868 } 869 870 void CMSEXPORT cmsGetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number* Flags) 871 { 872 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 873 memmove(Flags, &Icc -> attributes, sizeof(cmsUInt64Number)); 874 } 875 876 void CMSEXPORT cmsSetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number Flags) 877 { 878 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 879 memmove(&Icc -> attributes, &Flags, sizeof(cmsUInt64Number)); 880 } 881 882 void CMSEXPORT cmsGetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID) 883 { 884 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 885 memmove(ProfileID, Icc ->ProfileID.ID8, 16); 886 } 887 888 void CMSEXPORT cmsSetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID) 889 { 890 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 891 memmove(&Icc -> ProfileID, ProfileID, 16); 892 } 893 894 cmsBool CMSEXPORT cmsGetHeaderCreationDateTime(cmsHPROFILE hProfile, struct tm *Dest) 895 { 896 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 897 memmove(Dest, &Icc ->Created, sizeof(struct tm)); 898 return TRUE; 899 } 900 901 cmsColorSpaceSignature CMSEXPORT cmsGetPCS(cmsHPROFILE hProfile) 902 { 903 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 904 return Icc -> PCS; 905 } 906 907 void CMSEXPORT cmsSetPCS(cmsHPROFILE hProfile, cmsColorSpaceSignature pcs) 908 { 909 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 910 Icc -> PCS = pcs; 911 } 912 913 cmsColorSpaceSignature CMSEXPORT cmsGetColorSpace(cmsHPROFILE hProfile) 914 { 915 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 916 return Icc -> ColorSpace; 917 } 918 919 void CMSEXPORT cmsSetColorSpace(cmsHPROFILE hProfile, cmsColorSpaceSignature sig) 920 { 921 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 922 Icc -> ColorSpace = sig; 923 } 924 925 cmsProfileClassSignature CMSEXPORT cmsGetDeviceClass(cmsHPROFILE hProfile) 926 { 927 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 928 return Icc -> DeviceClass; 929 } 930 931 void CMSEXPORT cmsSetDeviceClass(cmsHPROFILE hProfile, cmsProfileClassSignature sig) 932 { 933 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 934 Icc -> DeviceClass = sig; 935 } 936 937 cmsUInt32Number CMSEXPORT cmsGetEncodedICCversion(cmsHPROFILE hProfile) 938 { 939 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 940 return Icc -> Version; 941 } 942 943 void CMSEXPORT cmsSetEncodedICCversion(cmsHPROFILE hProfile, cmsUInt32Number Version) 944 { 945 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 946 Icc -> Version = Version; 947 } 948 949 // Get an hexadecimal number with same digits as v 950 static 951 cmsUInt32Number BaseToBase(cmsUInt32Number in, int BaseIn, int BaseOut) 952 { 953 char Buff[100]; 954 int i, len; 955 cmsUInt32Number out; 956 957 for (len=0; in > 0 && len < 100; len++) { 958 959 Buff[len] = (char) (in % BaseIn); 960 in /= BaseIn; 961 } 962 963 for (i=len-1, out=0; i >= 0; --i) { 964 out = out * BaseOut + Buff[i]; 965 } 966 967 return out; 968 } 969 970 void CMSEXPORT cmsSetProfileVersion(cmsHPROFILE hProfile, cmsFloat64Number Version) 971 { 972 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 973 974 // 4.2 -> 0x4200000 975 976 Icc -> Version = BaseToBase((cmsUInt32Number) floor(Version * 100.0 + 0.5), 10, 16) << 16; 977 } 978 979 cmsFloat64Number CMSEXPORT cmsGetProfileVersion(cmsHPROFILE hProfile) 980 { 981 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 982 cmsUInt32Number n = Icc -> Version >> 16; 983 984 return BaseToBase(n, 16, 10) / 100.0; 985 } 986 // -------------------------------------------------------------------------------------------------------------- 987 988 989 // Create profile from IOhandler 990 cmsHPROFILE CMSEXPORT cmsOpenProfileFromIOhandlerTHR(cmsContext ContextID, cmsIOHANDLER* io) 991 { 992 _cmsICCPROFILE* NewIcc; 993 cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID); 994 995 if (hEmpty == NULL) return NULL; 996 997 NewIcc = (_cmsICCPROFILE*) hEmpty; 998 999 NewIcc ->IOhandler = io; 1000 if (!_cmsReadHeader(NewIcc)) goto Error; 1001 return hEmpty; 1002 1003 Error: 1004 cmsCloseProfile(hEmpty); 1005 return NULL; 1006 } 1007 1008 // Create profile from IOhandler 1009 cmsHPROFILE CMSEXPORT cmsOpenProfileFromIOhandler2THR(cmsContext ContextID, cmsIOHANDLER* io, cmsBool write) 1010 { 1011 _cmsICCPROFILE* NewIcc; 1012 cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID); 1013 1014 if (hEmpty == NULL) return NULL; 1015 1016 NewIcc = (_cmsICCPROFILE*) hEmpty; 1017 1018 NewIcc ->IOhandler = io; 1019 if (write) { 1020 1021 NewIcc -> IsWrite = TRUE; 1022 return hEmpty; 1023 } 1024 1025 if (!_cmsReadHeader(NewIcc)) goto Error; 1026 return hEmpty; 1027 1028 Error: 1029 cmsCloseProfile(hEmpty); 1030 return NULL; 1031 } 1032 1033 1034 // Create profile from disk file 1035 cmsHPROFILE CMSEXPORT cmsOpenProfileFromFileTHR(cmsContext ContextID, const char *lpFileName, const char *sAccess) 1036 { 1037 _cmsICCPROFILE* NewIcc; 1038 cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID); 1039 1040 if (hEmpty == NULL) return NULL; 1041 1042 NewIcc = (_cmsICCPROFILE*) hEmpty; 1043 1044 NewIcc ->IOhandler = cmsOpenIOhandlerFromFile(ContextID, lpFileName, sAccess); 1045 if (NewIcc ->IOhandler == NULL) goto Error; 1046 1047 if (*sAccess == 'W' || *sAccess == 'w') { 1048 1049 NewIcc -> IsWrite = TRUE; 1050 1051 return hEmpty; 1052 } 1053 1054 if (!_cmsReadHeader(NewIcc)) goto Error; 1055 return hEmpty; 1056 1057 Error: 1058 cmsCloseProfile(hEmpty); 1059 return NULL; 1060 } 1061 1062 1063 cmsHPROFILE CMSEXPORT cmsOpenProfileFromFile(const char *ICCProfile, const char *sAccess) 1064 { 1065 return cmsOpenProfileFromFileTHR(NULL, ICCProfile, sAccess); 1066 } 1067 1068 1069 cmsHPROFILE CMSEXPORT cmsOpenProfileFromStreamTHR(cmsContext ContextID, FILE* ICCProfile, const char *sAccess) 1070 { 1071 _cmsICCPROFILE* NewIcc; 1072 cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID); 1073 1074 if (hEmpty == NULL) return NULL; 1075 1076 NewIcc = (_cmsICCPROFILE*) hEmpty; 1077 1078 NewIcc ->IOhandler = cmsOpenIOhandlerFromStream(ContextID, ICCProfile); 1079 if (NewIcc ->IOhandler == NULL) goto Error; 1080 1081 if (*sAccess == 'w') { 1082 1083 NewIcc -> IsWrite = TRUE; 1084 return hEmpty; 1085 } 1086 1087 if (!_cmsReadHeader(NewIcc)) goto Error; 1088 return hEmpty; 1089 1090 Error: 1091 cmsCloseProfile(hEmpty); 1092 return NULL; 1093 1094 } 1095 1096 cmsHPROFILE CMSEXPORT cmsOpenProfileFromStream(FILE* ICCProfile, const char *sAccess) 1097 { 1098 return cmsOpenProfileFromStreamTHR(NULL, ICCProfile, sAccess); 1099 } 1100 1101 1102 // Open from memory block 1103 cmsHPROFILE CMSEXPORT cmsOpenProfileFromMemTHR(cmsContext ContextID, const void* MemPtr, cmsUInt32Number dwSize) 1104 { 1105 _cmsICCPROFILE* NewIcc; 1106 cmsHPROFILE hEmpty; 1107 1108 hEmpty = cmsCreateProfilePlaceholder(ContextID); 1109 if (hEmpty == NULL) return NULL; 1110 1111 NewIcc = (_cmsICCPROFILE*) hEmpty; 1112 1113 // Ok, in this case const void* is casted to void* just because open IO handler 1114 // shares read and writting modes. Don't abuse this feature! 1115 NewIcc ->IOhandler = cmsOpenIOhandlerFromMem(ContextID, (void*) MemPtr, dwSize, "r"); 1116 if (NewIcc ->IOhandler == NULL) goto Error; 1117 1118 if (!_cmsReadHeader(NewIcc)) goto Error; 1119 1120 return hEmpty; 1121 1122 Error: 1123 cmsCloseProfile(hEmpty); 1124 return NULL; 1125 } 1126 1127 cmsHPROFILE CMSEXPORT cmsOpenProfileFromMem(const void* MemPtr, cmsUInt32Number dwSize) 1128 { 1129 return cmsOpenProfileFromMemTHR(NULL, MemPtr, dwSize); 1130 } 1131 1132 1133 1134 // Dump tag contents. If the profile is being modified, untouched tags are copied from FileOrig 1135 static 1136 cmsBool SaveTags(_cmsICCPROFILE* Icc, _cmsICCPROFILE* FileOrig) 1137 { 1138 cmsUInt8Number* Data; 1139 cmsUInt32Number i; 1140 cmsUInt32Number Begin; 1141 cmsIOHANDLER* io = Icc ->IOhandler; 1142 cmsTagDescriptor* TagDescriptor; 1143 cmsTagTypeSignature TypeBase; 1144 cmsTagTypeSignature Type; 1145 cmsTagTypeHandler* TypeHandler; 1146 cmsFloat64Number Version = cmsGetProfileVersion((cmsHPROFILE) Icc); 1147 cmsTagTypeHandler LocalTypeHandler; 1148 1149 for (i=0; i < Icc -> TagCount; i++) { 1150 1151 if (Icc ->TagNames[i] == 0) continue; 1152 1153 // Linked tags are not written 1154 if (Icc ->TagLinked[i] != (cmsTagSignature) 0) continue; 1155 1156 Icc -> TagOffsets[i] = Begin = io ->UsedSpace; 1157 1158 Data = (cmsUInt8Number*) Icc -> TagPtrs[i]; 1159 1160 if (!Data) { 1161 1162 // Reach here if we are copying a tag from a disk-based ICC profile which has not been modified by user. 1163 // In this case a blind copy of the block data is performed 1164 if (FileOrig != NULL && Icc -> TagOffsets[i]) { 1165 1166 cmsUInt32Number TagSize = FileOrig -> TagSizes[i]; 1167 cmsUInt32Number TagOffset = FileOrig -> TagOffsets[i]; 1168 void* Mem; 1169 1170 if (!FileOrig ->IOhandler->Seek(FileOrig ->IOhandler, TagOffset)) return FALSE; 1171 1172 Mem = _cmsMalloc(Icc ->ContextID, TagSize); 1173 if (Mem == NULL) return FALSE; 1174 1175 if (FileOrig ->IOhandler->Read(FileOrig->IOhandler, Mem, TagSize, 1) != 1) return FALSE; 1176 if (!io ->Write(io, TagSize, Mem)) return FALSE; 1177 _cmsFree(Icc ->ContextID, Mem); 1178 1179 Icc -> TagSizes[i] = (io ->UsedSpace - Begin); 1180 1181 1182 // Align to 32 bit boundary. 1183 if (! _cmsWriteAlignment(io)) 1184 return FALSE; 1185 } 1186 1187 continue; 1188 } 1189 1190 1191 // Should this tag be saved as RAW? If so, tagsizes should be specified in advance (no further cooking is done) 1192 if (Icc ->TagSaveAsRaw[i]) { 1193 1194 if (io -> Write(io, Icc ->TagSizes[i], Data) != 1) return FALSE; 1195 } 1196 else { 1197 1198 // Search for support on this tag 1199 TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, Icc -> TagNames[i]); 1200 if (TagDescriptor == NULL) continue; // Unsupported, ignore it 1201 1202 if (TagDescriptor ->DecideType != NULL) { 1203 1204 Type = TagDescriptor ->DecideType(Version, Data); 1205 } 1206 else { 1207 1208 Type = TagDescriptor ->SupportedTypes[0]; 1209 } 1210 1211 TypeHandler = _cmsGetTagTypeHandler(Icc->ContextID, Type); 1212 1213 if (TypeHandler == NULL) { 1214 cmsSignalError(Icc ->ContextID, cmsERROR_INTERNAL, "(Internal) no handler for tag %x", Icc -> TagNames[i]); 1215 continue; 1216 } 1217 1218 TypeBase = TypeHandler ->Signature; 1219 if (!_cmsWriteTypeBase(io, TypeBase)) 1220 return FALSE; 1221 1222 LocalTypeHandler = *TypeHandler; 1223 LocalTypeHandler.ContextID = Icc ->ContextID; 1224 LocalTypeHandler.ICCVersion = Icc ->Version; 1225 if (!LocalTypeHandler.WritePtr(&LocalTypeHandler, io, Data, TagDescriptor ->ElemCount)) { 1226 1227 char String[5]; 1228 1229 _cmsTagSignature2String(String, (cmsTagSignature) TypeBase); 1230 cmsSignalError(Icc ->ContextID, cmsERROR_WRITE, "Couldn't write type '%s'", String); 1231 return FALSE; 1232 } 1233 } 1234 1235 1236 Icc -> TagSizes[i] = (io ->UsedSpace - Begin); 1237 1238 // Align to 32 bit boundary. 1239 if (! _cmsWriteAlignment(io)) 1240 return FALSE; 1241 } 1242 1243 1244 return TRUE; 1245 } 1246 1247 1248 // Fill the offset and size fields for all linked tags 1249 static 1250 cmsBool SetLinks( _cmsICCPROFILE* Icc) 1251 { 1252 cmsUInt32Number i; 1253 1254 for (i=0; i < Icc -> TagCount; i++) { 1255 1256 cmsTagSignature lnk = Icc ->TagLinked[i]; 1257 if (lnk != (cmsTagSignature) 0) { 1258 1259 int j = _cmsSearchTag(Icc, lnk, FALSE); 1260 if (j >= 0) { 1261 1262 Icc ->TagOffsets[i] = Icc ->TagOffsets[j]; 1263 Icc ->TagSizes[i] = Icc ->TagSizes[j]; 1264 } 1265 1266 } 1267 } 1268 1269 return TRUE; 1270 } 1271 1272 // Low-level save to IOHANDLER. It returns the number of bytes used to 1273 // store the profile, or zero on error. io may be NULL and in this case 1274 // no data is written--only sizes are calculated 1275 cmsUInt32Number CMSEXPORT cmsSaveProfileToIOhandler(cmsHPROFILE hProfile, cmsIOHANDLER* io) 1276 { 1277 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 1278 _cmsICCPROFILE Keep; 1279 cmsIOHANDLER* PrevIO = NULL; 1280 cmsUInt32Number UsedSpace; 1281 cmsContext ContextID; 1282 1283 _cmsAssert(hProfile != NULL); 1284 1285 memmove(&Keep, Icc, sizeof(_cmsICCPROFILE)); 1286 1287 ContextID = cmsGetProfileContextID(hProfile); 1288 PrevIO = Icc ->IOhandler = cmsOpenIOhandlerFromNULL(ContextID); 1289 if (PrevIO == NULL) return 0; 1290 1291 // Pass #1 does compute offsets 1292 1293 if (!_cmsWriteHeader(Icc, 0)) goto Error; 1294 if (!SaveTags(Icc, &Keep)) goto Error; 1295 1296 UsedSpace = PrevIO ->UsedSpace; 1297 1298 // Pass #2 does save to iohandler 1299 1300 if (io != NULL) { 1301 1302 Icc ->IOhandler = io; 1303 if (!SetLinks(Icc)) goto Error; 1304 if (!_cmsWriteHeader(Icc, UsedSpace)) goto Error; 1305 if (!SaveTags(Icc, &Keep)) goto Error; 1306 } 1307 1308 memmove(Icc, &Keep, sizeof(_cmsICCPROFILE)); 1309 if (!cmsCloseIOhandler(PrevIO)) return 0; 1310 1311 return UsedSpace; 1312 1313 1314 Error: 1315 cmsCloseIOhandler(PrevIO); 1316 memmove(Icc, &Keep, sizeof(_cmsICCPROFILE)); 1317 return 0; 1318 } 1319 1320 #ifdef _WIN32_WCE 1321 int wceex_unlink(const char *filename); 1322 #ifndef remove 1323 # define remove wceex_unlink 1324 #endif 1325 #endif 1326 1327 // Low-level save to disk. 1328 cmsBool CMSEXPORT cmsSaveProfileToFile(cmsHPROFILE hProfile, const char* FileName) 1329 { 1330 cmsContext ContextID = cmsGetProfileContextID(hProfile); 1331 cmsIOHANDLER* io = cmsOpenIOhandlerFromFile(ContextID, FileName, "w"); 1332 cmsBool rc; 1333 1334 if (io == NULL) return FALSE; 1335 1336 rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0); 1337 rc &= cmsCloseIOhandler(io); 1338 1339 if (rc == FALSE) { // remove() is C99 per 7.19.4.1 1340 remove(FileName); // We have to IGNORE return value in this case 1341 } 1342 return rc; 1343 } 1344 1345 // Same as anterior, but for streams 1346 cmsBool CMSEXPORT cmsSaveProfileToStream(cmsHPROFILE hProfile, FILE* Stream) 1347 { 1348 cmsBool rc; 1349 cmsContext ContextID = cmsGetProfileContextID(hProfile); 1350 cmsIOHANDLER* io = cmsOpenIOhandlerFromStream(ContextID, Stream); 1351 1352 if (io == NULL) return FALSE; 1353 1354 rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0); 1355 rc &= cmsCloseIOhandler(io); 1356 1357 return rc; 1358 } 1359 1360 1361 // Same as anterior, but for memory blocks. In this case, a NULL as MemPtr means calculate needed space only 1362 cmsBool CMSEXPORT cmsSaveProfileToMem(cmsHPROFILE hProfile, void *MemPtr, cmsUInt32Number* BytesNeeded) 1363 { 1364 cmsBool rc; 1365 cmsIOHANDLER* io; 1366 cmsContext ContextID = cmsGetProfileContextID(hProfile); 1367 1368 _cmsAssert(BytesNeeded != NULL); 1369 1370 // Should we just calculate the needed space? 1371 if (MemPtr == NULL) { 1372 1373 *BytesNeeded = cmsSaveProfileToIOhandler(hProfile, NULL); 1374 return (*BytesNeeded == 0) ? FALSE : TRUE; 1375 } 1376 1377 // That is a real write operation 1378 io = cmsOpenIOhandlerFromMem(ContextID, MemPtr, *BytesNeeded, "w"); 1379 if (io == NULL) return FALSE; 1380 1381 rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0); 1382 rc &= cmsCloseIOhandler(io); 1383 1384 return rc; 1385 } 1386 1387 1388 1389 // Closes a profile freeing any involved resources 1390 cmsBool CMSEXPORT cmsCloseProfile(cmsHPROFILE hProfile) 1391 { 1392 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 1393 cmsBool rc = TRUE; 1394 cmsUInt32Number i; 1395 1396 if (!Icc) return FALSE; 1397 1398 // Was open in write mode? 1399 if (Icc ->IsWrite) { 1400 1401 Icc ->IsWrite = FALSE; // Assure no further writting 1402 rc &= cmsSaveProfileToFile(hProfile, Icc ->IOhandler->PhysicalFile); 1403 } 1404 1405 for (i=0; i < Icc -> TagCount; i++) { 1406 1407 if (Icc -> TagPtrs[i]) { 1408 1409 cmsTagTypeHandler* TypeHandler = Icc ->TagTypeHandlers[i]; 1410 1411 if (TypeHandler != NULL) { 1412 cmsTagTypeHandler LocalTypeHandler = *TypeHandler; 1413 1414 LocalTypeHandler.ContextID = Icc ->ContextID; // As an additional parameters 1415 LocalTypeHandler.ICCVersion = Icc ->Version; 1416 LocalTypeHandler.FreePtr(&LocalTypeHandler, Icc -> TagPtrs[i]); 1417 } 1418 else 1419 _cmsFree(Icc ->ContextID, Icc ->TagPtrs[i]); 1420 } 1421 } 1422 1423 if (Icc ->IOhandler != NULL) { 1424 rc &= cmsCloseIOhandler(Icc->IOhandler); 1425 } 1426 1427 _cmsDestroyMutex(Icc->ContextID, Icc->UsrMutex); 1428 1429 _cmsFree(Icc ->ContextID, Icc); // Free placeholder memory 1430 1431 return rc; 1432 } 1433 1434 1435 // ------------------------------------------------------------------------------------------------------------------- 1436 1437 1438 // Returns TRUE if a given tag is supported by a plug-in 1439 static 1440 cmsBool IsTypeSupported(cmsTagDescriptor* TagDescriptor, cmsTagTypeSignature Type) 1441 { 1442 cmsUInt32Number i, nMaxTypes; 1443 1444 nMaxTypes = TagDescriptor->nSupportedTypes; 1445 if (nMaxTypes >= MAX_TYPES_IN_LCMS_PLUGIN) 1446 nMaxTypes = MAX_TYPES_IN_LCMS_PLUGIN; 1447 1448 for (i=0; i < nMaxTypes; i++) { 1449 if (Type == TagDescriptor ->SupportedTypes[i]) return TRUE; 1450 } 1451 1452 return FALSE; 1453 } 1454 1455 1456 // That's the main read function 1457 void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig) 1458 { 1459 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 1460 cmsIOHANDLER* io = Icc ->IOhandler; 1461 cmsTagTypeHandler* TypeHandler; 1462 cmsTagTypeHandler LocalTypeHandler; 1463 cmsTagDescriptor* TagDescriptor; 1464 cmsTagTypeSignature BaseType; 1465 cmsUInt32Number Offset, TagSize; 1466 cmsUInt32Number ElemCount; 1467 int n; 1468 1469 if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return NULL; 1470 1471 n = _cmsSearchTag(Icc, sig, TRUE); 1472 if (n < 0) goto Error; // Not found, return NULL 1473 1474 1475 // If the element is already in memory, return the pointer 1476 if (Icc -> TagPtrs[n]) { 1477 1478 if (Icc->TagTypeHandlers[n] == NULL) goto Error; 1479 1480 // Sanity check 1481 BaseType = Icc->TagTypeHandlers[n]->Signature; 1482 if (BaseType == 0) goto Error; 1483 1484 TagDescriptor = _cmsGetTagDescriptor(Icc->ContextID, sig); 1485 if (TagDescriptor == NULL) goto Error; 1486 1487 if (!IsTypeSupported(TagDescriptor, BaseType)) goto Error; 1488 1489 if (Icc ->TagSaveAsRaw[n]) goto Error; // We don't support read raw tags as cooked 1490 1491 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1492 return Icc -> TagPtrs[n]; 1493 } 1494 1495 // We need to read it. Get the offset and size to the file 1496 Offset = Icc -> TagOffsets[n]; 1497 TagSize = Icc -> TagSizes[n]; 1498 1499 // Seek to its location 1500 if (!io -> Seek(io, Offset)) 1501 goto Error; 1502 1503 // Search for support on this tag 1504 TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig); 1505 if (TagDescriptor == NULL) { 1506 1507 char String[5]; 1508 1509 _cmsTagSignature2String(String, sig); 1510 1511 // An unknown element was found. 1512 cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown tag type '%s' found.", String); 1513 goto Error; // Unsupported. 1514 } 1515 1516 // if supported, get type and check if in list 1517 BaseType = _cmsReadTypeBase(io); 1518 if (BaseType == 0) goto Error; 1519 1520 if (!IsTypeSupported(TagDescriptor, BaseType)) goto Error; 1521 1522 TagSize -= 8; // Alredy read by the type base logic 1523 1524 // Get type handler 1525 TypeHandler = _cmsGetTagTypeHandler(Icc ->ContextID, BaseType); 1526 if (TypeHandler == NULL) goto Error; 1527 LocalTypeHandler = *TypeHandler; 1528 1529 1530 // Read the tag 1531 Icc -> TagTypeHandlers[n] = TypeHandler; 1532 1533 LocalTypeHandler.ContextID = Icc ->ContextID; 1534 LocalTypeHandler.ICCVersion = Icc ->Version; 1535 Icc -> TagPtrs[n] = LocalTypeHandler.ReadPtr(&LocalTypeHandler, io, &ElemCount, TagSize); 1536 1537 // The tag type is supported, but something wrong happend and we cannot read the tag. 1538 // let know the user about this (although it is just a warning) 1539 if (Icc -> TagPtrs[n] == NULL) { 1540 1541 char String[5]; 1542 1543 _cmsTagSignature2String(String, sig); 1544 cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "Corrupted tag '%s'", String); 1545 goto Error; 1546 } 1547 1548 // This is a weird error that may be a symptom of something more serious, the number of 1549 // stored item is actually less than the number of required elements. 1550 if (ElemCount < TagDescriptor ->ElemCount) { 1551 1552 char String[5]; 1553 1554 _cmsTagSignature2String(String, sig); 1555 cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "'%s' Inconsistent number of items: expected %d, got %d", 1556 String, TagDescriptor ->ElemCount, ElemCount); 1557 } 1558 1559 1560 // Return the data 1561 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1562 return Icc -> TagPtrs[n]; 1563 1564 1565 // Return error and unlock tha data 1566 Error: 1567 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1568 return NULL; 1569 } 1570 1571 1572 // Get true type of data 1573 cmsTagTypeSignature _cmsGetTagTrueType(cmsHPROFILE hProfile, cmsTagSignature sig) 1574 { 1575 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 1576 cmsTagTypeHandler* TypeHandler; 1577 int n; 1578 1579 // Search for given tag in ICC profile directory 1580 n = _cmsSearchTag(Icc, sig, TRUE); 1581 if (n < 0) return (cmsTagTypeSignature) 0; // Not found, return NULL 1582 1583 // Get the handler. The true type is there 1584 TypeHandler = Icc -> TagTypeHandlers[n]; 1585 return TypeHandler ->Signature; 1586 } 1587 1588 1589 // Write a single tag. This just keeps track of the tak into a list of "to be written". If the tag is already 1590 // in that list, the previous version is deleted. 1591 cmsBool CMSEXPORT cmsWriteTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data) 1592 { 1593 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 1594 cmsTagTypeHandler* TypeHandler = NULL; 1595 cmsTagTypeHandler LocalTypeHandler; 1596 cmsTagDescriptor* TagDescriptor = NULL; 1597 cmsTagTypeSignature Type; 1598 int i; 1599 cmsFloat64Number Version; 1600 char TypeString[5], SigString[5]; 1601 1602 if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return FALSE; 1603 1604 // To delete tags. 1605 if (data == NULL) { 1606 1607 // Delete the tag 1608 i = _cmsSearchTag(Icc, sig, FALSE); 1609 if (i >= 0) { 1610 1611 // Use zero as a mark of deleted 1612 _cmsDeleteTagByPos(Icc, i); 1613 Icc ->TagNames[i] = (cmsTagSignature) 0; 1614 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1615 return TRUE; 1616 } 1617 // Didn't find the tag 1618 goto Error; 1619 } 1620 1621 if (!_cmsNewTag(Icc, sig, &i)) goto Error; 1622 1623 // This is not raw 1624 Icc ->TagSaveAsRaw[i] = FALSE; 1625 1626 // This is not a link 1627 Icc ->TagLinked[i] = (cmsTagSignature) 0; 1628 1629 // Get information about the TAG. 1630 TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig); 1631 if (TagDescriptor == NULL){ 1632 cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported tag '%x'", sig); 1633 goto Error; 1634 } 1635 1636 1637 // Now we need to know which type to use. It depends on the version. 1638 Version = cmsGetProfileVersion(hProfile); 1639 1640 if (TagDescriptor ->DecideType != NULL) { 1641 1642 // Let the tag descriptor to decide the type base on depending on 1643 // the data. This is useful for example on parametric curves, where 1644 // curves specified by a table cannot be saved as parametric and needs 1645 // to be casted to single v2-curves, even on v4 profiles. 1646 1647 Type = TagDescriptor ->DecideType(Version, data); 1648 } 1649 else { 1650 1651 Type = TagDescriptor ->SupportedTypes[0]; 1652 } 1653 1654 // Does the tag support this type? 1655 if (!IsTypeSupported(TagDescriptor, Type)) { 1656 1657 _cmsTagSignature2String(TypeString, (cmsTagSignature) Type); 1658 _cmsTagSignature2String(SigString, sig); 1659 1660 cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported type '%s' for tag '%s'", TypeString, SigString); 1661 goto Error; 1662 } 1663 1664 // Does we have a handler for this type? 1665 TypeHandler = _cmsGetTagTypeHandler(Icc->ContextID, Type); 1666 if (TypeHandler == NULL) { 1667 1668 _cmsTagSignature2String(TypeString, (cmsTagSignature) Type); 1669 _cmsTagSignature2String(SigString, sig); 1670 1671 cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported type '%s' for tag '%s'", TypeString, SigString); 1672 goto Error; // Should never happen 1673 } 1674 1675 1676 // Fill fields on icc structure 1677 Icc ->TagTypeHandlers[i] = TypeHandler; 1678 Icc ->TagNames[i] = sig; 1679 Icc ->TagSizes[i] = 0; 1680 Icc ->TagOffsets[i] = 0; 1681 1682 LocalTypeHandler = *TypeHandler; 1683 LocalTypeHandler.ContextID = Icc ->ContextID; 1684 LocalTypeHandler.ICCVersion = Icc ->Version; 1685 Icc ->TagPtrs[i] = LocalTypeHandler.DupPtr(&LocalTypeHandler, data, TagDescriptor ->ElemCount); 1686 1687 if (Icc ->TagPtrs[i] == NULL) { 1688 1689 _cmsTagSignature2String(TypeString, (cmsTagSignature) Type); 1690 _cmsTagSignature2String(SigString, sig); 1691 cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "Malformed struct in type '%s' for tag '%s'", TypeString, SigString); 1692 1693 goto Error; 1694 } 1695 1696 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1697 return TRUE; 1698 1699 Error: 1700 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1701 return FALSE; 1702 1703 } 1704 1705 // Read and write raw data. The only way those function would work and keep consistence with normal read and write 1706 // is to do an additional step of serialization. That means, readRaw would issue a normal read and then convert the obtained 1707 // data to raw bytes by using the "write" serialization logic. And vice-versa. I know this may end in situations where 1708 // raw data written does not exactly correspond with the raw data proposed to cmsWriteRaw data, but this approach allows 1709 // to write a tag as raw data and the read it as handled. 1710 1711 cmsInt32Number CMSEXPORT cmsReadRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, void* data, cmsUInt32Number BufferSize) 1712 { 1713 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 1714 void *Object; 1715 int i; 1716 cmsIOHANDLER* MemIO; 1717 cmsTagTypeHandler* TypeHandler = NULL; 1718 cmsTagTypeHandler LocalTypeHandler; 1719 cmsTagDescriptor* TagDescriptor = NULL; 1720 cmsUInt32Number rc; 1721 cmsUInt32Number Offset, TagSize; 1722 1723 if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0; 1724 1725 // Search for given tag in ICC profile directory 1726 i = _cmsSearchTag(Icc, sig, TRUE); 1727 if (i < 0) goto Error; // Not found, 1728 1729 // It is already read? 1730 if (Icc -> TagPtrs[i] == NULL) { 1731 1732 // No yet, get original position 1733 Offset = Icc ->TagOffsets[i]; 1734 TagSize = Icc ->TagSizes[i]; 1735 1736 // read the data directly, don't keep copy 1737 if (data != NULL) { 1738 1739 if (BufferSize < TagSize) 1740 TagSize = BufferSize; 1741 1742 if (!Icc ->IOhandler ->Seek(Icc ->IOhandler, Offset)) goto Error; 1743 if (!Icc ->IOhandler ->Read(Icc ->IOhandler, data, 1, TagSize)) goto Error; 1744 1745 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1746 return TagSize; 1747 } 1748 1749 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1750 return Icc ->TagSizes[i]; 1751 } 1752 1753 // The data has been already read, or written. But wait!, maybe the user choosed to save as 1754 // raw data. In this case, return the raw data directly 1755 if (Icc ->TagSaveAsRaw[i]) { 1756 1757 if (data != NULL) { 1758 1759 TagSize = Icc ->TagSizes[i]; 1760 if (BufferSize < TagSize) 1761 TagSize = BufferSize; 1762 1763 memmove(data, Icc ->TagPtrs[i], TagSize); 1764 1765 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1766 return TagSize; 1767 } 1768 1769 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1770 return Icc ->TagSizes[i]; 1771 } 1772 1773 // Already readed, or previously set by cmsWriteTag(). We need to serialize that 1774 // data to raw in order to maintain consistency. 1775 1776 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1777 Object = cmsReadTag(hProfile, sig); 1778 if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0; 1779 1780 if (Object == NULL) goto Error; 1781 1782 // Now we need to serialize to a memory block: just use a memory iohandler 1783 1784 if (data == NULL) { 1785 MemIO = cmsOpenIOhandlerFromNULL(cmsGetProfileContextID(hProfile)); 1786 } else{ 1787 MemIO = cmsOpenIOhandlerFromMem(cmsGetProfileContextID(hProfile), data, BufferSize, "w"); 1788 } 1789 if (MemIO == NULL) goto Error; 1790 1791 // Obtain type handling for the tag 1792 TypeHandler = Icc ->TagTypeHandlers[i]; 1793 TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig); 1794 if (TagDescriptor == NULL) { 1795 cmsCloseIOhandler(MemIO); 1796 goto Error; 1797 } 1798 1799 if (TypeHandler == NULL) goto Error; 1800 1801 // Serialize 1802 LocalTypeHandler = *TypeHandler; 1803 LocalTypeHandler.ContextID = Icc ->ContextID; 1804 LocalTypeHandler.ICCVersion = Icc ->Version; 1805 1806 if (!_cmsWriteTypeBase(MemIO, TypeHandler ->Signature)) { 1807 cmsCloseIOhandler(MemIO); 1808 goto Error; 1809 } 1810 1811 if (!LocalTypeHandler.WritePtr(&LocalTypeHandler, MemIO, Object, TagDescriptor ->ElemCount)) { 1812 cmsCloseIOhandler(MemIO); 1813 goto Error; 1814 } 1815 1816 // Get Size and close 1817 rc = MemIO ->Tell(MemIO); 1818 cmsCloseIOhandler(MemIO); // Ignore return code this time 1819 1820 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1821 return rc; 1822 1823 Error: 1824 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1825 return 0; 1826 } 1827 1828 // Similar to the anterior. This function allows to write directly to the ICC profile any data, without 1829 // checking anything. As a rule, mixing Raw with cooked doesn't work, so writting a tag as raw and then reading 1830 // it as cooked without serializing does result into an error. If that is wha you want, you will need to dump 1831 // the profile to memry or disk and then reopen it. 1832 cmsBool CMSEXPORT cmsWriteRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data, cmsUInt32Number Size) 1833 { 1834 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 1835 int i; 1836 1837 if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0; 1838 1839 if (!_cmsNewTag(Icc, sig, &i)) { 1840 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1841 return FALSE; 1842 } 1843 1844 // Mark the tag as being written as RAW 1845 Icc ->TagSaveAsRaw[i] = TRUE; 1846 Icc ->TagNames[i] = sig; 1847 Icc ->TagLinked[i] = (cmsTagSignature) 0; 1848 1849 // Keep a copy of the block 1850 Icc ->TagPtrs[i] = _cmsDupMem(Icc ->ContextID, data, Size); 1851 Icc ->TagSizes[i] = Size; 1852 1853 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1854 return TRUE; 1855 } 1856 1857 // Using this function you can collapse several tag entries to the same block in the profile 1858 cmsBool CMSEXPORT cmsLinkTag(cmsHPROFILE hProfile, cmsTagSignature sig, cmsTagSignature dest) 1859 { 1860 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 1861 int i; 1862 1863 if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return FALSE; 1864 1865 if (!_cmsNewTag(Icc, sig, &i)) { 1866 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1867 return FALSE; 1868 } 1869 1870 // Keep necessary information 1871 Icc ->TagSaveAsRaw[i] = FALSE; 1872 Icc ->TagNames[i] = sig; 1873 Icc ->TagLinked[i] = dest; 1874 1875 Icc ->TagPtrs[i] = NULL; 1876 Icc ->TagSizes[i] = 0; 1877 Icc ->TagOffsets[i] = 0; 1878 1879 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1880 return TRUE; 1881 } 1882 1883 1884 // Returns the tag linked to sig, in the case two tags are sharing same resource 1885 cmsTagSignature CMSEXPORT cmsTagLinkedTo(cmsHPROFILE hProfile, cmsTagSignature sig) 1886 { 1887 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 1888 int i; 1889 1890 // Search for given tag in ICC profile directory 1891 i = _cmsSearchTag(Icc, sig, FALSE); 1892 if (i < 0) return (cmsTagSignature) 0; // Not found, return 0 1893 1894 return Icc -> TagLinked[i]; 1895 } 1896