shareable: NewImage* now returns a cleared image

This commit is contained in:
Hajime Hoshi 2018-04-06 00:36:15 +09:00
parent 1a335032a2
commit 7b5ad46e49
4 changed files with 39 additions and 48 deletions

View File

@ -102,12 +102,11 @@ func (c *graphicsContext) Update(afterFrameUpdate func()) error {
// TODO: This clear is needed only when the screen size is changed.
if c.offsetX > 0 || c.offsetY > 0 {
op := &DrawImageOptions{}
w, h := dummyImage.Size()
w, h := emptyImage.Size()
s := float64(graphics.MaxImageSize())
op.GeoM.Scale(s/float64(w), s/float64(h))
op.ColorM.Scale(0, 0, 0, 0)
op.CompositeMode = CompositeModeCopy
c.screen.DrawImage(dummyImage, op)
c.screen.DrawImage(emptyImage, op)
}
dw, dh := c.screen.Size()

View File

@ -26,10 +26,14 @@ import (
"github.com/hajimehoshi/ebiten/internal/shareable"
)
// dummyImage is an empty image used for filling other images with a uniform color.
// emptyImage is an empty image used for filling other images with a uniform color.
//
// Do not call Fill or Clear on dummyImage or the program causes infinite recursion.
var dummyImage = newImageWithoutInit(16, 16)
// Do not call Fill or Clear on emptyImage or the program causes infinite recursion.
var emptyImage *Image
func init() {
emptyImage, _ = NewImage(16, 16, FilterDefault)
}
// Image represents a rectangle set of pixels.
// The pixel format is alpha-premultiplied RGBA.
@ -82,12 +86,11 @@ func (i *Image) Fill(clr color.Color) error {
func (i *Image) fill(r, g, b, a uint8) {
wd, hd := i.Size()
ws, hs := dummyImage.Size()
ws, hs := emptyImage.Size()
sw := float64(wd) / float64(ws)
sh := float64(hd) / float64(hs)
op := &DrawImageOptions{}
op.GeoM.Scale(sw, sh)
op.ColorM.Scale(0, 0, 0, 0)
if a > 0 {
rf := float64(r) / float64(a)
gf := float64(g) / float64(a)
@ -97,7 +100,7 @@ func (i *Image) fill(r, g, b, a uint8) {
}
op.CompositeMode = CompositeModeCopy
op.Filter = FilterNearest
_ = i.DrawImage(dummyImage, op)
_ = i.DrawImage(emptyImage, op)
}
func (i *Image) isDisposed() bool {
@ -351,23 +354,10 @@ func NewImage(width, height int, filter Filter) (*Image, error) {
filter: filter,
}
i.addr = i
i.fill(0, 0, 0, 0)
runtime.SetFinalizer(i, (*Image).Dispose)
return i, nil
}
// newImageWithoutInit creates an empty image without initialization.
func newImageWithoutInit(width, height int) *Image {
s := shareable.NewImage(width, height)
i := &Image{
shareableImage: s,
filter: FilterDefault,
}
i.addr = i
runtime.SetFinalizer(i, (*Image).Dispose)
return i
}
// newVolatileImage returns an empty 'volatile' image.
// A volatile image is always cleared at the start of a frame.
//
@ -389,7 +379,6 @@ func newVolatileImage(width, height int, filter Filter) *Image {
filter: filter,
}
i.addr = i
i.fill(0, 0, 0, 0)
runtime.SetFinalizer(i, (*Image).Dispose)
return i
}

View File

@ -72,43 +72,43 @@ type Image struct {
screen bool
}
type rand struct {
x, y, z, w uint32
var emptyImage = newImageWithoutInit(16, 16, false)
func init() {
w, h := emptyImage.Size()
pix := make([]byte, 4*w*h)
emptyImage.ReplacePixels(pix, 0, 0, w, h)
}
func (r *rand) next() uint32 {
// Xorshift: http://en.wikipedia.org/wiki/Xorshift
t := r.x ^ (r.x << 11)
r.x, r.y, r.z = r.y, r.z, r.w
r.w = (r.w ^ (r.w >> 19)) ^ (t ^ (t >> 8))
return r.w
}
var theRand = &rand{12345678, 4185243, 776511, 45411}
// NewImage creates an empty image with the given size.
func NewImage(width, height int, volatile bool) *Image {
func newImageWithoutInit(width, height int, volatile bool) *Image {
i := &Image{
image: graphics.NewImage(width, height),
volatile: volatile,
}
theImages.add(i)
runtime.SetFinalizer(i, (*Image).Dispose)
// Put a random color pixel on newImg to make tests reliable
// (e.g. shareable_test.TestEnsureNotShared. This test might pass
// without this ReplacePixels even when the next DrawImage has a bug).
// This avoids to use remaining GPU memory state unexpectedly.
//
// TODO: Is it better clearing the image? How about the cost?
v := theRand.next()
r, g, b := uint8(v>>24), uint8(v>>16), uint8(v>>8)
i.ReplacePixels([]byte{r, g, b, 0xff}, 0, 0, 1, 1)
return i
}
// NewImage creates an empty image with the given size.
//
// The returned image is cleared.
func NewImage(width, height int, volatile bool) *Image {
i := newImageWithoutInit(width, height, volatile)
i.Clear(0, 0, width, height)
return i
}
func (i *Image) Clear(x, y, width, height int) {
w, h := emptyImage.Size()
geom := (*affine.GeoM)(nil).Scale(float64(width)/float64(w), float64(height)/float64(h))
geom = geom.Translate(float64(x), float64(y))
i.DrawImage(emptyImage, 0, 0, w, h, geom, nil, opengl.CompositeModeCopy, graphics.FilterNearest)
}
// NewScreenFramebufferImage creates a special image that framebuffer is one for the screen.
//
// The returned image is cleared.
func NewScreenFramebufferImage(width, height int) *Image {
i := &Image{
image: graphics.NewScreenFramebufferImage(width, height),
@ -117,6 +117,7 @@ func NewScreenFramebufferImage(width, height int) *Image {
}
theImages.add(i)
runtime.SetFinalizer(i, (*Image).Dispose)
i.Clear(0, 0, width, height)
return i
}

View File

@ -188,6 +188,8 @@ func (s *Image) dispose() {
s.backend.page.Free(s.node)
if !s.backend.page.IsEmpty() {
// As this part can be reused, this should be cleared explicitly.
s.backend.restorable.Clear(s.region())
return
}