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
;
2528 LCMSBOOL LCMSEXPORT
cmsCloseProfile(cmsHPROFILE hProfile
)
2530 LPLCMSICCPROFILE Icc
= (LPLCMSICCPROFILE
) (LPSTR
) hProfile
;
2534 if (!Icc
) return FALSE
;
2536 // Was open in write mode?
2537 if (Icc
->IsWrite
) {
2539 Icc
->IsWrite
= FALSE
; // Assure no further writting
2540 rc
= _cmsSaveProfile(hProfile
, Icc
->PhysicalFile
);
2543 for (i
=0; i
< Icc
-> TagCount
; i
++) {
2545 if (Icc
-> TagPtrs
[i
])
2546 free(Icc
-> TagPtrs
[i
]);
2549 if (Icc
-> stream
!= NULL
) { // Was a memory (i.e. not serialized) profile?
2550 Icc
-> Close(Icc
); // No, close the stream
2553 free(Icc
); // Free placeholder memory
2560 // Write profile ------------------------------------------------------------
2565 LCMSBOOL
SaveWordsTable(int nEntries
, LPWORD Tab
, LPLCMSICCPROFILE Icc
)
2567 size_t nTabSize
= sizeof(WORD
) * nEntries
;
2568 LPWORD PtrW
= (LPWORD
) _cmsMalloc(nTabSize
);
2571 if (!PtrW
) return FALSE
;
2572 CopyMemory(PtrW
, Tab
, nTabSize
);
2573 AdjustEndianessArray16(PtrW
, nEntries
);
2574 rc
= Icc
->Write(Icc
, nTabSize
, PtrW
);
2582 // Saves profile header
2585 LCMSBOOL
SaveHeader(LPLCMSICCPROFILE Icc
)
2588 time_t now
= time(NULL
);
2590 Header
.size
= TransportValue32((icInt32Number
) Icc
->UsedSpace
);
2591 Header
.cmmId
= TransportValue32(lcmsSignature
);
2592 Header
.version
= TransportValue32((icInt32Number
) 0x02300000);
2593 Header
.deviceClass
= (icProfileClassSignature
) TransportValue32(Icc
-> DeviceClass
);
2594 Header
.colorSpace
= (icColorSpaceSignature
) TransportValue32(Icc
-> ColorSpace
);
2595 Header
.pcs
= (icColorSpaceSignature
) TransportValue32(Icc
-> PCS
);
2597 // NOTE: in v4 Timestamp must be in UTC rather than in local time
2598 EncodeDateTimeNumber(&Header
.date
, gmtime(&now
));
2600 Header
.magic
= TransportValue32(icMagicNumber
);
2603 Header
.platform
= (icPlatformSignature
)TransportValue32(icSigMacintosh
);
2605 Header
.platform
= (icPlatformSignature
)TransportValue32(icSigMicrosoft
);
2608 Header
.flags
= TransportValue32(Icc
-> flags
);
2609 Header
.manufacturer
= TransportValue32(lcmsSignature
);
2610 Header
.model
= TransportValue32(0);
2611 Header
.attributes
[0]= TransportValue32(Icc
-> attributes
);
2612 Header
.attributes
[1]= TransportValue32(0);
2614 Header
.renderingIntent
= TransportValue32(Icc
-> RenderingIntent
);
2616 // Illuminant is D50
2618 Header
.illuminant
.X
= TransportValue32(DOUBLE_TO_FIXED(Icc
-> Illuminant
.X
));
2619 Header
.illuminant
.Y
= TransportValue32(DOUBLE_TO_FIXED(Icc
-> Illuminant
.Y
));
2620 Header
.illuminant
.Z
= TransportValue32(DOUBLE_TO_FIXED(Icc
-> Illuminant
.Z
));
2622 Header
.creator
= TransportValue32(lcmsSignature
);
2624 ZeroMemory(&Header
.reserved
, sizeof(Header
.reserved
));
2627 CopyMemory(Header
.reserved
, Icc
->ProfileID
, 16);
2630 Icc
->UsedSpace
= 0; // Mark as begin-of-file
2632 return Icc
->Write(Icc
, sizeof(icHeader
), &Header
);
2637 // Setup base marker
2640 LCMSBOOL
SetupBase(icTagTypeSignature sig
, LPLCMSICCPROFILE Icc
)
2644 Base
.sig
= (icTagTypeSignature
) TransportValue32(sig
);
2645 ZeroMemory(&Base
.reserved
, sizeof(Base
.reserved
));
2646 return Icc
-> Write(Icc
, sizeof(icTagBase
), &Base
);
2653 LCMSBOOL
SaveXYZNumber(LPcmsCIEXYZ Value
, LPLCMSICCPROFILE Icc
)
2658 if (!SetupBase(icSigXYZType
, Icc
)) return FALSE
;
2660 XYZ
.X
= TransportValue32(DOUBLE_TO_FIXED(Value
-> X
));
2661 XYZ
.Y
= TransportValue32(DOUBLE_TO_FIXED(Value
-> Y
));
2662 XYZ
.Z
= TransportValue32(DOUBLE_TO_FIXED(Value
-> Z
));
2665 return Icc
-> Write(Icc
, sizeof(icXYZNumber
), &XYZ
);
2669 // Store a XYZ array.
2672 LCMSBOOL
SaveXYZArray(int n
, LPcmsCIEXYZ Value
, LPLCMSICCPROFILE Icc
)
2677 if (!SetupBase(icSigS15Fixed16ArrayType
, Icc
)) return FALSE
;
2679 for (i
=0; i
< n
; i
++) {
2681 XYZ
.X
= TransportValue32(DOUBLE_TO_FIXED(Value
-> X
));
2682 XYZ
.Y
= TransportValue32(DOUBLE_TO_FIXED(Value
-> Y
));
2683 XYZ
.Z
= TransportValue32(DOUBLE_TO_FIXED(Value
-> Z
));
2685 if (!Icc
-> Write(Icc
, sizeof(icXYZNumber
), &XYZ
)) return FALSE
;
2695 // Save a gamma structure as a table
2698 LCMSBOOL
SaveGammaTable(LPGAMMATABLE Gamma
, LPLCMSICCPROFILE Icc
)
2700 icInt32Number Count
;
2702 if (!SetupBase(icSigCurveType
, Icc
)) return FALSE
;
2704 Count
= TransportValue32(Gamma
->nEntries
);
2706 if (!Icc
->Write(Icc
, sizeof(icInt32Number
), &Count
)) return FALSE
;
2708 return SaveWordsTable(Gamma
->nEntries
, Gamma
->GammaTable
, Icc
);
2712 // Save a gamma structure as a one-value
2715 LCMSBOOL
SaveGammaOneValue(LPGAMMATABLE Gamma
, LPLCMSICCPROFILE Icc
)
2717 icInt32Number Count
;
2718 Fixed32 GammaFixed32
;
2721 if (!SetupBase(icSigCurveType
, Icc
)) return FALSE
;
2723 Count
= TransportValue32(1);
2724 if (!Icc
->Write(Icc
, sizeof(icInt32Number
), &Count
)) return FALSE
;
2726 GammaFixed32
= DOUBLE_TO_FIXED(Gamma
->Seed
.Params
[0]);
2727 GammaFixed8
= (WORD
) ((GammaFixed32
>> 8) & 0xFFFF);
2728 GammaFixed8
= TransportValue16(GammaFixed8
);
2730 return Icc
->Write(Icc
, sizeof(icInt16Number
), &GammaFixed8
);
2733 // Save a gamma structure as a parametric gamma
2736 LCMSBOOL
SaveGammaParametric(LPGAMMATABLE Gamma
, LPLCMSICCPROFILE Icc
)
2738 icUInt16Number Type
, Reserved
;
2740 int ParamsByType
[] = { 1, 3, 4, 5, 7 };
2742 if (!SetupBase(icSigParametricCurveType
, Icc
)) return FALSE
;
2744 nParams
= ParamsByType
[Gamma
-> Seed
.Type
];
2746 Type
= (icUInt16Number
) TransportValue16((WORD
) Gamma
-> Seed
. Type
);
2747 Reserved
= (icUInt16Number
) TransportValue16((WORD
) 0);
2749 Icc
-> Write(Icc
, sizeof(icInt16Number
), &Type
);
2750 Icc
-> Write(Icc
, sizeof(icUInt16Number
), &Reserved
);
2752 for (i
=0; i
< nParams
; i
++) {
2754 icInt32Number val
= TransportValue32(DOUBLE_TO_FIXED(Gamma
-> Seed
.Params
[i
]));
2755 Icc
->Write(Icc
, sizeof(icInt32Number
), &val
);
2764 // Save a gamma table
2767 LCMSBOOL
SaveGamma(LPGAMMATABLE Gamma
, LPLCMSICCPROFILE Icc
)
2769 // Is the gamma curve type supported by ICC format?
2771 if (Gamma
-> Seed
.Type
< 0 || Gamma
-> Seed
.Type
> 5 ||
2773 // has been modified by user?
2775 _cmsCrc32OfGammaTable(Gamma
) != Gamma
-> Seed
.Crc32
) {
2777 return SaveGammaTable(Gamma
, Icc
);
2780 if (Gamma
-> Seed
.Type
== 1) return SaveGammaOneValue(Gamma
, Icc
);
2782 // Only v4 profiles are allowed to hold parametric curves
2784 if (cmsGetProfileICCversion((cmsHPROFILE
) Icc
) >= 0x4000000)
2785 return SaveGammaParametric(Gamma
, Icc
);
2787 // Defaults to save as table
2789 return SaveGammaTable(Gamma
, Icc
);
2799 LCMSBOOL
SaveDescription(const char *Text
, LPLCMSICCPROFILE Icc
)
2802 icUInt32Number len
, Count
, TotalSize
, AlignedSize
;
2805 len
= (icUInt32Number
) (strlen(Text
) + 1);
2807 // * icInt8Number desc[count] * NULL terminated ascii string
2808 // * icUInt32Number ucLangCode; * UniCode language code
2809 // * icUInt32Number ucCount; * UniCode description length
2810 // * icInt16Number ucDesc[ucCount];* The UniCode description
2811 // * icUInt16Number scCode; * ScriptCode code
2812 // * icUInt8Number scCount; * ScriptCode count
2813 // * icInt8Number scDesc[67]; * ScriptCode Description
2815 TotalSize
= sizeof(icTagBase
) + sizeof(icUInt32Number
) + len
+
2816 sizeof(icUInt32Number
) + sizeof(icUInt32Number
) +
2817 sizeof(icUInt16Number
) + sizeof(icUInt8Number
) + 67;
2819 AlignedSize
= TotalSize
; // Can be unaligned!!
2821 if (!SetupBase(icSigTextDescriptionType
, Icc
)) return FALSE
;
2822 AlignedSize
-= sizeof(icTagBase
);
2824 Count
= TransportValue32(len
);
2825 if (!Icc
->Write(Icc
, sizeof(icUInt32Number
), &Count
)) return FALSE
;
2826 AlignedSize
-= sizeof(icUInt32Number
);
2828 if (!Icc
->Write(Icc
, len
, (LPVOID
)Text
)) return FALSE
;
2831 if (AlignedSize
< 0)
2833 if (AlignedSize
> 255)
2836 ZeroMemory(Filler
, AlignedSize
);
2837 if (!Icc
->Write(Icc
, AlignedSize
, Filler
)) return FALSE
;
2842 // Save an ASCII Tag
2845 LCMSBOOL
SaveText(const char *Text
, LPLCMSICCPROFILE Icc
)
2847 size_t len
= strlen(Text
) + 1;
2849 if (!SetupBase(icSigTextType
, Icc
)) return FALSE
;
2850 if (!Icc
->Write(Icc
, len
, (LPVOID
) Text
)) return FALSE
;
2855 // Save one of these new chromaticity values
2858 LCMSBOOL
SaveOneChromaticity(double x
, double y
, LPLCMSICCPROFILE Icc
)
2862 xf
= TransportValue32(DOUBLE_TO_FIXED(x
));
2863 yf
= TransportValue32(DOUBLE_TO_FIXED(y
));
2865 if (!Icc
->Write(Icc
, sizeof(Fixed32
), &xf
)) return FALSE
;
2866 if (!Icc
->Write(Icc
, sizeof(Fixed32
), &yf
)) return FALSE
;
2872 // New tag added in Addendum II of old spec.
2875 LCMSBOOL
SaveChromaticities(LPcmsCIExyYTRIPLE chrm
, LPLCMSICCPROFILE Icc
)
2879 if (!SetupBase(icSigChromaticityType
, Icc
)) return FALSE
;
2881 nChans
= TransportValue16(3);
2882 if (!Icc
->Write(Icc
, sizeof(WORD
) , &nChans
)) return FALSE
;
2883 Table
= TransportValue16(0);
2884 if (!Icc
->Write(Icc
, sizeof(WORD
) , &Table
)) return FALSE
;
2886 if (!SaveOneChromaticity(chrm
-> Red
.x
, chrm
-> Red
.y
, Icc
)) return FALSE
;
2887 if (!SaveOneChromaticity(chrm
-> Green
.x
, chrm
-> Green
.y
, Icc
)) return FALSE
;
2888 if (!SaveOneChromaticity(chrm
-> Blue
.x
, chrm
-> Blue
.y
, Icc
)) return FALSE
;
2895 LCMSBOOL
SaveSequenceDescriptionTag(LPcmsSEQ seq
, LPLCMSICCPROFILE Icc
)
2897 icUInt32Number nSeqs
;
2898 icDescStruct DescStruct
;
2900 LPcmsPSEQDESC pseq
= seq
->seq
;
2902 if (!SetupBase(icSigProfileSequenceDescType
, Icc
)) return FALSE
;
2904 nSeqs
= TransportValue32(n
);
2906 if (!Icc
->Write(Icc
, sizeof(icUInt32Number
) , &nSeqs
)) return FALSE
;
2908 for (i
=0; i
< n
; i
++) {
2910 LPcmsPSEQDESC sec
= pseq
+ i
;
2913 DescStruct
.deviceMfg
= (icTagTypeSignature
) TransportValue32(sec
->deviceMfg
);
2914 DescStruct
.deviceModel
= (icTagTypeSignature
) TransportValue32(sec
->deviceModel
);
2915 DescStruct
.technology
= (icTechnologySignature
) TransportValue32(sec
->technology
);
2916 DescStruct
.attributes
[0]= TransportValue32(sec
->attributes
[0]);
2917 DescStruct
.attributes
[1]= TransportValue32(sec
->attributes
[1]);
2919 if (!Icc
->Write(Icc
, sizeof(icDescStruct
) - SIZEOF_UINT8_ALIGNED
, &DescStruct
)) return FALSE
;
2921 if (!SaveDescription(sec
->Manufacturer
, Icc
)) return FALSE
;
2922 if (!SaveDescription(sec
->Model
, Icc
)) return FALSE
;
2929 // Saves a timestamp tag
2932 LCMSBOOL
SaveDateTimeNumber(const struct tm
*DateTime
, LPLCMSICCPROFILE Icc
)
2934 icDateTimeNumber Dest
;
2936 if (!SetupBase(icSigDateTimeType
, Icc
)) return FALSE
;
2937 EncodeDateTimeNumber(&Dest
, DateTime
);
2938 if (!Icc
->Write(Icc
, sizeof(icDateTimeNumber
), &Dest
)) return FALSE
;
2944 // Saves a named color list into a named color profile
2946 LCMSBOOL
SaveNamedColorList(LPcmsNAMEDCOLORLIST NamedColorList
, LPLCMSICCPROFILE Icc
)
2949 icUInt32Number vendorFlag
; // Bottom 16 bits for IC use
2950 icUInt32Number count
; // Count of named colors
2951 icUInt32Number nDeviceCoords
; // Num of device coordinates
2952 char prefix
[32]; // Prefix for each color name
2953 char suffix
[32]; // Suffix for each color name
2956 if (!SetupBase(icSigNamedColor2Type
, Icc
)) return FALSE
;
2958 vendorFlag
= TransportValue32(0);
2959 count
= TransportValue32(NamedColorList
->nColors
);
2960 nDeviceCoords
= TransportValue32(NamedColorList
->ColorantCount
);
2962 strncpy(prefix
, (const char*) NamedColorList
->Prefix
, 32);
2963 strncpy(suffix
, (const char*) NamedColorList
->Suffix
, 32);
2965 suffix
[31] = prefix
[31] = 0;
2967 if (!Icc
->Write(Icc
, sizeof(icUInt32Number
), &vendorFlag
)) return FALSE
;
2968 if (!Icc
->Write(Icc
, sizeof(icUInt32Number
), &count
)) return FALSE
;
2969 if (!Icc
->Write(Icc
, sizeof(icUInt32Number
), &nDeviceCoords
)) return FALSE
;
2970 if (!Icc
->Write(Icc
, 32 , prefix
)) return FALSE
;
2971 if (!Icc
->Write(Icc
, 32 , suffix
)) return FALSE
;
2973 for (i
=0; i
< NamedColorList
->nColors
; i
++) {
2975 icUInt16Number PCS
[3];
2976 icUInt16Number Colorant
[MAXCHANNELS
];
2978 LPcmsNAMEDCOLOR Color
;
2981 Color
= NamedColorList
->List
+ i
;
2983 strncpy(root
, Color
->Name
, 32);
2984 Color
->Name
[32] = 0;
2986 if (!Icc
->Write(Icc
, 32 , root
)) return FALSE
;
2988 for (j
=0; j
< 3; j
++)
2989 PCS
[j
] = TransportValue16(Color
->PCS
[j
]);
2991 if (!Icc
->Write(Icc
, 3 * sizeof(icUInt16Number
), PCS
)) return FALSE
;
2993 for (j
=0; j
< NamedColorList
->ColorantCount
; j
++)
2994 Colorant
[j
] = TransportValue16(Color
->DeviceColorant
[j
]);
2996 if (!Icc
->Write(Icc
,
2997 NamedColorList
->ColorantCount
* sizeof(icUInt16Number
), Colorant
)) return FALSE
;
3006 // Saves a colorant table. It is using the named color structure for simplicity sake
3009 LCMSBOOL
SaveColorantTable(LPcmsNAMEDCOLORLIST NamedColorList
, LPLCMSICCPROFILE Icc
)
3011 icUInt32Number count
; // Count of named colors
3014 if (!SetupBase(icSigColorantTableType
, Icc
)) return FALSE
;
3016 count
= TransportValue32(NamedColorList
->nColors
);
3018 if (!Icc
->Write(Icc
, sizeof(icUInt32Number
), &count
)) return FALSE
;
3020 for (i
=0; i
< NamedColorList
->nColors
; i
++) {
3022 icUInt16Number PCS
[3];
3023 icInt8Number root
[33];
3024 LPcmsNAMEDCOLOR Color
;
3027 Color
= NamedColorList
->List
+ i
;
3029 strncpy((char*) root
, Color
->Name
, 32);
3032 if (!Icc
->Write(Icc
, 32 , root
)) return FALSE
;
3034 for (j
=0; j
< 3; j
++)
3035 PCS
[j
] = TransportValue16(Color
->PCS
[j
]);
3037 if (!Icc
->Write(Icc
, 3 * sizeof(icUInt16Number
), PCS
)) return FALSE
;
3045 // Does serialization of LUT16 and writes it.
3048 LCMSBOOL
SaveLUT(const LUT
* NewLUT
, LPLCMSICCPROFILE Icc
)
3053 WORD NullTbl
[2] = { 0, 0xFFFFU
};
3056 if (!SetupBase(icSigLut16Type
, Icc
)) return FALSE
;
3058 LUT16
.clutPoints
= (icUInt8Number
) NewLUT
-> cLutPoints
;
3059 LUT16
.inputChan
= (icUInt8Number
) NewLUT
-> InputChan
;
3060 LUT16
.outputChan
= (icUInt8Number
) NewLUT
-> OutputChan
;
3062 LUT16
.inputEnt
= TransportValue16((WORD
) ((NewLUT
-> wFlags
& LUT_HASTL1
) ? NewLUT
-> InputEntries
: 2));
3063 LUT16
.outputEnt
= TransportValue16((WORD
) ((NewLUT
-> wFlags
& LUT_HASTL2
) ? NewLUT
-> OutputEntries
: 2));
3065 if (NewLUT
-> wFlags
& LUT_HASMATRIX
) {
3067 LUT16
.e00
= TransportValue32(NewLUT
-> Matrix
.v
[0].n
[0]);
3068 LUT16
.e01
= TransportValue32(NewLUT
-> Matrix
.v
[0].n
[1]);
3069 LUT16
.e02
= TransportValue32(NewLUT
-> Matrix
.v
[0].n
[2]);
3070 LUT16
.e10
= TransportValue32(NewLUT
-> Matrix
.v
[1].n
[0]);
3071 LUT16
.e11
= TransportValue32(NewLUT
-> Matrix
.v
[1].n
[1]);
3072 LUT16
.e12
= TransportValue32(NewLUT
-> Matrix
.v
[1].n
[2]);
3073 LUT16
.e20
= TransportValue32(NewLUT
-> Matrix
.v
[2].n
[0]);
3074 LUT16
.e21
= TransportValue32(NewLUT
-> Matrix
.v
[2].n
[1]);
3075 LUT16
.e22
= TransportValue32(NewLUT
-> Matrix
.v
[2].n
[2]);
3079 LUT16
.e00
= TransportValue32(DOUBLE_TO_FIXED(1));
3080 LUT16
.e01
= TransportValue32(DOUBLE_TO_FIXED(0));
3081 LUT16
.e02
= TransportValue32(DOUBLE_TO_FIXED(0));
3082 LUT16
.e10
= TransportValue32(DOUBLE_TO_FIXED(0));
3083 LUT16
.e11
= TransportValue32(DOUBLE_TO_FIXED(1));
3084 LUT16
.e12
= TransportValue32(DOUBLE_TO_FIXED(0));
3085 LUT16
.e20
= TransportValue32(DOUBLE_TO_FIXED(0));
3086 LUT16
.e21
= TransportValue32(DOUBLE_TO_FIXED(0));
3087 LUT16
.e22
= TransportValue32(DOUBLE_TO_FIXED(1));
3093 Icc
-> Write(Icc
, sizeof(icLut16
)- SIZEOF_UINT16_ALIGNED
, &LUT16
);
3095 // The prelinearization table
3097 for (i
=0; i
< NewLUT
-> InputChan
; i
++) {
3099 if (NewLUT
-> wFlags
& LUT_HASTL1
) {
3101 if (!SaveWordsTable(NewLUT
-> InputEntries
,
3102 NewLUT
-> L1
[i
], Icc
)) return FALSE
;
3105 else Icc
-> Write(Icc
, sizeof(WORD
)* 2, NullTbl
);
3109 nTabSize
= (NewLUT
-> OutputChan
* uipow(NewLUT
->cLutPoints
,
3110 NewLUT
->InputChan
));
3115 if (!SaveWordsTable((int) nTabSize
, NewLUT
-> T
, Icc
)) return FALSE
;
3117 // The postlinearization table
3119 for (i
=0; i
< NewLUT
-> OutputChan
; i
++) {
3121 if (NewLUT
-> wFlags
& LUT_HASTL2
) {
3123 if (!SaveWordsTable(NewLUT
-> OutputEntries
,
3124 NewLUT
-> L2
[i
], Icc
)) return FALSE
;
3126 else Icc
-> Write(Icc
, sizeof(WORD
)* 2, NullTbl
);
3135 // Does serialization of LUT8 and writes it
3138 LCMSBOOL
SaveLUT8(const LUT
* NewLUT
, LPLCMSICCPROFILE Icc
)
3147 if (NewLUT
-> wFlags
& LUT_HASTL1
) {
3149 if (NewLUT
-> InputEntries
!= 256) {
3150 cmsSignalError(LCMS_ERRC_ABORTED
, "LUT8 needs 256 entries on prelinearization");
3157 if (NewLUT
-> wFlags
& LUT_HASTL2
) {
3159 if (NewLUT
-> OutputEntries
!= 256) {
3160 cmsSignalError(LCMS_ERRC_ABORTED
, "LUT8 needs 256 entries on postlinearization");
3167 if (!SetupBase(icSigLut8Type
, Icc
)) return FALSE
;
3169 LUT8
.clutPoints
= (icUInt8Number
) NewLUT
-> cLutPoints
;
3170 LUT8
.inputChan
= (icUInt8Number
) NewLUT
-> InputChan
;
3171 LUT8
.outputChan
= (icUInt8Number
) NewLUT
-> OutputChan
;
3174 if (NewLUT
-> wFlags
& LUT_HASMATRIX
) {
3176 LUT8
.e00
= TransportValue32(NewLUT
-> Matrix
.v
[0].n
[0]);
3177 LUT8
.e01
= TransportValue32(NewLUT
-> Matrix
.v
[0].n
[1]);
3178 LUT8
.e02
= TransportValue32(NewLUT
-> Matrix
.v
[0].n
[2]);
3179 LUT8
.e10
= TransportValue32(NewLUT
-> Matrix
.v
[1].n
[0]);
3180 LUT8
.e11
= TransportValue32(NewLUT
-> Matrix
.v
[1].n
[1]);
3181 LUT8
.e12
= TransportValue32(NewLUT
-> Matrix
.v
[1].n
[2]);
3182 LUT8
.e20
= TransportValue32(NewLUT
-> Matrix
.v
[2].n
[0]);
3183 LUT8
.e21
= TransportValue32(NewLUT
-> Matrix
.v
[2].n
[1]);
3184 LUT8
.e22
= TransportValue32(NewLUT
-> Matrix
.v
[2].n
[2]);
3188 LUT8
.e00
= TransportValue32(DOUBLE_TO_FIXED(1));
3189 LUT8
.e01
= TransportValue32(DOUBLE_TO_FIXED(0));
3190 LUT8
.e02
= TransportValue32(DOUBLE_TO_FIXED(0));
3191 LUT8
.e10
= TransportValue32(DOUBLE_TO_FIXED(0));
3192 LUT8
.e11
= TransportValue32(DOUBLE_TO_FIXED(1));
3193 LUT8
.e12
= TransportValue32(DOUBLE_TO_FIXED(0));
3194 LUT8
.e20
= TransportValue32(DOUBLE_TO_FIXED(0));
3195 LUT8
.e21
= TransportValue32(DOUBLE_TO_FIXED(0));
3196 LUT8
.e22
= TransportValue32(DOUBLE_TO_FIXED(1));
3202 Icc
-> Write(Icc
, sizeof(icLut8
)- SIZEOF_UINT8_ALIGNED
, &LUT8
);
3204 // The prelinearization table
3206 for (i
=0; i
< NewLUT
-> InputChan
; i
++) {
3208 for (j
=0; j
< 256; j
++) {
3210 if (NewLUT
-> wFlags
& LUT_HASTL1
)
3211 val
= (BYTE
) floor(NewLUT
->L1
[i
][j
] / 257.0 + .5);
3215 Icc
->Write(Icc
, 1, &val
);
3221 nTabSize
= (NewLUT
-> OutputChan
* uipow(NewLUT
->cLutPoints
,
3222 NewLUT
->InputChan
));
3225 for (j
=0; j
< nTabSize
; j
++) {
3227 val
= (BYTE
) floor(NewLUT
->T
[j
] / 257.0 + .5);
3228 Icc
->Write(Icc
, 1, &val
);
3231 // The postlinearization table
3233 for (i
=0; i
< NewLUT
-> OutputChan
; i
++) {
3235 for (j
=0; j
< 256; j
++) {
3237 if (NewLUT
-> wFlags
& LUT_HASTL2
)
3238 val
= (BYTE
) floor(NewLUT
->L2
[i
][j
] / 257.0 + .5);
3242 Icc
->Write(Icc
, 1, &val
);
3252 // Set the LUT bitdepth to be saved
3254 void LCMSEXPORT
_cmsSetLUTdepth(cmsHPROFILE hProfile
, int depth
)
3256 LPLCMSICCPROFILE Icc
= (LPLCMSICCPROFILE
) (LPSTR
) hProfile
;
3260 case 8: Icc
->SaveAs8Bits
= TRUE
; break;
3261 case 16: Icc
->SaveAs8Bits
= FALSE
; break;
3264 cmsSignalError(LCMS_ERRC_ABORTED
, "%d is an unsupported as bitdepth, use 8 or 16 only.", depth
);
3269 // Saves Tag directory
3272 LCMSBOOL
SaveTagDirectory(LPLCMSICCPROFILE Icc
)
3276 icInt32Number Count
= 0;
3279 for (i
=0; i
< Icc
-> TagCount
; i
++) {
3280 if (Icc
->TagNames
[i
] != 0)
3284 Count
= TransportValue32(Count
);
3285 if (!Icc
->Write(Icc
, sizeof(icInt32Number
) , &Count
)) return FALSE
;
3287 for (i
=0; i
< Icc
-> TagCount
; i
++) {
3289 if (Icc
->TagNames
[i
] == 0) continue;
3291 Tag
.sig
= (icTagSignature
)TransportValue32(Icc
-> TagNames
[i
]);
3292 Tag
.offset
= TransportValue32((icInt32Number
) Icc
-> TagOffsets
[i
]);
3293 Tag
.size
= TransportValue32((icInt32Number
) Icc
-> TagSizes
[i
]);
3295 if (!Icc
->Write(Icc
, sizeof(icTag
), &Tag
)) return FALSE
;
3302 // Dump tag contents
3305 LCMSBOOL
SaveTags(LPLCMSICCPROFILE Icc
, LPLCMSICCPROFILE FileOrig
)
3311 size_t AlignedSpace
, FillerSize
;
3314 for (i
=0; i
< Icc
-> TagCount
; i
++) {
3316 if (Icc
->TagNames
[i
] == 0) continue;
3318 // Align to DWORD boundary, following new spec.
3320 AlignedSpace
= ALIGNLONG(Icc
->UsedSpace
);
3321 FillerSize
= AlignedSpace
- Icc
->UsedSpace
;
3322 if (FillerSize
> 0) {
3326 ZeroMemory(Filler
, 16);
3327 if (!Icc
->Write(Icc
, FillerSize
, Filler
)) return FALSE
;
3331 Icc
-> TagOffsets
[i
] = Begin
= Icc
->UsedSpace
;
3332 Data
= (LPBYTE
) Icc
-> TagPtrs
[i
];
3335 // Reach here if we are copying a tag from a disk-based ICC profile which has not been modified by user.
3336 // In this case a blind copy of the block data is performed
3338 if (Icc
-> TagOffsets
[i
]) {
3340 size_t TagSize
= FileOrig
-> TagSizes
[i
];
3341 size_t TagOffset
= FileOrig
-> TagOffsets
[i
];
3344 if (FileOrig
->Seek(FileOrig
, TagOffset
)) return FALSE
;
3346 Mem
= _cmsMalloc(TagSize
);
3348 if (FileOrig
->Read(Mem
, TagSize
, 1, FileOrig
) != 1) return FALSE
;
3349 if (!Icc
->Write(Icc
, TagSize
, Mem
)) return FALSE
;
3351 Icc
-> TagSizes
[i
] = (Icc
->UsedSpace
- Begin
);
3359 switch (Icc
-> TagNames
[i
]) {
3361 case icSigProfileDescriptionTag
:
3362 case icSigDeviceMfgDescTag
:
3363 case icSigDeviceModelDescTag
:
3364 if (!SaveDescription((const char *) Data
, Icc
)) return FALSE
;
3367 case icSigRedColorantTag
:
3368 case icSigGreenColorantTag
:
3369 case icSigBlueColorantTag
:
3370 case icSigMediaWhitePointTag
:
3371 case icSigMediaBlackPointTag
:
3372 if (!SaveXYZNumber((LPcmsCIEXYZ
) Data
, Icc
)) return FALSE
;
3376 case icSigRedTRCTag
:
3377 case icSigGreenTRCTag
:
3378 case icSigBlueTRCTag
:
3379 case icSigGrayTRCTag
:
3380 if (!SaveGamma((LPGAMMATABLE
) Data
, Icc
)) return FALSE
;
3383 case icSigCharTargetTag
:
3384 case icSigCopyrightTag
:
3385 if (!SaveText((const char *) Data
, Icc
)) return FALSE
;
3388 case icSigChromaticityTag
:
3389 if (!SaveChromaticities((LPcmsCIExyYTRIPLE
) Data
, Icc
)) return FALSE
;
3401 case icSigPreview0Tag
:
3402 case icSigPreview1Tag
:
3403 case icSigPreview2Tag
:
3405 if (Icc
->SaveAs8Bits
) {
3407 if (!SaveLUT8((LPLUT
) Data
, Icc
)) return FALSE
;
3411 if (!SaveLUT((LPLUT
) Data
, Icc
)) return FALSE
;
3415 case icSigProfileSequenceDescTag
:
3416 if (!SaveSequenceDescriptionTag((LPcmsSEQ
) Data
, Icc
)) return FALSE
;
3420 case icSigNamedColor2Tag
:
3421 if (!SaveNamedColorList((LPcmsNAMEDCOLORLIST
) Data
, Icc
)) return FALSE
;
3425 case icSigCalibrationDateTimeTag
:
3426 if (!SaveDateTimeNumber((struct tm
*) Data
, Icc
)) return FALSE
;
3430 case icSigColorantTableTag
:
3431 case icSigColorantTableOutTag
:
3432 if (!SaveColorantTable((LPcmsNAMEDCOLORLIST
) Data
, Icc
)) return FALSE
;
3436 case icSigChromaticAdaptationTag
:
3437 if (!SaveXYZArray(3, (LPcmsCIEXYZ
) Data
, Icc
)) return FALSE
;
3444 Icc
-> TagSizes
[i
] = (Icc
->UsedSpace
- Begin
);
3454 // Add tags to profile structure
3456 LCMSBOOL LCMSEXPORT
cmsAddTag(cmsHPROFILE hProfile
, icTagSignature sig
, const void* Tag
)
3462 case icSigCharTargetTag
:
3463 case icSigCopyrightTag
:
3464 case icSigProfileDescriptionTag
:
3465 case icSigDeviceMfgDescTag
:
3466 case icSigDeviceModelDescTag
:
3467 rc
= _cmsAddTextTag(hProfile
, sig
, (const char*) Tag
);
3470 case icSigRedColorantTag
:
3471 case icSigGreenColorantTag
:
3472 case icSigBlueColorantTag
:
3473 case icSigMediaWhitePointTag
:
3474 case icSigMediaBlackPointTag
:
3475 rc
= _cmsAddXYZTag(hProfile
, sig
, (const cmsCIEXYZ
*) Tag
);
3478 case icSigRedTRCTag
:
3479 case icSigGreenTRCTag
:
3480 case icSigBlueTRCTag
:
3481 case icSigGrayTRCTag
:
3482 rc
= _cmsAddGammaTag(hProfile
, sig
, (LPGAMMATABLE
) Tag
);
3492 case icSigPreview0Tag
:
3493 case icSigPreview1Tag
:
3494 case icSigPreview2Tag
:
3495 rc
= _cmsAddLUTTag(hProfile
, sig
, Tag
);
3498 case icSigChromaticityTag
:
3499 rc
= _cmsAddChromaticityTag(hProfile
, sig
, (LPcmsCIExyYTRIPLE
) Tag
);
3502 case icSigProfileSequenceDescTag
:
3503 rc
= _cmsAddSequenceDescriptionTag(hProfile
, sig
, (LPcmsSEQ
) Tag
);
3506 case icSigNamedColor2Tag
:
3507 rc
= _cmsAddNamedColorTag(hProfile
, sig
, (LPcmsNAMEDCOLORLIST
) Tag
);
3510 case icSigCalibrationDateTimeTag
:
3511 rc
= _cmsAddDateTimeTag(hProfile
, sig
, (struct tm
*) Tag
);
3514 case icSigColorantTableTag
:
3515 case icSigColorantTableOutTag
:
3516 rc
= _cmsAddColorantTableTag(hProfile
, sig
, (LPcmsNAMEDCOLORLIST
) Tag
);
3520 case icSigChromaticAdaptationTag
:
3521 rc
= _cmsAddChromaticAdaptationTag(hProfile
, sig
, (const cmsCIEXYZ
*) Tag
);
3525 cmsSignalError(LCMS_ERRC_ABORTED
, "cmsAddTag: Tag '%x' is unsupported", sig
);
3529 // Check for critical tags
3533 case icSigMediaWhitePointTag
:
3534 case icSigMediaBlackPointTag
:
3535 case icSigChromaticAdaptationTag
:
3537 ReadCriticalTags((LPLCMSICCPROFILE
) hProfile
);
3547 // Low-level save to disk. It closes the profile on exit
3549 LCMSBOOL LCMSEXPORT
_cmsSaveProfile(cmsHPROFILE hProfile
, const char* FileName
)
3551 LPLCMSICCPROFILE Icc
= (LPLCMSICCPROFILE
) (LPSTR
) hProfile
;
3552 LCMSICCPROFILE Keep
;
3555 CopyMemory(&Keep
, Icc
, sizeof(LCMSICCPROFILE
));
3556 _cmsSetSaveToDisk(Icc
, NULL
);
3558 // Pass #1 does compute offsets
3560 if (!SaveHeader(Icc
)) return FALSE
;
3561 if (!SaveTagDirectory(Icc
)) return FALSE
;
3562 if (!SaveTags(Icc
, &Keep
)) return FALSE
;
3565 _cmsSetSaveToDisk(Icc
, FileName
);
3568 // Pass #2 does save to file
3570 if (!SaveHeader(Icc
)) goto CleanUp
;
3571 if (!SaveTagDirectory(Icc
)) goto CleanUp
;
3572 if (!SaveTags(Icc
, &Keep
)) goto CleanUp
;
3574 rc
= (Icc
->Close(Icc
) == 0);
3575 CopyMemory(Icc
, &Keep
, sizeof(LCMSICCPROFILE
));
3583 CopyMemory(Icc
, &Keep
, sizeof(LCMSICCPROFILE
));
3588 // Low-level save from open stream
3589 LCMSBOOL LCMSEXPORT
_cmsSaveProfileToMem(cmsHPROFILE hProfile
, void *MemPtr
,
3590 size_t* BytesNeeded
)
3592 LPLCMSICCPROFILE Icc
= (LPLCMSICCPROFILE
) (LPSTR
) hProfile
;
3593 LCMSICCPROFILE Keep
;
3596 CopyMemory(&Keep
, Icc
, sizeof(LCMSICCPROFILE
));
3598 _cmsSetSaveToMemory(Icc
, NULL
, 0);
3600 // Pass #1 does compute offsets
3602 if (!SaveHeader(Icc
)) return FALSE
;
3603 if (!SaveTagDirectory(Icc
)) return FALSE
;
3604 if (!SaveTags(Icc
, &Keep
)) return FALSE
;
3608 // update BytesSaved so caller knows how many bytes are needed for MemPtr
3609 *BytesNeeded
= Icc
->UsedSpace
;
3610 CopyMemory(Icc
, &Keep
, sizeof(LCMSICCPROFILE
));
3614 if (*BytesNeeded
< Icc
->UsedSpace
) {
3616 // need at least UsedSpace in MemPtr to continue
3617 CopyMemory(Icc
, &Keep
, sizeof(LCMSICCPROFILE
));
3621 _cmsSetSaveToMemory(Icc
, MemPtr
, *BytesNeeded
);
3624 // Pass #2 does save to file into supplied stream
3625 if (!SaveHeader(Icc
)) goto CleanUp
;
3626 if (!SaveTagDirectory(Icc
)) goto CleanUp
;
3627 if (!SaveTags(Icc
, &Keep
)) goto CleanUp
;
3629 // update BytesSaved so caller knows how many bytes put into stream
3630 *BytesNeeded
= Icc
->UsedSpace
;
3633 CopyMemory(Icc
, &Keep
, sizeof(LCMSICCPROFILE
));
3639 CopyMemory(Icc
, &Keep
, sizeof(LCMSICCPROFILE
));