From e7d080ca4a3a77410c226d9c3b63af3df461f68b Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Wed, 21 Oct 2020 11:28:01 +0900 Subject: [PATCH] mipmap: Bug fix: Too big scale tried to allocate too big images Fixes #1399 --- examples/camera/main.go | 4 ++-- image_test.go | 11 +++++++++++ internal/mipmap/mipmap.go | 23 ++++++++++++++++------- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/examples/camera/main.go b/examples/camera/main.go index 2399c1ccc..aa6a26214 100644 --- a/examples/camera/main.go +++ b/examples/camera/main.go @@ -148,12 +148,12 @@ func (g *Game) Update() error { } if ebiten.IsKeyPressed(ebiten.KeyQ) { - if g.camera.ZoomFactor > -240 { + if g.camera.ZoomFactor > -2400 { g.camera.ZoomFactor -= 1 } } if ebiten.IsKeyPressed(ebiten.KeyE) { - if g.camera.ZoomFactor < 240 { + if g.camera.ZoomFactor < 2400 { g.camera.ZoomFactor += 1 } } diff --git a/image_test.go b/image_test.go index 237cf6874..90dca2d38 100644 --- a/image_test.go +++ b/image_test.go @@ -2149,3 +2149,14 @@ func TestImageDrawImageTooSmallScale(t *testing.T) { op.GeoM.Scale(1e-10, 1e-10) dst.DrawImage(src, op) } + +// Issue #1399 +func TestImageDrawImageCannotCreateMiamap(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) +} diff --git a/internal/mipmap/mipmap.go b/internal/mipmap/mipmap.go index 9165f200f..056812727 100644 --- a/internal/mipmap/mipmap.go +++ b/internal/mipmap/mipmap.go @@ -230,6 +230,13 @@ func (m *Mipmap) level(level int) *buffered.Image { m.imgs[level] = 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.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) @@ -262,7 +269,9 @@ func (m *Mipmap) MarkDisposed() { func (m *Mipmap) disposeMipmaps() { for _, img := range m.imgs { - img.MarkDisposed() + if img != nil { + img.MarkDisposed() + } } for k := range m.imgs { delete(m.imgs, k) @@ -271,7 +280,7 @@ func (m *Mipmap) disposeMipmaps() { // 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 { - const maxScale = 6 + const maxLevel = 6 if filter == driver.FilterScreen { return 0 @@ -287,7 +296,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). if math.IsInf(float64(scale), 0) { if filter == driver.FilterNearest { - return -maxScale + return -maxLevel } return 0 } @@ -327,8 +336,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 // enough. - if level < -maxScale { - level = -maxScale + if level < -maxLevel { + level = -maxLevel } return level } @@ -361,8 +370,8 @@ func mipmapLevelFromDistance(dx0, dy0, dx1, dy1, sx0, sy0, sx1, sy1 float32, fil } } - if level > maxScale { - level = maxScale + if level > maxLevel { + level = maxLevel } return level