mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-25 03:08:54 +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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
"log"
|
"log"
|
||||||
"math"
|
"math"
|
||||||
@ -28,6 +29,15 @@ import (
|
|||||||
"github.com/hajimehoshi/ebiten/v2/vector"
|
"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 (
|
const (
|
||||||
screenWidth = 640
|
screenWidth = 640
|
||||||
screenHeight = 480
|
screenHeight = 480
|
||||||
@ -99,10 +109,18 @@ func drawEbitenText(screen *ebiten.Image) {
|
|||||||
path.LineTo(320, 55)
|
path.LineTo(320, 55)
|
||||||
path.LineTo(290, 20)
|
path.LineTo(290, 20)
|
||||||
|
|
||||||
op := &vector.FillOptions{
|
op := &ebiten.DrawTrianglesOptions{
|
||||||
Color: color.RGBA{0xdb, 0x56, 0x20, 0xff},
|
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) {
|
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+3*unit)
|
||||||
path.LineTo(xf+unit, yf+4*unit)
|
path.LineTo(xf+unit, yf+4*unit)
|
||||||
|
|
||||||
op := &vector.FillOptions{
|
op := &ebiten.DrawTrianglesOptions{
|
||||||
Color: color.RGBA{0xdb, 0x56, 0x20, 0xff},
|
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 {
|
func maxCounter(index int) int {
|
||||||
@ -166,10 +192,18 @@ func drawWave(screen *ebiten.Image, counter int) {
|
|||||||
path.LineTo(screenWidth, screenHeight)
|
path.LineTo(screenWidth, screenHeight)
|
||||||
path.LineTo(0, screenHeight)
|
path.LineTo(0, screenHeight)
|
||||||
|
|
||||||
op := &vector.FillOptions{
|
op := &ebiten.DrawTrianglesOptions{
|
||||||
Color: color.RGBA{0x33, 0x66, 0xff, 0xff},
|
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 {
|
type Game struct {
|
||||||
|
34
image.go
34
image.go
@ -213,7 +213,7 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) {
|
|||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
|
|
||||||
srcs := [graphics.ShaderImageNum]*mipmap.Mipmap{img.mipmap}
|
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.
|
// Vertex represents a vertex passed to DrawTriangles.
|
||||||
@ -271,6 +271,19 @@ type DrawTrianglesOptions struct {
|
|||||||
// Address is a sampler address mode.
|
// Address is a sampler address mode.
|
||||||
// The default (zero) value is AddressUnsafe.
|
// The default (zero) value is AddressUnsafe.
|
||||||
Address Address
|
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.
|
// 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}
|
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.
|
// DrawTrianglesShaderOptions represents options for DrawTrianglesShader.
|
||||||
@ -371,6 +384,19 @@ type DrawTrianglesShaderOptions struct {
|
|||||||
// Images is a set of the source images.
|
// Images is a set of the source images.
|
||||||
// All the image must be the same size.
|
// All the image must be the same size.
|
||||||
Images [4]*Image
|
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() {
|
func init() {
|
||||||
@ -485,7 +511,7 @@ func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader
|
|||||||
}
|
}
|
||||||
|
|
||||||
us := shader.convertUniforms(options.Uniforms)
|
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.
|
// 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)
|
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.
|
// 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),
|
Width: float32(w - 2*paddingSize),
|
||||||
Height: float32(h - 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.dispose(false)
|
||||||
i.backend = &backend{
|
i.backend = &backend{
|
||||||
@ -354,7 +354,7 @@ func (i *Image) putOnAtlas() error {
|
|||||||
Width: w,
|
Width: w,
|
||||||
Height: h,
|
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)
|
newI.moveTo(i)
|
||||||
@ -402,13 +402,13 @@ func (i *Image) processSrc(src *Image) {
|
|||||||
// 5: Color G
|
// 5: Color G
|
||||||
// 6: Color B
|
// 6: Color B
|
||||||
// 7: Color Y
|
// 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()
|
backendsM.Lock()
|
||||||
defer backendsM.Unlock()
|
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 {
|
if i.disposed {
|
||||||
panic("atlas: the drawing target image must not be disposed (DrawTriangles)")
|
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 {
|
for _, src := range srcs {
|
||||||
if src == nil {
|
if src == nil {
|
||||||
|
@ -102,7 +102,7 @@ func TestEnsureIsolated(t *testing.T) {
|
|||||||
Width: size,
|
Width: size,
|
||||||
Height: 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
|
want := false
|
||||||
if got := img4.IsOnAtlasForTesting(); got != want {
|
if got := img4.IsOnAtlasForTesting(); got != want {
|
||||||
t.Errorf("got: %v, want: %v", 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.
|
// Check further drawing doesn't cause panic.
|
||||||
// This bug was fixed by 03dcd948.
|
// 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) {
|
func TestReputOnAtlas(t *testing.T) {
|
||||||
@ -179,7 +179,7 @@ func TestReputOnAtlas(t *testing.T) {
|
|||||||
Width: size,
|
Width: size,
|
||||||
Height: 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 {
|
if got, want := img1.IsOnAtlasForTesting(), false; got != want {
|
||||||
t.Errorf("got: %v, want: %v", got, want)
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
}
|
}
|
||||||
@ -191,7 +191,7 @@ func TestReputOnAtlas(t *testing.T) {
|
|||||||
if err := PutImagesOnAtlasForTesting(); err != nil {
|
if err := PutImagesOnAtlasForTesting(); err != nil {
|
||||||
t.Fatal(err)
|
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 {
|
if got, want := img1.IsOnAtlasForTesting(), false; got != want {
|
||||||
t.Errorf("got: %v, want: %v", 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.
|
// 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 {
|
if got, want := img1.IsOnAtlasForTesting(), true; got != want {
|
||||||
t.Errorf("got: %v, want: %v", 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.
|
// 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 {
|
if got, want := img1.IsOnAtlasForTesting(), false; got != want {
|
||||||
t.Errorf("got: %v, want: %v", got, want)
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
}
|
}
|
||||||
@ -255,7 +255,7 @@ func TestReputOnAtlas(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
img1.ReplacePixels(make([]byte, 4*size*size))
|
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 {
|
if got, want := img1.IsOnAtlasForTesting(), false; got != want {
|
||||||
t.Errorf("got: %v, want: %v", 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.
|
// 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 {
|
if got, want := img1.IsOnAtlasForTesting(), false; got != want {
|
||||||
t.Errorf("got: %v, want: %v", got, want)
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
}
|
}
|
||||||
@ -275,7 +275,7 @@ func TestReputOnAtlas(t *testing.T) {
|
|||||||
if err := PutImagesOnAtlasForTesting(); err != nil {
|
if err := PutImagesOnAtlasForTesting(); err != nil {
|
||||||
t.Fatal(err)
|
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 {
|
if got, want := img3.IsOnAtlasForTesting(), false; got != want {
|
||||||
t.Errorf("got: %v, want: %v", got, want)
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
}
|
}
|
||||||
@ -375,7 +375,7 @@ func TestReplacePixelsAfterDrawTriangles(t *testing.T) {
|
|||||||
Width: w,
|
Width: w,
|
||||||
Height: h,
|
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)
|
dst.ReplacePixels(pix)
|
||||||
|
|
||||||
pix, err := dst.Pixels(0, 0, w, h)
|
pix, err := dst.Pixels(0, 0, w, h)
|
||||||
@ -423,7 +423,7 @@ func TestSmallImages(t *testing.T) {
|
|||||||
Width: w,
|
Width: w,
|
||||||
Height: h,
|
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)
|
pix, err := dst.Pixels(0, 0, w, h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -471,7 +471,7 @@ func TestLongImages(t *testing.T) {
|
|||||||
Width: dstW,
|
Width: dstW,
|
||||||
Height: dstH,
|
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)
|
pix, err := dst.Pixels(0, 0, dstW, dstH)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -559,7 +559,7 @@ func TestDisposedAndReputOnAtlas(t *testing.T) {
|
|||||||
Width: size,
|
Width: size,
|
||||||
Height: 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 {
|
if got, want := src.IsOnAtlasForTesting(), false; got != want {
|
||||||
t.Errorf("got: %v, want: %v", got, want)
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
}
|
}
|
||||||
@ -569,7 +569,7 @@ func TestDisposedAndReputOnAtlas(t *testing.T) {
|
|||||||
if err := PutImagesOnAtlasForTesting(); err != nil {
|
if err := PutImagesOnAtlasForTesting(); err != nil {
|
||||||
t.Fatal(err)
|
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 {
|
if got, want := src.IsOnAtlasForTesting(), false; got != want {
|
||||||
t.Errorf("got: %v, want: %v", 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.
|
// 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 {
|
if got, want := src2.IsOnAtlasForTesting(), false; got != want {
|
||||||
t.Errorf("got: %v, want: %v", got, want)
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
}
|
}
|
||||||
@ -630,7 +630,7 @@ func TestImageIsNotReputOnAtlasWithoutUsingAsSource(t *testing.T) {
|
|||||||
if err := PutImagesOnAtlasForTesting(); err != nil {
|
if err := PutImagesOnAtlasForTesting(); err != nil {
|
||||||
t.Fatal(err)
|
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 {
|
if got, want := src2.IsOnAtlasForTesting(), false; got != want {
|
||||||
t.Errorf("got: %v, want: %v", 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.
|
// DrawTriangles draws the src image with the given vertices.
|
||||||
//
|
//
|
||||||
// Copying vertices and indices is the caller's responsibility.
|
// 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 {
|
for _, src := range srcs {
|
||||||
if i == src {
|
if i == src {
|
||||||
panic("buffered: Image.DrawTriangles: source images must be different from the receiver")
|
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 maybeCanAddDelayedCommand() {
|
||||||
if tryAddDelayedCommand(func() error {
|
if tryAddDelayedCommand(func() error {
|
||||||
// Arguments are not copied. Copying is the caller's responsibility.
|
// 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 nil
|
||||||
}) {
|
}) {
|
||||||
return
|
return
|
||||||
@ -238,7 +238,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f
|
|||||||
}
|
}
|
||||||
i.resolvePendingPixels(false)
|
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()
|
i.invalidatePendingPixels()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ type Graphics interface {
|
|||||||
//
|
//
|
||||||
// * float32
|
// * float32
|
||||||
// * []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.
|
// GraphicsNotReady represents that the graphics driver is not ready for recovering from the context lost.
|
||||||
|
@ -55,7 +55,7 @@ type command interface {
|
|||||||
NumIndices() int
|
NumIndices() int
|
||||||
AddNumVertices(n int)
|
AddNumVertices(n int)
|
||||||
AddNumIndices(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 {
|
type size struct {
|
||||||
@ -133,7 +133,7 @@ func (q *commandQueue) appendIndices(indices []uint16, offset uint16) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// EnqueueDrawTrianglesCommand enqueues a drawing-image command.
|
// 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 {
|
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))
|
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.
|
// TODO: If dst is the screen, reorder the command to be the last.
|
||||||
if !split && 0 < len(q.commands) {
|
if !split && 0 < len(q.commands) {
|
||||||
// TODO: Pass offsets and uniforms when merging considers the shader.
|
// 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.AddNumVertices(len(vertices))
|
||||||
last.AddNumIndices(len(indices))
|
last.AddNumIndices(len(indices))
|
||||||
return
|
return
|
||||||
@ -188,6 +188,7 @@ func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.Sh
|
|||||||
srcRegion: srcRegion,
|
srcRegion: srcRegion,
|
||||||
shader: shader,
|
shader: shader,
|
||||||
uniforms: uniforms,
|
uniforms: uniforms,
|
||||||
|
evenOdd: evenOdd,
|
||||||
}
|
}
|
||||||
q.commands = append(q.commands, c)
|
q.commands = append(q.commands, c)
|
||||||
}
|
}
|
||||||
@ -319,6 +320,7 @@ type drawTrianglesCommand struct {
|
|||||||
srcRegion driver.Region
|
srcRegion driver.Region
|
||||||
shader *Shader
|
shader *Shader
|
||||||
uniforms []interface{}
|
uniforms []interface{}
|
||||||
|
evenOdd bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *drawTrianglesCommand) String() string {
|
func (c *drawTrianglesCommand) String() string {
|
||||||
@ -428,7 +430,7 @@ func (c *drawTrianglesCommand) Exec(indexOffset int) error {
|
|||||||
imgs[0] = c.srcs[0].image.ID()
|
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 {
|
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
|
// CanMergeWithDrawTrianglesCommand returns a boolean value indicating whether the other drawTrianglesCommand can be merged
|
||||||
// with the drawTrianglesCommand c.
|
// 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.
|
// If a shader is used, commands are not merged.
|
||||||
//
|
//
|
||||||
// TODO: Merge shader commands considering uniform variables.
|
// TODO: Merge shader commands considering uniform variables.
|
||||||
@ -480,6 +482,9 @@ func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst *Image, srcs
|
|||||||
if c.srcRegion != srcRegion {
|
if c.srcRegion != srcRegion {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if c.evenOdd != evenOdd {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -513,7 +518,7 @@ func (c *replacePixelsCommand) AddNumVertices(n int) {
|
|||||||
func (c *replacePixelsCommand) AddNumIndices(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
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -550,7 +555,7 @@ func (c *pixelsCommand) AddNumVertices(n int) {
|
|||||||
func (c *pixelsCommand) AddNumIndices(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
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -583,7 +588,7 @@ func (c *disposeImageCommand) AddNumVertices(n int) {
|
|||||||
func (c *disposeImageCommand) AddNumIndices(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
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -616,7 +621,7 @@ func (c *disposeShaderCommand) AddNumVertices(n int) {
|
|||||||
func (c *disposeShaderCommand) AddNumIndices(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
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -655,7 +660,7 @@ func (c *newImageCommand) AddNumVertices(n int) {
|
|||||||
func (c *newImageCommand) AddNumIndices(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
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -691,7 +696,7 @@ func (c *newScreenFramebufferImageCommand) AddNumVertices(n int) {
|
|||||||
func (c *newScreenFramebufferImageCommand) AddNumIndices(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
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -726,7 +731,7 @@ func (c *newShaderCommand) AddNumVertices(n int) {
|
|||||||
func (c *newShaderCommand) AddNumIndices(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
|
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
|
// 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.
|
// 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 {
|
if shader == nil {
|
||||||
// Fast path for rendering without a shader (#1355).
|
// Fast path for rendering without a shader (#1355).
|
||||||
img := srcs[0]
|
img := srcs[0]
|
||||||
@ -160,7 +160,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [gra
|
|||||||
}
|
}
|
||||||
i.resolveBufferedReplacePixels()
|
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.
|
// Pixels returns the image's pixels.
|
||||||
|
@ -50,7 +50,7 @@ func TestClear(t *testing.T) {
|
|||||||
Width: w,
|
Width: w,
|
||||||
Height: h,
|
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()
|
pix, err := dst.Pixels()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -81,8 +81,8 @@ func TestReplacePixelsPartAfterDrawTriangles(t *testing.T) {
|
|||||||
Width: w,
|
Width: w,
|
||||||
Height: h,
|
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)
|
||||||
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)
|
dst.ReplacePixels(make([]byte, 4), 0, 0, 1, 1)
|
||||||
|
|
||||||
// TODO: Check the result.
|
// TODO: Check the result.
|
||||||
@ -100,11 +100,11 @@ func TestShader(t *testing.T) {
|
|||||||
Width: w,
|
Width: w,
|
||||||
Height: h,
|
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)
|
ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff)
|
||||||
s := NewShader(&ir)
|
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()
|
pix, err := dst.Pixels()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -296,11 +296,12 @@ FragmentShaderFunc(0, FILTER_SCREEN, ADDRESS_UNSAFE)
|
|||||||
`
|
`
|
||||||
|
|
||||||
type rpsKey struct {
|
type rpsKey struct {
|
||||||
useColorM bool
|
useColorM bool
|
||||||
filter driver.Filter
|
filter driver.Filter
|
||||||
address driver.Address
|
address driver.Address
|
||||||
compositeMode driver.CompositeMode
|
compositeMode driver.CompositeMode
|
||||||
screen bool
|
colorWriteMask bool
|
||||||
|
screen bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type Graphics struct {
|
type Graphics struct {
|
||||||
@ -313,7 +314,9 @@ type Graphics struct {
|
|||||||
rce mtl.RenderCommandEncoder
|
rce mtl.RenderCommandEncoder
|
||||||
|
|
||||||
screenDrawable ca.MetalDrawable
|
screenDrawable ca.MetalDrawable
|
||||||
|
|
||||||
lastDstTexture mtl.Texture
|
lastDstTexture mtl.Texture
|
||||||
|
lastStencilUse bool
|
||||||
|
|
||||||
vb mtl.Buffer
|
vb mtl.Buffer
|
||||||
ib mtl.Buffer
|
ib mtl.Buffer
|
||||||
@ -334,6 +337,18 @@ type Graphics struct {
|
|||||||
pool unsafe.Pointer
|
pool unsafe.Pointer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type stencilMode int
|
||||||
|
|
||||||
|
const (
|
||||||
|
prepareStencil stencilMode = iota
|
||||||
|
drawWithStencil
|
||||||
|
noStencil
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s stencilMode) useStencil() bool {
|
||||||
|
return s != noStencil
|
||||||
|
}
|
||||||
|
|
||||||
var theGraphics Graphics
|
var theGraphics Graphics
|
||||||
|
|
||||||
func Get() *Graphics {
|
func Get() *Graphics {
|
||||||
@ -539,8 +554,9 @@ func (g *Graphics) Reset() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
rpld := mtl.RenderPipelineDescriptor{
|
rpld := mtl.RenderPipelineDescriptor{
|
||||||
VertexFunction: vs,
|
VertexFunction: vs,
|
||||||
FragmentFunction: fs,
|
FragmentFunction: fs,
|
||||||
|
StencilAttachmentPixelFormat: mtl.PixelFormatStencil8,
|
||||||
}
|
}
|
||||||
rpld.ColorAttachments[0].PixelFormat = g.view.colorPixelFormat()
|
rpld.ColorAttachments[0].PixelFormat = g.view.colorPixelFormat()
|
||||||
rpld.ColorAttachments[0].BlendingEnabled = true
|
rpld.ColorAttachments[0].BlendingEnabled = true
|
||||||
@ -548,6 +564,7 @@ func (g *Graphics) Reset() error {
|
|||||||
rpld.ColorAttachments[0].DestinationRGBBlendFactor = mtl.BlendFactorZero
|
rpld.ColorAttachments[0].DestinationRGBBlendFactor = mtl.BlendFactorZero
|
||||||
rpld.ColorAttachments[0].SourceAlphaBlendFactor = mtl.BlendFactorOne
|
rpld.ColorAttachments[0].SourceAlphaBlendFactor = mtl.BlendFactorOne
|
||||||
rpld.ColorAttachments[0].SourceRGBBlendFactor = mtl.BlendFactorOne
|
rpld.ColorAttachments[0].SourceRGBBlendFactor = mtl.BlendFactorOne
|
||||||
|
rpld.ColorAttachments[0].WriteMask = mtl.ColorWriteMaskAll
|
||||||
rps, err := g.view.getMTLDevice().MakeRenderPipelineState(rpld)
|
rps, err := g.view.getMTLDevice().MakeRenderPipelineState(rpld)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -566,42 +583,51 @@ func (g *Graphics) Reset() error {
|
|||||||
driver.FilterLinear,
|
driver.FilterLinear,
|
||||||
} {
|
} {
|
||||||
for c := driver.CompositeModeSourceOver; c <= driver.CompositeModeMax; c++ {
|
for c := driver.CompositeModeSourceOver; c <= driver.CompositeModeMax; c++ {
|
||||||
cmi := 0
|
for _, cwm := range []bool{false, true} {
|
||||||
if cm {
|
cmi := 0
|
||||||
cmi = 1
|
if cm {
|
||||||
}
|
cmi = 1
|
||||||
fs, err := lib.MakeFunction(fmt.Sprintf("FragmentShader_%d_%d_%d", cmi, f, a))
|
}
|
||||||
if err != nil {
|
fs, err := lib.MakeFunction(fmt.Sprintf("FragmentShader_%d_%d_%d", cmi, f, a))
|
||||||
return err
|
if err != nil {
|
||||||
}
|
return err
|
||||||
rpld := mtl.RenderPipelineDescriptor{
|
}
|
||||||
VertexFunction: vs,
|
rpld := mtl.RenderPipelineDescriptor{
|
||||||
FragmentFunction: fs,
|
VertexFunction: vs,
|
||||||
}
|
FragmentFunction: fs,
|
||||||
|
StencilAttachmentPixelFormat: mtl.PixelFormatStencil8,
|
||||||
|
}
|
||||||
|
|
||||||
pix := mtl.PixelFormatRGBA8UNorm
|
pix := mtl.PixelFormatRGBA8UNorm
|
||||||
if screen {
|
if screen {
|
||||||
pix = g.view.colorPixelFormat()
|
pix = g.view.colorPixelFormat()
|
||||||
}
|
}
|
||||||
rpld.ColorAttachments[0].PixelFormat = pix
|
rpld.ColorAttachments[0].PixelFormat = pix
|
||||||
rpld.ColorAttachments[0].BlendingEnabled = true
|
rpld.ColorAttachments[0].BlendingEnabled = true
|
||||||
|
|
||||||
src, dst := c.Operations()
|
src, dst := c.Operations()
|
||||||
rpld.ColorAttachments[0].DestinationAlphaBlendFactor = operationToBlendFactor(dst)
|
rpld.ColorAttachments[0].DestinationAlphaBlendFactor = operationToBlendFactor(dst)
|
||||||
rpld.ColorAttachments[0].DestinationRGBBlendFactor = operationToBlendFactor(dst)
|
rpld.ColorAttachments[0].DestinationRGBBlendFactor = operationToBlendFactor(dst)
|
||||||
rpld.ColorAttachments[0].SourceAlphaBlendFactor = operationToBlendFactor(src)
|
rpld.ColorAttachments[0].SourceAlphaBlendFactor = operationToBlendFactor(src)
|
||||||
rpld.ColorAttachments[0].SourceRGBBlendFactor = operationToBlendFactor(src)
|
rpld.ColorAttachments[0].SourceRGBBlendFactor = operationToBlendFactor(src)
|
||||||
rps, err := g.view.getMTLDevice().MakeRenderPipelineState(rpld)
|
if cwm {
|
||||||
if err != nil {
|
rpld.ColorAttachments[0].WriteMask = mtl.ColorWriteMaskAll
|
||||||
return err
|
} 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.EndEncoding()
|
||||||
g.rce = mtl.RenderCommandEncoder{}
|
g.rce = mtl.RenderCommandEncoder{}
|
||||||
g.lastDstTexture = mtl.Texture{}
|
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 {
|
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() {
|
if g.lastDstTexture != dst.mtlTexture() || g.lastStencilUse != stencilMode.useStencil() {
|
||||||
g.flushRenderCommandEncoderIfNeeded()
|
g.flushRenderCommandEncoderIfNeeded()
|
||||||
}
|
}
|
||||||
|
|
||||||
if g.rce == (mtl.RenderCommandEncoder{}) {
|
if g.rce == (mtl.RenderCommandEncoder{}) {
|
||||||
rpd := mtl.RenderPassDescriptor{}
|
rpd := mtl.RenderPassDescriptor{}
|
||||||
// Even though the destination pixels are not used, mtl.LoadActionDontCare might cause glitches
|
// 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].Texture = t
|
||||||
rpd.ColorAttachments[0].ClearColor = mtl.ClearColor{}
|
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{}) {
|
if g.cb == (mtl.CommandBuffer{}) {
|
||||||
g.cb = g.cq.MakeCommandBuffer()
|
g.cb = g.cq.MakeCommandBuffer()
|
||||||
}
|
}
|
||||||
g.rce = g.cb.MakeRenderCommandEncoder(rpd)
|
g.rce = g.cb.MakeRenderCommandEncoder(rpd)
|
||||||
}
|
}
|
||||||
|
g.lastStencilUse = stencilMode.useStencil()
|
||||||
|
|
||||||
g.rce.SetRenderPipelineState(rps)
|
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)
|
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)
|
g.rce.DrawIndexedPrimitives(mtl.PrimitiveTypeTriangle, indexLen, mtl.IndexTypeUInt16, g.ib, indexOffset*2)
|
||||||
|
|
||||||
return nil
|
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]
|
dst := g.images[dstID]
|
||||||
|
|
||||||
if dst.screen {
|
if dst.screen {
|
||||||
@ -703,18 +779,28 @@ func (g *Graphics) DrawTriangles(dstID driver.ImageID, srcIDs [graphics.ShaderIm
|
|||||||
srcs[i] = g.images[srcID]
|
srcs[i] = g.images[srcID]
|
||||||
}
|
}
|
||||||
|
|
||||||
var rps mtl.RenderPipelineState
|
var rpss [2]mtl.RenderPipelineState
|
||||||
var uniformVars []interface{}
|
var uniformVars []interface{}
|
||||||
if shaderID == driver.InvalidShaderID {
|
if shaderID == driver.InvalidShaderID {
|
||||||
if dst.screen && filter == driver.FilterScreen {
|
if dst.screen && filter == driver.FilterScreen {
|
||||||
rps = g.screenRPS
|
rpss[1] = g.screenRPS
|
||||||
} else {
|
} else {
|
||||||
rps = g.rpss[rpsKey{
|
useColorM := colorM != nil
|
||||||
screen: dst.screen,
|
rpss[0] = g.rpss[rpsKey{
|
||||||
useColorM: colorM != nil,
|
screen: dst.screen,
|
||||||
filter: filter,
|
useColorM: useColorM,
|
||||||
address: address,
|
filter: filter,
|
||||||
compositeMode: mode,
|
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 {
|
} else {
|
||||||
var err error
|
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 {
|
if err != nil {
|
||||||
return err
|
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 {
|
if evenOdd {
|
||||||
return err
|
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
|
return nil
|
||||||
@ -889,6 +988,7 @@ type Image struct {
|
|||||||
height int
|
height int
|
||||||
screen bool
|
screen bool
|
||||||
texture mtl.Texture
|
texture mtl.Texture
|
||||||
|
stencil mtl.Texture
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Image) ID() driver.ImageID {
|
func (i *Image) ID() driver.ImageID {
|
||||||
@ -903,6 +1003,10 @@ func (i *Image) internalSize() (int, int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Image) Dispose() {
|
func (i *Image) Dispose() {
|
||||||
|
if i.stencil != (mtl.Texture{}) {
|
||||||
|
i.stencil.Release()
|
||||||
|
i.stencil = mtl.Texture{}
|
||||||
|
}
|
||||||
if i.texture != (mtl.Texture{}) {
|
if i.texture != (mtl.Texture{}) {
|
||||||
i.texture.Release()
|
i.texture.Release()
|
||||||
i.texture = mtl.Texture{}
|
i.texture = mtl.Texture{}
|
||||||
@ -1022,3 +1126,19 @@ func (i *Image) mtlTexture() mtl.Texture {
|
|||||||
}
|
}
|
||||||
return i.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"
|
"github.com/hajimehoshi/ebiten/v2/internal/shaderir/metal"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type shaderRpsKey struct {
|
||||||
|
compositeMode driver.CompositeMode
|
||||||
|
colorWriteMask bool
|
||||||
|
}
|
||||||
|
|
||||||
type Shader struct {
|
type Shader struct {
|
||||||
id driver.ShaderID
|
id driver.ShaderID
|
||||||
|
|
||||||
ir *shaderir.Program
|
ir *shaderir.Program
|
||||||
fs mtl.Function
|
fs mtl.Function
|
||||||
vs 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) {
|
func newShader(device mtl.Device, id driver.ShaderID, program *shaderir.Program) (*Shader, error) {
|
||||||
s := &Shader{
|
s := &Shader{
|
||||||
id: id,
|
id: id,
|
||||||
ir: program,
|
ir: program,
|
||||||
rpss: map[driver.CompositeMode]mtl.RenderPipelineState{},
|
rpss: map[shaderRpsKey]mtl.RenderPipelineState{},
|
||||||
}
|
}
|
||||||
if err := s.init(device); err != nil {
|
if err := s.init(device); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -83,31 +88,43 @@ func (s *Shader) init(device mtl.Device) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Shader) RenderPipelineState(device mtl.Device, c driver.CompositeMode) (mtl.RenderPipelineState, error) {
|
func (s *Shader) RenderPipelineState(device mtl.Device, compositeMode driver.CompositeMode, colorWriteMask bool) (mtl.RenderPipelineState, error) {
|
||||||
if rps, ok := s.rpss[c]; ok {
|
if rps, ok := s.rpss[shaderRpsKey{
|
||||||
|
compositeMode: compositeMode,
|
||||||
|
colorWriteMask: colorWriteMask,
|
||||||
|
}]; ok {
|
||||||
return rps, nil
|
return rps, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
rpld := mtl.RenderPipelineDescriptor{
|
rpld := mtl.RenderPipelineDescriptor{
|
||||||
VertexFunction: s.vs,
|
VertexFunction: s.vs,
|
||||||
FragmentFunction: s.fs,
|
FragmentFunction: s.fs,
|
||||||
|
StencilAttachmentPixelFormat: mtl.PixelFormatStencil8,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: For the precise pixel format, whether the render target is the screen or not must be considered.
|
// 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].PixelFormat = mtl.PixelFormatRGBA8UNorm
|
||||||
rpld.ColorAttachments[0].BlendingEnabled = true
|
rpld.ColorAttachments[0].BlendingEnabled = true
|
||||||
|
|
||||||
src, dst := c.Operations()
|
src, dst := compositeMode.Operations()
|
||||||
rpld.ColorAttachments[0].DestinationAlphaBlendFactor = operationToBlendFactor(dst)
|
rpld.ColorAttachments[0].DestinationAlphaBlendFactor = operationToBlendFactor(dst)
|
||||||
rpld.ColorAttachments[0].DestinationRGBBlendFactor = operationToBlendFactor(dst)
|
rpld.ColorAttachments[0].DestinationRGBBlendFactor = operationToBlendFactor(dst)
|
||||||
rpld.ColorAttachments[0].SourceAlphaBlendFactor = operationToBlendFactor(src)
|
rpld.ColorAttachments[0].SourceAlphaBlendFactor = operationToBlendFactor(src)
|
||||||
rpld.ColorAttachments[0].SourceRGBBlendFactor = 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)
|
rps, err := device.MakeRenderPipelineState(rpld)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return mtl.RenderPipelineState{}, err
|
return mtl.RenderPipelineState{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.rpss[c] = rps
|
s.rpss[shaderRpsKey{
|
||||||
|
compositeMode: compositeMode,
|
||||||
|
colorWriteMask: colorWriteMask,
|
||||||
|
}] = rps
|
||||||
return rps, nil
|
return rps, nil
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,7 @@ type context struct {
|
|||||||
screenFramebuffer framebufferNative // This might not be the default frame buffer '0' (e.g. iOS).
|
screenFramebuffer framebufferNative // This might not be the default frame buffer '0' (e.g. iOS).
|
||||||
lastFramebuffer framebufferNative
|
lastFramebuffer framebufferNative
|
||||||
lastTexture textureNative
|
lastTexture textureNative
|
||||||
|
lastRenderbuffer renderbufferNative
|
||||||
lastViewportWidth int
|
lastViewportWidth int
|
||||||
lastViewportHeight int
|
lastViewportHeight int
|
||||||
lastCompositeMode driver.CompositeMode
|
lastCompositeMode driver.CompositeMode
|
||||||
@ -68,6 +69,14 @@ func (c *context) bindTexture(t textureNative) {
|
|||||||
c.lastTexture = t
|
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) {
|
func (c *context) bindFramebuffer(f framebufferNative) {
|
||||||
if c.lastFramebuffer.equal(f) {
|
if c.lastFramebuffer.equal(f) {
|
||||||
return
|
return
|
||||||
|
@ -29,17 +29,22 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
textureNative uint32
|
textureNative uint32
|
||||||
framebufferNative uint32
|
renderbufferNative uint32
|
||||||
shader uint32
|
framebufferNative uint32
|
||||||
program uint32
|
shader uint32
|
||||||
buffer uint32
|
program uint32
|
||||||
|
buffer uint32
|
||||||
)
|
)
|
||||||
|
|
||||||
func (t textureNative) equal(rhs textureNative) bool {
|
func (t textureNative) equal(rhs textureNative) bool {
|
||||||
return t == rhs
|
return t == rhs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r renderbufferNative) equal(rhs renderbufferNative) bool {
|
||||||
|
return r == rhs
|
||||||
|
}
|
||||||
|
|
||||||
func (f framebufferNative) equal(rhs framebufferNative) bool {
|
func (f framebufferNative) equal(rhs framebufferNative) bool {
|
||||||
return f == rhs
|
return f == rhs
|
||||||
}
|
}
|
||||||
@ -198,6 +203,38 @@ func (c *context) isTexture(t textureNative) bool {
|
|||||||
panic("opengl: isTexture is not implemented")
|
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) {
|
func (c *context) newFramebuffer(texture textureNative) (framebufferNative, error) {
|
||||||
var f uint32
|
var f uint32
|
||||||
gl.GenFramebuffersEXT(1, &f)
|
gl.GenFramebuffersEXT(1, &f)
|
||||||
@ -220,6 +257,17 @@ func (c *context) newFramebuffer(texture textureNative) (framebufferNative, erro
|
|||||||
return framebufferNative(f), nil
|
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) {
|
func (c *context) setViewportImpl(width, height int) {
|
||||||
gl.Viewport(0, 0, int32(width), int32(height))
|
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)
|
gl.BindBuffer(gl.PIXEL_UNPACK_BUFFER, 0)
|
||||||
return pixels
|
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 (
|
type (
|
||||||
textureNative js.Value
|
textureNative js.Value
|
||||||
framebufferNative js.Value
|
renderbufferNative js.Value
|
||||||
shader js.Value
|
framebufferNative js.Value
|
||||||
buffer js.Value
|
shader js.Value
|
||||||
uniformLocation js.Value
|
buffer js.Value
|
||||||
|
uniformLocation js.Value
|
||||||
|
|
||||||
attribLocation int
|
attribLocation int
|
||||||
programID int
|
programID int
|
||||||
@ -44,6 +45,10 @@ func (t textureNative) equal(rhs textureNative) bool {
|
|||||||
return js.Value(t).Equal(js.Value(rhs))
|
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 {
|
func (f framebufferNative) equal(rhs framebufferNative) bool {
|
||||||
return js.Value(f).Equal(js.Value(rhs))
|
return js.Value(f).Equal(js.Value(rhs))
|
||||||
}
|
}
|
||||||
@ -100,6 +105,7 @@ func (c *context) initGL() {
|
|||||||
attr := js.Global().Get("Object").New()
|
attr := js.Global().Get("Object").New()
|
||||||
attr.Set("alpha", true)
|
attr.Set("alpha", true)
|
||||||
attr.Set("premultipliedAlpha", true)
|
attr.Set("premultipliedAlpha", true)
|
||||||
|
attr.Set("stencil", true)
|
||||||
|
|
||||||
if isWebGL2Available {
|
if isWebGL2Available {
|
||||||
gl = canvas.Call("getContext", "webgl2", attr)
|
gl = canvas.Call("getContext", "webgl2", attr)
|
||||||
@ -165,7 +171,7 @@ func (c *context) newTexture(width, height int) (textureNative, error) {
|
|||||||
gl := c.gl
|
gl := c.gl
|
||||||
t := gl.createTexture.Invoke()
|
t := gl.createTexture.Invoke()
|
||||||
if !t.Truthy() {
|
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))
|
c.bindTexture(textureNative(t))
|
||||||
|
|
||||||
@ -240,6 +246,37 @@ func (c *context) isTexture(t textureNative) bool {
|
|||||||
panic("opengl: isTexture is not implemented")
|
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) {
|
func (c *context) newFramebuffer(t textureNative) (framebufferNative, error) {
|
||||||
gl := c.gl
|
gl := c.gl
|
||||||
f := gl.createFramebuffer.Invoke()
|
f := gl.createFramebuffer.Invoke()
|
||||||
@ -253,6 +290,18 @@ func (c *context) newFramebuffer(t textureNative) (framebufferNative, error) {
|
|||||||
return framebufferNative(f), nil
|
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) {
|
func (c *context) setViewportImpl(width, height int) {
|
||||||
gl := c.gl
|
gl := c.gl
|
||||||
gl.viewport.Invoke(0, 0, width, height)
|
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)
|
gl.bindBuffer.Invoke(gles.PIXEL_UNPACK_BUFFER, nil)
|
||||||
return jsutil.Uint8ArrayToSlice(arr, l)
|
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 (
|
type (
|
||||||
textureNative uint32
|
textureNative uint32
|
||||||
framebufferNative uint32
|
renderbufferNative uint32
|
||||||
shader uint32
|
framebufferNative uint32
|
||||||
program uint32
|
shader uint32
|
||||||
buffer uint32
|
program uint32
|
||||||
|
buffer uint32
|
||||||
)
|
)
|
||||||
|
|
||||||
func (t textureNative) equal(rhs textureNative) bool {
|
func (t textureNative) equal(rhs textureNative) bool {
|
||||||
return t == rhs
|
return t == rhs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r renderbufferNative) equal(rhs renderbufferNative) bool {
|
||||||
|
return r == rhs
|
||||||
|
}
|
||||||
|
|
||||||
func (f framebufferNative) equal(rhs framebufferNative) bool {
|
func (f framebufferNative) equal(rhs framebufferNative) bool {
|
||||||
return f == rhs
|
return f == rhs
|
||||||
}
|
}
|
||||||
@ -185,6 +190,34 @@ func (c *context) isTexture(t textureNative) bool {
|
|||||||
return c.ctx.IsTexture(uint32(t))
|
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) {
|
func (c *context) newFramebuffer(texture textureNative) (framebufferNative, error) {
|
||||||
f := c.ctx.GenFramebuffers(1)[0]
|
f := c.ctx.GenFramebuffers(1)[0]
|
||||||
if f <= 0 {
|
if f <= 0 {
|
||||||
@ -206,6 +239,17 @@ func (c *context) newFramebuffer(texture textureNative) (framebufferNative, erro
|
|||||||
return framebufferNative(f), nil
|
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) {
|
func (c *context) setViewportImpl(width, height int) {
|
||||||
c.ctx.Viewport(0, 0, int32(width), int32(height))
|
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.
|
// As PBO is not used in mobiles, leave this unimplemented so far.
|
||||||
panic("opengl: getBufferSubData is not implemented for mobiles")
|
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)
|
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]
|
destination := g.images[dstID]
|
||||||
|
|
||||||
if !destination.pbo.equal(*new(buffer)) {
|
if !destination.pbo.equal(*new(buffer)) {
|
||||||
@ -308,7 +308,20 @@ func (g *Graphics) DrawTriangles(dstID driver.ImageID, srcIDs [graphics.ShaderIm
|
|||||||
return err
|
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
|
g.context.drawElements(indexLen, indexOffset*2) // 2 is uint16 size in bytes
|
||||||
|
if evenOdd {
|
||||||
|
g.context.disableStencilTest()
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ type Image struct {
|
|||||||
id driver.ImageID
|
id driver.ImageID
|
||||||
graphics *Graphics
|
graphics *Graphics
|
||||||
texture textureNative
|
texture textureNative
|
||||||
|
stencil renderbufferNative
|
||||||
framebuffer *framebuffer
|
framebuffer *framebuffer
|
||||||
pbo buffer
|
pbo buffer
|
||||||
width int
|
width int
|
||||||
@ -48,6 +49,9 @@ func (i *Image) Dispose() {
|
|||||||
if !i.texture.equal(*new(textureNative)) {
|
if !i.texture.equal(*new(textureNative)) {
|
||||||
i.graphics.context.deleteTexture(i.texture)
|
i.graphics.context.deleteTexture(i.texture)
|
||||||
}
|
}
|
||||||
|
if !i.stencil.equal(*new(renderbufferNative)) {
|
||||||
|
i.graphics.context.deleteRenderbuffer(i.stencil)
|
||||||
|
}
|
||||||
|
|
||||||
i.graphics.removeImage(i)
|
i.graphics.removeImage(i)
|
||||||
}
|
}
|
||||||
@ -105,6 +109,27 @@ func (i *Image) ensureFramebuffer() error {
|
|||||||
return nil
|
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) {
|
func (i *Image) ReplacePixels(args []*driver.ReplacePixelsArgs) {
|
||||||
if i.screen {
|
if i.screen {
|
||||||
panic("opengl: ReplacePixels cannot be called on the screen, that doesn't have a texture")
|
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)
|
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 {
|
if len(indices) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -164,7 +164,7 @@ func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderImageNum]*Mipmap, vertices [
|
|||||||
imgs[i] = src.orig
|
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()
|
m.disposeMipmaps()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,7 +226,7 @@ func (m *Mipmap) level(level int) *buffered.Image {
|
|||||||
Width: float32(w2),
|
Width: float32(w2),
|
||||||
Height: float32(h2),
|
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
|
m.imgs[level] = s
|
||||||
|
|
||||||
return m.imgs[level]
|
return m.imgs[level]
|
||||||
|
@ -76,6 +76,7 @@ type drawTrianglesHistoryItem struct {
|
|||||||
srcRegion driver.Region
|
srcRegion driver.Region
|
||||||
shader *Shader
|
shader *Shader
|
||||||
uniforms []interface{}
|
uniforms []interface{}
|
||||||
|
evenOdd bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Image represents an image that can be restored when GL context is lost.
|
// 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),
|
Width: float32(sw),
|
||||||
Height: float32(sh),
|
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
|
// Overwrite the history as if the image newImg is created only by ReplacePixels. Now drawTrianglesHistory
|
||||||
// and basePixels cannot be mixed.
|
// and basePixels cannot be mixed.
|
||||||
@ -247,7 +248,7 @@ func clearImage(i *graphicscommand.Image) {
|
|||||||
Width: float32(dw),
|
Width: float32(dw),
|
||||||
Height: float32(dh),
|
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.
|
// 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
|
// 5: Color G
|
||||||
// 6: Color B
|
// 6: Color B
|
||||||
// 7: Color Y
|
// 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 {
|
if i.priority {
|
||||||
panic("restorable: DrawTriangles cannot be called on a priority image")
|
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 {
|
if srcstale || i.screen || !NeedsRestoring() || i.volatile {
|
||||||
i.makeStale()
|
i.makeStale()
|
||||||
} else {
|
} 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
|
var s *graphicscommand.Shader
|
||||||
@ -391,11 +392,11 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [gra
|
|||||||
}
|
}
|
||||||
s = shader.shader
|
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.
|
// 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 {
|
if i.stale || i.volatile || i.screen {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -427,6 +428,7 @@ func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderImageNum]*Image,
|
|||||||
srcRegion: srcRegion,
|
srcRegion: srcRegion,
|
||||||
shader: shader,
|
shader: shader,
|
||||||
uniforms: uniforms,
|
uniforms: uniforms,
|
||||||
|
evenOdd: evenOdd,
|
||||||
}
|
}
|
||||||
i.drawTrianglesHistory = append(i.drawTrianglesHistory, item)
|
i.drawTrianglesHistory = append(i.drawTrianglesHistory, item)
|
||||||
}
|
}
|
||||||
@ -605,7 +607,7 @@ func (i *Image) restore() error {
|
|||||||
}
|
}
|
||||||
imgs[i] = img.image
|
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 {
|
if len(i.drawTrianglesHistory) > 0 {
|
||||||
|
@ -137,7 +137,7 @@ func TestRestoreChain(t *testing.T) {
|
|||||||
Width: 1,
|
Width: 1,
|
||||||
Height: 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 {
|
if err := ResolveStaleImages(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -185,10 +185,10 @@ func TestRestoreChain2(t *testing.T) {
|
|||||||
Width: w,
|
Width: w,
|
||||||
Height: h,
|
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[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)
|
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++ {
|
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 {
|
if err := ResolveStaleImages(); err != nil {
|
||||||
@ -234,10 +234,10 @@ func TestRestoreOverrideSource(t *testing.T) {
|
|||||||
Width: w,
|
Width: w,
|
||||||
Height: h,
|
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)
|
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)
|
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)
|
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 {
|
if err := ResolveStaleImages(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -323,23 +323,23 @@ func TestRestoreComplexGraph(t *testing.T) {
|
|||||||
Height: h,
|
Height: h,
|
||||||
}
|
}
|
||||||
var offsets [graphics.ShaderImageNum - 1][2]float32
|
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)
|
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)
|
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)
|
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)
|
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)
|
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)
|
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)
|
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)
|
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 {
|
if err := ResolveStaleImages(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -437,8 +437,8 @@ func TestRestoreRecursive(t *testing.T) {
|
|||||||
Width: w,
|
Width: w,
|
||||||
Height: h,
|
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)
|
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)
|
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 {
|
if err := ResolveStaleImages(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -538,7 +538,7 @@ func TestDrawTrianglesAndReplacePixels(t *testing.T) {
|
|||||||
Width: 2,
|
Width: 2,
|
||||||
Height: 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)
|
||||||
img1.ReplacePixels([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 0, 0, 2, 1)
|
img1.ReplacePixels([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 0, 0, 2, 1)
|
||||||
|
|
||||||
if err := ResolveStaleImages(); err != nil {
|
if err := ResolveStaleImages(); err != nil {
|
||||||
@ -581,8 +581,8 @@ func TestDispose(t *testing.T) {
|
|||||||
Width: 1,
|
Width: 1,
|
||||||
Height: 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)
|
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)
|
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()
|
img1.Dispose()
|
||||||
|
|
||||||
if err := ResolveStaleImages(); err != nil {
|
if err := ResolveStaleImages(); err != nil {
|
||||||
@ -696,7 +696,7 @@ func TestReplacePixelsOnly(t *testing.T) {
|
|||||||
Width: 1,
|
Width: 1,
|
||||||
Height: 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)
|
img0.ReplacePixels([]byte{5, 6, 7, 8}, 0, 0, 1, 1)
|
||||||
|
|
||||||
// BasePixelsForTesting is available without GPU accessing.
|
// BasePixelsForTesting is available without GPU accessing.
|
||||||
@ -756,7 +756,7 @@ func TestReadPixelsFromVolatileImage(t *testing.T) {
|
|||||||
Width: w,
|
Width: w,
|
||||||
Height: h,
|
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
|
// Read the pixels. If the implementation is correct, dst tries to read its pixels from GPU due to being
|
||||||
// stale.
|
// stale.
|
||||||
@ -783,7 +783,7 @@ func TestAllowReplacePixelsAfterDrawTriangles(t *testing.T) {
|
|||||||
Width: w,
|
Width: w,
|
||||||
Height: h,
|
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)
|
dst.ReplacePixels(make([]byte, 4*w*h), 0, 0, w, h)
|
||||||
// ReplacePixels for a whole image doesn't panic.
|
// ReplacePixels for a whole image doesn't panic.
|
||||||
}
|
}
|
||||||
@ -807,7 +807,7 @@ func TestDisallowReplacePixelsForPartAfterDrawTriangles(t *testing.T) {
|
|||||||
Width: w,
|
Width: w,
|
||||||
Height: h,
|
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)
|
dst.ReplacePixels(make([]byte, 4), 0, 0, 1, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -884,7 +884,7 @@ func TestMutateSlices(t *testing.T) {
|
|||||||
Width: w,
|
Width: w,
|
||||||
Height: h,
|
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 {
|
for i := range vs {
|
||||||
vs[i] = 0
|
vs[i] = 0
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ func clearImage(img *Image, w, h int) {
|
|||||||
Width: float32(w),
|
Width: float32(w),
|
||||||
Height: float32(h),
|
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) {
|
func TestShader(t *testing.T) {
|
||||||
@ -63,7 +63,7 @@ func TestShader(t *testing.T) {
|
|||||||
Width: 1,
|
Width: 1,
|
||||||
Height: 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 {
|
if err := ResolveStaleImages(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -99,7 +99,7 @@ func TestShaderChain(t *testing.T) {
|
|||||||
Width: 1,
|
Width: 1,
|
||||||
Height: 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 {
|
if err := ResolveStaleImages(); err != nil {
|
||||||
@ -138,7 +138,7 @@ func TestShaderMultipleSources(t *testing.T) {
|
|||||||
Width: 1,
|
Width: 1,
|
||||||
Height: 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.
|
// Clear one of the sources after DrawTriangles. dst should not be affected.
|
||||||
clearImage(srcs[0], 1, 1)
|
clearImage(srcs[0], 1, 1)
|
||||||
@ -180,7 +180,7 @@ func TestShaderMultipleSourcesOnOneTexture(t *testing.T) {
|
|||||||
Width: 1,
|
Width: 1,
|
||||||
Height: 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.
|
// Clear one of the sources after DrawTriangles. dst should not be affected.
|
||||||
clearImage(srcs[0], 3, 1)
|
clearImage(srcs[0], 3, 1)
|
||||||
@ -211,7 +211,7 @@ func TestShaderDispose(t *testing.T) {
|
|||||||
Width: 1,
|
Width: 1,
|
||||||
Height: 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
|
// Dispose the shader. This should invalidates all the images using this shader i.e., all the images become
|
||||||
// stale.
|
// 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
|
package vector
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"image"
|
|
||||||
"image/color"
|
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/v2"
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
"github.com/hajimehoshi/ebiten/v2/vector/internal/triangulate"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
type point struct {
|
||||||
emptyImage = ebiten.NewImage(3, 3)
|
x float32
|
||||||
emptySubImage = emptyImage.SubImage(image.Rect(1, 1, 2, 2)).(*ebiten.Image)
|
y float32
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
emptyImage.Fill(color.White)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Path represents a collection of path segments.
|
// Path represents a collection of path segments.
|
||||||
type Path struct {
|
type Path struct {
|
||||||
segs [][]triangulate.Point
|
segs [][]point
|
||||||
cur triangulate.Point
|
cur point
|
||||||
}
|
}
|
||||||
|
|
||||||
// MoveTo skips the current position of the path to the given position (x, y) without adding any strokes.
|
// 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) {
|
func (p *Path) MoveTo(x, y float32) {
|
||||||
p.cur = triangulate.Point{X: x, Y: y}
|
p.cur = point{x: x, y: y}
|
||||||
p.segs = append(p.segs, []triangulate.Point{p.cur})
|
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).
|
// 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).
|
// LineTo updates the current position to (x, y).
|
||||||
func (p *Path) LineTo(x, y float32) {
|
func (p *Path) LineTo(x, y float32) {
|
||||||
if len(p.segs) == 0 {
|
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.segs[len(p.segs)-1] = append(p.segs[len(p.segs)-1], point{x: x, y: y})
|
||||||
p.cur = triangulate.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).
|
// 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.
|
// QuadTo adds a quadratic Bézier curve to the path.
|
||||||
func (p *Path) QuadTo(cpx, cpy, x, y float32) {
|
func (p *Path) QuadTo(cpx, cpy, x, y float32) {
|
||||||
|
// TODO: Split more appropriate number of segments.
|
||||||
c := p.cur
|
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) {
|
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
|
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
|
yf := (1-t)*(1-t)*c.y + 2*t*(1-t)*cpy + t*t*y
|
||||||
p.LineTo(xf, yf)
|
p.LineTo(xf, yf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CubicTo adds a cubic Bézier curve to the path.
|
// CubicTo adds a cubic Bézier curve to the path.
|
||||||
func (p *Path) CubicTo(cp0x, cp0y, cp1x, cp1y, x, y float32) {
|
func (p *Path) CubicTo(cp0x, cp0y, cp1x, cp1y, x, y float32) {
|
||||||
|
// TODO: Split more appropriate number of segments.
|
||||||
c := p.cur
|
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) {
|
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
|
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
|
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)
|
p.LineTo(xf, yf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FillOptions represents options to fill a path.
|
// AppendVerticesAndIndices appends vertices and indices for this path and returns them.
|
||||||
type FillOptions struct {
|
// AppendVerticesAndIndices works in a similar way to the built-in append function.
|
||||||
// Color is a color to fill with.
|
// If the arguments are nils, AppendVerticesAndIndices returns new slices.
|
||||||
Color color.Color
|
//
|
||||||
}
|
// The returned vertice's SrcX and SrcY are 0, and ColorR, ColorG, ColorB, and ColorA are 1.
|
||||||
|
//
|
||||||
// Fill fills the region of the path with the given options op.
|
// The returned values are intended to be passed to DrawTriangles or DrawTrianglesShader with EvenOdd option
|
||||||
func (p *Path) Fill(dst *ebiten.Image, op *FillOptions) {
|
// in order to render a complex polygon like a concave polygon, a polygon with holes, or a self-intersecting polygon.
|
||||||
var vertices []ebiten.Vertex
|
func (p *Path) AppendVerticesAndIndices(vertices []ebiten.Vertex, indices []uint16) ([]ebiten.Vertex, []uint16) {
|
||||||
var indices []uint16
|
// TODO: Add tests.
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
var base uint16
|
var base uint16
|
||||||
for _, seg := range p.segs {
|
for _, seg := range p.segs {
|
||||||
for _, pt := range seg {
|
if len(seg) < 3 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for i, pt := range seg {
|
||||||
vertices = append(vertices, ebiten.Vertex{
|
vertices = append(vertices, ebiten.Vertex{
|
||||||
DstX: pt.X,
|
DstX: pt.x,
|
||||||
DstY: pt.Y,
|
DstY: pt.y,
|
||||||
SrcX: 0,
|
SrcX: 0,
|
||||||
SrcY: 0,
|
SrcY: 0,
|
||||||
ColorR: rf,
|
ColorR: 1,
|
||||||
ColorG: gf,
|
ColorG: 1,
|
||||||
ColorB: bf,
|
ColorB: 1,
|
||||||
ColorA: af,
|
ColorA: 1,
|
||||||
})
|
})
|
||||||
}
|
if i < 2 {
|
||||||
for _, idx := range triangulate.Triangulate(seg) {
|
continue
|
||||||
indices = append(indices, idx+base)
|
}
|
||||||
|
indices = append(indices, base, base+uint16(i-1), base+uint16(i))
|
||||||
}
|
}
|
||||||
base += uint16(len(seg))
|
base += uint16(len(seg))
|
||||||
}
|
}
|
||||||
dst.DrawTriangles(vertices, indices, emptySubImage, nil)
|
return vertices, indices
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user