image.go (view raw)
1package captcha
2
3import (
4 "image"
5 "image/png"
6 "io"
7 "os"
8 "rand"
9)
10
11const (
12 maxSkew = 3
13)
14
15type CaptchaImage struct {
16 *image.NRGBA
17 primaryColor image.NRGBAColor
18 numberWidth int
19 dotRadius int
20}
21
22func NewImage(numbers []byte, width, height int) *CaptchaImage {
23 img := new(CaptchaImage)
24 img.NRGBA = image.NewNRGBA(width, height)
25 img.primaryColor = image.NRGBAColor{uint8(rand.Intn(50)), uint8(rand.Intn(50)), uint8(rand.Intn(128)), 0xFF}
26 //width -= 30
27 //height -= 30
28 var border int = 0
29 // if width < height {
30 // border = width/4
31 // } else {
32 // border = height/4
33 // }
34 bwidth := width - border*2
35 bheight := height - border*2
36 //border := 15
37 //fullNumberWidth := int(float64(width/len(numbers)) *
38 // float64(fontWidth) / float64(fontHeight))
39 var fullNumberWidth int
40 if float64(fontWidth)/float64(fontHeight) > float64(bwidth)/float64(bheight) {
41 fullNumberWidth = bheight / fontHeight * fontWidth
42 } else {
43 fullNumberWidth = bwidth / len(numbers)
44 }
45 // add spacing
46 img.numberWidth = fullNumberWidth - fullNumberWidth/fontWidth
47 // center numbers in image
48 x := border
49 y := border
50 setRandomBrightness(&img.primaryColor, 180)
51 for _, n := range numbers {
52 //y = rand.Intn(dotSize * 4)
53 img.drawNumber(font[n], x, y)
54 x += fullNumberWidth
55 }
56 //img.strikeThrough(img.primaryColor)
57 return img
58}
59
60func NewRandomImage(width, height int) (img *CaptchaImage, numbers []byte) {
61 numbers = randomNumbers()
62 img = NewImage(numbers, width, height)
63 return
64}
65
66func (img *CaptchaImage) PNGEncode(w io.Writer) os.Error {
67 return png.Encode(w, img)
68}
69
70func (img *CaptchaImage) drawHorizLine(color image.Color, fromX, toX, y int) {
71 for x := fromX; x <= toX; x++ {
72 img.Set(x, y, color)
73 }
74}
75
76func (img *CaptchaImage) drawCircle(color image.Color, x, y, radius int) {
77 f := 1 - radius
78 dfx := 1
79 dfy := -2 * radius
80 xx := 0
81 yy := radius
82
83 img.Set(x, y+radius, color)
84 img.Set(x, y-radius, color)
85 img.drawHorizLine(color, x-radius, x+radius, y)
86
87 for xx < yy {
88 if f >= 0 {
89 yy--
90 dfy += 2
91 f += dfy
92 }
93 xx++
94 dfx += 2
95 f += dfx
96 img.drawHorizLine(color, x-xx, x+xx, y+yy)
97 img.drawHorizLine(color, x-xx, x+xx, y-yy)
98 img.drawHorizLine(color, x-yy, x+yy, y+xx)
99 img.drawHorizLine(color, x-yy, x+yy, y-xx)
100 }
101}
102
103
104func min3(x, y, z uint8) (o uint8) {
105 o = x
106 if y < o {
107 o = y
108 }
109 if z < o {
110 o = z
111 }
112 return
113}
114
115func max3(x, y, z uint8) (o uint8) {
116 o = x
117 if y > o {
118 o = y
119 }
120 if z > o {
121 o = z
122 }
123 return
124}
125
126func setRandomBrightness(c *image.NRGBAColor, max uint8) {
127 minc := min3(c.R, c.G, c.B)
128 maxc := max3(c.R, c.G, c.B)
129 if maxc > max {
130 return
131 }
132 n := rand.Intn(int(max-maxc)) - int(minc)
133 c.R = uint8(int(c.R) + n)
134 c.G = uint8(int(c.G) + n)
135 c.B = uint8(int(c.B) + n)
136}
137
138func rnd(from, to int) int {
139 return rand.Intn(to+1-from) + from
140}
141
142func (img *CaptchaImage) fillWithCircles(color image.NRGBAColor, n, maxradius int) {
143 maxx := img.Bounds().Max.X
144 maxy := img.Bounds().Max.Y
145 for i := 0; i < n; i++ {
146 setRandomBrightness(&color, 255)
147 r := rnd(1, maxradius)
148 img.drawCircle(color, rnd(r, maxx), rnd(r, maxy), r)
149 }
150}
151
152// func (img *CaptchaImage) strikeThrough(color image.Color) {
153// r := 0
154// maxx := img.Bounds().Max.X
155// maxy := img.Bounds().Max.Y
156// y := rnd(maxy/3, maxy-maxy/3)
157// for x := 0; x < maxx; x += r {
158// r = rnd(1, dotSize/2-1)
159// y += rnd(-2, 2)
160// if y <= 0 || y >= maxy {
161// y = rnd(maxy/3, maxy-maxy/3)
162// }
163// img.drawCircle(color, x, y, r)
164// }
165// }
166
167func (img *CaptchaImage) drawNumber(number []byte, x, y int) {
168 //skf := rand.Intn(maxSkew) - maxSkew/2
169 //if skf < 0 {
170 // x -= skf * numberHeight
171 //}
172 d := img.numberWidth / fontWidth // number height is ignored
173 println(img.numberWidth)
174 srad := d/2 // standard (minumum) radius
175 mrad := d //srad + srad/2 // maximum radius
176 // x += srad
177 // y += srad
178 for yy := 0; yy < fontHeight; yy++ {
179 for xx := 0; xx < fontWidth; xx++ {
180 if number[yy*fontWidth+xx] != 1 {
181 continue
182 }
183 // introduce random variations
184 or := srad //rnd(srad, mrad)
185 ox := x + (xx * mrad) //+ rnd(0, or/2)
186 oy := y + (yy * mrad) //+ rnd(0, or/2)
187 img.drawCircle(img.primaryColor, ox, oy, or)
188 }
189 //x += skf
190 }
191}