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