mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2025-02-04 06:54:28 +01:00
graphics: Remove imageImpl
This commit is contained in:
parent
e97a0b3e37
commit
6be25d3297
@ -107,7 +107,7 @@ func (c *graphicsContext) needsRestoring(context *opengl.Context) (bool, error)
|
|||||||
if err := graphics.FlushCommands(context); err != nil {
|
if err := graphics.FlushCommands(context); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
return c.offscreen.impl.isInvalidated(context), nil
|
return c.offscreen.restorable.IsInvalidated(context), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *graphicsContext) initializeIfNeeded(context *opengl.Context) error {
|
func (c *graphicsContext) initializeIfNeeded(context *opengl.Context) error {
|
||||||
|
112
image.go
112
image.go
@ -41,17 +41,17 @@ func glContext() *opengl.Context {
|
|||||||
type images struct {
|
type images struct {
|
||||||
images map[*restorable.Image]struct{}
|
images map[*restorable.Image]struct{}
|
||||||
m sync.Mutex
|
m sync.Mutex
|
||||||
lastChecked *imageImpl
|
lastChecked *restorable.Image
|
||||||
}
|
}
|
||||||
|
|
||||||
var theImagesForRestoring = images{
|
var theImagesForRestoring = images{
|
||||||
images: map[*restorable.Image]struct{}{},
|
images: map[*restorable.Image]struct{}{},
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *images) add(img *imageImpl) *Image {
|
func (i *images) add(img *restorable.Image) *Image {
|
||||||
i.m.Lock()
|
i.m.Lock()
|
||||||
defer i.m.Unlock()
|
defer i.m.Unlock()
|
||||||
i.images[img.restorable] = struct{}{}
|
i.images[img] = struct{}{}
|
||||||
eimg := &Image{img}
|
eimg := &Image{img}
|
||||||
runtime.SetFinalizer(eimg, theImagesForRestoring.remove)
|
runtime.SetFinalizer(eimg, theImagesForRestoring.remove)
|
||||||
return eimg
|
return eimg
|
||||||
@ -63,7 +63,7 @@ func (i *images) remove(img *Image) {
|
|||||||
}
|
}
|
||||||
i.m.Lock()
|
i.m.Lock()
|
||||||
defer i.m.Unlock()
|
defer i.m.Unlock()
|
||||||
delete(i.images, img.impl.restorable)
|
delete(i.images, img.restorable)
|
||||||
runtime.SetFinalizer(img, nil)
|
runtime.SetFinalizer(img, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,15 +82,16 @@ func (i *images) resolveStalePixels(context *opengl.Context) error {
|
|||||||
func (i *images) resetPixelsIfDependingOn(target *Image) {
|
func (i *images) resetPixelsIfDependingOn(target *Image) {
|
||||||
i.m.Lock()
|
i.m.Lock()
|
||||||
defer i.m.Unlock()
|
defer i.m.Unlock()
|
||||||
if i.lastChecked == target.impl {
|
if i.lastChecked == target.restorable {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
i.lastChecked = target.impl
|
i.lastChecked = target.restorable
|
||||||
if target.impl.isDisposed() {
|
if target.restorable == nil {
|
||||||
|
// disposed
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for img := range i.images {
|
for img := range i.images {
|
||||||
img.MakeStaleIfDependingOn(target.impl.restorable)
|
img.MakeStaleIfDependingOn(target.restorable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,12 +137,12 @@ func (i *images) clearVolatileImages() {
|
|||||||
//
|
//
|
||||||
// Functions of Image never returns error as of 1.5.0-alpha, and error values are always nil.
|
// Functions of Image never returns error as of 1.5.0-alpha, and error values are always nil.
|
||||||
type Image struct {
|
type Image struct {
|
||||||
impl *imageImpl
|
restorable *restorable.Image
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size returns the size of the image.
|
// Size returns the size of the image.
|
||||||
func (i *Image) Size() (width, height int) {
|
func (i *Image) Size() (width, height int) {
|
||||||
return i.impl.restorable.Size()
|
return i.restorable.Size()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear resets the pixels of the image into 0.
|
// Clear resets the pixels of the image into 0.
|
||||||
@ -151,7 +152,7 @@ func (i *Image) Size() (width, height int) {
|
|||||||
// Clear always returns nil as of 1.5.0-alpha.
|
// Clear always returns nil as of 1.5.0-alpha.
|
||||||
func (i *Image) Clear() error {
|
func (i *Image) Clear() error {
|
||||||
theImagesForRestoring.resetPixelsIfDependingOn(i)
|
theImagesForRestoring.resetPixelsIfDependingOn(i)
|
||||||
i.impl.Fill(color.Transparent)
|
i.restorable.Fill(color.RGBA{})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,7 +163,8 @@ func (i *Image) Clear() error {
|
|||||||
// Fill always returns nil as of 1.5.0-alpha.
|
// Fill always returns nil as of 1.5.0-alpha.
|
||||||
func (i *Image) Fill(clr color.Color) error {
|
func (i *Image) Fill(clr color.Color) error {
|
||||||
theImagesForRestoring.resetPixelsIfDependingOn(i)
|
theImagesForRestoring.resetPixelsIfDependingOn(i)
|
||||||
i.impl.Fill(clr)
|
rgba := color.RGBAModel.Convert(clr).(color.RGBA)
|
||||||
|
i.restorable.Fill(rgba)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,13 +192,41 @@ func (i *Image) Fill(clr color.Color) error {
|
|||||||
// DrawImage always returns nil as of 1.5.0-alpha.
|
// DrawImage always returns nil as of 1.5.0-alpha.
|
||||||
func (i *Image) DrawImage(image *Image, options *DrawImageOptions) error {
|
func (i *Image) DrawImage(image *Image, options *DrawImageOptions) error {
|
||||||
theImagesForRestoring.resetPixelsIfDependingOn(i)
|
theImagesForRestoring.resetPixelsIfDependingOn(i)
|
||||||
i.impl.DrawImage(image, options)
|
// Calculate vertices before locking because the user can do anything in
|
||||||
|
// options.ImageParts interface without deadlock (e.g. Call Image functions).
|
||||||
|
if options == nil {
|
||||||
|
options = &DrawImageOptions{}
|
||||||
|
}
|
||||||
|
parts := options.ImageParts
|
||||||
|
if parts == nil {
|
||||||
|
// Check options.Parts for backward-compatibility.
|
||||||
|
dparts := options.Parts
|
||||||
|
if dparts != nil {
|
||||||
|
parts = imageParts(dparts)
|
||||||
|
} else {
|
||||||
|
w, h := image.restorable.Size()
|
||||||
|
parts = &wholeImage{w, h}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w, h := image.restorable.Size()
|
||||||
|
vs := vertices(parts, w, h, &options.GeoM.impl)
|
||||||
|
if len(vs) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if i == image {
|
||||||
|
panic("ebiten: Image.DrawImage: image must be different from the receiver")
|
||||||
|
}
|
||||||
|
if i.restorable == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
mode := opengl.CompositeMode(options.CompositeMode)
|
||||||
|
i.restorable.DrawImage(image.restorable, vs, options.ColorM.impl, mode)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bounds returns the bounds of the image.
|
// Bounds returns the bounds of the image.
|
||||||
func (i *Image) Bounds() image.Rectangle {
|
func (i *Image) Bounds() image.Rectangle {
|
||||||
w, h := i.impl.restorable.Size()
|
w, h := i.restorable.Size()
|
||||||
return image.Rect(0, 0, w, h)
|
return image.Rect(0, 0, w, h)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,7 +241,15 @@ func (i *Image) ColorModel() color.Model {
|
|||||||
//
|
//
|
||||||
// This method can't be called before the main loop (ebiten.Run) starts (as of version 1.4.0-alpha).
|
// This method can't be called before the main loop (ebiten.Run) starts (as of version 1.4.0-alpha).
|
||||||
func (i *Image) At(x, y int) color.Color {
|
func (i *Image) At(x, y int) color.Color {
|
||||||
return i.impl.At(x, y, glContext())
|
if i.restorable == nil {
|
||||||
|
return color.Transparent
|
||||||
|
}
|
||||||
|
// TODO: Error should be delayed until flushing. Do not panic here.
|
||||||
|
clr, err := i.restorable.At(x, y, glContext())
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return clr
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dispose disposes the image data. After disposing, the image becomes invalid.
|
// Dispose disposes the image data. After disposing, the image becomes invalid.
|
||||||
@ -223,11 +261,13 @@ func (i *Image) At(x, y int) color.Color {
|
|||||||
//
|
//
|
||||||
// Dipose always return nil as of 1.5.0-alpha.
|
// Dipose always return nil as of 1.5.0-alpha.
|
||||||
func (i *Image) Dispose() error {
|
func (i *Image) Dispose() error {
|
||||||
if i.impl.isDisposed() {
|
if i.restorable == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
theImagesForRestoring.resetPixelsIfDependingOn(i)
|
theImagesForRestoring.resetPixelsIfDependingOn(i)
|
||||||
i.impl.Dispose()
|
i.restorable.Dispose()
|
||||||
|
i.restorable = nil
|
||||||
|
runtime.SetFinalizer(i, nil)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,7 +284,19 @@ func (i *Image) Dispose() error {
|
|||||||
// ReplacePixels always returns nil as of 1.5.0-alpha.
|
// ReplacePixels always returns nil as of 1.5.0-alpha.
|
||||||
func (i *Image) ReplacePixels(p []uint8) error {
|
func (i *Image) ReplacePixels(p []uint8) error {
|
||||||
theImagesForRestoring.resetPixelsIfDependingOn(i)
|
theImagesForRestoring.resetPixelsIfDependingOn(i)
|
||||||
i.impl.ReplacePixels(p)
|
w, h := i.restorable.Size()
|
||||||
|
if l := 4 * w * h; len(p) != l {
|
||||||
|
panic(fmt.Sprintf("ebiten: len(p) was %d but must be %d", len(p), l))
|
||||||
|
}
|
||||||
|
if i.restorable == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
w2, h2 := graphics.NextPowerOf2Int(w), graphics.NextPowerOf2Int(h)
|
||||||
|
pix := make([]uint8, 4*w2*h2)
|
||||||
|
for j := 0; j < h; j++ {
|
||||||
|
copy(pix[j*w2*4:], p[j*w*4:(j+1)*w*4])
|
||||||
|
}
|
||||||
|
i.restorable.ReplacePixels(pix)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,9 +318,9 @@ type DrawImageOptions struct {
|
|||||||
// Error returned by NewImage is always nil as of 1.5.0-alpha.
|
// Error returned by NewImage is always nil as of 1.5.0-alpha.
|
||||||
func NewImage(width, height int, filter Filter) (*Image, error) {
|
func NewImage(width, height int, filter Filter) (*Image, error) {
|
||||||
checkSize(width, height)
|
checkSize(width, height)
|
||||||
img := newImageImpl(width, height, filter, false)
|
r := restorable.NewImage(width, height, glFilter(filter), false)
|
||||||
img.Fill(color.Transparent)
|
r.Fill(color.RGBA{})
|
||||||
return theImagesForRestoring.add(img), nil
|
return theImagesForRestoring.add(r), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// newVolatileImage returns an empty 'volatile' image.
|
// newVolatileImage returns an empty 'volatile' image.
|
||||||
@ -286,9 +338,9 @@ func NewImage(width, height int, filter Filter) (*Image, error) {
|
|||||||
// Error returned by newVolatileImage is always nil as of 1.5.0-alpha.
|
// Error returned by newVolatileImage is always nil as of 1.5.0-alpha.
|
||||||
func newVolatileImage(width, height int, filter Filter) (*Image, error) {
|
func newVolatileImage(width, height int, filter Filter) (*Image, error) {
|
||||||
checkSize(width, height)
|
checkSize(width, height)
|
||||||
img := newImageImpl(width, height, filter, true)
|
r := restorable.NewImage(width, height, glFilter(filter), true)
|
||||||
img.Fill(color.Transparent)
|
r.Fill(color.RGBA{})
|
||||||
return theImagesForRestoring.add(img), nil
|
return theImagesForRestoring.add(r), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewImageFromImage creates a new image with the given image (source).
|
// NewImageFromImage creates a new image with the given image (source).
|
||||||
@ -298,15 +350,17 @@ func newVolatileImage(width, height int, filter Filter) (*Image, error) {
|
|||||||
// Error returned by NewImageFromImage is always nil as of 1.5.0-alpha.
|
// Error returned by NewImageFromImage is always nil as of 1.5.0-alpha.
|
||||||
func NewImageFromImage(source image.Image, filter Filter) (*Image, error) {
|
func NewImageFromImage(source image.Image, filter Filter) (*Image, error) {
|
||||||
size := source.Bounds().Size()
|
size := source.Bounds().Size()
|
||||||
checkSize(size.X, size.Y)
|
w, h := size.X, size.Y
|
||||||
img := newImageImplFromImage(source, filter)
|
checkSize(w, h)
|
||||||
return theImagesForRestoring.add(img), nil
|
rgbaImg := graphics.CopyImage(source)
|
||||||
|
r := restorable.NewImageFromImage(rgbaImg, w, h, glFilter(filter))
|
||||||
|
return theImagesForRestoring.add(r), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newImageWithScreenFramebuffer(width, height int) (*Image, error) {
|
func newImageWithScreenFramebuffer(width, height int) (*Image, error) {
|
||||||
checkSize(width, height)
|
checkSize(width, height)
|
||||||
img := newScreenImageImpl(width, height)
|
r := restorable.NewScreenFramebufferImage(width, height)
|
||||||
return theImagesForRestoring.add(img), nil
|
return theImagesForRestoring.add(r), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const MaxImageSize = graphics.MaxImageSize
|
const MaxImageSize = graphics.MaxImageSize
|
||||||
|
152
imageimpl.go
152
imageimpl.go
@ -13,155 +13,3 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package ebiten
|
package ebiten
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"image"
|
|
||||||
"image/color"
|
|
||||||
"runtime"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/internal/graphics"
|
|
||||||
"github.com/hajimehoshi/ebiten/internal/opengl"
|
|
||||||
"github.com/hajimehoshi/ebiten/internal/restorable"
|
|
||||||
)
|
|
||||||
|
|
||||||
type imageImpl struct {
|
|
||||||
restorable *restorable.Image
|
|
||||||
m sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func newImageImpl(width, height int, filter Filter, volatile bool) *imageImpl {
|
|
||||||
i := &imageImpl{
|
|
||||||
restorable: restorable.NewImage(width, height, glFilter(filter), volatile),
|
|
||||||
}
|
|
||||||
runtime.SetFinalizer(i, (*imageImpl).Dispose)
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
func newImageImplFromImage(source image.Image, filter Filter) *imageImpl {
|
|
||||||
size := source.Bounds().Size()
|
|
||||||
w, h := size.X, size.Y
|
|
||||||
|
|
||||||
// Don't lock while manipulating an image.Image interface.
|
|
||||||
|
|
||||||
// It is necessary to copy the source image since the actual construction of
|
|
||||||
// an image is delayed and we can't expect the source image is not modified
|
|
||||||
// until the construction.
|
|
||||||
rgbaImg := graphics.CopyImage(source)
|
|
||||||
i := &imageImpl{
|
|
||||||
restorable: restorable.NewImageFromImage(rgbaImg, w, h, glFilter(filter)),
|
|
||||||
}
|
|
||||||
runtime.SetFinalizer(i, (*imageImpl).Dispose)
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
func newScreenImageImpl(width, height int) *imageImpl {
|
|
||||||
i := &imageImpl{
|
|
||||||
restorable: restorable.NewScreenFramebufferImage(width, height),
|
|
||||||
}
|
|
||||||
runtime.SetFinalizer(i, (*imageImpl).Dispose)
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *imageImpl) Fill(clr color.Color) {
|
|
||||||
i.m.Lock()
|
|
||||||
defer i.m.Unlock()
|
|
||||||
if i.restorable == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
rgba := color.RGBAModel.Convert(clr).(color.RGBA)
|
|
||||||
i.restorable.Fill(rgba)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *imageImpl) DrawImage(image *Image, options *DrawImageOptions) {
|
|
||||||
// Calculate vertices before locking because the user can do anything in
|
|
||||||
// options.ImageParts interface without deadlock (e.g. Call Image functions).
|
|
||||||
if options == nil {
|
|
||||||
options = &DrawImageOptions{}
|
|
||||||
}
|
|
||||||
parts := options.ImageParts
|
|
||||||
if parts == nil {
|
|
||||||
// Check options.Parts for backward-compatibility.
|
|
||||||
dparts := options.Parts
|
|
||||||
if dparts != nil {
|
|
||||||
parts = imageParts(dparts)
|
|
||||||
} else {
|
|
||||||
w, h := image.impl.restorable.Size()
|
|
||||||
parts = &wholeImage{w, h}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w, h := image.impl.restorable.Size()
|
|
||||||
vs := vertices(parts, w, h, &options.GeoM.impl)
|
|
||||||
if len(vs) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if i == image.impl {
|
|
||||||
panic("ebiten: Image.DrawImage: image must be different from the receiver")
|
|
||||||
}
|
|
||||||
i.m.Lock()
|
|
||||||
defer i.m.Unlock()
|
|
||||||
if i.restorable == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
mode := opengl.CompositeMode(options.CompositeMode)
|
|
||||||
i.restorable.DrawImage(image.impl.restorable, vs, options.ColorM.impl, mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *imageImpl) At(x, y int, context *opengl.Context) color.Color {
|
|
||||||
if context == nil {
|
|
||||||
panic("ebiten: At can't be called when the GL context is not initialized (this panic happens as of version 1.4.0-alpha)")
|
|
||||||
}
|
|
||||||
i.m.Lock()
|
|
||||||
defer i.m.Unlock()
|
|
||||||
if i.restorable == nil {
|
|
||||||
return color.Transparent
|
|
||||||
}
|
|
||||||
// TODO: Error should be delayed until flushing. Do not panic here.
|
|
||||||
clr, err := i.restorable.At(x, y, context)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return clr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *imageImpl) Dispose() {
|
|
||||||
i.m.Lock()
|
|
||||||
defer i.m.Unlock()
|
|
||||||
if i.restorable == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
i.restorable.Dispose()
|
|
||||||
i.restorable = nil
|
|
||||||
runtime.SetFinalizer(i, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *imageImpl) ReplacePixels(p []uint8) {
|
|
||||||
w, h := i.restorable.Size()
|
|
||||||
if l := 4 * w * h; len(p) != l {
|
|
||||||
panic(fmt.Sprintf("ebiten: len(p) was %d but must be %d", len(p), l))
|
|
||||||
}
|
|
||||||
i.m.Lock()
|
|
||||||
defer i.m.Unlock()
|
|
||||||
if i.restorable == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w2, h2 := graphics.NextPowerOf2Int(w), graphics.NextPowerOf2Int(h)
|
|
||||||
pix := make([]uint8, 4*w2*h2)
|
|
||||||
for j := 0; j < h; j++ {
|
|
||||||
copy(pix[j*w2*4:], p[j*w*4:(j+1)*w*4])
|
|
||||||
}
|
|
||||||
i.restorable.ReplacePixels(pix)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *imageImpl) isDisposed() bool {
|
|
||||||
i.m.Lock()
|
|
||||||
defer i.m.Unlock()
|
|
||||||
return i.restorable == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *imageImpl) isInvalidated(context *opengl.Context) bool {
|
|
||||||
i.m.Lock()
|
|
||||||
defer i.m.Unlock()
|
|
||||||
return i.restorable.IsInvalidated(context)
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user