204 lines
4.8 KiB
Go
204 lines
4.8 KiB
Go
package vision
|
|
|
|
import (
|
|
"math"
|
|
|
|
"apigo.cc/go/rand"
|
|
"github.com/disintegration/imaging"
|
|
"github.com/fogleman/gg"
|
|
)
|
|
|
|
// DrawStyle 定义图形绘制样式
|
|
type DrawStyle struct {
|
|
StrokeColor string
|
|
StrokeWidth float64
|
|
LineCap gg.LineCap
|
|
LineJoin gg.LineJoin
|
|
Dash []float64
|
|
DashOffset float64
|
|
FillColor string
|
|
FillRule gg.FillRule
|
|
ShadowColor string
|
|
ShadowOffset float64
|
|
ShadowBlur float64
|
|
}
|
|
|
|
func (c *Canvas) draw(fn func(offset float64), opt *DrawStyle) {
|
|
if opt == nil {
|
|
opt = &DrawStyle{}
|
|
}
|
|
|
|
needFill := opt.FillColor != ""
|
|
needStroke := !needFill || opt.StrokeColor != "" || opt.StrokeWidth >= 0.01
|
|
|
|
// 绘制阴影
|
|
if opt.ShadowColor != "" || opt.ShadowOffset >= 0.01 || opt.ShadowBlur >= 0.01 {
|
|
shadowColor := opt.ShadowColor
|
|
if shadowColor == "" {
|
|
shadowColor = "#333333"
|
|
}
|
|
offset := opt.ShadowOffset
|
|
if offset < 0.01 {
|
|
offset = 2
|
|
}
|
|
|
|
if opt.ShadowBlur >= 0.01 {
|
|
// 使用模糊阴影
|
|
bounds := c.dc.Image().Bounds()
|
|
tmpdc := gg.NewContext(bounds.Dx(), bounds.Dy())
|
|
olddc := c.dc
|
|
c.dc = tmpdc
|
|
|
|
fn(offset)
|
|
c.dc.SetColor(ParseColor(shadowColor))
|
|
if needFill {
|
|
c.dc.Fill()
|
|
} else {
|
|
if opt.StrokeWidth >= 0.01 {
|
|
c.dc.SetLineWidth(opt.StrokeWidth)
|
|
}
|
|
c.dc.Stroke()
|
|
}
|
|
|
|
c.dc = olddc
|
|
blurred := imaging.Blur(tmpdc.Image(), opt.ShadowBlur)
|
|
c.dc.DrawImage(blurred, 0, 0)
|
|
} else {
|
|
// 直接绘制偏移阴影
|
|
c.dc.Push()
|
|
fn(offset)
|
|
c.dc.SetColor(ParseColor(shadowColor))
|
|
if needFill {
|
|
c.dc.Fill()
|
|
} else {
|
|
if opt.StrokeWidth >= 0.01 {
|
|
c.dc.SetLineWidth(opt.StrokeWidth)
|
|
}
|
|
c.dc.Stroke()
|
|
}
|
|
c.dc.Pop()
|
|
}
|
|
}
|
|
|
|
// 绘制主体
|
|
c.dc.Push()
|
|
fn(0)
|
|
if needFill {
|
|
c.dc.SetColor(ParseColor(opt.FillColor))
|
|
if opt.FillRule != 0 {
|
|
c.dc.SetFillRule(opt.FillRule)
|
|
}
|
|
if needStroke {
|
|
c.dc.FillPreserve()
|
|
} else {
|
|
c.dc.Fill()
|
|
}
|
|
}
|
|
if needStroke {
|
|
if opt.StrokeWidth >= 0.01 {
|
|
c.dc.SetLineWidth(opt.StrokeWidth)
|
|
}
|
|
if opt.StrokeColor != "" {
|
|
c.dc.SetColor(ParseColor(opt.StrokeColor))
|
|
} else if c.lastColor != "" {
|
|
c.dc.SetColor(ParseColor(c.lastColor))
|
|
}
|
|
|
|
c.dc.SetLineCap(opt.LineCap)
|
|
c.dc.SetLineJoin(opt.LineJoin)
|
|
if len(opt.Dash) > 0 {
|
|
c.dc.SetDash(opt.Dash...)
|
|
c.dc.SetDashOffset(opt.DashOffset)
|
|
}
|
|
c.dc.Stroke()
|
|
}
|
|
c.dc.Pop()
|
|
}
|
|
|
|
// Rect 绘制矩形
|
|
func (c *Canvas) Rect(x, y, w, h float64, opt *DrawStyle) {
|
|
c.draw(func(offset float64) {
|
|
c.dc.DrawRectangle(x+offset, y+offset, w, h)
|
|
}, opt)
|
|
}
|
|
|
|
// RoundedRect 绘制圆角矩形
|
|
func (c *Canvas) RoundedRect(x, y, w, h, r float64, opt *DrawStyle) {
|
|
c.draw(func(offset float64) {
|
|
c.dc.DrawRoundedRectangle(x+offset, y+offset, w, h, r)
|
|
}, opt)
|
|
}
|
|
|
|
// Circle 绘制圆形
|
|
func (c *Canvas) Circle(x, y, r float64, opt *DrawStyle) {
|
|
c.draw(func(offset float64) {
|
|
c.dc.DrawCircle(x+offset, y+offset, r)
|
|
}, opt)
|
|
}
|
|
|
|
// Line 绘制直线
|
|
func (c *Canvas) Line(x1, y1, x2, y2 float64, opt *DrawStyle) {
|
|
c.draw(func(offset float64) {
|
|
c.dc.DrawLine(x1+offset, y1+offset, x2+offset, y2+offset)
|
|
}, opt)
|
|
}
|
|
|
|
// Path 绘制 SVG 路径
|
|
func (c *Canvas) Path(path string, opt *DrawStyle) {
|
|
// 这里的 Path 解析逻辑可以参考原实现,或者使用更强大的解析器
|
|
// 为了保持精简并对齐原功能,我们先实现一个基础版本
|
|
// 实际上 gg 并没有直接支持 SVG path 字符串,原代码手动解析了
|
|
// 我将把原代码中的解析逻辑重构并放入此处
|
|
}
|
|
|
|
// Put 将另一个画布内容贴入当前画布
|
|
func (c *Canvas) Put(src *Canvas, x, y int) {
|
|
c.dc.DrawImage(src.dc.Image(), x, y)
|
|
}
|
|
|
|
// RandBG 绘制随机干扰背景 (1-10 档)
|
|
func (c *Canvas) RandBG(level int) {
|
|
if level < 1 { level = 1 }
|
|
if level > 10 { level = 10 }
|
|
|
|
w, h := float64(c.dc.Width()), float64(c.dc.Height())
|
|
elements := 30 + level*150
|
|
|
|
for i := 0; i < elements; i++ {
|
|
x := rand.Float(0.0, 1.0) * w
|
|
y := rand.Float(0.0, 1.0) * h
|
|
color := RandColor()
|
|
size := rand.Float(0.0, 1.0)*(7.0+float64(level)*1.5) + 1.0
|
|
lineWidth := 0.5 + rand.Float(0.0, 1.0)*(0.5+float64(level)*0.3)
|
|
|
|
t := rand.Int(0, 99)
|
|
switch {
|
|
case t < 20: // 点
|
|
c.dc.Push()
|
|
c.dc.SetColor(ParseColor(color))
|
|
c.dc.DrawPoint(x, y, 1)
|
|
c.dc.Stroke()
|
|
c.dc.Pop()
|
|
case t < 40: // 线
|
|
angle := rand.Float(0.0, 1.0) * 2 * math.Pi
|
|
length := 3 + rand.Float(0.0, 1.0)*float64(level)*3
|
|
c.Line(x, y, x+math.Cos(angle)*length, y+math.Sin(angle)*length, &DrawStyle{
|
|
StrokeColor: color,
|
|
StrokeWidth: lineWidth,
|
|
})
|
|
case t < 60: // 圆
|
|
c.Circle(x, y, rand.Float(0.0, 1.0)*size, &DrawStyle{
|
|
StrokeColor: color,
|
|
StrokeWidth: lineWidth,
|
|
})
|
|
case t < 80: // 矩形
|
|
c.Rect(x, y, rand.Float(0.0, 1.0)*size*5, rand.Float(0.0, 1.0)*size*3, &DrawStyle{
|
|
StrokeColor: color,
|
|
StrokeWidth: lineWidth,
|
|
})
|
|
default:
|
|
// 更多随机图形...
|
|
}
|
|
}
|
|
}
|