mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-26 11:48:55 +01:00
graphics: Add Image.evacuatePixels and restorePixels and introduce images
This commit is contained in:
parent
90aeee5144
commit
23863d5a2f
176
image.go
176
image.go
@ -63,16 +63,86 @@ func (t *delayedImageTasks) exec() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type images struct {
|
||||||
|
// TODO: This must be weak refs. Create a wrapper for *Image.
|
||||||
|
images map[*Image]struct{}
|
||||||
|
evacuated bool
|
||||||
|
m sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
var theImages = images{
|
||||||
|
images: map[*Image]struct{}{},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *images) add(img *Image) error {
|
||||||
|
i.m.Lock()
|
||||||
|
defer i.m.Unlock()
|
||||||
|
if i.evacuated {
|
||||||
|
return errors.New("ebiten: images must not be evacuated")
|
||||||
|
}
|
||||||
|
i.images[img] = struct{}{}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *images) remove(img *Image) error {
|
||||||
|
i.m.Lock()
|
||||||
|
defer i.m.Unlock()
|
||||||
|
if i.evacuated {
|
||||||
|
return errors.New("ebiten: images must not be evacuated")
|
||||||
|
}
|
||||||
|
delete(i.images, img)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *images) isEvacuated() bool {
|
||||||
|
i.m.Lock()
|
||||||
|
defer i.m.Unlock()
|
||||||
|
return i.evacuated
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *images) evacuatePixels() error {
|
||||||
|
i.m.Lock()
|
||||||
|
defer i.m.Unlock()
|
||||||
|
if i.evacuated {
|
||||||
|
return errors.New("ebiten: images must not be evacuated")
|
||||||
|
}
|
||||||
|
i.evacuated = true
|
||||||
|
for img := range i.images {
|
||||||
|
if err := img.evacuatePixels(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *images) restorePixels() error {
|
||||||
|
i.m.Lock()
|
||||||
|
defer i.m.Unlock()
|
||||||
|
if !i.evacuated {
|
||||||
|
return errors.New("ebiten: images must be evacuated")
|
||||||
|
}
|
||||||
|
for img := range i.images {
|
||||||
|
if err := img.restorePixels(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i.evacuated = false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Image represents an image.
|
// Image represents an image.
|
||||||
// The pixel format is alpha-premultiplied.
|
// The pixel format is alpha-premultiplied.
|
||||||
// Image implements image.Image.
|
// Image implements image.Image.
|
||||||
type Image struct {
|
type Image struct {
|
||||||
framebuffer *graphics.Framebuffer
|
framebuffer *graphics.Framebuffer
|
||||||
texture *graphics.Texture
|
texture *graphics.Texture
|
||||||
disposed bool
|
defaultFramebuffer bool
|
||||||
pixels []uint8
|
disposed bool
|
||||||
width int
|
evacuated bool
|
||||||
height int
|
pixels []uint8
|
||||||
|
width int
|
||||||
|
height int
|
||||||
|
filter Filter
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size returns the size of the image.
|
// Size returns the size of the image.
|
||||||
@ -219,6 +289,77 @@ func (i *Image) At(x, y int) color.Color {
|
|||||||
return color.RGBA{r, g, b, a}
|
return color.RGBA{r, g, b, a}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Image) evacuatePixels() error {
|
||||||
|
imageM.Lock()
|
||||||
|
defer imageM.Unlock()
|
||||||
|
defer func() {
|
||||||
|
i.evacuated = true
|
||||||
|
}()
|
||||||
|
if i.defaultFramebuffer {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if i.evacuated {
|
||||||
|
return errors.New("ebiten: image must not be evacuated")
|
||||||
|
}
|
||||||
|
if i.pixels == nil {
|
||||||
|
var err error
|
||||||
|
i.pixels, err = i.framebuffer.Pixels(glContext)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if i.framebuffer != nil {
|
||||||
|
if err := i.framebuffer.Dispose(glContext); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.framebuffer = nil
|
||||||
|
}
|
||||||
|
if i.texture != nil {
|
||||||
|
if err := i.texture.Dispose(glContext); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.texture = nil
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Image) restorePixels() error {
|
||||||
|
imageM.Lock()
|
||||||
|
defer imageM.Unlock()
|
||||||
|
defer func() {
|
||||||
|
i.evacuated = false
|
||||||
|
}()
|
||||||
|
if i.defaultFramebuffer {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if !i.evacuated {
|
||||||
|
return errors.New("ebiten: image must be evacuated")
|
||||||
|
}
|
||||||
|
if i.pixels == nil {
|
||||||
|
return errors.New("ebiten: pixels must not be nil")
|
||||||
|
}
|
||||||
|
if i.texture != nil {
|
||||||
|
return errors.New("ebiten: texture must be nil")
|
||||||
|
}
|
||||||
|
if i.framebuffer != nil {
|
||||||
|
return errors.New("ebiten: framebuffer must be nil")
|
||||||
|
}
|
||||||
|
img := image.NewRGBA(image.Rect(0, 0, i.width, i.height))
|
||||||
|
for j := 0; j < i.height; j++ {
|
||||||
|
copy(img.Pix[j*img.Stride:], i.pixels[j*i.width*4:(j+1)*i.width*4])
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
i.texture, err = graphics.NewTextureFromImage(glContext, img, glFilter(glContext, i.filter))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i.framebuffer, err = graphics.NewFramebufferFromTexture(glContext, i.texture)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Dispose disposes the image data. After disposing, the image becomes invalid.
|
// Dispose disposes the image data. After disposing, the image becomes invalid.
|
||||||
// This is useful to save memory.
|
// This is useful to save memory.
|
||||||
//
|
//
|
||||||
@ -246,6 +387,9 @@ func (i *Image) Dispose() error {
|
|||||||
}
|
}
|
||||||
i.disposed = true
|
i.disposed = true
|
||||||
i.pixels = nil
|
i.pixels = nil
|
||||||
|
if err := theImages.remove(i); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
runtime.SetFinalizer(i, nil)
|
runtime.SetFinalizer(i, nil)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -307,6 +451,10 @@ func NewImage(width, height int, filter Filter) (*Image, error) {
|
|||||||
image := &Image{
|
image := &Image{
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
|
filter: filter,
|
||||||
|
}
|
||||||
|
if err := theImages.add(image); err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
f := func() error {
|
f := func() error {
|
||||||
imageM.Lock()
|
imageM.Lock()
|
||||||
@ -349,6 +497,10 @@ func NewImageFromImage(img image.Image, filter Filter) (*Image, error) {
|
|||||||
eimg := &Image{
|
eimg := &Image{
|
||||||
width: w,
|
width: w,
|
||||||
height: h,
|
height: h,
|
||||||
|
filter: filter,
|
||||||
|
}
|
||||||
|
if err := theImages.add(eimg); err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
f := func() error {
|
f := func() error {
|
||||||
// Don't lock while manipulating an image.Image interface.
|
// Don't lock while manipulating an image.Image interface.
|
||||||
@ -392,10 +544,14 @@ func newImageWithZeroFramebuffer(width, height int) (*Image, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
img := &Image{
|
img := &Image{
|
||||||
framebuffer: f,
|
framebuffer: f,
|
||||||
texture: nil,
|
texture: nil,
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
|
defaultFramebuffer: true,
|
||||||
|
}
|
||||||
|
if err := theImages.add(img); err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
runtime.SetFinalizer(img, (*Image).Dispose)
|
runtime.SetFinalizer(img, (*Image).Dispose)
|
||||||
return img, nil
|
return img, nil
|
||||||
|
Loading…
Reference in New Issue
Block a user