github.com/cybriq/giocore@v0.0.7-0.20210703034601-cfb9cb5f3900/gpu/pack.go (about) 1 // SPDX-License-Identifier: Unlicense OR MIT 2 3 package gpu 4 5 import ( 6 "image" 7 ) 8 9 // packer packs a set of many smaller rectangles into 10 // much fewer larger atlases. 11 type packer struct { 12 maxDim int 13 spaces []image.Rectangle 14 15 sizes []image.Point 16 pos image.Point 17 } 18 19 type placement struct { 20 Idx int 21 Pos image.Point 22 } 23 24 // add adds the given rectangle to the atlases and 25 // return the allocated position. 26 func (p *packer) add(s image.Point) (placement, bool) { 27 if place, ok := p.tryAdd(s); ok { 28 return place, true 29 } 30 p.newPage() 31 return p.tryAdd(s) 32 } 33 34 func (p *packer) clear() { 35 p.sizes = p.sizes[:0] 36 p.spaces = p.spaces[:0] 37 } 38 39 func (p *packer) newPage() { 40 p.pos = image.Point{} 41 p.sizes = append(p.sizes, image.Point{}) 42 p.spaces = p.spaces[:0] 43 p.spaces = append(p.spaces, image.Rectangle{ 44 Max: image.Point{X: p.maxDim, Y: p.maxDim}, 45 }) 46 } 47 48 func (p *packer) tryAdd(s image.Point) (placement, bool) { 49 // Go backwards to prioritize smaller spaces first. 50 for i := len(p.spaces) - 1; i >= 0; i-- { 51 space := p.spaces[i] 52 rightSpace := space.Dx() - s.X 53 bottomSpace := space.Dy() - s.Y 54 if rightSpace >= 0 && bottomSpace >= 0 { 55 // Remove space. 56 p.spaces[i] = p.spaces[len(p.spaces)-1] 57 p.spaces = p.spaces[:len(p.spaces)-1] 58 // Put s in the top left corner and add the (at most) 59 // two smaller spaces. 60 pos := space.Min 61 if bottomSpace > 0 { 62 p.spaces = append(p.spaces, image.Rectangle{ 63 Min: image.Point{X: pos.X, Y: pos.Y + s.Y}, 64 Max: image.Point{X: space.Max.X, Y: space.Max.Y}, 65 }) 66 } 67 if rightSpace > 0 { 68 p.spaces = append(p.spaces, image.Rectangle{ 69 Min: image.Point{X: pos.X + s.X, Y: pos.Y}, 70 Max: image.Point{X: space.Max.X, Y: pos.Y + s.Y}, 71 }) 72 } 73 idx := len(p.sizes) - 1 74 size := &p.sizes[idx] 75 if x := pos.X + s.X; x > size.X { 76 size.X = x 77 } 78 if y := pos.Y + s.Y; y > size.Y { 79 size.Y = y 80 } 81 return placement{Idx: idx, Pos: pos}, true 82 } 83 } 84 return placement{}, false 85 }