1 | // tColour.h  |
2 | //  |
3 | // Colour and pixel classes. There are classes for:  |
4 | // * A 32 bit colour. 4 unsigned 8-bit integer components (rgb + alpha).  |
5 | // * A 96 bit colour. 3 32-bit float components.  |
6 | // * A 128 bit colour. 4 32-bit float components (rgb + alpha).  |
7 | //  |
8 | // Copyright (c) 2006, 2011, 2017, 2020 Tristan Grimmer.  |
9 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby  |
10 | // granted, provided that the above copyright notice and this permission notice appear in all copies.  |
11 | //  |
12 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL  |
13 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,  |
14 | // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN  |
15 | // AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR  |
16 | // PERFORMANCE OF THIS SOFTWARE.  |
17 |   |
18 | #pragma once  |
19 | #include <Foundation/tFundamentals.h>  |
20 | #include "Math/tVector3.h"  |
21 | #include "Math/tVector4.h"  |
22 | class tColouri;  |
23 | class tColourf;  |
24 | class tColour3f;  |
25 |   |
26 |   |
27 | namespace tMath  |
28 | {  |
29 | // Colour space conversions. The integer versions accept angle modes of Degrees and Norm256 only. The angle mode  |
30 | // determines the range of the hue. Degrees means angles are in [0, 360). Norm256 means angles are in [0, 256).  |
31 | // Saturation and value are both in [0, 256) for the integer conversion functions.  |
32 | void tRGBToHSV(int& h, int& s, int& v, int r, int g, int b, tAngleMode = tMath::tAngleMode::Degrees);  |
33 | void tHSVToRGB(int& r, int& g, int& b, int h, int s, int v, tMath::tAngleMode = tMath::tAngleMode::Degrees);  |
34 |   |
35 | // The floating point colour space conversion functions accept any angle mode. Radians mean angles are in [0.0, 2Pi).  |
36 | // Degrees means angles are in [0.0, 360.0). Norm256 means angles are in [0.0, 256.0). NormOne means angles are  |
37 | // in [0.0, 1.0].  |
38 | void tRGBToHSV(float& h, float& s, float& v, float r, float g, float b, tMath::tAngleMode = tMath::tAngleMode::Radians);  |
39 | void tHSVToRGB(float& r, float& g, float& b, float h, float s, float v, tMath::tAngleMode = tMath::tAngleMode::Radians);  |
40 |   |
41 | // Convert a standard web colour name (as may be found in rgb.txt for example) into a 32bit RGBA tColouri.  |
42 | tColouri tGetColour(const char* colourName);  |
43 |   |
44 | // Alpha is ignored for these colour difference functions.  |
45 | float tColourDiffEuclideanSq(const tColouri& a, const tColouri& b); // Returns value E [0.0, 195075.0]  |
46 | float tColourDiffEuclidean(const tColouri& a, const tColouri& b); // Returns value E [0.0, 441.672956]  |
47 | float tColourDiffRedmean(const tColouri& a, const tColouri& b); // Returns value E [0.0, 764.8340]  |
48 |   |
49 | enum ColourChannel  |
50 | {  |
51 | ColourChannel_R = 1 << 0,  |
52 | ColourChannel_G = 1 << 1,  |
53 | ColourChannel_B = 1 << 2,  |
54 | ColourChannel_A = 1 << 3,  |
55 | ColourChannel_RGB = ColourChannel_R | ColourChannel_G | ColourChannel_B,  |
56 | ColourChannel_RGBA = ColourChannel_R | ColourChannel_G | ColourChannel_B | ColourChannel_A,  |
57 | ColourChannel_All = ColourChannel_RGBA  |
58 | };  |
59 | }  |
60 |   |
61 |   |
62 | // The tColouri class represents a colour in 32 bits and is made of 4 unsigned byte-size integers in the order RGBA.  |
63 | class tColouri  |
64 | {  |
65 | public:  |
66 | tColouri() /* Does NOT set the colour values. */ { }  |
67 | tColouri(const tColouri& c) : BP(c.BP) { }  |
68 | tColouri(int r, int g, int b, int a = 0xFF) { R = tMath::tClamp(r, 0, 0xFF); G = tMath::tClamp(g, 0, 0xFF); B = tMath::tClamp(b, 0, 0xFF); A = tMath::tClamp(a, 0, 0xFF); }  |
69 | tColouri(uint8 r, uint8 g, uint8 b, uint8 a = 0xFF) : R(r), G(g), B(b), A(a) { }  |
70 | tColouri(uint32 bits) : BP(bits) { }  |
71 | tColouri(const tColourf& c) { Set(c); }  |
72 | tColouri(float r, float g, float b, float a = 1.0f) { Set(r, g, b, a); }  |
73 | tColouri(const float* src) { Set(src); }  |
74 |   |
75 | void Set(const tColouri& c) { BP = c.BP; }  |
76 | void Set(int r, int g, int b, int a = 255) { R = uint8(r); G = uint8(g); B = uint8(b); A = uint8(a); }  |
77 | void Set(const tColourf& c);  |
78 | void Set(const float* src) { SetR(src[0]); SetG(src[1]); SetB(src[2]); SetA(src[3]); }  |
79 |   |
80 | // The floating point set methods use a range of [0.0, 1.0] for each component.  |
81 | void Set(float r, float g, float b, float a = 1.0f) { SetR(r); SetG(g); SetB(b); SetA(a); }  |
82 | void SetR(float r) { R = tMath::tClamp( tMath::tFloatToInt(r*255.0f), 0, 0xFF ); }  |
83 | void SetG(float g) { G = tMath::tClamp( tMath::tFloatToInt(g*255.0f), 0, 0xFF ); }  |
84 | void SetB(float b) { B = tMath::tClamp( tMath::tFloatToInt(b*255.0f), 0, 0xFF ); }  |
85 | void SetA(float a) { A = tMath::tClamp( tMath::tFloatToInt(a*255.0f), 0, 0xFF ); }  |
86 |   |
87 | // The floating point get methods use a range of [0.0, 1.0] for each component.  |
88 | float GetR() const { return float(R) / 255.0f; }  |
89 | float GetG() const { return float(G) / 255.0f; }  |
90 | float GetB() const { return float(B) / 255.0f; }  |
91 | float GetA() const { return float(A) / 255.0f; }  |
92 | void Get(float* dest) const { dest[0] = GetR(); dest[1] = GetG(); dest[2] = GetB(); dest[3] = GetA(); }  |
93 | void Get(tMath::tVector3& dest) const { dest.x = GetR(); dest.y = GetG(); dest.z = GetB(); }  |
94 | void Get(tMath::tVector4& dest) const { dest.x = GetR(); dest.y = GetG(); dest.z = GetB(); dest.w = GetA(); }  |
95 | void Get(float& r, float&g, float& b, float& a) const { r = GetR(); g = GetG(); b = GetB(); a = GetA(); }  |
96 |   |
97 | // These floating point get methods use a range of [0.0, 255.0] for each component.  |
98 | float GetDenormR() const { return float(R); }  |
99 | float GetDenormG() const { return float(G); }  |
100 | float GetDenormB() const { return float(B); }  |
101 | float GetDenormA() const { return float(A); }  |
102 | void GetDenorm(float* dest) const { dest[0] = GetDenormR(); dest[1] = GetDenormG(); dest[2] = GetDenormB(); dest[3] = GetDenormA(); }  |
103 | void GetDenorm(tMath::tVector3& dest) const { dest.x = GetDenormR(); dest.y = GetDenormG(); dest.z = GetDenormB(); }  |
104 | void GetDenorm(tMath::tVector4& dest) const { dest.x = GetDenormR(); dest.y = GetDenormG(); dest.z = GetDenormB(); dest.w = GetDenormA(); }  |
105 | void GetDenorm(float& r, float&g, float& b, float& a) const { r = GetDenormR(); g = GetDenormG(); b = GetDenormB(); a = GetDenormA(); }  |
106 |   |
107 | void Get(tColouri& c) const { c.BP = BP; }  |
108 | void MakeZero() { R = 0x00; G = 0x00; B = 0x00; A = 0x00; }  |
109 | void MakeBlack() { R = 0x00; G = 0x00; B = 0x00; A = 0xFF; }  |
110 | void MakeWhite() { R = 0xFF; G = 0xFF; B = 0xFF; A = 0xFF; }  |
111 | void MakePink() { R = 0xFF; G = 0x80; B = 0x80; A = 0xFF; }  |
112 |   |
113 | void MakeRed() { R = 0xFF; G = 0x00; B = 0x00; A = 0xFF; }  |
114 | void MakeGreen() { R = 0x00; G = 0xFF; B = 0x00; A = 0xFF; }  |
115 | void MakeBlue() { R = 0x00; G = 0x00; B = 0xFF; A = 0xFF; }  |
116 |   |
117 | void MakeGrey() { R = 0x80; G = 0x80; B = 0x80; A = 0xFF; }  |
118 | void MakeLightGrey() { R = 0xC0; G = 0xC0; B = 0xC0; A = 0xFF; }  |
119 | void MakeDarkGrey() { R = 0x40; G = 0x40; B = 0x40; A = 0xFF;}  |
120 |   |
121 | void MakeCyan() { R = 0x00; G = 0xFF; B = 0xFF; A = 0xFF; }  |
122 | void MakeMagenta() { R = 0xFF; G = 0x00; B = 0xFF; A = 0xFF; }  |
123 | void MakeYellow() { R = 0xFF; G = 0xFF; B = 0x00; A = 0xFF; }  |
124 |   |
125 | // These querying calls ignore alpha.  |
126 | bool IsBlack() const { return ((R == 0x00) && (G == 0x00) && (B == 0x00)) ? true : false; }  |
127 | bool IsWhite() const { return ((R == 0xFF) && (G == 0xFF) && (B == 0xFF)) ? true : false; }  |
128 | bool IsRed() const { return ((R == 0xFF) && (G == 0x00) && (B == 0x00)) ? true : false; }  |
129 | bool IsGreen() const { return ((R == 0x00) && (G == 0xFF) && (B == 0x00)) ? true : false; }  |
130 | bool IsBlue() const { return ((R == 0x00) && (G == 0x00) && (B == 0xFF)) ? true : false; }  |
131 |   |
132 | // When using the HSV representation of a tColouri, the hue is in normalized angle units. See tAngleMode::Norm256.  |
133 | // Since only one byte is used, we divide the circle into 256 equal parts. All 4 values will be E [0, 255].  |
134 | // Consider using a tColoutf object when working in HSV space. It can more accurately represent the hue value  |
135 | // without as much loss in precision. See the tRGBToHSV function for retrieval of hue in different angle units.  |
136 | // Both of the functions below leave the alpha unchanged.  |
137 | void RGBToHSV(); // Assumes current values are RGB.  |
138 | void HSVToRGB(); // Assumes current values are HSV.  |
139 |   |
140 | bool Equal(const tColouri&, uint32 channels = tMath::ColourChannel_All) const;  |
141 | bool operator==(const tColouri& c) const { return (BP == c.BP); }  |
142 | bool operator!=(const tColouri& c) const { return (BP != c.BP); }  |
143 | tColouri& operator=(const tColouri& c) { BP = c.BP; return *this; }  |
144 |   |
145 | tColouri& operator*=(float f) { R = uint8(float(R)*f); G = uint8(float(G)*f); B = uint8(float(B)*f); A = uint8(float(A)*f); return *this; }  |
146 | const tColouri operator*(float f) const { tColouri res(*this); res *= f; return res; }  |
147 | tColouri& operator+=(const tColouri& c) { R += c.R; G += c.G; B += c.B; A += c.A; return *this; }  |
148 | const tColouri operator+(const tColouri& c) const { tColouri res(*this); res += c; return res; }  |
149 |   |
150 | // Predefined colours. Initialized using the C++11 aggregate initializer syntax. These may be used before main()  |
151 | // in normally (non-aggregate syntax) constructed objects.  |
152 | const static tColouri black;  |
153 | const static tColouri white;  |
154 | const static tColouri pink;  |
155 |   |
156 | const static tColouri red;  |
157 | const static tColouri green;  |
158 | const static tColouri blue;  |
159 |   |
160 | const static tColouri grey;  |
161 | const static tColouri lightgrey;  |
162 | const static tColouri darkgrey;  |
163 |   |
164 | const static tColouri cyan;  |
165 | const static tColouri magenta;  |
166 | const static tColouri yellow;  |
167 |   |
168 | const static tColouri transparent;  |
169 |   |
170 | union  |
171 | {  |
172 | struct { uint8 R, G, B, A; };  |
173 | struct { uint8 H, S, V, O; }; // O for opacity. Some compilers don't like a repeated A.  |
174 |   |
175 | // Bit Pattern member.  |
176 | // Accessing the colour as a 32 bit value using the BP member means you must take the machine's endianness into  |
177 | // account. This explains why the member isn't named something like RGBA. For example, in memory it's always in the  |
178 | // RGBA no matter what endianness, but on a little endian machine you'd access the blue component with something  |
179 | // like (colour.BP >> 16) % 0xFF  |
180 | uint32 BP;  |
181 |   |
182 | // Individual elements. Makes it easy to submit colours to OpenGL using glColor3ubv.  |
183 | uint8 E[4];  |
184 | };  |
185 | };  |
186 | typedef tColouri tPixel;  |
187 |   |
188 |   |
189 | // The tColourf class represents a colour in 4 floats and is made of 4 floats in the order RGBA.  |
190 | // The values of each float component are E [0.0, 1.0].  |
191 | class tColourf  |
192 | {  |
193 | public:  |
194 | tColourf() { }  |
195 | tColourf(const tColourf& src) { Set(src); }  |
196 | tColourf(float r, float g, float b, float a = 1.0f) { Set(r, g, b, a); }  |
197 | tColourf(const tMath::tVector3& c, float a = 1.0f) { Set(c, a); }  |
198 | tColourf(const tMath::tVector4& ca) { Set(ca); }  |
199 | tColourf(const tColouri& src) { Set(src); }  |
200 | tColourf(uint8 r, uint8 g, uint8 b, uint8 a = 0xFF) { Set(r, g, b, a); }  |
201 | tColourf(int r, int g, int b, int a = 255) { Set(r, g, b, a); }  |
202 |   |
203 | void Unset() { R = -1.0f; G = -1.0f; B = -1.0f; A = -1.0f; } // An unset colour has value (-1.0f, -1.0f, -1.0f, -1.0f).  |
204 | bool IsSet() const { if ((R != -1.0f) || (G != -1.0f) || (B != -1.0f) || (A != -1.0f)) return true; return false; } // Any set component means the whole colour is considered set.  |
205 | void Set(const tColourf& c) { BP0 = c.BP0; BP1 = c.BP1; }  |
206 | void Set(float r, float g, float b, float a = 1.0f) { R = r; G = g; B = b; A = a; }  |
207 | void Set(const float* src) { R = src[0]; G = src[1]; B = src[2]; A = src[3]; }  |
208 | void Set(const tMath::tVector3& c, float a = 1.0f) { R = c.x; G = c.y; B = c.z; A = a; }  |
209 | void Set(const tMath::tVector4& ca) { R = ca.x; G = ca.y; B = ca.z; A = ca.w; }  |
210 | void Set(const tColouri& c) { Set(float(c.R)/255.0f, float(c.G)/255.0f, float(c.B)/255.0f, float(c.A)/255.0f); }  |
211 | void Set(int r, int g, int b, int a = 255) { Set(float(r)/255.0f, float(g)/255.0f, float(b)/255.0f, float(a)/255.0f); }  |
212 | void SetR(int r) { R = float(r)/255.0f; }  |
213 | void SetG(int g) { G = float(g)/255.0f; }  |
214 | void SetB(int b) { B = float(b)/255.0f; }  |
215 | void SetA(int a) { A = float(a)/255.0f; }  |
216 |   |
217 | // The integer get and set methods use a range of [0, 255] for each component.  |
218 | int GetR() const { return tMath::tFloatToInt(R * 255.0f); }  |
219 | int GetG() const { return tMath::tFloatToInt(G * 255.0f); }  |
220 | int GetB() const { return tMath::tFloatToInt(B * 255.0f); }  |
221 | int GetA() const { return tMath::tFloatToInt(A * 255.0f); }  |
222 | void Get(int* dest) const { dest[0] = GetR(); dest[1] = GetG(); dest[2] = GetB(); dest[3] = GetA(); }  |
223 | void Get(tMath::tVector3& dest) const { dest.x = R; dest.y = G; dest.z = B; }  |
224 | void Get(tMath::tVector4& dest) const { dest.x = R; dest.y = G; dest.z = B; dest.w = A; }  |
225 | void Get(float& r, float&g, float& b, float& a) const { r = R; g = G; b = B; a = A; }  |
226 | void Get(tColourf& c) const { c.BP0 = BP0; c.BP1 = BP1;}  |
227 | void Saturate() { tMath::tiSaturate(R); tMath::tiSaturate(G); tMath::tiSaturate(B); tMath::tiSaturate(A); }  |
228 |   |
229 | void MakeBlack() { R = 0.0f; G = 0.0f; B = 0.0f; A = 1.0f; }  |
230 | void MakeWhite() { R = 1.0f; G = 1.0f; B = 1.0f; A = 1.0f; }  |
231 | void MakePink() { R = 1.0f; G = 0.5f; B = 0.5f; A = 1.0f; }  |
232 |   |
233 | void MakeRed() { R = 1.0f; G = 0.0f; B = 0.0f; A = 1.0f; }  |
234 | void MakeGreen() { R = 0.0f; G = 1.0f; B = 0.0f; A = 1.0f; }  |
235 | void MakeBlue() { R = 0.0f; G = 0.0f; B = 1.0f; A = 1.0f; }  |
236 |   |
237 | void MakeGrey() { R = 0.5f; G = 0.5f; B = 0.5f; A = 1.0f; }  |
238 | void MakeLightGrey() { R = 0.75f; G = 0.75f; B = 0.75f; A = 1.0f; }  |
239 | void MakeDarkGrey() { R = 0.25f; G = 0.25f; B = 0.25f; A = 1.0f;}  |
240 |   |
241 | void MakeCyan() { R = 0.0f; G = 1.0f; B = 1.0f; A = 1.0f; }  |
242 | void MakeMagenta() { R = 1.0f; G = 0.0f; B = 1.0f; A = 1.0f; }  |
243 | void MakeYellow() { R = 1.0f; G = 1.0f; B = 0.0f; A = 1.0f; }  |
244 |   |
245 | // These querying calls ignore alpha.  |
246 | bool IsBlack() const { return ((R == 0.0f) && (G == 0.0f) && (B == 0.0f)) ? true : false; }  |
247 | bool IsWhite() const { return ((R == 1.0f) && (G == 1.0f) && (B == 1.0f)) ? true : false; }  |
248 | bool IsRed() const { return ((R == 1.0f) && (G == 0.0f) && (B == 0.0f)) ? true : false; }  |
249 | bool IsGreen() const { return ((R == 0.0f) && (G == 1.0f) && (B == 0.0f)) ? true : false; }  |
250 | bool IsBlue() const { return ((R == 0.0f) && (G == 0.0f) && (B == 1.0f)) ? true : false; }  |
251 |   |
252 | // Colours in textures in files may be in Gamma space and ought to be converted to linear space before  |
253 | // lighting calculations are made. They should then be converted back to Gamma space before being displayed.  |
254 | // Gamma-space here should really be sRGB but we're just using an approximation by squaring (gamma=2) when the  |
255 | // average sRGB gamma should be 2.2. To do the conversion properly, the gamma varies with intensity from 1 to 2.4,  |
256 | // but, again, we're only approximating here.  |
257 | void ToLinearSpaceApprox() { R *= R; G *= G; B *= B; }  |
258 | void ToGammaSpaceApprox() { R = tMath::tSqrt(R); G = tMath::tSqrt(G); B = tMath::tSqrt(B); }  |
259 |   |
260 | // When using the HSV representation of a tColourf, the hue is in NormOne angle mode. See the tRGBToHSV and  |
261 | // tHSVToRGB functions if you wish to use different angle units. All the components (h, s, v, r, g, b, a) are in  |
262 | // [0.0, 1.0]. Both of the functions below leave the alpha unchanged.  |
263 | void RGBToHSV(); // Assumes current values are RGB.  |
264 | void HSVToRGB(); // Assumes current values are HSV.  |
265 |   |
266 | bool operator==(const tColourf& c) const { return ((BP0 == c.BP0) && (BP1 == c.BP1)); }  |
267 | bool operator!=(const tColourf& c) const { return ((BP0 != c.BP0) || (BP1 != c.BP1)); }  |
268 | tColourf& operator=(const tColourf& c) { BP0 = c.BP0; BP1 = c.BP1; return *this; }  |
269 | tColourf& operator*=(float f) { R *= f; G *= f; B *= f; A *= f; return *this; }  |
270 | const tColourf operator*(float f) const { tColourf res(*this); res *= f; return res; }  |
271 | tColourf& operator+=(const tColourf& c) { R += c.R; G += c.G; B += c.B; A += c.A; return *this; }  |
272 | const tColourf operator+(const tColourf& c) const { tColourf res(*this); res += c; return res; }  |
273 |   |
274 | // Predefined colours.  |
275 | const static tColourf invalid;  |
276 | const static tColourf black;  |
277 | const static tColourf white;  |
278 | const static tColourf hotpink;  |
279 |   |
280 | const static tColourf red;  |
281 | const static tColourf green;  |
282 | const static tColourf blue;  |
283 |   |
284 | const static tColourf grey;  |
285 | const static tColourf lightgrey;  |
286 | const static tColourf darkgrey;  |
287 |   |
288 | const static tColourf cyan;  |
289 | const static tColourf magenta;  |
290 | const static tColourf yellow;  |
291 |   |
292 | const static tColourf transparent;  |
293 |   |
294 | union  |
295 | {  |
296 | struct { float R, G, B, A; };  |
297 | struct { float H, S, V, O; };  |
298 | struct { uint64 BP0, BP1; }; // Bit Pattern.  |
299 | float E[4];  |
300 | };  |
301 | };  |
302 | typedef tColourf tColour;  |
303 |   |
304 |   |
305 | // The tColour3f class represents a colour in 3 floats and is made of 3 floats in the order RGB.  |
306 | // The values of each float component are E [0.0, 1.0].  |
307 | class tColour3f  |
308 | {  |
309 | public:  |
310 | tColour3f() { }  |
311 | tColour3f(const tColour3f& src) { Set(src); }  |
312 | tColour3f(float r, float g, float b) { Set(r, g, b); }  |
313 | tColour3f(const tMath::tVector3& c) { Set(c); }  |
314 | tColour3f(const tMath::tVector4& c) { Set(c); }  |
315 | tColour3f(const tColouri& src) { Set(src); }  |
316 | tColour3f(uint8 r, uint8 g, uint8 b) { Set(r, g, b); }  |
317 | tColour3f(int r, int g, int b) { Set(r, g, b); }  |
318 |   |
319 | void Unset() { R = -1.0f; G = -1.0f; B = -1.0f; } // An unset colour has value (-1.0f, -1.0f, -1.0f).  |
320 | bool IsSet() const { return ((R != -1.0f) || (G != -1.0f) || (B != -1.0f)); } // Any set component means the whole colour is considered set.  |
321 | void Set(const tColour3f& c) { R = c.R; G = c.G; B = c.B; }  |
322 | void Set(float r, float g, float b) { R = r; G = g; B = b; }  |
323 | void Set(const float* src) { R = src[0]; G = src[1]; B = src[2]; }  |
324 | void Set(const tMath::tVector3& c) { R = c.x; G = c.y; B = c.z; }  |
325 | void Set(const tMath::tVector4& c) { R = c.x; G = c.y; B = c.z; }  |
326 | void Set(const tColouri& c) { Set(float(c.R)/255.0f, float(c.G)/255.0f, float(c.B)/255.0f); }  |
327 | void Set(int r, int g, int b) { Set(float(r)/255.0f, float(g)/255.0f, float(b)/255.0f); }  |
328 | void SetR(int r) { R = float(r)/255.0f; }  |
329 | void SetG(int g) { G = float(g)/255.0f; }  |
330 | void SetB(int b) { B = float(b)/255.0f; }  |
331 |   |
332 | // The integer get and set methods use a range of [0, 255] for each component.  |
333 | int GetR() const { return tMath::tFloatToInt(R * 255.0f); }  |
334 | int GetG() const { return tMath::tFloatToInt(G * 255.0f); }  |
335 | int GetB() const { return tMath::tFloatToInt(B * 255.0f); }  |
336 | void Get(int* dest) const { dest[0] = GetR(); dest[1] = GetG(); dest[2] = GetB(); }  |
337 | void Get(tMath::tVector3& dest) const { dest.x = R; dest.y = G; dest.z = B; }  |
338 | void Get(tMath::tVector4& dest) const { dest.x = R; dest.y = G; dest.z = B; dest.w = 1.0f; }  |
339 | void Get(float& r, float&g, float& b) const { r = R; g = G; b = B; }  |
340 | void Get(tColour3f& c) const { c.R = R; c.G = G; c.B = B; }  |
341 |   |
342 | void MakeBlack() { R = 0.0f; G = 0.0f; B = 0.0f; }  |
343 | void MakeWhite() { R = 1.0f; G = 1.0f; B = 1.0f; }  |
344 | void MakePink() { R = 1.0f; G = 0.5f; B = 0.5f; }  |
345 |   |
346 | void MakeRed() { R = 1.0f; G = 0.0f; B = 0.0f; }  |
347 | void MakeGreen() { R = 0.0f; G = 1.0f; B = 0.0f; }  |
348 | void MakeBlue() { R = 0.0f; G = 0.0f; B = 1.0f; }  |
349 |   |
350 | void MakeGrey() { R = 0.5f; G = 0.5f; B = 0.5f; }  |
351 | void MakeLightGrey() { R = 0.75f; G = 0.75f; B = 0.75f; }  |
352 | void MakeDarkGrey() { R = 0.25f; G = 0.25f; B = 0.25f; }  |
353 |   |
354 | void MakeCyan() { R = 0.0f; G = 1.0f; B = 1.0f; }  |
355 | void MakeMagenta() { R = 1.0f; G = 0.0f; B = 1.0f; }  |
356 | void MakeYellow() { R = 1.0f; G = 1.0f; B = 0.0f; }  |
357 |   |
358 | // These querying calls ignore alpha.  |
359 | bool IsBlack() const { return ((R == 0.0f) && (G == 0.0f) && (B == 0.0f)); }  |
360 | bool IsWhite() const { return ((R == 1.0f) && (G == 1.0f) && (B == 1.0f)); }  |
361 | bool IsRed() const { return ((R == 1.0f) && (G == 0.0f) && (B == 0.0f)); }  |
362 | bool IsGreen() const { return ((R == 0.0f) && (G == 1.0f) && (B == 0.0f)); }  |
363 | bool IsBlue() const { return ((R == 0.0f) && (G == 0.0f) && (B == 1.0f)); }  |
364 |   |
365 | // Colours in textures in files may be in Gamma space and ought to be converted to linear space before  |
366 | // lighting calculations are made. They should then be converted back to Gamma space before being displayed.  |
367 | // Gamma-space here should really be sRGB but we're just using an approximation by squaring (gamma=2) when the  |
368 | // average sRGB gamma should be 2.2. To do the conversion properly, the gamma varies with intensity from 1 to 2.4,  |
369 | // but, again, we're only approximating here.  |
370 | void ToLinearSpaceApprox() { R *= R; G *= G; B *= B; }  |
371 | void ToGammaSpaceApprox() { R = tMath::tSqrt(R); G = tMath::tSqrt(G); B = tMath::tSqrt(B); }  |
372 |   |
373 | // When using the HSV representation of a tColourf, the hue is in NormOne angle mode. See the tRGBToHSV and  |
374 | // tHSVToRGB functions if you wish to use different angle units. All the components (h, s, v, r, g, b, a) are in  |
375 | // [0.0, 1.0]. Both of the functions below leave the alpha unchanged.  |
376 | void RGBToHSV(); // Assumes current values are RGB.  |
377 | void HSVToRGB(); // Assumes current values are HSV.  |
378 |   |
379 | bool operator==(const tColourf& c) const { return ((R == c.R) && (G == c.G) && (B == c.B)); }  |
380 | bool operator!=(const tColourf& c) const { return ((R != c.R) || (G != c.G) || (B != c.B)); }  |
381 | tColour3f& operator=(const tColour3f& c) { R = c.R; G = c.G; B = c.B; return *this; }  |
382 |   |
383 | // Predefined colours.  |
384 | const static tColour3f invalid;  |
385 | const static tColour3f black;  |
386 | const static tColour3f white;  |
387 | const static tColour3f hotpink;  |
388 |   |
389 | const static tColour3f red;  |
390 | const static tColour3f green;  |
391 | const static tColour3f blue;  |
392 |   |
393 | const static tColour3f grey;  |
394 | const static tColour3f lightgrey;  |
395 | const static tColour3f darkgrey;  |
396 |   |
397 | const static tColour3f cyan;  |
398 | const static tColour3f magenta;  |
399 | const static tColour3f yellow;  |
400 |   |
401 | union  |
402 | {  |
403 | struct { float R, G, B; };  |
404 | struct { float H, S, V; };  |
405 | float E[3];  |
406 | };  |
407 | };  |
408 | typedef tColour3f tColour3;  |
409 |   |
410 |   |
411 | // Implementation below this line.  |
412 |   |
413 |   |
414 | inline void tColouri::Set(const tColourf& c)  |
415 | {  |
416 | Set(c.R, c.G, c.B, c.A);  |
417 | }  |
418 |   |
419 |   |
420 | inline void tColouri::RGBToHSV()  |
421 | {  |
422 | int r = R;  |
423 | int g = G;  |
424 | int b = B;  |
425 | int h, s, v;  |
426 | tRGBToHSV(h, s, v, r, g, b, tMath::tAngleMode::Norm256);  |
427 | H = h;  |
428 | S = s;  |
429 | V = v;  |
430 | }  |
431 |   |
432 |   |
433 | inline void tColouri::HSVToRGB()  |
434 | {  |
435 | int h = H;  |
436 | int s = S;  |
437 | int v = V;  |
438 | int r, g, b;  |
439 | tHSVToRGB(r, g, b, h, s, v, tMath::tAngleMode::Norm256);  |
440 | R = r;  |
441 | G = g;  |
442 | B = b;  |
443 | }  |
444 |   |
445 |   |
446 | inline bool tColouri::Equal(const tColouri& colour, uint32 channels) const  |
447 | {  |
448 | if ((channels & tMath::ColourChannel_R) && (R != colour.R))  |
449 | return false;  |
450 |   |
451 | if ((channels & tMath::ColourChannel_G) && (R != colour.G))  |
452 | return false;  |
453 |   |
454 | if ((channels & tMath::ColourChannel_B) && (R != colour.B))  |
455 | return false;  |
456 |   |
457 | if ((channels & tMath::ColourChannel_A) && (R != colour.A))  |
458 | return false;  |
459 |   |
460 | return true;  |
461 | }  |
462 |   |
463 |   |
464 | inline void tColourf::RGBToHSV()  |
465 | {  |
466 | float r = R;  |
467 | float g = G;  |
468 | float b = B;  |
469 | tRGBToHSV(H, S, V, r, g, b, tMath::tAngleMode::NormOne);  |
470 | }  |
471 |   |
472 |   |
473 | inline void tColourf::HSVToRGB()  |
474 | {  |
475 | float h = H;  |
476 | float s = S;  |
477 | float v = V;  |
478 | tHSVToRGB(R, G, B, h, s, v, tMath::tAngleMode::NormOne);  |
479 | }  |
480 |   |
481 |   |
482 | inline float tMath::tColourDiffEuclideanSq(const tColouri& aa, const tColouri& bb)  |
483 | {  |
484 | tVector3 a; aa.GetDenorm(a);  |
485 | tVector3 b; bb.GetDenorm(b);  |
486 | return tDistBetweenSq(a, b);  |
487 | }  |
488 |   |
489 |   |
490 | inline float tMath::tColourDiffEuclidean(const tColouri& aa, const tColouri& bb)  |
491 | {  |
492 | tVector3 a; aa.GetDenorm(a);  |
493 | tVector3 b; bb.GetDenorm(b);  |
494 | return tDistBetween(a, b);  |
495 | }  |
496 | |