github.com/acrespo/mobile@v0.0.0-20190107162257-dc0771356504/exp/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/exp/sprite/portable"
    11  
    12  import (
    13  	"image"
    14  	"image/draw"
    15  
    16  	xdraw "golang.org/x/image/draw"
    17  	"golang.org/x/image/math/f64"
    18  	"golang.org/x/mobile/event/size"
    19  	"golang.org/x/mobile/exp/f32"
    20  	"golang.org/x/mobile/exp/sprite"
    21  	"golang.org/x/mobile/exp/sprite/clock"
    22  )
    23  
    24  // Engine builds a sprite Engine that renders onto dst.
    25  func Engine(dst *image.RGBA) sprite.Engine {
    26  	return &engine{
    27  		dst:   dst,
    28  		nodes: []*node{nil},
    29  	}
    30  }
    31  
    32  type node struct {
    33  	// TODO: move this into package sprite as Node.EngineFields.RelTransform??
    34  	relTransform f32.Affine
    35  }
    36  
    37  type texture struct {
    38  	m *image.RGBA
    39  }
    40  
    41  func (t *texture) Bounds() (w, h int) {
    42  	b := t.m.Bounds()
    43  	return b.Dx(), b.Dy()
    44  }
    45  
    46  func (t *texture) Download(r image.Rectangle, dst draw.Image) {
    47  	draw.Draw(dst, r, t.m, t.m.Bounds().Min, draw.Src)
    48  }
    49  
    50  func (t *texture) Upload(r image.Rectangle, src image.Image) {
    51  	draw.Draw(t.m, r, src, src.Bounds().Min, draw.Src)
    52  }
    53  
    54  func (t *texture) Release() {}
    55  
    56  type engine struct {
    57  	dst           *image.RGBA
    58  	nodes         []*node
    59  	absTransforms []f32.Affine
    60  }
    61  
    62  func (e *engine) Register(n *sprite.Node) {
    63  	if n.EngineFields.Index != 0 {
    64  		panic("portable: sprite.Node already registered")
    65  	}
    66  
    67  	o := &node{}
    68  	o.relTransform.Identity()
    69  
    70  	e.nodes = append(e.nodes, o)
    71  	n.EngineFields.Index = int32(len(e.nodes) - 1)
    72  }
    73  
    74  func (e *engine) Unregister(n *sprite.Node) {
    75  	panic("todo")
    76  }
    77  
    78  func (e *engine) LoadTexture(m image.Image) (sprite.Texture, error) {
    79  	b := m.Bounds()
    80  	w, h := b.Dx(), b.Dy()
    81  
    82  	t := &texture{m: image.NewRGBA(image.Rect(0, 0, w, h))}
    83  	t.Upload(b, m)
    84  	return t, nil
    85  }
    86  
    87  func (e *engine) SetSubTex(n *sprite.Node, x sprite.SubTex) {
    88  	n.EngineFields.Dirty = true // TODO: do we need to propagate dirtiness up/down the tree?
    89  	n.EngineFields.SubTex = x
    90  }
    91  
    92  func (e *engine) SetTransform(n *sprite.Node, m f32.Affine) {
    93  	n.EngineFields.Dirty = true // TODO: do we need to propagate dirtiness up/down the tree?
    94  	e.nodes[n.EngineFields.Index].relTransform = m
    95  }
    96  
    97  func (e *engine) Render(scene *sprite.Node, t clock.Time, sz size.Event) {
    98  	// Affine transforms are done in geom.Pt. When finally drawing
    99  	// the geom.Pt onto an image.Image we need to convert to system
   100  	// pixels. We scale by sz.PixelsPerPt to do this.
   101  	e.absTransforms = append(e.absTransforms[:0], f32.Affine{
   102  		{sz.PixelsPerPt, 0, 0},
   103  		{0, sz.PixelsPerPt, 0},
   104  	})
   105  	e.render(scene, t)
   106  }
   107  
   108  func (e *engine) render(n *sprite.Node, t clock.Time) {
   109  	if n.EngineFields.Index == 0 {
   110  		panic("portable: sprite.Node not registered")
   111  	}
   112  	if n.Arranger != nil {
   113  		n.Arranger.Arrange(e, n, t)
   114  	}
   115  
   116  	// Push absTransforms.
   117  	// TODO: cache absolute transforms and use EngineFields.Dirty?
   118  	rel := &e.nodes[n.EngineFields.Index].relTransform
   119  	m := f32.Affine{}
   120  	m.Mul(&e.absTransforms[len(e.absTransforms)-1], rel)
   121  	e.absTransforms = append(e.absTransforms, m)
   122  
   123  	if x := n.EngineFields.SubTex; x.T != nil {
   124  		// Affine transforms work in geom.Pt, which is entirely
   125  		// independent of the number of pixels in a texture. A texture
   126  		// of any image.Rectangle bounds rendered with
   127  		//
   128  		//	Affine{{1, 0, 0}, {0, 1, 0}}
   129  		//
   130  		// should have the dimensions (1pt, 1pt). To do this we divide
   131  		// by the pixel width and height, reducing the texture to
   132  		// (1px, 1px) of the destination image. Multiplying by
   133  		// sz.PixelsPerPt, done in Render above, makes it (1pt, 1pt).
   134  		dx, dy := x.R.Dx(), x.R.Dy()
   135  		if dx > 0 && dy > 0 {
   136  			m.Scale(&m, 1/float32(dx), 1/float32(dy))
   137  			// TODO(nigeltao): delete the double-inverse: one here and one
   138  			// inside func affine.
   139  			m.Inverse(&m) // See the documentation on the affine function.
   140  			affine(e.dst, x.T.(*texture).m, x.R, nil, &m, draw.Over)
   141  		}
   142  	}
   143  
   144  	for c := n.FirstChild; c != nil; c = c.NextSibling {
   145  		e.render(c, t)
   146  	}
   147  
   148  	// Pop absTransforms.
   149  	e.absTransforms = e.absTransforms[:len(e.absTransforms)-1]
   150  }
   151  
   152  func (e *engine) Release() {}
   153  
   154  // affine draws each pixel of dst using bilinear interpolation of the
   155  // affine-transformed position in src. This is equivalent to:
   156  //
   157  //      for each (x,y) in dst:
   158  //              dst(x,y) = bilinear interpolation of src(a*(x,y))
   159  //
   160  // While this is the simpler implementation, it can be counter-
   161  // intuitive as an affine transformation is usually described in terms
   162  // of the source, not the destination. For example, a scale transform
   163  //
   164  //      Affine{{2, 0, 0}, {0, 2, 0}}
   165  //
   166  // will produce a dst that is half the size of src. To perform a
   167  // traditional affine transform, use the inverse of the affine matrix.
   168  func affine(dst *image.RGBA, src image.Image, srcb image.Rectangle, mask image.Image, a *f32.Affine, op draw.Op) {
   169  	// For legacy compatibility reasons, the matrix a transforms from dst-space
   170  	// to src-space. The golang.org/x/image/draw package's matrices transform
   171  	// from src-space to dst-space, so we invert (and adjust for different
   172  	// origins).
   173  	i := *a
   174  	i[0][2] += float32(srcb.Min.X)
   175  	i[1][2] += float32(srcb.Min.Y)
   176  	i.Inverse(&i)
   177  	i[0][2] += float32(dst.Rect.Min.X)
   178  	i[1][2] += float32(dst.Rect.Min.Y)
   179  	m := f64.Aff3{
   180  		float64(i[0][0]),
   181  		float64(i[0][1]),
   182  		float64(i[0][2]),
   183  		float64(i[1][0]),
   184  		float64(i[1][1]),
   185  		float64(i[1][2]),
   186  	}
   187  	// TODO(nigeltao): is the caller or callee responsible for detecting
   188  	// transforms that are simple copies or scales, for which there are faster
   189  	// implementations in the xdraw package.
   190  	xdraw.ApproxBiLinear.Transform(dst, m, src, srcb, xdraw.Op(op), &xdraw.Options{
   191  		SrcMask: mask,
   192  	})
   193  }