internal/atlas: refactoring: make paddingSize a method

Updates #2131
This commit is contained in:
Hajime Hoshi 2022-06-10 00:14:04 +09:00
parent 5290605b2d
commit 9e34103491
3 changed files with 52 additions and 38 deletions

View File

@ -20,7 +20,6 @@ import (
const ( const (
BaseCountToPutOnAtlas = baseCountToPutOnAtlas BaseCountToPutOnAtlas = baseCountToPutOnAtlas
PaddingSize = paddingSize
) )
func PutImagesOnAtlasForTesting(graphicsDriver graphicsdriver.Graphics) error { func PutImagesOnAtlasForTesting(graphicsDriver graphicsdriver.Graphics) error {
@ -44,6 +43,10 @@ func ResetImageSizeForTesting() {
maxSize = oldMaxSize maxSize = oldMaxSize
} }
func (i *Image) PaddingSizeForTesting() int {
return i.paddingSize()
}
func (i *Image) IsOnAtlasForTesting() bool { func (i *Image) IsOnAtlasForTesting() bool {
backendsM.Lock() backendsM.Lock()
defer backendsM.Unlock() defer backendsM.Unlock()

View File

@ -28,12 +28,6 @@ import (
"github.com/hajimehoshi/ebiten/v2/internal/restorable" "github.com/hajimehoshi/ebiten/v2/internal/restorable"
) )
const (
// paddingSize represents the size of padding around an image.
// Every image or node except for a screen image has its padding.
paddingSize = 1
)
var ( var (
minSize = 0 minSize = 0
maxSize = 0 maxSize = 0
@ -264,6 +258,16 @@ func (i *Image) resetUsedAsSourceCount() {
delete(imagesToPutOnAtlas, i) delete(imagesToPutOnAtlas, i)
} }
func (i *Image) paddingSize() int {
// TODO: Do not use paddigns for ImageTypeVolatile and ImageTypeIsoalted.
// There is a contradiction with the comment in gameforui.go.
// See also #1938 and #2131
if i.imageType == ImageTypeScreen {
return 0
}
return 1
}
func (i *Image) ensureIsolated() { func (i *Image) ensureIsolated() {
i.resetUsedAsSourceCount() i.resetUsedAsSourceCount()
@ -305,10 +309,10 @@ func (i *Image) ensureIsolated() {
srcs := [graphics.ShaderImageNum]*restorable.Image{i.backend.restorable} srcs := [graphics.ShaderImageNum]*restorable.Image{i.backend.restorable}
var offsets [graphics.ShaderImageNum - 1][2]float32 var offsets [graphics.ShaderImageNum - 1][2]float32
dstRegion := graphicsdriver.Region{ dstRegion := graphicsdriver.Region{
X: paddingSize, X: float32(i.paddingSize()),
Y: paddingSize, Y: float32(i.paddingSize()),
Width: float32(w - 2*paddingSize), Width: float32(w - 2*i.paddingSize()),
Height: float32(h - 2*paddingSize), Height: float32(h - 2*i.paddingSize()),
} }
newImg.DrawTriangles(srcs, offsets, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeCopy, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dstRegion, graphicsdriver.Region{}, nil, nil, false) newImg.DrawTriangles(srcs, offsets, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeCopy, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, dstRegion, graphicsdriver.Region{}, nil, nil, false)
@ -342,7 +346,7 @@ func (i *Image) putOnAtlas(graphicsDriver graphicsdriver.Graphics) error {
pixels := make([]byte, 4*i.width*i.height) pixels := make([]byte, 4*i.width*i.height)
for y := 0; y < i.height; y++ { for y := 0; y < i.height; y++ {
for x := 0; x < i.width; x++ { for x := 0; x < i.width; x++ {
r, g, b, a, err := i.at(graphicsDriver, x+paddingSize, y+paddingSize) r, g, b, a, err := i.at(graphicsDriver, x+i.paddingSize(), y+i.paddingSize())
if err != nil { if err != nil {
return err return err
} }
@ -378,7 +382,7 @@ func (i *Image) regionWithPadding() (x, y, width, height int) {
panic("atlas: backend must not be nil: not allocated yet?") panic("atlas: backend must not be nil: not allocated yet?")
} }
if !i.isOnAtlas() { if !i.isOnAtlas() {
return 0, 0, i.width + 2*paddingSize, i.height + 2*paddingSize return 0, 0, i.width + 2*i.paddingSize(), i.height + 2*i.paddingSize()
} }
return i.node.Region() return i.node.Region()
} }
@ -435,22 +439,19 @@ func (i *Image) drawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f
i.processSrc(src) i.processSrc(src)
} }
var dx, dy float32 x, y, _, _ := i.regionWithPadding()
// A screen image doesn't have its padding. dx := float32(x + i.paddingSize())
if i.imageType != ImageTypeScreen { dy := float32(y + i.paddingSize())
x, y, _, _ := i.regionWithPadding() // TODO: Check if dstRegion does not to violate the region.
dx = float32(x) + paddingSize
dy = float32(y) + paddingSize
// TODO: Check if dstRegion does not to violate the region.
}
dstRegion.X += dx dstRegion.X += dx
dstRegion.Y += dy dstRegion.Y += dy
var oxf, oyf float32 var oxf, oyf float32
if srcs[0] != nil { if srcs[0] != nil {
ox, oy, _, _ := srcs[0].regionWithPadding() ox, oy, _, _ := srcs[0].regionWithPadding()
ox += paddingSize ox += srcs[0].paddingSize()
oy += paddingSize oy += srcs[0].paddingSize()
oxf, oyf = float32(ox), float32(oy) oxf, oyf = float32(ox), float32(oy)
sw, sh := srcs[0].backend.restorable.InternalSize() sw, sh := srcs[0].backend.restorable.InternalSize()
swf, shf := float32(sw), float32(sh) swf, shf := float32(sw), float32(sh)
@ -488,8 +489,8 @@ func (i *Image) drawTriangles(srcs [graphics.ShaderImageNum]*Image, vertices []f
continue continue
} }
ox, oy, _, _ := src.regionWithPadding() ox, oy, _, _ := src.regionWithPadding()
offsets[i][0] = float32(ox) + paddingSize - oxf + subimageOffset[0] offsets[i][0] = float32(ox+src.paddingSize()) - oxf + subimageOffset[0]
offsets[i][1] = float32(oy) + paddingSize - oyf + subimageOffset[1] offsets[i][1] = float32(oy+src.paddingSize()) - oyf + subimageOffset[1]
} }
s = shader.shader s = shader.shader
for i, src := range srcs { for i, src := range srcs {
@ -545,15 +546,21 @@ func (i *Image) replacePixels(pix []byte, mask []byte) {
return return
} }
ow, oh := pw-2*paddingSize, ph-2*paddingSize ow, oh := pw-2*i.paddingSize(), ph-2*i.paddingSize()
if l := 4 * ow * oh; len(pix) != l { if l := 4 * ow * oh; len(pix) != l {
panic(fmt.Sprintf("atlas: len(p) must be %d but %d", l, len(pix))) panic(fmt.Sprintf("atlas: len(p) must be %d but %d", l, len(pix)))
} }
// TODO: Just copy pix and mask and pass them as they are when i.paddingSize() == 0
pixb := theTemporaryBytes.alloc(4 * pw * ph) pixb := theTemporaryBytes.alloc(4 * pw * ph)
// Clear the edges. pixb might not be zero-cleared. // Clear the edges. pixb might not be zero-cleared.
// TODO: These loops assume that paddingSize is 1. // TODO: These loops assume that paddingSize is 1.
const paddingSize = 1
if paddingSize != i.paddingSize() {
panic(fmt.Sprintf("atlas: replacePixels assumes the padding is always 1 but the actual padding was %d", i.paddingSize()))
}
rowPixels := 4 * pw rowPixels := 4 * pw
for i := 0; i < rowPixels; i++ { for i := 0; i < rowPixels; i++ {
pixb[i] = 0 pixb[i] = 0
@ -616,8 +623,8 @@ func (img *Image) Pixels(graphicsDriver graphicsdriver.Graphics) ([]byte, error)
backendsM.Lock() backendsM.Lock()
defer backendsM.Unlock() defer backendsM.Unlock()
x := paddingSize x := img.paddingSize()
y := paddingSize y := img.paddingSize()
bs := make([]byte, 4*img.width*img.height) bs := make([]byte, 4*img.width*img.height)
idx := 0 idx := 0
@ -729,7 +736,7 @@ func (i *Image) canBePutOnAtlas() bool {
if i.imageType != ImageTypeRegular { if i.imageType != ImageTypeRegular {
return false return false
} }
return i.width+2*paddingSize <= maxSize && i.height+2*paddingSize <= maxSize return i.width+2*i.paddingSize() <= maxSize && i.height+2*i.paddingSize() <= maxSize
} }
func (i *Image) allocate(putOnAtlas bool) { func (i *Image) allocate(putOnAtlas bool) {
@ -753,20 +760,20 @@ func (i *Image) allocate(putOnAtlas bool) {
typ = restorable.ImageTypeVolatile typ = restorable.ImageTypeVolatile
} }
i.backend = &backend{ i.backend = &backend{
restorable: restorable.NewImage(i.width+2*paddingSize, i.height+2*paddingSize, typ), restorable: restorable.NewImage(i.width+2*i.paddingSize(), i.height+2*i.paddingSize(), typ),
} }
return return
} }
for _, b := range theBackends { for _, b := range theBackends {
if n, ok := b.tryAlloc(i.width+2*paddingSize, i.height+2*paddingSize); ok { if n, ok := b.tryAlloc(i.width+2*i.paddingSize(), i.height+2*i.paddingSize()); ok {
i.backend = b i.backend = b
i.node = n i.node = n
return return
} }
} }
size := minSize size := minSize
for i.width+2*paddingSize > size || i.height+2*paddingSize > size { for i.width+2*i.paddingSize() > size || i.height+2*i.paddingSize() > size {
if size == maxSize { if size == maxSize {
panic(fmt.Sprintf("atlas: the image being put on an atlas is too big: width: %d, height: %d", i.width, i.height)) panic(fmt.Sprintf("atlas: the image being put on an atlas is too big: width: %d, height: %d", i.width, i.height))
} }
@ -783,7 +790,7 @@ func (i *Image) allocate(putOnAtlas bool) {
} }
theBackends = append(theBackends, b) theBackends = append(theBackends, b)
n := b.page.Alloc(i.width+2*paddingSize, i.height+2*paddingSize) n := b.page.Alloc(i.width+2*i.paddingSize(), i.height+2*i.paddingSize())
if n == nil { if n == nil {
panic("atlas: Alloc result must not be nil at allocate") panic("atlas: Alloc result must not be nil at allocate")
} }
@ -795,7 +802,7 @@ func (i *Image) DumpScreenshot(graphicsDriver graphicsdriver.Graphics, path stri
backendsM.Lock() backendsM.Lock()
defer backendsM.Unlock() defer backendsM.Unlock()
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(i.paddingSize(), i.paddingSize(), i.width+i.paddingSize(), i.height+i.paddingSize()))
} }
func EndFrame(graphicsDriver graphicsdriver.Graphics) error { func EndFrame(graphicsDriver graphicsdriver.Graphics) error {

View File

@ -537,11 +537,15 @@ func TestExtendWithBigImage(t *testing.T) {
// Issue #1217 // Issue #1217
func TestMaxImageSize(t *testing.T) { func TestMaxImageSize(t *testing.T) {
img0 := atlas.NewImage(1, 1, atlas.ImageTypeRegular)
defer img0.MarkDisposed()
paddingSize := img0.PaddingSizeForTesting()
// 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*paddingSize
img := atlas.NewImage(s, s, atlas.ImageTypeRegular) img1 := atlas.NewImage(s, s, atlas.ImageTypeRegular)
defer img.MarkDisposed() defer img1.MarkDisposed()
img.ReplacePixels(make([]byte, 4*s*s), nil) img1.ReplacePixels(make([]byte, 4*s*s), nil)
} }
// Issue #1217 (disabled) // Issue #1217 (disabled)