119 lines
2.9 KiB
Go
119 lines
2.9 KiB
Go
package vision
|
|
|
|
import (
|
|
"image/color"
|
|
"math"
|
|
|
|
"apigo.cc/go/rand"
|
|
)
|
|
|
|
// CaptchaOption 定义验证码生成选项
|
|
type CaptchaOption struct {
|
|
Text string
|
|
Length int
|
|
Width int
|
|
Height int
|
|
NoiseLevel int // 1-10
|
|
}
|
|
|
|
// GenerateCaptcha 生成一个验证码画布
|
|
func GenerateCaptcha(opt *CaptchaOption) *Canvas {
|
|
if opt == nil { opt = &CaptchaOption{} }
|
|
if opt.Length == 0 { opt.Length = 4 }
|
|
if opt.Width == 0 { opt.Width = 150 }
|
|
if opt.Height == 0 { opt.Height = 50 }
|
|
if opt.NoiseLevel == 0 { opt.NoiseLevel = 3 }
|
|
if opt.Text == "" {
|
|
chars := "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnpqrstwxyz2345678"
|
|
text := make([]byte, opt.Length)
|
|
for i := 0; i < opt.Length; i++ {
|
|
text[i] = chars[rand.Int(0, len(chars)-1)]
|
|
}
|
|
opt.Text = string(text)
|
|
}
|
|
|
|
c := New(opt.Width, opt.Height, "#FFFFFF")
|
|
c.RandBG(opt.NoiseLevel)
|
|
|
|
// 计算字体大小
|
|
fontSize := math.Max(28, float64(opt.Height)*0.7)
|
|
_ = c.SetFont(fontSize)
|
|
|
|
// 绘制随机扭曲文本
|
|
c.RandText(opt.Text)
|
|
|
|
return c
|
|
}
|
|
|
|
// GenerateGIFCaptcha 生成动态 GIF 验证码
|
|
func GenerateGIFCaptcha(opt *CaptchaOption) *Animation {
|
|
if opt == nil {
|
|
opt = &CaptchaOption{Length: 4, Width: 120, Height: 40}
|
|
}
|
|
|
|
const chars = "0123456789"
|
|
code := ""
|
|
for i := 0; i < opt.Length; i++ {
|
|
code += string(chars[rand.Int(0, len(chars)-1)])
|
|
}
|
|
anim := NewAnimation()
|
|
|
|
// 每一帧显示不同的干扰和字符位移
|
|
for i := 0; i < 10; i++ {
|
|
c := New(opt.Width, opt.Height, "#FFFFFF")
|
|
c.RandBG(3)
|
|
|
|
// 绘制字符,每一帧字符的位置都有微小抖动
|
|
for idx, char := range code {
|
|
x := float64(idx)*float64(opt.Width/opt.Length) + 5 + rand.Float(0.0, 4.0) - 2.0
|
|
y := float64(opt.Height/2) + 10 + rand.Float(0.0, 6.0) - 3.0
|
|
c.dc.Push()
|
|
c.dc.RotateAbout(rand.Float(0.0, 0.4)-0.2, x, y)
|
|
c.dc.SetColor(ParseColor(RandColor()))
|
|
c.dc.DrawString(string(char), x, y)
|
|
c.dc.Pop()
|
|
}
|
|
|
|
anim.AddFrame(c, 15) // 150ms 每帧
|
|
}
|
|
|
|
return anim
|
|
}
|
|
|
|
// RandText 绘制随机扭曲文本 (用于验证码)
|
|
func (c *Canvas) RandText(text string) [][4]float64 {
|
|
w, h := float64(c.Width()), float64(c.Height())
|
|
fullWidth, _ := c.dc.MeasureString(text)
|
|
|
|
x := (w - fullWidth) / 2
|
|
y := h/2 + (c.dc.FontHeight()*0.7)/2
|
|
|
|
charPositions := make([][4]float64, 0, len(text))
|
|
for _, char := range text {
|
|
charStr := string(char)
|
|
charWidth, _ := c.dc.MeasureString(charStr)
|
|
charHeight := c.dc.FontHeight()
|
|
|
|
yOffset := rand.Float(0.0, 10.0) - 5
|
|
angle := rand.Float(0.0, 0.4) - 0.2 // ±11°
|
|
|
|
charPositions = append(charPositions, [4]float64{x, y + yOffset - charHeight, charWidth, charHeight})
|
|
|
|
c.dc.Push()
|
|
c.dc.RotateAbout(angle, x+charWidth/2, y+yOffset-charHeight/2)
|
|
|
|
// 绘制阴影
|
|
c.dc.SetColor(color.Gray{Y: 100})
|
|
c.dc.DrawString(charStr, x+1, y+yOffset+1)
|
|
|
|
// 绘制主体
|
|
c.dc.SetColor(ParseColor(RandColor()))
|
|
c.dc.DrawString(charStr, x, y+yOffset)
|
|
c.dc.Pop()
|
|
|
|
x += charWidth + rand.Float(0.0, 5.0)
|
|
}
|
|
|
|
return charPositions
|
|
}
|