canvas/backend/gogl/gogl.go
2019-02-22 16:38:27 +01:00

367 lines
11 KiB
Go

package goglbackend
import (
"fmt"
"image/color"
"github.com/go-gl/gl/v3.2-core/gl"
"github.com/tfriedel6/canvas"
"github.com/tfriedel6/canvas/backend/backendbase"
)
const alphaTexSize = 2048
type GoGLBackend struct {
x, y, w, h int
fx, fy, fw, fh float64
buf uint32
shadowBuf uint32
alphaTex uint32
sr solidShader
lgr linearGradientShader
rgr radialGradientShader
ipr imagePatternShader
sar solidAlphaShader
rgar radialGradientAlphaShader
lgar linearGradientAlphaShader
ipar imagePatternAlphaShader
ir imageShader
gauss15r gaussianShader
gauss63r gaussianShader
gauss127r gaussianShader
offscr1 offscreenBuffer
offscr2 offscreenBuffer
glChan chan func()
ptsBuf []float32
}
type offscreenBuffer struct {
tex uint32
w int
h int
renderStencilBuf uint32
frameBuf uint32
alpha bool
}
func New(x, y, w, h int) (canvas.Backend, error) {
err := gl.Init()
if err != nil {
return nil, err
}
gl.GetError() // clear error state
b := &GoGLBackend{
w: w,
h: h,
fw: float64(w),
fh: float64(h),
ptsBuf: make([]float32, 0, 4096),
}
err = loadShader(solidVS, solidFS, &b.sr.shaderProgram)
if err != nil {
return nil, err
}
b.sr.shaderProgram.mustLoadLocations(&b.sr)
if err = glError(); err != nil {
return nil, err
}
err = loadShader(linearGradientVS, linearGradientFS, &b.lgr.shaderProgram)
if err != nil {
return nil, err
}
b.lgr.shaderProgram.mustLoadLocations(&b.lgr)
if err = glError(); err != nil {
return nil, err
}
err = loadShader(radialGradientVS, radialGradientFS, &b.rgr.shaderProgram)
if err != nil {
return nil, err
}
b.rgr.shaderProgram.mustLoadLocations(&b.rgr)
if err = glError(); err != nil {
return nil, err
}
err = loadShader(imagePatternVS, imagePatternFS, &b.ipr.shaderProgram)
if err != nil {
return nil, err
}
b.ipr.shaderProgram.mustLoadLocations(&b.ipr)
if err = glError(); err != nil {
return nil, err
}
err = loadShader(solidAlphaVS, solidAlphaFS, &b.sar.shaderProgram)
if err != nil {
return nil, err
}
b.sar.shaderProgram.mustLoadLocations(&b.sar)
if err = glError(); err != nil {
return nil, err
}
err = loadShader(linearGradientAlphaVS, linearGradientFS, &b.lgar.shaderProgram)
if err != nil {
return nil, err
}
b.lgar.shaderProgram.mustLoadLocations(&b.lgar)
if err = glError(); err != nil {
return nil, err
}
err = loadShader(radialGradientAlphaVS, radialGradientAlphaFS, &b.rgar.shaderProgram)
if err != nil {
return nil, err
}
b.rgar.shaderProgram.mustLoadLocations(&b.rgar)
if err = glError(); err != nil {
return nil, err
}
err = loadShader(imagePatternAlphaVS, imagePatternAlphaFS, &b.ipar.shaderProgram)
if err != nil {
return nil, err
}
b.ipar.shaderProgram.mustLoadLocations(&b.ipar)
if err = glError(); err != nil {
return nil, err
}
err = loadShader(imageVS, imageFS, &b.ir.shaderProgram)
if err != nil {
return nil, err
}
b.ir.shaderProgram.mustLoadLocations(&b.ir)
if err = glError(); err != nil {
return nil, err
}
err = loadShader(gaussian15VS, gaussian15FS, &b.gauss15r.shaderProgram)
if err != nil {
return nil, err
}
b.gauss15r.shaderProgram.mustLoadLocations(&b.gauss15r)
if err = glError(); err != nil {
return nil, err
}
err = loadShader(gaussian63VS, gaussian63FS, &b.gauss63r.shaderProgram)
if err != nil {
return nil, err
}
b.gauss63r.shaderProgram.mustLoadLocations(&b.gauss63r)
if err = glError(); err != nil {
return nil, err
}
err = loadShader(gaussian127VS, gaussian127FS, &b.gauss127r.shaderProgram)
if err != nil {
return nil, err
}
b.gauss127r.shaderProgram.mustLoadLocations(&b.gauss127r)
if err = glError(); err != nil {
return nil, err
}
gl.GenBuffers(1, &b.buf)
if err = glError(); err != nil {
return nil, err
}
gl.GenBuffers(1, &b.shadowBuf)
if err = glError(); err != nil {
return nil, err
}
gl.ActiveTexture(gl.TEXTURE0)
gl.GenTextures(1, &b.alphaTex)
gl.BindTexture(gl.TEXTURE_2D, b.alphaTex)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.ALPHA, alphaTexSize, alphaTexSize, 0, gl.ALPHA, gl.UNSIGNED_BYTE, nil)
gl.Enable(gl.BLEND)
gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
gl.Enable(gl.STENCIL_TEST)
gl.StencilMask(0xFF)
gl.Clear(gl.STENCIL_BUFFER_BIT)
gl.StencilOp(gl.KEEP, gl.KEEP, gl.KEEP)
gl.StencilFunc(gl.EQUAL, 0, 0xFF)
gl.Enable(gl.SCISSOR_TEST)
return b, nil
}
// SetBounds updates the bounds of the canvas. This would
// usually be called for example when the window is resized
func (b *GoGLBackend) SetBounds(x, y, w, h int) {
b.x, b.y = x, y
b.fx, b.fy = float64(x), float64(y)
b.w, b.h = w, h
b.fw, b.fh = float64(w), float64(h)
}
func glError() error {
glErr := gl.GetError()
if glErr != gl.NO_ERROR {
return fmt.Errorf("GL Error: %x", glErr)
}
return nil
}
type glColor struct {
r, g, b, a float64
}
func colorGoToGL(c color.RGBA) glColor {
var glc glColor
glc.r = float64(c.R) / 255
glc.g = float64(c.G) / 255
glc.b = float64(c.B) / 255
glc.a = float64(c.A) / 255
return glc
}
func (b *GoGLBackend) useShader(style *backendbase.Style) (vertexLoc uint32) {
// if lg := style.LinearGradient; lg != nil {
// lg.load()
// gl.ActiveTexture(gl.TEXTURE0)
// gl.BindTexture(gl.TEXTURE_2D, lg.tex)
// gl.UseProgram(lgr.id)
// from := cv.tf(lg.from)
// to := cv.tf(lg.to)
// dir := to.sub(from)
// length := dir.len()
// dir = dir.divf(length)
// gl.Uniform2f(lgr.canvasSize, float32(cv.fw), float32(cv.fh))
// inv := cv.state.transform.invert().f32()
// gl.UniformMatrix3fv(lgr.invmat, 1, false, &inv[0])
// gl.Uniform2f(lgr.from, float32(from[0]), float32(from[1]))
// gl.Uniform2f(lgr.dir, float32(dir[0]), float32(dir[1]))
// gl.Uniform1f(lgr.len, float32(length))
// gl.Uniform1i(lgr.gradient, 0)
// gl.Uniform1f(lgr.globalAlpha, float32(cv.state.globalAlpha))
// return lgr.vertex
// }
// if rg := style.RadialGradient; rg != nil {
// rg.load()
// gl.ActiveTexture(gl.TEXTURE0)
// gl.BindTexture(gl.TEXTURE_2D, rg.tex)
// gl.UseProgram(rgr.id)
// from := cv.tf(rg.from)
// to := cv.tf(rg.to)
// dir := to.sub(from)
// length := dir.len()
// dir = dir.divf(length)
// gl.Uniform2f(rgr.canvasSize, float32(cv.fw), float32(cv.fh))
// inv := cv.state.transform.invert().f32()
// gl.UniformMatrix3fv(rgr.invmat, 1, false, &inv[0])
// gl.Uniform2f(rgr.from, float32(from[0]), float32(from[1]))
// gl.Uniform2f(rgr.to, float32(to[0]), float32(to[1]))
// gl.Uniform2f(rgr.dir, float32(dir[0]), float32(dir[1]))
// gl.Uniform1f(rgr.radFrom, float32(rg.radFrom))
// gl.Uniform1f(rgr.radTo, float32(rg.radTo))
// gl.Uniform1f(rgr.len, float32(length))
// gl.Uniform1i(rgr.gradient, 0)
// gl.Uniform1f(rgr.globalAlpha, float32(cv.state.globalAlpha))
// return rgr.vertex
// }
// if img := style.Image; img != nil {
// gl.UseProgram(ipr.id)
// gl.ActiveTexture(gl.TEXTURE0)
// gl.BindTexture(gl.TEXTURE_2D, img.tex)
// gl.Uniform2f(ipr.canvasSize, float32(cv.fw), float32(cv.fh))
// inv := cv.state.transform.invert().f32()
// gl.UniformMatrix3fv(ipr.invmat, 1, false, &inv[0])
// gl.Uniform2f(ipr.imageSize, float32(img.w), float32(img.h))
// gl.Uniform1i(ipr.image, 0)
// gl.Uniform1f(ipr.globalAlpha, float32(cv.state.globalAlpha))
// return ipr.vertex
// }
gl.UseProgram(b.sr.ID)
gl.Uniform2f(b.sr.CanvasSize, float32(b.fw), float32(b.fh))
c := colorGoToGL(style.Color)
gl.Uniform4f(b.sr.Color, float32(c.r), float32(c.g), float32(c.b), float32(c.a))
gl.Uniform1f(b.sr.GlobalAlpha, float32(style.GlobalAlpha))
return b.sr.Vertex
}
func (b *GoGLBackend) useAlphaShader(style *backendbase.Style, alphaTexSlot int32) (vertexLoc, alphaTexCoordLoc uint32) {
// if lg := style.LinearGradient; lg != nil {
// lg.load()
// gl.ActiveTexture(gl.TEXTURE0)
// gl.BindTexture(gl.TEXTURE_2D, lg.tex)
// gl.UseProgram(lgar.id)
// from := cv.tf(lg.from)
// to := cv.tf(lg.to)
// dir := to.sub(from)
// length := dir.len()
// dir = dir.divf(length)
// gl.Uniform2f(lgar.canvasSize, float32(cv.fw), float32(cv.fh))
// inv := cv.state.transform.invert().f32()
// gl.UniformMatrix3fv(lgar.invmat, 1, false, &inv[0])
// gl.Uniform2f(lgar.from, float32(from[0]), float32(from[1]))
// gl.Uniform2f(lgar.dir, float32(dir[0]), float32(dir[1]))
// gl.Uniform1f(lgar.len, float32(length))
// gl.Uniform1i(lgar.gradient, 0)
// gl.Uniform1i(lgar.alphaTex, alphaTexSlot)
// gl.Uniform1f(lgar.globalAlpha, float32(cv.state.globalAlpha))
// return lgar.vertex, lgar.alphaTexCoord
// }
// if rg := style.RadialGradient; rg != nil {
// rg.load()
// gl.ActiveTexture(gl.TEXTURE0)
// gl.BindTexture(gl.TEXTURE_2D, rg.tex)
// gl.UseProgram(rgar.id)
// from := cv.tf(rg.from)
// to := cv.tf(rg.to)
// dir := to.sub(from)
// length := dir.len()
// dir = dir.divf(length)
// gl.Uniform2f(rgar.canvasSize, float32(cv.fw), float32(cv.fh))
// inv := cv.state.transform.invert().f32()
// gl.UniformMatrix3fv(rgar.invmat, 1, false, &inv[0])
// gl.Uniform2f(rgar.from, float32(from[0]), float32(from[1]))
// gl.Uniform2f(rgar.to, float32(to[0]), float32(to[1]))
// gl.Uniform2f(rgar.dir, float32(dir[0]), float32(dir[1]))
// gl.Uniform1f(rgar.radFrom, float32(rg.radFrom))
// gl.Uniform1f(rgar.radTo, float32(rg.radTo))
// gl.Uniform1f(rgar.len, float32(length))
// gl.Uniform1i(rgar.gradient, 0)
// gl.Uniform1i(rgar.alphaTex, alphaTexSlot)
// gl.Uniform1f(rgar.globalAlpha, float32(cv.state.globalAlpha))
// return rgar.vertex, rgar.alphaTexCoord
// }
// if img := style.Image; img != nil {
// gl.UseProgram(ipar.id)
// gl.ActiveTexture(gl.TEXTURE0)
// gl.BindTexture(gl.TEXTURE_2D, img.tex)
// gl.Uniform2f(ipar.canvasSize, float32(cv.fw), float32(cv.fh))
// inv := cv.state.transform.invert().f32()
// gl.UniformMatrix3fv(ipar.invmat, 1, false, &inv[0])
// gl.Uniform2f(ipar.imageSize, float32(img.w), float32(img.h))
// gl.Uniform1i(ipar.image, 0)
// gl.Uniform1i(ipar.alphaTex, alphaTexSlot)
// gl.Uniform1f(ipar.globalAlpha, float32(cv.state.globalAlpha))
// return ipar.vertex, ipar.alphaTexCoord
// }
gl.UseProgram(b.sar.ID)
gl.Uniform2f(b.sar.CanvasSize, float32(b.fw), float32(b.fh))
c := colorGoToGL(style.Color)
gl.Uniform4f(b.sar.Color, float32(c.r), float32(c.g), float32(c.b), float32(c.a))
gl.Uniform1i(b.sar.AlphaTex, alphaTexSlot)
gl.Uniform1f(b.sar.GlobalAlpha, float32(style.GlobalAlpha))
return b.sar.Vertex, b.sar.AlphaTexCoord
}