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.
This commit is contained in:
Andreas Kling 2024-08-08 15:12:29 +02:00 committed by Andreas Kling
commit a3cc03f180
Notes: github-actions[bot] 2024-08-20 07:38:18 +00:00
15 changed files with 417 additions and 93 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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() };
};
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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