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 }