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  `