Skip to content

Commit

Permalink
linear gradient
Browse files Browse the repository at this point in the history
  • Loading branch information
wsw0108 committed Nov 21, 2016
1 parent db5b864 commit 1aa5706
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 1 deletion.
39 changes: 39 additions & 0 deletions examples/gradient-linear.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package main

import (
"image/color"

"github.com/fogleman/gg"
)

func main() {
dc := gg.NewContext(500, 400)

grad := gg.NewLinearGradient(20, 320, 400, 20)
grad.AddColorStop(0, color.RGBA{0, 255, 0, 255})
grad.AddColorStop(1, color.RGBA{0, 0, 255, 255})
grad.AddColorStop(0.5, color.RGBA{255, 0, 0, 255})

dc.SetColor(color.White)
dc.DrawRectangle(20, 20, 400-20, 300)
dc.Stroke()

dc.SetStrokeStyle(grad)
dc.SetLineWidth(4)
dc.MoveTo(10, 10)
dc.LineTo(410, 10)
dc.LineTo(410, 100)
dc.LineTo(10, 100)
dc.ClosePath()
dc.Stroke()

dc.SetFillStyle(grad)
dc.MoveTo(10, 120)
dc.LineTo(410, 120)
dc.LineTo(410, 300)
dc.LineTo(10, 300)
dc.ClosePath()
dc.Fill()

dc.SavePNG("out.png")
}
17 changes: 16 additions & 1 deletion examples/fill-pattern.go → examples/pattern-fill.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,27 @@
package main

import "github.com/fogleman/gg"
import (
"fmt"
"math"

"github.com/fogleman/gg"
)

func main() {
im, err := gg.LoadPNG("examples/lenna.png")
if err != nil {
panic(err)
}
var x0, y0, x1, y1 float64 = 5, 5, 15, 15
a := math.Atan2(y1-y0, x1-x0)
d := a * 180 / math.Pi
fmt.Println(a, d)
a = (2*math.Pi - a)
d = a * 180 / math.Pi
fmt.Println(a, d)
m := gg.Translate(-x0, -y0).Rotate(a)
tx, ty := m.TransformPoint(5, 5)
fmt.Println(tx, ty)
pattern := gg.NewSurfacePattern(im, gg.RepeatBoth)
dc := gg.NewContext(600, 600)
dc.MoveTo(20, 20)
Expand Down
131 changes: 131 additions & 0 deletions gradient.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
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
dx, dy float64
mag float64
px0, py0 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 := g.x0, g.y0
dx, dy := g.dx, g.dy

// 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)
}

px0, py0 := g.px0, g.py0
mag := g.mag

s0 := (px0-x0)*(fy-y0) - (py0-y0)*(fx-x0)
if s0 > 0 {
return g.stops[0].color
}
u := ((fx-x0)*(px0-x0) + (fy-y0)*(py0-y0)) / (mag * mag)
x2, y2 := x0+u*(px0-x0), y0+u*(py0-y0)
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 {
dx, dy := x1-x0, y1-y0
mag := math.Hypot(dx, dy)
px0, py0 := x0-dy, y0+dx
g := &linearGradient{
x0: x0, y0: y0,
x1: x1, y1: y1,
dx: dx, dy: dy,
px0: px0, py0: py0,
mag: mag,
}
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)
}

0 comments on commit 1aa5706

Please sign in to comment.