all repos — captcha @ f1572d4d2c2cd9730f3e2ac1acb5c88cb0023434

Go package captcha implements generation and verification of image and audio CAPTCHAs.

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	numberHeight int
 20	dotSize    int
 21}
 22
 23func (img *CaptchaImage) calculateSizes(width, height, ncount int) {
 24	// Goal: fit all numbers into the image.
 25	// Convert everything to floats for calculations.
 26	w := float64(width)
 27	h := float64(height)
 28	// fontWidth includes 1-dot spacing between numbers
 29	fw := float64(fontWidth) + 1
 30	fh := float64(fontHeight)
 31	nc := float64(ncount)
 32	// Calculate width of a sigle number if we only take into
 33	// account the width
 34	nw := w / nc
 35	// Calculate the number height from this width
 36	nh := nw * fh / fw
 37	// Number height too large?
 38	if nh > h {
 39		// Fit numbers based on height
 40		nh = h
 41		nw = fw/fh * nh
 42	}
 43	// Calculate dot size
 44	img.dotSize = int(nh / fh)
 45	// Save everything, making actual width smaller by 1 dot,
 46	// to account for spacing between numbers
 47	img.numberWidth = int(nw)
 48	img.numberHeight = int(nh) - img.dotSize
 49}
 50
 51func NewImage(numbers []byte, width, height int) *CaptchaImage {
 52	img := new(CaptchaImage)
 53	img.NRGBA = image.NewNRGBA(width, height)
 54	img.primaryColor = image.NRGBAColor{uint8(rand.Intn(50)), uint8(rand.Intn(50)), uint8(rand.Intn(128)), 0xFF}
 55	// We need some space, so calculate border
 56	var border int
 57	if width > height {
 58		border = height/5
 59	} else {
 60		border = width/5
 61	}
 62	bwidth := width-border*2
 63	bheight := height-border*2
 64	img.calculateSizes(bwidth, bheight, len(numbers))
 65	// Center numbers in image
 66	x := width/2 - (img.numberWidth*len(numbers))/2 - img.dotSize
 67	y := height/2 - img.numberHeight/2 + img.dotSize/2
 68	setRandomBrightness(&img.primaryColor, 180)
 69	for _, n := range numbers {
 70		//y = rand.Intn(dotSize * 4)
 71		img.drawNumber(font[n], x, y)
 72		x += img.numberWidth + img.dotSize
 73	}
 74	//img.strikeThrough(img.primaryColor)
 75	return img
 76}
 77
 78func NewRandomImage(width, height int) (img *CaptchaImage, numbers []byte) {
 79	numbers = randomNumbers()
 80	img = NewImage(numbers, width, height)
 81	return
 82}
 83
 84func (img *CaptchaImage) PNGEncode(w io.Writer) os.Error {
 85	return png.Encode(w, img)
 86}
 87
 88func (img *CaptchaImage) drawHorizLine(color image.Color, fromX, toX, y int) {
 89	for x := fromX; x <= toX; x++ {
 90		img.Set(x, y, color)
 91	}
 92}
 93
 94func (img *CaptchaImage) drawCircle(color image.Color, x, y, radius int) {
 95	f := 1 - radius
 96	dfx := 1
 97	dfy := -2 * radius
 98	xx := 0
 99	yy := radius
100
101	img.Set(x, y+radius, color)
102	img.Set(x, y-radius, color)
103	img.drawHorizLine(color, x-radius, x+radius, y)
104
105	for xx < yy {
106		if f >= 0 {
107			yy--
108			dfy += 2
109			f += dfy
110		}
111		xx++
112		dfx += 2
113		f += dfx
114		img.drawHorizLine(color, x-xx, x+xx, y+yy)
115		img.drawHorizLine(color, x-xx, x+xx, y-yy)
116		img.drawHorizLine(color, x-yy, x+yy, y+xx)
117		img.drawHorizLine(color, x-yy, x+yy, y-xx)
118	}
119}
120
121
122func min3(x, y, z uint8) (o uint8) {
123	o = x
124	if y < o {
125		o = y
126	}
127	if z < o {
128		o = z
129	}
130	return
131}
132
133func max3(x, y, z uint8) (o uint8) {
134	o = x
135	if y > o {
136		o = y
137	}
138	if z > o {
139		o = z
140	}
141	return
142}
143
144func setRandomBrightness(c *image.NRGBAColor, max uint8) {
145	minc := min3(c.R, c.G, c.B)
146	maxc := max3(c.R, c.G, c.B)
147	if maxc > max {
148		return
149	}
150	n := rand.Intn(int(max-maxc)) - int(minc)
151	c.R = uint8(int(c.R) + n)
152	c.G = uint8(int(c.G) + n)
153	c.B = uint8(int(c.B) + n)
154}
155
156func rnd(from, to int) int {
157	return rand.Intn(to+1-from) + from
158}
159
160func (img *CaptchaImage) fillWithCircles(color image.NRGBAColor, n, maxradius int) {
161	maxx := img.Bounds().Max.X
162	maxy := img.Bounds().Max.Y
163	for i := 0; i < n; i++ {
164		setRandomBrightness(&color, 255)
165		r := rnd(1, maxradius)
166		img.drawCircle(color, rnd(r, maxx), rnd(r, maxy), r)
167	}
168}
169
170// func (img *CaptchaImage) strikeThrough(color image.Color) {
171// 	r := 0
172// 	maxx := img.Bounds().Max.X
173// 	maxy := img.Bounds().Max.Y
174// 	y := rnd(maxy/3, maxy-maxy/3)
175// 	for x := 0; x < maxx; x += r {
176// 		r = rnd(1, dotSize/2-1)
177// 		y += rnd(-2, 2)
178// 		if y <= 0 || y >= maxy {
179// 			y = rnd(maxy/3, maxy-maxy/3)
180// 		}
181// 		img.drawCircle(color, x, y, r)
182// 	}
183// }
184
185func (img *CaptchaImage) drawNumber(number []byte, x, y int) {
186	//skf := rand.Intn(maxSkew) - maxSkew/2
187	//if skf < 0 {
188	//	x -= skf * numberHeight
189	//}
190	//d := img.numberWidth / fontWidth // number height is ignored
191	//println(img.numberWidth)
192	minr := img.dotSize/2 // minumum radius
193	maxr := img.dotSize // maximum radius
194	// x += srad
195	// y += srad
196	for yy := 0; yy < fontHeight; yy++ {
197		for xx := 0; xx < fontWidth; xx++ {
198			if number[yy*fontWidth+xx] != 1 {
199				continue
200			}
201			// introduce random variations
202			or := rnd(minr, maxr)
203			ox := x + (xx * maxr) //+ rnd(0, or/2)
204			oy := y + (yy * maxr) //+ rnd(0, or/2)
205			img.drawCircle(img.primaryColor, ox, oy, or)
206		}
207		//x += skf
208	}
209}