diff --git a/Libraries/LibGfx/Painter.h b/Libraries/LibGfx/Painter.h index 161a5e4ff49..0be56d07bd6 100644 --- a/Libraries/LibGfx/Painter.h +++ b/Libraries/LibGfx/Painter.h @@ -26,9 +26,11 @@ public: virtual void draw_bitmap(Gfx::FloatRect const& dst_rect, Gfx::ImmutableBitmap const& src_bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode, float global_alpha) = 0; virtual void stroke_path(Gfx::Path const&, Gfx::Color, float thickness) = 0; + virtual void stroke_path(Gfx::Path const&, Gfx::Color, float thickness, float blur_radius) = 0; virtual void stroke_path(Gfx::Path const&, Gfx::PaintStyle const&, float thickness, float global_alpha) = 0; virtual void fill_path(Gfx::Path const&, Gfx::Color, Gfx::WindingRule) = 0; + virtual void fill_path(Gfx::Path const&, Gfx::Color, Gfx::WindingRule, float blur_radius) = 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; diff --git a/Libraries/LibGfx/PainterSkia.cpp b/Libraries/LibGfx/PainterSkia.cpp index 04807c4ac0d..eaaca1c8ea5 100644 --- a/Libraries/LibGfx/PainterSkia.cpp +++ b/Libraries/LibGfx/PainterSkia.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include namespace Gfx { @@ -120,21 +121,6 @@ void PainterSkia::set_transform(Gfx::AffineTransform const& transform) impl().canvas()->setMatrix(matrix); } -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) - return; - - SkPaint paint; - paint.setAntiAlias(true); - paint.setStyle(SkPaint::kStroke_Style); - paint.setStrokeWidth(thickness); - paint.setColor(to_skia_color(color)); - auto sk_path = to_skia_path(path); - impl().canvas()->drawPath(sk_path, paint); -} - static SkPoint to_skia_point(auto const& point) { return SkPoint::Make(point.x(), point.y()); @@ -198,10 +184,41 @@ static SkPaint to_skia_paint(Gfx::PaintStyle const& style, Gfx::FloatRect const& return {}; } +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 <= 0) + return; + + SkPaint paint; + paint.setAntiAlias(true); + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(thickness); + paint.setColor(to_skia_color(color)); + auto sk_path = to_skia_path(path); + impl().canvas()->drawPath(sk_path, paint); +} + +void PainterSkia::stroke_path(Gfx::Path const& path, Gfx::Color color, float thickness, float blur_radius) +{ + // Skia treats zero thickness as a special case and will draw a hairline, while we want to draw nothing. + if (thickness <= 0) + return; + + SkPaint paint; + paint.setAntiAlias(true); + paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, blur_radius / 2)); + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(thickness); + paint.setColor(to_skia_color(color)); + auto sk_path = to_skia_path(path); + impl().canvas()->drawPath(sk_path, paint); +} + 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) + if (thickness <= 0) return; auto sk_path = to_skia_path(path); @@ -223,6 +240,17 @@ void PainterSkia::fill_path(Gfx::Path const& path, Gfx::Color color, Gfx::Windin impl().canvas()->drawPath(sk_path, paint); } +void PainterSkia::fill_path(Gfx::Path const& path, Gfx::Color color, Gfx::WindingRule winding_rule, float blur_radius) +{ + SkPaint paint; + paint.setAntiAlias(true); + paint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, blur_radius / 2)); + paint.setColor(to_skia_color(color)); + auto sk_path = to_skia_path(path); + sk_path.setFillType(to_skia_path_fill_type(winding_rule)); + impl().canvas()->drawPath(sk_path, paint); +} + 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); diff --git a/Libraries/LibGfx/PainterSkia.h b/Libraries/LibGfx/PainterSkia.h index 577ead75097..d52b80619a3 100644 --- a/Libraries/LibGfx/PainterSkia.h +++ b/Libraries/LibGfx/PainterSkia.h @@ -22,8 +22,10 @@ public: virtual void fill_rect(Gfx::FloatRect const&, Color) override; virtual void draw_bitmap(Gfx::FloatRect const& dst_rect, Gfx::ImmutableBitmap const& src_bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode, float global_alpha) override; virtual void stroke_path(Gfx::Path const&, Gfx::Color, float thickness) override; + virtual void stroke_path(Gfx::Path const&, Gfx::Color, float thickness, float blur_radius) 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::Color, Gfx::WindingRule, float blur_radius) 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; diff --git a/Libraries/LibWeb/HTML/Canvas/CanvasShadowStyles.h b/Libraries/LibWeb/HTML/Canvas/CanvasShadowStyles.h index 63eefb45bda..0b5afe39934 100644 --- a/Libraries/LibWeb/HTML/Canvas/CanvasShadowStyles.h +++ b/Libraries/LibWeb/HTML/Canvas/CanvasShadowStyles.h @@ -26,6 +26,9 @@ public: virtual float shadow_offset_y() const = 0; virtual void set_shadow_offset_y(float offsetY) = 0; + virtual float shadow_blur() const = 0; + virtual void set_shadow_blur(float offsetY) = 0; + virtual String shadow_color() const = 0; virtual void set_shadow_color(String color) = 0; diff --git a/Libraries/LibWeb/HTML/Canvas/CanvasShadowStyles.idl b/Libraries/LibWeb/HTML/Canvas/CanvasShadowStyles.idl index 913a1254528..b66ab658243 100644 --- a/Libraries/LibWeb/HTML/Canvas/CanvasShadowStyles.idl +++ b/Libraries/LibWeb/HTML/Canvas/CanvasShadowStyles.idl @@ -3,6 +3,6 @@ interface mixin CanvasShadowStyles { // shadows attribute unrestricted double shadowOffsetX; // (default 0) attribute unrestricted double shadowOffsetY; // (default 0) - [FIXME] attribute unrestricted double shadowBlur; // (default 0) + attribute unrestricted double shadowBlur; // (default 0) attribute DOMString shadowColor; // (default transparent black) }; diff --git a/Libraries/LibWeb/HTML/Canvas/CanvasState.h b/Libraries/LibWeb/HTML/Canvas/CanvasState.h index 3a5ec3e79cc..df3d05cc20a 100644 --- a/Libraries/LibWeb/HTML/Canvas/CanvasState.h +++ b/Libraries/LibWeb/HTML/Canvas/CanvasState.h @@ -82,6 +82,7 @@ public: FillOrStrokeStyle stroke_style { Gfx::Color::Black }; float shadow_offset_x { 0.0f }; float shadow_offset_y { 0.0f }; + float shadow_blur { 0.0f }; Gfx::Color shadow_color { Gfx::Color::Transparent }; float line_width { 1 }; Bindings::CanvasLineCap line_cap { Bindings::CanvasLineCap::Butt }; diff --git a/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp b/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp index 2650f675bf1..a11caacb3c7 100644 --- a/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp +++ b/Libraries/LibWeb/HTML/CanvasRenderingContext2D.cpp @@ -782,6 +782,22 @@ void CanvasRenderingContext2D::set_shadow_offset_y(float offsetY) drawing_state().shadow_offset_y = offsetY; } +float CanvasRenderingContext2D::shadow_blur() const +{ + return drawing_state().shadow_blur; +} + +// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-shadowblur +void CanvasRenderingContext2D::set_shadow_blur(float blur_radius) +{ + // On setting, the attribute must be set to the new value, + // except if the value is negative, infinite or NaN, in which case the new value must be ignored. + if (blur_radius < 0 || isinf(blur_radius) || isnan(blur_radius)) + return; + + drawing_state().shadow_blur = blur_radius; +} + String CanvasRenderingContext2D::shadow_color() const { // https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-shadowcolor @@ -824,7 +840,7 @@ void CanvasRenderingContext2D::paint_shadow_for_fill_internal(Gfx::Path const& p 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->fill_path(path_to_fill, state.shadow_color.with_opacity(state.global_alpha), winding_rule, state.shadow_blur); painter->restore(); @@ -844,7 +860,7 @@ void CanvasRenderingContext2D::paint_shadow_for_stroke_internal(Gfx::Path const& 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->stroke_path(path, state.shadow_color.with_opacity(state.global_alpha), state.line_width, state.shadow_blur); painter->restore(); diff --git a/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h b/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h index 858e3cc34cc..a69fce27b8b 100644 --- a/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h +++ b/Libraries/LibWeb/HTML/CanvasRenderingContext2D.h @@ -105,6 +105,8 @@ public: virtual void set_shadow_offset_x(float) override; virtual float shadow_offset_y() const override; virtual void set_shadow_offset_y(float) override; + virtual float shadow_blur() const override; + virtual void set_shadow_blur(float) override; virtual String shadow_color() const override; virtual void set_shadow_color(String) override;