captcha.go (view raw)
1package main
2
3import (
4 "image"
5 "image/png"
6 "os"
7 "rand"
8 "time"
9 crand "crypto/rand"
10 "io"
11)
12
13var numbers = [][]byte{
14 {
15 0, 1, 1, 1, 0,
16 1, 0, 0, 0, 1,
17 1, 0, 0, 0, 1,
18 1, 0, 0, 0, 1,
19 1, 0, 0, 0, 1,
20 1, 0, 0, 0, 1,
21 1, 0, 0, 0, 1,
22 0, 1, 1, 1, 0,
23 },
24 {
25 0, 0, 1, 0, 0,
26 0, 1, 1, 0, 0,
27 1, 0, 1, 0, 0,
28 0, 0, 1, 0, 0,
29 0, 0, 1, 0, 0,
30 0, 0, 1, 0, 0,
31 0, 0, 1, 0, 0,
32 1, 1, 1, 1, 1,
33 },
34 {
35 0, 1, 1, 1, 0,
36 1, 0, 0, 0, 1,
37 0, 0, 0, 0, 1,
38 0, 0, 0, 1, 1,
39 0, 1, 1, 0, 0,
40 1, 0, 0, 0, 0,
41 1, 0, 0, 0, 0,
42 1, 1, 1, 1, 1,
43 },
44 {
45 1, 1, 1, 1, 1,
46 0, 0, 0, 0, 1,
47 0, 0, 0, 1, 1,
48 0, 1, 1, 0, 0,
49 0, 0, 0, 1, 0,
50 0, 0, 0, 0, 1,
51 0, 0, 0, 0, 1,
52 1, 1, 1, 1, 0,
53 },
54 {
55 1, 0, 0, 1, 0,
56 1, 0, 0, 1, 0,
57 1, 0, 0, 1, 0,
58 1, 0, 0, 1, 0,
59 1, 1, 1, 1, 1,
60 0, 0, 0, 1, 0,
61 0, 0, 0, 1, 0,
62 0, 0, 0, 1, 0,
63 },
64 {
65 1, 1, 1, 1, 1,
66 1, 0, 0, 0, 0,
67 1, 0, 0, 0, 0,
68 1, 1, 1, 1, 0,
69 0, 0, 0, 1, 1,
70 0, 0, 0, 0, 1,
71 0, 0, 0, 1, 1,
72 1, 1, 1, 1, 0,
73 },
74 {
75 0, 0, 1, 1, 1,
76 0, 1, 0, 0, 0,
77 1, 0, 0, 0, 0,
78 1, 1, 1, 1, 0,
79 1, 1, 0, 0, 1,
80 1, 0, 0, 0, 1,
81 1, 1, 0, 0, 1,
82 0, 1, 1, 1, 0,
83 },
84 {
85 1, 1, 1, 1, 1,
86 0, 0, 0, 0, 1,
87 0, 0, 0, 0, 1,
88 0, 0, 0, 1, 0,
89 0, 0, 1, 0, 0,
90 0, 1, 0, 0, 0,
91 0, 1, 0, 0, 0,
92 0, 1, 0, 0, 0,
93 },
94 {
95 0, 1, 1, 1, 0,
96 1, 0, 0, 0, 1,
97 1, 1, 0, 1, 1,
98 0, 1, 1, 1, 0,
99 1, 1, 0, 1, 1,
100 1, 0, 0, 0, 1,
101 1, 1, 0, 1, 1,
102 0, 1, 1, 1, 0,
103 },
104 {
105 0, 1, 1, 1, 0,
106 1, 0, 0, 1, 1,
107 1, 0, 0, 0, 1,
108 1, 1, 0, 0, 1,
109 0, 1, 1, 1, 1,
110 0, 0, 0, 0, 1,
111 0, 0, 0, 0, 1,
112 1, 1, 1, 1, 0,
113 },
114}
115
116const (
117 NumberWidth = 5
118 NumberHeight = 8
119 DotSize = 6
120 SkewFactor = 3
121)
122
123func drawHorizLine(img *image.NRGBA, color image.Color, fromX, toX, y int) {
124 for x := fromX; x <= toX; x++ {
125 img.Set(x, y, color)
126 }
127}
128
129func drawCircle(img *image.NRGBA, color image.Color, x0, y0, radius int) {
130 f := 1 - radius
131 ddF_x := 1
132 ddF_y := -2 * radius
133 x := 0
134 y := radius
135
136 img.Set(x0, y0+radius, color)
137 img.Set(x0, y0-radius, color)
138 //img.Set(x0+radius, y0, color)
139 //img.Set(x0-radius, y0, color)
140 drawHorizLine(img, color, x0-radius, x0+radius, y0)
141
142 for x < y {
143 // ddF_x == 2 * x + 1;
144 // ddF_y == -2 * y;
145 // f == x*x + y*y - radius*radius + 2*x - y + 1;
146 if f >= 0 {
147 y--
148 ddF_y += 2
149 f += ddF_y
150 }
151 x++
152 ddF_x += 2
153 f += ddF_x
154 //img.Set(x0+x, y0+y, color)
155 //img.Set(x0-x, y0+y, color)
156 drawHorizLine(img, color, x0-x, x0+x, y0+y)
157 //img.Set(x0+x, y0-y, color)
158 //img.Set(x0-x, y0-y, color)
159 drawHorizLine(img, color, x0-x, x0+x, y0-y)
160 //img.Set(x0+y, y0+x, color)
161 //img.Set(x0-y, y0+x, color)
162 drawHorizLine(img, color, x0-y, x0+y, y0+x)
163 //img.Set(x0+y, y0-x, color)
164 //img.Set(x0-y, y0-x, color)
165 drawHorizLine(img, color, x0-y, x0+y, y0-x)
166 }
167}
168
169func min3(x, y, z uint8) (o uint8) {
170 o = x
171 if y < o {
172 o = y
173 }
174 if z < o {
175 o = z
176 }
177 return
178}
179
180func max3(x, y, z uint8) (o uint8) {
181 o = x
182 if y > o {
183 o = y
184 }
185 if z > o {
186 o = z
187 }
188 return
189}
190
191func setRandomBrightness(c *image.NRGBAColor, max uint8) {
192 minc := min3(c.R, c.G, c.B)
193 maxc := max3(c.R, c.G, c.B)
194 if maxc > max {
195 return
196 }
197 n := rand.Intn(int(max-maxc)) - int(minc)
198 c.R = uint8(int(c.R) + n)
199 c.G = uint8(int(c.G) + n)
200 c.B = uint8(int(c.B) + n)
201}
202
203func fillWithCircles(img *image.NRGBA, n, maxradius int) {
204 maxx := img.Bounds().Max.X
205 maxy := img.Bounds().Max.Y
206 color := image.NRGBAColor{0, 0, 0x80, 0xFF}
207 for i := 0; i < n; i++ {
208 setRandomBrightness(&color, 255)
209 r := rand.Intn(maxradius-1)+1
210 drawCircle(img, color, rand.Intn(maxx-r*2)+r, rand.Intn(maxy-r*2)+r, r)
211 }
212}
213
214func drawNumber(img *image.NRGBA, number []byte, x, y int, color image.NRGBAColor) {
215 skf := rand.Intn(SkewFactor)-SkewFactor/2
216 if skf < 0 {
217 x -= skf * NumberHeight
218 }
219 for y0 := 0; y0 < NumberHeight; y0++ {
220 for x0 := 0; x0 < NumberWidth; x0++ {
221 radius := rand.Intn(DotSize/2)+DotSize/2
222 addx := rand.Intn(radius/2)
223 addy := rand.Intn(radius/2)
224 if number[y0*NumberWidth+x0] == 1 {
225 drawCircle(img, color, x+x0*DotSize+DotSize+addx, y+y0*DotSize+DotSize+addy, radius)
226 }
227 }
228 x += skf
229 }
230}
231
232func drawNumbersToImage(ns []byte) {
233 img := image.NewNRGBA(NumberWidth*(DotSize+2)*len(ns)+DotSize, NumberHeight*DotSize+(DotSize*6))
234 for y := 0; y < img.Bounds().Max.Y; y++ {
235 for x := 0; x < img.Bounds().Max.X; x++ {
236 img.Set(x, y, image.NRGBAColor{0xFF, 0xFF, 0xFF, 0xFF})
237 }
238 }
239 fillWithCircles(img, 60, 3)
240 x := rand.Intn(DotSize)
241 y := 0
242 color := image.NRGBAColor{0, 0, 0x80, 0xFF}
243 setRandomBrightness(&color, 180)
244 for _, n := range ns {
245 y = rand.Intn(DotSize*4)
246 drawNumber(img, numbers[n], x, y, color)
247 x += DotSize * NumberWidth + rand.Intn(SkewFactor)+3
248 }
249 f, err := os.Create("captcha.png")
250 if err != nil {
251 panic(err)
252 }
253 defer f.Close()
254 png.Encode(f, img)
255}
256
257func main() {
258 rand.Seed(time.Seconds())
259 n := make([]byte, 6)
260 if _, err := io.ReadFull(crand.Reader, n); err != nil {
261 panic(err)
262 }
263 for i := range n {
264 n[i] %= 10
265 }
266 drawNumbersToImage(n)
267}
268