From a6822986bb2eb2f0a16f4279bbcb45e686f28314 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Wed, 13 Nov 2024 12:29:17 +0000 Subject: [PATCH] LibWeb/Painting: Apply clip and mask operations that are off-screen These operations should still apply even if they are off screen, because they affect painting of things outside of their bounding rectangles. This commit makes us always apply these, regardless of if they are in the visible region. However, if they are outside that region, we replace them with simple clip-rect commands, which have the same effect (not painting anything) but are cheaper than computing a full mask bitmap. --- Libraries/LibWeb/Painting/Command.h | 4 ++ Libraries/LibWeb/Painting/DisplayList.cpp | 20 +++++++ .../Ref/expected/clip-offscreen-ref.html | 2 + Tests/LibWeb/Ref/input/clip-offscreen.html | 57 +++++++++++++++++++ 4 files changed, 83 insertions(+) create mode 100644 Tests/LibWeb/Ref/expected/clip-offscreen-ref.html create mode 100644 Tests/LibWeb/Ref/input/clip-offscreen.html diff --git a/Libraries/LibWeb/Painting/Command.h b/Libraries/LibWeb/Painting/Command.h index fca91076711..e085ab80881 100644 --- a/Libraries/LibWeb/Painting/Command.h +++ b/Libraries/LibWeb/Painting/Command.h @@ -105,6 +105,8 @@ struct Translate { struct AddClipRect { Gfx::IntRect rect; + [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } + bool is_clip_or_mask() const { return true; } void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); } }; @@ -349,6 +351,7 @@ struct AddRoundedRectClip { CornerClip corner_clip; [[nodiscard]] Gfx::IntRect bounding_rect() const { return border_rect; } + bool is_clip_or_mask() const { return true; } void translate_by(Gfx::IntPoint const& offset) { border_rect.translate_by(offset); } }; @@ -358,6 +361,7 @@ struct AddMask { Gfx::IntRect rect; [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } + bool is_clip_or_mask() const { return true; } void translate_by(Gfx::IntPoint const& offset) { diff --git a/Libraries/LibWeb/Painting/DisplayList.cpp b/Libraries/LibWeb/Painting/DisplayList.cpp index 4883944a206..b7b0f89173d 100644 --- a/Libraries/LibWeb/Painting/DisplayList.cpp +++ b/Libraries/LibWeb/Painting/DisplayList.cpp @@ -24,6 +24,17 @@ static Optional command_bounding_rectangle(Command const& command) }); } +static bool command_is_clip_or_mask(Command const& command) +{ + return command.visit( + [&](auto const& command) -> bool { + if constexpr (requires { command.is_clip_or_mask(); }) + return command.is_clip_or_mask(); + else + return false; + }); +} + void DisplayListPlayer::execute(DisplayList& display_list) { auto const& commands = display_list.commands(); @@ -60,6 +71,15 @@ void DisplayListPlayer::execute(DisplayList& display_list) auto bounding_rect = command_bounding_rectangle(command); if (bounding_rect.has_value() && (bounding_rect->is_empty() || would_be_fully_clipped_by_painter(*bounding_rect))) { + // Any clip or mask that's located outside of the visible region is equivalent to a simple clip-rect, + // so replace it with one to avoid doing unnecessary work. + if (command_is_clip_or_mask(command)) { + if (command.has()) { + add_clip_rect(command.get()); + } else { + add_clip_rect({ bounding_rect.release_value() }); + } + } continue; } diff --git a/Tests/LibWeb/Ref/expected/clip-offscreen-ref.html b/Tests/LibWeb/Ref/expected/clip-offscreen-ref.html new file mode 100644 index 00000000000..397a3461531 --- /dev/null +++ b/Tests/LibWeb/Ref/expected/clip-offscreen-ref.html @@ -0,0 +1,2 @@ + +(This space intentionally left blank) diff --git a/Tests/LibWeb/Ref/input/clip-offscreen.html b/Tests/LibWeb/Ref/input/clip-offscreen.html new file mode 100644 index 00000000000..943192b0a7e --- /dev/null +++ b/Tests/LibWeb/Ref/input/clip-offscreen.html @@ -0,0 +1,57 @@ + + + +
+
+ 01) Overflow
+ 02) Overflow
+ 03) Overflow
+ 04) Overflow
+ 05) Overflow
+ 06) Overflow
+ 07) Overflow
+ 08) Overflow
+ 09) Overflow
+ 10) Overflow
+
+
+ +
+
+ 01) Border-radius overflow
+ 02) Border-radius overflow
+ 03) Border-radius overflow
+ 04) Border-radius overflow
+ 05) Border-radius overflow
+ 06) Border-radius overflow
+ 07) Border-radius overflow
+ 08) Border-radius overflow
+ 09) Border-radius overflow
+ 10) Border-radius overflow
+
+
+ +
+
+ 01) Masked overflow
+ 02) Masked overflow
+ 03) Masked overflow
+ 04) Masked overflow
+ 05) Masked overflow
+ 06) Masked overflow
+ 07) Masked overflow
+ 08) Masked overflow
+ 09) Masked overflow
+ 10) Masked overflow
+
+
+ +(This space intentionally left blank)