github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/mobile/gl/glutil/glimage.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 glutil 6 7 import ( 8 "encoding/binary" 9 "image" 10 "sync" 11 12 "golang.org/x/mobile/f32" 13 "golang.org/x/mobile/geom" 14 "golang.org/x/mobile/gl" 15 ) 16 17 var glimage struct { 18 sync.Once 19 quadXY gl.Buffer 20 quadUV gl.Buffer 21 program gl.Program 22 pos gl.Attrib 23 mvp gl.Uniform 24 uvp gl.Uniform 25 inUV gl.Attrib 26 textureSample gl.Uniform 27 } 28 29 func glInit() { 30 var err error 31 glimage.program, err = CreateProgram(vertexShader, fragmentShader) 32 if err != nil { 33 panic(err) 34 } 35 36 glimage.quadXY = gl.GenBuffer() 37 glimage.quadUV = gl.GenBuffer() 38 39 gl.BindBuffer(gl.ARRAY_BUFFER, glimage.quadXY) 40 gl.BufferData(gl.ARRAY_BUFFER, gl.STATIC_DRAW, quadXYCoords) 41 gl.BindBuffer(gl.ARRAY_BUFFER, glimage.quadUV) 42 gl.BufferData(gl.ARRAY_BUFFER, gl.STATIC_DRAW, quadUVCoords) 43 44 glimage.pos = gl.GetAttribLocation(glimage.program, "pos") 45 glimage.mvp = gl.GetUniformLocation(glimage.program, "mvp") 46 glimage.uvp = gl.GetUniformLocation(glimage.program, "uvp") 47 glimage.inUV = gl.GetAttribLocation(glimage.program, "inUV") 48 glimage.textureSample = gl.GetUniformLocation(glimage.program, "textureSample") 49 } 50 51 // Image bridges between an *image.RGBA and an OpenGL texture. 52 // 53 // The contents of the embedded *image.RGBA can be uploaded as a 54 // texture and drawn as a 2D quad. 55 // 56 // The number of active Images must fit in the system's OpenGL texture 57 // limit. The typical use of an Image is as a texture atlas. 58 type Image struct { 59 *image.RGBA 60 61 Texture gl.Texture 62 texWidth int 63 texHeight int 64 } 65 66 // NewImage creates an Image of the given size. 67 // 68 // Both a host-memory *image.RGBA and a GL texture are created. 69 func NewImage(w, h int) *Image { 70 dx := roundToPower2(w) 71 dy := roundToPower2(h) 72 73 // TODO(crawshaw): Using VertexAttribPointer we can pass texture 74 // data with a stride, which would let us use the exact number of 75 // pixels on the host instead of the rounded up power 2 size. 76 m := image.NewRGBA(image.Rect(0, 0, dx, dy)) 77 78 glimage.Do(glInit) 79 80 img := &Image{ 81 RGBA: m.SubImage(image.Rect(0, 0, w, h)).(*image.RGBA), 82 Texture: gl.GenTexture(), 83 texWidth: dx, 84 texHeight: dy, 85 } 86 // TODO(crawshaw): We don't have the context on a finalizer. Find a way. 87 // runtime.SetFinalizer(img, func(img *Image) { gl.DeleteTexture(img.Texture) }) 88 gl.BindTexture(gl.TEXTURE_2D, img.Texture) 89 gl.TexImage2D(gl.TEXTURE_2D, 0, dx, dy, gl.RGBA, gl.UNSIGNED_BYTE, nil) 90 gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) 91 gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) 92 gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) 93 gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) 94 95 return img 96 } 97 98 func roundToPower2(x int) int { 99 x2 := 1 100 for x2 < x { 101 x2 *= 2 102 } 103 return x2 104 } 105 106 // Upload copies the host image data to the GL device. 107 func (img *Image) Upload() { 108 gl.BindTexture(gl.TEXTURE_2D, img.Texture) 109 gl.TexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, img.texWidth, img.texHeight, gl.RGBA, gl.UNSIGNED_BYTE, img.Pix) 110 } 111 112 // Draw draws the srcBounds part of the image onto a parallelogram, defined by 113 // three of its corners, in the current GL framebuffer. 114 func (img *Image) Draw(topLeft, topRight, bottomLeft geom.Point, srcBounds image.Rectangle) { 115 // TODO(crawshaw): Adjust viewport for the top bar on android? 116 gl.UseProgram(glimage.program) 117 118 { 119 // We are drawing a parallelogram PQRS, defined by three of its 120 // corners, onto the entire GL framebuffer ABCD. The two quads may 121 // actually be equal, but in the general case, PQRS can be smaller, 122 // and PQRS is not necessarily axis-aligned. 123 // 124 // A +---------------+ B 125 // | P +-----+ Q | 126 // | | | | 127 // | S +-----+ R | 128 // D +---------------+ C 129 // 130 // There are two co-ordinate spaces: geom space and framebuffer space. 131 // In geom space, the ABCD rectangle is: 132 // 133 // (0, 0) (geom.Width, 0) 134 // (0, geom.Height) (geom.Width, geom.Height) 135 // 136 // and the PQRS quad is: 137 // 138 // (topLeft.X, topLeft.Y) (topRight.X, topRight.Y) 139 // (bottomLeft.X, bottomLeft.Y) (implicit, implicit) 140 // 141 // In framebuffer space, the ABCD rectangle is: 142 // 143 // (-1, +1) (+1, +1) 144 // (-1, -1) (+1, -1) 145 // 146 // First of all, convert from geom space to framebuffer space. For 147 // later convenience, we divide everything by 2 here: px2 is half of 148 // the P.X co-ordinate (in framebuffer space). 149 px2 := -0.5 + float32(topLeft.X/geom.Width) 150 py2 := +0.5 - float32(topLeft.Y/geom.Height) 151 qx2 := -0.5 + float32(topRight.X/geom.Width) 152 qy2 := +0.5 - float32(topRight.Y/geom.Height) 153 sx2 := -0.5 + float32(bottomLeft.X/geom.Width) 154 sy2 := +0.5 - float32(bottomLeft.Y/geom.Height) 155 // Next, solve for the affine transformation matrix 156 // [ a00 a01 a02 ] 157 // a = [ a10 a11 a12 ] 158 // [ 0 0 1 ] 159 // that maps A to P: 160 // a × [ -1 +1 1 ]' = [ 2*px2 2*py2 1 ]' 161 // and likewise maps B to Q and D to S. Solving those three constraints 162 // implies that C maps to R, since affine transformations keep parallel 163 // lines parallel. This gives 6 equations in 6 unknowns: 164 // -a00 + a01 + a02 = 2*px2 165 // -a10 + a11 + a12 = 2*py2 166 // +a00 + a01 + a02 = 2*qx2 167 // +a10 + a11 + a12 = 2*qy2 168 // -a00 - a01 + a02 = 2*sx2 169 // -a10 - a11 + a12 = 2*sy2 170 // which gives: 171 // a00 = (2*qx2 - 2*px2) / 2 = qx2 - px2 172 // and similarly for the other elements of a. 173 glimage.mvp.WriteAffine(&f32.Affine{{ 174 qx2 - px2, 175 px2 - sx2, 176 qx2 + sx2, 177 }, { 178 qy2 - py2, 179 py2 - sy2, 180 qy2 + sy2, 181 }}) 182 } 183 184 { 185 // Mapping texture co-ordinates is similar, except that in texture 186 // space, the ABCD rectangle is: 187 // 188 // (0,0) (1,0) 189 // (0,1) (1,1) 190 // 191 // and the PQRS quad is always axis-aligned. First of all, convert 192 // from pixel space to texture space. 193 w := float32(img.texWidth) 194 h := float32(img.texHeight) 195 px := float32(srcBounds.Min.X-img.Rect.Min.X) / w 196 py := float32(srcBounds.Min.Y-img.Rect.Min.Y) / h 197 qx := float32(srcBounds.Max.X-img.Rect.Min.X) / w 198 sy := float32(srcBounds.Max.Y-img.Rect.Min.Y) / h 199 // Due to axis alignment, qy = py and sx = px. 200 // 201 // The simultaneous equations are: 202 // 0 + 0 + a02 = px 203 // 0 + 0 + a12 = py 204 // a00 + 0 + a02 = qx 205 // a10 + 0 + a12 = qy = py 206 // 0 + a01 + a02 = sx = px 207 // 0 + a11 + a12 = sy 208 glimage.uvp.WriteAffine(&f32.Affine{{ 209 qx - px, 210 0, 211 px, 212 }, { 213 0, 214 sy - py, 215 py, 216 }}) 217 } 218 219 gl.ActiveTexture(gl.TEXTURE0) 220 gl.BindTexture(gl.TEXTURE_2D, img.Texture) 221 gl.Uniform1i(glimage.textureSample, 0) 222 223 gl.BindBuffer(gl.ARRAY_BUFFER, glimage.quadXY) 224 gl.EnableVertexAttribArray(glimage.pos) 225 gl.VertexAttribPointer(glimage.pos, 2, gl.FLOAT, false, 0, 0) 226 227 gl.BindBuffer(gl.ARRAY_BUFFER, glimage.quadUV) 228 gl.EnableVertexAttribArray(glimage.inUV) 229 gl.VertexAttribPointer(glimage.inUV, 2, gl.FLOAT, false, 0, 0) 230 231 gl.DrawArrays(gl.TRIANGLE_STRIP, 0, 4) 232 233 gl.DisableVertexAttribArray(glimage.pos) 234 gl.DisableVertexAttribArray(glimage.inUV) 235 } 236 237 var quadXYCoords = f32.Bytes(binary.LittleEndian, 238 -1, +1, // top left 239 +1, +1, // top right 240 -1, -1, // bottom left 241 +1, -1, // bottom right 242 ) 243 244 var quadUVCoords = f32.Bytes(binary.LittleEndian, 245 0, 0, // top left 246 1, 0, // top right 247 0, 1, // bottom left 248 1, 1, // bottom right 249 ) 250 251 const vertexShader = `#version 100 252 uniform mat3 mvp; 253 uniform mat3 uvp; 254 attribute vec3 pos; 255 attribute vec2 inUV; 256 varying vec2 UV; 257 void main() { 258 vec3 p = pos; 259 p.z = 1.0; 260 gl_Position = vec4(mvp * p, 1); 261 UV = (uvp * vec3(inUV, 1)).xy; 262 } 263 ` 264 265 const fragmentShader = `#version 100 266 precision mediump float; 267 varying vec2 UV; 268 uniform sampler2D textureSample; 269 void main(){ 270 gl_FragColor = texture2D(textureSample, UV); 271 } 272 `