From 6b53454b68195dc36644c905b2ec6faed03fe00b Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Thu, 17 Jul 2025 15:04:25 +0100 Subject: [PATCH] LibWeb/SVG: Move path data into Path.{h,cpp} More things need this than just the `` element, so let's avoid having to include `SVGPathElement.h` in places that don't need it. Minor changes at the same time: - Wrap it in a Path class - Specify underlying type for PathInstructionType - Make a couple of free functions into methods - Give PathInstruction an operator== No functionality changes. --- Libraries/LibWeb/CMakeLists.txt | 1 + Libraries/LibWeb/Forward.h | 1 + Libraries/LibWeb/HTML/Path2D.cpp | 4 +- Libraries/LibWeb/SVG/AttributeParser.cpp | 6 +- Libraries/LibWeb/SVG/AttributeParser.h | 25 +-- Libraries/LibWeb/SVG/Path.cpp | 248 +++++++++++++++++++++++ Libraries/LibWeb/SVG/Path.h | 60 ++++++ Libraries/LibWeb/SVG/SVGPathElement.cpp | 241 +--------------------- Libraries/LibWeb/SVG/SVGPathElement.h | 8 +- 9 files changed, 322 insertions(+), 272 deletions(-) create mode 100644 Libraries/LibWeb/SVG/Path.cpp create mode 100644 Libraries/LibWeb/SVG/Path.h diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index 8d32480ee99..b5fb3deaceb 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -816,6 +816,7 @@ set(SOURCES Streams/WritableStreamOperations.cpp SVG/AttributeNames.cpp SVG/AttributeParser.cpp + SVG/Path.cpp SVG/SVGAElement.cpp SVG/SVGAnimatedEnumeration.cpp SVG/SVGAnimatedLength.cpp diff --git a/Libraries/LibWeb/Forward.h b/Libraries/LibWeb/Forward.h index 588ae819894..f39ae338aa2 100644 --- a/Libraries/LibWeb/Forward.h +++ b/Libraries/LibWeb/Forward.h @@ -969,6 +969,7 @@ struct StorageEndpoint; namespace Web::SVG { +class Path; class SVGAnimatedEnumeration; class SVGAnimatedLength; class SVGAnimatedRect; diff --git a/Libraries/LibWeb/HTML/Path2D.cpp b/Libraries/LibWeb/HTML/Path2D.cpp index f000816d13f..9b15f857f0e 100644 --- a/Libraries/LibWeb/HTML/Path2D.cpp +++ b/Libraries/LibWeb/HTML/Path2D.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include namespace Web::HTML { @@ -41,7 +41,7 @@ Path2D::Path2D(JS::Realm& realm, Optional, String>> con // 4. Let svgPath be the result of parsing and interpreting path according to SVG 2's rules for path data. [SVG] auto path_instructions = SVG::AttributeParser::parse_path_data(path->get()); - auto svg_path = SVG::path_from_path_instructions(path_instructions); + auto svg_path = path_instructions.to_gfx_path(); if (!svg_path.is_empty()) { // 5. Let (x, y) be the last point in svgPath. diff --git a/Libraries/LibWeb/SVG/AttributeParser.cpp b/Libraries/LibWeb/SVG/AttributeParser.cpp index dedc24714db..e266731e985 100644 --- a/Libraries/LibWeb/SVG/AttributeParser.cpp +++ b/Libraries/LibWeb/SVG/AttributeParser.cpp @@ -26,7 +26,7 @@ Optional> AttributeParser::parse_transform(StringView input) return parser.parse_transform(); } -Vector AttributeParser::parse_path_data(StringView input) +Path AttributeParser::parse_path_data(StringView input) { AttributeParser parser { input }; parser.parse_whitespace(); @@ -37,9 +37,9 @@ Vector AttributeParser::parse_path_data(StringView input) } if (!parser.m_instructions.is_empty() && parser.m_instructions[0].type != PathInstructionType::Move) { // Invalid. "A path data segment (if there is one) must begin with a "moveto" command." - return {}; + return Path { {} }; } - return parser.m_instructions; + return Path { parser.m_instructions }; } Optional AttributeParser::parse_coordinate(StringView input) diff --git a/Libraries/LibWeb/SVG/AttributeParser.h b/Libraries/LibWeb/SVG/AttributeParser.h index 7fe794fe2ac..13419c5ad67 100644 --- a/Libraries/LibWeb/SVG/AttributeParser.h +++ b/Libraries/LibWeb/SVG/AttributeParser.h @@ -1,6 +1,6 @@ /* * Copyright (c) 2020, Matthew Olsson - * Copyright (c) 2022, Sam Atkins + * Copyright (c) 2022-2025, Sam Atkins * Copyright (c) 2024, Tim Ledbetter * * SPDX-License-Identifier: BSD-2-Clause @@ -12,29 +12,10 @@ #include #include #include +#include namespace Web::SVG { -enum class PathInstructionType { - Move, - ClosePath, - Line, - HorizontalLine, - VerticalLine, - Curve, - SmoothCurve, - QuadraticBezierCurve, - SmoothQuadraticBezierCurve, - EllipticalArc, - Invalid, -}; - -struct PathInstruction { - PathInstructionType type; - bool absolute; - Vector data; -}; - struct Transform { struct Translate { float x; @@ -154,7 +135,7 @@ public: static Optional parse_number_percentage(StringView input); static Optional parse_positive_length(StringView input); static Vector parse_points(StringView input); - static Vector parse_path_data(StringView input); + static Path parse_path_data(StringView input); static Optional> parse_transform(StringView input); static Optional parse_preserve_aspect_ratio(StringView input); static Optional parse_units(StringView input); diff --git a/Libraries/LibWeb/SVG/Path.cpp b/Libraries/LibWeb/SVG/Path.cpp new file mode 100644 index 00000000000..0b5fb9cc465 --- /dev/null +++ b/Libraries/LibWeb/SVG/Path.cpp @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2020, Matthew Olsson + * Copyright (c) 2022-2025, Sam Atkins + * Copyright (c) 2024, Tim Ledbetter + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include + +namespace Web::SVG { + +void PathInstruction::dump() const +{ + switch (type) { + case PathInstructionType::Move: + dbgln("Move (absolute={})", absolute); + for (size_t i = 0; i < data.size(); i += 2) + dbgln(" x={}, y={}", data[i], data[i + 1]); + break; + case PathInstructionType::ClosePath: + dbgln("ClosePath (absolute={})", absolute); + break; + case PathInstructionType::Line: + dbgln("Line (absolute={})", absolute); + for (size_t i = 0; i < data.size(); i += 2) + dbgln(" x={}, y={}", data[i], data[i + 1]); + break; + case PathInstructionType::HorizontalLine: + dbgln("HorizontalLine (absolute={})", absolute); + for (size_t i = 0; i < data.size(); ++i) + dbgln(" x={}", data[i]); + break; + case PathInstructionType::VerticalLine: + dbgln("VerticalLine (absolute={})", absolute); + for (size_t i = 0; i < data.size(); ++i) + dbgln(" y={}", data[i]); + break; + case PathInstructionType::Curve: + dbgln("Curve (absolute={})", absolute); + for (size_t i = 0; i < data.size(); i += 6) + dbgln(" (x1={}, y1={}, x2={}, y2={}), (x={}, y={})", data[i], data[i + 1], data[i + 2], data[i + 3], data[i + 4], data[i + 5]); + break; + case PathInstructionType::SmoothCurve: + dbgln("SmoothCurve (absolute={})", absolute); + for (size_t i = 0; i < data.size(); i += 4) + dbgln(" (x2={}, y2={}), (x={}, y={})", data[i], data[i + 1], data[i + 2], data[i + 3]); + break; + case PathInstructionType::QuadraticBezierCurve: + dbgln("QuadraticBezierCurve (absolute={})", absolute); + for (size_t i = 0; i < data.size(); i += 4) + dbgln(" (x1={}, y1={}), (x={}, y={})", data[i], data[i + 1], data[i + 2], data[i + 3]); + break; + case PathInstructionType::SmoothQuadraticBezierCurve: + dbgln("SmoothQuadraticBezierCurve (absolute={})", absolute); + for (size_t i = 0; i < data.size(); i += 2) + dbgln(" x={}, y={}", data[i], data[i + 1]); + break; + case PathInstructionType::EllipticalArc: + dbgln("EllipticalArc (absolute={})", absolute); + for (size_t i = 0; i < data.size(); i += 7) + dbgln(" (rx={}, ry={}) x-axis-rotation={}, large-arc-flag={}, sweep-flag={}, (x={}, y={})", + data[i], + data[i + 1], + data[i + 2], + data[i + 3], + data[i + 4], + data[i + 5], + data[i + 6]); + break; + case PathInstructionType::Invalid: + dbgln("Invalid"); + break; + } +} + +Gfx::Path Path::to_gfx_path() const +{ + Gfx::Path path; + Optional previous_control_point; + PathInstructionType last_instruction = PathInstructionType::Invalid; + + for (auto& instruction : m_instructions) { + // If the first path element uses relative coordinates, we treat them as absolute by making them relative to (0, 0). + auto last_point = path.last_point(); + + auto& absolute = instruction.absolute; + auto& data = instruction.data; + + if constexpr (PATH_DEBUG) { + instruction.dump(); + } + + bool clear_last_control_point = true; + + switch (instruction.type) { + case PathInstructionType::Move: { + Gfx::FloatPoint point = { data[0], data[1] }; + if (absolute) { + path.move_to(point); + } else { + path.move_to(point + last_point); + } + break; + } + case PathInstructionType::ClosePath: + path.close(); + break; + case PathInstructionType::Line: { + Gfx::FloatPoint point = { data[0], data[1] }; + if (absolute) { + path.line_to(point); + } else { + path.line_to(point + last_point); + } + break; + } + case PathInstructionType::HorizontalLine: { + if (absolute) + path.line_to(Gfx::FloatPoint { data[0], last_point.y() }); + else + path.line_to(Gfx::FloatPoint { data[0] + last_point.x(), last_point.y() }); + break; + } + case PathInstructionType::VerticalLine: { + if (absolute) + path.line_to(Gfx::FloatPoint { last_point.x(), data[0] }); + else + path.line_to(Gfx::FloatPoint { last_point.x(), data[0] + last_point.y() }); + break; + } + case PathInstructionType::EllipticalArc: { + double rx = data[0]; + double ry = data[1]; + double x_axis_rotation = AK::to_radians(static_cast(data[2])); + double large_arc_flag = data[3]; + double sweep_flag = data[4]; + + Gfx::FloatPoint next_point; + + if (absolute) + next_point = { data[5], data[6] }; + else + next_point = { data[5] + last_point.x(), data[6] + last_point.y() }; + + path.elliptical_arc_to(next_point, { rx, ry }, x_axis_rotation, large_arc_flag != 0, sweep_flag != 0); + break; + } + case PathInstructionType::QuadraticBezierCurve: { + clear_last_control_point = false; + + Gfx::FloatPoint through = { data[0], data[1] }; + Gfx::FloatPoint point = { data[2], data[3] }; + + if (absolute) { + path.quadratic_bezier_curve_to(through, point); + previous_control_point = through; + } else { + auto control_point = through + last_point; + path.quadratic_bezier_curve_to(control_point, point + last_point); + previous_control_point = control_point; + } + break; + } + case PathInstructionType::SmoothQuadraticBezierCurve: { + clear_last_control_point = false; + + if (!previous_control_point.has_value() + || ((last_instruction != PathInstructionType::QuadraticBezierCurve) && (last_instruction != PathInstructionType::SmoothQuadraticBezierCurve))) { + previous_control_point = last_point; + } + + auto dx_end_control = last_point.dx_relative_to(previous_control_point.value()); + auto dy_end_control = last_point.dy_relative_to(previous_control_point.value()); + auto control_point = Gfx::FloatPoint { last_point.x() + dx_end_control, last_point.y() + dy_end_control }; + + Gfx::FloatPoint end_point = { data[0], data[1] }; + + if (absolute) { + path.quadratic_bezier_curve_to(control_point, end_point); + } else { + path.quadratic_bezier_curve_to(control_point, end_point + last_point); + } + + previous_control_point = control_point; + break; + } + + case PathInstructionType::Curve: { + clear_last_control_point = false; + + Gfx::FloatPoint c1 = { data[0], data[1] }; + Gfx::FloatPoint c2 = { data[2], data[3] }; + Gfx::FloatPoint p2 = { data[4], data[5] }; + if (!absolute) { + p2 += last_point; + c1 += last_point; + c2 += last_point; + } + path.cubic_bezier_curve_to(c1, c2, p2); + + previous_control_point = c2; + break; + } + + case PathInstructionType::SmoothCurve: { + clear_last_control_point = false; + + if (!previous_control_point.has_value() + || ((last_instruction != PathInstructionType::Curve) && (last_instruction != PathInstructionType::SmoothCurve))) { + previous_control_point = last_point; + } + + // 9.5.2. Reflected control points https://svgwg.org/svg2-draft/paths.html#ReflectedControlPoints + // If the current point is (curx, cury) and the final control point of the previous path segment is (oldx2, oldy2), + // then the reflected point (i.e., (newx1, newy1), the first control point of the current path segment) is: + // (newx1, newy1) = (curx - (oldx2 - curx), cury - (oldy2 - cury)) + auto reflected_previous_control_x = last_point.x() - previous_control_point.value().dx_relative_to(last_point); + auto reflected_previous_control_y = last_point.y() - previous_control_point.value().dy_relative_to(last_point); + Gfx::FloatPoint c1 = Gfx::FloatPoint { reflected_previous_control_x, reflected_previous_control_y }; + Gfx::FloatPoint c2 = { data[0], data[1] }; + Gfx::FloatPoint p2 = { data[2], data[3] }; + if (!absolute) { + p2 += last_point; + c2 += last_point; + } + path.cubic_bezier_curve_to(c1, c2, p2); + + previous_control_point = c2; + break; + } + case PathInstructionType::Invalid: + VERIFY_NOT_REACHED(); + } + + if (clear_last_control_point) { + previous_control_point = Gfx::FloatPoint {}; + } + last_instruction = instruction.type; + } + + return path; +} + +} diff --git a/Libraries/LibWeb/SVG/Path.h b/Libraries/LibWeb/SVG/Path.h new file mode 100644 index 00000000000..fc09fc0031a --- /dev/null +++ b/Libraries/LibWeb/SVG/Path.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2020, Matthew Olsson + * Copyright (c) 2022-2025, Sam Atkins + * Copyright (c) 2024, Tim Ledbetter + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Web::SVG { + +enum class PathInstructionType : u8 { + Move, + ClosePath, + Line, + HorizontalLine, + VerticalLine, + Curve, + SmoothCurve, + QuadraticBezierCurve, + SmoothQuadraticBezierCurve, + EllipticalArc, + Invalid, +}; + +struct PathInstruction { + PathInstructionType type; + bool absolute; + Vector data; + + bool operator==(PathInstruction const&) const = default; + + void dump() const; +}; + +class Path { +public: + Path() = default; + + explicit Path(Vector instructions) + : m_instructions(move(instructions)) + { + } + ReadonlySpan instructions() const { return m_instructions; } + + [[nodiscard]] Gfx::Path to_gfx_path() const; + + bool operator==(Path const&) const = default; + +private: + Vector m_instructions; +}; + +} diff --git a/Libraries/LibWeb/SVG/SVGPathElement.cpp b/Libraries/LibWeb/SVG/SVGPathElement.cpp index 54fe0f6f446..810a49411d6 100644 --- a/Libraries/LibWeb/SVG/SVGPathElement.cpp +++ b/Libraries/LibWeb/SVG/SVGPathElement.cpp @@ -4,7 +4,6 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include #include #include #include @@ -17,74 +16,6 @@ namespace Web::SVG { GC_DEFINE_ALLOCATOR(SVGPathElement); -[[maybe_unused]] static void print_instruction(PathInstruction const& instruction) -{ - VERIFY(PATH_DEBUG); - - auto& data = instruction.data; - - switch (instruction.type) { - case PathInstructionType::Move: - dbgln("Move (absolute={})", instruction.absolute); - for (size_t i = 0; i < data.size(); i += 2) - dbgln(" x={}, y={}", data[i], data[i + 1]); - break; - case PathInstructionType::ClosePath: - dbgln("ClosePath (absolute={})", instruction.absolute); - break; - case PathInstructionType::Line: - dbgln("Line (absolute={})", instruction.absolute); - for (size_t i = 0; i < data.size(); i += 2) - dbgln(" x={}, y={}", data[i], data[i + 1]); - break; - case PathInstructionType::HorizontalLine: - dbgln("HorizontalLine (absolute={})", instruction.absolute); - for (size_t i = 0; i < data.size(); ++i) - dbgln(" x={}", data[i]); - break; - case PathInstructionType::VerticalLine: - dbgln("VerticalLine (absolute={})", instruction.absolute); - for (size_t i = 0; i < data.size(); ++i) - dbgln(" y={}", data[i]); - break; - case PathInstructionType::Curve: - dbgln("Curve (absolute={})", instruction.absolute); - for (size_t i = 0; i < data.size(); i += 6) - dbgln(" (x1={}, y1={}, x2={}, y2={}), (x={}, y={})", data[i], data[i + 1], data[i + 2], data[i + 3], data[i + 4], data[i + 5]); - break; - case PathInstructionType::SmoothCurve: - dbgln("SmoothCurve (absolute={})", instruction.absolute); - for (size_t i = 0; i < data.size(); i += 4) - dbgln(" (x2={}, y2={}), (x={}, y={})", data[i], data[i + 1], data[i + 2], data[i + 3]); - break; - case PathInstructionType::QuadraticBezierCurve: - dbgln("QuadraticBezierCurve (absolute={})", instruction.absolute); - for (size_t i = 0; i < data.size(); i += 4) - dbgln(" (x1={}, y1={}), (x={}, y={})", data[i], data[i + 1], data[i + 2], data[i + 3]); - break; - case PathInstructionType::SmoothQuadraticBezierCurve: - dbgln("SmoothQuadraticBezierCurve (absolute={})", instruction.absolute); - for (size_t i = 0; i < data.size(); i += 2) - dbgln(" x={}, y={}", data[i], data[i + 1]); - break; - case PathInstructionType::EllipticalArc: - dbgln("EllipticalArc (absolute={})", instruction.absolute); - for (size_t i = 0; i < data.size(); i += 7) - dbgln(" (rx={}, ry={}) x-axis-rotation={}, large-arc-flag={}, sweep-flag={}, (x={}, y={})", - data[i], - data[i + 1], - data[i + 2], - data[i + 3], - data[i + 4], - data[i + 5], - data[i + 6]); - break; - case PathInstructionType::Invalid: - dbgln("Invalid"); - break; - } -} - SVGPathElement::SVGPathElement(DOM::Document& document, DOM::QualifiedName qualified_name) : SVGGeometryElement(document, move(qualified_name)) { @@ -101,180 +32,12 @@ void SVGPathElement::attribute_changed(FlyString const& name, Optional c Base::attribute_changed(name, old_value, value, namespace_); if (name == "d") - m_instructions = AttributeParser::parse_path_data(value.value_or(String {})); -} - -Gfx::Path path_from_path_instructions(ReadonlySpan instructions) -{ - Gfx::Path path; - Optional previous_control_point; - PathInstructionType last_instruction = PathInstructionType::Invalid; - - for (auto& instruction : instructions) { - // If the first path element uses relative coordinates, we treat them as absolute by making them relative to (0, 0). - auto last_point = path.last_point(); - - auto& absolute = instruction.absolute; - auto& data = instruction.data; - - if constexpr (PATH_DEBUG) { - print_instruction(instruction); - } - - bool clear_last_control_point = true; - - switch (instruction.type) { - case PathInstructionType::Move: { - Gfx::FloatPoint point = { data[0], data[1] }; - if (absolute) { - path.move_to(point); - } else { - path.move_to(point + last_point); - } - break; - } - case PathInstructionType::ClosePath: - path.close(); - break; - case PathInstructionType::Line: { - Gfx::FloatPoint point = { data[0], data[1] }; - if (absolute) { - path.line_to(point); - } else { - path.line_to(point + last_point); - } - break; - } - case PathInstructionType::HorizontalLine: { - if (absolute) - path.line_to(Gfx::FloatPoint { data[0], last_point.y() }); - else - path.line_to(Gfx::FloatPoint { data[0] + last_point.x(), last_point.y() }); - break; - } - case PathInstructionType::VerticalLine: { - if (absolute) - path.line_to(Gfx::FloatPoint { last_point.x(), data[0] }); - else - path.line_to(Gfx::FloatPoint { last_point.x(), data[0] + last_point.y() }); - break; - } - case PathInstructionType::EllipticalArc: { - double rx = data[0]; - double ry = data[1]; - double x_axis_rotation = AK::to_radians(static_cast(data[2])); - double large_arc_flag = data[3]; - double sweep_flag = data[4]; - - Gfx::FloatPoint next_point; - - if (absolute) - next_point = { data[5], data[6] }; - else - next_point = { data[5] + last_point.x(), data[6] + last_point.y() }; - - path.elliptical_arc_to(next_point, { rx, ry }, x_axis_rotation, large_arc_flag != 0, sweep_flag != 0); - break; - } - case PathInstructionType::QuadraticBezierCurve: { - clear_last_control_point = false; - - Gfx::FloatPoint through = { data[0], data[1] }; - Gfx::FloatPoint point = { data[2], data[3] }; - - if (absolute) { - path.quadratic_bezier_curve_to(through, point); - previous_control_point = through; - } else { - auto control_point = through + last_point; - path.quadratic_bezier_curve_to(control_point, point + last_point); - previous_control_point = control_point; - } - break; - } - case PathInstructionType::SmoothQuadraticBezierCurve: { - clear_last_control_point = false; - - if (!previous_control_point.has_value() - || ((last_instruction != PathInstructionType::QuadraticBezierCurve) && (last_instruction != PathInstructionType::SmoothQuadraticBezierCurve))) { - previous_control_point = last_point; - } - - auto dx_end_control = last_point.dx_relative_to(previous_control_point.value()); - auto dy_end_control = last_point.dy_relative_to(previous_control_point.value()); - auto control_point = Gfx::FloatPoint { last_point.x() + dx_end_control, last_point.y() + dy_end_control }; - - Gfx::FloatPoint end_point = { data[0], data[1] }; - - if (absolute) { - path.quadratic_bezier_curve_to(control_point, end_point); - } else { - path.quadratic_bezier_curve_to(control_point, end_point + last_point); - } - - previous_control_point = control_point; - break; - } - - case PathInstructionType::Curve: { - clear_last_control_point = false; - - Gfx::FloatPoint c1 = { data[0], data[1] }; - Gfx::FloatPoint c2 = { data[2], data[3] }; - Gfx::FloatPoint p2 = { data[4], data[5] }; - if (!absolute) { - p2 += last_point; - c1 += last_point; - c2 += last_point; - } - path.cubic_bezier_curve_to(c1, c2, p2); - - previous_control_point = c2; - break; - } - - case PathInstructionType::SmoothCurve: { - clear_last_control_point = false; - - if (!previous_control_point.has_value() - || ((last_instruction != PathInstructionType::Curve) && (last_instruction != PathInstructionType::SmoothCurve))) { - previous_control_point = last_point; - } - - // 9.5.2. Reflected control points https://svgwg.org/svg2-draft/paths.html#ReflectedControlPoints - // If the current point is (curx, cury) and the final control point of the previous path segment is (oldx2, oldy2), - // then the reflected point (i.e., (newx1, newy1), the first control point of the current path segment) is: - // (newx1, newy1) = (curx - (oldx2 - curx), cury - (oldy2 - cury)) - auto reflected_previous_control_x = last_point.x() - previous_control_point.value().dx_relative_to(last_point); - auto reflected_previous_control_y = last_point.y() - previous_control_point.value().dy_relative_to(last_point); - Gfx::FloatPoint c1 = Gfx::FloatPoint { reflected_previous_control_x, reflected_previous_control_y }; - Gfx::FloatPoint c2 = { data[0], data[1] }; - Gfx::FloatPoint p2 = { data[2], data[3] }; - if (!absolute) { - p2 += last_point; - c2 += last_point; - } - path.cubic_bezier_curve_to(c1, c2, p2); - - previous_control_point = c2; - break; - } - case PathInstructionType::Invalid: - VERIFY_NOT_REACHED(); - } - - if (clear_last_control_point) { - previous_control_point = Gfx::FloatPoint {}; - } - last_instruction = instruction.type; - } - - return path; + m_path = AttributeParser::parse_path_data(value.value_or(String {})); } Gfx::Path SVGPathElement::get_path(CSSPixelSize) { - return path_from_path_instructions(m_instructions); + return m_path.to_gfx_path(); } } diff --git a/Libraries/LibWeb/SVG/SVGPathElement.h b/Libraries/LibWeb/SVG/SVGPathElement.h index 03a492e9888..4b6461bb140 100644 --- a/Libraries/LibWeb/SVG/SVGPathElement.h +++ b/Libraries/LibWeb/SVG/SVGPathElement.h @@ -6,9 +6,7 @@ #pragma once -#include -#include -#include +#include #include namespace Web::SVG { @@ -29,9 +27,7 @@ private: virtual void initialize(JS::Realm&) override; - Vector m_instructions; + Path m_path {}; }; -[[nodiscard]] Gfx::Path path_from_path_instructions(ReadonlySpan); - }