Skip to content

Commit cc766e7

Browse files
committed
Extract tileset caching into own loader
this commit also introduces an optional experimental interface to clean unused resource
1 parent fdcfd46 commit cc766e7

File tree

3 files changed

+108
-18
lines changed

3 files changed

+108
-18
lines changed

renderer.go

+5-18
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ type fullRenderer struct {
2222
//NewRenderer lets you draw the map on a custom canvas
2323
//with a default FilesystemLocator
2424
func NewRenderer(m Map, c Canvas) Renderer {
25-
return NewRendererWithResourceLocator(m, c, FilesystemLocator{})
25+
return NewRendererWithResourceLocator(m, c, NewLazyResourceLocator(FilesystemLocator{}))
2626
}
2727

2828
//NewRendererWithResourceLocator return a new renderer
@@ -32,7 +32,7 @@ func NewRendererWithResourceLocator(m Map, c Canvas, locator ResourceLocator) Re
3232

3333
//Render will generate a preview image of the tmx map provided
3434
func (r fullRenderer) Render() error {
35-
canvas := tilemap{subject: r.m, tilesets: map[string]image.Image{}}
35+
canvas := tilemap{subject: r.m}
3636
canvas.renderBackground(r)
3737
err := canvas.renderLayer(r)
3838
if err != nil {
@@ -43,8 +43,7 @@ func (r fullRenderer) Render() error {
4343
}
4444

4545
type tilemap struct {
46-
subject Map
47-
tilesets map[string]image.Image
46+
subject Map
4847
}
4948

5049
type subImager interface {
@@ -57,18 +56,6 @@ func (t tilemap) renderBackground(r fullRenderer) {
5756
}
5857

5958
func (t *tilemap) renderLayer(r fullRenderer) error {
60-
for _, tileset := range t.subject.Tilesets {
61-
path := tileset.Image.Source
62-
63-
tileset, err := r.loader.LocateResource(filepath.Clean(t.subject.filename + path))
64-
65-
if err != nil {
66-
return err
67-
}
68-
69-
t.tilesets[path] = tileset
70-
}
71-
7259
for _, l := range t.subject.Layers {
7360
if !l.IsVisible() {
7461
continue
@@ -89,8 +76,8 @@ func (t *tilemap) renderLayer(r fullRenderer) error {
8976
tx *= t.subject.TileWidth
9077
ty *= t.subject.TileHeight
9178

92-
tilesetgfx, found := t.tilesets[tileset.Image.Source]
93-
if !found {
79+
tilesetgfx, err := r.loader.LocateResource(filepath.Clean(t.subject.filename + tileset.Image.Source))
80+
if err != nil {
9481
panic("invalid tileset path")
9582
}
9683

resource.go

+36
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ type ResourceLocator interface {
1818
LocateResource(filepath string) (image.Image, error)
1919
}
2020

21+
//ResourceManager allows better memory handling
22+
//and cleanup of resources
23+
type ResourceManager interface {
24+
UnsetResource(filepath string)
25+
}
26+
2127
//FilesystemLocator loads files simply from the filesystem
2228
//it supports png, jpeg and non animated gifs
2329
type FilesystemLocator struct {
@@ -33,3 +39,33 @@ func (f FilesystemLocator) LocateResource(filepath string) (image.Image, error)
3339
data, _, err := image.Decode(file)
3440
return data, err
3541
}
42+
43+
type lazyLocator struct {
44+
parent ResourceLocator
45+
tilesets map[string]image.Image
46+
}
47+
48+
func (l *lazyLocator) LocateResource(filepath string) (image.Image, error) {
49+
cached, ok := l.tilesets[filepath]
50+
if ok {
51+
return cached, nil
52+
}
53+
54+
data, err := l.parent.LocateResource(filepath)
55+
if err != nil {
56+
return nil, err
57+
}
58+
59+
l.tilesets[filepath] = data
60+
61+
return data, nil
62+
}
63+
64+
func (l *lazyLocator) UnsetResource(filepath string) {
65+
delete(l.tilesets, filepath)
66+
}
67+
68+
//NewLazyResourceLocator wraps a ResourceLocator and caches results
69+
func NewLazyResourceLocator(l ResourceLocator) ResourceLocator {
70+
return &lazyLocator{parent: l, tilesets: map[string]image.Image{}}
71+
}

resource_test.go

+67
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package tmx_test
22

33
import (
4+
"errors"
45
"image"
6+
"io"
7+
"os"
58

69
. "github.com/manyminds/tmx"
710
. "github.com/onsi/ginkgo"
@@ -15,6 +18,21 @@ func (m nilResourceLoader) LocateResource(filepath string) (image.Image, error)
1518
return nil, nil
1619
}
1720

21+
type moduloErrorResourceLoader struct {
22+
callCount int
23+
}
24+
25+
var errAlreadyLoaded = errors.New("Already loaded")
26+
27+
func (m *moduloErrorResourceLoader) LocateResource(filepath string) (image.Image, error) {
28+
m.callCount++
29+
if m.callCount%2 == 0 {
30+
return nil, errAlreadyLoaded
31+
}
32+
33+
return nil, nil
34+
}
35+
1836
var _ = Describe("Test public api", func() {
1937
Context("check types for interface interface", func() {
2038
It("can be implemented", func() {
@@ -36,5 +54,54 @@ var _ = Describe("Test public api", func() {
3654
_, err := loader.LocateResource("/null")
3755
Expect(err).To(HaveOccurred())
3856
})
57+
58+
It("will find a testfile", func() {
59+
loader := FilesystemLocator{}
60+
_, err := loader.LocateResource("testfiles/chipset.png")
61+
Expect(err).ToNot(HaveOccurred())
62+
})
63+
})
64+
65+
Context("test lazy resource locator", func() {
66+
BeforeEach(func() {
67+
c, err := os.Open("testfiles/chipset.png")
68+
Expect(err).ToNot(HaveOccurred())
69+
t, err := os.Create("testfiles/chipset_copy.png")
70+
Expect(err).ToNot(HaveOccurred())
71+
_, err = io.Copy(t, c)
72+
Expect(err).ToNot(HaveOccurred())
73+
})
74+
75+
AfterEach(func() {
76+
os.Remove("testfiles/chipset_copy.png")
77+
})
78+
79+
It("caches files even after remove", func() {
80+
loader := NewLazyResourceLocator(FilesystemLocator{})
81+
img, err := loader.LocateResource("testfiles/chipset_copy.png")
82+
Expect(err).ToNot(HaveOccurred())
83+
Expect(img).ToNot(BeNil())
84+
err = os.Remove("testfiles/chipset_copy.png")
85+
Expect(err).ToNot(HaveOccurred())
86+
img, err = loader.LocateResource("testfiles/chipset_copy.png")
87+
Expect(err).ToNot(HaveOccurred())
88+
Expect(img).ToNot(BeNil())
89+
90+
manager, ok := loader.(ResourceManager)
91+
Expect(ok).To(BeTrue())
92+
manager.UnsetResource("testfiles/chipset_copy.png")
93+
img, err = loader.LocateResource("testfiles/chipset_copy.png")
94+
Expect(err).To(HaveOccurred())
95+
Expect(img).To(BeNil())
96+
})
97+
98+
It("will only load a specific resource once", func() {
99+
loader := &moduloErrorResourceLoader{}
100+
lazyLoader := NewLazyResourceLocator(loader)
101+
_, err := lazyLoader.LocateResource("lol")
102+
Expect(err).ToNot(HaveOccurred())
103+
_, err = lazyLoader.LocateResource("lol")
104+
Expect(err).ToNot(HaveOccurred())
105+
})
39106
})
40107
})

0 commit comments

Comments
 (0)