浏览代码

LibWeb: Save "background-clip: text" mask as a nested display list

Before this change, "background-clip: text" was implemented by saving a
Vector<Gfx::Path> of all glyphs needed to paint a mask for the
background. The issue with this approach was that once glyphs were
extracted into vector paths, the glyph rasterization cache could no
longer be utilized.

With this change, all text required for mask painting is saved in a
nested display list and rasterized as a regular text.
Aliaksandr Kalenik 11 月之前
父节点
当前提交
67d68eac64

二进制
Tests/LibWeb/Screenshot/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, Vector<Gfx::Path> const& clip_paths = {}) const = 0;
+    virtual void paint(PaintContext& context, DevicePixelRect const& dest_rect, ImageRendering, RefPtr<Painting::DisplayList> text_clip = {}) 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,12 +42,12 @@ 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, Vector<Gfx::Path> const& clip_paths) const
+void ConicGradientStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering, RefPtr<Painting::DisplayList> text_clip) const
 {
 {
     VERIFY(m_resolved.has_value());
     VERIFY(m_resolved.has_value());
     auto destination_rect = dest_rect.to_type<int>();
     auto destination_rect = dest_rect.to_type<int>();
     auto position = context.rounded_device_point(m_resolved->position).to_type<int>();
     auto position = context.rounded_device_point(m_resolved->position).to_type<int>();
-    context.display_list_recorder().fill_rect_with_conic_gradient(destination_rect, m_resolved->data, position, clip_paths);
+    context.display_list_recorder().fill_rect_with_conic_gradient(destination_rect, m_resolved->data, position, text_clip);
 }
 }
 
 
 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, Vector<Gfx::Path> const& clip_paths = {}) const override;
+    void paint(PaintContext&, DevicePixelRect const& dest_rect, CSS::ImageRendering, RefPtr<Painting::DisplayList> text_clip = {}) 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

@@ -133,11 +133,11 @@ Optional<CSSPixelFraction> ImageStyleValue::natural_aspect_ratio() const
     return {};
     return {};
 }
 }
 
 
-void ImageStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering, Vector<Gfx::Path> const& clip_paths) const
+void ImageStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering, RefPtr<Painting::DisplayList> text_clip) 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.display_list_recorder().draw_scaled_immutable_bitmap(dest_rect.to_type<int>(), *b, b->rect(), scaling_mode, clip_paths);
+        context.display_list_recorder().draw_scaled_immutable_bitmap(dest_rect.to_type<int>(), *b, b->rect(), scaling_mode, text_clip);
     }
     }
 }
 }
 
 

+ 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, Vector<Gfx::Path> const& clip_paths = {}) const override;
+    void paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering, RefPtr<Painting::DisplayList> 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;
     Gfx::ImmutableBitmap const* current_frame_bitmap(DevicePixelRect const& dest_rect) const;
     Gfx::ImmutableBitmap const* current_frame_bitmap(DevicePixelRect const& dest_rect) const;

+ 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, Vector<Gfx::Path> const& clip_paths) const
+void LinearGradientStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering, RefPtr<Painting::DisplayList> text_clip) const
 {
 {
     VERIFY(m_resolved.has_value());
     VERIFY(m_resolved.has_value());
-    context.display_list_recorder().fill_rect_with_linear_gradient(dest_rect.to_type<int>(), m_resolved->data, clip_paths);
+    context.display_list_recorder().fill_rect_with_linear_gradient(dest_rect.to_type<int>(), m_resolved->data, text_clip);
 }
 }
 
 
 }
 }

+ 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, Vector<Gfx::Path> const& clip_paths = {}) const override;
+    void paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering, RefPtr<Painting::DisplayList> text_clip = {}) 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)

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

@@ -207,12 +207,12 @@ 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, Vector<Gfx::Path> const& clip_paths) const
+void RadialGradientStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering, RefPtr<Painting::DisplayList> text_clip) const
 {
 {
     VERIFY(m_resolved.has_value());
     VERIFY(m_resolved.has_value());
     auto center = context.rounded_device_point(m_resolved->center).to_type<int>();
     auto center = context.rounded_device_point(m_resolved->center).to_type<int>();
     auto size = context.rounded_device_size(m_resolved->gradient_size).to_type<int>();
     auto size = context.rounded_device_size(m_resolved->gradient_size).to_type<int>();
-    context.display_list_recorder().fill_rect_with_radial_gradient(dest_rect.to_type<int>(), m_resolved->data, center, size, clip_paths);
+    context.display_list_recorder().fill_rect_with_radial_gradient(dest_rect.to_type<int>(), m_resolved->data, center, size, text_clip);
 }
 }
 
 
 }
 }

+ 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, Vector<Gfx::Path> const& clip_paths = {}) const override;
+    void paint(PaintContext&, DevicePixelRect const& dest_rect, CSS::ImageRendering, RefPtr<Painting::DisplayList> text_clip = {}) const override;
 
 
     virtual bool equals(StyleValue const& other) const override;
     virtual bool equals(StyleValue const& other) const override;
 
 

+ 1 - 0
Userland/Libraries/LibWeb/Forward.h

@@ -25,6 +25,7 @@ class XMLDocumentBuilder;
 
 
 namespace Web::Painting {
 namespace Web::Painting {
 class BackingStore;
 class BackingStore;
+class DisplayList;
 class DisplayListRecorder;
 class DisplayListRecorder;
 class SVGGradientPaintStyle;
 class SVGGradientPaintStyle;
 using PaintStyle = RefPtr<SVGGradientPaintStyle>;
 using PaintStyle = RefPtr<SVGGradientPaintStyle>;

+ 12 - 38
Userland/Libraries/LibWeb/Painting/BackgroundPainting.cpp

@@ -58,47 +58,21 @@ static CSSPixelSize run_default_sizing_algorithm(
     return default_size;
     return default_size;
 }
 }
 
 
-static Vector<Gfx::Path> compute_text_clip_paths(PaintContext& context, Paintable const& paintable)
+static RefPtr<DisplayList> compute_text_clip_paths(PaintContext& context, Paintable const& paintable)
 {
 {
-    Vector<Gfx::Path> text_clip_paths;
+    auto text_clip_paths = DisplayList::create();
+    DisplayListRecorder display_list_recorder(*text_clip_paths);
     auto add_text_clip_path = [&](PaintableFragment const& fragment) {
     auto add_text_clip_path = [&](PaintableFragment const& fragment) {
         auto glyph_run = fragment.glyph_run();
         auto glyph_run = fragment.glyph_run();
         if (!glyph_run || glyph_run->glyphs().is_empty())
         if (!glyph_run || glyph_run->glyphs().is_empty())
             return;
             return;
-        // Scale to the device pixels.
-        Gfx::Path glyph_run_path;
-        auto const& font = fragment.glyph_run()->font();
-        auto scaled_font = font.with_size(font.point_size() * static_cast<float>(context.device_pixels_per_css_pixel()));
-        for (auto glyph : fragment.glyph_run()->glyphs()) {
-            glyph.visit([&](auto& glyph) {
-                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 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_rect = fragment.absolute_rect();
         auto fragment_absolute_device_rect = context.enclosing_device_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));
+        DevicePixelPoint baseline_start { fragment_absolute_device_rect.x(), fragment_absolute_device_rect.y() + context.rounded_device_pixels(fragment.baseline()) };
+        auto scale = context.device_pixels_per_css_pixel();
+        display_list_recorder.draw_text_run(baseline_start.to_type<int>(), *glyph_run, Gfx::Color::Black, fragment_absolute_device_rect.to_type<int>(), scale);
     };
     };
 
 
     paintable.for_each_in_inclusive_subtree([&](auto& paintable) {
     paintable.for_each_in_inclusive_subtree([&](auto& paintable) {
@@ -124,9 +98,9 @@ static Vector<Gfx::Path> compute_text_clip_paths(PaintContext& context, Paintabl
 // 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> clip_paths {};
+    RefPtr<DisplayList> text_clip;
     if (background_layers && !background_layers->is_empty() && background_layers->last().clip == CSS::BackgroundBox::Text) {
     if (background_layers && !background_layers->is_empty() && background_layers->last().clip == CSS::BackgroundBox::Text) {
-        clip_paths = compute_text_clip_paths(context, *layout_node.paintable());
+        text_clip = compute_text_clip_paths(context, *layout_node.paintable());
     }
     }
 
 
     auto& display_list_recorder = context.display_list_recorder();
     auto& display_list_recorder = context.display_list_recorder();
@@ -191,7 +165,7 @@ void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMet
         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);
+        text_clip);
 
 
     if (!has_paintable_layers)
     if (!has_paintable_layers)
         return;
         return;
@@ -459,17 +433,17 @@ void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMet
                     fill_rect = fill_rect->united(image_device_rect);
                     fill_rect = fill_rect->united(image_device_rect);
                 }
                 }
             });
             });
-            display_list_recorder.fill_rect(fill_rect->to_type<int>(), color.value(), clip_paths);
+            display_list_recorder.fill_rect(fill_rect->to_type<int>(), color.value(), text_clip);
         } else if (is<CSS::ImageStyleValue>(image) && repeat_x && repeat_y && !repeat_x_has_gap && !repeat_y_has_gap) {
         } else if (is<CSS::ImageStyleValue>(image) && repeat_x && repeat_y && !repeat_x_has_gap && !repeat_y_has_gap) {
             // Use a dedicated painting command for repeated images instead of recording a separate command for each instance
             // Use a dedicated painting command for repeated images instead of recording a separate command for each instance
             // of a repeated background, so the painter has the opportunity to optimize the painting of repeated images.
             // of a repeated background, so the painter has the opportunity to optimize the painting of repeated images.
             auto dest_rect = context.rounded_device_rect(image_rect);
             auto dest_rect = context.rounded_device_rect(image_rect);
             auto const* bitmap = static_cast<CSS::ImageStyleValue const&>(image).current_frame_bitmap(dest_rect);
             auto const* bitmap = static_cast<CSS::ImageStyleValue const&>(image).current_frame_bitmap(dest_rect);
             auto scaling_mode = to_gfx_scaling_mode(image_rendering, bitmap->rect(), dest_rect.to_type<int>());
             auto scaling_mode = to_gfx_scaling_mode(image_rendering, bitmap->rect(), dest_rect.to_type<int>());
-            context.display_list_recorder().draw_repeated_immutable_bitmap(dest_rect.to_type<int>(), *bitmap, scaling_mode, { .x = repeat_x, .y = repeat_y }, clip_paths);
+            context.display_list_recorder().draw_repeated_immutable_bitmap(dest_rect.to_type<int>(), clip_rect.to_type<int>(), *bitmap, scaling_mode, { .x = repeat_x, .y = repeat_y }, text_clip);
         } 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, clip_paths);
+                image.paint(context, image_device_rect, image_rendering, text_clip);
             });
             });
         }
         }
     }
     }

+ 10 - 7
Userland/Libraries/LibWeb/Painting/Command.h

@@ -33,6 +33,8 @@
 
 
 namespace Web::Painting {
 namespace Web::Painting {
 
 
+class DisplayList;
+
 struct DrawGlyphRun {
 struct DrawGlyphRun {
     NonnullRefPtr<Gfx::GlyphRun> glyph_run;
     NonnullRefPtr<Gfx::GlyphRun> glyph_run;
     Color color;
     Color color;
@@ -48,7 +50,7 @@ struct DrawGlyphRun {
 struct FillRect {
 struct FillRect {
     Gfx::IntRect rect;
     Gfx::IntRect rect;
     Color color;
     Color color;
-    Vector<Gfx::Path> clip_paths;
+    RefPtr<DisplayList> text_clip;
 
 
     [[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); }
@@ -69,7 +71,7 @@ struct DrawScaledImmutableBitmap {
     NonnullRefPtr<Gfx::ImmutableBitmap> bitmap;
     NonnullRefPtr<Gfx::ImmutableBitmap> bitmap;
     Gfx::IntRect src_rect;
     Gfx::IntRect src_rect;
     Gfx::ScalingMode scaling_mode;
     Gfx::ScalingMode scaling_mode;
-    Vector<Gfx::Path> clip_paths;
+    RefPtr<DisplayList> text_clip;
 
 
     [[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); }
@@ -82,10 +84,11 @@ struct DrawRepeatedImmutableBitmap {
     };
     };
 
 
     Gfx::IntRect dst_rect;
     Gfx::IntRect dst_rect;
+    Gfx::IntRect clip_rect;
     NonnullRefPtr<Gfx::ImmutableBitmap> bitmap;
     NonnullRefPtr<Gfx::ImmutableBitmap> bitmap;
     Gfx::ScalingMode scaling_mode;
     Gfx::ScalingMode scaling_mode;
     Repeat repeat;
     Repeat repeat;
-    Vector<Gfx::Path> clip_paths;
+    RefPtr<DisplayList> text_clip;
 
 
     void translate_by(Gfx::IntPoint const& offset) { dst_rect.translate_by(offset); }
     void translate_by(Gfx::IntPoint const& offset) { dst_rect.translate_by(offset); }
 };
 };
@@ -129,7 +132,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;
+    RefPtr<DisplayList> text_clip;
 
 
     [[nodiscard]] Gfx::IntRect bounding_rect() const { return gradient_rect; }
     [[nodiscard]] Gfx::IntRect bounding_rect() const { return gradient_rect; }
 
 
@@ -170,7 +173,7 @@ struct FillRectWithRoundedCorners {
     Gfx::IntRect rect;
     Gfx::IntRect rect;
     Color color;
     Color color;
     CornerRadii corner_radii;
     CornerRadii corner_radii;
-    Vector<Gfx::Path> clip_paths;
+    RefPtr<DisplayList> text_clip;
 
 
     [[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); }
@@ -310,7 +313,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;
+    RefPtr<DisplayList> text_clip;
 
 
     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
 
 
@@ -321,7 +324,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;
+    RefPtr<DisplayList> text_clip;
 
 
     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
     [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
 
 

+ 37 - 18
Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp

@@ -62,6 +62,11 @@ public:
         m_surface->readPixels(pixmap, 0, 0);
         m_surface->readPixels(pixmap, 0, 0);
     }
     }
 
 
+    sk_sp<SkSurface> make_surface(int width, int height)
+    {
+        return m_surface->makeSurface(width, height);
+    }
+
 private:
 private:
     sk_sp<SkSurface> m_surface;
     sk_sp<SkSurface> m_surface;
 };
 };
@@ -368,17 +373,13 @@ static SkSamplingOptions to_skia_sampling_options(Gfx::ScalingMode scaling_mode)
     }
     }
 }
 }
 
 
-#define APPLY_PATH_CLIP_IF_NEEDED                     \
-    ScopeGuard restore_path_clip { [&] {              \
-        if (command.clip_paths.size() > 0)            \
-            surface().canvas().restore();             \
-    } };                                              \
-    if (command.clip_paths.size() > 0) {              \
-        surface().canvas().save();                    \
-        SkPath clip_path;                             \
-        for (auto const& path : command.clip_paths)   \
-            clip_path.addPath(to_skia_path(path));    \
-        surface().canvas().clipPath(clip_path, true); \
+#define APPLY_TEXT_CLIP_IF_NEEDED(MASK_RECT)                    \
+    ScopeGuard const restore { [&] {                            \
+        if (command.text_clip)                                  \
+            surface().canvas().restore();                       \
+    } };                                                        \
+    if (command.text_clip) {                                    \
+        apply_mask_painted_from(*command.text_clip, MASK_RECT); \
     }
     }
 
 
 DisplayListPlayerSkia::SkiaSurface& DisplayListPlayerSkia::surface() const
 DisplayListPlayerSkia::SkiaSurface& DisplayListPlayerSkia::surface() const
@@ -422,7 +423,7 @@ void DisplayListPlayerSkia::draw_glyph_run(DrawGlyphRun const& command)
 
 
 void DisplayListPlayerSkia::fill_rect(FillRect const& command)
 void DisplayListPlayerSkia::fill_rect(FillRect const& command)
 {
 {
-    APPLY_PATH_CLIP_IF_NEEDED
+    APPLY_TEXT_CLIP_IF_NEEDED(command.rect)
 
 
     auto const& rect = command.rect;
     auto const& rect = command.rect;
     auto& canvas = surface().canvas();
     auto& canvas = surface().canvas();
@@ -444,7 +445,7 @@ void DisplayListPlayerSkia::draw_scaled_bitmap(DrawScaledBitmap const& command)
 
 
 void DisplayListPlayerSkia::draw_scaled_immutable_bitmap(DrawScaledImmutableBitmap const& command)
 void DisplayListPlayerSkia::draw_scaled_immutable_bitmap(DrawScaledImmutableBitmap const& command)
 {
 {
-    APPLY_PATH_CLIP_IF_NEEDED
+    APPLY_TEXT_CLIP_IF_NEEDED(command.dst_rect)
 
 
     auto src_rect = to_skia_rect(command.src_rect);
     auto src_rect = to_skia_rect(command.src_rect);
     auto dst_rect = to_skia_rect(command.dst_rect);
     auto dst_rect = to_skia_rect(command.dst_rect);
@@ -457,7 +458,7 @@ void DisplayListPlayerSkia::draw_scaled_immutable_bitmap(DrawScaledImmutableBitm
 
 
 void DisplayListPlayerSkia::draw_repeated_immutable_bitmap(DrawRepeatedImmutableBitmap const& command)
 void DisplayListPlayerSkia::draw_repeated_immutable_bitmap(DrawRepeatedImmutableBitmap const& command)
 {
 {
-    APPLY_PATH_CLIP_IF_NEEDED
+    APPLY_TEXT_CLIP_IF_NEEDED(command.clip_rect)
 
 
     auto bitmap = to_skia_bitmap(command.bitmap->bitmap());
     auto bitmap = to_skia_bitmap(command.bitmap->bitmap());
     auto image = SkImages::RasterFromBitmap(bitmap);
     auto image = SkImages::RasterFromBitmap(bitmap);
@@ -655,7 +656,7 @@ static ColorStopList expand_repeat_length(ColorStopList const& color_stop_list,
 
 
 void DisplayListPlayerSkia::paint_linear_gradient(PaintLinearGradient const& command)
 void DisplayListPlayerSkia::paint_linear_gradient(PaintLinearGradient const& command)
 {
 {
-    APPLY_PATH_CLIP_IF_NEEDED
+    APPLY_TEXT_CLIP_IF_NEEDED(command.gradient_rect)
 
 
     auto const& linear_gradient_data = command.linear_gradient_data;
     auto const& linear_gradient_data = command.linear_gradient_data;
     auto color_stop_list = linear_gradient_data.color_stops.list;
     auto color_stop_list = linear_gradient_data.color_stops.list;
@@ -833,7 +834,7 @@ void DisplayListPlayerSkia::paint_text_shadow(PaintTextShadow const& command)
 
 
 void DisplayListPlayerSkia::fill_rect_with_rounded_corners(FillRectWithRoundedCorners const& command)
 void DisplayListPlayerSkia::fill_rect_with_rounded_corners(FillRectWithRoundedCorners const& command)
 {
 {
-    APPLY_PATH_CLIP_IF_NEEDED
+    APPLY_TEXT_CLIP_IF_NEEDED(command.rect)
 
 
     auto const& rect = command.rect;
     auto const& rect = command.rect;
 
 
@@ -1200,7 +1201,7 @@ void DisplayListPlayerSkia::draw_rect(DrawRect const& command)
 
 
 void DisplayListPlayerSkia::paint_radial_gradient(PaintRadialGradient const& command)
 void DisplayListPlayerSkia::paint_radial_gradient(PaintRadialGradient const& command)
 {
 {
-    APPLY_PATH_CLIP_IF_NEEDED
+    APPLY_TEXT_CLIP_IF_NEEDED(command.rect)
 
 
     auto const& radial_gradient_data = command.radial_gradient_data;
     auto const& radial_gradient_data = command.radial_gradient_data;
 
 
@@ -1249,7 +1250,7 @@ void DisplayListPlayerSkia::paint_radial_gradient(PaintRadialGradient const& com
 
 
 void DisplayListPlayerSkia::paint_conic_gradient(PaintConicGradient const& command)
 void DisplayListPlayerSkia::paint_conic_gradient(PaintConicGradient const& command)
 {
 {
-    APPLY_PATH_CLIP_IF_NEEDED
+    APPLY_TEXT_CLIP_IF_NEEDED(command.rect)
 
 
     auto const& conic_gradient_data = command.conic_gradient_data;
     auto const& conic_gradient_data = command.conic_gradient_data;
 
 
@@ -1312,4 +1313,22 @@ bool DisplayListPlayerSkia::would_be_fully_clipped_by_painter(Gfx::IntRect rect)
     return surface().canvas().quickReject(to_skia_rect(rect));
     return surface().canvas().quickReject(to_skia_rect(rect));
 }
 }
 
 
+void DisplayListPlayerSkia::apply_mask_painted_from(DisplayList& display_list, Gfx::IntRect rect)
+{
+    auto mask_surface = m_surface->make_surface(rect.width(), rect.height());
+
+    auto previous_surface = move(m_surface);
+    m_surface = make<SkiaSurface>(mask_surface);
+    surface().canvas().translate(-rect.x(), -rect.y());
+    execute(display_list);
+    m_surface = move(previous_surface);
+
+    SkMatrix mask_matrix;
+    mask_matrix.setTranslate(rect.x(), rect.y());
+    auto image = mask_surface->makeImageSnapshot();
+    auto shader = image->makeShader(SkSamplingOptions(), mask_matrix);
+    surface().canvas().save();
+    surface().canvas().clipShader(shader);
+}
+
 }
 }

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

@@ -80,6 +80,8 @@ private:
 
 
     bool would_be_fully_clipped_by_painter(Gfx::IntRect) const override;
     bool would_be_fully_clipped_by_painter(Gfx::IntRect) const override;
 
 
+    void apply_mask_painted_from(DisplayList&, Gfx::IntRect);
+
     class SkiaSurface;
     class SkiaSurface;
     SkiaSurface& surface() const;
     SkiaSurface& surface() const;
 
 

+ 18 - 17
Userland/Libraries/LibWeb/Painting/DisplayListRecorder.cpp

@@ -42,14 +42,14 @@ void DisplayListRecorder::blit_corner_clipping(u32 id)
     append(BlitCornerClipping { id, state().translation.map(clip_state.rect) });
     append(BlitCornerClipping { id, state().translation.map(clip_state.rect) });
 }
 }
 
 
-void DisplayListRecorder::fill_rect(Gfx::IntRect const& rect, Color color, Vector<Gfx::Path> const& clip_paths)
+void DisplayListRecorder::fill_rect(Gfx::IntRect const& rect, Color color, RefPtr<DisplayList> text_clip)
 {
 {
     if (rect.is_empty())
     if (rect.is_empty())
         return;
         return;
     append(FillRect {
     append(FillRect {
         .rect = state().translation.map(rect),
         .rect = state().translation.map(rect),
         .color = color,
         .color = color,
-        .clip_paths = clip_paths,
+        .text_clip = move(text_clip),
     });
     });
 }
 }
 
 
@@ -140,17 +140,17 @@ void DisplayListRecorder::fill_ellipse(Gfx::IntRect const& a_rect, Color color)
     });
     });
 }
 }
 
 
-void DisplayListRecorder::fill_rect_with_linear_gradient(Gfx::IntRect const& gradient_rect, LinearGradientData const& data, Vector<Gfx::Path> const& clip_paths)
+void DisplayListRecorder::fill_rect_with_linear_gradient(Gfx::IntRect const& gradient_rect, LinearGradientData const& data, RefPtr<DisplayList> text_clip)
 {
 {
     if (gradient_rect.is_empty())
     if (gradient_rect.is_empty())
         return;
         return;
     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 });
+        .text_clip = text_clip });
 }
 }
 
 
-void DisplayListRecorder::fill_rect_with_conic_gradient(Gfx::IntRect const& rect, ConicGradientData const& data, Gfx::IntPoint const& position, Vector<Gfx::Path> const& clip_paths)
+void DisplayListRecorder::fill_rect_with_conic_gradient(Gfx::IntRect const& rect, ConicGradientData const& data, Gfx::IntPoint const& position, RefPtr<DisplayList> text_clip)
 {
 {
     if (rect.is_empty())
     if (rect.is_empty())
         return;
         return;
@@ -158,10 +158,10 @@ void DisplayListRecorder::fill_rect_with_conic_gradient(Gfx::IntRect const& rect
         .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 });
+        .text_clip = text_clip });
 }
 }
 
 
-void DisplayListRecorder::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 DisplayListRecorder::fill_rect_with_radial_gradient(Gfx::IntRect const& rect, RadialGradientData const& data, Gfx::IntPoint center, Gfx::IntSize size, RefPtr<DisplayList> text_clip)
 {
 {
     if (rect.is_empty())
     if (rect.is_empty())
         return;
         return;
@@ -170,7 +170,7 @@ void DisplayListRecorder::fill_rect_with_radial_gradient(Gfx::IntRect const& rec
         .radial_gradient_data = data,
         .radial_gradient_data = data,
         .center = center,
         .center = center,
         .size = size,
         .size = size,
-        .clip_paths = clip_paths });
+        .text_clip = text_clip });
 }
 }
 
 
 void DisplayListRecorder::draw_rect(Gfx::IntRect const& rect, Color color, bool rough)
 void DisplayListRecorder::draw_rect(Gfx::IntRect const& rect, Color color, bool rough)
@@ -195,7 +195,7 @@ void DisplayListRecorder::draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::
     });
     });
 }
 }
 
 
-void DisplayListRecorder::draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode scaling_mode, Vector<Gfx::Path> const& clip_paths)
+void DisplayListRecorder::draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode scaling_mode, RefPtr<DisplayList> text_clip)
 {
 {
     if (dst_rect.is_empty())
     if (dst_rect.is_empty())
         return;
         return;
@@ -204,18 +204,19 @@ void DisplayListRecorder::draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_r
         .bitmap = bitmap,
         .bitmap = bitmap,
         .src_rect = src_rect,
         .src_rect = src_rect,
         .scaling_mode = scaling_mode,
         .scaling_mode = scaling_mode,
-        .clip_paths = clip_paths,
+        .text_clip = move(text_clip),
     });
     });
 }
 }
 
 
-void DisplayListRecorder::draw_repeated_immutable_bitmap(Gfx::IntRect dst_rect, NonnullRefPtr<Gfx::ImmutableBitmap> bitmap, Gfx::ScalingMode scaling_mode, DrawRepeatedImmutableBitmap::Repeat repeat, Vector<Gfx::Path> const& clip_paths)
+void DisplayListRecorder::draw_repeated_immutable_bitmap(Gfx::IntRect dst_rect, Gfx::IntRect clip_rect, NonnullRefPtr<Gfx::ImmutableBitmap> bitmap, Gfx::ScalingMode scaling_mode, DrawRepeatedImmutableBitmap::Repeat repeat, RefPtr<DisplayList> text_clip)
 {
 {
     append(DrawRepeatedImmutableBitmap {
     append(DrawRepeatedImmutableBitmap {
         .dst_rect = dst_rect,
         .dst_rect = dst_rect,
+        .clip_rect = clip_rect,
         .bitmap = move(bitmap),
         .bitmap = move(bitmap),
         .scaling_mode = scaling_mode,
         .scaling_mode = scaling_mode,
         .repeat = repeat,
         .repeat = repeat,
-        .clip_paths = clip_paths,
+        .text_clip = move(text_clip),
     });
     });
 }
 }
 
 
@@ -363,7 +364,7 @@ void DisplayListRecorder::paint_text_shadow(int blur_radius, Gfx::IntRect boundi
         .draw_location = state().translation.map(draw_location) });
         .draw_location = state().translation.map(draw_location) });
 }
 }
 
 
-void DisplayListRecorder::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 DisplayListRecorder::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, RefPtr<DisplayList> clip_paths)
 {
 {
     if (rect.is_empty())
     if (rect.is_empty())
         return;
         return;
@@ -382,18 +383,18 @@ void DisplayListRecorder::fill_rect_with_rounded_corners(Gfx::IntRect const& rec
             .bottom_right = bottom_right_radius,
             .bottom_right = bottom_right_radius,
             .bottom_left = bottom_left_radius,
             .bottom_left = bottom_left_radius,
         },
         },
-        .clip_paths = clip_paths,
+        .text_clip = clip_paths,
     });
     });
 }
 }
 
 
-void DisplayListRecorder::fill_rect_with_rounded_corners(Gfx::IntRect const& a_rect, Color color, int radius, Vector<Gfx::Path> const& clip_paths)
+void DisplayListRecorder::fill_rect_with_rounded_corners(Gfx::IntRect const& a_rect, Color color, int radius, RefPtr<DisplayList> clip_paths)
 {
 {
     if (a_rect.is_empty())
     if (a_rect.is_empty())
         return;
         return;
-    fill_rect_with_rounded_corners(a_rect, color, radius, radius, radius, radius, clip_paths);
+    fill_rect_with_rounded_corners(a_rect, color, radius, radius, radius, radius, move(clip_paths));
 }
 }
 
 
-void DisplayListRecorder::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 DisplayListRecorder::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, RefPtr<DisplayList> clip_paths)
 {
 {
     if (a_rect.is_empty())
     if (a_rect.is_empty())
         return;
         return;

+ 9 - 9
Userland/Libraries/LibWeb/Painting/DisplayListRecorder.h

@@ -40,7 +40,7 @@ class DisplayListRecorder {
     AK_MAKE_NONMOVABLE(DisplayListRecorder);
     AK_MAKE_NONMOVABLE(DisplayListRecorder);
 
 
 public:
 public:
-    void fill_rect(Gfx::IntRect const& rect, Color color, Vector<Gfx::Path> const& clip_paths = {});
+    void fill_rect(Gfx::IntRect const& rect, Color color, RefPtr<DisplayList> text_clip = {});
 
 
     struct FillPathUsingColorParams {
     struct FillPathUsingColorParams {
         Gfx::Path path;
         Gfx::Path path;
@@ -80,16 +80,16 @@ public:
 
 
     void fill_ellipse(Gfx::IntRect const& a_rect, Color color);
     void fill_ellipse(Gfx::IntRect const& a_rect, Color color);
 
 
-    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 fill_rect_with_linear_gradient(Gfx::IntRect const& gradient_rect, LinearGradientData const& data, RefPtr<DisplayList> text_clip = {});
+    void fill_rect_with_conic_gradient(Gfx::IntRect const& rect, ConicGradientData const& data, Gfx::IntPoint const& position, RefPtr<DisplayList> text_clip = {});
+    void fill_rect_with_radial_gradient(Gfx::IntRect const& rect, RadialGradientData const& data, Gfx::IntPoint center, Gfx::IntSize size, RefPtr<DisplayList> text_clip = {});
 
 
     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::ScalingMode scaling_mode = Gfx::ScalingMode::NearestNeighbor);
     void draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode scaling_mode = Gfx::ScalingMode::NearestNeighbor);
-    void draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode scaling_mode = Gfx::ScalingMode::NearestNeighbor, Vector<Gfx::Path> const& clip_paths = {});
+    void draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode scaling_mode = Gfx::ScalingMode::NearestNeighbor, RefPtr<DisplayList> text_clip = {});
 
 
-    void draw_repeated_immutable_bitmap(Gfx::IntRect dst_rect, NonnullRefPtr<Gfx::ImmutableBitmap> bitmap, Gfx::ScalingMode scaling_mode, DrawRepeatedImmutableBitmap::Repeat, Vector<Gfx::Path> const& clip_paths = {});
+    void draw_repeated_immutable_bitmap(Gfx::IntRect dst_rect, Gfx::IntRect clip_rect, NonnullRefPtr<Gfx::ImmutableBitmap> bitmap, Gfx::ScalingMode scaling_mode, DrawRepeatedImmutableBitmap::Repeat, RefPtr<DisplayList> text_clip = {});
 
 
     void draw_line(Gfx::IntPoint from, Gfx::IntPoint to, Color color, int thickness = 1, Gfx::LineStyle style = Gfx::LineStyle::Solid, Color alternate_color = Color::Transparent);
     void draw_line(Gfx::IntPoint from, Gfx::IntPoint to, Color color, int thickness = 1, Gfx::LineStyle style = Gfx::LineStyle::Solid, Color alternate_color = Color::Transparent);
 
 
@@ -131,9 +131,9 @@ public:
     void paint_inner_box_shadow_params(PaintBoxShadowParams params);
     void paint_inner_box_shadow_params(PaintBoxShadowParams params);
     void paint_text_shadow(int blur_radius, Gfx::IntRect bounding_rect, Gfx::IntRect text_rect, Gfx::GlyphRun const&, double glyph_run_scale, Color color, Gfx::IntPoint draw_location);
     void paint_text_shadow(int blur_radius, Gfx::IntRect bounding_rect, Gfx::IntRect text_rect, Gfx::GlyphRun const&, double glyph_run_scale, Color color, 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, 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 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, RefPtr<DisplayList> text_clip = {});
+    void fill_rect_with_rounded_corners(Gfx::IntRect const& a_rect, Color color, int radius, RefPtr<DisplayList> text_clip = {});
+    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, RefPtr<DisplayList> text_clip = {});
 
 
     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);