glimage.go 9.0 KB

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