graphics: Rename NextPowerOf2Int -> InternalImageSize and add image size adjustment

There is a minimum internal image size on some system like old iOS
devices. This change adds adjustment of the size.

Issue: #810
This commit is contained in:
Hajime Hoshi 2019-02-14 21:04:34 +09:00
parent a2fe3d5962
commit fd250c8d8c
10 changed files with 79 additions and 55 deletions

View File

@ -94,7 +94,7 @@ func TestImagePixels(t *testing.T) {
w, h := img0.Bounds().Size().X, img0.Bounds().Size().Y w, h := img0.Bounds().Size().X, img0.Bounds().Size().Y
// Check out of range part // Check out of range part
w2, h2 := graphics.NextPowerOf2Int(w), graphics.NextPowerOf2Int(h) w2, h2 := graphics.InternalImageSize(w), graphics.InternalImageSize(h)
for j := -100; j < h2+100; j++ { for j := -100; j < h2+100; j++ {
for i := -100; i < w2+100; i++ { for i := -100; i < w2+100; i++ {
got := img0.At(i, j) got := img0.At(i, j)

View File

@ -14,10 +14,19 @@
package graphics package graphics
// NextPowerOf2Int returns a nearest power of 2 to x. // minInternalImageSize is the minimum size of internal images (texture/framebuffer).
func NextPowerOf2Int(x int) int { //
// For example, the image size less than 15 is not supported on some iOS devices.
// See also: https://stackoverflow.com/questions/15935651/certain-framebuffer-sizes-fail-on-ios-devices-gl-framebuffer-unsupported
const minInternalImageSize = 16
// InternalImageSize returns a nearest appropriate size as an internal image.
func InternalImageSize(x int) int {
if x <= 0 { if x <= 0 {
panic("x must be positive") panic("graphics: x must be positive")
}
if x < minInternalImageSize {
return minInternalImageSize
} }
r := 1 r := 1
for r < x { for r < x {
@ -25,3 +34,13 @@ func NextPowerOf2Int(x int) int {
} }
return r return r
} }
func isInternalImageSize(x int) bool {
if x <= 0 {
return false
}
if x < minInternalImageSize {
return false
}
return (x & (x - 1)) == 0
}

View File

@ -20,7 +20,7 @@ import (
. "github.com/hajimehoshi/ebiten/internal/graphics" . "github.com/hajimehoshi/ebiten/internal/graphics"
) )
func TestNextPowerOf2(t *testing.T) { func TestInternalImageSize(t *testing.T) {
testCases := []struct { testCases := []struct {
expected int expected int
arg int arg int
@ -31,7 +31,7 @@ func TestNextPowerOf2(t *testing.T) {
} }
for _, testCase := range testCases { for _, testCase := range testCases {
got := NextPowerOf2Int(testCase.arg) got := InternalImageSize(testCase.arg)
wanted := testCase.expected wanted := testCase.expected
if wanted != got { if wanted != got {
t.Errorf("Clp(%d) = %d, wanted %d", testCase.arg, got, wanted) t.Errorf("Clp(%d) = %d, wanted %d", testCase.arg, got, wanted)

View File

@ -59,19 +59,14 @@ func (v *verticesBackend) slice(n int) []float32 {
return s return s
} }
func isPowerOf2(x int) bool {
if x <= 0 {
return false
}
return (x & (x - 1)) == 0
}
func QuadVertices(width, height int, sx0, sy0, sx1, sy1 int, a, b, c, d, tx, ty float32, cr, cg, cb, ca float32) []float32 { func QuadVertices(width, height int, sx0, sy0, sx1, sy1 int, a, b, c, d, tx, ty float32, cr, cg, cb, ca float32) []float32 {
if !isPowerOf2(width) { // For performance reason, graphics.InternalImageSize is not applied to width/height here.
panic(fmt.Sprintf("graphics: width must be power of 2 but not at QuadVertices: %d", width))
if !isInternalImageSize(width) {
panic(fmt.Sprintf("graphics: width must be an internal image size at QuadVertices: %d", width))
} }
if !isPowerOf2(height) { if !isInternalImageSize(height) {
panic(fmt.Sprintf("graphics: height must be power of 2 but not at QuadVertices: %d", height)) panic(fmt.Sprintf("graphics: height must be an internal image size at QuadVertices: %d", height))
} }
if sx0 >= sx1 || sy0 >= sy1 { if sx0 >= sx1 || sy0 >= sy1 {
@ -166,11 +161,11 @@ func QuadIndices() []uint16 {
} }
func PutVertex(vs []float32, width, height int, dx, dy, su, sv float32, u0, v0, u1, v1 float32, cr, cg, cb, ca float32) { func PutVertex(vs []float32, width, height int, dx, dy, su, sv float32, u0, v0, u1, v1 float32, cr, cg, cb, ca float32) {
if !isPowerOf2(width) { if !isInternalImageSize(width) {
panic(fmt.Sprintf("graphics: width must be power of 2 but not at PutVertices: %d", width)) panic(fmt.Sprintf("graphics: width must be an internal image size at PutVertices: %d", width))
} }
if !isPowerOf2(height) { if !isInternalImageSize(height) {
panic(fmt.Sprintf("graphics: height must be power of 2 but not at PutVertices: %d", height)) panic(fmt.Sprintf("graphics: height must be an internal image size at PutVertices: %d", height))
} }
vs[0] = dx vs[0] = dx

View File

@ -340,10 +340,10 @@ func (d *Driver) checkSize(width, height int) {
}) })
if width < 1 { if width < 1 {
panic(fmt.Sprintf("metal: width (%d) must be equal or more than 1", width)) panic(fmt.Sprintf("metal: width (%d) must be equal or more than %d", width, 1))
} }
if height < 1 { if height < 1 {
panic(fmt.Sprintf("metal: height (%d) must be equal or more than 1", height)) panic(fmt.Sprintf("metal: height (%d) must be equal or more than %d", height, 1))
} }
if width > m { if width > m {
panic(fmt.Sprintf("metal: width (%d) must be less than or equal to %d", width, m)) panic(fmt.Sprintf("metal: width (%d) must be less than or equal to %d", width, m))
@ -357,8 +357,8 @@ func (d *Driver) NewImage(width, height int) (graphicsdriver.Image, error) {
d.checkSize(width, height) d.checkSize(width, height)
td := mtl.TextureDescriptor{ td := mtl.TextureDescriptor{
PixelFormat: mtl.PixelFormatRGBA8UNorm, PixelFormat: mtl.PixelFormatRGBA8UNorm,
Width: graphics.NextPowerOf2Int(width), Width: graphics.InternalImageSize(width),
Height: graphics.NextPowerOf2Int(height), Height: graphics.InternalImageSize(height),
StorageMode: mtl.StorageModeManaged, StorageMode: mtl.StorageModeManaged,
// MTLTextureUsageRenderTarget might cause a problematic render result. Not sure the reason. // MTLTextureUsageRenderTarget might cause a problematic render result. Not sure the reason.
@ -581,8 +581,8 @@ func (d *Driver) Draw(indexLen int, indexOffset int, mode graphics.CompositeMode
rce.SetVertexBytes(unsafe.Pointer(&viewportSize[0]), unsafe.Sizeof(viewportSize), 1) rce.SetVertexBytes(unsafe.Pointer(&viewportSize[0]), unsafe.Sizeof(viewportSize), 1)
sourceSize := [...]float32{ sourceSize := [...]float32{
float32(graphics.NextPowerOf2Int(d.src.width)), float32(graphics.InternalImageSize(d.src.width)),
float32(graphics.NextPowerOf2Int(d.src.height)), float32(graphics.InternalImageSize(d.src.height)),
} }
rce.SetFragmentBytes(unsafe.Pointer(&sourceSize[0]), unsafe.Sizeof(sourceSize), 2) rce.SetFragmentBytes(unsafe.Pointer(&sourceSize[0]), unsafe.Sizeof(sourceSize), 2)
@ -644,7 +644,7 @@ func (i *Image) viewportSize() (int, int) {
if i.screen { if i.screen {
return i.width, i.height return i.width, i.height
} }
return graphics.NextPowerOf2Int(i.width), graphics.NextPowerOf2Int(i.height) return graphics.InternalImageSize(i.width), graphics.InternalImageSize(i.height)
} }
func (i *Image) Dispose() { func (i *Image) Dispose() {

View File

@ -39,10 +39,10 @@ func (d *Driver) SetWindow(window uintptr) {
func (d *Driver) checkSize(width, height int) { func (d *Driver) checkSize(width, height int) {
if width < 1 { if width < 1 {
panic(fmt.Sprintf("opengl: width (%d) must be equal or more than 1", width)) panic(fmt.Sprintf("opengl: width (%d) must be equal or more than %d", width, 1))
} }
if height < 1 { if height < 1 {
panic(fmt.Sprintf("opengl: height (%d) must be equal or more than 1", height)) panic(fmt.Sprintf("opengl: height (%d) must be equal or more than %d", height, 1))
} }
m := d.context.getMaxTextureSize() m := d.context.getMaxTextureSize()
if width > m { if width > m {
@ -59,8 +59,8 @@ func (d *Driver) NewImage(width, height int) (graphicsdriver.Image, error) {
width: width, width: width,
height: height, height: height,
} }
w := graphics.NextPowerOf2Int(width) w := graphics.InternalImageSize(width)
h := graphics.NextPowerOf2Int(height) h := graphics.InternalImageSize(height)
d.checkSize(w, h) d.checkSize(w, h)
t, err := d.context.newTexture(w, h) t, err := d.context.newTexture(w, h)
if err != nil { if err != nil {

View File

@ -76,7 +76,7 @@ func (i *Image) ensureFramebuffer() error {
return nil return nil
} }
w, h := graphics.NextPowerOf2Int(i.width), graphics.NextPowerOf2Int(i.height) w, h := graphics.InternalImageSize(i.width), graphics.InternalImageSize(i.height)
f, err := newFramebufferFromTexture(&i.driver.context, i.textureNative, w, h) f, err := newFramebufferFromTexture(&i.driver.context, i.textureNative, w, h)
if err != nil { if err != nil {
return err return err

View File

@ -310,8 +310,8 @@ func (d *Driver) useProgram(mode graphics.CompositeMode, colorM *affine.ColorM,
d.state.lastColorMatrixTranslation = esTranslate d.state.lastColorMatrixTranslation = esTranslate
} }
sw := graphics.NextPowerOf2Int(srcW) sw := graphics.InternalImageSize(srcW)
sh := graphics.NextPowerOf2Int(srcH) sh := graphics.InternalImageSize(srcH)
if d.state.lastSourceWidth != sw || d.state.lastSourceHeight != sh { if d.state.lastSourceWidth != sw || d.state.lastSourceHeight != sh {
d.context.uniformFloats(program, "source_size", []float32{float32(sw), float32(sh)}) d.context.uniformFloats(program, "source_size", []float32{float32(sw), float32(sh)})

View File

@ -230,8 +230,8 @@ func (i *Image) Size() (int, int) {
func (i *Image) InternalSize() (int, int) { func (i *Image) InternalSize() (int, int) {
if i.w2 == 0 || i.h2 == 0 { if i.w2 == 0 || i.h2 == 0 {
w, h := i.image.Size() w, h := i.image.Size()
i.w2 = graphics.NextPowerOf2Int(w) i.w2 = graphics.InternalImageSize(w)
i.h2 = graphics.NextPowerOf2Int(h) i.h2 = graphics.InternalImageSize(h)
} }
return i.w2, i.h2 return i.w2, i.h2
} }

View File

@ -105,6 +105,14 @@ func TestRestoreWithoutDraw(t *testing.T) {
} }
} }
func quadVertices(dw, dh, sw, sh, x, y int) []float32 {
// dw/dh must be internal image sizes.
return graphics.QuadVertices(dw, dh,
0, 0, sw, sh,
1, 0, 0, 1, float32(x), float32(y),
1, 1, 1, 1)
}
func TestRestoreChain(t *testing.T) { func TestRestoreChain(t *testing.T) {
const num = 10 const num = 10
imgs := []*Image{} imgs := []*Image{}
@ -120,8 +128,8 @@ func TestRestoreChain(t *testing.T) {
clr := color.RGBA{0x00, 0x00, 0x00, 0xff} clr := color.RGBA{0x00, 0x00, 0x00, 0xff}
imgs[0].Fill(clr.R, clr.G, clr.B, clr.A) imgs[0].Fill(clr.R, clr.G, clr.B, clr.A)
for i := 0; i < num-1; i++ { for i := 0; i < num-1; i++ {
w, h := imgs[i].Size() w, h := imgs[i].InternalSize()
vs := graphics.QuadVertices(w, h, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1) vs := quadVertices(w, h, 1, 1, 0, 0)
is := graphics.QuadIndices() is := graphics.QuadIndices()
imgs[i+1].DrawImage(imgs[i], vs, is, nil, graphics.CompositeModeCopy, graphics.FilterNearest, graphics.AddressClampToZero) imgs[i+1].DrawImage(imgs[i], vs, is, nil, graphics.CompositeModeCopy, graphics.FilterNearest, graphics.AddressClampToZero)
} }
@ -162,7 +170,7 @@ func TestRestoreChain2(t *testing.T) {
clr8 := color.RGBA{0x00, 0x00, 0xff, 0xff} clr8 := color.RGBA{0x00, 0x00, 0xff, 0xff}
imgs[8].Fill(clr8.R, clr8.G, clr8.B, clr8.A) imgs[8].Fill(clr8.R, clr8.G, clr8.B, clr8.A)
vs := graphics.QuadVertices(w, h, 0, 0, w, h, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1) vs := quadVertices(graphics.InternalImageSize(w), graphics.InternalImageSize(h), w, h, 0, 0)
is := graphics.QuadIndices() is := graphics.QuadIndices()
imgs[8].DrawImage(imgs[7], vs, is, nil, graphics.CompositeModeCopy, graphics.FilterNearest, graphics.AddressClampToZero) imgs[8].DrawImage(imgs[7], vs, is, nil, graphics.CompositeModeCopy, graphics.FilterNearest, graphics.AddressClampToZero)
imgs[9].DrawImage(imgs[8], vs, is, nil, graphics.CompositeModeCopy, graphics.FilterNearest, graphics.AddressClampToZero) imgs[9].DrawImage(imgs[8], vs, is, nil, graphics.CompositeModeCopy, graphics.FilterNearest, graphics.AddressClampToZero)
@ -204,7 +212,7 @@ func TestRestoreOverrideSource(t *testing.T) {
clr0 := color.RGBA{0x00, 0x00, 0x00, 0xff} clr0 := color.RGBA{0x00, 0x00, 0x00, 0xff}
clr1 := color.RGBA{0x00, 0x00, 0x01, 0xff} clr1 := color.RGBA{0x00, 0x00, 0x01, 0xff}
img1.Fill(clr0.R, clr0.G, clr0.B, clr0.A) img1.Fill(clr0.R, clr0.G, clr0.B, clr0.A)
vs := graphics.QuadVertices(w, h, 0, 0, w, h, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1) vs := quadVertices(graphics.InternalImageSize(w), graphics.InternalImageSize(h), w, h, 0, 0)
is := graphics.QuadIndices() is := graphics.QuadIndices()
img2.DrawImage(img1, vs, is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero) img2.DrawImage(img1, vs, is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero)
img3.DrawImage(img2, vs, is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero) img3.DrawImage(img2, vs, is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero)
@ -284,24 +292,26 @@ func TestRestoreComplexGraph(t *testing.T) {
img1.Dispose() img1.Dispose()
img0.Dispose() img0.Dispose()
}() }()
vs := graphics.QuadVertices(w, h, 0, 0, w, h, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1) dw := graphics.InternalImageSize(w)
dh := graphics.InternalImageSize(h)
vs := quadVertices(dw, dh, w, h, 0, 0)
is := graphics.QuadIndices() is := graphics.QuadIndices()
img3.DrawImage(img0, vs, is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero) img3.DrawImage(img0, vs, is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero)
vs = graphics.QuadVertices(w, h, 0, 0, w, h, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1) vs = quadVertices(dw, dh, w, h, 1, 0)
img3.DrawImage(img1, vs, is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero) img3.DrawImage(img1, vs, is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero)
vs = graphics.QuadVertices(w, h, 0, 0, w, h, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1) vs = quadVertices(dw, dh, w, h, 1, 0)
img4.DrawImage(img1, vs, is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero) img4.DrawImage(img1, vs, is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero)
vs = graphics.QuadVertices(w, h, 0, 0, w, h, 1, 0, 0, 1, 2, 0, 1, 1, 1, 1) vs = quadVertices(dw, dh, w, h, 2, 0)
img4.DrawImage(img2, vs, is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero) img4.DrawImage(img2, vs, is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero)
vs = graphics.QuadVertices(w, h, 0, 0, w, h, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1) vs = quadVertices(dw, dh, w, h, 0, 0)
img5.DrawImage(img3, vs, is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero) img5.DrawImage(img3, vs, is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero)
vs = graphics.QuadVertices(w, h, 0, 0, w, h, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1) vs = quadVertices(dw, dh, w, h, 0, 0)
img6.DrawImage(img3, vs, is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero) img6.DrawImage(img3, vs, is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero)
vs = graphics.QuadVertices(w, h, 0, 0, w, h, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1) vs = quadVertices(dw, dh, w, h, 1, 0)
img6.DrawImage(img4, vs, is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero) img6.DrawImage(img4, vs, is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero)
vs = graphics.QuadVertices(w, h, 0, 0, w, h, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1) vs = quadVertices(dw, dh, w, h, 0, 0)
img7.DrawImage(img2, vs, is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero) img7.DrawImage(img2, vs, is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero)
vs = graphics.QuadVertices(w, h, 0, 0, w, h, 1, 0, 0, 1, 2, 0, 1, 1, 1, 1) vs = quadVertices(dw, dh, w, h, 2, 0)
img7.DrawImage(img3, vs, is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero) img7.DrawImage(img3, vs, is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero)
ResolveStaleImages() ResolveStaleImages()
if err := Restore(); err != nil { if err := Restore(); err != nil {
@ -391,7 +401,7 @@ func TestRestoreRecursive(t *testing.T) {
img1.Dispose() img1.Dispose()
img0.Dispose() img0.Dispose()
}() }()
vs := graphics.QuadVertices(w, h, 0, 0, w, h, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1) vs := quadVertices(graphics.InternalImageSize(w), graphics.InternalImageSize(h), w, h, 1, 0)
is := graphics.QuadIndices() is := graphics.QuadIndices()
img1.DrawImage(img0, vs, is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero) img1.DrawImage(img0, vs, is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero)
img0.DrawImage(img1, vs, is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero) img0.DrawImage(img1, vs, is, nil, graphics.CompositeModeSourceOver, graphics.FilterNearest, graphics.AddressClampToZero)
@ -481,7 +491,7 @@ func TestDrawImageAndReplacePixels(t *testing.T) {
img1 := NewImage(2, 1) img1 := NewImage(2, 1)
defer img1.Dispose() defer img1.Dispose()
vs := graphics.QuadVertices(1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1) vs := quadVertices(graphics.InternalImageSize(1), graphics.InternalImageSize(1), 1, 1, 0, 0)
is := graphics.QuadIndices() is := graphics.QuadIndices()
img1.DrawImage(img0, vs, is, nil, graphics.CompositeModeCopy, graphics.FilterNearest, graphics.AddressClampToZero) img1.DrawImage(img0, vs, is, nil, graphics.CompositeModeCopy, graphics.FilterNearest, graphics.AddressClampToZero)
img1.ReplacePixels([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 0, 0, 2, 1) img1.ReplacePixels([]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 0, 0, 2, 1)
@ -514,7 +524,7 @@ func TestDispose(t *testing.T) {
img2 := newImageFromImage(base2) img2 := newImageFromImage(base2)
defer img2.Dispose() defer img2.Dispose()
vs := graphics.QuadVertices(1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1) vs := quadVertices(graphics.InternalImageSize(1), graphics.InternalImageSize(1), 1, 1, 0, 0)
is := graphics.QuadIndices() is := graphics.QuadIndices()
img1.DrawImage(img2, vs, is, nil, graphics.CompositeModeCopy, graphics.FilterNearest, graphics.AddressClampToZero) img1.DrawImage(img2, vs, is, nil, graphics.CompositeModeCopy, graphics.FilterNearest, graphics.AddressClampToZero)
img0.DrawImage(img1, vs, is, nil, graphics.CompositeModeCopy, graphics.FilterNearest, graphics.AddressClampToZero) img0.DrawImage(img1, vs, is, nil, graphics.CompositeModeCopy, graphics.FilterNearest, graphics.AddressClampToZero)
@ -608,7 +618,7 @@ func TestReplacePixelsOnly(t *testing.T) {
img0.ReplacePixels([]byte{1, 2, 3, 4}, i%w, i/w, 1, 1) img0.ReplacePixels([]byte{1, 2, 3, 4}, i%w, i/w, 1, 1)
} }
vs := graphics.QuadVertices(w, h, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1) vs := quadVertices(graphics.InternalImageSize(w), graphics.InternalImageSize(h), 1, 1, 0, 0)
is := graphics.QuadIndices() is := graphics.QuadIndices()
img1.DrawImage(img0, vs, is, nil, graphics.CompositeModeCopy, graphics.FilterNearest, graphics.AddressClampToZero) img1.DrawImage(img0, vs, is, nil, graphics.CompositeModeCopy, graphics.FilterNearest, graphics.AddressClampToZero)
img0.ReplacePixels([]byte{5, 6, 7, 8}, 0, 0, 1, 1) img0.ReplacePixels([]byte{5, 6, 7, 8}, 0, 0, 1, 1)
@ -653,7 +663,7 @@ func TestReadPixelsFromVolatileImage(t *testing.T) {
// Second, draw src to dst. If the implementation is correct, dst becomes stale. // Second, draw src to dst. If the implementation is correct, dst becomes stale.
src.Fill(0xff, 0xff, 0xff, 0xff) src.Fill(0xff, 0xff, 0xff, 0xff)
vs := graphics.QuadVertices(w, h, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1) vs := quadVertices(graphics.InternalImageSize(w), graphics.InternalImageSize(h), 1, 1, 0, 0)
is := graphics.QuadIndices() is := graphics.QuadIndices()
dst.DrawImage(src, vs, is, nil, graphics.CompositeModeCopy, graphics.FilterNearest, graphics.AddressClampToZero) dst.DrawImage(src, vs, is, nil, graphics.CompositeModeCopy, graphics.FilterNearest, graphics.AddressClampToZero)