mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-31 13:19:05 +00:00
LibWeb: Don't resolve colors with unresolved components
`CSSColorValue`s which have unresolved `calc` components should be able to be resolved. Previously we would always resolve them but with incorrect values. This is useful as we will now be able to now whether we should serialize colors in their normalized form or not. Slight regression in that we now serialize (RGB, HSL and HWB) colors with components that rely on compute-time information as an empty string, but that will be fixed in the next commit.
This commit is contained in:
parent
e66332c07a
commit
6a9c8d7767
Notes:
github-actions[bot]
2025-07-16 12:06:47 +00:00
Author: https://github.com/Calme1709
Commit: 6a9c8d7767
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5305
Reviewed-by: https://github.com/AtkinsSJ ✅
30 changed files with 278 additions and 129 deletions
|
@ -393,7 +393,7 @@ public:
|
||||||
|
|
||||||
virtual ValueComparingNonnullRefPtr<CSSStyleValue const> absolutized(CSSPixelRect const& viewport_rect, Length::FontMetrics const& font_metrics, Length::FontMetrics const& root_font_metrics) const;
|
virtual ValueComparingNonnullRefPtr<CSSStyleValue const> absolutized(CSSPixelRect const& viewport_rect, Length::FontMetrics const& font_metrics, Length::FontMetrics const& root_font_metrics) const;
|
||||||
|
|
||||||
virtual Color to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const { return {}; }
|
virtual Optional<Color> to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const { return {}; }
|
||||||
Keyword to_keyword() const;
|
Keyword to_keyword() const;
|
||||||
|
|
||||||
virtual String to_string(SerializationMode) const = 0;
|
virtual String to_string(SerializationMode) const = 0;
|
||||||
|
|
|
@ -226,7 +226,7 @@ Color ComputedProperties::color_or_fallback(PropertyID id, Layout::NodeWithStyle
|
||||||
auto const& value = property(id);
|
auto const& value = property(id);
|
||||||
if (!value.has_color())
|
if (!value.has_color())
|
||||||
return fallback;
|
return fallback;
|
||||||
return value.to_color(node, { .length_resolution_context = Length::ResolutionContext::for_layout_node(node) });
|
return value.to_color(node, { .length_resolution_context = Length::ResolutionContext::for_layout_node(node) }).value();
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://drafts.csswg.org/css-color-adjust-1/#determine-the-used-color-scheme
|
// https://drafts.csswg.org/css-color-adjust-1/#determine-the-used-color-scheme
|
||||||
|
@ -447,7 +447,7 @@ Color ComputedProperties::flood_color(Layout::NodeWithStyle const& node) const
|
||||||
{
|
{
|
||||||
auto const& value = property(PropertyID::FloodColor);
|
auto const& value = property(PropertyID::FloodColor);
|
||||||
if (value.has_color()) {
|
if (value.has_color()) {
|
||||||
return value.to_color(node, { .length_resolution_context = Length::ResolutionContext::for_layout_node(node) });
|
return value.to_color(node, { .length_resolution_context = Length::ResolutionContext::for_layout_node(node) }).value();
|
||||||
}
|
}
|
||||||
|
|
||||||
return InitialValues::flood_color();
|
return InitialValues::flood_color();
|
||||||
|
@ -940,7 +940,7 @@ Color ComputedProperties::caret_color(Layout::NodeWithStyle const& node) const
|
||||||
return node.computed_values().color();
|
return node.computed_values().color();
|
||||||
|
|
||||||
if (value.has_color())
|
if (value.has_color())
|
||||||
return value.to_color(node, { .length_resolution_context = Length::ResolutionContext::for_layout_node(node) });
|
return value.to_color(node, { .length_resolution_context = Length::ResolutionContext::for_layout_node(node) }).value();
|
||||||
|
|
||||||
return InitialValues::caret_color();
|
return InitialValues::caret_color();
|
||||||
}
|
}
|
||||||
|
@ -1203,7 +1203,7 @@ Vector<ShadowData> ComputedProperties::shadow(PropertyID property_id, Layout::No
|
||||||
maybe_offset_y.release_value(),
|
maybe_offset_y.release_value(),
|
||||||
maybe_blur_radius.release_value(),
|
maybe_blur_radius.release_value(),
|
||||||
maybe_spread_distance.release_value(),
|
maybe_spread_distance.release_value(),
|
||||||
value.color()->to_color(as<Layout::NodeWithStyle>(layout_node), { .length_resolution_context = Length::ResolutionContext::for_layout_node(layout_node) }),
|
value.color()->to_color(as<Layout::NodeWithStyle>(layout_node), { .length_resolution_context = Length::ResolutionContext::for_layout_node(layout_node) }).value(),
|
||||||
value.placement()
|
value.placement()
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1826,7 +1826,7 @@ Color ComputedProperties::stop_color() const
|
||||||
// FIXME: This is used by the SVGStopElement, which does not participate in layout, so we can't pass a layout
|
// FIXME: This is used by the SVGStopElement, which does not participate in layout, so we can't pass a layout
|
||||||
// node or CalculationResolutionContext. This means we don't support all valid colors (e.g. palette
|
// node or CalculationResolutionContext. This means we don't support all valid colors (e.g. palette
|
||||||
// colors, calculated values which depend on length resolution, etc)
|
// colors, calculated values which depend on length resolution, etc)
|
||||||
return value->to_color({}, {});
|
return value->to_color({}, {}).value_or(Color::Black);
|
||||||
}
|
}
|
||||||
return Color::Black;
|
return Color::Black;
|
||||||
}
|
}
|
||||||
|
@ -1910,8 +1910,8 @@ ScrollbarColorData ComputedProperties::scrollbar_color(Layout::NodeWithStyle con
|
||||||
|
|
||||||
if (value.is_scrollbar_color()) {
|
if (value.is_scrollbar_color()) {
|
||||||
auto& scrollbar_color_value = value.as_scrollbar_color();
|
auto& scrollbar_color_value = value.as_scrollbar_color();
|
||||||
auto thumb_color = scrollbar_color_value.thumb_color()->to_color(layout_node, { .length_resolution_context = Length::ResolutionContext::for_layout_node(layout_node) });
|
auto thumb_color = scrollbar_color_value.thumb_color()->to_color(layout_node, { .length_resolution_context = Length::ResolutionContext::for_layout_node(layout_node) }).value();
|
||||||
auto track_color = scrollbar_color_value.track_color()->to_color(layout_node, { .length_resolution_context = Length::ResolutionContext::for_layout_node(layout_node) });
|
auto track_color = scrollbar_color_value.track_color()->to_color(layout_node, { .length_resolution_context = Length::ResolutionContext::for_layout_node(layout_node) }).value();
|
||||||
return { thumb_color, track_color };
|
return { thumb_color, track_color };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -688,13 +688,25 @@ RefPtr<CSSStyleValue const> interpolate_box_shadow(DOM::Element& element, Calcul
|
||||||
auto interpolated_spread_distance = interpolate_value(element, calculation_context, from_shadow.spread_distance(), to_shadow.spread_distance(), delta, allow_discrete);
|
auto interpolated_spread_distance = interpolate_value(element, calculation_context, from_shadow.spread_distance(), to_shadow.spread_distance(), delta, allow_discrete);
|
||||||
if (!interpolated_offset_x || !interpolated_offset_y || !interpolated_blur_radius || !interpolated_spread_distance)
|
if (!interpolated_offset_x || !interpolated_offset_y || !interpolated_blur_radius || !interpolated_spread_distance)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
auto color_syntax = ColorSyntax::Legacy;
|
auto color_syntax = ColorSyntax::Legacy;
|
||||||
if ((!from_shadow.color()->is_keyword() && from_shadow.color()->as_color().color_syntax() == ColorSyntax::Modern)
|
if ((!from_shadow.color()->is_keyword() && from_shadow.color()->as_color().color_syntax() == ColorSyntax::Modern)
|
||||||
|| (!to_shadow.color()->is_keyword() && to_shadow.color()->as_color().color_syntax() == ColorSyntax::Modern)) {
|
|| (!to_shadow.color()->is_keyword() && to_shadow.color()->as_color().color_syntax() == ColorSyntax::Modern)) {
|
||||||
color_syntax = ColorSyntax::Modern;
|
color_syntax = ColorSyntax::Modern;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: If we aren't able to resolve the colors here, we should postpone interpolation until we can (perhaps
|
||||||
|
// by creating something similar to a ColorMixStyleValue).
|
||||||
|
auto from_color = from_shadow.color()->to_color(layout_node, resolution_context);
|
||||||
|
auto to_color = to_shadow.color()->to_color(layout_node, resolution_context);
|
||||||
|
|
||||||
|
Color interpolated_color = Color::Black;
|
||||||
|
|
||||||
|
if (from_color.has_value() && to_color.has_value())
|
||||||
|
interpolated_color = interpolate_color(from_color.value(), to_color.value(), delta, color_syntax);
|
||||||
|
|
||||||
auto result_shadow = ShadowStyleValue::create(
|
auto result_shadow = ShadowStyleValue::create(
|
||||||
CSSColorValue::create_from_color(interpolate_color(from_shadow.color()->to_color(layout_node, resolution_context), to_shadow.color()->to_color(layout_node, resolution_context), delta, color_syntax), ColorSyntax::Modern),
|
CSSColorValue::create_from_color(interpolated_color, ColorSyntax::Modern),
|
||||||
*interpolated_offset_x,
|
*interpolated_offset_x,
|
||||||
*interpolated_offset_y,
|
*interpolated_offset_y,
|
||||||
*interpolated_blur_radius,
|
*interpolated_blur_radius,
|
||||||
|
@ -817,7 +829,18 @@ static RefPtr<CSSStyleValue const> interpolate_value_impl(DOM::Element& element,
|
||||||
|| (!to.is_keyword() && to.as_color().color_syntax() == ColorSyntax::Modern)) {
|
|| (!to.is_keyword() && to.as_color().color_syntax() == ColorSyntax::Modern)) {
|
||||||
color_syntax = ColorSyntax::Modern;
|
color_syntax = ColorSyntax::Modern;
|
||||||
}
|
}
|
||||||
return CSSColorValue::create_from_color(interpolate_color(from.to_color(layout_node, resolution_context), to.to_color(layout_node, resolution_context), delta, color_syntax), ColorSyntax::Modern);
|
|
||||||
|
// FIXME: If we aren't able to resolve the colors here, we should postpone interpolation until we can (perhaps
|
||||||
|
// by creating something similar to a ColorMixStyleValue).
|
||||||
|
auto from_color = from.to_color(layout_node, resolution_context);
|
||||||
|
auto to_color = to.to_color(layout_node, resolution_context);
|
||||||
|
|
||||||
|
Color interpolated_color = Color::Black;
|
||||||
|
|
||||||
|
if (from_color.has_value() && to_color.has_value())
|
||||||
|
interpolated_color = interpolate_color(from_color.value(), to_color.value(), delta, color_syntax);
|
||||||
|
|
||||||
|
return CSSColorValue::create_from_color(interpolated_color, ColorSyntax::Modern);
|
||||||
}
|
}
|
||||||
case CSSStyleValue::Type::Edge: {
|
case CSSStyleValue::Type::Edge: {
|
||||||
auto resolved_from = from.as_edge().resolved_value(calculation_context);
|
auto resolved_from = from.as_edge().resolved_value(calculation_context);
|
||||||
|
|
|
@ -52,16 +52,26 @@ Optional<double> CSSColorValue::resolve_hue(CSSStyleValue const& style_value, Ca
|
||||||
return normalized(style_value.as_angle().angle().to_degrees());
|
return normalized(style_value.as_angle().angle().to_degrees());
|
||||||
|
|
||||||
if (style_value.is_calculated()) {
|
if (style_value.is_calculated()) {
|
||||||
if (style_value.as_calculated().resolves_to_number())
|
if (style_value.as_calculated().resolves_to_number()) {
|
||||||
return normalized(style_value.as_calculated().resolve_number_deprecated(resolution_context).value());
|
auto maybe_number = style_value.as_calculated().resolve_number(resolution_context);
|
||||||
|
|
||||||
if (style_value.as_calculated().resolves_to_angle())
|
if (!maybe_number.has_value())
|
||||||
return normalized(style_value.as_calculated().resolve_angle_deprecated(resolution_context).value().to_degrees());
|
return {};
|
||||||
|
|
||||||
|
return normalized(maybe_number.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (style_value.as_calculated().resolves_to_angle()) {
|
||||||
|
auto maybe_angle = style_value.as_calculated().resolve_angle(resolution_context);
|
||||||
|
|
||||||
|
if (!maybe_angle.has_value())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return normalized(maybe_angle.value().to_degrees());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (style_value.is_keyword() && style_value.to_keyword() == Keyword::None)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return {};
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<double> CSSColorValue::resolve_with_reference_value(CSSStyleValue const& style_value, float one_hundred_percent_value, CalculationResolutionContext const& resolution_context)
|
Optional<double> CSSColorValue::resolve_with_reference_value(CSSStyleValue const& style_value, float one_hundred_percent_value, CalculationResolutionContext const& resolution_context)
|
||||||
|
@ -79,16 +89,26 @@ Optional<double> CSSColorValue::resolve_with_reference_value(CSSStyleValue const
|
||||||
|
|
||||||
if (style_value.is_calculated()) {
|
if (style_value.is_calculated()) {
|
||||||
auto const& calculated = style_value.as_calculated();
|
auto const& calculated = style_value.as_calculated();
|
||||||
if (calculated.resolves_to_number())
|
if (calculated.resolves_to_number()) {
|
||||||
return calculated.resolve_number_deprecated(resolution_context).value();
|
auto maybe_number = calculated.resolve_number(resolution_context);
|
||||||
if (calculated.resolves_to_percentage())
|
|
||||||
return normalize_percentage(calculated.resolve_percentage_deprecated(resolution_context).value());
|
if (!maybe_number.has_value())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return maybe_number.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (calculated.resolves_to_percentage()) {
|
||||||
|
auto percentage = calculated.resolve_percentage(resolution_context);
|
||||||
|
|
||||||
|
if (!percentage.has_value())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return normalize_percentage(percentage.value());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (style_value.is_keyword() && style_value.to_keyword() == Keyword::None)
|
return 0;
|
||||||
return 0;
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<double> CSSColorValue::resolve_alpha(CSSStyleValue const& style_value, CalculationResolutionContext const& resolution_context)
|
Optional<double> CSSColorValue::resolve_alpha(CSSStyleValue const& style_value, CalculationResolutionContext const& resolution_context)
|
||||||
|
@ -108,16 +128,29 @@ Optional<double> CSSColorValue::resolve_alpha(CSSStyleValue const& style_value,
|
||||||
|
|
||||||
if (style_value.is_calculated()) {
|
if (style_value.is_calculated()) {
|
||||||
auto const& calculated = style_value.as_calculated();
|
auto const& calculated = style_value.as_calculated();
|
||||||
if (calculated.resolves_to_number())
|
if (calculated.resolves_to_number()) {
|
||||||
return normalized(calculated.resolve_number_deprecated(resolution_context).value());
|
auto maybe_number = calculated.resolve_number(resolution_context);
|
||||||
if (calculated.resolves_to_percentage())
|
|
||||||
return normalized(calculated.resolve_percentage_deprecated(resolution_context).value().as_fraction());
|
if (!maybe_number.has_value())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return normalized(maybe_number.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (calculated.resolves_to_percentage()) {
|
||||||
|
auto percentage = calculated.resolve_percentage(resolution_context);
|
||||||
|
|
||||||
|
if (!percentage.has_value())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return normalized(percentage.value().as_fraction());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (style_value.is_keyword() && style_value.to_keyword() == Keyword::None)
|
if (style_value.is_keyword() && style_value.to_keyword() == Keyword::None)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return {};
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSSColorValue::serialize_color_component(StringBuilder& builder, SerializationMode mode, CSSStyleValue const& component, float one_hundred_percent_value, Optional<double> clamp_min, Optional<double> clamp_max) const
|
void CSSColorValue::serialize_color_component(StringBuilder& builder, SerializationMode mode, CSSStyleValue const& component, float one_hundred_percent_value, Optional<double> clamp_min, Optional<double> clamp_max) const
|
||||||
|
@ -130,7 +163,16 @@ void CSSColorValue::serialize_color_component(StringBuilder& builder, Serializat
|
||||||
builder.append(component.to_string(mode));
|
builder.append(component.to_string(mode));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto resolved_value = resolve_with_reference_value(component, one_hundred_percent_value, {}).value_or(0);
|
|
||||||
|
auto maybe_resolved_value = resolve_with_reference_value(component, one_hundred_percent_value, {});
|
||||||
|
|
||||||
|
if (!maybe_resolved_value.has_value()) {
|
||||||
|
builder.append(component.to_string(mode));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto resolved_value = maybe_resolved_value.value();
|
||||||
|
|
||||||
if (clamp_min.has_value() && resolved_value < *clamp_min)
|
if (clamp_min.has_value() && resolved_value < *clamp_min)
|
||||||
resolved_value = *clamp_min;
|
resolved_value = *clamp_min;
|
||||||
if (clamp_max.has_value() && resolved_value > *clamp_max)
|
if (clamp_max.has_value() && resolved_value > *clamp_max)
|
||||||
|
@ -153,8 +195,15 @@ void CSSColorValue::serialize_alpha_component(StringBuilder& builder, Serializat
|
||||||
builder.append(component.to_string(mode));
|
builder.append(component.to_string(mode));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto resolved_value = resolve_alpha(component, {}).value_or(0);
|
|
||||||
builder.appendff("{}", resolved_value);
|
auto maybe_resolved_value = resolve_alpha(component, {});
|
||||||
|
|
||||||
|
if (!maybe_resolved_value.has_value()) {
|
||||||
|
builder.append(component.to_string(mode));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.appendff("{}", maybe_resolved_value.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSSColorValue::serialize_hue_component(StringBuilder& builder, SerializationMode mode, CSSStyleValue const& component) const
|
void CSSColorValue::serialize_hue_component(StringBuilder& builder, SerializationMode mode, CSSStyleValue const& component) const
|
||||||
|
@ -167,7 +216,15 @@ void CSSColorValue::serialize_hue_component(StringBuilder& builder, Serializatio
|
||||||
builder.append(component.to_string(mode));
|
builder.append(component.to_string(mode));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
builder.appendff("{:.4}", resolve_hue(component, {}).value_or(0));
|
|
||||||
|
auto maybe_resolved_value = resolve_hue(component, {});
|
||||||
|
|
||||||
|
if (!maybe_resolved_value.has_value()) {
|
||||||
|
builder.append(component.to_string(mode));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.appendff("{:.4}", maybe_resolved_value.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,14 +11,17 @@
|
||||||
|
|
||||||
namespace Web::CSS {
|
namespace Web::CSS {
|
||||||
|
|
||||||
Color CSSHSL::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
|
Optional<Color> CSSHSL::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
|
||||||
{
|
{
|
||||||
auto const h_val = resolve_hue(m_properties.h, resolution_context).value_or(0);
|
auto h_val = resolve_hue(m_properties.h, resolution_context);
|
||||||
auto const s_val = resolve_with_reference_value(m_properties.s, 100.0, resolution_context).value_or(0);
|
auto s_val = resolve_with_reference_value(m_properties.s, 100.0, resolution_context);
|
||||||
auto const l_val = resolve_with_reference_value(m_properties.l, 100.0, resolution_context).value_or(0);
|
auto l_val = resolve_with_reference_value(m_properties.l, 100.0, resolution_context);
|
||||||
auto const alpha_val = resolve_alpha(m_properties.alpha, resolution_context).value_or(1);
|
auto alpha_val = resolve_alpha(m_properties.alpha, resolution_context);
|
||||||
|
|
||||||
return Color::from_hsla(h_val, s_val / 100.0f, l_val / 100.0f, alpha_val);
|
if (!h_val.has_value() || !s_val.has_value() || !l_val.has_value() || !alpha_val.has_value())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return Color::from_hsla(h_val.value(), s_val.value() / 100.0f, l_val.value() / 100.0f, alpha_val.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CSSHSL::equals(CSSStyleValue const& other) const
|
bool CSSHSL::equals(CSSStyleValue const& other) const
|
||||||
|
@ -35,8 +38,11 @@ bool CSSHSL::equals(CSSStyleValue const& other) const
|
||||||
// https://www.w3.org/TR/css-color-4/#serializing-sRGB-values
|
// https://www.w3.org/TR/css-color-4/#serializing-sRGB-values
|
||||||
String CSSHSL::to_string(SerializationMode) const
|
String CSSHSL::to_string(SerializationMode) const
|
||||||
{
|
{
|
||||||
|
if (auto color = to_color({}, {}); color.has_value())
|
||||||
|
return serialize_a_srgb_value(color.value());
|
||||||
|
|
||||||
// FIXME: Do this properly, taking unresolved calculated values into account.
|
// FIXME: Do this properly, taking unresolved calculated values into account.
|
||||||
return serialize_a_srgb_value(to_color({}, {}));
|
return ""_string;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ public:
|
||||||
CSSStyleValue const& l() const { return *m_properties.l; }
|
CSSStyleValue const& l() const { return *m_properties.l; }
|
||||||
CSSStyleValue const& alpha() const { return *m_properties.alpha; }
|
CSSStyleValue const& alpha() const { return *m_properties.alpha; }
|
||||||
|
|
||||||
virtual Color to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
virtual Optional<Color> to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
||||||
|
|
||||||
virtual String to_string(SerializationMode) const override;
|
virtual String to_string(SerializationMode) const override;
|
||||||
|
|
||||||
|
|
|
@ -11,24 +11,30 @@
|
||||||
|
|
||||||
namespace Web::CSS {
|
namespace Web::CSS {
|
||||||
|
|
||||||
Color CSSHWB::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
|
Optional<Color> CSSHWB::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
|
||||||
{
|
{
|
||||||
auto const h_val = resolve_hue(m_properties.h, resolution_context).value_or(0);
|
auto h_val = resolve_hue(m_properties.h, resolution_context);
|
||||||
auto const w_val = clamp(resolve_with_reference_value(m_properties.w, 100.0, resolution_context).value_or(0), 0, 100) / 100.0f;
|
auto raw_w_value = resolve_with_reference_value(m_properties.w, 100.0, resolution_context);
|
||||||
auto const b_val = clamp(resolve_with_reference_value(m_properties.b, 100.0, resolution_context).value_or(0), 0, 100) / 100.0f;
|
auto raw_b_value = resolve_with_reference_value(m_properties.b, 100.0, resolution_context);
|
||||||
auto const alpha_val = resolve_alpha(m_properties.alpha, resolution_context).value_or(1);
|
auto alpha_val = resolve_alpha(m_properties.alpha, resolution_context);
|
||||||
|
|
||||||
|
if (!h_val.has_value() || !raw_w_value.has_value() || !raw_b_value.has_value() || !alpha_val.has_value())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
auto w_val = clamp(raw_w_value.value(), 0, 100) / 100.0f;
|
||||||
|
auto b_val = clamp(raw_b_value.value(), 0, 100) / 100.0f;
|
||||||
|
|
||||||
if (w_val + b_val >= 1.0f) {
|
if (w_val + b_val >= 1.0f) {
|
||||||
auto to_byte = [](float value) {
|
auto to_byte = [](float value) {
|
||||||
return round_to<u8>(clamp(value * 255.0f, 0.0f, 255.0f));
|
return round_to<u8>(clamp(value * 255.0f, 0.0f, 255.0f));
|
||||||
};
|
};
|
||||||
u8 gray = to_byte(w_val / (w_val + b_val));
|
u8 gray = to_byte(w_val / (w_val + b_val));
|
||||||
return Color(gray, gray, gray, to_byte(alpha_val));
|
return Color(gray, gray, gray, to_byte(alpha_val.value()));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto value = 1 - b_val;
|
auto value = 1 - b_val;
|
||||||
auto saturation = 1 - (w_val / value);
|
auto saturation = 1 - (w_val / value);
|
||||||
return Color::from_hsv(h_val, saturation, value).with_opacity(alpha_val);
|
return Color::from_hsv(h_val.value(), saturation, value).with_opacity(alpha_val.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CSSHWB::equals(CSSStyleValue const& other) const
|
bool CSSHWB::equals(CSSStyleValue const& other) const
|
||||||
|
@ -45,8 +51,11 @@ bool CSSHWB::equals(CSSStyleValue const& other) const
|
||||||
// https://www.w3.org/TR/css-color-4/#serializing-sRGB-values
|
// https://www.w3.org/TR/css-color-4/#serializing-sRGB-values
|
||||||
String CSSHWB::to_string(SerializationMode) const
|
String CSSHWB::to_string(SerializationMode) const
|
||||||
{
|
{
|
||||||
|
if (auto color = to_color({}, {}); color.has_value())
|
||||||
|
return serialize_a_srgb_value(color.value());
|
||||||
|
|
||||||
// FIXME: Do this properly, taking unresolved calculated values into account.
|
// FIXME: Do this properly, taking unresolved calculated values into account.
|
||||||
return serialize_a_srgb_value(to_color({}, {}));
|
return ""_string;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ public:
|
||||||
CSSStyleValue const& b() const { return *m_properties.b; }
|
CSSStyleValue const& b() const { return *m_properties.b; }
|
||||||
CSSStyleValue const& alpha() const { return *m_properties.alpha; }
|
CSSStyleValue const& alpha() const { return *m_properties.alpha; }
|
||||||
|
|
||||||
virtual Color to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
virtual Optional<Color> to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
||||||
|
|
||||||
virtual String to_string(SerializationMode) const override;
|
virtual String to_string(SerializationMode) const override;
|
||||||
|
|
||||||
|
|
|
@ -135,7 +135,7 @@ bool CSSKeywordValue::has_color() const
|
||||||
return is_color(keyword());
|
return is_color(keyword());
|
||||||
}
|
}
|
||||||
|
|
||||||
Color CSSKeywordValue::to_color(Optional<Layout::NodeWithStyle const&> node, CalculationResolutionContext const&) const
|
Optional<Color> CSSKeywordValue::to_color(Optional<Layout::NodeWithStyle const&> node, CalculationResolutionContext const&) const
|
||||||
{
|
{
|
||||||
if (keyword() == Keyword::Currentcolor) {
|
if (keyword() == Keyword::Currentcolor) {
|
||||||
if (!node.has_value() || !node->has_style())
|
if (!node.has_value() || !node->has_style())
|
||||||
|
|
|
@ -51,7 +51,7 @@ public:
|
||||||
|
|
||||||
static bool is_color(Keyword);
|
static bool is_color(Keyword);
|
||||||
virtual bool has_color() const override;
|
virtual bool has_color() const override;
|
||||||
virtual Color to_color(Optional<Layout::NodeWithStyle const&> node, CalculationResolutionContext const&) const override;
|
virtual Optional<Color> to_color(Optional<Layout::NodeWithStyle const&> node, CalculationResolutionContext const&) const override;
|
||||||
virtual String to_string(SerializationMode) const override;
|
virtual String to_string(SerializationMode) const override;
|
||||||
|
|
||||||
bool properties_equal(CSSKeywordValue const& other) const { return m_keyword == other.m_keyword; }
|
bool properties_equal(CSSKeywordValue const& other) const { return m_keyword == other.m_keyword; }
|
||||||
|
|
|
@ -27,14 +27,20 @@ bool CSSLCHLike::equals(CSSStyleValue const& other) const
|
||||||
return m_properties == other_oklch_like.m_properties;
|
return m_properties == other_oklch_like.m_properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
Color CSSLCH::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
|
Optional<Color> CSSLCH::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
|
||||||
{
|
{
|
||||||
auto const l_val = clamp(resolve_with_reference_value(m_properties.l, 100, resolution_context).value_or(0), 0, 100);
|
auto raw_l_val = resolve_with_reference_value(m_properties.l, 100, resolution_context);
|
||||||
auto const c_val = resolve_with_reference_value(m_properties.c, 150, resolution_context).value_or(0);
|
auto c_val = resolve_with_reference_value(m_properties.c, 150, resolution_context);
|
||||||
auto const h_val = AK::to_radians(resolve_hue(m_properties.h, resolution_context).value_or(0));
|
auto raw_h_val = resolve_hue(m_properties.h, resolution_context);
|
||||||
auto const alpha_val = resolve_alpha(m_properties.alpha, resolution_context).value_or(1);
|
auto alpha_val = resolve_alpha(m_properties.alpha, resolution_context);
|
||||||
|
|
||||||
return Color::from_lab(l_val, c_val * cos(h_val), c_val * sin(h_val), alpha_val);
|
if (!raw_l_val.has_value() || !c_val.has_value() || !raw_h_val.has_value() || !alpha_val.has_value())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
auto l_val = clamp(raw_l_val.value(), 0, 100);
|
||||||
|
auto h_val = AK::to_radians(raw_h_val.value());
|
||||||
|
|
||||||
|
return Color::from_lab(l_val, c_val.value() * cos(h_val), c_val.value() * sin(h_val), alpha_val.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://www.w3.org/TR/css-color-4/#serializing-lab-lch
|
// https://www.w3.org/TR/css-color-4/#serializing-lab-lch
|
||||||
|
@ -57,14 +63,21 @@ String CSSLCH::to_string(SerializationMode mode) const
|
||||||
return MUST(builder.to_string());
|
return MUST(builder.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
Color CSSOKLCH::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
|
Optional<Color> CSSOKLCH::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
|
||||||
{
|
{
|
||||||
auto const l_val = clamp(resolve_with_reference_value(m_properties.l, 1.0, resolution_context).value_or(0), 0, 1);
|
auto raw_l_val = resolve_with_reference_value(m_properties.l, 1.0, resolution_context);
|
||||||
auto const c_val = max(resolve_with_reference_value(m_properties.c, 0.4, resolution_context).value_or(0), 0);
|
auto raw_c_val = resolve_with_reference_value(m_properties.c, 0.4, resolution_context);
|
||||||
auto const h_val = AK::to_radians(resolve_hue(m_properties.h, resolution_context).value_or(0));
|
auto raw_h_val = resolve_hue(m_properties.h, resolution_context);
|
||||||
auto const alpha_val = resolve_alpha(m_properties.alpha, resolution_context).value_or(1);
|
auto alpha_val = resolve_alpha(m_properties.alpha, resolution_context);
|
||||||
|
|
||||||
return Color::from_oklab(l_val, c_val * cos(h_val), c_val * sin(h_val), alpha_val);
|
if (!raw_l_val.has_value() || !raw_c_val.has_value() || !raw_h_val.has_value() || !alpha_val.has_value())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
auto l_val = clamp(raw_l_val.value(), 0, 1);
|
||||||
|
auto c_val = max(raw_c_val.value(), 0);
|
||||||
|
auto h_val = AK::to_radians(raw_h_val.value());
|
||||||
|
|
||||||
|
return Color::from_oklab(l_val, c_val * cos(h_val), c_val * sin(h_val), alpha_val.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://www.w3.org/TR/css-color-4/#serializing-oklab-oklch
|
// https://www.w3.org/TR/css-color-4/#serializing-oklab-oklch
|
||||||
|
|
|
@ -56,7 +56,7 @@ public:
|
||||||
}
|
}
|
||||||
virtual ~CSSLCH() override = default;
|
virtual ~CSSLCH() override = default;
|
||||||
|
|
||||||
virtual Color to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
virtual Optional<Color> to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
||||||
|
|
||||||
virtual String to_string(SerializationMode) const override;
|
virtual String to_string(SerializationMode) const override;
|
||||||
};
|
};
|
||||||
|
@ -70,7 +70,7 @@ public:
|
||||||
}
|
}
|
||||||
virtual ~CSSOKLCH() override = default;
|
virtual ~CSSOKLCH() override = default;
|
||||||
|
|
||||||
virtual Color to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
virtual Optional<Color> to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
||||||
|
|
||||||
virtual String to_string(SerializationMode) const override;
|
virtual String to_string(SerializationMode) const override;
|
||||||
};
|
};
|
||||||
|
|
|
@ -26,14 +26,17 @@ bool CSSLabLike::equals(CSSStyleValue const& other) const
|
||||||
return m_properties == other_lab_like.m_properties;
|
return m_properties == other_lab_like.m_properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
Color CSSOKLab::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
|
Optional<Color> CSSOKLab::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
|
||||||
{
|
{
|
||||||
auto const l_val = clamp(resolve_with_reference_value(m_properties.l, 1.0, resolution_context).value_or(0), 0, 1);
|
auto const l_val = resolve_with_reference_value(m_properties.l, 1.0, resolution_context);
|
||||||
auto const a_val = resolve_with_reference_value(m_properties.a, 0.4, resolution_context).value_or(0);
|
auto const a_val = resolve_with_reference_value(m_properties.a, 0.4, resolution_context);
|
||||||
auto const b_val = resolve_with_reference_value(m_properties.b, 0.4, resolution_context).value_or(0);
|
auto const b_val = resolve_with_reference_value(m_properties.b, 0.4, resolution_context);
|
||||||
auto const alpha_val = resolve_alpha(m_properties.alpha, resolution_context).value_or(1);
|
auto const alpha_val = resolve_alpha(m_properties.alpha, resolution_context);
|
||||||
|
|
||||||
return Color::from_oklab(l_val, a_val, b_val, alpha_val);
|
if (!l_val.has_value() || !a_val.has_value() || !b_val.has_value() || !alpha_val.has_value())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return Color::from_oklab(clamp(l_val.value(), 0, 1), a_val.value(), b_val.value(), alpha_val.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://www.w3.org/TR/css-color-4/#serializing-oklab-oklch
|
// https://www.w3.org/TR/css-color-4/#serializing-oklab-oklch
|
||||||
|
@ -56,14 +59,17 @@ String CSSOKLab::to_string(SerializationMode mode) const
|
||||||
return MUST(builder.to_string());
|
return MUST(builder.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
Color CSSLab::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
|
Optional<Color> CSSLab::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
|
||||||
{
|
{
|
||||||
auto const l_val = clamp(resolve_with_reference_value(m_properties.l, 100, resolution_context).value_or(0), 0, 100);
|
auto l_val = resolve_with_reference_value(m_properties.l, 100, resolution_context);
|
||||||
auto const a_val = resolve_with_reference_value(m_properties.a, 125, resolution_context).value_or(0);
|
auto a_val = resolve_with_reference_value(m_properties.a, 125, resolution_context);
|
||||||
auto const b_val = resolve_with_reference_value(m_properties.b, 125, resolution_context).value_or(0);
|
auto b_val = resolve_with_reference_value(m_properties.b, 125, resolution_context);
|
||||||
auto const alpha_val = resolve_alpha(m_properties.alpha, resolution_context).value_or(1);
|
auto alpha_val = resolve_alpha(m_properties.alpha, resolution_context);
|
||||||
|
|
||||||
return Color::from_lab(l_val, a_val, b_val, alpha_val);
|
if (!l_val.has_value() || !a_val.has_value() || !b_val.has_value() || !alpha_val.has_value())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return Color::from_lab(clamp(l_val.value(), 0, 100), a_val.value(), b_val.value(), alpha_val.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://www.w3.org/TR/css-color-4/#serializing-lab-lch
|
// https://www.w3.org/TR/css-color-4/#serializing-lab-lch
|
||||||
|
|
|
@ -51,7 +51,7 @@ protected:
|
||||||
// https://drafts.css-houdini.org/css-typed-om-1/#cssoklab
|
// https://drafts.css-houdini.org/css-typed-om-1/#cssoklab
|
||||||
class CSSOKLab final : public CSSLabLike {
|
class CSSOKLab final : public CSSLabLike {
|
||||||
public:
|
public:
|
||||||
virtual Color to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
virtual Optional<Color> to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
||||||
virtual String to_string(SerializationMode) const override;
|
virtual String to_string(SerializationMode) const override;
|
||||||
|
|
||||||
CSSOKLab(Badge<CSSLabLike>, ValueComparingNonnullRefPtr<CSSStyleValue const> l, ValueComparingNonnullRefPtr<CSSStyleValue const> a, ValueComparingNonnullRefPtr<CSSStyleValue const> b, ValueComparingNonnullRefPtr<CSSStyleValue const> alpha)
|
CSSOKLab(Badge<CSSLabLike>, ValueComparingNonnullRefPtr<CSSStyleValue const> l, ValueComparingNonnullRefPtr<CSSStyleValue const> a, ValueComparingNonnullRefPtr<CSSStyleValue const> b, ValueComparingNonnullRefPtr<CSSStyleValue const> alpha)
|
||||||
|
@ -63,7 +63,7 @@ public:
|
||||||
// https://drafts.css-houdini.org/css-typed-om-1/#csslab
|
// https://drafts.css-houdini.org/css-typed-om-1/#csslab
|
||||||
class CSSLab final : public CSSLabLike {
|
class CSSLab final : public CSSLabLike {
|
||||||
public:
|
public:
|
||||||
virtual Color to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
virtual Optional<Color> to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
||||||
virtual String to_string(SerializationMode) const override;
|
virtual String to_string(SerializationMode) const override;
|
||||||
|
|
||||||
CSSLab(Badge<CSSLabLike>, ValueComparingNonnullRefPtr<CSSStyleValue const> l, ValueComparingNonnullRefPtr<CSSStyleValue const> a, ValueComparingNonnullRefPtr<CSSStyleValue const> b, ValueComparingNonnullRefPtr<CSSStyleValue const> alpha)
|
CSSLab(Badge<CSSLabLike>, ValueComparingNonnullRefPtr<CSSStyleValue const> l, ValueComparingNonnullRefPtr<CSSStyleValue const> a, ValueComparingNonnullRefPtr<CSSStyleValue const> b, ValueComparingNonnullRefPtr<CSSStyleValue const> alpha)
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
namespace Web::CSS {
|
namespace Web::CSS {
|
||||||
|
|
||||||
Color CSSLightDark::to_color(Optional<Layout::NodeWithStyle const&> node, CalculationResolutionContext const& resolution_context) const
|
Optional<Color> CSSLightDark::to_color(Optional<Layout::NodeWithStyle const&> node, CalculationResolutionContext const& resolution_context) const
|
||||||
{
|
{
|
||||||
if (node.has_value() && node.value().computed_values().color_scheme() == PreferredColorScheme::Dark)
|
if (node.has_value() && node.value().computed_values().color_scheme() == PreferredColorScheme::Dark)
|
||||||
return m_properties.dark->to_color(node, resolution_context);
|
return m_properties.dark->to_color(node, resolution_context);
|
||||||
|
|
|
@ -21,7 +21,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool equals(CSSStyleValue const&) const override;
|
virtual bool equals(CSSStyleValue const&) const override;
|
||||||
virtual Color to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
virtual Optional<Color> to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
||||||
virtual String to_string(SerializationMode) const override;
|
virtual String to_string(SerializationMode) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
namespace Web::CSS {
|
namespace Web::CSS {
|
||||||
|
|
||||||
Color CSSRGB::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
|
Optional<Color> CSSRGB::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
|
||||||
{
|
{
|
||||||
auto resolve_rgb_to_u8 = [&resolution_context](CSSStyleValue const& style_value) -> Optional<u8> {
|
auto resolve_rgb_to_u8 = [&resolution_context](CSSStyleValue const& style_value) -> Optional<u8> {
|
||||||
// <number> | <percentage> | none
|
// <number> | <percentage> | none
|
||||||
|
@ -32,16 +32,26 @@ Color CSSRGB::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolu
|
||||||
|
|
||||||
if (style_value.is_calculated()) {
|
if (style_value.is_calculated()) {
|
||||||
auto const& calculated = style_value.as_calculated();
|
auto const& calculated = style_value.as_calculated();
|
||||||
if (calculated.resolves_to_number())
|
if (calculated.resolves_to_number()) {
|
||||||
return normalized(calculated.resolve_number_deprecated(resolution_context).value());
|
auto maybe_number = calculated.resolve_number(resolution_context);
|
||||||
if (calculated.resolves_to_percentage())
|
|
||||||
return normalized(calculated.resolve_percentage_deprecated(resolution_context).value().value() * 255 / 100);
|
if (!maybe_number.has_value())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return normalized(maybe_number.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (calculated.resolves_to_percentage()) {
|
||||||
|
auto maybe_percentage = calculated.resolve_percentage(resolution_context);
|
||||||
|
|
||||||
|
if (!maybe_percentage.has_value())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return normalized(maybe_percentage.value().value() * 255 / 100);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (style_value.is_keyword() && style_value.to_keyword() == Keyword::None)
|
return 0;
|
||||||
return 0;
|
|
||||||
|
|
||||||
return {};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
auto resolve_alpha_to_u8 = [&resolution_context](CSSStyleValue const& style_value) -> Optional<u8> {
|
auto resolve_alpha_to_u8 = [&resolution_context](CSSStyleValue const& style_value) -> Optional<u8> {
|
||||||
|
@ -51,12 +61,15 @@ Color CSSRGB::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolu
|
||||||
return {};
|
return {};
|
||||||
};
|
};
|
||||||
|
|
||||||
u8 const r_val = resolve_rgb_to_u8(m_properties.r).value_or(0);
|
auto r_val = resolve_rgb_to_u8(m_properties.r);
|
||||||
u8 const g_val = resolve_rgb_to_u8(m_properties.g).value_or(0);
|
auto g_val = resolve_rgb_to_u8(m_properties.g);
|
||||||
u8 const b_val = resolve_rgb_to_u8(m_properties.b).value_or(0);
|
auto b_val = resolve_rgb_to_u8(m_properties.b);
|
||||||
u8 const alpha_val = resolve_alpha_to_u8(m_properties.alpha).value_or(255);
|
auto alpha_val = resolve_alpha_to_u8(m_properties.alpha);
|
||||||
|
|
||||||
return Color(r_val, g_val, b_val, alpha_val);
|
if (!r_val.has_value() || !g_val.has_value() || !b_val.has_value() || !alpha_val.has_value())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return Color(r_val.value(), g_val.value(), b_val.value(), alpha_val.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CSSRGB::equals(CSSStyleValue const& other) const
|
bool CSSRGB::equals(CSSStyleValue const& other) const
|
||||||
|
@ -73,10 +86,14 @@ bool CSSRGB::equals(CSSStyleValue const& other) const
|
||||||
// https://www.w3.org/TR/css-color-4/#serializing-sRGB-values
|
// https://www.w3.org/TR/css-color-4/#serializing-sRGB-values
|
||||||
String CSSRGB::to_string(SerializationMode mode) const
|
String CSSRGB::to_string(SerializationMode mode) const
|
||||||
{
|
{
|
||||||
// FIXME: Do this properly, taking unresolved calculated values into account.
|
|
||||||
if (mode != SerializationMode::ResolvedValue && m_properties.name.has_value())
|
if (mode != SerializationMode::ResolvedValue && m_properties.name.has_value())
|
||||||
return m_properties.name.value().to_string().to_ascii_lowercase();
|
return m_properties.name.value().to_string().to_ascii_lowercase();
|
||||||
return serialize_a_srgb_value(to_color({}, {}));
|
|
||||||
|
if (auto color = to_color({}, {}); color.has_value())
|
||||||
|
return serialize_a_srgb_value(color.value());
|
||||||
|
|
||||||
|
// FIXME: Do this properly, taking unresolved calculated values into account.
|
||||||
|
return ""_string;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ public:
|
||||||
CSSStyleValue const& b() const { return *m_properties.b; }
|
CSSStyleValue const& b() const { return *m_properties.b; }
|
||||||
CSSStyleValue const& alpha() const { return *m_properties.alpha; }
|
CSSStyleValue const& alpha() const { return *m_properties.alpha; }
|
||||||
|
|
||||||
virtual Color to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
virtual Optional<Color> to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
||||||
|
|
||||||
virtual String to_string(SerializationMode) const override;
|
virtual String to_string(SerializationMode) const override;
|
||||||
|
|
||||||
|
|
|
@ -82,13 +82,22 @@ bool ColorFunctionStyleValue::equals(CSSStyleValue const& other) const
|
||||||
return m_properties == other_lab_like.m_properties;
|
return m_properties == other_lab_like.m_properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
ColorFunctionStyleValue::Resolved ColorFunctionStyleValue::resolve_properties(CalculationResolutionContext const& resolution_context) const
|
Optional<ColorFunctionStyleValue::Resolved> ColorFunctionStyleValue::resolve_properties(CalculationResolutionContext const& resolution_context) const
|
||||||
{
|
{
|
||||||
float const c1 = resolve_with_reference_value(m_properties.channels[0], 1, resolution_context).value_or(0);
|
auto c1 = resolve_with_reference_value(m_properties.channels[0], 1, resolution_context);
|
||||||
float const c2 = resolve_with_reference_value(m_properties.channels[1], 1, resolution_context).value_or(0);
|
auto c2 = resolve_with_reference_value(m_properties.channels[1], 1, resolution_context);
|
||||||
float const c3 = resolve_with_reference_value(m_properties.channels[2], 1, resolution_context).value_or(0);
|
auto c3 = resolve_with_reference_value(m_properties.channels[2], 1, resolution_context);
|
||||||
float const alpha_val = resolve_alpha(m_properties.alpha, resolution_context).value_or(1);
|
auto alpha = resolve_alpha(m_properties.alpha, resolution_context);
|
||||||
return { .channels = { c1, c2, c3 }, .alpha = alpha_val };
|
|
||||||
|
if (!c1.has_value() || !c2.has_value() || !c3.has_value() || !alpha.has_value())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
float const c1_value = c1.value();
|
||||||
|
float const c2_value = c2.value();
|
||||||
|
float const c3_value = c3.value();
|
||||||
|
float const alpha_value = alpha.value();
|
||||||
|
|
||||||
|
return ColorFunctionStyleValue::Resolved { .channels = { c1_value, c2_value, c3_value }, .alpha = alpha_value };
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://www.w3.org/TR/css-color-4/#serializing-color-function-values
|
// https://www.w3.org/TR/css-color-4/#serializing-color-function-values
|
||||||
|
@ -102,14 +111,14 @@ String ColorFunctionStyleValue::to_string(SerializationMode mode) const
|
||||||
CalculationResolutionContext context {};
|
CalculationResolutionContext context {};
|
||||||
auto const& calculated = value->as_calculated();
|
auto const& calculated = value->as_calculated();
|
||||||
if (calculated.resolves_to_percentage()) {
|
if (calculated.resolves_to_percentage()) {
|
||||||
if (auto resolved_percentage = calculated.resolve_percentage_deprecated(context); resolved_percentage.has_value()) {
|
if (auto resolved_percentage = calculated.resolve_percentage(context); resolved_percentage.has_value()) {
|
||||||
auto resolved_number = resolved_percentage->value() / 100;
|
auto resolved_number = resolved_percentage->value() / 100;
|
||||||
if (!isfinite(resolved_number))
|
if (!isfinite(resolved_number))
|
||||||
resolved_number = 0;
|
resolved_number = 0;
|
||||||
return NumberStyleValue::create(resolved_number);
|
return NumberStyleValue::create(resolved_number);
|
||||||
}
|
}
|
||||||
} else if (calculated.resolves_to_number()) {
|
} else if (calculated.resolves_to_number()) {
|
||||||
if (auto resolved_number = calculated.resolve_number_deprecated(context); resolved_number.has_value())
|
if (auto resolved_number = calculated.resolve_number(context); resolved_number.has_value())
|
||||||
return NumberStyleValue::create(*resolved_number);
|
return NumberStyleValue::create(*resolved_number);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,9 +153,14 @@ String ColorFunctionStyleValue::to_string(SerializationMode mode) const
|
||||||
convert_percentage(m_properties.channels[2])->to_string(mode)));
|
convert_percentage(m_properties.channels[2])->to_string(mode)));
|
||||||
}
|
}
|
||||||
|
|
||||||
Color ColorFunctionStyleValue::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
|
Optional<Color> ColorFunctionStyleValue::to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const
|
||||||
{
|
{
|
||||||
auto [channels, alpha_val] = resolve_properties(resolution_context);
|
auto properties = resolve_properties(resolution_context);
|
||||||
|
|
||||||
|
if (!properties.has_value())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
auto [channels, alpha_val] = properties.value();
|
||||||
auto c1 = channels[0];
|
auto c1 = channels[0];
|
||||||
auto c2 = channels[1];
|
auto c2 = channels[1];
|
||||||
auto c3 = channels[2];
|
auto c3 = channels[2];
|
||||||
|
|
|
@ -18,7 +18,7 @@ public:
|
||||||
static ValueComparingNonnullRefPtr<ColorFunctionStyleValue const> create(StringView color_space, ValueComparingNonnullRefPtr<CSSStyleValue const> c1, ValueComparingNonnullRefPtr<CSSStyleValue const> c2, ValueComparingNonnullRefPtr<CSSStyleValue const> c3, ValueComparingRefPtr<CSSStyleValue const> alpha = {});
|
static ValueComparingNonnullRefPtr<ColorFunctionStyleValue const> create(StringView color_space, ValueComparingNonnullRefPtr<CSSStyleValue const> c1, ValueComparingNonnullRefPtr<CSSStyleValue const> c2, ValueComparingNonnullRefPtr<CSSStyleValue const> c3, ValueComparingRefPtr<CSSStyleValue const> alpha = {});
|
||||||
|
|
||||||
virtual bool equals(CSSStyleValue const&) const override;
|
virtual bool equals(CSSStyleValue const&) const override;
|
||||||
virtual Color to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const override;
|
virtual Optional<Color> to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const& resolution_context) const override;
|
||||||
virtual String to_string(SerializationMode) const override;
|
virtual String to_string(SerializationMode) const override;
|
||||||
|
|
||||||
virtual bool is_color_function() const override { return true; }
|
virtual bool is_color_function() const override { return true; }
|
||||||
|
@ -43,7 +43,7 @@ private:
|
||||||
float alpha {};
|
float alpha {};
|
||||||
};
|
};
|
||||||
|
|
||||||
Resolved resolve_properties(CalculationResolutionContext const& resolution_context) const;
|
Optional<Resolved> resolve_properties(CalculationResolutionContext const& resolution_context) const;
|
||||||
|
|
||||||
Properties m_properties;
|
Properties m_properties;
|
||||||
};
|
};
|
||||||
|
|
|
@ -175,15 +175,19 @@ ColorMixStyleValue::PercentageNormalizationResult ColorMixStyleValue::normalize_
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://drafts.csswg.org/css-color-5/#color-mix-result
|
// https://drafts.csswg.org/css-color-5/#color-mix-result
|
||||||
Color ColorMixStyleValue::to_color(Optional<Layout::NodeWithStyle const&> node, CalculationResolutionContext const& resolution_context) const
|
Optional<Color> ColorMixStyleValue::to_color(Optional<Layout::NodeWithStyle const&> node, CalculationResolutionContext const& resolution_context) const
|
||||||
{
|
{
|
||||||
// FIXME: Take the color space and hue interpolation method into account.
|
// FIXME: Take the color space and hue interpolation method into account.
|
||||||
// The current implementation only uses oklab interpolation.
|
// The current implementation only uses oklab interpolation.
|
||||||
auto normalized_percentages = normalize_percentages();
|
auto normalized_percentages = normalize_percentages();
|
||||||
auto from_color = m_properties.first_component.color;
|
auto from_color = m_properties.first_component.color->to_color(node, resolution_context);
|
||||||
auto to_color = m_properties.second_component.color;
|
auto to_color = m_properties.second_component.color->to_color(node, resolution_context);
|
||||||
auto delta = normalized_percentages.p2.value() / 100;
|
auto delta = normalized_percentages.p2.value() / 100;
|
||||||
return interpolate_color(from_color->to_color(node, resolution_context), to_color->to_color(node, resolution_context), delta, ColorSyntax::Modern);
|
|
||||||
|
if (!from_color.has_value() || !to_color.has_value())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return interpolate_color(from_color.value(), to_color.value(), delta, ColorSyntax::Modern);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ public:
|
||||||
static ValueComparingNonnullRefPtr<ColorMixStyleValue const> create(ColorInterpolationMethod, ColorMixComponent first_component, ColorMixComponent second_component);
|
static ValueComparingNonnullRefPtr<ColorMixStyleValue const> create(ColorInterpolationMethod, ColorMixComponent first_component, ColorMixComponent second_component);
|
||||||
|
|
||||||
virtual bool equals(CSSStyleValue const&) const override;
|
virtual bool equals(CSSStyleValue const&) const override;
|
||||||
virtual Color to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
virtual Optional<Color> to_color(Optional<Layout::NodeWithStyle const&>, CalculationResolutionContext const&) const override;
|
||||||
virtual String to_string(SerializationMode) const override;
|
virtual String to_string(SerializationMode) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -1677,7 +1677,7 @@ void Document::obtain_theme_color()
|
||||||
resolution_context.length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(*html_element()->layout_node());
|
resolution_context.length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(*html_element()->layout_node());
|
||||||
}
|
}
|
||||||
|
|
||||||
theme_color = css_value->to_color(root_node, resolution_context);
|
theme_color = css_value->to_color(root_node, resolution_context).value();
|
||||||
return TraversalDecision::Break;
|
return TraversalDecision::Break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1175,7 +1175,7 @@ Optional<String> effective_command_value(GC::Ptr<DOM::Node> node, FlyString cons
|
||||||
return NumericLimits<u8>::max();
|
return NumericLimits<u8>::max();
|
||||||
VERIFY(is<Layout::NodeWithStyle>(node->layout_node()));
|
VERIFY(is<Layout::NodeWithStyle>(node->layout_node()));
|
||||||
auto& layout_node = *static_cast<Layout::NodeWithStyle*>(node->layout_node());
|
auto& layout_node = *static_cast<Layout::NodeWithStyle*>(node->layout_node());
|
||||||
return background_color.value()->to_color(layout_node, { .length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(layout_node) }).alpha();
|
return background_color.value()->to_color(layout_node, { .length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(layout_node) }).value().alpha();
|
||||||
};
|
};
|
||||||
while (resolved_background_alpha() == 0 && node->parent() && is<DOM::Element>(*node->parent()))
|
while (resolved_background_alpha() == 0 && node->parent() && is<DOM::Element>(*node->parent()))
|
||||||
node = node->parent();
|
node = node->parent();
|
||||||
|
|
|
@ -52,7 +52,7 @@ public:
|
||||||
resolution_context.length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(*context->layout_node());
|
resolution_context.length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(*context->layout_node());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto parsedValue = style_value->to_color(layout_node, resolution_context);
|
auto parsedValue = style_value->to_color(layout_node, resolution_context).value_or(Color::Black);
|
||||||
|
|
||||||
// 4. Set this's fill style to parsedValue.
|
// 4. Set this's fill style to parsedValue.
|
||||||
my_drawing_state().fill_style = parsedValue;
|
my_drawing_state().fill_style = parsedValue;
|
||||||
|
@ -105,7 +105,7 @@ public:
|
||||||
resolution_context.length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(*context->layout_node());
|
resolution_context.length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(*context->layout_node());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto parsedValue = style_value->to_color(layout_node, resolution_context);
|
auto parsedValue = style_value->to_color(layout_node, resolution_context).value_or(Color::Black);
|
||||||
|
|
||||||
// 4. Set this's stroke style to parsedValue.
|
// 4. Set this's stroke style to parsedValue.
|
||||||
my_drawing_state().stroke_style = parsedValue;
|
my_drawing_state().stroke_style = parsedValue;
|
||||||
|
|
|
@ -950,7 +950,7 @@ void CanvasRenderingContext2D::set_shadow_color(String color)
|
||||||
resolution_context.length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(*node);
|
resolution_context.length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(*node);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto parsedValue = style_value->to_color(layout_node, resolution_context);
|
auto parsedValue = style_value->to_color(layout_node, resolution_context).value_or(Color::Black);
|
||||||
|
|
||||||
// 4. Set this's shadow color to parsedValue.
|
// 4. Set this's shadow color to parsedValue.
|
||||||
drawing_state().shadow_color = parsedValue;
|
drawing_state().shadow_color = parsedValue;
|
||||||
|
|
|
@ -288,7 +288,7 @@ void OffscreenCanvasRenderingContext2D::set_shadow_color(String color)
|
||||||
// 2. Let parsedValue be the result of parsing the given value with context if non-null.
|
// 2. Let parsedValue be the result of parsing the given value with context if non-null.
|
||||||
auto style_value = parse_css_value(CSS::Parser::ParsingParams(), color, CSS::PropertyID::Color);
|
auto style_value = parse_css_value(CSS::Parser::ParsingParams(), color, CSS::PropertyID::Color);
|
||||||
if (style_value && style_value->has_color()) {
|
if (style_value && style_value->has_color()) {
|
||||||
auto parsedValue = style_value->to_color({}, {});
|
auto parsedValue = style_value->to_color({}, {}).value_or(Color::Black);
|
||||||
|
|
||||||
// 4. Set this's shadow color to parsedValue.
|
// 4. Set this's shadow color to parsedValue.
|
||||||
drawing_state().shadow_color = parsedValue;
|
drawing_state().shadow_color = parsedValue;
|
||||||
|
|
|
@ -835,7 +835,7 @@ void NodeWithStyle::apply_style(CSS::ComputedProperties const& computed_style)
|
||||||
do_border_style(computed_values.border_bottom(), CSS::PropertyID::BorderBottomWidth, CSS::PropertyID::BorderBottomColor, CSS::PropertyID::BorderBottomStyle);
|
do_border_style(computed_values.border_bottom(), CSS::PropertyID::BorderBottomWidth, CSS::PropertyID::BorderBottomColor, CSS::PropertyID::BorderBottomStyle);
|
||||||
|
|
||||||
if (auto const& outline_color = computed_style.property(CSS::PropertyID::OutlineColor); outline_color.has_color())
|
if (auto const& outline_color = computed_style.property(CSS::PropertyID::OutlineColor); outline_color.has_color())
|
||||||
computed_values.set_outline_color(outline_color.to_color(*this, { .length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(*this) }));
|
computed_values.set_outline_color(outline_color.to_color(*this, { .length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(*this) }).value());
|
||||||
if (auto const& outline_offset = computed_style.property(CSS::PropertyID::OutlineOffset); outline_offset.is_length())
|
if (auto const& outline_offset = computed_style.property(CSS::PropertyID::OutlineOffset); outline_offset.is_length())
|
||||||
computed_values.set_outline_offset(outline_offset.as_length().length());
|
computed_values.set_outline_offset(outline_offset.as_length().length());
|
||||||
computed_values.set_outline_style(computed_style.outline_style());
|
computed_values.set_outline_style(computed_style.outline_style());
|
||||||
|
@ -875,16 +875,16 @@ void NodeWithStyle::apply_style(CSS::ComputedProperties const& computed_style)
|
||||||
|
|
||||||
auto const& fill = computed_style.property(CSS::PropertyID::Fill);
|
auto const& fill = computed_style.property(CSS::PropertyID::Fill);
|
||||||
if (fill.has_color())
|
if (fill.has_color())
|
||||||
computed_values.set_fill(fill.to_color(*this, { .length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(*this) }));
|
computed_values.set_fill(fill.to_color(*this, { .length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(*this) }).value());
|
||||||
else if (fill.is_url())
|
else if (fill.is_url())
|
||||||
computed_values.set_fill(fill.as_url().url());
|
computed_values.set_fill(fill.as_url().url());
|
||||||
auto const& stroke = computed_style.property(CSS::PropertyID::Stroke);
|
auto const& stroke = computed_style.property(CSS::PropertyID::Stroke);
|
||||||
if (stroke.has_color())
|
if (stroke.has_color())
|
||||||
computed_values.set_stroke(stroke.to_color(*this, { .length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(*this) }));
|
computed_values.set_stroke(stroke.to_color(*this, { .length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(*this) }).value());
|
||||||
else if (stroke.is_url())
|
else if (stroke.is_url())
|
||||||
computed_values.set_stroke(stroke.as_url().url());
|
computed_values.set_stroke(stroke.as_url().url());
|
||||||
if (auto const& stop_color = computed_style.property(CSS::PropertyID::StopColor); stop_color.has_color())
|
if (auto const& stop_color = computed_style.property(CSS::PropertyID::StopColor); stop_color.has_color())
|
||||||
computed_values.set_stop_color(stop_color.to_color(*this, { .length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(*this) }));
|
computed_values.set_stop_color(stop_color.to_color(*this, { .length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(*this) }).value());
|
||||||
auto const& stroke_width = computed_style.property(CSS::PropertyID::StrokeWidth);
|
auto const& stroke_width = computed_style.property(CSS::PropertyID::StrokeWidth);
|
||||||
// FIXME: Converting to pixels isn't really correct - values should be in "user units"
|
// FIXME: Converting to pixels isn't really correct - values should be in "user units"
|
||||||
// https://svgwg.org/svg2-draft/coords.html#TermUserUnits
|
// https://svgwg.org/svg2-draft/coords.html#TermUserUnits
|
||||||
|
|
|
@ -587,7 +587,7 @@ Optional<BordersData> borders_data_for_outline(Layout::Node const& layout_node,
|
||||||
// `auto` lets us do whatever we want for the outline. 2px of the accent colour seems reasonable.
|
// `auto` lets us do whatever we want for the outline. 2px of the accent colour seems reasonable.
|
||||||
line_style = CSS::LineStyle::Solid;
|
line_style = CSS::LineStyle::Solid;
|
||||||
// NOTE: CalculationResolutionContext is not required here as Accentcolor keyword value is guaranteed to not rely on it to resolve.
|
// NOTE: CalculationResolutionContext is not required here as Accentcolor keyword value is guaranteed to not rely on it to resolve.
|
||||||
outline_color = CSS::CSSKeywordValue::create(CSS::Keyword::Accentcolor)->to_color(*static_cast<Layout::NodeWithStyle const*>(&layout_node), {});
|
outline_color = CSS::CSSKeywordValue::create(CSS::Keyword::Accentcolor)->to_color(*static_cast<Layout::NodeWithStyle const*>(&layout_node), {}).value();
|
||||||
outline_width = 2;
|
outline_width = 2;
|
||||||
} else {
|
} else {
|
||||||
line_style = CSS::keyword_to_line_style(CSS::to_keyword(outline_style)).value_or(CSS::LineStyle::None);
|
line_style = CSS::keyword_to_line_style(CSS::to_keyword(outline_style)).value_or(CSS::LineStyle::None);
|
||||||
|
|
|
@ -30,7 +30,7 @@ static ColorStopData resolve_color_stop_positions(Layout::NodeWithStyle const& n
|
||||||
|
|
||||||
resolved_color_stops.ensure_capacity(expanded_size);
|
resolved_color_stops.ensure_capacity(expanded_size);
|
||||||
for (auto& stop : color_stop_list) {
|
for (auto& stop : color_stop_list) {
|
||||||
auto resolved_stop = Gfx::ColorStop { .color = stop.color_stop.color->to_color(node, { .length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(node) }) };
|
auto resolved_stop = Gfx::ColorStop { .color = stop.color_stop.color->to_color(node, { .length_resolution_context = CSS::Length::ResolutionContext::for_layout_node(node) }).value() };
|
||||||
for (int i = 0; i < color_stop_length(stop); i++)
|
for (int i = 0; i < color_stop_length(stop); i++)
|
||||||
resolved_color_stops.append(resolved_stop);
|
resolved_color_stops.append(resolved_stop);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue