started work on moving all the gl code into a backend package so that other backends can be created
This commit is contained in:
parent
6091afb755
commit
1e5180dd65
7 changed files with 975 additions and 7 deletions
5
backend.go
Normal file
5
backend.go
Normal file
|
@ -0,0 +1,5 @@
|
|||
package canvas
|
||||
|
||||
type Backend interface {
|
||||
ClearRect(x, y, w, h int)
|
||||
}
|
215
backend/goglbackend/gogl.go
Normal file
215
backend/goglbackend/gogl.go
Normal file
|
@ -0,0 +1,215 @@
|
|||
package goglbackend
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-gl/gl/v3.2-core/gl"
|
||||
"github.com/tfriedel6/canvas"
|
||||
)
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
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),
|
||||
}
|
||||
|
||||
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
|
||||
}
|
57
backend/goglbackend/rects.go
Normal file
57
backend/goglbackend/rects.go
Normal file
|
@ -0,0 +1,57 @@
|
|||
package goglbackend
|
||||
|
||||
import (
|
||||
"github.com/go-gl/gl/v3.2-core/gl"
|
||||
)
|
||||
|
||||
/*
|
||||
// FillRect fills a rectangle with the active fill style
|
||||
func (b *GoGLBackend) FillRect(x, y, w, h float64) {
|
||||
cv.activate()
|
||||
|
||||
p0 := cv.tf(vec{x, y})
|
||||
p1 := cv.tf(vec{x, y + h})
|
||||
p2 := cv.tf(vec{x + w, y + h})
|
||||
p3 := cv.tf(vec{x + w, y})
|
||||
|
||||
if cv.state.shadowColor.a != 0 {
|
||||
tris := [24]float32{
|
||||
0, 0,
|
||||
float32(cv.fw), 0,
|
||||
float32(cv.fw), float32(cv.fh),
|
||||
0, 0,
|
||||
float32(cv.fw), float32(cv.fh),
|
||||
0, float32(cv.fh),
|
||||
float32(p0[0]), float32(p0[1]),
|
||||
float32(p3[0]), float32(p3[1]),
|
||||
float32(p2[0]), float32(p2[1]),
|
||||
float32(p0[0]), float32(p0[1]),
|
||||
float32(p2[0]), float32(p2[1]),
|
||||
float32(p1[0]), float32(p1[1]),
|
||||
}
|
||||
cv.drawShadow(tris[:])
|
||||
}
|
||||
|
||||
gl.BindBuffer(gl_ARRAY_BUFFER, buf)
|
||||
data := [8]float32{float32(p0[0]), float32(p0[1]), float32(p1[0]), float32(p1[1]), float32(p2[0]), float32(p2[1]), float32(p3[0]), float32(p3[1])}
|
||||
gl.BufferData(gl_ARRAY_BUFFER, len(data)*4, unsafe.Pointer(&data[0]), gl_STREAM_DRAW)
|
||||
|
||||
gl.StencilFunc(gl_EQUAL, 0, 0xFF)
|
||||
|
||||
vertex := cv.useShader(&cv.state.fill)
|
||||
gl.VertexAttribPointer(vertex, 2, gl_FLOAT, false, 0, 0)
|
||||
gl.EnableVertexAttribArray(vertex)
|
||||
gl.DrawArrays(gl_TRIANGLE_FAN, 0, 4)
|
||||
gl.DisableVertexAttribArray(vertex)
|
||||
|
||||
gl.StencilFunc(gl_ALWAYS, 0, 0xFF)
|
||||
}
|
||||
*/
|
||||
|
||||
// ClearRect sets the color of the rectangle to transparent black
|
||||
func (b *GoGLBackend) ClearRect(x, y, w, h int) {
|
||||
gl.Scissor(int32(x), int32(b.h-y-h), int32(w), int32(h))
|
||||
gl.ClearColor(0, 0, 0, 0)
|
||||
gl.Clear(gl.COLOR_BUFFER_BIT)
|
||||
// cv.applyScissor()
|
||||
}
|
201
backend/goglbackend/shader.go
Normal file
201
backend/goglbackend/shader.go
Normal file
|
@ -0,0 +1,201 @@
|
|||
package goglbackend
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/go-gl/gl/v3.2-core/gl"
|
||||
)
|
||||
|
||||
type shaderProgram struct {
|
||||
ID, vs, fs uint32
|
||||
attribs map[string]uint32
|
||||
uniforms map[string]int32
|
||||
}
|
||||
|
||||
func loadShader(vs, fs string, sp *shaderProgram) error {
|
||||
glError() // clear the current error
|
||||
|
||||
// compile vertex shader
|
||||
{
|
||||
sp.vs = gl.CreateShader(gl.VERTEX_SHADER)
|
||||
csrc, freeFunc := gl.Strs(vs + "\x00")
|
||||
defer freeFunc()
|
||||
gl.ShaderSource(sp.vs, 1, csrc, nil)
|
||||
gl.CompileShader(sp.vs)
|
||||
|
||||
var status int32
|
||||
gl.GetShaderiv(sp.vs, gl.COMPILE_STATUS, &status)
|
||||
if status != gl.TRUE {
|
||||
var buf [65536]byte
|
||||
var length int32
|
||||
gl.GetShaderInfoLog(sp.vs, int32(len(buf)), &length, &buf[0])
|
||||
clog := string(buf[:length])
|
||||
gl.DeleteShader(sp.vs)
|
||||
return fmt.Errorf("failed to compile vertex shader:\n\n%s", clog)
|
||||
}
|
||||
if err := glError(); err != nil {
|
||||
return fmt.Errorf("gl error after compiling vertex shader: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// compile fragment shader
|
||||
{
|
||||
sp.fs = gl.CreateShader(gl.FRAGMENT_SHADER)
|
||||
csrc, freeFunc := gl.Strs(fs + "\x00")
|
||||
defer freeFunc()
|
||||
gl.ShaderSource(sp.fs, 1, csrc, nil)
|
||||
gl.CompileShader(sp.fs)
|
||||
|
||||
var status int32
|
||||
gl.GetShaderiv(sp.fs, gl.COMPILE_STATUS, &status)
|
||||
if status != gl.TRUE {
|
||||
var buf [65536]byte
|
||||
var length int32
|
||||
gl.GetShaderInfoLog(sp.fs, int32(len(buf)), &length, &buf[0])
|
||||
clog := string(buf[:length])
|
||||
gl.DeleteShader(sp.fs)
|
||||
return fmt.Errorf("failed to compile fragment shader:\n\n%s", clog)
|
||||
}
|
||||
if err := glError(); err != nil {
|
||||
return fmt.Errorf("gl error after compiling fragment shader: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// link shader program
|
||||
{
|
||||
sp.ID = gl.CreateProgram()
|
||||
gl.AttachShader(sp.ID, sp.vs)
|
||||
gl.AttachShader(sp.ID, sp.fs)
|
||||
gl.LinkProgram(sp.ID)
|
||||
|
||||
var status int32
|
||||
gl.GetProgramiv(sp.ID, gl.LINK_STATUS, &status)
|
||||
if status != gl.TRUE {
|
||||
var buf [65536]byte
|
||||
var length int32
|
||||
gl.GetProgramInfoLog(sp.ID, int32(len(buf)), &length, &buf[0])
|
||||
clog := string(buf[:length])
|
||||
gl.DeleteProgram(sp.ID)
|
||||
gl.DeleteShader(sp.vs)
|
||||
gl.DeleteShader(sp.fs)
|
||||
return fmt.Errorf("failed to link shader program:\n\n%s", clog)
|
||||
}
|
||||
if err := glError(); err != nil {
|
||||
return fmt.Errorf("gl error after linking shader: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
gl.UseProgram(sp.ID)
|
||||
var nameBuf [256]byte
|
||||
var length, size int32
|
||||
var xtype uint32
|
||||
var count int32
|
||||
|
||||
// load the attributes
|
||||
gl.GetProgramiv(sp.ID, gl.ACTIVE_ATTRIBUTES, &count)
|
||||
sp.attribs = make(map[string]uint32, int(count))
|
||||
for i := int32(0); i < count; i++ {
|
||||
gl.GetActiveAttrib(sp.ID, uint32(i), int32(len(nameBuf)), &length, &size, &xtype, &nameBuf[0])
|
||||
name := string(nameBuf[:length])
|
||||
loc := gl.GetAttribLocation(sp.ID, &nameBuf[0])
|
||||
sp.attribs[name] = uint32(loc)
|
||||
}
|
||||
|
||||
// load the uniforms
|
||||
gl.GetProgramiv(sp.ID, gl.ACTIVE_UNIFORMS, &count)
|
||||
sp.uniforms = make(map[string]int32, int(count))
|
||||
for i := int32(0); i < count; i++ {
|
||||
gl.GetActiveUniform(sp.ID, uint32(i), int32(len(nameBuf)), &length, &size, &xtype, &nameBuf[0])
|
||||
name := string(nameBuf[:length])
|
||||
loc := gl.GetUniformLocation(sp.ID, &nameBuf[0])
|
||||
sp.uniforms[name] = loc
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sp *shaderProgram) use() {
|
||||
gl.UseProgram(sp.ID)
|
||||
}
|
||||
|
||||
func (sp *shaderProgram) delete() {
|
||||
gl.DeleteProgram(sp.ID)
|
||||
gl.DeleteShader(sp.vs)
|
||||
gl.DeleteShader(sp.fs)
|
||||
}
|
||||
|
||||
func (sp *shaderProgram) loadLocations(target interface{}) error {
|
||||
val := reflect.ValueOf(target)
|
||||
if val.Kind() != reflect.Ptr {
|
||||
panic("target must be a pointer to a struct")
|
||||
}
|
||||
val = val.Elem()
|
||||
if val.Kind() != reflect.Struct {
|
||||
panic("target must be a pointer to a struct")
|
||||
}
|
||||
|
||||
gl.UseProgram(sp.ID)
|
||||
|
||||
var errs strings.Builder
|
||||
|
||||
for name, loc := range sp.attribs {
|
||||
field := val.FieldByName(sp.structName(name))
|
||||
if field == (reflect.Value{}) {
|
||||
fmt.Fprintf(&errs, "field for attribute \"%s\" not found; ", name)
|
||||
} else if field.Type() != reflect.TypeOf(uint32(0)) {
|
||||
fmt.Fprintf(&errs, "field for attribute \"%s\" must have type uint32; ", name)
|
||||
} else {
|
||||
field.Set(reflect.ValueOf(uint32(loc)))
|
||||
}
|
||||
}
|
||||
|
||||
for name, loc := range sp.uniforms {
|
||||
field := val.FieldByName(sp.structName(name))
|
||||
if field == (reflect.Value{}) {
|
||||
fmt.Fprintf(&errs, "field for uniform \"%s\" not found; ", name)
|
||||
} else if field.Type() != reflect.TypeOf(int32(0)) {
|
||||
fmt.Fprintf(&errs, "field for uniform \"%s\" must have type int32; ", name)
|
||||
} else {
|
||||
field.Set(reflect.ValueOf(int32(loc)))
|
||||
}
|
||||
}
|
||||
|
||||
if errs.Len() > 0 {
|
||||
return errors.New(strings.TrimSpace(errs.String()))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sp *shaderProgram) structName(name string) string {
|
||||
rn, sz := utf8.DecodeRuneInString(name)
|
||||
name = fmt.Sprintf("%c%s", unicode.ToUpper(rn), name[sz:])
|
||||
idx := strings.IndexByte(name, '[')
|
||||
if idx > 0 {
|
||||
name = name[:idx]
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
func (sp *shaderProgram) mustLoadLocations(target interface{}) {
|
||||
err := sp.loadLocations(target)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (sp *shaderProgram) enableAllVertexAttribArrays() {
|
||||
for _, loc := range sp.attribs {
|
||||
gl.EnableVertexAttribArray(loc)
|
||||
}
|
||||
}
|
||||
|
||||
func (sp *shaderProgram) disableAllVertexAttribArrays() {
|
||||
for _, loc := range sp.attribs {
|
||||
gl.DisableVertexAttribArray(loc)
|
||||
}
|
||||
}
|
490
backend/goglbackend/shaders.go
Executable file
490
backend/goglbackend/shaders.go
Executable file
|
@ -0,0 +1,490 @@
|
|||
package goglbackend
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var imageVS = `
|
||||
attribute vec2 vertex, texCoord;
|
||||
uniform vec2 canvasSize;
|
||||
varying vec2 v_texCoord;
|
||||
void main() {
|
||||
v_texCoord = texCoord;
|
||||
vec2 glp = vertex * 2.0 / canvasSize - 1.0;
|
||||
gl_Position = vec4(glp.x, -glp.y, 0.0, 1.0);
|
||||
}`
|
||||
var imageFS = `
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
varying vec2 v_texCoord;
|
||||
uniform sampler2D image;
|
||||
uniform float globalAlpha;
|
||||
void main() {
|
||||
vec4 col = texture2D(image, v_texCoord);
|
||||
col.a *= globalAlpha;
|
||||
gl_FragColor = col;
|
||||
}`
|
||||
|
||||
var solidVS = `
|
||||
attribute vec2 vertex;
|
||||
uniform vec2 canvasSize;
|
||||
void main() {
|
||||
vec2 glp = vertex * 2.0 / canvasSize - 1.0;
|
||||
gl_Position = vec4(glp.x, -glp.y, 0.0, 1.0);
|
||||
}`
|
||||
var solidFS = `
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
uniform vec4 color;
|
||||
uniform float globalAlpha;
|
||||
void main() {
|
||||
vec4 col = color;
|
||||
col.a *= globalAlpha;
|
||||
gl_FragColor = col;
|
||||
}`
|
||||
|
||||
var linearGradientVS = `
|
||||
attribute vec2 vertex;
|
||||
uniform vec2 canvasSize;
|
||||
varying vec2 v_cp;
|
||||
void main() {
|
||||
v_cp = vertex;
|
||||
vec2 glp = vertex * 2.0 / canvasSize - 1.0;
|
||||
gl_Position = vec4(glp.x, -glp.y, 0.0, 1.0);
|
||||
}`
|
||||
var linearGradientFS = `
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
varying vec2 v_cp;
|
||||
uniform mat3 invmat;
|
||||
uniform sampler2D gradient;
|
||||
uniform vec2 from, dir;
|
||||
uniform float len;
|
||||
uniform float globalAlpha;
|
||||
void main() {
|
||||
vec3 untf = vec3(v_cp, 1.0) * invmat;
|
||||
vec2 v = untf.xy - from;
|
||||
float r = dot(v, dir) / len;
|
||||
r = clamp(r, 0.0, 1.0);
|
||||
vec4 col = texture2D(gradient, vec2(r, 0.0));
|
||||
col.a *= globalAlpha;
|
||||
gl_FragColor = col;
|
||||
}`
|
||||
|
||||
var radialGradientVS = `
|
||||
attribute vec2 vertex;
|
||||
uniform vec2 canvasSize;
|
||||
varying vec2 v_cp;
|
||||
void main() {
|
||||
v_cp = vertex;
|
||||
vec2 glp = vertex * 2.0 / canvasSize - 1.0;
|
||||
gl_Position = vec4(glp.x, -glp.y, 0.0, 1.0);
|
||||
}`
|
||||
var radialGradientFS = `
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
varying vec2 v_cp;
|
||||
uniform mat3 invmat;
|
||||
uniform sampler2D gradient;
|
||||
uniform vec2 from, to, dir;
|
||||
uniform float radFrom, radTo;
|
||||
uniform float len;
|
||||
uniform float globalAlpha;
|
||||
bool isNaN(float v) {
|
||||
return v < 0.0 || 0.0 < v || v == 0.0 ? false : true;
|
||||
}
|
||||
void main() {
|
||||
vec3 untf = vec3(v_cp, 1.0) * invmat;
|
||||
float o_a = 0.5 * sqrt(
|
||||
pow(-2.0*from.x*from.x+2.0*from.x*to.x+2.0*from.x*untf.x-2.0*to.x*untf.x-2.0*from.y*from.y+2.0*from.y*to.y+2.0*from.y*untf.y-2.0*to.y*untf.y+2.0*radFrom*radFrom-2.0*radFrom*radTo, 2.0)
|
||||
-4.0*(from.x*from.x-2.0*from.x*untf.x+untf.x*untf.x+from.y*from.y-2.0*from.y*untf.y+untf.y*untf.y-radFrom*radFrom)
|
||||
*(from.x*from.x-2.0*from.x*to.x+to.x*to.x+from.y*from.y-2.0*from.y*to.y+to.y*to.y-radFrom*radFrom+2.0*radFrom*radTo-radTo*radTo)
|
||||
);
|
||||
float o_b = (from.x*from.x-from.x*to.x-from.x*untf.x+to.x*untf.x+from.y*from.y-from.y*to.y-from.y*untf.y+to.y*untf.y-radFrom*radFrom+radFrom*radTo);
|
||||
float o_c = (from.x*from.x-2.0*from.x*to.x+to.x*to.x+from.y*from.y-2.0*from.y*to.y+to.y*to.y-radFrom*radFrom+2.0*radFrom*radTo-radTo*radTo);
|
||||
float o1 = (-o_a + o_b) / o_c;
|
||||
float o2 = (o_a + o_b) / o_c;
|
||||
if (isNaN(o1) && isNaN(o2)) {
|
||||
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
return;
|
||||
}
|
||||
float o = max(o1, o2);
|
||||
//float r = radFrom + o * (radTo - radFrom);
|
||||
vec4 col = texture2D(gradient, vec2(o, 0.0));
|
||||
col.a *= globalAlpha;
|
||||
gl_FragColor = col;
|
||||
}`
|
||||
|
||||
var imagePatternVS = `
|
||||
attribute vec2 vertex;
|
||||
uniform vec2 canvasSize;
|
||||
varying vec2 v_cp;
|
||||
void main() {
|
||||
v_cp = vertex;
|
||||
vec2 glp = vertex * 2.0 / canvasSize - 1.0;
|
||||
gl_Position = vec4(glp.x, -glp.y, 0.0, 1.0);
|
||||
}`
|
||||
var imagePatternFS = `
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
varying vec2 v_cp;
|
||||
uniform vec2 imageSize;
|
||||
uniform mat3 invmat;
|
||||
uniform sampler2D image;
|
||||
uniform float globalAlpha;
|
||||
void main() {
|
||||
vec3 untf = vec3(v_cp, 1.0) * invmat;
|
||||
vec4 col = texture2D(image, mod(untf.xy / imageSize, 1.0));
|
||||
col.a *= globalAlpha;
|
||||
gl_FragColor = col;
|
||||
}`
|
||||
|
||||
var solidAlphaVS = `
|
||||
attribute vec2 vertex, alphaTexCoord;
|
||||
uniform vec2 canvasSize;
|
||||
varying vec2 v_atc;
|
||||
void main() {
|
||||
v_atc = alphaTexCoord;
|
||||
vec2 glp = vertex * 2.0 / canvasSize - 1.0;
|
||||
gl_Position = vec4(glp.x, -glp.y, 0.0, 1.0);
|
||||
}`
|
||||
var solidAlphaFS = `
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
varying vec2 v_atc;
|
||||
uniform vec4 color;
|
||||
uniform sampler2D alphaTex;
|
||||
uniform float globalAlpha;
|
||||
void main() {
|
||||
vec4 col = color;
|
||||
col.a *= texture2D(alphaTex, v_atc).a * globalAlpha;
|
||||
gl_FragColor = col;
|
||||
}`
|
||||
|
||||
var linearGradientAlphaVS = `
|
||||
attribute vec2 vertex, alphaTexCoord;
|
||||
uniform vec2 canvasSize;
|
||||
varying vec2 v_cp;
|
||||
varying vec2 v_atc;
|
||||
void main() {
|
||||
v_cp = vertex;
|
||||
v_atc = alphaTexCoord;
|
||||
vec2 glp = vertex * 2.0 / canvasSize - 1.0;
|
||||
gl_Position = vec4(glp.x, -glp.y, 0.0, 1.0);
|
||||
}`
|
||||
var linearGradientAlphaFS = `
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
varying vec2 v_cp;
|
||||
varying vec2 v_atc;
|
||||
varying vec2 v_texCoord;
|
||||
uniform mat3 invmat;
|
||||
uniform sampler2D gradient;
|
||||
uniform vec2 from, dir;
|
||||
uniform float len;
|
||||
uniform sampler2D alphaTex;
|
||||
uniform float globalAlpha;
|
||||
void main() {
|
||||
vec3 untf = vec3(v_cp, 1.0) * invmat;
|
||||
vec2 v = untf.xy - from;
|
||||
float r = dot(v, dir) / len;
|
||||
r = clamp(r, 0.0, 1.0);
|
||||
vec4 col = texture2D(gradient, vec2(r, 0.0));
|
||||
col.a *= texture2D(alphaTex, v_atc).a * globalAlpha;
|
||||
gl_FragColor = col;
|
||||
}`
|
||||
|
||||
var radialGradientAlphaVS = `
|
||||
attribute vec2 vertex, alphaTexCoord;
|
||||
uniform vec2 canvasSize;
|
||||
varying vec2 v_cp;
|
||||
varying vec2 v_atc;
|
||||
void main() {
|
||||
v_cp = vertex;
|
||||
v_atc = alphaTexCoord;
|
||||
vec2 glp = vertex * 2.0 / canvasSize - 1.0;
|
||||
gl_Position = vec4(glp.x, -glp.y, 0.0, 1.0);
|
||||
}`
|
||||
var radialGradientAlphaFS = `
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
varying vec2 v_cp;
|
||||
varying vec2 v_atc;
|
||||
uniform mat3 invmat;
|
||||
uniform sampler2D gradient;
|
||||
uniform vec2 from, to, dir;
|
||||
uniform float radFrom, radTo;
|
||||
uniform float len;
|
||||
uniform sampler2D alphaTex;
|
||||
uniform float globalAlpha;
|
||||
bool isNaN(float v) {
|
||||
return v < 0.0 || 0.0 < v || v == 0.0 ? false : true;
|
||||
}
|
||||
void main() {
|
||||
vec3 untf = vec3(v_cp, 1.0) * invmat;
|
||||
float o_a = 0.5 * sqrt(
|
||||
pow(-2.0*from.x*from.x+2.0*from.x*to.x+2.0*from.x*untf.x-2.0*to.x*untf.x-2.0*from.y*from.y+2.0*from.y*to.y+2.0*from.y*untf.y-2.0*to.y*untf.y+2.0*radFrom*radFrom-2.0*radFrom*radTo, 2.0)
|
||||
-4.0*(from.x*from.x-2.0*from.x*untf.x+untf.x*untf.x+from.y*from.y-2.0*from.y*untf.y+untf.y*untf.y-radFrom*radFrom)
|
||||
*(from.x*from.x-2.0*from.x*to.x+to.x*to.x+from.y*from.y-2.0*from.y*to.y+to.y*to.y-radFrom*radFrom+2.0*radFrom*radTo-radTo*radTo)
|
||||
);
|
||||
float o_b = (from.x*from.x-from.x*to.x-from.x*untf.x+to.x*untf.x+from.y*from.y-from.y*to.y-from.y*untf.y+to.y*untf.y-radFrom*radFrom+radFrom*radTo);
|
||||
float o_c = (from.x*from.x-2.0*from.x*to.x+to.x*to.x+from.y*from.y-2.0*from.y*to.y+to.y*to.y-radFrom*radFrom+2.0*radFrom*radTo-radTo*radTo);
|
||||
float o1 = (-o_a + o_b) / o_c;
|
||||
float o2 = (o_a + o_b) / o_c;
|
||||
if (isNaN(o1) && isNaN(o2)) {
|
||||
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
return;
|
||||
}
|
||||
float o = max(o1, o2);
|
||||
float r = radFrom + o * (radTo - radFrom);
|
||||
vec4 col = texture2D(gradient, vec2(o, 0.0));
|
||||
col.a *= texture2D(alphaTex, v_atc).a * globalAlpha;
|
||||
gl_FragColor = col;
|
||||
}`
|
||||
|
||||
var imagePatternAlphaVS = `
|
||||
attribute vec2 vertex, alphaTexCoord;
|
||||
uniform vec2 canvasSize;
|
||||
varying vec2 v_cp;
|
||||
varying vec2 v_atc;
|
||||
void main() {
|
||||
v_cp = vertex;
|
||||
v_atc = alphaTexCoord;
|
||||
vec2 glp = vertex * 2.0 / canvasSize - 1.0;
|
||||
gl_Position = vec4(glp.x, -glp.y, 0.0, 1.0);
|
||||
}`
|
||||
var imagePatternAlphaFS = `
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
varying vec2 v_cp;
|
||||
varying vec2 v_atc;
|
||||
uniform vec2 imageSize;
|
||||
uniform mat3 invmat;
|
||||
uniform sampler2D image;
|
||||
uniform sampler2D alphaTex;
|
||||
uniform float globalAlpha;
|
||||
void main() {
|
||||
vec3 untf = vec3(v_cp, 1.0) * invmat;
|
||||
vec4 col = texture2D(image, mod(untf.xy / imageSize, 1.0));
|
||||
col.a *= texture2D(alphaTex, v_atc).a * globalAlpha;
|
||||
gl_FragColor = col;
|
||||
}`
|
||||
|
||||
var gaussian15VS = `
|
||||
attribute vec2 vertex, texCoord;
|
||||
uniform vec2 canvasSize;
|
||||
varying vec2 v_texCoord;
|
||||
void main() {
|
||||
v_texCoord = texCoord;
|
||||
vec2 glp = vertex * 2.0 / canvasSize - 1.0;
|
||||
gl_Position = vec4(glp.x, -glp.y, 0.0, 1.0);
|
||||
}`
|
||||
var gaussian15FS = `
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
varying vec2 v_texCoord;
|
||||
uniform vec2 kernelScale;
|
||||
uniform sampler2D image;
|
||||
uniform float kernel[15];
|
||||
void main() {
|
||||
vec4 color = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
_SUM_
|
||||
gl_FragColor = color;
|
||||
}`
|
||||
|
||||
var gaussian63VS = `
|
||||
attribute vec2 vertex, texCoord;
|
||||
uniform vec2 canvasSize;
|
||||
varying vec2 v_texCoord;
|
||||
void main() {
|
||||
v_texCoord = texCoord;
|
||||
vec2 glp = vertex * 2.0 / canvasSize - 1.0;
|
||||
gl_Position = vec4(glp.x, -glp.y, 0.0, 1.0);
|
||||
}`
|
||||
var gaussian63FS = `
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
varying vec2 v_texCoord;
|
||||
uniform vec2 kernelScale;
|
||||
uniform sampler2D image;
|
||||
uniform float kernel[63];
|
||||
void main() {
|
||||
vec4 color = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
_SUM_
|
||||
gl_FragColor = color;
|
||||
}`
|
||||
|
||||
var gaussian127VS = `
|
||||
attribute vec2 vertex, texCoord;
|
||||
uniform vec2 canvasSize;
|
||||
varying vec2 v_texCoord;
|
||||
void main() {
|
||||
v_texCoord = texCoord;
|
||||
vec2 glp = vertex * 2.0 / canvasSize - 1.0;
|
||||
gl_Position = vec4(glp.x, -glp.y, 0.0, 1.0);
|
||||
}`
|
||||
var gaussian127FS = `
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
varying vec2 v_texCoord;
|
||||
uniform vec2 kernelScale;
|
||||
uniform sampler2D image;
|
||||
uniform float kernel[127];
|
||||
void main() {
|
||||
vec4 color = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
_SUM_
|
||||
gl_FragColor = color;
|
||||
}`
|
||||
|
||||
func init() {
|
||||
fstr := "\tcolor += texture2D(image, v_texCoord + vec2(%.1f * kernelScale.x, %.1f * kernelScale.y)) * kernel[%d];\n"
|
||||
bb := bytes.Buffer{}
|
||||
for i := 0; i < 127; i++ {
|
||||
off := float64(i) - 63
|
||||
fmt.Fprintf(&bb, fstr, off, off, i)
|
||||
}
|
||||
gaussian127FS = strings.Replace(gaussian127FS, "_SUM_", bb.String(), -1)
|
||||
bb.Reset()
|
||||
for i := 0; i < 63; i++ {
|
||||
off := float64(i) - 31
|
||||
fmt.Fprintf(&bb, fstr, off, off, i)
|
||||
}
|
||||
gaussian63FS = strings.Replace(gaussian63FS, "_SUM_", bb.String(), -1)
|
||||
bb.Reset()
|
||||
for i := 0; i < 15; i++ {
|
||||
off := float64(i) - 7
|
||||
fmt.Fprintf(&bb, fstr, off, off, i)
|
||||
}
|
||||
gaussian15FS = strings.Replace(gaussian15FS, "_SUM_", bb.String(), -1)
|
||||
}
|
||||
|
||||
type solidShader struct {
|
||||
shaderProgram
|
||||
Vertex uint32
|
||||
CanvasSize int32
|
||||
Color int32
|
||||
GlobalAlpha int32
|
||||
}
|
||||
|
||||
type imageShader struct {
|
||||
shaderProgram
|
||||
Vertex uint32
|
||||
TexCoord uint32
|
||||
CanvasSize int32
|
||||
Image int32
|
||||
GlobalAlpha int32
|
||||
}
|
||||
|
||||
type linearGradientShader struct {
|
||||
shaderProgram
|
||||
Vertex uint32
|
||||
CanvasSize int32
|
||||
Invmat int32
|
||||
Gradient int32
|
||||
From int32
|
||||
Dir int32
|
||||
Len int32
|
||||
GlobalAlpha int32
|
||||
}
|
||||
|
||||
type radialGradientShader struct {
|
||||
shaderProgram
|
||||
Vertex uint32
|
||||
CanvasSize int32
|
||||
Invmat int32
|
||||
Gradient int32
|
||||
From int32
|
||||
To int32
|
||||
Dir int32
|
||||
RadFrom int32
|
||||
RadTo int32
|
||||
Len int32
|
||||
GlobalAlpha int32
|
||||
}
|
||||
|
||||
type imagePatternShader struct {
|
||||
shaderProgram
|
||||
Vertex uint32
|
||||
CanvasSize int32
|
||||
ImageSize int32
|
||||
Invmat int32
|
||||
Image int32
|
||||
GlobalAlpha int32
|
||||
}
|
||||
|
||||
type solidAlphaShader struct {
|
||||
shaderProgram
|
||||
Vertex uint32
|
||||
AlphaTexCoord uint32
|
||||
CanvasSize int32
|
||||
Color int32
|
||||
AlphaTex int32
|
||||
GlobalAlpha int32
|
||||
}
|
||||
|
||||
type linearGradientAlphaShader struct {
|
||||
shaderProgram
|
||||
Vertex uint32
|
||||
AlphaTexCoord uint32
|
||||
CanvasSize int32
|
||||
Invmat int32
|
||||
Gradient int32
|
||||
From int32
|
||||
Dir int32
|
||||
Len int32
|
||||
AlphaTex int32
|
||||
GlobalAlpha int32
|
||||
}
|
||||
|
||||
type radialGradientAlphaShader struct {
|
||||
shaderProgram
|
||||
Vertex uint32
|
||||
AlphaTexCoord uint32
|
||||
CanvasSize int32
|
||||
Invmat int32
|
||||
Gradient int32
|
||||
From int32
|
||||
To int32
|
||||
Dir int32
|
||||
RadFrom int32
|
||||
RadTo int32
|
||||
Len int32
|
||||
AlphaTex int32
|
||||
GlobalAlpha int32
|
||||
}
|
||||
|
||||
type imagePatternAlphaShader struct {
|
||||
shaderProgram
|
||||
Vertex uint32
|
||||
AlphaTexCoord uint32
|
||||
CanvasSize int32
|
||||
ImageSize int32
|
||||
Invmat int32
|
||||
Image int32
|
||||
AlphaTex int32
|
||||
GlobalAlpha int32
|
||||
}
|
||||
|
||||
type gaussianShader struct {
|
||||
shaderProgram
|
||||
Vertex uint32
|
||||
TexCoord uint32
|
||||
CanvasSize int32
|
||||
KernelScale int32
|
||||
Image int32
|
||||
Kernel int32
|
||||
}
|
10
canvas.go
10
canvas.go
|
@ -16,6 +16,8 @@ import (
|
|||
// Canvas represents an area on the viewport on which to draw
|
||||
// using a set of functions very similar to the HTML5 canvas
|
||||
type Canvas struct {
|
||||
b Backend
|
||||
|
||||
x, y, w, h int
|
||||
fx, fy, fw, fh float64
|
||||
|
||||
|
@ -134,11 +136,11 @@ var Performance = struct {
|
|||
// While all functions on the canvas use the top left point as
|
||||
// the origin, since GL uses the bottom left coordinate, the
|
||||
// coordinates given here also use the bottom left as origin
|
||||
func New(x, y, w, h int) *Canvas {
|
||||
func New(backend Backend, x, y, w, h int) *Canvas {
|
||||
if gli == nil {
|
||||
panic("LoadGL must be called before a canvas can be created")
|
||||
}
|
||||
cv := &Canvas{stateStack: make([]drawState, 0, 20)}
|
||||
cv := &Canvas{b: backend, stateStack: make([]drawState, 0, 20)}
|
||||
cv.SetBounds(x, y, w, h)
|
||||
cv.state.lineWidth = 1
|
||||
cv.state.lineAlpha = 1
|
||||
|
@ -154,8 +156,8 @@ func New(x, y, w, h int) *Canvas {
|
|||
// does not render directly to the screen but renders to a
|
||||
// texture instead. If alpha is set to true, the offscreen
|
||||
// canvas will have an alpha channel
|
||||
func NewOffscreen(w, h int, alpha bool) *Canvas {
|
||||
cv := New(0, 0, w, h)
|
||||
func NewOffscreen(backend Backend, w, h int, alpha bool) *Canvas {
|
||||
cv := New(backend, 0, 0, w, h)
|
||||
cv.offscreen = true
|
||||
cv.offscrBuf.alpha = alpha
|
||||
return cv
|
||||
|
|
4
paths.go
4
paths.go
|
@ -666,9 +666,7 @@ func (cv *Canvas) ClearRect(x, y, w, h float64) {
|
|||
cv.activate()
|
||||
|
||||
if cv.state.transform == matIdentity() {
|
||||
gli.Scissor(int32(x+0.5), int32(cv.fh-y-h+0.5), int32(w+0.5), int32(h+0.5))
|
||||
gli.ClearColor(0, 0, 0, 0)
|
||||
gli.Clear(gl_COLOR_BUFFER_BIT)
|
||||
cv.b.ClearRect(int(x+0.5), int(y+0.5), int(w+0.5), int(h+0.5))
|
||||
cv.applyScissor()
|
||||
return
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue