Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/native/sun/java2d/cmm/lcms/cmsio0.c
38918 views
/*1* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.2*3* This code is free software; you can redistribute it and/or modify it4* under the terms of the GNU General Public License version 2 only, as5* published by the Free Software Foundation. Oracle designates this6* particular file as subject to the "Classpath" exception as provided7* by Oracle in the LICENSE file that accompanied this code.8*9* This code is distributed in the hope that it will be useful, but WITHOUT10* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or11* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License12* version 2 for more details (a copy is included in the LICENSE file that13* accompanied this code).14*15* You should have received a copy of the GNU General Public License version16* 2 along with this work; if not, write to the Free Software Foundation,17* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.18*19* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA20* or visit www.oracle.com if you need additional information or have any21* questions.22*/2324// This file is available under and governed by the GNU General Public25// License version 2 only, as published by the Free Software Foundation.26// However, the following notice accompanied the original version of this27// file:28//29//---------------------------------------------------------------------------------30//31// Little Color Management System32// Copyright (c) 1998-2020 Marti Maria Saguer33//34// Permission is hereby granted, free of charge, to any person obtaining35// a copy of this software and associated documentation files (the "Software"),36// to deal in the Software without restriction, including without limitation37// the rights to use, copy, modify, merge, publish, distribute, sublicense,38// and/or sell copies of the Software, and to permit persons to whom the Software39// is furnished to do so, subject to the following conditions:40//41// The above copyright notice and this permission notice shall be included in42// all copies or substantial portions of the Software.43//44// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,45// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO46// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND47// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE48// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION49// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION50// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.51//52//---------------------------------------------------------------------------------53//5455#include "lcms2_internal.h"5657// Generic I/O, tag dictionary management, profile struct5859// IOhandlers are abstractions used by littleCMS to read from whatever file, stream,60// memory block or any storage. Each IOhandler provides implementations for read,61// write, seek and tell functions. LittleCMS code deals with IO across those objects.62// In this way, is easier to add support for new storage media.6364// NULL stream, for taking care of used space -------------------------------------6566// NULL IOhandler basically does nothing but keep track on how many bytes have been67// written. This is handy when creating profiles, where the file size is needed in the68// header. Then, whole profile is serialized across NULL IOhandler and a second pass69// writes the bytes to the pertinent IOhandler.7071typedef struct {72cmsUInt32Number Pointer; // Points to current location73} FILENULL;7475static76cmsUInt32Number NULLRead(cmsIOHANDLER* iohandler, void *Buffer, cmsUInt32Number size, cmsUInt32Number count)77{78FILENULL* ResData = (FILENULL*) iohandler ->stream;7980cmsUInt32Number len = size * count;81ResData -> Pointer += len;82return count;8384cmsUNUSED_PARAMETER(Buffer);85}8687static88cmsBool NULLSeek(cmsIOHANDLER* iohandler, cmsUInt32Number offset)89{90FILENULL* ResData = (FILENULL*) iohandler ->stream;9192ResData ->Pointer = offset;93return TRUE;94}9596static97cmsUInt32Number NULLTell(cmsIOHANDLER* iohandler)98{99FILENULL* ResData = (FILENULL*) iohandler ->stream;100return ResData -> Pointer;101}102103static104cmsBool NULLWrite(cmsIOHANDLER* iohandler, cmsUInt32Number size, const void *Ptr)105{106FILENULL* ResData = (FILENULL*) iohandler ->stream;107108ResData ->Pointer += size;109if (ResData ->Pointer > iohandler->UsedSpace)110iohandler->UsedSpace = ResData ->Pointer;111112return TRUE;113114cmsUNUSED_PARAMETER(Ptr);115}116117static118cmsBool NULLClose(cmsIOHANDLER* iohandler)119{120FILENULL* ResData = (FILENULL*) iohandler ->stream;121122_cmsFree(iohandler ->ContextID, ResData);123_cmsFree(iohandler ->ContextID, iohandler);124return TRUE;125}126127// The NULL IOhandler creator128cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromNULL(cmsContext ContextID)129{130struct _cms_io_handler* iohandler = NULL;131FILENULL* fm = NULL;132133iohandler = (struct _cms_io_handler*) _cmsMallocZero(ContextID, sizeof(struct _cms_io_handler));134if (iohandler == NULL) return NULL;135136fm = (FILENULL*) _cmsMallocZero(ContextID, sizeof(FILENULL));137if (fm == NULL) goto Error;138139fm ->Pointer = 0;140141iohandler ->ContextID = ContextID;142iohandler ->stream = (void*) fm;143iohandler ->UsedSpace = 0;144iohandler ->ReportedSize = 0;145iohandler ->PhysicalFile[0] = 0;146147iohandler ->Read = NULLRead;148iohandler ->Seek = NULLSeek;149iohandler ->Close = NULLClose;150iohandler ->Tell = NULLTell;151iohandler ->Write = NULLWrite;152153return iohandler;154155Error:156if (iohandler) _cmsFree(ContextID, iohandler);157return NULL;158159}160161162// Memory-based stream --------------------------------------------------------------163164// Those functions implements an iohandler which takes a block of memory as storage medium.165166typedef struct {167cmsUInt8Number* Block; // Points to allocated memory168cmsUInt32Number Size; // Size of allocated memory169cmsUInt32Number Pointer; // Points to current location170int FreeBlockOnClose; // As title171172} FILEMEM;173174static175cmsUInt32Number MemoryRead(struct _cms_io_handler* iohandler, void *Buffer, cmsUInt32Number size, cmsUInt32Number count)176{177FILEMEM* ResData = (FILEMEM*) iohandler ->stream;178cmsUInt8Number* Ptr;179cmsUInt32Number len = size * count;180181if (ResData -> Pointer + len > ResData -> Size){182183len = (ResData -> Size - ResData -> Pointer);184cmsSignalError(iohandler ->ContextID, cmsERROR_READ, "Read from memory error. Got %d bytes, block should be of %d bytes", len, count * size);185return 0;186}187188Ptr = ResData -> Block;189Ptr += ResData -> Pointer;190memmove(Buffer, Ptr, len);191ResData -> Pointer += len;192193return count;194}195196// SEEK_CUR is assumed197static198cmsBool MemorySeek(struct _cms_io_handler* iohandler, cmsUInt32Number offset)199{200FILEMEM* ResData = (FILEMEM*) iohandler ->stream;201202if (offset > ResData ->Size) {203cmsSignalError(iohandler ->ContextID, cmsERROR_SEEK, "Too few data; probably corrupted profile");204return FALSE;205}206207ResData ->Pointer = offset;208return TRUE;209}210211// Tell for memory212static213cmsUInt32Number MemoryTell(struct _cms_io_handler* iohandler)214{215FILEMEM* ResData = (FILEMEM*) iohandler ->stream;216217if (ResData == NULL) return 0;218return ResData -> Pointer;219}220221222// Writes data to memory, also keeps used space for further reference.223static224cmsBool MemoryWrite(struct _cms_io_handler* iohandler, cmsUInt32Number size, const void *Ptr)225{226FILEMEM* ResData = (FILEMEM*) iohandler ->stream;227228if (ResData == NULL) return FALSE; // Housekeeping229230// Check for available space. Clip.231if (ResData->Pointer + size > ResData->Size) {232size = ResData ->Size - ResData->Pointer;233}234235if (size == 0) return TRUE; // Write zero bytes is ok, but does nothing236237memmove(ResData ->Block + ResData ->Pointer, Ptr, size);238ResData ->Pointer += size;239240if (ResData ->Pointer > iohandler->UsedSpace)241iohandler->UsedSpace = ResData ->Pointer;242243return TRUE;244}245246247static248cmsBool MemoryClose(struct _cms_io_handler* iohandler)249{250FILEMEM* ResData = (FILEMEM*) iohandler ->stream;251252if (ResData ->FreeBlockOnClose) {253254if (ResData ->Block) _cmsFree(iohandler ->ContextID, ResData ->Block);255}256257_cmsFree(iohandler ->ContextID, ResData);258_cmsFree(iohandler ->ContextID, iohandler);259260return TRUE;261}262263// Create a iohandler for memory block. AccessMode=='r' assumes the iohandler is going to read, and makes264// a copy of the memory block for letting user to free the memory after invoking open profile. In write265// mode ("w"), Buffer points to the begin of memory block to be written.266cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromMem(cmsContext ContextID, void *Buffer, cmsUInt32Number size, const char* AccessMode)267{268cmsIOHANDLER* iohandler = NULL;269FILEMEM* fm = NULL;270271_cmsAssert(AccessMode != NULL);272273iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER));274if (iohandler == NULL) return NULL;275276switch (*AccessMode) {277278case 'r':279fm = (FILEMEM*) _cmsMallocZero(ContextID, sizeof(FILEMEM));280if (fm == NULL) goto Error;281282if (Buffer == NULL) {283cmsSignalError(ContextID, cmsERROR_READ, "Couldn't read profile from NULL pointer");284goto Error;285}286287fm ->Block = (cmsUInt8Number*) _cmsMalloc(ContextID, size);288if (fm ->Block == NULL) {289290_cmsFree(ContextID, fm);291_cmsFree(ContextID, iohandler);292cmsSignalError(ContextID, cmsERROR_READ, "Couldn't allocate %ld bytes for profile", size);293return NULL;294}295296297memmove(fm->Block, Buffer, size);298fm ->FreeBlockOnClose = TRUE;299fm ->Size = size;300fm ->Pointer = 0;301iohandler -> ReportedSize = size;302break;303304case 'w':305fm = (FILEMEM*) _cmsMallocZero(ContextID, sizeof(FILEMEM));306if (fm == NULL) goto Error;307308fm ->Block = (cmsUInt8Number*) Buffer;309fm ->FreeBlockOnClose = FALSE;310fm ->Size = size;311fm ->Pointer = 0;312iohandler -> ReportedSize = 0;313break;314315default:316cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown access mode '%c'", *AccessMode);317return NULL;318}319320iohandler ->ContextID = ContextID;321iohandler ->stream = (void*) fm;322iohandler ->UsedSpace = 0;323iohandler ->PhysicalFile[0] = 0;324325iohandler ->Read = MemoryRead;326iohandler ->Seek = MemorySeek;327iohandler ->Close = MemoryClose;328iohandler ->Tell = MemoryTell;329iohandler ->Write = MemoryWrite;330331return iohandler;332333Error:334if (fm) _cmsFree(ContextID, fm);335if (iohandler) _cmsFree(ContextID, iohandler);336return NULL;337}338339// File-based stream -------------------------------------------------------340341// Read count elements of size bytes each. Return number of elements read342static343cmsUInt32Number FileRead(cmsIOHANDLER* iohandler, void *Buffer, cmsUInt32Number size, cmsUInt32Number count)344{345cmsUInt32Number nReaded = (cmsUInt32Number) fread(Buffer, size, count, (FILE*) iohandler->stream);346347if (nReaded != count) {348cmsSignalError(iohandler ->ContextID, cmsERROR_FILE, "Read error. Got %d bytes, block should be of %d bytes", nReaded * size, count * size);349return 0;350}351352return nReaded;353}354355// Position file pointer in the file356static357cmsBool FileSeek(cmsIOHANDLER* iohandler, cmsUInt32Number offset)358{359if (fseek((FILE*) iohandler ->stream, (long) offset, SEEK_SET) != 0) {360361cmsSignalError(iohandler ->ContextID, cmsERROR_FILE, "Seek error; probably corrupted file");362return FALSE;363}364365return TRUE;366}367368// Returns file pointer position or 0 on error, which is also a valid position.369static370cmsUInt32Number FileTell(cmsIOHANDLER* iohandler)371{372long t = ftell((FILE*)iohandler ->stream);373if (t == -1L) {374cmsSignalError(iohandler->ContextID, cmsERROR_FILE, "Tell error; probably corrupted file");375return 0;376}377378return (cmsUInt32Number)t;379}380381// Writes data to stream, also keeps used space for further reference. Returns TRUE on success, FALSE on error382static383cmsBool FileWrite(cmsIOHANDLER* iohandler, cmsUInt32Number size, const void* Buffer)384{385if (size == 0) return TRUE; // We allow to write 0 bytes, but nothing is written386387iohandler->UsedSpace += size;388return (fwrite(Buffer, size, 1, (FILE*)iohandler->stream) == 1);389}390391// Closes the file392static393cmsBool FileClose(cmsIOHANDLER* iohandler)394{395if (fclose((FILE*) iohandler ->stream) != 0) return FALSE;396_cmsFree(iohandler ->ContextID, iohandler);397return TRUE;398}399400// Create a iohandler for disk based files.401cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromFile(cmsContext ContextID, const char* FileName, const char* AccessMode)402{403cmsIOHANDLER* iohandler = NULL;404FILE* fm = NULL;405cmsInt32Number fileLen;406407_cmsAssert(FileName != NULL);408_cmsAssert(AccessMode != NULL);409410iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER));411if (iohandler == NULL) return NULL;412413switch (*AccessMode) {414415case 'r':416fm = fopen(FileName, "rb");417if (fm == NULL) {418_cmsFree(ContextID, iohandler);419cmsSignalError(ContextID, cmsERROR_FILE, "File '%s' not found", FileName);420return NULL;421}422fileLen = cmsfilelength(fm);423if (fileLen < 0)424{425fclose(fm);426_cmsFree(ContextID, iohandler);427cmsSignalError(ContextID, cmsERROR_FILE, "Cannot get size of file '%s'", FileName);428return NULL;429}430431iohandler -> ReportedSize = (cmsUInt32Number) fileLen;432break;433434case 'w':435fm = fopen(FileName, "wb");436if (fm == NULL) {437_cmsFree(ContextID, iohandler);438cmsSignalError(ContextID, cmsERROR_FILE, "Couldn't create '%s'", FileName);439return NULL;440}441iohandler -> ReportedSize = 0;442break;443444default:445_cmsFree(ContextID, iohandler);446cmsSignalError(ContextID, cmsERROR_FILE, "Unknown access mode '%c'", *AccessMode);447return NULL;448}449450iohandler ->ContextID = ContextID;451iohandler ->stream = (void*) fm;452iohandler ->UsedSpace = 0;453454// Keep track of the original file455strncpy(iohandler -> PhysicalFile, FileName, sizeof(iohandler -> PhysicalFile)-1);456iohandler -> PhysicalFile[sizeof(iohandler -> PhysicalFile)-1] = 0;457458iohandler ->Read = FileRead;459iohandler ->Seek = FileSeek;460iohandler ->Close = FileClose;461iohandler ->Tell = FileTell;462iohandler ->Write = FileWrite;463464return iohandler;465}466467// Create a iohandler for stream based files468cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromStream(cmsContext ContextID, FILE* Stream)469{470cmsIOHANDLER* iohandler = NULL;471cmsInt32Number fileSize;472473fileSize = cmsfilelength(Stream);474if (fileSize < 0)475{476cmsSignalError(ContextID, cmsERROR_FILE, "Cannot get size of stream");477return NULL;478}479480iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER));481if (iohandler == NULL) return NULL;482483iohandler -> ContextID = ContextID;484iohandler -> stream = (void*) Stream;485iohandler -> UsedSpace = 0;486iohandler -> ReportedSize = (cmsUInt32Number) fileSize;487iohandler -> PhysicalFile[0] = 0;488489iohandler ->Read = FileRead;490iohandler ->Seek = FileSeek;491iohandler ->Close = FileClose;492iohandler ->Tell = FileTell;493iohandler ->Write = FileWrite;494495return iohandler;496}497498499500// Close an open IO handler501cmsBool CMSEXPORT cmsCloseIOhandler(cmsIOHANDLER* io)502{503return io -> Close(io);504}505506// -------------------------------------------------------------------------------------------------------507508cmsIOHANDLER* CMSEXPORT cmsGetProfileIOhandler(cmsHPROFILE hProfile)509{510_cmsICCPROFILE* Icc = (_cmsICCPROFILE*)hProfile;511512if (Icc == NULL) return NULL;513return Icc->IOhandler;514}515516// Creates an empty structure holding all required parameters517cmsHPROFILE CMSEXPORT cmsCreateProfilePlaceholder(cmsContext ContextID)518{519time_t now = time(NULL);520_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) _cmsMallocZero(ContextID, sizeof(_cmsICCPROFILE));521if (Icc == NULL) return NULL;522523Icc ->ContextID = ContextID;524525// Set it to empty526Icc -> TagCount = 0;527528// Set default version529Icc ->Version = 0x02100000;530531// Set creation date/time532memmove(&Icc ->Created, gmtime(&now), sizeof(Icc ->Created));533534// Create a mutex if the user provided proper plugin. NULL otherwise535Icc ->UsrMutex = _cmsCreateMutex(ContextID);536537// Return the handle538return (cmsHPROFILE) Icc;539}540541cmsContext CMSEXPORT cmsGetProfileContextID(cmsHPROFILE hProfile)542{543_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;544545if (Icc == NULL) return NULL;546return Icc -> ContextID;547}548549550// Return the number of tags551cmsInt32Number CMSEXPORT cmsGetTagCount(cmsHPROFILE hProfile)552{553_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;554if (Icc == NULL) return -1;555556return (cmsInt32Number) Icc->TagCount;557}558559// Return the tag signature of a given tag number560cmsTagSignature CMSEXPORT cmsGetTagSignature(cmsHPROFILE hProfile, cmsUInt32Number n)561{562_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;563564if (n > Icc->TagCount) return (cmsTagSignature) 0; // Mark as not available565if (n >= MAX_TABLE_TAG) return (cmsTagSignature) 0; // As double check566567return Icc ->TagNames[n];568}569570571static572int SearchOneTag(_cmsICCPROFILE* Profile, cmsTagSignature sig)573{574int i;575576for (i=0; i < (int) Profile -> TagCount; i++) {577578if (sig == Profile -> TagNames[i])579return i;580}581582return -1;583}584585// Search for a specific tag in tag dictionary. Returns position or -1 if tag not found.586// If followlinks is turned on, then the position of the linked tag is returned587int _cmsSearchTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, cmsBool lFollowLinks)588{589int n;590cmsTagSignature LinkedSig;591592do {593594// Search for given tag in ICC profile directory595n = SearchOneTag(Icc, sig);596if (n < 0)597return -1; // Not found598599if (!lFollowLinks)600return n; // Found, don't follow links601602// Is this a linked tag?603LinkedSig = Icc ->TagLinked[n];604605// Yes, follow link606if (LinkedSig != (cmsTagSignature) 0) {607sig = LinkedSig;608}609610} while (LinkedSig != (cmsTagSignature) 0);611612return n;613}614615// Deletes a tag entry616617static618void _cmsDeleteTagByPos(_cmsICCPROFILE* Icc, int i)619{620_cmsAssert(Icc != NULL);621_cmsAssert(i >= 0);622623624if (Icc -> TagPtrs[i] != NULL) {625626// Free previous version627if (Icc ->TagSaveAsRaw[i]) {628_cmsFree(Icc ->ContextID, Icc ->TagPtrs[i]);629}630else {631cmsTagTypeHandler* TypeHandler = Icc ->TagTypeHandlers[i];632633if (TypeHandler != NULL) {634635cmsTagTypeHandler LocalTypeHandler = *TypeHandler;636LocalTypeHandler.ContextID = Icc ->ContextID; // As an additional parameter637LocalTypeHandler.ICCVersion = Icc ->Version;638LocalTypeHandler.FreePtr(&LocalTypeHandler, Icc -> TagPtrs[i]);639Icc ->TagPtrs[i] = NULL;640}641}642643}644}645646647// Creates a new tag entry648static649cmsBool _cmsNewTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, int* NewPos)650{651int i;652653// Search for the tag654i = _cmsSearchTag(Icc, sig, FALSE);655if (i >= 0) {656657// Already exists? delete it658_cmsDeleteTagByPos(Icc, i);659*NewPos = i;660}661else {662663// No, make a new one664if (Icc -> TagCount >= MAX_TABLE_TAG) {665cmsSignalError(Icc ->ContextID, cmsERROR_RANGE, "Too many tags (%d)", MAX_TABLE_TAG);666return FALSE;667}668669*NewPos = (int) Icc ->TagCount;670Icc -> TagCount++;671}672673return TRUE;674}675676677// Check existence678cmsBool CMSEXPORT cmsIsTag(cmsHPROFILE hProfile, cmsTagSignature sig)679{680_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) (void*) hProfile;681return _cmsSearchTag(Icc, sig, FALSE) >= 0;682}683684// Enforces that the profile version is per. spec.685// Operates on the big endian bytes from the profile.686// Called before converting to platform endianness.687// Byte 0 is BCD major version, so max 9.688// Byte 1 is 2 BCD digits, one per nibble.689// Reserved bytes 2 & 3 must be 0.690static691cmsUInt32Number _validatedVersion(cmsUInt32Number DWord)692{693cmsUInt8Number* pByte = (cmsUInt8Number*) &DWord;694cmsUInt8Number temp1;695cmsUInt8Number temp2;696697if (*pByte > 0x09) *pByte = (cmsUInt8Number) 0x09;698temp1 = (cmsUInt8Number) (*(pByte+1) & 0xf0);699temp2 = (cmsUInt8Number) (*(pByte+1) & 0x0f);700if (temp1 > 0x90U) temp1 = 0x90U;701if (temp2 > 0x09U) temp2 = 0x09U;702*(pByte+1) = (cmsUInt8Number)(temp1 | temp2);703*(pByte+2) = (cmsUInt8Number)0;704*(pByte+3) = (cmsUInt8Number)0;705706return DWord;707}708709// Read profile header and validate it710cmsBool _cmsReadHeader(_cmsICCPROFILE* Icc)711{712cmsTagEntry Tag;713cmsICCHeader Header;714cmsUInt32Number i, j;715cmsUInt32Number HeaderSize;716cmsIOHANDLER* io = Icc ->IOhandler;717cmsUInt32Number TagCount;718719720// Read the header721if (io -> Read(io, &Header, sizeof(cmsICCHeader), 1) != 1) {722return FALSE;723}724725// Validate file as an ICC profile726if (_cmsAdjustEndianess32(Header.magic) != cmsMagicNumber) {727cmsSignalError(Icc ->ContextID, cmsERROR_BAD_SIGNATURE, "not an ICC profile, invalid signature");728return FALSE;729}730731// Adjust endianness of the used parameters732Icc -> DeviceClass = (cmsProfileClassSignature) _cmsAdjustEndianess32(Header.deviceClass);733Icc -> ColorSpace = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Header.colorSpace);734Icc -> PCS = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Header.pcs);735736Icc -> RenderingIntent = _cmsAdjustEndianess32(Header.renderingIntent);737Icc -> flags = _cmsAdjustEndianess32(Header.flags);738Icc -> manufacturer = _cmsAdjustEndianess32(Header.manufacturer);739Icc -> model = _cmsAdjustEndianess32(Header.model);740Icc -> creator = _cmsAdjustEndianess32(Header.creator);741742_cmsAdjustEndianess64(&Icc -> attributes, &Header.attributes);743Icc -> Version = _cmsAdjustEndianess32(_validatedVersion(Header.version));744745// Get size as reported in header746HeaderSize = _cmsAdjustEndianess32(Header.size);747748// Make sure HeaderSize is lower than profile size749if (HeaderSize >= Icc ->IOhandler ->ReportedSize)750HeaderSize = Icc ->IOhandler ->ReportedSize;751752753// Get creation date/time754_cmsDecodeDateTimeNumber(&Header.date, &Icc ->Created);755756// The profile ID are 32 raw bytes757memmove(Icc ->ProfileID.ID32, Header.profileID.ID32, 16);758759760// Read tag directory761if (!_cmsReadUInt32Number(io, &TagCount)) return FALSE;762if (TagCount > MAX_TABLE_TAG) {763764cmsSignalError(Icc ->ContextID, cmsERROR_RANGE, "Too many tags (%d)", TagCount);765return FALSE;766}767768769// Read tag directory770Icc -> TagCount = 0;771for (i=0; i < TagCount; i++) {772773if (!_cmsReadUInt32Number(io, (cmsUInt32Number *) &Tag.sig)) return FALSE;774if (!_cmsReadUInt32Number(io, &Tag.offset)) return FALSE;775if (!_cmsReadUInt32Number(io, &Tag.size)) return FALSE;776777// Perform some sanity check. Offset + size should fall inside file.778if (Tag.offset + Tag.size > HeaderSize ||779Tag.offset + Tag.size < Tag.offset)780continue;781782Icc -> TagNames[Icc ->TagCount] = Tag.sig;783Icc -> TagOffsets[Icc ->TagCount] = Tag.offset;784Icc -> TagSizes[Icc ->TagCount] = Tag.size;785786// Search for links787for (j=0; j < Icc ->TagCount; j++) {788789if ((Icc ->TagOffsets[j] == Tag.offset) &&790(Icc ->TagSizes[j] == Tag.size)) {791792Icc ->TagLinked[Icc ->TagCount] = Icc ->TagNames[j];793}794795}796797Icc ->TagCount++;798}799800return TRUE;801}802803// Saves profile header804cmsBool _cmsWriteHeader(_cmsICCPROFILE* Icc, cmsUInt32Number UsedSpace)805{806cmsICCHeader Header;807cmsUInt32Number i;808cmsTagEntry Tag;809cmsUInt32Number Count;810811Header.size = _cmsAdjustEndianess32(UsedSpace);812Header.cmmId = _cmsAdjustEndianess32(lcmsSignature);813Header.version = _cmsAdjustEndianess32(Icc ->Version);814815Header.deviceClass = (cmsProfileClassSignature) _cmsAdjustEndianess32(Icc -> DeviceClass);816Header.colorSpace = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Icc -> ColorSpace);817Header.pcs = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Icc -> PCS);818819// NOTE: in v4 Timestamp must be in UTC rather than in local time820_cmsEncodeDateTimeNumber(&Header.date, &Icc ->Created);821822Header.magic = _cmsAdjustEndianess32(cmsMagicNumber);823824#ifdef CMS_IS_WINDOWS_825Header.platform = (cmsPlatformSignature) _cmsAdjustEndianess32(cmsSigMicrosoft);826#else827Header.platform = (cmsPlatformSignature) _cmsAdjustEndianess32(cmsSigMacintosh);828#endif829830Header.flags = _cmsAdjustEndianess32(Icc -> flags);831Header.manufacturer = _cmsAdjustEndianess32(Icc -> manufacturer);832Header.model = _cmsAdjustEndianess32(Icc -> model);833834_cmsAdjustEndianess64(&Header.attributes, &Icc -> attributes);835836// Rendering intent in the header (for embedded profiles)837Header.renderingIntent = _cmsAdjustEndianess32(Icc -> RenderingIntent);838839// Illuminant is always D50840Header.illuminant.X = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(cmsD50_XYZ()->X));841Header.illuminant.Y = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(cmsD50_XYZ()->Y));842Header.illuminant.Z = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(cmsD50_XYZ()->Z));843844// Created by LittleCMS (that's me!)845Header.creator = _cmsAdjustEndianess32(lcmsSignature);846847memset(&Header.reserved, 0, sizeof(Header.reserved));848849// Set profile ID. Endianness is always big endian850memmove(&Header.profileID, &Icc ->ProfileID, 16);851852// Dump the header853if (!Icc -> IOhandler->Write(Icc->IOhandler, sizeof(cmsICCHeader), &Header)) return FALSE;854855// Saves Tag directory856857// Get true count858Count = 0;859for (i=0; i < Icc -> TagCount; i++) {860if (Icc ->TagNames[i] != (cmsTagSignature) 0)861Count++;862}863864// Store number of tags865if (!_cmsWriteUInt32Number(Icc ->IOhandler, Count)) return FALSE;866867for (i=0; i < Icc -> TagCount; i++) {868869if (Icc ->TagNames[i] == (cmsTagSignature) 0) continue; // It is just a placeholder870871Tag.sig = (cmsTagSignature) _cmsAdjustEndianess32((cmsUInt32Number) Icc -> TagNames[i]);872Tag.offset = _cmsAdjustEndianess32((cmsUInt32Number) Icc -> TagOffsets[i]);873Tag.size = _cmsAdjustEndianess32((cmsUInt32Number) Icc -> TagSizes[i]);874875if (!Icc ->IOhandler -> Write(Icc-> IOhandler, sizeof(cmsTagEntry), &Tag)) return FALSE;876}877878return TRUE;879}880881// ----------------------------------------------------------------------- Set/Get several struct members882883884cmsUInt32Number CMSEXPORT cmsGetHeaderRenderingIntent(cmsHPROFILE hProfile)885{886_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;887return Icc -> RenderingIntent;888}889890void CMSEXPORT cmsSetHeaderRenderingIntent(cmsHPROFILE hProfile, cmsUInt32Number RenderingIntent)891{892_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;893Icc -> RenderingIntent = RenderingIntent;894}895896cmsUInt32Number CMSEXPORT cmsGetHeaderFlags(cmsHPROFILE hProfile)897{898_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;899return (cmsUInt32Number) Icc -> flags;900}901902void CMSEXPORT cmsSetHeaderFlags(cmsHPROFILE hProfile, cmsUInt32Number Flags)903{904_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;905Icc -> flags = (cmsUInt32Number) Flags;906}907908cmsUInt32Number CMSEXPORT cmsGetHeaderManufacturer(cmsHPROFILE hProfile)909{910_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;911return Icc ->manufacturer;912}913914void CMSEXPORT cmsSetHeaderManufacturer(cmsHPROFILE hProfile, cmsUInt32Number manufacturer)915{916_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;917Icc -> manufacturer = manufacturer;918}919920cmsUInt32Number CMSEXPORT cmsGetHeaderCreator(cmsHPROFILE hProfile)921{922_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;923return Icc ->creator;924}925926cmsUInt32Number CMSEXPORT cmsGetHeaderModel(cmsHPROFILE hProfile)927{928_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;929return Icc ->model;930}931932void CMSEXPORT cmsSetHeaderModel(cmsHPROFILE hProfile, cmsUInt32Number model)933{934_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;935Icc -> model = model;936}937938void CMSEXPORT cmsGetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number* Flags)939{940_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;941memmove(Flags, &Icc -> attributes, sizeof(cmsUInt64Number));942}943944void CMSEXPORT cmsSetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number Flags)945{946_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;947memmove(&Icc -> attributes, &Flags, sizeof(cmsUInt64Number));948}949950void CMSEXPORT cmsGetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID)951{952_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;953memmove(ProfileID, Icc ->ProfileID.ID8, 16);954}955956void CMSEXPORT cmsSetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID)957{958_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;959memmove(&Icc -> ProfileID, ProfileID, 16);960}961962cmsBool CMSEXPORT cmsGetHeaderCreationDateTime(cmsHPROFILE hProfile, struct tm *Dest)963{964_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;965memmove(Dest, &Icc ->Created, sizeof(struct tm));966return TRUE;967}968969cmsColorSpaceSignature CMSEXPORT cmsGetPCS(cmsHPROFILE hProfile)970{971_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;972return Icc -> PCS;973}974975void CMSEXPORT cmsSetPCS(cmsHPROFILE hProfile, cmsColorSpaceSignature pcs)976{977_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;978Icc -> PCS = pcs;979}980981cmsColorSpaceSignature CMSEXPORT cmsGetColorSpace(cmsHPROFILE hProfile)982{983_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;984return Icc -> ColorSpace;985}986987void CMSEXPORT cmsSetColorSpace(cmsHPROFILE hProfile, cmsColorSpaceSignature sig)988{989_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;990Icc -> ColorSpace = sig;991}992993cmsProfileClassSignature CMSEXPORT cmsGetDeviceClass(cmsHPROFILE hProfile)994{995_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;996return Icc -> DeviceClass;997}998999void CMSEXPORT cmsSetDeviceClass(cmsHPROFILE hProfile, cmsProfileClassSignature sig)1000{1001_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;1002Icc -> DeviceClass = sig;1003}10041005cmsUInt32Number CMSEXPORT cmsGetEncodedICCversion(cmsHPROFILE hProfile)1006{1007_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;1008return Icc -> Version;1009}10101011void CMSEXPORT cmsSetEncodedICCversion(cmsHPROFILE hProfile, cmsUInt32Number Version)1012{1013_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;1014Icc -> Version = Version;1015}10161017// Get an hexadecimal number with same digits as v1018static1019cmsUInt32Number BaseToBase(cmsUInt32Number in, int BaseIn, int BaseOut)1020{1021char Buff[100];1022int i, len;1023cmsUInt32Number out;10241025for (len=0; in > 0 && len < 100; len++) {10261027Buff[len] = (char) (in % BaseIn);1028in /= BaseIn;1029}10301031for (i=len-1, out=0; i >= 0; --i) {1032out = out * BaseOut + Buff[i];1033}10341035return out;1036}10371038void CMSEXPORT cmsSetProfileVersion(cmsHPROFILE hProfile, cmsFloat64Number Version)1039{1040_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;10411042// 4.2 -> 0x420000010431044Icc -> Version = BaseToBase((cmsUInt32Number) floor(Version * 100.0 + 0.5), 10, 16) << 16;1045}10461047cmsFloat64Number CMSEXPORT cmsGetProfileVersion(cmsHPROFILE hProfile)1048{1049_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;1050cmsUInt32Number n = Icc -> Version >> 16;10511052return BaseToBase(n, 16, 10) / 100.0;1053}1054// --------------------------------------------------------------------------------------------------------------105510561057// Create profile from IOhandler1058cmsHPROFILE CMSEXPORT cmsOpenProfileFromIOhandlerTHR(cmsContext ContextID, cmsIOHANDLER* io)1059{1060_cmsICCPROFILE* NewIcc;1061cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID);10621063if (hEmpty == NULL) return NULL;10641065NewIcc = (_cmsICCPROFILE*) hEmpty;10661067NewIcc ->IOhandler = io;1068if (!_cmsReadHeader(NewIcc)) goto Error;1069return hEmpty;10701071Error:1072cmsCloseProfile(hEmpty);1073return NULL;1074}10751076// Create profile from IOhandler1077cmsHPROFILE CMSEXPORT cmsOpenProfileFromIOhandler2THR(cmsContext ContextID, cmsIOHANDLER* io, cmsBool write)1078{1079_cmsICCPROFILE* NewIcc;1080cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID);10811082if (hEmpty == NULL) return NULL;10831084NewIcc = (_cmsICCPROFILE*) hEmpty;10851086NewIcc ->IOhandler = io;1087if (write) {10881089NewIcc -> IsWrite = TRUE;1090return hEmpty;1091}10921093if (!_cmsReadHeader(NewIcc)) goto Error;1094return hEmpty;10951096Error:1097cmsCloseProfile(hEmpty);1098return NULL;1099}110011011102// Create profile from disk file1103cmsHPROFILE CMSEXPORT cmsOpenProfileFromFileTHR(cmsContext ContextID, const char *lpFileName, const char *sAccess)1104{1105_cmsICCPROFILE* NewIcc;1106cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID);11071108if (hEmpty == NULL) return NULL;11091110NewIcc = (_cmsICCPROFILE*) hEmpty;11111112NewIcc ->IOhandler = cmsOpenIOhandlerFromFile(ContextID, lpFileName, sAccess);1113if (NewIcc ->IOhandler == NULL) goto Error;11141115if (*sAccess == 'W' || *sAccess == 'w') {11161117NewIcc -> IsWrite = TRUE;11181119return hEmpty;1120}11211122if (!_cmsReadHeader(NewIcc)) goto Error;1123return hEmpty;11241125Error:1126cmsCloseProfile(hEmpty);1127return NULL;1128}112911301131cmsHPROFILE CMSEXPORT cmsOpenProfileFromFile(const char *ICCProfile, const char *sAccess)1132{1133return cmsOpenProfileFromFileTHR(NULL, ICCProfile, sAccess);1134}113511361137cmsHPROFILE CMSEXPORT cmsOpenProfileFromStreamTHR(cmsContext ContextID, FILE* ICCProfile, const char *sAccess)1138{1139_cmsICCPROFILE* NewIcc;1140cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID);11411142if (hEmpty == NULL) return NULL;11431144NewIcc = (_cmsICCPROFILE*) hEmpty;11451146NewIcc ->IOhandler = cmsOpenIOhandlerFromStream(ContextID, ICCProfile);1147if (NewIcc ->IOhandler == NULL) goto Error;11481149if (*sAccess == 'w') {11501151NewIcc -> IsWrite = TRUE;1152return hEmpty;1153}11541155if (!_cmsReadHeader(NewIcc)) goto Error;1156return hEmpty;11571158Error:1159cmsCloseProfile(hEmpty);1160return NULL;11611162}11631164cmsHPROFILE CMSEXPORT cmsOpenProfileFromStream(FILE* ICCProfile, const char *sAccess)1165{1166return cmsOpenProfileFromStreamTHR(NULL, ICCProfile, sAccess);1167}116811691170// Open from memory block1171cmsHPROFILE CMSEXPORT cmsOpenProfileFromMemTHR(cmsContext ContextID, const void* MemPtr, cmsUInt32Number dwSize)1172{1173_cmsICCPROFILE* NewIcc;1174cmsHPROFILE hEmpty;11751176hEmpty = cmsCreateProfilePlaceholder(ContextID);1177if (hEmpty == NULL) return NULL;11781179NewIcc = (_cmsICCPROFILE*) hEmpty;11801181// Ok, in this case const void* is casted to void* just because open IO handler1182// shares read and writing modes. Don't abuse this feature!1183NewIcc ->IOhandler = cmsOpenIOhandlerFromMem(ContextID, (void*) MemPtr, dwSize, "r");1184if (NewIcc ->IOhandler == NULL) goto Error;11851186if (!_cmsReadHeader(NewIcc)) goto Error;11871188return hEmpty;11891190Error:1191cmsCloseProfile(hEmpty);1192return NULL;1193}11941195cmsHPROFILE CMSEXPORT cmsOpenProfileFromMem(const void* MemPtr, cmsUInt32Number dwSize)1196{1197return cmsOpenProfileFromMemTHR(NULL, MemPtr, dwSize);1198}1199120012011202// Dump tag contents. If the profile is being modified, untouched tags are copied from FileOrig1203static1204cmsBool SaveTags(_cmsICCPROFILE* Icc, _cmsICCPROFILE* FileOrig)1205{1206cmsUInt8Number* Data;1207cmsUInt32Number i;1208cmsUInt32Number Begin;1209cmsIOHANDLER* io = Icc ->IOhandler;1210cmsTagDescriptor* TagDescriptor;1211cmsTagTypeSignature TypeBase;1212cmsTagTypeSignature Type;1213cmsTagTypeHandler* TypeHandler;1214cmsFloat64Number Version = cmsGetProfileVersion((cmsHPROFILE) Icc);1215cmsTagTypeHandler LocalTypeHandler;12161217for (i=0; i < Icc -> TagCount; i++) {12181219if (Icc ->TagNames[i] == (cmsTagSignature) 0) continue;12201221// Linked tags are not written1222if (Icc ->TagLinked[i] != (cmsTagSignature) 0) continue;12231224Icc -> TagOffsets[i] = Begin = io ->UsedSpace;12251226Data = (cmsUInt8Number*) Icc -> TagPtrs[i];12271228if (!Data) {12291230// Reach here if we are copying a tag from a disk-based ICC profile which has not been modified by user.1231// In this case a blind copy of the block data is performed1232if (FileOrig != NULL && Icc -> TagOffsets[i]) {12331234cmsUInt32Number TagSize = FileOrig -> TagSizes[i];1235cmsUInt32Number TagOffset = FileOrig -> TagOffsets[i];1236void* Mem;12371238if (!FileOrig ->IOhandler->Seek(FileOrig ->IOhandler, TagOffset)) return FALSE;12391240Mem = _cmsMalloc(Icc ->ContextID, TagSize);1241if (Mem == NULL) return FALSE;12421243if (FileOrig ->IOhandler->Read(FileOrig->IOhandler, Mem, TagSize, 1) != 1) return FALSE;1244if (!io ->Write(io, TagSize, Mem)) return FALSE;1245_cmsFree(Icc ->ContextID, Mem);12461247Icc -> TagSizes[i] = (io ->UsedSpace - Begin);124812491250// Align to 32 bit boundary.1251if (! _cmsWriteAlignment(io))1252return FALSE;1253}12541255continue;1256}125712581259// Should this tag be saved as RAW? If so, tagsizes should be specified in advance (no further cooking is done)1260if (Icc ->TagSaveAsRaw[i]) {12611262if (io -> Write(io, Icc ->TagSizes[i], Data) != 1) return FALSE;1263}1264else {12651266// Search for support on this tag1267TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, Icc -> TagNames[i]);1268if (TagDescriptor == NULL) continue; // Unsupported, ignore it12691270if (TagDescriptor ->DecideType != NULL) {12711272Type = TagDescriptor ->DecideType(Version, Data);1273}1274else {12751276Type = TagDescriptor ->SupportedTypes[0];1277}12781279TypeHandler = _cmsGetTagTypeHandler(Icc->ContextID, Type);12801281if (TypeHandler == NULL) {1282cmsSignalError(Icc ->ContextID, cmsERROR_INTERNAL, "(Internal) no handler for tag %x", Icc -> TagNames[i]);1283continue;1284}12851286TypeBase = TypeHandler ->Signature;1287if (!_cmsWriteTypeBase(io, TypeBase))1288return FALSE;12891290LocalTypeHandler = *TypeHandler;1291LocalTypeHandler.ContextID = Icc ->ContextID;1292LocalTypeHandler.ICCVersion = Icc ->Version;1293if (!LocalTypeHandler.WritePtr(&LocalTypeHandler, io, Data, TagDescriptor ->ElemCount)) {12941295char String[5];12961297_cmsTagSignature2String(String, (cmsTagSignature) TypeBase);1298cmsSignalError(Icc ->ContextID, cmsERROR_WRITE, "Couldn't write type '%s'", String);1299return FALSE;1300}1301}130213031304Icc -> TagSizes[i] = (io ->UsedSpace - Begin);13051306// Align to 32 bit boundary.1307if (! _cmsWriteAlignment(io))1308return FALSE;1309}131013111312return TRUE;1313}131413151316// Fill the offset and size fields for all linked tags1317static1318cmsBool SetLinks( _cmsICCPROFILE* Icc)1319{1320cmsUInt32Number i;13211322for (i=0; i < Icc -> TagCount; i++) {13231324cmsTagSignature lnk = Icc ->TagLinked[i];1325if (lnk != (cmsTagSignature) 0) {13261327int j = _cmsSearchTag(Icc, lnk, FALSE);1328if (j >= 0) {13291330Icc ->TagOffsets[i] = Icc ->TagOffsets[j];1331Icc ->TagSizes[i] = Icc ->TagSizes[j];1332}13331334}1335}13361337return TRUE;1338}13391340// Low-level save to IOHANDLER. It returns the number of bytes used to1341// store the profile, or zero on error. io may be NULL and in this case1342// no data is written--only sizes are calculated1343cmsUInt32Number CMSEXPORT cmsSaveProfileToIOhandler(cmsHPROFILE hProfile, cmsIOHANDLER* io)1344{1345_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;1346_cmsICCPROFILE Keep;1347cmsIOHANDLER* PrevIO = NULL;1348cmsUInt32Number UsedSpace;1349cmsContext ContextID;13501351_cmsAssert(hProfile != NULL);13521353if (!_cmsLockMutex(Icc->ContextID, Icc->UsrMutex)) return 0;1354memmove(&Keep, Icc, sizeof(_cmsICCPROFILE));13551356ContextID = cmsGetProfileContextID(hProfile);1357PrevIO = Icc ->IOhandler = cmsOpenIOhandlerFromNULL(ContextID);1358if (PrevIO == NULL) {1359_cmsUnlockMutex(Icc->ContextID, Icc->UsrMutex);1360return 0;1361}13621363// Pass #1 does compute offsets13641365if (!_cmsWriteHeader(Icc, 0)) goto Error;1366if (!SaveTags(Icc, &Keep)) goto Error;13671368UsedSpace = PrevIO ->UsedSpace;13691370// Pass #2 does save to iohandler13711372if (io != NULL) {13731374Icc ->IOhandler = io;1375if (!SetLinks(Icc)) goto Error;1376if (!_cmsWriteHeader(Icc, UsedSpace)) goto Error;1377if (!SaveTags(Icc, &Keep)) goto Error;1378}13791380memmove(Icc, &Keep, sizeof(_cmsICCPROFILE));1381if (!cmsCloseIOhandler(PrevIO))1382UsedSpace = 0; // As a error marker13831384_cmsUnlockMutex(Icc->ContextID, Icc->UsrMutex);13851386return UsedSpace;138713881389Error:1390cmsCloseIOhandler(PrevIO);1391memmove(Icc, &Keep, sizeof(_cmsICCPROFILE));1392_cmsUnlockMutex(Icc->ContextID, Icc->UsrMutex);13931394return 0;1395}139613971398// Low-level save to disk.1399cmsBool CMSEXPORT cmsSaveProfileToFile(cmsHPROFILE hProfile, const char* FileName)1400{1401cmsContext ContextID = cmsGetProfileContextID(hProfile);1402cmsIOHANDLER* io = cmsOpenIOhandlerFromFile(ContextID, FileName, "w");1403cmsBool rc;14041405if (io == NULL) return FALSE;14061407rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0);1408rc &= cmsCloseIOhandler(io);14091410if (rc == FALSE) { // remove() is C99 per 7.19.4.11411remove(FileName); // We have to IGNORE return value in this case1412}1413return rc;1414}14151416// Same as anterior, but for streams1417cmsBool CMSEXPORT cmsSaveProfileToStream(cmsHPROFILE hProfile, FILE* Stream)1418{1419cmsBool rc;1420cmsContext ContextID = cmsGetProfileContextID(hProfile);1421cmsIOHANDLER* io = cmsOpenIOhandlerFromStream(ContextID, Stream);14221423if (io == NULL) return FALSE;14241425rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0);1426rc &= cmsCloseIOhandler(io);14271428return rc;1429}143014311432// Same as anterior, but for memory blocks. In this case, a NULL as MemPtr means calculate needed space only1433cmsBool CMSEXPORT cmsSaveProfileToMem(cmsHPROFILE hProfile, void *MemPtr, cmsUInt32Number* BytesNeeded)1434{1435cmsBool rc;1436cmsIOHANDLER* io;1437cmsContext ContextID = cmsGetProfileContextID(hProfile);14381439_cmsAssert(BytesNeeded != NULL);14401441// Should we just calculate the needed space?1442if (MemPtr == NULL) {14431444*BytesNeeded = cmsSaveProfileToIOhandler(hProfile, NULL);1445return (*BytesNeeded == 0) ? FALSE : TRUE;1446}14471448// That is a real write operation1449io = cmsOpenIOhandlerFromMem(ContextID, MemPtr, *BytesNeeded, "w");1450if (io == NULL) return FALSE;14511452rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0);1453rc &= cmsCloseIOhandler(io);14541455return rc;1456}1457145814591460// Closes a profile freeing any involved resources1461cmsBool CMSEXPORT cmsCloseProfile(cmsHPROFILE hProfile)1462{1463_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;1464cmsBool rc = TRUE;1465cmsUInt32Number i;14661467if (!Icc) return FALSE;14681469// Was open in write mode?1470if (Icc ->IsWrite) {14711472Icc ->IsWrite = FALSE; // Assure no further writing1473rc &= cmsSaveProfileToFile(hProfile, Icc ->IOhandler->PhysicalFile);1474}14751476for (i=0; i < Icc -> TagCount; i++) {14771478if (Icc -> TagPtrs[i]) {14791480cmsTagTypeHandler* TypeHandler = Icc ->TagTypeHandlers[i];14811482if (TypeHandler != NULL) {1483cmsTagTypeHandler LocalTypeHandler = *TypeHandler;14841485LocalTypeHandler.ContextID = Icc ->ContextID; // As an additional parameters1486LocalTypeHandler.ICCVersion = Icc ->Version;1487LocalTypeHandler.FreePtr(&LocalTypeHandler, Icc -> TagPtrs[i]);1488}1489else1490_cmsFree(Icc ->ContextID, Icc ->TagPtrs[i]);1491}1492}14931494if (Icc ->IOhandler != NULL) {1495rc &= cmsCloseIOhandler(Icc->IOhandler);1496}14971498_cmsDestroyMutex(Icc->ContextID, Icc->UsrMutex);14991500_cmsFree(Icc ->ContextID, Icc); // Free placeholder memory15011502return rc;1503}150415051506// -------------------------------------------------------------------------------------------------------------------150715081509// Returns TRUE if a given tag is supported by a plug-in1510static1511cmsBool IsTypeSupported(cmsTagDescriptor* TagDescriptor, cmsTagTypeSignature Type)1512{1513cmsUInt32Number i, nMaxTypes;15141515nMaxTypes = TagDescriptor->nSupportedTypes;1516if (nMaxTypes >= MAX_TYPES_IN_LCMS_PLUGIN)1517nMaxTypes = MAX_TYPES_IN_LCMS_PLUGIN;15181519for (i=0; i < nMaxTypes; i++) {1520if (Type == TagDescriptor ->SupportedTypes[i]) return TRUE;1521}15221523return FALSE;1524}152515261527// That's the main read function1528void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig)1529{1530_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;1531cmsIOHANDLER* io = Icc ->IOhandler;1532cmsTagTypeHandler* TypeHandler;1533cmsTagTypeHandler LocalTypeHandler;1534cmsTagDescriptor* TagDescriptor;1535cmsTagTypeSignature BaseType;1536cmsUInt32Number Offset, TagSize;1537cmsUInt32Number ElemCount;1538int n;15391540if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return NULL;15411542n = _cmsSearchTag(Icc, sig, TRUE);1543if (n < 0) goto Error; // Not found, return NULL154415451546// If the element is already in memory, return the pointer1547if (Icc -> TagPtrs[n]) {15481549if (Icc->TagTypeHandlers[n] == NULL) goto Error;15501551// Sanity check1552BaseType = Icc->TagTypeHandlers[n]->Signature;1553if (BaseType == 0) goto Error;15541555TagDescriptor = _cmsGetTagDescriptor(Icc->ContextID, sig);1556if (TagDescriptor == NULL) goto Error;15571558if (!IsTypeSupported(TagDescriptor, BaseType)) goto Error;15591560if (Icc ->TagSaveAsRaw[n]) goto Error; // We don't support read raw tags as cooked15611562_cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);1563return Icc -> TagPtrs[n];1564}15651566// We need to read it. Get the offset and size to the file1567Offset = Icc -> TagOffsets[n];1568TagSize = Icc -> TagSizes[n];15691570if (TagSize < 8) goto Error;15711572// Seek to its location1573if (!io -> Seek(io, Offset))1574goto Error;15751576// Search for support on this tag1577TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig);1578if (TagDescriptor == NULL) {15791580char String[5];15811582_cmsTagSignature2String(String, sig);15831584// An unknown element was found.1585cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown tag type '%s' found.", String);1586goto Error; // Unsupported.1587}15881589// if supported, get type and check if in list1590BaseType = _cmsReadTypeBase(io);1591if (BaseType == 0) goto Error;15921593if (!IsTypeSupported(TagDescriptor, BaseType)) goto Error;15941595TagSize -= 8; // Already read by the type base logic15961597// Get type handler1598TypeHandler = _cmsGetTagTypeHandler(Icc ->ContextID, BaseType);1599if (TypeHandler == NULL) goto Error;1600LocalTypeHandler = *TypeHandler;160116021603// Read the tag1604Icc -> TagTypeHandlers[n] = TypeHandler;16051606LocalTypeHandler.ContextID = Icc ->ContextID;1607LocalTypeHandler.ICCVersion = Icc ->Version;1608Icc -> TagPtrs[n] = LocalTypeHandler.ReadPtr(&LocalTypeHandler, io, &ElemCount, TagSize);16091610// The tag type is supported, but something wrong happened and we cannot read the tag.1611// let know the user about this (although it is just a warning)1612if (Icc -> TagPtrs[n] == NULL) {16131614char String[5];16151616_cmsTagSignature2String(String, sig);1617cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "Corrupted tag '%s'", String);1618goto Error;1619}16201621// This is a weird error that may be a symptom of something more serious, the number of1622// stored item is actually less than the number of required elements.1623if (ElemCount < TagDescriptor ->ElemCount) {16241625char String[5];16261627_cmsTagSignature2String(String, sig);1628cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "'%s' Inconsistent number of items: expected %d, got %d",1629String, TagDescriptor ->ElemCount, ElemCount);1630goto Error;1631}163216331634// Return the data1635_cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);1636return Icc -> TagPtrs[n];163716381639// Return error and unlock tha data1640Error:1641_cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);1642return NULL;1643}164416451646// Get true type of data1647cmsTagTypeSignature _cmsGetTagTrueType(cmsHPROFILE hProfile, cmsTagSignature sig)1648{1649_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;1650cmsTagTypeHandler* TypeHandler;1651int n;16521653// Search for given tag in ICC profile directory1654n = _cmsSearchTag(Icc, sig, TRUE);1655if (n < 0) return (cmsTagTypeSignature) 0; // Not found, return NULL16561657// Get the handler. The true type is there1658TypeHandler = Icc -> TagTypeHandlers[n];1659return TypeHandler ->Signature;1660}166116621663// Write a single tag. This just keeps track of the tak into a list of "to be written". If the tag is already1664// in that list, the previous version is deleted.1665cmsBool CMSEXPORT cmsWriteTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data)1666{1667_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;1668cmsTagTypeHandler* TypeHandler = NULL;1669cmsTagTypeHandler LocalTypeHandler;1670cmsTagDescriptor* TagDescriptor = NULL;1671cmsTagTypeSignature Type;1672int i;1673cmsFloat64Number Version;1674char TypeString[5], SigString[5];16751676if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return FALSE;16771678// To delete tags.1679if (data == NULL) {16801681// Delete the tag1682i = _cmsSearchTag(Icc, sig, FALSE);1683if (i >= 0) {16841685// Use zero as a mark of deleted1686_cmsDeleteTagByPos(Icc, i);1687Icc ->TagNames[i] = (cmsTagSignature) 0;1688_cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);1689return TRUE;1690}1691// Didn't find the tag1692goto Error;1693}16941695if (!_cmsNewTag(Icc, sig, &i)) goto Error;16961697// This is not raw1698Icc ->TagSaveAsRaw[i] = FALSE;16991700// This is not a link1701Icc ->TagLinked[i] = (cmsTagSignature) 0;17021703// Get information about the TAG.1704TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig);1705if (TagDescriptor == NULL){1706cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported tag '%x'", sig);1707goto Error;1708}170917101711// Now we need to know which type to use. It depends on the version.1712Version = cmsGetProfileVersion(hProfile);17131714if (TagDescriptor ->DecideType != NULL) {17151716// Let the tag descriptor to decide the type base on depending on1717// the data. This is useful for example on parametric curves, where1718// curves specified by a table cannot be saved as parametric and needs1719// to be casted to single v2-curves, even on v4 profiles.17201721Type = TagDescriptor ->DecideType(Version, data);1722}1723else {17241725Type = TagDescriptor ->SupportedTypes[0];1726}17271728// Does the tag support this type?1729if (!IsTypeSupported(TagDescriptor, Type)) {17301731_cmsTagSignature2String(TypeString, (cmsTagSignature) Type);1732_cmsTagSignature2String(SigString, sig);17331734cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported type '%s' for tag '%s'", TypeString, SigString);1735goto Error;1736}17371738// Does we have a handler for this type?1739TypeHandler = _cmsGetTagTypeHandler(Icc->ContextID, Type);1740if (TypeHandler == NULL) {17411742_cmsTagSignature2String(TypeString, (cmsTagSignature) Type);1743_cmsTagSignature2String(SigString, sig);17441745cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported type '%s' for tag '%s'", TypeString, SigString);1746goto Error; // Should never happen1747}174817491750// Fill fields on icc structure1751Icc ->TagTypeHandlers[i] = TypeHandler;1752Icc ->TagNames[i] = sig;1753Icc ->TagSizes[i] = 0;1754Icc ->TagOffsets[i] = 0;17551756LocalTypeHandler = *TypeHandler;1757LocalTypeHandler.ContextID = Icc ->ContextID;1758LocalTypeHandler.ICCVersion = Icc ->Version;1759Icc ->TagPtrs[i] = LocalTypeHandler.DupPtr(&LocalTypeHandler, data, TagDescriptor ->ElemCount);17601761if (Icc ->TagPtrs[i] == NULL) {17621763_cmsTagSignature2String(TypeString, (cmsTagSignature) Type);1764_cmsTagSignature2String(SigString, sig);1765cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "Malformed struct in type '%s' for tag '%s'", TypeString, SigString);17661767goto Error;1768}17691770_cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);1771return TRUE;17721773Error:1774_cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);1775return FALSE;17761777}17781779// Read and write raw data. The only way those function would work and keep consistence with normal read and write1780// is to do an additional step of serialization. That means, readRaw would issue a normal read and then convert the obtained1781// data to raw bytes by using the "write" serialization logic. And vice-versa. I know this may end in situations where1782// raw data written does not exactly correspond with the raw data proposed to cmsWriteRaw data, but this approach allows1783// to write a tag as raw data and the read it as handled.17841785cmsUInt32Number CMSEXPORT cmsReadRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, void* data, cmsUInt32Number BufferSize)1786{1787_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;1788void *Object;1789int i;1790cmsIOHANDLER* MemIO;1791cmsTagTypeHandler* TypeHandler = NULL;1792cmsTagTypeHandler LocalTypeHandler;1793cmsTagDescriptor* TagDescriptor = NULL;1794cmsUInt32Number rc;1795cmsUInt32Number Offset, TagSize;17961797if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0;17981799// Search for given tag in ICC profile directory1800i = _cmsSearchTag(Icc, sig, TRUE);1801if (i < 0) goto Error; // Not found,18021803// It is already read?1804if (Icc -> TagPtrs[i] == NULL) {18051806// No yet, get original position1807Offset = Icc ->TagOffsets[i];1808TagSize = Icc ->TagSizes[i];18091810// read the data directly, don't keep copy1811if (data != NULL) {18121813if (BufferSize < TagSize)1814TagSize = BufferSize;18151816if (!Icc ->IOhandler ->Seek(Icc ->IOhandler, Offset)) goto Error;1817if (!Icc ->IOhandler ->Read(Icc ->IOhandler, data, 1, TagSize)) goto Error;18181819_cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);1820return TagSize;1821}18221823_cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);1824return Icc ->TagSizes[i];1825}18261827// The data has been already read, or written. But wait!, maybe the user chose to save as1828// raw data. In this case, return the raw data directly1829if (Icc ->TagSaveAsRaw[i]) {18301831if (data != NULL) {18321833TagSize = Icc ->TagSizes[i];1834if (BufferSize < TagSize)1835TagSize = BufferSize;18361837memmove(data, Icc ->TagPtrs[i], TagSize);18381839_cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);1840return TagSize;1841}18421843_cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);1844return Icc ->TagSizes[i];1845}18461847// Already read, or previously set by cmsWriteTag(). We need to serialize that1848// data to raw in order to maintain consistency.18491850_cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);1851Object = cmsReadTag(hProfile, sig);1852if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0;18531854if (Object == NULL) goto Error;18551856// Now we need to serialize to a memory block: just use a memory iohandler18571858if (data == NULL) {1859MemIO = cmsOpenIOhandlerFromNULL(cmsGetProfileContextID(hProfile));1860} else{1861MemIO = cmsOpenIOhandlerFromMem(cmsGetProfileContextID(hProfile), data, BufferSize, "w");1862}1863if (MemIO == NULL) goto Error;18641865// Obtain type handling for the tag1866TypeHandler = Icc ->TagTypeHandlers[i];1867TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig);1868if (TagDescriptor == NULL) {1869cmsCloseIOhandler(MemIO);1870goto Error;1871}18721873if (TypeHandler == NULL) goto Error;18741875// Serialize1876LocalTypeHandler = *TypeHandler;1877LocalTypeHandler.ContextID = Icc ->ContextID;1878LocalTypeHandler.ICCVersion = Icc ->Version;18791880if (!_cmsWriteTypeBase(MemIO, TypeHandler ->Signature)) {1881cmsCloseIOhandler(MemIO);1882goto Error;1883}18841885if (!LocalTypeHandler.WritePtr(&LocalTypeHandler, MemIO, Object, TagDescriptor ->ElemCount)) {1886cmsCloseIOhandler(MemIO);1887goto Error;1888}18891890// Get Size and close1891rc = MemIO ->Tell(MemIO);1892cmsCloseIOhandler(MemIO); // Ignore return code this time18931894_cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);1895return rc;18961897Error:1898_cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);1899return 0;1900}19011902// Similar to the anterior. This function allows to write directly to the ICC profile any data, without1903// checking anything. As a rule, mixing Raw with cooked doesn't work, so writing a tag as raw and then reading1904// it as cooked without serializing does result into an error. If that is what you want, you will need to dump1905// the profile to memry or disk and then reopen it.1906cmsBool CMSEXPORT cmsWriteRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data, cmsUInt32Number Size)1907{1908_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;1909int i;19101911if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0;19121913if (!_cmsNewTag(Icc, sig, &i)) {1914_cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);1915return FALSE;1916}19171918// Mark the tag as being written as RAW1919Icc ->TagSaveAsRaw[i] = TRUE;1920Icc ->TagNames[i] = sig;1921Icc ->TagLinked[i] = (cmsTagSignature) 0;19221923// Keep a copy of the block1924Icc ->TagPtrs[i] = _cmsDupMem(Icc ->ContextID, data, Size);1925Icc ->TagSizes[i] = Size;19261927_cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);19281929if (Icc->TagPtrs[i] == NULL) {1930Icc->TagNames[i] = (cmsTagSignature) 0;1931return FALSE;1932}1933return TRUE;1934}19351936// Using this function you can collapse several tag entries to the same block in the profile1937cmsBool CMSEXPORT cmsLinkTag(cmsHPROFILE hProfile, cmsTagSignature sig, cmsTagSignature dest)1938{1939_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;1940int i;19411942if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return FALSE;19431944if (!_cmsNewTag(Icc, sig, &i)) {1945_cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);1946return FALSE;1947}19481949// Keep necessary information1950Icc ->TagSaveAsRaw[i] = FALSE;1951Icc ->TagNames[i] = sig;1952Icc ->TagLinked[i] = dest;19531954Icc ->TagPtrs[i] = NULL;1955Icc ->TagSizes[i] = 0;1956Icc ->TagOffsets[i] = 0;19571958_cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);1959return TRUE;1960}196119621963// Returns the tag linked to sig, in the case two tags are sharing same resource1964cmsTagSignature CMSEXPORT cmsTagLinkedTo(cmsHPROFILE hProfile, cmsTagSignature sig)1965{1966_cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;1967int i;19681969// Search for given tag in ICC profile directory1970i = _cmsSearchTag(Icc, sig, FALSE);1971if (i < 0) return (cmsTagSignature) 0; // Not found, return 019721973return Icc -> TagLinked[i];1974}197519761977