diff --git a/gpu/internal/rendertest/clip_test.go b/gpu/internal/rendertest/clip_test.go index 22703617..0820639f 100644 --- a/gpu/internal/rendertest/clip_test.go +++ b/gpu/internal/rendertest/clip_test.go @@ -185,6 +185,28 @@ func TestStrokedPathZeroWidth(t *testing.T) { }) } +func TestStrokedPathCoincidentControlPoint(t *testing.T) { + run(t, func(o *op.Ops) { + p := new(clip.Path) + p.Begin(o) + p.MoveTo(f32.Pt(70, 20)) + p.CubeTo(f32.Pt(70, 20), f32.Pt(70, 110), f32.Pt(120, 120)) + p.LineTo(f32.Pt(20, 120)) + p.LineTo(f32.Pt(70, 20)) + cl := clip.Stroke{ + Path: p.End(), + Width: 20, + }.Op().Push(o) + + paint.Fill(o, black) + cl.Pop() + }, func(r result) { + r.expect(0, 0, transparent) + r.expect(70, 20, colornames.Black) + r.expect(70, 90, transparent) + }) +} + func TestPathReuse(t *testing.T) { run(t, func(o *op.Ops) { var path clip.Path diff --git a/gpu/internal/rendertest/refs/TestStrokedPathCoincidentControlPoint.png b/gpu/internal/rendertest/refs/TestStrokedPathCoincidentControlPoint.png new file mode 100644 index 00000000..77540a4b Binary files /dev/null and b/gpu/internal/rendertest/refs/TestStrokedPathCoincidentControlPoint.png differ diff --git a/gpu/internal/rendertest/util_test.go b/gpu/internal/rendertest/util_test.go index a19f3026..3725301e 100644 --- a/gpu/internal/rendertest/util_test.go +++ b/gpu/internal/rendertest/util_test.go @@ -195,7 +195,7 @@ func verifyRef(t *testing.T, img *image.RGBA, frame int) (ok bool) { for y := bnd.Min.Y; y < bnd.Max.Y; y++ { exp := ref.RGBAAt(x, y) got := img.RGBAAt(x, y) - if !colorsClose(exp, got) { + if !colorsClose(exp, got) || !alphaClose(exp, got) { t.Error("not equal to ref at", x, y, " ", got, exp) return false } @@ -209,6 +209,11 @@ func colorsClose(c1, c2 color.RGBA) bool { return yiqEqApprox(c1, c2, delta) } +func alphaClose(c1, c2 color.RGBA) bool { + d := int8(c1.A - c2.A) + return d > -5 && d < 5 +} + // yiqEqApprox compares the colors of 2 pixels, in the NTSC YIQ color space, // as described in: //