diff --git a/internal/atlas/image.go b/internal/atlas/image.go index aedb424da..35b3e0c61 100644 --- a/internal/atlas/image.go +++ b/internal/atlas/image.go @@ -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) { diff --git a/internal/packing/packing.go b/internal/packing/packing.go index a2cc6210d..cbd55774c 100644 --- a/internal/packing/packing.go +++ b/internal/packing/packing.go @@ -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 } } } diff --git a/internal/packing/packing_test.go b/internal/packing/packing_test.go index c233cfebf..f47e8ae4a 100644 --- a/internal/packing/packing_test.go +++ b/internal/packing/packing_test.go @@ -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