diff --git a/internal/restorable/image.go b/internal/restorable/image.go index 25f51a122..18996789e 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -23,6 +23,7 @@ import ( "github.com/hajimehoshi/ebiten/internal/affine" "github.com/hajimehoshi/ebiten/internal/graphics" "github.com/hajimehoshi/ebiten/internal/opengl" + "github.com/hajimehoshi/ebiten/internal/sync" ) // drawImageHistoryItem is an item for history of draw-image commands. @@ -70,6 +71,8 @@ type Image struct { // screen indicates whether the image is used as an actual screen. screen bool + + m sync.Mutex } // NewImage creates an empty image with the given size. @@ -131,6 +134,9 @@ func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) { // For this purpuse, images should remember which part of that is used for DrawImage. theImages.makeStaleIfDependingOn(i) + i.m.Lock() + defer i.m.Unlock() + i.image.ReplacePixels(pixels, x, y, width, height) if x == 0 && y == 0 && width == w && height == h { @@ -160,11 +166,16 @@ func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) { // DrawImage draws a given image img to the image. func (i *Image) DrawImage(img *Image, sx0, sy0, sx1, sy1 int, geom *affine.GeoM, colorm *affine.ColorM, mode opengl.CompositeMode, filter graphics.Filter) { - vs := img.vertices(sx0, sy0, sx1, sy1, geom) + w, h := img.Size() + vs := vertices(w, h, sx0, sy0, sx1, sy1, geom) if vs == nil { return } theImages.makeStaleIfDependingOn(i) + + i.m.Lock() + defer i.m.Unlock() + if img.stale || img.volatile || i.screen || !IsRestoringEnabled() { i.makeStale() } else { @@ -210,6 +221,10 @@ func (i *Image) At(x, y int) (color.RGBA, error) { if x < 0 || y < 0 || w <= x || h <= y { return color.RGBA{}, nil } + + i.m.Lock() + defer i.m.Unlock() + if err := graphics.FlushCommands(); err != nil { return color.RGBA{}, err } @@ -225,6 +240,9 @@ func (i *Image) At(x, y int) (color.RGBA, error) { // makeStaleIfDependingOn makes the image stale if the image depends on target. func (i *Image) makeStaleIfDependingOn(target *Image) { + i.m.Lock() + defer i.m.Unlock() + if i.stale { return } @@ -250,6 +268,10 @@ func (i *Image) resolveStale() error { if !IsRestoringEnabled() { return nil } + + i.m.Lock() + defer i.m.Unlock() + if i.volatile { return nil } @@ -274,6 +296,9 @@ func (i *Image) dependsOn(target *Image) bool { // dependingImages returns all images that is depended by the image. func (i *Image) dependingImages() map[*Image]struct{} { + i.m.Lock() + defer i.m.Unlock() + r := map[*Image]struct{}{} for _, c := range i.drawImageHistory { r[c.image] = struct{}{} @@ -283,6 +308,9 @@ func (i *Image) dependingImages() map[*Image]struct{} { // hasDependency returns a boolean value indicating whether the image depends on another image. func (i *Image) hasDependency() bool { + i.m.Lock() + defer i.m.Unlock() + if i.stale { return false } @@ -291,6 +319,9 @@ func (i *Image) hasDependency() bool { // Restore restores *graphics.Image from the pixels using its state. func (i *Image) restore() error { + i.m.Lock() + defer i.m.Unlock() + w, h := i.image.Size() if i.screen { // The screen image should also be recreated because framebuffer might @@ -348,6 +379,10 @@ func (i *Image) restore() error { // After disposing, calling the function of the image causes unexpected results. func (i *Image) Dispose() { theImages.remove(i) + + i.m.Lock() + defer i.m.Unlock() + i.image.Dispose() i.image = nil i.basePixels = nil @@ -364,9 +399,12 @@ func (i *Image) IsInvalidated() (bool, error) { if err := graphics.FlushCommands(); err != nil { return false, err } - if !IsRestoringEnabled() { return false, nil } + + i.m.Lock() + defer i.m.Unlock() + return i.image.IsInvalidated(), nil } diff --git a/internal/restorable/vertices.go b/internal/restorable/vertices.go index 2404c5fa5..6cbdc0c18 100644 --- a/internal/restorable/vertices.go +++ b/internal/restorable/vertices.go @@ -43,7 +43,7 @@ func (v *verticesBackend) get() []float32 { return s } -func (i *Image) vertices(sx0, sy0, sx1, sy1 int, geo *affine.GeoM) []float32 { +func vertices(width, height int, sx0, sy0, sx1, sy1 int, geo *affine.GeoM) []float32 { if sx0 >= sx1 || sy0 >= sy1 { return nil } @@ -58,7 +58,6 @@ func (i *Image) vertices(sx0, sy0, sx1, sy1 int, geo *affine.GeoM) []float32 { // it really feels like we should be able to cache this computation // but it may not matter. - width, height := i.Size() w := 1 h := 1 for w < width {