added linear gradients
This commit is contained in:
parent
49eb9de8e6
commit
7edac03910
3 changed files with 306 additions and 18 deletions
111
canvas.go
111
canvas.go
|
@ -36,7 +36,8 @@ type pathPoint struct {
|
||||||
type drawState struct {
|
type drawState struct {
|
||||||
transform lm.Mat3x3
|
transform lm.Mat3x3
|
||||||
fill struct {
|
fill struct {
|
||||||
color glColor
|
color glColor
|
||||||
|
linearGradient *LinearGradient
|
||||||
}
|
}
|
||||||
stroke struct {
|
stroke struct {
|
||||||
color glColor
|
color glColor
|
||||||
|
@ -137,6 +138,7 @@ var (
|
||||||
buf uint32
|
buf uint32
|
||||||
sr *solidShader
|
sr *solidShader
|
||||||
tr *textureShader
|
tr *textureShader
|
||||||
|
lgr *linearGradientShader
|
||||||
glChan = make(chan func())
|
glChan = make(chan func())
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -163,6 +165,15 @@ func LoadGL(glimpl GL) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lgr, err = loadLinearGradientShader()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = glError()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
gli.GenBuffers(1, &buf)
|
gli.GenBuffers(1, &buf)
|
||||||
err = glError()
|
err = glError()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -211,6 +222,30 @@ void main() {
|
||||||
gl_FragColor = texture2D(image, v_texCoord);
|
gl_FragColor = texture2D(image, v_texCoord);
|
||||||
}`
|
}`
|
||||||
|
|
||||||
|
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 sampler1D gradient;
|
||||||
|
uniform vec2 from, dir;
|
||||||
|
uniform float length;
|
||||||
|
void main() {
|
||||||
|
vec2 v = v_cp - from;
|
||||||
|
float r = dot(v, dir) / length;
|
||||||
|
r = clamp(r, 0.0, 1.0);
|
||||||
|
gl_FragColor = texture1D(gradient, r);
|
||||||
|
}`
|
||||||
|
|
||||||
func glError() error {
|
func glError() error {
|
||||||
glErr := gli.GetError()
|
glErr := gli.GetError()
|
||||||
if glErr != gl_NO_ERROR {
|
if glErr != gl_NO_ERROR {
|
||||||
|
@ -219,8 +254,17 @@ func glError() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetFillColor sets the color for any fill calls
|
// SetFillStyle sets the color or gradient for any fill calls
|
||||||
func (cv *Canvas) SetFillColor(value ...interface{}) {
|
func (cv *Canvas) SetFillStyle(value ...interface{}) {
|
||||||
|
cv.state.fill.color = glColor{}
|
||||||
|
cv.state.fill.linearGradient = nil
|
||||||
|
if len(value) == 1 {
|
||||||
|
switch v := value[0].(type) {
|
||||||
|
case *LinearGradient:
|
||||||
|
cv.state.fill.linearGradient = v
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
c, ok := parseColor(value...)
|
c, ok := parseColor(value...)
|
||||||
if ok {
|
if ok {
|
||||||
cv.state.fill.color = c
|
cv.state.fill.color = c
|
||||||
|
@ -316,25 +360,56 @@ func (cv *Canvas) SetTransform(a, b, c, d, e, f float32) {
|
||||||
cv.state.transform = lm.Mat3x3{a, b, 0, c, d, 0, e, f, 1}
|
cv.state.transform = lm.Mat3x3{a, b, 0, c, d, 0, e, f, 1}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FillRect fills a rectangle with the active color
|
// FillRect fills a rectangle with the active fill style
|
||||||
func (cv *Canvas) FillRect(x, y, w, h float32) {
|
func (cv *Canvas) FillRect(x, y, w, h float32) {
|
||||||
cv.activate()
|
cv.activate()
|
||||||
|
|
||||||
gli.UseProgram(sr.id)
|
if lg := cv.state.fill.linearGradient; lg != nil {
|
||||||
|
p0 := cv.tf(lm.Vec2{x, y})
|
||||||
|
p1 := cv.tf(lm.Vec2{x, y + h})
|
||||||
|
p2 := cv.tf(lm.Vec2{x + w, y + h})
|
||||||
|
p3 := cv.tf(lm.Vec2{x + w, y})
|
||||||
|
|
||||||
x0f, y0f := cv.tfToGL(x, y)
|
gli.BindBuffer(gl_ARRAY_BUFFER, buf)
|
||||||
x1f, y1f := cv.tfToGL(x, y+h)
|
data := [8]float32{p0[0], p0[1], p1[0], p1[1], p2[0], p2[1], p3[0], p3[1]}
|
||||||
x2f, y2f := cv.tfToGL(x+w, y+h)
|
gli.BufferData(gl_ARRAY_BUFFER, len(data)*4, unsafe.Pointer(&data[0]), gl_STREAM_DRAW)
|
||||||
x3f, y3f := cv.tfToGL(x+w, y)
|
|
||||||
|
|
||||||
gli.BindBuffer(gl_ARRAY_BUFFER, buf)
|
lg.load()
|
||||||
data := [8]float32{x0f, y0f, x1f, y1f, x2f, y2f, x3f, y3f}
|
gli.UseProgram(lgr.id)
|
||||||
gli.BufferData(gl_ARRAY_BUFFER, len(data)*4, unsafe.Pointer(&data[0]), gl_STREAM_DRAW)
|
gli.VertexAttribPointer(lgr.vertex, 2, gl_FLOAT, false, 0, nil)
|
||||||
|
gli.ActiveTexture(gl_TEXTURE0)
|
||||||
|
gli.BindTexture(gl_TEXTURE_1D, lg.tex)
|
||||||
|
|
||||||
gli.VertexAttribPointer(sr.vertex, 2, gl_FLOAT, false, 0, nil)
|
gli.Uniform2f(lgr.canvasSize, cv.fw, cv.fh)
|
||||||
c := cv.state.fill.color
|
from := cv.tf(lg.from)
|
||||||
gli.Uniform4f(sr.color, c.r, c.g, c.b, c.a)
|
to := cv.tf(lg.to)
|
||||||
gli.EnableVertexAttribArray(sr.vertex)
|
dir := to.Sub(from)
|
||||||
gli.DrawArrays(gl_TRIANGLE_FAN, 0, 4)
|
length := dir.Len()
|
||||||
gli.DisableVertexAttribArray(sr.vertex)
|
dir = dir.DivF(length)
|
||||||
|
gli.Uniform2f(lgr.from, from[0], from[1])
|
||||||
|
gli.Uniform2f(lgr.dir, dir[0], dir[1])
|
||||||
|
gli.Uniform1f(lgr.length, length)
|
||||||
|
|
||||||
|
gli.Uniform1i(lgr.gradient, 0)
|
||||||
|
gli.EnableVertexAttribArray(lgr.vertex)
|
||||||
|
gli.DrawArrays(gl_TRIANGLE_FAN, 0, 4)
|
||||||
|
gli.DisableVertexAttribArray(lgr.vertex)
|
||||||
|
} else {
|
||||||
|
x0f, y0f := cv.tfToGL(x, y)
|
||||||
|
x1f, y1f := cv.tfToGL(x, y+h)
|
||||||
|
x2f, y2f := cv.tfToGL(x+w, y+h)
|
||||||
|
x3f, y3f := cv.tfToGL(x+w, y)
|
||||||
|
|
||||||
|
gli.BindBuffer(gl_ARRAY_BUFFER, buf)
|
||||||
|
data := [8]float32{x0f, y0f, x1f, y1f, x2f, y2f, x3f, y3f}
|
||||||
|
gli.BufferData(gl_ARRAY_BUFFER, len(data)*4, unsafe.Pointer(&data[0]), gl_STREAM_DRAW)
|
||||||
|
|
||||||
|
gli.UseProgram(sr.id)
|
||||||
|
gli.VertexAttribPointer(sr.vertex, 2, gl_FLOAT, false, 0, nil)
|
||||||
|
c := cv.state.fill.color
|
||||||
|
gli.Uniform4f(sr.color, c.r, c.g, c.b, c.a)
|
||||||
|
gli.EnableVertexAttribArray(lgr.vertex)
|
||||||
|
gli.DrawArrays(gl_TRIANGLE_FAN, 0, 4)
|
||||||
|
gli.DisableVertexAttribArray(lgr.vertex)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
108
gradients.go
Normal file
108
gradients.go
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
package canvas
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/barnex/fmath"
|
||||||
|
"github.com/tfriedel6/lm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LinearGradient struct {
|
||||||
|
from, to lm.Vec2
|
||||||
|
stops []gradientStop
|
||||||
|
tex uint32
|
||||||
|
loaded bool
|
||||||
|
deleted bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type gradientStop struct {
|
||||||
|
pos float32
|
||||||
|
color glColor
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLinearGradient(x0, y0, x1, y1 float32) *LinearGradient {
|
||||||
|
lg := &LinearGradient{from: lm.Vec2{x0, y0}, to: lm.Vec2{x1, y1}}
|
||||||
|
gli.GenTextures(1, &lg.tex)
|
||||||
|
gli.ActiveTexture(gl_TEXTURE0)
|
||||||
|
gli.BindTexture(gl_TEXTURE_1D, lg.tex)
|
||||||
|
gli.TexParameteri(gl_TEXTURE_1D, gl_TEXTURE_MIN_FILTER, gl_LINEAR)
|
||||||
|
gli.TexParameteri(gl_TEXTURE_1D, gl_TEXTURE_MAG_FILTER, gl_LINEAR)
|
||||||
|
gli.TexParameteri(gl_TEXTURE_1D, gl_TEXTURE_WRAP_S, gl_CLAMP_TO_EDGE)
|
||||||
|
runtime.SetFinalizer(lg, func(lg *LinearGradient) {
|
||||||
|
glChan <- func() {
|
||||||
|
gli.DeleteTextures(1, &lg.tex)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return lg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lg *LinearGradient) Delete() {
|
||||||
|
gli.DeleteTextures(1, &lg.tex)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lg *LinearGradient) load() {
|
||||||
|
if lg.loaded {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
gli.ActiveTexture(gl_TEXTURE0)
|
||||||
|
gli.BindTexture(gl_TEXTURE_1D, lg.tex)
|
||||||
|
var pixels [2048 * 4]byte
|
||||||
|
pp := 0
|
||||||
|
for i := 0; i < 2048; i++ {
|
||||||
|
c := lg.colorAt(float32(i) / 2047)
|
||||||
|
pixels[pp] = byte(fmath.Floor(c.r*255 + 0.5))
|
||||||
|
pixels[pp+1] = byte(fmath.Floor(c.g*255 + 0.5))
|
||||||
|
pixels[pp+2] = byte(fmath.Floor(c.b*255 + 0.5))
|
||||||
|
pixels[pp+3] = byte(fmath.Floor(c.a*255 + 0.5))
|
||||||
|
pp += 4
|
||||||
|
}
|
||||||
|
gli.TexImage1D(gl_TEXTURE_1D, 0, gl_RGBA, 2048, 0, gl_RGBA, gl_UNSIGNED_BYTE, gli.Ptr(&pixels[0]))
|
||||||
|
lg.loaded = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lg *LinearGradient) colorAt(pos float32) glColor {
|
||||||
|
if len(lg.stops) == 0 {
|
||||||
|
return glColor{}
|
||||||
|
} else if len(lg.stops) == 1 {
|
||||||
|
return lg.stops[0].color
|
||||||
|
}
|
||||||
|
beforeIdx, afterIdx := -1, -1
|
||||||
|
for i, stop := range lg.stops {
|
||||||
|
if stop.pos > pos {
|
||||||
|
afterIdx = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
beforeIdx = i
|
||||||
|
}
|
||||||
|
if beforeIdx == -1 {
|
||||||
|
return lg.stops[0].color
|
||||||
|
} else if afterIdx == -1 {
|
||||||
|
return lg.stops[len(lg.stops)-1].color
|
||||||
|
}
|
||||||
|
before, after := lg.stops[beforeIdx], lg.stops[afterIdx]
|
||||||
|
p := (pos - before.pos) / (after.pos - before.pos)
|
||||||
|
var c glColor
|
||||||
|
c.r = (after.color.r-before.color.r)*p + before.color.r
|
||||||
|
c.g = (after.color.g-before.color.g)*p + before.color.g
|
||||||
|
c.b = (after.color.b-before.color.b)*p + before.color.b
|
||||||
|
c.a = (after.color.a-before.color.a)*p + before.color.a
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lg *LinearGradient) AddColorStop(pos float32, color ...interface{}) {
|
||||||
|
c, _ := parseColor(color...)
|
||||||
|
insert := len(lg.stops)
|
||||||
|
for i, stop := range lg.stops {
|
||||||
|
if stop.pos > pos {
|
||||||
|
insert = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lg.stops = append(lg.stops, gradientStop{})
|
||||||
|
if insert < len(lg.stops)-1 {
|
||||||
|
copy(lg.stops[insert+1:], lg.stops[insert:len(lg.stops)-1])
|
||||||
|
}
|
||||||
|
lg.stops[insert] = gradientStop{pos: pos, color: c}
|
||||||
|
lg.loaded = false
|
||||||
|
}
|
105
made_shaders.go
105
made_shaders.go
|
@ -6,6 +6,111 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type linearGradientShader struct {
|
||||||
|
id uint32
|
||||||
|
vertex uint32
|
||||||
|
canvasSize int32
|
||||||
|
gradient int32
|
||||||
|
from int32
|
||||||
|
dir int32
|
||||||
|
length int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadLinearGradientShader() (*linearGradientShader, error) {
|
||||||
|
var vs, fs, program uint32
|
||||||
|
|
||||||
|
{
|
||||||
|
csource, freeFunc := gli.Strs(linearGradientVS + "\x00")
|
||||||
|
defer freeFunc()
|
||||||
|
|
||||||
|
vs = gli.CreateShader(gl_VERTEX_SHADER)
|
||||||
|
gli.ShaderSource(vs, 1, csource, nil)
|
||||||
|
gli.CompileShader(vs)
|
||||||
|
|
||||||
|
var logLength int32
|
||||||
|
gli.GetShaderiv(vs, gl_INFO_LOG_LENGTH, &logLength)
|
||||||
|
if logLength > 0 {
|
||||||
|
shLog := strings.Repeat("\x00", int(logLength+1))
|
||||||
|
gli.GetShaderInfoLog(vs, logLength, nil, gli.Str(shLog))
|
||||||
|
fmt.Printf("VERTEX_SHADER compilation log:\n\n%s\n", shLog)
|
||||||
|
}
|
||||||
|
|
||||||
|
var status int32
|
||||||
|
gli.GetShaderiv(vs, gl_COMPILE_STATUS, &status)
|
||||||
|
if status != gl_TRUE {
|
||||||
|
gli.DeleteShader(vs)
|
||||||
|
return nil, errors.New("Error compiling GL_VERTEX_SHADER shader part")
|
||||||
|
}
|
||||||
|
if glErr := gli.GetError(); glErr != gl_NO_ERROR {
|
||||||
|
return nil, errors.New("error compiling shader part, glError: " + fmt.Sprint(glErr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
csource, freeFunc := gli.Strs(linearGradientFS + "\x00")
|
||||||
|
defer freeFunc()
|
||||||
|
|
||||||
|
fs = gli.CreateShader(gl_FRAGMENT_SHADER)
|
||||||
|
gli.ShaderSource(fs, 1, csource, nil)
|
||||||
|
gli.CompileShader(fs)
|
||||||
|
|
||||||
|
var logLength int32
|
||||||
|
gli.GetShaderiv(fs, gl_INFO_LOG_LENGTH, &logLength)
|
||||||
|
if logLength > 0 {
|
||||||
|
shLog := strings.Repeat("\x00", int(logLength+1))
|
||||||
|
gli.GetShaderInfoLog(fs, logLength, nil, gli.Str(shLog))
|
||||||
|
fmt.Printf("FRAGMENT_SHADER compilation log:\n\n%s\n", shLog)
|
||||||
|
}
|
||||||
|
|
||||||
|
var status int32
|
||||||
|
gli.GetShaderiv(fs, gl_COMPILE_STATUS, &status)
|
||||||
|
if status != gl_TRUE {
|
||||||
|
gli.DeleteShader(fs)
|
||||||
|
return nil, errors.New("Error compiling GL_FRAGMENT_SHADER shader part")
|
||||||
|
}
|
||||||
|
if glErr := gli.GetError(); glErr != gl_NO_ERROR {
|
||||||
|
return nil, errors.New("error compiling shader part, glError: " + fmt.Sprint(glErr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
program = gli.CreateProgram()
|
||||||
|
gli.AttachShader(program, vs)
|
||||||
|
gli.AttachShader(program, fs)
|
||||||
|
gli.LinkProgram(program)
|
||||||
|
|
||||||
|
var logLength int32
|
||||||
|
gli.GetProgramiv(program, gl_INFO_LOG_LENGTH, &logLength)
|
||||||
|
if logLength > 0 {
|
||||||
|
shLog := strings.Repeat("\x00", int(logLength+1))
|
||||||
|
gli.GetProgramInfoLog(program, logLength, nil, gli.Str(shLog))
|
||||||
|
fmt.Printf("Shader link log:\n\n%s\n", shLog)
|
||||||
|
}
|
||||||
|
|
||||||
|
var status int32
|
||||||
|
gli.GetProgramiv(program, gl_LINK_STATUS, &status)
|
||||||
|
if status != gl_TRUE {
|
||||||
|
gli.DeleteShader(vs)
|
||||||
|
gli.DeleteShader(fs)
|
||||||
|
return nil, errors.New("error linking shader")
|
||||||
|
}
|
||||||
|
if glErr := gli.GetError(); glErr != gl_NO_ERROR {
|
||||||
|
return nil, errors.New("error linking shader, glError: " + fmt.Sprint(glErr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result := &linearGradientShader{}
|
||||||
|
result.id = program
|
||||||
|
result.vertex = uint32(gli.GetAttribLocation(program, gli.Str("vertex\x00")))
|
||||||
|
result.canvasSize = gli.GetUniformLocation(program, gli.Str("canvasSize\x00"))
|
||||||
|
result.gradient = gli.GetUniformLocation(program, gli.Str("gradient\x00"))
|
||||||
|
result.from = gli.GetUniformLocation(program, gli.Str("from\x00"))
|
||||||
|
result.dir = gli.GetUniformLocation(program, gli.Str("dir\x00"))
|
||||||
|
result.length = gli.GetUniformLocation(program, gli.Str("length\x00"))
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
type solidShader struct {
|
type solidShader struct {
|
||||||
id uint32
|
id uint32
|
||||||
vertex uint32
|
vertex uint32
|
||||||
|
|
Loading…
Reference in a new issue