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 | */ |
17 | inline 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 |
38 | template<typename DerivedT> |
39 | class color4_base |
40 | { |
41 | public: |
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 | |
172 | class ColorHSLA : public color4_base<ColorHSLA> |
173 | { |
174 | public: |
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 | |
201 | class ColorHSVA : public color4_base<ColorHSVA> |
202 | { |
203 | public: |
204 | using color4_base::color4_base; |
205 | ColorHSVA(){}; |
206 | }; |
207 | |
208 | class ColorRGBA : public color4_base<ColorRGBA> |
209 | { |
210 | public: |
211 | using color4_base::color4_base; |
212 | ColorRGBA(){}; |
213 | }; |
214 | |
215 | template<typename T, typename F> |
216 | T color_cast(const F &) = delete; |
217 | |
218 | template<> |
219 | inline 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 | |
232 | template<> |
233 | inline 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 | |
274 | template<> |
275 | inline 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 | |
281 | template<> |
282 | inline 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 | |
288 | template<> |
289 | inline ColorRGBA color_cast(const ColorHSVA &hsv) |
290 | { |
291 | return color_cast<ColorRGBA>(hsl: color_cast<ColorHSLA>(hsv)); |
292 | } |
293 | |
294 | template<> |
295 | inline ColorHSVA color_cast(const ColorRGBA &rgb) |
296 | { |
297 | return color_cast<ColorHSVA>(hsl: color_cast<ColorHSLA>(rgb)); |
298 | } |
299 | |
300 | template<typename T> |
301 | T 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 | |
306 | template<typename T> |
307 | T 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 | |
312 | template<typename T> |
313 | std::optional<T> color_parse(const char *pStr); |
314 | |
315 | #endif |
316 | |