Skip to content

Commit

Permalink
overflow clipping prototype wip
Browse files Browse the repository at this point in the history
  • Loading branch information
kalenikaliaksandr committed Jan 25, 2024
1 parent 09124fc commit 8ccd2bb
Show file tree
Hide file tree
Showing 10 changed files with 180 additions and 7 deletions.
13 changes: 13 additions & 0 deletions Userland/Libraries/LibWeb/HTML/Navigable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2086,7 +2086,20 @@ void Navigable::paint(Painting::RecordingPainter& recording_painter, PaintConfig
document->paintable()->collect_scroll_frames(context);
}

document->paintable()->assign_clip_rects();

document->paintable()->build_stacking_context_tree_if_needed();

dbgln(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>before paint all phases");
document->paintable()->paint_all_phases(context);
dbgln("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<after paint all phases");

// dbgln("=BEFORE");
// document->paintable()->for_each_in_inclusive_subtree_of_type<Painting::PaintableBox>([&](auto const& paintable_box) {
// dbgln(">>>node=({}) absolute_rect=({}) compute_absolute_rect_including_transforms=({})", paintable_box.layout_node().debug_description(), paintable_box.absolute_rect(), paintable_box.compute_absolute_rect_including_transforms());
// return Painting::TraversalDecision::Continue;
// });
// dbgln("=AFTER");

// FIXME: Support scrollable frames inside iframes.
if (is_traversable()) {
Expand Down
2 changes: 2 additions & 0 deletions Userland/Libraries/LibWeb/Painting/ImagePaintable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ void ImagePaintable::paint(PaintContext& context, PaintPhase phase) const
PaintableBox::paint(context, phase);

if (phase == PaintPhase::Foreground) {
dbgln("(phase=foreground) Painting ImagePaintable({}) at {},{} to {},{}", dom_node()->node_name(), absolute_rect().x(), absolute_rect().y(), absolute_rect().width(), absolute_rect().height());

auto image_rect = context.rounded_device_rect(absolute_rect());
if (layout_box().renders_as_alt_text()) {
auto& image_element = verify_cast<HTML::HTMLImageElement>(*dom_node());
Expand Down
10 changes: 5 additions & 5 deletions Userland/Libraries/LibWeb/Painting/Paintable.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ enum class TraversalDecision {
};

enum class PaintPhase {
Background,
Border,
Foreground,
Outline,
Overlay,
Background = 0,
Border = 1,
Foreground = 2,
Outline = 3,
Overlay = 4,
};

struct HitTestResult {
Expand Down
72 changes: 72 additions & 0 deletions Userland/Libraries/LibWeb/Painting/PaintableBox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,34 @@ CSSPixelRect PaintableBox::compute_absolute_rect() const
return rect;
}

CSSPixelRect PaintableBox::compute_absolute_rect_including_transforms() const
{
CSSPixelRect rect { offset(), content_size() };
for (auto const* block = containing_block(); block && block->paintable(); block = block->paintable()->containing_block()) {
auto offset = block->paintable_box()->offset();
auto const& transformations = block->computed_values().transformations();
auto matrix = Gfx::FloatMatrix4x4::identity();
for (auto const& transform : transformations)
matrix = matrix * transform.to_matrix(*block->paintable_box()).release_value();
auto affine_transform = Gfx::extract_2d_affine_transform(matrix);
// dbgln(">>>>>>>>>>>>>affine_transform translation=({})", affine_transform.translation());
// offset = affine_transform.map(offset.to_type<float>()).to_type<CSSPixels>();
offset.translate_by(affine_transform.translation().to_type<CSSPixels>());
rect.translate_by(offset);
}

auto const& transformations = computed_values().transformations();
auto matrix = Gfx::FloatMatrix4x4::identity();
for (auto const& transform : transformations)
matrix = matrix * transform.to_matrix(*this).release_value();
auto affine_transform = Gfx::extract_2d_affine_transform(matrix);
// dbgln(">>>>>>>>>>>>>affine_transform translation=({})", affine_transform.translation());

rect.translate_by(affine_transform.translation().to_type<CSSPixels>());

return rect;
}

CSSPixelRect PaintableBox::absolute_rect() const
{
if (!m_absolute_rect.has_value())
Expand Down Expand Up @@ -170,6 +198,10 @@ void PaintableBox::before_paint(PaintContext& context, [[maybe_unused]] PaintPha
if (!is_visible())
return;

// dbgln("PaintableBox::before_paint ({})", layout_node().debug_description());

apply_clip_overflow_rect(context, phase);

auto clip_rect = get_clip_rect();
if (clip_rect.has_value()) {
context.recording_painter().save();
Expand All @@ -182,6 +214,10 @@ void PaintableBox::after_paint(PaintContext& context, [[maybe_unused]] PaintPhas
if (!is_visible())
return;

// dbgln("PaintableBox::after_paint ({})", layout_node().debug_description());

clear_clip_overflow_rect(context, phase);

if (get_clip_rect().has_value())
context.recording_painter().restore();
}
Expand Down Expand Up @@ -408,6 +444,35 @@ void PaintableBox::apply_clip_overflow_rect(PaintContext& context, PaintPhase ph
if (!AK::first_is_one_of(phase, PaintPhase::Background, PaintPhase::Border, PaintPhase::Foreground))
return;

if (m_overflow_clipped_rect.has_value()) {
auto* parent_context = const_cast<PaintableBox*>(this)->enclosing_stacking_context();
if (stacking_context()) {
parent_context = const_cast<StackingContext*>(stacking_context());
}
auto overflow_clipped_rect = m_overflow_clipped_rect.value();
// auto sc_opacity = parent_context->paintable_box().computed_values().opacity();
// auto sc_transform = parent_context->paintable_box().transform();
// auto affine_transform = Gfx::extract_2d_affine_transform(sc_transform);
// if (parent_context && (sc_opacity < 1.0f || !affine_transform.is_identity())) {
// auto parent_context_absolute_rect_including_transforms = parent_context->paintable_box().compute_absolute_rect_including_transforms();
// (void)parent_context_absolute_rect_including_transforms;
// dbgln(">>>>has parent context ({}) parent_context_absolute_rect_including_transforms ({})", parent_context->paintable_box().layout_node().debug_description(), parent_context_absolute_rect_including_transforms);
// overflow_clipped_rect.translate_by(-parent_context_absolute_rect_including_transforms.location());
// }

if (parent_context) {
auto parent_context_absolute_rect_including_transforms = parent_context->paintable_box().compute_absolute_rect_including_transforms();
dbgln(">parent_context_absolute_rect_including_transforms=({})", parent_context_absolute_rect_including_transforms);
overflow_clipped_rect.translate_by(-parent_context_absolute_rect_including_transforms.location());
}
dbgln("=============apply m_overflow_clipped_rect=({}) box=({}) translation=({})", overflow_clipped_rect, layout_node().debug_description(), context.recording_painter().state().translation.translation());
context.recording_painter().save();
// context.recording_painter().add_absolute_clip_rect(context.enclosing_device_rect(overflow_clipped_rect).to_type<int>());
context.recording_painter().add_clip_rect(context.enclosing_device_rect(overflow_clipped_rect).to_type<int>());
}

return;

// FIXME: Support more overflow variations.
auto clip_rect = this->calculate_overflow_clipped_rect();
auto overflow_x = computed_values().overflow_x();
Expand Down Expand Up @@ -451,6 +516,13 @@ void PaintableBox::clear_clip_overflow_rect(PaintContext& context, PaintPhase ph
if (!AK::first_is_one_of(phase, PaintPhase::Background, PaintPhase::Border, PaintPhase::Foreground))
return;

if (m_overflow_clipped_rect.has_value()) {
dbgln("=============clear m_overflow_clipped_rect=({}) ({})", m_overflow_clipped_rect.value(), layout_node().debug_description());
context.recording_painter().restore();
}

return;

// FIXME: Support more overflow variations.
if (m_clipping_overflow) {
context.recording_painter().restore();
Expand Down
4 changes: 4 additions & 0 deletions Userland/Libraries/LibWeb/Painting/PaintableBox.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,10 @@ class PaintableBox : public Paintable {
void set_transform_origin(CSSPixelPoint transform_origin) { m_transform_origin = transform_origin; }
CSSPixelPoint const& transform_origin() const { return m_transform_origin; }

CSSPixelRect compute_absolute_rect_including_transforms() const;

mutable Optional<CSSPixelRect> m_overflow_clipped_rect;

protected:
explicit PaintableBox(Layout::Box const&);

Expand Down
16 changes: 16 additions & 0 deletions Userland/Libraries/LibWeb/Painting/RecordingPainter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ 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)
{
dbgln("=RecordingPainter::draw_scaled_immutable_bitmap dst_rect=({})", dst_rect);
push_command(DrawScaledImmutableBitmap {
.dst_rect = state().translation.map(dst_rect),
.bitmap = bitmap,
Expand Down Expand Up @@ -259,6 +260,19 @@ void RecordingPainter::add_clip_rect(Gfx::IntRect const& rect)
push_command(SetClipRect { .rect = *state().clip_rect });
}

void RecordingPainter::add_absolute_clip_rect(Gfx::IntRect const& rect)
{
auto prev_clip_rect = state().clip_rect;
if (!state().clip_rect.has_value()) {
state().clip_rect = rect;
} else {
state().clip_rect->intersect(rect);
}

if (prev_clip_rect != state().clip_rect)
push_command(SetClipRect { .rect = *state().clip_rect });
}

void RecordingPainter::translate(int dx, int dy)
{
m_state_stack.last().translation.translate(dx, dy);
Expand Down Expand Up @@ -291,6 +305,7 @@ void RecordingPainter::restore()

void RecordingPainter::push_stacking_context(PushStackingContextParams params)
{
dbgln("=RecordingPainter::push_stacking_context");
push_command(PushStackingContext {
.opacity = params.opacity,
.is_fixed_position = params.is_fixed_position,
Expand All @@ -310,6 +325,7 @@ void RecordingPainter::push_stacking_context(PushStackingContextParams params)

void RecordingPainter::pop_stacking_context()
{
dbgln("=RecordingPainter::pop_stacking_context");
m_state_stack.take_last();
push_command(PopStackingContext {});
}
Expand Down
3 changes: 2 additions & 1 deletion Userland/Libraries/LibWeb/Painting/RecordingPainter.h
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,7 @@ class RecordingPainter {
void draw_text_run(Gfx::IntPoint baseline_start, Span<Gfx::DrawGlyphOrEmoji const> glyph_run, Color color, Gfx::IntRect const& rect);

void add_clip_rect(Gfx::IntRect const& rect);
void add_absolute_clip_rect(Gfx::IntRect const& rect);

void translate(int dx, int dy);
void translate(Gfx::IntPoint delta);
Expand Down Expand Up @@ -598,7 +599,6 @@ class RecordingPainter {

void apply_scroll_offsets(Vector<Gfx::IntPoint> const& offsets_by_frame_id);

private:
struct State {
Gfx::AffineTransform translation;
Optional<Gfx::IntRect> clip_rect;
Expand All @@ -607,6 +607,7 @@ class RecordingPainter {
State& state() { return m_state_stack.last(); }
State const& state() const { return m_state_stack.last(); }

private:
void push_command(PaintingCommand command)
{
m_painting_commands.append({ state().scroll_frame_id, move(command) });
Expand Down
26 changes: 26 additions & 0 deletions Userland/Libraries/LibWeb/Painting/StackingContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ void StackingContext::paint_node_as_stacking_context(Paintable const& paintable,
paint_descendants(context, paintable, StackingContextPaintPhase::BackgroundAndBorders);
paint_descendants(context, paintable, StackingContextPaintPhase::Floats);
paint_descendants(context, paintable, StackingContextPaintPhase::BackgroundAndBordersForInlineLevelAndReplaced);
dbgln("====================BEFORE PAINT NODE FOREGROUND====================");
paint_node(paintable, context, PaintPhase::Foreground);
dbgln("====================AFTER PAINT NODE FOREGROUND====================");
paint_descendants(context, paintable, StackingContextPaintPhase::Foreground);
paint_node(paintable, context, PaintPhase::Outline);
paint_node(paintable, context, PaintPhase::Overlay);
Expand All @@ -88,6 +90,13 @@ void StackingContext::paint_node_as_stacking_context(Paintable const& paintable,

void StackingContext::paint_descendants(PaintContext& context, Paintable const& paintable, StackingContextPaintPhase phase)
{
if (paintable.is_paintable_box() && phase == StackingContextPaintPhase::Foreground)
dbgln(">>> StackingContext::paint_descendants({}, {})", paintable.layout_node().debug_description(), static_cast<int>(phase));
ScopeGuard guard([&] {
if (paintable.is_paintable_box() && phase == StackingContextPaintPhase::Foreground)
dbgln("<<< StackingContext::paint_descendants({}, {})", paintable.layout_node().debug_description(), static_cast<int>(phase));
});

paintable.apply_scroll_offset(context, to_paint_phase(phase));
paintable.before_children_paint(context, to_paint_phase(phase));
paintable.apply_clip_overflow_rect(context, to_paint_phase(phase));
Expand Down Expand Up @@ -117,6 +126,7 @@ void StackingContext::paint_descendants(PaintContext& context, Paintable const&
if (stacking_context) {
// FIXME: This may not be fully correct with respect to the paint phases.
if (phase == StackingContextPaintPhase::Foreground) {
dbgln(">1231231231231 StackingContext::paint_descendants({}, {})", stacking_context->paintable().layout_node().debug_description(), static_cast<int>(phase));
paint_child(context, *stacking_context);
}
// Note: Don't further recurse into descendants as paint_child() will do that.
Expand Down Expand Up @@ -174,6 +184,8 @@ void StackingContext::paint_descendants(PaintContext& context, Paintable const&

void StackingContext::paint_child(PaintContext& context, StackingContext const& child)
{
dbgln(">>>> StackingContext::paint_child({}, {})", child.paintable().layout_node().debug_description(), static_cast<int>(PaintPhase::Foreground));

auto parent_paintable = child.paintable().parent();
if (parent_paintable)
parent_paintable->before_children_paint(context, PaintPhase::Foreground);
Expand All @@ -183,13 +195,27 @@ void StackingContext::paint_child(PaintContext& context, StackingContext const&
if (nearest_scrollable_ancestor)
nearest_scrollable_ancestor->apply_scroll_offset(context, PaintPhase::Foreground);

auto containing_block = child.paintable().containing_block();
auto* containing_block_paintable = containing_block ? containing_block->paintable() : nullptr;
if (containing_block_paintable) {
dbgln("*********************apply");
containing_block_paintable->apply_clip_overflow_rect(context, PaintPhase::Foreground);
}

child.paint(context);

if (containing_block_paintable) {
dbgln("*********************clear");
containing_block_paintable->clear_clip_overflow_rect(context, PaintPhase::Foreground);
}

if (nearest_scrollable_ancestor)
nearest_scrollable_ancestor->reset_scroll_offset(context, PaintPhase::Foreground);

if (parent_paintable)
parent_paintable->after_children_paint(context, PaintPhase::Foreground);

dbgln("<<<< StackingContext::paint_child({}, {})", child.paintable().layout_node().debug_description(), static_cast<int>(PaintPhase::Foreground));
}

void StackingContext::paint_internal(PaintContext& context) const
Expand Down
38 changes: 38 additions & 0 deletions Userland/Libraries/LibWeb/Painting/ViewportPaintable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,42 @@ void ViewportPaintable::collect_scroll_frames(PaintContext& context) const
});
}

void ViewportPaintable::assign_clip_rects()
{
HashMap<Paintable const*, CSSPixelRect> clip_rects;
for_each_in_subtree_of_type<PaintableBox>([&](auto const& paintable_box) {
auto overflow_x = paintable_box.computed_values().overflow_x();
auto overflow_y = paintable_box.computed_values().overflow_y();
if (overflow_x != CSS::Overflow::Visible || overflow_y != CSS::Overflow::Visible) {
auto overflow_clip_rect = paintable_box.compute_absolute_rect_including_transforms();
for (auto block = paintable_box.containing_block(); block; block = block->containing_block()) {
auto const& block_paintable_box = *block->paintable_box();
auto block_overflow_x = block_paintable_box.computed_values().overflow_x();
auto block_overflow_y = block_paintable_box.computed_values().overflow_y();
if (block_overflow_x != CSS::Overflow::Visible || block_overflow_y != CSS::Overflow::Visible) {
overflow_clip_rect.intersect(block_paintable_box.compute_absolute_rect_including_transforms());
}
}
clip_rects.set(&paintable_box, overflow_clip_rect);
}
return TraversalDecision::Continue;
});

// for (auto& [paintable, clip_rect] : clip_rects) {
// dbgln("Paintable ({}) has clip rect {}", paintable->layout_node().debug_description(), clip_rect);
// }

for_each_in_subtree_of_type<PaintableBox>([&](auto const& paintable_box) {
for (auto block = paintable_box.containing_block(); block; block = block->containing_block()) {
if (auto clip_rect = clip_rects.get(block->paintable()); clip_rect.has_value()) {
// paintable_box.set_clip_rect(*clip_rect);
paintable_box.m_overflow_clipped_rect = *clip_rect;
dbgln(">>>Paintable ({}) has clip rect {}", paintable_box.layout_node().debug_description(), *clip_rect);
break;
}
}
return TraversalDecision::Continue;
});
}

}
3 changes: 2 additions & 1 deletion Userland/Libraries/LibWeb/Painting/ViewportPaintable.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ class ViewportPaintable final : public PaintableWithLines {
void build_stacking_context_tree_if_needed();

void collect_scroll_frames(PaintContext&) const;
void assign_clip_rects();

private:
void build_stacking_context_tree();

private:
explicit ViewportPaintable(Layout::Viewport const&);
};

Expand Down

0 comments on commit 8ccd2bb

Please sign in to comment.