graphics: Remove image commands and use functions instead

This commit is contained in:
Hajime Hoshi 2016-05-08 04:39:37 +09:00
parent b8a062e813
commit bbc2ad915a
2 changed files with 113 additions and 212 deletions

161
image.go
View File

@ -19,13 +19,31 @@ import (
"fmt" "fmt"
"image" "image"
"image/color" "image/color"
"runtime"
"sync" "sync"
"github.com/hajimehoshi/ebiten/internal/graphics" "github.com/hajimehoshi/ebiten/internal/graphics"
"github.com/hajimehoshi/ebiten/internal/graphics/opengl"
) )
var imageM sync.Mutex var imageM sync.Mutex
var (
imageCommandQueue = []func() error{}
)
func execBufferedImageCommands() error {
imageM.Lock()
defer imageM.Unlock()
for _, f := range imageCommandQueue {
if err := f(); err != nil {
return err
}
}
imageCommandQueue = nil
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.
@ -68,15 +86,18 @@ func (i *Image) Fill(clr color.Color) (err error) {
} }
func (i *Image) fill(clr color.Color) (err error) { func (i *Image) fill(clr color.Color) (err error) {
c := &fillCommand{ f := func() error {
dst: i, if i.isDisposed() {
color: clr, return errors.New("ebiten: image is already disposed")
}
i.pixels = nil
return i.framebuffer.Fill(glContext, clr)
} }
if imageCommandQueue != nil { if imageCommandQueue != nil {
imageCommandQueue = append(imageCommandQueue, c) imageCommandQueue = append(imageCommandQueue, f)
return return
} }
return c.Exec() return f()
} }
@ -126,19 +147,19 @@ func (i *Image) DrawImage(image *Image, options *DrawImageOptions) (err error) {
if i == image { if i == image {
return errors.New("ebiten: Image.DrawImage: image should be different from the receiver") return errors.New("ebiten: Image.DrawImage: image should be different from the receiver")
} }
c := &drawImageCommand{ f := func() error {
dst: i, if i.isDisposed() {
src: image, return errors.New("ebiten: image is already disposed")
vertices: vertices[:16*n], }
geoM: options.GeoM, i.pixels = nil
colorM: options.ColorM, m := opengl.CompositeMode(options.CompositeMode)
compositeMode: options.CompositeMode, return i.framebuffer.DrawTexture(glContext, image.texture, vertices[:16*n], &options.GeoM, &options.ColorM, m)
} }
if imageCommandQueue != nil { if imageCommandQueue != nil {
imageCommandQueue = append(imageCommandQueue, c) imageCommandQueue = append(imageCommandQueue, f)
return nil return nil
} }
return c.Exec() return f()
} }
// Bounds returns the bounds of the image. // Bounds returns the bounds of the image.
@ -194,14 +215,32 @@ func (i *Image) At(x, y int) color.Color {
func (i *Image) Dispose() error { func (i *Image) Dispose() error {
imageM.Lock() imageM.Lock()
defer imageM.Unlock() defer imageM.Unlock()
c := &disposeCommand{ f := func() error {
image: i, if i.isDisposed() {
} return errors.New("ebiten: image is already disposed")
if imageCommandQueue != nil { }
imageCommandQueue = append(imageCommandQueue, c) 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
}
i.disposed = true
i.pixels = nil
runtime.SetFinalizer(i, nil)
return nil return nil
} }
return c.Exec() if imageCommandQueue != nil {
imageCommandQueue = append(imageCommandQueue, f)
return nil
}
return f()
} }
func (i *Image) isDisposed() bool { func (i *Image) isDisposed() bool {
@ -218,20 +257,22 @@ func (i *Image) isDisposed() bool {
func (i *Image) ReplacePixels(p []uint8) error { func (i *Image) ReplacePixels(p []uint8) error {
imageM.Lock() imageM.Lock()
defer imageM.Unlock() defer imageM.Unlock()
// Don't set i.pixels here because i.pixels is used not every time.
i.pixels = nil
if l := 4 * i.width * i.height; len(p) != l { if l := 4 * i.width * i.height; len(p) != l {
return fmt.Errorf("ebiten: p's length must be %d", l) return fmt.Errorf("ebiten: p's length must be %d", l)
} }
c := &replacePixelsCommand{ f := func() error {
dst: i, // Don't set i.pixels here because i.pixels is used not every time.
pixels: p, i.pixels = nil
if i.isDisposed() {
return errors.New("ebiten: image is already disposed")
}
return i.framebuffer.ReplacePixels(glContext, i.texture, p)
} }
if imageCommandQueue != nil { if imageCommandQueue != nil {
imageCommandQueue = append(imageCommandQueue, c) imageCommandQueue = append(imageCommandQueue, f)
return nil return nil
} }
return c.Exec() return f()
} }
// A DrawImageOptions represents options to render an image on an image. // A DrawImageOptions represents options to render an image on an image.
@ -256,23 +297,36 @@ type DrawImageOptions struct {
func NewImage(width, height int, filter Filter) (*Image, error) { func NewImage(width, height int, filter Filter) (*Image, error) {
imageM.Lock() imageM.Lock()
defer imageM.Unlock() defer imageM.Unlock()
c := &newImageCommand{ image := &Image{
width: width, width: width,
height: height, height: height,
filter: filter, }
result: &Image{ f := func() error {
width: width, texture, err := graphics.NewTexture(glContext, width, height, glFilter(glContext, filter))
height: height, if err != nil {
}, return err
}
framebuffer, err := graphics.NewFramebufferFromTexture(glContext, texture)
if err != nil {
// TODO: texture should be removed here?
return err
}
image.framebuffer = framebuffer
image.texture = texture
runtime.SetFinalizer(image, (*Image).Dispose)
if err := image.framebuffer.Fill(glContext, color.Transparent); err != nil {
return err
}
return nil
} }
if imageCommandQueue != nil { if imageCommandQueue != nil {
imageCommandQueue = append(imageCommandQueue, c) imageCommandQueue = append(imageCommandQueue, f)
return c.result, nil return image, nil
} }
if err := c.Exec(); err != nil { if err := f(); err != nil {
return nil, err return nil, err
} }
return c.result, nil return image, nil
} }
// NewImageFromImage creates a new image with the given image (img). // NewImageFromImage creates a new image with the given image (img).
@ -288,20 +342,31 @@ func NewImageFromImage(img image.Image, filter Filter) (*Image, error) {
defer imageM.Unlock() defer imageM.Unlock()
size := img.Bounds().Size() size := img.Bounds().Size()
w, h := size.X, size.Y w, h := size.X, size.Y
c := &newImageFromImageCommand{ image := &Image{
image: img, width: w,
filter: filter, height: h,
result: &Image{ }
width: w, f := func() error {
height: h, texture, err := graphics.NewTextureFromImage(glContext, img, glFilter(glContext, filter))
}, if err != nil {
return err
}
framebuffer, err := graphics.NewFramebufferFromTexture(glContext, texture)
if err != nil {
// TODO: texture should be removed here?
return err
}
image.framebuffer = framebuffer
image.texture = texture
runtime.SetFinalizer(image, (*Image).Dispose)
return nil
} }
if imageCommandQueue != nil { if imageCommandQueue != nil {
imageCommandQueue = append(imageCommandQueue, c) imageCommandQueue = append(imageCommandQueue, f)
return c.result, nil return image, nil
} }
if err := c.Exec(); err != nil { if err := f(); err != nil {
return nil, err return nil, err
} }
return c.result, nil return image, nil
} }

View File

@ -1,164 +0,0 @@
// Copyright 2016 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ebiten
import (
"errors"
"image"
"image/color"
"runtime"
"github.com/hajimehoshi/ebiten/internal/graphics"
"github.com/hajimehoshi/ebiten/internal/graphics/opengl"
)
// Commands are used only before the GL Context is created.
type imageCommand interface {
Exec() error
}
var (
imageCommandQueue = []imageCommand{}
)
func execBufferedImageCommands() error {
imageM.Lock()
defer imageM.Unlock()
for _, c := range imageCommandQueue {
if err := c.Exec(); err != nil {
return err
}
}
imageCommandQueue = nil
return nil
}
type fillCommand struct {
dst *Image
color color.Color
}
func (c *fillCommand) Exec() error {
if c.dst.isDisposed() {
return errors.New("ebiten: image is already disposed")
}
c.dst.pixels = nil
return c.dst.framebuffer.Fill(glContext, c.color)
}
type drawImageCommand struct {
dst *Image
src *Image
vertices []int16
geoM GeoM
colorM ColorM
compositeMode CompositeMode
}
func (c *drawImageCommand) Exec() error {
if c.dst.isDisposed() {
return errors.New("ebiten: image is already disposed")
}
c.dst.pixels = nil
m := opengl.CompositeMode(c.compositeMode)
return c.dst.framebuffer.DrawTexture(glContext, c.src.texture, c.vertices, &c.geoM, &c.colorM, m)
}
type replacePixelsCommand struct {
dst *Image
pixels []uint8
}
func (c *replacePixelsCommand) Exec() error {
if c.dst.isDisposed() {
return errors.New("ebiten: image is already disposed")
}
return c.dst.framebuffer.ReplacePixels(glContext, c.dst.texture, c.pixels)
}
type disposeCommand struct {
image *Image
}
func (c *disposeCommand) Exec() error {
if c.image.isDisposed() {
return errors.New("ebiten: image is already disposed")
}
if c.image.framebuffer != nil {
if err := c.image.framebuffer.Dispose(glContext); err != nil {
return err
}
c.image.framebuffer = nil
}
if c.image.texture != nil {
if err := c.image.texture.Dispose(glContext); err != nil {
return err
}
c.image.texture = nil
}
c.image.disposed = true
c.image.pixels = nil
runtime.SetFinalizer(c.image, nil)
return nil
}
type newImageCommand struct {
result *Image
width int
height int
filter Filter
}
func (c *newImageCommand) Exec() error {
texture, err := graphics.NewTexture(glContext, c.width, c.height, glFilter(glContext, c.filter))
if err != nil {
return err
}
framebuffer, err := graphics.NewFramebufferFromTexture(glContext, texture)
if err != nil {
// TODO: texture should be removed here?
return err
}
c.result.framebuffer = framebuffer
c.result.texture = texture
runtime.SetFinalizer(c.result, (*Image).Dispose)
if err := c.result.framebuffer.Fill(glContext, color.Transparent); err != nil {
return err
}
return nil
}
type newImageFromImageCommand struct {
image image.Image
filter Filter
result *Image
}
func (c *newImageFromImageCommand) Exec() error {
texture, err := graphics.NewTextureFromImage(glContext, c.image, glFilter(glContext, c.filter))
if err != nil {
return err
}
framebuffer, err := graphics.NewFramebufferFromTexture(glContext, texture)
if err != nil {
// TODO: texture should be removed here?
return err
}
c.result.framebuffer = framebuffer
c.result.texture = texture
runtime.SetFinalizer(c.result, (*Image).Dispose)
return nil
}