grub2: bring back build of aros-side grub2 tools
[AROS.git] / workbench / libs / lcms2 / src / cmsio0.c
blobb7aea72979ff9c9913b16ed1cbf52e5576a5344a
1 //---------------------------------------------------------------------------------
2 //
3 // Little Color Management System
4 // Copyright (c) 1998-2012 Marti Maria Saguer
5 //
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.
43 typedef struct {
44 cmsUInt32Number Pointer; // Points to current location
45 } FILENULL;
47 static
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;
54 return count;
56 cmsUNUSED_PARAMETER(Buffer);
59 static
60 cmsBool NULLSeek(cmsIOHANDLER* iohandler, cmsUInt32Number offset)
62 FILENULL* ResData = (FILENULL*) iohandler ->stream;
64 ResData ->Pointer = offset;
65 return TRUE;
68 static
69 cmsUInt32Number NULLTell(cmsIOHANDLER* iohandler)
71 FILENULL* ResData = (FILENULL*) iohandler ->stream;
72 return ResData -> Pointer;
75 static
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;
84 return TRUE;
86 cmsUNUSED_PARAMETER(Ptr);
89 static
90 cmsBool NULLClose(cmsIOHANDLER* iohandler)
92 FILENULL* ResData = (FILENULL*) iohandler ->stream;
94 _cmsFree(iohandler ->ContextID, ResData);
95 _cmsFree(iohandler ->ContextID, iohandler);
96 return TRUE;
99 // The NULL IOhandler creator
100 cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromNULL(cmsContext ContextID)
102 struct _cms_io_handler* iohandler = NULL;
103 FILENULL* fm = 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;
111 fm ->Pointer = 0;
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;
125 return iohandler;
127 Error:
128 if (iohandler) _cmsFree(ContextID, iohandler);
129 return NULL;
134 // Memory-based stream --------------------------------------------------------------
136 // Those functions implements an iohandler which takes a block of memory as storage medium.
138 typedef struct {
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
144 } FILEMEM;
146 static
147 cmsUInt32Number MemoryRead(struct _cms_io_handler* iohandler, void *Buffer, cmsUInt32Number size, cmsUInt32Number count)
149 FILEMEM* ResData = (FILEMEM*) iohandler ->stream;
150 cmsUInt8Number* Ptr;
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);
157 return 0;
160 Ptr = ResData -> Block;
161 Ptr += ResData -> Pointer;
162 memmove(Buffer, Ptr, len);
163 ResData -> Pointer += len;
165 return count;
168 // SEEK_CUR is assumed
169 static
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");
176 return FALSE;
179 ResData ->Pointer = offset;
180 return TRUE;
183 // Tell for memory
184 static
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.
195 static
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;
216 return TRUE;
220 static
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);
233 return TRUE;
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;
242 FILEMEM* fm = NULL;
244 _cmsAssert(AccessMode != NULL);
246 iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER));
247 if (iohandler == NULL) return NULL;
249 switch (*AccessMode) {
251 case 'r':
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");
257 goto Error;
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);
266 return NULL;
270 memmove(fm->Block, Buffer, size);
271 fm ->FreeBlockOnClose = TRUE;
272 fm ->Size = size;
273 fm ->Pointer = 0;
274 iohandler -> ReportedSize = size;
275 break;
277 case 'w':
278 fm = (FILEMEM*) _cmsMallocZero(ContextID, sizeof(FILEMEM));
279 if (fm == NULL) goto Error;
281 fm ->Block = (cmsUInt8Number*) Buffer;
282 fm ->FreeBlockOnClose = FALSE;
283 fm ->Size = size;
284 fm ->Pointer = 0;
285 iohandler -> ReportedSize = 0;
286 break;
288 default:
289 cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown access mode '%c'", *AccessMode);
290 return NULL;
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;
304 return iohandler;
306 Error:
307 if (fm) _cmsFree(ContextID, fm);
308 if (iohandler) _cmsFree(ContextID, iohandler);
309 return NULL;
312 // File-based stream -------------------------------------------------------
314 // Read count elements of size bytes each. Return number of elements read
315 static
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);
322 return 0;
325 return nReaded;
328 // Postion file pointer in the file
329 static
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");
335 return FALSE;
338 return TRUE;
341 // Returns file pointer position
342 static
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
349 static
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);
358 // Closes the file
359 static
360 cmsBool FileClose(cmsIOHANDLER* iohandler)
362 if (fclose((FILE*) iohandler ->stream) != 0) return FALSE;
363 _cmsFree(iohandler ->ContextID, iohandler);
364 return TRUE;
367 // Create a iohandler for disk based files.
368 cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromFile(cmsContext ContextID, const char* FileName, const char* AccessMode)
370 cmsIOHANDLER* iohandler = NULL;
371 FILE* fm = 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) {
381 case 'r':
382 fm = fopen(FileName, "rb");
383 if (fm == NULL) {
384 _cmsFree(ContextID, iohandler);
385 cmsSignalError(ContextID, cmsERROR_FILE, "File '%s' not found", FileName);
386 return NULL;
388 iohandler -> ReportedSize = cmsfilelength(fm);
389 break;
391 case 'w':
392 fm = fopen(FileName, "wb");
393 if (fm == NULL) {
394 _cmsFree(ContextID, iohandler);
395 cmsSignalError(ContextID, cmsERROR_FILE, "Couldn't create '%s'", FileName);
396 return NULL;
398 iohandler -> ReportedSize = 0;
399 break;
401 default:
402 _cmsFree(ContextID, iohandler);
403 cmsSignalError(ContextID, cmsERROR_FILE, "Unknown access mode '%c'", *AccessMode);
404 return NULL;
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;
421 return iohandler;
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;
444 return iohandler;
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;
466 // Set it to empty
467 Icc -> TagCount = 0;
469 // Set default version
470 Icc ->Version = 0x02100000;
472 // Set creation date/time
473 memmove(&Icc ->Created, gmtime(&now), sizeof(Icc ->Created));
475 // Return the handle
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];
509 static
510 int SearchOneTag(_cmsICCPROFILE* Profile, cmsTagSignature sig)
512 cmsUInt32Number i;
514 for (i=0; i < Profile -> TagCount; i++) {
516 if (sig == Profile -> TagNames[i])
517 return i;
520 return -1;
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)
527 int n;
528 cmsTagSignature LinkedSig;
530 do {
532 // Search for given tag in ICC profile directory
533 n = SearchOneTag(Icc, sig);
534 if (n < 0)
535 return -1; // Not found
537 if (!lFollowLinks)
538 return n; // Found, don't follow links
540 // Is this a linked tag?
541 LinkedSig = Icc ->TagLinked[n];
543 // Yes, follow link
544 if (LinkedSig != (cmsTagSignature) 0) {
545 sig = LinkedSig;
548 } while (LinkedSig != (cmsTagSignature) 0);
550 return n;
554 // Create a new tag entry
556 static
557 cmsBool _cmsNewTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, int* NewPos)
559 int i;
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
565 if (i >= 0) {
566 cmsSignalError(Icc ->ContextID, cmsERROR_ALREADY_DEFINED, "Tag '%x' already exists", sig);
567 return FALSE;
569 else {
571 // New one
573 if (Icc -> TagCount >= MAX_TABLE_TAG) {
574 cmsSignalError(Icc ->ContextID, cmsERROR_RANGE, "Too many tags (%d)", MAX_TABLE_TAG);
575 return FALSE;
578 *NewPos = Icc ->TagCount;
579 Icc -> TagCount++;
582 return TRUE;
586 // Check existance
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)
597 cmsTagEntry Tag;
598 cmsICCHeader Header;
599 cmsUInt32Number i, j;
600 cmsUInt32Number HeaderSize;
601 cmsIOHANDLER* io = Icc ->IOhandler;
602 cmsUInt32Number TagCount;
605 // Read the header
606 if (io -> Read(io, &Header, sizeof(cmsICCHeader), 1) != 1) {
607 return FALSE;
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");
613 return FALSE;
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);
650 return FALSE;
654 // Read tag directory
655 Icc -> TagCount = 0;
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)
665 continue;
667 Icc -> TagNames[Icc ->TagCount] = Tag.sig;
668 Icc -> TagOffsets[Icc ->TagCount] = Tag.offset;
669 Icc -> TagSizes[Icc ->TagCount] = Tag.size;
671 // Search for links
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];
682 Icc ->TagCount++;
685 return TRUE;
688 // Saves profile header
689 cmsBool _cmsWriteHeader(_cmsICCPROFILE* Icc, cmsUInt32Number UsedSpace)
691 cmsICCHeader Header;
692 cmsUInt32Number i;
693 cmsTagEntry Tag;
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);
711 #else
712 Header.platform = (cmsPlatformSignature) _cmsAdjustEndianess32(cmsSigMacintosh);
713 #endif
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);
737 // Dump the header
738 if (!Icc -> IOhandler->Write(Icc->IOhandler, sizeof(cmsICCHeader), &Header)) return FALSE;
740 // Saves Tag directory
742 // Get true count
743 for (i=0; i < Icc -> TagCount; i++) {
744 if (Icc ->TagNames[i] != 0)
745 Count++;
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;
762 return TRUE;
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;
813 return Icc ->model;
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));
850 return TRUE;
853 cmsColorSpaceSignature CMSEXPORT cmsGetPCS(cmsHPROFILE hProfile)
855 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
856 return Icc -> PCS;
859 void CMSEXPORT cmsSetPCS(cmsHPROFILE hProfile, cmsColorSpaceSignature pcs)
861 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
862 Icc -> PCS = pcs;
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
902 static
903 cmsUInt32Number BaseToBase(cmsUInt32Number in, int BaseIn, int BaseOut)
905 char Buff[100];
906 int i, len;
907 cmsUInt32Number out;
909 for (len=0; in > 0 && len < 100; len++) {
911 Buff[len] = (char) (in % BaseIn);
912 in /= BaseIn;
915 for (i=len-1, out=0; i >= 0; --i) {
916 out = out * BaseOut + Buff[i];
919 return out;
922 void CMSEXPORT cmsSetProfileVersion(cmsHPROFILE hProfile, cmsFloat64Number Version)
924 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
926 // 4.2 -> 0x4200000
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;
953 return hEmpty;
955 Error:
956 cmsCloseProfile(hEmpty);
957 return NULL;
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;
977 return hEmpty;
980 if (!_cmsReadHeader(NewIcc)) goto Error;
981 return hEmpty;
983 Error:
984 cmsCloseProfile(hEmpty);
985 return NULL;
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;
1010 return hEmpty;
1013 if (!_cmsReadHeader(NewIcc)) goto Error;
1014 return hEmpty;
1016 Error:
1017 cmsCloseProfile(hEmpty);
1018 return NULL;
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;
1032 cmsHPROFILE hEmpty;
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;
1046 return hEmpty;
1048 Error:
1049 cmsCloseProfile(hEmpty);
1050 return NULL;
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
1061 static
1062 cmsBool SaveTags(_cmsICCPROFILE* Icc, _cmsICCPROFILE* FileOrig)
1064 cmsUInt8Number* Data;
1065 cmsUInt32Number i;
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];
1086 if (!Data) {
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];
1094 void* Mem;
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))
1110 return FALSE;
1113 continue;
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;
1122 else {
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);
1132 else {
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]);
1141 continue;
1144 TypeBase = TypeHandler ->Signature;
1145 if (!_cmsWriteTypeBase(io, TypeBase))
1146 return FALSE;
1148 LocalTypeHandler = *TypeHandler;
1149 LocalTypeHandler.ContextID = Icc ->ContextID;
1150 LocalTypeHandler.ICCVersion = Icc ->Version;
1151 if (!LocalTypeHandler.WritePtr(&LocalTypeHandler, io, Data, TagDescriptor ->ElemCount)) {
1153 char String[5];
1155 _cmsTagSignature2String(String, (cmsTagSignature) TypeBase);
1156 cmsSignalError(Icc ->ContextID, cmsERROR_WRITE, "Couldn't write type '%s'", String);
1157 return FALSE;
1162 Icc -> TagSizes[i] = (io ->UsedSpace - Begin);
1164 // Align to 32 bit boundary.
1165 if (! _cmsWriteAlignment(io))
1166 return FALSE;
1170 return TRUE;
1174 // Fill the offset and size fields for all linked tags
1175 static
1176 cmsBool SetLinks( _cmsICCPROFILE* Icc)
1178 cmsUInt32Number i;
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);
1186 if (j >= 0) {
1188 Icc ->TagOffsets[i] = Icc ->TagOffsets[j];
1189 Icc ->TagSizes[i] = Icc ->TagSizes[j];
1195 return TRUE;
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
1224 if (io != NULL) {
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;
1234 return UsedSpace;
1237 CleanUp:
1238 cmsCloseIOhandler(PrevIO);
1239 memmove(Icc, &Keep, sizeof(_cmsICCPROFILE));
1240 return 0;
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");
1249 cmsBool rc;
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
1259 return rc;
1262 // Same as anterior, but for streams
1263 cmsBool CMSEXPORT cmsSaveProfileToStream(cmsHPROFILE hProfile, FILE* Stream)
1265 cmsBool rc;
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);
1274 return rc;
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)
1281 cmsBool rc;
1282 cmsIOHANDLER* io;
1283 cmsContext ContextID = cmsGetProfileContextID(hProfile);
1285 // Should we just calculate the needed space?
1286 if (MemPtr == NULL) {
1288 *BytesNeeded = cmsSaveProfileToIOhandler(hProfile, NULL);
1289 return TRUE;
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);
1299 return rc;
1304 // Closes a profile freeing any involved resources
1305 cmsBool CMSEXPORT cmsCloseProfile(cmsHPROFILE hProfile)
1307 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
1308 cmsBool rc = TRUE;
1309 cmsUInt32Number i;
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]);
1333 else
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
1344 return rc;
1348 // -------------------------------------------------------------------------------------------------------------------
1351 // Returns TRUE if a given tag is supported by a plug-in
1352 static
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;
1365 return FALSE;
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;
1380 int n;
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))
1399 return NULL;
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
1413 // Get type handler
1414 TypeHandler = _cmsGetTagTypeHandler(BaseType);
1415 if (TypeHandler == NULL) return NULL;
1416 LocalTypeHandler = *TypeHandler;
1419 // Read the tag
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) {
1430 char String[5];
1432 _cmsTagSignature2String(String, sig);
1433 cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "Corrupted tag '%s'", String);
1434 return NULL;
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) {
1441 char String[5];
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);
1449 // Return the data
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;
1459 int n;
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;
1480 int i;
1481 cmsFloat64Number Version;
1482 char TypeString[5], SigString[5];
1485 if (data == NULL) {
1487 i = _cmsSearchTag(Icc, sig, FALSE);
1488 if (i >= 0)
1489 Icc ->TagNames[i] = (cmsTagSignature) 0;
1490 // Unsupported by now, reserved for future ampliations (delete)
1491 return FALSE;
1494 i = _cmsSearchTag(Icc, sig, FALSE);
1495 if (i >=0) {
1497 if (Icc -> TagPtrs[i] != NULL) {
1499 // Already exists. Free previous version
1500 if (Icc ->TagSaveAsRaw[i]) {
1501 _cmsFree(Icc ->ContextID, Icc ->TagPtrs[i]);
1503 else {
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]);
1516 else {
1517 // New one
1518 i = Icc -> TagCount;
1520 if (i >= MAX_TABLE_TAG) {
1521 cmsSignalError(Icc ->ContextID, cmsERROR_RANGE, "Too many tags (%d)", MAX_TABLE_TAG);
1522 return FALSE;
1525 Icc -> TagCount++;
1528 // This is not raw
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);
1538 return FALSE;
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);
1554 else {
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);
1567 return FALSE;
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);
1599 return FALSE;
1602 return TRUE;
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;
1614 void *Object;
1615 int i;
1616 cmsIOHANDLER* MemIO;
1617 cmsTagTypeHandler* TypeHandler = NULL;
1618 cmsTagTypeHandler LocalTypeHandler;
1619 cmsTagDescriptor* TagDescriptor = NULL;
1620 cmsUInt32Number rc;
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
1635 if (data != NULL) {
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;
1643 return TagSize;
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]) {
1653 if (data != NULL) {
1655 TagSize = Icc ->TagSizes[i];
1656 if (BufferSize < TagSize)
1657 TagSize = BufferSize;
1659 memmove(data, Icc ->TagPtrs[i], TagSize);
1661 return 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
1674 if (data == NULL) {
1675 MemIO = cmsOpenIOhandlerFromNULL(cmsGetProfileContextID(hProfile));
1676 } else{
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);
1686 return 0;
1689 // FIXME: No handling for TypeHandler == NULL here?
1690 // Serialize
1691 LocalTypeHandler = *TypeHandler;
1692 LocalTypeHandler.ContextID = Icc ->ContextID;
1693 LocalTypeHandler.ICCVersion = Icc ->Version;
1695 if (!_cmsWriteTypeBase(MemIO, TypeHandler ->Signature)) {
1696 cmsCloseIOhandler(MemIO);
1697 return 0;
1700 if (!LocalTypeHandler.WritePtr(&LocalTypeHandler, MemIO, Object, TagDescriptor ->ElemCount)) {
1701 cmsCloseIOhandler(MemIO);
1702 return 0;
1705 // Get Size and close
1706 rc = MemIO ->Tell(MemIO);
1707 cmsCloseIOhandler(MemIO); // Ignore return code this time
1709 return rc;
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;
1719 int i;
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;
1732 return TRUE;
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;
1739 int i;
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;
1752 return TRUE;
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;
1760 int i;
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];