Skip to content

Commit

Permalink
ebiten: Allow SubImage at DrawRectShader
Browse files Browse the repository at this point in the history
  • Loading branch information
hajimehoshi committed Sep 19, 2020
1 parent f287fad commit 4156453
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 40 deletions.
54 changes: 32 additions & 22 deletions image.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,26 +274,23 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) error {
vs := graphics.QuadVertices(sx0, sy0, sx1, sy1, a, b, c, d, tx, ty, 1, 1, 1, 1, filter == driver.FilterScreen)
is := graphics.QuadIndices()

var sr driver.Region
// Pass the source region only when the shader is used, since this affects the condition of merging graphics
// commands (#1293).
if options.Shader != nil {
sr = driver.Region{
X: float32(bounds.Min.X),
Y: float32(bounds.Min.Y),
Width: float32(bounds.Dx()),
Height: float32(bounds.Dy()),
}
}

srcs := [graphics.ShaderImageNum]*mipmap.Mipmap{img.mipmap}
if options.Shader == nil {
i.mipmap.DrawTriangles(srcs, vs, is, options.ColorM.impl, mode, filter, driver.AddressUnsafe, sr, nil, nil, canSkipMipmap(options.GeoM, filter))
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))
return nil
}

// Pass the source region only when the shader is used, since this affects the condition of merging graphics
// commands (#1293).
sr := driver.Region{
X: float32(bounds.Min.X),
Y: float32(bounds.Min.Y),
Width: float32(bounds.Dx()),
Height: float32(bounds.Dy()),
}

us := options.Shader.convertUniforms(options.Uniforms)
i.mipmap.DrawTriangles(srcs, vs, is, nil, mode, filter, driver.AddressUnsafe, sr, options.Shader.shader, us, canSkipMipmap(options.GeoM, filter))
i.mipmap.DrawTriangles(srcs, vs, is, nil, mode, filter, driver.AddressUnsafe, sr, [graphics.ShaderImageNum - 1][2]float32{}, options.Shader.shader, us, canSkipMipmap(options.GeoM, filter))
return nil
}

Expand Down Expand Up @@ -489,12 +486,12 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o
}

if options.Shader == nil {
i.mipmap.DrawTriangles(srcs, vs, is, options.ColorM.impl, mode, filter, driver.Address(options.Address), sr, nil, nil, false)
i.mipmap.DrawTriangles(srcs, vs, is, options.ColorM.impl, mode, filter, driver.Address(options.Address), sr, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false)
return
}

us := options.Shader.convertUniforms(options.Uniforms)
i.mipmap.DrawTriangles(srcs, vs, is, nil, mode, driver.FilterNearest, driver.AddressUnsafe, sr, options.Shader.shader, us, false)
i.mipmap.DrawTriangles(srcs, vs, is, nil, mode, driver.FilterNearest, driver.AddressUnsafe, sr, [graphics.ShaderImageNum - 1][2]float32{}, options.Shader.shader, us, false)
}

// DrawRectShaderOptions represents options for DrawRectShader
Expand Down Expand Up @@ -557,18 +554,21 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR
if img.isDisposed() {
panic("ebiten: the given image to DrawRectShader must not be disposed")
}
if img.isSubImage() {
// TODO: Implement this.
panic("ebiten: rendering a sub-image is not implemented (DrawRectShader)")
}
if w, h := img.Size(); width != w || height != h {
panic("ebiten: all the source images must be the same size with the rectangle")
}
imgs[i] = img.mipmap
}

sx, sy := float32(0), float32(0)
if options.Images[0] != nil {
b := options.Images[0].Bounds()
sx = float32(b.Min.X)
sy = float32(b.Min.Y)
}

a, b, c, d, tx, ty := options.GeoM.elements32()
vs := graphics.QuadVertices(0, 0, float32(width), float32(height), a, b, c, d, tx, ty, 1, 1, 1, 1, false)
vs := graphics.QuadVertices(sx, sy, sx+float32(width), sy+float32(height), a, b, c, d, tx, ty, 1, 1, 1, 1, false)
is := graphics.QuadIndices()

var sr driver.Region
Expand All @@ -582,8 +582,18 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR
}
}

var offsets [graphics.ShaderImageNum - 1][2]float32
for i, img := range options.Images[1:] {
if img == nil {
continue
}
b := img.Bounds()
offsets[i][0] = -sx + float32(b.Min.X)
offsets[i][1] = -sy + float32(b.Min.Y)
}

us := shader.convertUniforms(options.Uniforms)
i.mipmap.DrawTriangles(imgs, vs, is, nil, mode, driver.FilterNearest, driver.AddressUnsafe, sr, shader.shader, us, canSkipMipmap(options.GeoM, driver.FilterNearest))
i.mipmap.DrawTriangles(imgs, vs, is, nil, mode, driver.FilterNearest, driver.AddressUnsafe, sr, offsets, shader.shader, us, canSkipMipmap(options.GeoM, driver.FilterNearest))
}

// SubImage returns an image representing the portion of the image p visible through r.
Expand Down
6 changes: 3 additions & 3 deletions internal/buffered/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,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, 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, sourceRegion 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")
Expand All @@ -258,7 +258,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, shader, uniforms)
i.DrawTriangles(srcs, vertices, indices, colorm, mode, filter, address, sourceRegion, subimageOffsets, shader, uniforms)
return nil
}) {
return
Expand Down Expand Up @@ -286,7 +286,7 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f
imgs[i] = img.img
}

i.img.DrawTriangles(imgs, vertices, indices, colorm, mode, filter, address, sourceRegion, s, uniforms)
i.img.DrawTriangles(imgs, vertices, indices, colorm, mode, filter, address, sourceRegion, subimageOffsets, s, uniforms)
i.invalidatePendingPixels()
}

Expand Down
6 changes: 3 additions & 3 deletions internal/mipmap/mipmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,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, 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, sourceRegion driver.Region, subimageOffsets [graphics.ShaderImageNum - 1][2]float32, shader *Shader, uniforms []interface{}, canSkipMipmap bool) {
if len(indices) == 0 {
return
}
Expand Down Expand Up @@ -176,7 +176,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, s, uniforms)
m.orig.DrawTriangles(imgs, vertices, indices, colorm, mode, filter, address, sourceRegion, subimageOffsets, s, uniforms)
m.disposeMipmaps()
}

Expand Down Expand Up @@ -238,7 +238,7 @@ 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{}, nil, nil)
s.DrawTriangles([graphics.ShaderImageNum]*buffered.Image{src}, vs, is, nil, driver.CompositeModeCopy, filter, driver.AddressUnsafe, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
m.imgs[level] = s

return m.imgs[level]
Expand Down
6 changes: 3 additions & 3 deletions internal/shareable/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,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, 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, sourceRegion driver.Region, subimageOffsets [graphics.ShaderImageNum - 1][2]float32, shader *Shader, uniforms []interface{}) {
backendsM.Lock()
// Do not use defer for performance.

Expand Down Expand Up @@ -362,8 +362,8 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f
ox, oy, _, _ := src.regionWithPadding()
ox += paddingSize
oy += paddingSize
offsets[i][0] = float32(ox) - oxf
offsets[i][1] = float32(oy) - oyf
offsets[i][0] = float32(ox) - oxf + subimageOffsets[i][0]
offsets[i][1] = float32(oy) - oyf + subimageOffsets[i][0]
}

var s *restorable.Shader
Expand Down
18 changes: 9 additions & 9 deletions internal/shareable/image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ 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{}, nil, nil)
img4.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, 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)
Expand Down Expand Up @@ -126,7 +126,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{}, nil, nil)
img4.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
}

func TestReshared(t *testing.T) {
Expand Down Expand Up @@ -167,7 +167,7 @@ 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{}, nil, nil)
img1.DrawTriangles([graphics.ShaderImageNum]*Image{img2}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, 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)
}
Expand All @@ -177,7 +177,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{}, nil, nil)
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, 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)
}
Expand All @@ -204,7 +204,7 @@ func TestReshared(t *testing.T) {
}
}

img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil)
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img1}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, 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)
}
Expand Down Expand Up @@ -232,7 +232,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{}, nil, nil)
img0.DrawTriangles([graphics.ShaderImageNum]*Image{img3}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, 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)
}
Expand Down Expand Up @@ -327,7 +327,7 @@ 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{}, nil, nil)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
dst.ReplacePixels(pix)

pix, err := dst.Pixels(0, 0, w, h)
Expand Down Expand Up @@ -369,7 +369,7 @@ 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{}, nil, nil)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)

pix, err := dst.Pixels(0, 0, w, h)
if err != nil {
Expand Down Expand Up @@ -411,7 +411,7 @@ 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{}, nil, nil)
dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)

pix, err := dst.Pixels(0, 0, dstW, dstH)
if err != nil {
Expand Down
64 changes: 64 additions & 0 deletions shader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package ebiten_test

import (
"image"
"image/color"
"testing"

Expand Down Expand Up @@ -798,3 +799,66 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
}
}
}

func TestShaderSubImage(t *testing.T) {
const w, h = 16, 16

dst, _ := NewImage(w, h, FilterDefault)
s, err := NewShader([]byte(`package main
func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
r := imageSrc0At(texCoord).r
g := imageSrc1At(texCoord).g
return vec4(r, g, 0, 1)
}
`))
if err != nil {
t.Fatal(err)
}

src0, _ := NewImage(w, h, FilterDefault)
pix0 := make([]byte, 4*w*h)
for j := 0; j < h; j++ {
for i := 0; i < w; i++ {
if 2 <= i && i < 10 && 2 <= j && j < 10 {
pix0[4*(j*w+i)] = 0xff
pix0[4*(j*w+i)+1] = 0
pix0[4*(j*w+i)+2] = 0
pix0[4*(j*w+i)+3] = 0xff
}
}
}
src0.ReplacePixels(pix0)

src1, _ := NewImage(w, h, FilterDefault)
pix1 := make([]byte, 4*w*h)
for j := 0; j < h; j++ {
for i := 0; i < w; i++ {
if 6 <= i && i < 14 && 6 <= j && j < 14 {
pix1[4*(j*w+i)] = 0
pix1[4*(j*w+i)+1] = 0xff
pix1[4*(j*w+i)+2] = 0
pix1[4*(j*w+i)+3] = 0xff
}
}
}
src1.ReplacePixels(pix1)

op := &DrawRectShaderOptions{}
op.Images[0] = src0.SubImage(image.Rect(2, 2, 10, 10)).(*Image)
op.Images[1] = src1.SubImage(image.Rect(6, 6, 14, 14)).(*Image)
dst.DrawRectShader(w/2, h/2, s, op)

for j := 0; j < h; j++ {
for i := 0; i < w; i++ {
got := dst.At(i, j).(color.RGBA)
var want color.RGBA
if i < w/2 && j < h/2 {
want = color.RGBA{0xff, 0xff, 0, 0xff}
}
if got != want {
t.Errorf("dst.At(%d, %d): got: %v, want: %v", i, j, got, want)
}
}
}
}

0 comments on commit 4156453

Please sign in to comment.