mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-24 02:38:53 +01:00
internal/packing: refactoring: use image.Rectangle
This commit is contained in:
parent
6c5477adea
commit
82c7436be5
@ -375,7 +375,9 @@ func (i *Image) regionWithPadding() (x, y, width, height int) {
|
||||
if !i.isOnAtlas() {
|
||||
return 0, 0, i.width + i.paddingSize(), i.height + i.paddingSize()
|
||||
}
|
||||
return i.node.Region()
|
||||
// TODO: Use image.Rectangle as it is.
|
||||
r := i.node.Region()
|
||||
return r.Min.X, r.Min.Y, r.Dx(), r.Dy()
|
||||
}
|
||||
|
||||
func (i *Image) processSrc(src *Image) {
|
||||
|
@ -18,6 +18,7 @@ package packing
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
)
|
||||
|
||||
type Page struct {
|
||||
@ -65,10 +66,7 @@ func (p *Page) IsEmpty() bool {
|
||||
}
|
||||
|
||||
type Node struct {
|
||||
x int
|
||||
y int
|
||||
width int
|
||||
height int
|
||||
region image.Rectangle
|
||||
used bool
|
||||
|
||||
parent *Node
|
||||
@ -86,8 +84,8 @@ func (n *Node) canFree() bool {
|
||||
return n.child0.canFree() && n.child1.canFree()
|
||||
}
|
||||
|
||||
func (n *Node) Region() (x, y, width, height int) {
|
||||
return n.x, n.y, n.width, n.height
|
||||
func (n *Node) Region() image.Rectangle {
|
||||
return n.region
|
||||
}
|
||||
|
||||
// square returns a float value indicating how much the given rectangle is close to a square.
|
||||
@ -104,59 +102,47 @@ func square(width, height int) float64 {
|
||||
}
|
||||
|
||||
func alloc(n *Node, width, height int) *Node {
|
||||
if n.width < width || n.height < height {
|
||||
if n.region.Dx() < width || n.region.Dy() < height {
|
||||
return nil
|
||||
}
|
||||
if n.used {
|
||||
return nil
|
||||
}
|
||||
if n.child0 == nil && n.child1 == nil {
|
||||
if n.width == width && n.height == height {
|
||||
if n.region.Dx() == width && n.region.Dy() == height {
|
||||
n.used = true
|
||||
return n
|
||||
}
|
||||
if square(n.width-width, n.height) >= square(n.width, n.height-height) {
|
||||
if square(n.region.Dx()-width, n.region.Dy()) >= square(n.region.Dx(), n.region.Dy()-height) {
|
||||
// Split vertically
|
||||
n.child0 = &Node{
|
||||
x: n.x,
|
||||
y: n.y,
|
||||
width: width,
|
||||
height: n.height,
|
||||
region: image.Rect(n.region.Min.X, n.region.Min.Y, n.region.Min.X+width, n.region.Max.Y),
|
||||
parent: n,
|
||||
}
|
||||
n.child1 = &Node{
|
||||
x: n.x + width,
|
||||
y: n.y,
|
||||
width: n.width - width,
|
||||
height: n.height,
|
||||
region: image.Rect(n.region.Min.X+width, n.region.Min.Y, n.region.Max.X, n.region.Max.Y),
|
||||
parent: n,
|
||||
}
|
||||
} else {
|
||||
// Split horizontally
|
||||
n.child0 = &Node{
|
||||
x: n.x,
|
||||
y: n.y,
|
||||
width: n.width,
|
||||
height: height,
|
||||
region: image.Rect(n.region.Min.X, n.region.Min.Y, n.region.Max.X, n.region.Min.Y+height),
|
||||
parent: n,
|
||||
}
|
||||
n.child1 = &Node{
|
||||
x: n.x,
|
||||
y: n.y + height,
|
||||
width: n.width,
|
||||
height: n.height - height,
|
||||
region: image.Rect(n.region.Min.X, n.region.Min.Y+height, n.region.Max.X, n.region.Max.Y),
|
||||
parent: n,
|
||||
}
|
||||
}
|
||||
// Note: it now MUST fit, due to above preconditions (repeated here).
|
||||
if n.child0.width < width || n.child0.height < height {
|
||||
panic(fmt.Sprintf("packing: the newly created child node (%d, %d) unexpectedly does not contain the requested size (%d, %d)", n.child0.width, n.child0.height, width, height))
|
||||
if n.child0.region.Dx() < width || n.child0.region.Dy() < height {
|
||||
panic(fmt.Sprintf("packing: the newly created child node (%d, %d) unexpectedly does not contain the requested size (%d, %d)", n.child0.region.Dx(), n.child0.region.Dy(), width, height))
|
||||
}
|
||||
// Thus, alloc can't return nil, but it may do another split along the other dimension
|
||||
// to get a node with the exact size (width, height).
|
||||
node := alloc(n.child0, width, height)
|
||||
if node == nil {
|
||||
panic(fmt.Sprintf("packing: could not allocate the requested size (%d, %d) in the newly created child node (%d, %d)", width, height, n.child0.width, n.child0.height))
|
||||
panic(fmt.Sprintf("packing: could not allocate the requested size (%d, %d) in the newly created child node (%d, %d)", width, height, n.child0.region.Dx(), n.child0.region.Dy()))
|
||||
}
|
||||
return node
|
||||
}
|
||||
@ -183,8 +169,7 @@ func (p *Page) Alloc(width, height int) *Node {
|
||||
|
||||
if p.root == nil {
|
||||
p.root = &Node{
|
||||
width: p.width,
|
||||
height: p.height,
|
||||
region: image.Rect(0, 0, p.width, p.height),
|
||||
}
|
||||
}
|
||||
return p.extendForAndAlloc(width, height)
|
||||
@ -273,7 +258,7 @@ func (p *Page) extend(newWidth int, newHeight int) func() {
|
||||
aborted := false
|
||||
if p.root != nil {
|
||||
_ = walk(p.root, func(n *Node) error {
|
||||
if n.x+n.width < p.width && n.y+n.height < p.height {
|
||||
if n.region.Max.X < p.width && n.region.Max.Y < p.height {
|
||||
return nil
|
||||
}
|
||||
if n.used {
|
||||
@ -295,16 +280,10 @@ func (p *Page) extend(newWidth int, newHeight int) func() {
|
||||
if newHeight-p.height > 0 {
|
||||
upper := p.root
|
||||
lower := &Node{
|
||||
x: 0,
|
||||
y: p.height,
|
||||
width: p.width,
|
||||
height: newHeight - p.height,
|
||||
region: image.Rect(0, p.height, p.width, newHeight),
|
||||
}
|
||||
p.root = &Node{
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: p.width,
|
||||
height: newHeight,
|
||||
region: image.Rect(0, 0, p.width, newHeight),
|
||||
child0: upper,
|
||||
child1: lower,
|
||||
}
|
||||
@ -316,16 +295,10 @@ func (p *Page) extend(newWidth int, newHeight int) func() {
|
||||
if newWidth-p.width > 0 {
|
||||
left := p.root
|
||||
right := &Node{
|
||||
x: p.width,
|
||||
y: 0,
|
||||
width: newWidth - p.width,
|
||||
height: newHeight,
|
||||
region: image.Rect(p.width, 0, newWidth, newHeight),
|
||||
}
|
||||
p.root = &Node{
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: newWidth,
|
||||
height: newHeight,
|
||||
region: image.Rect(0, 0, newWidth, newHeight),
|
||||
child0: left,
|
||||
child1: right,
|
||||
}
|
||||
@ -344,28 +317,28 @@ func (p *Page) extend(newWidth int, newHeight int) func() {
|
||||
}
|
||||
} else {
|
||||
origWidth, origHeight := p.width, p.height
|
||||
origWidths := map[*Node]int{}
|
||||
origHeights := map[*Node]int{}
|
||||
origMaxXs := map[*Node]int{}
|
||||
origMaxYs := map[*Node]int{}
|
||||
|
||||
for _, n := range edgeNodes {
|
||||
if n.x+n.width == p.width {
|
||||
origWidths[n] = n.width
|
||||
n.width += newWidth - p.width
|
||||
if n.region.Max.X == p.width {
|
||||
origMaxXs[n] = n.region.Max.X
|
||||
n.region.Max.X = newWidth
|
||||
}
|
||||
if n.y+n.height == p.height {
|
||||
origHeights[n] = n.height
|
||||
n.height += newHeight - p.height
|
||||
if n.region.Max.Y == p.height {
|
||||
origMaxYs[n] = n.region.Max.Y
|
||||
n.region.Max.Y = newHeight
|
||||
}
|
||||
}
|
||||
|
||||
rollback = func() {
|
||||
p.width = origWidth
|
||||
p.height = origHeight
|
||||
for n, w := range origWidths {
|
||||
n.width = w
|
||||
for n, x := range origMaxXs {
|
||||
n.region.Max.X = x
|
||||
}
|
||||
for n, h := range origHeights {
|
||||
n.height = h
|
||||
for n, y := range origMaxYs {
|
||||
n.region.Max.Y = y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,17 +15,16 @@
|
||||
package packing_test
|
||||
|
||||
import (
|
||||
"image"
|
||||
"testing"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/packing"
|
||||
)
|
||||
|
||||
func TestPage(t *testing.T) {
|
||||
type Rect struct {
|
||||
X int
|
||||
Y int
|
||||
Width int
|
||||
Height int
|
||||
rect := func(x0, y0, x1, y1 int) *image.Rectangle {
|
||||
r := image.Rect(x0, y0, x1, y1)
|
||||
return &r
|
||||
}
|
||||
|
||||
type Op struct {
|
||||
@ -37,7 +36,7 @@ func TestPage(t *testing.T) {
|
||||
cases := []struct {
|
||||
Name string
|
||||
In []Op
|
||||
Out []*Rect
|
||||
Out []*image.Rectangle
|
||||
}{
|
||||
{
|
||||
Name: "alloc and random free",
|
||||
@ -56,20 +55,20 @@ func TestPage(t *testing.T) {
|
||||
{0, 0, 4},
|
||||
{1024, 1024, -1},
|
||||
},
|
||||
Out: []*Rect{
|
||||
{0, 0, 100, 100},
|
||||
{0, 100, 100, 100},
|
||||
{0, 200, 100, 100},
|
||||
{0, 300, 100, 100},
|
||||
{0, 400, 100, 100},
|
||||
{0, 500, 100, 100},
|
||||
Out: []*image.Rectangle{
|
||||
rect(0, 0, 100, 100),
|
||||
rect(0, 100, 100, 200),
|
||||
rect(0, 200, 100, 300),
|
||||
rect(0, 300, 100, 400),
|
||||
rect(0, 400, 100, 500),
|
||||
rect(0, 500, 100, 600),
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
{0, 0, 1024, 1024},
|
||||
rect(0, 0, 1024, 1024),
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -88,13 +87,13 @@ func TestPage(t *testing.T) {
|
||||
{0, 0, 4},
|
||||
{0, 0, 5},
|
||||
},
|
||||
Out: []*Rect{
|
||||
{0, 0, 31, 41},
|
||||
{31, 0, 59, 26},
|
||||
{31, 26, 53, 58},
|
||||
{31, 84, 97, 93},
|
||||
{0, 41, 28, 84},
|
||||
{31, 177, 62, 64},
|
||||
Out: []*image.Rectangle{
|
||||
rect(0, 0, 31, 41),
|
||||
rect(31, 0, 31+59, 26),
|
||||
rect(31, 26, 31+53, 26+58),
|
||||
rect(31, 84, 31+97, 84+93),
|
||||
rect(0, 41, 28, 41+84),
|
||||
rect(31, 177, 31+62, 177+64),
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
@ -118,18 +117,18 @@ func TestPage(t *testing.T) {
|
||||
{0, 0, 2},
|
||||
{100, 200, -1},
|
||||
},
|
||||
Out: []*Rect{
|
||||
{0, 0, 100, 200},
|
||||
Out: []*image.Rectangle{
|
||||
rect(0, 0, 100, 200),
|
||||
nil,
|
||||
{0, 200, 100, 200},
|
||||
{0, 400, 50, 50},
|
||||
{100, 0, 200, 200},
|
||||
rect(0, 200, 100, 400),
|
||||
rect(0, 400, 50, 450),
|
||||
rect(100, 0, 300, 200),
|
||||
nil,
|
||||
{100, 200, 500, 500},
|
||||
rect(100, 200, 600, 700),
|
||||
nil,
|
||||
{0, 450, 100, 100},
|
||||
rect(0, 450, 100, 550),
|
||||
nil,
|
||||
{0, 200, 100, 200},
|
||||
rect(0, 200, 100, 400),
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -157,26 +156,26 @@ func TestPage(t *testing.T) {
|
||||
|
||||
{256, 256, -1},
|
||||
},
|
||||
Out: []*Rect{
|
||||
{0, 0, 256, 256},
|
||||
{0, 256, 256, 256},
|
||||
{0, 512, 256, 256},
|
||||
{0, 768, 256, 256},
|
||||
Out: []*image.Rectangle{
|
||||
rect(0, 0, 256, 256),
|
||||
rect(0, 256, 256, 512),
|
||||
rect(0, 512, 256, 768),
|
||||
rect(0, 768, 256, 1024),
|
||||
|
||||
{256, 0, 256, 256},
|
||||
{512, 0, 256, 256},
|
||||
{768, 0, 256, 256},
|
||||
{256, 256, 256, 256},
|
||||
rect(256, 0, 512, 256),
|
||||
rect(512, 0, 768, 256),
|
||||
rect(768, 0, 1024, 256),
|
||||
rect(256, 256, 512, 512),
|
||||
|
||||
{256, 512, 256, 256},
|
||||
{256, 768, 256, 256},
|
||||
{512, 256, 256, 256},
|
||||
{768, 256, 256, 256},
|
||||
rect(256, 512, 512, 768),
|
||||
rect(256, 768, 512, 1024),
|
||||
rect(512, 256, 768, 512),
|
||||
rect(768, 256, 1024, 512),
|
||||
|
||||
{512, 512, 256, 256},
|
||||
{512, 768, 256, 256},
|
||||
{768, 512, 256, 256},
|
||||
{768, 768, 256, 256},
|
||||
rect(512, 512, 768, 768),
|
||||
rect(512, 768, 768, 1024),
|
||||
rect(768, 512, 1024, 768),
|
||||
rect(768, 768, 1024, 1024),
|
||||
|
||||
nil,
|
||||
},
|
||||
@ -195,16 +194,16 @@ func TestPage(t *testing.T) {
|
||||
{300, 300, -1},
|
||||
{300, 300, -1},
|
||||
},
|
||||
Out: []*Rect{
|
||||
{0, 0, 300, 300},
|
||||
{0, 300, 300, 300},
|
||||
{0, 600, 300, 300},
|
||||
{300, 0, 300, 300},
|
||||
{600, 0, 300, 300},
|
||||
{300, 300, 300, 300},
|
||||
{300, 600, 300, 300},
|
||||
{600, 300, 300, 300},
|
||||
{600, 600, 300, 300},
|
||||
Out: []*image.Rectangle{
|
||||
rect(0, 0, 300, 300),
|
||||
rect(0, 300, 300, 600),
|
||||
rect(0, 600, 300, 900),
|
||||
rect(300, 0, 600, 300),
|
||||
rect(600, 0, 900, 300),
|
||||
rect(300, 300, 600, 600),
|
||||
rect(300, 600, 600, 900),
|
||||
rect(600, 300, 900, 600),
|
||||
rect(600, 600, 900, 900),
|
||||
nil,
|
||||
},
|
||||
},
|
||||
@ -239,8 +238,7 @@ func TestPage(t *testing.T) {
|
||||
}
|
||||
continue
|
||||
}
|
||||
x, y, width, height := nodes[i].Region()
|
||||
got := Rect{x, y, width, height}
|
||||
got := nodes[i].Region()
|
||||
if out == nil {
|
||||
t.Errorf("%s: nodes[%d]: got: %v, want: %v", c.Name, i, got, nil)
|
||||
continue
|
||||
|
Loading…
Reference in New Issue
Block a user