1// tColour.cpp 
2// 
3// Colour and pixel classes. Both a 32 bit integral representation as well as a 4 component floating point one can be 
4// found in this file. 
5// 
6// Copyright (c) 2006, 2011, 2017, 2020 Tristan Grimmer. 
7// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby 
8// granted, provided that the above copyright notice and this permission notice appear in all copies. 
9// 
10// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 
11// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 
12// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 
13// AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 
14// PERFORMANCE OF THIS SOFTWARE. 
15 
16#include <Foundation/tString.h> 
17#include <Foundation/tHash.h> 
18#include <Math/tColour.h> 
19using namespace tMath
20 
21 
22// Uses C++11 aggregate initialization syntax. 
23const tColouri tColouri::black = { 0x00, 0x00, 0x00 }; 
24const tColouri tColouri::white = { 0xFF, 0xFF, 0xFF }; 
25const tColouri tColouri::pink = { 0xFF, 0x80, 0x80 }; 
26const tColouri tColouri::red = { 0xFF, 0x00, 0x00 }; 
27const tColouri tColouri::green = { 0x00, 0xFF, 0x00 }; 
28const tColouri tColouri::blue = { 0x00, 0x00, 0xFF }; 
29const tColouri tColouri::grey = { 0x80, 0x80, 0x80 }; 
30const tColouri tColouri::lightgrey = { 0xC0, 0xC0, 0xC0 }; 
31const tColouri tColouri::darkgrey = { 0x40, 0x40, 0x40 }; 
32const tColouri tColouri::cyan = { 0x00, 0xFF, 0xFF }; 
33const tColouri tColouri::magenta = { 0xFF, 0x00, 0xFF }; 
34const tColouri tColouri::yellow = { 0xFF, 0xFF, 0x00 }; 
35const tColouri tColouri::transparent = { 0x00, 0x00, 0x00, 0x00 }; 
36 
37 
38const tColourf tColourf::invalid = { -1.0f, -1.0f, -1.0f, -1.0f }; 
39const tColourf tColourf::black = { 0.0f, 0.0f, 0.0f }; 
40const tColourf tColourf::white = { 1.0f, 1.0f, 1.0f }; 
41const tColourf tColourf::hotpink = { 1.0f, 0.5f, 0.5f }; 
42const tColourf tColourf::red = { 1.0f, 0.0f, 0.0f }; 
43const tColourf tColourf::green = { 0.0f, 1.0f, 0.0f }; 
44const tColourf tColourf::blue = { 0.0f, 0.0f, 1.0f }; 
45const tColourf tColourf::grey = { 0.5f, 0.5f, 0.5f }; 
46const tColourf tColourf::lightgrey = { 0.75f, 0.75f, 0.75f }; 
47const tColourf tColourf::darkgrey = { 0.25f, 0.25f, 0.25f }; 
48const tColourf tColourf::cyan = { 0.0f, 1.0f, 1.0f }; 
49const tColourf tColourf::magenta = { 1.0f, 0.0f, 1.0f }; 
50const tColourf tColourf::yellow = { 1.0f, 1.0f, 0.0f }; 
51const tColourf tColourf::transparent = { 0.0f, 0.0f, 0.0f, 0.0f }; 
52 
53 
54const tColour3f tColour3f::invalid = { -1.0f, -1.0f, -1.0f }; 
55const tColour3f tColour3f::black = { 0.0f, 0.0f, 0.0f }; 
56const tColour3f tColour3f::white = { 1.0f, 1.0f, 1.0f }; 
57const tColour3f tColour3f::hotpink = { 1.0f, 0.5f, 0.5f }; 
58const tColour3f tColour3f::red = { 1.0f, 0.0f, 0.0f }; 
59const tColour3f tColour3f::green = { 0.0f, 1.0f, 0.0f }; 
60const tColour3f tColour3f::blue = { 0.0f, 0.0f, 1.0f }; 
61const tColour3f tColour3f::grey = { 0.5f, 0.5f, 0.5f }; 
62const tColour3f tColour3f::lightgrey = { 0.75f, 0.75f, 0.75f }; 
63const tColour3f tColour3f::darkgrey = { 0.25f, 0.25f, 0.25f }; 
64const tColour3f tColour3f::cyan = { 0.0f, 1.0f, 1.0f }; 
65const tColour3f tColour3f::magenta = { 1.0f, 0.0f, 1.0f }; 
66const tColour3f tColour3f::yellow = { 1.0f, 1.0f, 0.0f }; 
67 
68 
69void tMath::tRGBToHSV(int& h, int& s, int& v, int r, int g, int b, tAngleMode angleMode
70
71 double min = double( tMin(r, g, b) ); 
72 double max = double( tMax(r, g, b) ); 
73 double delta = max - min
74 
75 v = int(max); 
76 if (!delta
77
78 h = s = 0
79 return
80
81 
82 double temp = delta/max
83 s = int(temp*255.0); 
84 
85 if (r == int(max)) 
86 temp = double(g-b) / delta
87 else if (g == int(max)) 
88 temp = 2.0 + double(b-r) / delta
89 else 
90 temp = 4.0 + double(r-g) / delta
91 
92 // Compute hue in correct angle units. 
93 tAssert((angleMode == tAngleMode::Degrees) || (angleMode == tAngleMode::Norm256)); 
94 double fullCircle = 360.0
95 if (angleMode == tAngleMode::Norm256
96 fullCircle = 256.0
97 
98 temp *= fullCircle / 6.0
99 if (temp < 0.0
100 temp += fullCircle
101 
102 if (temp >= fullCircle
103 temp = 0
104 
105 h = int(temp); 
106
107 
108 
109void tMath::tHSVToRGB(int& r, int& g, int& b, int h, int s, int v, tAngleMode angleMode
110
111 tAssert((angleMode == tAngleMode::Degrees) || (angleMode == tAngleMode::Norm256)); 
112 int fullCircle = 360
113 if (angleMode == tAngleMode::Norm256
114 fullCircle = 256
115 
116 while (h >= fullCircle
117 h -= fullCircle
118 
119 while (h < 0
120 h += fullCircle
121 
122 tiClamp(h, 0, fullCircle-1); 
123 tiClamp(s, 0, 255); 
124 tiClamp(v, 0, 255); 
125 
126 if (!h && !s
127
128 r = g = b = v
129 return
130
131 
132 double max = double(v); 
133 double delta = max*s / 255.0
134 double min = max - delta
135 double hue = double(h); 
136 double circle = double(fullCircle); 
137 double oneSixthCircle = circle/6.0; // 60 degrees. 
138 double oneThirdCircle = circle/3.0; // 120 degrees. 
139 double oneHalfCircle = circle/2.0; // 180 degrees. 
140 double twoThirdCircle = (2.0*circle)/3.0; // 240 degrees. 
141 double fiveSixthCircle = (5.0*circle)/6.0; // 300 degrees. 
142 
143 if (h > fiveSixthCircle || h <= oneSixthCircle
144
145 r = int(max); 
146 if (h > fiveSixthCircle
147
148 g = int(min); 
149 hue = (hue - circle)/oneSixthCircle
150 b = int(min - hue*delta); 
151
152 else 
153
154 b = int(min); 
155 hue = hue / oneSixthCircle
156 g = int(hue*delta + min); 
157
158
159 else if (h > oneSixthCircle && h < oneHalfCircle
160
161 g = int(max); 
162 if (h < oneThirdCircle
163
164 b = int(min); 
165 hue = (hue/oneSixthCircle - 2.0) * delta
166 r = int(min - hue); 
167
168 else 
169
170 r = int(min); 
171 hue = (hue/oneSixthCircle - 2.0) * delta
172 b = int(min + hue); 
173
174
175 else 
176
177 b = int(max); 
178 if (h < twoThirdCircle
179
180 r = int(min); 
181 hue = (hue/oneSixthCircle - 4.0) * delta
182 g = int(min - hue); 
183
184 else 
185
186 g = int(min); 
187 hue = (hue/oneSixthCircle - 4.0) * delta
188 r = int(min + hue); 
189
190
191
192 
193 
194void tMath::tRGBToHSV(float& h, float& s, float& v, float r, float g, float b, tAngleMode angleMode
195
196 float min = tMin(r, g, b); 
197 float max = tMax(r, g, b); 
198 
199 v = max
200 float delta = max - min
201 if (max > 0.0f
202
203 s = (delta / max); 
204
205 else 
206
207 // Max is 0 so we're black with v = 0. 
208 // Hue and Sat are irrelevant at this point but we zero them to be clean. 
209 s = 0.0f
210 h = 0.0f
211
212 
213 if (r >= max
214 h = (g - b) / delta; // Between yellow & magenta. 
215 else if (g >= max
216 h = 2.0f + (b - r) / delta; // Between cyan & yellow. 
217 else 
218 h = 4.0f + (r - g) / delta; // Between magenta & cyan. 
219 
220 float fullCircle = 360.0f
221 switch (angleMode
222
223 case tAngleMode::Radians
224 fullCircle = TwoPi
225 break
226 
227 case tAngleMode::Degrees
228 fullCircle = 360.0f
229 break
230 
231 case tAngleMode::Norm256
232 fullCircle = 256.0f
233 break
234 
235 case tAngleMode::NormOne
236 fullCircle = 1.0f
237 break
238
239 
240 h *= fullCircle / 6.0f
241 
242 if (h < 0.0f
243 h += fullCircle
244
245 
246 
247void tMath::tHSVToRGB(float& r, float& g, float& b, float h, float s, float v, tAngleMode angleMode
248
249 // If sat is zero we always ignore the hue. That is, we're a shade of grey on the vertical line. 
250 if (s <= 0.0f
251
252 r = v
253 g = v
254 b = v
255 return
256
257 
258 float fullCircle = 360.0f
259 switch (angleMode
260
261 case tAngleMode::Radians
262 fullCircle = TwoPi
263 break
264 
265 case tAngleMode::Degrees
266 fullCircle = 360.0f
267 break
268 
269 case tAngleMode::Norm256
270 fullCircle = 256.0f
271 break
272 
273 case tAngleMode::NormOne
274 fullCircle = 1.0f
275 break
276
277 
278 if (h >= fullCircle
279 h = 0.0f
280 h /= (fullCircle/6.0f); 
281 
282 int i = int(h); 
283 float rem = h - i
284 float p = v * (1.0f - s); 
285 float q = v * (1.0f - (s * rem)); 
286 float t = v * (1.0f - (s * (1.0f - rem))); 
287 
288 switch (i
289
290 case 0
291 r = v
292 g = t
293 b = p
294 break
295 
296 case 1
297 r = q
298 g = v
299 b = p
300 break
301 
302 case 2
303 r = p
304 g = v
305 b = t
306 break
307 
308 case 3
309 r = p
310 g = q
311 b = v
312 break
313 
314 case 4
315 r = t
316 g = p
317 b = v
318 break
319 
320 case 5
321 default
322 r = v
323 g = p
324 b = q
325 break
326
327
328 
329 
330tColouri tMath::tGetColour(const char* colourName
331
332 tString lowerName(colourName); 
333 lowerName.ToLower(); 
334 uint32 colourHash = tHash::tHashStringFast32(lowerName); 
335 tColouri colour = tColouri::white
336 
337 // This switch uses compile-time hashes. Collisions will be automatically detected by the compiler. 
338 switch (colourHash
339
340 case tHash::tHashCT("none"): colour = 0xFFFFFFFF; break
341 case tHash::tHashCT("black"): colour = 0x000000FF; break
342 default: break
343
344  
345 return colour;  
346
347 
348 
349float tMath::tColourDiffRedmean(const tColouri& aa, const tColouri& bb
350
351 tVector3 a; aa.GetDenorm(a); 
352 tVector3 b; bb.GetDenorm(b); 
353 
354 float rhat = (a.x + b.x) / 2.0f
355 
356 float dR2 = tSquare(a.x - b.x); 
357 float dG2 = tSquare(a.y - b.y); 
358 float dB2 = tSquare(a.z - b.z); 
359 
360 float term1 = (2.0f + rhat/256.0f)*dR2
361 float term2 = 4.0f * dG2
362 float term3 = (2.0f + ((255.0f-rhat)/256.0f)) * dB2
363 
364 return tSqrt(term1 + term2 + term3); 
365
366