summaryrefslogtreecommitdiff
path: root/vendor/github.com/fogleman/gg/gradient.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/fogleman/gg/gradient.go')
-rw-r--r--vendor/github.com/fogleman/gg/gradient.go202
1 files changed, 202 insertions, 0 deletions
diff --git a/vendor/github.com/fogleman/gg/gradient.go b/vendor/github.com/fogleman/gg/gradient.go
new file mode 100644
index 0000000..1625520
--- /dev/null
+++ b/vendor/github.com/fogleman/gg/gradient.go
@@ -0,0 +1,202 @@
+package gg
+
+import (
+ "image/color"
+ "math"
+ "sort"
+)
+
+type stop struct {
+ pos float64
+ color color.Color
+}
+
+type stops []stop
+
+// Len satisfies the Sort interface.
+func (s stops) Len() int {
+ return len(s)
+}
+
+// Less satisfies the Sort interface.
+func (s stops) Less(i, j int) bool {
+ return s[i].pos < s[j].pos
+}
+
+// Swap satisfies the Sort interface.
+func (s stops) Swap(i, j int) {
+ s[i], s[j] = s[j], s[i]
+}
+
+type Gradient interface {
+ Pattern
+ AddColorStop(offset float64, color color.Color)
+}
+
+// Linear Gradient
+type linearGradient struct {
+ x0, y0, x1, y1 float64
+ stops stops
+}
+
+func (g *linearGradient) ColorAt(x, y int) color.Color {
+ if len(g.stops) == 0 {
+ return color.Transparent
+ }
+
+ fx, fy := float64(x), float64(y)
+ x0, y0, x1, y1 := g.x0, g.y0, g.x1, g.y1
+ dx, dy := x1-x0, y1-y0
+
+ // Horizontal
+ if dy == 0 && dx != 0 {
+ return getColor((fx-x0)/dx, g.stops)
+ }
+
+ // Vertical
+ if dx == 0 && dy != 0 {
+ return getColor((fy-y0)/dy, g.stops)
+ }
+
+ // Dot product
+ s0 := dx*(fx-x0) + dy*(fy-y0)
+ if s0 < 0 {
+ return g.stops[0].color
+ }
+ // Calculate distance to (x0,y0) alone (x0,y0)->(x1,y1)
+ mag := math.Hypot(dx, dy)
+ u := ((fx-x0)*-dy + (fy-y0)*dx) / (mag * mag)
+ x2, y2 := x0+u*-dy, y0+u*dx
+ d := math.Hypot(fx-x2, fy-y2) / mag
+ return getColor(d, g.stops)
+}
+
+func (g *linearGradient) AddColorStop(offset float64, color color.Color) {
+ g.stops = append(g.stops, stop{pos: offset, color: color})
+ sort.Sort(g.stops)
+}
+
+func NewLinearGradient(x0, y0, x1, y1 float64) Gradient {
+ g := &linearGradient{
+ x0: x0, y0: y0,
+ x1: x1, y1: y1,
+ }
+ return g
+}
+
+// Radial Gradient
+type circle struct {
+ x, y, r float64
+}
+
+type radialGradient struct {
+ c0, c1, cd circle
+ a, inva float64
+ mindr float64
+ stops stops
+}
+
+func dot3(x0, y0, z0, x1, y1, z1 float64) float64 {
+ return x0*x1 + y0*y1 + z0*z1
+}
+
+func (g *radialGradient) ColorAt(x, y int) color.Color {
+ if len(g.stops) == 0 {
+ return color.Transparent
+ }
+
+ // copy from pixman's pixman-radial-gradient.c
+
+ dx, dy := float64(x)+0.5-g.c0.x, float64(y)+0.5-g.c0.y
+ b := dot3(dx, dy, g.c0.r, g.cd.x, g.cd.y, g.cd.r)
+ c := dot3(dx, dy, -g.c0.r, dx, dy, g.c0.r)
+
+ if g.a == 0 {
+ if b == 0 {
+ return color.Transparent
+ }
+ t := 0.5 * c / b
+ if t*g.cd.r >= g.mindr {
+ return getColor(t, g.stops)
+ }
+ return color.Transparent
+ }
+
+ discr := dot3(b, g.a, 0, b, -c, 0)
+ if discr >= 0 {
+ sqrtdiscr := math.Sqrt(discr)
+ t0 := (b + sqrtdiscr) * g.inva
+ t1 := (b - sqrtdiscr) * g.inva
+
+ if t0*g.cd.r >= g.mindr {
+ return getColor(t0, g.stops)
+ } else if t1*g.cd.r >= g.mindr {
+ return getColor(t1, g.stops)
+ }
+ }
+
+ return color.Transparent
+}
+
+func (g *radialGradient) AddColorStop(offset float64, color color.Color) {
+ g.stops = append(g.stops, stop{pos: offset, color: color})
+ sort.Sort(g.stops)
+}
+
+func NewRadialGradient(x0, y0, r0, x1, y1, r1 float64) Gradient {
+ c0 := circle{x0, y0, r0}
+ c1 := circle{x1, y1, r1}
+ cd := circle{x1 - x0, y1 - y0, r1 - r0}
+ a := dot3(cd.x, cd.y, -cd.r, cd.x, cd.y, cd.r)
+ var inva float64
+ if a != 0 {
+ inva = 1.0 / a
+ }
+ mindr := -c0.r
+ g := &radialGradient{
+ c0: c0,
+ c1: c1,
+ cd: cd,
+ a: a,
+ inva: inva,
+ mindr: mindr,
+ }
+ return g
+}
+
+func getColor(pos float64, stops stops) color.Color {
+ if pos <= 0.0 || len(stops) == 1 {
+ return stops[0].color
+ }
+
+ last := stops[len(stops)-1]
+
+ if pos >= last.pos {
+ return last.color
+ }
+
+ for i, stop := range stops[1:] {
+ if pos < stop.pos {
+ pos = (pos - stops[i].pos) / (stop.pos - stops[i].pos)
+ return colorLerp(stops[i].color, stop.color, pos)
+ }
+ }
+
+ return last.color
+}
+
+func colorLerp(c0, c1 color.Color, t float64) color.Color {
+ r0, g0, b0, a0 := c0.RGBA()
+ r1, g1, b1, a1 := c1.RGBA()
+
+ return color.NRGBA{
+ lerp(r0, r1, t),
+ lerp(g0, g1, t),
+ lerp(b0, b1, t),
+ lerp(a0, a1, t),
+ }
+}
+
+func lerp(a, b uint32, t float64) uint8 {
+ return uint8(int32(float64(a)*(1.0-t)+float64(b)*t) >> 8)
+}