Refactor Render Texture-Cache Interface

Since SDL2 is using in-memory bitmaps the same as Canvas engine, the
function names of the render.Engine interface have been cleaned up:

* NewTexture(filename, image) -> StoreTexture(name, image)
  Create a new cached texture with a given name.
* NewBitmap(filename) -> LoadTexture(name)
  Recall a stored texture with a given name.
* level.Chunk.ToBitmap uses simpler names for the textures instead of
  userdir.CacheFilename file-like paths.
master
Noah 2019-06-27 13:01:01 -07:00
parent 4ad32c6edf
commit 981b2b98e0
4 changed files with 100 additions and 96 deletions

View File

@ -1,16 +1,10 @@
package canvas package canvas
import ( import (
"bytes"
"encoding/base64"
"errors"
"image"
"image/png"
"syscall/js" "syscall/js"
"time" "time"
"git.kirsle.net/apps/doodle/lib/events" "git.kirsle.net/apps/doodle/lib/events"
"git.kirsle.net/apps/doodle/lib/render"
) )
// Engine implements a rendering engine targeting an HTML canvas for // Engine implements a rendering engine targeting an HTML canvas for
@ -79,87 +73,6 @@ func (e *Engine) Present() error {
return nil return nil
} }
// Texture can hold on to cached image textures.
type Texture struct {
data string // data:image/png URI
image js.Value // DOM image element
canvas js.Value // Warmed up canvas element
ctx2d js.Value // 2D drawing context for the canvas.
width int
height int
}
// NewTexture caches a texture from a bitmap.
func (e *Engine) NewTexture(filename string, img image.Image) (render.Texturer, error) {
var (
fh = bytes.NewBuffer([]byte{})
imageSize = img.Bounds().Size()
width = imageSize.X
height = imageSize.Y
)
// Encode to PNG format.
if err := png.Encode(fh, img); err != nil {
return nil, err
}
var dataURI = "data:image/png;base64," + base64.StdEncoding.EncodeToString(fh.Bytes())
tex := &Texture{
data: dataURI,
width: width,
height: height,
}
// Preheat a cached Canvas object.
canvas := js.Global().Get("document").Call("createElement", "canvas")
canvas.Set("width", width)
canvas.Set("height", height)
tex.canvas = canvas
ctx2d := canvas.Call("getContext", "2d")
tex.ctx2d = ctx2d
// Load as a JS Image object.
image := js.Global().Call("eval", "new Image()")
image.Call("addEventListener", "load", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
ctx2d.Call("drawImage", image, 0, 0)
return nil
}))
image.Set("src", tex.data)
tex.image = image
// Cache the texture in memory.
e.textures[filename] = tex
return tex, nil
}
// Size returns the dimensions of the texture.
func (t *Texture) Size() render.Rect {
return render.NewRect(int32(t.width), int32(t.height))
}
// NewBitmap initializes a texture from a bitmap image. The image is stored
// in HTML5 Session Storage.
func (e *Engine) NewBitmap(filename string) (render.Texturer, error) {
if tex, ok := e.textures[filename]; ok {
return tex, nil
}
panic("no bitmap for " + filename)
return nil, errors.New("no bitmap data stored for " + filename)
}
// Copy a texturer bitmap onto the canvas.
func (e *Engine) Copy(t render.Texturer, src, dist render.Rect) {
tex := t.(*Texture)
// e.canvas.ctx2d.Call("drawImage", tex.image, dist.X, dist.Y)
e.canvas.ctx2d.Call("drawImage", tex.canvas, dist.X, dist.Y)
}
// Delay for a moment. // Delay for a moment.
func (e *Engine) Delay(delay uint32) { func (e *Engine) Delay(delay uint32) {
time.Sleep(time.Duration(delay) * time.Millisecond) time.Sleep(time.Duration(delay) * time.Millisecond)

91
canvas/texture.go Normal file
View File

@ -0,0 +1,91 @@
package canvas
import (
"bytes"
"encoding/base64"
"errors"
"image"
"image/png"
"syscall/js"
"git.kirsle.net/apps/doodle/lib/render"
)
// Texture can hold on to cached image textures.
type Texture struct {
data string // data:image/png URI
image js.Value // DOM image element
canvas js.Value // Warmed up canvas element
ctx2d js.Value // 2D drawing context for the canvas.
width int
height int
}
// StoreTexture caches a texture from a bitmap.
func (e *Engine) StoreTexture(name string, img image.Image) (render.Texturer, error) {
var (
fh = bytes.NewBuffer([]byte{})
imageSize = img.Bounds().Size()
width = imageSize.X
height = imageSize.Y
)
// Encode to PNG format.
if err := png.Encode(fh, img); err != nil {
return nil, err
}
var dataURI = "data:image/png;base64," + base64.StdEncoding.EncodeToString(fh.Bytes())
tex := &Texture{
data: dataURI,
width: width,
height: height,
}
// Preheat a cached Canvas object.
canvas := js.Global().Get("document").Call("createElement", "canvas")
canvas.Set("width", width)
canvas.Set("height", height)
tex.canvas = canvas
ctx2d := canvas.Call("getContext", "2d")
tex.ctx2d = ctx2d
// Load as a JS Image object.
image := js.Global().Call("eval", "new Image()")
image.Call("addEventListener", "load", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
ctx2d.Call("drawImage", image, 0, 0)
return nil
}))
image.Set("src", tex.data)
tex.image = image
// Cache the texture in memory.
e.textures[name] = tex
return tex, nil
}
// Size returns the dimensions of the texture.
func (t *Texture) Size() render.Rect {
return render.NewRect(int32(t.width), int32(t.height))
}
// LoadTexture recalls a cached texture image.
func (e *Engine) LoadTexture(name string) (render.Texturer, error) {
if tex, ok := e.textures[name]; ok {
return tex, nil
}
return nil, errors.New("no bitmap data stored for " + name)
}
// Copy a texturer bitmap onto the canvas.
func (e *Engine) Copy(t render.Texturer, src, dist render.Rect) {
tex := t.(*Texture)
// e.canvas.ctx2d.Call("drawImage", tex.image, dist.X, dist.Y)
e.canvas.ctx2d.Call("drawImage", tex.canvas, dist.X, dist.Y)
}

View File

@ -32,8 +32,8 @@ type Engine interface {
ComputeTextRect(Text) (Rect, error) ComputeTextRect(Text) (Rect, error)
// Texture caching. // Texture caching.
NewBitmap(filename string) (Texturer, error) StoreTexture(name string, img image.Image) (Texturer, error)
NewTexture(filename string, img image.Image) (Texturer, error) LoadTexture(name string) (Texturer, error)
Copy(t Texturer, src, dst Rect) Copy(t Texturer, src, dst Rect)
// Delay for a moment using the render engine's delay method, // Delay for a moment using the render engine's delay method,

View File

@ -28,8 +28,8 @@ type Texture struct {
height int32 height int32
} }
// NewTexture caches an SDL texture from a bitmap. // StoreTexture caches an SDL texture from a bitmap.
func (r *Renderer) NewTexture(filename string, img image.Image) (render.Texturer, error) { func (r *Renderer) StoreTexture(name string, img image.Image) (render.Texturer, error) {
var ( var (
fh = bytes.NewBuffer([]byte{}) fh = bytes.NewBuffer([]byte{})
) )
@ -65,7 +65,7 @@ func (r *Renderer) NewTexture(filename string, img image.Image) (render.Texturer
height: surface.H, height: surface.H,
tex: texture, tex: texture,
} }
r.textures[filename] = tex r.textures[name] = tex
return tex, nil return tex, nil
} }
@ -75,10 +75,10 @@ func (t *Texture) Size() render.Rect {
return render.NewRect(t.width, t.height) return render.NewRect(t.width, t.height)
} }
// NewBitmap initializes a texture from a bitmap image. // LoadTexture initializes a texture from a bitmap image.
func (r *Renderer) NewBitmap(filename string) (render.Texturer, error) { func (r *Renderer) LoadTexture(name string) (render.Texturer, error) {
if tex, ok := r.textures[filename]; ok { if tex, ok := r.textures[name]; ok {
return tex, nil return tex, nil
} }
return nil, fmt.Errorf("NewBitmap(%s): not found in texture cache", filename) return nil, fmt.Errorf("LoadTexture(%s): not found in texture cache", name)
} }