mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-24 10:48:53 +01:00
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:
parent
a2fe3d5962
commit
fd250c8d8c
@ -94,7 +94,7 @@ func TestImagePixels(t *testing.T) {
|
||||
|
||||
w, h := img0.Bounds().Size().X, img0.Bounds().Size().Y
|
||||
// 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 i := -100; i < w2+100; i++ {
|
||||
got := img0.At(i, j)
|
||||
|
@ -14,10 +14,19 @@
|
||||
|
||||
package graphics
|
||||
|
||||
// NextPowerOf2Int returns a nearest power of 2 to x.
|
||||
func NextPowerOf2Int(x int) int {
|
||||
// minInternalImageSize is the minimum size of internal images (texture/framebuffer).
|
||||
//
|
||||
// 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 {
|
||||
panic("x must be positive")
|
||||
panic("graphics: x must be positive")
|
||||
}
|
||||
if x < minInternalImageSize {
|
||||
return minInternalImageSize
|
||||
}
|
||||
r := 1
|
||||
for r < x {
|
||||
@ -25,3 +34,13 @@ func NextPowerOf2Int(x int) int {
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func isInternalImageSize(x int) bool {
|
||||
if x <= 0 {
|
||||
return false
|
||||
}
|
||||
if x < minInternalImageSize {
|
||||
return false
|
||||
}
|
||||
return (x & (x - 1)) == 0
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ import (
|
||||
. "github.com/hajimehoshi/ebiten/internal/graphics"
|
||||
)
|
||||
|
||||
func TestNextPowerOf2(t *testing.T) {
|
||||
func TestInternalImageSize(t *testing.T) {
|
||||
testCases := []struct {
|
||||
expected int
|
||||
arg int
|
||||
@ -31,7 +31,7 @@ func TestNextPowerOf2(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
got := NextPowerOf2Int(testCase.arg)
|
||||
got := InternalImageSize(testCase.arg)
|
||||
wanted := testCase.expected
|
||||
if wanted != got {
|
||||
t.Errorf("Clp(%d) = %d, wanted %d", testCase.arg, got, wanted)
|
||||
|
@ -59,19 +59,14 @@ func (v *verticesBackend) slice(n int) []float32 {
|
||||
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 {
|
||||
if !isPowerOf2(width) {
|
||||
panic(fmt.Sprintf("graphics: width must be power of 2 but not at QuadVertices: %d", width))
|
||||
// For performance reason, graphics.InternalImageSize is not applied to width/height here.
|
||||
|
||||
if !isInternalImageSize(width) {
|
||||
panic(fmt.Sprintf("graphics: width must be an internal image size at QuadVertices: %d", width))
|
||||
}
|
||||
if !isPowerOf2(height) {
|
||||
panic(fmt.Sprintf("graphics: height must be power of 2 but not at QuadVertices: %d", height))
|
||||
if !isInternalImageSize(height) {
|
||||
panic(fmt.Sprintf("graphics: height must be an internal image size at QuadVertices: %d", height))
|
||||
}
|
||||
|
||||
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) {
|
||||
if !isPowerOf2(width) {
|
||||
panic(fmt.Sprintf("graphics: width must be power of 2 but not at PutVertices: %d", width))
|
||||
if !isInternalImageSize(width) {
|
||||
panic(fmt.Sprintf("graphics: width must be an internal image size at PutVertices: %d", width))
|
||||
}
|
||||
if !isPowerOf2(height) {
|
||||
panic(fmt.Sprintf("graphics: height must be power of 2 but not at PutVertices: %d", height))
|
||||
if !isInternalImageSize(height) {
|
||||
panic(fmt.Sprintf("graphics: height must be an internal image size at PutVertices: %d", height))
|
||||
}
|
||||
|
||||
vs[0] = dx
|
||||
|
@ -340,10 +340,10 @@ func (d *Driver) checkSize(width, height int) {
|
||||
})
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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)
|
||||
td := mtl.TextureDescriptor{
|
||||
PixelFormat: mtl.PixelFormatRGBA8UNorm,
|
||||
Width: graphics.NextPowerOf2Int(width),
|
||||
Height: graphics.NextPowerOf2Int(height),
|
||||
Width: graphics.InternalImageSize(width),
|
||||
Height: graphics.InternalImageSize(height),
|
||||
StorageMode: mtl.StorageModeManaged,
|
||||
|
||||
// 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)
|
||||
|
||||
sourceSize := [...]float32{
|
||||
float32(graphics.NextPowerOf2Int(d.src.width)),
|
||||
float32(graphics.NextPowerOf2Int(d.src.height)),
|
||||
float32(graphics.InternalImageSize(d.src.width)),
|
||||
float32(graphics.InternalImageSize(d.src.height)),
|
||||
}
|
||||
rce.SetFragmentBytes(unsafe.Pointer(&sourceSize[0]), unsafe.Sizeof(sourceSize), 2)
|
||||
|
||||
@ -644,7 +644,7 @@ func (i *Image) viewportSize() (int, int) {
|
||||
if i.screen {
|
||||
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() {
|
||||
|
@ -39,10 +39,10 @@ func (d *Driver) SetWindow(window uintptr) {
|
||||
|
||||
func (d *Driver) checkSize(width, height int) {
|
||||
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 {
|
||||
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()
|
||||
if width > m {
|
||||
@ -59,8 +59,8 @@ func (d *Driver) NewImage(width, height int) (graphicsdriver.Image, error) {
|
||||
width: width,
|
||||
height: height,
|
||||
}
|
||||
w := graphics.NextPowerOf2Int(width)
|
||||
h := graphics.NextPowerOf2Int(height)
|
||||
w := graphics.InternalImageSize(width)
|
||||
h := graphics.InternalImageSize(height)
|
||||
d.checkSize(w, h)
|
||||
t, err := d.context.newTexture(w, h)
|
||||
if err != nil {
|
||||
|
@ -76,7 +76,7 @@ func (i *Image) ensureFramebuffer() error {
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -310,8 +310,8 @@ func (d *Driver) useProgram(mode graphics.CompositeMode, colorM *affine.ColorM,
|
||||
d.state.lastColorMatrixTranslation = esTranslate
|
||||
}
|
||||
|
||||
sw := graphics.NextPowerOf2Int(srcW)
|
||||
sh := graphics.NextPowerOf2Int(srcH)
|
||||
sw := graphics.InternalImageSize(srcW)
|
||||
sh := graphics.InternalImageSize(srcH)
|
||||
|
||||
if d.state.lastSourceWidth != sw || d.state.lastSourceHeight != sh {
|
||||
d.context.uniformFloats(program, "source_size", []float32{float32(sw), float32(sh)})
|
||||
|
@ -230,8 +230,8 @@ func (i *Image) Size() (int, int) {
|
||||
func (i *Image) InternalSize() (int, int) {
|
||||
if i.w2 == 0 || i.h2 == 0 {
|
||||
w, h := i.image.Size()
|
||||
i.w2 = graphics.NextPowerOf2Int(w)
|
||||
i.h2 = graphics.NextPowerOf2Int(h)
|
||||
i.w2 = graphics.InternalImageSize(w)
|
||||
i.h2 = graphics.InternalImageSize(h)
|
||||
}
|
||||
return i.w2, i.h2
|
||||
}
|
||||
|
@ -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) {
|
||||
const num = 10
|
||||
imgs := []*Image{}
|
||||
@ -120,8 +128,8 @@ func TestRestoreChain(t *testing.T) {
|
||||
clr := color.RGBA{0x00, 0x00, 0x00, 0xff}
|
||||
imgs[0].Fill(clr.R, clr.G, clr.B, clr.A)
|
||||
for i := 0; i < num-1; i++ {
|
||||
w, h := imgs[i].Size()
|
||||
vs := graphics.QuadVertices(w, h, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1)
|
||||
w, h := imgs[i].InternalSize()
|
||||
vs := quadVertices(w, h, 1, 1, 0, 0)
|
||||
is := graphics.QuadIndices()
|
||||
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}
|
||||
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()
|
||||
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)
|
||||
@ -204,7 +212,7 @@ func TestRestoreOverrideSource(t *testing.T) {
|
||||
clr0 := color.RGBA{0x00, 0x00, 0x00, 0xff}
|
||||
clr1 := color.RGBA{0x00, 0x00, 0x01, 0xff}
|
||||
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()
|
||||
img2.DrawImage(img1, 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()
|
||||
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()
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
ResolveStaleImages()
|
||||
if err := Restore(); err != nil {
|
||||
@ -391,7 +401,7 @@ func TestRestoreRecursive(t *testing.T) {
|
||||
img1.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()
|
||||
img1.DrawImage(img0, 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)
|
||||
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()
|
||||
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)
|
||||
@ -514,7 +524,7 @@ func TestDispose(t *testing.T) {
|
||||
img2 := newImageFromImage(base2)
|
||||
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()
|
||||
img1.DrawImage(img2, 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)
|
||||
}
|
||||
|
||||
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()
|
||||
img1.DrawImage(img0, vs, is, nil, graphics.CompositeModeCopy, graphics.FilterNearest, graphics.AddressClampToZero)
|
||||
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.
|
||||
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()
|
||||
dst.DrawImage(src, vs, is, nil, graphics.CompositeModeCopy, graphics.FilterNearest, graphics.AddressClampToZero)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user