diff --git a/image.go b/image.go index 038db1a85..5ca31639f 100644 --- a/image.go +++ b/image.go @@ -78,14 +78,6 @@ func (i *Image) Clear() error { return nil } -var emptyImage *Image - -func init() { - emptyImage, _ = NewImage(1, 1, FilterDefault) - // (*Image).Fill uses emptyImage, then Fill cannot be called here. - emptyImage.ReplacePixels([]byte{0xff, 0xff, 0xff, 0xff}) -} - // Fill fills the image with a solid color. // // When the image is disposed, Fill does nothing. @@ -104,27 +96,8 @@ func (i *Image) Fill(clr color.Color) error { i.resolvePendingPixels(false) - r, g, b, a := clr.RGBA() - rf, gf, bf, af := 0.0, 0.0, 0.0, 0.0 - if a > 0 { - rf = float64(r) / float64(a) - gf = float64(g) / float64(a) - bf = float64(b) / float64(a) - af = float64(a) / 0xffff - } - - sw, sh := emptyImage.Size() - dw, dh := i.Size() - - op := &DrawImageOptions{} - op.GeoM.Scale(float64(dw)/float64(sw), float64(dh)/float64(sh)) - op.ColorM.Scale(rf, gf, bf, af) - // TODO: Use the previous composite mode if possible. - if af < 1.0 { - op.CompositeMode = CompositeModeCopy - } - // TODO: As all the pixels will be changed, this image can reset the information for restoring. - i.DrawImage(emptyImage, op) + i.mipmap.original().Fill(clr) + i.disposeMipmaps() return nil } diff --git a/internal/restorable/image.go b/internal/restorable/image.go index a50d7fa83..38b5cc1fa 100644 --- a/internal/restorable/image.go +++ b/internal/restorable/image.go @@ -16,6 +16,7 @@ package restorable import ( "fmt" + "image/color" "github.com/hajimehoshi/ebiten/internal/affine" "github.com/hajimehoshi/ebiten/internal/driver" @@ -100,7 +101,9 @@ type Image struct { var emptyImage *Image func init() { - const w, h = 16, 16 + // Use a big-enough image as an rendering source. By enlarging with x128, this can reach to 16384. + // See #907 for details. + const w, h = 128, 128 emptyImage = &Image{ image: graphicscommand.NewImage(w, h), width: w, @@ -229,6 +232,40 @@ func (i *Image) clear() { i.stale = false } +// Fill fills the specified part of the image with a solid color. +func (i *Image) Fill(clr color.Color, x, y, width, height int) { + // If all the pixels will be changed, reset the information for restoring. + if w, h := i.Size(); x == 0 && y == 0 && width == w && height == h { + i.basePixels = Pixels{} + i.drawTrianglesHistory = nil + i.stale = false + } + + var rf, gf, bf, af float32 + if r, g, b, a := clr.RGBA(); a > 0 { + rf = float32(r) / float32(a) + gf = float32(g) / float32(a) + bf = float32(b) / float32(a) + af = float32(a) / 0xffff + } + + // TODO: Use the previous composite mode if possible. + compositemode := driver.CompositeModeSourceOver + if af < 1.0 { + compositemode = driver.CompositeModeCopy + } + + dw, dh := width, height + sw, sh := emptyImage.Size() + vs := make([]float32, 4*graphics.VertexFloatNum) + graphics.PutQuadVertices(vs, emptyImage, 0, 0, sw, sh, + float32(dw)/float32(sw), 0, 0, float32(dh)/float32(sh), float32(x), float32(y), + rf, gf, bf, af) + is := graphics.QuadIndices() + + i.DrawTriangles(emptyImage, vs, is, nil, compositemode, driver.FilterNearest, driver.AddressClampToZero) +} + func (i *Image) IsVolatile() bool { return i.volatile } diff --git a/internal/shareable/shareable.go b/internal/shareable/shareable.go index ef0242928..fdeb50e56 100644 --- a/internal/shareable/shareable.go +++ b/internal/shareable/shareable.go @@ -16,6 +16,7 @@ package shareable import ( "fmt" + "image/color" "runtime" "sync" @@ -329,6 +330,28 @@ func (i *Image) DrawTriangles(img *Image, vertices []float32, indices []uint16, } } +func (i *Image) Fill(clr color.Color) { + backendsM.Lock() + defer backendsM.Unlock() + + if i.disposed { + panic("shareable: the drawing target image must not be disposed (Fill)") + } + if i.backend == nil { + if _, _, _, a := clr.RGBA(); a == 0 { + return + } + } + + i.ensureNotShared() + + x, y, width, height := i.region() + i.backend.restorable.Fill(clr, x, y, width, height) + + i.nonUpdatedCount = 0 + delete(imagesToMakeShared, i) +} + // ClearFramebuffer clears the image with a color. This affects not only the (0, 0)-(width, height) region but also // the whole framebuffer region. func (i *Image) ClearFramebuffer() {