1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 19:17:44 +00:00

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.
This commit is contained in:
MacDue 2023-01-18 20:10:00 +01:00 committed by Andreas Kling
parent 2be4142138
commit 24cb57ac88
5 changed files with 89 additions and 17 deletions

View file

@ -2,6 +2,7 @@
* Copyright (c) 2020-2022, Andreas Kling <kling@serenityos.org> * Copyright (c) 2020-2022, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org> * Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org> * Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
* Copyright (c) 2023, MacDue <macdue@dueutil.tech>
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
@ -19,27 +20,39 @@ template<typename IncludingClass>
class CanvasFillStrokeStyles { class CanvasFillStrokeStyles {
public: public:
~CanvasFillStrokeStyles() = default; ~CanvasFillStrokeStyles() = default;
using FillOrStrokeStyleVariant = Variant<DeprecatedString, JS::Handle<CanvasGradient>>;
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<CanvasGradient> 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. // 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. // 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<CanvasGradient> create_radial_gradient(double x0, double y0, double r0, double x1, double y1, double r1) JS::NonnullGCPtr<CanvasGradient> create_radial_gradient(double x0, double y0, double r0, double x1, double y1, double r1)

View file

@ -3,9 +3,9 @@
// https://html.spec.whatwg.org/multipage/canvas.html#canvasfillstrokestyles // https://html.spec.whatwg.org/multipage/canvas.html#canvasfillstrokestyles
interface mixin CanvasFillStrokeStyles { interface mixin CanvasFillStrokeStyles {
// FIXME: Should be `(DOMString or CanvasGradient or CanvasPattern)` // FIXME: Should be `(DOMString or CanvasGradient or CanvasPattern)`
attribute DOMString strokeStyle; attribute (DOMString or CanvasGradient) strokeStyle;
// FIXME: Should be `(DOMString or CanvasGradient or CanvasPattern)` // 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 createLinearGradient(double x0, double y0, double x1, double y1);
CanvasGradient createRadialGradient(double x0, double y0, double r0, double x1, double y1, double r1); CanvasGradient createRadialGradient(double x0, double y0, double r0, double x1, double y1, double r1);
CanvasGradient createConicGradient(double startAngle, double x, double y); CanvasGradient createConicGradient(double startAngle, double x, double y);

View file

@ -1,6 +1,7 @@
/* /*
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org> * Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org> * Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
* Copyright (c) 2023, MacDue <macdue@dueutil.tech>
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
@ -39,4 +40,21 @@ bool CanvasState::is_context_lost()
return m_context_lost; return m_context_lost;
} }
NonnullRefPtr<Gfx::PaintStyle> 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<Gfx::Color> CanvasState::FillOrStrokeStyle::as_color() const
{
if (auto* color = m_fill_or_stoke_style.get_pointer<Gfx::Color>())
return *color;
return {};
}
} }

View file

@ -1,14 +1,18 @@
/* /*
* Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org> * Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
* Copyright (c) 2023, MacDue <macdue@dueutil.tech>
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#pragma once #pragma once
#include <AK/Variant.h>
#include <AK/Vector.h> #include <AK/Vector.h>
#include <LibGfx/AffineTransform.h> #include <LibGfx/AffineTransform.h>
#include <LibGfx/Color.h> #include <LibGfx/Color.h>
#include <LibGfx/PaintStyle.h>
#include <LibWeb/HTML/CanvasGradient.h>
namespace Web::HTML { namespace Web::HTML {
@ -22,11 +26,41 @@ public:
void reset(); void reset();
bool is_context_lost(); bool is_context_lost();
using FillOrStrokeVariant = Variant<Gfx::Color, JS::Handle<CanvasGradient>>;
struct FillOrStrokeStyle {
FillOrStrokeStyle(Gfx::Color color)
: m_fill_or_stoke_style(color)
{
}
FillOrStrokeStyle(JS::Handle<CanvasGradient> gradient)
: m_fill_or_stoke_style(gradient)
{
}
NonnullRefPtr<Gfx::PaintStyle> to_gfx_paint_style();
Optional<Gfx::Color> as_color() const;
Gfx::Color to_color_but_fixme_should_accept_any_paint_style() const;
Variant<DeprecatedString, JS::Handle<CanvasGradient>> to_js_fill_or_stoke_style() const
{
if (auto* handle = m_fill_or_stoke_style.get_pointer<JS::Handle<CanvasGradient>>())
return *handle;
return m_fill_or_stoke_style.get<Gfx::Color>().to_deprecated_string();
}
private:
FillOrStrokeVariant m_fill_or_stoke_style;
RefPtr<Gfx::PaintStyle> m_color_fill_style { nullptr };
};
// https://html.spec.whatwg.org/multipage/canvas.html#drawing-state // https://html.spec.whatwg.org/multipage/canvas.html#drawing-state
struct DrawingState { struct DrawingState {
Gfx::AffineTransform transform; Gfx::AffineTransform transform;
Gfx::Color fill_style { Gfx::Color::Black }; FillOrStrokeStyle fill_style { Gfx::Color::Black };
Gfx::Color stroke_style { Gfx::Color::Black }; FillOrStrokeStyle stroke_style { Gfx::Color::Black };
float line_width { 1 }; float line_width { 1 };
}; };
DrawingState& drawing_state() { return m_drawing_state; } DrawingState& drawing_state() { return m_drawing_state; }

View file

@ -1,6 +1,7 @@
/* /*
* Copyright (c) 2020-2022, Andreas Kling <kling@serenityos.org> * Copyright (c) 2020-2022, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org> * Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2023, MacDue <macdue@dueutil.tech>
* *
* SPDX-License-Identifier: BSD-2-Clause * 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& drawing_state = this->drawing_state();
auto rect = drawing_state.transform.map(Gfx::FloatRect(x, y, width, height)); 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<int>(), *drawing_state.fill_style.to_gfx_paint_style());
}
did_draw(rect); 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_right);
path.line_to(bottom_left); path.line_to(bottom_left);
path.line_to(top_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); 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<float>(max_width.value()) : painter->font().width(text), painter->font().pixel_size()); auto text_rect = Gfx::FloatRect(x, y, max_width.has_value() ? static_cast<float>(max_width.value()) : painter->font().width(text), painter->font().pixel_size());
auto transformed_rect = drawing_state.transform.map(text_rect); 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); did_draw(transformed_rect);
} }
@ -234,7 +241,7 @@ void CanvasRenderingContext2D::stroke_internal(Gfx::Path const& path)
auto& drawing_state = this->drawing_state(); 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()); did_draw(path.bounding_box());
} }
@ -266,7 +273,7 @@ void CanvasRenderingContext2D::fill_internal(Gfx::Path& path, DeprecatedString c
else else
dbgln("Unrecognized fillRule for CRC2D.fill() - this problem goes away once we pass an enum instead of a string"); 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()); did_draw(path.bounding_box());
} }