mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-12-07 21:59:54 +00:00
LibWeb+LibGfx: Draw shadows for stroke joins and caps
This commit is contained in:
parent
0f295e8989
commit
7db73118e9
Notes:
github-actions[bot]
2025-10-21 16:56:30 +00:00
Author: https://github.com/tcl3
Commit: 7db73118e9
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/6530
Reviewed-by: https://github.com/gmta ✅
15 changed files with 257 additions and 8 deletions
|
|
@ -29,7 +29,7 @@ public:
|
|||
virtual void draw_bitmap(Gfx::FloatRect const& dst_rect, Gfx::ImmutableBitmap const& src_bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode, Optional<Gfx::Filter> filters, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator) = 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, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator) = 0;
|
||||
virtual void stroke_path(Gfx::Path const&, Gfx::Color, float thickness, float blur_radius, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator, Gfx::Path::CapStyle, Gfx::Path::JoinStyle, float miter_limit, Vector<float> const& dash_array, float dash_offset) = 0;
|
||||
virtual void stroke_path(Gfx::Path const&, Gfx::PaintStyle const&, Optional<Gfx::Filter>, float thickness, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator) = 0;
|
||||
virtual void stroke_path(Gfx::Path const&, Gfx::PaintStyle const&, Optional<Gfx::Filter>, float thickness, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator, Gfx::Path::CapStyle const&, Gfx::Path::JoinStyle const&, float miter_limit, Vector<float> const&, float dash_offset) = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ void PainterSkia::stroke_path(Gfx::Path const& path, Gfx::Color color, float thi
|
|||
});
|
||||
}
|
||||
|
||||
void PainterSkia::stroke_path(Gfx::Path const& path, Gfx::Color color, float thickness, float blur_radius, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator)
|
||||
void PainterSkia::stroke_path(Gfx::Path const& path, Gfx::Color color, float thickness, float blur_radius, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator, Gfx::Path::CapStyle cap_style, Gfx::Path::JoinStyle join_style, float miter_limit, Vector<float> const& dash_array, float dash_offset)
|
||||
{
|
||||
// Skia treats zero thickness as a special case and will draw a hairline, while we want to draw nothing.
|
||||
if (thickness <= 0)
|
||||
|
|
@ -200,6 +200,10 @@ void PainterSkia::stroke_path(Gfx::Path const& path, Gfx::Color color, float thi
|
|||
paint.setStyle(SkPaint::kStroke_Style);
|
||||
paint.setStrokeWidth(thickness);
|
||||
paint.setColor(to_skia_color(color));
|
||||
paint.setStrokeCap(to_skia_cap(cap_style));
|
||||
paint.setStrokeJoin(to_skia_join(join_style));
|
||||
paint.setStrokeMiter(miter_limit);
|
||||
paint.setPathEffect(SkDashPathEffect::Make(dash_array.data(), dash_array.size(), dash_offset));
|
||||
paint.setBlender(to_skia_blender(compositing_and_blending_operator));
|
||||
auto sk_path = to_skia_path(path);
|
||||
impl().with_canvas([&](auto& canvas) {
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ 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, Optional<Gfx::Filter>, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator) 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, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator) override;
|
||||
virtual void stroke_path(Gfx::Path const&, Gfx::Color, float thickness, float blur_radius, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator, Gfx::Path::CapStyle, Gfx::Path::JoinStyle, float miter_limit, Vector<float> const& dash_array, float dash_offset) override;
|
||||
virtual void stroke_path(Gfx::Path const&, Gfx::PaintStyle const&, Optional<Gfx::Filter>, float thickness, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator) override;
|
||||
virtual void stroke_path(Gfx::Path const&, Gfx::PaintStyle const&, Optional<Gfx::Filter>, float thickness, float global_alpha, Gfx::CompositingAndBlendingOperator compositing_and_blending_operator, Gfx::Path::CapStyle const&, Gfx::Path::JoinStyle const&, float miter_limit, Vector<float> const&, float dash_offset) override;
|
||||
virtual void fill_path(Gfx::Path const&, Gfx::Color, Gfx::WindingRule) override;
|
||||
|
|
|
|||
|
|
@ -373,8 +373,6 @@ void CanvasRenderingContext2D::stroke_internal(Gfx::Path const& path)
|
|||
if (!painter)
|
||||
return;
|
||||
|
||||
paint_shadow_for_stroke_internal(path);
|
||||
|
||||
auto& state = drawing_state();
|
||||
|
||||
auto line_cap = to_gfx_cap(state.line_cap);
|
||||
|
|
@ -386,6 +384,7 @@ void CanvasRenderingContext2D::stroke_internal(Gfx::Path const& path)
|
|||
for (auto const& dash : state.dash_list) {
|
||||
dash_array.append(static_cast<float>(dash));
|
||||
}
|
||||
paint_shadow_for_stroke_internal(path, line_cap, line_join, dash_array);
|
||||
painter->stroke_path(path, state.stroke_style.to_gfx_paint_style(), state.filter, state.line_width, state.global_alpha, state.current_compositing_and_blending_operator, line_cap, line_join, state.miter_limit, dash_array, state.line_dash_offset);
|
||||
|
||||
did_draw(path.bounding_box());
|
||||
|
|
@ -1093,7 +1092,7 @@ void CanvasRenderingContext2D::paint_shadow_for_fill_internal(Gfx::Path const& p
|
|||
did_draw(path.bounding_box());
|
||||
}
|
||||
|
||||
void CanvasRenderingContext2D::paint_shadow_for_stroke_internal(Gfx::Path const& path)
|
||||
void CanvasRenderingContext2D::paint_shadow_for_stroke_internal(Gfx::Path const& path, Gfx::Path::CapStyle line_cap, Gfx::Path::JoinStyle line_join, Vector<float> const& dash_array)
|
||||
{
|
||||
auto* painter = this->painter();
|
||||
if (!painter)
|
||||
|
|
@ -1123,7 +1122,7 @@ void CanvasRenderingContext2D::paint_shadow_for_stroke_internal(Gfx::Path const&
|
|||
transform.translate(state.shadow_offset_x, state.shadow_offset_y);
|
||||
transform.multiply(state.transform);
|
||||
painter->set_transform(transform);
|
||||
painter->stroke_path(path, state.shadow_color.with_opacity(alpha), state.line_width, state.shadow_blur, state.current_compositing_and_blending_operator);
|
||||
painter->stroke_path(path, state.shadow_color.with_opacity(alpha), state.line_width, state.shadow_blur, state.current_compositing_and_blending_operator, line_cap, line_join, state.miter_limit, dash_array, state.line_dash_offset);
|
||||
|
||||
painter->restore();
|
||||
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ private:
|
|||
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&);
|
||||
void paint_shadow_for_stroke_internal(Gfx::Path const&, Gfx::Path::CapStyle, Gfx::Path::JoinStyle, Vector<float> const&);
|
||||
|
||||
GC::Ref<HTMLCanvasElement> m_element;
|
||||
OwnPtr<Gfx::Painter> m_painter;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 1 tests
|
||||
|
||||
1 Pass
|
||||
Pass Shadows are not drawn for areas outside stroke caps
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 1 tests
|
||||
|
||||
1 Pass
|
||||
Pass Shadows are drawn for stroke caps
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 1 tests
|
||||
|
||||
1 Pass
|
||||
Pass Shadows are not drawn for areas outside stroke joins
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 1 tests
|
||||
|
||||
1 Pass
|
||||
Pass Shadows are drawn for stroke joins
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
Harness status: OK
|
||||
|
||||
Found 1 tests
|
||||
|
||||
1 Pass
|
||||
Pass Shadows are drawn for stroke joins respecting miter limit
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
<!DOCTYPE html>
|
||||
<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Canvas test: 2d.shadow.stroke.cap.1</title>
|
||||
<script src="../../../../resources/testharness.js"></script>
|
||||
<script src="../../../../resources/testharnessreport.js"></script>
|
||||
<script src="../../../../html/canvas/resources/canvas-tests.js"></script>
|
||||
<link rel="stylesheet" href="../../../../html/canvas/resources/canvas-tests.css">
|
||||
<body class="show_output">
|
||||
|
||||
<h1>2d.shadow.stroke.cap.1</h1>
|
||||
<p class="desc">Shadows are not drawn for areas outside stroke caps</p>
|
||||
|
||||
|
||||
<p class="output">Actual output:</p>
|
||||
<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
|
||||
<p class="output expectedtext">Expected output:<p><img src="../../../../images/green-100x50.png" class="output expected" id="expected" alt="">
|
||||
<ul id="d"></ul>
|
||||
<script>
|
||||
var t = async_test("Shadows are not drawn for areas outside stroke caps");
|
||||
_addTest(function(canvas, ctx) {
|
||||
|
||||
ctx.fillStyle = '#0f0';
|
||||
ctx.fillRect(0, 0, 100, 50);
|
||||
ctx.strokeStyle = '#f00';
|
||||
ctx.shadowColor = '#f00';
|
||||
ctx.shadowOffsetY = 50;
|
||||
ctx.beginPath();
|
||||
ctx.lineWidth = 50;
|
||||
ctx.lineCap = 'butt';
|
||||
ctx.moveTo(-50, -25);
|
||||
ctx.lineTo(0, -25);
|
||||
ctx.moveTo(100, -25);
|
||||
ctx.lineTo(150, -25);
|
||||
ctx.stroke();
|
||||
|
||||
_assertPixel(canvas, 1,25, 0,255,0,255);
|
||||
_assertPixel(canvas, 50,25, 0,255,0,255);
|
||||
_assertPixel(canvas, 98,25, 0,255,0,255);
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
<!DOCTYPE html>
|
||||
<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Canvas test: 2d.shadow.stroke.cap.2</title>
|
||||
<script src="../../../../resources/testharness.js"></script>
|
||||
<script src="../../../../resources/testharnessreport.js"></script>
|
||||
<script src="../../../../html/canvas/resources/canvas-tests.js"></script>
|
||||
<link rel="stylesheet" href="../../../../html/canvas/resources/canvas-tests.css">
|
||||
<body class="show_output">
|
||||
|
||||
<h1>2d.shadow.stroke.cap.2</h1>
|
||||
<p class="desc">Shadows are drawn for stroke caps</p>
|
||||
|
||||
|
||||
<p class="output">Actual output:</p>
|
||||
<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
|
||||
<p class="output expectedtext">Expected output:<p><img src="../../../../images/green-100x50.png" class="output expected" id="expected" alt="">
|
||||
<ul id="d"></ul>
|
||||
<script>
|
||||
var t = async_test("Shadows are drawn for stroke caps");
|
||||
_addTest(function(canvas, ctx) {
|
||||
|
||||
ctx.fillStyle = '#f00';
|
||||
ctx.fillRect(0, 0, 100, 50);
|
||||
ctx.strokeStyle = '#f00';
|
||||
ctx.shadowColor = '#0f0';
|
||||
ctx.shadowOffsetY = 50;
|
||||
ctx.beginPath();
|
||||
ctx.lineWidth = 50;
|
||||
ctx.lineCap = 'square';
|
||||
ctx.moveTo(25, -25);
|
||||
ctx.lineTo(75, -25);
|
||||
ctx.stroke();
|
||||
|
||||
_assertPixel(canvas, 1,25, 0,255,0,255);
|
||||
_assertPixel(canvas, 50,25, 0,255,0,255);
|
||||
_assertPixel(canvas, 98,25, 0,255,0,255);
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
<!DOCTYPE html>
|
||||
<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Canvas test: 2d.shadow.stroke.join.1</title>
|
||||
<script src="../../../../resources/testharness.js"></script>
|
||||
<script src="../../../../resources/testharnessreport.js"></script>
|
||||
<script src="../../../../html/canvas/resources/canvas-tests.js"></script>
|
||||
<link rel="stylesheet" href="../../../../html/canvas/resources/canvas-tests.css">
|
||||
<body class="show_output">
|
||||
|
||||
<h1>2d.shadow.stroke.join.1</h1>
|
||||
<p class="desc">Shadows are not drawn for areas outside stroke joins</p>
|
||||
|
||||
|
||||
<p class="output">Actual output:</p>
|
||||
<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
|
||||
<p class="output expectedtext">Expected output:<p><img src="../../../../images/green-100x50.png" class="output expected" id="expected" alt="">
|
||||
<ul id="d"></ul>
|
||||
<script>
|
||||
var t = async_test("Shadows are not drawn for areas outside stroke joins");
|
||||
_addTest(function(canvas, ctx) {
|
||||
|
||||
ctx.fillStyle = '#0f0';
|
||||
ctx.fillRect(0, 0, 100, 50);
|
||||
ctx.strokeStyle = '#f00';
|
||||
ctx.shadowColor = '#f00';
|
||||
ctx.shadowOffsetX = 100;
|
||||
ctx.lineWidth = 200;
|
||||
ctx.lineJoin = 'bevel';
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(-200, -50);
|
||||
ctx.lineTo(-150, -50);
|
||||
ctx.lineTo(-151, -100);
|
||||
ctx.stroke();
|
||||
|
||||
_assertPixel(canvas, 1,1, 0,255,0,255);
|
||||
_assertPixel(canvas, 48,48, 0,255,0,255);
|
||||
_assertPixel(canvas, 50,25, 0,255,0,255);
|
||||
_assertPixel(canvas, 98,48, 0,255,0,255);
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<!DOCTYPE html>
|
||||
<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Canvas test: 2d.shadow.stroke.join.2</title>
|
||||
<script src="../../../../resources/testharness.js"></script>
|
||||
<script src="../../../../resources/testharnessreport.js"></script>
|
||||
<script src="../../../../html/canvas/resources/canvas-tests.js"></script>
|
||||
<link rel="stylesheet" href="../../../../html/canvas/resources/canvas-tests.css">
|
||||
<body class="show_output">
|
||||
|
||||
<h1>2d.shadow.stroke.join.2</h1>
|
||||
<p class="desc">Shadows are drawn for stroke joins</p>
|
||||
|
||||
|
||||
<p class="output">Actual output:</p>
|
||||
<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
|
||||
<p class="output expectedtext">Expected output:<p><img src="../../../../images/green-100x50.png" class="output expected" id="expected" alt="">
|
||||
<ul id="d"></ul>
|
||||
<script>
|
||||
var t = async_test("Shadows are drawn for stroke joins");
|
||||
_addTest(function(canvas, ctx) {
|
||||
|
||||
ctx.fillStyle = '#f00';
|
||||
ctx.fillRect(0, 0, 50, 50);
|
||||
ctx.fillStyle = '#0f0';
|
||||
ctx.fillRect(50, 0, 50, 50);
|
||||
ctx.strokeStyle = '#f00';
|
||||
ctx.shadowColor = '#0f0';
|
||||
ctx.shadowOffsetX = 100;
|
||||
ctx.lineWidth = 200;
|
||||
ctx.lineJoin = 'miter';
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(-200, -50);
|
||||
ctx.lineTo(-150, -50);
|
||||
ctx.lineTo(-151, -100);
|
||||
ctx.stroke();
|
||||
|
||||
_assertPixel(canvas, 1,1, 0,255,0,255);
|
||||
_assertPixel(canvas, 48,48, 0,255,0,255);
|
||||
_assertPixel(canvas, 50,25, 0,255,0,255);
|
||||
_assertPixel(canvas, 98,48, 0,255,0,255);
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
<!DOCTYPE html>
|
||||
<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. -->
|
||||
<meta charset="UTF-8">
|
||||
<title>Canvas test: 2d.shadow.stroke.join.3</title>
|
||||
<script src="../../../../resources/testharness.js"></script>
|
||||
<script src="../../../../resources/testharnessreport.js"></script>
|
||||
<script src="../../../../html/canvas/resources/canvas-tests.js"></script>
|
||||
<link rel="stylesheet" href="../../../../html/canvas/resources/canvas-tests.css">
|
||||
<body class="show_output">
|
||||
|
||||
<h1>2d.shadow.stroke.join.3</h1>
|
||||
<p class="desc">Shadows are drawn for stroke joins respecting miter limit</p>
|
||||
|
||||
|
||||
<p class="output">Actual output:</p>
|
||||
<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
|
||||
<p class="output expectedtext">Expected output:<p><img src="../../../../images/green-100x50.png" class="output expected" id="expected" alt="">
|
||||
<ul id="d"></ul>
|
||||
<script>
|
||||
var t = async_test("Shadows are drawn for stroke joins respecting miter limit");
|
||||
_addTest(function(canvas, ctx) {
|
||||
|
||||
ctx.fillStyle = '#0f0';
|
||||
ctx.fillRect(0, 0, 100, 50);
|
||||
ctx.strokeStyle = '#f00';
|
||||
ctx.shadowColor = '#f00';
|
||||
ctx.shadowOffsetX = 100;
|
||||
ctx.lineWidth = 200;
|
||||
ctx.lineJoin = 'miter';
|
||||
ctx.miterLimit = 0.1;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(-200, -50);
|
||||
ctx.lineTo(-150, -50);
|
||||
ctx.lineTo(-151, -100); // (not an exact right angle, to avoid some other bug in Firefox 3)
|
||||
ctx.stroke();
|
||||
|
||||
_assertPixel(canvas, 1,1, 0,255,0,255);
|
||||
_assertPixel(canvas, 48,48, 0,255,0,255);
|
||||
_assertPixel(canvas, 50,25, 0,255,0,255);
|
||||
_assertPixel(canvas, 98,48, 0,255,0,255);
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue