From 2cbbab8f73c34dcedf95cb236adcc9e6fb31cb56 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Fri, 4 Dec 2020 16:11:55 +0100 Subject: [PATCH] LibWeb: Compute the final border-style property before painting Instead of doing a CSS property lookup for the line style of each border edge during paint, we now cache the final CSS::LineStyle to use in the Layout::BorderData. --- Libraries/LibWeb/CSS/StyleProperties.cpp | 29 ++++++++++ Libraries/LibWeb/CSS/StyleProperties.h | 1 + Libraries/LibWeb/CSS/StyleValue.h | 13 +++++ Libraries/LibWeb/Layout/Box.cpp | 69 ++++++++++-------------- Libraries/LibWeb/Layout/Box.h | 2 +- Libraries/LibWeb/Layout/LayoutStyle.h | 1 + Libraries/LibWeb/Layout/Node.cpp | 17 +++--- 7 files changed, 82 insertions(+), 50 deletions(-) diff --git a/Libraries/LibWeb/CSS/StyleProperties.cpp b/Libraries/LibWeb/CSS/StyleProperties.cpp index 0f1db911bf0..386cf101f4d 100644 --- a/Libraries/LibWeb/CSS/StyleProperties.cpp +++ b/Libraries/LibWeb/CSS/StyleProperties.cpp @@ -259,6 +259,35 @@ Optional StyleProperties::white_space() const return {}; } +Optional StyleProperties::line_style(CSS::PropertyID property_id) const +{ + auto value = property(property_id); + if (!value.has_value() || !value.value()->is_string()) + return {}; + auto string = value.value()->to_string(); + if (string == "none") + return CSS::LineStyle::None; + if (string == "hidden") + return CSS::LineStyle::Hidden; + if (string == "dotted") + return CSS::LineStyle::Dotted; + if (string == "dashed") + return CSS::LineStyle::Dashed; + if (string == "solid") + return CSS::LineStyle::Solid; + if (string == "double") + return CSS::LineStyle::Double; + if (string == "groove") + return CSS::LineStyle::Groove; + if (string == "ridge") + return CSS::LineStyle::Ridge; + if (string == "inset") + return CSS::LineStyle::Inset; + if (string == "outset") + return CSS::LineStyle::Outset; + return {}; +} + Optional StyleProperties::float_() const { auto value = property(CSS::PropertyID::Float); diff --git a/Libraries/LibWeb/CSS/StyleProperties.h b/Libraries/LibWeb/CSS/StyleProperties.h index 8622fea9ca0..924575e6e36 100644 --- a/Libraries/LibWeb/CSS/StyleProperties.h +++ b/Libraries/LibWeb/CSS/StyleProperties.h @@ -64,6 +64,7 @@ public: CSS::Display display() const; Optional float_() const; Optional white_space() const; + Optional line_style(CSS::PropertyID) const; const Gfx::Font& font() const { diff --git a/Libraries/LibWeb/CSS/StyleValue.h b/Libraries/LibWeb/CSS/StyleValue.h index b21e57d8cc7..6d4d8cb953e 100644 --- a/Libraries/LibWeb/CSS/StyleValue.h +++ b/Libraries/LibWeb/CSS/StyleValue.h @@ -149,6 +149,19 @@ enum class Float { Right, }; +enum class LineStyle { + None, + Hidden, + Dotted, + Dashed, + Solid, + Double, + Groove, + Ridge, + Inset, + Outset, +}; + class StyleValue : public RefCounted { public: virtual ~StyleValue(); diff --git a/Libraries/LibWeb/Layout/Box.cpp b/Libraries/LibWeb/Layout/Box.cpp index 09d3ea4bbe9..3c0b151da0c 100644 --- a/Libraries/LibWeb/Layout/Box.cpp +++ b/Libraries/LibWeb/Layout/Box.cpp @@ -33,66 +33,53 @@ namespace Web::Layout { -void Box::paint_border(PaintContext& context, Edge edge, const Gfx::FloatRect& rect, CSS::PropertyID style_property_id, const BorderData& border_data) +void Box::paint_border(PaintContext& context, Edge edge, const Gfx::FloatRect& rect, const BorderData& border_data) { float width = border_data.width; if (width <= 0) return; auto color = border_data.color; - auto border_style = specified_style().property(style_property_id); + auto border_style = border_data.line_style; int int_width = max((int)width, 1); - auto first_point_for_edge = [](Edge edge, const Gfx::FloatRect& rect) { + struct Points { + Gfx::FloatPoint p1; + Gfx::FloatPoint p2; + }; + + auto points_for_edge = [](Edge edge, const Gfx::FloatRect& rect) -> Points { switch (edge) { case Edge::Top: - return rect.top_left(); + return { rect.top_left(), rect.top_right() }; case Edge::Right: - return rect.top_right(); + return { rect.top_right(), rect.bottom_right() }; case Edge::Bottom: - return rect.bottom_left(); - case Edge::Left: - default: - return rect.top_left(); + return { rect.bottom_left(), rect.bottom_right() }; + default: // Edge::Left + return { rect.top_left(), rect.bottom_left() }; } }; - auto second_point_for_edge = [](Edge edge, const Gfx::FloatRect& rect) { - switch (edge) { - case Edge::Top: - return rect.top_right(); - case Edge::Right: - return rect.bottom_right(); - case Edge::Bottom: - return rect.bottom_right(); - case Edge::Left: - default: - return rect.bottom_left(); - } - }; + auto [p1, p2] = points_for_edge(edge, rect); - auto p1 = first_point_for_edge(edge, rect); - auto p2 = second_point_for_edge(edge, rect); - - if (border_style.has_value() && border_style.value()->to_string() == "inset") { + if (border_style == CSS::LineStyle::Inset) { auto top_left_color = Color::from_rgb(0x5a5a5a); auto bottom_right_color = Color::from_rgb(0x888888); color = (edge == Edge::Left || edge == Edge::Top) ? top_left_color : bottom_right_color; - } else if (border_style.has_value() && border_style.value()->to_string() == "outset") { + } else if (border_style == CSS::LineStyle::Outset) { auto top_left_color = Color::from_rgb(0x888888); auto bottom_right_color = Color::from_rgb(0x5a5a5a); color = (edge == Edge::Left || edge == Edge::Top) ? top_left_color : bottom_right_color; } - auto line_style = Gfx::Painter::LineStyle::Solid; - if (border_style.has_value()) { - if (border_style.value()->to_string() == "dotted") - line_style = Gfx::Painter::LineStyle::Dotted; - if (border_style.value()->to_string() == "dashed") - line_style = Gfx::Painter::LineStyle::Dashed; - } + auto gfx_line_style = Gfx::Painter::LineStyle::Solid; + if (border_style == CSS::LineStyle::Dotted) + gfx_line_style = Gfx::Painter::LineStyle::Dotted; + if (border_style == CSS::LineStyle::Dashed) + gfx_line_style = Gfx::Painter::LineStyle::Dashed; - if (line_style != Gfx::Painter::LineStyle::Solid) { + if (gfx_line_style != Gfx::Painter::LineStyle::Solid) { switch (edge) { case Edge::Top: p1.move_by(int_width / 2, int_width / 2); @@ -111,12 +98,12 @@ void Box::paint_border(PaintContext& context, Edge edge, const Gfx::FloatRect& r p2.move_by(int_width / 2, -int_width / 2); break; } - context.painter().draw_line({ (int)p1.x(), (int)p1.y() }, { (int)p2.x(), (int)p2.y() }, color, int_width, line_style); + context.painter().draw_line({ (int)p1.x(), (int)p1.y() }, { (int)p2.x(), (int)p2.y() }, color, int_width, gfx_line_style); return; } auto draw_line = [&](auto& p1, auto& p2) { - context.painter().draw_line({ (int)p1.x(), (int)p1.y() }, { (int)p2.x(), (int)p2.y() }, color, 1, line_style); + context.painter().draw_line({ (int)p1.x(), (int)p1.y() }, { (int)p2.x(), (int)p2.y() }, color, 1, gfx_line_style); }; float p1_step = 0; @@ -200,10 +187,10 @@ void Box::paint(PaintContext& context, PaintPhase phase) bordered_rect.set_y(padded_rect.y() - box_model().border.top.to_px(*this)); bordered_rect.set_height(padded_rect.height() + box_model().border.top.to_px(*this) + box_model().border.bottom.to_px(*this)); - paint_border(context, Edge::Left, bordered_rect, CSS::PropertyID::BorderLeftStyle, style().border_left()); - paint_border(context, Edge::Right, bordered_rect, CSS::PropertyID::BorderRightStyle, style().border_right()); - paint_border(context, Edge::Top, bordered_rect, CSS::PropertyID::BorderTopStyle, style().border_top()); - paint_border(context, Edge::Bottom, bordered_rect, CSS::PropertyID::BorderBottomStyle, style().border_bottom()); + paint_border(context, Edge::Left, bordered_rect, style().border_left()); + paint_border(context, Edge::Right, bordered_rect, style().border_right()); + paint_border(context, Edge::Top, bordered_rect, style().border_top()); + paint_border(context, Edge::Bottom, bordered_rect, style().border_bottom()); } Layout::NodeWithStyleAndBoxModelMetrics::paint(context, phase); diff --git a/Libraries/LibWeb/Layout/Box.h b/Libraries/LibWeb/Layout/Box.h index 0354938e057..0fa084ec7bf 100644 --- a/Libraries/LibWeb/Layout/Box.h +++ b/Libraries/LibWeb/Layout/Box.h @@ -98,7 +98,7 @@ private: Bottom, Left, }; - void paint_border(PaintContext&, Edge, const Gfx::FloatRect&, CSS::PropertyID style_property_id, const BorderData&); + void paint_border(PaintContext&, Edge, const Gfx::FloatRect&, const BorderData&); Gfx::FloatPoint m_offset; Gfx::FloatSize m_size; diff --git a/Libraries/LibWeb/Layout/LayoutStyle.h b/Libraries/LibWeb/Layout/LayoutStyle.h index 2c0c4bb96e2..dba8290de4f 100644 --- a/Libraries/LibWeb/Layout/LayoutStyle.h +++ b/Libraries/LibWeb/Layout/LayoutStyle.h @@ -41,6 +41,7 @@ public: struct BorderData { public: Color color { Color::Transparent }; + CSS::LineStyle line_style { CSS::LineStyle::None }; float width { 0 }; }; diff --git a/Libraries/LibWeb/Layout/Node.cpp b/Libraries/LibWeb/Layout/Node.cpp index fa196022b3f..1014c05ea8a 100644 --- a/Libraries/LibWeb/Layout/Node.cpp +++ b/Libraries/LibWeb/Layout/Node.cpp @@ -241,15 +241,16 @@ void NodeWithStyle::apply_style(const CSS::StyleProperties& specified_style) style.set_margin(specified_style.length_box(CSS::PropertyID::MarginLeft, CSS::PropertyID::MarginTop, CSS::PropertyID::MarginRight, CSS::PropertyID::MarginBottom)); style.set_padding(specified_style.length_box(CSS::PropertyID::PaddingLeft, CSS::PropertyID::PaddingTop, CSS::PropertyID::PaddingRight, CSS::PropertyID::PaddingBottom)); - style.border_left().width = specified_style.length_or_fallback(CSS::PropertyID::BorderLeftWidth, {}).resolved_or_zero(*this, 0).to_px(*this); - style.border_top().width = specified_style.length_or_fallback(CSS::PropertyID::BorderTopWidth, {}).resolved_or_zero(*this, 0).to_px(*this); - style.border_right().width = specified_style.length_or_fallback(CSS::PropertyID::BorderRightWidth, {}).resolved_or_zero(*this, 0).to_px(*this); - style.border_bottom().width = specified_style.length_or_fallback(CSS::PropertyID::BorderBottomWidth, {}).resolved_or_zero(*this, 0).to_px(*this); + auto do_border_style = [&](BorderData& border, CSS::PropertyID width_property, CSS::PropertyID color_property, CSS::PropertyID style_property) { + border.width = specified_style.length_or_fallback(width_property, {}).resolved_or_zero(*this, 0).to_px(*this); + border.color = specified_style.color_or_fallback(color_property, document(), Color::Transparent); + border.line_style = specified_style.line_style(style_property).value_or(CSS::LineStyle::None); + }; - style.border_left().color = specified_style.color_or_fallback(CSS::PropertyID::BorderLeftColor, document(), Color::Transparent); - style.border_top().color = specified_style.color_or_fallback(CSS::PropertyID::BorderTopColor, document(), Color::Transparent); - style.border_right().color = specified_style.color_or_fallback(CSS::PropertyID::BorderRightColor, document(), Color::Transparent); - style.border_bottom().color = specified_style.color_or_fallback(CSS::PropertyID::BorderBottomColor, document(), Color::Transparent); + do_border_style(style.border_left(), CSS::PropertyID::BorderLeftWidth, CSS::PropertyID::BorderLeftColor, CSS::PropertyID::BorderLeftStyle); + do_border_style(style.border_top(), CSS::PropertyID::BorderTopWidth, CSS::PropertyID::BorderTopColor, CSS::PropertyID::BorderTopStyle); + do_border_style(style.border_right(), CSS::PropertyID::BorderRightWidth, CSS::PropertyID::BorderRightColor, CSS::PropertyID::BorderRightStyle); + do_border_style(style.border_bottom(), CSS::PropertyID::BorderBottomWidth, CSS::PropertyID::BorderBottomColor, CSS::PropertyID::BorderBottomStyle); } void Node::handle_mousedown(Badge, const Gfx::IntPoint&, unsigned, unsigned)