Skip to content

Commit

Permalink
Make atlas.Pack reenterant. Add some utility functions to atlas.Textu… (
Browse files Browse the repository at this point in the history
#109)

* Make atlas.Pack reenterant. Add some utility functions to atlas.TextureId

* Fix broken test

* Add documentation
  • Loading branch information
dusk125 authored Aug 20, 2024
1 parent 8c491eb commit dda5970
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 33 deletions.
32 changes: 31 additions & 1 deletion ext/atlas/atlas.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,37 @@ func (a *Atlas) SliceEmbed(fs embed.FS, path string, cellSize pixel.Vec) (id Sli
// to the atlas can be used.
func (a *Atlas) Pack() {
// If there's nothing to do, don't do anything
if a.clean || len(a.adding) == 0 {
if a.clean {
return
}

// If we've already packed the textures, we need to convert them back to images to repack them
if a.internal != nil && len(a.internal) > 0 {
images := make([]*image.RGBA, len(a.internal))
for i, data := range a.internal {
images[i] = data.Image()
}

for id, loc := range a.idMap {
bounds := image.Rect(0, 0, loc.rect.Dx(), loc.rect.Dy())
rgba := image.NewRGBA(bounds)
i := images[loc.index]

for y := 0; y < bounds.Dy(); y++ {
for x := 0; x < bounds.Dx(); x++ {
rgba.Set(x, y, i.At(loc.rect.Min.X+x, loc.rect.Min.Y+y))
}
}

entry := imageEntry{data: rgba}
entry.id = id
entry.bounds = bounds

a.adding = append(a.adding, entry)
}
}

if len(a.adding) == 0 {
return
}

Expand Down
23 changes: 0 additions & 23 deletions ext/atlas/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,29 +48,6 @@ func (a *Atlas) Clear(groups ...Group) {
}
}

images := make([]*image.RGBA, len(a.internal))
for i, data := range a.internal {
images[i] = data.Image()
}

for id, loc := range a.idMap {
bounds := image.Rect(0, 0, loc.rect.Dx(), loc.rect.Dy())
rgba := image.NewRGBA(bounds)
i := images[loc.index]

for y := 0; y < bounds.Dy(); y++ {
for x := 0; x < bounds.Dx(); x++ {
rgba.Set(x, y, i.At(loc.rect.Min.X+x, loc.rect.Min.Y+y))
}
}

entry := imageEntry{data: rgba}
entry.id = id
entry.bounds = bounds

a.adding = append(a.adding, entry)
}

a.clean = false

a.Pack()
Expand Down
2 changes: 1 addition & 1 deletion ext/atlas/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func pixelRect[T constraints.Integer | constraints.Float](minX, minY, maxX, maxY
}

func image2PixelRect(r image.Rectangle) pixel.Rect {
return pixel.R(float64(r.Min.X), float64(r.Min.Y), float64(r.Max.X), float64(r.Max.Y))
return pixelRect(r.Min.X, r.Min.Y, r.Max.X, r.Max.Y)
}

func loadEmbedSprite(fs embed.FS, file string) (i image.Image, err error) {
Expand Down
46 changes: 38 additions & 8 deletions ext/atlas/texture.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,60 @@ import (
"github.com/gopxl/pixel/v2"
)

// Get returns a texture with the given ID.
func (a *Atlas) Get(id uint32) TextureId {
return TextureId{
id: id,
atlas: a,
}
}

// TextureId is a reference to a texture in an atlas.
type TextureId struct {
id uint32
atlas *Atlas
sprite *pixel.Sprite
}

// ID returns the ID of the texture in the atlas.
func (t TextureId) ID() uint32 {
return t.id
}

// Frame returns the frame of the texture in the atlas.
func (t TextureId) Frame() pixel.Rect {
if !t.atlas.clean {
panic("Atlas is dirty, call atlas.Pack() first")
}
s, has := t.atlas.idMap[t.id]
if !has {
panic(fmt.Sprintf("id: %v does not exist in atlas", t.id))
}
r := image2PixelRect(s.rect)
c := t.atlas.internal[s.index].Bounds().Center()
m := pixel.IM.ScaledXY(c, pixel.V(1, -1))
r.Min = m.Project(r.Min)
r.Max = m.Project(r.Max)
return r
}

// Bounds returns the bounds of the texture in the atlas.
func (t TextureId) Bounds() pixel.Rect {
if !t.atlas.clean {
panic("Atlas is dirty, call atlas.Pack() first")
}

s, has := t.atlas.idMap[t.id]
if !has {
panic(fmt.Sprintf("id: %v does not exit in atlas", t.id))
panic(fmt.Sprintf("id: %v does not exist in atlas", t.id))
}
return pixelRect(0, 0, s.rect.Dx(), s.rect.Dy())
}

// Draw draws the texture in the atlas to the target with the given matrix.
func (t *TextureId) Draw(target pixel.Target, m pixel.Matrix) {
if !t.atlas.clean {
panic("Packer is dirty, call atlas.Pack() first")
panic("Atlas is dirty, call atlas.Pack() first")
}

l, has := t.atlas.idMap[t.id]
Expand All @@ -34,12 +68,8 @@ func (t *TextureId) Draw(target pixel.Target, m pixel.Matrix) {
}

if t.sprite == nil {
r := image2PixelRect(l.rect)
c := t.atlas.internal[l.index].Bounds().Center()
m := pixel.IM.ScaledXY(c, pixel.V(1, -1))
r.Min = m.Project(r.Min)
r.Max = m.Project(r.Max)
t.sprite = pixel.NewSprite(t.atlas.internal[l.index], r)
frame := t.Frame()
t.sprite = pixel.NewSprite(t.atlas.internal[l.index], frame)
}
t.sprite.Draw(target, m)
}

0 comments on commit dda5970

Please sign in to comment.