mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2025-01-26 10:42:42 +01:00
graphicscommand: Simplify Image
First I thought Metal requried an initialization process by replacing pixels, but now this is not needed. Initialize images by the dummy texture.
This commit is contained in:
parent
8b72ff5ec0
commit
5d0420cea0
@ -20,44 +20,20 @@ import (
|
|||||||
"github.com/hajimehoshi/ebiten/internal/graphicsdriver"
|
"github.com/hajimehoshi/ebiten/internal/graphicsdriver"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
// maxImageSize is the maximum texture size
|
|
||||||
//
|
|
||||||
// maxImageSize also represents the default size (width or height) of viewport.
|
|
||||||
maxImageSize = 0
|
|
||||||
)
|
|
||||||
|
|
||||||
// imageState is a state of an image.
|
|
||||||
type imageState int
|
|
||||||
|
|
||||||
const (
|
|
||||||
// imageStateInit represents that the image is just allocated and not ready for DrawImages.
|
|
||||||
imageStateInit imageState = iota
|
|
||||||
|
|
||||||
// imageStateReplacePixelsOnly represents that only ReplacePixels is acceptable.
|
|
||||||
imageStateReplacePixelsOnly
|
|
||||||
|
|
||||||
// imageStateDrawable represents that the image is ready to draw with any commands.
|
|
||||||
imageStateDrawable
|
|
||||||
|
|
||||||
// imageStateScreen is the special state for screen framebuffer.
|
|
||||||
// Only copying image on the screen image is allowed.
|
|
||||||
imageStateScreen
|
|
||||||
)
|
|
||||||
|
|
||||||
// Image represents an image that is implemented with OpenGL.
|
// Image represents an image that is implemented with OpenGL.
|
||||||
type Image struct {
|
type Image struct {
|
||||||
image graphicsdriver.Image
|
image graphicsdriver.Image
|
||||||
width int
|
width int
|
||||||
height int
|
height int
|
||||||
state imageState
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewImage returns a new image.
|
||||||
|
//
|
||||||
|
// Note that the image is not initialized yet.
|
||||||
func NewImage(width, height int) *Image {
|
func NewImage(width, height int) *Image {
|
||||||
i := &Image{
|
i := &Image{
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
state: imageStateInit, // The screen image must be inited with ReplacePixels first.
|
|
||||||
}
|
}
|
||||||
c := &newImageCommand{
|
c := &newImageCommand{
|
||||||
result: i,
|
result: i,
|
||||||
@ -72,7 +48,6 @@ func NewScreenFramebufferImage(width, height int) *Image {
|
|||||||
i := &Image{
|
i := &Image{
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
state: imageStateScreen,
|
|
||||||
}
|
}
|
||||||
c := &newScreenFramebufferImageCommand{
|
c := &newScreenFramebufferImageCommand{
|
||||||
result: i,
|
result: i,
|
||||||
@ -83,26 +58,6 @@ func NewScreenFramebufferImage(width, height int) *Image {
|
|||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
// clearByReplacingPixels clears the image by replacing pixels.
|
|
||||||
//
|
|
||||||
// The implementation must use replacing-pixels way instead of drawing polygons, since
|
|
||||||
// some environments (e.g. Metal) require replacing-pixels way as initialization.
|
|
||||||
func (i *Image) clearByReplacingPixels() {
|
|
||||||
if i.state != imageStateInit {
|
|
||||||
panic("not reached")
|
|
||||||
}
|
|
||||||
c := &replacePixelsCommand{
|
|
||||||
dst: i,
|
|
||||||
pixels: make([]byte, 4*i.width*i.height),
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
width: i.width,
|
|
||||||
height: i.height,
|
|
||||||
}
|
|
||||||
theCommandQueue.Enqueue(c)
|
|
||||||
i.state = imageStateDrawable
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *Image) Dispose() {
|
func (i *Image) Dispose() {
|
||||||
c := &disposeCommand{
|
c := &disposeCommand{
|
||||||
target: i,
|
target: i,
|
||||||
@ -116,56 +71,12 @@ func (i *Image) Size() (int, int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Image) DrawImage(src *Image, vertices []float32, indices []uint16, clr *affine.ColorM, mode graphics.CompositeMode, filter graphics.Filter) {
|
func (i *Image) DrawImage(src *Image, vertices []float32, indices []uint16, clr *affine.ColorM, mode graphics.CompositeMode, filter graphics.Filter) {
|
||||||
switch i.state {
|
|
||||||
case imageStateInit:
|
|
||||||
// Before DrawImage, the image must be initialized with ReplacePixels.
|
|
||||||
// Especially on Metal, the image might be broken when drawing without initializing.
|
|
||||||
i.clearByReplacingPixels()
|
|
||||||
case imageStateReplacePixelsOnly:
|
|
||||||
panic("not reached")
|
|
||||||
case imageStateDrawable:
|
|
||||||
// Do nothing
|
|
||||||
case imageStateScreen:
|
|
||||||
if mode != graphics.CompositeModeCopy {
|
|
||||||
panic("not reached")
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
panic("not reached")
|
|
||||||
}
|
|
||||||
|
|
||||||
switch src.state {
|
|
||||||
case imageStateInit:
|
|
||||||
src.clearByReplacingPixels()
|
|
||||||
case imageStateReplacePixelsOnly:
|
|
||||||
// Do nothing
|
|
||||||
// TODO: Check the region.
|
|
||||||
case imageStateDrawable:
|
|
||||||
// Do nothing
|
|
||||||
case imageStateScreen:
|
|
||||||
panic("not reached")
|
|
||||||
default:
|
|
||||||
panic("not reached")
|
|
||||||
}
|
|
||||||
|
|
||||||
theCommandQueue.EnqueueDrawImageCommand(i, src, vertices, indices, clr, mode, filter)
|
theCommandQueue.EnqueueDrawImageCommand(i, src, vertices, indices, clr, mode, filter)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pixels returns the image's pixels.
|
// Pixels returns the image's pixels.
|
||||||
// Pixels might return nil when OpenGL error happens.
|
// Pixels might return nil when OpenGL error happens.
|
||||||
func (i *Image) Pixels() []byte {
|
func (i *Image) Pixels() []byte {
|
||||||
switch i.state {
|
|
||||||
case imageStateInit:
|
|
||||||
i.clearByReplacingPixels()
|
|
||||||
case imageStateReplacePixelsOnly:
|
|
||||||
// Do nothing
|
|
||||||
// TODO: Check the region?
|
|
||||||
case imageStateDrawable:
|
|
||||||
// Do nothing
|
|
||||||
case imageStateScreen:
|
|
||||||
panic("not reached")
|
|
||||||
default:
|
|
||||||
panic("not reached")
|
|
||||||
}
|
|
||||||
c := &pixelsCommand{
|
c := &pixelsCommand{
|
||||||
result: nil,
|
result: nil,
|
||||||
img: i,
|
img: i,
|
||||||
@ -176,22 +87,6 @@ func (i *Image) Pixels() []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *Image) ReplacePixels(p []byte, x, y, width, height int) {
|
func (i *Image) ReplacePixels(p []byte, x, y, width, height int) {
|
||||||
switch i.state {
|
|
||||||
case imageStateInit:
|
|
||||||
if x == 0 && y == 0 && width == i.width && height == i.height {
|
|
||||||
i.state = imageStateDrawable
|
|
||||||
} else {
|
|
||||||
i.state = imageStateReplacePixelsOnly
|
|
||||||
}
|
|
||||||
case imageStateReplacePixelsOnly:
|
|
||||||
// Do nothing
|
|
||||||
case imageStateDrawable:
|
|
||||||
// Do nothing
|
|
||||||
case imageStateScreen:
|
|
||||||
panic("not reached")
|
|
||||||
default:
|
|
||||||
panic("not reached")
|
|
||||||
}
|
|
||||||
pixels := make([]byte, len(p))
|
pixels := make([]byte, len(p))
|
||||||
copy(pixels, p)
|
copy(pixels, p)
|
||||||
c := &replacePixelsCommand{
|
c := &replacePixelsCommand{
|
||||||
|
@ -57,7 +57,14 @@ type Image struct {
|
|||||||
h2 int
|
h2 int
|
||||||
}
|
}
|
||||||
|
|
||||||
var emptyImage = NewImage(16, 16, false)
|
var dummyImage *Image
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
dummyImage = &Image{
|
||||||
|
image: graphicscommand.NewImage(16, 16),
|
||||||
|
volatile: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NewImage creates an empty image with the given size.
|
// NewImage creates an empty image with the given size.
|
||||||
//
|
//
|
||||||
@ -69,6 +76,7 @@ func NewImage(width, height int, volatile bool) *Image {
|
|||||||
image: graphicscommand.NewImage(width, height),
|
image: graphicscommand.NewImage(width, height),
|
||||||
volatile: volatile,
|
volatile: volatile,
|
||||||
}
|
}
|
||||||
|
i.ReplacePixels(nil, 0, 0, width, height)
|
||||||
theImages.add(i)
|
theImages.add(i)
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
@ -143,13 +151,13 @@ func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) {
|
|||||||
if pixels != nil {
|
if pixels != nil {
|
||||||
i.image.ReplacePixels(pixels, x, y, width, height)
|
i.image.ReplacePixels(pixels, x, y, width, height)
|
||||||
} else {
|
} else {
|
||||||
// There are not 'drawImageHistoryItem's for this image and emptyImage.
|
// There are not 'drawImageHistoryItem's for this image and dummyImage.
|
||||||
// This means emptyImage might not be restored yet when this image is restored.
|
// This means dummyImage might not be restored yet when this image is restored.
|
||||||
// However, that's ok since this image will be stale or have its updated pixel data soon,
|
// However, that's ok since this image will be stale or have its updated pixel data soon,
|
||||||
// and this image can be restored without emptyImage.
|
// and this image can be restored without dummyImage.
|
||||||
//
|
//
|
||||||
// emptyImage should be restored later anyway.
|
// dummyImage should be restored later anyway.
|
||||||
dw, dh := emptyImage.Size()
|
dw, dh := dummyImage.Size()
|
||||||
w2 := graphics.NextPowerOf2Int(w)
|
w2 := graphics.NextPowerOf2Int(w)
|
||||||
h2 := graphics.NextPowerOf2Int(h)
|
h2 := graphics.NextPowerOf2Int(h)
|
||||||
vs := graphics.QuadVertices(w2, h2, 0, 0, dw, dh,
|
vs := graphics.QuadVertices(w2, h2, 0, 0, dw, dh,
|
||||||
@ -157,7 +165,7 @@ func (i *Image) ReplacePixels(pixels []byte, x, y, width, height int) {
|
|||||||
float32(x), float32(y),
|
float32(x), float32(y),
|
||||||
1, 1, 1, 1)
|
1, 1, 1, 1)
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
i.image.DrawImage(emptyImage.image, vs, is, nil, graphics.CompositeModeCopy, graphics.FilterNearest)
|
i.image.DrawImage(dummyImage.image, vs, is, nil, graphics.CompositeModeClear, graphics.FilterNearest)
|
||||||
}
|
}
|
||||||
|
|
||||||
if x == 0 && y == 0 && width == w && height == h {
|
if x == 0 && y == 0 && width == w && height == h {
|
||||||
|
Loading…
Reference in New Issue
Block a user