2019-04-10 00:35:44 +00:00
|
|
|
package sdl
|
|
|
|
|
|
|
|
import (
|
2019-06-27 19:46:35 +00:00
|
|
|
"bytes"
|
2019-04-10 00:35:44 +00:00
|
|
|
"fmt"
|
2019-06-27 18:57:26 +00:00
|
|
|
"image"
|
2019-04-10 00:35:44 +00:00
|
|
|
|
2019-12-22 23:53:52 +00:00
|
|
|
"git.kirsle.net/go/render"
|
2019-04-10 00:35:44 +00:00
|
|
|
"github.com/veandco/go-sdl2/sdl"
|
2019-06-27 18:57:26 +00:00
|
|
|
"golang.org/x/image/bmp"
|
2019-04-10 00:35:44 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Copy a texture into the renderer.
|
|
|
|
func (r *Renderer) Copy(t render.Texturer, src, dst render.Rect) {
|
|
|
|
if tex, ok := t.(*Texture); ok {
|
|
|
|
var (
|
|
|
|
a = RectToSDL(src)
|
|
|
|
b = RectToSDL(dst)
|
|
|
|
)
|
|
|
|
r.renderer.Copy(tex.tex, &a, &b)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Texture can hold on to SDL textures for caching and optimization.
|
|
|
|
type Texture struct {
|
2022-04-09 21:25:56 +00:00
|
|
|
render *Renderer // backref to free them up thoroughly
|
2019-04-10 00:35:44 +00:00
|
|
|
tex *sdl.Texture
|
2021-06-14 02:59:54 +00:00
|
|
|
image image.Image
|
2019-04-10 00:35:44 +00:00
|
|
|
width int32
|
|
|
|
height int32
|
|
|
|
}
|
|
|
|
|
2019-06-27 20:01:01 +00:00
|
|
|
// StoreTexture caches an SDL texture from a bitmap.
|
|
|
|
func (r *Renderer) StoreTexture(name string, img image.Image) (render.Texturer, error) {
|
2019-06-27 19:46:35 +00:00
|
|
|
var (
|
|
|
|
fh = bytes.NewBuffer([]byte{})
|
|
|
|
)
|
|
|
|
|
|
|
|
err := bmp.Encode(fh, img)
|
2019-06-27 18:57:26 +00:00
|
|
|
if err != nil {
|
2019-06-27 19:46:35 +00:00
|
|
|
return nil, fmt.Errorf("NewTexture: bmp.Encode: %s", err)
|
2019-06-27 18:57:26 +00:00
|
|
|
}
|
|
|
|
|
2019-06-27 19:46:35 +00:00
|
|
|
// Create an SDL RWOps from the bitmap data in memory.
|
|
|
|
sdlRW, err := sdl.RWFromMem(fh.Bytes())
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("NewTexture: sdl.RWFromMem: %s", err)
|
|
|
|
}
|
2019-04-10 00:35:44 +00:00
|
|
|
|
2019-06-27 19:46:35 +00:00
|
|
|
surface, err := sdl.LoadBMPRW(sdlRW, true)
|
2019-04-10 00:35:44 +00:00
|
|
|
if err != nil {
|
2019-06-27 19:46:35 +00:00
|
|
|
return nil, fmt.Errorf("NewTexture: sdl.LoadBMPRW: %s", err)
|
2019-04-10 00:35:44 +00:00
|
|
|
}
|
|
|
|
defer surface.Free()
|
|
|
|
|
|
|
|
// TODO: chroma key color hardcoded to white here
|
|
|
|
key := sdl.MapRGB(surface.Format, 255, 255, 255)
|
|
|
|
surface.SetColorKey(true, key)
|
|
|
|
|
2019-06-27 19:46:35 +00:00
|
|
|
texture, err := r.renderer.CreateTextureFromSurface(surface)
|
2019-04-10 00:35:44 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("NewBitmap: create texture: %s", err)
|
|
|
|
}
|
|
|
|
|
2019-06-27 19:46:35 +00:00
|
|
|
tex := &Texture{
|
2022-04-09 21:25:56 +00:00
|
|
|
render: r,
|
2019-04-10 00:35:44 +00:00
|
|
|
width: surface.W,
|
|
|
|
height: surface.H,
|
2019-06-27 19:46:35 +00:00
|
|
|
tex: texture,
|
2021-06-14 02:59:54 +00:00
|
|
|
image: img,
|
2019-06-27 19:46:35 +00:00
|
|
|
}
|
2022-04-10 19:27:20 +00:00
|
|
|
|
|
|
|
r.textureMu.Lock()
|
2019-06-27 20:01:01 +00:00
|
|
|
r.textures[name] = tex
|
2022-04-10 19:27:20 +00:00
|
|
|
r.textureMu.Unlock()
|
2019-06-27 19:46:35 +00:00
|
|
|
|
|
|
|
return tex, nil
|
|
|
|
}
|
|
|
|
|
2022-04-09 21:25:56 +00:00
|
|
|
// CountTextures is a custom function for the SDL2 Engine only that returns the
|
|
|
|
// size of the engine texture cache.
|
2022-04-10 19:27:20 +00:00
|
|
|
func (r *Renderer) CountTextures() int {
|
|
|
|
r.textureMu.RLock()
|
|
|
|
defer r.textureMu.RUnlock()
|
2022-04-09 21:25:56 +00:00
|
|
|
|
2022-04-10 19:27:20 +00:00
|
|
|
return len(r.textures)
|
2022-04-09 21:25:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ListTextures is a custom function to peek into the SDL2 texture cache names.
|
|
|
|
func (r *Renderer) ListTextures() []string {
|
2022-04-10 19:27:20 +00:00
|
|
|
r.textureMu.RLock()
|
|
|
|
defer r.textureMu.RUnlock()
|
|
|
|
|
2022-04-09 21:25:56 +00:00
|
|
|
var keys = []string{}
|
|
|
|
for key := range r.textures {
|
|
|
|
keys = append(keys, key)
|
|
|
|
}
|
|
|
|
return keys
|
|
|
|
}
|
|
|
|
|
2019-06-27 19:46:35 +00:00
|
|
|
// Size returns the dimensions of the texture.
|
|
|
|
func (t *Texture) Size() render.Rect {
|
2019-12-28 01:35:42 +00:00
|
|
|
return render.NewRect(int(t.width), int(t.height))
|
2019-06-27 19:46:35 +00:00
|
|
|
}
|
|
|
|
|
2021-06-14 02:59:54 +00:00
|
|
|
// Image returns the underlying Go image.Image.
|
|
|
|
func (t *Texture) Image() image.Image {
|
|
|
|
return t.image
|
|
|
|
}
|
|
|
|
|
2022-04-09 21:25:56 +00:00
|
|
|
// Free the SDL2 texture object.
|
|
|
|
func (t *Texture) Free() error {
|
2022-04-10 19:27:20 +00:00
|
|
|
t.render.textureMu.Lock()
|
|
|
|
defer t.render.textureMu.Unlock()
|
|
|
|
|
2022-04-09 21:25:56 +00:00
|
|
|
var err error
|
|
|
|
|
|
|
|
if t.tex != nil {
|
|
|
|
err = t.tex.Destroy()
|
|
|
|
t.tex = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Free up the cached texture too to garbage collect the image.Image cache etc.
|
|
|
|
for name, tex := range t.render.textures {
|
|
|
|
if tex == t {
|
|
|
|
delete(t.render.textures, name)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-06-27 20:01:01 +00:00
|
|
|
// LoadTexture initializes a texture from a bitmap image.
|
|
|
|
func (r *Renderer) LoadTexture(name string) (render.Texturer, error) {
|
2022-04-10 19:27:20 +00:00
|
|
|
r.textureMu.RLock()
|
|
|
|
defer r.textureMu.RUnlock()
|
|
|
|
|
2019-06-27 20:01:01 +00:00
|
|
|
if tex, ok := r.textures[name]; ok {
|
2022-04-09 21:25:56 +00:00
|
|
|
// If the SDL2 texture had been freed, recreate it.
|
|
|
|
if tex.tex == nil {
|
|
|
|
return r.StoreTexture(name, tex.image)
|
|
|
|
}
|
2019-06-27 19:46:35 +00:00
|
|
|
return tex, nil
|
|
|
|
}
|
2019-06-27 20:01:01 +00:00
|
|
|
return nil, fmt.Errorf("LoadTexture(%s): not found in texture cache", name)
|
2019-04-10 00:35:44 +00:00
|
|
|
}
|
2022-04-09 21:25:56 +00:00
|
|
|
|
|
|
|
// FreeTextures flushes the internal cache of SDL2 textures and frees their memory.
|
|
|
|
func (r *Renderer) FreeTextures() int {
|
2022-04-10 19:27:20 +00:00
|
|
|
r.textureMu.Lock()
|
|
|
|
defer r.textureMu.Unlock()
|
|
|
|
|
2022-04-09 21:25:56 +00:00
|
|
|
var num = len(r.textures)
|
|
|
|
for name, tex := range r.textures {
|
|
|
|
delete(r.textures, name)
|
|
|
|
tex.tex.Destroy()
|
|
|
|
}
|
|
|
|
return num
|
|
|
|
}
|