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.
29 This module provides conversion stages for handling intents.
31 The chain of evaluation in a transform is:
35 |From | |From | |Conversion | |Preview | |Gamut | |Conversion | |To | |To |
36 |Input|->|Device|->|Stage 1 |->|handling|->|Checking|->|Stage 2 |->|Device|->|output |
38 -------- ------- ------------- --------- ---------- ------------- ------- ---------
40 AToB0 prew0 gamut BToA0
41 Formatting LUT Adjusting LUT LUT Adjusting LUT Formatting
42 Intent Intent 1 intent intent Intent 2 Intent
45 Some of these LUT may be missing
47 There are two intents involved here, the intent of the transform itself, and the
48 intent the proof is being done, if is the case. Since the first intent is to be
49 applied to preview, is the proofing intent. The second intent identifies the
50 transform intent. Input data of any stage is taked as relative colorimetric
54 NOTES: V4 states than perceptual & saturation intents between mixed v2 & v4 profiles should
55 scale PCS from a black point equal to ZERO in v2 profiles to the reference media black of
56 perceptual v4 PCS. Since I found many v2 profiles to be using a perceptual intent with black
57 point not zero at all, I'm implementing that as a black point compensation from whatever
58 black from perceptal intent to the reference media black for v4 profiles.
65 int cdecl cmsChooseCnvrt(int Absolute
,
66 int Phase1
, LPcmsCIEXYZ BlackPointIn
,
67 LPcmsCIEXYZ WhitePointIn
,
68 LPcmsCIEXYZ IlluminantIn
,
69 LPMAT3 ChromaticAdaptationMatrixIn
,
71 int Phase2
, LPcmsCIEXYZ BlackPointOut
,
72 LPcmsCIEXYZ WhitePointOut
,
73 LPcmsCIEXYZ IlluminantOut
,
74 LPMAT3 ChromaticAdaptationMatrixOut
,
76 int DoBlackPointCompensation
,
77 double AdaptationState
,
79 LPWMAT3 wm
, LPWVEC3 wof
);
82 // -------------------------------------------------------------------------
86 LCMSAPI LPcmsCIEXYZ LCMSEXPORT
cmsD50_XYZ(void)
88 static cmsCIEXYZ D50XYZ
= {D50X
, D50Y
, D50Z
};
93 LCMSAPI LPcmsCIExyY LCMSEXPORT
cmsD50_xyY(void)
95 static cmsCIExyY D50xyY
;
96 cmsXYZ2xyY(&D50xyY
, cmsD50_XYZ());
102 // ---------------- From LUT to LUT --------------------------
105 // Calculate m, offset Relativ -> Absolute undoing any chromatic
106 // adaptation done by the profile.
109 #pragma warning(disable : 4100 4505)
114 // join scalings to obtain:
115 // relative input to absolute and then to relative output
118 void Rel2RelStepAbsCoefs(double AdaptationState
,
120 LPcmsCIEXYZ BlackPointIn
,
121 LPcmsCIEXYZ WhitePointIn
,
122 LPcmsCIEXYZ IlluminantIn
,
123 LPMAT3 ChromaticAdaptationMatrixIn
,
125 LPcmsCIEXYZ BlackPointOut
,
126 LPcmsCIEXYZ WhitePointOut
,
127 LPcmsCIEXYZ IlluminantOut
,
128 LPMAT3 ChromaticAdaptationMatrixOut
,
133 VEC3 WtPtIn
, WtPtInAdapted
;
134 VEC3 WtPtOut
, WtPtOutAdapted
;
135 MAT3 Scale
, m1
, m2
, m3
;
137 VEC3init(&WtPtIn
, WhitePointIn
->X
, WhitePointIn
->Y
, WhitePointIn
->Z
);
138 MAT3eval(&WtPtInAdapted
, ChromaticAdaptationMatrixIn
, &WtPtIn
);
140 VEC3init(&WtPtOut
, WhitePointOut
->X
, WhitePointOut
->Y
, WhitePointOut
->Z
);
141 MAT3eval(&WtPtOutAdapted
, ChromaticAdaptationMatrixOut
, &WtPtOut
);
143 VEC3init(&Scale
.v
[0], WtPtInAdapted
.n
[0] / WtPtOutAdapted
.n
[0], 0, 0);
144 VEC3init(&Scale
.v
[1], 0, WtPtInAdapted
.n
[1] / WtPtOutAdapted
.n
[1], 0);
145 VEC3init(&Scale
.v
[2], 0, 0, WtPtInAdapted
.n
[2] / WtPtOutAdapted
.n
[2]);
150 if (AdaptationState
== 1.0) {
152 // Observer is fully adapted. Keep chromatic adaptation
154 CopyMemory(m
, &Scale
, sizeof(MAT3
));
159 // Observer is not adapted, undo the chromatic adaptation
160 m1
= *ChromaticAdaptationMatrixIn
;
161 MAT3inverse(&m1
, &m2
);
163 MAT3per(&m3
, &m2
, &Scale
);
164 MAT3per(m
, &m3
, ChromaticAdaptationMatrixOut
);
168 VEC3init(of
, 0.0, 0.0, 0.0);
173 // The (in)famous black point compensation. Right now implemented as
174 // a linear scaling in XYZ
177 void ComputeBlackPointCompensationFactors(LPcmsCIEXYZ BlackPointIn
,
178 LPcmsCIEXYZ WhitePointIn
,
179 LPcmsCIEXYZ IlluminantIn
,
180 LPcmsCIEXYZ BlackPointOut
,
181 LPcmsCIEXYZ WhitePointOut
,
182 LPcmsCIEXYZ IlluminantOut
,
187 cmsCIEXYZ RelativeBlackPointIn
, RelativeBlackPointOut
;
188 double ax
, ay
, az
, bx
, by
, bz
, tx
, ty
, tz
;
190 // At first, convert both black points to relative.
192 cmsAdaptToIlluminant(&RelativeBlackPointIn
, WhitePointIn
, IlluminantIn
, BlackPointIn
);
193 cmsAdaptToIlluminant(&RelativeBlackPointOut
, WhitePointOut
, IlluminantOut
, BlackPointOut
);
195 // Now we need to compute a matrix plus an offset m and of such of
196 // [m]*bpin + off = bpout
197 // [m]*D50 + off = D50
199 // This is a linear scaling in the form ax+b, where
200 // a = (bpout - D50) / (bpin - D50)
201 // b = - D50* (bpout - bpin) / (bpin - D50)
204 tx
= RelativeBlackPointIn
.X
- IlluminantIn
->X
;
205 ty
= RelativeBlackPointIn
.Y
- IlluminantIn
->Y
;
206 tz
= RelativeBlackPointIn
.Z
- IlluminantIn
->Z
;
208 ax
= (RelativeBlackPointOut
.X
- IlluminantOut
->X
) / tx
;
209 ay
= (RelativeBlackPointOut
.Y
- IlluminantOut
->Y
) / ty
;
210 az
= (RelativeBlackPointOut
.Z
- IlluminantOut
->Z
) / tz
;
212 bx
= - IlluminantOut
-> X
* (RelativeBlackPointOut
.X
- RelativeBlackPointIn
.X
) / tx
;
213 by
= - IlluminantOut
-> Y
* (RelativeBlackPointOut
.Y
- RelativeBlackPointIn
.Y
) / ty
;
214 bz
= - IlluminantOut
-> Z
* (RelativeBlackPointOut
.Z
- RelativeBlackPointIn
.Z
) / tz
;
223 VEC3init(of
, bx
, by
, bz
);
227 // Return TRUE if both m and of are empy -- "m" being identity and "of" being 0
230 LCMSBOOL
IdentityParameters(LPWMAT3 m
, LPWVEC3 of
)
234 VEC3initF(&wv0
, 0, 0, 0);
236 if (!MAT3isIdentity(m
, 0.00001)) return FALSE
;
237 if (!VEC3equal(of
, &wv0
, 0.00001)) return FALSE
;
245 // ----------------------------------------- Inter PCS conversions
247 // XYZ to XYZ linear scaling. Aso used on Black point compensation
250 void XYZ2XYZ(WORD In
[], WORD Out
[], LPWMAT3 m
, LPWVEC3 of
)
259 MAT3evalW(&r
, m
, &a
);
261 Out
[0] = _cmsClampWord((r
.n
[VX
] + of
->n
[VX
]) >> 1);
262 Out
[1] = _cmsClampWord((r
.n
[VY
] + of
->n
[VY
]) >> 1);
263 Out
[2] = _cmsClampWord((r
.n
[VZ
] + of
->n
[VZ
]) >> 1);
267 // XYZ to Lab, scaling first
270 void XYZ2Lab(WORD In
[], WORD Out
[], LPWMAT3 m
, LPWVEC3 of
)
274 XYZ2XYZ(In
, XYZ
, m
, of
);
275 cmsXYZ2LabEncoded(XYZ
, Out
);
278 // Lab to XYZ, then scalling
281 void Lab2XYZ(WORD In
[], WORD Out
[], LPWMAT3 m
, LPWVEC3 of
)
285 cmsLab2XYZEncoded(In
, XYZ
);
286 XYZ2XYZ(XYZ
, Out
, m
, of
);
289 // Lab to XYZ, scalling and then, back to Lab
292 void Lab2XYZ2Lab(WORD In
[], WORD Out
[], LPWMAT3 m
, LPWVEC3 of
)
294 WORD XYZ
[3], XYZ2
[3];
296 cmsLab2XYZEncoded(In
, XYZ
);
297 XYZ2XYZ(XYZ
, XYZ2
, m
, of
);
298 cmsXYZ2LabEncoded(XYZ2
, Out
);
301 // ------------------------------------------------------------------
303 // Dispatcher for XYZ Relative LUT
306 int FromXYZRelLUT(int Absolute
,
307 LPcmsCIEXYZ BlackPointIn
,
308 LPcmsCIEXYZ WhitePointIn
,
309 LPcmsCIEXYZ IlluminantIn
,
310 LPMAT3 ChromaticAdaptationMatrixIn
,
312 int Phase2
, LPcmsCIEXYZ BlackPointOut
,
313 LPcmsCIEXYZ WhitePointOut
,
314 LPcmsCIEXYZ IlluminantOut
,
315 LPMAT3 ChromaticAdaptationMatrixOut
,
317 int DoBlackPointCompensation
,
318 double AdaptationState
,
325 // From relative XYZ to Relative XYZ.
331 // From input relative to absolute, and then
332 // back to output relative
334 Rel2RelStepAbsCoefs(AdaptationState
,
338 ChromaticAdaptationMatrixIn
,
342 ChromaticAdaptationMatrixOut
,
349 // XYZ Relative to XYZ relative, no op required
351 if (DoBlackPointCompensation
) {
354 ComputeBlackPointCompensationFactors(BlackPointIn
,
367 // From relative XYZ to Relative Lab
371 // First pass XYZ to absolute, then to relative and
372 // finally to Lab. I use here D50 for output in order
373 // to prepare the "to Lab" conversion.
378 Rel2RelStepAbsCoefs(AdaptationState
,
382 ChromaticAdaptationMatrixIn
,
386 ChromaticAdaptationMatrixOut
,
394 // Just Convert to Lab
397 VEC3init(of
, 0, 0, 0);
400 if (DoBlackPointCompensation
) {
402 ComputeBlackPointCompensationFactors(BlackPointIn
,
414 default: return FALSE
;
423 // From Lab Relative type LUT
426 int FromLabRelLUT(int Absolute
,
427 LPcmsCIEXYZ BlackPointIn
,
428 LPcmsCIEXYZ WhitePointIn
,
429 LPcmsCIEXYZ IlluminantIn
,
430 LPMAT3 ChromaticAdaptationMatrixIn
,
432 int Phase2
, LPcmsCIEXYZ BlackPointOut
,
433 LPcmsCIEXYZ WhitePointOut
,
434 LPcmsCIEXYZ IlluminantOut
,
435 LPMAT3 ChromaticAdaptationMatrixOut
,
437 int DoBlackPointCompensation
,
438 double AdaptationState
,
446 // From Lab Relative to XYZ Relative, very usual case
450 if (Absolute
) { // Absolute intent
452 // From lab relative, to XYZ absolute, and then,
453 // back to XYZ relative
455 Rel2RelStepAbsCoefs(AdaptationState
,
459 ChromaticAdaptationMatrixIn
,
463 ChromaticAdaptationMatrixOut
,
471 // From Lab relative, to XYZ relative.
474 if (DoBlackPointCompensation
) {
476 ComputeBlackPointCompensationFactors(BlackPointIn
,
494 // First pass to XYZ using the input illuminant
495 // * InIlluminant / D50, then to absolute. Then
496 // to relative, but for input
498 Rel2RelStepAbsCoefs(AdaptationState
,
500 WhitePointIn
, IlluminantIn
,
501 ChromaticAdaptationMatrixIn
,
503 WhitePointOut
, cmsD50_XYZ(),
504 ChromaticAdaptationMatrixOut
,
509 { // Lab -> Lab relative don't need any adjust unless
510 // black point compensation
513 if (DoBlackPointCompensation
) {
516 ComputeBlackPointCompensationFactors(BlackPointIn
,
530 default: return FALSE
;
537 // This function does calculate the necessary conversion operations
538 // needed from transpassing data from a LUT to a LUT. The conversion
539 // is modeled as a pointer of function and two coefficients, a and b
540 // The function is actually called only if not null pointer is provided,
541 // and the two paramaters are passed in. There are several types of
542 // conversions, but basically they do a linear scalling and a interchange
548 int cmsChooseCnvrt(int Absolute
,
549 int Phase1
, LPcmsCIEXYZ BlackPointIn
,
550 LPcmsCIEXYZ WhitePointIn
,
551 LPcmsCIEXYZ IlluminantIn
,
552 LPMAT3 ChromaticAdaptationMatrixIn
,
554 int Phase2
, LPcmsCIEXYZ BlackPointOut
,
555 LPcmsCIEXYZ WhitePointOut
,
556 LPcmsCIEXYZ IlluminantOut
,
557 LPMAT3 ChromaticAdaptationMatrixOut
,
559 int DoBlackPointCompensation
,
560 double AdaptationState
,
562 LPWMAT3 wm
, LPWVEC3 wof
)
571 VEC3init(&of
, 0, 0, 0);
575 // Input LUT is giving XYZ relative values.
577 case XYZRel
: rc
= FromXYZRelLUT(Absolute
,
581 ChromaticAdaptationMatrixIn
,
586 ChromaticAdaptationMatrixOut
,
587 DoBlackPointCompensation
,
594 // Input LUT is giving Lab relative values
596 case LabRel
: rc
= FromLabRelLUT(Absolute
,
600 ChromaticAdaptationMatrixIn
,
605 ChromaticAdaptationMatrixOut
,
606 DoBlackPointCompensation
,
614 // Unrecognized combination
616 default: cmsSignalError(LCMS_ERRC_ABORTED
, "(internal) Phase error");
624 // Do some optimization -- discard conversion if identity parameters.
626 if (*fn1
== XYZ2XYZ
|| *fn1
== Lab2XYZ2Lab
) {
628 if (IdentityParameters(wm
, wof
))