mirror of
https://github.com/SerenityOS/serenity.git
synced 2025-01-24 10:22:05 -05:00
LibWeb: Resolve border radius during layout and save it in paintables
This change fixes a problem that we should not call `to_px()` to resolve any length or percentage values during paintables traversal because that is supposed to happen while performing layout. Also it improves performance because before we were resolving border radii during each painting phase but now it happens only once during layout.
This commit is contained in:
parent
da134f6867
commit
d1d6da6ab6
8 changed files with 139 additions and 75 deletions
|
@ -10,6 +10,8 @@
|
|||
#include <LibWeb/Layout/LayoutState.h>
|
||||
#include <LibWeb/Layout/TextNode.h>
|
||||
#include <LibWeb/Layout/Viewport.h>
|
||||
#include <LibWeb/Painting/BorderRadiiData.h>
|
||||
#include <LibWeb/Painting/InlinePaintable.h>
|
||||
#include <LibWeb/Painting/SVGPathPaintable.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
@ -215,6 +217,125 @@ static void build_paint_tree(Node& node, Painting::Paintable* parent_paintable =
|
|||
}
|
||||
}
|
||||
|
||||
static Painting::BorderRadiiData normalized_border_radii_data(Layout::Node const& node, CSSPixelRect const& rect, CSS::BorderRadiusData top_left_radius, CSS::BorderRadiusData top_right_radius, CSS::BorderRadiusData bottom_right_radius, CSS::BorderRadiusData bottom_left_radius)
|
||||
{
|
||||
Painting::BorderRadiusData bottom_left_radius_px {};
|
||||
Painting::BorderRadiusData bottom_right_radius_px {};
|
||||
Painting::BorderRadiusData top_left_radius_px {};
|
||||
Painting::BorderRadiusData top_right_radius_px {};
|
||||
|
||||
bottom_left_radius_px.horizontal_radius = bottom_left_radius.horizontal_radius.to_px(node, rect.width());
|
||||
bottom_right_radius_px.horizontal_radius = bottom_right_radius.horizontal_radius.to_px(node, rect.width());
|
||||
top_left_radius_px.horizontal_radius = top_left_radius.horizontal_radius.to_px(node, rect.width());
|
||||
top_right_radius_px.horizontal_radius = top_right_radius.horizontal_radius.to_px(node, rect.width());
|
||||
|
||||
bottom_left_radius_px.vertical_radius = bottom_left_radius.vertical_radius.to_px(node, rect.height());
|
||||
bottom_right_radius_px.vertical_radius = bottom_right_radius.vertical_radius.to_px(node, rect.height());
|
||||
top_left_radius_px.vertical_radius = top_left_radius.vertical_radius.to_px(node, rect.height());
|
||||
top_right_radius_px.vertical_radius = top_right_radius.vertical_radius.to_px(node, rect.height());
|
||||
|
||||
// Scale overlapping curves according to https://www.w3.org/TR/css-backgrounds-3/#corner-overlap
|
||||
// Let f = min(Li/Si), where i ∈ {top, right, bottom, left},
|
||||
// Si is the sum of the two corresponding radii of the corners on side i,
|
||||
// and Ltop = Lbottom = the width of the box, and Lleft = Lright = the height of the box.
|
||||
auto l_top = rect.width();
|
||||
auto l_bottom = l_top;
|
||||
auto l_left = rect.height();
|
||||
auto l_right = l_left;
|
||||
auto s_top = (top_left_radius_px.horizontal_radius + top_right_radius_px.horizontal_radius);
|
||||
auto s_right = (top_right_radius_px.vertical_radius + bottom_right_radius_px.vertical_radius);
|
||||
auto s_bottom = (bottom_left_radius_px.horizontal_radius + bottom_right_radius_px.horizontal_radius);
|
||||
auto s_left = (top_left_radius_px.vertical_radius + bottom_left_radius_px.vertical_radius);
|
||||
CSSPixelFraction f = 1;
|
||||
f = min(f, l_top / s_top);
|
||||
f = min(f, l_right / s_right);
|
||||
f = min(f, l_bottom / s_bottom);
|
||||
f = min(f, l_left / s_left);
|
||||
|
||||
// If f < 1, then all corner radii are reduced by multiplying them by f.
|
||||
if (f < 1) {
|
||||
top_left_radius_px.horizontal_radius *= f;
|
||||
top_left_radius_px.vertical_radius *= f;
|
||||
top_right_radius_px.horizontal_radius *= f;
|
||||
top_right_radius_px.vertical_radius *= f;
|
||||
bottom_right_radius_px.horizontal_radius *= f;
|
||||
bottom_right_radius_px.vertical_radius *= f;
|
||||
bottom_left_radius_px.horizontal_radius *= f;
|
||||
bottom_left_radius_px.vertical_radius *= f;
|
||||
}
|
||||
|
||||
return Painting::BorderRadiiData { top_left_radius_px, top_right_radius_px, bottom_right_radius_px, bottom_left_radius_px };
|
||||
}
|
||||
|
||||
void LayoutState::resolve_border_radii()
|
||||
{
|
||||
Vector<Painting::InlinePaintable&> inline_paintables;
|
||||
|
||||
for (auto& it : used_values_per_layout_node) {
|
||||
auto& used_values = *it.value;
|
||||
auto& node = const_cast<NodeWithStyle&>(used_values.node());
|
||||
|
||||
auto* paintable = node.paintable();
|
||||
|
||||
if (paintable && is<Painting::InlinePaintable>(*paintable)) {
|
||||
auto& inline_paintable = static_cast<Painting::InlinePaintable&>(*paintable);
|
||||
inline_paintables.append(inline_paintable);
|
||||
}
|
||||
|
||||
if (paintable && is<Painting::PaintableBox>(*paintable)) {
|
||||
auto& paintable_box = static_cast<Painting::PaintableBox&>(*paintable);
|
||||
|
||||
CSSPixelRect const content_rect { 0, 0, used_values.content_width(), used_values.content_height() };
|
||||
auto border_rect = content_rect.inflated(used_values.border_top, used_values.border_right, used_values.border_bottom, used_values.border_left);
|
||||
|
||||
auto const& border_top_left_radius = node.computed_values().border_top_left_radius();
|
||||
auto const& border_top_right_radius = node.computed_values().border_top_right_radius();
|
||||
auto const& border_bottom_right_radius = node.computed_values().border_bottom_right_radius();
|
||||
auto const& border_bottom_left_radius = node.computed_values().border_bottom_left_radius();
|
||||
|
||||
auto radii_data = normalized_border_radii_data(node, border_rect, border_top_left_radius, border_top_right_radius, border_bottom_right_radius, border_bottom_left_radius);
|
||||
paintable_box.set_border_radii_data(radii_data);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& inline_paintable : inline_paintables) {
|
||||
Vector<Layout::LineBoxFragment&> fragments;
|
||||
verify_cast<Painting::PaintableWithLines>(*inline_paintable.containing_block()->paintable_box()).for_each_fragment([&](auto& fragment) {
|
||||
if (inline_paintable.layout_node().is_inclusive_ancestor_of(fragment.layout_node()))
|
||||
fragments.append(const_cast<Layout::LineBoxFragment&>(fragment));
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
|
||||
auto const& top_left_border_radius = inline_paintable.computed_values().border_top_left_radius();
|
||||
auto const& top_right_border_radius = inline_paintable.computed_values().border_top_right_radius();
|
||||
auto const& bottom_right_border_radius = inline_paintable.computed_values().border_bottom_right_radius();
|
||||
auto const& bottom_left_border_radius = inline_paintable.computed_values().border_bottom_left_radius();
|
||||
|
||||
auto containing_block_position_in_absolute_coordinates = inline_paintable.containing_block()->paintable_box()->absolute_position();
|
||||
for (size_t i = 0; i < fragments.size(); ++i) {
|
||||
auto is_first_fragment = i == 0;
|
||||
auto is_last_fragment = i == fragments.size() - 1;
|
||||
auto& fragment = fragments[i];
|
||||
|
||||
CSSPixelRect absolute_fragment_rect { containing_block_position_in_absolute_coordinates.translated(fragment.offset()), fragment.size() };
|
||||
|
||||
if (is_first_fragment) {
|
||||
auto extra_start_width = inline_paintable.box_model().padding.left;
|
||||
absolute_fragment_rect.translate_by(-extra_start_width, 0);
|
||||
absolute_fragment_rect.set_width(absolute_fragment_rect.width() + extra_start_width);
|
||||
}
|
||||
|
||||
if (is_last_fragment) {
|
||||
auto extra_end_width = inline_paintable.box_model().padding.right;
|
||||
absolute_fragment_rect.set_width(absolute_fragment_rect.width() + extra_end_width);
|
||||
}
|
||||
|
||||
auto border_radii_data = normalized_border_radii_data(inline_paintable.layout_node(), absolute_fragment_rect, top_left_border_radius, top_right_border_radius, bottom_right_border_radius, bottom_left_border_radius);
|
||||
fragment.set_border_radii_data(border_radii_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LayoutState::commit(Box& root)
|
||||
{
|
||||
// Only the top-level LayoutState should ever be committed.
|
||||
|
@ -309,6 +430,8 @@ void LayoutState::commit(Box& root)
|
|||
auto const& box = static_cast<Layout::Box const&>(used_values.node());
|
||||
measure_scrollable_overflow(box);
|
||||
}
|
||||
|
||||
resolve_border_radii();
|
||||
}
|
||||
|
||||
void LayoutState::UsedValues::set_node(NodeWithStyle& node, UsedValues const* containing_block_used_values)
|
||||
|
|
|
@ -186,6 +186,7 @@ struct LayoutState {
|
|||
|
||||
private:
|
||||
void resolve_relative_positions(Vector<Painting::PaintableWithLines&> const&);
|
||||
void resolve_border_radii();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <LibGfx/TextLayout.h>
|
||||
#include <LibJS/Heap/GCPtr.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
#include <LibWeb/Painting/BorderRadiiData.h>
|
||||
#include <LibWeb/PixelUnits.h>
|
||||
|
||||
namespace Web::Layout {
|
||||
|
@ -75,6 +76,9 @@ public:
|
|||
|
||||
Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run() const { return m_glyph_run; }
|
||||
|
||||
Painting::BorderRadiiData const& border_radii_data() const { return m_border_radii_data; }
|
||||
void set_border_radii_data(Painting::BorderRadiiData const& border_radii_data) { m_border_radii_data = border_radii_data; }
|
||||
|
||||
private:
|
||||
JS::NonnullGCPtr<Node const> m_layout_node;
|
||||
int m_start { 0 };
|
||||
|
@ -85,6 +89,7 @@ private:
|
|||
CSSPixels m_border_box_bottom { 0 };
|
||||
CSSPixels m_baseline { 0 };
|
||||
Vector<Gfx::DrawGlyphOrEmoji> m_glyph_run;
|
||||
Painting::BorderRadiiData m_border_radii_data;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -8,65 +8,11 @@
|
|||
|
||||
#include <AK/CircularQueue.h>
|
||||
#include <LibGfx/AntiAliasingPainter.h>
|
||||
#include <LibGfx/Painter.h>
|
||||
#include <LibGfx/Path.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/Layout/Node.h>
|
||||
#include <LibWeb/Painting/BorderPainting.h>
|
||||
#include <LibWeb/Painting/PaintContext.h>
|
||||
|
||||
namespace Web::Painting {
|
||||
|
||||
BorderRadiiData normalized_border_radii_data(Layout::Node const& node, CSSPixelRect const& rect, CSS::BorderRadiusData top_left_radius, CSS::BorderRadiusData top_right_radius, CSS::BorderRadiusData bottom_right_radius, CSS::BorderRadiusData bottom_left_radius)
|
||||
{
|
||||
BorderRadiusData bottom_left_radius_px {};
|
||||
BorderRadiusData bottom_right_radius_px {};
|
||||
BorderRadiusData top_left_radius_px {};
|
||||
BorderRadiusData top_right_radius_px {};
|
||||
|
||||
bottom_left_radius_px.horizontal_radius = bottom_left_radius.horizontal_radius.to_px(node, rect.width());
|
||||
bottom_right_radius_px.horizontal_radius = bottom_right_radius.horizontal_radius.to_px(node, rect.width());
|
||||
top_left_radius_px.horizontal_radius = top_left_radius.horizontal_radius.to_px(node, rect.width());
|
||||
top_right_radius_px.horizontal_radius = top_right_radius.horizontal_radius.to_px(node, rect.width());
|
||||
|
||||
bottom_left_radius_px.vertical_radius = bottom_left_radius.vertical_radius.to_px(node, rect.height());
|
||||
bottom_right_radius_px.vertical_radius = bottom_right_radius.vertical_radius.to_px(node, rect.height());
|
||||
top_left_radius_px.vertical_radius = top_left_radius.vertical_radius.to_px(node, rect.height());
|
||||
top_right_radius_px.vertical_radius = top_right_radius.vertical_radius.to_px(node, rect.height());
|
||||
|
||||
// Scale overlapping curves according to https://www.w3.org/TR/css-backgrounds-3/#corner-overlap
|
||||
// Let f = min(Li/Si), where i ∈ {top, right, bottom, left},
|
||||
// Si is the sum of the two corresponding radii of the corners on side i,
|
||||
// and Ltop = Lbottom = the width of the box, and Lleft = Lright = the height of the box.
|
||||
auto l_top = rect.width();
|
||||
auto l_bottom = l_top;
|
||||
auto l_left = rect.height();
|
||||
auto l_right = l_left;
|
||||
auto s_top = (top_left_radius_px.horizontal_radius + top_right_radius_px.horizontal_radius);
|
||||
auto s_right = (top_right_radius_px.vertical_radius + bottom_right_radius_px.vertical_radius);
|
||||
auto s_bottom = (bottom_left_radius_px.horizontal_radius + bottom_right_radius_px.horizontal_radius);
|
||||
auto s_left = (top_left_radius_px.vertical_radius + bottom_left_radius_px.vertical_radius);
|
||||
CSSPixelFraction f = 1;
|
||||
f = min(f, l_top / s_top);
|
||||
f = min(f, l_right / s_right);
|
||||
f = min(f, l_bottom / s_bottom);
|
||||
f = min(f, l_left / s_left);
|
||||
|
||||
// If f < 1, then all corner radii are reduced by multiplying them by f.
|
||||
if (f < 1) {
|
||||
top_left_radius_px.horizontal_radius *= f;
|
||||
top_left_radius_px.vertical_radius *= f;
|
||||
top_right_radius_px.horizontal_radius *= f;
|
||||
top_right_radius_px.vertical_radius *= f;
|
||||
bottom_right_radius_px.horizontal_radius *= f;
|
||||
bottom_right_radius_px.vertical_radius *= f;
|
||||
bottom_left_radius_px.horizontal_radius *= f;
|
||||
bottom_left_radius_px.vertical_radius *= f;
|
||||
}
|
||||
|
||||
return BorderRadiiData { top_left_radius_px, top_right_radius_px, bottom_right_radius_px, bottom_left_radius_px };
|
||||
}
|
||||
|
||||
static constexpr double dark_light_absolute_value_difference = 1. / 3;
|
||||
|
||||
static Color light_color_for_inset_and_outset(Color const& color)
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
namespace Web::Painting {
|
||||
|
||||
BorderRadiiData normalized_border_radii_data(Layout::Node const&, CSSPixelRect const&, CSS::BorderRadiusData top_left_radius, CSS::BorderRadiusData top_right_radius, CSS::BorderRadiusData bottom_right_radius, CSS::BorderRadiusData bottom_left_radius);
|
||||
|
||||
enum class BorderEdge {
|
||||
Top,
|
||||
Right,
|
||||
|
|
|
@ -34,10 +34,6 @@ void InlinePaintable::paint(PaintContext& context, PaintPhase phase) const
|
|||
auto& painter = context.recording_painter();
|
||||
|
||||
if (phase == PaintPhase::Background) {
|
||||
auto top_left_border_radius = computed_values().border_top_left_radius();
|
||||
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 = containing_block()->paintable_box()->absolute_position();
|
||||
|
||||
for_each_fragment([&](auto const& fragment, bool is_first_fragment, bool is_last_fragment) {
|
||||
|
@ -54,7 +50,7 @@ void InlinePaintable::paint(PaintContext& context, PaintPhase phase) const
|
|||
absolute_fragment_rect.set_width(absolute_fragment_rect.width() + extra_end_width);
|
||||
}
|
||||
|
||||
auto border_radii_data = normalized_border_radii_data(layout_node(), absolute_fragment_rect, top_left_border_radius, top_right_border_radius, bottom_right_border_radius, bottom_left_border_radius);
|
||||
auto const& border_radii_data = fragment.border_radii_data();
|
||||
paint_background(context, layout_node(), absolute_fragment_rect, computed_values().background_color(), computed_values().image_rendering(), &computed_values().background_layers(), border_radii_data);
|
||||
|
||||
if (auto computed_box_shadow = computed_values().box_shadow(); !computed_box_shadow.is_empty()) {
|
||||
|
@ -87,11 +83,6 @@ void InlinePaintable::paint(PaintContext& context, PaintPhase phase) const
|
|||
}
|
||||
|
||||
auto paint_border_or_outline = [&](Optional<BordersData> outline_data = {}, CSSPixels outline_offset = 0) {
|
||||
auto top_left_border_radius = computed_values().border_top_left_radius();
|
||||
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 borders_data = BordersData {
|
||||
.top = computed_values().border_top(),
|
||||
.right = computed_values().border_right(),
|
||||
|
@ -116,7 +107,7 @@ void InlinePaintable::paint(PaintContext& context, PaintPhase phase) const
|
|||
}
|
||||
|
||||
auto borders_rect = absolute_fragment_rect.inflated(borders_data.top.width, borders_data.right.width, borders_data.bottom.width, borders_data.left.width);
|
||||
auto border_radii_data = normalized_border_radii_data(layout_node(), borders_rect, top_left_border_radius, top_right_border_radius, bottom_right_border_radius, bottom_left_border_radius);
|
||||
auto border_radii_data = fragment.border_radii_data();
|
||||
|
||||
if (outline_data.has_value()) {
|
||||
auto outline_offset_x = outline_offset;
|
||||
|
|
|
@ -393,15 +393,10 @@ void PaintableBox::paint_box_shadow(PaintContext& context) const
|
|||
|
||||
BorderRadiiData PaintableBox::normalized_border_radii_data(ShrinkRadiiForBorders shrink) const
|
||||
{
|
||||
auto border_radius_data = Painting::normalized_border_radii_data(layout_box(),
|
||||
absolute_border_box_rect(),
|
||||
computed_values().border_top_left_radius(),
|
||||
computed_values().border_top_right_radius(),
|
||||
computed_values().border_bottom_right_radius(),
|
||||
computed_values().border_bottom_left_radius());
|
||||
auto border_radii_data = this->border_radii_data();
|
||||
if (shrink == ShrinkRadiiForBorders::Yes)
|
||||
border_radius_data.shrink(computed_values().border_top().width, computed_values().border_right().width, computed_values().border_bottom().width, computed_values().border_left().width);
|
||||
return border_radius_data;
|
||||
border_radii_data.shrink(computed_values().border_top().width, computed_values().border_right().width, computed_values().border_bottom().width, computed_values().border_left().width);
|
||||
return border_radii_data;
|
||||
}
|
||||
|
||||
Optional<CSSPixelRect> PaintableBox::calculate_overflow_clipped_rect() const
|
||||
|
|
|
@ -188,6 +188,9 @@ public:
|
|||
|
||||
BorderRadiiData normalized_border_radii_data(ShrinkRadiiForBorders shrink = ShrinkRadiiForBorders::No) const;
|
||||
|
||||
BorderRadiiData const& border_radii_data() const { return m_border_radii_data; }
|
||||
void set_border_radii_data(BorderRadiiData const& border_radii_data) { m_border_radii_data = border_radii_data; }
|
||||
|
||||
protected:
|
||||
explicit PaintableBox(Layout::Box const&);
|
||||
|
||||
|
@ -223,6 +226,8 @@ private:
|
|||
|
||||
Optional<BordersDataWithElementKind> m_override_borders_data;
|
||||
Optional<TableCellCoordinates> m_table_cell_coordinates;
|
||||
|
||||
BorderRadiiData m_border_radii_data;
|
||||
};
|
||||
|
||||
class PaintableWithLines : public PaintableBox {
|
||||
|
|
Loading…
Add table
Reference in a new issue