1
2#ifndef BASE_COLOR_H
3#define BASE_COLOR_H
4
5#include <base/math.h>
6#include <base/vmath.h>
7
8#include <optional>
9
10/*
11 Title: Color handling
12*/
13/*
14 Function: RgbToHue
15 Determines the hue from RGB values
16*/
17inline float RgbToHue(float r, float g, float b)
18{
19 float h_min = minimum(a: r, b: g, c: b);
20 float h_max = maximum(a: r, b: g, c: b);
21
22 float hue = 0.0f;
23 if(h_max != h_min)
24 {
25 float c = h_max - h_min;
26 if(h_max == r)
27 hue = (g - b) / c + (g < b ? 6 : 0);
28 else if(h_max == g)
29 hue = (b - r) / c + 2;
30 else
31 hue = (r - g) / c + 4;
32 }
33
34 return hue / 6.0f;
35}
36
37// Curiously Recurring Template Pattern for type safety
38template<typename DerivedT>
39class color4_base
40{
41public:
42 union
43 {
44 float x, r, h;
45 };
46 union
47 {
48 float y, g, s;
49 };
50 union
51 {
52 float z, b, l, v;
53 };
54 union
55 {
56 float w, a;
57 };
58
59 color4_base() :
60 x(), y(), z(), a()
61 {
62 }
63
64 color4_base(const vec4 &v4)
65 {
66 x = v4.x;
67 y = v4.y;
68 z = v4.z;
69 a = v4.w;
70 }
71
72 color4_base(const vec3 &v3)
73 {
74 x = v3.x;
75 y = v3.y;
76 z = v3.z;
77 a = 1.0f;
78 }
79
80 color4_base(float nx, float ny, float nz, float na)
81 {
82 x = nx;
83 y = ny;
84 z = nz;
85 a = na;
86 }
87
88 color4_base(float nx, float ny, float nz)
89 {
90 x = nx;
91 y = ny;
92 z = nz;
93 a = 1.0f;
94 }
95
96 color4_base(unsigned col, bool alpha = false)
97 {
98 a = alpha ? ((col >> 24) & 0xFF) / 255.0f : 1.0f;
99 x = ((col >> 16) & 0xFF) / 255.0f;
100 y = ((col >> 8) & 0xFF) / 255.0f;
101 z = ((col >> 0) & 0xFF) / 255.0f;
102 }
103
104 vec4 v4() const { return vec4(x, y, z, a); }
105 operator vec4() const { return vec4(x, y, z, a); }
106 float &operator[](int index)
107 {
108 return ((float *)this)[index];
109 }
110
111 bool operator==(const color4_base &col) const { return x == col.x && y == col.y && z == col.z && a == col.a; }
112 bool operator!=(const color4_base &col) const { return x != col.x || y != col.y || z != col.z || a != col.a; }
113
114 unsigned Pack(bool Alpha = true) const
115 {
116 return (Alpha ? ((unsigned)round_to_int(f: a * 255.0f) << 24) : 0) + ((unsigned)round_to_int(f: x * 255.0f) << 16) + ((unsigned)round_to_int(f: y * 255.0f) << 8) + (unsigned)round_to_int(f: z * 255.0f);
117 }
118
119 unsigned PackAlphaLast(bool Alpha = true) const
120 {
121 if(Alpha)
122 return ((unsigned)round_to_int(f: x * 255.0f) << 24) + ((unsigned)round_to_int(f: y * 255.0f) << 16) + ((unsigned)round_to_int(f: z * 255.0f) << 8) + (unsigned)round_to_int(f: a * 255.0f);
123 return ((unsigned)round_to_int(f: x * 255.0f) << 16) + ((unsigned)round_to_int(f: y * 255.0f) << 8) + (unsigned)round_to_int(f: z * 255.0f);
124 }
125
126 DerivedT WithAlpha(float alpha) const
127 {
128 DerivedT col(static_cast<const DerivedT &>(*this));
129 col.a = alpha;
130 return col;
131 }
132
133 DerivedT WithMultipliedAlpha(float alpha) const
134 {
135 DerivedT col(static_cast<const DerivedT &>(*this));
136 col.a *= alpha;
137 return col;
138 }
139
140 DerivedT Multiply(const DerivedT &Other) const
141 {
142 DerivedT Color(static_cast<const DerivedT &>(*this));
143 Color.x *= Other.x;
144 Color.y *= Other.y;
145 Color.z *= Other.z;
146 Color.a *= Other.a;
147 return Color;
148 }
149
150 template<typename UnpackT>
151 static UnpackT UnpackAlphaLast(unsigned Color, bool Alpha = true)
152 {
153 UnpackT Result;
154 if(Alpha)
155 {
156 Result.x = ((Color >> 24) & 0xFF) / 255.0f;
157 Result.y = ((Color >> 16) & 0xFF) / 255.0f;
158 Result.z = ((Color >> 8) & 0xFF) / 255.0f;
159 Result.a = ((Color >> 0) & 0xFF) / 255.0f;
160 }
161 else
162 {
163 Result.x = ((Color >> 16) & 0xFF) / 255.0f;
164 Result.y = ((Color >> 8) & 0xFF) / 255.0f;
165 Result.z = ((Color >> 0) & 0xFF) / 255.0f;
166 Result.a = 1.0f;
167 }
168 return Result;
169 }
170};
171
172class ColorHSLA : public color4_base<ColorHSLA>
173{
174public:
175 using color4_base::color4_base;
176 ColorHSLA(){};
177
178 constexpr static const float DARKEST_LGT = 0.5f;
179
180 ColorHSLA UnclampLighting(float Darkest = DARKEST_LGT) const
181 {
182 ColorHSLA col = *this;
183 col.l = Darkest + col.l * (1.0f - Darkest);
184 return col;
185 }
186
187 unsigned Pack(bool Alpha = true) const
188 {
189 return color4_base::Pack(Alpha);
190 }
191
192 unsigned Pack(float Darkest, bool Alpha = false) const
193 {
194 ColorHSLA col = *this;
195 col.l = (l - Darkest) / (1 - Darkest);
196 col.l = clamp(val: col.l, lo: 0.0f, hi: 1.0f);
197 return col.Pack(Alpha);
198 }
199};
200
201class ColorHSVA : public color4_base<ColorHSVA>
202{
203public:
204 using color4_base::color4_base;
205 ColorHSVA(){};
206};
207
208class ColorRGBA : public color4_base<ColorRGBA>
209{
210public:
211 using color4_base::color4_base;
212 ColorRGBA(){};
213};
214
215template<typename T, typename F>
216T color_cast(const F &) = delete;
217
218template<>
219inline ColorHSLA color_cast(const ColorRGBA &rgb)
220{
221 float Min = minimum(a: rgb.r, b: rgb.g, c: rgb.b);
222 float Max = maximum(a: rgb.r, b: rgb.g, c: rgb.b);
223
224 float c = Max - Min;
225 float h = RgbToHue(r: rgb.r, g: rgb.g, b: rgb.b);
226 float l = 0.5f * (Max + Min);
227 float s = (Max != 0.0f && Min != 1.0f) ? (c / (1 - (absolute(a: 2 * l - 1)))) : 0;
228
229 return ColorHSLA(h, s, l, rgb.a);
230}
231
232template<>
233inline ColorRGBA color_cast(const ColorHSLA &hsl)
234{
235 vec3 rgb = vec3(0, 0, 0);
236
237 float h1 = hsl.h * 6;
238 float c = (1.f - absolute(a: 2 * hsl.l - 1)) * hsl.s;
239 float x = c * (1.f - absolute(a: std::fmod(x: h1, y: 2) - 1.f));
240
241 switch(round_truncate(f: h1))
242 {
243 case 0:
244 rgb.r = c;
245 rgb.g = x;
246 break;
247 case 1:
248 rgb.r = x;
249 rgb.g = c;
250 break;
251 case 2:
252 rgb.g = c;
253 rgb.b = x;
254 break;
255 case 3:
256 rgb.g = x;
257 rgb.b = c;
258 break;
259 case 4:
260 rgb.r = x;
261 rgb.b = c;
262 break;
263 case 5:
264 case 6:
265 rgb.r = c;
266 rgb.b = x;
267 break;
268 }
269
270 float m = hsl.l - (c / 2);
271 return ColorRGBA(rgb.r + m, rgb.g + m, rgb.b + m, hsl.a);
272}
273
274template<>
275inline ColorHSLA color_cast(const ColorHSVA &hsv)
276{
277 float l = hsv.v * (1 - hsv.s * 0.5f);
278 return ColorHSLA(hsv.h, (l == 0.0f || l == 1.0f) ? 0 : (hsv.v - l) / minimum(a: l, b: 1 - l), l, hsv.a);
279}
280
281template<>
282inline ColorHSVA color_cast(const ColorHSLA &hsl)
283{
284 float v = hsl.l + hsl.s * minimum(a: hsl.l, b: 1 - hsl.l);
285 return ColorHSVA(hsl.h, v == 0.0f ? 0 : 2 - (2 * hsl.l / v), v, hsl.a);
286}
287
288template<>
289inline ColorRGBA color_cast(const ColorHSVA &hsv)
290{
291 return color_cast<ColorRGBA>(hsl: color_cast<ColorHSLA>(hsv));
292}
293
294template<>
295inline ColorHSVA color_cast(const ColorRGBA &rgb)
296{
297 return color_cast<ColorHSVA>(hsl: color_cast<ColorHSLA>(rgb));
298}
299
300template<typename T>
301T color_scale(const T &col, float s)
302{
303 return T(col.x * s, col.y * s, col.z * s, col.a * s);
304}
305
306template<typename T>
307T color_invert(const T &col)
308{
309 return T(1.0f - col.x, 1.0f - col.y, 1.0f - col.z, 1.0f - col.a);
310}
311
312template<typename T>
313std::optional<T> color_parse(const char *pStr);
314
315#endif
316