gioui.org/ui@v0.0.0-20190926171558-ce74bc0cbaea/app/internal/gpu/path.go (about)

     1  // SPDX-License-Identifier: Unlicense OR MIT
     2  
     3  package gpu
     4  
     5  // GPU accelerated path drawing using the algorithms from
     6  // Pathfinder (https://github.com/pcwalton/pathfinder).
     7  
     8  import (
     9  	"image"
    10  	"unsafe"
    11  
    12  	"gioui.org/ui/app/internal/gl"
    13  	"gioui.org/ui/f32"
    14  	"gioui.org/ui/internal/path"
    15  )
    16  
    17  type pather struct {
    18  	ctx *context
    19  
    20  	viewport image.Point
    21  
    22  	stenciler *stenciler
    23  	coverer   *coverer
    24  }
    25  
    26  type coverer struct {
    27  	ctx  *context
    28  	prog [2]gl.Program
    29  	vars [2]struct {
    30  		z                             gl.Uniform
    31  		uScale, uOffset               gl.Uniform
    32  		uUVScale, uUVOffset           gl.Uniform
    33  		uCoverUVScale, uCoverUVOffset gl.Uniform
    34  		uColor                        gl.Uniform
    35  	}
    36  }
    37  
    38  type stenciler struct {
    39  	ctx                *context
    40  	defFBO             gl.Framebuffer
    41  	indexBufQuads      int
    42  	prog               gl.Program
    43  	iprog              gl.Program
    44  	fbos               fboSet
    45  	intersections      fboSet
    46  	uScale, uOffset    gl.Uniform
    47  	uPathOffset        gl.Uniform
    48  	uIntersectUVOffset gl.Uniform
    49  	uIntersectUVScale  gl.Uniform
    50  	indexBuf           gl.Buffer
    51  }
    52  
    53  type fboSet struct {
    54  	fbos []stencilFBO
    55  }
    56  
    57  type stencilFBO struct {
    58  	size image.Point
    59  	fbo  gl.Framebuffer
    60  	tex  gl.Texture
    61  }
    62  
    63  type pathData struct {
    64  	ncurves int
    65  	data    gl.Buffer
    66  }
    67  
    68  var (
    69  	pathAttribs                = []string{"corner", "maxy", "from", "ctrl", "to"}
    70  	attribPathCorner gl.Attrib = 0
    71  	attribPathMaxY   gl.Attrib = 1
    72  	attribPathFrom   gl.Attrib = 2
    73  	attribPathCtrl   gl.Attrib = 3
    74  	attribPathTo     gl.Attrib = 4
    75  
    76  	intersectAttribs = []string{"pos", "uv"}
    77  )
    78  
    79  func newPather(ctx *context) *pather {
    80  	return &pather{
    81  		ctx:       ctx,
    82  		stenciler: newStenciler(ctx),
    83  		coverer:   newCoverer(ctx),
    84  	}
    85  }
    86  
    87  func newCoverer(ctx *context) *coverer {
    88  	prog, err := createColorPrograms(ctx, coverVSrc, coverFSrc)
    89  	if err != nil {
    90  		panic(err)
    91  	}
    92  	c := &coverer{
    93  		ctx:  ctx,
    94  		prog: prog,
    95  	}
    96  	for i, prog := range prog {
    97  		ctx.UseProgram(prog)
    98  		switch materialType(i) {
    99  		case materialTexture:
   100  			uTex := gl.GetUniformLocation(ctx.Functions, prog, "tex")
   101  			ctx.Uniform1i(uTex, 0)
   102  			c.vars[i].uUVScale = gl.GetUniformLocation(ctx.Functions, prog, "uvScale")
   103  			c.vars[i].uUVOffset = gl.GetUniformLocation(ctx.Functions, prog, "uvOffset")
   104  		case materialColor:
   105  			c.vars[i].uColor = gl.GetUniformLocation(ctx.Functions, prog, "color")
   106  		}
   107  		uCover := gl.GetUniformLocation(ctx.Functions, prog, "cover")
   108  		ctx.Uniform1i(uCover, 1)
   109  		c.vars[i].z = gl.GetUniformLocation(ctx.Functions, prog, "z")
   110  		c.vars[i].uScale = gl.GetUniformLocation(ctx.Functions, prog, "scale")
   111  		c.vars[i].uOffset = gl.GetUniformLocation(ctx.Functions, prog, "offset")
   112  		c.vars[i].uCoverUVScale = gl.GetUniformLocation(ctx.Functions, prog, "uvCoverScale")
   113  		c.vars[i].uCoverUVOffset = gl.GetUniformLocation(ctx.Functions, prog, "uvCoverOffset")
   114  	}
   115  	return c
   116  }
   117  
   118  func newStenciler(ctx *context) *stenciler {
   119  	defFBO := gl.Framebuffer(ctx.GetBinding(gl.FRAMEBUFFER_BINDING))
   120  	prog, err := gl.CreateProgram(ctx.Functions, stencilVSrc, stencilFSrc, pathAttribs)
   121  	if err != nil {
   122  		panic(err)
   123  	}
   124  	ctx.UseProgram(prog)
   125  	iprog, err := gl.CreateProgram(ctx.Functions, intersectVSrc, intersectFSrc, intersectAttribs)
   126  	if err != nil {
   127  		panic(err)
   128  	}
   129  	coverLoc := gl.GetUniformLocation(ctx.Functions, iprog, "cover")
   130  	ctx.UseProgram(iprog)
   131  	ctx.Uniform1i(coverLoc, 0)
   132  	return &stenciler{
   133  		ctx:                ctx,
   134  		defFBO:             defFBO,
   135  		prog:               prog,
   136  		iprog:              iprog,
   137  		uScale:             gl.GetUniformLocation(ctx.Functions, prog, "scale"),
   138  		uOffset:            gl.GetUniformLocation(ctx.Functions, prog, "offset"),
   139  		uPathOffset:        gl.GetUniformLocation(ctx.Functions, prog, "pathOffset"),
   140  		uIntersectUVScale:  gl.GetUniformLocation(ctx.Functions, iprog, "uvScale"),
   141  		uIntersectUVOffset: gl.GetUniformLocation(ctx.Functions, iprog, "uvOffset"),
   142  		indexBuf:           ctx.CreateBuffer(),
   143  	}
   144  }
   145  
   146  func (s *fboSet) resize(ctx *context, sizes []image.Point) {
   147  	// Add fbos.
   148  	for i := len(s.fbos); i < len(sizes); i++ {
   149  		tex := ctx.CreateTexture()
   150  		ctx.BindTexture(gl.TEXTURE_2D, tex)
   151  		ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
   152  		ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
   153  		ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
   154  		ctx.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
   155  		fbo := ctx.CreateFramebuffer()
   156  		s.fbos = append(s.fbos, stencilFBO{
   157  			fbo: fbo,
   158  			tex: tex,
   159  		})
   160  	}
   161  	// Resize fbos.
   162  	for i, sz := range sizes {
   163  		f := &s.fbos[i]
   164  		// Resizing or recreating FBOs can introduce rendering stalls.
   165  		// Avoid if the space waste is not too high.
   166  		resize := sz.X > f.size.X || sz.Y > f.size.Y
   167  		waste := float32(sz.X*sz.Y) / float32(f.size.X*f.size.Y)
   168  		resize = resize || waste > 1.2
   169  		if resize {
   170  			f.size = sz
   171  			ctx.BindTexture(gl.TEXTURE_2D, f.tex)
   172  			tt := ctx.caps.floatTriple
   173  			ctx.TexImage2D(gl.TEXTURE_2D, 0, tt.internalFormat, sz.X, sz.Y, tt.format, tt.typ, nil)
   174  			ctx.BindFramebuffer(gl.FRAMEBUFFER, f.fbo)
   175  			ctx.FramebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, f.tex, 0)
   176  		}
   177  	}
   178  	// Delete extra fbos.
   179  	s.delete(ctx, len(sizes))
   180  }
   181  
   182  func (s *fboSet) invalidate(ctx *context) {
   183  	for _, f := range s.fbos {
   184  		ctx.BindFramebuffer(gl.FRAMEBUFFER, f.fbo)
   185  		ctx.InvalidateFramebuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0)
   186  	}
   187  }
   188  
   189  func (s *fboSet) delete(ctx *context, idx int) {
   190  	for i := idx; i < len(s.fbos); i++ {
   191  		f := s.fbos[i]
   192  		ctx.DeleteFramebuffer(f.fbo)
   193  		ctx.DeleteTexture(f.tex)
   194  	}
   195  	s.fbos = s.fbos[:idx]
   196  }
   197  
   198  func (s *stenciler) release() {
   199  	s.fbos.delete(s.ctx, 0)
   200  	s.ctx.DeleteProgram(s.prog)
   201  	s.ctx.DeleteBuffer(s.indexBuf)
   202  }
   203  
   204  func (p *pather) release() {
   205  	p.stenciler.release()
   206  	p.coverer.release()
   207  }
   208  
   209  func (c *coverer) release() {
   210  	for _, p := range c.prog {
   211  		c.ctx.DeleteProgram(p)
   212  	}
   213  }
   214  
   215  func buildPath(ctx *context, p []byte) *pathData {
   216  	buf := ctx.CreateBuffer()
   217  	ctx.BindBuffer(gl.ARRAY_BUFFER, buf)
   218  	ctx.BufferData(gl.ARRAY_BUFFER, p, gl.STATIC_DRAW)
   219  	return &pathData{
   220  		ncurves: len(p) / path.VertStride,
   221  		data:    buf,
   222  	}
   223  }
   224  
   225  func (p *pathData) release(ctx *context) {
   226  	ctx.DeleteBuffer(p.data)
   227  }
   228  
   229  func (p *pather) begin(sizes []image.Point) {
   230  	p.stenciler.begin(sizes)
   231  }
   232  
   233  func (p *pather) end() {
   234  	p.stenciler.end()
   235  }
   236  
   237  func (p *pather) stencilPath(bounds image.Rectangle, offset f32.Point, uv image.Point, data *pathData) {
   238  	p.stenciler.stencilPath(bounds, offset, uv, data)
   239  }
   240  
   241  func (s *stenciler) beginIntersect(sizes []image.Point) {
   242  	s.ctx.ActiveTexture(gl.TEXTURE1)
   243  	s.ctx.BindTexture(gl.TEXTURE_2D, gl.Texture{})
   244  	s.ctx.ActiveTexture(gl.TEXTURE0)
   245  	s.ctx.BlendFunc(gl.DST_COLOR, gl.ZERO)
   246  	// 8 bit coverage is enough, but OpenGL ES only supports single channel
   247  	// floating point formats. Replace with GL_RGB+GL_UNSIGNED_BYTE if
   248  	// no floating point support is available.
   249  	s.intersections.resize(s.ctx, sizes)
   250  	s.ctx.ClearColor(1.0, 0.0, 0.0, 0.0)
   251  	s.ctx.UseProgram(s.iprog)
   252  }
   253  
   254  func (s *stenciler) endIntersect() {
   255  	s.ctx.BindFramebuffer(gl.FRAMEBUFFER, s.defFBO)
   256  }
   257  
   258  func (s *stenciler) invalidateFBO() {
   259  	s.intersections.invalidate(s.ctx)
   260  	s.fbos.invalidate(s.ctx)
   261  	s.ctx.BindFramebuffer(gl.FRAMEBUFFER, s.defFBO)
   262  }
   263  
   264  func (s *stenciler) cover(idx int) stencilFBO {
   265  	return s.fbos.fbos[idx]
   266  }
   267  
   268  func (s *stenciler) begin(sizes []image.Point) {
   269  	s.ctx.ActiveTexture(gl.TEXTURE1)
   270  	s.ctx.BindTexture(gl.TEXTURE_2D, gl.Texture{})
   271  	s.ctx.ActiveTexture(gl.TEXTURE0)
   272  	s.ctx.BlendFunc(gl.ONE, gl.ONE)
   273  	s.fbos.resize(s.ctx, sizes)
   274  	s.ctx.ClearColor(0.0, 0.0, 0.0, 0.0)
   275  	s.ctx.UseProgram(s.prog)
   276  	s.ctx.EnableVertexAttribArray(attribPathCorner)
   277  	s.ctx.EnableVertexAttribArray(attribPathMaxY)
   278  	s.ctx.EnableVertexAttribArray(attribPathFrom)
   279  	s.ctx.EnableVertexAttribArray(attribPathCtrl)
   280  	s.ctx.EnableVertexAttribArray(attribPathTo)
   281  	s.ctx.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, s.indexBuf)
   282  }
   283  
   284  func (s *stenciler) stencilPath(bounds image.Rectangle, offset f32.Point, uv image.Point, data *pathData) {
   285  	s.ctx.BindBuffer(gl.ARRAY_BUFFER, data.data)
   286  	s.ctx.Viewport(uv.X, uv.Y, bounds.Dx(), bounds.Dy())
   287  	// Transform UI coordinates to OpenGL coordinates.
   288  	texSize := f32.Point{X: float32(bounds.Dx()), Y: float32(bounds.Dy())}
   289  	scale := f32.Point{X: 2 / texSize.X, Y: 2 / texSize.Y}
   290  	orig := f32.Point{X: -1 - float32(bounds.Min.X)*2/texSize.X, Y: -1 - float32(bounds.Min.Y)*2/texSize.Y}
   291  	s.ctx.Uniform2f(s.uScale, scale.X, scale.Y)
   292  	s.ctx.Uniform2f(s.uOffset, orig.X, orig.Y)
   293  	s.ctx.Uniform2f(s.uPathOffset, offset.X, offset.Y)
   294  	// Draw in batches that fit in uint16 indices.
   295  	start := 0
   296  	nquads := data.ncurves / 4
   297  	for start < nquads {
   298  		batch := nquads - start
   299  		if max := int(^uint16(0)) / 6; batch > max {
   300  			batch = max
   301  		}
   302  		// Enlarge VBO if necessary.
   303  		if batch > s.indexBufQuads {
   304  			indices := make([]uint16, batch*6)
   305  			for i := 0; i < batch; i++ {
   306  				i := uint16(i)
   307  				indices[i*6+0] = i*4 + 0
   308  				indices[i*6+1] = i*4 + 1
   309  				indices[i*6+2] = i*4 + 2
   310  				indices[i*6+3] = i*4 + 2
   311  				indices[i*6+4] = i*4 + 1
   312  				indices[i*6+5] = i*4 + 3
   313  			}
   314  			s.ctx.BufferData(gl.ELEMENT_ARRAY_BUFFER, gl.BytesView(indices), gl.STATIC_DRAW)
   315  			s.indexBufQuads = batch
   316  		}
   317  		off := path.VertStride * start * 4
   318  		s.ctx.VertexAttribPointer(attribPathCorner, 2, gl.SHORT, false, path.VertStride, off+int(unsafe.Offsetof((*(*path.Vertex)(nil)).CornerX)))
   319  		s.ctx.VertexAttribPointer(attribPathMaxY, 1, gl.FLOAT, false, path.VertStride, off+int(unsafe.Offsetof((*(*path.Vertex)(nil)).MaxY)))
   320  		s.ctx.VertexAttribPointer(attribPathFrom, 2, gl.FLOAT, false, path.VertStride, off+int(unsafe.Offsetof((*(*path.Vertex)(nil)).FromX)))
   321  		s.ctx.VertexAttribPointer(attribPathCtrl, 2, gl.FLOAT, false, path.VertStride, off+int(unsafe.Offsetof((*(*path.Vertex)(nil)).CtrlX)))
   322  		s.ctx.VertexAttribPointer(attribPathTo, 2, gl.FLOAT, false, path.VertStride, off+int(unsafe.Offsetof((*(*path.Vertex)(nil)).ToX)))
   323  		s.ctx.DrawElements(gl.TRIANGLES, batch*6, gl.UNSIGNED_SHORT, 0)
   324  		start += batch
   325  	}
   326  }
   327  
   328  func (s *stenciler) end() {
   329  	s.ctx.DisableVertexAttribArray(attribPathCorner)
   330  	s.ctx.DisableVertexAttribArray(attribPathMaxY)
   331  	s.ctx.DisableVertexAttribArray(attribPathFrom)
   332  	s.ctx.DisableVertexAttribArray(attribPathCtrl)
   333  	s.ctx.DisableVertexAttribArray(attribPathTo)
   334  	s.ctx.BindFramebuffer(gl.FRAMEBUFFER, s.defFBO)
   335  }
   336  
   337  func (p *pather) cover(z float32, mat materialType, col [4]float32, scale, off, uvScale, uvOff, coverScale, coverOff f32.Point) {
   338  	p.coverer.cover(z, mat, col, scale, off, uvScale, uvOff, coverScale, coverOff)
   339  }
   340  
   341  func (c *coverer) cover(z float32, mat materialType, col [4]float32, scale, off, uvScale, uvOff, coverScale, coverOff f32.Point) {
   342  	c.ctx.UseProgram(c.prog[mat])
   343  	switch mat {
   344  	case materialColor:
   345  		c.ctx.Uniform4f(c.vars[mat].uColor, col[0], col[1], col[2], col[3])
   346  	case materialTexture:
   347  		c.ctx.Uniform2f(c.vars[mat].uUVScale, uvScale.X, uvScale.Y)
   348  		c.ctx.Uniform2f(c.vars[mat].uUVOffset, uvOff.X, uvOff.Y)
   349  	}
   350  	c.ctx.Uniform1f(c.vars[mat].z, z)
   351  	c.ctx.Uniform2f(c.vars[mat].uScale, scale.X, scale.Y)
   352  	c.ctx.Uniform2f(c.vars[mat].uOffset, off.X, off.Y)
   353  	c.ctx.Uniform2f(c.vars[mat].uCoverUVScale, coverScale.X, coverScale.Y)
   354  	c.ctx.Uniform2f(c.vars[mat].uCoverUVOffset, coverOff.X, coverOff.Y)
   355  	c.ctx.DrawArrays(gl.TRIANGLE_STRIP, 0, 4)
   356  }
   357  
   358  const stencilVSrc = `
   359  #version 100
   360  
   361  precision highp float;
   362  
   363  uniform vec2 scale;
   364  uniform vec2 offset;
   365  uniform vec2 pathOffset;
   366  
   367  attribute vec2 corner;
   368  attribute float maxy;
   369  attribute vec2 from;
   370  attribute vec2 ctrl;
   371  attribute vec2 to;
   372  
   373  varying vec2 vFrom;
   374  varying vec2 vCtrl;
   375  varying vec2 vTo;
   376  
   377  void main() {
   378  	// Add a one pixel overlap so curve quads cover their
   379  	// entire curves. Could use conservative rasterization
   380  	// if available.
   381  	vec2 from = from + pathOffset;
   382  	vec2 ctrl = ctrl + pathOffset;
   383  	vec2 to = to + pathOffset;
   384  	float maxy = maxy + pathOffset.y;
   385  	vec2 pos;
   386  	if (corner.x > 0.0) {
   387  		// East.
   388  		pos.x = max(max(from.x, ctrl.x), to.x)+1.0;
   389  	} else {
   390  		// West.
   391  		pos.x = min(min(from.x, ctrl.x), to.x)-1.0;
   392  	}
   393  	if (corner.y > 0.0) {
   394  		// North.
   395  		pos.y = maxy + 1.0;
   396  	} else {
   397  		// South.
   398  		pos.y = min(min(from.y, ctrl.y), to.y) - 1.0;
   399  	}
   400  	vFrom = from-pos;
   401  	vCtrl = ctrl-pos;
   402  	vTo = to-pos;
   403      pos *= scale;
   404      pos += offset;
   405      gl_Position = vec4(pos, 1, 1);
   406  }
   407  `
   408  
   409  const stencilFSrc = `
   410  #version 100
   411  
   412  precision mediump float;
   413  
   414  varying vec2 vFrom;
   415  varying vec2 vCtrl;
   416  varying vec2 vTo;
   417  
   418  uniform sampler2D areaLUT;
   419  
   420  void main() {
   421  	float dx = vTo.x - vFrom.x;
   422  	// Sort from and to in increasing order so the root below
   423  	// is always the positive square root, if any.
   424  	// We need the direction of the curve below, so this can't be
   425  	// done from the vertex shader.
   426  	bool increasing = vTo.x >= vFrom.x;
   427  	vec2 left = increasing ? vFrom : vTo;
   428  	vec2 right = increasing ? vTo : vFrom;
   429  
   430  	// The signed horizontal extent of the fragment.
   431  	vec2 extent = clamp(vec2(vFrom.x, vTo.x), -0.5, 0.5);
   432  	// Find the t where the curve crosses the middle of the
   433  	// extent, x₀.
   434  	// Given the Bézier curve with x coordinates P₀, P₁, P₂
   435  	// where P₀ is at the origin, its x coordinate in t
   436  	// is given by:
   437  	//
   438  	// x(t) = 2(1-t)tP₁ + t²P₂
   439  	// 
   440  	// Rearranging:
   441  	//
   442  	// x(t) = (P₂ - 2P₁)t² + 2P₁t
   443  	//
   444  	// Setting x(t) = x₀ and using Muller's quadratic formula ("Citardauq")
   445  	// for robustnesss,
   446  	//
   447  	// t = 2x₀/(2P₁±√(4P₁²+4(P₂-2P₁)x₀))
   448  	//
   449  	// which simplifies to
   450  	//
   451  	// t = x₀/(P₁±√(P₁²+(P₂-2P₁)x₀))
   452  	//
   453  	// Setting v = P₂-P₁,
   454  	//
   455  	// t = x₀/(P₁±√(P₁²+(v-P₁)x₀))
   456  	//
   457  	// t lie in [0; 1]; P₂ ≥ P₁ and P₁ ≥ 0 since we split curves where
   458  	// the control point lies before the start point or after the end point.
   459  	// It can then be shown that only the positive square root is valid.
   460  	float midx = mix(extent.x, extent.y, 0.5);
   461  	float x0 = midx - left.x;
   462  	vec2 p1 = vCtrl - left;
   463  	vec2 v = right - vCtrl;
   464  	float t = x0/(p1.x+sqrt(p1.x*p1.x+(v.x-p1.x)*x0));
   465  	// Find y(t) on the curve.
   466  	float y = mix(mix(left.y, vCtrl.y, t), mix(vCtrl.y, right.y, t), t);
   467  	// And the slope.
   468  	vec2 d_half = mix(p1, v, t);
   469  	float dy = d_half.y/d_half.x;
   470  	// Together, y and dy form a line approximation.
   471  
   472  	// Compute the fragment area above the line.
   473  	// The area is symmetric around dy = 0. Scale slope with extent width.
   474  	float width = extent.y - extent.x;
   475  	dy = abs(dy*width);
   476  
   477  	vec4 sides = vec4(dy*+0.5 + y, dy*-0.5 + y, (+0.5-y)/dy, (-0.5-y)/dy);
   478  	sides = clamp(sides+0.5, 0.0, 1.0);
   479  
   480  	float area = 0.5*(sides.z - sides.z*sides.y + 1.0 - sides.x+sides.x*sides.w);
   481  	area *= width;
   482  
   483  	// Work around issue #13.
   484  	if (width == 0.0)
   485  		area = 0.0;
   486  
   487  	gl_FragColor.r = area;
   488  }
   489  `
   490  
   491  const coverVSrc = `
   492  #version 100
   493  
   494  precision highp float;
   495  
   496  uniform float z;
   497  uniform vec2 scale;
   498  uniform vec2 offset;
   499  uniform vec2 uvScale;
   500  uniform vec2 uvOffset;
   501  uniform vec2 uvCoverScale;
   502  uniform vec2 uvCoverOffset;
   503  
   504  attribute vec2 pos;
   505  
   506  varying vec2 vCoverUV;
   507  
   508  attribute vec2 uv;
   509  varying vec2 vUV;
   510  
   511  void main() {
   512      gl_Position = vec4(pos*scale + offset, z, 1);
   513  	vUV = uv*uvScale + uvOffset;
   514  	vCoverUV = uv*uvCoverScale+uvCoverOffset;
   515  }
   516  `
   517  
   518  const coverFSrc = `
   519  #version 100
   520  
   521  precision mediump float;
   522  
   523  // Use high precision to be pixel accurate for
   524  // large cover atlases.
   525  varying highp vec2 vCoverUV;
   526  uniform sampler2D cover;
   527  varying vec2 vUV;
   528  
   529  HEADER
   530  
   531  void main() {
   532      gl_FragColor = GET_COLOR;
   533  	float cover = abs(texture2D(cover, vCoverUV).r);
   534  	gl_FragColor *= cover;
   535  }
   536  `
   537  
   538  const intersectVSrc = `
   539  #version 100
   540  
   541  precision highp float;
   542  
   543  attribute vec2 pos;
   544  attribute vec2 uv;
   545  
   546  uniform vec2 uvScale;
   547  uniform vec2 uvOffset;
   548  
   549  varying vec2 vUV;
   550  
   551  void main() {
   552  	vec2 p = pos;
   553  	p.y = -p.y;
   554  	gl_Position = vec4(p, 0, 1);
   555  	vUV = uv*uvScale + uvOffset;
   556  }
   557  `
   558  
   559  const intersectFSrc = `
   560  #version 100
   561  
   562  precision mediump float;
   563  
   564  // Use high precision to be pixel accurate for
   565  // large cover atlases.
   566  varying highp vec2 vUV;
   567  uniform sampler2D cover;
   568  
   569  void main() {
   570  	float cover = abs(texture2D(cover, vUV).r);
   571      gl_FragColor.r = cover;
   572  }
   573  `