2019-04-10 00:35:44 +00:00
|
|
|
// Package sdl provides an SDL2 renderer for Doodle.
|
|
|
|
package sdl
|
|
|
|
|
|
|
|
import (
|
2021-12-31 00:39:48 +00:00
|
|
|
"bytes"
|
2019-04-19 05:14:02 +00:00
|
|
|
"fmt"
|
2021-12-31 00:39:48 +00:00
|
|
|
"image"
|
2022-04-10 19:27:20 +00:00
|
|
|
"sync"
|
2019-04-10 00:35:44 +00:00
|
|
|
|
2019-12-22 23:53:52 +00:00
|
|
|
"git.kirsle.net/go/render"
|
|
|
|
"git.kirsle.net/go/render/event"
|
2019-04-10 00:35:44 +00:00
|
|
|
"github.com/veandco/go-sdl2/sdl"
|
|
|
|
"github.com/veandco/go-sdl2/ttf"
|
2021-12-31 00:39:48 +00:00
|
|
|
"golang.org/x/image/bmp"
|
2019-04-10 00:35:44 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Renderer manages the SDL state.
|
|
|
|
type Renderer struct {
|
|
|
|
// Configurable fields.
|
2022-02-20 02:22:55 +00:00
|
|
|
title string
|
|
|
|
width int32
|
|
|
|
height int32
|
2019-04-10 00:35:44 +00:00
|
|
|
|
|
|
|
// Private fields.
|
2022-04-10 19:27:20 +00:00
|
|
|
events *event.State
|
|
|
|
window *sdl.Window
|
|
|
|
renderer *sdl.Renderer
|
|
|
|
textures map[string]*Texture // cached textures
|
|
|
|
textureMu sync.RWMutex
|
2019-04-10 00:35:44 +00:00
|
|
|
|
|
|
|
// Optimizations to minimize SDL calls.
|
|
|
|
lastColor render.Color
|
|
|
|
}
|
|
|
|
|
|
|
|
// New creates the SDL renderer.
|
|
|
|
func New(title string, width, height int) *Renderer {
|
|
|
|
return &Renderer{
|
2019-12-22 22:11:01 +00:00
|
|
|
events: event.NewState(),
|
2019-06-27 19:46:35 +00:00
|
|
|
title: title,
|
|
|
|
width: int32(width),
|
|
|
|
height: int32(height),
|
|
|
|
textures: map[string]*Texture{},
|
2019-04-10 00:35:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Teardown tasks when exiting the program.
|
|
|
|
func (r *Renderer) Teardown() {
|
|
|
|
r.renderer.Destroy()
|
|
|
|
r.window.Destroy()
|
|
|
|
sdl.Quit()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setup the renderer.
|
|
|
|
func (r *Renderer) Setup() error {
|
|
|
|
// Initialize SDL.
|
|
|
|
if err := sdl.Init(sdl.INIT_EVERYTHING); err != nil {
|
2019-04-19 05:14:02 +00:00
|
|
|
return fmt.Errorf("sdl.Init: %s", err)
|
2019-04-10 00:35:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Initialize SDL_TTF.
|
|
|
|
if err := ttf.Init(); err != nil {
|
2019-04-19 05:14:02 +00:00
|
|
|
return fmt.Errorf("ttf.Init: %s", err)
|
2019-04-10 00:35:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Create our window.
|
|
|
|
window, err := sdl.CreateWindow(
|
|
|
|
r.title,
|
|
|
|
sdl.WINDOWPOS_CENTERED,
|
|
|
|
sdl.WINDOWPOS_CENTERED,
|
|
|
|
r.width,
|
|
|
|
r.height,
|
|
|
|
sdl.WINDOW_SHOWN|sdl.WINDOW_RESIZABLE,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
r.window = window
|
|
|
|
|
|
|
|
// Blank out the window in white.
|
|
|
|
renderer, err := sdl.CreateRenderer(window, -1, sdl.RENDERER_ACCELERATED)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
renderer.SetDrawBlendMode(sdl.BLENDMODE_BLEND)
|
|
|
|
r.renderer = renderer
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-12-31 00:39:48 +00:00
|
|
|
// SetWindowIcon sets an icon for the SDL2 window.
|
|
|
|
func (r *Renderer) SetWindowIcon(icon image.Image) error {
|
|
|
|
var (
|
|
|
|
fh = bytes.NewBuffer([]byte{}) // bitmap icon buffer
|
|
|
|
)
|
|
|
|
|
|
|
|
// Encode icon image to bitmap
|
|
|
|
err := bmp.Encode(fh, icon)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create an SDL2 RWOps from the bitmap data in memory.
|
|
|
|
rw, err := sdl.RWFromMem(fh.Bytes())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Surface from RWOps
|
|
|
|
surface, err := sdl.LoadBMPRW(rw, true)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer surface.Free()
|
|
|
|
|
|
|
|
// Set the app window icon.
|
|
|
|
r.window.SetIcon(surface)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-06-25 21:57:11 +00:00
|
|
|
// SetTitle sets the SDL window title.
|
|
|
|
func (r *Renderer) SetTitle(title string) {
|
|
|
|
r.title = title
|
|
|
|
r.window.SetTitle(title)
|
|
|
|
}
|
|
|
|
|
2019-04-10 00:35:44 +00:00
|
|
|
// GetTicks gets SDL's current tick count.
|
|
|
|
func (r *Renderer) GetTicks() uint32 {
|
|
|
|
return sdl.GetTicks()
|
|
|
|
}
|
|
|
|
|
|
|
|
// WindowSize returns the SDL window size.
|
|
|
|
func (r *Renderer) WindowSize() (int, int) {
|
|
|
|
w, h := r.window.GetSize()
|
|
|
|
return int(w), int(h)
|
|
|
|
}
|
|
|
|
|
2020-07-10 02:32:47 +00:00
|
|
|
// Maximize the window.
|
|
|
|
func (r *Renderer) Maximize() {
|
|
|
|
r.window.Maximize()
|
|
|
|
}
|
|
|
|
|
2019-04-10 00:35:44 +00:00
|
|
|
// Present the current frame.
|
|
|
|
func (r *Renderer) Present() error {
|
|
|
|
r.renderer.Present()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delay using sdl.Delay
|
|
|
|
func (r *Renderer) Delay(time uint32) {
|
|
|
|
sdl.Delay(time)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Loop is the main loop.
|
|
|
|
func (r *Renderer) Loop() error {
|
|
|
|
return nil
|
|
|
|
}
|
2022-03-05 23:22:12 +00:00
|
|
|
|
|
|
|
// Private access methods
|
|
|
|
func (r *Renderer) GetSDL2Renderer() *sdl.Renderer {
|
|
|
|
return r.renderer
|
|
|
|
}
|