restorable: Add Fill

This is useful to reduce opportunity to make the image stale.
This commit is contained in:
Hajime Hoshi 2019-02-12 22:50:35 +09:00
parent 40f728a326
commit 93d0b0dd09
2 changed files with 52 additions and 60 deletions

View File

@ -60,14 +60,20 @@ type Image struct {
priority bool priority bool
} }
var dummyImage *Image var emptyImage *Image
func init() { func init() {
dummyImage = &Image{ const w, h = 16, 16
image: graphicscommand.NewImage(16, 16), emptyImage = &Image{
image: graphicscommand.NewImage(w, h),
priority: true, priority: true,
} }
theImages.add(dummyImage) pix := make([]byte, 4*w*h)
for i := range pix {
pix[i] = 0xff
}
emptyImage.ReplacePixels(pix, 0, 0, w, h)
theImages.add(emptyImage)
} }
// NewImage creates an empty image with the given size. // NewImage creates an empty image with the given size.
@ -103,30 +109,61 @@ func NewScreenFramebufferImage(width, height int) *Image {
return i return i
} }
func (i *Image) Clear() { func (i *Image) Fill(r, g, b, a uint8) {
theImages.makeStaleIfDependingOn(i) theImages.makeStaleIfDependingOn(i)
i.clear() i.fill(r, g, b, a)
} }
func (i *Image) clear() { func (i *Image) clear() {
i.fill(0, 0, 0, 0)
}
func (i *Image) fill(r, g, b, a uint8) {
if i.priority { if i.priority {
panic("restorable: clear cannot be called on a priority image") panic("restorable: clear cannot be called on a priority image")
} }
// There are not 'drawImageHistoryItem's for this image and dummyImage. rf := float32(0)
// As dummyImage is a priority image, this is restored before other regular images are restored. gf := float32(0)
bf := float32(0)
af := float32(0)
if a > 0 {
rf = float32(r) / float32(a)
gf = float32(g) / float32(a)
bf = float32(b) / float32(a)
af = float32(a) / 0xff
}
// There are not 'drawImageHistoryItem's for this image and emptyImage.
// As emptyImage is a priority image, this is restored before other regular images are restored.
w, h := i.Size() w, h := i.Size()
sw, sh := dummyImage.Size() sw, sh := emptyImage.Size()
dw := graphics.NextPowerOf2Int(w) dw := graphics.NextPowerOf2Int(w)
dh := graphics.NextPowerOf2Int(h) dh := graphics.NextPowerOf2Int(h)
vs := graphics.QuadVertices(dw, dh, 0, 0, sw, sh, vs := graphics.QuadVertices(dw, dh, 0, 0, sw, sh,
float32(dw)/float32(sw), 0, 0, float32(dh)/float32(sh), float32(dw)/float32(sw), 0, 0, float32(dh)/float32(sh), 0, 0,
0, 0, rf, gf, bf, af)
1, 1, 1, 1)
is := graphics.QuadIndices() is := graphics.QuadIndices()
i.image.DrawImage(dummyImage.image, vs, is, nil, graphics.CompositeModeClear, graphics.FilterNearest, graphics.AddressClampToZero) c := graphics.CompositeModeCopy
if a == 0 {
c = graphics.CompositeModeClear
}
i.image.DrawImage(emptyImage.image, vs, is, nil, c, graphics.FilterNearest, graphics.AddressClampToZero)
i.basePixels = nil if a == 0 {
i.basePixels = nil
} else {
// TODO: Add baseColor?
if i.basePixels == nil {
i.basePixels = make([]byte, 4*w*h)
}
for idx := 0; idx < w*h; idx++ {
i.basePixels[4*idx] = r
i.basePixels[4*idx+1] = g
i.basePixels[4*idx+2] = b
i.basePixels[4*idx+3] = a
}
}
i.drawImageHistory = nil i.drawImageHistory = nil
i.stale = false i.stale = false
} }
@ -406,7 +443,6 @@ func (i *Image) restore() error {
gimg.ReplacePixels(i.basePixels, 0, 0, w, h) gimg.ReplacePixels(i.basePixels, 0, 0, w, h)
} else { } else {
// Clear the image explicitly. // Clear the image explicitly.
// TODO: Is dummyImage available for clearing?
pix := make([]uint8, w*h*4) pix := make([]uint8, w*h*4)
gimg.ReplacePixels(pix, 0, 0, w, h) gimg.ReplacePixels(pix, 0, 0, w, h)
} }

View File

@ -252,22 +252,6 @@ func (i *Image) DrawImage(img *Image, vertices []float32, indices []uint16, colo
// } // }
} }
// emptyImage is an empty image used for filling other images with a uniform color.
//
// Do not call Fill on emptyImage or the program causes infinite recursion.
var emptyImage = NewImage(16, 16)
func init() {
// Initialize emptyImage. As emptyImage is used for Fill, emptyImage itself cannot be initialized with Fill.
// Call ReplacePixels instead.
w, h := emptyImage.Size()
pix := make([]byte, 4*w*h)
for i := range pix {
pix[i] = 0xff
}
emptyImage.ReplacePixels(pix)
}
// Fill fills the image with a color. This affects not only the (0, 0)-(width, height) region but also the whole // Fill fills the image with a color. This affects not only the (0, 0)-(width, height) region but also the whole
// framebuffer region. // framebuffer region.
func (i *Image) Fill(r, g, b, a uint8) { func (i *Image) Fill(r, g, b, a uint8) {
@ -278,35 +262,7 @@ func (i *Image) Fill(r, g, b, a uint8) {
i.ensureNotShared() i.ensureNotShared()
backendsM.Unlock() backendsM.Unlock()
if r == 0 && g == 0 && b == 0 && a == 0 { i.backend.restorable.Fill(r, g, b, a)
i.backend.restorable.Clear()
return
}
rf := float32(0)
gf := float32(0)
bf := float32(0)
af := float32(0)
if a > 0 {
rf = float32(r) / float32(a)
gf = float32(g) / float32(a)
bf = float32(b) / float32(a)
af = float32(a) / 0xff
}
dw, dh := i.backend.restorable.SizePowerOf2()
sw, sh := emptyImage.Size()
vs := emptyImage.QuadVertices(0, 0, sw, sh,
float32(dw)/float32(sw), 0, 0, float32(dh)/float32(sh), 0, 0,
rf, gf, bf, af)
is := graphics.QuadIndices()
c := graphics.CompositeModeCopy
if a == 0xff {
// If the color is opaque, SourceOver is available and this is preferable for optimization.
c = graphics.CompositeModeSourceOver
}
i.DrawImage(emptyImage, vs, is, nil, c, graphics.FilterNearest, graphics.AddressClampToZero)
} }
func (i *Image) ReplacePixels(p []byte) { func (i *Image) ReplacePixels(p []byte) {