| /* |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| // This file is available under and governed by the GNU General Public |
| // License version 2 only, as published by the Free Software Foundation. |
| // However, the following notice accompanied the original version of this |
| // file: |
| // |
| // |
| // 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]) { |
| if (!_cmsValidateLUT((LPLUT) Icc ->TagPtrs[n])) { |
| return NULL; |
| } |
| |
| 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 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(×tamp, 1, sizeof(icDateTimeNumber), Icc) != sizeof(icDateTimeNumber)) |
| return FALSE; |
| |
| DecodeDateTimeNumber(×tamp, 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.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; |
| } |
| |