From 65acfe6c6091c9447a63ed17be895533259111de Mon Sep 17 00:00:00 2001 From: MacDue Date: Sun, 13 Nov 2022 15:30:36 +0000 Subject: [PATCH] LibWeb: Handle degenerate radial gradients --- Userland/Libraries/LibWeb/CSS/StyleValue.cpp | 31 ++++++++++++++++++- .../LibWeb/Painting/GradientPainting.cpp | 10 +++--- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp index e26c089973..5d3d73c75d 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValue.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp @@ -2086,7 +2086,7 @@ Gfx::FloatSize RadialGradientStyleValue::resolve_size(Layout::Node const& node, }; // https://w3c.github.io/csswg-drafts/css-images/#radial-gradient-syntax - return m_size.visit( + auto resolved_size = m_size.visit( [&](Extent extent) { switch (extent) { case Extent::ClosestSide: @@ -2117,6 +2117,35 @@ Gfx::FloatSize RadialGradientStyleValue::resolve_size(Layout::Node const& node, auto radius_b = ellipse_size.radius_b.resolved(node, CSS::Length::make_px(size.height())).to_px(node); return Gfx::FloatSize { radius_a, radius_b }; }); + + // Handle degenerate cases + // https://w3c.github.io/csswg-drafts/css-images/#degenerate-radials + + constexpr auto arbitrary_small_number = 1e-10; + constexpr auto arbitrary_large_number = 1e10; + + // If the ending shape is a circle with zero radius: + if (m_ending_shape == EndingShape::Circle && resolved_size.is_empty()) { + // Render as if the ending shape was a circle whose radius was an arbitrary very small number greater than zero. + // This will make the gradient continue to look like a circle. + return Gfx::FloatSize { arbitrary_small_number, arbitrary_small_number }; + } + // If the ending shape has zero width (regardless of the height): + if (resolved_size.width() <= 0) { + // Render as if the ending shape was an ellipse whose height was an arbitrary very large number + // and whose width was an arbitrary very small number greater than zero. + // This will make the gradient look similar to a horizontal linear gradient that is mirrored across the center of the ellipse. + // It also means that all color-stop positions specified with a percentage resolve to 0px. + return Gfx::FloatSize { arbitrary_small_number, arbitrary_large_number }; + } + // Otherwise, if the ending shape has zero height: + if (resolved_size.height() <= 0) { + // Render as if the ending shape was an ellipse whose width was an arbitrary very large number and whose height + // was an arbitrary very small number greater than zero. This will make the gradient look like a solid-color image equal + // to the color of the last color-stop, or equal to the average color of the gradient if it’s repeating. + return Gfx::FloatSize { arbitrary_large_number, arbitrary_small_number }; + } + return resolved_size; } void RadialGradientStyleValue::resolve_for_size(Layout::Node const& node, Gfx::FloatSize const& paint_size) const diff --git a/Userland/Libraries/LibWeb/Painting/GradientPainting.cpp b/Userland/Libraries/LibWeb/Painting/GradientPainting.cpp index 342f5cc08a..3bac34b8a9 100644 --- a/Userland/Libraries/LibWeb/Painting/GradientPainting.cpp +++ b/Userland/Libraries/LibWeb/Painting/GradientPainting.cpp @@ -220,19 +220,19 @@ public: } } - Gfx::Color get_color(int index) const + Gfx::Color get_color(uint64_t index) const { return m_gradient_line_colors[clamp(index, 0, m_gradient_line_colors.size() - 1)]; } Gfx::Color sample_color(float loc) const { - auto repeat_wrap_if_required = [&](int loc) { + auto repeat_wrap_if_required = [&](uint64_t loc) { if (m_repeating) - return (loc + m_start_offset) % static_cast(m_gradient_line_colors.size()); + return (loc + m_start_offset) % m_gradient_line_colors.size(); return loc; }; - auto int_loc = static_cast(floor(loc)); + auto int_loc = static_cast(floor(loc)); auto blend = loc - int_loc; auto color = get_color(repeat_wrap_if_required(int_loc)); // Blend between the two neighbouring colors (this fixes some nasty aliasing issues at small angles) @@ -314,7 +314,7 @@ void paint_radial_gradient(PaintContext& context, Gfx::IntRect const& gradient_r auto point = (Gfx::FloatPoint { x, y } - center_point); auto gradient_x = point.x() / size.width(); auto gradient_y = point.y() / size.height(); - return AK::sqrt(gradient_x * gradient_x + gradient_y * gradient_y) * size.width(); + return AK::sqrt(gradient_x * gradient_x + gradient_y * gradient_y) * max_visible_gradient; }); }