-
Notifications
You must be signed in to change notification settings - Fork 0
/
packer_test.go
177 lines (150 loc) · 4.55 KB
/
packer_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
package rectpack
import (
"fmt"
"image"
"image/color"
"image/draw"
"image/png"
"log"
"math/rand"
"os"
"path/filepath"
"testing"
)
func createAtlas(p *Packer, paths []string) (*image.RGBA, map[string]Rect, error) {
// Reset the packer to its initial state
p.Clear()
// Enumerate each given path and decode its header to retrieve the size
for i, path := range paths {
var cfg image.Config
if file, err := os.Open(path); err != nil {
return nil, nil, err
} else {
cfg, _, err = image.DecodeConfig(file)
file.Close()
if err != nil {
return nil, nil, err
}
}
// Insert the size into the packer, using the index as the ID
if !p.InsertSize(i, cfg.Width, cfg.Height) && p.Online {
// If packing in online mode, ensure each image fits as it is inserted.
size := p.Size()
return nil, nil, fmt.Errorf("cannot fit all images into size of %s", size.String())
}
}
// If packing in offline mode, perform tha packing and ensure all images were packed.
if !p.Online && !p.Pack() {
size := p.Size()
return nil, nil, fmt.Errorf("cannot fit all images into size of %s", size.String())
}
// Get the final required size of the atlas (includes any configured padding), and create
// a new image of that size to draw onto.
size := p.Size()
mapping := make(map[string]Rect, len(paths))
dst := image.NewRGBA(image.Rect(0, 0, size.Width, size.Height))
var zero image.Point
// Iterate through each packed rectangle.
for _, rect := range p.Rects() {
// The ID of the rectangle is the index of the path (assigned above).
path := paths[rect.ID]
file, err := os.Open(path)
if err != nil {
return nil, nil, err
}
// Decode the image at that path
src, _, err := image.Decode(file)
file.Close()
if err != nil {
return nil, nil, err
}
// Draw the image onto the destination at the rectangles location.
bounds := image.Rect(rect.X, rect.Y, rect.Right(), rect.Bottom())
draw.Draw(dst, bounds, src, zero, draw.Src)
// Map the path to the rectangle where the image is drawn at.
mapping[path] = rect
}
// Return the results
return dst, mapping, nil
}
// randomSize returns a size within the given minimum and maximum sizes.
func randomSize(id int, minSize, maxSize Size) Size {
w := rand.Intn(maxSize.Width-minSize.Width) + minSize.Width
h := rand.Intn(maxSize.Height-minSize.Height) + minSize.Height
return NewSizeID(id, w, h)
}
// randomColor (surprise!) returns a random color.
func randomColor() color.RGBA {
// Offset to use a minimum value so it is never pure black.
return color.RGBA{
R: uint8(rand.Intn(240)) + 15,
G: uint8(rand.Intn(240)) + 15,
B: uint8(rand.Intn(240)) + 15,
A: 255,
}
}
// createImage colorizes and creates an image from packed rectangles to provide
// a visual representation.
func createImage(t *testing.T, path string, packer *Packer) {
black := color.RGBA{0, 0, 0, 255}
size := packer.Size()
img := image.NewRGBA(image.Rect(0, 0, size.Width, size.Height))
draw.Draw(img, img.Bounds(), &image.Uniform{black}, image.Point{}, draw.Src)
for _, rect := range packer.Rects() {
color := randomColor()
r := image.Rect(rect.X, rect.Y, rect.Right(), rect.Bottom())
draw.Draw(img, r, &image.Uniform{color}, image.Point{0, 0}, draw.Src)
}
if file, err := os.Create(path); err == nil {
defer file.Close()
png.Encode(file, img)
} else {
t.Fatal(err)
}
}
func TestAtlas(t *testing.T) {
return
paths, _ := filepath.Glob("/usr/share/icons/Adwaita/32x32/devices/*.png")
packer, _ := NewPacker(512, 512, MaxRectsBAF)
img, mapping, err := createAtlas(packer, paths)
if err != nil {
fmt.Println(err)
log.Fatal(err)
}
for k, v := range mapping {
fmt.Printf("%v: %s\n", k, v.String())
}
file, _ := os.Create("atlas.png")
defer file.Close()
png.Encode(file, img)
}
func TestRandom(t *testing.T) {
const count = 1024
minSize := NewSize(32, 32)
maxSize := NewSize(96, 96)
packer, _ := NewPacker(1024, 8192, MaxRectsBSSF)
packer.AllowFlip(true)
packer.Online = false
packer.Padding = 2
packer.Sorter(SortArea, false)
sizes := make([]Size, count)
for i := 0; i < count; i++ {
sizes[i] = randomSize(i, minSize, maxSize)
}
packer.Insert(sizes...)
if !packer.Pack() {
t.Fatal("cannot fit all rectangles in the given dimensions")
}
packer.Pack()
// Compare every rectangle to every other and test for intersection
rects := packer.Rects()
for i := 0; i < len(rects)-1; i++ {
for j := i + 1; j < len(rects); j++ {
if rects[i].Intersects(rects[j]) {
t.Errorf("%s and %s intersect\n", rects[i].String(), rects[j].String())
}
}
}
createImage(t, "packed.png", packer)
}
// vim: ts=4