Explorar o código

LibGfx+LibWeb: Add new Path class with Skia backend, use for 2D canvas

This thing is essentially a wrapper around an SkPath, although we do
some indirection via a PathImpl class to keep the door open for
alternative rasterizer/path backends in the future.

We also update the 2D canvas code, since that was the only code that
used Painter+DeprecatedPath, and this allows us to just drop that
code instead of temporarily supporting it until the next commit.
Andreas Kling hai 10 meses
pai
achega
a3cc03f180

+ 2 - 0
Userland/Libraries/LibGfx/CMakeLists.txt

@@ -57,7 +57,9 @@ set(SOURCES
     ImmutableBitmap.cpp
     MedianCut.cpp
     Palette.cpp
+    Path.cpp
     PathClipper.cpp
+    PathSkia.cpp
     Painter.cpp
     PainterSkia.cpp
     Point.cpp

+ 1 - 0
Userland/Libraries/LibGfx/Forward.h

@@ -31,6 +31,7 @@ class Painter;
 class Palette;
 class PaletteImpl;
 class DeprecatedPath;
+class Path;
 class ShareableBitmap;
 struct SystemTheme;
 

+ 4 - 4
Userland/Libraries/LibGfx/Painter.h

@@ -25,11 +25,11 @@ public:
 
     virtual void draw_bitmap(Gfx::FloatRect const& dst_rect, Gfx::Bitmap const& src_bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode, float global_alpha) = 0;
 
-    virtual void stroke_path(Gfx::DeprecatedPath const&, Gfx::Color, float thickness) = 0;
-    virtual void stroke_path(Gfx::DeprecatedPath const&, Gfx::PaintStyle const&, float thickness, float global_alpha) = 0;
+    virtual void stroke_path(Gfx::Path const&, Gfx::Color, float thickness) = 0;
+    virtual void stroke_path(Gfx::Path const&, Gfx::PaintStyle const&, float thickness, float global_alpha) = 0;
 
-    virtual void fill_path(Gfx::DeprecatedPath const&, Gfx::Color, Gfx::WindingRule) = 0;
-    virtual void fill_path(Gfx::DeprecatedPath const&, Gfx::PaintStyle const&, float global_alpha, Gfx::WindingRule) = 0;
+    virtual void fill_path(Gfx::Path const&, Gfx::Color, Gfx::WindingRule) = 0;
+    virtual void fill_path(Gfx::Path const&, Gfx::PaintStyle const&, float global_alpha, Gfx::WindingRule) = 0;
 
     virtual void set_transform(Gfx::AffineTransform const&) = 0;
 

+ 7 - 55
Userland/Libraries/LibGfx/PainterSkia.cpp

@@ -7,8 +7,8 @@
 #define AK_DONT_REPLACE_STD
 
 #include <AK/OwnPtr.h>
-#include <LibGfx/DeprecatedPath.h>
 #include <LibGfx/PainterSkia.h>
+#include <LibGfx/PathSkia.h>
 
 #include <AK/TypeCasts.h>
 #include <core/SkBitmap.h>
@@ -56,57 +56,9 @@ static constexpr SkColor to_skia_color(Gfx::Color const& color)
     return SkColorSetARGB(color.alpha(), color.red(), color.green(), color.blue());
 }
 
-static SkPath to_skia_path(Gfx::DeprecatedPath const& path)
+static SkPath to_skia_path(Gfx::Path const& path)
 {
-    Optional<Gfx::FloatPoint> subpath_start_point;
-    Optional<Gfx::FloatPoint> subpath_last_point;
-    SkPathBuilder path_builder;
-    auto close_subpath_if_needed = [&](auto last_point) {
-        if (subpath_start_point == last_point)
-            path_builder.close();
-    };
-    for (auto const& segment : path) {
-        auto point = segment.point();
-        switch (segment.command()) {
-        case Gfx::DeprecatedPathSegment::Command::MoveTo: {
-            if (subpath_start_point.has_value() && subpath_last_point.has_value())
-                close_subpath_if_needed(subpath_last_point.value());
-            subpath_start_point = point;
-            path_builder.moveTo({ point.x(), point.y() });
-            break;
-        }
-        case Gfx::DeprecatedPathSegment::Command::LineTo: {
-            if (!subpath_start_point.has_value())
-                subpath_start_point = Gfx::FloatPoint { 0.0f, 0.0f };
-            path_builder.lineTo({ point.x(), point.y() });
-            break;
-        }
-        case Gfx::DeprecatedPathSegment::Command::QuadraticBezierCurveTo: {
-            if (!subpath_start_point.has_value())
-                subpath_start_point = Gfx::FloatPoint { 0.0f, 0.0f };
-            SkPoint pt1 = { segment.through().x(), segment.through().y() };
-            SkPoint pt2 = { segment.point().x(), segment.point().y() };
-            path_builder.quadTo(pt1, pt2);
-            break;
-        }
-        case Gfx::DeprecatedPathSegment::Command::CubicBezierCurveTo: {
-            if (!subpath_start_point.has_value())
-                subpath_start_point = Gfx::FloatPoint { 0.0f, 0.0f };
-            SkPoint pt1 = { segment.through_0().x(), segment.through_0().y() };
-            SkPoint pt2 = { segment.through_1().x(), segment.through_1().y() };
-            SkPoint pt3 = { segment.point().x(), segment.point().y() };
-            path_builder.cubicTo(pt1, pt2, pt3);
-            break;
-        }
-        default:
-            VERIFY_NOT_REACHED();
-        }
-        subpath_last_point = point;
-    }
-
-    close_subpath_if_needed(subpath_last_point);
-
-    return path_builder.snapshot();
+    return static_cast<PathImplSkia const&>(path.impl()).sk_path();
 }
 
 static SkPathFillType to_skia_path_fill_type(Gfx::WindingRule winding_rule)
@@ -200,7 +152,7 @@ void PainterSkia::set_transform(Gfx::AffineTransform const& transform)
     impl().canvas()->setMatrix(matrix);
 }
 
-void PainterSkia::stroke_path(Gfx::DeprecatedPath const& path, Gfx::Color color, float thickness)
+void PainterSkia::stroke_path(Gfx::Path const& path, Gfx::Color color, float thickness)
 {
     // Skia treats zero thickness as a special case and will draw a hairline, while we want to draw nothing.
     if (!thickness)
@@ -278,7 +230,7 @@ static SkPaint to_skia_paint(Gfx::PaintStyle const& style, Gfx::FloatRect const&
     return {};
 }
 
-void PainterSkia::stroke_path(Gfx::DeprecatedPath const& path, Gfx::PaintStyle const& paint_style, float thickness, float global_alpha)
+void PainterSkia::stroke_path(Gfx::Path const& path, Gfx::PaintStyle const& paint_style, float thickness, float global_alpha)
 {
     // Skia treats zero thickness as a special case and will draw a hairline, while we want to draw nothing.
     if (!thickness)
@@ -293,7 +245,7 @@ void PainterSkia::stroke_path(Gfx::DeprecatedPath const& path, Gfx::PaintStyle c
     impl().canvas()->drawPath(sk_path, paint);
 }
 
-void PainterSkia::fill_path(Gfx::DeprecatedPath const& path, Gfx::Color color, Gfx::WindingRule winding_rule)
+void PainterSkia::fill_path(Gfx::Path const& path, Gfx::Color color, Gfx::WindingRule winding_rule)
 {
     SkPaint paint;
     paint.setAntiAlias(true);
@@ -303,7 +255,7 @@ void PainterSkia::fill_path(Gfx::DeprecatedPath const& path, Gfx::Color color, G
     impl().canvas()->drawPath(sk_path, paint);
 }
 
-void PainterSkia::fill_path(Gfx::DeprecatedPath const& path, Gfx::PaintStyle const& paint_style, float global_alpha, Gfx::WindingRule winding_rule)
+void PainterSkia::fill_path(Gfx::Path const& path, Gfx::PaintStyle const& paint_style, float global_alpha, Gfx::WindingRule winding_rule)
 {
     auto sk_path = to_skia_path(path);
     sk_path.setFillType(to_skia_path_fill_type(winding_rule));

+ 4 - 4
Userland/Libraries/LibGfx/PainterSkia.h

@@ -20,10 +20,10 @@ public:
     virtual void clear_rect(Gfx::FloatRect const&, Color) override;
     virtual void fill_rect(Gfx::FloatRect const&, Color) override;
     virtual void draw_bitmap(Gfx::FloatRect const& dst_rect, Gfx::Bitmap const& src_bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode, float global_alpha) override;
-    virtual void stroke_path(Gfx::DeprecatedPath const&, Gfx::Color, float thickness) override;
-    virtual void stroke_path(Gfx::DeprecatedPath const&, Gfx::PaintStyle const&, float thickness, float global_alpha) override;
-    virtual void fill_path(Gfx::DeprecatedPath const&, Gfx::Color, Gfx::WindingRule) override;
-    virtual void fill_path(Gfx::DeprecatedPath const&, Gfx::PaintStyle const&, float global_alpha, Gfx::WindingRule) override;
+    virtual void stroke_path(Gfx::Path const&, Gfx::Color, float thickness) override;
+    virtual void stroke_path(Gfx::Path const&, Gfx::PaintStyle const&, float thickness, float global_alpha) override;
+    virtual void fill_path(Gfx::Path const&, Gfx::Color, Gfx::WindingRule) override;
+    virtual void fill_path(Gfx::Path const&, Gfx::PaintStyle const&, float global_alpha, Gfx::WindingRule) override;
     virtual void set_transform(Gfx::AffineTransform const&) override;
     virtual void save() override;
     virtual void restore() override;

+ 19 - 0
Userland/Libraries/LibGfx/Path.cpp

@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2024, Andreas Kling <andreas@ladybird.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibGfx/Path.h>
+#include <LibGfx/PathSkia.h>
+
+namespace Gfx {
+
+NonnullOwnPtr<Gfx::PathImpl> PathImpl::create()
+{
+    return PathImplSkia::create();
+}
+
+PathImpl::~PathImpl() = default;
+
+}

+ 101 - 0
Userland/Libraries/LibGfx/Path.h

@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2024, Andreas Kling <andreas@ladybird.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Forward.h>
+#include <AK/NonnullOwnPtr.h>
+#include <AK/Utf8View.h>
+#include <LibGfx/Forward.h>
+#include <LibGfx/PaintStyle.h>
+#include <LibGfx/ScalingMode.h>
+#include <LibGfx/WindingRule.h>
+
+namespace Gfx {
+
+class PathImpl {
+public:
+    static NonnullOwnPtr<Gfx::PathImpl> create();
+
+    virtual ~PathImpl();
+
+    virtual void clear() = 0;
+    virtual void move_to(Gfx::FloatPoint const&) = 0;
+    virtual void line_to(Gfx::FloatPoint const&) = 0;
+    virtual void close_all_subpaths() = 0;
+    virtual void close() = 0;
+    virtual void elliptical_arc_to(FloatPoint point, FloatSize radii, float x_axis_rotation, bool large_arc, bool sweep) = 0;
+    virtual void arc_to(FloatPoint point, float radius, bool large_arc, bool sweep) = 0;
+    virtual void quadratic_bezier_curve_to(FloatPoint through, FloatPoint point) = 0;
+    virtual void cubic_bezier_curve_to(FloatPoint c1, FloatPoint c2, FloatPoint p2) = 0;
+    virtual void text(Utf8View, Font const&) = 0;
+
+    virtual void append_path(Gfx::Path const&) = 0;
+    virtual void intersect(Gfx::Path const&) = 0;
+
+    [[nodiscard]] virtual bool is_empty() const = 0;
+    virtual Gfx::FloatPoint last_point() const = 0;
+    virtual Gfx::FloatRect bounding_box() const = 0;
+
+    virtual NonnullOwnPtr<PathImpl> clone() const = 0;
+    virtual NonnullOwnPtr<PathImpl> copy_transformed(Gfx::AffineTransform const&) const = 0;
+};
+
+class Path {
+public:
+    Path() = default;
+
+    Path(Path const& other)
+        : m_impl(other.impl().clone())
+    {
+    }
+
+    Path& operator=(Path const& other)
+    {
+        if (this != &other)
+            m_impl = other.impl().clone();
+        return *this;
+    }
+
+    Path(Path&& other) = default;
+    Path& operator=(Path&& other) = default;
+
+    void clear() { impl().clear(); }
+    void move_to(Gfx::FloatPoint const& point) { impl().move_to(point); }
+    void line_to(Gfx::FloatPoint const& point) { impl().line_to(point); }
+    void close_all_subpaths() { impl().close_all_subpaths(); }
+    void close() { impl().close(); }
+    void elliptical_arc_to(FloatPoint point, FloatSize radii, float x_axis_rotation, bool large_arc, bool sweep) { impl().elliptical_arc_to(point, radii, x_axis_rotation, large_arc, sweep); }
+    void arc_to(FloatPoint point, float radius, bool large_arc, bool sweep) { impl().arc_to(point, radius, large_arc, sweep); }
+    void quadratic_bezier_curve_to(FloatPoint through, FloatPoint point) { impl().quadratic_bezier_curve_to(through, point); }
+    void cubic_bezier_curve_to(FloatPoint c1, FloatPoint c2, FloatPoint p2) { impl().cubic_bezier_curve_to(c1, c2, p2); }
+    void text(Utf8View text, Font const& font) { impl().text(text, font); }
+
+    void append_path(Gfx::Path const& other) { impl().append_path(other); }
+    void intersect(Gfx::Path const& other) { impl().intersect(other); }
+
+    [[nodiscard]] bool is_empty() const { return impl().is_empty(); }
+    Gfx::FloatPoint last_point() const { return impl().last_point(); }
+    Gfx::FloatRect bounding_box() const { return impl().bounding_box(); }
+
+    Gfx::Path clone() const { return Gfx::Path { impl().clone() }; }
+    Gfx::Path copy_transformed(Gfx::AffineTransform const& transform) const { return Gfx::Path { impl().copy_transformed(transform) }; }
+
+    void transform(Gfx::AffineTransform const& transform) { m_impl = impl().copy_transformed(transform); }
+
+    PathImpl& impl() { return *m_impl; }
+    PathImpl const& impl() const { return *m_impl; }
+
+private:
+    explicit Path(NonnullOwnPtr<PathImpl>&& impl)
+        : m_impl(move(impl))
+    {
+    }
+
+    NonnullOwnPtr<PathImpl> m_impl { PathImpl::create() };
+};
+
+}

+ 180 - 0
Userland/Libraries/LibGfx/PathSkia.cpp

@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2024, Andreas Kling <andreas@ladybird.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#define AK_DONT_REPLACE_STD
+
+#include <AK/TypeCasts.h>
+#include <LibGfx/Font/ScaledFont.h>
+#include <LibGfx/PathSkia.h>
+#include <core/SkFont.h>
+#include <core/SkPath.h>
+#include <pathops/SkPathOps.h>
+#include <utils/SkTextUtils.h>
+
+namespace Gfx {
+
+NonnullOwnPtr<Gfx::PathImplSkia> PathImplSkia::create()
+{
+    return adopt_own(*new PathImplSkia);
+}
+
+PathImplSkia::PathImplSkia()
+    : m_path(adopt_own(*new SkPath))
+{
+}
+
+PathImplSkia::~PathImplSkia() = default;
+
+void PathImplSkia::clear()
+{
+    m_path->reset();
+}
+
+void PathImplSkia::move_to(Gfx::FloatPoint const& point)
+{
+    m_last_move_to = point;
+    m_path->moveTo(point.x(), point.y());
+}
+
+void PathImplSkia::line_to(Gfx::FloatPoint const& point)
+{
+    m_path->lineTo(point.x(), point.y());
+}
+
+void PathImplSkia::close_all_subpaths()
+{
+    SkPath new_path;
+    SkPath::Iter iter(*m_path, false);
+    SkPoint points[4];
+    SkPath::Verb verb;
+    bool need_close = false;
+
+    while ((verb = iter.next(points)) != SkPath::kDone_Verb) {
+        switch (verb) {
+        case SkPath::kMove_Verb:
+            if (need_close) {
+                new_path.close();
+            }
+            new_path.moveTo(points[0]);
+            need_close = true;
+            break;
+        case SkPath::kLine_Verb:
+            new_path.lineTo(points[1]);
+            break;
+        case SkPath::kQuad_Verb:
+            new_path.quadTo(points[1], points[2]);
+            break;
+        case SkPath::kCubic_Verb:
+            new_path.cubicTo(points[1], points[2], points[3]);
+            break;
+        case SkPath::kClose_Verb:
+            new_path.close();
+            need_close = false;
+            break;
+        case SkPath::kConic_Verb:
+            new_path.conicTo(points[1], points[2], iter.conicWeight());
+            break;
+        case SkPath::kDone_Verb:
+            break;
+        }
+    }
+
+    if (need_close) {
+        new_path.close();
+    }
+
+    *m_path = new_path;
+}
+
+void PathImplSkia::close()
+{
+    m_path->close();
+    m_path->moveTo(m_last_move_to.x(), m_last_move_to.y());
+}
+
+void PathImplSkia::elliptical_arc_to(FloatPoint point, FloatSize radii, float x_axis_rotation, bool large_arc, bool sweep)
+{
+    SkPoint skPoint = SkPoint::Make(point.x(), point.y());
+    SkScalar skWidth = SkFloatToScalar(radii.width());
+    SkScalar skHeight = SkFloatToScalar(radii.height());
+    SkScalar skXRotation = SkFloatToScalar(sk_float_radians_to_degrees(x_axis_rotation));
+    SkPath::ArcSize skLargeArc = large_arc ? SkPath::kLarge_ArcSize : SkPath::kSmall_ArcSize;
+    SkPathDirection skSweep = sweep ? SkPathDirection::kCW : SkPathDirection::kCCW;
+    m_path->arcTo(skWidth, skHeight, skXRotation, skLargeArc, skSweep, skPoint.x(), skPoint.y());
+}
+
+void PathImplSkia::arc_to(FloatPoint point, float radius, bool large_arc, bool sweep)
+{
+    SkPoint skPoint = SkPoint::Make(point.x(), point.y());
+    SkScalar skRadius = SkFloatToScalar(radius);
+    SkPath::ArcSize skLargeArc = large_arc ? SkPath::kLarge_ArcSize : SkPath::kSmall_ArcSize;
+    SkPathDirection skSweep = sweep ? SkPathDirection::kCW : SkPathDirection::kCCW;
+    m_path->arcTo(skRadius, skRadius, 0, skLargeArc, skSweep, skPoint.x(), skPoint.y());
+}
+
+void PathImplSkia::quadratic_bezier_curve_to(FloatPoint through, FloatPoint point)
+{
+    m_path->quadTo(through.x(), through.y(), point.x(), point.y());
+}
+
+void PathImplSkia::cubic_bezier_curve_to(FloatPoint c1, FloatPoint c2, FloatPoint p2)
+{
+    m_path->cubicTo(c1.x(), c1.y(), c2.x(), c2.y(), p2.x(), p2.y());
+}
+
+void PathImplSkia::text(Utf8View string, Font const& font)
+{
+    SkTextUtils::GetPath(string.as_string().characters_without_null_termination(), string.as_string().length(), SkTextEncoding::kUTF8, last_point().x(), last_point().y(), verify_cast<ScaledFont>(font).skia_font(1), m_path.ptr());
+}
+
+void PathImplSkia::append_path(Gfx::Path const& other)
+{
+    m_path->addPath(static_cast<PathImplSkia const&>(other.impl()).sk_path());
+}
+
+void PathImplSkia::intersect(Gfx::Path const& other)
+{
+    Op(*m_path, static_cast<PathImplSkia const&>(other.impl()).sk_path(), SkPathOp::kIntersect_SkPathOp, m_path.ptr());
+}
+
+bool PathImplSkia::is_empty() const
+{
+    return m_path->isEmpty();
+}
+
+Gfx::FloatPoint PathImplSkia::last_point() const
+{
+    SkPoint last {};
+    if (!m_path->getLastPt(&last))
+        return {};
+    return { last.fX, last.fY };
+}
+
+Gfx::FloatRect PathImplSkia::bounding_box() const
+{
+    auto bounds = m_path->getBounds();
+    return { bounds.fLeft, bounds.fTop, bounds.fRight - bounds.fLeft, bounds.fBottom - bounds.fTop };
+}
+
+NonnullOwnPtr<PathImpl> PathImplSkia::clone() const
+{
+    auto new_path = PathImplSkia::create();
+    new_path->sk_path().addPath(*m_path);
+    return new_path;
+}
+
+NonnullOwnPtr<PathImpl> PathImplSkia::copy_transformed(Gfx::AffineTransform const& transform) const
+{
+    auto new_path = PathImplSkia::create();
+    auto matrix = SkMatrix::MakeAll(
+        transform.a(), transform.c(), transform.e(),
+        transform.b(), transform.d(), transform.f(),
+        0, 0, 1);
+    new_path->sk_path().addPath(*m_path, matrix);
+    return new_path;
+}
+
+}

+ 52 - 0
Userland/Libraries/LibGfx/PathSkia.h

@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2024, Andreas Kling <andreas@ladybird.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibGfx/Path.h>
+
+class SkPath;
+
+namespace Gfx {
+
+class PathImplSkia final : public PathImpl {
+public:
+    static NonnullOwnPtr<Gfx::PathImplSkia> create();
+
+    virtual ~PathImplSkia() override;
+
+    virtual void clear() override;
+    virtual void move_to(Gfx::FloatPoint const&) override;
+    virtual void line_to(Gfx::FloatPoint const&) override;
+    virtual void close_all_subpaths() override;
+    virtual void close() override;
+    virtual void elliptical_arc_to(FloatPoint point, FloatSize radii, float x_axis_rotation, bool large_arc, bool sweep) override;
+    virtual void arc_to(FloatPoint point, float radius, bool large_arc, bool sweep) override;
+    virtual void quadratic_bezier_curve_to(FloatPoint through, FloatPoint point) override;
+    virtual void cubic_bezier_curve_to(FloatPoint c1, FloatPoint c2, FloatPoint p2) override;
+    virtual void text(Utf8View, Font const&) override;
+
+    virtual void append_path(Gfx::Path const&) override;
+    virtual void intersect(Gfx::Path const&) override;
+
+    [[nodiscard]] virtual bool is_empty() const override;
+    virtual Gfx::FloatPoint last_point() const override;
+    virtual Gfx::FloatRect bounding_box() const override;
+
+    virtual NonnullOwnPtr<PathImpl> clone() const override;
+    virtual NonnullOwnPtr<PathImpl> copy_transformed(Gfx::AffineTransform const&) const override;
+
+    SkPath const& sk_path() const { return *m_path; }
+    SkPath& sk_path() { return *m_path; }
+
+private:
+    PathImplSkia();
+
+    Gfx::FloatPoint m_last_move_to;
+    NonnullOwnPtr<SkPath> m_path;
+};
+
+}

+ 4 - 4
Userland/Libraries/LibWeb/HTML/Canvas/CanvasPath.h

@@ -6,7 +6,7 @@
 
 #pragma once
 
-#include <LibGfx/DeprecatedPath.h>
+#include <LibGfx/Path.h>
 #include <LibWeb/Geometry/DOMPointReadOnly.h>
 #include <LibWeb/HTML/Canvas/CanvasState.h>
 #include <LibWeb/WebIDL/ExceptionOr.h>
@@ -30,8 +30,8 @@ public:
     WebIDL::ExceptionOr<void> arc(float x, float y, float radius, float start_angle, float end_angle, bool counter_clockwise);
     WebIDL::ExceptionOr<void> ellipse(float x, float y, float radius_x, float radius_y, float rotation, float start_angle, float end_angle, bool counter_clockwise);
 
-    Gfx::DeprecatedPath& path() { return m_path; }
-    Gfx::DeprecatedPath const& path() const { return m_path; }
+    Gfx::Path& path() { return m_path; }
+    Gfx::Path const& path() const { return m_path; }
 
 protected:
     explicit CanvasPath(Bindings::PlatformObject& self)
@@ -52,7 +52,7 @@ private:
 
     JS::NonnullGCPtr<Bindings::PlatformObject> m_self;
     Optional<CanvasState const&> m_canvas_state;
-    Gfx::DeprecatedPath m_path;
+    Gfx::Path m_path;
 };
 
 }

+ 8 - 2
Userland/Libraries/LibWeb/HTML/Canvas/CanvasState.h

@@ -13,7 +13,8 @@
 #include <LibGfx/Color.h>
 #include <LibGfx/Font/Font.h>
 #include <LibGfx/PaintStyle.h>
-#include <LibGfx/PathClipper.h>
+#include <LibGfx/Path.h>
+#include <LibGfx/WindingRule.h>
 #include <LibWeb/Bindings/CanvasRenderingContext2DPrototype.h>
 #include <LibWeb/HTML/CanvasGradient.h>
 #include <LibWeb/HTML/CanvasPattern.h>
@@ -23,6 +24,11 @@ namespace Web::HTML {
 // https://html.spec.whatwg.org/multipage/canvas.html#canvasstate
 class CanvasState {
 public:
+    struct ClipPath {
+        Gfx::Path path;
+        Gfx::WindingRule winding_rule;
+    };
+
     virtual ~CanvasState() = default;
 
     void save();
@@ -81,7 +87,7 @@ public:
         bool image_smoothing_enabled { true };
         Bindings::ImageSmoothingQuality image_smoothing_quality { Bindings::ImageSmoothingQuality::Low };
         float global_alpha = { 1 };
-        Optional<Gfx::ClipPath> clip;
+        Optional<ClipPath> clip;
         RefPtr<CSS::CSSStyleValue> font_style_value { nullptr };
         RefPtr<Gfx::Font const> current_font { nullptr };
         Bindings::CanvasTextAlign text_align { Bindings::CanvasTextAlign::Start };

+ 12 - 12
Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp

@@ -71,7 +71,7 @@ JS::NonnullGCPtr<HTMLCanvasElement> CanvasRenderingContext2D::canvas_for_binding
     return *m_element;
 }
 
-Gfx::DeprecatedPath CanvasRenderingContext2D::rect_path(float x, float y, float width, float height)
+Gfx::Path CanvasRenderingContext2D::rect_path(float x, float y, float width, float height)
 {
     auto& drawing_state = this->drawing_state();
 
@@ -80,13 +80,12 @@ Gfx::DeprecatedPath CanvasRenderingContext2D::rect_path(float x, float y, float
     auto bottom_left = drawing_state.transform.map(Gfx::FloatPoint(x, y + height));
     auto bottom_right = drawing_state.transform.map(Gfx::FloatPoint(x + width, y + height));
 
-    Gfx::DeprecatedPath path;
+    Gfx::Path path;
     path.move_to(top_left);
     path.line_to(top_right);
     path.line_to(bottom_right);
     path.line_to(bottom_left);
     path.line_to(top_left);
-
     return path;
 }
 
@@ -191,7 +190,7 @@ Gfx::Painter* CanvasRenderingContext2D::painter()
     return m_painter.ptr();
 }
 
-Gfx::DeprecatedPath CanvasRenderingContext2D::text_path(StringView text, float x, float y, Optional<double> max_width)
+Gfx::Path CanvasRenderingContext2D::text_path(StringView text, float x, float y, Optional<double> max_width)
 {
     if (max_width.has_value() && max_width.value() <= 0)
         return {};
@@ -199,7 +198,7 @@ Gfx::DeprecatedPath CanvasRenderingContext2D::text_path(StringView text, float x
     auto& drawing_state = this->drawing_state();
     auto font = current_font();
 
-    Gfx::DeprecatedPath path;
+    Gfx::Path path;
     path.move_to({ x, y });
     path.text(Utf8View { text }, *font);
 
@@ -241,8 +240,7 @@ Gfx::DeprecatedPath CanvasRenderingContext2D::text_path(StringView text, float x
     }
 
     transform = Gfx::AffineTransform { drawing_state.transform }.multiply(transform);
-    path = path.copy_transformed(transform);
-    return path;
+    return path.copy_transformed(transform);
 }
 
 void CanvasRenderingContext2D::fill_text(StringView text, float x, float y, Optional<double> max_width)
@@ -260,7 +258,7 @@ void CanvasRenderingContext2D::begin_path()
     path().clear();
 }
 
-void CanvasRenderingContext2D::stroke_internal(Gfx::DeprecatedPath const& path)
+void CanvasRenderingContext2D::stroke_internal(Gfx::Path const& path)
 {
     auto* painter = this->painter();
     if (!painter)
@@ -298,7 +296,7 @@ static Gfx::WindingRule parse_fill_rule(StringView fill_rule)
     return Gfx::WindingRule::Nonzero;
 }
 
-void CanvasRenderingContext2D::fill_internal(Gfx::DeprecatedPath const& path, Gfx::WindingRule winding_rule)
+void CanvasRenderingContext2D::fill_internal(Gfx::Path const& path, Gfx::WindingRule winding_rule)
 {
     auto* painter = this->painter();
     if (!painter)
@@ -543,15 +541,17 @@ CanvasRenderingContext2D::PreparedText CanvasRenderingContext2D::prepare_text(By
     return prepared_text;
 }
 
-void CanvasRenderingContext2D::clip_internal(Gfx::DeprecatedPath& path, Gfx::WindingRule winding_rule)
+void CanvasRenderingContext2D::clip_internal(Gfx::Path& path, Gfx::WindingRule winding_rule)
 {
     // FIXME: This should calculate the new clip path by intersecting the given path with the current one.
     // See: https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-clip-dev
     path.close_all_subpaths();
     if (drawing_state().clip.has_value()) {
-        dbgln("FIXME: CRC2D: Calculate the new clip path by intersecting the given path with the current one.");
+        auto& current_clip = drawing_state().clip->path;
+        current_clip.intersect(path);
+        return;
     }
-    drawing_state().clip = Gfx::ClipPath { path, winding_rule };
+    drawing_state().clip = CanvasState::ClipPath { path, winding_rule };
 }
 
 void CanvasRenderingContext2D::clip(StringView fill_rule)

+ 6 - 8
Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h

@@ -11,10 +11,9 @@
 #include <AK/Variant.h>
 #include <LibGfx/AffineTransform.h>
 #include <LibGfx/Color.h>
-#include <LibGfx/DeprecatedPath.h>
 #include <LibGfx/Forward.h>
 #include <LibGfx/Painter.h>
-#include <LibGfx/PathClipper.h>
+#include <LibGfx/Path.h>
 #include <LibWeb/Bindings/PlatformObject.h>
 #include <LibWeb/HTML/Canvas/CanvasCompositing.h>
 #include <LibWeb/HTML/Canvas/CanvasDrawImage.h>
@@ -124,13 +123,12 @@ private:
 
     PreparedText prepare_text(ByteString const& text, float max_width = INFINITY);
 
-    Gfx::DeprecatedPath rect_path(float x, float y, float width, float height);
+    [[nodiscard]] Gfx::Path rect_path(float x, float y, float width, float height);
+    [[nodiscard]] Gfx::Path text_path(StringView text, float x, float y, Optional<double> max_width);
 
-    Gfx::DeprecatedPath text_path(StringView text, float x, float y, Optional<double> max_width);
-
-    void stroke_internal(Gfx::DeprecatedPath const&);
-    void fill_internal(Gfx::DeprecatedPath const&, Gfx::WindingRule);
-    void clip_internal(Gfx::DeprecatedPath&, Gfx::WindingRule);
+    void stroke_internal(Gfx::Path const&);
+    void fill_internal(Gfx::Path const&, Gfx::WindingRule);
+    void clip_internal(Gfx::Path&, Gfx::WindingRule);
 
     JS::NonnullGCPtr<HTMLCanvasElement> m_element;
     OwnPtr<Gfx::Painter> m_painter;

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

@@ -7,6 +7,7 @@
 #include <AK/Debug.h>
 #include <AK/Optional.h>
 #include <LibGfx/DeprecatedPath.h>
+#include <LibGfx/Path.h>
 #include <LibWeb/Bindings/SVGPathElementPrototype.h>
 #include <LibWeb/DOM/Document.h>
 #include <LibWeb/DOM/Event.h>
@@ -104,9 +105,10 @@ void SVGPathElement::attribute_changed(FlyString const& name, Optional<String> c
         m_instructions = AttributeParser::parse_path_data(value.value_or(String {}));
 }
 
-Gfx::DeprecatedPath path_from_path_instructions(ReadonlySpan<PathInstruction> instructions)
+template<typename PathType>
+PathType path_from_path_instructions(ReadonlySpan<PathInstruction> instructions)
 {
-    Gfx::DeprecatedPath path;
+    PathType path;
     Optional<Gfx::FloatPoint> previous_control_point;
     PathInstructionType last_instruction = PathInstructionType::Invalid;
 
@@ -272,9 +274,19 @@ Gfx::DeprecatedPath path_from_path_instructions(ReadonlySpan<PathInstruction> in
     return path;
 }
 
+Gfx::Path path_from_path_instructions(ReadonlySpan<PathInstruction> instructions)
+{
+    return path_from_path_instructions<Gfx::Path>(instructions);
+}
+
+Gfx::DeprecatedPath deprecated_path_from_path_instructions(ReadonlySpan<PathInstruction> instructions)
+{
+    return path_from_path_instructions<Gfx::DeprecatedPath>(instructions);
+}
+
 Gfx::DeprecatedPath SVGPathElement::get_path(CSSPixelSize)
 {
-    return path_from_path_instructions(m_instructions);
+    return deprecated_path_from_path_instructions(m_instructions);
 }
 
 }

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

@@ -32,6 +32,7 @@ private:
     Vector<PathInstruction> m_instructions;
 };
 
-Gfx::DeprecatedPath path_from_path_instructions(ReadonlySpan<PathInstruction>);
+[[nodiscard]] Gfx::Path path_from_path_instructions(ReadonlySpan<PathInstruction>);
+[[nodiscard]] Gfx::DeprecatedPath deprecated_path_from_path_instructions(ReadonlySpan<PathInstruction>);
 
 }