mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-25 17:10:23 +00:00
LibWeb/CSS: Split parse_rgb_or_hsl_color into separate functions
This commit is contained in:
parent
65be928d4e
commit
490a36bab1
Notes:
sideshowbarker
2024-07-17 23:07:41 +09:00
Author: https://github.com/Gingeh 🔰 Commit: https://github.com/LadybirdBrowser/ladybird/commit/490a36bab1 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/385 Reviewed-by: https://github.com/ADKaster ✅
2 changed files with 213 additions and 135 deletions
|
@ -2277,139 +2277,6 @@ RefPtr<StyleValue> Parser::parse_identifier_value(TokenStream<ComponentValue>& t
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
Optional<Color> Parser::parse_rgb_or_hsl_color(StringView function_name, Vector<ComponentValue> const& component_values)
|
||||
{
|
||||
Token params[4];
|
||||
bool legacy_syntax = false;
|
||||
auto tokens = TokenStream { component_values };
|
||||
|
||||
tokens.skip_whitespace();
|
||||
auto const& component1 = tokens.next_token();
|
||||
|
||||
if (!component1.is(Token::Type::Number)
|
||||
&& !component1.is(Token::Type::Percentage)
|
||||
&& !component1.is(Token::Type::Dimension))
|
||||
return {};
|
||||
params[0] = component1.token();
|
||||
|
||||
tokens.skip_whitespace();
|
||||
if (tokens.peek_token().is(Token::Type::Comma)) {
|
||||
legacy_syntax = true;
|
||||
tokens.next_token();
|
||||
}
|
||||
|
||||
tokens.skip_whitespace();
|
||||
auto const& component2 = tokens.next_token();
|
||||
if (!component2.is(Token::Type::Number) && !component2.is(Token::Type::Percentage))
|
||||
return {};
|
||||
params[1] = component2.token();
|
||||
|
||||
tokens.skip_whitespace();
|
||||
if (legacy_syntax && !tokens.next_token().is(Token::Type::Comma))
|
||||
return {};
|
||||
|
||||
tokens.skip_whitespace();
|
||||
auto const& component3 = tokens.next_token();
|
||||
if (!component3.is(Token::Type::Number) && !component3.is(Token::Type::Percentage))
|
||||
return {};
|
||||
params[2] = component3.token();
|
||||
|
||||
tokens.skip_whitespace();
|
||||
auto const& alpha_separator = tokens.peek_token();
|
||||
bool has_comma = alpha_separator.is(Token::Type::Comma);
|
||||
bool has_slash = alpha_separator.is_delim('/');
|
||||
if (legacy_syntax ? has_comma : has_slash) {
|
||||
tokens.next_token();
|
||||
|
||||
tokens.skip_whitespace();
|
||||
auto const& component4 = tokens.next_token();
|
||||
if (!component4.is(Token::Type::Number) && !component4.is(Token::Type::Percentage))
|
||||
return {};
|
||||
params[3] = component4.token();
|
||||
}
|
||||
|
||||
tokens.skip_whitespace();
|
||||
if (tokens.has_next_token())
|
||||
return {};
|
||||
|
||||
if (function_name.equals_ignoring_ascii_case("rgb"sv)
|
||||
|| function_name.equals_ignoring_ascii_case("rgba"sv)) {
|
||||
|
||||
// https://www.w3.org/TR/css-color-4/#rgb-functions
|
||||
|
||||
u8 a_val = 255;
|
||||
if (params[3].is(Token::Type::Number))
|
||||
a_val = clamp(lround(params[3].number_value() * 255.0), 0, 255);
|
||||
else if (params[3].is(Token::Type::Percentage))
|
||||
a_val = clamp(lround(params[3].percentage() * 2.55), 0, 255);
|
||||
|
||||
if (params[0].is(Token::Type::Number)
|
||||
&& params[1].is(Token::Type::Number)
|
||||
&& params[2].is(Token::Type::Number)) {
|
||||
|
||||
u8 r_val = clamp(llroundf(params[0].number_value()), 0, 255);
|
||||
u8 g_val = clamp(llroundf(params[1].number_value()), 0, 255);
|
||||
u8 b_val = clamp(llroundf(params[2].number_value()), 0, 255);
|
||||
|
||||
return Color(r_val, g_val, b_val, a_val);
|
||||
}
|
||||
|
||||
if (params[0].is(Token::Type::Percentage)
|
||||
&& params[1].is(Token::Type::Percentage)
|
||||
&& params[2].is(Token::Type::Percentage)) {
|
||||
|
||||
u8 r_val = lround(clamp(params[0].percentage() * 2.55, 0, 255));
|
||||
u8 g_val = lround(clamp(params[1].percentage() * 2.55, 0, 255));
|
||||
u8 b_val = lround(clamp(params[2].percentage() * 2.55, 0, 255));
|
||||
|
||||
return Color(r_val, g_val, b_val, a_val);
|
||||
}
|
||||
} else if (function_name.equals_ignoring_ascii_case("hsl"sv)
|
||||
|| function_name.equals_ignoring_ascii_case("hsla"sv)) {
|
||||
|
||||
// https://www.w3.org/TR/css-color-4/#the-hsl-notation
|
||||
|
||||
auto a_val = 1.0;
|
||||
if (params[3].is(Token::Type::Number))
|
||||
a_val = params[3].number_value();
|
||||
else if (params[3].is(Token::Type::Percentage))
|
||||
a_val = params[3].percentage() / 100.0;
|
||||
|
||||
if (params[0].is(Token::Type::Dimension)
|
||||
&& params[1].is(Token::Type::Percentage)
|
||||
&& params[2].is(Token::Type::Percentage)) {
|
||||
|
||||
auto numeric_value = params[0].dimension_value();
|
||||
auto unit_string = params[0].dimension_unit();
|
||||
auto angle_type = Angle::unit_from_name(unit_string);
|
||||
|
||||
if (!angle_type.has_value())
|
||||
return {};
|
||||
|
||||
auto angle = Angle { numeric_value, angle_type.release_value() };
|
||||
|
||||
float h_val = fmod(angle.to_degrees(), 360.0);
|
||||
float s_val = params[1].percentage() / 100.0;
|
||||
float l_val = params[2].percentage() / 100.0;
|
||||
|
||||
return Color::from_hsla(h_val, s_val, l_val, a_val);
|
||||
}
|
||||
|
||||
if (params[0].is(Token::Type::Number)
|
||||
&& params[1].is(Token::Type::Percentage)
|
||||
&& params[2].is(Token::Type::Percentage)) {
|
||||
|
||||
float h_val = fmod(params[0].number_value(), 360.0);
|
||||
float s_val = params[1].percentage() / 100.0;
|
||||
float l_val = params[2].percentage() / 100.0;
|
||||
|
||||
return Color::from_hsla(h_val, s_val, l_val, a_val);
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// https://www.w3.org/TR/CSS2/visufx.html#value-def-shape
|
||||
RefPtr<StyleValue> Parser::parse_rect_value(TokenStream<ComponentValue>& tokens)
|
||||
{
|
||||
|
@ -2488,6 +2355,210 @@ RefPtr<StyleValue> Parser::parse_rect_value(TokenStream<ComponentValue>& tokens)
|
|||
return RectStyleValue::create(EdgeRect { params[0], params[1], params[2], params[3] });
|
||||
}
|
||||
|
||||
Optional<Color> Parser::parse_rgb_color(Vector<ComponentValue> const& component_values)
|
||||
{
|
||||
u8 r_val = 0;
|
||||
u8 g_val = 0;
|
||||
u8 b_val = 0;
|
||||
|
||||
auto tokens = TokenStream { component_values };
|
||||
|
||||
tokens.skip_whitespace();
|
||||
auto const& red = tokens.next_token();
|
||||
|
||||
if (!red.is(Token::Type::Number)
|
||||
&& !red.is(Token::Type::Percentage))
|
||||
return {};
|
||||
|
||||
tokens.skip_whitespace();
|
||||
bool legacy_syntax = tokens.peek_token().is(Token::Type::Comma);
|
||||
if (legacy_syntax) {
|
||||
// Legacy syntax.
|
||||
tokens.next_token();
|
||||
tokens.skip_whitespace();
|
||||
|
||||
auto const& green = tokens.next_token();
|
||||
tokens.skip_whitespace();
|
||||
|
||||
tokens.next_token();
|
||||
tokens.skip_whitespace();
|
||||
|
||||
auto const& blue = tokens.next_token();
|
||||
|
||||
if (red.is(Token::Type::Percentage)) {
|
||||
// Percentage components.
|
||||
if (!green.is(Token::Type::Percentage) || !blue.is(Token::Type::Percentage))
|
||||
return {};
|
||||
|
||||
r_val = lround(clamp(red.token().percentage() * 2.55, 0, 255));
|
||||
g_val = lround(clamp(green.token().percentage() * 2.55, 0, 255));
|
||||
b_val = lround(clamp(blue.token().percentage() * 2.55, 0, 255));
|
||||
} else {
|
||||
// Number components.
|
||||
if (!green.is(Token::Type::Number) || !blue.is(Token::Type::Number))
|
||||
return {};
|
||||
|
||||
r_val = clamp(llroundf(red.token().number_value()), 0, 255);
|
||||
g_val = clamp(llroundf(green.token().number_value()), 0, 255);
|
||||
b_val = clamp(llroundf(blue.token().number_value()), 0, 255);
|
||||
}
|
||||
} else {
|
||||
// Modern syntax.
|
||||
|
||||
if (red.is(Token::Type::Number)) {
|
||||
r_val = clamp(llroundf(red.token().number_value()), 0, 255);
|
||||
} else {
|
||||
r_val = lround(clamp(red.token().percentage() * 2.55, 0, 255));
|
||||
}
|
||||
|
||||
auto const& green = tokens.next_token();
|
||||
if (green.is(Token::Type::Number)) {
|
||||
g_val = clamp(llroundf(green.token().number_value()), 0, 255);
|
||||
} else if (green.is(Token::Type::Percentage)) {
|
||||
g_val = lround(clamp(green.token().percentage() * 2.55, 0, 255));
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
|
||||
tokens.skip_whitespace();
|
||||
auto const& blue = tokens.next_token();
|
||||
if (blue.is(Token::Type::Number)) {
|
||||
b_val = clamp(llroundf(blue.token().number_value()), 0, 255);
|
||||
} else if (blue.is(Token::Type::Percentage)) {
|
||||
b_val = lround(clamp(blue.token().percentage() * 2.55, 0, 255));
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
u8 alpha_val = 255;
|
||||
tokens.skip_whitespace();
|
||||
if (tokens.has_next_token()) {
|
||||
auto const& separator = tokens.next_token();
|
||||
bool is_comma = separator.is(Token::Type::Comma);
|
||||
bool is_slash = separator.is_delim('/');
|
||||
if (legacy_syntax ? !is_comma : !is_slash)
|
||||
return {};
|
||||
|
||||
tokens.skip_whitespace();
|
||||
auto const& alpha = tokens.next_token();
|
||||
|
||||
if (alpha.is(Token::Type::Number))
|
||||
alpha_val = clamp(lround(alpha.token().number_value() * 255.0), 0, 255);
|
||||
else if (alpha.is(Token::Type::Percentage))
|
||||
alpha_val = clamp(lround(alpha.token().percentage() * 2.55), 0, 255);
|
||||
else
|
||||
return {};
|
||||
|
||||
tokens.skip_whitespace();
|
||||
if (tokens.has_next_token())
|
||||
return {}; // should have consumed all arguments.
|
||||
}
|
||||
|
||||
return Color(r_val, g_val, b_val, alpha_val);
|
||||
}
|
||||
|
||||
Optional<Color> Parser::parse_hsl_color(Vector<ComponentValue> const& component_values)
|
||||
{
|
||||
float h_val = 0.0;
|
||||
float s_val = 0.0;
|
||||
float l_val = 0.0;
|
||||
|
||||
auto tokens = TokenStream { component_values };
|
||||
|
||||
tokens.skip_whitespace();
|
||||
auto const& hue = tokens.next_token();
|
||||
|
||||
if (!hue.is(Token::Type::Number)
|
||||
&& !hue.is(Token::Type::Dimension))
|
||||
return {};
|
||||
|
||||
if (hue.is(Token::Type::Number)) {
|
||||
h_val = fmod(hue.token().number_value(), 360.0);
|
||||
} else {
|
||||
auto numeric_value = hue.token().dimension_value();
|
||||
auto unit_string = hue.token().dimension_unit();
|
||||
auto angle_type = Angle::unit_from_name(unit_string);
|
||||
|
||||
if (!angle_type.has_value())
|
||||
return {};
|
||||
|
||||
auto angle = Angle { numeric_value, angle_type.release_value() };
|
||||
|
||||
h_val = fmod(angle.to_degrees(), 360.0);
|
||||
}
|
||||
|
||||
tokens.skip_whitespace();
|
||||
bool legacy_syntax = tokens.peek_token().is(Token::Type::Comma);
|
||||
if (legacy_syntax) {
|
||||
// legacy syntax.
|
||||
tokens.next_token();
|
||||
tokens.skip_whitespace();
|
||||
|
||||
auto const& saturation = tokens.next_token();
|
||||
if (!saturation.is(Token::Type::Percentage))
|
||||
return {};
|
||||
s_val = max(saturation.token().percentage() / 100.0, 0);
|
||||
|
||||
tokens.skip_whitespace();
|
||||
tokens.next_token();
|
||||
tokens.skip_whitespace();
|
||||
|
||||
auto const& lightness = tokens.next_token();
|
||||
if (!lightness.is(Token::Type::Percentage))
|
||||
return {};
|
||||
l_val = lightness.token().percentage() / 100.0;
|
||||
} else {
|
||||
// Modern syntax.
|
||||
|
||||
auto const& saturation = tokens.next_token();
|
||||
if (saturation.is(Token::Type::Number)) {
|
||||
s_val = saturation.token().number_value() / 100.0;
|
||||
} else if (saturation.is(Token::Type::Percentage)) {
|
||||
s_val = saturation.token().percentage() / 100.0;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
s_val = max(s_val, 0);
|
||||
|
||||
tokens.skip_whitespace();
|
||||
auto const& lightness = tokens.next_token();
|
||||
if (lightness.is(Token::Type::Number)) {
|
||||
l_val = lightness.token().number_value() / 100.0;
|
||||
} else if (lightness.is(Token::Type::Percentage)) {
|
||||
l_val = lightness.token().percentage() / 100.0;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
float alpha_val = 1.0;
|
||||
tokens.skip_whitespace();
|
||||
if (tokens.has_next_token()) {
|
||||
auto const& separator = tokens.next_token();
|
||||
bool is_comma = separator.is(Token::Type::Comma);
|
||||
bool is_slash = separator.is_delim('/');
|
||||
if (legacy_syntax ? !is_comma : !is_slash)
|
||||
return {};
|
||||
|
||||
tokens.skip_whitespace();
|
||||
auto const& alpha = tokens.next_token();
|
||||
|
||||
if (alpha.is(Token::Type::Number))
|
||||
alpha_val = alpha.token().number_value();
|
||||
else if (alpha.is(Token::Type::Percentage))
|
||||
alpha_val = alpha.token().percentage() / 100.0;
|
||||
else
|
||||
return {};
|
||||
|
||||
tokens.skip_whitespace();
|
||||
if (tokens.has_next_token())
|
||||
return {}; // should have consumed all arguments.
|
||||
}
|
||||
|
||||
return Color::from_hsla(h_val, s_val, l_val, alpha_val);
|
||||
}
|
||||
|
||||
Optional<Color> Parser::parse_color(ComponentValue const& component_value)
|
||||
{
|
||||
// https://www.w3.org/TR/css-color-4/
|
||||
|
@ -2507,8 +2578,14 @@ Optional<Color> Parser::parse_color(ComponentValue const& component_value)
|
|||
} else if (component_value.is_function()) {
|
||||
auto const& function = component_value.function();
|
||||
auto const& values = function.values();
|
||||
auto const function_name = function.name();
|
||||
|
||||
return parse_rgb_or_hsl_color(function.name(), values);
|
||||
if (function_name.equals_ignoring_ascii_case("rgb"sv) || function_name.equals_ignoring_ascii_case("rgba"sv))
|
||||
return parse_rgb_color(values);
|
||||
if (function_name.equals_ignoring_ascii_case("hsl"sv) || function_name.equals_ignoring_ascii_case("hsla"sv))
|
||||
return parse_hsl_color(values);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// https://quirks.spec.whatwg.org/#the-hashless-hex-color-quirk
|
||||
|
|
|
@ -238,7 +238,8 @@ private:
|
|||
Optional<TimeOrCalculated> parse_time(TokenStream<ComponentValue>&);
|
||||
Optional<TimePercentage> parse_time_percentage(TokenStream<ComponentValue>&);
|
||||
|
||||
Optional<Color> parse_rgb_or_hsl_color(StringView function_name, Vector<ComponentValue> const&);
|
||||
Optional<Color> parse_rgb_color(Vector<ComponentValue> const&);
|
||||
Optional<Color> parse_hsl_color(Vector<ComponentValue> const&);
|
||||
Optional<Color> parse_color(ComponentValue const&);
|
||||
Optional<LengthOrCalculated> parse_source_size_value(TokenStream<ComponentValue>&);
|
||||
Optional<Ratio> parse_ratio(TokenStream<ComponentValue>&);
|
||||
|
|
Loading…
Reference in a new issue