mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-01 21:59:07 +00:00
LibWeb: Implemented shadows support for CanvasRenderingContext2D
This commit is contained in:
parent
6d68d6ddb2
commit
34f7bf979d
Notes:
github-actions[bot]
2024-10-23 18:44:55 +00:00
Author: https://github.com/uysalibov 🔰
Commit: 34f7bf979d
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/1848
Reviewed-by: https://github.com/kalenikaliaksandr ✅
5 changed files with 152 additions and 3 deletions
40
Userland/Libraries/LibWeb/HTML/Canvas/CanvasShadowStyles.h
Normal file
40
Userland/Libraries/LibWeb/HTML/Canvas/CanvasShadowStyles.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (c) 2024, İbrahim UYSAL <uysalibov@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <LibWeb/CSS/Parser/Parser.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasState.h>
|
||||
#include <LibWeb/HTML/CanvasGradient.h>
|
||||
#include <LibWeb/HTML/CanvasPattern.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#canvasshadowstyles
|
||||
template<typename IncludingClass>
|
||||
class CanvasShadowStyles {
|
||||
public:
|
||||
~CanvasShadowStyles() = default;
|
||||
|
||||
virtual float shadow_offset_x() const = 0;
|
||||
virtual void set_shadow_offset_x(float offsetX) = 0;
|
||||
|
||||
virtual float shadow_offset_y() const = 0;
|
||||
virtual void set_shadow_offset_y(float offsetY) = 0;
|
||||
|
||||
virtual String shadow_color() const = 0;
|
||||
virtual void set_shadow_color(String color) = 0;
|
||||
|
||||
protected:
|
||||
CanvasShadowStyles() = default;
|
||||
|
||||
private:
|
||||
CanvasState::DrawingState& my_drawing_state() { return reinterpret_cast<IncludingClass&>(*this).drawing_state(); }
|
||||
CanvasState::DrawingState const& my_drawing_state() const { return reinterpret_cast<IncludingClass const&>(*this).drawing_state(); }
|
||||
};
|
||||
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
// https://html.spec.whatwg.org/multipage/canvas.html#canvasshadowstyles
|
||||
interface mixin CanvasShadowStyles {
|
||||
// shadows
|
||||
[FIXME] attribute unrestricted double shadowOffsetX; // (default 0)
|
||||
[FIXME] attribute unrestricted double shadowOffsetY; // (default 0)
|
||||
attribute unrestricted double shadowOffsetX; // (default 0)
|
||||
attribute unrestricted double shadowOffsetY; // (default 0)
|
||||
[FIXME] attribute unrestricted double shadowBlur; // (default 0)
|
||||
[FIXME] attribute DOMString shadowColor; // (default transparent black)
|
||||
attribute DOMString shadowColor; // (default transparent black)
|
||||
};
|
||||
|
|
|
@ -80,6 +80,9 @@ public:
|
|||
Gfx::AffineTransform transform;
|
||||
FillOrStrokeStyle fill_style { Gfx::Color::Black };
|
||||
FillOrStrokeStyle stroke_style { Gfx::Color::Black };
|
||||
float shadow_offset_x { 0.0f };
|
||||
float shadow_offset_y { 0.0f };
|
||||
Gfx::Color shadow_color { Gfx::Color::Transparent };
|
||||
float line_width { 1 };
|
||||
Vector<double> dash_list;
|
||||
bool image_smoothing_enabled { true };
|
||||
|
|
|
@ -262,6 +262,8 @@ void CanvasRenderingContext2D::stroke_internal(Gfx::Path const& path)
|
|||
if (!painter)
|
||||
return;
|
||||
|
||||
paint_shadow_for_stroke_internal(path);
|
||||
|
||||
auto& state = drawing_state();
|
||||
|
||||
if (auto color = state.stroke_style.as_color(); color.has_value()) {
|
||||
|
@ -299,6 +301,8 @@ void CanvasRenderingContext2D::fill_internal(Gfx::Path const& path, Gfx::Winding
|
|||
if (!painter)
|
||||
return;
|
||||
|
||||
paint_shadow_for_fill_internal(path, winding_rule);
|
||||
|
||||
auto path_to_fill = path;
|
||||
path_to_fill.close_all_subpaths();
|
||||
auto& state = this->drawing_state();
|
||||
|
@ -719,4 +723,95 @@ void CanvasRenderingContext2D::set_global_alpha(float alpha)
|
|||
drawing_state().global_alpha = alpha;
|
||||
}
|
||||
|
||||
float CanvasRenderingContext2D::shadow_offset_x() const
|
||||
{
|
||||
return drawing_state().shadow_offset_x;
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2D::set_shadow_offset_x(float offsetX)
|
||||
{
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-shadowoffsetx
|
||||
drawing_state().shadow_offset_x = offsetX;
|
||||
}
|
||||
|
||||
float CanvasRenderingContext2D::shadow_offset_y() const
|
||||
{
|
||||
return drawing_state().shadow_offset_y;
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2D::set_shadow_offset_y(float offsetY)
|
||||
{
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-shadowoffsety
|
||||
drawing_state().shadow_offset_y = offsetY;
|
||||
}
|
||||
|
||||
String CanvasRenderingContext2D::shadow_color() const
|
||||
{
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-shadowcolor
|
||||
return drawing_state().shadow_color.to_string(Gfx::Color::HTMLCompatibleSerialization::Yes);
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2D::set_shadow_color(String color)
|
||||
{
|
||||
auto& realm = static_cast<CanvasRenderingContext2D&>(*this).realm();
|
||||
|
||||
// 1. Let context be this's canvas attribute's value, if that is an element; otherwise null.
|
||||
auto parser = CSS::Parser::Parser::create(CSS::Parser::ParsingContext(realm), color);
|
||||
|
||||
auto style_value = parser.parse_as_css_value(CSS::PropertyID::Color);
|
||||
|
||||
// 2. Let parsedValue be the result of parsing the given value with context if non-null.
|
||||
if (style_value && style_value->has_color()) {
|
||||
auto parsedValue = style_value->to_color(OptionalNone());
|
||||
|
||||
// 4. Set this's shadow color to parsedValue.
|
||||
drawing_state().shadow_color = parsedValue;
|
||||
} else {
|
||||
// 3. If parsedValue is failure, then return.
|
||||
return;
|
||||
}
|
||||
}
|
||||
void CanvasRenderingContext2D::paint_shadow_for_fill_internal(Gfx::Path const& path, Gfx::WindingRule winding_rule)
|
||||
{
|
||||
auto* painter = this->painter();
|
||||
if (!painter)
|
||||
return;
|
||||
|
||||
auto path_to_fill = path;
|
||||
path_to_fill.close_all_subpaths();
|
||||
|
||||
auto& state = this->drawing_state();
|
||||
|
||||
painter->save();
|
||||
|
||||
Gfx::AffineTransform transform;
|
||||
transform.translate(state.shadow_offset_x, state.shadow_offset_y);
|
||||
painter->set_transform(transform);
|
||||
painter->fill_path(path_to_fill, state.shadow_color.with_opacity(state.global_alpha), winding_rule);
|
||||
|
||||
painter->restore();
|
||||
|
||||
did_draw(path_to_fill.bounding_box());
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2D::paint_shadow_for_stroke_internal(Gfx::Path const& path)
|
||||
{
|
||||
auto* painter = this->painter();
|
||||
if (!painter)
|
||||
return;
|
||||
|
||||
auto& state = drawing_state();
|
||||
|
||||
painter->save();
|
||||
|
||||
Gfx::AffineTransform transform;
|
||||
transform.translate(state.shadow_offset_x, state.shadow_offset_y);
|
||||
painter->set_transform(transform);
|
||||
painter->stroke_path(path, state.shadow_color.with_opacity(state.global_alpha), state.line_width);
|
||||
|
||||
painter->restore();
|
||||
|
||||
did_draw(path.bounding_box());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <LibWeb/HTML/Canvas/CanvasPath.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasPathDrawingStyles.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasRect.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasShadowStyles.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasState.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasText.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasTextDrawingStyles.h>
|
||||
|
@ -41,6 +42,7 @@ class CanvasRenderingContext2D
|
|||
, public CanvasState
|
||||
, public CanvasTransform<CanvasRenderingContext2D>
|
||||
, public CanvasFillStrokeStyles<CanvasRenderingContext2D>
|
||||
, public CanvasShadowStyles<CanvasRenderingContext2D>
|
||||
, public CanvasRect
|
||||
, public CanvasDrawPath
|
||||
, public CanvasText
|
||||
|
@ -99,6 +101,13 @@ public:
|
|||
virtual float global_alpha() const override;
|
||||
virtual void set_global_alpha(float) override;
|
||||
|
||||
virtual float shadow_offset_x() const override;
|
||||
virtual void set_shadow_offset_x(float) override;
|
||||
virtual float shadow_offset_y() const override;
|
||||
virtual void set_shadow_offset_y(float) override;
|
||||
virtual String shadow_color() const override;
|
||||
virtual void set_shadow_color(String) override;
|
||||
|
||||
HTMLCanvasElement& canvas_element();
|
||||
HTMLCanvasElement const& canvas_element() const;
|
||||
|
||||
|
@ -131,6 +140,8 @@ private:
|
|||
void stroke_internal(Gfx::Path const&);
|
||||
void fill_internal(Gfx::Path const&, Gfx::WindingRule);
|
||||
void clip_internal(Gfx::Path&, Gfx::WindingRule);
|
||||
void paint_shadow_for_fill_internal(Gfx::Path const&, Gfx::WindingRule);
|
||||
void paint_shadow_for_stroke_internal(Gfx::Path const&);
|
||||
|
||||
JS::NonnullGCPtr<HTMLCanvasElement> m_element;
|
||||
OwnPtr<Gfx::Painter> m_painter;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue