1 //
2 // SPDX-License-Identifier: BSD-3-Clause
3 // Copyright (c) Contributors to the OpenEXR Project.
4 //
5 
6 //-----------------------------------------------------------------------------
7 //
8 //	CIE (x,y) chromaticities, and conversions between
9 //	RGB tiples and CIE XYZ tristimulus values.
10 //
11 //-----------------------------------------------------------------------------
12 
13 #include <ImfChromaticities.h>
14 #include "ImfNamespace.h"
15 #include <string.h>
16 
17 #include <stdexcept>
18 #include <float.h>
19 
20 #if defined(_MSC_VER)
21 // suppress warning about non-exported base classes
22 #pragma warning (disable : 4251)
23 #endif
24 
25 
26 OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER
27 
28 
Chromaticities(const IMATH_NAMESPACE::V2f & red,const IMATH_NAMESPACE::V2f & green,const IMATH_NAMESPACE::V2f & blue,const IMATH_NAMESPACE::V2f & white)29 Chromaticities::Chromaticities (const IMATH_NAMESPACE::V2f &red,
30 				const IMATH_NAMESPACE::V2f &green,
31 				const IMATH_NAMESPACE::V2f &blue,
32 				const IMATH_NAMESPACE::V2f &white)
33 :
34     red (red),
35     green (green),
36     blue (blue),
37     white (white)
38 {
39     // empty
40 }
41 
42 
43 bool
operator ==(const Chromaticities & c) const44 Chromaticities::operator == (const Chromaticities & c) const
45 {
46     return red == c.red && green == c.green && blue == c.blue && white == c.white;
47 }
48 
49 
50 bool
operator !=(const Chromaticities & c) const51 Chromaticities::operator != (const Chromaticities & c) const
52 {
53     return red != c.red || green != c.green || blue != c.blue || white != c.white;
54 }
55 
56 
57 IMATH_NAMESPACE::M44f
RGBtoXYZ(const Chromaticities & chroma,float Y)58 RGBtoXYZ (const Chromaticities &chroma, float Y)
59 {
60     //
61     // For an explanation of how the color conversion matrix is derived,
62     // see Roy Hall, "Illumination and Color in Computer Generated Imagery",
63     // Springer-Verlag, 1989, chapter 3, "Perceptual Response"; and
64     // Charles A. Poynton, "A Technical Introduction to Digital Video",
65     // John Wiley & Sons, 1996, chapter 7, "Color science for video".
66     //
67 
68     //
69     // X and Z values of RGB value (1, 1, 1), or "white"
70     //
71 
72     // prevent a division that rounds to zero
73     if (std::abs(chroma.white.y) <= 1.f && std::abs(chroma.white.x * Y) >= std::abs(chroma.white.y) * FLT_MAX)
74     {
75         throw std::invalid_argument("Bad chromaticities: white.y cannot be zero");
76     }
77 
78     float X = chroma.white.x * Y / chroma.white.y;
79     float Z = (1 - chroma.white.x - chroma.white.y) * Y / chroma.white.y;
80 
81     //
82     // Scale factors for matrix rows, compute numerators and common denominator
83     //
84 
85     float d = chroma.red.x   * (chroma.blue.y  - chroma.green.y) +
86 	      chroma.blue.x  * (chroma.green.y - chroma.red.y) +
87 	      chroma.green.x * (chroma.red.y   - chroma.blue.y);
88 
89 
90 
91     float SrN = (X * (chroma.blue.y - chroma.green.y) -
92 	        chroma.green.x * (Y * (chroma.blue.y - 1) +
93 		chroma.blue.y  * (X + Z)) +
94 		chroma.blue.x  * (Y * (chroma.green.y - 1) +
95 		chroma.green.y * (X + Z)));
96 
97 
98     float SgN = (X * (chroma.red.y - chroma.blue.y) +
99 		chroma.red.x   * (Y * (chroma.blue.y - 1) +
100 		chroma.blue.y  * (X + Z)) -
101 		chroma.blue.x  * (Y * (chroma.red.y - 1) +
102 		chroma.red.y   * (X + Z)));
103 
104     float SbN = (X * (chroma.green.y - chroma.red.y) -
105 		chroma.red.x   * (Y * (chroma.green.y - 1) +
106 		chroma.green.y * (X + Z)) +
107 		chroma.green.x * (Y * (chroma.red.y - 1) +
108 		chroma.red.y   * (X + Z)));
109 
110 
111     if ( std::abs(d)<1.f && (std::abs(SrN) >= std::abs(d)* FLT_MAX || std::abs(SgN) >= std::abs(d)* FLT_MAX || std::abs(SbN) >= std::abs(d)* FLT_MAX) )
112     {
113         // cannot generate matrix if all RGB primaries have the same y value
114         // or if they all have the an x value of zero
115         // in both cases, the primaries are colinear, which makes them unusable
116         throw std::invalid_argument("Bad chromaticities: RGBtoXYZ matrix is degenerate");
117     }
118 
119 
120 
121     float Sr = SrN / d;
122     float Sg = SgN / d;
123     float Sb = SbN / d;
124 
125 
126     //
127     // Assemble the matrix
128     //
129 
130     IMATH_NAMESPACE::M44f M;
131 
132     M[0][0] = Sr * chroma.red.x;
133     M[0][1] = Sr * chroma.red.y;
134     M[0][2] = Sr * (1 - chroma.red.x - chroma.red.y);
135 
136     M[1][0] = Sg * chroma.green.x;
137     M[1][1] = Sg * chroma.green.y;
138     M[1][2] = Sg * (1 - chroma.green.x - chroma.green.y);
139 
140     M[2][0] = Sb * chroma.blue.x;
141     M[2][1] = Sb * chroma.blue.y;
142     M[2][2] = Sb * (1 - chroma.blue.x - chroma.blue.y);
143 
144     return M;
145 }
146 
147 
148 IMATH_NAMESPACE::M44f
XYZtoRGB(const Chromaticities & chroma,float Y)149 XYZtoRGB (const Chromaticities &chroma, float Y)
150 {
151     return RGBtoXYZ (chroma, Y).inverse();
152 }
153 
154 
155 OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_EXIT
156