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