all repos — captcha @ d36aa1f342df1c83a78ffb1b37d3760eea13c626

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

image: refactor; add more color variations.
Dmitry Chestnykh dmitry@codingrobots.com
Thu, 21 Apr 2011 22:43:25 +0200
commit

d36aa1f342df1c83a78ffb1b37d3760eea13c626

parent

aa0588b4c9adf2cd98941eb25a8797644c22c863

2 files changed, 66 insertions(+), 57 deletions(-)

jump to
M font.gofont.go

@@ -3,6 +3,7 @@

const ( fontWidth = 5 fontHeight = 8 + blackChar = 1 ) var font = [][]byte{
M image.goimage.go

@@ -19,8 +19,8 @@

type CaptchaImage struct { *image.NRGBA primaryColor image.NRGBAColor - numberWidth int - numberHeight int + numWidth int + numHeight int dotSize int }

@@ -29,8 +29,13 @@ // given slice of numbers, where each number must be in range 0-9.

func NewImage(numbers []byte, width, height int) *CaptchaImage { img := new(CaptchaImage) img.NRGBA = image.NewNRGBA(width, height) - img.primaryColor = image.NRGBAColor{uint8(rand.Intn(50)), uint8(rand.Intn(50)), uint8(rand.Intn(128)), 0xFF} - // We need some space, so calculate border + img.primaryColor = image.NRGBAColor{ + uint8(rand.Intn(129)), + uint8(rand.Intn(129)), + uint8(rand.Intn(129)), + 0xFF, + } + // Calculate sizes var border int if width > height { border = height / 5

@@ -40,17 +45,19 @@ }

bwidth := width - border*2 bheight := height - border*2 img.calculateSizes(bwidth, bheight, len(numbers)) - // Background + // Draw background (10 random circles of random brightness) img.fillWithCircles(10, img.dotSize) - maxx := width - (img.numberWidth+img.dotSize)*len(numbers) - img.dotSize - maxy := height - img.numberHeight - img.dotSize*2 + // Randomly position captcha within image + maxx := width - (img.numWidth+img.dotSize)*len(numbers) - img.dotSize + maxy := height - img.numHeight - img.dotSize*2 x := rnd(img.dotSize*2, maxx) y := rnd(img.dotSize*2, maxy) - setRandomBrightness(&img.primaryColor, 180) + // Draw numbers for _, n := range numbers { img.drawNumber(font[n], x, y) - x += img.numberWidth + img.dotSize + x += img.numWidth + img.dotSize } + // Draw strike-through line img.strikeThrough() return img }

@@ -70,18 +77,18 @@ return png.Encode(w, img)

} func (img *CaptchaImage) calculateSizes(width, height, ncount int) { - // Goal: fit all numbers into the image. + // Goal: fit all numbers inside the image. // Convert everything to floats for calculations. w := float64(width) h := float64(height) - // fontWidth includes 1-dot spacing between numbers + // fw takes into account 1-dot spacing between numbers fw := float64(fontWidth) + 1 fh := float64(fontHeight) nc := float64(ncount) - // Calculate width of a sigle number if we only take into - // account the width + // Calculate the width of a single number taking into account only the + // width of the image nw := w / nc - // Calculate the number height from this width + // Calculate the height of a number from this width nh := nw * fh / fw // Number height too large? if nh > h {

@@ -91,10 +98,10 @@ nw = fw / fh * nh

} // Calculate dot size img.dotSize = int(nh / fh) - // Save everything, making actual width smaller by 1 dot, - // to account for spacing between numbers - img.numberWidth = int(nw) - img.numberHeight = int(nh) - img.dotSize + // Save everything, making actual width smaller by 1 dot, to account + // for spacing between numbers + img.numWidth = int(nw) + img.numHeight = int(nh) - img.dotSize } func (img *CaptchaImage) drawHorizLine(color image.Color, fromX, toX, y int) {

@@ -130,44 +137,6 @@ img.drawHorizLine(color, x-yy, x+yy, y-xx)

} } -func min3(x, y, z uint8) (o uint8) { - o = x - if y < o { - o = y - } - if z < o { - o = z - } - return -} - -func max3(x, y, z uint8) (o uint8) { - o = x - if y > o { - o = y - } - if z > o { - o = z - } - return -} - -func setRandomBrightness(c *image.NRGBAColor, max uint8) { - minc := min3(c.R, c.G, c.B) - maxc := max3(c.R, c.G, c.B) - if maxc > max { - return - } - n := rand.Intn(int(max-maxc)) - int(minc) - c.R = uint8(int(c.R) + n) - c.G = uint8(int(c.G) + n) - c.B = uint8(int(c.B) + n) -} - -func rnd(from, to int) int { - return rand.Intn(to+1-from) + from -} - func (img *CaptchaImage) fillWithCircles(n, maxradius int) { color := img.primaryColor maxx := img.Bounds().Max.X

@@ -202,7 +171,7 @@ maxr := img.dotSize/2 + img.dotSize/4 // maximum radius

y += rnd(-minr, minr) for yy := 0; yy < fontHeight; yy++ { for xx := 0; xx < fontWidth; xx++ { - if number[yy*fontWidth+xx] != 1 { + if number[yy*fontWidth+xx] != blackChar { continue } // introduce random variations

@@ -215,3 +184,42 @@ xs += skf

x = int(xs) } } + +func setRandomBrightness(c *image.NRGBAColor, max uint8) { + minc := min3(c.R, c.G, c.B) + maxc := max3(c.R, c.G, c.B) + if maxc > max { + return + } + n := rand.Intn(int(max-maxc)) - int(minc) + c.R = uint8(int(c.R) + n) + c.G = uint8(int(c.G) + n) + c.B = uint8(int(c.B) + n) +} + +func min3(x, y, z uint8) (o uint8) { + o = x + if y < o { + o = y + } + if z < o { + o = z + } + return +} + +func max3(x, y, z uint8) (o uint8) { + o = x + if y > o { + o = y + } + if z > o { + o = z + } + return +} + +// rnd returns a random number in range [from, to]. +func rnd(from, to int) int { + return rand.Intn(to+1-from) + from +}