瀏覽代碼

LibWeb: Paint `radial-gradient()`s

This almost looks too easy now :^), but it's just another way to sample
the gradient line.
MacDue 2 年之前
父節點
當前提交
476acae04f

+ 14 - 2
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<int>(), m_resolved->gradient_size);
 }
 
 String ConicGradientStyleValue::to_string() const

+ 8 - 0
Userland/Libraries/LibWeb/CSS/StyleValue.h

@@ -1271,6 +1271,14 @@ private:
     Size m_size;
     PositionValue m_position;
     Vector<LinearColorStopListElement> m_color_stop_list;
+
+    struct ResolvedData {
+        Painting::RadialGradientData data;
+        Gfx::FloatSize gradient_size;
+        Gfx::FloatPoint center;
+    };
+
+    mutable Optional<ResolvedData> m_resolved;
 };
 
 class ConicGradientStyleValue final : public AbstractImageStyleValue {

+ 29 - 1
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();
+    });
+}
+
 }

+ 6 - 0
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);
 
 }