From b9afea40e6d1374e34b054834ff140fdad2a1b9c Mon Sep 17 00:00:00 2001 From: MacDue Date: Sun, 3 Mar 2024 20:15:06 +0000 Subject: [PATCH] LibWeb: Update SVG `get_path()` API to take a viewport size This will allow resolving paths that use sizes that are relative to the viewport. This necessarily removes the on element caching, which has been redundant for a while as computed paths are stored on the paintable. --- .../LibWeb/Layout/SVGFormattingContext.cpp | 41 +++++++++---------- .../Libraries/LibWeb/SVG/SVGCircleElement.cpp | 14 ++----- .../Libraries/LibWeb/SVG/SVGCircleElement.h | 4 +- .../LibWeb/SVG/SVGEllipseElement.cpp | 18 ++------ .../Libraries/LibWeb/SVG/SVGEllipseElement.h | 4 +- .../Libraries/LibWeb/SVG/SVGGeometryElement.h | 2 +- .../Libraries/LibWeb/SVG/SVGLineElement.cpp | 12 +----- .../Libraries/LibWeb/SVG/SVGLineElement.h | 4 +- .../Libraries/LibWeb/SVG/SVGPathElement.cpp | 11 ++--- .../Libraries/LibWeb/SVG/SVGPathElement.h | 3 +- .../LibWeb/SVG/SVGPolygonElement.cpp | 18 +++----- .../Libraries/LibWeb/SVG/SVGPolygonElement.h | 4 +- .../LibWeb/SVG/SVGPolylineElement.cpp | 18 +++----- .../Libraries/LibWeb/SVG/SVGPolylineElement.h | 4 +- .../Libraries/LibWeb/SVG/SVGRectElement.cpp | 20 ++------- .../Libraries/LibWeb/SVG/SVGRectElement.h | 4 +- 16 files changed, 55 insertions(+), 126 deletions(-) diff --git a/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.cpp index 7ee092c839c..29868077129 100644 --- a/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/SVGFormattingContext.cpp @@ -262,31 +262,30 @@ void SVGFormattingContext::run(Box const& box, LayoutMode layout_mode, Available return IterationDecision::Continue; }); + auto viewport_width = [&] { + if (viewbox.has_value()) + return CSSPixels::nearest_value_for(viewbox->width); + if (svg_box_state.has_definite_width()) + return svg_box_state.content_width(); + dbgln_if(LIBWEB_CSS_DEBUG, "FIXME: Failed to resolve width of SVG viewport!"); + return CSSPixels {}; + }(); + + auto viewport_height = [&] { + if (viewbox.has_value()) + return CSSPixels::nearest_value_for(viewbox->height); + if (svg_box_state.has_definite_height()) + return svg_box_state.content_height(); + dbgln_if(LIBWEB_CSS_DEBUG, "FIXME: Failed to resolve height of SVG viewport!"); + return CSSPixels {}; + }(); + for_each_in_subtree(box, [&](Node const& descendant) { if (is(descendant.dom_node())) { // Layout for a nested SVG viewport. // https://svgwg.org/svg2-draft/coords.html#EstablishingANewSVGViewport. SVGFormattingContext nested_context(m_state, static_cast(descendant), this, viewbox_transform); auto& nested_viewport_state = m_state.get_mutable(static_cast(descendant)); - - auto viewport_width = [&] { - if (viewbox.has_value()) - return CSSPixels::nearest_value_for(viewbox->width); - if (svg_box_state.has_definite_width()) - return svg_box_state.content_width(); - dbgln_if(LIBWEB_CSS_DEBUG, "FIXME: Failed to resolve width of SVG viewport!"); - return CSSPixels {}; - }(); - - auto viewport_height = [&] { - if (viewbox.has_value()) - return CSSPixels::nearest_value_for(viewbox->height); - if (svg_box_state.has_definite_height()) - return svg_box_state.content_height(); - dbgln_if(LIBWEB_CSS_DEBUG, "FIXME: Failed to resolve height of SVG viewport!"); - return CSSPixels {}; - }(); - auto resolve_dimension = [](auto& node, auto size, auto reference_value) { // The value auto for width and height on the ‘svg’ element is treated as 100%. // https://svgwg.org/svg2-draft/geometry.html#Sizing @@ -318,7 +317,7 @@ void SVGFormattingContext::run(Box const& box, LayoutMode layout_mode, Available Gfx::Path path; if (is(descendant)) { - path = static_cast(dom_node).get_path(); + path = static_cast(dom_node).get_path({ viewport_width, viewport_height }); } else if (is(descendant)) { auto& text_element = static_cast(dom_node); @@ -363,7 +362,7 @@ void SVGFormattingContext::run(Box const& box, LayoutMode layout_mode, Available auto text_contents = text_path_element.text_contents(); Utf8View text_utf8 { text_contents }; - auto shape_path = const_cast(*path_or_shape).get_path(); + auto shape_path = const_cast(*path_or_shape).get_path({ viewport_width, viewport_height }); path = shape_path.place_text_along(text_utf8, font); } diff --git a/Userland/Libraries/LibWeb/SVG/SVGCircleElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGCircleElement.cpp index 7e1f1cbb731..cecc1a52e75 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGCircleElement.cpp +++ b/Userland/Libraries/LibWeb/SVG/SVGCircleElement.cpp @@ -40,11 +40,8 @@ void SVGCircleElement::attribute_changed(FlyString const& name, Optional } } -Gfx::Path& SVGCircleElement::get_path() +Gfx::Path SVGCircleElement::get_path(CSSPixelSize viewport_size) { - if (m_path.has_value()) - return m_path.value(); - float cx = m_center_x.value_or(0); float cy = m_center_y.value_or(0); float r = m_radius.value_or(0); @@ -52,10 +49,8 @@ Gfx::Path& SVGCircleElement::get_path() Gfx::Path path; // A zero radius disables rendering. - if (r == 0) { - m_path = move(path); - return m_path.value(); - } + if (r == 0) + return {}; bool large_arc = false; bool sweep = true; @@ -75,8 +70,7 @@ Gfx::Path& SVGCircleElement::get_path() // 5. arc with a segment-completing close path operation. path.arc_to({ cx + r, cy }, r, large_arc, sweep); - m_path = move(path); - return m_path.value(); + return path; } // https://www.w3.org/TR/SVG11/shapes.html#CircleElementCXAttribute diff --git a/Userland/Libraries/LibWeb/SVG/SVGCircleElement.h b/Userland/Libraries/LibWeb/SVG/SVGCircleElement.h index 06a334c1608..9887153ad0c 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGCircleElement.h +++ b/Userland/Libraries/LibWeb/SVG/SVGCircleElement.h @@ -20,7 +20,7 @@ public: virtual void attribute_changed(FlyString const& name, Optional const& value) override; - virtual Gfx::Path& get_path() override; + virtual Gfx::Path get_path(CSSPixelSize viewport_size) override; JS::NonnullGCPtr cx() const; JS::NonnullGCPtr cy() const; @@ -31,8 +31,6 @@ private: virtual void initialize(JS::Realm&) override; - Optional m_path; - Optional m_center_x; Optional m_center_y; Optional m_radius; diff --git a/Userland/Libraries/LibWeb/SVG/SVGEllipseElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGEllipseElement.cpp index d5dd6c9c6db..a0efafc833c 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGEllipseElement.cpp +++ b/Userland/Libraries/LibWeb/SVG/SVGEllipseElement.cpp @@ -30,24 +30,17 @@ void SVGEllipseElement::attribute_changed(FlyString const& name, Optional const& value) override; - virtual Gfx::Path& get_path() override; + virtual Gfx::Path get_path(CSSPixelSize viewport_size) override; JS::NonnullGCPtr cx() const; JS::NonnullGCPtr cy() const; @@ -32,8 +32,6 @@ private: virtual void initialize(JS::Realm&) override; - Optional m_path; - Optional m_center_x; Optional m_center_y; Optional m_radius_x; diff --git a/Userland/Libraries/LibWeb/SVG/SVGGeometryElement.h b/Userland/Libraries/LibWeb/SVG/SVGGeometryElement.h index 3a32b82e1cd..5d39a0840ac 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGGeometryElement.h +++ b/Userland/Libraries/LibWeb/SVG/SVGGeometryElement.h @@ -18,7 +18,7 @@ class SVGGeometryElement : public SVGGraphicsElement { public: virtual JS::GCPtr create_layout_node(NonnullRefPtr) override; - virtual Gfx::Path& get_path() = 0; + virtual Gfx::Path get_path(CSSPixelSize viewport_size) = 0; float get_total_length(); JS::NonnullGCPtr get_point_at_length(float distance); diff --git a/Userland/Libraries/LibWeb/SVG/SVGLineElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGLineElement.cpp index 1647599e987..55998a3b6a6 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGLineElement.cpp +++ b/Userland/Libraries/LibWeb/SVG/SVGLineElement.cpp @@ -30,24 +30,17 @@ void SVGLineElement::attribute_changed(FlyString const& name, Optional c if (name == SVG::AttributeNames::x1) { m_x1 = AttributeParser::parse_coordinate(value.value_or(String {})); - m_path.clear(); } else if (name == SVG::AttributeNames::y1) { m_y1 = AttributeParser::parse_coordinate(value.value_or(String {})); - m_path.clear(); } else if (name == SVG::AttributeNames::x2) { m_x2 = AttributeParser::parse_coordinate(value.value_or(String {})); - m_path.clear(); } else if (name == SVG::AttributeNames::y2) { m_y2 = AttributeParser::parse_coordinate(value.value_or(String {})); - m_path.clear(); } } -Gfx::Path& SVGLineElement::get_path() +Gfx::Path SVGLineElement::get_path(CSSPixelSize) { - if (m_path.has_value()) - return m_path.value(); - Gfx::Path path; float x1 = m_x1.value_or(0); float y1 = m_y1.value_or(0); @@ -60,8 +53,7 @@ Gfx::Path& SVGLineElement::get_path() // 2. perform an absolute lineto operation to absolute location (x2,y2) path.line_to({ x2, y2 }); - m_path = move(path); - return m_path.value(); + return path; } // https://www.w3.org/TR/SVG11/shapes.html#LineElementX1Attribute diff --git a/Userland/Libraries/LibWeb/SVG/SVGLineElement.h b/Userland/Libraries/LibWeb/SVG/SVGLineElement.h index c7fa5b3de68..62087b3bb63 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGLineElement.h +++ b/Userland/Libraries/LibWeb/SVG/SVGLineElement.h @@ -20,7 +20,7 @@ public: virtual void attribute_changed(FlyString const& name, Optional const& value) override; - virtual Gfx::Path& get_path() override; + virtual Gfx::Path get_path(CSSPixelSize viewport_size) override; JS::NonnullGCPtr x1() const; JS::NonnullGCPtr y1() const; @@ -32,8 +32,6 @@ private: virtual void initialize(JS::Realm&) override; - Optional m_path; - Optional m_x1; Optional m_y1; Optional m_x2; diff --git a/Userland/Libraries/LibWeb/SVG/SVGPathElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGPathElement.cpp index 6858badda37..b5685a7f1f7 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGPathElement.cpp +++ b/Userland/Libraries/LibWeb/SVG/SVGPathElement.cpp @@ -99,10 +99,8 @@ void SVGPathElement::attribute_changed(FlyString const& name, Optional c { SVGGeometryElement::attribute_changed(name, value); - if (name == "d") { + if (name == "d") m_instructions = AttributeParser::parse_path_data(value.value_or(String {})); - m_path.clear(); - } } Gfx::Path path_from_path_instructions(ReadonlySpan instructions) @@ -273,12 +271,9 @@ Gfx::Path path_from_path_instructions(ReadonlySpan instructions return path; } -Gfx::Path& SVGPathElement::get_path() +Gfx::Path SVGPathElement::get_path(CSSPixelSize) { - if (!m_path.has_value()) { - m_path = path_from_path_instructions(m_instructions); - } - return m_path.value(); + return path_from_path_instructions(m_instructions); } } diff --git a/Userland/Libraries/LibWeb/SVG/SVGPathElement.h b/Userland/Libraries/LibWeb/SVG/SVGPathElement.h index 00e90fd1ec2..286b236903b 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGPathElement.h +++ b/Userland/Libraries/LibWeb/SVG/SVGPathElement.h @@ -22,7 +22,7 @@ public: virtual void attribute_changed(FlyString const& name, Optional const& value) override; - virtual Gfx::Path& get_path() override; + virtual Gfx::Path get_path(CSSPixelSize viewport_size) override; private: SVGPathElement(DOM::Document&, DOM::QualifiedName); @@ -30,7 +30,6 @@ private: virtual void initialize(JS::Realm&) override; Vector m_instructions; - Optional m_path; }; Gfx::Path path_from_path_instructions(ReadonlySpan); diff --git a/Userland/Libraries/LibWeb/SVG/SVGPolygonElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGPolygonElement.cpp index 69172f72115..41c476c15a4 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGPolygonElement.cpp +++ b/Userland/Libraries/LibWeb/SVG/SVGPolygonElement.cpp @@ -28,23 +28,16 @@ void SVGPolygonElement::attribute_changed(FlyString const& name, Optional const& value) override; - virtual Gfx::Path& get_path() override; + virtual Gfx::Path get_path(CSSPixelSize viewport_size) override; private: SVGPolygonElement(DOM::Document&, DOM::QualifiedName); virtual void initialize(JS::Realm&) override; - Optional m_path; - Vector m_points; }; diff --git a/Userland/Libraries/LibWeb/SVG/SVGPolylineElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGPolylineElement.cpp index c6644366dae..0e282d917d3 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGPolylineElement.cpp +++ b/Userland/Libraries/LibWeb/SVG/SVGPolylineElement.cpp @@ -28,23 +28,16 @@ void SVGPolylineElement::attribute_changed(FlyString const& name, Optional const& value) override; - virtual Gfx::Path& get_path() override; + virtual Gfx::Path get_path(CSSPixelSize viewport_size) override; private: SVGPolylineElement(DOM::Document&, DOM::QualifiedName); virtual void initialize(JS::Realm&) override; - Optional m_path; - Vector m_points; }; diff --git a/Userland/Libraries/LibWeb/SVG/SVGRectElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGRectElement.cpp index 03967661f77..c352a91ebac 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGRectElement.cpp +++ b/Userland/Libraries/LibWeb/SVG/SVGRectElement.cpp @@ -32,30 +32,21 @@ void SVGRectElement::attribute_changed(FlyString const& name, Optional c if (name == SVG::AttributeNames::x) { m_x = AttributeParser::parse_coordinate(value.value_or(String {})); - m_path.clear(); } else if (name == SVG::AttributeNames::y) { m_y = AttributeParser::parse_coordinate(value.value_or(String {})); - m_path.clear(); } else if (name == SVG::AttributeNames::width) { m_width = AttributeParser::parse_positive_length(value.value_or(String {})); - m_path.clear(); } else if (name == SVG::AttributeNames::height) { m_height = AttributeParser::parse_positive_length(value.value_or(String {})); - m_path.clear(); } else if (name == SVG::AttributeNames::rx) { m_radius_x = AttributeParser::parse_length(value.value_or(String {})); - m_path.clear(); } else if (name == SVG::AttributeNames::ry) { m_radius_y = AttributeParser::parse_length(value.value_or(String {})); - m_path.clear(); } } -Gfx::Path& SVGRectElement::get_path() +Gfx::Path SVGRectElement::get_path(CSSPixelSize) { - if (m_path.has_value()) - return m_path.value(); - float width = m_width.value_or(0); float height = m_height.value_or(0); float x = m_x.value_or(0); @@ -63,10 +54,8 @@ Gfx::Path& SVGRectElement::get_path() Gfx::Path path; // If width or height is zero, rendering is disabled. - if (width == 0 && height == 0) { - m_path = move(path); - return m_path.value(); - } + if (width == 0 || height == 0) + return path; auto corner_radii = calculate_used_corner_radius_values(); float rx = corner_radii.width(); @@ -116,8 +105,7 @@ Gfx::Path& SVGRectElement::get_path() if (rx > 0 && ry > 0) path.elliptical_arc_to({ x + rx, y }, corner_radii, x_axis_rotation, large_arc_flag, sweep_flag); - m_path = move(path); - return m_path.value(); + return path; } Gfx::FloatSize SVGRectElement::calculate_used_corner_radius_values() const diff --git a/Userland/Libraries/LibWeb/SVG/SVGRectElement.h b/Userland/Libraries/LibWeb/SVG/SVGRectElement.h index 05caf1c3741..f6bc9ddfe2b 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGRectElement.h +++ b/Userland/Libraries/LibWeb/SVG/SVGRectElement.h @@ -20,7 +20,7 @@ public: virtual void attribute_changed(FlyString const& name, Optional const& value) override; - virtual Gfx::Path& get_path() override; + virtual Gfx::Path get_path(CSSPixelSize viewport_size) override; JS::NonnullGCPtr x() const; JS::NonnullGCPtr y() const; @@ -36,8 +36,6 @@ private: Gfx::FloatSize calculate_used_corner_radius_values() const; - Optional m_path; - Optional m_x; Optional m_y; Optional m_width;