ebiten: Allow rendering on a sub-image by scissor test

Fixes #1255
This commit is contained in:
Hajime Hoshi 2020-11-07 19:14:06 +09:00
parent e4e8e7254e
commit ed028110cf
29 changed files with 610 additions and 174 deletions

95
examples/clip/main.go Normal file
View File

@ -0,0 +1,95 @@
// Copyright 2020 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.
// +build example
package main
import (
"bytes"
"image"
_ "image/jpeg"
"log"
"math"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/examples/resources/images"
)
const (
screenWidth = 640
screenHeight = 480
)
var (
gophersImage *ebiten.Image
)
type Game struct {
count int
}
func (g *Game) Update() error {
g.count++
return nil
}
func (g *Game) Draw(screen *ebiten.Image) {
w, h := gophersImage.Size()
op := &ebiten.DrawImageOptions{}
// Move the image's center to the screen's upper-left corner.
// This is a preparation for rotating. When geometry matrices are applied,
// the origin point is the upper-left corner.
op.GeoM.Translate(-float64(w)/2, -float64(h)/2)
// Rotate the image. As a result, the anchor point of this rotate is
// the center of the image.
op.GeoM.Rotate(float64(g.count%360) * 2 * math.Pi / 360)
// Move the image to the screen's center.
op.GeoM.Translate(screenWidth/2, screenHeight/2)
// Use SubImage to clip the rendering region.
const size = 60
r := image.Rect(320-size, 240-size, 320+size, 240+size)
screen.SubImage(r).(*ebiten.Image).DrawImage(gophersImage, op)
}
func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
return screenWidth, screenHeight
}
func main() {
// Decode image from a byte slice instead of a file so that
// this example works in any working directory.
// If you want to use a file, there are some options:
// 1) Use os.Open and pass the file to the image decoder.
// This is a very regular way, but doesn't work on browsers.
// 2) Use ebitenutil.OpenFile and pass the file to the image decoder.
// This works even on browsers.
// 3) Use ebitenutil.NewImageFromFile to create an ebiten.Image directly from a file.
// This also works on browsers.
img, _, err := image.Decode(bytes.NewReader(images.Gophers_jpg))
if err != nil {
log.Fatal(err)
}
gophersImage = ebiten.NewImageFromImage(img)
ebiten.SetWindowSize(screenWidth, screenHeight)
ebiten.SetWindowTitle("Clip (Ebiten Demo)")
if err := ebiten.RunGame(&Game{}); err != nil {
log.Fatal(err)
}
}

View File

@ -158,9 +158,12 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) {
return
}
// TODO: Implement this.
if i.isSubImage() {
panic("ebiten: render to a sub-image is not implemented (DrawImage)")
dstBounds := i.Bounds()
dstRegion := driver.Region{
X: float32(dstBounds.Min.X),
Y: float32(dstBounds.Min.Y),
Width: float32(dstBounds.Dx()),
Height: float32(dstBounds.Dy()),
}
// Calculate vertices before locking because the user can do anything in
@ -183,7 +186,7 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) {
is := graphics.QuadIndices()
srcs := [graphics.ShaderImageNum]*mipmap.Mipmap{img.mipmap}
i.mipmap.DrawTriangles(srcs, vs, is, options.ColorM.impl, mode, filter, driver.AddressUnsafe, 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, canSkipMipmap(options.GeoM, filter))
}
// Vertex represents a vertex passed to DrawTriangles.
@ -267,10 +270,6 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o
return
}
if i.isSubImage() {
panic("ebiten: render to a sub-image is not implemented (DrawTriangles)")
}
if len(indices)%3 != 0 {
panic("ebiten: len(indices) % 3 must be 0")
}
@ -279,6 +278,14 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o
}
// TODO: Check the maximum value of indices and len(vertices)?
dstBounds := i.Bounds()
dstRegion := driver.Region{
X: float32(dstBounds.Min.X),
Y: float32(dstBounds.Min.Y),
Width: float32(dstBounds.Dx()),
Height: float32(dstBounds.Dy()),
}
if options == nil {
options = &DrawTrianglesOptions{}
}
@ -315,7 +322,7 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o
srcs := [graphics.ShaderImageNum]*mipmap.Mipmap{img.mipmap}
i.mipmap.DrawTriangles(srcs, vs, is, options.ColorM.impl, mode, filter, address, 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, false)
}
// DrawTrianglesShaderOptions represents options for DrawTrianglesShader.
@ -366,10 +373,6 @@ func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader
return
}
if i.isSubImage() {
panic("ebiten: render to a sub-image is not implemented (DrawTrianglesShader)")
}
if len(indices)%3 != 0 {
panic("ebiten: len(indices) % 3 must be 0")
}
@ -378,6 +381,14 @@ func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader
}
// TODO: Check the maximum value of indices and len(vertices)?
dstBounds := i.Bounds()
dstRegion := driver.Region{
X: float32(dstBounds.Min.X),
Y: float32(dstBounds.Min.Y),
Width: float32(dstBounds.Dx()),
Height: float32(dstBounds.Dy()),
}
if options == nil {
options = &DrawTrianglesShaderOptions{}
}
@ -447,7 +458,7 @@ func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader
}
us := shader.convertUniforms(options.Uniforms)
i.mipmap.DrawTriangles(imgs, vs, is, nil, mode, driver.FilterNearest, driver.AddressUnsafe, sr, offsets, shader.shader, us, false)
i.mipmap.DrawTriangles(imgs, vs, is, nil, mode, driver.FilterNearest, driver.AddressUnsafe, dstRegion, sr, offsets, shader.shader, us, false)
}
// DrawRectShaderOptions represents options for DrawRectShader.
@ -498,9 +509,12 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR
return
}
// TODO: Implement this.
if i.isSubImage() {
panic("ebiten: rendering to a sub-image is not implemented (DrawRectShader)")
dstBounds := i.Bounds()
dstRegion := driver.Region{
X: float32(dstBounds.Min.X),
Y: float32(dstBounds.Min.Y),
Width: float32(dstBounds.Dx()),
Height: float32(dstBounds.Dy()),
}
if options == nil {
@ -556,7 +570,7 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR
}
us := shader.convertUniforms(options.Uniforms)
i.mipmap.DrawTriangles(imgs, vs, is, nil, mode, driver.FilterNearest, driver.AddressUnsafe, 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, canSkipMipmap(options.GeoM, driver.FilterNearest))
}
// SubImage returns an image representing the portion of the image p visible through r.

View File

@ -2181,3 +2181,29 @@ func TestImageNewImageFromImageWithZeroSize(t *testing.T) {
img := image.NewRGBA(image.Rect(0, 0, 0, 1))
_ = NewImageFromImage(img)
}
func TestImageClip(t *testing.T) {
const (
w = 16
h = 16
)
dst := NewImage(w, h)
src := NewImage(w, h)
dst.Fill(color.RGBA{0xff, 0, 0, 0xff})
src.Fill(color.RGBA{0, 0xff, 0, 0xff})
dst.SubImage(image.Rect(4, 5, 12, 14)).(*Image).DrawImage(src, nil)
for j := 0; j < h; j++ {
for i := 0; i < w; i++ {
got := dst.At(i, j).(color.RGBA)
want := color.RGBA{0xff, 0, 0, 0xff}
if 4 <= i && i < 12 && 5 <= j && j < 14 {
want = color.RGBA{0, 0xff, 0, 0xff}
}
if got != want {
t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want)
}
}
}
}

View File

@ -249,7 +249,7 @@ func (i *Image) replacePendingPixels(pix []byte, x, y, width, height int) {
// DrawTriangles draws the src image with the given vertices.
//
// Copying vertices and indices is the caller's responsibility.
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion 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{}) {
for _, src := range srcs {
if i == src {
panic("buffered: Image.DrawTriangles: source images must be different from the receiver")
@ -259,7 +259,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f
if maybeCanAddDelayedCommand() {
if tryAddDelayedCommand(func() error {
// Arguments are not copied. Copying is the caller's responsibility.
i.DrawTriangles(srcs, vertices, indices, colorm, mode, filter, address, sourceRegion, subimageOffsets, shader, uniforms)
i.DrawTriangles(srcs, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, subimageOffsets, shader, uniforms)
return nil
}) {
return
@ -285,7 +285,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f
}
i.resolvePendingPixels(false)
i.img.DrawTriangles(imgs, vertices, indices, colorm, mode, filter, address, sourceRegion, subimageOffsets, s, uniforms)
i.img.DrawTriangles(imgs, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, subimageOffsets, s, uniforms)
i.invalidatePendingPixels()
}

View File

@ -50,7 +50,7 @@ type Graphics interface {
// Draw draws an image onto another image.
//
// TODO: Merge this into DrawShader.
Draw(dst, src ImageID, indexLen int, indexOffset int, mode CompositeMode, colorM *affine.ColorM, filter Filter, address Address, sourceRegion Region) error
Draw(dst, src ImageID, indexLen int, indexOffset int, mode CompositeMode, colorM *affine.ColorM, filter Filter, address Address, dstRegion, srcRegion Region) error
// DrawShader draws the shader.
//
@ -58,7 +58,7 @@ type Graphics interface {
//
// * float32
// * []float32
DrawShader(dst ImageID, srcs [graphics.ShaderImageNum]ImageID, offsets [graphics.ShaderImageNum - 1][2]float32, shader ShaderID, indexLen int, indexOffset int, sourceRegion Region, mode CompositeMode, uniforms []interface{}) error
DrawShader(dst ImageID, srcs [graphics.ShaderImageNum]ImageID, offsets [graphics.ShaderImageNum - 1][2]float32, shader ShaderID, indexLen int, indexOffset int, dstRegion, srcRegion Region, mode CompositeMode, uniforms []interface{}) error
}
// GraphicsNotReady represents that the graphics driver is not ready for recovering from the context lost.

View File

@ -54,7 +54,7 @@ type command interface {
NumIndices() int
AddNumVertices(n int)
AddNumIndices(n int)
CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion 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) bool
}
type size struct {
@ -132,7 +132,7 @@ func (q *commandQueue) appendIndices(indices []uint16, offset uint16) {
}
// EnqueueDrawTrianglesCommand enqueues a drawing-image command.
func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, vertices []float32, indices []uint16, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion 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{}) {
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))
}
@ -153,10 +153,10 @@ func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.Sh
if srcs[0] != nil {
w, h := srcs[0].InternalSize()
sourceRegion.X /= float32(w)
sourceRegion.Y /= float32(h)
sourceRegion.Width /= float32(w)
sourceRegion.Height /= float32(h)
srcRegion.X /= float32(w)
srcRegion.Y /= float32(h)
srcRegion.Width /= float32(w)
srcRegion.Height /= float32(h)
for i := range offsets {
offsets[i][0] /= float32(w)
offsets[i][1] /= float32(h)
@ -166,7 +166,7 @@ func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.Sh
// TODO: If dst is the screen, reorder the command to be the last.
if !split && 0 < len(q.commands) {
// TODO: Pass offsets and uniforms when merging considers the shader.
if last := q.commands[len(q.commands)-1]; last.CanMergeWithDrawTrianglesCommand(dst, srcs, color, mode, filter, address, sourceRegion, shader) {
if last := q.commands[len(q.commands)-1]; last.CanMergeWithDrawTrianglesCommand(dst, srcs, color, mode, filter, address, dstRegion, srcRegion, shader) {
last.AddNumVertices(len(vertices))
last.AddNumIndices(len(indices))
return
@ -183,7 +183,8 @@ func (q *commandQueue) EnqueueDrawTrianglesCommand(dst *Image, srcs [graphics.Sh
mode: mode,
filter: filter,
address: address,
sourceRegion: sourceRegion,
dstRegion: dstRegion,
srcRegion: srcRegion,
shader: shader,
uniforms: uniforms,
}
@ -317,7 +318,8 @@ type drawTrianglesCommand struct {
mode driver.CompositeMode
filter driver.Filter
address driver.Address
sourceRegion driver.Region
dstRegion driver.Region
srcRegion driver.Region
shader *Shader
uniforms []interface{}
}
@ -420,9 +422,9 @@ func (c *drawTrianglesCommand) Exec(indexOffset int) error {
imgs[i] = src.image.ID()
}
return theGraphicsDriver.DrawShader(c.dst.image.ID(), imgs, c.offsets, c.shader.shader.ID(), c.nindices, indexOffset, c.sourceRegion, c.mode, c.uniforms)
return theGraphicsDriver.DrawShader(c.dst.image.ID(), imgs, c.offsets, c.shader.shader.ID(), c.nindices, indexOffset, c.dstRegion, c.srcRegion, c.mode, c.uniforms)
}
return theGraphicsDriver.Draw(c.dst.image.ID(), c.srcs[0].image.ID(), c.nindices, indexOffset, c.mode, c.color, c.filter, c.address, c.sourceRegion)
return theGraphicsDriver.Draw(c.dst.image.ID(), c.srcs[0].image.ID(), c.nindices, indexOffset, c.mode, c.color, c.filter, c.address, c.dstRegion, c.srcRegion)
}
func (c *drawTrianglesCommand) NumVertices() int {
@ -443,7 +445,7 @@ func (c *drawTrianglesCommand) AddNumIndices(n int) {
// CanMergeWithDrawTrianglesCommand returns a boolean value indicating whether the other drawTrianglesCommand can be merged
// with the drawTrianglesCommand c.
func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst *Image, srcs [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion 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) bool {
// If a shader is used, commands are not merged.
//
// TODO: Merge shader commands considering uniform variables.
@ -468,7 +470,10 @@ func (c *drawTrianglesCommand) CanMergeWithDrawTrianglesCommand(dst *Image, srcs
if c.address != address {
return false
}
if c.sourceRegion != sourceRegion {
if c.dstRegion != dstRegion {
return false
}
if c.srcRegion != srcRegion {
return false
}
return true
@ -504,7 +509,7 @@ func (c *replacePixelsCommand) AddNumVertices(n int) {
func (c *replacePixelsCommand) AddNumIndices(n int) {
}
func (c *replacePixelsCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion 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) bool {
return false
}
@ -532,7 +537,7 @@ func (c *syncCommand) AddNumVertices(n int) {
func (c *syncCommand) AddNumIndices(n int) {
}
func (c *syncCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader) bool {
func (c *syncCommand) 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 {
return false
}
@ -573,7 +578,7 @@ func (c *pixelsCommand) AddNumVertices(n int) {
func (c *pixelsCommand) AddNumIndices(n int) {
}
func (c *pixelsCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion 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) bool {
return false
}
@ -606,7 +611,7 @@ func (c *disposeImageCommand) AddNumVertices(n int) {
func (c *disposeImageCommand) AddNumIndices(n int) {
}
func (c *disposeImageCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion 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) bool {
return false
}
@ -639,7 +644,7 @@ func (c *disposeShaderCommand) AddNumVertices(n int) {
func (c *disposeShaderCommand) AddNumIndices(n int) {
}
func (c *disposeShaderCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion 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) bool {
return false
}
@ -678,7 +683,7 @@ func (c *newImageCommand) AddNumVertices(n int) {
func (c *newImageCommand) AddNumIndices(n int) {
}
func (c *newImageCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion 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) bool {
return false
}
@ -714,7 +719,7 @@ func (c *newScreenFramebufferImageCommand) AddNumVertices(n int) {
func (c *newScreenFramebufferImageCommand) AddNumIndices(n int) {
}
func (c *newScreenFramebufferImageCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion 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) bool {
return false
}
@ -749,7 +754,7 @@ func (c *newShaderCommand) AddNumVertices(n int) {
func (c *newShaderCommand) AddNumIndices(n int) {
}
func (c *newShaderCommand) CanMergeWithDrawTrianglesCommand(dst *Image, src [graphics.ShaderImageNum]*Image, color *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion 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) bool {
return false
}

View File

@ -150,7 +150,7 @@ func (i *Image) InternalSize() (int, int) {
//
// If the source image is not specified, i.e., src is nil and there is no image in the uniform variables, the
// elements for the source image are not used.
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, vertices []float32, indices []uint16, clr *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion 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{}) {
if i.lastCommand == lastCommandNone {
if !i.screen && mode != driver.CompositeModeClear {
panic("graphicscommand: the image must be cleared first")
@ -177,7 +177,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [gra
}
i.resolveBufferedReplacePixels()
theCommandQueue.EnqueueDrawTrianglesCommand(i, srcs, offsets, vertices, indices, clr, mode, filter, address, sourceRegion, shader, uniforms)
theCommandQueue.EnqueueDrawTrianglesCommand(i, srcs, offsets, vertices, indices, clr, mode, filter, address, dstRegion, srcRegion, shader, uniforms)
if i.lastCommand == lastCommandNone && !i.screen {
i.lastCommand = lastCommandClear

View File

@ -44,7 +44,13 @@ func TestClear(t *testing.T) {
vs := quadVertices(w/2, h/2)
is := graphics.QuadIndices()
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
dr := driver.Region{
X: 0,
Y: 0,
Width: w,
Height: h,
}
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
pix, err := dst.Pixels()
if err != nil {
@ -74,8 +80,14 @@ func TestReplacePixelsPartAfterDrawTriangles(t *testing.T) {
dst := NewImage(w, h)
vs := quadVertices(w/2, h/2)
is := graphics.QuadIndices()
dst.DrawTriangles([graphics.ShaderImageNum]*Image{clr}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
dr := driver.Region{
X: 0,
Y: 0,
Width: w,
Height: h,
}
dst.DrawTriangles([graphics.ShaderImageNum]*Image{clr}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
dst.ReplacePixels(make([]byte, 4), 0, 0, 1, 1)
}
@ -85,11 +97,17 @@ func TestShader(t *testing.T) {
dst := NewImage(w, h)
vs := quadVertices(w, h)
is := graphics.QuadIndices()
dst.DrawTriangles([graphics.ShaderImageNum]*Image{clr}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
dr := driver.Region{
X: 0,
Y: 0,
Width: w,
Height: h,
}
dst.DrawTriangles([graphics.ShaderImageNum]*Image{clr}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff)
s := NewShader(&ir)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, 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)
pix, err := dst.Pixels()
if err != nil {

View File

@ -611,7 +611,7 @@ func (g *Graphics) Reset() error {
return nil
}
func (g *Graphics) draw(rps mtl.RenderPipelineState, dst *Image, 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{}) error {
g.view.update()
rpd := mtl.RenderPassDescriptor{}
@ -653,6 +653,12 @@ func (g *Graphics) draw(rps mtl.RenderPipelineState, dst *Image, srcs [graphics.
ZNear: -1,
ZFar: 1,
})
rce.SetScissorRect(mtl.ScissorRect{
X: int(dstRegion.X),
Y: int(dstRegion.Y),
Width: int(dstRegion.Width),
Height: int(dstRegion.Height),
})
rce.SetVertexBuffer(g.vb, 0, 0)
for i, u := range uniforms {
@ -680,7 +686,7 @@ func (g *Graphics) draw(rps mtl.RenderPipelineState, dst *Image, srcs [graphics.
return nil
}
func (g *Graphics) Draw(dstID, srcID driver.ImageID, indexLen int, indexOffset int, mode driver.CompositeMode, colorM *affine.ColorM, filter driver.Filter, address driver.Address, sourceRegion driver.Region) error {
func (g *Graphics) Draw(dstID, srcID driver.ImageID, indexLen int, indexOffset int, mode driver.CompositeMode, colorM *affine.ColorM, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region) error {
dst := g.images[dstID]
dst.waitUntilSyncFinishes()
@ -718,13 +724,13 @@ func (g *Graphics) Draw(dstID, srcID driver.ImageID, indexLen int, indexOffset i
esTranslate,
scale,
[]float32{
sourceRegion.X,
sourceRegion.Y,
sourceRegion.X + sourceRegion.Width,
sourceRegion.Y + sourceRegion.Height,
srcRegion.X,
srcRegion.Y,
srcRegion.X + srcRegion.Width,
srcRegion.Y + srcRegion.Height,
},
}
if err := g.draw(rps, dst, srcs, indexLen, indexOffset, uniforms); err != nil {
if err := g.draw(rps, dst, dstRegion, srcs, indexLen, indexOffset, uniforms); err != nil {
return err
}
return nil
@ -947,7 +953,7 @@ func (i *Image) ReplacePixels(args []*driver.ReplacePixelsArgs) {
bce.EndEncoding()
}
func (g *Graphics) DrawShader(dstID driver.ImageID, srcIDs [graphics.ShaderImageNum]driver.ImageID, offsets [graphics.ShaderImageNum - 1][2]float32, shader driver.ShaderID, indexLen int, indexOffset int, sourceRegion driver.Region, mode driver.CompositeMode, uniforms []interface{}) error {
func (g *Graphics) DrawShader(dstID driver.ImageID, srcIDs [graphics.ShaderImageNum]driver.ImageID, offsets [graphics.ShaderImageNum - 1][2]float32, shader driver.ShaderID, indexLen int, indexOffset int, dstRegion, srcRegion driver.Region, mode driver.CompositeMode, uniforms []interface{}) error {
dst := g.images[dstID]
dst.waitUntilSyncFinishes()
@ -987,11 +993,11 @@ func (g *Graphics) DrawShader(dstID driver.ImageID, srcIDs [graphics.ShaderImage
us[graphics.TextureSourceOffsetsUniformVariableIndex] = uoffsets
// Set the source region's origin of texture0.
uorigin := []float32{float32(sourceRegion.X), float32(sourceRegion.Y)}
uorigin := []float32{float32(srcRegion.X), float32(srcRegion.Y)}
us[graphics.TextureSourceRegionOriginUniformVariableIndex] = uorigin
// Set the source region's size of texture0.
ussize := []float32{float32(sourceRegion.Width), float32(sourceRegion.Height)}
ussize := []float32{float32(srcRegion.Width), float32(srcRegion.Height)}
us[graphics.TextureSourceRegionSizeUniformVariableIndex] = ussize
// Set the additional uniform variables.
@ -1000,7 +1006,7 @@ func (g *Graphics) DrawShader(dstID driver.ImageID, srcIDs [graphics.ShaderImage
us[offset+i] = v
}
if err := g.draw(rps, dst, srcs, indexLen, indexOffset, us); err != nil {
if err := g.draw(rps, dst, dstRegion, srcs, indexLen, indexOffset, us); err != nil {
return err
}
return nil

View File

@ -650,6 +650,13 @@ func (rce RenderCommandEncoder) SetViewport(viewport Viewport) {
C.RenderCommandEncoder_SetViewport(rce.commandEncoder, viewport.c())
}
// SetScissorRect sets the scissor rectangle for a fragment scissor test.
//
// Reference: https://developer.apple.com/documentation/metal/mtlrendercommandencoder/1515583-setscissorrect
func (rce RenderCommandEncoder) SetScissorRect(scissorRect ScissorRect) {
C.RenderCommandEncoder_SetScissorRect(rce.commandEncoder, scissorRect.c())
}
// SetVertexBuffer sets a buffer for the vertex shader function at an index
// in the buffer argument table with an offset that specifies the start of the data.
//
@ -909,3 +916,22 @@ func (v *Viewport) c() C.struct_Viewport {
ZFar: C.double(v.ZFar),
}
}
// ScissorRect represents a rectangle for the scissor fragment test.
//
// Reference: https://developer.apple.com/documentation/metal/mtlscissorrect
type ScissorRect struct {
X int
Y int
Width int
Height int
}
func (s *ScissorRect) c() C.struct_ScissorRect {
return C.struct_ScissorRect{
X: C.uint_t(s.X),
Y: C.uint_t(s.Y),
Width: C.uint_t(s.Width),
Height: C.uint_t(s.Height),
}
}

View File

@ -103,6 +103,13 @@ struct Viewport {
double ZFar;
};
struct ScissorRect {
uint_t X;
uint_t Y;
uint_t Width;
uint_t Height;
};
struct Device CreateSystemDefaultDevice();
struct Devices CopyAllDevices();
@ -139,6 +146,8 @@ void RenderCommandEncoder_SetRenderPipelineState(void *renderCommandEncoder,
void *renderPipelineState);
void RenderCommandEncoder_SetViewport(void *renderCommandEncoder,
struct Viewport viewport);
void RenderCommandEncoder_SetScissorRect(void *renderCommandEncoder,
struct ScissorRect scissorRect);
void RenderCommandEncoder_SetVertexBuffer(void *renderCommandEncoder,
void *buffer, uint_t offset,
uint_t index);

View File

@ -209,6 +209,17 @@ void RenderCommandEncoder_SetViewport(void *renderCommandEncoder,
}];
}
void RenderCommandEncoder_SetScissorRect(void *renderCommandEncoder,
struct ScissorRect scissorRect) {
[(id<MTLRenderCommandEncoder>)renderCommandEncoder
setScissorRect:(MTLScissorRect){
.x = scissorRect.X,
.y = scissorRect.Y,
.width = scissorRect.Width,
.height = scissorRect.Height,
}];
}
void RenderCommandEncoder_SetVertexBuffer(void *renderCommandEncoder,
void *buffer, uint_t offset,
uint_t index) {

View File

@ -116,6 +116,7 @@ func (c *context) reset() error {
c.lastViewportHeight = 0
c.lastCompositeMode = driver.CompositeModeUnknown
gl.Enable(gl.BLEND)
gl.Enable(gl.SCISSOR_TEST)
c.blendFunc(driver.CompositeModeSourceOver)
@ -135,6 +136,10 @@ func (c *context) blendFunc(mode driver.CompositeMode) {
gl.BlendFunc(uint32(s2), uint32(d2))
}
func (c *context) scissor(x, y, width, height int) {
gl.Scissor(int32(x), int32(y), int32(width), int32(height))
}
func (c *context) newTexture(width, height int) (textureNative, error) {
var t uint32
gl.GenTextures(1, &t)

View File

@ -104,6 +104,7 @@ var (
nearest js.Value
noError js.Value
rgba js.Value
scissorTest js.Value
texture2d js.Value
textureMagFilter js.Value
textureMinFilter js.Value
@ -159,6 +160,7 @@ func init() {
nearest = contextPrototype.Get("NEAREST")
noError = contextPrototype.Get("NO_ERROR")
rgba = contextPrototype.Get("RGBA")
scissorTest = contextPrototype.Get("SCISSOR_TEST")
texture0 = contextPrototype.Get("TEXTURE0").Int()
texture2d = contextPrototype.Get("TEXTURE_2D")
textureMagFilter = contextPrototype.Get("TEXTURE_MAG_FILTER")
@ -222,6 +224,7 @@ func (c *context) reset() error {
}
gl := c.gl
gl.Call("enable", blend)
gl.Call("enable", scissorTest)
c.blendFunc(driver.CompositeModeSourceOver)
f := gl.Call("getParameter", framebufferBinding)
c.screenFramebuffer = framebufferNative(f)
@ -240,6 +243,11 @@ func (c *context) blendFunc(mode driver.CompositeMode) {
gl.Call("blendFunc", int(s2), int(d2))
}
func (c *context) scissor(x, y, width, height int) {
gl := c.gl
gl.Call("scissor", x, y, width, height)
}
func (c *context) newTexture(width, height int) (textureNative, error) {
c.ensureGL()
gl := c.gl

View File

@ -106,6 +106,7 @@ func (c *context) reset() error {
c.lastViewportHeight = 0
c.lastCompositeMode = driver.CompositeModeUnknown
c.ctx.Enable(gles.BLEND)
c.ctx.Enable(gles.SCISSOR_TEST)
c.blendFunc(driver.CompositeModeSourceOver)
f := make([]int32, 1)
c.ctx.GetIntegerv(f, gles.FRAMEBUFFER_BINDING)
@ -124,6 +125,10 @@ func (c *context) blendFunc(mode driver.CompositeMode) {
c.ctx.BlendFunc(uint32(s2), uint32(d2))
}
func (c *context) scissor(x, y, width, height int) {
c.ctx.Scissor(int32(x), int32(y), int32(width), int32(height))
}
func (c *context) newTexture(width, height int) (textureNative, error) {
t := c.ctx.GenTextures(1)[0]
if t <= 0 {

View File

@ -57,6 +57,7 @@ const (
TEXTURE_WRAP_S = 0x2802
TEXTURE_WRAP_T = 0x2803
TRIANGLES = 0x0004
SCISSOR_TEST = 0x0C11
UNPACK_ALIGNMENT = 0x0CF5
UNSIGNED_BYTE = 0x1401
UNSIGNED_SHORT = 0x1403

View File

@ -147,6 +147,7 @@ package gl
// typedef void (APIENTRYP GPLINKPROGRAM)(GLuint program);
// typedef void (APIENTRYP GPPIXELSTOREI)(GLenum pname, GLint param);
// typedef void (APIENTRYP GPREADPIXELS)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void * pixels);
// typedef void (APIENTRYP GPSCISSOR)(GLint x, GLint y, GLsizei width, GLsizei height);
// typedef void (APIENTRYP GPSHADERSOURCE)(GLuint shader, GLsizei count, const GLchar *const* string, const GLint * length);
// typedef void (APIENTRYP GPTEXIMAGE2D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * pixels);
// typedef void (APIENTRYP GPTEXPARAMETERI)(GLenum target, GLenum pname, GLint param);
@ -320,6 +321,9 @@ package gl
// static void glowReadPixels(GPREADPIXELS fnptr, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void * pixels) {
// (*fnptr)(x, y, width, height, format, type, pixels);
// }
// static void glowScissor(GPSCISSOR fnptr, GLint x, GLint y, GLsizei width, GLsizei height) {
// (*fnptr)(x, y, width, height);
// }
// static void glowShaderSource(GPSHADERSOURCE fnptr, GLuint shader, GLsizei count, const GLchar *const* string, const GLint * length) {
// (*fnptr)(shader, count, string, length);
// }
@ -428,6 +432,7 @@ var (
gpLinkProgram C.GPLINKPROGRAM
gpPixelStorei C.GPPIXELSTOREI
gpReadPixels C.GPREADPIXELS
gpScissor C.GPSCISSOR
gpShaderSource C.GPSHADERSOURCE
gpTexImage2D C.GPTEXIMAGE2D
gpTexParameteri C.GPTEXPARAMETERI
@ -661,6 +666,10 @@ func ReadPixels(x int32, y int32, width int32, height int32, format uint32, xtyp
C.glowReadPixels(gpReadPixels, (C.GLint)(x), (C.GLint)(y), (C.GLsizei)(width), (C.GLsizei)(height), (C.GLenum)(format), (C.GLenum)(xtype), pixels)
}
func Scissor(x int32, y int32, width int32, height int32) {
C.glowScissor(gpScissor, (C.GLint)(x), (C.GLint)(y), (C.GLsizei)(width), (C.GLsizei)(height))
}
func ShaderSource(shader uint32, count int32, xstring **uint8, length *int32) {
C.glowShaderSource(gpShaderSource, (C.GLuint)(shader), (C.GLsizei)(count), (**C.GLchar)(unsafe.Pointer(xstring)), (*C.GLint)(unsafe.Pointer(length)))
}
@ -884,6 +893,10 @@ func InitWithProcAddrFunc(getProcAddr func(name string) unsafe.Pointer) error {
if gpReadPixels == nil {
return errors.New("glReadPixels")
}
gpScissor = (C.GPSCISSOR)(getProcAddr("glScissor"))
if gpScissor == nil {
return errors.New("glScissor")
}
gpShaderSource = (C.GPSHADERSOURCE)(getProcAddr("glShaderSource"))
if gpShaderSource == nil {
return errors.New("glShaderSource")

View File

@ -62,6 +62,7 @@ var (
gpLinkProgram uintptr
gpPixelStorei uintptr
gpReadPixels uintptr
gpScissor uintptr
gpShaderSource uintptr
gpTexImage2D uintptr
gpTexParameteri uintptr
@ -295,6 +296,10 @@ func ReadPixels(x int32, y int32, width int32, height int32, format uint32, xtyp
syscall.Syscall9(gpReadPixels, 7, uintptr(x), uintptr(y), uintptr(width), uintptr(height), uintptr(format), uintptr(xtype), uintptr(pixels), 0, 0)
}
func Scissor(x int32, y int32, width int32, height int32) {
syscall.Syscall6(gpScissor, 4, uintptr(x), uintptr(y), uintptr(width), uintptr(height), 0, 0)
}
func ShaderSource(shader uint32, count int32, xstring **uint8, length *int32) {
syscall.Syscall6(gpShaderSource, 4, uintptr(shader), uintptr(count), uintptr(unsafe.Pointer(xstring)), uintptr(unsafe.Pointer(length)), 0, 0)
}
@ -518,6 +523,10 @@ func InitWithProcAddrFunc(getProcAddr func(name string) uintptr) error {
if gpReadPixels == 0 {
return errors.New("glReadPixels")
}
gpScissor = getProcAddr("glScissor")
if gpScissor == 0 {
return errors.New("glScissor")
}
gpShaderSource = getProcAddr("glShaderSource")
if gpShaderSource == 0 {
return errors.New("glShaderSource")

View File

@ -54,6 +54,7 @@ const (
NO_ERROR = 0
READ_WRITE = 0x88BA
RGBA = 0x1908
SCISSOR_TEST = 0x0C11
TEXTURE0 = 0x84C0
TEXTURE_2D = 0x0DE1
TEXTURE_MAG_FILTER = 0x2800

View File

@ -242,6 +242,10 @@ func (DefaultContext) ReadPixels(dst []byte, x int32, y int32, width int32, heig
C.glReadPixels(C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height), C.GLenum(format), C.GLenum(xtype), unsafe.Pointer(&dst[0]))
}
func (DefaultContext) Scissor(x, y, width, height int32) {
C.glScissor(C.GLint(x), C.GLint(y), C.GLsizei(width), C.GLsizei(height))
}
func (DefaultContext) ShaderSource(shader uint32, xstring string) {
s, free := cStringPtr(xstring)
defer free()

View File

@ -224,6 +224,10 @@ func (g *GomobileContext) ReadPixels(dst []byte, x int32, y int32, width int32,
g.ctx.ReadPixels(dst, int(x), int(y), int(width), int(height), gl.Enum(format), gl.Enum(xtype))
}
func (g *GomobileContext) Scissor(x, y, width, height int32) {
g.ctx.Scissor(x, y, width, height)
}
func (g *GomobileContext) ShaderSource(shader uint32, xstring string) {
g.ctx.ShaderSource(gl.Shader{Value: shader}, xstring)
}

View File

@ -58,6 +58,7 @@ type Context interface {
LinkProgram(program uint32)
PixelStorei(pname uint32, param int32)
ReadPixels(dst []byte, x int32, y int32, width int32, height int32, format uint32, xtype uint32)
Scissor(x, y, width, height int32)
ShaderSource(shader uint32, xstring string)
TexImage2D(target uint32, level int32, internalformat int32, width int32, height int32, format uint32, xtype uint32, pixels []byte)
TexParameteri(target uint32, pname uint32, param int32)

View File

@ -148,7 +148,7 @@ func (g *Graphics) SetVertices(vertices []float32, indices []uint16) {
g.context.elementArrayBufferSubData(indices)
}
func (g *Graphics) Draw(dst, src driver.ImageID, indexLen int, indexOffset int, mode driver.CompositeMode, colorM *affine.ColorM, filter driver.Filter, address driver.Address, sourceRegion driver.Region) error {
func (g *Graphics) Draw(dst, src driver.ImageID, indexLen int, indexOffset int, mode driver.CompositeMode, colorM *affine.ColorM, filter driver.Filter, address driver.Address, dstRegion, srcRegion driver.Region) error {
destination := g.images[dst]
source := g.images[src]
@ -157,6 +157,12 @@ func (g *Graphics) Draw(dst, src driver.ImageID, indexLen int, indexOffset int,
if err := destination.setViewport(); err != nil {
return err
}
g.context.scissor(
int(dstRegion.X),
int(dstRegion.Y),
int(dstRegion.Width),
int(dstRegion.Height),
)
g.context.blendFunc(mode)
program := g.state.programs[programKey{
@ -176,10 +182,10 @@ func (g *Graphics) Draw(dst, src driver.ImageID, indexLen int, indexOffset int,
}, uniformVariable{
name: "source_region",
value: []float32{
sourceRegion.X,
sourceRegion.Y,
sourceRegion.X + sourceRegion.Width,
sourceRegion.Y + sourceRegion.Height,
srcRegion.X,
srcRegion.Y,
srcRegion.X + srcRegion.Width,
srcRegion.Y + srcRegion.Height,
},
typ: shaderir.Type{Main: shaderir.Vec4},
})
@ -284,7 +290,7 @@ func (g *Graphics) removeShader(shader *Shader) {
delete(g.shaders, shader.id)
}
func (g *Graphics) DrawShader(dst driver.ImageID, srcs [graphics.ShaderImageNum]driver.ImageID, offsets [graphics.ShaderImageNum - 1][2]float32, shader driver.ShaderID, indexLen int, indexOffset int, sourceRegion driver.Region, mode driver.CompositeMode, uniforms []interface{}) error {
func (g *Graphics) DrawShader(dst driver.ImageID, srcs [graphics.ShaderImageNum]driver.ImageID, offsets [graphics.ShaderImageNum - 1][2]float32, shader driver.ShaderID, indexLen int, indexOffset int, dstRegion, srcRegion driver.Region, mode driver.CompositeMode, uniforms []interface{}) error {
d := g.images[dst]
s := g.shaders[shader]
@ -293,6 +299,12 @@ func (g *Graphics) DrawShader(dst driver.ImageID, srcs [graphics.ShaderImageNum]
if err := d.setViewport(); err != nil {
return err
}
g.context.scissor(
int(dstRegion.X),
int(dstRegion.Y),
int(dstRegion.Width),
int(dstRegion.Height),
)
g.context.blendFunc(mode)
us := make([]uniformVariable, graphics.PreservedUniformVariablesNum+len(uniforms))
@ -331,14 +343,14 @@ func (g *Graphics) DrawShader(dst driver.ImageID, srcs [graphics.ShaderImageNum]
us[idx].typ = s.ir.Uniforms[idx]
}
{
origin := []float32{float32(sourceRegion.X), float32(sourceRegion.Y)}
origin := []float32{float32(srcRegion.X), float32(srcRegion.Y)}
const idx = graphics.TextureSourceRegionOriginUniformVariableIndex
us[idx].name = fmt.Sprintf("U%d", idx)
us[idx].value = origin
us[idx].typ = s.ir.Uniforms[idx]
}
{
size := []float32{float32(sourceRegion.Width), float32(sourceRegion.Height)}
size := []float32{float32(srcRegion.Width), float32(srcRegion.Height)}
const idx = graphics.TextureSourceRegionSizeUniformVariableIndex
us[idx].name = fmt.Sprintf("U%d", idx)
us[idx].value = size

View File

@ -91,7 +91,7 @@ func (m *Mipmap) Pixels(x, y, width, height int) ([]byte, error) {
return m.orig.Pixels(x, y, width, height)
}
func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderImageNum]*Mipmap, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion 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{}, canSkipMipmap bool) {
if len(indices) == 0 {
return
}
@ -170,7 +170,7 @@ func (m *Mipmap) DrawTriangles(srcs [graphics.ShaderImageNum]*Mipmap, vertices [
imgs[i] = src.orig
}
m.orig.DrawTriangles(imgs, vertices, indices, colorm, mode, filter, address, sourceRegion, subimageOffsets, s, uniforms)
m.orig.DrawTriangles(imgs, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, subimageOffsets, s, uniforms)
m.disposeMipmaps()
}
@ -225,7 +225,14 @@ func (m *Mipmap) level(level int) *buffered.Image {
}
s := buffered.NewImage(w2, h2)
s.SetVolatile(m.volatile)
s.DrawTriangles([graphics.ShaderImageNum]*buffered.Image{src}, vs, is, nil, driver.CompositeModeCopy, filter, driver.AddressUnsafe, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
dstRegion := driver.Region{
X: 0,
Y: 0,
Width: float32(w2),
Height: float32(h2),
}
s.DrawTriangles([graphics.ShaderImageNum]*buffered.Image{src}, vs, is, nil, driver.CompositeModeCopy, filter, driver.AddressUnsafe, dstRegion, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
m.imgs[level] = s
return m.imgs[level]

View File

@ -77,7 +77,8 @@ type drawTrianglesHistoryItem struct {
mode driver.CompositeMode
filter driver.Filter
address driver.Address
sourceRegion driver.Region
dstRegion driver.Region
srcRegion driver.Region
shader *Shader
uniforms []interface{}
}
@ -269,7 +270,13 @@ func fillImage(i *graphicscommand.Image, clr color.RGBA) {
is := graphics.QuadIndices()
srcs := [graphics.ShaderImageNum]*graphicscommand.Image{emptyImage.image}
var offsets [graphics.ShaderImageNum - 1][2]float32
i.DrawTriangles(srcs, offsets, vs, is, nil, compositemode, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
dstRegion := driver.Region{
X: 0,
Y: 0,
Width: float32(dw),
Height: float32(dh),
}
i.DrawTriangles(srcs, offsets, vs, is, nil, compositemode, driver.FilterNearest, driver.AddressUnsafe, dstRegion, driver.Region{}, nil, nil)
}
// BasePixelsForTesting returns the image's basePixels for testing.
@ -361,7 +368,7 @@ func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) {
// 5: Color G
// 6: Color B
// 7: Color Y
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion 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{}) {
if i.priority {
panic("restorable: DrawTriangles cannot be called on a priority image")
}
@ -385,7 +392,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [gra
if srcstale || i.screen || !needsRestoring() || i.volatile {
i.makeStale()
} else {
i.appendDrawTrianglesHistory(srcs, offsets, vertices, indices, colorm, mode, filter, address, sourceRegion, shader, uniforms)
i.appendDrawTrianglesHistory(srcs, offsets, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, shader, uniforms)
}
var s *graphicscommand.Shader
@ -402,11 +409,11 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [gra
}
s = shader.shader
}
i.image.DrawTriangles(imgs, offsets, vertices, indices, colorm, mode, filter, address, sourceRegion, s, uniforms)
i.image.DrawTriangles(imgs, offsets, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, s, uniforms)
}
// 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, sourceRegion 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{}) {
if i.stale || i.volatile || i.screen {
return
}
@ -433,7 +440,8 @@ func (i *Image) appendDrawTrianglesHistory(srcs [graphics.ShaderImageNum]*Image,
mode: mode,
filter: filter,
address: address,
sourceRegion: sourceRegion,
dstRegion: dstRegion,
srcRegion: srcRegion,
shader: shader,
uniforms: uniforms,
}
@ -625,7 +633,7 @@ func (i *Image) restore() error {
}
imgs[i] = img.image
}
gimg.DrawTriangles(imgs, c.offsets, c.vertices, c.indices, c.colorm, c.mode, c.filter, c.address, c.sourceRegion, 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)
}
if len(i.drawTrianglesHistory) > 0 {

View File

@ -131,7 +131,13 @@ func TestRestoreChain(t *testing.T) {
for i := 0; i < num-1; i++ {
vs := quadVertices(1, 1, 0, 0)
is := graphics.QuadIndices()
imgs[i+1].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[i]}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
dr := driver.Region{
X: 0,
Y: 0,
Width: 1,
Height: 1,
}
imgs[i+1].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[i]}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
}
if err := ResolveStaleImages(); err != nil {
t.Fatal(err)
@ -173,10 +179,16 @@ func TestRestoreChain2(t *testing.T) {
imgs[8].ReplacePixels([]byte{clr8.R, clr8.G, clr8.B, clr8.A}, 0, 0, w, h)
is := graphics.QuadIndices()
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, 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, driver.Region{}, nil, nil)
dr := driver.Region{
X: 0,
Y: 0,
Width: w,
Height: h,
}
imgs[8].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[7]}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
imgs[9].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[8]}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
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, 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)
}
if err := ResolveStaleImages(); err != nil {
@ -216,10 +228,16 @@ func TestRestoreOverrideSource(t *testing.T) {
clr1 := color.RGBA{0x00, 0x00, 0x01, 0xff}
img1.ReplacePixels([]byte{clr0.R, clr0.G, clr0.B, clr0.A}, 0, 0, w, h)
is := graphics.QuadIndices()
img2.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, 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, driver.Region{}, nil, nil)
dr := driver.Region{
X: 0,
Y: 0,
Width: w,
Height: h,
}
img2.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
img3.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 0, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
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, 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)
if err := ResolveStaleImages(); err != nil {
t.Fatal(err)
}
@ -298,24 +316,30 @@ func TestRestoreComplexGraph(t *testing.T) {
}()
vs := quadVertices(w, h, 0, 0)
is := graphics.QuadIndices()
dr := driver.Region{
X: 0,
Y: 0,
Width: w,
Height: h,
}
var offsets [graphics.ShaderImageNum - 1][2]float32
img3.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, 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)
vs = quadVertices(w, h, 1, 0)
img3.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, 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)
vs = quadVertices(w, h, 1, 0)
img4.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, 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)
vs = quadVertices(w, h, 2, 0)
img4.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, 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)
vs = quadVertices(w, h, 0, 0)
img5.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, 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)
vs = quadVertices(w, h, 0, 0)
img6.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, 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)
vs = quadVertices(w, h, 1, 0)
img6.DrawTriangles([graphics.ShaderImageNum]*Image{img4}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, 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)
vs = quadVertices(w, h, 0, 0)
img7.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, 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)
vs = quadVertices(w, h, 2, 0)
img7.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, offsets, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, 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)
if err := ResolveStaleImages(); err != nil {
t.Fatal(err)
}
@ -407,8 +431,14 @@ func TestRestoreRecursive(t *testing.T) {
img0.Dispose()
}()
is := graphics.QuadIndices()
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 1, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, 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, driver.Region{}, nil, nil)
dr := driver.Region{
X: 0,
Y: 0,
Width: w,
Height: h,
}
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 1, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(w, h, 1, 0), is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
if err := ResolveStaleImages(); err != nil {
t.Fatal(err)
}
@ -502,7 +532,13 @@ func TestDrawTrianglesAndReplacePixels(t *testing.T) {
vs := quadVertices(1, 1, 0, 0)
is := graphics.QuadIndices()
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
dr := driver.Region{
X: 0,
Y: 0,
Width: 2,
Height: 1,
}
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
img1.ReplacePixels([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 0, 0, 2, 1)
if err := ResolveStaleImages(); err != nil {
@ -539,8 +575,14 @@ func TestDispose(t *testing.T) {
defer img2.Dispose()
is := graphics.QuadIndices()
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, 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, driver.Region{}, nil, nil)
dr := driver.Region{
X: 0,
Y: 0,
Width: 1,
Height: 1,
}
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
img1.Dispose()
if err := ResolveStaleImages(); err != nil {
@ -648,7 +690,13 @@ func TestReplacePixelsOnly(t *testing.T) {
vs := quadVertices(1, 1, 0, 0)
is := graphics.QuadIndices()
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
dr := driver.Region{
X: 0,
Y: 0,
Width: 1,
Height: 1,
}
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img0}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
img0.ReplacePixels([]byte{5, 6, 7, 8}, 0, 0, 1, 1)
// BasePixelsForTesting is available without GPU accessing.
@ -702,7 +750,13 @@ func TestReadPixelsFromVolatileImage(t *testing.T) {
src.ReplacePixels(pix, 0, 0, w, h)
vs := quadVertices(1, 1, 0, 0)
is := graphics.QuadIndices()
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
dr := driver.Region{
X: 0,
Y: 0,
Width: w,
Height: h,
}
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
// Read the pixels. If the implementation is correct, dst tries to read its pixels from GPU due to being
// stale.
@ -723,7 +777,13 @@ func TestAllowReplacePixelsAfterDrawTriangles(t *testing.T) {
vs := quadVertices(w, h, 0, 0)
is := graphics.QuadIndices()
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
dr := driver.Region{
X: 0,
Y: 0,
Width: w,
Height: h,
}
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
dst.ReplacePixels(make([]byte, 4*w*h), 0, 0, w, h)
// ReplacePixels for a whole image doesn't panic.
}
@ -741,7 +801,13 @@ func TestDisallowReplacePixelsForPartAfterDrawTriangles(t *testing.T) {
vs := quadVertices(w, h, 0, 0)
is := graphics.QuadIndices()
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
dr := driver.Region{
X: 0,
Y: 0,
Width: w,
Height: h,
}
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
dst.ReplacePixels(make([]byte, 4), 0, 0, 1, 1)
}
@ -830,7 +896,13 @@ func TestFill2(t *testing.T) {
dst := NewImage(w, h)
vs := quadVertices(w, h, 0, 0)
is := graphics.QuadIndices()
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
dr := driver.Region{
X: 0,
Y: 0,
Width: w,
Height: h,
}
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
// Fill src with a different color. This should not affect dst.
src.Fill(color.RGBA{0, 0xff, 0, 0xff})
@ -869,7 +941,13 @@ func TestMutateSlices(t *testing.T) {
vs := quadVertices(w, h, 0, 0)
is := make([]uint16, len(graphics.QuadIndices()))
copy(is, graphics.QuadIndices())
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
dr := driver.Region{
X: 0,
Y: 0,
Width: w,
Height: h,
}
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, nil, nil)
for i := range vs {
vs[i] = 0
}

View File

@ -30,7 +30,13 @@ func TestShader(t *testing.T) {
ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff)
s := NewShader(&ir)
img.DrawTriangles([graphics.ShaderImageNum]*Image{}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, nil)
dr := driver.Region{
X: 0,
Y: 0,
Width: 1,
Height: 1,
}
img.DrawTriangles([graphics.ShaderImageNum]*Image{}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, s, nil)
if err := ResolveStaleImages(); err != nil {
t.Fatal(err)
@ -60,7 +66,13 @@ func TestShaderChain(t *testing.T) {
ir := etesting.ShaderProgramImages(1)
s := NewShader(&ir)
for i := 0; i < num-1; i++ {
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, driver.Region{}, s, nil)
dr := driver.Region{
X: 0,
Y: 0,
Width: 1,
Height: 1,
}
imgs[i+1].DrawTriangles([graphics.ShaderImageNum]*Image{imgs[i]}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, s, nil)
}
if err := ResolveStaleImages(); err != nil {
@ -93,7 +105,13 @@ func TestShaderMultipleSources(t *testing.T) {
ir := etesting.ShaderProgramImages(3)
s := NewShader(&ir)
var offsets [graphics.ShaderImageNum - 1][2]float32
dst.DrawTriangles(srcs, offsets, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, nil)
dr := driver.Region{
X: 0,
Y: 0,
Width: 1,
Height: 1,
}
dst.DrawTriangles(srcs, offsets, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, s, nil)
// Clear one of the sources after DrawTriangles. dst should not be affected.
srcs[0].Fill(color.RGBA{})
@ -129,7 +147,13 @@ func TestShaderMultipleSourcesOnOneTexture(t *testing.T) {
{1, 0},
{2, 0},
}
dst.DrawTriangles(srcs, offsets, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, nil)
dr := driver.Region{
X: 0,
Y: 0,
Width: 1,
Height: 1,
}
dst.DrawTriangles(srcs, offsets, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, s, nil)
// Clear one of the sources after DrawTriangles. dst should not be affected.
srcs[0].Fill(color.RGBA{})
@ -154,7 +178,13 @@ func TestShaderDispose(t *testing.T) {
ir := etesting.ShaderProgramFill(0xff, 0, 0, 0xff)
s := NewShader(&ir)
img.DrawTriangles([graphics.ShaderImageNum]*Image{}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, s, nil)
dr := driver.Region{
X: 0,
Y: 0,
Width: 1,
Height: 1,
}
img.DrawTriangles([graphics.ShaderImageNum]*Image{}, [graphics.ShaderImageNum - 1][2]float32{}, quadVertices(1, 1, 0, 0), graphics.QuadIndices(), nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, s, nil)
// Dispose the shader. This should invalidates all the images using this shader i.e., all the images become
// stale.

View File

@ -241,7 +241,13 @@ func (i *Image) ensureNotShared() {
is := graphics.QuadIndices()
srcs := [graphics.ShaderImageNum]*restorable.Image{i.backend.restorable}
var offsets [graphics.ShaderImageNum - 1][2]float32
newImg.DrawTriangles(srcs, offsets, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
dstRegion := driver.Region{
X: paddingSize,
Y: paddingSize,
Width: float32(w - 2*paddingSize),
Height: float32(h - 2*paddingSize),
}
newImg.DrawTriangles(srcs, offsets, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dstRegion, driver.Region{}, nil, nil)
i.dispose(false)
i.backend = &backend{
@ -325,7 +331,7 @@ func (i *Image) processSrc(src *Image) {
// 5: Color G
// 6: Color B
// 7: Color Y
func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []float32, indices []uint16, colorm *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion 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{}) {
backendsM.Lock()
// Do not use defer for performance.
@ -345,6 +351,9 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f
dy = paddingSize
}
dstRegion.X += dx
dstRegion.Y += dx
var oxf, oyf float32
if srcs[0] != nil {
ox, oy, _, _ := srcs[0].regionWithPadding()
@ -358,11 +367,11 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f
vertices[i*graphics.VertexFloatNum+2] += oxf
vertices[i*graphics.VertexFloatNum+3] += oyf
}
// sourceRegion can be delibarately empty when this is not needed in order to avoid unexpected
// srcRegion can be delibarately empty when this is not needed in order to avoid unexpected
// performance issue (#1293).
if sourceRegion.Width != 0 && sourceRegion.Height != 0 {
sourceRegion.X += oxf
sourceRegion.Y += oyf
if srcRegion.Width != 0 && srcRegion.Height != 0 {
srcRegion.X += oxf
srcRegion.Y += oyf
}
} else {
n := len(vertices) / graphics.VertexFloatNum
@ -397,7 +406,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f
}
}
i.backend.restorable.DrawTriangles(imgs, offsets, vertices, indices, colorm, mode, filter, address, sourceRegion, s, uniforms)
i.backend.restorable.DrawTriangles(imgs, offsets, vertices, indices, colorm, mode, filter, address, dstRegion, srcRegion, s, uniforms)
for _, src := range srcs {
if src == nil {
@ -428,6 +437,7 @@ func (i *Image) Fill(clr color.RGBA) {
i.ensureNotShared()
// As *restorable.Image is an independent image, it is fine to fill the entire image.
// TODO: Is it OK not to consider paddings?
i.backend.restorable.Fill(clr)
}

View File

@ -96,7 +96,13 @@ func TestEnsureNotShared(t *testing.T) {
// img4.ensureNotShared() should be called.
vs := quadVertices(size/2, size/2, size/4, size/4, 1)
is := graphics.QuadIndices()
img4.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
dr := driver.Region{
X: 0,
Y: 0,
Width: size,
Height: size,
}
img4.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
want := false
if got := img4.IsSharedForTesting(); got != want {
t.Errorf("got: %v, want: %v", got, want)
@ -126,7 +132,7 @@ func TestEnsureNotShared(t *testing.T) {
// Check further drawing doesn't cause panic.
// This bug was fixed by 03dcd948.
img4.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, 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)
}
func TestReshared(t *testing.T) {
@ -167,7 +173,13 @@ func TestReshared(t *testing.T) {
// Use img1 as a render target.
vs := quadVertices(size, size, 0, 0, 1)
is := graphics.QuadIndices()
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
dr := driver.Region{
X: 0,
Y: 0,
Width: size,
Height: size,
}
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
if got, want := img1.IsSharedForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -177,7 +189,7 @@ func TestReshared(t *testing.T) {
if err := MakeImagesSharedForTesting(); err != nil {
t.Fatal(err)
}
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, 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)
if got, want := img1.IsSharedForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -205,7 +217,7 @@ func TestReshared(t *testing.T) {
}
// img1 is on a shared image again.
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, 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)
if got, want := img1.IsSharedForTesting(), true; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -229,7 +241,7 @@ func TestReshared(t *testing.T) {
}
// Use img1 as a render target again.
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, 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)
if got, want := img1.IsSharedForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -240,7 +252,7 @@ func TestReshared(t *testing.T) {
t.Fatal(err)
}
img1.ReplacePixels(make([]byte, 4*size*size))
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, 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)
if got, want := img1.IsSharedForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -250,7 +262,7 @@ func TestReshared(t *testing.T) {
}
// img1 is not on a shared image due to ReplacePixels.
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, 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)
if got, want := img1.IsSharedForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -260,7 +272,7 @@ func TestReshared(t *testing.T) {
if err := MakeImagesSharedForTesting(); err != nil {
t.Fatal(err)
}
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, 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)
if got, want := img3.IsSharedForTesting(), false; got != want {
t.Errorf("got: %v, want: %v", got, want)
}
@ -355,7 +367,13 @@ func TestReplacePixelsAfterDrawTriangles(t *testing.T) {
vs := quadVertices(w, h, 0, 0, 1)
is := graphics.QuadIndices()
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
dr := driver.Region{
X: 0,
Y: 0,
Width: w,
Height: h,
}
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
dst.ReplacePixels(pix)
pix, err := dst.Pixels(0, 0, w, h)
@ -397,7 +415,13 @@ func TestSmallImages(t *testing.T) {
vs := quadVertices(w, h, 0, 0, 1)
is := graphics.QuadIndices()
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
dr := driver.Region{
X: 0,
Y: 0,
Width: w,
Height: h,
}
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
pix, err := dst.Pixels(0, 0, w, h)
if err != nil {
@ -439,7 +463,13 @@ func TestLongImages(t *testing.T) {
const scale = 120
vs := quadVertices(w, h, 0, 0, scale)
is := graphics.QuadIndices()
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
dr := driver.Region{
X: 0,
Y: 0,
Width: dstW,
Height: dstH,
}
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, dr, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
pix, err := dst.Pixels(0, 0, dstW, dstH)
if err != nil {