1 //---------------------------------------------------------------------------------
3 // Little Color Management System
4 // Copyright (c) 1998-2023 Marti Maria Saguer
6 // Permission is hereby granted, free of charge, to any person obtaining
7 // a copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the Software
11 // is furnished to do so, subject to the following conditions:
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
18 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 //---------------------------------------------------------------------------------
27 #include "lcms2_internal.h"
29 // Generic I/O, tag dictionary management, profile struct
31 // IOhandlers are abstractions used by littleCMS to read from whatever file, stream,
32 // memory block or any storage. Each IOhandler provides implementations for read,
33 // write, seek and tell functions. LittleCMS code deals with IO across those objects.
34 // In this way, is easier to add support for new storage media.
36 // NULL stream, for taking care of used space -------------------------------------
38 // NULL IOhandler basically does nothing but keep track on how many bytes have been
39 // written. This is handy when creating profiles, where the file size is needed in the
40 // header. Then, whole profile is serialized across NULL IOhandler and a second pass
41 // writes the bytes to the pertinent IOhandler.
44 cmsUInt32Number Pointer
; // Points to current location
48 cmsUInt32Number
NULLRead(cmsIOHANDLER
* iohandler
, void *Buffer
, cmsUInt32Number size
, cmsUInt32Number count
)
50 FILENULL
* ResData
= (FILENULL
*) iohandler
->stream
;
52 cmsUInt32Number len
= size
* count
;
53 ResData
-> Pointer
+= len
;
56 cmsUNUSED_PARAMETER(Buffer
);
60 cmsBool
NULLSeek(cmsIOHANDLER
* iohandler
, cmsUInt32Number offset
)
62 FILENULL
* ResData
= (FILENULL
*) iohandler
->stream
;
64 ResData
->Pointer
= offset
;
69 cmsUInt32Number
NULLTell(cmsIOHANDLER
* iohandler
)
71 FILENULL
* ResData
= (FILENULL
*) iohandler
->stream
;
72 return ResData
-> Pointer
;
76 cmsBool
NULLWrite(cmsIOHANDLER
* iohandler
, cmsUInt32Number size
, const void *Ptr
)
78 FILENULL
* ResData
= (FILENULL
*) iohandler
->stream
;
80 ResData
->Pointer
+= size
;
81 if (ResData
->Pointer
> iohandler
->UsedSpace
)
82 iohandler
->UsedSpace
= ResData
->Pointer
;
86 cmsUNUSED_PARAMETER(Ptr
);
90 cmsBool
NULLClose(cmsIOHANDLER
* iohandler
)
92 FILENULL
* ResData
= (FILENULL
*) iohandler
->stream
;
94 _cmsFree(iohandler
->ContextID
, ResData
);
95 _cmsFree(iohandler
->ContextID
, iohandler
);
99 // The NULL IOhandler creator
100 cmsIOHANDLER
* CMSEXPORT
cmsOpenIOhandlerFromNULL(cmsContext ContextID
)
102 struct _cms_io_handler
* iohandler
= NULL
;
105 iohandler
= (struct _cms_io_handler
*) _cmsMallocZero(ContextID
, sizeof(struct _cms_io_handler
));
106 if (iohandler
== NULL
) return NULL
;
108 fm
= (FILENULL
*) _cmsMallocZero(ContextID
, sizeof(FILENULL
));
109 if (fm
== NULL
) goto Error
;
113 iohandler
->ContextID
= ContextID
;
114 iohandler
->stream
= (void*) fm
;
115 iohandler
->UsedSpace
= 0;
116 iohandler
->ReportedSize
= 0;
117 iohandler
->PhysicalFile
[0] = 0;
119 iohandler
->Read
= NULLRead
;
120 iohandler
->Seek
= NULLSeek
;
121 iohandler
->Close
= NULLClose
;
122 iohandler
->Tell
= NULLTell
;
123 iohandler
->Write
= NULLWrite
;
128 if (iohandler
) _cmsFree(ContextID
, iohandler
);
134 // Memory-based stream --------------------------------------------------------------
136 // Those functions implements an iohandler which takes a block of memory as storage medium.
139 cmsUInt8Number
* Block
; // Points to allocated memory
140 cmsUInt32Number Size
; // Size of allocated memory
141 cmsUInt32Number Pointer
; // Points to current location
142 int FreeBlockOnClose
; // As title
147 cmsUInt32Number
MemoryRead(struct _cms_io_handler
* iohandler
, void *Buffer
, cmsUInt32Number size
, cmsUInt32Number count
)
149 FILEMEM
* ResData
= (FILEMEM
*) iohandler
->stream
;
151 cmsUInt32Number len
= size
* count
;
153 if (ResData
-> Pointer
+ len
> ResData
-> Size
){
155 len
= (ResData
-> Size
- ResData
-> Pointer
);
156 cmsSignalError(iohandler
->ContextID
, cmsERROR_READ
, "Read from memory error. Got %d bytes, block should be of %d bytes", len
, count
* size
);
160 Ptr
= ResData
-> Block
;
161 Ptr
+= ResData
-> Pointer
;
162 memmove(Buffer
, Ptr
, len
);
163 ResData
-> Pointer
+= len
;
168 // SEEK_CUR is assumed
170 cmsBool
MemorySeek(struct _cms_io_handler
* iohandler
, cmsUInt32Number offset
)
172 FILEMEM
* ResData
= (FILEMEM
*) iohandler
->stream
;
174 if (offset
> ResData
->Size
) {
175 cmsSignalError(iohandler
->ContextID
, cmsERROR_SEEK
, "Too few data; probably corrupted profile");
179 ResData
->Pointer
= offset
;
185 cmsUInt32Number
MemoryTell(struct _cms_io_handler
* iohandler
)
187 FILEMEM
* ResData
= (FILEMEM
*) iohandler
->stream
;
189 if (ResData
== NULL
) return 0;
190 return ResData
-> Pointer
;
194 // Writes data to memory, also keeps used space for further reference.
196 cmsBool
MemoryWrite(struct _cms_io_handler
* iohandler
, cmsUInt32Number size
, const void *Ptr
)
198 FILEMEM
* ResData
= (FILEMEM
*) iohandler
->stream
;
200 if (ResData
== NULL
) return FALSE
; // Housekeeping
202 // Check for available space. Clip.
203 if (ResData
->Pointer
+ size
> ResData
->Size
) {
204 size
= ResData
->Size
- ResData
->Pointer
;
207 if (size
== 0) return TRUE
; // Write zero bytes is ok, but does nothing
209 memmove(ResData
->Block
+ ResData
->Pointer
, Ptr
, size
);
210 ResData
->Pointer
+= size
;
212 if (ResData
->Pointer
> iohandler
->UsedSpace
)
213 iohandler
->UsedSpace
= ResData
->Pointer
;
220 cmsBool
MemoryClose(struct _cms_io_handler
* iohandler
)
222 FILEMEM
* ResData
= (FILEMEM
*) iohandler
->stream
;
224 if (ResData
->FreeBlockOnClose
) {
226 if (ResData
->Block
) _cmsFree(iohandler
->ContextID
, ResData
->Block
);
229 _cmsFree(iohandler
->ContextID
, ResData
);
230 _cmsFree(iohandler
->ContextID
, iohandler
);
235 // Create a iohandler for memory block. AccessMode=='r' assumes the iohandler is going to read, and makes
236 // a copy of the memory block for letting user to free the memory after invoking open profile. In write
237 // mode ("w"), Buffer points to the begin of memory block to be written.
238 cmsIOHANDLER
* CMSEXPORT
cmsOpenIOhandlerFromMem(cmsContext ContextID
, void *Buffer
, cmsUInt32Number size
, const char* AccessMode
)
240 cmsIOHANDLER
* iohandler
= NULL
;
243 _cmsAssert(AccessMode
!= NULL
);
245 iohandler
= (cmsIOHANDLER
*) _cmsMallocZero(ContextID
, sizeof(cmsIOHANDLER
));
246 if (iohandler
== NULL
) return NULL
;
248 switch (*AccessMode
) {
251 fm
= (FILEMEM
*) _cmsMallocZero(ContextID
, sizeof(FILEMEM
));
252 if (fm
== NULL
) goto Error
;
254 if (Buffer
== NULL
) {
255 cmsSignalError(ContextID
, cmsERROR_READ
, "Couldn't read profile from NULL pointer");
259 fm
->Block
= (cmsUInt8Number
*) _cmsMalloc(ContextID
, size
);
260 if (fm
->Block
== NULL
) {
262 _cmsFree(ContextID
, fm
);
263 _cmsFree(ContextID
, iohandler
);
264 cmsSignalError(ContextID
, cmsERROR_READ
, "Couldn't allocate %ld bytes for profile", (long) size
);
269 memmove(fm
->Block
, Buffer
, size
);
270 fm
->FreeBlockOnClose
= TRUE
;
273 iohandler
-> ReportedSize
= size
;
277 fm
= (FILEMEM
*) _cmsMallocZero(ContextID
, sizeof(FILEMEM
));
278 if (fm
== NULL
) goto Error
;
280 fm
->Block
= (cmsUInt8Number
*) Buffer
;
281 fm
->FreeBlockOnClose
= FALSE
;
284 iohandler
-> ReportedSize
= 0;
288 cmsSignalError(ContextID
, cmsERROR_UNKNOWN_EXTENSION
, "Unknown access mode '%c'", *AccessMode
);
292 iohandler
->ContextID
= ContextID
;
293 iohandler
->stream
= (void*) fm
;
294 iohandler
->UsedSpace
= 0;
295 iohandler
->PhysicalFile
[0] = 0;
297 iohandler
->Read
= MemoryRead
;
298 iohandler
->Seek
= MemorySeek
;
299 iohandler
->Close
= MemoryClose
;
300 iohandler
->Tell
= MemoryTell
;
301 iohandler
->Write
= MemoryWrite
;
306 if (fm
) _cmsFree(ContextID
, fm
);
307 if (iohandler
) _cmsFree(ContextID
, iohandler
);
311 // File-based stream -------------------------------------------------------
313 // Read count elements of size bytes each. Return number of elements read
315 cmsUInt32Number
FileRead(cmsIOHANDLER
* iohandler
, void *Buffer
, cmsUInt32Number size
, cmsUInt32Number count
)
317 cmsUInt32Number nReaded
= (cmsUInt32Number
) fread(Buffer
, size
, count
, (FILE*) iohandler
->stream
);
319 if (nReaded
!= count
) {
320 cmsSignalError(iohandler
->ContextID
, cmsERROR_FILE
, "Read error. Got %d bytes, block should be of %d bytes", nReaded
* size
, count
* size
);
327 // Position file pointer in the file
329 cmsBool
FileSeek(cmsIOHANDLER
* iohandler
, cmsUInt32Number offset
)
331 if (fseek((FILE*) iohandler
->stream
, (long) offset
, SEEK_SET
) != 0) {
333 cmsSignalError(iohandler
->ContextID
, cmsERROR_FILE
, "Seek error; probably corrupted file");
340 // Returns file pointer position or 0 on error, which is also a valid position.
342 cmsUInt32Number
FileTell(cmsIOHANDLER
* iohandler
)
344 long t
= ftell((FILE*)iohandler
->stream
);
346 cmsSignalError(iohandler
->ContextID
, cmsERROR_FILE
, "Tell error; probably corrupted file");
350 return (cmsUInt32Number
)t
;
353 // Writes data to stream, also keeps used space for further reference. Returns TRUE on success, FALSE on error
355 cmsBool
FileWrite(cmsIOHANDLER
* iohandler
, cmsUInt32Number size
, const void* Buffer
)
357 if (size
== 0) return TRUE
; // We allow to write 0 bytes, but nothing is written
359 iohandler
->UsedSpace
+= size
;
360 return (fwrite(Buffer
, size
, 1, (FILE*)iohandler
->stream
) == 1);
365 cmsBool
FileClose(cmsIOHANDLER
* iohandler
)
367 if (fclose((FILE*) iohandler
->stream
) != 0) return FALSE
;
368 _cmsFree(iohandler
->ContextID
, iohandler
);
372 // Create a iohandler for disk based files.
373 cmsIOHANDLER
* CMSEXPORT
cmsOpenIOhandlerFromFile(cmsContext ContextID
, const char* FileName
, const char* AccessMode
)
375 cmsIOHANDLER
* iohandler
= NULL
;
377 cmsInt32Number fileLen
;
378 char mode
[4] = { 0,0,0,0 };
380 _cmsAssert(FileName
!= NULL
);
381 _cmsAssert(AccessMode
!= NULL
);
383 iohandler
= (cmsIOHANDLER
*) _cmsMallocZero(ContextID
, sizeof(cmsIOHANDLER
));
384 if (iohandler
== NULL
) return NULL
;
386 // Validate access mode
387 while (*AccessMode
) {
395 mode
[0] = *AccessMode
;
399 _cmsFree(ContextID
, iohandler
);
400 cmsSignalError(ContextID
, cmsERROR_FILE
, "Access mode already specified '%c'", *AccessMode
);
405 // Close on exec. Not all runtime supports that. Up to the caller to decide.
411 _cmsFree(ContextID
, iohandler
);
412 cmsSignalError(ContextID
, cmsERROR_FILE
, "Wrong access mode '%c'", *AccessMode
);
422 fm
= fopen(FileName
, mode
);
424 _cmsFree(ContextID
, iohandler
);
425 cmsSignalError(ContextID
, cmsERROR_FILE
, "File '%s' not found", FileName
);
428 fileLen
= (cmsInt32Number
)cmsfilelength(fm
);
432 _cmsFree(ContextID
, iohandler
);
433 cmsSignalError(ContextID
, cmsERROR_FILE
, "Cannot get size of file '%s'", FileName
);
436 iohandler
-> ReportedSize
= (cmsUInt32Number
) fileLen
;
440 fm
= fopen(FileName
, mode
);
442 _cmsFree(ContextID
, iohandler
);
443 cmsSignalError(ContextID
, cmsERROR_FILE
, "Couldn't create '%s'", FileName
);
446 iohandler
-> ReportedSize
= 0;
450 _cmsFree(ContextID
, iohandler
); // Would never reach
454 iohandler
->ContextID
= ContextID
;
455 iohandler
->stream
= (void*) fm
;
456 iohandler
->UsedSpace
= 0;
458 // Keep track of the original file
459 strncpy(iohandler
-> PhysicalFile
, FileName
, sizeof(iohandler
-> PhysicalFile
)-1);
460 iohandler
-> PhysicalFile
[sizeof(iohandler
-> PhysicalFile
)-1] = 0;
462 iohandler
->Read
= FileRead
;
463 iohandler
->Seek
= FileSeek
;
464 iohandler
->Close
= FileClose
;
465 iohandler
->Tell
= FileTell
;
466 iohandler
->Write
= FileWrite
;
471 // Create a iohandler for stream based files
472 cmsIOHANDLER
* CMSEXPORT
cmsOpenIOhandlerFromStream(cmsContext ContextID
, FILE* Stream
)
474 cmsIOHANDLER
* iohandler
= NULL
;
475 cmsInt32Number fileSize
;
477 fileSize
= (cmsInt32Number
)cmsfilelength(Stream
);
480 cmsSignalError(ContextID
, cmsERROR_FILE
, "Cannot get size of stream");
484 iohandler
= (cmsIOHANDLER
*) _cmsMallocZero(ContextID
, sizeof(cmsIOHANDLER
));
485 if (iohandler
== NULL
) return NULL
;
487 iohandler
-> ContextID
= ContextID
;
488 iohandler
-> stream
= (void*) Stream
;
489 iohandler
-> UsedSpace
= 0;
490 iohandler
-> ReportedSize
= (cmsUInt32Number
) fileSize
;
491 iohandler
-> PhysicalFile
[0] = 0;
493 iohandler
->Read
= FileRead
;
494 iohandler
->Seek
= FileSeek
;
495 iohandler
->Close
= FileClose
;
496 iohandler
->Tell
= FileTell
;
497 iohandler
->Write
= FileWrite
;
504 // Close an open IO handler
505 cmsBool CMSEXPORT
cmsCloseIOhandler(cmsIOHANDLER
* io
)
507 return io
-> Close(io
);
510 // -------------------------------------------------------------------------------------------------------
512 cmsIOHANDLER
* CMSEXPORT
cmsGetProfileIOhandler(cmsHPROFILE hProfile
)
514 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*)hProfile
;
516 if (Icc
== NULL
) return NULL
;
517 return Icc
->IOhandler
;
520 // Creates an empty structure holding all required parameters
521 cmsHPROFILE CMSEXPORT
cmsCreateProfilePlaceholder(cmsContext ContextID
)
523 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) _cmsMallocZero(ContextID
, sizeof(_cmsICCPROFILE
));
524 if (Icc
== NULL
) return NULL
;
526 Icc
->ContextID
= ContextID
;
531 // Set default version
532 Icc
->Version
= 0x02100000;
534 // Set default CMM (that's me!)
535 Icc
->CMM
= lcmsSignature
;
537 // Set default creator
538 // Created by LittleCMS (that's me!)
539 Icc
->creator
= lcmsSignature
;
541 // Set default platform
542 #ifdef CMS_IS_WINDOWS_
543 Icc
->platform
= cmsSigMicrosoft
;
545 Icc
->platform
= cmsSigMacintosh
;
548 // Set default device class
549 Icc
->DeviceClass
= cmsSigDisplayClass
;
551 // Set creation date/time
552 if (!_cmsGetTime(&Icc
->Created
))
555 // Create a mutex if the user provided proper plugin. NULL otherwise
556 Icc
->UsrMutex
= _cmsCreateMutex(ContextID
);
559 return (cmsHPROFILE
) Icc
;
562 _cmsFree(ContextID
, Icc
);
566 cmsContext CMSEXPORT
cmsGetProfileContextID(cmsHPROFILE hProfile
)
568 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
570 if (Icc
== NULL
) return NULL
;
571 return Icc
-> ContextID
;
575 // Return the number of tags
576 cmsInt32Number CMSEXPORT
cmsGetTagCount(cmsHPROFILE hProfile
)
578 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
579 if (Icc
== NULL
) return -1;
581 return (cmsInt32Number
) Icc
->TagCount
;
584 // Return the tag signature of a given tag number
585 cmsTagSignature CMSEXPORT
cmsGetTagSignature(cmsHPROFILE hProfile
, cmsUInt32Number n
)
587 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
589 if (n
> Icc
->TagCount
) return (cmsTagSignature
) 0; // Mark as not available
590 if (n
>= MAX_TABLE_TAG
) return (cmsTagSignature
) 0; // As double check
592 return Icc
->TagNames
[n
];
597 int SearchOneTag(_cmsICCPROFILE
* Profile
, cmsTagSignature sig
)
601 for (i
=0; i
< (int) Profile
-> TagCount
; i
++) {
603 if (sig
== Profile
-> TagNames
[i
])
610 // Search for a specific tag in tag dictionary. Returns position or -1 if tag not found.
611 // If followlinks is turned on, then the position of the linked tag is returned
612 int _cmsSearchTag(_cmsICCPROFILE
* Icc
, cmsTagSignature sig
, cmsBool lFollowLinks
)
615 cmsTagSignature LinkedSig
;
619 // Search for given tag in ICC profile directory
620 n
= SearchOneTag(Icc
, sig
);
622 return -1; // Not found
625 return n
; // Found, don't follow links
627 // Is this a linked tag?
628 LinkedSig
= Icc
->TagLinked
[n
];
631 if (LinkedSig
!= (cmsTagSignature
) 0) {
635 } while (LinkedSig
!= (cmsTagSignature
) 0);
640 // Deletes a tag entry
643 void _cmsDeleteTagByPos(_cmsICCPROFILE
* Icc
, int i
)
645 _cmsAssert(Icc
!= NULL
);
649 if (Icc
-> TagPtrs
[i
] != NULL
) {
651 // Free previous version
652 if (Icc
->TagSaveAsRaw
[i
]) {
653 _cmsFree(Icc
->ContextID
, Icc
->TagPtrs
[i
]);
656 cmsTagTypeHandler
* TypeHandler
= Icc
->TagTypeHandlers
[i
];
658 if (TypeHandler
!= NULL
) {
660 cmsTagTypeHandler LocalTypeHandler
= *TypeHandler
;
661 LocalTypeHandler
.ContextID
= Icc
->ContextID
; // As an additional parameter
662 LocalTypeHandler
.ICCVersion
= Icc
->Version
;
663 LocalTypeHandler
.FreePtr(&LocalTypeHandler
, Icc
-> TagPtrs
[i
]);
664 Icc
->TagPtrs
[i
] = NULL
;
672 // Creates a new tag entry
674 cmsBool
_cmsNewTag(_cmsICCPROFILE
* Icc
, cmsTagSignature sig
, int* NewPos
)
678 // Search for the tag
679 i
= _cmsSearchTag(Icc
, sig
, FALSE
);
682 // Already exists? delete it
683 _cmsDeleteTagByPos(Icc
, i
);
688 // No, make a new one
689 if (Icc
-> TagCount
>= MAX_TABLE_TAG
) {
690 cmsSignalError(Icc
->ContextID
, cmsERROR_RANGE
, "Too many tags (%d)", MAX_TABLE_TAG
);
694 *NewPos
= (int) Icc
->TagCount
;
703 cmsBool CMSEXPORT
cmsIsTag(cmsHPROFILE hProfile
, cmsTagSignature sig
)
705 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) (void*) hProfile
;
706 return _cmsSearchTag(Icc
, sig
, FALSE
) >= 0;
711 // Checks for link compatibility
713 cmsBool
CompatibleTypes(const cmsTagDescriptor
* desc1
, const cmsTagDescriptor
* desc2
)
717 if (desc1
== NULL
|| desc2
== NULL
) return FALSE
;
719 if (desc1
->nSupportedTypes
!= desc2
->nSupportedTypes
) return FALSE
;
720 if (desc1
->ElemCount
!= desc2
->ElemCount
) return FALSE
;
722 for (i
= 0; i
< desc1
->nSupportedTypes
; i
++)
724 if (desc1
->SupportedTypes
[i
] != desc2
->SupportedTypes
[i
]) return FALSE
;
730 // Enforces that the profile version is per. spec.
731 // Operates on the big endian bytes from the profile.
732 // Called before converting to platform endianness.
733 // Byte 0 is BCD major version, so max 9.
734 // Byte 1 is 2 BCD digits, one per nibble.
735 // Reserved bytes 2 & 3 must be 0.
737 cmsUInt32Number
_validatedVersion(cmsUInt32Number DWord
)
739 cmsUInt8Number
* pByte
= (cmsUInt8Number
*) &DWord
;
740 cmsUInt8Number temp1
;
741 cmsUInt8Number temp2
;
743 if (*pByte
> 0x09) *pByte
= (cmsUInt8Number
) 0x09;
744 temp1
= (cmsUInt8Number
) (*(pByte
+1) & 0xf0);
745 temp2
= (cmsUInt8Number
) (*(pByte
+1) & 0x0f);
746 if (temp1
> 0x90U
) temp1
= 0x90U
;
747 if (temp2
> 0x09U
) temp2
= 0x09U
;
748 *(pByte
+1) = (cmsUInt8Number
)(temp1
| temp2
);
749 *(pByte
+2) = (cmsUInt8Number
)0;
750 *(pByte
+3) = (cmsUInt8Number
)0;
755 // Check device class
757 cmsBool
validDeviceClass(cmsProfileClassSignature cl
)
759 if ((int)cl
== 0) return TRUE
; // We allow zero because older lcms versions defaulted to that.
763 case cmsSigInputClass
:
764 case cmsSigDisplayClass
:
765 case cmsSigOutputClass
:
766 case cmsSigLinkClass
:
767 case cmsSigAbstractClass
:
768 case cmsSigColorSpaceClass
:
769 case cmsSigNamedColorClass
:
778 // Read profile header and validate it
779 cmsBool
_cmsReadHeader(_cmsICCPROFILE
* Icc
)
783 cmsUInt32Number i
, j
;
784 cmsUInt32Number HeaderSize
;
785 cmsIOHANDLER
* io
= Icc
->IOhandler
;
786 cmsUInt32Number TagCount
;
790 if (io
-> Read(io
, &Header
, sizeof(cmsICCHeader
), 1) != 1) {
794 // Validate file as an ICC profile
795 if (_cmsAdjustEndianess32(Header
.magic
) != cmsMagicNumber
) {
796 cmsSignalError(Icc
->ContextID
, cmsERROR_BAD_SIGNATURE
, "not an ICC profile, invalid signature");
800 // Adjust endianness of the used parameters
801 Icc
-> CMM
= _cmsAdjustEndianess32(Header
.cmmId
);
802 Icc
-> DeviceClass
= (cmsProfileClassSignature
) _cmsAdjustEndianess32(Header
.deviceClass
);
803 Icc
-> ColorSpace
= (cmsColorSpaceSignature
) _cmsAdjustEndianess32(Header
.colorSpace
);
804 Icc
-> PCS
= (cmsColorSpaceSignature
) _cmsAdjustEndianess32(Header
.pcs
);
806 Icc
-> RenderingIntent
= _cmsAdjustEndianess32(Header
.renderingIntent
);
807 Icc
-> platform
= (cmsPlatformSignature
)_cmsAdjustEndianess32(Header
.platform
);
808 Icc
-> flags
= _cmsAdjustEndianess32(Header
.flags
);
809 Icc
-> manufacturer
= _cmsAdjustEndianess32(Header
.manufacturer
);
810 Icc
-> model
= _cmsAdjustEndianess32(Header
.model
);
811 Icc
-> creator
= _cmsAdjustEndianess32(Header
.creator
);
813 _cmsAdjustEndianess64(&Icc
-> attributes
, &Header
.attributes
);
814 Icc
-> Version
= _cmsAdjustEndianess32(_validatedVersion(Header
.version
));
816 if (Icc
->Version
> 0x5000000) {
817 cmsSignalError(Icc
->ContextID
, cmsERROR_UNKNOWN_EXTENSION
, "Unsupported profile version '0x%x'", Icc
->Version
);
821 if (!validDeviceClass(Icc
->DeviceClass
)) {
822 cmsSignalError(Icc
->ContextID
, cmsERROR_UNKNOWN_EXTENSION
, "Unsupported device class '0x%x'", Icc
->DeviceClass
);
826 // Get size as reported in header
827 HeaderSize
= _cmsAdjustEndianess32(Header
.size
);
829 // Make sure HeaderSize is lower than profile size
830 if (HeaderSize
>= Icc
->IOhandler
->ReportedSize
)
831 HeaderSize
= Icc
->IOhandler
->ReportedSize
;
834 // Get creation date/time
835 _cmsDecodeDateTimeNumber(&Header
.date
, &Icc
->Created
);
837 // The profile ID are 32 raw bytes
838 memmove(Icc
->ProfileID
.ID32
, Header
.profileID
.ID32
, 16);
841 // Read tag directory
842 if (!_cmsReadUInt32Number(io
, &TagCount
)) return FALSE
;
843 if (TagCount
> MAX_TABLE_TAG
) {
845 cmsSignalError(Icc
->ContextID
, cmsERROR_RANGE
, "Too many tags (%d)", TagCount
);
850 // Read tag directory
852 for (i
=0; i
< TagCount
; i
++) {
854 if (!_cmsReadUInt32Number(io
, (cmsUInt32Number
*) &Tag
.sig
)) return FALSE
;
855 if (!_cmsReadUInt32Number(io
, &Tag
.offset
)) return FALSE
;
856 if (!_cmsReadUInt32Number(io
, &Tag
.size
)) return FALSE
;
858 // Perform some sanity check. Offset + size should fall inside file.
859 if (Tag
.size
== 0 || Tag
.offset
== 0) continue;
860 if (Tag
.offset
+ Tag
.size
> HeaderSize
||
861 Tag
.offset
+ Tag
.size
< Tag
.offset
)
864 Icc
-> TagNames
[Icc
->TagCount
] = Tag
.sig
;
865 Icc
-> TagOffsets
[Icc
->TagCount
] = Tag
.offset
;
866 Icc
-> TagSizes
[Icc
->TagCount
] = Tag
.size
;
869 for (j
=0; j
< Icc
->TagCount
; j
++) {
871 if ((Icc
->TagOffsets
[j
] == Tag
.offset
) &&
872 (Icc
->TagSizes
[j
] == Tag
.size
)) {
875 if (CompatibleTypes(_cmsGetTagDescriptor(Icc
->ContextID
, Icc
->TagNames
[j
]),
876 _cmsGetTagDescriptor(Icc
->ContextID
, Tag
.sig
))) {
878 Icc
->TagLinked
[Icc
->TagCount
] = Icc
->TagNames
[j
];
888 for (i
= 0; i
< Icc
->TagCount
; i
++) {
889 for (j
= 0; j
< Icc
->TagCount
; j
++) {
891 // Tags cannot be duplicate
892 if ((i
!= j
) && (Icc
->TagNames
[i
] == Icc
->TagNames
[j
])) {
893 cmsSignalError(Icc
->ContextID
, cmsERROR_RANGE
, "Duplicate tag found");
903 // Saves profile header
904 cmsBool
_cmsWriteHeader(_cmsICCPROFILE
* Icc
, cmsUInt32Number UsedSpace
)
909 cmsUInt32Number Count
;
911 Header
.size
= _cmsAdjustEndianess32(UsedSpace
);
912 Header
.cmmId
= _cmsAdjustEndianess32(Icc
->CMM
);
913 Header
.version
= _cmsAdjustEndianess32(Icc
->Version
);
915 Header
.deviceClass
= (cmsProfileClassSignature
) _cmsAdjustEndianess32(Icc
-> DeviceClass
);
916 Header
.colorSpace
= (cmsColorSpaceSignature
) _cmsAdjustEndianess32(Icc
-> ColorSpace
);
917 Header
.pcs
= (cmsColorSpaceSignature
) _cmsAdjustEndianess32(Icc
-> PCS
);
919 // NOTE: in v4 Timestamp must be in UTC rather than in local time
920 _cmsEncodeDateTimeNumber(&Header
.date
, &Icc
->Created
);
922 Header
.magic
= _cmsAdjustEndianess32(cmsMagicNumber
);
924 Header
.platform
= (cmsPlatformSignature
) _cmsAdjustEndianess32(Icc
-> platform
);
926 Header
.flags
= _cmsAdjustEndianess32(Icc
-> flags
);
927 Header
.manufacturer
= _cmsAdjustEndianess32(Icc
-> manufacturer
);
928 Header
.model
= _cmsAdjustEndianess32(Icc
-> model
);
930 _cmsAdjustEndianess64(&Header
.attributes
, &Icc
-> attributes
);
932 // Rendering intent in the header (for embedded profiles)
933 Header
.renderingIntent
= _cmsAdjustEndianess32(Icc
-> RenderingIntent
);
935 // Illuminant is always D50
936 Header
.illuminant
.X
= (cmsS15Fixed16Number
) _cmsAdjustEndianess32((cmsUInt32Number
) _cmsDoubleTo15Fixed16(cmsD50_XYZ()->X
));
937 Header
.illuminant
.Y
= (cmsS15Fixed16Number
) _cmsAdjustEndianess32((cmsUInt32Number
) _cmsDoubleTo15Fixed16(cmsD50_XYZ()->Y
));
938 Header
.illuminant
.Z
= (cmsS15Fixed16Number
) _cmsAdjustEndianess32((cmsUInt32Number
) _cmsDoubleTo15Fixed16(cmsD50_XYZ()->Z
));
940 Header
.creator
= _cmsAdjustEndianess32(Icc
->creator
);
942 memset(&Header
.reserved
, 0, sizeof(Header
.reserved
));
944 // Set profile ID. Endianness is always big endian
945 memmove(&Header
.profileID
, &Icc
->ProfileID
, 16);
948 if (!Icc
-> IOhandler
->Write(Icc
->IOhandler
, sizeof(cmsICCHeader
), &Header
)) return FALSE
;
950 // Saves Tag directory
954 for (i
=0; i
< Icc
-> TagCount
; i
++) {
955 if (Icc
->TagNames
[i
] != (cmsTagSignature
) 0)
959 // Store number of tags
960 if (!_cmsWriteUInt32Number(Icc
->IOhandler
, Count
)) return FALSE
;
962 for (i
=0; i
< Icc
-> TagCount
; i
++) {
964 if (Icc
->TagNames
[i
] == (cmsTagSignature
) 0) continue; // It is just a placeholder
966 Tag
.sig
= (cmsTagSignature
) _cmsAdjustEndianess32((cmsUInt32Number
) Icc
-> TagNames
[i
]);
967 Tag
.offset
= _cmsAdjustEndianess32((cmsUInt32Number
) Icc
-> TagOffsets
[i
]);
968 Tag
.size
= _cmsAdjustEndianess32((cmsUInt32Number
) Icc
-> TagSizes
[i
]);
970 if (!Icc
->IOhandler
-> Write(Icc
-> IOhandler
, sizeof(cmsTagEntry
), &Tag
)) return FALSE
;
976 // ----------------------------------------------------------------------- Set/Get several struct members
979 cmsUInt32Number CMSEXPORT
cmsGetHeaderRenderingIntent(cmsHPROFILE hProfile
)
981 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
982 return Icc
-> RenderingIntent
;
985 void CMSEXPORT
cmsSetHeaderRenderingIntent(cmsHPROFILE hProfile
, cmsUInt32Number RenderingIntent
)
987 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
988 Icc
-> RenderingIntent
= RenderingIntent
;
991 cmsUInt32Number CMSEXPORT
cmsGetHeaderFlags(cmsHPROFILE hProfile
)
993 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
994 return (cmsUInt32Number
) Icc
-> flags
;
997 void CMSEXPORT
cmsSetHeaderFlags(cmsHPROFILE hProfile
, cmsUInt32Number Flags
)
999 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1000 Icc
-> flags
= (cmsUInt32Number
) Flags
;
1003 cmsUInt32Number CMSEXPORT
cmsGetHeaderManufacturer(cmsHPROFILE hProfile
)
1005 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1006 return Icc
->manufacturer
;
1009 void CMSEXPORT
cmsSetHeaderManufacturer(cmsHPROFILE hProfile
, cmsUInt32Number manufacturer
)
1011 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1012 Icc
-> manufacturer
= manufacturer
;
1015 cmsUInt32Number CMSEXPORT
cmsGetHeaderCreator(cmsHPROFILE hProfile
)
1017 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1018 return Icc
->creator
;
1021 cmsUInt32Number CMSEXPORT
cmsGetHeaderModel(cmsHPROFILE hProfile
)
1023 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1027 void CMSEXPORT
cmsSetHeaderModel(cmsHPROFILE hProfile
, cmsUInt32Number model
)
1029 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1030 Icc
-> model
= model
;
1033 void CMSEXPORT
cmsGetHeaderAttributes(cmsHPROFILE hProfile
, cmsUInt64Number
* Flags
)
1035 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1036 memmove(Flags
, &Icc
-> attributes
, sizeof(cmsUInt64Number
));
1039 void CMSEXPORT
cmsSetHeaderAttributes(cmsHPROFILE hProfile
, cmsUInt64Number Flags
)
1041 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1042 memmove(&Icc
-> attributes
, &Flags
, sizeof(cmsUInt64Number
));
1045 void CMSEXPORT
cmsGetHeaderProfileID(cmsHPROFILE hProfile
, cmsUInt8Number
* ProfileID
)
1047 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1048 memmove(ProfileID
, Icc
->ProfileID
.ID8
, 16);
1051 void CMSEXPORT
cmsSetHeaderProfileID(cmsHPROFILE hProfile
, cmsUInt8Number
* ProfileID
)
1053 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1054 memmove(&Icc
-> ProfileID
, ProfileID
, 16);
1057 cmsBool CMSEXPORT
cmsGetHeaderCreationDateTime(cmsHPROFILE hProfile
, struct tm
*Dest
)
1059 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1060 memmove(Dest
, &Icc
->Created
, sizeof(struct tm
));
1064 cmsColorSpaceSignature CMSEXPORT
cmsGetPCS(cmsHPROFILE hProfile
)
1066 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1070 void CMSEXPORT
cmsSetPCS(cmsHPROFILE hProfile
, cmsColorSpaceSignature pcs
)
1072 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1076 cmsColorSpaceSignature CMSEXPORT
cmsGetColorSpace(cmsHPROFILE hProfile
)
1078 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1079 return Icc
-> ColorSpace
;
1082 void CMSEXPORT
cmsSetColorSpace(cmsHPROFILE hProfile
, cmsColorSpaceSignature sig
)
1084 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1085 Icc
-> ColorSpace
= sig
;
1088 cmsProfileClassSignature CMSEXPORT
cmsGetDeviceClass(cmsHPROFILE hProfile
)
1090 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1091 return Icc
-> DeviceClass
;
1094 void CMSEXPORT
cmsSetDeviceClass(cmsHPROFILE hProfile
, cmsProfileClassSignature sig
)
1096 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1097 Icc
-> DeviceClass
= sig
;
1100 cmsUInt32Number CMSEXPORT
cmsGetEncodedICCversion(cmsHPROFILE hProfile
)
1102 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1103 return Icc
-> Version
;
1106 void CMSEXPORT
cmsSetEncodedICCversion(cmsHPROFILE hProfile
, cmsUInt32Number Version
)
1108 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1109 Icc
-> Version
= Version
;
1112 // Get an hexadecimal number with same digits as v
1114 cmsUInt32Number
BaseToBase(cmsUInt32Number in
, int BaseIn
, int BaseOut
)
1118 cmsUInt32Number out
;
1120 for (len
=0; in
> 0 && len
< 100; len
++) {
1122 Buff
[len
] = (char) (in
% BaseIn
);
1126 for (i
=len
-1, out
=0; i
>= 0; --i
) {
1127 out
= out
* BaseOut
+ Buff
[i
];
1133 void CMSEXPORT
cmsSetProfileVersion(cmsHPROFILE hProfile
, cmsFloat64Number Version
)
1135 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1139 Icc
-> Version
= BaseToBase((cmsUInt32Number
) floor(Version
* 100.0 + 0.5), 10, 16) << 16;
1142 cmsFloat64Number CMSEXPORT
cmsGetProfileVersion(cmsHPROFILE hProfile
)
1144 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1145 cmsUInt32Number n
= Icc
-> Version
>> 16;
1147 return BaseToBase(n
, 16, 10) / 100.0;
1149 // --------------------------------------------------------------------------------------------------------------
1152 // Create profile from IOhandler
1153 cmsHPROFILE CMSEXPORT
cmsOpenProfileFromIOhandlerTHR(cmsContext ContextID
, cmsIOHANDLER
* io
)
1155 _cmsICCPROFILE
* NewIcc
;
1156 cmsHPROFILE hEmpty
= cmsCreateProfilePlaceholder(ContextID
);
1158 if (hEmpty
== NULL
) return NULL
;
1160 NewIcc
= (_cmsICCPROFILE
*) hEmpty
;
1162 NewIcc
->IOhandler
= io
;
1163 if (!_cmsReadHeader(NewIcc
)) goto Error
;
1167 cmsCloseProfile(hEmpty
);
1171 // Create profile from IOhandler
1172 cmsHPROFILE CMSEXPORT
cmsOpenProfileFromIOhandler2THR(cmsContext ContextID
, cmsIOHANDLER
* io
, cmsBool write
)
1174 _cmsICCPROFILE
* NewIcc
;
1175 cmsHPROFILE hEmpty
= cmsCreateProfilePlaceholder(ContextID
);
1177 if (hEmpty
== NULL
) return NULL
;
1179 NewIcc
= (_cmsICCPROFILE
*) hEmpty
;
1181 NewIcc
->IOhandler
= io
;
1184 NewIcc
-> IsWrite
= TRUE
;
1188 if (!_cmsReadHeader(NewIcc
)) goto Error
;
1192 cmsCloseProfile(hEmpty
);
1197 // Create profile from disk file
1198 cmsHPROFILE CMSEXPORT
cmsOpenProfileFromFileTHR(cmsContext ContextID
, const char *lpFileName
, const char *sAccess
)
1200 _cmsICCPROFILE
* NewIcc
;
1201 cmsHPROFILE hEmpty
= cmsCreateProfilePlaceholder(ContextID
);
1203 if (hEmpty
== NULL
) return NULL
;
1205 NewIcc
= (_cmsICCPROFILE
*) hEmpty
;
1207 NewIcc
->IOhandler
= cmsOpenIOhandlerFromFile(ContextID
, lpFileName
, sAccess
);
1208 if (NewIcc
->IOhandler
== NULL
) goto Error
;
1210 if (*sAccess
== 'W' || *sAccess
== 'w') {
1212 NewIcc
-> IsWrite
= TRUE
;
1217 if (!_cmsReadHeader(NewIcc
)) goto Error
;
1221 cmsCloseProfile(hEmpty
);
1226 cmsHPROFILE CMSEXPORT
cmsOpenProfileFromFile(const char *ICCProfile
, const char *sAccess
)
1228 return cmsOpenProfileFromFileTHR(NULL
, ICCProfile
, sAccess
);
1232 cmsHPROFILE CMSEXPORT
cmsOpenProfileFromStreamTHR(cmsContext ContextID
, FILE* ICCProfile
, const char *sAccess
)
1234 _cmsICCPROFILE
* NewIcc
;
1235 cmsHPROFILE hEmpty
= cmsCreateProfilePlaceholder(ContextID
);
1237 if (hEmpty
== NULL
) return NULL
;
1239 NewIcc
= (_cmsICCPROFILE
*) hEmpty
;
1241 NewIcc
->IOhandler
= cmsOpenIOhandlerFromStream(ContextID
, ICCProfile
);
1242 if (NewIcc
->IOhandler
== NULL
) goto Error
;
1244 if (*sAccess
== 'w') {
1246 NewIcc
-> IsWrite
= TRUE
;
1250 if (!_cmsReadHeader(NewIcc
)) goto Error
;
1254 cmsCloseProfile(hEmpty
);
1259 cmsHPROFILE CMSEXPORT
cmsOpenProfileFromStream(FILE* ICCProfile
, const char *sAccess
)
1261 return cmsOpenProfileFromStreamTHR(NULL
, ICCProfile
, sAccess
);
1265 // Open from memory block
1266 cmsHPROFILE CMSEXPORT
cmsOpenProfileFromMemTHR(cmsContext ContextID
, const void* MemPtr
, cmsUInt32Number dwSize
)
1268 _cmsICCPROFILE
* NewIcc
;
1271 hEmpty
= cmsCreateProfilePlaceholder(ContextID
);
1272 if (hEmpty
== NULL
) return NULL
;
1274 NewIcc
= (_cmsICCPROFILE
*) hEmpty
;
1276 // Ok, in this case const void* is casted to void* just because open IO handler
1277 // shares read and writing modes. Don't abuse this feature!
1278 NewIcc
->IOhandler
= cmsOpenIOhandlerFromMem(ContextID
, (void*) MemPtr
, dwSize
, "r");
1279 if (NewIcc
->IOhandler
== NULL
) goto Error
;
1281 if (!_cmsReadHeader(NewIcc
)) goto Error
;
1286 cmsCloseProfile(hEmpty
);
1290 cmsHPROFILE CMSEXPORT
cmsOpenProfileFromMem(const void* MemPtr
, cmsUInt32Number dwSize
)
1292 return cmsOpenProfileFromMemTHR(NULL
, MemPtr
, dwSize
);
1297 // Dump tag contents. If the profile is being modified, untouched tags are copied from FileOrig
1299 cmsBool
SaveTags(_cmsICCPROFILE
* Icc
, _cmsICCPROFILE
* FileOrig
)
1301 cmsUInt8Number
* Data
;
1303 cmsUInt32Number Begin
;
1304 cmsIOHANDLER
* io
= Icc
->IOhandler
;
1305 cmsTagDescriptor
* TagDescriptor
;
1306 cmsTagTypeSignature TypeBase
;
1307 cmsTagTypeSignature Type
;
1308 cmsTagTypeHandler
* TypeHandler
;
1309 cmsFloat64Number Version
= cmsGetProfileVersion((cmsHPROFILE
) Icc
);
1310 cmsTagTypeHandler LocalTypeHandler
;
1312 for (i
=0; i
< Icc
-> TagCount
; i
++) {
1314 if (Icc
->TagNames
[i
] == (cmsTagSignature
) 0) continue;
1316 // Linked tags are not written
1317 if (Icc
->TagLinked
[i
] != (cmsTagSignature
) 0) continue;
1319 Icc
-> TagOffsets
[i
] = Begin
= io
->UsedSpace
;
1321 Data
= (cmsUInt8Number
*) Icc
-> TagPtrs
[i
];
1325 // Reach here if we are copying a tag from a disk-based ICC profile which has not been modified by user.
1326 // In this case a blind copy of the block data is performed
1327 if (FileOrig
!= NULL
&& Icc
-> TagOffsets
[i
]) {
1329 if (FileOrig
->IOhandler
!= NULL
)
1331 cmsUInt32Number TagSize
= FileOrig
->TagSizes
[i
];
1332 cmsUInt32Number TagOffset
= FileOrig
->TagOffsets
[i
];
1335 if (!FileOrig
->IOhandler
->Seek(FileOrig
->IOhandler
, TagOffset
)) return FALSE
;
1337 Mem
= _cmsMalloc(Icc
->ContextID
, TagSize
);
1338 if (Mem
== NULL
) return FALSE
;
1340 if (FileOrig
->IOhandler
->Read(FileOrig
->IOhandler
, Mem
, TagSize
, 1) != 1) return FALSE
;
1341 if (!io
->Write(io
, TagSize
, Mem
)) return FALSE
;
1342 _cmsFree(Icc
->ContextID
, Mem
);
1344 Icc
->TagSizes
[i
] = (io
->UsedSpace
- Begin
);
1347 // Align to 32 bit boundary.
1348 if (!_cmsWriteAlignment(io
))
1357 // Should this tag be saved as RAW? If so, tagsizes should be specified in advance (no further cooking is done)
1358 if (Icc
->TagSaveAsRaw
[i
]) {
1360 if (io
-> Write(io
, Icc
->TagSizes
[i
], Data
) != 1) return FALSE
;
1364 // Search for support on this tag
1365 TagDescriptor
= _cmsGetTagDescriptor(Icc
-> ContextID
, Icc
-> TagNames
[i
]);
1366 if (TagDescriptor
== NULL
) continue; // Unsupported, ignore it
1368 if (TagDescriptor
->DecideType
!= NULL
) {
1370 Type
= TagDescriptor
->DecideType(Version
, Data
);
1374 Type
= TagDescriptor
->SupportedTypes
[0];
1377 TypeHandler
= _cmsGetTagTypeHandler(Icc
->ContextID
, Type
);
1379 if (TypeHandler
== NULL
) {
1380 cmsSignalError(Icc
->ContextID
, cmsERROR_INTERNAL
, "(Internal) no handler for tag %x", Icc
-> TagNames
[i
]);
1384 TypeBase
= TypeHandler
->Signature
;
1385 if (!_cmsWriteTypeBase(io
, TypeBase
))
1388 LocalTypeHandler
= *TypeHandler
;
1389 LocalTypeHandler
.ContextID
= Icc
->ContextID
;
1390 LocalTypeHandler
.ICCVersion
= Icc
->Version
;
1391 if (!LocalTypeHandler
.WritePtr(&LocalTypeHandler
, io
, Data
, TagDescriptor
->ElemCount
)) {
1395 _cmsTagSignature2String(String
, (cmsTagSignature
) TypeBase
);
1396 cmsSignalError(Icc
->ContextID
, cmsERROR_WRITE
, "Couldn't write type '%s'", String
);
1402 Icc
-> TagSizes
[i
] = (io
->UsedSpace
- Begin
);
1404 // Align to 32 bit boundary.
1405 if (! _cmsWriteAlignment(io
))
1414 // Fill the offset and size fields for all linked tags
1416 cmsBool
SetLinks( _cmsICCPROFILE
* Icc
)
1420 for (i
=0; i
< Icc
-> TagCount
; i
++) {
1422 cmsTagSignature lnk
= Icc
->TagLinked
[i
];
1423 if (lnk
!= (cmsTagSignature
) 0) {
1425 int j
= _cmsSearchTag(Icc
, lnk
, FALSE
);
1428 Icc
->TagOffsets
[i
] = Icc
->TagOffsets
[j
];
1429 Icc
->TagSizes
[i
] = Icc
->TagSizes
[j
];
1438 // Low-level save to IOHANDLER. It returns the number of bytes used to
1439 // store the profile, or zero on error. io may be NULL and in this case
1440 // no data is written--only sizes are calculated
1441 cmsUInt32Number CMSEXPORT
cmsSaveProfileToIOhandler(cmsHPROFILE hProfile
, cmsIOHANDLER
* io
)
1443 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1444 _cmsICCPROFILE Keep
;
1445 cmsIOHANDLER
* PrevIO
= NULL
;
1446 cmsUInt32Number UsedSpace
;
1447 cmsContext ContextID
;
1449 _cmsAssert(hProfile
!= NULL
);
1451 if (!_cmsLockMutex(Icc
->ContextID
, Icc
->UsrMutex
)) return 0;
1452 memmove(&Keep
, Icc
, sizeof(_cmsICCPROFILE
));
1454 ContextID
= cmsGetProfileContextID(hProfile
);
1455 PrevIO
= Icc
->IOhandler
= cmsOpenIOhandlerFromNULL(ContextID
);
1456 if (PrevIO
== NULL
) {
1457 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
1461 // Pass #1 does compute offsets
1463 if (!_cmsWriteHeader(Icc
, 0)) goto Error
;
1464 if (!SaveTags(Icc
, &Keep
)) goto Error
;
1466 UsedSpace
= PrevIO
->UsedSpace
;
1468 // Pass #2 does save to iohandler
1472 Icc
->IOhandler
= io
;
1473 if (!SetLinks(Icc
)) goto Error
;
1474 if (!_cmsWriteHeader(Icc
, UsedSpace
)) goto Error
;
1475 if (!SaveTags(Icc
, &Keep
)) goto Error
;
1478 memmove(Icc
, &Keep
, sizeof(_cmsICCPROFILE
));
1479 if (!cmsCloseIOhandler(PrevIO
))
1480 UsedSpace
= 0; // As a error marker
1482 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
1488 cmsCloseIOhandler(PrevIO
);
1489 memmove(Icc
, &Keep
, sizeof(_cmsICCPROFILE
));
1490 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
1496 // Low-level save to disk.
1497 cmsBool CMSEXPORT
cmsSaveProfileToFile(cmsHPROFILE hProfile
, const char* FileName
)
1499 cmsContext ContextID
= cmsGetProfileContextID(hProfile
);
1500 cmsIOHANDLER
* io
= cmsOpenIOhandlerFromFile(ContextID
, FileName
, "w");
1503 if (io
== NULL
) return FALSE
;
1505 rc
= (cmsSaveProfileToIOhandler(hProfile
, io
) != 0);
1506 rc
&= cmsCloseIOhandler(io
);
1508 if (rc
== FALSE
) { // remove() is C99 per 7.19.4.1
1509 remove(FileName
); // We have to IGNORE return value in this case
1514 // Same as anterior, but for streams
1515 cmsBool CMSEXPORT
cmsSaveProfileToStream(cmsHPROFILE hProfile
, FILE* Stream
)
1518 cmsContext ContextID
= cmsGetProfileContextID(hProfile
);
1519 cmsIOHANDLER
* io
= cmsOpenIOhandlerFromStream(ContextID
, Stream
);
1521 if (io
== NULL
) return FALSE
;
1523 rc
= (cmsSaveProfileToIOhandler(hProfile
, io
) != 0);
1524 rc
&= cmsCloseIOhandler(io
);
1530 // Same as anterior, but for memory blocks. In this case, a NULL as MemPtr means calculate needed space only
1531 cmsBool CMSEXPORT
cmsSaveProfileToMem(cmsHPROFILE hProfile
, void *MemPtr
, cmsUInt32Number
* BytesNeeded
)
1535 cmsContext ContextID
= cmsGetProfileContextID(hProfile
);
1537 _cmsAssert(BytesNeeded
!= NULL
);
1539 // Should we just calculate the needed space?
1540 if (MemPtr
== NULL
) {
1542 *BytesNeeded
= cmsSaveProfileToIOhandler(hProfile
, NULL
);
1543 return (*BytesNeeded
== 0) ? FALSE
: TRUE
;
1546 // That is a real write operation
1547 io
= cmsOpenIOhandlerFromMem(ContextID
, MemPtr
, *BytesNeeded
, "w");
1548 if (io
== NULL
) return FALSE
;
1550 rc
= (cmsSaveProfileToIOhandler(hProfile
, io
) != 0);
1551 rc
&= cmsCloseIOhandler(io
);
1556 // Free one tag contents
1558 void freeOneTag(_cmsICCPROFILE
* Icc
, cmsUInt32Number i
)
1560 if (Icc
->TagPtrs
[i
]) {
1562 cmsTagTypeHandler
* TypeHandler
= Icc
->TagTypeHandlers
[i
];
1564 if (TypeHandler
!= NULL
) {
1565 cmsTagTypeHandler LocalTypeHandler
= *TypeHandler
;
1567 LocalTypeHandler
.ContextID
= Icc
->ContextID
;
1568 LocalTypeHandler
.ICCVersion
= Icc
->Version
;
1569 LocalTypeHandler
.FreePtr(&LocalTypeHandler
, Icc
->TagPtrs
[i
]);
1572 _cmsFree(Icc
->ContextID
, Icc
->TagPtrs
[i
]);
1576 // Closes a profile freeing any involved resources
1577 cmsBool CMSEXPORT
cmsCloseProfile(cmsHPROFILE hProfile
)
1579 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1583 if (!Icc
) return FALSE
;
1585 // Was open in write mode?
1586 if (Icc
->IsWrite
) {
1588 Icc
->IsWrite
= FALSE
; // Assure no further writing
1589 rc
&= cmsSaveProfileToFile(hProfile
, Icc
->IOhandler
->PhysicalFile
);
1592 for (i
=0; i
< Icc
-> TagCount
; i
++) {
1597 if (Icc
->IOhandler
!= NULL
) {
1598 rc
&= cmsCloseIOhandler(Icc
->IOhandler
);
1601 _cmsDestroyMutex(Icc
->ContextID
, Icc
->UsrMutex
);
1603 _cmsFree(Icc
->ContextID
, Icc
); // Free placeholder memory
1609 // -------------------------------------------------------------------------------------------------------------------
1612 // Returns TRUE if a given tag is supported by a plug-in
1614 cmsBool
IsTypeSupported(cmsTagDescriptor
* TagDescriptor
, cmsTagTypeSignature Type
)
1616 cmsUInt32Number i
, nMaxTypes
;
1618 nMaxTypes
= TagDescriptor
->nSupportedTypes
;
1619 if (nMaxTypes
>= MAX_TYPES_IN_LCMS_PLUGIN
)
1620 nMaxTypes
= MAX_TYPES_IN_LCMS_PLUGIN
;
1622 for (i
=0; i
< nMaxTypes
; i
++) {
1623 if (Type
== TagDescriptor
->SupportedTypes
[i
]) return TRUE
;
1630 // That's the main read function
1631 void* CMSEXPORT
cmsReadTag(cmsHPROFILE hProfile
, cmsTagSignature sig
)
1633 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1635 cmsTagTypeHandler
* TypeHandler
;
1636 cmsTagTypeHandler LocalTypeHandler
;
1637 cmsTagDescriptor
* TagDescriptor
;
1638 cmsTagTypeSignature BaseType
;
1639 cmsUInt32Number Offset
, TagSize
;
1640 cmsUInt32Number ElemCount
;
1643 if (!_cmsLockMutex(Icc
->ContextID
, Icc
->UsrMutex
)) return NULL
;
1645 n
= _cmsSearchTag(Icc
, sig
, TRUE
);
1648 // Not found, return NULL
1649 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
1653 // If the element is already in memory, return the pointer
1654 if (Icc
-> TagPtrs
[n
]) {
1656 if (Icc
->TagTypeHandlers
[n
] == NULL
) goto Error
;
1659 BaseType
= Icc
->TagTypeHandlers
[n
]->Signature
;
1660 if (BaseType
== 0) goto Error
;
1662 TagDescriptor
= _cmsGetTagDescriptor(Icc
->ContextID
, sig
);
1663 if (TagDescriptor
== NULL
) goto Error
;
1665 if (!IsTypeSupported(TagDescriptor
, BaseType
)) goto Error
;
1667 if (Icc
->TagSaveAsRaw
[n
]) goto Error
; // We don't support read raw tags as cooked
1669 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
1670 return Icc
-> TagPtrs
[n
];
1673 // We need to read it. Get the offset and size to the file
1674 Offset
= Icc
-> TagOffsets
[n
];
1675 TagSize
= Icc
-> TagSizes
[n
];
1677 if (TagSize
< 8) goto Error
;
1679 io
= Icc
->IOhandler
;
1681 if (io
== NULL
) { // This is a built-in profile that has been manipulated, abort early
1683 cmsSignalError(Icc
->ContextID
, cmsERROR_CORRUPTION_DETECTED
, "Corrupted built-in profile.");
1687 // Seek to its location
1688 if (!io
-> Seek(io
, Offset
))
1691 // Search for support on this tag
1692 TagDescriptor
= _cmsGetTagDescriptor(Icc
-> ContextID
, sig
);
1693 if (TagDescriptor
== NULL
) {
1697 _cmsTagSignature2String(String
, sig
);
1699 // An unknown element was found.
1700 cmsSignalError(Icc
->ContextID
, cmsERROR_UNKNOWN_EXTENSION
, "Unknown tag type '%s' found.", String
);
1701 goto Error
; // Unsupported.
1704 // if supported, get type and check if in list
1705 BaseType
= _cmsReadTypeBase(io
);
1706 if (BaseType
== 0) goto Error
;
1708 if (!IsTypeSupported(TagDescriptor
, BaseType
)) goto Error
;
1710 TagSize
-= 8; // Already read by the type base logic
1713 TypeHandler
= _cmsGetTagTypeHandler(Icc
->ContextID
, BaseType
);
1714 if (TypeHandler
== NULL
) goto Error
;
1715 LocalTypeHandler
= *TypeHandler
;
1719 Icc
-> TagTypeHandlers
[n
] = TypeHandler
;
1721 LocalTypeHandler
.ContextID
= Icc
->ContextID
;
1722 LocalTypeHandler
.ICCVersion
= Icc
->Version
;
1723 Icc
-> TagPtrs
[n
] = LocalTypeHandler
.ReadPtr(&LocalTypeHandler
, io
, &ElemCount
, TagSize
);
1725 // The tag type is supported, but something wrong happened and we cannot read the tag.
1726 // let know the user about this (although it is just a warning)
1727 if (Icc
-> TagPtrs
[n
] == NULL
) {
1731 _cmsTagSignature2String(String
, sig
);
1732 cmsSignalError(Icc
->ContextID
, cmsERROR_CORRUPTION_DETECTED
, "Corrupted tag '%s'", String
);
1736 // This is a weird error that may be a symptom of something more serious, the number of
1737 // stored item is actually less than the number of required elements.
1738 if (ElemCount
< TagDescriptor
->ElemCount
) {
1742 _cmsTagSignature2String(String
, sig
);
1743 cmsSignalError(Icc
->ContextID
, cmsERROR_CORRUPTION_DETECTED
, "'%s' Inconsistent number of items: expected %d, got %d",
1744 String
, TagDescriptor
->ElemCount
, ElemCount
);
1750 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
1751 return Icc
-> TagPtrs
[n
];
1754 // Return error and unlock the data
1758 Icc
->TagPtrs
[n
] = NULL
;
1760 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
1765 // Get true type of data
1766 cmsTagTypeSignature
_cmsGetTagTrueType(cmsHPROFILE hProfile
, cmsTagSignature sig
)
1768 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1769 cmsTagTypeHandler
* TypeHandler
;
1772 // Search for given tag in ICC profile directory
1773 n
= _cmsSearchTag(Icc
, sig
, TRUE
);
1774 if (n
< 0) return (cmsTagTypeSignature
) 0; // Not found, return NULL
1776 // Get the handler. The true type is there
1777 TypeHandler
= Icc
-> TagTypeHandlers
[n
];
1778 return TypeHandler
->Signature
;
1782 // Write a single tag. This just keeps track of the tak into a list of "to be written". If the tag is already
1783 // in that list, the previous version is deleted.
1784 cmsBool CMSEXPORT
cmsWriteTag(cmsHPROFILE hProfile
, cmsTagSignature sig
, const void* data
)
1786 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1787 cmsTagTypeHandler
* TypeHandler
= NULL
;
1788 cmsTagTypeHandler LocalTypeHandler
;
1789 cmsTagDescriptor
* TagDescriptor
= NULL
;
1790 cmsTagTypeSignature Type
;
1792 cmsFloat64Number Version
;
1793 char TypeString
[5], SigString
[5];
1795 if (!_cmsLockMutex(Icc
->ContextID
, Icc
->UsrMutex
)) return FALSE
;
1801 i
= _cmsSearchTag(Icc
, sig
, FALSE
);
1804 // Use zero as a mark of deleted
1805 _cmsDeleteTagByPos(Icc
, i
);
1806 Icc
->TagNames
[i
] = (cmsTagSignature
) 0;
1807 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
1810 // Didn't find the tag
1814 if (!_cmsNewTag(Icc
, sig
, &i
)) goto Error
;
1817 Icc
->TagSaveAsRaw
[i
] = FALSE
;
1819 // This is not a link
1820 Icc
->TagLinked
[i
] = (cmsTagSignature
) 0;
1822 // Get information about the TAG.
1823 TagDescriptor
= _cmsGetTagDescriptor(Icc
-> ContextID
, sig
);
1824 if (TagDescriptor
== NULL
){
1825 cmsSignalError(Icc
->ContextID
, cmsERROR_UNKNOWN_EXTENSION
, "Unsupported tag '%x'", sig
);
1830 // Now we need to know which type to use. It depends on the version.
1831 Version
= cmsGetProfileVersion(hProfile
);
1833 if (TagDescriptor
->DecideType
!= NULL
) {
1835 // Let the tag descriptor to decide the type base on depending on
1836 // the data. This is useful for example on parametric curves, where
1837 // curves specified by a table cannot be saved as parametric and needs
1838 // to be casted to single v2-curves, even on v4 profiles.
1840 Type
= TagDescriptor
->DecideType(Version
, data
);
1844 Type
= TagDescriptor
->SupportedTypes
[0];
1847 // Does the tag support this type?
1848 if (!IsTypeSupported(TagDescriptor
, Type
)) {
1850 _cmsTagSignature2String(TypeString
, (cmsTagSignature
) Type
);
1851 _cmsTagSignature2String(SigString
, sig
);
1853 cmsSignalError(Icc
->ContextID
, cmsERROR_UNKNOWN_EXTENSION
, "Unsupported type '%s' for tag '%s'", TypeString
, SigString
);
1857 // Does we have a handler for this type?
1858 TypeHandler
= _cmsGetTagTypeHandler(Icc
->ContextID
, Type
);
1859 if (TypeHandler
== NULL
) {
1861 _cmsTagSignature2String(TypeString
, (cmsTagSignature
) Type
);
1862 _cmsTagSignature2String(SigString
, sig
);
1864 cmsSignalError(Icc
->ContextID
, cmsERROR_UNKNOWN_EXTENSION
, "Unsupported type '%s' for tag '%s'", TypeString
, SigString
);
1865 goto Error
; // Should never happen
1869 // Fill fields on icc structure
1870 Icc
->TagTypeHandlers
[i
] = TypeHandler
;
1871 Icc
->TagNames
[i
] = sig
;
1872 Icc
->TagSizes
[i
] = 0;
1873 Icc
->TagOffsets
[i
] = 0;
1875 LocalTypeHandler
= *TypeHandler
;
1876 LocalTypeHandler
.ContextID
= Icc
->ContextID
;
1877 LocalTypeHandler
.ICCVersion
= Icc
->Version
;
1878 Icc
->TagPtrs
[i
] = LocalTypeHandler
.DupPtr(&LocalTypeHandler
, data
, TagDescriptor
->ElemCount
);
1880 if (Icc
->TagPtrs
[i
] == NULL
) {
1882 _cmsTagSignature2String(TypeString
, (cmsTagSignature
) Type
);
1883 _cmsTagSignature2String(SigString
, sig
);
1884 cmsSignalError(Icc
->ContextID
, cmsERROR_CORRUPTION_DETECTED
, "Malformed struct in type '%s' for tag '%s'", TypeString
, SigString
);
1889 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
1893 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
1898 // Read and write raw data. Read/Write Raw/cooked pairs try to maintain consistency within the pair. Some sequences
1899 // raw/cooked would work, but at a cost. Data "cooked" may be converted to "raw" by using the "write" serialization logic.
1900 // In general it is better to avoid mixing pairs.
1902 cmsUInt32Number CMSEXPORT
cmsReadRawTag(cmsHPROFILE hProfile
, cmsTagSignature sig
, void* data
, cmsUInt32Number BufferSize
)
1904 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1907 cmsIOHANDLER
* MemIO
;
1908 cmsTagTypeHandler
* TypeHandler
= NULL
;
1909 cmsTagTypeHandler LocalTypeHandler
;
1910 cmsTagDescriptor
* TagDescriptor
= NULL
;
1912 cmsUInt32Number Offset
, TagSize
;
1915 if (data
!= NULL
&& BufferSize
== 0) return 0;
1917 if (!_cmsLockMutex(Icc
->ContextID
, Icc
->UsrMutex
)) return 0;
1919 // Search for given tag in ICC profile directory
1921 i
= _cmsSearchTag(Icc
, sig
, TRUE
);
1922 if (i
< 0) goto Error
; // Not found,
1924 // It is already read?
1925 if (Icc
-> TagPtrs
[i
] == NULL
) {
1927 // Not yet, get original position
1928 Offset
= Icc
->TagOffsets
[i
];
1929 TagSize
= Icc
->TagSizes
[i
];
1931 // read the data directly, don't keep copy
1935 if (BufferSize
< TagSize
)
1936 TagSize
= BufferSize
;
1938 if (!Icc
->IOhandler
->Seek(Icc
->IOhandler
, Offset
)) goto Error
;
1939 if (!Icc
->IOhandler
->Read(Icc
->IOhandler
, data
, 1, TagSize
)) goto Error
;
1941 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
1945 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
1946 return Icc
->TagSizes
[i
];
1949 // The data has been already read, or written. But wait!, maybe the user choose to save as
1950 // raw data. In this case, return the raw data directly
1952 if (Icc
->TagSaveAsRaw
[i
]) {
1956 TagSize
= Icc
->TagSizes
[i
];
1957 if (BufferSize
< TagSize
)
1958 TagSize
= BufferSize
;
1960 memmove(data
, Icc
->TagPtrs
[i
], TagSize
);
1962 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
1966 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
1967 return Icc
->TagSizes
[i
];
1970 // Already read, or previously set by cmsWriteTag(). We need to serialize that
1971 // data to raw to get something that makes sense
1973 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
1974 Object
= cmsReadTag(hProfile
, sig
);
1975 if (!_cmsLockMutex(Icc
->ContextID
, Icc
->UsrMutex
)) return 0;
1977 if (Object
== NULL
) goto Error
;
1979 // Now we need to serialize to a memory block: just use a memory iohandler
1982 MemIO
= cmsOpenIOhandlerFromNULL(cmsGetProfileContextID(hProfile
));
1984 MemIO
= cmsOpenIOhandlerFromMem(cmsGetProfileContextID(hProfile
), data
, BufferSize
, "w");
1986 if (MemIO
== NULL
) goto Error
;
1988 // Obtain type handling for the tag
1989 TypeHandler
= Icc
->TagTypeHandlers
[i
];
1990 TagDescriptor
= _cmsGetTagDescriptor(Icc
-> ContextID
, sig
);
1991 if (TagDescriptor
== NULL
) {
1992 cmsCloseIOhandler(MemIO
);
1996 if (TypeHandler
== NULL
) goto Error
;
1999 LocalTypeHandler
= *TypeHandler
;
2000 LocalTypeHandler
.ContextID
= Icc
->ContextID
;
2001 LocalTypeHandler
.ICCVersion
= Icc
->Version
;
2003 if (!_cmsWriteTypeBase(MemIO
, TypeHandler
->Signature
)) {
2004 cmsCloseIOhandler(MemIO
);
2008 if (!LocalTypeHandler
.WritePtr(&LocalTypeHandler
, MemIO
, Object
, TagDescriptor
->ElemCount
)) {
2009 cmsCloseIOhandler(MemIO
);
2013 // Get Size and close
2014 rc
= MemIO
->Tell(MemIO
);
2015 cmsCloseIOhandler(MemIO
); // Ignore return code this time
2017 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
2021 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
2025 // Similar to the anterior. This function allows to write directly to the ICC profile any data, without
2026 // checking anything. As a rule, mixing Raw with cooked doesn't work, so writing a tag as raw and then reading
2027 // it as cooked without serializing does result into an error. If that is what you want, you will need to dump
2028 // the profile to memry or disk and then reopen it.
2029 cmsBool CMSEXPORT
cmsWriteRawTag(cmsHPROFILE hProfile
, cmsTagSignature sig
, const void* data
, cmsUInt32Number Size
)
2031 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
2034 if (!_cmsLockMutex(Icc
->ContextID
, Icc
->UsrMutex
)) return 0;
2036 if (!_cmsNewTag(Icc
, sig
, &i
)) {
2037 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
2041 // Mark the tag as being written as RAW
2042 Icc
->TagSaveAsRaw
[i
] = TRUE
;
2043 Icc
->TagNames
[i
] = sig
;
2044 Icc
->TagLinked
[i
] = (cmsTagSignature
) 0;
2046 // Keep a copy of the block
2047 Icc
->TagPtrs
[i
] = _cmsDupMem(Icc
->ContextID
, data
, Size
);
2048 Icc
->TagSizes
[i
] = Size
;
2050 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
2052 if (Icc
->TagPtrs
[i
] == NULL
) {
2053 Icc
->TagNames
[i
] = (cmsTagSignature
) 0;
2059 // Using this function you can collapse several tag entries to the same block in the profile
2060 cmsBool CMSEXPORT
cmsLinkTag(cmsHPROFILE hProfile
, cmsTagSignature sig
, cmsTagSignature dest
)
2062 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
2065 if (!_cmsLockMutex(Icc
->ContextID
, Icc
->UsrMutex
)) return FALSE
;
2067 if (!_cmsNewTag(Icc
, sig
, &i
)) {
2068 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
2072 // Keep necessary information
2073 Icc
->TagSaveAsRaw
[i
] = FALSE
;
2074 Icc
->TagNames
[i
] = sig
;
2075 Icc
->TagLinked
[i
] = dest
;
2077 Icc
->TagPtrs
[i
] = NULL
;
2078 Icc
->TagSizes
[i
] = 0;
2079 Icc
->TagOffsets
[i
] = 0;
2081 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
2086 // Returns the tag linked to sig, in the case two tags are sharing same resource
2087 cmsTagSignature CMSEXPORT
cmsTagLinkedTo(cmsHPROFILE hProfile
, cmsTagSignature sig
)
2089 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
2092 // Search for given tag in ICC profile directory
2093 i
= _cmsSearchTag(Icc
, sig
, FALSE
);
2094 if (i
< 0) return (cmsTagSignature
) 0; // Not found, return 0
2096 return Icc
-> TagLinked
[i
];