github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/mobile/sprite/portable/portable.go (about)

     1  // Copyright 2014 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package portable implements a sprite Engine using the image package.
     6  //
     7  // It is intended to serve as a reference implementation for testing
     8  // other sprite Engines written against OpenGL, or other more exotic
     9  // modern hardware interfaces.
    10  package portable // import "golang.org/x/mobile/sprite/portable"
    11  
    12  import (
    13  	"image"
    14  	"image/draw"
    15  
    16  	"golang.org/x/mobile/f32"
    17  	"golang.org/x/mobile/geom"
    18  	"golang.org/x/mobile/sprite"
    19  	"golang.org/x/mobile/sprite/clock"
    20  )
    21  
    22  // Engine builds a sprite Engine that renders onto dst.
    23  func Engine(dst *image.RGBA) sprite.Engine {
    24  	return &engine{
    25  		dst:   dst,
    26  		nodes: []*node{nil},
    27  	}
    28  }
    29  
    30  type node struct {
    31  	// TODO: move this into package sprite as Node.EngineFields.RelTransform??
    32  	relTransform f32.Affine
    33  }
    34  
    35  type texture struct {
    36  	m *image.RGBA
    37  }
    38  
    39  func (t *texture) Bounds() (w, h int) {
    40  	b := t.m.Bounds()
    41  	return b.Dx(), b.Dy()
    42  }
    43  
    44  func (t *texture) Download(r image.Rectangle, dst draw.Image) {
    45  	draw.Draw(dst, r, t.m, t.m.Bounds().Min, draw.Src)
    46  }
    47  
    48  func (t *texture) Upload(r image.Rectangle, src image.Image) {
    49  	draw.Draw(t.m, r, src, src.Bounds().Min, draw.Src)
    50  }
    51  
    52  func (t *texture) Unload() { panic("TODO") }
    53  
    54  type engine struct {
    55  	dst           *image.RGBA
    56  	nodes         []*node
    57  	absTransforms []f32.Affine
    58  }
    59  
    60  func (e *engine) Register(n *sprite.Node) {
    61  	if n.EngineFields.Index != 0 {
    62  		panic("portable: sprite.Node already registered")
    63  	}
    64  
    65  	o := &node{}
    66  	o.relTransform.Identity()
    67  
    68  	e.nodes = append(e.nodes, o)
    69  	n.EngineFields.Index = int32(len(e.nodes) - 1)
    70  }
    71  
    72  func (e *engine) Unregister(n *sprite.Node) {
    73  	panic("todo")
    74  }
    75  
    76  func (e *engine) LoadTexture(m image.Image) (sprite.Texture, error) {
    77  	b := m.Bounds()
    78  	w, h := b.Dx(), b.Dy()
    79  
    80  	t := &texture{m: image.NewRGBA(image.Rect(0, 0, w, h))}
    81  	t.Upload(b, m)
    82  	return t, nil
    83  }
    84  
    85  func (e *engine) SetSubTex(n *sprite.Node, x sprite.SubTex) {
    86  	n.EngineFields.Dirty = true // TODO: do we need to propagate dirtiness up/down the tree?
    87  	n.EngineFields.SubTex = x
    88  }
    89  
    90  func (e *engine) SetTransform(n *sprite.Node, m f32.Affine) {
    91  	n.EngineFields.Dirty = true // TODO: do we need to propagate dirtiness up/down the tree?
    92  	e.nodes[n.EngineFields.Index].relTransform = m
    93  }
    94  
    95  func (e *engine) Render(scene *sprite.Node, t clock.Time) {
    96  	// Affine transforms are done in geom.Pt. When finally drawing
    97  	// the geom.Pt onto an image.Image we need to convert to system
    98  	// pixels. We scale by geom.PixelsPerPt to do this.
    99  	e.absTransforms = append(e.absTransforms[:0], f32.Affine{
   100  		{geom.PixelsPerPt, 0, 0},
   101  		{0, geom.PixelsPerPt, 0},
   102  	})
   103  	e.render(scene, t)
   104  }
   105  
   106  func (e *engine) render(n *sprite.Node, t clock.Time) {
   107  	if n.EngineFields.Index == 0 {
   108  		panic("portable: sprite.Node not registered")
   109  	}
   110  	if n.Arranger != nil {
   111  		n.Arranger.Arrange(e, n, t)
   112  	}
   113  
   114  	// Push absTransforms.
   115  	// TODO: cache absolute transforms and use EngineFields.Dirty?
   116  	rel := &e.nodes[n.EngineFields.Index].relTransform
   117  	m := f32.Affine{}
   118  	m.Mul(&e.absTransforms[len(e.absTransforms)-1], rel)
   119  	e.absTransforms = append(e.absTransforms, m)
   120  
   121  	if x := n.EngineFields.SubTex; x.T != nil {
   122  		// Affine transforms work in geom.Pt, which is entirely
   123  		// independent of the number of pixels in a texture. A texture
   124  		// of any image.Rectangle bounds rendered with
   125  		//
   126  		//	Affine{{1, 0, 0}, {0, 1, 0}}
   127  		//
   128  		// should have the dimensions (1pt, 1pt). To do this we divide
   129  		// by the pixel width and height, reducing the texture to
   130  		// (1px, 1px) of the destination image. Multiplying by
   131  		// geom.PixelsPerPt, done in Render above, makes it (1pt, 1pt).
   132  		dx, dy := x.R.Dx(), x.R.Dy()
   133  		if dx > 0 && dy > 0 {
   134  			m.Scale(&m, 1/float32(dx), 1/float32(dy))
   135  			m.Inverse(&m) // See the documentation on the affine function.
   136  			affine(e.dst, x.T.(*texture).m, x.R, nil, &m, draw.Over)
   137  		}
   138  	}
   139  
   140  	for c := n.FirstChild; c != nil; c = c.NextSibling {
   141  		e.render(c, t)
   142  	}
   143  
   144  	// Pop absTransforms.
   145  	e.absTransforms = e.absTransforms[:len(e.absTransforms)-1]
   146  }