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