ebiten: add FillRuleFillAll, FillRuleEvenOdd, and FillRuleNonZero

This change also deprecates the existing constants.

Closes #3006
This commit is contained in:
Hajime Hoshi 2024-06-08 12:06:00 +09:00
parent d37301eeeb
commit b121468991
6 changed files with 51 additions and 32 deletions

View File

@ -72,11 +72,11 @@ type DrawTrianglesOptions struct {
// FillRule indicates the rule how an overlapped region is rendered. // FillRule indicates the rule how an overlapped region is rendered.
// //
// The rules NonZero and EvenOdd are useful when you want to render a complex polygon. // The rules FileRuleNonZero and FillRuleEvenOdd are useful when you want to render a complex polygon.
// A complex polygon is a non-convex polygon like a concave polygon, a polygon with holes, or a self-intersecting polygon. // A complex polygon is a non-convex polygon like a concave polygon, a polygon with holes, or a self-intersecting polygon.
// See examples/vector for actual usages. // See examples/vector for actual usages.
// //
// The default (zero) value is ebiten.FillAll. // The default (zero) value is ebiten.FillRuleFillAll.
FillRule ebiten.FillRule FillRule ebiten.FillRule
// AntiAlias indicates whether the rendering uses anti-alias or not. // AntiAlias indicates whether the rendering uses anti-alias or not.

View File

@ -141,14 +141,14 @@ func drawEbitenText(screen *ebiten.Image, x, y int, aa bool, line bool) {
op := &ebiten.DrawTrianglesOptions{} op := &ebiten.DrawTrianglesOptions{}
op.AntiAlias = aa op.AntiAlias = aa
// For strokes (AppendVerticesAndIndicesForStroke), FillAll and NonZero work. // For strokes (AppendVerticesAndIndicesForStroke), FillRuleFillAll and FillRuleNonZero work.
// //
// For filling (AppendVerticesAndIndicesForFilling), NonZero and EvenOdd work. // For filling (AppendVerticesAndIndicesForFilling), FillRuleNonZero and FillRuleEvenOdd work.
// NonZero and EvenOdd differ when rendering a complex polygons with self-intersections and/or holes. // FillRuleNonZero and FillRuleEvenOdd differ when rendering a complex polygons with self-intersections and/or holes.
// See https://en.wikipedia.org/wiki/Nonzero-rule and https://en.wikipedia.org/wiki/Even%E2%80%93odd_rule . // See https://en.wikipedia.org/wiki/Nonzero-rule and https://en.wikipedia.org/wiki/Even%E2%80%93odd_rule .
// //
// For simplicity, this example always uses NonZero, whichever strokes or filling is done. // For simplicity, this example always uses FillRuleNonZero, whichever strokes or filling is done.
op.FillRule = ebiten.NonZero op.FillRule = ebiten.FillRuleNonZero
screen.DrawTriangles(vs, is, whiteSubImage, op) screen.DrawTriangles(vs, is, whiteSubImage, op)
} }
@ -203,7 +203,7 @@ func drawEbitenLogo(screen *ebiten.Image, x, y int, aa bool, line bool) {
op := &ebiten.DrawTrianglesOptions{} op := &ebiten.DrawTrianglesOptions{}
op.AntiAlias = aa op.AntiAlias = aa
op.FillRule = ebiten.NonZero op.FillRule = ebiten.FillRuleNonZero
screen.DrawTriangles(vs, is, whiteSubImage, op) screen.DrawTriangles(vs, is, whiteSubImage, op)
} }
@ -245,7 +245,7 @@ func drawArc(screen *ebiten.Image, count int, aa bool, line bool) {
op := &ebiten.DrawTrianglesOptions{} op := &ebiten.DrawTrianglesOptions{}
op.AntiAlias = aa op.AntiAlias = aa
op.FillRule = ebiten.NonZero op.FillRule = ebiten.FillRuleNonZero
screen.DrawTriangles(vs, is, whiteSubImage, op) screen.DrawTriangles(vs, is, whiteSubImage, op)
} }
@ -300,7 +300,7 @@ func drawWave(screen *ebiten.Image, counter int, aa bool, line bool) {
op := &ebiten.DrawTrianglesOptions{} op := &ebiten.DrawTrianglesOptions{}
op.AntiAlias = aa op.AntiAlias = aa
op.FillRule = ebiten.NonZero op.FillRule = ebiten.FillRuleNonZero
screen.DrawTriangles(vs, is, whiteSubImage, op) screen.DrawTriangles(vs, is, whiteSubImage, op)
} }

View File

@ -310,17 +310,36 @@ const (
// FillRule is the rule whether an overlapped region is rendered with DrawTriangles(Shader). // FillRule is the rule whether an overlapped region is rendered with DrawTriangles(Shader).
type FillRule int type FillRule int
const (
// FillRuleFillAll indicates all the triangles are rendered regardless of overlaps.
FillRuleFillAll FillRule = FillRule(graphicsdriver.FillRuleFillAll)
// FillRuleNonZero means that triangles are rendered based on the non-zero rule.
// If and only if the number of overlaps is not 0, the region is rendered.
FillRuleNonZero FillRule = FillRule(graphicsdriver.FillRuleNonZero)
// FillRuleEvenOdd means that triangles are rendered based on the even-odd rule.
// If and only if the number of overlaps is odd, the region is rendered.
FillRuleEvenOdd FillRule = FillRule(graphicsdriver.FillRuleEvenOdd)
)
const ( const (
// FillAll indicates all the triangles are rendered regardless of overlaps. // FillAll indicates all the triangles are rendered regardless of overlaps.
FillAll FillRule = FillRule(graphicsdriver.FillRuleFillAll) //
// Deprecated: as of v2.8. Use FillRuleFillAll instead.
FillAll = FillRuleFillAll
// NonZero means that triangles are rendered based on the non-zero rule. // NonZero means that triangles are rendered based on the non-zero rule.
// If and only if the number of overlaps is not 0, the region is rendered. // If and only if the number of overlaps is not 0, the region is rendered.
NonZero FillRule = FillRule(graphicsdriver.FillRuleNonZero) //
// Deprecated: as of v2.8. Use FillRuleNonZero instead.
NonZero = FillRuleNonZero
// EvenOdd means that triangles are rendered based on the even-odd rule. // EvenOdd means that triangles are rendered based on the even-odd rule.
// If and only if the number of overlaps is odd, the region is rendered. // If and only if the number of overlaps is odd, the region is rendered.
EvenOdd FillRule = FillRule(graphicsdriver.FillRuleEvenOdd) //
// Deprecated: as of v2.8. Use FillRuleEvenOdd instead.
EvenOdd = FillRuleEvenOdd
) )
// ColorScaleMode is the mode of color scales in vertices. // ColorScaleMode is the mode of color scales in vertices.
@ -371,11 +390,11 @@ type DrawTrianglesOptions struct {
// FillRule indicates the rule how an overlapped region is rendered. // FillRule indicates the rule how an overlapped region is rendered.
// //
// The rules NonZero and EvenOdd are useful when you want to render a complex polygon. // The rules FillRuleNonZero and FillRuleEvenOdd are useful when you want to render a complex polygon.
// A complex polygon is a non-convex polygon like a concave polygon, a polygon with holes, or a self-intersecting polygon. // A complex polygon is a non-convex polygon like a concave polygon, a polygon with holes, or a self-intersecting polygon.
// See examples/vector for actual usages. // See examples/vector for actual usages.
// //
// The default (zero) value is FillAll. // The default (zero) value is FillRuleFillAll.
FillRule FillRule FillRule FillRule
// AntiAlias indicates whether the rendering uses anti-alias or not. // AntiAlias indicates whether the rendering uses anti-alias or not.
@ -547,11 +566,11 @@ type DrawTrianglesShaderOptions struct {
// FillRule indicates the rule how an overlapped region is rendered. // FillRule indicates the rule how an overlapped region is rendered.
// //
// The rules NonZero and EvenOdd are useful when you want to render a complex polygon. // The rules FillRuleNonZero and FillRuleEvenOdd are useful when you want to render a complex polygon.
// A complex polygon is a non-convex polygon like a concave polygon, a polygon with holes, or a self-intersecting polygon. // A complex polygon is a non-convex polygon like a concave polygon, a polygon with holes, or a self-intersecting polygon.
// See examples/vector for actual usages. // See examples/vector for actual usages.
// //
// The default (zero) value is FillAll. // The default (zero) value is FillRuleFillAll.
FillRule FillRule FillRule FillRule
// AntiAlias indicates whether the rendering uses anti-alias or not. // AntiAlias indicates whether the rendering uses anti-alias or not.

View File

@ -2696,7 +2696,7 @@ func TestImageEvenOdd(t *testing.T) {
// Draw all the vertices once. The even-odd rule is applied for all the vertices once. // Draw all the vertices once. The even-odd rule is applied for all the vertices once.
dst := ebiten.NewImage(16, 16) dst := ebiten.NewImage(16, 16)
op := &ebiten.DrawTrianglesOptions{ op := &ebiten.DrawTrianglesOptions{
FillRule: ebiten.EvenOdd, FillRule: ebiten.FillRuleEvenOdd,
} }
dst.DrawTriangles(append(append(vs0, vs1...), vs2...), append(append(is0, is1...), is2...), emptySubImage, op) dst.DrawTriangles(append(append(vs0, vs1...), vs2...), append(append(is0, is1...), is2...), emptySubImage, op)
for j := 0; j < 16; j++ { for j := 0; j < 16; j++ {
@ -2794,15 +2794,15 @@ func TestImageEvenOdd(t *testing.T) {
} }
func TestImageFillRule(t *testing.T) { func TestImageFillRule(t *testing.T) {
for _, fillRule := range []ebiten.FillRule{ebiten.FillAll, ebiten.NonZero, ebiten.EvenOdd} { for _, fillRule := range []ebiten.FillRule{ebiten.FillRuleFillAll, ebiten.FillRuleNonZero, ebiten.FillRuleEvenOdd} {
fillRule := fillRule fillRule := fillRule
var name string var name string
switch fillRule { switch fillRule {
case ebiten.FillAll: case ebiten.FillRuleFillAll:
name = "FillAll" name = "FillAll"
case ebiten.NonZero: case ebiten.FillRuleNonZero:
name = "NonZero" name = "NonZero"
case ebiten.EvenOdd: case ebiten.FillRuleEvenOdd:
name = "EvenOdd" name = "EvenOdd"
} }
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
@ -2885,11 +2885,11 @@ func TestImageFillRule(t *testing.T) {
var want color.RGBA var want color.RGBA
switch { switch {
case 2 <= i && i < 7 && 2 <= j && j < 7: case 2 <= i && i < 7 && 2 <= j && j < 7:
if fillRule != ebiten.EvenOdd { if fillRule != ebiten.FillRuleEvenOdd {
want = color.RGBA{G: 0xff, A: 0xff} want = color.RGBA{G: 0xff, A: 0xff}
} }
case 9 <= i && i < 14 && 9 <= j && j < 14: case 9 <= i && i < 14 && 9 <= j && j < 14:
if fillRule == ebiten.FillAll { if fillRule == ebiten.FillRuleFillAll {
want = color.RGBA{B: 0xff, A: 0xff} want = color.RGBA{B: 0xff, A: 0xff}
} }
case 1 <= i && i < 15 && 1 <= j && j < 15: case 1 <= i && i < 15 && 1 <= j && j < 15:
@ -2922,11 +2922,11 @@ func TestImageFillRule(t *testing.T) {
var want color.RGBA var want color.RGBA
switch { switch {
case 3 <= i && i < 8 && 3 <= j && j < 8: case 3 <= i && i < 8 && 3 <= j && j < 8:
if fillRule != ebiten.EvenOdd { if fillRule != ebiten.FillRuleEvenOdd {
want = color.RGBA{G: 0xff, A: 0xff} want = color.RGBA{G: 0xff, A: 0xff}
} }
case 10 <= i && i < 15 && 10 <= j && j < 15: case 10 <= i && i < 15 && 10 <= j && j < 15:
if fillRule == ebiten.FillAll { if fillRule == ebiten.FillRuleFillAll {
want = color.RGBA{B: 0xff, A: 0xff} want = color.RGBA{B: 0xff, A: 0xff}
} }
case 2 <= i && i < 16 && 2 <= j && j < 16: case 2 <= i && i < 16 && 2 <= j && j < 16:
@ -3726,7 +3726,7 @@ func TestImageTooManyConstantBuffersInDirectX(t *testing.T) {
dst0 := ebiten.NewImage(16, 16) dst0 := ebiten.NewImage(16, 16)
dst1 := ebiten.NewImage(16, 16) dst1 := ebiten.NewImage(16, 16)
op := &ebiten.DrawTrianglesOptions{ op := &ebiten.DrawTrianglesOptions{
FillRule: ebiten.EvenOdd, FillRule: ebiten.FillRuleEvenOdd,
} }
for i := 0; i < 100; i++ { for i := 0; i < 100; i++ {
dst0.DrawTriangles(vs, is, src, op) dst0.DrawTriangles(vs, is, src, op)

View File

@ -59,7 +59,7 @@ func (g *Game) Update() error {
} }
func (g *Game) Draw(screen *ebiten.Image) { func (g *Game) Draw(screen *ebiten.Image) {
// Before the fix, some complex renderings with EvenOdd might cause a DirectX error like this (#2138): // Before the fix, some complex renderings with FillRuleEvenOdd might cause a DirectX error like this (#2138):
// panic: directx: IDXGISwapChain4::Present failed: HRESULT(2289696773) // panic: directx: IDXGISwapChain4::Present failed: HRESULT(2289696773)
screen.DrawImage(debugCircleImage, nil) screen.DrawImage(debugCircleImage, nil)
@ -74,7 +74,7 @@ func (g *Game) Draw(screen *ebiten.Image) {
p.Arc(100, 100, 6, 0, 2*math.Pi, vector.Clockwise) p.Arc(100, 100, 6, 0, 2*math.Pi, vector.Clockwise)
filling, indicies := p.AppendVerticesAndIndicesForFilling(nil, nil) filling, indicies := p.AppendVerticesAndIndicesForFilling(nil, nil)
screen.DrawTriangles(filling, indicies, whiteTextureImage, &ebiten.DrawTrianglesOptions{ screen.DrawTriangles(filling, indicies, whiteTextureImage, &ebiten.DrawTrianglesOptions{
FillRule: ebiten.EvenOdd, FillRule: ebiten.FillRuleEvenOdd,
}) })
} }

View File

@ -396,7 +396,7 @@ func (p *Path) Close() {
// //
// The returned vertice's SrcX and SrcY are 0, and ColorR, ColorG, ColorB, and ColorA are 1. // The returned vertice's SrcX and SrcY are 0, and ColorR, ColorG, ColorB, and ColorA are 1.
// //
// The returned values are intended to be passed to DrawTriangles or DrawTrianglesShader with the fill rule NonZero or EvenOdd // The returned values are intended to be passed to DrawTriangles or DrawTrianglesShader with FileRuleNonZero or FillRuleEvenOdd
// in order to render a complex polygon like a concave polygon, a polygon with holes, or a self-intersecting polygon. // in order to render a complex polygon like a concave polygon, a polygon with holes, or a self-intersecting polygon.
// //
// The returned vertices and indices should be rendered with a solid (non-transparent) color with the default Blend (source-over). // The returned vertices and indices should be rendered with a solid (non-transparent) color with the default Blend (source-over).
@ -480,7 +480,7 @@ type StrokeOptions struct {
// The returned vertice's SrcX and SrcY are 0, and ColorR, ColorG, ColorB, and ColorA are 1. // The returned vertice's SrcX and SrcY are 0, and ColorR, ColorG, ColorB, and ColorA are 1.
// //
// The returned values are intended to be passed to DrawTriangles or DrawTrianglesShader with a solid (non-transparent) color // The returned values are intended to be passed to DrawTriangles or DrawTrianglesShader with a solid (non-transparent) color
// with FillAll or NonZero fill rule, not EvenOdd fill rule. // with FillRuleFillAll or FillRuleNonZero, not FileRuleEvenOdd.
func (p *Path) AppendVerticesAndIndicesForStroke(vertices []ebiten.Vertex, indices []uint16, op *StrokeOptions) ([]ebiten.Vertex, []uint16) { func (p *Path) AppendVerticesAndIndicesForStroke(vertices []ebiten.Vertex, indices []uint16, op *StrokeOptions) ([]ebiten.Vertex, []uint16) {
if op == nil { if op == nil {
return vertices, indices return vertices, indices
@ -536,7 +536,7 @@ func (p *Path) AppendVerticesAndIndicesForStroke(vertices []ebiten.Vertex, indic
ColorA: 1, ColorA: 1,
}) })
} }
// All the triangles are rendered in clockwise order to enable NonZero filling rule (#2833). // All the triangles are rendered in clockwise order to enable FillRuleNonZero (#2833).
indices = append(indices, idx, idx+1, idx+2, idx+1, idx+3, idx+2) indices = append(indices, idx, idx+1, idx+2, idx+1, idx+3, idx+2)
// Add line joints. // Add line joints.