Ver Fonte

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.
MacDue há 1 ano atrás
pai
commit
b9afea40e6

+ 20 - 21
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<SVG::SVGViewport>(descendant.dom_node())) {
             // Layout for a nested SVG viewport.
             // https://svgwg.org/svg2-draft/coords.html#EstablishingANewSVGViewport.
             SVGFormattingContext nested_context(m_state, static_cast<Box const&>(descendant), this, viewbox_transform);
             auto& nested_viewport_state = m_state.get_mutable(static_cast<Box const&>(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<SVGGeometryBox>(descendant)) {
-                path = static_cast<SVG::SVGGeometryElement&>(dom_node).get_path();
+                path = static_cast<SVG::SVGGeometryElement&>(dom_node).get_path({ viewport_width, viewport_height });
             } else if (is<SVGTextBox>(descendant)) {
                 auto& text_element = static_cast<SVG::SVGTextPositioningElement&>(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<SVG::SVGGeometryElement&>(*path_or_shape).get_path();
+                auto shape_path = const_cast<SVG::SVGGeometryElement&>(*path_or_shape).get_path({ viewport_width, viewport_height });
                 path = shape_path.place_text_along(text_utf8, font);
             }
 

+ 4 - 10
Userland/Libraries/LibWeb/SVG/SVGCircleElement.cpp

@@ -40,11 +40,8 @@ void SVGCircleElement::attribute_changed(FlyString const& name, Optional<String>
     }
 }
 
-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

+ 1 - 3
Userland/Libraries/LibWeb/SVG/SVGCircleElement.h

@@ -20,7 +20,7 @@ public:
 
     virtual void attribute_changed(FlyString const& name, Optional<String> const& value) override;
 
-    virtual Gfx::Path& get_path() override;
+    virtual Gfx::Path get_path(CSSPixelSize viewport_size) override;
 
     JS::NonnullGCPtr<SVGAnimatedLength> cx() const;
     JS::NonnullGCPtr<SVGAnimatedLength> cy() const;
@@ -31,8 +31,6 @@ private:
 
     virtual void initialize(JS::Realm&) override;
 
-    Optional<Gfx::Path> m_path;
-
     Optional<float> m_center_x;
     Optional<float> m_center_y;
     Optional<float> m_radius;

+ 4 - 14
Userland/Libraries/LibWeb/SVG/SVGEllipseElement.cpp

@@ -30,24 +30,17 @@ void SVGEllipseElement::attribute_changed(FlyString const& name, Optional<String
 
     if (name == SVG::AttributeNames::cx) {
         m_center_x = AttributeParser::parse_coordinate(value.value_or(String {}));
-        m_path.clear();
     } else if (name == SVG::AttributeNames::cy) {
         m_center_y = AttributeParser::parse_coordinate(value.value_or(String {}));
-        m_path.clear();
     } else if (name == SVG::AttributeNames::rx) {
         m_radius_x = AttributeParser::parse_positive_length(value.value_or(String {}));
-        m_path.clear();
     } else if (name == SVG::AttributeNames::ry) {
         m_radius_y = AttributeParser::parse_positive_length(value.value_or(String {}));
-        m_path.clear();
     }
 }
 
-Gfx::Path& SVGEllipseElement::get_path()
+Gfx::Path SVGEllipseElement::get_path(CSSPixelSize)
 {
-    if (m_path.has_value())
-        return m_path.value();
-
     float rx = m_radius_x.value_or(0);
     float ry = m_radius_y.value_or(0);
     float cx = m_center_x.value_or(0);
@@ -55,10 +48,8 @@ Gfx::Path& SVGEllipseElement::get_path()
     Gfx::Path path;
 
     // A computed value of zero for either dimension, or a computed value of auto for both dimensions, disables rendering of the element.
-    if (rx == 0 || ry == 0) {
-        m_path = move(path);
-        return m_path.value();
-    }
+    if (rx == 0 || ry == 0)
+        return path;
 
     Gfx::FloatSize radii = { rx, ry };
     double x_axis_rotation = 0;
@@ -80,8 +71,7 @@ Gfx::Path& SVGEllipseElement::get_path()
     // 5. arc with a segment-completing close path operation.
     path.elliptical_arc_to({ cx + rx, cy }, radii, x_axis_rotation, large_arc, sweep);
 
-    m_path = move(path);
-    return m_path.value();
+    return path;
 }
 
 // https://www.w3.org/TR/SVG11/shapes.html#EllipseElementCXAttribute

+ 1 - 3
Userland/Libraries/LibWeb/SVG/SVGEllipseElement.h

@@ -20,7 +20,7 @@ public:
 
     virtual void attribute_changed(FlyString const& name, Optional<String> const& value) override;
 
-    virtual Gfx::Path& get_path() override;
+    virtual Gfx::Path get_path(CSSPixelSize viewport_size) override;
 
     JS::NonnullGCPtr<SVGAnimatedLength> cx() const;
     JS::NonnullGCPtr<SVGAnimatedLength> cy() const;
@@ -32,8 +32,6 @@ private:
 
     virtual void initialize(JS::Realm&) override;
 
-    Optional<Gfx::Path> m_path;
-
     Optional<float> m_center_x;
     Optional<float> m_center_y;
     Optional<float> m_radius_x;

+ 1 - 1
Userland/Libraries/LibWeb/SVG/SVGGeometryElement.h

@@ -18,7 +18,7 @@ class SVGGeometryElement : public SVGGraphicsElement {
 public:
     virtual JS::GCPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>) override;
 
-    virtual Gfx::Path& get_path() = 0;
+    virtual Gfx::Path get_path(CSSPixelSize viewport_size) = 0;
 
     float get_total_length();
     JS::NonnullGCPtr<Geometry::DOMPoint> get_point_at_length(float distance);

+ 2 - 10
Userland/Libraries/LibWeb/SVG/SVGLineElement.cpp

@@ -30,24 +30,17 @@ void SVGLineElement::attribute_changed(FlyString const& name, Optional<String> 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

+ 1 - 3
Userland/Libraries/LibWeb/SVG/SVGLineElement.h

@@ -20,7 +20,7 @@ public:
 
     virtual void attribute_changed(FlyString const& name, Optional<String> const& value) override;
 
-    virtual Gfx::Path& get_path() override;
+    virtual Gfx::Path get_path(CSSPixelSize viewport_size) override;
 
     JS::NonnullGCPtr<SVGAnimatedLength> x1() const;
     JS::NonnullGCPtr<SVGAnimatedLength> y1() const;
@@ -32,8 +32,6 @@ private:
 
     virtual void initialize(JS::Realm&) override;
 
-    Optional<Gfx::Path> m_path;
-
     Optional<float> m_x1;
     Optional<float> m_y1;
     Optional<float> m_x2;

+ 3 - 8
Userland/Libraries/LibWeb/SVG/SVGPathElement.cpp

@@ -99,10 +99,8 @@ void SVGPathElement::attribute_changed(FlyString const& name, Optional<String> 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<PathInstruction> instructions)
@@ -273,12 +271,9 @@ Gfx::Path path_from_path_instructions(ReadonlySpan<PathInstruction> 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);
 }
 
 }

+ 1 - 2
Userland/Libraries/LibWeb/SVG/SVGPathElement.h

@@ -22,7 +22,7 @@ public:
 
     virtual void attribute_changed(FlyString const& name, Optional<String> 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<PathInstruction> m_instructions;
-    Optional<Gfx::Path> m_path;
 };
 
 Gfx::Path path_from_path_instructions(ReadonlySpan<PathInstruction>);

+ 5 - 13
Userland/Libraries/LibWeb/SVG/SVGPolygonElement.cpp

@@ -28,23 +28,16 @@ void SVGPolygonElement::attribute_changed(FlyString const& name, Optional<String
 {
     SVGGeometryElement::attribute_changed(name, value);
 
-    if (name == SVG::AttributeNames::points) {
+    if (name == SVG::AttributeNames::points)
         m_points = AttributeParser::parse_points(value.value_or(String {}));
-        m_path.clear();
-    }
 }
 
-Gfx::Path& SVGPolygonElement::get_path()
+Gfx::Path SVGPolygonElement::get_path(CSSPixelSize)
 {
-    if (m_path.has_value())
-        return m_path.value();
-
     Gfx::Path path;
 
-    if (m_points.is_empty()) {
-        m_path = move(path);
-        return m_path.value();
-    }
+    if (m_points.is_empty())
+        return path;
 
     // 1. perform an absolute moveto operation to the first coordinate pair in the list of points
     path.move_to(m_points.first());
@@ -56,8 +49,7 @@ Gfx::Path& SVGPolygonElement::get_path()
     // 3. perform a closepath command
     path.close();
 
-    m_path = move(path);
-    return m_path.value();
+    return path;
 }
 
 }

+ 1 - 3
Userland/Libraries/LibWeb/SVG/SVGPolygonElement.h

@@ -19,15 +19,13 @@ public:
 
     virtual void attribute_changed(FlyString const& name, Optional<String> 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<Gfx::Path> m_path;
-
     Vector<Gfx::FloatPoint> m_points;
 };
 

+ 5 - 13
Userland/Libraries/LibWeb/SVG/SVGPolylineElement.cpp

@@ -28,23 +28,16 @@ void SVGPolylineElement::attribute_changed(FlyString const& name, Optional<Strin
 {
     SVGGeometryElement::attribute_changed(name, value);
 
-    if (name == SVG::AttributeNames::points) {
+    if (name == SVG::AttributeNames::points)
         m_points = AttributeParser::parse_points(value.value_or(String {}));
-        m_path.clear();
-    }
 }
 
-Gfx::Path& SVGPolylineElement::get_path()
+Gfx::Path SVGPolylineElement::get_path(CSSPixelSize)
 {
-    if (m_path.has_value())
-        return m_path.value();
-
     Gfx::Path path;
 
-    if (m_points.is_empty()) {
-        m_path = move(path);
-        return m_path.value();
-    }
+    if (m_points.is_empty())
+        return path;
 
     // 1. perform an absolute moveto operation to the first coordinate pair in the list of points
     path.move_to(m_points.first());
@@ -53,8 +46,7 @@ Gfx::Path& SVGPolylineElement::get_path()
     for (size_t point_index = 1; point_index < m_points.size(); ++point_index)
         path.line_to(m_points[point_index]);
 
-    m_path = move(path);
-    return m_path.value();
+    return path;
 }
 
 }

+ 1 - 3
Userland/Libraries/LibWeb/SVG/SVGPolylineElement.h

@@ -19,15 +19,13 @@ public:
 
     virtual void attribute_changed(FlyString const& name, Optional<String> 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<Gfx::Path> m_path;
-
     Vector<Gfx::FloatPoint> m_points;
 };
 

+ 4 - 16
Userland/Libraries/LibWeb/SVG/SVGRectElement.cpp

@@ -32,30 +32,21 @@ void SVGRectElement::attribute_changed(FlyString const& name, Optional<String> 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

+ 1 - 3
Userland/Libraries/LibWeb/SVG/SVGRectElement.h

@@ -20,7 +20,7 @@ public:
 
     virtual void attribute_changed(FlyString const& name, Optional<String> const& value) override;
 
-    virtual Gfx::Path& get_path() override;
+    virtual Gfx::Path get_path(CSSPixelSize viewport_size) override;
 
     JS::NonnullGCPtr<SVGAnimatedLength> x() const;
     JS::NonnullGCPtr<SVGAnimatedLength> y() const;
@@ -36,8 +36,6 @@ private:
 
     Gfx::FloatSize calculate_used_corner_radius_values() const;
 
-    Optional<Gfx::Path> m_path;
-
     Optional<float> m_x;
     Optional<float> m_y;
     Optional<float> m_width;