148 lines
4.0 KiB
Go
148 lines
4.0 KiB
Go
// Copyright 2014 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Package portable blah blah blah TODO.
|
|
package portable
|
|
|
|
import (
|
|
"fmt"
|
|
"image"
|
|
"image/draw"
|
|
|
|
"code.google.com/p/go.mobile/f32"
|
|
"code.google.com/p/go.mobile/geom"
|
|
"code.google.com/p/go.mobile/sprite"
|
|
"code.google.com/p/go.mobile/sprite/clock"
|
|
)
|
|
|
|
func Engine(dst *image.RGBA) sprite.Engine {
|
|
return &engine{
|
|
dst: dst,
|
|
nodes: []*node{nil},
|
|
sheets: []*image.RGBA{nil},
|
|
textures: []*image.RGBA{nil},
|
|
}
|
|
}
|
|
|
|
type node struct {
|
|
// TODO: move this into package sprite as Node.EngineFields.RelTransform??
|
|
relTransform f32.Affine
|
|
}
|
|
|
|
var _ sprite.Engine = (*engine)(nil)
|
|
|
|
type engine struct {
|
|
dst *image.RGBA
|
|
nodes []*node
|
|
sheets []*image.RGBA
|
|
textures []*image.RGBA
|
|
absTransforms []f32.Affine
|
|
}
|
|
|
|
func (e *engine) Register(n *sprite.Node) {
|
|
if n.EngineFields.Index != 0 {
|
|
panic("portable: sprite.Node already registered")
|
|
}
|
|
|
|
o := &node{}
|
|
o.relTransform.Identity()
|
|
|
|
e.nodes = append(e.nodes, o)
|
|
n.EngineFields.Index = int32(len(e.nodes) - 1)
|
|
}
|
|
|
|
func (e *engine) Unregister(n *sprite.Node) {
|
|
panic("todo")
|
|
}
|
|
|
|
func (e *engine) LoadSheet(a image.Image) (sprite.Sheet, error) {
|
|
rgba, ok := a.(*image.RGBA)
|
|
if !ok {
|
|
b := a.Bounds()
|
|
rgba = image.NewRGBA(b)
|
|
draw.Draw(rgba, b, a, b.Min, draw.Src)
|
|
}
|
|
e.sheets = append(e.sheets, rgba)
|
|
return sprite.Sheet(len(e.sheets) - 1), nil
|
|
}
|
|
|
|
func (e *engine) LoadTexture(s sprite.Sheet, bounds image.Rectangle) (sprite.Texture, error) {
|
|
if s < 0 || len(e.sheets) <= int(s) {
|
|
return 0, fmt.Errorf("portable: LoadTexture: sheet %d is out of bounds", s)
|
|
}
|
|
e.textures = append(e.textures, e.sheets[s].SubImage(bounds).(*image.RGBA))
|
|
return sprite.Texture(len(e.textures) - 1), nil
|
|
}
|
|
|
|
func (e *engine) UnloadSheet(s sprite.Sheet) error {
|
|
panic("todo")
|
|
}
|
|
|
|
func (e *engine) UnloadTexture(x sprite.Texture) error {
|
|
panic("todo")
|
|
}
|
|
|
|
func (e *engine) SetTexture(n *sprite.Node, t clock.Time, x sprite.Texture) {
|
|
n.EngineFields.Dirty = true // TODO: do we need to propagate dirtiness up/down the tree?
|
|
n.EngineFields.Texture = x
|
|
}
|
|
|
|
func (e *engine) SetTransform(n *sprite.Node, t clock.Time, m f32.Affine) {
|
|
n.EngineFields.Dirty = true // TODO: do we need to propagate dirtiness up/down the tree?
|
|
e.nodes[n.EngineFields.Index].relTransform = m
|
|
}
|
|
|
|
func (e *engine) Render(scene *sprite.Node, t clock.Time) {
|
|
// Affine transforms are done in geom.Pt. When finally drawing
|
|
// the geom.Pt onto an image.Image we need to convert to system
|
|
// pixels. We scale by geom.Scale to do this.
|
|
e.absTransforms = append(e.absTransforms[:0], f32.Affine{
|
|
{geom.PixelsPerPt, 0, 0},
|
|
{0, geom.PixelsPerPt, 0},
|
|
})
|
|
e.render(scene, t)
|
|
}
|
|
|
|
func (e *engine) render(n *sprite.Node, t clock.Time) {
|
|
if n.EngineFields.Index == 0 {
|
|
panic("portable: sprite.Node not registered")
|
|
}
|
|
if n.Arranger != nil {
|
|
n.Arranger.Arrange(e, n, t)
|
|
}
|
|
|
|
// Push absTransforms.
|
|
// TODO: cache absolute transforms and use EngineFields.Dirty?
|
|
rel := &e.nodes[n.EngineFields.Index].relTransform
|
|
m := f32.Affine{}
|
|
m.Mul(&e.absTransforms[len(e.absTransforms)-1], rel)
|
|
e.absTransforms = append(e.absTransforms, m)
|
|
|
|
if x := e.textures[n.EngineFields.Texture]; x != nil {
|
|
b := x.Bounds()
|
|
|
|
// Affine transforms work in geom.Pt, which is entirely
|
|
// independent of the number of pixels in a texture. A texture
|
|
// of any image.Rectangle bounds rendered with
|
|
//
|
|
// Affine{{1, 0, 0}, {0, 1, 0}}
|
|
//
|
|
// should have the dimensions (1pt, 1pt). To do this we divide
|
|
// by the pixel width and height, reducing the texture to
|
|
// (1px, 1px) of the destination image. Multiplying by geom.Scale
|
|
// makes it (1pt, 1pt).
|
|
m.Scale(&m, 1/float32(b.Dx()), 1/float32(b.Dy()))
|
|
|
|
m.Inverse(&m) // See the documentation on the affine function.
|
|
affine(e.dst, x, &m, draw.Over)
|
|
}
|
|
|
|
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
|
e.render(c, t)
|
|
}
|
|
|
|
// Pop absTransforms.
|
|
e.absTransforms = e.absTransforms[:len(e.absTransforms)-1]
|
|
}
|