summaryrefslogtreecommitdiffstats
path: root/debian/lcms/lcms-1.19.dfsg2/src/cmsio1.c
diff options
context:
space:
mode:
Diffstat (limited to 'debian/lcms/lcms-1.19.dfsg2/src/cmsio1.c')
-rwxr-xr-xdebian/lcms/lcms-1.19.dfsg2/src/cmsio1.c3727
1 files changed, 3727 insertions, 0 deletions
diff --git a/debian/lcms/lcms-1.19.dfsg2/src/cmsio1.c b/debian/lcms/lcms-1.19.dfsg2/src/cmsio1.c
new file mode 100755
index 00000000..e08af468
--- /dev/null
+++ b/debian/lcms/lcms-1.19.dfsg2/src/cmsio1.c
@@ -0,0 +1,3727 @@
+//
+// Little cms
+// Copyright (C) 1998-2007 Marti Maria
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the "Software"),
+// to deal in the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the Software
+// is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
+// THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// ICC profile serialization
+
+
+#include "lcms.h"
+
+// ----------------------------------------------------------------- Tag Serialization
+
+// Alignment of ICC file format uses 4 bytes DWORD
+
+#define ALIGNLONG(x) (((x)+3) & ~(3)) // Aligns to DWORD boundary
+
+
+static int GlobalLanguageCode; // Language & country descriptors, for ICC 4.0 support
+static int GlobalCountryCode;
+
+
+#ifdef __BEOS__
+# define USE_CUSTOM_SWAB 1
+#endif
+
+
+#ifdef USE_CUSTOM_SWAB
+
+// Replacement to swab function, thanks to YNOP
+// for providing the BeOS port
+//
+// from: @(#)swab.c 5.10 (Berkeley) 3/6/91
+
+static
+void xswab(const void *from, void *to, size_t len)
+{
+ register unsigned long temp;
+ register int n;
+ register char *fp, *tp;
+
+ n = (len >> 1) + 1;
+ fp = (char *)from;
+ tp = (char *)to;
+#define STEP temp = *fp++,*tp++ = *fp++,*tp++ = temp
+ /* round to multiple of 8 */
+ while ((--n) & 07)
+ STEP;
+ n >>= 3;
+ while (--n >= 0) {
+
+ STEP; STEP; STEP; STEP;
+ STEP; STEP; STEP; STEP;
+ }
+#undef STEP
+}
+#else
+#define xswab swab
+#endif
+
+
+//
+// Little-Endian to Big-Endian
+//
+
+#ifdef USE_BIG_ENDIAN
+#define AdjustEndianess16(a)
+#define AdjustEndianess32(a)
+#define AdjustEndianessArray16(a, b)
+#else
+
+static
+void AdjustEndianess16(LPBYTE pByte)
+{
+ BYTE tmp;
+
+ tmp = pByte[0];
+ pByte[0] = pByte[1];
+ pByte[1] = tmp;
+}
+
+static
+void AdjustEndianess32(LPBYTE pByte)
+{
+ BYTE temp1;
+ BYTE temp2;
+
+ temp1 = *pByte++;
+ temp2 = *pByte++;
+ *(pByte-1) = *pByte;
+ *pByte++ = temp2;
+ *(pByte-3) = *pByte;
+ *pByte = temp1;
+}
+
+
+// swap bytes in a array of words
+
+static
+void AdjustEndianessArray16(LPWORD p, size_t num_words)
+{
+ xswab((char*) p, (char*)p, (int) num_words * sizeof(WORD));
+}
+
+#endif
+
+
+// Transports to properly encoded values - note that icc profiles does use
+// big endian notation.
+
+static
+icInt32Number TransportValue32(icInt32Number Value)
+{
+ icInt32Number Temp = Value;
+
+ AdjustEndianess32((LPBYTE) &Temp);
+ return Temp;
+}
+
+static
+WORD TransportValue16(WORD Value)
+{
+ WORD Temp = Value;
+
+ AdjustEndianess16((LPBYTE) &Temp);
+ return Temp;
+}
+
+
+// from Fixed point 8.8 to double
+
+static
+double Convert8Fixed8(WORD fixed8)
+{
+ BYTE msb, lsb;
+
+ lsb = (BYTE) (fixed8 & 0xff);
+ msb = (BYTE) (((WORD) fixed8 >> 8) & 0xff);
+
+ return (double) ((double) msb + ((double) lsb / 256.0));
+}
+
+
+// from Fixed point 15.16 to double
+static
+double Convert15Fixed16(icS15Fixed16Number fix32)
+{
+ double floater, sign, mid, hack;
+ int Whole, FracPart;
+
+
+ AdjustEndianess32((LPBYTE) &fix32);
+
+ sign = (fix32 < 0 ? -1 : 1);
+ fix32 = abs(fix32);
+
+ Whole = LOWORD(fix32 >> 16);
+ FracPart = LOWORD(fix32 & 0x0000ffffL);
+
+ hack = 65536.0;
+ mid = (double) FracPart / hack;
+ floater = (double) Whole + mid;
+
+ return sign * floater;
+}
+
+
+// Auxiliar-- read base and return type
+
+static
+icTagTypeSignature ReadBase(LPLCMSICCPROFILE Icc)
+{
+ icTagBase Base;
+
+ if (Icc -> Read(&Base, sizeof(icTagBase), 1, Icc) != 1)
+ return (icTagTypeSignature) 0;
+ AdjustEndianess32((LPBYTE) &Base.sig);
+
+ return Base.sig;
+}
+
+
+static
+void DecodeDateTimeNumber(const icDateTimeNumber *Source, struct tm *Dest)
+{
+ Dest->tm_sec = TransportValue16(Source->seconds);
+ Dest->tm_min = TransportValue16(Source->minutes);
+ Dest->tm_hour = TransportValue16(Source->hours);
+ Dest->tm_mday = TransportValue16(Source->day);
+ Dest->tm_mon = TransportValue16(Source->month) - 1;
+ Dest->tm_year = TransportValue16(Source->year) - 1900;
+ Dest->tm_wday = -1;
+ Dest->tm_yday = -1;
+ Dest->tm_isdst = 0;
+}
+
+static
+void EncodeDateTimeNumber(icDateTimeNumber *Dest, const struct tm *Source)
+{
+ Dest->seconds = TransportValue16((WORD) Source->tm_sec);
+ Dest->minutes = TransportValue16((WORD) Source->tm_min);
+ Dest->hours = TransportValue16((WORD) Source->tm_hour);
+ Dest->day = TransportValue16((WORD) Source->tm_mday);
+ Dest->month = TransportValue16((WORD) (Source->tm_mon + 1));
+ Dest->year = TransportValue16((WORD) (Source->tm_year + 1900));
+}
+
+
+// Jun-21-2000: Some profiles (those that comes with W2K) comes
+// with the media white (media black?) x 100. Add a sanity check
+
+static
+void NormalizeXYZ(LPcmsCIEXYZ Dest)
+{
+ while (Dest -> X > 2. &&
+ Dest -> Y > 2. &&
+ Dest -> Z > 2.) {
+
+ Dest -> X /= 10.;
+ Dest -> Y /= 10.;
+ Dest -> Z /= 10.;
+ }
+}
+
+// Evaluates a XYZ tristimulous across chromatic adaptation matrix
+
+static
+void EvalCHRM(LPcmsCIEXYZ Dest, LPMAT3 Chrm, LPcmsCIEXYZ Src)
+{
+ VEC3 d, s;
+
+ s.n[VX] = Src -> X;
+ s.n[VY] = Src -> Y;
+ s.n[VZ] = Src -> Z;
+
+ MAT3eval(&d, Chrm, &s);
+
+ Dest ->X = d.n[VX];
+ Dest ->Y = d.n[VY];
+ Dest ->Z = d.n[VZ];
+
+}
+
+
+// Read profile header and validate it
+
+static
+LPLCMSICCPROFILE ReadHeader(LPLCMSICCPROFILE Icc, LCMSBOOL lIsFromMemory)
+{
+ icTag Tag;
+ icHeader Header;
+ icInt32Number TagCount, i;
+ icUInt32Number extent;
+
+ if (Icc -> Read(&Header, sizeof(icHeader), 1, Icc) != 1)
+ goto ErrorCleanup;
+
+ // Convert endian
+
+ AdjustEndianess32((LPBYTE) &Header.size);
+ AdjustEndianess32((LPBYTE) &Header.cmmId);
+ AdjustEndianess32((LPBYTE) &Header.version);
+ AdjustEndianess32((LPBYTE) &Header.deviceClass);
+ AdjustEndianess32((LPBYTE) &Header.colorSpace);
+ AdjustEndianess32((LPBYTE) &Header.pcs);
+ AdjustEndianess32((LPBYTE) &Header.magic);
+ AdjustEndianess32((LPBYTE) &Header.flags);
+ AdjustEndianess32((LPBYTE) &Header.attributes[0]);
+ AdjustEndianess32((LPBYTE) &Header.renderingIntent);
+
+ // Validate it
+
+ if (Header.magic != icMagicNumber) goto ErrorCleanup;
+
+ if (Icc ->Read(&TagCount, sizeof(icInt32Number), 1, Icc) != 1)
+ goto ErrorCleanup;
+
+ AdjustEndianess32((LPBYTE) &TagCount);
+
+ Icc -> DeviceClass = Header.deviceClass;
+ Icc -> ColorSpace = Header.colorSpace;
+ Icc -> PCS = Header.pcs;
+ Icc -> RenderingIntent = (icRenderingIntent) Header.renderingIntent;
+ Icc -> flags = Header.flags;
+ Icc -> attributes = Header.attributes[0];
+ Icc -> Illuminant.X = Convert15Fixed16(Header.illuminant.X);
+ Icc -> Illuminant.Y = Convert15Fixed16(Header.illuminant.Y);
+ Icc -> Illuminant.Z = Convert15Fixed16(Header.illuminant.Z);
+ Icc -> Version = Header.version;
+
+ // Get creation date/time
+
+ DecodeDateTimeNumber(&Header.date, &Icc ->Created);
+
+ // Fix illuminant, some profiles are broken in this field!
+
+ Icc ->Illuminant = *cmsD50_XYZ();
+
+ // The profile ID are 16 raw bytes
+
+ CopyMemory(Icc ->ProfileID, Header.reserved, 16);
+
+ // Get rid of possible wrong profiles
+
+ NormalizeXYZ(&Icc -> Illuminant);
+
+ // Read tag directory
+
+ if (TagCount > MAX_TABLE_TAG || TagCount < 0) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "Too many tags (%d)", TagCount);
+ goto ErrorCleanup;
+ }
+
+ Icc -> TagCount = TagCount;
+ for (i=0; i < TagCount; i++) {
+
+ if (Icc ->Read(&Tag, sizeof(icTag), 1, Icc) != 1)
+ goto ErrorCleanup;
+
+ AdjustEndianess32((LPBYTE) &Tag.offset);
+ AdjustEndianess32((LPBYTE) &Tag.size);
+ AdjustEndianess32((LPBYTE) &Tag.sig); // Signature
+
+ // Perform some sanity check. Offset + size should fall inside file.
+ extent = Tag.offset + Tag.size;
+ if (extent > Header.size || extent < Tag.offset)
+ goto ErrorCleanup;
+
+ Icc -> TagNames[i] = Tag.sig;
+ Icc -> TagOffsets[i] = Tag.offset;
+ Icc -> TagSizes[i] = Tag.size;
+ }
+
+ return Icc;
+
+
+ErrorCleanup:
+
+ Icc ->Close(Icc);
+
+ if (lIsFromMemory)
+ cmsSignalError(LCMS_ERRC_ABORTED, "Corrupted memory profile");
+ else
+ cmsSignalError(LCMS_ERRC_ABORTED, "Corrupted profile: '%s'", Icc->PhysicalFile);
+
+
+ _cmsFree(Icc);
+ return NULL;
+}
+
+static
+unsigned int uipow(unsigned int a, unsigned int b) {
+ unsigned int rv = 1;
+ for (; b > 0; b--)
+ rv *= a;
+ return rv;
+}
+
+
+
+// Convert between notations.
+
+#define TO16_TAB(x) (WORD) (((x) << 8) | (x))
+
+
+// LUT8 can come only in Lab space. There is a fatal flaw in
+// converting from Lut8 to Lut16. Due to particular encoding
+// of Lab, different actions should be taken from input and
+// output Lab8 LUTS. For input, is as easy as applying a << 8,
+// since numbers comes in fixed point. However, for output LUT
+// things goes a bit more complex.... LUT 16 is supposed to
+// have a domain of 0..ff00, so we should remap the LUT in order
+// to get things working. Affected signatures are B2Axx tags,
+// preview and gamut.
+
+// I do solve it by multiplying input matrix by:
+//
+// | 0xffff/0xff00 0 0 |
+// | 0 0xffff/0xff00 0 |
+// | 0 0 0xffff/0xff00 |
+//
+// The input values got then remapped to adequate domain
+
+static
+void FixLUT8(LPLUT Lut, icTagSignature sig, size_t nTabSize)
+{
+ MAT3 Fixup, Original, Result;
+ LPWORD PtrW;
+ size_t i;
+
+ switch (sig) {
+
+
+ case icSigBToA0Tag:
+ case icSigBToA1Tag:
+ case icSigBToA2Tag:
+ case icSigGamutTag:
+ case icSigPreview0Tag:
+ case icSigPreview1Tag:
+ case icSigPreview2Tag:
+
+
+ VEC3init(&Fixup.v[0], (double) 0xFFFF/0xFF00, 0, 0);
+ VEC3init(&Fixup.v[1], 0, (double) 0xFFFF/0xFF00, 0);
+ VEC3init(&Fixup.v[2], 0, 0, (double) 0xFFFF/0xFF00);
+
+
+ MAT3fromFix(&Original, &Lut->Matrix);
+ MAT3per(&Result, &Original, &Fixup);
+ MAT3toFix(&Lut->Matrix, &Result);
+
+ Lut -> wFlags |= LUT_HASMATRIX;
+ break;
+
+ // For input, clear low part since this has to be
+ // Lab in fixed point
+
+ default:
+
+ PtrW = Lut -> T;
+ for (i = 0; i < nTabSize; i++) {
+
+ *PtrW++ &= 0xFF00;
+ }
+ }
+
+}
+
+// On Lab -> Lab abstract or Lab identities, fix both sides of LUT
+
+static
+void FixLUT8bothSides(LPLUT Lut, size_t nTabSize)
+{
+ MAT3 Fixup, Original, Result;
+ LPWORD PtrW;
+ size_t i;
+
+ VEC3init(&Fixup.v[0], (double) 0xFFFF/0xFF00, 0, 0);
+ VEC3init(&Fixup.v[1], 0, (double) 0xFFFF/0xFF00, 0);
+ VEC3init(&Fixup.v[2], 0, 0, (double) 0xFFFF/0xFF00);
+
+ MAT3fromFix(&Original, &Lut->Matrix);
+ MAT3per(&Result, &Original, &Fixup);
+ MAT3toFix(&Lut->Matrix, &Result);
+
+ Lut -> wFlags |= LUT_HASMATRIX;
+
+ PtrW = Lut -> T;
+ for (i = 0; i < nTabSize; i++) {
+
+ *PtrW++ &= 0xFF00;
+ }
+
+}
+
+
+// The infamous LUT 8
+
+static
+LCMSBOOL ReadLUT8(LPLCMSICCPROFILE Icc, LPLUT NewLUT, icTagSignature sig)
+{
+ icLut8 LUT8;
+ LPBYTE Temp;
+ size_t nTabSize;
+ unsigned int i, j;
+ unsigned int AllLinear;
+ LPWORD PtrW;
+
+ if (Icc ->Read(&LUT8, sizeof(icLut8) - SIZEOF_UINT8_ALIGNED, 1, Icc) != 1) return FALSE;
+
+ NewLUT -> wFlags = LUT_HASTL1|LUT_HASTL2|LUT_HAS3DGRID;
+ NewLUT -> cLutPoints = LUT8.clutPoints;
+ NewLUT -> InputChan = LUT8.inputChan;
+ NewLUT -> OutputChan = LUT8.outputChan;
+ NewLUT -> InputEntries = 256;
+ NewLUT -> OutputEntries = 256;
+
+ // Do some checking
+ if (!_cmsValidateLUT(NewLUT)) {
+ return FALSE;
+ }
+
+ AdjustEndianess32((LPBYTE) &LUT8.e00);
+ AdjustEndianess32((LPBYTE) &LUT8.e01);
+ AdjustEndianess32((LPBYTE) &LUT8.e02);
+ AdjustEndianess32((LPBYTE) &LUT8.e10);
+ AdjustEndianess32((LPBYTE) &LUT8.e11);
+ AdjustEndianess32((LPBYTE) &LUT8.e12);
+ AdjustEndianess32((LPBYTE) &LUT8.e20);
+ AdjustEndianess32((LPBYTE) &LUT8.e21);
+ AdjustEndianess32((LPBYTE) &LUT8.e22);
+
+
+ // Matrix handling
+
+ NewLUT -> Matrix.v[0].n[0] = (Fixed32) LUT8.e00;
+ NewLUT -> Matrix.v[0].n[1] = (Fixed32) LUT8.e01;
+ NewLUT -> Matrix.v[0].n[2] = (Fixed32) LUT8.e02;
+ NewLUT -> Matrix.v[1].n[0] = (Fixed32) LUT8.e10;
+ NewLUT -> Matrix.v[1].n[1] = (Fixed32) LUT8.e11;
+ NewLUT -> Matrix.v[1].n[2] = (Fixed32) LUT8.e12;
+ NewLUT -> Matrix.v[2].n[0] = (Fixed32) LUT8.e20;
+ NewLUT -> Matrix.v[2].n[1] = (Fixed32) LUT8.e21;
+ NewLUT -> Matrix.v[2].n[2] = (Fixed32) LUT8.e22;
+
+
+ // Only operates if not identity...
+
+ if ((NewLUT -> InputChan == 3) && !MAT3isIdentity(&NewLUT -> Matrix, 0.0001)) {
+
+ NewLUT -> wFlags |= LUT_HASMATRIX;
+ }
+
+
+ // Copy input tables
+
+ Temp = (LPBYTE) _cmsMalloc(256);
+ if (Temp == NULL) return FALSE;
+
+ AllLinear = 0;
+ for (i=0; i < NewLUT -> InputChan; i++) {
+
+ PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * 256);
+ if (PtrW == NULL) {
+ _cmsFree(Temp);
+ return FALSE;
+ }
+
+ NewLUT -> L1[i] = PtrW;
+ if (Icc ->Read(Temp, 1, 256, Icc) != 256) {
+ _cmsFree(Temp);
+ return FALSE;
+ }
+
+ for (j=0; j < 256; j++)
+ PtrW[j] = TO16_TAB(Temp[j]);
+ AllLinear += cmsIsLinear(NewLUT -> L1[i], NewLUT -> InputEntries);
+ }
+
+ // Linear input, so ignore full step
+
+ if (AllLinear == NewLUT -> InputChan) {
+
+ NewLUT -> wFlags &= ~LUT_HASTL1;
+ }
+
+ _cmsFree(Temp);
+
+ // Copy 3D CLUT
+
+ nTabSize = (NewLUT -> OutputChan * uipow(NewLUT->cLutPoints,
+ NewLUT->InputChan));
+
+ if (nTabSize > 0) {
+
+ PtrW = (LPWORD) _cmsCalloc(sizeof(WORD), nTabSize);
+ if (PtrW == NULL) return FALSE;
+
+ Temp = (LPBYTE) _cmsMalloc(nTabSize);
+ if (Temp == NULL) {
+ _cmsFree(PtrW);
+ return FALSE;
+ }
+
+ if (Icc ->Read(Temp, 1, nTabSize, Icc) != nTabSize) {
+ _cmsFree(Temp);
+ _cmsFree(PtrW);
+ return FALSE;
+ }
+
+ NewLUT -> T = PtrW;
+ NewLUT -> Tsize = (unsigned int) (nTabSize * sizeof(WORD));
+
+ for (i = 0; i < nTabSize; i++) {
+
+ *PtrW++ = TO16_TAB(Temp[i]);
+ }
+ _cmsFree(Temp);
+ }
+ else {
+ NewLUT ->T = NULL;
+ NewLUT ->Tsize = 0;
+ NewLUT ->wFlags &= ~LUT_HAS3DGRID;
+ }
+
+
+ // Copy output tables
+
+ Temp = (LPBYTE) _cmsMalloc(256);
+ if (Temp == NULL) {
+ return FALSE;
+ }
+
+ AllLinear = 0;
+ for (i=0; i < NewLUT -> OutputChan; i++) {
+
+ PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * 256);
+ if (PtrW == NULL) {
+ _cmsFree(Temp);
+ return FALSE;
+ }
+
+ NewLUT -> L2[i] = PtrW;
+ if (Icc ->Read(Temp, 1, 256, Icc) != 256) {
+ _cmsFree(Temp);
+ return FALSE;
+ }
+
+ for (j=0; j < 256; j++)
+ PtrW[j] = TO16_TAB(Temp[j]);
+ AllLinear += cmsIsLinear(NewLUT -> L2[i], 256);
+ }
+
+ // Linear input, so ignore full step
+
+ if (AllLinear == NewLUT -> OutputChan) {
+
+ NewLUT -> wFlags &= ~LUT_HASTL2;
+ }
+
+
+ _cmsFree(Temp);
+
+ cmsCalcL16Params(NewLUT -> InputEntries, &NewLUT -> In16params);
+ cmsCalcL16Params(NewLUT -> OutputEntries, &NewLUT -> Out16params);
+ cmsCalcCLUT16Params(NewLUT -> cLutPoints, NewLUT -> InputChan,
+ NewLUT -> OutputChan,
+ &NewLUT -> CLut16params);
+ // Fixup
+
+ if (Icc ->PCS == icSigLabData) {
+
+ // Abstract or Lab identity
+
+ if (Icc -> ColorSpace == icSigLabData)
+
+ FixLUT8bothSides(NewLUT, nTabSize);
+ else
+ FixLUT8(NewLUT, sig, nTabSize);
+
+
+ // Now some additional fixup. Lab encoding on 8 bit makes
+ // impossible to place gray axis on a exact node. However,
+ // some profiles does claim to do that. Poor lcms will try
+ // to detect such condition and fix up "on the fly".
+
+ switch (sig) {
+
+ case icSigBToA0Tag:
+ case icSigBToA1Tag:
+ case icSigBToA2Tag:
+ case icSigGamutTag:
+ case icSigPreview0Tag:
+ case icSigPreview1Tag:
+ case icSigPreview2Tag:
+ {
+ LPWORD WhiteLab, ExpectedWhite;
+ WORD WhiteFixed[MAXCHANNELS], WhiteUnfixed[MAXCHANNELS];
+ int j, nChannels;
+ double Dist, DistFixed, DistUnfixed;
+
+ _cmsEndPointsBySpace(icSigLabData, &WhiteLab, NULL, NULL);
+
+ if (_cmsEndPointsBySpace(Icc -> ColorSpace,
+ &ExpectedWhite, NULL, &nChannels)) {
+
+ // 1.- Find white obtained by both combinations
+
+ NewLUT -> FixGrayAxes = FALSE;
+ cmsEvalLUT(NewLUT, WhiteLab, WhiteUnfixed);
+
+ NewLUT -> FixGrayAxes = TRUE;
+ cmsEvalLUT(NewLUT, WhiteLab, WhiteFixed);
+
+ // 2.- Which method gives closer white?
+
+ DistFixed = DistUnfixed = 0;
+ for (j=0; j < nChannels; j++) {
+
+ Dist = ExpectedWhite[j] - WhiteFixed[j];
+ DistFixed += Dist*Dist;
+ Dist = ExpectedWhite[j] - WhiteUnfixed[j];
+ DistUnfixed += Dist*Dist;
+ }
+
+ // 3.- Decide method
+
+ if (sqrt(DistFixed) < sqrt(DistUnfixed))
+ NewLUT -> FixGrayAxes = TRUE;
+ else
+ NewLUT -> FixGrayAxes = FALSE;
+ }
+
+ }
+ break;
+
+ default:;
+ }
+ }
+
+ return TRUE;
+}
+
+
+
+
+// Case LUT 16
+
+static
+LCMSBOOL ReadLUT16(LPLCMSICCPROFILE Icc, LPLUT NewLUT)
+{
+ icLut16 LUT16;
+ size_t nTabSize;
+ unsigned int i;
+ unsigned int AllLinear;
+ LPWORD PtrW;
+
+
+ if (Icc ->Read(&LUT16, sizeof(icLut16)- SIZEOF_UINT16_ALIGNED, 1, Icc) != 1)
+ return FALSE;
+
+ NewLUT -> wFlags = LUT_HASTL1 | LUT_HASTL2 | LUT_HAS3DGRID;
+ NewLUT -> cLutPoints = LUT16.clutPoints;
+ NewLUT -> InputChan = LUT16.inputChan;
+ NewLUT -> OutputChan = LUT16.outputChan;
+
+ AdjustEndianess16((LPBYTE) &LUT16.inputEnt);
+ AdjustEndianess16((LPBYTE) &LUT16.outputEnt);
+
+ NewLUT -> InputEntries = LUT16.inputEnt;
+ NewLUT -> OutputEntries = LUT16.outputEnt;
+
+ if (!_cmsValidateLUT(NewLUT)) {
+ return FALSE;
+ }
+
+ // Matrix handling
+
+ AdjustEndianess32((LPBYTE) &LUT16.e00);
+ AdjustEndianess32((LPBYTE) &LUT16.e01);
+ AdjustEndianess32((LPBYTE) &LUT16.e02);
+ AdjustEndianess32((LPBYTE) &LUT16.e10);
+ AdjustEndianess32((LPBYTE) &LUT16.e11);
+ AdjustEndianess32((LPBYTE) &LUT16.e12);
+ AdjustEndianess32((LPBYTE) &LUT16.e20);
+ AdjustEndianess32((LPBYTE) &LUT16.e21);
+ AdjustEndianess32((LPBYTE) &LUT16.e22);
+
+ NewLUT -> Matrix.v[0].n[0] = (Fixed32) LUT16.e00;
+ NewLUT -> Matrix.v[0].n[1] = (Fixed32) LUT16.e01;
+ NewLUT -> Matrix.v[0].n[2] = (Fixed32) LUT16.e02;
+ NewLUT -> Matrix.v[1].n[0] = (Fixed32) LUT16.e10;
+ NewLUT -> Matrix.v[1].n[1] = (Fixed32) LUT16.e11;
+ NewLUT -> Matrix.v[1].n[2] = (Fixed32) LUT16.e12;
+ NewLUT -> Matrix.v[2].n[0] = (Fixed32) LUT16.e20;
+ NewLUT -> Matrix.v[2].n[1] = (Fixed32) LUT16.e21;
+ NewLUT -> Matrix.v[2].n[2] = (Fixed32) LUT16.e22;
+
+ // Only operates if not identity...
+
+ if ((NewLUT -> InputChan == 3) && !MAT3isIdentity(&NewLUT -> Matrix, 0.0001)) {
+
+ NewLUT -> wFlags |= LUT_HASMATRIX;
+ }
+
+
+ // Copy input tables
+
+ AllLinear = 0;
+ for (i=0; i < NewLUT -> InputChan; i++) {
+
+ PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * NewLUT -> InputEntries);
+ if (PtrW == NULL) return FALSE;
+
+ NewLUT -> L1[i] = PtrW;
+ if (Icc ->Read(PtrW, sizeof(WORD), NewLUT -> InputEntries, Icc) != NewLUT -> InputEntries) {
+ return FALSE;
+ }
+
+ AdjustEndianessArray16(PtrW, NewLUT -> InputEntries);
+ AllLinear += cmsIsLinear(NewLUT -> L1[i], NewLUT -> InputEntries);
+ }
+
+ // Linear input, so ignore full step
+
+ if (AllLinear == NewLUT -> InputChan) {
+
+ NewLUT -> wFlags &= ~LUT_HASTL1;
+ }
+
+
+ // Copy 3D CLUT
+
+ nTabSize = (NewLUT -> OutputChan * uipow(NewLUT->cLutPoints,
+ NewLUT->InputChan));
+ if (nTabSize > 0) {
+
+ PtrW = (LPWORD) _cmsCalloc(sizeof(WORD), nTabSize);
+ if (PtrW == NULL)
+ return FALSE;
+
+ NewLUT -> T = PtrW;
+ NewLUT -> Tsize = (unsigned int) (nTabSize * sizeof(WORD));
+
+ if (Icc -> Read(PtrW, sizeof(WORD), nTabSize, Icc) != nTabSize) {
+ return FALSE;
+ }
+
+ AdjustEndianessArray16(NewLUT -> T, nTabSize);
+ }
+ else {
+ NewLUT ->T = NULL;
+ NewLUT ->Tsize = 0;
+ NewLUT -> wFlags &= ~LUT_HAS3DGRID;
+ }
+
+ // Copy output tables
+
+ AllLinear = 0;
+ for (i=0; i < NewLUT -> OutputChan; i++) {
+
+ PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * NewLUT -> OutputEntries);
+ if (PtrW == NULL) {
+ return FALSE;
+ }
+
+ NewLUT -> L2[i] = PtrW;
+ if (Icc ->Read(PtrW, sizeof(WORD), NewLUT -> OutputEntries, Icc) != NewLUT -> OutputEntries) {
+ return FALSE;
+ }
+
+ AdjustEndianessArray16(PtrW, NewLUT -> OutputEntries);
+ AllLinear += cmsIsLinear(NewLUT -> L2[i], NewLUT -> OutputEntries);
+ }
+
+ // Linear output, ignore step
+
+ if (AllLinear == NewLUT -> OutputChan)
+ {
+ NewLUT -> wFlags &= ~LUT_HASTL2;
+ }
+
+
+ cmsCalcL16Params(NewLUT -> InputEntries, &NewLUT -> In16params);
+ cmsCalcL16Params(NewLUT -> OutputEntries, &NewLUT -> Out16params);
+ cmsCalcCLUT16Params(NewLUT -> cLutPoints, NewLUT -> InputChan,
+ NewLUT -> OutputChan,
+ &NewLUT -> CLut16params);
+
+ return TRUE;
+}
+
+
+// This is a shared routine for reading curves. It can handle v2 curves
+// as linear, single gamma and table-based as well as v4 parametric curves.
+
+static
+LPGAMMATABLE ReadCurve(LPLCMSICCPROFILE Icc)
+{
+ icUInt32Number Count;
+ LPGAMMATABLE NewGamma;
+ icTagTypeSignature BaseType;
+ int n;
+
+
+ BaseType = ReadBase(Icc);
+ switch (BaseType) {
+
+
+ case ((icTagTypeSignature) 0x9478ee00): // Monaco 2 profiler is BROKEN!
+ case icSigCurveType:
+
+ if (Icc ->Read(&Count, sizeof(icUInt32Number), 1, Icc) != 1) return NULL;
+ AdjustEndianess32((LPBYTE) &Count);
+
+ switch (Count) {
+
+ case 0: // Linear.
+
+ NewGamma = cmsAllocGamma(2);
+ if (!NewGamma) return NULL;
+ NewGamma -> GammaTable[0] = 0;
+ NewGamma -> GammaTable[1] = 0xFFFF;
+ return NewGamma;
+
+ case 1: // Specified as the exponent of gamma function
+ {
+ WORD SingleGammaFixed;
+
+ if (Icc ->Read(&SingleGammaFixed, sizeof(WORD), 1, Icc) != 1) return NULL;
+ AdjustEndianess16((LPBYTE) &SingleGammaFixed);
+ return cmsBuildGamma(4096, Convert8Fixed8(SingleGammaFixed));
+ }
+
+ default: { // Curve
+
+ NewGamma = cmsAllocGamma(Count);
+ if (!NewGamma) return NULL;
+
+ if (Icc ->Read(NewGamma -> GammaTable, sizeof(WORD), Count, Icc) != Count)
+ return NULL;
+ AdjustEndianessArray16(NewGamma -> GammaTable, Count);
+ return NewGamma;
+ }
+ }
+ break;
+
+
+ // Parametric curves
+ case icSigParametricCurveType: {
+
+ int ParamsByType[] = { 1, 3, 4, 5, 7 };
+ double Params[10];
+ icS15Fixed16Number Num;
+ icUInt32Number Reserved;
+ icUInt16Number Type;
+ int i;
+
+ if (Icc -> Read(&Type, sizeof(icUInt16Number), 1, Icc) != 1) return NULL;
+ if (Icc -> Read(&Reserved, sizeof(icUInt16Number), 1, Icc) != 1) return NULL;
+
+ AdjustEndianess16((LPBYTE) &Type);
+ if (Type > 4) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "Unknown parametric curve type '%d' found.", Type);
+ return NULL;
+ }
+
+ ZeroMemory(Params, 10* sizeof(double));
+ n = ParamsByType[Type];
+
+ for (i=0; i < n; i++) {
+ Num = 0;
+ if (Icc -> Read(&Num, sizeof(icS15Fixed16Number), 1, Icc) != 1) return NULL;
+ Params[i] = Convert15Fixed16(Num);
+ }
+
+
+ NewGamma = cmsBuildParametricGamma(4096, Type+1, Params);
+ return NewGamma;
+ }
+
+
+ default:
+ cmsSignalError(LCMS_ERRC_ABORTED, "Bad tag signature '%lx' found.", BaseType);
+ return NULL;
+ }
+
+}
+
+
+// Similar to anterior, but curve is reversed
+
+static
+LPGAMMATABLE ReadCurveReversed(LPLCMSICCPROFILE Icc)
+{
+
+ icTagTypeSignature BaseType;
+ LPGAMMATABLE NewGamma, ReturnGamma;
+ icUInt32Number Count;
+ int n;
+
+
+ BaseType = ReadBase(Icc);
+
+ switch (BaseType) {
+
+
+ case 0x9478ee00L: // Monaco 2 profiler is BROKEN!
+ case icSigCurveType:
+
+ if (Icc -> Read(&Count, sizeof(icUInt32Number), 1, Icc) != 1) return NULL;
+ AdjustEndianess32((LPBYTE) &Count);
+
+
+ switch (Count) {
+
+ case 0: // Linear, reverse is same.
+
+ NewGamma = cmsAllocGamma(2);
+ if (!NewGamma) return NULL;
+
+ NewGamma -> GammaTable[0] = 0;
+ NewGamma -> GammaTable[1] = 0xFFFF;
+ return NewGamma;
+
+ case 1: {
+ WORD SingleGammaFixed;
+
+ if (Icc -> Read(&SingleGammaFixed, sizeof(WORD), 1, Icc) != 1) return NULL;
+ AdjustEndianess16((LPBYTE) &SingleGammaFixed);
+ return cmsBuildGamma(4096, 1./Convert8Fixed8(SingleGammaFixed));
+ }
+
+ default: { // Curve. Do our best to trying to reverse the curve
+
+ NewGamma = cmsAllocGamma(Count);
+ if (!NewGamma) return NULL;
+
+ if (Icc -> Read(NewGamma -> GammaTable, sizeof(WORD), Count, Icc) != Count)
+ return NULL;
+
+ AdjustEndianessArray16(NewGamma -> GammaTable, Count);
+
+ if (Count < 256)
+ Count = 256; // Reverse of simple curve has not necesarely to be simple
+
+ ReturnGamma = cmsReverseGamma(Count, NewGamma);
+ cmsFreeGamma(NewGamma);
+
+ return ReturnGamma;
+ }
+ }
+ break;
+
+
+ // Parametric curves
+ case icSigParametricCurveType: {
+
+ int ParamsByType[] = { 1, 3, 4, 5, 7 };
+ double Params[10];
+ icS15Fixed16Number Num;
+ icUInt32Number Reserved;
+ icUInt16Number Type;
+ int i;
+
+
+ if (Icc -> Read(&Type, sizeof(icUInt16Number), 1, Icc) != 1) return NULL;
+ if (Icc -> Read(&Reserved, sizeof(icUInt16Number), 1, Icc) != 1) return NULL;
+
+ AdjustEndianess16((LPBYTE) &Type);
+ if (Type > 4) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "Unknown parametric curve type '%d' found.", Type);
+ return NULL;
+ }
+
+ ZeroMemory(Params, 10* sizeof(double));
+ n = ParamsByType[Type];
+
+ for (i=0; i < n; i++) {
+ if (Icc -> Read(&Num, sizeof(icS15Fixed16Number), 1, Icc) != 1) return NULL;
+ Params[i] = Convert15Fixed16(Num);
+ }
+
+
+ // Negative type as a mark of reversed curve
+ NewGamma = cmsBuildParametricGamma(4096, -(Type+1), Params);
+ return NewGamma;
+ }
+
+
+ default:
+ cmsSignalError(LCMS_ERRC_ABORTED, "Bad tag signature '%lx' found.", BaseType);
+ return NULL;
+ }
+
+}
+
+
+// V4 stuff. Read matrix for LutAtoB and LutBtoA
+
+static
+LCMSBOOL ReadMatrixOffset(LPLCMSICCPROFILE Icc, size_t Offset, LPLUT NewLUT, DWORD dwFlags)
+{
+
+ icS15Fixed16Number All[12];
+ int i;
+ MAT3 m;
+ VEC3 o;
+
+ if (Icc -> Seek(Icc, Offset)) return FALSE;
+
+ if (Icc ->Read(All, sizeof(icS15Fixed16Number), 12, Icc) != 12)
+ return FALSE;
+
+ for (i=0; i < 12; i++)
+ AdjustEndianess32((LPBYTE) &All[i]);
+
+
+ m.v[0].n[0] = FIXED_TO_DOUBLE((Fixed32) All[0]);
+ m.v[0].n[1] = FIXED_TO_DOUBLE((Fixed32) All[1]);
+ m.v[0].n[2] = FIXED_TO_DOUBLE((Fixed32) All[2]);
+ m.v[1].n[0] = FIXED_TO_DOUBLE((Fixed32) All[3]);
+ m.v[1].n[1] = FIXED_TO_DOUBLE((Fixed32) All[4]);
+ m.v[1].n[2] = FIXED_TO_DOUBLE((Fixed32) All[5]);
+ m.v[2].n[0] = FIXED_TO_DOUBLE((Fixed32) All[6]);
+ m.v[2].n[1] = FIXED_TO_DOUBLE((Fixed32) All[7]);
+ m.v[2].n[2] = FIXED_TO_DOUBLE((Fixed32) All[8]);
+
+ o.n[0] = FIXED_TO_DOUBLE((Fixed32) All[9]);
+ o.n[1] = FIXED_TO_DOUBLE((Fixed32) All[10]);
+ o.n[2] = FIXED_TO_DOUBLE((Fixed32) All[11]);
+
+ cmsSetMatrixLUT4(NewLUT, &m, &o, dwFlags);
+
+ return TRUE;
+}
+
+
+// V4 stuff. Read CLUT part for LutAtoB and LutBtoA
+
+static
+LCMSBOOL ReadCLUT(LPLCMSICCPROFILE Icc, size_t Offset, LPLUT NewLUT)
+{
+ unsigned int j;
+ icCLutStruct CLUT;
+
+ if (Icc -> Seek(Icc, Offset)) return FALSE;
+ if (Icc ->Read(&CLUT, sizeof(icCLutStruct), 1, Icc) != 1) return FALSE;
+
+
+ for (j=1; j < NewLUT ->InputChan; j++) {
+ if (CLUT.gridPoints[0] != CLUT.gridPoints[j]) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "CLUT with different granulatity is currently unsupported.");
+ return FALSE;
+ }
+
+
+ }
+
+ if (cmsAlloc3DGrid(NewLUT, CLUT.gridPoints[0], NewLUT ->InputChan,
+ NewLUT ->OutputChan) == NULL) return FALSE;
+
+ // Precission can be 1 or 2 bytes
+
+ if (CLUT.prec == 1) {
+
+ BYTE v;
+ unsigned int i;
+
+ for (i=0; i < NewLUT->Tsize / sizeof(WORD); i++) {
+ if (Icc ->Read(&v, sizeof(BYTE), 1, Icc) != 1) return FALSE;
+ NewLUT->T[i] = TO16_TAB(v);
+ }
+
+ }
+ else
+ if (CLUT.prec == 2) {
+
+ size_t n = NewLUT->Tsize / sizeof(WORD);
+
+ if (Icc ->Read(NewLUT ->T, sizeof(WORD), n, Icc) != n) return FALSE;
+ AdjustEndianessArray16(NewLUT ->T, NewLUT->Tsize / sizeof(WORD));
+ }
+ else {
+ cmsSignalError(LCMS_ERRC_ABORTED, "Unknow precission of '%d'", CLUT.prec);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static
+void ResampleCurves(LPGAMMATABLE Curves[], int nCurves)
+{
+ int i;
+ LPSAMPLEDCURVE sc;
+
+ for (i=0; i < nCurves; i++) {
+ sc = cmsConvertGammaToSampledCurve(Curves[i], 4096);
+ cmsFreeGamma(Curves[i]);
+ Curves[i] = cmsConvertSampledCurveToGamma(sc, 0xFFFF);
+ cmsFreeSampledCurve(sc);
+ }
+
+}
+
+
+static
+void SkipAlignment(LPLCMSICCPROFILE Icc)
+{
+ BYTE Buffer[4];
+ size_t At = Icc ->Tell(Icc);
+ int BytesToNextAlignedPos = (int) (At % 4);
+
+ Icc ->Read(Buffer, 1, BytesToNextAlignedPos, Icc);
+}
+
+// Read a set of curves from specific offset
+static
+LCMSBOOL ReadSetOfCurves(LPLCMSICCPROFILE Icc, size_t Offset, LPLUT NewLUT, int nLocation)
+{
+ LPGAMMATABLE Curves[MAXCHANNELS];
+ unsigned int i, nCurves;
+
+ if (Icc -> Seek(Icc, Offset)) return FALSE;
+
+ if (nLocation == 1 || nLocation == 3)
+
+ nCurves = NewLUT ->InputChan;
+ else
+ nCurves = NewLUT ->OutputChan;
+
+ ZeroMemory(Curves, sizeof(Curves));
+ for (i=0; i < nCurves; i++) {
+
+ Curves[i] = ReadCurve(Icc);
+ if (Curves[i] == NULL) goto Error;
+ SkipAlignment(Icc);
+ }
+
+ // March-26'08: some V4 profiles may have different sampling
+ // rates, in this case resample all curves to maximum
+
+ for (i=1; i < nCurves; i++) {
+ if (Curves[i]->nEntries != Curves[0]->nEntries) {
+ ResampleCurves(Curves, nCurves);
+ break;
+ }
+ }
+
+ NewLUT = cmsAllocLinearTable(NewLUT, Curves, nLocation);
+ if (NewLUT == NULL) goto Error;
+
+ for (i=0; i < nCurves; i++)
+ cmsFreeGamma(Curves[i]);
+
+ return TRUE;
+
+Error:
+
+ for (i=0; i < nCurves; i++)
+ if (Curves[i])
+ cmsFreeGamma(Curves[i]);
+
+ return FALSE;
+
+
+}
+
+// V4 stuff. LutAtoB type
+//
+// [L1] -> [CLUT] -> [L4] -> [Mat4] -> [Ofs4] -> [L2]
+//
+// Mat, Mat3, Ofs3, L3 are missing
+// L1 = A curves
+// L4 = M curves
+// L2 = B curves
+
+static
+LCMSBOOL ReadLUT_A2B(LPLCMSICCPROFILE Icc, LPLUT NewLUT, size_t BaseOffset, icTagSignature sig)
+{
+ icLutAtoB LUT16;
+
+ if (Icc ->Read(&LUT16, sizeof(icLutAtoB), 1, Icc) != 1) return FALSE;
+
+ NewLUT -> InputChan = LUT16.inputChan;
+ NewLUT -> OutputChan = LUT16.outputChan;
+
+ // Validate the NewLUT here to avoid excessive number of channels
+ // (leading to stack-based buffer overflow in ReadSetOfCurves).
+ // Needs revalidation after table size is filled in.
+ if (!_cmsValidateLUT(NewLUT)) {
+ return FALSE;
+ }
+
+ AdjustEndianess32((LPBYTE) &LUT16.offsetB);
+ AdjustEndianess32((LPBYTE) &LUT16.offsetMat);
+ AdjustEndianess32((LPBYTE) &LUT16.offsetM);
+ AdjustEndianess32((LPBYTE) &LUT16.offsetC);
+ AdjustEndianess32((LPBYTE) &LUT16.offsetA);
+
+ if (LUT16.offsetB != 0)
+ ReadSetOfCurves(Icc, BaseOffset + LUT16.offsetB, NewLUT, 2);
+
+ if (LUT16.offsetMat != 0)
+ ReadMatrixOffset(Icc, BaseOffset + LUT16.offsetMat, NewLUT, LUT_HASMATRIX4);
+
+
+ if (LUT16.offsetM != 0)
+ ReadSetOfCurves(Icc, BaseOffset + LUT16.offsetM, NewLUT, 4);
+
+ if (LUT16.offsetC != 0)
+ ReadCLUT(Icc, BaseOffset + LUT16.offsetC, NewLUT);
+
+ if (LUT16.offsetA!= 0)
+ ReadSetOfCurves(Icc, BaseOffset + LUT16.offsetA, NewLUT, 1);
+
+ // Convert to v2 PCS
+
+ if (Icc ->PCS == icSigLabData) {
+
+ switch (sig) {
+
+ case icSigAToB0Tag:
+ case icSigAToB1Tag:
+ case icSigAToB2Tag:
+ case icSigGamutTag:
+ case icSigPreview0Tag:
+ case icSigPreview1Tag:
+ case icSigPreview2Tag:
+
+ NewLUT ->wFlags |= LUT_V4_INPUT_EMULATE_V2;
+ break;
+
+ default:;
+ }
+ }
+
+
+ return TRUE;
+}
+
+// V4 stuff. LutBtoA type
+
+static
+LCMSBOOL ReadLUT_B2A(LPLCMSICCPROFILE Icc, LPLUT NewLUT, size_t BaseOffset, icTagSignature sig)
+{
+ icLutBtoA LUT16;
+
+ if (Icc ->Read(&LUT16, sizeof(icLutBtoA), 1, Icc) != 1) return FALSE;
+
+ NewLUT -> InputChan = LUT16.inputChan;
+ NewLUT -> OutputChan = LUT16.outputChan;
+
+ // Validate the NewLUT here to avoid excessive number of channels
+ // (leading to stack-based buffer overflow in ReadSetOfCurves).
+ // Needs revalidation after table size is filled in.
+ if (!_cmsValidateLUT(NewLUT)) {
+ return FALSE;
+ }
+
+ AdjustEndianess32((LPBYTE) &LUT16.offsetB);
+ AdjustEndianess32((LPBYTE) &LUT16.offsetMat);
+ AdjustEndianess32((LPBYTE) &LUT16.offsetM);
+ AdjustEndianess32((LPBYTE) &LUT16.offsetC);
+ AdjustEndianess32((LPBYTE) &LUT16.offsetA);
+
+
+ if (LUT16.offsetB != 0)
+ ReadSetOfCurves(Icc, BaseOffset + LUT16.offsetB, NewLUT, 1);
+
+ if (LUT16.offsetMat != 0)
+ ReadMatrixOffset(Icc, BaseOffset + LUT16.offsetMat, NewLUT, LUT_HASMATRIX3);
+
+ if (LUT16.offsetM != 0)
+ ReadSetOfCurves(Icc, BaseOffset + LUT16.offsetM, NewLUT, 3);
+
+ if (LUT16.offsetC != 0)
+ ReadCLUT(Icc, BaseOffset + LUT16.offsetC, NewLUT);
+
+ if (LUT16.offsetA!= 0)
+ ReadSetOfCurves(Icc, BaseOffset + LUT16.offsetA, NewLUT, 2);
+
+
+ // Convert to v2 PCS
+
+ if (Icc ->PCS == icSigLabData) {
+
+ switch (sig) {
+
+ case icSigBToA0Tag:
+ case icSigBToA1Tag:
+ case icSigBToA2Tag:
+ case icSigGamutTag:
+ case icSigPreview0Tag:
+ case icSigPreview1Tag:
+ case icSigPreview2Tag:
+
+ NewLUT ->wFlags |= LUT_V4_OUTPUT_EMULATE_V2;
+ break;
+
+ default:;
+ }
+ }
+
+ return TRUE;
+}
+
+// CLUT main reader
+
+LPLUT LCMSEXPORT cmsReadICCLut(cmsHPROFILE hProfile, icTagSignature sig)
+{
+
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+ icTagTypeSignature BaseType;
+ int n;
+ size_t offset;
+ LPLUT NewLUT;
+
+ n = _cmsSearchTag(Icc, sig, TRUE);
+ if (n < 0)
+ return NULL;
+
+
+ // If is in memory, the LUT is already there, so throw a copy
+ if (Icc -> TagPtrs[n]) {
+
+ return cmsDupLUT((LPLUT) Icc ->TagPtrs[n]);
+ }
+
+ offset = Icc -> TagOffsets[n];
+
+ if (Icc -> Seek(Icc, offset))
+ return NULL;
+
+ BaseType = ReadBase(Icc);
+
+
+ NewLUT = cmsAllocLUT();
+ if (!NewLUT) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "cmsAllocLUT() failed");
+ return NULL;
+ }
+
+
+ switch (BaseType) {
+
+ case icSigLut8Type: if (!ReadLUT8(Icc, NewLUT, sig)) {
+ cmsFreeLUT(NewLUT);
+ return NULL;
+ }
+ break;
+
+ case icSigLut16Type: if (!ReadLUT16(Icc, NewLUT)) {
+ cmsFreeLUT(NewLUT);
+ return NULL;
+ }
+ break;
+
+ case icSiglutAtoBType: if (!ReadLUT_A2B(Icc, NewLUT, offset, sig)) {
+ cmsFreeLUT(NewLUT);
+ return NULL;
+ }
+ break;
+
+ case icSiglutBtoAType: if (!ReadLUT_B2A(Icc, NewLUT, offset, sig)) {
+ cmsFreeLUT(NewLUT);
+ return NULL;
+ }
+ break;
+
+ default: cmsSignalError(LCMS_ERRC_ABORTED, "Bad tag signature %lx found.", BaseType);
+ cmsFreeLUT(NewLUT);
+ return NULL;
+ }
+
+
+ return NewLUT;
+}
+
+
+// Sets the language & country preferences. Used only in ICC 4.0 profiles
+
+void LCMSEXPORT cmsSetLanguage(const char LanguageCode[4], const char CountryCode[4])
+{
+
+ int LanguageCodeInt = *(int *) LanguageCode;
+ int CountryCodeInt = *(int *) CountryCode;
+
+ AdjustEndianess32((LPBYTE) &LanguageCodeInt);
+ AdjustEndianess32((LPBYTE) &CountryCodeInt);
+
+ GlobalLanguageCode = LanguageCodeInt;
+ GlobalCountryCode = CountryCodeInt;
+}
+
+
+
+// Some tags (e.g, 'pseq') can have text tags embedded. This function
+// handles such special case. Returns -1 on error, or the number of bytes left on success.
+
+static
+int ReadEmbeddedTextTag(LPLCMSICCPROFILE Icc, size_t size, char* Name, size_t size_max)
+{
+ icTagTypeSignature BaseType;
+
+
+ BaseType = ReadBase(Icc);
+ size -= sizeof(icTagBase);
+
+ switch (BaseType) {
+
+ case icSigTextDescriptionType: {
+
+ icUInt32Number AsciiCount;
+ icUInt32Number i, UnicodeCode, UnicodeCount;
+ icUInt16Number ScriptCodeCode, Dummy;
+ icUInt8Number ScriptCodeCount;
+
+ if (Icc ->Read(&AsciiCount, sizeof(icUInt32Number), 1, Icc) != 1) return -1;
+
+ if (size < sizeof(icUInt32Number)) return (int) size;
+ size -= sizeof(icUInt32Number);
+
+ AdjustEndianess32((LPBYTE) &AsciiCount);
+ Icc ->Read(Name, 1,
+ (AsciiCount >= size_max) ? (size_max-1) : AsciiCount, Icc);
+
+ if (size < AsciiCount) return (int) size;
+ size -= AsciiCount;
+
+ // Skip Unicode code
+
+ if (Icc ->Read(&UnicodeCode, sizeof(icUInt32Number), 1, Icc) != 1) return -1;
+ if (size < sizeof(icUInt32Number)) return (int) size;
+ size -= sizeof(icUInt32Number);
+
+ if (Icc ->Read(&UnicodeCount, sizeof(icUInt32Number), 1, Icc) != 1) return -1;
+ if (size < sizeof(icUInt32Number)) return (int) size;
+ size -= sizeof(icUInt32Number);
+
+ AdjustEndianess32((LPBYTE) &UnicodeCount);
+
+ if (UnicodeCount > size) return (int) size;
+
+ for (i=0; i < UnicodeCount; i++) {
+ size_t nread = Icc ->Read(&Dummy, sizeof(icUInt16Number), 1, Icc);
+ if (nread != 1) return (int) size;
+ size -= sizeof(icUInt16Number);
+ }
+
+ // Skip ScriptCode code
+
+ if (Icc ->Read(&ScriptCodeCode, sizeof(icUInt16Number), 1, Icc) != 1) return -1;
+ size -= sizeof(icUInt16Number);
+ if (Icc ->Read(&ScriptCodeCount, sizeof(icUInt8Number), 1, Icc) != 1) return -1;
+ size -= sizeof(icUInt8Number);
+
+ // Should remain 67 bytes as filler
+
+ if (size < 67) return (int) size;
+
+ for (i=0; i < 67; i++) {
+ size_t nread = Icc ->Read(&Dummy, sizeof(icUInt8Number), 1, Icc);
+ if (nread != 1) return (int) size;
+ size --;
+ }
+ }
+ break;
+
+
+ case icSigCopyrightTag: // Broken profiles from agfa does store copyright info in such type
+ case icSigTextType:
+ {
+ char Dummy;
+ size_t i, Missing = 0;
+
+ if (size >= size_max) {
+
+ Missing = size - size_max + 1;
+ size = size_max - 1;
+ }
+
+ if (Icc -> Read(Name, 1, size, Icc) != size) return -1;
+
+ for (i=0; i < Missing; i++)
+ Icc -> Read(&Dummy, 1, 1, Icc);
+ }
+ break;
+
+ // MultiLocalizedUnicodeType, V4 only
+
+ case icSigMultiLocalizedUnicodeType: {
+
+ icUInt32Number Count, RecLen;
+ icUInt16Number Language, Country;
+ icUInt32Number ThisLen, ThisOffset;
+ size_t Offset = 0;
+ size_t Len = 0;
+ size_t i;
+ wchar_t* wchar = L"";
+
+
+ if (Icc ->Read(&Count, sizeof(icUInt32Number), 1, Icc) != 1) return -1;
+ AdjustEndianess32((LPBYTE) &Count);
+ if (Icc ->Read(&RecLen, sizeof(icUInt32Number), 1, Icc) != 1) return -1;
+ AdjustEndianess32((LPBYTE) &RecLen);
+
+ if (RecLen != 12) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "multiLocalizedUnicodeType of len != 12 is not supported.");
+ return -1;
+ }
+
+ for (i=0; i < Count; i++) {
+
+ if (Icc ->Read(&Language, sizeof(icUInt16Number), 1, Icc) != 1) return -1;
+ AdjustEndianess16((LPBYTE) &Language);
+ if (Icc ->Read(&Country, sizeof(icUInt16Number), 1, Icc) != 1) return -1;
+ AdjustEndianess16((LPBYTE) &Country);
+
+ if (Icc ->Read(&ThisLen, sizeof(icUInt32Number), 1, Icc) != 1) return -1;
+ AdjustEndianess32((LPBYTE) &ThisLen);
+
+ if (Icc ->Read(&ThisOffset, sizeof(icUInt32Number), 1, Icc) != 1) return -1;
+ AdjustEndianess32((LPBYTE) &ThisOffset);
+
+ if (Language == GlobalLanguageCode || Offset == 0) {
+
+ Len = ThisLen; Offset = ThisOffset;
+ if (Country == GlobalCountryCode)
+ break; // Found
+ }
+
+ }
+
+
+ if (Offset == 0) {
+
+ strcpy(Name, "(no info)");
+ break;
+ }
+
+ // Compute true offset
+ Offset -= 12 * Count + 8 + sizeof(icTagBase);
+
+ // Skip unused bytes
+ for (i=0; i < Offset; i++) {
+
+ char Discard;
+ if (Icc ->Read(&Discard, 1, 1, Icc) != 1) return -1;
+ }
+
+
+ // Bound len
+ if (Len < 0) Len = 0;
+ if (Len > 20*1024) Len = 20 * 1024;
+
+ wchar = (wchar_t*) _cmsMalloc(Len*sizeof(wchar_t)+2);
+ if (!wchar) return -1;
+
+ if (Icc ->Read(wchar, 1, Len, Icc) != Len) return -1;
+ AdjustEndianessArray16((LPWORD) wchar, Len / 2);
+
+ wchar[Len / 2] = L'\0';
+ i = wcstombs(Name, wchar, size_max );
+ if (i == ((size_t) -1)) {
+
+ Name[0] = 0; // Error
+ }
+
+ _cmsFree((void*) wchar);
+ }
+ break;
+
+ default:
+ cmsSignalError(LCMS_ERRC_ABORTED, "Bad tag signature %lx found.", BaseType);
+ return -1;
+ }
+
+ return (int) size;
+}
+
+
+// Take an ASCII item. Takes at most size_max bytes
+
+int LCMSEXPORT cmsReadICCTextEx(cmsHPROFILE hProfile, icTagSignature sig, char *Name, size_t size_max)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+ size_t offset, size;
+ int n;
+
+ n = _cmsSearchTag(Icc, sig, TRUE);
+ if (n < 0)
+ return -1;
+
+ size = Icc -> TagSizes[n];
+
+ if (Icc -> TagPtrs[n]) {
+
+ if (size > size_max)
+ size = size_max;
+
+ CopyMemory(Name, Icc -> TagPtrs[n], size);
+
+ return (int) Icc -> TagSizes[n];
+ }
+
+ offset = Icc -> TagOffsets[n];
+
+
+ if (Icc -> Seek(Icc, offset))
+ return -1;
+
+ if (ReadEmbeddedTextTag(Icc, size, Name, size_max) < 0) return -1;
+
+ return (int) size;
+}
+
+// Keep compatibility with older versions
+
+int LCMSEXPORT cmsReadICCText(cmsHPROFILE hProfile, icTagSignature sig, char *Text)
+{
+ return cmsReadICCTextEx(hProfile, sig, Text, LCMS_DESC_MAX);
+}
+
+
+// Take an XYZ item
+
+static
+int ReadICCXYZ(cmsHPROFILE hProfile, icTagSignature sig, LPcmsCIEXYZ Value, LCMSBOOL lIsFatal)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+ icTagTypeSignature BaseType;
+ size_t offset;
+ int n;
+ icXYZNumber XYZ;
+
+ n = _cmsSearchTag(Icc, sig, FALSE);
+ if (n < 0)
+ return -1;
+
+ if (Icc -> TagPtrs[n]) {
+
+ CopyMemory(Value, Icc -> TagPtrs[n], Icc -> TagSizes[n]);
+ return (int) Icc -> TagSizes[n];
+ }
+
+ offset = Icc -> TagOffsets[n];
+
+ if (Icc -> Seek(Icc, offset))
+ return -1;
+
+
+ BaseType = ReadBase(Icc);
+
+ switch (BaseType) {
+
+
+ case 0x7c3b10cL: // Some apple broken embedded profiles does not have correct type
+ case icSigXYZType:
+
+ Icc ->Read(&XYZ, sizeof(icXYZNumber), 1, Icc);
+ Value -> X = Convert15Fixed16(XYZ.X);
+ Value -> Y = Convert15Fixed16(XYZ.Y);
+ Value -> Z = Convert15Fixed16(XYZ.Z);
+ break;
+
+ // Aug/21-2001 - Monaco 2 does have WRONG values.
+
+ default:
+ if (lIsFatal)
+ cmsSignalError(LCMS_ERRC_ABORTED, "Bad tag signature %lx found.", BaseType);
+ return -1;
+ }
+
+ return 1;
+}
+
+
+// Read a icSigS15Fixed16ArrayType (currently only a 3x3 matrix)
+
+static
+int ReadICCXYZArray(cmsHPROFILE hProfile, icTagSignature sig, LPMAT3 v)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+ icTagTypeSignature BaseType;
+ size_t offset, sz;
+ int i, n;
+ icXYZNumber XYZ[3];
+ cmsCIEXYZ XYZdbl[3];
+
+
+ n = _cmsSearchTag(Icc, sig, FALSE);
+ if (n < 0)
+ return -1; // Not found
+
+ if (Icc -> TagPtrs[n]) {
+
+ CopyMemory(v, Icc -> TagPtrs[n], Icc -> TagSizes[n]);
+ return (int) Icc -> TagSizes[n];
+ }
+
+ offset = Icc -> TagOffsets[n];
+
+ if (Icc -> Seek(Icc, offset))
+ return -1;
+
+ BaseType = ReadBase(Icc);
+
+ switch (BaseType) {
+
+ case icSigS15Fixed16ArrayType:
+
+ sz = Icc ->TagSizes[n] / sizeof(icXYZNumber);
+
+ if (sz != 3) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "Bad array size of %d entries.", sz);
+ return -1;
+ }
+
+ Icc ->Read(XYZ, sizeof(icXYZNumber), 3, Icc);
+
+ for (i=0; i < 3; i++) {
+
+ XYZdbl[i].X = Convert15Fixed16(XYZ[i].X);
+ XYZdbl[i].Y = Convert15Fixed16(XYZ[i].Y);
+ XYZdbl[i].Z = Convert15Fixed16(XYZ[i].Z);
+ }
+
+ CopyMemory(v, XYZdbl, 3*sizeof(cmsCIEXYZ));
+ break;
+
+ default:
+ cmsSignalError(LCMS_ERRC_ABORTED, "Bad tag signature %lx found.", BaseType);
+ return -1;
+
+ }
+
+ return sizeof(MAT3);
+}
+
+
+
+// Primaries are to be in xyY notation
+
+LCMSBOOL LCMSEXPORT cmsTakeColorants(LPcmsCIEXYZTRIPLE Dest, cmsHPROFILE hProfile)
+{
+ if (ReadICCXYZ(hProfile, icSigRedColorantTag, &Dest -> Red, TRUE) < 0) return FALSE;
+ if (ReadICCXYZ(hProfile, icSigGreenColorantTag, &Dest -> Green, TRUE) < 0) return FALSE;
+ if (ReadICCXYZ(hProfile, icSigBlueColorantTag, &Dest -> Blue, TRUE) < 0) return FALSE;
+
+ return TRUE;
+}
+
+
+LCMSBOOL cmsReadICCMatrixRGB2XYZ(LPMAT3 r, cmsHPROFILE hProfile)
+{
+ cmsCIEXYZTRIPLE Primaries;
+
+ if (!cmsTakeColorants(&Primaries, hProfile)) return FALSE;
+
+ VEC3init(&r -> v[0], Primaries.Red.X, Primaries.Green.X, Primaries.Blue.X);
+ VEC3init(&r -> v[1], Primaries.Red.Y, Primaries.Green.Y, Primaries.Blue.Y);
+ VEC3init(&r -> v[2], Primaries.Red.Z, Primaries.Green.Z, Primaries.Blue.Z);
+
+ return TRUE;
+
+}
+
+
+// Always return a suitable matrix
+
+LCMSBOOL cmsReadChromaticAdaptationMatrix(LPMAT3 r, cmsHPROFILE hProfile)
+{
+
+ if (ReadICCXYZArray(hProfile, icSigChromaticAdaptationTag, r) < 0) {
+
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+
+ // For display profiles, revert to bradford. Else take identity.
+
+ MAT3identity(r);
+
+ // Emissive devices have non-identity chad
+
+ if ((cmsGetDeviceClass(hProfile) == icSigDisplayClass) ||
+ cmsTakeHeaderFlags(hProfile) & icTransparency) {
+
+ // NULL for cone defaults to Bradford, from media to D50
+ cmsAdaptationMatrix(r, NULL, &Icc ->MediaWhitePoint, &Icc ->Illuminant);
+ }
+ }
+
+ return TRUE;
+}
+
+
+
+LPGAMMATABLE LCMSEXPORT cmsReadICCGamma(cmsHPROFILE hProfile, icTagSignature sig)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+ size_t offset;
+ int n;
+
+
+ n = _cmsSearchTag(Icc, sig, TRUE);
+ if (n < 0)
+ return NULL;
+
+ if (Icc -> TagPtrs[n]) {
+
+ return cmsDupGamma((LPGAMMATABLE) Icc -> TagPtrs[n]);
+ }
+
+ offset = Icc -> TagOffsets[n];
+
+ if (Icc -> Seek(Icc, offset))
+ return NULL;
+
+ return ReadCurve(Icc);
+
+}
+
+
+// Some ways have analytical revese. This function accounts for that
+
+LPGAMMATABLE LCMSEXPORT cmsReadICCGammaReversed(cmsHPROFILE hProfile, icTagSignature sig)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+ size_t offset;
+ int n;
+
+
+ n = _cmsSearchTag(Icc, sig, TRUE);
+ if (n < 0)
+ return NULL;
+
+ if (Icc -> TagPtrs[n]) {
+
+ return cmsReverseGamma(256, (LPGAMMATABLE) Icc -> TagPtrs[n]);
+ }
+
+ offset = Icc -> TagOffsets[n];
+
+ if (Icc -> Seek(Icc, offset))
+ return NULL;
+
+ return ReadCurveReversed(Icc);
+}
+
+// Check Named color header
+
+static
+LCMSBOOL CheckHeader(LPcmsNAMEDCOLORLIST v, icNamedColor2* nc2)
+{
+ if (v ->Prefix[0] == 0 && v ->Suffix[0] == 0 && v ->ColorantCount == 0) return TRUE;
+
+ if (stricmp(v ->Prefix, (const char*) nc2 ->prefix) != 0) return FALSE;
+ if (stricmp(v ->Suffix, (const char*) nc2 ->suffix) != 0) return FALSE;
+
+ return ((int) v ->ColorantCount == (int) nc2 ->nDeviceCoords);
+}
+
+// Read named color list
+
+int cmsReadICCnamedColorList(cmsHTRANSFORM xform, cmsHPROFILE hProfile, icTagSignature sig)
+{
+ _LPcmsTRANSFORM v = (_LPcmsTRANSFORM) xform;
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+ int n;
+ icTagTypeSignature BaseType;
+ size_t offset;
+
+ n = _cmsSearchTag(Icc, sig, TRUE);
+ if (n < 0)
+ return 0;
+
+ if (Icc -> TagPtrs[n]) {
+
+ // This replaces actual named color list.
+ size_t size = Icc -> TagSizes[n];
+
+ if (v ->NamedColorList) cmsFreeNamedColorList(v ->NamedColorList);
+ v -> NamedColorList = (LPcmsNAMEDCOLORLIST) _cmsMalloc(size);
+ CopyMemory(v -> NamedColorList, Icc ->TagPtrs[n], size);
+ return v ->NamedColorList->nColors;
+ }
+
+ offset = Icc -> TagOffsets[n];
+
+ if (Icc -> Seek(Icc, offset))
+ return 0;
+
+ BaseType = ReadBase(Icc);
+
+ switch (BaseType) {
+
+ // I never have seen one of these. Probably is not worth of implementing.
+
+ case icSigNamedColorType: {
+
+ cmsSignalError(LCMS_ERRC_WARNING, "Ancient named color profiles are not supported.");
+ return 0;
+ }
+
+ // The named color struct
+
+ case icSigNamedColor2Type: {
+
+ icNamedColor2 nc2;
+ unsigned int i, j;
+
+ if (Icc -> Read(&nc2, sizeof(icNamedColor2) - SIZEOF_UINT8_ALIGNED, 1, Icc) != 1) return 0;
+ AdjustEndianess32((LPBYTE) &nc2.vendorFlag);
+ AdjustEndianess32((LPBYTE) &nc2.count);
+ AdjustEndianess32((LPBYTE) &nc2.nDeviceCoords);
+
+ if (!CheckHeader(v->NamedColorList, &nc2)) {
+ cmsSignalError(LCMS_ERRC_WARNING, "prefix/suffix/device for named color profiles mismatch.");
+ return 0;
+ }
+
+ if (nc2.nDeviceCoords > MAXCHANNELS) {
+ cmsSignalError(LCMS_ERRC_WARNING, "Too many device coordinates.");
+ return 0;
+ }
+
+ strncpy(v ->NamedColorList->Prefix, (const char*) nc2.prefix, 32);
+ strncpy(v ->NamedColorList->Suffix, (const char*) nc2.suffix, 32);
+ v ->NamedColorList->Prefix[32] = v->NamedColorList->Suffix[32] = 0;
+
+ v ->NamedColorList ->ColorantCount = nc2.nDeviceCoords;
+
+ for (i=0; i < nc2.count; i++) {
+
+ WORD PCS[3];
+ WORD Colorant[MAXCHANNELS];
+ char Root[33];
+
+ ZeroMemory(Colorant, sizeof(WORD) * MAXCHANNELS);
+ Icc -> Read(Root, 1, 32, Icc);
+ Icc -> Read(PCS, 3, sizeof(WORD), Icc);
+
+ for (j=0; j < 3; j++)
+ AdjustEndianess16((LPBYTE) &PCS[j]);
+
+ Icc -> Read(Colorant, sizeof(WORD), nc2.nDeviceCoords, Icc);
+
+ for (j=0; j < nc2.nDeviceCoords; j++)
+ AdjustEndianess16((LPBYTE) &Colorant[j]);
+
+ cmsAppendNamedColor(v, Root, PCS, Colorant);
+ }
+
+ return v ->NamedColorList->nColors;
+ }
+ break;
+
+ default:
+ cmsSignalError(LCMS_ERRC_WARNING, "Bad tag signature '%lx' found.", BaseType);
+ return 0;
+ }
+
+ // It would never reach here
+ // return 0;
+}
+
+
+
+// Read colorant tables
+
+LPcmsNAMEDCOLORLIST LCMSEXPORT cmsReadColorantTable(cmsHPROFILE hProfile, icTagSignature sig)
+{
+ icInt32Number n;
+ icUInt32Number Count, i;
+ size_t offset;
+ icTagTypeSignature BaseType;
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+ LPcmsNAMEDCOLORLIST List;
+
+ n = _cmsSearchTag(Icc, sig, FALSE);
+ if (n < 0)
+ return NULL; // Not found
+
+ if (Icc -> TagPtrs[n]) {
+
+ size_t size = Icc -> TagSizes[n];
+ void* v = _cmsMalloc(size);
+
+ if (v == NULL) return NULL;
+ CopyMemory(v, Icc -> TagPtrs[n], size);
+ return (LPcmsNAMEDCOLORLIST) v;
+ }
+
+
+ offset = Icc -> TagOffsets[n];
+
+ if (Icc -> Seek(Icc, offset))
+ return NULL;
+
+ BaseType = ReadBase(Icc);
+
+ if (BaseType != icSigColorantTableType) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "Bad tag signature '%lx' found.", BaseType);
+ return NULL;
+ }
+
+
+ if (Icc ->Read(&Count, sizeof(icUInt32Number), 1, Icc) != 1) return NULL;
+ AdjustEndianess32((LPBYTE) &Count);
+
+ if (Count > MAXCHANNELS) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "Too many colorants '%lx'", Count);
+ return NULL;
+ }
+
+ List = cmsAllocNamedColorList(Count);
+ for (i=0; i < Count; i++) {
+
+ if (!Icc ->Read(List->List[i].Name, 1, 32 , Icc)) goto Error;
+ if (!Icc ->Read(List->List[i].PCS, sizeof(icUInt16Number), 3, Icc)) goto Error;
+ AdjustEndianessArray16(List->List[i].PCS, 3);
+ }
+
+ return List;
+
+Error:
+ cmsFreeNamedColorList(List);
+ return NULL;
+
+}
+
+
+
+// Uncooked manufacturer
+
+const char* LCMSEXPORT cmsTakeManufacturer(cmsHPROFILE hProfile)
+{
+
+ static char Manufacturer[LCMS_DESC_MAX] = "";
+
+ Manufacturer[0] = 0;
+
+ if (cmsIsTag(hProfile, icSigDeviceMfgDescTag)) {
+
+ cmsReadICCTextEx(hProfile, icSigDeviceMfgDescTag, Manufacturer, LCMS_DESC_MAX);
+ }
+
+ return Manufacturer;
+}
+
+// Uncooked model
+
+const char* LCMSEXPORT cmsTakeModel(cmsHPROFILE hProfile)
+{
+
+ static char Model[LCMS_DESC_MAX] = "";
+
+ Model[0] = 0;
+
+ if (cmsIsTag(hProfile, icSigDeviceModelDescTag)) {
+
+ cmsReadICCTextEx(hProfile, icSigDeviceModelDescTag, Model, LCMS_DESC_MAX);
+ }
+
+ return Model;
+}
+
+
+const char* LCMSEXPORT cmsTakeCopyright(cmsHPROFILE hProfile)
+{
+
+ static char Copyright[LCMS_DESC_MAX] = "";
+
+ Copyright[0] = 0;
+ if (cmsIsTag(hProfile, icSigCopyrightTag)) {
+
+ cmsReadICCTextEx(hProfile, icSigCopyrightTag, Copyright, LCMS_DESC_MAX);
+ }
+
+ return Copyright;
+}
+
+
+// We compute name with model - manufacturer
+
+const char* LCMSEXPORT cmsTakeProductName(cmsHPROFILE hProfile)
+{
+ static char Name[LCMS_DESC_MAX*2+4];
+ char Manufacturer[LCMS_DESC_MAX], Model[LCMS_DESC_MAX];
+
+ Name[0] = '\0';
+ Manufacturer[0] = Model[0] = '\0';
+
+ if (cmsIsTag(hProfile, icSigDeviceMfgDescTag)) {
+
+ cmsReadICCTextEx(hProfile, icSigDeviceMfgDescTag, Manufacturer, LCMS_DESC_MAX);
+ }
+
+ if (cmsIsTag(hProfile, icSigDeviceModelDescTag)) {
+
+ cmsReadICCTextEx(hProfile, icSigDeviceModelDescTag, Model, LCMS_DESC_MAX);
+ }
+
+ if (!Manufacturer[0] && !Model[0]) {
+
+ if (cmsIsTag(hProfile, icSigProfileDescriptionTag)) {
+
+ cmsReadICCTextEx(hProfile, icSigProfileDescriptionTag, Name, LCMS_DESC_MAX);
+ return Name;
+ }
+ else return "{no name}";
+ }
+
+
+ if (!Manufacturer[0] ||
+ strncmp(Model, Manufacturer, 8) == 0 || strlen(Model) > 30)
+ strcpy(Name, Model);
+ else
+ sprintf(Name, "%s - %s", Model, Manufacturer);
+
+ return Name;
+
+}
+
+
+// We compute desc with manufacturer - model
+
+const char* LCMSEXPORT cmsTakeProductDesc(cmsHPROFILE hProfile)
+{
+ static char Name[2048];
+
+ if (cmsIsTag(hProfile, icSigProfileDescriptionTag)) {
+
+ cmsReadICCText(hProfile, icSigProfileDescriptionTag, Name);
+ }
+ else return cmsTakeProductName(hProfile);
+
+ if (strncmp(Name, "Copyrig", 7) == 0)
+ return cmsTakeProductName(hProfile);
+
+ return Name;
+}
+
+
+const char* LCMSEXPORT cmsTakeProductInfo(cmsHPROFILE hProfile)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+
+ static char Info[4096];
+
+ Info[0] = '\0';
+
+ if (cmsIsTag(hProfile, icSigProfileDescriptionTag))
+ {
+ char Desc[1024];
+
+ cmsReadICCText(hProfile, icSigProfileDescriptionTag, Desc);
+ strcat(Info, Desc);
+ strcat(Info, "\r\n\r\n");
+ }
+
+
+ if (cmsIsTag(hProfile, icSigCopyrightTag))
+ {
+ char Copyright[LCMS_DESC_MAX];
+
+ cmsReadICCText(hProfile, icSigCopyrightTag, Copyright);
+ strcat(Info, Copyright);
+ strcat(Info, "\r\n\r\n");
+ }
+
+
+
+// KODAK private tag... But very useful
+
+#define K007 (icTagSignature)0x4B303037
+
+ // MonCal
+
+ if (cmsIsTag(hProfile, K007))
+ {
+ char MonCal[LCMS_DESC_MAX];
+
+ cmsReadICCText(hProfile, K007, MonCal);
+ strcat(Info, MonCal);
+ strcat(Info, "\r\n\r\n");
+ }
+ else
+ {
+ cmsCIEXYZ WhitePt;
+ char WhiteStr[1024];
+
+ cmsTakeMediaWhitePoint(&WhitePt, hProfile);
+ _cmsIdentifyWhitePoint(WhiteStr, &WhitePt);
+ strcat(WhiteStr, "\r\n\r\n");
+ strcat(Info, WhiteStr);
+ }
+
+
+ if (Icc -> stream) {
+ strcat(Info, Icc -> PhysicalFile);
+ }
+ return Info;
+}
+
+// Extract the target data as a big string. Does not signal if tag is not present.
+
+LCMSBOOL LCMSEXPORT cmsTakeCharTargetData(cmsHPROFILE hProfile, char** Data, size_t* len)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+ int n;
+
+ *Data = NULL;
+ *len = 0;
+
+ n = _cmsSearchTag(Icc, icSigCharTargetTag, FALSE);
+ if (n < 0) return FALSE;
+
+
+ *len = Icc -> TagSizes[n];
+
+ // Make sure that is reasonable (600K)
+ if (*len > 600*1024) *len = 600*1024;
+
+ *Data = (char*) _cmsMalloc(*len + 1); // Plus zero marker
+
+ if (!*Data) {
+
+ cmsSignalError(LCMS_ERRC_ABORTED, "Out of memory allocating CharTarget space!");
+ return FALSE;
+ }
+
+ if (cmsReadICCTextEx(hProfile, icSigCharTargetTag, *Data, *len) < 0)
+ return FALSE;
+
+ (*Data)[*len] = 0; // Force a zero marker. Shouldn't be needed, but is
+ // here to simplify things.
+
+ return TRUE;
+}
+
+
+
+
+LCMSBOOL LCMSEXPORT cmsTakeCalibrationDateTime(struct tm *Dest, cmsHPROFILE hProfile)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+ int n;
+
+ n = _cmsSearchTag(Icc, icSigCalibrationDateTimeTag, FALSE);
+ if (n < 0) return FALSE;
+
+ if (Icc ->TagPtrs[n]) {
+
+ CopyMemory(Dest, Icc ->TagPtrs[n], sizeof(struct tm));
+ }
+ else
+ {
+ icDateTimeNumber timestamp;
+
+ if (Icc -> Seek(Icc, Icc -> TagOffsets[n] + sizeof(icTagBase)))
+ return FALSE;
+
+ if (Icc ->Read(&timestamp, 1, sizeof(icDateTimeNumber), Icc) != sizeof(icDateTimeNumber))
+ return FALSE;
+
+ DecodeDateTimeNumber(&timestamp, Dest);
+ }
+
+
+ return TRUE;
+}
+
+
+
+// PSEQ Tag, used in devicelink profiles
+
+LPcmsSEQ LCMSEXPORT cmsReadProfileSequenceDescription(cmsHPROFILE hProfile)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+ int n;
+ icUInt32Number i, Count;
+ icDescStruct DescStruct;
+ icTagTypeSignature BaseType;
+ size_t size, offset;
+ LPcmsSEQ OutSeq;
+
+
+ n = _cmsSearchTag(Icc, icSigProfileSequenceDescTag, FALSE);
+ if (n < 0) return NULL;
+
+ size = Icc -> TagSizes[n];
+ if (size < 12) return NULL;
+
+ if (Icc -> TagPtrs[n]) {
+
+ OutSeq = (LPcmsSEQ) _cmsMalloc(size);
+ if (OutSeq == NULL) return NULL;
+ CopyMemory(OutSeq, Icc ->TagPtrs[n], size);
+ return OutSeq;
+ }
+
+ offset = Icc -> TagOffsets[n];
+
+ if (Icc -> Seek(Icc, offset))
+ return NULL;
+
+ BaseType = ReadBase(Icc);
+
+ if (BaseType != icSigProfileSequenceDescType) return NULL;
+
+ Icc ->Read(&Count, sizeof(icUInt32Number), 1, Icc);
+ AdjustEndianess32((LPBYTE) &Count);
+
+ if (Count > 1000) {
+ return NULL;
+ }
+
+ size = sizeof(int) + Count * sizeof(cmsPSEQDESC);
+ OutSeq = (LPcmsSEQ) _cmsMalloc(size);
+ if (OutSeq == NULL) return NULL;
+
+ OutSeq ->n = Count;
+
+ // Get structures as well
+
+ for (i=0; i < Count; i++) {
+
+ LPcmsPSEQDESC sec = &OutSeq -> seq[i];
+
+ Icc -> Read(&DescStruct, sizeof(icDescStruct) - SIZEOF_UINT8_ALIGNED, 1, Icc);
+
+ AdjustEndianess32((LPBYTE) &DescStruct.deviceMfg);
+ AdjustEndianess32((LPBYTE) &DescStruct.deviceModel);
+ AdjustEndianess32((LPBYTE) &DescStruct.technology);
+ AdjustEndianess32((LPBYTE) &DescStruct.attributes[0]);
+ AdjustEndianess32((LPBYTE) &DescStruct.attributes[1]);
+
+ sec ->attributes[0] = DescStruct.attributes[0];
+ sec ->attributes[1] = DescStruct.attributes[1];
+ sec ->deviceMfg = DescStruct.deviceMfg;
+ sec ->deviceModel = DescStruct.deviceModel;
+ sec ->technology = DescStruct.technology;
+
+ if (ReadEmbeddedTextTag(Icc, size, sec ->Manufacturer, LCMS_DESC_MAX) < 0) return NULL;
+ if (ReadEmbeddedTextTag(Icc, size, sec ->Model, LCMS_DESC_MAX) < 0) return NULL;
+
+ }
+
+ return OutSeq;
+}
+
+
+void LCMSEXPORT cmsFreeProfileSequenceDescription(LPcmsSEQ pseq)
+{
+ if (pseq)
+ _cmsFree(pseq);
+}
+
+
+
+
+
+// Read a few tags that are hardly required
+
+
+static
+void ReadCriticalTags(LPLCMSICCPROFILE Icc)
+{
+ cmsHPROFILE hProfile = (cmsHPROFILE) Icc;
+
+ if (Icc ->Version >= 0x4000000) {
+
+ // v4 profiles
+
+ MAT3 ChrmCanonical;
+
+ if (ReadICCXYZ(hProfile,
+ icSigMediaWhitePointTag,
+ &Icc ->MediaWhitePoint, FALSE) < 0) {
+
+ Icc ->MediaWhitePoint = *cmsD50_XYZ();
+ }
+
+ // Read media black
+
+ if (ReadICCXYZ(hProfile,
+ icSigMediaBlackPointTag,
+ &Icc ->MediaBlackPoint, FALSE) < 0) {
+
+ Icc ->MediaBlackPoint.X = 0;
+ Icc ->MediaBlackPoint.Y = 0;
+ Icc ->MediaBlackPoint.X = 0;
+
+ }
+
+ NormalizeXYZ(&Icc ->MediaWhitePoint);
+ NormalizeXYZ(&Icc ->MediaBlackPoint);
+
+ if (ReadICCXYZArray(hProfile,
+ icSigChromaticAdaptationTag,
+ &ChrmCanonical) > 0) {
+
+ MAT3inverse(&ChrmCanonical, &Icc ->ChromaticAdaptation);
+
+ }
+ else {
+
+ MAT3identity(&Icc ->ChromaticAdaptation);
+ }
+
+
+ // Convert media white, black to absolute under original illuminant
+
+ EvalCHRM(&Icc ->MediaWhitePoint, &Icc ->ChromaticAdaptation, &Icc ->MediaWhitePoint);
+ EvalCHRM(&Icc ->MediaBlackPoint, &Icc ->ChromaticAdaptation, &Icc ->MediaBlackPoint);
+
+
+ }
+ else {
+
+ // v2 profiles
+
+ // Read media white
+
+ if (ReadICCXYZ(hProfile,
+ icSigMediaWhitePointTag,
+ &Icc ->MediaWhitePoint, FALSE) < 0) {
+
+ Icc ->MediaWhitePoint = *cmsD50_XYZ();
+ }
+
+ // Read media black
+
+ if (ReadICCXYZ(hProfile,
+ icSigMediaBlackPointTag,
+ &Icc ->MediaBlackPoint, FALSE) < 0) {
+
+ Icc ->MediaBlackPoint.X = 0;
+ Icc ->MediaBlackPoint.Y = 0;
+ Icc ->MediaBlackPoint.X = 0;
+
+ }
+
+ NormalizeXYZ(&Icc ->MediaWhitePoint);
+ NormalizeXYZ(&Icc ->MediaBlackPoint);
+
+
+ // Take Bradford as default for Display profiles only.
+
+ if (cmsGetDeviceClass(hProfile) == icSigDisplayClass) {
+
+
+ cmsAdaptationMatrix(&Icc -> ChromaticAdaptation,
+ NULL,
+ &Icc -> Illuminant,
+ &Icc -> MediaWhitePoint);
+ }
+ else
+ MAT3identity(&Icc ->ChromaticAdaptation);
+
+ }
+
+}
+
+
+// Create profile from disk file
+
+cmsHPROFILE LCMSEXPORT cmsOpenProfileFromFile(const char *lpFileName, const char *sAccess)
+{
+ LPLCMSICCPROFILE NewIcc;
+ cmsHPROFILE hEmpty;
+
+
+ // Open for write means an empty profile
+
+ if (*sAccess == 'W' || *sAccess == 'w') {
+
+ hEmpty = _cmsCreateProfilePlaceholder();
+ NewIcc = (LPLCMSICCPROFILE) (LPSTR) hEmpty;
+ NewIcc -> IsWrite = TRUE;
+ strncpy(NewIcc ->PhysicalFile, lpFileName, MAX_PATH-1);
+ NewIcc ->PhysicalFile[MAX_PATH-1] = 0;
+
+ // Save LUT as 8 bit
+
+ sAccess++;
+ if (*sAccess == '8') NewIcc ->SaveAs8Bits = TRUE;
+
+ return hEmpty;
+ }
+
+
+ // Open for read means a file placeholder
+
+ NewIcc = _cmsCreateProfileFromFilePlaceholder(lpFileName);
+ if (!NewIcc) return NULL;
+
+ if (!ReadHeader(NewIcc, FALSE)) return NULL;
+
+ ReadCriticalTags(NewIcc);
+
+ return (cmsHPROFILE) (LPSTR) NewIcc;
+}
+
+
+
+
+// Open from memory block
+
+cmsHPROFILE LCMSEXPORT cmsOpenProfileFromMem(LPVOID MemPtr, DWORD dwSize)
+{
+ LPLCMSICCPROFILE NewIcc;
+
+
+ NewIcc = _cmsCreateProfileFromMemPlaceholder(MemPtr, dwSize);
+ if (!NewIcc) return NULL;
+
+ if (!ReadHeader(NewIcc, TRUE)) return NULL;
+
+ ReadCriticalTags(NewIcc);
+
+ return (cmsHPROFILE) (LPSTR) NewIcc;
+
+}
+
+
+
+LCMSBOOL LCMSEXPORT cmsCloseProfile(cmsHPROFILE hProfile)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+ LCMSBOOL rc = TRUE;
+ icInt32Number i;
+
+ if (!Icc) return FALSE;
+
+ // Was open in write mode?
+ if (Icc ->IsWrite) {
+
+ Icc ->IsWrite = FALSE; // Assure no further writting
+ rc = _cmsSaveProfile(hProfile, Icc ->PhysicalFile);
+ }
+
+ for (i=0; i < Icc -> TagCount; i++) {
+
+ if (Icc -> TagPtrs[i])
+ free(Icc -> TagPtrs[i]);
+ }
+
+ if (Icc -> stream != NULL) { // Was a memory (i.e. not serialized) profile?
+ Icc -> Close(Icc); // No, close the stream
+ }
+
+ free(Icc); // Free placeholder memory
+
+ return rc;
+}
+
+
+
+// Write profile ------------------------------------------------------------
+
+
+
+static
+LCMSBOOL SaveWordsTable(int nEntries, LPWORD Tab, LPLCMSICCPROFILE Icc)
+{
+ size_t nTabSize = sizeof(WORD) * nEntries;
+ LPWORD PtrW = (LPWORD) _cmsMalloc(nTabSize);
+ LCMSBOOL rc;
+
+ if (!PtrW) return FALSE;
+ CopyMemory(PtrW, Tab, nTabSize);
+ AdjustEndianessArray16(PtrW, nEntries);
+ rc = Icc ->Write(Icc, nTabSize, PtrW);
+ free(PtrW);
+
+ return rc;
+}
+
+
+
+// Saves profile header
+
+static
+LCMSBOOL SaveHeader(LPLCMSICCPROFILE Icc)
+{
+ icHeader Header;
+ time_t now = time(NULL);
+
+ Header.size = TransportValue32((icInt32Number) Icc ->UsedSpace);
+ Header.cmmId = TransportValue32(lcmsSignature);
+ Header.version = TransportValue32((icInt32Number) 0x02300000);
+ Header.deviceClass = (icProfileClassSignature) TransportValue32(Icc -> DeviceClass);
+ Header.colorSpace = (icColorSpaceSignature) TransportValue32(Icc -> ColorSpace);
+ Header.pcs = (icColorSpaceSignature) TransportValue32(Icc -> PCS);
+
+ // NOTE: in v4 Timestamp must be in UTC rather than in local time
+ EncodeDateTimeNumber(&Header.date, gmtime(&now));
+
+ Header.magic = TransportValue32(icMagicNumber);
+
+#ifdef NON_WINDOWS
+ Header.platform = (icPlatformSignature)TransportValue32(icSigMacintosh);
+#else
+ Header.platform = (icPlatformSignature)TransportValue32(icSigMicrosoft);
+#endif
+
+ Header.flags = TransportValue32(Icc -> flags);
+ Header.manufacturer = TransportValue32(lcmsSignature);
+ Header.model = TransportValue32(0);
+ Header.attributes[0]= TransportValue32(Icc -> attributes);
+ Header.attributes[1]= TransportValue32(0);
+
+ Header.renderingIntent = TransportValue32(Icc -> RenderingIntent);
+
+ // Illuminant is D50
+
+ Header.illuminant.X = TransportValue32(DOUBLE_TO_FIXED(Icc -> Illuminant.X));
+ Header.illuminant.Y = TransportValue32(DOUBLE_TO_FIXED(Icc -> Illuminant.Y));
+ Header.illuminant.Z = TransportValue32(DOUBLE_TO_FIXED(Icc -> Illuminant.Z));
+
+ Header.creator = TransportValue32(lcmsSignature);
+
+ ZeroMemory(&Header.reserved, sizeof(Header.reserved));
+
+ // Set profile ID
+ CopyMemory(Header.reserved, Icc ->ProfileID, 16);
+
+
+ Icc ->UsedSpace = 0; // Mark as begin-of-file
+
+ return Icc ->Write(Icc, sizeof(icHeader), &Header);
+}
+
+
+
+// Setup base marker
+
+static
+LCMSBOOL SetupBase(icTagTypeSignature sig, LPLCMSICCPROFILE Icc)
+{
+ icTagBase Base;
+
+ Base.sig = (icTagTypeSignature) TransportValue32(sig);
+ ZeroMemory(&Base.reserved, sizeof(Base.reserved));
+ return Icc -> Write(Icc, sizeof(icTagBase), &Base);
+}
+
+
+// Store a XYZ tag
+
+static
+LCMSBOOL SaveXYZNumber(LPcmsCIEXYZ Value, LPLCMSICCPROFILE Icc)
+{
+
+ icXYZNumber XYZ;
+
+ if (!SetupBase(icSigXYZType, Icc)) return FALSE;
+
+ XYZ.X = TransportValue32(DOUBLE_TO_FIXED(Value -> X));
+ XYZ.Y = TransportValue32(DOUBLE_TO_FIXED(Value -> Y));
+ XYZ.Z = TransportValue32(DOUBLE_TO_FIXED(Value -> Z));
+
+
+ return Icc -> Write(Icc, sizeof(icXYZNumber), &XYZ);
+}
+
+
+// Store a XYZ array.
+
+static
+LCMSBOOL SaveXYZArray(int n, LPcmsCIEXYZ Value, LPLCMSICCPROFILE Icc)
+{
+ int i;
+ icXYZNumber XYZ;
+
+ if (!SetupBase(icSigS15Fixed16ArrayType, Icc)) return FALSE;
+
+ for (i=0; i < n; i++) {
+
+ XYZ.X = TransportValue32(DOUBLE_TO_FIXED(Value -> X));
+ XYZ.Y = TransportValue32(DOUBLE_TO_FIXED(Value -> Y));
+ XYZ.Z = TransportValue32(DOUBLE_TO_FIXED(Value -> Z));
+
+ if (!Icc -> Write(Icc, sizeof(icXYZNumber), &XYZ)) return FALSE;
+
+ Value++;
+ }
+
+ return TRUE;
+}
+
+
+
+// Save a gamma structure as a table
+
+static
+LCMSBOOL SaveGammaTable(LPGAMMATABLE Gamma, LPLCMSICCPROFILE Icc)
+{
+ icInt32Number Count;
+
+ if (!SetupBase(icSigCurveType, Icc)) return FALSE;
+
+ Count = TransportValue32(Gamma->nEntries);
+
+ if (!Icc ->Write(Icc, sizeof(icInt32Number), &Count)) return FALSE;
+
+ return SaveWordsTable(Gamma->nEntries, Gamma ->GammaTable, Icc);
+}
+
+
+// Save a gamma structure as a one-value
+
+static
+LCMSBOOL SaveGammaOneValue(LPGAMMATABLE Gamma, LPLCMSICCPROFILE Icc)
+{
+ icInt32Number Count;
+ Fixed32 GammaFixed32;
+ WORD GammaFixed8;
+
+ if (!SetupBase(icSigCurveType, Icc)) return FALSE;
+
+ Count = TransportValue32(1);
+ if (!Icc ->Write(Icc, sizeof(icInt32Number), &Count)) return FALSE;
+
+ GammaFixed32 = DOUBLE_TO_FIXED(Gamma ->Seed.Params[0]);
+ GammaFixed8 = (WORD) ((GammaFixed32 >> 8) & 0xFFFF);
+ GammaFixed8 = TransportValue16(GammaFixed8);
+
+ return Icc ->Write(Icc, sizeof(icInt16Number), &GammaFixed8);
+}
+
+// Save a gamma structure as a parametric gamma
+
+static
+LCMSBOOL SaveGammaParametric(LPGAMMATABLE Gamma, LPLCMSICCPROFILE Icc)
+{
+ icUInt16Number Type, Reserved;
+ int i, nParams;
+ int ParamsByType[] = { 1, 3, 4, 5, 7 };
+
+ if (!SetupBase(icSigParametricCurveType, Icc)) return FALSE;
+
+ nParams = ParamsByType[Gamma -> Seed.Type];
+
+ Type = (icUInt16Number) TransportValue16((WORD) Gamma -> Seed. Type);
+ Reserved = (icUInt16Number) TransportValue16((WORD) 0);
+
+ Icc -> Write(Icc, sizeof(icInt16Number), &Type);
+ Icc -> Write(Icc, sizeof(icUInt16Number), &Reserved);
+
+ for (i=0; i < nParams; i++) {
+
+ icInt32Number val = TransportValue32(DOUBLE_TO_FIXED(Gamma -> Seed.Params[i]));
+ Icc ->Write(Icc, sizeof(icInt32Number), &val);
+ }
+
+
+ return TRUE;
+
+}
+
+
+// Save a gamma table
+
+static
+LCMSBOOL SaveGamma(LPGAMMATABLE Gamma, LPLCMSICCPROFILE Icc)
+{
+ // Is the gamma curve type supported by ICC format?
+
+ if (Gamma -> Seed.Type < 0 || Gamma -> Seed.Type > 5 ||
+
+ // has been modified by user?
+
+ _cmsCrc32OfGammaTable(Gamma) != Gamma -> Seed.Crc32) {
+
+ return SaveGammaTable(Gamma, Icc);
+ }
+
+ if (Gamma -> Seed.Type == 1) return SaveGammaOneValue(Gamma, Icc);
+
+ // Only v4 profiles are allowed to hold parametric curves
+
+ if (cmsGetProfileICCversion((cmsHPROFILE) Icc) >= 0x4000000)
+ return SaveGammaParametric(Gamma, Icc);
+
+ // Defaults to save as table
+
+ return SaveGammaTable(Gamma, Icc);
+
+}
+
+
+
+
+// Save an DESC Tag
+
+static
+LCMSBOOL SaveDescription(const char *Text, LPLCMSICCPROFILE Icc)
+{
+
+ icUInt32Number len, Count, TotalSize, AlignedSize;
+ char Filler[256];
+
+ len = (icUInt32Number) (strlen(Text) + 1);
+
+ // * icInt8Number desc[count] * NULL terminated ascii string
+ // * icUInt32Number ucLangCode; * UniCode language code
+ // * icUInt32Number ucCount; * UniCode description length
+ // * icInt16Number ucDesc[ucCount];* The UniCode description
+ // * icUInt16Number scCode; * ScriptCode code
+ // * icUInt8Number scCount; * ScriptCode count
+ // * icInt8Number scDesc[67]; * ScriptCode Description
+
+ TotalSize = sizeof(icTagBase) + sizeof(icUInt32Number) + len +
+ sizeof(icUInt32Number) + sizeof(icUInt32Number) +
+ sizeof(icUInt16Number) + sizeof(icUInt8Number) + 67;
+
+ AlignedSize = TotalSize; // Can be unaligned!!
+
+ if (!SetupBase(icSigTextDescriptionType, Icc)) return FALSE;
+ AlignedSize -= sizeof(icTagBase);
+
+ Count = TransportValue32(len);
+ if (!Icc ->Write(Icc, sizeof(icUInt32Number), &Count)) return FALSE;
+ AlignedSize -= sizeof(icUInt32Number);
+
+ if (!Icc ->Write(Icc, len, (LPVOID)Text)) return FALSE;
+ AlignedSize -= len;
+
+ if (AlignedSize < 0)
+ AlignedSize = 0;
+ if (AlignedSize > 255)
+ AlignedSize = 255;
+
+ ZeroMemory(Filler, AlignedSize);
+ if (!Icc ->Write(Icc, AlignedSize, Filler)) return FALSE;
+
+ return TRUE;
+}
+
+// Save an ASCII Tag
+
+static
+LCMSBOOL SaveText(const char *Text, LPLCMSICCPROFILE Icc)
+{
+ size_t len = strlen(Text) + 1;
+
+ if (!SetupBase(icSigTextType, Icc)) return FALSE;
+ if (!Icc ->Write(Icc, len, (LPVOID) Text)) return FALSE;
+ return TRUE;
+}
+
+
+// Save one of these new chromaticity values
+
+static
+LCMSBOOL SaveOneChromaticity(double x, double y, LPLCMSICCPROFILE Icc)
+{
+ Fixed32 xf, yf;
+
+ xf = TransportValue32(DOUBLE_TO_FIXED(x));
+ yf = TransportValue32(DOUBLE_TO_FIXED(y));
+
+ if (!Icc ->Write(Icc, sizeof(Fixed32), &xf)) return FALSE;
+ if (!Icc ->Write(Icc, sizeof(Fixed32), &yf)) return FALSE;
+
+ return TRUE;
+}
+
+
+// New tag added in Addendum II of old spec.
+
+static
+LCMSBOOL SaveChromaticities(LPcmsCIExyYTRIPLE chrm, LPLCMSICCPROFILE Icc)
+{
+ WORD nChans, Table;
+
+ if (!SetupBase(icSigChromaticityType, Icc)) return FALSE;
+
+ nChans = TransportValue16(3);
+ if (!Icc ->Write(Icc, sizeof(WORD) , &nChans)) return FALSE;
+ Table = TransportValue16(0);
+ if (!Icc ->Write(Icc, sizeof(WORD) , &Table)) return FALSE;
+
+ if (!SaveOneChromaticity(chrm -> Red.x, chrm -> Red.y, Icc)) return FALSE;
+ if (!SaveOneChromaticity(chrm -> Green.x, chrm -> Green.y, Icc)) return FALSE;
+ if (!SaveOneChromaticity(chrm -> Blue.x, chrm -> Blue.y, Icc)) return FALSE;
+
+ return TRUE;
+}
+
+
+static
+LCMSBOOL SaveSequenceDescriptionTag(LPcmsSEQ seq, LPLCMSICCPROFILE Icc)
+{
+ icUInt32Number nSeqs;
+ icDescStruct DescStruct;
+ int i, n = seq ->n;
+ LPcmsPSEQDESC pseq = seq ->seq;
+
+ if (!SetupBase(icSigProfileSequenceDescType, Icc)) return FALSE;
+
+ nSeqs = TransportValue32(n);
+
+ if (!Icc ->Write(Icc, sizeof(icUInt32Number) , &nSeqs)) return FALSE;
+
+ for (i=0; i < n; i++) {
+
+ LPcmsPSEQDESC sec = pseq + i;
+
+
+ DescStruct.deviceMfg = (icTagTypeSignature) TransportValue32(sec ->deviceMfg);
+ DescStruct.deviceModel = (icTagTypeSignature) TransportValue32(sec ->deviceModel);
+ DescStruct.technology = (icTechnologySignature) TransportValue32(sec ->technology);
+ DescStruct.attributes[0]= TransportValue32(sec ->attributes[0]);
+ DescStruct.attributes[1]= TransportValue32(sec ->attributes[1]);
+
+ if (!Icc ->Write(Icc, sizeof(icDescStruct) - SIZEOF_UINT8_ALIGNED, &DescStruct)) return FALSE;
+
+ if (!SaveDescription(sec ->Manufacturer, Icc)) return FALSE;
+ if (!SaveDescription(sec ->Model, Icc)) return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+// Saves a timestamp tag
+
+static
+LCMSBOOL SaveDateTimeNumber(const struct tm *DateTime, LPLCMSICCPROFILE Icc)
+{
+ icDateTimeNumber Dest;
+
+ if (!SetupBase(icSigDateTimeType, Icc)) return FALSE;
+ EncodeDateTimeNumber(&Dest, DateTime);
+ if (!Icc ->Write(Icc, sizeof(icDateTimeNumber), &Dest)) return FALSE;
+
+ return TRUE;
+}
+
+
+// Saves a named color list into a named color profile
+static
+LCMSBOOL SaveNamedColorList(LPcmsNAMEDCOLORLIST NamedColorList, LPLCMSICCPROFILE Icc)
+{
+
+ icUInt32Number vendorFlag; // Bottom 16 bits for IC use
+ icUInt32Number count; // Count of named colors
+ icUInt32Number nDeviceCoords; // Num of device coordinates
+ char prefix[32]; // Prefix for each color name
+ char suffix[32]; // Suffix for each color name
+ int i;
+
+ if (!SetupBase(icSigNamedColor2Type, Icc)) return FALSE;
+
+ vendorFlag = TransportValue32(0);
+ count = TransportValue32(NamedColorList ->nColors);
+ nDeviceCoords = TransportValue32(NamedColorList ->ColorantCount);
+
+ strncpy(prefix, (const char*) NamedColorList->Prefix, 31);
+ strncpy(suffix, (const char*) NamedColorList->Suffix, 31);
+
+ suffix[31] = prefix[31] = 0;
+
+ if (!Icc ->Write(Icc, sizeof(icUInt32Number), &vendorFlag)) return FALSE;
+ if (!Icc ->Write(Icc, sizeof(icUInt32Number), &count)) return FALSE;
+ if (!Icc ->Write(Icc, sizeof(icUInt32Number), &nDeviceCoords)) return FALSE;
+ if (!Icc ->Write(Icc, 32 , prefix)) return FALSE;
+ if (!Icc ->Write(Icc, 32 , suffix)) return FALSE;
+
+ for (i=0; i < NamedColorList ->nColors; i++) {
+
+ icUInt16Number PCS[3];
+ icUInt16Number Colorant[MAXCHANNELS];
+ char root[32];
+ LPcmsNAMEDCOLOR Color;
+ int j;
+
+ Color = NamedColorList ->List + i;
+
+ strncpy(root, Color ->Name, 32);
+ Color ->Name[32] = 0;
+
+ if (!Icc ->Write(Icc, 32 , root)) return FALSE;
+
+ for (j=0; j < 3; j++)
+ PCS[j] = TransportValue16(Color ->PCS[j]);
+
+ if (!Icc ->Write(Icc, 3 * sizeof(icUInt16Number), PCS)) return FALSE;
+
+ for (j=0; j < NamedColorList ->ColorantCount; j++)
+ Colorant[j] = TransportValue16(Color ->DeviceColorant[j]);
+
+ if (!Icc ->Write(Icc,
+ NamedColorList ->ColorantCount * sizeof(icUInt16Number), Colorant)) return FALSE;
+ }
+
+
+ return TRUE;
+}
+
+
+
+// Saves a colorant table. It is using the named color structure for simplicity sake
+
+static
+LCMSBOOL SaveColorantTable(LPcmsNAMEDCOLORLIST NamedColorList, LPLCMSICCPROFILE Icc)
+{
+ icUInt32Number count; // Count of named colors
+ int i;
+
+ if (!SetupBase(icSigColorantTableType, Icc)) return FALSE;
+
+ count = TransportValue32(NamedColorList ->nColors);
+
+ if (!Icc ->Write(Icc, sizeof(icUInt32Number), &count)) return FALSE;
+
+ for (i=0; i < NamedColorList ->nColors; i++) {
+
+ icUInt16Number PCS[3];
+ icInt8Number root[33];
+ LPcmsNAMEDCOLOR Color;
+ int j;
+
+ Color = NamedColorList ->List + i;
+
+ strncpy((char*) root, Color ->Name, 32);
+ root[32] = 0;
+
+ if (!Icc ->Write(Icc, 32 , root)) return FALSE;
+
+ for (j=0; j < 3; j++)
+ PCS[j] = TransportValue16(Color ->PCS[j]);
+
+ if (!Icc ->Write(Icc, 3 * sizeof(icUInt16Number), PCS)) return FALSE;
+
+ }
+
+
+ return TRUE;
+}
+
+// Does serialization of LUT16 and writes it.
+
+static
+LCMSBOOL SaveLUT(const LUT* NewLUT, LPLCMSICCPROFILE Icc)
+{
+ icLut16 LUT16;
+ unsigned int i;
+ size_t nTabSize;
+ WORD NullTbl[2] = { 0, 0xFFFFU};
+
+
+ if (!SetupBase(icSigLut16Type, Icc)) return FALSE;
+
+ LUT16.clutPoints = (icUInt8Number) NewLUT -> cLutPoints;
+ LUT16.inputChan = (icUInt8Number) NewLUT -> InputChan;
+ LUT16.outputChan = (icUInt8Number) NewLUT -> OutputChan;
+ LUT16.pad = 0;
+
+ LUT16.inputEnt = TransportValue16((WORD) ((NewLUT -> wFlags & LUT_HASTL1) ? NewLUT -> InputEntries : 2));
+ LUT16.outputEnt = TransportValue16((WORD) ((NewLUT -> wFlags & LUT_HASTL2) ? NewLUT -> OutputEntries : 2));
+
+ if (NewLUT -> wFlags & LUT_HASMATRIX) {
+
+ LUT16.e00 = TransportValue32(NewLUT -> Matrix.v[0].n[0]);
+ LUT16.e01 = TransportValue32(NewLUT -> Matrix.v[0].n[1]);
+ LUT16.e02 = TransportValue32(NewLUT -> Matrix.v[0].n[2]);
+ LUT16.e10 = TransportValue32(NewLUT -> Matrix.v[1].n[0]);
+ LUT16.e11 = TransportValue32(NewLUT -> Matrix.v[1].n[1]);
+ LUT16.e12 = TransportValue32(NewLUT -> Matrix.v[1].n[2]);
+ LUT16.e20 = TransportValue32(NewLUT -> Matrix.v[2].n[0]);
+ LUT16.e21 = TransportValue32(NewLUT -> Matrix.v[2].n[1]);
+ LUT16.e22 = TransportValue32(NewLUT -> Matrix.v[2].n[2]);
+ }
+ else {
+
+ LUT16.e00 = TransportValue32(DOUBLE_TO_FIXED(1));
+ LUT16.e01 = TransportValue32(DOUBLE_TO_FIXED(0));
+ LUT16.e02 = TransportValue32(DOUBLE_TO_FIXED(0));
+ LUT16.e10 = TransportValue32(DOUBLE_TO_FIXED(0));
+ LUT16.e11 = TransportValue32(DOUBLE_TO_FIXED(1));
+ LUT16.e12 = TransportValue32(DOUBLE_TO_FIXED(0));
+ LUT16.e20 = TransportValue32(DOUBLE_TO_FIXED(0));
+ LUT16.e21 = TransportValue32(DOUBLE_TO_FIXED(0));
+ LUT16.e22 = TransportValue32(DOUBLE_TO_FIXED(1));
+ }
+
+
+ // Save header
+
+ Icc -> Write(Icc, sizeof(icLut16)- SIZEOF_UINT16_ALIGNED, &LUT16);
+
+ // The prelinearization table
+
+ for (i=0; i < NewLUT -> InputChan; i++) {
+
+ if (NewLUT -> wFlags & LUT_HASTL1) {
+
+ if (!SaveWordsTable(NewLUT -> InputEntries,
+ NewLUT -> L1[i], Icc)) return FALSE;
+
+ }
+ else Icc -> Write(Icc, sizeof(WORD)* 2, NullTbl);
+ }
+
+
+ nTabSize = (NewLUT -> OutputChan * uipow(NewLUT->cLutPoints,
+ NewLUT->InputChan));
+ // The 3D CLUT.
+
+ if (nTabSize > 0) {
+
+ if (!SaveWordsTable((int) nTabSize, NewLUT -> T, Icc)) return FALSE;
+ }
+ // The postlinearization table
+
+ for (i=0; i < NewLUT -> OutputChan; i++) {
+
+ if (NewLUT -> wFlags & LUT_HASTL2) {
+
+ if (!SaveWordsTable(NewLUT -> OutputEntries,
+ NewLUT -> L2[i], Icc)) return FALSE;
+ }
+ else Icc -> Write(Icc, sizeof(WORD)* 2, NullTbl);
+
+ }
+
+ return TRUE;
+}
+
+
+
+// Does serialization of LUT8 and writes it
+
+static
+LCMSBOOL SaveLUT8(const LUT* NewLUT, LPLCMSICCPROFILE Icc)
+{
+ icLut8 LUT8;
+ unsigned int i, j;
+ size_t nTabSize;
+ BYTE val;
+
+ // Sanity check
+
+ if (NewLUT -> wFlags & LUT_HASTL1) {
+
+ if (NewLUT -> InputEntries != 256) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "LUT8 needs 256 entries on prelinearization");
+ return FALSE;
+ }
+
+ }
+
+
+ if (NewLUT -> wFlags & LUT_HASTL2) {
+
+ if (NewLUT -> OutputEntries != 256) {
+ cmsSignalError(LCMS_ERRC_ABORTED, "LUT8 needs 256 entries on postlinearization");
+ return FALSE;
+ }
+ }
+
+
+
+ if (!SetupBase(icSigLut8Type, Icc)) return FALSE;
+
+ LUT8.clutPoints = (icUInt8Number) NewLUT -> cLutPoints;
+ LUT8.inputChan = (icUInt8Number) NewLUT -> InputChan;
+ LUT8.outputChan = (icUInt8Number) NewLUT -> OutputChan;
+
+
+ if (NewLUT -> wFlags & LUT_HASMATRIX) {
+
+ LUT8.e00 = TransportValue32(NewLUT -> Matrix.v[0].n[0]);
+ LUT8.e01 = TransportValue32(NewLUT -> Matrix.v[0].n[1]);
+ LUT8.e02 = TransportValue32(NewLUT -> Matrix.v[0].n[2]);
+ LUT8.e10 = TransportValue32(NewLUT -> Matrix.v[1].n[0]);
+ LUT8.e11 = TransportValue32(NewLUT -> Matrix.v[1].n[1]);
+ LUT8.e12 = TransportValue32(NewLUT -> Matrix.v[1].n[2]);
+ LUT8.e20 = TransportValue32(NewLUT -> Matrix.v[2].n[0]);
+ LUT8.e21 = TransportValue32(NewLUT -> Matrix.v[2].n[1]);
+ LUT8.e22 = TransportValue32(NewLUT -> Matrix.v[2].n[2]);
+ }
+ else {
+
+ LUT8.e00 = TransportValue32(DOUBLE_TO_FIXED(1));
+ LUT8.e01 = TransportValue32(DOUBLE_TO_FIXED(0));
+ LUT8.e02 = TransportValue32(DOUBLE_TO_FIXED(0));
+ LUT8.e10 = TransportValue32(DOUBLE_TO_FIXED(0));
+ LUT8.e11 = TransportValue32(DOUBLE_TO_FIXED(1));
+ LUT8.e12 = TransportValue32(DOUBLE_TO_FIXED(0));
+ LUT8.e20 = TransportValue32(DOUBLE_TO_FIXED(0));
+ LUT8.e21 = TransportValue32(DOUBLE_TO_FIXED(0));
+ LUT8.e22 = TransportValue32(DOUBLE_TO_FIXED(1));
+ }
+
+
+ // Save header
+
+ Icc -> Write(Icc, sizeof(icLut8)- SIZEOF_UINT8_ALIGNED, &LUT8);
+
+ // The prelinearization table
+
+ for (i=0; i < NewLUT -> InputChan; i++) {
+
+ for (j=0; j < 256; j++) {
+
+ if (NewLUT -> wFlags & LUT_HASTL1)
+ val = (BYTE) floor(NewLUT ->L1[i][j] / 257.0 + .5);
+ else
+ val = (BYTE) j;
+
+ Icc ->Write(Icc, 1, &val);
+ }
+
+ }
+
+
+ nTabSize = (NewLUT -> OutputChan * uipow(NewLUT->cLutPoints,
+ NewLUT->InputChan));
+ // The 3D CLUT.
+
+ for (j=0; j < nTabSize; j++) {
+
+ val = (BYTE) floor(NewLUT ->T[j] / 257.0 + .5);
+ Icc ->Write(Icc, 1, &val);
+ }
+
+ // The postlinearization table
+
+ for (i=0; i < NewLUT -> OutputChan; i++) {
+
+ for (j=0; j < 256; j++) {
+
+ if (NewLUT -> wFlags & LUT_HASTL2)
+ val = (BYTE) floor(NewLUT ->L2[i][j] / 257.0 + .5);
+ else
+ val = (BYTE) j;
+
+ Icc ->Write(Icc, 1, &val);
+ }
+
+ }
+
+ return TRUE;
+}
+
+
+
+// Set the LUT bitdepth to be saved
+
+void LCMSEXPORT _cmsSetLUTdepth(cmsHPROFILE hProfile, int depth)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+
+ switch (depth) {
+
+ case 8: Icc ->SaveAs8Bits = TRUE; break;
+ case 16: Icc ->SaveAs8Bits = FALSE; break;
+
+ default:
+ cmsSignalError(LCMS_ERRC_ABORTED, "%d is an unsupported as bitdepth, use 8 or 16 only.", depth);
+ }
+}
+
+
+// Saves Tag directory
+
+static
+LCMSBOOL SaveTagDirectory(LPLCMSICCPROFILE Icc)
+{
+ icInt32Number i;
+ icTag Tag;
+ icInt32Number Count = 0;
+
+ // Get true count
+ for (i=0; i < Icc -> TagCount; i++) {
+ if (Icc ->TagNames[i] != 0)
+ Count++;
+ }
+
+ Count = TransportValue32(Count);
+ if (!Icc ->Write(Icc, sizeof(icInt32Number) , &Count)) return FALSE;
+
+ for (i=0; i < Icc -> TagCount; i++) {
+
+ if (Icc ->TagNames[i] == 0) continue;
+
+ Tag.sig = (icTagSignature)TransportValue32(Icc -> TagNames[i]);
+ Tag.offset = TransportValue32((icInt32Number) Icc -> TagOffsets[i]);
+ Tag.size = TransportValue32((icInt32Number) Icc -> TagSizes[i]);
+
+ if (!Icc ->Write(Icc, sizeof(icTag), &Tag)) return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+// Dump tag contents
+
+static
+LCMSBOOL SaveTags(LPLCMSICCPROFILE Icc, LPLCMSICCPROFILE FileOrig)
+{
+
+ LPBYTE Data;
+ icInt32Number i;
+ size_t Begin;
+ size_t AlignedSpace, FillerSize;
+
+
+ for (i=0; i < Icc -> TagCount; i++) {
+
+ if (Icc ->TagNames[i] == 0) continue;
+
+ // Align to DWORD boundary, following new spec.
+
+ AlignedSpace = ALIGNLONG(Icc ->UsedSpace);
+ FillerSize = AlignedSpace - Icc ->UsedSpace;
+ if (FillerSize > 0) {
+
+ BYTE Filler[20];
+
+ ZeroMemory(Filler, 16);
+ if (!Icc ->Write(Icc, FillerSize, Filler)) return FALSE;
+ }
+
+
+ Icc -> TagOffsets[i] = Begin = Icc ->UsedSpace;
+ Data = (LPBYTE) Icc -> TagPtrs[i];
+ if (!Data) {
+
+ // Reach here if we are copying a tag from a disk-based ICC profile which has not been modified by user.
+ // In this case a blind copy of the block data is performed
+
+ if (Icc -> TagOffsets[i]) {
+
+ size_t TagSize = FileOrig -> TagSizes[i];
+ size_t TagOffset = FileOrig -> TagOffsets[i];
+ void* Mem;
+
+ if (FileOrig ->Seek(FileOrig, TagOffset)) return FALSE;
+
+ Mem = _cmsMalloc(TagSize);
+
+ if (FileOrig ->Read(Mem, TagSize, 1, FileOrig) != 1) return FALSE;
+ if (!Icc ->Write(Icc, TagSize, Mem)) return FALSE;
+
+ Icc -> TagSizes[i] = (Icc ->UsedSpace - Begin);
+ free(Mem);
+ }
+
+ continue;
+ }
+
+
+ switch (Icc -> TagNames[i]) {
+
+ case icSigProfileDescriptionTag:
+ case icSigDeviceMfgDescTag:
+ case icSigDeviceModelDescTag:
+ if (!SaveDescription((const char *) Data, Icc)) return FALSE;
+ break;
+
+ case icSigRedColorantTag:
+ case icSigGreenColorantTag:
+ case icSigBlueColorantTag:
+ case icSigMediaWhitePointTag:
+ case icSigMediaBlackPointTag:
+ if (!SaveXYZNumber((LPcmsCIEXYZ) Data, Icc)) return FALSE;
+ break;
+
+
+ case icSigRedTRCTag:
+ case icSigGreenTRCTag:
+ case icSigBlueTRCTag:
+ case icSigGrayTRCTag:
+ if (!SaveGamma((LPGAMMATABLE) Data, Icc)) return FALSE;
+ break;
+
+ case icSigCharTargetTag:
+ case icSigCopyrightTag:
+ if (!SaveText((const char *) Data, Icc)) return FALSE;
+ break;
+
+ case icSigChromaticityTag:
+ if (!SaveChromaticities((LPcmsCIExyYTRIPLE) Data, Icc)) return FALSE;
+ break;
+
+ // Save LUT
+
+ case icSigAToB0Tag:
+ case icSigAToB1Tag:
+ case icSigAToB2Tag:
+ case icSigBToA0Tag:
+ case icSigBToA1Tag:
+ case icSigBToA2Tag:
+ case icSigGamutTag:
+ case icSigPreview0Tag:
+ case icSigPreview1Tag:
+ case icSigPreview2Tag:
+
+ if (Icc ->SaveAs8Bits) {
+
+ if (!SaveLUT8((LPLUT) Data, Icc)) return FALSE;
+ }
+ else {
+
+ if (!SaveLUT((LPLUT) Data, Icc)) return FALSE;
+ }
+ break;
+
+ case icSigProfileSequenceDescTag:
+ if (!SaveSequenceDescriptionTag((LPcmsSEQ) Data, Icc)) return FALSE;
+ break;
+
+
+ case icSigNamedColor2Tag:
+ if (!SaveNamedColorList((LPcmsNAMEDCOLORLIST) Data, Icc)) return FALSE;
+ break;
+
+
+ case icSigCalibrationDateTimeTag:
+ if (!SaveDateTimeNumber((struct tm *) Data, Icc)) return FALSE;
+ break;
+
+
+ case icSigColorantTableTag:
+ case icSigColorantTableOutTag:
+ if (!SaveColorantTable((LPcmsNAMEDCOLORLIST) Data, Icc)) return FALSE;
+ break;
+
+
+ case icSigChromaticAdaptationTag:
+ if (!SaveXYZArray(3, (LPcmsCIEXYZ) Data, Icc)) return FALSE;
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ Icc -> TagSizes[i] = (Icc ->UsedSpace - Begin);
+ }
+
+
+
+ return TRUE;
+}
+
+
+
+// Add tags to profile structure
+
+LCMSBOOL LCMSEXPORT cmsAddTag(cmsHPROFILE hProfile, icTagSignature sig, const void* Tag)
+{
+ LCMSBOOL rc;
+
+ switch (sig) {
+
+ case icSigCharTargetTag:
+ case icSigCopyrightTag:
+ case icSigProfileDescriptionTag:
+ case icSigDeviceMfgDescTag:
+ case icSigDeviceModelDescTag:
+ rc = _cmsAddTextTag(hProfile, sig, (const char*) Tag);
+ break;
+
+ case icSigRedColorantTag:
+ case icSigGreenColorantTag:
+ case icSigBlueColorantTag:
+ case icSigMediaWhitePointTag:
+ case icSigMediaBlackPointTag:
+ rc = _cmsAddXYZTag(hProfile, sig, (const cmsCIEXYZ*) Tag);
+ break;
+
+ case icSigRedTRCTag:
+ case icSigGreenTRCTag:
+ case icSigBlueTRCTag:
+ case icSigGrayTRCTag:
+ rc = _cmsAddGammaTag(hProfile, sig, (LPGAMMATABLE) Tag);
+ break;
+
+ case icSigAToB0Tag:
+ case icSigAToB1Tag:
+ case icSigAToB2Tag:
+ case icSigBToA0Tag:
+ case icSigBToA1Tag:
+ case icSigBToA2Tag:
+ case icSigGamutTag:
+ case icSigPreview0Tag:
+ case icSigPreview1Tag:
+ case icSigPreview2Tag:
+ rc = _cmsAddLUTTag(hProfile, sig, Tag);
+ break;
+
+ case icSigChromaticityTag:
+ rc = _cmsAddChromaticityTag(hProfile, sig, (LPcmsCIExyYTRIPLE) Tag);
+ break;
+
+ case icSigProfileSequenceDescTag:
+ rc = _cmsAddSequenceDescriptionTag(hProfile, sig, (LPcmsSEQ) Tag);
+ break;
+
+ case icSigNamedColor2Tag:
+ rc = _cmsAddNamedColorTag(hProfile, sig, (LPcmsNAMEDCOLORLIST) Tag);
+ break;
+
+ case icSigCalibrationDateTimeTag:
+ rc = _cmsAddDateTimeTag(hProfile, sig, (struct tm*) Tag);
+ break;
+
+ case icSigColorantTableTag:
+ case icSigColorantTableOutTag:
+ rc = _cmsAddColorantTableTag(hProfile, sig, (LPcmsNAMEDCOLORLIST) Tag);
+ break;
+
+
+ case icSigChromaticAdaptationTag:
+ rc = _cmsAddChromaticAdaptationTag(hProfile, sig, (const cmsCIEXYZ*) Tag);
+ break;
+
+ default:
+ cmsSignalError(LCMS_ERRC_ABORTED, "cmsAddTag: Tag '%x' is unsupported", sig);
+ return FALSE;
+ }
+
+ // Check for critical tags
+
+ switch (sig) {
+
+ case icSigMediaWhitePointTag:
+ case icSigMediaBlackPointTag:
+ case icSigChromaticAdaptationTag:
+
+ ReadCriticalTags((LPLCMSICCPROFILE) hProfile);
+ break;
+
+ default:;
+ }
+
+ return rc;
+
+}
+
+// Low-level save to disk. It closes the profile on exit
+
+LCMSBOOL LCMSEXPORT _cmsSaveProfile(cmsHPROFILE hProfile, const char* FileName)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+ LCMSICCPROFILE Keep;
+ LCMSBOOL rc;
+
+ CopyMemory(&Keep, Icc, sizeof(LCMSICCPROFILE));
+ _cmsSetSaveToDisk(Icc, NULL);
+
+ // Pass #1 does compute offsets
+
+ if (!SaveHeader(Icc)) return FALSE;
+ if (!SaveTagDirectory(Icc)) return FALSE;
+ if (!SaveTags(Icc, &Keep)) return FALSE;
+
+
+ _cmsSetSaveToDisk(Icc, FileName);
+
+
+ // Pass #2 does save to file
+
+ if (!SaveHeader(Icc)) goto CleanUp;
+ if (!SaveTagDirectory(Icc)) goto CleanUp;
+ if (!SaveTags(Icc, &Keep)) goto CleanUp;
+
+ rc = (Icc ->Close(Icc) == 0);
+ CopyMemory(Icc, &Keep, sizeof(LCMSICCPROFILE));
+ return rc;
+
+
+ CleanUp:
+
+ Icc ->Close(Icc);
+ unlink(FileName);
+ CopyMemory(Icc, &Keep, sizeof(LCMSICCPROFILE));
+ return FALSE;
+}
+
+
+// Low-level save from open stream
+LCMSBOOL LCMSEXPORT _cmsSaveProfileToMem(cmsHPROFILE hProfile, void *MemPtr,
+ size_t* BytesNeeded)
+{
+ LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
+ LCMSICCPROFILE Keep;
+
+
+ CopyMemory(&Keep, Icc, sizeof(LCMSICCPROFILE));
+
+ _cmsSetSaveToMemory(Icc, NULL, 0);
+
+ // Pass #1 does compute offsets
+
+ if (!SaveHeader(Icc)) return FALSE;
+ if (!SaveTagDirectory(Icc)) return FALSE;
+ if (!SaveTags(Icc, &Keep)) return FALSE;
+
+ if (!MemPtr) {
+
+ // update BytesSaved so caller knows how many bytes are needed for MemPtr
+ *BytesNeeded = Icc ->UsedSpace;
+ CopyMemory(Icc, &Keep, sizeof(LCMSICCPROFILE));
+ return TRUE;
+ }
+
+ if (*BytesNeeded < Icc ->UsedSpace) {
+
+ // need at least UsedSpace in MemPtr to continue
+ CopyMemory(Icc, &Keep, sizeof(LCMSICCPROFILE));
+ return FALSE;
+ }
+
+ _cmsSetSaveToMemory(Icc, MemPtr, *BytesNeeded);
+
+
+ // Pass #2 does save to file into supplied stream
+ if (!SaveHeader(Icc)) goto CleanUp;
+ if (!SaveTagDirectory(Icc)) goto CleanUp;
+ if (!SaveTags(Icc, &Keep)) goto CleanUp;
+
+ // update BytesSaved so caller knows how many bytes put into stream
+ *BytesNeeded = Icc ->UsedSpace;
+
+ Icc ->Close(Icc);
+ CopyMemory(Icc, &Keep, sizeof(LCMSICCPROFILE));
+ return TRUE;
+
+CleanUp:
+
+ Icc ->Close(Icc);
+ CopyMemory(Icc, &Keep, sizeof(LCMSICCPROFILE));
+ return FALSE;
+}
+