Parcourir la source

LibWeb+LibGfx: Implement shadowBlur for Canvas2D

Lucien Fiorini il y a 8 mois
Parent
commit
e8cc0dc998

+ 2 - 0
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;

+ 44 - 16
Libraries/LibGfx/PainterSkia.cpp

@@ -15,6 +15,7 @@
 #include <AK/TypeCasts.h>
 #include <core/SkCanvas.h>
 #include <core/SkPath.h>
+#include <effects/SkBlurMaskFilter.h>
 #include <effects/SkGradientShader.h>
 
 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);

+ 2 - 0
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;

+ 3 - 0
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;
 

+ 1 - 1
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)
 };

+ 1 - 0
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 };

+ 18 - 2
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();
 

+ 2 - 0
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;