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 }