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