From 24cb57ac88534ed3ae778bf5625f7c1da793d46f Mon Sep 17 00:00:00 2001 From: MacDue Date: Wed, 18 Jan 2023 20:10:00 +0100 Subject: [PATCH] LibWeb: Update CRC2D .fillStyle and .strokeStyle to accept gradients While doing add some structures to hold these new fill styles and plumb them over to the painter. --- .../HTML/Canvas/CanvasFillStrokeStyles.h | 29 ++++++++++---- .../HTML/Canvas/CanvasFillStrokeStyles.idl | 4 +- .../LibWeb/HTML/Canvas/CanvasState.cpp | 18 +++++++++ .../LibWeb/HTML/Canvas/CanvasState.h | 38 ++++++++++++++++++- .../LibWeb/HTML/CanvasRenderingContext2D.cpp | 17 ++++++--- 5 files changed, 89 insertions(+), 17 deletions(-) diff --git a/Userland/Libraries/LibWeb/HTML/Canvas/CanvasFillStrokeStyles.h b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasFillStrokeStyles.h index e5fa17a78e..3ed170f201 100644 --- a/Userland/Libraries/LibWeb/HTML/Canvas/CanvasFillStrokeStyles.h +++ b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasFillStrokeStyles.h @@ -2,6 +2,7 @@ * Copyright (c) 2020-2022, Andreas Kling * Copyright (c) 2021-2022, Linus Groh * Copyright (c) 2022, Sam Atkins + * Copyright (c) 2023, MacDue * * SPDX-License-Identifier: BSD-2-Clause */ @@ -19,27 +20,39 @@ template class CanvasFillStrokeStyles { public: ~CanvasFillStrokeStyles() = default; + using FillOrStrokeStyleVariant = Variant>; - void set_fill_style(DeprecatedString style) + static CanvasState::FillOrStrokeStyle to_canvas_state_fill_or_stoke_style(auto const& style) + { + return style.visit( + [&](DeprecatedString const& string) -> CanvasState::FillOrStrokeStyle { + return Gfx::Color::from_string(string).value_or(Color::Black); + }, + [&](JS::Handle gradient) -> CanvasState::FillOrStrokeStyle { + return gradient; + }); + } + + void set_fill_style(FillOrStrokeStyleVariant style) { // FIXME: 2. If the given value is a CanvasPattern object that is marked as not origin-clean, then set this's origin-clean flag to false. - my_drawing_state().fill_style = Gfx::Color::from_string(style).value_or(Color::Black); + my_drawing_state().fill_style = to_canvas_state_fill_or_stoke_style(style); } - DeprecatedString fill_style() const + FillOrStrokeStyleVariant fill_style() const { - return my_drawing_state().fill_style.to_deprecated_string(); + return my_drawing_state().fill_style.to_js_fill_or_stoke_style(); } - void set_stroke_style(DeprecatedString style) + void set_stroke_style(FillOrStrokeStyleVariant style) { // FIXME: 2. If the given value is a CanvasPattern object that is marked as not origin-clean, then set this's origin-clean flag to false. - my_drawing_state().stroke_style = Gfx::Color::from_string(style).value_or(Color::Black); + my_drawing_state().stroke_style = to_canvas_state_fill_or_stoke_style(style); } - DeprecatedString stroke_style() const + FillOrStrokeStyleVariant stroke_style() const { - return my_drawing_state().stroke_style.to_deprecated_string(); + return my_drawing_state().stroke_style.to_js_fill_or_stoke_style(); } JS::NonnullGCPtr create_radial_gradient(double x0, double y0, double r0, double x1, double y1, double r1) diff --git a/Userland/Libraries/LibWeb/HTML/Canvas/CanvasFillStrokeStyles.idl b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasFillStrokeStyles.idl index 45b393c7cf..ee8e8c1134 100644 --- a/Userland/Libraries/LibWeb/HTML/Canvas/CanvasFillStrokeStyles.idl +++ b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasFillStrokeStyles.idl @@ -3,9 +3,9 @@ // https://html.spec.whatwg.org/multipage/canvas.html#canvasfillstrokestyles interface mixin CanvasFillStrokeStyles { // FIXME: Should be `(DOMString or CanvasGradient or CanvasPattern)` - attribute DOMString strokeStyle; + attribute (DOMString or CanvasGradient) strokeStyle; // FIXME: Should be `(DOMString or CanvasGradient or CanvasPattern)` - attribute DOMString fillStyle; + attribute (DOMString or CanvasGradient) fillStyle; CanvasGradient createLinearGradient(double x0, double y0, double x1, double y1); CanvasGradient createRadialGradient(double x0, double y0, double r0, double x1, double y1, double r1); CanvasGradient createConicGradient(double startAngle, double x, double y); diff --git a/Userland/Libraries/LibWeb/HTML/Canvas/CanvasState.cpp b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasState.cpp index c907fe52a2..880a28b31e 100644 --- a/Userland/Libraries/LibWeb/HTML/Canvas/CanvasState.cpp +++ b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasState.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2021-2022, Linus Groh * Copyright (c) 2022, Sam Atkins + * Copyright (c) 2023, MacDue * * SPDX-License-Identifier: BSD-2-Clause */ @@ -39,4 +40,21 @@ bool CanvasState::is_context_lost() return m_context_lost; } +NonnullRefPtr CanvasState::FillOrStrokeStyle::to_gfx_paint_style() +{ + VERIFY_NOT_REACHED(); +} + +Gfx::Color CanvasState::FillOrStrokeStyle::to_color_but_fixme_should_accept_any_paint_style() const +{ + return as_color().value_or(Gfx::Color::Black); +} + +Optional CanvasState::FillOrStrokeStyle::as_color() const +{ + if (auto* color = m_fill_or_stoke_style.get_pointer()) + return *color; + return {}; +} + } diff --git a/Userland/Libraries/LibWeb/HTML/Canvas/CanvasState.h b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasState.h index 9e230c8ba7..eef073bf77 100644 --- a/Userland/Libraries/LibWeb/HTML/Canvas/CanvasState.h +++ b/Userland/Libraries/LibWeb/HTML/Canvas/CanvasState.h @@ -1,14 +1,18 @@ /* * Copyright (c) 2022, Sam Atkins + * Copyright (c) 2023, MacDue * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once +#include #include #include #include +#include +#include namespace Web::HTML { @@ -22,11 +26,41 @@ public: void reset(); bool is_context_lost(); + using FillOrStrokeVariant = Variant>; + + struct FillOrStrokeStyle { + FillOrStrokeStyle(Gfx::Color color) + : m_fill_or_stoke_style(color) + { + } + + FillOrStrokeStyle(JS::Handle gradient) + : m_fill_or_stoke_style(gradient) + { + } + + NonnullRefPtr to_gfx_paint_style(); + + Optional as_color() const; + Gfx::Color to_color_but_fixme_should_accept_any_paint_style() const; + + Variant> to_js_fill_or_stoke_style() const + { + if (auto* handle = m_fill_or_stoke_style.get_pointer>()) + return *handle; + return m_fill_or_stoke_style.get().to_deprecated_string(); + } + + private: + FillOrStrokeVariant m_fill_or_stoke_style; + RefPtr m_color_fill_style { nullptr }; + }; + // https://html.spec.whatwg.org/multipage/canvas.html#drawing-state struct DrawingState { Gfx::AffineTransform transform; - Gfx::Color fill_style { Gfx::Color::Black }; - Gfx::Color stroke_style { Gfx::Color::Black }; + FillOrStrokeStyle fill_style { Gfx::Color::Black }; + FillOrStrokeStyle stroke_style { Gfx::Color::Black }; float line_width { 1 }; }; DrawingState& drawing_state() { return m_drawing_state; } diff --git a/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp index 2b7a30705f..1b41e9d448 100644 --- a/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp +++ b/Userland/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp @@ -1,6 +1,7 @@ /* * Copyright (c) 2020-2022, Andreas Kling * Copyright (c) 2021-2022, Linus Groh + * Copyright (c) 2023, MacDue * * SPDX-License-Identifier: BSD-2-Clause */ @@ -73,7 +74,13 @@ void CanvasRenderingContext2D::fill_rect(float x, float y, float width, float he auto& drawing_state = this->drawing_state(); auto rect = drawing_state.transform.map(Gfx::FloatRect(x, y, width, height)); - painter->fill_rect(rect, drawing_state.fill_style); + auto color_fill = drawing_state.fill_style.as_color(); + if (color_fill.has_value()) { + painter->fill_rect(rect, *color_fill); + } else { + // FIXME: This should use AntiAliasingPainter::fill_rect() too but that does not support FillPath yet. + painter->underlying_painter().fill_rect(rect.to_rounded(), *drawing_state.fill_style.to_gfx_paint_style()); + } did_draw(rect); } @@ -109,7 +116,7 @@ void CanvasRenderingContext2D::stroke_rect(float x, float y, float width, float path.line_to(bottom_right); path.line_to(bottom_left); path.line_to(top_left); - painter->stroke_path(path, drawing_state.stroke_style, drawing_state.line_width); + painter->stroke_path(path, drawing_state.stroke_style.to_color_but_fixme_should_accept_any_paint_style(), drawing_state.line_width); did_draw(rect); } @@ -211,7 +218,7 @@ void CanvasRenderingContext2D::fill_text(DeprecatedString const& text, float x, auto text_rect = Gfx::FloatRect(x, y, max_width.has_value() ? static_cast(max_width.value()) : painter->font().width(text), painter->font().pixel_size()); auto transformed_rect = drawing_state.transform.map(text_rect); - painter->draw_text(transformed_rect, text, Gfx::TextAlignment::TopLeft, drawing_state.fill_style); + painter->draw_text(transformed_rect, text, Gfx::TextAlignment::TopLeft, drawing_state.fill_style.to_color_but_fixme_should_accept_any_paint_style()); did_draw(transformed_rect); } @@ -234,7 +241,7 @@ void CanvasRenderingContext2D::stroke_internal(Gfx::Path const& path) auto& drawing_state = this->drawing_state(); - painter->stroke_path(path, drawing_state.stroke_style, drawing_state.line_width); + painter->stroke_path(path, drawing_state.stroke_style.to_color_but_fixme_should_accept_any_paint_style(), drawing_state.line_width); did_draw(path.bounding_box()); } @@ -266,7 +273,7 @@ void CanvasRenderingContext2D::fill_internal(Gfx::Path& path, DeprecatedString c else dbgln("Unrecognized fillRule for CRC2D.fill() - this problem goes away once we pass an enum instead of a string"); - painter->fill_path(path, drawing_state().fill_style, winding); + painter->fill_path(path, *drawing_state().fill_style.to_gfx_paint_style(), winding); did_draw(path.bounding_box()); }