From 476acae04feb0be109c4a88c43a59c0fa19a0ea7 Mon Sep 17 00:00:00 2001 From: MacDue Date: Sun, 13 Nov 2022 00:33:42 +0000 Subject: [PATCH] LibWeb: Paint `radial-gradient()`s This almost looks too easy now :^), but it's just another way to sample the gradient line. --- Userland/Libraries/LibWeb/CSS/StyleValue.cpp | 16 ++++++++-- Userland/Libraries/LibWeb/CSS/StyleValue.h | 8 +++++ .../LibWeb/Painting/GradientPainting.cpp | 30 ++++++++++++++++++- .../LibWeb/Painting/GradientPainting.h | 6 ++++ 4 files changed, 57 insertions(+), 3 deletions(-) diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp index ff4b975f68..e26c089973 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValue.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleValue.cpp @@ -2119,8 +2119,18 @@ Gfx::FloatSize RadialGradientStyleValue::resolve_size(Layout::Node const& node, }); } -void RadialGradientStyleValue::resolve_for_size(Layout::Node const&, Gfx::FloatSize const&) const +void RadialGradientStyleValue::resolve_for_size(Layout::Node const& node, Gfx::FloatSize const& paint_size) const { + Gfx::FloatRect gradient_box { { 0, 0 }, paint_size }; + auto center = m_position.resolved(node, gradient_box); + auto gradient_size = resolve_size(node, center, gradient_box); + if (m_resolved.has_value() && m_resolved->gradient_size == gradient_size) + return; + m_resolved = ResolvedData { + Painting::resolve_radial_gradient_data(node, gradient_size, *this), + gradient_size, + center, + }; } bool RadialGradientStyleValue::equals(StyleValue const& other) const @@ -2134,8 +2144,10 @@ bool RadialGradientStyleValue::equals(StyleValue const& other) const && m_color_stop_list == other_gradient.m_color_stop_list); } -void RadialGradientStyleValue::paint(PaintContext&, Gfx::IntRect const&, CSS::ImageRendering) const +void RadialGradientStyleValue::paint(PaintContext& context, Gfx::IntRect const& dest_rect, CSS::ImageRendering) const { + VERIFY(m_resolved.has_value()); + Painting::paint_radial_gradient(context, dest_rect, m_resolved->data, m_resolved->center.to_rounded(), m_resolved->gradient_size); } String ConicGradientStyleValue::to_string() const diff --git a/Userland/Libraries/LibWeb/CSS/StyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValue.h index 00b45aaa0c..2696577914 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValue.h +++ b/Userland/Libraries/LibWeb/CSS/StyleValue.h @@ -1271,6 +1271,14 @@ private: Size m_size; PositionValue m_position; Vector m_color_stop_list; + + struct ResolvedData { + Painting::RadialGradientData data; + Gfx::FloatSize gradient_size; + Gfx::FloatPoint center; + }; + + mutable Optional m_resolved; }; class ConicGradientStyleValue final : public AbstractImageStyleValue { diff --git a/Userland/Libraries/LibWeb/Painting/GradientPainting.cpp b/Userland/Libraries/LibWeb/Painting/GradientPainting.cpp index a523b78c36..342f5cc08a 100644 --- a/Userland/Libraries/LibWeb/Painting/GradientPainting.cpp +++ b/Userland/Libraries/LibWeb/Painting/GradientPainting.cpp @@ -155,6 +155,18 @@ ConicGradientData resolve_conic_gradient_data(Layout::Node const& node, CSS::Con return { conic_gradient.angle_degrees(), resolved_color_stops }; } +RadialGradientData resolve_radial_gradient_data(Layout::Node const& node, Gfx::FloatSize const& gradient_size, CSS::RadialGradientStyleValue const& radial_gradient) +{ + // Start center, goes right to ending point, where the gradient line intersects the ending shape + auto gradient_length = CSS::Length::make_px(gradient_size.width()); + auto resolved_color_stops = resolve_color_stop_positions( + radial_gradient.color_stop_list(), [&](auto const& length_percentage) { + return length_percentage.resolved(node, gradient_length).to_px(node) / gradient_size.width(); + }, + false); + return { resolved_color_stops }; +} + static float color_stop_step(ColorStop const& previous_stop, ColorStop const& next_stop, float position) { if (position < previous_stop.position) @@ -229,7 +241,7 @@ public: return color; } - void paint_into_rect(Gfx::Painter& painter, Gfx::IntRect const& rect, auto location_transform) + ALWAYS_INLINE void paint_into_rect(Gfx::Painter& painter, Gfx::IntRect const& rect, auto location_transform) { for (int y = 0; y < rect.height(); y++) { for (int x = 0; x < rect.width(); x++) { @@ -290,4 +302,20 @@ void paint_conic_gradient(PaintContext& context, Gfx::IntRect const& gradient_re }); } +void paint_radial_gradient(PaintContext& context, Gfx::IntRect const& gradient_rect, RadialGradientData const& data, Gfx::IntPoint center, Gfx::FloatSize const& size) +{ + // A conservative guesstimate on how many colors we need to generate: + auto max_dimension = max(gradient_rect.width(), gradient_rect.height()); + int max_visible_gradient = max(max_dimension / 2, min(size.width(), max_dimension)); + GradientLine gradient_line(max_visible_gradient, data.color_stops); + auto center_point = Gfx::FloatPoint { center }.translated(0.5, 0.5); + gradient_line.paint_into_rect(context.painter(), gradient_rect, [&](int x, int y) { + // FIXME: See if there's a more efficient calculation we do there :^) + 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(); + }); +} + } diff --git a/Userland/Libraries/LibWeb/Painting/GradientPainting.h b/Userland/Libraries/LibWeb/Painting/GradientPainting.h index 81ac5d113e..e13a820285 100644 --- a/Userland/Libraries/LibWeb/Painting/GradientPainting.h +++ b/Userland/Libraries/LibWeb/Painting/GradientPainting.h @@ -37,10 +37,16 @@ struct ConicGradientData { ColorStopData color_stops; }; +struct RadialGradientData { + ColorStopData color_stops; +}; + LinearGradientData resolve_linear_gradient_data(Layout::Node const&, Gfx::FloatSize const&, CSS::LinearGradientStyleValue const&); ConicGradientData resolve_conic_gradient_data(Layout::Node const&, CSS::ConicGradientStyleValue const&); +RadialGradientData resolve_radial_gradient_data(Layout::Node const&, Gfx::FloatSize const&, CSS::RadialGradientStyleValue const&); void paint_linear_gradient(PaintContext&, Gfx::IntRect const&, LinearGradientData const&); void paint_conic_gradient(PaintContext&, Gfx::IntRect const&, ConicGradientData const&, Gfx::IntPoint position); +void paint_radial_gradient(PaintContext&, Gfx::IntRect const&, RadialGradientData const&, Gfx::IntPoint position, Gfx::FloatSize const& size); }