LibWeb: Resolve outline CSS property before paint commands recording

Refactor to resolve paint-only properties before painting, aiming to
stop using layout nodes during recording of painting commands.

Also adds a test, as we have not had any for outlines yet.
This commit is contained in:
Aliaksandr Kalenik 2024-02-11 01:56:39 +01:00 committed by Alexander Kalenik
parent f19b17e089
commit 95d91a37d2
7 changed files with 64 additions and 11 deletions

View file

@ -0,0 +1,11 @@
<!DOCTYPE html>
<link rel="match" href="reference/outlines-ref.html" />
<style>
.outline {
width: 100px;
height: 100px;
outline: 5px solid black;
}
</style>
<div class="outline">hello from paintable box</div>
<p><span class="outline">hello from inline paintable</span></p>

View file

@ -0,0 +1,12 @@
<!DOCTYPE html>
<link rel="match" href="reference/outlines-ref.html" />
<style>
.border {
width: 100px;
height: 100px;
border: 5px solid black;
margin: -5px;
}
</style>
<div class="border">hello from paintable box</div>
<p><span class="border">hello from inline paintable</span></p>

View file

@ -167,11 +167,9 @@ void InlinePaintable::paint(PaintContext& context, PaintPhase phase) const
}
if (phase == PaintPhase::Outline) {
auto outline_width = computed_values().outline_width().to_px(layout_node());
auto maybe_outline_data = borders_data_for_outline(layout_node(), computed_values().outline_color(), computed_values().outline_style(), outline_width);
if (maybe_outline_data.has_value()) {
auto maybe_outline_data = this->outline_data();
if (maybe_outline_data.has_value())
paint_border_or_outline(maybe_outline_data.value(), computed_values().outline_offset().to_px(layout_node()));
}
}
if (phase == PaintPhase::Foreground) {

View file

@ -37,6 +37,12 @@ public:
void set_box_shadow_data(Vector<ShadowData>&& box_shadow_data) { m_box_shadow_data = move(box_shadow_data); }
Vector<ShadowData> const& box_shadow_data() const { return m_box_shadow_data; }
void set_outline_data(Optional<BordersData> outline_data) { m_outline_data = outline_data; }
Optional<BordersData> const& outline_data() const { return m_outline_data; }
void set_outline_offset(CSSPixels outline_offset) { m_outline_offset = outline_offset; }
CSSPixels outline_offset() const { return m_outline_offset; }
void set_enclosing_scroll_frame(RefPtr<ScrollFrame> scroll_frame) { m_enclosing_scroll_frame = scroll_frame; }
void set_enclosing_clip_frame(RefPtr<ClipFrame> clip_frame) { m_enclosing_clip_frame = clip_frame; }
@ -55,6 +61,8 @@ private:
RefPtr<ClipFrame const> m_enclosing_clip_frame;
Vector<ShadowData> m_box_shadow_data;
Optional<BordersData> m_outline_data;
CSSPixels m_outline_offset { 0 };
Vector<PaintableFragment> m_fragments;
};

View file

@ -259,10 +259,9 @@ void PaintableBox::paint(PaintContext& context, PaintPhase phase) const
}
if (phase == PaintPhase::Outline) {
auto outline_width = computed_values().outline_width().to_px(layout_node());
auto borders_data = borders_data_for_outline(layout_node(), computed_values().outline_color(), computed_values().outline_style(), outline_width);
if (borders_data.has_value()) {
auto outline_offset = computed_values().outline_offset().to_px(layout_node());
auto const& outline_data = this->outline_data();
if (outline_data.has_value()) {
auto outline_offset = this->outline_offset();
auto border_radius_data = normalized_border_radii_data(ShrinkRadiiForBorders::No);
auto borders_rect = absolute_border_box_rect();
@ -279,10 +278,10 @@ void PaintableBox::paint(PaintContext& context, PaintPhase phase) const
if ((borders_rect.height() / 2) + outline_offset_y < 0)
outline_offset_y = -borders_rect.height() / 2;
border_radius_data.inflate(outline_width + outline_offset_y, outline_width + outline_offset_x, outline_width + outline_offset_y, outline_width + outline_offset_x);
borders_rect.inflate(outline_width + outline_offset_y, outline_width + outline_offset_x, outline_width + outline_offset_y, outline_width + outline_offset_x);
border_radius_data.inflate(outline_data->top.width + outline_offset_y, outline_data->right.width + outline_offset_x, outline_data->bottom.width + outline_offset_y, outline_data->left.width + outline_offset_x);
borders_rect.inflate(outline_data->top.width + outline_offset_y, outline_data->right.width + outline_offset_x, outline_data->bottom.width + outline_offset_y, outline_data->left.width + outline_offset_x);
context.recording_painter().paint_borders(context.rounded_device_rect(borders_rect), border_radius_data.as_corners(context), borders_data->to_device_pixels(context));
context.recording_painter().paint_borders(context.rounded_device_rect(borders_rect), border_radius_data.as_corners(context), outline_data->to_device_pixels(context));
}
}

View file

@ -195,6 +195,12 @@ public:
void set_transform_origin(CSSPixelPoint transform_origin) { m_transform_origin = transform_origin; }
CSSPixelPoint const& transform_origin() const { return m_transform_origin; }
void set_outline_data(Optional<BordersData> outline_data) { m_outline_data = outline_data; }
Optional<BordersData> const& outline_data() const { return m_outline_data; }
void set_outline_offset(CSSPixels outline_offset) { m_outline_offset = outline_offset; }
CSSPixels outline_offset() const { return m_outline_offset; }
CSSPixelRect compute_absolute_padding_rect_with_css_transform_applied() const;
Gfx::AffineTransform compute_combined_css_transform() const;
@ -243,6 +249,9 @@ private:
Vector<ShadowData> m_box_shadow_data;
Gfx::FloatMatrix4x4 m_transform { Gfx::FloatMatrix4x4::identity() };
CSSPixelPoint m_transform_origin;
Optional<BordersData> m_outline_data;
CSSPixels m_outline_offset { 0 };
};
class PaintableWithLines : public PaintableBox {

View file

@ -227,6 +227,7 @@ void ViewportPaintable::resolve_paint_only_properties()
// - Text shadows
// - Transforms
// - Transform origins
// - Outlines
for_each_in_inclusive_subtree([&](Paintable& paintable) {
auto& node = paintable.layout_node();
@ -416,6 +417,21 @@ void ViewportPaintable::resolve_paint_only_properties()
paintable_box.set_transform_origin({ x, y });
}
// Outlines
auto const& computed_values = node.computed_values();
auto outline_width = computed_values.outline_width().to_px(node);
auto outline_data = borders_data_for_outline(node, computed_values.outline_color(), computed_values.outline_style(), outline_width);
auto outline_offset = computed_values.outline_offset().to_px(node);
if (is_paintable_box) {
auto& paintable_box = static_cast<Painting::PaintableBox&>(paintable);
paintable_box.set_outline_data(outline_data);
paintable_box.set_outline_offset(outline_offset);
} else if (is_inline_paintable) {
auto& inline_paintable = static_cast<Painting::InlinePaintable&>(paintable);
inline_paintable.set_outline_data(outline_data);
inline_paintable.set_outline_offset(outline_offset);
}
return TraversalDecision::Continue;
});
}