mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-11-10 04:57:26 +01:00
parent
358106bdc0
commit
3ca6184294
@ -72,7 +72,7 @@ type DrawTrianglesOptions struct {
|
||||
|
||||
// FillRule indicates the rule how an overlapped region is rendered.
|
||||
//
|
||||
// The rule EvenOdd is useful when you want to render a complex polygon.
|
||||
// The rules NonZero and EvenOdd 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.
|
||||
// See examples/vector for actual usages.
|
||||
//
|
||||
|
@ -141,7 +141,10 @@ func drawEbitenText(screen *ebiten.Image, x, y int, aa bool, line bool) {
|
||||
op := &ebiten.DrawTrianglesOptions{}
|
||||
op.AntiAlias = aa
|
||||
if !line {
|
||||
op.FillRule = ebiten.EvenOdd
|
||||
// ebiten.EvenOdd is also fine here.
|
||||
// NonZero and EvenOdd 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 .
|
||||
op.FillRule = ebiten.NonZero
|
||||
}
|
||||
screen.DrawTriangles(vs, is, whiteSubImage, op)
|
||||
}
|
||||
@ -197,7 +200,7 @@ func drawEbitenLogo(screen *ebiten.Image, x, y int, aa bool, line bool) {
|
||||
op := &ebiten.DrawTrianglesOptions{}
|
||||
op.AntiAlias = aa
|
||||
if !line {
|
||||
op.FillRule = ebiten.EvenOdd
|
||||
op.FillRule = ebiten.NonZero
|
||||
}
|
||||
screen.DrawTriangles(vs, is, whiteSubImage, op)
|
||||
}
|
||||
@ -241,7 +244,7 @@ func drawArc(screen *ebiten.Image, count int, aa bool, line bool) {
|
||||
op := &ebiten.DrawTrianglesOptions{}
|
||||
op.AntiAlias = aa
|
||||
if !line {
|
||||
op.FillRule = ebiten.EvenOdd
|
||||
op.FillRule = ebiten.NonZero
|
||||
}
|
||||
screen.DrawTriangles(vs, is, whiteSubImage, op)
|
||||
}
|
||||
@ -298,7 +301,7 @@ func drawWave(screen *ebiten.Image, counter int, aa bool, line bool) {
|
||||
op := &ebiten.DrawTrianglesOptions{}
|
||||
op.AntiAlias = aa
|
||||
if !line {
|
||||
op.FillRule = ebiten.EvenOdd
|
||||
op.FillRule = ebiten.NonZero
|
||||
}
|
||||
screen.DrawTriangles(vs, is, whiteSubImage, op)
|
||||
}
|
||||
|
8
image.go
8
image.go
@ -314,6 +314,10 @@ const (
|
||||
// FillAll indicates all the triangles are rendered regardless of overlaps.
|
||||
FillAll FillRule = FillRule(graphicsdriver.FillAll)
|
||||
|
||||
// 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.
|
||||
NonZero FillRule = FillRule(graphicsdriver.NonZero)
|
||||
|
||||
// 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.
|
||||
EvenOdd FillRule = FillRule(graphicsdriver.EvenOdd)
|
||||
@ -367,7 +371,7 @@ type DrawTrianglesOptions struct {
|
||||
|
||||
// FillRule indicates the rule how an overlapped region is rendered.
|
||||
//
|
||||
// The rule EvenOdd is useful when you want to render a complex polygon.
|
||||
// The rules NonZero and EvenOdd 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.
|
||||
// See examples/vector for actual usages.
|
||||
//
|
||||
@ -543,7 +547,7 @@ type DrawTrianglesShaderOptions struct {
|
||||
|
||||
// FillRule indicates the rule how an overlapped region is rendered.
|
||||
//
|
||||
// The rule EvenOdd is useful when you want to render a complex polygon.
|
||||
// The rules NonZero and EvenOdd 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.
|
||||
// See examples/vector for actual usages.
|
||||
//
|
||||
|
183
image_test.go
183
image_test.go
@ -2793,6 +2793,189 @@ func TestImageEvenOdd(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageFillRule(t *testing.T) {
|
||||
for _, fillRule := range []ebiten.FillRule{ebiten.FillAll, ebiten.NonZero, ebiten.EvenOdd} {
|
||||
fillRule := fillRule
|
||||
var name string
|
||||
switch fillRule {
|
||||
case ebiten.FillAll:
|
||||
name = "FillAll"
|
||||
case ebiten.NonZero:
|
||||
name = "NonZero"
|
||||
case ebiten.EvenOdd:
|
||||
name = "EvenOdd"
|
||||
}
|
||||
t.Run(name, func(t *testing.T) {
|
||||
whiteImage := ebiten.NewImage(3, 3)
|
||||
whiteImage.Fill(color.White)
|
||||
emptySubImage := whiteImage.SubImage(image.Rect(1, 1, 2, 2)).(*ebiten.Image)
|
||||
|
||||
// The outside rectangle (clockwise)
|
||||
vs0 := []ebiten.Vertex{
|
||||
{
|
||||
DstX: 1, DstY: 1, SrcX: 1, SrcY: 1,
|
||||
ColorR: 1, ColorG: 0, ColorB: 0, ColorA: 1,
|
||||
},
|
||||
{
|
||||
DstX: 15, DstY: 1, SrcX: 1, SrcY: 1,
|
||||
ColorR: 1, ColorG: 0, ColorB: 0, ColorA: 1,
|
||||
},
|
||||
{
|
||||
DstX: 15, DstY: 15, SrcX: 1, SrcY: 1,
|
||||
ColorR: 1, ColorG: 0, ColorB: 0, ColorA: 1,
|
||||
},
|
||||
{
|
||||
DstX: 1, DstY: 15, SrcX: 1, SrcY: 1,
|
||||
ColorR: 1, ColorG: 0, ColorB: 0, ColorA: 1,
|
||||
},
|
||||
}
|
||||
is0 := []uint16{0, 1, 2, 2, 3, 0}
|
||||
|
||||
// An inside rectangle (clockwise)
|
||||
vs1 := []ebiten.Vertex{
|
||||
{
|
||||
DstX: 2, DstY: 2, SrcX: 1, SrcY: 1,
|
||||
ColorR: 0, ColorG: 1, ColorB: 0, ColorA: 1,
|
||||
},
|
||||
{
|
||||
DstX: 7, DstY: 2, SrcX: 1, SrcY: 1,
|
||||
ColorR: 0, ColorG: 1, ColorB: 0, ColorA: 1,
|
||||
},
|
||||
{
|
||||
DstX: 7, DstY: 7, SrcX: 1, SrcY: 1,
|
||||
ColorR: 0, ColorG: 1, ColorB: 0, ColorA: 1,
|
||||
},
|
||||
{
|
||||
DstX: 2, DstY: 7, SrcX: 1, SrcY: 1,
|
||||
ColorR: 0, ColorG: 1, ColorB: 0, ColorA: 1,
|
||||
},
|
||||
}
|
||||
is1 := []uint16{4, 5, 6, 6, 7, 4}
|
||||
|
||||
// An inside rectangle (counter-clockwise)
|
||||
vs2 := []ebiten.Vertex{
|
||||
{
|
||||
DstX: 9, DstY: 9, SrcX: 1, SrcY: 1,
|
||||
ColorR: 0, ColorG: 0, ColorB: 1, ColorA: 1,
|
||||
},
|
||||
{
|
||||
DstX: 14, DstY: 9, SrcX: 1, SrcY: 1,
|
||||
ColorR: 0, ColorG: 0, ColorB: 1, ColorA: 1,
|
||||
},
|
||||
{
|
||||
DstX: 14, DstY: 14, SrcX: 1, SrcY: 1,
|
||||
ColorR: 0, ColorG: 0, ColorB: 1, ColorA: 1,
|
||||
},
|
||||
{
|
||||
DstX: 9, DstY: 14, SrcX: 1, SrcY: 1,
|
||||
ColorR: 0, ColorG: 0, ColorB: 1, ColorA: 1,
|
||||
},
|
||||
}
|
||||
is2 := []uint16{8, 11, 10, 10, 9, 8}
|
||||
|
||||
// Draw all the vertices once. The even-odd rule is applied for all the vertices once.
|
||||
dst := ebiten.NewImage(16, 16)
|
||||
op := &ebiten.DrawTrianglesOptions{
|
||||
FillRule: fillRule,
|
||||
}
|
||||
dst.DrawTriangles(append(append(vs0, vs1...), vs2...), append(append(is0, is1...), is2...), emptySubImage, op)
|
||||
for j := 0; j < 16; j++ {
|
||||
for i := 0; i < 16; i++ {
|
||||
got := dst.At(i, j)
|
||||
var want color.RGBA
|
||||
switch {
|
||||
case 2 <= i && i < 7 && 2 <= j && j < 7:
|
||||
if fillRule != ebiten.EvenOdd {
|
||||
want = color.RGBA{G: 0xff, A: 0xff}
|
||||
}
|
||||
case 9 <= i && i < 14 && 9 <= j && j < 14:
|
||||
if fillRule == ebiten.FillAll {
|
||||
want = color.RGBA{B: 0xff, A: 0xff}
|
||||
}
|
||||
case 1 <= i && i < 15 && 1 <= j && j < 15:
|
||||
want = color.RGBA{R: 0xff, A: 0xff}
|
||||
}
|
||||
if got != want {
|
||||
t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do the same thing but with a little shift. This confirms that the underlying stencil buffer is cleared correctly.
|
||||
for i := range vs0 {
|
||||
vs0[i].DstX++
|
||||
vs0[i].DstY++
|
||||
}
|
||||
for i := range vs1 {
|
||||
vs1[i].DstX++
|
||||
vs1[i].DstY++
|
||||
}
|
||||
for i := range vs2 {
|
||||
vs2[i].DstX++
|
||||
vs2[i].DstY++
|
||||
}
|
||||
dst.Clear()
|
||||
dst.DrawTriangles(append(append(vs0, vs1...), vs2...), append(append(is0, is1...), is2...), emptySubImage, op)
|
||||
for j := 0; j < 16; j++ {
|
||||
for i := 0; i < 16; i++ {
|
||||
got := dst.At(i, j)
|
||||
var want color.RGBA
|
||||
switch {
|
||||
case 3 <= i && i < 8 && 3 <= j && j < 8:
|
||||
if fillRule != ebiten.EvenOdd {
|
||||
want = color.RGBA{G: 0xff, A: 0xff}
|
||||
}
|
||||
case 10 <= i && i < 15 && 10 <= j && j < 15:
|
||||
if fillRule == ebiten.FillAll {
|
||||
want = color.RGBA{B: 0xff, A: 0xff}
|
||||
}
|
||||
case 2 <= i && i < 16 && 2 <= j && j < 16:
|
||||
want = color.RGBA{R: 0xff, A: 0xff}
|
||||
}
|
||||
if got != want {
|
||||
t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do the same thing but with split DrawTriangle calls. This confirms that fill rules are applied for one call.
|
||||
for i := range vs0 {
|
||||
vs0[i].DstX--
|
||||
vs0[i].DstY--
|
||||
}
|
||||
for i := range vs1 {
|
||||
vs1[i].DstX--
|
||||
vs1[i].DstY--
|
||||
}
|
||||
for i := range vs2 {
|
||||
vs2[i].DstX--
|
||||
vs2[i].DstY--
|
||||
}
|
||||
dst.Clear()
|
||||
dst.DrawTriangles(vs0, []uint16{0, 1, 2, 2, 3, 0}, emptySubImage, op)
|
||||
dst.DrawTriangles(vs1, []uint16{0, 1, 2, 2, 3, 0}, emptySubImage, op)
|
||||
dst.DrawTriangles(vs2, []uint16{0, 3, 2, 2, 1, 0}, emptySubImage, op)
|
||||
for j := 0; j < 16; j++ {
|
||||
for i := 0; i < 16; i++ {
|
||||
got := dst.At(i, j)
|
||||
var want color.RGBA
|
||||
switch {
|
||||
case 2 <= i && i < 7 && 2 <= j && j < 7:
|
||||
want = color.RGBA{G: 0xff, A: 0xff}
|
||||
case 9 <= i && i < 14 && 9 <= j && j < 14:
|
||||
want = color.RGBA{B: 0xff, A: 0xff}
|
||||
case 1 <= i && i < 15 && 1 <= j && j < 15:
|
||||
want = color.RGBA{R: 0xff, A: 0xff}
|
||||
}
|
||||
if got != want {
|
||||
t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// #1658
|
||||
func BenchmarkColorMScale(b *testing.B) {
|
||||
r := rand.Float64
|
||||
|
@ -166,7 +166,7 @@ func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst *Image, srcs
|
||||
if c.fillRule != fillRule {
|
||||
return false
|
||||
}
|
||||
if c.fillRule == graphicsdriver.EvenOdd && mightOverlapDstRegions(c.vertices, vertices) {
|
||||
if c.fillRule != graphicsdriver.FillAll && mightOverlapDstRegions(c.vertices, vertices) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
@ -584,25 +584,39 @@ func (g *graphics11) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphic
|
||||
switch fillRule {
|
||||
case graphicsdriver.FillAll:
|
||||
g.deviceContext.DrawIndexed(uint32(dstRegion.IndexCount), uint32(indexOffset), 0)
|
||||
case graphicsdriver.EvenOdd:
|
||||
bs, err := g.blendState(blend, prepareStencil)
|
||||
case graphicsdriver.NonZero:
|
||||
bs, err := g.blendState(blend, incrementStencil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.deviceContext.OMSetBlendState(bs, nil, 0xffffffff)
|
||||
dss, err := g.depthStencilState(prepareStencil)
|
||||
dss, err := g.depthStencilState(incrementStencil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.deviceContext.OMSetDepthStencilState(dss, 0)
|
||||
g.deviceContext.DrawIndexed(uint32(dstRegion.IndexCount), uint32(indexOffset), 0)
|
||||
|
||||
bs, err = g.blendState(blend, drawWithStencil)
|
||||
case graphicsdriver.EvenOdd:
|
||||
bs, err := g.blendState(blend, invertStencil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.deviceContext.OMSetBlendState(bs, nil, 0xffffffff)
|
||||
dss, err = g.depthStencilState(drawWithStencil)
|
||||
dss, err := g.depthStencilState(invertStencil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.deviceContext.OMSetDepthStencilState(dss, 0)
|
||||
g.deviceContext.DrawIndexed(uint32(dstRegion.IndexCount), uint32(indexOffset), 0)
|
||||
}
|
||||
|
||||
if fillRule != graphicsdriver.FillAll {
|
||||
bs, err := g.blendState(blend, drawWithStencil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.deviceContext.OMSetBlendState(bs, nil, 0xffffffff)
|
||||
dss, err := g.depthStencilState(drawWithStencil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -627,9 +641,9 @@ func (g *graphics11) genNextShaderID() graphicsdriver.ShaderID {
|
||||
}
|
||||
|
||||
func (g *graphics11) blendState(blend graphicsdriver.Blend, stencilMode stencilMode) (*_ID3D11BlendState, error) {
|
||||
writeMask := uint8(_D3D11_COLOR_WRITE_ENABLE_ALL)
|
||||
if stencilMode == prepareStencil {
|
||||
writeMask = 0
|
||||
var writeMask uint8
|
||||
if stencilMode == noStencil || stencilMode == drawWithStencil {
|
||||
writeMask = uint8(_D3D11_COLOR_WRITE_ENABLE_ALL)
|
||||
}
|
||||
|
||||
key := blendStateKey{
|
||||
@ -693,7 +707,11 @@ func (g *graphics11) depthStencilState(mode stencilMode) (*_ID3D11DepthStencilSt
|
||||
},
|
||||
}
|
||||
switch mode {
|
||||
case prepareStencil:
|
||||
case incrementStencil:
|
||||
desc.StencilEnable = 1
|
||||
desc.FrontFace.StencilPassOp = _D3D11_STENCIL_OP_INCR
|
||||
desc.BackFace.StencilPassOp = _D3D11_STENCIL_OP_DECR
|
||||
case invertStencil:
|
||||
desc.StencilEnable = 1
|
||||
desc.FrontFace.StencilPassOp = _D3D11_STENCIL_OP_INVERT
|
||||
desc.BackFace.StencilPassOp = _D3D11_STENCIL_OP_INVERT
|
||||
|
@ -1097,7 +1097,7 @@ func (g *graphics12) DrawTriangles(dstID graphicsdriver.ImageID, srcs [graphics.
|
||||
|
||||
// Release constant buffers when too many ones will be created.
|
||||
numPipelines := 1
|
||||
if fillRule == graphicsdriver.EvenOdd {
|
||||
if fillRule != graphicsdriver.FillAll {
|
||||
numPipelines = 2
|
||||
}
|
||||
if len(g.pipelineStates.constantBuffers[g.frameIndex])+numPipelines > numDescriptorsPerFrame {
|
||||
|
@ -35,9 +35,10 @@ import (
|
||||
type stencilMode int
|
||||
|
||||
const (
|
||||
prepareStencil stencilMode = iota
|
||||
noStencil stencilMode = iota
|
||||
incrementStencil
|
||||
invertStencil
|
||||
drawWithStencil
|
||||
noStencil
|
||||
)
|
||||
|
||||
const frameCount = 2
|
||||
|
@ -309,21 +309,31 @@ func (p *pipelineStates) drawTriangles(device *_ID3D12Device, commandList *_ID3D
|
||||
switch fillRule {
|
||||
case graphicsdriver.FillAll:
|
||||
commandList.DrawIndexedInstanced(uint32(dstRegion.IndexCount), 1, uint32(indexOffset), 0, 0)
|
||||
case graphicsdriver.EvenOdd:
|
||||
s, err := shader.pipelineState(blend, prepareStencil, screen)
|
||||
case graphicsdriver.NonZero:
|
||||
s, err := shader.pipelineState(blend, incrementStencil, screen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
commandList.SetPipelineState(s)
|
||||
commandList.DrawIndexedInstanced(uint32(dstRegion.IndexCount), 1, uint32(indexOffset), 0, 0)
|
||||
|
||||
s, err = shader.pipelineState(blend, drawWithStencil, screen)
|
||||
case graphicsdriver.EvenOdd:
|
||||
s, err := shader.pipelineState(blend, invertStencil, screen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
commandList.SetPipelineState(s)
|
||||
commandList.DrawIndexedInstanced(uint32(dstRegion.IndexCount), 1, uint32(indexOffset), 0, 0)
|
||||
}
|
||||
|
||||
if fillRule != graphicsdriver.FillAll {
|
||||
s, err := shader.pipelineState(blend, drawWithStencil, screen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
commandList.SetPipelineState(s)
|
||||
commandList.DrawIndexedInstanced(uint32(dstRegion.IndexCount), 1, uint32(indexOffset), 0, 0)
|
||||
}
|
||||
|
||||
indexOffset += dstRegion.IndexCount
|
||||
}
|
||||
|
||||
@ -443,14 +453,21 @@ func (p *pipelineStates) newPipelineState(device *_ID3D12Device, vsh, psh *_ID3D
|
||||
StencilFunc: _D3D12_COMPARISON_FUNC_ALWAYS,
|
||||
},
|
||||
}
|
||||
writeMask := uint8(_D3D12_COLOR_WRITE_ENABLE_ALL)
|
||||
|
||||
var writeMask uint8
|
||||
if stencilMode == noStencil || stencilMode == drawWithStencil {
|
||||
writeMask = uint8(_D3D12_COLOR_WRITE_ENABLE_ALL)
|
||||
}
|
||||
|
||||
switch stencilMode {
|
||||
case prepareStencil:
|
||||
case incrementStencil:
|
||||
depthStencilDesc.StencilEnable = 1
|
||||
depthStencilDesc.FrontFace.StencilPassOp = _D3D12_STENCIL_OP_INCR
|
||||
depthStencilDesc.BackFace.StencilPassOp = _D3D12_STENCIL_OP_DECR
|
||||
case invertStencil:
|
||||
depthStencilDesc.StencilEnable = 1
|
||||
depthStencilDesc.FrontFace.StencilPassOp = _D3D12_STENCIL_OP_INVERT
|
||||
depthStencilDesc.BackFace.StencilPassOp = _D3D12_STENCIL_OP_INVERT
|
||||
writeMask = 0
|
||||
case drawWithStencil:
|
||||
depthStencilDesc.StencilEnable = 1
|
||||
depthStencilDesc.FrontFace.StencilFunc = _D3D12_COMPARISON_FUNC_NOT_EQUAL
|
||||
|
@ -31,6 +31,7 @@ type FillRule int
|
||||
|
||||
const (
|
||||
FillAll FillRule = iota
|
||||
NonZero
|
||||
EvenOdd
|
||||
)
|
||||
|
||||
@ -38,6 +39,8 @@ func (f FillRule) String() string {
|
||||
switch f {
|
||||
case FillAll:
|
||||
return "FillAll"
|
||||
case NonZero:
|
||||
return "NonZero"
|
||||
case EvenOdd:
|
||||
return "EvenOdd"
|
||||
default:
|
||||
|
@ -70,9 +70,10 @@ type Graphics struct {
|
||||
type stencilMode int
|
||||
|
||||
const (
|
||||
prepareStencil stencilMode = iota
|
||||
noStencil stencilMode = iota
|
||||
incrementStencil
|
||||
invertStencil
|
||||
drawWithStencil
|
||||
noStencil
|
||||
)
|
||||
|
||||
var (
|
||||
@ -412,7 +413,35 @@ func (g *Graphics) Initialize() error {
|
||||
}
|
||||
|
||||
// The stencil reference value is always 0 (default).
|
||||
g.dsss[prepareStencil] = g.view.getMTLDevice().MakeDepthStencilState(mtl.DepthStencilDescriptor{
|
||||
g.dsss[noStencil] = g.view.getMTLDevice().MakeDepthStencilState(mtl.DepthStencilDescriptor{
|
||||
BackFaceStencil: mtl.StencilDescriptor{
|
||||
StencilFailureOperation: mtl.StencilOperationKeep,
|
||||
DepthFailureOperation: mtl.StencilOperationKeep,
|
||||
DepthStencilPassOperation: mtl.StencilOperationKeep,
|
||||
StencilCompareFunction: mtl.CompareFunctionAlways,
|
||||
},
|
||||
FrontFaceStencil: mtl.StencilDescriptor{
|
||||
StencilFailureOperation: mtl.StencilOperationKeep,
|
||||
DepthFailureOperation: mtl.StencilOperationKeep,
|
||||
DepthStencilPassOperation: mtl.StencilOperationKeep,
|
||||
StencilCompareFunction: mtl.CompareFunctionAlways,
|
||||
},
|
||||
})
|
||||
g.dsss[incrementStencil] = g.view.getMTLDevice().MakeDepthStencilState(mtl.DepthStencilDescriptor{
|
||||
BackFaceStencil: mtl.StencilDescriptor{
|
||||
StencilFailureOperation: mtl.StencilOperationKeep,
|
||||
DepthFailureOperation: mtl.StencilOperationKeep,
|
||||
DepthStencilPassOperation: mtl.StencilOperationDecrementWrap,
|
||||
StencilCompareFunction: mtl.CompareFunctionAlways,
|
||||
},
|
||||
FrontFaceStencil: mtl.StencilDescriptor{
|
||||
StencilFailureOperation: mtl.StencilOperationKeep,
|
||||
DepthFailureOperation: mtl.StencilOperationKeep,
|
||||
DepthStencilPassOperation: mtl.StencilOperationIncrementWrap,
|
||||
StencilCompareFunction: mtl.CompareFunctionAlways,
|
||||
},
|
||||
})
|
||||
g.dsss[invertStencil] = g.view.getMTLDevice().MakeDepthStencilState(mtl.DepthStencilDescriptor{
|
||||
BackFaceStencil: mtl.StencilDescriptor{
|
||||
StencilFailureOperation: mtl.StencilOperationKeep,
|
||||
DepthFailureOperation: mtl.StencilOperationKeep,
|
||||
@ -440,20 +469,6 @@ func (g *Graphics) Initialize() error {
|
||||
StencilCompareFunction: mtl.CompareFunctionNotEqual,
|
||||
},
|
||||
})
|
||||
g.dsss[noStencil] = g.view.getMTLDevice().MakeDepthStencilState(mtl.DepthStencilDescriptor{
|
||||
BackFaceStencil: mtl.StencilDescriptor{
|
||||
StencilFailureOperation: mtl.StencilOperationKeep,
|
||||
DepthFailureOperation: mtl.StencilOperationKeep,
|
||||
DepthStencilPassOperation: mtl.StencilOperationKeep,
|
||||
StencilCompareFunction: mtl.CompareFunctionAlways,
|
||||
},
|
||||
FrontFaceStencil: mtl.StencilDescriptor{
|
||||
StencilFailureOperation: mtl.StencilOperationKeep,
|
||||
DepthFailureOperation: mtl.StencilOperationKeep,
|
||||
DepthStencilPassOperation: mtl.StencilOperationKeep,
|
||||
StencilCompareFunction: mtl.CompareFunctionAlways,
|
||||
},
|
||||
})
|
||||
|
||||
g.cq = g.view.getMTLDevice().MakeCommandQueue()
|
||||
return nil
|
||||
@ -472,7 +487,7 @@ func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs
|
||||
// When preparing a stencil buffer, flush the current render command encoder
|
||||
// to make sure the stencil buffer is cleared when loading.
|
||||
// TODO: What about clearing the stencil buffer by vertices?
|
||||
if g.lastDst != dst || g.lastFillRule != fillRule || fillRule == graphicsdriver.EvenOdd {
|
||||
if g.lastDst != dst || g.lastFillRule != fillRule || fillRule != graphicsdriver.FillAll {
|
||||
g.flushRenderCommandEncoderIfNeeded()
|
||||
}
|
||||
g.lastDst = dst
|
||||
@ -498,7 +513,7 @@ func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs
|
||||
rpd.ColorAttachments[0].Texture = t
|
||||
rpd.ColorAttachments[0].ClearColor = mtl.ClearColor{}
|
||||
|
||||
if fillRule == graphicsdriver.EvenOdd {
|
||||
if fillRule != graphicsdriver.FillAll {
|
||||
dst.ensureStencil()
|
||||
rpd.StencilAttachment.LoadAction = mtl.LoadActionClear
|
||||
rpd.StencilAttachment.StoreAction = mtl.StoreActionDontCare
|
||||
@ -539,9 +554,10 @@ func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs
|
||||
}
|
||||
|
||||
var (
|
||||
prepareStencilRpss mtl.RenderPipelineState
|
||||
drawWithStencilRpss mtl.RenderPipelineState
|
||||
noStencilRpss mtl.RenderPipelineState
|
||||
noStencilRpss mtl.RenderPipelineState
|
||||
incrementStencilRpss mtl.RenderPipelineState
|
||||
invertStencilRpss mtl.RenderPipelineState
|
||||
drawWithStencilRpss mtl.RenderPipelineState
|
||||
)
|
||||
switch fillRule {
|
||||
case graphicsdriver.FillAll:
|
||||
@ -550,14 +566,21 @@ func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs
|
||||
return err
|
||||
}
|
||||
noStencilRpss = s
|
||||
case graphicsdriver.EvenOdd:
|
||||
s, err := shader.RenderPipelineState(&g.view, blend, prepareStencil, dst.screen)
|
||||
case graphicsdriver.NonZero:
|
||||
s, err := shader.RenderPipelineState(&g.view, blend, incrementStencil, dst.screen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
prepareStencilRpss = s
|
||||
|
||||
s, err = shader.RenderPipelineState(&g.view, blend, drawWithStencil, dst.screen)
|
||||
incrementStencilRpss = s
|
||||
case graphicsdriver.EvenOdd:
|
||||
s, err := shader.RenderPipelineState(&g.view, blend, invertStencil, dst.screen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
invertStencilRpss = s
|
||||
}
|
||||
if fillRule != graphicsdriver.FillAll {
|
||||
s, err := shader.RenderPipelineState(&g.view, blend, drawWithStencil, dst.screen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -577,11 +600,16 @@ func (g *Graphics) draw(dst *Image, dstRegions []graphicsdriver.DstRegion, srcs
|
||||
g.rce.SetDepthStencilState(g.dsss[noStencil])
|
||||
g.rce.SetRenderPipelineState(noStencilRpss)
|
||||
g.rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, dstRegion.IndexCount, mtl.IndexTypeUInt32, g.ib, indexOffset*int(unsafe.Sizeof(uint32(0))))
|
||||
case graphicsdriver.EvenOdd:
|
||||
g.rce.SetDepthStencilState(g.dsss[prepareStencil])
|
||||
g.rce.SetRenderPipelineState(prepareStencilRpss)
|
||||
case graphicsdriver.NonZero:
|
||||
g.rce.SetDepthStencilState(g.dsss[incrementStencil])
|
||||
g.rce.SetRenderPipelineState(incrementStencilRpss)
|
||||
g.rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, dstRegion.IndexCount, mtl.IndexTypeUInt32, g.ib, indexOffset*int(unsafe.Sizeof(uint32(0))))
|
||||
|
||||
case graphicsdriver.EvenOdd:
|
||||
g.rce.SetDepthStencilState(g.dsss[invertStencil])
|
||||
g.rce.SetRenderPipelineState(invertStencilRpss)
|
||||
g.rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, dstRegion.IndexCount, mtl.IndexTypeUInt32, g.ib, indexOffset*int(unsafe.Sizeof(uint32(0))))
|
||||
}
|
||||
if fillRule != graphicsdriver.FillAll {
|
||||
g.rce.SetDepthStencilState(g.dsss[drawWithStencil])
|
||||
g.rce.SetRenderPipelineState(drawWithStencilRpss)
|
||||
g.rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, dstRegion.IndexCount, mtl.IndexTypeUInt32, g.ib, indexOffset*int(unsafe.Sizeof(uint32(0))))
|
||||
|
@ -119,10 +119,10 @@ func (s *Shader) RenderPipelineState(view *view, blend graphicsdriver.Blend, ste
|
||||
rpld.ColorAttachments[0].AlphaBlendOperation = blendOperationToMetalBlendOperation(blend.BlendOperationAlpha)
|
||||
rpld.ColorAttachments[0].RGBBlendOperation = blendOperationToMetalBlendOperation(blend.BlendOperationRGB)
|
||||
|
||||
if stencilMode == prepareStencil {
|
||||
rpld.ColorAttachments[0].WriteMask = mtl.ColorWriteMaskNone
|
||||
} else {
|
||||
if stencilMode == noStencil || stencilMode == drawWithStencil {
|
||||
rpld.ColorAttachments[0].WriteMask = mtl.ColorWriteMaskAll
|
||||
} else {
|
||||
rpld.ColorAttachments[0].WriteMask = mtl.ColorWriteMaskNone
|
||||
}
|
||||
|
||||
rps, err := view.getMTLDevice().MakeRenderPipelineState(rpld)
|
||||
|
@ -17,10 +17,12 @@ package gl
|
||||
const (
|
||||
ALWAYS = 0x0207
|
||||
ARRAY_BUFFER = 0x8892
|
||||
BACK = 0x0405
|
||||
BLEND = 0x0BE2
|
||||
CLAMP_TO_EDGE = 0x812F
|
||||
COLOR_ATTACHMENT0 = 0x8CE0
|
||||
COMPILE_STATUS = 0x8B81
|
||||
DECR_WRAP = 0x8508
|
||||
DEPTH24_STENCIL8 = 0x88F0
|
||||
DST_ALPHA = 0x0304
|
||||
DST_COLOR = 0x0306
|
||||
@ -32,10 +34,13 @@ const (
|
||||
FRAMEBUFFER = 0x8D40
|
||||
FRAMEBUFFER_BINDING = 0x8CA6
|
||||
FRAMEBUFFER_COMPLETE = 0x8CD5
|
||||
FRONT = 0x0404
|
||||
FRONT_AND_BACK = 0x0408
|
||||
FUNC_ADD = 0x8006
|
||||
FUNC_REVERSE_SUBTRACT = 0x800b
|
||||
FUNC_SUBTRACT = 0x800a
|
||||
HIGH_FLOAT = 0x8DF2
|
||||
INCR_WRAP = 0x8507
|
||||
INFO_LOG_LENGTH = 0x8B84
|
||||
INVERT = 0x150A
|
||||
KEEP = 0x1E00
|
||||
|
@ -501,11 +501,11 @@ func (d *DebugContext) StencilFunc(arg0 uint32, arg1 int32, arg2 uint32) {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DebugContext) StencilOp(arg0 uint32, arg1 uint32, arg2 uint32) {
|
||||
d.Context.StencilOp(arg0, arg1, arg2)
|
||||
fmt.Fprintln(os.Stderr, "StencilOp")
|
||||
func (d *DebugContext) StencilOpSeparate(arg0 uint32, arg1 uint32, arg2 uint32, arg3 uint32) {
|
||||
d.Context.StencilOpSeparate(arg0, arg1, arg2, arg3)
|
||||
fmt.Fprintln(os.Stderr, "StencilOpSeparate")
|
||||
if e := d.Context.GetError(); e != NO_ERROR {
|
||||
panic(fmt.Sprintf("gl: GetError() returned %d at StencilOp", e))
|
||||
panic(fmt.Sprintf("gl: GetError() returned %d at StencilOpSeparate", e))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -244,9 +244,9 @@ package gl
|
||||
// typedef void (*fn)(GLenum func, GLint ref, GLuint mask);
|
||||
// ((fn)(fnptr))(func, ref, mask);
|
||||
// }
|
||||
// static void glowStencilOp(uintptr_t fnptr, GLenum fail, GLenum zfail, GLenum zpass) {
|
||||
// typedef void (*fn)(GLenum fail, GLenum zfail, GLenum zpass);
|
||||
// ((fn)(fnptr))(fail, zfail, zpass);
|
||||
// static void glowStencilOpSeparate(uintptr_t fnptr, GLenum face, GLenum fail, GLenum zfail, GLenum zpass) {
|
||||
// typedef void (*fn)(GLenum face, GLenum fail, GLenum zfail, GLenum zpass);
|
||||
// ((fn)(fnptr))(face, fail, zfail, zpass);
|
||||
// }
|
||||
// static void glowTexImage2D(uintptr_t fnptr, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void* pixels) {
|
||||
// typedef void (*fn)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void* pixels);
|
||||
@ -384,7 +384,7 @@ type defaultContext struct {
|
||||
gpScissor C.uintptr_t
|
||||
gpShaderSource C.uintptr_t
|
||||
gpStencilFunc C.uintptr_t
|
||||
gpStencilOp C.uintptr_t
|
||||
gpStencilOpSeparate C.uintptr_t
|
||||
gpTexImage2D C.uintptr_t
|
||||
gpTexParameteri C.uintptr_t
|
||||
gpTexSubImage2D C.uintptr_t
|
||||
@ -688,8 +688,8 @@ func (c *defaultContext) StencilFunc(xfunc uint32, ref int32, mask uint32) {
|
||||
C.glowStencilFunc(c.gpStencilFunc, C.GLenum(xfunc), C.GLint(ref), C.GLuint(mask))
|
||||
}
|
||||
|
||||
func (c *defaultContext) StencilOp(fail uint32, zfail uint32, zpass uint32) {
|
||||
C.glowStencilOp(c.gpStencilOp, C.GLenum(fail), C.GLenum(zfail), C.GLenum(zpass))
|
||||
func (c *defaultContext) StencilOpSeparate(face uint32, fail uint32, zfail uint32, zpass uint32) {
|
||||
C.glowStencilOpSeparate(c.gpStencilOpSeparate, C.GLenum(face), C.GLenum(fail), C.GLenum(zfail), C.GLenum(zpass))
|
||||
}
|
||||
|
||||
func (c *defaultContext) TexImage2D(target uint32, level int32, internalformat int32, width int32, height int32, format uint32, xtype uint32, pixels []byte) {
|
||||
@ -840,7 +840,7 @@ func (c *defaultContext) LoadFunctions() error {
|
||||
c.gpScissor = C.uintptr_t(g.get("glScissor"))
|
||||
c.gpShaderSource = C.uintptr_t(g.get("glShaderSource"))
|
||||
c.gpStencilFunc = C.uintptr_t(g.get("glStencilFunc"))
|
||||
c.gpStencilOp = C.uintptr_t(g.get("glStencilOp"))
|
||||
c.gpStencilOpSeparate = C.uintptr_t(g.get("glStencilOpSeparate"))
|
||||
c.gpTexImage2D = C.uintptr_t(g.get("glTexImage2D"))
|
||||
c.gpTexParameteri = C.uintptr_t(g.get("glTexParameteri"))
|
||||
c.gpTexSubImage2D = C.uintptr_t(g.get("glTexSubImage2D"))
|
||||
|
@ -79,7 +79,7 @@ type defaultContext struct {
|
||||
fnShaderSource js.Value
|
||||
fnStencilFunc js.Value
|
||||
fnStencilMask js.Value
|
||||
fnStencilOp js.Value
|
||||
fnStencilOpSeparate js.Value
|
||||
fnTexImage2D js.Value
|
||||
fnTexSubImage2D js.Value
|
||||
fnTexParameteri js.Value
|
||||
@ -210,7 +210,7 @@ func NewDefaultContext(v js.Value) (Context, error) {
|
||||
fnShaderSource: v.Get("shaderSource").Call("bind", v),
|
||||
fnStencilFunc: v.Get("stencilFunc").Call("bind", v),
|
||||
fnStencilMask: v.Get("stencilMask").Call("bind", v),
|
||||
fnStencilOp: v.Get("stencilOp").Call("bind", v),
|
||||
fnStencilOpSeparate: v.Get("stencilOpSeparate").Call("bind", v),
|
||||
fnTexImage2D: v.Get("texImage2D").Call("bind", v),
|
||||
fnTexSubImage2D: v.Get("texSubImage2D").Call("bind", v),
|
||||
fnTexParameteri: v.Get("texParameteri").Call("bind", v),
|
||||
@ -533,8 +533,8 @@ func (c *defaultContext) StencilFunc(func_ uint32, ref int32, mask uint32) {
|
||||
c.fnStencilFunc.Invoke(func_, ref, mask)
|
||||
}
|
||||
|
||||
func (c *defaultContext) StencilOp(sfail, dpfail, dppass uint32) {
|
||||
c.fnStencilOp.Invoke(sfail, dpfail, dppass)
|
||||
func (c *defaultContext) StencilOpSeparate(face, sfail, dpfail, dppass uint32) {
|
||||
c.fnStencilOpSeparate.Invoke(face, sfail, dpfail, dppass)
|
||||
}
|
||||
|
||||
func (c *defaultContext) TexImage2D(target uint32, level int32, internalformat int32, width int32, height int32, format uint32, xtype uint32, pixels []byte) {
|
||||
|
@ -80,7 +80,7 @@ type defaultContext struct {
|
||||
gpScissor uintptr
|
||||
gpShaderSource uintptr
|
||||
gpStencilFunc uintptr
|
||||
gpStencilOp uintptr
|
||||
gpStencilOpSeparate uintptr
|
||||
gpTexImage2D uintptr
|
||||
gpTexParameteri uintptr
|
||||
gpTexSubImage2D uintptr
|
||||
@ -384,8 +384,8 @@ func (c *defaultContext) StencilFunc(xfunc uint32, ref int32, mask uint32) {
|
||||
purego.SyscallN(c.gpStencilFunc, uintptr(xfunc), uintptr(ref), uintptr(mask))
|
||||
}
|
||||
|
||||
func (c *defaultContext) StencilOp(fail uint32, zfail uint32, zpass uint32) {
|
||||
purego.SyscallN(c.gpStencilOp, uintptr(fail), uintptr(zfail), uintptr(zpass))
|
||||
func (c *defaultContext) StencilOpSeparate(face uint32, fail uint32, zfail uint32, zpass uint32) {
|
||||
purego.SyscallN(c.gpStencilOpSeparate, uintptr(face), uintptr(fail), uintptr(zfail), uintptr(zpass))
|
||||
}
|
||||
|
||||
func (c *defaultContext) TexImage2D(target uint32, level int32, internalformat int32, width int32, height int32, format uint32, xtype uint32, pixels []byte) {
|
||||
@ -536,7 +536,7 @@ func (c *defaultContext) LoadFunctions() error {
|
||||
c.gpScissor = g.get("glScissor")
|
||||
c.gpShaderSource = g.get("glShaderSource")
|
||||
c.gpStencilFunc = g.get("glStencilFunc")
|
||||
c.gpStencilOp = g.get("glStencilOp")
|
||||
c.gpStencilOpSeparate = g.get("glStencilOpSeparate")
|
||||
c.gpTexImage2D = g.get("glTexImage2D")
|
||||
c.gpTexParameteri = g.get("glTexParameteri")
|
||||
c.gpTexSubImage2D = g.get("glTexSubImage2D")
|
||||
|
@ -267,8 +267,8 @@ func (g *gomobileContext) StencilFunc(func_ uint32, ref int32, mask uint32) {
|
||||
g.ctx.StencilFunc(gl.Enum(func_), int(ref), mask)
|
||||
}
|
||||
|
||||
func (g *gomobileContext) StencilOp(sfail, dpfail, dppass uint32) {
|
||||
g.ctx.StencilOp(gl.Enum(sfail), gl.Enum(dpfail), gl.Enum(dppass))
|
||||
func (g *gomobileContext) StencilOpSeparate(face, sfail, dpfail, dppass uint32) {
|
||||
g.ctx.StencilOpSeparate(gl.Enum(face), gl.Enum(sfail), gl.Enum(dpfail), gl.Enum(dppass))
|
||||
}
|
||||
|
||||
func (g *gomobileContext) TexImage2D(target uint32, level int32, internalformat int32, width int32, height int32, format uint32, xtype uint32, pixels []byte) {
|
||||
|
@ -81,7 +81,7 @@ type Context interface {
|
||||
Scissor(x, y, width, height int32)
|
||||
ShaderSource(shader uint32, xstring string)
|
||||
StencilFunc(func_ uint32, ref int32, mask uint32)
|
||||
StencilOp(sfail, dpfail, dppass uint32)
|
||||
StencilOpSeparate(face, sfail, dpfail, dppass uint32)
|
||||
TexImage2D(target uint32, level int32, internalformat int32, width int32, height int32, format uint32, xtype uint32, pixels []byte)
|
||||
TexParameteri(target uint32, pname uint32, param int32)
|
||||
TexSubImage2D(target uint32, level int32, xoffset int32, yoffset int32, width int32, height int32, format uint32, xtype uint32, pixels []byte)
|
||||
|
@ -243,7 +243,7 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.
|
||||
}
|
||||
g.uniformVars = g.uniformVars[:0]
|
||||
|
||||
if fillRule == graphicsdriver.EvenOdd {
|
||||
if fillRule != graphicsdriver.FillAll {
|
||||
if err := destination.ensureStencilBuffer(); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -257,23 +257,32 @@ func (g *Graphics) DrawTriangles(dstID graphicsdriver.ImageID, srcIDs [graphics.
|
||||
int32(dstRegion.Region.Dx()),
|
||||
int32(dstRegion.Region.Dy()),
|
||||
)
|
||||
if fillRule == graphicsdriver.EvenOdd {
|
||||
switch fillRule {
|
||||
case graphicsdriver.NonZero:
|
||||
g.context.ctx.Clear(gl.STENCIL_BUFFER_BIT)
|
||||
g.context.ctx.StencilFunc(gl.ALWAYS, 0x00, 0xff)
|
||||
g.context.ctx.StencilOp(gl.KEEP, gl.KEEP, gl.INVERT)
|
||||
g.context.ctx.StencilOpSeparate(gl.FRONT, gl.KEEP, gl.KEEP, gl.INCR_WRAP)
|
||||
g.context.ctx.StencilOpSeparate(gl.BACK, gl.KEEP, gl.KEEP, gl.DECR_WRAP)
|
||||
g.context.ctx.ColorMask(false, false, false, false)
|
||||
g.context.ctx.DrawElements(gl.TRIANGLES, int32(dstRegion.IndexCount), gl.UNSIGNED_INT, indexOffset*int(unsafe.Sizeof(uint32(0))))
|
||||
case graphicsdriver.EvenOdd:
|
||||
g.context.ctx.Clear(gl.STENCIL_BUFFER_BIT)
|
||||
g.context.ctx.StencilFunc(gl.ALWAYS, 0x00, 0xff)
|
||||
g.context.ctx.StencilOpSeparate(gl.FRONT_AND_BACK, gl.KEEP, gl.KEEP, gl.INVERT)
|
||||
g.context.ctx.ColorMask(false, false, false, false)
|
||||
|
||||
g.context.ctx.DrawElements(gl.TRIANGLES, int32(dstRegion.IndexCount), gl.UNSIGNED_INT, indexOffset*int(unsafe.Sizeof(uint32(0))))
|
||||
|
||||
}
|
||||
if fillRule != graphicsdriver.FillAll {
|
||||
g.context.ctx.StencilFunc(gl.NOTEQUAL, 0x00, 0xff)
|
||||
g.context.ctx.StencilOp(gl.KEEP, gl.KEEP, gl.KEEP)
|
||||
g.context.ctx.StencilOpSeparate(gl.FRONT_AND_BACK, gl.KEEP, gl.KEEP, gl.KEEP)
|
||||
g.context.ctx.ColorMask(true, true, true, true)
|
||||
}
|
||||
g.context.ctx.DrawElements(gl.TRIANGLES, int32(dstRegion.IndexCount), gl.UNSIGNED_INT, indexOffset*int(unsafe.Sizeof(uint32(0))))
|
||||
indexOffset += dstRegion.IndexCount
|
||||
}
|
||||
|
||||
if fillRule == graphicsdriver.EvenOdd {
|
||||
if fillRule != graphicsdriver.FillAll {
|
||||
g.context.ctx.Disable(gl.STENCIL_TEST)
|
||||
}
|
||||
|
||||
|
@ -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 values are intended to be passed to DrawTriangles or DrawTrianglesShader with the EvenOdd fill rule
|
||||
// The returned values are intended to be passed to DrawTriangles or DrawTrianglesShader with the fill rule NonZero or EvenOdd
|
||||
// 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).
|
||||
@ -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 values are intended to be passed to DrawTriangles or DrawTrianglesShader with a solid (non-transparent) color
|
||||
// with the FillAll fill rule (not the EvenOdd fill rule).
|
||||
// with the FillAll fill rule (not NonZero or EvenOdd fill rule).
|
||||
func (p *Path) AppendVerticesAndIndicesForStroke(vertices []ebiten.Vertex, indices []uint16, op *StrokeOptions) ([]ebiten.Vertex, []uint16) {
|
||||
if op == nil {
|
||||
return vertices, indices
|
||||
|
Loading…
Reference in New Issue
Block a user