mipmap: Bug fix: Too big scale tried to allocate too big images

Fixes #1399
This commit is contained in:
Hajime Hoshi 2020-10-21 11:28:01 +09:00
parent 356391a36c
commit 4c7eaeaf02
3 changed files with 29 additions and 9 deletions

View File

@ -148,12 +148,12 @@ func (g *Game) Update() error {
} }
if ebiten.IsKeyPressed(ebiten.KeyQ) { if ebiten.IsKeyPressed(ebiten.KeyQ) {
if g.camera.ZoomFactor > -240 { if g.camera.ZoomFactor > -2400 {
g.camera.ZoomFactor -= 1 g.camera.ZoomFactor -= 1
} }
} }
if ebiten.IsKeyPressed(ebiten.KeyE) { if ebiten.IsKeyPressed(ebiten.KeyE) {
if g.camera.ZoomFactor < 240 { if g.camera.ZoomFactor < 2400 {
g.camera.ZoomFactor += 1 g.camera.ZoomFactor += 1
} }
} }

View File

@ -2149,3 +2149,14 @@ func TestImageDrawImageTooSmallScale(t *testing.T) {
op.GeoM.Scale(1e-10, 1e-10) op.GeoM.Scale(1e-10, 1e-10)
dst.DrawImage(src, op) dst.DrawImage(src, op)
} }
// Issue #1399
func TestImageDrawImageCannotAllocateImageForMipmap(t *testing.T) {
dst := NewImage(1, 1)
src := NewImage(4096, 4096)
op := &DrawImageOptions{}
op.GeoM.Scale(64, 64)
dst.DrawImage(src, op)
dst.At(0, 0)
}

View File

@ -236,6 +236,13 @@ func (m *Mipmap) level(level int) *buffered.Image {
m.imgs[level] = nil m.imgs[level] = nil
return nil return nil
} }
// buffered.NewImage panics with a too big size when actual allocation happens.
// 4096 should be a safe size in most environments (#1399).
// Unfortunately a precise max image size cannot be obtained here since this requires GPU access.
if w2 > 4096 || h2 > 4096 {
m.imgs[level] = nil
return nil
}
s := buffered.NewImage(w2, h2) s := buffered.NewImage(w2, h2)
s.SetVolatile(m.volatile) 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) s.DrawTriangles([graphics.ShaderImageNum]*buffered.Image{src}, vs, is, nil, driver.CompositeModeCopy, filter, driver.AddressUnsafe, driver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil)
@ -268,7 +275,9 @@ func (m *Mipmap) MarkDisposed() {
func (m *Mipmap) disposeMipmaps() { func (m *Mipmap) disposeMipmaps() {
for _, img := range m.imgs { for _, img := range m.imgs {
img.MarkDisposed() if img != nil {
img.MarkDisposed()
}
} }
for k := range m.imgs { for k := range m.imgs {
delete(m.imgs, k) delete(m.imgs, k)
@ -277,7 +286,7 @@ func (m *Mipmap) disposeMipmaps() {
// mipmapLevel returns an appropriate mipmap level for the given distance. // mipmapLevel returns an appropriate mipmap level for the given distance.
func mipmapLevelFromDistance(dx0, dy0, dx1, dy1, sx0, sy0, sx1, sy1 float32, filter driver.Filter) int { func mipmapLevelFromDistance(dx0, dy0, dx1, dy1, sx0, sy0, sx1, sy1 float32, filter driver.Filter) int {
const maxScale = 6 const maxLevel = 6
if filter == driver.FilterScreen { if filter == driver.FilterScreen {
return 0 return 0
@ -293,7 +302,7 @@ func mipmapLevelFromDistance(dx0, dy0, dx1, dy1, sx0, sy0, sx1, sy1 float32, fil
// Scale can be infinite when the specified scale is extremely big (#1398). // Scale can be infinite when the specified scale is extremely big (#1398).
if math.IsInf(float64(scale), 0) { if math.IsInf(float64(scale), 0) {
if filter == driver.FilterNearest { if filter == driver.FilterNearest {
return -maxScale return -maxLevel
} }
return 0 return 0
} }
@ -333,8 +342,8 @@ func mipmapLevelFromDistance(dx0, dy0, dx1, dy1, sx0, sy0, sx1, sy1 float32, fil
// If tooBigScale is 32, level -6 means that the maximum scale is 32 * 2^6 = 2048. This should be // If tooBigScale is 32, level -6 means that the maximum scale is 32 * 2^6 = 2048. This should be
// enough. // enough.
if level < -maxScale { if level < -maxLevel {
level = -maxScale level = -maxLevel
} }
return level return level
} }
@ -367,8 +376,8 @@ func mipmapLevelFromDistance(dx0, dy0, dx1, dy1, sx0, sy0, sx1, sy1 float32, fil
} }
} }
if level > maxScale { if level > maxLevel {
level = maxScale level = maxLevel
} }
return level return level