forked from tfriedel6/canvas
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgradients.go
153 lines (141 loc) · 4.25 KB
/
gradients.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package canvas
import (
"math"
"runtime"
)
// LinearGradient is a gradient with any number of
// stops and any number of colors. The gradient will
// be drawn such that each point on the gradient
// will correspond to a straight line
type LinearGradient struct {
gradient
}
// RadialGradient is a gradient with any number of
// stops and any number of colors. The gradient will
// be drawn such that each point on the gradient
// will correspond to a circle
type RadialGradient struct {
gradient
radFrom, radTo float64
}
type gradient struct {
from, to vec
stops []gradientStop
tex uint32
loaded bool
deleted bool
}
type gradientStop struct {
pos float64
color glColor
}
// NewLinearGradient creates a new linear gradient with
// the coordinates from where to where the gradient
// will apply on the canvas
func NewLinearGradient(x0, y0, x1, y1 float64) *LinearGradient {
lg := &LinearGradient{gradient: gradient{from: vec{x0, y0}, to: vec{x1, y1}}}
gli.GenTextures(1, &lg.tex)
gli.ActiveTexture(gl_TEXTURE0)
gli.BindTexture(gl_TEXTURE_2D, lg.tex)
gli.TexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, gl_LINEAR)
gli.TexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, gl_LINEAR)
gli.TexParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_S, gl_CLAMP_TO_EDGE)
gli.TexParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_T, gl_CLAMP_TO_EDGE)
runtime.SetFinalizer(lg, func(lg *LinearGradient) {
glChan <- func() {
gli.DeleteTextures(1, &lg.tex)
}
})
return lg
}
// NewRadialGradient creates a new linear gradient with
// the coordinates and the radii for two circles. The
// gradient will apply from the first to the second
// circle
func NewRadialGradient(x0, y0, r0, x1, y1, r1 float64) *RadialGradient {
rg := &RadialGradient{gradient: gradient{from: vec{x0, y0}, to: vec{x1, y1}}, radFrom: r0, radTo: r1}
gli.GenTextures(1, &rg.tex)
gli.ActiveTexture(gl_TEXTURE0)
gli.BindTexture(gl_TEXTURE_2D, rg.tex)
gli.TexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, gl_LINEAR)
gli.TexParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, gl_LINEAR)
gli.TexParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_S, gl_CLAMP_TO_EDGE)
gli.TexParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_T, gl_CLAMP_TO_EDGE)
runtime.SetFinalizer(rg, func(rg *RadialGradient) {
glChan <- func() {
gli.DeleteTextures(1, &rg.tex)
}
})
return rg
}
// Delete explicitly deletes the gradient
func (g *gradient) Delete() {
gli.DeleteTextures(1, &g.tex)
g.deleted = true
}
func (g *gradient) load() {
if g.loaded {
return
}
gli.ActiveTexture(gl_TEXTURE0)
gli.BindTexture(gl_TEXTURE_2D, g.tex)
var pixels [2048 * 4]byte
pp := 0
for i := 0; i < 2048; i++ {
c := g.colorAt(float64(i) / 2047)
pixels[pp] = byte(math.Floor(c.r*255 + 0.5))
pixels[pp+1] = byte(math.Floor(c.g*255 + 0.5))
pixels[pp+2] = byte(math.Floor(c.b*255 + 0.5))
pixels[pp+3] = byte(math.Floor(c.a*255 + 0.5))
pp += 4
}
gli.TexImage2D(gl_TEXTURE_2D, 0, gl_RGBA, 2048, 1, 0, gl_RGBA, gl_UNSIGNED_BYTE, gli.Ptr(&pixels[0]))
g.loaded = true
}
func (g *gradient) colorAt(pos float64) glColor {
if len(g.stops) == 0 {
return glColor{}
} else if len(g.stops) == 1 {
return g.stops[0].color
}
beforeIdx, afterIdx := -1, -1
for i, stop := range g.stops {
if stop.pos > pos {
afterIdx = i
break
}
beforeIdx = i
}
if beforeIdx == -1 {
return g.stops[0].color
} else if afterIdx == -1 {
return g.stops[len(g.stops)-1].color
}
before, after := g.stops[beforeIdx], g.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
}
// AddColorStop adds a color stop to the gradient. The stops
// don't have to be added in order, they are sorted into the
// right place
func (g *gradient) AddColorStop(pos float64, color ...interface{}) {
c, _ := parseColor(color...)
insert := len(g.stops)
for i, stop := range g.stops {
if stop.pos > pos {
insert = i
break
}
}
g.stops = append(g.stops, gradientStop{})
if insert < len(g.stops)-1 {
copy(g.stops[insert+1:], g.stops[insert:len(g.stops)-1])
}
g.stops[insert] = gradientStop{pos: pos, color: c}
g.loaded = false
}