Skip to content

Commit

Permalink
LibWeb: Update SVG get_path() API to take a viewport size
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
MacDue authored and awesomekling committed Mar 4, 2024
1 parent 1fbf107 commit b9afea4
Show file tree
Hide file tree
Showing 16 changed files with 55 additions and 126 deletions.
41 changes: 20 additions & 21 deletions Userland/Libraries/LibWeb/Layout/SVGFormattingContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);
}

Expand Down
14 changes: 4 additions & 10 deletions Userland/Libraries/LibWeb/SVG/SVGCircleElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,22 +40,17 @@ 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);

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;
Expand All @@ -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
Expand Down
4 changes: 1 addition & 3 deletions Userland/Libraries/LibWeb/SVG/SVGCircleElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class SVGCircleElement final : public SVGGeometryElement {

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;
Expand All @@ -31,8 +31,6 @@ class SVGCircleElement final : public SVGGeometryElement {

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;
Expand Down
18 changes: 4 additions & 14 deletions Userland/Libraries/LibWeb/SVG/SVGEllipseElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,35 +30,26 @@ 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);
float cy = m_center_y.value_or(0);
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;
Expand All @@ -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
Expand Down
4 changes: 1 addition & 3 deletions Userland/Libraries/LibWeb/SVG/SVGEllipseElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class SVGEllipseElement final : public SVGGeometryElement {

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;
Expand All @@ -32,8 +32,6 @@ class SVGEllipseElement final : public SVGGeometryElement {

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;
Expand Down
2 changes: 1 addition & 1 deletion Userland/Libraries/LibWeb/SVG/SVGGeometryElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
12 changes: 2 additions & 10 deletions Userland/Libraries/LibWeb/SVG/SVGLineElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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
Expand Down
4 changes: 1 addition & 3 deletions Userland/Libraries/LibWeb/SVG/SVGLineElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class SVGLineElement final : public SVGGeometryElement {

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;
Expand All @@ -32,8 +32,6 @@ class SVGLineElement final : public SVGGeometryElement {

virtual void initialize(JS::Realm&) override;

Optional<Gfx::Path> m_path;

Optional<float> m_x1;
Optional<float> m_y1;
Optional<float> m_x2;
Expand Down
11 changes: 3 additions & 8 deletions Userland/Libraries/LibWeb/SVG/SVGPathElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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);
}

}
3 changes: 1 addition & 2 deletions Userland/Libraries/LibWeb/SVG/SVGPathElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,14 @@ class SVGPathElement final : public SVGGeometryElement {

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);

virtual void initialize(JS::Realm&) override;

Vector<PathInstruction> m_instructions;
Optional<Gfx::Path> m_path;
};

Gfx::Path path_from_path_instructions(ReadonlySpan<PathInstruction>);
Expand Down
18 changes: 5 additions & 13 deletions Userland/Libraries/LibWeb/SVG/SVGPolygonElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand All @@ -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;
}

}
4 changes: 1 addition & 3 deletions Userland/Libraries/LibWeb/SVG/SVGPolygonElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,13 @@ class SVGPolygonElement final : public SVGGeometryElement {

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;
};

Expand Down
18 changes: 5 additions & 13 deletions Userland/Libraries/LibWeb/SVG/SVGPolylineElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand All @@ -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;
}

}
Loading

0 comments on commit b9afea4

Please sign in to comment.