glimage.go 9.0 KB

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