mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-29 20:29:18 +00:00
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:
parent
c8f09312f7
commit
a3cc03f180
Notes:
github-actions[bot]
2024-08-20 07:38:18 +00:00
Author: https://github.com/awesomekling
Commit: a3cc03f180
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/1024
15 changed files with 417 additions and 93 deletions
|
@ -57,7 +57,9 @@ set(SOURCES
|
|||
ImmutableBitmap.cpp
|
||||
MedianCut.cpp
|
||||
Palette.cpp
|
||||
Path.cpp
|
||||
PathClipper.cpp
|
||||
PathSkia.cpp
|
||||
Painter.cpp
|
||||
PainterSkia.cpp
|
||||
Point.cpp
|
||||
|
|
|
@ -31,6 +31,7 @@ class Painter;
|
|||
class Palette;
|
||||
class PaletteImpl;
|
||||
class DeprecatedPath;
|
||||
class Path;
|
||||
class ShareableBitmap;
|
||||
struct SystemTheme;
|
||||
|
||||
|
|
|
@ -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,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));
|
||||
|
|
|
@ -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
Userland/Libraries/LibGfx/Path.cpp
Normal file
19
Userland/Libraries/LibGfx/Path.cpp
Normal 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;
|
||||
|
||||
}
|
101
Userland/Libraries/LibGfx/Path.h
Normal file
101
Userland/Libraries/LibGfx/Path.h
Normal 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() };
|
||||
};
|
||||
|
||||
}
|
180
Userland/Libraries/LibGfx/PathSkia.cpp
Normal file
180
Userland/Libraries/LibGfx/PathSkia.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
52
Userland/Libraries/LibGfx/PathSkia.h
Normal file
52
Userland/Libraries/LibGfx/PathSkia.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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>);
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue