diff --git a/gameforui.go b/gameforui.go index dd7c63ca8..e93284638 100644 --- a/gameforui.go +++ b/gameforui.go @@ -15,6 +15,7 @@ package ebiten import ( + "github.com/hajimehoshi/ebiten/v2/internal/atlas" "github.com/hajimehoshi/ebiten/v2/internal/ui" ) @@ -34,7 +35,17 @@ func (c *gameForUI) NewOffscreenImage(width, height int) *ui.Image { c.offscreen.Dispose() c.offscreen = nil } - c.offscreen = NewImage(width, height) + + // Keep the offscreen an isolated image from an atlas (#1938). + // The shader program for the screen is special and doesn't work well with an image on an atlas. + // An image on an atlas is surrounded by a transparent edge, + // and the shader program unexpectedly picks the pixel on the edges. + imageType := atlas.ImageTypeIsolated + if ui.IsScreenClearedEveryFrame() { + // A violatile image is also always isolated. + imageType = atlas.ImageTypeVolatile + } + c.offscreen = newImage(width, height, imageType) return c.offscreen.image } diff --git a/image.go b/image.go index 41d61ffa9..dc8447a79 100644 --- a/image.go +++ b/image.go @@ -20,6 +20,7 @@ import ( "image/color" "github.com/hajimehoshi/ebiten/v2/internal/affine" + "github.com/hajimehoshi/ebiten/v2/internal/atlas" "github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/ui" @@ -787,6 +788,10 @@ func (i *Image) ReplacePixels(pixels []byte) { // // NewImage panics if RunGame already finishes. func NewImage(width, height int) *Image { + return newImage(width, height, atlas.ImageTypeRegular) +} + +func newImage(width, height int, imageType atlas.ImageType) *Image { if isRunGameEnded() { panic(fmt.Sprintf("ebiten: NewImage cannot be called after RunGame finishes")) } @@ -797,7 +802,7 @@ func NewImage(width, height int) *Image { panic(fmt.Sprintf("ebiten: height at NewImage must be positive but %d", height)) } i := &Image{ - image: ui.NewImage(width, height), + image: ui.NewImage(width, height, imageType), bounds: image.Rect(0, 0, width, height), } i.addr = i @@ -839,7 +844,7 @@ func NewImageFromImage(source image.Image) *Image { } i := &Image{ - image: ui.NewImage(width, height), + image: ui.NewImage(width, height, atlas.ImageTypeRegular), bounds: image.Rect(0, 0, width, height), } i.addr = i diff --git a/internal/atlas/image.go b/internal/atlas/image.go index 6d46fc2b2..ee2187755 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -206,14 +206,21 @@ func init() { backendsM.Lock() } +type ImageType int + +const ( + ImageTypeRegular ImageType = iota + ImageTypeScreen + ImageTypeVolatile + ImageTypeIsolated +) + // Image is a rectangle pixel set that might be on an atlas. type Image struct { - width int - height int - disposed bool - isolated bool - volatile bool - screen bool + width int + height int + imageType ImageType + disposed bool backend *backend @@ -284,7 +291,7 @@ func (i *Image) ensureIsolated() { sx1 /= float32(sw) sy1 /= float32(sh) typ := restorable.ImageTypeRegular - if i.volatile { + if i.imageType == ImageTypeVolatile { typ = restorable.ImageTypeVolatile } newImg := restorable.NewImage(w, h, typ) @@ -326,11 +333,8 @@ func (i *Image) putOnAtlas(graphicsDriver graphicsdriver.Graphics) error { if !i.canBePutOnAtlas() { panic("atlas: putOnAtlas cannot be called on a image that cannot be on an atlas") } - if i.volatile { - panic("atlas: a volatile image cannot be put on an atlas") - } - newI := NewImage(i.width, i.height) + newI := NewImage(i.width, i.height, i.imageType) if restorable.NeedsRestoring() { // If the underlying graphics driver requires restoring from the context lost, the pixel data is @@ -433,7 +437,7 @@ func (i *Image) drawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f var dx, dy float32 // A screen image doesn't have its padding. - if !i.screen { + if i.imageType != ImageTypeScreen { x, y, _, _ := i.regionWithPadding() dx = float32(x) + paddingSize dy = float32(y) + paddingSize @@ -708,43 +712,20 @@ func (i *Image) dispose(markDisposed bool) { theBackends = append(theBackends[:index], theBackends[index+1:]...) } -func NewImage(width, height int) *Image { +func NewImage(width, height int, imageType ImageType) *Image { // Actual allocation is done lazily, and the lock is not needed. return &Image{ - width: width, - height: height, + width: width, + height: height, + imageType: imageType, } } -func (i *Image) SetIsolated(isolated bool) { - if i.backend != nil { - panic("atlas: SetIsolated must be called before its backend is allocated") - } - i.isolated = isolated -} - -func (i *Image) SetVolatile(volatile bool) { - i.volatile = volatile - if i.backend == nil { - return - } - if i.volatile { - i.ensureIsolated() - } - i.backend.restorable.SetVolatile(i.volatile) -} - func (i *Image) canBePutOnAtlas() bool { if minSize == 0 || maxSize == 0 { panic("atlas: minSize or maxSize must be initialized") } - if i.isolated { - return false - } - if i.volatile { - return false - } - if i.screen { + if i.imageType != ImageTypeRegular { return false } return i.width+2*paddingSize <= maxSize && i.height+2*paddingSize <= maxSize @@ -757,7 +738,7 @@ func (i *Image) allocate(putOnAtlas bool) { runtime.SetFinalizer(i, (*Image).MarkDisposed) - if i.screen { + if i.imageType == ImageTypeScreen { // A screen image doesn't have a padding. i.backend = &backend{ restorable: restorable.NewImage(i.width, i.height, restorable.ImageTypeScreen), @@ -767,7 +748,7 @@ func (i *Image) allocate(putOnAtlas bool) { if !putOnAtlas || !i.canBePutOnAtlas() { typ := restorable.ImageTypeRegular - if i.volatile { + if i.imageType == ImageTypeVolatile { typ = restorable.ImageTypeVolatile } i.backend = &backend{ @@ -792,7 +773,7 @@ func (i *Image) allocate(putOnAtlas bool) { } typ := restorable.ImageTypeRegular - if i.volatile { + if i.imageType == ImageTypeVolatile { typ = restorable.ImageTypeVolatile } b := &backend{ @@ -816,16 +797,6 @@ func (i *Image) DumpScreenshot(graphicsDriver graphicsdriver.Graphics, path stri return i.backend.restorable.Dump(graphicsDriver, path, blackbg, image.Rect(paddingSize, paddingSize, paddingSize+i.width, paddingSize+i.height)) } -func NewScreenFramebufferImage(width, height int) *Image { - // Actual allocation is done lazily. - i := &Image{ - width: width, - height: height, - screen: true, - } - return i -} - func EndFrame(graphicsDriver graphicsdriver.Graphics) error { backendsM.Lock() diff --git a/internal/atlas/image_test.go b/internal/atlas/image_test.go index 81db81a9d..4d29d97e1 100644 --- a/internal/atlas/image_test.go +++ b/internal/atlas/image_test.go @@ -60,25 +60,25 @@ const bigSize = 2049 func TestEnsureIsolated(t *testing.T) { // Create img1 and img2 with this size so that the next images are allocated // with non-upper-left location. - img1 := atlas.NewImage(bigSize, 100) + img1 := atlas.NewImage(bigSize, 100, atlas.ImageTypeRegular) defer img1.MarkDisposed() // Ensure img1's region is allocated. img1.ReplacePixels(make([]byte, 4*bigSize*100), nil) - img2 := atlas.NewImage(100, bigSize) + img2 := atlas.NewImage(100, bigSize, atlas.ImageTypeRegular) defer img2.MarkDisposed() img2.ReplacePixels(make([]byte, 4*100*bigSize), nil) const size = 32 - img3 := atlas.NewImage(size/2, size/2) + img3 := atlas.NewImage(size/2, size/2, atlas.ImageTypeRegular) defer img3.MarkDisposed() img3.ReplacePixels(make([]byte, (size/2)*(size/2)*4), nil) - img4 := atlas.NewImage(size, size) + img4 := atlas.NewImage(size, size, atlas.ImageTypeRegular) defer img4.MarkDisposed() - img5 := atlas.NewImage(size/2, size/2) + img5 := atlas.NewImage(size/2, size/2, atlas.ImageTypeRegular) defer img3.MarkDisposed() pix := make([]byte, size*size*4) @@ -155,18 +155,18 @@ func TestEnsureIsolated(t *testing.T) { func TestReputOnAtlas(t *testing.T) { const size = 16 - img0 := atlas.NewImage(size, size) + img0 := atlas.NewImage(size, size, atlas.ImageTypeRegular) defer img0.MarkDisposed() img0.ReplacePixels(make([]byte, 4*size*size), nil) - img1 := atlas.NewImage(size, size) + img1 := atlas.NewImage(size, size, atlas.ImageTypeRegular) defer img1.MarkDisposed() img1.ReplacePixels(make([]byte, 4*size*size), nil) if got, want := img1.IsOnAtlasForTesting(), true; got != want { t.Errorf("got: %v, want: %v", got, want) } - img2 := atlas.NewImage(size, size) + img2 := atlas.NewImage(size, size, atlas.ImageTypeRegular) defer img2.MarkDisposed() pix := make([]byte, 4*size*size) for j := 0; j < size; j++ { @@ -179,8 +179,8 @@ func TestReputOnAtlas(t *testing.T) { } img2.ReplacePixels(pix, nil) - img3 := atlas.NewImage(size, size) - img3.SetVolatile(true) + // Create a volatile image. This should always be isolated. + img3 := atlas.NewImage(size, size, atlas.ImageTypeVolatile) defer img3.MarkDisposed() img1.ReplacePixels(make([]byte, 4*size*size), nil) if got, want := img3.IsOnAtlasForTesting(), false; got != want { @@ -303,7 +303,7 @@ func TestReputOnAtlas(t *testing.T) { func TestExtend(t *testing.T) { const w0, h0 = 100, 100 - img0 := atlas.NewImage(w0, h0) + img0 := atlas.NewImage(w0, h0, atlas.ImageTypeRegular) defer img0.MarkDisposed() p0 := make([]byte, 4*w0*h0) @@ -316,7 +316,7 @@ func TestExtend(t *testing.T) { img0.ReplacePixels(p0, nil) const w1, h1 = minImageSizeForTesting + 1, 100 - img1 := atlas.NewImage(w1, h1) + img1 := atlas.NewImage(w1, h1, atlas.ImageTypeRegular) defer img1.MarkDisposed() p1 := make([]byte, 4*w1*h1) @@ -370,9 +370,9 @@ func TestExtend(t *testing.T) { func TestReplacePixelsAfterDrawTriangles(t *testing.T) { const w, h = 256, 256 - src := atlas.NewImage(w, h) + src := atlas.NewImage(w, h, atlas.ImageTypeRegular) defer src.MarkDisposed() - dst := atlas.NewImage(w, h) + dst := atlas.NewImage(w, h, atlas.ImageTypeRegular) defer dst.MarkDisposed() pix := make([]byte, 4*w*h) @@ -418,9 +418,9 @@ func TestReplacePixelsAfterDrawTriangles(t *testing.T) { // Issue #887 func TestSmallImages(t *testing.T) { const w, h = 4, 8 - src := atlas.NewImage(w, h) + src := atlas.NewImage(w, h, atlas.ImageTypeRegular) defer src.MarkDisposed() - dst := atlas.NewImage(w, h) + dst := atlas.NewImage(w, h, atlas.ImageTypeRegular) defer dst.MarkDisposed() pix := make([]byte, 4*w*h) @@ -463,11 +463,11 @@ func TestSmallImages(t *testing.T) { // Issue #887 func TestLongImages(t *testing.T) { const w, h = 1, 6 - src := atlas.NewImage(w, h) + src := atlas.NewImage(w, h, atlas.ImageTypeRegular) defer src.MarkDisposed() const dstW, dstH = 256, 256 - dst := atlas.NewImage(dstW, dstH) + dst := atlas.NewImage(dstW, dstH, atlas.ImageTypeRegular) defer dst.MarkDisposed() pix := make([]byte, 4*w*h) @@ -511,11 +511,11 @@ func TestLongImages(t *testing.T) { func TestDisposeImmediately(t *testing.T) { // This tests restorable.Image.ClearPixels is called but ReplacePixels is not called. - img0 := atlas.NewImage(16, 16) + img0 := atlas.NewImage(16, 16, atlas.ImageTypeRegular) img0.EnsureIsolatedForTesting() defer img0.MarkDisposed() - img1 := atlas.NewImage(16, 16) + img1 := atlas.NewImage(16, 16, atlas.ImageTypeRegular) img1.EnsureIsolatedForTesting() defer img1.MarkDisposed() @@ -524,12 +524,12 @@ func TestDisposeImmediately(t *testing.T) { // Issue #1028 func TestExtendWithBigImage(t *testing.T) { - img0 := atlas.NewImage(1, 1) + img0 := atlas.NewImage(1, 1, atlas.ImageTypeRegular) defer img0.MarkDisposed() img0.ReplacePixels(make([]byte, 4*1*1), nil) - img1 := atlas.NewImage(minImageSizeForTesting+1, minImageSizeForTesting+1) + img1 := atlas.NewImage(minImageSizeForTesting+1, minImageSizeForTesting+1, atlas.ImageTypeRegular) defer img1.MarkDisposed() img1.ReplacePixels(make([]byte, 4*(minImageSizeForTesting+1)*(minImageSizeForTesting+1)), nil) @@ -539,7 +539,7 @@ func TestExtendWithBigImage(t *testing.T) { func TestMaxImageSize(t *testing.T) { // This tests that a too-big image is allocated correctly. s := maxImageSizeForTesting - 2*atlas.PaddingSize - img := atlas.NewImage(s, s) + img := atlas.NewImage(s, s, atlas.ImageTypeRegular) defer img.MarkDisposed() img.ReplacePixels(make([]byte, 4*s*s), nil) } @@ -552,7 +552,7 @@ func Disable_TestMinImageSize(t *testing.T) { // This tests that extending a backend works correctly. // Though the image size is minimum size of the backend, extending the backend happens due to the paddings. s := minImageSizeForTesting - img := atlas.NewImage(s, s) + img := atlas.NewImage(s, s, atlas.ImageTypeRegular) defer img.MarkDisposed() img.ReplacePixels(make([]byte, 4*s*s), nil) } @@ -561,11 +561,11 @@ func Disable_TestMinImageSize(t *testing.T) { func TestDisposedAndReputOnAtlas(t *testing.T) { const size = 16 - src := atlas.NewImage(size, size) + src := atlas.NewImage(size, size, atlas.ImageTypeRegular) defer src.MarkDisposed() - src2 := atlas.NewImage(size, size) + src2 := atlas.NewImage(size, size, atlas.ImageTypeRegular) defer src2.MarkDisposed() - dst := atlas.NewImage(size, size) + dst := atlas.NewImage(size, size, atlas.ImageTypeRegular) defer dst.MarkDisposed() // Use src as a render target so that src is not on an atlas. @@ -609,11 +609,11 @@ func TestDisposedAndReputOnAtlas(t *testing.T) { func TestImageIsNotReputOnAtlasWithoutUsingAsSource(t *testing.T) { const size = 16 - src := atlas.NewImage(size, size) + src := atlas.NewImage(size, size, atlas.ImageTypeRegular) defer src.MarkDisposed() - src2 := atlas.NewImage(size, size) + src2 := atlas.NewImage(size, size, atlas.ImageTypeRegular) defer src2.MarkDisposed() - dst := atlas.NewImage(size, size) + dst := atlas.NewImage(size, size, atlas.ImageTypeRegular) defer dst.MarkDisposed() // Use src as a render target so that src is not on an atlas. diff --git a/internal/atlas/shader_test.go b/internal/atlas/shader_test.go index d9c8dfb0c..8c164d2e3 100644 --- a/internal/atlas/shader_test.go +++ b/internal/atlas/shader_test.go @@ -29,7 +29,7 @@ import ( func TestShaderFillTwice(t *testing.T) { const w, h = 1, 1 - dst := atlas.NewImage(w, h) + dst := atlas.NewImage(w, h, atlas.ImageTypeRegular) vs := quadVertices(w, h, 0, 0, 1) is := graphics.QuadIndices() @@ -60,10 +60,10 @@ func TestShaderFillTwice(t *testing.T) { func TestImageDrawTwice(t *testing.T) { const w, h = 1, 1 - dst := atlas.NewImage(w, h) - src0 := atlas.NewImage(w, h) + dst := atlas.NewImage(w, h, atlas.ImageTypeRegular) + src0 := atlas.NewImage(w, h, atlas.ImageTypeRegular) src0.ReplacePixels([]byte{0xff, 0xff, 0xff, 0xff}, nil) - src1 := atlas.NewImage(w, h) + src1 := atlas.NewImage(w, h, atlas.ImageTypeRegular) src1.ReplacePixels([]byte{0x80, 0x80, 0x80, 0xff}, nil) vs := quadVertices(w, h, 0, 0, 1) diff --git a/internal/buffered/image.go b/internal/buffered/image.go index e176e7ff7..9224095dc 100644 --- a/internal/buffered/image.go +++ b/internal/buffered/image.go @@ -45,70 +45,25 @@ func EndFrame(graphicsDriver graphicsdriver.Graphics) error { return atlas.EndFrame(graphicsDriver) } -func NewImage(width, height int) *Image { +func NewImage(width, height int, imageType atlas.ImageType) *Image { i := &Image{ width: width, height: height, } - i.initialize() + i.initialize(imageType) return i } -func (i *Image) initialize() { +func (i *Image) initialize(imageType atlas.ImageType) { if maybeCanAddDelayedCommand() { if tryAddDelayedCommand(func() error { - i.initialize() + i.initialize(imageType) return nil }) { return } } - i.img = atlas.NewImage(i.width, i.height) -} - -func (i *Image) SetIsolated(isolated bool) { - if maybeCanAddDelayedCommand() { - if tryAddDelayedCommand(func() error { - i.SetIsolated(isolated) - return nil - }) { - return - } - } - i.img.SetIsolated(isolated) -} - -func (i *Image) SetVolatile(volatile bool) { - if maybeCanAddDelayedCommand() { - if tryAddDelayedCommand(func() error { - i.SetVolatile(volatile) - return nil - }) { - return - } - } - i.img.SetVolatile(volatile) -} - -func NewScreenFramebufferImage(width, height int) *Image { - i := &Image{} - i.initializeAsScreenFramebuffer(width, height) - return i -} - -func (i *Image) initializeAsScreenFramebuffer(width, height int) { - if maybeCanAddDelayedCommand() { - if tryAddDelayedCommand(func() error { - i.initializeAsScreenFramebuffer(width, height) - return nil - }) { - return - } - } - - i.img = atlas.NewScreenFramebufferImage(width, height) - i.width = width - i.height = height + i.img = atlas.NewImage(i.width, i.height, imageType) } func (i *Image) invalidatePixels() { diff --git a/internal/mipmap/mipmap.go b/internal/mipmap/mipmap.go index 49ec31a78..f187ed58f 100644 --- a/internal/mipmap/mipmap.go +++ b/internal/mipmap/mipmap.go @@ -19,6 +19,7 @@ import ( "math" "github.com/hajimehoshi/ebiten/v2/internal/affine" + "github.com/hajimehoshi/ebiten/v2/internal/atlas" "github.com/hajimehoshi/ebiten/v2/internal/buffered" "github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" @@ -35,38 +36,15 @@ type Mipmap struct { imgs map[int]*buffered.Image } -func New(width, height int) *Mipmap { +func New(width, height int, imageType atlas.ImageType) *Mipmap { return &Mipmap{ - width: width, - height: height, - orig: buffered.NewImage(width, height), + width: width, + height: height, + orig: buffered.NewImage(width, height, imageType), + volatile: imageType == atlas.ImageTypeVolatile, } } -func NewScreenFramebufferMipmap(width, height int) *Mipmap { - return &Mipmap{ - width: width, - height: height, - orig: buffered.NewScreenFramebufferImage(width, height), - } -} - -func (m *Mipmap) SetIsolated(isolated bool) { - m.orig.SetIsolated(isolated) -} - -func (m *Mipmap) SetVolatile(volatile bool) { - if m.volatile == volatile { - return - } - - m.volatile = volatile - if m.volatile { - m.disposeMipmaps() - } - m.orig.SetVolatile(volatile) -} - func (m *Mipmap) DumpScreenshot(graphicsDriver graphicsdriver.Graphics, name string, blackbg bool) error { return m.orig.DumpScreenshot(graphicsDriver, name, blackbg) } @@ -203,8 +181,7 @@ func (m *Mipmap) level(level int) *buffered.Image { m.setImg(level, nil) return nil } - s := buffered.NewImage(w2, h2) - s.SetVolatile(m.volatile) + s := buffered.NewImage(w2, h2, atlas.ImageTypeVolatile) dstRegion := graphicsdriver.Region{ X: 0, diff --git a/internal/ui/context.go b/internal/ui/context.go index 1034954cc..b47298ac9 100644 --- a/internal/ui/context.go +++ b/internal/ui/context.go @@ -21,6 +21,7 @@ import ( "sync/atomic" "github.com/hajimehoshi/ebiten/v2/internal/affine" + "github.com/hajimehoshi/ebiten/v2/internal/atlas" "github.com/hajimehoshi/ebiten/v2/internal/buffered" "github.com/hajimehoshi/ebiten/v2/internal/clock" "github.com/hajimehoshi/ebiten/v2/internal/debug" @@ -149,13 +150,6 @@ func (c *context) drawGame(graphicsDriver graphicsdriver.Graphics) { w, h := c.offscreen.width, c.offscreen.height c.offscreen.MarkDisposed() c.offscreen = c.game.NewOffscreenImage(w, h) - - // TODO: Give volatile/isolated property to the constructor. - if theGlobalState.isScreenClearedEveryFrame() { - c.offscreen.setVolatile(true) - } else { - c.offscreen.mipmap.SetIsolated(true) - } } // Even though updateCount == 0, the offscreen is cleared and Draw is called. @@ -244,7 +238,7 @@ func (c *context) layoutGame(outsideWidth, outsideHeight float64, deviceScaleFac } } if c.screen == nil { - c.screen = newScreenFramebufferImage(sw, sh) + c.screen = NewImage(sw, sh, atlas.ImageTypeScreen) } if c.offscreen != nil { @@ -255,17 +249,6 @@ func (c *context) layoutGame(outsideWidth, outsideHeight float64, deviceScaleFac } if c.offscreen == nil { c.offscreen = c.game.NewOffscreenImage(ow, oh) - - // TODO: Give volatile/isolated property to the constructor. - if theGlobalState.isScreenClearedEveryFrame() { - c.offscreen.setVolatile(true) - } else { - // Keep the offscreen an isolated image from an atlas (#1938). - // The shader program for the screen is special and doesn't work well with an image on an atlas. - // An image on an atlas is surrounded by a transparent edge, - // and the shader program unexpectedly picks the pixel on the edges. - c.offscreen.mipmap.SetIsolated(true) - } } return ow, oh diff --git a/internal/ui/image.go b/internal/ui/image.go index d3ce4ed17..994694b2e 100644 --- a/internal/ui/image.go +++ b/internal/ui/image.go @@ -16,6 +16,7 @@ package ui import ( "github.com/hajimehoshi/ebiten/v2/internal/affine" + "github.com/hajimehoshi/ebiten/v2/internal/atlas" "github.com/hajimehoshi/ebiten/v2/internal/graphics" "github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver" "github.com/hajimehoshi/ebiten/v2/internal/mipmap" @@ -36,27 +37,14 @@ type Image struct { volatile bool } -func NewImage(width, height int) *Image { +func NewImage(width, height int, imageType atlas.ImageType) *Image { return &Image{ - mipmap: mipmap.New(width, height), + mipmap: mipmap.New(width, height, imageType), width: width, height: height, } } -func newScreenFramebufferImage(width, height int) *Image { - return &Image{ - mipmap: mipmap.NewScreenFramebufferMipmap(width, height), - width: width, - height: height, - } -} - -func (i *Image) setVolatile(volatile bool) { - i.volatile = volatile - i.mipmap.SetVolatile(volatile) -} - func (i *Image) MarkDisposed() { if i.mipmap == nil { return @@ -112,7 +100,7 @@ func DumpImages(dir string) error { } var ( - emptyImage = NewImage(3, 3) + emptyImage = NewImage(3, 3, atlas.ImageTypeRegular) ) func init() {