2021-07-30 17:26:23 +10:00
|
|
|
package engine
|
|
|
|
|
2021-07-31 16:38:54 +10:00
|
|
|
import (
|
2021-08-23 20:28:49 +10:00
|
|
|
"compress/gzip"
|
2021-08-20 13:45:01 +10:00
|
|
|
"encoding/gob"
|
2021-07-31 16:38:54 +10:00
|
|
|
"image"
|
2021-08-23 10:09:49 +10:00
|
|
|
"io/fs"
|
2021-08-25 15:04:38 +10:00
|
|
|
"os"
|
2021-07-31 16:38:54 +10:00
|
|
|
|
|
|
|
"github.com/hajimehoshi/ebiten/v2"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2021-08-23 11:00:51 +10:00
|
|
|
imageCache = make(map[assetKey]*ebiten.Image)
|
2021-08-20 15:01:31 +10:00
|
|
|
|
2021-08-23 20:28:49 +10:00
|
|
|
// Ensure types satisfy interfaces.
|
2021-08-20 17:05:47 +10:00
|
|
|
_ Loader = &ImageRef{}
|
2021-07-31 16:38:54 +10:00
|
|
|
)
|
2021-07-30 17:26:23 +10:00
|
|
|
|
2021-08-20 16:23:10 +10:00
|
|
|
func init() {
|
|
|
|
gob.Register(ImageRef{})
|
|
|
|
}
|
|
|
|
|
2021-08-23 11:00:51 +10:00
|
|
|
type assetKey struct {
|
|
|
|
assets fs.FS
|
|
|
|
path string
|
|
|
|
}
|
|
|
|
|
2021-08-26 11:31:39 +10:00
|
|
|
func LoadGobz(dst interface{}, assets fs.FS, path string) error {
|
2021-08-23 20:28:49 +10:00
|
|
|
f, err := assets.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2021-07-31 17:17:26 +10:00
|
|
|
}
|
2021-08-23 20:28:49 +10:00
|
|
|
defer f.Close()
|
|
|
|
gz, err := gzip.NewReader(f)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2021-07-31 19:49:24 +10:00
|
|
|
}
|
2021-08-23 20:28:49 +10:00
|
|
|
return gob.NewDecoder(gz).Decode(dst)
|
2021-07-31 17:17:26 +10:00
|
|
|
}
|
|
|
|
|
2021-08-26 11:31:39 +10:00
|
|
|
// SaveGobz takes an object, gob-encodes it, gzips it, and writes to disk.
|
2021-08-25 16:54:22 +10:00
|
|
|
// This requires running on something with a disk to write to (not JS)
|
2021-08-26 11:31:39 +10:00
|
|
|
func SaveGobz(src interface{}, name string) error {
|
2021-08-25 15:04:38 +10:00
|
|
|
f, err := os.CreateTemp(".", name)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer os.Remove(f.Name())
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
gz := gzip.NewWriter(f)
|
|
|
|
if err := gob.NewEncoder(gz).Encode(src); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := gz.Close(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := f.Close(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return os.Rename(f.Name(), name)
|
|
|
|
}
|
|
|
|
|
2021-07-31 17:15:32 +10:00
|
|
|
// ImageRef loads images from the AssetFS into *ebiten.Image form.
|
|
|
|
// It is your responsibility to import _ "image/..." for whatever
|
2021-08-20 17:05:47 +10:00
|
|
|
// format the files are in, and to load it (either return it as a
|
|
|
|
// subcomponent from Scan so that Game will Load it, or call Load
|
|
|
|
// yourself).
|
2021-07-30 17:26:23 +10:00
|
|
|
type ImageRef struct {
|
|
|
|
Path string
|
|
|
|
|
|
|
|
image *ebiten.Image
|
|
|
|
}
|
|
|
|
|
2021-08-20 17:05:47 +10:00
|
|
|
// Image returns the image, or nil if not loaded.
|
2021-07-31 17:15:32 +10:00
|
|
|
// Multiple distinct ImageRefs can use the same path.
|
2021-07-31 20:06:49 +10:00
|
|
|
func (r *ImageRef) Image() *ebiten.Image {
|
2021-08-20 17:05:47 +10:00
|
|
|
return r.image
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load loads the image. Load is required before Image returns.
|
|
|
|
// Loading the same path multiple times uses a cache to return
|
|
|
|
// the same image.
|
2021-08-23 10:09:49 +10:00
|
|
|
func (r *ImageRef) Load(assets fs.FS) error {
|
2021-08-20 17:05:47 +10:00
|
|
|
// Fast path load from cache
|
2021-08-23 11:00:51 +10:00
|
|
|
r.image = imageCache[assetKey{assets, r.Path}]
|
2021-07-31 16:38:54 +10:00
|
|
|
if r.image != nil {
|
2021-08-20 17:05:47 +10:00
|
|
|
return nil
|
2021-07-31 16:38:54 +10:00
|
|
|
}
|
2021-08-20 17:05:47 +10:00
|
|
|
// Slow path
|
2021-08-23 10:09:49 +10:00
|
|
|
f, err := assets.Open(r.Path)
|
2021-07-31 16:38:54 +10:00
|
|
|
if err != nil {
|
2021-08-20 17:05:47 +10:00
|
|
|
return err
|
2021-07-31 16:38:54 +10:00
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
i, _, err := image.Decode(f)
|
|
|
|
if err != nil {
|
2021-08-20 17:05:47 +10:00
|
|
|
return err
|
2021-07-30 17:26:23 +10:00
|
|
|
}
|
2021-07-31 16:38:54 +10:00
|
|
|
r.image = ebiten.NewImageFromImage(i)
|
2021-08-23 11:00:51 +10:00
|
|
|
imageCache[assetKey{assets, r.Path}] = r.image
|
2021-08-20 15:52:01 +10:00
|
|
|
return nil
|
2021-08-20 15:01:31 +10:00
|
|
|
}
|