render/sdl/texture.go

179 lines
3.9 KiB
Go

package sdl
import (
"bytes"
"fmt"
"image"
"git.kirsle.net/go/render"
"github.com/veandco/go-sdl2/sdl"
"golang.org/x/image/bmp"
)
// 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)
texture, err = tex.getTexture()
)
if err == nil {
r.renderer.Copy(texture, &a, &b)
} else {
fmt.Printf("Renderer.Copy: texture %s could not be created: %s\n", tex.name, err)
}
}
}
// Texture can hold on to SDL textures for caching and optimization.
type Texture struct {
name string
render *Renderer // backref to free them up thoroughly
tex *sdl.Texture
image image.Image
width int32
height int32
}
// Render the SDL2 Texture from the base Image if the texture is not ready.
func (t *Texture) getTexture() (*sdl.Texture, error) {
if t.tex != nil {
return t.tex, nil
}
var (
fh = bytes.NewBuffer([]byte{})
err = bmp.Encode(fh, t.image)
)
if err != nil {
return nil, fmt.Errorf("NewTexture: bmp.Encode: %s", err)
}
// 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)
}
surface, err := sdl.LoadBMPRW(sdlRW, true)
if err != nil {
return nil, fmt.Errorf("NewTexture: sdl.LoadBMPRW: %s", err)
}
defer surface.Free()
// TODO: chroma key color hardcoded to white here
key := sdl.MapRGB(surface.Format, 255, 255, 255)
surface.SetColorKey(true, key)
texture, err := t.render.renderer.CreateTextureFromSurface(surface)
if err != nil {
return nil, fmt.Errorf("NewBitmap: create texture: %s", err)
}
t.tex = texture
t.width = surface.W
t.height = surface.H
return t.tex, nil
}
// StoreTexture caches an SDL texture from a bitmap.
func (r *Renderer) StoreTexture(name string, img image.Image) (render.Texturer, error) {
tex := &Texture{
name: name,
render: r,
image: img,
}
_, err := tex.getTexture()
if err != nil {
return tex, err
}
r.textureMu.Lock()
r.textures[name] = tex
r.textureMu.Unlock()
return tex, nil
}
// CountTextures is a custom function for the SDL2 Engine only that returns the
// size of the engine texture cache.
func (r *Renderer) CountTextures() int {
r.textureMu.RLock()
defer r.textureMu.RUnlock()
return len(r.textures)
}
// ListTextures is a custom function to peek into the SDL2 texture cache names.
func (r *Renderer) ListTextures() []string {
r.textureMu.RLock()
defer r.textureMu.RUnlock()
var keys = []string{}
for key := range r.textures {
keys = append(keys, key)
}
return keys
}
// Size returns the dimensions of the texture.
func (t *Texture) Size() render.Rect {
return render.NewRect(int(t.width), int(t.height))
}
// Image returns the underlying Go image.Image.
func (t *Texture) Image() image.Image {
return t.image
}
// Free the SDL2 texture object.
func (t *Texture) Free() error {
t.render.textureMu.Lock()
defer t.render.textureMu.Unlock()
var err error
if t.tex != nil {
err = t.tex.Destroy()
t.tex = nil
}
return err
}
// LoadTexture initializes a texture from a bitmap image.
func (r *Renderer) LoadTexture(name string) (render.Texturer, error) {
r.textureMu.RLock()
defer r.textureMu.RUnlock()
if tex, ok := r.textures[name]; ok {
// If the SDL2 texture had been freed, recreate it.
if tex.tex == nil {
_, err := tex.getTexture()
if err != nil {
return tex, err
}
}
return tex, nil
}
return nil, fmt.Errorf("LoadTexture(%s): not found in texture cache", name)
}
// FreeTextures flushes the internal cache of SDL2 textures and frees their memory.
func (r *Renderer) FreeTextures() int {
r.textureMu.Lock()
defer r.textureMu.Unlock()
var num = len(r.textures)
for name, tex := range r.textures {
if tex.tex != nil {
tex.tex.Destroy()
tex.tex = nil
}
delete(r.textures, name)
}
return num
}