From b14c6eaef37c62384fe2f48444325e161e831fad Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sat, 12 Mar 2022 00:35:49 +0100 Subject: [PATCH] LibWeb: Let paintables cache their containing block and absolute rect The absolute rect of a paintable is somewhat expensive to compute. This is because all coordinates are relative to the nearest containing block, so we have to traverse the containing block chain and apply each offset to get the final rect. Paintables will never move between containing blocks, nor will their absolute rect change. If anything changes, we'll simpl make a new paintable and replace the old one. Take advantage of this by caching the containing block and absolute rect after first access. --- .../Libraries/LibWeb/Painting/InlinePaintable.cpp | 6 +++--- Userland/Libraries/LibWeb/Painting/Paintable.cpp | 2 +- Userland/Libraries/LibWeb/Painting/Paintable.h | 8 ++++++++ Userland/Libraries/LibWeb/Painting/PaintableBox.cpp | 13 ++++++++----- Userland/Libraries/LibWeb/Painting/PaintableBox.h | 2 ++ 5 files changed, 22 insertions(+), 9 deletions(-) diff --git a/Userland/Libraries/LibWeb/Painting/InlinePaintable.cpp b/Userland/Libraries/LibWeb/Painting/InlinePaintable.cpp index 0ac55593d3a..b394269bf14 100644 --- a/Userland/Libraries/LibWeb/Painting/InlinePaintable.cpp +++ b/Userland/Libraries/LibWeb/Painting/InlinePaintable.cpp @@ -38,7 +38,7 @@ void InlinePaintable::paint(PaintContext& context, Painting::PaintPhase phase) c auto top_right_border_radius = computed_values().border_top_right_radius(); auto bottom_right_border_radius = computed_values().border_bottom_right_radius(); auto bottom_left_border_radius = computed_values().border_bottom_left_radius(); - auto containing_block_position_in_absolute_coordinates = layout_node().containing_block()->paint_box()->absolute_position(); + auto containing_block_position_in_absolute_coordinates = containing_block()->paint_box()->absolute_position(); for_each_fragment([&](auto const& fragment, bool is_first_fragment, bool is_last_fragment) { Gfx::FloatRect absolute_fragment_rect { containing_block_position_in_absolute_coordinates.translated(fragment.offset()), fragment.size() }; @@ -89,7 +89,7 @@ void InlinePaintable::paint(PaintContext& context, Painting::PaintPhase phase) c .left = computed_values().border_left(), }; - auto containing_block_position_in_absolute_coordinates = layout_node().containing_block()->paint_box()->absolute_position(); + auto containing_block_position_in_absolute_coordinates = containing_block()->paint_box()->absolute_position(); for_each_fragment([&](auto const& fragment, bool is_first_fragment, bool is_last_fragment) { Gfx::FloatRect absolute_fragment_rect { containing_block_position_in_absolute_coordinates.translated(fragment.offset()), fragment.size() }; @@ -132,7 +132,7 @@ void InlinePaintable::for_each_fragment(Callback callback) const { // FIXME: This will be slow if the containing block has a lot of fragments! Vector fragments; - layout_node().containing_block()->paint_box()->for_each_fragment([&](auto& fragment) { + containing_block()->paint_box()->for_each_fragment([&](auto& fragment) { if (layout_node().is_inclusive_ancestor_of(fragment.layout_node())) fragments.append(fragment); return IterationDecision::Continue; diff --git a/Userland/Libraries/LibWeb/Painting/Paintable.cpp b/Userland/Libraries/LibWeb/Painting/Paintable.cpp index 008e871defc..4ddd640e313 100644 --- a/Userland/Libraries/LibWeb/Painting/Paintable.cpp +++ b/Userland/Libraries/LibWeb/Painting/Paintable.cpp @@ -24,7 +24,7 @@ void Paintable::handle_mousemove(Badge, Gfx::IntPoint const&, unsi bool Paintable::handle_mousewheel(Badge, Gfx::IntPoint const&, unsigned, unsigned, int wheel_delta_x, int wheel_delta_y) { - if (auto* containing_block = layout_node().containing_block()) { + if (auto* containing_block = this->containing_block()) { if (!containing_block->is_scrollable()) return false; auto new_offset = containing_block->scroll_offset(); diff --git a/Userland/Libraries/LibWeb/Painting/Paintable.h b/Userland/Libraries/LibWeb/Painting/Paintable.h index ea81a8d8675..686b1a99123 100644 --- a/Userland/Libraries/LibWeb/Painting/Paintable.h +++ b/Userland/Libraries/LibWeb/Painting/Paintable.h @@ -68,6 +68,13 @@ public: void set_needs_display() const { const_cast(m_layout_node).set_needs_display(); } + Layout::BlockContainer const* containing_block() const + { + if (!m_containing_block.has_value()) + m_containing_block = const_cast(m_layout_node).containing_block(); + return *m_containing_block; + } + protected: explicit Paintable(Layout::Node const& layout_node) : m_layout_node(layout_node) @@ -76,6 +83,7 @@ protected: private: Layout::Node const& m_layout_node; + Optional mutable m_containing_block; }; } diff --git a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp index b0ce44c7610..c1fdbfffa85 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp +++ b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp @@ -58,7 +58,7 @@ void PaintableBox::set_content_size(Gfx::FloatSize const& size) Gfx::FloatPoint PaintableBox::effective_offset() const { if (m_containing_line_box_fragment.has_value()) { - auto const& fragment = layout_box().containing_block()->paint_box()->line_boxes()[m_containing_line_box_fragment->line_box_index].fragments()[m_containing_line_box_fragment->fragment_index]; + auto const& fragment = containing_block()->paint_box()->line_boxes()[m_containing_line_box_fragment->line_box_index].fragments()[m_containing_line_box_fragment->fragment_index]; return fragment.offset(); } return m_offset; @@ -66,10 +66,13 @@ Gfx::FloatPoint PaintableBox::effective_offset() const Gfx::FloatRect PaintableBox::absolute_rect() const { - Gfx::FloatRect rect { effective_offset(), content_size() }; - for (auto* block = layout_box().containing_block(); block; block = block->containing_block()) - rect.translate_by(block->paint_box()->effective_offset()); - return rect; + if (!m_absolute_rect.has_value()) { + Gfx::FloatRect rect { effective_offset(), content_size() }; + for (auto const* block = containing_block(); block && block->paintable(); block = block->paintable()->containing_block()) + rect.translate_by(block->paint_box()->effective_offset()); + m_absolute_rect = rect; + } + return *m_absolute_rect; } void PaintableBox::set_containing_line_box_fragment(Optional fragment_coordinate) diff --git a/Userland/Libraries/LibWeb/Painting/PaintableBox.h b/Userland/Libraries/LibWeb/Painting/PaintableBox.h index 95545196dc4..6314a66c128 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintableBox.h +++ b/Userland/Libraries/LibWeb/Painting/PaintableBox.h @@ -131,6 +131,8 @@ private: Painting::BorderRadiusData normalized_border_radius_data() const; OwnPtr m_stacking_context; + + Optional mutable m_absolute_rect; }; class PaintableWithLines : public PaintableBox {