mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-24 02:38:53 +01:00
ebiten: Add EvenOdd to DrawTrianglesOptions and DrawShaderTrianglesOptions
Updates #844 Closes #1684
This commit is contained in:
parent
5c4d3325f6
commit
b466a0cbd7
@ -19,6 +19,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"log"
|
||||
"math"
|
||||
@ -28,6 +29,15 @@ import (
|
||||
"github.com/hajimehoshi/ebiten/v2/vector"
|
||||
)
|
||||
|
||||
var (
|
||||
emptyImage = ebiten.NewImage(3, 3)
|
||||
emptySubImage = emptyImage.SubImage(image.Rect(1, 1, 2, 2)).(*ebiten.Image)
|
||||
)
|
||||
|
||||
func init() {
|
||||
emptyImage.Fill(color.White)
|
||||
}
|
||||
|
||||
const (
|
||||
screenWidth = 640
|
||||
screenHeight = 480
|
||||
@ -99,10 +109,18 @@ func drawEbitenText(screen *ebiten.Image) {
|
||||
path.LineTo(320, 55)
|
||||
path.LineTo(290, 20)
|
||||
|
||||
op := &vector.FillOptions{
|
||||
Color: color.RGBA{0xdb, 0x56, 0x20, 0xff},
|
||||
op := &ebiten.DrawTrianglesOptions{
|
||||
EvenOdd: true,
|
||||
}
|
||||
path.Fill(screen, op)
|
||||
vs, is := path.AppendVerticesAndIndices(nil, nil)
|
||||
for i := range vs {
|
||||
vs[i].SrcX = 1
|
||||
vs[i].SrcY = 1
|
||||
vs[i].ColorR = 0xdb / float32(0xff)
|
||||
vs[i].ColorG = 0x56 / float32(0xff)
|
||||
vs[i].ColorB = 0x20 / float32(0xff)
|
||||
}
|
||||
screen.DrawTriangles(vs, is, emptySubImage, op)
|
||||
}
|
||||
|
||||
func drawEbitenLogo(screen *ebiten.Image, x, y int) {
|
||||
@ -131,10 +149,18 @@ func drawEbitenLogo(screen *ebiten.Image, x, y int) {
|
||||
path.LineTo(xf+unit, yf+3*unit)
|
||||
path.LineTo(xf+unit, yf+4*unit)
|
||||
|
||||
op := &vector.FillOptions{
|
||||
Color: color.RGBA{0xdb, 0x56, 0x20, 0xff},
|
||||
op := &ebiten.DrawTrianglesOptions{
|
||||
EvenOdd: true,
|
||||
}
|
||||
path.Fill(screen, op)
|
||||
vs, is := path.AppendVerticesAndIndices(nil, nil)
|
||||
for i := range vs {
|
||||
vs[i].SrcX = 1
|
||||
vs[i].SrcY = 1
|
||||
vs[i].ColorR = 0xdb / float32(0xff)
|
||||
vs[i].ColorG = 0x56 / float32(0xff)
|
||||
vs[i].ColorB = 0x20 / float32(0xff)
|
||||
}
|
||||
screen.DrawTriangles(vs, is, emptySubImage, op)
|
||||
}
|
||||
|
||||
func maxCounter(index int) int {
|
||||
@ -166,10 +192,18 @@ func drawWave(screen *ebiten.Image, counter int) {
|
||||
path.LineTo(screenWidth, screenHeight)
|
||||
path.LineTo(0, screenHeight)
|
||||
|
||||
op := &vector.FillOptions{
|
||||
Color: color.RGBA{0x33, 0x66, 0xff, 0xff},
|
||||
op := &ebiten.DrawTrianglesOptions{
|
||||
EvenOdd: true,
|
||||
}
|
||||
path.Fill(screen, op)
|
||||
vs, is := path.AppendVerticesAndIndices(nil, nil)
|
||||
for i := range vs {
|
||||
vs[i].SrcX = 1
|
||||
vs[i].SrcY = 1
|
||||
vs[i].ColorR = 0x33 / float32(0xff)
|
||||
vs[i].ColorG = 0x66 / float32(0xff)
|
||||
vs[i].ColorB = 0xff / float32(0xff)
|
||||
}
|
||||
screen.DrawTriangles(vs, is, emptySubImage, op)
|
||||
}
|
||||
|
||||
type Game struct {
|
||||
|
34
image.go
34
image.go
@ -213,7 +213,7 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) {
|
||||
is := graphics.QuadIndices()
|
||||
|
||||
srcs := [graphics.ShaderImageNum]*mipmap.Mipmap{img.mipmap}
|
||||
i.mipmap.DrawTriangles(srcs, vs, is, options.ColorM.impl, mode, filter, driver.AddressUnsafe, dstRegion, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, canSkipMipmap(options.GeoM, filter))
|
||||
i.mipmap.DrawTriangles(srcs, vs, is, options.ColorM.impl, mode, filter, driver.AddressUnsafe, dstRegion, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false, canSkipMipmap(options.GeoM, filter))
|
||||
}
|
||||
|
||||
// Vertex represents a vertex passed to DrawTriangles.
|
||||
@ -271,6 +271,19 @@ type DrawTrianglesOptions struct {
|
||||
// Address is a sampler address mode.
|
||||
// The default (zero) value is AddressUnsafe.
|
||||
Address Address
|
||||
|
||||
// EvenOdd represents whether the even-odd rule is applied or not.
|
||||
//
|
||||
// If EvenOdd is true, triangles are rendered based on the even-odd rule. If false, triangles are rendered without condition.
|
||||
// Whether overlapped regions by multiple triangles is rendered or not depends on the number of the overlapping:
|
||||
// if and only if the number is odd, the region is rendered.
|
||||
//
|
||||
// EvenOdd is 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.
|
||||
//
|
||||
// The default value is false.
|
||||
EvenOdd bool
|
||||
}
|
||||
|
||||
// MaxIndicesNum is the maximum number of indices for DrawTriangles.
|
||||
@ -349,7 +362,7 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o
|
||||
|
||||
srcs := [graphics.ShaderImageNum]*mipmap.Mipmap{img.mipmap}
|
||||
|
||||
i.mipmap.DrawTriangles(srcs, vs, is, options.ColorM.impl, mode, filter, address, dstRegion, sr, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
|
||||
i.mipmap.DrawTriangles(srcs, vs, is, options.ColorM.impl, mode, filter, address, dstRegion, sr, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, options.EvenOdd, false)
|
||||
}
|
||||
|
||||
// DrawTrianglesShaderOptions represents options for DrawTrianglesShader.
|
||||
@ -371,6 +384,19 @@ type DrawTrianglesShaderOptions struct {
|
||||
// Images is a set of the source images.
|
||||
// All the image must be the same size.
|
||||
Images [4]*Image
|
||||
|
||||
// EvenOdd represents whether the even-odd rule is applied or not.
|
||||
//
|
||||
// If EvenOdd is true, triangles are rendered based on the even-odd rule. If false, triangles are rendered without condition.
|
||||
// Whether overlapped regions by multiple triangles is rendered or not depends on the number of the overlapping:
|
||||
// if and only if the number is odd, the region is rendered.
|
||||
//
|
||||
// EvenOdd is 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.
|
||||
//
|
||||
// The default value is false.
|
||||
EvenOdd bool
|
||||
}
|
||||
|
||||
func init() {
|
||||
@ -485,7 +511,7 @@ func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader
|
||||
}
|
||||
|
||||
us := shader.convertUniforms(options.Uniforms)
|
||||
i.mipmap.DrawTriangles(imgs, vs, is, nil, mode, driver.FilterNearest, driver.AddressUnsafe, dstRegion, sr, offsets, shader.shader, us, false)
|
||||
i.mipmap.DrawTriangles(imgs, vs, is, nil, mode, driver.FilterNearest, driver.AddressUnsafe, dstRegion, sr, offsets, shader.shader, us, options.EvenOdd, false)
|
||||
}
|
||||
|
||||
// DrawRectShaderOptions represents options for DrawRectShader.
|
||||
@ -597,7 +623,7 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR
|
||||
}
|
||||
|
||||
us := shader.convertUniforms(options.Uniforms)
|
||||
i.mipmap.DrawTriangles(imgs, vs, is, nil, mode, driver.FilterNearest, driver.AddressUnsafe, dstRegion, sr, offsets, shader.shader, us, canSkipMipmap(options.GeoM, driver.FilterNearest))
|
||||
i.mipmap.DrawTriangles(imgs, vs, is, nil, mode, driver.FilterNearest, driver.AddressUnsafe, dstRegion, sr, offsets, shader.shader, us, false, canSkipMipmap(options.GeoM, driver.FilterNearest))
|
||||
}
|
||||
|
||||
// SubImage returns an image representing the portion of the image p visible through r.
|
||||
|
@ -298,7 +298,7 @@ func (i *Image) ensureIsolated() {
|
||||
Width: float32(w - 2*paddingSize),
|
||||
Height: float32(h - 2*paddingSize),
|
||||
}
|
||||
newImg.DrawTriangles(srcs, offsets, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dstRegion, driver.Region{}, nil, nil)
|
||||
newImg.DrawTriangles(srcs, offsets, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dstRegion, driver.Region{}, nil, nil, false)
|
||||
|
||||
i.dispose(false)
|
||||
i.backend = &backend{
|
||||
@ -354,7 +354,7 @@ func (i *Image) putOnAtlas() error {
|
||||
Width: w,
|
||||
Height: h,
|
||||
}
|
||||
newI.drawTriangles([graphics.ShaderImageNum]*Image{i}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, true)
|
||||
newI.drawTriangles([graphics.ShaderImageNum]*Image{i}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false, true)
|
||||
}
|
||||
|
||||
newI.moveTo(i)
|
||||
@ -402,13 +402,13 @@ func (i *Image) processSrc(src *Image) {
|
||||
// 5: Color G
|
||||
// 6: Color B
|
||||
// 7: Color Y
|
||||
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, subimageOffsets [graphics.ShaderImageNum - 1][2]float32, shader *Shader, uniforms []interface{}) {
|
||||
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, subimageOffsets [graphics.ShaderImageNum - 1][2]float32, shader *Shader, uniforms []interface{}, evenOdd bool) {
|
||||
backendsM.Lock()
|
||||
defer backendsM.Unlock()
|
||||
i.drawTriangles(srcs, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, subimageOffsets, shader, uniforms, false)
|
||||
i.drawTriangles(srcs, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, subimageOffsets, shader, uniforms, evenOdd, false)
|
||||
}
|
||||
|
||||
func (i *Image) drawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, subimageOffsets [graphics.ShaderImageNum - 1][2]float32, shader *Shader, uniforms []interface{}, keepOnAtlas bool) {
|
||||
func (i *Image) drawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, subimageOffsets [graphics.ShaderImageNum - 1][2]float32, shader *Shader, uniforms []interface{}, evenOdd bool, keepOnAtlas bool) {
|
||||
if i.disposed {
|
||||
panic("atlas: the drawing target image must not be disposed (DrawTriangles)")
|
||||
}
|
||||
@ -487,7 +487,7 @@ func (i *Image) drawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f
|
||||
}
|
||||
}
|
||||
|
||||
i.backend.restorable.DrawTriangles(imgs, offsets, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, s, uniforms)
|
||||
i.backend.restorable.DrawTriangles(imgs, offsets, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, s, uniforms, evenOdd)
|
||||
|
||||
for _, src := range srcs {
|
||||
if src == nil {
|
||||
|
@ -102,7 +102,7 @@ func TestEnsureIsolated(t *testing.T) {
|
||||
Width: size,
|
||||
Height: size,
|
||||
}
|
||||
img4.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
|
||||
img4.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
|
||||
want := false
|
||||
if got := img4.IsOnAtlasForTesting(); got != want {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
@ -132,7 +132,7 @@ func TestEnsureIsolated(t *testing.T) {
|
||||
|
||||
// Check further drawing doesn't cause panic.
|
||||
// This bug was fixed by 03dcd948.
|
||||
img4.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
|
||||
img4.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
|
||||
}
|
||||
|
||||
func TestReputOnAtlas(t *testing.T) {
|
||||
@ -179,7 +179,7 @@ func TestReputOnAtlas(t *testing.T) {
|
||||
Width: size,
|
||||
Height: size,
|
||||
}
|
||||
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
|
||||
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
|
||||
if got, want := img1.IsOnAtlasForTesting(), false; got != want {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
}
|
||||
@ -191,7 +191,7 @@ func TestReputOnAtlas(t *testing.T) {
|
||||
if err := PutImagesOnAtlasForTesting(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
|
||||
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
|
||||
if got, want := img1.IsOnAtlasForTesting(), false; got != want {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
}
|
||||
@ -219,7 +219,7 @@ func TestReputOnAtlas(t *testing.T) {
|
||||
}
|
||||
|
||||
// img1 is on an atlas again.
|
||||
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
|
||||
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
|
||||
if got, want := img1.IsOnAtlasForTesting(), true; got != want {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
}
|
||||
@ -243,7 +243,7 @@ func TestReputOnAtlas(t *testing.T) {
|
||||
}
|
||||
|
||||
// Use img1 as a render target again.
|
||||
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
|
||||
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
|
||||
if got, want := img1.IsOnAtlasForTesting(), false; got != want {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
}
|
||||
@ -255,7 +255,7 @@ func TestReputOnAtlas(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
img1.ReplacePixels(make([]byte, 4*size*size))
|
||||
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
|
||||
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
|
||||
if got, want := img1.IsOnAtlasForTesting(), false; got != want {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
}
|
||||
@ -265,7 +265,7 @@ func TestReputOnAtlas(t *testing.T) {
|
||||
}
|
||||
|
||||
// img1 is not on an atlas due to ReplacePixels.
|
||||
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
|
||||
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
|
||||
if got, want := img1.IsOnAtlasForTesting(), false; got != want {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
}
|
||||
@ -275,7 +275,7 @@ func TestReputOnAtlas(t *testing.T) {
|
||||
if err := PutImagesOnAtlasForTesting(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
|
||||
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
|
||||
if got, want := img3.IsOnAtlasForTesting(), false; got != want {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
}
|
||||
@ -375,7 +375,7 @@ func TestReplacePixelsAfterDrawTriangles(t *testing.T) {
|
||||
Width: w,
|
||||
Height: h,
|
||||
}
|
||||
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
|
||||
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
|
||||
dst.ReplacePixels(pix)
|
||||
|
||||
pix, err := dst.Pixels(0, 0, w, h)
|
||||
@ -423,7 +423,7 @@ func TestSmallImages(t *testing.T) {
|
||||
Width: w,
|
||||
Height: h,
|
||||
}
|
||||
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
|
||||
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
|
||||
|
||||
pix, err := dst.Pixels(0, 0, w, h)
|
||||
if err != nil {
|
||||
@ -471,7 +471,7 @@ func TestLongImages(t *testing.T) {
|
||||
Width: dstW,
|
||||
Height: dstH,
|
||||
}
|
||||
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
|
||||
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
|
||||
|
||||
pix, err := dst.Pixels(0, 0, dstW, dstH)
|
||||
if err != nil {
|
||||
@ -559,7 +559,7 @@ func TestDisposedAndReputOnAtlas(t *testing.T) {
|
||||
Width: size,
|
||||
Height: size,
|
||||
}
|
||||
src.DrawTriangles([graphics.ShaderImageNum]*Image{src2}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
|
||||
src.DrawTriangles([graphics.ShaderImageNum]*Image{src2}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
|
||||
if got, want := src.IsOnAtlasForTesting(), false; got != want {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
}
|
||||
@ -569,7 +569,7 @@ func TestDisposedAndReputOnAtlas(t *testing.T) {
|
||||
if err := PutImagesOnAtlasForTesting(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
|
||||
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
|
||||
if got, want := src.IsOnAtlasForTesting(), false; got != want {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
}
|
||||
@ -609,7 +609,7 @@ func TestImageIsNotReputOnAtlasWithoutUsingAsSource(t *testing.T) {
|
||||
}
|
||||
|
||||
// Use src2 as a rendering target, and make src2 an independent image.
|
||||
src2.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
|
||||
src2.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
|
||||
if got, want := src2.IsOnAtlasForTesting(), false; got != want {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
}
|
||||
@ -630,7 +630,7 @@ func TestImageIsNotReputOnAtlasWithoutUsingAsSource(t *testing.T) {
|
||||
if err := PutImagesOnAtlasForTesting(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src2}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
|
||||
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src2}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
|
||||
if got, want := src2.IsOnAtlasForTesting(), false; got != want {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
}
|
||||
|
@ -202,7 +202,7 @@ func (i *Image) replacePendingPixels(pix []byte, x, y, width, height int) {
|
||||
// DrawTriangles draws the src image with the given vertices.
|
||||
//
|
||||
// Copying vertices and indices is the caller's responsibility.
|
||||
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, subimageOffsets [graphics.ShaderImageNum - 1][2]float32, shader *Shader, uniforms []interface{}) {
|
||||
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, subimageOffsets [graphics.ShaderImageNum - 1][2]float32, shader *Shader, uniforms []interface{}, evenOdd bool) {
|
||||
for _, src := range srcs {
|
||||
if i == src {
|
||||
panic("buffered: Image.DrawTriangles: source images must be different from the receiver")
|
||||
@ -212,7 +212,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f
|
||||
if maybeCanAddDelayedCommand() {
|
||||
if tryAddDelayedCommand(func() error {
|
||||
// Arguments are not copied. Copying is the caller's responsibility.
|
||||
i.DrawTriangles(srcs, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, subimageOffsets, shader, uniforms)
|
||||
i.DrawTriangles(srcs, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, subimageOffsets, shader, uniforms, evenOdd)
|
||||
return nil
|
||||
}) {
|
||||
return
|
||||
@ -238,7 +238,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f
|
||||
}
|
||||
i.resolvePendingPixels(false)
|
||||
|
||||
i.img.DrawTriangles(imgs, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, subimageOffsets, s, uniforms)
|
||||
i.img.DrawTriangles(imgs, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, subimageOffsets, s, uniforms, evenOdd)
|
||||
i.invalidatePendingPixels()
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ type Graphics interface {
|
||||
//
|
||||
// * float32
|
||||
// * []float32
|
||||
DrawTriangles(dst ImageID, srcs [graphics.ShaderImageNum]ImageID, offsets [graphics.ShaderImageNum - 1][2]float32, shader ShaderID, indexLen int, indexOffset int, mode CompositeMode, colorM *affine.ColorM, filter Filter, address Address, dstRegion, srcRegion Region, uniforms []interface{}) error
|
||||
DrawTriangles(dst ImageID, srcs [graphics.ShaderImageNum]ImageID, offsets [graphics.ShaderImageNum - 1][2]float32, shader ShaderID, indexLen int, indexOffset int, mode CompositeMode, colorM *affine.ColorM, filter Filter, address Address, dstRegion, srcRegion Region, uniforms []interface{}, evenOdd bool) error
|
||||
}
|
||||
|
||||
// GraphicsNotReady represents that the graphics driver is not ready for recovering from the context lost.
|
||||
|
@ -55,7 +55,7 @@ type command interface {
|
||||
NumIndices() int
|
||||
AddNumVertices(n int)
|
||||
AddNumIndices(n int)
|
||||
CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader) bool
|
||||
CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader, evenOdd bool) bool
|
||||
}
|
||||
|
||||
type size struct {
|
||||
@ -133,7 +133,7 @@ func (q *commandQueue) appendIndices(indices []uint16, offset uint16) {
|
||||
}
|
||||
|
||||
// EnqueueDrawTrianglesCommand enqueues a drawing-image command.
|
||||
func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, vertices []float32, indices []uint16, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader, uniforms []interface{}) {
|
||||
func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, vertices []float32, indices []uint16, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader, uniforms []interface{}, evenOdd bool) {
|
||||
if len(indices) > graphics.IndicesNum {
|
||||
panic(fmt.Sprintf("graphicscommand: len(indices) must be <= graphics.IndicesNum but not at EnqueueDrawTrianglesCommand: len(indices): %d, graphics.IndicesNum: %d", len(indices), graphics.IndicesNum))
|
||||
}
|
||||
@ -167,7 +167,7 @@ func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.Sh
|
||||
// TODO: If dst is the screen, reorder the command to be the last.
|
||||
if !split && 0 < len(q.commands) {
|
||||
// TODO: Pass offsets and uniforms when merging considers the shader.
|
||||
if last := q.commands[len(q.commands)-1]; last.CanMergeWithDrawTrianglesCommand(dst, srcs, color, mode, filter, address, dstRegion, srcRegion, shader) {
|
||||
if last := q.commands[len(q.commands)-1]; last.CanMergeWithDrawTrianglesCommand(dst, srcs, color, mode, filter, address, dstRegion, srcRegion, shader, evenOdd) {
|
||||
last.AddNumVertices(len(vertices))
|
||||
last.AddNumIndices(len(indices))
|
||||
return
|
||||
@ -188,6 +188,7 @@ func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.Sh
|
||||
srcRegion: srcRegion,
|
||||
shader: shader,
|
||||
uniforms: uniforms,
|
||||
evenOdd: evenOdd,
|
||||
}
|
||||
q.commands = append(q.commands, c)
|
||||
}
|
||||
@ -319,6 +320,7 @@ type drawTrianglesCommand struct {
|
||||
srcRegion driver.Region
|
||||
shader *Shader
|
||||
uniforms []interface{}
|
||||
evenOdd bool
|
||||
}
|
||||
|
||||
func (c *drawTrianglesCommand) String() string {
|
||||
@ -428,7 +430,7 @@ func (c *drawTrianglesCommand) Exec(indexOffset int) error {
|
||||
imgs[0] = c.srcs[0].image.ID()
|
||||
}
|
||||
|
||||
return theGraphicsDriver.DrawTriangles(c.dst.image.ID(), imgs, c.offsets, shaderID, c.nindices, indexOffset, c.mode, c.color, c.filter, c.address, c.dstRegion, c.srcRegion, c.uniforms)
|
||||
return theGraphicsDriver.DrawTriangles(c.dst.image.ID(), imgs, c.offsets, shaderID, c.nindices, indexOffset, c.mode, c.color, c.filter, c.address, c.dstRegion, c.srcRegion, c.uniforms, c.evenOdd)
|
||||
}
|
||||
|
||||
func (c *drawTrianglesCommand) NumVertices() int {
|
||||
@ -449,7 +451,7 @@ func (c *drawTrianglesCommand) AddNumIndices(n int) {
|
||||
|
||||
// CanMergeWithDrawTrianglesCommand returns a boolean value indicating whether the other drawTrianglesCommand can be merged
|
||||
// with the drawTrianglesCommand c.
|
||||
func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst *Image, srcs [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader) bool {
|
||||
func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst *Image, srcs [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader, evenOdd bool) bool {
|
||||
// If a shader is used, commands are not merged.
|
||||
//
|
||||
// TODO: Merge shader commands considering uniform variables.
|
||||
@ -480,6 +482,9 @@ func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst *Image, srcs
|
||||
if c.srcRegion != srcRegion {
|
||||
return false
|
||||
}
|
||||
if c.evenOdd != evenOdd {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@ -513,7 +518,7 @@ func (c *replacePixelsCommand) AddNumVertices(n int) {
|
||||
func (c *replacePixelsCommand) AddNumIndices(n int) {
|
||||
}
|
||||
|
||||
func (c *replacePixelsCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader) bool {
|
||||
func (c *replacePixelsCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader, evenOdd bool) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@ -550,7 +555,7 @@ func (c *pixelsCommand) AddNumVertices(n int) {
|
||||
func (c *pixelsCommand) AddNumIndices(n int) {
|
||||
}
|
||||
|
||||
func (c *pixelsCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader) bool {
|
||||
func (c *pixelsCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader, evenOdd bool) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@ -583,7 +588,7 @@ func (c *disposeImageCommand) AddNumVertices(n int) {
|
||||
func (c *disposeImageCommand) AddNumIndices(n int) {
|
||||
}
|
||||
|
||||
func (c *disposeImageCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader) bool {
|
||||
func (c *disposeImageCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader, evenOdd bool) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@ -616,7 +621,7 @@ func (c *disposeShaderCommand) AddNumVertices(n int) {
|
||||
func (c *disposeShaderCommand) AddNumIndices(n int) {
|
||||
}
|
||||
|
||||
func (c *disposeShaderCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader) bool {
|
||||
func (c *disposeShaderCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader, evenOdd bool) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@ -655,7 +660,7 @@ func (c *newImageCommand) AddNumVertices(n int) {
|
||||
func (c *newImageCommand) AddNumIndices(n int) {
|
||||
}
|
||||
|
||||
func (c *newImageCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader) bool {
|
||||
func (c *newImageCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader, evenOdd bool) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@ -691,7 +696,7 @@ func (c *newScreenFramebufferImageCommand) AddNumVertices(n int) {
|
||||
func (c *newScreenFramebufferImageCommand) AddNumIndices(n int) {
|
||||
}
|
||||
|
||||
func (c *newScreenFramebufferImageCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader) bool {
|
||||
func (c *newScreenFramebufferImageCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader, copmlex bool) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@ -726,7 +731,7 @@ func (c *newShaderCommand) AddNumVertices(n int) {
|
||||
func (c *newShaderCommand) AddNumIndices(n int) {
|
||||
}
|
||||
|
||||
func (c *newShaderCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader) bool {
|
||||
func (c *newShaderCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader, evenOdd bool) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -139,7 +139,7 @@ func (i *Image) InternalSize() (int, int) {
|
||||
//
|
||||
// If the source image is not specified, i.e., src is nil and there is no image in the uniform variables, the
|
||||
// elements for the source image are not used.
|
||||
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, vertices []float32, indices []uint16, clr *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader, uniforms []interface{}) {
|
||||
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, vertices []float32, indices []uint16, clr *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader, uniforms []interface{}, evenOdd bool) {
|
||||
if shader == nil {
|
||||
// Fast path for rendering without a shader (#1355).
|
||||
img := srcs[0]
|
||||
@ -160,7 +160,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [gra
|
||||
}
|
||||
i.resolveBufferedReplacePixels()
|
||||
|
||||
theCommandQueue.EnqueueDrawTrianglesCommand(i, srcs, offsets, vertices, indices, clr, mode, filter, address, dstRegion, srcRegion, shader, uniforms)
|
||||
theCommandQueue.EnqueueDrawTrianglesCommand(i, srcs, offsets, vertices, indices, clr, mode, filter, address, dstRegion, srcRegion, shader, uniforms, evenOdd)
|
||||
}
|
||||
|
||||
// Pixels returns the image's pixels.
|
||||
|
@ -50,7 +50,7 @@ func TestClear(t *testing.T) {
|
||||
Width: w,
|
||||
Height: h,
|
||||
}
|
||||
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
|
||||
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
|
||||
|
||||
pix, err := dst.Pixels()
|
||||
if err != nil {
|
||||
@ -81,8 +81,8 @@ func TestReplacePixelsPartAfterDrawTriangles(t *testing.T) {
|
||||
Width: w,
|
||||
Height: h,
|
||||
}
|
||||
dst.DrawTriangles([graphics.ShaderImageNum]*Image{clr}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
|
||||
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
|
||||
dst.DrawTriangles([graphics.ShaderImageNum]*Image{clr}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
|
||||
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
|
||||
dst.ReplacePixels(make([]byte, 4), 0, 0, 1, 1)
|
||||
|
||||
// TODO: Check the result.
|
||||
@ -100,11 +100,11 @@ func TestShader(t *testing.T) {
|
||||
Width: w,
|
||||
Height: h,
|
||||
}
|
||||
dst.DrawTriangles([graphics.ShaderImageNum]*Image{clr}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
|
||||
dst.DrawTriangles([graphics.ShaderImageNum]*Image{clr}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
|
||||
|
||||
ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff)
|
||||
s := NewShader(&ir)
|
||||
dst.DrawTriangles([graphics.ShaderImageNum]*Image{}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, s, nil)
|
||||
dst.DrawTriangles([graphics.ShaderImageNum]*Image{}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, s, nil, false)
|
||||
|
||||
pix, err := dst.Pixels()
|
||||
if err != nil {
|
||||
|
@ -296,11 +296,12 @@ FragmentShaderFunc(0, FILTER_SCREEN, ADDRESS_UNSAFE)
|
||||
`
|
||||
|
||||
type rpsKey struct {
|
||||
useColorM bool
|
||||
filter driver.Filter
|
||||
address driver.Address
|
||||
compositeMode driver.CompositeMode
|
||||
screen bool
|
||||
useColorM bool
|
||||
filter driver.Filter
|
||||
address driver.Address
|
||||
compositeMode driver.CompositeMode
|
||||
colorWriteMask bool
|
||||
screen bool
|
||||
}
|
||||
|
||||
type Graphics struct {
|
||||
@ -313,7 +314,9 @@ type Graphics struct {
|
||||
rce mtl.RenderCommandEncoder
|
||||
|
||||
screenDrawable ca.MetalDrawable
|
||||
|
||||
lastDstTexture mtl.Texture
|
||||
lastStencilUse bool
|
||||
|
||||
vb mtl.Buffer
|
||||
ib mtl.Buffer
|
||||
@ -334,6 +337,18 @@ type Graphics struct {
|
||||
pool unsafe.Pointer
|
||||
}
|
||||
|
||||
type stencilMode int
|
||||
|
||||
const (
|
||||
prepareStencil stencilMode = iota
|
||||
drawWithStencil
|
||||
noStencil
|
||||
)
|
||||
|
||||
func (s stencilMode) useStencil() bool {
|
||||
return s != noStencil
|
||||
}
|
||||
|
||||
var theGraphics Graphics
|
||||
|
||||
func Get() *Graphics {
|
||||
@ -539,8 +554,9 @@ func (g *Graphics) Reset() error {
|
||||
return err
|
||||
}
|
||||
rpld := mtl.RenderPipelineDescriptor{
|
||||
VertexFunction: vs,
|
||||
FragmentFunction: fs,
|
||||
VertexFunction: vs,
|
||||
FragmentFunction: fs,
|
||||
StencilAttachmentPixelFormat: mtl.PixelFormatStencil8,
|
||||
}
|
||||
rpld.ColorAttachments[0].PixelFormat = g.view.colorPixelFormat()
|
||||
rpld.ColorAttachments[0].BlendingEnabled = true
|
||||
@ -548,6 +564,7 @@ func (g *Graphics) Reset() error {
|
||||
rpld.ColorAttachments[0].DestinationRGBBlendFactor = mtl.BlendFactorZero
|
||||
rpld.ColorAttachments[0].SourceAlphaBlendFactor = mtl.BlendFactorOne
|
||||
rpld.ColorAttachments[0].SourceRGBBlendFactor = mtl.BlendFactorOne
|
||||
rpld.ColorAttachments[0].WriteMask = mtl.ColorWriteMaskAll
|
||||
rps, err := g.view.getMTLDevice().MakeRenderPipelineState(rpld)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -566,42 +583,51 @@ func (g *Graphics) Reset() error {
|
||||
driver.FilterLinear,
|
||||
} {
|
||||
for c := driver.CompositeModeSourceOver; c <= driver.CompositeModeMax; c++ {
|
||||
cmi := 0
|
||||
if cm {
|
||||
cmi = 1
|
||||
}
|
||||
fs, err := lib.MakeFunction(fmt.Sprintf("FragmentShader_%d_%d_%d", cmi, f, a))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rpld := mtl.RenderPipelineDescriptor{
|
||||
VertexFunction: vs,
|
||||
FragmentFunction: fs,
|
||||
}
|
||||
for _, cwm := range []bool{false, true} {
|
||||
cmi := 0
|
||||
if cm {
|
||||
cmi = 1
|
||||
}
|
||||
fs, err := lib.MakeFunction(fmt.Sprintf("FragmentShader_%d_%d_%d", cmi, f, a))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rpld := mtl.RenderPipelineDescriptor{
|
||||
VertexFunction: vs,
|
||||
FragmentFunction: fs,
|
||||
StencilAttachmentPixelFormat: mtl.PixelFormatStencil8,
|
||||
}
|
||||
|
||||
pix := mtl.PixelFormatRGBA8UNorm
|
||||
if screen {
|
||||
pix = g.view.colorPixelFormat()
|
||||
}
|
||||
rpld.ColorAttachments[0].PixelFormat = pix
|
||||
rpld.ColorAttachments[0].BlendingEnabled = true
|
||||
pix := mtl.PixelFormatRGBA8UNorm
|
||||
if screen {
|
||||
pix = g.view.colorPixelFormat()
|
||||
}
|
||||
rpld.ColorAttachments[0].PixelFormat = pix
|
||||
rpld.ColorAttachments[0].BlendingEnabled = true
|
||||
|
||||
src, dst := c.Operations()
|
||||
rpld.ColorAttachments[0].DestinationAlphaBlendFactor = operationToBlendFactor(dst)
|
||||
rpld.ColorAttachments[0].DestinationRGBBlendFactor = operationToBlendFactor(dst)
|
||||
rpld.ColorAttachments[0].SourceAlphaBlendFactor = operationToBlendFactor(src)
|
||||
rpld.ColorAttachments[0].SourceRGBBlendFactor = operationToBlendFactor(src)
|
||||
rps, err := g.view.getMTLDevice().MakeRenderPipelineState(rpld)
|
||||
if err != nil {
|
||||
return err
|
||||
src, dst := c.Operations()
|
||||
rpld.ColorAttachments[0].DestinationAlphaBlendFactor = operationToBlendFactor(dst)
|
||||
rpld.ColorAttachments[0].DestinationRGBBlendFactor = operationToBlendFactor(dst)
|
||||
rpld.ColorAttachments[0].SourceAlphaBlendFactor = operationToBlendFactor(src)
|
||||
rpld.ColorAttachments[0].SourceRGBBlendFactor = operationToBlendFactor(src)
|
||||
if cwm {
|
||||
rpld.ColorAttachments[0].WriteMask = mtl.ColorWriteMaskAll
|
||||
} else {
|
||||
rpld.ColorAttachments[0].WriteMask = mtl.ColorWriteMaskNone
|
||||
}
|
||||
rps, err := g.view.getMTLDevice().MakeRenderPipelineState(rpld)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.rpss[rpsKey{
|
||||
screen: screen,
|
||||
useColorM: cm,
|
||||
filter: f,
|
||||
address: a,
|
||||
compositeMode: c,
|
||||
colorWriteMask: cwm,
|
||||
}] = rps
|
||||
}
|
||||
g.rpss[rpsKey{
|
||||
screen: screen,
|
||||
useColorM: cm,
|
||||
filter: f,
|
||||
address: a,
|
||||
compositeMode: c,
|
||||
}] = rps
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -619,12 +645,14 @@ func (g *Graphics) flushRenderCommandEncoderIfNeeded() {
|
||||
g.rce.EndEncoding()
|
||||
g.rce = mtl.RenderCommandEncoder{}
|
||||
g.lastDstTexture = mtl.Texture{}
|
||||
g.lastStencilUse = false
|
||||
}
|
||||
|
||||
func (g *Graphics) draw(rps mtl.RenderPipelineState, dst *Image, dstRegion driver.Region, srcs [graphics.ShaderImageNum]*Image, indexLen int, indexOffset int, uniforms []interface{}) error {
|
||||
if g.lastDstTexture != dst.mtlTexture() {
|
||||
func (g *Graphics) draw(rps mtl.RenderPipelineState, dst *Image, dstRegion driver.Region, srcs [graphics.ShaderImageNum]*Image, indexLen int, indexOffset int, uniforms []interface{}, stencilMode stencilMode) error {
|
||||
if g.lastDstTexture != dst.mtlTexture() || g.lastStencilUse != stencilMode.useStencil() {
|
||||
g.flushRenderCommandEncoderIfNeeded()
|
||||
}
|
||||
|
||||
if g.rce == (mtl.RenderCommandEncoder{}) {
|
||||
rpd := mtl.RenderPassDescriptor{}
|
||||
// Even though the destination pixels are not used, mtl.LoadActionDontCare might cause glitches
|
||||
@ -640,11 +668,19 @@ func (g *Graphics) draw(rps mtl.RenderPipelineState, dst *Image, dstRegion drive
|
||||
rpd.ColorAttachments[0].Texture = t
|
||||
rpd.ColorAttachments[0].ClearColor = mtl.ClearColor{}
|
||||
|
||||
if stencilMode.useStencil() {
|
||||
dst.ensureStencil()
|
||||
rpd.StencilAttachment.LoadAction = mtl.LoadActionClear
|
||||
rpd.StencilAttachment.StoreAction = mtl.StoreActionDontCare
|
||||
rpd.StencilAttachment.Texture = dst.stencil
|
||||
}
|
||||
|
||||
if g.cb == (mtl.CommandBuffer{}) {
|
||||
g.cb = g.cq.MakeCommandBuffer()
|
||||
}
|
||||
g.rce = g.cb.MakeRenderCommandEncoder(rpd)
|
||||
}
|
||||
g.lastStencilUse = stencilMode.useStencil()
|
||||
|
||||
g.rce.SetRenderPipelineState(rps)
|
||||
|
||||
@ -687,11 +723,51 @@ func (g *Graphics) draw(rps mtl.RenderPipelineState, dst *Image, dstRegion drive
|
||||
g.rce.SetFragmentTexture(mtl.Texture{}, i)
|
||||
}
|
||||
}
|
||||
|
||||
// The stencil reference value is always 0 (default).
|
||||
switch stencilMode {
|
||||
case prepareStencil:
|
||||
desc := mtl.DepthStencilDescriptor{
|
||||
BackFaceStencil: mtl.StencilDescriptor{
|
||||
StencilFailureOperation: mtl.StencilOperationKeep,
|
||||
DepthFailureOperation: mtl.StencilOperationKeep,
|
||||
DepthStencilPassOperation: mtl.StencilOperationInvert,
|
||||
StencilCompareFunction: mtl.CompareFunctionAlways,
|
||||
},
|
||||
FrontFaceStencil: mtl.StencilDescriptor{
|
||||
StencilFailureOperation: mtl.StencilOperationKeep,
|
||||
DepthFailureOperation: mtl.StencilOperationKeep,
|
||||
DepthStencilPassOperation: mtl.StencilOperationInvert,
|
||||
StencilCompareFunction: mtl.CompareFunctionAlways,
|
||||
},
|
||||
}
|
||||
g.rce.SetDepthStencilState(g.view.getMTLDevice().MakeDepthStencilState(desc))
|
||||
case drawWithStencil:
|
||||
desc := mtl.DepthStencilDescriptor{
|
||||
BackFaceStencil: mtl.StencilDescriptor{
|
||||
StencilFailureOperation: mtl.StencilOperationZero,
|
||||
DepthFailureOperation: mtl.StencilOperationZero,
|
||||
DepthStencilPassOperation: mtl.StencilOperationZero,
|
||||
StencilCompareFunction: mtl.CompareFunctionNotEqual,
|
||||
},
|
||||
FrontFaceStencil: mtl.StencilDescriptor{
|
||||
StencilFailureOperation: mtl.StencilOperationZero,
|
||||
DepthFailureOperation: mtl.StencilOperationZero,
|
||||
DepthStencilPassOperation: mtl.StencilOperationZero,
|
||||
StencilCompareFunction: mtl.CompareFunctionNotEqual,
|
||||
},
|
||||
}
|
||||
g.rce.SetDepthStencilState(g.view.getMTLDevice().MakeDepthStencilState(desc))
|
||||
case noStencil:
|
||||
g.rce.SetDepthStencilState(mtl.DepthStencilState{})
|
||||
}
|
||||
|
||||
g.rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, indexLen, mtl.IndexTypeUInt16, g.ib, indexOffset*2)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Graphics) DrawTriangles(dstID driver.ImageID, srcIDs [graphics.ShaderImageNum]driver.ImageID, offsets [graphics.ShaderImageNum - 1][2]float32, shaderID driver.ShaderID, indexLen int, indexOffset int, mode driver.CompositeMode, colorM *affine.ColorM, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, uniforms []interface{}) error {
|
||||
func (g *Graphics) DrawTriangles(dstID driver.ImageID, srcIDs [graphics.ShaderImageNum]driver.ImageID, offsets [graphics.ShaderImageNum - 1][2]float32, shaderID driver.ShaderID, indexLen int, indexOffset int, mode driver.CompositeMode, colorM *affine.ColorM, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, uniforms []interface{}, evenOdd bool) error {
|
||||
dst := g.images[dstID]
|
||||
|
||||
if dst.screen {
|
||||
@ -703,18 +779,28 @@ func (g *Graphics) DrawTriangles(dstID driver.ImageID, srcIDs [graphics.ShaderIm
|
||||
srcs[i] = g.images[srcID]
|
||||
}
|
||||
|
||||
var rps mtl.RenderPipelineState
|
||||
var rpss [2]mtl.RenderPipelineState
|
||||
var uniformVars []interface{}
|
||||
if shaderID == driver.InvalidShaderID {
|
||||
if dst.screen && filter == driver.FilterScreen {
|
||||
rps = g.screenRPS
|
||||
rpss[1] = g.screenRPS
|
||||
} else {
|
||||
rps = g.rpss[rpsKey{
|
||||
screen: dst.screen,
|
||||
useColorM: colorM != nil,
|
||||
filter: filter,
|
||||
address: address,
|
||||
compositeMode: mode,
|
||||
useColorM := colorM != nil
|
||||
rpss[0] = g.rpss[rpsKey{
|
||||
screen: dst.screen,
|
||||
useColorM: useColorM,
|
||||
filter: filter,
|
||||
address: address,
|
||||
compositeMode: mode,
|
||||
colorWriteMask: false,
|
||||
}]
|
||||
rpss[1] = g.rpss[rpsKey{
|
||||
screen: dst.screen,
|
||||
useColorM: useColorM,
|
||||
filter: filter,
|
||||
address: address,
|
||||
compositeMode: mode,
|
||||
colorWriteMask: true,
|
||||
}]
|
||||
}
|
||||
|
||||
@ -745,7 +831,11 @@ func (g *Graphics) DrawTriangles(dstID driver.ImageID, srcIDs [graphics.ShaderIm
|
||||
}
|
||||
} else {
|
||||
var err error
|
||||
rps, err = g.shaders[shaderID].RenderPipelineState(g.view.getMTLDevice(), mode)
|
||||
rpss[0], err = g.shaders[shaderID].RenderPipelineState(g.view.getMTLDevice(), mode, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rpss[1], err = g.shaders[shaderID].RenderPipelineState(g.view.getMTLDevice(), mode, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -798,8 +888,17 @@ func (g *Graphics) DrawTriangles(dstID driver.ImageID, srcIDs [graphics.ShaderIm
|
||||
}
|
||||
}
|
||||
|
||||
if err := g.draw(rps, dst, dstRegion, srcs, indexLen, indexOffset, uniformVars); err != nil {
|
||||
return err
|
||||
if evenOdd {
|
||||
if err := g.draw(rpss[0], dst, dstRegion, srcs, indexLen, indexOffset, uniformVars, prepareStencil); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := g.draw(rpss[1], dst, dstRegion, srcs, indexLen, indexOffset, uniformVars, drawWithStencil); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := g.draw(rpss[1], dst, dstRegion, srcs, indexLen, indexOffset, uniformVars, noStencil); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -889,6 +988,7 @@ type Image struct {
|
||||
height int
|
||||
screen bool
|
||||
texture mtl.Texture
|
||||
stencil mtl.Texture
|
||||
}
|
||||
|
||||
func (i *Image) ID() driver.ImageID {
|
||||
@ -903,6 +1003,10 @@ func (i *Image) internalSize() (int, int) {
|
||||
}
|
||||
|
||||
func (i *Image) Dispose() {
|
||||
if i.stencil != (mtl.Texture{}) {
|
||||
i.stencil.Release()
|
||||
i.stencil = mtl.Texture{}
|
||||
}
|
||||
if i.texture != (mtl.Texture{}) {
|
||||
i.texture.Release()
|
||||
i.texture = mtl.Texture{}
|
||||
@ -1022,3 +1126,19 @@ func (i *Image) mtlTexture() mtl.Texture {
|
||||
}
|
||||
return i.texture
|
||||
}
|
||||
|
||||
func (i *Image) ensureStencil() {
|
||||
if i.stencil != (mtl.Texture{}) {
|
||||
return
|
||||
}
|
||||
|
||||
td := mtl.TextureDescriptor{
|
||||
TextureType: mtl.TextureType2D,
|
||||
PixelFormat: mtl.PixelFormatStencil8,
|
||||
Width: graphics.InternalImageSize(i.width),
|
||||
Height: graphics.InternalImageSize(i.height),
|
||||
StorageMode: mtl.StorageModePrivate,
|
||||
Usage: mtl.TextureUsageRenderTarget,
|
||||
}
|
||||
i.stencil = i.graphics.view.getMTLDevice().MakeTexture(td)
|
||||
}
|
||||
|
@ -26,20 +26,25 @@ import (
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/shaderir/metal"
|
||||
)
|
||||
|
||||
type shaderRpsKey struct {
|
||||
compositeMode driver.CompositeMode
|
||||
colorWriteMask bool
|
||||
}
|
||||
|
||||
type Shader struct {
|
||||
id driver.ShaderID
|
||||
|
||||
ir *shaderir.Program
|
||||
fs mtl.Function
|
||||
vs mtl.Function
|
||||
rpss map[driver.CompositeMode]mtl.RenderPipelineState
|
||||
rpss map[shaderRpsKey]mtl.RenderPipelineState
|
||||
}
|
||||
|
||||
func newShader(device mtl.Device, id driver.ShaderID, program *shaderir.Program) (*Shader, error) {
|
||||
s := &Shader{
|
||||
id: id,
|
||||
ir: program,
|
||||
rpss: map[driver.CompositeMode]mtl.RenderPipelineState{},
|
||||
rpss: map[shaderRpsKey]mtl.RenderPipelineState{},
|
||||
}
|
||||
if err := s.init(device); err != nil {
|
||||
return nil, err
|
||||
@ -83,31 +88,43 @@ func (s *Shader) init(device mtl.Device) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Shader) RenderPipelineState(device mtl.Device, c driver.CompositeMode) (mtl.RenderPipelineState, error) {
|
||||
if rps, ok := s.rpss[c]; ok {
|
||||
func (s *Shader) RenderPipelineState(device mtl.Device, compositeMode driver.CompositeMode, colorWriteMask bool) (mtl.RenderPipelineState, error) {
|
||||
if rps, ok := s.rpss[shaderRpsKey{
|
||||
compositeMode: compositeMode,
|
||||
colorWriteMask: colorWriteMask,
|
||||
}]; ok {
|
||||
return rps, nil
|
||||
}
|
||||
|
||||
rpld := mtl.RenderPipelineDescriptor{
|
||||
VertexFunction: s.vs,
|
||||
FragmentFunction: s.fs,
|
||||
VertexFunction: s.vs,
|
||||
FragmentFunction: s.fs,
|
||||
StencilAttachmentPixelFormat: mtl.PixelFormatStencil8,
|
||||
}
|
||||
|
||||
// TODO: For the precise pixel format, whether the render target is the screen or not must be considered.
|
||||
rpld.ColorAttachments[0].PixelFormat = mtl.PixelFormatRGBA8UNorm
|
||||
rpld.ColorAttachments[0].BlendingEnabled = true
|
||||
|
||||
src, dst := c.Operations()
|
||||
src, dst := compositeMode.Operations()
|
||||
rpld.ColorAttachments[0].DestinationAlphaBlendFactor = operationToBlendFactor(dst)
|
||||
rpld.ColorAttachments[0].DestinationRGBBlendFactor = operationToBlendFactor(dst)
|
||||
rpld.ColorAttachments[0].SourceAlphaBlendFactor = operationToBlendFactor(src)
|
||||
rpld.ColorAttachments[0].SourceRGBBlendFactor = operationToBlendFactor(src)
|
||||
if colorWriteMask {
|
||||
rpld.ColorAttachments[0].WriteMask = mtl.ColorWriteMaskAll
|
||||
} else {
|
||||
rpld.ColorAttachments[0].WriteMask = mtl.ColorWriteMaskNone
|
||||
}
|
||||
|
||||
rps, err := device.MakeRenderPipelineState(rpld)
|
||||
if err != nil {
|
||||
return mtl.RenderPipelineState{}, err
|
||||
}
|
||||
|
||||
s.rpss[c] = rps
|
||||
s.rpss[shaderRpsKey{
|
||||
compositeMode: compositeMode,
|
||||
colorWriteMask: colorWriteMask,
|
||||
}] = rps
|
||||
return rps, nil
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ type context struct {
|
||||
screenFramebuffer framebufferNative // This might not be the default frame buffer '0' (e.g. iOS).
|
||||
lastFramebuffer framebufferNative
|
||||
lastTexture textureNative
|
||||
lastRenderbuffer renderbufferNative
|
||||
lastViewportWidth int
|
||||
lastViewportHeight int
|
||||
lastCompositeMode driver.CompositeMode
|
||||
@ -68,6 +69,14 @@ func (c *context) bindTexture(t textureNative) {
|
||||
c.lastTexture = t
|
||||
}
|
||||
|
||||
func (c *context) bindRenderbuffer(r renderbufferNative) {
|
||||
if c.lastRenderbuffer.equal(r) {
|
||||
return
|
||||
}
|
||||
c.bindRenderbufferImpl(r)
|
||||
c.lastRenderbuffer = r
|
||||
}
|
||||
|
||||
func (c *context) bindFramebuffer(f framebufferNative) {
|
||||
if c.lastFramebuffer.equal(f) {
|
||||
return
|
||||
|
@ -29,17 +29,22 @@ import (
|
||||
)
|
||||
|
||||
type (
|
||||
textureNative uint32
|
||||
framebufferNative uint32
|
||||
shader uint32
|
||||
program uint32
|
||||
buffer uint32
|
||||
textureNative uint32
|
||||
renderbufferNative uint32
|
||||
framebufferNative uint32
|
||||
shader uint32
|
||||
program uint32
|
||||
buffer uint32
|
||||
)
|
||||
|
||||
func (t textureNative) equal(rhs textureNative) bool {
|
||||
return t == rhs
|
||||
}
|
||||
|
||||
func (r renderbufferNative) equal(rhs renderbufferNative) bool {
|
||||
return r == rhs
|
||||
}
|
||||
|
||||
func (f framebufferNative) equal(rhs framebufferNative) bool {
|
||||
return f == rhs
|
||||
}
|
||||
@ -198,6 +203,38 @@ func (c *context) isTexture(t textureNative) bool {
|
||||
panic("opengl: isTexture is not implemented")
|
||||
}
|
||||
|
||||
func (c *context) newRenderbuffer(width, height int) (renderbufferNative, error) {
|
||||
var r uint32
|
||||
gl.GenRenderbuffersEXT(1, &r)
|
||||
if r <= 0 {
|
||||
return 0, errors.New("opengl: creating renderbuffer failed")
|
||||
}
|
||||
|
||||
renderbuffer := renderbufferNative(r)
|
||||
c.bindRenderbuffer(renderbuffer)
|
||||
|
||||
// GL_STENCIL_INDEX8 might not be available with OpenGL 2.1.
|
||||
// https://www.khronos.org/opengl/wiki/Image_Format
|
||||
gl.RenderbufferStorageEXT(gl.RENDERBUFFER, gl.DEPTH24_STENCIL8, int32(width), int32(height))
|
||||
|
||||
return renderbuffer, nil
|
||||
}
|
||||
|
||||
func (c *context) bindRenderbufferImpl(r renderbufferNative) {
|
||||
gl.BindRenderbufferEXT(gl.RENDERBUFFER, uint32(r))
|
||||
}
|
||||
|
||||
func (c *context) deleteRenderbuffer(r renderbufferNative) {
|
||||
rr := uint32(r)
|
||||
if !gl.IsRenderbufferEXT(rr) {
|
||||
return
|
||||
}
|
||||
if c.lastRenderbuffer.equal(r) {
|
||||
c.lastRenderbuffer = 0
|
||||
}
|
||||
gl.DeleteRenderbuffersEXT(1, &rr)
|
||||
}
|
||||
|
||||
func (c *context) newFramebuffer(texture textureNative) (framebufferNative, error) {
|
||||
var f uint32
|
||||
gl.GenFramebuffersEXT(1, &f)
|
||||
@ -220,6 +257,17 @@ func (c *context) newFramebuffer(texture textureNative) (framebufferNative, erro
|
||||
return framebufferNative(f), nil
|
||||
}
|
||||
|
||||
func (c *context) bindStencilBuffer(f framebufferNative, r renderbufferNative) error {
|
||||
c.bindFramebuffer(f)
|
||||
|
||||
gl.FramebufferRenderbufferEXT(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, uint32(r))
|
||||
if s := gl.CheckFramebufferStatusEXT(gl.FRAMEBUFFER); s != gl.FRAMEBUFFER_COMPLETE {
|
||||
return errors.New(fmt.Sprintf("opengl: glFramebufferRenderbuffer failed: %d", s))
|
||||
}
|
||||
gl.Clear(gl.STENCIL_BUFFER_BIT)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *context) setViewportImpl(width, height int) {
|
||||
gl.Viewport(0, 0, int32(width), int32(height))
|
||||
}
|
||||
@ -492,3 +540,23 @@ func (c *context) getBufferSubData(buffer buffer, width, height int) []byte {
|
||||
gl.BindBuffer(gl.PIXEL_UNPACK_BUFFER, 0)
|
||||
return pixels
|
||||
}
|
||||
|
||||
func (c *context) enableStencilTest() {
|
||||
gl.Enable(gl.STENCIL_TEST)
|
||||
}
|
||||
|
||||
func (c *context) disableStencilTest() {
|
||||
gl.Disable(gl.STENCIL_TEST)
|
||||
}
|
||||
|
||||
func (c *context) beginStencilWithEvenOddRule() {
|
||||
gl.StencilFunc(gl.ALWAYS, 0x00, 0xff)
|
||||
gl.StencilOp(gl.KEEP, gl.KEEP, gl.INVERT)
|
||||
gl.ColorMask(false, false, false, false)
|
||||
}
|
||||
|
||||
func (c *context) endStencilWithEvenOddRule() {
|
||||
gl.StencilFunc(gl.NOTEQUAL, 0x00, 0xff)
|
||||
gl.StencilOp(gl.ZERO, gl.ZERO, gl.ZERO)
|
||||
gl.ColorMask(true, true, true, true)
|
||||
}
|
||||
|
@ -26,11 +26,12 @@ import (
|
||||
)
|
||||
|
||||
type (
|
||||
textureNative js.Value
|
||||
framebufferNative js.Value
|
||||
shader js.Value
|
||||
buffer js.Value
|
||||
uniformLocation js.Value
|
||||
textureNative js.Value
|
||||
renderbufferNative js.Value
|
||||
framebufferNative js.Value
|
||||
shader js.Value
|
||||
buffer js.Value
|
||||
uniformLocation js.Value
|
||||
|
||||
attribLocation int
|
||||
programID int
|
||||
@ -44,6 +45,10 @@ func (t textureNative) equal(rhs textureNative) bool {
|
||||
return js.Value(t).Equal(js.Value(rhs))
|
||||
}
|
||||
|
||||
func (r renderbufferNative) equal(rhs renderbufferNative) bool {
|
||||
return js.Value(r).Equal(js.Value(rhs))
|
||||
}
|
||||
|
||||
func (f framebufferNative) equal(rhs framebufferNative) bool {
|
||||
return js.Value(f).Equal(js.Value(rhs))
|
||||
}
|
||||
@ -100,6 +105,7 @@ func (c *context) initGL() {
|
||||
attr := js.Global().Get("Object").New()
|
||||
attr.Set("alpha", true)
|
||||
attr.Set("premultipliedAlpha", true)
|
||||
attr.Set("stencil", true)
|
||||
|
||||
if isWebGL2Available {
|
||||
gl = canvas.Call("getContext", "webgl2", attr)
|
||||
@ -165,7 +171,7 @@ func (c *context) newTexture(width, height int) (textureNative, error) {
|
||||
gl := c.gl
|
||||
t := gl.createTexture.Invoke()
|
||||
if !t.Truthy() {
|
||||
return textureNative(js.Null()), errors.New("opengl: glGenTexture failed")
|
||||
return textureNative(js.Null()), errors.New("opengl: createTexture failed")
|
||||
}
|
||||
c.bindTexture(textureNative(t))
|
||||
|
||||
@ -240,6 +246,37 @@ func (c *context) isTexture(t textureNative) bool {
|
||||
panic("opengl: isTexture is not implemented")
|
||||
}
|
||||
|
||||
func (c *context) newRenderbuffer(width, height int) (renderbufferNative, error) {
|
||||
gl := c.gl
|
||||
r := gl.createRenderbuffer.Invoke()
|
||||
if !r.Truthy() {
|
||||
return renderbufferNative(js.Null()), errors.New("opengl: createRenderbuffer failed")
|
||||
}
|
||||
|
||||
c.bindRenderbuffer(renderbufferNative(r))
|
||||
// TODO: Is STENCIL_INDEX8 portable?
|
||||
// https://stackoverflow.com/questions/11084961/binding-a-stencil-render-buffer-to-a-frame-buffer-in-opengl
|
||||
gl.renderbufferStorage.Invoke(gles.RENDERBUFFER, gles.STENCIL_INDEX8, width, height)
|
||||
|
||||
return renderbufferNative(r), nil
|
||||
}
|
||||
|
||||
func (c *context) bindRenderbufferImpl(r renderbufferNative) {
|
||||
gl := c.gl
|
||||
gl.bindRenderbuffer.Invoke(gles.RENDERBUFFER, js.Value(r))
|
||||
}
|
||||
|
||||
func (c *context) deleteRenderbuffer(r renderbufferNative) {
|
||||
gl := c.gl
|
||||
if !gl.isRenderbuffer.Invoke(js.Value(r)).Bool() {
|
||||
return
|
||||
}
|
||||
if c.lastRenderbuffer.equal(r) {
|
||||
c.lastRenderbuffer = renderbufferNative(js.Null())
|
||||
}
|
||||
gl.deleteRenderbuffer.Invoke(js.Value(r))
|
||||
}
|
||||
|
||||
func (c *context) newFramebuffer(t textureNative) (framebufferNative, error) {
|
||||
gl := c.gl
|
||||
f := gl.createFramebuffer.Invoke()
|
||||
@ -253,6 +290,18 @@ func (c *context) newFramebuffer(t textureNative) (framebufferNative, error) {
|
||||
return framebufferNative(f), nil
|
||||
}
|
||||
|
||||
func (c *context) bindStencilBuffer(f framebufferNative, r renderbufferNative) error {
|
||||
gl := c.gl
|
||||
c.bindFramebuffer(f)
|
||||
|
||||
gl.framebufferRenderbuffer.Invoke(gles.FRAMEBUFFER, gles.STENCIL_ATTACHMENT, gles.RENDERBUFFER, js.Value(r))
|
||||
if s := gl.checkFramebufferStatus.Invoke(gles.FRAMEBUFFER); s.Int() != gles.FRAMEBUFFER_COMPLETE {
|
||||
return errors.New(fmt.Sprintf("opengl: framebufferRenderbuffer failed: %d", s.Int()))
|
||||
}
|
||||
gl.clear.Invoke(gles.STENCIL_BUFFER_BIT)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *context) setViewportImpl(width, height int) {
|
||||
gl := c.gl
|
||||
gl.viewport.Invoke(0, 0, width, height)
|
||||
@ -598,3 +647,27 @@ func (c *context) getBufferSubData(buffer buffer, width, height int) []byte {
|
||||
gl.bindBuffer.Invoke(gles.PIXEL_UNPACK_BUFFER, nil)
|
||||
return jsutil.Uint8ArrayToSlice(arr, l)
|
||||
}
|
||||
|
||||
func (c *context) enableStencilTest() {
|
||||
gl := c.gl
|
||||
gl.enable.Invoke(gles.STENCIL_TEST)
|
||||
}
|
||||
|
||||
func (c *context) disableStencilTest() {
|
||||
gl := c.gl
|
||||
gl.disable.Invoke(gles.STENCIL_TEST)
|
||||
}
|
||||
|
||||
func (c *context) beginStencilWithEvenOddRule() {
|
||||
gl := c.gl
|
||||
gl.stencilFunc.Invoke(gles.ALWAYS, 0x00, 0xff)
|
||||
gl.stencilOp.Invoke(gles.KEEP, gles.KEEP, gles.INVERT)
|
||||
gl.colorMask.Invoke(false, false, false, false)
|
||||
}
|
||||
|
||||
func (c *context) endStencilWithEvenOddRule() {
|
||||
gl := c.gl
|
||||
gl.stencilFunc.Invoke(gles.NOTEQUAL, 0x00, 0xff)
|
||||
gl.stencilOp.Invoke(gles.ZERO, gles.ZERO, gles.ZERO)
|
||||
gl.colorMask.Invoke(true, true, true, true)
|
||||
}
|
||||
|
@ -27,17 +27,22 @@ import (
|
||||
)
|
||||
|
||||
type (
|
||||
textureNative uint32
|
||||
framebufferNative uint32
|
||||
shader uint32
|
||||
program uint32
|
||||
buffer uint32
|
||||
textureNative uint32
|
||||
renderbufferNative uint32
|
||||
framebufferNative uint32
|
||||
shader uint32
|
||||
program uint32
|
||||
buffer uint32
|
||||
)
|
||||
|
||||
func (t textureNative) equal(rhs textureNative) bool {
|
||||
return t == rhs
|
||||
}
|
||||
|
||||
func (r renderbufferNative) equal(rhs renderbufferNative) bool {
|
||||
return r == rhs
|
||||
}
|
||||
|
||||
func (f framebufferNative) equal(rhs framebufferNative) bool {
|
||||
return f == rhs
|
||||
}
|
||||
@ -185,6 +190,34 @@ func (c *context) isTexture(t textureNative) bool {
|
||||
return c.ctx.IsTexture(uint32(t))
|
||||
}
|
||||
|
||||
func (c *context) newRenderbuffer(width, height int) (renderbufferNative, error) {
|
||||
r := c.ctx.GenRenderbuffers(1)[0]
|
||||
if r <= 0 {
|
||||
return 0, errors.New("opengl: creating renderbuffer failed")
|
||||
}
|
||||
|
||||
renderbuffer := renderbufferNative(r)
|
||||
c.bindRenderbuffer(renderbuffer)
|
||||
|
||||
c.ctx.RenderbufferStorage(gles.RENDERBUFFER, gles.STENCIL_INDEX8, int32(width), int32(height))
|
||||
|
||||
return renderbuffer, nil
|
||||
}
|
||||
|
||||
func (c *context) bindRenderbufferImpl(r renderbufferNative) {
|
||||
c.ctx.BindRenderbuffer(gles.RENDERBUFFER, uint32(r))
|
||||
}
|
||||
|
||||
func (c *context) deleteRenderbuffer(r renderbufferNative) {
|
||||
if !c.ctx.IsRenderbuffer(uint32(r)) {
|
||||
return
|
||||
}
|
||||
if c.lastRenderbuffer.equal(r) {
|
||||
c.lastRenderbuffer = 0
|
||||
}
|
||||
c.ctx.DeleteRenderbuffers([]uint32{uint32(r)})
|
||||
}
|
||||
|
||||
func (c *context) newFramebuffer(texture textureNative) (framebufferNative, error) {
|
||||
f := c.ctx.GenFramebuffers(1)[0]
|
||||
if f <= 0 {
|
||||
@ -206,6 +239,17 @@ func (c *context) newFramebuffer(texture textureNative) (framebufferNative, erro
|
||||
return framebufferNative(f), nil
|
||||
}
|
||||
|
||||
func (c *context) bindStencilBuffer(f framebufferNative, r renderbufferNative) error {
|
||||
c.bindFramebuffer(f)
|
||||
|
||||
c.ctx.FramebufferRenderbuffer(gles.FRAMEBUFFER, gles.STENCIL_ATTACHMENT, gles.RENDERBUFFER, uint32(r))
|
||||
if s := c.ctx.CheckFramebufferStatus(gles.FRAMEBUFFER); s != gles.FRAMEBUFFER_COMPLETE {
|
||||
return errors.New(fmt.Sprintf("opengl: glFramebufferRenderbuffer failed: %d", s))
|
||||
}
|
||||
c.ctx.Clear(gles.STENCIL_BUFFER_BIT)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *context) setViewportImpl(width, height int) {
|
||||
c.ctx.Viewport(0, 0, int32(width), int32(height))
|
||||
}
|
||||
@ -457,3 +501,23 @@ func (c *context) getBufferSubData(buffer buffer, width, height int) []byte {
|
||||
// As PBO is not used in mobiles, leave this unimplemented so far.
|
||||
panic("opengl: getBufferSubData is not implemented for mobiles")
|
||||
}
|
||||
|
||||
func (c *context) enableStencilTest() {
|
||||
c.ctx.Enable(gles.STENCIL_TEST)
|
||||
}
|
||||
|
||||
func (c *context) disableStencilTest() {
|
||||
c.ctx.Disable(gles.STENCIL_TEST)
|
||||
}
|
||||
|
||||
func (c *context) beginStencilWithEvenOddRule() {
|
||||
c.ctx.StencilFunc(gles.ALWAYS, 0x00, 0xff)
|
||||
c.ctx.StencilOp(gles.KEEP, gles.KEEP, gles.INVERT)
|
||||
c.ctx.ColorMask(false, false, false, false)
|
||||
}
|
||||
|
||||
func (c *context) endStencilWithEvenOddRule() {
|
||||
c.ctx.StencilFunc(gles.NOTEQUAL, 0x00, 0xff)
|
||||
c.ctx.StencilOp(gles.ZERO, gles.ZERO, gles.ZERO)
|
||||
c.ctx.ColorMask(true, true, true, true)
|
||||
}
|
||||
|
@ -142,7 +142,7 @@ func (g *Graphics) SetVertices(vertices []float32, indices []uint16) {
|
||||
g.context.elementArrayBufferSubData(indices)
|
||||
}
|
||||
|
||||
func (g *Graphics) DrawTriangles(dstID driver.ImageID, srcIDs [graphics.ShaderImageNum]driver.ImageID, offsets [graphics.ShaderImageNum - 1][2]float32, shaderID driver.ShaderID, indexLen int, indexOffset int, mode driver.CompositeMode, colorM *affine.ColorM, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, uniforms []interface{}) error {
|
||||
func (g *Graphics) DrawTriangles(dstID driver.ImageID, srcIDs [graphics.ShaderImageNum]driver.ImageID, offsets [graphics.ShaderImageNum - 1][2]float32, shaderID driver.ShaderID, indexLen int, indexOffset int, mode driver.CompositeMode, colorM *affine.ColorM, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, uniforms []interface{}, evenOdd bool) error {
|
||||
destination := g.images[dstID]
|
||||
|
||||
if !destination.pbo.equal(*new(buffer)) {
|
||||
@ -308,7 +308,20 @@ func (g *Graphics) DrawTriangles(dstID driver.ImageID, srcIDs [graphics.ShaderIm
|
||||
return err
|
||||
}
|
||||
|
||||
if evenOdd {
|
||||
if err := destination.ensureStencilBuffer(); err != nil {
|
||||
return err
|
||||
}
|
||||
g.context.enableStencilTest()
|
||||
g.context.beginStencilWithEvenOddRule()
|
||||
g.context.drawElements(indexLen, indexOffset*2)
|
||||
g.context.endStencilWithEvenOddRule()
|
||||
}
|
||||
g.context.drawElements(indexLen, indexOffset*2) // 2 is uint16 size in bytes
|
||||
if evenOdd {
|
||||
g.context.disableStencilTest()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@ type Image struct {
|
||||
id driver.ImageID
|
||||
graphics *Graphics
|
||||
texture textureNative
|
||||
stencil renderbufferNative
|
||||
framebuffer *framebuffer
|
||||
pbo buffer
|
||||
width int
|
||||
@ -48,6 +49,9 @@ func (i *Image) Dispose() {
|
||||
if !i.texture.equal(*new(textureNative)) {
|
||||
i.graphics.context.deleteTexture(i.texture)
|
||||
}
|
||||
if !i.stencil.equal(*new(renderbufferNative)) {
|
||||
i.graphics.context.deleteRenderbuffer(i.stencil)
|
||||
}
|
||||
|
||||
i.graphics.removeImage(i)
|
||||
}
|
||||
@ -105,6 +109,27 @@ func (i *Image) ensureFramebuffer() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Image) ensureStencilBuffer() error {
|
||||
if !i.stencil.equal(*new(renderbufferNative)) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := i.ensureFramebuffer(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r, err := i.graphics.context.newRenderbuffer(i.framebufferSize())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
i.stencil = r
|
||||
|
||||
if err := i.graphics.context.bindStencilBuffer(i.framebuffer.native, i.stencil); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Image) ReplacePixels(args []*driver.ReplacePixelsArgs) {
|
||||
if i.screen {
|
||||
panic("opengl: ReplacePixels cannot be called on the screen, that doesn't have a texture")
|
||||
|
@ -85,7 +85,7 @@ func (m *Mipmap) Pixels(x, y, width, height int) ([]byte, error) {
|
||||
return m.orig.Pixels(x, y, width, height)
|
||||
}
|
||||
|
||||
func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderImageNum]*Mipmap, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, subimageOffsets [graphics.ShaderImageNum - 1][2]float32, shader *Shader, uniforms []interface{}, canSkipMipmap bool) {
|
||||
func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderImageNum]*Mipmap, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, subimageOffsets [graphics.ShaderImageNum - 1][2]float32, shader *Shader, uniforms []interface{}, evenOdd bool, canSkipMipmap bool) {
|
||||
if len(indices) == 0 {
|
||||
return
|
||||
}
|
||||
@ -164,7 +164,7 @@ func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderImageNum]*Mipmap, vertices [
|
||||
imgs[i] = src.orig
|
||||
}
|
||||
|
||||
m.orig.DrawTriangles(imgs, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, subimageOffsets, s, uniforms)
|
||||
m.orig.DrawTriangles(imgs, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, subimageOffsets, s, uniforms, evenOdd)
|
||||
m.disposeMipmaps()
|
||||
}
|
||||
|
||||
@ -226,7 +226,7 @@ func (m *Mipmap) level(level int) *buffered.Image {
|
||||
Width: float32(w2),
|
||||
Height: float32(h2),
|
||||
}
|
||||
s.DrawTriangles([graphics.ShaderImageNum]*buffered.Image{src}, vs, is, nil, driver.CompositeModeCopy, filter, driver.AddressUnsafe, dstRegion, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
|
||||
s.DrawTriangles([graphics.ShaderImageNum]*buffered.Image{src}, vs, is, nil, driver.CompositeModeCopy, filter, driver.AddressUnsafe, dstRegion, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
|
||||
m.imgs[level] = s
|
||||
|
||||
return m.imgs[level]
|
||||
|
@ -76,6 +76,7 @@ type drawTrianglesHistoryItem struct {
|
||||
srcRegion driver.Region
|
||||
shader *Shader
|
||||
uniforms []interface{}
|
||||
evenOdd bool
|
||||
}
|
||||
|
||||
// Image represents an image that can be restored when GL context is lost.
|
||||
@ -186,7 +187,7 @@ func (i *Image) Extend(width, height int) *Image {
|
||||
Width: float32(sw),
|
||||
Height: float32(sh),
|
||||
}
|
||||
newImg.DrawTriangles(srcs, offsets, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
|
||||
newImg.DrawTriangles(srcs, offsets, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
|
||||
|
||||
// Overwrite the history as if the image newImg is created only by ReplacePixels. Now drawTrianglesHistory
|
||||
// and basePixels cannot be mixed.
|
||||
@ -247,7 +248,7 @@ func clearImage(i *graphicscommand.Image) {
|
||||
Width: float32(dw),
|
||||
Height: float32(dh),
|
||||
}
|
||||
i.DrawTriangles(srcs, offsets, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, dstRegion, driver.Region{}, nil, nil)
|
||||
i.DrawTriangles(srcs, offsets, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, dstRegion, driver.Region{}, nil, nil, false)
|
||||
}
|
||||
|
||||
// BasePixelsForTesting returns the image's basePixels for testing.
|
||||
@ -350,7 +351,7 @@ func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) {
|
||||
// 5: Color G
|
||||
// 6: Color B
|
||||
// 7: Color Y
|
||||
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader, uniforms []interface{}) {
|
||||
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader, uniforms []interface{}, evenOdd bool) {
|
||||
if i.priority {
|
||||
panic("restorable: DrawTriangles cannot be called on a priority image")
|
||||
}
|
||||
@ -374,7 +375,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [gra
|
||||
if srcstale || i.screen || !NeedsRestoring() || i.volatile {
|
||||
i.makeStale()
|
||||
} else {
|
||||
i.appendDrawTrianglesHistory(srcs, offsets, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, shader, uniforms)
|
||||
i.appendDrawTrianglesHistory(srcs, offsets, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, shader, uniforms, evenOdd)
|
||||
}
|
||||
|
||||
var s *graphicscommand.Shader
|
||||
@ -391,11 +392,11 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [gra
|
||||
}
|
||||
s = shader.shader
|
||||
}
|
||||
i.image.DrawTriangles(imgs, offsets, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, s, uniforms)
|
||||
i.image.DrawTriangles(imgs, offsets, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, s, uniforms, evenOdd)
|
||||
}
|
||||
|
||||
// appendDrawTrianglesHistory appends a draw-image history item to the image.
|
||||
func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader, uniforms []interface{}) {
|
||||
func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region, shader *Shader, uniforms []interface{}, evenOdd bool) {
|
||||
if i.stale || i.volatile || i.screen {
|
||||
return
|
||||
}
|
||||
@ -427,6 +428,7 @@ func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderImageNum]*Image,
|
||||
srcRegion: srcRegion,
|
||||
shader: shader,
|
||||
uniforms: uniforms,
|
||||
evenOdd: evenOdd,
|
||||
}
|
||||
i.drawTrianglesHistory = append(i.drawTrianglesHistory, item)
|
||||
}
|
||||
@ -605,7 +607,7 @@ func (i *Image) restore() error {
|
||||
}
|
||||
imgs[i] = img.image
|
||||
}
|
||||
gimg.DrawTriangles(imgs, c.offsets, c.vertices, c.indices, c.colorm, c.mode, c.filter, c.address, c.dstRegion, c.srcRegion, s, c.uniforms)
|
||||
gimg.DrawTriangles(imgs, c.offsets, c.vertices, c.indices, c.colorm, c.mode, c.filter, c.address, c.dstRegion, c.srcRegion, s, c.uniforms, c.evenOdd)
|
||||
}
|
||||
|
||||
if len(i.drawTrianglesHistory) > 0 {
|
||||
|
@ -137,7 +137,7 @@ func TestRestoreChain(t *testing.T) {
|
||||
Width: 1,
|
||||
Height: 1,
|
||||
}
|
||||
imgs[i+1].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[i]}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
|
||||
imgs[i+1].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[i]}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
|
||||
}
|
||||
if err := ResolveStaleImages(); err != nil {
|
||||
t.Fatal(err)
|
||||
@ -185,10 +185,10 @@ func TestRestoreChain2(t *testing.T) {
|
||||
Width: w,
|
||||
Height: h,
|
||||
}
|
||||
imgs[8].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[7]}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
|
||||
imgs[9].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[8]}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
|
||||
imgs[8].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[7]}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
|
||||
imgs[9].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[8]}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
|
||||
for i := 0; i < 7; i++ {
|
||||
imgs[i+1].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[i]}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
|
||||
imgs[i+1].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[i]}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
|
||||
}
|
||||
|
||||
if err := ResolveStaleImages(); err != nil {
|
||||
@ -234,10 +234,10 @@ func TestRestoreOverrideSource(t *testing.T) {
|
||||
Width: w,
|
||||
Height: h,
|
||||
}
|
||||
img2.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
|
||||
img3.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
|
||||
img2.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
|
||||
img3.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
|
||||
img0.ReplacePixels([]byte{clr1.R, clr1.G, clr1.B, clr1.A}, 0, 0, w, h)
|
||||
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
|
||||
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
|
||||
if err := ResolveStaleImages(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -323,23 +323,23 @@ func TestRestoreComplexGraph(t *testing.T) {
|
||||
Height: h,
|
||||
}
|
||||
var offsets [graphics.ShaderImageNum - 1][2]float32
|
||||
img3.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
|
||||
img3.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
|
||||
vs = quadVertices(w, h, 1, 0)
|
||||
img3.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
|
||||
img3.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
|
||||
vs = quadVertices(w, h, 1, 0)
|
||||
img4.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
|
||||
img4.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
|
||||
vs = quadVertices(w, h, 2, 0)
|
||||
img4.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
|
||||
img4.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
|
||||
vs = quadVertices(w, h, 0, 0)
|
||||
img5.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
|
||||
img5.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
|
||||
vs = quadVertices(w, h, 0, 0)
|
||||
img6.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
|
||||
img6.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
|
||||
vs = quadVertices(w, h, 1, 0)
|
||||
img6.DrawTriangles([graphics.ShaderImageNum]*Image{img4}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
|
||||
img6.DrawTriangles([graphics.ShaderImageNum]*Image{img4}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
|
||||
vs = quadVertices(w, h, 0, 0)
|
||||
img7.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
|
||||
img7.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
|
||||
vs = quadVertices(w, h, 2, 0)
|
||||
img7.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
|
||||
img7.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
|
||||
if err := ResolveStaleImages(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -437,8 +437,8 @@ func TestRestoreRecursive(t *testing.T) {
|
||||
Width: w,
|
||||
Height: h,
|
||||
}
|
||||
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 1, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
|
||||
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 1, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
|
||||
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 1, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
|
||||
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 1, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
|
||||
if err := ResolveStaleImages(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -538,7 +538,7 @@ func TestDrawTrianglesAndReplacePixels(t *testing.T) {
|
||||
Width: 2,
|
||||
Height: 1,
|
||||
}
|
||||
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
|
||||
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
|
||||
img1.ReplacePixels([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 0, 0, 2, 1)
|
||||
|
||||
if err := ResolveStaleImages(); err != nil {
|
||||
@ -581,8 +581,8 @@ func TestDispose(t *testing.T) {
|
||||
Width: 1,
|
||||
Height: 1,
|
||||
}
|
||||
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
|
||||
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
|
||||
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
|
||||
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
|
||||
img1.Dispose()
|
||||
|
||||
if err := ResolveStaleImages(); err != nil {
|
||||
@ -696,7 +696,7 @@ func TestReplacePixelsOnly(t *testing.T) {
|
||||
Width: 1,
|
||||
Height: 1,
|
||||
}
|
||||
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
|
||||
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
|
||||
img0.ReplacePixels([]byte{5, 6, 7, 8}, 0, 0, 1, 1)
|
||||
|
||||
// BasePixelsForTesting is available without GPU accessing.
|
||||
@ -756,7 +756,7 @@ func TestReadPixelsFromVolatileImage(t *testing.T) {
|
||||
Width: w,
|
||||
Height: h,
|
||||
}
|
||||
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
|
||||
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
|
||||
|
||||
// Read the pixels. If the implementation is correct, dst tries to read its pixels from GPU due to being
|
||||
// stale.
|
||||
@ -783,7 +783,7 @@ func TestAllowReplacePixelsAfterDrawTriangles(t *testing.T) {
|
||||
Width: w,
|
||||
Height: h,
|
||||
}
|
||||
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
|
||||
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
|
||||
dst.ReplacePixels(make([]byte, 4*w*h), 0, 0, w, h)
|
||||
// ReplacePixels for a whole image doesn't panic.
|
||||
}
|
||||
@ -807,7 +807,7 @@ func TestDisallowReplacePixelsForPartAfterDrawTriangles(t *testing.T) {
|
||||
Width: w,
|
||||
Height: h,
|
||||
}
|
||||
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
|
||||
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
|
||||
dst.ReplacePixels(make([]byte, 4), 0, 0, 1, 1)
|
||||
}
|
||||
|
||||
@ -884,7 +884,7 @@ func TestMutateSlices(t *testing.T) {
|
||||
Width: w,
|
||||
Height: h,
|
||||
}
|
||||
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
|
||||
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
|
||||
for i := range vs {
|
||||
vs[i] = 0
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ func clearImage(img *Image, w, h int) {
|
||||
Width: float32(w),
|
||||
Height: float32(h),
|
||||
}
|
||||
img.DrawTriangles([graphics.ShaderImageNum]*Image{emptyImage}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
|
||||
img.DrawTriangles([graphics.ShaderImageNum]*Image{emptyImage}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil, false)
|
||||
}
|
||||
|
||||
func TestShader(t *testing.T) {
|
||||
@ -63,7 +63,7 @@ func TestShader(t *testing.T) {
|
||||
Width: 1,
|
||||
Height: 1,
|
||||
}
|
||||
img.DrawTriangles([graphics.ShaderImageNum]*Image{}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, s, nil)
|
||||
img.DrawTriangles([graphics.ShaderImageNum]*Image{}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, s, nil, false)
|
||||
|
||||
if err := ResolveStaleImages(); err != nil {
|
||||
t.Fatal(err)
|
||||
@ -99,7 +99,7 @@ func TestShaderChain(t *testing.T) {
|
||||
Width: 1,
|
||||
Height: 1,
|
||||
}
|
||||
imgs[i+1].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[i]}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, s, nil)
|
||||
imgs[i+1].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[i]}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, s, nil, false)
|
||||
}
|
||||
|
||||
if err := ResolveStaleImages(); err != nil {
|
||||
@ -138,7 +138,7 @@ func TestShaderMultipleSources(t *testing.T) {
|
||||
Width: 1,
|
||||
Height: 1,
|
||||
}
|
||||
dst.DrawTriangles(srcs, offsets, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, s, nil)
|
||||
dst.DrawTriangles(srcs, offsets, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, s, nil, false)
|
||||
|
||||
// Clear one of the sources after DrawTriangles. dst should not be affected.
|
||||
clearImage(srcs[0], 1, 1)
|
||||
@ -180,7 +180,7 @@ func TestShaderMultipleSourcesOnOneTexture(t *testing.T) {
|
||||
Width: 1,
|
||||
Height: 1,
|
||||
}
|
||||
dst.DrawTriangles(srcs, offsets, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, s, nil)
|
||||
dst.DrawTriangles(srcs, offsets, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, s, nil, false)
|
||||
|
||||
// Clear one of the sources after DrawTriangles. dst should not be affected.
|
||||
clearImage(srcs[0], 3, 1)
|
||||
@ -211,7 +211,7 @@ func TestShaderDispose(t *testing.T) {
|
||||
Width: 1,
|
||||
Height: 1,
|
||||
}
|
||||
img.DrawTriangles([graphics.ShaderImageNum]*Image{}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, s, nil)
|
||||
img.DrawTriangles([graphics.ShaderImageNum]*Image{}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, s, nil, false)
|
||||
|
||||
// Dispose the shader. This should invalidates all the images using this shader i.e., all the images become
|
||||
// stale.
|
||||
|
@ -1,26 +0,0 @@
|
||||
// Copyright 2019 The Ebiten Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package triangulate
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
var nan32 = float32(math.NaN())
|
||||
|
||||
type Point struct {
|
||||
X float32
|
||||
Y float32
|
||||
}
|
@ -1,154 +0,0 @@
|
||||
// Copyright 2019 The Ebiten Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package triangulate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func cross(v0x, v0y, v1x, v1y float32) float32 {
|
||||
return v0x*v1y - v0y*v1x
|
||||
}
|
||||
|
||||
func triangleCross(pt0, pt1, pt2 Point) float32 {
|
||||
return cross(pt1.X-pt0.X, pt1.Y-pt0.Y, pt2.X-pt1.X, pt2.Y-pt1.Y)
|
||||
}
|
||||
|
||||
func adjacentIndices(indices []uint16, idx int) (uint16, uint16, uint16) {
|
||||
return indices[(idx+len(indices)-1)%len(indices)], indices[idx], indices[(idx+1)%len(indices)]
|
||||
}
|
||||
|
||||
func InTriangle(pt, pt0, pt1, pt2 Point) bool {
|
||||
if pt.X <= pt0.X && pt.X <= pt1.X && pt.X <= pt2.X {
|
||||
return false
|
||||
}
|
||||
if pt.X >= pt0.X && pt.X >= pt1.X && pt.X >= pt2.X {
|
||||
return false
|
||||
}
|
||||
if pt.Y <= pt0.Y && pt.Y <= pt1.Y && pt.Y <= pt2.Y {
|
||||
return false
|
||||
}
|
||||
if pt.Y >= pt0.Y && pt.Y >= pt1.Y && pt.Y >= pt2.Y {
|
||||
return false
|
||||
}
|
||||
c0 := cross(pt.X-pt0.X, pt.Y-pt0.Y, pt1.X-pt0.X, pt1.Y-pt0.Y)
|
||||
c1 := cross(pt.X-pt1.X, pt.Y-pt1.Y, pt2.X-pt1.X, pt2.Y-pt1.Y)
|
||||
c2 := cross(pt.X-pt2.X, pt.Y-pt2.Y, pt0.X-pt2.X, pt0.Y-pt2.Y)
|
||||
return (c0 <= 0 && c1 <= 0 && c2 <= 0) || (c0 >= 0 && c1 >= 0 && c2 >= 0)
|
||||
}
|
||||
|
||||
// Triangulate triangulates the region surrounded by the points pts and returnes the point indices.
|
||||
func Triangulate(pts []Point) []uint16 {
|
||||
if len(pts) < 3 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var currentIndices []uint16
|
||||
|
||||
// Split pts into the two point groups if there are the same points.
|
||||
for i := range pts {
|
||||
for j := 0; j < i; j++ {
|
||||
if pts[i] == pts[j] {
|
||||
is0 := Triangulate(pts[j:i])
|
||||
for idx := range is0 {
|
||||
is0[idx] += uint16(j)
|
||||
}
|
||||
is1 := Triangulate(append(pts[i:], pts[:j]...))
|
||||
for idx := range is1 {
|
||||
is1[idx] = uint16((int(is1[idx]) + i) % len(pts))
|
||||
}
|
||||
return append(is0, is1...)
|
||||
}
|
||||
}
|
||||
currentIndices = append(currentIndices, uint16(i))
|
||||
}
|
||||
|
||||
var indices []uint16
|
||||
|
||||
// Triangulation by Ear Clipping.
|
||||
// https://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf
|
||||
// TODO: Adopt a more efficient algorithm.
|
||||
for len(currentIndices) >= 3 {
|
||||
// Calculate cross-products and remove unneeded vertices.
|
||||
cs := make([]float32, len(currentIndices))
|
||||
idxToRemove := -1
|
||||
|
||||
// Determine the direction of the polygon from the upper-left point.
|
||||
var upperLeft int
|
||||
for i := range currentIndices {
|
||||
i0, i1, i2 := adjacentIndices(currentIndices, i)
|
||||
pt0 := pts[i0]
|
||||
pt1 := pts[i1]
|
||||
pt2 := pts[i2]
|
||||
c := triangleCross(pt0, pt1, pt2)
|
||||
if c == 0 {
|
||||
idxToRemove = i
|
||||
break
|
||||
}
|
||||
cs[i] = c
|
||||
|
||||
if pts[currentIndices[upperLeft]].X > pts[currentIndices[i]].X {
|
||||
upperLeft = i
|
||||
} else if pts[currentIndices[upperLeft]].X == pts[currentIndices[i]].X &&
|
||||
pts[currentIndices[upperLeft]].Y > pts[currentIndices[i]].Y {
|
||||
upperLeft = i
|
||||
}
|
||||
}
|
||||
if idxToRemove != -1 {
|
||||
currentIndices = append(currentIndices[:idxToRemove], currentIndices[idxToRemove+1:]...)
|
||||
continue
|
||||
}
|
||||
|
||||
clockwise := cs[upperLeft] < 0
|
||||
|
||||
idx := -1
|
||||
index:
|
||||
for i := range currentIndices {
|
||||
c := cs[i]
|
||||
if c == 0 {
|
||||
panic("math: cross value must not be 0")
|
||||
}
|
||||
if c < 0 && !clockwise || c > 0 && clockwise {
|
||||
// The angle is more than 180 degrees. This is not an ear.
|
||||
continue
|
||||
}
|
||||
|
||||
i0, i1, i2 := adjacentIndices(currentIndices, i)
|
||||
pt0 := pts[i0]
|
||||
pt1 := pts[i1]
|
||||
pt2 := pts[i2]
|
||||
for _, j := range currentIndices {
|
||||
if j == i0 || j == i1 || j == i2 {
|
||||
continue
|
||||
}
|
||||
if InTriangle(pts[j], pt0, pt1, pt2) {
|
||||
// If the triangle includes another point, the triangle is not an ear.
|
||||
continue index
|
||||
}
|
||||
}
|
||||
// The angle is less than 180 degrees. This is an ear.
|
||||
idx = i
|
||||
break
|
||||
}
|
||||
if idx < 0 {
|
||||
// TODO: This happens when there is self-crossing.
|
||||
panic(fmt.Sprintf("math: there is no ear in the polygon: %v", pts))
|
||||
}
|
||||
i0, i1, i2 := adjacentIndices(currentIndices, idx)
|
||||
indices = append(indices, i0, i1, i2)
|
||||
currentIndices = append(currentIndices[:idx], currentIndices[idx+1:]...)
|
||||
}
|
||||
return indices
|
||||
}
|
@ -1,328 +0,0 @@
|
||||
// Copyright 2019 The Ebiten Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package triangulate_test
|
||||
|
||||
import (
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
. "github.com/hajimehoshi/ebiten/v2/vector/internal/triangulate"
|
||||
)
|
||||
|
||||
func TestIsInTriangle(t *testing.T) {
|
||||
tests := []struct {
|
||||
Tri []Point
|
||||
Pt Point
|
||||
Out bool
|
||||
}{
|
||||
{
|
||||
Tri: []Point{
|
||||
{0, 0},
|
||||
{0, 10},
|
||||
{10, 10},
|
||||
},
|
||||
Pt: Point{1, 9},
|
||||
Out: true,
|
||||
},
|
||||
{
|
||||
Tri: []Point{
|
||||
{0, 0},
|
||||
{0, 10},
|
||||
{10, 10},
|
||||
},
|
||||
Pt: Point{8, 9},
|
||||
Out: true,
|
||||
},
|
||||
{
|
||||
Tri: []Point{
|
||||
{0, 0},
|
||||
{0, 10},
|
||||
{10, 10},
|
||||
},
|
||||
Pt: Point{10, 9},
|
||||
Out: false,
|
||||
},
|
||||
{
|
||||
Tri: []Point{
|
||||
{3, 5},
|
||||
{2, 7},
|
||||
{7, 7},
|
||||
},
|
||||
Pt: Point{3, 6},
|
||||
Out: true,
|
||||
},
|
||||
{
|
||||
Tri: []Point{
|
||||
{3, 5},
|
||||
{2, 7},
|
||||
{7, 7},
|
||||
},
|
||||
Pt: Point{7, 6},
|
||||
Out: false,
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
got := InTriangle(tc.Pt, tc.Tri[0], tc.Tri[1], tc.Tri[2])
|
||||
want := tc.Out
|
||||
if got != want {
|
||||
t.Errorf("InTriangle(%v, %v, %v, %v): got: %t, want: %t", tc.Pt, tc.Tri[0], tc.Tri[1], tc.Tri[2], got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTriangulate(t *testing.T) {
|
||||
tests := []struct {
|
||||
In []Point
|
||||
Out []uint16
|
||||
}{
|
||||
{
|
||||
In: []Point{},
|
||||
Out: nil,
|
||||
},
|
||||
{
|
||||
In: []Point{
|
||||
{0, 0},
|
||||
},
|
||||
Out: nil,
|
||||
},
|
||||
{
|
||||
In: []Point{
|
||||
{0, 0},
|
||||
{0, 1},
|
||||
},
|
||||
Out: nil,
|
||||
},
|
||||
{
|
||||
In: []Point{
|
||||
{0, 0},
|
||||
{0, 0},
|
||||
{1, 1},
|
||||
},
|
||||
Out: nil,
|
||||
},
|
||||
{
|
||||
In: []Point{
|
||||
{0, 0},
|
||||
{0.5, 0.5},
|
||||
{1, 1},
|
||||
},
|
||||
Out: nil,
|
||||
},
|
||||
{
|
||||
In: []Point{
|
||||
{0, 0},
|
||||
{0.5, 0.5},
|
||||
{1.5, 1.5},
|
||||
{1, 1},
|
||||
},
|
||||
Out: nil,
|
||||
},
|
||||
{
|
||||
In: []Point{
|
||||
{0, 0},
|
||||
{0, 1},
|
||||
{1, 1},
|
||||
},
|
||||
Out: []uint16{2, 0, 1},
|
||||
},
|
||||
{
|
||||
In: []Point{
|
||||
{0, 0},
|
||||
{1, 1},
|
||||
{0, 1},
|
||||
},
|
||||
Out: []uint16{2, 0, 1},
|
||||
},
|
||||
{
|
||||
In: []Point{
|
||||
{0, 0},
|
||||
{1, 1},
|
||||
{0, 1},
|
||||
{0, 0.5},
|
||||
},
|
||||
Out: []uint16{2, 0, 1},
|
||||
},
|
||||
{
|
||||
In: []Point{
|
||||
{0, 0},
|
||||
{0, 1},
|
||||
{1, 1},
|
||||
{1, 0},
|
||||
},
|
||||
Out: []uint16{3, 0, 1, 3, 1, 2},
|
||||
},
|
||||
{
|
||||
In: []Point{
|
||||
{2, 2},
|
||||
{2, 7},
|
||||
{7, 7},
|
||||
{7, 6},
|
||||
{3, 6},
|
||||
{3, 5},
|
||||
},
|
||||
Out: []uint16{5, 0, 1, 1, 2, 3, 1, 3, 4, 5, 1, 4},
|
||||
},
|
||||
{
|
||||
In: []Point{
|
||||
{2, 2},
|
||||
{2, 7},
|
||||
{7, 7},
|
||||
{7, 6},
|
||||
{3, 6},
|
||||
{3, 5},
|
||||
{7, 5},
|
||||
{7, 4},
|
||||
{3, 4},
|
||||
{3, 3},
|
||||
},
|
||||
Out: []uint16{9, 0, 1, 1, 2, 3, 1, 3, 4, 9, 1, 4, 8, 5, 6, 8, 6, 7},
|
||||
},
|
||||
{
|
||||
In: []Point{
|
||||
{0, 0},
|
||||
{0, 5},
|
||||
{2, 5},
|
||||
{3, 3},
|
||||
{2, 2},
|
||||
{3, 1},
|
||||
{2, 0},
|
||||
},
|
||||
Out: []uint16{6, 0, 1, 6, 1, 2, 6, 2, 3, 4, 5, 6, 6, 3, 4},
|
||||
},
|
||||
{
|
||||
In: []Point{
|
||||
{0, 0},
|
||||
{0, 5},
|
||||
{2, 5},
|
||||
{2, 5},
|
||||
{3, 3},
|
||||
{2, 2},
|
||||
{2, 2},
|
||||
{3, 1},
|
||||
{2, 0},
|
||||
},
|
||||
Out: []uint16{6, 7, 8, 6, 8, 0, 4, 0, 1, 4, 1, 3},
|
||||
},
|
||||
{
|
||||
In: []Point{
|
||||
{0, 0},
|
||||
{0, 1},
|
||||
{1, 1},
|
||||
{1, 0},
|
||||
{2, 0},
|
||||
},
|
||||
Out: []uint16{3, 0, 1, 3, 1, 2},
|
||||
},
|
||||
{
|
||||
In: []Point{
|
||||
{2, 0},
|
||||
{0, 0},
|
||||
{1, 0},
|
||||
{1, 1},
|
||||
{2, 1},
|
||||
},
|
||||
Out: []uint16{4, 0, 2, 4, 2, 3},
|
||||
},
|
||||
{
|
||||
In: []Point{
|
||||
{2, 0},
|
||||
{2, 1},
|
||||
{1, 1},
|
||||
{1, 0},
|
||||
{0, 0},
|
||||
},
|
||||
Out: []uint16{3, 0, 1, 3, 1, 2},
|
||||
},
|
||||
{
|
||||
// Butterfly
|
||||
In: []Point{
|
||||
{0, 2},
|
||||
{1, 1},
|
||||
{2, 2},
|
||||
{2, 0},
|
||||
{1, 1},
|
||||
{0, 0},
|
||||
},
|
||||
Out: []uint16{3, 1, 2, 0, 4, 5},
|
||||
},
|
||||
{
|
||||
In: []Point{
|
||||
{0, 6},
|
||||
{0, 9},
|
||||
{6, 6},
|
||||
{6, 3},
|
||||
{9, 3},
|
||||
{8, 0},
|
||||
{6, 0},
|
||||
{6, 3},
|
||||
},
|
||||
Out: []uint16{6, 3, 4, 6, 4, 5, 2, 7, 0, 2, 0, 1},
|
||||
},
|
||||
{
|
||||
In: []Point{
|
||||
{0, 4},
|
||||
{0, 6},
|
||||
{2, 6},
|
||||
{2, 5},
|
||||
{3, 5},
|
||||
{3, 4},
|
||||
{4, 4},
|
||||
{4, 2},
|
||||
{6, 2},
|
||||
{6, 1},
|
||||
{5, 1},
|
||||
{5, 0},
|
||||
{4, 0},
|
||||
{4, 2},
|
||||
{2, 2},
|
||||
{2, 3},
|
||||
{1, 3},
|
||||
{1, 4},
|
||||
},
|
||||
Out: []uint16{7, 8, 9, 7, 9, 10, 12, 7, 10, 12, 10, 11, 6, 13, 14, 6, 14, 15, 6, 15, 16, 6, 16, 17, 5, 0, 1, 1, 2, 3, 5, 1, 3, 5, 3, 4},
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
got := Triangulate(tc.In)
|
||||
want := tc.Out
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("Triangulate(%v): got: %v, want: %v", tc.In, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var benchmarkPath []Point
|
||||
|
||||
func init() {
|
||||
const (
|
||||
w = 640
|
||||
h = 480
|
||||
)
|
||||
var p []Point
|
||||
for i := 0; i < w; i++ {
|
||||
x := float32(i)
|
||||
y := h/2 + 80*float32(math.Sin(2*math.Pi*float64(i)/40))
|
||||
p = append(p, Point{X: x, Y: y})
|
||||
}
|
||||
p = append(p, Point{w, h}, Point{0, h})
|
||||
benchmarkPath = p
|
||||
}
|
||||
|
||||
func BenchmarkTriangulate(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
Triangulate(benchmarkPath)
|
||||
}
|
||||
}
|
101
vector/path.go
101
vector/path.go
@ -18,33 +18,26 @@
|
||||
package vector
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"math"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/vector/internal/triangulate"
|
||||
)
|
||||
|
||||
var (
|
||||
emptyImage = ebiten.NewImage(3, 3)
|
||||
emptySubImage = emptyImage.SubImage(image.Rect(1, 1, 2, 2)).(*ebiten.Image)
|
||||
)
|
||||
|
||||
func init() {
|
||||
emptyImage.Fill(color.White)
|
||||
type point struct {
|
||||
x float32
|
||||
y float32
|
||||
}
|
||||
|
||||
// Path represents a collection of path segments.
|
||||
type Path struct {
|
||||
segs [][]triangulate.Point
|
||||
cur triangulate.Point
|
||||
segs [][]point
|
||||
cur point
|
||||
}
|
||||
|
||||
// MoveTo skips the current position of the path to the given position (x, y) without adding any strokes.
|
||||
func (p *Path) MoveTo(x, y float32) {
|
||||
p.cur = triangulate.Point{X: x, Y: y}
|
||||
p.segs = append(p.segs, []triangulate.Point{p.cur})
|
||||
p.cur = point{x: x, y: y}
|
||||
p.segs = append(p.segs, []point{p.cur})
|
||||
}
|
||||
|
||||
// LineTo adds a line segument to the path, which starts from the current position and ends to the given position (x, y).
|
||||
@ -52,10 +45,10 @@ func (p *Path) MoveTo(x, y float32) {
|
||||
// LineTo updates the current position to (x, y).
|
||||
func (p *Path) LineTo(x, y float32) {
|
||||
if len(p.segs) == 0 {
|
||||
p.segs = append(p.segs, []triangulate.Point{p.cur})
|
||||
p.segs = append(p.segs, []point{p.cur})
|
||||
}
|
||||
p.segs[len(p.segs)-1] = append(p.segs[len(p.segs)-1], triangulate.Point{X: x, Y: y})
|
||||
p.cur = triangulate.Point{X: x, Y: y}
|
||||
p.segs[len(p.segs)-1] = append(p.segs[len(p.segs)-1], point{x: x, y: y})
|
||||
p.cur = point{x: x, y: y}
|
||||
}
|
||||
|
||||
// nseg returns a number of segments based on the given two points (x0, y0) and (x1, y1).
|
||||
@ -78,69 +71,61 @@ func nseg(x0, y0, x1, y1 float32) int {
|
||||
|
||||
// QuadTo adds a quadratic Bézier curve to the path.
|
||||
func (p *Path) QuadTo(cpx, cpy, x, y float32) {
|
||||
// TODO: Split more appropriate number of segments.
|
||||
c := p.cur
|
||||
num := nseg(c.X, c.Y, x, y)
|
||||
num := nseg(c.x, c.y, x, y)
|
||||
for t := float32(0.0); t <= 1; t += 1.0 / float32(num) {
|
||||
xf := (1-t)*(1-t)*c.X + 2*t*(1-t)*cpx + t*t*x
|
||||
yf := (1-t)*(1-t)*c.Y + 2*t*(1-t)*cpy + t*t*y
|
||||
xf := (1-t)*(1-t)*c.x + 2*t*(1-t)*cpx + t*t*x
|
||||
yf := (1-t)*(1-t)*c.y + 2*t*(1-t)*cpy + t*t*y
|
||||
p.LineTo(xf, yf)
|
||||
}
|
||||
}
|
||||
|
||||
// CubicTo adds a cubic Bézier curve to the path.
|
||||
func (p *Path) CubicTo(cp0x, cp0y, cp1x, cp1y, x, y float32) {
|
||||
// TODO: Split more appropriate number of segments.
|
||||
c := p.cur
|
||||
num := nseg(c.X, c.Y, x, y)
|
||||
num := nseg(c.x, c.y, x, y)
|
||||
for t := float32(0.0); t <= 1; t += 1.0 / float32(num) {
|
||||
xf := (1-t)*(1-t)*(1-t)*c.X + 3*(1-t)*(1-t)*t*cp0x + 3*(1-t)*t*t*cp1x + t*t*t*x
|
||||
yf := (1-t)*(1-t)*(1-t)*c.Y + 3*(1-t)*(1-t)*t*cp0y + 3*(1-t)*t*t*cp1y + t*t*t*y
|
||||
xf := (1-t)*(1-t)*(1-t)*c.x + 3*(1-t)*(1-t)*t*cp0x + 3*(1-t)*t*t*cp1x + t*t*t*x
|
||||
yf := (1-t)*(1-t)*(1-t)*c.y + 3*(1-t)*(1-t)*t*cp0y + 3*(1-t)*t*t*cp1y + t*t*t*y
|
||||
p.LineTo(xf, yf)
|
||||
}
|
||||
}
|
||||
|
||||
// FillOptions represents options to fill a path.
|
||||
type FillOptions struct {
|
||||
// Color is a color to fill with.
|
||||
Color color.Color
|
||||
}
|
||||
|
||||
// Fill fills the region of the path with the given options op.
|
||||
func (p *Path) Fill(dst *ebiten.Image, op *FillOptions) {
|
||||
var vertices []ebiten.Vertex
|
||||
var indices []uint16
|
||||
|
||||
if op.Color == nil {
|
||||
return
|
||||
}
|
||||
|
||||
r, g, b, a := op.Color.RGBA()
|
||||
var rf, gf, bf, af float32
|
||||
if a == 0 {
|
||||
return
|
||||
}
|
||||
rf = float32(r) / float32(a)
|
||||
gf = float32(g) / float32(a)
|
||||
bf = float32(b) / float32(a)
|
||||
af = float32(a) / 0xffff
|
||||
// AppendVerticesAndIndices appends vertices and indices for this path and returns them.
|
||||
// AppendVerticesAndIndices works in a similar way to the built-in append function.
|
||||
// If the arguments are nils, AppendVerticesAndIndices returns new slices.
|
||||
//
|
||||
// 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 EvenOdd option
|
||||
// in order to render a complex polygon like a concave polygon, a polygon with holes, or a self-intersecting polygon.
|
||||
func (p *Path) AppendVerticesAndIndices(vertices []ebiten.Vertex, indices []uint16) ([]ebiten.Vertex, []uint16) {
|
||||
// TODO: Add tests.
|
||||
|
||||
var base uint16
|
||||
for _, seg := range p.segs {
|
||||
for _, pt := range seg {
|
||||
if len(seg) < 3 {
|
||||
continue
|
||||
}
|
||||
for i, pt := range seg {
|
||||
vertices = append(vertices, ebiten.Vertex{
|
||||
DstX: pt.X,
|
||||
DstY: pt.Y,
|
||||
DstX: pt.x,
|
||||
DstY: pt.y,
|
||||
SrcX: 0,
|
||||
SrcY: 0,
|
||||
ColorR: rf,
|
||||
ColorG: gf,
|
||||
ColorB: bf,
|
||||
ColorA: af,
|
||||
ColorR: 1,
|
||||
ColorG: 1,
|
||||
ColorB: 1,
|
||||
ColorA: 1,
|
||||
})
|
||||
}
|
||||
for _, idx := range triangulate.Triangulate(seg) {
|
||||
indices = append(indices, idx+base)
|
||||
if i < 2 {
|
||||
continue
|
||||
}
|
||||
indices = append(indices, base, base+uint16(i-1), base+uint16(i))
|
||||
}
|
||||
base += uint16(len(seg))
|
||||
}
|
||||
dst.DrawTriangles(vertices, indices, emptySubImage, nil)
|
||||
return vertices, indices
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user