From 185e38c9644cbc6e31933aef87ddaf0508e1f01a Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Wed, 3 Mar 2021 12:42:45 +0000 Subject: [PATCH] [Project] Css: Add colors conversion functions --- src/libserver/css/css_value.cxx | 243 ++++++++++++++++++++++++++++++++ src/libserver/css/css_value.hxx | 16 ++- 2 files changed, 257 insertions(+), 2 deletions(-) diff --git a/src/libserver/css/css_value.cxx b/src/libserver/css/css_value.cxx index 520127da8..ac65c7422 100644 --- a/src/libserver/css/css_value.cxx +++ b/src/libserver/css/css_value.cxx @@ -34,9 +34,252 @@ auto css_value::maybe_color_from_string(const std::string_view &input) auto found_it = css_colors_map.find(input); if (found_it != css_colors_map.end()) { + return css_value{found_it->second}; + } + + return std::nullopt; +} + +constexpr static inline auto hexpair_decode(char c1, char c2) -> std::uint8_t +{ + std::uint8_t ret = 0; + + if (c1 >= '0' && c1 <= '9') ret = c1 - '0'; + else if (c1 >= 'A' && c1 <= 'F') ret = c1 - 'A' + 10; + else if (c1 >= 'a' && c1 <= 'f') ret = c1 - 'a' + 10; + + ret *= 16; + + if (c2 >= '0' && c2 <= '9') ret += c2 - '0'; + else if (c2 >= 'A' && c2 <= 'F') ret += c2 - 'A' + 10; + else if (c2 >= 'a' && c2 <= 'f') ret += c2 - 'a' + 10; + return ret; +} + +auto css_value::maybe_color_from_hex(const std::string_view &input) + -> std::optional +{ + if (input.length() == 6) { + /* Plain RGB */ + css_color col(hexpair_decode(input[0], input[1]), + hexpair_decode(input[2], input[3]), + hexpair_decode(input[4], input[5])); + return css_value(col); + } + else if (input.length() == 8) { + /* RGBA */ + css_color col(hexpair_decode(input[0], input[1]), + hexpair_decode(input[2], input[3]), + hexpair_decode(input[4], input[5]), + hexpair_decode(input[6], input[7])); + return css_value(col); } return std::nullopt; } + +constexpr static inline auto rgb_color_component_convert(const css_parser_token &tok) + -> std::uint8_t +{ + std::uint8_t ret = 0; + + if (tok.type == css_parser_token::token_type::number_token) { + auto dbl = std::get(tok.value); + + if (tok.flags & css_parser_token::number_percent) { + if (dbl > 100) { + dbl = 100; + } + else if (dbl < 0) { + dbl = 0; + } + ret = (std::uint8_t)(dbl / 100.0 * 255.0); + } + else { + if (dbl > 1) { + dbl = 1; + } + else if (dbl < 0) { + dbl = 0; + } + + ret = (std::uint8_t)(dbl * 255.0); + } + } + + return ret; +} + +constexpr static inline auto alpha_component_convert(const css_parser_token &tok) + -> std::uint8_t +{ + double ret = 1.0; + + if (tok.type == css_parser_token::token_type::number_token) { + auto dbl = std::get(tok.value); + + if (tok.flags & css_parser_token::number_percent) { + if (dbl > 100) { + dbl = 100; + } + else if (dbl < 0) { + dbl = 0; + } + ret = (dbl / 100.0); + } + else { + if (dbl > 255) { + dbl = 255; + } + else if (dbl < 0) { + dbl = 0; + } + + ret = dbl / 255.0; + } + } + + return (std::uint8_t)(ret * 255.0); +} + +constexpr static inline auto h_component_convert(const css_parser_token &tok) + -> double +{ + double ret = 0.0; + + if (tok.type == css_parser_token::token_type::number_token) { + auto dbl = std::get(tok.value); + + if (tok.flags & css_parser_token::number_percent) { + if (dbl > 100) { + dbl = 100; + } + else if (dbl < 0) { + dbl = 0; + } + ret = (dbl / 100.0) * 360.0; + } + else { + if (dbl > 360) { + dbl = 360; + } + else if (dbl < 0) { + dbl = 0; + } + + ret = dbl; + } + } + + return ret; +} + +constexpr static inline auto sl_component_convert(const css_parser_token &tok) + -> double +{ + double ret = 0.0; + + if (tok.type == css_parser_token::token_type::number_token) { + auto dbl = std::get(tok.value); + + if (tok.flags & css_parser_token::number_percent) { + if (dbl > 100) { + dbl = 100; + } + else if (dbl < 0) { + dbl = 0; + } + ret = (dbl / 100.0); + } + else { + if (dbl > 1) { + dbl = 1; + } + else if (dbl < 0) { + dbl = 0; + } + + ret = (dbl); + } + } + + return ret; +} + +static inline auto hsl_to_rgb(double h, double s, double l) + -> css_color +{ + constexpr auto hue2rgb = [](auto p, auto q, auto t) -> auto { + if (t < 0.0) { + t += 1.0; + } + if (t > 1.0) { + t -= 1.0; + } + if (t < 1.0/6.0) { + return p + (q - p) * 6.0 * t; + } + if (t < 0.5) { + return q; + } + if (t < 2.0/3.0) { + return p + (q - p) * (2.0/3.0 - t) * 6.0; + } + return p * 255.0; + }; + + css_color ret; + + auto q = l < 0.5 ? l * (1.0 + s) : l + s - l * s; + auto p = 2.0 * l - q; + ret.r = (std::uint8_t)hue2rgb(p, q, h + 1.0/3.0); + ret.g = (std::uint8_t)hue2rgb(p, q, h); + ret.b = (std::uint8_t)hue2rgb(p, q, h - 1.0/3.0); + + return ret; +} + +auto css_value::maybe_color_from_function(const std::string_view &func, + const std::vector &args) + -> std::optional +{ + if (func == "rgb" && args.size() == 3) { + css_color col{rgb_color_component_convert(args[0]), + rgb_color_component_convert(args[1]), + rgb_color_component_convert(args[2])}; + + return css_value(col); + } + else if (func == "rgba" && args.size() == 4) { + css_color col{rgb_color_component_convert(args[0]), + rgb_color_component_convert(args[1]), + rgb_color_component_convert(args[2]), + alpha_component_convert(args[3])}; + + return css_value(col); + } + else if (func == "hsl" && args.size() == 3) { + auto h = h_component_convert(args[0]); + auto s = sl_component_convert(args[1]); + auto l = sl_component_convert(args[2]); + + auto col = hsl_to_rgb(h, s, l); + + return css_value(col); + } + else if (func == "hsla" && args.size() == 4) { + auto h = h_component_convert(args[0]); + auto s = sl_component_convert(args[1]); + auto l = sl_component_convert(args[2]); + + auto col = hsl_to_rgb(h, s, l); + col.alpha = alpha_component_convert(args[3]); + + return css_value(col); + } + + return std::nullopt; +} + } diff --git a/src/libserver/css/css_value.hxx b/src/libserver/css/css_value.hxx index 882abf990..90ee4533d 100644 --- a/src/libserver/css/css_value.hxx +++ b/src/libserver/css/css_value.hxx @@ -19,10 +19,10 @@ #ifndef RSPAMD_CSS_VALUE_HXX #define RSPAMD_CSS_VALUE_HXX -#include "libserver/html.h" #include #include #include +#include #include "parse_error.hxx" #include "css_parser.hxx" #include "contrib/expected/expected.hpp" @@ -38,6 +38,7 @@ struct alignas(int) css_color { constexpr css_color(std::uint8_t _r, std::uint8_t _g, std::uint8_t _b, std::uint8_t _alpha = 255) : r(_r), g(_g), b(_b), alpha(_alpha) {} + css_color() = default; }; /* @@ -73,7 +74,13 @@ struct css_value { std::variant value; + css_flag_value, + std::monostate> value; + + css_value(const css_color &color) : + type(css_value_type::CSS_VALUE_COLOR), value(color) {} + css_value(double sz) : + type(css_value_type::CSS_VALUE_SIZE), value(sz) {} constexpr std::optional to_color(void) const { if (type == css_value_type::CSS_VALUE_COLOR) { @@ -115,6 +122,11 @@ struct css_value { static auto maybe_color_from_string(const std::string_view &input) -> std::optional; + static auto maybe_color_from_hex(const std::string_view &input) + -> std::optional; + static auto maybe_color_from_function(const std::string_view &func, + const std::vector &args) + -> std::optional; }; } -- 2.39.5