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 device class
535 Icc
->DeviceClass
= cmsSigDisplayClass
;
537 // Set creation date/time
538 if (!_cmsGetTime(&Icc
->Created
))
541 // Create a mutex if the user provided proper plugin. NULL otherwise
542 Icc
->UsrMutex
= _cmsCreateMutex(ContextID
);
545 return (cmsHPROFILE
) Icc
;
548 _cmsFree(ContextID
, Icc
);
552 cmsContext CMSEXPORT
cmsGetProfileContextID(cmsHPROFILE hProfile
)
554 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
556 if (Icc
== NULL
) return NULL
;
557 return Icc
-> ContextID
;
561 // Return the number of tags
562 cmsInt32Number CMSEXPORT
cmsGetTagCount(cmsHPROFILE hProfile
)
564 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
565 if (Icc
== NULL
) return -1;
567 return (cmsInt32Number
) Icc
->TagCount
;
570 // Return the tag signature of a given tag number
571 cmsTagSignature CMSEXPORT
cmsGetTagSignature(cmsHPROFILE hProfile
, cmsUInt32Number n
)
573 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
575 if (n
> Icc
->TagCount
) return (cmsTagSignature
) 0; // Mark as not available
576 if (n
>= MAX_TABLE_TAG
) return (cmsTagSignature
) 0; // As double check
578 return Icc
->TagNames
[n
];
583 int SearchOneTag(_cmsICCPROFILE
* Profile
, cmsTagSignature sig
)
587 for (i
=0; i
< (int) Profile
-> TagCount
; i
++) {
589 if (sig
== Profile
-> TagNames
[i
])
596 // Search for a specific tag in tag dictionary. Returns position or -1 if tag not found.
597 // If followlinks is turned on, then the position of the linked tag is returned
598 int _cmsSearchTag(_cmsICCPROFILE
* Icc
, cmsTagSignature sig
, cmsBool lFollowLinks
)
601 cmsTagSignature LinkedSig
;
605 // Search for given tag in ICC profile directory
606 n
= SearchOneTag(Icc
, sig
);
608 return -1; // Not found
611 return n
; // Found, don't follow links
613 // Is this a linked tag?
614 LinkedSig
= Icc
->TagLinked
[n
];
617 if (LinkedSig
!= (cmsTagSignature
) 0) {
621 } while (LinkedSig
!= (cmsTagSignature
) 0);
626 // Deletes a tag entry
629 void _cmsDeleteTagByPos(_cmsICCPROFILE
* Icc
, int i
)
631 _cmsAssert(Icc
!= NULL
);
635 if (Icc
-> TagPtrs
[i
] != NULL
) {
637 // Free previous version
638 if (Icc
->TagSaveAsRaw
[i
]) {
639 _cmsFree(Icc
->ContextID
, Icc
->TagPtrs
[i
]);
642 cmsTagTypeHandler
* TypeHandler
= Icc
->TagTypeHandlers
[i
];
644 if (TypeHandler
!= NULL
) {
646 cmsTagTypeHandler LocalTypeHandler
= *TypeHandler
;
647 LocalTypeHandler
.ContextID
= Icc
->ContextID
; // As an additional parameter
648 LocalTypeHandler
.ICCVersion
= Icc
->Version
;
649 LocalTypeHandler
.FreePtr(&LocalTypeHandler
, Icc
-> TagPtrs
[i
]);
650 Icc
->TagPtrs
[i
] = NULL
;
658 // Creates a new tag entry
660 cmsBool
_cmsNewTag(_cmsICCPROFILE
* Icc
, cmsTagSignature sig
, int* NewPos
)
664 // Search for the tag
665 i
= _cmsSearchTag(Icc
, sig
, FALSE
);
668 // Already exists? delete it
669 _cmsDeleteTagByPos(Icc
, i
);
674 // No, make a new one
675 if (Icc
-> TagCount
>= MAX_TABLE_TAG
) {
676 cmsSignalError(Icc
->ContextID
, cmsERROR_RANGE
, "Too many tags (%d)", MAX_TABLE_TAG
);
680 *NewPos
= (int) Icc
->TagCount
;
689 cmsBool CMSEXPORT
cmsIsTag(cmsHPROFILE hProfile
, cmsTagSignature sig
)
691 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) (void*) hProfile
;
692 return _cmsSearchTag(Icc
, sig
, FALSE
) >= 0;
697 // Checks for link compatibility
699 cmsBool
CompatibleTypes(const cmsTagDescriptor
* desc1
, const cmsTagDescriptor
* desc2
)
703 if (desc1
== NULL
|| desc2
== NULL
) return FALSE
;
705 if (desc1
->nSupportedTypes
!= desc2
->nSupportedTypes
) return FALSE
;
706 if (desc1
->ElemCount
!= desc2
->ElemCount
) return FALSE
;
708 for (i
= 0; i
< desc1
->nSupportedTypes
; i
++)
710 if (desc1
->SupportedTypes
[i
] != desc2
->SupportedTypes
[i
]) return FALSE
;
716 // Enforces that the profile version is per. spec.
717 // Operates on the big endian bytes from the profile.
718 // Called before converting to platform endianness.
719 // Byte 0 is BCD major version, so max 9.
720 // Byte 1 is 2 BCD digits, one per nibble.
721 // Reserved bytes 2 & 3 must be 0.
723 cmsUInt32Number
_validatedVersion(cmsUInt32Number DWord
)
725 cmsUInt8Number
* pByte
= (cmsUInt8Number
*) &DWord
;
726 cmsUInt8Number temp1
;
727 cmsUInt8Number temp2
;
729 if (*pByte
> 0x09) *pByte
= (cmsUInt8Number
) 0x09;
730 temp1
= (cmsUInt8Number
) (*(pByte
+1) & 0xf0);
731 temp2
= (cmsUInt8Number
) (*(pByte
+1) & 0x0f);
732 if (temp1
> 0x90U
) temp1
= 0x90U
;
733 if (temp2
> 0x09U
) temp2
= 0x09U
;
734 *(pByte
+1) = (cmsUInt8Number
)(temp1
| temp2
);
735 *(pByte
+2) = (cmsUInt8Number
)0;
736 *(pByte
+3) = (cmsUInt8Number
)0;
741 // Check device class
743 cmsBool
validDeviceClass(cmsProfileClassSignature cl
)
745 if ((int)cl
== 0) return TRUE
; // We allow zero because older lcms versions defaulted to that.
749 case cmsSigInputClass
:
750 case cmsSigDisplayClass
:
751 case cmsSigOutputClass
:
752 case cmsSigLinkClass
:
753 case cmsSigAbstractClass
:
754 case cmsSigColorSpaceClass
:
755 case cmsSigNamedColorClass
:
764 // Read profile header and validate it
765 cmsBool
_cmsReadHeader(_cmsICCPROFILE
* Icc
)
769 cmsUInt32Number i
, j
;
770 cmsUInt32Number HeaderSize
;
771 cmsIOHANDLER
* io
= Icc
->IOhandler
;
772 cmsUInt32Number TagCount
;
776 if (io
-> Read(io
, &Header
, sizeof(cmsICCHeader
), 1) != 1) {
780 // Validate file as an ICC profile
781 if (_cmsAdjustEndianess32(Header
.magic
) != cmsMagicNumber
) {
782 cmsSignalError(Icc
->ContextID
, cmsERROR_BAD_SIGNATURE
, "not an ICC profile, invalid signature");
786 // Adjust endianness of the used parameters
787 Icc
-> DeviceClass
= (cmsProfileClassSignature
) _cmsAdjustEndianess32(Header
.deviceClass
);
788 Icc
-> ColorSpace
= (cmsColorSpaceSignature
) _cmsAdjustEndianess32(Header
.colorSpace
);
789 Icc
-> PCS
= (cmsColorSpaceSignature
) _cmsAdjustEndianess32(Header
.pcs
);
791 Icc
-> RenderingIntent
= _cmsAdjustEndianess32(Header
.renderingIntent
);
792 Icc
-> flags
= _cmsAdjustEndianess32(Header
.flags
);
793 Icc
-> manufacturer
= _cmsAdjustEndianess32(Header
.manufacturer
);
794 Icc
-> model
= _cmsAdjustEndianess32(Header
.model
);
795 Icc
-> creator
= _cmsAdjustEndianess32(Header
.creator
);
797 _cmsAdjustEndianess64(&Icc
-> attributes
, &Header
.attributes
);
798 Icc
-> Version
= _cmsAdjustEndianess32(_validatedVersion(Header
.version
));
800 if (Icc
->Version
> 0x5000000) {
801 cmsSignalError(Icc
->ContextID
, cmsERROR_UNKNOWN_EXTENSION
, "Unsupported profile version '0x%x'", Icc
->Version
);
805 if (!validDeviceClass(Icc
->DeviceClass
)) {
806 cmsSignalError(Icc
->ContextID
, cmsERROR_UNKNOWN_EXTENSION
, "Unsupported device class '0x%x'", Icc
->DeviceClass
);
810 // Get size as reported in header
811 HeaderSize
= _cmsAdjustEndianess32(Header
.size
);
813 // Make sure HeaderSize is lower than profile size
814 if (HeaderSize
>= Icc
->IOhandler
->ReportedSize
)
815 HeaderSize
= Icc
->IOhandler
->ReportedSize
;
818 // Get creation date/time
819 _cmsDecodeDateTimeNumber(&Header
.date
, &Icc
->Created
);
821 // The profile ID are 32 raw bytes
822 memmove(Icc
->ProfileID
.ID32
, Header
.profileID
.ID32
, 16);
825 // Read tag directory
826 if (!_cmsReadUInt32Number(io
, &TagCount
)) return FALSE
;
827 if (TagCount
> MAX_TABLE_TAG
) {
829 cmsSignalError(Icc
->ContextID
, cmsERROR_RANGE
, "Too many tags (%d)", TagCount
);
834 // Read tag directory
836 for (i
=0; i
< TagCount
; i
++) {
838 if (!_cmsReadUInt32Number(io
, (cmsUInt32Number
*) &Tag
.sig
)) return FALSE
;
839 if (!_cmsReadUInt32Number(io
, &Tag
.offset
)) return FALSE
;
840 if (!_cmsReadUInt32Number(io
, &Tag
.size
)) return FALSE
;
842 // Perform some sanity check. Offset + size should fall inside file.
843 if (Tag
.size
== 0 || Tag
.offset
== 0) continue;
844 if (Tag
.offset
+ Tag
.size
> HeaderSize
||
845 Tag
.offset
+ Tag
.size
< Tag
.offset
)
848 Icc
-> TagNames
[Icc
->TagCount
] = Tag
.sig
;
849 Icc
-> TagOffsets
[Icc
->TagCount
] = Tag
.offset
;
850 Icc
-> TagSizes
[Icc
->TagCount
] = Tag
.size
;
853 for (j
=0; j
< Icc
->TagCount
; j
++) {
855 if ((Icc
->TagOffsets
[j
] == Tag
.offset
) &&
856 (Icc
->TagSizes
[j
] == Tag
.size
)) {
859 if (CompatibleTypes(_cmsGetTagDescriptor(Icc
->ContextID
, Icc
->TagNames
[j
]),
860 _cmsGetTagDescriptor(Icc
->ContextID
, Tag
.sig
))) {
862 Icc
->TagLinked
[Icc
->TagCount
] = Icc
->TagNames
[j
];
872 for (i
= 0; i
< Icc
->TagCount
; i
++) {
873 for (j
= 0; j
< Icc
->TagCount
; j
++) {
875 // Tags cannot be duplicate
876 if ((i
!= j
) && (Icc
->TagNames
[i
] == Icc
->TagNames
[j
])) {
877 cmsSignalError(Icc
->ContextID
, cmsERROR_RANGE
, "Duplicate tag found");
887 // Saves profile header
888 cmsBool
_cmsWriteHeader(_cmsICCPROFILE
* Icc
, cmsUInt32Number UsedSpace
)
893 cmsUInt32Number Count
;
895 Header
.size
= _cmsAdjustEndianess32(UsedSpace
);
896 Header
.cmmId
= _cmsAdjustEndianess32(lcmsSignature
);
897 Header
.version
= _cmsAdjustEndianess32(Icc
->Version
);
899 Header
.deviceClass
= (cmsProfileClassSignature
) _cmsAdjustEndianess32(Icc
-> DeviceClass
);
900 Header
.colorSpace
= (cmsColorSpaceSignature
) _cmsAdjustEndianess32(Icc
-> ColorSpace
);
901 Header
.pcs
= (cmsColorSpaceSignature
) _cmsAdjustEndianess32(Icc
-> PCS
);
903 // NOTE: in v4 Timestamp must be in UTC rather than in local time
904 _cmsEncodeDateTimeNumber(&Header
.date
, &Icc
->Created
);
906 Header
.magic
= _cmsAdjustEndianess32(cmsMagicNumber
);
908 #ifdef CMS_IS_WINDOWS_
909 Header
.platform
= (cmsPlatformSignature
) _cmsAdjustEndianess32(cmsSigMicrosoft
);
911 Header
.platform
= (cmsPlatformSignature
) _cmsAdjustEndianess32(cmsSigMacintosh
);
914 Header
.flags
= _cmsAdjustEndianess32(Icc
-> flags
);
915 Header
.manufacturer
= _cmsAdjustEndianess32(Icc
-> manufacturer
);
916 Header
.model
= _cmsAdjustEndianess32(Icc
-> model
);
918 _cmsAdjustEndianess64(&Header
.attributes
, &Icc
-> attributes
);
920 // Rendering intent in the header (for embedded profiles)
921 Header
.renderingIntent
= _cmsAdjustEndianess32(Icc
-> RenderingIntent
);
923 // Illuminant is always D50
924 Header
.illuminant
.X
= (cmsS15Fixed16Number
) _cmsAdjustEndianess32((cmsUInt32Number
) _cmsDoubleTo15Fixed16(cmsD50_XYZ()->X
));
925 Header
.illuminant
.Y
= (cmsS15Fixed16Number
) _cmsAdjustEndianess32((cmsUInt32Number
) _cmsDoubleTo15Fixed16(cmsD50_XYZ()->Y
));
926 Header
.illuminant
.Z
= (cmsS15Fixed16Number
) _cmsAdjustEndianess32((cmsUInt32Number
) _cmsDoubleTo15Fixed16(cmsD50_XYZ()->Z
));
928 // Created by LittleCMS (that's me!)
929 Header
.creator
= _cmsAdjustEndianess32(lcmsSignature
);
931 memset(&Header
.reserved
, 0, sizeof(Header
.reserved
));
933 // Set profile ID. Endianness is always big endian
934 memmove(&Header
.profileID
, &Icc
->ProfileID
, 16);
937 if (!Icc
-> IOhandler
->Write(Icc
->IOhandler
, sizeof(cmsICCHeader
), &Header
)) return FALSE
;
939 // Saves Tag directory
943 for (i
=0; i
< Icc
-> TagCount
; i
++) {
944 if (Icc
->TagNames
[i
] != (cmsTagSignature
) 0)
948 // Store number of tags
949 if (!_cmsWriteUInt32Number(Icc
->IOhandler
, Count
)) return FALSE
;
951 for (i
=0; i
< Icc
-> TagCount
; i
++) {
953 if (Icc
->TagNames
[i
] == (cmsTagSignature
) 0) continue; // It is just a placeholder
955 Tag
.sig
= (cmsTagSignature
) _cmsAdjustEndianess32((cmsUInt32Number
) Icc
-> TagNames
[i
]);
956 Tag
.offset
= _cmsAdjustEndianess32((cmsUInt32Number
) Icc
-> TagOffsets
[i
]);
957 Tag
.size
= _cmsAdjustEndianess32((cmsUInt32Number
) Icc
-> TagSizes
[i
]);
959 if (!Icc
->IOhandler
-> Write(Icc
-> IOhandler
, sizeof(cmsTagEntry
), &Tag
)) return FALSE
;
965 // ----------------------------------------------------------------------- Set/Get several struct members
968 cmsUInt32Number CMSEXPORT
cmsGetHeaderRenderingIntent(cmsHPROFILE hProfile
)
970 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
971 return Icc
-> RenderingIntent
;
974 void CMSEXPORT
cmsSetHeaderRenderingIntent(cmsHPROFILE hProfile
, cmsUInt32Number RenderingIntent
)
976 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
977 Icc
-> RenderingIntent
= RenderingIntent
;
980 cmsUInt32Number CMSEXPORT
cmsGetHeaderFlags(cmsHPROFILE hProfile
)
982 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
983 return (cmsUInt32Number
) Icc
-> flags
;
986 void CMSEXPORT
cmsSetHeaderFlags(cmsHPROFILE hProfile
, cmsUInt32Number Flags
)
988 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
989 Icc
-> flags
= (cmsUInt32Number
) Flags
;
992 cmsUInt32Number CMSEXPORT
cmsGetHeaderManufacturer(cmsHPROFILE hProfile
)
994 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
995 return Icc
->manufacturer
;
998 void CMSEXPORT
cmsSetHeaderManufacturer(cmsHPROFILE hProfile
, cmsUInt32Number manufacturer
)
1000 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1001 Icc
-> manufacturer
= manufacturer
;
1004 cmsUInt32Number CMSEXPORT
cmsGetHeaderCreator(cmsHPROFILE hProfile
)
1006 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1007 return Icc
->creator
;
1010 cmsUInt32Number CMSEXPORT
cmsGetHeaderModel(cmsHPROFILE hProfile
)
1012 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1016 void CMSEXPORT
cmsSetHeaderModel(cmsHPROFILE hProfile
, cmsUInt32Number model
)
1018 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1019 Icc
-> model
= model
;
1022 void CMSEXPORT
cmsGetHeaderAttributes(cmsHPROFILE hProfile
, cmsUInt64Number
* Flags
)
1024 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1025 memmove(Flags
, &Icc
-> attributes
, sizeof(cmsUInt64Number
));
1028 void CMSEXPORT
cmsSetHeaderAttributes(cmsHPROFILE hProfile
, cmsUInt64Number Flags
)
1030 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1031 memmove(&Icc
-> attributes
, &Flags
, sizeof(cmsUInt64Number
));
1034 void CMSEXPORT
cmsGetHeaderProfileID(cmsHPROFILE hProfile
, cmsUInt8Number
* ProfileID
)
1036 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1037 memmove(ProfileID
, Icc
->ProfileID
.ID8
, 16);
1040 void CMSEXPORT
cmsSetHeaderProfileID(cmsHPROFILE hProfile
, cmsUInt8Number
* ProfileID
)
1042 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1043 memmove(&Icc
-> ProfileID
, ProfileID
, 16);
1046 cmsBool CMSEXPORT
cmsGetHeaderCreationDateTime(cmsHPROFILE hProfile
, struct tm
*Dest
)
1048 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1049 memmove(Dest
, &Icc
->Created
, sizeof(struct tm
));
1053 cmsColorSpaceSignature CMSEXPORT
cmsGetPCS(cmsHPROFILE hProfile
)
1055 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1059 void CMSEXPORT
cmsSetPCS(cmsHPROFILE hProfile
, cmsColorSpaceSignature pcs
)
1061 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1065 cmsColorSpaceSignature CMSEXPORT
cmsGetColorSpace(cmsHPROFILE hProfile
)
1067 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1068 return Icc
-> ColorSpace
;
1071 void CMSEXPORT
cmsSetColorSpace(cmsHPROFILE hProfile
, cmsColorSpaceSignature sig
)
1073 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1074 Icc
-> ColorSpace
= sig
;
1077 cmsProfileClassSignature CMSEXPORT
cmsGetDeviceClass(cmsHPROFILE hProfile
)
1079 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1080 return Icc
-> DeviceClass
;
1083 void CMSEXPORT
cmsSetDeviceClass(cmsHPROFILE hProfile
, cmsProfileClassSignature sig
)
1085 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1086 Icc
-> DeviceClass
= sig
;
1089 cmsUInt32Number CMSEXPORT
cmsGetEncodedICCversion(cmsHPROFILE hProfile
)
1091 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1092 return Icc
-> Version
;
1095 void CMSEXPORT
cmsSetEncodedICCversion(cmsHPROFILE hProfile
, cmsUInt32Number Version
)
1097 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1098 Icc
-> Version
= Version
;
1101 // Get an hexadecimal number with same digits as v
1103 cmsUInt32Number
BaseToBase(cmsUInt32Number in
, int BaseIn
, int BaseOut
)
1107 cmsUInt32Number out
;
1109 for (len
=0; in
> 0 && len
< 100; len
++) {
1111 Buff
[len
] = (char) (in
% BaseIn
);
1115 for (i
=len
-1, out
=0; i
>= 0; --i
) {
1116 out
= out
* BaseOut
+ Buff
[i
];
1122 void CMSEXPORT
cmsSetProfileVersion(cmsHPROFILE hProfile
, cmsFloat64Number Version
)
1124 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1128 Icc
-> Version
= BaseToBase((cmsUInt32Number
) floor(Version
* 100.0 + 0.5), 10, 16) << 16;
1131 cmsFloat64Number CMSEXPORT
cmsGetProfileVersion(cmsHPROFILE hProfile
)
1133 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1134 cmsUInt32Number n
= Icc
-> Version
>> 16;
1136 return BaseToBase(n
, 16, 10) / 100.0;
1138 // --------------------------------------------------------------------------------------------------------------
1141 // Create profile from IOhandler
1142 cmsHPROFILE CMSEXPORT
cmsOpenProfileFromIOhandlerTHR(cmsContext ContextID
, cmsIOHANDLER
* io
)
1144 _cmsICCPROFILE
* NewIcc
;
1145 cmsHPROFILE hEmpty
= cmsCreateProfilePlaceholder(ContextID
);
1147 if (hEmpty
== NULL
) return NULL
;
1149 NewIcc
= (_cmsICCPROFILE
*) hEmpty
;
1151 NewIcc
->IOhandler
= io
;
1152 if (!_cmsReadHeader(NewIcc
)) goto Error
;
1156 cmsCloseProfile(hEmpty
);
1160 // Create profile from IOhandler
1161 cmsHPROFILE CMSEXPORT
cmsOpenProfileFromIOhandler2THR(cmsContext ContextID
, cmsIOHANDLER
* io
, cmsBool write
)
1163 _cmsICCPROFILE
* NewIcc
;
1164 cmsHPROFILE hEmpty
= cmsCreateProfilePlaceholder(ContextID
);
1166 if (hEmpty
== NULL
) return NULL
;
1168 NewIcc
= (_cmsICCPROFILE
*) hEmpty
;
1170 NewIcc
->IOhandler
= io
;
1173 NewIcc
-> IsWrite
= TRUE
;
1177 if (!_cmsReadHeader(NewIcc
)) goto Error
;
1181 cmsCloseProfile(hEmpty
);
1186 // Create profile from disk file
1187 cmsHPROFILE CMSEXPORT
cmsOpenProfileFromFileTHR(cmsContext ContextID
, const char *lpFileName
, const char *sAccess
)
1189 _cmsICCPROFILE
* NewIcc
;
1190 cmsHPROFILE hEmpty
= cmsCreateProfilePlaceholder(ContextID
);
1192 if (hEmpty
== NULL
) return NULL
;
1194 NewIcc
= (_cmsICCPROFILE
*) hEmpty
;
1196 NewIcc
->IOhandler
= cmsOpenIOhandlerFromFile(ContextID
, lpFileName
, sAccess
);
1197 if (NewIcc
->IOhandler
== NULL
) goto Error
;
1199 if (*sAccess
== 'W' || *sAccess
== 'w') {
1201 NewIcc
-> IsWrite
= TRUE
;
1206 if (!_cmsReadHeader(NewIcc
)) goto Error
;
1210 cmsCloseProfile(hEmpty
);
1215 cmsHPROFILE CMSEXPORT
cmsOpenProfileFromFile(const char *ICCProfile
, const char *sAccess
)
1217 return cmsOpenProfileFromFileTHR(NULL
, ICCProfile
, sAccess
);
1221 cmsHPROFILE CMSEXPORT
cmsOpenProfileFromStreamTHR(cmsContext ContextID
, FILE* ICCProfile
, const char *sAccess
)
1223 _cmsICCPROFILE
* NewIcc
;
1224 cmsHPROFILE hEmpty
= cmsCreateProfilePlaceholder(ContextID
);
1226 if (hEmpty
== NULL
) return NULL
;
1228 NewIcc
= (_cmsICCPROFILE
*) hEmpty
;
1230 NewIcc
->IOhandler
= cmsOpenIOhandlerFromStream(ContextID
, ICCProfile
);
1231 if (NewIcc
->IOhandler
== NULL
) goto Error
;
1233 if (*sAccess
== 'w') {
1235 NewIcc
-> IsWrite
= TRUE
;
1239 if (!_cmsReadHeader(NewIcc
)) goto Error
;
1243 cmsCloseProfile(hEmpty
);
1248 cmsHPROFILE CMSEXPORT
cmsOpenProfileFromStream(FILE* ICCProfile
, const char *sAccess
)
1250 return cmsOpenProfileFromStreamTHR(NULL
, ICCProfile
, sAccess
);
1254 // Open from memory block
1255 cmsHPROFILE CMSEXPORT
cmsOpenProfileFromMemTHR(cmsContext ContextID
, const void* MemPtr
, cmsUInt32Number dwSize
)
1257 _cmsICCPROFILE
* NewIcc
;
1260 hEmpty
= cmsCreateProfilePlaceholder(ContextID
);
1261 if (hEmpty
== NULL
) return NULL
;
1263 NewIcc
= (_cmsICCPROFILE
*) hEmpty
;
1265 // Ok, in this case const void* is casted to void* just because open IO handler
1266 // shares read and writing modes. Don't abuse this feature!
1267 NewIcc
->IOhandler
= cmsOpenIOhandlerFromMem(ContextID
, (void*) MemPtr
, dwSize
, "r");
1268 if (NewIcc
->IOhandler
== NULL
) goto Error
;
1270 if (!_cmsReadHeader(NewIcc
)) goto Error
;
1275 cmsCloseProfile(hEmpty
);
1279 cmsHPROFILE CMSEXPORT
cmsOpenProfileFromMem(const void* MemPtr
, cmsUInt32Number dwSize
)
1281 return cmsOpenProfileFromMemTHR(NULL
, MemPtr
, dwSize
);
1286 // Dump tag contents. If the profile is being modified, untouched tags are copied from FileOrig
1288 cmsBool
SaveTags(_cmsICCPROFILE
* Icc
, _cmsICCPROFILE
* FileOrig
)
1290 cmsUInt8Number
* Data
;
1292 cmsUInt32Number Begin
;
1293 cmsIOHANDLER
* io
= Icc
->IOhandler
;
1294 cmsTagDescriptor
* TagDescriptor
;
1295 cmsTagTypeSignature TypeBase
;
1296 cmsTagTypeSignature Type
;
1297 cmsTagTypeHandler
* TypeHandler
;
1298 cmsFloat64Number Version
= cmsGetProfileVersion((cmsHPROFILE
) Icc
);
1299 cmsTagTypeHandler LocalTypeHandler
;
1301 for (i
=0; i
< Icc
-> TagCount
; i
++) {
1303 if (Icc
->TagNames
[i
] == (cmsTagSignature
) 0) continue;
1305 // Linked tags are not written
1306 if (Icc
->TagLinked
[i
] != (cmsTagSignature
) 0) continue;
1308 Icc
-> TagOffsets
[i
] = Begin
= io
->UsedSpace
;
1310 Data
= (cmsUInt8Number
*) Icc
-> TagPtrs
[i
];
1314 // Reach here if we are copying a tag from a disk-based ICC profile which has not been modified by user.
1315 // In this case a blind copy of the block data is performed
1316 if (FileOrig
!= NULL
&& Icc
-> TagOffsets
[i
]) {
1318 if (FileOrig
->IOhandler
!= NULL
)
1320 cmsUInt32Number TagSize
= FileOrig
->TagSizes
[i
];
1321 cmsUInt32Number TagOffset
= FileOrig
->TagOffsets
[i
];
1324 if (!FileOrig
->IOhandler
->Seek(FileOrig
->IOhandler
, TagOffset
)) return FALSE
;
1326 Mem
= _cmsMalloc(Icc
->ContextID
, TagSize
);
1327 if (Mem
== NULL
) return FALSE
;
1329 if (FileOrig
->IOhandler
->Read(FileOrig
->IOhandler
, Mem
, TagSize
, 1) != 1) return FALSE
;
1330 if (!io
->Write(io
, TagSize
, Mem
)) return FALSE
;
1331 _cmsFree(Icc
->ContextID
, Mem
);
1333 Icc
->TagSizes
[i
] = (io
->UsedSpace
- Begin
);
1336 // Align to 32 bit boundary.
1337 if (!_cmsWriteAlignment(io
))
1346 // Should this tag be saved as RAW? If so, tagsizes should be specified in advance (no further cooking is done)
1347 if (Icc
->TagSaveAsRaw
[i
]) {
1349 if (io
-> Write(io
, Icc
->TagSizes
[i
], Data
) != 1) return FALSE
;
1353 // Search for support on this tag
1354 TagDescriptor
= _cmsGetTagDescriptor(Icc
-> ContextID
, Icc
-> TagNames
[i
]);
1355 if (TagDescriptor
== NULL
) continue; // Unsupported, ignore it
1357 if (TagDescriptor
->DecideType
!= NULL
) {
1359 Type
= TagDescriptor
->DecideType(Version
, Data
);
1363 Type
= TagDescriptor
->SupportedTypes
[0];
1366 TypeHandler
= _cmsGetTagTypeHandler(Icc
->ContextID
, Type
);
1368 if (TypeHandler
== NULL
) {
1369 cmsSignalError(Icc
->ContextID
, cmsERROR_INTERNAL
, "(Internal) no handler for tag %x", Icc
-> TagNames
[i
]);
1373 TypeBase
= TypeHandler
->Signature
;
1374 if (!_cmsWriteTypeBase(io
, TypeBase
))
1377 LocalTypeHandler
= *TypeHandler
;
1378 LocalTypeHandler
.ContextID
= Icc
->ContextID
;
1379 LocalTypeHandler
.ICCVersion
= Icc
->Version
;
1380 if (!LocalTypeHandler
.WritePtr(&LocalTypeHandler
, io
, Data
, TagDescriptor
->ElemCount
)) {
1384 _cmsTagSignature2String(String
, (cmsTagSignature
) TypeBase
);
1385 cmsSignalError(Icc
->ContextID
, cmsERROR_WRITE
, "Couldn't write type '%s'", String
);
1391 Icc
-> TagSizes
[i
] = (io
->UsedSpace
- Begin
);
1393 // Align to 32 bit boundary.
1394 if (! _cmsWriteAlignment(io
))
1403 // Fill the offset and size fields for all linked tags
1405 cmsBool
SetLinks( _cmsICCPROFILE
* Icc
)
1409 for (i
=0; i
< Icc
-> TagCount
; i
++) {
1411 cmsTagSignature lnk
= Icc
->TagLinked
[i
];
1412 if (lnk
!= (cmsTagSignature
) 0) {
1414 int j
= _cmsSearchTag(Icc
, lnk
, FALSE
);
1417 Icc
->TagOffsets
[i
] = Icc
->TagOffsets
[j
];
1418 Icc
->TagSizes
[i
] = Icc
->TagSizes
[j
];
1427 // Low-level save to IOHANDLER. It returns the number of bytes used to
1428 // store the profile, or zero on error. io may be NULL and in this case
1429 // no data is written--only sizes are calculated
1430 cmsUInt32Number CMSEXPORT
cmsSaveProfileToIOhandler(cmsHPROFILE hProfile
, cmsIOHANDLER
* io
)
1432 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1433 _cmsICCPROFILE Keep
;
1434 cmsIOHANDLER
* PrevIO
= NULL
;
1435 cmsUInt32Number UsedSpace
;
1436 cmsContext ContextID
;
1438 _cmsAssert(hProfile
!= NULL
);
1440 if (!_cmsLockMutex(Icc
->ContextID
, Icc
->UsrMutex
)) return 0;
1441 memmove(&Keep
, Icc
, sizeof(_cmsICCPROFILE
));
1443 ContextID
= cmsGetProfileContextID(hProfile
);
1444 PrevIO
= Icc
->IOhandler
= cmsOpenIOhandlerFromNULL(ContextID
);
1445 if (PrevIO
== NULL
) {
1446 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
1450 // Pass #1 does compute offsets
1452 if (!_cmsWriteHeader(Icc
, 0)) goto Error
;
1453 if (!SaveTags(Icc
, &Keep
)) goto Error
;
1455 UsedSpace
= PrevIO
->UsedSpace
;
1457 // Pass #2 does save to iohandler
1461 Icc
->IOhandler
= io
;
1462 if (!SetLinks(Icc
)) goto Error
;
1463 if (!_cmsWriteHeader(Icc
, UsedSpace
)) goto Error
;
1464 if (!SaveTags(Icc
, &Keep
)) goto Error
;
1467 memmove(Icc
, &Keep
, sizeof(_cmsICCPROFILE
));
1468 if (!cmsCloseIOhandler(PrevIO
))
1469 UsedSpace
= 0; // As a error marker
1471 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
1477 cmsCloseIOhandler(PrevIO
);
1478 memmove(Icc
, &Keep
, sizeof(_cmsICCPROFILE
));
1479 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
1485 // Low-level save to disk.
1486 cmsBool CMSEXPORT
cmsSaveProfileToFile(cmsHPROFILE hProfile
, const char* FileName
)
1488 cmsContext ContextID
= cmsGetProfileContextID(hProfile
);
1489 cmsIOHANDLER
* io
= cmsOpenIOhandlerFromFile(ContextID
, FileName
, "w");
1492 if (io
== NULL
) return FALSE
;
1494 rc
= (cmsSaveProfileToIOhandler(hProfile
, io
) != 0);
1495 rc
&= cmsCloseIOhandler(io
);
1497 if (rc
== FALSE
) { // remove() is C99 per 7.19.4.1
1498 remove(FileName
); // We have to IGNORE return value in this case
1503 // Same as anterior, but for streams
1504 cmsBool CMSEXPORT
cmsSaveProfileToStream(cmsHPROFILE hProfile
, FILE* Stream
)
1507 cmsContext ContextID
= cmsGetProfileContextID(hProfile
);
1508 cmsIOHANDLER
* io
= cmsOpenIOhandlerFromStream(ContextID
, Stream
);
1510 if (io
== NULL
) return FALSE
;
1512 rc
= (cmsSaveProfileToIOhandler(hProfile
, io
) != 0);
1513 rc
&= cmsCloseIOhandler(io
);
1519 // Same as anterior, but for memory blocks. In this case, a NULL as MemPtr means calculate needed space only
1520 cmsBool CMSEXPORT
cmsSaveProfileToMem(cmsHPROFILE hProfile
, void *MemPtr
, cmsUInt32Number
* BytesNeeded
)
1524 cmsContext ContextID
= cmsGetProfileContextID(hProfile
);
1526 _cmsAssert(BytesNeeded
!= NULL
);
1528 // Should we just calculate the needed space?
1529 if (MemPtr
== NULL
) {
1531 *BytesNeeded
= cmsSaveProfileToIOhandler(hProfile
, NULL
);
1532 return (*BytesNeeded
== 0) ? FALSE
: TRUE
;
1535 // That is a real write operation
1536 io
= cmsOpenIOhandlerFromMem(ContextID
, MemPtr
, *BytesNeeded
, "w");
1537 if (io
== NULL
) return FALSE
;
1539 rc
= (cmsSaveProfileToIOhandler(hProfile
, io
) != 0);
1540 rc
&= cmsCloseIOhandler(io
);
1545 // Free one tag contents
1547 void freeOneTag(_cmsICCPROFILE
* Icc
, cmsUInt32Number i
)
1549 if (Icc
->TagPtrs
[i
]) {
1551 cmsTagTypeHandler
* TypeHandler
= Icc
->TagTypeHandlers
[i
];
1553 if (TypeHandler
!= NULL
) {
1554 cmsTagTypeHandler LocalTypeHandler
= *TypeHandler
;
1556 LocalTypeHandler
.ContextID
= Icc
->ContextID
;
1557 LocalTypeHandler
.ICCVersion
= Icc
->Version
;
1558 LocalTypeHandler
.FreePtr(&LocalTypeHandler
, Icc
->TagPtrs
[i
]);
1561 _cmsFree(Icc
->ContextID
, Icc
->TagPtrs
[i
]);
1565 // Closes a profile freeing any involved resources
1566 cmsBool CMSEXPORT
cmsCloseProfile(cmsHPROFILE hProfile
)
1568 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1572 if (!Icc
) return FALSE
;
1574 // Was open in write mode?
1575 if (Icc
->IsWrite
) {
1577 Icc
->IsWrite
= FALSE
; // Assure no further writing
1578 rc
&= cmsSaveProfileToFile(hProfile
, Icc
->IOhandler
->PhysicalFile
);
1581 for (i
=0; i
< Icc
-> TagCount
; i
++) {
1586 if (Icc
->IOhandler
!= NULL
) {
1587 rc
&= cmsCloseIOhandler(Icc
->IOhandler
);
1590 _cmsDestroyMutex(Icc
->ContextID
, Icc
->UsrMutex
);
1592 _cmsFree(Icc
->ContextID
, Icc
); // Free placeholder memory
1598 // -------------------------------------------------------------------------------------------------------------------
1601 // Returns TRUE if a given tag is supported by a plug-in
1603 cmsBool
IsTypeSupported(cmsTagDescriptor
* TagDescriptor
, cmsTagTypeSignature Type
)
1605 cmsUInt32Number i
, nMaxTypes
;
1607 nMaxTypes
= TagDescriptor
->nSupportedTypes
;
1608 if (nMaxTypes
>= MAX_TYPES_IN_LCMS_PLUGIN
)
1609 nMaxTypes
= MAX_TYPES_IN_LCMS_PLUGIN
;
1611 for (i
=0; i
< nMaxTypes
; i
++) {
1612 if (Type
== TagDescriptor
->SupportedTypes
[i
]) return TRUE
;
1619 // That's the main read function
1620 void* CMSEXPORT
cmsReadTag(cmsHPROFILE hProfile
, cmsTagSignature sig
)
1622 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1624 cmsTagTypeHandler
* TypeHandler
;
1625 cmsTagTypeHandler LocalTypeHandler
;
1626 cmsTagDescriptor
* TagDescriptor
;
1627 cmsTagTypeSignature BaseType
;
1628 cmsUInt32Number Offset
, TagSize
;
1629 cmsUInt32Number ElemCount
;
1632 if (!_cmsLockMutex(Icc
->ContextID
, Icc
->UsrMutex
)) return NULL
;
1634 n
= _cmsSearchTag(Icc
, sig
, TRUE
);
1637 // Not found, return NULL
1638 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
1642 // If the element is already in memory, return the pointer
1643 if (Icc
-> TagPtrs
[n
]) {
1645 if (Icc
->TagTypeHandlers
[n
] == NULL
) goto Error
;
1648 BaseType
= Icc
->TagTypeHandlers
[n
]->Signature
;
1649 if (BaseType
== 0) goto Error
;
1651 TagDescriptor
= _cmsGetTagDescriptor(Icc
->ContextID
, sig
);
1652 if (TagDescriptor
== NULL
) goto Error
;
1654 if (!IsTypeSupported(TagDescriptor
, BaseType
)) goto Error
;
1656 if (Icc
->TagSaveAsRaw
[n
]) goto Error
; // We don't support read raw tags as cooked
1658 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
1659 return Icc
-> TagPtrs
[n
];
1662 // We need to read it. Get the offset and size to the file
1663 Offset
= Icc
-> TagOffsets
[n
];
1664 TagSize
= Icc
-> TagSizes
[n
];
1666 if (TagSize
< 8) goto Error
;
1668 io
= Icc
->IOhandler
;
1670 if (io
== NULL
) { // This is a built-in profile that has been manipulated, abort early
1672 cmsSignalError(Icc
->ContextID
, cmsERROR_CORRUPTION_DETECTED
, "Corrupted built-in profile.");
1676 // Seek to its location
1677 if (!io
-> Seek(io
, Offset
))
1680 // Search for support on this tag
1681 TagDescriptor
= _cmsGetTagDescriptor(Icc
-> ContextID
, sig
);
1682 if (TagDescriptor
== NULL
) {
1686 _cmsTagSignature2String(String
, sig
);
1688 // An unknown element was found.
1689 cmsSignalError(Icc
->ContextID
, cmsERROR_UNKNOWN_EXTENSION
, "Unknown tag type '%s' found.", String
);
1690 goto Error
; // Unsupported.
1693 // if supported, get type and check if in list
1694 BaseType
= _cmsReadTypeBase(io
);
1695 if (BaseType
== 0) goto Error
;
1697 if (!IsTypeSupported(TagDescriptor
, BaseType
)) goto Error
;
1699 TagSize
-= 8; // Already read by the type base logic
1702 TypeHandler
= _cmsGetTagTypeHandler(Icc
->ContextID
, BaseType
);
1703 if (TypeHandler
== NULL
) goto Error
;
1704 LocalTypeHandler
= *TypeHandler
;
1708 Icc
-> TagTypeHandlers
[n
] = TypeHandler
;
1710 LocalTypeHandler
.ContextID
= Icc
->ContextID
;
1711 LocalTypeHandler
.ICCVersion
= Icc
->Version
;
1712 Icc
-> TagPtrs
[n
] = LocalTypeHandler
.ReadPtr(&LocalTypeHandler
, io
, &ElemCount
, TagSize
);
1714 // The tag type is supported, but something wrong happened and we cannot read the tag.
1715 // let know the user about this (although it is just a warning)
1716 if (Icc
-> TagPtrs
[n
] == NULL
) {
1720 _cmsTagSignature2String(String
, sig
);
1721 cmsSignalError(Icc
->ContextID
, cmsERROR_CORRUPTION_DETECTED
, "Corrupted tag '%s'", String
);
1725 // This is a weird error that may be a symptom of something more serious, the number of
1726 // stored item is actually less than the number of required elements.
1727 if (ElemCount
< TagDescriptor
->ElemCount
) {
1731 _cmsTagSignature2String(String
, sig
);
1732 cmsSignalError(Icc
->ContextID
, cmsERROR_CORRUPTION_DETECTED
, "'%s' Inconsistent number of items: expected %d, got %d",
1733 String
, TagDescriptor
->ElemCount
, ElemCount
);
1739 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
1740 return Icc
-> TagPtrs
[n
];
1743 // Return error and unlock the data
1747 Icc
->TagPtrs
[n
] = NULL
;
1749 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
1754 // Get true type of data
1755 cmsTagTypeSignature
_cmsGetTagTrueType(cmsHPROFILE hProfile
, cmsTagSignature sig
)
1757 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1758 cmsTagTypeHandler
* TypeHandler
;
1761 // Search for given tag in ICC profile directory
1762 n
= _cmsSearchTag(Icc
, sig
, TRUE
);
1763 if (n
< 0) return (cmsTagTypeSignature
) 0; // Not found, return NULL
1765 // Get the handler. The true type is there
1766 TypeHandler
= Icc
-> TagTypeHandlers
[n
];
1767 return TypeHandler
->Signature
;
1771 // Write a single tag. This just keeps track of the tak into a list of "to be written". If the tag is already
1772 // in that list, the previous version is deleted.
1773 cmsBool CMSEXPORT
cmsWriteTag(cmsHPROFILE hProfile
, cmsTagSignature sig
, const void* data
)
1775 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1776 cmsTagTypeHandler
* TypeHandler
= NULL
;
1777 cmsTagTypeHandler LocalTypeHandler
;
1778 cmsTagDescriptor
* TagDescriptor
= NULL
;
1779 cmsTagTypeSignature Type
;
1781 cmsFloat64Number Version
;
1782 char TypeString
[5], SigString
[5];
1784 if (!_cmsLockMutex(Icc
->ContextID
, Icc
->UsrMutex
)) return FALSE
;
1790 i
= _cmsSearchTag(Icc
, sig
, FALSE
);
1793 // Use zero as a mark of deleted
1794 _cmsDeleteTagByPos(Icc
, i
);
1795 Icc
->TagNames
[i
] = (cmsTagSignature
) 0;
1796 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
1799 // Didn't find the tag
1803 if (!_cmsNewTag(Icc
, sig
, &i
)) goto Error
;
1806 Icc
->TagSaveAsRaw
[i
] = FALSE
;
1808 // This is not a link
1809 Icc
->TagLinked
[i
] = (cmsTagSignature
) 0;
1811 // Get information about the TAG.
1812 TagDescriptor
= _cmsGetTagDescriptor(Icc
-> ContextID
, sig
);
1813 if (TagDescriptor
== NULL
){
1814 cmsSignalError(Icc
->ContextID
, cmsERROR_UNKNOWN_EXTENSION
, "Unsupported tag '%x'", sig
);
1819 // Now we need to know which type to use. It depends on the version.
1820 Version
= cmsGetProfileVersion(hProfile
);
1822 if (TagDescriptor
->DecideType
!= NULL
) {
1824 // Let the tag descriptor to decide the type base on depending on
1825 // the data. This is useful for example on parametric curves, where
1826 // curves specified by a table cannot be saved as parametric and needs
1827 // to be casted to single v2-curves, even on v4 profiles.
1829 Type
= TagDescriptor
->DecideType(Version
, data
);
1833 Type
= TagDescriptor
->SupportedTypes
[0];
1836 // Does the tag support this type?
1837 if (!IsTypeSupported(TagDescriptor
, Type
)) {
1839 _cmsTagSignature2String(TypeString
, (cmsTagSignature
) Type
);
1840 _cmsTagSignature2String(SigString
, sig
);
1842 cmsSignalError(Icc
->ContextID
, cmsERROR_UNKNOWN_EXTENSION
, "Unsupported type '%s' for tag '%s'", TypeString
, SigString
);
1846 // Does we have a handler for this type?
1847 TypeHandler
= _cmsGetTagTypeHandler(Icc
->ContextID
, Type
);
1848 if (TypeHandler
== NULL
) {
1850 _cmsTagSignature2String(TypeString
, (cmsTagSignature
) Type
);
1851 _cmsTagSignature2String(SigString
, sig
);
1853 cmsSignalError(Icc
->ContextID
, cmsERROR_UNKNOWN_EXTENSION
, "Unsupported type '%s' for tag '%s'", TypeString
, SigString
);
1854 goto Error
; // Should never happen
1858 // Fill fields on icc structure
1859 Icc
->TagTypeHandlers
[i
] = TypeHandler
;
1860 Icc
->TagNames
[i
] = sig
;
1861 Icc
->TagSizes
[i
] = 0;
1862 Icc
->TagOffsets
[i
] = 0;
1864 LocalTypeHandler
= *TypeHandler
;
1865 LocalTypeHandler
.ContextID
= Icc
->ContextID
;
1866 LocalTypeHandler
.ICCVersion
= Icc
->Version
;
1867 Icc
->TagPtrs
[i
] = LocalTypeHandler
.DupPtr(&LocalTypeHandler
, data
, TagDescriptor
->ElemCount
);
1869 if (Icc
->TagPtrs
[i
] == NULL
) {
1871 _cmsTagSignature2String(TypeString
, (cmsTagSignature
) Type
);
1872 _cmsTagSignature2String(SigString
, sig
);
1873 cmsSignalError(Icc
->ContextID
, cmsERROR_CORRUPTION_DETECTED
, "Malformed struct in type '%s' for tag '%s'", TypeString
, SigString
);
1878 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
1882 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
1887 // Read and write raw data. Read/Write Raw/cooked pairs try to maintain consistency within the pair. Some sequences
1888 // raw/cooked would work, but at a cost. Data "cooked" may be converted to "raw" by using the "write" serialization logic.
1889 // In general it is better to avoid mixing pairs.
1891 cmsUInt32Number CMSEXPORT
cmsReadRawTag(cmsHPROFILE hProfile
, cmsTagSignature sig
, void* data
, cmsUInt32Number BufferSize
)
1893 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1896 cmsIOHANDLER
* MemIO
;
1897 cmsTagTypeHandler
* TypeHandler
= NULL
;
1898 cmsTagTypeHandler LocalTypeHandler
;
1899 cmsTagDescriptor
* TagDescriptor
= NULL
;
1901 cmsUInt32Number Offset
, TagSize
;
1904 if (data
!= NULL
&& BufferSize
== 0) return 0;
1906 if (!_cmsLockMutex(Icc
->ContextID
, Icc
->UsrMutex
)) return 0;
1908 // Search for given tag in ICC profile directory
1910 i
= _cmsSearchTag(Icc
, sig
, TRUE
);
1911 if (i
< 0) goto Error
; // Not found,
1913 // It is already read?
1914 if (Icc
-> TagPtrs
[i
] == NULL
) {
1916 // Not yet, get original position
1917 Offset
= Icc
->TagOffsets
[i
];
1918 TagSize
= Icc
->TagSizes
[i
];
1920 // read the data directly, don't keep copy
1924 if (BufferSize
< TagSize
)
1925 TagSize
= BufferSize
;
1927 if (!Icc
->IOhandler
->Seek(Icc
->IOhandler
, Offset
)) goto Error
;
1928 if (!Icc
->IOhandler
->Read(Icc
->IOhandler
, data
, 1, TagSize
)) goto Error
;
1930 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
1934 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
1935 return Icc
->TagSizes
[i
];
1938 // The data has been already read, or written. But wait!, maybe the user choose to save as
1939 // raw data. In this case, return the raw data directly
1941 if (Icc
->TagSaveAsRaw
[i
]) {
1945 TagSize
= Icc
->TagSizes
[i
];
1946 if (BufferSize
< TagSize
)
1947 TagSize
= BufferSize
;
1949 memmove(data
, Icc
->TagPtrs
[i
], TagSize
);
1951 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
1955 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
1956 return Icc
->TagSizes
[i
];
1959 // Already read, or previously set by cmsWriteTag(). We need to serialize that
1960 // data to raw to get something that makes sense
1962 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
1963 Object
= cmsReadTag(hProfile
, sig
);
1964 if (!_cmsLockMutex(Icc
->ContextID
, Icc
->UsrMutex
)) return 0;
1966 if (Object
== NULL
) goto Error
;
1968 // Now we need to serialize to a memory block: just use a memory iohandler
1971 MemIO
= cmsOpenIOhandlerFromNULL(cmsGetProfileContextID(hProfile
));
1973 MemIO
= cmsOpenIOhandlerFromMem(cmsGetProfileContextID(hProfile
), data
, BufferSize
, "w");
1975 if (MemIO
== NULL
) goto Error
;
1977 // Obtain type handling for the tag
1978 TypeHandler
= Icc
->TagTypeHandlers
[i
];
1979 TagDescriptor
= _cmsGetTagDescriptor(Icc
-> ContextID
, sig
);
1980 if (TagDescriptor
== NULL
) {
1981 cmsCloseIOhandler(MemIO
);
1985 if (TypeHandler
== NULL
) goto Error
;
1988 LocalTypeHandler
= *TypeHandler
;
1989 LocalTypeHandler
.ContextID
= Icc
->ContextID
;
1990 LocalTypeHandler
.ICCVersion
= Icc
->Version
;
1992 if (!_cmsWriteTypeBase(MemIO
, TypeHandler
->Signature
)) {
1993 cmsCloseIOhandler(MemIO
);
1997 if (!LocalTypeHandler
.WritePtr(&LocalTypeHandler
, MemIO
, Object
, TagDescriptor
->ElemCount
)) {
1998 cmsCloseIOhandler(MemIO
);
2002 // Get Size and close
2003 rc
= MemIO
->Tell(MemIO
);
2004 cmsCloseIOhandler(MemIO
); // Ignore return code this time
2006 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
2010 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
2014 // Similar to the anterior. This function allows to write directly to the ICC profile any data, without
2015 // checking anything. As a rule, mixing Raw with cooked doesn't work, so writing a tag as raw and then reading
2016 // it as cooked without serializing does result into an error. If that is what you want, you will need to dump
2017 // the profile to memry or disk and then reopen it.
2018 cmsBool CMSEXPORT
cmsWriteRawTag(cmsHPROFILE hProfile
, cmsTagSignature sig
, const void* data
, cmsUInt32Number Size
)
2020 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
2023 if (!_cmsLockMutex(Icc
->ContextID
, Icc
->UsrMutex
)) return 0;
2025 if (!_cmsNewTag(Icc
, sig
, &i
)) {
2026 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
2030 // Mark the tag as being written as RAW
2031 Icc
->TagSaveAsRaw
[i
] = TRUE
;
2032 Icc
->TagNames
[i
] = sig
;
2033 Icc
->TagLinked
[i
] = (cmsTagSignature
) 0;
2035 // Keep a copy of the block
2036 Icc
->TagPtrs
[i
] = _cmsDupMem(Icc
->ContextID
, data
, Size
);
2037 Icc
->TagSizes
[i
] = Size
;
2039 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
2041 if (Icc
->TagPtrs
[i
] == NULL
) {
2042 Icc
->TagNames
[i
] = (cmsTagSignature
) 0;
2048 // Using this function you can collapse several tag entries to the same block in the profile
2049 cmsBool CMSEXPORT
cmsLinkTag(cmsHPROFILE hProfile
, cmsTagSignature sig
, cmsTagSignature dest
)
2051 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
2054 if (!_cmsLockMutex(Icc
->ContextID
, Icc
->UsrMutex
)) return FALSE
;
2056 if (!_cmsNewTag(Icc
, sig
, &i
)) {
2057 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
2061 // Keep necessary information
2062 Icc
->TagSaveAsRaw
[i
] = FALSE
;
2063 Icc
->TagNames
[i
] = sig
;
2064 Icc
->TagLinked
[i
] = dest
;
2066 Icc
->TagPtrs
[i
] = NULL
;
2067 Icc
->TagSizes
[i
] = 0;
2068 Icc
->TagOffsets
[i
] = 0;
2070 _cmsUnlockMutex(Icc
->ContextID
, Icc
->UsrMutex
);
2075 // Returns the tag linked to sig, in the case two tags are sharing same resource
2076 cmsTagSignature CMSEXPORT
cmsTagLinkedTo(cmsHPROFILE hProfile
, cmsTagSignature sig
)
2078 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
2081 // Search for given tag in ICC profile directory
2082 i
= _cmsSearchTag(Icc
, sig
, FALSE
);
2083 if (i
< 0) return (cmsTagSignature
) 0; // Not found, return 0
2085 return Icc
-> TagLinked
[i
];