3 // Copyright (C) 1998-2007 Marti Maria
5 // Permission is hereby granted, free of charge, to any person obtaining
6 // a copy of this software and associated documentation files (the "Software"),
7 // to deal in the Software without restriction, including without limitation
8 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 // and/or sell copies of the Software, and to permit persons to whom the Software
10 // is furnished to do so, subject to the following conditions:
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
17 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 // ICC profile serialization
28 // ----------------------------------------------------------------- Tag Serialization
30 // Alignment of ICC file format uses 4 bytes DWORD
32 #define ALIGNLONG(x) (((x)+3) & ~(3)) // Aligns to DWORD boundary
35 static int GlobalLanguageCode
; // Language & country descriptors, for ICC 4.0 support
36 static int GlobalCountryCode
;
40 # define USE_CUSTOM_SWAB 1
44 #ifdef USE_CUSTOM_SWAB
46 // Replacement to swab function, thanks to YNOP
47 // for providing the BeOS port
49 // from: @(#)swab.c 5.10 (Berkeley) 3/6/91
52 void xswab(const void *from
, void *to
, size_t len
)
54 register unsigned long temp
;
56 register char *fp
, *tp
;
61 #define STEP temp = *fp++,*tp++ = *fp++,*tp++ = temp
62 /* round to multiple of 8 */
68 STEP
; STEP
; STEP
; STEP
;
69 STEP
; STEP
; STEP
; STEP
;
79 // Little-Endian to Big-Endian
83 #define AdjustEndianess16(a)
84 #define AdjustEndianess32(a)
85 #define AdjustEndianessArray16(a, b)
89 void AdjustEndianess16(LPBYTE pByte
)
99 void AdjustEndianess32(LPBYTE pByte
)
113 // swap bytes in a array of words
116 void AdjustEndianessArray16(LPWORD p
, size_t num_words
)
118 xswab((char*) p
, (char*)p
, (int) num_words
* sizeof(WORD
));
124 // Transports to properly encoded values - note that icc profiles does use
125 // big endian notation.
128 icInt32Number
TransportValue32(icInt32Number Value
)
130 icInt32Number Temp
= Value
;
132 AdjustEndianess32((LPBYTE
) &Temp
);
137 WORD
TransportValue16(WORD Value
)
141 AdjustEndianess16((LPBYTE
) &Temp
);
146 // from Fixed point 8.8 to double
149 double Convert8Fixed8(WORD fixed8
)
153 lsb
= (BYTE
) (fixed8
& 0xff);
154 msb
= (BYTE
) (((WORD
) fixed8
>> 8) & 0xff);
156 return (double) ((double) msb
+ ((double) lsb
/ 256.0));
160 // from Fixed point 15.16 to double
162 double Convert15Fixed16(icS15Fixed16Number fix32
)
164 double floater
, sign
, mid
, hack
;
168 AdjustEndianess32((LPBYTE
) &fix32
);
170 sign
= (fix32
< 0 ? -1 : 1);
173 Whole
= LOWORD(fix32
>> 16);
174 FracPart
= LOWORD(fix32
& 0x0000ffffL
);
177 mid
= (double) FracPart
/ hack
;
178 floater
= (double) Whole
+ mid
;
180 return sign
* floater
;
184 // Auxiliar-- read base and return type
187 icTagTypeSignature
ReadBase(LPLCMSICCPROFILE Icc
)
191 Icc
-> Read(&Base
, sizeof(icTagBase
), 1, Icc
);
192 AdjustEndianess32((LPBYTE
) &Base
.sig
);
199 void DecodeDateTimeNumber(const icDateTimeNumber
*Source
, struct tm
*Dest
)
201 Dest
->tm_sec
= TransportValue16(Source
->seconds
);
202 Dest
->tm_min
= TransportValue16(Source
->minutes
);
203 Dest
->tm_hour
= TransportValue16(Source
->hours
);
204 Dest
->tm_mday
= TransportValue16(Source
->day
);
205 Dest
->tm_mon
= TransportValue16(Source
->month
) - 1;
206 Dest
->tm_year
= TransportValue16(Source
->year
) - 1900;
213 void EncodeDateTimeNumber(icDateTimeNumber
*Dest
, const struct tm
*Source
)
215 Dest
->seconds
= TransportValue16((WORD
) Source
->tm_sec
);
216 Dest
->minutes
= TransportValue16((WORD
) Source
->tm_min
);
217 Dest
->hours
= TransportValue16((WORD
) Source
->tm_hour
);
218 Dest
->day
= TransportValue16((WORD
) Source
->tm_mday
);
219 Dest
->month
= TransportValue16((WORD
) (Source
->tm_mon
+ 1));
220 Dest
->year
= TransportValue16((WORD
) (Source
->tm_year
+ 1900));
224 // Jun-21-2000: Some profiles (those that comes with W2K) comes
225 // with the media white (media black?) x 100. Add a sanity check
228 void NormalizeXYZ(LPcmsCIEXYZ Dest
)
230 while (Dest
-> X
> 2. &&
240 // Evaluates a XYZ tristimulous across chromatic adaptation matrix
243 void EvalCHRM(LPcmsCIEXYZ Dest
, LPMAT3 Chrm
, LPcmsCIEXYZ Src
)
251 MAT3eval(&d
, Chrm
, &s
);
260 // Read profile header and validate it
263 LPLCMSICCPROFILE
ReadHeader(LPLCMSICCPROFILE Icc
, LCMSBOOL lIsFromMemory
)
267 icInt32Number TagCount
, i
;
269 if (Icc
-> Read(&Header
, sizeof(icHeader
), 1, Icc
) != 1)
274 AdjustEndianess32((LPBYTE
) &Header
.size
);
275 AdjustEndianess32((LPBYTE
) &Header
.cmmId
);
276 AdjustEndianess32((LPBYTE
) &Header
.version
);
277 AdjustEndianess32((LPBYTE
) &Header
.deviceClass
);
278 AdjustEndianess32((LPBYTE
) &Header
.colorSpace
);
279 AdjustEndianess32((LPBYTE
) &Header
.pcs
);
280 AdjustEndianess32((LPBYTE
) &Header
.magic
);
281 AdjustEndianess32((LPBYTE
) &Header
.flags
);
282 AdjustEndianess32((LPBYTE
) &Header
.attributes
[0]);
283 AdjustEndianess32((LPBYTE
) &Header
.renderingIntent
);
287 if (Header
.magic
!= icMagicNumber
) goto ErrorCleanup
;
289 if (Icc
->Read(&TagCount
, sizeof(icInt32Number
), 1, Icc
) != 1)
292 AdjustEndianess32((LPBYTE
) &TagCount
);
294 Icc
-> DeviceClass
= Header
.deviceClass
;
295 Icc
-> ColorSpace
= Header
.colorSpace
;
296 Icc
-> PCS
= Header
.pcs
;
297 Icc
-> RenderingIntent
= (icRenderingIntent
) Header
.renderingIntent
;
298 Icc
-> flags
= Header
.flags
;
299 Icc
-> attributes
= Header
.attributes
[0];
300 Icc
-> Illuminant
.X
= Convert15Fixed16(Header
.illuminant
.X
);
301 Icc
-> Illuminant
.Y
= Convert15Fixed16(Header
.illuminant
.Y
);
302 Icc
-> Illuminant
.Z
= Convert15Fixed16(Header
.illuminant
.Z
);
303 Icc
-> Version
= Header
.version
;
305 // Get creation date/time
307 DecodeDateTimeNumber(&Header
.date
, &Icc
->Created
);
309 // Fix illuminant, some profiles are broken in this field!
311 Icc
->Illuminant
= *cmsD50_XYZ();
313 // The profile ID are 16 raw bytes
315 CopyMemory(Icc
->ProfileID
, Header
.reserved
, 16);
317 // Get rid of possible wrong profiles
319 NormalizeXYZ(&Icc
-> Illuminant
);
321 // Read tag directory
323 if (TagCount
> MAX_TABLE_TAG
) {
325 cmsSignalError(LCMS_ERRC_ABORTED
, "Too many tags (%d)", TagCount
);
329 Icc
-> TagCount
= TagCount
;
330 for (i
=0; i
< TagCount
; i
++) {
332 if (Icc
->Read(&Tag
, sizeof(icTag
), 1, Icc
) != 1)
335 AdjustEndianess32((LPBYTE
) &Tag
.offset
);
336 AdjustEndianess32((LPBYTE
) &Tag
.size
);
337 AdjustEndianess32((LPBYTE
) &Tag
.sig
); // Signature
339 // Perform some sanity check. Offset + size should fall inside file.
341 if (Tag
.offset
+ Tag
.size
> Header
.size
) goto ErrorCleanup
;
343 Icc
-> TagNames
[i
] = Tag
.sig
;
344 Icc
-> TagOffsets
[i
] = Tag
.offset
;
345 Icc
-> TagSizes
[i
] = Tag
.size
;
356 cmsSignalError(LCMS_ERRC_ABORTED
, "Corrupted memory profile");
358 cmsSignalError(LCMS_ERRC_ABORTED
, "Corrupted profile: '%s'", Icc
->PhysicalFile
);
369 unsigned int uipow(unsigned int a
, unsigned int b
) {
378 // Convert between notations.
380 #define TO16_TAB(x) (WORD) (((x) << 8) | (x))
383 // LUT8 can come only in Lab space. There is a fatal flaw in
384 // converting from Lut8 to Lut16. Due to particular encoding
385 // of Lab, different actions should be taken from input and
386 // output Lab8 LUTS. For input, is as easy as applying a << 8,
387 // since numbers comes in fixed point. However, for output LUT
388 // things goes a bit more complex.... LUT 16 is supposed to
389 // have a domain of 0..ff00, so we should remap the LUT in order
390 // to get things working. Affected signatures are B2Axx tags,
391 // preview and gamut.
393 // I do solve it by multiplying input matrix by:
395 // | 0xffff/0xff00 0 0 |
396 // | 0 0xffff/0xff00 0 |
397 // | 0 0 0xffff/0xff00 |
399 // The input values got then remapped to adequate domain
402 void FixLUT8(LPLUT Lut
, icTagSignature sig
, size_t nTabSize
)
404 MAT3 Fixup
, Original
, Result
;
415 case icSigPreview0Tag
:
416 case icSigPreview1Tag
:
417 case icSigPreview2Tag
:
420 VEC3init(&Fixup
.v
[0], (double) 0xFFFF/0xFF00, 0, 0);
421 VEC3init(&Fixup
.v
[1], 0, (double) 0xFFFF/0xFF00, 0);
422 VEC3init(&Fixup
.v
[2], 0, 0, (double) 0xFFFF/0xFF00);
425 MAT3fromFix(&Original
, &Lut
->Matrix
);
426 MAT3per(&Result
, &Original
, &Fixup
);
427 MAT3toFix(&Lut
->Matrix
, &Result
);
429 Lut
-> wFlags
|= LUT_HASMATRIX
;
432 // For input, clear low part since this has to be
433 // Lab in fixed point
438 for (i
= 0; i
< nTabSize
; i
++) {
446 // On Lab -> Lab abstract or Lab identities, fix both sides of LUT
449 void FixLUT8bothSides(LPLUT Lut
, size_t nTabSize
)
451 MAT3 Fixup
, Original
, Result
;
455 VEC3init(&Fixup
.v
[0], (double) 0xFFFF/0xFF00, 0, 0);
456 VEC3init(&Fixup
.v
[1], 0, (double) 0xFFFF/0xFF00, 0);
457 VEC3init(&Fixup
.v
[2], 0, 0, (double) 0xFFFF/0xFF00);
459 MAT3fromFix(&Original
, &Lut
->Matrix
);
460 MAT3per(&Result
, &Original
, &Fixup
);
461 MAT3toFix(&Lut
->Matrix
, &Result
);
463 Lut
-> wFlags
|= LUT_HASMATRIX
;
466 for (i
= 0; i
< nTabSize
; i
++) {
474 // The infamous LUT 8
477 LCMSBOOL
ReadLUT8(LPLCMSICCPROFILE Icc
, LPLUT NewLUT
, icTagSignature sig
)
483 unsigned int AllLinear
;
486 if (Icc
->Read(&LUT8
, sizeof(icLut8
) - SIZEOF_UINT8_ALIGNED
, 1, Icc
) != 1) return FALSE
;
488 NewLUT
-> wFlags
= LUT_HASTL1
|LUT_HASTL2
|LUT_HAS3DGRID
;
489 NewLUT
-> cLutPoints
= LUT8
.clutPoints
;
490 NewLUT
-> InputChan
= LUT8
.inputChan
;
491 NewLUT
-> OutputChan
= LUT8
.outputChan
;
492 NewLUT
-> InputEntries
= 256;
493 NewLUT
-> OutputEntries
= 256;
496 if (NewLUT
-> cLutPoints
> 100) NewLUT
->cLutPoints
= 100;
497 if (NewLUT
-> InputChan
> MAXCHANNELS
) NewLUT
-> InputChan
= MAXCHANNELS
;
498 if (NewLUT
-> OutputChan
> MAXCHANNELS
) NewLUT
-> OutputChan
= MAXCHANNELS
;
500 AdjustEndianess32((LPBYTE
) &LUT8
.e00
);
501 AdjustEndianess32((LPBYTE
) &LUT8
.e01
);
502 AdjustEndianess32((LPBYTE
) &LUT8
.e02
);
503 AdjustEndianess32((LPBYTE
) &LUT8
.e10
);
504 AdjustEndianess32((LPBYTE
) &LUT8
.e11
);
505 AdjustEndianess32((LPBYTE
) &LUT8
.e12
);
506 AdjustEndianess32((LPBYTE
) &LUT8
.e20
);
507 AdjustEndianess32((LPBYTE
) &LUT8
.e21
);
508 AdjustEndianess32((LPBYTE
) &LUT8
.e22
);
513 NewLUT
-> Matrix
.v
[0].n
[0] = (Fixed32
) LUT8
.e00
;
514 NewLUT
-> Matrix
.v
[0].n
[1] = (Fixed32
) LUT8
.e01
;
515 NewLUT
-> Matrix
.v
[0].n
[2] = (Fixed32
) LUT8
.e02
;
516 NewLUT
-> Matrix
.v
[1].n
[0] = (Fixed32
) LUT8
.e10
;
517 NewLUT
-> Matrix
.v
[1].n
[1] = (Fixed32
) LUT8
.e11
;
518 NewLUT
-> Matrix
.v
[1].n
[2] = (Fixed32
) LUT8
.e12
;
519 NewLUT
-> Matrix
.v
[2].n
[0] = (Fixed32
) LUT8
.e20
;
520 NewLUT
-> Matrix
.v
[2].n
[1] = (Fixed32
) LUT8
.e21
;
521 NewLUT
-> Matrix
.v
[2].n
[2] = (Fixed32
) LUT8
.e22
;
524 // Only operates if not identity...
526 if ((NewLUT
-> InputChan
== 3) && !MAT3isIdentity(&NewLUT
-> Matrix
, 0.0001)) {
528 NewLUT
-> wFlags
|= LUT_HASMATRIX
;
534 Temp
= (LPBYTE
) _cmsMalloc(256);
535 if (Temp
== NULL
) return FALSE
;
538 for (i
=0; i
< NewLUT
-> InputChan
; i
++) {
540 PtrW
= (LPWORD
) _cmsMalloc(sizeof(WORD
) * 256);
546 NewLUT
-> L1
[i
] = PtrW
;
547 if (Icc
->Read(Temp
, 1, 256, Icc
) != 256) {
552 for (j
=0; j
< 256; j
++)
553 PtrW
[j
] = TO16_TAB(Temp
[j
]);
554 AllLinear
+= cmsIsLinear(NewLUT
-> L1
[i
], NewLUT
-> InputEntries
);
557 // Linear input, so ignore full step
559 if (AllLinear
== NewLUT
-> InputChan
) {
561 NewLUT
-> wFlags
&= ~LUT_HASTL1
;
568 nTabSize
= (NewLUT
-> OutputChan
* uipow(NewLUT
->cLutPoints
,
573 PtrW
= (LPWORD
) _cmsMalloc(sizeof(WORD
) * nTabSize
);
574 if (PtrW
== NULL
) return FALSE
;
576 Temp
= (LPBYTE
) _cmsMalloc(nTabSize
);
582 if (Icc
->Read(Temp
, 1, nTabSize
, Icc
) != nTabSize
) {
589 NewLUT
-> Tsize
= (unsigned int) (nTabSize
* sizeof(WORD
));
591 for (i
= 0; i
< nTabSize
; i
++) {
593 *PtrW
++ = TO16_TAB(Temp
[i
]);
600 NewLUT
->wFlags
&= ~LUT_HAS3DGRID
;
604 // Copy output tables
606 Temp
= (LPBYTE
) _cmsMalloc(256);
612 for (i
=0; i
< NewLUT
-> OutputChan
; i
++) {
614 PtrW
= (LPWORD
) _cmsMalloc(sizeof(WORD
) * 256);
620 NewLUT
-> L2
[i
] = PtrW
;
621 if (Icc
->Read(Temp
, 1, 256, Icc
) != 256) {
626 for (j
=0; j
< 256; j
++)
627 PtrW
[j
] = TO16_TAB(Temp
[j
]);
628 AllLinear
+= cmsIsLinear(NewLUT
-> L2
[i
], 256);
631 // Linear input, so ignore full step
633 if (AllLinear
== NewLUT
-> OutputChan
) {
635 NewLUT
-> wFlags
&= ~LUT_HASTL2
;
641 cmsCalcL16Params(NewLUT
-> InputEntries
, &NewLUT
-> In16params
);
642 cmsCalcL16Params(NewLUT
-> OutputEntries
, &NewLUT
-> Out16params
);
643 cmsCalcCLUT16Params(NewLUT
-> cLutPoints
, NewLUT
-> InputChan
,
644 NewLUT
-> OutputChan
,
645 &NewLUT
-> CLut16params
);
648 if (Icc
->PCS
== icSigLabData
) {
650 // Abstract or Lab identity
652 if (Icc
-> ColorSpace
== icSigLabData
)
654 FixLUT8bothSides(NewLUT
, nTabSize
);
656 FixLUT8(NewLUT
, sig
, nTabSize
);
659 // Now some additional fixup. Lab encoding on 8 bit makes
660 // impossible to place gray axis on a exact node. However,
661 // some profiles does claim to do that. Poor lcms will try
662 // to detect such condition and fix up "on the fly".
665 LPWORD WhiteLab
, ExpectedWhite
;
666 WORD WhiteFixed
[MAXCHANNELS
], WhiteUnfixed
[MAXCHANNELS
];
668 double Dist
, DistFixed
, DistUnfixed
;
670 _cmsEndPointsBySpace(icSigLabData
, &WhiteLab
, NULL
, NULL
);
672 if (_cmsEndPointsBySpace(Icc
-> ColorSpace
,
673 &ExpectedWhite
, NULL
, &nChannels
)) {
675 // 1.- Find white obtained by both combinations
677 NewLUT
-> FixGrayAxes
= FALSE
;
678 cmsEvalLUT(NewLUT
, WhiteLab
, WhiteUnfixed
);
680 NewLUT
-> FixGrayAxes
= TRUE
;
681 cmsEvalLUT(NewLUT
, WhiteLab
, WhiteFixed
);
683 // 2.- Which method gives closer white?
685 DistFixed
= DistUnfixed
= 0;
686 for (j
=0; j
< nChannels
; j
++) {
688 Dist
= ExpectedWhite
[j
] - WhiteFixed
[j
];
689 DistFixed
+= Dist
*Dist
;
690 Dist
= ExpectedWhite
[j
] - WhiteUnfixed
[j
];
691 DistUnfixed
+= Dist
*Dist
;
696 if (sqrt(DistFixed
) < sqrt(DistUnfixed
))
697 NewLUT
-> FixGrayAxes
= TRUE
;
699 NewLUT
-> FixGrayAxes
= FALSE
;
715 LCMSBOOL
ReadLUT16(LPLCMSICCPROFILE Icc
, LPLUT NewLUT
)
720 unsigned int AllLinear
;
724 if (Icc
->Read(&LUT16
, sizeof(icLut16
)- SIZEOF_UINT16_ALIGNED
, 1, Icc
) != 1)
727 NewLUT
-> wFlags
= LUT_HASTL1
| LUT_HASTL2
| LUT_HAS3DGRID
;
728 NewLUT
-> cLutPoints
= LUT16
.clutPoints
;
729 NewLUT
-> InputChan
= LUT16
.inputChan
;
730 NewLUT
-> OutputChan
= LUT16
.outputChan
;
732 AdjustEndianess16((LPBYTE
) &LUT16
.inputEnt
);
733 AdjustEndianess16((LPBYTE
) &LUT16
.outputEnt
);
735 NewLUT
-> InputEntries
= LUT16
.inputEnt
;
736 NewLUT
-> OutputEntries
= LUT16
.outputEnt
;
741 AdjustEndianess32((LPBYTE
) &LUT16
.e00
);
742 AdjustEndianess32((LPBYTE
) &LUT16
.e01
);
743 AdjustEndianess32((LPBYTE
) &LUT16
.e02
);
744 AdjustEndianess32((LPBYTE
) &LUT16
.e10
);
745 AdjustEndianess32((LPBYTE
) &LUT16
.e11
);
746 AdjustEndianess32((LPBYTE
) &LUT16
.e12
);
747 AdjustEndianess32((LPBYTE
) &LUT16
.e20
);
748 AdjustEndianess32((LPBYTE
) &LUT16
.e21
);
749 AdjustEndianess32((LPBYTE
) &LUT16
.e22
);
751 NewLUT
-> Matrix
.v
[0].n
[0] = (Fixed32
) LUT16
.e00
;
752 NewLUT
-> Matrix
.v
[0].n
[1] = (Fixed32
) LUT16
.e01
;
753 NewLUT
-> Matrix
.v
[0].n
[2] = (Fixed32
) LUT16
.e02
;
754 NewLUT
-> Matrix
.v
[1].n
[0] = (Fixed32
) LUT16
.e10
;
755 NewLUT
-> Matrix
.v
[1].n
[1] = (Fixed32
) LUT16
.e11
;
756 NewLUT
-> Matrix
.v
[1].n
[2] = (Fixed32
) LUT16
.e12
;
757 NewLUT
-> Matrix
.v
[2].n
[0] = (Fixed32
) LUT16
.e20
;
758 NewLUT
-> Matrix
.v
[2].n
[1] = (Fixed32
) LUT16
.e21
;
759 NewLUT
-> Matrix
.v
[2].n
[2] = (Fixed32
) LUT16
.e22
;
761 // Only operates if not identity...
763 if ((NewLUT
-> InputChan
== 3) && !MAT3isIdentity(&NewLUT
-> Matrix
, 0.0001)) {
765 NewLUT
-> wFlags
|= LUT_HASMATRIX
;
772 for (i
=0; i
< NewLUT
-> InputChan
; i
++) {
774 PtrW
= (LPWORD
) _cmsMalloc(sizeof(WORD
) * NewLUT
-> InputEntries
);
775 if (PtrW
== NULL
) return FALSE
;
777 NewLUT
-> L1
[i
] = PtrW
;
778 if (Icc
->Read(PtrW
, sizeof(WORD
), NewLUT
-> InputEntries
, Icc
) != NewLUT
-> InputEntries
) {
782 AdjustEndianessArray16(PtrW
, NewLUT
-> InputEntries
);
783 AllLinear
+= cmsIsLinear(NewLUT
-> L1
[i
], NewLUT
-> InputEntries
);
786 // Linear input, so ignore full step
788 if (AllLinear
== NewLUT
-> InputChan
) {
790 NewLUT
-> wFlags
&= ~LUT_HASTL1
;
796 nTabSize
= (NewLUT
-> OutputChan
* uipow(NewLUT
->cLutPoints
,
800 PtrW
= (LPWORD
) _cmsMalloc(sizeof(WORD
) * nTabSize
);
807 NewLUT
-> Tsize
= (unsigned int) (nTabSize
* sizeof(WORD
));
809 if (Icc
-> Read(PtrW
, sizeof(WORD
), nTabSize
, Icc
) != nTabSize
) {
813 AdjustEndianessArray16(NewLUT
-> T
, nTabSize
);
818 NewLUT
-> wFlags
&= ~LUT_HAS3DGRID
;
821 // Copy output tables
824 for (i
=0; i
< NewLUT
-> OutputChan
; i
++) {
826 PtrW
= (LPWORD
) _cmsMalloc(sizeof(WORD
) * NewLUT
-> OutputEntries
);
831 NewLUT
-> L2
[i
] = PtrW
;
832 if (Icc
->Read(PtrW
, sizeof(WORD
), NewLUT
-> OutputEntries
, Icc
) != NewLUT
-> OutputEntries
) {
836 AdjustEndianessArray16(PtrW
, NewLUT
-> OutputEntries
);
837 AllLinear
+= cmsIsLinear(NewLUT
-> L2
[i
], NewLUT
-> OutputEntries
);
840 // Linear output, ignore step
842 if (AllLinear
== NewLUT
-> OutputChan
)
844 NewLUT
-> wFlags
&= ~LUT_HASTL2
;
848 cmsCalcL16Params(NewLUT
-> InputEntries
, &NewLUT
-> In16params
);
849 cmsCalcL16Params(NewLUT
-> OutputEntries
, &NewLUT
-> Out16params
);
850 cmsCalcCLUT16Params(NewLUT
-> cLutPoints
, NewLUT
-> InputChan
,
851 NewLUT
-> OutputChan
,
852 &NewLUT
-> CLut16params
);
858 // This is a shared routine for reading curves. It can handle v2 curves
859 // as linear, single gamma and table-based as well as v4 parametric curves.
862 LPGAMMATABLE
ReadCurve(LPLCMSICCPROFILE Icc
)
864 icUInt32Number Count
;
865 LPGAMMATABLE NewGamma
;
866 icTagTypeSignature BaseType
;
870 BaseType
= ReadBase(Icc
);
874 case ((icTagTypeSignature
) 0x9478ee00): // Monaco 2 profiler is BROKEN!
877 if (Icc
->Read(&Count
, sizeof(icUInt32Number
), 1, Icc
) != 1) return NULL
;
878 AdjustEndianess32((LPBYTE
) &Count
);
884 NewGamma
= cmsAllocGamma(2);
885 if (!NewGamma
) return NULL
;
886 NewGamma
-> GammaTable
[0] = 0;
887 NewGamma
-> GammaTable
[1] = 0xFFFF;
890 case 1: // Specified as the exponent of gamma function
892 WORD SingleGammaFixed
;
894 if (Icc
->Read(&SingleGammaFixed
, sizeof(WORD
), 1, Icc
) != 1) return NULL
;
895 AdjustEndianess16((LPBYTE
) &SingleGammaFixed
);
896 return cmsBuildGamma(4096, Convert8Fixed8(SingleGammaFixed
));
901 NewGamma
= cmsAllocGamma(Count
);
902 if (!NewGamma
) return NULL
;
904 if (Icc
->Read(NewGamma
-> GammaTable
, sizeof(WORD
), Count
, Icc
) != Count
)
906 AdjustEndianessArray16(NewGamma
-> GammaTable
, Count
);
914 case icSigParametricCurveType
: {
916 int ParamsByType
[] = { 1, 3, 4, 5, 7 };
918 icS15Fixed16Number Num
;
919 icUInt32Number Reserved
;
923 if (Icc
-> Read(&Type
, sizeof(icUInt16Number
), 1, Icc
) != 1) return NULL
;
924 if (Icc
-> Read(&Reserved
, sizeof(icUInt16Number
), 1, Icc
) != 1) return NULL
;
926 AdjustEndianess16((LPBYTE
) &Type
);
929 cmsSignalError(LCMS_ERRC_ABORTED
, "Unknown parametric curve type '%d' found.", Type
);
933 ZeroMemory(Params
, 10* sizeof(double));
934 n
= ParamsByType
[Type
];
936 for (i
=0; i
< n
; i
++) {
938 if (Icc
-> Read(&Num
, sizeof(icS15Fixed16Number
), 1, Icc
) != 1) return NULL
;
939 Params
[i
] = Convert15Fixed16(Num
);
943 NewGamma
= cmsBuildParametricGamma(4096, Type
+1, Params
);
949 cmsSignalError(LCMS_ERRC_ABORTED
, "Bad tag signature '%lx' found.", BaseType
);
956 // Similar to anterior, but curve is reversed
959 LPGAMMATABLE
ReadCurveReversed(LPLCMSICCPROFILE Icc
)
962 icTagTypeSignature BaseType
;
963 LPGAMMATABLE NewGamma
, ReturnGamma
;
964 icUInt32Number Count
;
968 BaseType
= ReadBase(Icc
);
973 case 0x9478ee00L
: // Monaco 2 profiler is BROKEN!
976 if (Icc
-> Read(&Count
, sizeof(icUInt32Number
), 1, Icc
) != 1) return NULL
;
977 AdjustEndianess32((LPBYTE
) &Count
);
982 case 0: // Linear, reverse is same.
984 NewGamma
= cmsAllocGamma(2);
985 if (!NewGamma
) return NULL
;
987 NewGamma
-> GammaTable
[0] = 0;
988 NewGamma
-> GammaTable
[1] = 0xFFFF;
992 WORD SingleGammaFixed
;
994 if (Icc
-> Read(&SingleGammaFixed
, sizeof(WORD
), 1, Icc
) != 1) return NULL
;
995 AdjustEndianess16((LPBYTE
) &SingleGammaFixed
);
996 return cmsBuildGamma(4096, 1./Convert8Fixed8(SingleGammaFixed
));
999 default: { // Curve. Do our best to trying to reverse the curve
1001 NewGamma
= cmsAllocGamma(Count
);
1002 if (!NewGamma
) return NULL
;
1004 if (Icc
-> Read(NewGamma
-> GammaTable
, sizeof(WORD
), Count
, Icc
) != Count
)
1007 AdjustEndianessArray16(NewGamma
-> GammaTable
, Count
);
1010 Count
= 256; // Reverse of simple curve has not necesarely to be simple
1012 ReturnGamma
= cmsReverseGamma(Count
, NewGamma
);
1013 cmsFreeGamma(NewGamma
);
1021 // Parametric curves
1022 case icSigParametricCurveType
: {
1024 int ParamsByType
[] = { 1, 3, 4, 5, 7 };
1026 icS15Fixed16Number Num
;
1027 icUInt32Number Reserved
;
1028 icUInt16Number Type
;
1032 if (Icc
-> Read(&Type
, sizeof(icUInt16Number
), 1, Icc
) != 1) return NULL
;
1033 if (Icc
-> Read(&Reserved
, sizeof(icUInt16Number
), 1, Icc
) != 1) return NULL
;
1035 AdjustEndianess16((LPBYTE
) &Type
);
1038 cmsSignalError(LCMS_ERRC_ABORTED
, "Unknown parametric curve type '%d' found.", Type
);
1042 ZeroMemory(Params
, 10* sizeof(double));
1043 n
= ParamsByType
[Type
];
1045 for (i
=0; i
< n
; i
++) {
1046 if (Icc
-> Read(&Num
, sizeof(icS15Fixed16Number
), 1, Icc
) != 1) return NULL
;
1047 Params
[i
] = Convert15Fixed16(Num
);
1051 // Negative type as a mark of reversed curve
1052 NewGamma
= cmsBuildParametricGamma(4096, -(Type
+1), Params
);
1058 cmsSignalError(LCMS_ERRC_ABORTED
, "Bad tag signature '%lx' found.", BaseType
);
1065 // V4 stuff. Read matrix for LutAtoB and LutBtoA
1068 LCMSBOOL
ReadMatrixOffset(LPLCMSICCPROFILE Icc
, size_t Offset
, LPLUT NewLUT
, DWORD dwFlags
)
1071 icS15Fixed16Number All
[12];
1076 if (Icc
-> Seek(Icc
, Offset
)) return FALSE
;
1078 if (Icc
->Read(All
, sizeof(icS15Fixed16Number
), 12, Icc
) != 12)
1081 for (i
=0; i
< 12; i
++)
1082 AdjustEndianess32((LPBYTE
) &All
[i
]);
1085 m
.v
[0].n
[0] = FIXED_TO_DOUBLE((Fixed32
) All
[0]);
1086 m
.v
[0].n
[1] = FIXED_TO_DOUBLE((Fixed32
) All
[1]);
1087 m
.v
[0].n
[2] = FIXED_TO_DOUBLE((Fixed32
) All
[2]);
1088 m
.v
[1].n
[0] = FIXED_TO_DOUBLE((Fixed32
) All
[3]);
1089 m
.v
[1].n
[1] = FIXED_TO_DOUBLE((Fixed32
) All
[4]);
1090 m
.v
[1].n
[2] = FIXED_TO_DOUBLE((Fixed32
) All
[5]);
1091 m
.v
[2].n
[0] = FIXED_TO_DOUBLE((Fixed32
) All
[6]);
1092 m
.v
[2].n
[1] = FIXED_TO_DOUBLE((Fixed32
) All
[7]);
1093 m
.v
[2].n
[2] = FIXED_TO_DOUBLE((Fixed32
) All
[8]);
1095 o
.n
[0] = FIXED_TO_DOUBLE((Fixed32
) All
[9]);
1096 o
.n
[1] = FIXED_TO_DOUBLE((Fixed32
) All
[10]);
1097 o
.n
[2] = FIXED_TO_DOUBLE((Fixed32
) All
[11]);
1099 cmsSetMatrixLUT4(NewLUT
, &m
, &o
, dwFlags
);
1105 // V4 stuff. Read CLUT part for LutAtoB and LutBtoA
1108 LCMSBOOL
ReadCLUT(LPLCMSICCPROFILE Icc
, size_t Offset
, LPLUT NewLUT
)
1113 if (Icc
-> Seek(Icc
, Offset
)) return FALSE
;
1114 if (Icc
->Read(&CLUT
, sizeof(icCLutStruct
), 1, Icc
) != 1) return FALSE
;
1117 cmsAlloc3DGrid(NewLUT
, CLUT
.gridPoints
[0], NewLUT
->InputChan
,
1118 NewLUT
->OutputChan
);
1120 // Precission can be 1 or 2 bytes
1122 if (CLUT
.prec
== 1) {
1127 for (i
=0; i
< NewLUT
->Tsize
/ sizeof(WORD
); i
++) {
1128 if (Icc
->Read(&v
, sizeof(BYTE
), 1, Icc
) != 1) return FALSE
;
1129 NewLUT
->T
[i
] = TO16_TAB(v
);
1134 if (CLUT
.prec
== 2) {
1136 size_t n
= NewLUT
->Tsize
/ sizeof(WORD
);
1138 if (Icc
->Read(NewLUT
->T
, sizeof(WORD
), n
, Icc
) != n
) return FALSE
;
1139 AdjustEndianessArray16(NewLUT
->T
, NewLUT
->Tsize
/ sizeof(WORD
));
1142 cmsSignalError(LCMS_ERRC_ABORTED
, "Unknow precission of '%d'", CLUT
.prec
);
1151 void SkipAlignment(LPLCMSICCPROFILE Icc
)
1154 size_t At
= Icc
->Tell(Icc
);
1155 int BytesToNextAlignedPos
= (int) (At
% 4);
1157 Icc
->Read(Buffer
, 1, BytesToNextAlignedPos
, Icc
);
1160 // Read a set of curves from specific offset
1162 LCMSBOOL
ReadSetOfCurves(LPLCMSICCPROFILE Icc
, size_t Offset
, LPLUT NewLUT
, int nLocation
)
1164 LPGAMMATABLE Curves
[MAXCHANNELS
];
1165 unsigned int i
, nCurves
;
1167 if (Icc
-> Seek(Icc
, Offset
)) return FALSE
;
1169 if (nLocation
== 1 || nLocation
== 3)
1171 nCurves
= NewLUT
->InputChan
;
1173 nCurves
= NewLUT
->OutputChan
;
1175 for (i
=0; i
< nCurves
; i
++) {
1177 Curves
[i
] = ReadCurve(Icc
);
1178 if (Curves
[i
] == NULL
) return FALSE
;
1183 NewLUT
= cmsAllocLinearTable(NewLUT
, Curves
, nLocation
);
1185 for (i
=0; i
< nCurves
; i
++)
1186 cmsFreeGamma(Curves
[i
]);
1192 // V4 stuff. LutAtoB type
1194 // [L1] -> [CLUT] -> [L4] -> [Mat4] -> [Ofs4] -> [L2]
1196 // Mat, Mat3, Ofs3, L3 are missing
1202 LCMSBOOL
ReadLUT_A2B(LPLCMSICCPROFILE Icc
, LPLUT NewLUT
, size_t BaseOffset
, icTagSignature sig
)
1206 if (Icc
->Read(&LUT16
, sizeof(icLutAtoB
), 1, Icc
) != 1) return FALSE
;
1208 NewLUT
-> InputChan
= LUT16
.inputChan
;
1209 NewLUT
-> OutputChan
= LUT16
.outputChan
;
1211 AdjustEndianess32((LPBYTE
) &LUT16
.offsetB
);
1212 AdjustEndianess32((LPBYTE
) &LUT16
.offsetMat
);
1213 AdjustEndianess32((LPBYTE
) &LUT16
.offsetM
);
1214 AdjustEndianess32((LPBYTE
) &LUT16
.offsetC
);
1215 AdjustEndianess32((LPBYTE
) &LUT16
.offsetA
);
1217 if (LUT16
.offsetB
!= 0)
1218 ReadSetOfCurves(Icc
, BaseOffset
+ LUT16
.offsetB
, NewLUT
, 2);
1220 if (LUT16
.offsetMat
!= 0)
1221 ReadMatrixOffset(Icc
, BaseOffset
+ LUT16
.offsetMat
, NewLUT
, LUT_HASMATRIX4
);
1224 if (LUT16
.offsetM
!= 0)
1225 ReadSetOfCurves(Icc
, BaseOffset
+ LUT16
.offsetM
, NewLUT
, 4);
1227 if (LUT16
.offsetC
!= 0)
1228 ReadCLUT(Icc
, BaseOffset
+ LUT16
.offsetC
, NewLUT
);
1230 if (LUT16
.offsetA
!= 0)
1231 ReadSetOfCurves(Icc
, BaseOffset
+ LUT16
.offsetA
, NewLUT
, 1);
1233 // Convert to v2 PCS
1235 if (Icc
->PCS
== icSigLabData
) {
1243 case icSigPreview0Tag
:
1244 case icSigPreview1Tag
:
1245 case icSigPreview2Tag
:
1247 NewLUT
->wFlags
|= LUT_V4_INPUT_EMULATE_V2
;
1258 // V4 stuff. LutBtoA type
1261 LCMSBOOL
ReadLUT_B2A(LPLCMSICCPROFILE Icc
, LPLUT NewLUT
, size_t BaseOffset
, icTagSignature sig
)
1265 if (Icc
->Read(&LUT16
, sizeof(icLutBtoA
), 1, Icc
) != 1) return FALSE
;
1267 NewLUT
-> InputChan
= LUT16
.inputChan
;
1268 NewLUT
-> OutputChan
= LUT16
.outputChan
;
1270 AdjustEndianess32((LPBYTE
) &LUT16
.offsetB
);
1271 AdjustEndianess32((LPBYTE
) &LUT16
.offsetMat
);
1272 AdjustEndianess32((LPBYTE
) &LUT16
.offsetM
);
1273 AdjustEndianess32((LPBYTE
) &LUT16
.offsetC
);
1274 AdjustEndianess32((LPBYTE
) &LUT16
.offsetA
);
1277 if (LUT16
.offsetB
!= 0)
1278 ReadSetOfCurves(Icc
, BaseOffset
+ LUT16
.offsetB
, NewLUT
, 1);
1280 if (LUT16
.offsetMat
!= 0)
1281 ReadMatrixOffset(Icc
, BaseOffset
+ LUT16
.offsetMat
, NewLUT
, LUT_HASMATRIX3
);
1283 if (LUT16
.offsetM
!= 0)
1284 ReadSetOfCurves(Icc
, BaseOffset
+ LUT16
.offsetM
, NewLUT
, 3);
1286 if (LUT16
.offsetC
!= 0)
1287 ReadCLUT(Icc
, BaseOffset
+ LUT16
.offsetC
, NewLUT
);
1289 if (LUT16
.offsetA
!= 0)
1290 ReadSetOfCurves(Icc
, BaseOffset
+ LUT16
.offsetA
, NewLUT
, 2);
1293 // Convert to v2 PCS
1295 if (Icc
->PCS
== icSigLabData
) {
1303 case icSigPreview0Tag
:
1304 case icSigPreview1Tag
:
1305 case icSigPreview2Tag
:
1307 NewLUT
->wFlags
|= LUT_V4_OUTPUT_EMULATE_V2
;
1319 LPLUT LCMSEXPORT
cmsReadICCLut(cmsHPROFILE hProfile
, icTagSignature sig
)
1322 LPLCMSICCPROFILE Icc
= (LPLCMSICCPROFILE
) (LPSTR
) hProfile
;
1323 icTagTypeSignature BaseType
;
1328 n
= _cmsSearchTag(Icc
, sig
, TRUE
);
1333 // If is in memory, the LUT is already there, so throw a copy
1334 if (Icc
-> TagPtrs
[n
]) {
1336 return cmsDupLUT((LPLUT
) Icc
->TagPtrs
[n
]);
1339 offset
= Icc
-> TagOffsets
[n
];
1341 if (Icc
-> Seek(Icc
, offset
))
1344 BaseType
= ReadBase(Icc
);
1347 NewLUT
= cmsAllocLUT();
1350 cmsSignalError(LCMS_ERRC_ABORTED
, "cmsAllocLUT() failed");
1357 case icSigLut8Type
: if (!ReadLUT8(Icc
, NewLUT
, sig
)) {
1363 case icSigLut16Type
: if (!ReadLUT16(Icc
, NewLUT
)) {
1369 case icSiglutAtoBType
: if (!ReadLUT_A2B(Icc
, NewLUT
, offset
, sig
)) {
1375 case icSiglutBtoAType
: if (!ReadLUT_B2A(Icc
, NewLUT
, offset
, sig
)) {
1381 default: cmsSignalError(LCMS_ERRC_ABORTED
, "Bad tag signature %lx found.", BaseType
);
1391 // Sets the language & country preferences. Used only in ICC 4.0 profiles
1393 void LCMSEXPORT
cmsSetLanguage(const char LanguageCode
[4], const char CountryCode
[4])
1396 int LanguageCodeInt
= *(int *) LanguageCode
;
1397 int CountryCodeInt
= *(int *) CountryCode
;
1399 AdjustEndianess32((LPBYTE
) &LanguageCodeInt
);
1400 AdjustEndianess32((LPBYTE
) &CountryCodeInt
);
1402 GlobalLanguageCode
= LanguageCodeInt
;
1403 GlobalCountryCode
= CountryCodeInt
;
1408 // Some tags (e.g, 'pseq') can have text tags embedded. This function
1409 // handles such special case. Returns -1 on error, or the number of bytes left on success.
1412 int ReadEmbeddedTextTag(LPLCMSICCPROFILE Icc
, size_t size
, char* Name
, size_t size_max
)
1414 icTagTypeSignature BaseType
;
1417 BaseType
= ReadBase(Icc
);
1418 size
-= sizeof(icTagBase
);
1422 case icSigTextDescriptionType
: {
1424 icUInt32Number AsciiCount
;
1425 icUInt32Number i
, UnicodeCode
, UnicodeCount
;
1426 icUInt16Number ScriptCodeCode
, Dummy
;
1427 icUInt8Number ScriptCodeCount
;
1429 if (Icc
->Read(&AsciiCount
, sizeof(icUInt32Number
), 1, Icc
) != 1) return -1;
1431 if (size
< sizeof(icUInt32Number
)) return (int) size
;
1432 size
-= sizeof(icUInt32Number
);
1434 AdjustEndianess32((LPBYTE
) &AsciiCount
);
1436 (AsciiCount
>= size_max
) ? (size_max
-1) : AsciiCount
, Icc
);
1438 if (size
< AsciiCount
) return (int) size
;
1441 // Skip Unicode code
1443 if (Icc
->Read(&UnicodeCode
, sizeof(icUInt32Number
), 1, Icc
) != 1) return -1;
1444 if (size
< sizeof(icUInt32Number
)) return (int) size
;
1445 size
-= sizeof(icUInt32Number
);
1447 if (Icc
->Read(&UnicodeCount
, sizeof(icUInt32Number
), 1, Icc
) != 1) return -1;
1448 if (size
< sizeof(icUInt32Number
)) return (int) size
;
1449 size
-= sizeof(icUInt32Number
);
1451 AdjustEndianess32((LPBYTE
) &UnicodeCount
);
1453 if (UnicodeCount
> size
) return (int) size
;
1455 for (i
=0; i
< UnicodeCount
; i
++) {
1456 size_t nread
= Icc
->Read(&Dummy
, sizeof(icUInt16Number
), 1, Icc
);
1457 if (nread
!= 1) return (int) size
;
1458 size
-= sizeof(icUInt16Number
);
1461 // Skip ScriptCode code
1463 if (Icc
->Read(&ScriptCodeCode
, sizeof(icUInt16Number
), 1, Icc
) != 1) return -1;
1464 size
-= sizeof(icUInt16Number
);
1465 if (Icc
->Read(&ScriptCodeCount
, sizeof(icUInt8Number
), 1, Icc
) != 1) return -1;
1466 size
-= sizeof(icUInt8Number
);
1468 // Should remain 67 bytes as filler
1470 if (size
< 67) return (int) size
;
1472 for (i
=0; i
< 67; i
++) {
1473 size_t nread
= Icc
->Read(&Dummy
, sizeof(icUInt8Number
), 1, Icc
);
1474 if (nread
!= 1) return (int) size
;
1481 case icSigCopyrightTag
: // Broken profiles from agfa does store copyright info in such type
1485 size_t i
, Missing
= 0;
1487 if (size
>= size_max
) {
1489 Missing
= size
- size_max
+ 1;
1490 size
= size_max
- 1;
1493 if (Icc
-> Read(Name
, 1, size
, Icc
) != size
) return -1;
1495 for (i
=0; i
< Missing
; i
++)
1496 Icc
-> Read(&Dummy
, 1, 1, Icc
);
1500 // MultiLocalizedUnicodeType, V4 only
1502 case icSigMultiLocalizedUnicodeType
: {
1504 icUInt32Number Count
, RecLen
;
1505 icUInt16Number Language
, Country
;
1506 icUInt32Number ThisLen
, ThisOffset
;
1510 wchar_t* wchar
= L
"";
1513 if (Icc
->Read(&Count
, sizeof(icUInt32Number
), 1, Icc
) != 1) return -1;
1514 AdjustEndianess32((LPBYTE
) &Count
);
1515 if (Icc
->Read(&RecLen
, sizeof(icUInt32Number
), 1, Icc
) != 1) return -1;
1516 AdjustEndianess32((LPBYTE
) &RecLen
);
1520 cmsSignalError(LCMS_ERRC_ABORTED
, "multiLocalizedUnicodeType of len != 12 is not supported.");
1524 for (i
=0; i
< Count
; i
++) {
1526 if (Icc
->Read(&Language
, sizeof(icUInt16Number
), 1, Icc
) != 1) return -1;
1527 AdjustEndianess16((LPBYTE
) &Language
);
1528 if (Icc
->Read(&Country
, sizeof(icUInt16Number
), 1, Icc
) != 1) return -1;
1529 AdjustEndianess16((LPBYTE
) &Country
);
1531 if (Icc
->Read(&ThisLen
, sizeof(icUInt32Number
), 1, Icc
) != 1) return -1;
1532 AdjustEndianess32((LPBYTE
) &ThisLen
);
1534 if (Icc
->Read(&ThisOffset
, sizeof(icUInt32Number
), 1, Icc
) != 1) return -1;
1535 AdjustEndianess32((LPBYTE
) &ThisOffset
);
1537 if (Language
== GlobalLanguageCode
|| Offset
== 0) {
1539 Len
= ThisLen
; Offset
= ThisOffset
;
1540 if (Country
== GlobalCountryCode
)
1549 strcpy(Name
, "(no info)");
1553 // Compute true offset
1554 Offset
-= 12 * Count
+ 8 + sizeof(icTagBase
);
1556 // Skip unused bytes
1557 for (i
=0; i
< Offset
; i
++) {
1560 Icc
->Read(&Discard
, 1, 1, Icc
);
1565 if (Len
< 0) Len
= 0;
1566 if (Len
> 20*1024) Len
= 20 * 1024;
1568 wchar
= (wchar_t*) _cmsMalloc(Len
+2);
1569 if (!wchar
) return -1;
1571 if (Icc
->Read(wchar
, 1, Len
, Icc
) != Len
) return -1;
1572 AdjustEndianessArray16((LPWORD
) wchar
, Len
/ 2);
1574 wchar
[Len
/ 2] = L
'\0';
1575 i
= wcstombs(Name
, wchar
, size_max
);
1576 if (i
== ((size_t) -1)) {
1578 Name
[0] = 0; // Error
1581 _cmsFree((void*) wchar
);
1586 cmsSignalError(LCMS_ERRC_ABORTED
, "Bad tag signature %lx found.", BaseType
);
1594 // Take an ASCII item. Takes at most LCMS_DESC_MAX
1597 int LCMSEXPORT
cmsReadICCTextEx(cmsHPROFILE hProfile
, icTagSignature sig
, char *Name
, size_t size_max
)
1599 LPLCMSICCPROFILE Icc
= (LPLCMSICCPROFILE
) (LPSTR
) hProfile
;
1600 size_t offset
, size
;
1603 n
= _cmsSearchTag(Icc
, sig
, TRUE
);
1607 if (Icc
-> TagPtrs
[n
]) {
1609 CopyMemory(Name
, Icc
-> TagPtrs
[n
], Icc
-> TagSizes
[n
]);
1610 return (int) Icc
-> TagSizes
[n
];
1613 offset
= Icc
-> TagOffsets
[n
];
1614 size
= Icc
-> TagSizes
[n
];
1616 if (Icc
-> Seek(Icc
, offset
))
1619 return ReadEmbeddedTextTag(Icc
, size
, Name
, size_max
);
1622 // Keep compatibility with older versions
1624 int LCMSEXPORT
cmsReadICCText(cmsHPROFILE hProfile
, icTagSignature sig
, char *Text
)
1626 return cmsReadICCTextEx(hProfile
, sig
, Text
, LCMS_DESC_MAX
);
1633 int ReadICCXYZ(cmsHPROFILE hProfile
, icTagSignature sig
, LPcmsCIEXYZ Value
, LCMSBOOL lIsFatal
)
1635 LPLCMSICCPROFILE Icc
= (LPLCMSICCPROFILE
) (LPSTR
) hProfile
;
1636 icTagTypeSignature BaseType
;
1641 n
= _cmsSearchTag(Icc
, sig
, FALSE
);
1645 if (Icc
-> TagPtrs
[n
]) {
1647 CopyMemory(Value
, Icc
-> TagPtrs
[n
], Icc
-> TagSizes
[n
]);
1648 return (int) Icc
-> TagSizes
[n
];
1651 offset
= Icc
-> TagOffsets
[n
];
1653 if (Icc
-> Seek(Icc
, offset
))
1657 BaseType
= ReadBase(Icc
);
1662 case 0x7c3b10cL
: // Some apple broken embedded profiles does not have correct type
1665 Icc
->Read(&XYZ
, sizeof(icXYZNumber
), 1, Icc
);
1666 Value
-> X
= Convert15Fixed16(XYZ
.X
);
1667 Value
-> Y
= Convert15Fixed16(XYZ
.Y
);
1668 Value
-> Z
= Convert15Fixed16(XYZ
.Z
);
1671 // Aug/21-2001 - Monaco 2 does have WRONG values.
1675 cmsSignalError(LCMS_ERRC_ABORTED
, "Bad tag signature %lx found.", BaseType
);
1683 // Read a icSigS15Fixed16ArrayType (currently only a 3x3 matrix)
1686 int ReadICCXYZArray(cmsHPROFILE hProfile
, icTagSignature sig
, LPMAT3 v
)
1688 LPLCMSICCPROFILE Icc
= (LPLCMSICCPROFILE
) (LPSTR
) hProfile
;
1689 icTagTypeSignature BaseType
;
1693 cmsCIEXYZ XYZdbl
[3];
1696 n
= _cmsSearchTag(Icc
, sig
, FALSE
);
1698 return -1; // Not found
1700 if (Icc
-> TagPtrs
[n
]) {
1702 CopyMemory(v
, Icc
-> TagPtrs
[n
], Icc
-> TagSizes
[n
]);
1703 return (int) Icc
-> TagSizes
[n
];
1706 offset
= Icc
-> TagOffsets
[n
];
1708 if (Icc
-> Seek(Icc
, offset
))
1711 BaseType
= ReadBase(Icc
);
1715 case icSigS15Fixed16ArrayType
:
1717 sz
= Icc
->TagSizes
[n
] / sizeof(icXYZNumber
);
1720 cmsSignalError(LCMS_ERRC_ABORTED
, "Bad array size of %d entries.", sz
);
1724 Icc
->Read(XYZ
, sizeof(icXYZNumber
), 3, Icc
);
1726 for (i
=0; i
< 3; i
++) {
1728 XYZdbl
[i
].X
= Convert15Fixed16(XYZ
[i
].X
);
1729 XYZdbl
[i
].Y
= Convert15Fixed16(XYZ
[i
].Y
);
1730 XYZdbl
[i
].Z
= Convert15Fixed16(XYZ
[i
].Z
);
1733 CopyMemory(v
, XYZdbl
, 3*sizeof(cmsCIEXYZ
));
1737 cmsSignalError(LCMS_ERRC_ABORTED
, "Bad tag signature %lx found.", BaseType
);
1742 return sizeof(MAT3
);
1747 // Primaries are to be in xyY notation
1749 LCMSBOOL LCMSEXPORT
cmsTakeColorants(LPcmsCIEXYZTRIPLE Dest
, cmsHPROFILE hProfile
)
1751 if (ReadICCXYZ(hProfile
, icSigRedColorantTag
, &Dest
-> Red
, TRUE
) < 0) return FALSE
;
1752 if (ReadICCXYZ(hProfile
, icSigGreenColorantTag
, &Dest
-> Green
, TRUE
) < 0) return FALSE
;
1753 if (ReadICCXYZ(hProfile
, icSigBlueColorantTag
, &Dest
-> Blue
, TRUE
) < 0) return FALSE
;
1759 LCMSBOOL
cmsReadICCMatrixRGB2XYZ(LPMAT3 r
, cmsHPROFILE hProfile
)
1761 cmsCIEXYZTRIPLE Primaries
;
1763 if (!cmsTakeColorants(&Primaries
, hProfile
)) return FALSE
;
1765 VEC3init(&r
-> v
[0], Primaries
.Red
.X
, Primaries
.Green
.X
, Primaries
.Blue
.X
);
1766 VEC3init(&r
-> v
[1], Primaries
.Red
.Y
, Primaries
.Green
.Y
, Primaries
.Blue
.Y
);
1767 VEC3init(&r
-> v
[2], Primaries
.Red
.Z
, Primaries
.Green
.Z
, Primaries
.Blue
.Z
);
1774 // Always return a suitable matrix
1776 LCMSBOOL
cmsReadChromaticAdaptationMatrix(LPMAT3 r
, cmsHPROFILE hProfile
)
1779 if (ReadICCXYZArray(hProfile
, icSigChromaticAdaptationTag
, r
) < 0) {
1781 LPLCMSICCPROFILE Icc
= (LPLCMSICCPROFILE
) (LPSTR
) hProfile
;
1783 // For display profiles, revert to bradford. Else take identity.
1787 // Emissive devices have non-identity chad
1789 if ((cmsGetDeviceClass(hProfile
) == icSigDisplayClass
) ||
1790 cmsTakeHeaderFlags(hProfile
) & icTransparency
) {
1792 // NULL for cone defaults to Bradford, from media to D50
1793 cmsAdaptationMatrix(r
, NULL
, &Icc
->MediaWhitePoint
, &Icc
->Illuminant
);
1802 LPGAMMATABLE LCMSEXPORT
cmsReadICCGamma(cmsHPROFILE hProfile
, icTagSignature sig
)
1804 LPLCMSICCPROFILE Icc
= (LPLCMSICCPROFILE
) (LPSTR
) hProfile
;
1809 n
= _cmsSearchTag(Icc
, sig
, TRUE
);
1813 if (Icc
-> TagPtrs
[n
]) {
1815 return cmsDupGamma((LPGAMMATABLE
) Icc
-> TagPtrs
[n
]);
1818 offset
= Icc
-> TagOffsets
[n
];
1820 if (Icc
-> Seek(Icc
, offset
))
1823 return ReadCurve(Icc
);
1828 // Some ways have analytical revese. This function accounts for that
1830 LPGAMMATABLE LCMSEXPORT
cmsReadICCGammaReversed(cmsHPROFILE hProfile
, icTagSignature sig
)
1832 LPLCMSICCPROFILE Icc
= (LPLCMSICCPROFILE
) (LPSTR
) hProfile
;
1837 n
= _cmsSearchTag(Icc
, sig
, TRUE
);
1841 if (Icc
-> TagPtrs
[n
]) {
1843 return cmsReverseGamma(256, (LPGAMMATABLE
) Icc
-> TagPtrs
[n
]);
1846 offset
= Icc
-> TagOffsets
[n
];
1848 if (Icc
-> Seek(Icc
, offset
))
1851 return ReadCurveReversed(Icc
);
1854 // Check Named color header
1857 LCMSBOOL
CheckHeader(LPcmsNAMEDCOLORLIST v
, icNamedColor2
* nc2
)
1859 if (v
->Prefix
[0] == 0 && v
->Suffix
[0] == 0 && v
->ColorantCount
== 0) return TRUE
;
1861 if (stricmp(v
->Prefix
, (const char*) nc2
->prefix
) != 0) return FALSE
;
1862 if (stricmp(v
->Suffix
, (const char*) nc2
->suffix
) != 0) return FALSE
;
1864 return ((int) v
->ColorantCount
== (int) nc2
->nDeviceCoords
);
1867 // Read named color list
1869 int cmsReadICCnamedColorList(cmsHTRANSFORM xform
, cmsHPROFILE hProfile
, icTagSignature sig
)
1871 _LPcmsTRANSFORM v
= (_LPcmsTRANSFORM
) xform
;
1872 LPLCMSICCPROFILE Icc
= (LPLCMSICCPROFILE
) (LPSTR
) hProfile
;
1874 icTagTypeSignature BaseType
;
1877 n
= _cmsSearchTag(Icc
, sig
, TRUE
);
1881 if (Icc
-> TagPtrs
[n
]) {
1883 // This replaces actual named color list.
1884 size_t size
= Icc
-> TagSizes
[n
];
1886 if (v
->NamedColorList
) cmsFreeNamedColorList(v
->NamedColorList
);
1887 v
-> NamedColorList
= (LPcmsNAMEDCOLORLIST
) _cmsMalloc(size
);
1888 CopyMemory(v
-> NamedColorList
, Icc
->TagPtrs
[n
], size
);
1889 return v
->NamedColorList
->nColors
;
1892 offset
= Icc
-> TagOffsets
[n
];
1894 if (Icc
-> Seek(Icc
, offset
))
1897 BaseType
= ReadBase(Icc
);
1901 // I never have seen one of these. Probably is not worth of implementing.
1903 case icSigNamedColorType
: {
1905 cmsSignalError(LCMS_ERRC_WARNING
, "Ancient named color profiles are not supported.");
1909 // The named color struct
1911 case icSigNamedColor2Type
: {
1916 if (Icc
-> Read(&nc2
, sizeof(icNamedColor2
) - SIZEOF_UINT8_ALIGNED
, 1, Icc
) != 1) return 0;
1917 AdjustEndianess32((LPBYTE
) &nc2
.vendorFlag
);
1918 AdjustEndianess32((LPBYTE
) &nc2
.count
);
1919 AdjustEndianess32((LPBYTE
) &nc2
.nDeviceCoords
);
1921 if (!CheckHeader(v
->NamedColorList
, &nc2
)) {
1922 cmsSignalError(LCMS_ERRC_WARNING
, "prefix/suffix/device for named color profiles mismatch.");
1926 if (nc2
.nDeviceCoords
> MAXCHANNELS
) {
1927 cmsSignalError(LCMS_ERRC_WARNING
, "Too many device coordinates.");
1931 strncpy(v
->NamedColorList
->Prefix
, (const char*) nc2
.prefix
, 32);
1932 strncpy(v
->NamedColorList
->Suffix
, (const char*) nc2
.suffix
, 32);
1933 v
->NamedColorList
->Prefix
[32] = v
->NamedColorList
->Suffix
[32] = 0;
1935 v
->NamedColorList
->ColorantCount
= nc2
.nDeviceCoords
;
1937 for (i
=0; i
< nc2
.count
; i
++) {
1940 WORD Colorant
[MAXCHANNELS
];
1943 ZeroMemory(Colorant
, sizeof(WORD
) * MAXCHANNELS
);
1944 Icc
-> Read(Root
, 1, 32, Icc
);
1945 Icc
-> Read(PCS
, 3, sizeof(WORD
), Icc
);
1947 for (j
=0; j
< 3; j
++)
1948 AdjustEndianess16((LPBYTE
) &PCS
[j
]);
1950 Icc
-> Read(Colorant
, sizeof(WORD
), nc2
.nDeviceCoords
, Icc
);
1952 for (j
=0; j
< nc2
.nDeviceCoords
; j
++)
1953 AdjustEndianess16((LPBYTE
) &Colorant
[j
]);
1955 cmsAppendNamedColor(v
, Root
, PCS
, Colorant
);
1958 return v
->NamedColorList
->nColors
;
1963 cmsSignalError(LCMS_ERRC_WARNING
, "Bad tag signature '%lx' found.", BaseType
);
1967 // It would never reach here
1973 // Read colorant tables
1975 LPcmsNAMEDCOLORLIST LCMSEXPORT
cmsReadColorantTable(cmsHPROFILE hProfile
, icTagSignature sig
)
1977 icInt32Number n
, Count
, i
;
1979 icTagTypeSignature BaseType
;
1980 LPLCMSICCPROFILE Icc
= (LPLCMSICCPROFILE
) (LPSTR
) hProfile
;
1981 LPcmsNAMEDCOLORLIST List
;
1983 n
= _cmsSearchTag(Icc
, sig
, FALSE
);
1985 return NULL
; // Not found
1987 if (Icc
-> TagPtrs
[n
]) {
1989 size_t size
= Icc
-> TagSizes
[n
];
1990 void* v
= _cmsMalloc(size
);
1992 if (v
== NULL
) return NULL
;
1993 CopyMemory(v
, Icc
-> TagPtrs
[n
], size
);
1994 return (LPcmsNAMEDCOLORLIST
) v
;
1998 offset
= Icc
-> TagOffsets
[n
];
2000 if (Icc
-> Seek(Icc
, offset
))
2003 BaseType
= ReadBase(Icc
);
2005 if (BaseType
!= icSigColorantTableType
) {
2006 cmsSignalError(LCMS_ERRC_ABORTED
, "Bad tag signature '%lx' found.", BaseType
);
2011 if (Icc
->Read(&Count
, sizeof(icUInt32Number
), 1, Icc
) != 1) return NULL
;
2012 AdjustEndianess32((LPBYTE
) &Count
);
2014 if (Count
> MAXCHANNELS
) {
2015 cmsSignalError(LCMS_ERRC_ABORTED
, "Too many colorants '%lx'", Count
);
2019 List
= cmsAllocNamedColorList(Count
);
2020 for (i
=0; i
< Count
; i
++) {
2022 if (!Icc
->Read(List
->List
[i
].Name
, 1, 32 , Icc
)) goto Error
;
2023 if (!Icc
->Read(List
->List
[i
].PCS
, sizeof(icUInt16Number
), 3, Icc
)) goto Error
;
2024 AdjustEndianessArray16(List
->List
[i
].PCS
, 3);
2030 cmsFreeNamedColorList(List
);
2037 // Uncooked manufacturer
2039 const char* LCMSEXPORT
cmsTakeManufacturer(cmsHPROFILE hProfile
)
2042 static char Manufacturer
[LCMS_DESC_MAX
] = "";
2044 Manufacturer
[0] = 0;
2046 if (cmsIsTag(hProfile
, icSigDeviceMfgDescTag
)) {
2048 cmsReadICCTextEx(hProfile
, icSigDeviceMfgDescTag
, Manufacturer
, LCMS_DESC_MAX
);
2051 return Manufacturer
;
2056 const char* LCMSEXPORT
cmsTakeModel(cmsHPROFILE hProfile
)
2059 static char Model
[LCMS_DESC_MAX
] = "";
2063 if (cmsIsTag(hProfile
, icSigDeviceModelDescTag
)) {
2065 cmsReadICCTextEx(hProfile
, icSigDeviceModelDescTag
, Model
, LCMS_DESC_MAX
);
2072 const char* LCMSEXPORT
cmsTakeCopyright(cmsHPROFILE hProfile
)
2075 static char Copyright
[LCMS_DESC_MAX
] = "";
2078 if (cmsIsTag(hProfile
, icSigCopyrightTag
)) {
2080 cmsReadICCTextEx(hProfile
, icSigCopyrightTag
, Copyright
, LCMS_DESC_MAX
);
2087 // We compute name with model - manufacturer
2089 const char* LCMSEXPORT
cmsTakeProductName(cmsHPROFILE hProfile
)
2091 static char Name
[LCMS_DESC_MAX
*2+4];
2092 char Manufacturer
[LCMS_DESC_MAX
], Model
[LCMS_DESC_MAX
];
2095 Manufacturer
[0] = Model
[0] = '\0';
2097 if (cmsIsTag(hProfile
, icSigDeviceMfgDescTag
)) {
2099 cmsReadICCTextEx(hProfile
, icSigDeviceMfgDescTag
, Manufacturer
, LCMS_DESC_MAX
);
2102 if (cmsIsTag(hProfile
, icSigDeviceModelDescTag
)) {
2104 cmsReadICCTextEx(hProfile
, icSigDeviceModelDescTag
, Model
, LCMS_DESC_MAX
);
2107 if (!Manufacturer
[0] && !Model
[0]) {
2109 if (cmsIsTag(hProfile
, icSigProfileDescriptionTag
)) {
2111 cmsReadICCTextEx(hProfile
, icSigProfileDescriptionTag
, Name
, LCMS_DESC_MAX
);
2114 else return "{no name}";
2118 if (!Manufacturer
[0] ||
2119 strncmp(Model
, Manufacturer
, 8) == 0 || strlen(Model
) > 30)
2120 strcpy(Name
, Model
);
2122 sprintf(Name
, "%s - %s", Model
, Manufacturer
);
2129 // We compute desc with manufacturer - model
2131 const char* LCMSEXPORT
cmsTakeProductDesc(cmsHPROFILE hProfile
)
2133 static char Name
[2048];
2135 if (cmsIsTag(hProfile
, icSigProfileDescriptionTag
)) {
2137 cmsReadICCText(hProfile
, icSigProfileDescriptionTag
, Name
);
2139 else return cmsTakeProductName(hProfile
);
2141 if (strncmp(Name
, "Copyrig", 7) == 0)
2142 return cmsTakeProductName(hProfile
);
2148 const char* LCMSEXPORT
cmsTakeProductInfo(cmsHPROFILE hProfile
)
2150 LPLCMSICCPROFILE Icc
= (LPLCMSICCPROFILE
) (LPSTR
) hProfile
;
2152 static char Info
[4096];
2156 if (cmsIsTag(hProfile
, icSigProfileDescriptionTag
))
2160 cmsReadICCText(hProfile
, icSigProfileDescriptionTag
, Desc
);
2162 strcat(Info
, "\r\n\r\n");
2166 if (cmsIsTag(hProfile
, icSigCopyrightTag
))
2168 char Copyright
[LCMS_DESC_MAX
];
2170 cmsReadICCText(hProfile
, icSigCopyrightTag
, Copyright
);
2171 strcat(Info
, Copyright
);
2172 strcat(Info
, "\r\n\r\n");
2177 // KODAK private tag... But very useful
2179 #define K007 (icTagSignature)0x4B303037
2183 if (cmsIsTag(hProfile
, K007
))
2185 char MonCal
[LCMS_DESC_MAX
];
2187 cmsReadICCText(hProfile
, K007
, MonCal
);
2188 strcat(Info
, MonCal
);
2189 strcat(Info
, "\r\n\r\n");
2194 char WhiteStr
[1024];
2196 cmsTakeMediaWhitePoint(&WhitePt
, hProfile
);
2197 _cmsIdentifyWhitePoint(WhiteStr
, &WhitePt
);
2198 strcat(WhiteStr
, "\r\n\r\n");
2199 strcat(Info
, WhiteStr
);
2203 if (Icc
-> stream
) {
2204 strcat(Info
, Icc
-> PhysicalFile
);
2209 // Extract the target data as a big string. Does not signal if tag is not present.
2211 LCMSBOOL LCMSEXPORT
cmsTakeCharTargetData(cmsHPROFILE hProfile
, char** Data
, size_t* len
)
2213 LPLCMSICCPROFILE Icc
= (LPLCMSICCPROFILE
) (LPSTR
) hProfile
;
2219 n
= _cmsSearchTag(Icc
, icSigCharTargetTag
, FALSE
);
2220 if (n
< 0) return FALSE
;
2223 *len
= Icc
-> TagSizes
[n
];
2225 // Make sure that is reasonable (600K)
2226 if (*len
> 600*1024) *len
= 600*1024;
2228 *Data
= (char*) _cmsMalloc(*len
+ 1); // Plus zero marker
2232 cmsSignalError(LCMS_ERRC_ABORTED
, "Out of memory allocating CharTarget space!");
2236 if (cmsReadICCTextEx(hProfile
, icSigCharTargetTag
, *Data
, *len
) < 0)
2239 (*Data
)[*len
] = 0; // Force a zero marker. Shouldn't be needed, but is
2240 // here to simplify things.
2248 LCMSBOOL LCMSEXPORT
cmsTakeCalibrationDateTime(struct tm
*Dest
, cmsHPROFILE hProfile
)
2250 LPLCMSICCPROFILE Icc
= (LPLCMSICCPROFILE
) (LPSTR
) hProfile
;
2253 n
= _cmsSearchTag(Icc
, icSigCalibrationDateTimeTag
, FALSE
);
2254 if (n
< 0) return FALSE
;
2256 if (Icc
->TagPtrs
[n
]) {
2258 CopyMemory(Dest
, Icc
->TagPtrs
[n
], sizeof(struct tm
));
2262 icDateTimeNumber timestamp
;
2264 if (Icc
-> Seek(Icc
, Icc
-> TagOffsets
[n
] + sizeof(icTagBase
)))
2267 if (Icc
->Read(×tamp
, 1, sizeof(icDateTimeNumber
), Icc
) != sizeof(icDateTimeNumber
))
2270 DecodeDateTimeNumber(×tamp
, Dest
);
2279 // PSEQ Tag, used in devicelink profiles
2281 LPcmsSEQ LCMSEXPORT
cmsReadProfileSequenceDescription(cmsHPROFILE hProfile
)
2283 LPLCMSICCPROFILE Icc
= (LPLCMSICCPROFILE
) (LPSTR
) hProfile
;
2285 icUInt32Number i
, Count
;
2286 icDescStruct DescStruct
;
2287 icTagTypeSignature BaseType
;
2288 size_t size
, offset
;
2292 n
= _cmsSearchTag(Icc
, icSigProfileSequenceDescTag
, FALSE
);
2293 if (n
< 0) return NULL
;
2295 size
= Icc
-> TagSizes
[n
];
2296 if (size
< 12) return NULL
;
2298 if (Icc
-> TagPtrs
[n
]) {
2300 OutSeq
= (LPcmsSEQ
) _cmsMalloc(size
);
2301 if (OutSeq
== NULL
) return NULL
;
2302 CopyMemory(OutSeq
, Icc
->TagPtrs
[n
], size
);
2306 offset
= Icc
-> TagOffsets
[n
];
2308 if (Icc
-> Seek(Icc
, offset
))
2311 BaseType
= ReadBase(Icc
);
2313 if (BaseType
!= icSigProfileSequenceDescType
) return NULL
;
2315 Icc
->Read(&Count
, sizeof(icUInt32Number
), 1, Icc
);
2316 AdjustEndianess32((LPBYTE
) &Count
);
2318 size
= sizeof(int) + Count
* sizeof(cmsPSEQDESC
);
2319 OutSeq
= (LPcmsSEQ
) _cmsMalloc(size
);
2320 if (OutSeq
== NULL
) return NULL
;
2324 // Get structures as well
2326 for (i
=0; i
< Count
; i
++) {
2328 LPcmsPSEQDESC sec
= &OutSeq
-> seq
[i
];
2330 Icc
-> Read(&DescStruct
, sizeof(icDescStruct
) - SIZEOF_UINT8_ALIGNED
, 1, Icc
);
2332 AdjustEndianess32((LPBYTE
) &DescStruct
.deviceMfg
);
2333 AdjustEndianess32((LPBYTE
) &DescStruct
.deviceModel
);
2334 AdjustEndianess32((LPBYTE
) &DescStruct
.technology
);
2335 AdjustEndianess32((LPBYTE
) &DescStruct
.attributes
[0]);
2336 AdjustEndianess32((LPBYTE
) &DescStruct
.attributes
[1]);
2338 sec
->attributes
[0] = DescStruct
.attributes
[0];
2339 sec
->attributes
[1] = DescStruct
.attributes
[1];
2340 sec
->deviceMfg
= DescStruct
.deviceMfg
;
2341 sec
->deviceModel
= DescStruct
.deviceModel
;
2342 sec
->technology
= DescStruct
.technology
;
2344 if (ReadEmbeddedTextTag(Icc
, size
, sec
->Manufacturer
, LCMS_DESC_MAX
) < 0) return NULL
;
2345 if (ReadEmbeddedTextTag(Icc
, size
, sec
->Model
, LCMS_DESC_MAX
) < 0) return NULL
;
2353 void LCMSEXPORT
cmsFreeProfileSequenceDescription(LPcmsSEQ pseq
)
2363 // Read a few tags that are hardly required
2367 void ReadCriticalTags(LPLCMSICCPROFILE Icc
)
2369 cmsHPROFILE hProfile
= (cmsHPROFILE
) Icc
;
2371 if (Icc
->Version
>= 0x4000000) {
2377 if (ReadICCXYZ(hProfile
,
2378 icSigMediaWhitePointTag
,
2379 &Icc
->MediaWhitePoint
, FALSE
) < 0) {
2381 Icc
->MediaWhitePoint
= *cmsD50_XYZ();
2386 if (ReadICCXYZ(hProfile
,
2387 icSigMediaBlackPointTag
,
2388 &Icc
->MediaBlackPoint
, FALSE
) < 0) {
2390 Icc
->MediaBlackPoint
.X
= 0;
2391 Icc
->MediaBlackPoint
.Y
= 0;
2392 Icc
->MediaBlackPoint
.X
= 0;
2396 NormalizeXYZ(&Icc
->MediaWhitePoint
);
2397 NormalizeXYZ(&Icc
->MediaBlackPoint
);
2399 if (ReadICCXYZArray(hProfile
,
2400 icSigChromaticAdaptationTag
,
2401 &ChrmCanonical
) > 0) {
2403 MAT3inverse(&ChrmCanonical
, &Icc
->ChromaticAdaptation
);
2408 MAT3identity(&Icc
->ChromaticAdaptation
);
2412 // Convert media white, black to absolute under original illuminant
2414 EvalCHRM(&Icc
->MediaWhitePoint
, &Icc
->ChromaticAdaptation
, &Icc
->MediaWhitePoint
);
2415 EvalCHRM(&Icc
->MediaBlackPoint
, &Icc
->ChromaticAdaptation
, &Icc
->MediaBlackPoint
);
2425 if (ReadICCXYZ(hProfile
,
2426 icSigMediaWhitePointTag
,
2427 &Icc
->MediaWhitePoint
, FALSE
) < 0) {
2429 Icc
->MediaWhitePoint
= *cmsD50_XYZ();
2434 if (ReadICCXYZ(hProfile
,
2435 icSigMediaBlackPointTag
,
2436 &Icc
->MediaBlackPoint
, FALSE
) < 0) {
2438 Icc
->MediaBlackPoint
.X
= 0;
2439 Icc
->MediaBlackPoint
.Y
= 0;
2440 Icc
->MediaBlackPoint
.X
= 0;
2444 NormalizeXYZ(&Icc
->MediaWhitePoint
);
2445 NormalizeXYZ(&Icc
->MediaBlackPoint
);
2448 // Take Bradford as default for Display profiles only.
2450 if (cmsGetDeviceClass(hProfile
) == icSigDisplayClass
) {
2453 cmsAdaptationMatrix(&Icc
-> ChromaticAdaptation
,
2456 &Icc
-> MediaWhitePoint
);
2459 MAT3identity(&Icc
->ChromaticAdaptation
);
2466 // Create profile from disk file
2468 cmsHPROFILE LCMSEXPORT
cmsOpenProfileFromFile(const char *lpFileName
, const char *sAccess
)
2470 LPLCMSICCPROFILE NewIcc
;
2474 // Open for write means an empty profile
2476 if (*sAccess
== 'W' || *sAccess
== 'w') {
2478 hEmpty
= _cmsCreateProfilePlaceholder();
2479 NewIcc
= (LPLCMSICCPROFILE
) (LPSTR
) hEmpty
;
2480 NewIcc
-> IsWrite
= TRUE
;
2481 strncpy(NewIcc
->PhysicalFile
, lpFileName
, MAX_PATH
-1);
2482 NewIcc
->PhysicalFile
[MAX_PATH
-1] = 0;
2484 // Save LUT as 8 bit
2487 if (*sAccess
== '8') NewIcc
->SaveAs8Bits
= TRUE
;
2493 // Open for read means a file placeholder
2495 NewIcc
= _cmsCreateProfileFromFilePlaceholder(lpFileName
);
2496 if (!NewIcc
) return NULL
;
2498 if (!ReadHeader(NewIcc
, FALSE
)) return NULL
;
2500 ReadCriticalTags(NewIcc
);
2502 return (cmsHPROFILE
) (LPSTR
) NewIcc
;
2508 // Open from memory block
2510 cmsHPROFILE LCMSEXPORT
cmsOpenProfileFromMem(LPVOID MemPtr
, DWORD dwSize
)
2512 LPLCMSICCPROFILE NewIcc
;
2515 NewIcc
= _cmsCreateProfileFromMemPlaceholder(MemPtr
, dwSize
);
2516 if (!NewIcc
) return NULL
;
2518 if (!ReadHeader(NewIcc
, TRUE
)) return NULL
;
2520 ReadCriticalTags(NewIcc
);
2522 return (cmsHPROFILE
) (LPSTR
) NewIcc
;
2526 // Checks a profile for obvious inconsistencies and returns
2527 // TRUE if the profile looks bogus and should probably be
2529 LCMSBOOL LCMSEXPORT
cmsProfileIsBogus(cmsHPROFILE hProfile
)
2532 cmsCIEXYZTRIPLE primaries
;
2533 VEC3 sum
, target
, tolerance
;
2536 // Read the primaries
2537 cmsTakeColorants(&primaries
, hProfile
);
2540 sum
.n
[0] = primaries
.Red
.X
+ primaries
.Green
.X
+ primaries
.Blue
.X
;
2541 sum
.n
[1] = primaries
.Red
.Y
+ primaries
.Green
.Y
+ primaries
.Blue
.Y
;
2542 sum
.n
[2] = primaries
.Red
.Z
+ primaries
.Green
.Z
+ primaries
.Blue
.Z
;
2544 // Build our target vector (see mozilla bug 460629)
2545 target
.n
[0] = 0.96420;
2546 target
.n
[1] = 1.00000;
2547 target
.n
[2] = 0.82491;
2549 // Our tolerance vector - Recommended by Chris Murphy based on
2550 // conversion from the LAB space criterion of no more than 3 in any one
2551 // channel. This is similar to, but slightly more tolerant than Adobe's
2553 tolerance
.n
[0] = 0.02;
2554 tolerance
.n
[1] = 0.02;
2555 tolerance
.n
[2] = 0.04;
2557 // Compare with our tolerance
2558 for (i
= 0; i
< 3; ++i
) {
2559 if (!(((sum
.n
[i
] - tolerance
.n
[i
]) <= target
.n
[i
]) &&
2560 ((sum
.n
[i
] + tolerance
.n
[i
]) >= target
.n
[i
])))
2568 LCMSBOOL LCMSEXPORT
cmsCloseProfile(cmsHPROFILE hProfile
)
2570 LPLCMSICCPROFILE Icc
= (LPLCMSICCPROFILE
) (LPSTR
) hProfile
;
2574 if (!Icc
) return FALSE
;
2576 // Free any precaches
2577 for (i
= 0; i
< PRECACHE_TYPE_COUNT
; ++i
)
2578 if (Icc
->Precache
[i
] != NULL
)
2579 PRECACHE_RELEASE(Icc
->Precache
[i
]);
2581 // Was open in write mode?
2582 if (Icc
->IsWrite
) {
2584 Icc
->IsWrite
= FALSE
; // Assure no further writting
2585 rc
= _cmsSaveProfile(hProfile
, Icc
->PhysicalFile
);
2588 for (i
=0; i
< Icc
-> TagCount
; i
++) {
2590 if (Icc
-> TagPtrs
[i
])
2591 free(Icc
-> TagPtrs
[i
]);
2594 if (Icc
-> stream
!= NULL
) { // Was a memory (i.e. not serialized) profile?
2595 Icc
-> Close(Icc
); // No, close the stream
2598 free(Icc
); // Free placeholder memory
2605 // Write profile ------------------------------------------------------------
2610 LCMSBOOL
SaveWordsTable(int nEntries
, LPWORD Tab
, LPLCMSICCPROFILE Icc
)
2612 size_t nTabSize
= sizeof(WORD
) * nEntries
;
2613 LPWORD PtrW
= (LPWORD
) _cmsMalloc(nTabSize
);
2616 if (!PtrW
) return FALSE
;
2617 CopyMemory(PtrW
, Tab
, nTabSize
);
2618 AdjustEndianessArray16(PtrW
, nEntries
);
2619 rc
= Icc
->Write(Icc
, nTabSize
, PtrW
);
2627 // Saves profile header
2630 LCMSBOOL
SaveHeader(LPLCMSICCPROFILE Icc
)
2633 time_t now
= time(NULL
);
2635 Header
.size
= TransportValue32((icInt32Number
) Icc
->UsedSpace
);
2636 Header
.cmmId
= TransportValue32(lcmsSignature
);
2637 Header
.version
= TransportValue32((icInt32Number
) 0x02300000);
2638 Header
.deviceClass
= (icProfileClassSignature
) TransportValue32(Icc
-> DeviceClass
);
2639 Header
.colorSpace
= (icColorSpaceSignature
) TransportValue32(Icc
-> ColorSpace
);
2640 Header
.pcs
= (icColorSpaceSignature
) TransportValue32(Icc
-> PCS
);
2642 // NOTE: in v4 Timestamp must be in UTC rather than in local time
2643 EncodeDateTimeNumber(&Header
.date
, gmtime(&now
));
2645 Header
.magic
= TransportValue32(icMagicNumber
);
2648 Header
.platform
= (icPlatformSignature
)TransportValue32(icSigMacintosh
);
2650 Header
.platform
= (icPlatformSignature
)TransportValue32(icSigMicrosoft
);
2653 Header
.flags
= TransportValue32(Icc
-> flags
);
2654 Header
.manufacturer
= TransportValue32(lcmsSignature
);
2655 Header
.model
= TransportValue32(0);
2656 Header
.attributes
[0]= TransportValue32(Icc
-> attributes
);
2657 Header
.attributes
[1]= TransportValue32(0);
2659 Header
.renderingIntent
= TransportValue32(Icc
-> RenderingIntent
);
2661 // Illuminant is D50
2663 Header
.illuminant
.X
= TransportValue32(DOUBLE_TO_FIXED(Icc
-> Illuminant
.X
));
2664 Header
.illuminant
.Y
= TransportValue32(DOUBLE_TO_FIXED(Icc
-> Illuminant
.Y
));
2665 Header
.illuminant
.Z
= TransportValue32(DOUBLE_TO_FIXED(Icc
-> Illuminant
.Z
));
2667 Header
.creator
= TransportValue32(lcmsSignature
);
2669 ZeroMemory(&Header
.reserved
, sizeof(Header
.reserved
));
2672 CopyMemory(Header
.reserved
, Icc
->ProfileID
, 16);
2675 Icc
->UsedSpace
= 0; // Mark as begin-of-file
2677 return Icc
->Write(Icc
, sizeof(icHeader
), &Header
);
2682 // Setup base marker
2685 LCMSBOOL
SetupBase(icTagTypeSignature sig
, LPLCMSICCPROFILE Icc
)
2689 Base
.sig
= (icTagTypeSignature
) TransportValue32(sig
);
2690 ZeroMemory(&Base
.reserved
, sizeof(Base
.reserved
));
2691 return Icc
-> Write(Icc
, sizeof(icTagBase
), &Base
);
2698 LCMSBOOL
SaveXYZNumber(LPcmsCIEXYZ Value
, LPLCMSICCPROFILE Icc
)
2703 if (!SetupBase(icSigXYZType
, Icc
)) return FALSE
;
2705 XYZ
.X
= TransportValue32(DOUBLE_TO_FIXED(Value
-> X
));
2706 XYZ
.Y
= TransportValue32(DOUBLE_TO_FIXED(Value
-> Y
));
2707 XYZ
.Z
= TransportValue32(DOUBLE_TO_FIXED(Value
-> Z
));
2710 return Icc
-> Write(Icc
, sizeof(icXYZNumber
), &XYZ
);
2714 // Store a XYZ array.
2717 LCMSBOOL
SaveXYZArray(int n
, LPcmsCIEXYZ Value
, LPLCMSICCPROFILE Icc
)
2722 if (!SetupBase(icSigS15Fixed16ArrayType
, Icc
)) return FALSE
;
2724 for (i
=0; i
< n
; i
++) {
2726 XYZ
.X
= TransportValue32(DOUBLE_TO_FIXED(Value
-> X
));
2727 XYZ
.Y
= TransportValue32(DOUBLE_TO_FIXED(Value
-> Y
));
2728 XYZ
.Z
= TransportValue32(DOUBLE_TO_FIXED(Value
-> Z
));
2730 if (!Icc
-> Write(Icc
, sizeof(icXYZNumber
), &XYZ
)) return FALSE
;
2740 // Save a gamma structure as a table
2743 LCMSBOOL
SaveGammaTable(LPGAMMATABLE Gamma
, LPLCMSICCPROFILE Icc
)
2745 icInt32Number Count
;
2747 if (!SetupBase(icSigCurveType
, Icc
)) return FALSE
;
2749 Count
= TransportValue32(Gamma
->nEntries
);
2751 if (!Icc
->Write(Icc
, sizeof(icInt32Number
), &Count
)) return FALSE
;
2753 return SaveWordsTable(Gamma
->nEntries
, Gamma
->GammaTable
, Icc
);
2757 // Save a gamma structure as a one-value
2760 LCMSBOOL
SaveGammaOneValue(LPGAMMATABLE Gamma
, LPLCMSICCPROFILE Icc
)
2762 icInt32Number Count
;
2763 Fixed32 GammaFixed32
;
2766 if (!SetupBase(icSigCurveType
, Icc
)) return FALSE
;
2768 Count
= TransportValue32(1);
2769 if (!Icc
->Write(Icc
, sizeof(icInt32Number
), &Count
)) return FALSE
;
2771 GammaFixed32
= DOUBLE_TO_FIXED(Gamma
->Seed
.Params
[0]);
2772 GammaFixed8
= (WORD
) ((GammaFixed32
>> 8) & 0xFFFF);
2773 GammaFixed8
= TransportValue16(GammaFixed8
);
2775 return Icc
->Write(Icc
, sizeof(icInt16Number
), &GammaFixed8
);
2778 // Save a gamma structure as a parametric gamma
2781 LCMSBOOL
SaveGammaParametric(LPGAMMATABLE Gamma
, LPLCMSICCPROFILE Icc
)
2783 icUInt16Number Type
, Reserved
;
2785 int ParamsByType
[] = { 1, 3, 4, 5, 7 };
2787 if (!SetupBase(icSigParametricCurveType
, Icc
)) return FALSE
;
2789 nParams
= ParamsByType
[Gamma
-> Seed
.Type
];
2791 Type
= (icUInt16Number
) TransportValue16((WORD
) Gamma
-> Seed
. Type
);
2792 Reserved
= (icUInt16Number
) TransportValue16((WORD
) 0);
2794 Icc
-> Write(Icc
, sizeof(icInt16Number
), &Type
);
2795 Icc
-> Write(Icc
, sizeof(icUInt16Number
), &Reserved
);
2797 for (i
=0; i
< nParams
; i
++) {
2799 icInt32Number val
= TransportValue32(DOUBLE_TO_FIXED(Gamma
-> Seed
.Params
[i
]));
2800 Icc
->Write(Icc
, sizeof(icInt32Number
), &val
);
2809 // Save a gamma table
2812 LCMSBOOL
SaveGamma(LPGAMMATABLE Gamma
, LPLCMSICCPROFILE Icc
)
2814 // Is the gamma curve type supported by ICC format?
2816 if (Gamma
-> Seed
.Type
< 0 || Gamma
-> Seed
.Type
> 5 ||
2818 // has been modified by user?
2820 _cmsCrc32OfGammaTable(Gamma
) != Gamma
-> Seed
.Crc32
) {
2822 return SaveGammaTable(Gamma
, Icc
);
2825 if (Gamma
-> Seed
.Type
== 1) return SaveGammaOneValue(Gamma
, Icc
);
2827 // Only v4 profiles are allowed to hold parametric curves
2829 if (cmsGetProfileICCversion((cmsHPROFILE
) Icc
) >= 0x4000000)
2830 return SaveGammaParametric(Gamma
, Icc
);
2832 // Defaults to save as table
2834 return SaveGammaTable(Gamma
, Icc
);
2844 LCMSBOOL
SaveDescription(const char *Text
, LPLCMSICCPROFILE Icc
)
2847 icUInt32Number len
, Count
, TotalSize
, AlignedSize
;
2850 len
= (icUInt32Number
) (strlen(Text
) + 1);
2852 // * icInt8Number desc[count] * NULL terminated ascii string
2853 // * icUInt32Number ucLangCode; * UniCode language code
2854 // * icUInt32Number ucCount; * UniCode description length
2855 // * icInt16Number ucDesc[ucCount];* The UniCode description
2856 // * icUInt16Number scCode; * ScriptCode code
2857 // * icUInt8Number scCount; * ScriptCode count
2858 // * icInt8Number scDesc[67]; * ScriptCode Description
2860 TotalSize
= sizeof(icTagBase
) + sizeof(icUInt32Number
) + len
+
2861 sizeof(icUInt32Number
) + sizeof(icUInt32Number
) +
2862 sizeof(icUInt16Number
) + sizeof(icUInt8Number
) + 67;
2864 AlignedSize
= TotalSize
; // Can be unaligned!!
2866 if (!SetupBase(icSigTextDescriptionType
, Icc
)) return FALSE
;
2867 AlignedSize
-= sizeof(icTagBase
);
2869 Count
= TransportValue32(len
);
2870 if (!Icc
->Write(Icc
, sizeof(icUInt32Number
), &Count
)) return FALSE
;
2871 AlignedSize
-= sizeof(icUInt32Number
);
2873 if (!Icc
->Write(Icc
, len
, (LPVOID
)Text
)) return FALSE
;
2876 if (AlignedSize
< 0)
2878 if (AlignedSize
> 255)
2881 ZeroMemory(Filler
, AlignedSize
);
2882 if (!Icc
->Write(Icc
, AlignedSize
, Filler
)) return FALSE
;
2887 // Save an ASCII Tag
2890 LCMSBOOL
SaveText(const char *Text
, LPLCMSICCPROFILE Icc
)
2892 size_t len
= strlen(Text
) + 1;
2894 if (!SetupBase(icSigTextType
, Icc
)) return FALSE
;
2895 if (!Icc
->Write(Icc
, len
, (LPVOID
) Text
)) return FALSE
;
2900 // Save one of these new chromaticity values
2903 LCMSBOOL
SaveOneChromaticity(double x
, double y
, LPLCMSICCPROFILE Icc
)
2907 xf
= TransportValue32(DOUBLE_TO_FIXED(x
));
2908 yf
= TransportValue32(DOUBLE_TO_FIXED(y
));
2910 if (!Icc
->Write(Icc
, sizeof(Fixed32
), &xf
)) return FALSE
;
2911 if (!Icc
->Write(Icc
, sizeof(Fixed32
), &yf
)) return FALSE
;
2917 // New tag added in Addendum II of old spec.
2920 LCMSBOOL
SaveChromaticities(LPcmsCIExyYTRIPLE chrm
, LPLCMSICCPROFILE Icc
)
2924 if (!SetupBase(icSigChromaticityType
, Icc
)) return FALSE
;
2926 nChans
= TransportValue16(3);
2927 if (!Icc
->Write(Icc
, sizeof(WORD
) , &nChans
)) return FALSE
;
2928 Table
= TransportValue16(0);
2929 if (!Icc
->Write(Icc
, sizeof(WORD
) , &Table
)) return FALSE
;
2931 if (!SaveOneChromaticity(chrm
-> Red
.x
, chrm
-> Red
.y
, Icc
)) return FALSE
;
2932 if (!SaveOneChromaticity(chrm
-> Green
.x
, chrm
-> Green
.y
, Icc
)) return FALSE
;
2933 if (!SaveOneChromaticity(chrm
-> Blue
.x
, chrm
-> Blue
.y
, Icc
)) return FALSE
;
2940 LCMSBOOL
SaveSequenceDescriptionTag(LPcmsSEQ seq
, LPLCMSICCPROFILE Icc
)
2942 icUInt32Number nSeqs
;
2943 icDescStruct DescStruct
;
2945 LPcmsPSEQDESC pseq
= seq
->seq
;
2947 if (!SetupBase(icSigProfileSequenceDescType
, Icc
)) return FALSE
;
2949 nSeqs
= TransportValue32(n
);
2951 if (!Icc
->Write(Icc
, sizeof(icUInt32Number
) , &nSeqs
)) return FALSE
;
2953 for (i
=0; i
< n
; i
++) {
2955 LPcmsPSEQDESC sec
= pseq
+ i
;
2958 DescStruct
.deviceMfg
= (icTagTypeSignature
) TransportValue32(sec
->deviceMfg
);
2959 DescStruct
.deviceModel
= (icTagTypeSignature
) TransportValue32(sec
->deviceModel
);
2960 DescStruct
.technology
= (icTechnologySignature
) TransportValue32(sec
->technology
);
2961 DescStruct
.attributes
[0]= TransportValue32(sec
->attributes
[0]);
2962 DescStruct
.attributes
[1]= TransportValue32(sec
->attributes
[1]);
2964 if (!Icc
->Write(Icc
, sizeof(icDescStruct
) - SIZEOF_UINT8_ALIGNED
, &DescStruct
)) return FALSE
;
2966 if (!SaveDescription(sec
->Manufacturer
, Icc
)) return FALSE
;
2967 if (!SaveDescription(sec
->Model
, Icc
)) return FALSE
;
2974 // Saves a timestamp tag
2977 LCMSBOOL
SaveDateTimeNumber(const struct tm
*DateTime
, LPLCMSICCPROFILE Icc
)
2979 icDateTimeNumber Dest
;
2981 if (!SetupBase(icSigDateTimeType
, Icc
)) return FALSE
;
2982 EncodeDateTimeNumber(&Dest
, DateTime
);
2983 if (!Icc
->Write(Icc
, sizeof(icDateTimeNumber
), &Dest
)) return FALSE
;
2989 // Saves a named color list into a named color profile
2991 LCMSBOOL
SaveNamedColorList(LPcmsNAMEDCOLORLIST NamedColorList
, LPLCMSICCPROFILE Icc
)
2994 icUInt32Number vendorFlag
; // Bottom 16 bits for IC use
2995 icUInt32Number count
; // Count of named colors
2996 icUInt32Number nDeviceCoords
; // Num of device coordinates
2997 char prefix
[32]; // Prefix for each color name
2998 char suffix
[32]; // Suffix for each color name
3001 if (!SetupBase(icSigNamedColor2Type
, Icc
)) return FALSE
;
3003 vendorFlag
= TransportValue32(0);
3004 count
= TransportValue32(NamedColorList
->nColors
);
3005 nDeviceCoords
= TransportValue32(NamedColorList
->ColorantCount
);
3007 strncpy(prefix
, (const char*) NamedColorList
->Prefix
, 32);
3008 strncpy(suffix
, (const char*) NamedColorList
->Suffix
, 32);
3010 suffix
[31] = prefix
[31] = 0;
3012 if (!Icc
->Write(Icc
, sizeof(icUInt32Number
), &vendorFlag
)) return FALSE
;
3013 if (!Icc
->Write(Icc
, sizeof(icUInt32Number
), &count
)) return FALSE
;
3014 if (!Icc
->Write(Icc
, sizeof(icUInt32Number
), &nDeviceCoords
)) return FALSE
;
3015 if (!Icc
->Write(Icc
, 32 , prefix
)) return FALSE
;
3016 if (!Icc
->Write(Icc
, 32 , suffix
)) return FALSE
;
3018 for (i
=0; i
< NamedColorList
->nColors
; i
++) {
3020 icUInt16Number PCS
[3];
3021 icUInt16Number Colorant
[MAXCHANNELS
];
3023 LPcmsNAMEDCOLOR Color
;
3026 Color
= NamedColorList
->List
+ i
;
3028 strncpy(root
, Color
->Name
, 32);
3029 Color
->Name
[32] = 0;
3031 if (!Icc
->Write(Icc
, 32 , root
)) return FALSE
;
3033 for (j
=0; j
< 3; j
++)
3034 PCS
[j
] = TransportValue16(Color
->PCS
[j
]);
3036 if (!Icc
->Write(Icc
, 3 * sizeof(icUInt16Number
), PCS
)) return FALSE
;
3038 for (j
=0; j
< NamedColorList
->ColorantCount
; j
++)
3039 Colorant
[j
] = TransportValue16(Color
->DeviceColorant
[j
]);
3041 if (!Icc
->Write(Icc
,
3042 NamedColorList
->ColorantCount
* sizeof(icUInt16Number
), Colorant
)) return FALSE
;
3051 // Saves a colorant table. It is using the named color structure for simplicity sake
3054 LCMSBOOL
SaveColorantTable(LPcmsNAMEDCOLORLIST NamedColorList
, LPLCMSICCPROFILE Icc
)
3056 icUInt32Number count
; // Count of named colors
3059 if (!SetupBase(icSigColorantTableType
, Icc
)) return FALSE
;
3061 count
= TransportValue32(NamedColorList
->nColors
);
3063 if (!Icc
->Write(Icc
, sizeof(icUInt32Number
), &count
)) return FALSE
;
3065 for (i
=0; i
< NamedColorList
->nColors
; i
++) {
3067 icUInt16Number PCS
[3];
3068 icInt8Number root
[33];
3069 LPcmsNAMEDCOLOR Color
;
3072 Color
= NamedColorList
->List
+ i
;
3074 strncpy((char*) root
, Color
->Name
, 32);
3077 if (!Icc
->Write(Icc
, 32 , root
)) return FALSE
;
3079 for (j
=0; j
< 3; j
++)
3080 PCS
[j
] = TransportValue16(Color
->PCS
[j
]);
3082 if (!Icc
->Write(Icc
, 3 * sizeof(icUInt16Number
), PCS
)) return FALSE
;
3090 // Does serialization of LUT16 and writes it.
3093 LCMSBOOL
SaveLUT(const LUT
* NewLUT
, LPLCMSICCPROFILE Icc
)
3098 WORD NullTbl
[2] = { 0, 0xFFFFU
};
3101 if (!SetupBase(icSigLut16Type
, Icc
)) return FALSE
;
3103 LUT16
.clutPoints
= (icUInt8Number
) NewLUT
-> cLutPoints
;
3104 LUT16
.inputChan
= (icUInt8Number
) NewLUT
-> InputChan
;
3105 LUT16
.outputChan
= (icUInt8Number
) NewLUT
-> OutputChan
;
3107 LUT16
.inputEnt
= TransportValue16((WORD
) ((NewLUT
-> wFlags
& LUT_HASTL1
) ? NewLUT
-> InputEntries
: 2));
3108 LUT16
.outputEnt
= TransportValue16((WORD
) ((NewLUT
-> wFlags
& LUT_HASTL2
) ? NewLUT
-> OutputEntries
: 2));
3110 if (NewLUT
-> wFlags
& LUT_HASMATRIX
) {
3112 LUT16
.e00
= TransportValue32(NewLUT
-> Matrix
.v
[0].n
[0]);
3113 LUT16
.e01
= TransportValue32(NewLUT
-> Matrix
.v
[0].n
[1]);
3114 LUT16
.e02
= TransportValue32(NewLUT
-> Matrix
.v
[0].n
[2]);
3115 LUT16
.e10
= TransportValue32(NewLUT
-> Matrix
.v
[1].n
[0]);
3116 LUT16
.e11
= TransportValue32(NewLUT
-> Matrix
.v
[1].n
[1]);
3117 LUT16
.e12
= TransportValue32(NewLUT
-> Matrix
.v
[1].n
[2]);
3118 LUT16
.e20
= TransportValue32(NewLUT
-> Matrix
.v
[2].n
[0]);
3119 LUT16
.e21
= TransportValue32(NewLUT
-> Matrix
.v
[2].n
[1]);
3120 LUT16
.e22
= TransportValue32(NewLUT
-> Matrix
.v
[2].n
[2]);
3124 LUT16
.e00
= TransportValue32(DOUBLE_TO_FIXED(1));
3125 LUT16
.e01
= TransportValue32(DOUBLE_TO_FIXED(0));
3126 LUT16
.e02
= TransportValue32(DOUBLE_TO_FIXED(0));
3127 LUT16
.e10
= TransportValue32(DOUBLE_TO_FIXED(0));
3128 LUT16
.e11
= TransportValue32(DOUBLE_TO_FIXED(1));
3129 LUT16
.e12
= TransportValue32(DOUBLE_TO_FIXED(0));
3130 LUT16
.e20
= TransportValue32(DOUBLE_TO_FIXED(0));
3131 LUT16
.e21
= TransportValue32(DOUBLE_TO_FIXED(0));
3132 LUT16
.e22
= TransportValue32(DOUBLE_TO_FIXED(1));
3138 Icc
-> Write(Icc
, sizeof(icLut16
)- SIZEOF_UINT16_ALIGNED
, &LUT16
);
3140 // The prelinearization table
3142 for (i
=0; i
< NewLUT
-> InputChan
; i
++) {
3144 if (NewLUT
-> wFlags
& LUT_HASTL1
) {
3146 if (!SaveWordsTable(NewLUT
-> InputEntries
,
3147 NewLUT
-> L1
[i
], Icc
)) return FALSE
;
3150 else Icc
-> Write(Icc
, sizeof(WORD
)* 2, NullTbl
);
3154 nTabSize
= (NewLUT
-> OutputChan
* uipow(NewLUT
->cLutPoints
,
3155 NewLUT
->InputChan
));
3160 if (!SaveWordsTable((int) nTabSize
, NewLUT
-> T
, Icc
)) return FALSE
;
3162 // The postlinearization table
3164 for (i
=0; i
< NewLUT
-> OutputChan
; i
++) {
3166 if (NewLUT
-> wFlags
& LUT_HASTL2
) {
3168 if (!SaveWordsTable(NewLUT
-> OutputEntries
,
3169 NewLUT
-> L2
[i
], Icc
)) return FALSE
;
3171 else Icc
-> Write(Icc
, sizeof(WORD
)* 2, NullTbl
);
3180 // Does serialization of LUT8 and writes it
3183 LCMSBOOL
SaveLUT8(const LUT
* NewLUT
, LPLCMSICCPROFILE Icc
)
3192 if (NewLUT
-> wFlags
& LUT_HASTL1
) {
3194 if (NewLUT
-> InputEntries
!= 256) {
3195 cmsSignalError(LCMS_ERRC_ABORTED
, "LUT8 needs 256 entries on prelinearization");
3202 if (NewLUT
-> wFlags
& LUT_HASTL2
) {
3204 if (NewLUT
-> OutputEntries
!= 256) {
3205 cmsSignalError(LCMS_ERRC_ABORTED
, "LUT8 needs 256 entries on postlinearization");
3212 if (!SetupBase(icSigLut8Type
, Icc
)) return FALSE
;
3214 LUT8
.clutPoints
= (icUInt8Number
) NewLUT
-> cLutPoints
;
3215 LUT8
.inputChan
= (icUInt8Number
) NewLUT
-> InputChan
;
3216 LUT8
.outputChan
= (icUInt8Number
) NewLUT
-> OutputChan
;
3219 if (NewLUT
-> wFlags
& LUT_HASMATRIX
) {
3221 LUT8
.e00
= TransportValue32(NewLUT
-> Matrix
.v
[0].n
[0]);
3222 LUT8
.e01
= TransportValue32(NewLUT
-> Matrix
.v
[0].n
[1]);
3223 LUT8
.e02
= TransportValue32(NewLUT
-> Matrix
.v
[0].n
[2]);
3224 LUT8
.e10
= TransportValue32(NewLUT
-> Matrix
.v
[1].n
[0]);
3225 LUT8
.e11
= TransportValue32(NewLUT
-> Matrix
.v
[1].n
[1]);
3226 LUT8
.e12
= TransportValue32(NewLUT
-> Matrix
.v
[1].n
[2]);
3227 LUT8
.e20
= TransportValue32(NewLUT
-> Matrix
.v
[2].n
[0]);
3228 LUT8
.e21
= TransportValue32(NewLUT
-> Matrix
.v
[2].n
[1]);
3229 LUT8
.e22
= TransportValue32(NewLUT
-> Matrix
.v
[2].n
[2]);
3233 LUT8
.e00
= TransportValue32(DOUBLE_TO_FIXED(1));
3234 LUT8
.e01
= TransportValue32(DOUBLE_TO_FIXED(0));
3235 LUT8
.e02
= TransportValue32(DOUBLE_TO_FIXED(0));
3236 LUT8
.e10
= TransportValue32(DOUBLE_TO_FIXED(0));
3237 LUT8
.e11
= TransportValue32(DOUBLE_TO_FIXED(1));
3238 LUT8
.e12
= TransportValue32(DOUBLE_TO_FIXED(0));
3239 LUT8
.e20
= TransportValue32(DOUBLE_TO_FIXED(0));
3240 LUT8
.e21
= TransportValue32(DOUBLE_TO_FIXED(0));
3241 LUT8
.e22
= TransportValue32(DOUBLE_TO_FIXED(1));
3247 Icc
-> Write(Icc
, sizeof(icLut8
)- SIZEOF_UINT8_ALIGNED
, &LUT8
);
3249 // The prelinearization table
3251 for (i
=0; i
< NewLUT
-> InputChan
; i
++) {
3253 for (j
=0; j
< 256; j
++) {
3255 if (NewLUT
-> wFlags
& LUT_HASTL1
)
3256 val
= (BYTE
) floor(NewLUT
->L1
[i
][j
] / 257.0 + .5);
3260 Icc
->Write(Icc
, 1, &val
);
3266 nTabSize
= (NewLUT
-> OutputChan
* uipow(NewLUT
->cLutPoints
,
3267 NewLUT
->InputChan
));
3270 for (j
=0; j
< nTabSize
; j
++) {
3272 val
= (BYTE
) floor(NewLUT
->T
[j
] / 257.0 + .5);
3273 Icc
->Write(Icc
, 1, &val
);
3276 // The postlinearization table
3278 for (i
=0; i
< NewLUT
-> OutputChan
; i
++) {
3280 for (j
=0; j
< 256; j
++) {
3282 if (NewLUT
-> wFlags
& LUT_HASTL2
)
3283 val
= (BYTE
) floor(NewLUT
->L2
[i
][j
] / 257.0 + .5);
3287 Icc
->Write(Icc
, 1, &val
);
3297 // Set the LUT bitdepth to be saved
3299 void LCMSEXPORT
_cmsSetLUTdepth(cmsHPROFILE hProfile
, int depth
)
3301 LPLCMSICCPROFILE Icc
= (LPLCMSICCPROFILE
) (LPSTR
) hProfile
;
3305 case 8: Icc
->SaveAs8Bits
= TRUE
; break;
3306 case 16: Icc
->SaveAs8Bits
= FALSE
; break;
3309 cmsSignalError(LCMS_ERRC_ABORTED
, "%d is an unsupported as bitdepth, use 8 or 16 only.", depth
);
3314 // Saves Tag directory
3317 LCMSBOOL
SaveTagDirectory(LPLCMSICCPROFILE Icc
)
3321 icInt32Number Count
= 0;
3324 for (i
=0; i
< Icc
-> TagCount
; i
++) {
3325 if (Icc
->TagNames
[i
] != 0)
3329 Count
= TransportValue32(Count
);
3330 if (!Icc
->Write(Icc
, sizeof(icInt32Number
) , &Count
)) return FALSE
;
3332 for (i
=0; i
< Icc
-> TagCount
; i
++) {
3334 if (Icc
->TagNames
[i
] == 0) continue;
3336 Tag
.sig
= (icTagSignature
)TransportValue32(Icc
-> TagNames
[i
]);
3337 Tag
.offset
= TransportValue32((icInt32Number
) Icc
-> TagOffsets
[i
]);
3338 Tag
.size
= TransportValue32((icInt32Number
) Icc
-> TagSizes
[i
]);
3340 if (!Icc
->Write(Icc
, sizeof(icTag
), &Tag
)) return FALSE
;
3347 // Dump tag contents
3350 LCMSBOOL
SaveTags(LPLCMSICCPROFILE Icc
, LPLCMSICCPROFILE FileOrig
)
3356 size_t AlignedSpace
, FillerSize
;
3359 for (i
=0; i
< Icc
-> TagCount
; i
++) {
3361 if (Icc
->TagNames
[i
] == 0) continue;
3363 // Align to DWORD boundary, following new spec.
3365 AlignedSpace
= ALIGNLONG(Icc
->UsedSpace
);
3366 FillerSize
= AlignedSpace
- Icc
->UsedSpace
;
3367 if (FillerSize
> 0) {
3371 ZeroMemory(Filler
, 16);
3372 if (!Icc
->Write(Icc
, FillerSize
, Filler
)) return FALSE
;
3376 Icc
-> TagOffsets
[i
] = Begin
= Icc
->UsedSpace
;
3377 Data
= (LPBYTE
) Icc
-> TagPtrs
[i
];
3380 // Reach here if we are copying a tag from a disk-based ICC profile which has not been modified by user.
3381 // In this case a blind copy of the block data is performed
3383 if (Icc
-> TagOffsets
[i
]) {
3385 size_t TagSize
= FileOrig
-> TagSizes
[i
];
3386 size_t TagOffset
= FileOrig
-> TagOffsets
[i
];
3389 if (FileOrig
->Seek(FileOrig
, TagOffset
)) return FALSE
;
3391 Mem
= _cmsMalloc(TagSize
);
3393 if (FileOrig
->Read(Mem
, TagSize
, 1, FileOrig
) != 1) return FALSE
;
3394 if (!Icc
->Write(Icc
, TagSize
, Mem
)) return FALSE
;
3396 Icc
-> TagSizes
[i
] = (Icc
->UsedSpace
- Begin
);
3404 switch (Icc
-> TagNames
[i
]) {
3406 case icSigProfileDescriptionTag
:
3407 case icSigDeviceMfgDescTag
:
3408 case icSigDeviceModelDescTag
:
3409 if (!SaveDescription((const char *) Data
, Icc
)) return FALSE
;
3412 case icSigRedColorantTag
:
3413 case icSigGreenColorantTag
:
3414 case icSigBlueColorantTag
:
3415 case icSigMediaWhitePointTag
:
3416 case icSigMediaBlackPointTag
:
3417 if (!SaveXYZNumber((LPcmsCIEXYZ
) Data
, Icc
)) return FALSE
;
3421 case icSigRedTRCTag
:
3422 case icSigGreenTRCTag
:
3423 case icSigBlueTRCTag
:
3424 case icSigGrayTRCTag
:
3425 if (!SaveGamma((LPGAMMATABLE
) Data
, Icc
)) return FALSE
;
3428 case icSigCharTargetTag
:
3429 case icSigCopyrightTag
:
3430 if (!SaveText((const char *) Data
, Icc
)) return FALSE
;
3433 case icSigChromaticityTag
:
3434 if (!SaveChromaticities((LPcmsCIExyYTRIPLE
) Data
, Icc
)) return FALSE
;
3446 case icSigPreview0Tag
:
3447 case icSigPreview1Tag
:
3448 case icSigPreview2Tag
:
3450 if (Icc
->SaveAs8Bits
) {
3452 if (!SaveLUT8((LPLUT
) Data
, Icc
)) return FALSE
;
3456 if (!SaveLUT((LPLUT
) Data
, Icc
)) return FALSE
;
3460 case icSigProfileSequenceDescTag
:
3461 if (!SaveSequenceDescriptionTag((LPcmsSEQ
) Data
, Icc
)) return FALSE
;
3465 case icSigNamedColor2Tag
:
3466 if (!SaveNamedColorList((LPcmsNAMEDCOLORLIST
) Data
, Icc
)) return FALSE
;
3470 case icSigCalibrationDateTimeTag
:
3471 if (!SaveDateTimeNumber((struct tm
*) Data
, Icc
)) return FALSE
;
3475 case icSigColorantTableTag
:
3476 case icSigColorantTableOutTag
:
3477 if (!SaveColorantTable((LPcmsNAMEDCOLORLIST
) Data
, Icc
)) return FALSE
;
3481 case icSigChromaticAdaptationTag
:
3482 if (!SaveXYZArray(3, (LPcmsCIEXYZ
) Data
, Icc
)) return FALSE
;
3489 Icc
-> TagSizes
[i
] = (Icc
->UsedSpace
- Begin
);
3499 // Add tags to profile structure
3501 LCMSBOOL LCMSEXPORT
cmsAddTag(cmsHPROFILE hProfile
, icTagSignature sig
, const void* Tag
)
3507 case icSigCharTargetTag
:
3508 case icSigCopyrightTag
:
3509 case icSigProfileDescriptionTag
:
3510 case icSigDeviceMfgDescTag
:
3511 case icSigDeviceModelDescTag
:
3512 rc
= _cmsAddTextTag(hProfile
, sig
, (const char*) Tag
);
3515 case icSigRedColorantTag
:
3516 case icSigGreenColorantTag
:
3517 case icSigBlueColorantTag
:
3518 case icSigMediaWhitePointTag
:
3519 case icSigMediaBlackPointTag
:
3520 rc
= _cmsAddXYZTag(hProfile
, sig
, (const cmsCIEXYZ
*) Tag
);
3523 case icSigRedTRCTag
:
3524 case icSigGreenTRCTag
:
3525 case icSigBlueTRCTag
:
3526 case icSigGrayTRCTag
:
3527 rc
= _cmsAddGammaTag(hProfile
, sig
, (LPGAMMATABLE
) Tag
);
3537 case icSigPreview0Tag
:
3538 case icSigPreview1Tag
:
3539 case icSigPreview2Tag
:
3540 rc
= _cmsAddLUTTag(hProfile
, sig
, Tag
);
3543 case icSigChromaticityTag
:
3544 rc
= _cmsAddChromaticityTag(hProfile
, sig
, (LPcmsCIExyYTRIPLE
) Tag
);
3547 case icSigProfileSequenceDescTag
:
3548 rc
= _cmsAddSequenceDescriptionTag(hProfile
, sig
, (LPcmsSEQ
) Tag
);
3551 case icSigNamedColor2Tag
:
3552 rc
= _cmsAddNamedColorTag(hProfile
, sig
, (LPcmsNAMEDCOLORLIST
) Tag
);
3555 case icSigCalibrationDateTimeTag
:
3556 rc
= _cmsAddDateTimeTag(hProfile
, sig
, (struct tm
*) Tag
);
3559 case icSigColorantTableTag
:
3560 case icSigColorantTableOutTag
:
3561 rc
= _cmsAddColorantTableTag(hProfile
, sig
, (LPcmsNAMEDCOLORLIST
) Tag
);
3565 case icSigChromaticAdaptationTag
:
3566 rc
= _cmsAddChromaticAdaptationTag(hProfile
, sig
, (const cmsCIEXYZ
*) Tag
);
3570 cmsSignalError(LCMS_ERRC_ABORTED
, "cmsAddTag: Tag '%x' is unsupported", sig
);
3574 // Check for critical tags
3578 case icSigMediaWhitePointTag
:
3579 case icSigMediaBlackPointTag
:
3580 case icSigChromaticAdaptationTag
:
3582 ReadCriticalTags((LPLCMSICCPROFILE
) hProfile
);
3592 // Low-level save to disk. It closes the profile on exit
3594 LCMSBOOL LCMSEXPORT
_cmsSaveProfile(cmsHPROFILE hProfile
, const char* FileName
)
3596 LPLCMSICCPROFILE Icc
= (LPLCMSICCPROFILE
) (LPSTR
) hProfile
;
3597 LCMSICCPROFILE Keep
;
3600 CopyMemory(&Keep
, Icc
, sizeof(LCMSICCPROFILE
));
3601 _cmsSetSaveToDisk(Icc
, NULL
);
3603 // Pass #1 does compute offsets
3605 if (!SaveHeader(Icc
)) return FALSE
;
3606 if (!SaveTagDirectory(Icc
)) return FALSE
;
3607 if (!SaveTags(Icc
, &Keep
)) return FALSE
;
3610 _cmsSetSaveToDisk(Icc
, FileName
);
3613 // Pass #2 does save to file
3615 if (!SaveHeader(Icc
)) goto CleanUp
;
3616 if (!SaveTagDirectory(Icc
)) goto CleanUp
;
3617 if (!SaveTags(Icc
, &Keep
)) goto CleanUp
;
3619 rc
= (Icc
->Close(Icc
) == 0);
3620 CopyMemory(Icc
, &Keep
, sizeof(LCMSICCPROFILE
));
3628 CopyMemory(Icc
, &Keep
, sizeof(LCMSICCPROFILE
));
3633 // Low-level save from open stream
3634 LCMSBOOL LCMSEXPORT
_cmsSaveProfileToMem(cmsHPROFILE hProfile
, void *MemPtr
,
3635 size_t* BytesNeeded
)
3637 LPLCMSICCPROFILE Icc
= (LPLCMSICCPROFILE
) (LPSTR
) hProfile
;
3638 LCMSICCPROFILE Keep
;
3641 CopyMemory(&Keep
, Icc
, sizeof(LCMSICCPROFILE
));
3643 _cmsSetSaveToMemory(Icc
, NULL
, 0);
3645 // Pass #1 does compute offsets
3647 if (!SaveHeader(Icc
)) return FALSE
;
3648 if (!SaveTagDirectory(Icc
)) return FALSE
;
3649 if (!SaveTags(Icc
, &Keep
)) return FALSE
;
3653 // update BytesSaved so caller knows how many bytes are needed for MemPtr
3654 *BytesNeeded
= Icc
->UsedSpace
;
3655 CopyMemory(Icc
, &Keep
, sizeof(LCMSICCPROFILE
));
3659 if (*BytesNeeded
< Icc
->UsedSpace
) {
3661 // need at least UsedSpace in MemPtr to continue
3662 CopyMemory(Icc
, &Keep
, sizeof(LCMSICCPROFILE
));
3666 _cmsSetSaveToMemory(Icc
, MemPtr
, *BytesNeeded
);
3669 // Pass #2 does save to file into supplied stream
3670 if (!SaveHeader(Icc
)) goto CleanUp
;
3671 if (!SaveTagDirectory(Icc
)) goto CleanUp
;
3672 if (!SaveTags(Icc
, &Keep
)) goto CleanUp
;
3674 // update BytesSaved so caller knows how many bytes put into stream
3675 *BytesNeeded
= Icc
->UsedSpace
;
3678 CopyMemory(Icc
, &Keep
, sizeof(LCMSICCPROFILE
));
3684 CopyMemory(Icc
, &Keep
, sizeof(LCMSICCPROFILE
));