Pārlūkot izejas kodu

LibWeb: Support CSS property `background-clip: text`

From https://drafts.csswg.org/css-backgrounds-4/#background-clip
"The background is painted within (clipped to) the intersection of the
border box and the geometry of the text in the element and its in-flow
and floated descendants"

This change implements it in the following way:
1. Traverse the descendants of the element, collecting the Gfx::Path of
   glyphs into a vector.
2. The vector of collected paths is saved in the background painting
   command.
3. The painting commands executor uses the list of glyphs to paint a
   mask for background clipping.

Co-authored-by: Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
Zac Brannelly 1 gadu atpakaļ
vecāks
revīzija
9165faca5e
27 mainītis faili ar 353 papildinājumiem un 105 dzēšanām
  1. 84 0
      Tests/LibWeb/Ref/css-background-clip-text.html
  2. 15 0
      Tests/LibWeb/Ref/reference/css-background-clip-text-ref.html
  3. BIN
      Tests/LibWeb/Ref/reference/images/css-background-clip-text.png
  4. 1 1
      Userland/Libraries/LibWeb/CSS/StyleValues/AbstractImageStyleValue.h
  5. 2 2
      Userland/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.cpp
  6. 1 1
      Userland/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.h
  7. 2 2
      Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.cpp
  8. 1 1
      Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.h
  9. 2 2
      Userland/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.cpp
  10. 1 1
      Userland/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.h
  11. 3 2
      Userland/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.cpp
  12. 1 1
      Userland/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.h
  13. 6 5
      Userland/Libraries/LibWeb/Painting/BackgroundPainting.cpp
  14. 1 1
      Userland/Libraries/LibWeb/Painting/BackgroundPainting.h
  15. 6 0
      Userland/Libraries/LibWeb/Painting/Command.h
  16. 85 24
      Userland/Libraries/LibWeb/Painting/CommandExecutorCPU.cpp
  17. 6 6
      Userland/Libraries/LibWeb/Painting/CommandExecutorCPU.h
  18. 10 6
      Userland/Libraries/LibWeb/Painting/CommandExecutorGPU.cpp
  19. 6 6
      Userland/Libraries/LibWeb/Painting/CommandExecutorGPU.h
  20. 7 6
      Userland/Libraries/LibWeb/Painting/CommandList.cpp
  21. 7 6
      Userland/Libraries/LibWeb/Painting/CommandList.h
  22. 6 6
      Userland/Libraries/LibWeb/Painting/GradientPainting.cpp
  23. 3 3
      Userland/Libraries/LibWeb/Painting/GradientPainting.h
  24. 67 1
      Userland/Libraries/LibWeb/Painting/PaintableBox.cpp
  25. 2 0
      Userland/Libraries/LibWeb/Painting/PaintableBox.h
  26. 20 14
      Userland/Libraries/LibWeb/Painting/RecordingPainter.cpp
  27. 8 8
      Userland/Libraries/LibWeb/Painting/RecordingPainter.h

+ 84 - 0
Tests/LibWeb/Ref/css-background-clip-text.html

@@ -0,0 +1,84 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <link rel="match" href="reference/css-background-clip-text-ref.html" />
+  <title>Document</title>
+
+  <style>
+    p, .container {
+      border: 0.8em darkviolet;
+      border-style: dotted double;
+      margin: 1em 0;
+      padding: 1.4em;
+      font: 900 1.2em sans-serif;
+      text-decoration: underline;
+    }
+
+    .linear-gradient {
+      background: linear-gradient(60deg, red, yellow, red, yellow, red);
+    }
+
+    .radial-gradient {
+      background: radial-gradient(circle, red, yellow, red, yellow, red);
+    }
+
+    .conic-gradient {
+      background: conic-gradient(red, yellow, red, yellow, red);
+    }
+
+    .image-background {
+      background: url('./assets/car.png');
+    }
+
+    .border-box {
+      background-clip: border-box;
+    }
+    .padding-box {
+      background-clip: padding-box;
+    }
+    .content-box {
+      background-clip: content-box;
+    }
+
+    .text {
+      background-clip: text;
+      color: rgb(0 0 0 / 20%);
+    }
+    .new-background {
+      background: rgb(255 255 0 / 30%);
+    }
+  </style>
+</head>
+<body>
+  <!-- Hack to make the test runner wait for the image to load -->
+  <img src="./assets/car.png" />
+  
+  <p class="border-box linear-gradient">The background extends behind the border.</p>
+  <p class="padding-box radial-gradient">
+    The background extends to the inside edge of the border.
+  </p>
+  <p class="content-box conic-gradient">
+    The background extends only to the edge of the content box.
+  </p>
+  <div class="text container linear-gradient">
+    The background is clipped to the foreground text.
+    <span>Some other text in a sub-element</span>
+  </div>
+  <div class="text container radial-gradient">
+    The background is clipped to the foreground text.
+    <span>Some other text in a sub-element</span>
+  </div>
+  <div class="text container conic-gradient">
+    The background is clipped to the foreground text.
+    <span>Some other text in a sub-element</span>
+  </div>
+  <div class="text container image-background">
+    Testing text.
+    <div>
+      <div class="new-background" style="color: rgb(0 0 0 / 20%);">The is nested text that should still be clipped to the background</div>
+    </div>
+  </div>
+</body>
+</html>

+ 15 - 0
Tests/LibWeb/Ref/reference/css-background-clip-text-ref.html

@@ -0,0 +1,15 @@
+<style>
+  * {
+    margin: 0;
+  }
+  body {
+    background-color: white;
+  }
+</style>
+<!-- To rebase:
+  1. Open background-clip-text.html in Ladybird
+  2. Resize the window just above the width of the canvas
+  3. Right click > "Take Full Screenshot"
+  4. Update the image below:
+-->
+<img src="./images/css-background-clip-text.png">

BIN
Tests/LibWeb/Ref/reference/images/css-background-clip-text.png


+ 1 - 1
Userland/Libraries/LibWeb/CSS/StyleValues/AbstractImageStyleValue.h

@@ -36,7 +36,7 @@ public:
     virtual void resolve_for_size(Layout::NodeWithStyleAndBoxModelMetrics const&, CSSPixelSize) const {};
     virtual void resolve_for_size(Layout::NodeWithStyleAndBoxModelMetrics const&, CSSPixelSize) const {};
 
 
     virtual bool is_paintable() const = 0;
     virtual bool is_paintable() const = 0;
-    virtual void paint(PaintContext& context, DevicePixelRect const& dest_rect, ImageRendering) const = 0;
+    virtual void paint(PaintContext& context, DevicePixelRect const& dest_rect, ImageRendering, Vector<Gfx::Path> const& clip_paths = {}) const = 0;
 
 
     virtual Optional<Gfx::Color> color_if_single_pixel_bitmap() const { return {}; }
     virtual Optional<Gfx::Color> color_if_single_pixel_bitmap() const { return {}; }
 };
 };

+ 2 - 2
Userland/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.cpp

@@ -42,10 +42,10 @@ void ConicGradientStyleValue::resolve_for_size(Layout::NodeWithStyleAndBoxModelM
     m_resolved->position = m_properties.position->resolved(node, CSSPixelRect { { 0, 0 }, size });
     m_resolved->position = m_properties.position->resolved(node, CSSPixelRect { { 0, 0 }, size });
 }
 }
 
 
-void ConicGradientStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering) const
+void ConicGradientStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering, Vector<Gfx::Path> const& clip_paths) const
 {
 {
     VERIFY(m_resolved.has_value());
     VERIFY(m_resolved.has_value());
-    Painting::paint_conic_gradient(context, dest_rect, m_resolved->data, context.rounded_device_point(m_resolved->position));
+    Painting::paint_conic_gradient(context, dest_rect, m_resolved->data, context.rounded_device_point(m_resolved->position), clip_paths);
 }
 }
 
 
 bool ConicGradientStyleValue::equals(StyleValue const& other) const
 bool ConicGradientStyleValue::equals(StyleValue const& other) const

+ 1 - 1
Userland/Libraries/LibWeb/CSS/StyleValues/ConicGradientStyleValue.h

@@ -25,7 +25,7 @@ public:
 
 
     virtual String to_string() const override;
     virtual String to_string() const override;
 
 
-    void paint(PaintContext&, DevicePixelRect const& dest_rect, CSS::ImageRendering) const override;
+    void paint(PaintContext&, DevicePixelRect const& dest_rect, CSS::ImageRendering, Vector<Gfx::Path> const& clip_paths = {}) const override;
 
 
     virtual bool equals(StyleValue const& other) const override;
     virtual bool equals(StyleValue const& other) const override;
 
 

+ 2 - 2
Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.cpp

@@ -131,11 +131,11 @@ Optional<CSSPixelFraction> ImageStyleValue::natural_aspect_ratio() const
     return {};
     return {};
 }
 }
 
 
-void ImageStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering) const
+void ImageStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering, Vector<Gfx::Path> const& clip_paths) const
 {
 {
     if (auto const* b = bitmap(m_current_frame_index, dest_rect.size().to_type<int>()); b != nullptr) {
     if (auto const* b = bitmap(m_current_frame_index, dest_rect.size().to_type<int>()); b != nullptr) {
         auto scaling_mode = to_gfx_scaling_mode(image_rendering, b->rect(), dest_rect.to_type<int>());
         auto scaling_mode = to_gfx_scaling_mode(image_rendering, b->rect(), dest_rect.to_type<int>());
-        context.recording_painter().draw_scaled_immutable_bitmap(dest_rect.to_type<int>(), *b, b->rect(), scaling_mode);
+        context.recording_painter().draw_scaled_immutable_bitmap(dest_rect.to_type<int>(), *b, b->rect(), scaling_mode, clip_paths);
     }
     }
 }
 }
 
 

+ 1 - 1
Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.h

@@ -45,7 +45,7 @@ public:
     Optional<CSSPixelFraction> natural_aspect_ratio() const override;
     Optional<CSSPixelFraction> natural_aspect_ratio() const override;
 
 
     virtual bool is_paintable() const override;
     virtual bool is_paintable() const override;
-    void paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering) const override;
+    void paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering, Vector<Gfx::Path> const& clip_paths = {}) const override;
 
 
     virtual Optional<Gfx::Color> color_if_single_pixel_bitmap() const override;
     virtual Optional<Gfx::Color> color_if_single_pixel_bitmap() const override;
 
 

+ 2 - 2
Userland/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.cpp

@@ -109,10 +109,10 @@ void LinearGradientStyleValue::resolve_for_size(Layout::NodeWithStyleAndBoxModel
     m_resolved = ResolvedData { Painting::resolve_linear_gradient_data(node, size, *this), size };
     m_resolved = ResolvedData { Painting::resolve_linear_gradient_data(node, size, *this), size };
 }
 }
 
 
-void LinearGradientStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering) const
+void LinearGradientStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering, Vector<Gfx::Path> const& clip_paths) const
 {
 {
     VERIFY(m_resolved.has_value());
     VERIFY(m_resolved.has_value());
-    Painting::paint_linear_gradient(context, dest_rect, m_resolved->data);
+    Painting::paint_linear_gradient(context, dest_rect, m_resolved->data, clip_paths);
 }
 }
 
 
 }
 }

+ 1 - 1
Userland/Libraries/LibWeb/CSS/StyleValues/LinearGradientStyleValue.h

@@ -60,7 +60,7 @@ public:
     void resolve_for_size(Layout::NodeWithStyleAndBoxModelMetrics const&, CSSPixelSize) const override;
     void resolve_for_size(Layout::NodeWithStyleAndBoxModelMetrics const&, CSSPixelSize) const override;
 
 
     bool is_paintable() const override { return true; }
     bool is_paintable() const override { return true; }
-    void paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering) const override;
+    void paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering, Vector<Gfx::Path> const& clip_paths = {}) const override;
 
 
 private:
 private:
     LinearGradientStyleValue(GradientDirection direction, Vector<LinearColorStopListElement> color_stop_list, GradientType type, GradientRepeating repeating)
     LinearGradientStyleValue(GradientDirection direction, Vector<LinearColorStopListElement> color_stop_list, GradientType type, GradientRepeating repeating)

+ 3 - 2
Userland/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.cpp

@@ -207,12 +207,13 @@ bool RadialGradientStyleValue::equals(StyleValue const& other) const
     return m_properties == other_gradient.m_properties;
     return m_properties == other_gradient.m_properties;
 }
 }
 
 
-void RadialGradientStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering) const
+void RadialGradientStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering, Vector<Gfx::Path> const& clip_paths) const
 {
 {
     VERIFY(m_resolved.has_value());
     VERIFY(m_resolved.has_value());
     Painting::paint_radial_gradient(context, dest_rect, m_resolved->data,
     Painting::paint_radial_gradient(context, dest_rect, m_resolved->data,
         context.rounded_device_point(m_resolved->center),
         context.rounded_device_point(m_resolved->center),
-        context.rounded_device_size(m_resolved->gradient_size));
+        context.rounded_device_size(m_resolved->gradient_size),
+        clip_paths);
 }
 }
 
 
 }
 }

+ 1 - 1
Userland/Libraries/LibWeb/CSS/StyleValues/RadialGradientStyleValue.h

@@ -51,7 +51,7 @@ public:
 
 
     virtual String to_string() const override;
     virtual String to_string() const override;
 
 
-    void paint(PaintContext&, DevicePixelRect const& dest_rect, CSS::ImageRendering) const override;
+    void paint(PaintContext&, DevicePixelRect const& dest_rect, CSS::ImageRendering, Vector<Gfx::Path> const& clip_paths = {}) const override;
 
 
     virtual bool equals(StyleValue const& other) const override;
     virtual bool equals(StyleValue const& other) const override;
 
 

+ 6 - 5
Userland/Libraries/LibWeb/Painting/BackgroundPainting.cpp

@@ -61,7 +61,7 @@ static CSSPixelSize run_default_sizing_algorithm(
 }
 }
 
 
 // https://www.w3.org/TR/css-backgrounds-3/#backgrounds
 // https://www.w3.org/TR/css-backgrounds-3/#backgrounds
-void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMetrics const& layout_node, CSSPixelRect const& border_rect, Color background_color, CSS::ImageRendering image_rendering, Vector<CSS::BackgroundLayerData> const* background_layers, BorderRadiiData const& border_radii)
+void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMetrics const& layout_node, CSSPixelRect const& border_rect, Color background_color, CSS::ImageRendering image_rendering, Vector<CSS::BackgroundLayerData> const* background_layers, BorderRadiiData const& border_radii, Vector<Gfx::Path> const& clip_paths)
 {
 {
     auto& painter = context.recording_painter();
     auto& painter = context.recording_painter();
 
 
@@ -118,13 +118,14 @@ void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMet
         }
         }
     }
     }
 
 
-    context.recording_painter().fill_rect_with_rounded_corners(
+    painter.fill_rect_with_rounded_corners(
         context.rounded_device_rect(color_box.rect).to_type<int>(),
         context.rounded_device_rect(color_box.rect).to_type<int>(),
         background_color,
         background_color,
         color_box.radii.top_left.as_corner(context),
         color_box.radii.top_left.as_corner(context),
         color_box.radii.top_right.as_corner(context),
         color_box.radii.top_right.as_corner(context),
         color_box.radii.bottom_right.as_corner(context),
         color_box.radii.bottom_right.as_corner(context),
-        color_box.radii.bottom_left.as_corner(context));
+        color_box.radii.bottom_left.as_corner(context),
+        clip_paths);
 
 
     if (!has_paintable_layers)
     if (!has_paintable_layers)
         return;
         return;
@@ -388,10 +389,10 @@ void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMet
                     fill_rect = fill_rect->united(image_device_rect);
                     fill_rect = fill_rect->united(image_device_rect);
                 }
                 }
             });
             });
-            painter.fill_rect(fill_rect->to_type<int>(), color.value());
+            painter.fill_rect(fill_rect->to_type<int>(), color.value(), clip_paths);
         } else {
         } else {
             for_each_image_device_rect([&](auto const& image_device_rect) {
             for_each_image_device_rect([&](auto const& image_device_rect) {
-                image.paint(context, image_device_rect, image_rendering);
+                image.paint(context, image_device_rect, image_rendering, clip_paths);
             });
             });
         }
         }
     }
     }

+ 1 - 1
Userland/Libraries/LibWeb/Painting/BackgroundPainting.h

@@ -12,6 +12,6 @@
 
 
 namespace Web::Painting {
 namespace Web::Painting {
 
 
-void paint_background(PaintContext&, Layout::NodeWithStyleAndBoxModelMetrics const&, CSSPixelRect const&, Color background_color, CSS::ImageRendering, Vector<CSS::BackgroundLayerData> const*, BorderRadiiData const&);
+void paint_background(PaintContext&, Layout::NodeWithStyleAndBoxModelMetrics const&, CSSPixelRect const&, Color background_color, CSS::ImageRendering, Vector<CSS::BackgroundLayerData> const*, BorderRadiiData const&, Vector<Gfx::Path> const& clip_paths = {});
 
 
 }
 }

+ 6 - 0
Userland/Libraries/LibWeb/Painting/Command.h

@@ -65,6 +65,7 @@ struct DrawText {
 struct FillRect {
 struct FillRect {
     Gfx::IntRect rect;
     Gfx::IntRect rect;
     Color color;
     Color color;
+    Vector<Gfx::Path> clip_paths;
 
 
     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
     void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); }
     void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); }
@@ -85,6 +86,7 @@ struct DrawScaledImmutableBitmap {
     NonnullRefPtr<Gfx::ImmutableBitmap> bitmap;
     NonnullRefPtr<Gfx::ImmutableBitmap> bitmap;
     Gfx::IntRect src_rect;
     Gfx::IntRect src_rect;
     Gfx::Painter::ScalingMode scaling_mode;
     Gfx::Painter::ScalingMode scaling_mode;
+    Vector<Gfx::Path> clip_paths;
 
 
     [[nodiscard]] Gfx::IntRect bounding_rect() const { return dst_rect; }
     [[nodiscard]] Gfx::IntRect bounding_rect() const { return dst_rect; }
     void translate_by(Gfx::IntPoint const& offset) { dst_rect.translate_by(offset); }
     void translate_by(Gfx::IntPoint const& offset) { dst_rect.translate_by(offset); }
@@ -128,6 +130,7 @@ struct PopStackingContext { };
 struct PaintLinearGradient {
 struct PaintLinearGradient {
     Gfx::IntRect gradient_rect;
     Gfx::IntRect gradient_rect;
     LinearGradientData linear_gradient_data;
     LinearGradientData linear_gradient_data;
+    Vector<Gfx::Path> clip_paths;
 
 
     [[nodiscard]] Gfx::IntRect bounding_rect() const { return gradient_rect; }
     [[nodiscard]] Gfx::IntRect bounding_rect() const { return gradient_rect; }
 
 
@@ -170,6 +173,7 @@ struct FillRectWithRoundedCorners {
     Gfx::AntiAliasingPainter::CornerRadius top_right_radius;
     Gfx::AntiAliasingPainter::CornerRadius top_right_radius;
     Gfx::AntiAliasingPainter::CornerRadius bottom_left_radius;
     Gfx::AntiAliasingPainter::CornerRadius bottom_left_radius;
     Gfx::AntiAliasingPainter::CornerRadius bottom_right_radius;
     Gfx::AntiAliasingPainter::CornerRadius bottom_right_radius;
+    Vector<Gfx::Path> clip_paths;
 
 
     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
     void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); }
     void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); }
@@ -334,6 +338,7 @@ struct PaintRadialGradient {
     RadialGradientData radial_gradient_data;
     RadialGradientData radial_gradient_data;
     Gfx::IntPoint center;
     Gfx::IntPoint center;
     Gfx::IntSize size;
     Gfx::IntSize size;
+    Vector<Gfx::Path> clip_paths;
 
 
     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
 
 
@@ -344,6 +349,7 @@ struct PaintConicGradient {
     Gfx::IntRect rect;
     Gfx::IntRect rect;
     ConicGradientData conic_gradient_data;
     ConicGradientData conic_gradient_data;
     Gfx::IntPoint position;
     Gfx::IntPoint position;
+    Vector<Gfx::Path> clip_paths;
 
 
     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
 
 

+ 85 - 24
Userland/Libraries/LibWeb/Painting/CommandExecutorCPU.cpp

@@ -55,10 +55,38 @@ CommandResult CommandExecutorCPU::draw_text(Gfx::IntRect const& rect, String con
     return CommandResult::Continue;
     return CommandResult::Continue;
 }
 }
 
 
-CommandResult CommandExecutorCPU::fill_rect(Gfx::IntRect const& rect, Color const& color)
+template<typename Callback>
+void apply_clip_paths_to_painter(Gfx::IntRect const& rect, Callback callback, Vector<Gfx::Path> const& clip_paths, Gfx::Painter& target_painter)
 {
 {
-    auto& painter = this->painter();
-    painter.fill_rect(rect, color);
+    // Setup a painter for a background canvas that we will paint to first.
+    auto background_canvas = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, rect.size()).release_value_but_fixme_should_propagate_errors();
+    Gfx::Painter painter(*background_canvas);
+
+    // Offset the painter to paint in the correct location.
+    painter.translate(-rect.location());
+
+    // Paint the background canvas.
+    callback(painter);
+
+    // Apply the clip path to the target painter.
+    Gfx::AntiAliasingPainter aa_painter(target_painter);
+    for (auto const& clip_path : clip_paths) {
+        auto fill_offset = clip_path.bounding_box().location().to_type<int>() - rect.location();
+        auto paint_style = Gfx::BitmapPaintStyle::create(*background_canvas, fill_offset).release_value_but_fixme_should_propagate_errors();
+        aa_painter.fill_path(clip_path, paint_style);
+    }
+}
+
+CommandResult CommandExecutorCPU::fill_rect(Gfx::IntRect const& rect, Color const& color, Vector<Gfx::Path> const& clip_paths)
+{
+    auto paint_op = [&](Gfx::Painter& painter) {
+        painter.fill_rect(rect, color);
+    };
+    if (clip_paths.is_empty()) {
+        paint_op(painter());
+    } else {
+        apply_clip_paths_to_painter(rect, paint_op, clip_paths, painter());
+    }
     return CommandResult::Continue;
     return CommandResult::Continue;
 }
 }
 
 
@@ -69,10 +97,16 @@ CommandResult CommandExecutorCPU::draw_scaled_bitmap(Gfx::IntRect const& dst_rec
     return CommandResult::Continue;
     return CommandResult::Continue;
 }
 }
 
 
-CommandResult CommandExecutorCPU::draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const& immutable_bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode)
+CommandResult CommandExecutorCPU::draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const& immutable_bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode, Vector<Gfx::Path> const& clip_paths)
 {
 {
-    auto& painter = this->painter();
-    painter.draw_scaled_bitmap(dst_rect, immutable_bitmap.bitmap(), src_rect, 1, scaling_mode);
+    auto paint_op = [&](Gfx::Painter& painter) {
+        painter.draw_scaled_bitmap(dst_rect, immutable_bitmap.bitmap(), src_rect, 1, scaling_mode);
+    };
+    if (clip_paths.is_empty()) {
+        paint_op(painter());
+    } else {
+        apply_clip_paths_to_painter(dst_rect, paint_op, clip_paths, painter());
+    }
     return CommandResult::Continue;
     return CommandResult::Continue;
 }
 }
 
 
@@ -200,12 +234,18 @@ CommandResult CommandExecutorCPU::pop_stacking_context()
     return CommandResult::Continue;
     return CommandResult::Continue;
 }
 }
 
 
-CommandResult CommandExecutorCPU::paint_linear_gradient(Gfx::IntRect const& gradient_rect, Web::Painting::LinearGradientData const& linear_gradient_data)
+CommandResult CommandExecutorCPU::paint_linear_gradient(Gfx::IntRect const& gradient_rect, Web::Painting::LinearGradientData const& linear_gradient_data, Vector<Gfx::Path> const& clip_paths)
 {
 {
-    auto const& data = linear_gradient_data;
-    painter().fill_rect_with_linear_gradient(
-        gradient_rect, data.color_stops.list,
-        data.gradient_angle, data.color_stops.repeat_length);
+    auto paint_op = [&](Gfx::Painter& painter) {
+        painter.fill_rect_with_linear_gradient(
+            gradient_rect, linear_gradient_data.color_stops.list,
+            linear_gradient_data.gradient_angle, linear_gradient_data.color_stops.repeat_length);
+    };
+    if (clip_paths.is_empty()) {
+        paint_op(painter());
+    } else {
+        apply_clip_paths_to_painter(gradient_rect, paint_op, clip_paths, painter());
+    }
     return CommandResult::Continue;
     return CommandResult::Continue;
 }
 }
 
 
@@ -255,16 +295,23 @@ CommandResult CommandExecutorCPU::paint_text_shadow(int blur_radius, Gfx::IntRec
     return CommandResult::Continue;
     return CommandResult::Continue;
 }
 }
 
 
-CommandResult CommandExecutorCPU::fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color const& color, Gfx::AntiAliasingPainter::CornerRadius const& top_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& top_right_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_right_radius)
+CommandResult CommandExecutorCPU::fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color const& color, Gfx::AntiAliasingPainter::CornerRadius const& top_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& top_right_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_right_radius, Vector<Gfx::Path> const& clip_paths)
 {
 {
-    Gfx::AntiAliasingPainter aa_painter(painter());
-    aa_painter.fill_rect_with_rounded_corners(
-        rect,
-        color,
-        top_left_radius,
-        top_right_radius,
-        bottom_right_radius,
-        bottom_left_radius);
+    auto paint_op = [&](Gfx::Painter& painter) {
+        Gfx::AntiAliasingPainter aa_painter(painter);
+        aa_painter.fill_rect_with_rounded_corners(
+            rect,
+            color,
+            top_left_radius,
+            top_right_radius,
+            bottom_right_radius,
+            bottom_left_radius);
+    };
+    if (clip_paths.is_empty()) {
+        paint_op(painter());
+    } else {
+        apply_clip_paths_to_painter(rect, paint_op, clip_paths, painter());
+    }
     return CommandResult::Continue;
     return CommandResult::Continue;
 }
 }
 
 
@@ -380,15 +427,29 @@ CommandResult CommandExecutorCPU::draw_rect(Gfx::IntRect const& rect, Color cons
     return CommandResult::Continue;
     return CommandResult::Continue;
 }
 }
 
 
-CommandResult CommandExecutorCPU::paint_radial_gradient(Gfx::IntRect const& rect, Web::Painting::RadialGradientData const& radial_gradient_data, Gfx::IntPoint const& center, Gfx::IntSize const& size)
+CommandResult CommandExecutorCPU::paint_radial_gradient(Gfx::IntRect const& rect, Web::Painting::RadialGradientData const& radial_gradient_data, Gfx::IntPoint const& center, Gfx::IntSize const& size, Vector<Gfx::Path> const& clip_paths)
 {
 {
-    painter().fill_rect_with_radial_gradient(rect, radial_gradient_data.color_stops.list, center, size, radial_gradient_data.color_stops.repeat_length);
+    auto paint_op = [&](Gfx::Painter& painter) {
+        painter.fill_rect_with_radial_gradient(rect, radial_gradient_data.color_stops.list, center, size, radial_gradient_data.color_stops.repeat_length);
+    };
+    if (clip_paths.is_empty()) {
+        paint_op(painter());
+    } else {
+        apply_clip_paths_to_painter(rect, paint_op, clip_paths, painter());
+    }
     return CommandResult::Continue;
     return CommandResult::Continue;
 }
 }
 
 
-CommandResult CommandExecutorCPU::paint_conic_gradient(Gfx::IntRect const& rect, Web::Painting::ConicGradientData const& conic_gradient_data, Gfx::IntPoint const& position)
+CommandResult CommandExecutorCPU::paint_conic_gradient(Gfx::IntRect const& rect, Web::Painting::ConicGradientData const& conic_gradient_data, Gfx::IntPoint const& position, Vector<Gfx::Path> const& clip_paths)
 {
 {
-    painter().fill_rect_with_conic_gradient(rect, conic_gradient_data.color_stops.list, position, conic_gradient_data.start_angle, conic_gradient_data.color_stops.repeat_length);
+    auto paint_op = [&](Gfx::Painter& painter) {
+        painter.fill_rect_with_conic_gradient(rect, conic_gradient_data.color_stops.list, position, conic_gradient_data.start_angle, conic_gradient_data.color_stops.repeat_length);
+    };
+    if (clip_paths.is_empty()) {
+        paint_op(painter());
+    } else {
+        apply_clip_paths_to_painter(rect, paint_op, clip_paths, painter());
+    }
     return CommandResult::Continue;
     return CommandResult::Continue;
 }
 }
 
 

+ 6 - 6
Userland/Libraries/LibWeb/Painting/CommandExecutorCPU.h

@@ -15,18 +15,18 @@ class CommandExecutorCPU : public CommandExecutor {
 public:
 public:
     CommandResult draw_glyph_run(Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run, Color const&, Gfx::FloatPoint translation, double scale) override;
     CommandResult draw_glyph_run(Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run, Color const&, Gfx::FloatPoint translation, double scale) override;
     CommandResult draw_text(Gfx::IntRect const& rect, String const& raw_text, Gfx::TextAlignment alignment, Color const&, Gfx::TextElision, Gfx::TextWrapping, Optional<NonnullRefPtr<Gfx::Font>> const&) override;
     CommandResult draw_text(Gfx::IntRect const& rect, String const& raw_text, Gfx::TextAlignment alignment, Color const&, Gfx::TextElision, Gfx::TextWrapping, Optional<NonnullRefPtr<Gfx::Font>> const&) override;
-    CommandResult fill_rect(Gfx::IntRect const& rect, Color const&) override;
+    CommandResult fill_rect(Gfx::IntRect const& rect, Color const&, Vector<Gfx::Path> const& clip_paths) override;
     CommandResult draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode) override;
     CommandResult draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode) override;
-    CommandResult draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const&, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode) override;
+    CommandResult draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const&, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode, Vector<Gfx::Path> const& clip_paths = {}) override;
     CommandResult set_clip_rect(Gfx::IntRect const& rect) override;
     CommandResult set_clip_rect(Gfx::IntRect const& rect) override;
     CommandResult clear_clip_rect() override;
     CommandResult clear_clip_rect() override;
     CommandResult push_stacking_context(float opacity, bool is_fixed_position, Gfx::IntRect const& source_paintable_rect, Gfx::IntPoint post_transform_translation, CSS::ImageRendering image_rendering, StackingContextTransform transform, Optional<StackingContextMask> mask) override;
     CommandResult push_stacking_context(float opacity, bool is_fixed_position, Gfx::IntRect const& source_paintable_rect, Gfx::IntPoint post_transform_translation, CSS::ImageRendering image_rendering, StackingContextTransform transform, Optional<StackingContextMask> mask) override;
     CommandResult pop_stacking_context() override;
     CommandResult pop_stacking_context() override;
-    CommandResult paint_linear_gradient(Gfx::IntRect const&, Web::Painting::LinearGradientData const&) override;
+    CommandResult paint_linear_gradient(Gfx::IntRect const&, Web::Painting::LinearGradientData const&, Vector<Gfx::Path> const& clip_paths = {}) override;
     CommandResult paint_outer_box_shadow(PaintOuterBoxShadowParams const&) override;
     CommandResult paint_outer_box_shadow(PaintOuterBoxShadowParams const&) override;
     CommandResult paint_inner_box_shadow(PaintOuterBoxShadowParams const&) override;
     CommandResult paint_inner_box_shadow(PaintOuterBoxShadowParams const&) override;
     CommandResult paint_text_shadow(int blur_radius, Gfx::IntRect const& shadow_bounding_rect, Gfx::IntRect const& text_rect, Span<Gfx::DrawGlyphOrEmoji const>, Color const&, int fragment_baseline, Gfx::IntPoint const& draw_location) override;
     CommandResult paint_text_shadow(int blur_radius, Gfx::IntRect const& shadow_bounding_rect, Gfx::IntRect const& text_rect, Span<Gfx::DrawGlyphOrEmoji const>, Color const&, int fragment_baseline, Gfx::IntPoint const& draw_location) override;
-    CommandResult fill_rect_with_rounded_corners(Gfx::IntRect const&, Color const&, Gfx::AntiAliasingPainter::CornerRadius const& top_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& top_right_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_right_radius) override;
+    CommandResult fill_rect_with_rounded_corners(Gfx::IntRect const&, Color const&, Gfx::AntiAliasingPainter::CornerRadius const& top_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& top_right_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_right_radius, Vector<Gfx::Path> const& clip_paths) override;
     CommandResult fill_path_using_color(Gfx::Path const&, Color const&, Gfx::Painter::WindingRule winding_rule, Gfx::FloatPoint const& aa_translation) override;
     CommandResult fill_path_using_color(Gfx::Path const&, Color const&, Gfx::Painter::WindingRule winding_rule, Gfx::FloatPoint const& aa_translation) override;
     CommandResult fill_path_using_paint_style(Gfx::Path const&, Gfx::PaintStyle const& paint_style, Gfx::Painter::WindingRule winding_rule, float opacity, Gfx::FloatPoint const& aa_translation) override;
     CommandResult fill_path_using_paint_style(Gfx::Path const&, Gfx::PaintStyle const& paint_style, Gfx::Painter::WindingRule winding_rule, float opacity, Gfx::FloatPoint const& aa_translation) override;
     CommandResult stroke_path_using_color(Gfx::Path const&, Color const& color, float thickness, Gfx::FloatPoint const& aa_translation) override;
     CommandResult stroke_path_using_color(Gfx::Path const&, Color const& color, float thickness, Gfx::FloatPoint const& aa_translation) override;
@@ -38,8 +38,8 @@ public:
     CommandResult paint_frame(Gfx::IntRect const& rect, Palette const&, Gfx::FrameStyle) override;
     CommandResult paint_frame(Gfx::IntRect const& rect, Palette const&, Gfx::FrameStyle) override;
     CommandResult apply_backdrop_filter(Gfx::IntRect const& backdrop_region, Web::CSS::ResolvedBackdropFilter const& backdrop_filter) override;
     CommandResult apply_backdrop_filter(Gfx::IntRect const& backdrop_region, Web::CSS::ResolvedBackdropFilter const& backdrop_filter) override;
     CommandResult draw_rect(Gfx::IntRect const& rect, Color const&, bool rough) override;
     CommandResult draw_rect(Gfx::IntRect const& rect, Color const&, bool rough) override;
-    CommandResult paint_radial_gradient(Gfx::IntRect const& rect, Web::Painting::RadialGradientData const& radial_gradient_data, Gfx::IntPoint const& center, Gfx::IntSize const& size) override;
-    CommandResult paint_conic_gradient(Gfx::IntRect const& rect, Web::Painting::ConicGradientData const& conic_gradient_data, Gfx::IntPoint const& position) override;
+    CommandResult paint_radial_gradient(Gfx::IntRect const& rect, Web::Painting::RadialGradientData const& radial_gradient_data, Gfx::IntPoint const& center, Gfx::IntSize const& size, Vector<Gfx::Path> const& clip_paths = {}) override;
+    CommandResult paint_conic_gradient(Gfx::IntRect const& rect, Web::Painting::ConicGradientData const& conic_gradient_data, Gfx::IntPoint const& position, Vector<Gfx::Path> const& clip_paths = {}) override;
     CommandResult draw_triangle_wave(Gfx::IntPoint const& p1, Gfx::IntPoint const& p2, Color const&, int amplitude, int thickness) override;
     CommandResult draw_triangle_wave(Gfx::IntPoint const& p1, Gfx::IntPoint const& p2, Color const&, int amplitude, int thickness) override;
     CommandResult sample_under_corners(u32 id, CornerRadii const&, Gfx::IntRect const&, CornerClip) override;
     CommandResult sample_under_corners(u32 id, CornerRadii const&, Gfx::IntRect const&, CornerClip) override;
     CommandResult blit_corner_clipping(u32 id) override;
     CommandResult blit_corner_clipping(u32 id) override;

+ 10 - 6
Userland/Libraries/LibWeb/Painting/CommandExecutorGPU.cpp

@@ -53,8 +53,9 @@ CommandResult CommandExecutorGPU::draw_text(Gfx::IntRect const&, String const&,
     return CommandResult::Continue;
     return CommandResult::Continue;
 }
 }
 
 
-CommandResult CommandExecutorGPU::fill_rect(Gfx::IntRect const& rect, Color const& color)
+CommandResult CommandExecutorGPU::fill_rect(Gfx::IntRect const& rect, Color const& color, Vector<Gfx::Path> const&)
 {
 {
+    // FIXME: Support clip paths
     painter().fill_rect(rect, color);
     painter().fill_rect(rect, color);
     return CommandResult::Continue;
     return CommandResult::Continue;
 }
 }
@@ -80,8 +81,9 @@ CommandResult CommandExecutorGPU::draw_scaled_bitmap(Gfx::IntRect const& dst_rec
     return CommandResult::Continue;
     return CommandResult::Continue;
 }
 }
 
 
-CommandResult CommandExecutorGPU::draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const& immutable_bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode)
+CommandResult CommandExecutorGPU::draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const& immutable_bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode, Vector<Gfx::Path> const&)
 {
 {
+    // TODO: Support clip paths
     painter().draw_scaled_immutable_bitmap(dst_rect, immutable_bitmap, src_rect, to_accelgfx_scaling_mode(scaling_mode));
     painter().draw_scaled_immutable_bitmap(dst_rect, immutable_bitmap, src_rect, to_accelgfx_scaling_mode(scaling_mode));
     return CommandResult::Continue;
     return CommandResult::Continue;
 }
 }
@@ -160,8 +162,9 @@ CommandResult CommandExecutorGPU::pop_stacking_context()
     return CommandResult::Continue;
     return CommandResult::Continue;
 }
 }
 
 
-CommandResult CommandExecutorGPU::paint_linear_gradient(Gfx::IntRect const& rect, Web::Painting::LinearGradientData const& data)
+CommandResult CommandExecutorGPU::paint_linear_gradient(Gfx::IntRect const& rect, Web::Painting::LinearGradientData const& data, Vector<Gfx::Path> const&)
 {
 {
+    // FIXME: Support clip paths
     painter().fill_rect_with_linear_gradient(rect, data.color_stops.list, data.gradient_angle, data.color_stops.repeat_length);
     painter().fill_rect_with_linear_gradient(rect, data.color_stops.list, data.gradient_angle, data.color_stops.repeat_length);
     return CommandResult::Continue;
     return CommandResult::Continue;
 }
 }
@@ -201,8 +204,9 @@ CommandResult CommandExecutorGPU::paint_text_shadow(int blur_radius, Gfx::IntRec
     return CommandResult::Continue;
     return CommandResult::Continue;
 }
 }
 
 
-CommandResult CommandExecutorGPU::fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color const& color, Gfx::AntiAliasingPainter::CornerRadius const& top_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& top_right_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_right_radius)
+CommandResult CommandExecutorGPU::fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color const& color, Gfx::AntiAliasingPainter::CornerRadius const& top_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& top_right_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_right_radius, Vector<Gfx::Path> const&)
 {
 {
+    // FIXME: Support clip paths
     painter().fill_rect_with_rounded_corners(
     painter().fill_rect_with_rounded_corners(
         rect, color,
         rect, color,
         { static_cast<float>(top_left_radius.horizontal_radius), static_cast<float>(top_left_radius.vertical_radius) },
         { static_cast<float>(top_left_radius.horizontal_radius), static_cast<float>(top_left_radius.vertical_radius) },
@@ -286,13 +290,13 @@ CommandResult CommandExecutorGPU::draw_rect(Gfx::IntRect const&, Color const&, b
     return CommandResult::Continue;
     return CommandResult::Continue;
 }
 }
 
 
-CommandResult CommandExecutorGPU::paint_radial_gradient(Gfx::IntRect const&, Web::Painting::RadialGradientData const&, Gfx::IntPoint const&, Gfx::IntSize const&)
+CommandResult CommandExecutorGPU::paint_radial_gradient(Gfx::IntRect const&, Web::Painting::RadialGradientData const&, Gfx::IntPoint const&, Gfx::IntSize const&, Vector<Gfx::Path> const&)
 {
 {
     // FIXME
     // FIXME
     return CommandResult::Continue;
     return CommandResult::Continue;
 }
 }
 
 
-CommandResult CommandExecutorGPU::paint_conic_gradient(Gfx::IntRect const&, Web::Painting::ConicGradientData const&, Gfx::IntPoint const&)
+CommandResult CommandExecutorGPU::paint_conic_gradient(Gfx::IntRect const&, Web::Painting::ConicGradientData const&, Gfx::IntPoint const&, Vector<Gfx::Path> const&)
 {
 {
     // FIXME
     // FIXME
     return CommandResult::Continue;
     return CommandResult::Continue;

+ 6 - 6
Userland/Libraries/LibWeb/Painting/CommandExecutorGPU.h

@@ -16,18 +16,18 @@ class CommandExecutorGPU : public CommandExecutor {
 public:
 public:
     CommandResult draw_glyph_run(Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run, Color const&, Gfx::FloatPoint translation, double scale) override;
     CommandResult draw_glyph_run(Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run, Color const&, Gfx::FloatPoint translation, double scale) override;
     CommandResult draw_text(Gfx::IntRect const& rect, String const& raw_text, Gfx::TextAlignment alignment, Color const&, Gfx::TextElision, Gfx::TextWrapping, Optional<NonnullRefPtr<Gfx::Font>> const&) override;
     CommandResult draw_text(Gfx::IntRect const& rect, String const& raw_text, Gfx::TextAlignment alignment, Color const&, Gfx::TextElision, Gfx::TextWrapping, Optional<NonnullRefPtr<Gfx::Font>> const&) override;
-    CommandResult fill_rect(Gfx::IntRect const& rect, Color const&) override;
+    CommandResult fill_rect(Gfx::IntRect const& rect, Color const&, Vector<Gfx::Path> const& clip_paths) override;
     CommandResult draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode) override;
     CommandResult draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode) override;
-    CommandResult draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const&, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode) override;
+    CommandResult draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const&, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode, Vector<Gfx::Path> const& clip_paths = {}) override;
     CommandResult set_clip_rect(Gfx::IntRect const& rect) override;
     CommandResult set_clip_rect(Gfx::IntRect const& rect) override;
     CommandResult clear_clip_rect() override;
     CommandResult clear_clip_rect() override;
     CommandResult push_stacking_context(float opacity, bool, Gfx::IntRect const& source_paintable_rect, Gfx::IntPoint post_transform_translation, CSS::ImageRendering image_rendering, StackingContextTransform transform, Optional<StackingContextMask> mask) override;
     CommandResult push_stacking_context(float opacity, bool, Gfx::IntRect const& source_paintable_rect, Gfx::IntPoint post_transform_translation, CSS::ImageRendering image_rendering, StackingContextTransform transform, Optional<StackingContextMask> mask) override;
     CommandResult pop_stacking_context() override;
     CommandResult pop_stacking_context() override;
-    CommandResult paint_linear_gradient(Gfx::IntRect const&, Web::Painting::LinearGradientData const&) override;
+    CommandResult paint_linear_gradient(Gfx::IntRect const&, Web::Painting::LinearGradientData const&, Vector<Gfx::Path> const& clip_paths = {}) override;
     CommandResult paint_outer_box_shadow(PaintOuterBoxShadowParams const&) override;
     CommandResult paint_outer_box_shadow(PaintOuterBoxShadowParams const&) override;
     CommandResult paint_inner_box_shadow(PaintOuterBoxShadowParams const&) override;
     CommandResult paint_inner_box_shadow(PaintOuterBoxShadowParams const&) override;
     CommandResult paint_text_shadow(int blur_radius, Gfx::IntRect const& shadow_bounding_rect, Gfx::IntRect const& text_rect, Span<Gfx::DrawGlyphOrEmoji const>, Color const&, int fragment_baseline, Gfx::IntPoint const& draw_location) override;
     CommandResult paint_text_shadow(int blur_radius, Gfx::IntRect const& shadow_bounding_rect, Gfx::IntRect const& text_rect, Span<Gfx::DrawGlyphOrEmoji const>, Color const&, int fragment_baseline, Gfx::IntPoint const& draw_location) override;
-    CommandResult fill_rect_with_rounded_corners(Gfx::IntRect const&, Color const&, Gfx::AntiAliasingPainter::CornerRadius const& top_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& top_right_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_right_radius) override;
+    CommandResult fill_rect_with_rounded_corners(Gfx::IntRect const&, Color const&, Gfx::AntiAliasingPainter::CornerRadius const& top_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& top_right_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_right_radius, Vector<Gfx::Path> const& clip_paths) override;
     CommandResult fill_path_using_color(Gfx::Path const&, Color const&, Gfx::Painter::WindingRule winding_rule, Gfx::FloatPoint const& aa_translation) override;
     CommandResult fill_path_using_color(Gfx::Path const&, Color const&, Gfx::Painter::WindingRule winding_rule, Gfx::FloatPoint const& aa_translation) override;
     CommandResult fill_path_using_paint_style(Gfx::Path const&, Gfx::PaintStyle const& paint_style, Gfx::Painter::WindingRule winding_rule, float opacity, Gfx::FloatPoint const& aa_translation) override;
     CommandResult fill_path_using_paint_style(Gfx::Path const&, Gfx::PaintStyle const& paint_style, Gfx::Painter::WindingRule winding_rule, float opacity, Gfx::FloatPoint const& aa_translation) override;
     CommandResult stroke_path_using_color(Gfx::Path const&, Color const& color, float thickness, Gfx::FloatPoint const& aa_translation) override;
     CommandResult stroke_path_using_color(Gfx::Path const&, Color const& color, float thickness, Gfx::FloatPoint const& aa_translation) override;
@@ -39,8 +39,8 @@ public:
     CommandResult paint_frame(Gfx::IntRect const& rect, Palette const&, Gfx::FrameStyle) override;
     CommandResult paint_frame(Gfx::IntRect const& rect, Palette const&, Gfx::FrameStyle) override;
     CommandResult apply_backdrop_filter(Gfx::IntRect const& backdrop_region, Web::CSS::ResolvedBackdropFilter const& backdrop_filter) override;
     CommandResult apply_backdrop_filter(Gfx::IntRect const& backdrop_region, Web::CSS::ResolvedBackdropFilter const& backdrop_filter) override;
     CommandResult draw_rect(Gfx::IntRect const& rect, Color const&, bool rough) override;
     CommandResult draw_rect(Gfx::IntRect const& rect, Color const&, bool rough) override;
-    CommandResult paint_radial_gradient(Gfx::IntRect const& rect, Web::Painting::RadialGradientData const& radial_gradient_data, Gfx::IntPoint const& center, Gfx::IntSize const& size) override;
-    CommandResult paint_conic_gradient(Gfx::IntRect const& rect, Web::Painting::ConicGradientData const& conic_gradient_data, Gfx::IntPoint const& position) override;
+    CommandResult paint_radial_gradient(Gfx::IntRect const& rect, Web::Painting::RadialGradientData const& radial_gradient_data, Gfx::IntPoint const& center, Gfx::IntSize const& size, Vector<Gfx::Path> const& clip_paths = {}) override;
+    CommandResult paint_conic_gradient(Gfx::IntRect const& rect, Web::Painting::ConicGradientData const& conic_gradient_data, Gfx::IntPoint const& position, Vector<Gfx::Path> const& clip_paths = {}) override;
     CommandResult draw_triangle_wave(Gfx::IntPoint const& p1, Gfx::IntPoint const& p2, Color const&, int amplitude, int thickness) override;
     CommandResult draw_triangle_wave(Gfx::IntPoint const& p1, Gfx::IntPoint const& p2, Color const&, int amplitude, int thickness) override;
     CommandResult sample_under_corners(u32 id, CornerRadii const&, Gfx::IntRect const&, CornerClip) override;
     CommandResult sample_under_corners(u32 id, CornerRadii const&, Gfx::IntRect const&, CornerClip) override;
     CommandResult blit_corner_clipping(u32) override;
     CommandResult blit_corner_clipping(u32) override;

+ 7 - 6
Userland/Libraries/LibWeb/Painting/CommandList.cpp

@@ -96,7 +96,7 @@ void CommandList::execute(CommandExecutor& executor)
                     command.elision, command.wrapping, command.font);
                     command.elision, command.wrapping, command.font);
             },
             },
             [&](FillRect const& command) {
             [&](FillRect const& command) {
-                return executor.fill_rect(command.rect, command.color);
+                return executor.fill_rect(command.rect, command.color, command.clip_paths);
             },
             },
             [&](DrawScaledBitmap const& command) {
             [&](DrawScaledBitmap const& command) {
                 return executor.draw_scaled_bitmap(command.dst_rect, command.bitmap, command.src_rect,
                 return executor.draw_scaled_bitmap(command.dst_rect, command.bitmap, command.src_rect,
@@ -104,7 +104,7 @@ void CommandList::execute(CommandExecutor& executor)
             },
             },
             [&](DrawScaledImmutableBitmap const& command) {
             [&](DrawScaledImmutableBitmap const& command) {
                 return executor.draw_scaled_immutable_bitmap(command.dst_rect, command.bitmap, command.src_rect,
                 return executor.draw_scaled_immutable_bitmap(command.dst_rect, command.bitmap, command.src_rect,
-                    command.scaling_mode);
+                    command.scaling_mode, command.clip_paths);
             },
             },
             [&](SetClipRect const& command) {
             [&](SetClipRect const& command) {
                 return executor.set_clip_rect(command.rect);
                 return executor.set_clip_rect(command.rect);
@@ -122,15 +122,15 @@ void CommandList::execute(CommandExecutor& executor)
                 return executor.pop_stacking_context();
                 return executor.pop_stacking_context();
             },
             },
             [&](PaintLinearGradient const& command) {
             [&](PaintLinearGradient const& command) {
-                return executor.paint_linear_gradient(command.gradient_rect, command.linear_gradient_data);
+                return executor.paint_linear_gradient(command.gradient_rect, command.linear_gradient_data, command.clip_paths);
             },
             },
             [&](PaintRadialGradient const& command) {
             [&](PaintRadialGradient const& command) {
                 return executor.paint_radial_gradient(command.rect, command.radial_gradient_data,
                 return executor.paint_radial_gradient(command.rect, command.radial_gradient_data,
-                    command.center, command.size);
+                    command.center, command.size, command.clip_paths);
             },
             },
             [&](PaintConicGradient const& command) {
             [&](PaintConicGradient const& command) {
                 return executor.paint_conic_gradient(command.rect, command.conic_gradient_data,
                 return executor.paint_conic_gradient(command.rect, command.conic_gradient_data,
-                    command.position);
+                    command.position, command.clip_paths);
             },
             },
             [&](PaintOuterBoxShadow const& command) {
             [&](PaintOuterBoxShadow const& command) {
                 return executor.paint_outer_box_shadow(command.outer_box_shadow_params);
                 return executor.paint_outer_box_shadow(command.outer_box_shadow_params);
@@ -148,7 +148,8 @@ void CommandList::execute(CommandExecutor& executor)
                     command.top_left_radius,
                     command.top_left_radius,
                     command.top_right_radius,
                     command.top_right_radius,
                     command.bottom_left_radius,
                     command.bottom_left_radius,
-                    command.bottom_right_radius);
+                    command.bottom_right_radius,
+                    command.clip_paths);
             },
             },
             [&](FillPathUsingColor const& command) {
             [&](FillPathUsingColor const& command) {
                 return executor.fill_path_using_color(command.path, command.color, command.winding_rule,
                 return executor.fill_path_using_color(command.path, command.color, command.winding_rule,

+ 7 - 6
Userland/Libraries/LibWeb/Painting/CommandList.h

@@ -49,16 +49,16 @@ public:
 
 
     virtual CommandResult draw_glyph_run(Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run, Color const&, Gfx::FloatPoint translation, double scale) = 0;
     virtual CommandResult draw_glyph_run(Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run, Color const&, Gfx::FloatPoint translation, double scale) = 0;
     virtual CommandResult draw_text(Gfx::IntRect const&, String const&, Gfx::TextAlignment alignment, Color const&, Gfx::TextElision, Gfx::TextWrapping, Optional<NonnullRefPtr<Gfx::Font>> const&) = 0;
     virtual CommandResult draw_text(Gfx::IntRect const&, String const&, Gfx::TextAlignment alignment, Color const&, Gfx::TextElision, Gfx::TextWrapping, Optional<NonnullRefPtr<Gfx::Font>> const&) = 0;
-    virtual CommandResult fill_rect(Gfx::IntRect const&, Color const&) = 0;
+    virtual CommandResult fill_rect(Gfx::IntRect const&, Color const&, Vector<Gfx::Path> const& clip_paths) = 0;
     virtual CommandResult draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode) = 0;
     virtual CommandResult draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode) = 0;
-    virtual CommandResult draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const&, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode) = 0;
+    virtual CommandResult draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const&, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode, Vector<Gfx::Path> const& clip_paths = {}) = 0;
     virtual CommandResult set_clip_rect(Gfx::IntRect const& rect) = 0;
     virtual CommandResult set_clip_rect(Gfx::IntRect const& rect) = 0;
     virtual CommandResult clear_clip_rect() = 0;
     virtual CommandResult clear_clip_rect() = 0;
     virtual CommandResult push_stacking_context(float opacity, bool is_fixed_position, Gfx::IntRect const& source_paintable_rect, Gfx::IntPoint post_transform_translation, CSS::ImageRendering image_rendering, StackingContextTransform transform, Optional<StackingContextMask> mask) = 0;
     virtual CommandResult push_stacking_context(float opacity, bool is_fixed_position, Gfx::IntRect const& source_paintable_rect, Gfx::IntPoint post_transform_translation, CSS::ImageRendering image_rendering, StackingContextTransform transform, Optional<StackingContextMask> mask) = 0;
     virtual CommandResult pop_stacking_context() = 0;
     virtual CommandResult pop_stacking_context() = 0;
-    virtual CommandResult paint_linear_gradient(Gfx::IntRect const&, LinearGradientData const&) = 0;
-    virtual CommandResult paint_radial_gradient(Gfx::IntRect const& rect, RadialGradientData const&, Gfx::IntPoint const& center, Gfx::IntSize const& size) = 0;
-    virtual CommandResult paint_conic_gradient(Gfx::IntRect const& rect, ConicGradientData const&, Gfx::IntPoint const& position) = 0;
+    virtual CommandResult paint_linear_gradient(Gfx::IntRect const&, LinearGradientData const&, Vector<Gfx::Path> const& clip_paths = {}) = 0;
+    virtual CommandResult paint_radial_gradient(Gfx::IntRect const& rect, RadialGradientData const&, Gfx::IntPoint const& center, Gfx::IntSize const& size, Vector<Gfx::Path> const& clip_paths = {}) = 0;
+    virtual CommandResult paint_conic_gradient(Gfx::IntRect const& rect, ConicGradientData const&, Gfx::IntPoint const& position, Vector<Gfx::Path> const& clip_paths = {}) = 0;
     virtual CommandResult paint_outer_box_shadow(PaintOuterBoxShadowParams const&) = 0;
     virtual CommandResult paint_outer_box_shadow(PaintOuterBoxShadowParams const&) = 0;
     virtual CommandResult paint_inner_box_shadow(PaintOuterBoxShadowParams const&) = 0;
     virtual CommandResult paint_inner_box_shadow(PaintOuterBoxShadowParams const&) = 0;
     virtual CommandResult paint_text_shadow(int blur_radius, Gfx::IntRect const& shadow_bounding_rect, Gfx::IntRect const& text_rect, Span<Gfx::DrawGlyphOrEmoji const>, Color const&, int fragment_baseline, Gfx::IntPoint const& draw_location) = 0;
     virtual CommandResult paint_text_shadow(int blur_radius, Gfx::IntRect const& shadow_bounding_rect, Gfx::IntRect const& text_rect, Span<Gfx::DrawGlyphOrEmoji const>, Color const&, int fragment_baseline, Gfx::IntPoint const& draw_location) = 0;
@@ -66,7 +66,8 @@ public:
         Gfx::AntiAliasingPainter::CornerRadius const& top_left_radius,
         Gfx::AntiAliasingPainter::CornerRadius const& top_left_radius,
         Gfx::AntiAliasingPainter::CornerRadius const& top_right_radius,
         Gfx::AntiAliasingPainter::CornerRadius const& top_right_radius,
         Gfx::AntiAliasingPainter::CornerRadius const& bottom_left_radius,
         Gfx::AntiAliasingPainter::CornerRadius const& bottom_left_radius,
-        Gfx::AntiAliasingPainter::CornerRadius const& bottom_right_radius)
+        Gfx::AntiAliasingPainter::CornerRadius const& bottom_right_radius,
+        Vector<Gfx::Path> const& clip_paths = {})
         = 0;
         = 0;
     virtual CommandResult fill_path_using_color(Gfx::Path const&, Color const& color, Gfx::Painter::WindingRule, Gfx::FloatPoint const& aa_translation) = 0;
     virtual CommandResult fill_path_using_color(Gfx::Path const&, Color const& color, Gfx::Painter::WindingRule, Gfx::FloatPoint const& aa_translation) = 0;
     virtual CommandResult fill_path_using_paint_style(Gfx::Path const&, Gfx::PaintStyle const& paint_style, Gfx::Painter::WindingRule winding_rule, float opacity, Gfx::FloatPoint const& aa_translation) = 0;
     virtual CommandResult fill_path_using_paint_style(Gfx::Path const&, Gfx::PaintStyle const& paint_style, Gfx::Painter::WindingRule winding_rule, float opacity, Gfx::FloatPoint const& aa_translation) = 0;

+ 6 - 6
Userland/Libraries/LibWeb/Painting/GradientPainting.cpp

@@ -146,19 +146,19 @@ RadialGradientData resolve_radial_gradient_data(Layout::NodeWithStyleAndBoxModel
     return { resolved_color_stops };
     return { resolved_color_stops };
 }
 }
 
 
-void paint_linear_gradient(PaintContext& context, DevicePixelRect const& gradient_rect, LinearGradientData const& data)
+void paint_linear_gradient(PaintContext& context, DevicePixelRect const& gradient_rect, LinearGradientData const& data, Vector<Gfx::Path> const& clip_paths)
 {
 {
-    context.recording_painter().fill_rect_with_linear_gradient(gradient_rect.to_type<int>(), data);
+    context.recording_painter().fill_rect_with_linear_gradient(gradient_rect.to_type<int>(), data, clip_paths);
 }
 }
 
 
-void paint_conic_gradient(PaintContext& context, DevicePixelRect const& gradient_rect, ConicGradientData const& data, DevicePixelPoint position)
+void paint_conic_gradient(PaintContext& context, DevicePixelRect const& gradient_rect, ConicGradientData const& data, DevicePixelPoint position, Vector<Gfx::Path> const& clip_paths)
 {
 {
-    context.recording_painter().fill_rect_with_conic_gradient(gradient_rect.to_type<int>(), data, position.to_type<int>());
+    context.recording_painter().fill_rect_with_conic_gradient(gradient_rect.to_type<int>(), data, position.to_type<int>(), clip_paths);
 }
 }
 
 
-void paint_radial_gradient(PaintContext& context, DevicePixelRect const& gradient_rect, RadialGradientData const& data, DevicePixelPoint center, DevicePixelSize size)
+void paint_radial_gradient(PaintContext& context, DevicePixelRect const& gradient_rect, RadialGradientData const& data, DevicePixelPoint center, DevicePixelSize size, Vector<Gfx::Path> const& clip_paths)
 {
 {
-    context.recording_painter().fill_rect_with_radial_gradient(gradient_rect.to_type<int>(), data, center.to_type<int>(), size.to_type<int>());
+    context.recording_painter().fill_rect_with_radial_gradient(gradient_rect.to_type<int>(), data, center.to_type<int>(), size.to_type<int>(), clip_paths);
 }
 }
 
 
 }
 }

+ 3 - 3
Userland/Libraries/LibWeb/Painting/GradientPainting.h

@@ -20,8 +20,8 @@ LinearGradientData resolve_linear_gradient_data(Layout::NodeWithStyleAndBoxModel
 ConicGradientData resolve_conic_gradient_data(Layout::NodeWithStyleAndBoxModelMetrics const&, CSS::ConicGradientStyleValue const&);
 ConicGradientData resolve_conic_gradient_data(Layout::NodeWithStyleAndBoxModelMetrics const&, CSS::ConicGradientStyleValue const&);
 RadialGradientData resolve_radial_gradient_data(Layout::NodeWithStyleAndBoxModelMetrics const&, CSSPixelSize, CSS::RadialGradientStyleValue const&);
 RadialGradientData resolve_radial_gradient_data(Layout::NodeWithStyleAndBoxModelMetrics const&, CSSPixelSize, CSS::RadialGradientStyleValue const&);
 
 
-void paint_linear_gradient(PaintContext&, DevicePixelRect const&, LinearGradientData const&);
-void paint_conic_gradient(PaintContext&, DevicePixelRect const&, ConicGradientData const&, DevicePixelPoint position);
-void paint_radial_gradient(PaintContext&, DevicePixelRect const&, RadialGradientData const&, DevicePixelPoint position, DevicePixelSize size);
+void paint_linear_gradient(PaintContext&, DevicePixelRect const&, LinearGradientData const&, Vector<Gfx::Path> const& clip_paths = {});
+void paint_conic_gradient(PaintContext&, DevicePixelRect const&, ConicGradientData const&, DevicePixelPoint position, Vector<Gfx::Path> const& clip_paths = {});
+void paint_radial_gradient(PaintContext&, DevicePixelRect const&, RadialGradientData const&, DevicePixelPoint position, DevicePixelSize size, Vector<Gfx::Path> const& clip_paths = {});
 
 
 }
 }

+ 67 - 1
Userland/Libraries/LibWeb/Painting/PaintableBox.cpp

@@ -6,6 +6,7 @@
  */
  */
 
 
 #include <AK/GenericShorthands.h>
 #include <AK/GenericShorthands.h>
+#include <LibGfx/Font/ScaledFont.h>
 #include <LibUnicode/CharacterTypes.h>
 #include <LibUnicode/CharacterTypes.h>
 #include <LibWeb/CSS/SystemColor.h>
 #include <LibWeb/CSS/SystemColor.h>
 #include <LibWeb/DOM/Document.h>
 #include <LibWeb/DOM/Document.h>
@@ -455,7 +456,72 @@ void PaintableBox::paint_background(PaintContext& context) const
     if (computed_values().border_top().width != 0 || computed_values().border_right().width != 0 || computed_values().border_bottom().width != 0 || computed_values().border_left().width != 0)
     if (computed_values().border_top().width != 0 || computed_values().border_right().width != 0 || computed_values().border_bottom().width != 0 || computed_values().border_left().width != 0)
         background_rect = absolute_border_box_rect();
         background_rect = absolute_border_box_rect();
 
 
-    Painting::paint_background(context, layout_box(), background_rect, background_color, computed_values().image_rendering(), background_layers, normalized_border_radii_data());
+    Vector<Gfx::Path> text_clip_paths {};
+    if (background_layers && !background_layers->is_empty() && background_layers->last().clip == CSS::BackgroundBox::Text) {
+        text_clip_paths = compute_text_clip_paths(context);
+    }
+
+    Painting::paint_background(context, layout_box(), background_rect, background_color, computed_values().image_rendering(), background_layers, normalized_border_radii_data(), text_clip_paths);
+}
+
+Vector<Gfx::Path> PaintableBox::compute_text_clip_paths(PaintContext& context) const
+{
+    Vector<Gfx::Path> text_clip_paths;
+    auto add_text_clip_path = [&](PaintableFragment const& fragment) {
+        // Scale to the device pixels.
+        Gfx::Path glyph_run_path;
+        for (auto glyph : fragment.glyph_run().glyphs()) {
+            glyph.visit([&](auto& glyph) {
+                glyph.font = *glyph.font->with_size(glyph.font->point_size() * static_cast<float>(context.device_pixels_per_css_pixel()));
+                glyph.position = glyph.position.scaled(context.device_pixels_per_css_pixel());
+            });
+
+            if (glyph.has<Gfx::DrawGlyph>()) {
+                auto const& draw_glyph = glyph.get<Gfx::DrawGlyph>();
+
+                // Get the path for the glyph.
+                Gfx::Path glyph_path;
+                auto const& scaled_font = static_cast<Gfx::ScaledFont const&>(*draw_glyph.font);
+                auto glyph_id = scaled_font.glyph_id_for_code_point(draw_glyph.code_point);
+                scaled_font.append_glyph_path_to(glyph_path, glyph_id);
+
+                // Transform the path to the fragment's position.
+                // FIXME: Record glyphs and use Painter::draw_glyphs() instead to avoid this duplicated code.
+                auto top_left = draw_glyph.position + Gfx::FloatPoint(scaled_font.glyph_left_bearing(draw_glyph.code_point), 0);
+                auto glyph_position = Gfx::GlyphRasterPosition::get_nearest_fit_for(top_left);
+                auto transform = Gfx::AffineTransform {}.translate(glyph_position.blit_position.to_type<float>());
+                glyph_run_path.append_path(glyph_path.copy_transformed(transform));
+            }
+        }
+
+        // Calculate the baseline start position.
+        auto fragment_absolute_rect = fragment.absolute_rect();
+        auto fragment_absolute_device_rect = context.enclosing_device_rect(fragment_absolute_rect);
+        DevicePixelPoint baseline_start { fragment_absolute_device_rect.x(), fragment_absolute_device_rect.y() + context.rounded_device_pixels(fragment.baseline()) };
+
+        // Add the path to text_clip_paths.
+        auto transform = Gfx::AffineTransform {}.translate(baseline_start.to_type<int>().to_type<float>());
+        text_clip_paths.append(glyph_run_path.copy_transformed(transform));
+    };
+
+    for_each_in_inclusive_subtree([&](auto& paintable) {
+        if (is<PaintableWithLines>(paintable)) {
+            auto const& paintable_lines = static_cast<PaintableWithLines const&>(paintable);
+            for (auto const& fragment : paintable_lines.fragments()) {
+                if (is<Layout::TextNode>(fragment.layout_node()))
+                    add_text_clip_path(fragment);
+            }
+        } else if (is<InlinePaintable>(paintable)) {
+            auto const& inline_paintable = static_cast<InlinePaintable const&>(paintable);
+            for (auto const& fragment : inline_paintable.fragments()) {
+                if (is<Layout::TextNode>(fragment.layout_node()))
+                    add_text_clip_path(fragment);
+            }
+        }
+        return TraversalDecision::Continue;
+    });
+
+    return text_clip_paths;
 }
 }
 
 
 void PaintableBox::paint_box_shadow(PaintContext& context) const
 void PaintableBox::paint_box_shadow(PaintContext& context) const

+ 2 - 0
Userland/Libraries/LibWeb/Painting/PaintableBox.h

@@ -227,6 +227,8 @@ protected:
     virtual CSSPixelRect compute_absolute_paint_rect() const;
     virtual CSSPixelRect compute_absolute_paint_rect() const;
 
 
 private:
 private:
+    Vector<Gfx::Path> compute_text_clip_paths(PaintContext&) const;
+
     [[nodiscard]] virtual bool is_paintable_box() const final { return true; }
     [[nodiscard]] virtual bool is_paintable_box() const final { return true; }
 
 
     enum class ScrollDirection {
     enum class ScrollDirection {

+ 20 - 14
Userland/Libraries/LibWeb/Painting/RecordingPainter.cpp

@@ -34,11 +34,12 @@ void RecordingPainter::blit_corner_clipping(u32 id, Gfx::IntRect border_rect)
     append(BlitCornerClipping { id, border_rect = state().translation.map(border_rect) });
     append(BlitCornerClipping { id, border_rect = state().translation.map(border_rect) });
 }
 }
 
 
-void RecordingPainter::fill_rect(Gfx::IntRect const& rect, Color color)
+void RecordingPainter::fill_rect(Gfx::IntRect const& rect, Color color, Vector<Gfx::Path> const& clip_paths)
 {
 {
     append(FillRect {
     append(FillRect {
         .rect = state().translation.map(rect),
         .rect = state().translation.map(rect),
         .color = color,
         .color = color,
+        .clip_paths = clip_paths,
     });
     });
 }
 }
 
 
@@ -118,29 +119,31 @@ void RecordingPainter::fill_ellipse(Gfx::IntRect const& a_rect, Color color, Gfx
     });
     });
 }
 }
 
 
-void RecordingPainter::fill_rect_with_linear_gradient(Gfx::IntRect const& gradient_rect, LinearGradientData const& data)
+void RecordingPainter::fill_rect_with_linear_gradient(Gfx::IntRect const& gradient_rect, LinearGradientData const& data, Vector<Gfx::Path> const& clip_paths)
 {
 {
     append(PaintLinearGradient {
     append(PaintLinearGradient {
         .gradient_rect = state().translation.map(gradient_rect),
         .gradient_rect = state().translation.map(gradient_rect),
         .linear_gradient_data = data,
         .linear_gradient_data = data,
-    });
+        .clip_paths = clip_paths });
 }
 }
 
 
-void RecordingPainter::fill_rect_with_conic_gradient(Gfx::IntRect const& rect, ConicGradientData const& data, Gfx::IntPoint const& position)
+void RecordingPainter::fill_rect_with_conic_gradient(Gfx::IntRect const& rect, ConicGradientData const& data, Gfx::IntPoint const& position, Vector<Gfx::Path> const& clip_paths)
 {
 {
     append(PaintConicGradient {
     append(PaintConicGradient {
         .rect = state().translation.map(rect),
         .rect = state().translation.map(rect),
         .conic_gradient_data = data,
         .conic_gradient_data = data,
-        .position = position });
+        .position = position,
+        .clip_paths = clip_paths });
 }
 }
 
 
-void RecordingPainter::fill_rect_with_radial_gradient(Gfx::IntRect const& rect, RadialGradientData const& data, Gfx::IntPoint center, Gfx::IntSize size)
+void RecordingPainter::fill_rect_with_radial_gradient(Gfx::IntRect const& rect, RadialGradientData const& data, Gfx::IntPoint center, Gfx::IntSize size, Vector<Gfx::Path> const& clip_paths)
 {
 {
     append(PaintRadialGradient {
     append(PaintRadialGradient {
         .rect = state().translation.map(rect),
         .rect = state().translation.map(rect),
         .radial_gradient_data = data,
         .radial_gradient_data = data,
         .center = center,
         .center = center,
-        .size = size });
+        .size = size,
+        .clip_paths = clip_paths });
 }
 }
 
 
 void RecordingPainter::draw_rect(Gfx::IntRect const& rect, Color color, bool rough)
 void RecordingPainter::draw_rect(Gfx::IntRect const& rect, Color color, bool rough)
@@ -161,13 +164,14 @@ void RecordingPainter::draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bit
     });
     });
 }
 }
 
 
-void RecordingPainter::draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode)
+void RecordingPainter::draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode, Vector<Gfx::Path> const& clip_paths)
 {
 {
     append(DrawScaledImmutableBitmap {
     append(DrawScaledImmutableBitmap {
         .dst_rect = state().translation.map(dst_rect),
         .dst_rect = state().translation.map(dst_rect),
         .bitmap = bitmap,
         .bitmap = bitmap,
         .src_rect = src_rect,
         .src_rect = src_rect,
         .scaling_mode = scaling_mode,
         .scaling_mode = scaling_mode,
+        .clip_paths = clip_paths,
     });
     });
 }
 }
 
 
@@ -327,10 +331,10 @@ void RecordingPainter::paint_text_shadow(int blur_radius, Gfx::IntRect bounding_
         .draw_location = state().translation.map(draw_location) });
         .draw_location = state().translation.map(draw_location) });
 }
 }
 
 
-void RecordingPainter::fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color color, Gfx::AntiAliasingPainter::CornerRadius top_left_radius, Gfx::AntiAliasingPainter::CornerRadius top_right_radius, Gfx::AntiAliasingPainter::CornerRadius bottom_right_radius, Gfx::AntiAliasingPainter::CornerRadius bottom_left_radius)
+void RecordingPainter::fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color color, Gfx::AntiAliasingPainter::CornerRadius top_left_radius, Gfx::AntiAliasingPainter::CornerRadius top_right_radius, Gfx::AntiAliasingPainter::CornerRadius bottom_right_radius, Gfx::AntiAliasingPainter::CornerRadius bottom_left_radius, Vector<Gfx::Path> const& clip_paths)
 {
 {
     if (!top_left_radius && !top_right_radius && !bottom_right_radius && !bottom_left_radius) {
     if (!top_left_radius && !top_right_radius && !bottom_right_radius && !bottom_left_radius) {
-        fill_rect(rect, color);
+        fill_rect(rect, color, clip_paths);
         return;
         return;
     }
     }
 
 
@@ -341,21 +345,23 @@ void RecordingPainter::fill_rect_with_rounded_corners(Gfx::IntRect const& rect,
         .top_right_radius = top_right_radius,
         .top_right_radius = top_right_radius,
         .bottom_left_radius = bottom_left_radius,
         .bottom_left_radius = bottom_left_radius,
         .bottom_right_radius = bottom_right_radius,
         .bottom_right_radius = bottom_right_radius,
+        .clip_paths = clip_paths,
     });
     });
 }
 }
 
 
-void RecordingPainter::fill_rect_with_rounded_corners(Gfx::IntRect const& a_rect, Color color, int radius)
+void RecordingPainter::fill_rect_with_rounded_corners(Gfx::IntRect const& a_rect, Color color, int radius, Vector<Gfx::Path> const& clip_paths)
 {
 {
-    fill_rect_with_rounded_corners(a_rect, color, radius, radius, radius, radius);
+    fill_rect_with_rounded_corners(a_rect, color, radius, radius, radius, radius, clip_paths);
 }
 }
 
 
-void RecordingPainter::fill_rect_with_rounded_corners(Gfx::IntRect const& a_rect, Color color, int top_left_radius, int top_right_radius, int bottom_right_radius, int bottom_left_radius)
+void RecordingPainter::fill_rect_with_rounded_corners(Gfx::IntRect const& a_rect, Color color, int top_left_radius, int top_right_radius, int bottom_right_radius, int bottom_left_radius, Vector<Gfx::Path> const& clip_paths)
 {
 {
     fill_rect_with_rounded_corners(a_rect, color,
     fill_rect_with_rounded_corners(a_rect, color,
         { top_left_radius, top_left_radius },
         { top_left_radius, top_left_radius },
         { top_right_radius, top_right_radius },
         { top_right_radius, top_right_radius },
         { bottom_right_radius, bottom_right_radius },
         { bottom_right_radius, bottom_right_radius },
-        { bottom_left_radius, bottom_left_radius });
+        { bottom_left_radius, bottom_left_radius },
+        clip_paths);
 }
 }
 
 
 void RecordingPainter::draw_triangle_wave(Gfx::IntPoint a_p1, Gfx::IntPoint a_p2, Color color, int amplitude, int thickness = 1)
 void RecordingPainter::draw_triangle_wave(Gfx::IntPoint a_p1, Gfx::IntPoint a_p2, Color color, int amplitude, int thickness = 1)

+ 8 - 8
Userland/Libraries/LibWeb/Painting/RecordingPainter.h

@@ -44,7 +44,7 @@ class RecordingPainter {
     AK_MAKE_NONMOVABLE(RecordingPainter);
     AK_MAKE_NONMOVABLE(RecordingPainter);
 
 
 public:
 public:
-    void fill_rect(Gfx::IntRect const& rect, Color color);
+    void fill_rect(Gfx::IntRect const& rect, Color color, Vector<Gfx::Path> const& clip_paths = {});
 
 
     struct FillPathUsingColorParams {
     struct FillPathUsingColorParams {
         Gfx::Path path;
         Gfx::Path path;
@@ -84,14 +84,14 @@ public:
 
 
     void fill_ellipse(Gfx::IntRect const& a_rect, Color color, Gfx::AntiAliasingPainter::BlendMode blend_mode = Gfx::AntiAliasingPainter::BlendMode::Normal);
     void fill_ellipse(Gfx::IntRect const& a_rect, Color color, Gfx::AntiAliasingPainter::BlendMode blend_mode = Gfx::AntiAliasingPainter::BlendMode::Normal);
 
 
-    void fill_rect_with_linear_gradient(Gfx::IntRect const& gradient_rect, LinearGradientData const& data);
-    void fill_rect_with_conic_gradient(Gfx::IntRect const& rect, ConicGradientData const& data, Gfx::IntPoint const& position);
-    void fill_rect_with_radial_gradient(Gfx::IntRect const& rect, RadialGradientData const& data, Gfx::IntPoint center, Gfx::IntSize size);
+    void fill_rect_with_linear_gradient(Gfx::IntRect const& gradient_rect, LinearGradientData const& data, Vector<Gfx::Path> const& clip_paths = {});
+    void fill_rect_with_conic_gradient(Gfx::IntRect const& rect, ConicGradientData const& data, Gfx::IntPoint const& position, Vector<Gfx::Path> const& clip_paths = {});
+    void fill_rect_with_radial_gradient(Gfx::IntRect const& rect, RadialGradientData const& data, Gfx::IntPoint center, Gfx::IntSize size, Vector<Gfx::Path> const& clip_paths = {});
 
 
     void draw_rect(Gfx::IntRect const& rect, Color color, bool rough = false);
     void draw_rect(Gfx::IntRect const& rect, Color color, bool rough = false);
 
 
     void draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode = Gfx::Painter::ScalingMode::NearestNeighbor);
     void draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode = Gfx::Painter::ScalingMode::NearestNeighbor);
-    void draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode = Gfx::Painter::ScalingMode::NearestNeighbor);
+    void draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode = Gfx::Painter::ScalingMode::NearestNeighbor, Vector<Gfx::Path> const& clip_paths = {});
 
 
     void draw_line(Gfx::IntPoint from, Gfx::IntPoint to, Color color, int thickness = 1, Gfx::Painter::LineStyle style = Gfx::Painter::LineStyle::Solid, Color alternate_color = Color::Transparent);
     void draw_line(Gfx::IntPoint from, Gfx::IntPoint to, Color color, int thickness = 1, Gfx::Painter::LineStyle style = Gfx::Painter::LineStyle::Solid, Color alternate_color = Color::Transparent);
 
 
@@ -137,9 +137,9 @@ public:
     void paint_inner_box_shadow_params(PaintOuterBoxShadowParams params);
     void paint_inner_box_shadow_params(PaintOuterBoxShadowParams params);
     void paint_text_shadow(int blur_radius, Gfx::IntRect bounding_rect, Gfx::IntRect text_rect, Span<Gfx::DrawGlyphOrEmoji const> glyph_run, Color color, int fragment_baseline, Gfx::IntPoint draw_location);
     void paint_text_shadow(int blur_radius, Gfx::IntRect bounding_rect, Gfx::IntRect text_rect, Span<Gfx::DrawGlyphOrEmoji const> glyph_run, Color color, int fragment_baseline, Gfx::IntPoint draw_location);
 
 
-    void fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color color, Gfx::AntiAliasingPainter::CornerRadius top_left_radius, Gfx::AntiAliasingPainter::CornerRadius top_right_radius, Gfx::AntiAliasingPainter::CornerRadius bottom_right_radius, Gfx::AntiAliasingPainter::CornerRadius bottom_left_radius);
-    void fill_rect_with_rounded_corners(Gfx::IntRect const& a_rect, Color color, int radius);
-    void fill_rect_with_rounded_corners(Gfx::IntRect const& a_rect, Color color, int top_left_radius, int top_right_radius, int bottom_right_radius, int bottom_left_radius);
+    void fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color color, Gfx::AntiAliasingPainter::CornerRadius top_left_radius, Gfx::AntiAliasingPainter::CornerRadius top_right_radius, Gfx::AntiAliasingPainter::CornerRadius bottom_right_radius, Gfx::AntiAliasingPainter::CornerRadius bottom_left_radius, Vector<Gfx::Path> const& clip_paths = {});
+    void fill_rect_with_rounded_corners(Gfx::IntRect const& a_rect, Color color, int radius, Vector<Gfx::Path> const& clip_paths = {});
+    void fill_rect_with_rounded_corners(Gfx::IntRect const& a_rect, Color color, int top_left_radius, int top_right_radius, int bottom_right_radius, int bottom_left_radius, Vector<Gfx::Path> const& clip_paths = {});
 
 
     void draw_triangle_wave(Gfx::IntPoint a_p1, Gfx::IntPoint a_p2, Color color, int amplitude, int thickness);
     void draw_triangle_wave(Gfx::IntPoint a_p1, Gfx::IntPoint a_p2, Color color, int amplitude, int thickness);