From 4f2770a74531670308451891747b48995ddfaa4d Mon Sep 17 00:00:00 2001 From: MacDue Date: Sat, 17 Jun 2023 00:17:22 +0100 Subject: [PATCH] LibWeb: Apply canvas transform while building paths The transform can change between path building operations (and before the path is filled or stroked). This fixes the sun ray backgroun on the https://www.kevs3d.co.uk/dev/html5logo/ canvas demo. --- .../LibWeb/HTML/Canvas/CanvasPath.cpp | 37 +++++++++++++------ .../Libraries/LibWeb/HTML/Canvas/CanvasPath.h | 10 +++++ .../LibWeb/HTML/CanvasRenderingContext2D.cpp | 10 ++--- 3 files changed, 40 insertions(+), 17 deletions(-) diff --git a/Userland/Libraries/LibWeb/HTML/Canvas/CanvasPath.cpp b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasPath.cpp index 2577583f68..1b42fb3bbc 100644 --- a/Userland/Libraries/LibWeb/HTML/Canvas/CanvasPath.cpp +++ b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasPath.cpp @@ -10,6 +10,13 @@ namespace Web::HTML { +Gfx::AffineTransform CanvasPath::active_transform() const +{ + if (m_canvas_state.has_value()) + return m_canvas_state->drawing_state().transform; + return {}; +} + void CanvasPath::close_path() { m_path.close(); @@ -17,22 +24,25 @@ void CanvasPath::close_path() void CanvasPath::move_to(float x, float y) { - m_path.move_to({ x, y }); + m_path.move_to(active_transform().map(Gfx::FloatPoint { x, y })); } void CanvasPath::line_to(float x, float y) { - m_path.line_to({ x, y }); + m_path.line_to(active_transform().map(Gfx::FloatPoint { x, y })); } void CanvasPath::quadratic_curve_to(float cx, float cy, float x, float y) { - m_path.quadratic_bezier_curve_to({ cx, cy }, { x, y }); + auto transform = active_transform(); + m_path.quadratic_bezier_curve_to(transform.map(Gfx::FloatPoint { cx, cy }), transform.map(Gfx::FloatPoint { x, y })); } void CanvasPath::bezier_curve_to(double cp1x, double cp1y, double cp2x, double cp2y, double x, double y) { - m_path.cubic_bezier_curve_to(Gfx::FloatPoint(cp1x, cp1y), Gfx::FloatPoint(cp2x, cp2y), Gfx::FloatPoint(x, y)); + auto transform = active_transform(); + m_path.cubic_bezier_curve_to( + transform.map(Gfx::FloatPoint { cp1x, cp1y }), transform.map(Gfx::FloatPoint { cp2x, cp2y }), transform.map(Gfx::FloatPoint { x, y })); } WebIDL::ExceptionOr CanvasPath::arc(float x, float y, float radius, float start_angle, float end_angle, bool counter_clockwise) @@ -97,23 +107,28 @@ WebIDL::ExceptionOr CanvasPath::ellipse(float x, float y, float radius_x, auto start_point = resolve_point_with_angle(start_angle); auto end_point = resolve_point_with_angle(end_angle); - m_path.move_to(start_point); - auto delta_theta = end_angle - start_angle; - m_path.elliptical_arc_to(end_point, { radius_x, radius_y }, rotation, delta_theta > AK::Pi, !counter_clockwise); + auto transform = active_transform(); + m_path.move_to(transform.map(start_point)); + m_path.elliptical_arc_to( + transform.map(Gfx::FloatPoint { end_point }), + transform.map(Gfx::FloatSize { radius_x, radius_y }), + rotation + transform.rotation(), + delta_theta > AK::Pi, !counter_clockwise); return {}; } void CanvasPath::rect(float x, float y, float width, float height) { - m_path.move_to({ x, y }); + auto transform = active_transform(); + m_path.move_to(transform.map(Gfx::FloatPoint { x, y })); if (width == 0 || height == 0) return; - m_path.line_to({ x + width, y }); - m_path.line_to({ x + width, y + height }); - m_path.line_to({ x, y + height }); + m_path.line_to(transform.map(Gfx::FloatPoint { x + width, y })); + m_path.line_to(transform.map(Gfx::FloatPoint { x + width, y + height })); + m_path.line_to(transform.map(Gfx::FloatPoint { x, y + height })); m_path.close(); } diff --git a/Userland/Libraries/LibWeb/HTML/Canvas/CanvasPath.h b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasPath.h index c9c584d5a7..6510239c15 100644 --- a/Userland/Libraries/LibWeb/HTML/Canvas/CanvasPath.h +++ b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasPath.h @@ -7,6 +7,7 @@ #pragma once #include +#include #include namespace Web::HTML { @@ -35,8 +36,17 @@ protected: { } + explicit CanvasPath(Bindings::PlatformObject& self, CanvasState const& canvas_state) + : m_self(self) + , m_canvas_state(canvas_state) + { + } + private: + Gfx::AffineTransform active_transform() const; + JS::NonnullGCPtr m_self; + Optional m_canvas_state; Gfx::Path m_path; }; diff --git a/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp index 85515d76c6..e614126c2d 100644 --- a/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp +++ b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp @@ -33,7 +33,7 @@ WebIDL::ExceptionOr> CanvasRenderingC CanvasRenderingContext2D::CanvasRenderingContext2D(JS::Realm& realm, HTMLCanvasElement& element) : PlatformObject(realm) - , CanvasPath(static_cast(*this)) + , CanvasPath(static_cast(*this), *this) , m_element(element) { } @@ -243,8 +243,7 @@ void CanvasRenderingContext2D::stroke_internal(Gfx::Path const& path) void CanvasRenderingContext2D::stroke() { - auto transformed_path = path().copy_transformed(drawing_state().transform); - stroke_internal(transformed_path); + stroke_internal(path()); } void CanvasRenderingContext2D::stroke(Path2D const& path) @@ -265,7 +264,7 @@ static Gfx::Painter::WindingRule parse_fill_rule(StringView fill_rule) void CanvasRenderingContext2D::fill_internal(Gfx::Path& path, StringView fill_rule_value) { - draw_clipped([&](auto& painter) { + draw_clipped([=, this](auto& painter) mutable { path.close_all_subpaths(); auto& drawing_state = this->drawing_state(); auto fill_rule = parse_fill_rule(fill_rule_value); @@ -280,8 +279,7 @@ void CanvasRenderingContext2D::fill_internal(Gfx::Path& path, StringView fill_ru void CanvasRenderingContext2D::fill(DeprecatedString const& fill_rule) { - auto transformed_path = path().copy_transformed(drawing_state().transform); - return fill_internal(transformed_path, fill_rule); + return fill_internal(path(), fill_rule); } void CanvasRenderingContext2D::fill(Path2D& path, DeprecatedString const& fill_rule)