1 //---------------------------------------------------------------------------------
3 // Little Color Management System
4 // Copyright (c) 1998-2012 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 (iohandler
->UsedSpace
+ size
> ResData
->Size
) {
204 size
= ResData
->Size
- iohandler
->UsedSpace
;
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
;
211 iohandler
->UsedSpace
+= size
;
213 if (ResData
->Pointer
> iohandler
->UsedSpace
)
214 iohandler
->UsedSpace
= ResData
->Pointer
;
221 cmsBool
MemoryClose(struct _cms_io_handler
* iohandler
)
223 FILEMEM
* ResData
= (FILEMEM
*) iohandler
->stream
;
225 if (ResData
->FreeBlockOnClose
) {
227 if (ResData
->Block
) _cmsFree(iohandler
->ContextID
, ResData
->Block
);
230 _cmsFree(iohandler
->ContextID
, ResData
);
231 _cmsFree(iohandler
->ContextID
, iohandler
);
236 // Create a iohandler for memory block. AccessMode=='r' assumes the iohandler is going to read, and makes
237 // a copy of the memory block for letting user to free the memory after invoking open profile. In write
238 // mode ("w"), Buffere points to the begin of memory block to be written.
239 cmsIOHANDLER
* CMSEXPORT
cmsOpenIOhandlerFromMem(cmsContext ContextID
, void *Buffer
, cmsUInt32Number size
, const char* AccessMode
)
241 cmsIOHANDLER
* iohandler
= NULL
;
244 _cmsAssert(AccessMode
!= NULL
);
246 iohandler
= (cmsIOHANDLER
*) _cmsMallocZero(ContextID
, sizeof(cmsIOHANDLER
));
247 if (iohandler
== NULL
) return NULL
;
249 switch (*AccessMode
) {
252 fm
= (FILEMEM
*) _cmsMallocZero(ContextID
, sizeof(FILEMEM
));
253 if (fm
== NULL
) goto Error
;
255 if (Buffer
== NULL
) {
256 cmsSignalError(ContextID
, cmsERROR_READ
, "Couldn't read profile from NULL pointer");
260 fm
->Block
= (cmsUInt8Number
*) _cmsMalloc(ContextID
, size
);
261 if (fm
->Block
== NULL
) {
263 _cmsFree(ContextID
, fm
);
264 _cmsFree(ContextID
, iohandler
);
265 cmsSignalError(ContextID
, cmsERROR_READ
, "Couldn't allocate %ld bytes for profile", size
);
270 memmove(fm
->Block
, Buffer
, size
);
271 fm
->FreeBlockOnClose
= TRUE
;
274 iohandler
-> ReportedSize
= size
;
278 fm
= (FILEMEM
*) _cmsMallocZero(ContextID
, sizeof(FILEMEM
));
279 if (fm
== NULL
) goto Error
;
281 fm
->Block
= (cmsUInt8Number
*) Buffer
;
282 fm
->FreeBlockOnClose
= FALSE
;
285 iohandler
-> ReportedSize
= 0;
289 cmsSignalError(ContextID
, cmsERROR_UNKNOWN_EXTENSION
, "Unknown access mode '%c'", *AccessMode
);
293 iohandler
->ContextID
= ContextID
;
294 iohandler
->stream
= (void*) fm
;
295 iohandler
->UsedSpace
= 0;
296 iohandler
->PhysicalFile
[0] = 0;
298 iohandler
->Read
= MemoryRead
;
299 iohandler
->Seek
= MemorySeek
;
300 iohandler
->Close
= MemoryClose
;
301 iohandler
->Tell
= MemoryTell
;
302 iohandler
->Write
= MemoryWrite
;
307 if (fm
) _cmsFree(ContextID
, fm
);
308 if (iohandler
) _cmsFree(ContextID
, iohandler
);
312 // File-based stream -------------------------------------------------------
314 // Read count elements of size bytes each. Return number of elements read
316 cmsUInt32Number
FileRead(cmsIOHANDLER
* iohandler
, void *Buffer
, cmsUInt32Number size
, cmsUInt32Number count
)
318 cmsUInt32Number nReaded
= (cmsUInt32Number
) fread(Buffer
, size
, count
, (FILE*) iohandler
->stream
);
320 if (nReaded
!= count
) {
321 cmsSignalError(iohandler
->ContextID
, cmsERROR_FILE
, "Read error. Got %d bytes, block should be of %d bytes", nReaded
* size
, count
* size
);
328 // Postion file pointer in the file
330 cmsBool
FileSeek(cmsIOHANDLER
* iohandler
, cmsUInt32Number offset
)
332 if (fseek((FILE*) iohandler
->stream
, (long) offset
, SEEK_SET
) != 0) {
334 cmsSignalError(iohandler
->ContextID
, cmsERROR_FILE
, "Seek error; probably corrupted file");
341 // Returns file pointer position
343 cmsUInt32Number
FileTell(cmsIOHANDLER
* iohandler
)
345 return ftell((FILE*)iohandler
->stream
);
348 // Writes data to stream, also keeps used space for further reference. Returns TRUE on success, FALSE on error
350 cmsBool
FileWrite(cmsIOHANDLER
* iohandler
, cmsUInt32Number size
, const void* Buffer
)
352 if (size
== 0) return TRUE
; // We allow to write 0 bytes, but nothing is written
354 iohandler
->UsedSpace
+= size
;
355 return (fwrite(Buffer
, size
, 1, (FILE*) iohandler
->stream
) == 1);
360 cmsBool
FileClose(cmsIOHANDLER
* iohandler
)
362 if (fclose((FILE*) iohandler
->stream
) != 0) return FALSE
;
363 _cmsFree(iohandler
->ContextID
, iohandler
);
367 // Create a iohandler for disk based files.
368 cmsIOHANDLER
* CMSEXPORT
cmsOpenIOhandlerFromFile(cmsContext ContextID
, const char* FileName
, const char* AccessMode
)
370 cmsIOHANDLER
* iohandler
= NULL
;
373 _cmsAssert(FileName
!= NULL
);
374 _cmsAssert(AccessMode
!= NULL
);
376 iohandler
= (cmsIOHANDLER
*) _cmsMallocZero(ContextID
, sizeof(cmsIOHANDLER
));
377 if (iohandler
== NULL
) return NULL
;
379 switch (*AccessMode
) {
382 fm
= fopen(FileName
, "rb");
384 _cmsFree(ContextID
, iohandler
);
385 cmsSignalError(ContextID
, cmsERROR_FILE
, "File '%s' not found", FileName
);
388 iohandler
-> ReportedSize
= cmsfilelength(fm
);
392 fm
= fopen(FileName
, "wb");
394 _cmsFree(ContextID
, iohandler
);
395 cmsSignalError(ContextID
, cmsERROR_FILE
, "Couldn't create '%s'", FileName
);
398 iohandler
-> ReportedSize
= 0;
402 _cmsFree(ContextID
, iohandler
);
403 cmsSignalError(ContextID
, cmsERROR_FILE
, "Unknown access mode '%c'", *AccessMode
);
407 iohandler
->ContextID
= ContextID
;
408 iohandler
->stream
= (void*) fm
;
409 iohandler
->UsedSpace
= 0;
411 // Keep track of the original file
412 strncpy(iohandler
-> PhysicalFile
, FileName
, sizeof(iohandler
-> PhysicalFile
)-1);
413 iohandler
-> PhysicalFile
[sizeof(iohandler
-> PhysicalFile
)-1] = 0;
415 iohandler
->Read
= FileRead
;
416 iohandler
->Seek
= FileSeek
;
417 iohandler
->Close
= FileClose
;
418 iohandler
->Tell
= FileTell
;
419 iohandler
->Write
= FileWrite
;
424 // Create a iohandler for stream based files
425 cmsIOHANDLER
* CMSEXPORT
cmsOpenIOhandlerFromStream(cmsContext ContextID
, FILE* Stream
)
427 cmsIOHANDLER
* iohandler
= NULL
;
429 iohandler
= (cmsIOHANDLER
*) _cmsMallocZero(ContextID
, sizeof(cmsIOHANDLER
));
430 if (iohandler
== NULL
) return NULL
;
432 iohandler
-> ContextID
= ContextID
;
433 iohandler
-> stream
= (void*) Stream
;
434 iohandler
-> UsedSpace
= 0;
435 iohandler
-> ReportedSize
= cmsfilelength(Stream
);
436 iohandler
-> PhysicalFile
[0] = 0;
438 iohandler
->Read
= FileRead
;
439 iohandler
->Seek
= FileSeek
;
440 iohandler
->Close
= FileClose
;
441 iohandler
->Tell
= FileTell
;
442 iohandler
->Write
= FileWrite
;
449 // Close an open IO handler
450 cmsBool CMSEXPORT
cmsCloseIOhandler(cmsIOHANDLER
* io
)
452 return io
-> Close(io
);
455 // -------------------------------------------------------------------------------------------------------
457 // Creates an empty structure holding all required parameters
458 cmsHPROFILE CMSEXPORT
cmsCreateProfilePlaceholder(cmsContext ContextID
)
460 time_t now
= time(NULL
);
461 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) _cmsMallocZero(ContextID
, sizeof(_cmsICCPROFILE
));
462 if (Icc
== NULL
) return NULL
;
464 Icc
->ContextID
= ContextID
;
469 // Set default version
470 Icc
->Version
= 0x02100000;
472 // Set creation date/time
473 memmove(&Icc
->Created
, gmtime(&now
), sizeof(Icc
->Created
));
476 return (cmsHPROFILE
) Icc
;
479 cmsContext CMSEXPORT
cmsGetProfileContextID(cmsHPROFILE hProfile
)
481 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
483 if (Icc
== NULL
) return NULL
;
484 return Icc
-> ContextID
;
488 // Return the number of tags
489 cmsInt32Number CMSEXPORT
cmsGetTagCount(cmsHPROFILE hProfile
)
491 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
492 if (Icc
== NULL
) return -1;
494 return Icc
->TagCount
;
497 // Return the tag signature of a given tag number
498 cmsTagSignature CMSEXPORT
cmsGetTagSignature(cmsHPROFILE hProfile
, cmsUInt32Number n
)
500 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
502 if (n
> Icc
->TagCount
) return (cmsTagSignature
) 0; // Mark as not available
503 if (n
>= MAX_TABLE_TAG
) return (cmsTagSignature
) 0; // As double check
505 return Icc
->TagNames
[n
];
510 int SearchOneTag(_cmsICCPROFILE
* Profile
, cmsTagSignature sig
)
514 for (i
=0; i
< Profile
-> TagCount
; i
++) {
516 if (sig
== Profile
-> TagNames
[i
])
523 // Search for a specific tag in tag dictionary. Returns position or -1 if tag not found.
524 // If followlinks is turned on, then the position of the linked tag is returned
525 int _cmsSearchTag(_cmsICCPROFILE
* Icc
, cmsTagSignature sig
, cmsBool lFollowLinks
)
528 cmsTagSignature LinkedSig
;
532 // Search for given tag in ICC profile directory
533 n
= SearchOneTag(Icc
, sig
);
535 return -1; // Not found
538 return n
; // Found, don't follow links
540 // Is this a linked tag?
541 LinkedSig
= Icc
->TagLinked
[n
];
544 if (LinkedSig
!= (cmsTagSignature
) 0) {
548 } while (LinkedSig
!= (cmsTagSignature
) 0);
554 // Create a new tag entry
557 cmsBool
_cmsNewTag(_cmsICCPROFILE
* Icc
, cmsTagSignature sig
, int* NewPos
)
561 // Search for the tag
562 i
= _cmsSearchTag(Icc
, sig
, FALSE
);
564 // Now let's do it easy. If the tag has been already written, that's an error
566 cmsSignalError(Icc
->ContextID
, cmsERROR_ALREADY_DEFINED
, "Tag '%x' already exists", sig
);
573 if (Icc
-> TagCount
>= MAX_TABLE_TAG
) {
574 cmsSignalError(Icc
->ContextID
, cmsERROR_RANGE
, "Too many tags (%d)", MAX_TABLE_TAG
);
578 *NewPos
= Icc
->TagCount
;
587 cmsBool CMSEXPORT
cmsIsTag(cmsHPROFILE hProfile
, cmsTagSignature sig
)
589 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) (void*) hProfile
;
590 return _cmsSearchTag(Icc
, sig
, FALSE
) >= 0;
594 // Read profile header and validate it
595 cmsBool
_cmsReadHeader(_cmsICCPROFILE
* Icc
)
599 cmsUInt32Number i
, j
;
600 cmsUInt32Number HeaderSize
;
601 cmsIOHANDLER
* io
= Icc
->IOhandler
;
602 cmsUInt32Number TagCount
;
606 if (io
-> Read(io
, &Header
, sizeof(cmsICCHeader
), 1) != 1) {
610 // Validate file as an ICC profile
611 if (_cmsAdjustEndianess32(Header
.magic
) != cmsMagicNumber
) {
612 cmsSignalError(Icc
->ContextID
, cmsERROR_BAD_SIGNATURE
, "not an ICC profile, invalid signature");
616 // Adjust endianess of the used parameters
617 Icc
-> DeviceClass
= (cmsProfileClassSignature
) _cmsAdjustEndianess32(Header
.deviceClass
);
618 Icc
-> ColorSpace
= (cmsColorSpaceSignature
) _cmsAdjustEndianess32(Header
.colorSpace
);
619 Icc
-> PCS
= (cmsColorSpaceSignature
) _cmsAdjustEndianess32(Header
.pcs
);
621 Icc
-> RenderingIntent
= _cmsAdjustEndianess32(Header
.renderingIntent
);
622 Icc
-> flags
= _cmsAdjustEndianess32(Header
.flags
);
623 Icc
-> manufacturer
= _cmsAdjustEndianess32(Header
.manufacturer
);
624 Icc
-> model
= _cmsAdjustEndianess32(Header
.model
);
625 Icc
-> creator
= _cmsAdjustEndianess32(Header
.creator
);
627 _cmsAdjustEndianess64(&Icc
-> attributes
, &Header
.attributes
);
628 Icc
-> Version
= _cmsAdjustEndianess32(Header
.version
);
630 // Get size as reported in header
631 HeaderSize
= _cmsAdjustEndianess32(Header
.size
);
633 // Make sure HeaderSize is lower than profile size
634 if (HeaderSize
>= Icc
->IOhandler
->ReportedSize
)
635 HeaderSize
= Icc
->IOhandler
->ReportedSize
;
638 // Get creation date/time
639 _cmsDecodeDateTimeNumber(&Header
.date
, &Icc
->Created
);
641 // The profile ID are 32 raw bytes
642 memmove(Icc
->ProfileID
.ID32
, Header
.profileID
.ID32
, 16);
645 // Read tag directory
646 if (!_cmsReadUInt32Number(io
, &TagCount
)) return FALSE
;
647 if (TagCount
> MAX_TABLE_TAG
) {
649 cmsSignalError(Icc
->ContextID
, cmsERROR_RANGE
, "Too many tags (%d)", TagCount
);
654 // Read tag directory
656 for (i
=0; i
< TagCount
; i
++) {
658 if (!_cmsReadUInt32Number(io
, (cmsUInt32Number
*) &Tag
.sig
)) return FALSE
;
659 if (!_cmsReadUInt32Number(io
, &Tag
.offset
)) return FALSE
;
660 if (!_cmsReadUInt32Number(io
, &Tag
.size
)) return FALSE
;
662 // Perform some sanity check. Offset + size should fall inside file.
663 if (Tag
.offset
+ Tag
.size
> HeaderSize
||
664 Tag
.offset
+ Tag
.size
< Tag
.offset
)
667 Icc
-> TagNames
[Icc
->TagCount
] = Tag
.sig
;
668 Icc
-> TagOffsets
[Icc
->TagCount
] = Tag
.offset
;
669 Icc
-> TagSizes
[Icc
->TagCount
] = Tag
.size
;
672 for (j
=0; j
< Icc
->TagCount
; j
++) {
674 if ((Icc
->TagOffsets
[j
] == Tag
.offset
) &&
675 (Icc
->TagSizes
[j
] == Tag
.size
)) {
677 Icc
->TagLinked
[Icc
->TagCount
] = Icc
->TagNames
[j
];
688 // Saves profile header
689 cmsBool
_cmsWriteHeader(_cmsICCPROFILE
* Icc
, cmsUInt32Number UsedSpace
)
694 cmsInt32Number Count
= 0;
696 Header
.size
= _cmsAdjustEndianess32(UsedSpace
);
697 Header
.cmmId
= _cmsAdjustEndianess32(lcmsSignature
);
698 Header
.version
= _cmsAdjustEndianess32(Icc
->Version
);
700 Header
.deviceClass
= (cmsProfileClassSignature
) _cmsAdjustEndianess32(Icc
-> DeviceClass
);
701 Header
.colorSpace
= (cmsColorSpaceSignature
) _cmsAdjustEndianess32(Icc
-> ColorSpace
);
702 Header
.pcs
= (cmsColorSpaceSignature
) _cmsAdjustEndianess32(Icc
-> PCS
);
704 // NOTE: in v4 Timestamp must be in UTC rather than in local time
705 _cmsEncodeDateTimeNumber(&Header
.date
, &Icc
->Created
);
707 Header
.magic
= _cmsAdjustEndianess32(cmsMagicNumber
);
709 #ifdef CMS_IS_WINDOWS_
710 Header
.platform
= (cmsPlatformSignature
) _cmsAdjustEndianess32(cmsSigMicrosoft
);
712 Header
.platform
= (cmsPlatformSignature
) _cmsAdjustEndianess32(cmsSigMacintosh
);
715 Header
.flags
= _cmsAdjustEndianess32(Icc
-> flags
);
716 Header
.manufacturer
= _cmsAdjustEndianess32(Icc
-> manufacturer
);
717 Header
.model
= _cmsAdjustEndianess32(Icc
-> model
);
719 _cmsAdjustEndianess64(&Header
.attributes
, &Icc
-> attributes
);
721 // Rendering intent in the header (for embedded profiles)
722 Header
.renderingIntent
= _cmsAdjustEndianess32(Icc
-> RenderingIntent
);
724 // Illuminant is always D50
725 Header
.illuminant
.X
= _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(cmsD50_XYZ()->X
));
726 Header
.illuminant
.Y
= _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(cmsD50_XYZ()->Y
));
727 Header
.illuminant
.Z
= _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(cmsD50_XYZ()->Z
));
729 // Created by LittleCMS (that's me!)
730 Header
.creator
= _cmsAdjustEndianess32(lcmsSignature
);
732 memset(&Header
.reserved
, 0, sizeof(Header
.reserved
));
734 // Set profile ID. Endianess is always big endian
735 memmove(&Header
.profileID
, &Icc
->ProfileID
, 16);
738 if (!Icc
-> IOhandler
->Write(Icc
->IOhandler
, sizeof(cmsICCHeader
), &Header
)) return FALSE
;
740 // Saves Tag directory
743 for (i
=0; i
< Icc
-> TagCount
; i
++) {
744 if (Icc
->TagNames
[i
] != 0)
748 // Store number of tags
749 if (!_cmsWriteUInt32Number(Icc
->IOhandler
, Count
)) return FALSE
;
751 for (i
=0; i
< Icc
-> TagCount
; i
++) {
753 if (Icc
->TagNames
[i
] == 0) continue; // It is just a placeholder
755 Tag
.sig
= (cmsTagSignature
) _cmsAdjustEndianess32((cmsInt32Number
) Icc
-> TagNames
[i
]);
756 Tag
.offset
= _cmsAdjustEndianess32((cmsInt32Number
) Icc
-> TagOffsets
[i
]);
757 Tag
.size
= _cmsAdjustEndianess32((cmsInt32Number
) Icc
-> TagSizes
[i
]);
759 if (!Icc
->IOhandler
-> Write(Icc
-> IOhandler
, sizeof(cmsTagEntry
), &Tag
)) return FALSE
;
765 // ----------------------------------------------------------------------- Set/Get several struct members
768 cmsUInt32Number CMSEXPORT
cmsGetHeaderRenderingIntent(cmsHPROFILE hProfile
)
770 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
771 return Icc
-> RenderingIntent
;
774 void CMSEXPORT
cmsSetHeaderRenderingIntent(cmsHPROFILE hProfile
, cmsUInt32Number RenderingIntent
)
776 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
777 Icc
-> RenderingIntent
= RenderingIntent
;
780 cmsUInt32Number CMSEXPORT
cmsGetHeaderFlags(cmsHPROFILE hProfile
)
782 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
783 return (cmsUInt32Number
) Icc
-> flags
;
786 void CMSEXPORT
cmsSetHeaderFlags(cmsHPROFILE hProfile
, cmsUInt32Number Flags
)
788 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
789 Icc
-> flags
= (cmsUInt32Number
) Flags
;
792 cmsUInt32Number CMSEXPORT
cmsGetHeaderManufacturer(cmsHPROFILE hProfile
)
794 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
795 return Icc
->manufacturer
;
798 void CMSEXPORT
cmsSetHeaderManufacturer(cmsHPROFILE hProfile
, cmsUInt32Number manufacturer
)
800 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
801 Icc
-> manufacturer
= manufacturer
;
804 cmsUInt32Number CMSEXPORT
cmsGetHeaderCreator(cmsHPROFILE hProfile
)
806 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
807 return Icc
->creator
;
810 cmsUInt32Number CMSEXPORT
cmsGetHeaderModel(cmsHPROFILE hProfile
)
812 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
816 void CMSEXPORT
cmsSetHeaderModel(cmsHPROFILE hProfile
, cmsUInt32Number model
)
818 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
819 Icc
-> model
= model
;
822 void CMSEXPORT
cmsGetHeaderAttributes(cmsHPROFILE hProfile
, cmsUInt64Number
* Flags
)
824 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
825 memmove(Flags
, &Icc
-> attributes
, sizeof(cmsUInt64Number
));
828 void CMSEXPORT
cmsSetHeaderAttributes(cmsHPROFILE hProfile
, cmsUInt64Number Flags
)
830 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
831 memmove(&Icc
-> attributes
, &Flags
, sizeof(cmsUInt64Number
));
834 void CMSEXPORT
cmsGetHeaderProfileID(cmsHPROFILE hProfile
, cmsUInt8Number
* ProfileID
)
836 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
837 memmove(ProfileID
, Icc
->ProfileID
.ID8
, 16);
840 void CMSEXPORT
cmsSetHeaderProfileID(cmsHPROFILE hProfile
, cmsUInt8Number
* ProfileID
)
842 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
843 memmove(&Icc
-> ProfileID
, ProfileID
, 16);
846 cmsBool CMSEXPORT
cmsGetHeaderCreationDateTime(cmsHPROFILE hProfile
, struct tm
*Dest
)
848 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
849 memmove(Dest
, &Icc
->Created
, sizeof(struct tm
));
853 cmsColorSpaceSignature CMSEXPORT
cmsGetPCS(cmsHPROFILE hProfile
)
855 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
859 void CMSEXPORT
cmsSetPCS(cmsHPROFILE hProfile
, cmsColorSpaceSignature pcs
)
861 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
865 cmsColorSpaceSignature CMSEXPORT
cmsGetColorSpace(cmsHPROFILE hProfile
)
867 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
868 return Icc
-> ColorSpace
;
871 void CMSEXPORT
cmsSetColorSpace(cmsHPROFILE hProfile
, cmsColorSpaceSignature sig
)
873 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
874 Icc
-> ColorSpace
= sig
;
877 cmsProfileClassSignature CMSEXPORT
cmsGetDeviceClass(cmsHPROFILE hProfile
)
879 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
880 return Icc
-> DeviceClass
;
883 void CMSEXPORT
cmsSetDeviceClass(cmsHPROFILE hProfile
, cmsProfileClassSignature sig
)
885 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
886 Icc
-> DeviceClass
= sig
;
889 cmsUInt32Number CMSEXPORT
cmsGetEncodedICCversion(cmsHPROFILE hProfile
)
891 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
892 return Icc
-> Version
;
895 void CMSEXPORT
cmsSetEncodedICCversion(cmsHPROFILE hProfile
, cmsUInt32Number Version
)
897 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
898 Icc
-> Version
= Version
;
901 // Get an hexadecimal number with same digits as v
903 cmsUInt32Number
BaseToBase(cmsUInt32Number in
, int BaseIn
, int BaseOut
)
909 for (len
=0; in
> 0 && len
< 100; len
++) {
911 Buff
[len
] = (char) (in
% BaseIn
);
915 for (i
=len
-1, out
=0; i
>= 0; --i
) {
916 out
= out
* BaseOut
+ Buff
[i
];
922 void CMSEXPORT
cmsSetProfileVersion(cmsHPROFILE hProfile
, cmsFloat64Number Version
)
924 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
928 Icc
-> Version
= BaseToBase((cmsUInt32Number
) floor(Version
* 100.0), 10, 16) << 16;
931 cmsFloat64Number CMSEXPORT
cmsGetProfileVersion(cmsHPROFILE hProfile
)
933 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
934 cmsUInt32Number n
= Icc
-> Version
>> 16;
936 return BaseToBase(n
, 16, 10) / 100.0;
938 // --------------------------------------------------------------------------------------------------------------
941 // Create profile from IOhandler
942 cmsHPROFILE CMSEXPORT
cmsOpenProfileFromIOhandlerTHR(cmsContext ContextID
, cmsIOHANDLER
* io
)
944 _cmsICCPROFILE
* NewIcc
;
945 cmsHPROFILE hEmpty
= cmsCreateProfilePlaceholder(ContextID
);
947 if (hEmpty
== NULL
) return NULL
;
949 NewIcc
= (_cmsICCPROFILE
*) hEmpty
;
951 NewIcc
->IOhandler
= io
;
952 if (!_cmsReadHeader(NewIcc
)) goto Error
;
956 cmsCloseProfile(hEmpty
);
960 // Create profile from disk file
961 cmsHPROFILE CMSEXPORT
cmsOpenProfileFromFileTHR(cmsContext ContextID
, const char *lpFileName
, const char *sAccess
)
963 _cmsICCPROFILE
* NewIcc
;
964 cmsHPROFILE hEmpty
= cmsCreateProfilePlaceholder(ContextID
);
966 if (hEmpty
== NULL
) return NULL
;
968 NewIcc
= (_cmsICCPROFILE
*) hEmpty
;
970 NewIcc
->IOhandler
= cmsOpenIOhandlerFromFile(ContextID
, lpFileName
, sAccess
);
971 if (NewIcc
->IOhandler
== NULL
) goto Error
;
973 if (*sAccess
== 'W' || *sAccess
== 'w') {
975 NewIcc
-> IsWrite
= TRUE
;
980 if (!_cmsReadHeader(NewIcc
)) goto Error
;
984 cmsCloseProfile(hEmpty
);
989 cmsHPROFILE CMSEXPORT
cmsOpenProfileFromFile(const char *ICCProfile
, const char *sAccess
)
991 return cmsOpenProfileFromFileTHR(NULL
, ICCProfile
, sAccess
);
995 cmsHPROFILE CMSEXPORT
cmsOpenProfileFromStreamTHR(cmsContext ContextID
, FILE* ICCProfile
, const char *sAccess
)
997 _cmsICCPROFILE
* NewIcc
;
998 cmsHPROFILE hEmpty
= cmsCreateProfilePlaceholder(ContextID
);
1000 if (hEmpty
== NULL
) return NULL
;
1002 NewIcc
= (_cmsICCPROFILE
*) hEmpty
;
1004 NewIcc
->IOhandler
= cmsOpenIOhandlerFromStream(ContextID
, ICCProfile
);
1005 if (NewIcc
->IOhandler
== NULL
) goto Error
;
1007 if (*sAccess
== 'w') {
1009 NewIcc
-> IsWrite
= TRUE
;
1013 if (!_cmsReadHeader(NewIcc
)) goto Error
;
1017 cmsCloseProfile(hEmpty
);
1022 cmsHPROFILE CMSEXPORT
cmsOpenProfileFromStream(FILE* ICCProfile
, const char *sAccess
)
1024 return cmsOpenProfileFromStreamTHR(NULL
, ICCProfile
, sAccess
);
1028 // Open from memory block
1029 cmsHPROFILE CMSEXPORT
cmsOpenProfileFromMemTHR(cmsContext ContextID
, const void* MemPtr
, cmsUInt32Number dwSize
)
1031 _cmsICCPROFILE
* NewIcc
;
1034 hEmpty
= cmsCreateProfilePlaceholder(ContextID
);
1035 if (hEmpty
== NULL
) return NULL
;
1037 NewIcc
= (_cmsICCPROFILE
*) hEmpty
;
1039 // Ok, in this case const void* is casted to void* just because open IO handler
1040 // shares read and writting modes. Don't abuse this feature!
1041 NewIcc
->IOhandler
= cmsOpenIOhandlerFromMem(ContextID
, (void*) MemPtr
, dwSize
, "r");
1042 if (NewIcc
->IOhandler
== NULL
) goto Error
;
1044 if (!_cmsReadHeader(NewIcc
)) goto Error
;
1049 cmsCloseProfile(hEmpty
);
1053 cmsHPROFILE CMSEXPORT
cmsOpenProfileFromMem(const void* MemPtr
, cmsUInt32Number dwSize
)
1055 return cmsOpenProfileFromMemTHR(NULL
, MemPtr
, dwSize
);
1060 // Dump tag contents. If the profile is being modified, untouched tags are copied from FileOrig
1062 cmsBool
SaveTags(_cmsICCPROFILE
* Icc
, _cmsICCPROFILE
* FileOrig
)
1064 cmsUInt8Number
* Data
;
1066 cmsUInt32Number Begin
;
1067 cmsIOHANDLER
* io
= Icc
->IOhandler
;
1068 cmsTagDescriptor
* TagDescriptor
;
1069 cmsTagTypeSignature TypeBase
;
1070 cmsTagTypeSignature Type
;
1071 cmsTagTypeHandler
* TypeHandler
;
1072 cmsFloat64Number Version
= cmsGetProfileVersion((cmsHPROFILE
) Icc
);
1073 cmsTagTypeHandler LocalTypeHandler
;
1075 for (i
=0; i
< Icc
-> TagCount
; i
++) {
1077 if (Icc
->TagNames
[i
] == 0) continue;
1079 // Linked tags are not written
1080 if (Icc
->TagLinked
[i
] != (cmsTagSignature
) 0) continue;
1082 Icc
-> TagOffsets
[i
] = Begin
= io
->UsedSpace
;
1084 Data
= (cmsUInt8Number
*) Icc
-> TagPtrs
[i
];
1088 // Reach here if we are copying a tag from a disk-based ICC profile which has not been modified by user.
1089 // In this case a blind copy of the block data is performed
1090 if (FileOrig
!= NULL
&& Icc
-> TagOffsets
[i
]) {
1092 cmsUInt32Number TagSize
= FileOrig
-> TagSizes
[i
];
1093 cmsUInt32Number TagOffset
= FileOrig
-> TagOffsets
[i
];
1096 if (!FileOrig
->IOhandler
->Seek(FileOrig
->IOhandler
, TagOffset
)) return FALSE
;
1098 Mem
= _cmsMalloc(Icc
->ContextID
, TagSize
);
1099 if (Mem
== NULL
) return FALSE
;
1101 if (FileOrig
->IOhandler
->Read(FileOrig
->IOhandler
, Mem
, TagSize
, 1) != 1) return FALSE
;
1102 if (!io
->Write(io
, TagSize
, Mem
)) return FALSE
;
1103 _cmsFree(Icc
->ContextID
, Mem
);
1105 Icc
-> TagSizes
[i
] = (io
->UsedSpace
- Begin
);
1108 // Align to 32 bit boundary.
1109 if (! _cmsWriteAlignment(io
))
1117 // Should this tag be saved as RAW? If so, tagsizes should be specified in advance (no further cooking is done)
1118 if (Icc
->TagSaveAsRaw
[i
]) {
1120 if (io
-> Write(io
, Icc
->TagSizes
[i
], Data
) != 1) return FALSE
;
1124 // Search for support on this tag
1125 TagDescriptor
= _cmsGetTagDescriptor(Icc
-> TagNames
[i
]);
1126 if (TagDescriptor
== NULL
) continue; // Unsupported, ignore it
1128 if (TagDescriptor
->DecideType
!= NULL
) {
1130 Type
= TagDescriptor
->DecideType(Version
, Data
);
1134 Type
= TagDescriptor
->SupportedTypes
[0];
1137 TypeHandler
= _cmsGetTagTypeHandler(Type
);
1139 if (TypeHandler
== NULL
) {
1140 cmsSignalError(Icc
->ContextID
, cmsERROR_INTERNAL
, "(Internal) no handler for tag %x", Icc
-> TagNames
[i
]);
1144 TypeBase
= TypeHandler
->Signature
;
1145 if (!_cmsWriteTypeBase(io
, TypeBase
))
1148 LocalTypeHandler
= *TypeHandler
;
1149 LocalTypeHandler
.ContextID
= Icc
->ContextID
;
1150 LocalTypeHandler
.ICCVersion
= Icc
->Version
;
1151 if (!LocalTypeHandler
.WritePtr(&LocalTypeHandler
, io
, Data
, TagDescriptor
->ElemCount
)) {
1155 _cmsTagSignature2String(String
, (cmsTagSignature
) TypeBase
);
1156 cmsSignalError(Icc
->ContextID
, cmsERROR_WRITE
, "Couldn't write type '%s'", String
);
1162 Icc
-> TagSizes
[i
] = (io
->UsedSpace
- Begin
);
1164 // Align to 32 bit boundary.
1165 if (! _cmsWriteAlignment(io
))
1174 // Fill the offset and size fields for all linked tags
1176 cmsBool
SetLinks( _cmsICCPROFILE
* Icc
)
1180 for (i
=0; i
< Icc
-> TagCount
; i
++) {
1182 cmsTagSignature lnk
= Icc
->TagLinked
[i
];
1183 if (lnk
!= (cmsTagSignature
) 0) {
1185 int j
= _cmsSearchTag(Icc
, lnk
, FALSE
);
1188 Icc
->TagOffsets
[i
] = Icc
->TagOffsets
[j
];
1189 Icc
->TagSizes
[i
] = Icc
->TagSizes
[j
];
1198 // Low-level save to IOHANDLER. It returns the number of bytes used to
1199 // store the profile, or zero on error. io may be NULL and in this case
1200 // no data is written--only sizes are calculated
1201 cmsUInt32Number CMSEXPORT
cmsSaveProfileToIOhandler(cmsHPROFILE hProfile
, cmsIOHANDLER
* io
)
1203 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1204 _cmsICCPROFILE Keep
;
1205 cmsIOHANDLER
* PrevIO
;
1206 cmsUInt32Number UsedSpace
;
1207 cmsContext ContextID
;
1209 memmove(&Keep
, Icc
, sizeof(_cmsICCPROFILE
));
1211 ContextID
= cmsGetProfileContextID(hProfile
);
1212 PrevIO
= Icc
->IOhandler
= cmsOpenIOhandlerFromNULL(ContextID
);
1213 if (PrevIO
== NULL
) return 0;
1215 // Pass #1 does compute offsets
1217 if (!_cmsWriteHeader(Icc
, 0)) return 0;
1218 if (!SaveTags(Icc
, &Keep
)) return 0;
1220 UsedSpace
= PrevIO
->UsedSpace
;
1222 // Pass #2 does save to iohandler
1225 Icc
->IOhandler
= io
;
1226 if (!SetLinks(Icc
)) goto CleanUp
;
1227 if (!_cmsWriteHeader(Icc
, UsedSpace
)) goto CleanUp
;
1228 if (!SaveTags(Icc
, &Keep
)) goto CleanUp
;
1231 memmove(Icc
, &Keep
, sizeof(_cmsICCPROFILE
));
1232 if (!cmsCloseIOhandler(PrevIO
)) return 0;
1238 cmsCloseIOhandler(PrevIO
);
1239 memmove(Icc
, &Keep
, sizeof(_cmsICCPROFILE
));
1244 // Low-level save to disk.
1245 cmsBool CMSEXPORT
cmsSaveProfileToFile(cmsHPROFILE hProfile
, const char* FileName
)
1247 cmsContext ContextID
= cmsGetProfileContextID(hProfile
);
1248 cmsIOHANDLER
* io
= cmsOpenIOhandlerFromFile(ContextID
, FileName
, "w");
1251 if (io
== NULL
) return FALSE
;
1253 rc
= (cmsSaveProfileToIOhandler(hProfile
, io
) != 0);
1254 rc
&= cmsCloseIOhandler(io
);
1256 if (rc
== FALSE
) { // remove() is C99 per 7.19.4.1
1257 remove(FileName
); // We have to IGNORE return value in this case
1262 // Same as anterior, but for streams
1263 cmsBool CMSEXPORT
cmsSaveProfileToStream(cmsHPROFILE hProfile
, FILE* Stream
)
1266 cmsContext ContextID
= cmsGetProfileContextID(hProfile
);
1267 cmsIOHANDLER
* io
= cmsOpenIOhandlerFromStream(ContextID
, Stream
);
1269 if (io
== NULL
) return FALSE
;
1271 rc
= (cmsSaveProfileToIOhandler(hProfile
, io
) != 0);
1272 rc
&= cmsCloseIOhandler(io
);
1278 // Same as anterior, but for memory blocks. In this case, a NULL as MemPtr means calculate needed space only
1279 cmsBool CMSEXPORT
cmsSaveProfileToMem(cmsHPROFILE hProfile
, void *MemPtr
, cmsUInt32Number
* BytesNeeded
)
1283 cmsContext ContextID
= cmsGetProfileContextID(hProfile
);
1285 // Should we just calculate the needed space?
1286 if (MemPtr
== NULL
) {
1288 *BytesNeeded
= cmsSaveProfileToIOhandler(hProfile
, NULL
);
1292 // That is a real write operation
1293 io
= cmsOpenIOhandlerFromMem(ContextID
, MemPtr
, *BytesNeeded
, "w");
1294 if (io
== NULL
) return FALSE
;
1296 rc
= (cmsSaveProfileToIOhandler(hProfile
, io
) != 0);
1297 rc
&= cmsCloseIOhandler(io
);
1304 // Closes a profile freeing any involved resources
1305 cmsBool CMSEXPORT
cmsCloseProfile(cmsHPROFILE hProfile
)
1307 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1311 if (!Icc
) return FALSE
;
1313 // Was open in write mode?
1314 if (Icc
->IsWrite
) {
1316 Icc
->IsWrite
= FALSE
; // Assure no further writting
1317 rc
&= cmsSaveProfileToFile(hProfile
, Icc
->IOhandler
->PhysicalFile
);
1320 for (i
=0; i
< Icc
-> TagCount
; i
++) {
1322 if (Icc
-> TagPtrs
[i
]) {
1324 cmsTagTypeHandler
* TypeHandler
= Icc
->TagTypeHandlers
[i
];
1326 if (TypeHandler
!= NULL
) {
1327 cmsTagTypeHandler LocalTypeHandler
= *TypeHandler
;
1329 LocalTypeHandler
.ContextID
= Icc
->ContextID
; // As an additional parameters
1330 LocalTypeHandler
.ICCVersion
= Icc
->Version
;
1331 LocalTypeHandler
.FreePtr(&LocalTypeHandler
, Icc
-> TagPtrs
[i
]);
1334 _cmsFree(Icc
->ContextID
, Icc
->TagPtrs
[i
]);
1338 if (Icc
->IOhandler
!= NULL
) {
1339 rc
&= cmsCloseIOhandler(Icc
->IOhandler
);
1342 _cmsFree(Icc
->ContextID
, Icc
); // Free placeholder memory
1348 // -------------------------------------------------------------------------------------------------------------------
1351 // Returns TRUE if a given tag is supported by a plug-in
1353 cmsBool
IsTypeSupported(cmsTagDescriptor
* TagDescriptor
, cmsTagTypeSignature Type
)
1355 cmsUInt32Number i
, nMaxTypes
;
1357 nMaxTypes
= TagDescriptor
->nSupportedTypes
;
1358 if (nMaxTypes
>= MAX_TYPES_IN_LCMS_PLUGIN
)
1359 nMaxTypes
= MAX_TYPES_IN_LCMS_PLUGIN
;
1361 for (i
=0; i
< nMaxTypes
; i
++) {
1362 if (Type
== TagDescriptor
->SupportedTypes
[i
]) return TRUE
;
1369 // That's the main read function
1370 void* CMSEXPORT
cmsReadTag(cmsHPROFILE hProfile
, cmsTagSignature sig
)
1372 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1373 cmsIOHANDLER
* io
= Icc
->IOhandler
;
1374 cmsTagTypeHandler
* TypeHandler
;
1375 cmsTagTypeHandler LocalTypeHandler
;
1376 cmsTagDescriptor
* TagDescriptor
;
1377 cmsTagTypeSignature BaseType
;
1378 cmsUInt32Number Offset
, TagSize
;
1379 cmsUInt32Number ElemCount
;
1382 n
= _cmsSearchTag(Icc
, sig
, TRUE
);
1383 if (n
< 0) return NULL
; // Not found, return NULL
1386 // If the element is already in memory, return the pointer
1387 if (Icc
-> TagPtrs
[n
]) {
1389 if (Icc
->TagSaveAsRaw
[n
]) return NULL
; // We don't support read raw tags as cooked
1390 return Icc
-> TagPtrs
[n
];
1393 // We need to read it. Get the offset and size to the file
1394 Offset
= Icc
-> TagOffsets
[n
];
1395 TagSize
= Icc
-> TagSizes
[n
];
1397 // Seek to its location
1398 if (!io
-> Seek(io
, Offset
))
1401 // Search for support on this tag
1402 TagDescriptor
= _cmsGetTagDescriptor(sig
);
1403 if (TagDescriptor
== NULL
) return NULL
; // Unsupported.
1405 // if supported, get type and check if in list
1406 BaseType
= _cmsReadTypeBase(io
);
1407 if (BaseType
== 0) return NULL
;
1409 if (!IsTypeSupported(TagDescriptor
, BaseType
)) return NULL
;
1411 TagSize
-= 8; // Alredy read by the type base logic
1414 TypeHandler
= _cmsGetTagTypeHandler(BaseType
);
1415 if (TypeHandler
== NULL
) return NULL
;
1416 LocalTypeHandler
= *TypeHandler
;
1420 Icc
-> TagTypeHandlers
[n
] = TypeHandler
;
1422 LocalTypeHandler
.ContextID
= Icc
->ContextID
;
1423 LocalTypeHandler
.ICCVersion
= Icc
->Version
;
1424 Icc
-> TagPtrs
[n
] = LocalTypeHandler
.ReadPtr(&LocalTypeHandler
, io
, &ElemCount
, TagSize
);
1426 // The tag type is supported, but something wrong happend and we cannot read the tag.
1427 // let know the user about this (although it is just a warning)
1428 if (Icc
-> TagPtrs
[n
] == NULL
) {
1432 _cmsTagSignature2String(String
, sig
);
1433 cmsSignalError(Icc
->ContextID
, cmsERROR_CORRUPTION_DETECTED
, "Corrupted tag '%s'", String
);
1437 // This is a weird error that may be a symptom of something more serious, the number of
1438 // stored item is actually less than the number of required elements.
1439 if (ElemCount
< TagDescriptor
->ElemCount
) {
1443 _cmsTagSignature2String(String
, sig
);
1444 cmsSignalError(Icc
->ContextID
, cmsERROR_CORRUPTION_DETECTED
, "'%s' Inconsistent number of items: expected %d, got %d",
1445 String
, TagDescriptor
->ElemCount
, ElemCount
);
1450 return Icc
-> TagPtrs
[n
];
1454 // Get true type of data
1455 cmsTagTypeSignature
_cmsGetTagTrueType(cmsHPROFILE hProfile
, cmsTagSignature sig
)
1457 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1458 cmsTagTypeHandler
* TypeHandler
;
1461 // Search for given tag in ICC profile directory
1462 n
= _cmsSearchTag(Icc
, sig
, TRUE
);
1463 if (n
< 0) return (cmsTagTypeSignature
) 0; // Not found, return NULL
1465 // Get the handler. The true type is there
1466 TypeHandler
= Icc
-> TagTypeHandlers
[n
];
1467 return TypeHandler
->Signature
;
1471 // Write a single tag. This just keeps track of the tak into a list of "to be written". If the tag is already
1472 // in that list, the previous version is deleted.
1473 cmsBool CMSEXPORT
cmsWriteTag(cmsHPROFILE hProfile
, cmsTagSignature sig
, const void* data
)
1475 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1476 cmsTagTypeHandler
* TypeHandler
= NULL
;
1477 cmsTagTypeHandler LocalTypeHandler
;
1478 cmsTagDescriptor
* TagDescriptor
= NULL
;
1479 cmsTagTypeSignature Type
;
1481 cmsFloat64Number Version
;
1482 char TypeString
[5], SigString
[5];
1487 i
= _cmsSearchTag(Icc
, sig
, FALSE
);
1489 Icc
->TagNames
[i
] = (cmsTagSignature
) 0;
1490 // Unsupported by now, reserved for future ampliations (delete)
1494 i
= _cmsSearchTag(Icc
, sig
, FALSE
);
1497 if (Icc
-> TagPtrs
[i
] != NULL
) {
1499 // Already exists. Free previous version
1500 if (Icc
->TagSaveAsRaw
[i
]) {
1501 _cmsFree(Icc
->ContextID
, Icc
->TagPtrs
[i
]);
1504 TypeHandler
= Icc
->TagTypeHandlers
[i
];
1506 if (TypeHandler
!= NULL
) {
1508 LocalTypeHandler
= *TypeHandler
;
1509 LocalTypeHandler
.ContextID
= Icc
->ContextID
; // As an additional parameter
1510 LocalTypeHandler
.ICCVersion
= Icc
->Version
;
1511 LocalTypeHandler
.FreePtr(&LocalTypeHandler
, Icc
-> TagPtrs
[i
]);
1518 i
= Icc
-> TagCount
;
1520 if (i
>= MAX_TABLE_TAG
) {
1521 cmsSignalError(Icc
->ContextID
, cmsERROR_RANGE
, "Too many tags (%d)", MAX_TABLE_TAG
);
1529 Icc
->TagSaveAsRaw
[i
] = FALSE
;
1531 // This is not a link
1532 Icc
->TagLinked
[i
] = (cmsTagSignature
) 0;
1534 // Get information about the TAG.
1535 TagDescriptor
= _cmsGetTagDescriptor(sig
);
1536 if (TagDescriptor
== NULL
){
1537 cmsSignalError(Icc
->ContextID
, cmsERROR_UNKNOWN_EXTENSION
, "Unsupported tag '%x'", sig
);
1542 // Now we need to know which type to use. It depends on the version.
1543 Version
= cmsGetProfileVersion(hProfile
);
1545 if (TagDescriptor
->DecideType
!= NULL
) {
1547 // Let the tag descriptor to decide the type base on depending on
1548 // the data. This is useful for example on parametric curves, where
1549 // curves specified by a table cannot be saved as parametric and needs
1550 // to be casted to single v2-curves, even on v4 profiles.
1552 Type
= TagDescriptor
->DecideType(Version
, data
);
1557 Type
= TagDescriptor
->SupportedTypes
[0];
1560 // Does the tag support this type?
1561 if (!IsTypeSupported(TagDescriptor
, Type
)) {
1563 _cmsTagSignature2String(TypeString
, (cmsTagSignature
) Type
);
1564 _cmsTagSignature2String(SigString
, sig
);
1566 cmsSignalError(Icc
->ContextID
, cmsERROR_UNKNOWN_EXTENSION
, "Unsupported type '%s' for tag '%s'", TypeString
, SigString
);
1570 // Does we have a handler for this type?
1571 TypeHandler
= _cmsGetTagTypeHandler(Type
);
1572 if (TypeHandler
== NULL
) {
1574 _cmsTagSignature2String(TypeString
, (cmsTagSignature
) Type
);
1575 _cmsTagSignature2String(SigString
, sig
);
1577 cmsSignalError(Icc
->ContextID
, cmsERROR_UNKNOWN_EXTENSION
, "Unsupported type '%s' for tag '%s'", TypeString
, SigString
);
1578 return FALSE
; // Should never happen
1582 // Fill fields on icc structure
1583 Icc
->TagTypeHandlers
[i
] = TypeHandler
;
1584 Icc
->TagNames
[i
] = sig
;
1585 Icc
->TagSizes
[i
] = 0;
1586 Icc
->TagOffsets
[i
] = 0;
1588 LocalTypeHandler
= *TypeHandler
;
1589 LocalTypeHandler
.ContextID
= Icc
->ContextID
;
1590 LocalTypeHandler
.ICCVersion
= Icc
->Version
;
1591 Icc
->TagPtrs
[i
] = LocalTypeHandler
.DupPtr(&LocalTypeHandler
, data
, TagDescriptor
->ElemCount
);
1593 if (Icc
->TagPtrs
[i
] == NULL
) {
1595 _cmsTagSignature2String(TypeString
, (cmsTagSignature
) Type
);
1596 _cmsTagSignature2String(SigString
, sig
);
1597 cmsSignalError(Icc
->ContextID
, cmsERROR_CORRUPTION_DETECTED
, "Malformed struct in type '%s' for tag '%s'", TypeString
, SigString
);
1605 // Read and write raw data. The only way those function would work and keep consistence with normal read and write
1606 // is to do an additional step of serialization. That means, readRaw would issue a normal read and then convert the obtained
1607 // data to raw bytes by using the "write" serialization logic. And vice-versa. I know this may end in situations where
1608 // raw data written does not exactly correspond with the raw data proposed to cmsWriteRaw data, but this approach allows
1609 // to write a tag as raw data and the read it as handled.
1611 cmsInt32Number CMSEXPORT
cmsReadRawTag(cmsHPROFILE hProfile
, cmsTagSignature sig
, void* data
, cmsUInt32Number BufferSize
)
1613 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1616 cmsIOHANDLER
* MemIO
;
1617 cmsTagTypeHandler
* TypeHandler
= NULL
;
1618 cmsTagTypeHandler LocalTypeHandler
;
1619 cmsTagDescriptor
* TagDescriptor
= NULL
;
1621 cmsUInt32Number Offset
, TagSize
;
1623 // Search for given tag in ICC profile directory
1624 i
= _cmsSearchTag(Icc
, sig
, TRUE
);
1625 if (i
< 0) return 0; // Not found, return 0
1627 // It is already read?
1628 if (Icc
-> TagPtrs
[i
] == NULL
) {
1630 // No yet, get original position
1631 Offset
= Icc
->TagOffsets
[i
];
1632 TagSize
= Icc
->TagSizes
[i
];
1634 // read the data directly, don't keep copy
1637 if (BufferSize
< TagSize
)
1638 TagSize
= BufferSize
;
1640 if (!Icc
->IOhandler
->Seek(Icc
->IOhandler
, Offset
)) return 0;
1641 if (!Icc
->IOhandler
->Read(Icc
->IOhandler
, data
, 1, TagSize
)) return 0;
1646 return Icc
->TagSizes
[i
];
1649 // The data has been already read, or written. But wait!, maybe the user choosed to save as
1650 // raw data. In this case, return the raw data directly
1651 if (Icc
->TagSaveAsRaw
[i
]) {
1655 TagSize
= Icc
->TagSizes
[i
];
1656 if (BufferSize
< TagSize
)
1657 TagSize
= BufferSize
;
1659 memmove(data
, Icc
->TagPtrs
[i
], TagSize
);
1664 return Icc
->TagSizes
[i
];
1667 // Already readed, or previously set by cmsWriteTag(). We need to serialize that
1668 // data to raw in order to maintain consistency.
1669 Object
= cmsReadTag(hProfile
, sig
);
1670 if (Object
== NULL
) return 0;
1672 // Now we need to serialize to a memory block: just use a memory iohandler
1675 MemIO
= cmsOpenIOhandlerFromNULL(cmsGetProfileContextID(hProfile
));
1677 MemIO
= cmsOpenIOhandlerFromMem(cmsGetProfileContextID(hProfile
), data
, BufferSize
, "w");
1679 if (MemIO
== NULL
) return 0;
1681 // Obtain type handling for the tag
1682 TypeHandler
= Icc
->TagTypeHandlers
[i
];
1683 TagDescriptor
= _cmsGetTagDescriptor(sig
);
1684 if (TagDescriptor
== NULL
) {
1685 cmsCloseIOhandler(MemIO
);
1689 // FIXME: No handling for TypeHandler == NULL here?
1691 LocalTypeHandler
= *TypeHandler
;
1692 LocalTypeHandler
.ContextID
= Icc
->ContextID
;
1693 LocalTypeHandler
.ICCVersion
= Icc
->Version
;
1695 if (!_cmsWriteTypeBase(MemIO
, TypeHandler
->Signature
)) {
1696 cmsCloseIOhandler(MemIO
);
1700 if (!LocalTypeHandler
.WritePtr(&LocalTypeHandler
, MemIO
, Object
, TagDescriptor
->ElemCount
)) {
1701 cmsCloseIOhandler(MemIO
);
1705 // Get Size and close
1706 rc
= MemIO
->Tell(MemIO
);
1707 cmsCloseIOhandler(MemIO
); // Ignore return code this time
1712 // Similar to the anterior. This function allows to write directly to the ICC profile any data, without
1713 // checking anything. As a rule, mixing Raw with cooked doesn't work, so writting a tag as raw and then reading
1714 // it as cooked without serializing does result into an error. If that is wha you want, you will need to dump
1715 // the profile to memry or disk and then reopen it.
1716 cmsBool CMSEXPORT
cmsWriteRawTag(cmsHPROFILE hProfile
, cmsTagSignature sig
, const void* data
, cmsUInt32Number Size
)
1718 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1721 if (!_cmsNewTag(Icc
, sig
, &i
)) return FALSE
;
1723 // Mark the tag as being written as RAW
1724 Icc
->TagSaveAsRaw
[i
] = TRUE
;
1725 Icc
->TagNames
[i
] = sig
;
1726 Icc
->TagLinked
[i
] = (cmsTagSignature
) 0;
1728 // Keep a copy of the block
1729 Icc
->TagPtrs
[i
] = _cmsDupMem(Icc
->ContextID
, data
, Size
);
1730 Icc
->TagSizes
[i
] = Size
;
1735 // Using this function you can collapse several tag entries to the same block in the profile
1736 cmsBool CMSEXPORT
cmsLinkTag(cmsHPROFILE hProfile
, cmsTagSignature sig
, cmsTagSignature dest
)
1738 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1741 if (!_cmsNewTag(Icc
, sig
, &i
)) return FALSE
;
1743 // Keep necessary information
1744 Icc
->TagSaveAsRaw
[i
] = FALSE
;
1745 Icc
->TagNames
[i
] = sig
;
1746 Icc
->TagLinked
[i
] = dest
;
1748 Icc
->TagPtrs
[i
] = NULL
;
1749 Icc
->TagSizes
[i
] = 0;
1750 Icc
->TagOffsets
[i
] = 0;
1756 // Returns the tag linked to sig, in the case two tags are sharing same resource
1757 cmsTagSignature CMSEXPORT
cmsTagLinkedTo(cmsHPROFILE hProfile
, cmsTagSignature sig
)
1759 _cmsICCPROFILE
* Icc
= (_cmsICCPROFILE
*) hProfile
;
1762 // Search for given tag in ICC profile directory
1763 i
= _cmsSearchTag(Icc
, sig
, FALSE
);
1764 if (i
< 0) return (cmsTagSignature
) 0; // Not found, return 0
1766 return Icc
-> TagLinked
[i
];