header_utils
Loading...
Searching...
No Matches
colors.h
1
4
5#pragma once
6
7#include <charconv>
8#include <string_view>
9#include <glm/common.hpp>
10#include <glm/ext/vector_float4.hpp>
11#include "named.h"
12#include "constexpr_math.h"
13
14namespace ghassanpl
15{
18
20
23
24 enum class color_space
25 {
26 linear_rgba,
27 srgb,
28 hsva,
29
30 rgbe,
31 };
32
34 using color_rgba_t = glm::vec4;
36 using color_hsva_t = named<glm::vec4, "color_hsva">;
39
41 using color_rgba_u32_t = named<uint32_t, "color_rgba_u32">;
43 using color_abgr_u32_t = named<uint32_t, "color_abgr_u32">;
44
47 namespace colors
48 {
49#define DEF_COLOR(name, r, g, b) \
50 constexpr color_t get_##name(float alpha) { return color_t{ float(r), float(g), float(b), alpha }; } \
51 constexpr inline color_t name = get_##name(1.0f);
52#define DEF_COLORS(name, r, g, b) \
53 DEF_COLOR(name, r, g, b) \
54 constexpr color_t get_dark_##name(float alpha) { return color_t{ float(r) * 0.5f, float(g) * 0.5f, float(b) * 0.5f, alpha }; } \
55 constexpr inline color_t dark_##name = get_dark_##name(1.0f); \
56 constexpr color_t get_light_##name(float alpha) { return color_t{ r + float(1.0f-r) * 0.5f, g + float(1.0f-g) * 0.5f, b + float(1.0f-b) * 0.5f, alpha }; } \
57 constexpr inline color_t light_##name = get_light_##name(1.0f);
58
59 DEF_COLORS(red, 1, 0, 0)
60 DEF_COLORS(green, 0, 1, 0)
61 DEF_COLORS(blue, 0, 0, 1)
62 DEF_COLORS(yellow, 1, 1, 0)
63 DEF_COLORS(magenta, 1, 0, 1)
64 DEF_COLORS(cyan, 0, 1, 1)
65 DEF_COLORS(gray, 0.5f, 0.5f, 0.5f)
66 DEF_COLORS(grey, 0.5f, 0.5f, 0.5f)
67
68 DEF_COLORS(orange, 1, 0.65f, 0)
69 DEF_COLORS(brown, 0.59f, 0.29f, 0)
70
71 DEF_COLOR(black, 0, 0, 0)
72 DEF_COLOR(white, 1, 1, 1)
73 constexpr inline color_t transparent = get_black(0.0f);
74
75 #undef DEF_COLOR
76 #undef DEF_COLORS
77 }
78
81 {
82 return { color.x * color.w, color.y * color.w, color.z * color.w, color.w };
83 }
84
86 constexpr color_t saturated(color_t const& color)
87 {
88 return glm::clamp(color_t(color.x, color.y, color.z, color.w), color_t{ 0,0,0,0 }, color_t{ 1,1,1,1 });
89 }
90
92 constexpr color_t lighten(color_t const& color, float coef)
93 {
94 const auto rgb_max = glm::max(color.x, glm::max(color.y, color.z));
95 const auto lighter = color * (1.0f / rgb_max);
96 const auto dif = rgb_max;
97 return saturated(color_t(lighter.x + dif * coef, lighter.y + dif * coef, lighter.z + dif * coef, 1.0f) * rgb_max);
98 }
99
103 constexpr color_t contrast(color_t const& color, float contrast)
104 {
105 const auto t = (1.0f - contrast) * 0.5f;
106 return color_t(color.x*contrast + t, color.y*contrast + t, color.z*contrast + t, color.w);
107 }
108
113 constexpr color_t contrast2(color_t const& color, float contrast)
114 {
115 constexpr double m = 1.0156862745098039215686274509804;
116 const auto t = (m * (contrast + 1.0f)) / (m - contrast);
117 return color_t(t * (color.x - 0.5f) + 0.5f, t * (color.y - 0.5f) + 0.5f, t * (color.z - 0.5f) + 0.5f, color.w);
118 }
119
121 constexpr color_t gamma_correct(color_t const& color, const float gamma)
122 {
123 const auto gamma_correct = 1.0f / gamma;
124 return { cem::pow(color.x, gamma_correct), cem::pow(color.y, gamma_correct), cem::pow(color.z, gamma_correct), color.w };
125 }
126
128 constexpr color_t inverted(color_t const& color)
129 {
130 return color_t(1.0f - color.x, 1.0f - color.y, 1.0f - color.z, color.w);
131 }
132
135 {
136 return color_t(cem::fmod(color.x + 0.5f, 1.0f), cem::fmod(color.y + 0.5f, 1.0f), cem::fmod(color.z + 0.5f, 1.0f), color.w);
137 }
138
140 constexpr float luminance(color_t const& color)
141 {
142 return color.x * 0.3f + color.y * 0.59f + color.z * 0.11f;
143 }
144
147 {
148 const auto l = luminance(color);
149 return glm::mix(color, color_t{l, l, l, color.w}, desaturation);
150 }
151
152 namespace detail
153 {
154 constexpr float b2f(uint32_t byte) { return (byte & 0xFF) / 255.0f; }
155 constexpr uint32_t f2b(float f) { return uint8_t(0.5f + f * 255.0f); }
156 constexpr uint32_t f2u4(float b1, float b2, float b3, float b4) { return (f2b(b1) << 24) | (f2b(b2) << 16) | (f2b(b3) << 8) | f2b(b4); }
157 constexpr uint32_t f2u4(float b1, float b2, float b3) { return (f2b(b1) << 16) | (f2b(b2) << 8) | f2b(b3); }
158
160 template <typename T> constexpr auto min(const T& x, const T& y, const T& z) { return glm::min(glm::min(x, y), z); }
161 template <typename T> constexpr auto max(const T& x, const T& y, const T& z) { return glm::max(glm::max(x, y), z); }
162 }
163
165 constexpr color_t from_u32_rgb(uint32_t rgb) { return color_t(detail::b2f(rgb >> 16), detail::b2f(rgb >> 8), detail::b2f(rgb), 1.0f); }
167 constexpr color_t from_u32_bgr(uint32_t rgb) { return color_t(detail::b2f(rgb), detail::b2f(rgb >> 8), detail::b2f(rgb >> 16), 1.0f); }
169 constexpr color_t from_u32_rgba(uint32_t rgb) { return color_t(detail::b2f(rgb >> 24), detail::b2f(rgb >> 16), detail::b2f(rgb >> 8), detail::b2f(rgb)); }
171 constexpr color_t from_u32_bgra(uint32_t rgb) { return color_t(detail::b2f(rgb >> 8), detail::b2f(rgb >> 16), detail::b2f(rgb >> 24), detail::b2f(rgb)); }
173 constexpr color_t from_u32_argb(uint32_t rgb) { return color_t(detail::b2f(rgb >> 16), detail::b2f(rgb >> 8), detail::b2f(rgb), detail::b2f(rgb >> 24)); }
175 constexpr color_t from_u32_abgr(uint32_t rgb) { return color_t(detail::b2f(rgb), detail::b2f(rgb >> 8), detail::b2f(rgb >> 16), detail::b2f(rgb >> 24)); }
176
178 constexpr uint32_t to_u32_argb(color_t const& rgba) { return detail::f2u4(rgba.w, rgba.x, rgba.y, rgba.z); }
180 constexpr uint32_t to_u32_abgr(color_t const& rgba) { return detail::f2u4(rgba.w, rgba.z, rgba.y, rgba.x); }
182 constexpr uint32_t to_u32_rgba(color_t const& rgba) { return detail::f2u4(rgba.x, rgba.y, rgba.z, rgba.w); }
184 constexpr uint32_t to_u32_bgra(color_t const& rgba) { return detail::f2u4(rgba.z, rgba.y, rgba.x, rgba.w); }
186 constexpr uint32_t to_u32_rgb(color_t const& rgba) { return detail::f2u4(rgba.x, rgba.y, rgba.z); }
188 constexpr uint32_t to_u32_bgr(color_t const& rgba) { return detail::f2u4(rgba.z, rgba.y, rgba.x); }
189
190 template <std::same_as<color_rgba_u32_t> TO>
191 constexpr TO named_cast(color_rgba_t const& from)
192 {
193 return TO{ to_u32_rgba(from) };
194 }
195
196 template <std::same_as<color_abgr_u32_t> TO>
197 constexpr TO named_cast(color_rgba_t const& from)
198 {
199 return TO{ to_u32_abgr(from) };
200 }
201
202 template <typename TO, typename FROM>
203 TO color_cast(FROM const& from)
204 {
205 return named_cast<TO>(from);
206 }
207
209 constexpr glm::tvec4<uint8_t> to_u8(color_t const& rgba) { return { detail::f2b(rgba.x), detail::f2b(rgba.y), detail::f2b(rgba.z), detail::f2b(rgba.w) }; }
210
212 constexpr color_t to_rgb(color_hsva_t const& hsva)
213 {
214 const auto hue = hsva->r;
215 const auto saturation = hsva->g;
216 const auto value = hsva->b;
217 const auto alpha = hsva->a;
218 const auto i = (int)hue;
219 float fraction = hue - (float)i;
220 if (!(i & 1)) fraction = 1.0f - fraction;
221 const auto m = value * (1.0f - saturation);
222 const auto n = value * (1.0f - saturation * fraction);
223 switch (i)
224 {
225 case 6:
226 case 0: return color_t(value, n, m, alpha);
227 case 1: return color_t(n, value, m, alpha);
228 case 2: return color_t(m, value, n, alpha);
229 case 3: return color_t(m, n, value, alpha);
230 case 4: return color_t(n, m, value, alpha);
231 case 5: return color_t(value, m, n, alpha);
232 default: return color_t(value, value, value, alpha);
233 }
234 }
235
237 constexpr color_hsva_t to_hsv(color_t const& rgba)
238 {
239 const auto min = detail::min(rgba.x, rgba.y, rgba.z);
240 const auto max = detail::max(rgba.x, rgba.y, rgba.z);
241 const auto delta = max - min;
242
243 float h = 0;
244
245 if (delta != 0)
246 {
247 if (rgba.x == max)
248 h = fmod((rgba.y - rgba.z) / delta, 6.0f);
249 else if (rgba.y == max)
250 h = 2 + (rgba.z - rgba.x) / delta;
251 else
252 h = 4 + (rgba.x - rgba.y) / delta;
253 }
254
255 return color_hsva_t{
256 h,
257 (max != 0) ? (delta / max) : 0,
258 max,
259 rgba.w
260 };
261 }
262
264 constexpr color_rgba_t from_html(const char* str, size_t n)
265 {
266 if (n == 0)
267 throw n;
268 if (str[0] == '#')
269 {
270 str++;
271 n--;
272 }
273 uint8_t r{};
274 uint8_t g{};
275 uint8_t b{};
276 uint8_t a = 255;
277 switch (n)
278 {
279 case 4:
280 std::from_chars(str + 3, str + 4, a, 16); a = a << 4 | a;
281 [[fallthrough]];
282 case 3:
283 std::from_chars(str + 0, str + 1, r, 16); r = r << 4 | r;
284 std::from_chars(str + 1, str + 2, g, 16); g = g << 4 | g;
285 std::from_chars(str + 2, str + 3, b, 16); b = b << 4 | b;
286 break;
287 case 8:
288 std::from_chars(str + 6, str + 8, a, 16);
289 [[fallthrough]];
290 case 6:
291 std::from_chars(str + 0, str + 2, r, 16);
292 std::from_chars(str + 2, str + 4, g, 16);
293 std::from_chars(str + 4, str + 6, b, 16);
294 break;
295 default: throw "invalid number of characters";
296 }
297 return { r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f };
298 }
299
301 consteval color_rgba_t operator ""_rgb(const char* str, size_t n)
302 {
303 return from_html(str, n);
304 }
305
307 constexpr color_rgba_t from_html(std::string_view html)
308 {
309 return from_html(html.data(), html.size());
310 }
311
313
314#if 0
315
316 float GetDotProduct() const {
317 return R*R + G*G + B*B;
318 }
319 float GetFullDotProduct() const {
320 return R*R + G*G + B*B + A*A;
321 }
322
324 return std::min_element(palette.begin(), palette.end(), [this](const Color& a, const Color& b) {
325 const auto diff_a = a - *this;
326 const auto diff_b = b - *this;
327 return diff_a.GetDotProduct() < diff_b.GetDotProduct();
328 });
329 }
330
332 return std::min_element(palette.begin(), palette.end(), [this](const Color& a, const Color& b) {
333 const auto diff_a = a.FullDifference(*this);
334 const auto diff_b = b.FullDifference(*this);
335 return diff_a.GetFullDotProduct() < diff_b.GetFullDotProduct();
336 });
337 }
338
339
340 enum class GrayscaleType
341 {
342 Lightness,
343 Average,
345 };
346
347 template <GrayscaleType TYPE = GrayscaleType::Luminosity>
348 Color Grayscale() const {
349 switch (TYPE)
350 {
351 case GrayscaleType::Lightness: return Color((Max3(R, G, B) + Min3(R, G, B))*0.5, A);
352 case GrayscaleType::Average: return Color((R+G+B)/3.0, A);
353 default:
354 case GrayscaleType::Luminosity: return Color(R*0.21 + G*0.72 + B*0.07, A);
355 }
356 }
357
358 Color BlendedOver(const Color& other) const {
359 const auto oma = 1.0f - A;
360 return *this * A + other * oma;
361 }
362
363 template <typename FUNC>
364 Color BlendWithAlpha(const Color& other, FUNC func) const {
365 return Lerp(func(*this, other), other, A);
366 }
367 /*
368 static Color Multiply(const Color& c1, const Color& c2 ) { return Color(c1.x*c2.x, c1.y*c2.y, c1.z*c2.z); }
369 static Color Screen(const Color& c1, const Color& c2) { return Multiply(c1.Inverted(), c2.Inverted()).Inverted(); }
370 static Color Overlay(const Color& c1, const Color& c2) {
371 return Color(
372 c2.x < 0.5f ? c1.x*c2.x * 2 : (1 - 2 * (1 - c2.x)*(1 - c1.x)),
373 c2.y < 0.5f ? c1.y*c2.y * 2 : (1 - 2 * (1 - c2.y)*(1 - c1.y)),
374 c2.z < 0.5f ? c1.z*c2.z * 2 : (1 - 2 * (1 - c2.z)*(1 - c1.z)));
375 }
376 static Color HardLight(const Color& c2, const Color& c1) {
377 return Color(
378 c2.x < 0.5f ? c1.x*c2.x * 2 : (1 - 2 * (1 - c2.x)*(1 - c1.x)),
379 c2.y < 0.5f ? c1.y*c2.y * 2 : (1 - 2 * (1 - c2.y)*(1 - c1.y)),
380 c2.z < 0.5f ? c1.z*c2.z * 2 : (1 - 2 * (1 - c2.z)*(1 - c1.z)));
381 }
382 static Color Overlay(const Color& c1, const Color& c2) {
383 return Multiply((2 * c2).Inverted(), Multiply(c1, c1)) + 2 * Multiply(c1, c2); /// pegtop!
384 }
385 */
386 /*
387 #define ChannelBlend_Normal(A,B) ((uint8)(A))
388 #define ChannelBlend_Lighten(A,B) ((uint8)((B > A) ? B:A))
389 #define ChannelBlend_Darken(A,B) ((uint8)((B > A) ? A:B))
390 #define ChannelBlend_Multiply(A,B) ((uint8)((A * B) / 255))
391 #define ChannelBlend_Average(A,B) ((uint8)((A + B) / 2))
392 #define ChannelBlend_Add(A,B) ((uint8)(min(255, (A + B))))
393 #define ChannelBlend_Subtract(A,B) ((uint8)((A + B < 255) ? 0:(A + B - 255)))
394 #define ChannelBlend_Difference(A,B) ((uint8)(abs(A - B)))
395 #define ChannelBlend_Negation(A,B) ((uint8)(255 - abs(255 - A - B)))
396 #define ChannelBlend_Screen(A,B) ((uint8)(255 - (((255 - A) * (255 - B)) >> 8)))
397 #define ChannelBlend_Exclusion(A,B) ((uint8)(A + B - 2 * A * B / 255))
398 #define ChannelBlend_Overlay(A,B) ((uint8)((B < 128) ? (2 * A * B / 255):(255 - 2 * (255 - A) * (255 - B) / 255)))
399 #define ChannelBlend_SoftLight(A,B) ((uint8)((B < 128)?(2*((A>>1)+64))*((float)B/255):(255-(2*(255-((A>>1)+64))*(float)(255-B)/255))))
400 #define ChannelBlend_HardLight(A,B) (ChannelBlend_Overlay(B,A))
401 #define ChannelBlend_ColorDodge(A,B) ((uint8)((B == 255) ? B:min(255, ((A << 8 ) / (255 - B)))))
402 #define ChannelBlend_ColorBurn(A,B) ((uint8)((B == 0) ? B:max(0, (255 - ((255 - A) << 8 ) / B))))
403 #define ChannelBlend_LinearDodge(A,B)(ChannelBlend_Add(A,B))
404 #define ChannelBlend_LinearBurn(A,B) (ChannelBlend_Subtract(A,B))
405 #define ChannelBlend_LinearLight(A,B)((uint8)(B < 128)?ChannelBlend_LinearBurn(A,(2 * B)):ChannelBlend_LinearDodge(A,(2 * (B - 128))))
406 #define ChannelBlend_VividLight(A,B) ((uint8)(B < 128)?ChannelBlend_ColorBurn(A,(2 * B)):ChannelBlend_ColorDodge(A,(2 * (B - 128))))
407 #define ChannelBlend_PinLight(A,B) ((uint8)(B < 128)?ChannelBlend_Darken(A,(2 * B)):ChannelBlend_Lighten(A,(2 * (B - 128))))
408 #define ChannelBlend_HardMix(A,B) ((uint8)((ChannelBlend_VividLight(A,B) < 128) ? 0:255))
409 #define ChannelBlend_Reflect(A,B) ((uint8)((B == 255) ? B:min(255, (A * A / (255 - B)))))
410 #define ChannelBlend_Glow(A,B) (ChannelBlend_Reflect(B,A))
411 #define ChannelBlend_Phoenix(A,B) ((uint8)(min(A,B) - max(A,B) + 255))
412 #define ChannelBlend_Alpha(A,B,O) ((uint8)(O * A + (1 - O) * B))
413 #define ChannelBlend_AlphaF(A,B,F,O) (ChannelBlend_Alpha(F(A,B),A,O))
414 */
415 //Color BlendMultiply(const Color& other) const { return BlendWithAlpha(other, colors::Multiply); }
416
417 static Color FromHTML(StringView str)
418 {
419 str.Consume('#');
420 if (str.size() == 6)
421 {
422 uint32_t hex = StringToUnsignedLong(str, nullptr, 16);
423 return colors::FromRGBInt(hex);
424 }
425 else if (str.size() == 8)
426 {
427 uint32_t hex = StringToUnsignedLong(str, nullptr, 16);
428 return colors::FromRGBAInt(hex);
429 }
430 else if (str.size() == 3)
431 {
432 uint32_t hex = StringToUnsignedLong(str, nullptr, 16);
433 hex = ((hex << 12) & 0xF00000) | ((hex << 8) & 0xF000) | ((hex << 4) & 0xF0);
434 hex = hex | (hex >> 4);
435 return colors::FromRGBInt(hex);
436 }
437 else return Color();
438 }
439
441
442 uint8_t GetR8() const { return uint8_t(R*255.0f); }
443 uint8_t GetG8() const { return uint8_t(G*255.0f); }
444 uint8_t GetB8() const { return uint8_t(B*255.0f); }
445 uint8_t GetA8() const { return uint8_t(A*255.0f); }
446
447#endif
448
450}
constexpr auto bit_count
Equal to the number of bits in the type.
Definition bits.h:33
constexpr float luminance(color_t const &color)
Get brightness of color.
Definition colors.h:140
named< uint32_t, "color_abgr_u32"> color_abgr_u32_t
Represent a packed ABGR color with 8 bits per pixel.
Definition colors.h:43
color_rgba_t color_t
Default color_t type is RGBA.
Definition colors.h:38
constexpr uint32_t to_u32_bgra(color_t const &rgba)
Creates an 8bpp BGRA integer from a color.
Definition colors.h:184
color_space
Implementation note: These functions use the .xyzw members instead of the .rgba members because acces...
Definition colors.h:25
constexpr uint32_t to_u32_rgb(color_t const &rgba)
Creates a 32 bitbpp RGB integer from a color, with the most significant 8 bits set to 0.
Definition colors.h:186
constexpr color_t premultiplied(color_t const &color)
Returns the color multiplied by its own alpha.
Definition colors.h:80
constexpr color_t from_u32_rgba(uint32_t rgb)
Gets a color from an RGBA 8bpp integer, with R being most significant.
Definition colors.h:169
constexpr color_t from_u32_bgr(uint32_t rgb)
Gets a color from an BGR 8bpp integer, with R being least significant.
Definition colors.h:167
constexpr color_t lighten(color_t const &color, float coef)
Returns a color lightened by a coefficient.
Definition colors.h:92
constexpr color_t saturated(color_t const &color)
Returns a color with all elements clamped between 0 and 1.
Definition colors.h:86
constexpr color_t to_rgb(color_hsva_t const &hsva)
Converts a HSVA color to RGBA space.
Definition colors.h:212
constexpr uint32_t to_u32_rgba(color_t const &rgba)
Creates an 8bpp RGBA integer from a color.
Definition colors.h:182
constexpr uint32_t to_u32_abgr(color_t const &rgba)
Creates an 8bpp ABGR integer from a color.
Definition colors.h:180
glm::vec4 color_rgba_t
Represents a color in RGBA color space, with 0.0-1.0 float elements.
Definition colors.h:34
constexpr color_t contrast(color_t const &color, float contrast)
Returns a color with its contrast changed.
Definition colors.h:103
named< uint32_t, "color_rgba_u32"> color_rgba_u32_t
Represent a packed RGBA color with 8 bits per pixel.
Definition colors.h:41
constexpr color_t contrast2(color_t const &color, float contrast)
Returns a color with its contrast changed This uses a differen algorithm and contrast value than cont...
Definition colors.h:113
constexpr color_t from_u32_abgr(uint32_t rgb)
Gets a color from an ABGR 8bpp integer, with A being most significant, and R being least significant.
Definition colors.h:175
constexpr color_t from_u32_argb(uint32_t rgb)
Gets a color from an ARGB 8bpp integer, with A being most significant, and B being least significant.
Definition colors.h:173
constexpr color_rgba_t from_html(const char *str, size_t n)
Converts a HTML color string (like #FBA or fafafa) to an RGBA color.
Definition colors.h:264
constexpr color_t gamma_correct(color_t const &color, const float gamma)
Returns a gamma corrected color.
Definition colors.h:121
constexpr color_t contrasting(color_t const &color)
Returns a color that's a good contrasting color for the original.
Definition colors.h:134
constexpr color_hsva_t to_hsv(color_t const &rgba)
Converts an RGBA color to HSVA space.
Definition colors.h:237
constexpr color_t inverted(color_t const &color)
Returns an inverted color, i.e. with (1.0-x) on all of its elements, excluding its alpha.
Definition colors.h:128
constexpr uint32_t to_u32_argb(color_t const &rgba)
Creates an 8bpp ARGB integer from a color.
Definition colors.h:178
constexpr color_t desaturated(color_t const &color, float desaturation)
Returns a color sapped of desaturation percent (0-1) of its saturation.
Definition colors.h:146
constexpr color_t from_u32_rgb(uint32_t rgb)
Gets a color from an RGB 8bpp integer, with R being most significant.
Definition colors.h:165
constexpr color_t from_u32_bgra(uint32_t rgb)
Gets a color from an BGRA 8bpp integer, with A being least significant, and B being most significant.
Definition colors.h:171
constexpr uint32_t to_u32_bgr(color_t const &rgba)
Creates a 32 bitbpp BGR integer from a color, with the most significant 8 bits set to 0.
Definition colors.h:188
named< glm::vec4, "color_hsva"> color_hsva_t
Represents a color in HSVA color space, with H=0.0-6.0, S=0.0-1.0, V=0.0-1.0, A=0....
Definition colors.h:36
The below code is based on Sun's libm library code, which is licensed under the following license:
Primary namespace for everything in this library.
Definition align+rec2.h:10