1
0
Fork 0
mirror of https://github.com/LadybirdBrowser/ladybird.git synced 2025-06-11 18:20:43 +09:00

LibWeb: Deduplicate clipping code

This makes it so that PaintableWithLines no longer has its own
bespoke clipping logic, using the same code as regular scroll/
overflow clipping.
This commit is contained in:
Psychpsyo 2025-05-03 11:22:14 +02:00 committed by Alexander Kalenik
parent 155e60a5ff
commit 85883ee5ce
Notes: github-actions[bot] 2025-05-13 12:32:43 +00:00
7 changed files with 52 additions and 44 deletions

View file

@ -38,11 +38,9 @@ Optional<CSSPixelRect> ClippableAndScrollable::clip_rect_for_hit_testing() const
return {};
}
void ClippableAndScrollable::apply_clip(PaintContext& context) const
void ClippableAndScrollable::apply_clip(PaintContext& context, RefPtr<ClipFrame const> from_clip_frame) const
{
if (!m_enclosing_clip_frame)
return;
auto const& clip_rects = m_enclosing_clip_frame->clip_rects();
auto const& clip_rects = from_clip_frame->clip_rects();
if (clip_rects.is_empty())
return;
@ -64,11 +62,9 @@ void ClippableAndScrollable::apply_clip(PaintContext& context) const
}
}
void ClippableAndScrollable::restore_clip(PaintContext& context) const
void ClippableAndScrollable::restore_clip(PaintContext& context, RefPtr<ClipFrame const> from_clip_frame) const
{
if (!m_enclosing_clip_frame)
return;
auto const& clip_rects = m_enclosing_clip_frame->clip_rects();
auto const& clip_rects = from_clip_frame->clip_rects();
if (clip_rects.is_empty())
return;

View file

@ -16,7 +16,9 @@ public:
virtual ~ClippableAndScrollable() = default;
void set_enclosing_scroll_frame(RefPtr<ScrollFrame const> scroll_frame) { m_enclosing_scroll_frame = scroll_frame; }
void set_own_scroll_frame(RefPtr<ScrollFrame> scroll_frame) { m_own_scroll_frame = scroll_frame; }
void set_enclosing_clip_frame(RefPtr<ClipFrame const> clip_frame) { m_enclosing_clip_frame = clip_frame; }
void set_own_clip_frame(RefPtr<ClipFrame const> clip_frame) { m_own_clip_frame = clip_frame; }
[[nodiscard]] RefPtr<ScrollFrame const> enclosing_scroll_frame() const { return m_enclosing_scroll_frame; }
[[nodiscard]] Optional<int> scroll_frame_id() const;
@ -31,10 +33,12 @@ public:
return m_own_scroll_frame->own_offset();
return {};
}
void set_own_scroll_frame(RefPtr<ScrollFrame> scroll_frame) { m_own_scroll_frame = scroll_frame; }
void apply_clip(PaintContext&) const;
void restore_clip(PaintContext&) const;
[[nodiscard]] RefPtr<ClipFrame const> enclosing_clip_frame() const { return m_enclosing_clip_frame; }
[[nodiscard]] RefPtr<ClipFrame const> own_clip_frame() const { return m_own_clip_frame; }
void apply_clip(PaintContext&, RefPtr<ClipFrame const>) const;
void restore_clip(PaintContext&, RefPtr<ClipFrame const>) const;
Gfx::AffineTransform const& combined_css_transform() const { return m_combined_css_transform; }
void set_combined_css_transform(Gfx::AffineTransform const& transform) { m_combined_css_transform = transform; }
@ -43,6 +47,7 @@ private:
RefPtr<ScrollFrame const> m_enclosing_scroll_frame;
RefPtr<ScrollFrame const> m_own_scroll_frame;
RefPtr<ClipFrame const> m_enclosing_clip_frame;
RefPtr<ClipFrame const> m_own_clip_frame;
Gfx::AffineTransform m_combined_css_transform;
};

View file

@ -246,6 +246,13 @@ CSSPixelRect PaintableBox::absolute_border_box_rect() const
return rect;
}
// https://drafts.csswg.org/css-overflow-4/#overflow-clip-edge
CSSPixelRect PaintableBox::overflow_clip_edge_rect() const
{
// FIXME: Apply overflow-clip-margin-* properties
return absolute_padding_box_rect();
}
CSSPixelRect PaintableBox::absolute_paint_rect() const
{
if (!m_absolute_paint_rect.has_value())
@ -623,18 +630,24 @@ void PaintableBox::reset_scroll_offset(PaintContext& context, PaintPhase) const
void PaintableBox::apply_clip_overflow_rect(PaintContext& context, PaintPhase phase) const
{
if (!enclosing_clip_frame())
return;
if (!AK::first_is_one_of(phase, PaintPhase::Background, PaintPhase::Border, PaintPhase::TableCollapsedBorder, PaintPhase::Foreground, PaintPhase::Outline))
return;
apply_clip(context);
apply_clip(context, enclosing_clip_frame());
}
void PaintableBox::clear_clip_overflow_rect(PaintContext& context, PaintPhase phase) const
{
if (!enclosing_clip_frame())
return;
if (!AK::first_is_one_of(phase, PaintPhase::Background, PaintPhase::Border, PaintPhase::TableCollapsedBorder, PaintPhase::Foreground, PaintPhase::Outline))
return;
restore_clip(context);
restore_clip(context, enclosing_clip_frame());
}
void paint_cursor_if_needed(PaintContext& context, TextPaintable const& paintable, PaintableFragment const& fragment)
@ -847,32 +860,8 @@ void PaintableWithLines::paint(PaintContext& context, PaintPhase phase) const
PaintableBox::paint(context, phase);
if (fragments().is_empty())
return;
bool should_clip_overflow = computed_values().overflow_x() != CSS::Overflow::Visible && computed_values().overflow_y() != CSS::Overflow::Visible;
auto clip_box = absolute_padding_box_rect();
if (get_clip_rect().has_value()) {
clip_box.intersect(get_clip_rect().value());
should_clip_overflow = true;
}
if (should_clip_overflow) {
context.display_list_recorder().save();
// FIXME: Handle overflow-x and overflow-y being different values.
context.display_list_recorder().add_clip_rect(context.rounded_device_rect(clip_box).to_type<int>());
auto border_radii = normalized_border_radii_data(ShrinkRadiiForBorders::Yes);
CornerRadii corner_radii {
.top_left = border_radii.top_left.as_corner(context),
.top_right = border_radii.top_right.as_corner(context),
.bottom_right = border_radii.bottom_right.as_corner(context),
.bottom_left = border_radii.bottom_left.as_corner(context)
};
if (corner_radii.has_any_radius()) {
context.display_list_recorder().add_rounded_rect_clip(corner_radii, context.rounded_device_rect(clip_box).to_type<int>(), CornerClip::Outside);
}
if (own_clip_frame()) {
apply_clip(context, own_clip_frame());
if (own_scroll_frame_id().has_value()) {
context.display_list_recorder().push_scroll_frame_id(own_scroll_frame_id().value());
}
@ -900,12 +889,11 @@ void PaintableWithLines::paint(PaintContext& context, PaintPhase phase) const
paint_text_fragment(context, static_cast<TextPaintable const&>(fragment.paintable()), fragment, phase);
}
if (should_clip_overflow) {
context.display_list_recorder().restore();
if (own_clip_frame()) {
if (own_scroll_frame_id().has_value()) {
context.display_list_recorder().pop_scroll_frame_id();
}
restore_clip(context, own_clip_frame());
}
}

View file

@ -84,6 +84,7 @@ public:
CSSPixelRect absolute_rect() const;
CSSPixelRect absolute_padding_box_rect() const;
CSSPixelRect absolute_border_box_rect() const;
CSSPixelRect overflow_clip_edge_rect() const;
CSSPixelRect absolute_paint_rect() const;
// These united versions of the above rects take continuation into account.

View file

@ -136,6 +136,12 @@ void ViewportPaintable::assign_clip_frames()
});
for_each_in_subtree([&](auto const& paintable) {
if (paintable.is_paintable_box()) {
auto const& paintable_box = static_cast<PaintableBox const&>(paintable);
if (auto clip_frame = clip_state.get(paintable_box); clip_frame.has_value()) {
const_cast<PaintableBox&>(paintable_box).set_own_clip_frame(clip_frame.value());
}
}
for (auto block = paintable.containing_block(); !block->is_viewport(); block = block->containing_block()) {
if (auto clip_frame = clip_state.get(block); clip_frame.has_value()) {
if (paintable.is_paintable_box()) {