mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-11-10 04:57:26 +01:00
internal/atlas: refactoring: remove SetVolatile and SetIsolate
Pass an image type to NewImage instead.
This commit is contained in:
parent
b8e8d72377
commit
81f91658ff
13
gameforui.go
13
gameforui.go
@ -15,6 +15,7 @@
|
|||||||
package ebiten
|
package ebiten
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/hajimehoshi/ebiten/v2/internal/atlas"
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/ui"
|
"github.com/hajimehoshi/ebiten/v2/internal/ui"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -34,7 +35,17 @@ func (c *gameForUI) NewOffscreenImage(width, height int) *ui.Image {
|
|||||||
c.offscreen.Dispose()
|
c.offscreen.Dispose()
|
||||||
c.offscreen = nil
|
c.offscreen = nil
|
||||||
}
|
}
|
||||||
c.offscreen = NewImage(width, height)
|
|
||||||
|
// Keep the offscreen an isolated image from an atlas (#1938).
|
||||||
|
// The shader program for the screen is special and doesn't work well with an image on an atlas.
|
||||||
|
// An image on an atlas is surrounded by a transparent edge,
|
||||||
|
// and the shader program unexpectedly picks the pixel on the edges.
|
||||||
|
imageType := atlas.ImageTypeIsolated
|
||||||
|
if ui.IsScreenClearedEveryFrame() {
|
||||||
|
// A violatile image is also always isolated.
|
||||||
|
imageType = atlas.ImageTypeVolatile
|
||||||
|
}
|
||||||
|
c.offscreen = newImage(width, height, imageType)
|
||||||
return c.offscreen.image
|
return c.offscreen.image
|
||||||
}
|
}
|
||||||
|
|
||||||
|
9
image.go
9
image.go
@ -20,6 +20,7 @@ import (
|
|||||||
"image/color"
|
"image/color"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/affine"
|
"github.com/hajimehoshi/ebiten/v2/internal/affine"
|
||||||
|
"github.com/hajimehoshi/ebiten/v2/internal/atlas"
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/graphics"
|
"github.com/hajimehoshi/ebiten/v2/internal/graphics"
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
|
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/ui"
|
"github.com/hajimehoshi/ebiten/v2/internal/ui"
|
||||||
@ -787,6 +788,10 @@ func (i *Image) ReplacePixels(pixels []byte) {
|
|||||||
//
|
//
|
||||||
// NewImage panics if RunGame already finishes.
|
// NewImage panics if RunGame already finishes.
|
||||||
func NewImage(width, height int) *Image {
|
func NewImage(width, height int) *Image {
|
||||||
|
return newImage(width, height, atlas.ImageTypeRegular)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newImage(width, height int, imageType atlas.ImageType) *Image {
|
||||||
if isRunGameEnded() {
|
if isRunGameEnded() {
|
||||||
panic(fmt.Sprintf("ebiten: NewImage cannot be called after RunGame finishes"))
|
panic(fmt.Sprintf("ebiten: NewImage cannot be called after RunGame finishes"))
|
||||||
}
|
}
|
||||||
@ -797,7 +802,7 @@ func NewImage(width, height int) *Image {
|
|||||||
panic(fmt.Sprintf("ebiten: height at NewImage must be positive but %d", height))
|
panic(fmt.Sprintf("ebiten: height at NewImage must be positive but %d", height))
|
||||||
}
|
}
|
||||||
i := &Image{
|
i := &Image{
|
||||||
image: ui.NewImage(width, height),
|
image: ui.NewImage(width, height, imageType),
|
||||||
bounds: image.Rect(0, 0, width, height),
|
bounds: image.Rect(0, 0, width, height),
|
||||||
}
|
}
|
||||||
i.addr = i
|
i.addr = i
|
||||||
@ -839,7 +844,7 @@ func NewImageFromImage(source image.Image) *Image {
|
|||||||
}
|
}
|
||||||
|
|
||||||
i := &Image{
|
i := &Image{
|
||||||
image: ui.NewImage(width, height),
|
image: ui.NewImage(width, height, atlas.ImageTypeRegular),
|
||||||
bounds: image.Rect(0, 0, width, height),
|
bounds: image.Rect(0, 0, width, height),
|
||||||
}
|
}
|
||||||
i.addr = i
|
i.addr = i
|
||||||
|
@ -206,14 +206,21 @@ func init() {
|
|||||||
backendsM.Lock()
|
backendsM.Lock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ImageType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
ImageTypeRegular ImageType = iota
|
||||||
|
ImageTypeScreen
|
||||||
|
ImageTypeVolatile
|
||||||
|
ImageTypeIsolated
|
||||||
|
)
|
||||||
|
|
||||||
// Image is a rectangle pixel set that might be on an atlas.
|
// Image is a rectangle pixel set that might be on an atlas.
|
||||||
type Image struct {
|
type Image struct {
|
||||||
width int
|
width int
|
||||||
height int
|
height int
|
||||||
disposed bool
|
imageType ImageType
|
||||||
isolated bool
|
disposed bool
|
||||||
volatile bool
|
|
||||||
screen bool
|
|
||||||
|
|
||||||
backend *backend
|
backend *backend
|
||||||
|
|
||||||
@ -284,7 +291,7 @@ func (i *Image) ensureIsolated() {
|
|||||||
sx1 /= float32(sw)
|
sx1 /= float32(sw)
|
||||||
sy1 /= float32(sh)
|
sy1 /= float32(sh)
|
||||||
typ := restorable.ImageTypeRegular
|
typ := restorable.ImageTypeRegular
|
||||||
if i.volatile {
|
if i.imageType == ImageTypeVolatile {
|
||||||
typ = restorable.ImageTypeVolatile
|
typ = restorable.ImageTypeVolatile
|
||||||
}
|
}
|
||||||
newImg := restorable.NewImage(w, h, typ)
|
newImg := restorable.NewImage(w, h, typ)
|
||||||
@ -326,11 +333,8 @@ func (i *Image) putOnAtlas(graphicsDriver graphicsdriver.Graphics) error {
|
|||||||
if !i.canBePutOnAtlas() {
|
if !i.canBePutOnAtlas() {
|
||||||
panic("atlas: putOnAtlas cannot be called on a image that cannot be on an atlas")
|
panic("atlas: putOnAtlas cannot be called on a image that cannot be on an atlas")
|
||||||
}
|
}
|
||||||
if i.volatile {
|
|
||||||
panic("atlas: a volatile image cannot be put on an atlas")
|
|
||||||
}
|
|
||||||
|
|
||||||
newI := NewImage(i.width, i.height)
|
newI := NewImage(i.width, i.height, i.imageType)
|
||||||
|
|
||||||
if restorable.NeedsRestoring() {
|
if restorable.NeedsRestoring() {
|
||||||
// If the underlying graphics driver requires restoring from the context lost, the pixel data is
|
// If the underlying graphics driver requires restoring from the context lost, the pixel data is
|
||||||
@ -433,7 +437,7 @@ func (i *Image) drawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f
|
|||||||
|
|
||||||
var dx, dy float32
|
var dx, dy float32
|
||||||
// A screen image doesn't have its padding.
|
// A screen image doesn't have its padding.
|
||||||
if !i.screen {
|
if i.imageType != ImageTypeScreen {
|
||||||
x, y, _, _ := i.regionWithPadding()
|
x, y, _, _ := i.regionWithPadding()
|
||||||
dx = float32(x) + paddingSize
|
dx = float32(x) + paddingSize
|
||||||
dy = float32(y) + paddingSize
|
dy = float32(y) + paddingSize
|
||||||
@ -708,43 +712,20 @@ func (i *Image) dispose(markDisposed bool) {
|
|||||||
theBackends = append(theBackends[:index], theBackends[index+1:]...)
|
theBackends = append(theBackends[:index], theBackends[index+1:]...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewImage(width, height int) *Image {
|
func NewImage(width, height int, imageType ImageType) *Image {
|
||||||
// Actual allocation is done lazily, and the lock is not needed.
|
// Actual allocation is done lazily, and the lock is not needed.
|
||||||
return &Image{
|
return &Image{
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
|
imageType: imageType,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Image) SetIsolated(isolated bool) {
|
|
||||||
if i.backend != nil {
|
|
||||||
panic("atlas: SetIsolated must be called before its backend is allocated")
|
|
||||||
}
|
|
||||||
i.isolated = isolated
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *Image) SetVolatile(volatile bool) {
|
|
||||||
i.volatile = volatile
|
|
||||||
if i.backend == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if i.volatile {
|
|
||||||
i.ensureIsolated()
|
|
||||||
}
|
|
||||||
i.backend.restorable.SetVolatile(i.volatile)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *Image) canBePutOnAtlas() bool {
|
func (i *Image) canBePutOnAtlas() bool {
|
||||||
if minSize == 0 || maxSize == 0 {
|
if minSize == 0 || maxSize == 0 {
|
||||||
panic("atlas: minSize or maxSize must be initialized")
|
panic("atlas: minSize or maxSize must be initialized")
|
||||||
}
|
}
|
||||||
if i.isolated {
|
if i.imageType != ImageTypeRegular {
|
||||||
return false
|
|
||||||
}
|
|
||||||
if i.volatile {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if i.screen {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return i.width+2*paddingSize <= maxSize && i.height+2*paddingSize <= maxSize
|
return i.width+2*paddingSize <= maxSize && i.height+2*paddingSize <= maxSize
|
||||||
@ -757,7 +738,7 @@ func (i *Image) allocate(putOnAtlas bool) {
|
|||||||
|
|
||||||
runtime.SetFinalizer(i, (*Image).MarkDisposed)
|
runtime.SetFinalizer(i, (*Image).MarkDisposed)
|
||||||
|
|
||||||
if i.screen {
|
if i.imageType == ImageTypeScreen {
|
||||||
// A screen image doesn't have a padding.
|
// A screen image doesn't have a padding.
|
||||||
i.backend = &backend{
|
i.backend = &backend{
|
||||||
restorable: restorable.NewImage(i.width, i.height, restorable.ImageTypeScreen),
|
restorable: restorable.NewImage(i.width, i.height, restorable.ImageTypeScreen),
|
||||||
@ -767,7 +748,7 @@ func (i *Image) allocate(putOnAtlas bool) {
|
|||||||
|
|
||||||
if !putOnAtlas || !i.canBePutOnAtlas() {
|
if !putOnAtlas || !i.canBePutOnAtlas() {
|
||||||
typ := restorable.ImageTypeRegular
|
typ := restorable.ImageTypeRegular
|
||||||
if i.volatile {
|
if i.imageType == ImageTypeVolatile {
|
||||||
typ = restorable.ImageTypeVolatile
|
typ = restorable.ImageTypeVolatile
|
||||||
}
|
}
|
||||||
i.backend = &backend{
|
i.backend = &backend{
|
||||||
@ -792,7 +773,7 @@ func (i *Image) allocate(putOnAtlas bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
typ := restorable.ImageTypeRegular
|
typ := restorable.ImageTypeRegular
|
||||||
if i.volatile {
|
if i.imageType == ImageTypeVolatile {
|
||||||
typ = restorable.ImageTypeVolatile
|
typ = restorable.ImageTypeVolatile
|
||||||
}
|
}
|
||||||
b := &backend{
|
b := &backend{
|
||||||
@ -816,16 +797,6 @@ func (i *Image) DumpScreenshot(graphicsDriver graphicsdriver.Graphics, path stri
|
|||||||
return i.backend.restorable.Dump(graphicsDriver, path, blackbg, image.Rect(paddingSize, paddingSize, paddingSize+i.width, paddingSize+i.height))
|
return i.backend.restorable.Dump(graphicsDriver, path, blackbg, image.Rect(paddingSize, paddingSize, paddingSize+i.width, paddingSize+i.height))
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewScreenFramebufferImage(width, height int) *Image {
|
|
||||||
// Actual allocation is done lazily.
|
|
||||||
i := &Image{
|
|
||||||
width: width,
|
|
||||||
height: height,
|
|
||||||
screen: true,
|
|
||||||
}
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
func EndFrame(graphicsDriver graphicsdriver.Graphics) error {
|
func EndFrame(graphicsDriver graphicsdriver.Graphics) error {
|
||||||
backendsM.Lock()
|
backendsM.Lock()
|
||||||
|
|
||||||
|
@ -60,25 +60,25 @@ const bigSize = 2049
|
|||||||
func TestEnsureIsolated(t *testing.T) {
|
func TestEnsureIsolated(t *testing.T) {
|
||||||
// Create img1 and img2 with this size so that the next images are allocated
|
// Create img1 and img2 with this size so that the next images are allocated
|
||||||
// with non-upper-left location.
|
// with non-upper-left location.
|
||||||
img1 := atlas.NewImage(bigSize, 100)
|
img1 := atlas.NewImage(bigSize, 100, atlas.ImageTypeRegular)
|
||||||
defer img1.MarkDisposed()
|
defer img1.MarkDisposed()
|
||||||
// Ensure img1's region is allocated.
|
// Ensure img1's region is allocated.
|
||||||
img1.ReplacePixels(make([]byte, 4*bigSize*100), nil)
|
img1.ReplacePixels(make([]byte, 4*bigSize*100), nil)
|
||||||
|
|
||||||
img2 := atlas.NewImage(100, bigSize)
|
img2 := atlas.NewImage(100, bigSize, atlas.ImageTypeRegular)
|
||||||
defer img2.MarkDisposed()
|
defer img2.MarkDisposed()
|
||||||
img2.ReplacePixels(make([]byte, 4*100*bigSize), nil)
|
img2.ReplacePixels(make([]byte, 4*100*bigSize), nil)
|
||||||
|
|
||||||
const size = 32
|
const size = 32
|
||||||
|
|
||||||
img3 := atlas.NewImage(size/2, size/2)
|
img3 := atlas.NewImage(size/2, size/2, atlas.ImageTypeRegular)
|
||||||
defer img3.MarkDisposed()
|
defer img3.MarkDisposed()
|
||||||
img3.ReplacePixels(make([]byte, (size/2)*(size/2)*4), nil)
|
img3.ReplacePixels(make([]byte, (size/2)*(size/2)*4), nil)
|
||||||
|
|
||||||
img4 := atlas.NewImage(size, size)
|
img4 := atlas.NewImage(size, size, atlas.ImageTypeRegular)
|
||||||
defer img4.MarkDisposed()
|
defer img4.MarkDisposed()
|
||||||
|
|
||||||
img5 := atlas.NewImage(size/2, size/2)
|
img5 := atlas.NewImage(size/2, size/2, atlas.ImageTypeRegular)
|
||||||
defer img3.MarkDisposed()
|
defer img3.MarkDisposed()
|
||||||
|
|
||||||
pix := make([]byte, size*size*4)
|
pix := make([]byte, size*size*4)
|
||||||
@ -155,18 +155,18 @@ func TestEnsureIsolated(t *testing.T) {
|
|||||||
func TestReputOnAtlas(t *testing.T) {
|
func TestReputOnAtlas(t *testing.T) {
|
||||||
const size = 16
|
const size = 16
|
||||||
|
|
||||||
img0 := atlas.NewImage(size, size)
|
img0 := atlas.NewImage(size, size, atlas.ImageTypeRegular)
|
||||||
defer img0.MarkDisposed()
|
defer img0.MarkDisposed()
|
||||||
img0.ReplacePixels(make([]byte, 4*size*size), nil)
|
img0.ReplacePixels(make([]byte, 4*size*size), nil)
|
||||||
|
|
||||||
img1 := atlas.NewImage(size, size)
|
img1 := atlas.NewImage(size, size, atlas.ImageTypeRegular)
|
||||||
defer img1.MarkDisposed()
|
defer img1.MarkDisposed()
|
||||||
img1.ReplacePixels(make([]byte, 4*size*size), nil)
|
img1.ReplacePixels(make([]byte, 4*size*size), nil)
|
||||||
if got, want := img1.IsOnAtlasForTesting(), true; got != want {
|
if got, want := img1.IsOnAtlasForTesting(), true; got != want {
|
||||||
t.Errorf("got: %v, want: %v", got, want)
|
t.Errorf("got: %v, want: %v", got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
img2 := atlas.NewImage(size, size)
|
img2 := atlas.NewImage(size, size, atlas.ImageTypeRegular)
|
||||||
defer img2.MarkDisposed()
|
defer img2.MarkDisposed()
|
||||||
pix := make([]byte, 4*size*size)
|
pix := make([]byte, 4*size*size)
|
||||||
for j := 0; j < size; j++ {
|
for j := 0; j < size; j++ {
|
||||||
@ -179,8 +179,8 @@ func TestReputOnAtlas(t *testing.T) {
|
|||||||
}
|
}
|
||||||
img2.ReplacePixels(pix, nil)
|
img2.ReplacePixels(pix, nil)
|
||||||
|
|
||||||
img3 := atlas.NewImage(size, size)
|
// Create a volatile image. This should always be isolated.
|
||||||
img3.SetVolatile(true)
|
img3 := atlas.NewImage(size, size, atlas.ImageTypeVolatile)
|
||||||
defer img3.MarkDisposed()
|
defer img3.MarkDisposed()
|
||||||
img1.ReplacePixels(make([]byte, 4*size*size), nil)
|
img1.ReplacePixels(make([]byte, 4*size*size), nil)
|
||||||
if got, want := img3.IsOnAtlasForTesting(), false; got != want {
|
if got, want := img3.IsOnAtlasForTesting(), false; got != want {
|
||||||
@ -303,7 +303,7 @@ func TestReputOnAtlas(t *testing.T) {
|
|||||||
|
|
||||||
func TestExtend(t *testing.T) {
|
func TestExtend(t *testing.T) {
|
||||||
const w0, h0 = 100, 100
|
const w0, h0 = 100, 100
|
||||||
img0 := atlas.NewImage(w0, h0)
|
img0 := atlas.NewImage(w0, h0, atlas.ImageTypeRegular)
|
||||||
defer img0.MarkDisposed()
|
defer img0.MarkDisposed()
|
||||||
|
|
||||||
p0 := make([]byte, 4*w0*h0)
|
p0 := make([]byte, 4*w0*h0)
|
||||||
@ -316,7 +316,7 @@ func TestExtend(t *testing.T) {
|
|||||||
img0.ReplacePixels(p0, nil)
|
img0.ReplacePixels(p0, nil)
|
||||||
|
|
||||||
const w1, h1 = minImageSizeForTesting + 1, 100
|
const w1, h1 = minImageSizeForTesting + 1, 100
|
||||||
img1 := atlas.NewImage(w1, h1)
|
img1 := atlas.NewImage(w1, h1, atlas.ImageTypeRegular)
|
||||||
defer img1.MarkDisposed()
|
defer img1.MarkDisposed()
|
||||||
|
|
||||||
p1 := make([]byte, 4*w1*h1)
|
p1 := make([]byte, 4*w1*h1)
|
||||||
@ -370,9 +370,9 @@ func TestExtend(t *testing.T) {
|
|||||||
|
|
||||||
func TestReplacePixelsAfterDrawTriangles(t *testing.T) {
|
func TestReplacePixelsAfterDrawTriangles(t *testing.T) {
|
||||||
const w, h = 256, 256
|
const w, h = 256, 256
|
||||||
src := atlas.NewImage(w, h)
|
src := atlas.NewImage(w, h, atlas.ImageTypeRegular)
|
||||||
defer src.MarkDisposed()
|
defer src.MarkDisposed()
|
||||||
dst := atlas.NewImage(w, h)
|
dst := atlas.NewImage(w, h, atlas.ImageTypeRegular)
|
||||||
defer dst.MarkDisposed()
|
defer dst.MarkDisposed()
|
||||||
|
|
||||||
pix := make([]byte, 4*w*h)
|
pix := make([]byte, 4*w*h)
|
||||||
@ -418,9 +418,9 @@ func TestReplacePixelsAfterDrawTriangles(t *testing.T) {
|
|||||||
// Issue #887
|
// Issue #887
|
||||||
func TestSmallImages(t *testing.T) {
|
func TestSmallImages(t *testing.T) {
|
||||||
const w, h = 4, 8
|
const w, h = 4, 8
|
||||||
src := atlas.NewImage(w, h)
|
src := atlas.NewImage(w, h, atlas.ImageTypeRegular)
|
||||||
defer src.MarkDisposed()
|
defer src.MarkDisposed()
|
||||||
dst := atlas.NewImage(w, h)
|
dst := atlas.NewImage(w, h, atlas.ImageTypeRegular)
|
||||||
defer dst.MarkDisposed()
|
defer dst.MarkDisposed()
|
||||||
|
|
||||||
pix := make([]byte, 4*w*h)
|
pix := make([]byte, 4*w*h)
|
||||||
@ -463,11 +463,11 @@ func TestSmallImages(t *testing.T) {
|
|||||||
// Issue #887
|
// Issue #887
|
||||||
func TestLongImages(t *testing.T) {
|
func TestLongImages(t *testing.T) {
|
||||||
const w, h = 1, 6
|
const w, h = 1, 6
|
||||||
src := atlas.NewImage(w, h)
|
src := atlas.NewImage(w, h, atlas.ImageTypeRegular)
|
||||||
defer src.MarkDisposed()
|
defer src.MarkDisposed()
|
||||||
|
|
||||||
const dstW, dstH = 256, 256
|
const dstW, dstH = 256, 256
|
||||||
dst := atlas.NewImage(dstW, dstH)
|
dst := atlas.NewImage(dstW, dstH, atlas.ImageTypeRegular)
|
||||||
defer dst.MarkDisposed()
|
defer dst.MarkDisposed()
|
||||||
|
|
||||||
pix := make([]byte, 4*w*h)
|
pix := make([]byte, 4*w*h)
|
||||||
@ -511,11 +511,11 @@ func TestLongImages(t *testing.T) {
|
|||||||
func TestDisposeImmediately(t *testing.T) {
|
func TestDisposeImmediately(t *testing.T) {
|
||||||
// This tests restorable.Image.ClearPixels is called but ReplacePixels is not called.
|
// This tests restorable.Image.ClearPixels is called but ReplacePixels is not called.
|
||||||
|
|
||||||
img0 := atlas.NewImage(16, 16)
|
img0 := atlas.NewImage(16, 16, atlas.ImageTypeRegular)
|
||||||
img0.EnsureIsolatedForTesting()
|
img0.EnsureIsolatedForTesting()
|
||||||
defer img0.MarkDisposed()
|
defer img0.MarkDisposed()
|
||||||
|
|
||||||
img1 := atlas.NewImage(16, 16)
|
img1 := atlas.NewImage(16, 16, atlas.ImageTypeRegular)
|
||||||
img1.EnsureIsolatedForTesting()
|
img1.EnsureIsolatedForTesting()
|
||||||
defer img1.MarkDisposed()
|
defer img1.MarkDisposed()
|
||||||
|
|
||||||
@ -524,12 +524,12 @@ func TestDisposeImmediately(t *testing.T) {
|
|||||||
|
|
||||||
// Issue #1028
|
// Issue #1028
|
||||||
func TestExtendWithBigImage(t *testing.T) {
|
func TestExtendWithBigImage(t *testing.T) {
|
||||||
img0 := atlas.NewImage(1, 1)
|
img0 := atlas.NewImage(1, 1, atlas.ImageTypeRegular)
|
||||||
defer img0.MarkDisposed()
|
defer img0.MarkDisposed()
|
||||||
|
|
||||||
img0.ReplacePixels(make([]byte, 4*1*1), nil)
|
img0.ReplacePixels(make([]byte, 4*1*1), nil)
|
||||||
|
|
||||||
img1 := atlas.NewImage(minImageSizeForTesting+1, minImageSizeForTesting+1)
|
img1 := atlas.NewImage(minImageSizeForTesting+1, minImageSizeForTesting+1, atlas.ImageTypeRegular)
|
||||||
defer img1.MarkDisposed()
|
defer img1.MarkDisposed()
|
||||||
|
|
||||||
img1.ReplacePixels(make([]byte, 4*(minImageSizeForTesting+1)*(minImageSizeForTesting+1)), nil)
|
img1.ReplacePixels(make([]byte, 4*(minImageSizeForTesting+1)*(minImageSizeForTesting+1)), nil)
|
||||||
@ -539,7 +539,7 @@ func TestExtendWithBigImage(t *testing.T) {
|
|||||||
func TestMaxImageSize(t *testing.T) {
|
func TestMaxImageSize(t *testing.T) {
|
||||||
// This tests that a too-big image is allocated correctly.
|
// This tests that a too-big image is allocated correctly.
|
||||||
s := maxImageSizeForTesting - 2*atlas.PaddingSize
|
s := maxImageSizeForTesting - 2*atlas.PaddingSize
|
||||||
img := atlas.NewImage(s, s)
|
img := atlas.NewImage(s, s, atlas.ImageTypeRegular)
|
||||||
defer img.MarkDisposed()
|
defer img.MarkDisposed()
|
||||||
img.ReplacePixels(make([]byte, 4*s*s), nil)
|
img.ReplacePixels(make([]byte, 4*s*s), nil)
|
||||||
}
|
}
|
||||||
@ -552,7 +552,7 @@ func Disable_TestMinImageSize(t *testing.T) {
|
|||||||
// This tests that extending a backend works correctly.
|
// This tests that extending a backend works correctly.
|
||||||
// Though the image size is minimum size of the backend, extending the backend happens due to the paddings.
|
// Though the image size is minimum size of the backend, extending the backend happens due to the paddings.
|
||||||
s := minImageSizeForTesting
|
s := minImageSizeForTesting
|
||||||
img := atlas.NewImage(s, s)
|
img := atlas.NewImage(s, s, atlas.ImageTypeRegular)
|
||||||
defer img.MarkDisposed()
|
defer img.MarkDisposed()
|
||||||
img.ReplacePixels(make([]byte, 4*s*s), nil)
|
img.ReplacePixels(make([]byte, 4*s*s), nil)
|
||||||
}
|
}
|
||||||
@ -561,11 +561,11 @@ func Disable_TestMinImageSize(t *testing.T) {
|
|||||||
func TestDisposedAndReputOnAtlas(t *testing.T) {
|
func TestDisposedAndReputOnAtlas(t *testing.T) {
|
||||||
const size = 16
|
const size = 16
|
||||||
|
|
||||||
src := atlas.NewImage(size, size)
|
src := atlas.NewImage(size, size, atlas.ImageTypeRegular)
|
||||||
defer src.MarkDisposed()
|
defer src.MarkDisposed()
|
||||||
src2 := atlas.NewImage(size, size)
|
src2 := atlas.NewImage(size, size, atlas.ImageTypeRegular)
|
||||||
defer src2.MarkDisposed()
|
defer src2.MarkDisposed()
|
||||||
dst := atlas.NewImage(size, size)
|
dst := atlas.NewImage(size, size, atlas.ImageTypeRegular)
|
||||||
defer dst.MarkDisposed()
|
defer dst.MarkDisposed()
|
||||||
|
|
||||||
// Use src as a render target so that src is not on an atlas.
|
// Use src as a render target so that src is not on an atlas.
|
||||||
@ -609,11 +609,11 @@ func TestDisposedAndReputOnAtlas(t *testing.T) {
|
|||||||
func TestImageIsNotReputOnAtlasWithoutUsingAsSource(t *testing.T) {
|
func TestImageIsNotReputOnAtlasWithoutUsingAsSource(t *testing.T) {
|
||||||
const size = 16
|
const size = 16
|
||||||
|
|
||||||
src := atlas.NewImage(size, size)
|
src := atlas.NewImage(size, size, atlas.ImageTypeRegular)
|
||||||
defer src.MarkDisposed()
|
defer src.MarkDisposed()
|
||||||
src2 := atlas.NewImage(size, size)
|
src2 := atlas.NewImage(size, size, atlas.ImageTypeRegular)
|
||||||
defer src2.MarkDisposed()
|
defer src2.MarkDisposed()
|
||||||
dst := atlas.NewImage(size, size)
|
dst := atlas.NewImage(size, size, atlas.ImageTypeRegular)
|
||||||
defer dst.MarkDisposed()
|
defer dst.MarkDisposed()
|
||||||
|
|
||||||
// Use src as a render target so that src is not on an atlas.
|
// Use src as a render target so that src is not on an atlas.
|
||||||
|
@ -29,7 +29,7 @@ import (
|
|||||||
func TestShaderFillTwice(t *testing.T) {
|
func TestShaderFillTwice(t *testing.T) {
|
||||||
const w, h = 1, 1
|
const w, h = 1, 1
|
||||||
|
|
||||||
dst := atlas.NewImage(w, h)
|
dst := atlas.NewImage(w, h, atlas.ImageTypeRegular)
|
||||||
|
|
||||||
vs := quadVertices(w, h, 0, 0, 1)
|
vs := quadVertices(w, h, 0, 0, 1)
|
||||||
is := graphics.QuadIndices()
|
is := graphics.QuadIndices()
|
||||||
@ -60,10 +60,10 @@ func TestShaderFillTwice(t *testing.T) {
|
|||||||
func TestImageDrawTwice(t *testing.T) {
|
func TestImageDrawTwice(t *testing.T) {
|
||||||
const w, h = 1, 1
|
const w, h = 1, 1
|
||||||
|
|
||||||
dst := atlas.NewImage(w, h)
|
dst := atlas.NewImage(w, h, atlas.ImageTypeRegular)
|
||||||
src0 := atlas.NewImage(w, h)
|
src0 := atlas.NewImage(w, h, atlas.ImageTypeRegular)
|
||||||
src0.ReplacePixels([]byte{0xff, 0xff, 0xff, 0xff}, nil)
|
src0.ReplacePixels([]byte{0xff, 0xff, 0xff, 0xff}, nil)
|
||||||
src1 := atlas.NewImage(w, h)
|
src1 := atlas.NewImage(w, h, atlas.ImageTypeRegular)
|
||||||
src1.ReplacePixels([]byte{0x80, 0x80, 0x80, 0xff}, nil)
|
src1.ReplacePixels([]byte{0x80, 0x80, 0x80, 0xff}, nil)
|
||||||
|
|
||||||
vs := quadVertices(w, h, 0, 0, 1)
|
vs := quadVertices(w, h, 0, 0, 1)
|
||||||
|
@ -45,70 +45,25 @@ func EndFrame(graphicsDriver graphicsdriver.Graphics) error {
|
|||||||
return atlas.EndFrame(graphicsDriver)
|
return atlas.EndFrame(graphicsDriver)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewImage(width, height int) *Image {
|
func NewImage(width, height int, imageType atlas.ImageType) *Image {
|
||||||
i := &Image{
|
i := &Image{
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
}
|
}
|
||||||
i.initialize()
|
i.initialize(imageType)
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Image) initialize() {
|
func (i *Image) initialize(imageType atlas.ImageType) {
|
||||||
if maybeCanAddDelayedCommand() {
|
if maybeCanAddDelayedCommand() {
|
||||||
if tryAddDelayedCommand(func() error {
|
if tryAddDelayedCommand(func() error {
|
||||||
i.initialize()
|
i.initialize(imageType)
|
||||||
return nil
|
return nil
|
||||||
}) {
|
}) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
i.img = atlas.NewImage(i.width, i.height)
|
i.img = atlas.NewImage(i.width, i.height, imageType)
|
||||||
}
|
|
||||||
|
|
||||||
func (i *Image) SetIsolated(isolated bool) {
|
|
||||||
if maybeCanAddDelayedCommand() {
|
|
||||||
if tryAddDelayedCommand(func() error {
|
|
||||||
i.SetIsolated(isolated)
|
|
||||||
return nil
|
|
||||||
}) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i.img.SetIsolated(isolated)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *Image) SetVolatile(volatile bool) {
|
|
||||||
if maybeCanAddDelayedCommand() {
|
|
||||||
if tryAddDelayedCommand(func() error {
|
|
||||||
i.SetVolatile(volatile)
|
|
||||||
return nil
|
|
||||||
}) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i.img.SetVolatile(volatile)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewScreenFramebufferImage(width, height int) *Image {
|
|
||||||
i := &Image{}
|
|
||||||
i.initializeAsScreenFramebuffer(width, height)
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *Image) initializeAsScreenFramebuffer(width, height int) {
|
|
||||||
if maybeCanAddDelayedCommand() {
|
|
||||||
if tryAddDelayedCommand(func() error {
|
|
||||||
i.initializeAsScreenFramebuffer(width, height)
|
|
||||||
return nil
|
|
||||||
}) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
i.img = atlas.NewScreenFramebufferImage(width, height)
|
|
||||||
i.width = width
|
|
||||||
i.height = height
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Image) invalidatePixels() {
|
func (i *Image) invalidatePixels() {
|
||||||
|
@ -19,6 +19,7 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/affine"
|
"github.com/hajimehoshi/ebiten/v2/internal/affine"
|
||||||
|
"github.com/hajimehoshi/ebiten/v2/internal/atlas"
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/buffered"
|
"github.com/hajimehoshi/ebiten/v2/internal/buffered"
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/graphics"
|
"github.com/hajimehoshi/ebiten/v2/internal/graphics"
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
|
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
|
||||||
@ -35,38 +36,15 @@ type Mipmap struct {
|
|||||||
imgs map[int]*buffered.Image
|
imgs map[int]*buffered.Image
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(width, height int) *Mipmap {
|
func New(width, height int, imageType atlas.ImageType) *Mipmap {
|
||||||
return &Mipmap{
|
return &Mipmap{
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
orig: buffered.NewImage(width, height),
|
orig: buffered.NewImage(width, height, imageType),
|
||||||
|
volatile: imageType == atlas.ImageTypeVolatile,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewScreenFramebufferMipmap(width, height int) *Mipmap {
|
|
||||||
return &Mipmap{
|
|
||||||
width: width,
|
|
||||||
height: height,
|
|
||||||
orig: buffered.NewScreenFramebufferImage(width, height),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Mipmap) SetIsolated(isolated bool) {
|
|
||||||
m.orig.SetIsolated(isolated)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Mipmap) SetVolatile(volatile bool) {
|
|
||||||
if m.volatile == volatile {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
m.volatile = volatile
|
|
||||||
if m.volatile {
|
|
||||||
m.disposeMipmaps()
|
|
||||||
}
|
|
||||||
m.orig.SetVolatile(volatile)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Mipmap) DumpScreenshot(graphicsDriver graphicsdriver.Graphics, name string, blackbg bool) error {
|
func (m *Mipmap) DumpScreenshot(graphicsDriver graphicsdriver.Graphics, name string, blackbg bool) error {
|
||||||
return m.orig.DumpScreenshot(graphicsDriver, name, blackbg)
|
return m.orig.DumpScreenshot(graphicsDriver, name, blackbg)
|
||||||
}
|
}
|
||||||
@ -203,8 +181,7 @@ func (m *Mipmap) level(level int) *buffered.Image {
|
|||||||
m.setImg(level, nil)
|
m.setImg(level, nil)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
s := buffered.NewImage(w2, h2)
|
s := buffered.NewImage(w2, h2, atlas.ImageTypeVolatile)
|
||||||
s.SetVolatile(m.volatile)
|
|
||||||
|
|
||||||
dstRegion := graphicsdriver.Region{
|
dstRegion := graphicsdriver.Region{
|
||||||
X: 0,
|
X: 0,
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/affine"
|
"github.com/hajimehoshi/ebiten/v2/internal/affine"
|
||||||
|
"github.com/hajimehoshi/ebiten/v2/internal/atlas"
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/buffered"
|
"github.com/hajimehoshi/ebiten/v2/internal/buffered"
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/clock"
|
"github.com/hajimehoshi/ebiten/v2/internal/clock"
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/debug"
|
"github.com/hajimehoshi/ebiten/v2/internal/debug"
|
||||||
@ -149,13 +150,6 @@ func (c *context) drawGame(graphicsDriver graphicsdriver.Graphics) {
|
|||||||
w, h := c.offscreen.width, c.offscreen.height
|
w, h := c.offscreen.width, c.offscreen.height
|
||||||
c.offscreen.MarkDisposed()
|
c.offscreen.MarkDisposed()
|
||||||
c.offscreen = c.game.NewOffscreenImage(w, h)
|
c.offscreen = c.game.NewOffscreenImage(w, h)
|
||||||
|
|
||||||
// TODO: Give volatile/isolated property to the constructor.
|
|
||||||
if theGlobalState.isScreenClearedEveryFrame() {
|
|
||||||
c.offscreen.setVolatile(true)
|
|
||||||
} else {
|
|
||||||
c.offscreen.mipmap.SetIsolated(true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Even though updateCount == 0, the offscreen is cleared and Draw is called.
|
// Even though updateCount == 0, the offscreen is cleared and Draw is called.
|
||||||
@ -244,7 +238,7 @@ func (c *context) layoutGame(outsideWidth, outsideHeight float64, deviceScaleFac
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if c.screen == nil {
|
if c.screen == nil {
|
||||||
c.screen = newScreenFramebufferImage(sw, sh)
|
c.screen = NewImage(sw, sh, atlas.ImageTypeScreen)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.offscreen != nil {
|
if c.offscreen != nil {
|
||||||
@ -255,17 +249,6 @@ func (c *context) layoutGame(outsideWidth, outsideHeight float64, deviceScaleFac
|
|||||||
}
|
}
|
||||||
if c.offscreen == nil {
|
if c.offscreen == nil {
|
||||||
c.offscreen = c.game.NewOffscreenImage(ow, oh)
|
c.offscreen = c.game.NewOffscreenImage(ow, oh)
|
||||||
|
|
||||||
// TODO: Give volatile/isolated property to the constructor.
|
|
||||||
if theGlobalState.isScreenClearedEveryFrame() {
|
|
||||||
c.offscreen.setVolatile(true)
|
|
||||||
} else {
|
|
||||||
// Keep the offscreen an isolated image from an atlas (#1938).
|
|
||||||
// The shader program for the screen is special and doesn't work well with an image on an atlas.
|
|
||||||
// An image on an atlas is surrounded by a transparent edge,
|
|
||||||
// and the shader program unexpectedly picks the pixel on the edges.
|
|
||||||
c.offscreen.mipmap.SetIsolated(true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ow, oh
|
return ow, oh
|
||||||
|
@ -16,6 +16,7 @@ package ui
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/affine"
|
"github.com/hajimehoshi/ebiten/v2/internal/affine"
|
||||||
|
"github.com/hajimehoshi/ebiten/v2/internal/atlas"
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/graphics"
|
"github.com/hajimehoshi/ebiten/v2/internal/graphics"
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
|
"github.com/hajimehoshi/ebiten/v2/internal/graphicsdriver"
|
||||||
"github.com/hajimehoshi/ebiten/v2/internal/mipmap"
|
"github.com/hajimehoshi/ebiten/v2/internal/mipmap"
|
||||||
@ -36,27 +37,14 @@ type Image struct {
|
|||||||
volatile bool
|
volatile bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewImage(width, height int) *Image {
|
func NewImage(width, height int, imageType atlas.ImageType) *Image {
|
||||||
return &Image{
|
return &Image{
|
||||||
mipmap: mipmap.New(width, height),
|
mipmap: mipmap.New(width, height, imageType),
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newScreenFramebufferImage(width, height int) *Image {
|
|
||||||
return &Image{
|
|
||||||
mipmap: mipmap.NewScreenFramebufferMipmap(width, height),
|
|
||||||
width: width,
|
|
||||||
height: height,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *Image) setVolatile(volatile bool) {
|
|
||||||
i.volatile = volatile
|
|
||||||
i.mipmap.SetVolatile(volatile)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *Image) MarkDisposed() {
|
func (i *Image) MarkDisposed() {
|
||||||
if i.mipmap == nil {
|
if i.mipmap == nil {
|
||||||
return
|
return
|
||||||
@ -112,7 +100,7 @@ func DumpImages(dir string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
emptyImage = NewImage(3, 3)
|
emptyImage = NewImage(3, 3, atlas.ImageTypeRegular)
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
Loading…
Reference in New Issue
Block a user