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