49 return 0.2126f*
r + 0.7152f*
b + 0.0722f*
b;
124 return {lhs.
r*rhs, lhs.
g*rhs, lhs.
b*rhs};
131 return {rhs.
r*lhs, rhs.
g*lhs, rhs.
b*lhs};
140 return lhs.
r * rhs.
r + lhs.
g * rhs.
g + lhs.
b * rhs.
b;
158 : r(c.r), g(c.g), b(c.b), a(alpha)
172 return fmt::format(
"#{:0>2x}{:0>2x}{:0>2x}", c.r, c.g, c.b);
177 return fmt::format(
"({}, {}, {}, {})", c.r, c.g, c.b, c.a);
182 return fmt::format(
"({}, {}, {})", v.r, v.g, v.b);
187 return fmt::format(
"({}, {}, {}, {})", v.r, v.g, v.b, v.a);
192 return fmt::format(
"({:.0f}°, {:.0f}%, {:.0f}%)", v.h.as_degrees(), v.s * 100, v.l * 100);
202 return lhs.
r == rhs.
r && lhs.
g == rhs.
g && lhs.
b == rhs.
b;
209 return !(lhs == rhs);
216 return lhs.
r == rhs.
r && lhs.
g == rhs.
g && lhs.
b == rhs.
b && lhs.
a == rhs.
a;
223 return !(lhs == rhs);
242 return {c.r, c.g, c.b};
266 auto hue2rgb = [](
float p,
float q,
float t)
268 if(t < 0.0f) { t += 1.0f; }
269 if(t > 1.0f) { t -= 1.0f; }
271 if(t < 1.0f / 6.0f) {
return p + (q - p) * 6.0f * t; }
272 else if(t < 1.0f / 2.0f) {
return q; }
273 else if(t < 2.0f / 3.0f) {
return p + (q - p) * (2.0f / 3.0f - t) * 6.0f; }
277 const auto q = hsl.
l < 0.5f ? hsl.
l * (1.0f + hsl.
s)
278 : hsl.
l + hsl.
s - hsl.
l * hsl.
s;
279 const auto p = 2.0f * hsl.
l - q;
297 const auto l = (
max +
min) / 2;
300 enum class BiggestValue
308 const auto cl = [](
float r,
float g,
float b) -> BiggestValue {
309 constexpr
auto min_diff = 0.001f;
310 if(
abs(r - g) < min_diff &&
abs(g - b) < min_diff)
312 return BiggestValue::same;
316 return BiggestValue::r;
320 return BiggestValue::g;
322 ASSERTX(b >= r && b >= g, r, g, b);
323 return BiggestValue::b;
326 if(cl == BiggestValue::same)
333 const auto s = l > 0.5f ? d / (2 -
max -
min) : d / (
max +
min);
335 const float h = [cl, &c, d]() ->
float
339 case BiggestValue::r:
return (c.g - c.b) / d + (c.g < c.b ? 6.0f : 0.0f);
340 case BiggestValue::g:
return (c.b - c.r) / d + 2;
341 case BiggestValue::b:
return (c.r - c.g) / d + 4;
342 default:
DIE(
"Unreachable");
return 0.0f;
376 return {c.r, c.g, c.b};
424 return fmt::format(
"0x{:0>2x}{:0>2x}{:0>2x}", c.r, c.g, c.b);
429 return fmt::format(
"rgb({}, {}, {})", c.r, c.g, c.b);
441 hsl.
s += hsl.s * amount;
458 hsl.
s -= hsl.s * amount;
475 hsl.
l += hsl.l * amount;
492 hsl.
l -= hsl.l * amount;
506 const float t = percentage < 0 ? 0.0f : 1.0f;
507 const float p = percentage < 0 ? -percentage : percentage;
508 const float r = (t - color.r) * p + color.r;
509 const float g = (t - color.g) * p + color.g;
510 const float b = (t - color.b) * p + color.b;
518 constexpr std::optional<U8> parse_hex_char(
char c)
522 case '0':
return {
static_cast<U8>(0)};
523 case '1':
return {
static_cast<U8>(1)};
524 case '2':
return {
static_cast<U8>(2)};
525 case '3':
return {
static_cast<U8>(3)};
526 case '4':
return {
static_cast<U8>(4)};
527 case '5':
return {
static_cast<U8>(5)};
528 case '6':
return {
static_cast<U8>(6)};
529 case '7':
return {
static_cast<U8>(7)};
530 case '8':
return {
static_cast<U8>(8)};
531 case '9':
return {
static_cast<U8>(9)};
533 case 'a':
case 'A':
return {
static_cast<U8>(10)};
534 case 'b':
case 'B':
return {
static_cast<U8>(11)};
535 case 'c':
case 'C':
return {
static_cast<U8>(12)};
536 case 'd':
case 'D':
return {
static_cast<U8>(13)};
537 case 'e':
case 'E':
return {
static_cast<U8>(14)};
538 case 'f':
case 'F':
return {
static_cast<U8>(15)};
545 constexpr std::optional<U8> combine_hex_char(std::optional<U8> pc, std::optional<U8> pd)
549 return static_cast<U8>((*pc<<4) | *pd);
559 std::pair<std::optional<U8>, std::string_view>
560 parse_hex(
const std::string_view& value,
int index)
562 const auto s = value.substr(1 + index * size, size);
565 if constexpr (size == 1)
567 const auto p = parse_hex_char(s[0]);
568 r = combine_hex_char(p, p);
570 else if constexpr (size == 2)
574 parse_hex_char(s[0]),
580 ASSERT(
false &&
"unreachable");
583 if (r) {
return { *
r,
s }; }
584 else {
return { std::nullopt,
s }; };
587 using R = Result<Rgbi>;
591 R parse_rgb_hex(
const std::string_view& value)
593 const auto [
r, r_value] = parse_hex<size>(value, 0);
594 const auto [
g, g_value] = parse_hex<size>(value, 1);
595 const auto [
b, b_value] = parse_hex<size>(value, 2);
598 return R::create_value({ *
r, *
g, *
b });
602 auto invalids = std::vector<std::string>{};
603 if (!r) { invalids.emplace_back(fmt::format(
"red({})", r_value)); }
604 if (!g) { invalids.emplace_back(fmt::format(
"green({})", g_value)); }
605 if (!b) { invalids.emplace_back(fmt::format(
"blue({})", b_value)); }
606 return R::create_error
609 "#color contains invalid hex for {}",
618 parse_hash_hex_rgbi(
const std::string& value)
620 const auto size = value.length();
623 case 4:
return parse_rgb_hex<1>(value);
624 case 7:
return parse_rgb_hex<2>(value);
626 return R::create_error
629 (
"a hexadecimal color needs to be either #abc or #aabbcc. current count: {}", size-1)
639 const auto value =
trim(original_value);
641 if(value.empty()) {
return R::create_error(
"empty string is not a color");}
645 return parse_hash_hex_rgbi(value);
649 const auto match = from_string_to_enum<NamedColor>(value);
650 if(match.single_match) {
return R::create_value(
to_rgbi(match.values[0])); }
651 return R::create_error
655 "bad name. Hex values require a #, but it could also be either {}",
std::string trim(const std::string &string_to_trim, std::string_view trim_characters)
Remove characters from both the start and the end.
constexpr unsigned int to_color_hex(unsigned int r, unsigned int g, unsigned int b)
constexpr U8 get_green(unsigned int c)
constexpr float to_float(U8 c)
constexpr U8 to_unsigned_char(float f)
constexpr U8 get_red(unsigned int c)
constexpr U8 get_blue(unsigned int c)
constexpr StringMerger english_and
constexpr StringMerger english_or
T keep_within(const Range< T > &range, T value)
Rgbai to_rgbai(const Rgba &c)
Angle operator+(const Angle &lhs, const Angle &rhs)
bool operator==(const Lrud< T > &lhs, const Lrud< T > &rhs)
Rgbi to_rgbi(const Rgb &c)
Angle operator-(const Angle &lhs, const Angle &rhs)
Hsl get_saturated(const Hsl &ahsl, float amount, IsAbsolute method)
Angle operator/(const Angle &lhs, float rhs)
float lerp_float(float f, float scale, float t)
Hsl get_lightened(const Hsl &ahsl, float amount, IsAbsolute method)
Rgb to_rgb(const Rgbi &c)
std::string to_html_rgb(const Rgbi &c)
constexpr float abs(float r)
std::string to_string(const Aabb &a)
bool operator!=(const Lrud< T > &lhs, const Lrud< T > &rhs)
Rgb get_shaded_color(const Rgb &color, float percentage)
Makes a color brighter or darker.
Result< Rgbi > to_rgbi(const std::string &original_value)
size2f min(const size2f lhs, const size2f rhs)
Rgba to_rgba(const Rgbai &c)
Rgb lerp_rgb(const Rgb &from, float v, const Rgb &to)
Hsl get_darkened(const Hsl &ahsl, float amount, IsAbsolute method)
constexpr Range< float > r01
std::string to_js_hex_color(const Rgbi &c)
size2f max(const size2f lhs, const size2f rhs)
Angle operator*(const Angle &lhs, float rhs)
float dot(const quatf &lhs, const quatf &rhs)
Hsl get_desaturated(const Hsl &ahsl, float amount, IsAbsolute method)
String utility functions.
constexpr static Angle from_percent_of_360(float percent)
constexpr static Angle from_radians(float radians)
constexpr float from_percent_of_360() const
void operator-=(const Rgb &rhs)
void operator/=(float rhs)
float calc_luminance() const
Rgb(float red, float green, float blue)
float get_length_squared() const
static Rgb from_hex(unsigned int hex)
void operator*=(const Rgb &rhs)
void operator+=(const Rgb &rhs)
Rgba(float red, float green, float blue, float alpha=1.0f)