diff --git a/internal/graphicscommand/image.go b/internal/graphicscommand/image.go index 164df5501..9699b5ba6 100644 --- a/internal/graphicscommand/image.go +++ b/internal/graphicscommand/image.go @@ -26,15 +26,6 @@ import ( "github.com/hajimehoshi/ebiten/internal/png" ) -type lastCommand int - -const ( - lastCommandNone lastCommand = iota - lastCommandClear - lastCommandDrawTriangles - lastCommandReplacePixels -) - // Image represents an image that is implemented with OpenGL. type Image struct { image driver.Image @@ -51,8 +42,6 @@ type Image struct { id int bufferedRP []*driver.ReplacePixelsArgs - - lastCommand lastCommand } var nextID = 1 @@ -151,12 +140,6 @@ func (i *Image) InternalSize() (int, int) { // If the source image is not specified, i.e., src is nil and there is no image in the uniform variables, the // elements for the source image are not used. func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [graphics.ShaderImageNum - 1][2]float32, vertices []float32, indices []uint16, clr *affine.ColorM, mode driver.CompositeMode, filter driver.Filter, address driver.Address, sourceRegion driver.Region, shader *Shader, uniforms []interface{}) { - if i.lastCommand == lastCommandNone { - if !i.screen && mode != driver.CompositeModeClear { - panic("graphicscommand: the image must be cleared first") - } - } - for _, src := range srcs { if src == nil { continue @@ -169,12 +152,6 @@ func (i *Image) DrawTriangles(srcs [graphics.ShaderImageNum]*Image, offsets [gra i.resolveBufferedReplacePixels() theCommandQueue.EnqueueDrawTrianglesCommand(i, srcs, offsets, vertices, indices, clr, mode, filter, address, sourceRegion, shader, uniforms) - - if i.lastCommand == lastCommandNone && !i.screen { - i.lastCommand = lastCommandClear - } else { - i.lastCommand = lastCommandDrawTriangles - } } // Pixels returns the image's pixels. @@ -193,12 +170,6 @@ func (i *Image) Pixels() ([]byte, error) { } func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) { - // ReplacePixels for a part might invalidate the current image that are drawn by DrawTriangles (#593, #738). - if i.lastCommand == lastCommandDrawTriangles { - if x != 0 || y != 0 || i.width != width || i.height != height { - panic("graphicscommand: ReplacePixels for a part after DrawTriangles is forbidden") - } - } i.bufferedRP = append(i.bufferedRP, &driver.ReplacePixelsArgs{ Pixels: pixels, X: x, @@ -206,7 +177,6 @@ func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) { Width: width, Height: height, }) - i.lastCommand = lastCommandReplacePixels } func (i *Image) IsInvalidated() bool { diff --git a/internal/graphicscommand/image_test.go b/internal/graphicscommand/image_test.go index bb76042eb..ebab11fec 100644 --- a/internal/graphicscommand/image_test.go +++ b/internal/graphicscommand/image_test.go @@ -63,11 +63,6 @@ func TestClear(t *testing.T) { } func TestReplacePixelsPartAfterDrawTriangles(t *testing.T) { - defer func() { - if r := recover(); r == nil { - t.Errorf("ReplacePixels must panic but not") - } - }() const w, h = 32, 32 clr := NewImage(w, h) src := NewImage(w/2, h/2) @@ -77,6 +72,8 @@ func TestReplacePixelsPartAfterDrawTriangles(t *testing.T) { dst.DrawTriangles([graphics.ShaderImageNum]*Image{clr}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeClear, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) dst.DrawTriangles([graphics.ShaderImageNum]*Image{src}, [graphics.ShaderImageNum - 1][2]float32{}, vs, is, nil, driver.CompositeModeSourceOver, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) dst.ReplacePixels(make([]byte, 4), 0, 0, 1, 1) + + // TODO: Check the result. } func TestShader(t *testing.T) { diff --git a/internal/restorable/image.go b/internal/restorable/image.go index 0febc1f45..1b33a4302 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -153,6 +153,18 @@ func NewImage(width, height int, volatile bool) *Image { return i } +func newImageWithColor(width, height int, volatile bool, clr color.RGBA) *Image { + i := &Image{ + image: graphicscommand.NewImage(width, height), + width: width, + height: height, + volatile: volatile, + } + fillImage(i.image, clr) + theImages.add(i) + return i +} + // Extend extends the image by the given size. // Extend creates a new image with the given size and copies the pixels of the given source image. // Extend disposes itself after its call. @@ -167,21 +179,22 @@ func (i *Image) Extend(width, height int) *Image { panic(fmt.Sprintf("restorable: the original size (%d, %d) cannot be extended to (%d, %d)", i.width, i.height, width, height)) } - if i.stale { - panic("restorable: Extend at a stale image is forbidden") - } + newImg := newImageWithColor(width, height, i.volatile, i.basePixels.baseColor) - if len(i.drawTrianglesHistory) > 0 { - panic("restorable: Extend after DrawTriangles is forbidden") - } + // Use DrawTriangles instead of ReplacePixels because the image i might be stale and not have its pixels + // information. + srcs := [graphics.ShaderImageNum]*Image{i} + var offsets [graphics.ShaderImageNum - 1][2]float32 + sw, sh := i.image.InternalSize() + vs := quadVertices(0, 0, float32(sw), float32(sh), 0, 0, float32(sw), float32(sh), 1, 1, 1, 1) + is := graphics.QuadIndices() + newImg.DrawTriangles(srcs, offsets, vs, is, nil, driver.CompositeModeCopy, driver.FilterNearest, driver.AddressUnsafe, driver.Region{}, nil, nil) - newImg := NewImage(width, height, i.volatile) - i.basePixels.Apply(newImg.image) - - if i.basePixels.baseColor != (color.RGBA{}) { - panic("restorable: baseColor must be empty at Extend") - } + // Overwrite the history as if the image newImg is created only by ReplacePixels. Now drawTrianglesHistory + // and basePixels cannot be mixed. + newImg.drawTrianglesHistory = nil newImg.basePixels = i.basePixels + newImg.stale = i.stale i.Dispose() @@ -312,6 +325,11 @@ func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) { i.image.ReplacePixels(make([]byte, 4*width*height), x, y, width, height) } + if !needsRestoring() || i.screen || i.volatile { + i.makeStale() + return + } + if x == 0 && y == 0 && width == w && height == h { if pixels != nil { i.basePixels.AddOrReplace(pixels, 0, 0, w, h) @@ -323,9 +341,7 @@ func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) { return } - // It looked like ReplacePixels on a part of image deletes other region that are rendered by DrawTriangles - // (#593, #758). - + // drawTrianglesHistory and basePixels cannot be mixed. if len(i.drawTrianglesHistory) > 0 { panic("restorable: ReplacePixels for a part after DrawTriangles is forbidden") }