diff --git a/Userland/Libraries/LibGfx/CMakeLists.txt b/Userland/Libraries/LibGfx/CMakeLists.txt index 8ef54b98b62..14a3dafc5c5 100644 --- a/Userland/Libraries/LibGfx/CMakeLists.txt +++ b/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 diff --git a/Userland/Libraries/LibGfx/Forward.h b/Userland/Libraries/LibGfx/Forward.h index 723c7450fbf..b510e2cee03 100644 --- a/Userland/Libraries/LibGfx/Forward.h +++ b/Userland/Libraries/LibGfx/Forward.h @@ -31,6 +31,7 @@ class Painter; class Palette; class PaletteImpl; class DeprecatedPath; +class Path; class ShareableBitmap; struct SystemTheme; diff --git a/Userland/Libraries/LibGfx/Painter.h b/Userland/Libraries/LibGfx/Painter.h index 0b832638f15..510099ab3b3 100644 --- a/Userland/Libraries/LibGfx/Painter.h +++ b/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; diff --git a/Userland/Libraries/LibGfx/PainterSkia.cpp b/Userland/Libraries/LibGfx/PainterSkia.cpp index ebaa8b04b2b..f9889920a63 100644 --- a/Userland/Libraries/LibGfx/PainterSkia.cpp +++ b/Userland/Libraries/LibGfx/PainterSkia.cpp @@ -7,8 +7,8 @@ #define AK_DONT_REPLACE_STD #include -#include #include +#include #include #include @@ -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 subpath_start_point; - Optional 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(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)); diff --git a/Userland/Libraries/LibGfx/PainterSkia.h b/Userland/Libraries/LibGfx/PainterSkia.h index 4d95a9baaed..bbb7319be67 100644 --- a/Userland/Libraries/LibGfx/PainterSkia.h +++ b/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; diff --git a/Userland/Libraries/LibGfx/Path.cpp b/Userland/Libraries/LibGfx/Path.cpp new file mode 100644 index 00000000000..06dd5fa27fd --- /dev/null +++ b/Userland/Libraries/LibGfx/Path.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2024, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace Gfx { + +NonnullOwnPtr PathImpl::create() +{ + return PathImplSkia::create(); +} + +PathImpl::~PathImpl() = default; + +} diff --git a/Userland/Libraries/LibGfx/Path.h b/Userland/Libraries/LibGfx/Path.h new file mode 100644 index 00000000000..8c34c140e5a --- /dev/null +++ b/Userland/Libraries/LibGfx/Path.h @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2024, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace Gfx { + +class PathImpl { +public: + static NonnullOwnPtr 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 clone() const = 0; + virtual NonnullOwnPtr 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&& impl) + : m_impl(move(impl)) + { + } + + NonnullOwnPtr m_impl { PathImpl::create() }; +}; + +} diff --git a/Userland/Libraries/LibGfx/PathSkia.cpp b/Userland/Libraries/LibGfx/PathSkia.cpp new file mode 100644 index 00000000000..fd43a1c9f75 --- /dev/null +++ b/Userland/Libraries/LibGfx/PathSkia.cpp @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2024, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#define AK_DONT_REPLACE_STD + +#include +#include +#include +#include +#include +#include +#include + +namespace Gfx { + +NonnullOwnPtr 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(font).skia_font(1), m_path.ptr()); +} + +void PathImplSkia::append_path(Gfx::Path const& other) +{ + m_path->addPath(static_cast(other.impl()).sk_path()); +} + +void PathImplSkia::intersect(Gfx::Path const& other) +{ + Op(*m_path, static_cast(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 PathImplSkia::clone() const +{ + auto new_path = PathImplSkia::create(); + new_path->sk_path().addPath(*m_path); + return new_path; +} + +NonnullOwnPtr 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; +} + +} diff --git a/Userland/Libraries/LibGfx/PathSkia.h b/Userland/Libraries/LibGfx/PathSkia.h new file mode 100644 index 00000000000..1ed2db8b0d7 --- /dev/null +++ b/Userland/Libraries/LibGfx/PathSkia.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +class SkPath; + +namespace Gfx { + +class PathImplSkia final : public PathImpl { +public: + static NonnullOwnPtr 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 clone() const override; + virtual NonnullOwnPtr 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 m_path; +}; + +} diff --git a/Userland/Libraries/LibWeb/HTML/Canvas/CanvasPath.h b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasPath.h index 4566db55b8a..e729abfa419 100644 --- a/Userland/Libraries/LibWeb/HTML/Canvas/CanvasPath.h +++ b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasPath.h @@ -6,7 +6,7 @@ #pragma once -#include +#include #include #include #include @@ -30,8 +30,8 @@ public: WebIDL::ExceptionOr arc(float x, float y, float radius, float start_angle, float end_angle, bool counter_clockwise); WebIDL::ExceptionOr 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 m_self; Optional m_canvas_state; - Gfx::DeprecatedPath m_path; + Gfx::Path m_path; }; } diff --git a/Userland/Libraries/LibWeb/HTML/Canvas/CanvasState.h b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasState.h index 14c7a8d4388..8eb4274b122 100644 --- a/Userland/Libraries/LibWeb/HTML/Canvas/CanvasState.h +++ b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasState.h @@ -13,7 +13,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -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 clip; + Optional clip; RefPtr font_style_value { nullptr }; RefPtr current_font { nullptr }; Bindings::CanvasTextAlign text_align { Bindings::CanvasTextAlign::Start }; diff --git a/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp index d6ceebbc68b..300163600aa 100644 --- a/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp +++ b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp @@ -71,7 +71,7 @@ JS::NonnullGCPtr 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 max_width) +Gfx::Path CanvasRenderingContext2D::text_path(StringView text, float x, float y, Optional 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 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) diff --git a/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h index 0da6af547b8..5072cdcb6be 100644 --- a/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h +++ b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h @@ -11,10 +11,9 @@ #include #include #include -#include #include #include -#include +#include #include #include #include @@ -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 max_width); - Gfx::DeprecatedPath text_path(StringView text, float x, float y, Optional 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 m_element; OwnPtr m_painter; diff --git a/Userland/Libraries/LibWeb/SVG/SVGPathElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGPathElement.cpp index 34e49706166..15cde35a03c 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGPathElement.cpp +++ b/Userland/Libraries/LibWeb/SVG/SVGPathElement.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -104,9 +105,10 @@ void SVGPathElement::attribute_changed(FlyString const& name, Optional c m_instructions = AttributeParser::parse_path_data(value.value_or(String {})); } -Gfx::DeprecatedPath path_from_path_instructions(ReadonlySpan instructions) +template +PathType path_from_path_instructions(ReadonlySpan instructions) { - Gfx::DeprecatedPath path; + PathType path; Optional previous_control_point; PathInstructionType last_instruction = PathInstructionType::Invalid; @@ -272,9 +274,19 @@ Gfx::DeprecatedPath path_from_path_instructions(ReadonlySpan in return path; } +Gfx::Path path_from_path_instructions(ReadonlySpan instructions) +{ + return path_from_path_instructions(instructions); +} + +Gfx::DeprecatedPath deprecated_path_from_path_instructions(ReadonlySpan instructions) +{ + return path_from_path_instructions(instructions); +} + Gfx::DeprecatedPath SVGPathElement::get_path(CSSPixelSize) { - return path_from_path_instructions(m_instructions); + return deprecated_path_from_path_instructions(m_instructions); } } diff --git a/Userland/Libraries/LibWeb/SVG/SVGPathElement.h b/Userland/Libraries/LibWeb/SVG/SVGPathElement.h index bd16a41b893..b98dfd8119c 100644 --- a/Userland/Libraries/LibWeb/SVG/SVGPathElement.h +++ b/Userland/Libraries/LibWeb/SVG/SVGPathElement.h @@ -32,6 +32,7 @@ private: Vector m_instructions; }; -Gfx::DeprecatedPath path_from_path_instructions(ReadonlySpan); +[[nodiscard]] Gfx::Path path_from_path_instructions(ReadonlySpan); +[[nodiscard]] Gfx::DeprecatedPath deprecated_path_from_path_instructions(ReadonlySpan); }