b=450088 backing out (new reftest failed)
[wine-gecko.git] / modules / lcms / src / cmsio1.c
blobb1b4e58281dc2798f177a18240b2ea79b3e2ed25
1 //
2 // Little cms
3 // Copyright (C) 1998-2007 Marti Maria
4 //
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
26 #include "lcms.h"
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;
39 #ifdef __BEOS__
40 # define USE_CUSTOM_SWAB 1
41 #endif
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
51 static
52 void xswab(const void *from, void *to, size_t len)
54 register unsigned long temp;
55 register int n;
56 register char *fp, *tp;
58 n = (len >> 1) + 1;
59 fp = (char *)from;
60 tp = (char *)to;
61 #define STEP temp = *fp++,*tp++ = *fp++,*tp++ = temp
62 /* round to multiple of 8 */
63 while ((--n) & 07)
64 STEP;
65 n >>= 3;
66 while (--n >= 0) {
68 STEP; STEP; STEP; STEP;
69 STEP; STEP; STEP; STEP;
71 #undef STEP
73 #else
74 #define xswab swab
75 #endif
79 // Little-Endian to Big-Endian
82 #ifdef USE_BIG_ENDIAN
83 #define AdjustEndianess16(a)
84 #define AdjustEndianess32(a)
85 #define AdjustEndianessArray16(a, b)
86 #else
88 static
89 void AdjustEndianess16(LPBYTE pByte)
91 BYTE tmp;
93 tmp = pByte[0];
94 pByte[0] = pByte[1];
95 pByte[1] = tmp;
98 static
99 void AdjustEndianess32(LPBYTE pByte)
101 BYTE temp1;
102 BYTE temp2;
104 temp1 = *pByte++;
105 temp2 = *pByte++;
106 *(pByte-1) = *pByte;
107 *pByte++ = temp2;
108 *(pByte-3) = *pByte;
109 *pByte = temp1;
113 // swap bytes in a array of words
115 static
116 void AdjustEndianessArray16(LPWORD p, size_t num_words)
118 xswab((char*) p, (char*)p, (int) num_words * sizeof(WORD));
121 #endif
124 // Transports to properly encoded values - note that icc profiles does use
125 // big endian notation.
127 static
128 icInt32Number TransportValue32(icInt32Number Value)
130 icInt32Number Temp = Value;
132 AdjustEndianess32((LPBYTE) &Temp);
133 return Temp;
136 static
137 WORD TransportValue16(WORD Value)
139 WORD Temp = Value;
141 AdjustEndianess16((LPBYTE) &Temp);
142 return Temp;
146 // from Fixed point 8.8 to double
148 static
149 double Convert8Fixed8(WORD fixed8)
151 BYTE msb, lsb;
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
161 static
162 double Convert15Fixed16(icS15Fixed16Number fix32)
164 double floater, sign, mid, hack;
165 int Whole, FracPart;
168 AdjustEndianess32((LPBYTE) &fix32);
170 sign = (fix32 < 0 ? -1 : 1);
171 fix32 = abs(fix32);
173 Whole = LOWORD(fix32 >> 16);
174 FracPart = LOWORD(fix32 & 0x0000ffffL);
176 hack = 65536.0;
177 mid = (double) FracPart / hack;
178 floater = (double) Whole + mid;
180 return sign * floater;
184 // Auxiliar-- read base and return type
186 static
187 icTagTypeSignature ReadBase(LPLCMSICCPROFILE Icc)
189 icTagBase Base;
191 Icc -> Read(&Base, sizeof(icTagBase), 1, Icc);
192 AdjustEndianess32((LPBYTE) &Base.sig);
194 return Base.sig;
198 static
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;
207 Dest->tm_wday = -1;
208 Dest->tm_yday = -1;
209 Dest->tm_isdst = 0;
212 static
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
227 static
228 void NormalizeXYZ(LPcmsCIEXYZ Dest)
230 while (Dest -> X > 2. &&
231 Dest -> Y > 2. &&
232 Dest -> Z > 2.) {
234 Dest -> X /= 10.;
235 Dest -> Y /= 10.;
236 Dest -> Z /= 10.;
240 // Evaluates a XYZ tristimulous across chromatic adaptation matrix
242 static
243 void EvalCHRM(LPcmsCIEXYZ Dest, LPMAT3 Chrm, LPcmsCIEXYZ Src)
245 VEC3 d, s;
247 s.n[VX] = Src -> X;
248 s.n[VY] = Src -> Y;
249 s.n[VZ] = Src -> Z;
251 MAT3eval(&d, Chrm, &s);
253 Dest ->X = d.n[VX];
254 Dest ->Y = d.n[VY];
255 Dest ->Z = d.n[VZ];
260 // Read profile header and validate it
262 static
263 LPLCMSICCPROFILE ReadHeader(LPLCMSICCPROFILE Icc, LCMSBOOL lIsFromMemory)
265 icTag Tag;
266 icHeader Header;
267 icInt32Number TagCount, i;
269 if (Icc -> Read(&Header, sizeof(icHeader), 1, Icc) != 1)
270 goto ErrorCleanup;
272 // Convert endian
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);
285 // Validate it
287 if (Header.magic != icMagicNumber) goto ErrorCleanup;
289 if (Icc ->Read(&TagCount, sizeof(icInt32Number), 1, Icc) != 1)
290 goto ErrorCleanup;
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);
326 goto ErrorCleanup;
329 Icc -> TagCount = TagCount;
330 for (i=0; i < TagCount; i++) {
332 if (Icc ->Read(&Tag, sizeof(icTag), 1, Icc) != 1)
333 goto ErrorCleanup;
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;
348 return Icc;
351 ErrorCleanup:
353 Icc ->Close(Icc);
355 if (lIsFromMemory)
356 cmsSignalError(LCMS_ERRC_ABORTED, "Corrupted memory profile");
357 else
358 cmsSignalError(LCMS_ERRC_ABORTED, "Corrupted profile: '%s'", Icc->PhysicalFile);
361 _cmsFree(Icc);
362 return NULL;
368 static
369 unsigned int uipow(unsigned int a, unsigned int b) {
370 unsigned int rv = 1;
371 for (; b > 0; b--)
372 rv *= a;
373 return rv;
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
401 static
402 void FixLUT8(LPLUT Lut, icTagSignature sig, size_t nTabSize)
404 MAT3 Fixup, Original, Result;
405 LPWORD PtrW;
406 size_t i;
408 switch (sig) {
411 case icSigBToA0Tag:
412 case icSigBToA1Tag:
413 case icSigBToA2Tag:
414 case icSigGamutTag:
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;
430 break;
432 // For input, clear low part since this has to be
433 // Lab in fixed point
435 default:
437 PtrW = Lut -> T;
438 for (i = 0; i < nTabSize; i++) {
440 *PtrW++ &= 0xFF00;
446 // On Lab -> Lab abstract or Lab identities, fix both sides of LUT
448 static
449 void FixLUT8bothSides(LPLUT Lut, size_t nTabSize)
451 MAT3 Fixup, Original, Result;
452 LPWORD PtrW;
453 size_t i;
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;
465 PtrW = Lut -> T;
466 for (i = 0; i < nTabSize; i++) {
468 *PtrW++ &= 0xFF00;
474 // The infamous LUT 8
476 static
477 LCMSBOOL ReadLUT8(LPLCMSICCPROFILE Icc, LPLUT NewLUT, icTagSignature sig)
479 icLut8 LUT8;
480 LPBYTE Temp;
481 size_t nTabSize;
482 unsigned int i, j;
483 unsigned int AllLinear;
484 LPWORD PtrW;
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;
495 // Do some checking
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);
511 // Matrix handling
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;
532 // Copy input tables
534 Temp = (LPBYTE) _cmsMalloc(256);
535 if (Temp == NULL) return FALSE;
537 AllLinear = 0;
538 for (i=0; i < NewLUT -> InputChan; i++) {
540 PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * 256);
541 if (PtrW == NULL) {
542 _cmsFree(Temp);
543 return FALSE;
546 NewLUT -> L1[i] = PtrW;
547 if (Icc ->Read(Temp, 1, 256, Icc) != 256) {
548 _cmsFree(Temp);
549 return FALSE;
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;
564 _cmsFree(Temp);
566 // Copy 3D CLUT
568 nTabSize = (NewLUT -> OutputChan * uipow(NewLUT->cLutPoints,
569 NewLUT->InputChan));
571 if (nTabSize > 0) {
573 PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * nTabSize);
574 if (PtrW == NULL) return FALSE;
576 Temp = (LPBYTE) _cmsMalloc(nTabSize);
577 if (Temp == NULL) {
578 _cmsFree(PtrW);
579 return FALSE;
582 if (Icc ->Read(Temp, 1, nTabSize, Icc) != nTabSize) {
583 _cmsFree(Temp);
584 _cmsFree(PtrW);
585 return FALSE;
588 NewLUT -> T = PtrW;
589 NewLUT -> Tsize = (unsigned int) (nTabSize * sizeof(WORD));
591 for (i = 0; i < nTabSize; i++) {
593 *PtrW++ = TO16_TAB(Temp[i]);
595 _cmsFree(Temp);
597 else {
598 NewLUT ->T = NULL;
599 NewLUT ->Tsize = 0;
600 NewLUT ->wFlags &= ~LUT_HAS3DGRID;
604 // Copy output tables
606 Temp = (LPBYTE) _cmsMalloc(256);
607 if (Temp == NULL) {
608 return FALSE;
611 AllLinear = 0;
612 for (i=0; i < NewLUT -> OutputChan; i++) {
614 PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * 256);
615 if (PtrW == NULL) {
616 _cmsFree(Temp);
617 return FALSE;
620 NewLUT -> L2[i] = PtrW;
621 if (Icc ->Read(Temp, 1, 256, Icc) != 256) {
622 _cmsFree(Temp);
623 return FALSE;
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;
639 _cmsFree(Temp);
641 cmsCalcL16Params(NewLUT -> InputEntries, &NewLUT -> In16params);
642 cmsCalcL16Params(NewLUT -> OutputEntries, &NewLUT -> Out16params);
643 cmsCalcCLUT16Params(NewLUT -> cLutPoints, NewLUT -> InputChan,
644 NewLUT -> OutputChan,
645 &NewLUT -> CLut16params);
646 // Fixup
648 if (Icc ->PCS == icSigLabData) {
650 // Abstract or Lab identity
652 if (Icc -> ColorSpace == icSigLabData)
654 FixLUT8bothSides(NewLUT, nTabSize);
655 else
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];
667 int j, nChannels;
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;
694 // 3.- Decide method
696 if (sqrt(DistFixed) < sqrt(DistUnfixed))
697 NewLUT -> FixGrayAxes = TRUE;
698 else
699 NewLUT -> FixGrayAxes = FALSE;
706 return TRUE;
712 // Case LUT 16
714 static
715 LCMSBOOL ReadLUT16(LPLCMSICCPROFILE Icc, LPLUT NewLUT)
717 icLut16 LUT16;
718 size_t nTabSize;
719 unsigned int i;
720 unsigned int AllLinear;
721 LPWORD PtrW;
724 if (Icc ->Read(&LUT16, sizeof(icLut16)- SIZEOF_UINT16_ALIGNED, 1, Icc) != 1)
725 return FALSE;
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;
739 // Matrix handling
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;
769 // Copy input tables
771 AllLinear = 0;
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) {
779 return FALSE;
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;
794 // Copy 3D CLUT
796 nTabSize = (NewLUT -> OutputChan * uipow(NewLUT->cLutPoints,
797 NewLUT->InputChan));
798 if (nTabSize > 0) {
800 PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * nTabSize);
801 if (PtrW == NULL) {
802 _cmsFree(PtrW);
803 return FALSE;
806 NewLUT -> T = PtrW;
807 NewLUT -> Tsize = (unsigned int) (nTabSize * sizeof(WORD));
809 if (Icc -> Read(PtrW, sizeof(WORD), nTabSize, Icc) != nTabSize) {
810 return FALSE;
813 AdjustEndianessArray16(NewLUT -> T, nTabSize);
815 else {
816 NewLUT ->T = NULL;
817 NewLUT ->Tsize = 0;
818 NewLUT -> wFlags &= ~LUT_HAS3DGRID;
821 // Copy output tables
823 AllLinear = 0;
824 for (i=0; i < NewLUT -> OutputChan; i++) {
826 PtrW = (LPWORD) _cmsMalloc(sizeof(WORD) * NewLUT -> OutputEntries);
827 if (PtrW == NULL) {
828 return FALSE;
831 NewLUT -> L2[i] = PtrW;
832 if (Icc ->Read(PtrW, sizeof(WORD), NewLUT -> OutputEntries, Icc) != NewLUT -> OutputEntries) {
833 return FALSE;
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);
854 return TRUE;
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.
861 static
862 LPGAMMATABLE ReadCurve(LPLCMSICCPROFILE Icc)
864 icUInt32Number Count;
865 LPGAMMATABLE NewGamma;
866 icTagTypeSignature BaseType;
867 int n;
870 BaseType = ReadBase(Icc);
871 switch (BaseType) {
874 case ((icTagTypeSignature) 0x9478ee00): // Monaco 2 profiler is BROKEN!
875 case icSigCurveType:
877 if (Icc ->Read(&Count, sizeof(icUInt32Number), 1, Icc) != 1) return NULL;
878 AdjustEndianess32((LPBYTE) &Count);
880 switch (Count) {
882 case 0: // Linear.
884 NewGamma = cmsAllocGamma(2);
885 if (!NewGamma) return NULL;
886 NewGamma -> GammaTable[0] = 0;
887 NewGamma -> GammaTable[1] = 0xFFFF;
888 return NewGamma;
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));
899 default: { // Curve
901 NewGamma = cmsAllocGamma(Count);
902 if (!NewGamma) return NULL;
904 if (Icc ->Read(NewGamma -> GammaTable, sizeof(WORD), Count, Icc) != Count)
905 return NULL;
906 AdjustEndianessArray16(NewGamma -> GammaTable, Count);
907 return NewGamma;
910 break;
913 // Parametric curves
914 case icSigParametricCurveType: {
916 int ParamsByType[] = { 1, 3, 4, 5, 7 };
917 double Params[10];
918 icS15Fixed16Number Num;
919 icUInt32Number Reserved;
920 icUInt16Number Type;
921 int i;
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);
927 if (Type > 4) {
929 cmsSignalError(LCMS_ERRC_ABORTED, "Unknown parametric curve type '%d' found.", Type);
930 return NULL;
933 ZeroMemory(Params, 10* sizeof(double));
934 n = ParamsByType[Type];
936 for (i=0; i < n; i++) {
937 Num = 0;
938 if (Icc -> Read(&Num, sizeof(icS15Fixed16Number), 1, Icc) != 1) return NULL;
939 Params[i] = Convert15Fixed16(Num);
943 NewGamma = cmsBuildParametricGamma(4096, Type+1, Params);
944 return NewGamma;
948 default:
949 cmsSignalError(LCMS_ERRC_ABORTED, "Bad tag signature '%lx' found.", BaseType);
950 return NULL;
956 // Similar to anterior, but curve is reversed
958 static
959 LPGAMMATABLE ReadCurveReversed(LPLCMSICCPROFILE Icc)
962 icTagTypeSignature BaseType;
963 LPGAMMATABLE NewGamma, ReturnGamma;
964 icUInt32Number Count;
965 int n;
968 BaseType = ReadBase(Icc);
970 switch (BaseType) {
973 case 0x9478ee00L: // Monaco 2 profiler is BROKEN!
974 case icSigCurveType:
976 if (Icc -> Read(&Count, sizeof(icUInt32Number), 1, Icc) != 1) return NULL;
977 AdjustEndianess32((LPBYTE) &Count);
980 switch (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;
989 return NewGamma;
991 case 1: {
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)
1005 return NULL;
1007 AdjustEndianessArray16(NewGamma -> GammaTable, Count);
1009 if (Count < 256)
1010 Count = 256; // Reverse of simple curve has not necesarely to be simple
1012 ReturnGamma = cmsReverseGamma(Count, NewGamma);
1013 cmsFreeGamma(NewGamma);
1015 return ReturnGamma;
1018 break;
1021 // Parametric curves
1022 case icSigParametricCurveType: {
1024 int ParamsByType[] = { 1, 3, 4, 5, 7 };
1025 double Params[10];
1026 icS15Fixed16Number Num;
1027 icUInt32Number Reserved;
1028 icUInt16Number Type;
1029 int i;
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);
1036 if (Type > 4) {
1038 cmsSignalError(LCMS_ERRC_ABORTED, "Unknown parametric curve type '%d' found.", Type);
1039 return NULL;
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);
1053 return NewGamma;
1057 default:
1058 cmsSignalError(LCMS_ERRC_ABORTED, "Bad tag signature '%lx' found.", BaseType);
1059 return NULL;
1065 // V4 stuff. Read matrix for LutAtoB and LutBtoA
1067 static
1068 LCMSBOOL ReadMatrixOffset(LPLCMSICCPROFILE Icc, size_t Offset, LPLUT NewLUT, DWORD dwFlags)
1071 icS15Fixed16Number All[12];
1072 int i;
1073 MAT3 m;
1074 VEC3 o;
1076 if (Icc -> Seek(Icc, Offset)) return FALSE;
1078 if (Icc ->Read(All, sizeof(icS15Fixed16Number), 12, Icc) != 12)
1079 return FALSE;
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);
1101 return TRUE;
1105 // V4 stuff. Read CLUT part for LutAtoB and LutBtoA
1107 static
1108 LCMSBOOL ReadCLUT(LPLCMSICCPROFILE Icc, size_t Offset, LPLUT NewLUT)
1111 icCLutStruct CLUT;
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) {
1124 BYTE v;
1125 unsigned int i;
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);
1133 else
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));
1141 else {
1142 cmsSignalError(LCMS_ERRC_ABORTED, "Unknow precission of '%d'", CLUT.prec);
1143 return FALSE;
1146 return TRUE;
1150 static
1151 void SkipAlignment(LPLCMSICCPROFILE Icc)
1153 BYTE Buffer[4];
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
1161 static
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;
1172 else
1173 nCurves = NewLUT ->OutputChan;
1175 for (i=0; i < nCurves; i++) {
1177 Curves[i] = ReadCurve(Icc);
1178 if (Curves[i] == NULL) return FALSE;
1179 SkipAlignment(Icc);
1183 NewLUT = cmsAllocLinearTable(NewLUT, Curves, nLocation);
1185 for (i=0; i < nCurves; i++)
1186 cmsFreeGamma(Curves[i]);
1188 return TRUE;
1192 // V4 stuff. LutAtoB type
1194 // [L1] -> [CLUT] -> [L4] -> [Mat4] -> [Ofs4] -> [L2]
1196 // Mat, Mat3, Ofs3, L3 are missing
1197 // L1 = A curves
1198 // L4 = M curves
1199 // L2 = B curves
1201 static
1202 LCMSBOOL ReadLUT_A2B(LPLCMSICCPROFILE Icc, LPLUT NewLUT, size_t BaseOffset, icTagSignature sig)
1204 icLutAtoB LUT16;
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) {
1237 switch (sig) {
1239 case icSigAToB0Tag:
1240 case icSigAToB1Tag:
1241 case icSigAToB2Tag:
1242 case icSigGamutTag:
1243 case icSigPreview0Tag:
1244 case icSigPreview1Tag:
1245 case icSigPreview2Tag:
1247 NewLUT ->wFlags |= LUT_V4_INPUT_EMULATE_V2;
1248 break;
1250 default:;
1255 return TRUE;
1258 // V4 stuff. LutBtoA type
1260 static
1261 LCMSBOOL ReadLUT_B2A(LPLCMSICCPROFILE Icc, LPLUT NewLUT, size_t BaseOffset, icTagSignature sig)
1263 icLutBtoA LUT16;
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) {
1297 switch (sig) {
1299 case icSigBToA0Tag:
1300 case icSigBToA1Tag:
1301 case icSigBToA2Tag:
1302 case icSigGamutTag:
1303 case icSigPreview0Tag:
1304 case icSigPreview1Tag:
1305 case icSigPreview2Tag:
1307 NewLUT ->wFlags |= LUT_V4_OUTPUT_EMULATE_V2;
1308 break;
1310 default:;
1314 return TRUE;
1317 // CLUT main reader
1319 LPLUT LCMSEXPORT cmsReadICCLut(cmsHPROFILE hProfile, icTagSignature sig)
1322 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
1323 icTagTypeSignature BaseType;
1324 int n;
1325 size_t offset;
1326 LPLUT NewLUT;
1328 n = _cmsSearchTag(Icc, sig, TRUE);
1329 if (n < 0)
1330 return NULL;
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))
1342 return NULL;
1344 BaseType = ReadBase(Icc);
1347 NewLUT = cmsAllocLUT();
1348 if (!NewLUT) {
1350 cmsSignalError(LCMS_ERRC_ABORTED, "cmsAllocLUT() failed");
1351 return NULL;
1355 switch (BaseType) {
1357 case icSigLut8Type: if (!ReadLUT8(Icc, NewLUT, sig)) {
1358 cmsFreeLUT(NewLUT);
1359 return NULL;
1361 break;
1363 case icSigLut16Type: if (!ReadLUT16(Icc, NewLUT)) {
1364 cmsFreeLUT(NewLUT);
1365 return NULL;
1367 break;
1369 case icSiglutAtoBType: if (!ReadLUT_A2B(Icc, NewLUT, offset, sig)) {
1370 cmsFreeLUT(NewLUT);
1371 return NULL;
1373 break;
1375 case icSiglutBtoAType: if (!ReadLUT_B2A(Icc, NewLUT, offset, sig)) {
1376 cmsFreeLUT(NewLUT);
1377 return NULL;
1379 break;
1381 default: cmsSignalError(LCMS_ERRC_ABORTED, "Bad tag signature %lx found.", BaseType);
1382 cmsFreeLUT(NewLUT);
1383 return NULL;
1387 return NewLUT;
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.
1411 static
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);
1420 switch (BaseType) {
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);
1435 Icc ->Read(Name, 1,
1436 (AsciiCount >= size_max) ? (size_max-1) : AsciiCount, Icc);
1438 if (size < AsciiCount) return (int) size;
1439 size -= AsciiCount;
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;
1475 size --;
1478 break;
1481 case icSigCopyrightTag: // Broken profiles from agfa does store copyright info in such type
1482 case icSigTextType:
1484 char Dummy;
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);
1498 break;
1500 // MultiLocalizedUnicodeType, V4 only
1502 case icSigMultiLocalizedUnicodeType: {
1504 icUInt32Number Count, RecLen;
1505 icUInt16Number Language, Country;
1506 icUInt32Number ThisLen, ThisOffset;
1507 size_t Offset = 0;
1508 size_t Len = 0;
1509 size_t i;
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);
1518 if (RecLen != 12) {
1520 cmsSignalError(LCMS_ERRC_ABORTED, "multiLocalizedUnicodeType of len != 12 is not supported.");
1521 return -1;
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)
1541 break; // Found
1547 if (Offset == 0) {
1549 strcpy(Name, "(no info)");
1550 break;
1553 // Compute true offset
1554 Offset -= 12 * Count + 8 + sizeof(icTagBase);
1556 // Skip unused bytes
1557 for (i=0; i < Offset; i++) {
1559 char Discard;
1560 Icc ->Read(&Discard, 1, 1, Icc);
1564 // Bound len
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);
1583 break;
1585 default:
1586 cmsSignalError(LCMS_ERRC_ABORTED, "Bad tag signature %lx found.", BaseType);
1587 return -1;
1590 return (int) size;
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;
1601 int n;
1603 n = _cmsSearchTag(Icc, sig, TRUE);
1604 if (n < 0)
1605 return -1;
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))
1617 return -1;
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);
1630 // Take an XYZ item
1632 static
1633 int ReadICCXYZ(cmsHPROFILE hProfile, icTagSignature sig, LPcmsCIEXYZ Value, LCMSBOOL lIsFatal)
1635 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
1636 icTagTypeSignature BaseType;
1637 size_t offset;
1638 int n;
1639 icXYZNumber XYZ;
1641 n = _cmsSearchTag(Icc, sig, FALSE);
1642 if (n < 0)
1643 return -1;
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))
1654 return -1;
1657 BaseType = ReadBase(Icc);
1659 switch (BaseType) {
1662 case 0x7c3b10cL: // Some apple broken embedded profiles does not have correct type
1663 case icSigXYZType:
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);
1669 break;
1671 // Aug/21-2001 - Monaco 2 does have WRONG values.
1673 default:
1674 if (lIsFatal)
1675 cmsSignalError(LCMS_ERRC_ABORTED, "Bad tag signature %lx found.", BaseType);
1676 return -1;
1679 return 1;
1683 // Read a icSigS15Fixed16ArrayType (currently only a 3x3 matrix)
1685 static
1686 int ReadICCXYZArray(cmsHPROFILE hProfile, icTagSignature sig, LPMAT3 v)
1688 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
1689 icTagTypeSignature BaseType;
1690 size_t offset, sz;
1691 int i, n;
1692 icXYZNumber XYZ[3];
1693 cmsCIEXYZ XYZdbl[3];
1696 n = _cmsSearchTag(Icc, sig, FALSE);
1697 if (n < 0)
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))
1709 return -1;
1711 BaseType = ReadBase(Icc);
1713 switch (BaseType) {
1715 case icSigS15Fixed16ArrayType:
1717 sz = Icc ->TagSizes[n] / sizeof(icXYZNumber);
1719 if (sz != 3) {
1720 cmsSignalError(LCMS_ERRC_ABORTED, "Bad array size of %d entries.", sz);
1721 return -1;
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));
1734 break;
1736 default:
1737 cmsSignalError(LCMS_ERRC_ABORTED, "Bad tag signature %lx found.", BaseType);
1738 return -1;
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;
1755 return TRUE;
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);
1769 return TRUE;
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.
1785 MAT3identity(r);
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);
1797 return TRUE;
1802 LPGAMMATABLE LCMSEXPORT cmsReadICCGamma(cmsHPROFILE hProfile, icTagSignature sig)
1804 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
1805 size_t offset;
1806 int n;
1809 n = _cmsSearchTag(Icc, sig, TRUE);
1810 if (n < 0)
1811 return NULL;
1813 if (Icc -> TagPtrs[n]) {
1815 return cmsDupGamma((LPGAMMATABLE) Icc -> TagPtrs[n]);
1818 offset = Icc -> TagOffsets[n];
1820 if (Icc -> Seek(Icc, offset))
1821 return NULL;
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;
1833 size_t offset;
1834 int n;
1837 n = _cmsSearchTag(Icc, sig, TRUE);
1838 if (n < 0)
1839 return NULL;
1841 if (Icc -> TagPtrs[n]) {
1843 return cmsReverseGamma(256, (LPGAMMATABLE) Icc -> TagPtrs[n]);
1846 offset = Icc -> TagOffsets[n];
1848 if (Icc -> Seek(Icc, offset))
1849 return NULL;
1851 return ReadCurveReversed(Icc);
1854 // Check Named color header
1856 static
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;
1873 int n;
1874 icTagTypeSignature BaseType;
1875 size_t offset;
1877 n = _cmsSearchTag(Icc, sig, TRUE);
1878 if (n < 0)
1879 return 0;
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))
1895 return 0;
1897 BaseType = ReadBase(Icc);
1899 switch (BaseType) {
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.");
1906 return 0;
1909 // The named color struct
1911 case icSigNamedColor2Type: {
1913 icNamedColor2 nc2;
1914 unsigned int i, j;
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.");
1923 return 0;
1926 if (nc2.nDeviceCoords > MAXCHANNELS) {
1927 cmsSignalError(LCMS_ERRC_WARNING, "Too many device coordinates.");
1928 return 0;
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++) {
1939 WORD PCS[3];
1940 WORD Colorant[MAXCHANNELS];
1941 char Root[33];
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;
1960 break;
1962 default:
1963 cmsSignalError(LCMS_ERRC_WARNING, "Bad tag signature '%lx' found.", BaseType);
1964 return 0;
1967 // It would never reach here
1968 // return 0;
1973 // Read colorant tables
1975 LPcmsNAMEDCOLORLIST LCMSEXPORT cmsReadColorantTable(cmsHPROFILE hProfile, icTagSignature sig)
1977 icInt32Number n, Count, i;
1978 size_t offset;
1979 icTagTypeSignature BaseType;
1980 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
1981 LPcmsNAMEDCOLORLIST List;
1983 n = _cmsSearchTag(Icc, sig, FALSE);
1984 if (n < 0)
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))
2001 return NULL;
2003 BaseType = ReadBase(Icc);
2005 if (BaseType != icSigColorantTableType) {
2006 cmsSignalError(LCMS_ERRC_ABORTED, "Bad tag signature '%lx' found.", BaseType);
2007 return NULL;
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);
2016 return NULL;
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);
2027 return List;
2029 Error:
2030 cmsFreeNamedColorList(List);
2031 return NULL;
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;
2054 // Uncooked model
2056 const char* LCMSEXPORT cmsTakeModel(cmsHPROFILE hProfile)
2059 static char Model[LCMS_DESC_MAX] = "";
2061 Model[0] = 0;
2063 if (cmsIsTag(hProfile, icSigDeviceModelDescTag)) {
2065 cmsReadICCTextEx(hProfile, icSigDeviceModelDescTag, Model, LCMS_DESC_MAX);
2068 return Model;
2072 const char* LCMSEXPORT cmsTakeCopyright(cmsHPROFILE hProfile)
2075 static char Copyright[LCMS_DESC_MAX] = "";
2077 Copyright[0] = 0;
2078 if (cmsIsTag(hProfile, icSigCopyrightTag)) {
2080 cmsReadICCTextEx(hProfile, icSigCopyrightTag, Copyright, LCMS_DESC_MAX);
2083 return Copyright;
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];
2094 Name[0] = '\0';
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);
2112 return Name;
2114 else return "{no name}";
2118 if (!Manufacturer[0] ||
2119 strncmp(Model, Manufacturer, 8) == 0 || strlen(Model) > 30)
2120 strcpy(Name, Model);
2121 else
2122 sprintf(Name, "%s - %s", Model, Manufacturer);
2124 return Name;
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);
2144 return Name;
2148 const char* LCMSEXPORT cmsTakeProductInfo(cmsHPROFILE hProfile)
2150 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
2152 static char Info[4096];
2154 Info[0] = '\0';
2156 if (cmsIsTag(hProfile, icSigProfileDescriptionTag))
2158 char Desc[1024];
2160 cmsReadICCText(hProfile, icSigProfileDescriptionTag, Desc);
2161 strcat(Info, 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
2181 // MonCal
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");
2191 else
2193 cmsCIEXYZ WhitePt;
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);
2206 return Info;
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;
2214 int n;
2216 *Data = NULL;
2217 *len = 0;
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
2230 if (!*Data) {
2232 cmsSignalError(LCMS_ERRC_ABORTED, "Out of memory allocating CharTarget space!");
2233 return FALSE;
2236 if (cmsReadICCTextEx(hProfile, icSigCharTargetTag, *Data, *len) < 0)
2237 return FALSE;
2239 (*Data)[*len] = 0; // Force a zero marker. Shouldn't be needed, but is
2240 // here to simplify things.
2242 return TRUE;
2248 LCMSBOOL LCMSEXPORT cmsTakeCalibrationDateTime(struct tm *Dest, cmsHPROFILE hProfile)
2250 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
2251 int n;
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));
2260 else
2262 icDateTimeNumber timestamp;
2264 if (Icc -> Seek(Icc, Icc -> TagOffsets[n] + sizeof(icTagBase)))
2265 return FALSE;
2267 if (Icc ->Read(&timestamp, 1, sizeof(icDateTimeNumber), Icc) != sizeof(icDateTimeNumber))
2268 return FALSE;
2270 DecodeDateTimeNumber(&timestamp, Dest);
2274 return TRUE;
2279 // PSEQ Tag, used in devicelink profiles
2281 LPcmsSEQ LCMSEXPORT cmsReadProfileSequenceDescription(cmsHPROFILE hProfile)
2283 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
2284 int n;
2285 icUInt32Number i, Count;
2286 icDescStruct DescStruct;
2287 icTagTypeSignature BaseType;
2288 size_t size, offset;
2289 LPcmsSEQ OutSeq;
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);
2303 return OutSeq;
2306 offset = Icc -> TagOffsets[n];
2308 if (Icc -> Seek(Icc, offset))
2309 return NULL;
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;
2322 OutSeq ->n = Count;
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;
2349 return OutSeq;
2353 void LCMSEXPORT cmsFreeProfileSequenceDescription(LPcmsSEQ pseq)
2355 if (pseq)
2356 _cmsFree(pseq);
2363 // Read a few tags that are hardly required
2366 static
2367 void ReadCriticalTags(LPLCMSICCPROFILE Icc)
2369 cmsHPROFILE hProfile = (cmsHPROFILE) Icc;
2371 if (Icc ->Version >= 0x4000000) {
2373 // v4 profiles
2375 MAT3 ChrmCanonical;
2377 if (ReadICCXYZ(hProfile,
2378 icSigMediaWhitePointTag,
2379 &Icc ->MediaWhitePoint, FALSE) < 0) {
2381 Icc ->MediaWhitePoint = *cmsD50_XYZ();
2384 // Read media black
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);
2406 else {
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);
2419 else {
2421 // v2 profiles
2423 // Read media white
2425 if (ReadICCXYZ(hProfile,
2426 icSigMediaWhitePointTag,
2427 &Icc ->MediaWhitePoint, FALSE) < 0) {
2429 Icc ->MediaWhitePoint = *cmsD50_XYZ();
2432 // Read media black
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,
2454 NULL,
2455 &Icc -> Illuminant,
2456 &Icc -> MediaWhitePoint);
2458 else
2459 MAT3identity(&Icc ->ChromaticAdaptation);
2466 // Create profile from disk file
2468 cmsHPROFILE LCMSEXPORT cmsOpenProfileFromFile(const char *lpFileName, const char *sAccess)
2470 LPLCMSICCPROFILE NewIcc;
2471 cmsHPROFILE hEmpty;
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
2486 sAccess++;
2487 if (*sAccess == '8') NewIcc ->SaveAs8Bits = TRUE;
2489 return hEmpty;
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
2528 // ignored.
2529 LCMSBOOL LCMSEXPORT cmsProfileIsBogus(cmsHPROFILE hProfile)
2532 cmsCIEXYZTRIPLE primaries;
2533 VEC3 sum, target, tolerance;
2534 unsigned i;
2536 // Read the primaries
2537 cmsTakeColorants(&primaries, hProfile);
2539 // Sum the values
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
2552 // criterion.
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])))
2561 return TRUE;
2564 // All Good
2565 return FALSE;
2568 LCMSBOOL LCMSEXPORT cmsCloseProfile(cmsHPROFILE hProfile)
2570 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
2571 LCMSBOOL rc = TRUE;
2572 icInt32Number i;
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
2600 return rc;
2605 // Write profile ------------------------------------------------------------
2609 static
2610 LCMSBOOL SaveWordsTable(int nEntries, LPWORD Tab, LPLCMSICCPROFILE Icc)
2612 size_t nTabSize = sizeof(WORD) * nEntries;
2613 LPWORD PtrW = (LPWORD) _cmsMalloc(nTabSize);
2614 LCMSBOOL rc;
2616 if (!PtrW) return FALSE;
2617 CopyMemory(PtrW, Tab, nTabSize);
2618 AdjustEndianessArray16(PtrW, nEntries);
2619 rc = Icc ->Write(Icc, nTabSize, PtrW);
2620 free(PtrW);
2622 return rc;
2627 // Saves profile header
2629 static
2630 LCMSBOOL SaveHeader(LPLCMSICCPROFILE Icc)
2632 icHeader Header;
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);
2647 #ifdef NON_WINDOWS
2648 Header.platform = (icPlatformSignature)TransportValue32(icSigMacintosh);
2649 #else
2650 Header.platform = (icPlatformSignature)TransportValue32(icSigMicrosoft);
2651 #endif
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));
2671 // Set profile ID
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
2684 static
2685 LCMSBOOL SetupBase(icTagTypeSignature sig, LPLCMSICCPROFILE Icc)
2687 icTagBase Base;
2689 Base.sig = (icTagTypeSignature) TransportValue32(sig);
2690 ZeroMemory(&Base.reserved, sizeof(Base.reserved));
2691 return Icc -> Write(Icc, sizeof(icTagBase), &Base);
2695 // Store a XYZ tag
2697 static
2698 LCMSBOOL SaveXYZNumber(LPcmsCIEXYZ Value, LPLCMSICCPROFILE Icc)
2701 icXYZNumber XYZ;
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.
2716 static
2717 LCMSBOOL SaveXYZArray(int n, LPcmsCIEXYZ Value, LPLCMSICCPROFILE Icc)
2719 int i;
2720 icXYZNumber XYZ;
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;
2732 Value++;
2735 return TRUE;
2740 // Save a gamma structure as a table
2742 static
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
2759 static
2760 LCMSBOOL SaveGammaOneValue(LPGAMMATABLE Gamma, LPLCMSICCPROFILE Icc)
2762 icInt32Number Count;
2763 Fixed32 GammaFixed32;
2764 WORD GammaFixed8;
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
2780 static
2781 LCMSBOOL SaveGammaParametric(LPGAMMATABLE Gamma, LPLCMSICCPROFILE Icc)
2783 icUInt16Number Type, Reserved;
2784 int i, nParams;
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);
2804 return TRUE;
2809 // Save a gamma table
2811 static
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);
2841 // Save an DESC Tag
2843 static
2844 LCMSBOOL SaveDescription(const char *Text, LPLCMSICCPROFILE Icc)
2847 icUInt32Number len, Count, TotalSize, AlignedSize;
2848 char Filler[256];
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;
2874 AlignedSize -= len;
2876 if (AlignedSize < 0)
2877 AlignedSize = 0;
2878 if (AlignedSize > 255)
2879 AlignedSize = 255;
2881 ZeroMemory(Filler, AlignedSize);
2882 if (!Icc ->Write(Icc, AlignedSize, Filler)) return FALSE;
2884 return TRUE;
2887 // Save an ASCII Tag
2889 static
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;
2896 return TRUE;
2900 // Save one of these new chromaticity values
2902 static
2903 LCMSBOOL SaveOneChromaticity(double x, double y, LPLCMSICCPROFILE Icc)
2905 Fixed32 xf, yf;
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;
2913 return TRUE;
2917 // New tag added in Addendum II of old spec.
2919 static
2920 LCMSBOOL SaveChromaticities(LPcmsCIExyYTRIPLE chrm, LPLCMSICCPROFILE Icc)
2922 WORD nChans, Table;
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;
2935 return TRUE;
2939 static
2940 LCMSBOOL SaveSequenceDescriptionTag(LPcmsSEQ seq, LPLCMSICCPROFILE Icc)
2942 icUInt32Number nSeqs;
2943 icDescStruct DescStruct;
2944 int i, n = seq ->n;
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;
2970 return TRUE;
2974 // Saves a timestamp tag
2976 static
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;
2985 return TRUE;
2989 // Saves a named color list into a named color profile
2990 static
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
2999 int i;
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];
3022 char root[32];
3023 LPcmsNAMEDCOLOR Color;
3024 int j;
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;
3046 return TRUE;
3051 // Saves a colorant table. It is using the named color structure for simplicity sake
3053 static
3054 LCMSBOOL SaveColorantTable(LPcmsNAMEDCOLORLIST NamedColorList, LPLCMSICCPROFILE Icc)
3056 icUInt32Number count; // Count of named colors
3057 int i;
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;
3070 int j;
3072 Color = NamedColorList ->List + i;
3074 strncpy((char*) root, Color ->Name, 32);
3075 root[32] = 0;
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;
3087 return TRUE;
3090 // Does serialization of LUT16 and writes it.
3092 static
3093 LCMSBOOL SaveLUT(const LUT* NewLUT, LPLCMSICCPROFILE Icc)
3095 icLut16 LUT16;
3096 unsigned int i;
3097 size_t nTabSize;
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]);
3122 else {
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));
3136 // Save header
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));
3156 // The 3D CLUT.
3158 if (nTabSize > 0) {
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);
3175 return TRUE;
3180 // Does serialization of LUT8 and writes it
3182 static
3183 LCMSBOOL SaveLUT8(const LUT* NewLUT, LPLCMSICCPROFILE Icc)
3185 icLut8 LUT8;
3186 unsigned int i, j;
3187 size_t nTabSize;
3188 BYTE val;
3190 // Sanity check
3192 if (NewLUT -> wFlags & LUT_HASTL1) {
3194 if (NewLUT -> InputEntries != 256) {
3195 cmsSignalError(LCMS_ERRC_ABORTED, "LUT8 needs 256 entries on prelinearization");
3196 return FALSE;
3202 if (NewLUT -> wFlags & LUT_HASTL2) {
3204 if (NewLUT -> OutputEntries != 256) {
3205 cmsSignalError(LCMS_ERRC_ABORTED, "LUT8 needs 256 entries on postlinearization");
3206 return FALSE;
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]);
3231 else {
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));
3245 // Save header
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);
3257 else
3258 val = (BYTE) j;
3260 Icc ->Write(Icc, 1, &val);
3266 nTabSize = (NewLUT -> OutputChan * uipow(NewLUT->cLutPoints,
3267 NewLUT->InputChan));
3268 // The 3D CLUT.
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);
3284 else
3285 val = (BYTE) j;
3287 Icc ->Write(Icc, 1, &val);
3292 return TRUE;
3297 // Set the LUT bitdepth to be saved
3299 void LCMSEXPORT _cmsSetLUTdepth(cmsHPROFILE hProfile, int depth)
3301 LPLCMSICCPROFILE Icc = (LPLCMSICCPROFILE) (LPSTR) hProfile;
3303 switch (depth) {
3305 case 8: Icc ->SaveAs8Bits = TRUE; break;
3306 case 16: Icc ->SaveAs8Bits = FALSE; break;
3308 default:
3309 cmsSignalError(LCMS_ERRC_ABORTED, "%d is an unsupported as bitdepth, use 8 or 16 only.", depth);
3314 // Saves Tag directory
3316 static
3317 LCMSBOOL SaveTagDirectory(LPLCMSICCPROFILE Icc)
3319 icInt32Number i;
3320 icTag Tag;
3321 icInt32Number Count = 0;
3323 // Get true count
3324 for (i=0; i < Icc -> TagCount; i++) {
3325 if (Icc ->TagNames[i] != 0)
3326 Count++;
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;
3343 return TRUE;
3347 // Dump tag contents
3349 static
3350 LCMSBOOL SaveTags(LPLCMSICCPROFILE Icc, LPLCMSICCPROFILE FileOrig)
3353 LPBYTE Data;
3354 icInt32Number i;
3355 size_t Begin;
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) {
3369 BYTE Filler[20];
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];
3378 if (!Data) {
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];
3387 void* Mem;
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);
3397 free(Mem);
3400 continue;
3404 switch (Icc -> TagNames[i]) {
3406 case icSigProfileDescriptionTag:
3407 case icSigDeviceMfgDescTag:
3408 case icSigDeviceModelDescTag:
3409 if (!SaveDescription((const char *) Data, Icc)) return FALSE;
3410 break;
3412 case icSigRedColorantTag:
3413 case icSigGreenColorantTag:
3414 case icSigBlueColorantTag:
3415 case icSigMediaWhitePointTag:
3416 case icSigMediaBlackPointTag:
3417 if (!SaveXYZNumber((LPcmsCIEXYZ) Data, Icc)) return FALSE;
3418 break;
3421 case icSigRedTRCTag:
3422 case icSigGreenTRCTag:
3423 case icSigBlueTRCTag:
3424 case icSigGrayTRCTag:
3425 if (!SaveGamma((LPGAMMATABLE) Data, Icc)) return FALSE;
3426 break;
3428 case icSigCharTargetTag:
3429 case icSigCopyrightTag:
3430 if (!SaveText((const char *) Data, Icc)) return FALSE;
3431 break;
3433 case icSigChromaticityTag:
3434 if (!SaveChromaticities((LPcmsCIExyYTRIPLE) Data, Icc)) return FALSE;
3435 break;
3437 // Save LUT
3439 case icSigAToB0Tag:
3440 case icSigAToB1Tag:
3441 case icSigAToB2Tag:
3442 case icSigBToA0Tag:
3443 case icSigBToA1Tag:
3444 case icSigBToA2Tag:
3445 case icSigGamutTag:
3446 case icSigPreview0Tag:
3447 case icSigPreview1Tag:
3448 case icSigPreview2Tag:
3450 if (Icc ->SaveAs8Bits) {
3452 if (!SaveLUT8((LPLUT) Data, Icc)) return FALSE;
3454 else {
3456 if (!SaveLUT((LPLUT) Data, Icc)) return FALSE;
3458 break;
3460 case icSigProfileSequenceDescTag:
3461 if (!SaveSequenceDescriptionTag((LPcmsSEQ) Data, Icc)) return FALSE;
3462 break;
3465 case icSigNamedColor2Tag:
3466 if (!SaveNamedColorList((LPcmsNAMEDCOLORLIST) Data, Icc)) return FALSE;
3467 break;
3470 case icSigCalibrationDateTimeTag:
3471 if (!SaveDateTimeNumber((struct tm *) Data, Icc)) return FALSE;
3472 break;
3475 case icSigColorantTableTag:
3476 case icSigColorantTableOutTag:
3477 if (!SaveColorantTable((LPcmsNAMEDCOLORLIST) Data, Icc)) return FALSE;
3478 break;
3481 case icSigChromaticAdaptationTag:
3482 if (!SaveXYZArray(3, (LPcmsCIEXYZ) Data, Icc)) return FALSE;
3483 break;
3485 default:
3486 return FALSE;
3489 Icc -> TagSizes[i] = (Icc ->UsedSpace - Begin);
3494 return TRUE;
3499 // Add tags to profile structure
3501 LCMSBOOL LCMSEXPORT cmsAddTag(cmsHPROFILE hProfile, icTagSignature sig, const void* Tag)
3503 LCMSBOOL rc;
3505 switch (sig) {
3507 case icSigCharTargetTag:
3508 case icSigCopyrightTag:
3509 case icSigProfileDescriptionTag:
3510 case icSigDeviceMfgDescTag:
3511 case icSigDeviceModelDescTag:
3512 rc = _cmsAddTextTag(hProfile, sig, (const char*) Tag);
3513 break;
3515 case icSigRedColorantTag:
3516 case icSigGreenColorantTag:
3517 case icSigBlueColorantTag:
3518 case icSigMediaWhitePointTag:
3519 case icSigMediaBlackPointTag:
3520 rc = _cmsAddXYZTag(hProfile, sig, (const cmsCIEXYZ*) Tag);
3521 break;
3523 case icSigRedTRCTag:
3524 case icSigGreenTRCTag:
3525 case icSigBlueTRCTag:
3526 case icSigGrayTRCTag:
3527 rc = _cmsAddGammaTag(hProfile, sig, (LPGAMMATABLE) Tag);
3528 break;
3530 case icSigAToB0Tag:
3531 case icSigAToB1Tag:
3532 case icSigAToB2Tag:
3533 case icSigBToA0Tag:
3534 case icSigBToA1Tag:
3535 case icSigBToA2Tag:
3536 case icSigGamutTag:
3537 case icSigPreview0Tag:
3538 case icSigPreview1Tag:
3539 case icSigPreview2Tag:
3540 rc = _cmsAddLUTTag(hProfile, sig, Tag);
3541 break;
3543 case icSigChromaticityTag:
3544 rc = _cmsAddChromaticityTag(hProfile, sig, (LPcmsCIExyYTRIPLE) Tag);
3545 break;
3547 case icSigProfileSequenceDescTag:
3548 rc = _cmsAddSequenceDescriptionTag(hProfile, sig, (LPcmsSEQ) Tag);
3549 break;
3551 case icSigNamedColor2Tag:
3552 rc = _cmsAddNamedColorTag(hProfile, sig, (LPcmsNAMEDCOLORLIST) Tag);
3553 break;
3555 case icSigCalibrationDateTimeTag:
3556 rc = _cmsAddDateTimeTag(hProfile, sig, (struct tm*) Tag);
3557 break;
3559 case icSigColorantTableTag:
3560 case icSigColorantTableOutTag:
3561 rc = _cmsAddColorantTableTag(hProfile, sig, (LPcmsNAMEDCOLORLIST) Tag);
3562 break;
3565 case icSigChromaticAdaptationTag:
3566 rc = _cmsAddChromaticAdaptationTag(hProfile, sig, (const cmsCIEXYZ*) Tag);
3567 break;
3569 default:
3570 cmsSignalError(LCMS_ERRC_ABORTED, "cmsAddTag: Tag '%x' is unsupported", sig);
3571 return FALSE;
3574 // Check for critical tags
3576 switch (sig) {
3578 case icSigMediaWhitePointTag:
3579 case icSigMediaBlackPointTag:
3580 case icSigChromaticAdaptationTag:
3582 ReadCriticalTags((LPLCMSICCPROFILE) hProfile);
3583 break;
3585 default:;
3588 return rc;
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;
3598 LCMSBOOL rc;
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));
3621 return rc;
3624 CleanUp:
3626 Icc ->Close(Icc);
3627 unlink(FileName);
3628 CopyMemory(Icc, &Keep, sizeof(LCMSICCPROFILE));
3629 return FALSE;
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;
3651 if (!MemPtr) {
3653 // update BytesSaved so caller knows how many bytes are needed for MemPtr
3654 *BytesNeeded = Icc ->UsedSpace;
3655 CopyMemory(Icc, &Keep, sizeof(LCMSICCPROFILE));
3656 return TRUE;
3659 if (*BytesNeeded < Icc ->UsedSpace) {
3661 // need at least UsedSpace in MemPtr to continue
3662 CopyMemory(Icc, &Keep, sizeof(LCMSICCPROFILE));
3663 return FALSE;
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;
3677 Icc ->Close(Icc);
3678 CopyMemory(Icc, &Keep, sizeof(LCMSICCPROFILE));
3679 return TRUE;
3681 CleanUp:
3683 Icc ->Close(Icc);
3684 CopyMemory(Icc, &Keep, sizeof(LCMSICCPROFILE));
3685 return FALSE;