Skip to content

Commit ddba284

Browse files
committed
Added captcha Config
1 parent 65af0f3 commit ddba284

File tree

5 files changed

+313
-24
lines changed

5 files changed

+313
-24
lines changed

canvas.go

+201-21
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@ package captcha
22

33
import (
44
// "fmt"
5+
"image"
6+
"image/draw"
57

68
"embed"
79
// "fmt"
810
"github.com/golang/freetype"
911
"github.com/golang/freetype/truetype"
10-
"image"
12+
// "image"
1113
"image/color"
12-
// "math"
14+
"math"
1315
"math/rand"
1416
"time"
1517
)
@@ -33,6 +35,13 @@ type Canvas struct {
3335
*image.NRGBA
3436
Width int
3537
Height int
38+
Config Config
39+
}
40+
41+
//DrawBackgroud 设置画板背景
42+
func (c *Canvas) DrawBackgroud() {
43+
backgroupColor := image.NewUniform(c.Config.BackgroupColor)
44+
draw.Draw(c, c.Bounds(), backgroupColor, image.ZP, draw.Over)
3645
}
3746

3847
func (c *Canvas) DrawLines(lineCount int) {
@@ -51,12 +60,12 @@ func (c *Canvas) DrawLines(lineCount int) {
5160
func (c *Canvas) DrawLine(p1, p2 Point, color color.Color) {
5261

5362
var k float64 = 0
54-
b := p1.Y
63+
var b float64 = 0
5564
if (p2.X - p1.X) != 0 {
5665
k = float64(p2.Y-p1.Y) / float64(p2.X-p1.X)
5766
}
5867

59-
b = p1.Y - int(k*float64(p1.X))
68+
b = float64(p1.Y) - k*float64(p1.X)
6069

6170
sx := 1
6271
offsetX := p1.X
@@ -65,9 +74,32 @@ func (c *Canvas) DrawLine(p1, p2 Point, color color.Color) {
6574
sx = -1
6675
}
6776

77+
if k == 0 && p2.X == p1.X {
78+
79+
tmpY := p1.Y
80+
81+
if p2.Y < p1.Y {
82+
sx = -1
83+
}
84+
85+
for {
86+
87+
c.Set(offsetX, tmpY, color)
88+
89+
if tmpY == p2.Y {
90+
break
91+
}
92+
93+
tmpY = tmpY + sx
94+
95+
}
96+
97+
return
98+
}
99+
68100
for {
69101

70-
offsetY := int(k*float64(offsetX)) + b
102+
offsetY := int(k*float64(offsetX) + b)
71103
c.Set(offsetX, offsetY, color)
72104

73105
if offsetX == p2.X {
@@ -78,32 +110,180 @@ func (c *Canvas) DrawLine(p1, p2 Point, color color.Color) {
78110
}
79111
}
80112

113+
//DrawBezierLines 画贝塞尔干扰线
114+
func (c *Canvas) DrawBezierLines(lineCount int) {
115+
116+
for lineCount > 0 {
117+
118+
lineColor := randomColor()
119+
120+
x1 := rand.Intn(c.Width / 4)
121+
y1 := randomInt(c.Height/4, int(float64(c.Height)*0.9))
122+
123+
x2 := randomInt(c.Width/2, int(float64(c.Width)*0.9))
124+
y2 := randomInt(c.Height/4, int(float64(c.Height)*0.9))
125+
126+
cx := randomInt(c.Width/4, int(float64(c.Width)*0.7))
127+
cy := randomInt(c.Height/4, int(float64(c.Height)*0.6))
128+
129+
// B(t) = (1-t)P0 + 2t(1-t)P1 + tP2, t∈[0,1]
130+
// x = Math.pow(1-t, 2) * x1 + 2 * t * (1-t) * cx + Math.pow(t, 2) * x2
131+
// y = Math.pow(1-t, 2) * y1 + 2 * t * (1-t) * cy + Math.pow(t, 2) * y2
132+
var t float64
133+
for t < 1 {
134+
135+
x := math.Pow(1-t, 2.0)*float64(x1) + 2*t*(1-t)*float64(cx) + math.Pow(t, 2.0)*float64(x2)
136+
y := math.Pow(1-t, 2)*float64(y1) + 2*t*(1-t)*float64(cy) + math.Pow(t, 2)*float64(y2)
137+
138+
c.Set(int(x), int(y), lineColor)
139+
t = t + 0.001
140+
}
141+
142+
lineCount--
143+
}
144+
145+
}
146+
81147
func (c *Canvas) DrawString(text string) {
82148

149+
var drawPos []DrawPos
83150
for _, ch := range text {
84151

85-
dc := freetype.NewContext()
86-
dc.SetDPI(float64(72))
87-
dc.SetFont(defaultFont)
88-
dc.SetClip(c.Bounds())
89-
dc.SetDst(c)
152+
pos := c.randomFontPosition(56)
90153

91-
// 文字大小
92-
dc.SetFontSize(float64(56))
154+
fontImg, areaPoint := c.DrawFont(string(ch), c.Config.FrontColors)
155+
156+
minX := areaPoint.MinX
157+
maxX := areaPoint.MaxX
158+
minY := areaPoint.MinY
159+
maxY := areaPoint.MaxY
160+
width := maxX - minX
161+
height := maxY - minY
162+
nW := fontImg.Bounds().Max.X
163+
nH := fontImg.Bounds().Max.Y
164+
for x := 0; x < nW; x++ {
165+
for y := 0; y < nH; y++ {
166+
co := fontImg.At(x, y)
167+
if _, _, _, a := co.RGBA(); a > 0 {
168+
if x >= minX && x <= maxX && y >= minY && y <= maxY {
169+
c.Set(pos.X+(x-minX), pos.Y-height+(y-minY), fontImg.At(x, y))
170+
}
171+
}
172+
}
173+
}
93174

94-
// 文字颜色
95-
fontColor := image.NewUniform(randomColor())
96-
dc.SetSrc(fontColor)
175+
var dp DrawPos
176+
dp.X = minX + pos.X
177+
dp.Y = pos.Y - height
178+
dp.Width = width
179+
dp.Height = height
97180

98-
pos := c.randomFontPosition(56)
99-
// 画文本
100-
pt := freetype.Pt(pos.X, pos.Y) // 字出现的位置
101-
_, err := dc.DrawString(string(ch), pt)
102-
if err != nil {
103-
panic(err)
181+
drawPos = append(drawPos, dp)
182+
183+
}
184+
185+
// lineColor := randomColor()
186+
187+
// for _, linePos := range drawPos {
188+
// p1 := Point{X: linePos.X, Y: linePos.Y}
189+
// p2 := Point{X: linePos.X + linePos.Width, Y: linePos.Y}
190+
191+
// p3 := Point{X: linePos.X, Y: linePos.Y + linePos.Height}
192+
// p4 := Point{X: linePos.X + linePos.Width, Y: linePos.Y + linePos.Height}
193+
// c.DrawLine(p1, p2, lineColor)
194+
// c.DrawLine(p1, p3, lineColor)
195+
// c.DrawLine(p3, p4, lineColor)
196+
// c.DrawLine(p2, p4, lineColor)
197+
198+
// }
199+
// fmt.Printf("drawpos %+v %s", drawPos, text)
200+
201+
}
202+
203+
//DrawFont 绘制验证码字
204+
func (c *Canvas) DrawFont(fontText string, fontColors []color.Color) (*Palette, *AreaPoint) {
205+
fontSize := 56
206+
207+
rand.Seed(time.Now().UnixNano())
208+
209+
fntColorIndex := rand.Intn(len(fontColors))
210+
211+
rColor := fontColors[fntColorIndex]
212+
p := []color.Color{
213+
color.RGBA{R: 0xFF, G: 0xFF, B: 0xFF, A: 0x00},
214+
rColor,
215+
}
216+
217+
canvas := NewPalette(image.Rect(0, 0, fontSize, fontSize), p)
218+
219+
dc := freetype.NewContext()
220+
dc.SetDPI(float64(72))
221+
dc.SetFont(defaultFont)
222+
dc.SetClip(canvas.Bounds())
223+
dc.SetDst(canvas)
224+
225+
// 文字大小
226+
dc.SetFontSize(float64(56))
227+
228+
// 文字颜色
229+
fontColor := image.NewUniform(rColor)
230+
dc.SetSrc(fontColor)
231+
232+
// 画文本
233+
pt := freetype.Pt(0, fontSize) // 字出现的位置
234+
_, err := dc.DrawString(string(fontText), pt)
235+
236+
if err != nil {
237+
panic(err)
238+
}
239+
240+
//旋转角度
241+
canvas.Rotate(randomInt(-30, 30))
242+
ap := c.calcImageSpace(canvas)
243+
return canvas, ap
244+
245+
}
246+
247+
func (c *Canvas) calcImageSpace(pa *Palette) *AreaPoint {
248+
nW := pa.Bounds().Max.X
249+
nH := pa.Bounds().Max.Y
250+
// 计算裁剪的最小及最大的坐标
251+
minX := nW
252+
maxX := 0
253+
minY := nH
254+
maxY := 0
255+
for x := 0; x < nW; x++ {
256+
for y := 0; y < nH; y++ {
257+
co := pa.At(x, y)
258+
if _, _, _, a := co.RGBA(); a > 0 {
259+
if x < minX {
260+
minX = x
261+
}
262+
if x > maxX {
263+
maxX = x
264+
}
265+
266+
if y < minY {
267+
minY = y
268+
}
269+
if y > maxY {
270+
maxY = y
271+
}
272+
}
104273
}
105274
}
106275

276+
minX = int(math.Max(0, float64(minX-2)))
277+
maxX = int(math.Min(float64(nW), float64(maxX+2)))
278+
minY = int(math.Max(0, float64(minY-2)))
279+
maxY = int(math.Min(float64(nH), float64(maxY+2)))
280+
281+
return &AreaPoint{
282+
minX,
283+
maxX,
284+
minY,
285+
maxY,
286+
}
107287
}
108288

109289
func (c *Canvas) randomFontPosition(fontSize int) Point {

captcha.go

+46-3
Original file line numberDiff line numberDiff line change
@@ -5,33 +5,76 @@ import (
55
"encoding/base64"
66
"fmt"
77
"image"
8-
// "image/color"
8+
"image/color"
99
"image/png"
1010
"os"
1111
)
1212

13+
type Config struct {
14+
FrontColors []color.Color
15+
BackgroupColor color.Color
16+
}
17+
1318
type Captcha struct {
1419
Width int
1520
Height int
21+
Config Config
1622
}
1723

1824
func NewCaptcha(width, height int) *Captcha {
19-
return &Captcha{Width: width, Height: height}
25+
cp := Captcha{Width: width, Height: height}
26+
27+
config := Config{}
28+
config.FrontColors = []color.Color{
29+
color.RGBA{R: uint8(66), G: uint8(153), B: uint8(244), A: uint8(255)},
30+
color.RGBA{R: uint8(234), G: uint8(67), B: uint8(53), A: uint8(255)},
31+
color.RGBA{R: uint8(251), G: uint8(188), B: uint8(5), A: uint8(255)},
32+
color.RGBA{R: uint8(52), G: uint8(168), B: uint8(83), A: uint8(255)},
33+
}
34+
config.BackgroupColor = color.White
35+
36+
cp.Config = config
37+
return &cp
38+
39+
}
40+
41+
//SetFontColors 设置字体颜色
42+
func (cp *Captcha) SetFontColors(colors ...color.Color) {
43+
if len(colors) == 0 {
44+
return
45+
}
46+
47+
cp.Config.FrontColors = cp.Config.FrontColors[:0]
48+
for _, cr := range colors {
49+
cp.Config.FrontColors = append(cp.Config.FrontColors, cr)
50+
}
51+
52+
}
53+
54+
//SetBackgroundColor 设置背景颜色
55+
func (cp *Captcha) SetBackgroundColor(color color.Color) {
56+
cp.Config.BackgroupColor = color
2057
}
2158

2259
func (cp *Captcha) GenCaptchaImage() (string, error) {
60+
2361
c := Canvas{
2462
Width: cp.Width,
2563
Height: cp.Height,
2664
NRGBA: image.NewNRGBA(image.Rect(0, 0, cp.Width, cp.Height)),
65+
Config: cp.Config,
2766
}
2867

68+
c.DrawBackgroud()
69+
2970
c.DrawLines(5)
3071

31-
c.DrawString("测试阿斯蒂")
72+
c.DrawString("3567中")
3273

3374
c.DrawCircles(120)
3475

76+
// c.DrawBezierLines(5)
77+
3578
imageBytes, err := encodingWithPng(c)
3679

3780
if err != nil {

captcha_test.go

+3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package captcha
22

33
import (
4+
"image/color"
45
"testing"
56
)
67

78
func TestGenCaptcha(t *testing.T) {
89

910
cpt := NewCaptcha(460, 200)
11+
cpt.SetBackgroundColor(color.RGBA{R: uint8(20), G: uint8(8), B: uint8(100), A: uint8(255)})
12+
1013
image, err := cpt.GenCaptchaImage()
1114
t.Logf("GenCaptchaImage %s %+v", image, err)
1215
}

0 commit comments

Comments
 (0)