diff --git a/gpu/internal/rendertest/clip_test.go b/gpu/internal/rendertest/clip_test.go index 3a147312..6a5ad9ae 100644 --- a/gpu/internal/rendertest/clip_test.go +++ b/gpu/internal/rendertest/clip_test.go @@ -277,3 +277,17 @@ func TestPathInterleave(t *testing.T) { paint.ColorOp{}.Add(ops) }) } + +func TestStrokedRect(t *testing.T) { + run(t, func(o *op.Ops) { + r := clip.Rect{Min: image.Pt(50, 50), Max: image.Pt(100, 100)} + paint.FillShape(o, + color.NRGBA{R: 0xff, A: 0xFF}, + clip.Stroke{ + Path: r.Path(), + Width: 5, + }.Op(), + ) + }, func(r result) { + }) +} diff --git a/gpu/internal/rendertest/refs/TestStrokedRect.png b/gpu/internal/rendertest/refs/TestStrokedRect.png new file mode 100644 index 00000000..77b74a46 Binary files /dev/null and b/gpu/internal/rendertest/refs/TestStrokedRect.png differ diff --git a/op/clip/clip.go b/op/clip/clip.go index c94e1a0b..a8510794 100644 --- a/op/clip/clip.go +++ b/op/clip/clip.go @@ -48,6 +48,20 @@ func (p Op) Push(o *op.Ops) Stack { func (p Op) add(o *op.Ops) { path := p.path + if !path.hasSegments && p.width > 0 { + if p.path.shape != ops.Rect { + panic("only rects have empty paths") + } + b := frect(path.bounds) + var rect Path + rect.Begin(o) + rect.MoveTo(b.Min) + rect.LineTo(f32.Pt(b.Max.X, b.Min.Y)) + rect.LineTo(b.Max) + rect.LineTo(f32.Pt(b.Min.X, b.Max.Y)) + rect.Close() + path = rect.End() + } bo := binary.LittleEndian if path.hasSegments { data := ops.Write(&o.Internal, ops.TypePathLen) @@ -345,3 +359,17 @@ func (o Outline) Op() Op { outline: true, } } + +// frect converts a rectangle to a f32.Rectangle. +func frect(r image.Rectangle) f32.Rectangle { + return f32.Rectangle{ + Min: fpt(r.Min), Max: fpt(r.Max), + } +} + +// fpt converts an point to a f32.Point. +func fpt(p image.Point) f32.Point { + return f32.Point{ + X: float32(p.X), Y: float32(p.Y), + } +}