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