Skip to content

Commit 66021e0

Browse files
committedMay 26, 2022
captcha init
0 parents  commit 66021e0

8 files changed

+298
-0
lines changed
 

‎.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
test.png

‎canvas.go

+194
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
package captcha
2+
3+
import (
4+
// "fmt"
5+
6+
"embed"
7+
// "fmt"
8+
"github.com/golang/freetype"
9+
"github.com/golang/freetype/truetype"
10+
"image"
11+
"image/color"
12+
// "math"
13+
"math/rand"
14+
"time"
15+
)
16+
17+
//go:embed fonts/*
18+
var fontFS embed.FS
19+
20+
var defaultFont *truetype.Font
21+
22+
func init() {
23+
24+
fontdata, _ := fontFS.ReadFile("fonts/SourceHanSerifCN-Light.ttf")
25+
defaultFont, _ = freetype.ParseFont(fontdata)
26+
27+
rand.Seed(time.Now().UnixNano())
28+
29+
// fmt.Printf("defaultFont %+v :err:%+v", defaultFont, err)
30+
}
31+
32+
type Canvas struct {
33+
*image.NRGBA
34+
Width int
35+
Height int
36+
}
37+
38+
func (c *Canvas) DrawLines(lineCount int) {
39+
40+
for lineCount > 0 {
41+
42+
p1 := Point{X: rand.Intn(c.Width), Y: rand.Intn(c.Height)}
43+
p2 := Point{X: rand.Intn(c.Width), Y: rand.Intn(c.Height)}
44+
45+
c.DrawLine(p1, p2, randomColor())
46+
lineCount--
47+
}
48+
}
49+
50+
//DrawLine 画干扰线
51+
func (c *Canvas) DrawLine(p1, p2 Point, color color.Color) {
52+
53+
var k float64 = 0
54+
b := p1.Y
55+
if (p2.X - p1.X) != 0 {
56+
k = float64(p2.Y-p1.Y) / float64(p2.X-p1.X)
57+
}
58+
59+
b = p1.Y - int(k*float64(p1.X))
60+
61+
sx := 1
62+
offsetX := p1.X
63+
64+
if p2.X < p1.X {
65+
sx = -1
66+
}
67+
68+
for {
69+
70+
offsetY := int(k*float64(offsetX)) + b
71+
c.Set(offsetX, offsetY, color)
72+
73+
if offsetX == p2.X {
74+
break
75+
}
76+
offsetX = offsetX + sx
77+
78+
}
79+
}
80+
81+
func (c *Canvas) DrawString(text string) {
82+
83+
for _, ch := range text {
84+
85+
dc := freetype.NewContext()
86+
dc.SetDPI(float64(72))
87+
dc.SetFont(defaultFont)
88+
dc.SetClip(c.Bounds())
89+
dc.SetDst(c)
90+
91+
// 文字大小
92+
dc.SetFontSize(float64(56))
93+
94+
// 文字颜色
95+
fontColor := image.NewUniform(randomColor())
96+
dc.SetSrc(fontColor)
97+
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)
104+
}
105+
}
106+
107+
}
108+
109+
func (c *Canvas) randomFontPosition(fontSize int) Point {
110+
minX := fontSize
111+
minY := fontSize
112+
113+
maxX := c.Width - fontSize
114+
maxY := c.Height - fontSize
115+
116+
x := randomInt(minX, maxX)
117+
y := randomInt(minY, maxY)
118+
119+
return Point{x, y}
120+
121+
}
122+
123+
//randomInt 返回随机数 [min,max)
124+
func randomInt(min, max int) int {
125+
if min >= max || max == 0 {
126+
return max
127+
}
128+
return rand.Intn(max-min) + min
129+
}
130+
131+
func randomColor() color.RGBA {
132+
red := rand.Intn(256)
133+
green := rand.Intn(256)
134+
blue := rand.Intn(256)
135+
136+
return color.RGBA{R: uint8(red), G: uint8(green), B: uint8(blue), A: uint8(255)}
137+
}
138+
139+
//DrawCircles 随机产生circleCount干扰点
140+
func (c *Canvas) DrawCircles(circleCount int) {
141+
142+
for circleCount > 0 {
143+
144+
cc := randomColor()
145+
x := rand.Intn(c.Width)
146+
y := rand.Intn(c.Height)
147+
148+
r := rand.Intn(3)
149+
150+
fillCircle := rand.Intn(5) > 1
151+
152+
c.DrawCircle(x, y, r, fillCircle, cc)
153+
154+
circleCount = circleCount - 1
155+
156+
}
157+
158+
}
159+
160+
func (c *Canvas) drawCircle8(xc, yc, x, y int, cc color.Color) {
161+
c.Set(xc+x, yc+y, cc)
162+
c.Set(xc-x, yc+y, cc)
163+
c.Set(xc+x, yc-y, cc)
164+
c.Set(xc-x, yc-y, cc)
165+
c.Set(xc+y, yc+x, cc)
166+
c.Set(xc-y, yc+x, cc)
167+
c.Set(xc+y, yc-x, cc)
168+
c.Set(xc-y, yc-x, cc)
169+
}
170+
171+
// DrawCircle 画圆
172+
func (c *Canvas) DrawCircle(xc, yc, r int, fill bool, cc color.Color) {
173+
size := c.Bounds().Size()
174+
if xc+r < 0 || xc-r >= size.X || yc+r < 0 || yc-r >= size.Y {
175+
return
176+
}
177+
x, y, d := 0, r, 3-2*r
178+
for x <= y {
179+
if fill {
180+
for yi := x; yi <= y; yi++ {
181+
c.drawCircle8(xc, yc, x, yi, cc)
182+
}
183+
} else {
184+
c.drawCircle8(xc, yc, x, y, cc)
185+
}
186+
if d < 0 {
187+
d = d + 4*x + 6
188+
} else {
189+
d = d + 4*(x-y) + 10
190+
y--
191+
}
192+
x++
193+
}
194+
}

‎captcha.go

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package captcha
2+
3+
import (
4+
"bytes"
5+
"encoding/base64"
6+
"fmt"
7+
"image"
8+
// "image/color"
9+
"image/png"
10+
"os"
11+
)
12+
13+
type Captcha struct {
14+
Width int
15+
Height int
16+
}
17+
18+
func NewCaptcha(width, height int) *Captcha {
19+
return &Captcha{Width: width, Height: height}
20+
}
21+
22+
func (cp *Captcha) GenCaptchaImage() (string, error) {
23+
c := Canvas{
24+
Width: cp.Width,
25+
Height: cp.Height,
26+
NRGBA: image.NewNRGBA(image.Rect(0, 0, cp.Width, cp.Height)),
27+
}
28+
29+
c.DrawLines(5)
30+
31+
c.DrawString("测试阿斯蒂")
32+
33+
c.DrawCircles(120)
34+
35+
imageBytes, err := encodingWithPng(c)
36+
37+
if err != nil {
38+
return "", err
39+
}
40+
41+
writeImageFile("./test.png", imageBytes)
42+
return fmt.Sprintf("data:%s;base64,%s", "image/png", base64.StdEncoding.EncodeToString(imageBytes)), nil
43+
44+
}
45+
46+
func encodingWithPng(img image.Image) (encodeResult []byte, err error) {
47+
48+
var buf bytes.Buffer
49+
50+
if err := png.Encode(&buf, img); err != nil {
51+
return encodeResult, err
52+
}
53+
54+
encodeResult = buf.Bytes()
55+
56+
buf.Reset()
57+
return encodeResult, nil
58+
59+
}
60+
61+
func writeImageFile(filepath string, imageBytes []byte) {
62+
file, err := os.OpenFile(filepath, os.O_WRONLY|os.O_CREATE, 0666)
63+
if err != nil {
64+
panic(err)
65+
}
66+
defer file.Close()
67+
file.Write(imageBytes)
68+
}

‎captcha_test.go

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package captcha
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestGenCaptcha(t *testing.T) {
8+
9+
cpt := NewCaptcha(460, 200)
10+
image, err := cpt.GenCaptchaImage()
11+
t.Logf("GenCaptchaImage %s %+v", image, err)
12+
}

‎fonts/SourceHanSerifCN-Light.ttf

13.8 MB
Binary file not shown.

‎go.mod

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module github.com/feiin/go-captcha
2+
3+
go 1.18
4+
5+
require github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
6+
7+
require golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9 // indirect

‎go.sum

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
2+
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
3+
golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9 h1:LRtI4W37N+KFebI/qV0OFiLUv4GLOWeEW5hn/KEJvxE=
4+
golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
5+
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
6+
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

‎point.go

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package captcha
2+
3+
type Point struct {
4+
X int
5+
Y int
6+
}
7+
8+
func NewPoint(x int, y int) *Point {
9+
return &Point{X: x, Y: y}
10+
}

0 commit comments

Comments
 (0)
Please sign in to comment.