srctree

Robin Linden parent 894ef40e 264c82f0
gfx: Implement creating Color from hsl/a values

gfx/color.h added: 70, removed: 2, total 68
@@ -5,6 +5,8 @@
#ifndef GFX_COLOR_H_
#define GFX_COLOR_H_
 
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <optional>
#include <string_view>
@@ -27,6 +29,34 @@ struct Color {
return color;
}
 
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
static Color from_hsl(float hue, float saturation, float light) { return from_hsla(hue, saturation, light, 1.f); }
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
static Color from_hsla(float hue, float saturation, float light, float alpha) {
// https://www.w3.org/TR/css-color-3/#hsl-color
hue = std::fmod(hue, 360.f);
if (hue < 0.f) {
hue += 360.f;
}
 
saturation = std::clamp(saturation, 0.f, 1.f);
light = std::clamp(light, 0.f, 1.f);
alpha = std::clamp(alpha, 0.f, 1.f);
 
auto hue_to_rgb = [&](int n) {
float k = std::fmod(n + hue / 30.f, 12.f);
float aa = saturation * std::min(light, 1.f - light);
return light - aa * std::clamp(std::min(k - 3.f, 9.f - k), -1.f, 1.f);
};
 
return {
.r = static_cast<std::uint8_t>(std::lround(255 * hue_to_rgb(0))),
.g = static_cast<std::uint8_t>(std::lround(255 * hue_to_rgb(8))),
.b = static_cast<std::uint8_t>(std::lround(255 * hue_to_rgb(4))),
.a = static_cast<std::uint8_t>(std::lround(255 * alpha)),
};
}
 
static std::optional<Color> from_css_name(std::string_view);
 
std::uint8_t r, g, b, a{0xFF};
 
gfx/color_test.cpp added: 70, removed: 2, total 68
@@ -26,6 +26,44 @@ int main() {
expect(Color{0xFF, 0xFF, 0xFF, 0x00} == Color::from_rgba(0xFF'FF'FF'00));
});
 
// Some of the HSL test-cases are from or inspired by
// https://github.com/web-platform-tests/wpt/blob/0bbb3104a8bc5381d3974adf4535fa0dfe191060/css/css-color/parsing/color-computed-hsl.html
etest::test("Color::from_hsl", [] {
expect_eq(Color::from_hsl(120.f, 1.f, 0.25f), Color{.g = 0x80});
 
expect_eq(Color::from_hsl(120.f, .3f, .5f), Color{89, 166, 89});
expect_eq(Color::from_hsl(0.f, 0.f, 0.f), Color{0, 0, 0});
expect_eq(Color::from_hsl(0.f, 1.f, .5f), Color{255, 0, 0});
expect_eq(Color::from_hsl(120.f, 0.f, 0.f), Color{0, 0, 0});
expect_eq(Color::from_hsl(120.f, 0.f, .5f), Color{128, 128, 128});
expect_eq(Color::from_hsl(120.f, 1.f, .5f), Color{0, 255, 0});
expect_eq(Color::from_hsl(120.f, .3f, .5f), Color{89, 166, 89});
expect_eq(Color::from_hsl(120.f, .8f, 0), Color{0, 0, 0});
 
expect_eq(Color::from_hsl(300.f, .5f, .5f), Color{191, 64, 191});
expect_eq(Color::from_hsl(60.f, 1.00f, .375f), Color{191, 191, 0});
expect_eq(Color::from_hsl(30.f, 1.f, 1.f), Color{255, 255, 255});
 
// Angles are represented as a part of a circle and wrap around.
expect_eq(Color::from_hsl(-300.f, 1.f, .375f), Color{191, 191, 0});
expect_eq(Color::from_hsl(780.f, 1.f, .375f), Color{191, 191, 0});
});
 
etest::test("Color::from_hsl/a", [] {
expect_eq(Color::from_hsla(0.f, 0.f, 0.f, 0.f), Color{0, 0, 0, 0});
expect_eq(Color::from_hsla(0.f, 0.f, 0.f, 0.5f), Color{0, 0, 0, 128});
expect_eq(Color::from_hsla(120.f, .3f, .5f, .5f), Color{89, 166, 89, 128});
expect_eq(Color::from_hsla(30.f, 1.f, 1.f, 1.f), Color{255, 255, 255});
 
// Angles are represented as a part of a circle and wrap around.
// Invalid alpha values should be clamped to 0 and 1 respectively
expect_eq(Color::from_hsla(-300.f, 1.f, .375f, -3.f), Color{191, 191, 0, 0});
expect_eq(Color::from_hsla(-300.f, 1.f, .375f, 0.f), Color{191, 191, 0, 0});
expect_eq(Color::from_hsla(-300.f, 1.f, .375f, .2f), Color{191, 191, 0, 51});
expect_eq(Color::from_hsla(-300.f, 1.f, .375f, 1.f), Color{191, 191, 0});
expect_eq(Color::from_hsla(-300.f, 1.f, .375f, 12.f), Color{191, 191, 0});
});
 
etest::test("Color::from_css_name", [] {
expect_eq(Color::from_css_name("blue"), Color{.b = 0xFF});
expect_eq(Color::from_css_name("not a valid css name"), std::nullopt);