144 lines
2.9 KiB
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)
|
|
}
|