restorable: Add (*Image).Fill

This resets the infomation for restoring if possible. This makes
it more efficient to use offscreens.
This commit is contained in:
Hajime Hoshi 2019-08-25 02:39:59 +09:00
parent 856e2df1ec
commit 3960a4bbdf
3 changed files with 63 additions and 30 deletions

View File

@ -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
}

View File

@ -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
}

View File

@ -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() {