canvas/gradient.go

144 lines
2.9 KiB
Go

package canvas
import (
"image/color"
"math"
"sort"
)
// Gradient 渐变接口
type Gradient interface {
At(x, y int) color.Color
}
// GradientStop 渐变色标
type GradientStop struct {
Offset float64
Color color.Color
}
// LinearGradient 线性渐变
type LinearGradient struct {
X0, Y0, X1, Y1 float64
Stops []GradientStop
}
// NewLinearGradient 创建线性渐变
func NewLinearGradient(x0, y0, x1, y1 float64) *LinearGradient {
return &LinearGradient{
X0: x0, Y0: y0, X1: x1, Y1: y1,
Stops: []GradientStop{},
}
}
// AddColorStop 添加色标
func (g *LinearGradient) AddColorStop(offset float64, color color.Color) {
g.Stops = append(g.Stops, GradientStop{Offset: offset, Color: color})
sort.Slice(g.Stops, func(i, j int) bool {
return g.Stops[i].Offset < g.Stops[j].Offset
})
}
// At 获取指定位置的颜色
func (g *LinearGradient) At(x, y int) color.Color {
if len(g.Stops) == 0 {
return color.Transparent
}
// 计算投影位置 (0-1)
dx := g.X1 - g.X0
dy := g.Y1 - g.Y0
lenSq := dx*dx + dy*dy
t := 0.0
if lenSq > 0 {
t = ((float64(x)-g.X0)*dx + (float64(y)-g.Y0)*dy) / lenSq
if t < 0 {
t = 0
} else if t > 1 {
t = 1
}
}
// 查找色标区间
var start, end GradientStop
for i, stop := range g.Stops {
if stop.Offset >= t {
if i == 0 {
return stop.Color
}
start = g.Stops[i-1]
end = stop
break
}
}
if start.Color == nil {
return g.Stops[len(g.Stops)-1].Color
}
// 区间插值
localT := (t - start.Offset) / (end.Offset - start.Offset)
return interpolateColor(start.Color, end.Color, localT)
}
// RadialGradient 径向渐变
type RadialGradient struct {
X0, Y0, R0, X1, Y1, R1 float64
Stops []GradientStop
}
// NewRadialGradient 创建径向渐变
func NewRadialGradient(x0, y0, r0, x1, y1, r1 float64) *RadialGradient {
return &RadialGradient{
X0: x0, Y0: y0, R0: r0, X1: x1, Y1: y1, R1: r1,
Stops: []GradientStop{},
}
}
// AddColorStop 添加色标
func (g *RadialGradient) AddColorStop(offset float64, color color.Color) {
g.Stops = append(g.Stops, GradientStop{Offset: offset, Color: color})
sort.Slice(g.Stops, func(i, j int) bool {
return g.Stops[i].Offset < g.Stops[j].Offset
})
}
// At 获取指定位置的颜色
func (g *RadialGradient) At(x, y int) color.Color {
if len(g.Stops) == 0 {
return color.Transparent
}
// 计算到焦点的距离
dx1, dy1 := float64(x)-g.X1, float64(y)-g.Y1
d1 := math.Sqrt(dx1*dx1 + dy1*dy1)
// 计算梯度值 (0-1)
t := (d1 - g.R0) / (g.R1 - g.R0)
if t < 0 {
t = 0
} else if t > 1 {
t = 1
}
// 查找色标区间
var start, end GradientStop
for i, stop := range g.Stops {
if stop.Offset >= t {
if i == 0 {
return stop.Color
}
start = g.Stops[i-1]
end = stop
break
}
}
if start.Color == nil {
return g.Stops[len(g.Stops)-1].Color
}
// 区间插值
localT := (t - start.Offset) / (end.Offset - start.Offset)
return interpolateColor(start.Color, end.Color, localT)
}