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